System tłumaczenia

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

```

### **Kod na menu wyboru języku w XML**

```
			<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>

```