Jest to oczywiste, iż jeżeli twój addon ma się wybić lub rozrosnąć na wielką skalę, daleko nie zajdzie bez tłumaczenia na główne języki. Jak zapewne niektórzy zauważyli, miło jest widzieć addony w swoim języku co ułatwia nawigację i możesz dzięki temu wykorzystać jego cały potencjał. Pisząc ten artykuł teraz powiem, iż niedawno ja zacząłem tłumaczyć addony - na forum RoM
Istnieją różne systemy tłumaczenia. Ja wam przedstawię większość z nich wraz z zastosowaniem ich zarówno w LUA jak i XML
Sposób 1[ | ]
Zacznijmy od najprostrzego, który ma swoje minusy, ponieważ nie wykrywa sam języka użytkownika, a jedynie gdy wykryje np. PL załaduje daną paczkę. Taki system tłumaczenia używa np. zarządzany przeze mnie nkCooldown i na jego bazie opiszę ten sposób.
Najpierw zajrzymy w składnię pliku z tłumaczeniem - Localization.PL.lua
, który jest umieszczony w <nazwa addona>/Locales
Zamieszczę tylko jego fragment:
if ( GetLanguage("player")=="PL") then -- jeżeli zostanie wykryty język PL (Polski) pobiera następujące dane: nkCooldownLangText = { -- nazwa typu tekstów np. Chat oraz otwarcie klamry - { ["DESCRIPTION"] = "nkCooldown pokazuje czas odnowy umiejętności i przedmiotów."; -- nazwy danego tekstu są już umieszczone w kwadratowym nawiasie i z cudzysłowiem, dla późniejszego ułatwienia, koniec danej linii jest oznaczony ; ["LOADED"] = "załadowany"; ["CHATCMD"] = "Wpisz /nkcd lub /nkCooldown, aby wyświetlić okno ustawień"; ["RESET"] = "Zresetuj ustawienia do domyślnych"; ["UNITMIN"] = "minut"; ["UNITSEC"] = "sekund"; ["YOUUSE"] = "Używasz"; ["COMMANDS"] = "/nkcd, /nkCooldown"; } -- zamknięcie klamry nkCooldownUI = { ["ALPHADESC"] = "Widoczność"; } nkCooldownLongCDDisplay = { -- jak widać tutaj tekst nie został umieszczony w kwadratowym nawiasie i cudzysłowiu, ponieważ jest on przystosowany do innego zamieszczenia "głównym pasku"; "na dwóch paskach" } end; -- zakończenie ładowania tłumaczenia
Umieszczony tekst z tłumaczenia w normalnym pliku LUA[ | ]
description = nkCooldownLangText["DESCRIPTION"], -- kawałek funkcji opisu addona w AddonManager (nkCooldown.AddonName .. ": " .. nkCooldownLangText["RESET"]) -- fragment kodu wywołującego nazwę addona, dwukropek i tekst '''Zresetuj ustawienia do domyślnych'''
Jak wygląda wpis w pliku TOC[ | ]
-- wszystkie nasze tłumaczenia Locales\Localization.DE.lua Locales\Localization.EN.lua Locales\Localization.PL.lua Locales\Localization.ES.lua Locales\Localization.CN.lua Locales\Localization.FR.lua Locales\Localization.JP.lua Locales\Localization.KR.lua Locales\Localization.RU.lua Locales\Localization.TW.lua
Tłumaczenie w pliku XML[ | ]
-- posłużę się na danym fragmencie kodu: nkCooldownUI = { ["ALPHADESC"] = "Widoczność"; } -- A teraz fragment z kodu XML: <Frame name="nkCooldownCfg_UI" parent="nkCooldownCfg" hidden="false"> -- funkcja name nazywa ustalany tekst w kodzie poniżej (patrz name w AlphaDesc ) -- funkcja parent przekierowywuje nazwę do danej kategorii <Size> <AbsDimension x="430" y="310"/> </Size> <Anchors> <Anchor point="TOPLEFT" relativeTo="nkCooldownCfg" relativePoint="TOPLEFT"> -- funkcja relativeTo przekierowywuje nazwę do danej kategorii <Offset> <AbsDimension x="10" y="30"/> </Offset> </Anchor> </Anchors> <FontString name="nkCooldownCfg_AlphaDesc" inherits="GameFontLabel" justifyH="LEFT"> -- funkcja name zamiera nazwę pobieranego tekstu (to po _ ) <Size> <AbsDimension x="300" y="20"/> </Size> <FontHeight> <AbsValue val="12"/> </FontHeight> <Anchors> <Anchor point="TOPLEFT" relativeTo="nkCooldownCfg_UI" relativePoint="TOPLEFT"> -- funkcja relativeTo przekierowywuje nazwę do danej kategorii </Anchor> </Anchors> </FontString>
Sposób 2[ | ]
Ten sposób jest jednym z najpopularniejszych, między innymi, dzięki swej prostocie.
Poradnik przedstawię na podstawie addona farmcalc
Najpierw zajrzymy w składnię pliku z tłumaczeniem - PL.lua
, który jest umieszczony w <nazwa addona>/locales
Zamieszczę jego całość:
farmcalc.Strings = { -- ogólna kategoria -- w tekstach = { jest zastępowane po prostu kropką Last = "Ostatnio:", LastMoney = "Ostatnio:", LastTime = "Ostatnio:", Total = "Łącznie:", TotalMoney = "Łącznie:", TotalTime = "Czas:", PerHour = "Na godzinę:", ThousandsDivider = ",", Loaded = "załadowany. Wpisz %s, aby zobaczyć komendy.", Add = "Dodano %s do obszaru farmienia.", Remove = "Usunięto %s z obszaru farmienia.", Help1 = "Wpisz %s, aby dodać aktualną strefę do obszaru farmienia", Help2 = "DODAWAJ TYLKO INSTANCJE!", Help3 = "Wpisz %s, aby usunąć aktualną strefę z obszaru farmienia", }
Funkcja pobierania tłumaczenia w pliku LUA[ | ]
-- przykład (fragment) - wpis w OnEvent function farmcalc.onEvent(event, this) if event == "VARIABLES_LOADED" then farmcalc.translate() end end -- funkcja pobierania tłumaczenia function farmcalc.translate() -- nazwa funkcji local language = string.upper(string.sub(GetLanguage(),1,2)) -- pobiera język klienta np. PL local func, err = loadfile("Interface/Addons/farmcalc/locales/"..language..".lua") -- lokalizacja pliku językowego if (err) then -- jeżeli nie wykryje danego języka language = "EN" -- pobiera język angielski end dofile("Interface/Addons/farmcalc/locales/"..language..".lua") -- załadowanie danego języka -- teksty dostosowane do XML - część do dwukropka farmcalcFrame_Head_Text1:SetText(farmcalc.Strings.LastMoney) farmcalcFrame_Head_Text2:SetText(farmcalc.Strings.TotalMoney) farmcalcFrame_Head_Text3:SetText(farmcalc.Strings.TotalTime) farmcalcFrame_Head_Text4:SetText(farmcalc.Strings.LastTime) farmcalcFrame_Head_Text5:SetText(farmcalc.Strings.PerHour) end;
Umieszczony tekst z tłumaczenia w normalnym pliku LUA[ | ]
-- opis w AddonManager wymyślony na potrzeby poradnika description = farmcalc.Strings.Description, -- kawałek funkcji opisu addona w AddonManager (farmcalc.Strings.Help3,"|cff00ff00/farmcalc remove|r") -- fragment funkcji wywołującej na chacie pomoc
Tłumaczenie w pliku XML[ | ]
-- posłużę się na podstawie podanych zmienionych nazw w LUA -- fragment jak umieścić tłumaczenie: <Frame name="farmcalcFrame" frameStrata="MEDIUM" parent="UIParent"> -- główna ramka, następuje po jej nazwie _ <Frame name="$parent_Head"> -- kategoria, następuje po jej nazwie _ <FontString name="$parent_Text1" justifyH="LEFT" justifyV="MIDDLE" inherits="Font_Normal"> -- tekst Text1 </FontString> </Frame> </Frame>
Sposób 3 (nieukończony)[ | ]
Ten sposób jest używany w addonach takich jak SlashAFK, ComeOnIn i w części w pbInfo.
Ten sposób jest zalecany, jeżeli chcesz umożliwić użytkownikowi zmianę języka, dzięki menu.
Poradnik przedstawię na podstawie zarządzanego przeze mnie addona - SlashAFK
Najpierw zajrzymy w składnię pliku z tłumaczeniem - PL.lua
, który jest umieszczony w <nazwa addona>/Languages
Zamieszczę jego fragment:
do -- wykonuje (pobiera) następujące dane SLASH_AFK.Language = { -- główna kategoria ["NotInChannel"] = "Nie dołączyłeś do kanału", ["InvalidChannel"] = "Wpisany kanał jest niepoprawny.", ["Default"] = { -- podkategoria ["AFKReply"] = "Zaraz Wracam.", ["AFKReturn"] = "Wróciłem.", ["AFKAway"] = "Zaraz Wracam.", }, ["Help"] = { ["AddAlias"] = "[Nazwa Aliasu](Gdy ktoś napisze na chacie twój alias np. bestia otrzyma informację taką jak po napisaniu nick, gdy jesteś na ZW)", ["ToggleNotifications"] = "[Opcjonalne 'on', 'off'] (Włącz/wyłącz powiadomienia)", }, ["GUI"] = { ["ChannelsText"] = "Własne kanały", ["SaveAwayMessage"] = "Zapisz wiadomość ZW", ["SaveReturnMessage"] = "Zapisz wiadomość powrotu", }, } end
Funkcja pobierania tłumaczenia w Language.lua[ | ]
Plik musi się znajdować w tej samej lokalizacji co pliki językowe
Fragment (dostępne języki):
SLASH_AFK.AvailableLanguages = {} -- Wykrywanie języku function SLASH_AFK.InitLanguage() table.insert(SLASH_AFK.AvailableLanguages, { ["text"] = "English", ["value"] = "ENEU", ["func"] = SLASH_AFK.LanguageButtonClicked, }) table.insert(SLASH_AFK.AvailableLanguages, { ["text"] = "Deutsch", ["value"] = "DE", ["func"] = SLASH_AFK.LanguageButtonClicked, }) table.insert(SLASH_AFK.AvailableLanguages, { ["text"] = "Polski", ["value"] = "PL", ["func"] = SLASH_AFK.LanguageButtonClicked, }) SaveVariables("SLASH_AFK_CurrentLanguage") -- Zapamiętuje ostatnio wybrany język if (SLASH_AFK_CurrentLanguage ~= nil) then SLASH_AFK.LoadLanguage(SLASH_AFK_CurrentLanguage) else if (SLASH_AFK.LoadLanguage(GetLanguage()) == false) then SLASH_AFK.LoadLanguage("ENEU") end end end -- Funkcja ładowania języku function SLASH_AFK.LoadLanguage(lang) lang = string.upper(lang) local funct, err = loadfile("Interface/AddOns/Slash_AFK/Languages/" .. lang .. ".lua") -- Sprawdzanie pliku if (err) then --If the loading has failed return false, err end dofile("Interface/AddOns/Slash_AFK/Languages/" .. lang .. ".lua") --Ładowanie pliku SLASH_AFK_CurrentLanguage = lang return true end function SLASH_AFK.LanguageButtonClicked(lang) SLASH_AFK.SetLanguage(lang) end function SLASH_AFK.SetLanguage(button) if (SLASH_AFK.LoadLanguage(button.value) == false) then SLASH_AFK_CurrentLanguage = "EN" SLASH_AFK.LoadLanguage(SLASH_AFK_CurrentLanguage) end SLASH_AFK.reloadUI() end
Funkcja tłumaczenia po stronie LUA[ | ]
-- teksty umieszczone przy okazji w funkcji do przeładowania UI addona -- dopasowane do umieszczenia w XML, fragment: function SLASH_AFK.reloadUI() SLASH_AFK.SetCurrentLanguageInDropdown() SLASH_AFK_GUIChannelsText:SetText(SLASH_AFK.Language.GUI.ChannelsText) SLASH_AFK_returnButton:SetText(SLASH_AFK.Language.GUI.SaveReturnMessage) SLASH_AFK_awayButton:SetText(SLASH_AFK.Language.GUI.SaveAwayMessage) end
[ | ]
<Frame name="SLASH_AFK_Language_DropdownBox" inherits="UIDropDownMenuTemplate" id="6"> <Anchors> <Anchor point="TOPLEFT" relativeTo="SLASH_AFK_lockButton" relativePoint="BOTTOMLEFT"> <Offset><AbsDimension x="0" y="7"/></Offset> </Anchor> </Anchors> <Scripts> <OnLoad> UIDropDownMenu_SetWidth(this, 150) UIDropDownMenu_Initialize(this, SLASH_AFK.Language_DropdownBox_OnLoad) </OnLoad> </Scripts> </Frame>
Umieszczony tekst z tłumaczenia w normalnym pliku LUA[ | ]
description = SLASH_AFK.Language.Description, -- kawałek funkcji opisu addona w AddonManager (SLASH_AFK.Language.AddonLoaded) -- fragment funkcji wywołującej na chacie info o załadowaniu addona
Tłumaczenie w pliku XML[ | ]
-- posłużę się na podstawie podanych zmienionych nazw w LUA -- fragment jak umieścić tłumaczenie: <Button name="SLASH_AFK_returnButton" inherits="SLASH_AFK_Button_Template" id="8"> -- w name nazwa tekstu <Anchors> <Anchor point="TOPLEFT" relativeTo="SLASH_AFK_replyButton" relativePoint="BOTTOMLEFT"> <Offset><AbsDimension x="0" y="10"/></Offset> </Anchor> </Anchors> <Scripts> <OnClick> SLASH_AFK:ActionPerformed() SLASH_AFK.SetAFKReturn(SLASH_AFK_uiReturnText:GetText()) SLASH_AFK_uiReturnText:ClearFocus() </OnClick> </Scripts> </Button>