0
点赞
收藏
分享

微信扫一扫

iOS小技能:APP内的本地化语言切换(案例:登录界面切换中英文)

闲鱼不咸_99f1 2022-08-31 阅读 205

前言

原理:

  1. 从指定的表和包中返回字符串的本地化版本:​​NSLocalizedStringFromTableInBundle(key, table, _bundle, nil)​
  2. 切换语言生效方式:采用销毁根控制器,重新进入一次
  3. 本地化字符串指定参数顺序


iOS小技能:APP内的本地化语言切换(案例:登录界面切换中英文)_#define

iOS小技能:APP内的本地化语言切换(案例:登录界面切换中英文)_iOS_02

I 字符串的本地化

1.1 用法

  • 创建字符串资源文件Localizable.strings

iOS小技能:APP内的本地化语言切换(案例:登录界面切换中英文)_字符串_03

  • 为Localizable.strings添加多语言支持
  • iOS小技能:APP内的本地化语言切换(案例:登录界面切换中英文)_bundle_04

选 make Localize代表会将Localizable.strings加入到英语的本地化文件夹en.lproj中去

iOS小技能:APP内的本地化语言切换(案例:登录界面切换中英文)_iOS_05

Localizable.strings(English)对应en.lproj文件夹中的Localizable.strings文件 Localizable.strings(Chinese)对应zh-Hans.lproj文件夹中的Localizable.strings文件

  • 在资源文件Localizable.strings配置字符串对应的key和value。

​en.lproj/Localizable.strings​

static NSString * const kSystem      = @"SystemDefault";

static NSString * const kCH = @"zh-Hans";
static NSString * const kEN = @"en";


static NSString * const kProj = @"lproj";
static NSString * const kLanguageSet = @"kLanguageSet";
"Connected" = "Connected";
"Tip"="Tip";

​zh-Hans.lproj/Localizable.strings​

"Connected" = "已连接";
"Tip"="提示";

iOS小技能:APP内的本地化语言切换(案例:登录界面切换中英文)_iOS_06

  • 在代码中使用NSLocalizedString(key, comment)来读取本地化字符串,key是Localizable.strings文件中等号左边的字符串,comment纯粹是注释

