diff --git a/client/modules/i18n/main.js b/client/modules/i18n/main.js index 7a821d0b29f..4c743558c50 100644 --- a/client/modules/i18n/main.js +++ b/client/modules/i18n/main.js @@ -1,16 +1,9 @@ -import _ from "lodash"; import i18next from "i18next"; -import i18nextBrowserLanguageDetector from "i18next-browser-languagedetector"; -import i18nextLocalStorageCache from "i18next-localstorage-cache"; -import i18nextSprintfPostProcessor from "i18next-sprintf-postprocessor"; -import i18nextJquery from "jquery-i18next"; import { Meteor } from "meteor/meteor"; import { Tracker } from "meteor/tracker"; -import { Session } from "meteor/session"; import { SimpleSchema } from "meteor/aldeed:simple-schema"; import { Reaction } from "/client/api"; -import { Packages, Shops, Translations } from "/lib/collections"; -import * as Schemas from "/lib/collections/schemas"; +import { Packages, Translations } from "/lib/collections"; // // Reaction i18n Translations, RTL and Currency Exchange Support @@ -40,7 +33,7 @@ export function getBrowserLanguage() { * @param {String} name - name * @return {Object} return schema label object */ -function getLabelsFor(schema, name) { +export function getLabelsFor(schema, name) { const labels = {}; // loop through all the rendered form fields and generate i18n keys for (const fieldName of schema._schemaKeys) { @@ -68,7 +61,7 @@ function getLabelsFor(schema, name) { * @todo implement messaging hierarchy from simple-schema * @return {Object} returns i18n translated message for schema labels */ -function getMessagesFor() { +export function getMessagesFor() { const messages = {}; for (const message in SimpleSchema._globalMessages) { if ({}.hasOwnProperty.call(SimpleSchema._globalMessages, message)) { @@ -89,19 +82,14 @@ function getMessagesFor() { */ export const i18nextDep = new Tracker.Dependency(); export const localeDep = new Tracker.Dependency(); -const packageNamespaces = []; +export const packageNamespaces = []; Meteor.startup(() => { Tracker.autorun(function (c) { + // setting local and active packageNamespaces + // packageNamespaces are used to determine i18n namespace if (Reaction.Subscriptions.Shops.ready()) { - // TODO: i18nextBrowserLanguageDetector - // const defaultLanguage = lng.detect() || shop.language; - - // set default session language - Session.setDefault("language", getBrowserLanguage()); - - // every package gets a namespace, fetch them - // const packageNamespaces = []; + // every package gets a namespace, fetch them and export const packages = Packages.find({}, { fields: { name: 1 @@ -128,97 +116,4 @@ Meteor.startup(() => { }); }); -// use tracker autorun to detect language changes -Tracker.autorun(function () { - return Meteor.subscribe("Translations", Session.get("language"), () => { - // fetch reaction translations - const translations = Translations.find({}, { - fields: { - _id: 0 - } - }).fetch(); - - // map reduce translations into i18next formatting - const resources = translations.reduce(function (x, y) { - const ns = Object.keys(y.translation)[0]; - // first creating the structure, when add additional namespaces - if (x[y.i18n]) { - x[y.i18n][ns] = y.translation[ns]; - } else { - x[y.i18n] = y.translation; - } - return x; - }, {}); - - const shop = Shops.findOne(Reaction.getShopId()); - - // - // initialize i18next - // - i18next - .use(i18nextBrowserLanguageDetector) - .use(i18nextLocalStorageCache) - .use(i18nextSprintfPostProcessor) - .use(i18nextJquery) - .init({ - debug: false, - ns: packageNamespaces, // translation namespace for every package - defaultNS: "core", // reaction "core" is the default namespace - lng: Session.get("language"), // user session language - fallbackLng: shop ? shop.language : null, // Shop language - resources: resources - // saveMissing: true, - // missingKeyHandler: function (lng, ns, key, fallbackValue) { - // Meteor.call("i18n/addTranslation", lng, ns, key, fallbackValue); - // } - }, (err, t) => { - // someday this should work - // see: https://github.com/aldeed/meteor-simple-schema/issues/494 - for (const schema in _.omit(Schemas, "__esModule")) { - if ({}.hasOwnProperty.call(Schemas, schema)) { - const ss = Schemas[schema]; - ss.labels(getLabelsFor(ss, schema)); - ss.messages(getMessagesFor(ss, schema)); - } - } - - i18nextDep.changed(); - - // global first time init event finds and replaces - // data-i18n attributes in html/template source. - $elements = $("[data-i18n]").localize(); - - // apply language direction to html - if (t("languageDirection") === "rtl") { - return $("html").addClass("rtl"); - } - return $("html").removeClass("rtl"); - }); - }); -}); - -// -// init i18nextJquery -// -i18nextJquery.init(i18next, $, { - tName: "t", // --> appends $.t = i18next.t - i18nName: "i18n", // --> appends $.i18n = i18next - handleName: "localize", // --> appends $(selector).localize(opts); - selectorAttr: "data-i18n", // selector for translating elements - targetAttr: "data-i18n-target", // element attribute to grab target element to translate (if diffrent then itself) - parseDefaultValueFromContent: true // parses default values from content ele.val or ele.text -}); - -// global onRendered event finds and replaces -// data-i18n attributes in html/template source. -// uses methods from i18nextJquery -Template.onRendered(function () { - this.autorun((function () { - return function () { - i18nextDep.depend(); - $elements = $("[data-i18n]").localize(); - }; - })(this)); -}); - export default i18next; diff --git a/client/modules/i18n/startup.js b/client/modules/i18n/startup.js new file mode 100644 index 00000000000..26f01511883 --- /dev/null +++ b/client/modules/i18n/startup.js @@ -0,0 +1,140 @@ +import _ from "lodash"; +import i18nextBrowserLanguageDetector from "i18next-browser-languagedetector"; +import i18nextLocalStorageCache from "i18next-localstorage-cache"; +import i18nextSprintfPostProcessor from "i18next-sprintf-postprocessor"; +import i18nextJquery from "jquery-i18next"; +import { Meteor } from "meteor/meteor"; +import { Tracker } from "meteor/tracker"; +import { Reaction } from "/client/api"; +import { Shops, Translations } from "/lib/collections"; +import * as Schemas from "/lib/collections/schemas"; +import i18next, { packageNamespaces, getLabelsFor, getMessagesFor, i18nextDep } from "./main"; + +// +// setup options for i18nextBrowserLanguageDetector +// note: this isn't fully operational yet +// language is set by user currently +// progress toward detecting language +// should focus around i18nextBrowserLanguageDetector +// +const options = { + // order and from where user language should be detected + order: ["querystring", "cookie", "localStorage", "navigator", "htmlTag"], + + // keys or params to lookup language from + lookupQuerystring: "lng", + lookupCookie: "i18next", + lookupLocalStorage: "i18nextLng", + + // cache user language on + caches: ["localStorage", "cookie"], + // optional htmlTag with lang attribute, the default is: + htmlTag: document.documentElement +}; + + +Meteor.startup(() => { + // use tracker autorun to detect language changes + // this only runs on initial page loaded + // and when user.profile.lang updates + Tracker.autorun(function () { + if (Reaction.Subscriptions.Shops.ready() && Meteor.user()) { + const shop = Shops.findOne(Reaction.getShopId()); + let language = shop.language; + if (Meteor.user() && Meteor.user().profile && Meteor.user().profile.lang) { + language = Meteor.user().profile.lang; + } + // + // subscribe to user + shop Translations + // + return Meteor.subscribe("Translations", language, () => { + // fetch reaction translations + const translations = Translations.find({}, { + fields: { + _id: 0 + } + }).fetch(); + + // map reduce translations into i18next formatting + const resources = translations.reduce(function (x, y) { + const ns = Object.keys(y.translation)[0]; + // first creating the structure, when add additional namespaces + if (x[y.i18n]) { + x[y.i18n][ns] = y.translation[ns]; + } else { + x[y.i18n] = y.translation; + } + return x; + }, {}); + + // + // initialize i18next + // + i18next + .use(i18nextBrowserLanguageDetector) + .use(i18nextLocalStorageCache) + .use(i18nextSprintfPostProcessor) + .use(i18nextJquery) + .init({ + detection: options, + debug: false, + ns: packageNamespaces, // translation namespace for every package + defaultNS: "core", // reaction "core" is the default namespace + lng: language, // user session language + fallbackLng: shop ? shop.language : null, // Shop language + resources: resources + // saveMissing: true, + // missingKeyHandler: function (lng, ns, key, fallbackValue) { + // Meteor.call("i18n/addTranslation", lng, ns, key, fallbackValue); + // } + }, (err, t) => { + // someday this should work + // see: https://github.com/aldeed/meteor-simple-schema/issues/494 + for (const schema in _.omit(Schemas, "__esModule")) { + if ({}.hasOwnProperty.call(Schemas, schema)) { + const ss = Schemas[schema]; + ss.labels(getLabelsFor(ss, schema)); + ss.messages(getMessagesFor(ss, schema)); + } + } + + i18nextDep.changed(); + + // global first time init event finds and replaces + // data-i18n attributes in html/template source. + $elements = $("[data-i18n]").localize(); + + // apply language direction to html + if (t("languageDirection") === "rtl") { + return $("html").addClass("rtl"); + } + return $("html").removeClass("rtl"); + }); + }); // return + } + }); + + // + // init i18nextJquery + // + i18nextJquery.init(i18next, $, { + tName: "t", // --> appends $.t = i18next.t + i18nName: "i18n", // --> appends $.i18n = i18next + handleName: "localize", // --> appends $(selector).localize(opts); + selectorAttr: "data-i18n", // selector for translating elements + targetAttr: "data-i18n-target", // element attribute to grab target element to translate (if diffrent then itself) + parseDefaultValueFromContent: true // parses default values from content ele.val or ele.text + }); + + // global onRendered event finds and replaces + // data-i18n attributes in html/template source. + // uses methods from i18nextJquery + Template.onRendered(function () { + this.autorun((function () { + return function () { + i18nextDep.depend(); + $elements = $("[data-i18n]").localize(); + }; + })(this)); + }); +}); diff --git a/client/modules/i18n/templates/header/i18n.html b/client/modules/i18n/templates/header/i18n.html index 10daac5c9d3..da9f628ace1 100644 --- a/client/modules/i18n/templates/header/i18n.html +++ b/client/modules/i18n/templates/header/i18n.html @@ -9,7 +9,7 @@