iOSAPP内的国际化切换(案例:登录界面切换中英文)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: iOSAPP内的国际化切换(案例:登录界面切换中英文)

前言

原理:

  1. 自定义解析本地化字符串的工具类LanguageManager
  2. 应用内切换语言生效的技术实现:采用销毁根控制器,重新进入一次
  3. 本地化字符串指定参数顺序

划重点:

  1. 字符串的本地化
  2. 自定义解析本地化字符串的工具类LanguageManager、
  3. 例子:登录界面切换中英文(内含demo)

应用内切换语言生效的技术实现:

  1. 销毁根控制器,重新进入一次(采用) 2.通过发通知,到各个控制器更新文字(不采用)

从CSDN下载【iOS APP 内的国际化切换】demo源码:https://download.csdn.net/download/u011018979/19089505

image.png

image.png

I 字符串的本地化

1.1 用法

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

image.png

为Localizable.strings添加多语言支持

image.png

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

image.png

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"="提示";

image.png

  • 在代码中使用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

image.png

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支持中英文切换】 | 蓄力计划

https://kunnan.blog.csdn.net/article/details/103902362

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

see also

目录
相关文章
|
C#
WPF 界面实现多语言支持 中英文切换 动态加载资源字典
原文:WPF 界面实现多语言支持 中英文切换 动态加载资源字典 1、使用资源字典,首先新建两个字典文件en-us.xaml、zh-cn.xaml。定义中英文的字符串在这里面【注意:添加xmlns:s="clr-namespace:System;assembly=mscorlib】 zh-cn.
3179 0
|
24天前
|
存储 自然语言处理 小程序
微信小程序多语言切换神器:简繁体切换功能完全指南
随着全球化的发展,支持多种语言的应用程序愈发重要。本文介绍了如何在微信小程序中实现简体与繁体字体之间的切换功能,以满足不同地区用户的需求。通过创建utils文件夹并编写相应的转换函数,开发者可以方便地实现语言切换,从而提升用户体验。文章中还附带了示例代码和效果图,帮助读者更好地理解和应用这一功能。
72 0
微信小程序多语言切换神器:简繁体切换功能完全指南
|
6月前
|
移动开发
移动端H5不能自由选中文本进行复制的问题
移动端H5不能自由选中文本进行复制的问题
184 0
|
12月前
|
编解码
后台管理系统中英文版本切换
后台管理系统中英文版本切换
136 0
|
前端开发 JavaScript 测试技术
前端国际化辅助工具——自动替换中文并翻译
前端国际化辅助工具——自动替换中文并翻译
647 0
|
测试技术 数据库 数据安全/隐私保护
推荐一个牛逼的开源在线文档系统,支持Markdown和富文本编辑模式!
推荐一个牛逼的开源在线文档系统,支持Markdown和富文本编辑模式!
3672 0
推荐一个牛逼的开源在线文档系统,支持Markdown和富文本编辑模式!
|
JSON 小程序 数据格式
微信小程序中英文切换
最近公司接了个项目,客户那边要求要有中英翻译;本来以为在网上有类似的框架,后面找了大半天,最后还是自己动手丰衣足食; 这里有3个地方需要进行中英切换的,顶部页面标题,小程序内容页,还有底部tabBar;
338 0
|
PHP 自然语言处理
ThinkPHP3.2中英文切换!
小伙伴们好久不见!!! 最近公司项目版本升级,小梦已经忙成了狗,无暇顾及文章,今天抽时间写一篇助助兴! 用Thinkphp这个国产框架已经2年多了,现在有一个小功能:网站中英文切换功能,当然这个功能分为2种方案: 1、使用第...
1244 0
|
JSON JavaScript 前端开发
Struts2实现国际化操作及中英文切换(九)下
Struts2实现国际化操作及中英文切换(九)
179 0
Struts2实现国际化操作及中英文切换(九)下
|
前端开发 Java
Struts2实现国际化操作及中英文切换(九)上
Struts2实现国际化操作及中英文切换(九)
203 0
Struts2实现国际化操作及中英文切换(九)上