--- layout: page title: Internationalisation (for devs) --- # Internationalisation in the code

This page describes some technical aspects of internationalising the Alaveteli code. It's mostly aimed at devs who are working on the codebase — if you just want to translate Alaveteli into your own language, see translating Alaveteli instead.

## Deployment notes Deployed translations for the project live in ``locale/``. Translations live in the project page at [Transifex](https://www.transifex.net/projects/p/alaveteli/) and should be submitted there. For example, to deploy English and Spanish translations at once: * Ensure their PO files are at ```locale/en/app.po``` and ```locale/es/app.po``` (for example, by downloading them from Transifex) * Set AVAILABLE_LOCALES to en es To update the `.pot` and `.po` files for each language, run: bundle exec rake gettext:store_model_attributes followed by: bundle exec rake gettext:find If `gettext:find` only creates the file `locale/im-config.pot` then you need to unset the `TEXTDOMAIN` environment variable and try again. For more details about the translations, see the page about [translating Alaveteli]({{ site.baseurl }}docs/customising/translation/). ## Technical implementation details ### Getting the current locale This is complicated by the fact that there are two competing ways to define a locale+territory combination. The POSIX (and gettext and Transifex) way is like `en_GB`; the Rails way is like `en-US`. Because we are using gettext and Transifex for translations, we must deal with both. Wherever you need to know the Rails version of the currently selected locale, use `I18n.locale`; wherever you want to know the POSIX version of the locale, use `FastGettext.locale`. ## I18n in templates Before you add i18n strings to the source, you should read [internationalisation guidelines](http://mysociety.github.io/internationalization.html) that apply to all our projects. Some hints for adding the strings into the Alaveteli code: * Simple strings: ```<% = _("String to translate") %>``` * Strings that include variables: give the translator a hand by inserting strings that can be interpolated, so the variable has meaning. For example, ```<%= "Nothing found for '" + h(@query) + "'" %>``` might become ```<%= _("Nothing found for '{{search_terms}}'", :search_terms => h(@query)) %>``` * Strings containing numbers: ```<%= n_('%d request', '%d requests', @quantity) % @quantity %>``` * We allow some inline HTML where it helps with meaningful context, for example: ``` _('Browse all or ask us to add it.', :browse_url => @browse_url, :add_url => @add_url) ``` Similar rules can apply to strings in the Ruby source code. ## Programmatic access of translated PublicBodies Apart from the templates, the only other area of i18n currently implemented is in the PublicBodies. The implementation allows for getting different locales of a PublicBody like so: ```ruby PublicBody.with_locale("es") do puts PublicBody.find(230).name end ``` Usually, that's all the code you need to know about. There's a method ```self.locale_from_params()``` available on all models which returns a locale specified as ```locale=xx``` in the query string, and which falls back to the default locale, that you can use in conjunction with the ```with_locale``` method above. All the joining on internal translation tables should usually be handled automagically -- but there are some exceptions, that follow below. ### Overriding model field setters Internally, we use the [Globalize plugin](https://github.com/globalize/globalize) to localize model fields. Where column "foo" has been marked in the model as ```:translates```, globalize overrides ```foo.baz = 12``` to actually set the value in column ```baz``` of table ```foo_translations```. A side effect of the way it does this is that if you wish to override a specific attribute setter, you will need to explicitly call the Globalize machinery; something like: ```ruby def name=(name) globalize.write(self.class.locale || I18n.locale, "name", name) self["name"] = short_name # your other stuff here end ``` ### Searching The ```find_first_by_``` and ```find_all_by_``` magic methods should work. If you want to do a more programmatic search, you will need to join on the translation table. For example: ```ruby query = "#{translated_attr_name(someattr) = ? AND #{translated_attr_name('locale')} IN (?)" locales = Globalize.fallbacks(locale || I18n.locale).map(&:to_s) find( :first, :joins => :translations, :conditions => [query, value, locales], :readonly => false ) ``` You may also need to do some lower-level SQL joins or conditions. See ```PublicBodyController.list``` for an example of a query that has a condition that is explicitly locale-aware (look for the ```locale_condition``` variable) ## Translation and releases The release manager will enforce a translation freeze just before a new release is cut. During such time, you must not introduce new strings to the code if your work is due for inclusion in this release. This is necessary to allow translators time to complete and check their translations against all the known strings. See more about [translating Alaveteli]({{ site.baseurl }}docs/customising/translation/).