From 567ee506a190c8e531ca163ba1d73d6bfac444e9 Mon Sep 17 00:00:00 2001 From: ASTROWwwW Date: Tue, 21 Jul 2026 16:50:25 +0200 Subject: [PATCH] feat(es_extended): fall back to English for missing locale keys When a key is absent from the active locale, Translate now borrows the English string instead of returning a placeholder. Locale loading is factored out so the target and English tables are lazily cached. Toggle with the esx:localeFallback convar (on by default) or per-resource Config.LocaleFallback. --- [core]/es_extended/locale.lua | 44 +++++++++++++++++------ [core]/es_extended/shared/config/main.lua | 5 +++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/[core]/es_extended/locale.lua b/[core]/es_extended/locale.lua index 3f059b62..8b909f30 100644 --- a/[core]/es_extended/locale.lua +++ b/[core]/es_extended/locale.lua @@ -1,20 +1,35 @@ Locales = {} +--- English fallback for missing keys. locale.lua runs in every resource VM, so +--- a resource may opt out through its own Config.LocaleFallback; otherwise the +--- shared "esx:localeFallback" convar decides (enabled by default). +local function localeFallbackEnabled() + if Config.LocaleFallback ~= nil then + return Config.LocaleFallback + end + + return GetConvar("esx:localeFallback", "true") ~= "false" +end + +--- Lazily load and cache a locale file, false if it cannot be read +local function loadLocale(locale) + if Locales[locale] == nil then + local success, result = pcall(function() + return assert(load(LoadResourceFile(GetCurrentResourceName(), ("locales/%s.lua"):format(locale))))() + end) + + Locales[locale] = success and result or false + end + + return Locales[locale] +end + function Translate(str, ...) -- Translate string if not str then error(("Resource ^5%s^1 You did not specify a parameter for the Translate function or the value is nil!"):format(GetInvokingResource() or GetCurrentResourceName())) end - --- Load the locale file if it hasn't been loaded yet - if Locales[Config.Locale] == nil then - local success, result = pcall(function() - return assert(load(LoadResourceFile(GetCurrentResourceName(), ("locales/%s.lua"):format(Config.Locale))))() - end) - - Locales[Config.Locale] = success and result or false - end - - local translations = Locales[Config.Locale] + local translations = loadLocale(Config.Locale) if not translations then if Config.Locale == "en" then return "Locale [en] does not exist" @@ -29,6 +44,15 @@ function Translate(str, ...) -- Translate string return translations[str]:format(...) end + -- The locale exists but is missing this key: borrow the English string + -- rather than surfacing a placeholder. + if Config.Locale ~= "en" and localeFallbackEnabled() then + local english = loadLocale("en") + if english and english[str] then + return english[str]:format(...) + end + end + return ("Translation [%s][%s] does not exist"):format(Config.Locale, str) end diff --git a/[core]/es_extended/shared/config/main.lua b/[core]/es_extended/shared/config/main.lua index 0bb55117..a1194e45 100644 --- a/[core]/es_extended/shared/config/main.lua +++ b/[core]/es_extended/shared/config/main.lua @@ -4,6 +4,11 @@ local txAdminLocale = GetConvar("txAdmin-locale", "en") local esxLocale = GetConvar("esx:locale", "invalid") Config.Locale = (esxLocale ~= "invalid") and esxLocale or (txAdminLocale ~= "custom" and txAdminLocale) or "en" +-- When a key is missing from the active locale, fall back to the English string +-- instead of returning a placeholder. Set convar "esx:localeFallback" to "false" +-- to keep the placeholder (useful when authoring a new translation). +Config.LocaleFallback = GetConvar("esx:localeFallback", "true") ~= "false" + -- For ox inventory, this will automatically be adjusted, do not change! For other inventories, leave as false unless specifically instructed to change. Config.CustomInventory = false