My app is displaying the price of an in app purchase product. How can I (at design time) enumerate all the currency symbols and characters used in all of Apple's international app stores? I am displaying text in my app using "texture atlas" based bitmap fonts, i.e. I have to manually include each character I want to display.
I realize that this is a moving target, so I plan to make my logic forgiving. For example if some future equivalent of the Euro symbol is added by Apple and somebody's running an old version of my app, I will silently drop that character and just display the numeric part as "2.99" or "2,99" etc.
But how can I make my list as accurate as possible today, per Apple's official list?
Here's how the string is formatted (straight from Apple's sample):
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:product.price];
Nobody's rushing to answer this, so here's the best option I've found so far. Basically, the approach would be scraping the currency symbols and Latin character alternates from the following page:
http://en.wikipedia.org/wiki/Currency_symbol#List_of_presently-circulating_currency_symbols
Notice that there is a generic currency symbol that can be used as a fallback.
If anybody has a better answer (meaning somehow gleaned from Apple), I'll be happy to accept your answer over this one.
I quickly wrote a Swift playground to grab the NumberFormatter's output for every available locale. Filtering this to only include currency symbols and punctuation gives a relatively complete set of characters to include.
let price = 0 as NSDecimalNumber
let availableIdentifiers = Locale.availableIdentifiers
var allCurrencySymbols: String = ""
for identifier in availableIdentifiers
{
let locale = Locale(identifier: identifier)
let formatter = NumberFormatter()
formatter.formatterBehavior = NumberFormatter.Behavior.behavior10_4
formatter.numberStyle = NumberFormatter.Style.currency
formatter.locale = locale
let formattedPrice = formatter.string(from: price)!
let currencySymbolsOnly = formattedPrice.replacingOccurrences(of: "0", with: "")
allCurrencySymbols.append(currencySymbolsOnly)
}
var set = Set<Character>()
let allCurrencySymbolsMinusDuplicates = String(allCurrencySymbols.characters.filter{ set.insert($0).inserted } )
print(allCurrencySymbolsMinusDuplicates)
On my Mac, this produces the output…
, ¤KMFCABuRE.₪٠٫۰$০₹YDTShN¥H€₺₦L₸rOP£៛၀nG₵денКМأم०UفجقVsk/zł؋Q༠Zد֏رسل₱یاoʻmكብርXdjbI₾ع₽сом₼ت₩f₡¥Wරුeب₭नेरूtإë₴l৳يp₫лвč₮
…which you can use to create your bitmap font. But remember your source font will need to support the characters, too.
Related
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 months ago.
Improve this question
I ran into such a problem: from the server I receive such a figure in the string format "20760.326586753041" (example), but I want to change it so that the user's screen has such a figure 20,761.93.
How can I format it?
Tried to do like this:
func setup(coin: Coin) {
self.nameCoin.text = coin.name
self.symbolCoin.text = coin.symbol
self.priceCoin.text = String(format: "%.2f", coin.priceUsd)
}
Show 0.0 on screen
You should use a NumberFormatter
import Foundation
let coinFormatter : NumberFormatter = {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.numberStyle = .decimal
formatter.locale = Locale(identifier: "en-US")
return formatter
}()
let stringFromServer = "20760.326586753041"
if let value = coinFormatter.number(from: stringFromServer),
let reformattedString = coinFormatter.string(for: value) {
print(reformattedString)
}
I create a number formatter called coinFormatter that format numbers into decimals that have at most 2 decimal places. The code below that shows how you might use such a number formatter to convert the string from the server to a number, then the number back to a string with the expected format.
It also looks like you might be trying to format the number as a currency value. There are mechanisms in NumberFormatter for properly formatting currency values that you should look into as well.
P.S. As you will see in the comments below, NumberFormatter takes into account many complexities like the Locale and common radix marks used, how you want negative numbers represented, or whether the currency symbol should be written before or after the number when representing money. Please take the time to learn more about NumberFormatter and the power it offers you.
I use In App Purchases in an iOS app. I want to display the price in the right format depending on the user/device.
Here's my code:
let price=product.price
let numberFormatter = NumberFormatter()
numberFoxrmatter.formatterBehavior = .behavior10_4 //doesn't change anything if I remove this line
numberFormatter.numberStyle = .currency
numberFormatter.locale = product.priceLocale
let formattedPrice=numberFormatter.string(from: price)
But the currency symbol is not the good one and/or misplaced in some cases.
In my example, the price product is $19.99 or 20,99€.
Examples
From device:
product.priceLocale: en_FR#currency=EUR (fixed)
Locale.current: en_FR (current)
Output: €20,99
Should display: 20,99€
From simulator:
product.priceLocale: en_FR#currency=EUR (fixed)
Locale.current: en_US (current)
Output: $20.99
Should display: 20,99€ or $19.99
I have several users who have the same issue with other currencies where the symbol should be placed after the price, unlike the dollars format. And another user who sees $7290 instead of 7290₸ (which is quite a different price...).
I'm pretty sure it has to do with the language setting or the Locale.current. But if I change my primary language to French on my device, I have the same price "€20,99". What is weird is my Locale.current switches to en_US (current).
Any way to solve this?
Another solution I'd be happy with: display the price in dollars for everyone, whatever the user's language & currency.
Try this
let currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true
currencyFormatter.numberStyle = .currency
// localize to your grouping and decimal separator
currencyFormatter.locale = Locale.current
// We'll force unwrap with the !, if you've got defined data you may need more error checking
let priceString = currencyFormatter.string(from: 9999.99)!
print(priceString) // Displays $9,999.99 in the US locale
Example
currencyFormatter.locale = Locale(identifier: "fr_FR")
if let priceString = currencyFormatter.string(from: 9999.99) {
print(priceString) // Displays 9 999,99 € in the French locale
}
For more detail please check https://supereasyapps.com/blog/2016/2/8/how-to-use-nsnumberformatter-in-swift-to-make-currency-numbers-easy-to-read
Locale setting is key to the correct output. en_FR reads like English language and french region. This will result in formatted output for an english speaker with french price -> €10.00
Use the simulator and set region & language to french and use Locale.current. It should read fr_FR and give correct output. 10,00€
Did you try to change language and region on the simulator and does it effect priceLocale?
Situation
I want to format a Double 23.54435678 into a String like 23.54 fps respecting the user's locale.
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
let formatted = formatter.string(from: fps as NSNumber)! + " fps"
For the localized number formatting I use DateFormatter.
Question
How should I handle the unit part? Is it valid to just append the unit to the formatted number? Is the placement of the symbol not locale dependent? How do I handle that?
Cocoa has no built-in support for the unit "frames per second", so you will have to provide the suffix yourself, e.g. using Xcode's localization system.
You still need to format the numeric value with NumberFormatter for the current locale and then insert the resulting number string into the localized format string:
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
let numberString = formatter.string(from: fps)
let formatString = NSLocalizedString("%# fps", comment: "") // provide localizations via .strings files
let fpsString = String(format: formatString, arguments: numberString)
If unit placement is locale-dependent (you will have to this find out yourself for the target locales of your app), you have to deal with this manually as well. You can leverage the localization system here by providing localizations with an adequately positioned placeholder for the numeric value, e.g. %# fps for English and x %# yz for... well, Fantasy Language.
The following with return १२३४५६७८९० instead of 123456790:
let numberFormatter = NSNumberFormatter()
numberFormatter.locale = NSLocale(localeIdentifier: "hi_IN")
numberFormatter.stringFromNumber(1234567890)
Is there a way to configure NSNumberFormatter so that arabic numeral is used?
If you want to get ١٢٣٤٥٦٧٨٩٠ then use the localeIdentifier ar_SA or ar_IN
If you want to still get the return value as 123456789 then you can use any other indian locale that does not contain the numerals in its respective script. For e.g. en_IN. Refer https://gist.github.com/jacobbubu/1836273 for the locale identifiers that you want to use.
How do I know which is the default measure system (imperial or metric) on iOS ?
How do I get this preference from the device settings, so I know what to display in my app ?
thanks
The NSLocale can tell you:
NSLocale *locale = [NSLocale currentLocale];
BOOL isMetric = [[locale objectForKey:NSLocaleUsesMetricSystem] boolValue];
Only three countries do not use the metric system: the US, Liberia and Myanmar. The later uses its own system, the former two use Imperial Units.
Apples documentation says (emphasis mine):
NSLocaleUsesMetricSystem
The key for the flag that indicates whether the locale uses the metric system.
The corresponding value is a Boolean NSNumber object. If the value is NO, you can typically assume American measurement units (for example, the statute mile).
Available in iOS 2.0 and later.
#DarkDust answer for swift3
//User region setting return
let locale = Locale.current //NSLocale.current
//Returns true if the locale uses the metric system
let isMetric = locale.usesMetricSystem
here's a swift version
var locale = NSLocale.currentLocale()
let isMetric = locale.objectForKey(NSLocaleUsesMetricSystem) as! Bool
For swift 3
let locale = NSLocale.current
let isMetric = locale.usesMetricSystem
As others mentioned before, the UK uses a mix of metric and imperial units.
I would recommend using the new MeassurementFormatter introduced in iOS 10 which handles most of these discrepancies:
import Foundation
let locale = Locale(identifier: "EN_UK")
locale.usesMetricSystem // true!
var formatter = MeasurementFormatter()
formatter.locale = locale
formatter.string(from: Measurement(value: 1000, unit: UnitLength.meters)) // 0.621 mi
To render a distance as a string in local, natural unit, use:
let distanceInMeters: CLLocationDistance = 1000
let formatter = MeasurementFormatter()
formatter.string(from: Measurement(value: distanceInMeters, unit: UnitLength.meters)) // 0.621 mi
Official documentation: https://developer.apple.com/documentation/foundation/measurementformatter
You should probably just have a setting in your app and let your users choose -- this is what Apple does in the Weather app.
If you want to choose a sensible default you could look at the locale. If it's US, pick imperial otherwise choose metric. It is a heuristic, it will be wrong sometimes, but it's just a default that can be changed.