最近研究了一下本地化,给大家用简单易懂的方式说明我是怎么实现的,使用CSV表格填写翻译,然后在Godot中读取为字典
表格填写
首先,我们表格可以按照下面这种格式填写
id | zh | en | ja | ru | es | de | fr |
apple | 苹果 | apple | リンゴ | яблоко | manzana | Apfel | pomme |
banana | 香蕉 | banana | バナナ | банан | plátano | Banane | banane |
orange | 橙子 | orange | オレンジ | апельсин | naranja | Orange | orange |
大家看表格应该能明白,用第一列作为键,然后再用语言作为第二层的键,就能调用到对应语言的文本了,有一点需要注意,我们保存的时候,格式需要选择为 CSV UTF-8(用逗号分隔)的格式
打开查看,我们可以看到是下面这种格式
有一点需要注意,当我们打开表格的时候是有保护的,这时候不能在这里面修改保存,关闭表格即可
导入表格
我们直接把表格拖入到Godot里面,你可能会注意到Godot报错
Failed to open 'C:\Users\Administrator\Videos\Fruits.csv'.
我们在资源管理器进行移动,Godot中会自动创建一堆.translation格式的翻译文件
我们需要在导入中设置为原样导出,选择后点击重新导入即可
接下来,你会发现文件变成了X号的图标,不要双击打开他,Godot会直接闪退,我们删除这些创建出来的.translation文件就行
读取代码
首先,我们需要创建一个双层字典用来保存翻译文本
[Export] //暴露在编辑器中
public Godot.Collections.Dictionary<string, Godot.Collections.Dictionary<string, string>> language = new Dictionary<string, Dictionary<string, string>>();
private string csv_language_path = "res://Language/Fruits.csv"; //文件路径
public string Now_Language = "zh"; //当前语言
然后我们写一个函数,在初始化的时候把数据写入字典
void Load_CSV_Language()
{
if (!FileAccess.FileExists(csv_language_path)) //判断文件是否存在
{
GD.Print($"{csv_language_path}文件不存在");
return;
}
var file = FileAccess.Open(csv_language_path, FileAccess.ModeFlags.Read);
var header = file.GetLine().Split(","); //获取语言头
while (!file.EofReached()) //循环,直到指向尾部
{
Dictionary<string, string> lang = new Dictionary<string, string>(); //创建内层字典
var line = file.GetLine().Split(","); //获取行(键,语言,语言,语言)
if (string.IsNullOrWhiteSpace(line[0])) continue; //跳过空行,防止越界
for (int i = 1; i < line.Length; i++)
{
lang.Add(header[i], line[i]);
}
language.Add(line[0], lang);
}
file.Close(); //关闭文件流
}
接下来,我们写一个获取对应文本的函数方法即可
public string Get_Language(string key) //返回本地化语言
{
return language[key][Now_Language];
}
单例加载
有一点,在C#中使用Godot的自动加载功能,我们需要额外写一些代码
我这里则是放在了树的加载函数中,大家根据需要进行调整
public static LanguageManager Instance;
public override void _EnterTree()
{
Instance = this;
Load_CSV_Language(); //调用加载字典函数
}
public override void _ExitTree()
{
Instance = null;
}
这样,我们运行场景,就能看到场景的根节点下,多了一个节点
接下来,我们只需要在其他节点中获取即可调用 Get_Language 方法了
public LanguageManager languageManager;
public override void _Ready()
{
languageManager = GetTree().Root.GetNode<LanguageManager>("LanguageManager");
}
切换语言
为了在切换语言的时候更改场景的文本,我们先把所有的设置文本的函数都放在一个函数中,大家可以参照我的示例进行修改
public void SetLanguage()
{
Get_Language();
SetText();
}
public void Get_Language() //获取翻译
{
name = languageManager.Get_Language($"{id}");
}
private void SetText() //设置文本
{
T_name.Text = name;
}
我们修改本地化脚本的 Now_Language 变量
private string _Now_Language = "zh";
public string Now_Language
{
get => _Now_Language;
set
{
_Now_Language = value;
UpdateLanguage(); //修改值时调用
}
}
我们可以给脚本的节点加上分组,使用CallGroup方法调用函数
public void UpdateLanguage() //切换语言
{
GetTree().CallGroup("Text", "SetLanguage"); //通知组,调用设置语言方法
}
这里我创建的组叫“Text”,我们需要在Godot中把组分给节点(或者在脚本中使用AddToGroup方法也可以)
这样,我们修改 Now_Language 变量,文本就会发生改变
结语
通过这种结构化的双层字典方案,我们成功构建了一个动态可扩展的本地化系统。如果项目文本量巨大,我们可以进行异步加载来进行优化,大家可以根据自己的需要进行修改。