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
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>
)
}
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>
);
}
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
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}`)
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 />
}
}
}