2017-06-30 10:04:16 aa654403231 阅读数 2120

重点是app 内部切换

1.配置languege.strings.infoPist.Strings












2.LocalizationTool.swift,实现内部切换

//
//  LocalizationTool.swift
//  IFXY
//
//  Created by LiuXing on 2017/3/7.
//  Copyright © 2017年 IFly. All rights reserved.
//

import UIKit

class LocalizationTool: NSObject {
     static let shareInstance = LocalizationTool()

     let def = UserDefaults.standard
     var bundle : Bundle?
     
     func valueWithKey(key: String!) -> String {
          let bundle = LocalizationTool.shareInstance.bundle
          let str = bundle?.localizedString(forKey: key, value: nil, table: "Language")
          return str!
     }
//    class func valueWithKey(key: String!) -> String {
//        
//        let languageString = SynthesizerSettingTool.shareIntance.vocieParams?.languageType
//        let path = Bundle.main.path(forResource: languageString!, ofType: "lproj")
//        let value = (Bundle.init(path: path!)?.localizedString(forKey: key, value: nil, table: "Language"))!
//        return value
//    }
     
     func setLanguage(langeuage:String) {
          var str=langeuage
          if langeuage=="" || langeuage==nil{
               
               let languages:[String]=UserDefaults.standard.object(forKey: "AppleLanguages") as! [String]
               let str2:String=languages[0]
               if ((str2=="zh-Hans-CN")||(str2=="zh-Hans")){
                    str="zh-Hans"
               }else{
                    str="en"
               }
               
          }
          UserDefaults.standard.set(str, forKey: "langeuage")
          UserDefaults.standard.synchronize()
          let path = Bundle.main.path(forResource:str , ofType: "lproj")
          bundle = Bundle(path: path!)
//          def.set(langeuage, forKey: UserLanguage)
//          def.synchronize()
          NotificationCenter.default.post(name: NSNotification.Name(rawValue: "LanguageChanged"), object: nil)

          
     }
}

3.在appdelegate里面     

       var languege:String=""
//        guard languege==UserDefaults.standard.value(forKey: "langeuage") as! String else{
//               languege=""
//        }
        if  (UserDefaults.standard.value(forKey: "langeuage")) != nil {
            languege=UserDefaults.standard.value(forKey: "langeuage") as! String
        }
        LocalizationTool.shareInstance.setLanguage(langeuage: languege)

4.语言切换界面



  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        tableView.deselectRow(at: indexPath, animated: false)
        selectCol = indexPath.row
        tableView.reloadData()
        if   indexPath.row == 0 { 
 
          LocalizationTool.shareInstance.setLanguage(langeuage: "zh-Hans")
        }  else { 
 
          LocalizationTool.shareInstance.setLanguage(langeuage: "en")
        }
     

    
    }
