背景
除了经常访问我网站的朋友,几乎就没人知道,我其实一直在做 ElasticSearch 文档的中文翻译,虽然不知道到底能帮助多少人,但既然已经坚持四年了,从 7.11 版本翻译到现在 9.x 版本,我就准备继续坚持下去。
在文档翻译过程中,我最开始的做法,是复制内容到百度翻译一类的网站上进行翻译,然后自己校对,调整格式,保存文档,发布到网站上。
后来,我找到一款比较好用的翻译工具 pot。这款工具很好用,适当减轻了一些工作量,但也只是让我不用去翻译网站上操作。
我翻译文档最大困难,在于翻译过程中,不能保留原网页的格式,我只能一段段翻译,再自己整理为 Markdown 格式,非常浪费时间。
在之前的文章《一种Tauri+NextJS国际化方案》中,我提到我们在开发一款 AI 应用。虽然它胎死腹中,但还是多少给互联网留下了一些电子垃圾。🤣
在文章中我还提到,我自己吃不来 react-i18next 的细糠,所以只能先撸了一个国际化方案来应急解决国际化问题。
我就想复用之前的开发经验,开发一款可以直接将网页翻译为指定 Markdown 格式文档的 AI 工具,这样我复制出翻译结果,就能直接保存为 Docusaurus 能解析的文档,效率倍增!
这次开发同样采用了 Tauri + Next.JS 架构,国际化也先复用了之前的文章中的经验。但应用初步开发出来后,我发现切换页面时,页面会出现闪动,会闪现未翻译的原始 key,非常难看。
AI help AI!
于是,我再次寻求 AI 帮助。可能得益于 AI 模型的升级,这次 DeepSeek 在"调教"下给出了一个基于 next-i18next 的可用方案,真是让我等对前端不熟悉的人觉得香死了。以下记录方案实施方法。
解决方案
1. 目标
Tauri应用中前端页面实现国际化切换功能
2. 场景
- 纯客户端的场景(
Tauri中无法使用Nextjs的SSR)。 Nextjs(AppRouter模式)
已基于 Tauri 和 Nextjs 开发了可运行的应用。
3. 安装依赖
bash
npm install i18next react-i18next i18next-browser-languagedetector
4. 目录结构示例

i18n.ts国际化配置文件locales国际化翻译 json 文件layout.tsx动态加载 i18n 对象
5. 关键文件示例
i18n.ts
tsx
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import en from "../../locales/en.json";
import zh from "../../locales/zh.json";
import { TauriAdapter } from "../utils/utils";
const adapter = new TauriAdapter();
export async function initI18n() {
// 从配置文件读取语言设置
const config = await adapter.readAppData();
const locale = config.locale || "zh";
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources: {
en: { translation: en },
zh: { translation: zh },
},
lng: locale,
fallbackLng: "zh",
interpolation: {
escapeValue: false,
},
});
return i18n;
}
export default i18n;
const config = await adapter.readAppData(); 是我自己实现的从配置文件读取当前语言的方法,如果你是其他方式读取语言,需要换成你自己的实现。
同时在以上配置项 resources 里需要把支持的语言都列出。
zh.json
json
{
"locales": {
"zh": "中文",
"en": "English"
}
}
json 格式的翻译文字。
layout.tsx
tsx
"use client";
import { useEffect, useState } from "react";
import { I18nextProvider } from "react-i18next";
import "./globals.css";
import "./lib/i18n"; // 导入 i18n 配置
import { initI18n } from "./lib/i18n";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const [i18nInstance, setI18nInstance] = useState<any>(null);
useEffect(() => {
const initializeApp = async () => {
try {
const i18n = await initI18n();
setI18nInstance(i18n);
} catch (error) {
console.error("Failed to initialize i18n:", error);
}
};
initializeApp();
}, []);
return (
<html lang="en">
<body className={`antialiased`}>
{i18nInstance ? (
<I18nextProvider i18n={i18nInstance}>
{children}
</I18nextProvider>
) : (
<div
data-tauri-drag-region
className="flex flex-col justify-center items-center h-screen"
>
<span className="loading loading-dots loading-xl text-primary h-[156px]"></span>
</div>
)}
</body>
</html>
);
}
在 layout.tsx 中使用国际化的 Provider,并动态加载 i18n 对象。
在文件中使用
tsx
const { t } = useTranslation();
....
<h1 className="text-[clamp(2rem,5vw,3rem)] font-bold text-gray-800 mb-4 text-center">
{t("app_name")}
</h1>
语言切换
在我们自己实现的语言切换选择器中,当选中新语言后,使用以下逻辑进行语言变更:
tsx
const { i18n, t } = useTranslation();
......
i18n.changeLanguage(newLang);
而语言变更后的国际化,就需要自己处理了。比如在 Tauri 中,目前我是将语言存储在配置文件中。
6.扩展
在以上实现中,我们知道页面上的国际化其实是依赖于全局 layout.tsx 中的国际化 Provider 支持的。但比如在 Tauri 中,还存在独立页面,比如独立的系统托盘菜单。
这种页面是没法依赖 Provider 来使用 t("app_name") 来实现国际化的,但可以通过引入 i18n 对象来实现。如下:
tsx
import i18n from "./lib/i18n";
......
i18n.t("translation.clipboard.conflict")
小记
作为非专业的前端,在 AI 帮助下解决了国际化的问题,我觉得值得记下此方案,以备日后复用。