美文网首页
多语言框架的设计

多语言框架的设计

作者: 啧啧同学 | 来源:发表于2020-08-05 13:04 被阅读0次

app根据服务区域不同,比如香港地区, 除了支持通用的简体外,还需要支持繁体和英文

基本的Xcode配置即Strings文件的创建不在此介绍

多语言主要是处理两类:文字 + 图片

调用方的实现API

"公用国际化文本".cLocal  

"与当前文件同名的国际化文本".local()

"登录模块文案中的国际化".loginLocal()

"从指定table中获取国际化文本".fLocal("loginViewController")

"imageName".localImageName()// 对应语言的图片
1.通过枚举来定义app支持的语言类型
@objc
enum YFLanguage: Int {
    case English = 1                //英文
    case SimpleCh = 2               //简体中文
    case TraditionCh = 3            //繁体中文
    
    //默认跟随系统语言,若系统语言为中英文外的其他语言,统一设置为英文
    init() {
        let bundleLang = NSLocale.preferredLanguages.first ?? "en"
        self = YFLanguage.language(from: bundleLang)
    }
    
    var bundleName: String {
        switch self {
        case .English:
            return "en"
        case .SimpleCh:
            return "zh-Hans"
        case .TraditionCh:
            return "zh-Hant"
        }
    }
    
    var uniformCode: String {
        switch self {
        case .English:
            return "en"
        case .SimpleCh:
            return "zh-hans"
        case .TraditionCh:
            return "zh-hant"
        }
    }
    
    private static func language(from appleLanguage: String) -> YFLanguage {
        let dic: [String: YFLanguage] = [
            "en": .English,
            "zh-Hans": .SimpleCh,
            "zh-Hant": .TraditionCh
        ]
        var didFind: YFLanguage?
        for (key, value) in dic {
            if appleLanguage.hasPrefix(key) {
                didFind = value
            }
        }
        let final = didFind ?? .English
        return final
    }
}

2.为了对上层调用方更友好,对系统的NSLocalizedString提供的API进行二次封装,并对外提供本地语言缓存及切换语言的操作API
@objcMembers
class FCLocalization: NSObject {
    static let shared = FCLocalization()
    
    /// 获取当前语言环境下的文本
    /// - Parameters:
    ///   - key: 待替换文本
    ///   - table: 国际化文本table: 如果为nil,则直接从Localizable.strings获取
    /// - Returns: 当前语言环境下的文本
    static func getString(key: String, table: String?) -> String {
        guard let bundle = shared.currentBundle else {
            return NSLocalizedString(key, tableName: table,  comment: "")
        }
        
        return NSLocalizedString(key, tableName: table, bundle: bundle, comment: "")
    }
    
     /// 获取目标bundle语言环境下的文本
    /// - Parameters:
    ///   - key: 待替换文本
    ///   - table: 国际化文本table: 如果为nil,则直接从Localizable.strings获取
    ///   - bundle: 目标语言bundle
    /// - Returns: bundle对应的国际化文本
    static func getString(key: String, table: String?, bundle: Bundle) -> String {
        let languageBundle = shared.languageBundle(in: bundle)
        return NSLocalizedString(key, tableName: table, bundle: languageBundle, comment: "")
    }
    
    static func uniformLanguageCode() -> String {
        return shared.language.uniformCode
    }
    
    override init() {
        super.init()
        Bundle.localizationMode
    }
    
    //MARK: public
    func uniformCode() -> String {
        return language.uniformCode
    }
    
    /** App启动时初始化语言设置 */
    func initializeLanguage() {
        let set = Int(YFStorage.shared.getString(languageSetKey) ?? "0") ?? 0
        let setLanguage = YFLanguage(rawValue: set)
        if let unwrapedSet = setLanguage {
            language = unwrapedSet
        }else {
            language = YFLanguage()
            saveToLocal()
        }
        refreshConfig()
    }
    
    /// 切换语言
    /// - Parameter lang: 目标语言
    func changeTo(_ lang: YFLanguage?) {
        guard let unwrapedLang = lang, language != unwrapedLang else { return }
        language = unwrapedLang
        saveToLocal()
        refreshConfig()
        let saveLanguageString = String(language.rawValue)
        // 发送通知
        NotificationCenter.default.post(name: .LanguageDidChange, object: saveLanguageString)
    }
    
    //返回国际化图片
    func image(_ name: String) -> UIImage? {
        let path = self.currentBundle?.path(forResource: name + "@\(Int(UIScreen.main.scale))x", ofType: "png") ?? ""
        return UIImage(contentsOfFile: path)
    }
    
    func languageBundle(in bundle: Bundle) -> Bundle {
        guard let path = bundle.path(forResource: language.bundleName, ofType: "lproj") else {
            return bundle
        }
        guard let languageBundle = Bundle(path: path) else { return bundle }
        return languageBundle
    }
    
