iOS 支持多語(yǔ)言

簡(jiǎn)單嘗試

  • 首先先創(chuàng)建一個(gè)叫LociOS 空項(xiàng)目,創(chuàng)建好后目錄結(jié)構(gòu)如下:
  • 創(chuàng)建一個(gè)Language的文件夾在Loc目錄下, 然后在Language目錄下創(chuàng)建一個(gè)叫 LocalizableString File(Legacy)文件, 創(chuàng)建好后,我們show in finder可以看到Xcode 自動(dòng)幫我們創(chuàng)建了一個(gè)叫 en.lproj的文件夾


這是因?yàn)轫?xiàng)目默認(rèn)選擇的語(yǔ)言是英文,可以在下圖中查看,如果你設(shè)置的是中文,生成的就是zh-Hans.lproj文件夾

  • 添加支持新的語(yǔ)言, 比如我們這個(gè)項(xiàng)目要同時(shí)支持簡(jiǎn)體中文和英文,這時(shí)我們只需要在下圖點(diǎn)+按鈕選擇簡(jiǎn)體中文就好

完成后可以看到Language->Localizable中有兩個(gè)配置文件,分別對(duì)應(yīng)英文和簡(jiǎn)體中文。 XcodeLocalizations 中也可以看到有英文和簡(jiǎn)體中文的配置了

  • 在配置文件中添加對(duì)應(yīng)字符串,我們?cè)?code>Localizable(English)中添加 "personal_name" = "英文-san zhang"; ,在Localizable(Chinese, Simplified) 中添加"personal_name" = "簡(jiǎn)體中文-張三";
  • 現(xiàn)在我們可以使用了, 將ContentView中代碼改成如下:
struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text(NSLocalizedString("personal_name", comment: ""))
        }
        .padding()
    }
}

那么運(yùn)行就會(huì)得到下圖結(jié)果,左邊英文,右邊中文

我們看到會(huì)根據(jù)不同的系統(tǒng)語(yǔ)言去相應(yīng)的配置中去取對(duì)應(yīng)的內(nèi)容,這里我們的項(xiàng)目就簡(jiǎn)單支持多語(yǔ)言了。


NSLocalizedString

  • 上面我們簡(jiǎn)單在配置文件中為不同語(yǔ)言配置了內(nèi)容,然后NSLocalizedString會(huì)根據(jù)不同語(yǔ)言加載不同配置文件中的內(nèi)容,那么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, 注釋有很多,我這里只保留了參數(shù)的注釋。
    這里說(shuō)明以下幾點(diǎn):
  1. 如果找不到對(duì)應(yīng)keyvalue, 就會(huì)從value參數(shù)中取,如果 value參數(shù)中也去不到,就會(huì)將key作為value來(lái)顯示
  2. 如果tableName沒(méi)有設(shè)置,就會(huì)從Localizable table里查找
  3. 默認(rèn)使用main bundle里的配置資源,如果在其它bundle,這里bundle不能省略,需要設(shè)置對(duì)應(yīng)的bundle,比如語(yǔ)言配置可以在一個(gè)package里,這時(shí)就需要將bundle設(shè)置為這個(gè)packagebundle
  4. 不能使用空字符串作key
  5. comment 是對(duì)應(yīng)說(shuō)明

優(yōu)化

  • 有了對(duì)NSLocalizedString的了解,我們就可以做一些優(yōu)化。比如我們可以String 加上一個(gè)分類(lèi),來(lái)優(yōu)化我們?nèi)≈担?具體做法: 首先我們新建一個(gè)String+Localizing的文件來(lái)給String添加一個(gè)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值我們很容易寫(xiě)錯(cuò),所以我們可以定義一個(gè)枚舉,把這些key值定義成枚舉里的變量, 比如我們新建一個(gè)叫Localizable.swift的文件,在這個(gè)文件里我們定義一個(gè)Localizable的枚舉,然后定義一個(gè)叫personalName的靜態(tài)變量如下:
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,那么我們也可以制作多個(gè)table將我們的文本根據(jù)一定的規(guī)則進(jìn)行分類(lèi),這樣可以將文本分散到不同的table中,防止所有文本都存放到一個(gè)table里面導(dǎo)致table內(nèi)容過(guò)多.

比如,我們新建一個(gè)LocalizableGeneral.strings的文件

不過(guò)這次我們看到Xcode左側(cè)文件導(dǎo)航里并沒(méi)有像Localizable那樣幫我們分成Localizable(English)Localizable(Chinese, Simplified)兩個(gè)文件,我們show in finder中看到只在en.lproj目錄下多了一個(gè)LocalizableGeneral.strings文件,而zh-Hans.lproj中并沒(méi)有

查看Xcode的配置看到也是只在English下多了一個(gè)文件,而中文下還是1個(gè)

