Polyglot 1.10 - i18n_headers 优化

Jekyll-Polyglot 1.10 现已发布。本次发布对 liquid 插件 i18n_headers 进行了大量优化和改动以提升 SEO 效果,并对并行构建的幂等性做了些许调整。社区贡献者和 Vibe Coding 在本次发布的诸多功能、测试,以及博客撰写中发挥了重要作用。

现版本的 polyglot 需要更新 ruby 至 3.1 及以上版本,可能会破坏您现有的构建系统。

i18n_headers 改进

i18n_headers 插件现已具备更多功能:

  • 其会为每种语言的页面添加 <link rel="canonical" ...>,以确保各站点的索引唯一。
  • 其会添加 <link rel="alternate" hreflang="x-default" ...>。当浏览器未请求匹配语言时,该标签会指向默认站点语言版本。
  • 对于具有自定义永久链接的集合中的页面和文章,该插件会正确地生成 <link rel="alternate" hreflang="...">
  • 如果 site.baseUrl 具有定义值,默认 URL 也会包含该变量。

此外还修复了一个会导致绝对 URL 相对化时意外破坏这些标签的 bug。

vibe-coded 贡献

在本次更新发现、衡量,以及验证 bug 修复和新功能的过程中,使用了某些 vibe coding 工具。vibe coding 是一种全新的软件开发方式,有它的助力,我们创建了可以在多个已构建的多语言站点中针对 jekyll 插件代码进行的高级 ruby 测试。

通过 vibe coding 的方式编写的测试还保持了高测试覆盖率。自动化测试有了保障,我们由此得以自信地添加复杂的新功能和特性。

此外,vibe coding 工具还帮助将这篇博客文章翻译成了多种语言。

社区贡献

一直以来,Jekyll-Polyglot 得到了社区里的人类用户的大力支持。本项目的人类语言文档均由那些希望能看到本插件以自己母语获得文档记录的人类用户贡献。是人类用户们贡献了 bug 修复和文档,本插件因而得以在每次新版本发布时都获得数千次下载。而 AI 辅助编程——无论是经由在下之手还是诸君之手——都将以我们书写和交流的各种语言,塑造我们所使用的这款软件。

ruby 版本更新需求

jekyll-polyglot 构建时依赖的持续安全更新需要将 ruby 升级到 3.1 及以上主版本。这一更新可能会影响使用了 jekyll-polyglot 构建文档的构建系统。不过,现在也是升级到最新 ruby 主版本的绝好时机。如果这些更改导致了 Jekyll 构建出现问题,请及时反馈。

Polyglot 1.9.0 - 教程优化

Jekyll-Polyglot 1.9.0 现已发布。更新内容包括少许依赖项更新,以及助您充分利用多语言网站特性的优化版教程。

社区提供的教程改进

感谢 aturret 帮助维护现有的简体中文 (zh-CN) 网站页面。谢谢!

george-gca 优化了可选的 derive_lang_from_path 配置。其可以更好地从路径推断中识别文档语言。他实用的功能优化 PR 中还包括了测试。此项改进有助于推断缺少 lang 前页内容字段的帖子和页面的语言,这些语言来自文档文件路径的任何部分。

Github 用户 yunseo-kim 提交了一份优化网站地图生成的教程。为适配 SEO 有关规范,一个网站应该只有一个根目录下的 sitemap.xml,其他语言的子站点内不应包含该文件的副本。请务必将 sitemap.xml 添加到 exclude_from_localization 配置中。

Polyglot 1.8.1 - 社区 Bug 修复发布

Jekyll-Polyglot 1.8.1 版本已经发布,其中包含了一些功能改进,并修复了社区发现的 Bug。

社区提供的 Bug 修复

hacketiwack 提供了用于更严格检查设置文档永久链接的代码,防止因空的前置字段导致的下游问题。

Github 用户 blackpill 提交了针对 i18n headers 标签的单字符错误修复,用于渲染默认语言链接的替代链接(href)。

Polyglot 1.8.0 - 社区贡献发布

非常兴奋地宣布 Jekyll-Polyglot 1.8.0 版本发布了,这个版本增加了一些功能改进,并认可了来自社区的文档和贡献!

不同语言的专属永久链接

Polyglot 1.8.0 版本增加了一些新功能,可以为页面设置特定于语言的永久链接,并保留它们与其他相关页面的关联。这个新功能由一位绅士和学者—— antoniovazquezblanco 提供。

网站地图生成和 i18n SEO

这个版本还认可了 jerturowetz 提供的高质量 sitemap.xmlrobots.txt 解决方案。本网站现在通过这些文件更好地展示和捕获了搜索引擎提供的 SEO 力量。可以在这里查看示例网站的文件。

jekyll :polyglot :post_write 钩子

GitHub 用户 obfusk 在几年前贡献了一个微小的 PR

通过多语言 :site, :post_write 钩子,像这样为每个子进程运行:

Jekyll::Hooks.register :site, :post_write do |site|
  ...
end

