请选择 进入手机版 | 继续访问电脑版

[IOS] iOS文本的多语言适配以及实践指南

[复制链接]
查看31 | 回复5 | 2021-9-13 17:34:03 | 显示全部楼层 |阅读模式
目次

背景

产品被多个国家使用 ,产品方盼望 产品拥有更好的多语言使用 体验,以是 计划 师提供多种字体来适配指定的语言。基于以上背景,客户端必要 快速给出办理 方案并且上线。

字体包的多语言适配和实践

需求分析

起首 ,在相识 产品需求和计划 方案之后,连合 业务研发职员 的痛点,整理出以下需求。

产品和计划 的需求

  • 不同语言,对应字体包不雷同 。
  • 全局字体默认使用 计划 师指定的字体包。
  • 某些语言的字体包缺少某些字重版本,要求降级使用 下一个字重版本。
  • 存在某些特别 文案不使用 全局字体包(比方 :中文,它有专属的字体包,和语言环境无关)。
  • 产品迭代必要 快速支持扩展,只管 减少研发投入成本。

计划 师要求的字体包资源

iOS文本的多语言适配以及实践指南

研发的痛点和需求

  • 存在公用组件(其他业务线都在使用 ,伴鱼公共业务组件如今 有50+),不能修改通用组件。
  • 仅壳工程支持且依靠 字体包。
  • 字体包资源泉源 方式要机动 。

总结一下,产品和计划 的需求夸大 字体适配的全局性、多样性、可扩展性,研发关心的是解耦、职责单一、机动 性。

技术计划

分析过后,先确定技术框架的分层。

垂直分层和程度 模块

iOS文本的多语言适配以及实践指南

如图所示分3层,1.基础组件提供核心实现,并支持需求扩展 2.业务组件(无干系 修改)3.壳工程提供资源包和代理者。

FontPackage组件要负责什么?

  • FontPackageManager,负责绑定代理来获取资源包,控制流程逻辑。
  • FontPackageExtension,负责AOP,增长 文本属性来满足 特别 场景的多样性。
  • FontPackageModel,映射字体包资源的设置 信息,明白 了使用 协议。上层业务可以增长 和调整参数来设置 字体包资源。

壳工程的资源包设置

  • env:国际编码, default 表示计划 师指定的默认字体。留意 有些国际编码代表一种语言,比方 英语存在 en-US、en-GB 等多种编码,必要 同一 为 en。
  • font:字重范例 ,0:light、1:medium、2:bold。斜体默认更换 为medium
  • name:字体源文件的名称。比方 :GothamRndSSm-Medium

备注:由于 计划 师只要求3种字重,默认light字重,这个和体系 提供的 UIFontWeight 不太划一 。

  1. //壳工程中的配置文件,反序列化传回FontPackage层
  2. //appfont.json
  3. {
  4. "list": [{
  5. "env" : "vi",
  6. "note" : "越南语,按照国际编码:vi、vi-VN。FontPackageManager 判断国际编码来对应",
  7. "data" : [{
  8. "font" : 0,
  9. "name" : "genjyuu_light(越南细)"
  10. },{
  11. "font" : 1,
  12. "name" : "genjyuu_medium(越南中)"
  13. },{
  14. "font" : 2,
  15. "name" : "genjyuu_bold(越南粗)"
  16. }
  17. ]
  18. }, {
  19. "env" : "default",
  20. "note" : "其他语种默认使用字体,但优先判断设备的国际编码来匹配字体包",
  21. "data" : [{
  22. "font" : 0,
  23. "name" : "GothamRndSSm-Light"
  24. },{
  25. "font" : 1,
  26. "name" : "GothamRndSSm-Medium"
  27. },{
  28. "font" : 2,
  29. "name" : "GothamRndSSm-Bold"
  30. }
  31. ]
  32. }
  33. ]}
复制代码

添加字体包和设置 文件,还有冷启动流程:

冷启动流程图

iOS文本的多语言适配以及实践指南

技术开辟

FontPackage 功能组件共3个Class,200+行代码。

起首 ,在冷启动时间 FontPackage 根据 json 设置 缓存语言编码匹配到的字体包资源 Model。

