Wowpedia

We have moved to Warcraft Wiki. Click here for information and the new URL.

READ MORE

Wowpedia
Advertisement

The locale is what makes it possible to know what language the interface is using and thus determine which specific code is going to be executed in a localized addon.

typically, this is used in a GetLocale block:

Code:

   if (GetLocale() == "frFR") then
       -- things for the french client
   else
       -- for the rest, usually English since it's the default language
   end

Variables

The whole deal doing a successful addon localization is simply using the translated interface texts (as translated ingame) in the language you want to enable for the element you want to use (texts, item names, spells, etc.).

So, whenever texts (well almost) are used, they should be replaced by variables which will be set in a GetLocale block in order to give them the correct value for each locale

Currently, it is best to put these in a file separate from the main code (usually name localization.lua for easy reference but any name will do).

That file must then be declared in the XML part of the addon, just before the other scripts that compose it, example:


Code:

   <Script file="localization.lua"/>
   <Script file="GatherIcons.lua"/>

If this is a modification on an existing addon and you want to submit it to the author, it's better to use English for the variables names and to prefix them with the addon name (to prevent conflicts with existing variables from the game or from other addons), keep the original strings and put them in variables too so that the author can match them with his original code (don't forget to comment).

Example:

Code:

   if (skillName == "Herbalism") then
       GatherSkills.herbs = skillRank;
   elseif (skillName == "Mining") then
       GatherSkills.mining = skillRank;
   end

becomes:

Code:

   if (skillName == GATHERER_TRADE_HERBALISM) then
       GatherSkills.herbs = skillRank;
   elseif (skillName == GATHERER_TRADE_MINING) then
       GatherSkills.mining = skillRank;
   end

the GATHERER_TRADE_HERBALISM and GATHERER_TRADE_MINING variables are declared in the localization.lua file as follow:


Code:

   if( GetLocale() == "frFR" ) then
       -- french part
       GATHERER_TRADE_HERBALISM="Botanique";
       GATHERER_TRADE_MINING="Minage";
   else
       -- default, English
       GATHERER_TRADE_HERBALISM="Herbalism";
       GATHERER_TRADE_MINING="Mining";
   end

As you could see in the example, you simply replace the original string between double quotes by the corresponding variable.

Warning: If you need to use a localized string in the xml files, you need to keep the double quotes around the variable name.

Example:

Code:

   [FontString name="bcTM_PopupTitle" inherits="GameFontHighlight" text="TRACKMENU_TOOLTIP_MENU_TITLE"]

will allow to use the content of the TRACKMENU_TOOLTIP_MENU_TITLE variable for this xml element.

Grammar and case sensitivity

This part is usually only useful if the addon has to process sentences (extracted from the chat windows), such as in processing to keep specific parts of the string. In that case it can be necessary to add a GetLocale block since words are not always ordered in the same manner in various languages.

Example:

Quote:

   copper vein => veine de cuivre  


another thing linked to this is that since word placement may be different, uppercases may be applied on different words.

Examples:

Quote:

   Damaged chest => Coffre endommagé
   Small damaged chest => Petit coffre endommagé

Accents, Special Chars and Umlauts

This is somewhat delicate as wow deals with accents directly as unicode which means 2 things:

First, it will not recognize an accent in a localized lua textfile if it's not in unicode and thus will not be able to match it to the accent that can be seen in the game interface.

In the previous example, if you try to match "endommagé" with the same string provided by the game, it will fail.

Second, since these are unicode char, they're actually coded on several distinct characters (usually 2), which has to be taken into account for string manipulations based on length.

The use of and UTF-8 compatible editor is a must if you're dealing with accented characters, otherwise they'll probably be converted to the local charset equivalent which will not match the ingame data.

For a list of codes corresponding to various accentuated characters see http://www.allegro-c.de/unicode/zcodes.htm (page is in German but the table allows to find what's needed quite easily).

For our earlier chest example, a string that will match the ingame data would look like:

