Pourquoi la localisation est importante pour les serveurs FiveM
Les communautés de jeu de rôle FiveM s'étendent dans le monde entier, avec d'énormes bases de joueurs en Allemagne, en France, au Brésil, en Turquie et dans des dizaines d'autres pays. Si tes scripts ne prennent en charge que l'anglais, tu coupes une grande partie des clients potentiels et limitez les communautés qui peuvent utiliser tes ressources. Un script correctement localisé adapte tous les textes, notifications, menus et messages d'erreur destinés à l'utilisateur à la langue préférée du joueur. Il ne s’agit pas seulement d’une fonctionnalité de qualité de vie, mais d’un avantage concurrentiel qui différencie les scripts amateurs des scripts professionnels. La bonne nouvelle est que l’implémentation de l’internationalisation (i18n) dans FiveM est simple une fois que tu as compris le modèle.
Configuration du système de paramètres régionaux
La base de tout système de localisation est une manière structurée de stocker et de récupérer les chaînes traduites. L'approche la plus courante dans FiveM consiste à utiliser des fichiers de paramètres régionaux, un par langue, stockés dans un locales répertoire à l’intérieur de ton ressource. Chaque fichier exporte un tableau de paires clé-valeur où la clé est un identifiant unique et la valeur est la chaîne traduite. Voici comment structurer ton module locale :
-- 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.',
}
Construire la fonction de traduction
Le cœur du système est une fonction de traduction qui recherche les clés et prend en charge le formatage des chaînes avec des arguments variables. Cette fonction devrait revenir gracieusement à une langue par défaut lorsqu'une clé est manquante dans les paramètres régionaux actifs, et elle devrait avertir les développeurs des traductions manquantes pendant le développement plutôt que d'afficher les clés brutes aux joueurs.
-- 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
Utiliser des traductions dans tes scripts
Une fois le module locale chargé, utiliser les traductions dans tout ton script est aussi simple que d'appeler le L() fonction avec la clé et tous les arguments de format. Cela maintient le code de ton script propre et sépare entièrement le contenu de la logique.
-- 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)
Détection de langue par joueur
Un système de localisation véritablement professionnel détecte automatiquement la langue de chaque joueur. Tu peux y parvenir en lisant les paramètres de langue du jeu du client ou en laissant les joueurs choisir leur langue via une configuration ou une commande. L'approche de détection côté client utilise le GetCurrentLanguage native, qui renvoie la langue du jeu sous forme de code à deux lettres. Tu peux ensuite l'envoyer au serveur afin que toutes les notifications de ce joueur utilisent sa langue préférée.
-- 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)
Stockage local par joueur côté serveur
Côté serveur, stockez les préférences locales de chaque joueur afin de pouvoir formater les messages dans la bonne langue avant de les envoyer. Ceci est essentiel pour les notifications déclenchées par le serveur et les messages de discussion provenant de la logique côté serveur pour lesquels tu n'avez pas d'accès direct aux paramètres régionaux du client.
-- 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
Localisation des interfaces NUI et JavaScript
De nombreux scripts FiveM utilisent NUI (HTML/JS) pour leurs interfaces utilisateur, et ceux-ci nécessitent également une localisation. La meilleure approche consiste à envoyer l'intégralité de la table de paramètres régionaux à la trame NUI lors de son initialisation, puis à utiliser une fonction de traduction JavaScript qui reflète celle de Lua. Cela évite les va-et-vient constants des rappels NUI pour chaque chaîne.
// 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);
});
}
Configuration du manifeste de ressources
Ton fxmanifest.lua doit inclure tous les fichiers de paramètres régionaux afin qu'ils soient chargés au démarrage de la ressource. Utilisez un modèle global pour récupérer automatiquement tous les nouveaux fichiers de paramètres régionaux que tu ajoutes sans avoir à mettre à jour le manifeste à chaque fois. Assurez-tu que le module de paramètres régionaux partagés se charge avant les fichiers de données de paramètres régionaux.
-- 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/**/*',
}
Meilleures pratiques pour la localisation FiveM
- Utiliser des clés descriptives au lieu d'identifiants numériques. Des clés comme
inventory_fullsont auto-documentés et rendent la maintenance plus facile quemsg_042. - Utilisez toujours des espaces réservés de format (
%s,%d) pour les valeurs dynamiques au lieu de la concaténation de chaînes. Différentes langues ont des ordres de mots différents, les valeurs doivent donc pouvoir être insérées à différentes positions. - Inclure des commentaires contextuels dans tes fichiers de paramètres régionaux afin que les traducteurs comprennent où chaque chaîne est affichée et ce que représentent les arguments de format.
- Testez avec de longues chaînes. Le texte allemand est généralement 30 % plus long que l’anglais. Assurez-tu que tes éléments d'interface utilisateur peuvent gérer des traductions plus longues sans casser la mise en page.
- Ne codez jamais en dur les chaînes destinées à l’utilisateur. Chaque notification, étiquette de menu, texte d'aide et message d'erreur doit passer par le
L()fonction, même si tu ne prenez initialement en charge qu’une seule langue. - Fournir une commande de langue comme
/lang deafin que les joueurs puissent remplacer la langue détectée automatiquement à tout moment.
