软件多语言文案脚本自动化方案
开发高效提速系列目录
- 软件多语言文案脚本自动化方案
软件多语言文案脚本自动化方案
- 背景
- 目标
- 整体方案
- 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…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...