<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Maud de Vries</title>
    <description>The latest articles on DEV Community by Maud de Vries (@mauddevries).</description>
    <link>https://dev.to/mauddevries</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F172621%2Fa54be0e9-ac55-43de-9aac-ce279cc2c7c8.png</url>
      <title>DEV Community: Maud de Vries</title>
      <link>https://dev.to/mauddevries</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mauddevries"/>
    <language>en</language>
    <item>
      <title>Getting Started With System Tests in Rails With Minitest</title>
      <dc:creator>Maud de Vries</dc:creator>
      <pubDate>Wed, 19 Feb 2020 15:55:28 +0000</pubDate>
      <link>https://dev.to/appsignal/getting-started-with-system-tests-in-rails-with-minitest-47oi</link>
      <guid>https://dev.to/appsignal/getting-started-with-system-tests-in-rails-with-minitest-47oi</guid>
      <description>&lt;p&gt;In today's post, we'll look at system tests in Rails 6. System tests are meant to auto-test the way users interact with your application, including the Javascript in your user interface. Minitest, being the default testing framework in Rails, is a great match for system testing. With all the configuration that Rails handles for us, there are just a few steps needed before we have our first tests up and running.&lt;/p&gt;

&lt;p&gt;System tests were added to the Rails stack in Rails 5.1. When I sat down to start using them, I found it hard to gather relevant, up to date information that is not about RSpec. Here’s all the latest and greatest that I collected on working with the system tests with Minitest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to System Tests
&lt;/h2&gt;

&lt;p&gt;In Rails jargon, system testing refers to "testing an application as a whole system". That is done by using a browser in the tests. Instead of testing separate parts, with system tests, we can test a whole 'workflow', just like what a user goes through while interacting with our app, including the JavaScript parts. In practice, it means that we don't want a system test to check &lt;em&gt;if&lt;/em&gt; a record is created in the database when a user clicks a button; we just test if that new record appears on their screen. These kinds of user interaction tests are also called feature tests or acceptance tests. They are different from integration tests: integration tests are for testing the behavior, especially of all the parts of the app together, but not via the user interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;The configuration is so simple, it's almost confusing.&lt;/p&gt;

&lt;p&gt;The Capybara gem is used for interacting with the browser. It's through Capybara that we can make the tests visit pages, fill in forms, click on links and buttons. If you've worked with Capybara before, you're aware of all the things you have to coordinate to make it work with a database cleaning strategy and the browser configuration. Hooray for the Rails system test set up: it takes care of all the configuration that is needed to work with Capybara out-of-the-box. So we can focus on the actual test writing. It uses the Selenium driver by default.&lt;/p&gt;

&lt;p&gt;From the docs: “By default, ActionDispatch::SystemTestCase is driven by the Selenium driver, with the Chrome browser, and a browser size of 1400x1400.” The Selenium driver is different from Capybara's default and is chosen because it works with Javascript.&lt;/p&gt;

&lt;p&gt;All the settings are available through &lt;code&gt;application_system_test_case.rb&lt;/code&gt;. The only thing you need to do is require this in each test file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#application_system_test_case.rb (default)&lt;/span&gt;


&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationSystemTestCase&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SystemTestCase&lt;/span&gt;
  &lt;span class="n"&gt;driven_by&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;using: :chrome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;screen_size: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1400&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you generated a new app with Rails 6, this is really all you need. Meaning: you can skip all the advice on how to set up the Selenium driver that the Internet shows for user interface tests or RSpec's feature tests.&lt;/p&gt;

&lt;p&gt;In existing apps, generating a scaffold will automatically generate the &lt;code&gt;application_system_test_case.rb&lt;/code&gt; and everything you need for the system tests.&lt;/p&gt;

