Translating » History » Version 70

Tomer Brisker, 04/11/2019 12:37 PM

1 6 Lukas Zapletal
h1. Translating for contributors
2 1 Lukas Zapletal
3 7 Lukas Zapletal
h2. General tips
4 7 Lukas Zapletal
5 7 Lukas Zapletal
It is important not to change punctuation and whitespace. For example if the English string is "blah." it must be translated as "xyz." with the dot at the end. The same for an extra space - e.g. "blah " must be "xyz ". Although we try to eliminate all the extra spaces, there are rare cases where we need them. There is a checker (pofilter) which is executed regularly by developers to catch and fix all these types of mistakes. 
6 7 Lukas Zapletal
7 32 Dominic Cleal
There are model names in the translation strings, you can get the full list here:
8 7 Lukas Zapletal
9 7 Lukas Zapletal
These model names are in two formats: "Model name" (name of the database table) and "Modelname|Column name" for column name. Here are few examples how to translate them:
10 7 Lukas Zapletal
11 7 Lukas Zapletal
 _('Compute resource') -> "Compute Resource"
12 7 Lukas Zapletal
13 7 Lukas Zapletal
 _('ComputeResource|Description') -> "Description"
14 7 Lukas Zapletal
15 7 Lukas Zapletal
Several models have prefixes in the form something/ or Something:: - you can ignore these. Example:
16 7 Lukas Zapletal
17 7 Lukas Zapletal
 _('Audited/adapters/active record/audit') -> "Audit"
18 7 Lukas Zapletal
19 7 Lukas Zapletal
 _('Audited::Adapters::ActiveRecord::Audit|Associated name') -> "Associated Name"
20 7 Lukas Zapletal
21 6 Lukas Zapletal
h2. Using Transifex
22 6 Lukas Zapletal
23 6 Lukas Zapletal
Go to and register/login. Then you can use the Transifex interface to do all translations. The project on Transifex automatically updates when we add new strings into git. Foreman team regularly downloads new translations to the develop branch in git as well, therefore there is no action needed when you finish with translations. It will be pulled eventually (e.g. before the next release).
24 6 Lukas Zapletal
25 6 Lukas Zapletal
Read the tips bellow if you want to start translating now.
26 1 Lukas Zapletal
27 2 Lukas Zapletal
h2. Manually
28 2 Lukas Zapletal
29 2 Lukas Zapletal
If you prefer, you can edit PO files directly using your preferred editor. Please make sure the encoding of the files is UTF-8. It is also recommended to test your translations before submitting a Pull Request on the github using either:
30 2 Lukas Zapletal
31 28 Lukas Zapletal
  foreman# rake locale:pack
32 1 Lukas Zapletal
33 2 Lukas Zapletal
34 1 Lukas Zapletal
35 7 Lukas Zapletal
  foreman# make -C locale check all-mo
36 2 Lukas Zapletal
37 29 Lukas Zapletal
Note that locale:pack is an alias for gettext:pack which is only avaiable in the development environment.
38 29 Lukas Zapletal
39 1 Lukas Zapletal
The above command should not print any error message. Also you should start Foreman UI and see if your translations do fit (sometimes longer strings can wrap or even break the UI). If you start Foreman in the production mode, you need to do one of the above commands every time you change your translation. In the development mode, you only need to restart Foreman to see the changes.
40 1 Lukas Zapletal
41 56 Benjamin Papillon
Note: For CentOS/RHEL6 systems, you need the following packages : 
42 56 Benjamin Papillon
43 56 Benjamin Papillon
  gettext make
44 56 Benjamin Papillon
45 1 Lukas Zapletal
More info about contributing your translation directly is on our [[Contribute]] wiki page. 
46 1 Lukas Zapletal
47 33 Lukas Zapletal
h2. Changing language
48 33 Lukas Zapletal
49 33 Lukas Zapletal
To change your language, to to User account page and change our language from Browser default to your preferred one. (This is work in progress)
50 33 Lukas Zapletal
51 33 Lukas Zapletal
To quickly change language without going to user settings add ?locale=XX to any Foreman URL. For example visit:
52 33 Lukas Zapletal
53 33 Lukas Zapletal
54 33 Lukas Zapletal
55 39 Lukas Zapletal
and Foreman will remember the language setting for the whole session. Note: The current user needs to have "Browser Locale" set in the account settings, otherwise this will not work.
56 33 Lukas Zapletal
57 6 Lukas Zapletal
h1. Translating for developers
58 1 Lukas Zapletal
59 15 Lukas Zapletal
h2. Extracting strings
60 15 Lukas Zapletal
61 15 Lukas Zapletal
There are several rules to follow when marking strings for translations with _("") and similar functions:
62 15 Lukas Zapletal
63 30 Dominic Cleal
 * To translate a string use @_("My string")@
64 45 Lukas Zapletal
 * To translate string with a parameter use @_("String with param: %s") % param@
65 45 Lukas Zapletal
 * To translate string with more than one parameters do not use @_("Params: %s and %s") % [param1, param2]@ which is translator-unfriendly
66 45 Lukas Zapletal
 * Therefore for more than one parameters use @_("Params: %{a} and %{b}") % {:a => foo, :b => bar}@
67 62 Tomer Brisker
 * Note that parameters are not translated. Variables should not contain text that needs translation.
68 30 Dominic Cleal
 * To mark something for translation (but not translate) use @N_("String")@
69 30 Dominic Cleal
 * To use plural form use @n_("One", "Two", number)@ - note this function always accepts three parameters as the base language is usually English but translators are able to define as many plural forms as they need.
70 45 Lukas Zapletal
 * Plural forms are usually used with one parameter, do not forget to add trailing parameter to it: @n_("%s minute", "%s minutes", @param) % @param@
71 47 Lukas Zapletal
 * When using strings with ERB (e.g. for deface gem), escape properly: n_('Test <%%= %{var1} %%>') % { :var1 => "xxx" }
72 47 Lukas Zapletal
 * Note that gettext does not extract from interpolation - this will not work: "blah #{_('key')} blah"
73 30 Dominic Cleal
74 30 Dominic Cleal
h3. Models and columns
75 30 Dominic Cleal
76 30 Dominic Cleal
 * To render name from model use @s_("Modelname|Fieldname")@ - check @./locale/model_attributes.rb@ for all models/fields which are accepted.
77 30 Dominic Cleal
 * All our form helpers have been enriched and understand model and column names - if the field is in the model_attribute.rb (above), it does not need to be translated explicitly (e.g. text_f f, :name is enough and will be translated to "Modelname|Name")
78 30 Dominic Cleal
79 59 Lukas Zapletal
h3. Exceptions
80 59 Lukas Zapletal
81 59 Lukas Zapletal
We have defined two exceptions `Foreman::Exception` and `Foreman::WrappedException`. Both are generic exceptions which are i18n-friendly. Use the following style to properly format messages:
82 59 Lukas Zapletal
83 59 Lukas Zapletal
84 59 Lukas Zapletal
  raise"Localized error message"))
85 59 Lukas Zapletal
  raise"Localized error message: %s", substring_message))
86 59 Lukas Zapletal
  raise"Localized error message %{a} and %{b}", {:a => a, :b => b}))
87 63 Daniel Lobato Garcia
  raise, N_("Localized error message"))
88 59 Lukas Zapletal
89 59 Lukas Zapletal
90 59 Lukas Zapletal
It is _very_ important to use `N_` and not the simple `_` (underscore) function and to avoid using Ruby string interpolation, because those exceptions prints out error code which are generated from exception class names and main (untranslated) messages. There is also a rake task that goes through our codebase and generates list of all possible error codes which we keep on the wiki page [[ErrorCodes]].
91 59 Lukas Zapletal
92 60 Lukas Zapletal
If an exception is needed to be untranslated, it can be used without underscore functions. Beware, that parameters can be also passed, but Ruby only support hash interpolation:
93 60 Lukas Zapletal
94 60 Lukas Zapletal
95 60 Lukas Zapletal
  raise"This works fine")
96 60 Lukas Zapletal
  raise"This works fine %{foo}", {:foo => "too"})
97 60 Lukas Zapletal
  raise"But this %{s} will not work", 'example')
