fix(make_game): a launcher's build keeps the folder's language, and a first one takes the system's (#2184) #2284

Merged
jarynclouatre merged 2 commits from fix/launcher-build-language into main 2026-11-11 22:01:19 +00:00
jarynclouatre commented 2026-11-11 13:27:30 +00:00 (Migrated from github.com)

A game built through RecompLauncher is always in English, and a language the player set in language.txt goes back to English the next time the launcher builds the folder (#2184).

Why

The launcher builds with --cli --prepare --game <copy> (recomplauncher.json): no window, and no --language. make_game.py took en-US for that and wrote it to language.txt at the end of every build. Only --update read the folder's own language first.

So the one way a launcher's player has to change the language, the code in language.txt (the file says so itself, and the readme's "wrong language" row), lasted until the next build. Anything else that writes the player's choice to that file is undone the same way; the "Game language" row of #1864 would be.

The change

With no --language, a build now takes, in this order:

  1. the language the folder has: the build before, or the player's own change to language.txt;
  2. with --prepare and no folder yet: the game language closest to the system's (on Linux LANGUAGE's first entry unless the locale is C, else LC_ALL, LC_MESSAGES, LANG, as gettext reads them; on Windows the display language, the first of GetUserPreferredUILanguages, as the builder's CurrentUICulture), by the rule the Windows builder's window uses for its first choice (the exact one, then the family; es-MX is Spanish (Latin America), zh-TW Chinese (Traditional));
  3. en-US, as before.

A --language given is still the one built, so both builder windows, which always pass theirs, are unchanged. --update keeps the folder's as before. A plain command line build without --language is still English into a new folder; into a folder built before it now keeps that folder's language rather than turning it English.

Tested

  • Linux: python -I -B tests/test_make_game_prepare.py: 17 pass, 8 of them new. One runs main itself up to its first step with --prepare over a folder whose language.txt says ru: on main it fails with 'en-US' != 'ru', here it is ru. The others cover the order above, the system names (de_DE.UTF-8, pt_PT, es_MX, zh_TW, zh-Hant-HK, C, ...) and which of the three variables is read.
  • Linux: python -I -B tests/run_all.py: nothing new fails.
  • Windows 11 (Python 3.13.7): python -I -B tests\test_make_game_prepare.py, 16 pass and the Linux environment one skips. On that PC the display language is en-US and the regional format en-CA; system_language() reads en-US, where GetUserDefaultLocaleName (the rule before review) read en-CA.

Not tested: a whole build through RecompLauncher (no launcher here), and a Windows locale the game has a language for.

Part of #2184

