エキスパート Python プログラミングに学ぶ PyMOTW 翻訳の進め方

Taru-hime's waterwall
Taru-hime's waterfall posted by (C)t2y

PyMOTW-ja *1 という Python 標準モジュールのチュートリアルを翻訳しています。2009年の5月頃から始めて、一時中断していたものの、現在は数10個のチュートリアルの翻訳が完了し、まだまだ鋭意翻訳中です。

PyMOTW それ自体がパッケージングして配布することを意識しているため、オリジナルのリポジトリ翻訳中のリポジトリ は bitbucket で公開されています。翻訳プロジェクトと言うとちょっと大げさですが、PyMOTW-ja の翻訳の進め方を1つの方法論として提案してみようと思います。Python の勉強をしたい方、英語の勉強をしたい方、その両方をやりたい方など、ご興味のある方は PyMOTW-ja の翻訳にぜひ参加してください(@ にご連絡ください)。

この翻訳の進め方のオリジナルの仕組み *2 は、エキスパート Python プログラミング *3 という書籍を翻訳するにあたり、他の翻訳者の方々(@, @, @)と協力して行った体験に基づいています。その体験が PyMOTW-ja の翻訳に生かされていて本当に感謝しています。

ドキュメントを Sphinx (reST) で管理する

SphinxPython ベースのプロジェクトで使用されるドキュメント生成ツールです。PythonDocutils: Documentation Utilities で採用された wikipedia:ReStructuredText でドキュメントを記述します。Sphinx-Users.jp — Python製ドキュメンテーションビルダー、Sphinxの日本ユーザ会Sphinx 開発の経緯 *4 や翻訳ドキュメントが充実しています。

reST で翻訳ドキュメントを作成する場合、オリジナルの英文をコメントとして残します。

.. This is translation string.

これは翻訳文字列です。

reST のソースファイルはサイト上で公開されています。例えば、itertools – 効率的なループ処理のためのイテレータ関数 - Python Module of the Week のソースは このようになります

この方法はオリジナルの英文と翻訳文をセットで管理することによって、複数人によるレビューや後で内容の不確かさに気付いたときにオリジナルの英文を読み返すのが容易という大きなメリットがあります(もちろんオリジナルの英文の diff を管理できないというデメリットもあります)。

リポジトリに分散バージョン管理を使用する

Git, Mecruial, Bazaar と様々な分散バージョン管理システムがあります *5 。無料でリポジトリが使用できるホスティングサイト GitHubbitbucket.org が有名です。PyMOTW が bitbucket.org を使用していたので私も使い始めました。bitbucket.org では無料のプランで容量が 1GB まで、プライベートリポジトリが1つだけ持てます(パブリックリポジトリは無制限に持てます)。翻訳のドキュメント管理に限った利点ではないですが、

  • 一時的作業の履歴管理が容易
  • オフラインによる開発


の2点が複数人による翻訳作業に便利だと、私は思います。

前述した reST のソースファイルでオリジナル英文をコメントに、翻訳文を本文として書く方法では diff が管理できないデメリットを分散バージョン管理システムの運用で対応します。具体的には PyMOTW と PyMOTW-ja の2つのリポジトリを使用するわけですが、更新された差分をマージすることで差分更新漏れがないことを管理し易いようにします。

基本ルールを守る

これも翻訳作業に限ったことではありませんが、複数人で共同作業を行う場合、必ず中軸となるような基本ルールを作るべきだと私は考えています。たとえ分散バージョン管理システムを利用して、個々人が自由に作業できたとしても最終的には1つの成果物になります。そこに至る過程や仕組みを理解しておくことは重要です。それを基本ルールとして共有します。

PyMOTW から PyMOTW-ja を派生させるにあたって以下の基本ルールを作成しました *6

  1. オリジナルの PyMOTW リポジトリから pull する、その差分は新しい記事か、何らかの修正があった内容になる(hg pull)
  2. どんな理由であろうとアップデートがあればその差分を調べる
    1. その差分が新しい記事か、未翻訳の記事の修正(hg update)
    2. その差分が翻訳された記事の修正(hg merge)
  3. PyMOTW-ja はオリジナルの reST フォーマットを維持したままで翻訳する
  4. 翻訳した内容は PyMOTW-ja のリポジトリへ更新する(hg commit/push)


PyMOTW が更新されている限りは、上述した手順を繰り返して PyMOTW-ja を更新します。基本的にオリジナルの PyMOTW は毎週金曜日に更新されます。更新通知は PyMOTW の RSS フィード や bitbucket のリポジトリを確認することができます。PyMOTW-ja も(おそらくは)毎週1回ビルドされてサイト上で更新されます。

そして PyMOTW-ja は私の管理しているリポジトリをマスタリポジトリとします。あるチュートリアルを翻訳したら pull request を私宛に送ってください。翻訳内容の簡単なレビューと文体の修正を行ってマージするようにします。

メーリングリストTwitter で気軽にやり取りする

英語のコミュニケーションに対してあまり難しく考え過ぎることはありません *7 。特にメーリングリストTwitter なら、ローカルで辞書を引きながら返信することができます。PyMOTW のメーリングリストGoogle グループ で著者の Doug Hellmann の Twitter ID は @ です。

PyMOTW を翻訳して良いかの確認を行ったときや誤植を見つけたときのやり取りです。

もっと適切なやり取りもあるでしょうし、もっと正しい英文もあるでしょうけれど、コミュニケーションを取りたいのであれば、こんなもんだと私は思います。OSS やコミュニティに参加することの楽しさの1つに現実の世界では会えない人や海外の人と簡単にやり取りできるのもあると私は思います。

PyMOTW の翻訳の進め方

具体的な PyMOTW/PyMOTW-ja の翻訳の進め方を説明します。先ず作業に必要なライブラリやツール類を揃えます。プラットホームに依存しないために easy_install を使用してインストールしますが、各自の環境にあわせて好みで管理してください。

$ sudo easy_install -UZ BeautifulSoup Jinja2 Paver Pygments \
                        Sphinx sphinxcontrib-paverutils \
                        distribute docutils mercurial pygame

bitbucket.org で行う作業とローカルマシン上で行う作業の2種類があります。

bitbucket.org にログインして http://bitbucket.org/t2y/pymotw-ja を fork します。基本的に翻訳作業は fork した自分のリポジトリに対して行います。bitbucket.org で fork した後でそのリポジトリをクローンします。

$ hg clone https://your_account@bitbucket.org/your_account/pymotw-ja
$ ls
pymotw-ja

あなた専用のリポジトリが取得できました。

$ cd pymotw-ja
$ hg log | head
チェンジセット:   757:29e4d02caedb
タグ:             tip
親:               754:db3581aabae3
親:               756:be8331d6bc22
ユーザ:         t2y
日付:             Tue Aug 17 02:24:55 2010 +0900
要約:             merged from original
$ tree -L 2
.
|-- LICENSE.txt
|-- MANIFEST.in
|-- PyMOTW
|   |-- BaseHTTPServer
|   |-- ConfigParser
|   |-- Cookie
... (snip)
|-- README.txt
|-- bin
|   |-- SendToMarsEdit.applescript
|   |-- new_module.sh
|   `-- template.rst
|-- module
|-- motw
|-- pavement.py
|-- sitemap_gen_config.xml
|-- sphinx
|   |-- blog
|   |-- conf.py
|   `-- templates
`-- utils
    `-- PyMOTW.tmbundle

PyMOTW ディレクトリの配下に各モジュールのチュートリアルがあります。"PYMOTW/モジュール名/index.rst" を直接編集していきます。index.rst ファイルと同じ場所にチュートリアル内で紹介しているサンプルコードもあります。サンプルコード内のコメント(docstring は翻訳しない)も翻訳します。サンプルコードによってはエンコーディング指定がないファイルもあるので "-*- coding: utf-8 -*-" が追加されているかに注意してください。例えば、itertools を翻訳したいとします。

$ vi PyMOTW/itertools/index.rst 
  1 ..
  2     =====================================================
  3     itertools -- Iterator functions for efficient looping
  4     =====================================================
  5 
  6 =====================================================
  7 itertools -- 効率的なループ処理のためのイテレータ関数
  8 =====================================================

このようにフォーマットを維持した状態で英文をコメントに、その直後に翻訳を行います。またサンプルコードそのものは Sphinx によるドキュメント生成時に、サンプルコードの実行結果は Ned Batchelder: Cog で出力するように記述されています。

 57 .. include:: itertools_chain.py
 58     :literal:
 59     :start-after: #end_pymotw_header
 60 
 61 .. {{{cog
 62 .. cog.out(run_script(cog.inFile, 'itertools_chain.py'))
 63 .. }}}
 64 .. {{{end}}}

