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:

lua
ESX.ShowNotification('You have no money')

el código escribe:

lua
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:

text
my_script/
  fxmanifest.lua
  config.lua
  locales/
    en.lua
    es.lua
  shared/locale.lua

locales/en.lua:

lua
Locales = Locales or {}

Locales['en'] = {
    no_money = 'You have no money',
    bought   = 'You bought %s for $%s',
}

locales/es.lua:

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:

lua
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
end

Manifiesto, con los locales primero y la configuración antes de la función:

lua
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:

lua
Config = {}
Config.Locale = 'en'

Uso, con placeholders %s llenados por string.format:

lua
print(L('bought', 'bread', 5))   -- You bought bread for $5

Los 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 en con 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:

lua
-- fxmanifest.lua
shared_script '@ox_lib/init.lua'
files { 'locales/*.json' }
json
{
  "no_money": "You have no money",
  "bought": "You bought %s for $%s"
}
lua
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:

lua
-- fxmanifest.lua
shared_scripts {
    '@es_extended/imports.lua',
    '@es_extended/locale.lua',
    'locales/*.lua',
    'config.lua',
}
lua
-- locales/en.lua
Locales['en'] = {
    ['no_money'] = 'You have no money',
}
lua
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:

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 →

Sigue leyendo