回顾Dart - 过度设计的国际化
起因
我们的App作为To B产品的附加值在供客户使用。在今年5月份时,我们有了来自日本和美国的客户,为了满足客户的使用场景,App要开始完善国际化多语言使用场景。
现有项目分析
- 项目在最初立项时,没有考虑到多语言使用场景,在代码中没有预留key
- UI中非接口加载的数据,都是硬编码在view层
- 项目在搭建底层框架时,依赖了GetX框架,如果要做多语言切换,只要需要处理多语言部分即可。
错误的初步改造
步骤一
生成txt文件:在dart语法中,字符串可以是"xx",也可以是'xx',起初我们全部按照"xx"去扫描项目中的文件,通过人工对比,发现漏掉了'xx'部分的,我们在编写扫描脚本代码时要注意。
步骤二
通过Chatgpt 生成唯一key:通过步骤一
,我们得到了一份txt文件,里面包含了项目中会用到的所有中文,
大概格式如下:
"今天",
"明天",
"你好",
此时该Chatgpt
登场,通过投喂示例,让Chatgpt
帮我们生成了下面一份文件
"today": "今天",
"tomorrow": "明天",
"hello": "你好",
其中today
就是我们项目中要使用的key,得益于GetX的支持,在项目中只要使用"today".tr
,便可根据项目中的配置,展示对应的语言。
步骤三
错误的过度设计:版本在最初迭代中,汉字部分有多处重合,我抱着便于管理字符串的心态,设计了enum的使用场景,代码如下面:
enum XXKey {
case today
case tomorrow
case hello
}
extension XXKeyExtension on XXKey {
String get key {
swith(this) {
case XXKey.today:
return "today";
case XXKey.tomorrow:
return "tomorrow";
case XXKey.hello:
return "hello"
}
}
}
class XXKeyConst {
static get keys = {
"en_US": {
XXKey.today.key: "today",
XXKey.tomorrow.key: "tomorrow",
XXKey.hello.key: "hello",
},
"zh_CN": {
XXKey.today.key: "今天",
XXKey.tomorrow.key: "明天",
XXKey.hello.key: "你好
}
}
}
在view
中,"today".tr
改成使用XXKey.today.key.tr
因为有Chatgpt
的辅助生成,在业务中只要关心view
中的替换逻辑即可。
错误处
可随着版本的迭代与维护,XXKey.today.key.tr
这种使用方式变的异常繁琐。
首先要先整理新迭代中的所有文案,然后在使用Chatgpt
生成如下内容:
/// 新的
case new
case XXKey.new:
return "new";
XXKey.new.key: "new"
XXKey.new.key: "新的"
每个词组都会生成上面四个内容,随着数量的增加,即使是复制粘贴,这份工作也变的越来越难维护。
在使用中,我发现定义的枚举
和extension
中的key
,并不是必须的。我这么做的初衷是为了方便管理,可随着数量的增加,枚举的方式使代码管理变的越来越复杂化。
回归正确的改造
化繁为简,参照正规做法:当我们拿到下面这份内容时,无非是想要它有中文的对应关系和英文的对应关系即可。
"今天",
"明天",
"你好",
通过Chatgpt
,生成了下面内容
/// zh_CN.json
{
"today": "今天",
"tomorrow": "明天",
"hello": "你好",
}
/// en_US.json
{
"today": "today",
"tomorrow": "tomorrow",
"hello": "hello",
}
对比XXKey.today.key.tr
这种用法,在项目中直接使用"today".tr
显然更符合直觉逻辑,参与当前版本迭代的同事们,只需要去关心zh_CN.json
中的key就行,枚举维护的这层工作被省掉,大家在代码合并上也减少了不必要的冲突。
但经过几次迭代,项目中已经存在了大量XXKey.today.key.tr
这种写法,现在重新去修改,显然不符合我们的预期,这也让我之前的努力付之东流,大受打击。
尝试自动化
通过Python脚本去生成对应dart文件:分析XXKey.today.key.tr
,在不采用枚举的情况下,可以是下面这种情况:
class XXKey {
static const String today = "today";
static const String tomorrow = "tomorrow";
static const String hello = "hello";
}
关于.key
的部分,我们也可以通过对String
进行extension
来处理
extension XXKeyString on String {
String get key => this;
}
在项目启动的时机中,将zh_CN.json
与en_US.json
加载到GetX所需要的多语言配置中,现有的XXKey.today.key.tr
还是可以正常工作。
但关于class XXKey
,如果还采取手动管理,这和最初的enum方案大同小异,经过我们对比,zh_CN.json
文件在开发阶段就是比较完整的,en_US.json
中的部分字段,还需要产品或者其他同事翻译后才可以正常使用。
我们通过类似于下面的Python脚本,将class XXKey
自动生成
path = "../../zh_CN.json"
with open(path, mode="r", encoding="utf-8") as file:
result = file.read()
print(result)
# str -> json
result = json.loads(result)
print(result)
# 生成一个dart文件
action = ["class Action {"]
# 便利字典result
for key, value in result.items():
action.append(f" static const String {key} = "{value}";")
action.append("}")
print(action)
with open("action.dart", mode="w", encoding="utf-8") as f:
f.write("\n".join(action))
print("生成成功")
当有新的文案需要添加,我们可以通过Chatgpt生成下面的内容即可
"today": "今天",
"tomorrow": "明天",
"hello": "你好"
在文案生成后,直接拷贝到zh_CN.json
中,通过Python
脚本,class XXKey
也会生成最新的代码。
将上述内容发给产品,他们也可以很方便的进行翻译工作,等待翻译工作结束,我们直接放入到en_US.json
中,因为class XXKey
是根据zh_CN.json
生成,view
中的使用并不会受到影响,开发速度与体验大幅提升。
后续使用
因为历史版本的缘故,XXKey.today.key.tr
这种使用方式我们会兼顾保留,在后续的迭代中,我们会统一使用XXKey.today.tr
在view
中预埋key
。
最后
经过此次改造过程,我也意识到,在进行一项长期工作时,一定要先三思而会后行,多参考一些优秀的设计。
代码最初的设计就是化繁为简,不要去过度设计,让本来简易的程序流程复杂化。
转载自:https://juejin.cn/post/7385003658390192163