React i18next和更改语言的正确方法

22

我正在使用React、i18next和i18next-browser-languagedetector来开发一个多语言应用程序。

我是这样初始化i18next的:

<p>I am developing a multilanguage application using React, <code>i18next</code> and <code>i18next-browser-languagedetector</code>.</p><p>I initialize i18next the following way:</p>
i18n
  .use(LanguageDetector)
  .init({
    lng: localStorage.getItem(I18N_LANGUAGE) || "pt",
    fallbackLng: "pt",
    resources: {
      en: stringsEn,
      pt: stringsPt
    },
    detection: {
      order: ["localStorage", "navigator"],
      lookupQuerystring: "lng",
      lookupLocalStorage: I18N_LANGUAGE,
      caches: ["localStorage"]
    }
  });

export default i18n;

我已经实现了一个语言选择器,它只是改变localStorage中的值为用户所选择的语言。

这样做是正确的吗?

我问这个问题是因为尽管这种方式有效,但我感觉我在“欺骗”,通过设置 localStorage.getItem(I18N_LANGUAGE) || "pt" ,我没有像应该那样使用语言检测。


我正在尝试解决同样的问题,并找到了两个解决方案。第一个:你可以将语言环境作为URL参数存储(:locale\you_url),例如这个。第二个是你的变体-将语言环境存储在localStorage或cookies中。我想做和你一样的事情,但是i18next找不到键。你能否写一个示例或分享你的代码? - qwe asd
我们选择在localStorage中设置区域设置。你说i18next找不到键?顺便说一下,我正在使用react-i18next。请检查此链接: https://gist.github.com/pteixeira/4a75160ca15e3edf6975我还有一个语言选择器组件,在初始化时设置localStorage条目的值,检查默认值或用户选择的值。 - pteixeira
谢谢,这对我真的很有帮助。还有一个问题:是否有办法在运行时更改语言? - qwe asd
在运行时你是什么意思?如果你是在应用程序运行时询问的话,在我上面评论中提到的语言选择器组件中,我添加了一个事件监听器到一个选择组件,该组件具有我们支持的语言选项,并且我们通过使用 i18n.changeLanguage(ev.target.value);i18n来自于i18next)和 localStorage.setItem(I18N_LANGUAGE, ev.target.value); 来设置语言。希望这对你有用 :) - pteixeira
5个回答

10
根据文档,您无需自己指定语言:
import i18next from 'i18next';
import LngDetector from 'i18next-browser-languagedetector';

i18next
  .use(LngDetector)
  .init({
    detection: options
  });

根据 i18next源代码,它确实使用了插件的检测功能:

if (!lng && this.services.languageDetector) lng = this.services.languageDetector.detect();

这是正确的做法吗?

不,让插件自己完成任务就可以了。 :)


1
正确的回答是...如果你在初始化时设置了语言,语言检测器就不会执行其检测工作。可以标记为正确答案。最好的Jan(又名i18next的维护者jamuhl)。 - jamuhl
我查看了代码并进行了重构,以正确地使用插件。还将答案标记为已接受。 - pteixeira
我在这里有一个疑问。当然,插件可以完美地完成这部分工作,但是如果用户想要更新默认的首选语言而不使用插件设置的语言呢? 根据这个答案,用户将始终加载默认值,但是他/她将无法将所选的首选语言保存在本地存储中,然后重新加载应用程序。我们该如何实现这一点? - Sonhja

6
希望这对未来的某个人有所帮助。文档并没有完整地告诉你如何设置检测功能,然后我发现了一个 关闭的Github问题,其中有几个人提出了合理的问题,而维护者们的回答有点粗鲁,但也提供了一个 链接,它应该出现在文档中,但是在那个Github评论之外没有任何参考。那个示例通过从当前文档所述做一些小调整,解决了我的问题。
然后,我能够在我的url中使用 https:www.domain.com?lng=es 和使用浏览器扩展程序更改浏览器语言进行语言检测。
这是我的可用的 i18n.ts 文件:
import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import { initReactI18next } from 'react-i18next'
import XHR from "i18next-http-backend" // <---- add this

import commonDe from './locales/de/common.json'
import commonEn from './locales/en/common.json'
import commonEs from './locales/es/common.json'
import commonFr from './locales/fr/common.json'

const resources = {
  de: { common: commonDe },
  en: { common: commonEn },
  es: { common: commonEs },
  fr: { common: commonFr }
}

const options = {
  order: ['querystring', 'navigator'],
  lookupQuerystring: 'lng'
}

i18n
  .use(XHR) // <---- add this
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    // lng: 'en' // <--- turn off for detection to work
    detection: options,
    resources,
    ns: ['common'],
    defaultNS: 'common',
    fallbackLng: 'en',
    supportedLngs: ['de', 'en', 'es', 'fr'],
    interpolation: {
      escapeValue: false,
    },
    debug: false,
  })

export default i18n

我正在进行一个Next.js项目,上述文件是在project-root/pages/_app.tsx文件中加载的,如下所示:

(额外帮助 - 如果有人在这部分卡住了)

import React from 'react'
import { AppProps } from 'next/app'
import '../i18n/i18n'

import '../public/styles.css'

const TacoFridayApp = ({ Component, pageProps}: AppProps): JSX.Element => {
  
  return <Component {...pageProps} />
}

export default TacoFridayApp

4

我认为你非常接近了。您可以最初使用后备语言设置i18n。然后,在加载本地存储或本地缓存或任何存储的保存语言信息后,调用i18nInstance.changeLanguage(lng)


0

@firstdoit:

关于自动浏览器语言检测的好答案。然而,您不认为将自动和手动配置都提供给用户是更好的方法吗?

例如,如果一个用户将浏览器设置为英语,这对于您根据文档建议的自动方法来说是可以的。但是,如果用户将页面语言从英语更改为法语,这并不会影响浏览器语言,因此仅保持网站以英语显示,因为配置设置为自动检测浏览器语言。

我反过来会优先考虑当前页面的语言:

  • 通过参数传递(/george.php?lang=fr或/fr_FR/george.php)

这将作为属性传递给我的代码,如下所示:

  • var lang = this.props.lang || this.services.languageDetector.detect() || "en";

你怎么看?


0

我在这个问题上浪费了很多时间,但幸运的是taco_friday通过他的答案帮助了我。 在这里补充一下我的看法:

我基本上尝试从我的localStorage中加载数据到i18-next。 有一个名为i18next-localstorage-backend的插件可以方便地实现此操作,但我没有成功运行它。

但是,在从taco-friday分享的内容中找到灵感后,我可以相应地调整我的i18n.ts

import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import { initReactI18next } from 'react-i18next'
import BrowserStorageManager from './services/browserStorageManager'
import loadTranslations from './services/loadTranslations'

const service = new BrowserStorageManager()
loadTranslations()

const resources = {
    'de-CH': service.getItem<any>('de-CH')?.value,
}

i18n.use(LanguageDetector)
    .use(initReactI18next)
    .init({
        debug: false,
        fallbackLng: 'deCH',
        resources,
        interpolation: {
            escapeValue: false, // not needed for react!!
        },
    }) 

browserStorageManager.ts 的内容如下:

import logSymbols from 'log-symbols'

class BrowserStorageManager {
    public setItem(key: string, value: any): void {
        localStorage.setItem(key, JSON.stringify({ value }))
    }

    public getItem<T>(key: string): T | null {
        const data: string | null = localStorage.getItem(key)
        if (data !== null) {
            return JSON.parse(data)
        }
        console.log(logSymbols.error, ` ${key}-key is empty - aborting ...`)
        return null
    }
}

export default BrowserStorageManager

最后,我的localStorage中的resourceBundle结构如下:

{
    value:{
        translation:{
            key: "value",
            ....
        }
    }
} 

translation 是默认的命名空间,因此在 .init 中不需要定义它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接