React-intl multi language app: изменение языков и перевод хранения
У меня есть приложение react-router и я хотел бы добавить i18n. In react-intl пример корневой компонент, завернутый в IntlProvider:
ReactDOM.render(
<IntlProvider locale="en">
<App />
</IntlProvider>,
document.getElementById('container')
);
но есть только один язык. Как обновить приложение для добавления других языков и как лучше хранить переводы?
2 ответов
я столкнулся с той же проблемой и вот что я выяснил:
для изменения языка я использовал предоставленное решение здесь, который в основном связывает IntlProvider с ReduxStore с функцией Connect. Также не забудьте включить ключ для повторного рендеринга компонентов при изменении языка. Это в основном весь код:
Это ConnectedIntlProvider.js, просто связывает IntlProvider по умолчанию(обратите внимание на ключевую опору, которая отсутствует в исходном комментарии github)
import { connect } from 'react-redux';
import { IntlProvider } from 'react-intl';
// This function will map the current redux state to the props for the component that it is "connected" to.
// When the state of the redux store changes, this function will be called, if the props that come out of
// this function are different, then the component that is wrapped is re-rendered.
function mapStateToProps(state) {
const { lang, messages } = state.locales;
return { locale: lang, key: lang, messages };
}
export default connect(mapStateToProps)(IntlProvider);
а затем в файле точки входа:
// index.js (your top-level file)
import ConnectedIntlProvider from 'ConnectedIntlProvider';
const store = applyMiddleware(thunkMiddleware)(createStore)(reducers);
ReactDOM.render((
<Provider store={store}>
<ConnectedIntlProvider>
<Router history={createHistory()}>{routes}</Router>
</ConnectedIntlProvider>
</Provider>
), document.getElementById( APP_DOM_CONTAINER ));
следующее, что нужно сделать, это просто реализовать reducer для управления locale и сделать создателей действий для изменения языков по требованию.
Что касается лучшего способа хранения переводов - я нашел много дискуссий на эту тему, и ситуация кажется довольно запутанной, честно говоря, я совершенно сбит с толку, что создатели react-intl так много внимания уделяют форматам даты и числа и забывают о переводе. Так, я не знаю абсолютно правильный способ справиться с этим, но это то, что я делаю:
создать папку " locales "и внутри создать кучу файлов, таких как" en.js", " fi.js", " ru.js", etc. В основном все языки, с которыми вы работаете.
В каждом файле экспортируйте объект json с такими переводами:
export const ENGLISH_STATE = {
lang: 'en',
messages: {
'app.header.title': 'Awesome site',
'app.header.subtitle': 'check it out',
'app.header.about': 'About',
'app.header.services': 'services',
'app.header.shipping': 'Shipping & Payment',
}
}
другие файлы имеют точно такую же структуру, но с переведенными строками внутри.
Затем в reducer, который отвечает за изменение языка, импортируйте все состояния из этих файлов и загрузите их в redux store, как только будет отправлено действие по изменению языка. Ваш компонент, созданный на предыдущем шаге, распространит изменения в IntlProvider и будет иметь место новая локаль. Выведите его на страницу с помощью <FormattedMessage>
или intl.formatMessage({id: 'app.header.title'})}
, подробнее об этом читайте в их GitHub wiki.
У них есть некоторые функции DefineMessages, но, честно говоря, я не мог найти никакой хорошей информации, как ее использовать, в основном вы можете забыть об этом и быть в порядке.
С Новым контекстным API я считаю, что теперь не требуется использовать redux:
IntlContext.jsx
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 { Provider, Consumer } = 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 (
<Provider value={this.state}>
<IntlProvider
key={locale}
locale={locale}
messages={messages}
defaultLocale="en"
>
{children}
</IntlProvider>
</Provider>
);
}
}
export { IntlProviderWrapper as IntlProvider, Consumer as IntlConsumer };
Main App.jsx компоненты:
import { Provider } from "react-redux";
import { IntlProvider } from "./IntlContext";
class App extends Component {
render() {
return (
<Provider store={store}>
<IntlProvider>
...
</IntlProvider>
</Provider>
);
}
}
LanguageSwitch.jsx
import React from "react";
import { Text, Button } from "native-base";
import { IntlConsumer } from "../IntlContext";
const LanguageSwitch = () => (
<IntlConsumer>
{({ switchToEnglish, switchToDeutsch }) => (
<React.Fragment>
<button onClick={switchToEnglish}>
English
</button>
<button onClick={switchToDeutsch}>
Deutsch
</button>
</React.Fragment>
)}
</IntlConsumer>
);
export default LanguageSwitch;
Я создал хранилище что демонстрирует эту идею.