文章目录
- 前言
- 国际化
- 翻译Api选择
- 小牛测试
 
- 语言选择
- 代码逻辑
- 实体对象
- 翻译帮助类
- 导出模板
- 读取文件
- 翻译
- 测试
- 多语言测试
 
- 综合翻译
- 文件准备
- 测试代码
- 测试结果
 
 
 
- 完整代码
- 实体类
- 翻译帮助类
- 网络帮助类
 
- 最终效果
- 翻译前
- 翻译中
- 翻译后
 
 
- 总结
前言
为了面向更大的市场,国际化是肯定的。我想如果我开发游戏,至少要支持两种语言,英语和中文简体。
国际化
翻译Api选择

 国内Api也有三种选择,百度,有道和小牛。我这里就选小牛了,比较便宜。
小牛测试
小牛翻译平台
使用PostMan测试成功
 
语言选择

 
首先,作为中文为母语,所以我至少支持三种语言。简中,繁中和英语。下面附上小牛的对应参数表
| 语言 | 参数 | 
|---|---|
| 简中 | zh | 
| 繁中 | cht | 
| 英语 | en | 
| 德语 | de | 
| 法语 | fr | 
| 西班牙语 | es | 
| 意大利语 | it | 
| 葡萄牙语 | pt | 
| 俄语 | ru | 
| 日语 | ja | 
| 韩语 | ko | 
吐槽一下,写了这么多,我都快认不清这个【语】字是什么意思了。
原文选择我这里选择英语作为原文,因为这样可以顺便学一下英语。
代码逻辑
这里使用了MiniExcel
 
 Json化方式
 
实体对象
public class TranslateModel
{
    public TranslateModel() { }
    public TranslateModel(string scene,string key,string en) {
        Scene = scene;
        Key = key;
        EN = en;
    }
    public string Scene { get; set; }
    public string Key { get; set; }
    /// <summary>
    /// 英语
    /// </summary>
    public string EN { get; set; }
    /// <summary>
    /// 简体中文
    /// </summary>
    public string ZH { get; set; }
    
    /// <summary>
    /// 繁体中文
    /// </summary>
    public string CHT { get; set; }
    /// <summary>
    /// 德语
    /// </summary>
    public string DE { get; set; }
    /// <summary>
    /// 法语
    /// </summary>
    public string FR { get; set; }
    /// <summary>
    /// 西班牙语
    /// </summary>
    public string ES { get; set; }
    /// <summary>
    /// 意大利语
    /// </summary>
    public string IT { get; set; }
    /// <summary>
    /// 葡萄牙语
    /// </summary>
    public string PT { get; set; }
    /// <summary>
    /// 俄语
    /// </summary>
    public string RU { get; set; }
    /// <summary>
    /// 日语
    /// </summary>
    public string JA { get; set; }
    /// <summary>
    /// 韩语
    /// </summary>
    public string KO { get; set; }
}
翻译帮助类
public class TranslateHelper
{
/// <summary>
/// 存放读取的数据
/// </summary>
public IEnumerable<TranslateModel> Translates { get; private set; }
            /// <summary>
        /// 模板路径
        /// </summary>
        public readonly string TemplateUrl = "Assests/translate_template.xlsx";
        /// <summary>
        /// 读取路径
        /// </summary>
        public readonly string ReadUrl = "Assests/translate_read.xlsx";
        /// <summary>
        /// 翻译导出路径
        /// </summary>
        public readonly string WriteUrl = "Assests/translate_read.xlsx";
    public TranslateHelper()
    {
    }
}
导出模板
/// <summary>
/// 导出模板,如果文件存在则跳过
/// </summary>
public void CreateTemplate()
{
    if (!File.Exists(TemplateUrl))
    {
        var config = new OpenXmlConfiguration()
        {
            TableStyles = TableStyles.None
        };
        var saveTemplate = new List<TranslateModel>();
        saveTemplate.Add(new TranslateModel("Main", "Hello", "Hello World!"));
        MiniExcel.SaveAs(TemplateUrl, saveTemplate,configuration: config);
    }
}

读取文件


 
翻译
这里用一下我的封装
C# HttpClient Get Post简单封装
先来一个Hello world!翻译

