I may have hinted at it once or twice that I really love how Rails makes Internationalization (I18n) really simple (in stark contrast to what I had to do back then in .NET).
But one thing Rails I18n will not do for you is translating the models in your database, which is to me one of the major problems I often face at work when doing I18n. (Of course there are gems that alleviate that pain)
So today I was sitting down to write a system design for a new application at work that has to be localized in a lot of different languages while still being fast.
After thinking a bit about the problem I decided that doing so in ActiveRecord would be cumbersome to say the least, and I decided for a Document-Database approach.
Since I know MongoDB best I went out and looked at the Mongoid documentation on how to best integrate transparent localization into the system (fully expecting that I'd have to implement this myself).
Well, Mongoid blew my mind:
class Post
include Mongoid::Document
field :title, localized: true
end
That's it, Mongoid will now transparently store the title field in a hash and return the correct value depending on I18n.locale
. We are talking literally zero cost here.
Mongoid even accounts for the reality of translations not always being present so you might want to fallback to another locale instead of not presenting anything. Wow..
So here is how to set this up with fallbacks inside your config/application.rb
:
config.i18n.fallbacks = true
config.i18n.default_locale = :en
That's all, and here is how it works:
post = Post.new(:title => 'Hello World')
puts post.title # => "Hello World"
I18n.locale = :de
puts post.title # => "Hello World"
post.title = "Hallo Welt"
puts post.title # => "Hallo Welt"
I18n.locale = :en
puts post.title # => "Hello World"
Unfortunately this at the moment only works for regular fields, not for relations so I still have to do some work in my current project - but even the field i18n saves me a lot of headache.
Multiple fallbacks: Something that is not really covered in the documentation but becomes apparent when you look at the tests is that you can define a callback chain per locale. Meaning you can tell Mongoid to do go through a list of locales while searching for an existing value.
::I18n.fallbacks[:de] = [ :de, :en, :es ]
Mongoid will now first look for a german value, then fall back to the english one, and if that's not present either it will try the spanish one.
Very handy especially if you run the locale schema down to country codes like 'de-AT', 'de-CH'.
So for example this is the fallback chain for switzerland:
::I18n.fallbacks['de-CH'] = ['de-DE', 'de', 'en']
::I18n.fallbacks['fr-CH'] = ['fr-FR', 'fr', 'en']
::I18n.fallbacks['it-CH'] = ['it-IT', 'it', 'en']
Yes, Switzerland is a mess - but at least it gives you a good example for crazy fallbacks.