由于產品需求,在 APP 里增加了語言設置選項,即用戶可以在不改變系統語言的情況下,只修改 APP 內的語言。可是如何讓遠程推送也跟隨 APP 語言呢?
每個遠程推送通知都包含一個 payload
。payload
包含系統要顯示給用戶的信息,也包括你自定義的數據。有關 payload key
可以看看這里 https://www.zybuluo.com/evolxb/note/482251。
看過上面這篇文章后,小伙伴應該知道了蘋果其實提供了實現語言國際化的方案,即通過 loc-key
和 loc-args
這兩個字段。其中 loc-key
是格式化前的內容,loc-args
里面放的是 loc-key
格式化過程中需要用到的參數。比如要向小明同學推送一條消息,內容是:
小明,你的包裹已出發!
小明的手機語言設置成了英文,收到這條推送后,需要顯示為:
小明,your package has sent!
那么 loc-key
和 loc-args
這兩個字段就應該這樣寫:
"loc-key": "%@,你的包裹已出發!",
"loc-args": ["小明"]
如果 app 的 Localizable.strings
文件中有這樣的定義:
"%@,你的包裹已出發!"="%@,your package has sent!";
小明收到的推送就將顯示為
“小明,your package has sent! ”。
看,就是這么簡單!但這還不夠,這只是跟隨系統語言而已,如果想要跟隨 app 語言,還需要做些事情。
推送文案跟隨 APP 語言
首先,創建一個 Notification Service Extension
,創建方法自己搜一下哈。
然后,在系統自動生成的 NotificationService.m
文件里修改 payload
,代碼如下:
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
[self configLanguage]; //動態切換語言
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
NSDictionary *userInfo = self.bestAttemptContent.userInfo;
NSDictionary *alert = [[userInfo objectForKey:@"aps"] objectForKey:@"alert"];
NSArray *args = [alert objectForKey:@"loc-args"];
NSString *content = [alert objectForKey:@"loc-key"];
if (content.length) {
self.bestAttemptContent.body = [NSString stringWithFormat:NSLocalizedString(content, nil) array:args]; //根據當前語言進行格式化
}
NSString *title = [alert objectForKey:@"title"];
self.bestAttemptContent.title = title;
self.contentHandler(self.bestAttemptContent);
}
- (void)configLanguage {
NSUserDefaults *sharedUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.test"];
NSString *language = [[self.sharedUserDefaults objectForKey:@"publicSettingKey"] objectForKey:@"kLanguageKey"];
if (language.length == 0) {
//APP 目前沒有選擇語言,則跟隨系統
NSString *systemLanguage = [[[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"] objectAtIndex:0];
if (![systemLanguage hasPrefix:@"zh"]) {
language = @"en"; //產品需求,如果系統語言不是中文,就默認設置為英文
}
}
[NSBundle setLanguage:language];
}
NSString 不定參數格式化
+ (NSString *)stringWithFormat:(NSString *)format array:(NSArray *)arguments {
NSRange range = NSMakeRange(0, [arguments count]);
NSMutableData* data = [NSMutableData dataWithLength: sizeof(id) * [arguments count]];
[arguments getObjects: (__unsafe_unretained id *)data.mutableBytes range:range];
NSString *content = [[NSString alloc] initWithFormat:format arguments:data.mutableBytes];
return content;
}
動態切換 APP 語言
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
NSBundle+Language.h
#import <Foundation/Foundation.h>
@interface NSBundle (Language)
+ (void)setLanguage:(NSString *)language;
@end
APP 與 Extension 間數據共享
應該有同學注意到了,APP 的當前語言是從這里 [[NSUserDefaults alloc] initWithSuiteName:@"group.com.test"]
取的。由于 Extension 有自己獨立的 Bundle,不能直接訪問主 APP 的 Bundle,這時候就要用到蘋果的 App Groups 機制了。
開啟主 APP 的 App Groups
:
被勾選的地方就是我們要設置的 group container identifier,也就是共享數據的 bundle identifier。格式是 "group.xxx",其中 xxx 是 APP 的 bundleID。比如APP 的 bundleID 是 “com.test”,那么 group identifier 就要設置為"group.com.test"。
開啟 Extension 的 App Groups
,group identifier 同上。
這樣,APP 和 Extension 就可以通過 group identifier 這個 bundle 來共享數據了。
首先,在 APP 里將 language 寫入 [[NSUserDefaults alloc] initWithSuiteName:@"group.com.test"]
.
然后,在 NotificationService.m
里就可以讀取到 language
了。
總結
如果只是跟隨系統語言,那么就使用 loc-key 和 loc-args,這兩個字段名不可隨意更改。
如果是通過 Notification Service Extension
,就不是非要用 loc-key
和 loc-args
字段了,甚至也不用在 alert
結構里設置,在 payload
的任何地方都可以自行定義字段哦,不要和蘋果的字段重復就好~