Decimal Separator of Decimal Pad differs from Locale.current.decimalSeparator

I am trying to find out which decimal separator is used by the decimal pad keyboard in iOS, so I can convert strings entered by the user to numbers with NumberFormatter and back.


As I want to pre-fill the text field with an existing value, I need to have a number formatter that uses the same decimal separator as the decimal pad keyboard.


The language that my device is set to (German, Germany) uses a comma as the decimal separator. I have configured iOS to have the German keyboard as the primary and active keyboard and English (US, QWERTY) as a secondary keyboard.


The app that I am working on only has a base localization, which is English. In the scheme settings, region and language are set to system default.


If I run my app, the decimal separator used by the decimal pad keyboard is ".", which is the decimal separator used by the en-US keyboard, but not the de-DE keyboard. The normal alphabetic keyboard shows the German keyboard layout.


If I remove the en-US keyboard on the iOS device, the decimal separator changes to ",".


How can I reliably find out, which decimal separator is used by the decimal pad keyboard?


None of the solutions that I have tried so far work:

  • Using the preset `decimalSeparator` of `NumberFormatter` always gives ",".
  • Using `Locale.current.decimalSeparator` always returns "," as well.
  • Using `textField.textInputMode?.primaryLanguage` to figure out the locale always returns `de-DE`.
  • Using `Bundle.main.preferredLocalizations` to figure out the localization used by the app always returns `en`.


This is how the number formatter is configured:

let numberFormatter = NumberFormatter()
numberFormatter.minimumIntegerDigits = 1
numberFormatter.minimumFractionDigits = 0
numberFormatter.maximumFractionDigits = 2


It seems to be possible to determine the locale used by the decimal pad by finding matches between the active text input modes and app localizations:


let inputLocales = UITextInputMode.activeInputModes.compactMap {$0.primaryLanguage}.map(Locale.init(identifier:))
let localizations = Bundle.main.preferredLocalizations.map(Locale.init(identifier:))

let locale = inputLocales.flatMap { l in localizations.map {(l, $0)}}
  .filter { preferredLanguage, preferredLocalization in
    if preferredLocalization.regionCode == nil || preferredLanguage.regionCode == nil {
      return preferredLanguage.languageCode == preferredLocalization.languageCode
    } else {
      return preferredLanguage == preferredLocalization
    }
  }
  .first?.0
  ?? Locale.current
numberFormatter.locale = locale


However this solution has several disadvantages:


1. I do not know whether UIKit selects the decimal separator exactly this way. The behavior may be different for some languages

2. It has to be computed every time a number will be formatted, as the user may change keyboards while the app is running.

Replies

I'm having this same issue in one of my apps starting in iOS 12. Weirdly, the decimal separator of the keyboard does not match the decimal separator of the system locale. If you managed to fix this, please share your solution 😀.


Thanks.

I have the same issue.


"Locale.current.decimalSeparator" is not maatching the keybord's "." or "," there for it is not detectable!


Need hellp here + Is it a iOS12 bug?

I am having a similar issue. It depends on region, and the problem goes away if one changes region and back. At one point I could recreate on device and got a log in Xcode about keyboard 8 not being supported and using default. Is there a bug report.

Yes, I've reported my issue to Apple (rdar://42338031) and it has been closed as a duplicate in August. The original report is still marked as open though.

Hi


I'am using this workaround


Checking if the text contains the currentLocale decimal separator, or a "."


- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
   
    NSString * newValue = [textField.text stringByReplacingCharactersInRange:range withString:string];
   
    // Valid decimal cost input
    {
        NSString * decimalSeparator = [[NSLocale currentLocale] decimalSeparator];
        if (![newValue containsString:decimalSeparator] && [newValue containsString:@"."]) {
            // fix bug where the decimal separator doesn't corespond with locale
            // see https://forums.developer.apple.com/thread/107269
            decimalSeparator = @".";
        }
     ...

}

Hi,


same problem with one of my apps.

Things are a little bit more complex.

I have a few apps on the store, some are localized, a few are not. In general the localized apps have English, German and French, the none localiced apps are in English.

The affected app is a none localized app. Up to now I have not checked the other none localized apps for the bug.

Now things get weired...

Setting the system language to German as well as setting the region to German gives a dot "." instead of a comma "," as the decimal separator.

Result: Customers can not input floating numbers as the dot is not accepted by the string to float formatter command.

Number formatter is set.

NSNumberFormatter *NumFormatter = [[NSNumberFormatter alloc] init];
[NumFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[NumFormatter setMinimumFractionDigits:2];
[NumFormatter setMaximumFractionDigits:2];


float currprice = [[NumFormatter numberFromString: strcurrprice] floatValue];

If I set the system language to English and the region to German

Surprise, surprise...

Number pad shows a comma, what is correct!


The funny thing is that if I run the app in Xcode 9.2 with simulated iOS devices running iOS 11.2 it works correctly.

I don´t know if it makes sense to code a workaround just because there is a bug in the system. From my point of view it is not the responsibility of the app developer to fix system bugs by coding workarounds. Apple should fix the bug.


The following code might fix the problem for most of the cases.

stringinput is the input in the field converted to string

// Test decimal separator function

NSString *locDecSeparator = [[NSLocale currentLocale] decimalSeparator];
// Could be removed, just to check what locale decimal separator is set
NSLog(@"Locale Dec-SEparator: %@", locDecSeparator);

if (![stringinput containsString:locDecSeparator]) {
     NSLog(@"locdecsepa wrong!");
     stringinput = [stringinput stringByReplacingOccurrencesOfString:@"." withString:locDecSeparator];
}

It will not completely fix the problem but the cases where a dot "." is shown instead of the comma "," and the locale is set to use the comma.

The final fix for the problem might be this:


// Set up NumberFormatter
NSNumberFormatter *NumFormatter = [[NSNumberFormatter alloc] init];
[NumFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[NumFormatter setMinimumFractionDigits:2];
[NumFormatter setMaximumFractionDigits:2];

// Convert input to string
NSString *inputstr = [[NSString alloc] initWithFormat:@"%@", input.text];
// Get the current Locale decimal separator
NSString *locDecSeparator = [[NSLocale currentLocale] decimalSeparator];
// Check if inputstr does not contain the Locale decimal separator
    if (![inputstr containsString:locDecSeparator]) {
//  No LocaleDecimalSeparator in inputstr or wrong LocaleDecimalSeparator in inputstr
//  If there is a "." in inputstr replace by ","
        if ([inputstr containsString:@"."]) {
            inputstr = [inputstr stringByReplacingOccurrencesOfString:@"." withString:locDecSeparator];
        }
//  Else replace "," by "."
        if ([inputstr containsString:@","]) {
            inputstr = [inputstr stringByReplacingOccurrencesOfString:@"," withString:locDecSeparator];
        }
    }
// Convert inputstr with NumberFormatter to float
    float inputfloat = [[NumFormatter numberFromString: inputstr] floatValue];


It is still bad because the user sees the wrong decimal separator. The only good thing is that entering float values is possible, although with the wrong separator.