升鲜宝生鲜配送供应链管理系统,辅助开发工具,《多语言自动翻译与导出工具(WinForms版)》开发文档 及 阿里云机器翻译,数据库Mysql .net 全部源代码

 

首先展示一下工具的界面

 

image

 

 

 

 

 

多语言自动翻译与导出工具(WinForms版)开发文档

一、系统简介

本工具是一款用于自动翻译多语言字段并导出国际化数据的桌面应用,支持从 MySQL 数据库读取中文内容,调用阿里云机器翻译 API 自动生成 繁体中文 (zh-TW)、英文 (en-US)、日文 (ja-JP) 等多语言版本,并将结果同步回数据库或导出为 Excel 文件。

二、功能结构

数据库配置区:输入 MySQL 服务器地址、端口、数据库名、用户名、密码,并可测试连接与保存配置。
阿里云配置区:输入 AccessKeyId 与 AccessKeySecret,选择需要翻译的目标语言(支持多选)。
翻译控制区:一键开始多线程翻译、支持中断与进度显示、自动限流与断点缓存。
导出区:将数据库中的多语言结果导出为 Excel 文件(支持自定义保存路径与打开文件夹)。
缓存机制:所有翻译结果写入本地 cache.json,下次启动自动跳过已翻译内容。

三、界面设计

主界面包含两个主要分组:数据库配置区与阿里云配置区。前者用于设置 MySQL 连接信息,后者用于输入阿里云 API 凭证、勾选目标语言、启动翻译与导出操作。

四、核心逻辑架构

系统包含 MainForm 主窗体、LangRecord 数据实体、TranslateWorker 线程池调度器、CacheManager 缓存管理器与 ExcelExporter 导出模块。其中 MainForm 提供图形界面与交互逻辑,TranslateWorker 负责并发任务分发。

五、数据库结构

sys_language 表字段包括表名、主键ID、字段名、字段值、语言等,主键为(table_name, table_id, field_name, language)。

六、核心功能说明

1. 数据库连接测试:检测服务器可用性并提示详细错误。
2. 翻译逻辑:基于 AlibabaCloud.SDK.Alimt20181012 官方 SDK 调用阿里云机器翻译服务。
3. 只翻译缺失语言:自动跳过已有翻译值的字段,节省调用次数。
4. Excel 导出:支持选择保存路径、防止 BigInt 精度丢失、自动样式化输出。
5. 缓存机制:使用 cache.json 文件缓存已翻译文本以避免重复调用。

七、依赖库

MySql.Data 6.9.12 - MySQL 8.0 连接库
EPPlus 4.5.3.3 - Excel 导出
AlibabaCloud.SDK.Alimt20181012 - 翻译 SDK
Newtonsoft.Json - JSON 缓存文件读写

八、性能优化

使用多线程并发翻译、自动限流机制与缓存避免重复调用,分页查询减少内存压力。

九、常见问题

400 错误请求:语言参数错误
403 无权限:AccessKey 无效或未开通服务
精度丢失:Excel 科学计数法,已通过字符串格式解决

十、部署与运行

1. 安装依赖包(MySql.Data、EPPlus、AlibabaCloud.SDK.Alimt20181012 等)
2. 编译并运行程序。
3. 填写数据库与阿里云配置,测试连接后开始翻译。
4. 翻译完成后点击“导出Excel”。

十一、未来扩展方向

可扩展支持更多语言、批量翻译多个表、导入Excel回写、集成DeepL等翻译服务。

 

主要的C#源代码:

   

