はじめに
Kotlin In ActionというKotlinの解説書があります。
Kotlinの開発チームのメンバーによって書かれた本で、単にKotlinの機能や文法を解説するだけではなく、その背景にある考え方や動機、内部的な仕組みについても深く説明しているので、Kotlinを触る上ではまず読むべき一冊ではないかと思います。
そして、縁があってこの本の翻訳作業に参加させて頂き、日本語版の「Kotlin イン・アクション」を出版することが出来ました。私が英文の本を翻訳をする事自体は初めてなのですが、Kotlinという言語を日本語圏に広めるという意味でも重要な役割を果たす本であり、また、お金を出して買って頂くので、プレッシャーを感じつつ、クオリティ高く仕上げることに全力を注ぎました。
現役のエンジニアで翻訳する機会に恵まれるのはあまりないとは思いますが、twitterで以下のようなことを呟いてみました。
技術書の翻訳は今年とても大変だった仕事のうちの1つだけど、とても勉強になった素晴らしい機会だった。あまり需要もないかもだけど、同様の体験した人とノウハウ交換したい。
— Satoru Fujiwara (@satorufujiwara) 2017年10月16日
それなりに反応も頂いたので、校了の熱が覚めやらぬうちにエンジニアらしく知見をオープンにして、今後同様の翻訳を行う方(自身も含む)の参考に少しでもなればと思い、翻訳中に得たノウハウなどをこの記事にまとめました。翻訳は私も含む4名で行っているので、共同作業を進める上でのノウハウも書きたいと思います。
本記事の英文については、全てKotlin In Actionからの引用です。
また、訳文については、全て「Kotlin イン・アクション」からの引用です。
進め方について
期間
2017年3月にキックオフしました。原著の出版が2017年2月なのでその直後ということになります。
ただ実際の作業は5月にGoogleI/O 2017でKotlinのAndroid開発言語オフィシャル化が発表されてから、急ピッチで進みました。
なので、実際の翻訳作業は6月〜9月の4ヶ月間ぐらいかなと思います(10月はほとんど校正にあてました)。
もちろん平日夜や土日などを使ってやるので、この期間は大変な日々が続きました。。
使ったツール
本の執筆には大きく分けて、原稿執筆、編集、校正と3つのフェーズがあります。
最初の原稿執筆まではRe:VIEW+GitHubによる管理、編集と校正のフェーズでは、pdfのやりとりによって作業を進めました。
Re:VIEW
Re:Viewはマークダウンのような記法で記事が書け、書いた記事をpdfなどにエクスポート出来るツールです。このツールを使うこと自体は2度目(1度目は「Kotlin入門までの助走読本」を書いた時)だったので、使い慣れたツールでした。エディタにはATOMを使って、Re:VIEW用のpackageを導入して随時プレビューしながら進めました。
この際、やってて良かったなと思うのが最初にRe:VIEWの記法のルールを統一したことです。今回は翻訳だったので、コード(リスト)、図、注釈、コラムなど、書籍内に登場する文章以外の要素があらかじめ分かっていました。なので、それらをRe:VIEWの記法でどのように表現するかを最初に決めて、全員がそれを守って原稿を書き進めました。
以下がそのルールの一例です。
- コード:
@<code>{...}
- リスト:
//list[label][caption]{...//}
- 採番なしリスト:
//emlist[]{...//}
- 注釈:
//footnote[label][body]
上記以外にも、@<kw>{訳語,原語}
や@<em>{強調}
などもルールを決めました。
また、原著には以下のようにコードにコメントがついています。
(「Kotlin In Action」P4 Listing 1.1)
このコメントに対する翻訳は下記のように、独自の記法(★+行数)を決めて、それを原稿に記載し、編集のタイミングで適切な図へと変換して頂きました。このようなルールのおかげで非常に迅速に翻訳が出来たように思います。
GitHubによるissue管理とレビュー
今回は4人で翻訳したので、章ごとにissueを立て、章ごとにアサインを決めて翻訳作業を進めました。レビュワーも章ごとに担当を決めて、それぞれの章の翻訳が終わったらレビュワーがレビューコメントをして、「LGTM!」が出ればマージとなります。
レビューの観点についてですが、日本語の言い回しなどは後で編集するので、主に以下のようなことに気をつけてレビューを進めました。
- 誤訳がないか
- 対訳と合っているか
- 技術的に合っているか
- typoの指摘
対訳については、翻訳しながら新出の用語などをリストアップし、それぞれどう訳すかを決めて他の章にも反映していく方法をとりました。対訳については後ほど詳しく記述します。画像についてもこのタイミングで中の英語を翻訳し、Re:VIEW記法を用いて埋め込んだ上でPRをしました。したがって、masterにマージされたタイミングではpdfとして書き出すと一応書籍の形となっています。
このレビューで一番困ったことはGitHubのユニコーンが出て、PRが見れなくなることです。
今回の進め方だと1章=1テキストファイルとなり、このファイルが時には2000行を超え、この1つのテキストファイルに150件以上のコメントがつくと上記のような状態になります。これが出る前にPRを作り直すなどして工夫しましたが、何度も何度もこのユニコーンを拝むことになりました。またユニコーンが出るとPRについているコメントも見れなくなるので、指摘された内容が分からなくなります。その際にはPRのURLの最後に/files
をつけることで回避していました(あまり使い道のないWorkaroundな気がします)。
GitHub上の作業は概ね進めやすかったです。翻訳に限らず最近の技術書執筆の主流かと思います。
編集と校正
原稿を入稿後、編集が終わってからの校正のやりとりは全てpdfを使って行いました。また、今回は翻訳者以外にもレビュワーにも協力して頂いていたので、レビュワーとのやりとりも全てpdfを使って行いました。
上記のようにpdfにマーカを入れ、Noteを追加し、そこに修正して欲しい項目を書き込みました。pdfのやりとりは全てGitHubのissueです(「〜章の校正」のようなissue)。pdfが入り乱れる校正作業でしたが、デグレはほとんど無かったので、編集さんさすがだなと思って眺めていました。しかし次はもっと楽な方法が良いかなと思っています。
校正のタイミングでpdfのやりとりによる修正が結構多くなってしまったのですが、やはりレビュー、修正といった作業はGitHubでテキストをやりとりしたほうが楽なので、もう少しその形で進めても良かったなと思っています。
翻訳について
実際に英文を翻訳していくなかで気づいたノウハウをまとめました。プロの翻訳家にとっては当たり前と思われる内容かもしれません。また、どう訳すか?は人によって意見が異なる部分もあると思います。何かご意見等あればぜひ指摘していだければと思います。
対訳
翻訳作業を進めながら対訳表を作り、適宜議論をしながら対訳を決めていきました。その中で特に迷った部分などについて記載します。
parameter / argument
英語の技術書を訳す際に最初に当たる壁は何と言ってもこれな気がします。以下の訳注のような結論になりました。
【訳注1】関数を定義する際に用いられる変数を仮引数(parameter)、関数を呼び出す際に渡す値を実引数 (argument)と呼びます。英語では parameter と argument は比較的よく使い分けられていますが、日本 語では「仮引数」「実引数」は使い分けずに「引数」と呼ばれることも多いため、本書では実引数と仮引
数の違いに注目していない部分では、どちらも引数と呼ぶことにしています。(「Kotlin イン・アクション」 P23)
immutable / mutable
Kotlinの解説では必ずこの用語が出てきます。「不変/可変」と訳すか、「イミュータブル/ミュータブル」と訳すか非常に迷いました。Effective Javaだと「不変/可変」、コップ本だと、「イミュータブル/ミュータブル」と使われている、といった先駆者の様々な情報を集めました。そして、総合的に判断した結果、「イミュータブル/ミュータブル」を用いることにしました。また、某所でエンジニアにアンケートをとって、その結果もカタカナの方が優勢だったのもこの対訳を後押ししました。
block-body / expression-body
まず、statement
とexpression
の違いについて正しく認識する必要があります。
英語 | 対訳 | 違い |
---|---|---|
statement | 文 | 値を持たない。 |
expression | 式 | 値をもつ。文や他の式の一部として使える。 |
従って、expression-body function
というのは以下のようなbody
が値を持つexpression
で成り立つ関数を指します。
fun max(a: Int, b: Int): Int = if (a > b) a else b
これに対して以下のようなものがblock-body function
です。
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}
すなわち、関数のbody
の表現に{}
を使わず=
を使った関数がexpression-body function
ということになります。この対訳を決める際にKotlinのリファレンスを確認したらsingle-expression function
という表現はあるのですが、expression-body function
という表現がありませんでした。したがって、これまでのKotlinでの=
を使った式は「単一式関数」と表現されていました。
しかし、たとえば上の例でのa > b
も式(expression)なので、expression-body function
は「全体としては式だが内部に複数の式を持ちうる関数」と言うことが出来るのでこれを「単一式関数」と訳すのは抵抗がありました。これについてもいろいろ議論した結果、expression-body
を「式本体」、expression-body function
を「式本体の関数」と訳すことになりました。
その他いろいろ
他にも対訳はいろいろ決めましたが、そのうち理由がはっきりしているものをを以下に掲載します。
英語 | 対訳 | 理由 |
---|---|---|
curly brace | 波括弧 | 大中小という順番は議論別れるみたいなので、中という表現は避けた。 |
throw an exception | 例外をスローする | 例外のスロー/例外のキャッチと、カタカナ語で統一した。 |
predicate | 述部 | 「述語」と迷ったが、predicateとして用いられるラムダ内には述語を修飾する語が入るので。 |
頻出の構文
英語の翻訳でよく出る表現についても様々迷って以下のような方針で進めることにしました。
主語のyou
英語は主語を省略できない言語なので、主語のyou
が広く用いられます。
たとえば、
You can store a lambda expression in a variable and then treat this variable like a normal function
(「Kotlin In Action」P106 5.1.3節)
上記をそのまま訳すと
「あなたはラムダ式を変数に格納して、この変数を通常の関数のように扱うことができます。」
という訳になりますが、日本語では「あなた」という主語はあまり使われません。この本におけるyou
は「読者」もしくは「Kotlinを使っている人」を指すことがほとんどなので、その場合はこのyou
は極力省略することにしました。
ラムダ式は変数に格納することができ、この変数は通常の関数のように扱うことができます
(「Kotlin イン・アクション」P139 5.1.3節)
もちろんあえて「あなた」と訳している場面もあります。
そして、同様にwe
もそのまま「我々」と訳すのではなく、何を指しているかを考えながら省略するなりしました。we
は「Kotlinを開発している人たち」すなわち「JetBrainsチーム」のことを指していることが多かったように思います。
provide
技術書の英文にはprovide
が頻出します。検索したらそれなりに記事が出て来る(ここやここなど)ので、この本の特徴ではなく、技術書の英文の特徴かと思っています。
原著にもおそらく100箇所以上provide
が用いられていて、これをどう訳すかはとても迷いました。
This expression is called member reference, and it provides a short syntax for creating a function value that calls exactly one method or accesses a property.
「Kotlin In Action」P111 5.1.5節
上記のように無生物が主語で動詞がprovide
の場合は、多くの場合、以下のように「〜できる」と訳しました。
この式はメンバ参照と呼ばれ、これによって、メソッドを呼び出したりプロパティを参照したりする関数の値を作るための短い構文が使えます。
(「Kotlin イン・アクション」P146 5.1.5節)
さらに意訳をしたprovide
もあります。
Functional style provides many benefits when it comes to manipulating collections.
(「Kotlin In Action」P113 5.2節)
これは以下のように訳しました。
関数型プログラミングのスタイルは、コレクションの操作に非常に役立ちます。
(「Kotlin イン・アクション」P148 5.2節)
もちろん、「提供する」と訳した場面もあります。provide
が出てきた場合、毎回どのような訳にするのかを慎重に検討しました。
英語の語順と日本語の語順
単独の文であればある程度語順を入れ替えても文章は繋がりますが、本のように文脈がある文章を訳す場合は、接続詞やカンマや関係代名詞などで繋がった文を英語の語順通りに訳した方が良い場合がありました。
You removed the duplication in the logic, but instead quite a bit of boilerplate is required to create the ObservableProperty instance for each property and to delegate the getter and setter to it.
(「Kotlin In Action」P194 7.5.3節)
この文の後半のto
は「〜するために」という訳になるので、instead
以降は次のように訳せます。
「各プロパティごとにObservablePropertyのインスタンスを生成し、setter/getterを委譲する必要があるるために少しばかりのボイラープレートが代わりに必要です。」
これを前半の文章と繋げると
「ロジック内の重複を削除しましたが、各プロパティごとにObservablePropertyのインスタンスを生成し、setter/getterを委譲する必要があるるために少しばかりのボイラープレートが代わりに必要です。」
という訳になります。この訳でも意味は間違ってはいませんが、英語では繋がっていたbut
とinstead
が日本語では離れており、「重複の削除」の代わりに「ボイラープレートが必要」であることがわかりづらくなっています。
したがって、以下のようにto
をbecause
であるかのように訳しました。
重複したロジックを削除できましたが、代わりに少しばかりのボイラープレートが必要です。なぜなら、各プロパティごとにObservablePropertyのインスタンスを生成し、setter/getterを委譲する必要があるからです。
(「Kotlin イン・アクション」P254 7.5.3節)
英語で隣り合っている2つの文、特に上の例のように接続詞でつながっている場合は、それらの文章を離さないようにして訳していくようにしました。
関係代名詞が出て来る例も1つあげておきます。
A non-inline function can save the lambda passed to it in a variable and execute it later, when the function has already returned, so it’s too late for the lambda to affect when the surrounding function returns.
(「Kotlin In Action」P218 8.3.1節)
ここも前から訳していき、when
の前後であえて2つの文に分けました。
非インライン関数は、渡されたラムダを変数に保存し、そのラムダを後ほど実行します。そのときには関数がすでにリターンされたあとなので、周囲の関数がリターンするタイミングにラムダが影響を与えることはできません。
(「Kotlin イン・アクション」P287 8.3.1節)
注釈について
原書にも注釈はついているのですが、それ以外に翻訳でも「訳注」という形で注釈をつけました。主に3つの訳注がついています。
- 対訳など翻訳に関するもの
- Kotlin 1.1に関する言及
- 注釈がないと意味が分かりくそうなもの
翻訳に関する注釈
既にあげたように、対訳などに関しての注釈を適宜いれています。
【訳注1】関数を定義する際に用いられる変数を仮引数(parameter)、関数を呼び出す際に渡す値を実引数 (argument)と呼びます。英語では parameter と argument は比較的よく使い分けられていますが、日本 語では「仮引数」「実引数」は使い分けずに「引数」と呼ばれることも多いため、本書では実引数と仮引
数の違いに注目していない部分では、どちらも引数と呼ぶことにしています。(「Kotlin イン・アクション」 P23)
Kotlin 1.1に関する注釈
原著の出版が2017年の2月で、Kotlin1.1のリリースは2017年の3月です。したがって、原著においてはKotlin1.1の内容について言及されているものの、「執筆時点では開発中です」などの表現が用いられています。このような箇所のうち、Kotlin 1.1で対応済みのものに関しては訳注でその旨を補足しました。
しかし、執筆時点では、JavaScriptサポートはJetBrains内で開発中の試作段階なので、本書では取り上げません。
【訳注1】Kotlin 1.1でJavaScriptが正式サポートされました。(「Kotlin イン・アクション」P5 1.2.1節)
注釈がないと意味が分かりくそうなもの
以下のように英語圏では常識だが、日本語圏では常識じゃなさそうなものには訳注をつけました。
(頭を左に傾ければこれがエルビスに見えますよね?)
【訳注2】1950年代に活躍したロックスター、エルビス・プレスリーのことです。髪型はリーゼントで、エルビス演算子の「?」をその髪型に見たてています。(「Kotlin イン・アクション」P183 6.1.4節)
10章の後半はJKidというJSONのデシリアライズのライブラリの解説なのですが、以下のようなコードが出てきます。
init {
constructor.parameters.forEach { cacheDataForParameter(cls, it) }
}
このコードは40行ぐらいあるコードの一部ですが、コード全体の解説として以下のような文が出てきます。
初期化時に、このコードはそれぞれのコンストラクタの仮引数に対応したプロパティを見つけ、それぞれのアノテーションを探索します。
(「Kotlin イン・アクション」P386 10.2.5節)
英文でこの文章を読んだ時に、この解説がコードのどこのことを指しているのか全くわからなかったのですが、関数cacheDataForParameterの実装の内部を見れば、この関数が「それぞれのコンストラクタの仮引数に対応したプロパティを見つけ、それぞれのアノテーションを探索」しているので、この文章が上の抜粋したコードの解説であることが分かりました。cacheDataForParameter
の内部の実装は紙面の都合で省略されていたのです。
したがって以下のような訳注を付けました。
【訳注2】これらの処理は関数cacheDataForParameter内にあります。
(「Kotlin イン・アクション」P386 10.2.5節)
まとめ
- GitHub上でテキスト管理するとレビューがしやすい。issue管理も良い。
- 編集後の構成はpdfで行うが、バージョン管理に難あり。
- 翻訳時は対訳と誤訳に気をつける。対訳は表などにまとめて揺れがないようにする。
- 英語独特の言い回しなどをあらかじめどのように訳すか決めておくと良さそう。
- 相互レビューを行ったり、外部レビュワーを付けたりするのはとても良い。
- 注釈を使うことで翻訳にさらなる価値をつけることができる。
冒頭のTwitterでも書いたように、技術書の翻訳はとても大変ですが、とても勉強になる最高の機会でした。ここに書いたようなことは私一人で考えたことではなく、一緒に翻訳したメンバーや、レビューに協力して頂いた方々からのアイデアによるものです。もっとこうした方が良いなど、様々なご意見もあるとは思いますが、これから技術書の翻訳に関わる方の参考に少しでもなれば幸いです。
最後に、この本を通じての私の最大の仕事は、第二部の「Embracing Kotlin」を「Kotlinを愛でる」と訳したことだと思っています。