点击切换语言之后,需要立即修改之前缓存的页面

 

        NotificationCenter.default.addObserver(self, selector: #selector(changeLanguage), name: NSNotification.Name(rawValue: "LanguageChanged"), object: nil)

页面所有的viewdidload方法里面加上监听

 

 func changeLanguage(){
           items = [["title": LocalizationTool.shareInstance.valueWithKey(key: "my_language_zh")],["title": LocalizationTool.shareInstance.valueWithKey(key: "trans_title_en")]]
          self.title = LocalizationTool.shareInstance.valueWithKey(key: "my_language_switch")//"切换语言"
          
          if (UserDefaults.standard.value(forKey: "langeuage") as! String) == "en"{ // 英语
               selectCol = 1
          } else {
               selectCol = 0
          }
          m_TableView.reloadData()
    }

tip:日期选择器

fileprivate lazy var datePicker:UIDatePicker = {[unowned self] in
        let datePic = UIDatePicker()
        datePic.backgroundColor = UIColor.white
     let languege=UserDefaults.standard.value(forKey: "langeuage") as! String
     var sexstr=UserAccountTool.shareIntance.account?.sex
     if languege=="en" {
          datePic.locale = Locale(identifier: "en")

 }else{
        datePic.locale = Locale(identifier: "zh_CN")  
     }
//        datePic.locale = Locale(identifier: "zh_CN")
        return datePic
        
    }()


2014-10-20 09:42:02 superyan 阅读数 7272

昨天,缘创派的新版本app终于审核通过。这个版本是用swift开发的一个版本,几乎是在appstore允许提交swift程序的第一天我们就提交了。只是等待审核的时间超过两周。


回想起来,当时听到苹果推出来新的编程语言的时候,我们立刻决定采用swift来开发我们的app。原因很简单:第一、技术合伙人之前从来没有学过Object C,从来没有开发过iOS的应用。第二、我们之前的app是外包的,很多需求没有办法跟进。


最初觉得使用swift的时候,其实抱着一种试一试的态度。毕竟这个语言和开发工具都处在beta阶段。


当然,根本没有书,只能查看技术文档。好在技术合伙人的学习能力够强。大概看了两周文档之后,便开始决定正式搭框架了。而过了一个月之后,已经把整个app的主体功能实现了。


在整个开发过程中,遇到的主要问题包括以下几个方法:


  1. Xcode问题 beta1 经常软件崩溃 indexing cpu 100%无法完成  beta3解决  beta5 继续indexing CPU100% 无法完成。
  2. 每一次 xcode 升级语法都有调整  每一次更新软件都是,bug 调整好久 其中 ? !的加减似乎每个版本都有调整。
  3. 升级到 beta6时,苹果对底层框架进行了一些调整,有些方法之前实现好好的,忽然就在父类找不到了,或者换了方法。

开始用swift开发时,就被有经验的ios开发者告诉过 苹果升级 xcode 只关心用户体验,不会关心开发者的体验的,所以很多大的调整要习惯,有些功能不能实现也属正常,所以对一些功能好久都不能实现  总是徘徊在 是不是xcode 这个版本有Bug 还是我的实现方式不正确上。


现在自己电脑上的xode 版本是6.0 前几天有尝试升级到  6.1 GM SEED2  发现升级后部分语法又有调整,这还是小事 严重的是 代码indexing又把cpu跑到了100%(不知道其他开发者有没有遇到过这种问题) ,由于已经用6.0打包提交了一半,未敢在新版本上逗留太久  就切换回6.0了还是等待 除了6.1正式版再说吧。



但不管怎样,从没有接触过iOS开发,到我们的第一款用Swift语言的app 缘创派上线,肯定是国内第一波Swift的app,中间的曲折和经验还是值得记住的。


我们的缘创派app下载地址是:https://itunes.apple.com/cn/app/id695423658



缘创派(http://www.ycpai.com),找互联网创业合伙人

------------------------------------------------------------------------

2018-04-27 09:09:44 qq_40201300 阅读数 0

前言

语言本地化 大家肯定都多少都听过,今天我要分享的是快速实现语言本地化,与App内语言切换

核心内容主要是三个部分

  • storyboard/xib本地化

  • 纯代码本地化

  • 语言切换

准备工作

项目中添加语言

storyboard/xib本地化

storyboard/xib做本地化Xcode基本上是一键搞定了。

很简单

只要勾勾选选就可以了

这边只涉及到一个更新的问题

通过 ibtools命令 可以使storyboard/xib生成新的代码

首先cd 到stroyboard/xib 目录

执行ibtool xxx.storyboard --generate-strings-file new.strings

打开new.strings 将新内容手动复制到原来的string上。

纯代码本地化

创建string文件

勾选语言,把几种全部勾上,包括Base (为下文使用脚本生成代码做准备)


添加脚本

将脚本执行移动到编译上方


移动位置

添加脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Localizable.strings文件路径
localizableFile="${SRCROOT}/${PROJECT_NAME}/Support/en.lproj/Localizable.strings"
# 生成的swift文件路径(根据个人习惯修改)
localizedFile="${SRCROOT}/${PROJECT_NAME}/Source/Utils/LocalizedUtils.swift"
# 将localizable.strings中的文本转为swift格式的常量,存入一个临时文件
sed "s/^\"/  static var localized_/g" "${localizableFile}" | sed "s/\" = \"/: String { return \"/g" | sed "s/;$/.localized }/g" "${localizedFile}.tmp"
# 先将localized作为计算属性输出到目标文件
echo -e "import Foundation\n\nextension String {\n  var localized: String { return NSLocalizedString(self, comment: self) }" "${localizedFile}"
# 再将临时文件中的常量增量输出到目标文件
cat "${localizedFile}.tmp" >> "${localizedFile}"
# 最后增量输出一个"}"到目标文件,完成输出
echo -e "\n}" >> "${localizedFile}"
# 删除临时文件
rm "${localizedFile}.tmp"

这里需要注意的是几个目录需要对应好,否则会报错

build一下就能自动生成相关代码 就可以直接用了

语言切换

语言切换的基本原理是使用Userdefault存储当前选择的语言,在设置的时候改变其内容即可

主要涉及到两个问题

  • storyboard/xib如何切换语言

  • 如何刷新界面

对于上面都算是正常的本地化的内容,基本上介绍本地化的教程都会有。

对于自动化脚本这块算是比较新颖。

但是,脚本对于带空格的字符串生成的内容还是有问题,由于是使用sed命令,本人还不是很熟,只能想其他办法,这时候Base.lproj就派上用场了

我们将空格都替换成下划线,或者驼峰命名,在Base中一一对应,

在具体的en和zh中写具体内容,这时Base的作用就是为了方便自动生成代码而已了。(如果不想搞乱Base,新建一个即可)

关于storyboard/xib切换语言

替换Bundle即可

自定义一个Bundle,重写localizedString方法,每次都从Userdefault中获取当前选择语言,再使用方法替换将Bundle.main替换成自定义的Bundle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
enum Language : String {
    case english = "en"
    case chinese = "zh-Hans"
}
 
/**
 *  当调用onLanguage后替换掉mainBundle为当前语言的bundle
 */
 
class BundleEx: Bundle {
     
    override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
        if let bundle = Bundle.getLanguageBundel() {
            return bundle.localizedString(forKey: key, value: value, table: tableName)
        }else {
            return super.localizedString(forKey: key, value: value, table: tableName)
        }
    }
}
 
 
extension Bundle {
     
    private static var onLanguageDispatchOnce: ()->Void = {
        //替换Bundle.main为自定义的BundleEx
        object_setClass(Bundle.main, BundleEx.self)
    }
     
    func onLanguage(){
        Bundle.onLanguageDispatchOnce()
    }
     
    class func getLanguageBundel() -> Bundle? {
        let languageBundlePath = Bundle.main.path(forResource: UserDefaults.standard[AppStatic.kCurrentLanguage] asString, ofType: "lproj")
//        print("path = \(languageBundlePath)")
        guard languageBundlePath != nil else {
            return nil
        }
        let languageBundle = Bundle.init(path: languageBundlePath!)
        guard languageBundle != nil else {
            return nil
        }
        return languageBundle!
         
    }
}

其中为Userdefault自定义了下标

1
2
3
4
5
6
7
8
    public subscript(key: String) -> Any? {
        get {
            return object(forKey: key)
        }
        set {
            set(newValue, forKey: key)
        }
    }

在读取字符串时执行一次

Bundle.main.onLanguage()

我就直接写到了脚本里,将脚本修改如下(几个文件的路径自己注意一下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Localizable.strings文件路径
localizableFile="${SRCROOT}/Base.lproj/Localizable.strings"
# 生成的swift文件路径(根据个人习惯修改)
localizedFile="${SRCROOT}/Public/LocalizedUtils.swift"
# 将localizable.strings中的文本转为swift格式的常量,存入一个临时文件
sed "s/^\"/  static var localized_/g" "${localizableFile}" | sed "s/\" = \"/: String { return \"/g" | sed "s/;$/.localized }/g" "${localizedFile}.tmp"
# 先将localized作为计算属性输出到目标文件
echo -e "import Foundation\n\nextension String {\n  var localized: String { Bundle.main.onLanguage() \n return NSLocalizedString(self, comment: self) }" "${localizedFile}"
# 再将临时文件中的常量增量输出到目标文件
cat "${localizedFile}.tmp" >> "${localizedFile}"
# 最后增量输出一个"}"到目标文件,完成输出
echo -e "\n}" >> "${localizedFile}"
# 删除临时文件
rm "${localizedFile}.tmp"

关于刷新界面

对于所有界面的刷新最方便的就是重新设置rootViewController

将keyWindow先变黑,假装loading个几秒,再变回来即可。

如果需要再次回到之前所在页面,再添加相应的跳转VC的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    func chooseLanguage() {
        DispatchQueue.global().async {
            let sheet = UIAlertController.init(title: String.localized_Choose_Language, message: nil, preferredStyle: .actionSheet)
             
            sheet.addAction(UIAlertAction.init(title: String.localized_English, style: .default, handler: { (action) in
                UserDefaults.standard[AppStatic.kCurrentLanguage] = Language.english.rawValue
                UIApplication.shared.keyWindow?.rootViewController = RedbotTabBar()
                UIApplication.shared.keyWindow?.alpha = 0
                AlertHelper.showHudWithMessage(message: "Setting Language...")
                DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1, execute: {
                    UIView.animate(withDuration: 1, animations: {UIApplication.shared.keyWindow?.alpha = 1})
                    AlertHelper.hideHudMessage()
                })
                 
            }))
            sheet.addAction(UIAlertAction.init(title: String.localized_Chinese, style: .default, handler: { (action) in
                UserDefaults.standard[AppStatic.kCurrentLanguage] = Language.chinese.rawValue
                UIApplication.shared.keyWindow?.rootViewController = RedbotTabBar()
                UIApplication.shared.keyWindow?.alpha = 0
                AlertHelper.showHudWithMessage(message: "Setting Language...")
                DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1, execute: {
                    UIView.animate(withDuration: 1, animations: {UIApplication.shared.keyWindow?.alpha = 1})
                    AlertHelper.hideHudMessage()
                })
            }))
            sheet.addAction(UIAlertAction.init(title: String.localized_Cancel, style: .cancel, handler: nil))
            self.present(sheet, animated: true, completion: nil)
        }
    }

