Post

ブログのデザインを一新して英語版記事をサポートした

久しぶりにブログを大幅にアップデートした。具体的にどのような変更を行ったかを残しておく。

ブログのデザインを一新して英語版記事をサポートした

はじめに

表題のとおり、このブログを大幅にアップデートした。以前の状態を知らない人のために、また自分の記録のためにも、具体的にどのような変更を行ったかを記事として残しておくことにする。

GitHub Actions × ChatGPT による英語版記事の自動翻訳機能の実装

もともと技術記事を英語で書いて DEV Community に投稿するという構想はだいぶ昔からあった。それと同じことをこのブログにおいても実現しようというのがざっくりとした要約だ。

実を言うとブログを英語対応するというのは直近まで考えていなかった。技術記事と違ってあまり多くの人に見てもらおうとかアピールしたいという気持ちがなくて、読みたい人だけが読みに来てくれればいいというくらいの軽い温度感で続けるつもりだったからだ。それに、技術記事であれば英語で書くことに意味があるかもしれないが、個人のブログを英語で書いたところで需要もないだろうし、共有するのはせいぜい SNS くらいだから相手はほとんど日本人のみとなる。誰も見ていないのにがんばって英語で書くなんてどう考えても労力に見合わないし別にやりたいことでもなかった。

しかし、昨今 AI の進化は目覚ましく、テキストベースの簡単な処理であればたいていのことは AI がやってくれるようになった。その中に英訳も含まれている。だから、ChatGPT にお願いすれば日本語で書いたブログ記事もほとんど労力をかけずに英訳してくれるのではないかと思った。

また、SNS 上ではほとんど海外の人との関わりはないのだが、長年オンライン英会話を続けてきたこともあって、仲の良い先生が何人かいる。もはや先生というより友達という感覚に近い。その先生たちに自分の思っていることや出来事を共有する手段として、このブログが使えるのではないかと思った。そして先日、その先生たちに「もしぼくのブログが英語で読めたら興味ある?」と聞いてみたら興味あると言ってくれたので、それなら英語版の記事も公開する価値があるかもしれないと思った。

そして実際に GitHub Actions と OpenAI (ChatGPT API) を使って英訳をしてもらったところ、いい感じに自動化できたのでこの機能をこのブログに実装することにした。コードは以下のとおり。

ChatGPT Feedback は、もともと日記で感情記録をする際にその感情に対するフィードバックを ChatGPT にしてもらうというのを自動化するために作った。しかし日記に自分が書いた文章ではないものが保存されるのはなんかちょっと微妙だなと思ったのと、結果が返ってくるまでに少し時間がかかるのであまりフィードバックを見る機会がなく形骸化してしまっているなと思ったので廃止した。それ以降はしばらく使っていなかったのだが、今回ブログに英訳機能を実装する(ChatGPT を使用する)にあたり、これを多少いじれば使えるのではないかなと思い改良した。まあもともとプロンプトを GitHub Actions 経由で受け取ってただ ChatGPT に投げて結果を返すだけの実装だったので改良はさほどしていないが。

そして、自分が日本語でブログ記事を書いて投稿したら、その記事の中身を、英訳してほしいというプロンプトとともに ChatGPT に丸投げして、返ってきた結果をファイルに書き込んで英語用の記事フォルダに保存する、というワークフローを作った。やっていることは実にシンプルで、その実装自体も ChatGPT に聞きながら行った。

少し技術的な話になるが、GitHub にプッシュされた日本語記事ファイルが複数あった場合にそれぞれに対して英訳をするループ処理が必要だが、YAML にはループをする仕組みがないのでこれは ChatGPT Feedback 側で複数のファイルに対してループ処理をする必要があるのかなと最初思っていたし途中までその仕様で実装を進めていた。でも、ChatGPT Feedback はあくまでプロンプトに対して結果を返すだけのシンプルな設計にとどめておきたかった。今後オンライン英会話の内容を要約して日記の issue に記録する用途でも使いたいと思っているので、プッシュされたファイルに対して動作する専用の実装をこの中に含めたくなかった。

