--- 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 langauge, see this page on 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. To deploy, say, 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 The ``pot``-file at ``locale/app.pot`` acts as the template for PO files. When new translation strings have been added to the source, this ``pot``-file can be updated using the script at ``script/generate_pot.sh``. This looks for new translatable strings in the source and creates entries in the ``pot``-file. For more details about the translations, see the page about [translating Alaveteli]({{ site.baseurl }}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 python source code, as long as you import ```_```, ```n_```, etc. ## 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://rubygems.org/gems/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) ## I18n in URLs We have tried using the [translate_routes plugin](https://github.com/raul/translate_routes) to localize URLs. This looks up URL segments as translation strings in a YAML file at```config/i18n-routes.yml```. However, we no longer use it because we found it was overly complex.