至此App的语言切换与本地化就都讲完了,是不是很简单呢~~

后记

对于普通的小项目本地化的内容其实远没有那么复杂,需要替换的内容也很少,只要添加过一次语言,再添加新语言就非常简单了。


2017-09-05 10:51:23 amberoot 阅读数 1991

1. 首先要配置localizable.strings,如下图:

详细的配置过程这里就不说了,可以另行百度。今天主要说的是怎样在内部切换语言环境。


2.  新建类:LanguageHelper.swift

代码中注释已经很详细了,这里就不多说了。请看代码:

//
//  LanguageHelper.swift
//
//  Created by amberoot on 17/8/24.
//  Copyright  2017年 amberoot. All rights reserved.
//

import UIKit

class LanguageHelper: NSObject {
    //单例
    static let shareInstance = LanguageHelper()
    
    let def = UserDefaults.standard
    var bundle : Bundle?
    
    ///根据用户设置的语言类型获取字符串
    func getUserStr(key: String) -> String
    {
         // 获取本地化字符串,字符串根据手机系统语言自动切换
        let str = NSLocalizedString(key, comment: "default")
        return str
    }
    ///根据app内部设置的语言类型获取字符串
    func getAppStr(key: String) -> String
    {
        // 获取本地化字符串,字符串会根据app系统语言自动切换
        let str = NSLocalizedString(key, tableName: "Localizable", bundle: LanguageHelper.shareInstance.bundle!, value: "default", comment: "default")
        return str
    }
    
    ///设置app语言环境
    func setLanguage(langeuage: String) {
        var str = langeuage
        //如果获取不到系统语言,就把app语言设置为首选语言
        if langeuage == "" {
            //获取系统首选语言顺序
            let languages:[String] = UserDefaults.standard.object(forKey: "AppleLanguages") as! [String]
            let str2:String = languages[0]
            //如果首选语言是中文,则设置APP语言为中文,否则设置成英文
            if ((str2=="zh-Hans-CN")||(str2=="zh-Hans"))
            {
                str = "zh-Hans"
            }else
            {
                str="en"
            }
            
        }
        //语言设置
        def.set(str, forKey: "langeuage")
        def.synchronize()
        //根据str获取语言数据(因为设置了本地化,所以项目中有en.lproj和zn-Hans.lproj)
        let path = Bundle.main.path(forResource:str , ofType: "lproj")
        bundle = Bundle(path: path!)
 
    
    }
}


3. LanguageHelper.swift的应用

(1)在AppDelegate.swift中设置app的初始语言

可以获取手机系统语言来设置APP的初始语言环境,也可以根据自己的需求设置APP的初始语言环境。




(2)实际应用:配置label的text或button的title等等



参考博客地址:http://www.zhimengzhe.com/bianchengjiaocheng/swift/337155.html


2014-09-15 10:24:00 weixin_34236869 阅读数 0

上篇  

