feat: localization

This commit is contained in:
Vati
2026-04-09 01:46:07 +04:00
parent 5797e0f15f
commit c1ff6ee4be
14 changed files with 453 additions and 180 deletions
+29 -28
View File
@@ -2,6 +2,7 @@
-- Sets up all modules and auto-save system
local addon = Guda
local L = Guda_L
local Main = {}
addon.Modules.Main = Main
@@ -10,7 +11,7 @@ addon.Modules.Main = Main
function Main:Initialize()
-- Wait for PLAYER_LOGIN to ensure saved variables are loaded
addon.Modules.Events:OnPlayerLogin(function()
addon:Print("Initializing...")
addon:Print(L["Initializing..."])
-- Initialize database
addon.Modules.DB:Initialize()
@@ -38,7 +39,7 @@ function Main:Initialize()
end
-- Initialize UI
addon:Print("Initializing UI...")
addon:Print(L["Initializing UI..."])
addon.Modules.BagFrame:Initialize()
addon.Modules.BankFrame:Initialize()
addon.Modules.MailboxFrame:Initialize()
@@ -100,7 +101,7 @@ function Main:Initialize()
end
addon:Debug("Initialization complete")
addon:Print("Ready! Type /guda to open bags")
addon:Print(L["Ready! Type /guda to open bags"])
end, "Main")
end
@@ -135,24 +136,24 @@ function Main:SetupSlashCommands()
elseif msg == "debug" then
-- Toggle debug
addon.DEBUG = not addon.DEBUG
addon:Print("Debug mode: %s", addon.DEBUG and "ON" or "OFF")
addon:Print(L["Debug mode: %s"], addon.DEBUG and L["ON"] or L["OFF"])
elseif msg == "debugsort" then
-- Toggle debug sort (verbose sorting output)
addon.DEBUG_SORT = not addon.DEBUG_SORT
addon:Print("Debug sort mode: %s", addon.DEBUG_SORT and "ON" or "OFF")
addon:Print(L["Debug sort mode: %s"], addon.DEBUG_SORT and L["ON"] or L["OFF"])
elseif msg == "debugcat" then
-- Toggle debug category view (for troubleshooting layout issues)
addon.DEBUG_CATEGORY = not addon.DEBUG_CATEGORY
addon:Print("Debug category mode: %s", addon.DEBUG_CATEGORY and "ON" or "OFF")
addon:Print(L["Debug category mode: %s"], addon.DEBUG_CATEGORY and L["ON"] or L["OFF"])
elseif msg == "quest" then
-- Toggle quest bar
local show = not addon.Modules.DB:GetSetting("showQuestBar")
addon.Modules.DB:SetSetting("showQuestBar", show)
addon.Modules.QuestItemBar:Update()
addon:Print("Quest bar: %s", show and "ON" or "OFF")
addon:Print(L["Quest bar: %s"], show and L["ON"] or L["OFF"])
elseif msg == "track" then
-- Toggle tracked items bar visibility
@@ -172,7 +173,7 @@ function Main:SetupSlashCommands()
elseif addon.Modules.SettingsPopup and addon.Modules.SettingsPopup.Toggle then
addon.Modules.SettingsPopup:Toggle()
else
addon:Print("Settings window not available")
addon:Print(L["Settings window not available"])
end
elseif msg == "cleanup" then
@@ -184,7 +185,7 @@ function Main:SetupSlashCommands()
if addon.Modules.Utils and addon.Modules.Utils.PrintPerformanceStats then
addon.Modules.Utils:PrintPerformanceStats()
else
addon:Print("Performance stats not available")
addon:Print(L["Performance stats not available"])
end
-- Also show category cache stats
if addon.Modules.CategoryManager and addon.Modules.CategoryManager.GetCacheStats then
@@ -215,7 +216,7 @@ function Main:SetupSlashCommands()
-- Reset performance statistics
if addon.Modules.Utils and addon.Modules.Utils.ResetPerformanceStats then
addon.Modules.Utils:ResetPerformanceStats()
addon:Print("Performance stats reset")
addon:Print(L["Performance stats reset"])
end
-- Also clear category cache
if addon.Modules.CategoryManager and addon.Modules.CategoryManager.ClearCache then
@@ -235,35 +236,35 @@ function Main:SetupSlashCommands()
local bagFrame = getglobal("Guda_BagFrame")
local bankFrame = getglobal("Guda_BankFrame")
if (bagFrame and bagFrame:IsShown()) or (bankFrame and bankFrame:IsShown()) then
addon:Print("Cannot reset pool while bag/bank frames are open. Close them first.")
addon:Print(L["Cannot reset pool while bag/bank frames are open. Close them first."])
else
if Guda_ResetButtonPool then
Guda_ResetButtonPool()
addon:Print("Button pool reset. Pool is now empty.")
addon:Print(L["Button pool reset. Pool is now empty."])
else
addon:Print("Button pool reset function not available.")
addon:Print(L["Button pool reset function not available."])
end
end
elseif msg == "help" then
-- Show help
addon:Print("Commands:")
addon:Print("/guda - Toggle bags")
addon:Print("/guda bank - Toggle bank")
addon:Print("/guda mail - Toggle mailbox")
addon:Print("/guda settings - Open settings")
addon:Print("/guda sort - Sort bags")
addon:Print("/guda sortbank - Sort bank")
addon:Print("/guda track - Toggle item tracking")
addon:Print("/guda debug - Toggle debug mode")
addon:Print("/guda debugsort - Toggle sort debug output")
addon:Print("/guda cleanup - Remove old characters")
addon:Print("/guda perf - Show performance stats")
addon:Print("/guda perfreset - Reset performance stats")
addon:Print("/guda poolreset - Reset button pool (debug)")
addon:Print(L["Commands:"])
addon:Print(L["/guda - Toggle bags"])
addon:Print(L["/guda bank - Toggle bank"])
addon:Print(L["/guda mail - Toggle mailbox"])
addon:Print(L["/guda settings - Open settings"])
addon:Print(L["/guda sort - Sort bags"])
addon:Print(L["/guda sortbank - Sort bank"])
addon:Print(L["/guda track - Toggle item tracking"])
addon:Print(L["/guda debug - Toggle debug mode"])
addon:Print(L["/guda debugsort - Toggle sort debug output"])
addon:Print(L["/guda cleanup - Remove old characters"])
addon:Print(L["/guda perf - Show performance stats"])
addon:Print(L["/guda perfreset - Reset performance stats"])
addon:Print(L["/guda poolreset - Reset button pool (debug)"])
else
addon:Print("Unknown command. Type /guda help for commands")
addon:Print(L["Unknown command. Type /guda help for commands"])
end
end
+1 -1
View File
@@ -100,7 +100,7 @@ function SharedData:Initialize()
end
if importCount > 0 then
addon:Print("Imported " .. importCount .. " character(s) from other accounts")
addon:Print(format(Guda_L["Imported %d character(s) from other accounts"], importCount))
end
-- Clean up the global to free memory
+11 -11
View File
@@ -325,16 +325,16 @@ function Tooltip:AddInventoryInfo(tooltip, link)
tooltip:AddLine(" ")
-- Inventory label in exact bag frame title color
tooltip:AddLine("|cFFFFD200Inventory|r")
tooltip:AddLine("|cFFFFD200" .. Guda_L["Inventory"] .. "|r")
-- Total line with cyan label and white count (reuse module-level table)
local totalText = "|cFF00FFFFTotal|r: |cFFFFFFFF" .. totalCount .. "|r"
local totalText = "|cFF00FFFF" .. Guda_L["Total"] .. "|r: |cFFFFFFFF" .. totalCount .. "|r"
local breakdownParts = _breakdownParts
local bpIndex = 0
if totalBags > 0 then bpIndex = bpIndex + 1; breakdownParts[bpIndex] = "|cFF00FFFFBags|r: |cFFFFFFFF" .. totalBags .. "|r" end
if totalBank > 0 then bpIndex = bpIndex + 1; breakdownParts[bpIndex] = "|cFF00FFFFBank|r: |cFFFFFFFF" .. totalBank .. "|r" end
if totalMail > 0 then bpIndex = bpIndex + 1; breakdownParts[bpIndex] = "|cFF00FFFFMail|r: |cFFFFFFFF" .. totalMail .. "|r" end
if totalEquipped > 0 then bpIndex = bpIndex + 1; breakdownParts[bpIndex] = "|cFF00FFFFEquipped|r: |cFFFFFFFF" .. totalEquipped .. "|r" end
if totalBags > 0 then bpIndex = bpIndex + 1; breakdownParts[bpIndex] = "|cFF00FFFF" .. Guda_L["Bags"] .. "|r: |cFFFFFFFF" .. totalBags .. "|r" end
if totalBank > 0 then bpIndex = bpIndex + 1; breakdownParts[bpIndex] = "|cFF00FFFF" .. Guda_L["Bank"] .. "|r: |cFFFFFFFF" .. totalBank .. "|r" end
if totalMail > 0 then bpIndex = bpIndex + 1; breakdownParts[bpIndex] = "|cFF00FFFF" .. Guda_L["Mail"] .. "|r: |cFFFFFFFF" .. totalMail .. "|r" end
if totalEquipped > 0 then bpIndex = bpIndex + 1; breakdownParts[bpIndex] = "|cFF00FFFF" .. Guda_L["Equipped"] .. "|r: |cFFFFFFFF" .. totalEquipped .. "|r" end
for i = bpIndex + 1, table.getn(breakdownParts) do breakdownParts[i] = nil end
local breakdownText = ""
@@ -356,23 +356,23 @@ function Tooltip:AddInventoryInfo(tooltip, link)
local sharedSeparatorShown = false
for _, charInfo in ipairs(characterCounts) do
if charInfo.isShared and not sharedSeparatorShown then
tooltip:AddLine("|cFF80C0FFOther Accounts|r")
tooltip:AddLine("|cFF80C0FF" .. Guda_L["Other Accounts"] .. "|r")
sharedSeparatorShown = true
end
local r, g, b = GetClassColor(charInfo.classToken)
local pIndex = 0
if charInfo.bagCount > 0 then
pIndex = pIndex + 1; parts[pIndex] = "|cFF00FFFFBags|r: |cFFFFFFFF" .. charInfo.bagCount .. "|r"
pIndex = pIndex + 1; parts[pIndex] = "|cFF00FFFF" .. Guda_L["Bags"] .. "|r: |cFFFFFFFF" .. charInfo.bagCount .. "|r"
end
if charInfo.bankCount > 0 then
pIndex = pIndex + 1; parts[pIndex] = "|cFF00FFFFBank|r: |cFFFFFFFF" .. charInfo.bankCount .. "|r"
pIndex = pIndex + 1; parts[pIndex] = "|cFF00FFFF" .. Guda_L["Bank"] .. "|r: |cFFFFFFFF" .. charInfo.bankCount .. "|r"
end
if charInfo.mailCount > 0 then
pIndex = pIndex + 1; parts[pIndex] = "|cFF00FFFFMail|r: |cFFFFFFFF" .. charInfo.mailCount .. "|r"
pIndex = pIndex + 1; parts[pIndex] = "|cFF00FFFF" .. Guda_L["Mail"] .. "|r: |cFFFFFFFF" .. charInfo.mailCount .. "|r"
end
if charInfo.equippedCount > 0 then
pIndex = pIndex + 1; parts[pIndex] = "|cFF00FFFFEquipped|r: |cFFFFFFFF" .. charInfo.equippedCount .. "|r"
pIndex = pIndex + 1; parts[pIndex] = "|cFF00FFFF" .. Guda_L["Equipped"] .. "|r: |cFFFFFFFF" .. charInfo.equippedCount .. "|r"
end
for i = pIndex + 1, table.getn(parts) do parts[i] = nil end
+88 -31
View File
@@ -291,12 +291,12 @@ end
-- Print current performance statistics (for debugging)
function Utils:PrintPerformanceStats()
local stats = self:GetPerformanceStats()
addon:Print("=== Guda Performance Stats ===")
addon:Print("Frame Budget: %.0fms", stats.frameBudget * 1000)
addon:Print("Last Update: %.1fms", stats.lastUpdateDuration * 1000)
addon:Print(Guda_L["=== Guda Performance Stats ==="])
addon:Print(Guda_L["Frame Budget: %.0fms"], stats.frameBudget * 1000)
addon:Print(Guda_L["Last Update: %.1fms"], stats.lastUpdateDuration * 1000)
addon:Print("Avg Update: %.1fms", stats.averageUpdateDuration * 1000)
addon:Print("Total Updates: %d", stats.totalUpdates)
addon:Print("Budget Exceeded: %d times", stats.budgetExceededCount)
addon:Print(Guda_L["Total Updates: %d"], stats.totalUpdates)
addon:Print(Guda_L["Budget Exceeded: %d times"], stats.budgetExceededCount)
addon:Print("Work Queue: %d items", stats.workQueueSize)
end
@@ -987,6 +987,55 @@ end
-- Returns: "soul", "herb", "enchant", "quiver", "ammo", or nil
-- This is the consolidated bag type detection function with tooltip fallback
-- =============================================================================
-- Locale-independent type resolution
-- =============================================================================
-- WoW's GetItemInfo returns LOCALIZED type/subtype strings ("Quiver" on enUS,
-- "箭袋" on zhCN, etc.). We resolve the local-client strings at runtime by
-- querying a few reference itemIDs that exist in every WoW 1.12 client, so
-- the rest of the code can match against the local language without hardcoding.
local localizedBagTypes = {} -- lowercased localized subType -> "quiver"/"ammo"/...
local localizedProjectiles = {} -- localized subType (any case) -> "arrow"/"bullet"
local localizedTypesResolved = false
-- Reference itemIDs guaranteed to exist in every WoW 1.12 client.
-- Soul/herb/enchant bag IDs are intentionally omitted because they vary per
-- server (TurtleWoW custom items, etc.) — those still use the English fallback.
local BAG_REFERENCE_ITEMS = {
[2101] = "quiver", -- Light Quiver
[2102] = "ammo", -- Small Ammo Pouch
}
local PROJECTILE_REFERENCE_ITEMS = {
[2512] = "arrow", -- Rough Arrow
[2516] = "bullet", -- Light Shot
}
local function ResolveLocalizedTypes()
if localizedTypesResolved then return end
local allResolved = true
for itemID, key in pairs(BAG_REFERENCE_ITEMS) do
-- Position 6 in TurtleWoW GetItemInfo signature is itemType (subType)
local _, _, _, _, _, itemType = GetItemInfo(itemID)
if itemType then
localizedBagTypes[string.lower(itemType)] = key
else
allResolved = false
end
end
for itemID, key in pairs(PROJECTILE_REFERENCE_ITEMS) do
local _, _, _, _, _, itemType = GetItemInfo(itemID)
if itemType then
localizedProjectiles[itemType] = key
else
allResolved = false
end
end
if allResolved then localizedTypesResolved = true end
end
function Utils:GetSpecializedBagType(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
@@ -1012,6 +1061,12 @@ function Utils:GetSpecializedBagType(bagID)
if itemType then
local typeLower = string.lower(itemType)
-- Locale-independent: match against resolved local-client strings
ResolveLocalizedTypes()
if localizedBagTypes[typeLower] then
return localizedBagTypes[typeLower]
end
if string.find(typeLower, "soul bag") or string.find(typeLower, "soul pouch") then
return "soul"
end
@@ -1176,17 +1231,28 @@ function Utils:GetConsumableRestoreTag(bagID, slotID, itemLink)
return tag
end
-- Check if item is Arrow or Bullet (for Quiver routing)
-- Check if item is Arrow or Bullet (for Quiver routing).
-- Locale-independent: also matches against the local client's translated
-- subtype strings, resolved from reference vanilla items at runtime.
function Utils:IsArrowOrBullet(itemType)
if not itemType then return false end
-- Exact matching only
return itemType == "Arrow" or itemType == "Bullet"
if itemType == "Arrow" or itemType == "Bullet" then return true end
ResolveLocalizedTypes()
return localizedProjectiles[itemType] ~= nil
end
-- Check if item is Ammo (any type - for Ammo Pouch)
function Utils:IsAmmo(itemType)
if not itemType then return false end
return itemType == "Arrow" or itemType == "Bullet"
return self:IsArrowOrBullet(itemType)
end
-- Returns "arrow", "bullet", or nil for the given item subtype string.
function Utils:GetProjectileKind(itemType)
if not itemType then return nil end
if itemType == "Arrow" then return "arrow" end
if itemType == "Bullet" then return "bullet" end
ResolveLocalizedTypes()
return localizedProjectiles[itemType]
end
-- Get preferred container type for an item
@@ -1206,13 +1272,14 @@ function Utils:GetItemPreferredContainer(itemLink)
local itemName, _, itemRarity, itemLevel, itemCategory, itemType, itemStackCount, itemSubType, itemTexture = self:GetItemInfoSafe(itemID)
if not itemType then return nil end
-- Only route PROJECTILE category items that are specifically arrows or bullets
if itemCategory == "Projectile" then
if itemType == "Arrow" then
return "quiver"
elseif itemType == "Bullet" then
return "ammo"
end
-- Locale-independent projectile routing. Don't gate on itemCategory string
-- (it's localized); the projectile kind lookup already covers both English
-- and the local client's strings via runtime-resolved reference items.
local projectileKind = self:GetProjectileKind(itemType)
if projectileKind == "arrow" then
return "quiver"
elseif projectileKind == "bullet" then
return "ammo"
end
-- Route herbs to herb bags (robust: category/subtype OR texture pattern fallback)
@@ -1387,15 +1454,12 @@ function Utils:IsHerbItem(itemLink)
local name, _, quality, iLevel, itemCategory, itemType, itemStackCount, itemSubType, itemTexture = self:GetItemInfoSafe(itemID)
if itemCategory ~= "Trade Goods" then
return false
end
if not itemTexture then
return false
end
-- Normalize and check texture name
-- Texture path is locale-independent (English in every WoW client),
-- so we don't need to gate on the localized "Trade Goods" category.
local tex = string.lower(itemTexture)
tex = string.gsub(tex, "^interface\\\\icons\\\\", "")
@@ -1418,14 +1482,7 @@ function Utils:IsEnchantingItem(itemLink)
local name, _, quality, iLevel, itemCategory, itemType, itemStackCount, itemSubType, itemTexture = self:GetItemInfoSafe(itemID)
if itemCategory ~= "Trade Goods" then
-- Some private servers may report Enchanting directly as type
if itemType ~= "Enchanting" and itemSubType ~= "Enchanting" then
return false
end
end
-- If explicit Enchanting subtype/type, accept immediately
-- Explicit English subtype (rare but possible on some servers)
if itemType == "Enchanting" or itemSubType == "Enchanting" then
return true
end
@@ -1434,7 +1491,7 @@ function Utils:IsEnchantingItem(itemLink)
return false
end
-- Normalize and check texture name
-- Texture path is locale-independent — primary signal for non-English clients.
local tex = string.lower(itemTexture)
tex = string.gsub(tex, "^interface\\\\icons\\\\", "")