与路由的配合

这种方式很容易出现问题,应该将 language 存储到 localStorage 或其他缓存,而非根据 url 匹配

重点是加载切换

i18n 下的路由示例(hash 模式要加前缀)

  • /en-US/***
  • /zh-CN/***
  • /zh-TW/***

示意图

加载机制.drawio.svg切换机制.drawio.svg


与 react-router 结合的核心代码

import { useHistory, useLocation } from 'react-router-dom'
import { useMemo } from 'react'
import { LanguageEnum } from '@liuli-util/i18next-util'

export function convertLanguagePrefix(value: LanguageEnum): string
export function convertLanguagePrefix(value: string): LanguageEnum
export function convertLanguagePrefix(
  value: LanguageEnum | string,
): LanguageEnum | string {
  const LanguagePrefixMap = {
    [LanguageEnum.En]: '/en-US/',
    [LanguageEnum.ZhCN]: '/zh-CN/',
    '/zh-CN/': LanguageEnum.ZhCN,
    '/en-US/': LanguageEnum.En,
  }
  return LanguagePrefixMap[value as keyof typeof LanguagePrefixMap]
}

export function useLanguage(init: LanguageEnum): LanguageEnum {
  const location = useLocation()
  const history = useHistory()
  return useMemo(() => {
    const regexp = /(\/.+?\/)/
    const res = regexp.exec(location.pathname)
    if (!res) {
      history.push(convertLanguagePrefix(init))
      return init
    }
    return convertLanguagePrefix(res[0])
  }, [history, location.pathname])
}
function App() {
  const language = useLanguage(LanguageEnum.En)
  const [{ value: list }, fetch] = useAsyncFn(
    async (language) => {
      console.log('language: ', language)
      await i18n.init({ en, zhCN }, language)
      const prefix = convertLanguagePrefix(language)
      return routeList().map((item) => ({
        ...item,
        path: PathUtil.join(prefix, item.path as string),
      }))
    },
    [language],
  )

  useMount(() => fetch(language))

  const location = useLocation()
  const history = useHistory()

  async function changeLanguage(value: LanguageEnum) {
    const path = location.pathname.replace(
      convertLanguagePrefix(language),
      convertLanguagePrefix(value),
    )
    console.log('path: ', language, value, path)
    await fetch(value)
    history.push(path)
  }

  return (
    <div>
      <header>
        <ul>
          {list &&
            list.map((item) => (
              <li key={item.path as string}>
                <Link to={item.path as string}>{item.title}</Link>
              </li>
            ))}
        </ul>
        <section>
          <button onClick={() => changeLanguage(LanguageEnum.En)}>
            English
          </button>
          <button onClick={() => changeLanguage(LanguageEnum.ZhCN)}>
            中文
          </button>
        </section>
      </header>
      <section>
        <Suspense fallback={'loading...'}>{renderRoutes(list)}</Suspense>
      </section>
    </div>
  )
}