iOS文本的多語言適配和實踐

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"產品被多個國家使用,產品方希望產品擁有更好的多語言使用體驗,所以設計師提供多種字體來適配指定的語言。基於以上背景,客戶端需要快速給出解決方案並且上線。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"字體包的多語言適配和實踐"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"需求分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,在瞭解產品需求和設計方案之後,結合業務研發人員的痛點,整理出以下需求。"}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"產品和設計的需求"}]}]}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不同語言,對應字體包不相同。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"全局字體默認使用設計師指定的字體包。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"某些語言的字體包缺少某些字重版本,要求降級使用下一個字重版本。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"存在某些特殊文案不使用全局字體包(例如:中文,它有專屬的字體包,和語言環境無關)。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"產品迭代需要快速支持擴展,儘量減少研發投入成本。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"設計師要求的字體包資源"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/4e\/4e3f408855dc8b7d343441bc90a2a06a.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"研發的痛點和需求"}]}]}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"存在公用組件(其他業務線都在使用,伴魚公共業務組件目前有50+),不能修改通用組件。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"僅殼工程支持且依賴字體包。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"字體包資源來源方式要靈活。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總結一下,產品和設計的需求強調字體適配的全局性、多樣性、可擴展性,研發關心的是解耦、職責單一、靈活性。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"技術設計"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分析過後,先確定技術框架的分層。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"垂直分層和水平模塊"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/b6\/b6744132240c02863881344feeaf59f1.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如圖所示分3層,1.基礎組件提供核心實現,並支持需求擴展 2.業務組件(無相關修改)3.殼工程提供資源包和代理者。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"FontPackage組件要負責什麼?"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FontPackageManager,負責綁定代理來獲取資源包,控制流程邏輯。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FontPackageExtension,負責AOP,增加文本屬性來滿足特殊場景的多樣性。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FontPackageModel,映射字體包資源的配置信息,明確了使用協議。上層業務可以增加和調整參數來配置字體包資源。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"殼工程的資源包配置"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"env:國際編碼, default 表示設計師指定的默認字體。注意有些國際編碼代表一種語言,例如英語存在 en-US、en-GB 等多種編碼,需要統一爲 en。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"font:字重類型,0:light、1:medium、2:bold。斜體默認替換爲medium"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"name:字體源文件的名稱。例如:GothamRndSSm-Medium"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"備註:因爲設計師只要求3種字重,默認light字重,這個和系統提供的 "},{"type":"link","attrs":{"href":"https:\/\/developer.apple.com\/documentation\/uikit\/uifontweight","title":null,"type":null},"content":[{"type":"text","text":"UIFontWeight"}]},{"type":"text","text":" 不太一致。"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"\/\/殼工程中的配置文件,反序列化傳回FontPackage層\n\/\/appfont.json\n{\n \"list\": [{\n \"env\" : \"vi\",\n \"note\" : \"越南語,按照國際編碼:vi、vi-VN。FontPackageManager 判斷國際編碼來對應\",\n \"data\" : [{\n \"font\" : 0,\n \"name\" : \"genjyuu_light(越南細)\"\n },{\n \"font\" : 1,\n \"name\" : \"genjyuu_medium(越南中)\"\n },{\n \"font\" : 2,\n \"name\" : \"genjyuu_bold(越南粗)\"\n }\n ]\n }, {\n \"env\" : \"default\",\n \"note\" : \"其他語種默認使用字體,但優先判斷設備的國際編碼來匹配字體包\",\n \"data\" : [{\n \"font\" : 0,\n \"name\" : \"GothamRndSSm-Light\"\n },{\n \"font\" : 1,\n \"name\" : \"GothamRndSSm-Medium\"\n },{\n \"font\" : 2,\n \"name\" : \"GothamRndSSm-Bold\"\n }\n ]\n }\n]}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"添加字體包和配置文件,還有冷啓動流程:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"冷啓動流程圖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/cb\/cb97aba2022863cedffb17c9063149e9.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"技術開發"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FontPackage 功能組件共3個Class,200+行代碼。首先,在冷啓動時候 FontPackage 根據 json 配置緩存語言編碼匹配到的字體包資源 Model。然後使用 runtime hook UIFont 類的幾個構造函數,更換構造函數的 "},{"type":"link","attrs":{"href":"https:\/\/developer.apple.com\/documentation\/uikit\/uifont\/1619033-familyname","title":null,"type":null},"content":[{"type":"text","text":"fontName"}]},{"type":"text","text":" 參數。目前確定5個構造函數:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"\/\/已處理\n+ (UIFont *)systemFontOfSize:(CGFloat)fontSize;\n+ (UIFont *)systemFontOfSize:(CGFloat)fontSize weight:(UIFontWeight)weight;\n+ (UIFont *)boldSystemFontOfSize:(CGFloat)fontSize;\n+ (UIFont *)italicSystemFontOfSize:(CGFloat)fontSize;\n+ (UIFont *)fontWithName:(NSString *)fontName size:(CGFloat)fontSize;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後統一使用 "},{"type":"codeinline","content":[{"type":"text","text":"+fontWithName:size:"}]},{"type":"text","text":" 函數初始化,"},{"type":"link","attrs":{"href":"https:\/\/developer.apple.com\/documentation\/uikit\/uifont\/1619033-familyname","title":null,"type":null},"content":[{"type":"text","text":"fontName"}]},{"type":"text","text":" 爲自定義字體包。函數 "},{"type":"codeinline","content":[{"type":"text","text":"-fontpackage_name:"}]},{"type":"text","text":" 根據原 "},{"type":"link","attrs":{"href":"https:\/\/developer.apple.com\/documentation\/uikit\/uifont\/1619033-familyname","title":null,"type":null},"content":[{"type":"text","text":"fontName"}]},{"type":"text","text":" 更換爲對應的自定義字體包。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"\/\/FontPackageExtension.m \n\/\/UIFont+FontPackage.m\n\n\n\n+ (UIFont *)xxxFontPackage_systemFontOfSize:(CGFloat)fontSize weight:(UIFontWeight)weight {\n NSString *fontName = @\"\";\n if (weight == UIFontWeightMedium) {\n fontName = @\"medium\";\n } else if (weight > UIFontWeightMedium) {\n fontName = @\"bold\";\n }\n return [self fontWithName:fontName size:fontSize];\n}\n\n+ (UIFont *)xxxFontPackage_italicSystemFontOfSize:(CGFloat)fontSize {\n \/\/斜體默認是medium\n return [self fontWithName:@\"medium\" size:fontSize];\n}\n\n+ (UIFont *)xxxFontPackage_boldSystemFontOfSize:(CGFloat)fontSize {\n return [self fontWithName:@\"bold\" size:fontSize];\n}\n\n+ (UIFont *)xxxFontPackage_systemFontOfSize:(CGFloat)fontSize {\n return [self fontWithName:@\"\" size:fontSize];\n}\n\n+ (UIFont *)xxxFontPackage_fontWithName:(NSString *)fontName size:(CGFloat)fontSize {\n fontName = [self fontpackage_name:fontName];\n return [self xxxFontPackage_fontWithName:fontName size:fontSize];\n}\n\n\n\n+ (NSString *)fontpackage_name:(NSString *)fontName {\n fontName = [fontName lowercaseString];\n FontPackageFont replaceFont = FontPackageFontLight; \/\/默認light\n if ([fontName containsString:@\"medium\"]) {\n replaceFont = FontPackageFontMedium;\n } else if ([fontName containsString:@\"bold\"]) {\n replaceFont = FontPackageFontBold;\n }\n \/\/匹配替換的字體\n NSString *replaceFontName = [[FontPackageManager shareInstance].fontPackageInfo.dataMap objectForKey:@(replaceFont)];\n return replaceFontName;\n}"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"文本信息的多語言適配和實踐"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"針對海外用戶做語言本地化也是一項重要的產品功能,但很多組件在開發之初並未預留本地化拓展的接口,客戶端需要提供一套優雅的解決方案來應對此問題。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"需求分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、"},{"type":"text","marks":[{"type":"strong"}],"text":"產品和設計的需求"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"語言本地化"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"未提供本地化的語言,默認使用產品指定的語言"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"快速支持新語言本地化"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、"},{"type":"text","marks":[{"type":"strong"}],"text":"技術要求"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接入成本低,不需要對成熟組件做改動"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解耦,其他組件無需依賴本功能"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"技術設計"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"垂直分層和水平模塊"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/da\/dabe77ded11430fb4d1cc532161f1092.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如圖所示分3層:1、基礎組件提供需求擴展 2、業務組件(基本不需要修改,如有特殊屬性需求可以依賴基礎組件)3、殼工程提供資源包和以及資源包的更新"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"LocalizedString 組件要負責什麼?"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LocalizedString,負責文字本地化適配。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LocalizedTool,負責語言包的配置、讀取、更換功能。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LocalizedExtension,負責AOP,補充某些屬性。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"語言包目錄如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"語言包目錄"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/cd\/cdfcea8a4451212e79a64c6633d3e40d.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到,語言包是按照語言碼進行命名的,方便在使用中及時定位到對應文件並讀取(存在多種編碼的語言,統一使用其基礎類)。同時,在殼工程中會對本地語言包進行刷新,App啓動後會檢查是否有新的語言包可用,如果有會保證數據同步。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置好語言包後,接下來需要冷啓動時初始化LocalizedString 組件。啓動時組件任務流程圖如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"冷啓動流程圖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/bb\/bb2b65413274e448efbba349b4502743.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"技術開發"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"考慮到字符串最終都會依託於 UILabel 進行展示,"},{"type":"codeinline","content":[{"type":"text","text":"[UILabel setText:]"}]},{"type":"text","text":"會作爲設置展示文本的唯一收口。所以我們對"},{"type":"codeinline","content":[{"type":"text","text":"[UILabel setText:]"}]},{"type":"text","text":"進行了 hook 和拓展,其內部操作流程圖如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"AOP流程圖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/99\/99d625af1e041f63288a6d2247c658e6.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LocalizedString 組件有 NSString、UILabel 分類分別做了屬性拓展。具體代碼如下:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"@interface UILabel (Localized)\n@property (nonatomic, assign) BOOL isAutoLocalized; \/\/\/< 設置的文字是否要自動轉換成本地化的語言,默認YES\n@end\n\n@interface NSString (Localized)\n@property (nonatomic, copy) NSString *oriStr; \/\/\/< 上次本地化的字符串原始值\n@property (nonatomic, copy) NSString *localizedStr; \/\/\/< oriStr 本地化後的字符串\n@end"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對 UILabel 的分類拓展可以判斷 Label 是否需要被本地化;對 NSString 的分類拓展會對本地化後的結果進行緩存,當同一個 string 對象再次本地化時,可以快速從緩存拿到結果減少在 map 中的檢索次數、提高效率。類拓展的方式也保證了本組件的侵入性極低。整個工程使用了 pod 進行集成,基礎組件無需聲明依賴,對本組件有依賴要求的只在特定業務中出現。hook + pod 的方式保證了本組件的靈活使用和充分解耦。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"與NSLocalizedString的兼容"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從上面的流程介紹可以看到,本地化替換髮生在對 Label 設置文本的時候,不同於 NSLocalizedString 需要先顯式本地化再設置文本的方式。所以,當使用方提前對文本進行了本地化,本組件的自動本地化不生效。考慮到本組件主要應用於新語言地區,NSLocalizedString 尚未配置對應的結果,故目前仍然可以使用本組件兜底。我們也會後續優化本組件,完成與 NSLocalizedString 的兼容,更加方便本組的使用。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"拓展"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於上述方法只適用於"},{"type":"codeinline","content":[{"type":"text","text":"[UILabel setText:]"}]},{"type":"text","text":"這種形式的無侵入調整,對於字符串拼接的情況,仍需要開發人員使用 LocalizedString 類對子串進行逐一本地化。同時,爲了支持以後可能的應用內變更語言,LocalizedString 也提供了動態變更語言包功能。LocalizedString 主要 API 如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"\/**\n @brief 直接返回指定 key 對應的 本地化文字\n @param key 轉譯文件表中的key\n *\/\n+ (NSString *)forKey:(NSString *)key;\n\n \/**\n @brief 根據指定的 language code,返回key 對應的 本地化文字\n @param key 轉譯文件表中的key\n @param langCode語言編碼\n *\/\n+ (NSString *)forKey:(NSString *)key langCode:(NSString *)langCode;\n\n\/**\n @brief 設置當前默認的語言編碼\n @param langCode語言編碼\n *\/\n+ (void)setCurrentLangCode:(NSString *)langCode;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在多個產品同時的迭代情況下,使用組件化已經變得非常普遍,不斷地重構優化組件來保證低耦合。當面對國際化場景時,需要沉澱打磨國際化適配框架來支撐業務高效迭代,並且不能給其他業務造成負擔。目前以上功能都已上線,滿足了產品的需求,解決了研發的痛點。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"作者介紹"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"呂洪陽,伴魚 iOS 工程師,伴魚繪本iOS端負責人"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"趙傑,伴魚 iOS 工程師,負責伴魚繪本客戶端研發,功能降級框架等工作"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"參考"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/developer.apple.com\/documentation\/uikit\/uifont","title":null,"type":null},"content":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uifont"}]},{"type":"link","attrs":{"href":"http:\/\/www.lingoes.cn\/zh\/translator\/langcode.htm","title":null,"type":null},"content":[{"type":"text","text":"http:\/\/www.lingoes.cn\/zh\/translator\/langcode.htm"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:呂洪陽、趙傑"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文:https:\/\/tech.ipalfish.com\/blog\/2021\/08\/29\/reading_ios_internationlization\/"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文:iOS文本的多語言適配和實踐"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"來源:伴魚技術博客"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"轉載:著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章