文章

魔改 Chirpy 主题构建适配 Polyglot 插件的 Jekyll 双语博客

如何自己手动修改 Chirpy 主题的模板,构建功能完全的,顺畅的 Polyglot 双语博客。

在 Polyglot 的 Issue 区下,我发现有人问如何在 Chirpy 主题下配置 Polyglot。

Chirpy 现在已经是当之无愧的 Jekyll 技术写作类博客模板一哥。而毕竟 Polyglot 是一款具有独特实现方式的小众插件,想让大多数主题内置对 Polyglot 的支持并不现实。得自己动手折腾起来啦。

基础修改

Chirpy 官方提供了两种部署方式。一种是使用 Starter 仓库模板创建项目,另一种是直接 fork 主题再加以修改。前者的好处是更新方便,后者的好处是能更多地魔改主题。

如果要自定义这个主题,必须选择后一种方式。否则,所有的网页模板文件都会内嵌在主题的 gem 里,无法手动修改。

Polyglot 执行上的原理是在编译整个项目时扫描整个项目,把每种语言生成一遍内容。最后在 _site 目录下,主语言站点在根目录下,而每种非主语言会各存在一个完整网站的目录。

在生成网站时,变量 site.active_lang 会以当前正在生成的语言标识符赋值。Jekyll 采用 Liquid 模板生成 HTML。可以在网页的模板中根据 Liquid 语法引用这些内容,比如如此修改网站标题界面来实现自动切换不同语言的网站标题:

1
2
3
4
<h1 class="site-title">
      <a href="/">交界处的空间站</a>
</h1>
<p class="site-subtitle fst-italic mb-0">各类技能笔记</p>

不同语言创作的文章需要放在对应语言标识符的目录下,但是具有相同的文件名,比如我的这个网站英语是主语言,中文文章都会放在 _post/zh-CN 下。

而 Chirpy 默认一个站点只选用了一种语言,由 _config.yml 下的 lang 参数定义。在渲染过程中,绝大多数语言相关的模板词条采用的都是_data 目录下定义的变量赋值,然后再由 site.data.locales[site.lang].variable_name 引用该变量。

为了能适配 Polyglot,我们需要把所有涉及到 [site.lang] 的内容都改成 [site.active_lang]。建议使用 vscode 之类的文本编辑器的全项目文件搜索替换实现。记得加完整词条匹配设置。

更加具体的使用方法,也可以外加参照 Polyglot 官网对这一特性的介绍

菜单选择器

如果要让读者能在各种界面都自由调换语言,就需要在 _include/sidebar.html 里添加一个语言选择菜单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    <div class="sidebar-bottom d-flex flex-wrap align-items-center w-100">
    <div class="lang-div d-flex flex-wrap w-100" >
    <p style="font-weight: bold;" id="language"> {{site.data.locales[site.active_lang].tabs.languages}}
    </p>
    
    </div>
    <!-- jekyll-polyglot will process ferh= into href= through the static_href liquid block tag without relativizing the url; useful for making language navigation switchers  -->  
    <div class="lang-div d-flex flex-column">
      
    {% for tongue in site.languages %}
      <p class="lang-name-box">
        {% if tongue == site.active_lang %}
          <a class="lang-name" id="current-lang" style="font-weight: bold;">
            {{ site.data.locales[tongue].tabs.lang_name }}
          </a>
        {% else %}

          {% if page.lang-exclusive %}
            {% assign is_lang_exclusive = false %}
            {% for lang in page.lang-exclusive %}
              {% if tongue == lang %}
                {% assign is_lang_exclusive = true %}
                <span>This language is in the lang_ex list</span>
              {% endif %}
            {% endfor %}
            {% if is_lang_exclusive == false %}
              <a class="lang-name disabled-lang">
                {{ site.data.locales[tongue].tabs.lang_name }}
              </a>
            {% else %}
              <a class="lang-name" {% static_href %}href="{% if tongue == site.default_lang %}{{site.baseurl}}{{page.url}}{% else %}{{site.baseurl}}/{{ tongue }}{{page.url}}{% endif %}"{% endstatic_href %}>
                {{ site.data.locales[tongue].tabs.lang_name }}
              </a>
            {% endif %}
          {% else %}
              <a class="lang-name" {% static_href %}href="{% if tongue == site.default_lang %}{{site.baseurl}}{{page.url}}{% else %}{{site.baseurl}}/{{ tongue }}{{page.url}}{% endif %}"{% endstatic_href %}>
                {{ site.data.locales[tongue].tabs.lang_name }}
              </a>
          {% endif %}
        {% endif %}
      </p>  
    {% endfor %}
    </div>
  </div>

这里用了一个比较复杂的写法,判断当前页面有没有对某种语言单独呈现。最后实现的效果就是如果没有对应语言页面,则该按钮不包含超链接,且具有独特的 disabled-lang class,方便处理 CSS。

