news 2026/6/3 21:53:05

多国语言多渠道适配脚本

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
多国语言多渠道适配脚本

思路:工具以各个渠道下的中文文本为基础,去文言表中搜索对应的翻译,并且在当前渠道创建对应的国家文言文件

1、将多国语言excel表放到根目录下,Excel表的结构如下所示

2、代码示例

import java.io.File import org.apache.poi.ss.usermodel.Row import org.apache.poi.ss.usermodel.Sheet import org.apache.poi.ss.usermodel.WorkbookFactory import java.io.PrintStream import javax.xml.parsers.DocumentBuilderFactory /** * ***************************使用说明*************************** * * 思路:工具以各个渠道下的中文文本为基础,去文言表中搜索对 * 应的翻译,并且在当前渠道创建对应的国家文言文件 * * 使用方法: * 1、在项目中导入以下依赖 * implementation 'org.apache.poi:poi:5.2.3' * implementation 'org.apache.poi:poi-ooxml:5.2.3' * * 2、将文言表放在项目的根目录(与项目级build.gradle文件同级), * 确保项目中只有一个后缀为xlsx的文件,无需重命名 * * 3、修改以下变量来指定要翻译的渠道中的文本 * chineseText变量 定义的文言表中的基准中文文本列 * needTranslateCountry变量 定义要翻译的国家语言 * valuesZhDirs 变量 定义要翻译的渠道 * * 4、执行main方法 * * 注意:法语翻译中有“'”符号时,应当在“'"前加转义字符”\“ * 如“’” -> "\'" * * 遇到保存Could not create task ':app:com. * SourceSet with name 'main' not found * 在项目根目录下的 .idea/gradle.xml 文件中添加以下配置: * <GradleProjectSettings> * <option name="delegatedBuild" value="false" /> * </GradleProjectSettings> * */ // XML 文件头部 const val stringTitle = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" // 项目根目录 val projectDir = File(System.getProperty("user.dir")) // 中文 strings 文件的初始目录 val traverseBaseDir = File(System.getProperty("user.dir") + "/app/src") // 文言表翻译的 中文列 标题 const val chineseText = "代码文本" // 需要翻译的语言 val needTranslateCountry = hashMapOf( "英文" to "values" // "香港繁体" to "values-zh-rHK", // "泰文" to "values-th-rTH", // "俄文" to "values-ru-rRU", // "阿拉伯语(右对齐)(开发和测试统一用点击进行的编辑文案)" to "values-ar-rEG", // "拉美西班牙语" to "values-es-rUS", // "挪威语" to "values-nb-rNO", // "印尼语" to "values-in-rID", // "马来西亚语" to "values-ms-rMY", // "越南语" to "values-vi-rVN", // "德语" to "values-de-rDE", // "法语" to "values-fr-rFR", // "意大利语" to "values-it-rIT", // "西班牙语" to "values-es-rES", // "荷兰语" to "values-nl-rNL", // "瑞典" to "values-sv-rSE", // "葡萄牙" to "values-pt-rPT", // "巴西葡萄牙" to "values-pt-rBR" ) fun main() { System.setOut(PrintStream(System.out, true, "UTF-8")) // 查找所有 values-zh-rCN 目录 val valuesZhDirs = traverseBaseDir.walkTopDown() .filter { it.isDirectory && it.name == "values-zh-rCN"} // .filter { it.path.contains("flavor_a") } // .filter { it.path.contains("flavor_b") } // .filter { it.path.contains("flavor_c") } .filter { it.path.contains("main") } // .filter { it.path.contains("flavor_d") } // .filter { it.path.contains("flavor_e") } .toList() if (valuesZhDirs.isEmpty()) { logError("未找到中文目录") return } for (dir in valuesZhDirs) { println("扫描到中文目录: $dir") val stringsFile = File(dir, "strings.xml") if (stringsFile.exists()) { val uniqueStrings = parseStringsXml(stringsFile) if (uniqueStrings.isNotEmpty()) { translateForeignLanguages(dir, uniqueStrings) } } } } /** * 解析 strings.xml 文件 */ fun parseStringsXml(stringsFile: File): List<TextItem> { val uniqueStrings = mutableListOf<TextItem>() try { val document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stringsFile) val stringNodes = document.getElementsByTagName("string") for (i in 0 until stringNodes.length) { val node = stringNodes.item(i) val name = node.attributes.getNamedItem("name").nodeValue val value = node.textContent.trim() val translatableAttr = node.attributes.getNamedItem("translatable") val isIgnore = translatableAttr?.nodeValue == "false" uniqueStrings.add(TextItem(name, value, isIgnore)) } } catch (e: Exception) { logError("解析 strings.xml 文件时发生错误: ${e.message}") } return uniqueStrings } /** * 翻译中文到指定语言 */ fun translateForeignLanguages(parentPath: File, chineseList: List<TextItem>) { val xlsxFile = projectDir.walk().firstOrNull { it.isFile && it.extension == "xlsx" } if (xlsxFile == null) { logError("未找到文言表文件") return } try { xlsxFile.inputStream().use { inputStream -> WorkbookFactory.create(inputStream).use { workbook -> val wenYanSheet = workbook.getSheetAt(0) val headerRow = wenYanSheet.getRow(0) ?: run { logError("Excel 文件缺少标题行") return } val columnIndices = getColumnIndices(headerRow, needTranslateCountry) if (columnIndices.isEmpty()) { logError("未找到匹配的列") return } for ((languageKey, languageDir) in needTranslateCountry) { val chineseColumnIndex = columnIndices[chineseText] ?: continue val languageColumnIndex = columnIndices[languageKey] ?: continue val newDir = createLanguageDirectory(parentPath, languageDir) val translatedList = translateSheet( wenYanSheet, chineseColumnIndex, languageColumnIndex, languageKey, languageDir, newDir.path, chineseList ) createStringsFile(newDir.path, translatedList) } } } } catch (e: Exception) { logError("处理文言表时发生错误: ${e.message}") } } /** * 获取 Excel 表头中对应列的索引 */ private fun getColumnIndices(headerRow: Row, translateMap: Map<String, String>): Map<String, Int> { val columnIndices = mutableMapOf<String, Int>() for (cell in headerRow) { val cellValue = cell.stringCellValue.trim() if (cellValue == chineseText || translateMap.containsKey(cellValue)) { columnIndices[cellValue] = cell.columnIndex } } return columnIndices } /** * 翻译 Excel 表中的内容 */ private fun translateSheet( sheet: Sheet, chineseColumnIndex: Int, languageColumnIndex: Int, languageCountry: String, languageDir: String, newPath: String, chineseList: List<TextItem> ): List<TextItem> { val translationTable = mutableMapOf<String, String?>() for (rowIndex in 1..sheet.lastRowNum) { val row = sheet.getRow(rowIndex) ?: continue val chineseCell = row.getCell(chineseColumnIndex)?.stringCellValue?.trim() val languageCell = row.getCell(languageColumnIndex)?.stringCellValue?.trim() if (!chineseCell.isNullOrEmpty()) { translationTable[chineseCell] = languageCell ?: "" } } return chineseList.map { item -> if (item.isIgnore) { item } else { val translatedText = translationTable[item.value] if (!translatedText.isNullOrEmpty()) { item.copy(value = translatedText) } else { if (translatedText == null) { logError("【警告】:'${item.value}'没有被收录进文言表,需及时添加,当前国家文言($languageCountry(${languageDir}))已设置成中文,当前项目路径为:$newPath") } else { logError("【警告】:'${item.value}'的${languageCountry}(${languageDir})翻译,在文言表${languageColumnIndex + 1}列未找到,已设置成默认中文,当前项目路径为:$newPath") } item } } } } /** * 创建语言目录 */ private fun createLanguageDirectory(parentPath: File, languageDir: String): File { val newDir = File(parentPath.parentFile, languageDir) if (!newDir.exists() && !newDir.mkdirs()) { logError("目录创建失败: ${newDir.path}") } return newDir } /** * 创建 strings.xml 文件 */ private fun createStringsFile(directoryPath: String, translatedList: List<TextItem>) { val file = File(directoryPath, "strings.xml") file.bufferedWriter().use { writer -> writer.write(stringTitle) writer.write("<resources>\n") for (item in translatedList) { if (item.isIgnore) { writer.write(" <string name=\"${item.name}\" translatable=\"false\">${item.value}</string>\n") } else { writer.write(" <string name=\"${item.name}\">${item.value}</string>\n") } } writer.write("</resources>") } println("${file.path} 翻译完成") } /** * 日志输出 */ fun logError(message: String) { val redText = "\u001B[31m$message\u001B[0m" println(redText) } /** * 数据类表示一个文本项 */ data class TextItem(val name: String, val value: String, val isIgnore: Boolean = false)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/3 21:49:16

