React.js i18n dynamically change language - javascript

I am using "i18n" for multilingual support in my "React.js" project. I have a "Switch" button in "Navbar" component. and when that button is clicked I add current language information to "lcoalstore". And when I check in browser, language option works dynamically in "localstore". My problem is I want to change app language every time user changes language. But that doesn't happen.
Here is my i18n code:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import messages_az from "./translations/az.json";
import messages_en from "./translations/en.json";
// the translations
// (tip move them in a JSON file and import them)
const resources = {
en: {
translation: messages_en,
},
az: {
translation: messages_az,
},
};
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
resources,
lng: localStorage.getItem("lang"),
detection: {
order: ["localStorage", "lang"],
lookupQuerystring: "lang",
lookupLocalStorage: "lang",
caches: ["localStorage"],
},
keySeparator: false, // we do not use keys in form messages.welcome
fallbackLng: "az", // use az if detected lng is not available
interpolation: {
escapeValue: false, // react already safes from xss
},
});
export default i18n;

I fixed That!
Here is my swtich button :
<Menu.Item key="lang" className=" navMenuItem">
<Switch
onChange={langSwitch}
className="langSwitch"
checkedChildren="En"
unCheckedChildren="Az"
defaultChecked
/>
</Menu.Item>
So, i Added this code to my langSwitch() function :
function langSwitch(checked) {
console.log(`switch to ${checked}`);
if (checked) {
i18n.changeLanguage("en");
} else {
i18n.changeLanguage("az");
}
}

Related

React internalisation SPA using i18next

