Localization can help your game reach more people and is fairly easy to do. The best way to do that is having a text file for each languages that is easy to read and edit for people who will translate your text and who might not be technical at all. If you translate your game yourself it's still very practical to have language text files that you can edit with a simple text editor.
The file can be a simple .ini formated file, json or xml. Each format has its pros and cons. I believe ini files are the easiest for human to parse but lack flexibility. Json is easier to parse by computer but hard to understand when you read it. I personnaly like xml, it's still hard to read sometimes but you can always format it in a way that stay simple enough to edit by hand. Also xml has build in support with c# and it's really easy to parse.
For my game In The Shadows I use a simple localization system based on xml files. I am sharing the basics of it here so that you can use it and extend on it if you find it useful. I tried to make the simpliest yet usable localization system I could think of for localizing text in a game here. I guess this tutorial is targeted to people with a moderate level of knowledge of C# and Unity.
XML format
Let's start defining the xml file structure. I will keep the example simple so it's easier to explain. This file will be saved somewhere in your project Assets folder. Save this file as ENGLISH.xml
<?xml version="1.0" encoding="utf-8"?>
<Language LANG="english" ID="0">
<!-- Main Menu -->
<text key="MAIN_TITLE">My Game Title</text>
<text key="CONTINUE">Continue</text>
<text key="START_NEW_GAME">Start New Game</text>
<text key="OPTIONS">Options</text>
<text key="QUIT">Quit</text>
</Language>
The structure here is very simple. Easy to parse and easy to edit manualy. The main root node of the xml file define the language and the ID attribute will be used set the language used ingame. Set a different ID to each language file.
Then I use a "text" node with a "key" attribute and the value of the node is the actual text that we will see in-game. With this system each language has it's own file and you just need to edit the "text" node value for each language. The node "key" attribute stay the same for each language. This file is saved in the Asset folder under the filename "ENGLISH.xml". If you have a file for french name it "FRENCH.xml" and so on.
Now we need a way to read the language xml file content to use the key value pair in the game.
Localization Class
This is a simple class that will read and populate itself with the xml content. You simply need to add this script to an empty GameObject that will stay in all your scenes. Save this file as LocalizationManager.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
public class LocalizationManager : MonoBehaviour
{
public static LocalizationManager Instance { get { return instance; } }
public int currentLanguageID = 0;
[SerializeField]
public List<TextAsset> languageFiles = new List<TextAsset>();
public List<Language> languages = new List<Language>();
private static LocalizationManager instance; // GameSystem local instance
void Awake()
{
instance = this;
DontDestroyOnLoad(this);
// This will read each XML file from the languageFiles list<> and populate the languages list with the data
foreach (TextAsset languageFile in languageFiles)
{
XDocument languageXMLData = XDocument.Parse(languageFile.text);
Language language = new Language();
language.languageID = System.Int32.Parse(languageXMLData.Element("Language").Attribute("ID").Value);
language.languageString = languageXMLData.Element("Language").Attribute("LANG").Value;
foreach (XElement textx in languageXMLData.Element("Language").Elements())
{
TextKeyValue textKeyValue = new TextKeyValue();
textKeyValue.key = textx.Attribute("key").Value;
textKeyValue.value = textx.Value;
language.textKeyValueList.Add(textKeyValue);
}
languages.Add(language);
}
}
// GetText will go through each language in the languages list and return a string matching the key provided
public string GetText(string key)
{
foreach(Language language in languages)
{
if (language.languageID == currentLanguageID)
{
foreach(TextKeyValue textKeyValue in language.textKeyValueList)
{
if (textKeyValue.key == key)
{
return textKeyValue.value;
}
}
}
}
return "Undefined";
}
}
// Simple Class to hold the language metadata
[System.Serializable]
public class Language
{
public string languageString;
public int languageID;
public List<TextKeyValue> textKeyValueList = new List<TextKeyValue>();
}
// Simple class to hold the key/value pair data
[System.Serializable]
public class TextKeyValue
{
public string key;
public string value;
}
Some details about what is going on in this script :
The class use DontDestroyOnLoad(this); so the GameObject holding the script will stay in scene even after you load a new scene. You can add your localization gameobject on the first scene you load or use any other mean of scene management that you already have.
Using [SerializeField] over the languageFile List<> will embed that list in Unity inspector so you can easily drag and drop your XML file in there inside the editor. The script uses [System.Serializable] to embed the language sub classes in Unity inspector to easily visualise the data and help debuging.
The variable currentLanguageID will define which language file to use. You can set this value any way you want, it has to match the ID value from the XML file you want to use. Here it is set to 0 so it will use the English.xml file since its ID attribute is set to 0. You could detect the system language to set this value to the system current language and use a menu / option that the player can change manually.
The script in the inspector. You can drag and drop your xml file in the Language File List<>
Localized UI Text component
You will also need a second script that you add to your GameObject with a UI Text component. Save this file as LocalizationUIText.cs
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof (Text))]
public class LocalizationUIText : MonoBehaviour
{
public string key;
void Start()
{
// Get the string value from localization manager from key
// and set the text component text value to the returned string value
GetComponent<Text>().text = LocalizationManager.Instance.GetText(key);
}
}
This script can be added to any GameObject with a Text component on it. It can be the Text Component of a button or just a label or whatever. This script simply calls GetText() from the LocalizationManager and populate the Text component text value with the string returned.
The [RequireComponent(typeof (Text))] line will add a Text Component to the GameObject to which you added the script, if there is not one already attached to it.
Add the script to a GameObject with a UI Text component. Set Key to the value you want to use in the XML file.
You can now specify the Key to use for this particular Text component text value. The text value will be automaticly changed by the localization script.
You can also use the LocalizationManager in other scripts, if you need to set a string in UI to different value programmaticaly, you can simply call the GetText() method with the Key you want to use.
string localizedString = LocalizationManager.Instance.GetText("START_NEW_GAME");
This will return the string "Start New Game" in the language specified by currentLanguageID from LocalizationManager.
To conclude
Well thats pretty much it. This is a very simple way to handle localization I think, there is ways to make extremely flexible localization system that will also handle images or sound, but I wanted to share a simple way to localize text, while still using an extenal file format that you can easily modify by hand. In The Shadows uses something similar to this altho I do handle image localization as well, but that was out of scope for a simple tutorial. Maybe this example will be useful to you or give you ideas for your own system.