I have two different Apps that I run from XCode on the same device.
In AppDelegate application didFinishLaunchingWithOptions I print out the following debugging message:
print( Locale.current )
In one App it prints out sv_SE (as I would expect), but on the other App it prints out en_SE !!
As a result, dateFormatter.string will produce english names instead of the Swedish names that I where expecting.
func dayOfWeek(date:Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
dateFormatter.locale = Locale.current // locale is en_SE not sv_SE ???
let dayname = dateFormatter.string(from: date).capitalized
return dayname // returns SATURDAY not LÖRDAG
}
Question: Why do I get different locales in different Apps when ran on the same device?
I found the answer here: Locale.current reporting wrong language on device
The answer was that Locale.current is not the locale set on the device, but a "compromise" between that locale what the App supports. My first App did support swedish but my second did not. To get the locale on the device one should instead use Locale.preferredLanguages.first, as is done in the answer to Locale.current reporting wrong language on device.
Related
I’m working on a date/time app that does things that Apple didn’t build into its OSs and bundled applications.
I’m leveraging built-in classes like Date and DateFormatter for certain parts, and I’m getting some odd results for dates on the Hebrew calendar localized in Hebrew.
If I request the standard full, long, medium, and short formats, everything is fine. But when I supply a template for DateFormatter to use, something weird happens.
That middle row in the three-date complication (containing the date on the Gregorian, Hebrew, and Muslim calendars), is wrong.
It should be יום א׳, ז׳ בחשון תשפ״א, because the convention is for Hebrew dates in Hebrew to use Hebrew numerals.
The relevant code that produces the Hebrew date:
We first start with setting the locale and time zone of our specialized Hebrew and Islamic calendar class’s date formatter.
if localeIdentifier == "" {
self.dateFormatter.locale = Locale.current
} else {
self.dateFormatter.locale = Locale(identifier: localeIdentifier)
}
self.dateFormatter.timeZone = timeZone
After some code dealing with generating the correct time (not really used in a complication), we set the DateFormatter’s dateStyle or date format based one the value of an enum (“majorDateFormat”); if we are supposed to use a template (case .localizedLDML), we use a specific string (“dateGeekFormat”) as the template.
In this case, I have specified a format with weekday, day, month, and year narrow enough to squeeze into a watchOS complication without losing information. We then generate the date string for the Date fixedNow.
switch majorDateFormat {
case .localizedLDML:
let dateFormat = DateFormatter.dateFormat(fromTemplate:dateGeekFormat, options: 0, locale: self.dateFormatter.locale)!
self.dateFormatter.setLocalizedDateFormatFromTemplate(dateFormat)
case .none:
self.dateFormatter.dateStyle = .none
case .full:
self.dateFormatter.dateStyle = .full
case .long:
self.dateFormatter.dateStyle = .long
case .medium:
self.dateFormatter.dateStyle = .medium
case .short:
self.dateFormatter.dateStyle = .short
default:
self.dateFormatter.dateStyle = .full
} // switch majorDateFormat
let dateString = self.dateFormatter.string(from: fixedNow)
Now the weird things are:
For the Islamic calendar in Arabic, Arabic numerals are correctly produced.
This code actually used to correctly work for the Hebrew calendar in Hebrew.
I had no problems with this on watchOS 5 on my old series 0 Apple Watch, but I recently upgraded to an Apple Watch SE running watchOS 7 and discovered this problem.
Has anyone else seen such a problem before? Is this a bug on Apple’s part, or is there something I’ve missed?
Thanks in advance for any help anyone can provide.
Here's how you can produce "יום א׳, ז׳ בחשון תשפ״א"
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .hebrew)
formatter.locale = Locale(identifier: "he")
formatter.dateStyle = .short // must come before date format below
formatter.setLocalizedDateFormatFromTemplate("c dd MMMM yyyy")
print(formatter.string(from: Date())) // יום א׳, ז׳ בחשון תשפ״א
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?
I developed an application in which I use a singleton for keeping a single instance of an NSDateFormatter.
My date formatter is initialized as below:
timeDateFormatter = NSDateFormatter()
timeDateFormatter.locale = NSLocale(localeIdentifier:EN_US_POSIX_LocaleIdentifier)
timeDateFormatter.dateFormat = StringDateType.DetailSigningHour.rawValue
let EN_US_POSIX_LocaleIdentifier = "en_US_POSIX"
With 24-Hour Time turned off the application runs as it should, but when going to Settings-->General-->Date&Time and turning on 24-Hour Time, then going and tapping on the app icon, the app tries to come up and then immediately exit.
I read that this may be an unknown issue for Apple.
Can you help me with some more information about this?
Update
When the system time was changed from 12-hour format to 24-hour format, my date formatter was messed up.
The good part is that the system is sending a notification (NSCurrentLocaleDidChangeNotification) letting you know that the locale has changed. All what I have done was to add an observer for this notification and to re-initialize my date formatter.
Setting the locale to en_US_POSIX, you force the 12-hour mode, regardless of the user's 24/12-hour mode setting.
I'm going to assume that you're force-unwrapping the result of timeDateFormatter.dateFromString with a wrong date format.
If you do something like this:
let timeDateFormatter = NSDateFormatter()
timeDateFormatter.locale = NSLocale(localeIdentifier:"en_US_POSIX")
timeDateFormatter.dateFormat = "hh:mm"
And force-unwrap the result:
let d = timeDateFormatter.dateFromString("11:42")!
You will get a crash at runtime if the date string is more than 12h:
let d = timeDateFormatter.dateFromString("13:42")! // crash
because "hh:mm" deals with 12h format only.
To use 24h format you should use "HH:mm":
timeDateFormatter.dateFormat = "HH:mm"
And to avoid crashes, avoid force-unwrapping:
if let d = timeDateFormatter.dateFromString("11:42") {
print(d)
} else {
// oops
}
If my diagnostic is wrong, please add details to your question. :)
I do not know that issue, however I always used my formatters this way, maybe this is also convenient to you.
let formatter = NSDateFormatter()
formatter.timeStyle = NSDateFormatterStyle.ShortStyle // here are different values possible, and all are good declared.
formatter.dateStyle = .MediumStyle // Same possible values like in timeStyle.
I have the following code:
var displayValue: Double{
get{
println("display.text =\(display.text!)")
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
}
set{
display.text = "\(newValue)"
userIsInTheMiddleOfTypingANumber = false;
}
}
It works fine in the simulator. but when I try it on phone it crashes.
here is the console:
digit= 3
display.text =3
operandStack =[3.0]
digit= 2
display.text =2
operandStack =[3.0, 2.0]
display.text =6.0
fatal error: unexpectedly found nil while unwrapping an Optional value
This line:
NSNumberFormatter().numberFromString(display.text!)!
is returning nil which causing the app to crash cause it couldn't unwrap the optional. I really don't know what's wrong. I'm following some tutorials in iTunes U.
any help would be appreciated.
Try:
get{
println("display.text =\(display.text!)")
let formatter = NSNumberFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
return formatter.numberFromString(display.text!)!.doubleValue
}
Because, NSNumberFormatter uses devices locale by default, it's possible that the decimal separator is not ".". For example:
let formatter = NSNumberFormatter()
formatter.locale = NSLocale(localeIdentifier: "ar-SA")
print(formatter.decimalSeparator!) // -> outputs "٫"
formatter.numberFromString("6.0") // -> nil
The formatter that uses such locales cannot parse strings like "6.0". So if you want consistent result from the formatter, you should explicitly specify the locale.
As for en_US_POSIX locale, see the document:
In most cases the best locale to choose is en_US_POSIX, a locale that's specifically designed to yield US English results regardless of both user and system preferences. en_US_POSIX is also invariant in time (if the US, at some point in the future, changes the way it formats dates, en_US will change to reflect the new behavior, but en_US_POSIX will not), and between platforms (en_US_POSIX works the same on iPhone OS as it does on OS X, and as it does on other platforms).
I am reading the tutorial provided by Raywenderlich, Chapter 29 What’s New with Testing, and run into a strange problem.
Following is the code in the tutorial converting a string into date:
// Convert date string to date.
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss zzz"
var date: NSDate? = dateFormatter.dateFromString(dateString)
The dateString is of the form
2014-06-21 14:56:00 EST
However, the date variable is always nil.
NOTE: when playing this code in the playground, it works properly, as the image shows:
I am working in iOS SDK 8.0. Is it a bug of the SDK?
Updated
I am testing using a latest iPod Touch with iOS 8.
When you set a dateFormat string you must also set the locale property to something that is compatible with the format provided. Otherwise the locale will be based on the device settings which may not be compatible.
The date format you provided here will work with the "en" locale but it will not work with many others, such as "eu". Here's an example:
let dateString = "2014-06-21 14:56:00 EST"
let localeStr = "eu" // this will give nil
let localeStr = "us" // this will succeed
let dateFormatter = NSDateFormatter()
dateFormatter.locale = NSLocale(localeIdentifier: localeStr)
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss zzz"
var date: NSDate? = dateFormatter.dateFromString(dateString)
The problem here is "EST" which only exists in the north america. In all other locales it is an invalid timezone. If you change your date string to this:
"2014-06-21 14:56:00 UTC-5"
Then it the date will correctly format no matter what value locale is set to.
NSDateFormatter's behaviors are heavily depends on it's locale.
By default, it uses device locale.
If you want consistent result from it, You should manually specify the locale. In most cases, en_US_POSIX is the best.
The document says:
If you're working with fixed-format dates, you should first set the locale of the date formatter to something appropriate for your fixed format. In most cases the best locale to choose is en_US_POSIX, a locale that's specifically designed to yield US English results regardless of both user and system preferences. en_US_POSIX is also invariant in time (if the US, at some point in the future, changes the way it formats dates, en_US will change to reflect the new behavior, but en_US_POSIX will not), and between platforms (en_US_POSIX works the same on iPhone OS as it does on OS X, and as it does on other platforms).
Like this:
let dateString = "2014-06-21 14:56:00 EST"
let dateFormatter = NSDateFormatter()
dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss zzz"
var date: NSDate? = dateFormatter.dateFromString(dateString)