解决传统工程中的网页需要批量国际化的问题

遇到这样一个需求,需要将旧系统中的几十个页面做国际化。由于之前是写死中文,所以需要耗费大量的工作将系统中的中文全部提取出来做翻译工作。人工查找不仅费时费力,而且还有可能出现遗漏。站在开发者的角度思考问题,我们应该把抽象的问题具体化,并且将可行的方案用程序实现,将重复工作交给计算机去做,下次遇到同样的问题就可以直接使用上次的工具,避免重复无用的劳动力。

#原始需求分析

由于目前还没有专业的前端团队,并且旧系统需要做国际化,涉及的工程模块比较多,需要将每个html页面中写死的中文修改成国际化支持。
要找出所有页面的中文,将中文罗列出来交给专业人士进行一一对应的翻译,
翻译后要逐个替换为国际化支持的表达式,其中的键名在翻译前就需要确定。
主要的体力劳动在于找出所有的中文和替换掉所有的中文为表达式的操作。

#问题具体化

目前系统页面的写法:中文内容写死在html中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<title>vue-demo</title>
<script src="vue.min.js"></script>
</head>
<body>
<!-- Vue示例 -->
<div id="app">
<p>中文内容</p>
</div>
</body>
<script>
new Vue({
el: '#app'
});
</script>
</html>

使用VueI18n国际化的写法:不同语种对应的词使用相同的键名,页面根据键名取值,值内容由语言环境决定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<html>
<head>
<title>vue-i18n-demo</title>
<script src="vue.min.js"></script>
<script src="vue-i18n.min.js"></script>
</head>
<body>
<div id="app">
<!-- 根据键名输出值 -->
<p>{{ $t("message.title") }}</p>
</div>
</body>
<script>
// 消息定义,一般将不同语种分别放置到不同的文件中
const messages = {
en: {
message: {
title: 'English Content'
}
},
cn: {
message: {
title: '中文内容'
}
}
};
// VueI18n 实例,设置当前语言环境和消息对象
const i18n = new VueI18n({
locale: 'en',
messages: messages,
});

new Vue({
el: '#app',
i18n: i18n
});

</script>
</html>

在实际使用中,我们一般将不同的语种放置到不同的文件中,每个语种都可以请相关专业的人进行翻译。
系统使用时再跟进语言环境加载不同的语言包文件,这点和Android App等移动开发类似,也是采用这种方案。

实现方案

1.将工程下所有的html页面中的所有中文提取到一个文件中(提取内容要忽略掉代码中的注释文件)
2.将提取出来的中文做翻译工作(可以交给专业人士翻译),每个语种对应生成一个js语言变量文件。

1
2
3
4
const en = {
简体中文标题: 'English Title',
简体中文内容: 'English Content',
}

其中js变量文件中自动生成好默认的键,根据源代码中开发者写死的中文作为键的好处是翻译人员可以明确要翻译的内容,翻译后把对应的值赋值上即可。程序中也以当前开发者之前所写的语言作为键,省去人工比对翻译键值对的操作。

3.将html的中文替换为VueI18n模板表达式,变量值为语言文件中对应的键

做完上述工作后就可以根据语言环境的切换自动加载不同的语言文件,页面上的文字值显示的为对应js语言文件中的值。

编码实现

第一步:提取中文到指定文件 TextUtils.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import os
import re

__author__ = '蓝士钦'


# 获取指定目录下指定后缀的文件全路径名,返回数组
def get_file_path_list(path, find_suffix):
files = os.listdir(path) # 得到文件夹下的所有文件名称
s = []
for file in files: # 遍历文件夹
if os.path.splitext(file)[1] == find_suffix:
s.append(path + "/" + file)
return s


# 读取文件,根据匹配规则查找目标,输出到指定文件
def read_file_match_out(match_rule, exclude_rule, file_path_list, write_file):
out_file = open(write_file, "w")
for file_obj in file_path_list:
file = open(file_obj, 'r')
line = file.readline()
while line:
text_list = text_split_list(match_rule, exclude_rule, line)
if not text_list:
pass
else:
for text in text_list:
out_file.write(text + '\n')
line = file.readline()
file.close()
out_file.close()


# 根据规则查找出目标文字,返回数组
def text_split_list(match_rule, exclude_rule, text_str):
for exr in exclude_rule:
text_str = re.sub(exr, "", text_str)
array_list = match_rule.split(text_str)
while '' in array_list:
array_list.remove('')
return array_list


