带有变量的HTML i18n / l10n的编码样式

时间:2013-03-07 14:16:13

标签: php python html localization internationalization

我一直在从事网络开发工作已有一段时间了,我一直在努力寻找一个干净的解决方案来解决我在HTML字符串i18n中遇到的问题,主要是锚标签。

首先让我向您展示一个典型的问题示例。这是HTML模板中经常遇到的字符串:

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

如何在仍具有以下属性的情况下翻译此字符串:

  • 动态生成网址(例如使用路由器)
  • 尽可能可读的可翻译字符串(因此翻译人员无需查看代码即可完成)
  • 因为字符串包含HTML,我可能想要转义我插入的部分内容(例如URL),所以如果此URL包含用户输入,我不会让自己容易受到XSS的攻击
  • 在代码中它应该看起来尽可能好

如果字符串包含动态内容和HTML,您如何翻译它们?

1 个答案:

答案 0 :(得分:3)

当我现在想要将i18n应用于该字符串时,我可能会转向gettext或框架函数。由于我来自PHP / Joomla!世界,我之前使用过JText::_,其行为与gettext非常相似。在Python中,我现在使用Babel。两者都有相同的问题,也可能有更多的语言。我在这里分享的所有代码都是我在Python中的方式,更明确地说,在我的Mako templates

当然,问题是:我们的字符串中有待翻译的HTML(以及URL)。以下是我的选择,我将在后面解释:

  • 将原始字符串传递给gettext
  • 将文本拆分为三位
  • 用变量包围链接的单词
  • 使用一个单独构建的变量

将原始字符串传递给gettext

这个似乎是人们可能采取的第一种方法,如果不了解其含义的话。

方法1:

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

对于这个msgid,您现在可以翻译它,保持HTML完整。

优点:

  • 这在代码中看起来非常干净且易于理解
  • 如果翻译人员保持HTML完整,则不会产生任何问题

缺点:

  • 翻译必须至少知道一点HTML
  • 字符串完全不灵活,例如如果网址发生变化,则必须调整所有翻译
  • 它不允许使用类似路由器
  • 的动态生成URL

所以作为结论,虽然我使用了这个,但我很快达到了极限。我的下一个想法是:

将文本拆分为三位

方法2:

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

优点:

  • 网址现在非常灵活
  • 仅翻译人员的实际文字

缺点:

  • 将一个句子分成三部分
  • 译者必须知道哪些部分相关联,否则他可能无法产生有意义的句子
  • 代码不太漂亮
  • msgid可能只是一个单词,可能会导致问题(提防上下文),但可以修复。

我使用这种技术已有一段时间了,因为我不知道PHP中的printf样式字符串(我当时使用过)。因为这看起来很难看,我尝试了另一种方法:

用变量

包围链接的单词

方法3:

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

优点:

  • 要翻译的单个字符串,一个完整的句子
  • Translator从字符串中获取上下文
  • 代码不是 丑陋

缺点:

  • 译者必须注意不要错过%s(可能会出现混淆,因为它看起来像sproducts
  • 为每个网址引入两个格式字符串变量,其中一个仅为</a>

使用一个单独构建的变量

从这里我有一些不同的方法,但我最终得到了一个我目前使用的方法(可能看起来有点矫枉过正,但我​​现在更喜欢它。)

方法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提供automatic escaping,但在这样的声明中这没有意义:

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

它会破坏网址,因此我必须应用|n过滤器来移除任何转义。但是,如果用户提供了任何参数,它也会打开我想要阻止的XSS。没有冒任何风险,我可以逃避任何输入(与defualt一样好的模板引擎),然后删除Mako为这一个字符串转义。所以

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

实际上看起来像

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

escape导入markupsafe的位置(请参阅Markupsafe)。

现在最后一部分是通过路由器的动态网址:request.route_url('products_view')

要结合这些可能性,我必须制作一些非常难看的东西(请注意,这会使用mapping 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'))}) 的{​​{1}}关键字参数,但它结合了我所有的好处想要/需要翻译:

最终结果:

msgid

优点:

  • 完整的HTML escpaing
  • 完全动态
  • 非常好_('products') s用于翻译

缺点:

  • 模板(或程序中的)丑陋的构造
  • 语言提取器无法捕获{{1}},因此我们手动提取

就是这样,我总结了解决这个问题的方法。也许我正在做一些方式复杂的事情,你有更好的想法,或者这可能是一个问题,取决于特定类型的可翻译文本(并且必须选择正确的方法)。

我是否错过任何可以改善我的方法的解决方案或任何方法?