不過(guò)這沒(méi)關(guān)系,我們可以把en.lproj目錄下的LocalizableGeneral.strings復(fù)制一份到zh-Hans.lproj目錄下,然后將LocalizableGeneral.strings拖動(dòng)到Xcode LocalizableGeneral文件下,這樣X(jué)code 自動(dòng)幫我們分成了LocalizableGeneral(English)LocalizableGeneral(Chinese,Simplified)

或者: 我們直接選中文件,然后在右邊的 Show the File inspector 中找到 Localization 欄, 把對(duì)應(yīng)需要支持的 Chinese,Simplified 選上,這樣X(jué)code 會(huì)自動(dòng)幫我們生成對(duì)應(yīng)的文件了

選中后

然后我們分別在LocalizableGeneral(English)LocalizableGeneral(Chinese,Simplified)定義好"personal_name_general"對(duì)應(yīng)的文本

然后我們?cè)賱?chuàng)建一個(gè)LocalizableGeneral.swift的文件,內(nèi)容如下:

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

然后在String 的分類(lèi)中再添加一個(gè)fetchGeneral的方法,在文件中添加localizableGeneralTableName變量名,注意這個(gè)變量對(duì)應(yīng)內(nèi)容要和創(chuàng)建的LocalizableGeneral文件名一致, static func fetch(_ localizedStringKey: String) -> String 中的實(shí)現(xiàn)沒(méi)有添加tableName的原因是,如果不添加默認(rèn)會(huì)到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()
    }
}

運(yùn)行得到如下結(jié)果:

  • 除了上面這種,我門(mén)還可以把文本分散到Localizable的分類(lèi)中。比如,我們創(chuàng)建一個(gè)LocalizableError.strings的配置文件,方式同上面LocalizableGeneral.strings文件一樣, 然后再創(chuàng)建一個(gè)LocalizableError.swift文件,對(duì)應(yīng)文件內(nèi)容如下:

不過(guò)這次我們要修改一下fetch方法,因?yàn)槟壳?code>fetch方法只會(huì)去Localizable配置文件中去找,但我們配置在LocalizableError中,這樣就會(huì)導(dǎo)致照不到,fetch方法具體修改如下:

/// 首先去Localizable配置文件中根據(jù)localizedStringKey 查找,如果找不到就會(huì)使用value的值,而value的值就會(huì)去LocalizableError這個(gè)配置文件中去找
    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()
    }
}

運(yùn)行得到下面結(jié)果:



app 名 和 申請(qǐng)權(quán)限提示的配置

  • 除了上面文本的顯示,多語(yǔ)言還涉及到不同語(yǔ)言環(huán)境顯示的app 名稱(chēng)也不一樣,另外還有訪問(wèn)權(quán)限的提示也不一樣。這兩個(gè)問(wèn)題都是在Info.plist文件中設(shè)置的,解決這兩個(gè)問(wèn)題就需要我們創(chuàng)建一個(gè)名為InfoPlist.strings的文件,注意:名字一定要是InfoPlist,別的名字Xcode不認(rèn)。
    名字的話我們?cè)诓煌Z(yǔ)言配置文件中設(shè)置CFBundleDisplayName key的value就好,權(quán)限的話也是一樣,根據(jù)不同的語(yǔ)言環(huán)境給相應(yīng)權(quán)限的key設(shè)置value。 下面我們?cè)O(shè)置的是簡(jiǎn)體中文和英文環(huán)境下,app 名字 和 網(wǎng)絡(luò)請(qǐng)求提示的配置。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • APP語(yǔ)言本地化,即語(yǔ)言國(guó)際化。指的是根據(jù)用戶操作系統(tǒng)的語(yǔ)言設(shè)置,自動(dòng)將APP的語(yǔ)言設(shè)置為和操作系統(tǒng)一致的語(yǔ)言環(huán)境...
    喔牛慢慢爬閱讀 6,252評(píng)論 0 11
  • 歡迎訪問(wèn)我的博客muhlenXi,該文章出自我的博客。 版權(quán)聲明:本文為muhlenXi原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處,...
    賣(mài)碼維生閱讀 3,410評(píng)論 0 12
  • ** 原文發(fā)表在:https://www.xiaolei0808.com/2016/04/24/Localized...
    金小白先生閱讀 17,934評(píng)論 16 90
  • 根據(jù)當(dāng)前設(shè)備語(yǔ)言自動(dòng)切換顯示。 幾個(gè)涉及到多語(yǔ)言本地化設(shè)置的: 1.應(yīng)用名稱(chēng) 2.文字 3.圖片、素材 4.Sto...
    齊玉婷閱讀 3,762評(píng)論 2 3
  • 相關(guān)問(wèn)題 國(guó)際化官網(wǎng)鏈接 Infoplist.strings為什么可以替換info.plist的名稱(chēng)?什么時(shí)機(jī)替換...
    wxkkkkk閱讀 2,674評(píng)論 0 1

友情鏈接更多精彩內(nèi)容