|
1 .. -*- coding: utf-8 -*- |
|
2 |
|
3 .. _internationalization: |
|
4 |
|
5 Internationalization |
|
6 --------------------- |
|
7 |
|
8 Cubicweb fully supports the internalization of its content and interface. |
|
9 |
|
10 Cubicweb's interface internationalization is based on the translation project `GNU gettext`_. |
|
11 |
|
12 .. _`GNU gettext`: http://www.gnu.org/software/gettext/ |
|
13 |
|
14 Cubicweb' internalization involves two steps: |
|
15 |
|
16 * in your Python code and cubicweb-tal templates : mark translatable strings |
|
17 |
|
18 * in your instance : handle the translation catalog, edit translations |
|
19 |
|
20 String internationalization |
|
21 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
22 |
|
23 User defined string |
|
24 ``````````````````` |
|
25 |
|
26 In the Python code and cubicweb-tal templates translatable strings can be |
|
27 marked in one of the following ways : |
|
28 |
|
29 * by using the *built-in* function `_` :: |
|
30 |
|
31 class PrimaryView(EntityView): |
|
32 """the full view of an non final entity""" |
|
33 __regid__ = 'primary' |
|
34 title = _('primary') |
|
35 |
|
36 OR |
|
37 |
|
38 * by using the equivalent request's method :: |
|
39 |
|
40 class NoResultView(View): |
|
41 """default view when no result has been found""" |
|
42 __regid__ = 'noresult' |
|
43 |
|
44 def call(self, **kwargs): |
|
45 self.w(u'<div class="searchMessage"><strong>%s</strong></div>\n' |
|
46 % self._cw._('No result matching query')) |
|
47 |
|
48 The goal of the *built-in* function `_` is only **to mark the |
|
49 translatable strings**, it will only return the string to translate |
|
50 itself, but not its translation (it's actually another name for the |
|
51 `unicode` builtin). |
|
52 |
|
53 In the other hand the request's method `self._cw._` is also meant to |
|
54 retrieve the proper translation of translation strings in the |
|
55 requested language. |
|
56 |
|
57 Finally you can also use the `__` attribute of request object to get a |
|
58 translation for a string *which should not itself added to the catalog*, |
|
59 usually in case where the actual msgid is created by string interpolation :: |
|
60 |
|
61 self._cw.__('This %s' % etype) |
|
62 |
|
63 In this example ._cw.__` is used instead of ._cw._` so we don't have 'This %s' in |
|
64 messages catalogs. |
|
65 |
|
66 Translations in cubicweb-tal template can also be done with TAL tags |
|
67 `i18n:content` and `i18n:replace`. |
|
68 |
|
69 If you need to add messages on top of those that can be found in the source, |
|
70 you can create a file named `i18n/static-messages.pot`. |
|
71 |
|
72 You could put there messages not found in the python sources or |
|
73 overrides for some messages of used cubes. |
|
74 |
|
75 Generated string |
|
76 ```````````````` |
|
77 |
|
78 We do not need to mark the translation strings of entities/relations used by a |
|
79 particular instance's schema as they are generated automatically. String for |
|
80 various actions are also generated. |
|
81 |
|
82 For exemple the following schema :: |
|
83 |
|
84 Class EntityA(EntityType): |
|
85 relation_a2b = SubjectRelation('EntityB') |
|
86 |
|
87 class EntityB(EntityType): |
|
88 pass |
|
89 |
|
90 May generate the following message :: |
|
91 |
|
92 add EntityA relation_a2b EntityB subject |
|
93 |
|
94 This message will be used in views of ``EntityA`` for creation of a new |
|
95 ``EntityB`` with a preset relation ``relation_a2b`` between the current |
|
96 ``EntityA`` and the new ``EntityB``. The opposite message :: |
|
97 |
|
98 add EntityA relation_a2b EntityB object |
|
99 |
|
100 Is used for similar creation of an ``EntityA`` from a view of ``EntityB``. The |
|
101 title of they respective creation form will be :: |
|
102 |
|
103 creating EntityB (EntityA %(linkto)s relation_a2b EntityB) |
|
104 |
|
105 creating EntityA (EntityA relation_a2b %(linkto)s EntityA) |
|
106 |
|
107 In the translated string you can use ``%(linkto)s`` for reference to the source |
|
108 ``entity``. |
|
109 |
|
110 Handling the translation catalog |
|
111 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
112 |
|
113 Once the internationalization is done in your code, you need to populate and |
|
114 update the translation catalog. Cubicweb provides the following commands for this |
|
115 purpose: |
|
116 |
|
117 |
|
118 * `i18ncubicweb` updates Cubicweb framework's translation |
|
119 catalogs. Unless you actually work on the framework itself, you |
|
120 don't need to use this command. |
|
121 |
|
122 * `i18ncube` updates the translation catalogs of *one particular cube* |
|
123 (or of all cubes). After this command is executed you must update |
|
124 the translation files *.po* in the "i18n" directory of your |
|
125 cube. This command will of course not remove existing translations |
|
126 still in use. It will mark unused translation but not remove them. |
|
127 |
|
128 * `i18ninstance` recompiles the translation catalogs of *one particular |
|
129 instance* (or of all instances) after the translation catalogs of |
|
130 its cubes have been updated. This command is automatically |
|
131 called every time you create or update your instance. The compiled |
|
132 catalogs (*.mo*) are stored in the i18n/<lang>/LC_MESSAGES of |
|
133 instance where `lang` is the language identifier ('en' or 'fr' |
|
134 for exemple). |
|
135 |
|
136 |
|
137 Example |
|
138 ``````` |
|
139 |
|
140 You have added and/or modified some translation strings in your cube |
|
141 (after creating a new view or modifying the cube's schema for exemple). |
|
142 To update the translation catalogs you need to do: |
|
143 |
|
144 1. `cubicweb-ctl i18ncube <cube>` |
|
145 2. Edit the <cube>/i18n/xxx.po files and add missing translations (empty `msgstr`) |
|
146 3. `hg ci -m "updated i18n catalogs"` |
|
147 4. `cubicweb-ctl i18ninstance <myinstance>` |
|
148 |
|
149 Editing po files |
|
150 ~~~~~~~~~~~~~~~~ |
|
151 |
|
152 Using a PO aware editor |
|
153 ```````````````````````` |
|
154 |
|
155 Many tools exist to help maintain .po (PO) files. Common editors or |
|
156 development environment provides modes for these. One can also find |
|
157 dedicated PO files editor, such as `poedit`_. |
|
158 |
|
159 .. _`poedit`: http://www.poedit.net/ |
|
160 |
|
161 While usage of such a tool is commendable, PO files are perfectly |
|
162 editable with a (unicode aware) plain text editor. It is also useful |
|
163 to know their structure for troubleshooting purposes. |
|
164 |
|
165 Structure of a PO file |
|
166 `````````````````````` |
|
167 |
|
168 In this section, we selectively quote passages of the `GNU gettext`_ |
|
169 manual chapter on PO files, available there:: |
|
170 |
|
171 http://www.gnu.org/software/hello/manual/gettext/PO-Files.html |
|
172 |
|
173 One PO file entry has the following schematic structure:: |
|
174 |
|
175 white-space |
|
176 # translator-comments |
|
177 #. extracted-comments |
|
178 #: reference... |
|
179 #, flag... |
|
180 #| msgid previous-untranslated-string |
|
181 msgid untranslated-string |
|
182 msgstr translated-string |
|
183 |
|
184 |
|
185 A simple entry can look like this:: |
|
186 |
|
187 #: lib/error.c:116 |
|
188 msgid "Unknown system error" |
|
189 msgstr "Error desconegut del sistema" |
|
190 |
|
191 It is also possible to have entries with a context specifier. They |
|
192 look like this:: |
|
193 |
|
194 white-space |
|
195 # translator-comments |
|
196 #. extracted-comments |
|
197 #: reference... |
|
198 #, flag... |
|
199 #| msgctxt previous-context |
|
200 #| msgid previous-untranslated-string |
|
201 msgctxt context |
|
202 msgid untranslated-string |
|
203 msgstr translated-string |
|
204 |
|
205 |
|
206 The context serves to disambiguate messages with the same |
|
207 untranslated-string. It is possible to have several entries with the |
|
208 same untranslated-string in a PO file, provided that they each have a |
|
209 different context. Note that an empty context string and an absent |
|
210 msgctxt line do not mean the same thing. |
|
211 |
|
212 Contexts and CubicWeb |
|
213 ````````````````````` |
|
214 |
|
215 CubicWeb PO files have both non-contextual and contextual msgids. |
|
216 |
|
217 Contextual entries are automatically used in some cases. For instance, |
|
218 entity.dc_type(), eschema.display_name(req) or display_name(etype, |
|
219 req, form, context) methods/function calls will use them. |
|
220 |
|
221 It is also possible to explicitly use the with _cw.pgettext(context, |
|
222 msgid). |