详解NSLocale.preferredLanguages的含义

·  阅读 388

NSLocale.preferredLanguages的应用

在我们开发iOS程序时,经常有“使用当前应用设置的语言进行差异化操作”的需求。

比如当你开发一个浏览器,需要根据用户设置的语言,给用户推送不同语言的网页文章时。这时候就需要用到NSLocale.preferredLanguages

一般情况下,应用开发者只需要区分中英文就可以完成基本需求了,比如如下代码:

if NSLocale.preferredLanguages[0].hasPrefix("en") {
    // 分支逻辑
}
复制代码

而如果你试着去打印NSLocale.preferredLanguages,用如下代码

print("\(NSLocale.preferredLanguages)")
复制代码

你将会得到一个数组,示例结果如下:

["en-HK", "zh-Hans-HK", "zh-Hant-HK", "zh-Hant-TW", "yue-Hant-HK"]
复制代码

数组的含义

为什么NSLocale.preferredLanguages返回结果是一个数组,不是只要返回一个en-HK就够了吗?

该函数的作用是提供本地设备语言偏好,提供数组的意义在于系统会根据用户本机信息以及用户的系统设置,返回多个按优先级排序的语言-地区设置。这样一来当最高级优先级语言应用程序无法支持时,可以依据列表选择可能支持的语言。

en-HK是什么?

preferredLanguages中,我们可以看到类似这样一个字符串en-HK,凭借直觉,我们可以猜测,en指的是英文,HK指的是香港。

这个猜测自然是没错的,但是具体这些编码是遵循什么严格的标准,如果我们要支持多语言,应该怎么写代码来进行判断。

首先说enen是用来表示一门语言,比如英语用en,中文用zh,这种编码叫全球语言标准码 - ISO-639

ISO-639的语言编码又有细分,后续细分通过数字代号来表示,苹果选用的编码为ISO-639-1ISO-639-2。其中ISO-639-1通过两个英文字母来标记一门语言,ISO-639-2是通过三个字母来标记一门语言。

语言对应的ISO-639-1ISO-639-2代码示例如下:

语言ISO-639-1ISO-639-2
英语eneng
法语frfre
德语deger
日语jajpn

preferredLanguages返回结果的单元中,en即是ISO-639-1中的英语代号。

但是,因为ISO-639-1表示法不能穷尽所有语言,比如对于夏威夷语,在ISO-639-1中就无法找到对应的代号,而在ISO-639-2中可以找到,其代号为haw

所以苹果的语言代号的返回规则是:优先返回ISO-639-1中找到的对应代号,如果没有,则返回ISO-639-2的代号

同理,符号HK属于地区编码,该编码采用的是ISO 3166-1,是国家和地区建立国际认可的编码。

ISO 3166-1编码示例如下:

地区ISO 3166-1
中国大陆CN
美国US
英国GB
法国FR

文字编码

前面已经讲清楚了en-hk的含义,但是我们有时候会看到返回的结果不只两个代码,也有类似zh-Hant-HK这样的,有两个分隔符分隔三个代码的,这又是怎么回事?

这就要说到文字编码了,比如我们中文来说,即使一样是普通话写出来的文字,也分简体中文和繁体中文。

所以在返回的编码结果中,如果文字系统没有区分和疑义的,比如英文,那么就不需要在中间追加文字编码,而如果有细分的,则会追加文字编码。文字编码也是由ISO标准化组织规定。

简体中文和繁体中文的文字编码示例如下:

文字编码
简体中文Hans
繁体中文Hant

所以,在例子zh-Hant-HK中,我们可以得出该返回的结果表示中文-繁体-香港

其他注意事项

可能有些人看到这里,在写代码的时候会默认preferredLanguages返回的元素编码一定带有-,事实绝非如此,返回结果有时候可能只是一个en,并没有地区编码。

也就是说,当我们获取NSLocale.preferredLanguages时,返回的数组元素值有三种可能,具体可以用以下三个值表示:

en
en-hk
zh-Hant-HK
复制代码

当返回结果只有语言,没有地区时,开发者可以优先选用该语言的代表地区作为默认选项,具体而言,英文可以默认美国,中文简体默认中国大陆等。

写一个简易的封装语言地区代码的类

下面我们用一个用来接收preferredLanguages数组元素的类来结束这篇文章。

class LanguageRegionCode {
    private var _languageCode: String?
    private var _scriptCode: String?
    private var _regionCode: String?
    
    var languageCode: String? {
        _languageCode
    }
    
    var scriptCode: String? {
        _scriptCode
    }
    
    var regionCode: String? {
        _regionCode
    }
    
    init(code: String) {
        let splits = code.split(separator: "-")
        if let lCode = splits.first {
            _languageCode = String(lCode)
        }
        if splits.count == 3 {
            _scriptCode = String(splits[1])
            _regionCode = String(splits[2])
        } else if splits.count == 2 {
            _regionCode = String(splits[1])
        }
    }
}
复制代码

该类的使用代码如下:

let code = NSLocale.preferredLanguages[0]
let languageRegionCode = LanguageRegionCode(code: code)

if let languageCode = languageRegionCode.languageCode {
    print("language code = \(languageCode)")
}

if let scriptCode = languageRegionCode.scriptCode {
    print("script code = \(scriptCode)")
}

if let regionCode = languageRegionCode.regionCode {
    print("region code = \(regionCode)")
}
复制代码

对于zh-Hant-HK,打印结果如下:

language code = zh
script code = Hant
region code = HK
复制代码

参考资料:
苹果 NSLocale.preferredLanguages 官方文档:developer.apple.com/documentati…
苹果技术文档:developer.apple.com/library/arc…


想要一起讨论的朋友可以在我的公众号风海铜锣的加群菜单栏中申请加群完成加群申请,一起共同进步。

分类:
iOS
标签:
分类:
iOS
标签: