Search code examples
ioscocoa-touchlocalizationformattinginternationalization

Localizing a naturally-formatted list of API data


Say I've downloaded this list of data from an API:

[
    "Anita",
    "Jean",
    "Peter"
]

I'd like to list this data in a natural sentece format. For example, in US English, this string should be displayed to the user:

Anita, Jean, and Peter

However, if the user is Swedish for example, they should see this string:

Anita, Jean och Peter

Notice that the Oxford comma is missing, and Swedish uses a different word for "and", which is what a Swedish user would expect to see. How can I format this data in a natural-language way that would respect the user's locality? There can be a variable amount of data, not necessarily just 3 items. My instinct is to subclass Formatter/NSFormatter, but I'd like to build this in a way I can easily expand it to support languages that I don't speak, so I'm wondering if there's an iOS-standard or 3rd party formatter that I haven't been able to find in my searches


Solution

  • Starting with iOS 13, Foundation has a ListFormatter type. According to your needs, you can use it in different ways to get a textual representation of your array.


    The simplest way to use ListFormatter is to use its static localizedString(byJoining:) method. The following Swift 5.1 / iOS 13 Playground sample code shows how to implement this method in order to convert your array into a localized string representation:

    import Foundation
    
    let array = [
        "Anita",
        "Jean",
        "Peter"
    ]
    
    let string = ListFormatter.localizedString(byJoining: array)
    print(string) // prints: Anita, Jean, and Peter (for en_US locale)
    

    As an alternative to localizedString(byJoining:), you can create an instance of ListFormatter and use string(from:) method. This can be useful if you need to specify a locale for your textual representation:

    import Foundation
    
    let array = [
        "Anita",
        "Jean",
        "Peter"
    ]
    
    let listFormatter = ListFormatter()
    listFormatter.locale = Locale(identifier: "fr_FR") // set only if necessary
    
    let string = listFormatter.string(from: array)
    print(String(describing: string)) // prints: Optional("Anita, Jean et Peter")
    

    If needed, you can also set the itemFormatter property of your ListFormatter instance with a specialized formatter type:

    import Foundation
    
    let array = [
        55,
        112,
        8
    ]
    
    let locale = Locale(identifier: "es_ES") // set only if necessary
    
    let numberFormatter = NumberFormatter()
    numberFormatter.locale = locale
    numberFormatter.numberStyle = NumberFormatter.Style.spellOut
    
    let listFormatter = ListFormatter()
    listFormatter.locale = locale
    listFormatter.itemFormatter = numberFormatter
    
    let string = listFormatter.string(from: array)
    print(String(describing: string)) // prints: Optional("cincuenta y cinco, ciento doce y ocho")