Locale codes and language selection
Which codes to use, known locales versus shipped translations, and RU/EN selection.
A locale identifies a language and, when needed, a script or region. Recognizing a code does not mean the game contains a translation for it. Keep the engine's locale catalog separate from the list of languages you actually ship.
Choose a code for your catalog
| Code | Meaning | Use |
|---|---|---|
| en | English | shared English catalog |
| ru | Russian | shared Russian catalog |
| en_US | English, United States | region-specific wording |
| en_GB | English, United Kingdom | region-specific wording |
| zh_Hans | Chinese, simplified script | script-specific translation |
| zh_Hant_TW | Chinese, traditional script, Taiwan | script and region-specific translation |
| sr_Latn | Serbian, Latin script | distinguish from Cyrillic Serbian |
The canonical pattern is language_Script_COUNTRY_VARIANT. Language is normally lower-case, the optional script is title-case and the optional country is upper-case. Use recognized codes and only the distinctions your translations actually implement. Region alone does not select currency, measurement units or a server region in your game.
Use ru/en as CSV headers, the Translation.Locale value, or the Language header of a PO catalog. A filename like ru.po does not override its PO header.
Normalize and compare
TranslationServer.StandardizeLocale("ru-RU") returns ru_RU; zh-Hant-TW becomes zh_Hant_TW. The known script alias sr@latin becomes sr_Latn. Supply canonical casing: normalization is not a validator for arbitrary player input or a guarantee that every malformed code will be repaired.
The optional addDefaults argument affects some ambiguous locales. For example, StandardizeLocale("zh") returns zh, while StandardizeLocale("zh", true) returns zh_Hans_CN in this runtime. Do not add defaults blindly when the player explicitly chose a script or regional catalog.
CompareLocales returns 10 for an exact/equivalent match, a positive score for a matching language, and 0 for different languages. A positive result is a similarity score, not proof that catalogs, font glyphs or translated messages exist. Two scripts of the same language can still need separate content.
Select a language the game ships
This complete GameLanguage.cs deliberately supports only ru and en. It maps a regional request to the shipped language catalog and uses en for everything else. It is not a universal locale matcher:
using Reckfell;
public static class GameLanguage
{
// This game ships only ru and en catalogs.
public static string Resolve(string requested)
{
if (string.IsNullOrWhiteSpace(requested)) return "en";
string normalized = TranslationServer.StandardizeLocale(requested);
string language = normalized.Split('_')[0];
return language == "ru" ? "ru" : "en";
}
public static void Apply(string requested)
{
TranslationServer.SetLocale(Resolve(requested));
}
}Register both catalogs first. Inside your startup method, use GameLanguage.Apply(savedLocale) when a player choice exists; otherwise use GameLanguage.Apply(OS.GetLocale()). Keep a manual language selector. Resolve returns ru for ru-RU and en for en-GB, fr-FR or an empty request. Save the chosen value separately: Apply does not persist it.
When adding script-specific or regional languages, replace this two-language policy with an explicit supported-locale list. Preserve the requested script; do not reduce zh_Hant and zh_Hans to a shared zh just because this RU/EN example reduces regions.
Get the full current code catalogs
Use these SDK methods instead of copying a static table that can drift from the installed runtime:
| Identifiers | Readable name |
|---|---|
| TranslationServer.GetAllLanguages() | GetLanguageName(code) |
| TranslationServer.GetAllScripts() | GetScriptName(code) |
| TranslationServer.GetAllCountries() | GetCountryName(code) |
These catalogs describe known codes. TranslationServer.GetLoadedLocales() instead lists the locales of resources registered in the main translation domain. An empty result is possible even though GetAllLanguages contains ru and en. The loaded-locale list still does not certify that every message has a translation.
For the game's language menu, use a curated list of shipped languages with labels such as Русский and English. Do not offer every known engine language as if you had translated it.
Diagnose language selection
| Symptom | Check |
|---|---|
| Code is recognized but the game shows keys | Register the catalog; verify its messages and locale |
| en_GB request shows shared English | Only an en catalog is shipped, or matching falls back to it |
| A preferred script was lost | Your selection code reduced the locale to just the language |
| Game returns to the OS language after restart | Player preference was not saved or was applied before being loaded |
| Correct text but wrong glyphs, direction or formatting | Font/shaping/layout/number-format settings; locale matching alone does not validate these |
See translation registration and fallback and CSV contexts/plurals.
Native checks cover the normalization examples, comparison categories, code catalog queries, their distinction from registered locales and the exact two-language resolver. OS-specific locale acquisition, preference persistence, every possible locale combination, rendered typography and platform exports are not covered.