这个版本增加了一个自定义 :post_write 钩子,它在所有语言处理完成后运行一次(无论是否使用 parallel_localization):

Jekyll::Hooks.register :polyglot, :post_write do |site|
  # do something amazing here!
end

这一特性对于使用了 Jekyll hook 插件的复杂的 Jekyll 静态站点非常有用。

她还为语言子进程崩溃时的额外日志记录提供了修复。感谢这个贡献!

本地化变量和葡萄牙语翻译

george-gca 是一个才华横溢的、很棒的家伙,他为如何最好地从站点数据本地化富文本贡献了一整篇博客文章。他还提供了本站的葡萄牙语翻译

本地化变量

Polyglot 允许您在 Jekyll 站点中为不同语言拥有不同的页面。例如,一个人可以在英语中有一个 about.md 页面,在西班牙语中有另一个 about.md 页面,它们具有完全不同的布局。但是,如果您希望为这两个页面使用相同的布局,您可以使用本地化变量。这是一种在 Jekyll 站点中为不同语言拥有不同数据的方法,但对所有语言使用相同的布局。

下面我将使用一个使用 Polyglot 创建的模板站点 作为示例。

在页面之间共享布局

在这个网站中,他们为每种语言的每个页面都有一个关于页面。其中,英语版本在 _pages/en-us/about.md,而巴西葡萄牙语版本在 _pages/pt-br/about.md 中。在这两个页面中,我们可以看到它们的前置元数据中有相同的键,但有些值不同。这两个文件都指向相同的布局关于页面模板,并且此页面模板使用前置元数据中的值来渲染页面。

例如,英语页面的 subtitle 键的值为 subtitle: <a href='#'>Affiliations</a>. Address. Contacts. Moto. Etc.,而巴西葡萄牙语页面的值为 subtitle: <a href='#'>Afiliações</a>. Endereço. Contatos. Lema. Etc.。要在布局中使用此信息,可以这样使用:

{{ page.subtitle }}

这两个文件中前置元数据下方的内容也是一样的,可以在布局中这样使用:

{{ content }}

Polyglot 会自动使用当前语言的正确值渲染页面。

在页面之间共享布局和本地化数据

对于页面的 subtitle,他们在前置元数据中使用了 key: value 键值对,但有时我们希望在站点的不同部分中使用这些相同的对。例如,如果我们想在 about.md 和另一个页面中使用相同的 subtitle,我们将不得不在这两个页面的前置元数据中重复相同的对。这并不是我们想要的,因为如果我们需要更改 subtitle,我们将不得不在两个地方更改它。这就是本地化数据的用武之地。您可以创建一个文件,例如 _data/:lang/strings.yml,每种语言一个,Polyglot 将这些键带到 site.data[:lang].strings 下。

比如说,在模板站点中有两个文件,_data/en-us/strings.yml_data/pt-br/strings.yml。在第一个文件中,前置元数据内容包括:

latest_posts: latest posts

而在第二个文件中,前置元数据内容包括:

latest_posts: últimas postagens

这样,他们可以在布局中使用 latest_posts 键,如下所示:

{{ site.data[site.active_lang].strings.latest_posts }}

这样一来,latest_posts 变量的值将正确获取到当前语言的 _data/:lang/strings.yml 文件中定义的值。

在前置元数据中定义要使用的变量

现在,如果您想在页面的前置元数据中定义这个变量,这就有点棘手了。一个可能的解决方案是检查变量的值中是否有 .,如果有,就使用文件 _data/:lang/strings.yml 中的值。你可以这么进行操作:

{% if frontmatter_var contains '.' %}
  {% assign first_part = frontmatter_var | split: '.' | first %}
  {% assign last_part = frontmatter_var | split: '.' | last %}
  {% capture result %}{{ site.data[site.active_lang].strings[first_part][last_part] }}{% endcapture %}
{% endif %}

{{ result }}

如果 frontmatter_var = blog.title ,这段代码就会生效。 This will work, for example, if frontmatter_var = blog.title.

现在,如果您需要在使用它之前检查本地化字符串(该案例情况下是 blog.title)是否实际存在于文件 _data/:lang/strings.yml 中,您将不得不创建一个插件来检查变量是否存在于文件 _data/:lang/strings.yml 中,如果存在,则使用它,否则回退到任何您想要的值。我不会详细介绍如何做到这一点,但我会向您展示如何使用它。您可以在这里参阅该插件的代码。

{% if frontmatter_var contains '.' %}
  {% capture contains_localization %}{% localization_exists {{ frontmatter_var }} %}{% endcapture %}
  {% if contains_localization == 'true' %}
    {% assign first_part = frontmatter_var | split: '.' | first %}
    {% assign last_part = frontmatter_var | split: '.' | last %}
    {% capture result %}{{ site.data[site.active_lang].strings[first_part][last_part] }}{% endcapture %}
  {% else %}
    {% capture result %}fallback value{% endcapture %}
  {% endif %}
{% endif %}

{{ result }}