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 "platform/Language.h" 28 29#include "public/platform/Platform.h" 30#include "wtf/text/WTFString.h" 31 32namespace blink { 33 34static const AtomicString& platformLanguage() 35{ 36 DEFINE_STATIC_LOCAL(AtomicString, computedDefaultLanguage, ()); 37 if (computedDefaultLanguage.isEmpty()) { 38 computedDefaultLanguage = blink::Platform::current()->defaultLocale(); 39 ASSERT(!computedDefaultLanguage.isEmpty()); 40 } 41 return computedDefaultLanguage; 42} 43 44AtomicString defaultLanguage() 45{ 46 Vector<AtomicString> languages = userPreferredLanguages(); 47 if (!languages.isEmpty()) 48 return languages[0]; 49 50 return emptyAtom; 51} 52 53static Vector<AtomicString>& preferredLanguagesOverride() 54{ 55 DEFINE_STATIC_LOCAL(Vector<AtomicString>, override, ()); 56 return override; 57} 58 59Vector<AtomicString> userPreferredLanguagesOverride() 60{ 61 return preferredLanguagesOverride(); 62} 63 64void overrideUserPreferredLanguages(const Vector<AtomicString>& override) 65{ 66 preferredLanguagesOverride() = override; 67} 68 69Vector<AtomicString> userPreferredLanguages() 70{ 71 Vector<AtomicString>& override = preferredLanguagesOverride(); 72 if (!override.isEmpty()) 73 return override; 74 75 Vector<AtomicString> languages; 76 languages.reserveInitialCapacity(1); 77 languages.append(platformLanguage()); 78 return languages; 79} 80 81static String canonicalLanguageIdentifier(const String& languageCode) 82{ 83 String lowercaseLanguageCode = languageCode.lower(); 84 85 if (lowercaseLanguageCode.length() >= 3 && lowercaseLanguageCode[2] == '_') 86 lowercaseLanguageCode.replace(2, 1, "-"); 87 88 return lowercaseLanguageCode; 89} 90 91size_t indexOfBestMatchingLanguageInList(const AtomicString& language, const Vector<AtomicString>& languageList) 92{ 93 AtomicString languageWithoutLocaleMatch; 94 AtomicString languageMatchButNotLocale; 95 size_t languageWithoutLocaleMatchIndex = 0; 96 size_t languageMatchButNotLocaleMatchIndex = 0; 97 bool canMatchLanguageOnly = (language.length() == 2 || (language.length() >= 3 && language[2] == '-')); 98 99 for (size_t i = 0; i < languageList.size(); ++i) { 100 String canonicalizedLanguageFromList = canonicalLanguageIdentifier(languageList[i]); 101 102 if (language == canonicalizedLanguageFromList) 103 return i; 104 105 if (canMatchLanguageOnly && canonicalizedLanguageFromList.length() >= 2) { 106 if (language[0] == canonicalizedLanguageFromList[0] && language[1] == canonicalizedLanguageFromList[1]) { 107 if (!languageWithoutLocaleMatch.length() && canonicalizedLanguageFromList.length() == 2) { 108 languageWithoutLocaleMatch = languageList[i]; 109 languageWithoutLocaleMatchIndex = i; 110 } 111 if (!languageMatchButNotLocale.length() && canonicalizedLanguageFromList.length() >= 3) { 112 languageMatchButNotLocale = languageList[i]; 113 languageMatchButNotLocaleMatchIndex = i; 114 } 115 } 116 } 117 } 118 119 // If we have both a language-only match and a languge-but-not-locale match, return the 120 // languge-only match as is considered a "better" match. For example, if the list 121 // provided has both "en-GB" and "en" and the user prefers "en-US" we will return "en". 122 if (languageWithoutLocaleMatch.length()) 123 return languageWithoutLocaleMatchIndex; 124 125 if (languageMatchButNotLocale.length()) 126 return languageMatchButNotLocaleMatchIndex; 127 128 return languageList.size(); 129} 130 131} 132