美文网首页iOS 专题
IOS多语言切换4-framework中的语言切换和资源使用

IOS多语言切换4-framework中的语言切换和资源使用

作者: 肆点壹陆 | 来源:发表于2020-06-18 18:33 被阅读0次

    开发语言:Swift 5.0
    开发环境:Xcode 11.5
    发布平台:IOS 13

    1、Framework创建和资源使用

    在IOS多语言切换3文章中,我们已经实现了用户通过App手动设置语言,来设置程序文字和图片的多语言化,但随着App开发的需求,一旦引入了Framework,按照上一章实现的功能,所有在Framework中使用的文字和图片资源,无法随着用户设置的语言进行切换,原因在于每个Framework或者项目都有自己的独立Bundle,上一章中,我们只对Bundle.main包进行了处理,所以接下来,我们要对每个Framework的Bundle都进行多语言的处理。

    以以下demo为例,建立一个包含2个framework的项目。

    • FrameworkTest为主项目,包含Main.Storyboard,放置了一个文字用于测试多语言,同时点击此文字,会加载其他framework中的storyboard
    • MainRes作为所有项目都要使用的framework,通过MyLanguage维护所有项目的语言设置,并且包含了一些其他项目可复用的资源文件。
    • SubRes作为一个独立的项目,包含SubRes.Storyboard,并且使用MainRes中的资源,同时通过MyResource来读取此framework中的多语言文字和图片

    在Main.Storyboard和SubRes.Storyboard中,已经设置好文字的中文与英文,此时运行程序可以通过切换IOS设备的语言来实现App的语言切换

    2、SubRes使用MyRes中的图片资源

    在SubRes.Storyboard中,使用MainRes中一张图片arrowblue。


    但运行程序后,图片无法显示,原因是ios中,每个不同的framework有着自己默认的bundle,而加载SubRes.Storyboard时,使用的是SubRes的bundle,但是arrowblue存在于MainRes的bundle中,SubRes.Storyboard无法找到这个图片。
    此问题的解决办法是在Target->SubRes->BuildPhases->CopyBundleResources中,点击+号,添加MainRes的Assets.xcassets。

    这样程序在编译时,会把MainRes的bundle中的资源,复制到SubRes的bundle内,这样程序运行就正常了

    3、MyRes.MyLanguage实现

    我们期待程序使用用户指定的语言而非系统指定的语言,和前几章相同,我们通过实现MyLanguage类来完成此功能,MyLanguage类中,维护了一个bundleDir,保存当前所有framework的bundle,每次用户切换语言,我们更新bundleDir,构造并且记录用户指定语言的bundle。

    public class MyLanguage {
        
        static public let shareInstance = MyLanguage()
        
        private let def = UserDefaults.standard
        
        fileprivate var bundleDir:Dictionary<String,Bundle> = Dictionary<String, Bundle>()
        
        public var dir:Dictionary<String,Bundle>  {
            return bundleDir
        }
        
        let UserLanguage = "UserLanguage"
        let AppleLanguages = "AppleLanguages"
        var language = ""
        
        //用于向MyLanguage类托管一个bundle,维护其语言的设置
        public func registerBundle(_ bundleName:String? = nil) {
            var bb = Bundle.main
            var name = "main"
            if let t = bundleName,let bundle = Bundle.init(identifier: t) {
                bb = bundle
                name = t
            }
            
            
            var string:String = def.value(forKey: UserLanguage) as! String? ?? ""
            if string == "" {
                let languages = def.object(forKey: AppleLanguages) as? NSArray
                if languages?.count != 0 {
                    let current = languages?.object(at: 0) as? String
                    if current != nil {
                        string = current!
                        def.set(current, forKey: UserLanguage)
                    }
                }
            }
            
            string = string.replacingOccurrences(of: "-CN", with: "")
            string = string.replacingOccurrences(of: "-US", with: "")
            
            var path = bb.path(forResource:string , ofType: "lproj")
            if path == nil {
                path = bb.path(forResource:"en" , ofType: "lproj")
            }
    
            bundleDir[name] = Bundle(path: path!)
            bb.bundleName = name
            
            //类型替换
            object_setClass(bb, BundleEx.self)
        }
        //设置当前语言
        public func setLanguage(language:String) {
            self.language = language
            
            for str in bundleDir.keys {
                var bundle = Bundle.main
                var name = "main"
                if str != "main" {
                    bundle = Bundle.init(identifier: str)!
                    name = str
                }
                //获取当前语言的bundle
                let path = bundle.path(forResource:language , ofType: "lproj")
                
    
                bundleDir[name] = Bundle(path: path!)
            }
            
            def.set(language, forKey: UserLanguage)
        }
        
        public func getLanguage()->String {
            return language
        }
        
        private init() {
            
        }
    }
    

    同时我们必须重定义Bundle类。

    class BundleEx: Bundle {
        //由于storyborad默认的构造流程中,会调用此函数获取storyborad使用的文字信息,所以重写此函数
        //让storyboard去自己定义的包里面取string
        override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
            //通过MyLanguage获取包
            if let bundle = MyLanguage.shareInstance.bundleDir[bundleName] {
                return bundle.localizedString(forKey: key, value: value, table: tableName)
            } else {
                return super.localizedString(forKey: key, value: value, table: tableName)
            }
        }
    }
    

    最后,我们需要扩展Bundle类,记录当前Bundle的名字,方便通过字典查找。

    var bundleNameKey = 10000
    
    //为storyboard服务
    extension Bundle {
        var bundleName: String {
            set {
                objc_setAssociatedObject(self, &bundleNameKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY_NONATOMIC)
            }
            
            get {
                if let rs = objc_getAssociatedObject(self, &bundleNameKey) as? String {
                    return rs
                }
                return ""
            }
        }
    }
    

    在AppDelegate中使用MyLanguage

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
           
        //注册mainbundle
        MyLanguage.shareInstance.registerBundle()
        //注册SubRes的bundle
        //注意包名为Target->General->Bundle Identifier
        MyLanguage.shareInstance.registerBundle("com.luv.SubRes")
        //设置中文
        MyLanguage.shareInstance.setLanguage(language: "zh-Hans")
            
        return true
    }
    

    此时,运行程序,可以看到模拟器的语言是英文,但APP中的文字已经成功设置为中文了。

    4、SubRes.MyResource实现

    在SubRes实现MyResource类,用于获取此framework中的多语言文字和图片,具体内容前几章有提到,就不多做介绍了

    class MyResource {
        static public let shareInstance = MyResource()
        
        func GetString(key:String) -> String{
            guard let bundle = MyLanguage.shareInstance.dir["com.luv.SubRes"] else {
                return ""
            }
            return NSLocalizedString(key, tableName: nil, bundle: bundle, comment: "")
            
        }
        
        func GetImage(key:String) ->UIImage? {
            return UIImage.init(named: key, in: MyLanguage.shareInstance.dir["com.luv.SubRes"], compatibleWith: nil)
            
        }
        
        private init() {
            
        }
    }
    

    相关文章

      网友评论

        本文标题:IOS多语言切换4-framework中的语言切换和资源使用

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