然后使用 runtime hook UIFont 类的几个构造函数,更换构造函数的 fontName 参数。如今 确定5个构造函数:

  1. //已处理
  2. + (UIFont *)systemFontOfSize:(CGFloat)fontSize;
  3. + (UIFont *)systemFontOfSize:(CGFloat)fontSize weight:(UIFontWeight)weight;
  4. + (UIFont *)boldSystemFontOfSize:(CGFloat)fontSize;
  5. + (UIFont *)italicSystemFontOfSize:(CGFloat)fontSize;
  6. + (UIFont *)fontWithName:(NSString *)fontName size:(CGFloat)fontSize;
复制代码

末了 同一 使用 +fontWithName:size: 函数初始化,fontName 为自定义字体包。

函数 -fontpackage_name: 根据原 fontName 更换为对应的自定义字体包。

  1. //FontPackageExtension.m
  2. //UIFont+FontPackage.m
  3. + (UIFont *)xxxFontPackage_systemFontOfSize:(CGFloat)fontSize weight:(UIFontWeight)weight {
  4. NSString *fontName = @"";
  5. if (weight == UIFontWeightMedium) {
  6. fontName = @"medium";
  7. } else if (weight > UIFontWeightMedium) {
  8. fontName = @"bold";
  9. }
  10. return [self fontWithName:fontName size:fontSize];
  11. }
  12. + (UIFont *)xxxFontPackage_italicSystemFontOfSize:(CGFloat)fontSize {
  13. //斜体默认是medium
  14. return [self fontWithName:@"medium" size:fontSize];
  15. }
  16. + (UIFont *)xxxFontPackage_boldSystemFontOfSize:(CGFloat)fontSize {
  17. return [self fontWithName:@"bold" size:fontSize];
  18. }
  19. + (UIFont *)xxxFontPackage_systemFontOfSize:(CGFloat)fontSize {
  20. return [self fontWithName:@"" size:fontSize];
  21. }
  22. + (UIFont *)xxxFontPackage_fontWithName:(NSString *)fontName size:(CGFloat)fontSize {
  23. fontName = [self fontpackage_name:fontName];
  24. return [self xxxFontPackage_fontWithName:fontName size:fontSize];
  25. }
  26. + (NSString *)fontpackage_name:(NSString *)fontName {
  27. fontName = [fontName lowercaseString];
  28. FontPackageFont replaceFont = FontPackageFontLight; //默认light
  29. if ([fontName containsString:@"medium"]) {
  30. replaceFont = FontPackageFontMedium;
  31. } else if ([fontName containsString:@"bold"]) {
  32. replaceFont = FontPackageFontBold;
  33. }
  34. //匹配替换的字体
  35. NSString *replaceFontName = [[FontPackageManager shareInstance].fontPackageInfo.dataMap objectForKey:@(replaceFont)];
  36. return replaceFontName;
  37. }
复制代码

文本信息的多语言适配和实践

针对海外用户做语言本地化也是一项告急 的产品功能,但很多组件在开辟 之初并未预留本地化拓展的接口,客户端必要 提供一套优雅的办理 方案来应对此题目 。

需求分析

1、产品和计划 的需求

  • 语言本地化
  • 未提供本地化的语言,默认使用 产品指定的语言
  • 快速支持新语言本地化

2、技术要求

  • 接入成本低,不必要 对成熟组件做改动
  • 解耦,其他组件无需依靠 本功能

技术计划

垂直分层和程度 模块

iOS文本的多语言适配以及实践指南

如图所示分3层:1、基础组件提供需求扩展 2、业务组件(基本不必要 修改,如有特别 属性需求可以依靠 基础组件)3、壳工程提供资源包和以及资源包的更新

LocalizedString 组件要负责什么?

  • LocalizedString,负责笔墨 本地化适配。
  • LocalizedTool,负责语言包的设置 、读取、更换功能。
  • LocalizedExtension,负责AOP,补充某些属性。

语言包目次 如下:

语言包目次

iOS文本的多语言适配以及实践指南

可以看到,语言包是按照语言码举行 定名 的,方便在使用 中及时定位到对应文件并读取(存在多种编码的语言,同一 使用 其基础类)。同时,在壳工程中会对本地语言包举行 革新 ,App启动后会检查是否有新的语言包可用,假如 有会保证数据同步。

设置 好语言包后,接下来必要 冷启动时初始化LocalizedString 组件。启动时组件使命 流程图如下:

冷启动流程图

iOS文本的多语言适配以及实践指南

技术开辟

思量 到字符串终极 都会依托于 UILabel 举行 展示,[UILabel setText:]会作为设置展示文本的唯一收口。以是 我们对[UILabel setText:]举行 了 hook 和拓展,其内部操作流程图如下:

