react-i18next: strings not being translated - reactjs

I had a frustrating problem with the react-i18next library. I just couldn't get the library to translate the strings in my app.
The code was as follows:
App.tsx:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import resources from './resources';
// code omitted...
i18n
.use(initReactI18next)
.init({
resources,
lng: 'en',
interpolation: {
escapeValue: false,
},
});
// rest of the code here...
resources/index.tsx:
export default {
en: {
'Math Visualized': 'Math Visualized asd',
},
fi: {
'Math Visualized': 'Matematiikan visualisointia',
},
};
components/header/Header.tsx:
import { withTranslation } from 'react-i18next';
// code omitted...
class HeaderComponent extends React.Component<Props, State> {
render() {
const { headerText, subheaderText, t } = this.props;
// react-bootstrap used here
return (
<Navbar bg="dark" variant="dark">
<Navbar.Brand>{t(headerText)}</Navbar.Brand>
{subheaderText && <Navbar.Text>{t(subheaderText)}</Navbar.Text>}
</Navbar>
);
}
}
export const Header = withTranslation()(HeaderComponent);
The header and subheader text strings simply refused to be translated.

I had simply forgotten to add the translation namespace to the resources file. I modified it like this:
export default {
en: {
translation: { // THIS NAMESPACE HERE WAS MISSING
'Math Visualized': 'Math Visualized asd',
},
},
fi: {
translation: {
'Math Visualized': 'Matematiikan visualisointia',
},
},
};
And everything worked.

Related

Get i18n locale as global variable in all pages in Next.js

I want to get the locale as a global variable in any component of my app without using any other external libraries.
I have json files for translations en.js and es.js:
es.js
export default {
home: 'Inicio',
signin: 'Iniciar Sesion',
welcome: 'Bienvenido',
};
I have already set next.config.js:
i18n: {
locales: ['en', 'es'],
defaultLocale: 'es',
},
And I can get the locale in any component by router.locale:
home.js
import en from "../lib/i18n/en";
import es from "../lib/i18n/es";
const HomePage = () => {
const router = useRouter()
const { locale } = router;
const t = locale === 'en' ? en : es;
return (
<div>t.home</div>
)
}
All good, the question is, how can I replicate this in all the components without having to call the router all the time and replicating the logic to get t. A solution that would allow me something like this:
home.js
import t from "../lib/i18n/t";
const HomePage = () => {
return (
<div>t.home</div>
)
}
Making t a global variable or that all the components have the context to use it.
I am doing it generally different:
//localized.js
import { useRouter } from 'next/router';
export const words = {
login: {
de: 'Login',
en: 'Login',
},
register: {
de: 'Register',
en: 'Registrieren',
},
includeUpdate: {
de: 'Update Link anhängen.',
en: 'Include update link.',
},
// ......
}
export default function t(text) {
const { locale } = useRouter();
return text?.[locale] || text;
}
Nor you can use it like so:
import {words, t} from 'path/to/localized.js'
cont Test = () => {
return (
<div>{t(words.login)}</div>
<div>{t({de: "Ein anderer Text.", en: "Some other text."})}</div>
)
}

react-i18next switching i18n json file based on theme name isn't working

I’m trying to switch language based on two factors 1. Theme Name(MN, NY, ..) and 2.language (en,es) selected. It. Doesn’t seem to work.
Sample Button.json
{
"generic": "Button Text goes here..",
"submit": "Submit Button",
"reset": "Reset",
"cancel": "Cancel"
}
Storybook/Preview.js
import { I18nextProvider } from 'react-i18next';
import { ChakraProvider } from '#chakra-ui/react';
import { stateTheme_GI } from '../src/theme/foundations/colors/mn_theme';
import { stateTheme_GA } from '../src/theme/foundations/colors/ny_theme';
import { stateTheme_DEFAULT } from '../src/theme/foundations/colors/default_theme';
import './i18next';
export const parameters = {
actions: {argTypesRegex: '^on[A-Z].*'},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}
export const decorators = [
(Story, context) => {
const theme = context.globals.theme;
let storyTheme = {};
let i18n;
if (theme === 'mn') {
storyTheme = stateTheme_MN;
i18n = i18nMN
} else if (theme === 'ny') {
storyTheme = stateTheme_NY;
i18n = i18nNY
} else {
storyTheme = stateTheme_DEFAULT;
}
return (
<ChakraProvider theme={storyTheme}>
<I18nextProvider i18n={i18n}>
<Story/>
</I18nextProvider>
</ChakraProvider>
);
},
];
Storybook/i18n.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
const i18nGI = await i18n.use(initReactI18next)
.use(Backend)
.init({
backend: {loadPath: '/i18n/mn/locales/{{lng}}/{{ns}}.json', addPath: "/locales/add/{{lng}}/{{ns}}",},
fallbackLng: 'en-US',
debug: true,
});
const i18nPA = await i18n.use(initReactI18next)
.use(Backend)
.init({
backend: {loadPath: '/i18n/ny/locales/{{lng}}/{{ns}}.json', addPath: "/locales/add/{{lng}}/{{ns}}",},
fallbackLng: 'en-US',
debug: true,
});
export {i18nMN, i18nNY};
Usage in Component:
import { Button } from '#chakra-ui/button';
import { useStyleConfig } from '#chakra-ui/react';
import { useTranslation } from 'react-i18next';
export default function MyButton({
buttonText='submit',
id,
name,
size='md',
type='submit',
variant='primary'}) {
const [t] = useTranslation(['button']);
return (
<Button
sx={styles}
id={id}
name={name}
size={size}
type={type}
variant={variant}
>
{t(buttonText)}
</Button>
);
}

useTranslation() doesn't work with react-i18next

I try to make react-i18next package working but i'm facing an issue.
I have a provider for i18n initialization :
import React, {useState, useEffect, useRef} from 'react';
import {useLocalization} from '#shopify/hydrogen';
import {I18nextProvider} from 'react-i18next';
import i18n from 'i18next';
import {initReactI18next} from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import resourcesToBackend from 'i18next-resources-to-backend';
// Bundle the default translation
import fr from '../assets/locales/fr/translation.json';
i18n
// load other translations dynamically
.use(
resourcesToBackend((language, namespace, callback) => {
import(`../assets/locales/${language}/${namespace}.json`)
.then((resources) => {
callback(null, resources);
})
.catch((error) => {
callback(error, null);
});
})
)
.use(LanguageDetector)
.use(initReactI18next);
export function TranslationProvider({children}) {
const {language} = useLocalization();
const init = useRef(false);
const [i18nInstance, setI18nInstance] = useState(null);
useEffect(() => {
if (init.current) return;
init.current = true;
const cachedLang = localStorage.getItem('i18nextLng');
const defaultLang = language.isoCode.toLowerCase();
if (!cachedLang) {
localStorage.setItem('i18nextLng', defaultLang);
}
// initialize i18next
i18n.init(
{
partialBundledLanguages: true, // This allows some resources to be set on init while others after
fallbackLng: defaultLang,
debug: true,
react: {useSuspense: true, wait: true},
ns: ['translation'],
defaultNS: 'translation',
interpolation: {
escapeValue: false,
},
resources: {
fr: {translation: fr},
},
},
() => {
setI18nInstance(i18n);
}
);
}, [i18n, setI18nInstance, i18nInstance, language]);
return (
i18nInstance && (
<I18nextProvider i18n={i18nInstance}>{children}</I18nextProvider>
)
);
}
Then I call my Provider like this :
<Suspense fallback={'Loading lang..'}>
<TranslationProvider>
<Router>
<FileRoutes
basePath={languageCode ? `/${languageCode}/` : undefined}
/>
<Route path="*" page={<NotFound />} />
</Router>
</TranslationProvider>
</Suspense>
Finally I use it in a component :
import {useTranslation} from 'react-i18next';
const {t} = useTranslation();
{t('footer.language')}
I have no error but it displays :
footer.language instead of the expected translation
When I inspect t, i get [Function: notReadyT]
Does anyone can help me with this ?
Thank you

react-i18Next: Using an enum for translation key values

