TS/ReactJS: can't change i18n language - javascript

I am now using TypeScript on my project and my i18n system was working fine until then. Now I can't toggle my languages with "i18n.changeLanguage([language])" and I don't find where is the problem.
import "./App.css";
import React from "react";
import { Header } from "./Header/Header";
import { useTranslation } from "react-i18next";
import i18n from "./i18n";
export const App = () => {
const { t } = useTranslation();
const changeLanguage = (lng: string) => {
i18n.changeLanguage(lng);
};
return (
<div className="App">
<Header changeLanguage={changeLanguage} /> // the toggle is here and works just fine
<Center t={t} />
</div>
);
};
My i18n.ts file looks like this:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import fr from "./assets/locales/fr/translation.json";
import en from "./assets/locales/en/translation.json";
export const defaultNS = "fr";
export const resources = {
fr: { fr },
en: { en },
};
i18n.use(initReactI18next).init({
lng: "fr",
fallbackLng: "fr",
ns: ["fr", "en"],
interpolation: {
escapeValue: false,
},
defaultNS,
resources,
});
export default i18n;
I don't know if it could be important but I changed my "whitelist:" for "ns:" since it doesn't work with TS.

I finally find another way to process, but without the i18n library.
My i18n.ts file:
import french from "./assets/locales/fr/translation.json";
import english from "./assets/locales/en/translation.json";
export interface Translations {
welcome: string;
lang: string;
// more variables and types...
}
const fr: Translations = french;
const en: Translations = english;
function getTranslations(language: string): Translations {
switch (language) {
case "fr":
return fr;
default:
return en;
}
}
export default getTranslations;
In my App.tsx :
import "./App.css";
import React, { useState } from "react";
import getTranslations from "./i18n";
export const App = () => {
const [translate, setTranslate] = useState(getTranslations("fr"));
const changeLanguage = (code: string) => {
setTranslate(getTranslations(code));
};
return (
<div className="App">
<Header changeLanguage={changeLanguage} /> // the toggle is here
<Center t={translate} /> // I can use the translated variables here
</div>
);
};
In the end, I needed an interface for my JSON files, a switch case to change the languages and a state to update.

Related

Cannot shallow render a component using IntlProvider

I have the following component, using Flow:
//#flow
import React, { type Node } from 'react';
import { useIntl } from 'react-intl';
type Props = { balance: Object };
const AvailableDiscount = ({ balance }: Props): Node => {
const { formatMessage, locale } = useIntl();
return (
<div>
{formatMessage({ id: 'XAM_DISCOUNT_DETAILS' })}: {balance.value}
</>
);
};
And while testing it, I seem to have a problem when trying it so mount it with shallow, using Enzyme:
// #flow
import { mount, shallow } from 'enzyme';
import React from 'react';
import { IntlProvider } from 'react-intl';
import balance from '../../../utils/testHelpers/testData/customerBalance';
import AvailableDiscount from './AvailableDiscount';
describe('AvailableDiscount', () => {
it('renders correctly', () => {
const component = <AvailableDiscount balance={balance} />;
const wrappingOptions = {
wrappingComponent: IntlProvider,
wrappingComponentProps: {
locale: 'en',
defaultLocale: 'en',
messages: {},
},
};
const mountedComponent = mount(component, wrappingOptions); // <-- This works
const shallowComponent = shallow(component, wrappingOptions); // <-- This does NOT work
});
});
It tells me that the component does not seem to be wrapped in the provider.
While this seems to work for mount, shallow keeps giving me this error. Why could this be?

Unable to render React function using ReactDOM

