Multi language with Rails app to prepare YML file - ruby-on-rails

I am building a car rental app with Ruby on Rails and currently I have been coding with English. Basically I have an user and car model, where user can login, sign in, log out and list their car.
I would like to implement another language allowing the user to select English or Spanish from a dropdown. I have default error messages returns from the controller actions such as "can't be blank, already used" etc. And I also have custom JS messages, such as if user successfully adds a car, jQuery returns "your car has been published". Lastly, I also have Flash messages. But I do not know how should I handle errors, custom JS and Flash messages in the YML file according to user's language selection.

It actually really depends on the structure of your YAML file, for example, for ActiveRecord error messages:
it:
errors:
format: "%{attribute} %{message}"
messages: &errors_messages
empty: "non può essere vuoto"
blank: "non può essere lasciato in bianco"
If you want to access I18n (a.k.a Internationalization) outside of views, you can use I18n.t(your_key), which is in fact the helper you have access to in your views. So for example, if you wish to translate a notice in a controller you could be doing something like:
def your_action
redirect_to other_path, notice: I18n.t("notices.successful_action")
end
And then having in your YAML the correspondent structure:
en:
notices:
successful_action: "You did it!"
Hope this helps!

I do not know how should I handle error, custom js and flash messages in yml file according to user's language selection
For Rails app-related translations in general, including error messages, the Rails i18n gem has you covered. You can see what languages have been translated in the gem's locale file.
For specifically overriding error messages or creating new ones, see The Rails Guide sections on looking up translations and translations for ActiveRecord models to see what keys in your YAML files you should be putting your translations under.
For flash messages, have a look at this StackOverflow question to see how you can use lazy lookups (eg t('.notice')) in your controllers.
For the ability to use translations in Javascript files in the same way you do in Ruby (eg I18n.t("my_translation"), and be able to manage those translations in your YAML files with the rest of your Rails translations, have a look at the i18n-js gem. Setup instructions are in the README file.

Related

ActiveRecord errors, how do I find out the locale path to change the messages?

I know you can change ActiveRecord error messages in the locales like this:
en:
activerecord:
errors:
models:
user:
attributes:
email:
blank: "is required"
But that is an incredibly specific tree—how do I know what ActiveRecord is looking for? Is there a way to inspect an error to see what path ActiveRecord is searching? Or is there a way to generate a complete en.yml with all messages in there?
The ActiveModel::Error class (what you get if you call record.errors) has a details attribute, which is a hash of the attributes and their errors, including the error key.
foo.valid?
=> false
foo.errors.details
=> {:user=>[{:error=>:blank}]}
The ActiveModel source has an en.yml, which will give you the canonical set of keys. The rails-i18n gem includes translations for many of the standard keys, but there are definite gaps (that is, some locales are complete, some are not).
As #Mirror318 says, you can see the set of error message translations for the current locale by running I18n.t 'errors'. If your current locale is en.yml that will be the same as the canonical list; otherwise it is the available set of translated messages.
In a Rails console, you can enter:
I18n.t 'errors'
And that will return a hash of all current error messages. You can adjust that hash via the locale .yml file

I18n - locale file - use ruby - use translation()

In a I18n locale file (eg el.yml):
can I use translation method in order to use an already defined translation?
can I use ruby code inside?
For example, in the locale yml file, in several places I want to use the word "Invalid". So one approach is to translate the word "Invalid" each time
Another approach is to translate it once, at the top, and then in each translation inside the locale yml which contains this word I could use something like t(:invalid)..
eg:
el:
# global translations - in order to be used later in the file
locked: 'kleidomeno'
invalid: 'mi egiro'
password: 'kodikos'
devise:
failure:
invalid: "#{Invalid} %{authentication_keys} or #{password}."
locked: "Your account is #{locked}."
last_attempt: "You have one more attempt before your account is #{locked}."
not_found_in_database: "#{invalid} %{authentication_keys} ή #{password}."
can I use translation method in order to use an already defined translation?
Yes, that's what I18n translation comes for, e.g.
t 'something'
can I use ruby code inside?
No, it's a .yml file, which does not accept Ruby or any other programming languages.
Another approach is to translate it once
To translate once, you can write a new Rake task to generate the target yml for you.
Or maybe, wrap the official translation function with a new method, which can recognize your custom string syntax:
# custom translate
def ct(msg)
msg.gsub(/#\{\s*([^\}]+)\s*\}/) {|_| t $1}
end
Call it like:
ct 'Your account is #{locked}.'
I think you'd better remove these failure strings out of your yml file if so.

Internationalizing whole text with markup in Rails 3

What's the best practice for internationalizing, say, a Terms of Service document in Rails 3? I can think of two options:
Creating a partial for each locale and choosing which one to load according to the current user's locale.
<li><%= I18n.t :tos_paragraph_1 %></li><li><%= I18n.t :tos_paragraph_2 %></li>
None of these seems like a good solution. Any ideas?
There are a few solutions, but if I were doing this for a production project, I would probably do something like the following:
Create files for your translated Terms of Services in config/locales/terms/, naming them terms.en.html, replacing en with the locale for each translation and html with the format of the file (e.g. you could use Haml, Markdown, etc.).
Create the following helper methods (put them in app/helpers/application_helper.rb to use them everywhere, but you can put them in whatever helper file you need/want):
def localized_document_for(document, locale)
raise ArgumentError.new('nil is not a valid document') if document.nil?
raise I18n::InvalidLocale.new('nil is not a valid locale') if locale.nil?
localized_document = path_for_localized_document(document, locale)
raise MissingTranslationData unless File.exists?(localized_document)
# If you're using Markdown, etc. replace with code to parse/format your document
File.open(localized_document).readlines.join
end
def path_for_localized_document(document, locale)
"#{Rails.root}/config/locales/#{document}/#{document}.#{locale.to_s}.html"
end
Now, in your views, you can use localized_document_for('terms', I18n.locale) any time you need to get the contents of the Terms of Service in the user's language. Now the code you're using to fetch the document is DRY (you can easily fetch other documents by creating another directory in config/locales and changing the value of the document argument), and your translated documents are stored in their own directory and can easily be edited (and don't depend on YAML, which brings no value to storing a single document in a file).
Note that if you wanted to do it the "Rails 3 Way," you could use the I18n::Backend::Chain (see https://github.com/svenfuchs/i18n/blob/master/lib/i18n/backend/chain.rb), and pass in I18n::Backend::Simple.new along with a custom backend that reads the files as necessary, but for a one-off deal I believe the helpers work sufficiently.
Creating a partial for each language is definitly undry.
The Rails way is the second option you suggest with a .yml for each language.
The doc provides great examples to let you organize the yml according to each page. Thus you have shortcuts for all your variables.
See http://guides.rubyonrails.org/i18n.html

Supporting different locale regions using Rails i18n

I'm using the standard Rails I18n API to localise some of our views. This is working really well, but we now have a few use cases for regional changes to the en locale.
The API guide mentions that this isn't supported directly, and other plugins should be used. However, I'm wondering whether there's a simpler way to do this.
I already have en.yml, so in theory I could just create en-AU.yml and en-US.yml which are effectively clones of en.yml but with a few regional changes applied. I could then add additional English - American and English - Australian options to our configuration which would map to the new region-specific locales and allow users to use a region-specific locale.
The only problem I can think of with this is that it isn't DRY -- I would have duplicate translations for all common English words. I can't see a way around this.
Are there any other disadvantages to this approach, or should I just bite the bullet and dive into one of the plug-ins such as Globalize2 instead?
The rails-i18n-translation-inheritance-helper is getting a bit old now, so here's my approach for a Rails 3.2 project.
If you keep both en-US and en-AU in the same en.yml file you can use the yml repeated node to have a super en section:
For example:
en: &en
errors:
messages:
expired: "has expired, please request a new one"
not_found: "not found"
en-US:
<<: *en
en-AU:
<<: *en
errors:
messages:
not_found: "tis not found"
I'm using translation inheritance helper plugin for this.
In newer versions of Rails/i18n, they've added a fallback feature. Works similar to the outdated translation inheritance helper gem
see this answer for more info: Fall back to default language if translation missing

Plugin to use Ruby on Rails Simple I18n backend with translations overridable in the database?

Hoping some learned Rails developers here can recommend an existing Ruby on Rails plugin or gem that allows you to continue using the Simple I18n backend whilst allowing you to optionally specify translations in the database.
Here's why:
I have one Rails app used for many websites. For the example I'll just use 2 websites:
Website 1: Leprechauns R Us
Website 2: Unicorns R Us
Most translations are the same for both websites, but occassionally I want to override a translation. For example, in my en-US.yml file I have the following translation:
view_all: View all
And for most websites this translation is fine, including for website 1 (Leprechauns) where I'm happy to use "View all".
However, for website 2, I'd like to use "View all Unicorns" as the view_all translation and I'd like to specify this in the database. For maintenance reasons I don't want to specify this override in a YAML file.
Many thanks,
Eliot
In the end I opted to take advantage of Rails' I18n::Backend::Simple's ability to process both .yml files and .rb files as locale dictionaries.
Artifacts created:
DB migration to create a translations table with columns: locale, key, text
Translation model to map to the translations table
Class method to_locale_hash on Translation model that returns a locale keyed hash as required by I18n::Backend::Simple.load_rb
A single-line file located at config/translations.rb with the line 'Translation.to_locale_hash'
For the source code see the Spree extension (sorry not in a Rails plugin structure, it will be easy to move over to a plugin should you require it) here:
http://github.com/eliotsykes/spree-i18n-db/tree/master

Resources