今 itertools の翻訳が完了しました。ドキュメントを生成する前に Cog で記述したサンプルコードの実行結果を出力します。

$ paver cog PyMOTW/itertools
---> pavement.cog
/Library/Python/2.6/site-packages/Paver-1.0.3-py2.6.egg/paver/cog/cogapp.py:9:
  DeprecationWarning: the md5 module is deprecated; use hashlib instead
  import md5, os, re, string, sys, traceback, types
cog PyMOTW/itertools/index.rst
Cogging PyMOTW/itertools/index.rst cd PyMOTW/itertools;
 python itertools_chain.py 2>&1
cd PyMOTW/itertools; python itertools_izip.py 2>&1
cd PyMOTW/itertools; python itertools_islice.py 2>&1
cd PyMOTW/itertools; python itertools_tee.py 2>&1
cd PyMOTW/itertools; python itertools_tee_error.py 2>&1
cd PyMOTW/itertools; python itertools_imap.py 2>&1
cd PyMOTW/itertools; python itertools_starmap.py 2>&1
cd PyMOTW/itertools; python itertools_count.py 2>&1
cd PyMOTW/itertools; python itertools_cycle.py 2>&1
cd PyMOTW/itertools; python itertools_repeat.py 2>&1
cd PyMOTW/itertools; python itertools_repeat_izip.py 2>&1
cd PyMOTW/itertools; python itertools_repeat_imap.py 2>&1
cd PyMOTW/itertools; python itertools_dropwhile.py 2>&1
cd PyMOTW/itertools; python itertools_takewhile.py 2>&1
cd PyMOTW/itertools; python itertools_ifilter.py 2>&1
cd PyMOTW/itertools; python itertools_ifilterfalse.py 2>&1
cd PyMOTW/itertools; python itertools_groupby.py 2>&1
cd PyMOTW/itertools; python itertools_groupby_seq.py 2>&1
  (changed)

$ vi PyMOTW/itertools/index.rst 
 57 .. include:: itertools_chain.py
 58     :literal:
 59     :start-after: #end_pymotw_header
 60 
 61 .. {{{cog
 62 .. cog.out(run_script(cog.inFile, 'itertools_chain.py'))
 63 .. }}}
 64 
 65 ::
 66 
 67     $ python itertools_chain.py
 68     1
 69     2
 70     3
 71     a
 72     b
 73     c
 74 
 75 .. {{{end}}}

ここで PyMOTW のドキュメント生成には Paver というスクリプティングツールを使用します。Paver は Sphinx や Cog といった Python のツールをラップして統一的に1つのコマンドで扱うためのインタフェースを提供します。Paver の詳細については Paver ドキュメント(翻訳) を参照してください。ここでは "paver cog" はドキュメントを生成する前のプリプロセスとして Cog を実行しました。

次に Paver から Sphinx を使用して HTML ドキュメントを生成します。

$ paver html
---> pavement.html
Set TEMPLATES = "pkg"
---> sphinxcontrib.paverutils.html
mkdir ./PyMOTW/docs (mode 511)
mkdir sphinx/doctrees (mode 511)
sphinx-build  -b html -d sphinx/doctrees -c sphinx -Amodule=doctest
 ./PyMOTW PyMOTW/docs
Running Sphinx v1.0.1
... (snip)
preparing documents... done
writing output... [100%] zlib/index
writing additional files... genindex py-modindex search index
copying images... [100%] EasyDialogs/AskString_too_long.png
copying static files... done
dumping search index... done
dumping object inventory... done
build succeeded, 31 warnings.

ブラウザで翻訳したモジュールのドキュメントを確認します。motw コマンドについては motw コマンドラインインタフェースを参照してください。

$ ./motw --html itertools

ブラウザで全体として読み返してみると、誤字脱字や文章の繋がりのおかしい部分を発見し易くなります。私はこの過程を何度か繰り返して翻訳を行っています。

翻訳が完了したらリポジトリへ push しますが、その前に必ず Cog で生成したサンプルコードの実行結果を index.rst から取り除いてください。

$ paver uncog
---> paver.doctools.uncog
/Library/Python/2.6/site-packages/Paver-1.0.3-py2.6.egg/paver/cog/cogapp.py:9:
  DeprecationWarning: the md5 module is deprecated; use hashlib instead
  import md5, os, re, string, sys, traceback, types
cog ./PyMOTW/abc/index.rst
Cogging ./PyMOTW/abc/index.rst
... (snip)

Cog で生成される実行結果は Doug がドキュメントのビルド時に生成しています。Cog を使用することにより、サンプルコードの修正に対してドキュメント内の実行結果の修正が不要になるという大きなメリットがあります。

次にあなたのリポジトリに変更内容(翻訳ドキュメント)を push します。

$ hg status
M PyMOTW/itertools/index.rst
$ hg commit
translated itertools into Japanese
$ hg push

bitbucket.org のあなたのリポジトリに変更が反映されました。それから bitbucket.org のサイトから pull request を私宛に送るか、Twitter 等、何らかの方法で私に連絡して頂ければ良いです。

その後、私の方で fork したあなたのリポジトリから merge するようにします。そして最終的に私の pymotw-ja リポジトリを Doug が pull してドキュメントをビルドして PyMOTW-ja にアップロードしてくれます。

他の人のリポジトリをマージする

自分のリポジトリを更新する方法は分かりました。しかし、Doug は毎週新しいモジュールのチュートリアルを公開しますし、私も並行して他のモジュールの翻訳を行います。どうしても他の人のリポジトリを自分のリポジトリにマージする作業が必要になります。

私は以下のようにして Doug のリポジトリをマージしています。先ずオリジナルの PyMOTW リポジトリをクローンして、そのまま差分を確認するために置いておきます。Mercurial の詳細については Mercurial の利用 が分かり易いです。

$ hg clone https://your_account@bitbucket.org/dhellmann/pymotw
(1週間経つと更新される)
$ cd pymotw
$ hg pull -u
$ hg diff -r xx:yy
(差分を確認)

次に私のリポジトリの hgrc を設定します。デフォルトで使用するリポジトリとオリジナルのリポジトリを設定しています。

$ hg clone https://your_account@bitbucket.org/your_account/pymotw-ja
$ cd pymotw-ja
$ vi .hg/hgrc 
[ui]
username = t2y

[paths]
default = ssh://hg@bitbucket.org/t2y/pymotw-ja
doug = https://t2y@bitbucket.org/dhellmann/pymotw-ja
original = https://t2y@bitbucket.org/dhellmann/pymotw
default-push = ssh://hg@bitbucket.org/t2y/pymotw-ja

オリジナルのリポジトリに更新があれば、そちらからマージします。

$ hg pull -u original
($ hg merge)
$ hg commit
$ hg push

このとき、オリジナルのリポジトリで変更された差分が新規追加のみ、又はうまくマージされれば競合は発生しません。特に何か作業をする必要はありません。競合が発生したり新規ヘッドが追加された場合、自分でマージして修正する必要があります。

  • 競合が発生した場合
オリジナルのリポジトリでの作業
$ cd pymotw
$ hg pull -u
$ hg log PyMOTW/exceptions/index.rst
$ hg diff -r713:722 PyMOTW/exceptions/index.rst
(差分を黙視で確認)

自分のリポジトリでの作業
$ cd pytmow-ja
$ hg pull -u original
https://t2y@bitbucket.org/dhellmann/pymotw から取り込み中
変更点を探索中
チェンジセットを追加中
マニフェストを追加中
ファイルの変更を追加中
4 のチェンジセット(40 の変更を 38 ファイルに適用)を追加(+1個のヘッド)
新規ヘッドが追加されたため、作業領域は更新しません
(ヘッド一覧表示は 'hg heads'、マージ実施は 'hg merge')

$ hg merge
PyMOTW/exceptions/index.rst をマージ中
警告: マージ中に衝突を発見。
PyMOTW/exceptions/index.rst のマージに失敗!
PyMOTW/history.rst をマージ中
PyMOTW/site/index.rst をマージ中
PyMOTW/string_services.rst をマージ中
PyMOTW/sys/interpreter.rst をマージ中
pavement.py をマージ中
ファイル状態: 更新数 32、マージ数 5、削除数 0、衝突未解決数 1
'hg resolve' での再度衝突解消か、'hg up -C' で変更破棄してください

