Skip to content

feat: Language, the player's language as a BCP 47 tag - #18

Merged
LucasAntunesdeAlmeida merged 1 commit into
masterfrom
language
Sep 29, 2026
Merged

LucasAntunesdeAlmeida merged 1 commit into
masterfrom
language

Conversation

@LucasAntunesdeAlmeida

Copy link
Copy Markdown
Owner

What

g.Language() string: the player's language as a BCP 47 tag ("pt-BR", "zh-CN", "en-US", "de"), or "" when unknown. Games call it while building scenes to pick a translation.

Platform Source
Windows GetUserPreferredUILanguages(MUI_LANGUAGE_NAME) (the display language) via golang.org/x/sys/windows, no cgo; fallback GetUserDefaultLocaleName
macOS first entry of defaults read -g AppleLanguages (Finder-launched apps get no LANG), then the environment
Linux / other Unix first set of LC_ALL, LC_MESSAGES, LANG; pt_BR.UTF-8@euro becomes pt-BR, C/POSIX are ""; GNU LANGUAGE wins over a real locale, as for gettext
Browser navigator.language

Every answer is normalized the same way (_ to -, language lower case, script title case, region upper case). The system is asked once per process.

Design choices

  • A Game method, not a package function. "Agent or test run" is already one definition in the engine (g.agentOrTestRun(), which gates saves and audio), and it lives on the Game (Headless, Autopilot, DisallowAgents). New is the first line of every game, so the method is available before any scene exists.
  • Agent and test runs get "". That covers Headless and Autopilot once called, COLLIDER_AGENT=mcp, and go test. The game falls back to its default language, and agents and CI read the same strings on every machine. mcp-window is a person's window, so it gets the real language, the same way it gets real audio and saves. Contract item 10.
  • COLLIDER_LANG overrides it everywhere, agent runs included (COLLIDER_LANG=zh-CN go run ., or an MCP agent testing the Chinese UI). C forces "". It stays active after DisallowAgents: that lock stops outside control and file writes, while this only picks among the game's own strings, which a player can already do with LANG or the OS setting. It also lets testers check a translation on a release build.
  • The name Language matches what games ask for ("which language?"). It returns a tag rather than a parsed struct so the API stays one string. The doc comment explains how to match it leniently (primary subtag, and region or script where it matters, for example zh-CN vs zh-TW).

Proof

  • language_test.go: normalization table, the Unix env rules (priority, C/POSIX, LANGUAGE), AppleLanguages parsing, the agent/test/mcp/mcp-window/DisallowAgents matrix, the override, and the real platform call on the test machine (en-US here). language_windows_test.go covers the locale fallback.
  • examples/dialog speaks English or Portuguese (COLLIDER_LANG=pt-BR go run .). Its typewriter now slices runes, not bytes, so accented letters never split.
  • CI also vets and builds for Windows (language_windows.go), cross-compiled like the wasm step.
  • README cheat-sheet row, contract item 10, AGENTS.md note, dialog README.

g.Language() string answers "pt-BR", "zh-CN", "en-US" (or "" when
unknown) so a game can pick its translation while building scenes.

- Windows: the user's display language (GetUserPreferredUILanguages via
  golang.org/x/sys/windows, no cgo), falling back to
  GetUserDefaultLocaleName.
- macOS: the first of AppleLanguages (defaults read -g), then the
  environment.
- Linux and other Unix: LC_ALL, LC_MESSAGES, LANG, with GNU's LANGUAGE
  list first as gettext does; encoding and modifier stripped, C and
  POSIX mean unknown.
- Browser: navigator.language.

Agent and test runs (the agentOrTestRun set: Headless, Autopilot,
COLLIDER_AGENT=mcp, go test) get "", so games fall back to their default
language and agents see the same text everywhere. COLLIDER_LANG
overrides the answer in every run, to try or test a translation; like
LANG it only picks among the game's own strings, so DisallowAgents
keeps it. Contract item 10.

The dialog example speaks English or Portuguese through it (and its
typewriter now counts runes, not bytes). CI also vets and builds for
Windows, which now has a platform file of its own.
@LucasAntunesdeAlmeida
LucasAntunesdeAlmeida merged commit bfb828d into master Sep 29, 2026
1 check passed
@LucasAntunesdeAlmeida
LucasAntunesdeAlmeida deleted the language branch September 29, 2026 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant