Wiktionary では変化形を Lua のスクリプトで生成します。テンプレートの仕組みをざっと見て、スクリプトをローカルで動かします。
シリーズの記事です。
- Wiktionaryの効率的な処理方法を探る
- Wiktionaryの処理速度をF#とPythonで比較
- Wiktionaryの言語コードを取得
- Wiktionaryから特定の言語を抽出
- Wiktionaryで英語の不規則動詞を調査
- Wiktionaryのスクリプトをローカルで動かす ← この記事
この記事のスクリプトは以下のリポジトリに掲載しています。
テンプレート
MediaWiki にはテンプレートという機能があります。{{名前}}
という文字列を テンプレート:名前
というページで指示した内容に置き換えます。指示にはタグなどを組み合わせます。
en-verb
前回の記事で見たように、英語の動詞変化形は {{en-verb}}
というテンプレートで作られます。
{{en-verb}}
の参照先は Template:en-verb
です。
{{#invoke:en-headword|show|verbs}}<!--
-->{{#if:{{#invoke:ugly hacks|is_valid_page_name|{{{1|valid}}}}}||[[Category:Template with raw link/en-verb]][[Category:Template with raw link/en-verb/1]]}}<!--
-->{{#if:{{#invoke:ugly hacks|is_valid_page_name|{{{2|valid}}}}}||[[Category:Template with raw link/en-verb]][[Category:Template with raw link/en-verb/2]]}}<!--
-->{{#if:{{#invoke:ugly hacks|is_valid_page_name|{{{3|valid}}}}}||[[Category:Template with raw link/en-verb]][[Category:Template with raw link/en-verb/3]]}}<!--
-->{{#if:{{#invoke:ugly hacks|is_valid_page_name|{{{4|valid}}}}}||[[Category:Template with raw link/en-verb]][[Category:Template with raw link/en-verb/4]]}}<!--
--><noinclude>{{documentation}}</noinclude>
この内容をざっくり見ます。
invoke
{{#invoke:en-headword|show|verbs}}
#invoke
は MediaWiki の Scribunto という拡張機能で、Lua で記述されたモジュールを呼び出します。
関数の呼び出しを表します。
- モジュール:
en-headword
- 関数:
show
- 引数:
verbs
モジュールは Module:モジュール名
というページに記述された Lua のスクリプトです。
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
後でこのスクリプトを実際に動かします。👉スクリプトを動かす
コメント
<!--
-->
ソースの見やすさのため改行を入れる際、表示から除外するためコメントにしているようです。
カテゴリー
{{#if:{{#invoke:ugly hacks|is_valid_page_name|{{{1|valid}}}}}||[[Category:Template with raw link/en-verb]][[Category:Template with raw link/en-verb/1]]}}
ページへの引数をチェックします。引数では変化形が指定され、そのページが存在しなければ Template with raw link/en-verb
などのカテゴリーに入れます。変化形も見出しに入れるためにチェックするのが目的のようです。
ページに対する引数は {{{1}}}
のように取得します。引数が存在しなければチェックする必要はありませんが、ここでは {{{1|valid}}}
と指定することで、引数がなければ valid
という存在するページを使ってチェックを通しているようです。4番目の引数まで同じコードが続きます。
ある種のプログラミングですが、独特な手法のため初見ではすぐに意味が分かりませんでした。
日本語版ではこの手法で変化形も生成します。👉日本語版
documentation
<noinclude>{{documentation}}</noinclude>
ブラウザで Template:en-verb を開くと説明が表示されますが、それを埋め込んでいます。
指定は相対パスで、実体は Template:en-verb/documentation
です。
<noinclude>
が指定されているため、他のページから {{en-verb}}
で埋め込むときには無視されます。
スクリプトを動かす
Module:en-headword は読むにはやや長いため、まずは動かしてみます。
依存関係
モジュールは mw.ustring
などの MediaWiki のライブラリを使用します。
Wiktionary の別のモジュールも呼び出します。
準備
依存するファイルをダウンロードしたりダンプから取り出すスクリプトを用意しました。
ダンプからの取り出しは、以前の記事で作成したデータベースを使用します。(enwiktionary.db)
実行
Template:en-verb
からの Module:en-headword
の呼び出しをエミュレートするスクリプトを用意しました。コマンドライン引数を frame
に入れてモジュールに渡します。
start = 1
if arg[1] == "-s" then start = 2 end
if not arg[start] then
print("usage: lua " .. arg[0] .. " [-s] word [args...]")
return
end
lualib = "mediawiki-extensions-Scribunto/includes/engines/LuaCommon/lualib/"
package.path = lualib .. "?.lua;" .. lualib .. "mw.?.lua;" .. package.path
frame = {
args = {"verbs"},
title = word,
getParent = function()
args = {}
for k, v in pairs(arg) do
if k > start then args[k - start] = v end
end
return {args = args}
end,
expandTemplate = function(frame, title)
--print(title.title)
end,
}
mw = {
getCurrentFrame = function()
return frame
end,
loadData = require,
title = {
getCurrentTitle = function()
return { text = arg[start], subpageText = arg[start], }
end,
},
text = require("text"),
ustring = require("ustring/ustring"),
}
result = require("Module:en-headword").show(frame)
if start > 1 then result = string.gsub(result, "<.->", "") end
print(result)
これを実行すれば、Wiktionary に埋め込まれるデータが取得できます。Lua への最初の引数には原形、それ以降は en-verb
の引数を与えます。
- 例: set
{{en-verb|sets|setting|set}}
$ lua en-verb.lua set sets setting set
<strong class="Latn headword" lang="en">set</strong> (<i>third-person singular simple present</i>
<b class="Latn form-of lang-en 3|s|pres-form-of " lang="en">[[sets#English|sets]]</b>,
<i>present participle</i> <b class="Latn form-of lang-en pres|ptcp-form-of " lang="en">
[[setting#English|setting]]</b>, <i>simple past and past participle</i>
<b class="Latn form-of lang-en past|and|past|ptcp-form-of " lang="en">[[set#English|set]]</b>)
ごちゃごちゃしているので、タグを取り除くオプション -s
を用意しました。
$ lua en-verb.lua -s set sets setting set
set (third-person singular simple present [[sets#English|sets]], present participle
[[setting#English|setting]], simple past and past participle [[set#English|set]])
※ 変化形はリンクになっています。説明と区別するため取り除かずに残しました。
例を見ながら色々と試してみると良いでしょう。
最短一致
他の言語の正規表現では *
に対する最短一致は *?
が一般的ですが、Lua では -
です。これによってタグを取り除きます。
if start > 1 then result = string.gsub(result, "<.->", "") end
参考
Lua の仕様はコンパクトで、1つのウェブページに網羅されています。
日本語版
日本語版では Lua のモジュールは使わずに、タグによる条件分岐で変化形を生成します。
<onlyinclude>{{head|en|verb|head={{{head|}}}}}
(<small>三単現: </small>{{#if:{{isValidPageName|{{{1|valid}}}}}|''[[<!--
-->{{en-verb/getPres3rdSg|{{{1|}}}|{{{2|}}}|{{{3|}}}|{{{4|}}}}}<!--
-->]]''|{{{1|-}}}}},
<small>現在分詞: </small>{{#if:{{isValidPageName|{{{2|valid}}}}}|''[[<!--
-->{{en-verb/getPresP|{{{1|}}}|{{{2|}}}|{{{3|}}}|{{{4|}}}}}<!--
-->]]''|{{{2|-}}}}},
<small>過去形: </small>{{#if:{{isValidPageName|{{{3|valid}}}}}|''[[<!--
-->{{en-verb/getPast|{{{1|}}}|{{{2|}}}|{{{3|}}}|{{{4|}}}}}<!--
-->]]''|{{{3|-}}}}},
<small>過去分詞: </small>{{#if:{{isValidPageName|{{{4|{{{3|valid}}}}}}}}|''[[<!--
-->{{en-verb/getPastP|{{{1|}}}|{{{2|}}}|{{{3|}}}|{{{4|}}}}}<!--
-->]]''|{{{4|{{{3|-}}}}}}}} )
<includeonly>[[Category:{{eng}}]][[Category:{{eng}} {{verb}}]]</includeonly></onlyinclude>
このうち三単現の {{en-verb/getPres3rdSg}}
だけ見てみます。
<onlyinclude><!--
// 第2/3パラメータが "es"であるとき、"{1}[{2}]es"をかえす
-->{{#ifeq:{{#if:{{{3|}}}|{{{3}}}|{{{2}}}}}|es|{{{1}}}{{#if:{{{3|}}}|{{{2}}}}}es|<!--
// その他の場合で、第2/3パラメータが "ied" であるとき、"{1}{2}es"をかえす
-->{{#ifeq:{{{2}}}{{{3}}}|ied|{{{1}}}{{{2}}}es|<!--
// その他の場合で、第2/3パラメータが"d", "ed", 又は "ing"であるとき、"{PAGENAME}s"を返す。
-->{{#switch: {{#if:{{{3|}}}|{{{3}}}|{{{2}}}}}|d|ed|ing={{PAGENAME}}s|<!--
// その他の場合で、3個以上のパラメータがあるとき、{1}を返す。
-->{{#if:{{{3|}}}|{{{1}}}|<!--
// その他は{PAGENAME}sを返す。
-->{{PAGENAME}}s<!--
-->}}}}}}}}</onlyinclude>
処理内容はコメントに書いてある通りですが、慣れないと分かりにくいため、一部だけ説明します。
{{#if:{{{3|}}}|{{{3}}}|{{{2}}}}}
これは三項演算子で書けば exists($3) ? $3 : $2
のような意味です。第3パラメータがあればそれを、なければ第2パラメータを返す式です。null 合体演算子で書けば $3 ?? $2
に相当します。
{{{3|}}}
と {{{3}}}
は引数が存在しないときの挙動が異なるため、使い分けます。{{{3|}}}
は引数が存在しないときに else を選ばせるためのイディオムのようです。
記法が独特で取っつきにくいですが、コードは局所化されて規模が小さいので、少し慣れれば Lua のスクリプトよりも追いやすいかもしれません。
感想
今まで Wiktionary で変化形をどう記述しているのかさっぱり分かりませんでした。実際に調べてみると、テンプレートとモジュールを組み合わせた複雑な仕組みを持っていることが分かりました。言語の専門家とプログラマーが共同で作業しないと作り込めないため、個人にはなかなか敷居が高いように感じました。
OmegaWiki
OmegaWiki という多言語辞書プロジェクトは、Wiktionary への不満がきっかけで作られたようです。
The idea of OmegaWiki was born out of frustration with Wiktionary. Many Wiktionary projects worked together in using templates to indicate the non-language specific information. The labels of this information were indicated using templates. It proved useful; much information was copied from one project to another. It became problematic when updates happened. It had to be done manually in all participating projects. Given that the ISO 639-6 recognises in between 7,000 and 8,000 languages, it just does not scale.
DeepL 翻訳に手を加えた訳
OmegaWiki のアイデアは、Wiktionary への不満から生まれました。多くの Wiktionary のプロジェクトは、言語に固有ではない情報を示すためにテンプレートを使っていました。この情報のラベルはテンプレートを使って表示されました。それは役に立ったため、多くの情報がプロジェクトから別のプロジェクトへとコピーされました。しかし更新が行われると問題になります。コピーされたすべてのプロジェクトを手動で更新する必要があります。ISO 639-6 では 7,000 から 8,000 の言語を識別しますが、このやり方はスケールしません。
今回の調査から、何となくこの気持ちが分かる気がします。