NSLocale to Country name [closed] - ios

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have seen this answered in objective-C, but I don't know how to convert to swift.
My app receives the public information of a user from Facebook, and I need to convert the locale into the country name.
FBRequestConnection.startForMeWithCompletionHandler({
connection, result, error in
user["locale"] = result["locale"]
user["email"] = result["email"]
user.save()
println(result.locale)
})
For example, for a French user, the code sends "Optional(fr_FR)" to the log. However I need it to just send the country name. According to localeplanet.com, the display name of "fr_FR" is "French (France)". So in the log all I want is "France".

Working off of this SO question, I've whipped up a Swift translation. Try this:
let locale: NSLocale = NSLocale(localeIdentifier: result.locale!)
let countryCode = locale.objectForKey(NSLocaleCountryCode) as String
var country: String? = locale.displayNameForKey(NSLocaleCountryCode, value: countryCode)
// According to the docs, "Not all locale property keys
// have values with display name values" (thus why the
// "country" variable's an optional). But if this one
// does have a display name value, you can print it like so.
if let foundCounty = country {
print(foundCounty)
}
Updated for Swift 4:
FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"locale"]).start { (connection, result, error) in
guard let resultDictionary = result as? [String:Any],
let localeIdentifier = resultDictionary["locale"] as? String else {
return
}
let locale: NSLocale = NSLocale(localeIdentifier: localeIdentifier)
if let countryCode = locale.object(forKey: NSLocale.Key.countryCode) as? String,
let country = locale.displayName(forKey: NSLocale.Key.countryCode, value: countryCode) {
print(country)
}
}

Related

trying to convert a string to a decimal for iOS 13

I have an app that this was all working correctly before iOS 13. I've checked a few posts but they seem to be saying what I have already done.
I'm passing a string that has currency symbols and formatting, and I want to strip that and use the string value.
func changeCurrencyToDecimal(stringNumber:String) -> Decimal {
let numberFormatter = NumberFormatter()
// Pull apart the components of the user's locale
var locComps = Locale.components(fromIdentifier: Locale.current.identifier)
// Set the specific currency code
locComps[NSLocale.Key.currencyCode.rawValue] = options?.currencyCode // or any other specific currency code
// Get the updated locale identifier
let locId = Locale.identifier(fromComponents: locComps)
// Get the new custom locale
//numberFormatter.locale = Locale(identifier: locId)
if(options?.currencyCode == nil) {
print("There is no currency code so use the default locale")
numberFormatter.locale = Locale.current
}
else{
print("We have a currency code!")
numberFormatter.locale = Locale(identifier: locId)
}
numberFormatter.numberStyle = .currency
numberFormatter.currencySymbol = ""
numberFormatter.decimalSeparator = ","
print("\(stringNumber) is the number being passed")
let number = numberFormatter.number(from: stringNumber)
// check to see if the number is nil and if so, return 0.
print("\(number) is the number converted")
if number == nil{
return 0
}
else{
print("\(number!) is the number")
let amount = number?.decimalValue
return amount!
}
}
An example of a string that I am passing: $300.00 the $ always triggers a return of nil. If I pass 300.00, then the converter works just fine. But I need to be able to check for any currency the user has set for their device.
The currency codes that I am checking are the ones Apple supplies in the var currencyCode: String? { get } options is just where I am storing those codes.
The numberFormatter produces nil every time, and it seems because my decimal is not being stripped of its formatting.
Again this worked before iOS 13, so I am guessing something changed on Apple's side and I just might not have come acrossed it yet.
UPDATE
Here is a user seniaro. The user enters an amount. If the user hits save right away, I take the amount and convert it into a decimal to save in coredata. But if the user dismisses the keyboard, I take that amount and convert it into a string to display the currency symbol. I allow the user to set their currency, so using the locale doesn't work for me, as sometimes they are not using that system. So after the keyboard is dismissed and their amount is displayed with the correct formatting, I have it programmed that if they would happen to change their amount and once again dismiss the keyboard, and the same steps are repeated. The currency symbols are stripped and the string is converted into a decimal.
I hope this better gives you an idea of how this works in my app.
UPDATE
I've added an if/else statement to see if the locale has been set or if it comes back nil, and if so to set it to the Locale.current
Assuming the input string is using the device's current locale you can create a NumberFormatter with Locale.current and set the numberStyle to currency.
I used a small playground to test it:
import Foundation
func changeCurrencyToDecimal(_ stringNumber: String) -> Decimal? {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
let number = numberFormatter.number(from: stringNumber)
return number?.decimalValue
}
let numbersFormatted = ["$300", "$3,000.04", "3.000,04", "Wazaa", "3000000"]
numbersFormatted
.map { changeCurrencyToDecimal($0) }
.forEach { print($0 ?? "Not a value") }
This prints:
300
3000.04
Not a value
Not a value
3000000
If you keep getting nil as a result your device's locale is different as the sample string number you are using in the question ($300).

