0

私はかなり長い間Web開発に取り組んできましたが、i18nのHTML文字列(主にアンカータグ)で発生した問題のクリーンな解決策を見つけるのに常に苦労していました。

まず、典型的な問題のある例を示しましょう。これは、HTMLテンプレートで頻繁に使用される文字列です。

Welcome to my site. Check out our cool <a href="/products">products</a> 
you should not miss.

次のプロパティを保持したまま、この文字列を翻訳するにはどうすればよいですか。

  • URLの動的生成(ルーターの使用など)
  • 可能な限り読みやすい翻訳可能な文字列(翻訳者はコードを見なくても実行できます)
  • 文字列にはHTMLが含まれているため、挿入した一部(URLなど)をエスケープしたい場合があります。このURLにユーザー入力が含まれていても、XSSに対して脆弱になることはありません。
  • コードでも可能な限り見栄えがする必要があります

動的コンテンツとHTMLが含まれている場合、文字列をどのように翻訳しますか?

4

1 に答える 1

3

その文字列にi18nを適用したいときは、おそらくgettextフレームワーク関数に目を向けます。私はPHP/ Joomlaから来たので!私が以前使用した世界は、とJText::_非常によく似ていgettextます。Pythonでは、Babelを使用しています。どちらも同じ問題を共有しており、おそらくより多くの言語も共有しています。ここで共有するすべてのコードは、Pythonで、より明確に、Makoテンプレートでそれを行う方法です。

もちろん、問題は次のとおりです。翻訳する文字列にHTMLが含まれています(さらに言えば、URLも含まれています)。これが私のオプションです。それぞれ後で説明します。

  • 生の文字列をに渡すgettext
  • テキストを3ビットに分割する
  • リンクされた単語を変数で囲む
  • 個別にビルドされる1つの変数を使用する

生の文字列をに渡すgettext

これは、影響を認識していない場合、最初に採用する可能性のあるアプローチのようです。

アプローチ1:

_('Welcome to my site. Check out our cool <a href="/products">products</a> \
you should not miss.')

このためmsgidに、HTMLをそのままにして、翻訳することができます。

利点:

  • これはコードでは非常にきれいに見え、理解しやすいです
  • 翻訳者がHTMLをそのまま保持している場合、これによって問題が発生することはありません。

短所:

  • 翻訳者は少なくとも少しのHTMLを知っている必要があります
  • 文字列は完全に柔軟性がありません。たとえば、URLが変更された場合は、すべての翻訳を調整する必要があります
  • ルーターのようなものを使用してURLを動的に生成することはできません

結論として、これを使用している間、私はすぐに限界に達しました。私の次のアイデアは:

テキストを3ビットに分割する

アプローチ2:

_('Welcome to my site. Check out our cool ') + '<a href="/products">' +\
_('products') + '</a>' + _(' you should not miss.')

利点:

  • URLは完全に柔軟になりました
  • 翻訳者向けの実際のテキストのみ

短所:

  • 文を3つの部分に分割します
  • 翻訳者は、どの部分が相互に関連しているかを知る必要があります。そうしないと、意味のある文を生成できない可能性があります。
  • コードではあまりきれいではありません
  • msgid単一の単語である可能性があり、問題を引き起こす可能性があります(コンテキストに注意してください)が、修正することができます。

printfPHPのスタイル文字列(当時使用していた)について知らなかったため、この手法をしばらく使用しました。これはとても醜いように見えたので、私は別のアプローチを試しました:

リンクされた単語を変数で囲む

アプローチ3:

_('Welcome to my site. Check out our cool %sproducts%s you should not miss.' % \
('<a href="/products">', '</a>')

利点:

  • 翻訳する単一の文字列、完全な文
  • 翻訳者は文字列からコンテキストを正しく取得します
  • コードはそれほど醜いものではありません

短所:

  • 翻訳者は、%s行方不明にならないように注意する必要があります(次のように読むと混乱する可能性がありますsproducts
  • すべてのURLに2つのフォーマット文字列変数を導入します。1つはのみです。</a>

個別にビルドされる1つの変数を使用する

ここからいくつかの異なるアプローチがありましたが、最終的に現在使用しているアプローチを思いつきました(これはやり過ぎのように見えるかもしれませんが、今のところそれを実行します)。

アプローチ4:

_('Welcome to my site. Check out our cool %s \
you should not miss.') % ('<a href="%s">%s</a>' % ('/products', _('products')))

この(一見狂ったように見える)アプローチを推論するために少し時間を割いてみましょう。まず、実際の翻訳文字列は次のようになります。

_('Welcome to my site. Checkout our cool ${product_url} \
you should not miss.')

これにより、翻訳者はそこに挿入されている情報を残すことができます(これがtranslationstringバージョンです)。次に、HTMLに挿入されたすべての部分を手動でエスケープできるようにします。Makoは自動エスケープを提供しますが、これは次のようなステートメントでは意味がありません。

${'This is a <a href="/">url</a>'}

それはURLを破壊するので|n、エスケープを削除するためにフィルターを適用する必要があります。ただし、その引数のいずれかがユーザー指定である場合は、防止したいXSSも開きます。リスクを冒さずに、入力をエスケープして(優れたテンプレートエンジンがdefualtで行うのと同じ方法で)、この1つの文字列に対するMakoのエスケープを削除できます。それで

'<a href="%s">%s</a>' % ('/products', _('products'))

実際には次のように見えます

'<a href="%s">%s</a>' % (escape('/products'), _('products'))

どこescapeからインポートされますかmarkupsafeMarkupsafeを参照)。

最後の部分は、ルーターを介した動的URLです。request.route_url('products_view')

これらの可能性のそれぞれを組み合わせるには、非常に醜いものを作成する必要があります(これは、(translationstring.TranslationStringmapping )のキーワード引数を使用しますが、翻訳から必要なすべての利点を組み合わせています。translationstring

最終結果:

_('Welcome to my site. Checkout our cool ${product_url} \
you should not miss.', mapping={'product_url': '<a href="%s">%s</a>' %\
(escape(request.route_url('products_view')), _('products'))})

利点:

  • 完全なHTMLエスケープ
  • 完全に動的
  • msgid翻訳に非常に良い

短所:

  • テンプレート(またはとにかくプログラム)の非常に醜い構成
  • lingua抽出機能がキャッチされない_('products')ため、手動で抽出する必要があります

以上で、この問題への私のアプローチは終わりです。たぶん私は何か複雑な方法をやっていて、あなたはもっと良いアイデアを持っているか、あるいはこれは特定のタイプの翻訳可能なテキストに依存する問題です(そして正しいアプローチを選択する必要があります)。

私のアプローチを改善する解決策や何かを逃しましたか?

于 2013-03-07T14:16:13.163 に答える