First I want to say that I'm relatively new to React and ES6 in general.
So far I've implemented react-i18next with great success in components. I've also added the i18next-scanner to automatically extract translation keys to .json-files.
I have a router/index.js
file containing the routes for my project. This file is also used for creating the sidebar navigation.
In this file I have the names used for the sidebar navigation. The routes are stored as objects which may contain child routes. My problem is that I want to use the i18next t()
function to translate these names.
My code
i18n.js:
// i18n.js
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
const i18n = i18next
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
debug: true,
// ...
},
});
export { i18next }; // instance
export default i18n; // promise
routes/index.js:
// routes/index.js
import React from 'react';
import i18n, { i18next } from 'i18n';
import { Trans } from 'react-i18next';
// other imports...
// Does not work since the translations aren't loaded yet.
const dashboardRoute = {
name: i18next.t('dashboard'),
path: '/',
component: MyPageComponent,
children: null,
};
// Solution 1: Trans component for generating strings
const dashboardRouteTrans = {
name: <Trans i18nKey="dashboard" />,
path: '/',
component: MyPageComponent,
children: null,
};
// Solution 2: Use promise to rewrite string
const dashboardRoutePromise = {
name: '', // Init with empty string
path: '/',
component: MyPageComponent,
children: null,
};
i18n.then(function(t) {
// Overwrite with translated string using promise
dashboardRoutePromise.name = t('dashboard');
});
export default [
dashboardRoute,
dashboardRouteTrans,
dashboardRoutePromise
];
Problems
The two solutions I've so far have come up with do work. But it feels like there should be a better way to implement this. Using the <Trans>
component for generating strings feels wrong. And setting empty strings and overwrite these later using the promise doesn't feel elegant at all.
I want to still be able to extract keys using the scanner. This makes the solution to pass on the keys to later be used with the t()
function in the sidebar component troublesome. The scanner does not pick up on dynamic translations using variables.
My questions
What am I missing? How should a proper promise solution look? What is the correct way to translate strings outside of components?
I probably am thinking about this wrongly. But like I stated in the intro, I'm not a well experienced JS/React-developer and my prior knowledge and experience of promises is limited. I feel like my solution to using promises is wrongly implemented. And I'd like the final solution to be scannable by the key extractor.
When a user changes the language using i18n.changeLanguage(lng)
, the translation keys that are generated outside of components, don't update. What is the best approach to update these as well?
All feedback and help is greatly appreciated.
I've found what I believe are good solutions to my problems. There is actually no need to use the promise in this case. I can solve this problem in two ways:
1) Rewrite routes/index.js
to take the t()
function as an argument. Pass this function from the component on render (using i18next hook or HOC).
2) Use translation keys in routes/index.js
and translate these on render in the component. To be able to scan for keys, add a dummy function that takes a key as argument and simply returns it. Add this function to the scanner. One way of doing this is to add the following to the i18n.js
file.
export const tk = (key) => key;
If anyone has a better solution or would like to explain how to properly use the promise returned by i18next, I'm all ears! :)