iOS国际化

一. 针对两种文件的国际化处理

代码中即.m文件的国际化

首先在你需要进行国际化处理的字符串外面加一层NSLocalizedString,注意中文也是可以的哦

  textfield.text = [NSString stringWithFormat:NSLocalizedString(@"使用帮助", nil)];

NSLocalizedString是一个定义在NSBundle.h中的宏,其用途是寻找当前系统语言对应的Localizable.strings文件中的某个key的值。
  第一个参数是key的名字,第二个参数是对这个“键值对”的注释,在用genstrings工具生成Localizable.strings文件时会自动加上去。

当我们把所有的.m文件都修改好了,就可以动用genstrings工具了

1. 启动终端,进入工程所在目录。
  2. 新建两个目录,推荐放在资源目录下。

目录名会作用到Localizable.strings文件对应的语言,不能写错了。这里zh-Hans指简体中文,注意不能用zh.lproj表示。

    mkdir zh-Hans.lproj
    mkdir en.lproj

3. 生成Localizable.strings文件

    genstrings -o zh-Hans.lproj *.m
    genstrings -o en.lproj *.m

-o <文件夹>,指定生成的Localizable.strings文件放置的目录。
  *.m,扫描所有的.m文件。这里支持的文件还包括.h, .java等。

如果你认为这样就结束了,那就too naive了,实际上,上面genstrings指令只能是该目录下的文件遍历,但不能实现递归遍历,这样在一些大工程里面明显不能满足需求,于是在工程里更普遍的用法是

     find ./ -name *.m | xargs genstrings -o en.lproj

这是传说中shell组合指令find+xargs,find ./ -name *.m 会递归所有.m文件并存在一个数组中,这个数组经由pipe传递给了下一个指令,而xargs会将收到的数组一一分割,并交给genstrings执行。

这样我们就可以得到如下的文件结构


202337379557006.png

202337565962779.png
storyboard的国际化处理

看过文档或者其他介绍的都知道storyboard的国际化非常简单,这里简单介绍一下

  1. PROJECT -> Info -> Localizations 在这里添加你想增加的语种


    230051357213305.png
  2. 将storyboard的inspector栏目的localization项勾上后(图左),project的file navigator那里会多出几个文件(图右)


    230056484717958.png

    230101588463815.png

    230102460182437.png
国际化后续优化
  • 不足之处

到了这里其实关于iOS的国际化已经算是完成了,但你可能也发现一个问题了,上面两种方法全都是一次性生成文件,但我们的项目往往会不断地更新。重新照上面流程跑,生成的string文件会将之前翻译好的文件覆盖掉。

代码的国际化还好,毕竟一般面对新需求我们都会新建新的类和文件,只要把新代码文件脱到一个文件夹下,执行genstrings便可以得到一份对应的本地化文件,然后拿给翻译人员,最后将翻译好的键值对追加在初始的localizable.string文件尾部即可。

而storyboard的国际化便不能如此了,当然新建一个storyboard也是可以的,但如果是小改动我们一般还是会在原storyboard那里添加控件,这时候,原storyboard新增控件的国际化是我们今天要解决的重点。

  • 解决方案

仔细观察storyboard的翻译文件,你会发现这里面也是一个个键值对,key是控件ID+状态,值就是显示文本。假设原来我们就有翻译文件A,添加控件后,我们再执行一次国际化指令,生成文件B,我们拿A和B对比,把B中多出来的键值对插入A文件的尾部,将A中有而B中没有的键值对删掉(即控件被删除),这样我们就算是更新了storyboard的翻译文件了。而这一步操作我们可以借助脚本文件来实现,XCode的Run Script也提供了脚本支持,可以让我们在Build后执行脚本。

  • 脚本代码
#!/usr/bin/env python
# encoding: utf-8

"""
untitled.py

Created by linyu on 2015-02-13.
Copyright (c) 2015 __MyCompanyName__. All rights reserved.
"""

import imp 
import sys 
import os
import glob
import string  
import re  
import time

