iosstringlocalizationuikitios15

Taking advantage of the iOS 15 grammar agreement feature for English strings


New in iOS 15 there is a grammar agreement feature for English and Spanish. For example, I should be able to say

You have x apple.

And if x is 1, then "apple" is "apple", but if x is more than 1, then "apple" magically becomes "apples".

This sounds like a great way to get rid of all that code where I myself am looking at the number and choosing between hard-coded singular and plural options.

However, it looks to me like this feature is applicable only for localized apps, and only for attributed strings in UIKit. (SwiftUI is different because strings are localized automatically.) How do I do this for just a normal user-facing string?


Solution

  • You do have to use a localized attributed string in order to get this feature. But that doesn't mean the string you present has to be localized or attributed!

    Here's an example. At some point, we might say this:

    let count = 1
    let output = String(AttributedString(
        localized:"You have ^[\(count) \("apple")](inflect: true).").characters)
    print(output) // You have 1 apple.
    

    Now imagine that at some other point, we say this:

    let count = 2
    let output = String(AttributedString(
        localized:"You have ^[\(count) \("apple")](inflect: true).").characters)
    print(output) // You have 2 apples.
    

    Notice that my attributed string is the same in both. And yet, based on the count value, the runtime has changed "apple" to "apples" where needed. This is just what we wanted.

    So here's the deal, as far as I can tell.

    That gives you the automatic inflection. Then, in this instance, because I just want the string, I pass through the characters view of the attributed string and coerce that to a string.

    Observe that my app is not localized in any meaningful sense. I have no other languages declared for the app, and I have no .strings files or .stringsDict files. I'm only passing through the localized: constructor because that's how you access this feature.