Translations is one of the most common things we all need in our games. The assets store is crowded with solutions but I always prefer to use my own stuff for simple tasks like this one.
To handle the text I'm using json files, which are just a simple txt file even if you save them as .json.
In order to create our json file I use http://jsonlint.com/ to parse the file and make sure the structure is correct, that will avoid loading problems later...
I'm using a key and an array of "StringsToRead" to handle everything in a dictionary.
The structure of my json is this one:
{
"TranslatedStrings": [
{
"key": "tapToPlay",
"StringsToRead": {
"ENGLISH": {
"Text1": "Press any button to play",
"Text2": ""
},
"SPANISH": {
"Text1": "Apreta un boton para jugar",
"Text2": ""
},
"ITALIAN": {
"Text1": "Premere un tasto per giocare",
"Text2": ""
},
"DEUTSCH": {
"Text1": "Drücke einen beliebigen Knopf zum Starten",
"Text2": ""
},
"JAPANESE": {
"Text1": "ボタンを押してスタート",
"Text2": ""
}
}
},
{
"key": "Language",
"StringsToRead": {
"ENGLISH": {
"Text1": "English",
"Text2": ""
},
"SPANISH": {
"Text1": "Español",
"Text2": ""
},
"ITALIAN": {
"Text1": "Italiano",
"Text2": ""
},
"DEUTSCH": {
"Text1": "Deutsch",
"Text2": ""
},
"FRENCH": {
"Text1": "Français",
"Text2": ""
},
"JAPANESE": {
"Text1": "日本語",
"Text2": ""
}
}
}
]
}
As you can see we have a main key called "key" which contains the label used to identify our string. And then we have the strings for each language we want to support.
In this case I have only 2 strings (Text1 and Text2), but you can add more if you need to handle more text than that.
Remember to save the file as .txt in order to be used with unity easily
Now it's time to get some code, for start I'm using a simple enum, this is more than enough for my needs.
public enum LanguageType {
ENGLISH = 0,
SPANISH = 1,
FRENCH = 2,
CATALAN = 3,
ITALIAN = 4,
DEUTSCH = 5,
JAPANESE = 6,
NONE = 7,
MAX = 8
};
Some may argue that enum is a really shitty solution, but for a simple task like this one it fits perfectly my needs and I can add more languages in a future if I need them.
Anyways, let's create the class to handle this,
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LanguageManager { | |
public class LanguageData | |
{ | |
public Dictionary<LanguageType, List<string>> textStringsList = new Dictionary<LanguageType, List<string>>(); | |
} | |
//our dictionary to access the strings loaded | |
Dictionary<string, LanguageData> localeStrings; | |
//our local language selected | |
LanguageType languageSelected; | |
public System.Action EvntLanguageChanged; |
public Dictionary<LanguageType, List<string>> textStringsList = new Dictionary<LanguageType, List<string>>();
First of all we will store the json definition within the class LanguageData, which is a dictionary with the enum (to know what language are we referring to) and the list of strings for that language.
That small class will handle our json file, now we define the dictionary to be used which contains the string key and the LanguageData to access the information of our json file. And everything gets packed in this nice dictionary:
Dictionary<string, LanguageData> localeStrings;
I'm also storing the enum to know our current language selected, and finally we add a simple action to notify other classes that the language has changed, for example you change the language in your options screen and you need to refresh the screen with the new and updated labels.
LanguageType languageSelected;
public System.Action EvntLanguageChanged;
Nothing really complex here, just these 2 vars plus the action/delegate to notify anyone about changes.
Our load function looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void LoadLanguageFile(string filename, LanguageType _mylanguage, System.Action<bool> callback) | |
{ | |
languageSelected = _mylanguage; | |
loadTextFileJSON(filename, success => { | |
if (success) { | |
callback(true); | |
} else { | |
callback(false); | |
} | |
}); | |
} |
Now the core of the manager is the load function itself, it's private because we use the above function to let people to load something, but we keep private the implementation. Let's take a look:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#region parse json file | |
//the parsing function itself, we will read the json file and store the results on our dictionary | |
void loadTextFileJSON(string filename, System.Action<bool> callback) { | |
//clean any previous stuff | |
ClearAllStrings(); | |
// create reader | |
TextAsset txt; | |
//check first if that language file exists | |
//the resources must be in the Resources folder within your project files | |
txt = Resources.Load("GameConfigFiles/TextAssets/" + filename, typeof(TextAsset)) as TextAsset; | |
if (txt == null) { | |
Debug.LogError("Could not find file in Resources/GameConfigFiles/Dialogs/: " + filename); | |
callback(false); | |
Resources.UnloadUnusedAssets(); | |
return; | |
} | |
//at this point the file was loaded, we can create the object now | |
localeStrings = new Dictionary<string, LanguageData>(); | |
// | |
// | |
//loop the json file and parse all the strings into our dictionary | |
//to select a text later during the game is that easy as use this | |
// | |
//string mytext = languageManager.GetLocaleString("helloKey"); | |
//or any other string based on your json file | |
// | |
// | |
//var decodedHash = MiniJSON.Json.Deserialize (txt.text); | |
var decodedHash = (Hashtable)MiniJSONLanguages.MiniJSON.jsonDecode(txt.text); | |
ArrayList obj = (ArrayList)decodedHash["TranslatedStrings"]; | |
for (int list = 0; list < obj.Count; list++) { | |
Hashtable currentEventParsed = (Hashtable)obj[list]; | |
LanguageData newData = new LanguageData(); | |
string key = currentEventParsed["key"].ToString(); | |
var itemInternal = (Hashtable)((Hashtable)currentEventParsed["StringsToRead"]); | |
//add all languages in your struct languages, add more if needed | |
for (int i = 0; i < (int)LanguageType.MAX; i++) { | |
newData.textStringsList.Add((LanguageType)i, ParseLanguage(itemInternal, (LanguageType)i)); | |
} | |
localeStrings.Add(key, newData); | |
} | |
// clean up a bit after loading all our text | |
Resources.UnloadUnusedAssets(); | |
txt = null; | |
callback(true); | |
} | |
List<string> ParseLanguage(Hashtable hashToParse, LanguageType language) { | |
var itemsToParse = (Hashtable)hashToParse[language.ToString()]; | |
if (itemsToParse != null) { | |
List<string> stringsFound = new List<string>(); | |
foreach (string s in itemsToParse.Values) { | |
stringsFound.Add(s); | |
} | |
return stringsFound; | |
} | |
return null; | |
} | |
#endregion |
As you can see in the code I'm using a folder within Resources called GameConfigFiles with a subfolder called TextAssets, which is where I store all my json files.
So the final path is "Resources/GameConfigFiles/TextAssets", no need to say that you can change that path to something of your liking.
Like I mentioned early, the file it's just a .txt file saved as such in order to be recognised by unity and loaded as a TextAsset. If you import the file as .json unity will not be able to read it.
The final piece of code important is the function that returns the string from the dictionary:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public string GetLocaleString(string key) { | |
if (!localeStrings.ContainsKey(key)) { | |
return null; | |
} else { | |
LanguageData languageData = localeStrings[key]; | |
List<string> allStrings = languageData.textStringsList[languageSelected]; | |
string localeString = allStrings[0]; | |
return localeString; | |
} | |
} |
I'm not gonna explain here the rest of the code (check the project included) because the remaining code is dead simple, just some public functions to change language, and some general getters, nothing you've never seen before.
The project contains a small menu class to show you how to load and init the manager.
So this is it, this is our simplistic language manager. As you can see it's not difficult at all to create something like this, of course you can expand this to read xls files or whatever you might need. In my case I prefer this classic json/txt files, but it's a matter of personal preferences.
Here's the link to the project
No comments:
Post a Comment