98 60 Lukas Zapletal
99 60 Lukas Zapletal
100 67 Lukas Zapletal
Please note the difference between using Ruby interpolation (@%@) vs passing options into exception (@,@). In the first case, the ERF error code is miscalculated for different outputs, the latter is correct - any input leads to the same error code:
101 67 Lukas Zapletal
102 67 Lukas Zapletal
103 67 Lukas Zapletal
>"Localized error message: %s" % "Incorrect usage 1")).code
104 67 Lukas Zapletal
=> "ERF42-5336"
105 67 Lukas Zapletal
>"Localized error message: %s" % "Incorrect usage 2")).code
106 67 Lukas Zapletal
=> "ERF42-1122"
107 67 Lukas Zapletal
108 67 Lukas Zapletal
>"Localized error message: %s"), "Correct usage 1").code
109 67 Lukas Zapletal
=> "ERF42-6923"
110 67 Lukas Zapletal
>"Localized error message: %s"), "Correct usage 2").code
111 67 Lukas Zapletal
=> "ERF42-6923"
112 67 Lukas Zapletal
113 67 Lukas Zapletal
114 67 Lukas Zapletal
115 67 Lukas Zapletal
116 30 Dominic Cleal
h3. Helping translators
117 30 Dominic Cleal
118 1 Lukas Zapletal
 * Do not break strings with newlines because then the strings have many whitespace and it looks confusing for translators like "blah    \\n     blah". If you must separate string on several lines, you can use HEREDOC or you can contatenate strings like "line1" + "line2" because Ruby Gettext detects them both.
119 30 Dominic Cleal
 * If you want to leave a note to the translator, just drop a comment before the string in the format of @# TRANSLATORS: your comment here@
120 1 Lukas Zapletal
 * Note that all HEREDOC strings are automatically extracted, when adding API documentation descriptions via HEREDOC, leave a message to translators not to translate these (API documentation will not be translated at the moment).
121 30 Dominic Cleal
122 31 Dominic Cleal
h3. JavaScript
123 31 Dominic Cleal
124 58 Walden Raines
 * Both @__@ and @n__@ functions are available and work in much the same way as in Ruby, with the following exceptions..
125 58 Walden Raines
 * Interpolation of values uses @Jed.sprintf@ on the translated string, so for a single parameter: @Jed.sprintf(__("Foo: %s"), "bar")@
126 69 Tomer Brisker
 * Multiple parameters must be named: @Jed.sprintf(__("Example: %(foo)s %(bar)s"), {foo: "a", bar: "b"})@. Note that named parameter must include the trailing @s@ for the interpolation to work properly.
127 31 Dominic Cleal
128 30 Dominic Cleal
h3. Storing strings
129 30 Dominic Cleal
130 25 Lukas Zapletal
 * Strings get extracted into ./locale/foreman.pot file, model and column names are in ./locale/model_attributes.rb
131 66 Dominic Cleal
 * Additional Rails strings are provided by the rails-i18n gem
132 31 Dominic Cleal
 * PO files are converted into JSON objects to be consumed by Jed, stored at @./app/assets/javascripts/locale/$LANG/app.js@
133 15 Lukas Zapletal
 * For more info go here:
134 15 Lukas Zapletal
135 15 Lukas Zapletal
From time to time it is good to extract strings and update translations with incoming strings, so translators are able to work on them. We usually do this before releases, but it is good idea to do this on a weekly/monthly basis. For string extractions, please *do not* use rake gettext:find but use
136 15 Lukas Zapletal
137 15 Lukas Zapletal
  foreman# rake locale:find
138 15 Lukas Zapletal
139 21 Lukas Zapletal
because it also extracts model names and columns and filters them plus it adds some notes to translators (see locale:find_model rake task). Although locale:find does some checks for malformed strings, it is good idea to run additional pofilter check which is able to find many mistakes like trailing whitespace and others:
140 21 Lukas Zapletal
141 21 Lukas Zapletal
  foreman# make -C locale check -j4
142 21 Lukas Zapletal
  foreman# make -C locale clean
143 15 Lukas Zapletal
144 15 Lukas Zapletal
h2. Generating gettext translate tables
145 15 Lukas Zapletal
146 15 Lukas Zapletal
For production environment, you need to compile PO files into binary translate tables (MO files). This is *not needed* for development or test environments as in these modes Foreman reads PO files directly.
147 15 Lukas Zapletal
148 15 Lukas Zapletal
To generate gettext MO files, you can do either
149 15 Lukas Zapletal
150 15 Lukas Zapletal
  foreman# rake gettext:pack
151 15 Lukas Zapletal
152 15 Lukas Zapletal
153 15 Lukas Zapletal
154 15 Lukas Zapletal
  foreman# make -C locale
