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_fullkendi 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 deböylece oyuncular otomatik algılanan dili istedikleri zaman geçersiz kılabilir.