public class TranslateHelper
{
    public enum Language { zh, cht, en, de, fr, es, it, pt, ru, ja, ko }
    /// <summary>
    /// 存放读取的数据
    /// </summary>
    public IEnumerable<TranslateModel> Translates { get; private set; }
    /// <summary>
    /// 模板路径
    /// </summary>
    public const string TemplateUrl = "Assests/translate_template.xlsx";
    /// <summary>
    /// 读取路径
    /// </summary>
    public const string ReadUrl = "Assests/translate_read.xlsx";
    /// <summary>
    /// 翻译导出路径
    /// </summary>
    public const string WriteUrl = "Assests/translate_read.xlsx";
    public const string API_KEY = "你的key";
    public const string APPID = "你的ip";
    public const string API_URL = "https://api.niutrans.com/NiuTransServer/translation";
    public TranslateHelper()
    {
    }
    /// <summary>
    /// 导出模板,如果文件存在则跳过
    /// </summary>
    public void CreateTemplate()
    {
        if (!File.Exists(TemplateUrl))
        {
            var config = new OpenXmlConfiguration()
            {
                TableStyles = TableStyles.None
            };
            var saveTemplate = new List<TranslateModel>();
            saveTemplate.Add(new TranslateModel("Main", "Hello", "Hello World!"));
            MiniExcel.SaveAs(TemplateUrl, saveTemplate, configuration: config);
        }
    }
    public void Read()
    {
        Translates = MiniExcel.Query<TranslateModel>(ReadUrl);
    }
    public async Task<string> TranslateFromEN(string en, Language language)
    {
    //这个是我自己封装的,你也可以用微软官方的原生httpclient
        var httpHelper = new MyHttpHelper(API_URL);
        var data = new
        {
            src_text = en,
            from = "en",
            to = language.ToString(),
            apikey = API_KEY,
        };
        var res = await httpHelper.JsonHttpPost<Dictionary<string,string>>("", data, null);
        var str = res["tgt_text"];
        return str;
    }
}
测试
        static void Main(string[] args)
        {
            var helper = new TranslateHelper();
            helper.CreateTemplate();
            Console.WriteLine("开始!");
            Task.Run(async () =>
            {
                var res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.zh);
                Console.WriteLine(res);
            });
            Console.WriteLine("结束!");
            Console.ReadKey();
        }

多语言测试
Task.Run(async () =>
{
    var res = "";
    res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.zh);
    Console.WriteLine(res);
    res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.cht);
    Console.WriteLine(res);
    res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.de);
    Console.WriteLine(res);
    res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.fr);
    Console.WriteLine(res);
    res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.es);
    Console.WriteLine(res);
    res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.it);
    Console.WriteLine(res);
    res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.pt);
    Console.WriteLine(res);
    res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.ru);
    Console.WriteLine(res);
    res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.ja);
    Console.WriteLine(res);
    res = await helper.TranslateFromEN("Hello world!", TranslateHelper.Language.ko);
    Console.WriteLine(res);
});

综合翻译
/// <summary>
/// 读取并写入机翻结果
/// </summary>
/// <returns></returns>
public async Task ReadAndWrite()
{
    if(!File.Exists(ReadUrl))
    {
        Console.WriteLine($"[{ReadUrl}]文件不存在,读取失败");
        return;
    }
    else
    {
        Translates = MiniExcel.Query<TranslateModel>(ReadUrl).ToArray();
    }
    if (File.Exists(WriteUrl))
    {
        Console.WriteLine($"[{WriteUrl}]文件已存在,跳过翻译");
        return ;
    }
    else
    {
        for(var i = 0;i< Translates.Count();i++)
        {
            Console.WriteLine($"翻译进度:[{i}/{Translates.Count()}]");
            Translates[i] = await TranslateAllFromEN(Translates[i]);
        }
        //将数据保留
        MiniExcel.SaveAs(WriteUrl, Translates);
    }
    
}
/// <summary>
/// 从EN翻译全部
/// </summary>
/// <returns></returns>
public async Task<TranslateModel> TranslateAllFromEN(TranslateModel translateModel)
{
    if (translateModel.EN != null && translateModel.EN != "")
    {
        
        var en = translateModel.EN;
        translateModel.ZH = await TranslateFromEN(en, Language.zh);
        translateModel.CHT = await TranslateFromEN(en, Language.cht);
        translateModel.DE = await TranslateFromEN(en, Language.de);
        translateModel.FR = await TranslateFromEN(en, Language.fr);
        translateModel.ES = await TranslateFromEN(en, Language.es);
        translateModel.IT = await TranslateFromEN(en, Language.it);
        translateModel.PT = await TranslateFromEN(en, Language.pt);
        translateModel.RU = await TranslateFromEN(en, Language.ru);
        translateModel.JA = await TranslateFromEN(en, Language.ja);
        translateModel.KO = await TranslateFromEN(en, Language.ko);
        Console.WriteLine(JsonConvert.SerializeObject(translateModel));
    }
    else
    {
        Console.WriteLine($"Scene[{translateModel.Scene}],Key[{translateModel.Key}]的EN为空");
    }
    return translateModel;
    
}
/// <summary>
/// EN单独翻译
/// </summary>
/// <param name="en"></param>
/// <param name="language"></param>
/// <returns></returns>
public async Task<string> TranslateFromEN(string en, Language language)
{
    //防止请求过快,小牛API并发有限制
    await Task.Delay(100);
    //Console.WriteLine(language.ToString());
    var httpHelper = new MyHttpHelper(API_URL);
    var data = new
    {
        src_text = en,
        from = "en",
        to = language.ToString(),
        apikey = API_KEY,
    };
    var res = await httpHelper.JsonHttpPost<Dictionary<string,string>>("", data, null);
    var str = res["tgt_text"];
    return str;
}
文件准备


