本文记录iOS 多语言全局适配解决方案,适用于项目中后期快速适配多语言;
实现思路:runtime method swizzling,拦截UILabel 的setText:
方法、UIViewController 的 setTitle:
方法、UINavigationItem 的setTitle:
方法;
一、多语言基本适配:
该部分请参考:VV木公子
作品:
3分钟实现iOS语言本地化/国际化(图文详解)!
二、多语言配置读取:
NSString *GlobalLanguePath;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self configAppLangue];
}
-(void)configAppLangue{
//根据NSUserDefaults的key去取多语言类型, k_AppLaguageKey为与用户相关的 Key
NSString *laguageType = [[NSUserDefaults standardUserDefaults] objectForKey:k_AppLaguageKey];
if (laguageType == nil) { // 如果没有用户偏好则根据系统第一语言配置启动,并且不做偏好设置记录
NSString *preferredLanguage = [NSLocale preferredLanguages][0];
if ([preferredLanguage hasPrefix:@"en"]) {
laguageType = @"en";
}else{
laguageType = @"zh-Hans";
}
}
NSLog(@"当前语言环境:%@",laguageType);
GlobalLanguePath = [[NSBundle mainBundle] pathForResource:laguageType ofType:@"lproj"];
}
为了方便使用,pch 来个宏定义:
// 语言加载路径 全局变量
extern NSString *GlobalLanguePath;
#define k_LangueKey(key) [[NSBundle bundleWithPath:GlobalLanguePath] localizedStringForKey:key value:nil table:@"Localizable"]
使用如下:
UILabel *label = [UILabel new];
label.text = k_LangueKey(@"你所配置的key");
一点注意: 如若手动切换语言环境,而不是跟随系统语言环境变化,则在语言切换之后重新初始化 window.rootViewController ,然后代码 push 到切换语言之前的 controller(如XXSettingViewController) 即可;
三、全局修改方式:
到此处可以使用 k_LangueKey() 一处一处修改了,然后在项目后期何其麻烦又是何其繁琐,所以自然想到拦截 UILabel 的 setText 方法:
#import "NSObject+swapMethod.h"
#import
@implementation UILabel (Langues)
+(void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swapSelector:@selector(setText:) toSelector:@selector(langues_setText:)];
});
}
-(void)langues_setText:(NSString *)text{
[self langues_setText:k_LangueKey(text)];
}
@end
然而,此时也许你会发现 导航栏 title 和 tabbarItem title 全变黑了,So ...
#import "NSObject+swapMethod.h"
@implementation UINavigationItem (Langues)
+(void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swapSelector:@selector(setTitle:) toSelector:@selector(langues_setTitle:)];
});
}
-(void)langues_setTitle:(NSString *)title{
[self langues_setTitle:k_LangueKey(title)];
}
@end
#import "NSObject+swapMethod.h"
@implementation UIViewController (Langues)
+(void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swapSelector:@selector(setTitle:) toSelector:@selector(langues_setTitle:)];
});
}
-(void)langues_setTitle:(NSString *)title{
[self langues_setTitle:k_LangueKey(title)];
}
@end
最后附上 swapSelector:toSelector方法:
#import "NSObject+swapMethod.h"
#import
@implementation NSObject (swapMethod)
+(void)swapSelector:(SEL)systemSelector toSelector:(SEL)customSelector{
SEL org_Selector = systemSelector;
SEL new_Selector = customSelector;
Method org_method = class_getInstanceMethod([self class], org_Selector);
Method new_method = class_getInstanceMethod([self class], new_Selector);
BOOL isAdd = class_addMethod(self, org_Selector, method_getImplementation(new_method), method_getTypeEncoding(new_method));
if (isAdd) {
class_replaceMethod(self, customSelector, method_getImplementation(new_method), method_getTypeEncoding(new_method));
}else{
method_exchangeImplementations(org_method, new_method);
}
}
@end
storyboard 手动切换语言环境的实现:
基本配置略过,加载过程替换 bundle 即可:
+(void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swapSelector:@selector(localizedStringForKey:value:table:) toSelector:@selector(langues_localizedStringForKey:value:table:)];
});
}
// 强行切换 bundle 资源文件
-(NSString *)langues_localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName{
BOOL isSystemBundle = [self.bundleIdentifier containsString:@"com.apple"];
if (isSystemBundle) {
return [self langues_localizedStringForKey:key value:value table:tableName];
}else{
return [[NSBundle bundleWithPath: GlobalLanguePath] langues_localizedStringForKey:key value:value table:tableName];
}
}
然而你会发现一些第三方(比如mjrefresh)的多语言加载失败,也是强行切换bundle资源带来的隐患,所以需将第三方的多语言文件复制到自己的 Localizable.strings 文件当中。