Quote:

   Coffre endommag\195\169  

Some corresponding codes :

   à : \195\160    è : \195\168    ì : \195\172    ò : \195\178    ù : \195\185
   á : \195\161    é : \195\169    í : \195\173    ó : \195\179    ú : \195\186
   â : \195\162    ê : \195\170    î : \195\174    ô : \195\180    û : \195\187
   ã : \195\163    ë : \195\171    ï : \195\175    õ : \195\181    ü : \195\188
   ä : \195\164                    ñ : \195\177    ö : \195\182
   æ : \195\166                                    ø : \195\184
   ç : \195\167                                    œ : \197\147
   
   Ä : \195\132
   Ö : \195\150
   Ü : \195\156
   ß : \195\159

Unicode

WOW can recognize unicode characters directly in any .lua file, if this file is saved in unicode format. The exact format to use is "utf-8".

In order to edit any text file in utf-8, you need a text editor which supports utf-8. By default, the normal encoding for text files is usually iso8859-1. Some older editors might use the ANSI character set instead though.

My preference in text file editing goes to EditPad Lite. By default, it edits using the ANSI charset, but it can convert the whole buffer to UTF-8 using the Convert/Unicode/ANSI->UTF-8 function. There are of course lots of other commercial software around which are able to save files in UTF-8 encoding.

If you don't know the UTF-8 sequence, you can convert it from UTF-16 manually by converting it to Binary and using the following table (ref. [1]):

 UTF-16 Code Unit  1st Byte  2nd Byte  3rd Byte
 000000000xxxxxxx  0xxxxxxx
 00000yyyyyxxxxxx  110yyyyy  10xxxxxx
 zzzzyyyyyyxxxxxx  1110zzzz  10yyyyyy  10xxxxxx

After you convert the UTF-16 code to Binary (remember it starts out in Hexadecimal), separate the bytes out, then convert them Decimal. You then type them as sequences followed by "\". Ex: \195\145

The latest Unicode charts can be found here: http://www.unicode.org/charts/ Remember that not all codes are supported by the WoW client.

The single quote case

Single quote is a bit of a special case, it also has it's equivalent in unicode but this is actually not always the case ingame, has to be checked experimentally.

Examples:

   D\195\169couverte d'herbes ==> that match with game data
   Veine d'argent ==> this one used not to match, not true anymore in 1.2.2-4196 though.  
   Nefarian's Lair ==> the apostrophe is actually the UTF8 sequence \239\191\189

As a guideline, especially check anything coming from chat windows since that's where the special coding seems (might not be true anymore for 1.2.2-4196 versions and up) to be used most often instead of the standard single quote.

Note: the Unicode equivalent of the single quote is encoded with 3 UTF-8 bytes:

   ’ : \226\128\153 (UCS 2019 -- RIGHT SINGLE QUOTATION MARK)

It can for instance be found in the frFR locale game data for the Cri d’intimidation warrior ability (at least in 1.8.4).

Check the GlobalStrings.lua

For some strings, Blizzard does the job for you, check the contents of the GlobalStrings.lua file (extracted from the Interface.mpq for the basis and from the patch.mpq for the latest up to date). This file contains predefined variables whose values are directly in the client's language.

A word on table ordering

Most tables in WoW are organized alphabetically which means that the order the entries appear in the table depends on the translation of the individual items that compose it.

Example: zone names in English clients (UK and US) for Kalimdor continent:

   Ashenvale, Azshara, Darkshore, etc.

in the french client you have the following order:

   Ashenvale, Azshara, Un'Goro Crater (translated as "Cratère d'Un'Goro"), etc.

3rd entry represent a different zone.

A static table supplying, say scaling values for the minimap based on zone order, would have to be "localized" in the sense that the table need to be sorted differently according to the client's locale to give correct values.

Spells in the spellbook

The same holds true for spells in the spellbook; they are in alphabetical order based on the locale.

The first 8 spells in a hunter's spellbook:

