Comment traduire des scripts grâce à l'IA (Guide FiveM)
Public: Propriétaires de serveurs FiveM, scripteurs et mainteneurs qui souhaitent des traductions de haute qualité sans casser les espaces réservés ou l'interface utilisateur.
TL;DR
- Centralisez tout le texte dans des fichiers de paramètres régionaux (tables JSON ou Lua). Ne codez jamais en dur les chaînes dans la logique du jeu.
- Protéger les espaces réservés (par exemple,
%s,%d,%{nom},{0},~r~,^1) pendant la traduction. - Utilisez l’IA pour la traduction de premier passage + glossaire + vérifications automatisées → révision humaine rapide → expédition.
- Conservez une source unique de vérité (généralement l'anglais), comparez les modifications et régénérez uniquement les clés modifiées.
Pourquoi traduire vos scripts FiveM
- Accessibilité et croissance : Les serveurs localisés attirent plus de joueurs et les maintiennent engagés.
- Professionnalisme: Terminologie cohérente entre les commandes, les interfaces utilisateur et les messages d'erreur.
- Convivial pour les contributeurs : Une structure locale claire invite les relations publiques communautaires.
Si vous avez besoin d'un rappel fondamental sur la structure et les meilleures pratiques, consultez : Comment traduire les scripts FiveM (de la bonne manière).
Architecture : la bonne façon de localiser
But: Aucune chaîne visible par l'utilisateur dans le code de jeu. Acheminez tout via une couche locale.
Disposition des ressources recommandée
my_resource/ ├─ fxmanifest.lua ├─ locales/ │ ├─ en.json # langue source (source unique de vérité) │ ├─ de.json # traduit (généré/édité) │ ├─ es.json # traduit (généré/édité) │ └─ qa.rules.json # facultatif : liste blanche d'espaces réservés et vérifications ├─ client/ │ └─ main.lua ├─ server/ │ └─ main.lua └─ shared/ └─ i18n.lua # aide à la traduction
fxmanifest.lua (exemple minimal)
fx_version 'cerulean' jeu 'gta5' lua54 'oui' shared_scripts { 'shared/i18n.lua', } fichiers { 'locales/*.json' }
partagé/i18n.lua (chargeur léger + substitution d'espace réservé)
local LOCALE = GetConvar('my_locale', 'en') local CACHE = {} local function loadJSON(path) local file = io.open(path, 'r') if not file then return {} end local content = file:read('*a') file:close() local ok, data = pcall(function() return json.decode(content) end) return ok and data or {} end local function readLocale(lang) if CACHE[lang] then return CACHE[lang] end local file = ('locales/%s.json'):format(lang) local dict = loadJSON(file) CACHE[lang] = dict return dict end local function interpolate(str, vars) if not vars then return str end for k, v in pairs(vars) do str = str:gsub('%%{'..k..'}', tostring(v)) -- %{name} fin de retour str fin de fonction _U(key, vars) local dict = readLocale(LOCALE) local src = dict[key] si pas src alors -- retour à l'anglais si manquant src = readLocale('en')[key] ou key fin de retour interpolate(src, vars) fin exports('Translate', _U)
Utilisation dans le code client/serveur
-- Client lib.notify({ title = _U('notify_title'), description = _U('welcome_player', { name = GetPlayerName(PlayerId()) }), }) -- Serveur print(('[MyRes] %s'):format(_U('server_started')))
locales/en.json (source)
{ "notify_title": "Message du serveur", "welcome_player": "Bienvenue, %{name}!", "server_started": "Le module serveur est prêt.", "no_permission": "Vous n'avez pas l'autorisation.", "items_remaining": "%{count} éléments restants" }
Flux de travail de traduction IA (rapide et sûr)
- Extraire et congeler la source
- Garder Anglais (ou votre source) comme
locales/en.json. - Appliquer la dénomination des clés :
domaine.action.sujet(par exemple,inventaire.drop.confirm).
- Créer/étendre un glossaire
- Carte CSV ou JSON des termes canoniques → termes cibles. Exemple :
source, cible EMS, Rettungsdienst PD, Polizei Mechanic, Mechaniker
- Protéger les espaces réservés et le balisage
- Espaces réservés :
%{nom},%s,%d,{0} - Codes couleur FiveM :
~r~,~g~,~s~; codes de discussion :^1,^2 - Balises NUI/HTML :
<b>,<span>…
- Traduire via API (lot)
- Envoyer valeurs seulement, garder clés inchangé.
- Fournir glossaire et style (ton) au modèle/moteur.
- Contrôle qualité automatisé
- Valider JSON.
- Vérifier parité de l'espace réservé (chaque espace réservé dans la source existe dans la cible).
- Indiquez les modifications interdites (par exemple, les codes de couleur modifiés ou la ponctuation ajoutée lorsque cela n'est pas autorisé).
- Contrôles ponctuels humains (5 à 10 minutes)
- Passez en revue les commandes, les messages d’erreur et les longues chaînes d’interface utilisateur.
- Expédier et itérer
- Gardez un mémoire de traduction (sorties précédentes) pour éviter de retraduire des clés inchangées.
Garde-fous : des règles rapides et efficaces
Invite LLM pour la traduction par lots JSON
Tâche : traduire les valeurs JSON de l'anglais vers Pour un contexte de jeu de rôle FiveM/GTA. Règles : - CONSERVER LES CLÉS INCHANGÉES. - CONSERVER tous les espaces réservés : %{var}, %s, %d, {0}, ~r~, ~g~, ^1, ^2, etc. - Conserver les majuscules et les jetons de style de code (commandes, commandes /slash) inchangés. - Ne pas ajouter de guillemets, de ponctuation supplémentaire ni modifier le sens. - Renvoyer UNIQUEMENT du JSON valide avec la même structure. JSON à traduire :
Regex que vous pouvez utiliser dans un script d'assurance qualité
- Espaces réservés :
%%\{[A-Za-z0-9_]+\} - C printf :
%(?:\d+\$)?[sdif] - Codes de discussion :
\^\d - Codes de couleur Tilde :
~[rgbso]~
Exemple : traduire avec DeepL (Node.js)
Idéal pour les travaux ponctuels ou les CI.
package.json (scripts)
{ "type": "module", "scripts": { "i18n:translate:de": "node tools/translate-deepl.js en de", "i18n:check": "node tools/i18n-check.js" } }
outils/translate-deepl.js
import fs depuis 'fs'; import path depuis 'path'; import assert depuis 'assert'; import fetch depuis 'node-fetch'; const [,, srcLang, dstLang] = process.argv; const apiKey = process.env.DEEPL_API_KEY; // définir dans CI/ENV assert(apiKey, 'DEEPL_API_KEY est requis'); const src = JSON.parse(fs.readFileSync('locales/en.json', 'utf8')); const out = {}; const GLOSSAIRE = { 'EMS': 'Service de secours', 'PD': 'Polizei', }; function protect(str){ // Remplacer les espaces réservés par des jetons que DeepL ne modifiera pas return str .replace(/%\{([^}]+)\}/g, '⟦$1⟧') .replace(/%s/g, '⟪S⟫') .replace(/%d/g, '⟪D⟫'); } function restore(str){ return str .replace(/⟦([^⟧]+)⟧/g, '%{$1}') .replace(/⟪S⟫/g, '%s') .replace(/⟪D⟫/g, '%d'); } async function translate(text){ const res = await fetch('https://api.deepl.com/v2/translate', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ auth_key: apiKey, text: text, source_lang: srcLang.toUpperCase(), target_lang: dstLang.toUpperCase(), formality: 'prefer_more' }) }); const json = await res.json(); if (!json.translations) throw new Error(JSON.stringify(json)); return json.translations[0].text; } for (const [k, v] of Object.entries(src)) { const protectedText = protect(v); // Pré-passage du glossaire (simple) : let glossed = protectedText; for (const [from, to] of Object.entries(GLOSSARY)) { glossed = glossed.replace(new RegExp(`\\b${from}\\b`, 'g'), to); } // Traduire // eslint-disable-next-line no-await-in-loop const tr = await translate(glossed); out[k] = restore(tr); } fs.writeFileSync(`locales/${dstLang}.json`, JSON.stringify(out, null, 2)); console.log(`Wrote locales/${dstLang}.json`);
outils/i18n-check.js (parité d'espace réservé)
importer fs depuis 'fs'; const src = JSON.parse(fs.readFileSync('locales/en.json', 'utf8')); const dst = JSON.parse(fs.readFileSync('locales/de.json', 'utf8')); const reVar = /%\{[^}]+\}/g; const reS = /%s/g; const reD = /%d/g; let ok = true; pour (const k de Object.keys(src)) { const a = (src[k].match(reVar)||[]).length === (dst[k]?.match(reVar)||[]).length; const b = (src[k].match(reS)||[]).length === (dst[k]?.match(reS)||[]).length; const c = (src[k].match(reD)||[]).length === (dst[k]?.match(reD)||[]).length; if (!(a && b && c)) { console.error('Incompatibilité d'espace réservé pour la clé :', k); ok = false; } } process.exit(ok ? 0 : 1);
Utiliser efficacement les LLM (OpenAI/autres)
- Morceau par sujet/domaine pour un meilleur contexte (par exemple, inventaire, police, emplois).
- Fournir brèves descriptions par groupe (deux lignes) pour définir le ton et le public.
- Quelques exemples: 2 à 3 paires correctement traduites avec des espaces réservés améliorent la cohérence.
- Politique de nouvelle tentative: réexécuter uniquement les clés ayant échoué signalées par
vérification i18n.
Modèle à quelques clichés (système + utilisateur)
Système : vous traduisez les chaînes de l'interface utilisateur du jeu FiveM pour . - Conserver les clés inchangées, préserver les espaces réservés et maintenir un ton concis. Exemples d'utilisateurs : EN : « Vous avez %{count} amendes. » DE : « Du hast %{count} Strafzettel. » EN : « ~r~Erreur :~s~ Vous n'avez pas l'autorisation. » DE : « ~r~Fehler:~s~ Dir fehlt die Berechtigung. » Traduisez maintenant les valeurs JSON suivantes de l'anglais vers . Renvoyer uniquement du JSON valide :
Traductions NUI (HTML/JS)
Pour les interfaces utilisateur des navigateurs, une bibliothèque côté client est pratique.
Approche recommandée
- Utilisez un bundle JSON par langue dans
web/locales/ .json. - Chargez votre framework d'interface utilisateur et exposez un
t(clé, vars)auxiliaire. - Gardez le mêmes clés comme paramètres régionaux du serveur pour réduire la charge cognitive.
Assistant JS minimal
const dict = await (await fetch(`/locales/${lang}.json`)).json(); export function t(key, vars){ let s = dict[key] || key; for (const [k,v] of Object.entries(vars||{})) s = s.replace(`%{${k}}`, v); return s; }
Spécificités ESX/QBCore
- De nombreux scripts ESX sont livrés
locales/en.lua,locales/de.luaavec un_Uauxiliaire. - Si vous utilisez des tables Lua pour les paramètres régionaux, conservez un style dans votre dépôt. Mélanger JSON et Lua pour la même ressource augmente les coûts de maintenance.
- QBCore utilise souvent des messages pilotés par la configuration. Migrez les chaînes répétées vers des fichiers de paramètres régionaux pour éviter toute divergence.
Paramètres régionaux de la table Lua (si vous préférez Lua à JSON)
Locales = Locales ou {} Locales['fr'] = { no_permission = 'Vous n'avez pas l'autorisation.', Welcome_player = 'Bienvenue, %{name} !' } Locales['de'] = { no_permission = 'Vous avez votre Berechtigung.', Welcome_player = 'Willkommen, %{name}!' }
Contrôle de qualité avant expédition
- Vérification de l'analyse JSON/Lua en CI.
- Parité de l'espace réservé (vérifications regex comme indiqué).
- Modifications interdites: interdire les modifications à
/commandes, lettres de raccourcis clavier, codes couleur/chat. - Deltas de longueur: drapeau +40% croissance pour les boutons de l'interface utilisateur ; peut interrompre la mise en page.
- Test de fumée: démarrez votre serveur et vérifiez ponctuellement les flux critiques.
Vous débutez dans la configuration d'un serveur de test ? Suivez ce guide : Comment créer un serveur FiveM.
Stratégie de maintenance
- Traiter
fr.jsoncomme source de vérité; créer un travail CI qui diffèrefr.jsonet les mises à jour ne modifient que les clés dans les cibles. - Gardez un
CHANGELOG.i18n.mdpour les traducteurs. - Encouragez la communauté à contribuer via les relations publiques ; documentez votre guide de style et glossaire dans
/docs/i18n.md.
Pièges courants (et solutions)
- Espaces réservés brisés → Utilisez des contrôles automatisés et des jetons de protection.
- Terminologie incohérente → Maintenir un glossaire et l’appliquer dans les invites et le prétraitement.
- Paramètres régionaux mixtes dans le code → Échec du CI si des chaînes sont détectées à l'extérieur
locales/. - Langues RTL → Assurez-vous que vos ensembles CSS NUI
direction : rtl ;et utilise des polices avec prise en charge RTL. - Dérive de casse et de ponctuation → Instruisez explicitement l’IA et exécutez un linter pour normaliser la ponctuation.
Ressources externes
- API DeepL — documentation du développeur : https://www.deepl.com/docs-api
- Traduction Google Cloud — docs et bonnes pratiques : https://cloud.google.com/translate/docs
- Manifeste de ressources FiveM (fxmanifest.lua) - référence: https://docs.fivem.net/docs/scripting-reference/resource-manifest/resource-manifest/
Ressources internes (lectures connexes)
- Comment traduire les scripts FiveM (de la bonne manière) — flux de travail et modèles : https://fivemx.com/fivem-scripts-translation/
- Comment créer un serveur FiveM — mettre en place un banc d’essai pour l’assurance qualité : https://fivemx.com/how-to-create-a-fivem-server/
Listes de contrôle copier-coller
Pré-traduction
- Toutes les chaînes centralisées dans
locales/en.json(ou table Lua) - Les clés suivent une convention de dénomination
- Glossaire préparé
- Espaces réservés audités
Courir
- Traduction par lots avec glossaire
- Enregistrer la sortie dans
locales/ .json
assurance qualité
- JSON/Lua valide
- Parité de l'espace réservé OK
- Jetons interdits inchangés
- Deltas de longueur acceptables
- Contrôle humain ponctuel effectué
Bateau
- CI vert
- Journal des modifications mis à jour
- Inviter la communauté à donner son avis