   ///更新语言环境后保存至本地
    private func saveToLocal() {
        let saveLanguageString = String(language.rawValue)
        YFStorage.shared.saveString(saveLanguageString, key: languageSetKey)
    }
    
    //MARK: private
    /** 根据当前语言,刷新相关配置 */
    private func refreshConfig() {
        let bundleName = language.bundleName
        guard let path = Bundle.main.path(forResource: bundleName, ofType: "lproj") else {
            currentBundle = nil
            return }
        
        let bundle = Bundle(path: path)
        currentBundle = bundle
    }
    
    //MARK: property
    //当前的bundle
    var currentBundle: Bundle?
    
    //当前语言
    var language = YFLanguage()
    
    //国际化语言的缓存key
    private let languageSetKey = "com.fc.languageSetKey"
}

为了使调用更简洁,通过extension为String添加国际化快捷方法

extension String {
    /// 返回公用国际化文本
    var cLocal: String {
        return FCLocalization.getString(key: self, table: nil)
    }
    
    /// 返回与当前文件同名的 国际化文本
    func local(_ path: String = #file) -> String {
        let fileName = getFileName(from: path)
        return FCLocalization.getString(key: self, table: fileName)
    }
    
    /// 从指定table中获取国际化文本
   func fLocal(_ file: String) -> String {
        return FCLocalization.getString(key: self, table: file)
    }
        /// 从路径中获取文件名
    ///
    /// - Parameter path: 路径
    /// - Returns: 文件名
    func getFileName(from path: String) -> String {
        guard let lastPathComponent = path.components(separatedBy: "/").last else { return "" }
        
        if let range = lastPathComponent.range(of: ".", options: .backwards) {
            let fileName = lastPathComponent[lastPathComponent.startIndex..<range.lowerBound]
            return String(fileName)
        } else {
            return lastPathComponent
        }
    }

    /// 获取国际化图片
    func localImage() -> UIImage? {
        return FCLocalization.shared.image(self)
    }
}

因为app大多是通过模块来处理的,比如首页模块、用户模块、登录模块等等,所以为了避免创建太多的xxxx.strings文件,仅通过模块创建对应的strings文件,该模块的所有类的国际化文本均存放在此模块strings中,所以对应模块的国际化调用也可通过extension为 String添加快捷方法,如下:

extension String {
    
    /// 首页模块
    ///
    /// - Returns: 首页模块语言本地化
    func mainLocal() -> String {
        return fLocal("main")
    }

    //调用
    let text = "首页".mainLocal()
    //text -> 首页 首頁 main
    
    /// 用户模块
    ///
    /// - Returns: 用户模块语言本地化
    func userLocal() -> String {
        return fLocal("User")
    }
    //调用
    let text = "用户".mainLocal()
    //text -> 用户 用戶 user
}

相关文章

  • 多语言框架的设计

    app根据服务区域不同,比如香港地区, 除了支持通用的简体外,还需要支持繁体和英文 基本的Xcode配置即Stri...

  • Grpc原理

    1 rpc框架原理 rpc调用原理框架如图: 2 业内比较成熟的rpc框架 支持多语言的主要分为3类 -支持多语言...

  • 多语言框架设计

    为新项目增加一个多语言框架通过在Text 组件下增加一个Language.cs 组件 来配置多语言Key 。在Ed...

  • 使用Node解决简单重复问题之Excel内容获取

    始因 -- 懒 最近项目中,经常用到多语言翻译,而iOS的多语言适配,设计给出的多语言都是指定的翻译制作成的一系列...

  • Centos7部署Sentry实战

    Sentry 由python开发,django为框架的跨平台多语言/框架的日志聚合平台,功能十分强悍。截至目前最新...

  • 项目错误跟踪告警平台sentry安装和接入php项目

    Sentry 由python开发,django为框架的跨平台多语言/框架的日志聚合平台,功能十分强悍。截至目前最新...

  • 多语言设计的思考

    现在跨境电商越来越火,引发我对关于多语言设计的问题的思考,例如如何本土化,本土化的多语言环境如何设计等。 本土化的...

  • 使用 hadoop streaming 编程的几点经验和教训

    hadoop streaming 是 hadoop 的一个多语言编程框架。关于 streaming 的使用方法可以...

  • 软件设计混淆概念书目录

    设计概念 设计模式 框架 架构 平台 框架与架构之间的关系 框架与设计模式之间的关系

  • 并发模型相关资料

    现在很多语言和框架基本都有并发的处理或者library可以调用, 但是在实现底层框架或者组件,就需要考虑并发模型了...

网友评论

      本文标题:多语言框架的设计

      本文链接:https://www.haomeiwen.com/subject/uqunrktx.html