Winform中英文切换 最效率版

Winform中英文切换 最效率版

前言

公司最近接到欧美订单,需要做相应的软件英文版本,项目即有WPF也有Winform。介绍一下所用方法。

  • WPF项目使用网上推荐的Resx Manager,把所有需要翻译的绑定都定义一个key,然后就是原文和翻译,总体感觉这种方式挺好就是大项目操作太过繁琐。
  • Winform项目使用遍历主窗口及其子控件的所有Text,使用百度API翻译并保存本地,优点是对付大项目够快,缺点是每次切换都要来回遍历效率差点意思。

Winform

先说Winform,参考了winform项目实现国际化(语言切换)功能,该文章使用了作者自定义的sqllite操作封装,需要自行替换。因为是大项目,在此基础上做优化。下面为主要优化项:

  • 使用System.Data.SQLite原始库,复制即用。
  • 添加调用百度翻译API的QPS为8,即每次调用间隔时间约为125毫秒,根据百度文档定义10qps自行调整,未限制会报频繁访问错误,翻译结果为null;
  • 过滤控件Text为数字、字符符号、空、未包含中文的情况,否则保存文件一大堆空和数字符号条目,且浪费API调用次数;
  • 语言选择直接保存在app.config中Language字段,记录语言选择;
  • 考虑可选优化项:存储在sqllite的数据可在启动时读取到内存,避免每次切换都读取数据库,但考虑到语言切换非常用功能,即设置后正常加载后不会再进行切换,暂时搁置。
  • 数据库主键也可以优化为控件Name,包括业务代码方法内的where字句,我这样直接使用name+text主要为项目老项目,重命名返工定义唯一任务量比较大,设计时未定义好不同页面控件Name名称,规范命名为页面名_控件名,使之实现程序内控件name唯一。这样就可使之成为主键,或者最起码Where字句只需要筛选name。
  • 保存到sqllite的方案当然也可以自行实现替换为Json文件。
    public class SqliteDAL
    {
        private readonly string _connectionString;

        public SqliteDAL()
        {
            var config = ConfigManager.Instance;
            _connectionString = string.Format("Data Source={0};Version=3;"
                , config.GetSetting("path", $"{App.Path}\\DataBase\\你的数据库.db")
            );
            InitializeDatabase();
        }

        /// <summary>
        /// 初始化数据库,创建 ControlTexts 表(如果不存在)。
        /// </summary>
        private void InitializeDatabase()
        {
            using (var conn = new SQLiteConnection(_connectionString))
            {
                conn.Open();
                string createTableQuery = @"
                CREATE TABLE IF NOT EXISTS ControlTexts (
                    Id INTEGER PRIMARY KEY AUTOINCREMENT,
                    ControlName TEXT NOT NULL,
                    OriginalText TEXT NOT NULL,
                    TranslatedText TEXT
                )";
                using (var cmd = new SQLiteCommand(createTableQuery, conn))
                {
                    cmd.ExecuteNonQuery();
                }
            }
        }

        /// <summary>
        /// 递归遍历主窗体控件form,并保存其文本(原文和翻译)到数据库
        /// </summary>
        /// <param name="form"></param>
        /// <returns></returns>
        public async Task SaveControlTextsToDatabase(Form form)
        {
            if (form == null) return;

            var controlsData = await CollectControlTexts(form);
            SaveControlTextsToDatabase(controlsData);
        }

        /// <summary>
        /// 递归遍历控件并翻译文本
        /// </summary>
        private async Task<(string ControlName, string OriginalText, string TranslatedText)[]> CollectControlTexts(Control parent)
        {
            var controlDataList = new System.Collections.Generic.List<(string, string, string)>();

            async Task ProcessControl(Control control)
            {
                // 如果是中文字符,进行翻译
                if (!string.IsNullOrWhiteSpace(control.Name) && !string.IsNullOrWhiteSpace(control.Text) && ContainsChinese(control.Text))
                {
                    await Task.Delay(125);
                    string translatedText = await TranslateText(control.Text);
                    if (string.IsNullOrEmpty(translatedText))
                    {
                        Console.WriteLine($"翻译失败:{control.Text}");
                        translatedText = control.Text;
                    }
                    controlDataList.Add((control.Name, control.Text, translatedText));
                }

                foreach (Control child in control.Controls)
                {
                    await ProcessControl(child);
                }
            }

            await ProcessControl(parent);
            return controlDataList.ToArray();
        }

        /// <summary>
        /// 翻译文本
        /// </summary>
        private async Task<string> TranslateText(string originalText)
        {
            var translationResult = await TranslateUtil.TranslatePost(originalText, Language.中文, Language.英语);
            if (translationResult?.ResponseJson != null)
            {
                try
                {
                    using (var jsonDocument = JsonDocument.Parse(translationResult.ResponseJson))
                    {
                        var root = jsonDocument.RootElement;

                        if (root.TryGetProperty("error_code", out _))
                        {
                            return null;
                        }

                        if (root.TryGetProperty("trans_result", out var transResult))
                        {
                            return transResult[0].GetProperty("dst").GetString();
                        }
                    }
                }
                catch (Exception)
                {
                    return null;
                }
            }
            return null;
        }

        /// <summary>
        /// 将控件文本数据保存到 SQLite 数据库
        /// </summary>
        private void SaveControlTextsToDatabase((string ControlName, string OriginalText, string TranslatedText)[] controlsData)
        {
            using (var conn = new SQLiteConnection(_connectionString))
            {
                conn.Open();
                using (var transaction = conn.BeginTransaction())
                {
                    foreach (var (controlName, originalText, translatedText) in controlsData)
                    {
                        string selectQuery = @"SELECT COUNT(*) FROM ControlTexts WHERE ControlName = @ControlName AND OriginalText = @OriginalText";
                        using (var selectCmd = new SQLiteCommand(selectQuery, conn))
                        {
                            selectCmd.Parameters.AddWithValue("@ControlName", controlName);
                            selectCmd.Parameters.AddWithValue("@OriginalText", originalText);
                            long count = (long)selectCmd.ExecuteScalar();

                            if (count > 0)
                            {
                                string updateQuery = @"UPDATE ControlTexts SET TranslatedText = @TranslatedText WHERE ControlName = @ControlName AND OriginalText = @OriginalText";
                                using (var updateCmd = new SQLiteCommand(updateQuery, conn))
                                {
                                    updateCmd.Parameters.AddWithValue("@TranslatedText", translatedText);
                                    updateCmd.Parameters.AddWithValue("@ControlName", controlName);
                                    updateCmd.Parameters.AddWithValue("@OriginalText", originalText);
                                    updateCmd.ExecuteNonQuery();
                                }

                            }
                            else
                            {
                                string insertQuery = @"INSERT INTO ControlTexts (ControlName, OriginalText, TranslatedText) VALUES (@ControlName, @OriginalText, @TranslatedText)";
                                using (var insertCmd = new SQLiteCommand(insertQuery, conn))
                                {
                                    insertCmd.Parameters.AddWithValue("@ControlName", controlName);
                                    insertCmd.Parameters.AddWithValue("@OriginalText", originalText);
                                    insertCmd.Parameters.AddWithValue("@TranslatedText", translatedText);
                                    insertCmd.ExecuteNonQuery();
                                }

                            }
                        }
                    }
                    transaction.Commit();
                }
            }
        }

        /// <summary>
        /// 替换窗体控件的文本为数据库中的翻译文本
        /// </summary>
        public void ReplaceToEnglish(Form form)
        {
            ReplaceControlTexts(form, true);
        }

        /// <summary>
        /// 替换窗体控件的文本为数据库中的原文本
        /// </summary>
        public void ReplaceToChinese(Form form)
        {
            ReplaceControlTexts(form, false);
        }

        private void ReplaceControlTexts(Form form, bool useTranslatedText)
        {
            if (form == null) return;

            using (var conn = new SQLiteConnection(_connectionString))
            {
                conn.Open();
                ReplaceControlTextsRecursive(form, conn, useTranslatedText);
            }
        }

        private void ReplaceControlTextsRecursive(Control parent, SQLiteConnection conn, bool useTranslatedText)
        {

            if (!string.IsNullOrEmpty(parent.Text))
            {
                var controlName = parent.Name;
                var column = useTranslatedText ? "TranslatedText" : "OriginalText";
                var sql = useTranslatedText ? "AND OriginalText = @Text" : $"AND TranslatedText = @Text";
                var query = $"SELECT {column} FROM ControlTexts WHERE ControlName = @ControlName {sql}";

                using (var cmd = new SQLiteCommand(query, conn))
                {
                    cmd.Parameters.AddWithValue("@ControlName", controlName);
                    cmd.Parameters.AddWithValue("@Text", parent.Text);
                    var result = cmd.ExecuteScalar();
                    if (result is string text && !string.IsNullOrEmpty(text))
                    {
                        parent.Text = text;
                    }
                }
            }

            foreach (Control control in parent.Controls)
            {
                if (!string.IsNullOrEmpty(control.Text))
                {
                    var controlName = control.Name;
                    var column = useTranslatedText ? "TranslatedText" : "OriginalText";
                    var sql = useTranslatedText ? "AND OriginalText = @Text" : $"AND TranslatedText = @Text";
                    var query = $"SELECT {column} FROM ControlTexts WHERE ControlName = @ControlName {sql}";

                    using (var cmd = new SQLiteCommand(query, conn))
                    {
                        cmd.Parameters.AddWithValue("@ControlName", controlName);
                        cmd.Parameters.AddWithValue("@Text", control.Text);
                        var result = cmd.ExecuteScalar();
                        if (result is string text && !string.IsNullOrEmpty(text))
                        {
                            control.Text = text;
                        }
                    }
                }

                if (control.HasChildren)
                {
                    ReplaceControlTextsRecursive(control, conn, useTranslatedText);
                }
            }
        }


        // 检查字符串是否包含中文字符
        private bool ContainsChinese(string text)
        {
            return Regex.IsMatch(text, @"[\u4e00-\u9fa5]");
        }
    }