A game built through RecompLauncher is always in English, and a language the player set in `language.txt` goes back to English the next time the launcher builds the folder (#2184). ## Why The launcher builds with `--cli --prepare --game <copy>` (`recomplauncher.json`): no window, and no `--language`. `make_game.py` took `en-US` for that and wrote it to `language.txt` at the end of every build. Only `--update` read the folder's own language first. So the one way a launcher's player has to change the language, the code in `language.txt` (the file says so itself, and the readme's "wrong language" row), lasted until the next build. Anything else that writes the player's choice to that file is undone the same way; the "Game language" row of #1864 would be. ## The change With no `--language`, a build now takes, in this order: 1. the language the folder has: the build before, or the player's own change to `language.txt`; 2. with `--prepare` and no folder yet: the game language closest to the system's (on Linux `LANGUAGE`'s first entry unless the locale is C, else `LC_ALL`, `LC_MESSAGES`, `LANG`, as gettext reads them; on Windows the display language, the first of `GetUserPreferredUILanguages`, as the builder's `CurrentUICulture`), by the rule the Windows builder's window uses for its first choice (the exact one, then the family; `es-MX` is Spanish (Latin America), `zh-TW` Chinese (Traditional)); 3. `en-US`, as before. A `--language` given is still the one built, so both builder windows, which always pass theirs, are unchanged. `--update` keeps the folder's as before. A plain command line build without `--language` is still English into a new folder; into a folder built before it now keeps that folder's language rather than turning it English. ## Tested - Linux: `python -I -B tests/test_make_game_prepare.py`: 17 pass, 8 of them new. One runs `main` itself up to its first step with `--prepare` over a folder whose `language.txt` says `ru`: on `main` it fails with `'en-US' != 'ru'`, here it is `ru`. The others cover the order above, the system names (`de_DE.UTF-8`, `pt_PT`, `es_MX`, `zh_TW`, `zh-Hant-HK`, `C`, ...) and which of the three variables is read. - Linux: `python -I -B tests/run_all.py`: nothing new fails. - Windows 11 (Python 3.13.7): `python -I -B tests\test_make_game_prepare.py`, 16 pass and the Linux environment one skips. On that PC the display language is en-US and the regional format en-CA; `system_language()` reads `en-US`, where `GetUserDefaultLocaleName` (the rule before review) read `en-CA`. Not tested: a whole build through RecompLauncher (no launcher here), and a Windows locale the game has a language for. Part of #2184
Felitendo commented 2026-11-11 19:28:57 +00:00 (Migrated from github.com)

yes that would be awesome if we could get this merged

yes that would be awesome if we could get this merged
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 20:07:39 +00:00
Joey-Magnus (Migrated from github.com) left a comment

Good fix, and the order is right: an explicit --language wins, an update keeps the folder's language, then the folder's own language, then the system's (only with --prepare), then en-US. Moving a.language to after out is resolved means the plain command-line build and the update path now share one rule. Both builder windows always pass --language, and their update paths rely on the folder's language. I checked MainWindow.xaml.cs:752, window.py:1342 and builder/linux/hookshot_builder.py:1043. tests/test_make_game_prepare.py and the other make_game checks pass here, the branch merges cleanly with main, and the appimage failure is a Launchpad timeout, not this PR.

  • should fix nx2/make_game.py:1126 reads GetUserDefaultLocaleName. That returns the user's regional-format locale (Settings → Region → Format), not the display language. The Windows builder picks its first language from CultureInfo.CurrentUICulture (builder/HookshotBuilder/Strings.cs:165), which is the UI language. The two often differ. Someone with English Windows and German or Polish regional formats would get a German or Polish game from the launcher, and the opposite also happens. That breaks the "by the rule the Windows builder's window uses" claim. Reading the UI language instead would match the builder, for example:

    k32 = ctypes.windll.kernel32
    buf = ctypes.create_unicode_buffer(85)
    name = buf.value if k32.LCIDToLocaleName(k32.GetUserDefaultUILanguage(), buf, 85, 0) else ""
    

    (GetUserPreferredUILanguages is the closer match to .NET if you want the first entry of the list.)

  • minor On Linux, gettext puts LANGUAGE ahead of LC_ALL/LC_MESSAGES/LANG for message catalogs. Some desktops, including GNOME's language setting, set it, e.g. LANGUAGE=de:en. If you want to match what the desktop shows, check its first entry before the others (and still ignore it when the locale is C, as gettext does). Fine to leave out if you'd rather stay with the three.

  • minor CONTRIBUTING's build table (line 151) and the readme (lines 512–513) still show --language en-US in every example, which is fine, but nothing documents the new behaviour without --language. One sentence in the readme near line 215 ("The game is in the wrong language") would help: rebuilding now keeps the language set in language.txt.

Good fix, and the order is right: an explicit `--language` wins, an update keeps the folder's language, then the folder's own language, then the system's (only with `--prepare`), then en-US. Moving `a.language` to after `out` is resolved means the plain command-line build and the update path now share one rule. Both builder windows always pass `--language`, and their update paths rely on the folder's language. I checked `MainWindow.xaml.cs:752`, `window.py:1342` and `builder/linux/hookshot_builder.py:1043`. `tests/test_make_game_prepare.py` and the other `make_game` checks pass here, the branch merges cleanly with main, and the `appimage` failure is a Launchpad timeout, not this PR. - **should fix** `nx2/make_game.py:1126` reads `GetUserDefaultLocaleName`. That returns the user's regional-format locale (Settings → Region → Format), not the display language. The Windows builder picks its first language from `CultureInfo.CurrentUICulture` (`builder/HookshotBuilder/Strings.cs:165`), which is the UI language. The two often differ. Someone with English Windows and German or Polish regional formats would get a German or Polish game from the launcher, and the opposite also happens. That breaks the "by the rule the Windows builder's window uses" claim. Reading the UI language instead would match the builder, for example: ```python k32 = ctypes.windll.kernel32 buf = ctypes.create_unicode_buffer(85) name = buf.value if k32.LCIDToLocaleName(k32.GetUserDefaultUILanguage(), buf, 85, 0) else "" ``` (`GetUserPreferredUILanguages` is the closer match to .NET if you want the first entry of the list.) - **minor** On Linux, gettext puts `LANGUAGE` ahead of `LC_ALL`/`LC_MESSAGES`/`LANG` for message catalogs. Some desktops, including GNOME's language setting, set it, e.g. `LANGUAGE=de:en`. If you want to match what the desktop shows, check its first entry before the others (and still ignore it when the locale is `C`, as gettext does). Fine to leave out if you'd rather stay with the three. - **minor** CONTRIBUTING's build table (line 151) and the readme (lines 512–513) still show `--language en-US` in every example, which is fine, but nothing documents the new behaviour without `--language`. One sentence in the readme near line 215 ("The game is in the wrong language") would help: rebuilding now keeps the language set in `language.txt`.
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 21:23:20 +00:00
Joey-Magnus (Migrated from github.com) left a comment

The precedence (--language, the folder's language on update, the system's with --prepare, then en-US) is right, and all 17 tests pass.

The open point is still open: nx2/make_game.py:1126 uses GetUserDefaultLocaleName, which is the regional-format locale. The Windows builder uses CurrentUICulture, so English Windows with German regional formats gets a German game. GetUserDefaultUILanguage (or GetUserPreferredUILanguages) matches the builder.

Also worth a line in the docs: a plain CLI build into an existing folder now keeps that folder's language instead of resetting to English.

The precedence (`--language`, the folder's language on update, the system's with `--prepare`, then en-US) is right, and all 17 tests pass. The open point is still open: `nx2/make_game.py:1126` uses `GetUserDefaultLocaleName`, which is the regional-format locale. The Windows builder uses `CurrentUICulture`, so English Windows with German regional formats gets a German game. `GetUserDefaultUILanguage` (or `GetUserPreferredUILanguages`) matches the builder. Also worth a line in the docs: a plain CLI build into an existing folder now keeps that folder's language instead of resetting to English.
jarynclouatre commented 2026-11-11 21:26:10 +00:00 (Migrated from github.com)

Thanks, all three done in 9071c78:

  • Windows: system_language now reads the display language, the first of GetUserPreferredUILanguages (MUI_LANGUAGE_NAME), else LCIDToLocaleName(GetUserDefaultUILanguage()), so it follows the same setting as the builder's CurrentUICulture. On a Windows 11 PC whose display language is English (United States) and regional format English (Canada), it reads en-US (PowerShell's Get-UICulture says en-US, Get-Culture en-CA, and GetUserDefaultLocaleName returned en-CA).
  • Linux: LANGUAGE's first entry comes first, unless the locale from LC_ALL/LC_MESSAGES/LANG is C or POSIX, as gettext does. The test has five more cases for it.
  • The readme's wrong-language line now says an update keeps the code set in language.txt.

