软件多语言文案脚本自动化方案
开发高效提速系列目录
- 软件多语言文案脚本自动化方案
软件多语言文案脚本自动化方案
- 背景
- 目标
- 整体方案
- 1. 创建文案资源文件
- 2. python脚本开发
- 3. Python脚本执行与管理
- 4. 人员职责分配
- PyCharm使用说明
- 1. PyCharm下载
- 2. PyCharm安装配置
- 3. 异常情况解决
- 总结
博客创建时间:2023.05.03
博客更新时间:2023.05.03
以Android studio gradle=7.5,SDKVersion 32来分析讲解。如图文和网上其他资料不一致,可能是别的资料版本较低而已。
背景
在海外项目中多语言的支持是很重要的一部分。在我们的项目中常常需要支持简中、繁体、英、日、韩、西班牙语等十多种语言,当我们进行文案的新增、修改、删除时,需要在软件项目多语言文案资源文件中进行文案Key和文案内容对应的复制粘贴,非常耗时耗力且容易出现疲劳性错误。
目前关于多语言文案的管理,有部分公司的项目管理可能还停留在如下流程中:
-
文案由开发在项目资源文件中进行维护(如Android的strings.xml、IOS的Localizable.strings、PC云端的XXX.ini ),当需要进行文案翻译时,将文案文件语言.xml文件给到产品经理

-
由产品经理经过其他操作对文案进行翻译,提供多语言翻译后的excel文档给到软件开发,此时文案Key和文案内容失去关联关系

-
软件开发根据给到的翻译内容查找到对应的文案Key,然后复制到对应的资源文件中进行文案的更新操作
该流程实现过程中,会出现可能的几点问题:
- 在版本开发迭代期间,频繁的文案新增修改,需要开发人员频繁手动对齐文案和Key的关系,费时费力
- 每一中语言都需要将操作重复进行一遍,语言种类越多耗时越久且手动复制粘贴中容易出现疲劳性错误
目标
为了方便对文案资源进行统一管理,方便产品、开发、测试对文案内容的统一对齐,应该在已有的翻译excel文档的基础上完善文案,对每条文案新增对应的文案Key,同时采用python脚本方法,实现脚本自动化程序更新文案资源,达到提升文案管理和使用效率提升。该方案可以支持Android、IOS、PC云端项目的。
整体方案
该方案的主要思路是:
- 创建好统一管理Android、IOS或PC 云端项目的文案管理excel文件
- 根据文案资源Excel文件进行python脚本的编写,达到执行脚本时能生成不同格式的文案资源文件
- 当文案资源有更新时,在excel文档中进行更新,执行脚本程序完成文案资源的更新

根据思路我将其细分为如下几个细分步骤:
- 建立标准化文案管理文件。一般是用excel文件管理
- 编写Python脚本开发
- Python文件管理与程序触发执行
- 项目组内人员职责分配
1. 创建文案资源文件
多语言文案可以放在某个excel文件中进行管理,一般使用公司的办公软件如钉钉或飞书软件中进行管理。文案资源的格式可以一般同产品沟通协商共同制作,符合自己公司产品的需求即可,我这里给出移动端文案sheet和PC 云端sheet模板样式,云端和移动端因文案差异太大,建议不用放在一起。



注意:多语言的文案翻译根据软件的实际需求进行使用,如软件不支持某种语言如"土耳其语",将其对应列空着即可。
2. python脚本开发
python脚本代码如下
# coding=utf-8
import importlib
import re
import sys
import os
import xlrd2importlib.reload(sys)
# isAndroid = True
ANDROID = "Android"
IOS = "IOS"
PC = "PC"
# 配置文件修改区 START *****/
# 平台类型 Android、IOS、PC 区分大小写
platformType = "Android"# 项目多语言资源文件
excelPath = os.path.expanduser('~/Downloads/多语言文案.xlsx')
# 输出路径
outputPath = "D:/Ken.Luo/res/"# 选择需要导出文案的sheet名
sheetName = "App文案"
# 配置文件修改区 END *****/# 导出文案的类型
# entryType = "Android"
commentColNum = 4
typeColNum = 2
keyColNum = 2
colToStringsMap = None# 切换到iOS
def switchToAndroid():global keyColNum, colToStringsMapkeyColNum = 2# 对应关系元组Android (列数 : 多语言文件)colToStringsMap = [(7, "values/strings.xml"),(5, "values-zh/strings.xml"),(6, "values-zh-TW/strings.xml"),(8, "values-de/strings.xml"),(9, "values-fr/strings.xml"),(10, "values-es/strings.xml"),(11, "values-ja/strings.xml"),(12, "values-ko/strings.xml"),(13, "values-ar/strings.xml"),(14, "values-it/strings.xml"),(15, "values-pt/strings.xml"),(16, "values-tr/strings.xml"),(17, "values-ru/strings.xml")]# 切换到iOS
def switchToIOS():global keyColNum, colToStringsMapkeyColNum = 3colToStringsMap = [(7, "values/Localizable.strings"),(5, "values-zh/Localizable.strings"),(6, "values-zh-TW/Localizable.strings"),(8, "values-de/Localizable.strings"),(9, "values-fr/Localizable.strings"),(10, "values-es/Localizable.strings"),(11, "values-ja/Localizable.strings"),(12, "values-ko/Localizable.strings"),(13, "values-ar/Localizable.strings"),(14, "values-it/Localizable.strings"),(15, "values-pt/Localizable.strings"),(16, "values-tr/Localizable.strings"),(17, "values-ru/Localizable.strings")]def switchToPC():global keyColNum, colToStringsMapkeyColNum = 2colToStringsMap = [(7, "values/strings.ini"),(5, "values-zh/strings.ini"),(6, "values-zh-TW/strings.ini"),(8, "values-de/strings.ini"),(9, "values-fr/strings.ini"),(10, "values-es/strings.ini"),(11, "values-ja/strings.ini"),(12, "values-ko/strings.ini"),(13, "values-ar/strings.ini"),(14, "values-it/strings.ini"),(15, "values-pt/strings.ini"),(16, "values-tr/strings.ini"),(17, "values-ru/strings.ini")]# ******* #def formatValue(originalStr):''' 格式化字符串成为app 程序需要的样子'''print("originalStr:" + originalStr)# excel中的换行符换成 \n转义字符, 文案首尾的换行符认为是翻译人员不小心按出来的,在此去掉originalStr = re.sub(r'\n+$', "", originalStr, re.S)originalStr = re.sub(r'^\n+', "", originalStr, re.S)originalStr = re.sub(r'\n', "\\\\n", originalStr, re.S)# 安卓需要对&进行处理if platformType is ANDROID:# 不处理,待后续直接替换成空格if originalStr != ' ':originalStr = re.sub(r'&', "&", originalStr, re.S)print("")return originalStr### 写多语言文件函数 ###
def write_header(strFile):if platformType is ANDROID:strFile.write("""<?xml version="1.0" encoding="utf-8" ?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">\n""")def write_trailer(strFile):if platformType is ANDROID:strFile.write("\n</resources>")def write_comment(strFile, comment):'''生成注释文案'''if comment != "" and comment != None:if platformType is ANDROID:strFile.write("\t<!-- " + str(comment) + "-->\n")elif platformType is IOS:strFile.write("/** " + str(comment) + " */\n")elif platformType is PC:strFile.write("# " + str(comment) + " \n")def write_key_value(strFile, key, value, defaultValue):# iOS对于value是空使用defaultValueif value == " ":value = " "if not typeColNum is ANDROID and (value is None or value == ""):value = defaultValueif key is None or key == "" or value is None or value == "":return# 处理一个文案多个key的情况keyColValues = key.splitlines()for keyColValue in keyColValues:if platformType is ANDROID:strFile.write("\t<string name=\"" + keyColValue.strip() + "\">" + value.strip() + "</string>\n")elif platformType is IOS:strFile.write("\"" + keyColValue.strip() + "\" = \"" + value.strip() + "\";\n")elif platformType is PC:strFile.write(keyColValue.strip() + " = \"" + value.strip() + "\";\n")# ******* #
workbook = xlrd2.open_workbook(excelPath)
table = workbook.sheet_by_name(sheetName)def writeToFiles():nrows = table.nrows# 打开文件colToFileMap = []for (col, fileName) in colToStringsMap:paths = os.path.split(fileName)print("paths 路径", paths)if len(paths) > 1:pathDir = outputPath + paths[0]print("dir 路径", pathDir)# 输出目录文件夹不存在就创建if not (os.path.exists(pathDir)):os.makedirs(pathDir)file = open(outputPath + fileName, "w+", 1024, "utf-8")colToFileMap.append((col, file))write_header(file)# 导出文案for i in range(2, nrows):print(table)comment = table.cell(i, commentColNum).valuetypeColValue = table.cell(i, typeColNum).valuekeyColValue = table.cell(i, keyColNum).valuedefaultValue = ""# 将一行的各列写入对应的多语言文件for (col, strFile) in colToFileMap:value = table.cell(i, col).valuevalue = formatValue(value)if col == colToFileMap[0][0]:defaultValue = valuewrite_comment(strFile, comment)# if typeColValue.find(entryType) >= 0:write_key_value(strFile, keyColValue, value, defaultValue)# 关闭文件for (_, file) in colToFileMap:write_trailer(file)file.close()if __name__ == '__main__':if platformType is ANDROID:switchToAndroid()writeToFiles()elif platformType is IOS:switchToIOS()writeToFiles()elif platformType is PC:switchToPC()writeToFiles()
3. Python脚本执行与管理
Python脚本的执行需要在电脑中进行触发程序执行,需要电脑有python运行环境,我使用的是Pycharm idea软件,一键安装即可用,和Android studio使用是一样的。后面会专门讲解PyCharm的使用操作。
参数配置
当执行软件安装好后,需要进行部分参数的配置,配置完参数后即可进行程序的执行,生成对应的资源文件。
ANDROID = "Android"
IOS = "IOS"
PC = "PC"
# 配置文件修改区 START *****/
# 平台类型 Android、IOS、PC 区分大小写
platformType = "PC"# 项目多语言资源文件
excelPath = os.path.expanduser('~/Downloads/Remo文案汇总.xlsx')
# 输出路径
outputPath = "D:/Ken.Luo/res88888/"# 选择需要导出文案的sheet名
sheetName = "APP文案"
# 配置文件修改区 END *****/
主要修改的是参数为
- platformType:资源使用的平台,目前定义有Android、IOS、PC云端
- excelPath :excel资源文件的目录地址
- outputPath:脚本执行后的生成的输出位置
- sheetName :APP和PC云端的文案资源我设计是分开放的,所以是两个sheet,需要执行程序时手动选择
执行效果
以Android举例,多语言文案资源是放置在res/value/strings.xml文件中下的。现在我们预计通过执行python脚本程序,实现文案内容自动更新到各语言的strings.xml中。

脚本管理
python脚本是为了减少开发进行文案更新时提效的,当脚本程序开发完毕后考虑后期可能有优化,需要对齐进行持久化管理,所以首先想到的是使用gitee进行管理。https://gitee.com/luofaxin/LanguageRes.git
4. 人员职责分配
在多语言文案管理中,不同角色有自己的任务分配

PyCharm使用说明
在python脚本准备好后,需要通过某种方式实现程序的触发,第一阶段先考虑手动操作pycharm程序执行脚本程序,后期也可以考虑在服务器上进行脚本的自触发。
1. PyCharm下载
手动触发方式需要使用者安装Pycharm 软件,用于执行文案脚本程序。https://www.jetbrains.com/pycharm/download/#section=windows

2. PyCharm安装配置

-
配置.py运行环境


-
配置脚本执行变量
对于.py文件,需要动态的更改路径的配置

-
执行脚本程序
点击执行脚本程序,python脚本将会自动执行,将excel文件生成res/strings.xml文件

3. 异常情况解决
- Python ModuleNotFoundError: No module named ‘xlrd‘ 异常
解决方案


总结
希望这个python脚本自动化方案能帮助大家,多语言文案资源和pyhton脚本我都放到gitee仓库中了,可以下载使用。欢迎大家提出更好的优化建议。
仓库地址:https://gitee.com/luofaxin/LanguageRes.git
相关链接:
- 软件多语言文案脚本自动化方案
扩展链接:
- 项目开发混淆从初识到理解
- 项目开发代码分支管理
博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收藏 ^ _ ^ !
相关文章:
软件多语言文案脚本自动化方案
开发高效提速系列目录 软件多语言文案脚本自动化方案 软件多语言文案脚本自动化方案 背景目标整体方案1. 创建文案资源文件2. python脚本开发3. Python脚本执行与管理4. 人员职责分配 PyCharm使用说明1. PyCharm下载2. PyCharm安装配置3. 异常情况解决 总结 博客创建时间&…...
C++017-C++文件读写应用
文章目录 C017-C文件读写应用C文件读写应用CSP-J目标1. 文件的基本概念、文本文件的基本操作2.文本文件类型与二进制文件类型文本文件类型二进制文件类型二进制查看工具 3.文件重定向、文件读写等操作关闭文件文件操作-写入文本文件文件操作-读取文本文件文件操作-写入二进制文…...
计算机网络 实验二
⭐计网实验专栏,欢迎订阅与关注! ★观前提示:本篇内容为计算机网络实验。内容可能会不符合每个人实验的要求,因此以下内容建议仅做思路参考。 一、实验目的 (1)掌握IP地址的基本结构(网络部分与主机部分的…...
Unity 3D 学习笔记(1)
文章目录 1.Unity 3D 概述2.Unity的安装过程3.Unity 3D 的项目管理4.Unity 3D 中的场景5.Unity 3D 的界面组成 1.Unity 3D 概述 Unity 3D简介:Unity 3D是虚拟现实行业中使用率较高的一款开发引擎,由Unity Technology公司开发。通过Unity,开发…...
P1050 [NOIP2005 普及组] 循环
题目描述 乐乐是一个聪明而又勤奋好学的孩子。他总喜欢探求事物的规律。一天,他突然对数的正整数次幂产生了兴趣。 众所周知,22 的正整数次幂最后一位数总是不断的在重复 2,4,8,6,2,4,8,6…2,4,8,6,2,4,8,6… 我们说 22 的正整数次幂最后一位的循环长度…...
软考算法-排序篇-上
数据排序 一:故事背景二:直接插入排序2.1 概念2.2 画图表示2.3 代码实现2.4 总结提升 三:希尔排序3.1 概念3.2 画图表示3.3 代码实现3.4 总结提升 四:直接选择排序4.1 概念4.2 画图表示4.3 代码实现4.4 总结提升 五:堆…...
总结836
学习目标: 4月(复习完高数18讲内容,背诵21篇短文,熟词僻义300词基础词) 学习内容: 暴力英语:背诵《keep your direction》,默写,英语语法 高等数学:刷题&a…...
ginbuilder 工具快速创建
ginbuilder github 地址 快速创建一个ginweb项目: 目前apps下只有http服务,如果后续有需要的话,会添加上rpc服务,websocket服务后边如果有需要会添加上swagger 创建完成的目录结构 ├── apps │ ├── apis // 所有的apis…...
【Java基础面试宝典】堆、栈、方法区分别都存储了那些内容?wait 和 sleep 方法的区别?
目录 堆、栈、方法区分别都存储了那些内容? 堆(heap) 栈(stack) 方法区(method) 在 java 中 wait 和 sleep 方法的区别? 堆、栈、方法区分别都存储了那些内容? 堆&a…...
古剑飞仙手游Linux系统服务器架设教程
安装宝塔直接运行命令即可。 yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh 搭建环境: centos 7以上系统服务器 宝塔面板安装应用如下: Nginx1.14 mysql5.7 php5.6 1…...
python实战应用讲解-【numpy数组篇】常用函数(十)(附python示例代码)
目录 Python Numpy MaskedArray.ravel()函数 Python Numpy MaskedArray.reshape()函数 Python Numpy MaskedArray.resize()函数 Python Numpy MaskedArray.std()函数 Python Numpy MaskedArray.sum()函数 Python Numpy MaskedArray.swapaxes()函数 Python Numpy MaskedA…...
计算机组成原理(考研408)练习题#2
用于复习408或计算机组成原理期末考试。如有错误请在评论区指出。 So lets start studying with questions! それでは、問題の勉強を始めましょう! 11.某 cache 采用全相联映射,假设 cache 有 3 块,程序运行过程中需要访问的主存块号依 次为…...
Apache POI,springboot中导出excel报表
2. Apache POI 2.1 介绍 Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是,我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。 一般情况下,POI 都是用于操作 Excel 文件。 Apache POI 的应用场景…...
CSS(一)-- 三种样式表
目录 1. 行内样式表 2. 内部样式表 3. 外部样式表(即引入 .css文件)(重点掌握) 1. 行内样式表 行内样式表(内联样式表)是在元素标签内部的 style 属性中设定 CSS 样式。适合于修改简单样式。 <di…...
嵌入式之Samba服务器搭建
在嵌入式系统开发应用平台中,tftp、nfs和samba服务器是最常用的文件传输工具 tftp和nfs是在嵌入式Linux开发环境中经常使用的传输工具 samba则是Linux和Windows之间的文件传输工具。 下面演示在linux上搭建Samba服务器 sudo apt-get install samba chmod -R 77…...
vue3+go——看到了就去学习吧
vue3go——看到了就去学习吧 Vue3.2 Vite Element-Plus 实现最基础的 CRUD1.效果展示【02:36】2.创建项目【03:16】3.添加github管理【04:10】4.引入element-plus【04:21】5.内容布局【08:59】6.布局优化【05:31】7.添加弹窗【09:34】8.ref改$ref【02:47】9.新增数据【09:22】…...
Perf工具统计CPU性能
Perf 性能检测工具 Perf 是一个内置于Linux内核中的工具,用于性能分析和调优。它可以对系统的CPU使用情况、内存使用情况、磁盘I/O、网络I/O等进行监控和分析,并提供了丰富的分析和可视化工具,以帮助用户定位和解决性能问题。perf可以进行函…...
考验大家指针功底的时候到了:请问如何理解 (int*)1 + 1 ?
来,猜猜看,这里的执行结果是什么? 这是今天课上的一道理解题,给大家一点点思考时间。 (心里有答案了再往下滑哦) 5 4 3 2 1 . 答案是,报warning!因为%d不是用来输出指针的哈…...
英语基础-介词
介词 方位介词 in:在…里面 Its in the box. 在盒子里 in my backpack 在背包里 in the tree 长在树上on:在…上面(指与物体表面接触) Its on the box. 在盒子上(和盒子接触) on the floor.在地板上 on the tree.在树上under:在…下面 Its unde…...
Linux进程通信:进程组 会话
1. 进程组 (1)概念:一个或多个进程的集合,也称为“作业”。 (2)父进程创建子进程时,默认属于同一个进程组。进程组ID为组长进程ID。 (3)进程组中只要有一个进程存在&a…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...
API网关Kong的鉴权与限流:高并发场景下的核心实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...
python基础语法Ⅰ
python基础语法Ⅰ 常量和表达式变量是什么变量的语法1.定义变量使用变量 变量的类型1.整数2.浮点数(小数)3.字符串4.布尔5.其他 动态类型特征注释注释是什么注释的语法1.行注释2.文档字符串 注释的规范 常量和表达式 我们可以把python当作一个计算器,来进行一些算术…...
OCC笔记:TDF_Label中有多个相同类型属性
注:OCCT版本:7.9.1 TDF_Label中有多个相同类型的属性的方案 OCAF imposes the restriction that only one attribute type may be allocated to one label. It is necessary to take into account the design of the application data tree. For exampl…...
