iOS 优雅的集成 App 内切换多国语言功能

2020-05-21  本文已影响0人  醒着z

前言

本文在 App 已经手动集成语言本地化/国际化功能的基础上实现用户手动切换语言功能。
如果对集成本地化/国际化有疑问的同学,可以参考 这篇文章

原理

App 内需要适配多语言的字段都通过 NSLocalizedString(key, comment)这个系统提供的宏进行适配,而这个宏的内容是 NSBundle 类的对象调用实例方法:

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName

因此我们可以通过 iOS 的运行时机制 Method Swizzing 来交换这个系统方法,从而达到注入需求代码的目的。

效果

实现

话不多说,直接贴代码

LanguageManager

//LanguageManager.h
#import <Foundation/Foundation.h>
extern NSString * const kLocalizedLanguageDidChangedNotification;
@interface LanguageManager : NSObject

/// 当前使用语言
+ (NSString *)currentLanguageName;

/// 重置使用系统语言
+ (BOOL)resetToSystemLanguage;

/// 切换语言
/// @param language 语言缩写
+ (BOOL)switchLanguage:(NSString *)language;
@end
//LanguageManager.m
#import "LanguageManager.h"
NSString * const kLocalizedLanguageDidChangedNotification = @"kLocalizedLanguageDidChangedNotification";

//系统默认读取 app 语言的 key
#define kAppleLanguages     @"appleLanguages"
//记录用户使用的语言
#define kLastUsedLanguage   @"kLastUsedLanguage"

@implementation LanguageManager
+ (NSString *)currentLanguageName {
    NSString *lastLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:kLastUsedLanguage];
    NSString *language = lastLanguage.length ? lastLanguage : NSLocale.preferredLanguages.firstObject;
    return language;
}
+ (BOOL)resetToSystemLanguage {
    [[NSUserDefaults standardUserDefaults] setObject:nil forKey:kLastUsedLanguage];
    [[NSUserDefaults standardUserDefaults] setObject:nil forKey:kAppleLanguages];
    return [[NSUserDefaults standardUserDefaults] synchronize];
}
+ (BOOL)switchLanguage:(NSString *)language {
    if (!language.length) {
        return NO;
    }
    [[NSUserDefaults standardUserDefaults] setObject:language forKey:kLastUsedLanguage];
    [[NSUserDefaults standardUserDefaults] setObject:language forKey:kAppleLanguages];
    BOOL rs = [[NSUserDefaults standardUserDefaults] synchronize];
    if (rs) {
        [[NSNotificationCenter defaultCenter] postNotificationName:kLocalizedLanguageDidChangedNotification object:language];
    }
    return rs;
}
@end

NSBundle+Language 类别

//NSBundle+Language.m
#import "NSBundle+Language.h"
#import "LanguageManager.h"
#import <objc/runtime.h>

@implementation NSBundle (Language)
+ (void)load {
    SEL originalSel = @selector(localizedStringForKey:value:table:);
    SEL newSel = @selector(exchange_localizedStringForKey:value:table:);
    
    Method originalMethod = class_getInstanceMethod(self, originalSel);
    Method newMethod = class_getInstanceMethod(self, newSel);
    //交换方法
    method_exchangeImplementations(originalMethod, newMethod);
}
- (NSString *)exchange_localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName {
    NSString *language = [LanguageManager currentLanguageName];
    NSString *bundlePath = [NSBundle.mainBundle pathForResource:language ofType:@".lproj"];
    if (bundlePath.length) {
        NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
        return [bundle exchange_localizedStringForKey:key value:value table:tableName];
    }
    return [self exchange_localizedStringForKey:key value:value table:tableName];
}
@end

集成

1、手动集成

手动前往 Github 项目地址 ,下载完成后拷贝 Localized 目录到工程中

2、Cocopods 集成

Podfile 文件导入 pod 'CCLocalizedLanguage'

使用

1、导入 #import "LanguageManager.h"
2、切换语言
指定国家语言
[LanguageManager switchLanguage:selectedLanguage];
恢复跟随系统语言
[LanguageManager resetToSystemLanguage];
3、刷新,重新指定根控制器以起到刷新所有页面的效果
NSArray *array = UIApplication.sharedApplication.connectedScenes.allObjects;
UIWindowScene *windowScene = array.firstObject;
UIWindow *window = windowScene.windows.firstObject;
UINavigationController *rootVC = (UINavigationController *)window.rootViewController;

UIViewController *newVC = [rootVC.viewControllers.firstObject.class.alloc init];
UINavigationController *newRootVC = [[UINavigationController alloc] initWithRootViewController:newVC];
            
SwitchLanguageVC *languageVC = [[SwitchLanguageVC alloc] init];
[newRootVC pushViewController:languageVC animated:NO];
            
window.rootViewController = newRootVC;
rootVC = nil;
            
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [newRootVC popViewControllerAnimated:YES];
});

结语

这个功能就算完成了,文中对切换语言的逻辑没有很详细的描述,有不明白的同学可以下载 Demo 直接查看代码,也可以给我留言,我有看到会第一时间回复的。

上一篇 下一篇

猜你喜欢

热点阅读