Martin

思想空间

我是Martin,iOS / Android 开发者。


欢迎来到我的思想空间

APP内切换语言

APP内切换语言

最近有需求需要做APP内切换语言,包括代码中设置的文本和storyboard中的文字

查了一些资料,主要参考:iOS App的国际化,以及App内的语言切换

里面包含了APP国际化的方法,这里主要记录下实现APP内切换语言的方法

在NSUserDefault中有一个字段:”AppleLanguages”,这个字段是一个数组,负责存储App支持的所有语言的字段,默认这个字段会根据系统语言去变动,中文系统就zh-Hans排在前面,英文系统就en排在前面。我们的项目包含中文和英文,试着打印下,结果如下。

AppDelegate.application(_:didFinishLaunchingWithOptions:)[96]:Optional(<__NSCFArray 0x170262dc0>(
zh-Hans-CN,
en-CN
)
)

(注意这里系统默认的带”-CN”,没有这个”-CN”也是可以的。)

所以我们的思路就是修改”AppleLanguages”字段使其根据我们的设置变化,例如设置英文:

UserDefaults.standard.set(["en"], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()

//重新加载sb,reload所有的viewcontroller
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as? AppDelegate
appDelegate?.window?.rootViewController = storyboard.instantiateInitialViewController()

这样并不能立刻生效,必须在重新启动APP才能生效,因为语言文件是以bundle的形式在APP启动时就加载进去了,例如如我们都会有zh-Hans.lproj 和 en.lproj这样两个文件夹

所以我们现在的思路就是在APP运行时改变加载的bundle

根据上面的参考资料,主要代码如下

NSBundle+Language.h

#import <Foundation/Foundation.h>

@interface NSBundle (Language)

+ (void)setLanguage:(NSString *)language;

@end

头文件只是声明一个setLanguage方法

主要看下实现代码

#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

这里主要是定义了一个NSBundle的子类,并重写了localizedStringForKey方法

然后是setLanguage方法的实现

我们先看setLanguage方法:

最主要的是object_setClass([NSBundle mainBundle], [BundleEx class]),这行代码将[NSBundle mainBundle]的类型变成了我们定义的BundleEx类,如此在系统调用localizedStringForKey方法时就会调用我们自己实现的代码

然后用我们设置的language创建一个bundle赋给了一个动态生成的属性_bundle

下面看我们自己实现的localizedStringForKey方法: 这里我们拿到我们动态生成的属性_bundle并使用它调用localizedStringForKey的方法来拿到我们想要的文字

所以最后设置英文的代码为:

Bundle.setLanguage("en")
UserDefaults.standard.set(["en"], forKey: "AppleLanguages")
UserDefaults.standard.synchronize() 

//重新加载sb,reload所有的viewcontroller
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let appDelegate = UIApplication.shared.delegate as? AppDelegate
appDelegate?.window?.rootViewController = storyboard.instantiateInitialViewController()

注:我们在设置”AppleLanguages”时没有加”-CN”,但是重启APP后依然有效

最近的文章

WebRTCDemo

WebRTCDemo最近公司有需求做局域网视频通讯,于是调研了webrtc并实现一个简单demo,本文主要记录demo的实现逻辑,以便以后使用时尽快上手。本文主要参考 :iOS下音视频通信-基于WebRTCiOS下WebRTC音视频通话(二)-局域网内音视频通话一些基本概念这里不讲,关于什么是ice,sdp参考上面的文章,直接讲思路及代码思路要建立p2p连接,我们需要:发起端A,接受端B,信令服务。这里信令服务的作用主要是在A和B建立连接前做一些信息的交换,当A和B真正建立连接后就不需要信...…

总结知识管理继续阅读
更早的文章

mac/iphone实用技巧总结

录制iphone手机屏幕、以及将视频做成gif动图转载自:IPHONE上看到好的动效设计,如何保存成GIF?录制手机屏幕你需要准备:iPhone(iOS 需要升级至iOS 8)Mac(OS X 需要升级为Yosemite) 首先用手机数据线连接iPhone和Mac。找到Mac系统自带的软件QuickTime,双击打开。 在Dock栏上右键QuickTime图标,选择“新建影片录制”。 这时候你会看到默认打开的是mac摄像头录制的画面而不是手机屏幕的画面。这时你需要点击界面下方红色圆点...…

总结知识管理继续阅读