日本語と英語を混ぜられるようにbibtexスタイルファイルを改造しよう

全国の卒論や論文に追われる同志の皆様，こんにちは！N.Mです．今回自分も修論の関係で，参考文献をbibtexで管理，出力する必要があり，スタイルの微調整に追われることになりました（修論のルールや日本語文献に合わせる等）．
[Bibtexで参考文献を書くときに日本語と英語混ぜるのどうしてる？](https://qiita.com/ossyaritoori/items/b4b1afc451a76e3bc63e)

自分も学部卒論を書いていたときは，この方法でbblファイルをいじって微調整していましたが，Cloud LaTeXなどのようにオンラインのTeXシステムを使用する場合，bblファイルが出力されないため，この方法が使えません．かといって，bibitemで一つ一つ書いていく方法に変更するのは面倒ですし，変更の際にミスが発生してしまう可能性も高いです．なので，bstファイル，bibtexのスタイルファイルを改造しようという話になりました．

# 前提
まずは，自分が希望するスタイル，並び順に近いスタイルを見つけてきましょう．特に，並び順のソートもbstファイルで実装しているスタイルも存在しています．このソーティングをbstファイルの文法で一から書くのは大変なので，特に並び順を優先してスタイルを探しましょう．今回の自分の修論の場合，第1著者のファミリーネームのアルファベット順にしているようだったので，そのように並び替えられる[ieeetranS.bst](http://tug.ctan.org/tex-archive/macros/latex/contrib/IEEEtran/bibtex/IEEEtranS.bst)を用いました．この後の説明もこれを用いることにします．

# 最低限必要な基本文法
※この文法は，bstファイルをいじったり，少し調べてある程度理解した文法なので，間違っているところも多少あるかもしれませんが，この知識だけでも改造することはできました．より詳細な文法は[ここ](https://qiita.com/HexagramNM/items/7c59f307e55010caf693)を読んでください．

### ・代入
    値 '変数 :=

    (例)
    #1 'japanese.flag :=

`japanese.flag`は独自変数です．代入する変数の前に`'`を入れるようです．また値の前にも`#`を入れます．見ていただいてわかるように，Pascalの文法を逆ポーランド記法（最後に演算子を置く）にしています．

### ・比較
    変数or値 変数or値 =

    (例)
    japanese.flag #1 =
これで，`japanese.flag`が1なら真を，違うなら偽を返すようになります．Pascalと同じように，代入には`:=`を，比較には`=`を使います．

### ・if文
    条件文
      { 真のときの処理 }
      { 偽のときの処理 }
    if$

    (例)
    #1 japanese.flag =
      { #0 'japanese.flag := }
      { skip$ }
    if$

例の場合，`japanese.flag`が1なら，その`japanese.flag`を0にし，そうでないなら，何も処理しないという分岐処理になります．処理のところに`skip$`と書くことで，何も処理せず通過します．

# さっそく改造してみよう

今回は表題のように，日本語文献の場合にスタイルを切り替えるということをやっていきますが，具体的には

- フルネーム表記に変更（デフォルトはファーストネームは頭文字で省略される）
- カンマやピリオドを日本語に合わせて全角にする．
- 英語タイトルだと`"title,"`となるところを，日本語に合わせて`"タイトル"，`にする．
- 著者名で，英語だと最後の一部分で`著者 and 著者`というようにandがついてしまう部分を，日本語に合わせて全部カンマでつなぐようにする．

ということをやっていきます．ではieeetranS.bstの上から順に改造していきましょう．

### フルネーム表記に変更

`FUNCTION {default.name.format.string}`の部分を以下のように変更してください．ここの部分の詳細は[
.bstファイルをいじって著者名表記を変える](http://may46onez.hatenablog.com/entry/2018/02/01/140136)に詳しい解説があるので，詳細は省きますが，`f.`だとファミリーネームが頭文字だけになり，`ff`だとファミリーネームもフルに表記されるようになります．

    FUNCTION {default.name.format.string}{ "{f.~}{vv~}{ll}{, jj}" }
    ↓
    FUNCTION {default.name.format.string}{ "{ff~}{vv~}{ll}{, jj}" }

（19/12/20追記）
英語文献の場合はこれで良いのですが，日本語文献の場合，"{ff~}{vv~}{ll}{, jj}"を"{ff~ }{vv~}{ll}{, jj}" （ff~のあとに半角スペースを入れる）にしないと，著者が6人以上出てきたときに一部苗字と名前の間に半角スペースが現れないようです（詳細の原因は不明です）．この切替えについては，「日本語文献の複数著者の場合に出てきてしまうandの抑制」のところで後述します．

### 準備：エントリの追加
エントリというのは，bibtexでauthor（著者）とかtitleとか指定するところです．さすがに，その文献が日本語かどうかを自動で判別するのは厳しいので，新たにisjapaneseというエントリを用意しておき，もしなにかisjapaneseにデータが入っていれば日本語の文献としてスタイルを整えるというようにします．ieeetranS.bst内に`ENTRY`から始まる

    ENTRY
    { address
      assignee
      articleno
      author
      booktitle
      chapter
      day
      ...

みたいに続く部分があるので，ここに`isjapanese`を追加します．

    ENTRY
    { 
      isjapanese
      address
      assignee
      articleno
      author
      booktitle
      chapter
      day
      ...

これで使用しているbibファイルで

    @misc{Label,
      author = {著者},
      year = {2019},
      title = {タイトル},
      isjapanese = {true}
    }
のようにisjapaneseのエントリを指定できるようになりました．

（19/02/07追記）
日本語文献が1つや2つなら大丈夫ですが，多くなってくるとisjapaneseのエントリを追加するのも面倒になってしまいます．この記事を書くきっかけとなった記事の投稿者の方が，この記事を見ていただいたようで，さらに2バイト文字が含まれているかどうかで，このisjapaneseを自動で追加するPythonプログラムを書いてくださいましたので，紹介させていただきます．
[bibファイルの日本語文献（2バイト文字）を判定してフラグを建てるPythonプログラム](https://qiita.com/ossyaritoori/items/efec1978b8a03c8cb737)

### 準備：独自変数の定義

日本語の文献かどうかを管理するフラグ変数をあらかじめ定義しておきます．ieeetranS.bstに`INTEGERS`から始まる

    INTEGERS { prev.status.punct this.status.punct punct.std
               punct.no punct.comma punct.period 
               prev.status.space this.status.space space.std
               space.no space.normal space.large
               prev.status.quote this.status.quote quote.std
               quote.no quote.close
               prev.status.nline this.status.nline nline.std
               nline.no nline.newblock 
               status.cap cap.std
               cap.no cap.yes
             }

というような部分があるので，ここに独自変数`japanese.flag`を追加します．

    INTEGERS { prev.status.punct this.status.punct punct.std
               punct.no punct.comma punct.period 
               prev.status.space this.status.space space.std
               space.no space.normal space.large
               prev.status.quote this.status.quote quote.std
               quote.no quote.close
               prev.status.nline this.status.nline nline.std
               nline.no nline.newblock 
               status.cap cap.std
               cap.no cap.yes
               japanese.flag
             }

これで，ieeetranS.bst内で独自変数`japanese.flag`が使えるようになりました．

### 準備：独自変数の初期化
上で`japanese.flag`を定義しましたが，値を0に初期化する必要があります．ieeetranS.bstに初期化の関数，`FUNCTION {initialize.status.constants}`があると思いますので，以下のようにjapanese.flagの初期化も追加します．ちなみに，bstファイルの関数は以下のように，`FUNCTION {関数名}`というように表記します．

    FUNCTION {initialize.status.constants}
    { #0 'punct.no :=
      #1 'punct.comma :=
      #2 'punct.period :=
      #0 'space.no := 
      #1 'space.normal :=
      #2 'space.large :=
      #0 'quote.no :=
      #1 'quote.close :=
      #0 'cap.no :=
      #1 'cap.yes :=
      #0 'nline.no :=
      #1 'nline.newblock :=
      #0 'japanese.flag :=
    }

### 日本語文献のカンマやピリオドの全角化，"の後にカンマが来るように変更
さてここからが本題です．まず，どこでカンマやピリオドの処理をしているかについては，","や", "（ダブルクォーテーション含む）で検索をかけると良いでしょう．","や", "が出てくるところが一番怪しいです．今回のieeetranS.bstでは`FUNCTION {output.nonnull}`, `FUNCTION {fin.entry}`のところで，処理をしていました．`", " *`と出てくるところを`japanese.flag`が0かそうでないかで分岐して，0でない（日本語文献）の場合に`"，" *`というように全角カンマに書き換えれば良いです．以下に自分が修正したieeetranS.bstを示します．（`"文字列" *`がその文字列をくっつけるという処理をしているようです．）

・`FUNCTION {output.nonnull}`

    FUNCTION {output.nonnull}
    { swap$
      %%追加箇所1：日本語文献なら，"のあとにカンマ
      japanese.flag #1 =
        {
          prev.status.quote quote.close =
            { "''" * }
            { skip$ }
          if$
        }
        { skip$ }
      if$
      %%追加箇所1ここまで
      %%変更箇所2：日本語文献なら全角カンマに
      japanese.flag #0 =
        { 
          prev.status.punct punct.comma =
            { "," * }
            {skip$}
          if$
        }
        {
          prev.status.punct punct.comma =
            { "，" * }
            {skip$}
          if$
        }
      if$ 
      %%変更箇所2ここまで
      %%変更箇所3：日本語の場合は全角ピリオド
      prev.status.punct punct.period =
        { 
          japanese.flag #0 =
            { add.period$ }
            { "．" * }
          if$
        }
        { skip$ }
      if$ 
      %%変更箇所3ここまで
      %%変更箇所4：英語の場合は"の前にカンマ
      japanese.flag #0 =
        {
          prev.status.quote quote.close =
            { "''" * }
            { skip$ }
          if$
        }
        { skip$ }
      if$
      %%変更箇所4ここまで
      %%変更箇所5：日本語の場合の謎スペースの抑制（全角カンマの後ろの空白で十分）
      prev.status.space space.normal =
        { 
          japanese.flag #0 =
            { " " * }
            { skip$ }
          if$
        }
        { skip$ }
      if$
      %%変更箇所5ここまで
      prev.status.space space.large =
        { large.space * }
        { skip$ }
      if$
      write$
      prev.status.nline nline.newblock =
        { newline$ "\newblock " write$ }
        { skip$ }
      if$
    }

・`FUNCTION {fin.entry}`

    FUNCTION {fin.entry}
    { this.status.punct punct.no =
        { skip$ }
        { 
          %%変更箇所：日本語文献の場合，全角ピリオドに
          japanese.flag #0 =
            { add.period$ }
            { "．" * }
          if$
        }
      if$
      this.status.quote quote.close =
        { "''" * }
        { skip$ }
      if$
      write$
      newline$
    }

### 日本語文献の複数著者の場合に出てきてしまうandの抑制
英語の場合，複数人著者がいる場合には最後の著者の前にandをつけますが，日本語の場合はandをつけません．この著者名等を扱う場所の見つけ方ですが，まず，authorsの形式を変更するので，`FUNCTION{format.authors}`のところを見ます．そして，その関数の中をみると`FUNCTION{format.names}`という関数を内部で使っていることがわかるので，その関数を見に行くと，該当する場所を見つけられます．この`FUNCTION{format.names}`の上に`FUNCTION{change.name.format.string}`を追加し，`FUNCTION{format.names}`を以下のように変更します．

（19/12/20追記）
change.name.format.string関数を追加したのは，前述のように名前のフォーマット用文字列を英語文献の場合と日本語文献の場合とで切り替えられるようにするためです．

    FUNCTION{change.name.format.string}
    { 
      japanese.flag #0 =
      {
        "{ff~}{vv~}{ll}{, jj}" 'name.format.string :=
      }
      {
        "{ff~ }{vv~}{ll}{, jj}" 'name.format.string :=
      }
      if$ 
    }

    FUNCTION {format.names}
    { 'bibinfo :=
      duplicate$ empty$ 'skip$ {
      this.to.prev.status
      this.status.std
      's :=
      "" 't :=
      #1 'nameptr :=
      s num.names$ 'numnames :=
      numnames 'namesleft :=
      change.name.format.string
        { namesleft #0 > }
        { s nameptr
          name.format.string
          format.name$
          bibinfo bibinfo.check
          't :=
          nameptr #1 >
            { nameptr num.names.shown.with.forced.et.al #1 + =
              numnames max.num.names.before.forced.et.al >
              is.forced.et.al and and
                { 
                  "others" 't :=
                  #1 'namesleft :=
                }
                { skip$ }
              if$
              namesleft #1 >
                { 
                %%変更箇所1：日本語の場合にカンマを全角に変更．
                  japanese.flag #0 =
                    {
                      ", " * t do.name.latex.cmd * 
                    }
                    {
                      "，" * t do.name.latex.cmd * 
                    }
                  if$
                %%変更箇所1ここまで
                }
                { s nameptr "{ll}" format.name$ duplicate$ "others" =
                    { 't := }
                    { pop$ }
                  if$
                  t "others" =
                    { " " * bbl.etal emphasize * }
                    { numnames #2 >
                        { 
                          %%変更箇所2：日本語の場合にカンマを全角に変更．
                          japanese.flag #0 =
                            {
                              "," * 
                            }
                            {
                              "，" *
                            }
                          if$
                          %%変更箇所2ここまで
                        }
                        { skip$ }
                      if$
                      %%変更箇所3：日本語の場合で著者が2人の場合はandの代わりに全角カンマを入れる．
                      %%bbl.andはandをくっつける処理と推測
                      japanese.flag #0 =
                        {
                          bbl.and
                          space.word * t do.name.latex.cmd *
                        }
                        {
                          numnames #2 =
                            { "，" * }
                            { skip$ }
                          if$
                          t do.name.latex.cmd *
                        }
                      if$
                      %%変更箇所3ここまで
                    }
                  if$
                }
              if$
            }
            { t do.name.latex.cmd }
          if$
          nameptr #1 + 'nameptr :=
          namesleft #1 - 'namesleft :=
        }
      while$
      cap.status.std
      } if$
    }

### 日本語を使う可能性のある文献形式にフラグ管理する処理を追加
あとは，日本語文献の場合に上で書いた処理が動くようにフラグを立てる処理を追加すれば完了です．自分の場合はmisc形式で日本語文献が出てきたので，`FUNCTION{misc}`に書いています．論文であるarticle形式に適用したい場合は`FUNCTION{article}`に同じように書きましょう．

    FUNCTION {misc}
    { 
      std.status.using.comma
      start.entry
      %%変更箇所1：日本語エントリに何かあれば日本語化するフラグを立てる
      isjapanese empty$
        {skip$}
        {#1 'japanese.flag :=}
      if$
      %%変更箇所1ここまで
      if.url.alt.interword.spacing
      format.authors output
      name.or.dash
      format.article.title output
      format.howpublished "howpublished" bibinfo.check output 
      format.organization "organization" bibinfo.check output
      format.address "address" bibinfo.check output
      format.pages output
      format.date output
      format.note output
      format.url output
      fin.entry
      empty.entry.warn
      if.url.std.interword.spacing
      %%変更箇所2：次の文献のために日本語化フラグの解除(0に戻しておく)
      #0 'japanese.flag :=
      %%変更箇所2ここまで
    }

他の形式の文献もそうですが，ここの関数の羅列を見れば，どの要素を記述しているかを知ることができます．今回のようにauthorの形式を変える場合は，`FUNCTION{format.authors}`を見ました．例えば，ページ数の形式を変えたい場合は`FUNCTION{format.pages}`を見るというように，`FUNCTION{format.要素}`のところにその要素の形式が書かれている可能性が高いです．また，表記の順序を変えたい場合は，この関数呼び出しをしている`format.`から始まる文を入れ替えればよいでしょう．

# 結果
以下のようなテストbibファイルを用意しました．

    @misc{japaneseTest1,
     author = {山田 一郎 and 山田 次郎 and 山田 三郎 and 山田 四郎},
     year = {2019},
     title = {文献1},
     isjapanese = {true}
    }
    @misc{japaneseTest2,
     author = {山田 五郎 and 山田 六郎},
     year = {2019},
     title = {文献2},
     isjapanese = {true}
    }
    @misc{englishTest1,
     author = {Ichiro Yamada and Jiro Yamada and Saburo Yamada and Shiro Yamada},
     year = {2019},
     title = {Title1}
    }
    @misc{englishTest2,
     author = {Goro Yamada and Rokuro Yamada},
     year = {2019},
     title = {Title2}
    }

そして，無事日本語か英語かで形式を切り替えることができました！

![samplebibtex.PNG](https://qiita-image-store.s3.amazonaws.com/0/152791/2c8369f6-ee5d-4bfa-eda0-86c36d441150.png)