Styx11/react-intl-linter

【需求】多语言支持

Styx11 opened this issue · 1 comments

描述

项目的国际化在通常情况下有可能需要支持多国语言,比如,本地语言为中文,国际化要支持到英文、日文、繁体中文等。

现状

受限于我在公司维护的项目规模😅只需要覆盖到英文的国际化即可,所以本插件在 demo 阶段只支持中文到英文的翻译,以及中英文的国际化配置文件的更新。

但这样是限制极大的,因为一旦需要支持另一种语言的国际化工作,本插件就完全无法胜任,所以现在急需对多语言国际化做适配,并且在代码层面上能有良好的拓展性,方便之后支持新的语言。

期望

目前我的期望是本插件能够支持到繁体中文和日文,当然语言的支持只受限于翻译接口,而不是代码层面。

代码层面应该具有良好的拓展性,这样在之后开放更多语言的支持时能足够健壮。

多语言支持重构

首先多语言支持的重构思路我分为两个部分,一个是翻译部分,这部分对多语言的反映是「本地语言到国际语言」的一对多的翻译关系;二是配置文件部分,这部分从「封装变化」的角度来看,不管是本地语言还是国际语言,对他们的配置文件的管理应该是一致的,也就是说他们应当是平行的关系并由一个高层次的类进行统一管理。

支持的语言

我使用一个枚举类型 SupportLanguage 来规定插件支持的所有语言

export enum SupportLanguage
{
	ZH = 'zh',    // 中文
	EN = 'en',    // 英语
	CHT = 'cht',  // 繁体中文
	JP = 'jp',    // 日文
}

用户需要选择配置其中一个作为插件的本地语言 LocalLanguage ,以及使用数组来配置想要支持的目标国际语言IntlLanguage[]

配置文件

因为对每种语言的配置文件的操作都是相同的,比如文件的初始化、读取、排序和写入操作。不同的只是他们的文件名称而已。所以我定义了一个 Visitor 类用于管理它所对应的配置文件。然后我提供了一个工厂函数IntlConfigFactory根据传入的工作区路径和配置文件名数组生产对应的 Visitor 数组

在此之上我定义了一个高层次的 Singleton IntlConfigManager 用于统一管理所有的 Visitor,包括所有配置的初始化、读取和写入操作,这里要注意的是我们要写入的文本数组和 Visitor 数组是一一对应的,而且本地配置总是写入本地语言文本

这样配置文件的管理在插件中的关系图是这样的:

截屏2022-03-14 下午2 46 46

我们使用 Visitor 类抽象了所有配置文件并由每个 Visitor 自身去负责文件相关的各种工作,这种模式的好处是 IntlConfigManager 只需要维护一个 Visitor 数组并将所有操作委托给他们就好了,这个单例并不关心文件管理的细节,也不关心代码生成了多少具体的 Visitor,这就将客户代码和用户配置的语言解藕了,并且在我们需要支持新语言的时候足够健壮

翻译

多语言的支持要求翻译模块去构建一个「本地语言」到「国际语言」的一对多关系,例如我们需要支持 N 种国际语言,那么就要去创建 N 个本地语言到该国际语言的翻译 Token ,然后并行地去做请求。翻译模块并没有太多需要做抽象的地方,所以我们只需要使用一个单例去做所有工作:

截屏2022-03-14 下午3 03 23

协作

我们需要了解一下翻译模块和配置管理模块是如何在一起工作的,虽然在多语言的拓展上本插件可以做的很灵活,但还是需要有一些约束来规范用户的行为。

翻译结果如何与配置文件一一对应呢?因为配置文件 Visitor 是一个数组,需要写入的内容也是一个数组。

我们规定:用户配置的"reactIntlLinter.intlLanguage"项目数组和"reactIntlLinter.intlLanguageConfigName"数组要严格地一一对应,前者代表目标国际语言的语言代码,后者代表国际语言对映配置文件的文件名

两者对应后,IntlConfigManagerTranslateManager分别会取这个两个配置,并顺序地进行读取写入。另外因为我们总是将本地文本写入对应的本地配置中,所以这两个内容都位于各自数组的第一项

当然如果用户想要配置本地语言和本地语言配置文件名,也可以通过"reactIntlLinter.localLanguage""reactIntlLinter.localLanguageConfigName" 这两项进行自定义

这两个模块的关系如下图所示:

截屏2022-03-14 下午3 05 31