Localization support for Components
Opened this issue · 0 comments
Results of the research for the Localization support on Lumin, iOS and Android.
TL;DR in my opinion we have two options for Lumin:
- (In big shortcut) Use magic-script-cli to send JSON files with translations to cloud and receive *.dat file with ICU translations that will be used by Lumin 'automagically' + add magic-script-components-lumin support for LocalizedText
- Use JS library to utilize translations in the runtime (need to have some discussion on this one)
And two for React Native:
- Use
react-native-localize
underneath and add LocalizedText node - Use JS library to utilize translations in the runtime (need to have some discussion on this one)
Idea no. 1
Localization on Lumin
Right now, according to the project that @kpiascik shared with me last week, there are few steps we need to follow, to create properly defined locale.dat
file that will be used by Lumin to provide localization support based on the System Locale.
To create such file, properly formatted with ICU standard, the project uses several Linux available tools between *.xlf <-> locale.dat:
Building & Compilation
- First of all we have to have *.xliff files, where
*
stands for ICU country code, such asen
,de
etc. We also need (probably) theroot.xlf
file with default translation - Then, we need to execute
XLIFF2ICU.jar
tool, which converts*.xlf
to*.txt
files with ICU standard - Next, the
genrb
tool converts*.txt
files into*.res
files, and those are packaged withpkgdata
tool intolocale.dat
file. (genrb
andpkgdata
are Linux tools)
Source code
- The Lumin SDK provide special factory methods for UiText node,
CreateLocalized
andCreateLocalizedEclipseLabel
which have Prism, Key and LocaleHelper.Param parameters - The LocaleHelper class is initialized by default with corresponding values:
locale: String = 'en'
path: String = 'res'
file: String = 'locale'
- The LocaleHelper.Param is used for passing replaceable values for texts, f.e.
let param = {};
param["QUANTITY"] = new utils.LocaleHelper.Param(10);
new ui.UiText.CreateLocalized(prism, "apples", param)
where apples
is key for translated String in locale.dat
file, f.e. Today I bought {{QUANTITY}} apples
;
Lumin components
Also the Lumin Magic Script Components need to have support for localization, so either we have a new <LocalizedString />
component, or some kind of property flag translatable: true/false
for Text, EditText etc.
ReactNative components
If we follow this idea, we should also add support for ReactNative platform with this approach, so having we have to utilize JSON translation files and add middle layer to provide proper Component and properties handling
Conclusion
After digging more into it, I haven't found easy way to utilize several steps for generating locale.dat
file with MagicScript CLI with ICU support. Even if we find several equivalent tools we can use on Windows & MacOS, we can't expect developers to have all of that installed. On the other hand, if we somehow add the tools to the MagicScript CLI itself, the amount of the code and unit tests to cover will be a huge deal.
If we want to follow this path, my idea is to add mechanism for generating the locale.dat
file in the cloud and download this file to res/
directory. After that we still need to add the support for MagicScript Components Lumin and MagicScript Components React Native
Idea no. 2
i18next
After finding the bottleneck in the first solution, I started looking into JS libraries and what possibilities we have. The i18next library has one mechanism that brought my attention - Creating own plugins
With this approach, there is a chance that we can provide own plugin with MagicScript Components Lumin and MagicScript Components ReactNative (probably we can utilize already created one). Also there is ICU plugin available: So the Localization architecture with this library could be something like this:
MagicScript Components
import i18next from 'i18next';
import ICU from "i18next-icu";
class MagicLocalization {
constructor(nativeBackend) {
this.nativeBackend = nativeBackend; // here we are providing proper plugin from the magic-script-components-* library and we are creating an object in either
}
static init(translations) {
i18next
.use(ICU)
.use(nativeBackend)
.init({...})
}
static t(key, params) {
return i18next.t(key, params)
}
static changeLanguage(lng, callback) {
i18next.changeLanguage(lng, callback)
}
}
App source code
import React from 'react';
import { View, Text, MagicLocalization } from 'magic-script-components';
import { en, fr, de } from './localization.js'
export default class MyApp extends React.Component {
constructor (props) {
super(props);
this.state = {
message: props.message
};
MagicLocalization.init({ en, fr, de })
}
componentDidMount() {
this.state = {
apples: 10
}
}
changeLanguage() {
MagicLocalization.changeLanguage('de', (err, t) => {
this.state = { germanText: t("key", { apples: 10 })}
});
}
render () {
const { apples } = state;
return (
<View name="main-view">
<Text textSize={0.1} localPosition={[-0.3, 0, 0]}>
MagicLocalization.t("key", { param: apples })
</Text>
<Text textSize={0.1} localPosition={[-0.3, 1, 0]}>
{this.state.germanText}
</Text>
</View>
);
}
}
Conclusion
With this approach we have full support of the framework, probably ready to use plugin for ReactNative, the one thing we need to write is the plugin for Lumin platform to retreive the language.
But also we have to consider if we want to support changing the system language in the runtime and going back to the app (both RN and Lumin). There are callbacks in Lumin SDK to onLocalChanged()
and hooks to subscribe/unsubscribe to that event. If we consider this functionality, I will check RN support as well.