最近接到了一个老板的需求,要把我们的程序进行国际化,就是新增一个英语的支持。
大致的需求是这个样子的:程序内添加语言切换选项,并在第一次安装的时候自动选择合适的语言(中文系统的手机(无论简体中文繁体中文)一律使用简体中文,其它语言的(无论英语还是法语德语)都默认使用英文)。
其实如果只是添加多语言支持,不再应用内切换语言,苹果的开发就能很好的支持了。就是多了这个应用内切换语言(像微信那样),逻辑就复杂了很多。
因为之前的时候字符串都是写死在程序中的,所以不可能大范围的更改。又要涉及到语言的切换,所以,只能用一个折中的方法,写个宏去处理。
实现的逻辑大约是这个样子的:userdefaults存一个用户设置的语言,如果没有存储就根据系统的语言去做逻辑判断,最终得到确定使用哪种语言然后再去读取相应的语言文件,根据key-value去查找,如果查找不到就默认的使用key作为value使用了。
逻辑大约应该是这样的:
//OC [[NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@",([[[NSUserDefaults standardUserDefaults] objectForKey:@"Language"] hasPrefix:@"zh"] || ([[NSUserDefaults standardUserDefaults] objectForKey:@"Language"] == nil && [[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode] hasPrefix:@"zh"]))?@"zh-Hans":@"en"] ofType:@"lproj"]] localizedStringForKey:(key) value:key table:nil]; //swift func L(key:String) -> String { var language:String? = NSUserDefaults .standardUserDefaults().objectForKey("Language") as? String if language == nil { language = NSLocale.currentLocale().objectForKey(NSLocaleLanguageCode)! as? String } let bundle:NSBundle = NSBundle.init(path: NSBundle.mainBundle().pathForResource(((language!.hasPrefix("zh")) ? "zh-Hans" : "en"), ofType: "lproj")!)! return NSLocalizedString(key, tableName: nil, bundle: bundle, value: key, comment: key) }
以上的key就是宏的形参了。
这样的宏其实是在编译之前就进行了代码的替换,其实就是把工程的所有的相关的地方替换上了这么长的代码,然后再进行编译(swift的其实是用一个public的函数去处理的,不是代码替换)。
后来发现有个更好的东西可以替换这个,就是内联函数
内联函数首先是一个函数,可以有函数的参数检查的优势
有普通的函数不具备的优势,函数的代码本质是放在符号表中的,使用的时候进行替换,没有了普通函数调用的时候的开销
执行效率和宏定义本质是一样的(内联函数是编译器首先在函数调用处使用函数体本身语句替换了函数调用语句,然后编译替换后的代码),但是还有优势
更多的内联函数的定义还是放几个博客吧:
http://blog.csdn.net/sqc3375177/article/details/8264778
http://blog.csdn.net/chsadin/article/details/47982923
http://www.cnblogs.com/QG-whz/p/4641479.html
OC的差不多这个样子写法:
static inline NSString * L (NSString * key) { return [[NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@",([[[NSUserDefaults standardUserDefaults] objectForKey:@"Language"] hasPrefix:@"zh"] || ([[NSUserDefaults standardUserDefaults] objectForKey:@"Language"] == nil && [[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode] hasPrefix:@"zh"]))?@"zh-Hans":@"en"] ofType:@"lproj"]] localizedStringForKey:(key) value:key table:nil]; }
swift的这样就可以了
@inline(__always) func L(key:String) -> String { var language:String? = NSUserDefaults .standardUserDefaults().objectForKey("Language") as? String if language == nil { language = NSLocale.currentLocale().objectForKey(NSLocaleLanguageCode)! as? String } let bundle:NSBundle = NSBundle.init(path: NSBundle.mainBundle().pathForResource(((language!.hasPrefix("zh")) ? "zh-Hans" : "en"), ofType: "lproj")!)! return NSLocalizedString(key, tableName: nil, bundle: bundle, value: key, comment: key) }
@inline后面的括号里面可以有两种关键词“never”和“__always”
naver是可以避免这个替代的时候使用了过大的代码块。这个到底使用普通函数还是内联函数是由编译器决定的(如果内联函数里面有循环和开关语句)就直接和普通函数没什么区别了
__always其实就是程序的运行效率优先了,不管什么,直接按照内联函数对待了
总结一下,就是以后写有参宏的时候直接用内联函数就好了,写代码有提示还能尽可能的保证不出错。