☰
PhotoPrism 前端多语言本地化完全指南:gettext 工作流、翻译文件与构建流程
2026/9/30 6:49:05 网站建设 项目流程
  • 后端
  • 前端
  • 图像处理
  • 人工智能
  • AI 应用

【免费下载链接】photoprism

AI-Powered Photos App 🌈💎✨

项目地址:https://gitcode.com/gh_mirrors/ph/photoprism
点击查看免费下载

本篇技术指南以 PhotoPrism 仓库中 frontend/src/locales/README.md 为核心,系统讲解 PhotoPrism 如何基于 gettext 标准完成前端(及后端)的多语言本地化:从*.po/*.pot翻译文件的结构与命名规范,到用 Poedit 创建、更新翻译的操作步骤,再到make gettext-extract、npm run gettext-compile等构建命令的底层实现与源码证据。读完本文,你将掌握为 PhotoPrism 添加一门新语言、维护现有翻译、在开发环境中编译并验证多语言界面的完整实战流程。

PhotoPrism 的本地化架构:前端与后端共用同一套 gettext 标准

PhotoPrism 使用 gettext 作为前后端统一的本地化标准,它是翻译用户界面时被广泛采用的标准之一(见 frontend/src/locales/README.md)。这一设计带来的核心约定是:

  • 以人类可读的英文消息作为翻译 ID:例如File not found本身就是msgid,翻译系统依靠它查找对应的译文;
  • 同一字符串在没有翻译时自动作为默认文案回退:因此即使某个语言尚未翻译完成,界面也不会出现空白或乱码;
  • 支持占位符插值:消息中可包含%{n}之类的占位符,用于数字等动态变量,例如%{n} files found。

从源码实现看,这一约定在前端运行时被 frontend/src/common/gettext.js 具体兑现:

function interpolate(message, params = {}) { // ... return text.replace(/%\{(\w+)\}/g, (_, key) => { // 用 params 中的值替换 %{key} 占位符 }); }

这段代码负责把%{n} files found中的%{n}替换为实际数字。需要说明的是,占位符命名语法在前后端存在差异:前端*.vue/*.js源码中的动态消息使用%{n}(花括号风格),而后端 Go 代码(镜像自 pkg/i18n/messages.go)的占位符则是 Go 的 printf 动词风格,如%s、%d。frontend/src/common/gettext.js中的Tp()函数专门桥接了这两种风格——它先用英文源消息 id 查译文,再通过interpolatePositional()按顺序把有序参数数组逐一填入%s、%d等位置占位符,从而让后端推送的通知也能以当前 UI 语言渲染。为此 frontend/src/locales.js 中专门定义了BackendMessages(),把后端可能出现的通知消息(如"Something went wrong, try again"、"%d files uploaded in %d s")注册到前端目录中供提取翻译。

翻译文件布局:locale 命名、.po/.pot/.mo/.json的分工

仓库中所有前端翻译文件都集中在 frontend/src/locales 目录,每个语言对应一个*.po文件,以 locale 名称 命名。从该目录的实际内容可以看到当前维护的 46 种语言,例如:

  • de.po—— 德语
  • pt_BR.po—— 巴西葡萄牙语(注意下划线写法,与葡萄牙语pt.po区分)
  • zh.po—— 简体中文、zh_TW.po—— 繁体中文
  • he.po、ar.po、fa.po、ku.po—— 希伯来语、阿拉伯语、波斯语、库尔德语(均为从右向左书写的 RTL 语言)

这些语言在 frontend/src/locales.js 的Options数组中统一注册,包含显示名称(如简体中文)、locale 值(如zh),RTL 语言还会带rtl: true标记,前端据此自动切换排版方向。

各类文件的职责划分如下:

文件作用说明
translations.pot模板文件(Portable Object Template)由源码自动提取生成的"待翻译字符串清单",是所有语言翻译的基准;见 frontend/src/locales/translations.pot
*.po各语言的翻译文件(Portable Object)每条消息含msgid(源字符串)与msgstr(译文),可用 Poedit 打开编辑
*.mo编译后的机器对象文件(Machine Object)随*.po自动生成,供程序高效读取,文本编辑器无法直接阅读
json/前端运行时加载的编译结果每个 locale 一个 JSON 文件,由gettext-compile生成,前端可直接 import

打开 frontend/src/locales/en.po 可以看到典型条目结构:

#: src/locales.js:272 msgid "{0} appended action" msgstr ""

而 frontend/src/locales/zh.po 中对应条目则是:

#: src/locales.js:272 msgid "{0} appended action" msgstr "{0}附加行动"

其中#:开头的注释行记录了该字符串在源码中的引用位置(如src/page/photos.vue:529),便于译者定位上下文。注意en.po与translations.pot的差异:en.po带有完整的文件头元信息(Project-Id-Version、Last-Translator、Plural-Forms等),而translations.pot仅保留最基本的头信息,因为它是模板而非某个具体语言的翻译。

用 Poedit 创建与更新翻译:从打开 POT 到保存 PO

PhotoPrism 官方强烈推荐使用 Poedit 创建和更新翻译,它在 Mac、Windows、Linux 上均可免费下载使用,其源码托管在 GitHub 上的 vslavik/poedit 项目。*.po文件可以用 Poedit 打开、编辑并保存,以更新现有翻译。

添加一门全新语言的完整流程

  1. 用 Poedit 打开 frontend/src/locales/translations.pot;
  2. 点击窗口底部的"Create New Translation"(创建新翻译);
  3. 在弹出的对话框中选择目标语言,即可开始逐条翻译;
  4. 翻译完成后,以 locale 名称作为文件名保存为*.po文件,例如德语保存为de.po、巴西葡萄牙语保存为pt_BR.po,并放入frontend/src/locales/目录;
  5. 在 frontend/src/locales.js 的Options数组中登记新语言,否则该语言不会出现在前端语言选择器中。参照现有条目添加即可,RTL 语言记得加上rtl: true。

更新已有翻译

当源码新增了待翻译字符串后,在 Poedit 菜单栏执行"Catalogue" > "Update from POT File..."(目录 > 从 POT 文件更新),选择新的translations.pot,Poedit 就会把新增的msgid合并进当前语言的*.po,已有译文保持不变,只翻译新增条目即可。

Git 提交时的文件取舍

保存*.po时,Poedit 会自动在旁生成对应的二进制*.mo文件。.mo无法在文本编辑器中阅读,但必须随.po一起包含在 git 提交中,或在你通过邮件发送翻译时一并附上。相反,编译生成的*.json文件不需要提交(frontend/src/locales/json/目录在 PR 中应保持缺席)——因为它经常引发合并冲突,且可由gettext-compile随时重新生成。

开发环境验证:编译 JSON、构建与实时重载

如果你已经搭好可用的开发环境,可以通过以下命令在本地完整走一遍"翻译 → 编译 → 预览"链路。

第一步:把 PO 编译成前端可用的 JSON

在frontend目录下运行:

npm run gettext-compile

该命令由 frontend/package.json 定义,实际执行vue-gettext-compile --config gettext.config.js,会把frontend/src/locales/下现有的全部*.po翻译编译成可由前端 import 的*.json文件。注意命令中设置了GETTEXT_MERGE=1,对应配置见 frontend/gettext.config.js 的逻辑:当GETTEXT_MERGE非 0 或 false 时,vue3-gettext 会通过 msgmerge 把msgstr条目合并进编译结果。

编译配置的其余关键点同样集中在 frontend/gettext.config.js:

  • 输入范围:include默认覆盖src/**/*.{vue,js,ts},并排除src/common/gettext.js(避免把运行时插值函数误当作翻译源);
  • 输出位置:potPath为translations.pot,jsonPath为json,且splitJson: true、flat: true,即每个语言生成一个扁平结构的 JSON 文件;
  • 语言清单:locales直接通过 glob 扫描src/locales/*.po动态生成(见 frontend/gettext.config.js),所以新增语言只需放入.po文件即可被自动识别。

第二步:构建前端或启动 watch 模式

编译完成后,运行:

npm run build

或者让下面这条命令在后台保持运行,每当源码或翻译文件发生变化时自动重新编译 JS 和 CSS:

npm run watch

watch脚本对应 frontend/package.json 中的vite build --watch,由 Vite 驱动增量重建。

第三步:在 Web UI 中验证

确保photoprism服务正在运行,然后在受支持的浏览器中打开 Web UI。进入Settings(设置)切换语言后,界面会自动触发一次重载,新语言即刻生效。语言切换的具体实现位于 frontend/src/locales.js 的Locale()函数:它从配置中读取当前语言 locale 与 RTL 状态,把Messages(T)编译出的消息对象按 locale 打包返回给 vue3-gettext 运行时。

提取新字符串:从源码扫描到 POT 更新的完整链路

当你在*.js或*.vue源码中新增了界面文案(通过$gettext(...)等调用包裹),需要重新提取这些待翻译字符串并更新 POT 模板。在仓库根目录运行:

make gettext-extract

该目标定义于 Makefile,实际调用./scripts/gettext-extract.sh。深入阅读 scripts/gettext-extract.sh 可以看到完整执行流程:

  1. 首先确定扫描目录列表:始终包含frontend/src,并自动检测可用的私有前端 overlay——plus/frontend、pro/frontend、portal/frontend目录存在时也会加入扫描,这正是 README 中"自动扫描社区版源码及私有前端 overlay"的底层实现;另外可通过GETTEXT_EXTRA_SRC环境变量追加额外的源码目录;
  2. 在frontend目录内以SRC=... GETTEXT_MERGE=0 npm run gettext-extract执行提取(GETTEXT_MERGE=0表示提取 POT 时跳过 msgmerge 自动回填);
  3. 用sed把 overlay 目录的相对引用(如../plus/frontend)统一替换为src,保证translations.pot中的源码引用位置在不同构建环境与私有 overlay 下保持稳定;
  4. 最后调用 scripts/gettext-merge.sh,用msgmerge --previous --no-fuzzy-matching --update把新模板合并回各个*.po,同时也会合并后端的assets/locales目录。

如果你只希望扫描 Community Edition(社区版)源码、不包含任何私有 overlay,可以仅运行:

cd frontend && npm run gettext-extract

这对应 frontend/package.json 中不带SRC环境变量的提取命令,此时 frontend/gettext.config.js 会把扫描目录回退为默认的src。

翻译维护的最佳实践小结

  • 翻译 ID 即默认文案:请保证msgid是准确、人类可读的英文句子,因为它在任何未翻译的语言中会直接显示给用户;
  • 占位符不可翻译:%{n}、%s、%d等占位符必须原样保留在译文中,否则运行时插值会失败。前端使用%{name}花括号风格、后端通知使用 Go printf 风格(%s/%d),两者分别由frontend/src/common/gettext.js的interpolate与interpolatePositional处理;
  • 提交.po与.mo,跳过json/:避免 JSON 编译产物进入 PR 引起合并冲突;
  • 提取、更新、编译三步走:源码改动后执行make gettext-extract更新 POT 与各语言文件,在 Poedit 中用 "Update from POT File..." 完成翻译,最后用npm run gettext-compile编译验证;
  • 新增语言要登记:除创建*.po外,务必在 frontend/src/locales.js 的Options中注册,RTL 语言加rtl: true;
  • 后端消息共用同一目录:后端通知类消息通过 frontend/src/locales.js 的BackendMessages()注册进前端目录,配合Tp()实现"后端发消息、前端按当前语言翻译"的体验。

通过以上流程,任何贡献者都可以为 PhotoPrism 添加一门新语言或在数分钟内更新现有翻译,并借助npm run watch在本地即时预览效果。

  • 后端
  • 前端
  • 图像处理
  • 人工智能
  • AI 应用

【免费下载链接】photoprism

AI-Powered Photos App 🌈💎✨

项目地址:https://gitcode.com/gh_mirrors/ph/photoprism
点击查看免费下载
上一篇:从实验记录到模型上线:MLflow 实验跟踪与部署实战
下一篇:3步搞定!让《星际争霸》《红警2》等经典游戏在Windows 10/11重获联机生命

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询