wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.Localization.php
Go to the documentation of this file.
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2014 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of any of the following licenses
7  * at your choice:
8  *
9  * - GNU Lesser General Public License (LGPL)
10  * http://www.gnu.org/licenses/lgpl.html
11  * - Eclipse Public License (EPL)
12  * http://www.eclipse.org/org/documents/epl-v10.php
13  *
14  * See the license.txt file distributed with this work for
15  * additional information.
16  *
17  * $Id: class.Localization.php 929 2009-02-22 23:20:49Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/util/class.InifileParser.php");
20 require_once(BASE."wcmf/lib/util/class.ObjectFactory.php");
21 require_once(BASE."wcmf/lib/persistence/class.PersistenceFacade.php");
22 
23 
24 /**
25  * @class Localization
26  * @ingroup i18n
27  * @brief Localization is used to store localized entity instances
28  * and retrieve them back. Entity instances are localized value by value,
29  * where a translation of a value of one instance into a specific language
30  * is represented by one instance of the entity type that is defined
31  * in the key 'translationType' in the configuration section 'i18n' (e.g. Translation).
32  *
33  * The translation entity type must have the attributes 'objectid',
34  * 'attribute', 'translation', 'language' (all DATATYPE_ATTRIBUTE) with the
35  * appropriate getter and setter methods.
36  *
37  * Localization is done against a default language, which is defined
38  * in the configuration key 'defaultLanguage' in section 'i18n'. This means
39  * that all entity data in the store is supposed to use the default language
40  * except those data stored in Translation instances.
41  *
42  * All languages available for translation are either defined in the configuration
43  * section 'languages', where each language has it's own entry: e.g. en = English
44  * or in an entity tyoe that is defined in the key 'languageType' in the
45  * configuration section 'i18n' (e.g. Language). The entity type must have the
46  * attributes 'code' and 'name' (all DATATYPE_ATTRIBUTE) with the appropriate
47  * getter and setter methods.
48  * If entity type and configuration section are defined, the configuration section is preferred.
49  * Language key names may conform to ISO 639 language codes, but this is not mandatory.
50  * One of the keys must be equal to the value of defaultLanguage.
51  *
52  * Generally only values whose datatype does not equal DATATYPE_IGNORE are
53  * translatable.
54  * To exclude values of a special type (like date values) from the translation,
55  * they may be omitted in the array that is given in the key 'inputTypes' in
56  * the configuration section 'i18n'. This array lists all input_types whose
57  * translations are stored.
58  *
59  * @note: Localization is not aware of value datatypes. That means if an
60  * entity has two values with the same name, but different datatype, localization
61  * behaviour is not defined for these two values.
62  *
63  * @author ingo herwig <ingo@wemove.com>
64  */
66 {
68 
69  /**
70  * Returns an instance of the class.
71  * @return A reference to the only instance of the Singleton object
72  */
73  function &getInstance()
74  {
75  static $instance = null;
76 
77  if (!isset($instance))
78  $instance = new Localization();
79 
80  return $instance;
81  }
82 
83  /**
84  * Get the default language that is used in the store.
85  * Reads the key 'defaultLanguage' in the configuation section 'i18n'.
86  * @return The default language value (e.g. en)
87  */
88  function getDefaultLanguage()
89  {
90  $parser = &InifileParser::getInstance();
91 
92  if (($defaultLanguage = $parser->getValue('defaultLanguage', 'i18n')) === false) {
93  WCMFException::throwEx("No default language defined in configfile. ".$parser->getErrorMsg(),
94  __FILE__, __LINE__);
95  }
96  $defaultLanguages = $this->getSupportedLanguages();
97  if (!isset($defaultLanguages[$defaultLanguage])) {
98  WCMFException::throwEx("No supported language equals the default language '".$defaultLanguage."'",
99  __FILE__, __LINE__);
100  }
101  return $defaultLanguage;
102  }
103  /**
104  * Get all supported languages.
105  * @return An associative array with the language codes as keys and the names as values.
106  */
108  {
109  if ($this->_supportedLanguages == null)
110  {
111  // check if the configuration section exists
112  $parser = &InifileParser::getInstance();
113  if (($languages = $parser->getSection('languages')) !== false) {
114  $this->_supportedLanguages = $languages;
115  }
116  // if not, use the languageType
117  else
118  {
119  $languageType = $parser->getValue('languageType', 'i18n');
120  if ($languageType === false) {
121  WCMFException::throwEx("No 'languageType' defined in configfile. ".$parser->getErrorMsg(),
122  __FILE__, __LINE__);
123  }
124  else
125  {
126  $persistenceFacade = &PersistenceFacade::getInstance();
127  $languages = $persistenceFacade->loadObjects($languageType, BUILDEPTH_SINGLE);
128  for($i=0; $i<sizeof($languages); $i++)
129  {
130  $curLanguage = &$languages[$i];
131  $this->_supportedLanguages[$curLanguage->getCode()] = $curLanguage->getName();
132  }
133  }
134  }
135  }
137  }
138  /**
139  * Get the type name of the translation instances.
140  * @return The type name.
141  */
143  {
144  $parser = &InifileParser::getInstance();
145  if (($type = $parser->getValue('translationType', 'i18n')) === false) {
146  WCMFException::throwEx("No translation type defined in configfile. ".$parser->getErrorMsg(),
147  __FILE__, __LINE__);
148  }
149  return $type;
150  }
151  /**
152  * Get the input types that are translatable.
153  * @return The input type names.
154  */
156  {
157  $parser = &InifileParser::getInstance();
158  if (($inputTypes = $parser->getValue('inputTypes', 'i18n')) === false) {
159  WCMFException::throwEx("No input types defined in configfile. ".$parser->getErrorMsg(),
160  __FILE__, __LINE__);
161  }
162  return $inputTypes;
163  }
164  /**
165  * Get a newly created instance of the type defined in
166  * the key 'type' in the configuration section 'i18n'.
167  * @return An instance.
168  */
170  {
171  $objectFactory = &ObjectFactory::getInstance();
172  $obj = &$objectFactory->createInstanceFromConfig('i18n', 'translationType');
173  return $obj;
174  }
175  /**
176  * Load a single translated object. The object is always loaded with BUILDDEPTH_SINGLE.
177  * @param oid The id of the object to load the translation for.
178  * @param lang The language of the translation to load.
179  * @param useDefaults True/False wether to use the default language values
180  * for untranslated/empty values or not. Optional, default is true
181  * @return A reference to the translated object.
182  */
183  function &loadTranslatedObject($oid, $lang, $useDefaults=true)
184  {
185  $persistenceFacade = &PersistenceFacade::getInstance();
186  $object = &$persistenceFacade->load($oid, BUILDDEPTH_SINGLE);
187 
188  $this->loadTranslation($object, $lang, $useDefaults, false);
189  return $object;
190  }
191  /**
192  * Load a translation of an entity for a specific language.
193  * @param object A reference to the object to load the translation into. The object
194  * is supposed to have it's values in the default language.
195  * @param lang The language of the translation to load.
196  * @param useDefaults True/False wether to use the default language values
197  * for untranslated/empty values or not. Optional, default is true.
198  * @param recursive True/False wether to load translations for children too or not.
199  * Optional, default is true. For recursive use, the object must have a getChildren method.
200  */
201  function loadTranslation(&$object, $lang, $useDefaults=true, $recursive=true)
202  {
203  if ($object == null) {
204  WCMFException::throwEx("Cannot load translation for null", __FILE__, __LINE__);
205  }
206  // if the requested language is the default language, return the original object
207  if ($lang == $this->getDefaultLanguage()) {
208  // nothing to do
209  }
210  // load the translations and translate the object for any other language
211  else
212  {
213  $type = $this->getTranslationType();
214  $query = &PersistenceFacade::createObjectQuery($type);
215  $tpl = &$query->getObjectTemplate($type);
216  $tpl->setObjectid("= '".$object->getOID()."'");
217  $tpl->setLanguage("= '".$lang."'");
218  $translations = $query->execute(BUILDDEPTH_SINGLE);
219 
220  // set the translated values in the object
221  $processor = new NodeProcessor('setTranslatedValue', array(&$translations, $useDefaults), new Localization());
222  $processor->run($object, false);
223  }
224 
225  // recurse if requested
226  if ($recursive)
227  {
228  // translate children
229  $children = $object->getChildren();
230  for($i=0; $i<sizeOf($children); $i++) {
231  $this->loadTranslation($children[$i], $lang, $useDefaults, $recursive);
232  }
233  }
234  }
235  /**
236  * Save a translation of an entity for a specific language. Only the
237  * values that have a non-empty value are considered as translations and stored.
238  * Only values whose input_type property is listed in the 'inputTypes' key in
239  * the configuration 'i18n' are stored.
240  * @param object An instance of the entity type that holds the translations as values.
241  * @param lang The language of the translation.
242  * @param saveEmptyValues True/False wether to save empty translations or not.
243  * Optional, default is false
244  * @param recursive True/False wether to save translations for children too or not.
245  * Optional, default is true. For recursive use, the object must have a getChildren method.
246  */
247  function saveTranslation(&$object, $lang, $saveEmptyValues=false, $recursive=true)
248  {
249  // if the requested language is the default language, do nothing
250  if ($lang == $this->getDefaultLanguage()) {
251  // nothing to do
252  }
253  // save the translations for any other language
254  else
255  {
256  // get the existing translations for the requested language
257  $type = $this->getTranslationType();
258  $query = &PersistenceFacade::createObjectQuery($type);
259  $tpl = &$query->getObjectTemplate($type);
260  $tpl->setObjectid("= '".$object->getOID()."'");
261  $tpl->setLanguage("= '".$lang."'");
262  $translations = $query->execute(BUILDDEPTH_SINGLE);
263 
264  // save the translations
265  $processor = new NodeProcessor('saveTranslatedValue', array(&$translations, $lang, $saveEmptyValues),
266  new Localization());
267  $processor->run($object, false);
268  }
269 
270  // recurse if requested
271  if ($recursive)
272  {
273  // translate children
274  $children = $object->getChildren();
275  for($i=0; $i<sizeOf($children); $i++) {
276  $this->loadTranslation($children[$i], $lang, $useDefaults, $recursive);
277  }
278  }
279  }
280  /**
281  * Remove translations for a given entity.
282  * @param oid The id of the object
283  * @param lang The language of the translation to remove. If null, all translations
284  * will be deleted [default: null]
285  */
286  function deleteTranslation($oid, $lang=null)
287  {
288  // if the requested language is the default language, do nothing
289  if ($lang == $this->getDefaultLanguage()) {
290  // nothing to do
291  }
292  // delete the translations for any other language
293  else
294  {
295  // get the existing translations for the requested language or all languages
296  $type = $this->getTranslationType();
297  $query = &PersistenceFacade::createObjectQuery($type);
298  $tpl = &$query->getObjectTemplate($type);
299  $tpl->setObjectid("= '".$oid."'");
300  if ($lang != null) {
301  $tpl->setLanguage("= '".$lang."'");
302  }
303  $translationOIDs = $query->execute(false);
304 
305  // delete the found tranlations
306  $persistenceFacade = &PersistenceFacade::getInstance();
307  foreach ($translationOIDs as $curTranslationOID) {
308  $persistenceFacade->delete($curTranslationOID);
309  }
310  }
311  }
312  /**
313  * Delete all translations for a given language.
314  * @param lang The language of the translations to remove
315  */
317  {
318  // if the requested language is the default language, do nothing
319  if ($lang == $this->getDefaultLanguage()) {
320  // nothing to do
321  }
322  // delete the translations for any other language
323  else
324  {
325  // get the existing translations for the requested language
326  $type = $this->getTranslationType();
327  $query = &PersistenceFacade::createObjectQuery($type);
328  $tpl = &$query->getObjectTemplate($type);
329  $tpl->setLanguage("= '".$lang."'");
330  $translationOIDs = $query->execute(false);
331 
332  // delete the found tranlations
333  $persistenceFacade = &PersistenceFacade::getInstance();
334  foreach ($translationOIDs as $curTranslationOID) {
335  $persistenceFacade->delete($curTranslationOID);
336  }
337  }
338  }
339  /**
340  * Callback for setting translated values in the given object
341  * @see NodeProcessor
342  */
343  function setTranslatedValue(&$obj, $valueName, $dataType, &$translations, $useDefaults)
344  {
345  $inputType = $obj->getValueProperty($valueName, 'input_type', $dataType);
346  $inputTypes = $this->getIncludedInputTypes();
347  if ($dataType != DATATYPE_IGNORE && in_array($inputType, $inputTypes))
348  {
349  // empty the value, if the default language values should not be used
350  if (!$useDefaults) {
351  $obj->setValue($valueName, null, $dataType);
352  }
353  // translate the value
354  for ($i=0; $i<sizeof($translations); $i++)
355  {
356  $curValueName = $translations[$i]->getAttribute();
357  if ($curValueName == $valueName)
358  {
359  $translation = $translations[$i]->getTranslation();
360  if (!($useDefaults && strlen($translation) == 0)) {
361  $obj->setValue($valueName, $translation, DATATYPE_ATTRIBUTE);
362  }
363  break;
364  }
365  }
366  }
367  }
368  /**
369  * Callback for saving translated values for the given object
370  * @see NodeProcessor
371  */
372  function saveTranslatedValue(&$obj, $valueName, $dataType, &$existingTranslations, $lang, $saveEmptyValues)
373  {
374  $inputType = $obj->getValueProperty($valueName, 'input_type', $dataType);
375  $inputTypes = $this->getIncludedInputTypes();
376  if ($dataType != DATATYPE_IGNORE && in_array($inputType, $inputTypes))
377  {
378  $value = $obj->getValue($valueName, $dataType);
379  if ($saveEmptyValues || strlen($value) > 0)
380  {
381  $translation = null;
382 
383  // check if a translation already exists
384  for ($i=0; $i<sizeof($existingTranslations); $i++)
385  {
386  $curValueName = $existingTranslations[$i]->getAttribute();
387  if ($curValueName == $valueName)
388  {
389  $translation = &$existingTranslations[$i];
390  break;
391  }
392  }
393 
394  // if not, create a new translation
395  if ($translation == null)
396  {
397  $persistenceFacade = &PersistenceFacade::getInstance();
398  $translation = &$persistenceFacade->create($this->getTranslationType());
399  }
400 
401  // set all required properties and save
402  $translation->setObjectid($obj->getOID());
403  $translation->setAttribute($valueName);
404  $translation->setTranslation($obj->getValue($valueName, $dataType));
405  $translation->setLanguage($lang);
406  $translation->save();
407  }
408  }
409  }
410 }
411 ?>
setTranslatedValue(&$obj, $valueName, $dataType, &$translations, $useDefaults)
const DATATYPE_ATTRIBUTE
NodeProcessor is used to iterate over all values of a Node and apply a given callback function...
loadTranslation(&$object, $lang, $useDefaults=true, $recursive=true)
saveTranslation(&$object, $lang, $saveEmptyValues=false, $recursive=true)
throwEx($message, $file='', $line='')
const DATATYPE_IGNORE
Localization is used to store localized entity instances and retrieve them back. Entity instances are...
saveTranslatedValue(&$obj, $valueName, $dataType, &$existingTranslations, $lang, $saveEmptyValues)
& loadTranslatedObject($oid, $lang, $useDefaults=true)
$lang
Definition: message.js.php:12
deleteTranslation($oid, $lang=null)
const BUILDDEPTH_SINGLE