Advanced techniques, tips and tricks - Get on Rails with Globalize! Part 5 of 8
Bridge Globalize and TZInfo
Timezones are something that are able to scare the #%@! out of a programmer. Some of the best informed folks even drew the conclusion that timezones must have been specifically designed to do so. ;-)
Luckily Ruby provides the TZInfo library which can be used from within Rails easily with the TZInfo Timezone plugin by Jamis Buck.
Lourens Naudé describes how you can get these work with Globalize and get "DRY timezones indexed by country, with daylight savings time covered.". To read more about this head over to his blog: Bridge Globalize and TZInfo
Preload View Translations in Production
After you're done with your Globlized application, extracted and translated everything and are now preparing stuff for production you might want to consider pre-loading your view translations in one go during the application startup process.
Lourens Naudé has a snippet that does right this. It hooks into the Dispatcher#to_prepare application startup stage and adds all view translations for all supported languages to the translations cache.
Go and grab the code right here: Preloading Globalize view translations in production
Please note that you might need to adjust how that code loops through your supported languages in case that you don't use Globalize's SupportedLanguage class.
Clear Globalize's translations runtime cache
When you're devoloping your Globalized application you might find it useful to know about the fact that Globalize caches ViewTranslations in memory ... and how to clear this cache.
Here's how:
# clear the viewtranslations cache
Locale.translator.cache_reset
Note the fact that this method belongs to the translator
. Don't confuse it with the Locale#clear_cache
method which resets the locale cache, not the translations cache.
Use nice predicated block helpers in your views
Sometimes you want to show or hide stuff depending on whether the current locale is your base_language
or not (or similar conditions). For example you might want to allow creating new items only for the base_language
.
Here's a nice piece of code from Jeremy Voorhis’ Canada on Rails slides that also shows a typical Ruby'ish pattern (Jeremy calls it "predicated blocks") for purposes like this:
# in your app/helpers/application_helper.rb
def base_language_only
yield if Locale.base?
end
def not_base_language
yield unless Locale.base?
end
<!-- in your template -->
<% base_language_only do -%>
<%= link_to 'New product', :action => 'new' %>
<% end -%>
Organize your translations through Globalize's namespaces
Multilingual URLs are a great example for a situation where you might want to namcespace your translations.
Imagine an application that contains "sections" which are available at http://example.com/section
. The following funny dialog from Saimons blog (who originally invented that feature in Globalize) demonstrates nicely why you might want to namespace your translations:
Your boss phones you up and bellows: “None of the site’s links are working in Spanish?”
Oops! You start to investigate and find that the stupid translators, went ahead and translated
'section' => 'sección' (in Spanish)
... which you were using to translate the url:
http://example.com/section => http://example.com/seccion
You: “Jeez, don’t translators know you can’t use non-ascii characters in URLs?”
Boss: “Umm, well no. They’re translators. That’s your job.”
You: “Ah, well, ok I’ll just give them a list of words that are urls and should be translated without accents.”
Boss: “But we use the word ‘section’ in the view as well …”
You: “ummm … Let me think …” (time passes)
You: “Well, what we need is to be able to have two sorts of translation of the word ‘section’, one for the front end and one for the urls.”
... and that’s what namespaced translations are all about. You find a pretty complete writeup about how to use this Globalize feature on Saimons blog.
Alternative Storage Mechanism for Model Translations
Globalize stores all translations in one big database table and transparently JOINs this table into SQL queries as needed.
This is cool and nifty but it comes with some costs. For example you're restricted from using the ActiveRecord :select and :include feature because these would get in conflict with the internal JOIN usage. This in turn leads to incompatibility with Rails plugins that rely on this functionality. Also, sometimes you'd rather want to spare your datbase to do all these joins for performance reasons.
In those cases you might want to consider switching to the alternative storage mechanism for model translations that Globalize comes with since the for-1.2 release.
class Product <ActiveRecord::Base
self.keep_translations_in_model = true
translates :name
end
Basically, what this does is that the translations for the field name
will be kept in extra columns in the products
table, one per language: name_fr
, name_es
, name_de
...
You can find an extensive tutorial on Globalize's Alterative Storage Mechanism on Saimons blog.
Disable Globalize::ViewTranslation's SQL logging
Sometimes you just want less noise in your log files, especially when your Globalize setup runs fine and you don't need it to tell you about every single move it makes. Globalize can be just too noisy then.
Joost Hietbrink thought this, too, and sent me this code so you don't need to suffer from Globalize's chattiness any more.
module Globalize # :nodoc:
class DbViewTranslator
alias_method :orig_fetch_view_translation, :fetch_view_translation
def fetch_view_translation(key, language, idx, namespace = nil)
ActiveRecord::Base.silence do
orig_fetch_view_translation(key, language, idx, namespace = nil)
end
end
end
end
Obviously, this wraps around the fetch_view_translation method and uses ActiveRecord::Base#silence to prevent ActiveRecord from logging the query.
That's handy. Thanks, Joost!
Feedback? Suggestions?
Of course a list like this can't ever really be complete. Please drop me a note if you have any corrections or additions to this list.