Can Locale identify country by currency code?

I was looking for that answer in here, and also on official documentation, but I can't find the answer for that. I've got currency codes (e.g. "EUR"). I need to get country code (e.g "EU"). So I've seen that I can do it inversely (getting currency code by using country code), but I was trying to change this solution for my needs, and I got nil as result. Also I know I can use simple solution - remove last letter from currency code to get country code (It works for most cases from my API data, but not everywhere) - but I feel this approach is not safe. So my question is like in title: Can I identify country from currency code by using Locale?
Here is my approach:
with NSLocale
extension NSLocale {
static func currencySymbolFromCode(code: String) -> String? {
let localeIdentifier = NSLocale.localeIdentifier(fromComponents: [NSLocale.Key.currencyCode.rawValue : code])
let locale = NSLocale(localeIdentifier: localeIdentifier)
return locale.object(forKey: NSLocale.Key.countryCode) as? String
}
}
with Locale
extension Locale {
static let countryCode: [String: String] = Locale.isoCurrencyCodes.reduce(into: [:]) {
let locale = Locale(identifier: Locale.identifier(fromComponents: [NSLocale.Key.currencyCode.rawValue: $1]))
$0[$1] = (locale.regionCode)
}
}
A currency can be associated with multiple countries, you should create your own custom data implementation, such as following json
{
"EUR":"PL",
"USD":"CA"
}
Create a file in your Xcode project and name it for e.g. data.json, and paste such JSON as above, finally use following method
func getCountryCode(currency: String) -> String? {
var countryCode: String?
let url = Bundle.main.url(forResource: "data", withExtension: "json")!
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: String]
countryCode = json?[currency]
} catch {
print(error.localizedDescription)
}
return countryCode
}
Usage
if let countryCode = getCountryCode(currency: "EUR") {
print(countryCode)
}
The answer from #AamirR is a bit not accurate.
EUR != PL, PL = PLN = Poland
USD != CA, CA = CAD = Canada
In majority of cases (with some exceptions), first 2 letters of currency code = ISO 3166-2 country code:
PLN = PL = Poland
GBP = GB = Great Britain
USD = US = United States of America
AMD = AM = Armenia
CZK = CZ = Czech Republic
BRL = BR = Brazilia
and so on ...
So if you have currency codes you can get country names (with 99% accuracy) as follows:
let currencyCode = "USD"
let countryCode = String(currencyCode.prefix(2))
let countryName = Locale.current.localizedString(forRegionCode: countryCode)
print(countryName)
Optional("United States")
However, as some commented above, EU for example, is not a country, and looks like in your specific case you do not need a country flag, you need "currency flag", so for EUR you will display EU flag, which is correct, as you cant determine which EU country flag to display - France, Germany, etc ... so this approach might work for mapping currencies to countries flags.
Though there are some exceptions, in terms of first 2 letters of currency code not matching first 2 letters of country name, from ISO 3166-2 country code perspective, they still match:
CHF = CH = Switzerland
HRK = HR = Croatia
DKK = DK = Denmark
... and so on
The other interesting pattern is that first 2 letters of currency codes, would usually match country internet domain, but again, there are some exceptions from this too.

iOS: Use CNPostalAddressFormatter to show contact addresses with proper delimiter (e.g., comma) between sections

The Apple documentation suggests using CNPostalAddressFormatter to display addresses as a formatted string, which helps immensely with internationalization.
This code:
let postalString = CNPostalAddressFormatter.string(from: postalAddress.value, style: .mailingAddress)
yields this work address for the John Appleseed contact on the simulator:
3494 Kuhl Avenue
Atlanta GA 30303
The address is missing the comma between city and state.
Is there a way for CNPostalAddressFormatter to insert the comma? The documentation doesn't list anything.
If not, doesn't this mean you must manually format addresses for each locality, and are unable to use CNPostalAddressFormatter for localization?
Here is my snippet to add "," after city for US addresses.
#if canImport(Contacts)
import Contacts
public extension CLPlacemark {
/// Get a formatted address.
var formattedAddress: String? {
guard let postalAddress = postalAddress else {
return nil
}
let updatedPostalAddress: CNPostalAddress
if postalAddress.isoCountryCode == "US" {
// add "," after city name
let mutablePostalAddress = postalAddress.mutableCopy() as! CNMutablePostalAddress
mutablePostalAddress.city += ","
updatedPostalAddress = mutablePostalAddress
} else {
updatedPostalAddress = postalAddress
}
return CNPostalAddressFormatter.string(from: updatedPostalAddress, style: .mailingAddress)
}
}
#endif

How to give users in iOS the choice to choose their own currency

