news 2026/9/12 5:07:38

34 种语言、49 个 PO 文件:Penpot 多语言本地化工作流完整实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
34 种语言、49 个 PO 文件:Penpot 多语言本地化工作流完整实战

34 种语言、49 个 PO 文件:Penpot 多语言本地化工作流完整实战

【免费下载链接】penpotPenpot: The open-source design platform for Product teams that need scalable collaboration.项目地址: https://gitcode.com/GitHub_Trending/pe/penpot

设计团队打开 Penpot 想以中文评审界面,整站却是英文;有人补了一条翻译,界面上又冒出auth.check-mail这种带点号的文本——这大概率不是 bug,而是翻译缺失的显式信号。Penpot 作为开源设计与原型平台,其多语言能力由三层构成:frontend/src/app/util/i18n.cljs里的翻译引擎、frontend/translations/下的 49 个 PO 文件、以及 Weblate 平台上的社区协作流程。本文按实际操作路径走一遍:先把界面切到目标语言,再读懂 PO 文件结构,然后本地验证改动,最后把翻译推回主线。

如何把 Penpot 本地界面切到 34 种语言之一

先拉取代码并启动开发环境:

git clone https://gitcode.com/GitHub_Trending/pe/penpot cd penpot ./manage.sh run-devenv

界面显示哪种语言,由i18n.cljs里的supported-locales表决定,共 34 个条目,覆盖zh_cnzh_hantarja_jpfr_ca等。语言确定分两条路:启动时autodetect读取navigator.language,经parse-locale归一化(zh-CN变成zh_cn,带国家码不支持时回退到纯语言码zh);也可以在设置里显式调用set-locale。两种路径都会校验白名单,查不到就落到cf/default-language(值为"en")。

locale 定下来后,load函数动态导入翻译表并挂到全局变量:

(let [path (str "./translation." locale ".js?version=" cf/version-tag)] (->> (mod/import path) (p/fmap (fn [result] (unchecked-get result "default"))) (p/fnly (fn [data cause] (if cause (js/console.error "unexpected error on fetching locale" cause) (set-translations locale data))))))

而所有 UI 文案统一走tr查表:

(defn tr ([code] (t *current-locale* code)) ([code & args] (apply t *current-locale* code args)))

用一句人话概括:组件里不硬编码任何字符串,文案全部由tr按当前 locale 查表返回。切语言本质上是换一张表,所以前端不存在"改代码换语言"这一步。

切完语言后你会发现一部分文案还是英文——这就是缺失回退到en的结果。要补上它,得先读懂 PO 文件。

如何读懂 frontend/translations 的 49 个 PO 翻译文件

frontend/translations/下有 49 个.po文件,按 ISO 语言码命名(zh_CN.poar.pofr_CA.po等),比运行时支持的 34 个多出的部分是尚未接入切换器的储备语言。每条翻译的长这样(取自zh_CN.po的真实条目):

#: src/app/main/ui/auth/register.cljs:214 msgid "auth.already-have-account" msgstr "已经有账号了?"

三个信息点都有用:msgid是键名,msgstr是译文,#:开头的注释指向该键在源码中的引用位置(文件加行号),排查上下文不用全局搜索。此外还有两个标志位:unused表示这个键已不再被任何源码引用,fuzzy表示译文待复核。文件头部的Plural-Forms: nplurals=2; plural=(n != 1)声明复数规则,中文和英文一样按"等于 1 / 不等于 1"分两支。

复数在代码侧的写法见frontend/src/app/main/ui/dashboard/fonts.cljs

[:span (tr "dashboard.fonts.fonts-added" (i18n/c (count fonts)))]

(i18n/c ...)把参数标记为计数值;t函数检测到翻译值是指数组时,按计数等于 1 还是其他取msgstr[0]msgstr[1]。这就是为什么.po里同一个键会同时出现msgidmsgid_plural两条。

回退链路值得记清楚:目标语言查不到该键,回落到enen也查不到,t直接返回键名本身。界面上看到带点号的文本,就按这条链路反查是哪个环节漏了。

PO 文件改好了,下一步是本地验证和批量维护。

如何本地验证翻译改动:watch 热更新与 rehash、sync 命令

开发服务自带热更新。frontend/scripts/watch.js第 95 行起注册了对 translations 目录的监听:

log.info("watch: translations (~)"); h.watch("translations", null, async function (path) { ... })

也就是说改完.po文件保存即可,前端会自动重新编译加载,不用手动重启。

