1 /*
2  *  Multi-language Support - language codes
3  *  Copyright (C) 2012 Adam Sutton
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <string.h>
20 #include <stdlib.h>
21 
22 #include "lang_codes.h"
23 #include "config.h"
24 
25 /* **************************************************************************
26  * Code list
27  * *************************************************************************/
28 
29 const lang_code_t lang_codes[] = {
30   { "und", NULL, NULL , "Undetermined" },
31   { "aar", "aa", NULL , "Afar" },
32   { "abk", "ab", NULL , "Abkhazian" },
33   { "ace", NULL, NULL , "Achinese" },
34   { "ach", NULL, NULL , "Acoli" },
35   { "ada", NULL, NULL , "Adangme" },
36   { "ady", NULL, NULL , "Adyghe; Adygei" },
37   { "afa", NULL, NULL , "Afro-Asiatic languages" },
38   { "afh", NULL, NULL , "Afrihili" },
39   { "afr", "af", NULL , "Afrikaans" },
40   { "ain", NULL, NULL , "Ainu" },
41   { "aka", "ak", NULL , "Akan" },
42   { "akk", NULL, NULL , "Akkadian" },
43   { "alb", "sq", "sqi", "Albanian" },
44   { "ale", NULL, NULL , "Aleut" },
45   { "alg", NULL, NULL , "Algonquian languages" },
46   { "alt", NULL, NULL , "Southern Altai" },
47   { "amh", "am", NULL , "Amharic" },
48   { "anp", NULL, NULL , "Angika" },
49   { "apa", NULL, NULL , "Apache languages" },
50   { "ara", "ar", NULL , "Arabic" },
51   { "arc", NULL, NULL , "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)" },
52   { "arg", "an", NULL , "Aragonese" },
53   { "arm", "hy", "hye", "Armenian" },
54   { "arn", NULL, NULL , "Mapudungun; Mapuche" },
55   { "arp", NULL, NULL , "Arapaho" },
56   { "art", NULL, NULL , "Artificial languages" },
57   { "arw", NULL, NULL , "Arawak" },
58   { "asm", "as", NULL , "Assamese" },
59   { "ast", NULL, NULL , "Asturian; Bable; Leonese; Asturleonese" },
60   { "ath", NULL, NULL , "Athapascan languages" },
61   { "aus", NULL, NULL , "Australian languages" },
62   { "ava", "av", NULL , "Avaric" },
63   { "ave", "ae", NULL , "Avestan" },
64   { "awa", NULL, NULL , "Awadhi" },
65   { "aym", "ay", NULL , "Aymara" },
66   { "aze", "az", NULL , "Azerbaijani" },
67   { "bad", NULL, NULL , "Banda languages" },
68   { "bai", NULL, NULL , "Bamileke languages" },
69   { "bak", "ba", NULL , "Bashkir" },
70   { "bal", NULL, NULL , "Baluchi" },
71   { "bam", "bm", NULL , "Bambara" },
72   { "ban", NULL, NULL , "Balinese" },
73   { "baq", "eu", "eus", "Basque" },
74   { "bas", NULL, NULL , "Basa" },
75   { "bat", NULL, NULL , "Baltic languages" },
76   { "bej", NULL, NULL , "Beja; Bedawiyet" },
77   { "bel", "be", NULL , "Belarusian" },
78   { "bem", NULL, NULL , "Bemba" },
79   { "ben", "bn", NULL , "Bengali" },
80   { "ber", NULL, NULL , "Berber languages" },
81   { "bho", NULL, NULL , "Bhojpuri" },
82   { "bih", "bh", NULL , "Bihari languages" },
83   { "bik", NULL, NULL , "Bikol" },
84   { "bin", NULL, NULL , "Bini; Edo" },
85   { "bis", "bi", NULL , "Bislama" },
86   { "bla", NULL, NULL , "Siksika" },
87   { "bnt", NULL, NULL , "Bantu languages" },
88   { "bos", "bs", NULL , "Bosnian" },
89   { "bra", NULL, NULL , "Braj" },
90   { "bre", "br", NULL , "Breton" },
91   { "btk", NULL, NULL , "Batak languages" },
92   { "bua", NULL, NULL , "Buriat" },
93   { "bug", NULL, NULL , "Buginese" },
94   { "bul", "bg", NULL , "Bulgarian" },
95   { "bur", "my", "mya", "Burmese" },
96   { "byn", NULL, NULL , "Blin; Bilin" },
97   { "cad", NULL, NULL , "Caddo" },
98   { "cai", NULL, NULL , "Central American Indian languages" },
99   { "car", NULL, NULL , "Galibi Carib" },
100   { "cat", "ca", NULL , "Catalan; Valencian" },
101   { "cau", NULL, NULL , "Caucasian languages" },
102   { "ceb", NULL, NULL , "Cebuano" },
103   { "cel", NULL, NULL , "Celtic languages" },
104   { "cha", "ch", NULL , "Chamorro" },
105   { "chb", NULL, NULL , "Chibcha" },
106   { "che", "ce", NULL , "Chechen" },
107   { "chg", NULL, NULL , "Chagatai" },
108   { "chi", "zh", "zho", "Chinese", "CN" },
109   { "chk", NULL, NULL , "Chuukese" },
110   { "chm", NULL, NULL , "Mari" },
111   { "chn", NULL, NULL , "Chinook jargon" },
112   { "cho", NULL, NULL , "Choctaw" },
113   { "chp", NULL, NULL , "Chipewyan; Dene Suline" },
114   { "chr", NULL, NULL , "Cherokee" },
115   { "chu", "cu", NULL , "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic" },
116   { "chv", "cv", NULL , "Chuvash" },
117   { "chy", NULL, NULL , "Cheyenne" },
118   { "cmc", NULL, NULL , "Chamic languages" },
119   { "cop", NULL, NULL , "Coptic" },
120   { "cor", "kw", NULL , "Cornish" },
121   { "cos", "co", NULL , "Corsican" },
122   { "cre", "cr", NULL , "Cree" },
123   { "crh", NULL, NULL , "Crimean Tatar; Crimean Turkish" },
124   { "crp", NULL, NULL , "Creoles and pidgins" },
125   { "csb", NULL, NULL , "Kashubian" },
126   { "cus", NULL, NULL , "Cushitic languages" },
127   { "cze", "cs", "ces", "Czech" },
128   { "dak", NULL, NULL , "Dakota" },
129   { "dan", "da", NULL , "Danish" },
130   { "dar", NULL, NULL , "Dargwa" },
131   { "day", NULL, NULL , "Land Dayak languages" },
132   { "del", NULL, NULL , "Delaware" },
133   { "den", NULL, NULL , "Slave (Athapascan)" },
134   { "dgr", NULL, NULL , "Dogrib" },
135   { "din", NULL, NULL , "Dinka" },
136   { "div", "dv", NULL , "Divehi; Dhivehi; Maldivian" },
137   { "doi", NULL, NULL , "Dogri" },
138   { "dra", NULL, NULL , "Dravidian languages" },
139   { "dsb", NULL, NULL , "Lower Sorbian" },
140   { "dua", NULL, NULL , "Duala" },
141   { "dut", "nl", "nld", "Dutch; Flemish" },
142   { "dyu", NULL, NULL , "Dyula" },
143   { "dzo", "dz", NULL , "Dzongkha" },
144   { "efi", NULL, NULL , "Efik" },
145   { "egy", NULL, NULL , "Egyptian (Ancient)" },
146   { "eka", NULL, NULL , "Ekajuk" },
147   { "elx", NULL, NULL , "Elamite" },
148   { "eng", "en", NULL , "English", "US|GB" },
149   { "epo", "eo", NULL , "Esperanto" },
150   { "est", "et", NULL , "Estonian" },
151   { "ewe", "ee", NULL , "Ewe" },
152   { "ewo", NULL, NULL , "Ewondo" },
153   { "fan", NULL, NULL , "Fang" },
154   { "fao", "fo", NULL , "Faroese" },
155   { "fat", NULL, NULL , "Fanti" },
156   { "fij", "fj", NULL , "Fijian" },
157   { "fil", NULL, NULL , "Filipino; Pilipino" },
158   { "fin", "fi", NULL , "Finnish" },
159   { "fiu", NULL, NULL , "Finno-Ugrian languages" },
160   { "fon", NULL, NULL , "Fon" },
161   { "fre", "fr", "fra", "French" },
162   { "frr", NULL, NULL , "Northern Frisian" },
163   { "frs", NULL, NULL , "Eastern Frisian" },
164   { "fry", "fy", NULL , "Western Frisian" },
165   { "ful", "ff", NULL , "Fulah" },
166   { "fur", NULL, NULL , "Friulian" },
167   { "gaa", NULL, NULL , "Ga" },
168   { "gay", NULL, NULL , "Gayo" },
169   { "gba", NULL, NULL , "Gbaya" },
170   { "gem", NULL, NULL , "Germanic languages" },
171   { "geo", "ka", "kat", "Georgian" },
172   { "ger", "de", "deu", "German" },
173   { "gez", NULL, NULL , "Geez" },
174   { "gil", NULL, NULL , "Gilbertese" },
175   { "gla", "gd", NULL , "Gaelic; Scottish Gaelic" },
176   { "gle", "ga", NULL , "Irish" },
177   { "glg", "gl", NULL , "Galician" },
178   { "glv", "gv", NULL , "Manx" },
179   { "gon", NULL, NULL , "Gondi" },
180   { "gor", NULL, NULL , "Gorontalo" },
181   { "got", NULL, NULL , "Gothic" },
182   { "grb", NULL, NULL , "Grebo" },
183   { "gre", "el", NULL , "Greek" },
184   { "grn", "gn", NULL , "Guarani" },
185   { "gsw", NULL, NULL , "Swiss German; Alemannic; Alsatian" },
186   { "guj", "gu", NULL , "Gujarati" },
187   { "gwi", NULL, NULL , "Gwich'in" },
188   { "hai", NULL, NULL , "Haida" },
189   { "hat", "ht", NULL , "Haitian; Haitian Creole" },
190   { "hau", "ha", NULL , "Hausa" },
191   { "haw", NULL, NULL , "Hawaiian" },
192   { "heb", "he", NULL , "Hebrew" },
193   { "her", "hz", NULL , "Herero" },
194   { "hil", NULL, NULL , "Hiligaynon" },
195   { "him", NULL, NULL , "Himachali languages; Western Pahari languages" },
196   { "hin", "hi", NULL , "Hindi" },
197   { "hit", NULL, NULL , "Hittite" },
198   { "hmn", NULL, NULL , "Hmong; Mong" },
199   { "hmo", "ho", NULL , "Hiri Motu" },
200   { "hrv", "hr", NULL , "Croatian" },
201   { "hsb", NULL, NULL , "Upper Sorbian" },
202   { "hun", "hu", NULL , "Hungarian" },
203   { "hup", NULL, NULL , "Hupa" },
204   { "iba", NULL, NULL , "Iban" },
205   { "ibo", "ig", NULL , "Igbo" },
206   { "ice", "is", "isl", "Icelandic" },
207   { "ido", "io", NULL , "Ido" },
208   { "iii", "ii", NULL , "Sichuan Yi; Nuosu" },
209   { "ijo", NULL, NULL , "Ijo languages" },
210   { "iku", "iu", NULL , "Inuktitut" },
211   { "ile", "ie", NULL , "Interlingue; Occidental" },
212   { "ilo", NULL, NULL , "Iloko" },
213   { "ina", "ia", NULL , "Interlingua (International Auxiliary Language Association)" },
214   { "inc", NULL, NULL , "Indic languages" },
215   { "ind", "id", NULL , "Indonesian" },
216   { "ine", NULL, NULL , "Indo-European languages" },
217   { "inh", NULL, NULL , "Ingush" },
218   { "ipk", "ik", NULL , "Inupiaq" },
219   { "ira", NULL, NULL , "Iranian languages" },
220   { "iro", NULL, NULL , "Iroquoian languages" },
221   { "ita", "it", NULL , "Italian" },
222   { "jav", "jv", NULL , "Javanese" },
223   { "jbo", NULL, NULL , "Lojban" },
224   { "jpn", "ja", NULL , "Japanese" },
225   { "jpr", NULL, NULL , "Judeo-Persian" },
226   { "jrb", NULL, NULL , "Judeo-Arabic" },
227   { "kaa", NULL, NULL , "Kara-Kalpak" },
228   { "kab", NULL, NULL , "Kabyle" },
229   { "kac", NULL, NULL , "Kachin; Jingpho" },
230   { "kal", "kl", NULL , "Kalaallisut; Greenlandic" },
231   { "kam", NULL, NULL , "Kamba" },
232   { "kan", "kn", NULL , "Kannada" },
233   { "kar", NULL, NULL , "Karen languages" },
234   { "kas", "ks", NULL , "Kashmiri" },
235   { "kau", "kr", NULL , "Kanuri" },
236   { "kaw", NULL, NULL , "Kawi" },
237   { "kaz", "kk", NULL , "Kazakh" },
238   { "kbd", NULL, NULL , "Kabardian" },
239   { "kha", NULL, NULL , "Khasi" },
240   { "khi", NULL, NULL , "Khoisan languages" },
241   { "khm", "km", NULL , "Central Khmer" },
242   { "kho", NULL, NULL , "Khotanese; Sakan" },
243   { "kik", "ki", NULL , "Kikuyu; Gikuyu" },
244   { "kin", "rw", NULL , "Kinyarwanda" },
245   { "kir", "ky", NULL , "Kirghiz; Kyrgyz" },
246   { "kmb", NULL, NULL , "Kimbundu" },
247   { "kok", NULL, NULL , "Konkani" },
248   { "kom", "kv", NULL , "Komi" },
249   { "kon", "kg", NULL , "Kongo" },
250   { "kor", "ko", NULL , "Korean" },
251   { "kos", NULL, NULL , "Kosraean" },
252   { "kpe", NULL, NULL , "Kpelle" },
253   { "krc", NULL, NULL , "Karachay-Balkar" },
254   { "krl", NULL, NULL , "Karelian" },
255   { "kro", NULL, NULL , "Kru languages" },
256   { "kru", NULL, NULL , "Kurukh" },
257   { "kua", "kj", NULL , "Kuanyama; Kwanyama" },
258   { "kum", NULL, NULL , "Kumyk" },
259   { "kur", "ku", NULL , "Kurdish" },
260   { "kut", NULL, NULL , "Kutenai" },
261   { "lad", NULL, NULL , "Ladino" },
262   { "lah", NULL, NULL , "Lahnda" },
263   { "lam", NULL, NULL , "Lamba" },
264   { "lao", "lo", NULL , "Lao" },
265   { "lat", "la", NULL , "Latin" },
266   { "lav", "lv", NULL , "Latvian" },
267   { "lez", NULL, NULL , "Lezghian" },
268   { "lim", "li", NULL , "Limburgan; Limburger; Limburgish" },
269   { "lin", "ln", NULL , "Lingala" },
270   { "lit", "lt", NULL , "Lithuanian" },
271   { "lol", NULL, NULL , "Mongo" },
272   { "loz", NULL, NULL , "Lozi" },
273   { "ltz", "lb", NULL , "Luxembourgish; Letzeburgesch" },
274   { "lua", NULL, NULL , "Luba-Lulua" },
275   { "lub", "lu", NULL , "Luba-Katanga" },
276   { "lug", "lg", NULL , "Ganda" },
277   { "lui", NULL, NULL , "Luiseno" },
278   { "lun", NULL, NULL , "Lunda" },
279   { "luo", NULL, NULL , "Luo (Kenya and Tanzania)" },
280   { "lus", NULL, NULL , "Lushai" },
281   { "mac", "mk", "mkd", "Macedonian" },
282   { "mad", NULL, NULL , "Madurese" },
283   { "mag", NULL, NULL , "Magahi" },
284   { "mah", "mh", NULL , "Marshallese" },
285   { "mai", NULL, NULL , "Maithili" },
286   { "mak", NULL, NULL , "Makasar" },
287   { "mal", "ml", NULL , "Malayalam" },
288   { "man", NULL, NULL , "Mandingo" },
289   { "mao", "mi", "mri", "Maori" },
290   { "map", NULL, NULL , "Austronesian languages" },
291   { "mar", "mr", NULL , "Marathi" },
292   { "mas", NULL, NULL , "Masai" },
293   { "may", "ms", "msa", "Malay" },
294   { "mdf", NULL, NULL , "Moksha" },
295   { "mdr", NULL, NULL , "Mandar" },
296   { "men", NULL, NULL , "Mende" },
297   { "mic", NULL, NULL , "Mi'kmaq; Micmac" },
298   { "min", NULL, NULL , "Minangkabau" },
299   { "mis", NULL, NULL , "Uncoded languages" },
300   { "mkh", NULL, NULL , "Mon-Khmer languages" },
301   { "mlg", "mg", NULL , "Malagasy" },
302   { "mlt", "mt", NULL , "Maltese" },
303   { "mnc", NULL, NULL , "Manchu" },
304   { "mni", NULL, NULL , "Manipuri" },
305   { "mno", NULL, NULL , "Manobo languages" },
306   { "moh", NULL, NULL , "Mohawk" },
307   { "mon", "mn", NULL , "Mongolian" },
308   { "mos", NULL, NULL , "Mossi" },
309   { "mul", NULL, NULL , "Multiple languages" },
310   { "mun", NULL, NULL , "Munda languages" },
311   { "mus", NULL, NULL , "Creek" },
312   { "mwl", NULL, NULL , "Mirandese" },
313   { "mwr", NULL, NULL , "Marwari" },
314   { "myn", NULL, NULL , "Mayan languages" },
315   { "myv", NULL, NULL , "Erzya" },
316   { "nah", NULL, NULL , "Nahuatl languages" },
317   { "nai", NULL, NULL , "North American Indian languages" },
318   { "nap", NULL, NULL , "Neapolitan" },
319   { "nar", NULL, NULL , "Narration: (audio described)"},
320   // Note: above is not part of the ISO spec, but is used in DVB
321   { "nau", "na", NULL , "Nauru" },
322   { "nav", "nv", NULL , "Navajo; Navaho" },
323   { "ndo", "ng", NULL , "Ndonga" },
324   { "nep", "ne", NULL , "Nepali" },
325   { "new", NULL, NULL , "Nepal Bhasa; Newari" },
326   { "nia", NULL, NULL , "Nias" },
327   { "nic", NULL, NULL , "Niger-Kordofanian languages" },
328   { "niu", NULL, NULL , "Niuean" },
329   { "nog", NULL, NULL , "Nogai" },
330   { "nor", "no", NULL , "Norwegian" },
331   { "nqo", NULL, NULL , "N'Ko" },
332   { "nso", NULL, NULL , "Pedi; Sepedi; Northern Sotho" },
333   { "nub", NULL, NULL , "Nubian languages" },
334   { "nwc", NULL, NULL , "Classical Newari; Old Newari; Classical Nepal Bhasa" },
335   { "nya", "ny", NULL , "Chichewa; Chewa; Nyanja" },
336   { "nym", NULL, NULL , "Nyamwezi" },
337   { "nyn", NULL, NULL , "Nyankole" },
338   { "nyo", NULL, NULL , "Nyoro" },
339   { "nzi", NULL, NULL , "Nzima" },
340   { "oci", "oc", NULL , "Occitan (post 1500)" },
341   { "oji", "oj", NULL , "Ojibwa" },
342   { "ori", "or", NULL , "Oriya" },
343   { "orm", "om", NULL , "Oromo" },
344   { "osa", NULL, NULL , "Osage" },
345   { "oss", "os", NULL , "Ossetian; Ossetic" },
346   { "oto", NULL, NULL , "Otomian languages" },
347   { "paa", NULL, NULL , "Papuan languages" },
348   { "pag", NULL, NULL , "Pangasinan" },
349   { "pal", NULL, NULL , "Pahlavi" },
350   { "pam", NULL, NULL , "Pampanga; Kapampangan" },
351   { "pan", "pa", NULL , "Panjabi; Punjabi" },
352   { "pap", NULL, NULL , "Papiamento" },
353   { "pau", NULL, NULL , "Palauan" },
354   { "per", "fa", "fas", "Persian" },
355   { "phi", NULL, NULL , "Philippine languages" },
356   { "phn", NULL, NULL , "Phoenician" },
357   { "pli", "pi", NULL , "Pali" },
358   { "pol", "pl", NULL , "Polish" },
359   { "pon", NULL, NULL , "Pohnpeian" },
360   { "por", "pt", NULL , "Portuguese" },
361   { "pra", NULL, NULL , "Prakrit languages" },
362   { "pus", "ps", NULL , "Pushto; Pashto" },
363   { "qaa", NULL, NULL , "Reserved" },
364   // Note: above is actually range from qaa to qtz
365   { "que", "qu", NULL , "Quechua" },
366   { "raj", NULL, NULL , "Rajasthani" },
367   { "rap", NULL, NULL , "Rapanui" },
368   { "rar", NULL, NULL , "Rarotongan; Cook Islands Maori" },
369   { "roa", NULL, NULL , "Romance languages" },
370   { "roh", "rm", NULL , "Romansh" },
371   { "rom", NULL, NULL , "Romany" },
372   { "rum", "ro", "ron", "Romanian; Moldavian; Moldovan" },
373   { "run", "rn", NULL , "Rundi" },
374   { "rup", NULL, NULL , "Aromanian; Arumanian; Macedo-Romanian" },
375   { "rus", "ru", NULL , "Russian" },
376   { "sad", NULL, NULL , "Sandawe" },
377   { "sag", "sg", NULL , "Sango" },
378   { "sah", NULL, NULL , "Yakut" },
379   { "sai", NULL, NULL , "South American Indian languages" },
380   { "sal", NULL, NULL , "Salishan languages" },
381   { "sam", NULL, NULL , "Samaritan Aramaic" },
382   { "san", "sa", NULL , "Sanskrit" },
383   { "sas", NULL, NULL , "Sasak" },
384   { "sat", NULL, NULL , "Santali" },
385   { "scn", NULL, NULL , "Sicilian" },
386   { "sco", NULL, NULL , "Scots" },
387   { "sel", NULL, NULL , "Selkup" },
388   { "sem", NULL, NULL , "Semitic languages" },
389   { "sgn", NULL, NULL , "Sign Languages" },
390   { "shn", NULL, NULL , "Shan" },
391   { "sid", NULL, NULL , "Sidamo" },
392   { "sin", "si", NULL , "Sinhala; Sinhalese" },
393   { "sio", NULL, NULL , "Siouan languages" },
394   { "sit", NULL, NULL , "Sino-Tibetan languages" },
395   { "sla", NULL, NULL , "Slavic languages" },
396   { "slo", "sk", "slk", "Slovak" },
397   { "slv", "sl", NULL , "Slovenian" },
398   { "sma", NULL, NULL , "Southern Sami" },
399   { "sme", "se", NULL , "Northern Sami" },
400   { "smi", NULL, NULL , "Sami languages" },
401   { "smj", NULL, NULL , "Lule Sami" },
402   { "smn", NULL, NULL , "Inari Sami" },
403   { "smo", "sm", NULL , "Samoan" },
404   { "sms", NULL, NULL , "Skolt Sami" },
405   { "sna", "sn", NULL , "Shona" },
406   { "snd", "sd", NULL , "Sindhi" },
407   { "snk", NULL, NULL , "Soninke" },
408   { "sog", NULL, NULL , "Sogdian" },
409   { "som", "so", NULL , "Somali" },
410   { "son", NULL, NULL , "Songhai languages" },
411   { "spa", "es", NULL , "Spanish" },
412   { "srd", "sc", NULL , "Sardinian" },
413   { "srn", NULL, NULL , "Sranan Tongo" },
414   { "srp", "sr", NULL , "Serbian" },
415   { "srr", NULL, NULL , "Serer" },
416   { "ssa", NULL, NULL , "Nilo-Saharan languages" },
417   { "ssw", "ss", NULL , "Swati" },
418   { "suk", NULL, NULL , "Sukuma" },
419   { "sun", "su", NULL , "Sundanese" },
420   { "sus", NULL, NULL , "Susu" },
421   { "sux", NULL, NULL , "Sumerian" },
422   { "swa", "sw", NULL , "Swahili" },
423   { "swe", "sv", NULL , "Swedish" },
424   { "syc", NULL, NULL , "Classical Syriac" },
425   { "syn", NULL, NULL , "Narration: (sync audio described)"},
426   { "syr", NULL, NULL , "Syriac" },
427   { "tah", "ty", NULL , "Tahitian" },
428   { "tai", NULL, NULL , "Tai languages" },
429   { "tam", "ta", NULL , "Tamil" },
430   { "tat", "tt", NULL , "Tatar" },
431   { "tel", "te", NULL , "Telugu" },
432   { "tem", NULL, NULL , "Timne" },
433   { "ter", NULL, NULL , "Tereno" },
434   { "tet", NULL, NULL , "Tetum" },
435   { "tgk", "tg", NULL , "Tajik" },
436   { "tgl", "tl", NULL , "Tagalog" },
437   { "tha", "th", NULL , "Thai" },
438   { "tib", "bo", "bod", "Tibetan" },
439   { "tig", NULL, NULL , "Tigre" },
440   { "tir", "ti", NULL , "Tigrinya" },
441   { "tiv", NULL, NULL , "Tiv" },
442   { "tkl", NULL, NULL , "Tokelau" },
443   { "tlh", NULL, NULL , "Klingon; tlhIngan-Hol" },
444   { "tli", NULL, NULL , "Tlingit" },
445   { "tmh", NULL, NULL , "Tamashek" },
446   { "tog", NULL, NULL , "Tonga (Nyasa)" },
447   { "ton", "to", NULL , "Tonga (Tonga Islands)" },
448   { "tpi", NULL, NULL , "Tok Pisin" },
449   { "tsi", NULL, NULL , "Tsimshian" },
450   { "tsn", "tn", NULL , "Tswana" },
451   { "tso", "ts", NULL , "Tsonga" },
452   { "tuk", "tk", NULL , "Turkmen" },
453   { "tum", NULL, NULL , "Tumbuka" },
454   { "tup", NULL, NULL , "Tupi languages" },
455   { "tur", "tr", NULL , "Turkish" },
456   { "tut", NULL, NULL , "Altaic languages" },
457   { "tvl", NULL, NULL , "Tuvalu" },
458   { "twi", "tw", NULL , "Twi" },
459   { "tyv", NULL, NULL , "Tuvinian" },
460   { "udm", NULL, NULL , "Udmurt" },
461   { "uga", NULL, NULL , "Ugaritic" },
462   { "uig", "ug", NULL , "Uighur; Uyghur" },
463   { "ukr", "uk", NULL , "Ukrainian" },
464   { "umb", NULL, NULL , "Umbundu" },
465   { "urd", "ur", NULL , "Urdu" },
466   { "uzb", "uz", NULL , "Uzbek" },
467   { "v.o", NULL, NULL , "Voice Original" },
468   { "vai", NULL, NULL , "Vai" },
469   { "ven", "ve", NULL , "Venda" },
470   { "vie", "vi", NULL , "Vietnamese" },
471   { "vol", "vo", NULL , "Volapük" },
472   { "vot", NULL, NULL , "Votic" },
473   { "wak", NULL, NULL , "Wakashan languages" },
474   { "wal", NULL, NULL , "Wolaitta; Wolaytta" },
475   { "war", NULL, NULL , "Waray" },
476   { "was", NULL, NULL , "Washo" },
477   { "wel", "cy", "cym", "Welsh" },
478   { "wen", NULL, NULL , "Sorbian languages" },
479   { "wln", "wa", NULL , "Walloon" },
480   { "wol", "wo", NULL , "Wolof" },
481   { "xal", NULL, NULL , "Kalmyk; Oirat" },
482   { "xho", "xh", NULL , "Xhosa" },
483   { "yao", NULL, NULL , "Yao" },
484   { "yap", NULL, NULL , "Yapese" },
485   { "yid", "yi", NULL , "Yiddish" },
486   { "yor", "yo", NULL , "Yoruba" },
487   { "ypk", NULL, NULL , "Yupik languages" },
488   { "zap", NULL, NULL , "Zapotec" },
489   { "zbl", NULL, NULL , "Blissymbols; Blissymbolics; Bliss" },
490   { "zen", NULL, NULL , "Zenaga" },
491   { "zha", "za", NULL , "Zhuang; Chuang" },
492   { "znd", NULL, NULL , "Zande languages" },
493   { "zul", "zu", NULL , "Zulu" },
494   { "zun", NULL, NULL , "Zuni" },
495   { "zza", NULL, NULL , "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki" },
496   { NULL, NULL, NULL, NULL }
497 };
498 
499 lang_code_lookup_t* lang_codes_code2b = NULL;
500 lang_code_lookup_t* lang_codes_code1 = NULL;
501 lang_code_lookup_t* lang_codes_code2t = NULL;
502 
503 /* **************************************************************************
504  * Functions
505  * *************************************************************************/
506 
507 /* Compare language codes */
_lang_code2b_cmp(void * a,void * b)508 static int _lang_code2b_cmp ( void *a, void *b )
509 {
510   return strcmp(((lang_code_lookup_element_t*)a)->lang_code->code2b,
511       ((lang_code_lookup_element_t*)b)->lang_code->code2b);
512 }
513 
_lang_code1_cmp(void * a,void * b)514 static int _lang_code1_cmp ( void *a, void *b )
515 {
516   return strcmp(((lang_code_lookup_element_t*)a)->lang_code->code1,
517       ((lang_code_lookup_element_t*)b)->lang_code->code1);
518 }
519 
_lang_code2t_cmp(void * a,void * b)520 static int _lang_code2t_cmp ( void *a, void *b )
521 {
522   return strcmp(((lang_code_lookup_element_t*)a)->lang_code->code2t,
523       ((lang_code_lookup_element_t*)b)->lang_code->code2t);
524 }
525 
_lang_code_lookup_add(lang_code_lookup_t * lookup_table,const lang_code_t * code,int (* func)(void *,void *))526 static int _lang_code_lookup_add( lang_code_lookup_t* lookup_table, const lang_code_t *code, int (*func)(void*, void*) ) {
527   lang_code_lookup_element_t *element;
528   element = (lang_code_lookup_element_t *)calloc(1, sizeof(lang_code_lookup_element_t));
529   element->lang_code = code;
530   RB_INSERT_SORTED(lookup_table, element, link, func);
531   return 0;
532 }
533 
_lang_code_get(const char * code,size_t len)534 static const lang_code_t *_lang_code_get ( const char *code, size_t len )
535 {
536   int i;
537   char tmp[4];
538 
539   if (lang_codes_code2b == NULL) {
540     const lang_code_t *c = lang_codes;
541 
542     lang_codes_code2b = (lang_code_lookup_t *)calloc(1, sizeof(lang_code_lookup_t));
543     lang_codes_code1 = (lang_code_lookup_t *)calloc(1, sizeof(lang_code_lookup_t));
544     lang_codes_code2t = (lang_code_lookup_t *)calloc(1, sizeof(lang_code_lookup_t));
545 
546     while (c->code2b) {
547       _lang_code_lookup_add(lang_codes_code2b, c, _lang_code2b_cmp);
548       if (c->code1) _lang_code_lookup_add(lang_codes_code1, c, _lang_code1_cmp);
549       if (c->code2t) _lang_code_lookup_add(lang_codes_code2t, c, _lang_code2t_cmp);
550       c++;
551     }
552   }
553 
554   if (code && *code && len) {
555 
556     /* Extract the code (lowercase) */
557     i = 0;
558     while (i < 3 && *code && len) {
559       if (*code == ';' || *code == ',' || *code == '-') break;
560       if (*code != ' ')
561         tmp[i++] = *code | 0x20; // |0x20 = lower case
562       code++;
563       len--;
564     }
565     tmp[i] = '\0';
566 
567     /* Convert special case (qaa..qtz) */
568     if (*tmp == 'q') {
569       if (tmp[1] >= 'a' && tmp[1] <= 'z' && tmp[2] >= 'a' && tmp[2] <= 'z') {
570         tmp[1] = 'a';
571         tmp[2] = 'a';
572       }
573     }
574 
575     /* Search */
576     if (i) {
577       lang_code_lookup_element_t sample, *element;
578       lang_code_t lang_code;
579       lang_code.code1 = tmp;
580       lang_code.code2b = tmp;
581       lang_code.code2t = tmp;
582       sample.lang_code = &lang_code;
583       element = RB_FIND(lang_codes_code2b, &sample, link, _lang_code2b_cmp);
584       if (element != NULL) return element->lang_code;
585       element = RB_FIND(lang_codes_code1, &sample, link, _lang_code1_cmp);
586       if (element != NULL) return element->lang_code;
587       element = RB_FIND(lang_codes_code2t, &sample, link, _lang_code2t_cmp);
588       if (element != NULL) return element->lang_code;
589     }
590   }
591   return &lang_codes[0];
592 }
593 
lang_code_get(const char * code)594 const char *lang_code_get ( const char *code )
595 {
596   return lang_code_get3(code)->code2b;
597 }
598 
lang_code_get2(const char * code,size_t len)599 const char *lang_code_get2 ( const char *code, size_t len )
600 {
601   return _lang_code_get(code, len)->code2b;
602 }
603 
lang_code_get3(const char * code)604 const lang_code_t *lang_code_get3 ( const char *code )
605 {
606   return _lang_code_get(code, strlen(code ?: ""));
607 }
608 
lang_code_split(const char * codes)609 const char **lang_code_split ( const char *codes )
610 {
611   int i = 0;
612   const lang_code_t **lcs = lang_code_split2(codes);
613   const char **ret;
614 
615   if(!lcs) return NULL;
616 
617   while(lcs[i])
618     i++;
619 
620   ret = calloc(1+i, sizeof(char*));
621 
622   i = 0;
623   while(lcs[i]) {
624       ret[i] = lcs[i]->code2b;
625       i++;
626   }
627   ret[i] = NULL;
628   free(lcs);
629 
630   return ret;
631 }
632 
lang_code_split2(const char * codes)633 const lang_code_t **lang_code_split2 ( const char *codes )
634 {
635   int n;
636   const char *c, *p;
637   const lang_code_t **ret;
638   const lang_code_t *co;
639 
640   /* Defaults */
641   if (!codes) codes = config_get_language();
642 
643   /* No config */
644   if (!codes) return NULL;
645 
646   /* Count entries */
647   n = 0;
648   c = codes;
649   while (*c) {
650     if (*c == ',') n++;
651     c++;
652   }
653   ret = calloc(2+n, sizeof(lang_code_t*));
654 
655   /* Create list */
656   n = 0;
657   p = c = codes;
658   while (*c) {
659     if (*c == ',') {
660       co = lang_code_get3(p);
661       if(co)
662         ret[n++] = co;
663       p = c + 1;
664     }
665     c++;
666   }
667   if (*p) ret[n++] = lang_code_get3(p);
668   ret[n] = NULL;
669 
670   return ret;
671 }
672 
lang_code_free(lang_code_lookup_t * l)673 static void lang_code_free( lang_code_lookup_t *l )
674 {
675   lang_code_lookup_element_t *element;
676   if (l == NULL)
677     return;
678   while ((element = RB_FIRST(l)) != NULL) {
679     RB_REMOVE(l, element, link);
680     free(element);
681   }
682   free(l);
683 }
684 
lang_code_preferred(void)685 const char *lang_code_preferred( void )
686 {
687   const char *codes = config_get_language(), *ret = "und";
688   const lang_code_t *co;
689 
690   if (codes) {
691     co = lang_code_get3(codes);
692     if (co && co->code2b)
693       ret = co->code2b;
694   }
695 
696   return ret;
697 }
698 
lang_code_user(const char * ucode)699 char *lang_code_user( const char *ucode )
700 {
701   const char *codes = config_get_language(), *s;
702   char *ret;
703 
704   if (!codes)
705     return ucode ? strdup(ucode) : NULL;
706   if (!ucode)
707     return strdup(codes);
708   ret = malloc(strlen(codes) + strlen(ucode) + 2);
709   strcpy(ret, ucode);
710   while (codes && *codes) {
711     s = codes;
712     while (*s && *s != ',')
713       s++;
714     if (strncmp(codes, ucode, s - codes)) {
715       strcat(ret, ",");
716       strncat(ret, codes, s - codes);
717     }
718     if (*s && *s == ',')
719       s++;
720     codes = s;
721   }
722   return ret;
723 }
724 
lang_code_done(void)725 void lang_code_done( void )
726 {
727   lang_code_free(lang_codes_code2b);
728   lang_code_free(lang_codes_code1);
729   lang_code_free(lang_codes_code2t);
730 }
731