We are using i18Next in our React project and I wonder if there is way to use an enum for keys of the translation file to avoid typos while using it like this:
export enum AppLocaleKey {
test = 'test'
}
...
import translationEN from './locales/en/translation';
const resources = {
en: { translation: translationEN },
...
};
i18n
.use(initReactI18next)
.init({
resources,
...
})
...
const translation = {
[AppLocaleKey.test]: 'Test...',
};
export default translation;
...
import { AppLocaleKey } from './locales/localeKeys';
import { useTranslation } from 'react-i18next';
const App = (props: Props) => {
const { t, i18n } = useTranslation();
return (
<>
<p>{t(AppLocaleKey.test)}</p>
<>
)
}
But this didn't work. Do you know any similar method?
If you are using TS you can with ts4.1 declare all the keys of the json as a valid inputs.
Check out the official example,
And working example out of it.
I believe this is what you are looking for
export enum VisitorType {
SCHOOL_VS,
SCHOOL_NOT_VS,
GROUP,
PRIVATE
}
...
resources: {
en: {
translation: {
visitorType: {
[VisitorType.SCHOOL_VS]: 'State school',
[VisitorType.SCHOOL_NOT_VS]: 'Non state school',
[VisitorType.GROUP]: 'Private groups',
[VisitorType.PRIVATE]: 'Private'
},
}
},
...
t(`visitorType.${VisitorType.SCHOOL_VS}`)

Render base component based on .env config varibles

I want to show the root component of the App based on config variable from react-native-config
I want to achieve something like this. I have a IS_STORYBOOK variable in the .env file, i want to setup my environment so i can just set the value from config and switch to main application and storyboard mode in my react-native application.
By doing this way.. i am getting this error bundling failed: SyntaxError: D:\Projects\React\React-Native Sample app\MobileApp\App.js: 'import' and 'export' may only appear at the top level (62:1)
//App.js
import React from 'react'; // eslint-disable-line
import { Provider } from 'react-redux';
import { pushNotifications } from './src/global/services';
import configureStore from './src/store/configureStore';
import {StackNavigator, createDrawerNavigator } from 'react-navigation'
import { generateStack } from './src/navigation/routesBuilder'
import Drawer from './src/components/drawer/container'
import {items} from './src/components/drawer/draweritems';
import DrawerIcon from './src/components/navIcons/drawerIcon'
import {data} from './src/global/data'
import {scale} from './src/utils/scale'
import StoryBook from './storybook';
import Config from 'react-native-config'
const store = configureStore();
pushNotifications.configure();
data.populateData();
const drawerRoutes = {"app.home":{
screen:generateStack("app.home", "Home", true,true)
}}
for(var i=0; i<items.length; i++){
drawerRoutes[items[i].navigateTo] = {
screen : generateStack(items[i].navigateTo, items[i].title, true, true),
}
}
const RootStack = StackNavigator({
Splash: {
screen: generateStack('app.splash', '', false, false),
navigationOptions:{
header: null
}
},
Auth: {
screen : generateStack('auth.login', '', false, false),
navigationOptions:{
header: null
}
},
Home:{
screen : createDrawerNavigator({
...drawerRoutes
},
{
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
drawerPosition:'left',
drawerWidth:scale(300),
drawerIcon : (<DrawerIcon />),
contentComponent: (props) => <Drawer {...props}/>
}),
}
}, {
headerMode:
'none'
});
export default class App extends React.Component {
render() {
if(Config.IS_STORYBOOK){
return <StoryBoard />
} else {
return(
<Provider store={store}>
<RootStack />
</Provider>
)
}
}
}
//storybook.js
import { AppRegistry } from "react-native";
import { getStorybookUI, configure } from "#storybook/react-native";
import { loadStories } from "./storyLoader";
configure(() => {
loadStories();
}, module);
const StorybookUI = getStorybookUI({
port: 7007,
host: "localhost",
onDeviceUI: true,
resetStorybook: true
});
AppRegistry.registerComponent("sampleproject", () => StorybookUI);
export { StorybookUI as default };
// .env
IS_STORYBOOK=false
React components are bundled during build-time and not during run-time.
You cannot conditionally export or import your components. Instead you should conditionally render stuff in your components.
Something like this might work for you
import StoryBook from './storybook';
export default class App extends React.Component {
render() {
if(!Config.IS_STORYBOOK){
return(
<Provider store={store}>
<RootStack />
</Provider>
)
} else {
return <StoryBook />
}
}
}

Resources