Economize 20% com WELCOMEVer ofertas
Como traduzir scripts usando IA (Guia FiveM)

Como Traduzir FiveM Scripts Usando IA

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)

  1. 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).
  1. 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

  1. 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>
  1. Traduzir via API (lote)
  • Enviar valores apenas, mantenha chaves inalterado.
  • Fornecer glossário e estilo (tom) para o modelo/motor.
  1. 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).
  1. Verificação pontual humana (5–10 minutos)
  • Revise comandos, mensagens de erro e longas sequências de caracteres da interface do usuário.
  1. 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.lua com um _U ajudante.
  • 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.json como fonte da verdade; crie um trabalho de CI que difere en.json e as atualizações apenas alteraram as chaves nos alvos.
  • Mantenha um CHANGELOG.i18n.md para 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


Recursos internos (leitura relacionada)


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