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
相关产品推荐
相关产品推荐