&lt;p&gt;When Eileen Uchitelle &lt;a href="https://www.youtube.com/watch?v=sSn4B8orX70"&gt;introduced system tests&lt;/a&gt;, Chrome was chosen over Firefox because at the time FF didn't play nicely with Selenium. That is fixed in later versions of Firefox. So I replace Chrome with FF, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#application_system_test_case.rb (change driver to Firefox)&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationSystemTestCase&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SystemTestCase&lt;/span&gt;
  &lt;span class="n"&gt;driven_by&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;using: :firefox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;screen_size: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1400&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(Initially, I ran &lt;code&gt;$ rails db:test:prepare after&lt;/code&gt; switching drivers to ensure a clean start, BUT the new test runner takes care of that too: invoking test will autorun &lt;code&gt;:prepare.&lt;/code&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  New in Rails 6
&lt;/h2&gt;

&lt;p&gt;Since the introduction of system tests in 5.1, the chromedriver-helper gem has been deprecated. In new Rails 6 apps, it is replaced with the webdrivers gem. In Rails 5 apps, you should replace the chromedriver-helper with the webdrivers gem if you haven't done that yet. With the webdrivers gem in place, you don't even need the selenium-webdriver gem; webdrivers takes care of that one too. Fun fact: Rails 6 gives us both the selenium-webdriver gem and the webdrivers gem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sanity Check: What You Don't Need Anymore
&lt;/h2&gt;

&lt;p&gt;Setting up Capybara used to be a pain. It wasn't clear what you needed to set it up from scratch, including finding the correct database cleaning strategy, the dependencies of the capybara-webkit gem and all kinds of unexpected requirements that you needed to figure out before you could start writing tests. Now that Rails provides everything we need, I found the only confusing part to be all the information available in guides and documentation that is obsolete now. Here are some things that we can ignore now.&lt;/p&gt;

&lt;p&gt;First, the precooked Rails setup means that we can omit more gems that are often mentioned in regards to testing, like minitest-rails-capybara, or the extras for Minitest: like-colored output, fast failing tests and running one line/test. These Minitest features are in the &lt;a href="https://guides.rubyonrails.org/5_0_release_notes.html#test-runner"&gt;test runner introduced in Rails 5.0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There's no need for the database_cleaner gem anymore; Rails does the cleanup for us ('transactional tests').&lt;/p&gt;

&lt;p&gt;Since Rails uses Selenium instead of the Capybara default, we don't need capybara-webkit and we don't need to do any extras to include testing of Javascript elements.&lt;/p&gt;

&lt;p&gt;You don't even need to use Capybara's &lt;code&gt;save_and_open_screenshot&lt;/code&gt;, because Rails provides a &lt;code&gt;take_screenshot&lt;/code&gt; method. It saves a screenshot in &lt;code&gt;/tmp&lt;/code&gt; and provides a link in the test output for easy access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Between a Real Browser and Headless Browser
&lt;/h2&gt;

&lt;p&gt;I can't get enough of watching the system tests running in the browser and see how all the links are clicked, forms are filled in, etc. But, it is slow. To speed up the test run, you can use a 'headless' browser: that is a browser that has the same access to your app as a regular browser, but without the graphical user interface. Meaning: it works the same but you don't actually see the tests doing their work since a headless driver doesn't open an actual browser window.&lt;/p&gt;

&lt;p&gt;If you do want to go headless, there's &lt;code&gt;headless_chrome&lt;/code&gt; and &lt;code&gt;headless_firefox&lt;/code&gt;. To use them, there's one small change needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# To change driver to headless_*&lt;/span&gt;
&lt;span class="c1"&gt;#application_system_test_case.rb (change driver to headless_*:)&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationSystemTestCase&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SystemTestCase&lt;/span&gt;
  &lt;span class="n"&gt;driven_by&lt;/span&gt; &lt;span class="ss"&gt;:selenium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;using: :headless_firefox&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note: In the Rails documentation and in several articles I found, Poltergeist was named as an option for the driver. It used to be popular as the - headless - PhantomJS driver for Capybara. PhantomJS has been abandoned. So, no need to dive into the particulars—the reason I mention it here is because Poltergeist and PhantomJS are still mentioned in the Rails docs.&lt;/p&gt;

&lt;p&gt;There's more customization you can do, but to get started, this is really all you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Tests
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;$ rails test&lt;/code&gt; will run all the tests except the system tests. You need to explicitly run &lt;code&gt;$ rails test:system&lt;/code&gt;. (Fun fact: the &lt;code&gt;$ rails&lt;/code&gt; command will always run through bin/rails. No need to type &lt;code&gt;$ bin/rails&lt;/code&gt; anymore.)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Run all the system tests:
&lt;code&gt;$ rails test:system&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run test in a specific file (here: users_test.rb)
&lt;code&gt;$ rails test/system/users_test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;... or one specific test:
&lt;code&gt;$ rails test test/system/users_test.rb:21&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To run all the tests: run the system tests first:
&lt;code&gt;$ rails test:system test&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: the options flags don't work with &lt;code&gt;test:system&lt;/code&gt;; if you want to use flags like &lt;code&gt;-f&lt;/code&gt; (for fail fast) or &lt;code&gt;-v&lt;/code&gt; (for verbose), use &lt;code&gt;$ rails test test/system -v -f&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What (Not) to Test
&lt;/h2&gt;

&lt;p&gt;System tests are complementary to unit tests, not a substitute. It will do to test a happy path, plus maybe one path with an error message or redirect. System tests are not meant to test all the edge cases in the browser; just cover the main features.&lt;/p&gt;

&lt;p&gt;When choosing my test subject, I try to find an entity to test that reflects how the user uses the app. For the naming of my tests, I borrowed GitLab's naming convention of ROLE_ACTION_test.rb, which fits this approach well. For instance: &lt;code&gt;user_shares_card_test.rb&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  General Tips and Tricks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you use Devise for authentication, you can use the Devise integration helpers to log in and log out test users. Add &lt;code&gt;include Devise::Test::IntegrationHelpers&lt;/code&gt; to a test class, or add it in the &lt;code&gt;test_helper.rb&lt;/code&gt; file to make them available in all tests. Now you can create a user and &lt;code&gt;sign_in(@user)&lt;/code&gt;. If you put that in a &lt;code&gt;setup&lt;/code&gt; method, you don’t need to log out the user in a teardown. Rails takes care of cleaning up what's in the setup method.&lt;/li&gt;
&lt;li&gt;Working with forms, it's tempting to reference the ids that Rails creates automatically from model name + field name and the button types for &lt;code&gt;click_on&lt;/code&gt;s. ( &lt;code&gt;fill_in "user_email"&lt;/code&gt;, &lt;code&gt;click_on :commit&lt;/code&gt;). But since systems tests are about what a user would actually &lt;em&gt;see&lt;/em&gt; on their screen, it makes sense to use visible elements, i.e. texts. A reasonable option would be to have reference keys in i18n locale files and use those keys instead of the ever-changing literal texts. (&lt;code&gt;fill_in :user_email&lt;/code&gt;). Capybara finds the text only with the full I18n syntax: &lt;code&gt;assert_selector "h1", text: I18n.t("activerecord.models.things")&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The path helpers are included by default. For some libraries you need to include the helpers in the test class, for instance &lt;code&gt;include ActionMailer::TestHelper&lt;/code&gt;, to use the &lt;code&gt;assert_emails&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Create custom classes to run the tests on different screen sizes. &lt;a href="https://guides.rubyonrails.org/testing.html#testing-for-multiple-screen-sizes"&gt;Check out the guides&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The screenshot feature can also be used to take screenshots for your documentation and promotional materials. If you make it a screencast and slow down the playback speed, you have a product video in minutes!&lt;/li&gt;
&lt;li&gt;Rails provides a generator for system tests.&lt;/li&gt;
&lt;li&gt;Minitest in Rails is slightly different than Minitest itself, and also adds Rails specific methods and assertions. Check the Rails docs first before the Minitest docs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had good fun with the system tests, and that was, for the most part, thanks to how easy it was to use it out-of-the-box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, we looked at what configuration Rails delivers out-of-the-box and what minimal customizations we may want to add. Minitest is great for writing System tests. A few tips and tricks should help to get your first system tests up and running. Watch them do their magic in the browser!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Guest author Maud de Vries is a freelance Ruby on Rails developer, a Coach for (solo) entrepreneurs and she used to be an editor as well. The writer inside sometimes escapes.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>beginners</category>
      <category>testing</category>
    </item>
    <item>
      <title>The Ultimate Checklist to Properly Internationalize Devise</title>
      <dc:creator>Maud de Vries</dc:creator>
      <pubDate>Tue, 11 Jun 2019 12:19:37 +0000</pubDate>
      <link>https://dev.to/appsignal/the-ultimate-checklist-to-properly-internationalize-devise-59m6</link>
      <guid>https://dev.to/appsignal/the-ultimate-checklist-to-properly-internationalize-devise-59m6</guid>
      <description>&lt;p&gt;Sometimes, the best blog post comes from a need to scratch your own itch. And today's blog post is an example of that!&lt;/p&gt;

&lt;p&gt;You probably know &lt;a href="https://rubygems.org/gems/devise"&gt;Devise&lt;/a&gt;—a popular authentication solution for Rails applications. When working with Devise, I found that getting a translation up and running was more complex than I had anticipated. Past me wished that there was a checklist that could help guide me through the process.&lt;/p&gt;

&lt;p&gt;So here it is - for future me and you - the ultimate Devise Internationalization Checklist!&lt;/p&gt;

&lt;h2&gt;
  
  
  When (Not) to Use the devise-i18n Gem?
&lt;/h2&gt;

&lt;p&gt;Devise's policy is to not make the strings in the Devise views translatable. All the strings are hard coded. Kudos to the creators of the &lt;a href="https://github.com/tigrish/devise-i18n"&gt;devise-i18n&lt;/a&gt; gem, which makes all the Devise templates translatable and also provides community-sourced translations. For international apps, where you need multi-language support, the devise-i18n gem is the easiest solution.&lt;/p&gt;

&lt;p&gt;However, the devise-i18n gem explicitly chooses to follow the Devise text as closely as possible. But the text that Devise provides isn't very consistent to start with. And since literal translation will never generate fluent language, I'm constantly tweaking translations (and copying files between projects). That is an ongoing struggle because it's hard to get all the translations consistent when looking at one string or one view at a time.&lt;/p&gt;

&lt;p&gt;As a result, the quality of the translations of the i18n-devise gem don't reach the standards required for my monolingual, Dutch-only projects.&lt;/p&gt;

&lt;p&gt;Another disadvantage of using the gem arrises with customized views. Even when it's only to do with styling, every update of the devise-i18n gem that touches any changes that Devise made to the views, will mean you have to fix things. You will either need to regenerate the views and apply the customizations or you will have to copy/paste the changes into your views. This problem is not solved with the approach in the checklist below, by the way, but it's also not more difficult to solve without the devise-i18n gem. 🤷‍♀&lt;/p&gt;


&lt;h2&gt;🇳🇱 A Dutch treat&lt;/h2&gt;

&lt;p&gt;
I decided to create one nice Dutch translation file for Devise, and never have to worry about it again. Sounds like a treat? You can find the &lt;a href="https://github.com/F3PiX/Devise-i18n-checklist/blob/master/devise.nl.yml"&gt;new and improved translation file&lt;/a&gt; on GitHub. See step 3 in the checklist. The rationale for each translation decision is explained in the file.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Ultimate Checklist
&lt;/h2&gt;

&lt;p&gt;Now that we're flying solo with the :nl translation, let's get started with of all the details and start checking boxes.&lt;/p&gt;

&lt;p&gt;Note that this assumes that you have an app ready, with Devise added and installed with a User model, and the Devise views generated. If you're coding along: &lt;a href="https://github.com/F3PiX/sandbox-devise-i18n-ed"&gt;this is the starting point&lt;/a&gt;. And note that &lt;code&gt;nl&lt;/code&gt; in &lt;code&gt;*.nl.yml&lt;/code&gt; can be replaced by the language of your choice, and that User and :user can be replaced by the model name you use in Devise.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step One: Set locale
&lt;/h2&gt;

&lt;p&gt;Add &lt;code&gt;:nl&lt;/code&gt; to the available locales (if you don't have a strong opinion on the matter, start with putting it in &lt;code&gt;application.rb&lt;/code&gt; for now). Now you can set it as default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# application.rb&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;available_locales&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:nl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:en&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:nl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are better, perhaps cleaner options to set the locale than adding it to &lt;code&gt;application.rb&lt;/code&gt;. Check the &lt;a href="https://guides.rubyonrails.org/i18n.html"&gt;Rails i18n guide&lt;/a&gt; after reading this post to choose the option that suits you best.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step Two: Add Rails translation
&lt;/h2&gt;

&lt;p&gt;Add the Rails &lt;a href="https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/nl.yml"&gt;:nl translation file&lt;/a&gt; to take care of the strings in the interface that are not Devise specific (especially the Rails validation error messages).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step Three: The Devise keys
&lt;/h2&gt;

&lt;p&gt;Translate the Devise keys to &lt;code&gt;:nl&lt;/code&gt;: add the New and Improved Dutch Translation! (Link above 🇳🇱)&lt;/p&gt;

&lt;h2&gt;
  
  
  Step Four: Create views.*.yml files
&lt;/h2&gt;

&lt;p&gt;Add Dutch and English &lt;code&gt;.yml&lt;/code&gt; files for the views, &lt;code&gt;views.*.yml&lt;/code&gt;. There's &lt;a href="https://github.com/F3PiX/Devise-i18n-checklist/blob/master/views.nl.yml"&gt;an example of a finished views.nl.yml file in the repo&lt;/a&gt;, reflecting steps 4, 6 and 7.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;#example views.nl.yml&lt;/span&gt;
&lt;span class="na"&gt;nl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;devise&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# to be filled in later with Devise scopes:&lt;/span&gt;
    &lt;span class="c1"&gt;# registrations:&lt;/span&gt;
    &lt;span class="c1"&gt;#   new:&lt;/span&gt;
    &lt;span class="c1"&gt;#     forget_password: Wachtwoord vergeten?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Step Five: The Active Record attributes
&lt;/h2&gt;

&lt;p&gt;Now, add translations for the labels of the Active Record attributes, like email and password. Translate just the ones that the user and the admin user see, for each of the modules that you implemented. No need to translate token names and such (as the i18n gem does). Here's what I consider useful 'translatables':&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In: active_record.nl.yml&lt;/span&gt;
&lt;span class="c1"&gt;# translating the labels that map to User attributes&lt;/span&gt;
&lt;span class="na"&gt;nl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;activerecord&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;current_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Huidig wachtwoord&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;e-mailadres&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wachtwoord&lt;/span&gt;
        &lt;span class="na"&gt;password_confirmation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wachtwoord bevestigen&lt;/span&gt;
        &lt;span class="na"&gt;remember_me&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingelogd blijven?&lt;/span&gt;
    &lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gebruiker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that the &lt;code&gt;:email&lt;/code&gt; and &lt;code&gt;:password&lt;/code&gt; values are downcased. That's because of the tweaks to the Rails error messages in step 7.&lt;/p&gt;

&lt;p&gt;For now, put the keys in &lt;code&gt;active_record.*.yml&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step Six: Check every Devise view
&lt;/h2&gt;

&lt;p&gt;Next, all the Devise-specific templates and strings need to be i18n-ed with the corresponding translations. Doing it yourself is an annoying job. Before you dive into it, see if one of these shortcuts work for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You could use the 118n-devise gem for the views, but you still have to copy the keys from the gem's &lt;code&gt;.yml&lt;/code&gt; files into your project's &lt;code&gt;.yml&lt;/code&gt; files. The gem interleaves them in &lt;code&gt;devise.*.yml&lt;/code&gt;, but we don't want to overwrite our nice new Dutch translation. And if you have styled the Devise views, you'll need to copy the styling too. Yugh. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you already have i18n-ed projects, you could copy the view files. (Same disadvantages with this as in the previous option.) &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the repo for &lt;a href="https://github.com/F3PiX/Devise-i18n-checklist/blob/master/devise_views/"&gt;examples of a few views&lt;/a&gt; that I used in my latest project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or solve it once and for all: head down, create a set of files that you can use from now on, exactly the way you want them to be. The examples in the previous step are my own first version; they may help you kick-start your own. Check out the mini-checklist below for all the requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mini Checklist for the Devise Forms
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Strings that are not Active Record strings need to be 118n-ed and have (properly scoped) keys and translations (stored in, for instance, a &lt;code&gt;views.*.yml&lt;/code&gt; file—see example).&lt;/li&gt;
&lt;li&gt;Take care that the Dutch translations, when adding your own, are consistent with the new &lt;code&gt;devise.nl.yml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ignore the downcased labels; they will be capitalized properly in step 7.&lt;/li&gt;
&lt;li&gt;If you already have the mailers i18n-ed, that's okay. If not, you may ignore them until step 8.&lt;/li&gt;
&lt;li&gt;The following Devise views need to be made translatable with the Rails translate (&lt;code&gt;t&lt;/code&gt;) helper:

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;devise/views/shared/_links&lt;/code&gt; partial: all the link names;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;devise/views/shared/_errors&lt;/code&gt; partial (since Devise 4.6)&lt;/li&gt;
&lt;li&gt;In the Devise forms for each of the modules you use: all the strings that are not labels for Active Record attributes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step Seven: The Active Record error messages
&lt;/h2&gt;

&lt;p&gt;Next, we need to tweak the Active Record error messages that are related to the Devise functionality. Like "password can't be blank", and other validations.&lt;/p&gt;

&lt;p&gt;The thing is, now that we have upgraded the Devise translations, the Active Record messages need tweaking as well because the inconsistency in style is really showing. So I tweaked them too, following the same rules as for the Devise translations, and making sure they are consistent.&lt;/p&gt;

&lt;p&gt;You'll see that the format of the messages has changed: from "%{attribute} %{message}" to "%{message}". Now we are referencing the attribute within the translated string instead of forcing it at the start of the string, makes it so much easier to prettify the Dutch messages. I rather have the duplication than putting up with the sometimes weird messages.&lt;/p&gt;

&lt;p&gt;The new message format is the reason why, in step 5, the email and password attributes needed to be downcased: in the error messages, they can appear anywhere within a sentence.&lt;/p&gt;

&lt;p&gt;I also replaced the &lt;code&gt;:taken&lt;/code&gt; message. The Devise policy is to not reveal if an email address is 'not found' or 'invalid'. I extended that policy to the Rails' &lt;code&gt;taken&lt;/code&gt; message.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://github.com/F3PiX/Devise-i18n-checklist/blob/master/rails_error_messages"&gt;this file&lt;/a&gt; if you want to follow the new messages style. Then replace the corresponding part of the Rails &lt;code&gt;error: messages:&lt;/code&gt; keys in the original &lt;code&gt;nl.yml&lt;/code&gt; file. One caveat: when you're adding validations for none Devise-related input, please double check if the error messages still make sense. (I need to investigate further; I only checked the ones that are related to Devise.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Step Eight: Fix the labels we just broke
&lt;/h2&gt;

&lt;p&gt;In step 5, we downcased the email and password translations for use in the new error messages. Now we need to fix their appearance in the &lt;em&gt;labels&lt;/em&gt;. There are several options, but the one I like most is adding separate 'label' keys.&lt;/p&gt;

&lt;p&gt;This adds some duplication for the user keys, but it keeps the labels in the forms clean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# in active_record.nl.yml add the label helper keys:&lt;/span&gt;
&lt;span class="na"&gt;nl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;activerecord&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;current_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Huidig wachtwoord&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;e-mailadres&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wachtwoord&lt;/span&gt;
        &lt;span class="na"&gt;password_confirmation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Herhaal het wachtwoord&lt;/span&gt;
        &lt;span class="na"&gt;remember_me&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wil je ingelogd blijven?&lt;/span&gt;
    &lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gebruiker&lt;/span&gt;
  &lt;span class="na"&gt;helpers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Emailadres&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wachtwoord&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that there is already a &lt;code&gt;:helpers&lt;/code&gt; scope in the original &lt;code&gt;nl.yml&lt;/code&gt;. In general, it's a good idea to keep the keys for a scope together, for easy lookup (by humans).&lt;/p&gt;

&lt;p&gt;The other changes we need to make are straightforward. No big surprises will pop up from here on. 🎉.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step Nine: Update navigation links
&lt;/h2&gt;

&lt;p&gt;Find all the links that point to Devise (think navigation) and i18n them with the Rails &lt;code&gt;t&lt;/code&gt; helper.&lt;/p&gt;

&lt;p&gt;I like to keep those somewhere in the Devise scope of the .yml's. For example, I'd add &lt;code&gt;t(".devise.sign_out")&lt;/code&gt; to have &lt;code&gt;:sign_out&lt;/code&gt; in the same scope as &lt;code&gt;:sign_up&lt;/code&gt; and &lt;code&gt;:sign_in&lt;/code&gt; (see the example files).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step Ten: Translate the mailers.
&lt;/h2&gt;

&lt;p&gt;Translate the mailer views. There are a few options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You can 'i18n' every string and link name, just like we did in step 6 with the other views. You may want to add a separate &lt;code&gt;mailer.*.yml&lt;/code&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Or you can add separate mailer &lt;a href="https://guides.rubyonrails.org/i18n.html#localized-views"&gt;views for each locale&lt;/a&gt;: &lt;code&gt;devise/mailer/reset_password.nl.html.erb&lt;/code&gt; and &lt;code&gt;devise/mailer/reset_password.en.html.erb&lt;/code&gt;. Each with their own text. Rails will pick the one that matches the locale that is set. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alright! Now all the Devise strings are properly translated. Yay!&lt;/p&gt;

&lt;h2&gt;
  
  
  After Care: Declutter
&lt;/h2&gt;

&lt;p&gt;In my views example, I extracted the shared keys (like &lt;code&gt;:forgot_password&lt;/code&gt;) and collected them in the general devise scope. This makes it easier to change them, but Rails can't find them automatically, so we need the verbose syntax (&lt;code&gt;t("devise.forgot_password")&lt;/code&gt;), because the dot syntax (&lt;code&gt;t(".new_confirmation_mail")&lt;/code&gt;) won't work.&lt;/p&gt;

&lt;p&gt;If you go with the &lt;code&gt;active_record.*.yml&lt;/code&gt;, it makes sense to collect all the keys with active record scope into that file. Move the Active Record keys from the Rails &lt;code&gt;*.yml&lt;/code&gt; files into the corresponding &lt;code&gt;active_record.*.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We also have two pairs of &lt;code&gt;*.yml&lt;/code&gt; files that duplicate the &lt;code&gt;devise:&lt;/code&gt; scope. Consider mixing the keys from the Devise views into the &lt;code&gt;devise.*.yml&lt;/code&gt; views. It is what the Devise-i18n gem does, and I like it. You'd do it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# mixing the view scopes into devise.nl.yml&lt;/span&gt;
&lt;span class="na"&gt;nl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;devise&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;confirmations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="na"&gt;registrations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="s"&gt;edit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;...&lt;/span&gt;
      &lt;span class="na"&gt;new&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Some Tweaks and Tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I am not a fan of the header in the error message ('x errors prohibited this user from being saved'), so I removed that from the &lt;code&gt;errors&lt;/code&gt; partial.&lt;/li&gt;
&lt;li&gt;If you set the &lt;code&gt;default_locale&lt;/code&gt; to &lt;code&gt;:nl&lt;/code&gt;, Rails will 'humanize' the keys if it can't find a translation. So, &lt;code&gt;t(:some_untranslated_key)&lt;/code&gt; will show as &lt;code&gt;Some Untranslated Key&lt;/code&gt; in the view.&lt;/li&gt;
&lt;li&gt;With the &lt;a href="https://github.com/glebm/i18n-tasks"&gt;I18n-tasks&lt;/a&gt; gem you can clean up unused keys and add missing keys. It's a great tool! However, my IDE has great i18n support and for me that turned out to be a quicker workflow than the gem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parting Is Such Sweet Sorrow
&lt;/h2&gt;

&lt;p&gt;A quick final word before we part. When I started this journey I didn't expect it to be as much work as it turned out to be. I'm very happy that I now know where the complexity comes from. And future me and you now have a list of all the changes needed. Woohoo!&lt;/p&gt;

&lt;p&gt;But even with this checklist in hand, think before you act: maybe it's worth living with the less optimal translations for a while…&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Guest author Maud de Vries is a freelance Ruby on Rails developer, a Coach for (solo) entrepreneurs and she used to be an editor as well. The writer inside sometimes escapes.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>learning</category>
      <category>rails</category>
      <category>devise</category>
    </item>
  </channel>
</rss>
