web-dev-qa-db-fra.com

Comment utiliser react-i18next avec un composant connecté

Je voudrais utiliser react-i18next avec mon composant connecté react-redux et je ne sais pas comment procéder.

J'ai simplifié mon code pour montrer un exemple de composant connecté:

import React from 'react';
import {connect} from 'react-redux';
import {userSelectors} from "./userSelectors";

interface IConnectedProps {
    activeUserName: string | undefined;
}
export class LandingPageComponent extends React.Component<IConnectedProps> {
    public render(): JSX.Element {
        return (
            <React.Suspense fallback={<Spinner/>}>
                <React.Fragment>
                    <div>
                    ... a bunch of controls using translated text
                    </div>
                    <div>{activeUserName}</div>
                </React.Fragment>
            </React.Suspense>
        );
    }
}

const mapStateToProps = (state: ICoreRootState) : IConnectedProps => ({
    activeUserName: userSelectors.getDisplayName(state),
});

export const LandingPage = connect(mapStateToProps)(LandingPageComponent);

Versions des packages installés:

react version: 16.8.4 
react-redux version: 5.1.1 
react-i18next version: 10.6.0

Ce que j'ai essayé:

1) J'obtiens l'erreur ci-dessous lorsque j'utilise withTranslation, WithTranslation comme suit:

export class LandingPageComponent extends React.Component<IConnectedProps & WithTranslation> {...}
export const LandingPage = connect(mapStateToProps)(withTranslation()(LandingPageComponent));

Erreur:

The above error occurred in the <withI18nextTranslation(LandingPageComponent)> component:
    in withI18nextTranslation(LandingPageComponent) (created by Connect(withI18nextTranslation(LandingPageComponent)))
    in Connect(withI18nextTranslation(LandingPageComponent))
    in Route
    in t
    in Connect(t) (at App.tsx:49)
    in Switch (at App.tsx:45)
    in App (at src/index.tsx:14)
    in Router (created by ConnectedRouter)
    in ConnectedRouter (created by Connect(ConnectedRouter))
    in Connect(ConnectedRouter) (at src/index.tsx:13)
    in Provider (at src/index.tsx:12)

2) J'obtiens l'erreur ci-dessous lorsque j'utilise withTranslation, WithTranslation comme suit:

export class LandingPageComponent extends React.Component<IConnectedProps & WithTranslation> {...}
export const LandingPage = withTranslation()(connect(mapStateToProps)(LandingPageComponent));

Erreur:

index.js:1446 The above error occurred in the <withI18nextTranslation(Connect(LandingPageComponent))> component:
    in withI18nextTranslation(Connect(LandingPageComponent))
    in Route
    in t
    in Connect(t) (at App.tsx:49)
    in Switch (at App.tsx:45)
    in App (at src/index.tsx:14)
    in Router (created by ConnectedRouter)
    in ConnectedRouter (created by Connect(ConnectedRouter))
    in Connect(ConnectedRouter) (at src/index.tsx:13)
    in Provider (at src/index.tsx:12)

3) Je ne peux pas utiliser useTranslation car les hooks ne sont pas autorisés à être utilisés dans une classe.

J'ai également essayé ce qui suit:

... a bunch of imports
interface ILogoutButtonProps {
    userName?: string;
}
interface IConnectedHandlers {
    readonly logout: any;
    readonly Push: any;
}
class InnerLogoutComponent extends React.Component<IButtonProps & IConnectedHandlers & ILogoutButtonProps & WithTranslation, {}> {

    public render() {
        const {userName, onClick, logout: Logout, Push: Push, ...buttonProps} = this.props;
        const logoutText = this.props.i18n.t(StringNames.logout);
        const buttonText = userName ? logoutText + " " + userName : logoutText;
        return (
            <Button {...buttonProps} text={buttonText} onClick={this.handleClick}/>
        );
    }

