最近一个一直在迭代的老项目收到一份新的开发需求, 项目需要做国际化适配, 简体中文 + 英文由于项目中采用了 storyboard 和纯代码两种布局方式, 所以国际化也要同时实现上网查了些资料, 实现了更改系统语言后, 修改 app 内语言的问题具体国际化方式可以参考下文:
3 分钟实现 iOS 语言本地化 / 国际化
这篇文章讲的比较详细, 很容易实现
这个需求实现后不久, 产品又给我提了一个需求, 让我要在 app 内实现语言切换还好之前的国际化也做了些准备, 不慌不慌
接下来就是方案的选定, 通过广泛查阅资料, 得出两个备选方案:
方案一: 在原国际化版本的基础上做修改, 在 info.plist 文件中新增 key="appLanguage" 的键值对, 保存用户设定的语言类别通过切换语言类别来改变语言 (例子: 微信)
优点: 之前有国际化操作的基础, 执行起来并不复杂
缺点: 切换完语言后, 需要重新创建 app keywindow 的跟控制器, 会有个跳转的过程, 用户体验不好
方案二: 切换语言后, 发送通知, 每个控制器收到通知后, 更改语言 (例子: 新浪微博)
优点: 很自然的切换语言, 选择语言后即可切换, 不需要重置根控制器, 用户体验好
缺点: 每个控制器都得注册接收通知, 工作量太大, 而且 storyboard 也得单独处理
综合两个方案的优缺点, 我们选择方案一
中英切换, 就是让 App 根据自身设置的语言去读取对应的国际化文件在 NSUserDefault 中有一个字段:"AppleLanguages", 这个字段就是负责存储 App 语言的字段, 默认这个字段会根据系统语言去变动, 中文系统他就存储中文, 英文系统就存储英文
废话少说, 切换语言的过程上代:
- // NTVLocalized.h
- #import <Foundation/Foundation.h>
- static NSString * const AppLanguage = @"appLanguage";
- @interface NTVLocalized : NSObject
- + (NTVLocalized *)sharedInstance;
- // 初始化多语言功能
- - (void)initLanguage;
- // 当前语言
- - (NSString *)currentLanguage;
- // 设置要转换的语言
- - (void)setLanguage:(NSString *)language;
- // 设置为系统语言
- - (void)systemLanguage;
- @end
- // NTVLocalized.m
- #import "NTVLocalized.h"
- @implementation NTVLocalized
- + (NTVLocalized *)sharedInstance {
- static NTVLocalized *instance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- instance = [[NTVLocalized alloc] init];
- });
- return instance;
- }
- - (void)initLanguage{
- NSString *language=[self currentLanguage];
- if (language.length>0) {
- NSLog(@"自设置语言:%@",language);
- }else{
- [self systemLanguage];
- }
- }
- - (NSString *)currentLanguage{
- NSString *language=[[NSUserDefaults standardUserDefaults]objectForKey:AppLanguage];
- return language;
- }
- - (void)setLanguage:(NSString *)language{
- [[NSUserDefaults standardUserDefaults] setObject:language forKey:AppLanguage];
- }
- - (void)systemLanguage{
- NSString *languageCode = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"][0];
- NSLog(@"系统语言:%@",languageCode);
- if([languageCode hasPrefix:@"zh-Hans"]){
- languageCode = @"zh-Hans";// 简体中文
- }else if([languageCode hasPrefix:@"en"]){
- languageCode = @"en";// 英语
- }
- [self setLanguage:languageCode];
- }
- @end
当语言设置完成后, 需要重新设置 keywindow 的 rootViewController 才可以实现语言的切换
然而这样设置后, 我们发现只有 NSLocalizedString(key, comment) 设置的语言才能正常显示我们需要的语言, storyBoard 和 xib 配置的页面语言不跟着切换
设置 AppleLanguages 字段的话, 只会在下次启动 App 才会生效, 在 App 启动后就已经生成了一个 Bundle, 里面识别好了对应着 AppleLanguages 的国际化文件, 在 App 运行期间设置这个字段, 是不生效的, 所以我们去修改这个 Bundle, 写一个 NSBundle 的扩展
- // NSBundle+language.h
- #import <Foundation/Foundation.h>
- @interface NSBundle (language)
- // 设置语言
- + (void)setLanguage:(NSString *)language;
- @end
- // NSBundle+language.m
- #import "NSBundle+language.h"
- #import <objc/runtime.h>
- static const char _bundle = 0;
- @interface BundleEx : NSBundle
- @end
- @implementation BundleEx
- - (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName {
- NSBundle *bundle = objc_getAssociatedObject(self, &_bundle);
- return bundle ? [bundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName];
- }
- @end
- @implementation NSBundle (Language)
- + (void)setLanguage:(NSString *)language {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- object_setClass([NSBundle mainBundle], [BundleEx class]);
- });
- objc_setAssociatedObject([NSBundle mainBundle], &_bundle, language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- @end
重新写一下设置语言的方法:
- - (void)setLanguage:(NSString *)language{
- [NSBundle setLanguage:language];
- [[NSUserDefaults standardUserDefaults] setObject:language forKey:AppLanguage];
- [[NSUserDefaults standardUserDefaults] synchronize];
- }
综上所述, 只是修改 appleLanguage, 在不重启应用的情况下, 不能修改语言所以我们选择修改 bundle 的方法
代码在 github 上可以下载到:
https://github.com/FrankiezZZ/NTVLocalized
来源: https://www.cnblogs.com/FrankieZ/p/8552830.html