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.
This commit is contained in:
Robert Müller 2023-03-20 22:01:26 +01:00
parent cddbc78592
commit a91b51c8a2
4 changed files with 63 additions and 3 deletions

View file

@ -2028,19 +2028,22 @@ 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;

View file

@ -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

View file

@ -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

View file

@ -56,6 +56,7 @@ public:
void LoadIndexfile(class IStorage *pStorage, class IConsole *pConsole);
const std::vector<CLanguage> &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);