批量维护用frontend/目录下的pnpm translations <子命令>,脚本是frontend/scripts/translations.js,提供四个子命令(以脚本自带的 help 输出为准):

  • rehash:用正则\(tr\s+"([\w\.\-]+)"扫描src/下所有.clj/.cljs/.cljc文件,把源码里出现但en.po里没有的键补录进去(标fuzzy并写入引用位置),同时把源码中已不引用的键标成unused,结束时打印Found N used translationsFound N unused strings两个数字;
  • sync:以en为基准对齐其他语言文件的键集合,基准里不存在的键直接从各语言文件中删除;
  • fuzzy <prefix>:把某前缀下的所有条目批量标记为 fuzzy,适合英文原文批量改动后要求各语言重新确认;
  • delete <prefix>:按前缀删除条目。

日常节奏是:新增文案后先跑rehash补键,各语言翻译完成后再跑sync清掉孤儿键。本地验证通过后,改动要进入主线还得走审核流程。

如何把翻译推回主线:Weblate 协作与 waiting for review 状态机

Penpot 的官方翻译平台是 Weblate,流程定义在docs/contributing-guide/translations/index.njk。入场的门槛是:注册 Weblate 账号,然后在仓库提交 issue,写清三样东西——要翻译的语言、翻译类型(新语言 / 补字符串 / 改已有译文)、你的 Weblate 用户名,由团队授予对应权限。

上图为 Weblate 的语言列表页,每行显示一种语言的进度,铅笔图标用于进入该语言的字符串编辑。

三类操作的状态流转不同:

  • 新增语言:在语言列表页点 "Start new translation",选择语言后开始逐条翻译;
  • 补充缺失字符串:点语言行的编辑图标,找到未翻译条目填入译文并保存,状态自动变为 "waiting for review";
  • 修改已批准的译文:进入该语言点 Browse,有权限直接 Save(同样进入待审核),没有权限则点 Suggest,条目状态标记为 "Approved strings with suggestions"。

上图是单个语言的字符串列表,未翻译条目一目了然,补漏时按这个清单逐条处理即可。

团队会定期检查待审核字符串,确认无误后批准,批准的结果随下个版本发布。界面文案这条链路到此闭环,但多语言产品还有另一半:设计稿本身要能承载不同语言的排版。

设计稿区域化:组件覆盖与灵活布局的多语言适配

i18n 体系只覆盖产品界面文案,用户设计稿里的多语言适配要靠设计手段。官方文档docs/user-guide/designing/text-typo.njk覆盖了 RTL(从右到左)文本的排版规则,阿拉伯语、希伯来语这类市场在设计阶段就要按 RTL 习惯安排对齐与留白。

组件层的做法是把语言作为变体维度:一个按钮组件拆出"中文-主要""英文-次要"等变体,实际使用时通过组件覆盖(component override)替换为对应语言的文本节点,而不是复制整份组件。

上图展示了在组件实例上覆盖属性的操作,语言变体切换走的正是这条机制。

文本长度差异用灵活布局吸收:容器不写死宽度,依赖 flex 的换行与伸缩规则(docs 中的 flexlayout 系列演示),德语比英语长 30% 左右、东亚文字密度更高时,布局自动重排而不会撑破容器。关键文案建议再收敛到设计标记(Design Tokens)统一管理,docs/user-guide/design-tokens/有完整说明。

设计和翻译两边都就绪后,交付前过一遍下面的清单。

交付前检查清单

  • i18n.cljssupported-locales中确认目标语言已注册,否则界面切换器不会出现该语言;
  • 逐个切换目标语言,扫描界面有无带点号的键名文本,有则说明该键缺失或回退到en
  • 打开目标语言的.po文件头,核对Plural-Forms与该语言的复数规则一致,并抽查一条msgid_plural的两支译文;
  • frontend/下执行pnpm translations rehash,确认输出中没有新增fuzzy条目、没有遗留该清理的unused键;
  • 需要多语言键集合对齐时补跑pnpm translations sync,确认没有其他语言文件残留孤儿键;
  • Weblate 上的目标条目状态为已批准(approved),而非停留在 "waiting for review";
  • 设计稿侧:组件覆盖的语言变体文本已更新,承载多语言文本的容器使用灵活布局而非固定宽度。

七项全绿,这套从tr查表到 PO 文件、再到社区审核的 Penpot 多语言链路就算完整跑通了。

【免费下载链接】penpotPenpot: The open-source design platform for Product teams that need scalable collaboration.项目地址: https://gitcode.com/GitHub_Trending/pe/penpot

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 1:28:05

Alacritty 终端模拟器深度解析:GPU 加速如何让你甩开卡顿

Alacritty 终端模拟器深度解析&#xff1a;GPU 加速如何让你甩开卡顿 【免费下载链接】alacritty A cross-platform, OpenGL terminal emulator. 项目地址: https://gitcode.com/GitHub_Trending/al/alacritty Alacritty 是一款用 Rust 编写的跨平台 OpenGL 终端模拟器&…

作者头像 李华
网站建设 2026/9/4 9:07:58

机器学习流程卡顿时先查哪里

机器学习流程卡顿时先查哪里本文围绕“卡顿时先查哪里”整理可复现的检查思路。所有阈值、配置和结果均应在隔离环境中记录输入、版本与资源条件后再解释&#xff1b;下文示例不对应真实组织、用户、流量或成本数据。 1. 用受控样例界定问题 # 登上卡顿节点&#xff0c;检查 GP…

作者头像 李华
网站建设 2026/9/4 16:32:11

故障复盘要留下证据和改动,别只留下“加强注意”

故障复盘要留下证据和改动&#xff0c;别只留下“加强注意”线上故障发生后&#xff0c;团队通常很快能找到一个表面原因&#xff1a;某次发布、一个超时、一次连接耗尽。真正难的是把现场保存下来&#xff0c;并让下一次相似问题更难发生。若复盘只写“开发不够仔细”“加强测…

作者头像 李华
网站建设 2026/9/4 16:21:54

LingBot-Map特殊Token机制:scale token与锚点上下文的巧妙设计

LingBot-Map特殊Token机制&#xff1a;scale token与锚点上下文的巧妙设计 【免费下载链接】lingbot-map A feed-forward 3D foundation model for reconstructing scenes from streaming data 项目地址: https://gitcode.com/GitHub_Trending/li/lingbot-map LingBot-M…

作者头像 李华
网站建设 2026/9/4 15:22:55

AI需求泡沫下的工程判断:如何识别真实需求与伪需求

这两年聊 AI 的人&#xff0c;通常都有一种分裂感&#xff1a;一边是铺天盖地的融资新闻、大模型发布会、各种“AI 颠覆行业”的标题&#xff1b;另一边是自己在公司里想推动一个 AI 项目时&#xff0c;需求评审、数据准备、效果评估、成本核算&#xff0c;每一步都像在翻山越岭…

作者头像 李华