从本主题开始《云客Drupal源码分析》系列将连续发布和前端js相关的内容,如果您对JavaScript还不熟悉或者需要来一次系统性的整理回顾,在此云客为您准备了以下资料:

《PHP开发者的JavaScript快速文档》

首发于爱码文档汇(nowicode.com),您也可以到云客的博客阅读,该篇资料全文6万字,A4页面45页,简明系统的介绍了js相关知识,由于从Drupal8.4开始核心为每一个js文件加入了对应的ES6版本,因此该资料也全面补充介绍了ES6,该资料对比了php和js语言,是针对php开发者而写的,在继续阅读本系列前强烈推荐您阅读。

前端js使用翻译:

本系列已经讲述了php程序和twig模板中的翻译如何处理,请见本系列的以下主题:

《国际化Internationalization:核心翻译系统》

《twig服务》

在前端的js程序中可以使用以下方法进行翻译:

Drupal.t(str, args, options):

返回单数翻译,参数解释如下:

str:为要翻译的字符串

args:为可选的翻译参数,一个对象,属性名的第一个字符有特殊含义,如果是“@”那么意指属性值是一个原始文本,里面不会存在标签,将先用Drupal.checkPlain(str);处理后再替换;如果是“!”那么代表属性值是安全的,将直接替换;如果既不是“@”也不是“!”,那么将被Drupal.theme('placeholder', args[key])处理后再替换

options:为选项参数,其context属性可以指定翻译上下文

Drupal.formatPlural(count, singular, plural, args, options):

复数翻译,参数含义如下:

count:代表传递的数量

singular:代表单数时的字符串

plural:代表复数时的字符串

args:代表需要替换的额外占位符,和单数翻译中的含义一样,内部属性名不用使用“@count”,该属性名被保留来存放参数count的值

options:代表选项,比如指定上下文,该方法内部使用单数翻译方法

示例如下:

Drupal.t('Enabled');

Drupal.formatPlural(5, '1 new comment', '@count new comments')

以上两个翻译方法定义在核心库“core/drupal”中,因此需要用到翻译的js在库声明中应声明对其的依赖,这样才能使js在其后执行;该库在文档加载阶段被立即执行,因此如果你的js是在异步阶段执行的,也可以不用声明依赖

翻译原理:

翻译数据来自哪里呢?你可能会想是通过ajax获取的,但实际上不是,页面加载了一个翻译数据文件,默认类似如下:

  <script src="/sites/default/files/languages/zh-hans_G0IT0RIYYhJK8iKLwxkqQH25OXr1R25YPE5Q4aeqUkY.js?prmq4x"></script>

该js文件在核心库“core/drupal”之前加载,建立了一个纯数据全局对象:window.drupalTranslations,称为翻译数据对象,包含了系统中已加载过的全部js程序中会用到的所有当前语言的翻译数据,而不仅仅是本次请求加载的js文件,没有被翻译的源字符串不被包含(见下),翻译数据对象格式如下:

strings.上下文.源字符串=翻译字符串

pluralFormula={ 1: 0,default: 1}

没有上下文的翻译源字符串的上下文值为空字符串“""”,属性pluralFormula用于指示当前语言支持多少种复数形式,用单复数分隔符将翻译后字符串分隔后,用该变量指示对应复数字符串在数组中的位置,大多数语言为{ 1: 0,default: 1},意为单数时采用第一个数组元素,其他形式的复数(多于一个时)一律采用默认值,也就是第二个元素,程序内部使用,来源于“locale.plural.formula”服务

翻译数据文件:

翻译数据文件是一个纯粹用于保存数据的js文件,那么她来自哪里呢?这要从locale模块的translations库说起,见:

core/modules/locale/ locale.libraries.yml

该库仅声明了一个js文件:locale.translation.js

实际上该文件并不存在,仅作为一个占位符而已,她将在以下修改钩子中替换成真实的js翻译数据文件:

function locale_js_alter(&$javascript, AttachedAssetsInterface $assets)

产生并返回翻译数据文件涉及多个函数,介绍如下:

function locale_js_alter(&$javascript, AttachedAssetsInterface $assets)

用真实的翻译数据文件替换“locale/translations”库中定义的占位符,并保证在core/misc/drupal.js之前被加载,如果不存在翻译数据文件,那么不加载(删除占位符)。

function locale_js_translate(array $files = [])

返回翻译数据文件的绝对路径(流包装器方式“public://”),如果不存在则返回null,参数$files为本次请求中加载的所有本地js文件(不包括翻译数据占位符文件),是一个索引数组,键值为js文件的绝对路径。该方法将查看是否有新增的js文件,如果有则解析提取里面的翻译源字符串和上下文到数据库翻译相关表中,以供管理员翻译,并重建翻译数据js文件,每个js文件只会被解析一次,理解该方法需要注意以下三个数据的含义:

翻译数据文件默认目录($dir = 'public://' . \Drupal::config('locale.settings')->get('javascript.directory'); ):

翻译数据文件默认储存目录为“public://languages”,也就是:/sites/default/files/languages

系统中已解析的全部js文件(\Drupal::state()->get('system.javascript_parsed') ):

是一个索引数组,保存已被解析过的js文件的绝对路径,另外可包含刷新旗标:如果其中存在“refresh:语言id”这样的键名(值为“waiting”),说明该语言对应的翻译数据文件需要刷新重建

翻译数据文件哈希(\Drupal::state()->get('locale.translation.javascript')

保存各语言的翻译数据文件哈希值,是一个数组,键名为语言id,键值为对应的翻译数据文件的哈希值,来自文件内容的哈希运算,被用于翻译数据文件的文件名:“默认目录/语言id_哈希值.js”如:

/sites/default/files/languages/zh-hans_G0IT0RIYYhJK8iKLwxkqQH25OXr1R25YPE5Q4aeqUkY.js

function _locale_parse_js_file($filepath)

参数为本地js文件的全路径(以core开始),读取并用正则表达式解析这个js文件,将翻译方法Drupal.t和Drupal.formatPlural的参数提取出来,也就是将翻译源字符串和翻译上下文提取出来,然后保存到数据库的翻译数据表中,这样管理员就可以在后台为其添加翻译了,由于是正则表达式解析,在书写js时有限制,见文末补充说明。

function _locale_invalidate_js($langcode = NULL)

在已解析js文件数组中设立某语言的刷新旗标,被设置了刷新旗标的语言,其翻译数据文件将被重建,旗标是在已解析js文件数组中添加键名“refresh:$langcode”,键值为“waiting”,重建后该旗标会被删除。如果没有传递语言参数,将为系统中所有语言设置刷新旗标,如果英语被设置为不可翻译的,在参数为空时不会为英语设置旗标

function _locale_rebuild_js($langcode = NULL)

重建某个语言的翻译数据文件,参数为语言代码,如果没有被传递将为当前语言重建,返回布尔值,表明翻译数据文件重建后是否已经存在可用了(如果不需要翻译数据文件时会返回true)

翻译数据文件中的翻译数据,仅包含本语言的有翻译的数据,且翻译源字符串来源是javascript,也就是从js文件中提取的翻译源字符串,未被加载过的js文件中的翻译不存在;文件名是“语言id_哈希值”,其中哈希值是对文件内容执行以下操作的结果:

\Drupal\Component\Utility\Crypt::hashBase64($data);

哈希值会被保存到状态信息中,见上文。

缓存问题:

翻译数据js文件重建后形成新文件,文件名将改变,原文件会被删除,没有失效任何缓存标签,这对页面缓存有什么影响呢?在drupal内部针对响应对象(整页面,而非局部渲染数组)的缓存有两级:

动态页面缓存(服务id:dynamic_page_cache_subscriber)

这是针对任意用户的动态缓存,在派发请求和响应事件时执行,由于动态页面缓存中被缓存的响应尚未将js占位符替换成js文件,因此翻译数据js文件不论是否重建,均不影响该缓存

匿名页面缓存(服务id:http_middleware.page_cache)

这是针对匿名用户的缓存,在HTTP核心堆栈中执行,翻译数据js文件重建后文件名发生改变,这将导致被缓存的页面无法加载到,因此需要手动失效该缓存

比较大的项目在系统外部经常也会设置缓存,如多级缓存服务器,在这些外部缓存中,如果页面和js文件不是一起缓存的,将会受到重建影响

翻译数据js文件重建操作没有考虑到任何缓存问题,没有失效任何缓存标签,这是一个需要改进的地方,云客已向官方建议,解决办法有两个:

1、在响应事件订阅器“finish_response_subscriber”中为每个可缓存响应设置了'http_response'缓存标签,当发生重建时可失效该标签以使页面正确加载

2、翻译数据js文件采用固定文件名,不使用哈希,这是首选方法,不影响性能

补充:

1、在drupal的前端,如果是引入外部js,那么无法为其提供翻译功能,这并不是说外部无法使用翻译方法,而是外部js使用drupal的翻译函数也可能无法翻译,因为外部js没有经过翻译源字符串数据的提取,所以可能不存在翻译数据,如果系统中已经存在翻译数据了,那么是可以正常使用的。

2、系统在提取js文件的翻译源数据时,采用正则解析方式,因此不能深入理解js的逻辑,在使用翻译方法Drupal.t和Drupal.formatPlural时,参数不能使用变量名,需要采用字面量,否则无法提取源字符串供后端管理员翻译,这仅是无法提取而已,并不妨碍js执行,如果系统已经有翻译数据,那么即便采用变量做参数也是可以执行翻译的;此外这种提取方式即便翻译方法位于注释块中也会被提取,由于这些不完美,云客建议drupal改进提取方法,比如像插件释文一样,在页头添加翻译注释元数据,但这会给js开发者带来额外工作,是否值得有待商榷。

3、翻译数据js文件不可被随意删除,其仅在遇到没有被解析过的js文件时才自动重建,如果不小心删除了,可调用一次失效方法_locale_invalidate_js,或直接调用_locale_rebuild_js($langcode = NULL)方法重建。每个js文件仅会进行一次翻译源字符串的解析提取,如果有变动,比如新加了翻译,那么需要在状态系统中从已解析文件列表中删除,这样就能再次解析提取了

4、在后端的翻译函数中能通过选项参数(键名langcode)指定要翻译的目标语言,前端无此功能,仅能被翻译为当前页面语言,前端的选项参数中仅上下文context参数可用。

我是云客,【云游天下,做客四方】,联系方式见主页,欢迎转载,但须注明出处

云客Drupal源码分析之前端js中的翻译相关推荐

  1. 云客Drupal源码分析之国际化Internationalization:核心翻译系统

    各位<云客drupal源码分析>系列的读者: 本系列一直以每周一篇的速度进行博客原创更新,希望帮助大家理解drupal底层原理,并缩短学习时间,但自<插件系统(上)>主题开始博 ...

  2. 云客Drupal源码分析之Session进阶

    在本系列之前写过<云客Drupal源码分析之Session系统>,但那部分仅仅讲到了drupal会话的基础:Symfony的Session组件 至于drupal怎么去使用这个基础就是本主题 ...

  3. 云客Drupal源码分析之数据库Schema及创建数据表

    本主题是<云客Drupal源码分析之数据库系统及其使用>的补充,便于查询,所以独立成一个主题 讲解数据库系统如何操作Schema(创建修改数据库.数据表.字段:判断它们的存在性等等),以及 ...

  4. 云客Drupal源码分析之配置系统Configuration(一)

    各位<云客drupal源码分析>系列的读者: 本系列一直以每周一篇的速度进行博客原创更新,希望帮助大家理解drupal8底层原理,并缩短学习时间,但自<插件系统(上)>主题开始 ...

  5. 云客Drupal源码分析之节点实体访问控制处理器

    以下内容仅是一个预览,完整内容请见文尾: 本篇讲解节点实体的访问控制,总结了访问检查链,对"域"."授权id"进行了清晰论述(该知识点可能是中文资料第一次提及, ...

  6. 云客Drupal源码分析之类型化数据Typed Data API

    各位<云客drupal源码分析>系列的读者: 本系列一直以每周一篇的速度进行博客原创更新,希望帮助大家理解drupal底层原理,并缩短学习时间,但自<插件系统(上)>主题开始博 ...

  7. 云客Drupal源码分析之插件系统(上)

    各位<云客drupal源码分析>系列的读者: 本系列一直以每周一篇的速度进行博客原创更新,希望帮助大家理解drupal底层原理,并缩短学习时间,但自<插件系统(上)>主题开始博 ...

  8. 云客Drupal源码分析之前言

    Drupal是一个非常优秀的网站系统,可以说她是一个网站应用开发框架,也可以说是一个cms,她在世界范围内被广泛使用,最为人所知的是美国白宫.联合国等知名机构的官方网站使用了她,随着Drupal8的来 ...

  9. 云客Drupal源码分析之实体表单显示EntityFormDisplay

    以下内容仅是一个预览,完整内容请见文尾: 实体的显示分为表单显示和视图显示,前者用于不同情况下的信息输入,后者用于不同情况下的信息展示,本篇很多内容不止用于本篇所讲的表单主题,也是学习drupal视图 ...

最新文章

  1. 启动Genymotion时报错Failed to initialize backend EGL display
  2. SpringBoot2.0.3之quartz集成,不是你想的那样哦!
  3. OKR能够为企业带来什么价值?如何正确制定OKR?
  4. 文巾解题 793. 阶乘函数后 K 个零
  5. 面试题:一条 sql 语句是如何经过 MySQL 的体系结构的?
  6. android碎片化的解决方法,解决 Android 设备碎片化--屏幕适配
  7. HDU 4430 amp; ZOJ 3665 Yukari#39;s Birthday(二分法+枚举)
  8. spring WebSocket详解
  9. 使用 mock 数据在本地运行 SAP Fiori Elements 应用的工作原理
  10. jh锂电保护电路_锂电池过充电、过放电、过流及短路保护电路原理及电路图
  11. docker的swarm介绍
  12. 跨域问题是怎样造成的
  13. android遍历图片,Android获取手机所有图片并显示
  14. 本地创建MYSQL数据库详解
  15. WiFi 转DMX512模块 支持Art-Net sACN RDM DMX
  16. 机械工程师CAD2016
  17. AIS数据修复-三次样条插值法(Cubic spline interpolation)
  18. FMC150-两路250Msps AD、两路500Msps DA FMC子卡模块
  19. 电容笔有必要买吗?双十一性价比高的电容笔推荐
  20. Revit二次开发之族库管理系统

热门文章

  1. 批量写入tidb提高写入效率
  2. xd羽化怎么调_【Cyberpunk】用一加轻松调出赛博朋克风XD
  3. Materials Studio 2021新版本发布|达索系统®
  4. 朴素贝叶斯算法实现垃圾邮件过滤
  5. 月亮代表我的心计算机谱子,月亮代表我的心(超简单) C调钢琴谱
  6. 《Network Warrior中文版(第2版)——思科网络工程师必备手册》一第2章 HUB和交换机2.1 HUB...
  7. MySQL8下载与安装(msi)
  8. android软键盘显现,Android软键盘的显示和隐藏
  9. 爱情智慧:1招让你搞定不愿意付出的男人
  10. adroid studio 飞花令