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 20:01:13 +0000 (20:01 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Apr 2016 20:01:13 +0000 (20:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156993

Reviewed by Geoffrey Garen.

Source/WebCore:

This is tested by the NavigatorLanguage API test.

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::isValidICUCountryCode):
(WebCore::platformUserPreferredLanguages):

Tools:

Change the test expectations for this test. If the variant is not explicitly specified in
the AppleLanguage, then we use the locale's country code, which this test assumes is US.

* TestWebKitAPI/Tests/mac/NavigatorLanguage.mm:
(TestWebKitAPI::languageForSystemLanguage):
(TestWebKitAPI::TEST):

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

Source/WebCore/ChangeLog
Source/WebCore/platform/mac/Language.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/mac/NavigatorLanguage.mm

index 64b45d9..6a7256d 100644 (file)
@@ -1,3 +1,27 @@
+2016-04-26  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.
+
+        This is tested by the NavigatorLanguage API test.
+        
+        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::isValidICUCountryCode):
+        (WebCore::platformUserPreferredLanguages):
+
 2016-04-26  Myles C. Maxfield  <mmaxfield@apple.com>
 
         Use auto-generated operators in FontPlatformData
index c1cc872..f751f21 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,12 +70,14 @@ static Vector<String>& preferredLanguages()
 
 namespace WebCore {
 
-static String httpStyleLanguageCode(NSString *language)
+static String httpStyleLanguageCode(NSString *language, NSString *country)
 {
     SInt32 languageCode;
     SInt32 regionCode; 
     SInt32 scriptCode; 
-    CFStringEncoding stringEncoding; 
+    CFStringEncoding stringEncoding;
+    
+    bool languageDidSpecifyExplicitVariant = [language rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"-_"]].location != NSNotFound;
 
     // FIXME: This transformation is very wrong:
     // 1. There is no reason why CFBundle localization names would be at all related to language names as used on the Web.
@@ -87,9 +90,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 and the language did not specify a variant, 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 && !languageDidSpecifyExplicitVariant)
+            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 +111,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 +138,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));
         }
     }
 
index 4c78d50..36e7734 100644 (file)
@@ -1,3 +1,17 @@
+2016-04-26  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.
+
+        Change the test expectations for this test. If the variant is not explicitly specified in
+        the AppleLanguage, then we use the locale's country code, which this test assumes is US.
+
+        * TestWebKitAPI/Tests/mac/NavigatorLanguage.mm:
+        (TestWebKitAPI::languageForSystemLanguage):
+        (TestWebKitAPI::TEST):
+
 2016-04-25  Ryosuke Niwa  <rniwa@webkit.org>
 
         Remove the build flag for template elements
index c4d756b..b97da0f 100644 (file)
@@ -68,17 +68,21 @@ static NSString *languageForSystemLanguage(WebView* webView, NSString *systemLan
     return [webView stringByEvaluatingJavaScriptFromString:@"navigator.language"];
 }
 
-// These tests document current behavior. Some of the current results may not be right.
+// These tests document current behavior. Some of the current results may not be right. Note that
+// this oddly assumes that the user has set their language to something possibly-foreign but still
+// left their region as US. Hence the "-us" variants.
+// FIXME: These tests should also set the region to see how WebKit will handle that.
+// https://bugs.webkit.org/show_bug.cgi?id=157039
 NSArray *tests = @[
-    @[@"ru", @"ru-ru"], // This does not match other browsers or CFNetwork's Accept-Language, which all use "ru".
+    @[@"ru", @"ru-us"], // This does not match other browsers or CFNetwork's Accept-Language, which all use "ru".
     @[@"en", @"en-us"],
     @[@"en-GB", @"en-gb"],
     @[@"en-US", @"en-us"],
-    @[@"ja", @"ja-jp"],
-    @[@"hi", @"hi-in"],
+    @[@"ja", @"ja-us"],
+    @[@"hi", @"hi-us"],
     @[@"zh-TW", @"zh-tw"], // This should not map to the generic zh-hant, see rdar://problem/21395180.
     @[@"zh-HK", @"zh-tw"],
-    @[@"es", @"es-es"],
+    @[@"es", @"es-us"],
     @[@"es-MX", @"es-xl"],
     @[@"es-ES", @"es-es"],
     @[@"es-419", @"es-xl"],
@@ -86,7 +90,7 @@ NSArray *tests = @[
     @[@"zh-Hant", @"zh-tw"],
     @[@"pt-BR", @"pt-br"],
     @[@"pt-PT", @"pt-pt"],
-    @[@"fr", @"fr-fr"],
+    @[@"fr", @"fr-us"],
     @[@"fr-CA", @"fr-ca"],
 ];