I'm trying to write tests for the rendering function Game(), which is for a Connect 4 react page.
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import * as useGame from "./hooks/use-game";
import Game from ".";
const STATE_SPY = jest.spyOn(useGame, "default");
STATE_SPY.mockReturnValue({
winner: "",
dimensions: {
numRows: 4,
numCols: 4,
},
squares: [
["", "", "", ""],
["", "", "", ""],
["", "", "", ""],
["", "", "", ""],
],
});
const { container } = render(<Game />, document.getElementById("game"));
When I run the tests with npm test, I get Target container is not a DOM element.
If I create an element around the error I get a different error:
const { container } = render(<Game />, document.createElement("div"));
TypeError: Cannot destructure property 'container' of '(0 , _reactDom.render)(...)' as it is null.
Any idea how I need to write this render() function properly? I've searched all over the Internet, but it seems that I have set this up in a pretty standard way.
Here's the code being tested:
import React from "react";
import { useGame } from "./hooks";
import { Board, Settings } from "./components";
import { DARK_SYMBOL, LIGHT_SYMBOL, UseStyles } from "styles/styles";
import { dimensionsFormInput } from "types/form-inputs";
const INIT_ROW = 6;
const INIT_COL = 7;
// Game keeps track of the active player and winners of the Connect Four game
export default function Game(): JSX.Element {
const {
dimensions,
setDimensions,
squares,
darkIsNext,
winner,
handleSquareClick,
} = useGame(INIT_ROW, INIT_COL);
const classes = UseStyles();
return (
<div id="game">
<Board
squares={squares}
onClick={(row: number, col: number) => handleSquareClick(row, col)}
winner={winner}
/>
<span className={classes.blueText}>
{winner.length > 0
? "Winner is " + winner
: "Next piece: ".concat(darkIsNext ? DARK_SYMBOL : LIGHT_SYMBOL)}
</span>
<Settings
dimensions={dimensions}
onSubmit={(data: dimensionsFormInput) => setDimensions(data)}
/>
</div>
);
}
did you try to create your own render function?, something like this, adapting the function to your needs, and use it in your test:
import React from 'react';
import { render as rtlRender } from '#testing-library/react';
import ConnectedRouter from 'react-router-redux/ConnectedRouter';
import { Provider } from 'react-redux';
import ReduxConnectedIntlProvider from '../ReduxConnectedIntlProvider';
import ThemeContext from '../context/theme-context';
import { store } from '../store';
import theme from '../theme/theme';
import { createMemoryHistory } from 'history';
const render = (
ui,
{
route = '/',
history = createMemoryHistory({ initialEntries: [route] }),
...renderOptions
} = {}
) => {
const Wrapper = ({ children }) => {
return (
<Provider store={store}>
<ReduxConnectedIntlProvider>
<ThemeContext.Provider value={theme}>
<ConnectedRouter history={history}>{children}</ConnectedRouter>
</ThemeContext.Provider>
</ReduxConnectedIntlProvider>
</Provider>
);
};
return { ...rtlRender(ui, { wrapper: Wrapper, ...renderOptions }), history };
};
// re-export everything
export * from '#testing-library/react';
// override render method
export { render };
The code for the ReduxConnectedIntlProvider:
import { connect } from 'react-redux';
import { IntlProvider } from 'react-intl';
function mapStateToProps(state) {
const { language, messages } = state.intl;
return { locale: language, key: language, messages };
}
export default connect(mapStateToProps)(IntlProvider);
Regards.
This import is correct? import Game from "." i think that the correct is: import Game from "./"
Instead of using render from ReactDOM, I used render from 1#testing-library/react`. This allows me to conduct the tests I want to.

React native - Data fetch from Async storage before rendering the app component

