网上例子比较多步骤简单回顾
第一步:Projext
- Localizations
- +
自己需要的语言
第二步:Targets
- Info
添加 Localized resources can be mixed
YES
好像非必须,还未搞懂…
第三步:创建 Strings File
命名 InfoPlist
和 Localizable
InfoPlist.strings 存放系统的国际化
Localizable.strings 存放本地的国际化
选中InfoPlist
或者Localizable
右侧边栏勾选需要的语言就会产生下拉框,不同的语言区分文件填写
方法实现
Objextive-c
宏文件
#define YZMsg(key) [[RookieTools shareInstance] getStringForKey:key withTable:@"InfoPlist"]
#define CurrentLanguage @"will_show_language"
#define getImagename(a) [NSString stringWithFormat:@"%@%@",a,[Config canshu]]
#define lagType [[NSUserDefaults standardUserDefaults] objectForKey:CurrentLanguage]
#define ZH_CN @"zh-Hans"
#define EN @"en"
.h
@interface RookieTools : NSObject
+(id)shareInstance;
/**
* 返回table中指定的key的值
*
* @param key key
* @param table table
*
* @return 返回table中指定的key的值
*/
-(NSString *)getStringForKey:(NSString *)key withTable:(NSString *)table;
/**
* 重置语言
*
* @param language 新语言
*/
-(void)resetLanguage:(NSString*)language withFrom:(NSString *)appdelegate;
@end
.m
static RookieTools *shareTool = nil;
@interface RookieTools()
@property(nonatomic,strong)NSBundle *bundle;
@property(nonatomic,copy)NSString *language;
@end
@implementation RookieTools
+ (void)load {
// 交换MJ的国际化方法
Method mjMethod = class_getClassMethod([NSBundle class],@selector(mj_localizedStringForKey:value:));
Method myMethod = class_getClassMethod(self, @selector(hook_mj_localizedStringForKey:value:));
method_exchangeImplementations(mjMethod, myMethod);
}
/// hook刷新控件的提示文字
+ (NSString *)hook_mj_localizedStringForKey:(NSString *)key value:(NSString *)value {
NSString *language;
if ([lagType isEqual:ZH_CN]) {
language = @"zh-Hans";
}else{
language = @"en";
}
NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle mj_refreshBundle] pathForResource:language ofType:@"lproj"]];
return [bundle localizedStringForKey:key value:nil table:@"Localizable"];
}
+(id)shareInstance {
@synchronized (self) {
if (shareTool == nil) {
shareTool = [[RookieTools alloc]init];
}
}
return shareTool;
}
+(instancetype)allocWithZone:(struct _NSZone *)zone {
if (shareTool == nil) {
shareTool = [super allocWithZone:zone];
}
return shareTool;
}
-(NSString *)getStringForKey:(NSString *)key withTable:(NSString *)table {
if (self.bundle) {
return NSLocalizedStringFromTableInBundle(key, table, self.bundle, @"");
}
return NSLocalizedStringFromTable(key, table, @"");
}
-(void)resetLanguage:(NSString *)language withFrom:(NSString *)appdelegate{
if ([language isEqualToString:self.language]) {
return;
}
if ([language isEqualToString:@"en"] || [language isEqualToString:@"zh-Hans"]) {
NSString *path = [[NSBundle mainBundle]pathForResource:language ofType:@"lproj"];
self.bundle = [NSBundle bundleWithPath:path];
}
self.language = language;
[[NSUserDefaults standardUserDefaults] setObject:language forKey:CurrentLanguage];
[[NSUserDefaults standardUserDefaults]synchronize];
if (![appdelegate isEqualToString:@"appdelegate"]) {
[self resetRootViewController];
}
// ===>20220426 并新增NSBundle扩展
NSMutableArray *userDefaultLanguages = [[NSUserDefaults standardUserDefaults]objectForKey:@"AppleLanguages"];
NSLog(@"rk-language-tool-get:%@",userDefaultLanguages);
NSString *langStr = @"zh-Hans-US";
if (![lagType isEqual:ZH_CN]) {
langStr = @"en-US";
}
[NSBundle setLanguage:langStr];
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:langStr,nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSMutableArray *userDefaultLanguages1 = [[NSUserDefaults standardUserDefaults]objectForKey:@"AppleLanguages"];
NSLog(@"rk-language-tool-set:%@",userDefaultLanguages1);
// <===20220426
}
-(void)resetRootViewController {
UIWindow *window = [UIApplication sharedApplication].keyWindow;
YBTabBarController *root = [[YBTabBarController alloc]init];
UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:root];
window.rootViewController = navi;
[[TUIKit sharedInstance] initKit:TXIMSdkAppid accountType:TXIMSdkAccountType withConfig:[TUIKitConfig defaultConfig]];
// [root changeLanguage];
}
@end
NSBundle扩展
注意:目前没有发现有什么作用,切换语言立即打开系统相机还是不会立刻发生变化…
.h
//
// NSBundle+RKLanguage.h
// iphoneLive
//
// Created by YB007 on 2022/4/20.
// Copyright © 2022 cat. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSBundle (RKLanguage)
// 设置语言
+ (void)setLanguage:(NSString *)language;
@end
NS_ASSUME_NONNULL_END
.m
//
// NSBundle+RKLanguage.m
// iphoneLive
//
// Created by YB007 on 2022/4/20.
// Copyright © 2022 cat. All rights reserved.
//
#import "NSBundle+RKLanguage.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);
NSString *locStr = bundle ? [bundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName];
NSLog(@"rk-language-budle-set-val:%@",locStr);
return locStr;
}
@end
@implementation NSBundle (RKLanguage)
+ (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);
NSLog(@"rk-language-budle-set-language:%@",language);
}
@end
Swift
import UIKit
import MJRefresh
/// 键值替换
func rkLocalized(key:String) -> String {
let currnetL = rkLanguageType()
let getPath = Bundle.main.path(forResource: currnetL as String, ofType: "lproj")
guard let pathL = getPath else {
return key
}
let getValue = Bundle.init(path: pathL)?.localizedString(forKey: key as String, value: nil, table: "Localizable")
let value:String = getValue ?? key
return value
}
/// 初始化
private let onceToken = "Method Swizzling"
func rkLanguageInit() {
if (UserDefaults.standard.object(forKey: rkLanguageKey) == nil) {
let languageArray = NSLocale.preferredLanguages;
let languageStr = languageArray[0]
if languageStr.hasPrefix(rkZhCn as String) {
UserDefaults.standard.set(rkZhCn, forKey: rkLanguageKey)
}else{
UserDefaults.standard.set(rkEn, forKey: rkLanguageKey)
}
UserDefaults.standard.synchronize()
}
//动态设置MJ国际化
let langClas = RKHook()
langClas.mjLanguage()
// langClas.hookTest()
}
/// 获取语言
func rkLanguageType() -> String {
return UserDefaults.standard.object(forKey: rkLanguageKey) as! String
}
/// 切换语言
func rkSwitchLanguage() {
let currentL = rkLanguageType()
if currentL.isEqual(rkEn) {
UserDefaults.standard.set(rkZhCn, forKey: rkLanguageKey)
}else{
UserDefaults.standard.set(rkEn, forKey: rkLanguageKey)
}
UserDefaults.standard.synchronize()
let tabVC = RKTabBarController()
UIApplication.shared.rkWindow?.rootViewController = tabVC
UIApplication.shared.rkWindow?.makeKeyAndVisible()
tabVC.selectedIndex = 4
let setVC = RKSetVC()
UIApplication.shared.pushViewController(setVC, animated: false)
}
/**
* Swift 自定义类使用 method swizzling
* 1.包含 swizzle 的class必须是继承自 NSObject
* 2.swizzle 方法必须有动态属性
*/
class RKHook:NSObject {
@objc var aBool: Bool = true
/// 测试方法
@objc func hookTest(){
var methodNum:UInt32 = 0
let rkHook:RKHook = RKHook()
let methodList = class_copyMethodList(object_getClass(rkHook), &methodNum)
rkprint("method-start")
for index in 0 ..< numericCast(methodNum) {
let method: Method = methodList![index]
rkprint(String(utf8String: method_getTypeEncoding(method)!) as Any)
rkprint(String(utf8String: method_copyReturnType(method)) as Any)
rkprint(String(_sel:method_getName(method)))
rkprint("==a:\(method_getImplementation(method))")
}
rkprint("method-end")
rkprint("property-start")
var propertyNums:UInt32 = 0
let propertyList = class_copyPropertyList(object_getClass(rkHook), &propertyNums)
for index in 0 ..< numericCast(propertyNums) {
let property:objc_property_t = propertyList![index]
rkprint(String(utf8String: property_getName(property)) as Any)
rkprint(String(utf8String: property_getAttributes(property)!) as Any)
}
rkprint("property-end")
}
/// 动态设置MJ的国际化
@objc func mjLanguage() {
DispatchQueue.once(token: onceToken) {
let mj_selector = #selector(Bundle.mj_localizedString(forKey:value:))
let hook_selector = #selector(self.hook_mj_localizedString(forKey:value:))
let mjMethod = class_getClassMethod(Bundle.self, mj_selector)
let hookMethod = class_getInstanceMethod(RKHook.self, hook_selector)
method_exchangeImplementations(mjMethod!, hookMethod!)
/*
//在进行 Swizzling 的时候,需要用 class_addMethod 先进行判断一下原有类中是否有要替换方法的实现
let didAddMethod: Bool = class_addMethod(Bundle.self, mj_selector, method_getImplementation(hookMethod!), method_getTypeEncoding(hookMethod!))
//如果 class_addMethod 返回 yes,说明当前类中没有要替换方法的实现,所以需要在父类中查找,这时候就用到 method_getImplemetation 去获取 class_getInstanceMethod 里面的方法实现,然后再进行 class_replaceMethod 来实现 Swizzing
if didAddMethod {
class_replaceMethod(RKHook.self, hook_selector, method_getImplementation(mjMethod!), method_getTypeEncoding(mjMethod!))
} else {
method_exchangeImplementations(mjMethod!, hookMethod!)
}
*/
}
}
@objc dynamic func hook_mj_localizedString(forKey:String,value:String) -> String{
let currentL = rkLanguageType()
var mjLanguage:String = ""
if currentL.isEqual(rkEn) {
mjLanguage = "en"
}else{
mjLanguage = "zh-Hans"
}
let getPath = Bundle.mj_refresh().path(forResource: mjLanguage, ofType: "lproj")
//rkprint("===kk:\(String(describing: getPath))")
guard let pathL = getPath else {
return currentL
}
let rkMjBundle = Bundle.init(path: pathL)
guard let getMjb = rkMjBundle else {
return currentL
}
let getValue = getMjb.localizedString(forKey: forKey, value: nil, table: "Localizable")
let value:String = getValue
//rkprint("kkkllll:\(forKey)\nbbbbb:\(value)")
return value
}
}
推送
总结
oc
中可以使用load
方法来来实现动态改变MJ
的国际化
Swift
要想利用oc
的运行时需要注意两点:
1.继承自NSObject
2.动态属性