From a91b51c8a2a7a8fc9849596bd3bbb8b638c3acd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Mon, 20 Mar 2023 22:01:26 +0100 Subject: [PATCH] Select language on first start based on user locale On first client start (when `cl_show_welcome` is `1`), determine the user locale with `os_locale_str` and initially select the most fitting language for this locale. For this the language index must also be loaded immediately on client launch, before the initial language is loaded. --- src/game/client/components/menus_settings.cpp | 9 ++-- src/game/client/gameclient.cpp | 3 ++ src/game/localization.cpp | 53 +++++++++++++++++++ src/game/localization.h | 1 + 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index e264a3c78..7045201f5 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -2028,18 +2028,21 @@ void CMenus::RenderSettingsSound(CUIRect MainView) bool CMenus::RenderLanguageSelection(CUIRect MainView) { - static int s_SelectedLanguage = -1; + static int s_SelectedLanguage = -2; // -2 = unloaded, -1 = unset static CListBox s_ListBox; - if(g_Localization.Languages().empty()) + if(s_SelectedLanguage == -2) { - g_Localization.LoadIndexfile(Storage(), Console()); + s_SelectedLanguage = -1; for(size_t i = 0; i < g_Localization.Languages().size(); i++) + { if(str_comp(g_Localization.Languages()[i].m_FileName.c_str(), g_Config.m_ClLanguagefile) == 0) { s_SelectedLanguage = i; + s_ListBox.ScrollToSelected(); break; } + } } const int OldSelected = s_SelectedLanguage; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index dece62d1e..859768131 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -243,6 +243,9 @@ void CGameClient::OnInit() } // set the language + g_Localization.LoadIndexfile(Storage(), Console()); + if(g_Config.m_ClShowWelcome) + g_Localization.SelectDefaultLanguage(Console(), g_Config.m_ClLanguagefile, sizeof(g_Config.m_ClLanguagefile)); g_Localization.Load(g_Config.m_ClLanguagefile, Storage(), Console()); // TODO: this should be different diff --git a/src/game/localization.cpp b/src/game/localization.cpp index c27c0a094..6092cf607 100644 --- a/src/game/localization.cpp +++ b/src/game/localization.cpp @@ -146,6 +146,59 @@ void CLocalizationDatabase::LoadIndexfile(IStorage *pStorage, IConsole *pConsole std::sort(m_vLanguages.begin(), m_vLanguages.end()); } +void CLocalizationDatabase::SelectDefaultLanguage(IConsole *pConsole, char *pFilename, size_t Length) const +{ + char aLocaleStr[128]; + os_locale_str(aLocaleStr, sizeof(aLocaleStr)); + + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "Choosing default language based on user locale '%s'", aLocaleStr); + pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "localization", aBuf); + + while(true) + { + const CLanguage *pPrefixMatch = nullptr; + for(const auto &Language : Languages()) + { + for(const auto &LanguageCode : Language.m_vLanguageCodes) + { + if(LanguageCode == aLocaleStr) + { + // Exact match found, use it immediately + str_copy(pFilename, Language.m_FileName.c_str(), Length); + return; + } + else if(LanguageCode.rfind(aLocaleStr, 0) == 0) + { + // Locale is prefix of language code, e.g. locale is "en" and current language is "en-US" + pPrefixMatch = &Language; + } + } + } + // Use prefix match if no exact match was found + if(pPrefixMatch) + { + str_copy(pFilename, pPrefixMatch->m_FileName.c_str(), Length); + return; + } + + // Remove last segment of locale string and try again with more generic locale, e.g. "en-US" -> "en" + int i = str_length(aLocaleStr) - 1; + for(; i >= 0; --i) + { + if(aLocaleStr[i] == '-') + { + aLocaleStr[i] = '\0'; + break; + } + } + + // Stop if no more locale segments are left + if(i == 0) + break; + } +} + bool CLocalizationDatabase::Load(const char *pFilename, IStorage *pStorage, IConsole *pConsole) { // empty string means unload diff --git a/src/game/localization.h b/src/game/localization.h index de183983c..b6d3173c5 100644 --- a/src/game/localization.h +++ b/src/game/localization.h @@ -56,6 +56,7 @@ public: void LoadIndexfile(class IStorage *pStorage, class IConsole *pConsole); const std::vector &Languages() const { return m_vLanguages; } + void SelectDefaultLanguage(class IConsole *pConsole, char *pFilename, size_t Length) const; bool Load(const char *pFilename, class IStorage *pStorage, class IConsole *pConsole);