RDK Documentation (Open Sourced RDK Components)
iso639map.cpp
Go to the documentation of this file.
1 /*
2 * If not stated otherwise in this file or this component's license file the
3 * following copyright and licenses apply:
4 *
5 * Copyright 2018 RDK Management
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19 
20 /**
21  * @file iso639map.cpp
22  * @brief ISO639 is a standard with representation of names for languages
23  */
24 
25 #include "iso639map.h"
26 #include <cstdlib>
27 #include <string.h>
28 #include <ctype.h>
29 
30 #define ISO639_BTMAP_ENTRY_SIZE (3+3+1)
31 #define ISO639_BTMAP_ENTRY_COUNT 20
32 
33 #define ISO639_23MAP_ENTRY_SIZE (2+3+1)
34 #define ISO639_23MAP_ENTRY_COUNT (204-ISO639_BTMAP_ENTRY_COUNT)
35 
36 /**
37  * sorted list of packed iso639-1, iso639-2 pairs
38  * delimited by a nul terminator, allowing pointer to the iso632-2 entry as valid c string
39  * entries sorted by the iso639-1 language code to facilitate binary search
40  */
41 static const char mISO639_2_3_pairs[ISO639_23MAP_ENTRY_COUNT][ISO639_23MAP_ENTRY_SIZE] =
42 {
43  "aa" "aar", // Afar
44  "ab" "abk", // Abkhazian
45  "ae" "ave", // Avestan
46  "af" "afr", // Afrikaans
47  "ak" "aka", // Akan
48  "am" "amh", // Amharic
49  "an" "arg", // Aragonese
50  "ar" "ara", // Arabic
51  "as" "asm", // Assamese
52  "av" "ava", // Avaric
53  "ay" "aym", // Aymara
54  "az" "aze", // Azerbaijani
55  "ba" "bak", // Bashkir
56  "be" "bel", // Belarusian
57  "bg" "bul", // Bulgarian
58  "bh" "bih", // Bihari languages
59  "bi" "bis", // Bislama
60  "bm" "bam", // Bambara
61  "bn" "ben", // Bengali
62  "bo" "bod", // (T) Tibetan
63  "br" "bre", // Breton
64  "bs" "bos", // Bosnian
65  "ca" "cat", // Catalan
66  "ce" "che", // Chechen
67  "ch" "cha", // Chamorro
68  "co" "cos", // Corsican
69  "cr" "cre", // Cree
70  "cs" "ces", // (T) Czech
71  "cu" "chu", // Church Slavic
72  "cv" "chv", // Chuvash
73  "cy" "cym", // (T) Welsh
74  "da" "dan", // Danish
75  "de" "deu", // (T) German
76  "dv" "div", // Divehi
77  "dz" "dzo", // Dzongkha
78  "ee" "ewe", // Ewe
79  "el" "ell", // (T) Greek, Modern
80  "en" "eng", // English
81  "eo" "epo", // Esperanto
82  "es" "spa", // Spanish
83  "et" "est", // Estonian
84  "eu" "eus", // (T) Basque
85  "fa" "fas", // (T) Persian
86  "ff" "ful", // Fulah
87  "fi" "fin", // Finnish
88  "fj" "fij", // Fijian
89  "fo" "fao", // Faroese
90  "fr" "fra", // (T) French
91  "fy" "fry", // Western Frisian
92  "ga" "gle", // Irish
93  "gd" "gla", // Gaelic
94  "gl" "glg", // Galician
95  "gn" "grn", // Guarani
96  "gu" "guj", // Gujarati
97  "gv" "glv", // Manx
98  "ha" "hau", // Hausa
99  "he" "heb", // Hebrew
100  "hi" "hin", // Hindi
101  "ho" "hmo", // Hiri Motu
102  "hr" "hrv", // Croatian
103  "ht" "hat", // Haitian
104  "hu" "hun", // Hungarian
105  "hy" "hye", // (T) Armenian
106  "hz" "her", // Herero
107  "ia" "ina", // Interlingua
108  "id" "ind", // Indonesian
109  "ie" "ile", // Interlingue
110  "ig" "ibo", // Igbo
111  "ii" "iii", // Sichuan Yi
112  "ik" "ipk", // Inupiaq
113  "io" "ido", // Ido
114  "is" "isl", // (T) Icelandic
115  "it" "ita", // Italian
116  "iu" "iku", // Inuktitut
117  "ja" "jpn", // Japanese
118  "jv" "jav", // Javanese
119  "ka" "kat", // (T) Georgian
120  "kg" "kon", // Kongo
121  "ki" "kik", // Kikuyu
122  "kj" "kua", // Kuanyama
123  "kk" "kaz", // Kazakh
124  "kl" "kal", // Kalaallisut
125  "km" "khm", // Central Khmer
126  "kn" "kan", // Kannada
127  "ko" "kor", // Korean
128  "kr" "kau", // Kanuri
129  "ks" "kas", // Kashmiri
130  "ku" "kur", // Kurdish
131  "kv" "kom", // Komi
132  "kw" "cor", // Cornish
133  "ky" "kir", // Kirghiz
134  "la" "lat", // Latin
135  "lb" "ltz", // Luxembourgish
136  "lg" "lug", // Ganda
137  "li" "lim", // Limburgan
138  "ln" "lin", // Lingala
139  "lo" "lao", // Lao
140  "lt" "lit", // Lithuanian
141  "lu" "lub", // Luba-Katanga
142  "lv" "lav", // Latvian
143  "mg" "mlg", // Malagasy
144  "mh" "mah", // Marshallese
145  "mi" "mri", // (T) Maori
146  "mk" "mkd", // (T) Macedonian
147  "ml" "mal", // Malayalam
148  "mn" "mon", // Mongolian
149  "mr" "mar", // Marathi
150  "ms" "msa", // (T) Malay
151  "mt" "mlt", // Maltese
152  "my" "mya", // (T) Burmese
153  "na" "nau", // Nauru
154  "nb" "nob", // Bokmål, Norwegian
155  "nd" "nde", // Ndebele, North
156  "ne" "nep", // Nepali
157  "ng" "ndo", // Ndonga
158  "nl" "nld", // (T) Dutch
159  "nn" "nno", // Norwegian Nynorsk
160  "no" "nor", // Norwegian
161  "nr" "nbl", // Ndebele, South
162  "nv" "nav", // Navajo
163  "ny" "nya", // Chichewa
164  "oc" "oci", // Occitan
165  "oj" "oji", // Ojibwa
166  "om" "orm", // Oromo
167  "or" "ori", // Oriya
168  "os" "oss", // Ossetian
169  "pa" "pan", // Panjabi
170  "pi" "pli", // Pali
171  "pl" "pol", // Polish
172  "ps" "pus", // Pushto
173  "pt" "por", // Portuguese
174  "qu" "que", // Quechua
175  "rm" "roh", // Romansh
176  "rn" "run", // Rundi
177  "ro" "ron", // (T) Romanian
178  "ru" "rus", // Russian
179  "rw" "kin", // Kinyarwanda
180  "sa" "san", // Sanskrit
181  "sc" "srd", // Sardinian
182  "sd" "snd", // Sindhi
183  "se" "sme", // Northern Sami
184  "sg" "sag", // Sango
185  "si" "sin", // Sinhala
186  "sk" "slk", // (T) Slovak
187  "sl" "slv", // Slovenian
188  "sm" "smo", // Samoan
189  "sn" "sna", // Shona
190  "so" "som", // Somali
191  "sq" "sqi", // (T) Albanian
192  "sr" "srp", // Serbian
193  "ss" "ssw", // Swati
194  "st" "sot", // Sotho, Southern
195  "su" "sun", // Sundanese
196  "sv" "swe", // Swedish
197  "sw" "swa", // Swahili
198  "ta" "tam", // Tamil
199  "te" "tel", // Telugu
200  "tg" "tgk", // Tajik
201  "th" "tha", // Thai
202  "ti" "tir", // Tigrinya
203  "tk" "tuk", // Turkmen
204  "tl" "tgl", // Tagalog
205  "tn" "tsn", // Tswana
206  "to" "ton", // Tonga
207  "tr" "tur", // Turkish
208  "ts" "tso", // Tsonga
209  "tt" "tat", // Tatar
210  "tw" "twi", // Twi
211  "ty" "tah", // Tahitian
212  "ug" "uig", // Uighur
213  "uk" "ukr", // Ukrainian
214  "ur" "urd", // Urdu
215  "uz" "uzb", // Uzbek
216  "ve" "ven", // Venda
217  "vi" "vie", // Vietnamese
218  "vo" "vol", // Volapük
219  "wa" "wln", // Walloon
220  "wo" "wol", // Wolof
221  "xh" "xho", // Xhosa
222  "yi" "yid", // Yiddish
223  "yo" "yor", // Yoruba
224  "za" "zha", // Zhuang
225  "zh" "zho", // (T) Chinese
226  "zu" "zul", // Zulu
227 };
228 
229 /**
230  * sorted list of packed iso639-2 bibliographic, terminology language code pairs
231  * delimited by a nul terminator, allowing pointer to the terminology entry as valid c string
232  * entries sorted by the bibliographic language code to facilitate binary search
233  *
234  * Used to normalize use of the 'terminology' version of iso639-2 langauge codes.
235  */
236 static const char mBibliographicTerminologyPairs[ISO639_BTMAP_ENTRY_COUNT][ISO639_BTMAP_ENTRY_SIZE] =
237 { // bibliographic code, terminology code
238  "alb" "sqi", // Albanian
239  "arm" "hye", // Armenian
240  "baq" "eus", // Basque
241  "bur" "mya", // Burmese
242  "chi" "zho", // Chinese
243  "cze" "ces", // Czech
244  "dut" "nld", // Dutch
245  "fre" "fra", // French
246  "geo" "kat", // Georgian
247  "ger" "deu", // German
248  "gre" "ell", // Greek, Modern
249  "ice" "isl", // Icelandic
250  "mac" "mkd", // Macedonian
251  "mao" "mri", // Maori
252  "may" "msa", // Malay
253  "per" "fas", // Persian
254  "rum" "ron", // Romanian
255  "slo" "slk", // Slovak
256  "tib" "bod", // Tibetan
257  "wel" "cym", // Welsh
258 };
259 
260 /**
261  * @param pkey pointer to two-character ISO639-1 language key
262  * @param pelem pointer to mISO639_2_3_pairs record
263  *
264  * @retval negative iff pkey is before pelem
265  * @retval positive iff pkey is after pelem
266  * @retval zero iff pkey matches pelem
267  */
268 static int myCompare23(const void*pkey,const void*pelem)
269 {
270  int rc = ((char *)pkey)[0] - ((char *)pelem)[0];
271  if( !rc )
272  {
273  rc = ((char *)pkey)[1] - ((char *)pelem)[1];
274  }
275  return rc;
276 }
277 
278 /**
279  * @param pkey pointer to three-character ISO639-2 language key
280  * @param pelem pointer to mBibliographicTerminologyPairs record
281  *
282  * @retval negative iff pkey is before pelem
283  * @retval positive iff pkey is after pelem
284  * @retval zero iff pkey matches pelem
285  */
286 static int myCompareBT(const void * pkey,const void * pelem)
287 {
288  int rc = ((char *)pkey)[0] - ((char *)pelem)[0];
289  if( !rc )
290  {
291  rc = ((char *)pkey)[1] - ((char *)pelem)[1];
292  if( !rc )
293  {
294  rc = ((char *)pkey)[2] - ((char *)pelem)[2];
295  }
296  }
297  return rc;
298 }
299 
300 /**
301  * @brief map "bibliographic" language code to corresponding "terminology" language code
302  *
303  * @param language3 three-character ISO639-2 language key
304 
305  * @retval corresponding terminology code if language3 is a bibliographic code
306  * @retval echo language3 if not mapped
307  */
308 static const char *MapISO639_BibliographicToTerminology( const char *language3 )
309 {
310  const char *rc = (const char *)bsearch(
311  (const void *)language3, // key
312  (const void *)mBibliographicTerminologyPairs, // base
313  ISO639_BTMAP_ENTRY_COUNT,
314  ISO639_BTMAP_ENTRY_SIZE,
315  myCompareBT);
316  if( !rc )
317  {
318  rc = language3;
319  }
320  else
321  {
322  rc+=3; // skip past bibliographic key to terminology code
323  }
324  return rc;
325 }
326 
327 static void MapISO639_TerminologyToBibliographic( char *language3 )
328 {
329  for( int i=0; i<ISO639_BTMAP_ENTRY_COUNT; i++ )
330  { // brute force search
331  const char *entry = mBibliographicTerminologyPairs[i];
332  if( memcmp(language3,&entry[3],3)==0 )
333  {
334  memcpy( language3, entry, 3 );
335  break;
336  }
337  }
338 }
339 
340 /**
341  * @brief convert 2-character ISO639-1 code, map to 3-character ISO639-2 code
342  * @param lang string to convert (if not already 3 character Terminology code
343  */
344 static void ConvertLanguage2to3( char lang[], bool useTerminologyVariant )
345 {
346  if( lang[0] && lang[1] )
347  { // at least two characters
348  if( lang[2] )
349  { // at least three characters
350  if( lang[3]==0x00 )
351  { // exactly three characters
352  if( useTerminologyVariant )
353  {
354  const char *normalized = MapISO639_BibliographicToTerminology(lang);
355  if( normalized!=lang )
356  { // avoid overlaping strcpy
357  strcpy( lang, normalized );
358  }
359  }
360  else
361  {
362  MapISO639_TerminologyToBibliographic( lang );
363  }
364  return;
365  }
366  }
367  else
368  { // exactly two characters
369  const char *rc = (const char *)bsearch(
370  (const void *)lang, // key
371  (const void *)mISO639_2_3_pairs, // base
372  ISO639_23MAP_ENTRY_COUNT,
373  ISO639_23MAP_ENTRY_SIZE,
374  myCompare23);
375  if( rc )
376  {
377  rc+=2; // skip past ISO639-1 key to three-character ISO639-2 language code
378  strcpy( lang, rc );
379  if( !useTerminologyVariant )
380  {
381  MapISO639_TerminologyToBibliographic( lang );
382  }
383  return;
384  }
385  }
386  }
387  strcpy( lang, "und" ); // default - error
388 }
389 /**
390 * @brief convert 3-character ISO639-2 code, map to 2-character ISO639-1 code
391 * @param lang string to convert (if not already 2 character code
392 */
393 static void ConvertLanguage3to2( char lang[] )
394 {
395  size_t len = strlen(lang);
396  if( len==3 )
397  {
398  const char *key = MapISO639_BibliographicToTerminology(lang);
399  for( int i=0; i<ISO639_23MAP_ENTRY_COUNT; i++ )
400  { // brute force search matching 3 character code to find corresponding two-character code
401  const char *entry = mISO639_2_3_pairs[i]; // 2 character code followed by 3 character code
402  if( memcmp(key,&entry[2],3)==0 )
403  {
404  lang[0] = entry[0];
405  lang[1] = entry[1];
406  lang[2] = 0;
407  return;
408  }
409  }
410  if( strcmp(lang,"mul")!=0 )
411  {
412  strcpy( lang, "un" ); // default - error
413  }
414  }
415 }
416 /**
417 * @brief Convert lang code to contain only lower case chars
418 * @param lang string to convert
419 */
420 static void ConvertToLowerCase( char lang[] )
421 {
422  for (;;)
423  {
424  char code = *lang;
425  if ( code )
426  {
427  *lang++ = tolower(code);
428  }
429  else
430  {
431  break;
432  }
433  }
434 }
435 
436 void iso639map_NormalizeLanguageCode( char lang[], LangCodePreference langCodePreference )
437 {
438  //XIONE-503: Some streams contains lang codes with uppercase chars
439  ConvertToLowerCase(lang);
440 
441  switch( langCodePreference )
442  {
443  case ISO639_NO_LANGCODE_PREFERENCE:
444  break;
445  case ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE:
446  ConvertLanguage2to3( lang, false );
447  break;
448  case ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE:
449  ConvertLanguage2to3( lang, true );
450  break;
451  case ISO639_PREFER_2_CHAR_LANGCODE:
452  ConvertLanguage3to2( lang );
453  break;
454  }
455 }
456 
457 #ifdef INCLUDE_ISO639MAP_TESTS
458 
459 static void TestHelper( const char *lang, LangCodePreference langCodePreference, const char *expectedResult )
460 {
461  printf( "%s[", lang );
462  switch( langCodePreference )
463  {
464  case ISO639_NO_LANGCODE_PREFERENCE:
465  printf( "ISO639_NO_LANGCODE_PREFERENCE" );
466  break;
467  case ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE:
468  printf( "ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE" );
469  break;
470  case ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE:
471  printf( "ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE" );
472  break;
473  case ISO639_PREFER_2_CHAR_LANGCODE:
474  printf( "ISO639_PREFER_2_CHAR_LANGCODE" );
475  break;
476  }
477  char temp[256];
478  strcpy( temp, lang );
479  iso639map_NormalizeLanguageCode( temp, langCodePreference );
480  printf( "] -> %s : ", temp );
481  if( strcmp(temp,expectedResult)!=0 )
482  {
483  printf( "FAIL! expected %s\n", expectedResult );
484  }
485  else
486  {
487  printf( "PASS\n" );
488  }
489 }
490 
491 void iso639map_Test( void )
492 {
493  TestHelper( "en", ISO639_NO_LANGCODE_PREFERENCE, "en" );
494  TestHelper( "en", ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE, "eng" );
495  TestHelper( "en", ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE, "eng" );
496  TestHelper( "en", ISO639_PREFER_2_CHAR_LANGCODE, "en" );
497 
498  TestHelper( "eng", ISO639_NO_LANGCODE_PREFERENCE, "eng" );
499  TestHelper( "eng", ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE, "eng" );
500  TestHelper( "eng", ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE, "eng" );
501  TestHelper( "eng", ISO639_PREFER_2_CHAR_LANGCODE, "en" );
502 
503  TestHelper( "de", ISO639_NO_LANGCODE_PREFERENCE, "de" );
504  TestHelper( "de", ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE, "ger" );
505  TestHelper( "de", ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE, "deu" );
506  TestHelper( "de", ISO639_PREFER_2_CHAR_LANGCODE, "de" );
507 
508  TestHelper( "deu", ISO639_NO_LANGCODE_PREFERENCE, "deu" );
509  TestHelper( "deu", ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE, "ger" );
510  TestHelper( "deu", ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE, "deu" );
511  TestHelper( "deu", ISO639_PREFER_2_CHAR_LANGCODE, "de" );
512 
513  TestHelper( "ger", ISO639_NO_LANGCODE_PREFERENCE, "ger" );
514  TestHelper( "ger", ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE, "ger" );
515  TestHelper( "ger", ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE, "deu" );
516  TestHelper( "ger", ISO639_PREFER_2_CHAR_LANGCODE, "de" );
517 
518  //XIONE-503: Uppercase lang codes case:
519  TestHelper( "DE", ISO639_NO_LANGCODE_PREFERENCE, "de" );
520  TestHelper( "DE", ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE, "ger" );
521  TestHelper( "DE", ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE, "deu" );
522  TestHelper( "DE", ISO639_PREFER_2_CHAR_LANGCODE, "de" );
523 
524  TestHelper( "DEU", ISO639_NO_LANGCODE_PREFERENCE, "deu" );
525  TestHelper( "DEU", ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE, "ger" );
526  TestHelper( "DEU", ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE, "deu" );
527  TestHelper( "DEU", ISO639_PREFER_2_CHAR_LANGCODE, "de" );
528 
529  TestHelper( "GER", ISO639_NO_LANGCODE_PREFERENCE, "ger" );
530  TestHelper( "GER", ISO639_PREFER_3_CHAR_BIBLIOGRAPHIC_LANGCODE, "ger" );
531  TestHelper( "GER", ISO639_PREFER_3_CHAR_TERMINOLOGY_LANGCODE, "deu" );
532  TestHelper( "GER", ISO639_PREFER_2_CHAR_LANGCODE, "de" );
533 }
534 
535 #endif // INCLUDE_ISO639MAP_TESTS
iso639map.h
ISO639 is a standard with representation of names for languages.
ConvertLanguage3to2
static void ConvertLanguage3to2(char lang[])
convert 3-character ISO639-2 code, map to 2-character ISO639-1 code
Definition: iso639map.cpp:393
MapISO639_BibliographicToTerminology
static const char * MapISO639_BibliographicToTerminology(const char *language3)
map "bibliographic" language code to corresponding "terminology" language code
Definition: iso639map.cpp:308
ConvertToLowerCase
static void ConvertToLowerCase(char lang[])
Convert lang code to contain only lower case chars.
Definition: iso639map.cpp:420
myCompareBT
static int myCompareBT(const void *pkey, const void *pelem)
Definition: iso639map.cpp:286
myCompare23
static int myCompare23(const void *pkey, const void *pelem)
Definition: iso639map.cpp:268
mISO639_2_3_pairs
static const char mISO639_2_3_pairs[(204- 20)][(2+3+1)]
Definition: iso639map.cpp:41
ConvertLanguage2to3
static void ConvertLanguage2to3(char lang[], bool useTerminologyVariant)
convert 2-character ISO639-1 code, map to 3-character ISO639-2 code
Definition: iso639map.cpp:344
LangCodePreference
LangCodePreference
Language Code Preference types.
Definition: main_aamp.h:165
mBibliographicTerminologyPairs
static const char mBibliographicTerminologyPairs[20][(3+3+1)]
Definition: iso639map.cpp:236