测试代码
Task.Run(async () =>
{
    await helper.ReadAndWrite();
  
});
测试结果

 
 
完整代码
实体类
public class TranslateModel
{
    public TranslateModel() { }
    public TranslateModel(string scene,string key,string en) {
        Scene = scene;
        Key = key;
        EN = en;
    }
    public string Scene { get; set; }
    public string Key { get; set; }
    /// <summary>
    /// 英语
    /// </summary>
    public string EN { get; set; }
    /// <summary>
    /// 简体中文
    /// </summary>
    public string ZH { get; set; }
    
    /// <summary>
    /// 繁体中文
    /// </summary>
    public string CHT { get; set; }
    /// <summary>
    /// 德语
    /// </summary>
    public string DE { get; set; }
    /// <summary>
    /// 法语
    /// </summary>
    public string FR { get; set; }
    /// <summary>
    /// 西班牙语
    /// </summary>
    public string ES { get; set; }
    /// <summary>
    /// 意大利语
    /// </summary>
    public string IT { get; set; }
    /// <summary>
    /// 葡萄牙语
    /// </summary>
    public string PT { get; set; }
    /// <summary>
    /// 俄语
    /// </summary>
    public string RU { get; set; }
    /// <summary>
    /// 日语
    /// </summary>
    public string JA { get; set; }
    /// <summary>
    /// 韩语
    /// </summary>
    public string KO { get; set; }
}
翻译帮助类
public class TranslateHelper
{
    public enum Language { zh, cht, en, de, fr, es, it, pt, ru, ja, ko }
    /// <summary>
    /// 存放读取的数据
    /// </summary>
    public TranslateModel[] Translates { get; private set; }
    /// <summary>
    /// 模板路径
    /// </summary>
    public const string TemplateUrl = "Assests/translate_template.xlsx";
    /// <summary>
    /// 读取路径
    /// </summary>
    public const string ReadUrl = "Assests/translate_read.xlsx";
    /// <summary>
    /// 翻译导出路径
    /// </summary>
    public const string WriteUrl = "Assests/translate_write.xlsx";
    public const string API_KEY = "你的key";
    public const string APPID = "你的appid";
    public const string API_URL = "https://api.niutrans.com/NiuTransServer/translation";
    public TranslateHelper()
    {
    }
    /// <summary>
    /// 导出模板,如果文件存在则跳过
    /// </summary>
    public void CreateTemplate()
    {
        if (!File.Exists(TemplateUrl))
        {
            var config = new OpenXmlConfiguration()
            {
                TableStyles = TableStyles.None
            };
            var saveTemplate = new List<TranslateModel>();
            saveTemplate.Add(new TranslateModel("Main", "Hello", "Hello World!"));
            MiniExcel.SaveAs(TemplateUrl, saveTemplate, configuration: config);
        }
        else
        {
            Console.WriteLine($"[{TemplateUrl}]文件已存在,跳过创建");
        }
    }
    /// <summary>
    /// 读取并写入机翻结果
    /// </summary>
    /// <returns></returns>
    public async Task ReadAndWrite()
    {
        if(!File.Exists(ReadUrl))
        {
            Console.WriteLine($"[{ReadUrl}]文件不存在,读取失败");
            return;
        }
        else
        {
            Translates = MiniExcel.Query<TranslateModel>(ReadUrl).ToArray();
        }
        if (File.Exists(WriteUrl))
        {
            Console.WriteLine($"[{WriteUrl}]文件已存在,跳过翻译");
            return ;
        }
        else
        {
            for(var i = 0;i< Translates.Count();i++)
            {
                Console.WriteLine($"翻译进度:[{i+1}/{Translates.Count()}]");
                Translates[i] = await TranslateAllFromEN(Translates[i]);
            }
            var config = new OpenXmlConfiguration()
            {
                TableStyles = TableStyles.None
            };
            //将数据保留
            MiniExcel.SaveAs(WriteUrl, Translates,configuration:config);
        }
        
    }
    /// <summary>
    /// 从EN翻译全部
    /// </summary>
    /// <returns></returns>
    public async Task<TranslateModel> TranslateAllFromEN(TranslateModel translateModel)
    {
        if (translateModel.EN != null && translateModel.EN != "")
        {
            
            var en = translateModel.EN;
            translateModel.ZH = await TranslateFromEN(en, Language.zh);
            translateModel.CHT = await TranslateFromEN(en, Language.cht);
            translateModel.DE = await TranslateFromEN(en, Language.de);
            translateModel.FR = await TranslateFromEN(en, Language.fr);
            translateModel.ES = await TranslateFromEN(en, Language.es);
            translateModel.IT = await TranslateFromEN(en, Language.it);
            translateModel.PT = await TranslateFromEN(en, Language.pt);
            translateModel.RU = await TranslateFromEN(en, Language.ru);
            translateModel.JA = await TranslateFromEN(en, Language.ja);
            translateModel.KO = await TranslateFromEN(en, Language.ko);
            Console.WriteLine(JsonConvert.SerializeObject(translateModel));
        }
        else
        {
            Console.WriteLine($"Scene[{translateModel.Scene}],Key[{translateModel.Key}]的EN为空");
        }
        return translateModel;
        
    }
    /// <summary>
    /// EN单独翻译
    /// </summary>
    /// <param name="en"></param>
    /// <param name="language"></param>
    /// <returns></returns>
    public async Task<string> TranslateFromEN(string en, Language language)
    {
        //防止请求过快,小牛API并发有限制
        await Task.Delay(100);
        //Console.WriteLine(language.ToString());
        var httpHelper = new MyHttpHelper(API_URL);
        var data = new
        {
            src_text = en,
            from = "en",
            to = language.ToString(),
            apikey = API_KEY,
        };
        var res = await httpHelper.JsonHttpPost<Dictionary<string,string>>("", data, null);
        var str = res["tgt_text"];
        return str;
    }
}
网络帮助类
public class MyHttpHelper
{
    private string baseUrl;
    /// <summary>
    /// 基础Api
    /// </summary>
    public string BaseUrl
    {
        get
        {
            return baseUrl;
        }
        set
        {
            baseUrl = value;
            MyHttpClient = new HttpClient()
            {
                BaseAddress = new Uri(baseUrl)
            };
        }
    }
    public HttpClient MyHttpClient { get; set; }
    public MyHttpHelper()
    {
    }
    public MyHttpHelper(string baseUrl)
    {
        BaseUrl = baseUrl;
    }
    /// <summary>
    /// 序列化返回值
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="api"></param>
    /// <param name="strParams"></param>
    /// <returns></returns>
    public async Task<T> JsonHttpGet<T>(string api, Dictionary<string, string> strParams)
    {
        var res = await MyHttpGet(api, strParams);
        return await res.Content.ReadFromJsonAsync<T>();
    }
    /// <summary>
    /// 序列化返回值
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="api"></param>
    /// <param name="value"></param>
    /// <param name="strParams"></param>
    /// <returns></returns>
    public async Task<T> JsonHttpPost<T>(string api, object value, Dictionary<string, string> strParams)
    {
        var res = await MyHttpPost(api, value, strParams);
        return await res.Content.ReadFromJsonAsync<T>();
    }
    /// <summary>
    /// 序列化请求
    /// </summary>
    /// <param name="api"></param>
    /// <param name="param"></param>
    /// <returns></returns>
    public async Task<HttpResponseMessage> MyHttpGet(string api, Dictionary<string, string> param)
    {
        string paramStr = DictionaryToParams(param);
        return await MyHttpClient.GetAsync(api + paramStr);
    }
    /// <summary>
    /// 自定义的转发功能
    /// </summary>
    /// <param name="api"></param>
    /// <param name="dataname"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public Task<HttpResponseMessage> MyHttpPost(string api, object value, Dictionary<string, string> strParams)
    {
        return MyHttpClient.PostAsync(api + DictionaryToParams(strParams), new StringContent(JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json"));
    }
    /// <summary>
    /// 字典转参数
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public string DictionaryToParams(Dictionary<string, string> param)
    {
        var res = "";
        if (param != null)
        {
            var list = param.ToList();
            if (list.Count != 0)
            {
                for (var i = 0; i < list.Count; i++)
                {
                    if (i != 0)
                    {
                        res += "&";
                    }
                    res += $"{list[i].Key}={list[i].Value}";
                }
                res = "?" + res;
            }
        }
        return res;
    }
}
最终效果
翻译前

翻译中

翻译后

总结
多语言发布,这样可以让我们的游戏在更多的国家用户使用。虽然我暂时没有这个需求,但是说不定也有别的项目需要多语言的本地化接入。








![[RAM] 图解 RAM 结构原理](https://img-blog.csdnimg.cn/direct/aae86ce08c4d42dca71ff318a174e963.png)










