WebCore on Mac ignores the user's preferred region (country) while getting the language
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Apr 2016 16:22:32 +0000 (16:22 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Apr 2016 16:22:32 +0000 (16:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156993

Reviewed by Geoffrey Garen.

I don't know how to test this since this depends on user settings.

WebCore was previously getting the list of preferred languages, and for each one, deducing
the default region. That's wrong, since for example it doesn't respect the user's choice (in
System Preferences) to display dates/calenders/etc according to a different region (like how
I have my machine set to en-pl right now).

It might be possible for the country code we get via kCFLocaleCountryCode to be something
that our ICU doesn't handle. To defend against this, we search for the resulting country
code in ICU's ISO countries list. If it doesn't appear in that list, we fall back on old
behavior.

* platform/mac/Language.mm:
(WebCore::httpStyleLanguageCode):
(WebCore::platformUserPreferredLanguages):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@200089 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/mac/Language.mm

index 3f33dce..219b9a3 100644 (file)
@@ -1,3 +1,26 @@
+2016-04-25  Filip Pizlo  <fpizlo@apple.com>
+
+        WebCore on Mac ignores the user's preferred region (country) while getting the language
+        https://bugs.webkit.org/show_bug.cgi?id=156993
+
+        Reviewed by Geoffrey Garen.
+
+        I don't know how to test this since this depends on user settings.
+        
+        WebCore was previously getting the list of preferred languages, and for each one, deducing
+        the default region. That's wrong, since for example it doesn't respect the user's choice (in
+        System Preferences) to display dates/calenders/etc according to a different region (like how
+        I have my machine set to en-pl right now).
+        
+        It might be possible for the country code we get via kCFLocaleCountryCode to be something
+        that our ICU doesn't handle. To defend against this, we search for the resulting country
+        code in ICU's ISO countries list. If it doesn't appear in that list, we fall back on old
+        behavior.
+
+        * platform/mac/Language.mm:
+        (WebCore::httpStyleLanguageCode):
+        (WebCore::platformUserPreferredLanguages):
+
 2016-04-26  Chris Dumez  <cdumez@apple.com>
 
         [Web IDL] Specify default values for optional parameters of TypedArray types
index c1cc872..ee95c45 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003, 2005, 2006, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2005, 2006, 2010, 2011, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,6 +30,7 @@
 #import "CFBundleSPI.h"
 #import "WebCoreNSStringExtras.h"
 #import <mutex>
+#import <unicode/uloc.h>
 #import <wtf/Assertions.h>
 #import <wtf/Lock.h>
 #import <wtf/NeverDestroyed.h>
@@ -69,7 +70,7 @@ static Vector<String>& preferredLanguages()
 
 namespace WebCore {
 
-static String httpStyleLanguageCode(NSString *language)
+static String httpStyleLanguageCode(NSString *language, NSString *country)
 {
     SInt32 languageCode;
     SInt32 regionCode; 
@@ -87,9 +88,19 @@ static String httpStyleLanguageCode(NSString *language)
 
     // Make the string lowercase.
     NSString *lowercaseLanguageCode = [language lowercaseString];
-
-    // Turn a '_' into a '-' if it appears after a 2-letter language code.
+    NSString *lowercaseCountryCode = [country lowercaseString];
+    
+    // If we see a "_" after a 2-letter language code:
+    // If the country is valid, replace the "_" and whatever comes after it with "-" followed by the
+    // country code.
+    // Otherwise, replace the "_" with a "-" and use whatever country
+    // CFBundleCopyLocalizationForLocalizationInfo() returned.
     if ([lowercaseLanguageCode length] >= 3 && [lowercaseLanguageCode characterAtIndex:2] == '_') {
+        if (country)
+            return [NSString stringWithFormat:@"%@-%@", [lowercaseLanguageCode substringWithRange:NSMakeRange(0, 2)], lowercaseCountryCode];
+        
+        // Fall back to older behavior, which used the original language-based code but just changed
+        // the "_" to a "-".
         RetainPtr<NSMutableString> mutableLanguageCode = adoptNS([lowercaseLanguageCode mutableCopy]);
         [mutableLanguageCode.get() replaceCharactersInRange:NSMakeRange(2, 1) withString:@"-"];
         return mutableLanguageCode.get();
@@ -98,6 +109,18 @@ static String httpStyleLanguageCode(NSString *language)
     return lowercaseLanguageCode;
 }
 
+static bool isValidICUCountryCode(NSString* countryCode)
+{
+    const char* const* countries = uloc_getISOCountries();
+    const char* countryUTF8 = [countryCode UTF8String];
+    for (unsigned i = 0; countries[i]; ++i) {
+        const char* possibleCountry = countries[i];
+        if (!strcmp(countryUTF8, possibleCountry))
+            return true;
+    }
+    return false;
+}
+
 Vector<String> platformUserPreferredLanguages()
 {
 #if PLATFORM(MAC)
@@ -113,13 +136,19 @@ Vector<String> platformUserPreferredLanguages()
     Vector<String>& userPreferredLanguages = preferredLanguages();
 
     if (userPreferredLanguages.isEmpty()) {
+        RetainPtr<CFLocaleRef> locale = adoptCF(CFLocaleCopyCurrent());
+        NSString *countryCode = (NSString *)CFLocaleGetValue(locale.get(), kCFLocaleCountryCode);
+        
+        if (!isValidICUCountryCode(countryCode))
+            countryCode = nil;
+        
         RetainPtr<CFArrayRef> languages = adoptCF(CFLocaleCopyPreferredLanguages());
         CFIndex languageCount = CFArrayGetCount(languages.get());
         if (!languageCount)
             userPreferredLanguages.append("en");
         else {
             for (CFIndex i = 0; i < languageCount; i++)
-                userPreferredLanguages.append(httpStyleLanguageCode((NSString *)CFArrayGetValueAtIndex(languages.get(), i)));
+                userPreferredLanguages.append(httpStyleLanguageCode((NSString *)CFArrayGetValueAtIndex(languages.get(), i), countryCode));
         }
     }