I need to translate my app, but i don't knomw how to use useTranslation() in the top-level files (i store there some consts which contain some text). One of this file is
import { useTranslation } from "react-i18next";
const {t} = useTranslation()
export const selectThemeOptions = [
{ value: "choose", text: "Choose theme" },
{ value: "Algebra", text: "Algebra" },
{ value: "Geometry", text: "Geomerty" },
{ value: "Programming", text: "Programming" },
{ value: "Logic", text: "Logic" },
];
so in this case i have an error:
src\Configs\ThemesOfProblems.js
Line 3:13: React Hook "useTranslation" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
I need this array in my component, and it use in the next fragment :
<Form.Group as={Col} controlId="problemTheme">
<Form.Label>{t("userprofile.theme")}</Form.Label>
<Form.Select
name="theme"
value={values.theme}
onChange={handleChange}
isValid={touched.theme && !errors.theme}
isInvalid={!!errors.theme}
onBlur={handleBlur}
>
{selectThemeOptions.map((el, index) => {
return <option key={index} value={el.value}> {el.text} </option>
})}
</Form.Select>
</Form.Group>
And i've got a lot of such situations, i don't have any ideas how to do it
Basically it says it has to be called in a react component. It could be called in a functional component where you return your jsx or a class component that has a render method in it. If you call the function outside of one of these, then you will get this error.
You called const {t} = useTranslation(); outside of a React component, your selectThemeOptions file seems to be regular JavaScript due to the absence of JSX or a returning statement with your HTML.
Here is the correct way to do it:
/* Everything above this point is considered top-level, hence using your `useTranslation()` hook here would cause an error */
const Component = (props) => {
const { t } = useTranslation();
}
export default Component;
Here is a way to organise your translations:
• Your src folder should contain an i18n.js file with the following code:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import en from "../locales/en.json";
import ru from "../locales/ru.json";
const isReturningUser = "lang" in localStorage; // Returns true if user already used the website.
const savedLanguage = JSON.parse(localStorage.getItem("lang")); // Gets persisted language from previous visit.
// Get previously used language in case of returning user or set language to Russian by default.
const language = isReturningUser ? savedLanguage : "ru";
const resources = {
en: {
translation: en,
},
ru: {
translation: ru,
},
};
i18n.use(initReactI18next).init({
resources,
lng: language,
keyseparator: false,
interpolation: {
escapeValue: false,
},
});
export default i18n;
Your src folder should contain a locales folder with json files of the languages your application uses. Example: ru.json and en.json:
{
"choose": "Выбрать",
"chooseATheme": "Выбрать тему",
"algebra": "Алгебра",
"geometry": "Геометрия",
"programming": "Программирование",
"logic": "Логика",
}
Your component should look like this – note that the translations are in json files instead of your React component – :
import React from "react";
import { useTranslation } from "react-i18next";
const Component = (props) => {
const { t } = useTranslation();
const selectThemeOptions = [
{ value: t("choose"), text: t("chooseATheme") },
{ value: t("algebra"), text: t("algebra") },
{ value: t("geometry"), text: t("geometry") },
{ value: t("programming"), text: t("programming") },
{ value: t("logic"), text: t("logic") },
];
return( //Your UI )
}
export default Component;
This way, your translations wouldn't be hard-coded on your selectThemeOptions and will adapt to whichever translation your json locales contain.
Please tell me if this works.
Edit: If you want a concrete example of implementation of my solution here it is: https://github.com/YHADJRABIA/ecommerce/tree/main/src
Edit2: There might be a better solution of doing this, this is merely what worked for me.
Edit3: Following Nikita's comment, here's a solution to use the translation function outside of a react component —How to use react-i18next inside BASIC function (not component)?
P.S. Since you are from Belarus I assume that you want your translation to be made in Russian since Belarusian isn't as widely spoken.

characters wont display correct with Quasar Framework together with i18n

Im using i18n together with Quasar Framework. It works great but I have some problems with some special characters in certain languages when grabbing text from my own definations.
I got this in i18n.js in my boot:
import Vue from "vue";
import VueI18n from "vue-i18n";
import messages from "src/i18n";
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: "en-us",
fallbackLocale: "en-us",
messages,
});
export default ({ app }) => {
// Set i18n instance on app
app.i18n = i18n;
};
// if you need to import it from
// other files, then:
export { i18n };
and use it in template like this:
<template>
...
<div>{{ $t('hello') }}</div>
...
and in my bootfile
boot: ["i18n"],
framework: {
iconSet: "material-icons", // Quasar icon set
lang: "en-us", // Quasar language pack
Here is the setup in my language component:
export default {
data() {
return {
toggleSMenu: false,
myData: ["one", "two"],
lang: this.$i18n.locale,
langOptions: [
{ value: "en-us", label: "EN" },
{ value: "nl", label: "NL" }
]
};
},
When I change to Dutch (NL) some caracters wont display correctly. How can I add support for dutch letters?

Storybook not showing styles

I have a dialog component which is using the Primereact dialog internally. When I make a storybook for the same, the custom css for button is being imported as it is imported inside dialog.jsx. But the default css of Primereact dialog is not loading and reflecting in the storybook. Although it is being loaded in my React app.
dialogComp.jsx
import { Dialog } from "primereact/dialog";
const DialogComp = (props) => {
return (
<Dialog
className="dialog-modal"
header={props.header}
visible={true}
>
{props.children}
</Dialog>
);
};
export default DialogModal;
dialog.storybook.js
import React from "react";
import DialogModal from "./dialogComp";
import { addDecorator, addParameters } from "#storybook/react";
import { Store, withState } from "#sambego/storybook-state";
import { store } from "./../../utils/storyStore";
const DialogModalComp = (props) => {
return [
<div>
<DialogModal
header="Dialog Modal"
displayModal={true}
>
Modal content
</DialogModal>
</div>,
];
};
addDecorator(withState());
addParameters({
state: {
store,
},
});
export default {
title: "dialog",
};
export const DialogModalComponent = () => DialogModalComp;
storybook---main.js
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials",
"#storybook/preset-create-react-app"
]
}
Am I missing something in the configuration?
You'll need to import any styles you use in App.js globally in Storybook, by importing them in .storybook/preview.js (create the file if it doesn't already exist).
Every component in React is self contained - your DialogModal component won't get styled because in Storybook it is not being rendered within your App component (where you're importing your styles).
To simulate your app when using Storybook, you import the css in a preview.js file.
Docs:
To control the way stories are rendered and add global decorators and
parameters, create a .storybook/preview.js file. This is loaded in the
Canvas tab, the “preview” iframe that renders your components in
isolation. Use preview.js for global code (such as CSS imports or
JavaScript mocks) that applies to all stories.
TL;DR
import your styles in .storybook/preview.js
import "../src/index.css";
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
If you use storybook and emotion, and if you implement Global styles or Theming, you may add a decorator into the .storybook/preview.js like this:
I'm using Create React App, therefore I'm using jsxImportSource
/** #jsxImportSource #emotion/react */
import { Global } from '#emotion/react'
import { GlobalStyles } from '../src/styles'
const withGlobalProvider = (Story) => (
<>
<Global styles={GlobalStyles} />
<Story />
</>
)
export const decorators = [withGlobalProvider]
You may find more information on: https://storybook.js.org/docs/react/essentials/toolbars-and-globals#global-types-and-the-toolbar-annotation

