// Adds data that is used to translate
addTranslationTag "Hello", "?????"
// Then translate something
put translate("Hello") // -> ?????
I18n is an internationalization library written in and for LiveCode. It consists of a translation system that allows support for multiple languages in our LiveCode applications.
Note: This library is inspired by i18njs.com, www.i18next.com and other with some modifications.
We actually define all the translations for a language, in a separate JSON file that is passed to the loadLocaleFromFile method. We can also store the array of translations in custom LC properties. To load the saved array into the latter we use the loadLocale handler.
Here is an example of what the JSON file might look like.
{
"id":"ja",
"values":{
"Yes": "??",
"No": "???",
"Ok": "Ok",
"Cancel": "?????"
},
"name":"Japanese"
}
Here is an example of what the Array might look like.
tLongArray["id"] // "ja"
tLongArray["values"]["Yes"] // "??"
tLongArray["values"]["No"] // "???"
...
tLongArray["name"] // Japanese"
When you load the language pack, in the way that is most comfortable for you. Everything will be ready to start using i18n.
// Load the JSON File
loadLocaleFromFile "/locales/ja/ja.json"
// Translate away
put translate("Yes") // -> ??
put translate("No") // -> ???
Pluralisation is how we take care of all the funny rules langauges have when talking about a number of things.
Even if you are not translating your application, i18n can help you make the English on your app sound more natural.
{
"id":"en",
"values":{
"%n comments":[
[0, 0, "%n comments"],
[1, 1, "%n comment"],
[2, null, "%n comments"]
]
},
"name":"English"
}
Instead of just supplying a single translation we supply a series of translations. As we supply each translation we supply the range which tells i18n when to use it.
put translate("%n comments", 0) // -> 0 comments
put translate("%n comments", 1) // -> 1 comment
put translate("%n comments", 2) // -> 2 comments
Notice that the keys passed in for each of the above examples are identical.
In Japanese it is all the same so we only need to provide single entry.
{
"id":"ja",
"values":{
"%n comments":[
[0, null, "%n ????"]
]
},
"name":"Japanese"
}
put translate("%n comments", 0) // -> 0 ????
put translate("%n comments", 1) // -> 1 ????
put translate("%n comments", 2) // -> 2 ????
You can also do more creative things with pluralisation
{
"id":"en",
"values":{
"Due in %n days":[
[null, -2, "Due -%n days ago"],
[-1, -1, "Due Yesterday"],
[0, 0, "Due Today"],
[1, 1, "Due Tomorrow"],
[2, null, "Due in %n days"]
]
},
"name":"English"
}
put translate("Due in %n days", 2) // -> Due in 2 days
put translate("Due in %n days", 1) // -> Due Tomorrow
put translate("Due in %n days", 0) // -> Due Today
put translate("Due in %n days", -1) // -> Due Yesterday
put translate("Due in %n days", -2) // -> Due 2 days ago
Sometimes you want to mix variables into the text you are translating.
local tFormattingArray
put "John" into tFormattingArray["name"]
put translate("Welcome %{name}", tFormattingArray) // -> Welcome John
Just pass in a "formatting" array with the keys matching the replacements.
And then there will be times when you want to pass in extra information to be used when translating. A common example is working with genders.
{
"id":"en",
"values":{
"Yes": "Yes",
"No": "No"
},
"contexts":[
{
"matches":{
"gender":"male"
},
"values":{
"%{name} updated their profile":
"%{name} updated his profile"
}
},
{
"matches":{
"gender":"female"
},
"values":{
"%{name} updated their profile":
"%{name} updated her profile"
}
}
],
"name":"English"
}
And then to use the context...
local tFormattingArray, tContextArray
put "John" into tFormattingArray["name"]
put "male" into tContextArray["gender"]
put translate("%{name} updated their profile", tFormattingArray, tContextArray) // -> John updated his profile
put "Jane" into tFormattingArray["name"]
put "female" into tContextArray["gender"]
put translate("%{name} updated their profile", tFormattingArray, tContextArray) // -> Jane updated her profile
And of course, you can combine all of these concepts to achieve very sophisticated translation.
{
"id":"en",
"values":{
"Yes": "Yes",
"No": "No"
},
"contexts":[
{
"matches":{
"gender":"male"
},
"values":{
"%{name} uploaded %n photos to their %{album} album":[
[0, 0, "%{name} uploaded %n photos to his %{album} album"],
[1, 1, "%{name} uploaded %n photo to his %{album} album"],
[2, null, "%{name} uploaded %n photos to his %{album} album"]
]
}
},
{
"matches":{
"gender":"female"
},
"values":{
"%{name} uploaded %n photos to their %{album} album":[
[0, 0, "%{name} uploaded %n photos to her %{album} album"],
[1, 1, "%{name} uploaded %n photo to her %{album} album"],
[2, null, "%{name} uploaded %n photos to her %{album} album"]
]
}
}
],
"name":"English"
}
local tFormattingArray, tContextArray
put "John" into tFormattingArray["name"]
put "Buck's Night" into tFormattingArray["album"]
put "male" into tContextArray["gender"]
put translate("%{name} uploaded %n photos to their %{album} album", 1, tFormattingArray, tContextArray)
// -> John uploaded 1 photo to his Buck's Night album
put "Jane" into tFormattingArray["name"]
put "Hen's Night" into tFormattingArray["album"]
put "female" into tContextArray["gender"]
put translate("%{name} uploaded %n photos to their %{album} album", 4, tFormattingArray, tContextArray)
// -> Jane uploaded 4 photos to her Hen's Night album
So, that may look like a lot of work for a bit of string concatenation. At this point let me remind you of the original objective of i18n -> internationalisation.
So, here is the equivalent Japanese language file.
{
"id":"ja",
"values":{
"Yes": "??",
"No": "???"
},
"contexts":[
{
"matches":{
"gender":"male"
},
"values":{
"%{name} uploaded %n photos to their %{album} album":[
[0, 0, "%{name}???%{album}???????%n????????????"]
]
}
},
{
"matches":{
"gender":"female"
},
"values":{
"%{name} uploaded %n photos to their %{album} album":[
[0, 0, "%{name}????%{album}???????%n????????????"]
]
}
}
],
"name":"Japanese"
}
And then without changing any of our application code we get a beautifully translated sentence with pluralisation, formatting and context.
You may also notice that the word order in Japanese is completely different compared to English. This is why it is so important to use formatting rather than string concatenation.
local tFormattingArray, tContextArray
put "John" into tFormattingArray["name"]
put "Buck's Night" into tFormattingArray["album"]
put "male" into tContextArray["gender"]
put translate("%{name} uploaded %n photos to their %{album} album", 1, tFormattingArray, tContextArray)
// -> John???Buck's Night???????1????????????
put "Jane" into tFormattingArray["name"]
put "Hen's Night" into tFormattingArray["album"]
put "female" into tContextArray["gender"]
put translate("%{name} uploaded %n photos to their %{album} album", 4, tFormattingArray, tContextArray)
// -> Jane????Hen's Night???????4????????????
In all of the examples above I have used the actual English text as the keys for the translation. You don't have to do it this way. It is perfectly legitimate to do the following.
{
"id":"en",
"values":{
"loginFail":
"There was an error logging in, please check your email and password."
},
"name":"English"
}
And then you would grab the translation like so.
put translate("loginFail")
// -> There was an error logging in, please check your email and password.
Advantages:
You can also provide a default string in case the key can not be found.
put translate("emailRequired", "We require an email when signing up.")
// -> We require an email when signing up.
Nota: This file is a modified and adapted version of what can be found at i18njs