diff --git a/Libs/DF/cooltip.lua b/Libs/DF/cooltip.lua index 03b5a076..58981be8 100644 --- a/Libs/DF/cooltip.lua +++ b/Libs/DF/cooltip.lua @@ -15,7 +15,7 @@ local max = math.max --api locals local PixelUtil = PixelUtil or DFPixelUtil -local version = 14 +local version = 16 local CONST_MENU_TYPE_MAINMENU = "main" local CONST_MENU_TYPE_SUBMENU = "sub" @@ -594,12 +594,12 @@ function DF:CreateCoolTip() statusbar.subMenuArrow:SetTexture("Interface\\CHATFRAME\\ChatFrameExpandArrow") statusbar.subMenuArrow:Hide() - statusbar.leftText = statusbar:CreateFontString("$parent_LeftText", "OVERLAY", "GameTooltipHeaderText") + statusbar.leftText = statusbar:CreateFontString("$parent_LeftText", "overlay", "GameFontNormal") statusbar.leftText:SetJustifyH("LEFT") statusbar.leftText:SetPoint("LEFT", statusbar.leftIcon, "RIGHT", 3, 0) DF:SetFontSize(statusbar.leftText, 10) - statusbar.rightText = statusbar:CreateFontString("$parent_TextRight", "OVERLAY", "GameTooltipHeaderText") + statusbar.rightText = statusbar:CreateFontString("$parent_TextRight", "overlay", "GameFontNormal") statusbar.rightText:SetJustifyH("RIGHT") statusbar.rightText:SetPoint("RIGHT", statusbar.rightIcon, "LEFT", -3, 0) DF:SetFontSize(statusbar.leftText, 10) @@ -940,7 +940,24 @@ function DF:CreateCoolTip() --set text if (leftTextSettings) then + --font settings + local languageId = DF.Language.DetectLanguageId(leftTextSettings[1]) + if (languageId ~= menuButton.leftText.languageId) then + local newFont = DF.Language.GetFontForLanguageID(languageId) + DF:SetFontFace(menuButton.leftText, newFont) + menuButton.leftText.languageId = languageId + + local bIsLatinAlphabet = DF:IsLatinLanguage(languageId) + if (not bIsLatinAlphabet) then + menuButton.leftText.requiredFont = newFont + gameCooltip.OptionsTable.TextFont = newFont + else + menuButton.leftText.requiredFont = nil + end + end + menuButton.leftText:SetText(leftTextSettings[1]) + local r, g, b, a = leftTextSettings[2], leftTextSettings[3], leftTextSettings[4], leftTextSettings[5] if (r == 0 and g == 0 and b == 0 and a == 0) then @@ -978,9 +995,10 @@ function DF:CreateCoolTip() local _, size, flags = menuButton.leftText:GetFont() flags = leftTextSettings[8] or gameCooltip.OptionsTable.TextShadow or nil size = leftTextSettings[6] or gameCooltip.OptionsTable.TextSize or size - menuButton.leftText:SetFont(font, size, flags) + menuButton.leftText:SetFont(menuButton.leftText.requiredFont or font, size, flags) end + --font settings elseif (leftTextSettings[7]) then if (_G[leftTextSettings[7]]) then menuButton.leftText:SetFontObject(leftTextSettings[7]) @@ -1000,7 +1018,7 @@ function DF:CreateCoolTip() menuButton.leftText:SetFont(gameCooltip.defaultFont, leftTextSettings[6] or gameCooltip.OptionsTable.TextSize or 10, leftTextSettings[8] or gameCooltip.OptionsTable.TextShadow) end - local heightMod = gameCooltip.OptionsTable.TextHeightMod or 0 + local heightMod = gameCooltip.OptionsTable.TextHeightMod or 0 menuButton.leftText:SetPoint("center", menuButton.leftIcon, "center", 0, 0 + heightMod) menuButton.leftText:SetPoint("left", menuButton.leftIcon, "right", 3, 0 + heightMod) else @@ -1008,7 +1026,23 @@ function DF:CreateCoolTip() end if (rightTextSettings) then + local languageId = DF.Language.DetectLanguageId(rightTextSettings[1]) + if (languageId ~= menuButton.rightText.languageId) then + local newFont = DF.Language.GetFontForLanguageID(languageId) + DF:SetFontFace(menuButton.rightText, newFont) + menuButton.rightText.languageId = languageId + + local bIsLatinAlphabet = DF:IsLatinLanguage(languageId) + if (not bIsLatinAlphabet) then + menuButton.rightText.requiredFont = newFont + gameCooltip.OptionsTable.TextFont = newFont + else + menuButton.rightText.requiredFont = nil + end + end + menuButton.rightText:SetText(rightTextSettings[1]) + local r, g, b, a = rightTextSettings[2], rightTextSettings[3], rightTextSettings[4], rightTextSettings[5] if (r == 0 and g == 0 and b == 0 and a == 0) then @@ -1043,7 +1077,7 @@ function DF:CreateCoolTip() local _, fontSize, fontFlags = menuButton.rightText:GetFont() fontFlags = rightTextSettings[8] or gameCooltip.OptionsTable.TextShadow or nil fontSize = rightTextSettings[6] or gameCooltip.OptionsTable.TextSize or fontSize - menuButton.rightText:SetFont(fontFace, fontSize, fontFlags) + menuButton.rightText:SetFont(menuButton.leftText.requiredFont or fontFace, fontSize, fontFlags) end elseif (rightTextSettings[7]) then @@ -3615,30 +3649,6 @@ function DF:CreateCoolTip() end) end - local cyrillic = "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯЁЂЃЄЅІЇЈЉЊЋЌЎЏҐабвгдежзийклмнопрстуфхцчшщъыьэюяёђѓєѕіїјљњћќўџґАаБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯя" - local latin = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - local chinese = "ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚ᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑ하ᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵ" - - local alphabetTable = {} - - for letter in latin:gmatch(".") do - alphabetTable[letter] = "enUS" - end - for letter in cyrillic:gmatch(".") do - alphabetTable[letter] = "ruRU" - end - for letter in chinese:gmatch(".") do - alphabetTable[letter] = "zhCN" - end - - function gameCooltip:DetectLanguageId(text) - for letter in text:gmatch(".") do - if (alphabetTable[letter]) then - return alphabetTable[letter] - end - end - end - return gameCooltip end diff --git a/Libs/DF/dropdown.lua b/Libs/DF/dropdown.lua index 2da300f4..d064dc32 100644 --- a/Libs/DF/dropdown.lua +++ b/Libs/DF/dropdown.lua @@ -37,6 +37,7 @@ DF:Mixin(DropDownMetaFunctions, DF.SetPointMixin) DF:Mixin(DropDownMetaFunctions, DF.FrameMixin) DF:Mixin(DropDownMetaFunctions, DF.TooltipHandlerMixin) DF:Mixin(DropDownMetaFunctions, DF.ScriptHookMixin) +DF:Mixin(DropDownMetaFunctions, DF.Language.LanguageMixin) ------------------------------------------------------------------------------------------------------------ --metatables @@ -492,7 +493,28 @@ function DropDownMetaFunctions:Selected(thisOption) self.last_select = thisOption self:NoOption(false) - self.label:SetText(thisOption.label) + local addonId = self.addonId + local languageId = thisOption.languageId + local phraseId = thisOption.phraseId + + local overrideFont + if (addonId) then + local thisLanguageId = languageId or DF.Language.GetLanguageIdForAddonId(addonId) + if (thisLanguageId) then + if (thisLanguageId ~= self.label.languageId) then + local newFont = DF.Language.GetFontForLanguageID(thisLanguageId) + self.label.languageId = thisLanguageId + overrideFont = newFont + end + end + end + + if (addonId and phraseId) then + self.label:SetText(DF.Language.GetText(addonId, phraseId)) + else + self.label:SetText(thisOption.label) + end + self.icon:SetTexture(thisOption.icon) if (thisOption.icon) then @@ -543,8 +565,12 @@ function DropDownMetaFunctions:Selected(thisOption) self.label:SetTextColor(1, 1, 1, 1) end - if (thisOption.font) then + if (overrideFont) then + self.label:SetFont(overrideFont, 10) + + elseif (thisOption.font) then self.label:SetFont(thisOption.font, 10) + else self.label:SetFont("GameFontHighlightSmall", 10) end @@ -648,7 +674,7 @@ end function DetailsFrameworkDropDownOnMouseDown(button, buttontype) local object = button.MyObject - --click to open + --~click to open if (not object.opened and not rawget(object, "lockdown")) then local optionsTable = DF:Dispatch(object.func, object) object.builtMenu = optionsTable @@ -734,12 +760,6 @@ function DetailsFrameworkDropDownOnMouseDown(button, buttontype) thisOptionFrame.icon:SetSize(thisOptionFrame:GetHeight()-6, thisOptionFrame:GetHeight()-6) end - if (thisOption.font) then - thisOptionFrame.label:SetFont(thisOption.font, 10.5) - else - thisOptionFrame.label:SetFont("GameFontHighlightSmall", 10.5) - end - if (thisOption.statusbar) then thisOptionFrame.statusbar:SetTexture(thisOption.statusbar) if (thisOption.statusbarcolor) then @@ -757,8 +777,35 @@ function DetailsFrameworkDropDownOnMouseDown(button, buttontype) thisOptionFrame.rightButton:Hide() end + local overrideFont + local languageId = thisOption.languageId + if (languageId) then + if (languageId ~= thisOptionFrame.label.languageId) then + local newFont = DF.Language.GetFontForLanguageID(languageId) + thisOptionFrame.label.languageId = languageId + overrideFont = newFont + end + else + languageId = DF.Language.DetectLanguageId(thisOption.label) + if (languageId ~= thisOptionFrame.label.languageId) then + local newFont = DF.Language.GetFontForLanguageID(languageId) + thisOptionFrame.label.languageId = languageId + overrideFont = newFont + end + end + thisOptionFrame.label:SetText(thisOption.label) + if (overrideFont) then + thisOptionFrame.label:SetFont(overrideFont, 10.5) + + elseif (thisOption.font) then + thisOptionFrame.label:SetFont(thisOption.font, 10.5) + + else + thisOptionFrame.label:SetFont("GameFontHighlightSmall", 10.5) + end + if (currentText and currentText == thisOption.label) then if (thisOption.icon) then selectedTexture:SetPoint("left", thisOptionFrame.icon, "left", -3, 0) @@ -859,10 +906,6 @@ function DetailsFrameworkDropDownOnMouseDown(button, buttontype) end object:Open() - - --scrollFrame:SetHeight(300) - --scrollChild:SetHeight(300) - --scrollBorder:SetHeight(300) else --clear menu end diff --git a/Libs/DF/fw.lua b/Libs/DF/fw.lua index 1e92033f..75b91aef 100644 --- a/Libs/DF/fw.lua +++ b/Libs/DF/fw.lua @@ -1,6 +1,6 @@ -local dversion = 415 +local dversion = 416 local major, minor = "DetailsFramework-1.0", dversion local DF, oldminor = LibStub:NewLibrary(major, minor) @@ -827,7 +827,7 @@ function DF:SetFontFace(fontString, fontface) end local _, size, flags = fontString:GetFont() - fontString:SetFont(fontface, size, flags) + return fontString:SetFont(fontface, size, flags) end ---get the FontString passed and set the font color @@ -1085,6 +1085,8 @@ function DF:TruncateText(fontString, maxWidth) fontString:SetText(text) end +---@param text string +---@return string function DF:CleanTruncateUTF8String(text) if type(text) == "string" and text ~= "" then local b1 = (#text > 0) and strbyte(strsub(text, #text, #text)) or nil @@ -1676,6 +1678,17 @@ end return widgetObject end + --get the description phrase from the language table or use the .desc or .deschraseid + local getDescPhraseText = function(languageTable, widgetTable) + local descPhraseId = languageTable and (languageTable[widgetTable.descPhraseId] or languageTable[widgetTable.desc]) + return descPhraseId or widgetTable.descPhraseId or widgetTable.desc or widgetTable.name or "-?-" + end + + local getNamePhraseText = function(languageTable, widgetTable, useColon) + local namePhrase = languageTable and (languageTable[widgetTable.namePhraseId] or languageTable[widgetTable.name]) + return namePhrase or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or widgetTable.name or "-?-" + end + --volatile menu can be called several times, each time all settings are reset and a new menu is built using the same widgets function DF:BuildMenuVolatile(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook) if (not parent.widget_list) then @@ -1753,7 +1766,8 @@ end local label = getMenuWidgetVolative(parent, "label", widgetIndexes) widgetCreated = label - label.text = (languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.get and widgetTable.get() or widgetTable.text) or (widgetTable.namePhraseId) or "" + local namePhrase = (languageTable and (languageTable[widgetTable.namePhraseId] or languageTable[widgetTable.name])) or (widgetTable.get and widgetTable.get()) or widgetTable.text or (widgetTable.namePhraseId) or "" + label.text = namePhrase label.color = widgetTable.color if (widgetTable.font) then @@ -1777,7 +1791,7 @@ end --dropdowns elseif (widgetTable.type == "select" or widgetTable.type == "dropdown") then - assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'select'") + assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get() not found in the widget table for 'select'") local dropdown = getMenuWidgetVolative(parent, "dropdown", widgetIndexes) widgetCreated = dropdown @@ -1786,11 +1800,13 @@ end dropdown:Select(widgetTable.get()) dropdown:SetTemplate(dropdownTemplate) - dropdown:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) + local descPhrase = getDescPhraseText(languageTable, widgetTable) + dropdown:SetTooltip(descPhrase) dropdown._get = widgetTable.get dropdown.widget_type = "select" - dropdown.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or "" + local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon) + dropdown.hasLabel.text = namePhrase dropdown.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) dropdown:ClearAllPoints() @@ -1828,7 +1844,8 @@ end switch:SetTemplate(switchTemplate) switch:SetAsCheckBox() --it's always a checkbox on volatile menu - switch:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) + local descPhrase = getDescPhraseText(languageTable, widgetTable) + switch:SetTooltip(descPhrase) switch._get = widgetTable.get switch.widget_type = "toggle" switch.OnSwitch = widgetTable.set @@ -1851,7 +1868,8 @@ end switch:SetHeight(widgetTable.height) end - switch.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or "" + local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon) + switch.hasLabel.text = namePhrase switch.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) switch:ClearAllPoints() @@ -1899,7 +1917,8 @@ end slider:SetTemplate(sliderTemplate) - slider:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) + local descPhrase = getDescPhraseText(languageTable, widgetTable) + slider:SetTooltip(descPhrase) slider._get = widgetTable.get slider.widget_type = "range" slider:SetHook("OnValueChange", widgetTable.set) @@ -1921,7 +1940,8 @@ end end end - slider.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or "" + local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon) + slider.hasLabel.text = namePhrase slider.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) slider:SetPoint("left", slider.hasLabel, "right", 2) @@ -1945,7 +1965,8 @@ end colorpick:SetTemplate(buttonTemplate) colorpick:SetSize(18, 18) - colorpick:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) + local descPhrase = getDescPhraseText(languageTable, widgetTable) + colorpick:SetTooltip(descPhrase) colorpick._get = widgetTable.get colorpick.widget_type = "color" @@ -1968,7 +1989,9 @@ end end local label = colorpick.hasLabel - label.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or "" + + local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon) + label.text = namePhrase label:SetTemplate(widgetTable.text_template or textTemplate) label:ClearAllPoints() @@ -2005,7 +2028,9 @@ end button.textcolor = textTemplate.color button.textfont = textTemplate.font button.textsize = textTemplate.size - button.text = (languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.name) or (widgetTable.namePhraseId) or "" + + local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon) + button.text = namePhrase if (widgetTable.inline) then if (latestInlineWidget) then @@ -2019,7 +2044,8 @@ end button:SetPoint(currentXOffset, currentYOffset) end - button:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) + local descPhrase = getDescPhraseText(languageTable, widgetTable) + button:SetTooltip(descPhrase) button.widget_type = "execute" --hook list @@ -2054,14 +2080,16 @@ end textentry:SetTemplate(widgetTable.template or widgetTable.button_template or buttonTemplate) textentry:SetSize(widgetTable.width or 120, widgetTable.height or 18) - textentry:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId)) + local descPhrase = getDescPhraseText(languageTable, widgetTable) + textentry:SetTooltip(descPhrase) textentry.text = widgetTable.get() textentry._get = widgetTable.get textentry.widget_type = "textentry" textentry:SetHook("OnEnterPressed", widgetTable.func or widgetTable.set) textentry:SetHook("OnEditFocusLost", widgetTable.func or widgetTable.set) - textentry.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or "" + local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon) + textentry.hasLabel.text = namePhrase textentry.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) textentry:SetPoint("left", textentry.hasLabel, "right", 2) textentry.hasLabel:SetPoint(currentXOffset, currentYOffset) @@ -2116,6 +2144,52 @@ end DF.RefreshUnsafeOptionsWidgets() end + local getDescripttionPhraseID = function(widgetTable, languageAddonId, languageTable) + if (widgetTable.descPhraseId) then + return widgetTable.descPhraseId + end + + if (not languageTable) then + return + end + + local hasValue = DF.Language.DoesPhraseIDExistsInDefaultLanguage(languageAddonId, widgetTable.desc) + if (not hasValue) then + return + end + + return widgetTable.desc + end + + local getNamePhraseID = function(widgetTable, languageAddonId, languageTable) + if (widgetTable.namePhraseId) then + return widgetTable.namePhraseId + end + + if (not languageTable) then + return + end + + local keyName = widgetTable.name + + if (widgetTable.type == "label" and widgetTable.get) then + local key = widgetTable.get() + if (key and type(key) == "string") then + keyName = key + end + end + + --embed key is when the phraseId is inside a string surounded by @ + local embedPhraseId = keyName:match("@(.-)@") + + local hasValue = DF.Language.DoesPhraseIDExistsInDefaultLanguage(languageAddonId, embedPhraseId or keyName) + if (not hasValue) then + return + end + + return keyName + end + function DF:BuildMenu(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook) if (not parent.widget_list) then DF:SetAsOptionsPanel(parent) @@ -2182,8 +2256,10 @@ end label.widget_type = "label" label:SetPoint(currentXOffset, currentYOffset) - if (widgetTable.namePhraseId) then - DetailsFramework.Language.RegisterFontString(languageAddonId, label.widget, widgetTable.namePhraseId) + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable) + if (namePhraseId) then + DetailsFramework.Language.RegisterObject(languageAddonId, label.widget, namePhraseId) + label.languageAddonId = languageAddonId else local textToSet = (widgetTable.get and widgetTable.get()) or widgetTable.text or "" label:SetText(textToSet) @@ -2203,13 +2279,21 @@ end assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'select'") local dropdown = DF:NewDropDown(parent, nil, "$parentWidget" .. index, nil, 140, 18, widgetTable.values, widgetTable.get(), dropdownTemplate) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, dropdown, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, dropdown, "have_tooltip", descPhraseId, widgetTable.desc) dropdown._get = widgetTable.get dropdown.widget_type = "select" local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + + dropdown.addonId = languageAddonId + if (languageAddonId) then + DF.Language.RegisterCallback(languageAddonId, function(addonId, languageId, ...) dropdown:Select(dropdown:GetValue()) end) + C_Timer.After(0.1, function() dropdown:Select(dropdown:GetValue()) end) + end dropdown:SetPoint("left", label, "right", 2) label:SetPoint(currentXOffset, currentYOffset) @@ -2246,7 +2330,8 @@ end elseif (widgetTable.type == "toggle") then local switch = DF:NewSwitch(parent, nil, "$parentWidget" .. index, nil, 60, 20, nil, nil, widgetTable.get(), nil, nil, nil, nil, switchTemplate) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, switch, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, switch, "have_tooltip", descPhraseId, widgetTable.desc) switch._get = widgetTable.get switch.widget_type = "toggle" @@ -2275,7 +2360,9 @@ end end local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then switch:SetPoint(currentXOffset, currentYOffset) @@ -2314,7 +2401,8 @@ end local isDecimanls = widgetTable.usedecimals local slider = DF:NewSlider(parent, nil, "$parentWidget" .. index, nil, 140, 20, widgetTable.min, widgetTable.max, widgetTable.step, widgetTable.get(), isDecimanls, nil, nil, sliderTemplate) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, slider, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, slider, "have_tooltip", descPhraseId, widgetTable.desc) slider._get = widgetTable.get slider.widget_type = "range" @@ -2338,7 +2426,8 @@ end end local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) slider:SetPoint("left", label, "right", 2) label:SetPoint(currentXOffset, currentYOffset) @@ -2364,7 +2453,8 @@ end assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'color'") local colorpick = DF:NewColorPickButton(parent, "$parentWidget" .. index, nil, widgetTable.set, nil, buttonTemplate) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, colorpick, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, colorpick, "have_tooltip", descPhraseId, widgetTable.desc) colorpick._get = widgetTable.get colorpick.widget_type = "color" @@ -2385,7 +2475,8 @@ end end local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then label:SetPoint("left", colorpick, "right", 2) @@ -2416,7 +2507,9 @@ end elseif (widgetTable.type == "execute") then local button = DF:NewButton(parent, nil, "$parentWidget" .. index, nil, 120, 18, widgetTable.func, widgetTable.param1, widgetTable.param2, nil, "", nil, buttonTemplate, textTemplate) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, button.widget, widgetTable.namePhraseId, widgetTable.name) + + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, button.widget, namePhraseId, widgetTable.name) if (not buttonTemplate) then button:InstallCustomTexture() @@ -2434,7 +2527,8 @@ end button:SetPoint(currentXOffset, currentYOffset) end - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, button, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, button, "have_tooltip", descPhraseId, widgetTable.desc) button.widget_type = "execute" @@ -2476,7 +2570,8 @@ end elseif (widgetTable.type == "textentry") then local textentry = DF:CreateTextEntry(parent, widgetTable.func or widgetTable.set, 120, 18, nil, "$parentWidget" .. index, nil, buttonTemplate) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, textentry, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc) + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, textentry, "have_tooltip", descPhraseId, widgetTable.desc) textentry.text = widgetTable.get() textentry._get = widgetTable.get @@ -2485,7 +2580,9 @@ end textentry:SetHook("OnEditFocusLost", widgetTable.func or widgetTable.set) local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) textentry:SetPoint("left", label, "right", 2) label:SetPoint(currentXOffset, currentYOffset) @@ -2652,7 +2749,7 @@ end for _, widget in ipairs(self.widget_list) do if (widget._get) then if (widget.widget_type == "label") then - if (widget._get()) then + if (widget._get() and not widget.languageAddonId) then widget:SetText(widget._get()) end @@ -2816,6 +2913,16 @@ end --~templates local latinLanguageIds = {"enUS", "deDE", "esES", "esMX", "frFR", "itIT", "ptBR"} +local latinLanguageIdsMap = { + ["enUS"] = true, + ["deDE"] = true, + ["esES"] = true, + ["esMX"] = true, + ["frFR"] = true, + ["itIT"] = true, + ["ptBR"] = true, +} + local alphbets = { [latinLanguageIds] = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}, ["zhCN"] = {}, @@ -2850,27 +2957,27 @@ function DF:GetClientRegion() end DF.registeredFontPaths = DF.registeredFontPaths or {} - -function DF:GetBestFontPathForLanguage(locale) - local fontPath = DF.registeredFontPaths[locale] +-- ~language ~locale ~fontpath +function DF:GetBestFontPathForLanguage(languageId) + local fontPath = DF.registeredFontPaths[languageId] if (fontPath) then return fontPath end --font paths gotten from creating a FontString with template "GameFontNormal" and getting the font returned from FontString:GetFont() - if (locale == "enUS" or locale == "deDE" or locale == "esES" or locale == "esMX" or locale == "frFR" or locale == "itIT" or locale == "ptBR") then + if (languageId == "enUS" or languageId == "deDE" or languageId == "esES" or languageId == "esMX" or languageId == "frFR" or languageId == "itIT" or languageId == "ptBR") then return [[Fonts\FRIZQT__.TTF]] - elseif (locale == "ruRU") then + elseif (languageId == "ruRU") then return [[Fonts\FRIZQT___CYR.TTF]] - elseif (locale == "zhCN") then + elseif (languageId == "zhCN") then return [[Fonts\ARKai_T.ttf]] - elseif (locale == "zhTW") then + elseif (languageId == "zhTW") then return [[Fonts\blei00d.TTF]] - elseif (locale == "koKR") then + elseif (languageId == "koKR") then return [[Fonts\2002.TTF]] end @@ -2878,25 +2985,29 @@ function DF:GetBestFontPathForLanguage(locale) return [[Fonts\FRIZQT__.TTF]] end +function DF:IsLatinLanguage(languageId) + return latinLanguageIdsMap[languageId] +end + --return the best font to use for the client language -function DF:GetBestFontForLanguage(language, western, cyrillic, china, korean, taiwan) - if (not language) then - language = DF.ClientLanguage +function DF:GetBestFontForLanguage(languageId, western, cyrillic, china, korean, taiwan) + if (not languageId) then + languageId = DF.ClientLanguage end - if (language == "enUS" or language == "deDE" or language == "esES" or language == "esMX" or language == "frFR" or language == "itIT" or language == "ptBR") then + if (languageId == "enUS" or languageId == "deDE" or languageId == "esES" or languageId == "esMX" or languageId == "frFR" or languageId == "itIT" or languageId == "ptBR") then return western or "Friz Quadrata TT" - elseif (language == "ruRU") then + elseif (languageId == "ruRU") then return cyrillic or "Friz Quadrata TT" - elseif (language == "zhCN") then + elseif (languageId == "zhCN") then return china or "AR CrystalzcuheiGBK Demibold" - elseif (language == "koKR") then + elseif (languageId == "koKR") then return korean or "2002" - elseif (language == "zhTW") then + elseif (languageId == "zhTW") then return taiwan or "AR CrystalzcuheiGBK Demibold" end end diff --git a/Libs/DF/languages.lua b/Libs/DF/languages.lua index 599bee7f..bc11ea4f 100644 --- a/Libs/DF/languages.lua +++ b/Libs/DF/languages.lua @@ -134,6 +134,10 @@ --]=] + +---@class addonNamespaceTable : table store language settings for an addon +---@class fontPath : string font path for a font file on the hardware of the user + local DF = _G["DetailsFramework"] if (not DF or not DetailsFrameworkCanLoad) then return @@ -188,11 +192,61 @@ local fontLanguageCompatibility = { ["ruRU"] = 4, } +--this table contains all the registered languages with their name and fonts +--by default it has all languages the game supports +--it is used when the dropdown shows the languages available for an addon, it take from here the name and font +--new non-native game languages registered with DetailsFramework.Language.RegisterLanguage() will be added to this table +local languagesAvailable = { + deDE = {text = "Deutsch", font = "Fonts\\FRIZQT__.TTF"}, + enUS = {text = "English (US)", font = "Fonts\\FRIZQT__.TTF"}, + esES = {text = "Español (ES)", font = "Fonts\\FRIZQT__.TTF"}, + esMX = {text = "Español (MX)", font = "Fonts\\FRIZQT__.TTF"}, + frFR = {text = "Français", font = "Fonts\\FRIZQT__.TTF"}, + itIT = {text = "Italiano", font = "Fonts\\FRIZQT__.TTF"}, + koKR = {text = "한국어", font = [[Fonts\2002.TTF]]}, + ptBR = {text = "Português (BR)", font = "Fonts\\FRIZQT__.TTF"}, + ruRU = {text = "Русский", font = "Fonts\\FRIZQT___CYR.TTF"}, + zhCN = {text = "简体中文", font = [[Fonts\ARKai_T.ttf]]}, + zhTW = {text = "繁體中文", font = [[Fonts\blei00d.TTF]]}, +} + local fontPathToLanguageId = { ["Fonts\\FRIZQT__.TTF"] = "enUS", ["Fonts\\FRIZQT___CYR.TTF"] = "ruRU", } +local cyrillic = "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯЁЂЃЄЅІЇЈЉЊЋЌЎЏҐабвгдежзийклмнопрстуфхцчшщъыьэюяёђѓєѕіїјљњћќўџґАаБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯя" +local latin = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +local zhCN = "的一是不了人有在他这与大为上们国我以要他时来用到也再说生去子到了地个用发当没成方主于作者我自以前从他者进过家对小多然些起主要只当从把以后看作者们进无对从以后就以于着个过去以及时日能够年月进天们动" +local zhTW = "ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚ᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑ하ᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵ" +local koKR = "ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣ" +local arAR = "ءآأؤإئابتثجحخدذرزسشصضطظعغفقكلمنهويىًٌٍَُِّْٰ" + +local alphabetTable = {} + +for letter in latin:gmatch(".") do + alphabetTable[letter] = "enUS" +end + +for letter in cyrillic:gmatch(".") do + alphabetTable[letter] = "ruRU" +end + +for letter in zhCN:gmatch(".") do + alphabetTable[letter] = "zhCN" +end + +for letter in zhTW:gmatch(".") do + alphabetTable[letter] = "zhTW" +end + +for letter in koKR:gmatch(".") do + alphabetTable[letter] = "koKR" +end + +for letter in arAR:gmatch(".") do + alphabetTable[letter] = "arAR" +end local functionSignature = { ["RegisterLanguage"] = "RegisterLanguage(addonID, languageID[, gameLanguageOnly])", @@ -281,6 +335,15 @@ local errorText = { DF.Language = DF.Language or {version = 1} DF.Language.RegisteredNamespaces = DF.Language.RegisteredNamespaces or {} +DF.Language.LanguageMixin = { + SetAddonID = function(self, addonId) + self.addonId = addonId + end, + + GetAddonID = function(self, addonId) + return self.addonId + end, +} --internal functions local isValid_AddonID = function(addonId) @@ -320,6 +383,9 @@ local getOrCreateAddonNamespace = function(addonId, languageId) addonNamespaceTable = { addonId = addonId, + --store registered functions to call when the language is changed + callbacks = {}, + --by default, the current language is the first registered language currentLanguageId = languageId, languages = {}, @@ -349,16 +415,33 @@ local getOrCreateAddonNamespace = function(addonId, languageId) return addonNamespaceTable end + --just get the addon namespace returning nil if not registered yet local getAddonNamespace = function(addonId) return DF.Language.RegisteredNamespaces[addonId] end +local triggerRegisteredCallbacksForAddonId = function(addonId) + local addonNamespaceTable = getAddonNamespace(addonId) + if (not addonNamespaceTable) then + return + end + + local callbacks = addonNamespaceTable.callbacks + + for i = 1, #callbacks do + local callback = callbacks[i] + --trigger a secure callback using xpcall + xpcall(callback.callback, _G["geterrorhandler"](), addonId, addonNamespaceTable.currentLanguageId, unpack(callback.payload)) + end +end + +--these two are only used for the dropdown create the change the language +--for custom callbacks use DF.Language.RegisterCallback() local setLanguageChangedCallback = function(addonNamespaceTable, callback) printDebug("setLanguageChangedCallback", "addonId:", addonNamespaceTable.addonId, "callbackType:", type(callback)) addonNamespaceTable.onLanguageChangeCallback = callback end - local getLanguageChangedCallback = function(addonNamespaceTable) return addonNamespaceTable.onLanguageChangeCallback end @@ -384,12 +467,6 @@ local setOption = function(addonNamespaceTable, optionId, value) addonNamespaceTable.options[optionId] = value end ---if invalid, the __index from the metatable get the value from the first registered table ---will return nil if the languageTable is from the first registered language -local getTextFromLangugeTable = function(languageTable, phraseId) - return languageTable[phraseId] -end - local getRegisteredObjects = function(addonNamespaceTable) return addonNamespaceTable.registeredObjects end @@ -403,11 +480,20 @@ local getText = function(addonNamespaceTable, phraseId) local text = phraseId + --embed phraseId is when the phraseId is within the string but is surrounded by @ + local embedPhraseId = phraseId:match("@(.-)@") + --get the text from the current language table if (languageTable) then - text = rawget(languageTable, phraseId) + text = rawget(languageTable, embedPhraseId or phraseId) if (isValid_Text(text)) then - return text, currentLanguageId + if (embedPhraseId) then + --replace the embed phraseId with the translated text + text = phraseId:gsub("@" .. embedPhraseId .. "@", text) + return text, currentLanguageId + else + return text, currentLanguageId + end end end @@ -417,9 +503,15 @@ local getText = function(addonNamespaceTable, phraseId) if (currentLanguageId ~= clientLanguage) then languageTable = getLanguageTable(addonNamespaceTable, clientLanguage) if (languageTable) then - text = rawget(languageTable, phraseId) + text = rawget(languageTable, embedPhraseId or phraseId) if (isValid_Text(text)) then - return text, clientLanguage + if (embedPhraseId) then + --replace the embed phraseId with the translated text + text = phraseId:gsub("@" .. embedPhraseId .. "@", text) + return text, currentLanguageId + else + return text, clientLanguage + end end end end @@ -428,10 +520,15 @@ local getText = function(addonNamespaceTable, phraseId) if (currentLanguageId ~= CONST_LANGUAGEID_ENUS and clientLanguage ~= CONST_LANGUAGEID_ENUS) then languageTable = getLanguageTable(addonNamespaceTable, CONST_LANGUAGEID_ENUS) if (languageTable) then - --text = getTextFromLangugeTable(languageTable, phraseId) - text = rawget(languageTable, phraseId) + text = rawget(languageTable, embedPhraseId or phraseId) if (isValid_Text(text)) then - return text, CONST_LANGUAGEID_ENUS + if (embedPhraseId) then + --replace the embed phraseId with the translated text + text = phraseId:gsub("@" .. embedPhraseId .. "@", text) + return text, CONST_LANGUAGEID_ENUS + else + return text, CONST_LANGUAGEID_ENUS + end end end end @@ -443,11 +540,25 @@ local setLanguageTableForLanguageId = function(addonNamespaceTable, languageId, local isFirstLanguage = not next(addonNamespaceTable.languages) if (isFirstLanguage) then printDebug("setLanguageTableForLanguageId", "(first to be registered) addonId:", addonNamespaceTable.addonId, "languageId:", languageId, "languageTable:", languageTable, "languageIdType:", type(languageId), "languageTableType:", type(languageTable)) - --defaultLanguageTable is constant addonNamespaceTable.defaultLanguageTable = languageTable else printDebug("setLanguageTableForLanguageId", "addonId:", addonNamespaceTable.addonId, "languageId:", languageId, "languageTable:", languageTable, "languageIdType:", type(languageId), "languageTableType:", type(languageTable)) - local defaultLanguageMetatable = {__index = function(table, key) return addonNamespaceTable.defaultLanguageTable[key] or key end} + + local defaultLanguageMetatable = {__index = function(table, key) + local value = rawget(table, key) + if (value) then + return value + end + + local defaultLanguageTable = addonNamespaceTable.defaultLanguageTable + value = defaultLanguageTable[key] + if (value) then + return value + end + + return key + end} + setmetatable(languageTable, defaultLanguageMetatable) end @@ -463,8 +574,10 @@ local setCurrentLanguageId = function(addonNamespaceTable, languageId) local callbackFunc = getLanguageChangedCallback(addonNamespaceTable) if (callbackFunc) then printDebug("setCurrentLanguageId", "addonId:", addonNamespaceTable.addonId, "calling callback", "callbackFuncType:", type(callbackFunc)) - xpcall(callbackFunc, _G["geterrorhandler"](), languageId) + xpcall(callbackFunc, _G["geterrorhandler"](), languageId, addonNamespaceTable.addonId) end + + triggerRegisteredCallbacksForAddonId(addonNamespaceTable.addonId) end local parseArguments = function(...) @@ -493,7 +606,11 @@ local updatePhraseInfo_Arguments = function(phraseInfoTable, ...) phraseInfoTable.arguments = parseArguments(...) end -local getFontForLanguageId = function(addonNamespaceTable, languageId) +---return the fontPath for the languageId or nil if the languageId is not registered for the addon +---@param addonNamespaceTable table +---@param languageId string +---@return fontPath|nil +local getFontForLanguageIdFromAddonNamespace = function(addonNamespaceTable, languageId) return addonNamespaceTable.fonts[languageId] end @@ -508,19 +625,19 @@ local shouldChangeFontForNewLanguage = function(addonNamespaceTable, oldLanguage else if (addonNamespaceTable.options.ChangeOnlyRegisteredFont) then --can change only if the font was previously registered with SetFontForLanguageId() or SetFontByAlphabetOrRegion() - local languageFontPath = getFontForLanguageId(addonNamespaceTable, newLanguageId) + local languageFontPath = getFontForLanguageIdFromAddonNamespace(addonNamespaceTable, newLanguageId) if (languageFontPath) then --the font is registered return true, languageFontPath end else - local languageFontPath = getFontForLanguageId(addonNamespaceTable, newLanguageId) + local languageFontPath = getFontForLanguageIdFromAddonNamespace(addonNamespaceTable, newLanguageId) if (languageFontPath) then --the font is registered return true, languageFontPath else --the font is not registered for this language, get the default font from the framework - languageFontPath = DF:GetBestFontPathForLanguage(newLanguageId) + languageFontPath = DF.Language.GetFontForLanguageID(newLanguageId) return true, languageFontPath end end @@ -551,13 +668,22 @@ local setObject_Text = function(addonNamespaceTable, object, phraseInfoTable, te if (textLanguageId ~= object.__languageId) then local bShouldChangeFont, fontPath = shouldChangeFontForNewLanguage(addonNamespaceTable, object.__languageId, textLanguageId) if (bShouldChangeFont) then - local font, size, flags = object:GetFont() - object:SetFont(fontPath, size, flags) + if (object:GetObjectType() == "Button") then + local fontString = object:GetFontString() + if (fontString) then + local font, size, flags = fontString:GetFont() + fontString:SetFont(fontPath, size, flags) + end + else + local font, size, flags = object:GetFont() + object:SetFont(fontPath, size, flags) + end setObject_InternalMembers(object, false, false, false, textLanguageId) end end local formattedText = getFormattedText(phraseInfoTable, text) + object:SetText(formattedText) end @@ -661,7 +787,7 @@ local registerTableKeyTable = function(addonNamespaceTable, table, tableKeyTable addonNamespaceTable.tableKeys[table] = tableKeyTable end -local registerTableKey = function(addonNamespaceTable, table, key, phraseId, ...) +local registerTableKey = function(addonNamespaceTable, table, key, phraseId, ...) --~registerTableKey local tableKeyTable = getTableKeyTable(addonNamespaceTable, table) if (not tableKeyTable) then tableKeyTable = {} @@ -708,24 +834,142 @@ local setFontForLanguageId = function(addonNamespaceTable, languageId, fontPath) end end +---when the language has changed for an addon, call all callbacks registered for that addon +---the function will be called with the following parameters: callback(addonId, languageId, unpack(payload)) +---@param addonId string +---@param callback function +---@vararg any +---@return boolean +function DF.Language.RegisterCallback(addonId, callback, ...) + if (not isValid_AddonID(addonId)) then + error("RegisterCallback() param #1 'addonId' must be a string or a table, got: " .. type(addonId) .. ".") + end + if (type(callback) ~= "function") then + error("RegisterCallback() param #2 'callback' must be a function, got: " .. type(callback) .. ".") + end ---create a language table within an addon namespace ---@addonId: an identifier, can be any table or string, will be used when getting the table with phrase translations, example: "DetailsLocalization", "Details", "PlaterLoc", _G.Plater ---@languageId: game languages: "deDE", "enUS", "esES", "esMX", "frFR", "itIT", "koKR", "ptBR", "ruRU", "zhCN", "zhTW", or any other value if 'gameLanguageOnly' is false (default) ---@gameLanguageOnly: if true won't allow to register a language not supported by the game, a supported language is any language returnted by GetLocale() ---return value: return a languageTable, this table holds translations for the registered language -function DF.Language.RegisterLanguage(addonId, languageId, gameLanguageOnly) + local addonNamespaceTable = getAddonNamespace(addonId) + if (not addonNamespaceTable) then + return false + end + + addonNamespaceTable.callbacks[#addonNamespaceTable.callbacks+1] = {callback = callback, payload = {...}} + return true +end + +---unregister a registered addon callback +---@param addonId string +---@param callback function +---@return boolean +function DF.Language.UnregisterCallback(addonId, callback) + if (not isValid_AddonID(addonId)) then + error("UnregisterCallback() param #1 'addonId' must be a string or a table, got: " .. type(addonId) .. ".") + end + if (type(callback) ~= "function") then + error("UnregisterCallback() param #2 'callback' must be a function, got: " .. type(callback) .. ".") + end + + local addonNamespaceTable = getAddonNamespace(addonId) + if (not addonNamespaceTable) then + return false + end + + for i = 1, #addonNamespaceTable.callbacks do + local callbackTable = addonNamespaceTable.callbacks[i] + if (callbackTable.callback == callback) then + tremove(addonNamespaceTable.callbacks, i) + return true + end + end + return false +end + +---return the languageId of the text passed, if not found return the default languageId (enUS) +---@param text string +---@return string +function DF.Language.DetectLanguageId(text) + for letter in text:gmatch(".") do + if (alphabetTable[letter]) then + return alphabetTable[letter] + end + end + return "enUS" +end + +---get the languageId set to be used on an addon +---@param addonId string +---@return string +function DF.Language.GetLanguageIdForAddonId(addonId) + if (not isValid_AddonID(addonId)) then + error("GetLanguageIdForAddonId() param #1 'addonId' must be a string or a table, got: " .. type(addonId) .. ".") + end + + local addonNamespaceTable = getAddonNamespace(addonId) + if (not addonNamespaceTable) then + return "enUS" + end + + return addonNamespaceTable.currentLanguageId +end + +---check if the key exists in the default language table for the addon +---@param addonId string +---@param key string +---@return boolean +function DF.Language.DoesPhraseIDExistsInDefaultLanguage(addonId, key) --~DoesPhraseIDExistsInDefaultLanguage + if (not isValid_AddonID(addonId)) then + error("DoesPhraseIDExistsInDefaultLanguage() param #1 'addonId' must be a string or a table, got: " .. type(addonId) .. ".") + end + + local addonNamespaceTable = getAddonNamespace(addonId) + if (not addonNamespaceTable) then + return false + end + + local defaultLanguageTable = addonNamespaceTable.defaultLanguageTable + if (not defaultLanguageTable) then + return false + end + + return rawget(defaultLanguageTable, key) and true +end + + +---create a language table within an addon namespace +---if bIsNativeGameLanguage is true, languageName and languageFont are required +---@param addonId string an identifier, can be any table or string, will be used when getting the table with phrase translations, example: "DetailsLocalization", "Details", "PlaterLoc", _G.Plater +---@param languageId string game languages: "deDE", "enUS", "esES", "esMX", "frFR", "itIT", "koKR", "ptBR", "ruRU", "zhCN", "zhTW", or any other value if 'gameLanguageOnly' is false (default) +---@param bNotSupportedWoWLanguage boolean if true it indicates that this is not a native game language +---@return table: return a languageTable, this table holds translations for the registered language +function DF.Language.RegisterLanguage(addonId, languageId, bNotSupportedWoWLanguage, languageName, languageFont) --~RegisterLanguage if (not isValid_AddonID(addonId)) then error(functionCallPath["RegisterLanguage"] .. ": " .. format(errorText["AddonID"], 1) .. ", use: " .. functionSignature["RegisterLanguage"] .. ".") - elseif (gameLanguageOnly and not supportedGameLanguages[languageId]) then + elseif (not bNotSupportedWoWLanguage and not supportedGameLanguages[languageId]) then error(functionCallPath["RegisterLanguage"] .. ": " .. format(errorText["LanguageID"], 2) .. ", use: " .. functionSignature["RegisterLanguage"] .. ".") + + elseif (bNotSupportedWoWLanguage) then + if (not languageName) then + error(functionCallPath["RegisterLanguage"] .. ": " .. "Invalid Language Name" .. ", use: " .. functionSignature["RegisterLanguage"] .. ".") + end + if (not languageFont) then + error(functionCallPath["RegisterLanguage"] .. ": " .. "Invalid Language Font" .. ", use: " .. functionSignature["RegisterLanguage"] .. ".") + end + + if (not supportedGameLanguages[languageId]) then + languagesAvailable[languageId] = {text = languageName, font = languageFont} + end end --get the language namespace, the namespace can be a string or a table. --if the namespace isn't created yet, this function will create local addonNamespaceTable = getOrCreateAddonNamespace(addonId, languageId) + if (bNotSupportedWoWLanguage) then + setFontForLanguageId(addonNamespaceTable, languageId, languageFont) + DF.registeredFontPaths[languageId] = languageFont + end + --create a table to hold traslations for this languageId local languageTable = {} setLanguageTableForLanguageId(addonNamespaceTable, languageId, languageTable) @@ -1009,7 +1253,7 @@ end --@phraseId: any string to identify the a translated text, example: token: "OPTIONS_FRAME_WIDTH" text: "Adjust the Width of the frame." --@silent: if true won't error on invalid phrase text or table already registered, it will still error on invalid addonId, table, key and phraseId --@vararg: arguments to pass for format(text, ...) -function DF.Language.RegisterTableKey(addonId, table, key, phraseId, silent, ...) +function DF.Language.RegisterTableKey(addonId, table, key, phraseId, silent, ...) --~RegisterTableKey if (not isValid_AddonID(addonId)) then error(functionCallPath["RegisterTableKey"] .. ": " .. format(errorText["AddonID"], 1) .. ", use: " .. functionSignature["RegisterTableKey"] .. ".") end @@ -1036,7 +1280,7 @@ function DF.Language.RegisterTableKey(addonId, table, key, phraseId, silent, ... local text, textLanguageId = getText(addonNamespaceTable, phraseId) if (not isValid_Text(text)) then if (not silent) then - error(functionCallPath["RegisterTableKey"] .. ": " .. errorText["PhraseIDNotRegistered"] .. ", use: " .. functionSignature["GetLanguageTable"] .. "['PhraseID'] = 'translated text'.") + error(functionCallPath["RegisterTableKey"] .. ": " .. errorText["PhraseIDNotRegistered"] .. ": " .. phraseId .. ", use: " .. functionSignature["GetLanguageTable"] .. "['PhraseID'] = 'translated text'.") else text = phraseId end @@ -1087,7 +1331,7 @@ function DF.Language.UpdateTableKeyArguments(addonId, table, key, ...) end -function DF.Language.RegisterTableKeyWithDefault(addonId, table, key, phraseId, defaultText, ...) +function DF.Language.RegisterTableKeyWithDefault(addonId, table, key, phraseId, defaultText, ...) --~RegisterTableKeyWithDefault if (addonId and phraseId) then DetailsFramework.Language.RegisterTableKey(addonId, table, key, phraseId, ...) else @@ -1299,19 +1543,56 @@ function DF.Language.CreateLanguageSelector(addonId, parent, callback, selectedL local onSelectLanguage = function(self, addonId, languageId) DF.Language.SetCurrentLanguage(addonId, languageId) + C_Timer.After(0.5, function() + self:Select(languageId) + end) end local buildOptionsFunc = function() local resultTable = {} for languageId in pairs(allLanguagesRegistered) do - resultTable[#resultTable+1] = {value = languageId, label = languageId, onclick = onSelectLanguage} --, icon = icon, iconcolor = iconcolor, iconsize = iconsize + local languageIdInfo = languagesAvailable[languageId] + resultTable[#resultTable+1] = {value = languageId, label = languageIdInfo.text, onclick = onSelectLanguage, font = languageIdInfo.font} end return resultTable end - local languageSelector = DF:CreateDropDown(parent, buildOptionsFunc, selectedLanguage or getCurrentLanguageId(addonNamespaceTable), 80, 20, nil, nil, DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")) + local languageSelector = DF:CreateDropDown(parent, buildOptionsFunc, selectedLanguage or getCurrentLanguageId(addonNamespaceTable), 120, 20, nil, nil, DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")) + languageSelector:SetAddonID(addonId) languageSelector:SetFixedParameter(addonId) + + local languageLabel = DF:CreateLabel(parent, _G.LANGUAGE .. ":", 10, "silver") + languageLabel:SetPoint("right", languageSelector, "left", -3, 0) + return languageSelector +end + + +---return a font (path for a file) which works for the languageId passed, if the languageId is not registered, it'll return a compatible font registered by another addon or the default font +---@param languageId string +---@param addonId string +---@return string +function DF.Language.GetFontForLanguageID(languageId, addonId) + if (addonId) then + ---@type addonNamespaceTable + local addonNamespaceTable = getAddonNamespace(addonId) + if (not addonNamespaceTable) then + error(functionCallPath["CreateLanguageSelector"] .. ": " .. errorText["AddonIDInvalidOrNotRegistered"] .. ", use: " .. functionSignature["RegisterLanguage"] .. ".") + end + + --get the font from the addon namespace table + ---@type fontPath|nil + local fontPath = getFontForLanguageIdFromAddonNamespace(addonNamespaceTable, languageId) + if (fontPath) then + return fontPath + end + end + + local languageIdInfo = languagesAvailable[languageId] + if (languageIdInfo) then + return languageIdInfo.font + end + return "Fonts\\FRIZQT__.TTF" end \ No newline at end of file diff --git a/Libs/DF/panel.lua b/Libs/DF/panel.lua index 881b51d1..21be940e 100644 --- a/Libs/DF/panel.lua +++ b/Libs/DF/panel.lua @@ -3517,24 +3517,24 @@ local tab_container_on_show = function(self) self.SelectIndex (self.AllButtons[index], nil, index) end -function detailsFramework:CreateTabContainer (parent, title, frame_name, frameList, options_table, hookList) +function detailsFramework:CreateTabContainer (parent, title, frameName, frameList, optionsTable, hookList, languageInfo) local options_text_template = detailsFramework:GetTemplate("font", "OPTIONS_FONT_TEMPLATE") local options_dropdown_template = detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE") local options_switch_template = detailsFramework:GetTemplate("switch", "OPTIONS_CHECKBOX_TEMPLATE") local options_slider_template = detailsFramework:GetTemplate("slider", "OPTIONS_SLIDER_TEMPLATE") local options_button_template = detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE") - options_table = options_table or {} + optionsTable = optionsTable or {} local parentFrameWidth = parent:GetWidth() - local y_offset = options_table.y_offset or 0 - local buttonWidth = options_table.button_width or 160 - local buttonHeight = options_table.button_height or 20 - local buttonAnchorX = options_table.button_x or 230 - local buttonAnchorY = options_table.button_y or -32 - local button_text_size = options_table.button_text_size or 10 - local containerWidthOffset = options_table.container_width_offset or 0 + local y_offset = optionsTable.y_offset or 0 + local buttonWidth = optionsTable.button_width or 160 + local buttonHeight = optionsTable.button_height or 20 + local buttonAnchorX = optionsTable.button_x or 230 + local buttonAnchorY = optionsTable.button_y or -32 + local button_text_size = optionsTable.button_text_size or 10 + local containerWidthOffset = optionsTable.container_width_offset or 0 - local mainFrame = CreateFrame("frame", frame_name, parent.widget or parent, "BackdropTemplate") + local mainFrame = CreateFrame("frame", frameName, parent.widget or parent, "BackdropTemplate") mainFrame:SetAllPoints() detailsFramework:Mixin(mainFrame, detailsFramework.TabContainerFunctions) mainFrame.hookList = hookList or {} @@ -3548,50 +3548,69 @@ function detailsFramework:CreateTabContainer (parent, title, frame_name, frameLi mainFrame.AllButtons = {} mainFrame.CurrentIndex = 1 mainFrame.IsContainer = true - mainFrame.ButtonSelectedBorderColor = options_table.button_selected_border_color or {1, 1, 0, 1} - mainFrame.ButtonNotSelectedBorderColor = options_table.button_border_color or {0, 0, 0, 0} + mainFrame.ButtonSelectedBorderColor = optionsTable.button_selected_border_color or {1, 1, 0, 1} + mainFrame.ButtonNotSelectedBorderColor = optionsTable.button_border_color or {0, 0, 0, 0} - if (options_table.right_click_interact ~= nil) then - mainFrame.CanCloseWithRightClick = options_table.right_click_interact + if (optionsTable.right_click_interact ~= nil) then + mainFrame.CanCloseWithRightClick = optionsTable.right_click_interact else mainFrame.CanCloseWithRightClick = true end - for i, frame in ipairs(frameList) do - local f = CreateFrame("frame", "$parent" .. frame.name, mainFrame, "BackdropTemplate") + --languageInfo + local addonId = languageInfo.language_addonId + local languageTable = DetailsFramework.Language.GetLanguageTable(addonId) + + for i, frameInfo in ipairs(frameList) do + local f = CreateFrame("frame", "$parent" .. frameInfo.name, mainFrame, "BackdropTemplate") f:SetAllPoints() f:SetFrameLevel(210) f:Hide() - local title = detailsFramework:CreateLabel(f, frame.title, 16, "silver") + --attempt to get the localized text from the language system using the addonId and the frameInfo.title + local phraseId = frameInfo.title + local bIsLanguagePrahseID = detailsFramework.Language.DoesPhraseIDExistsInDefaultLanguage(addonId, phraseId) + + local title = detailsFramework:CreateLabel(f, "", 16, "silver") + if (bIsLanguagePrahseID) then + DetailsFramework.Language.RegisterObjectWithDefault(addonId, title.widget, frameInfo.title, frameInfo.title) + else + title:SetText(frameInfo.title) + end + title:SetPoint("topleft", mainTitle, "bottomleft", 0, 0) f.titleText = title - local tabButton = detailsFramework:CreateButton(mainFrame, detailsFramework.TabContainerFunctions.SelectIndex, buttonWidth, buttonHeight, frame.title, i, nil, nil, nil, "$parentTabButton" .. frame.name, false, button_tab_template) + local tabButton = detailsFramework:CreateButton(mainFrame, detailsFramework.TabContainerFunctions.SelectIndex, buttonWidth, buttonHeight, frameInfo.title, i, nil, nil, nil, "$parentTabButton" .. frameInfo.name, false, button_tab_template) + + if (bIsLanguagePrahseID) then + DetailsFramework.Language.RegisterObjectWithDefault(addonId, tabButton.widget, frameInfo.title, frameInfo.title) + end + PixelUtil.SetSize(tabButton, buttonWidth, buttonHeight) tabButton:SetFrameLevel(220) tabButton.textsize = button_text_size tabButton.mainFrame = mainFrame - detailsFramework.TabContainerFunctions.CreateUnderlineGlow (tabButton) + detailsFramework.TabContainerFunctions.CreateUnderlineGlow(tabButton) - local right_click_to_back - if (i == 1 or options_table.rightbutton_always_close) then - right_click_to_back = detailsFramework:CreateLabel(f, "right click to close", 10, "gray") - right_click_to_back:SetPoint("bottomright", f, "bottomright", -1, options_table.right_click_y or 0) - if (options_table.close_text_alpha) then - right_click_to_back:SetAlpha(options_table.close_text_alpha) + local rightClickToBack + if (i == 1 or optionsTable.rightbutton_always_close) then + rightClickToBack = detailsFramework:CreateLabel(f, "right click to close", 10, "gray") + rightClickToBack:SetPoint("bottomright", f, "bottomright", -1, optionsTable.right_click_y or 0) + if (optionsTable.close_text_alpha) then + rightClickToBack:SetAlpha(optionsTable.close_text_alpha) end f.IsFrontPage = true else - right_click_to_back = detailsFramework:CreateLabel(f, "right click to go back to main menu", 10, "gray") - right_click_to_back:SetPoint("bottomright", f, "bottomright", -1, options_table.right_click_y or 0) - if (options_table.close_text_alpha) then - right_click_to_back:SetAlpha(options_table.close_text_alpha) + rightClickToBack = detailsFramework:CreateLabel(f, "right click to go back to main menu", 10, "gray") + rightClickToBack:SetPoint("bottomright", f, "bottomright", -1, optionsTable.right_click_y or 0) + if (optionsTable.close_text_alpha) then + rightClickToBack:SetAlpha(optionsTable.close_text_alpha) end end - if (options_table.hide_click_label) then - right_click_to_back:Hide() + if (optionsTable.hide_click_label) then + rightClickToBack:Hide() end f:SetScript("OnMouseDown", detailsFramework.TabContainerFunctions.OnMouseDown) @@ -3628,6 +3647,7 @@ function detailsFramework:CreateTabContainer (parent, title, frame_name, frameLi --select the first frame mainFrame.SelectIndex (mainFrame.AllButtons[1], nil, 1) + print() return mainFrame end