
Cómo traducir guiones con IA (Guía FiveM)
Los scripts gratuitos son útiles para comprobaciones rápidas. Para servidores de producción, compare los paquetes completos para servidores o los scripts de pago con mantenimiento, teniendo en cuenta el framework y el caso de uso.
Audiencia: Propietarios de servidores, programadores y mantenedores de FiveM que desean traducciones de alta calidad sin alterar los marcadores de posición ni la interfaz de usuario.
Resumen
- Centraliza todo el texto en archivos de configuración regional (tablas JSON o Lua). Nunca incorpores cadenas de código en la lógica del juego.
- Proteger los marcadores de posición (por ejemplo,
%s,%d,%{nombre},{0},~r~,^1) durante la traducción. - Utilice IA para traducción de primera pasada + glosario + controles automáticos → revisión humana rápida → envío.
- Mantenga una única fuente de verdad (generalmente en inglés), compare los cambios y regenere solo las claves modificadas.
¿Por qué traducir tus guiones de FiveM?
- Accesibilidad y crecimiento: Los servidores localizados atraen a más jugadores y los mantienen interesados.
- Profesionalismo: Terminología consistente en todos los comandos, interfaces de usuario y mensajes de error.
- Amigable para los colaboradores: Una estructura local clara invita a las relaciones públicas de la comunidad.
Si necesita un repaso básico sobre la estructura y las mejores prácticas, consulte: Cómo traducir los guiones FiveM (de la manera correcta).
Arquitectura: la forma correcta de localizar
Meta: No hay cadenas visibles para el usuario dentro del código del juego. Todo se enruta a través de una capa de configuración regional.
Diseño de recursos recomendado
my_resource/ ├─ fxmanifest.lua ├─ locales/ │ ├─ en.json # idioma de origen (fuente única de verdad) │ ├─ de.json # traducido (generado/editado) │ ├─ es.json # traducido (generado/editado) │ └─ qa.rules.json # opcional: lista blanca de marcadores de posición y comprobaciones ├─ cliente/ │ └─ main.lua ├─ servidor/ │ └─ main.lua └─ compartido/ └─ i18n.lua # ayudante de traducción
fxmanifest.lua (ejemplo mínimo)
fx_version 'cerulean' juego 'gta5' lua54 'sí' shared_scripts { 'shared/i18n.lua', } archivos { 'locales/*.json' }
compartido/i18n.lua (cargador ligero + sustitución de marcador de posición)
local LOCALE = GetConvar('my_locale', 'en') local CACHE = {} función local loadJSON(path) archivo local = io.open(path, 'r') si no es archivo entonces devuelve {} fin local contenido = archivo:read('*a') archivo:close() local ok, datos = pcall(function() return json.decode(content) end) devuelve ok y datos o {} fin local función readLocale(lang) si CACHE[lang] entonces devuelve CACHE[lang] fin local archivo = ('locales/%s.json'):format(lang) local dict = loadJSON(file) CACHE[lang] = dict return dict fin local función interpolar(str, vars) si no es vars entonces devuelve str fin para k, v en pares(vars) hacer str = str:gsub('%%{'..k..'}', tostring(v)) -- %{nombre} fin retorno str fin función _U(clave, vars) local dict = readLocale(LOCALE) local src = dict[clave] si no es src entonces -- se vuelve al inglés si falta src = readLocale('en')[clave] o clave fin retorno interpolado(src, vars) fin exportaciones('Traducir', _U)
Uso en código cliente/servidor
-- Cliente lib.notify({ título = _U('notify_title'), descripción = _U('welcome_player', { nombre = GetPlayerName(PlayerId()) }), }) -- Servidor print(('[MyRes] %s'):format(_U('server_started')))
locales/en.json (fuente)
{ "notify_title": "Mensaje del servidor", "welcome_player": "¡Bienvenido, %{name}!", "server_started": "El módulo del servidor está listo.", "no_permission": "No tienes permiso.", "items_remaining": "Quedan %{count} elementos" }
Flujo de trabajo de traducción con IA (rápido y seguro)
- Extraer y congelar la fuente
- Mantener Inglés (o su fuente) como
locales/en.json. - Hacer cumplir la denominación de clave:
dominio.acción.asunto(p.ej,inventario.caída.confirmar).
- Crear/ampliar un glosario
- Mapa CSV o JSON de términos canónicos → términos objetivo. Ejemplo:
fuente, destino EMS, Rettungsdienst PD, Polizei Mechanic, Mechaniker
- Proteger marcadores de posición y marcado
- Marcadores de posición:
%{nombre},%s,%d,{0} - Códigos de color FiveM:
~r~,~g~,~s~; códigos de chat:^1,^2 - Etiquetas NUI/HTML:
<b>,<span>…
- Traducir mediante API (lote)
- Enviar valores solamente, mantén llaves sin alterar.
- Suministrar glosario y estilo (tono) al modelo/motor.
- Control de calidad automatizado
- Validar JSON.
- Verificar paridad de marcador de posición (cada marcador de posición en el origen existe en el destino).
- Marcar cambios prohibidos (por ejemplo, códigos de colores alterados o puntuación agregada cuando no están permitidos).
- Control humano aleatorio (5–10 minutos)
- Revise comandos, mensajes de error y cadenas de interfaz de usuario largas.
- Enviar e iterar
- Mantener un memoria de traducción (salidas anteriores) para evitar volver a traducir claves sin cambios.
Barandillas: indicaciones y reglas que realmente funcionan
Solicitud de LLM para traducción por lotes de JSON
Tarea: Traducir valores JSON del inglés al Para un contexto de FiveM/GTA RP. Reglas: - MANTENER LAS CLAVES INALTERADAS. - CONSERVAR todos los marcadores de posición exactamente: %{var}, %s, %d, {0}, ~r~, ~g~, ^1, ^2, etc. - Mantener las mayúsculas y los tokens de estilo de código (comandos, comandos /slash) sin cambios. - No añadir comillas, puntuación adicional ni cambiar el significado. - Devolver SOLO JSON válido con la misma estructura. JSON para traducir:
Expresiones regulares que puedes usar en un script de control de calidad
- Marcadores de posición:
%%\{[A-Za-z0-9_]+\} - C printf:
%(?:\d+\$)?[sdif] - Códigos de chat:
\^\d - Códigos de colores de tilde:
~[rgbso]~
Ejemplo: traducir con DeepL (Node.js)
Funciona muy bien para trabajos puntuales o CI.
paquete.json (scripts)
{ "tipo": "módulo", "scripts": { "i18n:translate:de": "node tools/translate-deepl.js en de", "i18n:check": "node tools/i18n-check.js" } }
herramientas/translate-deepl.js
importar fs desde 'fs'; importar ruta desde 'ruta'; importar afirmación desde 'assert'; importar búsqueda desde 'nodo-fetch'; const [,, srcLang, dstLang] = proceso.argv; const apiKey = proceso.env.DEEPL_API_KEY; // establecer en CI/ENV assert(apiKey, 'DEEPL_API_KEY es obligatorio'); const src = JSON.parse(fs.readFileSync('locales/en.json', 'utf8')); const salida = {}; const GLOSARIO = { 'EMS': 'Servicio de atención médica', 'PD': 'Polizai', }; función protect(str){ // Reemplazar marcadores de posición con tokens que DeepL no alterará return str .replace(/%\{([^}]+)\}/g, '⟦$1⟧') .replace(/%s/g, '⟪S⟫') .replace(/%d/g, '⟪D⟫'); } función restore(str){ return str .replace(/⟦([^⟧]+)⟧/g, '%{$1}') .replace(/⟪S⟫/g, '%s') .replace(/⟪D⟫/g, '%d'); } función asíncrona translate(texto){ const res = await fetch('https://api.deepl.com/v2/translate', { método: 'POST', encabezados: { 'Tipo-De-Contenido': 'application/x-www-form-urlencoded' }, cuerpo: new URLSearchParams({ clave_de_autenticación: apiKey, texto: texto, idioma_de_origen: srcLang.toUpperCase(), idioma_de_destino: dstLang.toUpperCase(), formalidad: 'preferir_más' }) }); const json = await res.json(); si (!json.translations) generar un nuevo Error(JSON.stringify(json)); devolver json.translations[0].texto; } para (const [k, v] de Object.entries(src)) { const protectedText = protect(v); // Glosario pre-pass (simple): let glossed = protectedText; para (const [desde, hasta] de Object.entries(GLOSSARY)) { glossed = glossed.replace(new RegExp(`\\b${desde}\\b`, 'g'), hasta); } // Traducir // 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`);
herramientas/i18n-check.js (paridad de marcador de posición)
importar fs desde '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; para (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('Desajuste del marcador de posición para la clave:', k); ok = false; } } process.exit(ok ? 0 : 1);
Uso eficaz de LLMs (OpenAI/otros)
- Fragmentos por tema/dominio para un mejor contexto (por ejemplo, inventario, policía, trabajos).
- Proporcionar descripciones breves por grupo (dos líneas) para definir tono y audiencia.
- Algunos ejemplos:2 o 3 pares traducidos correctamente con marcadores de posición mejoran la consistencia.
- Política de reintentos: vuelva a ejecutar solo las claves fallidas marcadas por
verificación i18n.
Plantilla de pocas tomas (sistema + usuario)
Sistema: Traduce las cadenas de interfaz de usuario del juego FiveM para . - Mantener las claves sin cambios, conservar los marcadores de posición y mantener un tono conciso. Ejemplos de usuario: EN: "Tienes %{count} multas". DE: "Tienes %{count} Strafzettel". EN: "~r~Error:~s~ No tienes permiso". DE: "~r~Fehler:~s~ Dir fehlt die Berechtigung". Ahora traduce los siguientes valores JSON del inglés al español. Devuelve solo JSON válido:
Traducciones de NUI (HTML/JS)
Para las interfaces de usuario del navegador, una biblioteca del lado del cliente resulta práctica.
Enfoque recomendado
- Utilice un paquete JSON por idioma en
web/configuraciones locales/ .json. - Cargue con su marco de interfaz de usuario y exponga un
t(clave, variables)ayudante. - Mantener el mismas llaves como configuraciones regionales del servidor para reducir la carga cognitiva.
Ayudante de JS minimalista
const dict = await (await fetch(`/locales/${lang}.json`)).json(); exportar función t(clave, vars){ dejar s = dict[clave] || clave; para (const [k,v] de Object.entries(vars||{})) s = s.replace(`%{${k}}`, v); devolver s; }
Especificaciones de ESX/QBCore
- Muchos scripts de ESX se entregan
locales/en.lua,locales/de.luacon un_Uayudante. - Si utiliza tablas Lua para configuraciones regionales, mantenga un estilo En todo tu repositorio. Mezclar JSON y Lua para el mismo recurso aumenta el costo de mantenimiento.
- QBCore suele usar mensajes basados en la configuración. Migre las cadenas repetidas a los archivos de configuración regional para evitar divergencias.
Configuración regional de la tabla Lua (si prefiere Lua sobre JSON)
Locales = Locales o {} Locales['en'] = { no_permission = 'No tienes permiso.', bienvenido_player = '¡Bienvenido, %{nombre}!' } Locales['de'] = { no_permission = 'Du hast keine Berechtigung.', bienvenido_player = 'Willkommen, %{name}!' }
Puertas de calidad antes del envío
- Comprobación de análisis de JSON/Lua en CI.
- Paridad de marcador de posición (verificaciones de expresiones regulares como se muestra).
- Cambios prohibidos:no permitir ediciones a
/comandos, letras de combinación de teclas, códigos de color/chat. - Deltas de longitud:bandera +40% crecimiento para los botones de UI; puede dañar el diseño.
- Prueba de humo:Ponga en marcha su servidor y verifique flujos críticos.
¿Es nuevo en la configuración de un servidor para pruebas? Siga esta guía: Cómo crear un servidor FiveM.
Estrategia de mantenimiento
- Tratar
en.jsoncomo fuente de verdad; crear un trabajo de CI que difiereen.jsony solo actualiza las claves modificadas en los objetivos. - Mantener un
REGISTRO DE CAMBIOS.i18n.mdpara traductores. - Incentive a la comunidad a contribuir a través de relaciones públicas; documente su guía de estilo y glosario en
/docs/i18n.md.
Errores comunes (y soluciones)
- Marcadores de posición rotos → Utilice controles automatizados y tokens de protección.
- Terminología inconsistente → Mantener un glosario y aplicarlo en las indicaciones y el preprocesamiento.
- Localidades mixtas en el código → Falla CI si se detectan cadenas fuera
locales/. - Idiomas RTL → Asegúrese de que sus conjuntos CSS de NUI
dirección: rtl;y utiliza fuentes con soporte RTL. - Desviación de mayúsculas y minúsculas y puntuación → Instruya a la IA explícitamente y ejecute un linter para normalizar la puntuación.
Recursos externos
- API de DeepL — documentación para desarrolladores: https://www.deepl.com/docs-api
- Traducción de Google Cloud — documentos y mejores prácticas: https://cloud.google.com/translate/docs
- Manifiesto de recursos de FiveM (fxmanifest.lua) - referencia: https://docs.fivem.net/docs/scripting-reference/resource-manifest/resource-manifest/
Recursos internos (lecturas relacionadas)
- Cómo traducir los guiones FiveM (de la manera correcta) — flujo de trabajo y patrones: https://fivemx.com/fivem-scripts-translation/
- Cómo crear un servidor FiveM — crear un banco de pruebas para el control de calidad: https://fivemx.com/how-to-create-a-fivem-server/
Listas de verificación de copiar y pegar
Pretraducción
- Todas las cadenas centralizadas en
locales/en.json(o tabla Lua) - Las claves siguen una convención de nomenclatura
- Glosario preparado
- Marcadores de posición auditados
Correr
- Traducción por lotes con glosario
- Guardar la salida en
locales/ .json
Control de calidad
- JSON/Lua válido
- Paridad de marcador de posición OK
- Fichas prohibidas sin cambios
- Deltas de longitud aceptables
- Se realizó una verificación humana aleatoria
Barco
- CI verde
- Registro de cambios actualizado
- Invitar a la comunidad a dar su opinión






