FiveM-Skripte mit KI übersetzen (Anleitung)
Schritt-für-Schritt-Anleitung zur KI-gestützten Übersetzung von FiveM-Skripten. Locale-Struktur, Platzhalter-Schutz, DeepL, LLM-Workflows und QA-Checklisten.

Zielgruppe: FiveM-Server-Betreiber, Scripter und Maintainer
Zielgruppe: FiveM-Server-Betreiber, Scripter und Maintainer, die hochwertige Übersetzungen ohne kaputte Platzhalter oder defekte UI wollen.
Dieser Leitfaden ist Teil unseres vollständigen FiveM-Content-Creation-Guides, der alles von MLO-Design bis hin zu Scripting, Fahrzeug-Modding und dem Aufbau deiner Creator-Marke abdeckt.
Kurzfassung
- Alle Texte in Locale-Dateien (JSON oder Lua-Tabellen) zentralisieren. Strings niemals direkt in die Spiellogik schreiben.
- Platzhalter (z. B.
%s,%d,%{name},{0},~r~,^1) während der Übersetzung schützen. - KI für Erst-Übersetzung + Glossar + automatisierte Checks → kurze menschliche Überprüfung → fertig.
- Eine einzige Quelle der Wahrheit pflegen (meist Englisch), Änderungen per Diff ermitteln und nur geänderte Keys neu generieren.
Warum du deine FiveM-Skripte übersetzen solltest
- Zugänglichkeit & Wachstum: Lokalisierte Server ziehen mehr Spieler an und binden sie langfristig.
- Professionalität: Einheitliche Terminologie über Befehle, UIs und Fehlermeldungen hinweg.
- Contributor-freundlich: Klare Locale-Struktur lädt Community-PRs ein.
Für eine grundlegende Einführung in Struktur und Best Practices: FiveM-Skripte übersetzen (der richtige Weg).
Architektur: Die richtige Art zu lokalisieren
Ziel: Kein für Nutzer sichtbarer String direkt im Gameplay-Code. Alles über eine Locale-Schicht leiten.
Empfohlene Ressourcenstruktur
my_resource/ ├─ fxmanifest.lua ├─ locales/ │ ├─ en.json # Quellsprache (einzige Quelle der Wahrheit) │ ├─ de.json # Übersetzt (generiert/bearbeitet) │ ├─ es.json # Übersetzt (generiert/bearbeitet) │ └─ qa.rules.json # Optional: Platzhalter-Whitelist & Checks ├─ client/ │ └─ main.lua ├─ server/ │ └─ main.lua └─ shared/ └─ i18n.lua # Übersetzungs-Helper
fxmanifest.lua (minimales Beispiel)
fx_version 'cerulean'
game 'gta5'
lua54 'yes'
shared_scripts {
'shared/i18n.lua',
}
files {
'locales/*.json'
}
shared/i18n.lua (Lightweight-Loader + Platzhalter-Substitution)
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}
end
return str
end
function _U(key, vars)
local dict = readLocale(LOCALE)
local src = dict[key]
if not src then
-- Fallback auf Englisch
src = readLocale('en')[key] or key
end
return interpolate(src, vars)
end
exports('Translate', _U)
Verwendung in Client-/Server-Code
-- Client
lib.notify({
title = _U('notify_title'),
description = _U('welcome_player', { name = GetPlayerName(PlayerId()) }),
})
-- Server
print(('[MyRes] %s'):format(_U('server_started')))
locales/en.json (Quelle)
{
"notify_title": "Server Message",
"welcome_player": "Welcome, %{name}!",
"server_started": "Server module is ready.",
"no_permission": "You do not have permission.",
"items_remaining": "%{count} items remaining"
}
KI-Übersetzungs-Workflow (schnell und sicher)
- Quelle extrahieren & einfrieren
- Englisch (oder deine Quellsprache) als
locales/en.jsonbehalten. - Key-Benennung durchsetzen:
domain.aktion.objekt(z. B.inventory.drop.confirm).
- Glossar erstellen/erweitern
- CSV- oder JSON-Map mit kanonischen Begriffen → Zielsprachen-Begriffe. Beispiel:
quelle,ziel EMS,Rettungsdienst PD,Polizei Mechanic,Mechaniker
- Platzhalter & Markup schützen
- Platzhalter:
%{name},%s,%d,{0} - FiveM-Farbcodes:
~r~,~g~,~s~; Chat-Codes:^1,^2 - NUI/HTML-Tags:
<b>,<span>…
- Via API übersetzen (Batch)
- Nur Werte senden, Keys unverändert lassen.
- Glossar und Stil (Ton) dem Modell/der Engine übergeben.
- Automatisiertes QA
- JSON validieren.
- Platzhalter-Parität prüfen (jeder Platzhalter aus der Quelle im Ziel vorhanden).
- Verbotene Änderungen markieren (z. B. geänderte Farbcodes oder unerlaubte Satzzeichen).
- Menschliche Stichprobe (5–10 Minuten)
- Befehle, Fehlermeldungen und lange UI-Strings überprüfen.
- Ausliefern & iterieren
- Ein Translation Memory (frühere Ausgaben) pflegen, um unveränderte Keys nicht neu zu übersetzen.
Leitplanken: Prompts & Regeln, die wirklich funktionieren
LLM-Prompt für JSON-Batch-Übersetzung
Aufgabe: JSON-Werte von Englisch nach <ZIELSPRACHE> für einen FiveM/GTA-RP-Kontext übersetzen. Regeln:
- KEYS UNVERÄNDERT LASSEN.
- ALLE Platzhalter exakt erhalten: %{var}, %s, %d, {}, ~r~, ~g~, ^1, ^2, etc.
- Groß-/Kleinschreibung und Code-Style-Token (Befehle, /slash-Befehle) unverändert lassen.
- Keine Anführungszeichen, Satzzeichen oder Bedeutungsänderungen hinzufügen.
- Nur gültiges JSON mit derselben Struktur zurückgeben.
JSON zum Übersetzen: <EN.JSON-INHALT HIER EINFÜGEN>
Regex für ein QA-Skript
- Platzhalter:
%%\{[A-Za-z0-9_]+\} - C printf:
%(?:\d+\$)?[sdif] - Chat-Codes:
\^\d - Tilde-Farbcodes:
~[rgbso]~
Beispiel: Mit DeepL übersetzen (Node.js)
Ideal für einmalige Jobs oder CI.
package.json (scripts)
{
"type": "module",
"scripts": {
"i18n:translate:de": "node tools/translate-deepl.js en de",
"i18n:check": "node tools/i18n-check.js"
}
}
tools/translate-deepl.js
import fs from 'fs';
import path from 'path';
import assert from 'assert';
import fetch from 'node-fetch';
const [,, srcLang, dstLang] = process.argv;
const apiKey = process.env.DEEPL_API_KEY;
assert(apiKey, 'DEEPL_API_KEY ist erforderlich');
const src = JSON.parse(fs.readFileSync('locales/en.json', 'utf8'));
const out = {};
const GLOSSARY = {
'EMS': 'Rettungsdienst',
'PD': 'Polizei',
};
function protect(str){
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);
let glossed = protectedText;
for (const [from, to] of Object.entries(GLOSSARY)) {
glossed = glossed.replace(new RegExp(`\\b${from}\\b`, 'g'), to);
}
const tr = await translate(glossed);
out[k] = restore(tr);
}
fs.writeFileSync(`locales/${dstLang}.json`, JSON.stringify(out, null, 2));
console.log(`locales/${dstLang}.json geschrieben`);
tools/i18n-check.js (Platzhalter-Parität)
import fs from '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;
for (const k of 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('Platzhalter-Abweichung bei Key:', k);
ok = false;
}
}
process.exit(ok ? 0 : 1);
LLMs (OpenAI/andere) effektiv einsetzen
- Nach Thema/Domäne aufteilen für besseren Kontext (z. B. Inventar, Polizei, Jobs).
- Kurze Beschreibungen pro Gruppe (zwei Zeilen) liefern, die Ton und Zielgruppe definieren.
- Few-Shot-Beispiele: 2–3 korrekt übersetzte Paare mit Platzhaltern verbessern die Konsistenz.
- Retry-Richtlinie: Nur fehlgeschlagene Keys neu übersetzen, die von
i18n-checkmarkiert wurden.
Few-Shot-Vorlage (System + User)
System: Du übersetzt FiveM-Spiel-UI-Strings für <ZIELSPRACHE>.
- Keys unverändert lassen, Platzhalter erhalten, Ton knapp halten.
User-Beispiele: EN: "You have %{count} fines." DE: "Du hast %{count} Strafzettel."
EN: "~r~Error:~s~ You lack permission." DE: "~r~Fehler:~s~ Dir fehlt die Berechtigung."
Übersetze nun die folgenden JSON-Werte von Englisch nach <ZIELSPRACHE>. Nur gültiges JSON zurückgeben: <JSON HIER EINFÜGEN>
NUI (HTML/JS)-Übersetzungen
Für Browser-UIs ist eine clientseitige Bibliothek praktisch.
Empfohlener Ansatz
- Pro Sprache ein JSON-Bundle in
web/locales/<lang>.jsonverwenden. - Mit deinem UI-Framework laden und einen
t(key, vars)-Helper bereitstellen. - Dieselben Keys wie in Server-Locales verwenden, um kognitive Last zu reduzieren.
Minimaler JS-Helper
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;
}
ESX/QBCore-Besonderheiten
- Viele ESX-Skripte liefern
locales/en.lua,locales/de.luamit einem_U-Helper. - Wenn du Lua-Tabellen für Locales verwendest, einen einheitlichen Stil in deinem gesamten Repo beibehalten. JSON und Lua für dieselbe Ressource zu mischen erhöht den Wartungsaufwand.
- QBCore nutzt oft config-gesteuerte Meldungen. Wiederholte Strings in Locale-Dateien auslagern.
Lua-Tabellen-Locale (falls Lua gegenüber JSON bevorzugt)
Locales = Locales or {} Locales['en'] = { no_permission = 'You do not have permission.', welcome_player = 'Welcome, %{name}!' } Locales['de'] = { no_permission = 'Du hast keine Berechtigung.', welcome_player = 'Willkommen, %{name}!' }
Qualitätsgates vor dem Ausliefern
- JSON/Lua-Parse-Check in CI.
- Platzhalter-Parität (Regex-Checks wie gezeigt).
- Verbotene Änderungen: Bearbeitungen an
/commands, Keybind-Buchstaben, Farb-/Chat-Codes verbieten. - Längen-Deltas: Wachstum von +40 % für UI-Schaltflächen markieren; kann Layout brechen.
- Rauch-Test: Server hochfahren und kritische Flows stichprobenartig prüfen.
Neu beim Server-Setup für Tests? Folge diesem Starter: Wie du einen FiveM-Server erstellst.
Wartungsstrategie
en.jsonals Quelle der Wahrheit behandeln; CI-Job erstellen, deren.jsonmit Zielsprachen vergleicht und nur geänderte Keys aktualisiert.- Ein
CHANGELOG.i18n.mdfür Übersetzer pflegen. - Community zur Mitarbeit via PRs einladen; Style Guide und Glossar in
/docs/i18n.mddokumentieren.
Häufige Fallstricke (und Lösungen)
- Kaputte Platzhalter → Automatisierte Checks und Schutz-Token verwenden.
- Inkonsistente Terminologie → Glossar pflegen und in Prompts und Vorverarbeitung durchsetzen.
- Gemischte Locales im Code → CI-Fehler auslösen, wenn Strings außerhalb von
locales/gefunden werden. - RTL-Sprachen → NUI-CSS muss
direction: rtl;setzen und Schriften mit RTL-Unterstützung verwenden. - Groß-/Kleinschreibungs- und Satzzeichendrift → KI explizit anweisen und einen Linter zur Normalisierung nutzen.
Externe Ressourcen
- DeepL API — Entwicklerdokumentation: https://www.deepl.com/docs-api
- Google Cloud Translation — Docs & Best Practices: https://cloud.google.com/translate/docs
- FiveM Resource Manifest (fxmanifest.lua) — Referenz: https://docs.fivem.net/docs/scripting-reference/resource-manifest/resource-manifest/
Copy-Paste-Checklisten
Vor der Übersetzung
- Alle Strings in
locales/en.json(oder Lua-Tabelle) zentralisiert - Keys folgen einer Namenskonvention
- Glossar vorbereitet
- Platzhalter geprüft
Ausführen
- Batch-Übersetzung mit Glossar
- Ausgabe in
locales/<lang>.jsonspeichern
QA
- JSON/Lua gültig
- Platzhalter-Parität OK
- Verbotene Token unverändert
- Längen-Deltas akzeptabel
- Menschliche Stichprobe abgeschlossen
Ausliefern
- CI grün
- Changelog aktualisiert
- Community-Feedback eingeladen


