stalno u pokretu

< IT & tech


17. 09. 2014. back end * gettext * i18n&l10n * ruby

Ruby gettext internationalization tutorial on using the fast_gettext gem

I originally published this article on Lingohub blog in 2013, while working for the company. Lingohub specializes in providing app localization for developers and translators.</a>

This concludes the Ruby month of my tutorial series. So before I look at the interesting case of the ruby gettext approach to internationalization (using the fast_gettext gem) here are the links to previous articles on internationalization for Ruby that I have covered so far (in case you want to catch up):

Ruby gettext internationalization: What Fast Gettext can do

The topic of our fourth and final article will be covering the fast_gettext gem. Fast Gettext features simplicity of use, clean namespace, it is thread safe, extendible and it supports multiple backends. Since translations are cached after the first use, performance is almost the same for all the backends.

When it comes to the performance, it is claimed on the fast_gettex Github page that it  is much faster and much more efficient then the competition:

Hash FastGettext GetText ActiveSupport I18n::Simple
Speed* 0.82s 1.36s 4.88s 21.77s
RAM* 4K 8K 4480K 10100K
Included backends db, yml, mo, po, logger, chain mo yml (db/key-value/po/chain in other I18n backends)
*50.000 translations with ruby enterprise 1.8.6 through rake benchmark

We already wrote extensively about Gettext in some of our previous articles, you can get back to them in order to read more about PHP internationalization with Gettext or setting up gettext backend for i18n Ruby gem.

Setting up for ruby gettext: Installation of fast_gettext

$ sudo gem install fast_gettext

Setting the text domain and current locale

This should be done once in every Thread (e.g. Rails -> ApplicationController):

FastGettext.text_domain = 'my_app'
# only allow these locales to be set (optional)
FastGettext.available_locales = ['de','en','fr','en_US','en_UK'] 
FastGettext.locale = 'de' 

Translation lookup with fast gettext

include FastGettext::Translation
_('Car') == 'Auto'
_('not-found') == 'not-found'
s_('Namespace|no-found') == 'not-found'
n_('Axis','Axis',3) == 'Achsen' #German plural of Axis
_('Hello %{name}!') % {:name => "Pete"} == 'Hello Pete!'

Managing translations – MO/PO files

PO files are plain text files and can be created in any plain text editor. There is a command line tool from the GNU gettext package that can automatize this process to a degree. After the PO file is ready, it can be converted to an MO file using a command line tool from the same package. We have covered this process in detail in one of our previous articles. The author of fast_gettext has started to work on MO/PO parser/generator, so you can check that project as well.

There are also rake tasks that can be used or customized.

When PO or MO files are ready, it is possible to load either of them:

MO files:

FastGettext.add_text_domain('my_app',:path => 'locale')

PO files:

FastGettext.add_text_domain('my_app',:path => 'locale', :type => :po)
# :ignore_fuzzy => true to not use fuzzy translations
# :report_warning => false to hide warnings about obsolete/fuzzy translations

Managing translations – database

Any model DataMapper/Sequel or any other (non-database) backend can be used. The only thing that is required is to implement the response to self.translation(key, locale) call. The plurals are separated by default with pipes ( ), but it is possible to overwrite this. Database access is cached, so only the first lookup hits the database.
require "fast_gettext/translation_repository/db"
FastGettext::TranslationRepository::Db.require_models #load and include default models
FastGettext.add_text_domain('my_app', :type => :db, :model => TranslationKey)

Managing translations – yaml

If you already have a bunch of yaml files, or you just like the format and yet you want to exploit the performance of fast gettext, no problem, it supports yaml. The syntax and indentation rules should be used as in i18n.

FastGettext.add_text_domain('my_app', :path => 'config/locales', :type => :yaml)

Rails plugin

There is a rails plugin that simplifies the setup.

To install it as a gem, just include it in Gemfile and run bundle:

gem 'gettext_i18n_rails'

The plugin can make use of the i18n Rails localization, you just need to add the files to config/locales directory.

