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