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 Strings
为NO
。多个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
ns = dict(xliffNameSpace='urn:oasis:names:tc:xliff:document:1.2')
excelPath = os.path.join(os.path.expanduser("~"), 'Desktop/localizable.xlsx')
xliffRootPath = os.path.join(os.path.expanduser("~"), 'Desktop/exportLoaclization')
languages = {"Spanish" : "es", "Portuguese" : "pt", "Indonesian" : "id", "Arabic" : "ar", "Japanese" : "ja", "Vietnam" : "vi-VN"}
targetName = "" sourceName = "English"
sheetName = "ios"
def GetDesktopPath(): return os.path.join(os.path.expanduser("~"), 'Desktop')
def readExcel(): if not os.path.exists(excelPath): print('excel路径不存在') return
data = xlrd.open_workbook(excelPath) sheet1 = data.sheet_by_index(0) if sheetName in data.sheet_names(): sheet1 = data.sheet_by_name(sheetName) else: print("sheetName不存在 默认读取第一个")
sourceIndex = -1 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
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) 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
echo "=========开始执行========="
languages=("ar" "en" "es" "id" "ja" "pt" "vi-VN" "zh-Hant" ) outPath="$HOME/Desktop/exportLoaclization" scheme="UDictionary"
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
commandStr="xcodebuild -exportLocalizations -project $scheme.xcodeproj -localizationPath $outPath $exportLanguageStr"
echo $commandStr
eval $commandStr echo "=========Xliff导出完成========="
}
runPython(){ echo "=========执行Python脚本读取Excel========="
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导入完成=========" }
exportLocalizations
runPython
importLocalizations
|
把excel放到桌面,在Terminal中运行shell即可
chmod +x Localization.sh ./Localization.sh
|
附上脚本Git地址:https://github.com/liweican1992/ExcelToXliff