なにかいい方法はないかと調べていたら、GitHub Actions の matrix strategy が使えるのではないかということに気づいた。これは本来は、というか公式の想定としては複数の環境(たとえば Node.js v20 と Node.js v22 だったり、Windows や macOS だったり)でビルドしたり、動作が問題ないか検証したりするために使われるらしいが、やっていることは単なる並列処理に過ぎないので、これを使えばワークフローだけで実質的にループ処理ができるのではないかと思った。実際それで試してみたらうまくいったのでこの設計を採用した。これは技術記事として公開する価値があるかもな。

少し話は脱線したが、ともあれこれでほとんど労力をかけずに英語の記事を公開することができるようになったので、英語を母語とする友達(オンライン英会話で知り合った先生たち)にも自分のブログを共有することができるようになった。多くの人に見てもらえるような大層な記事を書くつもりはないのだが、見てくれる知り合いが多ければ、ブログを定期的に更新するモチベーションも維持できるというもの。つい最近まで自分のブログに英語版なんかいらないだろと思っていたが、いざ実装して今後これを先生たちに共有することを想像したらワクワクし始めてきたので、やってみるものだなと思った。

余談だが、技術記事のほうに関しては英語版の記事を執筆しようと前から思っていたのにまだそっちは着手できていなくて、直近まで英語版を公開するつもりがまったくなかったブログのほうで先にそれが実装されてしまったのはなんとも皮肉だ。しかし今までの経験からも、人生では往々にしてこういうことが起こる。いつも計画通りにいくとは限らないから、少なくとも自分がやりたいことは、多少行き当たりばったりでもとにかくやってみるということが肝心なのかもしれないなと思った。まあ仕組み自体はすでに出来上がったから、あとはこれを技術記事のほうにも転用すればそれ自体はすぐにできるのだが、まさかそのモチベーションが技術記事のほうではなくブログになるとは思わなんだ。

テーマ(デザイン)を変更

さて、せっかく英語版の記事が自動翻訳されたとしても、そのままでは日本語の記事と英語の記事が区別なくまとめて表示されてしまう。しかもパス(URL の階層構造)を分けておらず、日本語版と英語版の同じ記事は同じ slug(ファイル名)を持ってしまっているので、英語版の記事にアクセスしようとすると日本語の記事に飛ばされてしまっていた。

なので国際化対応をする必要がある。日本語の記事一覧と英語の記事一覧を別々で表示して、それぞれの言語で表示を分けられるようにする必要があった。それを実現するためのプラグインが Jekyll にはいくつかあって、今回はそのうちの Polyglot というのを採用することにした。国際化対応については詳しくは後述するのだが、このプラグインを導入する過程で、Yunseo Kim’s Study Notes という、このプラグインを採用した人のブログに行き着いた。そしてこのブログのデザインがとても美しくてすげえなあと感心していたら、どうやらこれは Chirpy Jekyll Theme というオープンソースのテーマを使っているということがわかった。あまりにもこのテーマのデザインが魅力的に感じたので、国際化対応そっちのけで先にテーマの変更を行うことにした。そして今(2025 年 6 月 29 日現在)適用しているのが、そのテーマである。

たしかにおしゃれなデザインでそこに関してはとても気に入っているのだが、設計の部分に関してはいろいろと不満がある。端的に言えばブログの設定がこのテーマにかなり依存してしまっている。

たとえばマークダウンパーサには kramdown を使うことが前提となっている。kramdown ではなく CommonMark を使用した場合、いくつかの機能に不具合が出る。

  • スクロールによる ToC の追従やリンクが機能しない
  • コードブロックのレイアウトが崩れる