This hunter has Enchanting and Herbalist profs.
-- Get spells 1-8
for n = 1, 8 do
    SpellsTable[n] = GetSpellName(n)
end

enUS client

SpellsTable = {
    "Attack", -- [1]
    "Basic Campfire", -- [2]
    "Beast Training", -- [3]
    "Cooking", -- [4]
    "Disenchant", -- [5]
    "Dodge", -- [6]
    "Dual Wield", -- [7]
    "Enchanting", -- [8]
}

esES client

SpellsTable = {
    "Adiestramiento de bestias", -- [1] (Beast Training)
    "Atacar", -- [2] (Attack)
    "Buscar hierbas", -- [3] (Find Herbs)
    "Cocina", -- [4] (Cooking)
    "Desencantar", -- [5] (Disenchant)
    "Doble empuñadura", -- [6] (Multi-Shot)
    "Encantamiento", -- [7] (Enchanting)
    "Espíritu de fuego fatuo", -- [8] (Basic Campfire)
}


In other words, GetSpellName(n) doesn't return the same value on all clients.

Moderate yourself

Not everything must have a translation, some things will not work anymore if they are translated. A classical example would be lua objects name (variables usually, functions occasionally) or xml objects (components names defined in the .xml file) that are reconstructed in order to be accessed.

Example:

   getglobal(EN_DUR_FRAME..FrameName.."SlotLeft"):Show();

Here, we're getting an XML object to display it, the object name itself being rebuilt by appending together the variables EN_DUR_FRAME, FrameName and the "SlotLeft" character string.

Since the objects names are hard-coded in the .xml file, a translation in one of the variable used will result in a failure getting the objet in localized clients.

Final touch, the .toc file

The .toc file allows localization by adding entries postfixed by the locale they should appear on:

Example:

Quote:

    1. Title: Gatherer
    2. Title-frFR: Gatherer (en Francais, in French)
    3. Notes: Gatherer, displays stuff you gather in your minimap
    4. Notes-frFR: Gatherer, affiche les objets recoltes dans la minicarte


The -frFR postfix indicate to a French localized client that a translation is available and should be preferred to the default (which most of the time correspond to English), if no "localized" entry exists for a specific client, the default one is displayed.

Addendum (patch 1.4.0): in patch 1.4.0 a slightly cryptic comment from Slouken on the US board announce that they left the decimal point to the European value (ie a comma). The impact of this is that table index that uses a strings containing a dot do not work anymore, since that behaviour didn't exist before patch 1.4.0 on European version, caution would be avoid using dots in text index for table.

Example: prior to patch this table was fine.

   ["FR 1.4.0"] = ...

Post patch, trying to get the index content would result in an error because it was translated by the game engine to "FR 1,4,0" which of course doesn't match the index up there.

Some tips

Here are some of Sarf's tips that can be used to keep addons localized during maintenance and updates.

See the Localization Information page for lists of known localized strings (zones, channels, etc.)

Do not use item names

Q: How do you refer to items if not by their names?

A: By using their item ids.

Thanks to CastOptions.lua for the origin of this code snippet:

function MyAddOn_ExtractItemID(link)
	_, _, id = string.find(link, "Hitem:(.+):%d+:%d+:%d+%\124");
	return id;
end

If you are wondering how to get item links, check out the Global API - Item Functions.

Use predefined, global names for skills

By using predefined, global names for skills you can change the skill on-the-fly as well as not have to care what the name actually is!

Do remember to keep rank and spell name separate, however. This allows for easy integration with GetSpellName(spellID, "bookType").

Separate localization code/variables from normal code

Usually, one file per language / locale is about right. The naming convention is often localization<SEPERATOR><LOCALE>.lua

Examples (German localization):

localization.de.lua

or

localization_de.lua

You can have empty localization file, or you can just put this into them:

if ( GetLocale() == "deDE" ) then
    -- todo
end

 

shortcut iconSee also: HOWTO: Localize an addon easily
Advertisement