Audacity  3.0.3
Languages.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Languages.cpp
6 
7  Dominic Mazzoni
8 
9 
10 *******************************************************************//*******************************************************************/
31 
32 
33 
34 #include "Languages.h"
35 #include <memory>
36 #include "wxArrayStringEx.h"
37 
38 #include "Internat.h"
39 #include "wxArrayStringEx.h"
40 
41 #include <wx/defs.h>
42 #include <wx/dir.h>
43 #include <wx/filename.h>
44 #include <wx/intl.h>
45 #include <wx/stdpaths.h>
46 #include <wx/textfile.h>
47 #include <wx/utils.h> // for wxSetEnv
48 
49 #include <clocale>
50 #include <unordered_map>
51 
52 using LangHash = std::unordered_map<wxString, TranslatableString>;
53 using ReverseLangHash = std::unordered_map<TranslatableString, wxString>;
54 
55 static void FindFilesInPathList(const wxString & pattern,
56  const FilePaths & pathList, FilePaths & results)
57 {
58  wxFileName ff;
59  for (const auto &path : pathList) {
60  ff = path + wxFILE_SEP_PATH + pattern;
61  wxDir::GetAllFiles(ff.GetPath(), &results, ff.GetFullName(), wxDIR_FILES);
62  }
63 }
64 
65 static bool TranslationExists(const FilePaths &pathList, wxString code)
66 {
67  FilePaths results;
68  FindFilesInPathList(code + L"/audacity.mo", pathList, results);
69 #if defined(__WXMAC__)
70  FindFilesInPathList(code + L".lproj/audacity.mo", pathList, results);
71 #endif
72  FindFilesInPathList(code + L"/LC_MESSAGES/audacity.mo", pathList, results);
73  return (results.size() > 0);
74 }
75 
76 #ifdef __WXMAC__
77 #include <CoreFoundation/CFLocale.h>
78 #include <wx/osx/core/cfstring.h>
79 #endif
80 
81 namespace Languages {
82 
83 wxString GetSystemLanguageCode(const FilePaths &pathList)
84 {
85  wxArrayString langCodes;
86  TranslatableStrings langNames;
87 
88  GetLanguages(pathList, langCodes, langNames);
89 
90  int sysLang = wxLocale::GetSystemLanguage();
91 
92  const wxLanguageInfo *info;
93 
94 #ifdef __WXMAC__
95  // PRL: Bug 1227, system language on Mac may not be right because wxW3 is
96  // dependent on country code too in wxLocale::GetSystemLanguage().
97 
98  if (sysLang == wxLANGUAGE_UNKNOWN)
99  {
100  // wxW3 did a too-specific lookup of language and country, when
101  // there is nothing for that combination; try it by language alone.
102 
103  // The following lines are cribbed from that function.
104  wxCFRef<CFLocaleRef> userLocaleRef(CFLocaleCopyCurrent());
105  wxCFStringRef str(wxCFRetain((CFStringRef)CFLocaleGetValue(userLocaleRef, kCFLocaleLanguageCode)));
106  auto lang = str.AsString();
107 
108  // Now avoid wxLocale::GetLanguageInfo(), instead calling:
109  info = wxLocale::FindLanguageInfo(lang);
110  }
111  else
112 #endif
113  {
114  info = wxLocale::GetLanguageInfo(sysLang);
115  }
116 
117  if (info) {
118  wxString fullCode = info->CanonicalName;
119  if (fullCode.length() < 2)
120  return wxT("en");
121 
122  wxString code = fullCode.Left(2);
123  unsigned int i;
124 
125  for(i=0; i<langCodes.size(); i++) {
126  if (langCodes[i] == fullCode)
127  return fullCode;
128 
129  if (langCodes[i] == code)
130  return code;
131  }
132  }
133 
134  return wxT("en");
135 }
136 
137 void GetLanguages( FilePaths pathList,
138  wxArrayString &langCodes, TranslatableStrings &langNames)
139 {
140  static const char *const utf8Names[] = {
141 "af Afrikaans",
142 "ar \330\247\331\204\330\271\330\261\330\250\331\212\330\251",
143 "be \320\221\320\265\320\273\320\260\321\200\321\203\321\201\320\272\320\260\321\217",
144 "bg \320\221\321\212\320\273\320\263\320\260\321\200\321\201\320\272\320\270",
145 "bn \340\246\254\340\246\276\340\246\202\340\246\262\340\246\276",
146 "bs Bosanski",
147 "ca Catal\303\240",
148 "[email protected] Valenci\303\240",
149 "co Corsu",
150 "cs \304\214e\305\241tina",
151 "cy Cymraeg",
152 "da Dansk",
153 "de Deutsch",
154 "el \316\225\316\273\316\273\316\267\316\275\316\271\316\272\316\254",
155 "en English",
156 "es Espa\303\261ol",
157 "eu Euskara",
158 "eu_ES Euskara (Espainiako)",
159 "fa \331\201\330\247\330\261\330\263\333\214",
160 "fi Suomi",
161 "fr Fran\303\247ais",
162 "ga Gaeilge",
163 "gl Galego",
164 "he \327\242\327\221\327\250\327\231\327\252",
165 "hi \340\244\271\340\244\277\340\244\250\340\245\215\340\244\246\340\245\200",
166 "hr Hrvatski",
167 "hu Magyar",
168 "hy \325\200\325\241\325\265\325\245\326\200\325\245\325\266",
169 "id Bahasa Indonesia",
170 "it Italiano",
171 "ja \346\227\245\346\234\254\350\252\236",
172 "ka \341\203\245\341\203\220\341\203\240\341\203\227\341\203\243\341\203\232\341\203\230",
173 "km \341\236\201\341\237\201\341\236\230\341\236\232\341\236\227\341\236\266\341\236\237\341\236\266",
174 "ko \355\225\234\352\265\255\354\226\264",
175 "lt Lietuvi\305\263",
176 "mk \320\234\320\260\320\272\320\265\320\264\320\276\320\275\321\201\320\272\320\270",
177 "mr \340\244\256\340\244\260\340\244\276\340\244\240\340\245\200",
178 "my \341\200\231\341\200\274\341\200\224\341\200\272\341\200\231\341\200\254\341\200\205\341\200\254",
179 "nb Norsk",
180 "nl Nederlands",
181 "oc Occitan",
182 "pl Polski",
183 "pt Portugu\303\252s",
184 "pt_BR Portugu\303\252s (Brasil)",
185 "ro Rom\303\242n\304\203",
186 "ru \320\240\321\203\321\201\321\201\320\272\320\270\320\271",
187 "sk Sloven\304\215ina",
188 "sl Sloven\305\241\304\215ina",
189 "sr_RS \320\241\321\200\320\277\321\201\320\272\320\270",
190 "[email protected] Srpski",
191 "sv Svenska",
192 "ta \340\256\244\340\256\256\340\256\277\340\256\264\340\257\215",
193 "tg \320\242\320\276\322\267\320\270\320\272\323\243",
194 "tr T\303\274rk\303\247e",
195 "uk \320\243\320\272\321\200\320\260\321\227\320\275\321\201\321\214\320\272\320\260",
196 "vi Ti\341\272\277ng Vi\341\273\207t",
197 "zh_CN \344\270\255\346\226\207\357\274\210\347\256\200\344\275\223\357\274\211",
198 "zh_TW \344\270\255\346\226\207\357\274\210\347\271\201\351\253\224\357\274\211",
199  };
200 
201  TranslatableStrings tempNames;
202  wxArrayString tempCodes;
203  ReverseLangHash reverseHash;
204  LangHash tempHash;
205 
206  const LangHash localLanguageName = []{
207  LangHash localLanguageName;
208  for ( auto utf8Name : utf8Names )
209  {
210  auto str = wxString::FromUTF8(utf8Name);
211  auto code = str.BeforeFirst(' ');
212  auto name = str.AfterFirst(' ');
213  localLanguageName[code] = Verbatim( name );
214  }
215  return localLanguageName;
216  }();
217 
218 #if defined(__WXGTK__)
219  {
220  wxFileName pathNorm{ wxStandardPaths::Get().GetInstallPrefix() + L"/share/locale" };
221  pathNorm.Normalize();
222  const wxString newPath{ pathNorm.GetFullPath() };
223  if (pathList.end() ==
224  std::find(pathList.begin(), pathList.end(), newPath))
225  pathList.push_back(newPath);
226  }
227 #endif
228 
229  // For each language in our list we look for a corresponding entry in
230  // wxLocale.
231  for ( auto end = localLanguageName.end(), i = localLanguageName.begin();
232  i != end; ++i )
233  {
234  const wxLanguageInfo *info = wxLocale::FindLanguageInfo(i->first);
235 
236  if (!info) {
237  wxASSERT(info != NULL);
238  continue;
239  }
240 
241  wxString fullCode = info->CanonicalName;
242  wxString code = fullCode.Left(2);
243  auto name = Verbatim( info->Description );
244 
245  // Logic: Languages codes are sometimes hierarchical, with a
246  // general language code and then a subheading. For example,
247  // zh_TW for Traditional Chinese and zh_CN for Simplified
248  // Chinese - but just zh for Chinese in general. First we look
249  // for the full code, like zh_TW. If that doesn't exist, we
250  // look for a code corresponding to the first two letters.
251  // Note that if the language for a fullCode exists but we only
252  // have a name for the short code, we will use the short code's
253  // name but associate it with the full code. This allows someone
254  // to drop in a NEW language and still get reasonable behavior.
255 
256  if (fullCode.length() < 2)
257  continue;
258 
259  auto found = localLanguageName.find( code );
260  if ( found != end ) {
261  name = found->second;
262  }
263  found = localLanguageName.find( fullCode );
264  if ( found != end ) {
265  name = found->second;
266  }
267 
268  if (TranslationExists(pathList, fullCode)) {
269  code = fullCode;
270  }
271 
272  if (!tempHash[code].empty())
273  continue;
274 
275  if (TranslationExists(pathList, code) || code==wxT("en")) {
276  tempCodes.push_back(code);
277  tempNames.push_back(name);
278  tempHash[code] = name;
279 
280 /* wxLogDebug(wxT("code=%s name=%s fullCode=%s name=%s -> %s"),
281  code, localLanguageName[code],
282  fullCode, localLanguageName[fullCode],
283  name);*/
284  }
285  }
286 
287  // JKC: Adding language for simplified audacity.
288  {
289  wxString code;
290  code = wxT("en-simple");
291  auto name = XO("Simplified");
292  if (TranslationExists(pathList, code) ) {
293  tempCodes.push_back(code);
294  tempNames.push_back(name);
295  tempHash[code] = name;
296  }
297  }
298 
299 
300  // Sort
301  unsigned int j;
302  for(j=0; j<tempNames.size(); j++){
303  reverseHash[tempNames[j]] = tempCodes[j];
304  }
305 
306  std::sort( tempNames.begin(), tempNames.end(),
307  []( const TranslatableString &a, const TranslatableString &b ){
308  return a.Translation() < b.Translation();
309  } );
310 
311  // Add system language
312  langNames.push_back(XO("System"));
313  langCodes.push_back(wxT("System"));
314 
315  for(j=0; j<tempNames.size(); j++) {
316  langNames.push_back(tempNames[j]);
317  langCodes.push_back(reverseHash[tempNames[j]]);
318  }
319 }
320 
321 static std::unique_ptr<wxLocale> sLocale;
322 static wxString sLocaleName;
323 
324 wxString SetLang( const FilePaths &pathList, const wxString & lang )
325 {
326  wxString result = lang;
327 
328  sLocale.reset();
329 
330 #if defined(__WXMAC__)
331  // This should be reviewed again during the wx3 conversion.
332 
333  // On OSX, if the LANG environment variable isn't set when
334  // using a language like Japanese, an assertion will trigger
335  // because conversion to Japanese from "?" doesn't return a
336  // valid length, so make OSX happy by defining/overriding
337  // the LANG environment variable with U.S. English for now.
338  wxSetEnv(wxT("LANG"), wxT("en_US.UTF-8"));
339 #endif
340 
341  const wxLanguageInfo *info = NULL;
342  if (!lang.empty() && lang != wxT("System")) {
343  // Try to find the given language
344  info = wxLocale::FindLanguageInfo(lang);
345  }
346  if (!info)
347  {
348  // Not given a language or can't find it; substitute the system language
349  result = Languages::GetSystemLanguageCode(pathList);
350  info = wxLocale::FindLanguageInfo(result);
351  if (!info)
352  // Return the substituted system language, but we can't complete setup
353  // Should we try to do something better?
354  return result;
355  }
356  sLocale = std::make_unique<wxLocale>(info->Language);
357 
358  for( const auto &path : pathList )
359  sLocale->AddCatalogLookupPathPrefix( path );
360 
361  // LL: Must add the wxWidgets catalog manually since the search
362  // paths were not set up when mLocale was created. The
363  // catalogs are search in LIFO order, so add wxstd first.
364  sLocale->AddCatalog(wxT("wxstd"));
365 
366  // Must match TranslationExists() in Languages.cpp
367  sLocale->AddCatalog("audacity");
368 
369  // Initialize internationalisation (number formats etc.)
370  //
371  // This must go _after_ creating the wxLocale instance because
372  // creating the wxLocale instance sets the application-wide locale.
373 
374  Internat::Init();
375 
376  using future1 = decltype(
377  // The file of unused strings is part of the source tree scanned by
378  // xgettext when compiling the catalog template audacity.pot.
379  // Including it here doesn't change that but does make the C++ compiler
380  // check for correct syntax, but also generate no object code for them.
381 #include "FutureStrings.h"
382  0
383  );
384 
385  sLocaleName = wxSetlocale(LC_ALL, NULL);
386 
387  return result;
388 }
389 
390 wxString GetLocaleName()
391 {
392  return sLocaleName;
393 }
394 
395 wxString GetLang()
396 {
397  if (sLocale)
398  return sLocale->GetSysName();
399  else
400  return {};
401 }
402 
403 wxString GetLangShort()
404 {
405  if (sLocale)
406  return sLocale->GetName();
407  else
408  return {};
409 }
410 }
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: TranslatableString.h:295
Languages::sLocale
static std::unique_ptr< wxLocale > sLocale
Definition: Languages.cpp:321
XO
#define XO(s)
Definition: Internat.h:31
Languages::sLocaleName
static wxString sLocaleName
Definition: Languages.cpp:322
Languages
Definition: Languages.cpp:81
Languages::GetLocaleName
wxString GetLocaleName()
Definition: Languages.cpp:390
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
Languages::GetLang
wxString GetLang()
Definition: Languages.cpp:395
BasicUI::Get
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:26
Languages.h
name
const TranslatableString name
Definition: Distortion.cpp:98
TranslationExists
static bool TranslationExists(const FilePaths &pathList, wxString code)
Definition: Languages.cpp:65
ReverseLangHash
std::unordered_map< TranslatableString, wxString > ReverseLangHash
Definition: Languages.cpp:53
Internat.h
Languages::GetLanguages
void GetLanguages(FilePaths pathList, wxArrayString &langCodes, TranslatableStrings &langNames)
Definition: Languages.cpp:137
wxArrayStringEx.h
FindFilesInPathList
static void FindFilesInPathList(const wxString &pattern, const FilePaths &pathList, FilePaths &results)
Definition: Languages.cpp:55
Verbatim
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
Definition: TranslatableString.h:321
Languages::GetSystemLanguageCode
wxString GetSystemLanguageCode(const FilePaths &pathList)
Definition: Languages.cpp:83
LangHash
std::unordered_map< wxString, TranslatableString > LangHash
Definition: Languages.cpp:52
Languages::SetLang
wxString SetLang(const FilePaths &pathList, const wxString &lang)
Definition: Languages.cpp:324
Languages::GetLangShort
wxString GetLangShort()
Definition: Languages.cpp:403
Internat::Init
static void Init()
Initialize internationalisation support. Call this once at program start.
Definition: Internat.cpp:78