>
Tutorial 2026-05-02

Localizar os Teus Scripts FiveM - Suporte Multilingue

TDYSKY

TDYSKY

Fundador & Lead Developer na Agency Scripts

Por que a localização é importante para servidores FiveM

As comunidades de RPG FiveM abrangem todo o mundo, com enormes bases de jogadores na Alemanha, França, Brasil, Turquia e dezenas de outros países. Se seus scripts suportam apenas o inglês, você está eliminando uma grande parte de clientes em potencial e limitando as comunidades que podem usar seus recursos. Um script devidamente localizado adapta todos os textos, notificações, menus e mensagens de erro voltados para o usuário ao idioma preferido do jogador. Este não é apenas um recurso de qualidade de vida, mas uma vantagem competitiva que separa os roteiros amadores dos profissionais. A boa notícia é que implementar a internacionalização (i18n) no FiveM é simples quando você entende o padrão.

Configurando o sistema local

A base de qualquer sistema de localização é uma forma estruturada de armazenar e recuperar strings traduzidas. A abordagem mais comum no FiveM é usar arquivos de localidade, um por idioma, armazenados em um diretório locales dentro do seu recurso. Cada arquivo exporta uma tabela de pares chave-valor onde a chave é um identificador exclusivo e o valor é a string traduzida. Veja como estruturar seu módulo de localidade:

-- locales/en.lua
Locales = Locales or {}
Locales['en'] = {
    ['job_started']       = 'You have started your shift as %s.',
    ['job_ended']         = 'You have ended your shift. Earnings: $%d',
    ['not_enough_money']  = 'You do not have enough money. You need $%d.',
    ['inventory_full']    = 'Your inventory is full. Free up some space first.',
    ['vehicle_spawned']   = 'Your vehicle has been spawned nearby.',
    ['access_denied']     = 'You do not have permission to do that.',
    ['cooldown_active']   = 'Please wait %d seconds before doing that again.',
    ['item_received']     = 'You received %dx %s.',
}
-- locales/de.lua
Locales = Locales or {}
Locales['de'] = {
    ['job_started']       = 'Du hast deine Schicht als %s begonnen.',
    ['job_ended']         = 'Du hast deine Schicht beendet. Verdienst: $%d',
    ['not_enough_money']  = 'Du hast nicht genug Geld. Du brauchst $%d.',
    ['inventory_full']    = 'Dein Inventar ist voll. Schaffe zuerst Platz.',
    ['vehicle_spawned']   = 'Dein Fahrzeug wurde in der Naehe gespawnt.',
    ['access_denied']     = 'Du hast keine Berechtigung dafuer.',
    ['cooldown_active']   = 'Bitte warte %d Sekunden, bevor du das erneut tust.',
    ['item_received']     = 'Du hast %dx %s erhalten.',
}

Construindo a Função de Tradução

O núcleo do sistema é uma função de tradução que procura chaves e suporta formatação de strings com argumentos variáveis. Esta função deve voltar normalmente para um idioma padrão quando uma chave estiver faltando na localidade ativa e deve alertar os desenvolvedores sobre traduções faltantes durante o desenvolvimento, em vez de mostrar chaves brutas aos jogadores.

-- shared/locale.lua
local currentLocale = 'en'
local fallbackLocale = 'en'

function SetLocale(locale)
    if Locales[locale] then
        currentLocale = locale
    else
        print(('[^1LOCALE^0] Language "%s" not found, falling back to "%s"'):format(locale, fallbackLocale))
        currentLocale = fallbackLocale
    end
end

function L(key, ...)
    local str = nil

    if Locales[currentLocale] and Locales[currentLocale][key] then
        str = Locales[currentLocale][key]
    elseif Locales[fallbackLocale] and Locales[fallbackLocale][key] then
        print(('[^3LOCALE^0] Missing key "%s" for locale "%s", using fallback'):format(key, currentLocale))
        str = Locales[fallbackLocale][key]
    end

    if not str then
        print(('[^1LOCALE^0] Missing translation key: "%s"'):format(key))
        return key
    end

    if ... then
        return str:format(...)
    end

    return str
end

Usando traduções em seus scripts

Depois que o módulo locale for carregado, usar traduções em todo o seu script é tão simples quanto chamar a função L() com a chave e quaisquer argumentos de formato. Isso mantém o código do script limpo e separa totalmente o conteúdo da lógica.

-- server/main.lua
RegisterNetEvent('myresource:startJob', function(jobName)
    local src = source
    local xPlayer = ESX.GetPlayerFromId(src) -- or your framework equivalent

    if not xPlayer then return end

    if not HasPermission(src, jobName) then
        TriggerClientEvent('ox_lib:notify', src, {
            title = L('access_denied'),
            type = 'error'
        })
        return
    end

    ActiveJobs[src] = { name = jobName, started = os.time() }

    TriggerClientEvent('ox_lib:notify', src, {
        title = L('job_started', jobName),
        type = 'success'
    })
end)

Detecção de idioma por jogador

Um sistema de localização verdadeiramente profissional detecta automaticamente o idioma de cada jogador. Você pode fazer isso lendo a configuração de idioma do jogo do cliente ou permitindo que os jogadores escolham seu idioma por meio de uma configuração ou comando. A abordagem de detecção do lado do cliente usa o nativo GetCurrentLanguage, que retorna a linguagem do jogo como um código de duas letras. Você pode então enviar isso ao servidor para que todas as notificações desse jogador usem o idioma de sua preferência.

