Localization in meteor with React.js

Can anybody guide which library I should use for localization?

Or if anybody is using customize code please share

We use the npm package “i18next”.

Along with this init script, which uses dynamic loading to get the translations. I have placed my translation files in /imports/i18n/ as you can see.

// i18n.js


import _ from "lodash";
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import XHR from "i18next-xhr-backend";
import en from "/imports/i18n/en.i18n.json";
import { Meteor } from "meteor/meteor";
import { Session } from "meteor/session";

global.i18n = i18n;

const locales = {
  en: () => new Promise(r => r(en)),
  no: () => import("/imports/i18n/no.i18n.json"),
  se: () => import("/imports/i18n/se.i18n.json"),
  dk: () => import("/imports/i18n/dk.i18n.json"),
};

function loadLocales (url, options, callback, data) {
  try {
    locales[url]().then(locale => {
      callback(locale, { status: "200" });
    });
  } catch (e) {
    callback(null, { status: "404" });
  }
}

i18n
  .use(LanguageDetector)
  .use(XHR)
  .init({
    fallbackLng: "en",
    debug: false,
    backend: {
      loadPath: "__lng__",
      parse: data => (_.isString(data) ? JSON.parse(data) : data),
      ajax: loadLocales
    },

    compatibilityJSON: "v1",

    // have a common namespace used around the full app
    // ns: ["translations"],
    // defaultNS: "translations",

    keySeparator: ".",

    interpolation: {
      escapeValue: false,
      formatSeparator: ",",
      prefix: "__",
      suffix: "__",
    },

    react: {
      wait: true
    }
  });

Meteor.isClient && Meteor.startup(() => {
  const initialLanguage = Session.get("language") || "en";
  if (i18n.language !== initialLanguage) {
    i18n.changeLanguage(initialLanguage);
    Session.set("language", initialLanguage);
  }
});

export default i18n;

and this

// translate.js

import _ from "lodash";
import React from "react";
import i18n from "./i18n";

const shouldTranslate = !Meteor.isTest;


/**
 * @summary Get a translation by key. You can insert named variables py placing them in options, and referencing them in the string using __variablename__ .
 * @param  {String} key   Key to look up with.
 * @param  {Object} opts Additional options, and named variables to use in the tanslation string. If the value is an object, the inner object will be traslated using the "key" as key and "data" as data.
 * @return {String}      The translated string
 */
const translate = (key, opts = { reallyDoTranslate: false }) => {
  const {
    html,
    reallyDoTranslate,
    capitalize = false,
    ...strings
  } = opts;

  if (!shouldTranslate && !reallyDoTranslate) return `{${key}}`;

  const data = _.mapValues(strings, v => {
    if (_.isObject(v)) {
      const innerKey = v.key;
      const innerData = v.data || {};

      return translate(innerKey, innerData);
    }
    return v;
  });

  let translated = i18n.t(key, data);

  if (capitalize) {
    translated = translated.charAt(0).toUpperCase() + translated.slice(1);
  }

  if (translated && translated !== key) {
    return translated;
  }

  if (opts.default) {
    return opts.default;
  }

  return `{${key}}`;
};

translate.has = function (key) {
  let translated;

  try {
    translated = i18n.t(key);
  } catch (e) {
    console.error(e, "Problem with translating", key, "does translation have special characters?");
    return false;
  }

  return !!translated && translated !== key;
};

global.__ = translate;

export default translate;

in react code:

import React from "react";
import __ from "/imports/translation";

class MyComponent extends React.Component {
  render () {
    const {  } = this.props;

    return (
      <div>
        {__("my.translation.key")}
      </div>
    );
  }
}

export default MyComponent;

2 Likes

Dear is there any example code ?

The problem with the approach I have outlined is that it is not reactive to language change. I didn’t want to deeply integrate the translation into React’s context api or passing it as props for various reasons either, which lead me to doing it like this:

//switch_language.js

import { Session } from "meteor/session";
import { FlowRouter } from "meteor/ostrio:flow-router-extra";

export default (lang) => {
  if (Session.get("language") === lang) {
    Session.set("language-is-set", true);
    return;
  }

  const path = FlowRouter.current().path;
  FlowRouter.go("/translate-bounce");
  Session.set("language-is-set", true);
  Session.setTemporary("language-loading", true);
  i18n.changeLanguage(lang, () => {
    Session.setTemporary("language-loading", false);
    Session.set("language", lang);
    FlowRouter.go(path);
  });
};

Using this function to switch language, I redirect to a loading page with a “clean” template (/translate-bounce) which unmounts everthing, and then go back to the previous page again once loaded. This re-mounts all the components and thus refreshes the translations. I admit it is a bit hacky, but it has served our puposes well.

// router for language

import React from "react";
import Loading from "/imports/utils/ui/Loading";
import { FlowRouter } from "meteor/ostrio:flow-router-extra";
import { mount } from "react-mounter";

const BareLayout = ({ content }) => (
  <div className="full-page-container" id="container">
    {content}
  </div>
);

// This route is used to clear the dom, so we can re-translate everything
FlowRouter.route("/translate-bounce", {
  name: "translate-bounce",
  action () {
    mount(BareLayout, {
      content: <Loading overlay size="big" text="Loading language" />
    });
  },
});

react-intl is another great solution.