licc

心有猛虎 细嗅蔷薇

0%

iOS国际化—XLIFF批量录入脚本(一键操作)

XLIFF(XML Localisation Interchange File Format,即XML本地化交换文件格式)是一种基于XML的交换格式,旨在标准化本地化过程中在工具之间传递可本地化数据的方式,是CAT工具中常用的一种文件格式。XLIFF由结构化信息标准促进组织(OASIS)于2002年标准化,目前规范为2014年8月5日发布的v2.0
                                   ——Wiki


update:升级到Xcode13以后,Export Localizationns 会报错,需要在Build Setting中设置Use Compiler to Extract Swift StringsNO。多个targert的话每个target都设置即可。


Apple对国际化文案的管理也是基于XLIFF的,这几年一直负责海外项目。国际化文案翻译和录入是必不可免的。XLIFF是业内的通用做法。

如果你对XLIFF不了解,可以参考WWDC Session 401:
《Localizing with Xcode 9》

Apple在2018年升级了国际化文案管理方式,从XLIFF升级到了Localization Catalog。不过本质上文案管理还是XLIFF。

具体参考WWDC Session404:
《New Localization Workflows in Xcode 10》

Localization Catalog 解决了XLIFF的单一性,可以让翻译人员根据上下文语境更准确的翻译。



一般来说,正确的方法是从Xcode中生成Localization Catalog,直接把Localization Catalog给到翻译人员,翻译人员根据storyboard或者图片结合上下文,对文案进行翻译,并且录入到XLIFF中。然后我们只需要导入到Xcode中就可以了。


不过现实中一般都是用Excel进行文案管理,特别是云端Excel,可以方便管理也方便安卓使用。之前推动过XLIFF,用了几次每次都要给对方进行培训。成本太高。

在写脚本之前,我们用的是XLiffTool这个工具,可以在Mac的App Store直接下载。比较方人工录入。

但是人工录入耗时耗力还容易出错,特别是我们有十几种国际化语言,每次要录入几千条文案。

所以考虑用Python撸一个脚本。Python对Excel和XML的读写都很友好。也顺带学习下Python3.8的语法。

对Excel的读写可以使用xlrd,指定Excel的路径和要读取的sheet。

update:更新了下python文件,支持批量操作

import keyword
import xlrd
import xml.etree.ElementTree as ET
from xml.dom import minidom
import os

# 解析xliff的nameSpace Xliff版本变更请更改这里
ns = dict(xliffNameSpace='urn:oasis:names:tc:xliff:document:1.2')

# excel地址
excelPath = os.path.join(os.path.expanduser("~"), 'Desktop/localizable.xlsx')
# xliff根目录
xliffRootPath = os.path.join(os.path.expanduser("~"), 'Desktop/exportLoaclization')
# 语言list
languages = {"Spanish" : "es", "Portuguese" : "pt", "Indonesian" : "id", "Arabic" : "ar", "Japanese" : "ja", "Vietnam" : "vi-VN"}

# 目标语言 需要和excel中一致
targetName = ""
sourceName = "English"

# sheetName 从哪个表读取,找不到会默认读取第一个表
sheetName = "ios"

def GetDesktopPath():
return os.path.join(os.path.expanduser("~"), 'Desktop')

def readExcel():

if not os.path.exists(excelPath):
print('excel路径不存在')
return

# if not os.path.exists(xliffPath):
# print('xliff路径不存在')
# return

data = xlrd.open_workbook(excelPath)
# 拿到索引 默认是在sheet0
sheet1 = data.sheet_by_index(0)
if sheetName in data.sheet_names():
sheet1 = data.sheet_by_name(sheetName)
else:
print("sheetName不存在 默认读取第一个")


# 找English索引
sourceIndex = -1
# 确定Index
row_0 = sheet1.row_values(0)
for k in range(len(row_0)):
if row_0[k] == sourceName:
sourceIndex = k
print(sourceName + " 所在索引 " + str(k))

if sourceIndex == -1:
print('未找到English Index')
return


#找target索引
for subLan in languages:
print(subLan)
targetName = subLan
targetIndex = -1
for k in range(len(row_0)):
if row_0[k] == targetName:
targetIndex = k
print(targetName + " 所在索引 " + str(k))
# 遍历字典
dict = {}
for i in range(sheet1.nrows):
row_value = sheet1.cell_value(i, targetIndex)
row_key = sheet1.cell_value(i, sourceIndex)
if row_key == 'English' or row_key == "":
continue
dict[row_key] = row_value
print('excel数据读取完毕,共 ', len(dict), ' 条数据')
print(dict)
# 读取Xliff
writeXliff(dict,targetName)

