+namespace
+{
+
+#if !wxUSE_UNICODE
+// We need to keep track of (char*) msgids in non-Unicode legacy builds. Instead
+// of making the public wxMsgCatalog and wxTranslationsLoader APIs ugly, we
+// store them in this global map.
+wxStringToStringHashMap gs_msgIdCharset;
+#endif
+
+// ----------------------------------------------------------------------------
+// Platform specific helpers
+// ----------------------------------------------------------------------------
+
+void LogTraceArray(const char *prefix, const wxArrayString& arr)
+{
+ wxLogTrace(TRACE_I18N, "%s: [%s]", prefix, wxJoin(arr, ','));
+}
+
+// Use locale-based detection as a fallback
+wxString GetPreferredUILanguageFallback(const wxArrayString& WXUNUSED(available))
+{
+ const wxString lang = wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage());
+ wxLogTrace(TRACE_I18N, " - obtained best language from locale: %s", lang);
+ return lang;
+}
+
+#ifdef __WINDOWS__
+
+wxString GetPreferredUILanguage(const wxArrayString& available)
+{
+ typedef BOOL (WINAPI *GetUserPreferredUILanguages_t)(DWORD, PULONG, PWSTR, PULONG);
+ static GetUserPreferredUILanguages_t s_pfnGetUserPreferredUILanguages = NULL;
+ static bool s_initDone = false;
+ if ( !s_initDone )
+ {
+ wxLoadedDLL dllKernel32("kernel32.dll");
+ wxDL_INIT_FUNC(s_pfn, GetUserPreferredUILanguages, dllKernel32);
+ s_initDone = true;
+ }
+
+ if ( s_pfnGetUserPreferredUILanguages )
+ {
+ ULONG numLangs;
+ ULONG bufferSize = 0;
+ if ( (*s_pfnGetUserPreferredUILanguages)(MUI_LANGUAGE_NAME,
+ &numLangs,
+ NULL,
+ &bufferSize) )
+ {
+ wxScopedArray<WCHAR> langs(new WCHAR[bufferSize]);
+ if ( (*s_pfnGetUserPreferredUILanguages)(MUI_LANGUAGE_NAME,
+ &numLangs,
+ langs.get(),
+ &bufferSize) )
+ {
+ wxArrayString preferred;
+
+ WCHAR *buf = langs.get();
+ for ( unsigned i = 0; i < numLangs; i++ )
+ {
+ const wxString lang(buf);
+ preferred.push_back(lang);
+ buf += lang.length() + 1;
+ }
+ LogTraceArray(" - system preferred languages", preferred);
+
+ for ( wxArrayString::const_iterator j = preferred.begin();
+ j != preferred.end();
+ ++j )
+ {
+ wxString lang(*j);
+ lang.Replace("-", "_");
+ if ( available.Index(lang, /*bCase=*/false) != wxNOT_FOUND )
+ return lang;
+ size_t pos = lang.find('_');
+ if ( pos != wxString::npos )
+ {
+ lang = lang.substr(0, pos);
+ if ( available.Index(lang, /*bCase=*/false) != wxNOT_FOUND )
+ return lang;
+ }
+ }
+ }
+ }
+ }
+
+ return GetPreferredUILanguageFallback(available);
+}
+
+#elif defined(__WXOSX__)
+
+void LogTraceArray(const char *prefix, CFArrayRef arr)
+{
+ wxString s;
+ const unsigned count = CFArrayGetCount(arr);
+ if ( count )
+ {
+ s += wxCFStringRef::AsString((CFStringRef)CFArrayGetValueAtIndex(arr, 0));
+ for ( unsigned i = 1 ; i < count; i++ )
+ s += "," + wxCFStringRef::AsString((CFStringRef)CFArrayGetValueAtIndex(arr, i));
+ }
+ wxLogTrace(TRACE_I18N, "%s: [%s]", prefix, s);
+}
+
+wxString GetPreferredUILanguage(const wxArrayString& available)
+{
+ wxStringToStringHashMap availableNormalized;
+ wxCFRef<CFMutableArrayRef> availableArr(
+ CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
+
+ for ( wxArrayString::const_iterator i = available.begin();
+ i != available.end();
+ ++i )
+ {
+ wxString lang(*i);
+ wxCFStringRef code_wx(*i);
+ wxCFStringRef code_norm(
+ CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorDefault, code_wx));
+ CFArrayAppendValue(availableArr, code_norm);
+ availableNormalized[code_norm.AsString()] = *i;
+ }
+ LogTraceArray(" - normalized available list", availableArr);
+
+ wxCFRef<CFArrayRef> prefArr(
+ CFBundleCopyLocalizationsForPreferences(availableArr, NULL));
+ LogTraceArray(" - system preferred languages", prefArr);
+
+ unsigned prefArrLength = CFArrayGetCount(prefArr);
+ if ( prefArrLength > 0 )
+ {
+ // Lookup the name in 'available' by index -- we need to get the
+ // original value corresponding to the normalized one chosen.
+ wxString lang(wxCFStringRef::AsString((CFStringRef)CFArrayGetValueAtIndex(prefArr, 0)));
+ wxStringToStringHashMap::const_iterator i = availableNormalized.find(lang);
+ if ( i == availableNormalized.end() )
+ return lang;
+ else
+ return i->second;
+ }
+
+ return GetPreferredUILanguageFallback(available);
+}
+
+#else
+
+// On Unix, there's just one language=locale setting, so we should always
+// use that.
+#define GetPreferredUILanguage GetPreferredUILanguageFallback
+
+#endif
+
+} // anonymous namespace
+