Excalidraw 国际化多语言包贡献指南
在远程协作日益成为主流工作方式的今天,一款真正“无国界”的工具往往能迅速赢得全球开发者的青睐。Excalidraw 就是这样一个典型——它以极简的手绘风格和实时协作能力,在架构设计、产品原型和教学演示中广受欢迎。但再优秀的设计,如果只说一种语言,它的影响力始终受限。
当一位中文用户第一次打开 Excalidraw 却面对满屏英文菜单时,那种轻微的认知摩擦,正是推动项目走向国际化的原始动力。而 Excalidraw 的聪明之处在于:它没有选择复杂庞大的国际化框架,而是用一套轻量、透明且极易参与的机制,把“翻译”这件事变成了社区共建的一部分。
这不仅是一个技术实现问题,更是一场关于开放与包容的实践。
核心机制解析
i18n 架构:从硬编码到动态加载
很多前端项目初期都会直接在代码里写死文本:
<button>Save</button>这种写法简单粗暴,但一旦要支持中文,就得改逻辑层,甚至可能需要维护多个分支。Excalidraw 走的是另一条路:所有界面文本全部外置为语言资源文件,运行时根据用户偏好动态注入。
它的核心思路其实很朴素——定义一个全局翻译函数t(key),比如:
<button>{t("buttons.save")}</button>然后通过键名去查找当前语言包中的对应值。这个过程不需要任何第三方库,完全由项目自研的轻量模块完成,既避免了打包体积膨胀,也降低了新贡献者的理解门槛。
更重要的是,这套机制允许非技术人员只需编辑 JSON 文件就能完成翻译任务。你不需要懂 React 或 TypeScript,只要会写 JSON 对象,就可以为母语用户发声。
多语言包结构设计哲学
Excalidraw 的语言文件统一放在/src/i18n/lang/目录下,每个语言对应一个.json文件,如zh.json、ja.json、fr.json等。这些文件采用扁平化的键命名方式,结构清晰直观:
{ "labels.undo": "撤销", "labels.redo": "重做", "menus.file": "文件", "exportImage": "导出图片" }为什么不用嵌套对象?这是有意为之的权衡。虽然嵌套结构看起来更有层次感(如{ labels: { undo: "撤销" } }),但在实际维护中容易引发合并冲突,尤其是在多人协作翻译时。扁平键名配合点号分隔,既能表达语义路径,又便于工具扫描和自动化处理。
此外,系统内置了智能回退机制:如果某个键在中文包中缺失,会自动尝试从英文包中读取。这意味着你可以逐步完善翻译,而不必一次性覆盖全部内容。这种“渐进式本地化”策略极大提升了社区参与的可持续性。
当前支持的关键参数
| 参数 | 含义 | 示例 |
|---|---|---|
lang.code | ISO 语言代码 | zh,es,ar |
lang.name | 语言显示名称 | "中文","Español" |
isRTL | 是否右向左书写 | true(阿拉伯语) |
dateFormat | 日期格式模板 | DD/MM/YYYY |
值得一提的是,对 RTL(Right-to-Left)语言的支持并非仅限于文本方向,还包括布局镜像、图标顺序调整等细节。例如阿拉伯语用户启用后,整个工具栏会自然地从右侧开始排列,带来真正的本地化体验。
动态语言切换是如何实现的?
Excalidraw 在启动时会按以下优先级确定默认语言:
- 检查
localStorage中是否保存过用户选择; - 读取浏览器
navigator.language设置(如zh-CN); - 若无匹配项,则降级使用
en(英文)作为兜底。
切换语言的过程也非常干净利落:
export const setLanguage = (code: string) => { const lang = languages[code] || en; currentLang = lang.data; document.documentElement.setAttribute("lang", code); };只需调用setLanguage("zh"),React 组件树就会重新渲染,所有调用t()的地方都会更新为中文文本。整个过程无需刷新页面,用户体验流畅。
如何参与多语言包贡献?
如果你希望为 Excalidraw 添加或改进某种语言的支持,流程非常简单。
第一步:克隆仓库并定位语言目录
git clone https://github.com/excalidraw/excalidraw.git cd excalidraw进入语言资源目录:
cd src/i18n/lang/你会看到类似如下结构:
en.json zh.json es.json ja.json ...第二步:创建或更新语言文件
假设你要贡献韩文(ko),可以复制en.json并重命名为ko.json:
cp en.json ko.json然后逐条翻译键值。注意保持键名不变,只修改右侧的字符串值:
{ "labels.undo": "실행 취소", "labels.redo": "다시 실행", "buttons.save": "저장", "menus.help": "도움말" }建议使用标准的 ISO 639-1 语言代码命名文件,如de.json(德语)、pt.json(葡萄牙语)等。
第三步:注册新语言
打开src/i18n.ts,将新语言添加到languages对象中:
const languages: Record<string, { label: string; data: LanguagePack }> = { en: { label: "English", data: en }, zh: { label: "中文", data: zh }, ko: { label: "한국어", data: ko } // 新增韩文 };这里的label是在语言选择器中显示的名称,应使用该语言的本地拼写。
第四步:本地测试与提交 PR
启动开发服务器预览效果:
npm run start访问http://localhost:3000,打开右上角设置面板,检查语言选项是否正常出现,并确认界面文本已正确切换。
一切正常后,提交你的更改:
git add . git commit -m "feat(i18n): add Korean language support" git push origin main最后前往 GitHub 创建 Pull Request。维护团队通常会在几天内审核,若无冲突即可合并。
✅小贴士:如果你发现某些键遗漏翻译,请不要留空字符串!保持原英文内容或暂时跳过,系统会自动回退显示英文,确保功能可用性不受影响。
AI 图表生成的本地化挑战与应对
随着 AI 插件生态的发展,Excalidraw 已支持通过自然语言指令生成图表。例如输入“画一个三层架构图”,AI 可自动生成包含前端、后端和数据库的草图。
但这引出了一个新的问题:AI 模型大多训练于英文语料,如何让中文用户也能顺畅使用?
目前主流解决方案是“双阶段翻译”策略:
- 用户输入中文指令 → 客户端翻译成英文 → 发送给 AI 模型;
- AI 返回英文结果 → 再次翻译回中文 → 渲染到画布。
这种方式虽然增加了一次网络请求,但优势明显:无需重新训练模型,即可快速支持数十种语言。
实现示例(Python 插件)
import requests def translate_text(text: str, source: str, target: str = "en") -> str: url = "https://api-free.deepl.com/v2/translate" payload = { "auth_key": "your_api_key", "text": text, "source_lang": source.upper(), "target_lang": target.upper() } response = requests.post(url, data=payload) return response.json()["translations"][0]["text"] def generate_diagram(prompt: str, user_lang: str = "zh"): if user_lang != "en": prompt = translate_text(prompt, source=user_lang, target="en") # 调用 GPT-4 解析指令 import openai response = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": f"Generate a diagram in Excalidraw JSON format: {prompt}"}] ) ai_output = response.choices[0].message.content # 可选:将输出中的标签翻译回用户语言 return ai_output这种方法已在多个社区插件中验证可行。未来随着多语言微调模型(如 multilingual BERT、GPT-4 Turbo 支持更多语言)的普及,有望实现端到端的理解与生成,进一步减少延迟和误译风险。
系统架构与协作模式
Excalidraw 的国际化架构呈现出典型的分层结构:
graph TD A[用户界面] --> B[i18n管理模块] B --> C[语言资源文件] C --> D[浏览器环境] D --> E[localStorage / navigator.language] F[AI插件] -- HTTP --> G[翻译API] G -- 请求 --> H[LLM服务] H -- 响应 --> F F --> A整个系统保持松耦合:UI 层不关心语言来源,只负责调用t();i18n 模块专注键值映射;资源文件独立存放,便于版本控制和协作。这种设计使得新增语言几乎零成本,也方便 CI/CD 流程自动检测翻译覆盖率。
更值得称道的是其社区驱动模式——每一个翻译 PR 都会被认真对待,即使只是补全几个按钮文本。这种“微贡献友好”的文化,正是开源项目生命力的源泉。
设计背后的思考
在构建国际化系统时,Excalidraw 团队做出了一系列务实的技术取舍:
- 拒绝重型框架:没有引入
react-i18next或linguijs,因为它们带来的 bundle size 增长对于一个轻量白板工具来说得不偿失。 - 优先高频词汇:确保“撤销”、“分享”、“导出”等高频操作翻译准确,低频菜单可暂缓。
- 品牌一致性:保留 “Excalidraw” 不翻译,防止混淆;但帮助文档、官网介绍则全面本地化。
- 防过度工程:不追求 100% 翻译率,允许部分字段回退英文,降低维护压力。
- 鼓励渐进完善:提供
TODO: translate注释标记待翻译项,引导社区持续补充。
这些决策背后体现的是一种克制的工程美学:用最小的代价,换取最大的可用性提升。
结语
Excalidraw 的国际化之路告诉我们,真正的全球化不是靠堆砌功能实现的,而是通过开放、透明和低门槛的协作机制,让世界各地的人们都能用自己的语言参与创造。
当你花半小时翻译几十个词条并提交 PR 时,你不仅仅是在“修 bug”或“加 feature”,而是在帮助一位从未谋面的巴西教师用葡萄牙语讲解课程,或是让一名日本工程师更顺畅地绘制系统架构。
这种连接,才是开源最动人的部分。
我们邀请每一位使用者:
- 检查你所使用的语言包是否完整;
- 补充缺失的翻译项;
- 推动 AI 插件支持你的母语指令;
- 在团队中倡导多语言协作习惯。
每一份微小的努力,都在让这个世界变得更易沟通一点。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考