iOS 實現遠程推送通知國際化

由于產品需求,在 APP 里增加了語言設置選項,即用戶可以在不改變系統語言的情況下,只修改 APP 內的語言。可是如何讓遠程推送也跟隨 APP 語言呢?

每個遠程推送通知都包含一個 payloadpayload包含系統要顯示給用戶的信息,也包括你自定義的數據。有關 payload key 可以看看這里 https://www.zybuluo.com/evolxb/note/482251

看過上面這篇文章后,小伙伴應該知道了蘋果其實提供了實現語言國際化的方案,即通過 loc-keyloc-args 這兩個字段。其中 loc-key 是格式化前的內容,loc-args 里面放的是 loc-key 格式化過程中需要用到的參數。比如要向小明同學推送一條消息,內容是:

小明,你的包裹已出發!

小明的手機語言設置成了英文,收到這條推送后,需要顯示為:

小明,your package has sent!

那么 loc-keyloc-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 :

開啟主 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 同上。

開啟Extension 的 App Groups

這樣,APP 和 Extension 就可以通過 group identifier 這個 bundle 來共享數據了。

首先,在 APP 里將 language 寫入 [[NSUserDefaults alloc] initWithSuiteName:@"group.com.test"] .

然后,在 NotificationService.m 里就可以讀取到 language 了。

總結

如果只是跟隨系統語言,那么就使用 loc-key 和 loc-args,這兩個字段名不可隨意更改。

如果是通過 Notification Service Extension,就不是非要用 loc-keyloc-args 字段了,甚至也不用在 alert 結構里設置,在 payload 的任何地方都可以自行定義字段哦,不要和蘋果的字段重復就好~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容