FastGettext / Rails integration.
Translate via FastGettext, use any other I18n backend as extension/fallback.
Rails does: I18n.t('syntax.with.lots.of.dots')
with nested yml files
We do: _('Just translate my damn text!')
with simple, flat mo/po/yml files or directly from db
To use I18n calls add a syntax.with.lots.of.dots
translation.
See it working in the example application.
# Gemfile
gem 'gettext_i18n_rails'
Add gettext
if you want to find translations or build .mo files
# Gemfile
gem 'gettext', '>=3.0.2', :require => false
Add the first language using:
rake gettext:add_language[xx]
or
LANGUAGE=xx rake gettext:add_language
where xx
is the lowercased ISO 639-1 2-letter code for the language you want to create.
for example:
rake gettext:add_language[es]
This will also create the locales
directory (where the translations are being stored) and run gettext:find
to find any strings marked for translation.
You can, of course, add more languages using the same command.
Copy default locales with dates/sentence-connectors/AR-errors you want from e.g. rails i18n into 'config/locales'
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_action :set_gettext_locale
Performance is almost the same for all backends since translations are cached after first use.
FastGettext.add_text_domain 'app', :path => 'locale', :type => :po
_('translations')
rake gettext:find
, to let GetText find all translations usedrake gettext:store_model_attributes
, to parse the database for columns that can be translatedcp locale/app.pot locale/de/app.po
for every locale you want to useNew translations will be marked "fuzzy", search for this and remove it, so that they will be used. Obsolete translations are marked with ~#, they usually can be removed since they are no longer needed
Dynamic translations like _("x"+"u")
cannot be found. You have 4 options:
N_('xu')
somewhere else in the code, so the parser sees itN_('xu')
in a totally separate file like locale/unfound_translations.rb
, so the parser sees itFastGettext.add_text_domain 'app', :path => 'locale'
rake gettext:pack
to write binary GetText .mo filesMost scalable method, all translators can work simultaneously and online.
Easiest to use with the translation database Rails engine.
Translations can be edited under /translation_keys
FastGettext::TranslationRepository::Db.require_models
FastGettext.add_text_domain 'app', :type => :db, :model => TranslationKey
I18n.locale <==> FastGettext.locale.to_sym
I18n.locale = :de <==> FastGettext.locale = 'de'
Any call to I18n that matches a gettext key will be translated through FastGettext.
Car|Model means Model in namespace Car. You do not have to translate this into english "Model", if you use the namespace-aware translation
s_('Car|Model') == 'Model' #when no translation was found
If you trust your translators and all your usages of % on translations:
# config/environment.rb
GettextI18nRails.translations_are_html_safe = true
String % vs html_safe is buggy
My recommended fix is: require 'gettext_i18n_rails/string_interpolate_fix'
ActiveRecord error messages are translated through Rails::I18n, but
model names and model attributes are translated through FastGettext.
Therefore a validation error on a BigCar's wheelssize needs `('big car')and
_('BigCar|Wheels size')`
to display localized.
The model/attribute translations can be found through rake gettext:store_model_attributes
,
(which ignores some commonly untranslated columns like id,type,xxx_count,...).
Error messages can be translated through FastGettext, if the ':message' is a translation-id or the matching Rails I18n key is translated.
Define a translation for "I need my rating!" and use it as message.
validates_inclusion_of :rating, :in=>1..5, :message=>N_('I need my rating!')
validates_inclusion_of :rating, :in=>1..5
Make a translation for the I18n key: activerecord.errors.models.rating.attributes.rating.inclusion
Add a translation to each config/locales/*.yml files
en:
activerecord:
errors:
models:
rating:
attributes:
rating:
inclusion: " -- please choose!"
The rails I18n guide can help with Option B and C.
FastGettext supports pluralization
n_('Apple','Apples',3) == 'Apples'
Abnormal plurals like e.g. Polish that has 4 different can also be addressed, see FastGettext Readme
When you run
rake gettext:find
by default the following files are going to be scanned for translations: {app,lib,config,locale}/*/.{rb,erb,haml,slim}. If you want to specify a different list, you can redefine files_to_translate in the gettext namespace in a file like lib/tasks/gettext.rake:
namespace :gettext do
def files_to_translate
Dir.glob("{app,lib,config,locale}/**/*.{rb,erb,haml,slim,rhtml}")
end
end
By default a single application text domain is created (named app
or if you load the environment the value of FastGettext.text_domain
is being used).
If you want to have multiple text domains or change the definition of the text domains in any way, you can do so by overriding the :setup
task in a file like lib/tasks/gettext.rake:
# Remove the provided gettext setup task
Rake::Task["gettext:setup"].clear
namespace :gettext do
task :setup => [:environment] do
domains = Application.config.gettext["domains"]
domains.each do |domain, options|
files = Dir.glob(options["paths"])
GetText::Tools::Task.define do |task|
task.package_name = options["name"]
task.package_version = "1.0.0"
task.domain = options["name"]
task.po_base_directory = locale_path
task.mo_base_directory = locale_path
task.files = files
task.enable_description = false
task.msgmerge_options = gettext_msgmerge_options
task.msgcat_options = gettext_msgcat_options
task.xgettext_options = gettext_xgettext_options
end
end
end
end
The default options for parsing and create .po
files are:
--sort-by-msgid --no-location --no-wrap
These options sort the translations by the msgid (original / source string), don't add location information in the po file and don't wrap long message lines into several lines.
If you want to override them you can put the following into an initializer like config/initializers/gettext.rb:
Rails.application.config.gettext_i18n_rails.msgmerge = %w[--no-location]
Rails.application.config.gettext_i18n_rails.msgcat = %w[--no-location]
Rails.application.config.gettext_i18n_rails.xgettext = %w[--no-location]
or
Rails.application.config.gettext_i18n_rails.default_options = %w[--no-location]
to override both.
You can see the available options by running rgettext -h
, rmsgcat -f
and rxgettext -h
.
If you want to disable translations to model name and attributes you can put the following into an initializer like config/initializers/gettext.rb:
Rails.application.config.gettext_i18n_rails.use_for_active_record_attributes = false
And now you can use your I18n yaml files instead.
If want to use your .PO files on client side javascript you should have a look at the GettextI18nRailsJs extension.
Michael Grosser
michael@grosser.it
License: MIT