To initialize:

# config/initializers/fast_gettext.rb
FastGettext.add_text_domain 'app', :path => 'locale', :type => :po
FastGettext.default_available_locales = ['en','de'] #all you want to allow
FastGettext.default_text_domain = 'app'

And in your application:

# app/controllers/application_controller.rb
class ApplicationController < ...
  before_filter :set_gettext_locale

Any call to I18n that matches a gettext key will be translated through FastGettext:

I18n.locale <==> FastGettext.locale.to_sym
I18n.locale = :de <==> FastGettext.locale = 'de'

Pluralization

Plurals are selected by index, for example: [‘car’, ‘cars’][index], and the pluralisation rule decides which form will be used. By default, only the plural form for English and other similar languages is supported:  count == 1 ? 0 : 1

If a language that uses a different plural form needs to be supported, a custom pluralisation rule needs to be added either via Ruby:

FastGettext.pluralisation_rule = lamda{|count| count > 5 ? 1 : (count > 2 ? 0 : 2)}

or via mo/po file:

Plural-Forms: nplurals=2; plural=n==2?3:4;

Defaults

If only one text domain is used, setting FastGettext.default_text_domain = ‘app’ is sufficient and no more text_domain= is needed.

If the simple rule of “first availble_locale or ‘en’” is not suficcient, FastGettext.default_locale = ‘de’ can be set.

If no available_locales are set, fallback can be set in default_available_locales.

Loading multiple repositories

Any number of repositories can be used to find a translation, and when the first can not translate a given key, the next one is searched:

repos = [
  FastGettext::TranslationRepository.build('new', :path=>'....'),
  FastGettext::TranslationRepository.build('old', :path=>'....')
]
FastGettext.add_text_domain 'combined', :type=>:chain, :chain=>repos

Logging

It can be useful to keep track of the keys that could not be translated or that were used. In order to do so, a Logger should be added to a Chain:

repos = [
  FastGettext::TranslationRepository.build('app', :path=>'....')
  FastGettext::TranslationRepository.build('logger', :type=>:logger, :callback=>lamda{|key_or_array_of_ids| ... }),
}
FastGettext.add_text_domain 'combined', :type=>:chain, :chain=>repos

If the Logger is the first in the chain, it will record all the used translations. However if it is last, it will record only those that were not found.

Some other useful Ruby gettext packages

Ok, so far we have covered i18n gettext backend (in one of the previous articles) and fast gettext. It does not end there, there are some other solutions as well:

  • Gettext for Ruby – an original package; fast_gettext was created as a response to it; this project has not been updated for some time now
  • GettextI18nRailsJs – an extension of gettext_i18n_rails that makes the PO files available client side via javascript (as JSON)

Conclusion

This brings us to the end of our internationalization for Ruby series. To sum up the various alternatives:

  • Ruby i18n – comes as a Rails default and would be natural first choice for the Rails developers. Can be easily used in plain Ruby or other frameworks as well. Supports multiple backends and resource files. It is quite easy to use.
  • r18n – an alternative to i18n, can be used as a superset to it, has somewhat different approach to translation lookup and also add some interesting features to yaml files (filters).
  • Ruby gettext – has not been maintained for a while. Several other packages have risen as a response to it.
  • Ruby gettext using fast_gettext – very light and yet powerful. Also supports backends and resource files other then gettext, with no or not much additional cost in performance.

Further reading


Related posts


17. 09. 2014. back end * i18n&l10n * ruby
i18n gem advanced features – Ruby on Rails internationalization

Using Different Backends for Ruby i18n gem, chaining backends, caching is an i18n gem advanced feature, fallbacks, Translation Metadata, cascading lookups, translation symlinks, using custom exception handlers
[ reading time: ~ 15 min. ]

17. 09. 2014. back end * i18n&l10n * padrino * ruby * sinatra
Internationalization for Ruby – i18n gem

i18n for plain Ruby & the i18n gem, i18n for Ruby on Rails, i18n for Sinatra, i18n for Padrino
[ reading time: ~ 20 min. ]