I am creating an iOS app on React Native and want to support multi languages for the app.
The application works when set the target language, but when I open the app again it shows the default language. I am trying to get the defined language from async storage but it does not reflect the changes and render the app component. I ma using context api for localization.
I use the Launchscreen.xib for showing the splash screen and hide this splash screen using react-native-splash-screen method SplashScreen.hide() in app.js useEffect hook and I want to set the previously chosen language before loading the app component.
Localization.js file -
import React, {createContext, useState} from 'react';
import * as RNLocalize from 'react-native-localize';
import LocalizedStrings from 'react-native-localization';
import AsyncStorage from '#react-native-community/async-storage';
import en from './translations/en.json';
import de from './translations/de.json';
import bg from './translations/bg.json';
import it from './translations/it.json';
import nl from './translations/nl.json';
import pl from './translations/pl.json';
import pt from './translations/pt.json';
import ru from './translations/ru.json';
import fr from './translations/fr.json';
import es from './translations/es.json';
const APP_LANGUAGE = 'appLanguage';
const DEFAULT_LANGUAGE = 'en';
const languages = {en, de, bg, it, nl, pl, pt, ru, fr, es};
const translations = new LocalizedStrings(languages);
export const LocalizationContext = createContext({
translations,
setAppLanguage: () => {},
appLanguage: DEFAULT_LANGUAGE,
initializeAppLanguage: () => {},
});
export const LocalizationProvider = ({children}) => {
const [appLanguage, setAppLanguage] = useState(DEFAULT_LANGUAGE);
//console.log(appLanguage);
const setLanguage = language => {
translations.setLanguage(language);
setAppLanguage(language);
AsyncStorage.setItem(APP_LANGUAGE, language);
};
const initializeAppLanguage = async () => {
const currentLanguage = await AsyncStorage.getItem(APP_LANGUAGE);
console.log('language' , currentLanguage);
if (currentLanguage === null) {
let localeCode = DEFAULT_LANGUAGE;
const supportedLocaleCodes = translations.getAvailableLanguages();
const phoneLocaleCodes = RNLocalize.getLocales().map(
locale => locale.languageCode,
);
phoneLocaleCodes.some(code => {
if (supportedLocaleCodes.includes(code)) {
localeCode = code;
return true;
}
});
setLanguage(localeCode);
} else {
setLanguage(currentLanguage);
}
onSuccess();
};
return (
<LocalizationContext.Provider
value={{
translations,
setAppLanguage: setLanguage,
appLanguage,
initializeAppLanguage,
}}>
{children}
</LocalizationContext.Provider>
);
};
App.js file
import React, {useEffect, useContext, useState} from 'react';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import {composeWithDevTools} from 'redux-devtools-extension';
import {Provider} from 'react-redux';
import logger from 'redux-logger';
import ReduxThunk from 'redux-thunk';
import loginReducer from '_store/reducers/login.js';
import transportReducer from '_store/reducers/transport.js';
import gpsReducer from '_store/reducers/gpslogger.js';
import AppNavigator from '_navigations/AppNavigator';
import SplashScreen from 'react-native-splash-screen';
import FlashMessage from 'react-native-flash-message';
import {LocalizationContext} from './Localization';
const rootReducer = combineReducers({
auth: loginReducer,
transport: transportReducer,
gps: gpsReducer
});
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(logger, ReduxThunk)));
const App = props => {
const {initializeAppLanguage, appLanguage} = useContext(LocalizationContext);
const [loaded, setLoaded] = useState(false);
useEffect(() => {
initializeAppLanguage();
SplashScreen.hide();
}, []);
return (
<Provider store={store}>
<AppNavigator />
<FlashMessage />
</Provider>
);
};
export default App;
How can I achieve the required behaviour?
I solved it by creating a separate splash screen and putting a setTimeout method there. it gives time to fetch the data from async storage and then navigate to main app.
We are doing sth that achieve the same wanted behavior in app.js
setI18nConfig = () => {
const {dispatch} = this.store;
const translationGetters = {
ar: () => require("../src/assets/translations/ar.json"),
en: () => require("../src/assets/translations/en.json"),
};
const translate = helpers.translate();
const fallback = { languageTag: "en", isRTL: false };
const { languageTag, isRTL } = this.appLanguage ? this.appLanguage : (RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) || fallback)
dispatch(setAppLanguage({
languageTag,
isRTL
}));
if(!this.appLanguage) {
this.appLanguage = {languageTag, isRTL}
}
// clear translation cache
translate.cache.clear();
// set i18n-js config
i18n.translations = { [languageTag]: translationGetters[languageTag]() };
i18n.locale = languageTag;
}