IOS APP 国际化(实现不跟随系统语言,不用重启应用,代码切换stroyboard ,xib ,图片,其他资源

介绍了纯代码刷新 实现程序内切换语言。 但效率底下,也存在一些问题。暂放弃。

第二种是 从跟视图切换 storyboard 实现 代码结合 storyboard 刷新 UI,

上篇遗留的问题是,在开发的时候我们要用到 base.lproj 和 对应语言下得.string 文件。

  方便开发(脚本实现 base storyboard 被编辑会 自动刷新对应语言的.string 文件。而不会覆盖掉之前翻译好的内容。)编译触发脚本

   but  如果我要运行程序调试在 模拟器或真机上。我就要去掉base 。把对应语言.string 文件转换为.storyboard . 因为刷新sb 文件原理上就是去 app 包种的mainbundle 中 对应语言包下找 sb 文件。加载它。 如果对应语言包下 是。string 或 没有sb 文件则会奔溃。

   来看: 这是对应 demo 工程 编译后的 app 包内容。 从 .storyboard 变成了 .storybardc  。这是xcode 编译 sb 文件后的 文件后缀名。

  应该想到 如果我把 对应的.string 文件转换为.storyboardc 文件 拷贝到 对应app 包种的.Lproj 中。 是不是就可以啦。

so  各种嗨皮的 去搞在shell 中。 使用ibtool 转换.string 文件为.storyboard。 然后 修改 .storyboard 文件后缀 .storybardc 。拷贝到 app 包中。运行 切换语言奔溃。

靠,明明在 那个路径下。就是加载不到。 郁闷了几天。

可怎么能得到.storyboardc 文件? 使用ibtool 可以做到。 到这里你会以为 转换的.storyboardc 是个文件。其实 它跟.lproj  一样是个文件夹。

      看 显示包内容。。   每个vc -> .nib 文件 还有.plist 清单。 

到此如果我们有这个脚本,那么就太幸福啦。使用base 和对应语言.string 文件 编辑 运行程序也不用去掉。脚本生成对应语言.storyboardc 文件到 

               

 对应语言下同时存在.storyboardc 和 .string  这样一切问题得宜解决。  支持重设系统语言  和  程序内切换语言。 而且开发时只关心程序逻辑 翻译复制粘贴。 剩下的一切交给脚本搞定。

  同样支持 xib 的国际化 ,工程中 可以同时用 sb 和 xib  或其中一个。

===================支持xib和sb中得图片国际化==========================

2015.4.30

demo 已更新,切换语言图片不会丢失。

说明:适用于不同语言使用相同图片在 sb 和 xib中。如果想不同语言使用不同图片在sb 和 xib 中的话,还是别折腾了吧。有那功夫还是直接在代码里写吧。

图片国际化实现过程:

      由于在xib 和 sb 中添加Img 控件并不会在 对应.string 文件里有类似这样的记录

  

及  控件id =  “图片名”。这里记录得也只是控件的文本。默认并不会对图片国际化。

其实xib 和 sb 只是个xml 文件而已,

source Code  查看。

 搜索image 可以查看ImageView 控件节点。当然也可以看到

 resources 节点。。没错,这就是xib 和 sb 用到的资源。

到这里思路就有了。我是不是可以找到这些图片 分别拷贝到 不同语言包(xx.lproj)下面和 sb 或xib 一个目录。

脚本实现为 解析xml ,在

${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH} 【参见xcode 环境变量】下找到图片,在拷贝到对应文件夹下。

so easy。

 由于当时也只是技术调研,并咩有真正做一个国际化的应用;如果有什么好需求可以在这里提...欢迎讨论和交流。

 

    最终demo 在这里 :

      https://github.com/githhhh/Test_Local_Two

 

以上还有些小不足,算是记录当时遇到的坑,推荐一个不错的解决方案:

虽然是swift ,但思路很不错。
http://blog.csdn.net/VictorMoKai/article/details/48894873

接管系统Bundle方法这样不需要在拷贝storyboardc文件到对应.lproj目录。

object_setClass(NSBundle.mainBundle(), BundleEx.self) 

找到一个oc 版本:

https://github.com/maximbilan/ios_language_manager/blob/master/README.md

 

    

 参考:

  http://www.futuresvision.net/?p=1127

http://stackoverflow.com/questions/1371351/add-files-to-an-xcode-project-from-a-script