せっかくマークダウンパーサに CommonMark を使えるようにしたというのに、パーサがテーマに依存してしまっているのは非常にがっかりした。kramdown を避けた背景としては GitHub Pages で Jekyll を使うときは GitHub Actions を使ったほうがいいかもというお話 でまとめたように、パーサの挙動がおかしいからだ。簡単にまとめると以下のような問題点がある。

  1. ヘッドライン(見出し)とテーブルの間に空行を入れないとテーブルが正しくパースされない
  2. <details> タグ内でマークダウンが使えない(<details> タグの前後に空行を入れても改善せず)
  3. [Example Domainexample.com](https://example.com/) のように、リンクの中に縦線 | が入っているとテーブルとして解釈されてしまうバグがある
  4. https://example.com のような裸の URL を書いてもリンクにならない

GitHub Actions を使わなくても GitHub Pages ではデフォルトで Jekyll が使える。しかしその場合は kramdown しか使えない。これを避けたくて GitHub Actions を使うように変更して、それを簡略化するためにわざわざ Jekyll Build Pages まで作ったというのに、テーマのせいで kramdown を使わざるを得ないのは誠に遺憾である。

せっかくこのテーマを使っているのだから、このテーマの機能やデザインは最大限利用したい。そうなると kramdown でパースされることを我慢しなければならない。百歩譲って 3 は [Example Domain \| example.com](https://example.com/) のように | の前に \ を入れることで回避できるし、4 も [Example Domain](https://example.com/) のように書けばいいので許容できるが、2 に関してはもうどうしようもないのでこの書き方自体を控えるしかない。パーサのせいでマークダウンの書き方までもが制限されてしまうのはストレスなのでさすがに許容しがたい。1 に関しても個人的には空行を常に入れて書かないといけないのはめんどくさいし気持ち悪いと感じてしまう。

それに、導入するのも少し苦労した。なぜなら公式が提供しているセットアップ方法が、テンプレートリポジトリから新規リポジトリを作成するか、テーマのリポジトリをクローンして使うかしか提供されていない からだ。今回新しくブログを新設する方法しかドキュメントには記載されておらず、すでにあるブログに適用する方法については明記されていない。

Jekyll Remote Theme を使いたかったのだが実装が Jekyll の思想から大きく外れているせいかうまくビルドできなかったし、Chirpy Jekyll Theme をインストールして直接使うにしても最初は CSS が一切適用されなかったりして一悶着あった。テンプレートリポジトリをクローンしてきて動作検証して何が問題なのかを調べたり不具合が発生している部分を修正していくのに時間がかかった。もともと Minima という初期テーマに少しだけ改良を加えていたのも原因だったのだが、初めてブログをこのテーマで新設する場合についてしかサポートされていないのは、ユーザフレンドリーではないなと思った。

とりあえず今のところはこれで満足しているが、CommonMark が使えないあたりからも最終的にはやっぱり自分でテーマを作って使うことになりそうだなと思った。利用させてもらっていて文句を言うのは筋違いかもしれないが、設定を制限してユーザの選択肢を狭める思想は個人的に好きじゃないので。このテーマを見た当初は、「このテーマが使えるならもはやプラットフォームも静的サイトジェネレータもプラグインもテーマも、全部他の人や組織が作った素晴らしいものに乗っかったほうが、自分で作る手間が省けていいのではないか」と思ったりもしたけど、自分の満足のいくサイトを作り上げるには、やっぱりある程度自分で作ったほうがいいのかもなと最終的には思い直した。まあそれでも GitHub や Jekyll、後述する Polyglot など、既存の素晴らしいプロジェクトを十分に利用させてもらっているのだが。

国際化対応

最後に国際化対応をした。これは前項のデザイン変更で少しだけ言及したが、ただ英語の記事を置いただけでは、ブログで見たときに日本語記事に混じって表示されてしまったり、パスが間違っていてその記事にアクセスできなかったりする。それを解消するために国際化対応のプラグインを導入する必要がある。

代表的、なのかはわからないが、パッと出てくるものでスターが多いのは Jekyll Multiple Languages Plugin である。しかしこのプラグインは現時点で 2 年前を最終更新として開発がストップしてしまっている。他にも類似のプラグインは存在する がそのほとんどが現在ではメンテナンスされておらず、スターの数も少ない。

そんな中、唯一今でも更新されているのが Polyglot だった。スターの数的には Jekyll Multiple Languages Plugin の半分程度(現時点)ではあるが、採用されている事例もあるようだし README を見た感じだと設計もシンプルな印象を受けたので今回はこれを採用することにした。ちなみに Yunseo Kim’s Study Notes を見つけたのも Polyglot の README に書かれていた からである。

懸念点としては How To Use It に記載されているように

1
2
3
_posts/2010-03-01-salad-recipes-en.md
_posts/2010-03-01-salad-recipes-sv.md
_posts/2010-03-01-salad-recipes-fr.md

のようにファイル名の最後(拡張子を除く)に言語コード(日本語なら ja、英語なら en、スウェーデン語なら sv、フランス語なら fr といった各言語を 2 文字で表したもの)を付与しないといけないことだった。これもさっきの Chirpy と同じで、プラグインのためにファイル名、しかも記事のファイル名を変えなければならないのは許容できなかった。

しかしその懸念は実際にローカルで動作検証したら杞憂だった。これはあくまで言語を識別する手段の一つに過ぎず、これ以外にも以下の方法を用いて言語を識別できるようにすればファイル名を変更する必要はないことに気づいた。

  1. 各記事ファイルの YAML front matter に lang というキーと言語コードを付与する
  2. _posts/ja_posts/en のように言語ごとにディレクトリを分け _config.ymllang_from_path: true を有効にする

今回は 2 番目のディレクトリを分ける方法で言語を分ける方法を採用した。というより、仮に他の方法(たとえば 1 のやり方)でしか言語を分けられなかったとしても、リポジトリ内のディレクトリ構造を見やすくするために _posts/ja_posts/en のように分けるつもりでいた。だから 2 番目の方法を採用すれば今までの全記事ファイルを編集する必要もなく、一石二鳥だった。

パスは、日本語の記事は今までと変わらず /blog/<slug> で、英語は /blog/en/<slug> となる2。英語版にアクセスした場合はそのページ内のリンクもすべて /blog/en/ から始まるのがありがたい。最初は baseurl: "/blog/" となっていたせいでうまく機能していなかったのだが、トレイリングスラッシュを削除して baseurl: "/blog" とすることで正しく機能するようになった。なんで /blog/ と書いていたんだったかな。

言語切替のプルダウン表示

ただしテーマ自体が国際化対応されているわけではなく、今回採用した Chirpy も例外ではないので、そのままだと英語の記事にアクセスしたい場合には手動でアドレスバーに en を追加しなければならない。それだと 404 Not Found のページに行ってしまったときなどに日本語のパスに戻ってしまったりするのでページの中に言語を選択するメニューを表示させたかった。そのほうが、検索エンジンからやってきた人に対してもユーザフレンドリーでもあるし。

そしてこの実装に関しても先ほどからたびたび出てきている Yunseo Kim’s Study Notes さんのブログを参考にした。lang-selector.html というのでサイドメニューに言語選択のプルダウンを表示できるようになっていたので、こちらを借りることにした。

自動翻訳であることを示す注釈表示

さらに、英語版は完全に ChatGPT に丸投げして自分では一切書くつもりがない3のだが、いくら AI が進化して自然な文章を生成できるようになったとはいえ、自動翻訳なので、細かいニュアンスや文化的な言葉、専門用語などが間違っている可能性がある。実際、この記事を書く前に 2 記事だけ英語版を生成してもらった(それ以外の過去記事は今後対応予定)のだが、その中で使われている SNS という単語が、そのまま表示されてしまっていた。SNS (Social Networking Service) というのは和製英語で、英語では Social Media という。実際この記事でも先ほど SNS という言葉を使用しているが、ここも正しく翻訳されない可能性がある。

なので、英語版の記事に関しては、誤解を避けるために、日本語から翻訳されたから記載が正しくない可能性があるという旨を注意書きとして明記する必要があると思った。オンライン英会話の仲の良い先生たちだけに見せるなら事前に伝えておくこともできるだろうが、一応世界中に公開されているということは検索エンジンからアクセスする人もいる可能性もゼロではないわけで、そういう人たちに誤解を与えてしまうのは不本意である。

当初は ChatGPT のプロンプトに、以下の文章を本文の先頭に付与するよう指示することで対応しようとしていた。

This article was automatically translated from Japanese to English by ChatGPT. Please note that some nuances or information may not be accurately reflected.

しかし通常の本文と一緒にこれが毎回表示されているのはなんか微妙だなと思ったし、ChatGPT も完璧ではないのであまりプロンプトの指示が多すぎると意図したとおりに書いてくれない可能性もある。だから、ここは ChatGPT ではなくシステム的に、しかも注意書きであることがはっきりとわかる形式・デザインで表記したほうがいいなと思った。

そのやり方についても ChatGPT に聞いたら簡単にわかったので対応した。Chirpy の post.html をコピーして、数行を追加するだけ で済んだ。警告のような表記は、すでに Chirpy 側で用意されていたのでそれを利用した。

これで国際化対応においてもある程度満足がいく形でデプロイすることができた。

今後やりたいこと

もう先生たちに「英語版できたよー!」って紹介しちゃってもいいのだが、先に共有しちゃうにしてもまだ少しやり残していることがあるので直近でそれを対応したい。

過去記事の自動翻訳対応

新しい記事に関しては、日本語記事のプッシュ(公開)をトリガーにして自動で ChatGPT が英訳する処理が自動化されているので問題ないが、過去記事に関してはまだ英語版が存在しない。とはいえ _posts/ja 内に日本語の記事が入るとワークフローが実行されているようになっていて、過去記事はまだ _posts 直下にあるのでこれを _posts/ja に移動するだけで良い。ただ、一気にまとめて実行すると API 制限がかかったり、意図しない挙動になったりする可能性があるので、数ファイルずつ様子を見ながらちょこちょこ対応していくことにする。

コメントシステムの導入

以前から Disqus というコメントシステムを利用していた。今後も引き続き Disqus を利用するつもりでいたのだが、今回の刷新の際に giscus という他のコメントシステムがあることを知った。厳密に言えば、今までもこのコメントシステムを採用しているブログを見たことはあったのだが、これが giscus というものであることを知ったのは今回の刷新がきっかけだ。

Disqus で特に不満があるというわけではなかったが、giscus を見た瞬間、あっという間に魅了されてしまったのでこの刷新を機にコメントシステムも Disqus から giscus へ移行することにした。移行といっても過去のコメントを引き継ぐわけではない4ので乗り換えるといったほうが適切かもしれない。

giscus に魅了された理由は、なんといっても GitHub のエコシステムに乗っかっていることである。ブログを Ghost から Jekyll に移行した でも書いたとおり、できるだけ他のプラットフォームに依存したくないという気持ちがある。静的サイトは技術的にコメント機能をビルトインすることができないので、外部ツールに頼ることは必然である。しかし giscus ならコメントも GitHub で管理できるようになるので GitHub のエコシステムにどっぷり浸かっている自分にはお誂え向きである。

そのほか、utterancesGitalk という類似のコメントシステムも見つかったが、最も活発で人気の高い giscus を採用することにした。ちなみに giscus は utterances にインスパイアされて開発されたらしい。両者の大きな違いは GitHub Issues を使用する(utterances)か GitHub Discussions を利用する(giscus)かである。

この設計思想は当初は個人的にはどちらでもいいと思っていたのだが、GitHub Discussions の場合はカテゴリごとに内容を分けることができ、実際 giscus でもカテゴリを選択する項目がある。これを利用すれば、このブログだけでなく技術記事や日記など複数のサイトに対して、一つのリポジトリでコメントを一元管理できるというメリットがある。そして実際、自分もこの方法でコメントを一つのリポジトリにまとめて管理しようと考えている。

導入自体はそんなに難しくなさそうなので、昨日今日で導入しようと予定していたのだが、先述したとおり、一つのリポジトリで一元管理するなら、新しいリポジトリを作る必要がある。そのリポジトリ名を何にしようかと考えていてなかなか決まらず、結局まだ導入できていない。最近まともに仕事をしていなかったが、そろそろ進捗を出さないといけないのだが、ブログに関してやりたいことがまだたくさんあるし記事ネタもたくさんあるので、先にこの記事を書いてしまうことにした。

コメントシステムを導入したらまたそれをお知らせする記事を書こうと思う。

サムネイル表示の修正

現状だとサムネイルが表示されていない過去記事が多数存在する。

しかしこれは画像がリンク切れになっているのではなく、最初から存在していないのだ。ではなぜ存在しない画像(サムネイル)を読み取ろうとしてしまっているのかといえば、image: '' という空の画像を読み取る設定が YAML front matter の中に含まれてしまっているからだ。過去に使っていた Minima というテーマでは、そもそもサムネイルを表示するデザインにはなっていなかったのでこれで問題なかったが、Chirpy ではサムネイルが表示されるので、このような表記になっていると空のサムネイルを読み取ろうとしてしまう。

おそらくこれは Ghost から移行した際にサムネイルが設定されていない記事をエクスポートしたときに入ってしまったのではないかな。エラーになるというわけではないが単純に見た目がよろしくないので修正する。実際にはサムネイルが設定されていないものなので image: '' という記述を削除するだけでいいのだが、サムネイルとして設定できそうな画像が記事内にあった場合にはそれをサムネイルとして新たに設定してもいいかもしれない。そのへんは作業するときに臨機応変に対応する。

過去記事の見出し(ヘッドライン)を h3 以下に修正

Chirpy では ToC を表示する機能があるが、なぜか <h1> タグだけは認識しない。またこれも、テーマの仕様なのかバグなのかに記事の書き方を合わせないといけないのは微妙なのだが、それを抜きにしても、日記に自分の書いたブログの本文を自動転記する際に <h1><h2> を使ってしまうと他の階層と被ってしまう(あるいは階層構造がおかしくなってしまう)ことがあるので、ブログ記事内で使用するヘッドラインを <h3> 以下に統一したいと思った。

日本語のタグを英語に変更

現状だと日本語と英語のタグが混在してしまっているのでこれを修正したい。

まとめ

長いこと、特に Jekyll に移行してからはブログ更新も滞りがちだった。しかし最近になってまたブログを更新したいという熱意がわいてきて、管理されていなかったブログを手入れして理想的なかたちになっていくのを見るのはとても嬉しい。自己満足でしかないのだが、誰の役に立つのかわからないものや記事を作ったり執筆したりして達成感を感じるのも、人生を豊かにする手段の一つだと信じている。結果的にそれが世の中の誰か一人にでも役に立てば幸いである。だから自己満足でもいい。お金を稼げるものだけが価値のすべてではないのだ。

  1. 当初予定していた使い方とだいぶ変わってきたので、このリポジトリ名は今後変更するかもしれない。 ↩︎

  2. まあこれは将来的に変更する可能性があるかもしれないが現時点ではこのようになっている。 ↩︎

  3. ブログを更新するのに毎回複数の言語で書くのはさすがに大変すぎるしそんなことをしていたらブログを書く気が失せてしまうので ↩︎

  4. 尤も、ブログを Ghost から Jekyll に移行した に移行した際に URL が変わったことで過去のコメントが引き継げなくなってしまったのでその段階ですでにコメントは途切れている。そして Jekyll に移行したあとしばらくは Disqus も導入しておらず、Jekyll に Disqus を導入したこと自体がわりと最近のことであった。そのため Jekyll でビルドされたブログにおいてはコメントがゼロの状態なので引き継ぐものもないのが現状である。 ↩︎

This post is licensed under CC BY 4.0 by the author.