1 /* 2 * Copyright (C) 2010, 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "core/platform/Language.h" 28 29 #include "wtf/HashMap.h" 30 #include "wtf/RetainPtr.h" 31 #include "wtf/text/WTFString.h" 32 33 namespace WebCore { 34 35 typedef HashMap<void*, LanguageChangeObserverFunction> ObserverMap; 36 static ObserverMap& observerMap() 37 { 38 DEFINE_STATIC_LOCAL(ObserverMap, map, ()); 39 return map; 40 } 41 42 void addLanguageChangeObserver(void* context, LanguageChangeObserverFunction customObserver) 43 { 44 observerMap().set(context, customObserver); 45 } 46 47 void removeLanguageChangeObserver(void* context) 48 { 49 ASSERT(observerMap().contains(context)); 50 observerMap().remove(context); 51 } 52 53 void languageDidChange() 54 { 55 ObserverMap::iterator end = observerMap().end(); 56 for (ObserverMap::iterator iter = observerMap().begin(); iter != end; ++iter) 57 iter->value(iter->key); 58 } 59 60 String defaultLanguage() 61 { 62 Vector<String> languages = userPreferredLanguages(); 63 if (languages.size()) 64 return languages[0]; 65 66 return emptyString(); 67 } 68 69 static Vector<String>& preferredLanguagesOverride() 70 { 71 DEFINE_STATIC_LOCAL(Vector<String>, override, ()); 72 return override; 73 } 74 75 Vector<String> userPreferredLanguagesOverride() 76 { 77 return preferredLanguagesOverride(); 78 } 79 80 void overrideUserPreferredLanguages(const Vector<String>& override) 81 { 82 preferredLanguagesOverride() = override; 83 } 84 85 Vector<String> userPreferredLanguages() 86 { 87 Vector<String>& override = preferredLanguagesOverride(); 88 if (!override.isEmpty()) 89 return override; 90 91 return platformUserPreferredLanguages(); 92 } 93 94 static String canonicalLanguageIdentifier(const String& languageCode) 95 { 96 String lowercaseLanguageCode = languageCode.lower(); 97 98 if (lowercaseLanguageCode.length() >= 3 && lowercaseLanguageCode[2] == '_') 99 lowercaseLanguageCode.replace(2, 1, "-"); 100 101 return lowercaseLanguageCode; 102 } 103 104 size_t indexOfBestMatchingLanguageInList(const String& language, const Vector<String>& languageList) 105 { 106 String languageWithoutLocaleMatch; 107 String languageMatchButNotLocale; 108 size_t languageWithoutLocaleMatchIndex = 0; 109 size_t languageMatchButNotLocaleMatchIndex = 0; 110 bool canMatchLanguageOnly = (language.length() == 2 || (language.length() >= 3 && language[2] == '-')); 111 112 for (size_t i = 0; i < languageList.size(); ++i) { 113 String canonicalizedLanguageFromList = canonicalLanguageIdentifier(languageList[i]); 114 115 if (language == canonicalizedLanguageFromList) 116 return i; 117 118 if (canMatchLanguageOnly && canonicalizedLanguageFromList.length() >= 2) { 119 if (language[0] == canonicalizedLanguageFromList[0] && language[1] == canonicalizedLanguageFromList[1]) { 120 if (!languageWithoutLocaleMatch.length() && canonicalizedLanguageFromList.length() == 2) { 121 languageWithoutLocaleMatch = languageList[i]; 122 languageWithoutLocaleMatchIndex = i; 123 } 124 if (!languageMatchButNotLocale.length() && canonicalizedLanguageFromList.length() >= 3) { 125 languageMatchButNotLocale = languageList[i]; 126 languageMatchButNotLocaleMatchIndex = i; 127 } 128 } 129 } 130 } 131 132 // If we have both a language-only match and a languge-but-not-locale match, return the 133 // languge-only match as is considered a "better" match. For example, if the list 134 // provided has both "en-GB" and "en" and the user prefers "en-US" we will return "en". 135 if (languageWithoutLocaleMatch.length()) 136 return languageWithoutLocaleMatchIndex; 137 138 if (languageMatchButNotLocale.length()) 139 return languageMatchButNotLocaleMatchIndex; 140 141 return languageList.size(); 142 } 143 144 } 145