I have an iOS app that keeps getting feedback from the users that they would like the ability to set their own currency type. Right now I'm using the currencyFormatter.locale to get the user local currency and use that for the app. My thought is that I might be able to create a tableView in the settings section of the app and let the users pick the currency they want to use.
I would save there desired currency and then use that instead of the locale one being displayed now.
To make this work what data would I need to pull? Would this data be supplied by Apple? I think I would need currency codes but would this be the correct data to display for the user to choose from?
I found this repo and it seems like this is something that would be useful, but it doesn't seem to be supported/updated anymore.
I just need some help being pointed in the correct direction for this, as my research isn't making any progress for me.
Money
EDIT
I found an app that I think is doing something similar to what I would like to implement.
You're right, you should make a list screen and let the users choose their desired currency from that. Save that currency and use it instead of the locale. You can use this code to get a list of currency names and currency symbols.
NSLocale *locale = [NSLocale currentLocale];
NSArray *currencyCodesArray = [NSLocale ISOCurrencyCodes];
NSMutableDictionary *currenciesDict = [NSMutableDictionary new];
for (NSString *currencyCode in currencyCodesArray) {
NSString *currencyName = [locale displayNameForKey:NSLocaleCurrencyCode value:currencyCode];
NSString *currencySymbol = [locale displayNameForKey:NSLocaleCurrencySymbol value:currencyCode];
if (currencyName != nil && currencySymbol != nil) {
[currenciesDict setValue:currencySymbol forKey:currencyName];
}
}
return currenciesDict;
Edit
Here's the same code in swift.
let locale = NSLocale.current as NSLocale
let currencyCodesArray = NSLocale.isoCurrencyCodes
var currenciesDict = [String: String]()
for currencyCode in currencyCodesArray {
let currencyName = locale.displayName(forKey: NSLocale.Key.currencyCode, value : currencyCode)
let currencySymbol = locale.displayName(forKey: NSLocale.Key.currencySymbol, value : currencyCode)
if let currencySymbol = currencySymbol, let currencyName = currencyName {
currenciesDict[currencySymbol] = currencyName
}
}
print(currenciesDict)
Edit 2
Same code with custom model.
This is how your custom model will look like.
class CurrencyModel {
var currencyName = ""
var currencyCode = ""
}
And this is how the code will look like.
let locale = NSLocale.current as NSLocale
let currencyCodesArray = NSLocale.isoCurrencyCodes
var currencies = [CurrencyModel]()
for currencyCode in currencyCodesArray {
let currencyName = locale.displayName(forKey:
NSLocale.Key.currencyCode, value : currencyCode)
let currencySymbol = locale.displayName(forKey:NSLocale.Key.currencySymbol, value : currencyCode)
if let currencySymbol = currencySymbol, let currencyName = currencyName {
let currencyModel = CurrencyModel()
currencyModel.currencyName = currencyName
currencyModel.currencyCode = currencyCode
currencies.append(currencyModel)
}
}
print(currencies)
You should set the currencySymbol of the formatter instead:
let currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .currency
currencyFormatter.currencySymbol = "¥"
currencyFormatter.string(from: 123) // "¥123.00"
You can provide the user with a UITextField to let them enter any arbitrary string as the currency symbol.
If you want the user to choose a locale from a list and use that locale's currency symbol, the currencySymbol property of a Locale instance can also be used.
Clarification on locales and currencies:
Currencies and locales don't have a one-to-one correspondence. One currency can be used in multiple locales, but one locale usually has one corresponding currency, as there is a currencySymbol in Locale. And locale is used to decide where the currency symbol is. In France, for example, the currency symbol would be placed at the end. This means that if you just have a currency symbol, there is no "right" place for it in a string, because you have not specified a locale. So if you want the currency symbol to be at the user's expected place, ask the user for the locale they desire, and not the currency symbol. If you don't care whether the currency symbol is displayed at the user's expected place, then display it with the user's current locale.

Get currency code / symbol from language code in iOS [duplicate]

This question already has answers here:
Get local currency in swift
(2 answers)
iPhone: How to get local currency symbol (i.e. "$" instead of "AU$")
(7 answers)
Closed 5 years ago.
In my swift project, I have to show the list of available Locale Identifiers. If user selects a locale like "ar" i.e. lanugage code alone. How I can get the currency symbols or number formats for this.
func listCountriesAndCurrencies() {
let localeIds = Locale.availableIdentifiers
var countryCurrency = [String: String]()
for localeId in localeIds {
let locale = Locale(identifier: localeId)
if let country = locale.regionCode, country.count == 2 { // how to get currency for locale without region code ?
if let currency = locale.currencySymbol {
countryCurrency[localeId] = currency
}
}
}
let sorted = countryCurrency.keys.sorted()
for country in sorted {
let currency = countryCurrency[country]!
print("country: \(country), currency: \(currency)")
}
}
My question is explained in the code comment

Resources