開発者向けの国際化ガイド

Plone 翻訳チームが翻訳作業をできるようにするには、Zope Page Templateをどのように作成すればよいのかを説明します。

導入

……あるいは "文化的な理解を深める (そして野蛮な欧米人の侵略から身を守る) ための手引き"

Ploneの国際化は、次の2つから成り立っています。

  • 開発者が、プロダクトの中で翻訳すべきフレーズを指定する
  • 翻訳者が、各言語用の翻訳ファイルを作成する

この文書で扱うのは前者、つまりプロダクトの開発者向けの内容です。これを読み終えれば、あなたの作成したプロダクトの翻訳を翻訳チームに簡単に任せられるようになるでしょう。

はじめに

次のようなシンプルなページテンプレートを考えてみましょう。

<html>
<body>
  <p>Welcome to Plone.</p>
  <img src="plone.gif" alt="Plone Icon" />
  <p>There have been over 
    <span tal:content="here/download_count">100,000</span>
    downloads of Plone.
  </p>
  <p>Please visit 
    <a href="about">About Plone</a>
    for more information.
  </p>
</body>
</html>

この中には翻訳を要する箇所がいくつかあります。これらのさまざまな形式について理解しておくと、ZPTを国際化対応させるのによい助けとなることでしょう。

  1. "Welcome to Plone" は、各言語向けに直接翻訳しなければなりません。
  2. 画像の代替テキスト "Plone Icon" も翻訳が必要です。
  3. それから "There have been over 100,000 downloads of Plone." という文も。ただこの場合は、数字の部分は動的に計算しています。この文の構造は、言語によって異なることに注意しましょう。たとえば英語の場合は上の例のようになりますが、他の言語では数字が先頭にくるかもしれませんし、あるいは数字が最後にくるかもしれません。
  4. "Please visit About Plone for more information." のところでは、"About Plone" の部分のリンクをそのまま維持する必要があります。つまり、この部分は個別に翻訳することになるということです。さらに、先ほどの例と同様、文章の中におけるリンクの位置は言語によって変わる可能性があります。
  5. 言語によっては、"There have been over ${number} downloads of Plone" の翻訳が ${number} の値によって変化するかもしれません。しかし、このようなケースは現状の Plone や Zope ではサポートしていません。

例1: "Welcome to Plone"

In this case, we want to tell the translation team that this is a single concept to translate, without any text in it that must be treated specially or dynamically calculated.

To do this, we merely need to use the i18n:translate attribute with the value "XXX" in the enclosing <P> tag.

For our example:

<p i18n:translate="XXX">Welcome to Plone.</p>

Now, when the translation team uses their tools (http://plone.org/products/i18ndude) to extract all the msgids from the page templates, they can conveniently look-up the messages that need to be given a message id.

Ultimately, the translation team will choose a message identifer (msgid), effectively replacing "XXX" to uniquely identify this phrase, and that will become the new value of the i18n:translate attribute.

例2: 画像の代替テキスト

In this case, we need to let the team know that it's the value of an attribute, not the contents of the tag itself, that needs translating.

To do this, we'll use the i18n:attributes attribute.

This attribute should list all of the tag's attributes that contain text that must be translated. If you have more than one attribute, you should separate them with spaces.

For our example:

<img src="plone.gif" alt="Plone Icon" i18n:attributes="alt" />

An example with two attributes would be:

<img src="plone.gif" alt="Plone Icon" title="Plone Icon Title"
 i18n:attributes="alt title">

In Zope 2.7 and later, an additional feature has been added that will allow the translation team to identify not just the actual attribute to translate, but also note what the msgid for that attribute will be. If this feature is used, it is done as a semicolon-separated list, such as:

<img src="plone.gif" alt="Plone Icon" title="Plone Icon Title"
 i18n:attributes="alt plone-icon; title plone-icon-title">

meaning that we have two dynamic attributes: alt, with msgid "plone-icon", and title, with msgid "plone-icon-title".

This doesn't really matter if you're creating a new template, as you shouldn't be trying to assign msgids anyway (that's the job of the translation team); however, if you're editing a template that's already been handled by the translation team, this will help you understand what you're seeing.

例3: 動的コンテンツ

Our third case was:

<p>There have been over 
  <span tal:content="here/download_count">100,000</span>
  downloads of Plone.
</p>

