FiveM скрипт locales: переводимые скрипты с en.lua, ox_lib и ESX

Сделайте ваш FiveM скрипт переводимым: папка locales с en.lua и es.lua, простая функция locale, ox_lib система locale, ESX TranslateCap и конфиг языка.

Ваш скрипт имеет 'You have no money' жёстко кодированный в сорока местах. Владелец сервера в Мадриде или Берлине хочет его на его языке, и единственный способ это редактировать код. Исправление это слой locale: строки живут в файлах на язык, код просит ключи. Это руководство показывает простую версию обычного Lua, затем ox_lib систему и ESX.

Идея

Вместо:

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

код пишет:

lua
ESX.ShowNotification(L('no_money'))

и файл языка поставляет текст для no_money. Переводчики затем меняют файлы, никогда не код, и открытые файлы locale могут отправляться без шифрования (смотрите escrow_ignore).

Функция обычного Lua locale

Макет папки:

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, функция которая выглядит ключ вверх:

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

Манифест, с locales сначала и конфигом перед функцией:

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'

И в config.lua:

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

Использование, с %s заполнителями заполненными string.format:

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

Fallbacks важны: отсутствующий ключ в es откатывает к en, и отсутствующий ключ везде возвращает ключ сам, поэтому вы видите no_money на экране вместо ошибки. Дыра в переводе затем показывает вверх как видимый ключ, не краш.

Совет: держите одинаковые ключи в каждом файле языка. Маленький скрипт который сравнивает ключи en с каждым другим файлом ловит отсутствующие переводы перед выпуском.

Система locale ox_lib

Если ваш скрипт уже зависит от ox_lib, вы можете использовать его встроенные locales вместо написания L(). Форма это: загрузите ox_lib в манифест, поместите ваши переводы в папку locales/ (ox_lib использует .json файлы названные по языку, как locales/en.json), включите их с files, и вызовите lib.locale() один раз:

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

Проверьте документацию ox_lib для текущего формата и для того как язык выбирается (сервер convar), потому что макет файла и имена опций изменились между версиями. Если вы видите ox_lib/init.lua не найдено, исправьте это сначала: ox_lib init.lua не найдено.

ESX и TranslateCap

ESX скрипты имеют свой собственный паттерн locale. Файлы Locale заполняют таблицу Locales и фреймворк предоставляет функцию перевода:

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'))

Более старые скрипты используют _U('no_money') для одинакового. TranslateCap это текущее имя в недавних ESX Legacy, и язык выбирается по Config.Locale или собственной настройке фреймворка. Как с ox_lib, проверьте документацию версии ESX которую вы запускаете для точных имён файлов и которая из двух функций это ожидает.

QBCore имеет подобный модуль Lang (locales/en.lua используя Lang:t('key')) которые работают так же: таблица на язык и функция которая разрешает ключи.

Выбор языка

Предложите одну строку конфига, рядом с вершиной config.lua:

lua
Config.Locale = 'en'   -- 'en', 'es', 'fr'...

Если ваш скрипт это смесь клиента, сервера и NUI, помните что NUI текст нуждается его собственные переводы: отправьте строки на страницу один раз когда она открывается, вместо написания английского в HTML. Смотрите FiveM NUI с React и Vite для передачи данных на страницу.

Контрольный список

Симптом Решение
Английский текст жёстко кодированный в коде Переместите его в locales/en.lua и вызовите L('key')
attempt to index a nil value на Locales[...] Загрузите файлы locale перед кодом которые их читают
Сырой ключ показывает на экране Ключ отсутствует в выбранном языке и в en
Неправильный язык используется Проверьте Config.Locale и locale convar
ox_lib locale() возвращает ключ Вызовите lib.locale() сначала и включите locales/*.json в files
NUI всё ещё английский Передайте переведённые строки на страницу

Короткие ответы

Где я должен поместить переводы в FiveM скрипт?

В папке locales/ с одним файлом на язык, такой как en.lua и es.lua, загруженный перед кодом который их использует. Скрипт затем просит ключ, никогда за литеральную строку.

Имеет ли ox_lib система locale?

Да. Это читает файлы locale и даёт вам locale('key') после того как вы вызовите lib.locale(). Проверьте ox_lib документацию для текущего формата файла и настройки которая выбирает язык.

Как я выбираю язык?

С Config.Locale значением в вашем конфиге, или с convar ваш фреймворк или ox_lib читает. Откатитесь на английский когда ключ или язык отсутствует.

Скрипты без этой проблемы

Mic PhoneСкладной телефон, который раскладывается в планшет и работает и на настоящем телефоне игрока.Смотреть скрипт →Shop CreatorМагазин меньше чем за минуту — владельцы, сотрудники, сейфы и ограбления в комплекте.Смотреть скрипт →Item Creator V2Создавайте используемые предметы с анимациями, пропами, эффектами и не только — без кода.Смотреть скрипт →

Читайте также