Bloga Geri Dön
Tutorial6 dk. okuma

FiveM Yerelleştirme ve i18n: Çok Dilli Scriptler

FiveM scriptlerini birden fazla dilde yayınlayın. Locale dosyaları, fallback anahtarları, araçlar ve ox_lib ile diğer büyük açık kaynaklı kaynaklar tarafından kullanılan kalıplar.

Agency Scripts

Agency Scripts Kurucusu ve Baş Geliştiricisi

FiveM Sunucuları İçin Yerelleştirme Neden Önemlidir?

FiveM roleplay toplulukları Almanya, Fransa, Brezilya, Türkiye ve düzinelerce diğer ülkede devasa oyuncu kitleleriyle tüm dünyaya yayılmıştır. Script'leriniz yalnızca İngilizceyi destekliyorsa, potansiyel müşterilerin büyük bir kısmını kaybeder ve resource'larınızı kullanabilecek toplulukları sınırlandırırsınız. Düzgün bir şekilde yerelleştirilmiş bir script, kullanıcıya yönelik tüm metinleri, bildirimleri, menüleri ve hata mesajlarını oyuncunun tercih ettiği dile uyarlar. Bu sadece bir yaşam kalitesi özelliği değil, amatör script'leri profesyonel olanlardan ayıran rekabetçi bir avantajdır. İyi haber şu ki mantığı kavradığınızda FiveM'de uluslararasılaştırmayı (i18n) uygulamak oldukça kolaydır.

Locale Sistemini Kurma

Herhangi bir yerelleştirme sisteminin temeli, çevrilmiş metinleri depolamak ve almak için yapılandırılmış bir yoldur. FiveM'deki en yaygın yaklaşım, her dil için bir tane olmak üzere şu konumda saklanan yerel ayar (locale) dosyalarını kullanmaktır: locales resource'unuzun içindeki dizin. Her dosya, anahtarın benzersiz bir tanımlayıcı ve değerin çevrilmiş metin olduğu bir anahtar-değer çifti tablosu dışa aktarır. Yerel ayar modülünüzü şu şekilde yapılandırabilirsiniz:

-- 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.',
}

Çeviri Fonksiyonunu Geliştirme

Sistemin temeli, anahtarları arayan ve değişken argümanlarla dize biçimlendirmeyi destekleyen bir çeviri fonksiyonudur. Bu fonksiyon, aktif yerel ayarda bir anahtar eksik olduğunda varsayılan bir dile sorunsuzca geri dönmeli ve oyunculara ham anahtarları göstermek yerine geliştirme sırasında geliştiricileri eksik çeviriler konusunda uyarmalıdır.

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

Script'lerinizde Çevirileri Kullanma

locale modülü yüklendikten sonra, script'iniz genelinde çevirileri kullanmak şu fonksiyonu çağırmak kadar kolaydır: L() anahtar ve biçimlendirme argümanlarıyla birlikte function. Bu, script kodunuzu temiz tutar ve içeriği mantıktan tamamen ayırır.

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

Oyuncu Başına Dil Algılama

Gerçekten profesyonel bir yerelleştirme sistemi, her oyuncunun dilini otomatik olarak algılar. Bunu istemcinin oyun dili ayarını okuyarak veya oyuncuların dillerini bir yapılandırma ya da komut aracılığıyla seçmelerine izin vererek gerçekleştirebilirsiniz. İstemci tarafı algılama yaklaşımı şunu kullanır: GetCurrentLanguage oyun dilini iki harfli bir kod olarak döndüren native. Daha sonra bunu sunucuya gönderebilirsiniz, böylece o oyuncu için tüm bildirimler tercih ettiği dili kullanır.

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

Oyuncu Başına Sunucu Taraflı Dil Depolaması

Server tarafında, mesajları göndermeden önce doğru dilde biçimlendirebilmek için her oyuncunun yerel ayar tercihini saklayın. Bu, client tarafının yerel ayarlarına doğrudan erişiminizin olmadığı ve server tarafı mantığından kaynaklanan sohbet mesajları ile sunucu kaynaklı bildirimler için kritik öneme sahiptir.

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

NUI ve JavaScript Arayüzlerini Yerelleştirme

Birçok FiveM script'i kullanıcı arayüzleri için NUI (HTML/JS) kullanır ve bunların da yerelleştirilmesi gerekir. En iyi yaklaşım, NUI frame'i başlatıldığında tüm locale tablosunu göndermek, ardından Lua fonksiyonunu yansıtan bir JavaScript çeviri fonksiyonu kullanmaktır. Bu sayede her dize için sürekli NUI callback gidiş gelişleri önlenir.

// 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);
    });
}

Resource Manifest Yapılandırması

Sizin fxmanifest.lua kaynak başladığında yüklenebilmeleri için tüm yerel ayar dosyalarını içermelidir. Manifest dosyasını her seferinde güncellemek zorunda kalmadan eklediğiniz yeni yerel ayar dosyalarını otomatik olarak dahil etmek için bir glob deseni kullanın. Paylaşılan yerel ayar modülünün yerel ayar veri dosyalarından önce yüklendiğinden emin olun.

-- 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/**/*',
}

FiveM Yerelleştirmesi İçin En İyi Uygulamalar

  • Açıklayıcı anahtarlar kullanın sayısal ID'ler yerine. Şunun gibi anahtarlar: inventory_full kendi kendini belgeler ve bakımı şuna kıyasla daha kolay hale getirir: msg_042.
  • Her zaman biçimlendirme yer tutucularını kullanın (%s, %d) metin birleştirme yerine dinamik değerler için kullanın. Farklı diller farklı sözcük dizilimlerine sahiptir, bu nedenle değerlerin farklı konumlara eklenebilmesi gerekir.
  • Bağlam açıklamalarını dahil edin çevirmenlerin her dizenin nerede görüntülendiğini ve biçim bağımsız değişkenlerinin neyi temsil ettiğini anlaması için yerel ayar dosyalarınızda.
  • Uzun dizelerle test edin. Almanca metinler genellikle İngilizceye kıyasla %30 daha uzundur. UI öğelerinizin düzeni bozmadan daha uzun çevirileri destekleyebildiğinden emin olun.
  • Kullanıcıya gösterilen metinleri asla doğrudan koda gömmeyin. Her bildirim, menü etiketi, yardım metni ve hata mesajı şunun üzerinden geçmelidir: L() başlangıçta yalnızca tek bir dili destekliyor olsanız bile function.
  • Bir dil komutu sağlayın beğen /lang de böylece oyuncular otomatik algılanan dili istedikleri zaman geçersiz kılabilir.

Başlamaya Hazır mısınız?

Scriptleri mağazamızdan edinin veya destek, güncellemeler ve gelecek yeniliklere göz atmak için Discord'a katılın.