コンテンツを翻訳

はじめに

コンテンツの翻訳は LinguaPlone アドオンプロダクト によって行われます。

以下の例を参照してください。

翻訳に対応したコンテンツタイプ (Archetypes)

LinguaPlone は各フィールドが言語に依存するか、非依存かのマークをつけることが可能です。

ノート

多言語対応をするために Products.Archetypes.atapi.* の代わりに
Products.LinguaPlone.public.* API 関数を使用します。

Example:

try:
    from Products.LinguaPlone import public as atapi
except ImportError:
    # No multilingual support
from Products.Archetypes import atapi

class MyContent(atapi.ATContent):
    pass

atapi.registerType(MyContent, ...)

例:

try:
    from Products.LinguaPlone import public as atapi
except ImportError:
    # No multilingual support
    from Products.Archetypes import atapi


class MyContent(atapi.ATContent):
      pass

atapi.registerType(MyContent, ...)

より詳細な情報は以下を参照してください。

他の言語のコンテントアイテムを取得する

考えられるユースケース

  • 既知のパスを指定して翻訳されたコンテントアイテムを取得したい場合。 例: “portal/footer” というコンテントアイテムがふくんでいる他の言語用の動的なテキストを取得することが可能です。
  • コンテンツの中身を多数の言語で同時に表示したい場合。

ITranslatable.getTranslation() メソッドを使用することにより、現在の言語で翻訳されたコンテンツを表示することができます。

def getTranslation(language=’language’):
“”” Return the object corresponding to a translated version or None. If called without arguments it returns the translation in the currently selected language, or self. “”“

Example:

class Footer(BaseViewlet):
    """ A footer viewlet which will pull editable and translated footer text.

    The localized text is available as Archetypes Page object which id
    is "footer-text" in the site root. LinguaPlone translation look-up
    is performed.

        1. Create the footer in your site main language as a hidden
           Page which id is set to "footer-text"

        2. Use "Translate to..." action to create localized versions
           of this footer

        3. Leave footer private so that it does not appear in the
           search results etc.

    Note that the workflow state of the footer content is not considered.

    Note that "footer" is reserved id in Plone, thus we use "footer-text".
    """

例:

class Footer(BaseViewlet):
    """ A footer viewlet which will pull editable and translated footer text.

    The localized text is available as Archetypes Page object which id
    is "footer-text" in the site root. LinguaPlone translation look-up
    is performed.

        1. Create the footer in your site main language as a hidden
           Page which id is set to "footer-text"

        2. Use "Translate to..." action to create localized versions
           of this footer

        3. Leave footer private so that it does not appear in the
           search results etc.

    Note that the workflow state of the footer content is not considered.

    Note that "footer" is reserved id in Plone, thus we use "footer-text".
    """

    grok.name("plone.footer")
    grok.viewletmanager(IPortalFooter)

    def getFooterText(self):
        """ Call this from your viewlet template.

        Example::

            <div id="portal-footer">
              <div tal:replace="structure viewlet/getFooterText" />
            </div>
        """

        from Products.LinguaPlone.interfaces import ITranslatable

        portal = self.portal_state().portal()

        if "footer-text" in portal.objectIds(): # Note that you can use has_key() for BTree based folders
            footer = portal["footer-text"]
            if ITranslatable.providedBy(footer):
                translated = footer.getTranslation()
                if translated:
                    return translated.getText()

            return footer.getText()

        return ""

コンテンツを翻訳する

LinguaPlone は翻訳を作成する方法を参照するための、ユニットテスト用のコードを含んでいます。 context.addTranslation(言語コード) メソッドと context.getTranslation(言語コード) メソッドを使用することができます。

例:

from Products.LinguaPlone.I18NBaseObject import AlreadyTranslated

try:
    object.addTranslation(lang)
except AlreadyTranslated:
    # 備考: Products.Linguaplone がインストールされていない場合は、常に AlreadyTranslated が発生します
    pass

translated = object.getTranslation(lang)

http://svn.plone.org/svn/plone/LinguaPlone/tags/2.1.1/Products/LinguaPlone/tests/translate_edit.txt を参照して下さい。