上一篇文章里忘了讲对应的 CSS 怎么做,需要修改 _sass/addon/common.sass,在 .sidebar-bottom 内增加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 .lang-name-box {
      font-family: 'NotoColorEmojiLimited', -apple-system, BlinkMacSystemFont,
        'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji',
        'Segoe UI Emoji', 'Segoe UI Symbol';
      margin: 0.1rem;
      margin-bottom: 0.15rem;

      .lang-name {
        padding: 0.8rem;
        border: 1px solid transparent;
        border-radius: 25%/75%;
        width: 100%;
        text-align: left;
        font-size: 0.9rem;
        margin-bottom: 0.1rem;
        justify-content: left;
        color: var(--sidebar-active-color);
        // background-color: var(--sidebar-bg);
      }

      .disabled-lang {
        background-color: var(--sidebar-disabled-color);
        color: #ff0000;
        &:hover {
          background-color: #ffe3e5;
        }
      }

      #current-lang {
        background-color: var(--sidebar-hover-bg);
      }
    }

    .lang-div {
      margin-bottom: 0.2rem;
      padding: 0;
    }

    #language {
      font-size: 1.1rem;
      margin-bottom: 0.3rem;
    }

这里为了能在 Windows 平台上也显示出国旗,我特意选择了 NotoColorEmoji 作为语言选项栏字体的首选 font-family

1
2
3
4
5
@font-face {
  font-family: NotoColorEmojiLimited;
  unicode-range: U+1F1E6-1F1FF;
  src: url(https://raw.githack.com/googlefonts/noto-emoji/main/fonts/NotoColorEmoji.ttf);
}

RSS 定制化

Chirpy 的作者自制了主题的 RSS 输出格式。RSS 文件在 assets/feed.xml 里。

我个人比较喜欢全文输出的 RSS。所以对其进行魔改。主要是把 <summary><content> 改一下。这样,RSS 就可以输出全文了。

1
2
3
4
5
6
7
  <summary></summary>
  <content type="html">
  <![CDATA[ 
  {%- include no-linenos.html content=post.content -%}
  	{{- content | html -}}
  ]]>
  </content>

使用 no-linenos.html 模板是为了移除代码块的行数。

SEO 优化

Chirpy 主题采用的 SEO 优化插件是 jekyll-seo-tag。jekyll-seo-tag 默认取 site.titlesite.descriptionsite.lang 为 SEO 内容的 <meta> 标签赋值。

这就和我们之前提到的需要改用 site.active_lang 来选取的特定语言的对应变量有所冲突。

直接修改 seo.html 不能解决这个问题。我做了一个专门适用于 Polyglot 的 jekyll-seo-tag 作为临时解决方案。需要在 jekyll-theme-chirpy.gemspec 中移除或注释掉使用 jekyll-seo-tag 的那一行。同时在 GEMFILE 中引入我的修改版插件。

1
2
3
4
5
6
7
8
9
10
11
12
# jekyll-theme-chirpy.gemspec


  spec.required_ruby_version = "~> 3.1"

  spec.add_runtime_dependency "jekyll", "~> 4.3"
  spec.add_runtime_dependency "jekyll-paginate", "~> 1.1"
  spec.add_runtime_dependency "jekyll-redirect-from", "~> 0.16"
  # spec.add_runtime_dependency "jekyll-seo-tag", "~> 2.8" <- remove it
  spec.add_runtime_dependency "jekyll-archives", "~> 2.2"
  spec.add_runtime_dependency "jekyll-sitemap", "~> 1.4"
  spec.add_runtime_dependency "jekyll-include-cache", "~> 0.2"
1
2
3
4
5
6
7
8
# GEMFILE

group :jekyll_plugins do
    
  gem "jekyll-polyglot"
  gem "jekyll-seo-tag", git:"https://github.com/aturret/jekyll-seo-tag-polyglot.git" # my version of the plugin
    
end

如果是在本机测试,别忘了使用 bundle installbundle update 安装这个新的插件。

此外,Polyglot 也有自带的 SEO 解决方案。建议把两个都加上。即在 head.html 中,在 SEO 的段落之前加入 `

`。

1
2
3
4
5
6
  <meta http-equiv="Content-Language" content="zh-CN">
<link rel="alternate" hreflang="en" href="https://aturret.space//posts/:title/"/>
<link rel="alternate" hreflang="zh-CN" href="https://aturret.space/zh-CN//posts/:title/"/>
<!-- Setup Open Graph image -->

可能引发的 Vercel 部署错误

不知道为什么,采用自定义插件之后,每次 Vercel 自动部署都会在 git fetch 环节报错。似乎是因为其环境配置不支持从 GitHub 仓库获取 Ruby gem 导致的。

不想思考那么多了。在 Vercel 的部署环境变量中添加 VERCEL_FORCE_NO_BUILD_CACHE=1,禁用采用先前环境缓存要求其每次部署都从头编译,即可解决这个问题。这样一来,每次部署时间会从40秒提升到80秒。但对于静态博客来说重要的是省心,这点时间问题不是事。

本文由作者按照 CC BY 4.0 进行授权