Locales en scripts de FiveM: scripts traducibles con en.lua, ox_lib y ESX
Haz tu script de FiveM traducible: una carpeta locales con en.lua y es.lua, una función locale simple, el sistema de locale de ox_lib, ESX TranslateCap y una configuración de idioma.
Tu script tiene 'You have no money' codificado en cuarenta lugares. Un dueño de servidor en Madrid o Berlín lo quiere en su idioma, y la única forma es editar el código. La solución es una capa de locale: las cadenas viven en archivos por idioma, el código pide claves. Esta guía muestra una versión simple de Lua puro, luego el sistema de ox_lib y ESX.
La idea
En lugar de:
ESX.ShowNotification('You have no money')el código escribe:
ESX.ShowNotification(L('no_money'))y un archivo de idioma suministra el texto para no_money. Los traductores luego cambian archivos, nunca código, y los archivos de locale abiertos pueden enviarse sin encriptar (ver escrow_ignore).
Una función locale de Lua puro
Diseño de carpeta:
my_script/
fxmanifest.lua
config.lua
locales/
en.lua
es.lua
shared/locale.lualocales/en.lua:
Locales = Locales or {}
Locales['en'] = {
no_money = 'You have no money',
bought = 'You bought %s for $%s',
}locales/es.lua:
Locales = Locales or {}
Locales['es'] = {
no_money = 'No tienes dinero',
bought = 'Compraste %s por $%s',
}shared/locale.lua, la función que busca una clave:
function L(key, ...)
local lang = Locales[Config.Locale] or Locales['en']
local text = lang[key] or Locales['en'][key] or key
if select('#', ...) > 0 then
return text:format(...)
end
return text
endManifiesto, con los locales primero y la configuración antes de la función:
fx_version 'cerulean'
game 'gta5'
shared_scripts {
'config.lua',
'locales/*.lua',
'shared/locale.lua',
}
client_script 'client/main.lua'
server_script 'server/main.lua'Y en config.lua:
Config = {}
Config.Locale = 'en'Uso, con placeholders %s llenados por string.format:
print(L('bought', 'bread', 5)) -- You bought bread for $5Los respaldos importan: una clave faltante en es vuelve a en, y una clave faltante en todas partes devuelve la clave en sí, así ves no_money en pantalla en lugar de un error. Un agujero en una traducción luego se muestra como una clave visible, no un crash.
Consejo: mantén las mismas claves en cada archivo de idioma. Un pequeño script que compara las claves de
encon cada otro archivo atrapa traducciones faltantes antes del lanzamiento.
El sistema de locale de ox_lib
Si tu script ya depende de ox_lib, puedes usar sus locales integrados en lugar de escribir L(). La forma es: carga ox_lib en el manifiesto, pon tus traducciones en una carpeta locales/ (ox_lib usa archivos .json nombrados por idioma, como locales/en.json), inclúyelos con files, y llama lib.locale() una vez:
-- fxmanifest.lua
shared_script '@ox_lib/init.lua'
files { 'locales/*.json' }{
"no_money": "You have no money",
"bought": "You bought %s for $%s"
}lib.locale() -- load the language
lib.notify({ description = locale('no_money'), type = 'error' })
print(locale('bought', 'bread', 5))Comprueba los docs de ox_lib para el formato actual y para cómo se elige el idioma (una convar del servidor), porque el diseño de archivo y los nombres de opción han cambiado entre versiones. Si ves ox_lib/init.lua no encontrado, arreglalo primero: ox_lib init.lua no encontrado.
ESX y TranslateCap
Los scripts de ESX tienen su propio patrón de locale. Los archivos de locale llenan una tabla Locales y el framework proporciona una función de traducción:
-- fxmanifest.lua
shared_scripts {
'@es_extended/imports.lua',
'@es_extended/locale.lua',
'locales/*.lua',
'config.lua',
}-- locales/en.lua
Locales['en'] = {
['no_money'] = 'You have no money',
}TriggerEvent('esx:showNotification', TranslateCap('no_money'))Los scripts más antiguos usan _U('no_money') para lo mismo. TranslateCap es el nombre actual en ESX Legacy reciente, y el idioma se selecciona por Config.Locale o la configuración propia del framework. Como con ox_lib, comprueba los docs de la versión ESX que ejecutas para los nombres de archivo exactos y cuál de las dos funciones espera.
QBCore tiene un módulo similar Lang (locales/en.lua usando Lang:t('key')) que funciona de la misma manera: una tabla por idioma y una función que resuelve claves.
Eligiendo el idioma
Ofrece una línea de configuración, cerca de la parte superior de config.lua:
Config.Locale = 'en' -- 'en', 'es', 'fr'...Si tu script es una mezcla de cliente, servidor y NUI, recuerda que el texto de NUI necesita sus propias traducciones: envía las cadenas a la página una vez cuando se abre, en lugar de escribir inglés en el HTML. Ver NUI en FiveM con React y Vite para pasar datos a la página.
Checklist
| Síntoma | Solución |
|---|---|
| Texto en inglés codificado en el código | Muévelo a locales/en.lua y llama L('key') |
attempt to index a nil value en Locales[...] |
Carga archivos de locale antes del código que los lee |
| La clave sin procesar se muestra en pantalla | La clave falta en el idioma elegido y en en |
| Idioma incorrecto usado | Comprueba Config.Locale y la convar de locale |
locale() de ox_lib devuelve la clave |
Llama lib.locale() primero e incluye locales/*.json en files |
| NUI aún en inglés | Pasa las cadenas traducidas a la página |
Respuestas rápidas
¿Dónde debería poner traducciones en un script de FiveM?
En una carpeta locales/ con un archivo por idioma, como en.lua y es.lua, cargado antes del código que lo usa. El script luego pide una clave, nunca una cadena literal.
¿Tiene ox_lib un sistema de locale?
Sí. Lee archivos de locale y te da locale('key') después de que llamas lib.locale(). Comprueba los docs de ox_lib para el formato de archivo actual y la configuración que elige el idioma.
¿Cómo elijo el idioma?
Con un valor Config.Locale en tu configuración, o con una convar que tu framework u ox_lib lee. Vuelve al inglés cuando falta una clave o idioma.
Scripts que evitan este problema
Mic PhoneUn móvil plegable que se abre en tablet y llega al móvil real del jugador.Ver script →
Shop CreatorCrea una tienda en menos de un minuto — dueños, empleados, caja fuerte y atracos.Ver script →
Item Creator V2Crea items usables con animaciones, props, efectos y más — sin escribir código.Ver script →