i18n_l10n.doxy
1 /*!
2 \page i18n_l10n I18n & l10n
3 <div class="has-toc"></div>
4 
5 # Internationalization & localization # {#i18n_main}
6 
7 _Internationalization_ of an application requires to identify all language dependent
8 resources and make them exchangeable for the actual _localization_ into a specific
9 language. This includes __static__ and __dynamic texts__ as well as __images__.
10 Since images are referenced by their filename or represented as text (e.g. _base64_
11 encoded), it is sufficient to focus on text.
12 
13 ## Static text ## {#i18n_static}
14 
15 Static text refers to text used in __application templates__ or __messages__ generated
16 while the application is executed. To keep the application code language agnostic
17 only so called _text identifiers_ are used in these places and the actual
18 translation is retrieved by calling the
19 \link wcmf::lib::i18n::Message::getText `Message::getText`\endlink method:
20 
21 <div class="php">
22 ```
23 $textId = 'An error occured';
24 
25 // retrieve the translation
26 $message = ObjectFactory::getInstance('Message');
27 $errorText = $message->getText($textId);
28 
29 // use the translation
30 $ex = new Exception($errorText);
31 ```
32 </div>
33 
34 @note Implementations of \link wcmf::lib::i18n::Message `Message`\endlink
35 must return the _text identifier_, if no translation is found or the translation
36 is empty. This helps to simplify the translation process, if the default language
37 is used in _text identifiers_. In this case, no translation is required for the
38 default language.
39 
40 If no language parameter is passed to the
41 \link wcmf::lib::i18n::Message::getText `Message::getText`\endlink method, the
42 returned translation will be in the application's __default user interface language__.
43 This language is determined in the following way:
44 
45 1. Use the value of the `language` parameter of the `Message` configuration section
46 2. If not defined use the value of the global variable <code>$_SERVER['HTTP_ACCEPT_LANGUAGE']</code>
47 
48 If parts of the text vary depending on the application state, it is necessary to
49 put __variables__ into the text identifier. Variable names are `%0%`, `%1%`, ...
50 and they are replaced in the order of the values passed into the
51 \link wcmf::lib::i18n::Message::getText `Message::getText`\endlink method:
52 
53 <div class="php">
54 ```
55 $textId = 'Logged in as %0% since %1%';
56 $parameters = [$username, $date];
57 
58 // retrieve the translation
59 $message = ObjectFactory::getInstance('Message');
60 $loginMessage = $message->getText($textId, $parameters);
61 
62 // use the translation
63 $view->setValue('loginMessage', $loginMessage);
64 ```
65 </div>
66 
67 ### Template text ### {#i18n_static_tpl}
68 
69 wCMF provides the `translate` plugin for embedding language dependent text into
70 _Smarty_ templates. The plugin is automatically included and used in the following
71 way:
72 
73 <div class="php">
74 ```
75 {translate text="An error occured"}
76 
77 {* with variables *}
78 {translate text="Logged in as %0% since %1%" r0=$login r1=$logindate}
79 ```
80 </div>
81 
82 ### Localization ### {#i18n_static_locale}
83 
84 Since \link wcmf::lib::i18n::Message `Message`\endlink defines the interface, it
85 needs concrete implementations to actually get the translations. The
86 \link wcmf::lib::i18n::impl::FileMessage `FileMessage`\endlink class retrieves
87 translations from the file system. It expects one file per language defining
88 a language dependent array that maps text identifiers to translations.
89 The language code (e.g. _en_) is used to identify the language of the translation.
90 The file must be named like <em>messages_</em>code.<em>php</em>,
91 the variable like <em>$messages_</em>code.
92 
93 The following __examples__ show definitions for English (_en_) and German (_de_)
94 texts, where the array keys are the text identifiers used in the application and
95 the values are the translations in the appropriate language.
96 
97 <em>messages_en.php</em>
98 
99 <div class="php">
100 ```
101 $messages_en = [];
102 $messages_en['%0% selected'] = '';
103 $messages_en['Author'] = '';
104 $messages_en['Book'] = '';
105 ```
106 </div>
107 
108 <em>messages_de.php</em>
109 
110 <div class="php">
111 ```
112 $messages_de = [];
113 $messages_de['%0% selected'] = '%0% ausgewählt';
114 $messages_de['Author'] = 'Autor';
115 $messages_de['Book'] = 'Buch';
116 ```
117 </div>
118 
119 @note Note that the text identifiers are in the application's default language
120 (_en_) and therefor no translations need to be provided.
121 
122 The default __configuration__ of \link wcmf::lib::i18n::Message `Message`\endlink
123 is as follows:
124 
125 <div class="ini">
126 ```
127 [Message]
128 __class = wcmf\lib\i18n\impl\FileMessage
129 localeDir = app/locale/
130 language = en
131 ```
132 </div>
133 
134 The parameter `localeDir` defines the directory where
135 \link wcmf::lib::i18n::impl::FileMessage `FileMessage`\endlink searchs for
136 translation files and the `language` parameter defines the application's default
137 language.
138 
139 ### Tools ### {#i18n_static_tools}
140 
141 The \ref app "default application" ships with a tool for finding translatable text
142 and generating localization files from it. After installing the application
143 it is available under `http://localhost/wcmf-default-app/tools/locale/`.
144 
145 The tool uses \link wcmf::lib::util::I18nUtil `I18nUtil`\endlink internally to
146 search for occurrences of
147 
148 - <code>->getText('Text to translate', ...)</code> as used in
149  <code>$message->getText('Text to translate', ...)</code> in PHP code
150 - `{translate:"Text to translate" ...}` as used in Smarty templates
151 - `Dict.translate("Text to translate", ...)` as used in the Javascript code of the
152  \ref app "default application"
153 
154 _Text to translate_ is supposed to be the message to be localized. The tool generates
155 one localization file for each language puts it into the appropriate directory.
156 It's capable of merging already existing translations into the newly generated
157 files.
158 
159 The tool's configuration is defined in the file _config.ini_ in the tool's directory
160 and defaults to:
161 
162 <div class="ini">
163 ```
164 [Application]
165 localeDir = app/locale/
166 
167 [I18n]
168 searchDirs = {vendor/wcmf/wcmf/src/wcmf, app/}
169 exclude = {vendor, .git, .svn, test}
170 languages = {de, en}
171 ```
172 </div>
173 
174 ## Model content ## {#i18n_dynamic}
175 
176 Model content is initially created in the application's __default model language__,
177 which might differ from the _default user interface language_ (see \ref i18n_static).
178 Localization of the model content is done by using implementations of
179 \link wcmf::lib::i18n::Localization `Localization`\endlink. The interface defines
180 methods for loading and saving localized model content. The following code
181 demonstrates how to use it to get translated content:
182 
183 <div class="php">
184 ```
185 $oid = new ObjectId('Book', 1);
186 
187 // retrieve a localized entity instance
188 $localization = ObjectFactory::getInstance('localization');
189 $bookDe = $localization->loadTranslatedObject($oid, 'de');
190 ```
191 </div>
192 
193 ### Localization ### {#i18n_dynamic_locale}
194 
195 There are several strategies for storing localized versions of the content, all
196 having advantages and disadvantages regarding extensibility and performance. The
197 idea behind the
198 \link wcmf::lib::i18n::impl::DefaultLocalization `DefaultLocalization`\endlink
199 implementation is to keep the domain model clear of localization related attributes
200 by storing translations in a separate entity type. Using one entity type for all
201 content leads to more flexibility when extending the domain model.
202 
203 The __translation entity type__ must have the following attributes:
204 
205 - `objectid` object id of the translated entity (e.g. _Book:1_)
206 - `attribute` translated attribute (e.g. _title_)
207 - `language` the language code of the translation (e.g. _en_)
208 - `translation` the actual translation
209 
210 The supported content languages are either defined in the `Languages` configuration
211 section (see below) or in the language entity type that is used by
212 \link wcmf::lib::i18n::impl::DefaultLocalization `DefaultLocalization`\endlink,
213 if the configuration section does not exist.
214 
215 The __language entity type__ must have the following attributes:
216 
217 - `code` language code (e.g. _en_)
218 - `name` language name (e.g. _English_)
219 
220 The following __example__ demonstrates the creation of localized content
221 using the \link wcmf::lib::i18n::Localization `Localization`\endlink interface:
222 
223 <div class="php">
224 ```
225 $oid = new ObjectId('Book', 1);
226 
227 // retrieve the original entity instance
228 $persistenceFacade = ObjectFactory::getInstance('persistenceFacade');
229 $book = $persistenceFacade->load($oid);
230 
231 // localize the instance
232 $book->setValue('title', 'Übersetzter Titel');
233 
234 // save the translation
235 $localization = ObjectFactory::getInstance('localization');
236 $localization->saveTranslation($book, 'de');
237 ```
238 </div>
239 
240 The localization __configuration__ of the \ref app "default application" is the
241 following:
242 
243 <div class="ini">
244 ```
245 [Localization]
246 __class = wcmf\lib\i18n\impl\DefaultLocalization
247 defaultLanguage = en
248 languageType = app.src.model.wcmf.Language
249 translationType = app.src.model.wcmf.Translation
250 
251 [Languages]
252 de = Deutsch
253 en = English
254 ```
255 </div>
256 
257 In this example the _default model language_ is set to English and the
258 _language entity type_ is ignored, because the available languages are defined
259 in the configuration.
260 */