imp.reload(sys) 
sys.setdefaultencoding(‘utf-8‘) #设置默认编码,只能是utf-8,下面\u4e00-\u9fa5要求的

KSourceFile = ‘Base.lproj/*.storyboard‘

KTargetFile = ‘*.lproj/*.strings‘  

KGenerateStringsFile = ‘TempfileOfStoryboardNew.strings‘

ColonRegex = ur‘["](.*?)["]‘

KeyParamRegex = ur‘["](.*?)["](\s*)=(\s*)["](.*?)["];‘

AnotationRegexPrefix = ur‘/(.*?)/‘

def getCharaset(string_txt):
    filedata = bytearray(string_txt[:4])
    if len(filedata) < 4 :
        return 0
    if  (filedata[0] == 0xEF) and (filedata[1] == 0xBB) and (filedata[2] == 0xBF):
        print ‘utf-8‘
        return 1
    elif (filedata[0] == 0xFF) and (filedata[1] == 0xFE) and (filedata[2] == 0x00) and (filedata[3] == 0x00):
        print ‘utf-32/UCS-4,little endian‘
        return 3
    elif (filedata[0] == 0x00) and (filedata[1] == 0x00) and (filedata[2] == 0xFE) and (filedata[3] == 0xFF):
        print ‘utf-32/UCS-4,big endian‘
        return 3
    elif (filedata[0] == 0xFE) and (filedata[1] == 0xFF):
        print ‘utf-16/UCS-2,little endian‘
        return 2
    elif (filedata[0] == 0xFF) and (filedata[1] == 0xFE):
        print ‘utf-16/UCS-2,big endian‘
        return 2
    else:
        print ‘can not recognize!‘
        return 0

def decoder(string_txt):
    var  = getCharaset(string_txt)
    if var == 1:
        return string_txt.decode("utf-8")
    elif var == 2:
        return string_txt.decode("utf-16")
    elif var == 3:
        return string_txt.decode("utf-32")
    else:
        return string_txt

def constructAnotationRegex(str):
    return AnotationRegexPrefix + ‘\n‘ + str

def getAnotationOfString(string_txt,suffix):
    anotationRegex = constructAnotationRegex(suffix)
    anotationMatch = re.search(anotationRegex,string_txt)
    anotationString = ‘‘
    if anotationMatch:
        match = re.search(AnotationRegexPrefix,anotationMatch.group(0))
        if match:
            anotationString = match.group(0)
    return anotationString

def compareWithFilePath(newStringPath,originalStringPath):
    #read newStringfile 
    nspf=open(newStringPath,"r")
    #newString_txt =  str(nspf.read(5000000)).decode("utf-16")
    newString_txt =  decoder(str(nspf.read(5000000)))
    nspf.close()
    newString_dic = {}
    anotation_dic = {}
    for stfmatch in re.finditer(KeyParamRegex , newString_txt):
        linestr = stfmatch.group(0)
        anotationString = getAnotationOfString(newString_txt,linestr)
        linematchs = re.findall(ColonRegex, linestr)
        if len(linematchs) == 2:
            leftvalue = linematchs[0]
            rightvalue = linematchs[1]
            newString_dic[leftvalue] = rightvalue
            anotation_dic[leftvalue] = anotationString
    #read originalStringfile 
    ospf=open(originalStringPath,"r")
    originalString_txt =  decoder(str(ospf.read(5000000)))
    ospf.close()
    originalString_dic = {}
    for stfmatch in re.finditer(KeyParamRegex , originalString_txt):
        linestr = stfmatch.group(0)
        linematchs = re.findall(ColonRegex, linestr)
        if len(linematchs) == 2:
            leftvalue = linematchs[0]
            rightvalue = linematchs[1]
            originalString_dic[leftvalue] = rightvalue
    #compare and remove the useless param in original string
    for key in originalString_dic:
        if(key not in newString_dic):
            keystr = ‘"%s"‘%key
            replacestr = ‘//‘+keystr
            match = re.search(replacestr , originalString_txt)
            if match is None:
                originalString_txt = originalString_txt.replace(keystr,replacestr)
    #compare and add new param to original string
    executeOnce = 1
    for key in newString_dic:
        values = (key, newString_dic[key])
        if(key not in originalString_dic):
            newline = ‘‘
            if executeOnce == 1:
                timestamp = time.strftime(‘%Y-%m-%d %H:%M:%S‘,time.localtime(time.time()))
                newline = ‘\n//##################################################################################\n‘
                newline +=‘//#           AutoGenStrings            ‘+timestamp+‘\n‘
                newline +=‘//##################################################################################\n‘
                executeOnce = 0
            newline += ‘\n‘+anotation_dic[key]
            newline += ‘\n"%s" = "%s";\n‘%values
            originalString_txt += newline
    #write into origial file
    sbfw=open(originalStringPath,"w")
    sbfw.write(originalString_txt)
    sbfw.close()

def extractFileName(file_path):
    seg = file_path.split(‘/‘)
    lastindex = len(seg) - 1
    return seg[lastindex]

def extractFilePrefix(file_path):
    seg = file_path.split(‘/‘)
    lastindex = len(seg) - 1
    prefix =  seg[lastindex].split(‘.‘)[0]
    return prefix

def generateStoryboardStringsfile(storyboard_path,tempstrings_path):
    cmdstring = ‘ibtool ‘+storyboard_path+‘ --generate-strings-file ‘+tempstrings_path
    if os.system(cmdstring) == 0:
        return 1

def main():
    filePath = sys.argv[1]
    sourceFilePath = filePath + ‘/‘ + KSourceFile 
    sourceFile_list = glob.glob(sourceFilePath)
    if len(sourceFile_list) == 0:
        print ‘error dictionary,you should choose the dic upper the Base.lproj‘
        return
    targetFilePath = filePath + ‘/‘ + KTargetFile
    targetFile_list = glob.glob(targetFilePath)
    tempFile_Path = filePath + ‘/‘ + KGenerateStringsFile
    if len(targetFile_list) == 0:
        print ‘error framework , no .lproj dic was found‘
        return
    for sourcePath in sourceFile_list:
        sourceprefix = extractFilePrefix(sourcePath)
        sourcename = extractFileName(sourcePath)
        print ‘init with %s‘%sourcename
        if generateStoryboardStringsfile(sourcePath,tempFile_Path) == 1:
            print ‘- - genstrings %s successfully‘%sourcename
            for targetPath in targetFile_list:
                targetprefix = extractFilePrefix(targetPath)
                targetname = extractFileName(targetPath) 
                if cmp(sourceprefix,targetprefix) == 0:
                    print ‘- - dealing with %s‘%targetPath
                    compareWithFilePath(tempFile_Path,targetPath)
            print ‘finish with %s‘%sourcename
            os.remove(tempFile_Path)
        else:
            print ‘- - genstrings %s error‘%sourcename




if __name__ == ‘__main__‘:
    main()
  1. 新建一个文件夹RunScript,存放python脚本,放在工程文件的目录下,如图:


    242015326743681.png

    为了直观可以顺便把该文件夹拖入工程中(不做也可以)


    242014003616848.png
  2. Target->Build Phases->New Run Script Phase,在shell里面写入下面指令

  python ${SRCROOT}/${TARGET_NAME}/RunScript/AutoGenStrings.py ${SRCROOT}/${TARGET_NAME}
242021027361377.png
  1. 在Storyboard中添加一个textfield控件,再Bulid一下,成功后我们打开翻译文件,发增加了如下文本


    242033470961496.png
  2. 我们没有必要每次Build都运行一次脚本,所以当你不需要的时候,可以在步骤2选上Run Script Only when installing,这样他就不会跑脚本,具体原因有兴趣的可以自行百度一下。

原文链接:http://www.mamicode.com/info-detail-484956.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容