# 根据你的实际情况
if __name__ == '__main__':
# 要匹配的文件路径和文件后缀
file_input_path = '/Users/lanshiqin/Temp/vue-demo'
file_input_list = get_file_path_list(file_input_path, '.html')
# 要匹配的规则
match_re = re.compile('[^\u4E00-\u9FA5]')
# 要排除的规则数组 排除html中的<!-- 和 // 注释
exclude_re = [r'<!--.*$', r'//.*$']
# 匹配到的值要输出到的指定文件
file_out_path = '/Users/lanshiqin/Desktop/zh_cn.txt'
# 匹配输出
read_file_match_out(match_re, exclude_re, file_input_list, file_out_path)

第二步:生成需要翻译的多语种js文件 GenerateUtils.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 生成多语言js文件
def generate_language_js_files(file_input, file_output_list):
for file_output in file_output_list:
file_out = open(file_output + '.js', 'w')
file_out.write('const ' + file_output + ' = {\n')
file = open(file_input, 'r')
line = file.readline()
while line:
line = line.replace("\n", "")
print(line)
file_out.write("\t" + line + ": '" + line + "',\n")
line = file.readline()
file_out.write("}")
file.close()
file_out.close()

if __name__ == '__main__':
# 第一步找到的简体中文
input_file = '/Users/lanshiqin/Desktop/zh_cn.txt'
# 要输出的目标语种文件文件
output_file_list = ['cn', 'en']
# 生成多语言目标文件
generate_language_js_files(input_file, output_file_list)

第三步:替换html中的中文为VueI18n模板表达式,其中的键为语种文件的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 按行读取文件内容转换成数组
def read_file_to_list(file_input):
file = open(file_input, 'r')
line = file.readline()
file_list = []
while line:
line = line.replace('\n', '')
file_list.append(line)
line = file.readline()
return file_list


# 根据中文键名称替换目标文件集合中的中文为VueI18n的模板表达式
def replace_I18n_template(key_list, file_path_list):
for file_obj in file_path_list:
file_obj_temp = ''
for key in key_list:
file = open(file_obj, 'r')
line = file.readline()
while line:
line = line.replace(key, '{{ $t("' + key + '") }}')
file_obj_temp += line
line = file.readline()
file.close()
file_write = open(file_obj, 'w')
file_write.write(file_obj_temp)
file_obj_temp = ''
file_write.close()

# 根据你的实际情况
if __name__ == '__main__':
# 读取指定文件包装成数组
input_file = '/Users/lanshiqin/Desktop/zh_cn.txt'
file_key_list = read_file_to_list(input_file)
# 要匹配的文件路径和文件后缀
file_input_path = '/Users/lanshiqin/Temp/vue-demo'
file_input_list = get_file_path_list(file_input_path, '.html')
# 对匹配的文件进行VueI18n模板表达式替换
replace_I18n_template(file_key_list, file_input_list)

这样一个传统的Vue构建的前端本地化工程,就可以实现一秒钟完成国际化的操作。
如果觉得多语言文件中的键采用中文有不妥,可以继续采用匹配方案,将所有的键都批量重命名。
默认采用中文作为键,这样做的目的是高效,精确,节省时间。
我的考虑是:采用开发者原生语言作为键,也就是我们本土化的人都认识的语言,然后将对应的语言文件交给掌握本土化语言并且掌握目标要翻译语言的这样一个人,这样他就能够精确高效的翻译,翻译后的文件也是程序可直接使用的。

开发者无需关系键名和键值,如果比较强迫症,某一天突然想好了这个键要取什么名字,可以进行匹配替换操作。
这里仅仅针对传统开发的Vue进行I18n的支持,如果需要翻译Android App的多语言文件,或者其他类似平台的多语言文件时,这些平台可能不能使用中文作为键,中文作为键可以认为是一个中间过程,等翻译工作全部完成后,再写个自动化脚本自动批量替换想好的键即可。

一键国际化演示

为了操作方便,我把3个python文件的代码都放在一个vue_language_global.py 文件中了
github地址:https://github.com/lanshiqin/VueLanguageUtils
欢迎 Star&Fork

总结

如果涉及到英语以及其他外语,单纯的国际化支持其实是不够的,除了不能用机器翻译外,还需要考虑每个字词对应的翻译内容长度,原先的UI设计和交互可能就需要改变,因此语言跨度比较大的国际化,一般会单独做一个版本。

本次遇到的需求其实是需要支持繁体中文,每个汉字对应一个繁体字,不涉及语法以及词序的问题。
所以这步翻译操作可以采用调用api的方式完成自动化翻译生成文件。

0%