簡(jiǎn)單嘗試
- 首先先創(chuàng)建一個(gè)叫
Loc的iOS空項(xiàng)目,創(chuàng)建好后目錄結(jié)構(gòu)如下:
- 創(chuàng)建一個(gè)
Language的文件夾在Loc目錄下, 然后在Language目錄下創(chuàng)建一個(gè)叫Localizable的String 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)體中文。 Xcode 的Localizations 中也可以看到有英文和簡(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):
- 如果找不到對(duì)應(yīng)
key的value, 就會(huì)從value參數(shù)中取,如果value參數(shù)中也去不到,就會(huì)將key作為value來(lái)顯示 - 如果
tableName沒(méi)有設(shè)置,就會(huì)從Localizabletable里查找 - 默認(rèn)使用
main bundle里的配置資源,如果在其它bundle,這里bundle不能省略,需要設(shè)置對(duì)應(yīng)的bundle,比如語(yǔ)言配置可以在一個(gè)package里,這時(shí)就需要將bundle設(shè)置為這個(gè)package的bundle了 - 不能使用空字符串作
key -
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()
}
}

- 既然
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)求提示的配置。


