与路由的配合

这种方式很容易出现问题,应该将 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]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56