$ hg resolve -l
U PyMOTW/exceptions/index.rst
R PyMOTW/history.rst
R PyMOTW/site/index.rst
R PyMOTW/string_services.rst
R PyMOTW/sys/interpreter.rst
R pavement.py
(PyMOTW/exceptions/index.rst に競合が発生)

$ vi PyMOTW/modules/index.rst
<<<<<<< local
..
    UnicodeError is a subclass of ValueError and is raised when a Unicode
    problem occurs.  There are separate subclasses for UnicodeEncodeError,
    UnicodeDecodeError, and UnicodeTranslateError.

UnicodeError は ValueError のサブクラスでユニコードに関する
問題で発生します。UnicodeEncodeError, UnicodeDecodeError や
UnicodeTranslateError は UnicodeError のサブクラスです。
=======
:class:`UnicodeError` is a subclass of :class:`ValueError` and is
raised when a Unicode problem occurs.  There are separate subclasses
for :class:`UnicodeEncodeError`, :class:`UnicodeDecodeError`, and
:class:`UnicodeTranslateError`.
>>>>>>> other
("<<<<<<< local", "||||||| base", ">>>>>>> other" といった
 マージ結果が検出されるので、オリジナルの差分を黙視で確認しながら、
 対象ファイルを手動で修正します)

$ hg diff PyMOTW/exceptions/index.rst
diff -r 10f99892eb8e PyMOTW/exceptions/index.rst
--- a/PyMOTW/exceptions/index.rst Sun Aug 29 16:17:47 2010 +0900
+++ b/PyMOTW/exceptions/index.rst Mon Aug 30 14:06:50 2010 +0900
@@ -628,16 +628,18 @@
 .. }}}
 .. {{{end}}}
 
+.. _exceptions-UnicodeError:
 
 UnicodeError
 ------------
 
 ..
-    UnicodeError is a subclass of ValueError and is raised when a Unicode
-    problem occurs.  There are separate subclasses for UnicodeEncodeError,
-    UnicodeDecodeError, and UnicodeTranslateError.
+    :class:`UnicodeError` is a subclass of :class:`ValueError` and is
+    raised when a Unicode problem occurs.  There are separate subclasses
+    for :class:`UnicodeEncodeError`, :class:`UnicodeDecodeError`, and
+    :class:`UnicodeTranslateError`.
 
-UnicodeError は ValueError のサブクラスでユニコードに関する問題
 で発生します。UnicodeEncodeError, UnicodeDecodeError や
 UnicodeTranslateError は UnicodeError のサブクラスです。
+:class:`UnicodeError` は :class:`ValueError` のサブクラスで
 ユニコードに関する問題で発生します。さらに :class:`UnicodeEncodeError`,
 :class:`UnicodeDecodeError` や :class:`UnicodeTranslateError` 
 といったサブクラスに分割されます。
 
 .. _exceptions-ValueError:

$ hg resolve -m PyMOTW/exceptions/index.rst
(手動で競合を解除)

$ hg resolve -l
R PyMOTW/exceptions/index.rst
R PyMOTW/history.rst
R PyMOTW/site/index.rst
R PyMOTW/string_services.rst
R PyMOTW/sys/interpreter.rst
R pavement.py
(競合が解消されていることを確認)

$ hg commit
merged from original
$ hg push
  • 新規ヘッドが追加された場合
$ hg pull -u original
https://t2y@bitbucket.org/dhellmann/pymotw から取り込み中
変更点を探索中
チェンジセットを追加中
マニフェストを追加中
ファイルの変更を追加中
7 のチェンジセット(43 の変更を 39 ファイルに適用)を追加(+1個のヘッド)
新規ヘッドが追加されたため、作業領域は更新しません
(ヘッド一覧表示は 'hg heads'、マージ実施は 'hg merge')
$ hg merge
PyMOTW/exceptions/index.rst をマージ中
PyMOTW/history.rst をマージ中
PyMOTW/numeric.rst をマージ中
pavement.py をマージ中
35 個のファイルが更新, 4 個のファイルがマージ,
0 個のファイルが削除, 0 個のファイルが衝突未解決
(マージ結果の commit を忘れずに)
$ hg resolve -l
R PyMOTW/exceptions/index.rst
R PyMOTW/history.rst
R PyMOTW/numeric.rst
R pavement.py
(自動マージに成功したので手動で編集する必要はありません)
$ hg commit
merged from original
$ hg push

エキスパートPythonプログラミング

エキスパートPythonプログラミング