OJET: Internationalization - Localization
In this post I will bring my thoughs and first experience by making an Oracle JET Application ready for Multilanguage. Probably, not the best practices yet, but it is an initial step
My application (ojet-internationalization) can be found in:
https://github.com/DanielMerchan/ojet-examples
This is well explained in the official documentation: https://docs.oracle.com/en/middleware/jet/6.1/develop/internationalizing-and-localizing-applications.html
But, in this post I will bring what I did and my feelings.
My application (ojet-internationalization) can be found in:
https://github.com/DanielMerchan/ojet-examples
How to make your Oracle JET Application ready for Multilanguage?
This is well explained in the official documentation: https://docs.oracle.com/en/middleware/jet/6.1/develop/internationalizing-and-localizing-applications.htmlBut, in this post I will bring what I did and my feelings.
- I created a Factory utility Module called Languages.js in js/utils. This factory contains an array of the supported languages in Oracle JET and some wrapper methods I used / un-used meanwhile my app was changing.
setLocale: (newLocale, langCallback) => { console.log(`Old locale: ${Languages.getCurrentLocale()} and New Locale: ${newLocale}`); oj.Config.setLocale(newLocale, () => { $('html').attr('lang', newLocale); if (newLocale.startsWith('ar')) { $('html').attr('dir', 'rtl'); } else { $('html').attr('dir', 'ltr'); } langCallback(); // Dispatch a JS Event to allow other modules to subscribe and change translations accordingly. document.dispatchEvent(new CustomEvent('localeChangedEvent')); }); },
- I added the utils/Languages to the define and function blocks of the appController.js to be able to use the Factory I just defined.
define(['ojs/ojcore', 'knockout', 'ojs/ojmodule-element-utils', 'utils/Languages', 'ojs/ojarraydataprovider', 'ojs/ojlistdataproviderview', 'ojs/ojmodule-element', 'ojs/ojrouter', 'ojs/ojknockout', 'ojs/ojarraytabledatasource', 'ojs/ojoffcanvas', 'ojs/ojselectcombobox'], function(oj, ko, moduleUtils, Languages, ArrayDataProvider, ListDataProviderView) {
- Following best practices, I created my Resource Bundle files under /resources/nls folder and updated the main.js to merge the Out-of-the-Box OJET Translations with my Custom Translations. I was dissapointed you can only register 1 Resource Bundle as clearly said in the official documentation. So, I recommend you structure your Resource Bundle by blocks as I did. Have a look into menu, footer, nav sections in mytranslations.js
requirejs.config( { baseUrl: 'js', // Path mappings for the logical module names // Update the main-release-paths.json for release mode when updating the mappings paths: //injector:mainReleasePaths { 'knockout': 'libs/knockout/knockout-3.4.2.debug', 'jquery': 'libs/jquery/jquery-3.3.1', 'jqueryui-amd': 'libs/jquery/jqueryui-amd-1.12.1', 'promise': 'libs/es6-promise/es6-promise', 'hammerjs': 'libs/hammer/hammer-2.0.8', 'ojdnd': 'libs/dnd-polyfill/dnd-polyfill-1.0.0', 'ojs': 'libs/oj/v6.1.0/debug', 'ojL10n': 'libs/oj/v6.1.0/ojL10n', 'ojtranslations': 'libs/oj/v6.1.0/resources', 'text': 'libs/require/text', 'signals': 'libs/js-signals/signals', 'customElements': 'libs/webcomponents/custom-elements.min', 'proj4': 'libs/proj4js/dist/proj4-src', 'css': 'libs/require-css/css', 'touchr': 'libs/touchr/touchr' } //endinjector ,config: { ojL10n: { merge: { 'ojtranslations/nls/ojtranslations': 'resources/nls/mytranslations' } } }
define({ "root": { "app": { "appName": "OJET Internationalization Example" }, "nav": { "dashboard": "Dashboard", "incidents": "Incidents", "customers": "Customers", "about": "About" }, "menu": { "languages": "Languages", "preferences": "Preferences", "help": "Help", "about": "About", "signOut": "Sign Out" }, "footer": { "aboutOracle": "About Oracle", "contactUs": "Contact Us", "legalNotices": 'Legal Notices', "termsOfUse": "Terms of Use", "yourPrivacyRights": "Your Privacy Rights" } }, "es": true });
- I added two functions in appController.js which are called initTranslations and refreshTranslations
- The initTranslations is a function I always add in the modules (xx.js) to initate the initial value of our custom translations.
self.initTranslations = () => { // Header // Application Name used in Branding Area self.appName = ko.observable(oj.Translations.getTranslatedString("app.appName")); // Navigation Labels self.dashboardMenuLabel = ko.observable(oj.Translations.getTranslatedString('nav.dashboard')); self.incidentsMenuLabel = ko.observable(oj.Translations.getTranslatedString('nav.incidents')); self.customersMenuLabel = ko.observable(oj.Translations.getTranslatedString('nav.customers')); self.aboutMenuLabel = ko.observable(oj.Translations.getTranslatedString('nav.about')); // Menu Labels self.preferencesLabel = ko.observable(oj.Translations.getTranslatedString('menu.preferences')); self.laguangesLabel = ko.observable(oj.Translations.getTranslatedString('menu.languages')); self.helpLabel = ko.observable(oj.Translations.getTranslatedString('menu.help')); self.aboutLabel = ko.observable(oj.Translations.getTranslatedString('menu.about')); self.signOutLabel = ko.observable(oj.Translations.getTranslatedString('menu.signOut')); // Footer Labels self.aboutOracleLabel = ko.observable(oj.Translations.getTranslatedString('footer.aboutOracle')); self.contactUsLabel = ko.observable(oj.Translations.getTranslatedString('footer.contactUs')); self.legalNoticesLabel = ko.observable(oj.Translations.getTranslatedString('footer.legalNotices')); self.termsofUseLabel = ko.observable(oj.Translations.getTranslatedString('footer.termsOfUse')); self.yourPrivacyRightsLabel = ko.observable(oj.Translations.getTranslatedString('footer.yourPrivacyRights')); console.log("AppController Translations initiated!"); }
- The refreshTranslations is a function I always add in the modules (xx.js) to refresh the custom translations and add custom logic if necessary when a language changes.
self.refreshTranslations = () => { // Header // Application Name used in Branding Area self.appName(oj.Translations.getTranslatedString("app.appName")); // Refresh Navigation Labels self.dashboardMenuLabel(oj.Translations.getTranslatedString('nav.dashboard')); self.incidentsMenuLabel(oj.Translations.getTranslatedString('nav.incidents')); self.customersMenuLabel(oj.Translations.getTranslatedString('nav.customers')); self.aboutMenuLabel(oj.Translations.getTranslatedString('nav.about')); // Refresh Labels self.preferencesLabel(oj.Translations.getTranslatedString('menu.preferences')); self.laguangesLabel(oj.Translations.getTranslatedString('menu.languages')); self.helpLabel(oj.Translations.getTranslatedString('menu.help')); self.aboutLabel(oj.Translations.getTranslatedString('menu.about')); self.signOutLabel(oj.Translations.getTranslatedString('menu.signOut')); // Refreshc Labels self.aboutOracleLabel(oj.Translations.getTranslatedString('footer.aboutOracle')); self.contactUsLabel(oj.Translations.getTranslatedString('footer.contactUs')); self.legalNoticesLabel(oj.Translations.getTranslatedString('footer.legalNotices')); self.termsofUseLabel(oj.Translations.getTranslatedString('footer.termsOfUse')); self.yourPrivacyRightsLabel(oj.Translations.getTranslatedString('footer.yourPrivacyRights')); console.log("AppController Translations refreshed"); }
- I added a oj-select as the language selector, but you can add whatever control you want in the index.html for managing the Language. The logic of the Localisation can be found in the setLangAction function of the appController.js
// Change Language self.setLangAction = event => { // Change Language Event const newLocale = event.target.value; Languages.setLocale(newLocale, self.refreshTranslations); }
- The code of setLangAction changes the Locale by using the utility wrapper of the Languages module (wraps the oj.Config.setLocale and other code) and also throws a custom event called localeChangedEvent. Other modules like dashboard.js, customers.js etc.. will need to listen to this event to invoke their owns refreshTranslations functions when the locale has changed. Remember that the Oracle JET components changes its language without any other extra code, but our custom translations needs to be refreshed.
Languages.jssetLocale: (newLocale, langCallback) => { console.log(`Old locale: ${Languages.getCurrentLocale()} and New Locale: ${newLocale}`); oj.Config.setLocale(newLocale, () => { $('html').attr('lang', newLocale); if (newLocale.startsWith('ar')) { $('html').attr('dir', 'rtl'); } else { $('html').attr('dir', 'ltr'); } langCallback(); // Dispatch a JS Event to allow other modules to subscribe and change translations accordingly. document.dispatchEvent(new CustomEvent('localeChangedEvent')); }); },
dashboard.js// Localisation self.initTranslations = () => { self.dashboardTitle = ko.observable(oj.Translations.getTranslatedString('dashboard.dashboardTitle')); }; self.refreshTranslations = () => { self.dashboardTitle(oj.Translations.getTranslatedString('dashboard.dashboardTitle')); }; self.initTranslations(); // Listen to a Locale Change document.addEventListener("localeChangedEvent", function () { self.refreshTranslations(); });
Comments
Post a Comment