What is the correct way to localize and pluralize a string when the number is not included in the text?
I know I can do the following with AttributedString (already a great feature):
var count = 3
AttributedString(localized: "^[\(count) Chief-of-staff](inflect: true)")
// "3 Chiefs-of-staff"
I want to list names in a table/list, and use either "Chief-of-staff" or "Chiefs-of-staff" for the list header, localized and as appropriate for the number of names in the list.
Following the excellent article here, this use-case looks analogous to the existing implementations of InflectionConcept. Maybe it needs a new count inflection concept to be implemented. One might then be able to do something like this (pseudo-code, does not work!):
var options = AttributedString.LocalizationOptions()
options.concepts = [.count(count)]
AttributedString(localized: "^[Chief-of-staff](agreeWithConcept: 1)", options: options)
The only progress I have made is to use Morphology directly, but this fails because the logic for determining GrammaticalNumber is locale-specific. The following works in English and other languages that only have two forms (singular and plural), but not in languages that use pluralTwo, pluralFew, pluralMary, and so on.
extension Morphology.GrammaticalNumber {
init(count: Int) {
switch count {
case 1: self = .singular
default: self = .plural
}
}
}
extension LocalizedStringResource.StringInterpolation {
mutating func appendInterpolation(_ input: String, count: Int) {
var morphology = Morphology()
morphology.number = .init(count: count)
var string = AttributedString(input)
string.inflect = InflectionRule(morphology: morphology)
appendInterpolation(string.inflected())
}
}
AttributedString(localized: "\("Chief-of-staff", count: count)")
One possibility is to use the number in the string and then strip it out, but this feels like a horrible hack:
var header = AttributedString(localized: "^[\(count) Chief-of-staff](inflect: true)")
let range = header.range(of: "\(count) ")!
header.removeSubrange(range)
// Chief-of-staff
Am I missing something obvious, or is there a neater solution?
While I agree it's a hack, here's at least some packaging to hide the mess using your solution: