For AI agents: the complete documentation index is available at https://docs.halo.run/llms.txt, the full documentation bundle is available at https://docs.halo.run/llms-full.txt, and this page is available as Markdown at https://docs.halo.run/developer-guide/theme/i18n.md.

国际化

主题可以在根目录的 i18n 中提供国际化消息。建议始终提供 default.properties 作为兜底,再按需要添加语言和地区文件。

i18n
default.properties
en.properties
zh.properties
zh_CN.properties
zh_TW.properties

Halo 会依次合并 default.properties、语言文件和语言地区文件,后读取的更具体文件会覆盖前面的同名键。例如访问者的语言为 zh-CN 时,读取顺序为 default.propertieszh.propertieszh_CN.properties

定义消息

i18n/default.properties
pagination.previous=Previous
pagination.next=Next
site.poweredBy=Powered by {0}
i18n/zh_CN.properties
pagination.previous=上一页
pagination.next=下一页
site.poweredBy=由 {0} 提供支持

消息键建议按页面或组件分组,保持各语言文件使用相同的键集合。

在模板中使用

使用 #{...} 读取消息,并将当前语言写入 HTML:

<html th:lang="${#locale.toLanguageTag}">
  <body>
    <a th:text="#{pagination.previous}">Previous</a>
    <p th:text="#{site.poweredBy(${site.title})}">Powered by Halo</p>
  </body>
</html>

如果某条消息确实是可选项,可以使用 #messages.msgOrNull 提供显式兜底:

<span th:text="${#messages.msgOrNull('notice.optional') ?: 'Notice'}"></span>

普通必需消息不应静默兜底。缺少键时,页面会显示类似 ??pagination.previous_zh_CN?? 的标记,便于在测试时发现遗漏。

页面级消息

如果某个模板需要覆盖全局消息,也可以在模板旁提供同名的 properties 文件:

templates
index.html
index.properties
index_zh_CN.properties

页面级文件中的同名键优先于根目录 i18n 中的消息。多个页面共用的文案仍应放在 i18n,只把页面专属或需要覆盖的消息放在模板旁。

在 JavaScript 中使用

使用 Thymeleaf JavaScript 内联,让消息值按 JavaScript 字面量规则转义:

<script th:inline="javascript">
  const messages = {
    previous: /*[[#{pagination.previous}]]*/ 'Previous',
    next: /*[[#{pagination.next}]]*/ 'Next',
  };
</script>

不要把消息直接拼接进带引号的 JavaScript 字符串,否则翻译中的引号、换行等字符可能破坏脚本。

切换和测试语言

Halo 依次从 language 查询参数、language Cookie、已登录用户的语言偏好和浏览器 Accept-Language 请求头解析当前语言。开发时可以直接访问以下地址检查指定语言:

http://localhost:8090/?language=zh-CN

至少检查默认语言、每个已提供的地区文件,以及不存在对应地区文件时能否正确回退到语言文件或 default.properties