Here, we'll use the i18n:name attribute. This gives us a way to identify that part of a longer piece to be translated should be represented as an individual piece that can be moved around.

For example, we want our translators to see our text as something like:

There have been ${count} downloads of Plone.

So that they can translate that, moving the actual count elsewhere in the sentence, as needed.

So, using i18n:name, we give a name to the dynamically calculated part:

<p i18n:translate="">There have been over 
  <span tal:content="here/download_count"
   i18n:name="count">100,000</span>
  downloads of Plone.
</p>

Note that the 'name' does not need to have any relationship to the TAL expression that calculates it. Also, the 'name' is only relevant in the context of the enclosing tag (the paragraph tag in our case), so it only has to be unique within that tag. Therefore, you can assign these, and don't have to let the i18n team do that for you.

A case where there are more than one replaceable:

<p i18n:translate="">My name is 
  <span tal:content="here/first" i18n:name="first">first</span>
  <span tal:content="here/last" i18n:name="last">last</span>
</p>

The translation team will see this as:

My name is ${first} ${last}

which is exactly what we want.

例4: 合わせ技

In case four, we are combining ideas from all of our previous cases.

We have:

<p>Please visit 
  <a href="about">About Plone</a>
  for more information.
</p>

We want the sentence to come to the translators as:

Please visit ${about-plone} for more information.

But, unlike our dynamic content case, above, we want this internal phrase, "About Plone", to be translated as well.

To do this, we'll put an i18n:name attribute on the link tag, and put an i18n:translate tag around it, so that translators can see that it requires translation itself. So:

<p i18n:translate="">Please visit 
  <span i18n:name="about-plone">
    <a href="about" i18n:translate="">
      About Plone</a>
  </span>
  for more information.
</p>

To add in our second case, if we had an attribute of the link tag (such as 'title') that needed to be translated, we could add that, too:

<p i18n:translate="">Please visit 
  <span i18n:name="about-plone">
    <a href="about" i18n:translate=""
       i18n:attributes="title" title="Go to About Page">
    About Plone</a>
  </span>
  for more information.
</p>

ヒントと注意点

あまりがんばりすぎない

It's possible to do something like:

<p i18n:translate="">
  Cart has <tal:block replace="number">#</tal:block> 
  book<tal:block condition=
    "python: number <> 1">s</tal:block>.</p>

to dynamically output "book" or "books" depending on whether one or more than one book is in the cart.

Such tricks make the work of the documentation team very difficult, since some languages do not have a consistent way of pluralizing words, and many do not use the common English-style case of added a letter or two at the end.

It is better to simply say:

<p i18n:translate="">
  Cart has <tal:block replace="number">#</tal:block> books.
</p>

Never mix i18n:name and i18n:translate attributes at same tag

If you want to reuse some translations, you should separate the i18n:name and i18n:translate attributes. Do not say:

<p i18n:translate="text_long">
  Bar
  <a href="" i18n:name="foo" 
             i18n:translate="text_used_other_places">
      Foo
  </a>
  Baz
</p> 

Instead, you should say:

<p i18n:translate="text_long">
  Bar
  <span i18n:name="foo">
    <a href="" i18n:translate="text_used_other_places">Foo</a>
  </span>
  Baz
</p> 

Include everything in the tag

Include the entire phrase or sentence in the tag that has i18n:translate, including punctuation. Do not say:

<span i18n:translate="">The participants</span>:

(note the trailing colon). As many languages do not use colons to end a declarative introduction, this would be rendered inappropriately.

Instead, simply say:

<span i18n:translate="">The participants:</span>

Which allows the translation team to make this decision.

Pythonスクリプトからは決してHTMLを返さない

Returning rendered HTML from a PythonScript? (or other non-ZPT generator) makes it essentially impossible for the translation team to do the right thing with your page.

Instead, do what you should be doing anyway: use ZPT for markup, and use scripts to generate data structures for markup by ZPT (this is also easier to debug, and leads to generally better concepts and code reusability).

ひとつの文はひとつのタグにまとめる

Don't do this:

<p>
  <span i18n:translate="">Hello, my name is</span>
  <span tal:content="user/name">user</span>
  <span i18n:translate=""> and I live in Zopeville.</span>
</p> 

Instead, be sure to put the i18n:translate tag on the enclosing paragraph tag (as demonstrated above), and use i18n:name to show the replaceable user name part.