美文网首页
iOS 支持多语言

iOS 支持多语言

作者: 张_何 | 来源:发表于2024-12-09 13:12 被阅读0次

简单尝试

  • 首先先创建一个叫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 名字 和 网络请求提示的配置。

相关文章

  • ios应用的多语言支持

    ios应用的多语言支持主要包括应用名称的多语言、字符串的多语言、sotrybard多语言等等要添加多语言,首先需要...

  • iOS的Internationalization和localiz

    今天介绍如何使iOS应用支持多语言。 多语言支持本质是使用多个键值对列表,App在运行时根据当前机器的语言环境选择...

  • iOS 多语言版本的开发

    iOS 多语言版本的开发 iOS 多语言版本的开发(一) iOS 多语言版本的开发(二)

  • iOS支持多语言

    这篇文章介绍的多语言方案非常简单,就是使用string资源文件来实现多语言的支持。 string资源文件的创建和使...

  • iOS 多语言版本的开发(一)

    iOS 多语言版本的开发(一) 引言 多语言 & 本地化,随你怎么叫,道理差不多;一个App 要想走出国门,只支持...

  • iOS本地化-语言切换

    如果公司的iOS应用需要支持歪果仁,那么多语言是必不可少的,至少要支持一门英语吧,要不然还不如不做,关于iOS本地...

  • App Languages 批量化导入管理iOS多语言文案

      自己最近开发了一款软件 App languages可以实现可视化的形式管理iOS、mac多语言文案,支持批量导...

  • Flutter 仿新浪微博App

    Flutter开发的微博客户端,同时支持Android和iOS。与官方微博x9.99%相似度体验,离线模式,多语言...

  • Flutter 仿微博客户端

    Flutter开发的微博客户端,同时支持Android和iOS。与官方微博x9.99%相似度体验,离线模式,多语言...

  • iOS APP 多语言支持

    欢迎访问我的博客muhlenXi,该文章出自我的博客。 版权声明:本文为muhlenXi原创文章,转载请注明出处,...

网友评论

      本文标题:iOS 支持多语言

      本文链接:https://www.haomeiwen.com/subject/qfgcsjtx.html