升鲜宝生鲜配送供应链管理系统,辅助开发工具,《多语言自动翻译与导出工具(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#源代码:

   

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

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<stringstring> cache = new Dictionary<stringstring>();

        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<stringstring>>(File.ReadAllText(ConfigFile));

            if (cfg == nullreturn;

            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<stringstring>

            {

                {"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 fromstring 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<stringstring>(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<stringstring> 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<stringstring> 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 fromstring 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 fromstring 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<stringstring>>(File.ReadAllText(CacheFile));

                if (cache == null) cache = new Dictionary<stringstring>();

                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) 进行二次定制。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