TeX Alchemist Online

TeX のこと,フォントのこと,Mac のこと

英単語から発音記号を自動出力する

この記事は TeX & LaTeX Advent Calendar 2022 の13日目の記事です。12日目はzr_tex8rさんでした。 13日目は yi_chemist さんです。

TeX Forumで 発音記号&アクセントの自動出力 というスレッドが立っており,50件以上のコメントが付く長大スレッドとなっていました。

okumuralab.org

やりたいこと

TeX Live に含まれるtipaパッケージを用いれば,IPAの国際音声記号(発音記号)をLaTeX文書で自在に出力できるようになります。

【入力例】

\textipa{/""Ekspl@"neIS@n/}
\textipa{/Int@rn\`\ae S@n@l@z\'eIS@n/}

【出力例】

ただ,誰しもが抱くであろう感想として,「入力法が面倒」という点が挙げられます。これを何とかしたい,という質問でした。つまり,英単語を入力すればそれに対応するtipaパッケージの入力に変換してほしいというわけです。

自動化の方法

上記スレッドにおいて,自分は

  1. 要求された単語の辞書における定義文を何らかの方法で取得しに行く
  2. そうして得られた辞書の定義文から発音記号部分だけを正規表現などでマッチングさせて取り出す
  3. 次にその発音記号部のUnicode文字列を tipa.sty のソース形式に1文字ずつ翻訳してゆく (例:æ\ae, ʌ2, ɒ6 ) というプログラムを書く必要があるでしょう。

コメントしました

その方法の一例を示してみようと思います。

方針の策定

「発音記号自動解決システム」を構築しようと思ったとき,いくつかの方針が考えられます。

  • 発音記号情報をどこから持ってくるか?
    • OS内蔵辞書を検索する?
      • メリット:オフライン環境でも使える
      • デメリット:特定の環境でしか使えない
    • オンラインの辞書サイトから検索する?
      • メリット:そのようなプログラムをLuaスクリプトとして書いておけばポータビリティが高まる
      • デメリット:オフライン環境では使えない
  • 発音記号の解決をいつやるか?
    • ツールを使って単語ごとに解決し,それをLaTeXソースに手動でコピペするか?
      • メリット:最も単純なので,数が少なければそれが楽。結果を修正したいときも融通が利く。
      • デメリット:TeX的に面白い部分がなく TeX & LaTeX Advent Calendar のネタにならない
    • 事前に必要な単語の発音記号の辞書ファイルのようなものを作成して,それをLaTeX文書から読み込むか?
      • メリット:コンパイルが速い
      • デメリット:ユーザー側の準備作業が必要
    • コンパイルごとに毎回動的に解決するか?
      • メリット:ユーザー側からは「全自動解決」されているように見えて楽
      • デメリット:コンパイルが重くなる(特にオンラインで毎回検索する方式の場合)。-shell-escape が必要。検索結果をカスタマイズしたいときに自由が利かない。

今回の質問者は macOS ユーザということでしたので,ここでは「macOSの内蔵辞書(ウィズダム英和辞典)を検索して発音記号を取得する」というツールを作ってみましょう。

Swiftコード

1ファイルからなるシンプルなSwiftコードを用意しました。

英単語から \textipa の入力に自動変換するツール · GitHub

これをコンパイルしてバイナリを生成します。もしSwiftコンパイラがインストールされていなければ次のコマンドでインストールできます。

$ xcode-select --install

そして,Gistからダウンロードしたソースをコンパイルします。

$ swiftc word2tipa.swift

使い方

事前準備

macOS標準の辞書アプリを起動し,設定画面で,ウィズダム英和辞典をONにして,一番上に持っていっておきましょう。これをしないと正しく検索されないことがあるようです。

ツールの使い方

生成された word2tipa は,「引数に英単語を与えれば,それをウィズダム英和辞典で検索して,見つけた最初の発音記号を tipa パッケージの入力法に変換して出力する」という動きをします。

【使用例】

$ ./word2tipa pronunciation
\textipa{pr@n\`2nsi\'eIS(@)n}

複数単語を与えてもOKです。

【使用例】

$ ./word2tipa quick brown fox
\textipa{kwIk} \textipa{braUn} \textipa{fA(:)ks}

発音記号が見つからない単語を与えた場合は,代わりに が出力されます。派生語などは,辞書の見出し語にはあっても,発音記号が記載されていないケースが多いですので,それは仕方ありません。

【使用例】

$ ./word2tipa visualization
☃

オプション解説

word2tipa にオプションを与えれば,次のように出力をカスタマイズできます。

  • --flattenParen: (ə) のような「発音が省かれることがあることを示す丸括弧で囲まれた音声記号」の括弧を外して見やすくします。
  • --removeParen : 逆に (ə) のような丸括弧付き音声記号を省略して消してしまいます。
  • --stressMonosyllabicWord : 単音節の単語にも母音部分に強勢記号を付与します。(例:/bɪɡ/ → /bɪ́ɡ/ )
  • --removeSecondStress : 第二強勢の記号を削除します。 (例:/ɪ̀ntərfɪ́ər/ → /ɪntərfɪ́ər/)

LaTeX文書からの自動解決

次に,「LaTeX文書のコンパイル時に自動的に word2tipa を起動して発音記号を動的に解決させる」というのを試みてみましょう。ここでは文書ファイルと同ディレクトリに word2tipa のバイナリを置いている状況を想定し,それを起動して標準出力をTeXソースに流し込みます。

LuaLaTeXでの例

LuaLaTeXの場合,「外部コマンドの標準出力を流し込む」というのが容易にできます。(なお,このコードについての注意点を別記事で補足しました。)

【LuaLaTeXのコード例】

%#!lualatex -shell-escape
\documentclass{jlreq}
\usepackage{tipa}
\usepackage{luacode}

\begin{luacode*}
function readFromPipe(cmd)
  local f = assert(io.popen(cmd, 'r'))
  local s = assert(f:read('*a'))
  f:close()
  return s
end
\end{luacode*}

\newcommand\wordToTipa[2][]{\directlua{tex.print(readFromPipe('./word2tipa #1 #2'))}}

\begin{document}
/\wordToTipa{control}/ /\wordToTipa{sequence}/\par
\wordToTipa{quick brown fox}\par
\wordToTipa[--flattenParen --stressMonosyllabicWord]{quick brown fox}
\end{document}

【コンパイル法】

外部ツールを起動するので -shell-escape が必要です。

$ lualatex -shell-escape hoge.tex

【出力】

一般のLaTeXエンジンでの例

次のようにすれば,LuaLaTeXだけでなく,(u)pLaTeX, XeLaTeX, pdfLaTeX にも対応させることが可能です。

\documentclass{article}
\usepackage{tipa}
\makeatletter

\newread\inputstream

% ファイルの内容を読み込んで命令の内容にセットする \readfromfile{ファイル名}{命令名}
\newcommand\readfromfile[2]{%
  \def\readfromfile@result{}%
  \openin\inputstream=#1\relax
  \@readfromfile
  \closein\inputstream
  \removeTrailingSpaces\readfromfile@result
  \let#2\readfromfile@result
}

\def\@readfromfile{%
  \global\read\inputstream to \readfromfile@line\relax
  \ifeof\inputstream
    \let\readfromfile@next\relax
  \else
    \expandafter\expandafter\expandafter\def
    \expandafter\expandafter\expandafter\readfromfile@result
    \expandafter\expandafter\expandafter{%
    \expandafter\readfromfile@result\readfromfile@line}%
    \let\readfromfile@next\@readfromfile
  \fi
  \readfromfile@next
}

\long\def\removeTrailingSpaces#1{%
  \def\@removeTrailingSpaces@a{}%
  \def\@removeTrailingSpaces@b{#1}%
  \expandafter\@removeTrailingSpaces#1 \relax
  \expandafter\let\@removeTrailingSpaces@b\@removeTrailingSpaces@a
}

\long\def\@removeTrailingSpaces#1 #2\relax{%
  \def\@tempa{#1}%
  \def\@tempb{#2\relax}%
  \def\@tempc{\relax}%
  \ifx\@removeTrailingSpaces@a\@empty
    \def\@removeTrailingSpaces@a{#1}%
  \else
    \ifnum0\ifx\@tempb\@tempc\else1\fi\ifx\@tempa\@empty\else1\fi>0\relax
      \expandafter\expandafter\expandafter\def
      \expandafter\expandafter\expandafter\@removeTrailingSpaces@a
      \expandafter\expandafter\expandafter{%
      \expandafter\@removeTrailingSpaces@a\space#1}%
    \fi
  \fi
  \ifx\@tempb\@tempc\else
    \@removeTrailingSpaces#2\relax
  \fi
}

% 外部コマンドの標準出力を読み込んで命令の内容にセットする \readfrompipe{コマンド}{命令名}
\def\readfrompipe#1#2{\readfromfile{|"#1"}{#2}}

\newcommand\wordToTipa[2][]{\readfrompipe{./word2tipa #1 #2}{\@tempa}\@tempa}
\makeatother

\begin{document}
/\wordToTipa{control}/ /\wordToTipa{sequence}/\par
\wordToTipa{quick brown fox}\par
\wordToTipa[--flattenParen --stressMonosyllabicWord]{quick brown fox}
\end{document}

【コンパイル法】

ptex2pdf でコンパイルする場合,-ot オプションでTeXエンジンに対して -shell-escape を付与してコンパイルします。

$ ptex2pdf -u -l -ot "-shell-escape" hoge.tex

【出力】