Público: Proprietários, criadores de scripts e mantenedores de servidores FiveM que desejam traduções de alta qualidade sem quebrar espaços reservados ou interface do usuário.
Resumo
- Centralize todo o texto em arquivos de localidade (tabelas JSON ou Lua). Nunca codifique strings na lógica do jogo.
- Proteja os espaços reservados (por exemplo,
%s,%d,%{nome},{0},~r~,^1) durante a tradução. - Use IA para tradução de primeira passagem + glossário + verificações automatizadas → revisão humana rápida → envio.
- Mantenha uma única fonte de verdade (geralmente inglês), compare as alterações e gere novamente apenas as chaves alteradas.
Por que traduzir seus scripts FiveM
- Acessibilidade e crescimento: Servidores localizados atraem mais jogadores e os mantêm engajados.
- Profissionalismo: Terminologia consistente em comandos, interfaces de usuário e mensagens de erro.
- Amigável para colaboradores: Uma estrutura local clara convida a relações públicas da comunidade.
Se você precisar de uma atualização básica sobre estrutura e melhores práticas, consulte: Como traduzir scripts FiveM (da maneira certa).
Arquitetura: a maneira certa de localizar
Meta: Nenhuma string visível ao usuário dentro do código do jogo. Roteie tudo por meio de uma camada de localidade.
Layout de recursos recomendado
my_resource\ ├─ fxmanifest.lua ├─ locales\ │ ├─ en.json # idioma de origem (única fonte de verdade) │ ├─ de.json # traduzido (gerado/editado) │ ├─ es.json # traduzido (gerado/editado) │ └─ qa.rules.json # opcional: lista de permissões e verificações de placeholders ├─ client\ │ └─ main.lua ├─ server\ │ └─ main.lua └─ shared\ └─ i18n.lua # auxiliar de tradução
fxmanifest.lua (exemplo mínimo)
fx_version 'cerulean'
game 'gta5'
lua54 'yes'
shared_scripts {
'shared\/i18n.lua',
}
files {
'locales\/*.json'
}
compartilhado/i18n.lua (carregador leve + substituição de espaço reservado)
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 para inglês se ausente
src = readLocale('en')[key] or key
end
return interpolate(src, vars)
end
exports('Translate', _U)
Uso em código cliente/servidor
-- Cliente
lib.notify({
title = _U('notify_title'),
description = _U('welcome_player', { name = GetPlayerName(PlayerId()) }),
})
-- Servidor
print(('[MyRes] %s'):format(_U('server_started')))
locales/en.json (fonte)
{
"notify_title": "Mensagem do Servidor",
"welcome_player": "Bem-vindo, %{name}!",
"server_started": "Módulo do servidor está pronto.",
"no_permission": "Você não tem permissão.",
"items_remaining": "%{count} itens restantes"
}
Fluxo de trabalho de tradução de IA (rápido e seguro)
- Extrair e congelar fonte
- Manter Inglês (ou sua fonte) como
locais/en.json. - Aplicar nomenclatura de chaves:
domínio.ação.assunto(por exemplo,inventário.queda.confirmar).
- Criar/ampliar um glossário
- Mapa CSV ou JSON de termos canônicos → termos-alvo. Exemplo:
fonte, alvo EMS, Rettungsdienst PD, Mecânico Polizei, Mechaniker
- Proteja marcadores de posição e marcação
- Espaços reservados:
%{nome},%s,%d,{0} - Códigos de cores FiveM:
~r~,~g~,~s~; códigos de bate-papo:^1,^2 - Etiquetas NUI/HTML:
<b>,<span>…
- Traduzir via API (lote)
- Enviar valores apenas, mantenha chaves inalterado.
- Fornecer glossário e estilo (tom) para o modelo/motor.
- QA automatizado
- Validar JSON.
- Verificar paridade de espaço reservado (todo espaço reservado na origem existe no destino).
- Sinalize alterações proibidas (por exemplo, códigos de cores alterados ou pontuação adicionada quando não permitidos).
- Verificação pontual humana (5–10 minutos)
- Revise comandos, mensagens de erro e longas sequências de caracteres da interface do usuário.
- Enviar e iterar
- Mantenha um memória de tradução (saídas anteriores) para evitar a retradução de chaves inalteradas.
Guardrails: dicas e regras que realmente funcionam
Prompt LLM para tradução em lote JSON
Tarefa: Traduzir valores JSON do inglês para para um contexto FiveM/GTA RP. Regras: - MANTENHA AS CHAVES INALTERADAS. - PRESERVE todos os espaços reservados exatamente: %{var}, %s, %d, {0}, ~r~, ~g~, ^1, ^2, etc. - Mantenha a capitalização e os tokens de estilo de código (comandos, comandos com barra) inalterados. - Não adicione aspas, pontuação extra ou altere o significado. - Retorne SOMENTE JSON válido com a mesma estrutura. JSON para traduzir:
Regex que você pode usar em um script de controle de qualidade
- Espaços reservados:
%%{[A-Za-z0-9_]+} - C printf:
%(?:d+$)?[sdif] - Códigos de bate-papo:
^d - Códigos de cores til:
~[rgbso]~
Exemplo: traduzir com DeepL (Node.js)
Funciona muito bem para trabalhos únicos ou IC.
pacote.json (scripts)
{
"type": "module",
"scripts": {
"i18n:translate:de": "node tools\/translate-deepl.js en de",
"i18n:check": "node tools\/i18n-check.js"
}
}
ferramentas/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; // set in CI/ENV
assert(apiKey, 'DEEPL_API_KEY is required');
const src = JSON.parse(fs.readFileSync('locales/en.json', 'utf8'));
const out = {};
const GLOSSARY = {
'EMS': 'Rettungsdienst',
'PD': 'Polizei',
};
function protect(str){
// Replace placeholders with tokens DeepL won't alter
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);
// Glossary pre-pass (simple):
let glossed = protectedText;
for (const [from, to] of Object.entries(GLOSSARY)) {
glossed = glossed.replace(new RegExp(`b${from}b`, 'g'), to);
}
// Translate
// 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`);
ferramentas/i18n-check.js (paridade de espaço reservado)
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('Mismatch de placeholders para a chave:', k);
ok = false;
}
}
process.exit(ok ? 0 : 1);
Usando LLMs (OpenAI/outros) de forma eficaz
- Fragmento por tópico/domínio para melhor contexto (por exemplo, inventário, polícia, empregos).
- Fornecer descrições curtas por grupo (duas linhas) para definir tom e público.
- Exemplos de poucos disparos: 2–3 pares traduzidos corretamente com marcadores de posição melhoram a consistência.
- Política de repetição: execute novamente apenas as chaves com falha sinalizadas por
i18n-check.
Modelo de poucos disparos (sistema + usuário)
Sistema: Você traduz as strings da interface do usuário do jogo FiveM para . - Mantenha as chaves inalteradas, preserve os espaços reservados e mantenha o tom conciso. Exemplos de usuários: EN: "Você tem %{count} multas." DE: "Você tem %{count} multas." EN: "~r~Error:~s~ Você não tem permissão." DE: "~r~Fehler:~s~ Dir sentiu a permissão." Agora traduza os seguintes valores JSON do inglês para . Retorna somente JSON válido:
Traduções NUI (HTML/JS)
Para interfaces de usuário de navegador, uma biblioteca do lado do cliente é prática.
Abordagem recomendada
- Use um pacote JSON por idioma em
web/locais/ .json. - Carregue com sua estrutura de IU e exponha um
t(chave, variáveis)ajudante. - Mantenha o mesmas chaves como localidades de servidor para reduzir a carga cognitiva.
Auxiliar JS mínimo
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;
}
Especificações do ESX/QBCore
- Muitos scripts ESX são enviados
locais/en.lua,locais/de.luacom um_Uajudante. - Se você usar tabelas Lua para localidades, mantenha um estilo em todo o seu repositório. Misturar JSON e Lua para o mesmo recurso aumenta o custo de manutenção.
- O QBCore frequentemente usa mensagens orientadas por configuração. Migre strings repetidas para arquivos de localidade para evitar divergências.
Localidade da tabela Lua (se você preferir Lua em vez de JSON)
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}!'
}
Portões de qualidade antes do envio
- Verificação de análise JSON/Lua em CI.
- Paridade de espaço reservado (verificações de regex conforme mostrado).
- Mudanças proibidas: não permitir edições em
/comandos, letras de atalho, códigos de cores/bate-papo. - Deltas de comprimento: sinalizador +40% crescimento para botões da interface do usuário; pode quebrar o layout.
- Teste de fumaça: ative seu servidor e verifique fluxos críticos.
É novo na configuração de um servidor para testes? Siga este guia: Como criar um servidor FiveM.
Estratégia de manutenção
- Tratar
en.jsoncomo fonte da verdade; crie um trabalho de CI que difereen.jsone as atualizações apenas alteraram as chaves nos alvos. - Mantenha um
CHANGELOG.i18n.mdpara tradutores. - Incentive a comunidade a contribuir por meio de RP; documente seu guia de estilo e glossário em
/docs/i18n.md.
Armadilhas comuns (e soluções)
- Espaços reservados quebrados → Use verificações automatizadas e tokens de proteção.
- Terminologia inconsistente → Mantenha um glossário e aplique-o em prompts e pré-processamento.
- Localidades mistas no código → Falha no CI se strings forem detectadas fora
locais/. - Idiomas RTL → Certifique-se de que seus conjuntos NUI CSS
direção: rtl;e usa fontes com suporte RTL. - Desvio de maiúsculas e minúsculas e pontuação → Instrua a IA explicitamente e execute um linter para normalizar a pontuação.
Recursos externos
- API DeepL — documentação do desenvolvedor: https://www.deepl.com/docs-api
- Tradução do Google Cloud — documentos e melhores práticas: https://cloud.google.com/translate/docs
- Manifesto de Recursos FiveM (fxmanifest.lua) — referência: https://docs.fivem.net/docs/scripting-reference/resource-manifest/resource-manifest/
Recursos internos (leitura relacionada)
- Como traduzir scripts FiveM (da maneira certa) — fluxo de trabalho e padrões: https://fivemx.com/fivem-scripts-translation/
- Como criar um servidor FiveM — criar um ambiente de testes para controle de qualidade: https://fivemx.com/how-to-create-a-fivem-server/
Listas de verificação de copiar e colar
Pré-tradução
- Todas as strings centralizadas em
locais/en.json(ou tabela Lua) - As chaves seguem uma convenção de nomenclatura
- Glossário preparado
- Espaços reservados auditados
Correr
- Tradução em lote com glossário
- Salvar saída para
locais/ .json
Controle de qualidade
- JSON/Lua válido
- Paridade de espaço reservado OK
- Fichas proibidas inalteradas
- Deltas de comprimento aceitáveis
- Verificação humana pontual realizada
Enviar
- CI verde
- Log de alterações atualizado
- Convidar a comunidade a dar feedback