ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
Language.php
Go to the documentation of this file.
1 <?php
2 namespace Gettext\Languages;
3 
4 use Exception;
5 
9 class Language
10 {
15  public $id;
20  public $name;
25  public $supersededBy;
30  public $script;
35  public $territory;
40  public $baseLanguage;
45  public $categories;
50  public $formula;
56  private function __construct($info)
57  {
58  $this->id = $info['id'];
59  $this->name = $info['name'];
60  $this->supersededBy = isset($info['supersededBy']) ? $info['supersededBy'] : null;
61  $this->script = isset($info['script']) ? $info['script'] : null;
62  $this->territory = isset($info['territory']) ? $info['territory'] : null;
63  $this->baseLanguage = isset($info['baseLanguage']) ? $info['baseLanguage'] : null;
64  // Let's build the category list
65  $this->categories = array();
66  foreach ($info['categories'] as $cldrCategoryId => $cldrFormulaAndExamples) {
67  $category = new Category($cldrCategoryId, $cldrFormulaAndExamples);
68  foreach ($this->categories as $c) {
69  if ($category->id === $c->id) {
70  throw new Exception("The category '{$category->id}' is specified more than once");
71  }
72  }
73  $this->categories[] = $category;
74  }
75  if (empty($this->categories)) {
76  throw new Exception("The language '{$info['id']}' does not have any plural category");
77  }
78  // Let's sort the categories from 'zero' to 'other'
79  usort($this->categories, function (Category $category1, Category $category2) {
80  return array_search($category1->id, CldrData::$categories) - array_search($category2->id, CldrData::$categories);
81  });
82  // The 'other' category should always be there
83  if ($this->categories[count($this->categories) - 1]->id !== CldrData::OTHER_CATEGORY) {
84  throw new Exception("The language '{$info['id']}' does not have the '".CldrData::OTHER_CATEGORY."' plural category");
85  }
89  $this->formula = $this->buildFormula();
90  }
96  public static function getAll()
97  {
98  $result = array();
99  foreach (array_keys(CldrData::getLanguageNames()) as $cldrLanguageId) {
100  $result[] = new Language(CldrData::getLanguageInfo($cldrLanguageId));
101  }
102 
103  return $result;
104  }
110  public static function getById($id)
111  {
112  $result = null;
114  if (isset($info)) {
115  $result = new Language($info);
116  }
117 
118  return $result;
119  }
120 
126  private function checkAlwaysTrueCategories()
127  {
128  $alwaysTrueCategory = null;
129  foreach ($this->categories as $category) {
130  if ($category->formula === true) {
131  if (!isset($category->examples)) {
132  throw new Exception("The category '{$category->id}' should always occur, but it does not have examples (so for CLDR it will never occur for integers!)");
133  }
134  $alwaysTrueCategory = $category;
135  break;
136  }
137  }
138  if (isset($alwaysTrueCategory)) {
139  foreach ($this->categories as $category) {
140  if (($category !== $alwaysTrueCategory) && isset($category->examples)) {
141  throw new Exception("The category '{$category->id}' should never occur, but it has some examples (so for CLDR it will occur!)");
142  }
143  }
144  $alwaysTrueCategory->id = CldrData::OTHER_CATEGORY;
145  $alwaysTrueCategory->formula = null;
146  $this->categories = array($alwaysTrueCategory);
147  }
148  }
154  private function checkAlwaysFalseCategories()
155  {
156  $filtered = array();
157  foreach ($this->categories as $category) {
158  if ($category->formula === false) {
159  if (isset($category->examples)) {
160  throw new Exception("The category '{$category->id}' should never occur, but it has examples (so for CLDR it may occur!)");
161  }
162  } else {
163  $filtered[] = $category;
164  }
165  }
166  $this->categories = $filtered;
167  }
175  {
176  $allCategoriesIds = array();
177  $goodCategories = array();
178  $badCategories = array();
179  $badCategoriesIds = array();
180  foreach ($this->categories as $category) {
181  $allCategoriesIds[] = $category->id;
182  if (isset($category->examples)) {
183  $goodCategories[] = $category;
184  } else {
185  $badCategories[] = $category;
186  $badCategoriesIds[] = $category->id;
187  }
188  }
189  if (empty($badCategories)) {
190  return;
191  }
192  $removeCategoriesWithoutExamples = false;
193  switch (implode(',', $badCategoriesIds).'@'.implode(',', $allCategoriesIds)) {
195  switch ($this->buildFormula()) {
196  case '(n % 10 == 1 && n % 100 != 11) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : ((n % 10 == 0 || n % 10 >= 5 && n % 10 <= 9 || n % 100 >= 11 && n % 100 <= 14) ? 2 : 3))':
197  // Numbers ending with 0 => case 2 ('many')
198  // Numbers ending with 1 but not with 11 => case 0 ('one')
199  // Numbers ending with 11 => case 2 ('many')
200  // Numbers ending with 2 but not with 12 => case 1 ('few')
201  // Numbers ending with 12 => case 2 ('many')
202  // Numbers ending with 3 but not with 13 => case 1 ('few')
203  // Numbers ending with 13 => case 2 ('many')
204  // Numbers ending with 4 but not with 14 => case 1 ('few')
205  // Numbers ending with 14 => case 2 ('many')
206  // Numbers ending with 5 => case 2 ('many')
207  // Numbers ending with 6 => case 2 ('many')
208  // Numbers ending with 7 => case 2 ('many')
209  // Numbers ending with 8 => case 2 ('many')
210  // Numbers ending with 9 => case 2 ('many')
211  // => the 'other' case never occurs: use 'other' for 'many'
212  $removeCategoriesWithoutExamples = true;
213  break;
214  case '(n == 1) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : ((n != 1 && (n % 10 == 0 || n % 10 == 1) || n % 10 >= 5 && n % 10 <= 9 || n % 100 >= 12 && n % 100 <= 14) ? 2 : 3))':
215  // Numbers ending with 0 => case 2 ('many')
216  // Numbers ending with 1 but not number 1 => case 2 ('many')
217  // Number 1 => case 0 ('one')
218  // Numbers ending with 2 but not with 12 => case 1 ('few')
219  // Numbers ending with 12 => case 2 ('many')
220  // Numbers ending with 3 but not with 13 => case 1 ('few')
221  // Numbers ending with 13 => case 2 ('many')
222  // Numbers ending with 4 but not with 14 => case 1 ('few')
223  // Numbers ending with 14 => case 2 ('many')
224  // Numbers ending with 5 => case 2 ('many')
225  // Numbers ending with 6 => case 2 ('many')
226  // Numbers ending with 7 => case 2 ('many')
227  // Numbers ending with 8 => case 2 ('many')
228  // Numbers ending with 9 => case 2 ('many')
229  // => the 'other' case never occurs: use 'other' for 'many'
230  $removeCategoriesWithoutExamples = true;
231  break;
232  }
233  }
234  if (!$removeCategoriesWithoutExamples) {
235  throw new Exception("Unhandled case of plural categories without examples '".implode(', ', $badCategoriesIds)."' out of '".implode(', ', $allCategoriesIds)."'");
236  }
237  if ($badCategories[count($badCategories) - 1]->id === CldrData::OTHER_CATEGORY) {
238  // We're removing the 'other' cagory: let's change the last good category to 'other'
239  $lastGood = $goodCategories[count($goodCategories) - 1];
240  $lastGood->id = CldrData::OTHER_CATEGORY;
241  $lastGood->formula = null;
242  }
243  $this->categories = $goodCategories;
244  }
249  private function buildFormula()
250  {
251  $numCategories = count($this->categories);
252  switch ($numCategories) {
253  case 1:
254  // Just one category
255  return '0';
256  case 2:
257  return self::reduceFormula(self::reverseFormula($this->categories[0]->formula));
258  default:
259  $formula = strval($numCategories - 1);
260  for ($i = $numCategories - 2; $i >= 0; $i--) {
261  $f = self::reduceFormula($this->categories[$i]->formula);
262  if (!preg_match('/^\([^()]+\)$/', $f)) {
263  $f = "($f)";
264  }
265  $formula = "$f ? $i : $formula";
266  if ($i > 0) {
267  $formula = "($formula)";
268  }
269  }
270 
271  return $formula;
272  }
273  }
280  private static function reverseFormula($formula)
281  {
282  if (preg_match('/^n( % \d+)? == \d+(\.\.\d+|,\d+)*?$/', $formula)) {
283  return str_replace(' == ', ' != ', $formula);
284  }
285  if (preg_match('/^n( % \d+)? != \d+(\.\.\d+|,\d+)*?$/', $formula)) {
286  return str_replace(' != ', ' == ', $formula);
287  }
288  if (preg_match('/^\(?n == \d+ \|\| n == \d+\)?$/', $formula)) {
289  return trim(str_replace(array(' == ', ' || '), array(' != ', ' && '), $formula), '()');
290  }
291  $m = null;
292  if (preg_match('/^(n(?: % \d+)?) == (\d+) && (n(?: % \d+)?) != (\d+)$/', $formula, $m)) {
293  return "{$m[1]} != {$m[2]} || {$m[3]} == {$m[4]}";
294  }
295  switch ($formula) {
296  case '(n == 1 || n == 2 || n == 3) || n % 10 != 4 && n % 10 != 6 && n % 10 != 9':
297  return 'n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9)';
298  case '(n == 0 || n == 1) || n >= 11 && n <= 99':
299  return 'n >= 2 && (n < 11 || n > 99)';
300  }
301  throw new Exception("Unable to reverse the formula '$formula'");
302  }
308  private static function reduceFormula($formula)
309  {
310  $map = array(
311  'n != 0 && n != 1' => 'n > 1' ,
312  '(n == 0 || n == 1) && n != 0' => 'n == 1',
313  );
314 
315  return isset($map[$formula]) ? $map[$formula] : $formula;
316  }
322  private static function asciifier(&$value)
323  {
324  if (is_string($value) && ($value !== '')) {
325  // Avoid converting from 'Ÿ' to '"Y', let's prefer 'Y'
326  $transliterated = strtr($value, array(
327  'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A',
328  'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E',
329  'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I',
330  'Ñ' => 'N',
331  'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O',
332  'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U',
333  'Ÿ' => 'Y', 'Ý' => 'Y',
334  'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a',
335  'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e',
336  'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i',
337  'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o',
338  'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u',
339  'ý' => 'y', 'ÿ' => 'y',
340  ));
341  $transliterated = @iconv('UTF-8', 'US-ASCII//IGNORE//TRANSLIT', $transliterated);
342  if (($transliterated === false) || ($transliterated === '')) {
343  throw new Exception("Unable to transliterate '$value'");
344  }
345  $value = $transliterated;
346  }
347  }
352  public function getUSAsciiClone()
353  {
354  $clone = clone $this;
355  self::asciifier($clone->name);
356  self::asciifier($clone->formula);
357  $clone->categories = array();
358  foreach ($this->categories as $category) {
359  $categoryClone = clone $category;
360  self::asciifier($categoryClone->examples);
361  $clone->categories[] = $categoryClone;
362  }
363 
364  return $clone;
365  }
366 }
getUSAsciiClone()
Returns a clone of this instance with all the strings to US-ASCII.
Definition: Language.php:352
checkAlwaysFalseCategories()
Let&#39;s look for categories that will never occur.
Definition: Language.php:154
buildFormula()
Build the formula starting from the currently defined categories.
Definition: Language.php:249
A helper class that handles a single category rules (eg &#39;zero&#39;, &#39;one&#39;, ...) and its formula and examp...
Definition: Category.php:9
checkAlwaysTrueCategories()
Let&#39;s look for categories that will always occur.
Definition: Language.php:126
$result
static getById($id)
Return a Language instance given the language id.
Definition: Language.php:110
static getAll()
Return a list of all languages available.
Definition: Language.php:96
static getLanguageNames()
Returns a dictionary containing the language names.
Definition: CldrData.php:140
Main class to convert the plural rules of a language from CLDR to gettext.
Definition: Language.php:9
static reverseFormula($formula)
Reverse a formula.
Definition: Language.php:280
static asciifier(&$value)
Take one variable and, if it&#39;s a string, we transliterate it to US-ASCII.
Definition: Language.php:322
static getLanguageInfo($id)
Retrieve the name of a language, as well as if a language code is deprecated in favor of another lang...
Definition: CldrData.php:199
Create styles array
The data for the language used.
__construct($info)
Initialize the instance and parse the language code.
Definition: Language.php:56
$i
Definition: disco.tpl.php:19
static reduceFormula($formula)
Reduce some excessively complex formulas.
Definition: Language.php:308
checkAllCategoriesWithExamples()
Let&#39;s look for categories that don&#39;t have examples.
Definition: Language.php:174
$info
Definition: index.php:5