首页 > 解决方案 > SwiftUI 自动字符串本地化问题

问题描述

我一直在研究 SwiftUI 应用程序的本地化,但我遇到了一个我不太了解的与本地化相关的情况(鉴于,我对 SwiftUI 还不太精通,首先)

据我了解,至少在 iOS 14 中,SwiftUI 几乎会自动将本地化应用于所有“正常”字符串(只要我设置了正确的本地化文件——我会这样做)。但是,我有两个相同字符串文字的实例——一个得到自动本地化处理。另一个没有。

这就是我想要弄清楚的情况。

我有以下代码:

NavigationView {
  NavigationLink(destination: CalendarSettingsView()) {
    SettingsNavLinkView(label: "Calendar") // <- this doesn't get localized
  }
}

SettingsNavLinkView设置如下(只是与问题相关的骨架):

struct SettingsNavLinkView: View {
  var label:String
  
  var body: some View {
    Text(label) // <- localized "Calendar" is expected to be passed here
  }
}

此外,CalendarSettingsView将其标题定义为:

struct CalendarSettingsView: View {
  var body: some View {
    ScrollView {
       //some code
    }
    .navigationBarTitle("Calendar", displayMode: .inline) // <- "Calendar" here does get localized
  }
}

我的本地化文件中确实有“日历”的关键条目。

正在发生的事情(以及我不明白的事情)是针对SettingsNavLinkView(label: "Calendar")组件的,“日历”没有被本地化,但是,对于CalendarSettingsView组件(和相关的用例:), .navigationBarTitle("Calendar", displayMode: .inline)“日历”字符串确实被本地化了。

这两种情况对我来说似乎都一样String,所以我只是想弄清楚这里发生了什么。

SettingsNavLinkView我确实通过专门添加 LocalizedStringKey 初始化来修改这个问题,如下所示:

struct SettingsNavLinkView: View {
  var label:String
  let localizedLabel = LocalizedStringKey(label) // <-- NEW

  var body: some View {
    Text(localizedLabel) // <-- UPDATED to use localizedLabel instead of label
  }
}

但为什么我必须这样做?为什么“日历”字符串在SettingsNavLinkView按照此代码传递给时没有自动本地化SettingsNavLinkView(label: "Calendar")

SwiftUI 本地化中的错误?我对本地化如何工作的不完全理解?我宁愿不必求助于LocalizedStringKey“简单字符串”……但是从“自动”本地化如何真正起作用的角度来看,我不确定我所要求的是否有效。

任何想法表示赞赏。谢谢!

标签: swiftui

解决方案


Because different Text constructors are inferred for literal string and for variable string and that is documented in SwiftUI API

/// Creates a text view that displays a stored string without localization.
///
/// Use this intializer to create a text view that displays — without
/// localization — the text in a string variable.
///
///     Text(someString) // Displays the contents of `someString` without localization.
///
/// SwiftUI doesn't call the `init(_:)` method when you initialize a text
/// view with a string literal as the input. Instead, a string literal
/// triggers the ``Text/init(_:tableName:bundle:comment:)`` method — which
/// treats the input as a ``LocalizedStringKey`` instance — and attempts to
/// perform localization.
///
/// By default, SwiftUI assumes that you don't want to localize stored
/// strings, but if you do, you can first create a localized string key from
/// the value, and initialize the text view with that. Using a key as input
/// triggers the ``Text/init(_:tableName:bundle:comment:)`` method instead.
///
/// - Parameter content: The string value to display without localization.
public init<S>(_ content: S) where S : StringProtocol

as they said no localization.

but next with localization:

/// Creates a text view that displays localized content identified by a key.
///
/// Use this intializer to look for the `key` parameter in a localization
/// table and display the associated string value in the initialized text
/// view. If the initializer can't find the key in the table, or if no table
/// exists, the text view displays the string representation of the key
/// instead.
///
///     Text("pencil") // Localizes the key if possible, or displays "pencil" if not.
///
/// When you initialize a text view with a string literal, the view triggers
/// this initializer because it assumes you want the string localized, even
/// when you don't explicitly specify a table, as in the above example. If
/// you haven't provided localization for a particular string, you still get
/// reasonable behavior, because the initializer displays the key, which
/// typically contains the unlocalized string.
///
/// If you initialize a text view with a string variable rather than a
/// string literal, the view triggers the ``Text/init(_:)-9d1g4``
/// initializer instead, because it assumes that you don't want localization
/// in that case. If you do want to localize the value stored in a string
/// variable, you can choose to call the `init(_:tableName:bundle:comment:)`
/// initializer by first creating a ``LocalizedStringKey`` instance from the
/// string variable:
///
///     Text(LocalizedStringKey(someString)) // Localizes the contents of `someString`.
///
/// If you have a string literal that you don't want to localize, use the
/// ``Text/init(verbatim:)`` initializer instead.
///
/// - Parameters:
///   - key: The key for a string in the table identified by `tableName`.
///   - tableName: The name of the string table to search. If `nil`, use the
///     table in the `Localizable.strings` file.
///   - bundle: The bundle containing the strings file. If `nil`, use the
///     main bundle.
///   - comment: Contextual information about this key-value pair.
public init(_ key: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil)

I would recommend to use the following (tested with Xcode 12.1 / iOS 14.1)

struct SettingsNavLinkView: View {
  var label: String
  
  var body: some View {
    Text(LocalizedStringKey(label))     // << inline !!
  }
}

推荐阅读