Language Switching during Runtime and with start args

Implemented runtime Language Switcher in Settings as well as with start args
- System Language currently Crashes TODO: fix
- Start args only sometimes trigger correctly idek?
This commit is contained in:
ApfelTeeSaft 2026-03-07 11:27:31 +01:00
parent e754bad17e
commit 331f79ef5c
8 changed files with 431 additions and 40 deletions

View file

@ -58,6 +58,9 @@
#else
#include "UI\UI.h"
#include "UI\UIScene_PauseMenu.h"
#ifdef _WINDOWS64
#include "..\Windows64\Win64LanguageRuntime.h"
#endif
#endif
#ifdef __PS3__
#include <sys/tty.h>
@ -1797,6 +1800,15 @@ void CMinecraftApp::SetMinecraftLanguage(int iPad, unsigned char ucLanguage)
{
GameSettingsA[iPad]->ucLanguage = ucLanguage;
GameSettingsA[iPad]->bSettingsChanged = true;
#ifdef _WINDOWS64
DWORD currentLocale = XGetLocale();
if (GameSettingsA[iPad]->ucLocale != MINECRAFT_LANGUAGE_DEFAULT)
{
currentLocale = GameSettingsA[iPad]->ucLocale;
}
Win64SetLanguageLocale(ucLanguage, currentLocale);
#endif
}
unsigned char CMinecraftApp::GetMinecraftLanguage(int iPad)
@ -1816,6 +1828,15 @@ void CMinecraftApp::SetMinecraftLocale(int iPad, unsigned char ucLocale)
{
GameSettingsA[iPad]->ucLocale = ucLocale;
GameSettingsA[iPad]->bSettingsChanged = true;
#ifdef _WINDOWS64
DWORD currentLanguage = XGetLanguage();
if (GameSettingsA[iPad]->ucLanguage != MINECRAFT_LANGUAGE_DEFAULT)
{
currentLanguage = GameSettingsA[iPad]->ucLanguage;
}
Win64SetLanguageLocale(currentLanguage, ucLocale);
#endif
}
unsigned char CMinecraftApp::GetMinecraftLocale(int iPad)
@ -9601,6 +9622,20 @@ void CMinecraftApp::getLocale(vector<wstring> &vecWstrLocales)
vector<eMCLang> locales;
DWORD dwSystemLanguage = XGetLanguage( );
DWORD dwSystemLocale = XGetLocale( );
int primaryPad = ProfileManager.GetPrimaryPad();
if (primaryPad >= 0 && primaryPad < XUSER_MAX_COUNT && GameSettingsA[primaryPad] != NULL)
{
if (GameSettingsA[primaryPad]->ucLanguage != MINECRAFT_LANGUAGE_DEFAULT)
{
dwSystemLanguage = GameSettingsA[primaryPad]->ucLanguage;
}
if (GameSettingsA[primaryPad]->ucLocale != MINECRAFT_LANGUAGE_DEFAULT)
{
dwSystemLocale = GameSettingsA[primaryPad]->ucLocale;
}
}
// 4J-PB - restrict the 360 language until we're ready to have them in
@ -9620,7 +9655,7 @@ void CMinecraftApp::getLocale(vector<wstring> &vecWstrLocales)
locales.push_back(eMCLang_esES);
break;
case XC_LANGUAGE_PORTUGUESE :
if(XGetLocale()==XC_LOCALE_BRAZIL)
if(dwSystemLocale==XC_LOCALE_BRAZIL)
{
locales.push_back(eMCLang_ptBR);
}
@ -9641,7 +9676,7 @@ void CMinecraftApp::getLocale(vector<wstring> &vecWstrLocales)
{
case XC_LANGUAGE_ENGLISH:
switch(XGetLocale())
switch(dwSystemLocale)
{
case XC_LOCALE_AUSTRALIA:
case XC_LOCALE_CANADA:
@ -9669,7 +9704,7 @@ void CMinecraftApp::getLocale(vector<wstring> &vecWstrLocales)
locales.push_back(eMCLang_jaJP);
break;
case XC_LANGUAGE_GERMAN :
switch(XGetLocale())
switch(dwSystemLocale)
{
case XC_LOCALE_AUSTRIA:
locales.push_back(eMCLang_deAT);
@ -9683,7 +9718,7 @@ void CMinecraftApp::getLocale(vector<wstring> &vecWstrLocales)
locales.push_back(eMCLang_deDE);
break;
case XC_LANGUAGE_FRENCH :
switch(XGetLocale())
switch(dwSystemLocale)
{
case XC_LOCALE_BELGIUM:
locales.push_back(eMCLang_frBE);
@ -9700,7 +9735,7 @@ void CMinecraftApp::getLocale(vector<wstring> &vecWstrLocales)
locales.push_back(eMCLang_frFR);
break;
case XC_LANGUAGE_SPANISH :
switch(XGetLocale())
switch(dwSystemLocale)
{
case XC_LOCALE_MEXICO:
case XC_LOCALE_ARGENTINA:
@ -9723,7 +9758,7 @@ void CMinecraftApp::getLocale(vector<wstring> &vecWstrLocales)
locales.push_back(eMCLang_koKR);
break;
case XC_LANGUAGE_TCHINESE :
switch(XGetLocale())
switch(dwSystemLocale)
{
case XC_LOCALE_HONG_KONG:
locales.push_back(eMCLang_zhHK);
@ -9739,7 +9774,7 @@ void CMinecraftApp::getLocale(vector<wstring> &vecWstrLocales)
locales.push_back(eMCLang_zhCHT);
break;
case XC_LANGUAGE_PORTUGUESE :
if(XGetLocale()==XC_LOCALE_BRAZIL)
if(dwSystemLocale==XC_LOCALE_BRAZIL)
{
locales.push_back(eMCLang_ptBR);
}
@ -9764,7 +9799,7 @@ void CMinecraftApp::getLocale(vector<wstring> &vecWstrLocales)
locales.push_back(eMCLang_nnNO);
break;
case XC_LANGUAGE_DUTCH :
switch(XGetLocale())
switch(dwSystemLocale)
{
case XC_LOCALE_BELGIUM:
locales.push_back(eMCLang_nlBE);
@ -9775,7 +9810,7 @@ void CMinecraftApp::getLocale(vector<wstring> &vecWstrLocales)
locales.push_back(eMCLang_nlNL);
break;
case XC_LANGUAGE_SCHINESE :
switch(XGetLocale())
switch(dwSystemLocale)
{
case XC_LOCALE_SINGAPORE:
locales.push_back(eMCLang_zhSG);
@ -10205,4 +10240,4 @@ bool CMinecraftApp::HasReachedMainMenu()
{
return m_hasReachedMainMenu;
}
#endif
#endif

View file

@ -2,7 +2,7 @@
#include "UI.h"
#include "UIScene_SettingsOptionsMenu.h"
#if defined(_XBOX_ONE)
#if defined(_XBOX_ONE) || defined(_WINDOWS64)
#define _ENABLE_LANGUAGE_SELECT
#endif
@ -137,14 +137,7 @@ UIScene_SettingsOptionsMenu::UIScene_SettingsOptionsMenu(int iPad, void *initDat
// MGH - disabled the language select for the patch build, we'll re-enable afterwards
// 4J Stu - Removed it with a preprocessor def as we turn this off in various places
#ifdef _ENABLE_LANGUAGE_SELECT
if (app.GetGameStarted())
{
removeControl( &m_buttonLanguageSelect, false );
}
else
{
m_buttonLanguageSelect.init(IDS_LANGUAGE_SELECTOR, eControl_Languages);
}
m_buttonLanguageSelect.init(IDS_LANGUAGE_SELECTOR, eControl_Languages);
#else
removeControl( &m_buttonLanguageSelect, false );
#endif
@ -361,14 +354,7 @@ void UIScene_SettingsOptionsMenu::handleReload()
// MGH - disabled the language select for the patch build, we'll re-enable afterwards
// 4J Stu - Removed it with a preprocessor def as we turn this off in various places
#ifdef _ENABLE_LANGUAGE_SELECT
// 4J-JEV: Changing languages in-game will produce many a bug.
if (app.GetGameStarted())
{
removeControl( &m_buttonLanguageSelect, false );
}
else
{
}
// Enabled on Win64/Xbox One for runtime language switching.
#else
removeControl( &m_buttonLanguageSelect, false );
#endif

View file

@ -1,4 +1,5 @@
#include "stdafx.h"
#include "Windows64\Win64LanguageRuntime.h"
#ifndef __PS3__
//#include <compressapi.h>
#endif // __PS3__
@ -502,9 +503,67 @@ void XMemDestroyDecompressionContext(XMEMDECOMPRESSION_CONTEXT Context)
//#ifndef __PS3__
#if !(defined _DURANGO || defined __PS3__ || defined __ORBIS__ || defined __PSVITA__)
DWORD XGetLanguage() { return 1; }
DWORD XGetLocale() { return 0; }
static DWORD s_win64Language = XC_LANGUAGE_ENGLISH;
static DWORD s_win64Locale = XC_LOCALE_UNITED_STATES;
static bool s_win64TryAllLanguagesAtStartup = false;
static bool s_win64LanguageValidationFailed = false;
static std::wstring s_win64ForceLocaleCode;
static std::vector<std::wstring> s_win64ExtraLanguageCodes;
DWORD XGetLanguage() { return s_win64Language; }
DWORD XGetLocale() { return s_win64Locale; }
DWORD XEnableGuestSignin(BOOL fEnable) { return 0; }
void Win64SetLanguageLocale(DWORD language, DWORD locale)
{
s_win64Language = language;
s_win64Locale = locale;
}
void Win64ForceEnglishLanguage()
{
Win64SetLanguageLocale(XC_LANGUAGE_ENGLISH, XC_LOCALE_UNITED_STATES);
}
void Win64SetForceLocaleCode(const std::wstring& localeCode)
{
s_win64ForceLocaleCode = localeCode;
}
const std::wstring& Win64GetForceLocaleCode()
{
return s_win64ForceLocaleCode;
}
void Win64SetTryAllLanguagesAtStartup(bool enabled)
{
s_win64TryAllLanguagesAtStartup = enabled;
}
bool Win64GetTryAllLanguagesAtStartup()
{
return s_win64TryAllLanguagesAtStartup;
}
void Win64SetLanguageValidationFailed(bool failed)
{
s_win64LanguageValidationFailed = failed;
}
bool Win64GetLanguageValidationFailed()
{
return s_win64LanguageValidationFailed;
}
void Win64SetExtraLanguageCodes(const std::vector<std::wstring>& localeCodes)
{
s_win64ExtraLanguageCodes = localeCodes;
}
const std::vector<std::wstring>& Win64GetExtraLanguageCodes()
{
return s_win64ExtraLanguageCodes;
}
#endif
@ -798,4 +857,4 @@ void StatsCounter::setupStatBoards() {}
#ifdef _DEBUG
void StatsCounter::WipeLeaderboards() {}
#endif
*/
*/

View file

@ -22090,6 +22090,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU</Comman
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ORBIS'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage_Vita|ORBIS'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="Windows64\Win64LanguageRuntime.h" />
<ClInclude Include="Windows64\Windows64_App.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ContentPackage|Durango'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='CONTENTPACKAGE_SYMBOLS|Durango'">true</ExcludedFromBuild>

View file

@ -3790,6 +3790,9 @@
<ClInclude Include="Common\Audio\stb_vorbis.h">
<Filter>Common\Source Files\Audio</Filter>
</ClInclude>
<ClInclude Include="Windows64\Win64LanguageRuntime.h">
<Filter>Windows64</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">

View file

@ -1,5 +1,8 @@
#include "StdAfx.h"
#include "StringTable.h"
#ifdef _WINDOWS64
#include "Windows64\Win64LanguageRuntime.h"
#endif
StringTable::StringTable(void)
{
@ -43,6 +46,59 @@ void StringTable::ProcessStringTableData(void)
vector<wstring> locales;
app.getLocale(locales);
#ifdef _WINDOWS64
std::wstring forcedLocale = Win64GetForceLocaleCode();
if (!forcedLocale.empty())
{
app.DebugPrintf("StringTable:: forced locale request '%ls'.\n", forcedLocale.c_str());
locales.insert(locales.begin(), forcedLocale);
}
const std::vector<std::wstring>& extraLocales = Win64GetExtraLanguageCodes();
for (size_t i = 0; i < extraLocales.size(); ++i)
{
if (!extraLocales[i].empty())
locales.insert(locales.begin(), extraLocales[i]);
}
if (Win64GetTryAllLanguagesAtStartup() && !Win64GetLanguageValidationFailed())
{
static const wchar_t* kStandardLocales[] =
{
L"en-US", L"en-GB", L"de-DE", L"fr-FR", L"es-ES", L"es-MX", L"it-IT", L"pt-PT", L"pt-BR",
L"ja-JP", L"ko-KR", L"zh-CHT", L"ru-RU", L"nl-NL", L"pl-PL", L"sv-SE", L"tr-TR", L"nb-NO",
L"da-DK", L"fi-FI", L"cs-CZ", L"sk-SK", L"el-GR", L"la-LAS"
};
for (unsigned int i = 0; i < sizeof(kStandardLocales) / sizeof(kStandardLocales[0]); ++i)
{
const std::wstring localeCode = kStandardLocales[i];
bool foundLocale = false;
for (size_t j = 0; j < langSizeMap.size(); ++j)
{
if (langSizeMap[j].first == localeCode)
{
foundLocale = true;
break;
}
}
if (!foundLocale)
{
std::wstring msg = L"Missing language localization asset for locale: " + localeCode +
L"\n\nDefaulting language to English (en-US).";
MessageBoxW(NULL, msg.c_str(), L"Minecraft Localization", MB_OK | MB_ICONERROR);
Win64SetLanguageValidationFailed(true);
Win64ForceEnglishLanguage();
locales.clear();
locales.push_back(L"en-US");
locales.push_back(L"en-EN");
break;
}
}
}
#endif
bool foundLang = false;
int64_t bytesToSkip = 0;
int dataSize = 0;
@ -118,6 +174,16 @@ void StringTable::ProcessStringTableData(void)
else
{
app.DebugPrintf("Failed to get language\n");
#ifdef _WINDOWS64
std::wstring forcedLocale = Win64GetForceLocaleCode();
if (!forcedLocale.empty())
{
std::wstring msg = L"Failed to load locale '" + forcedLocale + L"'.\n\nDefaulting to English (en-US).";
MessageBoxW(NULL, msg.c_str(), L"Minecraft Localization", MB_OK | MB_ICONERROR);
Win64ForceEnglishLanguage();
Win64SetForceLocaleCode(L"");
}
#endif
#ifdef _DEBUG
__debugbreak();
#endif
@ -181,8 +247,3 @@ LPCWSTR StringTable::getString(int id)
else
return L"";
}

View file

@ -0,0 +1,23 @@
#pragma once
#include <Windows.h>
#include <string>
#include <vector>
DWORD XGetLanguage();
DWORD XGetLocale();
void Win64SetLanguageLocale(DWORD language, DWORD locale);
void Win64ForceEnglishLanguage();
void Win64SetForceLocaleCode(const std::wstring& localeCode);
const std::wstring& Win64GetForceLocaleCode();
void Win64SetTryAllLanguagesAtStartup(bool enabled);
bool Win64GetTryAllLanguagesAtStartup();
void Win64SetLanguageValidationFailed(bool failed);
bool Win64GetLanguageValidationFailed();
void Win64SetExtraLanguageCodes(const std::vector<std::wstring>& localeCodes);
const std::vector<std::wstring>& Win64GetExtraLanguageCodes();

View file

@ -5,6 +5,8 @@
#include <assert.h>
#include <iostream>
#include <vector>
#include <cwctype>
#include <ShellScalingApi.h>
#include <shellapi.h>
#include "GameConfig\Minecraft.spa.h"
@ -47,6 +49,7 @@
#include "..\GameRenderer.h"
#include "Network\WinsockNetLayer.h"
#include "Windows64_Xuid.h"
#include "Win64LanguageRuntime.h"
#include "Xbox/resource.h"
@ -116,8 +119,146 @@ struct Win64LaunchOptions
int screenMode;
bool serverMode;
bool fullscreen;
bool tryAllLanguages;
std::wstring forceLocaleCode;
std::vector<std::wstring> extraLocaleCodes;
};
static bool ParseLanguageCode(const std::wstring& localeCode, DWORD& outLanguage, DWORD& outLocale);
static bool IsOptionToken(const std::wstring& arg, const wchar_t* longName, const wchar_t* shortName)
{
const std::wstring longDash = L"--" + std::wstring(longName);
const std::wstring shortDash = L"-" + std::wstring(shortName);
const std::wstring longSlash = L"/" + std::wstring(longName);
const std::wstring shortSlash = L"/" + std::wstring(shortName);
return (_wcsicmp(arg.c_str(), longDash.c_str()) == 0)
|| (_wcsicmp(arg.c_str(), shortDash.c_str()) == 0)
|| (_wcsicmp(arg.c_str(), longSlash.c_str()) == 0)
|| (_wcsicmp(arg.c_str(), shortSlash.c_str()) == 0);
}
static bool TryReadOptionValue(const std::wstring& arg, const wchar_t* longName, const wchar_t* shortName, std::wstring& outValue)
{
const std::wstring longEq1 = L"--" + std::wstring(longName) + L"=";
const std::wstring longEq2 = L"--" + std::wstring(longName) + L":";
const std::wstring shortEq1 = L"-" + std::wstring(shortName) + L"=";
const std::wstring shortEq2 = L"-" + std::wstring(shortName) + L":";
const std::wstring longSlash1 = L"/" + std::wstring(longName) + L":";
const std::wstring shortSlash1 = L"/" + std::wstring(shortName) + L":";
if (arg.size() >= longEq1.size() && _wcsnicmp(arg.c_str(), longEq1.c_str(), (int)longEq1.size()) == 0) { outValue = arg.substr(longEq1.size()); return true; }
if (arg.size() >= longEq2.size() && _wcsnicmp(arg.c_str(), longEq2.c_str(), (int)longEq2.size()) == 0) { outValue = arg.substr(longEq2.size()); return true; }
if (arg.size() >= shortEq1.size() && _wcsnicmp(arg.c_str(), shortEq1.c_str(), (int)shortEq1.size()) == 0) { outValue = arg.substr(shortEq1.size()); return true; }
if (arg.size() >= shortEq2.size() && _wcsnicmp(arg.c_str(), shortEq2.c_str(), (int)shortEq2.size()) == 0) { outValue = arg.substr(shortEq2.size()); return true; }
if (arg.size() >= longSlash1.size() && _wcsnicmp(arg.c_str(), longSlash1.c_str(), (int)longSlash1.size()) == 0) { outValue = arg.substr(longSlash1.size()); return true; }
if (arg.size() >= shortSlash1.size() && _wcsnicmp(arg.c_str(), shortSlash1.c_str(), (int)shortSlash1.size()) == 0) { outValue = arg.substr(shortSlash1.size()); return true; }
return false;
}
static void DetectWinApiDefaultLanguage(DWORD& outLanguage, DWORD& outLocale)
{
outLanguage = XC_LANGUAGE_ENGLISH;
outLocale = XC_LOCALE_UNITED_STATES;
wchar_t localeName[LOCALE_NAME_MAX_LENGTH] = { 0 };
if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) <= 0)
{
return;
}
std::wstring detected = localeName;
for (size_t i = 0; i < detected.size(); ++i)
{
if (detected[i] == L'_')
detected[i] = L'-';
}
if (!ParseLanguageCode(detected, outLanguage, outLocale))
{
if (detected.size() >= 2)
{
std::wstring twoLetter = detected.substr(0, 2);
if (twoLetter == L"en") ParseLanguageCode(L"en-US", outLanguage, outLocale);
else if (twoLetter == L"de") ParseLanguageCode(L"de-DE", outLanguage, outLocale);
else if (twoLetter == L"fr") ParseLanguageCode(L"fr-FR", outLanguage, outLocale);
else if (twoLetter == L"es") ParseLanguageCode(L"es-ES", outLanguage, outLocale);
else if (twoLetter == L"it") ParseLanguageCode(L"it-IT", outLanguage, outLocale);
else if (twoLetter == L"pt") ParseLanguageCode(L"pt-PT", outLanguage, outLocale);
else if (twoLetter == L"ja") ParseLanguageCode(L"ja-JP", outLanguage, outLocale);
else if (twoLetter == L"ko") ParseLanguageCode(L"ko-KR", outLanguage, outLocale);
else if (twoLetter == L"ru") ParseLanguageCode(L"ru-RU", outLanguage, outLocale);
else if (twoLetter == L"nl") ParseLanguageCode(L"nl-NL", outLanguage, outLocale);
else if (twoLetter == L"pl") ParseLanguageCode(L"pl-PL", outLanguage, outLocale);
else if (twoLetter == L"sv") ParseLanguageCode(L"sv-SE", outLanguage, outLocale);
else if (twoLetter == L"tr") ParseLanguageCode(L"tr-TR", outLanguage, outLocale);
else if (twoLetter == L"da") ParseLanguageCode(L"da-DK", outLanguage, outLocale);
else if (twoLetter == L"fi") ParseLanguageCode(L"fi-FI", outLanguage, outLocale);
else if (twoLetter == L"el") ParseLanguageCode(L"el-GR", outLanguage, outLocale);
else if (twoLetter == L"cs") ParseLanguageCode(L"cs-CZ", outLanguage, outLocale);
else if (twoLetter == L"sk") ParseLanguageCode(L"sk-SK", outLanguage, outLocale);
else if (twoLetter == L"zh") ParseLanguageCode(L"zh-CHT", outLanguage, outLocale);
}
}
}
static std::wstring TrimArg(const std::wstring& value)
{
if (value.empty())
return value;
size_t start = 0;
while (start < value.size() && iswspace(value[start]))
++start;
size_t end = value.size();
while (end > start && iswspace(value[end - 1]))
--end;
return value.substr(start, end - start);
}
static bool ParseLanguageCode(const std::wstring& localeCode, DWORD& outLanguage, DWORD& outLocale)
{
if (localeCode == L"en-US" || localeCode == L"en")
{
outLanguage = XC_LANGUAGE_ENGLISH;
outLocale = XC_LOCALE_UNITED_STATES;
return true;
}
if (localeCode == L"en-GB")
{
outLanguage = XC_LANGUAGE_ENGLISH;
outLocale = XC_LOCALE_GREAT_BRITAIN;
return true;
}
if (localeCode == L"de-DE") { outLanguage = XC_LANGUAGE_GERMAN; outLocale = XC_LOCALE_GERMANY; return true; }
if (localeCode == L"fr-FR") { outLanguage = XC_LANGUAGE_FRENCH; outLocale = XC_LOCALE_FRANCE; return true; }
if (localeCode == L"es-ES") { outLanguage = XC_LANGUAGE_SPANISH; outLocale = XC_LOCALE_SPAIN; return true; }
if (localeCode == L"es-MX" || localeCode == L"la-LAS") { outLanguage = XC_LANGUAGE_SPANISH; outLocale = XC_LOCALE_LATIN_AMERICA; return true; }
if (localeCode == L"it-IT") { outLanguage = XC_LANGUAGE_ITALIAN; outLocale = XC_LOCALE_ITALY; return true; }
if (localeCode == L"pt-BR") { outLanguage = XC_LANGUAGE_PORTUGUESE; outLocale = XC_LOCALE_BRAZIL; return true; }
if (localeCode == L"pt-PT") { outLanguage = XC_LANGUAGE_PORTUGUESE; outLocale = XC_LOCALE_PORTUGAL; return true; }
if (localeCode == L"ja-JP") { outLanguage = XC_LANGUAGE_JAPANESE; outLocale = XC_LOCALE_JAPAN; return true; }
if (localeCode == L"ko-KR") { outLanguage = XC_LANGUAGE_KOREAN; outLocale = XC_LOCALE_KOREA; return true; }
if (localeCode == L"zh-CHT" || localeCode == L"zh-TW") { outLanguage = XC_LANGUAGE_TCHINESE; outLocale = XC_LOCALE_TAIWAN; return true; }
if (localeCode == L"ru-RU") { outLanguage = XC_LANGUAGE_RUSSIAN; outLocale = XC_LOCALE_RUSSIAN_FEDERATION; return true; }
if (localeCode == L"nl-NL") { outLanguage = XC_LANGUAGE_DUTCH; outLocale = XC_LOCALE_NETHERLANDS; return true; }
if (localeCode == L"pl-PL") { outLanguage = XC_LANGUAGE_POLISH; outLocale = XC_LOCALE_POLAND; return true; }
if (localeCode == L"sv-SE") { outLanguage = XC_LANGUAGE_SWEDISH; outLocale = XC_LOCALE_SWEDEN; return true; }
if (localeCode == L"tr-TR") { outLanguage = XC_LANGUAGE_TURKISH; outLocale = XC_LOCALE_TURKEY; return true; }
if (localeCode == L"nb-NO" || localeCode == L"no-NO") { outLanguage = XC_LANGUAGE_BNORWEGIAN; outLocale = XC_LOCALE_NORWAY; return true; }
if (localeCode == L"da-DK" || localeCode == L"da-DA") { outLanguage = XC_LANGUAGE_DANISH; outLocale = XC_LOCALE_DENMARK; return true; }
if (localeCode == L"fi-FI") { outLanguage = XC_LANGUAGE_FINISH; outLocale = XC_LOCALE_FINLAND; return true; }
if (localeCode == L"el-GR") { outLanguage = XC_LANGUAGE_GREEK; outLocale = XC_LOCALE_GREECE; return true; }
if (localeCode == L"cs-CZ") { outLanguage = XC_LANGUAGE_CZECH; outLocale = XC_LOCALE_CZECH_REPUBLIC; return true; }
if (localeCode == L"sk-SK") { outLanguage = XC_LANGUAGE_SLOVAK; outLocale = XC_LOCALE_SLOVAK_REPUBLIC; return true; }
return false;
}
static void CopyWideArgToAnsi(LPCWSTR source, char* dest, size_t destSize)
{
if (destSize == 0)
@ -201,6 +342,8 @@ static Win64LaunchOptions ParseLaunchOptions()
Win64LaunchOptions options = {};
options.screenMode = 0;
options.serverMode = false;
options.fullscreen = false;
options.tryAllLanguages = false;
g_Win64MultiplayerJoin = false;
g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT;
@ -209,6 +352,7 @@ static Win64LaunchOptions ParseLaunchOptions()
g_Win64DedicatedServerBindIP[0] = 0;
int argc = 0;
app.DebugPrintf("[Win64] Raw command line: %ls\n", GetCommandLineW());
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (argv == NULL)
return options;
@ -232,11 +376,14 @@ static Win64LaunchOptions ParseLaunchOptions()
for (int i = 1; i < argc; ++i)
{
if (_wcsicmp(argv[i], L"-name") == 0 && (i + 1) < argc)
std::wstring currentArg = argv[i];
std::wstring inlineValue;
if (IsOptionToken(currentArg, L"name", L"name") && (i + 1) < argc)
{
CopyWideArgToAnsi(argv[++i], g_Win64Username, sizeof(g_Win64Username));
}
else if (_wcsicmp(argv[i], L"-ip") == 0 && (i + 1) < argc)
else if (IsOptionToken(currentArg, L"ip", L"ip") && (i + 1) < argc)
{
char ipBuf[256];
CopyWideArgToAnsi(argv[++i], ipBuf, sizeof(ipBuf));
@ -250,7 +397,7 @@ static Win64LaunchOptions ParseLaunchOptions()
g_Win64MultiplayerJoin = true;
}
}
else if (_wcsicmp(argv[i], L"-port") == 0 && (i + 1) < argc)
else if (IsOptionToken(currentArg, L"port", L"port") && (i + 1) < argc)
{
wchar_t* endPtr = NULL;
long port = wcstol(argv[++i], &endPtr, 10);
@ -262,8 +409,52 @@ static Win64LaunchOptions ParseLaunchOptions()
g_Win64MultiplayerPort = (int)port;
}
}
else if (_wcsicmp(argv[i], L"-fullscreen") == 0)
else if (IsOptionToken(currentArg, L"fullscreen", L"fullscreen"))
options.fullscreen = true;
else if (TryReadOptionValue(currentArg, L"language", L"lang", inlineValue))
{
options.forceLocaleCode = TrimArg(inlineValue);
}
else if (IsOptionToken(currentArg, L"language", L"lang") && (i + 1) < argc)
{
options.forceLocaleCode = TrimArg(argv[++i]);
}
else if (TryReadOptionValue(currentArg, L"addlanguages", L"languages", inlineValue)
|| TryReadOptionValue(currentArg, L"languages", L"languages", inlineValue))
{
wstring languagesArg = TrimArg(inlineValue);
size_t start = 0;
while (start < languagesArg.size())
{
size_t comma = languagesArg.find(L',', start);
wstring token = (comma == wstring::npos) ? languagesArg.substr(start) : languagesArg.substr(start, comma - start);
if (!token.empty())
options.extraLocaleCodes.push_back(token);
if (comma == wstring::npos)
break;
start = comma + 1;
}
}
else if ((IsOptionToken(currentArg, L"addlanguages", L"languages") || IsOptionToken(currentArg, L"languages", L"languages")) && (i + 1) < argc)
{
wstring languagesArg = TrimArg(argv[++i]);
size_t start = 0;
while (start < languagesArg.size())
{
size_t comma = languagesArg.find(L',', start);
wstring token = (comma == wstring::npos) ? languagesArg.substr(start) : languagesArg.substr(start, comma - start);
token = TrimArg(token);
if (!token.empty())
options.extraLocaleCodes.push_back(token);
if (comma == wstring::npos)
break;
start = comma + 1;
}
}
else if (IsOptionToken(currentArg, L"tryalllanguages", L"tryalllanguages"))
{
options.tryAllLanguages = true;
}
}
LocalFree(argv);
@ -1240,6 +1431,38 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
// Load stuff from launch options, including username
Win64LaunchOptions launchOptions = ParseLaunchOptions();
app.DebugPrintf("[Win64] Launch options parsed: language='%ls', extraLanguages=%d, tryAll=%s\n",
launchOptions.forceLocaleCode.c_str(),
(int)launchOptions.extraLocaleCodes.size(),
launchOptions.tryAllLanguages ? "true" : "false");
Win64SetForceLocaleCode(launchOptions.forceLocaleCode);
Win64SetExtraLanguageCodes(launchOptions.extraLocaleCodes);
Win64SetTryAllLanguagesAtStartup(launchOptions.tryAllLanguages);
if (!launchOptions.forceLocaleCode.empty())
{
DWORD language = XC_LANGUAGE_ENGLISH;
DWORD locale = XC_LOCALE_UNITED_STATES;
if (ParseLanguageCode(launchOptions.forceLocaleCode, language, locale))
{
Win64SetLanguageLocale(language, locale);
}
else
{
app.DebugPrintf("[Win64] Invalid -language argument '%ls' - defaulting to English.\n", launchOptions.forceLocaleCode.c_str());
MessageBoxW(NULL, L"Unsupported -language code. Defaulting to English (en-US).", L"Minecraft Localization", MB_OK | MB_ICONWARNING);
Win64ForceEnglishLanguage();
}
}
else
{
DWORD language = XC_LANGUAGE_ENGLISH;
DWORD locale = XC_LOCALE_UNITED_STATES;
DetectWinApiDefaultLanguage(language, locale);
Win64SetLanguageLocale(language, locale);
app.DebugPrintf("[Win64] Default locale from WinAPI: lang=%u locale=%u\n", (unsigned int)language, (unsigned int)locale);
}
ApplyScreenMode(launchOptions.screenMode);
// Ensure uid.dat exists from startup in client mode (before any multiplayer/login path).
@ -1927,4 +2150,4 @@ void MemPixStuff()
PIXAddNamedCounter(((float)allSectsTotal)/(4096.0f),"MemSect total pages");
}
#endif
#endif