拿到需要的文案,放到字典里面,然后开始读写XLIFF

查看XLIFF源码可以看到内部结构。注意是有namespace的,需要进行处理

把dict传递给writeXliff函数,把译文填写到target中,没有target要先进行创建。

def writeXliff(dict, targetName):
print('读取'+targetName+'xliff文件')
lan = languages[targetName]
xliffPath = os.path.join(xliffRootPath, lan+'.xcloc/Localized Contents/'+lan+'.xliff')
print(xliffPath)
# 重写nameSpace
ET.register_namespace('', 'urn:oasis:names:tc:xliff:document:1.2')
# 拿到根节点
tree = ET.parse(xliffPath)
root = tree.getroot()
# print(root.tag)
count = 0
for file in root.findall('xliffNameSpace:file', ns):
#print('读取子文件:' + file.attrib['original'])
body = file.find('xliffNameSpace:body', ns)
for unit in body.findall('xliffNameSpace:trans-unit', ns):
source = unit.find('xliffNameSpace:source', ns)
#print(source.text)
target = unit.find('xliffNameSpace:target', ns)
# target不存在就创建 在excel中填写译文 否则写入原文
if target is None:
# 创建target
print("target为空")
node = ET.SubElement(unit, "target")
if source.text in dict:
node.text = dict[source.text]
count += 1
else:
node.text = source.text
print('填补空白译文')
node.tail = '\n\t'
print('create success')
elif source.text in dict:
target.text = dict[source.text]
count += 1

print('写入完成,共写入 ' + str(count) + ' 条数据')
saveXML(root, xliffPath)
print('finish')


def subElement(root, tag, text):
ele = ET.SubElement(root, tag)
ele.text = text
# 格式优化 不再换行
def saveXML(root, filename, indent="\t", newl="", encoding="utf-8"):
rawText = ET.tostring(root)
dom = minidom.parseString(rawText)
with open(filename, 'w') as f:
dom.writexml(f, "", indent, newl, encoding)

saveXML是一个自己写的优化XML格式的方法。

注意存的时候要指定UTF-8编码,要不会有乱码问题。

Xcode导出和导入Xliff文件也可以使用xocdebuild命令做成自动化。
所以可以用shell脚本解决,支持一键导出、录入、导入操作。

#!/bin/sh

# Localization.sh
# UDictionary
#
# Created by 李伟灿 on 2021/8/27.
# Copyright © 2021 com.youdao. All rights reserved.

echo "=========开始执行========="


languages=("ar"
#"de"
"en"
"es"
#"fr"
"id"
#"it"
"ja"
#"ko"
"pt"
#"ru"
"vi-VN"
"zh-Hant"
)
outPath="$HOME/Desktop/exportLoaclization"
scheme="UDictionary"


#导出xliff xcode10后为.xcloc格式文件夹
exportLocalizations(){
echo "=========开始导出Xliff文件========="

if test -e $outPath
then
echo "=========outPath existed, clean outPath"
rm -rf $outPath
fi

exportLanguageStr=""

for subLanguage in "${languages[@]}"
do
tmpStr="-exportLanguage $subLanguage "
exportLanguageStr=$exportLanguageStr$tmpStr
done
#echo $exportLanguageStr

#xcodebuild -exportLocalizations -project UDictionary.xcodeproj -localizationPath $HOME/Desktop/outData2 -exportLanguage en -exportLanguage ja

#拼接命令
commandStr="xcodebuild -exportLocalizations -project $scheme.xcodeproj -localizationPath $outPath $exportLanguageStr"

echo $commandStr

eval $commandStr
echo "=========Xliff导出完成========="

}

runPython(){
echo "=========执行Python脚本读取Excel========="
# path=$HOME/Desktop/ExcelToXliff
# cd $path
chmod +x xliff.py
python3 xliff.py
}

importLocalizations(){
for sub in "${languages[@]}"
do
echo $sub

tmpStr="-localizationPath $outPath/$sub.xcloc "
commandStr="xcodebuild -importLocalizations -project $scheme.xcodeproj $tmpStr"
echo $commandStr
eval $commandStr
done
echo "=========Xliff导入完成========="
}


#导出xliff
exportLocalizations
#读取excel 写入xliff
runPython
#导入xliff
importLocalizations

把excel放到桌面,在Terminal中运行shell即可

chmod +x Localization.sh
./Localization.sh

附上脚本Git地址:https://github.com/liweican1992/ExcelToXliff