iOS 多语言全局适配

本文记录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 <UIKit/UIKit.h>
@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 <objc/runtime.h>

@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 文件当中。

So End All

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明AGI阅读 16,003评论 3 119
  • 关于腾讯和Facebook的差异,我一直都在思考。两家公司到底有什么不同?这几年我找到了一些答案,目前也还在继续探...
    光荣与梦想1987阅读 1,335评论 0 0
  • 这是我最初的心愿,这么多年来时不时会想起来,2005年到今天将近13年啦,一直没有彻底放下。 常说,念念不忘,必有...
    彭晓芬阅读 190评论 1 1
  • @所有人: 两天内写一篇2000字的 第四阶段的内训总结。 配上相应的图片, 每一个环节都要有图片。
    张永红助力身心灵健康阅读 168评论 0 0
  • 1. 因为带着小宝在外面浪了好几天,晚上早早就睡了。然后婆婆带着二个熊孩子出去玩。大概是九点钟,就听到外面特别喧哗...
    小H嗨皮阅读 268评论 0 0