爱意满满的作品展示区。
alangc

easy-lang 中文即 key,让 [前端国际化] 回归所见即所得!

  •  
  •   alangc · 19h 36m ago · 645 views

    Easy Lang:让前端国际化回归本质

    开源地址: https://github.com/chennlang/easy-lang · 觉得不错的话,欢迎 ⭐ Star 原文地址: https://juejin.cn/post/7673359458647179315 tips: 由于平台字数限制,让 AI 进行二次总结,如需查看完整内容,可点击上面链接。


    传统 i18n 的痛点

    不知道大家在前端国际化开发中有没有遇到这些问题:

    1. 变量命名——每次翻译都得想一个英文变量名,命名流程重复且繁琐
    2. 失去可读性——代码变成 t('login.title') 这类英文变量,想通过界面中文搜索定位模块几乎不可能
    3. 翻译流程复杂——先命名、再翻译、再写入多个翻译文件……

    传统 i18n 方案示例

    一个登录页面,传统方案翻译前是这样的:

    <h1>用户登录</h1>
    <label>用户名:</label>
    <button>登录</button>
    

    翻译后变成:

    <h1>{t('login.title')}</h1>
    <label>{t('login.username.label')}:</label>
    <button>{t('login.submit.text')}</button>
    

    同时需要维护多个语言文件( en-US 、zh-CN 、zh-TW 、ja-JP……),每个文件都要按模块组织嵌套结构,维护成本极高。

    核心问题总结

    问题 说明
    变量命名 每个中文都要想英文 key ,浪费精力
    可读性 代码中全是 t('xxx.xxx.xxx'),难以直观理解
    检索能力 复制页面中文无法搜到组件,只能搜到翻译文件
    高侵入性 国际化代码重构了业务逻辑,去不掉
    流程复杂 命名→改代码→翻译→写入多个文件
    复用性差 "确认""取消"等通用词在各模块重复定义

    开发应该只关注功能和业务,为什么要消耗这么多时间在国际化上?


    Easy Lang:回归本质

    我理解的国际化翻译原理其实很简单:

    const translations = {
        "退出登录": { "zh_CN": "退出登录", "en": "Logout" }
    }
    function t(text) { return translations[text][currentLang] }
    

    Easy Lang 正是基于这个理念开发的——中文即 Key ,所见即所得。

    安装

    pnpm add easy-lang
    

    快速上手

    1. 新建翻译文件 locales/translation.json

    {
      "用户登录": { "zh-CN": "用户登录", "en-US": "User Login" },
      "登录失败: {error}": { "zh-CN": "登录失败: {error}", "en-US": "Login failed: {error}" },
      "密码": { "zh-CN": "密码", "en-US": "Password" }
    }
    

    2. 创建实例 locales/index.ts

    import { createI18nTool } from "easy-lang";
    import translations from "./translation.json";
    
    export const i18nTool = createI18nTool({
      defaultLang: 'zh-CN',
      langs: ['zh-CN', 'en-US'],
      translations,
    });
    export const $t = i18nTool.$t;
    

    3. 在代码中使用:

    function LoginForm() {
      return (
        <div>
          <h1>{$t('用户登录')}</h1>
          <label>{$t('密码')}:</label>
          <button>{$t('登录')}</button>
          <span>{$t('您还可以尝试 {count} 次', { count: 3 })}</span>
        </div>
      );
    }
    

    对应的翻译文件只有 translation.json,所有语言集中管理,不用再在多个文件间切换。

    对比传统方案

    对比项 传统 i18n Easy Lang
    代码写法 t('login.title') $t('用户登录')
    变量命名 需要先想英文变量名 不需要
    翻译文件 每个语言一个文件 一个文件,中文即 key
    可读性 通篇英文变量 中文原样保留
    全局搜索 只能搜到翻译文件 直接搜中文定位组件

    模块化翻译(适用于大型项目)

    const translations = {
      default: { '你好': { "zh-CN": "你好", "en-US": "Hello" } },
      custom: { '欢迎 {name}': { "zh-CN": "欢迎 {name}", "en-US": "Welcome {name}" } }
    };
    
    const i18n = createI18nTool({ defaultLang: "zh-CN", langs: ["zh-CN", "en-US"], translations });
    
    // 指定模块
    i18n.$t('欢迎 {name}', { name: '张三', module: 'custom' });
    // 或创建专用函数
    const $t_custom = i18n.$module('custom');
    $t_custom('测试');
    

    类型安全:不带 module 时只允许 default 模块的 key ,带 { module: "xxx" } 时自动限定对应模块,享受完整类型提示。

    React 集成

    pnpm add @easy-lang/react zustand
    
    // locales/index.ts
    import { createI18nTool } from "easy-lang";
    import { createReactI18nTool } from "@easy-lang/react";
    import translations from "./translation.json";
    
    const reactI18nTool = createReactI18nTool(
      createI18nTool({ defaultLang: "zh_CN", langs: ["zh_CN", "en"], translations })
    );
    export const useTranslate = reactI18nTool.useTranslate();
    
    // App.tsx
    function App() {
      const { $t, changeLang, currentLang } = useTranslate();
      return (
        <div>
          <button onClick={() => changeLang("en")}>English</button>
          <div>{$t("用户登录")}</div>
        </div>
      );
    }
    

    changeLang 默认刷新页面;若只需响应式更新,设置 autoReload: false

    变量替换

    // translation.json: { "欢迎 {name}": { "en": "Welcome, {name}!" } }
    $t("欢迎 {name}", { name: "Tom" }); // => "Welcome, Tom!"
    

    强制指定语言

    $t("保存", {}, "zh_HK"); // 强制使用繁体中文
    

    运行时配置 configure()

    i18n.configure({
      defaultLang: "zh_CN",
      autoReload: false,        // 不刷新页面,响应式更新
      storageKey: "tenant-lang", // 自定义存储 key
    });
    

    自定义语言存储

    默认使用 localStorage,也支持从 query 参数、cookie 等来源读取:

    const i18n = createI18nTool({
      // ...其他配置
      storage: {
        getLang({ defaultLang, langs, storageKey }) {
          const stored = localStorage.getItem(storageKey);
          return stored && langs.includes(stored) ? stored : defaultLang;
        },
        setLang(lang, { storageKey }) {
          localStorage.setItem(storageKey, lang);
        },
      },
    });
    

    SSR 场景下自动安全降级。


    Easy Lang 解决了哪些问题?

    1. 不需要变量命名

    直接使用中文原文,不改变代码结构,只需用 $t() 包裹:

    $t("你好");
    $t("欢迎 {name}", { name: "Tom" });
    

    翻译文本原样保留,兼具可读性和搜索能力。

    2. 自带 TS 类型检测

    未翻译的文本会标红提示,排查更方便。

    3. 适应 AI 编辑器

    翻译文件结构简单,所有语言的翻译集中在同一个 key 下,Cursor 等工具的自动补全更加高效。

    4. 极简翻译流程

    所有未翻译文本会被收集到 i18n.untranslatedList,开发完成后打印出来,通过 AI 统一翻译后写回文件:

    console.log(i18n.untranslatedList); // ['暂无数据', '更新时间']
    

    5. 一词多意( context 支持)

    同一中文词在不同场景翻译不同:

    {
      "模型管理": {
        "zh-CN": "模型管理",
        "en-US": "Model Management",
        "contexts": {
          "sidebar": { "zh-CN": "模型", "en-US": "Models" }
        }
      }
    }
    
    $t('模型管理', { context: 'sidebar' }); // => "Models"
    

    6. 模块化隔离

    大型项目中各模块翻译独立,避免互相影响。


    VSCode 插件:翻译流程再简化

    配合 VSCode 插件,实现一键翻译未覆盖文本。

    安装

    从 GitHub Releases 下载 .vsix 文件,在 VSCode 中执行 Extensions: Install from VSIX... 安装。

    配置 .vscode/easy-lang.json

    {
      "translationPath": "locales/translation.json",
      "translateMode": "google",
      "targetLangs": ["en-US", "zh-CN", "zh-HK"]
    }
    

    功能

    • 侧边栏展示已翻译/未翻译列表
    • 点击"全部翻译"一键翻译并写入文件
    • 支持 Google 翻译或大模型翻译

    也可使用仓库自带的 Codex skill 自动生成配置。


    开发中遇到的问题及解决

    问题一:切换语言不刷新页面,如何响应式更新?

    最直接的方案是切换语言后刷新页面——实际场景中切换语言并不频繁,这是可接受的。

    若追求无感切换,配合 @easy-lang/react 的 hook 使用:

    export const useVARS = () => {
      const { $t } = useTranslate();
      return [$t('常量 1'), $t('常量 2')];
    };
    

    问题二:闭包中的翻译函数未更新

    闭包内的函数不会因 state 变化而重新生成,需要监听 $t 重新设置:

    useEffect(() => {
      setPagination({
        ...pagination,
        showTotal: (total) => $t(`总共 {total} 条`, { total }),
      });
    }, [$t]);
    

    AI/Codex Skill

    可通过以下提示词让 AI 自动接入或配置:

    应用接入 easy-lang 国际化:

    请安装 GitHub 仓库 chennlang/easy-lang 中的 Codex skill ,路径为 skills/easy-lang-app-i18n ,使用 $easy-lang-app-i18n 帮我在应用中接入 easy-lang 国际化。

    配置 VSCode 插件:

    请安装 GitHub 仓库 chennlang/easy-lang 中的 Codex skill ,路径为 skills/easy-lang-vscode-config ,使用 $easy-lang-vscode-config 帮我生成 easy-lang-vscode 插件所需的配置文件。


    使用体验

    切换到 Easy Lang 后最明显的感受:定位 BUG 效率大幅提升,直接搜索界面文字就能定位到组件。相较之前,节省了大量定位时间。


    最后

    如果 Easy-Lang 对你有帮助,欢迎到 GitHub 点个 ⭐ Star ,你的支持是我持续迭代的动力!也欢迎提交 Issue 和 PR ,一起让前端国际化这件事变得简单。

    Easy-Lang 使用 MIT 许可证开源,可以放心用到你的项目中。

    6 replies    2026-08-25 13:50:19 +08:00
    Razio
        1
    Razio  
       19h 21m ago
    现有的不一样也能 t('商品.表单.标题') 吗. 碰到多义词,重复的 key 不同翻译,不还是要 t("商品 1") t("商品 2")
    crocoBaby
        2
    crocoBaby  
       19h 21m ago
    可以做成编译时 ai 翻译
    alangc
        3
    alangc  
    OP
       17h 51m ago
    @Razio
    easy-lang 不仅可以 key 是中文,还支持 ts 未翻译提示;重复的 key 其实业务中真的不多,也支持用模块去区分。moulde1.t('商品'),moulde2.t('商品')

    @crocoBaby
    这个方式之前考虑过,本质上 google 翻译,AI 翻译都需要经过人校验,所以企业项目自动翻译是不可靠的
    crocoBaby
        4
    crocoBaby  
       17h 46m ago
    @alangc 用专用翻译 LLM
    94
        5
    94  
       17h 12m ago
    为什么会失去可读性,有那么多的插件可以直接显示成对应语言的翻译,以及自动提取 key 和自动翻译。




    [i18n Ally - Visual Studio Marketplace]( https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally)
    [Du I18N - Visual Studio Marketplace]( https://marketplace.visualstudio.com/items?itemName=DewuTeam.du-i18n)
    alangc
        6
    alangc  
    OP
       15h 23m ago
    @94
    1 、插件我用过,依赖插件只能显示,不支持检索。
    2 、换了其他编辑器插件就不能用了。vecoding 时代哪个编辑器轻量化就用哪个
    3 、easy-lang 也有 vscode 插件,支持 LLM 翻译和 google 两种方式
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   936 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 39ms · UTC 21:14 · PVG 05:14 · LAX 14:14 · JFK 17:14
    ♥ Do have faith in what you're doing.