JavaScript代码中的国际化
将翻译添加到JavaScript会带来一些问题:
-
JavaScript代码无法访问gettext实现。
-
JavaScript代码无法访问.po或.mo文件; 他们需要由服务器交付。
-
JavaScript的翻译目录应尽可能小。
Django为这些问题提供了一个集成的解决方案:它将翻译传递给JavaScript,所以你可以在JavaScript中调用gettext等。
Javascript_Catalog视图
这些问题的主要解决方案是django.views.i18n.javascript_catalog()视图,该视图发送一个带有模拟gettext接口的函数的JavaScript代码库,以及一组转换字符串。
这些翻译字符串是根据您在info_dict或URL中指定的内容从应用程序或Django核心中获取的。 LOCALE_PATHS中列出的路径也包含在内。
You hook it up like this:
from django.views.i18n import javascript_catalog
js_info_dict = {
'packages': ('your.app.package',),
}
urlpatterns = [
url(r'^jsi18n/$', javascript_catalog, js_info_dict),
]
包中的每个字符串都应该采用Python虚线包语法(与INSTALLED_APPS中的字符串格式相同),并且应该引用包含语言环境目录的包。 如果您指定了多个包,则所有这些目录都将合并到一个目录中。 如果您使用的JavaScript使用来自不同应用程序的字符串,这很有用。
翻译的优先级是这样的,后面出现在软件包参数中的软件包的优先级高于出现在开头的软件包的优先级,这对于相同文字的翻译冲突很重要。
默认情况下,视图使用djangojs gettext域。 这可以通过改变域参数来改变。
您可以通过将包放入URL模式来使视图变为动态:
urlpatterns = [
url(r'^jsi18n/(?P<packages>\S+?)/$', javascript_catalog),
]
借此,您可以将包指定为URL中由'+'标记分隔的包名称列表。 如果您的页面使用来自不同应用程序的代码,并且这种情况经常发生变化,并且您不想拉入一个大的目录文件,这一点尤其有用。 作为安全措施,这些值只能是django.conf或INSTALLED_APPS设置中的任何软件包。
在LOCALE_PATHS设置中列出的路径中找到的JavaScript翻译也始终包含在内。 为了与用于Python和模板的翻译查找顺序算法保持一致,LOCALE_PATHS中列出的目录具有最高的优先顺序,首先出现的目录优先级高于稍后出现的目录。
使用Javascript翻译目录
要使用目录,只需像下面这样引入动态生成的脚本:
<script type="text/javascript" src="{% url ‘django.views.i18n.javascript_catalog’ %}"></script>
这使用反向URL查找来查找JavaScript目录视图的URL。 加载目录时,JavaScript代码可以使用标准的gettext接口来访问它:
document.write(gettext('this is to be translated'));
There is also an `ngettext` interface:
var object_cnt = 1 // or 0, or 2, or 3, ...
s = ngettext('literal for the singular case',
'literal for the plural case', object_cnt);
甚至还有一个字符串插值函数:
function interpolate(fmt, obj, named);
插值语法是从Python借用的,所以插值函数支持位置和指定插值:
- 位置插值:obj包含一个JavaScript数组对象,其元素值按照它们出现的相同顺序依次插入到其相应的fmt占位符中。 例如:
fmts = ngettext('There is %s object. Remaining: %s',
'There are %s objects. Remaining: %s', 11);
s = interpolate(fmts, [11, 20]);
// s is 'There are 11 objects. Remaining: 20'
- 命名插值:通过将可选的布尔命名参数作为true来选择此模式。 obj包含一个JavaScript对象或关联数组。 例如:
d = {
count: 10,
total: 50
};
fmts = ngettext('Total: %(total)s, there is %(count)s object',
'there are %(count)s of a total of %(total)s objects', d.count);
s = interpolate(fmts, d, true);
尽管如此,你不应该用字符串插值来解决问题:这仍然是JavaScript,因此代码必须重复进行正则表达式替换。 这不像Python中的字符串插值那么快,因此请将其保留到您真正需要的地方(例如,与ngettext一起生成适当的复数形式)。
性能说明
javascript_catalog()视图根据每个请求从.mo文件生成目录。 由于它的输出是恒定的 - 至少对于给定版本的站点而言 - 它是一个很好的缓存候选者。
服务器端缓存将减少CPU负载。 它很容易用cache_page()装饰器实现。 要在翻译更改时触发缓存失效,请提供版本相关的键前缀(如以下示例中所示),或将视图映射到依赖于版本的URL。
from django.views.decorators.cache import cache_page
from django.views.i18n import javascript_catalog
# The value returned by get_version() must change when translations change.
@cache_page(86400, key_prefix='js18n-%s' % get_version())
def cached_javascript_catalog(request, domain='djangojs', packages=None):
return javascript_catalog(request, domain, packages)
客户端缓存将节省带宽并使网站加载速度更快。 如果您使用的是ETags(USE_ETAGS = True),则您已被覆盖。 否则,您可以应用条件装饰器。 在以下示例中,每当您重新启动应用程序服务器时,缓存都会失效。
from django.utils import timezone
from django.views.decorators.http import last_modified
from django.views.i18n import javascript_catalog
last_modified_date = timezone.now()
@last_modified(lambda req, **kw: last_modified_date)
def cached_javascript_catalog(request, domain='djangojs', packages=None):
return javascript_catalog(request, domain, packages)
您甚至可以预先生成JavaScript目录作为部署过程的一部分,并将其作为静态文件提供。