React和i18n-通过在URL中添加语言环境进行翻译

时间:2019-07-12 08:12:45

标签: reactjs react-router internationalization i18next

你好,我正在构建一个演示应用程序,只是为了学习React,而且我对翻译过程还有些执着。我想做的是建立一个多语言网站,默认语言为“希腊语”,第二语言为“英语”。启用希腊语后,URL中不应包含任何语言环境,但是当启用英语时,应使用/ en /重写URL。

i18n配置

import translationEn from './locales/en/translation';
import translationEl from './locales/el/translation';
import Constants from './Utility/Constants';

i18n
    .use(Backend)
    .use(LanguageDetector)
    .use(initReactI18next)
    .init({
        fallbackLng: 'el',
        debug: true,
        ns: ['translations'],
        defaultNS: 'translations',
        detection: {
            order: ['path'],
            lookupFromPathIndex: 0,

        },
        resources: {

            el: {
                translations: translationEl
            },
            en: {
                translations: translationEn

            }
        },

        interpolation: {
            escapeValue: false, // not needed for react as it escapes by default

        }
    }, () => {

        // Why the fuck this doesnt work automatically with fallbackLng ??

        if (!Constants.allowed_locales.includes(i18n.language)) {

            i18n.changeLanguage(Constants.default_locale);
        }
        return;


    });

i18n.on('languageChanged', function (lng) {

    // if somehow it get injected

    if (!Constants.allowed_locales.includes(i18n.language)) {
        i18n.changeLanguage(Constants.default_locale);
    }


    // if the language we switched to is the default language we need to remove the /en from URL
    if (Constants.default_locale === lng) {

        Constants.allowed_locales.map((item) => {
            if (window.location.pathname.includes("/" + item)) {
                let newUrl = window.location.pathname.replace("/" + item, "");
                window.location.replace(newUrl);

            }
        })


    } else { // Add the /en in the URL

        // @todo: add elseif for more than 2 langs because this works only for default + 1 more language

        let newUrl = "/" + lng + window.location.pathname;
        window.location.replace(newUrl);


    }

});


export default i18n;

我使用Language Detector插件进行了 path 的检测,因此它可以从URL解析语言环境。现在没有初始化时添加的回调函数,如果URL是LanguageDetectorwww.example.com/en/www.example.com/el,则www.example.com/en/company将正确设置语言。但是,如果我直接访问了www.example.com/company(在第一次访问房屋之前,将设置语言环境),i18n会将语言环境/语言设置为"company" !!!

我认为fallbackLng有一个选项,如果LanguageDetector没有检测到它,它将设置为您配置的语言,但是似乎没有设置可用语言或默认设置的选项。对i18n使用语言(或者我是个白痴,无法找到它),因此LanguageDetector设置了他在URL中找到的任何内容。为了解决这个问题,我在上面添加了一个Constants文件和回调函数。

Contants.js

const Constants = {
    "allowed_locales": ['el','en'],
    "default_locale": 'el'
}

export default Constants;

我还添加了一个事件处理程序,该事件处理程序在LanguageChange上触发,因此如果英语处于活动状态,它将使用/ en /重写URL,而如果希腊语则将其删除/ el/。

index.js


ReactDOM.render(
    <BrowserRouter>
        <I18nextProvider i18n={i18n}>
            <App/>
        </I18nextProvider>
    </BrowserRouter>
    ,
    document.getElementById('root')
);

App.js


class App extends React.Component {


    render() {

        return (

            <div className="App">
                <Suspense fallback="loading">
                    <Header {...this.props}/>
                    <Routes {...this.props} />
                </Suspense>
            </div>

        );
    }
}

export default withTranslation('translations')(App);

index.js和App.js没什么特别的

标题组件


class Header extends React.Component {


    linkGenerator(link) {
        // if the current language is the default language dont add the lang prefix
        const languageLocale = this.props.i18n.options.fallbackLng[0] === this.props.i18n.language ? null : this.props.i18n.language;
        return languageLocale ? "/" + languageLocale + link : link;
    }

    render() {

        return (


            <div className="header">

                <Navbar bg="light" expand="lg">
                    <Container>
                        <Navbar.Brand className="logo" href="/"> <Image src="/assets/logo.png" rounded/>
                        </Navbar.Brand>
                        {/*Used For Mobile Navigation*/}
                        <Navbar.Toggle aria-controls="basic-navbar-nav"/>
                        <Navbar.Collapse id="basic-navbar-nav" className="float-right">
                            <Nav className="ml-auto">
                                <Nav.Link as={NavLink} exact to={this.linkGenerator("/")}>{this.props.t('menu.home')}</Nav.Link>
                                <Nav.Link as={NavLink} to={this.linkGenerator("/company")}>{this.props.t('menu.company')}</Nav.Link>

                            </Nav>
                            <Nav className="mr-auto">
                                {this.props.i18n.language !== "el" ? <button onClick={() => this.props.i18n.changeLanguage('el')}>gr</button>
                                    : null}
                                {this.props.i18n.language !== "en" ? <button onClick={() => this.props.i18n.changeLanguage('en')}>en</button>
                                    : null}
                            </Nav>

                        </Navbar.Collapse>

                    </Container>
                </Navbar>
            </div>


        )

    }
}

