当前位置: 首页 > news >正文

iOS开发之APP内部切换语言

前言

iOS开发中,随着APP越来越完善,很多APP都做了国际化,也实现了APP内部切换语言。

原理

国际化都会走到NSBundle的- (NSString *)localizedStringForKey:(NSString *)key value:(nullable NSString *)value table:(nullable NSString *)tableName方法,使用自定义Bundle替换NSBundle,通过切换根控制器来完成APP内部切换语言。

1.拦截国际化方法

通过分类和runtime来实现拦截,将Bundle替换为自定义CLBundle。

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //动态继承、交换,方法类似KVO,通过修改[NSBundle mainBundle]对象的isa指针,使其指向它的子类CLBundle,这样便可以调用子类的方法;其实这里也可以使用method_swizzling来交换mainBundle的实现,来动态判断,可以同样实现。
        object_setClass([NSBundle mainBundle], [CLBundle class]);
    });
}
复制代码

2.重写方法判断加载哪一种语言

重写mainBundle方法,通过语言管理类获取当前应该加载哪一种语言。

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName {
    if ([CLBundle cl_mainBundle]) {
        return [[CLBundle cl_mainBundle] localizedStringForKey:key value:value table:tableName];
    } else {
        return [super localizedStringForKey:key value:value table:tableName];
    }
}

+ (NSBundle *)cl_mainBundle {
    if ([NSBundle currentLanguage].length) {
        NSString *path = [[NSBundle mainBundle] pathForResource:[NSBundle currentLanguage] ofType:@"lproj"];
        if (path.length) {
            return [NSBundle bundleWithPath:path];
        }
    }
    return nil;
}

+ (NSString *)currentLanguage {
    return [CLLanguageManager userLanguage] ? : [NSLocale preferredLanguages].firstObject;
}
复制代码

3.语言管理类

将语言切换调用设置简单封装,方便调用。

+ (void)setUserLanguage:(NSString *)userLanguage {
    //跟随手机系统
    if (!userLanguage.length) {
        [self resetSystemLanguage];
        return;
    }
    //用户自定义
    [[NSUserDefaults standardUserDefaults] setValue:userLanguage forKey:CLUserLanguageKey];
    [[NSUserDefaults standardUserDefaults] setValue:@[userLanguage] forKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

+ (NSString *)userLanguage {
    return [[NSUserDefaults standardUserDefaults] valueForKey:CLUserLanguageKey];
}

/**
 重置系统语言
 */
+ (void)resetSystemLanguage {
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:CLUserLanguageKey];
    [[NSUserDefaults standardUserDefaults] setValue:nil forKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}
复制代码

4.切换根控制器来切换语言

为了减少工作量,直接切换根控制器来达到语言切换。

[CLLanguageManager setUserLanguage:@"en"];
 CLTabbarController *tabbarController = [[CLTabbarController alloc] init];
tabbarController.selectedIndex = 3;
UINavigationController *navigationController = tabbarController.selectedViewController;
NSMutableArray *viewControllers = navigationController.viewControllers.mutableCopy;
    //取出我的页面,提前加载,解决返回按钮不变化
 CLMyController *me = (CLMyController *)[viewControllers firstObject];
[me loadViewIfNeeded];
    //新建设置语言页面
CLChangeLanguageController *languageController = [[CLChangeLanguageController alloc] init];
languageController.hidesBottomBarWhenPushed = YES;
[viewControllers addObject:languageController];
    //解决奇怪的动画bug。
dispatch_async(dispatch_get_main_queue(), ^{
    [UIApplication sharedApplication].keyWindow.rootViewController = tabbarController;
    navigationController.viewControllers = viewControllers;
    CLLog(@"已切换到语言 %@", [NSBundle currentLanguage]);
});
复制代码

效果图

总结

以上是使用国际化来实现APP内部切换语言,希望能够给大家帮助,demo地址--->>CLDemo

相关文章:

  • Thanos如何让Prometheus更易用?
  • Spring任务调度之Spring-Task
  • P5112 FZOUTSY
  • java B2B2C springmvc mybatis多租户电子商城系统- 路由定位器
  • linux对vxlan的支持
  • Mysql优化
  • 3.1Python的判断选择语句
  • 深度解析ES6通过WeakMap解决内存泄漏问题
  • Redis 和 memcache 简单比较
  • verilog语法实例学习(1)
  • Docker三剑客之docker-machine
  • 正者表达式exec和match
  • Linux操作系统有什么吸引力,在程序员中这么受欢迎!
  • Oracle常用语句
  • Ubuntu Vscode安装
  • 【Leetcode】101. 对称二叉树
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • Android框架之Volley
  • C++11: atomic 头文件
  • canvas 高仿 Apple Watch 表盘
  • eclipse(luna)创建web工程
  • React系列之 Redux 架构模式
  • React组件设计模式(一)
  • select2 取值 遍历 设置默认值
  • 成为一名优秀的Developer的书单
  • 基于组件的设计工作流与界面抽象
  • 排序(1):冒泡排序
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • 新海诚画集[秒速5センチメートル:樱花抄·春]
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​queue --- 一个同步的队列类​
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • #162 (Div. 2)
  • $.each()与$(selector).each()
  • (MATLAB)第五章-矩阵运算
  • (windows2012共享文件夹和防火墙设置
  • (力扣)1314.矩阵区域和
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (十三)Java springcloud B2B2C o2o多用户商城 springcloud架构 - SSO单点登录之OAuth2.0 根据token获取用户信息(4)...
  • (十一)手动添加用户和文件的特殊权限
  • (一)u-boot-nand.bin的下载
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • (转)项目管理杂谈-我所期望的新人
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .NET 分布式技术比较
  • .NET国产化改造探索(一)、VMware安装银河麒麟
  • .net最好用的JSON类Newtonsoft.Json获取多级数据SelectToken
  • /dev/sda2 is mounted; will not make a filesystem here!
  • [20171113]修改表结构删除列相关问题4.txt
  • [android] 天气app布局练习
  • [BUUCTF NewStarCTF 2023 公开赛道] week4 crypto/pwn
  • [bzoj4240] 有趣的家庭菜园
  • [c++] 单例模式 + cyberrt TimingWheel 单例分析