How to transfer value from Vuex Store to i18n.js?

I am trying to make the language that I chose was saved when the program was restarted.
How to save the value of locale:'' in i18n when switching languages?
I want to transfer this value from VUEX Store to i18n, but I don’t know how to do it right.
The default local: 'en'.
When the language is changed, the value in locale is overwritten and stored in localStorage, and when the program is restarted, the value at the created () stage is taken from localStorage.
Maybe at least an example exists where a similar function is implemented?
<template>
...
<f7-menu-dropdown-item href="#" #click="setLocale('en')">
<f7-menu-dropdown-item href="#" #click="setLocale('ru')">
...
</template>
export default {
methods: {
setLocale(locale){
import(`../langs/${locale}.json`).then((msgs) => {
this.$i18n.setLocaleMessage(locale, msgs)
this.$i18n.locale = locale
localStorage.setItem('language', locale);
})
},
created(){
if(localStorage.getItem('language') !== null){
localStorage.setItem('language', navigator.language || navigator.userLanguage);
}
}
}
}
i18n.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import en from '../langs/en.json'
import ru from '../langs/ru.json'
Vue.use(VueI18n)
export const i18n = new VueI18n({
locale: window.localStorage.getItem("language"),
fallbackLocale: 'ru',
messages:{
en,
ru
}
})

react-i18next, add resources from props

I have repo with components and repo with main app. I implemented i18next in repo with components and it works fine when I have an i18n config file in this repo (or when I pass it by props from app repo). But I have a problem when I'm trying to send only "resource" part from main app and replace it in config file in components. I tried to clone i18n instance and set resources but, it's not work.
It's my config file:
i18n.js
import i18n from 'i18next';
import LngDetector from 'i18next-browser-languagedetector';
import { reactI18nextModule } from 'react-i18next';
i18n
.use(LngDetector)
.use(reactI18nextModule)
.init({
detection: {
order: ['cookie', 'localStorage'],
lookupLocalStorage: 'i18n_lang',
lookupCookie: 'i18n_lang',
caches: ['localStorage'],
},
load: 'current',
fallbackLng: 'en',
ns: ['components'],
defaultNS: 'components',
keySeparator: false,
interpolation: {
escapeValue: false,
formatSeparator: ',',
},
react: {
wait: true,
},
});
export default i18n;
resources.js file (I tried with resources key at beginning but it's still doesn't work):
import * as en from './en.json';
import * as de from './de.json';
export default {
en: {
components: en,
},
de: {
components: de,
},
};
Now I tried something like this:
import * as langs from './resources';
const newI18 = i18n.cloneInstance({ resources: langs });
const i18ProviderDecorator = (storyFn) => (
<I18nextProvider i18n={newI18}>
{ storyFn() }
</I18nextProvider>
When I pass i18n.js by props with resources, it works perfect, but I want to remove i18next from main app and leave it only in the components.
Greetings
a i18next cloned instance uses the same store as the original instance -> and does not init that again -> so passing in resources that way does not work: https://github.com/i18next/i18next/blob/master/src/i18next.js#L308
make a new instance i18n.createInstance or pass resources to clone using i18n.addResourceBundle: https://www.i18next.com/api.html#addresourcebundle

Categories

Resources