Skip to content

Commit

Permalink
Merge branch 'pr/1377' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Lionel Laské committed Jul 9, 2023
2 parents 9f7c8eb + 70a5fb2 commit 288bf3c
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 0 deletions.
119 changes: 119 additions & 0 deletions docs/i18nmigrationguide/guide_vanillajs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Migrate Vue.JS Activity to i18next internationalization framework
To migrate a Vanilla.JS Activity you would need to follow the steps mentioned below.

## Step1: Downloading dependencies
To add the i18next as a dependency you would need to download i18next min.js into the lib directory of the activity. You can download it from [here](../../activities/Measure.activity/lib/i18next.min.js).

You would also need to add axios min.js into the lib directory. You can download it from [here](../../activities/Measure.activity/lib/axios.min.js).

## Step2: Convert ini file to json
Currently translation strings are stored in ini files but to adapt it to i18next format we need to convert it to seperate json files.

To achieve that you can use [this script](https://github.com/llaske/l10nstudy/blob/master/ini2json.js).

## Step3: Add new l10n.js file
Create a new file named l10n.js in lib directory and add this code.
```
define(['i18next.min', 'axios.min'], function (i18next, axios) {
const l10n = {};
let initialized = false;
l10n.init = async (lang) => {
await i18next.init({
lng: lang,
debug: true,
fallbackLng: "en",
resources: {}
}).then(() => {
l10n.switchTo(lang);
});
};
l10n.get = (key) => {
return i18next.t(key);
};
l10n.loadLanguageResource = (lang) => {
return new Promise((resolve, reject) => {
axios.get("./locales/" + lang + ".json").then((response) => {
resolve(response.data);
}).catch((error) => {
console.log("Failed to load " + lang + " language: " + error);
resolve(null); // Resolve with null to indicate failure
});
});
};
l10n.switchTo = (lang) => {
if (!i18next.hasResourceBundle(lang, "translation")) {
console.log("Loading " + lang + " language");
l10n.loadLanguageResource(lang).then((locales) => {
if (locales !== null) {
i18next.addResourceBundle(lang, "translation", locales);
} else {
l10n.init("en");
}
i18next.changeLanguage(lang);
initialized = true;
triggerLocalizedEvent();
});
} else {
i18next.changeLanguage(lang);
initialized = true;
triggerLocalizedEvent();
}
};
function triggerLocalizedEvent() {
const event = new Event("localized");
window.dispatchEvent(event);
};
return l10n;
});
```
Brief explanation of methods:


- `l10n.init(lang)`: Initializes the i18next library with the specified language. It then calls `l10n.switchTo(lang)` to handle switching to the specified language.

- `l10n.get(key)`: Returns the translated string for the given translation key using i18next.

- `l10n.loadLanguageResource(lang)`: Fetches the language resource file for the specified language using axios. It returns a promise that resolves with the translation resources if the request is successful. If there is an error, it logs an error message and resolves with `null` to indicate failure.

- `l10n.switchTo(lang)`: Handles switching to the specified language. If the language resource bundle is not already loaded, it calls `l10n.loadLanguageResource(lang)` to load the resource. If the resource is successfully loaded, it adds it to i18next using `i18next.addResourceBundle`. If the resource is not available or loading fails, it falls back to initializing the default language ("en") using `l10n.init("en")`. After setting the language, it updates the `initialized` flag, and triggers a custom "localized" event.

- `triggerLocalizedEvent()`: Triggers a custom event named "localized" by creating a new event object and dispatching it on the `window` object. This event can be used by other parts of the application to respond to localization changes.

## Step4: Modify the js/activity.js
Now you need to replace the webl10n code with i18next.
You can refer to this [code](https://github.com/llaske/sugarizer/pull/1371/files#diff-b2447869bafe96b01d12ef5db78589d5a1aa490d31188e694300e3f674211d7fR3) and spin up a similar instance.

## Step5: Remove webl10n reference from sugarweb
Navigate to lib/sugarweb/activity.js and first remove webl10n code from requirejs statement like this.

```
define(["sugar-web/activity/shortcut",
"sugar-web/bus",
"sugar-web/env",
"sugar-web/datastore",
"sugar-web/presence",
"sugar-web/graphics/icon",
"sugar-web/graphics/activitypalette"],
function (shortcut, bus, env, datastore, presence, icon, activitypalette) {
```
Also remove ```l10n.start();``` activity.setup() function.

Now remove ``` "webL10n": "lib/webL10n" ``` from test/loader.js and ``` "webL10n": "github:sugarlabs/webL10n",``` from sugar-web/package.json.

## Step6: Deleting unnecessary files
Delete files locale.ini (ini file) and lib/webL10n.js.

Remove statement mentioned below from index.html file.
```
<link rel="prefetch" type="application/l10n" href="locale.ini" />
```

That's it, happy contributing.
111 changes: 111 additions & 0 deletions docs/i18nmigrationguide/guide_vuejs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Migrate Vue.JS Activity to i18next internationalization framework
To migrate a Vue.JS Activity you would need to follow certain steps.

## Step1: Downloading dependencies
To add the i18next as a dependency you would need to download i18next min.js into the lib directory of the activity. You can download it from [here](../../activities/Measure.activity/lib/i18next.min.js).

You would also need to add axios min.js into the lib directory. You can download it from [here](../../activities/Measure.activity/lib/axios.min.js).

## Step2: Convert ini file to json
Currently translation strings are stored in ini files but to adapt it to i18next format we need to convert it to seperate json files.

To achieve that you can use the following [script](https://github.com/llaske/l10nstudy/blob/master/ini2json.js).

## Step3: Modifying SugarL10n component
**3.1. Add methods**
Add two methods loadLanguageFile and subscribeLanguageChange in the methods block.
```
loadLanguageFile: function (language) {
const vm = this;
requirejs(['lib/i18next.min.js', 'lib/axios.min.js'], function (i18next, axios) {
axios.get(`./locales/${language}.json`).then((response) => {
i18next.init(
{
lng: language,
fallbackLng: 'en',
debug: true,
resources: {
[language]: {
translation: response.data
}
},
},
() => {
vm.l10n = i18next;
vm.code = i18next.language;
vm.dictionary = i18next.getResourceBundle(i18next.language, 'translation');
vm.subscribeLanguageChange();
vm.activityInitialized = true;
}
);
}).catch((error) => {
vm.loadLanguageFile('en'); // Load default language
console.log(error);
});
});
},
subscribeLanguageChange: function () {
const vm = this;
requirejs(['lib/i18next.min.js'], function (i18next) {
i18next.on('languageChanged', (lng) => {
vm.code = lng;
vm.dictionary = i18next.getResourceBundle(lng, 'translation'); // Update dictionary with new language
vm.$emit('localized');
vm.eventReceived = true;
});
});
},
```
Here, *loadLanguageFile* Method is responsible for loading the language file and initializing the i18next. After initializing i18next, the method updates various properties of the Vue instance. The l10n property is assigned the initialized i18next instance, code is updated with the current language code obtained from i18next.language, and dictionary is set with the translation resource bundle for the current language, accessed using i18next.getResourceBundle

*subscribeLanguageFile* Method sets up a listener for the 'languageChanged' event in i18next. This event is triggered whenever the language is changed in i18next. The listener updates the code property with the new language code and updates the dictionary property with the updated translation resource bundle for the new language.

**3.2. Update code in mounted hook**
Now in mounted hook block remove the webl10n reference like this:
```
requirejs(['sugar-web/env'], function (env) {
```
Remove webl10n code
```
webL10n.language.code = language;
window.addEventListener("localized", function () {
if (!vm.eventReceived) {
vm.code = language;
vm.dictionary = vm.l10n.dictionary;
} else if (webL10n.language.code != language) {
webL10n.language.code = language;
}
});
```
And replace it with the loadLanguageFile method call.
```
vm.loadLanguageFile(language);
```
## Step4: Remove webl10n reference from sugarweb
Navigate to lib/sugarweb/activity.js and first remove webl10n code from requirejs statement like this.

```
define(["sugar-web/activity/shortcut",
"sugar-web/bus",
"sugar-web/env",
"sugar-web/datastore",
"sugar-web/presence",
"sugar-web/graphics/icon",
"sugar-web/graphics/activitypalette"],
function (shortcut, bus, env, datastore, presence, icon, activitypalette) {
```
Also remove ```l10n.start();``` activity.setup() function.

Now remove ``` "webL10n": "lib/webL10n" ``` from test/loader.js and ``` "webL10n": "github:sugarlabs/webL10n",``` from sugar-web/package.json.

## Step5: Deleting unnecessary files
Delete files locale.ini (ini file) and lib/webL10n.js.

Remove statement mentioned below from index.html file.
```
<link rel="prefetch" type="application/l10n" href="locale.ini" />
```

That's it, happy contributing.
6 changes: 6 additions & 0 deletions docs/webL10n2i18n.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Migrate Activities to i18next internationalization framework
Currently Sugarizer activity relies on [WebL10n localizaton framework](https://github.com/fabi1cazenave/webL10n) which is outdated and no longer actively maintained. In order to ensure continued support and modern localization capabilities, it is recommended to migrate the activities to the [i18next internationalization framework](https://www.i18next.com/).

Activities either use vanilla javascript or Vue.JS depending on that choose your path.
* [Vue.JS](i18nmigrationguide/guide_vuejs.md)
* [Vanilla JS](i18nmigrationguide/guide_vanillajs.md)

0 comments on commit 288bf3c

Please sign in to comment.