Skip to content
Go back

続・HugoでCitationを扱うには?

前段階#

  • 先日、Hugoでwikilink形式のcitationを扱えるようにしたが、wikilink形式は普通のリンクであったほうが嬉しい気がしてきた
    • citekeyなのに脚注に飛ばないのは不親切な気がしてきた
  • Pandoc Reference Listの挙動に合わせるほうが良いかも
    • @citekey著者姓 [年]
    • [@citekey][著者姓 年]
      • citekeyは;で区切って複数並べられる
    • [[@citekey]][著者姓 年]で文献ノートにリンクする
  • 問題は、@citekeyの区切りをどうやって識別するか
    • Pandoc Reference Listもそこまで細かくは対応していないみたい
    • 欧文だと、 ,.!?;()あたりで区切れたり、文字が続くと区切れなかったりする
    • 和文だと、、。・()「」とかで句切れたりする
    • 誤作動を防ぐため、基本的には後にスペースを入れる運用になりそう
  • 表示スタイルは様々あるそうなので、そこまで厳密にやらなくても良いと思う
    • 著者が2人だとandでつなぐとか、3人以上だとet al.で略されるとか
    • First nameをイニシャルにするとか、Last, Fist表記にするとか
  • そもそも、人の名前はどう扱えば良いのだろう?
    • 姓 名の順の文化圏の人はどうするとか
    • last nameがどこまでなのかとか
    • Pandoc Reference Listはどこまで整形してくれるのかとか

作業中#

  • とりあえず、replace-wikilinks.htmlからcitation周りを取り除いて、代わりにreplace-citations.htmlを以下の内容で作った
    • [[@citekey]]形式は明示的に除外している
    • 単純にするため、citekeyは\w+でマッチさせる
    • 文中引用にid="cite-@citekey:N"、参考文献のバックリンクにid="@citekey:N"がついていることを想定している
{{- /* @citekeyを脚注へのリンクに置き換える */ -}}
{{- $content := .Content -}}

