264 lines
8.5 KiB
Lua
264 lines
8.5 KiB
Lua
---@class l10n
|
|
---@field continentLookup table
|
|
---@field zoneLookup table
|
|
---@field zoneCategoryLookup table
|
|
---@field questCategoryLookup table
|
|
local l10n = QuestieLoader:CreateModule("l10n")
|
|
local _l10n = {}
|
|
l10n.translations = {}
|
|
|
|
l10n.itemLookup = {}
|
|
l10n.npcNameLookup = {}
|
|
l10n.objectNameLookup = {}
|
|
l10n.objectLookup = {}
|
|
l10n.questLookup = {}
|
|
|
|
---@type QuestieDB
|
|
local QuestieDB = QuestieLoader:ImportModule("QuestieDB")
|
|
|
|
local locale = 'enUS'
|
|
local supportedLocals = {
|
|
['enUS'] = true,
|
|
['esES'] = true,
|
|
['esMX'] = true,
|
|
['ptBR'] = true,
|
|
['frFR'] = true,
|
|
['deDE'] = true,
|
|
['ruRU'] = true,
|
|
['zhCN'] = true,
|
|
['zhTW'] = true,
|
|
['koKR'] = true,
|
|
}
|
|
|
|
function l10n:InitializeLocaleOverride()
|
|
local overridingLocale = QUESTIE_LOCALES_OVERRIDE.locale
|
|
supportedLocals[overridingLocale] = true
|
|
l10n.itemLookup[overridingLocale] = QUESTIE_LOCALES_OVERRIDE.itemLookup
|
|
l10n.questLookup[overridingLocale] = QUESTIE_LOCALES_OVERRIDE.questLookup
|
|
l10n.npcNameLookup[overridingLocale] = QUESTIE_LOCALES_OVERRIDE.npcNameLookup
|
|
l10n.objectLookup[overridingLocale] = QUESTIE_LOCALES_OVERRIDE.objectLookup
|
|
|
|
for id, _ in pairs(l10n.translations) do
|
|
if QUESTIE_LOCALES_OVERRIDE.translations[id] ~= nil then
|
|
l10n.translations[id][overridingLocale] = QUESTIE_LOCALES_OVERRIDE.translations[id]
|
|
else
|
|
l10n.translations[id][overridingLocale] = false
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param zoneName string
|
|
---@return number
|
|
function l10n:GetAreaIdByLocalName(zoneName)
|
|
if not zoneName or zoneName == "" then return 0 end
|
|
for _, zoneTable in pairs(l10n.zoneLookup) do
|
|
for areaId, name in pairs(zoneTable) do
|
|
if name == zoneName then return areaId end
|
|
end
|
|
end
|
|
return 0
|
|
end
|
|
|
|
---@param areaId number
|
|
---@return string
|
|
function l10n:GetLocalNameByAreaId(areaId)
|
|
if not areaId or areaId <= 0 then return l10n("Unknown Zone") end
|
|
for _, zoneTable in pairs(l10n.zoneLookup) do
|
|
if zoneTable[areaId] then
|
|
return zoneTable[areaId]
|
|
end
|
|
end
|
|
return l10n("Unknown Zone")
|
|
end
|
|
|
|
---@param areaId number
|
|
---@return number
|
|
function l10n:GetContinentIdByAreaId(areaId)
|
|
if not areaId or areaId <= 0 then return 0 end
|
|
for continentId, zoneTable in pairs(l10n.zoneLookup) do
|
|
if zoneTable[areaId] then
|
|
return continentId
|
|
end
|
|
end
|
|
return 0
|
|
end
|
|
|
|
function l10n:Initialize()
|
|
-- Load item locales
|
|
if l10n.itemLookup and l10n.itemLookup[locale] then
|
|
for id, name in pairs(l10n.itemLookup[locale]) do
|
|
if QuestieDB.itemData[id] and name then
|
|
QuestieDB.itemData[id][QuestieDB.itemKeys.name] = name
|
|
end
|
|
end
|
|
end
|
|
|
|
-- data is {<questName>, {<questDescription>,...}, {<questObjective>,...}}
|
|
-- Load quest locales
|
|
if l10n.questLookup and l10n.questLookup[locale] then
|
|
for id, data in pairs(l10n.questLookup[locale]) do
|
|
if QuestieDB.questData[id] then
|
|
if data[1] then
|
|
QuestieDB.questData[id][QuestieDB.questKeys.name] = data[1]
|
|
end
|
|
-- TODO add details text to questDB.lua (data[2])
|
|
if data[3] then
|
|
-- needs to be saved as a table for tooltips to have lines
|
|
if type(data[3]) == "string" then
|
|
QuestieDB.questData[id][QuestieDB.questKeys.objectivesText] = {data[3]}
|
|
else
|
|
QuestieDB.questData[id][QuestieDB.questKeys.objectivesText] = data[3]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Load NPC locales
|
|
if l10n.npcNameLookup and l10n.npcNameLookup[locale] then
|
|
for id, data in pairs(l10n.npcNameLookup[locale]) do
|
|
if QuestieDB.npcData[id] and data then
|
|
if type(data) == "string" then
|
|
QuestieDB.npcData[id][QuestieDB.npcKeys.name] = data
|
|
else
|
|
QuestieDB.npcData[id][QuestieDB.npcKeys.name] = data[1]
|
|
QuestieDB.npcData[id][QuestieDB.npcKeys.subName] = data[2]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Load object locales
|
|
if l10n.objectLookup and l10n.objectLookup[locale] then
|
|
for id, name in pairs(l10n.objectLookup[locale]) do
|
|
if QuestieDB.objectData[id] and name then
|
|
QuestieDB.objectData[id][QuestieDB.objectKeys.name] = name
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--Must be run in a coroutine as it yields
|
|
function l10n:PostBoot()
|
|
|
|
local count = 0
|
|
-- Create {['name'] = {ID, },} table for lookup of possible object IDs by name
|
|
if not QuestieDB.ObjectPointers then return end
|
|
for id in pairs(QuestieDB.ObjectPointers) do
|
|
local name = QuestieDB.QueryObjectSingle(id, "name")
|
|
if name then -- We (meaning me, BreakBB) introduced Fake IDs for objects to show additional locations, so we need to check this
|
|
local entry = l10n.objectNameLookup[name]
|
|
if not entry then
|
|
l10n.objectNameLookup[name] = { id }
|
|
else
|
|
entry[#entry+1] = id
|
|
end
|
|
end
|
|
|
|
if count > 300 then
|
|
count = 0
|
|
coroutine.yield()
|
|
end
|
|
count = count + 1
|
|
end
|
|
end
|
|
|
|
local format, unpack, tostring = string.format, unpack, tostring
|
|
|
|
-- Safe wrapper around string.format that never errors. string.format throws
|
|
-- "invalid option 'X' to 'format'" when the format string contains a stray %
|
|
-- followed by a non-format character, or when the caller's args don't match
|
|
-- the placeholders. l10n callers pass user-controlled data (quest names,
|
|
-- npc names, error messages from xpcall) that can contain %X edge cases;
|
|
-- crashing the chat with a Lua error for a missing translation is much worse
|
|
-- than showing a slightly ugly fallback string. See user report: 8x
|
|
-- l10n.lua:181 "invalid option in 'format'" during initial quest log sync.
|
|
local function safeFormat(fmt, args)
|
|
if not args or #args == 0 then
|
|
-- No args, so just return the format string as-is (no format parsing
|
|
-- needed). This handles trailing-% edge cases too.
|
|
return fmt
|
|
end
|
|
local ok, result = pcall(format, fmt, unpack(args))
|
|
if ok then return result end
|
|
-- Fallback: concatenate the format string with the args separated by spaces.
|
|
-- This loses the original formatting intent but never crashes the user.
|
|
return fmt .. " " .. table.concat(args, " ")
|
|
end
|
|
|
|
function _l10n:translate(key, ...)
|
|
if key == nil then
|
|
return ""
|
|
end
|
|
key = tostring(key)
|
|
local args = {...}
|
|
|
|
for i, v in ipairs(args) do
|
|
args[i] = tostring(v);
|
|
end
|
|
|
|
local translationEntry = l10n.translations[key]
|
|
if not translationEntry then
|
|
if (Questie.db.profile.debugEnabled) then Questie:Debug(Questie.DEBUG_ELEVATED, "ERROR: Translations for '" .. tostring(key) .. "' are missing completely!") end
|
|
return safeFormat(key, args)
|
|
end
|
|
|
|
local translationValue = translationEntry[locale]
|
|
if (not translationValue) then
|
|
if (Questie.db.profile.debugEnabled) then Questie:Debug(Questie.DEBUG_ELEVATED, "ERROR: Translations for '" .. tostring(key) .. "' are missing the entry for language" , locale, "!") end
|
|
return safeFormat(key, args)
|
|
end
|
|
|
|
if translationValue == true then
|
|
-- Fallback to enUS which is the key
|
|
return safeFormat(key, args)
|
|
end
|
|
|
|
if type(translationValue) ~= "string" then
|
|
if (Questie.db.profile.debugEnabled) then Questie:Debug(Questie.DEBUG_ELEVATED, "ERROR: Translation for '" .. tostring(key) .. "' is not a string!") end
|
|
return safeFormat(key, args)
|
|
end
|
|
|
|
if #args == 0 then
|
|
return translationValue
|
|
end
|
|
|
|
return safeFormat(translationValue, args)
|
|
end
|
|
|
|
setmetatable(l10n, { __call = function(_, ...) return _l10n:translate(...) end})
|
|
|
|
function _l10n:GetFallbackLocale(lang)
|
|
if (not lang) then
|
|
return 'enUS'
|
|
end
|
|
|
|
if supportedLocals[lang] then
|
|
return lang
|
|
elseif lang == 'enGB' then
|
|
return 'enUS'
|
|
elseif lang == 'enCN' then
|
|
return 'zhCN'
|
|
elseif lang == 'enTW' then
|
|
return 'zhTW'
|
|
elseif lang == 'esMX' then
|
|
return 'esES'
|
|
elseif lang == 'ptPT' then
|
|
return 'ptBR'
|
|
else
|
|
return 'enUS'
|
|
end
|
|
end
|
|
|
|
function l10n:SetUILocale(lang)
|
|
if lang then
|
|
locale = _l10n:GetFallbackLocale(lang)
|
|
else
|
|
locale = _l10n:GetFallbackLocale(GetLocale())
|
|
end
|
|
end
|
|
|
|
function l10n:GetUILocale()
|
|
return locale
|
|
end
|