Hugo 標準テンプレートで OGP・TwitterCard・Google Analytics を設定する

静的サイトジェネレーター Hugo には Template と呼ばれる仕組みでサイトのデザイン等の調整を行いますが、Internal Template として内部組み込みのテンプレートがいくつか用意されています。

Internal Template のページに書いてある通りなのですが、若干位置づけが読み取りづらかったので(どういう使い方で、どのレベルまで実現できるのか)、理解の補足用としてメモします。この記事では Google Analytics, OpenGraph (OGP), そして Twitter Card を設定します。

Internal Templates の内容

ドキュメントには以下の通り記載されています(2019/05/07 現在)。

  • _internal/disqus.html
  • _internal/google_news.html
  • _internal/google_analytics.html
  • _internal/google_analytics_async.html
  • _internal/opengraph.html
  • _internal/pagination.html
  • _internal/schema.html
  • _internal/twitter_cards.html

一方、これらを実現しているソースコードは GitHub の hugo/tpl/tplimpl/embedded/templates/ においてあります。

hugo/tpl/tplimpl/embedded/templates at master · gohugoio/hugo

Google Analytics

設定はシンプルで、テンプレートを一行組み込んだあとに(大部分のテーマでは標準で実装済みのようです)、 config.tomlgoogleAnalytics = "UA-XXX" を記載するのみ完了します。設定は baseURLtheme などの並びに書くようにしましょう。つい [params] の下に書きがちですが、動きません。

ここで気になるのが google_analytics.htmlgoogle_analytics_async.html の違いです。asyn (非同期)の方を使っておけば間違いないだろうと想像するものの、一応裏付けとしてチェックしておきます。