{{- /* @citekeyと[@citekey]のみ扱う。[[@citekey]]は通常のリンクとする。 */ -}}
{{- range (findRESubmatch `(\[?\[?)(@\w+(?:;\s*@\w+)*)(\]?\]?)` $content) -}}
    {{- /* 括弧の数が揃っていなければ、書き間違いと思われるので、置き換えない */ -}}
    {{- $left_brackets := index . 1 -}}
    {{- $right_brackets := index . 3 -}}
    {{- if ne (len $left_brackets) (len $right_brackets) -}}
        {{- continue -}}
    {{- end -}}

    {{- /* [[@citekey]]は通常のリンクとして扱うので、ここでは置き換えない */ -}}
    {{- $type := len $left_brackets -}}
    {{- if ge $type 2 -}}
        {{- continue -}}
    {{- end -}}

    {{- /* 括弧の中身を処理する */ -}}
    {{- $matched := index . 0 -}}
    {{- $citekeys := index . 2 -}}
    {{- $result := "" -}}
    {{- range (findRESubmatch `(;?\s*)(@\w+)` $citekeys) -}}
        {{- $spacing := index . 1 -}}
        {{- $citekey := index . 2 -}}

        {{- /* 文献ノートがあれば、それをもとに引用表示リンクを作る */ -}}
        {{- $url := relref page $citekey -}}
        {{- if $url -}}
            {{- /* citekeyを一覧に追加する */ -}}
            {{- $count_k := print "references::" $citekey ".count" -}}
            {{- $count_v := add (or ($.Store.Get $count_k) 0) 1 -}}
            {{- $.Store.Set $count_k $count_v -}}
            {{- $.Store.Add "references::citekeys" (slice $citekey) -}}

            {{- /* 文献ノートからメタ情報を取得する */ -}}
            {{- $page :=  site.GetPage $url -}}
            {{- $authors := "" -}}
            {{- with $page.Params.authors -}}
                {{- /* findRESubmatch => [["First Last", "Last"]] */ -}}
                {{- $authors = index (index (findRESubmatch `.* (.+)` (index . 0) 1) 0) 1 -}}
                {{- if eq (len .) 2 -}}
                    {{- $authors = print $authors " and " (index (index (findRESubmatch `.* (.+)` (index . 1) 1) 0) 1) -}}
                {{- else if gt (len .) 2 -}}
                    {{- $authors = print $authors " et al." -}}
                {{- end -}}
            {{- end -}}

            {{- $year := $page.Params.year -}}
            {{- if eq $type 0 -}}
                {{- $year = print "[" $year "]" -}}
            {{- end -}}

            {{- /* HTMLに変換する */ -}}
            {{- $result = print $result $spacing "<a id=\"cite-" $citekey ":" $count_v "\" href=\"#" $citekey ":" $count_v "\">" (delimit (slice $authors $year) " ") "</a>" -}}
        {{- else -}}
            {{- /* 文献ノートがなければ、そのままにする */ -}}
            {{- $result = print $result (index . 0) -}}
        {{- end -}}
    {{- end -}}

    {{- /* 内容を置き換える */ -}}
    {{- if eq $type 0 -}}
        {{- $content = replace $content $matched $result 1 -}}
    {{- else -}}
        {{- $content = replace $content $matched (print "[" $result "]") 1 -}}
    {{- end -}}
{{- end -}}
{{- $content | safeHTML -}}
  • 合わせてreferences.htmlを修正した
    • 表示内容とid名を微調整して、文献ノートへのリンクを付けた
{{- /* ページ内citekeyリストから参考文献一覧を作る */ -}}
{{- with .Get "references::citekeys" | uniq | sort -}}
    <div class="footnotes" role="doc-endnotes"><hr><h6>References</h6><ul>
    {{- range . -}}
        {{- /* 文献ノートがあれば、そのメタ情報を使って参考文献表示を作る */ -}}
        {{- $citekey := . -}}
        {{- $url := relref page $citekey -}}
        {{- if $url -}}
            {{- $page := site.GetPage $url -}}

            {{- /* 文献ノートからメタ情報を取り出す */ -}}
            {{- $body := slice -}}
            {{- with $page.Params.authors -}}
                {{- $body = $body | append (slice (print (delimit . ", " " and ") ".")) -}}
            {{- end -}}
            {{- with $page.Params.year -}}
                {{- $body = $body | append (slice (print . ".")) -}}
            {{- end -}}
            {{- with $page.Title -}}
                {{- $body = $body | append (slice (print . ".")) -}}
            {{- end -}}

            {{- /* HTMLを作る */ -}}
            <li id="{{.}}"><p>[{{substr $citekey 1}}] {{/**/ -}}
            {{delimit $body " "}}
            {{- range $.Get (print "references::" $citekey ".count") -}}
                {{- /**/}} <a href="#cite-{{$citekey}}:{{add . 1}}" id="{{$citekey}}:{{add . 1}}" class="footnote-backref" role="doc-backlink">↩︎</a>
            {{- end -}}
            {{- /**/}} <a href="{{$url}}">🗅</a></p></li>
        {{- end -}}
    {{- end -}}
    </ul></div>
{{- end -}}
  • post_content.htmlにその他と同じような呼び出しを追加しておしまい
    • 担当範囲が被らないようになっているので、順番はどうでも大丈夫
    • @Example2025[@Example2025]と書くと、Last [2025Last, F. 2025. Example.][Last 2025Last, F. 2025. Example.]となり、最下段に参考文献一覧が追加されるようになる
  • これで置換対象が増えたので、そこまで頻発しないとは思うけど、少し注意が必要になった
    • これまでの通り、エスケープしても置換時に判別できない
    • 生の@を文中に使いたい場合は、同名の文献ノートと被らないようにする
      • 代わりに、全角@を使うとか、コードブロックにするとかで回避する
    • [@citekey]はreference型のリンクと記法を共有するので被らないようにする
      • 被ったときはどちらかをリネームするしかない

おまけ#

  • delimitは最後の区切りを別の文字にすることができる
    • 文章のように、コンマで並べて最後だけandにする、といったことができる

参考文献#