解密云端几何计算:3种高效集成方法实战指南

解密云端几何计算&#xff1a;3种高效集成方法实战指南 【免费下载链接】compute.rhino3d REST geometry server based on RhinoCommon and headless Rhino 项目地址: https://gitcode.com/gh_mirrors/co/compute.rhino3d Rhino Compute作为基于REST API的无界面几何计算…

作者头像 李华
网站建设 2026/6/3 21:46:39

Arduino光敏互动装置:从传感器原理到“勿扰骷髅”项目实战

1. 项目概述&#xff1a;一个会“生气”的互动骷髅你有没有想过&#xff0c;给一个安静的骷髅赋予生命&#xff0c;让它能对你的“打扰”做出反应&#xff1f;这个想法听起来像是万圣节的恶作剧&#xff0c;但在创客的世界里&#xff0c;把它变成现实并不复杂。今天分享的这个“…

作者头像 李华
网站建设 2026/6/3 21:45:51

告别磁盘混乱:Czkawka文件管理工具实战指南

告别磁盘混乱&#xff1a;Czkawka文件管理工具实战指南 【免费下载链接】czkawka Multi functional app to find duplicates, empty folders, similar images etc. 项目地址: https://gitcode.com/GitHub_Trending/cz/czkawka 你是否曾经面对杂乱无章的文件夹感到无从下…

作者头像 李华
网站建设 2026/6/3 21:45:42

KMS智能激活解决方案:Windows与Office的终极免费激活指南

KMS智能激活解决方案&#xff1a;Windows与Office的终极免费激活指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 在数字办公时代&#xff0c;Windows系统和Office办公软件的激活状态直接影响…

作者头像 李华
网站建设 2026/6/3 21:44:39

AI写论文福音!4款高效AI论文生成工具,解决写论文难题!

是不是对期刊论文的写作感到无比烦恼&#xff1f; 面对成堆的参考文献、繁琐的排版格式和反复的修改&#xff0c;许多学术工作者常常觉得效率低下。别发愁&#xff0c;下面我将为你推荐4款经过实测的AI论文写作工具&#xff0c;它们能够帮助你从文献检索、论文大纲的生成&…

作者头像 李华
网站建设 2026/6/3 21:44:18

ssm学院党员管理系统(10160)

有需要的同学&#xff0c;源代码和配套文档领取&#xff0c;加文章最下方的名片哦 一、项目演示 项目演示视频 二、资料介绍 完整源代码&#xff08;前后端源代码SQL脚本&#xff09;配套文档&#xff08;LWPPT开题报告/任务书&#xff09;远程调试控屏包运行一键启动项目&…

作者头像 李华