前言
原理:
- 自定义解析本地化字符串的工具类LanguageManager
- 应用内切换语言生效的技术实现:采用销毁根控制器,重新进入一次
- 本地化字符串指定参数顺序
划重点:
- 字符串的本地化
- 自定义解析本地化字符串的工具类LanguageManager、
- 例子:登录界面切换中英文(内含demo)
应用内切换语言生效的技术实现:
- 销毁根控制器,重新进入一次(采用) 2.通过发通知,到各个控制器更新文字(不采用)
从CSDN下载【iOS APP 内的国际化切换】demo源码:https://download.csdn.net/download/u011018979/19089505
I 字符串的本地化
1.1 用法
- 创建字符串资源文件Localizable.strings
为Localizable.strings添加多语言支持
选 make Localize代表会将Localizable.strings加入到英语的本地化文件夹en.lproj中去
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"="提示";
- 在代码中使用NSLocalizedString(key, comment)来读取本地化字符串,key是Localizable.strings文件中等号左边的字符串,comment纯粹是注释
#define NSLocalizedString(key, comment) \ [NSBundle.mainBundle localizedStringForKey:(key) value:@"" table:nil] #define NSLocalizedStringFromTable(key, tbl, comment) \ [NSBundle.mainBundle localizedStringForKey:(key) value:@"" table:(tbl)] #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \ [bundle localizedStringForKey:(key) value:@"" table:(tbl)] #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \ [bundle localizedStringForKey:(key) value:(val) table:(tbl)]
这里使用封装的宏进行读取本地化字符串
//程序的本地化,引用国际化的文件 #define QCTLocal(x, ...) HZLocalizedString(x, nil) #define HZLocalizedString(key, comment) HZLocalizedStringFromTable(key, @"Localizable", nil) #define HZLocalizedStringFromTable(key, tbl, comment) [[HZLanguageManager defaultManager] stringWithKey:key table:tbl] /** 国际化 @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这个字符串
1.2 本地化字符串指定参数顺序
对于在中英文语法的区别,在本地化字符串的时候,我们常常需要调整stringWithFormat的参数顺序
- iOS本地化字符串指定参数顺序:应用场景:app内的多语言切换
文章:https://kunnan.blog.csdn.net/article/details/108793150 原理:在%和@中间加上数值,2$),数字代表参数的顺序
"FORMAT" = "I am %2$@ from '%1$@'";
II、 自定义解析本地化字符串的工具类LanguageManager
2.1 代码实现
- LanguageManager
从CSDN下载【iOS APP 内的国际化切换】demo源码:https://download.csdn.net/download/u011018979/19089505
#import <Foundation/Foundation.h> #define HZLocalizedString(key, comment) HZLocalizedStringFromTable(key, @"Localizable", nil) #define HZLocalizedStringFromTable(key, tbl, comment) [[HZLanguageManager defaultManager] stringWithKey:key table:tbl] 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 )isEN;
- 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 tmp; }
III 、 info.plist 的国际化
- 新建一个 .strings文件,叫做InfoPlist.strings(文件名必须是这个)
- 新建以后,点击右侧的 localized 才有意义。
- 关键的步骤来了,这时候,点击我们的info.plist,右键选择open as source code 查看我们所要获取权限的原始 key
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]; } }
https://github.com/zhangkn/LanguageManager/tree/main/iUIPageControlFix4iOS14/LanguageManager
- 搭建切换按钮: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$),数字代表参数的顺序 文章:https://blog.csdn.net/z929118967/article/details/108793150