在之前的文章中,我不止一次介绍开源国际开发框架——Singleton。对于大型企业应用,其优势确实肉眼可见。然后,这是否意味着只要介绍Singleton框架、国际化问题或国际化中的三层问题完全解决了吗?答案是否定的。在接下来的短文中,我想和大家分享的是,即使在import Singleton之后,由于各种不规范、不推荐的写作和配置,直接引发了各种奇妙的国际问题。
问题
首先,让我们关注字符串读取的问题,现象如下:
不断点击产品中文主页上的导航栏,发现UI大部分内容都已经被本地化,但依然存在少量模块(例如Inventory模块中的一小部分UI 内容仍然是英文的。检查代码中的资源文件,确定模块对应的资源文件string已本地化,无遗漏问题。回到中文导航栏继续点击,Inventory模块的内容莫名其妙地变成了中文,问题不再重现。然而与此同时会产生新的随机事件,原先已经显示中文内容的某些模块(例如Deploy同样的问题也会发生在模块中,UI一小部分内容退回,变成英语。这里需要注意的是,这个问题很随机,没有必要遵循的步骤。
分析
遇到这个问题后,个人的第一反应是,这个问题是一个与多线程相关的国际问题。我们在之前的文章《国际化与多线程》和《国际化与多线程续约》中总结过Python和C#异步法带来的#locale混乱的问题,而现在的问题和之前总结的有很高的相似性。但事实是什么呢?本项目已引入Singleton框架,而Singleton中Lazy Load Module也提供了异步非阻塞load string官方文件明确说明了方案。这里需要特别注意的是,我们必须使用它stream订阅以确保同步使用API加载数据。假如我们不想实现locale切换或订阅流事件路由守卫切换或订阅流事件I18nDataGuard以阻塞的方式加载数据,然后直接使用同步API。
这里的路由守卫实现了访问特定路由组件前的翻译检查功能。如果检查通过,它将被释放,否则它将被阻止。从路由参数中读取简化Singleton配置,将i18nDataGuard配置为路由对象的一部分Lazy Module加载i18n在模块激活和初始化时,准备资源。
// Sample code for I18nDataGuard const routes: Routes = [ { path: '', component: SampleComponent, canActivate: [I18nDataGuard], data: { // 'vipConfig' is specified keyword of vip configuration. // In the simplified configuration of lazy loading module, // only the component name is required, and other fields are optional. vipConfig: { component: 'sample', sourceBundles: [ENGLISH] } } } ]
本案的症结是开发人员既没有这样做stream同时,订阅没有按照文档要求实现i18n路由守卫。
解决方案
在这里,我们试着选择第二种方式——路由守卫来解决问题。Lazy Load Module添加独立性l10n.guard.ts文件。其次,在路由词典中添加i18n路由守卫。
// Sample fixing code // shared/guards/src/lib/ll10n/l10n.guard.ts import { Injectable } from '@angular/core'; import { CanActivate } from '@angular/router'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { L10nService } from '@vmw/ngx-vip'; @Injectable() export class L10nGuard implements CanActivate { constructor(private l10nService: L10nService) {} public canActivate(): Observable<boolean> { return this.l10nService.stream.pipe(map(() => true)); } } // subscriptions/src/lib/subscriptions-routing.module.ts @Injectable() export class SubscriptionGuard implements CanActivate { …. { path: 'list', component: Component1, canActivate: [L10nGuard], }, { path: 'create', component: Component2, canActivate: [SubscriptionGuard, L10nGuard], }, …
再次刷新已经添加canActivate[… L10nGuard]模块,问题消失了,未添加的旧模块问题仍然随机出现。
总结
归根结底,引入这个问题的根源在于开发人员的引入Lazy Load Module必须使用时间stream订阅以确保加载翻译内容,或在需要保护的模块中调用I18nDataGuard,这种不规范或不正确的使用直接导致了本案中的奇怪现象。事实上,这种随意甚至理所当然的情况在日常国际研发过程中并不少见。对于我们这一代的一线编码人员来说,编程语言的不断阅读和准确理解Singleton开源框架对应的文档严格按照不同的模式(如Singleton中的Root和Lazy)编码推荐的方法论是避免千里之堤毁于蚁穴的唯一途径。