前提
まずは gettext
の基本的な手順の説明です。
main.cpp
に _()
マクロで gettext が使用されている前提です。
xgettext --keyword='_' -o foo.pot main.cpp
このコマンドで foo.pot
が出力されます。*.pot
はバージョン管理しないようにしてください。
msginit --locale=ja --input=foo.pot
このコマンドで foo.pot
から日本語用の ja.po
が生成されます。
これが通常の gettext
を利用した初期化手順です。
次に main.cpp
翻訳対象のテキストが増えた場合の手順ですが、先程出てきた xgettext
を再度使います。
xgettext --keyword='_' -o foo.pot main.cpp
しかし、次に ja.po
を msginit
で生成してしまうと翻訳済みのテキストが全て上書きされて失われてしまいます。そのため gettext
には msgmerge
というコマンドが用意されています。
msgmerge ja.po foo.pot -o ja.po
このようにすると、元の ja.po
に新たに生まれた foo.pot
の翻訳されていないテキストが追加された新しい ja.po
が生まれます。
問題
例えば make
と打てばビルドから *.pot
, *.po
, *.mo
も生成したいようなプロジェクトだった場合、例えばこのような Makefile となるでしょう。
.PHONY: all
all:
gcc main.cpp -o main
xgettext --keyword='_' -o foo.pot main.cpp
msgmerge ja.po foo.pot -o ja.po
.PHONY: init-i18n
init-i18n:
xgettext --keyword='_' -o foo.pot main.cpp
msginit --locale=ja --input=foo.pot
この Makefile
であれば、最初だけ make init-i18n
と入力して、それ以降は make
でコンパイルと ja.po
の生成ができます。(.mo
の生成は割愛してます)
main.cpp
にテキストが追加されていないときに make
をもう一度実行することも当然あるのですが、このときに、バージョン管理上の問題が発生します。その例として以下の diff が作られます。
diff --git a/ja.po b/ja.po
index 529e532..10a871e 100644
--- a/ja.po
+++ b/ja.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-20 20:07+0900\n"
+"POT-Creation-Date: 2018-09-20 20:08+0900\n"
"PO-Revision-Date: 2018-09-20 20:07+0900\n"
"Last-Translator: Toshiyuki Hirooka <mail@address>\n"
"Language-Team: Japanese\n"
これは make
を実行した時間が foo.pot
を生成した時間であり、それが ja.po
に msgmerge
で結合されたことによる問題です。生成時刻はバージョン管理を行う必要性がないと考えてよいのですが、この手順ではどうしてもこの diff が発生してしまいます。
また、main.cpp
に変更や新たなテキストが生成されたときの問題もあります。
index 016f223..5a60241 100644
--- a/ja.po
+++ b/ja.po
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-#: main.cpp:2
+#: main.cpp:3
+msgid "Yahoo!"
+msgstr ""
+
+#: main.cpp:4
msgid "Hello, World!"
msgstr "こんにちは、世界!"
この diff は "Hello, World!" というテキストの登場位置が変わったり、新たな "Yahoo!" というテキストが追加されていることを示しています。
例えばこれをバージョン管理でコミットすると、+#: main.cpp:4
の行がコンフリクトする可能性があります。なぜなら、"Hello, World!" の位置が変わる修正は他者が行う可能性があり git などのバージョン管理ソフトではそれを許容しているのに、その場合に ja.po
で同じ行に対して他者と競合してしまうからです。
解決方法
長くなりましたが、解決方法は簡単です。
.PHONY: all
all:
gcc main.cpp -o main
xgettext --keyword='_' -o foo.pot main.cpp
msgmerge ja.po foo.pot -o ja.po && msgcat ja.po --no-location --no-wrap --sort-output --output ja.po && cat ja.po | grep -v POT-Creation-Date > tmp.po && mv tmp.po ja.po
.PHONY: init-i18n
init-i18n:
xgettext --keyword='_' -o foo.pot main.cpp
msginit --locale=ja --input=foo.pot
先程の Makefile
をこのように変更しました。この変更では次のことをやっています。
-
ja.po
をmsgcat
を使って登場位置の削除やテキストの順番のソートなどを行っている -
POT-Creation-Date
の行を削除している
これで新たにテキストが追加された時、削除された時、変更された時だけ次のように diff が発生します。
diff --git a/ja.po b/ja.po
index c6c235a..1d69722 100644
--- a/ja.po
+++ b/ja.po
@@ -18,3 +18,6 @@ msgstr ""
msgid "Hello, World!"
msgstr "こんにちは、世界!"
+
+msgid "Yahoo!"
+msgstr ""
余談
取り急ぎだったので甘い作りですが、POT-Creation-Date
の regex をもう少しキレイにしたり、tmp.po
を一旦作らなくても良いような修正をしたほうが安全です。
以上です。