tests/test_make_game_prepare.py passes on Linux (17) and on that Windows PC (16, 1 Linux-only skip).

Thanks, all three done in 9071c78: - Windows: `system_language` now reads the display language, the first of `GetUserPreferredUILanguages` (MUI_LANGUAGE_NAME), else `LCIDToLocaleName(GetUserDefaultUILanguage())`, so it follows the same setting as the builder's `CurrentUICulture`. On a Windows 11 PC whose display language is English (United States) and regional format English (Canada), it reads `en-US` (PowerShell's `Get-UICulture` says en-US, `Get-Culture` en-CA, and `GetUserDefaultLocaleName` returned en-CA). - Linux: `LANGUAGE`'s first entry comes first, unless the locale from `LC_ALL`/`LC_MESSAGES`/`LANG` is C or POSIX, as gettext does. The test has five more cases for it. - The readme's wrong-language line now says an update keeps the code set in `language.txt`. `tests/test_make_game_prepare.py` passes on Linux (17) and on that Windows PC (16, 1 Linux-only skip).
Joey-Magnus (Migrated from github.com) approved these changes 2026-11-11 21:46:33 +00:00
Joey-Magnus (Migrated from github.com) left a comment

9071c78 addresses all three points from my last review.

  • Windows now reads the display language: the first of GetUserPreferredUILanguages (MUI_LANGUAGE_NAME), else LCIDToLocaleName(GetUserDefaultUILanguage()) (windows_ui_language in nx2/make_game.py). That's the setting behind the builder's CurrentUICulture, and your check with an en-US display language and en-CA regional format was the case that mattered.
  • On Linux, LANGUAGE's first entry now comes ahead of LC_ALL/LC_MESSAGES/LANG. It's ignored when the locale is C, POSIX or unset, as gettext does.
  • The readme's "wrong language" row now says an update keeps the language set in language.txt.

tests/test_make_game_prepare.py passes here (17 tests), the branch merges cleanly with current main, and CI is green on the new head. Nothing is left open from my side.

9071c78 addresses all three points from my last review. - Windows now reads the display language: the first of `GetUserPreferredUILanguages` (MUI_LANGUAGE_NAME), else `LCIDToLocaleName(GetUserDefaultUILanguage())` (`windows_ui_language` in `nx2/make_game.py`). That's the setting behind the builder's `CurrentUICulture`, and your check with an en-US display language and en-CA regional format was the case that mattered. - On Linux, `LANGUAGE`'s first entry now comes ahead of `LC_ALL`/`LC_MESSAGES`/`LANG`. It's ignored when the locale is C, POSIX or unset, as gettext does. - The readme's "wrong language" row now says an update keeps the language set in `language.txt`. `tests/test_make_game_prepare.py` passes here (17 tests), the branch merges cleanly with current main, and CI is green on the new head. Nothing is left open from my side.
Commenting is not possible because the repository is archived.
No description provided.