Ruby GetText, but 3.5x faster + 560x less memory + simple + clean namespace + threadsave + extendable + multiple backends + Rails3 ready
Pull request Compare This branch is 160 commits behind grosser:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


GetText but 3.5 x faster, 560 x less memory, simple, clean namespace (7 vs 34) and threadsave!

It supports multiple backends (.mo, .po, .yml files, Database(ActiveRecor + any other), Chain, Loggers) and can easily be extended.

Example Rails application


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

*50.000 translations with ruby enterprise 1.8.6 through rake benchmark


1. Install

sudo gem install fast_gettext

2. Add a translation repository

From mo files (traditional/default) FastGettext.add_text_domain('my_app',:path=>'locale')

Or po files (less maintenance than mo) FastGettext.add_text_domain('my_app',:path=>'locale', :type=>:po) # :ignore_fuzzy => true to silence warnings about fuzzy translations # :ignore_obsolete => true to silence warnings about obsolete translations

Or yaml files (use I18n syntax/indentation) FastGettext.add_text_domain('my_app',:path=>'config/locales', :type=>:yaml)

Or database (scaleable, good for many locales/translators) # db access is cached <-> only first lookup hits the db 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)

3. Choose text domain and locale for translation

Do this once in every Thread. (e.g. Rails -> ApplicationController) FastGettext.text_domain = 'my_app' FastGettext.available_locales = ['de','en','fr','en_US','en_UK'] # only allow these locales to be set (optional) FastGettext.locale = 'de'

4. Start translating

include FastGettext::Translation
_('Car') == 'Auto'
_('not-found') == 'not-found'
s_('Namespace|no-found') == 'not-found'
n_('Axis','Axis',3) == 'Achsen' #German plural of Axis

Managing translations


Generate .po or .mo files using GetText parser (example tasks at gettext_i18n_rails)

Tell Gettext where your .mo or .po files lie, e.g. for locale/de/my_app.po and locale/de/LC_MESSAGES/ FastGettext.add_text_domain('my_app',:path=>'locale')

Use the original GetText to create and manage po/mo-files. (Work on a po/mo parser & reader that is easier to use has started, contributions welcome @ pomo )


Example migration for ActiveRecord
The default plural seperator is |||| but you may overwrite it (or suggest a better one..).

This is usable with any model DataMapper/Sequel or any other(non-database) backend, the only thing you need to do is respond to the self.translation(key, locale) call. If you want to use your own models, have a look at the default models to see what you want/need to implement.

To manage translations via a Web GUI, use a Rails application and the translation_db_engine


Try the gettext_i18n_rails plugin, it simplifies the setup.
Try the translation_db_engine, to manage your translations in a db.

Setting available_locales,text_domain or locale will not work inside the evironment.rb, since it runs in a different thread then e.g. controllers, so set them inside your application_controller.

#environment.rb after initializers

class ApplicationController ...
  include FastGettext::Translation
  before_filter :set_locale
  def set_locale
    FastGettext.available_locales = ['de','en',...]
    FastGettext.text_domain = 'frontend'
    session[:locale] = I18n.locale = FastGettext.set_locale(params[:locale] || session[:locale] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')

Advanced features

Abnormal pluralisation

Plurals are selected by index, think of it as ['car', 'cars'][index]
A pluralisation rules decides which form to use e.g. in english its count == 1 ? 0 : 1.
If you have any languages that do not fit this rule, you have to add a custom pluralisation rule.

Via Ruby:

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

Via mo/pofile:

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

Plural expressions for all languages.


If you only use one text domain, 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 for you, set FastGettext.default_locale = 'de'.


Fallback when no available_locales are set


You can use any number of repositories to find a translation. Simply add them to a chain and when the first cannot translate a given key, the next is asked and so forth. repos = ['new', :path=>'....'),'old', :path=>'....') ] FastGettext.add_text_domain 'combined', :type=>:chain, :chain=>repos


When you want to know which keys could not be translated or were used, add a Logger to a Chain: repos = ['app', :path=>'....')'logger', :type=>:logger, :callback=>lamda{|key_or_array_of_ids| ... }), } FastGettext.add_text_domain 'combined', :type=>:chain, :chain=>repos If the Logger is in position #1 it will see all translations, if it is in position #2 it will only see the unfound. Unfound may not always mean missing, if you chose not to translate a word because the key is a good translation, it will appear nevertheless. A lambda or anything that responds to call will do as callback. A good starting point may be examples/missing_translations_logger.rb.


Want a xml version ? Write your own TranslationRepository! #fast_gettext/translation_repository/xxx.rb module FastGettext module TranslationRepository class Wtf define initialize(name,options), [key], plural(*keys) and either inherit from TranslationRepository::Base or define available_locales and pluralisation_rule end end end



  • Add a fallback for Iconv.conv in ruby 1.9.4 -> lib/fast_gettext/vendor/iconv
  • YML backend that reads ActiveSupport::I18n files


Mo/Po-file parsing from Masao Mutoh, see vendor/README


Michael Grosser
Hereby placed under public domain, do what you want, just do not hold me accountable...
Flattr Build Status