在 Swift 中,什么是 NSLocalizedString 的等效方法?

248

在 Swift 中是否有与 NSLocalizedString(...) 相对应的方法?在 Objective-C 中,我们通常使用:

NSString *string = NSLocalizedString(@"key", @"comment");

我该如何在Swift中实现同样的功能?我找到了一个函数:

func NSLocalizedString(
    key: String,
    tableName: String? = default,
    bundle: NSBundle = default,
    value: String = default,
    #comment: String) -> String

然而,它非常长并且一点也不方便。


2
最好创建代码片段的较短版本:NSLocalizedString("", comment: "")... 我喜欢扩展解决方案,但问题是genstrings无法将这些字符串捕获到翻译文件中。 - Matej Ukmar
3
在 Swift 3 中,你可以直接使用 NSLocalizedString("Cancel", comment: "Cancel button title") 来利用默认值。我认为这样很方便。 - LShi
这是一篇关于本地化的非常好的文章(字符串扩展、不同的字符串表甚至复数形式):https://medium.com/@marcosantadev/app-localization-tips-with-swift-4e9b2d9672c9 - LightMan
这是一篇关于Swift本地化和强大架构的非常好的文章 https://medium.com/@mendibarouk/enhance-your-localized-capabilities-on-your-ios-applications-d3ba17138077 - Mendy
19个回答

397

我使用以下解决方案:

1) 创建扩展程序:

extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}

2)在 Localizable.strings 文件中:

"Hi" = "Привет";

3) 使用示例:

myLabel.text = "Hi".localized

享受! ;)

--更新:--

对于带有注释的情况,您可以使用以下解决方案:

1)扩展:

extension String {
    func localized(withComment:String) -> String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: withComment)
    }
}

2) 在.strings文件中:

/* with !!! */
"Hi" = "Привет!!!";

3) 使用:

myLabel.text = "Hi".localized(withComment: "with !!!")

97
唯一的问题是,你将无法使用 genstrings 工具来生成你的 .strings 文件。 - Ned
9
这是一个非常好的想法!我还通过更改为func localized(comment: String = "") -> String使其变得更加智能化,因此它更加简洁,并带有可选的注释 :) - Gui Moura
2
有没有想过如何在这个项目中使用 genstrings - Chris
51
大家都非常期待这个答案,但对于任何具有多种语言的重要项目而言,一个非常大的问题是,这会完全搞乱您对翻译消息的管理,因为 genstrings 仅适用于传递给 NSLocalizedString 的字面字符串。通过这种巧妙的解决方法,您失去了使用 genstrings 工具更新 .strings 文件的能力,至少对我而言,这意味着我将无法使用这种简化的方法。 - Erik van der Neut
15
我发现这个很棒的解决方案在 https://github.com/marmelroy/Localize-Swift 中实现。genstrings 的问题也被作者包含的自定义 Python 脚本解决了。我不是作者。 - Tomek Cejner
显示剩余14条评论

294

NSLocalizedString 在 Swift 的世界中也存在。