155 15 Lukas Zapletal
156 15 Lukas Zapletal
Both tools generate the same result, the latter is a bit faster and allows additional checks (see locale/Makefile targets). If you install from distribution packages, you do not need to run this because everything has been pre-compiled already.
157 15 Lukas Zapletal
158 8 Lukas Zapletal
h2. Adding new language
159 6 Lukas Zapletal
160 70 Tomer Brisker
Adding new language into Foreman is easy. Create a new directory for it under the locale directory and run the `rake locale:find` task.
161 12 Lukas Zapletal
162 70 Tomer Brisker
Add the language to Transifex using their web interface.
163 2 Lukas Zapletal
164 6 Lukas Zapletal
h2. How to pull translations
165 1 Lukas Zapletal
166 4 Lukas Zapletal
To get updated translations from Transifex you will need account there ( and the tx cli tool.
167 4 Lukas Zapletal
168 4 Lukas Zapletal
On Fedora:
169 4 Lukas Zapletal
170 51 Lukas Zapletal
    # yum -y install transifex-client gettext make intltool
171 4 Lukas Zapletal
172 4 Lukas Zapletal
On Debian:
173 4 Lukas Zapletal
174 51 Lukas Zapletal
    # apt-get install transifex-client gettext make intltool-debian
175 4 Lukas Zapletal
176 4 Lukas Zapletal
Then configure your account:
177 4 Lukas Zapletal
178 51 Lukas Zapletal
    $ cat ~/.transifexrc
179 51 Lukas Zapletal
180 51 Lukas Zapletal
hostname =
181 51 Lukas Zapletal
username = <your_username>
182 51 Lukas Zapletal
password = <your_password>
183 51 Lukas Zapletal
token =
184 4 Lukas Zapletal
185 48 Lukas Zapletal
Note that token is empty, should not be set. Then prepare new topic branch (because the following command will make new commits to your git repo):
186 1 Lukas Zapletal
187 51 Lukas Zapletal
    git checkout -b update-translations
188 4 Lukas Zapletal
189 48 Lukas Zapletal
Before you start check if there are new languages on the Transifex site that are worth adding into Foreman (are close to 100 %). You need to download all po files manually and create the proper directory structure. Make sure the PO file header is correct, specifically plural settings and when necessary edit .tx/config file to add a mapping there.
190 43 Dominic Cleal
191 55 Dominic Cleal
Now you can update translations from Transiflex and Rails i18n upstream, regenerate the JavaScript locale files, extract new strings (but throw away PO files merge - we do not need that when using
192 48 Lukas Zapletal
193 51 Lukas Zapletal
    make -C locale tx-update
194 53 Lukas Zapletal
195 54 Lukas Zapletal
Note: run this command in the Foreman git root directory.
196 4 Lukas Zapletal
197 49 Lukas Zapletal
Check git log, you should have few new commits stacked, all with i18n prefix. Then you can push changes.
198 4 Lukas Zapletal
199 51 Lukas Zapletal
    git push
200 9 Lukas Zapletal
201 57 Lukas Zapletal
h2. Not extracted
202 9 Lukas Zapletal
203 57 Lukas Zapletal
Some parts are (yet) not extracted for translation. These include:
204 9 Lukas Zapletal
205 19 Dominic Cleal
 * "Clear" tooltip in scoped_search
206 1 Lukas Zapletal
 * any external assert gems, e.g. SPICE HTML 5 console, noVNC?
207 20 Dominic Cleal
 * column names used in scoped_search
208 61 Dominic Cleal
209 61 Dominic Cleal
A number of other items are "filed as bugs":[]=status_id&op[status_id]=o&f[]=category_id&op[category_id]=%3D&v[category_id][]=57&f[]=&c[]=tracker&c[]=status&c[]=priority&c[]=subject&c[]=author&c[]=assigned_to&c[]=updated_on&c[]=category&c[]=fixed_version&c[]=position&group_by=.
210 57 Lukas Zapletal
211 57 Lukas Zapletal
Things that perhaps does not need to be translated:
212 41 Dominic Cleal
 * rake tasks
213 41 Dominic Cleal
 * logs?
214 35 Dominic Cleal
215 35 Dominic Cleal
Some parts live in external libraries or vendored assets:
216 36 Dominic Cleal
217 35 Dominic Cleal
 * will_paginate (Dom working on this)
218 35 Dominic Cleal
 ** @page_entries_info .. :model@ has unextracted strings in places, but we can't change this easily without fixing will_paginate
219 35 Dominic Cleal
 * jquery.dataTables (this has its own localisation method)
220 10 Lukas Zapletal
 * Puppet log messages, log levels
221 10 Lukas Zapletal
 * noVNC, spice-html5