自动化生成国际化文件

762 阅读4分钟

一、国际化步骤

  1. 添加相关语言
  2. 创建Localizable.strings、InfoPlist.strings文件
  3. 写入需要国际化的键值对
  4. 通过NSLocalizedString(@"", nil) 调用

二、问题

在实现的过程中遇到的问题:

  1. 项目已写好,怎么获取需要国际化的内容
  2. 获取到国际化的内容怎么一键写入文件
  3. 当有多国语言时,怎么根据Excel提供的翻译快速录入

关于问题1、2 可以通过脚本或shell命令来查找,由于我当时使用的是shell命令,就只讲这一点

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

这个命令有个缺点:只能识别被NSLocalizedString(@"国际化", nil) 包裹的内容,如这里的"国际化",且需提前cd到'en.lproj'同级目录下

三、主菜

接下来就聊聊本次的主要内容,如何自动化写入多国语言

前提条件

  • Localizable.strings里基础语言已准备完毕
  • 工具类:CHCSVParser
  • Excel翻译文件,如下这种:

image.png

Localizable.strings

这个文件可以通过字典的方式读取:

//读取基础语言键值对
NSDictionary * dic = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"source" ofType:@"strings"]];
//获取所有key
_keyArr = dic.allKeys;

获取基础语言所有key,是为了后续写入其他语言时作为依据;因为产品提供的Excel翻译包含的是iOS和Android中的,可能存在有些翻译未使用的情况,所以我们以自己的基础语言为准

CHCSVParser

这是找的一个工具类,用来读取excel.csv文件,将普通的Excel文件另存为以UTF8编码的excel.csv文件即可

image.png

//excel.csv文件路径
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:(fileName.pathExtension.length > 0) ? nil : @"csv"];
//初始化
CHCSVParser *parse = [[CHCSVParser alloc] initWithContentsOfCSVURL:[NSURL fileURLWithPath:filePath]];
parse.delegate = self;

CHCSVParserDelegate 代理如下:

// 开始读取文件
- (void)parserDidBeginDocument:(CHCSVParser *)parser
// 文件读取失败
- (void)parser:(CHCSVParser *)parser didFailWithError:(NSError *)error
// 读取到一行的某一列,field:内容,fieldIndex:列下标(从0开始)
- (void)parser:(CHCSVParser *)parser didReadField:(NSString *)field atIndex:(NSInteger)fieldIndex
// 文件读取完毕
- (void)parserDidEndDocument:(CHCSVParser *)parser
// 开始读取某一行
- (void)parser:(CHCSVParser *)parser didBeginLine:(NSUInteger)recordNumber

至此,我们已能够完成两件事:

  • 基础语言的读取
  • Excel文件的读取

parser:didReadField:atIndex: 代理中,可以对文件内容进行特异化处理后进行保存

- (voi*)parser:(CHCSVParser *)parser didReadField:(NSString *)field atIndex:(NSInteger)fieldIndex {
    if (fieldIndex >= _paeseResults.count) {
        //超出语言个数的部分不处理
        return;

    }

    field = [self removeInvalidStr:field];
    //如果有等号 取等号前面的
    field = [field componentsSeparatedByString:@"="].firstObject;

    if ([field containsString:@"\""]) {
        //如果字符串包含引号,添加\,以保证写入时有转义符
        field = [field stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
    }

    [_paeseResults[fieldIndex] addObject:field];

}

- (NSString *)removeInvalidStr:(NSString *)sourceStr

{
    NSMutableString *aLanguage = [[NSMutableString alloc] initWithString:sourceStr];

    if ([aLanguage containsString:@","] && [aLanguage hasPrefix:@"\""] && [aLanguage hasSuffix:@"\""]) {
        [aLanguage replaceCharactersInRange:NSMakeRange(0, 1) withString:@""];
        [aLanguage deleteCharactersInRange:NSMakeRange(aLanguage.length-1, 1)];
    }

    return [aLanguage copy];
}

parserDidEndDocument: 代理中进行文件写入: image.png

/*
这里我们以英语为基础语言,
中文为注释,法语、西班牙语为需要自动化写入的语言
_paeseResults:二级数组,每个数组元素包含的是每一列的数据
*/
// 第一列:你好、名字,属于注释部分
NSArray * notes = _paeseResults[0];
// 第二列:Hello、Name,为Excel包含的所有英语
NSArray * keyArr = _paeseResults[1];
// _keys为基础语言(英语)文件读出的所有key

for (int i = 2; i < _paeseResults.count; i++) {
        NSArray * array = _paeseResults[i];
        NSMutableString * content = [NSMutableString string];

        for (int j = 0; j < _keys.count; j++) {
            NSString * key = _keys[j];
            if ([key containsString:@"\""]) {
            //基础语言的key可能包含引号,添加转义符
                key = [key stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
            }

            //注释
            NSString * note;
            //写入的值:翻译
            NSString * value;
            if ([keyArr containsObject:key]) {
                NSInteger index = [keyArr indexOfObject:key];
                note = [NSString stringWithFormat:@"/* %@. */",notes[index]];
                value = array[index];
            } else {
                note = @"/* No comment provided by engineer. */";
                value = @"";
            }

            [content appendString:@"\n"];
            [content appendString:note];
            [content appendString:@"\n"];
            [content appendFormat:@"\"%@\"",key];
            [content appendString:@" = "];
            [content appendFormat:@"\"%@\"",value];
            [content appendString:@"\n"];
        }

        [self writeContent:content index:i];
}
    
- (void)writeContent:(NSString *)content index:(int)index {
    NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString * fileName = [NSString stringWithFormat:@"local_%d.strings",index];
    NSString * filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
    BOOL isDir;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager fileExistsAtPath:filePath isDirectory:&isDir]) {
        [fileManager createFileAtPath:filePath contents:nil attributes:nil];
    }

    [content writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

结果如下:

image.png

image.png

至此,基本生成完毕,没有找到翻译的key:写入空值,方便搜索排查 缺点:由于基础语言文件是用字典读取的,获取的key没有顺序,最后写入文件的顺序与基础语言排版顺序不一致,如果基础语言是按字母顺序排序的话,写入前可以先对_keys排序

CHCSVParser

都看到这里了给作者个赞鼓励下呀