using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using OfficeOpenXml;
using Org.BouncyCastle.Asn1.Cmp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Web.Script.Serialization;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace AliyunTranslator40
{
    public partial class MainForm : Form
    {
        private string host = "", port = "3306", database = "", user = "", password = "";
        private string AccessKeyId = "", AccessKeySecret = "";
        private const string ConfigFile = "config.json";
        private const string CacheFile = "cache.json";
        private string ConnStr = "";

        private volatile bool stopRequested = false;
        private Dictionary<string, string> cache = new Dictionary<string, string>();
        private int total = 0, done = 0;
        private Semaphore sema = new Semaphore(4, 4);
        private Stopwatch sw = new Stopwatch();

        public MainForm()
        {
            InitializeComponent();
            LoadConfig();
        }

        #region 配置管理
        private void btnSaveDb_Click(object sender, EventArgs e)
        {
            host = txtHost.Text.Trim();
            port = txtPort.Text.Trim();
            database = txtDb.Text.Trim();
            user = txtUser.Text.Trim();
            password = txtPwd.Text.Trim();
            BuildConnStr();
            SaveConfig();
            Log("✅ 数据库配置已保存。");
        }

        private void btnSaveKey_Click(object sender, EventArgs e)
        {
            AccessKeyId = txtKeyId.Text.Trim();
            AccessKeySecret = txtKeySecret.Text.Trim();
            SaveConfig();
            Log("✅ 阿里云密钥已保存。");
        }

        private void BuildConnStr()
        {
            ConnStr = $"Server={host};Port={port};Database={database};Uid={user};Pwd={password};Charset=utf8mb4;SslMode=None;";
        }

        private void LoadConfig()
        {
            if (!File.Exists(ConfigFile)) return;
            var js = new JavaScriptSerializer();
            var cfg = js.Deserialize<Dictionary<string, string>>(File.ReadAllText(ConfigFile));
            if (cfg == null) return;

            host = cfg.ContainsKey("Host") ? cfg["Host"] : "";
            port = cfg.ContainsKey("Port") ? cfg["Port"] : "3306";
            database = cfg.ContainsKey("Database") ? cfg["Database"] : "";
            user = cfg.ContainsKey("User") ? cfg["User"] : "";
            password = cfg.ContainsKey("Password") ? cfg["Password"] : "";
            AccessKeyId = cfg.ContainsKey("AccessKeyId") ? cfg["AccessKeyId"] : "";
            AccessKeySecret = cfg.ContainsKey("AccessKeySecret") ? cfg["AccessKeySecret"] : "";

            txtHost.Text = host;
            txtPort.Text = port;
            txtDb.Text = database;
            txtUser.Text = user;
            txtPwd.Text = password;
            txtKeyId.Text = AccessKeyId;
            txtKeySecret.Text = AccessKeySecret;
            BuildConnStr();
            Log("✅ 已加载配置。");
        }

        private void SaveConfig()
        {
            var js = new JavaScriptSerializer();
            var cfg = new Dictionary<string, string>
            {
                {"Host", host}, {"Port", port}, {"Database", database},
                {"User", user}, {"Password", password},
                {"AccessKeyId", AccessKeyId}, {"AccessKeySecret", AccessKeySecret}
            };
            File.WriteAllText(ConfigFile, js.Serialize(cfg));
        }

        #endregion

        #region 翻译流程
        private void btnStart_Click(object sender, EventArgs e)
        {
            stopRequested = false;
            ThreadPool.QueueUserWorkItem(_ => RunTranslate());
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            stopRequested = true;
            Log("🛑 已请求停止。");
        }

        private void RunTranslate()
        {
            try
            {
                LoadCache();
                List<LangRecord> list = new List<LangRecord>();
                UpdateStatus("正在读取数据...");

                using (MySqlConnection conn = new MySqlConnection(ConnStr))
                {
                    conn.Open();
                    var cmd = new MySqlCommand("SELECT table_name, table_id, field_name, field_value FROM sys_language WHERE language='zh-CN';", conn);
                    var reader = cmd.ExecuteReader();
                    while (reader.Read())
                    {
                        list.Add(new LangRecord
                        {
                            TableName = reader["table_name"].ToString(),
                            TableId = Convert.ToInt64(reader["table_id"]),
                            FieldName = reader["field_name"].ToString(),
                            FieldValue = reader["field_value"].ToString()
                        });
                    }
                    reader.Close();
                }

                total = list.Count;
                done = 0;
                sw.Restart();
                UpdateStatus($"共 {total} 条数据,开始翻译...");

                foreach (var rec in list)
                {
                    if (stopRequested) break;
                    sema.WaitOne();
                    ThreadPool.QueueUserWorkItem(state => ProcessRecord(rec));
                }

                while (done < total && !stopRequested)
                    Thread.Sleep(500);

                SaveCache();
                ExportToExcel();
                UpdateStatus("✅ 翻译完成。");
            }
            catch (Exception ex)
            {
                Log("❌ 翻译错误:" + ex.Message);
            }
        }

        private void ProcessRecord(LangRecord rec)
        {
            try
            {
                if (stopRequested) return;
                string cn = rec.FieldValue.Trim();
                if (chkEnUs.Checked && !HasTranslation(rec, "en-US"))
                    SaveTranslation(rec, "en-US", TranslateAliyun(cn, "zh", "en"));
                if (chkZhTw.Checked && !HasTranslation(rec, "zh-TW"))
                    SaveTranslation(rec, "zh-TW", TranslateAliyun(cn, "zh", "zh-tw"));

                if (chkJaJp.Checked && !HasTranslation(rec, "ja-JP"))
                    SaveTranslation(rec, "ja-JP", TranslateAliyun(cn, "zh", "ja"));
            }
            catch (Exception ex)
            {
                Log("⚠️ 单条失败: " + ex.Message);
            }
            finally
            {
                Interlocked.Increment(ref done);
                UpdateProgress();
                sema.Release();
            }
        }
        #endregion



        #region 阿里云签名翻译
        private string TranslateAliyun(string text, string from, string to)
        {
            if (string.IsNullOrWhiteSpace(text))
                return text;

            if (string.IsNullOrWhiteSpace(AccessKeyId) || string.IsNullOrWhiteSpace(AccessKeySecret))
            {
                Log("⚠️ 阿里云AccessKey未配置");
                return text;
            }

            try
            {
                string endpoint = "https://mt.aliyuncs.com";
                string version = "2018-10-12";
                string action = "TranslateGeneral";

                // 构建参数(按字母顺序排序,这是阿里云的要求)
                var parameters = new SortedDictionary<string, string>(StringComparer.Ordinal)
                {
                    ["AccessKeyId"] = AccessKeyId,
                    ["Action"] = action,
                    ["Format"] = "JSON",
                    ["FormatType"] = "text",
                    ["Scene"] = "general",
                    ["SignatureMethod"] = "HMAC-SHA1",
                    ["SignatureNonce"] = Guid.NewGuid().ToString(),
                    ["SignatureVersion"] = "1.0",
                    ["SourceLanguage"] = from,
                    ["SourceText"] = text,
                    ["TargetLanguage"] = to,
                    ["Timestamp"] = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"),
                    ["Version"] = version
                };

                // 构建查询字符串
                string queryString = BuildQueryString(parameters);

                // 计算签名
                string signature = CalculateSignature("GET", parameters);

                // 构建最终URL
                string finalUrl = $"{endpoint}/?{queryString}&Signature={Uri.EscapeDataString(signature)}";

                return ExecuteTranslationRequest(finalUrl, text, from, to);
            }
            catch (Exception ex)
            {
                Log($"⚠️ 翻译异常: {ex.Message}");
                return text;
            }
        }

        /// <summary>
        /// 构建查询字符串(URL编码)
        /// </summary>
        private string BuildQueryString(SortedDictionary<string, string> parameters)
        {
            var encodedParams = parameters.Select(p =>
                $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}");

            return string.Join("&", encodedParams);
        }

        /// <summary>
        /// 计算阿里云签名
        /// </summary>
        private string CalculateSignature(string method, SortedDictionary<string, string> parameters)
        {
            // 1. 构建规范化查询字符串
            string canonicalizedQueryString = BuildQueryString(parameters);

            // 2. 构建待签名字符串
            string stringToSign = $"{method}&{Uri.EscapeDataString("/")}&{Uri.EscapeDataString(canonicalizedQueryString)}";

            // 3. 计算HMAC-SHA1签名
            string key = $"{AccessKeySecret}&";
            using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(key)))
            {
                byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
                return Convert.ToBase64String(hash);
            }
        }

        /// <summary>
        /// 执行翻译请求并解析结果
        /// </summary>
        private string ExecuteTranslationRequest(string url, string originalText, string from, string to)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";
            request.Timeout = 15000; // 15秒超时
            request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36";

            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            using (Stream stream = response.GetResponseStream())
            using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
            {
                string responseText = reader.ReadToEnd();

                if (response.StatusCode != HttpStatusCode.OK)
                {
                    Log($"⚠️ API返回错误状态码: {(int)response.StatusCode}");
                    return originalText;
                }

                return ParseTranslationResult(responseText, originalText, from, to, JsonDocument.Parse(responseText));
            }
        }

        /// <summary>
        /// 解析翻译结果
        /// </summary>
        private string ParseTranslationResult(string jsonResponse, string originalText, string from, string to, JsonDocument doc)
        {
            try
            {
                JsonElement root = doc.RootElement;

                // 检查是否有错误
                if (root.TryGetProperty("Code", out JsonElement codeElement))
                {
                    string errorCode = codeElement.GetString();
                    if (!string.IsNullOrEmpty(errorCode) && errorCode != "200")
                    {
                        string errorMessage = root.TryGetProperty("Message", out JsonElement messageElement)
                            ? messageElement.GetString()
                            : "未知错误";
                        Log($"⚠️ 翻译API错误: {errorCode} - {errorMessage}");
                        return originalText;
                    }
                }

                // 提取翻译结果
                if (root.TryGetProperty("Data", out JsonElement dataElement) &&
                    dataElement.TryGetProperty("Translated", out JsonElement translatedElement))
                {
                    string translatedText = translatedElement.GetString();
                    if (!string.IsNullOrEmpty(translatedText))
                    {
                        Log($"🌍 [{from}->{to}] {originalText} => {translatedText}");
                        return translatedText;
                    }
                }

                Log($"⚠️ 无法解析翻译结果: {jsonResponse}");
                return originalText;
            }
            catch (Newtonsoft.Json.JsonException ex)
            {
                Log($"⚠️ JSON解析失败: {ex.Message}");
                return originalText;
            }
        }
        #endregion


        #region 数据保存与导出
        private void SaveTranslation(LangRecord rec, string lang, string val)
        {
            using (MySqlConnection conn = new MySqlConnection(ConnStr))
            {
                conn.Open();
                string sql = "INSERT INTO sys_language (table_name, table_id, field_name, language, field_value) VALUES (@t,@id,@f,@lang,@val) ON DUPLICATE KEY UPDATE field_value=VALUES(field_value)";
                MySqlCommand cmd = new MySqlCommand(sql, conn);
                cmd.Parameters.AddWithValue("@t", rec.TableName);
                cmd.Parameters.AddWithValue("@id", rec.TableId);
                cmd.Parameters.AddWithValue("@f", rec.FieldName);
                cmd.Parameters.AddWithValue("@lang", lang);
                cmd.Parameters.AddWithValue("@val", val);
                cmd.ExecuteNonQuery();
            }
        }

        private void btnTestDb_Click(object sender, EventArgs e)
        {
            try
            {
                // 获取输入
                host = txtHost.Text.Trim();
                port = txtPort.Text.Trim();
                database = txtDb.Text.Trim();
                user = txtUser.Text.Trim();
                password = txtPwd.Text.Trim();
                BuildConnStr();

                // 检查输入有效性
                if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(database) || string.IsNullOrEmpty(user))
                {
                    MessageBox.Show("请输入完整的数据库连接信息(服务器、数据库、用户名)!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }

                // 显示测试中状态
                lblStatus.Text = "状态: 正在连接数据库...";
                lblStatus.Refresh();

                DateTime start = DateTime.Now;

                using (var conn = new MySqlConnection(ConnStr))
                {
                    conn.Open();
                    MySqlCommand cmd = new MySqlCommand("SELECT VERSION();", conn);
                    string version = Convert.ToString(cmd.ExecuteScalar());
                    TimeSpan elapsed = DateTime.Now - start;

                    MessageBox.Show(
                        $"✅ 数据库连接成功!\n\n服务器: {host}\n数据库: {database}\n版本: {version}\n耗时: {elapsed.TotalMilliseconds:F0} ms",
                        "连接成功",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information
                    );

                    Log($"✅ 成功连接数据库 [{database}] (版本: {version}),耗时 {elapsed.TotalMilliseconds:F0} ms");
                    lblStatus.Text = "状态: 数据库连接成功 ✅";
                }
            }
            catch (MySqlException ex)
            {
                string msg;
                switch (ex.Number)
                {
                    case 1045: msg = "用户名或密码错误"; break;
                    case 1042: msg = "无法连接到指定主机"; break;
                    case 1049: msg = "数据库不存在"; break;
                    default: msg = "MySQL 错误: " + ex.Message; break;
                }
                MessageBox.Show($"❌ 连接失败: {msg}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Log("❌ 连接失败: " + msg);
                lblStatus.Text = "状态: 连接失败 ❌";
            }
            catch (Exception ex)
            {
                MessageBox.Show($"❌ 未知错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Log("❌ 未知错误: " + ex.Message);
                lblStatus.Text = "状态: 连接异常 ❌";
            }
        }


        private void btnExport_Click(object sender, EventArgs e)
        {
            ExportToExcel();
        }



        private void ExportToExcel()
        {
            try
            {
                UpdateStatus("正在准备导出 Excel...");

                // 1️⃣ 弹出保存文件对话框
                SaveFileDialog sfd = new SaveFileDialog();
                sfd.Title = "选择导出路径";
                sfd.Filter = "Excel 文件 (*.xlsx)|*.xlsx";
                sfd.FileName = $"translate_result_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx";
                sfd.InitialDirectory = AppDomain.CurrentDomain.BaseDirectory;

                if (sfd.ShowDialog() != DialogResult.OK)
                {
                    Log("⚠️ 用户取消导出。");
                    UpdateStatus("已取消导出");
                    return;
                }

                string file = sfd.FileName;
                Log("📁 导出路径:" + file);
                UpdateStatus("正在导出 Excel,请稍候...");

                // 2️⃣ 创建 Excel
                using (var pkg = new OfficeOpenXml.ExcelPackage())
                {
                    var ws = pkg.Workbook.Worksheets.Add("Translations");

                    // 表头
                    string[] headers = { "表名", "主键ID", "字段名", "中文(zh-CN)", "繁体(zh-TW)", "英文(en-US)", "日文(ja-JP)" };
                    for (int i = 0; i < headers.Length; i++)
                    {
                        ws.Cells[1, i + 1].Value = headers[i];
                        ws.Cells[1, i + 1].Style.Font.Bold = true;
                        ws.Cells[1, i + 1].Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
                        ws.Cells[1, i + 1].Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightGray);
                        ws.Cells[1, i + 1].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
                    }

                    // 3️⃣ 查询数据并写入
                    using (MySqlConnection conn = new MySqlConnection(ConnStr))
                    {
                        conn.Open();
                        string sql = @"
                    SELECT 
                        table_name, 
                        table_id, 
                        field_name,
                        MAX(CASE WHEN language='zh-CN' THEN field_value END) AS zhCN,
                        MAX(CASE WHEN language='zh-TW' THEN field_value END) AS zhTW,
                        MAX(CASE WHEN language='en-US' THEN field_value END) AS enUS,
                        MAX(CASE WHEN language='ja-JP' THEN field_value END) AS jaJP
                    FROM sys_language
                    GROUP BY table_name, table_id, field_name
                    ORDER BY table_name, table_id, field_name;";

                        MySqlCommand cmd = new MySqlCommand(sql, conn);
                        var reader = cmd.ExecuteReader();
                        int row = 2;

                        while (reader.Read())
                        {
                            string tableName = Convert.ToString(reader["table_name"]);
                            string tableId = Convert.ToString(reader["table_id"]);  // ⚠️ BigInt → string
                            string fieldName = Convert.ToString(reader["field_name"]);

                            ws.Cells[row, 1].Value = tableName;
                            ws.Cells[row, 2].Value = tableId;
                            ws.Cells[row, 3].Value = fieldName;
                            ws.Cells[row, 4].Value = Convert.ToString(reader["zhCN"]);
                            ws.Cells[row, 5].Value = Convert.ToString(reader["zhTW"]);
                            ws.Cells[row, 6].Value = Convert.ToString(reader["enUS"]);
                            ws.Cells[row, 7].Value = Convert.ToString(reader["jaJP"]);

                            // 设置 ID 列为文本格式(防止科学计数法)
                            ws.Cells[row, 2].Style.Numberformat.Format = "@";
                            row++;
                        }
                        reader.Close();
                    }

                    // 4️⃣ 自动列宽 + 边框
                    ws.Cells.AutoFitColumns();
                    var range = ws.Cells[1, 1, ws.Dimension.End.Row, ws.Dimension.End.Column];
                    range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                    range.Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                    range.Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
                    range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;

                    // 5️⃣ 保存文件
                    pkg.SaveAs(new FileInfo(file));
                }

                Log("✅ Excel 导出完成:" + file);
                UpdateStatus("✅ Excel 导出完成");

                MessageBox.Show($"✅ 导出成功!\n文件已保存至:\n{file}", "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information);

                // 6️⃣ 询问是否打开文件夹
                if (MessageBox.Show("是否打开文件所在文件夹?", "完成", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    try
                    {
                        System.Diagnostics.Process.Start("explorer.exe", "/select,\"" + file + "\"");
                    }
                    catch { }
                }
            }
            catch (Exception ex)
            {
                Log("❌ 导出失败: " + ex.Message);
                MessageBox.Show("❌ 导出失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                UpdateStatus("❌ 导出失败");
            }
        }



        #endregion

        #region UI 控制
        private void UpdateProgress()
        {
            if (InvokeRequired) { Invoke(new Action(UpdateProgress)); return; }
            double pct = total > 0 ? done * 100.0 / total : 0;
            progressBar.Value = (int)Math.Min(100, pct);
            lblStatus.Text = $"进度: {pct:F1}% ({done}/{total})";
        }

        private void UpdateStatus(string msg)
        {
            if (InvokeRequired) { Invoke(new Action<string>(UpdateStatus), msg); return; }
            lblStatus.Text = "状态: " + msg;
        }

        private void Log(string msg)
        {
            if (InvokeRequired) { Invoke(new Action<string>(Log), msg); return; }
            txtLog.AppendText($"[{DateTime.Now:HH:mm:ss}] {msg}\r\n");
        }

        private void LoadCache()
        {
            if (File.Exists(CacheFile))
            {
                var js = new JavaScriptSerializer();
                cache = js.Deserialize<Dictionary<string, string>>(File.ReadAllText(CacheFile));
                if (cache == null) cache = new Dictionary<string, string>();
                Log($"🧠 加载缓存:{cache.Count} 条");
            }
        }

        private void SaveCache()
        {
            var js = new JavaScriptSerializer();
            File.WriteAllText(CacheFile, js.Serialize(cache));
            Log($"💾 保存缓存:{cache.Count} 条");
        }
        #endregion



        private bool HasTranslation(LangRecord rec, string lang)
        {
            try
            {
                using (MySqlConnection conn = new MySqlConnection(ConnStr))
                {
                    conn.Open();
                    string sql = "SELECT COUNT(*) FROM sys_language WHERE table_name=@t AND table_id=@id AND field_name=@f AND language=@lang AND field_value IS NOT NULL AND field_value <> '';";
                    MySqlCommand cmd = new MySqlCommand(sql, conn);
                    cmd.Parameters.AddWithValue("@t", rec.TableName);
                    cmd.Parameters.AddWithValue("@id", rec.TableId);
                    cmd.Parameters.AddWithValue("@f", rec.FieldName);
                    cmd.Parameters.AddWithValue("@lang", lang);
                    object count = cmd.ExecuteScalar();
                    return Convert.ToInt32(count) > 0;
                }
            }
            catch (Exception ex)
            {
                Log("⚠️ 检查翻译状态出错:" + ex.Message);
                return false;
            }
        }

    }

    public class LangRecord
    {
        public string TableName;
        public long TableId;
        public string FieldName;
        public string FieldValue;
    }
}

 升鲜宝多语言翻译工具V1.0--使用帮助文档

 

多语言自动翻译与导出工具 使用帮助文档

一、软件简介

本软件用于自动翻译系统中 sys_language 表的多语言字段,支持将中文内容批量翻译为繁体中文、英文、日文,并自动写入数据库或导出 Excel。适合用于多语言网站、供应链管理系统、零售POS、多租户SaaS 等项目中批量国际化场景。

二、软件界面说明

主界面分为数据库配置区、阿里云配置区、操作区和日志区。
数据库配置区:输入 MySQL 连接信息。
阿里云配置区:输入 AccessKeyId 与 AccessKeySecret 并勾选翻译目标语言。
操作区:包含测试连接、保存配置、开始翻译、停止和导出Excel按钮。
日志区:显示实时进度与状态。

三、安装与运行

系统要求:Windows 7/10/11,.NET Framework 4.0,MySQL 8.0。
步骤:
1. 启动程序 MultiLangTranslator.exe。
2. 输入数据库信息并点击“测试连接”。
3. 填写阿里云 AccessKey 并勾选语言。
4. 点击“开始翻译”执行任务。
5. 完成后点击“导出Excel”生成文件。

四、翻译功能说明

从 sys_language 表读取中文(zh-CN)字段,通过阿里云 API 翻译为 zh-TW、en-US、ja-JP。支持多线程并发、自动限流(429时降速)和缓存机制。只翻译缺失语言功能可跳过已有翻译记录。

五、Excel 导出功能

点击“导出Excel”后选择保存路径,生成包含中、繁、英、日的多语言对照表。
BigInt 主键转为字符串避免科学计数法问题。
导出完成后可一键打开文件所在目录。

六、日志与进度

日志区实时显示连接信息、翻译进度和错误提示。
状态栏显示当前任务阶段:连接中、翻译中、导出中、完成。

七、配置文件

配置文件 config.json 自动保存数据库和阿里云凭证。
{
  'Host': '127.0.0.1',
  'Port': '3306',
  'Database': 'sxbscm',
  'User': 'root',
  'Password': '123456',
  'AccessKeyId': 'your_key',
  'AccessKeySecret': 'your_secret'
}

八、常见问题

连接失败:检查数据库网络或密码。
400错误请求:检查翻译参数语言代码。
429限流:阿里云请求过多,系统会自动降低并发。
Excel精度丢失:程序已将BigInt强制为字符串。

九、安全提示

妥善保管 AccessKeyId 与 AccessKeySecret。
建议使用权限受限的阿里云子账号。
程序仅调用阿里云翻译接口,不上传数据库内容。

十、版本更新

v1.0:初版发布,支持中/繁/英/日翻译与导出。
v1.1(规划):新增进度条、DeepL支持、任务暂停恢复。

十一、联系与定制

如需扩展功能(新增语言、支持DeepL或Google翻译、企业私有部署),请联系系统开发团队(升鲜宝 余东升  微信:sxbscm2012) 进行二次定制。

posted @ 2025-10-17 00:50  升鲜宝供应链管理系统  阅读(25)  评论(0)    收藏  举报