Localizing a naturally-formatted list of API data - ios

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

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")

In previous versions of iOS, this question remains unsolved and I'd still appreciate an answer if you have one. However, iOS 13 exposes NSListFormatter, which can be used to do exactly this. For example:
Swift:
ListFormatter().localizedString(byJoining: ["Anita", "Jean", "Peter"])
Objective-C:
[[NSListFormatter new] localizedStringByJoiningStrings:#["Anita", "Jean", "Peter"]];

Related

How to add commas or space for every 4 digits in swift 4?

My case is a little special, I need to insert space or comma for every 4 digits.
Example:
18686305
1868,6305 or 1868 6305
How can I do in swift 4?
A NumberFormatter is designed to convert numerical values to String values based on a pre-defined format. In your case, the following will insert grouping separators every four digits:
import Foundation
let groupingSeparator = "," // determined based on user input, as per the question
let formatter = NumberFormatter()
formatter.positiveFormat = "####,####"
formatter.negativeFormat = "-####,####"
formatter.groupingSeparator = groupingSeparator
if let string = formatter.string(from: 18686305) {
print(string) // prints "1868,6305"
}
The positiveFormat and negativeFormat variables follow the Unicode Technical Standard #35.

Can I use iOS9 Contacts Framework to get a formatted phone number?

I would like to be able to store a phone number in a standard way, e.g. just the digits (potentially with the '+' for the country code), something like these examples...
"17185555555"
"+17185555555"
"+447788888888"
... but I'd like to be able to DISPLAY it to a user in a properly formatted way, e.g.
"1 (718) 555-5555"
"+1 (718) 555-5555"
"+44 (7788) 888888"
...WITHOUT having to rely on a CNContactViewController to format it.
Obviously doing this just for US/Canada numbers would be easy - it's just one standard format - but I'd like it to be generic so it can work for numbers from any country. I know this question gives an answer for US/Can numbers only.
I know that I could use a CNContactViewController to display the number in the correct format, e.g.
let newContact = CNMutableContact()
newContact.givenName = "John"
newContact.familyName = "Smith"
newContact.phoneNumbers = [CNLabeledValue(label: CNLabelPhoneNumberiPhone, value: CNPhoneNumber(stringValue:"17185555555"))]
let contactView = CNContactViewController(forContact: newContact)
self.presentViewController(contactView, animated: true, completion: nil)
This will show the number on screen properly formatted, i.e.
1 (718) 555-5555
... so I know that something in the framework can do it. (This approach works for other country phone number formats, as long as you prefix the number with the right country code - e.g. "+44...")
From various other questions I know that I can get the raw digits and country code out of a CNContact, e.g. (following above example)
for pNumber: CNLabeledValue in newContact.phoneNumbers {
let value = pNumber.value as! CNPhoneNumber
let cc = value.valueForKey("countryCode") as? String
let digits = value.valueForKey("digits") as? String
print("cc:" + cc + ", " + digits)
}
... but this will just show the unformatted string again - not what I am looking for in this case.
Any help or other recommended approaches really appreciated!
My answer proposes another lib
You can format your numbers with this lib.
And you can use like this:
let phoneNumber: NBPhoneNumber = try phoneUtil.parse("17185555555", defaultRegion: yourRegion)
let formattedString: String = try phoneUtil.format(phoneNumber, numberFormat: .E164)

Swift / XMLParser / How to parse a specific element / CheatyXML

I want to parse and download the current EUR - USD exchange rate. I have descided to get the value from the European Central Bank Feed.
I'm using the CheatyXML XMLParser extension.
How can I get the USD value?
With the following code, I get the value: "European Central Bank". My String is an optional on porpuse. Because my app crashed like 1 trillion times during finding the correct code to get the currency rate...
let feedUrl = NSURL(string: "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml")
let parser: XMLParser! = XMLParser(contentsOfURL: feedUrl!)
let exchangeString: String? = parser["gesmes:Sender"]["gesmes:name"].string // Returns an optional String
print(exchangeString)
How do I get the value of <Cube currency="USD" ?
let blogName: String? = parser["Cube"]["Cube"].string // Returns an optional String
Is not working.
Help is very appreciated.
You need to go one level deeper (there's three "Cube" fields), then get the attributes and finally subscript with the right key, for example:
parser["Cube"]["Cube"]["Cube"].attributes["currency"] as? String // "USD"
parser["Cube"]["Cube"]["Cube"].attributes["rate"] as? String // "1.1287"

How to get detailed language of device in swift

I have a database on the server that contains all the languages keywords that I use inside my applications and these languages could be changed any time.
In the language table of database there is column for Language (id , key, value, lang).
In the android application I read the language from device and it returns for example en-GB or en-US.
But in the iOS application I can't get the language like the above example , It always returns just the language (en , es, fr).
So I can't query to database to get the language specified keywords on the iOS application. Because the languages on the database is en-GB, en-US style.
var langId: String? = NSLocale.preferredLanguages().first as? String
How can I get the language with more details ?
Start with the currentLocale() and ask questions about it. For example:
let lang = NSLocale.currentLocale().localeIdentifier
Or, at a finer level of granularity:
let langId = NSLocale.currentLocale().objectForKey(NSLocaleLanguageCode) as! String
let countryId = NSLocale.currentLocale().objectForKey(NSLocaleCountryCode) as! String
let language = "\(langId)-\(countryId)" // en-US on my machine
Swift 3 does not have an NSLocaleCountryCode, this is replaced by regionCode
https://developer.apple.com/reference/foundation/nslocale.key/1417845-countrycode
solution in Swift 5:
let langCode = Locale.current.languageCode ?? ""
let regionCode = Locale.current.regionCode ?? ""
let language = "\(langCode)-\(regionCode)"
Another solution is NSLocale.current.identifier
This will return e.g. en_us
You could replace _ for -. it will look like this: NSLocale.current.identifier.replacingOccurrences(of: "_", with: "-")
No need an extra effort, just use the following as per your requirement.
Locale.current.identifier //en_US
Locale.current.collatorIdentifier //en-US
For Swift 4 use instead:
NSLocale.current.identifier //Output: en-US, pt-BR
Swift 5
Locale.currentLanguage.rawValue
just use this code line can solve:
let identifier = Locale.current.identifier

Range(0...4) and substringWithRange?

I have to say that for a "modern" language Swift certainly makes a meal of specifying a range to capture a substring.
My question: Is there a way to create a <String.Index> using Range(0...4) or indeed a way to cast the result (of type <Int>) so that it can be used with substringWithRange?
let myString = "HappyDays"
var rangeString = Range(start: advance(myString.startIndex, 5), end: advance(myString.endIndex, -1))
myString.substringWithRange(rangeString) // >>> "Day"
.
// Can this be used to generate a range for substringWithRange?
var hardRange = Range(0...4)
Use an extension on string e.g. stackoverflow.com/a/24144365/1032372

Resources