-- client/locale_detect.lua
CreateThread(function()
    local gameLang = GetCurrentLanguage()

    -- Map GTA language codes to your locale codes
    local langMap = {
        ['en-us'] = 'en',
        ['de-de'] = 'de',
        ['fr-fr'] = 'fr',
        ['es-es'] = 'es',
        ['pt-br'] = 'pt',
        ['it-it'] = 'it',
        ['pl-pl'] = 'pl',
        ['tr-tr'] = 'tr',
        ['ru-ru'] = 'ru',
        ['zh-cn'] = 'zh',
        ['ja-jp'] = 'ja',
        ['ko-kr'] = 'ko',
    }

    local detected = langMap[gameLang] or 'en'
    SetLocale(detected)

    TriggerServerEvent('myresource:setPlayerLocale', detected)
end)

Armazenamento local por jogador no servidor

No lado do servidor, armazene a preferência de localidade de cada jogador para que você possa formatar as mensagens no idioma correto antes de enviá-las. Isso é fundamental para notificações acionadas pelo servidor e mensagens de bate-papo originadas da lógica do lado do servidor, onde você não tem acesso direto à configuração de localidade do cliente.

-- server/locale_manager.lua
local PlayerLocales = {}

RegisterNetEvent('myresource:setPlayerLocale', function(locale)
    local src = source
    if Locales[locale] then
        PlayerLocales[src] = locale
    else
        PlayerLocales[src] = 'en'
    end
end)

AddEventHandler('playerDropped', function()
    PlayerLocales[source] = nil
end)

function GetPlayerLocale(src)
    return PlayerLocales[src] or 'en'
end

function LForPlayer(src, key, ...)
    local locale = GetPlayerLocale(src)
    local str = nil

    if Locales[locale] and Locales[locale][key] then
        str = Locales[locale][key]
    elseif Locales['en'] and Locales['en'][key] then
        str = Locales['en'][key]
    end

    if not str then return key end
    if ... then return str:format(...) end
    return str
end

Localizando interfaces NUI e JavaScript

Muitos scripts FiveM usam NUI (HTML/JS) para suas interfaces de usuário e também precisam de localização. A melhor abordagem é enviar toda a tabela de localidade para o quadro NUI quando ele for inicializado e, em seguida, usar uma função de tradução JavaScript que espelhe a de Lua. Isso evita retornos de chamada NUI constantes para cada string.

// nui/js/locale.js
let currentLocale = {};
let fallbackLocale = {};

window.addEventListener('message', (event) => {
    if (event.data.action === 'setLocale') {
        currentLocale = event.data.locale || {};
        fallbackLocale = event.data.fallback || {};
        updateAllTranslations();
    }
});

function L(key, ...args) {
    let str = currentLocale[key] || fallbackLocale[key] || key;

    if (args.length > 0) {
        let i = 0;
        str = str.replace(/%[sd]/g, () => args[i++] ?? '');
    }

    return str;
}

function updateAllTranslations() {
    document.querySelectorAll('[data-locale]').forEach((el) => {
        const key = el.getAttribute('data-locale');
        el.textContent = L(key);
    });
}

Configuração do manifesto de recursos

Seu fxmanifest.lua precisa incluir todos os arquivos de localidade para que sejam carregados quando o recurso for iniciado. Use um padrão glob para selecionar automaticamente quaisquer novos arquivos de localidade adicionados sem precisar atualizar o manifesto todas as vezes. Certifique-se de que o módulo de localidade compartilhado seja carregado antes dos arquivos de dados de localidade.

-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'

shared_scripts {
    'shared/locale.lua',
    'locales/*.lua',
}

client_scripts {
    'client/locale_detect.lua',
    'client/main.lua',
}

server_scripts {
    'server/locale_manager.lua',
    'server/main.lua',
}

ui_page 'nui/index.html'

files {
    'nui/**/*',
}

Práticas recomendadas para localização FiveM

  • Use chaves descritivas em vez de IDs numéricos. Chaves como inventory_full são autodocumentadas e facilitam a manutenção do que msg_042.
  • Sempre use marcadores de formato (%s, %d) para valores dinâmicos em vez de concatenação de strings. Idiomas diferentes têm ordens de palavras diferentes, portanto, os valores precisam poder ser inseridos em posições diferentes.
  • Inclua comentários de contexto em seus arquivos de localidade para que os tradutores entendam onde cada string é exibida e o que os argumentos de formato representam.
  • Teste com strings longas. O texto em alemão normalmente é 30% mais longo que o em inglês. Certifique-se de que os elementos da IU possam lidar com traduções mais longas sem interromper o layout.
  • Nunca codifique strings voltadas para o usuário. Cada notificação, rótulo de menu, texto de ajuda e mensagem de erro devem passar pela função L(), mesmo se você oferecer suporte inicialmente a apenas um idioma.
  • Forneça um comando de idioma como /lang de para que os jogadores possam substituir o idioma detectado automaticamente a qualquer momento.

Partilhar este artigo

Pronto para melhorar o teu servidor?

Explora os nossos scripts FiveM premium na loja Agency Scripts ou junta-te à nossa comunidade no Discord para suporte e atualizações.