#define NSLocalizedString(key, comment) \
[NSBundle.mainBundle localizedStringForKey:(key) value:@""
#define NSLocalizedStringFromTable(key, tbl, comment) \
[NSBundle.mainBundle localizedStringForKey:(key) value:@""
#define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \
[bundle localizedStringForKey:(key) value:@""
#define

1.2 语言的切换

//程序的本地化,引用国际化的文件
#define

#define HZLocalizedString(key, comment) HZLocalizedStringFromTable(key, @"Localizable", nil)
#define




/**

@param key <#key description#>
@param table Localizable
@return <#return value description#>
*/
- (NSString *)stringWithKey:(NSString *)key table:(NSString *)table
{
//假如为跟随系统
if (self.languageType==HZLanguageTypeSystem) {
return NSLocalizedString(key, nil);
}

//返回对应国际化文字
if (_bundle) {
return NSLocalizedStringFromTableInBundle(key, table, _bundle, nil);
}

return NSLocalizedStringFromTable(key, table, nil);
}





// 使用QCTLocal读取本地化字符串

[LBAlertController showAlertTitle:QCTLocal(@"tips") content:QCTLocal(@"str_cann_printer") cancelString:QCTLocal(@"cancel") cancleBlock:nil sureString:QCTLocal(@"sure") sureBlock:^{

如果没有对字符串进行本地化 或者 找不到key对应的值,NSLocalizedString将直接返回key这个字符串

重置TableBundle

- (void)resetBundle{

NSLog(@"resetBundle:%@",_languageString);

if ([_languageString isEqualToString:kEN] || [_languageString isEqualToString:kCH]) {
NSString *path = [[NSBundle mainBundle] pathForResource:_languageString ofType:kProj];
self.bundle = [NSBundle

1.3 强制为中文

由于审核时境外IP无法访问,决定去掉英文版本。

// 由于审核时,境外IP无法访问,决定去掉英文版本

- (void)setLanguageType:(HZLanguageType)languageType{
//HZLanguageTypeChineseSimple
languageType =HZLanguageTypeChineseSimple;
_languageString = kCH;//强制为中文

_languageType =languageType;

}
// 中英文切换
- (void)changeLanguageType:(HZLanguageType)type;
{
if (self.currentLanguageType == type) {
return;
}

self.languageType = type;

switch (self.languageType) {

case HZLanguageTypeSystem:

self.languageString=kSystem;
break;
case HZLanguageTypeEnglish:

self.languageString = kEN;
break;
case HZLanguageTypeChineseSimple:

self.languageString = kCH;
break;

}

//bundle 设置
[self resetBundle];

//设置语言,并作记录保存
[[NSUserDefaults standardUserDefaults] setObject:_languageString forKey:kLanguageSet];
[[NSUserDefaults standardUserDefaults] synchronize];

//使用通知更改文字 [[NSNotificationCenter defaultCenter] postNotificationName:kNoticeLanguageChange object:nil];

1.4 本地化字符串指定参数顺序

对于在中英文语法的区别,在本地化字符串的时候,我们常常需要调整stringWithFormat的参数顺序

  • ​​iOS本地化字符串指定参数顺序:应用场景:app内的多语言切换​​

原理:在%和@中间加上数值,2$),数字代表参数的顺序

"FORMAT" = "I am %2$@ from '%1$@'";

II 自定义解析本地化字符串的工具类LanguageManager

2.1 代码实现

  • ​​LanguageManager​​

#import <Foundation/Foundation.h>

#define HZLocalizedString(key, comment) HZLocalizedStringFromTable(key, @"Localizable", nil)
#define

static NSString * const kNoticeLanguageChange = @"kNoticeLanguageChange";


typedef NS_ENUM(NSUInteger, HZLanguageType) {
HZLanguageTypeSystem,
HZLanguageTypeEnglish,
HZLanguageTypeChineseSimple,
};

@interface HZLanguageManager : NSObject

@property (nonatomic, assign, getter=currentLanguageType) HZLanguageType languageType;

+ (instancetype)defaultManager;

- (void)changeLanguageType:(HZLanguageType)type;
- (NSString *)stringWithKey:(NSString *)key table:(NSString *)table;
- (HZLanguageType)currentLanguage;

+ (BOOL

  • LanguageManager.h .m

static NSString * const kSystem      = @"SystemDefault";

static NSString * const kCH = @"zh-Hans";
static NSString * const kEN = @"en";


static NSString * const kProj = @"lproj";
static NSString * const kLanguageSet = @"kLanguageSet";

@interface HZLanguageManager()

@property (nonatomic, strong) NSBundle *bundle;
@property (nonatomic, strong) NSString *languageString;

@end

@implementation HZLanguageManager

+ (instancetype)defaultManager
{
static dispatch_once_t onceToken;
static HZLanguageManager *manager;
dispatch_once(&onceToken, ^{
if (!manager) {
manager = [[HZLanguageManager alloc] init];
}
});
return manager;
}

- (instancetype)init
{
self = [super init];
if (!self) {
return nil;
}
//取字段看是哪种语言
NSString *tempStr = [[NSUserDefaults standardUserDefaults] objectForKey:kLanguageSet];
// NSString *path;

//假如用户没有设置过语言
if (!tempStr) {
tempStr=kSystem;
}

self.languageString = tempStr;


if ([self.languageString isEqualToString:kCH]) {//为中文

self.languageType = HZLanguageTypeChineseSimple;

}else if ([self.languageString isEqualToString:kEN]) {//为英文

self.languageType = HZLanguageTypeEnglish;

}else if([self.languageString isEqualToString:kSystem]){//为系统默认

self.languageType= HZLanguageTypeSystem;

if([self isENLanguage]){//HZLanguageManager
self.languageType = HZLanguageTypeEnglish;

}else{
self.languageType = HZLanguageTypeChineseSimple;

}


}

if ([_languageString isEqualToString:kEN] || [_languageString isEqualToString:kCH]) {
NSString *path = [[NSBundle mainBundle] pathForResource:_languageString ofType:kProj];
self.bundle = [NSBundle bundleWithPath:path];
}

NSLog(@"当前的语言 HZLanguageTypeSystem HZLanguageTypeEnglish HZLanguageTypeChineseSimple:%lu",(unsigned long)self.languageType);

return self;
}



- (void)changeLanguageType:(HZLanguageType)type;
{
if (self.currentLanguageType == type) {
return;
}

_languageType = type;
switch (type) {
case HZLanguageTypeSystem:

self.languageString=kSystem;
break;
case HZLanguageTypeEnglish:

self.languageString = kEN;
break;
case HZLanguageTypeChineseSimple:

self.languageString = kCH;
break;

}

//bundle 设置
[self resetBundle];

//设置语言,并作记录保存
[[NSUserDefaults standardUserDefaults] setObject:_languageString forKey:kLanguageSet];
[[NSUserDefaults standardUserDefaults] synchronize];

//使用通知更改文字 [[NSNotificationCenter defaultCenter] postNotificationName:kNoticeLanguageChange object:nil];
}

- (void)resetBundle{
if ([_languageString isEqualToString:kEN] || [_languageString isEqualToString:kCH]) {
NSString *path = [[NSBundle mainBundle] pathForResource:_languageString ofType:kProj];
self.bundle = [NSBundle bundleWithPath:path];
}
}

- (NSString *)stringWithKey:(NSString *)key table:(NSString *)table
{
//假如为跟随系统
if (self.languageType==HZLanguageTypeSystem) {
return NSLocalizedString(key, nil);
}

//返回对应国际化文字
if (_bundle) {
return NSLocalizedStringFromTableInBundle(key, table, _bundle, nil);
}

return NSLocalizedStringFromTable(key, table, nil);
}

-(HZLanguageType)currentLanguage{
//获取当前语言
// NSString *tempStr = [[NSUserDefaults standardUserDefaults] objectForKey:kLanguageSet];
// if ([tempStr rangeOfString:@"zh"].length) {
//// tempStr = kCH;
// return HZLanguageTypeChineseSimple;
// }else if([tempStr isEqualToString:kEN]){
//// tempStr = kEN;
// return HZLanguageTypeEnglish;
// }else {//if([tempStr isEqualToString:kSystem])
//
// return HZLanguageTypeSystem;
// }
return self.languageType;
}


+ (NSArray *)english {
return @[@"en", @"en-CN"];
}

+ (NSArray *)chineseHans {
return @[@"zh-Hans", @"zh-Hans-CN"];
}

+ (NSArray *)chineseHant {
return @[@"zh-Hant", @"zh-Hant-CN"];
}




/**
(lldb) po [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"]
<__NSCFArray 0x2833357a0>(
zh-Hans-AM,
en-AM,
en-GB,
ru-AM,
zh-Hant-AM,
zh-Hant-HK
)

第一个就是用户设置的语言

@return
*/
- (BOOL )isENLanguage {
// NSString *language = [[NSUserDefaults standardUserDefaults] objectForKey:GHLLanguageKey];

// if (){
// if ([HZLanguageManager defaultManager].currentLanguage==HZLanguageTypeEnglish) {

if (self.currentLanguage == HZLanguageTypeEnglish) {

return YES;
}



// if () {
NSArray *languages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];
NSString *language = languages.firstObject;

// }

// if([[HZLanguageManager chineseHans] containsObject:language] || [[HZLanguageManager chineseHant] containsObject:language] ){
if([language containsString:@"zh-Hans"] || [language containsString:@"zh-Hant"] ){


return NO;
}else{
return YES;

}

}



+ (BOOL )isEN{

if([HZLanguageManager defaultManager].currentLanguage == HZLanguageTypeEnglish ){
// make.width.mas_equalTo(kAdjustRatio(73 * 3));
return YES;

}else{
// make.width.mas_equalTo(kAdjustRatio(50 * 3));
// make.left.equalTo(weakSelf.contentView).offset(kAdjustRatio(20));
// make.right.equalTo(weakSelf.contentView).offset(- kAdjustRatio(20));
//
return NO;

}

}
@end

2.2 strings文件的文本内容过长导致key 失效的解决方案

​ /consts/Localizable/en.lproj/Localizable.strings ​​ 文本内容太长了。 会导致key 失效。

  • 解决方案

新增一个table来存储,解析的时候从两个文件查找。

new file:   retail/retail/class/consts/Localizable/en.lproj/Localizable1.strings
new file: retail/retail/class/consts/Localizable/zh-Hans.lproj/Localizable1.strings

  • 读取本地字符串的代码:​​如果你的字符串资源文件名不是Localizable.strings,如KN.strings,那么你就得使用NSLocalizedStringFromTable()、NSLocalizedStringFromTableInBundle来读取本地化字符串: ​

- (NSString *)stringWithKey:(NSString *)key table:(NSString *)table
{
//假如为跟随系统
if (self.languageType==HZLanguageTypeSystem) {
return NSLocalizedString(key, nil);
}

//返回对应国际化文字
if (_bundle) {// 修改成为,从另外一个文件找,先从文件找,如果找不到就从文件二找。
//NSLocalizedStringFromTableInBundleWithKey
return [self NSLocalizedStringFromTableInBundleWithKey:key table:table];
return NSLocalizedStringFromTableInBundle(key, table, _bundle, nil);



}

return NSLocalizedStringFromTable(key, table, nil);
}



/**

如果你的字符串资源文件名不是Localizable.strings,如KN.strings,那么你就得使用NSLocalizedStringFromTable()来读取本地化字符串:


*/
//{// 修改成为,从另外一个文件找,先从文件找,如果找不到就从文件二找。
- (NSString*)NSLocalizedStringFromTableInBundleWithKey:(NSString *)key table:(NSString *)table{
NSString* tmp = key;
if(_bundle){

tmp =NSLocalizedStringFromTableInBundle(key, table, _bundle, nil);

if([tmp isEqualToString:key]){
// 继续从备份表查找

NSLog(@"开始从Localizable1 找key:%@", tmp);
tmp =NSLocalizedStringFromTableInBundle(key, @"Localizable1", _bundle, nil);
// NSLog([NSString stringWithFormat:@"结束从Localizable1 找key:%@",tmp]);
NSLog(@"结束从Localizable1 找key:%@", tmp);


}
}
return

III info.plist 的国际化

  • 新建一个 .strings文件,叫做InfoPlist.strings(文件名必须是这个)
  • 新建以后,点击右侧的 localized 才有意义。
  • 关键的步骤来了,这时候,点击我们的info.plist,右键选择open as source code 查看我们所要获取权限的原始 key

iOS小技能:APP内的本地化语言切换(案例:登录界面切换中英文)_字符串_08

Info.plist中的CFBundleDisplayName的key决定了应用程序的名称

"NSMicrophoneUsageDescription" = "Allow this app to use your microphone for decibel detection and sound recording; audio is recorded locally only and is not sent to anyone, only for decibel detection and sound variation.";

IV 案例

4.1 《用户协议及隐私政策》弹框(包含超链接属性)【本文包含完整demo源码,demo支持中英文切换】

iOS《用户协议及隐私政策》弹框(包含超链接属性)【本文包含完整demo源码,demo支持中英文切换】 | 蓄力计划


4.2 登录界面切换中英文

  • AppDelegate: 设置默认的语言

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if([LanguageManager isEN] ){
// _titleLab.font = kPingFangFont(10);
[[LanguageManager defaultManager] changeLanguageType: HZLanguageTypeEnglish];

NSLog(@"当前位英文版本");

}else{
// _titleLab.font = kPingFangFont(12);
NSLog(@"当前位中文版本");
[[HZLanguageManager defaultManager] changeLanguageType: HZLanguageTypeChineseSimple];


}

}

​​github.com/zhangkn/Lan…​​

  • 搭建切换按钮:languageLab

处理切换中英文语言的动作事件

UILabel *)languageLab{
if (!_languageLab) {
_languageLab = [[UILabel alloc]init];
_languageLab.textColor = rgb(51,51,51);
_languageLab.font = kBoldFont(15);


_languageLab.textAlignment = NSTextAlignmentRight;



// 根据配置进行显示
// typedef NS_ENUM(NSUInteger, HZLanguageType) {
// HZLanguageTypeSystem,
// HZLanguageTypeEnglish,
// HZLanguageTypeChineseSimple,
// };

// 获取配置信息

//设置选中的语言
if ([HZLanguageManager defaultManager].currentLanguage==HZLanguageTypeSystem) {
// self.seletedIndex=@0;




}
else if ([HZLanguageManager defaultManager].currentLanguage==HZLanguageTypeChineseSimple) {
// self.seletedIndex=@1;
// _languageLab.text = @"";
_languageLab.text = @"English";


}
else if ([HZLanguageManager defaultManager].currentLanguage==HZLanguageTypeEnglish) {
// self.seletedIndex=@2;

_languageLab.text = @"简体中文";

}else{

// _languageLab.text = @"简体中文";


}




//
// ti ti阿佳
//

_languageLab.userInteractionEnabled = YES;




[self addSubview:_languageLab];
__weak __typeof__(self) weakSelf = self;

[_languageLab mas_makeConstraints:^(MASConstraintMaker *make) {

make.top.equalTo(weakSelf).offset(36);
make.right.equalTo(weakSelf).offset(-kAdjustRatio(16));

make.size.mas_equalTo(CGSizeMake(kAdjustRatio(100), kAdjustRatio(40)));



}];


UITapGestureRecognizer *cutTap = [[UITapGestureRecognizer alloc] init];
// __weak __typeof__(self) weakSelf = self;

[[cutTap rac_gestureSignal] subscribeNext:^(id x) {



switch ([HZLanguageManager defaultManager].currentLanguage) {
case HZLanguageTypeSystem:
{

}
break;

case HZLanguageTypeEnglish:
{

[[HZLanguageManager defaultManager] changeLanguageType: HZLanguageTypeChineseSimple];

weakSelf.languageLab.text = @"简体中文";

}
break;

case HZLanguageTypeChineseSimple:
{
[[HZLanguageManager defaultManager] changeLanguageType: HZLanguageTypeEnglish];

weakSelf.languageLab.text = @"English";

}
break;



default:
{

}
break;
}

[ AccountLoginView rootViewController4nil];


}];
[_languageLab addGestureRecognizer:cutTap];


}
return _languageLab;
}


+ (void) rootViewController4nil{

//销毁 root
UIWindow *oldWindow=[UIApplication sharedApplication].keyWindow;
oldWindow.rootViewController=nil;

//新 root
UIWindow *newWindow = [UIApplication sharedApplication].keyWindow;


AccountLoginViewController *VC = [[AccountLoginViewController alloc]init];

HWNavigationController *NA = [[HWNavigationController alloc]initWithRootViewController:VC];



[AccountLoginView restoreRootViewController:NA newWindow:newWindow];

}

/**
UIModalTransitionStyleCoverVertical = 0, //从下到上盖上进入

UIModalTransitionStyleFlipHorizontal, //水平翻转

UIModalTransitionStyleCrossDissolve, //渐变出现

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2

UIModalTransitionStylePartialCurl, //类似翻页的卷曲



@param rootViewController <#rootViewController description#>
@param newWindow <#newWindow description#>
https://github.com/codeRiding/CRProject/blob/165886b8426fa43b52e76e659b8e18496e2fa8c8/CRProject/Classes/Expand/Tool/CRChangeVC.m
*/
+ (void)restoreRootViewController:(UIViewController *)rootViewController newWindow:(UIWindow*)newWindow
{
typedef void (^Animation)(void);
UIWindow* window = newWindow;
//
rootViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
//UIModalPresentationPopover
//UIModalPresentationNone
// rootViewController.modalPresentationStyle = UIModalPresentationOverFullScreen;//UIModalPresentationOverFullScreen

// UIModalTransitionStyleCoverVertical = 0,
// UIModalTransitionStyleFlipHorizontal __TVOS_PROHIBITED,
// UIModalTransitionStyleCrossDissolve,
// UIModalTransitionStylePartialCurl
// rootViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
Animation animation = ^{
BOOL oldState = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
// [UIApplication sharedApplication].keyWindow.rootViewController = rootViewController;
[newWindow switchWithRootViewController:rootViewController];

[UIView setAnimationsEnabled:oldState];
};
//
[UIView transitionWithView:window
duration:0.5f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:animation
completion:nil];

}

4.3 本地化字符串指定参数顺序

对于在中英文语法的区别,在本地化字符串的时候,我们常常需要调整stringWithFormat的参数顺序

原理:在%和@中间加上数值,2$),数字代表参数的顺序

see also

  • iOS精品资源汇总:(持续更新)


  • ​​合并develop到master,并打版本tag​​


更多内容请关注​​#小程序:iOS逆向​​,只为你呈现有价值的信息,专注于移动端技术研究领域。

举报

相关推荐

0 条评论