    private handleClick = (event: React.MouseEvent<HTMLElement>) : void => {
        this.props.logout()
            .then(() => this.props.Push(LoginPaths.verifyUser));
    }
}
const InnerLogoutTranslatedComponent = withTranslation()(InnerLogoutComponent);

class LogoutComponentInternal extends React.Component<IButtonProps & IConnectedHandlers & ILogoutButtonProps, {}> {
    public render () {
        return (
            <InnerLogoutTranslatedComponent {...this.props}/>
        );
    }
}
export const LogoutComponent = connect(null,{logout, Push})(LogoutComponentInternal);

mais j'obtiens l'erreur suivante:

Hooks can only be called inside the body of a function component. 

Merci d'avance...

18
mdebeus

Dans notre projet, nous utilisons avec succès ceci:

import { compose } from 'redux';
import { withNamespaces } from 'react-i18next';
import { connect } from 'react-redux';
...
export default compose(withNamespaces('translation'), connect(mapStateToProps))(ComponentName);

Avec cela, nous nous connectons à Redux avec mapStateToProps et nous avons traductions.

1
Tomislav Milanović

J'utilise SSR avec RazzleJS et dans mon cas, cela fonctionne parfaitement bien. J'ai connecté mes connect et withTranslation comme ceci:

export default connect(mapStateToProps,mapDispatchToProps)(withTranslation()(Component));
1
Ahsan Ahmed

Cela fonctionne pour moi:

export default withTranslation()(connect(mapStateToProps, mapDispatchToProps)(Component));
0
Gregory Tippett

J'ai en fait du mal à déterminer dans quel ordre vous enveloppez vos composants dans des HOC. Dans le projet sur lequel je travaille actuellement, nous enveloppons comme withNamespaces(connect(withStyles(component))), ce qui fonctionne très bien (withNamespaces est essentiellement le même que withTranslations). Nous avons rencontré des problèmes lors de la tentative de connexion d'un composant traduit, vous rencontrez peut-être les mêmes problèmes en ce moment. Voici donc notre façon de procéder:

Vous avez un composant "normal" comme

type InjectedProps = StateProps & ExternalProps & MyComponentsTranslations
export class MyComponent extends React.Component<InjectedProps> {
    ...
} 

(Remarque: la procédure fonctionne exactement de la même manière avec les composants fonctionnels)

vous pouvez const MyConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(MyComponent)

et enfin vous faites

import {WithNamespaces, withNamespaces} from "react-i18next"
export const LocalizedMyComponent = withNamespaces()(
  ({t,...rest}): WithNamepsaces) => (
    <MyConnectedComponent translations={{ put translations here }} {...rest} />
  )
)

Maintenant, l'astuce est que nous définissons un interface MyComponentsTranslations {} Où nous mettons toutes les traductions ou les fonctions de traduction requises (dans le cas de pluriels). MyComponentsTranslations est ajouté au InjectedProps pour les rendre disponibles dans le composant d'origine.

Vous pouvez toujours simplement injecter la fonction t- de i18n dans vos composants, mais dans mon projet actuel, nous avons décidé qu'il est beaucoup plus propre de

  • nommer explicitement les traductions requises pour le composant
  • Étant donné que ni le composant d'origine ni le composant connecté ne dépendent de la fonction t, ils sont silencieux et faciles à tester

Faites-moi savoir si cela fonctionne pour vous.

De plus, pour rendre le tout un peu plus élégant, vous pouvez utiliser ces aides:

export interface Translations<T> {
  translations: T
}

export const createTranslations = <T>(translations: T): Translations<T> => ({
  translations,
})

Cela vous permet de définir

type InjectedProps = StateProps & Translations<MyComponentTranslations>

et dans le withNamespace hoc:

<MyConnectedComponent {...createTranslations<MyComponentTranslations>({ put translations here })} {...rest} />
0
konqi

Dans mon cas, je l'ai corrigé en faisant:

export default withTranslation(null, {withRef: true})(MyComponent);

withRef est false par défaut.

Source: https://github.com/i18next/react-i18next/blob/master/src/withTranslation.js

0
w35l3y