【问题标题】:What's NSLocalizedString equivalent in Swift?Swift 中的 NSLocalizedString 等价物是什么?
【发布时间】:2014-09-24 17:56:09
【问题描述】:

是否有 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

但是,它很长,一点也不方便。

【问题讨论】:

  • 最好是创建更短版本的代码 sn-p: NSLocalizedString("", comment: "") ...我喜欢扩展解决方案,但问题是 genstrings 不会将这些字符串捕获到翻译文件。
  • 在 Swift 3 中,您可以使用 NSLocalizedString("Cancel", comment: "Cancel button title") 来利用默认值。我觉得很方便。
  • 这是一篇关于本地化(字符串扩展、不同字符串表甚至复数)的非常好的文章:medium.com/@marcosantadev/…
  • 这是一篇关于 Swift 本地化的非常好的文章,用于构建健壮的架构 medium.com/@mendibarouk/…

标签: ios swift localization nslocalizedstring


【解决方案1】:

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 - 没有变化,仍然如此。

【讨论】:

  • 唯一的区别是评论不能为nil,自动补全对于短版来说远非直观。
  • 这不再起作用我收到错误消息说没有使用足够的参数。
  • 在 Xcode 6.3 和 Swift 1.2 中,与 Objective-c 的具体变化不同,上述内容不是正确的,注释(如 Marcin 所述)不能为 nil,但它可以是“”(空)。
  • 一个 nil/empty 注释使得稍后在字符串文件中重新定位字符串变得困难;如果没有别的,请在用作注释的地方添加类/文件名。
  • 这是正确答案。一旦 Apple 为 Swift 更新了它,Xcode 将能够自动将此 API 转换为新的 Swift API,其他任何东西都不会中断。即使在当前 Xcode 的 Refractor 菜单(v 11.4.1)中,也有一个 Wrap in NSLocalizedString 选项,只需突出显示文本、右键单击并选择菜单项,就可以使事情变得非常简单。
【解决方案2】:

我使用下一个解决方案:

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

享受吧! ;)

--upd:--

对于带有 cmets 的情况,您可以使用此解决方案:

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 !!!")

【讨论】:

  • 唯一的问题是您将无法使用genstrings 实用程序来生成您的 .strings 文件。
  • 这是个好主意!我还通过更改为func localized(comment: String = "") -> String 让它变得更聪明了一点,所以它变得更小并且带有可选的 cmets :)
  • 知道如何使用genstrings 吗?
  • 每个人都对这个答案感到非常兴奋,但最大的问题(对于任何具有多种语言的严肃项目)是这完全打乱了您对翻译消息的管理,因为 genstrings 仅适用于文字传递给 NSLocalizedString 的字符串。使用这种巧妙的解决方法,您将无法使用 genstrings 工具更新 .strings 文件,至少对我而言,这意味着我将无法使用这种简化的方法。
  • 我在github.com/marmelroy/Localize-Swift 中发现了这个很棒的解决方案。作者提供的自定义 python 脚本也解决了 genstrings 的问题。我不是作家。
【解决方案3】:

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

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 :)")

【讨论】:

  • 这个答案太棒了,应该得到更多的支持!如果您希望避免引入另一个库,这是迄今为止我发现的最简单的解决方案。这是一个很好的原生解决方案。
【解决方案4】:

为始终忽略“注释”的情况创建了一个小型辅助方法。更少的代码更容易阅读:

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

只要把它放在任何地方(类之外),Xcode 就会找到这个全局方法。

【讨论】:

  • 这是不好的做法。除非您自己完成所有翻译,否则建议使用评论并提供帮助。
  • 即使您自己翻译,cmets 也会很有帮助,尤其是在大型项目中。
  • 我使用一个有 cmets 位置的翻译数据库,我的代码中不需要这些 cmets 或空的“comment:”参数。所以我使用这个,在“key:”参数前加上一个下划线,使函数调用更清晰。
【解决方案5】:

有助于在单元测试中使用:

这是一个简单的版本,可以扩展到不同的用例(例如,使用 tableNames)。

public func NSLocalizedString(key: String, referenceClass: AnyClass, comment: String = "") -> String 
{
    let bundle = NSBundle(forClass: referenceClass)
    return NSLocalizedString(key, tableName:nil, bundle: bundle, comment: comment)
}

像这样使用它:

NSLocalizedString("YOUR-KEY", referenceClass: self)

或者像这样评论:

NSLocalizedString("YOUR-KEY", referenceClass: self, comment: "usage description")

【讨论】:

  • 省略 cmets 是不好的做法。
  • @José 感谢您的评论。代码是一个想法,而不是复制和粘贴的模板。但如果你愿意,我添加了添加 cmets 的选项 ;)
【解决方案6】:

Swift 3 版本 :)...

import Foundation

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

