From d7d76604e49375a5f4cd6257f29aa4e25e62522a Mon Sep 17 00:00:00 2001 From: Vinayak Nayar Date: Fri, 7 Jul 2023 12:53:37 +0530 Subject: [PATCH] docs: migration guide for i18next --- docs/i18nmigrationguide/guide_vanillajs.md | 119 +++++++++++++++++++++ docs/i18nmigrationguide/guide_vuejs.md | 111 +++++++++++++++++++ docs/webL10n2i18n.md | 6 ++ 3 files changed, 236 insertions(+) create mode 100644 docs/i18nmigrationguide/guide_vanillajs.md create mode 100644 docs/i18nmigrationguide/guide_vuejs.md create mode 100644 docs/webL10n2i18n.md diff --git a/docs/i18nmigrationguide/guide_vanillajs.md b/docs/i18nmigrationguide/guide_vanillajs.md new file mode 100644 index 000000000..c65aa26c4 --- /dev/null +++ b/docs/i18nmigrationguide/guide_vanillajs.md @@ -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. +``` + +``` + +That's it, happy contributing. \ No newline at end of file diff --git a/docs/i18nmigrationguide/guide_vuejs.md b/docs/i18nmigrationguide/guide_vuejs.md new file mode 100644 index 000000000..7cb27e53b --- /dev/null +++ b/docs/i18nmigrationguide/guide_vuejs.md @@ -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. +``` + +``` + +That's it, happy contributing. \ No newline at end of file diff --git a/docs/webL10n2i18n.md b/docs/webL10n2i18n.md new file mode 100644 index 000000000..c6325c608 --- /dev/null +++ b/docs/webL10n2i18n.md @@ -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) \ No newline at end of file