SwiftUI — 单复数形式

在 SwiftUI 中,开发者可以通过 String.LocalizationValueFoundation 框架的本地化规则 实现智能的单复数形式适配。以下是如何在 SwiftUI 中优雅处理单复数问题的完整方案:


一、基础方案:使用 .localizedStringWithFormat #

利用 String 的本地化方法,根据数值动态选择单复数形式:

struct ContentView: View {
    let appleCount = 5

    var body: some View {
        Text(String.localizedStringWithFormat(
            NSLocalizedString("%d apple(s)", comment: ""),
            appleCount
        ))
    }
}

字符串本地化文件 (Localizable.strings) 中定义规则:

// 英语
"%d apple(s)" = "%d {apple, plural, one{apple} other{apples}}";

// 中文
"%d apple(s)" = "%d{apple, plural, other{苹果}}"; // 中文通常不需要复数变化

二、SwiftUI 原生优化:LocalizedStringKey + 参数化 #

通过扩展 LocalizedStringKey 实现更简洁的语法:

extension Text {
    func inflecting(_ count: Int) -> Text {
        let format = String.LocalizationValue(
            localized: "item_count_plural",
            defaultValue: "\(count) items"
        )
        return Text(format)
    }
}

// 使用示例
struct ContentView: View {
    let itemCount = 1

    var body: some View {
        Text("item_count_plural", defaultValue: "\(itemCount) items")
            .inflecting(itemCount)
    }
}

本地化字符串定义(需在 Localizable.stringsdict 中配置复数规则):

<!-- Localizable.stringsdict -->
<dict>
    <key>item_count_plural</key>
    <dict>
        <key>NSStringLocalizedFormatKey</key>
        <string>%#@items@</string>
        <key>items</key>
        <dict>
            <key>NSStringFormatSpecTypeKey</key>
            <string>NSStringPluralRuleType</string>
            <key>NSStringFormatValueTypeKey</key>
            <string>d</string>
            <key>one</key>
            <string>%d item</string>
            <key>other</key>
            <string>%d items</string>
        </dict>
    </dict>
</dict>

三、高级方案:自定义 Inflect 修饰符 #

创建可复用的修饰符封装单复数逻辑:

struct Inflect: ViewModifier {
    let count: Int
    let singular: LocalizedStringKey
    let plural: LocalizedStringKey

    func body(content: Content) -> some View {
        Group {
            if count == 1 {
                Text(singular)
            } else {
                Text(plural)
            }
        }
    }
}

extension View {
    func inflect(singular: LocalizedStringKey, plural: LocalizedStringKey, count: Int) -> some View {
        modifier(Inflect(count: count, singular: singular, plural: plural))
    }
}

// 使用示例
Text("item")
    .inflect(
        singular: "You have 1 apple",
        plural: "You have %d apples",
        count: 5
    )

四、多语言复杂规则处理 #

对于俄语、阿拉伯语等有复杂复数形式的语言,必须使用 .stringsdict 文件:

<!-- 俄语复数规则(6种形式) -->
<key>message_count</key>
<dict>
    <key>NSStringLocalizedFormatKey</key>
    <string>%#@messages@</string>
    <key>messages</key>
    <dict>
        <key>NSStringFormatSpecTypeKey</key>
        <string>NSStringPluralRuleType</string>
        <key>NSStringFormatValueTypeKey</key>
        <string>d</string>
        <key>zero</key>
        <string>нет сообщений</string>
        <key>one</key>
        <string>%d сообщение</string>
        <key>two</key>
        <string>%d сообщения</string>
        <key>few</key>
        <string>%d сообщения</string>
        <key>many</key>
        <string>%d сообщений</string>
        <key>other</key>
        <string>%d сообщений</string>
    </dict>
</dict>

五、最佳实践 #

  1. 优先使用 .stringsdict:覆盖所有复数形式,避免硬编码条件判断
  2. 动态更新机制:配合 @State@ObservedObject 实现实时切换
  3. 测试工具:使用 Locale(identifier: "ar") 强制切换语言环境进行验证
  4. 性能优化:对高频更新的数值使用 NumberFormatter

六、完整代码示例 #