17. 09. 2014. back end * i18n&l10n * ruby
Internationalization for Ruby with the r18n gem

Installation and setup, translation lookup, filters for translations processing, pluralization, translating activeRecord models and plain Ruby objects, locale settings, r18n wrappers for rails and sinatra, r18n vs i18n
[ reading time: ~ 9 min. ]

17. 09. 2014. back end * gettext * i18n&l10n * php
PHP internationalization with gettext tutorial

Installation, portable object template files, plurals, an example of a PO file, directory structure, machine object files, gettext caching problems, setting up PHP for internationalization with gettext...
[ reading time: ~ 18 min. ]

15. 08. 2014. api * back end * ruby
API - design, implementation, testing

notes and resources
[ reading time: ~ 3 min. ]

01. 09. 2014. api * back end * bdd / tdd * cucumber * ruby * ruby gem
Developing rails REST API with Cucumber

json_spec, writing the definitions, checking the http headers, testing authentication...
[ reading time: ~ 3 min. ]

03. 09. 2014. api * back end * ruby * ruby gem * security
Devise as authentication solution for rails API

Authentication token, the use of sessions, intsallation and setup, configuring the model, the routes, application controller, custom devise controllers, testing the controllers
[ reading time: ~ 9 min. ]

07. 09. 2014. api * back end * ruby
Building a JSON API in rails

notes
[ reading time: ~ 1 min. ]

12. 09. 2014. back end * ruby
ActiveRecord notes

Uniqueness on a combination of fields, inclusion in array, getting all model associations, overriding model getters and setters, serialization...
[ reading time: ~ 2 min. ]

17. 09. 2014. back end * cakephp * codeigniter * i18n&l10n * php * symfony * yii * zend
Internationalization How To for the 5 most popular PHP frameworks

In this how-To you will read about using CodeIgniter, CakePHP, Zend, Yii and Symfony
[ reading time: ~ 8 min. ]

17. 09. 2014. back end * fuelphp * i18n&l10n * laravel * php
PHP internationalization frameworks: Laravel and FuelPHP

The article covers two more PHP frameworks that are frequently used, Laravel and FuelPHP
[ reading time: ~ 6 min. ]

17. 09. 2014. back end * i18n&l10n * php
PHP internationalization – i18n mechanisms tutorial

Static web and internationalization, dynamic web applications, localizing strings directly in the code, storing the strings in a relational database, message catalogues (string arrays), JSON, use of resource files
[ reading time: ~ 10 min. ]

17. 09. 2014. back end * i18n&l10n
Resource file formats

ini files, properties files, iOS .strings files, YAML files, RESX and RESW files, RESJSON files
[ reading time: ~ 4 min. ]

22. 11. 2014. back end * deployment * ruby
rails deployment

Setting up Debian 7 server on VPS, nginx, php-fpm, ruby, unicorn/phusion passanger, essentials, ssh keys, capistrano...
[ reading time: ~ 2 min. ]

10. 08. 2014. api * back end
Representational state transfer (REST)

notes
[ reading time: ~ 3 min. ]

21. 08. 2014. database * ruby
Adding UUID to rails

notes
[ reading time: ~ 1 min. ]

04. 09. 2014. api * back end
Design principles of the REST-ful JSON API (HATEOAS)

REST, JSON, API, HATEOAS, media types, top level resource representations, url templates, compound documents, urls, responses, errors, JSend response types, HTTP Status codes
[ reading time: ~ 12 min. ]

24. 11. 2014. back end * dev_tool
nginx notes

notes
[ reading time: ~ 1 min. ]

10. 12. 2014. back end * ruby gem
Capistrano

notes
[ reading time: ~ 1 min. ]

Ovaj sajt ne sadrži first party kukije i druge mehanizme za aktivno praćenje poseta/ponašanja (facebook, google analytics, itd...). Na nekim stranicama se nalaze embedovani youtube video klipovi i google mape koji učitavaju svoje kukije.