Shaka Packager SDK
language_utils.cc
1 // Copyright 2015 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/media/base/language_utils.h"
8 
9 #include "packager/base/logging.h"
10 
11 namespace {
12 
13 // A map from 3-letter language codes (ISO 639-2) to 2-letter language codes
14 // (ISO 639-1) for all languages which have both in the registry.
15 typedef struct {
16  const char iso_639_2[4]; // 3 letters + nul
17  const char iso_639_1[3]; // 2 letters + nul
18 } LanguageMapPairType;
19 const LanguageMapPairType kLanguageMap[] = {
20  { "aar", "aa" }, { "abk", "ab" }, { "afr", "af" }, { "aka", "ak" },
21  { "alb", "sq" }, { "amh", "am" }, { "ara", "ar" }, { "arg", "an" },
22  { "arm", "hy" }, { "asm", "as" }, { "ava", "av" }, { "ave", "ae" },
23  { "aym", "ay" }, { "aze", "az" }, { "bak", "ba" }, { "bam", "bm" },
24  { "baq", "eu" }, { "bel", "be" }, { "ben", "bn" }, { "bih", "bh" },
25  { "bis", "bi" }, { "bod", "bo" }, { "bos", "bs" }, { "bre", "br" },
26  { "bul", "bg" }, { "bur", "my" }, { "cat", "ca" }, { "ces", "cs" },
27  { "cha", "ch" }, { "che", "ce" }, { "chi", "zh" }, { "chu", "cu" },
28  { "chv", "cv" }, { "cor", "kw" }, { "cos", "co" }, { "cre", "cr" },
29  { "cym", "cy" }, { "cze", "cs" }, { "dan", "da" }, { "deu", "de" },
30  { "div", "dv" }, { "dut", "nl" }, { "dzo", "dz" }, { "ell", "el" },
31  { "eng", "en" }, { "epo", "eo" }, { "est", "et" }, { "eus", "eu" },
32  { "ewe", "ee" }, { "fao", "fo" }, { "fas", "fa" }, { "fij", "fj" },
33  { "fin", "fi" }, { "fra", "fr" }, { "fre", "fr" }, { "fry", "fy" },
34  { "ful", "ff" }, { "geo", "ka" }, { "ger", "de" }, { "gla", "gd" },
35  { "gle", "ga" }, { "glg", "gl" }, { "glv", "gv" }, { "gre", "el" },
36  { "grn", "gn" }, { "guj", "gu" }, { "hat", "ht" }, { "hau", "ha" },
37  { "heb", "he" }, { "her", "hz" }, { "hin", "hi" }, { "hmo", "ho" },
38  { "hrv", "hr" }, { "hun", "hu" }, { "hye", "hy" }, { "ibo", "ig" },
39  { "ice", "is" }, { "ido", "io" }, { "iii", "ii" }, { "iku", "iu" },
40  { "ile", "ie" }, { "ina", "ia" }, { "ind", "id" }, { "ipk", "ik" },
41  { "isl", "is" }, { "ita", "it" }, { "jav", "jv" }, { "jpn", "ja" },
42  { "kal", "kl" }, { "kan", "kn" }, { "kas", "ks" }, { "kat", "ka" },
43  { "kau", "kr" }, { "kaz", "kk" }, { "khm", "km" }, { "kik", "ki" },
44  { "kin", "rw" }, { "kir", "ky" }, { "kom", "kv" }, { "kon", "kg" },
45  { "kor", "ko" }, { "kua", "kj" }, { "kur", "ku" }, { "lao", "lo" },
46  { "lat", "la" }, { "lav", "lv" }, { "lim", "li" }, { "lin", "ln" },
47  { "lit", "lt" }, { "ltz", "lb" }, { "lub", "lu" }, { "lug", "lg" },
48  { "mac", "mk" }, { "mah", "mh" }, { "mal", "ml" }, { "mao", "mi" },
49  { "mar", "mr" }, { "may", "ms" }, { "mkd", "mk" }, { "mlg", "mg" },
50  { "mlt", "mt" }, { "mon", "mn" }, { "mri", "mi" }, { "msa", "ms" },
51  { "mya", "my" }, { "nau", "na" }, { "nav", "nv" }, { "nbl", "nr" },
52  { "nde", "nd" }, { "ndo", "ng" }, { "nep", "ne" }, { "nld", "nl" },
53  { "nno", "nn" }, { "nob", "nb" }, { "nor", "no" }, { "nya", "ny" },
54  { "oci", "oc" }, { "oji", "oj" }, { "ori", "or" }, { "orm", "om" },
55  { "oss", "os" }, { "pan", "pa" }, { "per", "fa" }, { "pli", "pi" },
56  { "pol", "pl" }, { "por", "pt" }, { "pus", "ps" }, { "que", "qu" },
57  { "roh", "rm" }, { "ron", "ro" }, { "rum", "ro" }, { "run", "rn" },
58  { "rus", "ru" }, { "sag", "sg" }, { "san", "sa" }, { "sin", "si" },
59  { "slk", "sk" }, { "slo", "sk" }, { "slv", "sl" }, { "sme", "se" },
60  { "smo", "sm" }, { "sna", "sn" }, { "snd", "sd" }, { "som", "so" },
61  { "sot", "st" }, { "spa", "es" }, { "sqi", "sq" }, { "srd", "sc" },
62  { "srp", "sr" }, { "ssw", "ss" }, { "sun", "su" }, { "swa", "sw" },
63  { "swe", "sv" }, { "tah", "ty" }, { "tam", "ta" }, { "tat", "tt" },
64  { "tel", "te" }, { "tgk", "tg" }, { "tgl", "tl" }, { "tha", "th" },
65  { "tib", "bo" }, { "tir", "ti" }, { "ton", "to" }, { "tsn", "tn" },
66  { "tso", "ts" }, { "tuk", "tk" }, { "tur", "tr" }, { "twi", "tw" },
67  { "uig", "ug" }, { "ukr", "uk" }, { "urd", "ur" }, { "uzb", "uz" },
68  { "ven", "ve" }, { "vie", "vi" }, { "vol", "vo" }, { "wel", "cy" },
69  { "wln", "wa" }, { "wol", "wo" }, { "xho", "xh" }, { "yid", "yi" },
70  { "yor", "yo" }, { "zha", "za" }, { "zho", "zh" }, { "zul", "zu" },
71 };
72 
73 void SplitLanguageTag(const std::string& tag,
74  std::string* main_language, std::string* subtag) {
75  // Split the main language from its subtag (if any).
76  *main_language = tag;
77  subtag->clear();
78  size_t dash = main_language->find('-');
79  if (dash != std::string::npos) {
80  *subtag = main_language->substr(dash);
81  main_language->erase(dash);
82  }
83 }
84 
85 } // namespace
86 
87 namespace shaka {
88 
89 std::string LanguageToShortestForm(const std::string& language) {
90  // Do not try to mangle blank strings.
91  if (language.size() == 0) {
92  return language;
93  }
94 
95  std::string main_language;
96  std::string subtag;
97  SplitLanguageTag(language, &main_language, &subtag);
98 
99  if (main_language.size() == 2) {
100  // Presumably already a valid ISO-639-1 code, and therefore conforms to
101  // BCP-47's requirement to use the shortest possible code.
102  return main_language + subtag;
103  }
104 
105  for (size_t i = 0; i < arraysize(kLanguageMap); ++i) {
106  if (main_language == kLanguageMap[i].iso_639_2) {
107  return kLanguageMap[i].iso_639_1 + subtag;
108  }
109  }
110 
111  // This could happen legitimately for languages which have no 2-letter code,
112  // but that would imply that the input language code is a 3-letter code.
113  DCHECK_EQ(3u, main_language.size()) << main_language;
114  return main_language + subtag;
115 }
116 
117 std::string LanguageToISO_639_2(const std::string& language) {
118  std::string main_language;
119  std::string subtag;
120  SplitLanguageTag(language, &main_language, &subtag);
121 
122  if (main_language.size() == 3) {
123  // Presumably already a valid ISO-639-2 code.
124  return main_language + subtag;
125  }
126 
127  for (size_t i = 0; i < arraysize(kLanguageMap); ++i) {
128  if (main_language == kLanguageMap[i].iso_639_1) {
129  return kLanguageMap[i].iso_639_2 + subtag;
130  }
131  }
132 
133  LOG(WARNING) << "No equivalent 3-letter language code for " << main_language;
134  // This is probably a mistake on the part of the user and should be treated
135  // as invalid input.
136  return "und";
137 }
138 
139 } // namespace shaka
All the methods that are virtual are virtual for mocking.
std::string LanguageToISO_639_2(const std::string &language)
std::string LanguageToShortestForm(const std::string &language)