Internationalisation (i18n)¶
Chaque chaîne présentée à l'utilisateur dans CTLD est traduisible. Le code source n'émet jamais de
texte brut vers les joueurs — il appelle ctld.tr("English text"), et la couche i18n résout cet
appel vers la langue active à l'exécution. CTLD fournit quatre dictionnaires : anglais
(référence), français, espagnol et coréen.
Le sous-système i18n est délibérément scindé entre logique et données. src/CTLD_i18n.lua contient
le singleton CTLDi18n, la fonction ctld.tr() et les fonctions utilitaires d'audit — aucune
chaîne. Chaque langue vit dans son propre fichier dictionnaire CTLD_i18n_XX.lua, maintenu
indépendamment (souvent par différents traducteurs). Un outil PowerShell maintient ces dictionnaires
synchronisés avec les clés réellement utilisées dans le source.
Les quatre dictionnaires¶
| Fichier | Langue | Rôle |
|---|---|---|
CTLD_i18n.lua |
— | Logique de classe uniquement : singleton CTLDi18n, ctld.tr(), utilitaires d'audit. Aucune donnée de dictionnaire. |
CTLD_i18n_en.lua |
Anglais | Dictionnaire de référence. Chaque valeur est égale à sa clé. Jamais vide. |
CTLD_i18n_fr.lua |
Français | Valeur vide "" = non traduit (repli sur l'EN). |
CTLD_i18n_es.lua |
Espagnol | Même règle. |
CTLD_i18n_ko.lua |
Coréen | Même règle. |
Les cinq fichiers sont listés — dans cet ordre — dans tools/build/listToMerge.txt, de sorte que
CTLD_i18n.lua se charge avant tout dictionnaire et que le dictionnaire de référence en se charge
en premier.
La langue active est sélectionnée en haut de src/CTLD_i18n.lua :
ctld.i18n_lang = "en"
--ctld.i18n_lang = "fr"
--ctld.i18n_lang = "es"
--ctld.i18n_lang = "ko"
Le contrat de ctld.tr()¶
ctld.tr() traduit une clé vers la langue active et substitue les éventuels paramètres
positionnels.
ctld.tr("Troops loaded")
ctld.tr("Sorry you must wait %1 seconds before you can get another crate", waitTime)
ctld.tr("%1 loaded %2 vehicles into %3", pilotName, count, zoneName)
Règle : la clé passée à ctld.tr() est TOUJOURS un littéral de chaîne complet. Les parties
variables sont exprimées avec des marqueurs %1, %2, … à l'intérieur du littéral ; les valeurs
dynamiques sont passées en arguments séparés. Ne jamais construire une clé par concaténation :
-- FORBIDDEN — breaks the static scan, keys go silently missing from dicts
ctld.tr("prefix_" .. suffix)
ctld.tr(someVariable)
Cela a son importance parce que generate_i18n_dicts.ps1 extrait les clés avec une expression
régulière. Si chaque clé est un littéral, le scan est fiable à 100 % ; la concaténation crée des
angles morts.
ctld.i18n_translate est un alias de compatibilité ascendante pour ctld.tr — le nouveau code
utilise ctld.tr.
Chaîne de repli¶
ctld.tr() ne retourne jamais nil ni une chaîne vide. Elle résout dans cet ordre :
- Le dictionnaire de la langue active (
ctld.i18n[ctld.i18n_lang]). - Le dictionnaire anglais.
- La clé elle-même (qui est le texte anglais).
Une langue active inconnue journalise un WARN et se replie sur en.
Versionnage des dictionnaires¶
Chaque dictionnaire porte sa propre translation_version, une chaîne "MAJOR.MINOR" :
ctld.i18n["en"].translation_version = "1.8"
Les versions sont indépendantes par dictionnaire — les dictionnaires sont maintenus séparément,
donc chacun n'est incrémenté que lorsque lui-même change, pas lorsqu'un dictionnaire frère change.
ctld.i18n_check() et ctld.i18n_audit() signalent un écart entre l'EN et une autre langue comme
un indice que l'autre dictionnaire est peut-être en retard sur l'EN et nécessite une revue. Un écart
est un avertissement, pas une erreur : la chaîne de repli résout toujours chaque clé.
Maintenir les dictionnaires synchronisés — generate_i18n_dicts.ps1¶
tools/build/generate_i18n_dicts.ps1 scanne chaque fichier src/*.lua (à l'exclusion des
dictionnaires eux-mêmes) à la recherche des clés ctld.tr(), puis réconcilie chaque dictionnaire
avec cet ensemble. Exécutez-le après tout ajout ou suppression de ctld.tr() dans le source.
# Audit only — reports what would change, writes nothing (default)
powershell -ExecutionPolicy Bypass -File tools\build\generate_i18n_dicts.ps1
# Apply — add missing keys, mark stale ones, bump versions
powershell -ExecutionPolicy Bypass -File tools\build\generate_i18n_dicts.ps1 -Apply
Par dictionnaire, -Apply effectue :
| Situation | Action |
|---|---|
| Clé utilisée dans le source, absente du dictionnaire | Ajoutée en fin de fichier. L'EN reçoit value = key ; les autres langues reçoivent value = "" (non traduit). |
| Clé dans le dictionnaire, plus utilisée dans le source | Sa ligne est préfixée par -- STALE: — jamais supprimée, afin qu'un humain confirme avant de l'enlever. |
| Toute modification apportée au dictionnaire | La translation_version propre à ce dictionnaire est incrémentée (composante patch +1). |
L'outil réécrit uniquement les fichiers dictionnaires — il ne régénère aucun loader et ne touche
pas à listToMerge.txt. L'ordre de fusion qui assemble CTLD.lua est entièrement piloté par
listToMerge.txt (voir Architecture).
Ajouter une clé de traduction¶
- Appelez
ctld.tr("My new text")dans le module source (clé littérale, marqueurs%1… pour les variables). - Exécutez
generate_i18n_dicts.ps1 -Apply. La nouvelle clé est ajoutée à l'ensemble des quatre dictionnaires — avec le texte anglais comme valeur EN et""dans les autres — et la version de chaque dictionnaire modifié est incrémentée. - Les traducteurs remplissent les valeurs vides dans
CTLD_i18n_fr/es/ko.lua. - Reconstruisez
CTLD.lua(tools\build\merge_CTLD.ps1) et ajoutez une couverture busted pour la nouvelle logique.
Ajouter une langue¶
Pour ajouter une cinquième langue XX :
- Copiez
src/CTLD_i18n_en.luaverssrc/CTLD_i18n_XX.lua, remplacez chaquectld.i18n["en"]parctld.i18n["XX"], et traduisez les valeurs (gardez les clés identiques à l'EN). - Enregistrez le fichier dans
tools/build/listToMerge.txt, après les dictionnaires existants. - Enregistrez la langue dans la table ordonnée
$DictFilesdetools/build/generate_i18n_dicts.ps1afin que l'outil de synchronisation la suive :
$DictFiles = [ordered]@{
"en" = (Join-Path $DictDir "CTLD_i18n_en.lua")
"fr" = (Join-Path $DictDir "CTLD_i18n_fr.lua")
"es" = (Join-Path $DictDir "CTLD_i18n_es.lua")
"ko" = (Join-Path $DictDir "CTLD_i18n_ko.lua")
"XX" = (Join-Path $DictDir "CTLD_i18n_XX.lua")
}
- Ajoutez la langue comme option dans
src/CTLD_i18n.lua(--ctld.i18n_lang = "XX"), et sélectionnez-la à cet endroit lorsque vous voulez l'activer. - Chargez le dictionnaire dans les tests qui exercent toutes les langues —
tests/ci/unit/i18n_spec.luaeffectue undofileexplicite des dictionnaires non-EN (letests/ci/helpers/loader.luapartagé ne charge queCTLD_i18n_en.lua). - Exécutez
generate_i18n_dicts.ps1pour confirmer que le nouveau dictionnaire est complet, puis reconstruisez.
Surcharges pour les créateurs de mission (ctld.i18n_overrides)¶
Les créateurs de mission peuvent surcharger n'importe quelle chaîne sans toucher à un fichier
dictionnaire, depuis CTLD_userConfig.lua (la config côté .miz — ctld.i18n_overrides n'est
jamais défini à l'intérieur de src/) :
-- In CTLD_userConfig.lua, not in src/
ctld.i18n_overrides = {
fr = { ["Troops loaded"] = "Soldats embarqués" },
en = { ["CTLD Commands"] = "Helicopter Commands" },
}
Les surcharges sont appliquées une seule fois au démarrage, en mémoire, par CTLDi18n:_init() —
le premier appel à CTLDi18n.getInstance() parcourt ctld.i18n_overrides et écrase les entrées de
dictionnaire correspondantes. La séquence à l'exécution :
1. CTLD.lua loads → dictionaries populated in ctld.i18n[lang]
2. CTLD_userConfig.lua → sets ctld.i18n_overrides
3. getInstance() → _init() copies overrides into the in-memory dicts
4. ctld.tr(key) → returns the overridden value
Il s'agit d'un mécanisme d'exécution (runtime), orthogonal à generate_i18n_dicts.ps1, qui est
un outil de construction (build-time) opérant sur les fichiers source des dictionnaires. Les
surcharges ne touchent jamais à src/.
API d'audit pour traducteurs¶
Trois fonctions détectent les écarts entre l'EN et une langue cible. La paire audit retourne des
données structurées (adaptées aux assertions busted et aux déclencheurs DO SCRIPT) ; i18n_check
est la variante héritée qui journalise directement vers env.*.
ctld.i18n_audit(language)¶
Compare une langue à l'EN et retourne une table de résultat (plus une chaîne d'erreur lorsque la langue est inconnue) :
local result, err = ctld.i18n_audit("fr")
if err then
-- language not loaded
else
if not result.version_match then
-- EN was bumped; FR may need review
end
-- result.en_version : EN's translation_version
-- result.lang_version : target's translation_version
-- result.missing : keys present in EN but absent in FR
-- result.untranslated : keys where the FR value equals the EN value
end
ctld.i18n_auditAll()¶
Exécute ctld.i18n_audit() sur chaque langue non-EN chargée et retourne une table indexée par code
de langue :
local results = ctld.i18n_auditAll() -- results["fr"], results["es"], results["ko"]
for lang, r in pairs(results) do
print(lang, "#missing=" .. #r.missing, "#untranslated=" .. #r.untranslated)
end
ctld.i18n_check(language, verbose) (hérité — DCS uniquement)¶
Journalise les clés manquantes en tant que env.error et les clés non traduites en tant que
env.warning, en écrivant directement dans le log DCS. Comme elle ne retourne pas de données, elle
ne convient pas aux assertions — utilisez ctld.i18n_audit() dans les tests et les scripts. Un
rapport d'écarts par langue prêt à l'emploi, que vous pouvez coller dans un déclencheur DO SCRIPT,
se trouve dans le bloc de commentaire au bas de src/CTLD_i18n.lua.
Voir Building & testing pour la façon dont les specs i18n s'exécutent
sous busted, et Architecture pour l'ordre de fusion qui assemble les
dictionnaires dans CTLD.lua.