export default Header

为了使用语言环境创建菜单的网址,我创建了linkGenerator function

最后,在处理所有路由的Routes组件中,我在实际的url之前添加了一个常量,以便它适用于所有这些/page/el/page/en/page

路由组件

import React from 'react';
import {Switch, Route} from 'react-router-dom';

import CompanyPage from './Pages/CompanyPage';
import HomePage from './Pages/HomePage';
import NotFound from './Pages/NotFound';


class Routes extends React.Component {


    render() {
        const localesString = "/:locale(el|en)?";

        return (

            <Switch>
                <Route exact path={localesString + "/"} component={HomePage}/>
                <Route path={localesString + "/company"} component={CompanyPage}/>
                <Route component={NotFound}/>
            </Switch>
        );
    }


}

export default Routes

该代码以某种方式起作用,但是充满了诸如:

  1. 额外的配置文件(constants.js)

  2. 回调函数,用于将语言从“公司”更改为默认语言环境。 (这会触发2次页面重新加载)

  3. 功能来处理菜单中的语言环境并路由

等等。

在没有上述技巧的情况下,是否有任何“内置”功能或更好的方法来实现同一目标?

1 个答案:

答案 0 :(得分:0)

我也需要做同样的事情,我发现您可以设置i18n.init选项的whitelist属性并指定支持的语言。之后,如果您在checkWhitelist: true选项中设置了detection,则 LanguageDetector 仅在白名单数组中存在的语言下才匹配。

无论如何,您仍然需要定义languageChanged事件,以便在与默认语言匹配时重定向页面,但是,如果它是另一种受支持的语言(至少我不需要),则不再需要重定向。

我做过的最后一件事是,我先定义了languageChanged事件,然后才定义为 i18n.init ,这样它将首次触发设置该语言的事件

这是我的代码:

i18n.js

import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'

i18n.on('languageChanged', function (lng) {
  // if the language we switched to is the default language we need to remove the /en from URL
  if (lng === i18n.options.fallbackLng[0]) {
    if (window.location.pathname.includes('/' + i18n.options.fallbackLng[0])) {
      const newUrl = window.location.pathname.replace('/' + i18n.options.fallbackLng[0], '')
      window.location.replace(newUrl)
    }
  }
})

i18n
  .use(LanguageDetector)
  .init({
    resources: {
      en: {
        translation: require('./translations/en.js').default
      },
      pt: {
        translation: require('./translations/pt.js').default
      }
    },
    whitelist: ['en', 'pt'],
    fallbackLng: ['en'],
    detection: {
      order: ['path'],
      lookupFromPathIndex: 0,
      checkWhitelist: true
    },
    interpolation: {
      escapeValue: false,
      formatSeparator: '.'
    }
  })

export default i18n

App.js

import { Route, Switch } from "react-router-dom";

import AboutPage from "./AboutPage";
import HomePage from "./Homepage/HomePage";
import NotFoundPage from "./NotFoundPage";
import PropTypes from "prop-types";
import React from "react";
import { hot } from "react-hot-loader";
import {
  Collapse,
  Navbar,
  NavbarToggler,
  NavbarBrand,
  Nav,
  NavItem,
  NavLink } from 'reactstrap';
import i18n from "../i18n";

const baseRouteUrl = "/:locale(pt|en)?";
export const baseUrl = i18n.language === 'en' ? '' : '/'+i18n.language;

class App extends React.Component {
  state = {
    isOpen: false
  }

  render() {
    return (
      <div>
        <div>
          <Navbar color="grey" expand="md">
            <NavbarBrand href="/">Testing</NavbarBrand>
            <Nav className="ml-auto" navbar>
              <NavItem>
                <NavLink href={baseUrl + "/"}>Home</NavLink>
              </NavItem>
              <NavItem>
                <NavLink href={baseUrl + "/about/"}>About</NavLink>
              </NavItem>
            </Nav>
          </Navbar>
        </div>
        <Switch>
          <Route exact path={baseRouteUrl + "/"} component={HomePage} />
          <Route path={baseRouteUrl + "/about"} component={AboutPage} />
          <Route component={NotFoundPage} />
        </Switch>
      </div>
    );
  }
}

App.propTypes = {
  children: PropTypes.element
};

export default hot(module)(App);

在我的情况下,当我需要翻译某些内容时,我导入i18n.js并按如下所示调用相应的键:

<div>{i18n.t('home.bannerStart')}</div>