如何对依赖自动语法协议的字符串使用插值?
How to use interpolation with strings that rely on automatic grammar agreement?
我正在尝试解决一个看似简单的问题:在 SwiftUI Text
视图中向用户显示免费试用条款。我有一个设置为 "Week"
的单位字符串和一个可以从 1 切换到 2 的 value
。为了处理复数,我利用了 iOS 15 上的自动语法协议。我在我的 Localizable.strings
文件中准备好字符串,如下所示:
"Subscription period: %lld %@" = "^[%lld ^[%@](grammar: { partOfSpeech: \"noun\" })](inflect: true)";
为了构建字符串,我需要 使用字符串插值来添加单词 'free'。我知道在字符串文件中添加单词可以解决问题,但我不想那样做。我想普遍使用该字符串,不一定与免费试用有关。我不明白为什么我不能调用一些 init,给它传递一个键,然后得到一个我可以随意插入的纯字符串。我已经尝试了很多在所附代码片段中展示的方法。找不到字符串,或者未应用自动语法协议。似乎有 String.LocalizationValue.StringInterpolation
结构,但我不知道如何使用它,因为没有任何文档。我究竟做错了什么?如果有任何帮助,我将不胜感激!
struct ContentView: View {
/// This value mimics what **StoreKit 2** returns for `Product.SubscriptionPeriod.Unit` in its `localizedDescription` on iOS 15.4.
let unit = "Week"
@State var value = 1
var key: LocalizedStringKey {
"Subscription period: \(value) \(unit)"
}
var plainStringKey: String {
"Subscription period: \(value) \(unit)"
}
var body: some View {
Form {
Section {
// Works fine without string interpolation.
Text(key)
.foregroundColor(.green)
// `NSLocalizedString` doesn't work and it shouldn't as per its docs.
Text("\(NSLocalizedString(plainStringKey, comment: "")) free")
.foregroundColor(.red)
// Doesn't see the string.
Text("\(String(localized: String.LocalizationValue(stringLiteral: plainStringKey))) free")
.foregroundColor(.red)
// This way also doesn't see the string, which is strange, because it should be just like the following way, which does see the string.
Text("\(String(localized: String.LocalizationValue.init(plainStringKey))) free")
.foregroundColor(.red)
// Sees the string (only if I pass literal, not the plainStringKey property), but doesn't apply automatic grammar agreement.
Text("\(String(localized: String.LocalizationValue("Subscription period: \(value) \(unit)"))) free")
.foregroundColor(.red)
// MARK: Bad solution:
/*
- Doesn't work with Dynamic Type properly
- You have to approximate horizontal spacing
*/
HStack(spacing: 3) {
Text("Subscription period: \(value) \(unit)")
.textCase(.lowercase)
Text("free")
}
.foregroundColor(.orange)
}
Section {
Stepper(value: $value, in: 1...2) {
Text("Value: \(value)")
}
}
}
}
}
您可以下载示例项目here。
其实我和这段代码非常接近:
Text("\(String(localized: String.LocalizationValue("Subscription period: \(value) \(unit)"))) free")
但是使用 String(localized:)
是不正确的。我猜是因为自动语法协议使用markdown,这是AttributedString
的特权。我们应该使用 AttributedString(localized:)
。但即便如此,它也不会屈折变化,因为 Foundation 中显然存在错误。但是一旦打上补丁,它就会发挥作用。
这是正确的解决方案:
func freeTrialDuration() -> Text {
// Use plain string.
let duration: String = "^[\(value) ^[\(unit.lowercased())](grammar: { partOfSpeech: \"noun\" })](inflect: true)"
// Use the following way to get the localized string.
let localizedDuration = AttributedString(localized: String.LocalizationValue(duration))
return Text("\(localizedDuration) free")
}
我正在尝试解决一个看似简单的问题:在 SwiftUI Text
视图中向用户显示免费试用条款。我有一个设置为 "Week"
的单位字符串和一个可以从 1 切换到 2 的 value
。为了处理复数,我利用了 iOS 15 上的自动语法协议。我在我的 Localizable.strings
文件中准备好字符串,如下所示:
"Subscription period: %lld %@" = "^[%lld ^[%@](grammar: { partOfSpeech: \"noun\" })](inflect: true)";
为了构建字符串,我需要 使用字符串插值来添加单词 'free'。我知道在字符串文件中添加单词可以解决问题,但我不想那样做。我想普遍使用该字符串,不一定与免费试用有关。我不明白为什么我不能调用一些 init,给它传递一个键,然后得到一个我可以随意插入的纯字符串。我已经尝试了很多在所附代码片段中展示的方法。找不到字符串,或者未应用自动语法协议。似乎有 String.LocalizationValue.StringInterpolation
结构,但我不知道如何使用它,因为没有任何文档。我究竟做错了什么?如果有任何帮助,我将不胜感激!
struct ContentView: View {
/// This value mimics what **StoreKit 2** returns for `Product.SubscriptionPeriod.Unit` in its `localizedDescription` on iOS 15.4.
let unit = "Week"
@State var value = 1
var key: LocalizedStringKey {
"Subscription period: \(value) \(unit)"
}
var plainStringKey: String {
"Subscription period: \(value) \(unit)"
}
var body: some View {
Form {
Section {
// Works fine without string interpolation.
Text(key)
.foregroundColor(.green)
// `NSLocalizedString` doesn't work and it shouldn't as per its docs.
Text("\(NSLocalizedString(plainStringKey, comment: "")) free")
.foregroundColor(.red)
// Doesn't see the string.
Text("\(String(localized: String.LocalizationValue(stringLiteral: plainStringKey))) free")
.foregroundColor(.red)
// This way also doesn't see the string, which is strange, because it should be just like the following way, which does see the string.
Text("\(String(localized: String.LocalizationValue.init(plainStringKey))) free")
.foregroundColor(.red)
// Sees the string (only if I pass literal, not the plainStringKey property), but doesn't apply automatic grammar agreement.
Text("\(String(localized: String.LocalizationValue("Subscription period: \(value) \(unit)"))) free")
.foregroundColor(.red)
// MARK: Bad solution:
/*
- Doesn't work with Dynamic Type properly
- You have to approximate horizontal spacing
*/
HStack(spacing: 3) {
Text("Subscription period: \(value) \(unit)")
.textCase(.lowercase)
Text("free")
}
.foregroundColor(.orange)
}
Section {
Stepper(value: $value, in: 1...2) {
Text("Value: \(value)")
}
}
}
}
}
您可以下载示例项目here。
其实我和这段代码非常接近:
Text("\(String(localized: String.LocalizationValue("Subscription period: \(value) \(unit)"))) free")
但是使用 String(localized:)
是不正确的。我猜是因为自动语法协议使用markdown,这是AttributedString
的特权。我们应该使用 AttributedString(localized:)
。但即便如此,它也不会屈折变化,因为 Foundation 中显然存在错误。但是一旦打上补丁,它就会发挥作用。
这是正确的解决方案:
func freeTrialDuration() -> Text {
// Use plain string.
let duration: String = "^[\(value) ^[\(unit.lowercased())](grammar: { partOfSpeech: \"noun\" })](inflect: true)"
// Use the following way to get the localized string.
let localizedDuration = AttributedString(localized: String.LocalizationValue(duration))
return Text("\(localizedDuration) free")
}