【讨论】:

    【解决方案7】:

    当你翻译时,比如说从英语,一个短语相同,到另一种不同的语言(因为性别,动词变位或变格)最简单 Swift 中适用于所有情况的 NSString 形式是三个参数之一。例如,英语短语“previous was”对于“weight”(“предыдущий был”)和“waist”(“предыдущая была")。

    在这种情况下,您需要为一个来源提供两种不同的翻译(根据 WWDC 2018 中推荐的 XLIFF 工具)。你不能用两个参数 NSLocalizedString 来实现它,其中“以前是”对于“键”和英文翻译(即值)都是相同的。唯一的方法是使用三参数形式

    NSLocalizedString("previousWasFeminine", value: "previous was", comment: "previousWasFeminine")
    
    NSLocalizedString("previousWasMasculine", value: "previous was", comment: "previousWasMasculine")
    

    其中的键(“previousWasFeminine”和“previousWasMasculine”)不同。

    我知道一般建议是将短语作为整体进行翻译,但有时它既费时又不方便。

    【讨论】:

      【解决方案8】:

      可能最好的方法是here

      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 仍然可以工作
      • 您可以添加更多扩展,例如每个视图控制器一个以保持整洁

      【讨论】:

      • 需要注意的是,上述方式定义的字符串是静态字符串。在 iOS 设置应用程序中更改语言后,应重新启动该应用程序。如果没有,请自行重新启动它以查看更改。它也可能有内存开销,因为我们一次初始化所有字符串,而不是在需要它们的时候。
      • 我认为这里最好使用计算属性,比如static var Hello: String = { return NSLocalizedString("Hello") }
      • 投反对票,因为它不遵循 Swift naming guidelines
      【解决方案9】:

      实际上,您可以使用两个阶段来翻译 Swift 项目中的文本:

      1) 第一阶段是使用旧方法创建所有可翻译字符串:

      NSLocalisedString("Text to translate", comment: "Comment to comment")
      

      1.1) 那么你应该使用genstrings来生成Localizable.strings:

      $ genstrings *swift
      

      2) 之后,你应该使用这个answer

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

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

      替换成

      $1.localized
      

      或者(如果你有 cmets)

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

      替换成

      $1.localizedWithComment(comment: $2)
      

      您可以随意使用正则表达式和不同的扩展组合。一般的方法是将整个过程分为两个阶段。希望对您有所帮助。

      【讨论】:

      • 对不起,我在这里没有得到很多答案。该方法比使用NSLocalizedString("Cancel", comment: "Cancel button title") 有什么好处?
      • @LShi 有些人抱怨说,NSLocalizedString 看起来不像它应该看起来那么 Swiftier。另一方面,String.localized 看起来更 Swifty,但您无法使用 gesntrings 实用程序,它通常用于简化您的国际化工作。我的观点是混合这两种方法很容易。所以主要是可读性的问题。
      • 如果你需要再做一轮genstrings会怎样?您是否将所有.localized 替换为NSLocalizedString
      【解决方案10】:

      我创建了自己的 genstrings 类工具,用于使用自定义翻译函数提取字符串

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

      https://gist.github.com/Maxdw/e9e89af731ae6c6b8d85f5fa60ba848c

      它将解析您所有的 swift 文件并将您代码中的字符串和 cmets 导出到 .strings 文件。

      可能不是最简单的方法,但有可能。

      【讨论】:

        【解决方案11】:

        虽然这不能解决缩短问题,但这有助于我组织消息,我为错误消息创建了一个结构,如下所示

        struct Constants {
            // Error Messages
            struct ErrorMessages {
                static let unKnownError = NSLocalizedString("Unknown Error", comment: "Unknown Error Occured")
                static let downloadError = NSLocalizedString("Error in Download", comment: "Error in Download")
            }
        }
        
        let error = Constants.ErrorMessages.unKnownError
        

        通过这种方式,您可以组织消息并使 genstrings 工作。

        这是使用的 genstrings 命令

        find ./ -name \*.swift -print0 | xargs -0 genstrings -o .en.lproj
        

        【讨论】:

          【解决方案12】:

          现有答案的变体:

          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 不适用于此解决方案。

          【讨论】:

            【解决方案13】:

            在开发 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.main,你会得到一个错误的值(实际上它与键是同一个字符串)。

            但是如果你想使用dr OX提到的String扩展。你需要做更多。源扩展名如下所示。

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

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

            在 YourLocalizeDemoSDK 的 NSObject 实例中运行以下代码。你会得到YourLocalizeDemoSDK的URL。

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

            将这两个url都打印出来,你会发现我们可以在mainBundleURL的基础上构建bundleURLofSDK。在这种情况下,它将是:

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

            字符串扩展名将是:

            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: "")
                }
            }
            

            希望对你有帮助。

            【讨论】:

              【解决方案14】:

              使用默认语言进行本地化:

              extension String {
              func localized() -> String {
                     let defaultLanguage = "en"
                     let path = Bundle.main.path(forResource: defaultLanguage, ofType: "lproj")
                     let bundle = Bundle(path: path!)
              
                     return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
                  }
              }
              

              【讨论】:

                【解决方案15】:

                这是对“.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 的故事板翻译文件使文件管理器变得混乱,也不能很好地处理故事板的更新。更好的方法是创建一个新的基本标签类并将其分配给所有情节提要标签:

                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()
                    }
                }
                

                现在,假设您已为其提供翻译,您在情节提要中添加并为其提供默认默认值的每个标签都将自动得到翻译。

                你可以对 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)
                    }
                }
                

                【讨论】:

                  【解决方案16】:

                  除了写 here 的大扩展之外,如果你懒得查找和替换旧的 NSLocalizedString,你可以在 Xcode 中打开查找和替换,在查找部分你可以写 NSLocalizedString\(\(".*"\), comment: ""\) 然后在你需要的替换部分写$1.localized 将项目中的所有NSLocalizedString 更改为"blabla".localized

                  【讨论】:

                    【解决方案17】:

                    没有足够的声誉来评论我对@Kay 的fantastic solution 的附加组件:

                    对于 SwiftUI:

                    genstrings -SwiftUI *.swift -s localize -o .
                    

                    没有-SwiftUI,它只会解析localize()内的那个

                    【讨论】:

                      猜你喜欢
                      • 2014-07-31
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-08-12
                      • 2014-08-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多