Browse Source

Add a `--lang` command line argument

This allows forcing the locale without changing the settings.

The `forceLocale` option also lets us avoid changing the locale in settings if fonts.mpq is missing, fixing the following scenario:
1. System locale is ja
2. Launch the game -- fonts are missing
3. Close the game, download fonts.mpq
4. Launch the game again -- it is now in English because the settings have changed
pull/5601/head
Gleb Mazovetskiy 3 years ago
parent
commit
56f94c7471
  1. 19
      Source/diablo.cpp
  2. 2
      Source/init.cpp
  3. 31
      Source/utils/language.cpp
  4. 4
      Source/utils/language.h
  5. 2
      tools/measure_timedemo_performance.py

19
Source/diablo.cpp

@ -907,6 +907,7 @@ void PrintHelpOption(string_view flags, string_view description)
PrintHelpOption("--data-dir", _(/* TRANSLATORS: Commandline Option */ "Specify the folder of diabdat.mpq")); PrintHelpOption("--data-dir", _(/* TRANSLATORS: Commandline Option */ "Specify the folder of diabdat.mpq"));
PrintHelpOption("--save-dir", _(/* TRANSLATORS: Commandline Option */ "Specify the folder of save files")); PrintHelpOption("--save-dir", _(/* TRANSLATORS: Commandline Option */ "Specify the folder of save files"));
PrintHelpOption("--config-dir", _(/* TRANSLATORS: Commandline Option */ "Specify the location of diablo.ini")); PrintHelpOption("--config-dir", _(/* TRANSLATORS: Commandline Option */ "Specify the location of diablo.ini"));
PrintHelpOption("--lang", _(/* TRANSLATORS: Commandline Option */ "Specify the language code (e.g. en or pt_BR)"));
PrintHelpOption("-n", _(/* TRANSLATORS: Commandline Option */ "Skip startup videos")); PrintHelpOption("-n", _(/* TRANSLATORS: Commandline Option */ "Skip startup videos"));
PrintHelpOption("-f", _(/* TRANSLATORS: Commandline Option */ "Display frames per second")); PrintHelpOption("-f", _(/* TRANSLATORS: Commandline Option */ "Display frames per second"));
PrintHelpOption("--verbose", _(/* TRANSLATORS: Commandline Option */ "Enable verbose logging")); PrintHelpOption("--verbose", _(/* TRANSLATORS: Commandline Option */ "Enable verbose logging"));
@ -939,7 +940,7 @@ void PrintHelpOption(string_view flags, string_view description)
void PrintFlagsRequiresArgument(string_view flag) void PrintFlagsRequiresArgument(string_view flag)
{ {
printInConsole(flag); printInConsole(flag);
printInConsole("requires an argument"); printInConsole(" requires an argument");
printNewlineInConsole(); printNewlineInConsole();
} }
@ -968,26 +969,32 @@ void DiabloParseFlags(int argc, char **argv)
} else if (arg == "--data-dir") { } else if (arg == "--data-dir") {
if (i + 1 == argc) { if (i + 1 == argc) {
PrintFlagsRequiresArgument("--data-dir"); PrintFlagsRequiresArgument("--data-dir");
diablo_quit(0); diablo_quit(64);
} }
paths::SetBasePath(argv[++i]); paths::SetBasePath(argv[++i]);
} else if (arg == "--save-dir") { } else if (arg == "--save-dir") {
if (i + 1 == argc) { if (i + 1 == argc) {
PrintFlagsRequiresArgument("--save-dir"); PrintFlagsRequiresArgument("--save-dir");
diablo_quit(0); diablo_quit(64);
} }
paths::SetPrefPath(argv[++i]); paths::SetPrefPath(argv[++i]);
} else if (arg == "--config-dir") { } else if (arg == "--config-dir") {
if (i + 1 == argc) { if (i + 1 == argc) {
PrintFlagsRequiresArgument("--config-dir"); PrintFlagsRequiresArgument("--config-dir");
diablo_quit(0); diablo_quit(64);
} }
paths::SetConfigPath(argv[++i]); paths::SetConfigPath(argv[++i]);
} else if (arg == "--lang") {
if (i + 1 == argc) {
PrintFlagsRequiresArgument("--lang");
diablo_quit(64);
}
forceLocale = argv[++i];
#ifndef DISABLE_DEMOMODE #ifndef DISABLE_DEMOMODE
} else if (arg == "--demo") { } else if (arg == "--demo") {
if (i + 1 == argc) { if (i + 1 == argc) {
PrintFlagsRequiresArgument("--demo"); PrintFlagsRequiresArgument("--demo");
diablo_quit(0); diablo_quit(64);
} }
demoNumber = SDL_atoi(argv[++i]); demoNumber = SDL_atoi(argv[++i]);
gbShowIntro = false; gbShowIntro = false;
@ -996,7 +1003,7 @@ void DiabloParseFlags(int argc, char **argv)
} else if (arg == "--record") { } else if (arg == "--record") {
if (i + 1 == argc) { if (i + 1 == argc) {
PrintFlagsRequiresArgument("--record"); PrintFlagsRequiresArgument("--record");
diablo_quit(0); diablo_quit(64);
} }
recordNumber = SDL_atoi(argv[++i]); recordNumber = SDL_atoi(argv[++i]);
} else if (arg == "--create-reference") { } else if (arg == "--create-reference") {

2
Source/init.cpp

@ -216,7 +216,7 @@ void LoadLanguageArchive()
lang_mpq = std::nullopt; lang_mpq = std::nullopt;
#endif #endif
string_view code = *sgOptions.Language.code; string_view code = GetLanguageCode();
if (code != "en") { if (code != "en") {
std::string langMpqName { code }; std::string langMpqName { code };
#ifdef UNPACKED_MPQS #ifdef UNPACKED_MPQS

31
Source/utils/language.cpp

@ -19,6 +19,8 @@
#define MO_MAGIC 0x950412de #define MO_MAGIC 0x950412de
std::string forceLocale;
namespace { namespace {
using namespace devilution; using namespace devilution;
@ -112,16 +114,16 @@ string_view TrimRight(string_view str)
return str; return str;
} }
// English, Danish, Spanish, Italian, Swedish
unsigned PluralForms = 2;
tl::function_ref<int(int n)> GetLocalPluralId = [](int n) -> int { return n != 1 ? 1 : 0; };
/** plural=(n != 1); */ /** plural=(n != 1); */
int PluralIfNotOne(int n) int PluralIfNotOne(int n)
{ {
return n != 1 ? 1 : 0; return n != 1 ? 1 : 0;
} }
// English, Danish, Spanish, Italian, Swedish
unsigned PluralForms = 2;
tl::function_ref<int(int n)> GetLocalPluralId = PluralIfNotOne;
/** /**
* Match plural=(n != 1);" * Match plural=(n != 1);"
*/ */
@ -148,7 +150,7 @@ void SetPluralForm(string_view expression)
// en, bg, da, de, es, it, sv // en, bg, da, de, es, it, sv
if (expression == "(n != 1)") { if (expression == "(n != 1)") {
GetLocalPluralId = &PluralIfNotOne; GetLocalPluralId = PluralIfNotOne;
return; return;
} }
@ -340,9 +342,16 @@ bool HasTranslation(const std::string &locale)
}); });
} }
string_view GetLanguageCode()
{
if (!forceLocale.empty())
return forceLocale;
return *sgOptions.Language.code;
}
bool IsSmallFontTall() bool IsSmallFontTall()
{ {
const string_view code = (*sgOptions.Language.code).substr(0, 2); const string_view code = GetLanguageCode().substr(0, 2);
return code == "zh" || code == "ja" || code == "ko"; return code == "zh" || code == "ja" || code == "ko";
} }
@ -356,14 +365,14 @@ void LanguageInitialize()
UiErrorOkDialog( UiErrorOkDialog(
"Missing fonts.mpq", "Missing fonts.mpq",
StrCat("fonts.mpq is required for locale \"", StrCat("fonts.mpq is required for locale \"",
*sgOptions.Language.code, GetLanguageCode(),
"\"\n\n" "\"\n\n"
"Please download fonts.mpq from:\n" "Please download fonts.mpq from:\n"
"github.com/diasurgical/\ndevilutionx-assets/releases")); "github.com/diasurgical/\ndevilutionx-assets/releases"));
sgOptions.Language.code = "en"; forceLocale = "en";
} }
const std::string lang(*sgOptions.Language.code); const std::string lang(GetLanguageCode());
AssetHandle handle; AssetHandle handle;
const uint32_t loadTranslationsStart = SDL_GetTicks(); const uint32_t loadTranslationsStart = SDL_GetTicks();
@ -377,7 +386,9 @@ void LanguageInitialize()
break; break;
} }
if (!handle.ok()) { if (!handle.ok()) {
GetLocalPluralId = &PluralIfNotOne; // Reset to English plural form // Reset to English, which is always available:
forceLocale = "en";
GetLocalPluralId = PluralIfNotOne;
return; return;
} }

4
Source/utils/language.h

@ -10,6 +10,10 @@
#define N_(x) (x) #define N_(x) (x)
#define P_(context, x) (x) #define P_(context, x) (x)
extern std::string forceLocale;
devilution::string_view GetLanguageCode();
bool HasTranslation(const std::string &locale); bool HasTranslation(const std::string &locale);
void LanguageInitialize(); void LanguageInitialize();

2
tools/measure_timedemo_performance.py

@ -15,7 +15,7 @@ class RunMetrics(NamedTuple):
def measure(binary: str) -> RunMetrics: def measure(binary: str) -> RunMetrics:
result: subprocess.CompletedProcess = subprocess.run( result: subprocess.CompletedProcess = subprocess.run(
[binary, '--diablo', '--spawn', '--demo', '0', '--timedemo'], capture_output=True) [binary, '--diablo', '--spawn', '--lang', 'en', '--demo', '0', '--timedemo'], capture_output=True)
match = _TIME_AND_FPS_REGEX.search(result.stderr) match = _TIME_AND_FPS_REGEX.search(result.stderr)
if not match: if not match:
raise Exception(f"Failed to parse output in:\n{result.stderr}") raise Exception(f"Failed to parse output in:\n{result.stderr}")

Loading…
Cancel
Save