iOS 支持多语言

简单尝试

  • 首先先创建一个叫LociOS 空项目,创建好后目录结构如下:
  • 创建一个Language的文件夹在Loc目录下, 然后在Language目录下创建一个叫 LocalizableString File(Legacy)文件, 创建好后,我们show in finder可以看到Xcode 自动帮我们创建了一个叫 en.lproj的文件夹


这是因为项目默认选择的语言是英文,可以在下图中查看,如果你设置的是中文,生成的就是zh-Hans.lproj文件夹

  • 添加支持新的语言, 比如我们这个项目要同时支持简体中文和英文,这时我们只需要在下图点+按钮选择简体中文就好

完成后可以看到Language->Localizable中有两个配置文件,分别对应英文和简体中文。 XcodeLocalizations 中也可以看到有英文和简体中文的配置了

  • 在配置文件中添加对应字符串,我们在Localizable(English)中添加 "personal_name" = "英文-san zhang"; ,在Localizable(Chinese, Simplified) 中添加"personal_name" = "简体中文-张三";
  • 现在我们可以使用了, 将ContentView中代码改成如下:
struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text(NSLocalizedString("personal_name", comment: ""))
        }
        .padding()
    }
}

那么运行就会得到下图结果,左边英文,右边中文

我们看到会根据不同的系统语言去相应的配置中去取对应的内容,这里我们的项目就简单支持多语言了。


NSLocalizedString

  • 上面我们简单在配置文件中为不同语言配置了内容,然后NSLocalizedString会根据不同语言加载不同配置文件中的内容,那么NSLocalizedString做了什么呢?
 /// Returns the localized version of a string.
///
/// - parameter key: An identifying value used to reference a localized string.
///   Don't use the empty string as a key. Values keyed by the empty string will
///   not be localized.
/// - parameter tableName: The name of the table containing the localized string
///   identified by `key`. This is the prefix of the strings file—a file with
///   the `.strings` extension—containing the localized values. If `tableName`
///   is `nil` or the empty string, the `Localizable` table is used.
/// - parameter bundle: The bundle containing the table's strings file. The main
///   bundle is used by default.
/// - parameter value: A user-visible string to return when the localized string
///   for `key` cannot be found in the table. If `value` is the empty string,
///   `key` would be returned instead.
/// - parameter comment: A note to the translator describing the context where
///   the localized string is presented to the user.
///
/// - returns: A localized version of the string designated by `key` in the
///   table identified by `tableName`. If the localized string for `key` cannot
///   be found within the table, `value` is returned. However, `key` is returned
///   instead when `value` is the empty string.
....

@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
public func NSLocalizedString(_ key: String, tableName: String? = nil, bundle: Bundle = Bundle.main, value: String = "", comment: String) -> String
  • 上面是NSLocalizedString的API, 注释有很多,我这里只保留了参数的注释。
    这里说明以下几点:
  1. 如果找不到对应keyvalue, 就会从value参数中取,如果 value参数中也去不到,就会将key作为value来显示
  2. 如果tableName没有设置,就会从Localizable table里查找
  3. 默认使用main bundle里的配置资源,如果在其它bundle,这里bundle不能省略,需要设置对应的bundle,比如语言配置可以在一个package里,这时就需要将bundle设置为这个packagebundle
  4. 不能使用空字符串作key
  5. comment 是对应说明

优化

  • 有了对NSLocalizedString的了解,我们就可以做一些优化。比如我们可以String 加上一个分类,来优化我们取值, 具体做法: 首先我们新建一个String+Localizing的文件来给String添加一个fetch的方法:
import Foundation

public extension String {

    /// - Parameter localizedStringKey: Key for the localized string.
    /// - Returns: The localized string associated with the key.
    static func fetch(_ localizedStringKey: String) -> String {
        return NSLocalizedString(
            localizedStringKey,
            comment: ""
        )
    }
}

这样我们就可以在ContentView使用了,使用String.fetch("personal_name")了和NSLocalizedString("personal_name", comment: "")效果是一样的

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text(NSLocalizedString("personal_name", comment: ""))
            Text(String.fetch("personal_name"))
        }
        .padding()
    }
}
  • 这样直接使用key值我们很容易写错,所以我们可以定义一个枚举,把这些key值定义成枚举里的变量, 比如我们新建一个叫Localizable.swift的文件,在这个文件里我们定义一个Localizable的枚举,然后定义一个叫personalName的静态变量如下:
public enum Localizable {
    /// ...
    public static let personalName = "personal_name"
}

这样我们就可以使用String.fetch(Localizable.personalName)取获取文本了,和上面两种使用效果是一样的

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text(NSLocalizedString("personal_name", comment: ""))
            Text(String.fetch("personal_name"))
            Text(String.fetch(Localizable.personalName))
        }
        .padding()
    }
}
image.png
  • 既然public func NSLocalizedString(_ key: String, tableName: String? = nil, bundle: Bundle = Bundle.main, value: String = "", comment: String) -> String支持传递tableName,那么我们也可以制作多个table将我们的文本根据一定的规则进行分类,这样可以将文本分散到不同的table中,防止所有文本都存放到一个table里面导致table内容过多.

比如,我们新建一个LocalizableGeneral.strings的文件

不过这次我们看到Xcode左侧文件导航里并没有像Localizable那样帮我们分成Localizable(English)Localizable(Chinese, Simplified)两个文件,我们show in finder中看到只在en.lproj目录下多了一个LocalizableGeneral.strings文件,而zh-Hans.lproj中并没有

查看Xcode的配置看到也是只在English下多了一个文件,而中文下还是1个

不过这没关系,我们可以把en.lproj目录下的LocalizableGeneral.strings复制一份到zh-Hans.lproj目录下,然后将LocalizableGeneral.strings拖动到Xcode LocalizableGeneral文件下,这样Xcode 自动帮我们分成了LocalizableGeneral(English)LocalizableGeneral(Chinese,Simplified)

然后我们分别在LocalizableGeneral(English)LocalizableGeneral(Chinese,Simplified)定义好"personal_name_general"对应的文本

然后我们再创建一个LocalizableGeneral.swift的文件,内容如下:

public enum LocalizableGeneral {
    /// ...
    public static let personalNameGeneral = "personal_name_general"
}

然后在String 的分类中再添加一个fetchGeneral的方法,在文件中添加localizableGeneralTableName变量名,注意这个变量对应内容要和创建的LocalizableGeneral文件名一致, static func fetch(_ localizedStringKey: String) -> String 中的实现没有添加tableName的原因是,如果不添加默认会到Localizable中查找

import Foundation

private let localizableTableName = "Localizable"
private let localizableGeneralTableName = "LocalizableGeneral"

public extension String {

    /// - Parameter localizedStringKey: Key for the localized string.
    /// - Returns: The localized string associated with the key.
    static func fetch(_ localizedStringKey: String) -> String {
        return NSLocalizedString(
            localizedStringKey,
            comment: ""
        )
    }

    static func fetchGeneral(_ localizedStringKey: String) -> String {
        return NSLocalizedString(
            localizedStringKey,
            tableName: localizableGeneralTableName,
            comment: ""
        )
    }
}

这样我们就可以在ContentView中使用了

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text(NSLocalizedString("personal_name", comment: ""))
            Text(String.fetch("personal_name"))
            Text(String.fetch(Localizable.personalName))
            Text(String.fetchGeneral(LocalizableGeneral.personalNameGeneral))
        }
        .padding()
    }
}

运行得到如下结果:

  • 除了上面这种,我门还可以把文本分散到Localizable的分类中。比如,我们创建一个LocalizableError.strings的配置文件,方式同上面LocalizableGeneral.strings文件一样, 然后再创建一个LocalizableError.swift文件,对应文件内容如下:

不过这次我们要修改一下fetch方法,因为目前fetch方法只会去Localizable配置文件中去找,但我们配置在LocalizableError中,这样就会导致照不到,fetch方法具体修改如下:

/// 首先去Localizable配置文件中根据localizedStringKey 查找,如果找不到就会使用value的值,而value的值就会去LocalizableError这个配置文件中去找
    static func fetch(_ localizedStringKey: String) -> String {
        return NSLocalizedString(
            localizedStringKey,
            value: NSLocalizedString(
                localizedStringKey,
                tableName: localizableErrorTableName,
                comment: ""),
            comment: ""
        )
    }

这样我们可以在ContentView中使用了

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text(NSLocalizedString("personal_name", comment: ""))
            Text(String.fetch("personal_name"))
            Text(String.fetch(Localizable.personalName))
            Text(String.fetchGeneral(LocalizableGeneral.personalNameGeneral))
            Text(String.fetch(Localizable.personalNameError))
        }
        .padding()
    }
}

运行得到下面结果:



app 名 和 申请权限提示的配置

  • 除了上面文本的显示,多语言还涉及到不同语言环境显示的app 名称也不一样,另外还有访问权限的提示也不一样。这两个问题都是在Info.plist文件中设置的,解决这两个问题就需要我们创建一个名为InfoPlist.strings的文件,注意:名字一定要是InfoPlist,别的名字Xcode不认
    名字的话我们在不同语言配置文件中设置CFBundleDisplayName key的value就好,权限的话也是一样,根据不同的语言环境给相应权限的key设置value。 下面我们设置的是简体中文和英文环境下,app 名字 和 网络请求提示的配置。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容

  • APP语言本地化,即语言国际化。指的是根据用户操作系统的语言设置,自动将APP的语言设置为和操作系统一致的语言环境...
    喔牛慢慢爬阅读 5,507评论 0 11
  • 欢迎访问我的博客muhlenXi,该文章出自我的博客。 版权声明:本文为muhlenXi原创文章,转载请注明出处,...
    卖码维生阅读 3,273评论 0 12
  • ** 原文发表在:https://www.xiaolei0808.com/2016/04/24/Localized...
    金小白先生阅读 17,561评论 16 90
  • 根据当前设备语言自动切换显示。 几个涉及到多语言本地化设置的: 1.应用名称 2.文字 3.图片、素材 4.Sto...
    齐玉婷阅读 3,505评论 2 3
  • 相关问题 国际化官网链接 Infoplist.strings为什么可以替换info.plist的名称?什么时机替换...
    wxkkkkk阅读 2,573评论 0 1