AOP流程图

iOS文本的多语言适配以及实践指南

LocalizedString 组件有 NSString、UILabel 分类分别做了属性拓展。详细 代码如下:

  1. @interface UILabel (Localized)
  2. @property (nonatomic, assign) BOOL isAutoLocalized; ///< 设置的文字是否要自动转换成本地化的语言,默认YES
  3. @end
  4. @interface NSString (Localized)
  5. @property (nonatomic, copy) NSString *oriStr; ///< 上次本地化的字符串原始值
  6. @property (nonatomic, copy) NSString *localizedStr; ///< oriStr 本地化后的字符串
  7. @end
复制代码

对 UILabel 的分类拓展可以判断 Label 是否必要 被本地化;对 NSString 的分类拓展会对本地化后的结果 举行 缓存,当同一个 string 对象再次本地化时,可以快速从缓存拿到结果 减少在 map 中的检索次数、进步 服从 。类拓展的方式也保证了本组件的侵入性极低。
整个工程使用 了 pod 举行 集成,基础组件无需声明依靠 ,对本组件有依靠 要求的只在特定业务中出现。hook + pod 的方式保证了本组件的机动 使用 和充分解耦。

与NSLocalizedString的兼容

从上面的流程先容 可以看到,本地化更换 发生在对 Label 设置文本的时间 ,不同于 NSLocalizedString 必要 先显式本地化再设置文本的方式。以是 ,当使用 方提前对文本举行 了本地化,本组件的自动 本地化不见效 。思量 到本组件重要 应用于新语言地区,NSLocalizedString 尚未设置 对应的结果 ,故如今 仍旧 可以使用 本组件兜底。我们也会后续优化本组件,完成与 NSLocalizedString 的兼容,更加方便本组的使用 。

拓展

由于上述方法只实用 于[UILabel setText:]这种情势 的无侵入调整,对于字符串拼接的环境 ,仍必要 开辟 职员 使用 LocalizedString 类对子串举行 逐一本地化。同时,为了支持以后大概 的应用内变更语言,LocalizedString 也提供了动态变更语言包功能。LocalizedString 重要 API 如下:

  1. /**
  2. @brief 直接返回指定 key 对应的 本地化文字
  3. @param key 转译文件表中的key
  4. */
  5. + (NSString *)forKey:(NSString *)key;
  6. /**
  7. @brief 根据指定的 language code,返回key 对应的 本地化文字
  8. @param key 转译文件表中的key
  9. @param langCode语言编码
  10. */
  11. + (NSString *)forKey:(NSString *)key langCode:(NSString *)langCode;
  12. /**
  13. @brief 设置当前默认的语言编码
  14. @param langCode语言编码
  15. */
  16. + (void)setCurrentLangCode:(NSString *)langCode;
复制代码

总结

在多个产品同时的迭代环境 下,使用 组件化已经变得非常广泛 ,不断地重构优化组件来保证低耦合。当面对 国际化场景时,必要 沉淀打磨国际化适配框架来支持 业务高效迭代,并且不能给其他业务造成负担。

如今 以上功能都已上线,满足 了产品的需求,办理 了研发的痛点。

到此这篇关于iOS文本的多语言适配以及实践的文章就先容 到这了,更多干系 iOS文本多语言适配内容请搜刮 脚本之家从前 的文章或继续欣赏 下面的干系 文章盼望 大家以后多多支持脚本之家!

作者先容

  • 吕洪阳,伴鱼 iOS 工程师,伴鱼绘本iOS端负责人
  • 赵杰,伴鱼 iOS 工程师,负责伴鱼绘本客户端研发,功能降级框架等工作

参考

  • https://developer.apple.com/documentation/uikit/uifont
  • http://www.lingoes.cn/zh/translator/langcode.htm

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 123456809 | 2021-9-26 22:39:03 | 显示全部楼层
强,我和我的小伙伴们都惊呆了!
回复

使用道具 举报

avatar 刘金栋 | 2021-10-4 09:58:25 | 显示全部楼层
admin楼主是一个神奇的青年!
回复

使用道具 举报

这么好的帖子,应该加精华!
回复

使用道具 举报

看帖不回帖都是耍流氓!
回复

使用道具 举报

avatar 兔仔妹致 | 昨天 20:29 | 显示全部楼层
admin楼主的帖子提神醒脑啊!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则