10.04.2017 Views

Rails I18n – Seven Best Practices That You Should Know About

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>Rails</strong> Internationalization (<strong>I18n</strong>)<br />

<strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong><br />

<strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

Table of Content<br />

Split Translations 4<br />

Take Advantage Of Nesting 5<br />

Employ “Lazy” Lookups 6<br />

Enforce Available Locales 7<br />

Utilize Localized Views 8<br />

Take Advantage Of Variables 8<br />

Helper Methods 10<br />

Stick With PhraseApp! 10<br />

Conclusion 11<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

Are you creating web applications with the Ruby on <strong>Rails</strong> framework? If you are, have a look at<br />

these seven <strong>I18n</strong> best practices, including advices that will help you organize your translations<br />

better and take full advantage of <strong>Rails</strong> power.<br />

I really love crafting web applications with the Ruby on <strong>Rails</strong> framework. Creating small<br />

maintainable applications is not a problem at all <strong>–</strong> your code is beautifully structured and<br />

everything fits in. However, as your app grows, it may become significantly harder to organize<br />

parts of the code so that it does not turn into something monstrous and unmanageable. This, of<br />

course, applies to ​internationalization​ as well.<br />

In this article you will learn about seven <strong>I18n</strong> best practices, including advice that will help<br />

organize your translations better and take full advantage of <strong>Rails</strong> power. I am assuming that you<br />

already know the basics behind internationalization and localization in <strong>Rails</strong>, but if you wish to<br />

refresh your knowledge, take a look at ​this official guide or ​our article describing the topic<br />

thoroughly. Have a nice reading!<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

Split Translations<br />

Large applications may have hundreds of translations keys. If they are stored in a single file it<br />

becomes really hard to manage them, therefore it’s a good idea to split your messages into<br />

various files stored in separate folders. For example, you may have a folder named ​models<br />

storing translations for the models’ attributes,​ forms​ to translate form-related stuff etc:<br />

● locales<br />

○ models<br />

■ en.yml<br />

■ ru.yml<br />

○ forms<br />

■ en.yml<br />

■ ru.yml<br />

However, by default this is not going to work as <strong>Rails</strong> does not load translations from the nested<br />

directories. To fix this, you’ll need to add the following line of code inside your<br />

config/application.rb​ file:<br />

1 config.i18n.load_path += Dir[<strong>Rails</strong>.root.join('config', 'locales', '**', '*.{rb,yml}')]<br />

load_path​ is a setting to announce your custom translation files and it should do the trick.<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

Take Advantage Of Nesting<br />

It is not advised to store all your translation messages on the same level of nesting (using flat<br />

naming). For example, this is not a recommended practice:<br />

● en:<br />

○ submit: ‘Submit’<br />

○ log_out: ‘Log Out’<br />

○ blog: ‘Browse Blog’<br />

○ errors: ‘Errors were found:’<br />

As you see, all the messages here are messed up <strong>–</strong> they relate to different pieces of the<br />

application but still they are mixed together. It is much better to introduce the parent keys to<br />

nest translations of the same type. For example, you might have something like that:<br />

● en:<br />

○ forms:<br />

■ submit: ‘Submit’<br />

■ errors: ‘Errors were found’<br />

○ main_menu:<br />

■ log_out: ‘Log Out’<br />

■ blog: ‘Browse Blog’<br />

This way the messages are grouped and it is easier to manage them. By the way, when<br />

translating <strong>Rails</strong> models you must follow this principle, as attributes’ names should be nested<br />

properly:<br />

1<br />

2<br />

3<br />

4<br />

5<br />

en:<br />

activerecord:<br />

attributes:<br />

category:<br />

name: 'Name'<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

Employ “Lazy” Lookups<br />

One cool thing about <strong>I18n</strong> in <strong>Rails</strong> is that when naming and nesting your keys properly, you can<br />

write less code inside the views and controllers. How is that possible? Well, let’s say you have a<br />

view called ​about.html.erb stored inside the ​app/views/pages folder. Inside you wish to display<br />

the page’s title:<br />

1 <br />

However, this code can be as simple as:<br />

1 <br />

In order for this to work, you need to nest the ​title​ key under the ​pages.about​scope:<br />

1<br />

2<br />

3<br />

pages:<br />

about:<br />

title: '<strong>About</strong> us'<br />

This works, because the scope is written properly: it contains the folder’s and the view’s name<br />

(​pages​ and ​about​ respectively).<br />

The same approach works for controllers, so having the following translation in place:<br />

1<br />

orders:<br />

2<br />

3<br />

create:<br />

success: '<strong>You</strong>r order is created!'<br />

<strong>You</strong> may create a flash message easily:<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

class OrdersController < ApplicationController<br />

def create<br />

# ...<br />

flash[:success] = t(:success)<br />

end<br />

end<br />

It’s really convenient, so don’t be lazy to employ the “lazy” lookups!<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

Enforce Available Locales<br />

By default each <strong>Rails</strong> application uses English locale and does not explicitly say which<br />

languages are supported. This is okay for applications that are not meant to be localized, but if<br />

you are planning to support multiple locales, then list them inside your ​config.rb​ file:<br />

1 config.i18n.available_locales = [:en, :de, :ru]<br />

This is convenient, because later you can take advantage of this array (fetch it using<br />

<strong>I18n</strong>.available_locales() method). For instance, this array may be employed to generate the proper<br />

routing scopes based on the languages’ codes:<br />

1 scope "(:locale)", locale: /#{<strong>I18n</strong>.available_locales.join("|")}/ do<br />

