You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift自动语法一致化:如何实现单复数双向适配?

问题:实现Swift语法一致化的单复数双向适配

我创建了如下数据模型:

import Foundation

struct Effort {
    let amount: Int
    let unit: String

    var text: String {
        "\(amount) \(unit)"
    }

    var attributedText: String {
        String(
            AttributedString(localized: "^[\(amount) \(unit)](inflect: true)").characters
        )
    }

    init(amount: Int, unit: String) {
        self.amount = amount
        self.unit = unit
    }
}

当传入单数形式的unit时,Swift自动语法一致化表现正常:

  • Effort(amount: 4, unit: "day").attributedText 返回结果:"4 days"
  • Effort(amount: 1, unit: "day").attributedText 返回结果:"1 day"

但如果传入复数形式的unit,自动语法一致化无法将其转为单数:

  • Effort(amount: 1, unit: "days").attributedText 返回结果:"1 days"

需求背景:服务器返回的单位已是复数形式,希望客户端无需关注服务器返回格式,实现单复数双向适配,哪怕手动编写词形变化规则也可以。

我目前的思路是:

AttributedString(localized: "^[\(amount) \(String(unit.dropLast()))](inflect: true)")

本质是去掉unit的最后一个字符,但这种方式显然不如系统原生处理健壮。


解决方案

方案1:利用NSLinguisticTagger实现智能单数转换

Swift的自动语法一致化仅支持以单数形式作为输入,因此核心思路是先将任意单复数的unit转换为单数原型,再交给系统处理。使用NSLinguisticTagger可以可靠地获取单词的原型(单数形式),覆盖大部分常规英文单词:

extension String {
    var singularForm: String {
        let tagger = NSLinguisticTagger(tagSchemes: [.lemma], options: 0)
        tagger.string = self
        var singular = self
        tagger.enumerateTags(in: NSRange(location: 0, length: self.utf16.count), scheme: .lemma, options: .omitWhitespace) { tag, range, _ in
            if let lemma = tag?.rawValue {
                singular = lemma
            }
        }
        return singular
    }
}

// 修改Effort的attributedText属性
struct Effort {
    let amount: Int
    let unit: String

    var text: String {
        "\(amount) \(unit)"
    }

    var attributedText: String {
        let singularUnit = unit.singularForm
        return String(AttributedString(localized: "^[\(amount) \(singularUnit)](inflect: true)").characters)
    }

    init(amount: Int, unit: String) {
        self.amount = amount
        self.unit = unit
    }
}

测试效果:

  • Effort(amount: 1, unit: "days").attributedText 返回:"1 day"
  • Effort(amount: 5, unit: "days").attributedText 返回:"5 days"
  • Effort(amount: 1, unit: "children").attributedText 返回:"1 child"

方案2:手动维护不规则词形映射表

对于一些特殊的不规则单复数变化(如children/child、feet/foot),可以在方案1的基础上补充手动映射表,确保覆盖所有场景:

extension String {
    var singularForm: String {
        // 不规则单复数映射
        let irregularPlurals: [String: String] = [
            "children": "child",
            "feet": "foot",
            "teeth": "tooth",
            "men": "man",
            "women": "woman",
            "geese": "goose"
        ]
        
        // 优先匹配不规则映射,保留原单词的大小写格式
        let lowercasedUnit = self.lowercased()
        if let singularBase = irregularPlurals[lowercasedUnit] {
            return self.hasPrefix(String(self.first!).uppercased()) ? 
                singularBase.capitalized(with: Locale.current) : 
                singularBase
        }
        
        // 常规词形转换
        let tagger = NSLinguisticTagger(tagSchemes: [.lemma], options: 0)
        tagger.string = self
        var singular = self
        tagger.enumerateTags(in: NSRange(location: 0, length: self.utf16.count), scheme: .lemma, options: .omitWhitespace) { tag, range, _ in
            if let lemma = tag?.rawValue {
                singular = lemma
            }
        }
        return singular
    }
}

方案3:手动实现常规词形转换(轻量场景)

如果不需要处理大量不规则单词,也可以手动实现常规的复数转单数规则,适合场景单一的情况:

extension String {
    var singularForm: String {
        let lowercasedUnit = self.lowercased()
        
        switch lowercasedUnit {
        case let str where str.hasSuffix("ies"):
            return String(str.dropLast(3)) + "y"
        case let str where str.hasSuffix("es"):
            return String(str.dropLast(2))
        case let str where str.hasSuffix("s"):
            return String(str.dropLast())
        default:
            return self
        }
    }
}

内容的提问来源于stack exchange,提问作者Richard Topchii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 18:33:24