Saltar al contenido

Aplicación multilingüe React-intl: cambio de idiomas y almacenamiento de traducciones

Solución:

Me he enfrentado al mismo problema y esto es lo que descubrí:

Para cambiar el idioma, utilicé la solución proporcionada aquí, que básicamente vincula IntlProvider a ReduxStore con la función Connect. Además, no olvide incluir una clave para volver a renderizar los componentes en el cambio de idioma. Este es básicamente todo el código:

Esto es ConnectedIntlProvider.js, solo enlaza IntlProvider predeterminado (observe la propiedad clave que falta en el comentario original en 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);

Y luego en su archivo de punto de entrada:

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

Lo siguiente que debe hacer es implementar reductor para administrar la configuración regional y hacer que los creadores de acciones cambien los idiomas a pedido.

En cuanto a la mejor manera de almacenar traducciones, encontré muchas discusiones sobre este tema y la situación parece bastante confusa, honestamente, estoy bastante desconcertado de que los creadores de react-intl se centren tanto en los formatos de fecha y número y se olviden de la traducción. Entonces, no sé la forma absolutamente correcta de manejarlo, pero esto es lo que hago:

Cree la carpeta “locales” y dentro cree un montón de archivos como “en.js”, “fi.js”, “ru.js”, etc. Básicamente, todos los idiomas con los que trabaja.
En cada archivo, exporte el objeto json con traducciones como esta:

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',
  }
}

Otros archivos tienen exactamente la misma estructura pero con cadenas traducidas en su interior.
Luego, en el reductor que es responsable del cambio de idioma, importe todos los estados de estos archivos y cárguelos en la tienda redux tan pronto como se envíe la acción para cambiar el idioma. Su componente creado en el paso anterior propagará los cambios a IntlProvider y se llevará a cabo una nueva configuración regional. Salida en la página usando <FormattedMessage> o intl.formatMessage({id: 'app.header.title'})}, lea más sobre eso en su wiki de github.
Tienen alguna función DefineMessages allí, pero honestamente no pude encontrar ninguna buena información sobre cómo usarla, básicamente puedes olvidarte de ella y estar bien.

Con una nueva API de contexto, creo que no es necesario usar redux ahora:

IntlContext.jsx

import React from "react";
import { IntlProvider, addLocaleData } from "react-intl";
import en from "react-intl/locale-data/en";
import de from "react-intl/locale-data/de";

const deTranslation = { 
  //... 
};
const enTranslation = { 
  //... 
};

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 };

Principal App.jsx componente:

import { Provider } from "react-redux";
import {  IntlProviderWrapper } from "./IntlContext";

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <IntlProviderWrapper>
          ...
        </IntlProviderWrapper>
      </Provider>
    );
  }
}

LanguageSwitch.jsx

import React from "react";
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;

Creé un repositorio que demuestra esta idea. Y también ejemplo de codeandbox.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *