使用 react-18next 外部组件(我需要代码审查)

时间:2021-01-07 11:43:55

标签: javascript reactjs internationalization i18next react-i18next

在我的项目中,我有很多 enum 文件,在我的 React 项目中随处可见,其中包含需要翻译的文本。

这是一个简单的例子(不工作):

// src/index.js 

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import * as serviceWorker from './serviceWorker'
import './i18n'

ReactDOM.render(<App />, document.getElementById('root'))

serviceWorker.register()
// src/i18n.js

import i18n from 'i18next'
import Backend from 'i18next-http-backend'
import LanguageDetector from 'i18next-browser-languagedetector'
import { initReactI18next } from 'react-i18next'

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    ns: ['pokemons'],
    defaultNS: 'pokemons',
    lng: ['en', 'fr'],
    fallbackLng: 'en',
    loadPath: '/locales/{{lng}}/{{ns}}.json',
    debug: true,
    interpolation: {
      escapeValue: false,
    },
  })

export default i18n

// src/App.js

import React, { Suspense } from 'react'
import pokemons from './enums/pokemons'

const PokemonItem = ({ pokemon: { id, name, type } }) => {
  return (
    <div>
      <span>{id}</span>
      <span>{name}</span>
      <span>{type}</span>
    </div>
  )
}

const App = () => {
  return (
    <Suspense fallback="loading...">
      {pokemons.map((pokemon) => (
        <PokemonItem key={pokemon.id} pokemon={pokemon} />
      ))}
    </Suspense>
  )
}

export default App
// public/locales/en/pokemons.json

{
  "Bulbasaur": {
    "name": "Bulbasaur",
    "type": "Grass/Poison"
  },
  "Charmander": {
    "name": "Charmander",
    "type": "Fire"
  },
  "Squirtle": {
    "name": "Squirtle",
    "type": "Water"
  }
}
// public/locales/fr/pokemons.json

{
  "Bulbasaur": {
    "name": "Bulbizarre",
    "type": "Plante/Poison"
  },
  "Charmander": {
    "name": "Salamèche",
    "type": "Feu"
  },
  "Squirtle": {
    "name": "Carapuce",
    "type": "Eau"
  }
}
// src/enums/pokemons.js

import i18n from './i18n'
// import i18n from 'i18n'  <-- I tried that too

const t = i18n.getFixedT(null, 'pokemons')
const pokemons = [
  {
    id: 1,
    name: t('Bulbasaur.name'),
    type: t('Bulbasaur.type'),
  },
  {
    id: 4,
    name: t('Charmander.name'),
    type: t('Charmander.type'),
  },
  {
    id: 7,
    name: t('Squirtle.name'),
    type: t('Squirtle.type'),
  },
]

export default pokemons

在这个例子中,我没有使用 react-i18next 提供的钩子/HOC,因为我的文本位于外部或 React 范围,如果我尝试使用 i18n 实例(或直接使用 lib 的默认导出) ),它不起作用,因为实例尚未初始化。

经过多次尝试,我最终得到了一个奇怪的工作解决方案,奇怪到让我怀疑它是否是一个好的解决方案:

我创建了一个“useEnum”钩子:

// hooks/enums.js 

import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'

const useEnum = (fileName, defaultValue = null) => {
  const { t, i18n } = useTranslation('enums')
  const [data, setData] = useState(defaultValue)

  useEffect(() => {
    import(`enums/${fileName}`).then((data) => setData(data.default(t)))
  }, [i18n.language])

  return data
}

export default useEnum

我更改了我的枚举文件:

const pokemons = (t) => [
  {
    id: 1,
    name: t('Bulbasaur.name'),
    type: t('Bulbasaur.type'),
  },
  {
    id: 4,
    name: t('Charmander.name'),
    type: t('Charmander.type'),
  },
  {
    id: 7,
    name: t('Squirtle.name'),
    type: t('Squirtle.type'),
  },
]

export default pokemons

我就是这样使用的:

import React, { Suspense } from 'react'

const PokemonItem = ({ pokemon: { id, name, type } }) => {
  return (
    <div>
      <span>{id}</span>
      <span>{name}</span>
      <span>{type}</span>
    </div>
  )
}

const App = () => {
  const pokemons = useEnum('pokemons', [])

  return (
    <Suspense fallback="loading...">
      {pokemons.map((pokemon) => (
        <PokemonItem key={pokemon.id} pokemon={pokemon} />
      ))}
    </Suspense>
  )
}
export default App

即使语言发生变化,此解决方案也能完美运行。问题是我在 useEffect 中使用了动态导入,我真的不知道在捆绑这些狗屎时 webpack 将如何处理这个问题。

所以我的问题是:“那...好吗?”,以及“是否还有其他解决方案可以在组件之外使用 i18next?”

谢谢,祝您有美好的一天!

更新:

发现我可以直接在 enums 文件中使用 i18n 实例,只有在不使用 http 后端时。

来自:

// src/i18n.js

import i18n from 'i18next'
import Backend from 'i18next-http-backend'
import LanguageDetector from 'i18next-browser-languagedetector'
import { initReactI18next } from 'react-i18next'

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    ns: ['pokemons'],
    defaultNS: 'pokemons',
    lng: ['en', 'fr'],
    fallbackLng: 'en',
    loadPath: '/locales/{{lng}}/{{ns}}.json',
    debug: true,
    interpolation: {
      escapeValue: false,
    },
  })

export default i18n

致:

// src/i18n.js

import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import { initReactI18next } from 'react-i18next'

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    resources: {
      fr: pokemonFr,
      en: pokemonEn
    },
    ns: ['pokemons'],
    defaultNS: 'pokemons',
    lng: ['en', 'fr'],
    fallbackLng: 'en',
    debug: true,
    interpolation: {
      escapeValue: false,
    },
  })

export default i18n

这样一来,i18n 实例就不再是 Promise,翻译是同步加载的。

现在我需要找到一种在更改语言时能够重新加载导入的方法。我真的很厌倦这个,我想我会以添加一个好的 ol 来结束'

i18n.on('languageChanged', () => window.location.reload())

有什么线索吗?

0 个答案:

没有答案