正しいドメイン名によって翻訳されたコンテンツを提供する

以下のような場合に適用されます。

  • 単一の Plone インスタンスを複数の言語用のホストとして使用したい。
  • Plone インスタンスが異なったドメイン名にマップされている。
  • 言語がトップレベルドメイン名またはサブドメイン名で決定される。

SEO とユーザビリティ上の理由により、一定のドメインからは必ず一定のコンテンツが提供されなければいけません。 ユーザーが日本語ドメインの /news や逆に英語ドメインの /oshirase コンテクストにアクセスすることを、Plone は防ぐことができません。 これらの URL がサーチエンジンに漏れた場合、混乱の原因となります。

以下のような複雑な publication 後のフック処理により、ユーザーの要求に対してその言語で提供すべき正しいドメインに対してリダイレクトします。:

"""

    Domain aware language redirects.

    Redirect the user to a correct domain where the language should be served,
    if mixing and matching different domain names and language versions.

    http://mfabrik.com

"""

import urlparse

from zope.interface import Interface
from zope.component import adapter, getUtility, getMultiAdapter
from plone.postpublicationhook.interfaces import IAfterPublicationEvent

from gomobile.mobile.utilities import get_host_domain_name
from gomobile.mobile.interfaces import IMobileRequestDiscriminator, MobileRequestType

from Products.CMFCore.interfaces import IContentish

def get_contentish(object):
    """
    Traverse acquisition upwards until we get contentish object used for the HTTP response.
    """

    contentish = object
    while not IContentish.providedBy(contentish):

        if hasattr(contentish, "aq_parent"):
            contentish = contentish.aq_parent
        else:
            break

    return contentish


def redirect_domain(request, response, new_domain):
    """ Redirect user to a new domain, but having URI in intact.

    This also keeps port part of netloc in intact.

    @param new_domain: New domain name to redirect, without port.
    """

    # Get the

    url = request["ACTUAL_URL"]
    parts = urlparse.urlparse(url)

    # Replace domain name
    parts = list(parts)
    netloc = parts[1]

    # TODO: Handle @ and HTTP Basic auth here
    if ":" in netloc:
        domain, port = netloc.split(":")
        netloc = new_domain + ":" + port
    else:
        netloc = new_domain

    parts[1] = netloc
    new_url = urlparse.urlunparse(parts)

    # Make 301 Permanent Redirect response
    response.redirect(new_url, status=301)
    response.body = ""
    response.setHeader("Content-length", 0)

def ensure_in_domain(request, response, language_now, wanted_language, wanted_domain):
    """ Make sure that certain language gets served from a correct domain.

    If the user tries to access URI of page, and the page language
    does not match the domain we expect, redirect the user to the correct domain.
    """

    domain_now = get_host_domain_name(request)

    if language_now == wanted_language:
        if domain_now != wanted_domain:
            # print "Fixing language " + language_now + " to go to " + wanted_domain + " from " + domain_now
            redirect_domain(request, response, wanted_domain)

@adapter(Interface, IAfterPublicationEvent)
def language_fixer(object, event):
    """ Redirect mobile users to mobile site using gomobile.mobile.interfaces.IRedirector.

    Note: Plone does not provide a good hook doing this before traversing, so we must
    do it in post-publication. This adds extra latency, but is doable.
    """

    # print "language_fixer"

    request = event.request
    response = request.response
    context = get_contentish(object)

    if hasattr(context, "Language"):
        # Check whether the context has a Language() accessor to get
        # the language where it was written in
        language_now = context.Language()

        #print "Resolving mobility"
        discriminator = getUtility(IMobileRequestDiscriminator)
        flags = discriminator.discriminate(context, request)

        if MobileRequestType.MOBILE in flags:
            # Do mobile
            ensure_in_domain(request, response, language_now, "fi", "m.mfabrik.fi")
            ensure_in_domain(request, response, language_now, "en", "mfabrik.mobi")
        else:
            # Do web
            ensure_in_domain(request, response, language_now, "fi", "mfabrik.fi")
            ensure_in_domain(request, response, language_now, "en", "mfabrik.com")

    # print "Done"