React-Auth0? Working! React-Redux-Auth0? I am losing my mind :/

This is a really long post, but I really need some help :/
I will be eternally grateful if someone would be able to help.
I have managed to get Auth0 working for an application i am working on with just react. It is an Overwatch SR tracker, and is essentially just a spreadsheet so I wasn't too concerned with protecting backend routes when I make them. There isn't any private information there.
My application state/props network became too complicated to manage, and through the process of implementing redux I simply cannot get it to work. I've been at it for three days, and I'm running out of ideas. Do I need Thunk with my current Auth setup to do this? I would imagine it is async since it needs to go get something that isnt there.
Granted I am a junior Dev, and dont have much experience with authentication. Can someone take a look at my working react application and guide me in the direction of what i may need to do to set it up with redux? I do have an understanding of redux flow, so if the proper method to do this was explained to me i feel i might get it.
here is some code:
my Auth.js file :
/*eslint no-restricted-globals: 0 */
import auth0 from "auth0-js";
import jwtDecode from 'jwt-decode';
const LOGIN_SUCCESS_PAGE = '/menu';
const LOGIN_FAILURE_PAGE = '/';
export default class Auth {
auth0 = new auth0.WebAuth({
domain: "redacted.auth0.com",
clientID: "redacted",
redirectUri: "http://localhost:3000/callback",
audience: "https://redacted.auth0.com/userinfo",
responseType: "token id_token",
scope: "openid profile"
});
constructor() {
this.login = this.login.bind(this);
}
login() {
this.auth0.authorize();
}
handleAuthentication() {
this.auth0.parseHash((err, authResults) => {
if (authResults && authResuslts.accessToken && authResults.idToken) {
let expiresAt = JSON.stringify((authResults.expiresIn) * 1000 + new Date().getTime());
localStorage.setItem("access_token", authResults.accessToken);
localStorage.setItem("id_token", authResults.idToken);
localStorage.setItem("expires_at", expiresAt);
location.hash = "";
location.pathname = LOGIN_SUCCESS_PAGE;
} else if (err) {
location.pathname = LOGIN_FAILURE_PAGE;
console.log(err);
}
});
}
isAuthenticated() {
let expiresAt = JSON.parse(localStorage.getItem('expires_at'));
return new Date().getTime() < expiresAt;
}
logout() {
localStorage.removeItem("access_token");
localStorage.removeItem("id_token");
localStorage.removeItem('expires_at');
location.pathname = LOGIN_FAILURE_PAGE;
}
getProfile() {
if (localStorage.getItem("id_token")) {
console.log(jwtDecode(localStorage.getItem("id_token")))
console.log(localStorage.getItem("id_token"));
return jwtDecode(localStorage.getItem("id_token"));
} else {
return {
name: 'Anon',
nickname: 'Anon',
picture: 'placeholder',
uid: null,
}
}
}
}
my index.js file:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import Auth from './Auth';
import { BrowserRouter } from 'react-router-dom';
const auth = new Auth();
let state = {};
window.setState = (changes) => {
state = Object.assign({}, state, changes)
ReactDOM.render(
<BrowserRouter>
<App {...state} />
</BrowserRouter>,
document.getElementById('root'));
}
/* eslint no-restricted-globals: 0*/
let getUserProfile = auth.getProfile();
let initialState = {
owSrTrackInfo: {
infoSaved: false,
accounts: [],
},
user: getUserProfile,
location: location.pathname.replace(/^\/?|\/$/g, ""),
auth,
}
window.setState(initialState);
registerServiceWorker();
my App.js file:
import React, { Component } from "react";
import "./App.css";
import Main from "./Components/Main/Main";
import Menu from "./Pages/Menu/Menu";
import NotFound from "./Components/NotFound/NotFound";
import Callback from './Components/Callback/Callback';
import Header from './Components/Header/Header';
class App extends Component {
render() {
let mainComponent = "";
switch (this.props.location) {
case "":
mainComponent = <Main {...this.props} />;
break;
case "callback":
mainComponent = <Callback />
break;
case "menu":
mainComponent = this.props.auth.isAuthenticated() ? < Menu {...this.props} /> : <NotFound />;
break;
default:
mainComponent = <NotFound />;
}
return (
<div className="app">
<Header {...this.props} />
{mainComponent}
</div>
);
}
}
export default App;
my Callback.js component:
import React, {Component} from 'react';
import Auth from '../../Auth'
export default class Callback extends Component {
componentDidMount() {
const auth = new Auth();
auth.handleAuthentication();
}
render() {
return(
<p className="loading">Loading.....</p>
)
}
}
My current MAIN.js component:
import React, { Component } from "react";
export default class Main extends Component {
render() {
console.log(this.props.auth.getProfile())
return (
<div className="container">
<div className='container--logged-out'>
<h1 className="heading u-margin-bottom-small">welcome to redacteds' overwatch sr tracker</h1>
<p>Hello there {this.props.user.nickname}! Sign in single click or email via Auth0 so we can save your results, and make the app usable by more than one person. I intend for more than one person to use this, so just to launch it and so the app knows your spreadsheet from someone elses I'll tie each user to their own UID. Feel free to come back, log in, and get your spreadsheet for the season back anytime.</p>
</div>
Go to the app menu!
<button onClick={() =>this.props.auth.getProfile()}>asdgkljsdngk</button>
</div>
);
}
}
my current HEADER.js component:
import React, { Component } from 'react';
export default class Header extends Component {
render() {
return (
<header className="header">
<h1 className='header__text'>SR TRACKER</h1>
{this.props.auth.isAuthenticated() ?
<button className='btn btn--logout' onClick={() => this.props.auth.logout()}>Logout</button>
:
<button className='btn btn--login' onClick={() => this.props.auth.login()}>Login or Sign Up</button>}
</header>
)
}
}
I simply want to map this authentication to a redux store instead to be consitent with the rest of my app (when redux is implemented) I have blown it away and started over multiple times, but a rough idea of what my redux flow might look like is like this template i use and have successfully implemented several times:
redux store:
import { createStore, compose, applyMiddleware } from 'redux';
import { createLogger } from 'redux-logger';
import thunk from 'redux-thunk';
import rootReducer from './reducers/rootReducer';
export default function configureStore(initialState) {
const middleware = [
createLogger({
collapsed: false,
duration: true,
diff: true,
}),
thunk,
];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
window.devToolsExtension ? window.devToolsExtension() : format => format, // add support for Redux dev tools),
),
);
return store;
}
actionTypes.js in actions folder:
const actions = {
GET_FRIENDS: 'GET_FRIENDS',
REMOVE_FRIEND: 'REMOVE_FRIEND',
GET_MOVIES: 'GET_MOVIES',
GET_MOVIES_SUCCESS: 'GET_MOVIES_SUCCESS',
GET_MOVIES_FAILURE: 'GET_MOVIES_FAILURE',
DEVIN_FUN: 'DEVIN_FUN',
};
export default actions;
Sample actions page:
import axios from 'axios';
import actionTypes from './actionTypes';
export const getMoviesSuccess = data => {
return {
type: actionTypes.GET_MOVIES_SUCCESS,
data,
};
};
export const getMoviesFailure = () => {
return {
type: actionTypes.GET_MOVIES_FAILURE,
};
};
export const devinIsHavingFun = () => {
return {
type: actionTypes.DEVIN_FUN,
};
};
export const retrieveMovies = () => {
return function(dispatch) {
const API_KEY = 'trilogy';
dispatch(devinIsHavingFun());
axios
.get(`http://www.omdbapi.com?apikey=${API_KEY}&s=frozen`)
.then(data => {
dispatch(getMoviesSuccess(data.data.Search));
})
.catch(error => {
console.log(error);
dispatch(getMoviesFailure());
});
};
};
in the reducers folder wed have some files like initialState.js and root reducer that look like this respectively:
initialState.js:
export default {
friends: [],
movies: [],
};
rootReducer.js:
import { combineReducers } from 'redux';
import friends from './friendReducer';
import movies from './movieReducer';
const rootReducer = combineReducers({
friends,
movies,
});
export default rootReducer;
and a sample reducer:
import actionTypes from '../actions/actionTypes';
import initialState from './initialState';
export default function movieReducer(state = initialState.movies, action) {
switch (action.type) {
case actionTypes.GET_MOVIES_SUCCESS: {
return action.data;
}
default: {
return state;
}
}
}
I just dont know what to do. Do i need to use thunk? am I overthinking this? I'm pulling my hair out.
I also do connect my components in this fashion when redux is implemented :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as friendActionCreators from './actions/friendActions';
import * as movieActionCreators from './actions/movieActions';
....................
function mapStateToProps(state) {
return {
myFriends: state.friends,
movies: state.movies,
};
}
function mapDispatchToProps(dispatch) {
return {
friendActions: bindActionCreators(friendActionCreators, dispatch),
movieActions: bindActionCreators(movieActionCreators, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Please let me know if anyone can point me in the right direction. thank you so much in advance.

React-Intl how to switch locale and messages from variable

I'm trying to figure out how to change language using React-Intl. This is my first React App and it has been made with create-react-app, I'm not using Redux nor Flux.
In my index.js I have the following code:
import React from 'react';
import ReactDOM from 'react-dom';
import TodoApp from './components/TodoApp';
import registerServiceWorker from './registerServiceWorker';
import './index.css';
// Bootstrap CSS libraries
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/css/bootstrap-theme.css';
import { IntlProvider, addLocaleData } from 'react-intl';
import intlEN from 'react-intl/locale-data/en';
import intlES from 'react-intl/locale-data/es';
import intlMessagesES from './i18n/locales/es.json';
import intlMessagesEN from './i18n/locales/en.json';
addLocaleData([...intlEN, ...intlES]);
/* Define your translations */
let i18nConfig = {
locale: 'es',
messages: intlMessagesES
};
let changeLanguage = (lang) => {
i18nConfig = { locale: lang, messages: intlMessagesEN };
return i18nConfig;
}
ReactDOM.render(
<IntlProvider locale={ i18nConfig.locale } key={ i18nConfig.locale } messages={ i18nConfig.messages }>
<TodoApp onChangeLanguage={changeLanguage} />
</IntlProvider>,
document.getElementById('root'));
registerServiceWorker();
TodoApp is sending a string on 'lang' parameter by props (i.e.: 'es' or 'en'), when I change i18nConfig nothing seems to change with IntlProvider. My thought was that change my i18nConfig variable then all my app would change language as well.
I have FormattedMessages in TodoApp and my two JSON messages are filled like this:
// i18n/locales/en.json
{
"footer.add.placeholder": "Enter a name ...",
"footer.add.priority0.text": "No priority",
"footer.add.priority1.text": "Priority 1",
...
}
Do you know what am I missing on my code ?? Maybe I have not understand something right about React-Intl. Any advice will be helpful, thank you.
It works if you remove all from root:
ReactDOM.render(<TodoApp />, document.getElementById('root'));
But now we change TodoApp component like this:
1) We add 'locale' as component state and import React-Intl:
import { IntlProvider, addLocaleData } from 'react-intl';
import intlEN from 'react-intl/locale-data/en';
import intlES from 'react-intl/locale-data/es';
import intlMessagesES from './../i18n/locales/es.json';
import intlMessagesEN from './../i18n/locales/en.json';
addLocaleData([...intlEN, ...intlES]);
/* Define your default translations */
let i18nConfig = {
locale: 'en',
messages: intlMessagesEN
};
2) Change our changeLanguage function (this time called 'onChangeLanguage'), this function receives 'lang' from a subComponent language selector:
onChangeLanguage(lang) {
switch (lang) {
case 'ES': i18nConfig.messages = intlMessagesES; break;
case 'EN': i18nConfig.messages = intlMessagesEN; break;
default: i18nConfig.messages = intlMessagesEN; break;
}
this.setState({ locale: lang });
i18nConfig.locale = lang;
}
And finally render:
render() {
return (
<IntlProvider key={ i18nConfig.locale } locale={ i18nConfig.locale } messages={ i18nConfig.messages }>
<div>
<Header onChangeLanguage={this.onChangeLanguage} />
// Other components ...
</div>
</IntlProvider>
);
}
If someone doesn't understand at all, ask in comments! Thanks to #TomásEhrich
With a new React Context API it is quite easy to do. Create a wrapper:
import React from "react";
import Types from "prop-types";
import { IntlProvider, addLocaleData } from "react-intl";
import en from "react-intl/locale-data/en";
import de from "react-intl/locale-data/de";
import deTranslation from "../../lang/de";
import enTranslation from "../../lang/en";
addLocaleData([...en, ...de]);
const Context = React.createContext();
class IntlProviderWrapper extends React.Component {
constructor(...args) {
super(...args);
this.switchToEnglish = () =>
this.setState({ locale: "en", messages: enTranslation });
this.switchToDeutsch = () =>
this.setState({ locale: "de", messages: deTranslation });
// pass everything in state to avoid creating object inside render method (like explained in the documentation)
this.state = {
locale: "en",
messages: enTranslation,
switchToEnglish: this.switchToEnglish,
switchToDeutsch: this.switchToDeutsch
};
}
render() {
const { children } = this.props;
const { locale, messages } = this.state;
return (
<Context.Provider value={this.state}>
<IntlProvider
key={locale}
locale={locale}
messages={messages}
defaultLocale="en"
>
{children}
</IntlProvider>
</Context.Provider>
);
}
}
export { IntlProviderWrapper, Context as IntlContext };
And then use that Provider and Consumer:
import { Provider } from "react-redux";
import { IntlProviderWrapper } from "./IntlContext";
class App extends Component {
render() {
return (
<Provider store={store}>
<IntlProviderWrapper>
...
</IntlProviderWrapper>
</Provider>
);
}
}
somewhere in the app:
import React from "react";
import { Text, Button } from "native-base";
import { IntlContext } from "../IntlContext";
const LanguageSwitch = () => (
<IntlContext.Consumer>
{({ switchToEnglish, switchToDeutsch }) => (
<React.Fragment>
<button onClick={switchToEnglish}>
English
</button>
<button onClick={switchToDeutsch}>
Deutsch
</button>
</React.Fragment>
)}
</IntlContext.Consumer>
);
// with hooks
const LanguageSwitch2 = () => {
const { switchToEnglish, switchToDeutsch } = React.useContext(IntlContext);
return (
<>
<button onClick={switchToEnglish}>English</button>
<button onClick={switchToDeutsch}>Deutsch</button>
</>
);
};
export default LanguageSwitch;
Example on CodeSandbox
Here is the relevant repository with a more general solution.
Note: at the moment react-intl is still using an old context API but in the future solution like this might work out-of-the-box.
you can use redux manage your locale and localeMessage. just add a key in IntlProvider.
import React, { Component } from 'react';
import { IntlProvider } from 'react-intl';
class Inter extends Component {
render() {
let { locale, localeMessage, children } = this.props;
return (
<IntlProvider key={locale} locale={locale} messages={localeMessage}>
{children}
</IntlProvider>
)
}
};
export default Inter;

Categories