HTML Tidy 本地化实战指南:基于 poconvert.rb 的 PO/POT 翻译与多语言集成全流程

发布时间:2026/10/16 9:09:35
HTML Tidy 本地化实战指南:基于 poconvert.rb 的 PO/POT 翻译与多语言集成全流程
【免费下载链接】foo_openlyricsAn open-source lyric display panel for foobar2000项目地址https://gitcode.com/gh_mirrors/fo/foo_openlyrics点击查看免费下载HTML Tidy 与libtidy在全球被广泛使用但对非英语用户并不友好。为此 Tidy 项目为所有输出字符串建立了以language_en.h为唯一源头、以 gettext 风格 PO/POT 文件为可选翻译工作流的本地化体系。本文以 Tidy 官方本地化文档为主体结合当前仓库中3rdparty/tidy-html5-5.8.0的完整源码系统讲解 Tidy 本地化的设计原理、poconvert.rb全部命令用法、如何把一门新语言编译进 Tidy以及如何通过-lang与 locale 环境变量进行验证——读完你即可独立完成翻译 → 生成语言头文件 → 注册语言 → 构建测试的完整链路。一、本地化设计理念为什么 Tidy 不直接用 gettextHTML Tidy 的本地化围绕一个核心文件展开3rdparty/tidy-html5-5.8.0/src/language_en.h。官方文档明确说明没有这个文件 Tidy 根本无法工作因此所有语言本地化工作都从这一个文件起源。这里有一个看似矛盾的设计Tidy 使用 gettext 兼容的 PO/POT 文件与工具但Tidy 本身并不使用 gettext。原因在于 Tidy 的构建哲学是在任何地方、用任何工具链构建build it anywhere and build it with anything而gettext并非在所有平台上都可用因此 Tidy 不能依赖它。取而代之的是所有翻译在构建期直接编译进库以及单体内构建的命令行可执行文件运行时不需要 MO 文件PO 经msgfmt生成的二进制参与任何通用操作系统上都能直接运行若对体积敏感可用-DSUPPORT_LOCALIZATIONSNO构建开关轻松去掉额外语言支持language.c与tidylib.c中所有本地化代码均以#if SUPPORT_LOCALIZATIONS包裹见 language.c 与 tidylib.c。1.1 H 文件真正的受控源码语言本地化使用与language_en.h结构完全一致的 C 头文件只是字符串不同。每个语言头文件包含语言专属的复数形式函数whichPluralForm_ll(uint n)一个静态languageDefinition结构内含以TIDY_LANGUAGE开头的全部字符串条目并以TIDY_MESSAGE_TYPE_LAST结尾参见 language.h 的枚举定义。1.2 PO/POT 文件仅供翻译者使用的便捷层为了照顾不习惯直接编辑 C 文件的翻译者Tidy 源码附带 Ruby 脚本poconvert.rb提供可选的 gettext PO/POT 工作流。请务必注意PO/POT 文件只是为翻译者提供的便利Tidy 的语言头文件才是受控的真源码翻译好的 PO 必须转换回 H 文件格式才能被 Tidy 使用。二、仓库中的本地化文件体系当前仓库3rdparty/tidy-html5-5.8.0中本地化相关的文件布局如下类别路径说明官方本地化指南README/LOCALIZE.md总入口指向 localize 目录完整操作手册localize/README.md本地化哲学、全部命令与最佳实践转换工具localize/poconvert.rbRuby 编写的 POT/PO/H 互转工具1276 行依赖清单localize/Gemfilethor、i18n、git三个 gem头文件模板localize/language_ll_cc.h.erb生成语言头文件的 ERB 模板翻译样例localize/translations/tidy.pot模板 7 个语言的 PO 文件语言主文件src/language_*.hlanguage_en.h及 7 个已安装语言头文件当前已随仓库提供的语言包括en内置基准、en_gb、es、es_mx、pt_br、zh_cn、fr、de对应的 PO 文件位于localize/translations/H 文件位于src/。2.1 对翻译者的三点关键差异熟悉 gettext 的译者需要注意 Tidy PO 文件的三个特殊约定msgctxt字段存放字符串标识符Tidy 不使用msgctxt指向源码行号而是存放字符串的标识符如TidyAccessibilityCheckLevel。因为 PO 格式不允许任意元数据这是生成 H 文件所必需的。需要深入源码时用msgctxt值做符号或字符串搜索即可定位上下文见 translations/tidy.pot。msgid字段是可丢弃的Tidy 的语言工具不使用msgid值它仅为翻译者便利而存在。正因如此Tidy 的工具完全支持从非英语语言出发进行翻译。不要用 gettext 官方工具处理 Tidy 的 PO/POT 文件除非为其他项目复用字符串所有工作流都应使用poconvert.rb完成。三、翻译工作流查找或创建翻译文件3.1 目标语言已存在若想翻译的语言已存在于localize/translations/例如language_zh_cn.po直接用你喜欢的 PO 编辑器打开开工即可。虽然普通文本编辑器也能编辑但官方强烈建议使用专用 PO 编辑器以免文件格式被破坏导致转换工具无法解析。3.2 目标语言不存在如果目标语言尚未翻译有两条路径路径 A手工创建复制模板tidy.pot为language_ll.po基础语言或language_ll_cc.po区域变体其中ll为两位语言代码、cc为两位区域代码相应修改 PO 文件头部的Language:、Plural-Forms:等信息。路径 B用 poconvert.rb 生成./poconvert.rb msginit --locale ll其中ll是目标语言代码。该工具识别的区域与 gettext 的msginit相同若指定语言不被识别请改用上面手工创建的方法。更多选项可运行./poconvert.rb help查看。命名规范PO 文件一律命名为language_ll.po或language_ll_cc.poTidy不使用gettext 从 PO 生成的 MO 文件。3.3 提交 Pull Request翻译完成后即可向 Tidy 提交 PR针对master分支提交 PO 文件时官方开发者会将其转换为 H 文件格式若你直接修改了 H 文件则会直接合并。也欢迎自行完成全部转换、将新语言完整加入 Tidy 后整体提交 PR。推荐的 Git 操作顺序Fork 仓库到个人账户建议创建主题分支名称简短且能说明目的如localize Portuguese按逻辑节点分次提交修改推送至个人账户创建 Pull Request关注评论与合并结果。仓库约定提交 PO 文件时只能提交msgid为英文的 PO——gettext 约定msgid仅使用英文否则其他译者可能看不懂原字符串。poconvert.rb支持生成以其他语言为msgid的 PO便于从非英语翻译或从基础语言翻译区域变体如从es翻译es_mx这类文件仅供本地工作提交前需转换回英文msgid的 PO具体命令见下文提交 PR 前的准备。四、把新语言编译进 Tidy添加语言到源码官方并不要求贡献者必须走完这一流程但自行完成可以立即测试翻译效果也为开发者团队节省步骤。完整流程如下4.1 生成并放置头文件# 将 PO 转换为 Tidy 语言头文件Windows 上需加 ruby 前缀 ./poconvert.rb msgfmt path_to_your_file.po工具会在当前工作目录生成language_ll_cc.h将该文件复制到src/目录。4.2 修改 language.c 注册语言编辑 src/language.c在#include区约第 13-21 行位于#if SUPPORT_LOCALIZATIONS内加入新头文件找到约第 40 行开始的static tidyLanguagesType tidyLanguages结构定位注释These additional languages are installed.按相同格式把新语言追加到数组中数组末尾必须是NULL且该结构预设了 9 个语言槽位见 language.c。4.3 构建并测试按常规方式构建 Tidy然后用-lang选项验证输出。从源码可看到该结构体初始时.currentLanguage与.fallbackLanguage均指向language_en而语言查找通过tidyTestLanguage()逐个比对每个语言头文件首条TIDY_LANGUAGE记录的代码字符串见 language.c。五、最佳实践继承机制与翻译约束5.1 语言继承Language InheritanceTidy 会按指定语言 → 基础语言 → 默认英语逐级回退。例如把libtidy设为es_mx若不存在则自动改用es若es也不存在则继续使用当前语言。该逻辑在tidySetLanguage()中实现同时探测ll_cc与ll两个代码前者作为 currentLanguage、后者作为 fallbackLanguage见 language.c。注意方向性es_mx缺失可回退es但请求es时不会反向去选es_XX变体。5.2 字符串继承String Inheritance除语言级回退外单个字符串也会回退。tidyLocalizedStringN()的查找顺序为当前语言 → fallback 语言 → 内建en→ 内建en单数形式见 language.c。例如libtidy设为es_mx某字符串在es_mx找不到就查es再找不到就用英文原文。5.3 基础语言优先区域变体只写差异正是因为有继承机制官方希望先完成基础语言——这样区域变体只需翻译与基础语言不同的字符串从而保持 Tidy 与libtidy体积精简。如果你在翻译区域变体如us_CA请只翻译与基础语言确实不同的字符串。poconvert.rb的--baselang参数正是为此设计见下节。5.4 位置参数Positional ParametersTidy当前不支持位置参数如%1$s且鉴于 Tidy 大部分输出的特性预期未来也不需要。翻译时请保证替换值的出现顺序与原字符串一致。六、测试本地化输出6.1 命令行选项使用-lang或-language选项并指定 POSIX 或 Windows 语言名称。该选项必须放在命令行的最前面因为控制台程序按先入先出first-in, first-out顺序解析并执行选项。从源码可见其处理逻辑-lang help会打印语言帮助列表tidySetLanguage()返回失败时输出语言未找到提示并回显当前语言见 console/tidy.c。示例与英文帮助文档中一致tidy -lang es -lang help6.2 修改 shell locale在 Unix/Mac 与 Linux 上可临时修改 shell 的 locale 来测试把en_GB换成目标语言即可export LANGen_GB export LC_ALLen_GB对应地libtidy初始化时会依次尝试LC_MESSAGES与LANG环境变量来设置语言见 tidylib.c。6.3 东亚语言东亚语言如中文、日文、韩文在 Linux、macOS、Windows 上均已完整支持并测试通过。但 Windows 上需要把操作系统而非控制台区域设置为东亚语言环境才能在 Windows Console 与 PowerShell 中正常显示PowerShell ISE 则始终支持东亚语言无需修改系统区域。七、poconvert.rb 命令完全参考poconvert.rb是 Tidy 源码自带的 Ruby 批处理脚本1276 行用于生成 POT、PO、H 文件并在三者之间互转设计风格模仿 gettext 工具族并额外支持以不同源语言工作。使用前先安装 Ruby 与依赖# 依赖为 Gemfile 中列出的包thor、i18n、git [sudo] gem install xxx # xxx 逐个替换 # 或安装了 Bundler 时直接 bundle install两个重要特性必须牢记依赖当前路径脚本依赖其所在位置若移动脚本必须同步修改脚本内的default_en指向src/language_en.h与header_template指向language_ll_cc.h.erb两个变量见 poconvert.rb所有输出写入当前工作目录防止意外覆盖重要文件。各子命令详解如下Windows 下请在命令前加ruby。7.1 创建新 POTxgettext./poconvert.rb xgettext在当前目录生成一份全新的tidy.pot源码虽已自带tidy.pot但可自行再生成。7.2 以非英语 msgid 创建 POT./poconvert.rb xgettext language_cc_ll.h其中language_cc_ll.h是已存在的 Tidy 语言头文件路径。生成的tidy.pot以该语言的译文作为msgid缺失处用英文兜底。这对区域变体翻译如es→es_mx很有价值。7.3 从 H 转换出 POmsgunfmt./poconvert.rb msgunfmt language_cc_ll.h从 Tidy H 文件生成全新 PO 文件。7.4 以不同 msgid 语言转换 H 为 PO./poconvert.rb msgunfmt language_cc_ll.h --baselangother-language_cc_ll.h生成 PO 时未翻译字符串取自另一个语言而不强制用英文。7.5 为特定区域创建空白 POmsginit./poconvert.rb msginit ./poconvert.rb msginit --localeLOCALE第一条命令尝试猜测当前区域第二条使用指定区域ll或ll_CC格式。Tidy 只认识 gettext 所知的区域若--locale不被识别需要手工创建 PO 并自行修改区域设置。从源码可见其内部维护了一份完整的 locale 表含语言名与复数公式如中文ja/vi/ko为nplurals1; plural0;见 poconvert.rb。若要使用其他 Tidy 语言的msgid字符串创建空白 PO./poconvert.rb msginit language_cc_ll.h [--localeLOCALE]7.6 生成 Tidy 语言头文件msgfmt./poconvert.rb msgfmt language_cc_ll.po生成language_cc_ll.h语言代码取自 PO 头部的Language:字段。注意msgfmt输出的 H 文件以 UTF-8 BOM 开头以满足 MSVC 编译要求见 poconvert.rb。区域变体瘦身生成区域变体时强烈建议排除父语言已有的字符串以减小库与可执行文件体积。例如es已有字符串 archivoes_mx就没必要再包含它./poconvert.rb msgfmt language_cc_ll.po --baselangother-language_cc_ll.h7.7 提交 PR 前的准备非英文 PO 转回英文仓库只接受msgid为英文的 PO。两条命令即可完成转换./poconvert.rb msgfmt language_cc_ll.po ./poconvert.rb msgunfmt language_cc_ll.h第一条把非标准 PO 转成 Tidy 语言头文件第二条从刚生成的头文件再产出msgid为英文的全新 PO。7.8 PO 与新版 POT 同步当 Tidy 的 POT 变化新增字符串、新注释等时最简单的更新方式是把 PO 先转为头文件会按最新 Tidy 标准规范化再转回新 PO./poconvert.rb msgfmt language_cc_ll.po ./poconvert.rb msgunfmt language_cc_ll.h此外poconvert.rb还提供rebase [--shaHASH]命令在language_en.h变更后一次性重新生成全新 POT、为src/下每种语言重新生成 PO再为每个新 PO 重新生成语言头文件并借助--sha指定的提交 SHA-1 比对旧版language_en.h将发生变化的条目在 PO 中标记为 fuzzy见 poconvert.rb。所有生成文件均输出到工作目录不会覆盖源文件。八、本项目中的应用背景本仓库 foo_openlyricsfoobar2000 的开源歌词显示面板将 HTML Tidy 作为第三方库引入用于清洗抓取到的网页 HTML在 src/sources/lyric_source.cpp 中创建TidyDoc、设置TidyXhtmlOut与TidyForceOutput经tidyParseString→tidyCleanAndRepair→tidyRunDiagnostics→tidySaveBuffer完成解析与修复。Tidy 的本地化机制与该集成方式正交多语言字符串随库整体编译libtidy按环境 locale 或显式语言设置输出对应语言的诊断信息本项目对应的静态库构建工程位于 openlyrics_build/tidy-static.vcxproj其中包含对src/language.c与src/language_en.h的编译引用。九、小结HTML Tidy 的本地化体系可以概括为一句话以language_en.h为唯一事实源H 文件是编译期真正生效的语言载体PO/POT 只是翻译者友好的中间格式。掌握poconvert.rb的xgettext/msginit/msgunfmt/msgfmt四类子命令配合语言继承与字符串继承的规则你既可以作为翻译者提交 PO 贡献也可以作为集成者把新语言完整注册进language.c并构建验证——这正是 Tidy build it anywhere 哲学在多语言输出上的落地方式。赞分享【免费下载链接】foo_openlyricsAn open-source lyric display panel for foobar2000项目地址https://gitcode.com/gh_mirrors/fo/foo_openlyrics点击查看免费下载相关推荐HTML Tidy 多语言本地化实战指南poconvert.rb 与 gettext 工作流详解以 foo_openlyrics 集成的 tidy-html5-5.8.0 为例HTML Tidy 多语言本地化实战指南poconvert.rb 与 gettext 工作流详解以 foo_openlyrics 集成的 tidy htmlComprehensive Rust 多语言翻译工作流实战指南基于 Gettext 的 .po 文件本地化体系Comprehensive Rust 多语言翻译工作流实战指南基于 Gettext 的 .po 文件本地化体系 Comprehensive Rust 是 Go文档教程FlyByWire A380X 本地化实战指南基于 Localazy 的 locPak 多语言翻译工作流FlyByWire A380X 本地化实战指南基于 Localazy 的 locPak 多语言翻译工作流 本指南以 fbw a380x/src/localiz游戏开发上一篇如何快速搭建Sunshine游戏串流从零到客厅大屏的完整上手指南下一篇网盘直链解析怎么做免费脚本帮你彻底告别下载限速创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考