So you will get routes like ​/en/blog or ​/ru/shop​. Even if the available locales change, the routes<br />

file will not require any changes.<br />

Moreover, this setting can come in handy when implementing a switching locale feature.<br />

Specifically, it can be used to check whether a user is requesting a supported locale and fallback<br />

to the default one if not. It does not really matter how exactly this feature is built, but suppose<br />

you have a method to extract locale data:<br />

1<br />

2<br />

3<br />

def extract_locale<br />

end<br />

parsed_locale = request.subdomains.first<br />

<strong>You</strong> may then easily check whether the requested locale is supported or not:<br />

1<br />

2<br />

3<br />

4<br />

def extract_locale<br />

parsed_locale = request.subdomains.first<br />

<strong>I18n</strong>.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil<br />

end<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

available_locales() returns an array with symbols, so here we convert them to strings and then<br />

check whether the requested language code was found inside. <strong>You</strong> can read more on this<br />

technique at our ​Setting and Managing Locales in <strong>Rails</strong> <strong>I18n</strong>​ article.<br />

Utilize Localized Views<br />

Some developers tend to place all translations inside the YAML files regardless of their length<br />

and complexity. In some cases, however, that’s not really convenient. Suppose you have a page<br />

that looks totally different depending on the chosen language. Of course, you ​might do<br />

something like this:<br />

1<br />

<br />

2<br />

3<br />

<br />

4<br />

5<br />

<br />

Then you would have to define these keys, but the corresponding messages are too large:<br />

1<br />

2<br />

3<br />

welcome: 'Welcome!'<br />

special_offer: 'Here is our cool special offer! And then more text here...'<br />

about: 'Long text goes here.... and more text... even more...'<br />

Alternatively, you may take advantage of ​HTML translations but that’s not going to help a lot <strong>–</strong><br />

you still have long messages that are hard to maintain.<br />

What you can do instead, is stick with the ​localized views that allows you to store totally<br />

different content for each locale. They are employed by simply prefixing your views with the<br />

locale’s code, for example ​about.ru.html.erb and ​about.en.html.erb​. <strong>Rails</strong> will render the proper<br />

view automatically depending on the value returned by the ​<strong>I18n</strong>.locale()​ method.<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

Take Advantage Of Variables<br />

Another important thing some novice developers tend to forget about is the fact that you can<br />

pass variables to your translations in <strong>Rails</strong>. Suppose, for example, you wish to display how many<br />

new messages has the user received. Of course, you may simply employ string interpolation like<br />

this:<br />

1 <br />

But even though this piece of code is small, it looks overly complex and too ugly. Instead, let’s<br />

allow the ​message_count​ to accept a variable:<br />

1 message_count: "%{count} new messages"<br />

Then simply pass a hash to the ​t​ method as the second argument:<br />

1 <br />

Clean and simple. <strong>You</strong> can further extend this example by introducting ​pluralization rules that<br />

also rely on variables.<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

Helper Methods<br />

<strong>Rails</strong> offers us a bunch of powerful ​helper methods that can be used to display datetime and<br />

month select boxes, convert numbers to currency and display distance of time in words. Some<br />

of them work as magic and can really save you from writing many lines of code every time.<br />

However, be warned that some of these methods are somewhat expensive in tems of processing<br />

time. ​distance_of_time_in_words is one of such methods <strong>–</strong> it says how much time has passed since<br />

the provided datetime. If you are planning to use this method extensively in some view (the<br />

typical example is the comments block to say how old the comment is), then maybe it is better<br />

to reconsider and put it away. <strong>You</strong> might think about employing ​fragment caching here but then<br />

you’ll need to constantly expire it. Time runs fast and the phrase “the comment was published X<br />

days ago” should be constantly updated.<br />

Another solution to this problem is performing the calculation on the client-side. For example,<br />

there is a great library called ​Moment.js​ ​that supports this feature (and many others).<br />

Stick With PhraseApp!<br />

Working with translations is hard: you might easily miss some translations for a specific<br />

language which will lead to user’s confusion. And so PhraseApp can make your life easier!<br />

Grab your 14-days trial​. PhraseApp supports many different languages and frameworks,<br />

including JavaScript of course. It allows to easily import and export translations data. What’s<br />

cool, you can quickly understand which translation keys are missing because it’s easy to lose<br />

track when working with many languages in big applications. On top of that, you can<br />

collaborate with translators as it’s much better to have ​professionally done localization for your<br />

website.<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

Conclusion<br />

So, in this article we have discussed some common best practices advised when working with<br />

<strong>I18n</strong> in <strong>Rails</strong>. I hope it will help you crafting maintainable and beautifil applications! Still, if you<br />

think that I have missed something, do not hesitate to share your opinion in the comments.<br />

Also, if you wish to learn more about internationalizing <strong>Rails</strong> applications, you may be<br />

interested in our article ​The Last <strong>Rails</strong> <strong>I18n</strong> Guide <strong>You</strong>’ll Ever Need​.<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp


<strong>Rails</strong> Internationalization (<strong>I18n</strong>) - <strong>Seven</strong> <strong>Best</strong> <strong>Practices</strong> <strong>That</strong> <strong>You</strong> <strong>Should</strong> <strong>Know</strong> <strong>About</strong><br />

phraseapp.com<br />

sales@phraseapp.com<br />

+49-40-357-187-76<br />

ABC-Straße 4<br />

Hamburg, Germany<br />

phraseapp.com | sales@phraseapp.com | +49-40-357-187-76 | twitter.com/phraseapp | facebook.com/phraseapp | linkedin.com/company/phraseapp

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!