--- ga  2019-05-07 12:16:20.000000000 +0900
+++ ga_async    2019-05-07 12:16:04.000000000 +0900
@@ -4,10 +4,7 @@
 <script type="application/javascript">
 {{ template "__ga_js_set_doNotTrack" $ }}
 if (!doNotTrack) {
-       (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
-       (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
-       m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
-       })(window,document,'script','<https://www.google-analytics.com/analytics.js','ga>');
+       window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
        {{- if $pc.UseSessionStorage }}
        if (window.sessionStorage) {
                var GA_SESSION_STORAGE_KEY = 'ga:clientId';
@@ -26,14 +23,6 @@
        ga('send', 'pageview');
 }
 </script>
+<script async src='<https://www.google-analytics.com/analytics.js>'></script>
 {{ end }}
 {{- end -}}
-{{- define "__ga_js_set_doNotTrack" -}}{{/* This is also used in the async version. */}}
-{{- $pc := .Site.Config.Privacy.GoogleAnalytics -}}
-{{- if not $pc.RespectDoNotTrack -}}
-var doNotTrack = false;
-{{- else -}}
-var dnt = (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack);
-var doNotTrack = (dnt == "1" || dnt == "yes");
-{{- end -}}
-{{- end -}}

どう違うの?という質問はフォーラムにもありました(2016 年)。

Implementing Google Analytics in Hugo - support - Hugo Discussion

このページでリンクされているのか次のページ。

Make Google Analytics Asynchronous

ここで、以下の通り Apache or Nginx それぞれに設定しなければならない記載があります。

# Apache:
ModPagespeedEnableFilters make_google_analytics_async

# Nginx:
pagespeed EnableFilters make_google_analytics_async;

Nginx PageSpeed は Google が開発したレスポンス向上のための仕組みのようです。

nginx pagespeed - Google 検索

試してみたいところですが Kubernetes の Ingress (Nginx) を利用しており、この Issue にてリクエストがあがっているものの実現されていないと思われる?(詳細未調査)

Support for Pagespeed · Issue #287 · kubernetes/ingress-nginx

一旦、自分の環境では作業保留しますが、Nginx を自由に弄れる環境ならば試したいところです。GitHub はこちら。

apache/incubator-pagespeed-ngx: Automatic PageSpeed optimization module for Nginx

OGP & Twitter Card の設定

OGP とか Twitter Card はただのリンクがリッチになる、SNS 等でよく見かけるあれです。

これらを実現するための meta タグ設定は Internal Templates で実現できるようです。

自分でイチから書き起こす場合は The Open Graph protocol などを理解して作業しなければならないところですが、標準で準備されているのは有り難い。必要な情報は公式にまとまっているのですが(Internal Templates | Hugo)、Hugo に慣れていない時はこれだけだと戸惑います。おそらく、以下の点を抑えておけば実現できます。

設定方法 1. テーマの編集

大前提として、自身が利用しているサイトテーマ(Theme)によりファイル構造が異なるため、「どのファイルに修正を加える」という表現がこんなんです。<head> を構成しているファイルに対して以下のコードを追加します。

<head>
~省略~
{{ template "_internal/opengraph.html" . }}
{{ template "_internal/twitter_cards.html" . }}
~省略~
</head>

Theme それぞれ条件が異なるものと理解してください。Theme によっては上記コードが含まれているものもありますので、重複設定にならないよう注意しましょう。

設定方法 2. 設定ファイルの編集

続いて config.toml です。内容は公式に記載のとおりです。

[params]
  description = "Text about my cool site"
  images = ["site-feature-image.jpg"]
  title = "My cool site"

[taxonomies]             # この2行は意味がわからなければまだ
  series = "series"      # 利用していないということなので意識せず消してよい

ほか、マニュアルにある content/blog/my-post.toml の例は、各記事で表示する個別の設定を行うものです。基本は以下の 3 項目を意識しておけばよいでしょう。
※動画・音楽などのメディア系のサイトの場合は、audiovideo も重要になってくるはずです。

title = "Post title"
description = "Text about this post"
images = ["post-cover.png"]

記事の要約である description は設定しなくても自動抽出してくれるのですが、日本語だと気持ちよく抽出してくれませんので、現時点は期待しないほうがいいと思います。そもそも要約はどう生成しているのかというと、以下の通りです。

If there is a summary divider present in the article the text up to the divider will be provided as per the manual summary split methodIf there is a summary variable in the article front matter the value of the variable will be provided as per the front matter summary methodThe text at the start of the article will be provided as per the automatic summary split method

Content Summaries | Hugo

  • 各種ブログツールをはじめ定番の <!-- more --> のセパレータで分離して、それを要約として設定してくれる。
  • Front-Matter (各記事の.mdファイル上部に書く description の内容)を要約として設定
  • 自動抽出

自動抽出は 3 番目で、config.tomlsummarylength = 10(例) と書けば字数指定で抽出してくれるのですが、日本語等のマルチバイト文字だと(?)期待通りの動きになりません。原因未調査ですが、素直に 1 or 2 で意図的に設定した方がシンプルなので気にしないことにしました。

なお、この説明の順番を見ると Front-Matter (description) よりも <!-- more --> の方が強そうですが、両方設定した場合は Front-Matter の方が <head> で使われます。この動きの方が自然でいいですね。

(注釈:この記事中では <!-- more --> と記載していますが、正しくは more の両端にはスペースが入らない --more-- です。コードブロックで書いても本記事で反応してしまっているので回避のためスペースを挿入しています。)

config.toml 設定時に気をつけること

  title = "My cool site"

この title だけを見ると既に初期作業で設定していることが多いため、「あるからいいや」と思いがちなのですが、記載する部位が [params] という欄(セクション?ディレクティブ?呼び方の理解が不十分なので、欄とします)に記載しなければなりません。理由は、og:site_name を表示するためのコードが以下のようになっているためです。

{{- with .Site.Params.title }}<meta property="og:site_name" content="{{ . }}" />{{ end }}

したがって、config.toml でのサイトタイトルの設定が複数登場することになります。おそらく以下のようになるでしょう。

title = "My cool site"
[params]
  ~省略~
  title = "My cool site"

ほか、images についても、最初は少々違和感がありました。

# こう設定することとなっているが
images = ["post-cover.png"]

# なぜこうじゃないのだろう?
image = "post-cover.png"

理由は以下の通り、それぞれが配列で渡されることを期待していて、1 つ目の要素をイメージとして設定しているためです。

/* opengraph.html の場合 */
{{ with $.Param "images" }}{{ range first 6 . }}
<meta property="og:image" content="{{ . | absURL }}" />
{{ end }}{{ end }}

/* twitter_card.html の場合 */
{{- with $.Params.images -}}
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="{{ index . 0 | absURL }}"/>
{{ else -}}

ここまで、OpenGraph (OGP)Twitter Cards の区別をしていませんでしたが、この設定で両者カバーできるので気にする必要はありません。

その他参考

toml 書き方で悩んだ時に、仕様はこちら。 toml-lang/toml: Tom’s Obvious, Minimal Language