func NSLocalizedString(
    key: String,
    tableName: String? = default,
    bundle: NSBundle = default,
    value: String = default,
    #comment: String) -> String

tableNamebundlevalue 参数带有 default 关键字,这意味着在调用函数时我们可以省略这些参数。在这种情况下,默认值将被使用。

这意味着方法调用可以简化为:

NSLocalizedString("key", comment: "comment")

Swift 5 - 没有变化,仍然像以前一样工作。


44
评论不能为 nil 是唯一的区别,而且对于短版本来说,自动完成远非直观。 - Marcin
1
这个不再起作用了,我收到一个错误,说使用的参数不足。 - Apps 4 U
2
请注意,在Xcode 6.3,Swift 1.2中,根据从Objective-C进行的特定更改,上述内容是正确的。正如Marcin所述,注释不能为nil,但可以是“”(空)。 - Neil
2
一个空的注释会使得在字符串文件中重新定位该字符串变得困难;如果没有别的办法,至少在使用该字符串的地方添加类/文件名作为注释。 - Johan
这是正确的答案。一旦苹果为Swift更新它,Xcode将能够自动将此API转换为其新的Swift API,而不会出现其他问题。即使在当前的Xcode Refractor菜单中(v 11.4.1),也有一个“Wrap in NSLocalizedString”选项,只需突出显示文本,右键单击并选择菜单项即可轻松完成。 - Ethan Allen

40

一种现有答案的变体:

Swift 5.1:

extension String {

    func localized(withComment comment: String? = nil) -> String {
        return NSLocalizedString(self, comment: comment ?? "")
    }

}

您可以直接使用它,带注释或不带注释都可以:

"Goodbye".localized()
"Hello".localized(withComment: "Simple greeting")

请注意genstrings 无法与此解决方案配合使用。


18

使用这种方式可以为不同类型(例如Int或自定义类如CurrencyUnit等)创建不同的实现。同时,也可以使用genstrings工具扫描此方法调用。
只需将routine标志添加到命令中即可。

genstrings MyCoolApp/Views/SomeView.swift -s localize -o .

扩展:

import UIKit

extension String {
    public static func localize(key: String, comment: String) -> String {
        return NSLocalizedString(key, comment: comment)
    }
}

使用方法:

String.localize("foo.bar", comment: "Foo Bar Comment :)")

这对我也管用,但与在当前目录结尾处添加 . 不同,我必须将实际文件夹添加到 genstrings 命令中,就像这样:genstrings MyCoolApp/Views/SomeView.swift -s localize -o en.lproj - devjme

6

创建了一个小的帮助方法,用于那些“注释”总是被忽略的情况。少一些代码更易读:

public func NSLocalizedString(key: String) -> String {
    return NSLocalizedString(key, comment: "")
}

只需将其放置在类外部任何地方,Xcode 就会找到这个全局方法。


14
这是不好的做法。除非你自己完成了所有的翻译工作,否则建议留下评论并获得帮助。 - Jeremiah
即使你在翻译自己的代码,注释也是非常有帮助的,特别是在大型项目中。 - shim
我使用一个翻译数据库,其中有评论的位置,我不需要这些评论或空的“comment:”参数出现在我的代码中。因此,我使用这个方法,并在“key:”参数之前加下划线,使函数调用更加简洁。 - arlomedia

6

Swift 3 version :)...

import Foundation

extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}

6
实际上,在Swift项目中,您可以使用两个阶段来翻译您的文本:
1)第一阶段是使用旧方法创建所有可翻译的字符串:
NSLocalisedString("Text to translate", comment: "Comment to comment")

1.1) 首先,您应该使用genstrings生成Localizable.strings:

$ genstrings *swift

2)接下来,您应该使用这个答案

2.1)使用XCode的“查找和替换”选项,基于正则表达式。 对于给定的示例(如果没有注释),正则表达式将是:

NSLocalizedString\((.*)\, comment:\ \"\"\) 

并将其替换为

$1.localized

或(如果您有评论)
NSLocalizedString\((.*)\, comment:\ (.*)\)

将其替换为
$1.localizedWithComment(comment: $2)

您可以自由地使用正则表达式和不同的扩展组合。一般的方法是将整个过程分为两个阶段。希望这能帮到您。


1
抱歉,我不理解这里许多答案的要点。使用该方法相比于使用 NSLocalizedString("Cancel", comment: "Cancel button title") 有什么好处? - LShi
1
@LShi 有些人抱怨说 NSLocalizedString 看起来不够 Swift,而 String.localized 则更符合 Swift 的风格,但你无法使用常用于国际化的 genstrings 工具。我的观点是两种方法都可以很容易地混合使用,主要取决于可读性。 - GYFK
如果需要再进行一轮“genstrings”,该怎么办?您是否需要将所有“.localized”替换为“NSLocalizedString”? - Cristik

5

可能最好的方法就是这里

fileprivate func NSLocalizedString(_ key: String) -> String {
    return NSLocalizedString(key, comment: "")
}

并且

import Foundation
extension String {
    static let Hello = NSLocalizedString("Hello")
    static let ThisApplicationIsCreated = NSLocalizedString("This application is created by the swifting.io team")
    static let OpsNoFeature = NSLocalizedString("Ops! It looks like this feature haven't been implemented yet :(!")
}

你可以像这样使用它。
let message: String = .ThisApplicationIsCreated
print(message)

对我来说,这是最好的原因是

  • 硬编码字符串在一个特定的文件中,所以当你想要更改它时非常容易
  • 比手动输入文件中的字符串更容易使用
  • genstrings仍然可以工作
  • 您可以添加更多的扩展名,如每个视图控制器一个扩展名,以保持事情整洁

3
需要注意的是,以描述方式定义的字符串是静态字符串。在 iOS 设置应用程序中更改语言后,应该重新启动应用程序。如果没有,请自行重新启动以查看更改。它还可能具有内存开销,因为我们一次性初始化所有字符串,而不是在需要时初始化。 - iDevAmit
3
我认为在这里最好使用计算属性,就像这样: static var Hello: String = { return NSLocalizedString("Hello") } - art-of-dreams
因为它没有遵循 Swift 命名规范而被 Downvote 了。详见 Swift 命名指南 - Cristik

4
这是对“.localized”方法的改进。首先要添加类扩展名,因为这将有助于您在编程时设置任何字符串:
extension String {
    func localized (bundle: Bundle = .main, tableName: String = "Localizable") -> String {
        return NSLocalizedString(self, tableName: tableName, value: "\(self)", comment: "")
    }
}

编程中字符串的示例用法:

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

现在,Xcode的Storyboard翻译文件会使文件管理器混乱,并且也无法很好地处理Storyboard的更新。一个更好的方法是创建一个新的基本标签类,并将其分配给你所有的Storyboard标签:

class BasicLabel: UILabel {
    //initWithFrame to init view from code
    override init(frame: CGRect) {
      super.init(frame: frame)
      setupView()
    }

    //initWithCode to init view from xib or storyboard
    required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      setupView()
    }

    //common func to init our view
    private func setupView() {
        let storyboardText = self.text
        text = storyboardText?.localized()
    }
}

现在,您在Storyboard中添加并提供默认值的每个标签都会自动进行翻译,前提是您为其提供了翻译。

您也可以对UIButton执行相同操作:

class BasicBtn: UIButton {
    //initWithFrame to init view from code
    override init(frame: CGRect) {
      super.init(frame: frame)
      setupView()
    }

    //initWithCode to init view from xib or storyboard
    required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      setupView()
    }

    //common func to init our view
    private func setupView() {
        let storyboardText = self.titleLabel?.text
        let lclTxt = storyboardText?.localized()
        setTitle(lclTxt, for: .normal)
    }
}

3

当您开发SDK时,需要进行一些额外的操作。

1)像往常一样在YourLocalizeDemoSDK中创建 Localizable.strings

2)在YourLocalizeDemo中创建相同的 Localizable.strings

3)查找YourLocalizeDemoSDK的 Bundle Path

Swift4:

// if you use NSLocalizeString in NSObject, you can use it like this
let value = NSLocalizedString("key", tableName: nil, bundle: Bundle(for: type(of: self)), value: "", comment: "")
Bundle(for: type(of: self))可以帮助你找到YourLocalizeDemoSDK中的bundle。如果你使用Bundle.main的话,你会得到错误的值(实际上它将是与键相同的字符串)。
但如果你想使用dr OX提到的String扩展名,你需要做更多的工作。原始扩展看起来像这样。
extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}

我们知道,我们正在开发一个SDK,Bundle.main将获取YourLocalizeDemo包的bundle。这不是我们想要的。我们需要YourLocalizeDemoSDK中的bundle。这是一个快速查找它的技巧。

在YourLocalizeDemoSDK中的NSObject实例中运行下面的代码。然后你将获得YourLocalizeDemoSDK的URL。

let bundleURLOfSDK = Bundle(for: type(of: self)).bundleURL
let mainBundleURL = Bundle.main.bundleURL

打印这两个网址,你会发现我们可以基于mainBundleURL构建bundleURLofSDK。在这种情况下,它将是:

let bundle = Bundle(url: Bundle.main.bundleURL.appendingPathComponent("Frameworks").appendingPathComponent("YourLocalizeDemoSDK.framework")) ?? Bundle.main

并且 String 扩展将会是这样的:

extension String {
    var localized: String {
        let bundle = Bundle(url: Bundle.main.bundleURL.appendingPathComponent("Frameworks").appendingPathComponent("YourLocalizeDemoSDK.framework")) ?? Bundle.main
        return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: "")
    }
}

希望这能有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接