主窗口调用

    #region 多语言

    private void uiSwitch1_ValueChanged(object sender, bool value)
    {
        try
        {
            if (value)
            {
                SwitchLanguage(Language.英语);
            }
            else
            {
                SwitchLanguage(Language.中文);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    private void SwitchLanguage(string language)
    {
        SqliteDAL sqliteDAL = new();

        if (language == Language.英语)
        {
/*            //遍历翻译并保存
            await sqliteDAL.SaveControlTextsToDatabase(this);*/
            sqliteDAL.ReplaceToEnglish(this);
            SaveLanguageSetting(Language.英语);
        }
        else
        {
            sqliteDAL.ReplaceToChinese(this);
            SaveLanguageSetting(Language.中文);
        }
    }
    private void InitLanguageSetting()
    {
        currentLanguage = ConfigurationManager.AppSettings["Language"];
        if (string.IsNullOrEmpty(currentLanguage))
        {
            currentLanguage = Language.中文;  // 默认语言
        }
        if (currentLanguage == Language.英语)
        {
            SwitchLanguage(Language.英语);
        }
    }
    private void SaveLanguageSetting(string lang)
    {
        // 1. 获取当前的配置文件
        Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

        // 2. 修改 appSettings 中的值
        config.AppSettings.Settings["Language"].Value = lang;

        // 3. 保存配置文件
        config.Save(ConfigurationSaveMode.Modified);

        // 4. 强制重新加载配置
        ConfigurationManager.RefreshSection("appSettings");
    }
    #endregion

程序启动初始化选择好时机放置语言初始化方法,完成以上就完成了翻译工作,当然并没有结束,程序多语言还有个大问题是语言界面排版,需要使用缩写缩短翻译+调整控件大小间距。

大家有什么更好的方法,或者优化项欢迎留言讨论!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值