前言
本文在 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 直接查看代码,也可以给我留言,我有看到会第一时间回复的。