struct PluralDemoView: View {
    @State private var count: Int = 0

    var body: some View {
        VStack {
            Stepper("Count: \(count)", value: $count, in: 0...100)
            
            // 方案1:直接使用本地化键
            Text("message_count \(count)")
            
            // 方案2:自定义修饰符
            Text("Message")
                .inflect(singular: "1 message", plural: "%d messages", count: count)
        }
        .environment(\.locale, .init(identifier: "ru")) // 测试俄语规则
    }
}

通过以上方法,可以在 SwiftUI 应用中实现专业级的单复数适配方案,完美支持全球 200+ 语言的本地化需求。

字符串插值 #

在 Swift 5.5+ 和 SwiftUI 中,^[\(selectedpeople.count) Person](inflect: true) 是一种基于 Swift 本地化字符串插值 的高级语法,用于实现智能的单复数适配。以下是逐层解析:


一、语法结构与核心概念 #

1. 基础格式 #

^[\(value) Key](inflect: true)
  • ^[]:Swift 本地化字符串插值的特殊标识符,表示这是一个需要本地化处理的字符串。
  • \(value):动态插入的数值变量(如 selectedpeople.count)。
  • Key:本地化字符串的键(对应 .strings.stringsdict 中的条目)。
  • inflect: true:启用语法变形(如单复数、性别等),依赖系统级语言规则。

二、实际应用场景 #

示例代码解析 #

Text("^[\(selectedpeople.count) Person](inflect: true)")
  • 输入selectedpeople.count = 3
  • 输出(英文环境):3 People
  • 输出(中文环境):3 人(中文无需复数变化)

三、底层实现原理 #

  1. 依赖 .stringsdict 文件
    系统会自动查找 Localizable.stringsdict 中定义的复数规则。例如:

    <key>Person</key>
    <dict>
        <key>NSStringLocalizedFormatKey</key>
        <string>%#@count@</string>
        <key>count</key>
        <dict>
            <key>NSStringFormatSpecTypeKey</key>
            <string>NSStringPluralRuleType</string>
            <key>one</key>
            <string>%d Person</string> <!-- 单数 -->
            <key>other</key>
            <string>%d People</string> <!-- 复数 -->
        </dict>
    </dict>
    
  2. inflect: true 的作用
    根据当前语言环境的语法规则,自动调整单词形式:

    • 英语Person → People(复数变形)
    • 俄语:根据数值选择 6 种复数形式之一
    • 中文:保持 (无变化)

四、与传统方案的对比 #

方案代码量多语言支持维护成本适用场景
条件语句简单单复数,仅支持少数语言
.stringsdict需要完整复数规则支持
inflect: true快速实现,依赖系统级规则

五、使用注意事项 #

  1. 必须配置本地化文件
    如果未在 .stringsdict 中定义 Person 的复数规则,将回退到默认的 other 分支。

  2. 动态更新与状态绑定
    selectedpeople.count 变化时,文本会自动刷新:

    @State private var selectedpeople: [Person] = []
    
  3. 自定义变形规则
    若需要覆盖系统默认规则(如特殊行业术语),仍需手动编写 .stringsdict 配置。


六、完整示例代码 #

import SwiftUI

struct ContentView: View {
    @State private var selectedpeople: [Person] = [Person(), Person()] // 初始数量为2

    var body: some View {
        VStack {
            Text("^[\(selectedpeople.count) Person](inflect: true)")
                .font(.title)
            
            Button("Add Person") {
                selectedpeople.append(Person())
            }
        }
    }
}

struct Person: Identifiable {
    let id = UUID()
}
  • 效果:点击按钮时,文本会动态显示 1 Person2 People3 People(英文环境)。

七、扩展:其他格式选项 #

inflect: true 可与其他格式修饰符组合使用:

// 格式化数值 + 单复数适配
Text("^[\(selectedpeople.count.formatted(.number)) Person](inflect: true)")
// 输出示例:3,000 People

通过 ^[\(value) Key](inflect: true) 语法,开发者能以极简代码实现专业级国际化支持,是 SwiftUI 现代本地化实践的标志性特性。

本文共 1732 字,创建于 Mar 4, 2025
相关标签: Xcode, SwiftUI