1 // Copyright 2010 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "i18n-extension.h" 29 30 #include <algorithm> 31 #include <string> 32 33 #include "break-iterator.h" 34 #include "natives.h" 35 #include "unicode/locid.h" 36 #include "unicode/uloc.h" 37 38 namespace v8 { 39 namespace internal { 40 41 I18NExtension* I18NExtension::extension_ = NULL; 42 43 // Returns a pointer to static string containing the actual 44 // JavaScript code generated from i18n.js file. 45 static const char* GetScriptSource() { 46 int index = NativesCollection<I18N>::GetIndex("i18n"); 47 Vector<const char> script_data = 48 NativesCollection<I18N>::GetScriptSource(index); 49 50 return script_data.start(); 51 } 52 53 I18NExtension::I18NExtension() 54 : v8::Extension("v8/i18n", GetScriptSource()) { 55 } 56 57 v8::Handle<v8::FunctionTemplate> I18NExtension::GetNativeFunction( 58 v8::Handle<v8::String> name) { 59 if (name->Equals(v8::String::New("NativeJSLocale"))) { 60 return v8::FunctionTemplate::New(JSLocale); 61 } else if (name->Equals(v8::String::New("NativeJSAvailableLocales"))) { 62 return v8::FunctionTemplate::New(JSAvailableLocales); 63 } else if (name->Equals(v8::String::New("NativeJSMaximizedLocale"))) { 64 return v8::FunctionTemplate::New(JSMaximizedLocale); 65 } else if (name->Equals(v8::String::New("NativeJSMinimizedLocale"))) { 66 return v8::FunctionTemplate::New(JSMinimizedLocale); 67 } else if (name->Equals(v8::String::New("NativeJSDisplayLanguage"))) { 68 return v8::FunctionTemplate::New(JSDisplayLanguage); 69 } else if (name->Equals(v8::String::New("NativeJSDisplayScript"))) { 70 return v8::FunctionTemplate::New(JSDisplayScript); 71 } else if (name->Equals(v8::String::New("NativeJSDisplayRegion"))) { 72 return v8::FunctionTemplate::New(JSDisplayRegion); 73 } else if (name->Equals(v8::String::New("NativeJSDisplayName"))) { 74 return v8::FunctionTemplate::New(JSDisplayName); 75 } else if (name->Equals(v8::String::New("NativeJSBreakIterator"))) { 76 return v8::FunctionTemplate::New(BreakIterator::JSBreakIterator); 77 } 78 79 return v8::Handle<v8::FunctionTemplate>(); 80 } 81 82 v8::Handle<v8::Value> I18NExtension::JSLocale(const v8::Arguments& args) { 83 // TODO(cira): Fetch browser locale. Accept en-US as good default for now. 84 // We could possibly pass browser locale as a parameter in the constructor. 85 std::string locale_name("en-US"); 86 if (args.Length() == 1 && args[0]->IsString()) { 87 locale_name = *v8::String::Utf8Value(args[0]->ToString()); 88 } 89 90 v8::Local<v8::Object> locale = v8::Object::New(); 91 locale->Set(v8::String::New("locale"), v8::String::New(locale_name.c_str())); 92 93 icu::Locale icu_locale(locale_name.c_str()); 94 95 const char* language = icu_locale.getLanguage(); 96 locale->Set(v8::String::New("language"), v8::String::New(language)); 97 98 const char* script = icu_locale.getScript(); 99 if (strlen(script)) { 100 locale->Set(v8::String::New("script"), v8::String::New(script)); 101 } 102 103 const char* region = icu_locale.getCountry(); 104 if (strlen(region)) { 105 locale->Set(v8::String::New("region"), v8::String::New(region)); 106 } 107 108 return locale; 109 } 110 111 // TODO(cira): Filter out locales that Chrome doesn't support. 112 v8::Handle<v8::Value> I18NExtension::JSAvailableLocales( 113 const v8::Arguments& args) { 114 v8::Local<v8::Array> all_locales = v8::Array::New(); 115 116 int count = 0; 117 const icu::Locale* icu_locales = icu::Locale::getAvailableLocales(count); 118 for (int i = 0; i < count; ++i) { 119 all_locales->Set(i, v8::String::New(icu_locales[i].getName())); 120 } 121 122 return all_locales; 123 } 124 125 // Use - as tag separator, not _ that ICU uses. 126 static std::string NormalizeLocale(const std::string& locale) { 127 std::string result(locale); 128 // TODO(cira): remove STL dependency. 129 std::replace(result.begin(), result.end(), '_', '-'); 130 return result; 131 } 132 133 v8::Handle<v8::Value> I18NExtension::JSMaximizedLocale( 134 const v8::Arguments& args) { 135 if (!args.Length() || !args[0]->IsString()) { 136 return v8::Undefined(); 137 } 138 139 UErrorCode status = U_ZERO_ERROR; 140 std::string locale_name = *v8::String::Utf8Value(args[0]->ToString()); 141 char max_locale[ULOC_FULLNAME_CAPACITY]; 142 uloc_addLikelySubtags(locale_name.c_str(), max_locale, 143 sizeof(max_locale), &status); 144 if (U_FAILURE(status)) { 145 return v8::Undefined(); 146 } 147 148 return v8::String::New(NormalizeLocale(max_locale).c_str()); 149 } 150 151 v8::Handle<v8::Value> I18NExtension::JSMinimizedLocale( 152 const v8::Arguments& args) { 153 if (!args.Length() || !args[0]->IsString()) { 154 return v8::Undefined(); 155 } 156 157 UErrorCode status = U_ZERO_ERROR; 158 std::string locale_name = *v8::String::Utf8Value(args[0]->ToString()); 159 char min_locale[ULOC_FULLNAME_CAPACITY]; 160 uloc_minimizeSubtags(locale_name.c_str(), min_locale, 161 sizeof(min_locale), &status); 162 if (U_FAILURE(status)) { 163 return v8::Undefined(); 164 } 165 166 return v8::String::New(NormalizeLocale(min_locale).c_str()); 167 } 168 169 // Common code for JSDisplayXXX methods. 170 static v8::Handle<v8::Value> GetDisplayItem(const v8::Arguments& args, 171 const std::string& item) { 172 if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) { 173 return v8::Undefined(); 174 } 175 176 std::string base_locale = *v8::String::Utf8Value(args[0]->ToString()); 177 icu::Locale icu_locale(base_locale.c_str()); 178 icu::Locale display_locale = 179 icu::Locale(*v8::String::Utf8Value(args[1]->ToString())); 180 icu::UnicodeString result; 181 if (item == "language") { 182 icu_locale.getDisplayLanguage(display_locale, result); 183 } else if (item == "script") { 184 icu_locale.getDisplayScript(display_locale, result); 185 } else if (item == "region") { 186 icu_locale.getDisplayCountry(display_locale, result); 187 } else if (item == "name") { 188 icu_locale.getDisplayName(display_locale, result); 189 } else { 190 return v8::Undefined(); 191 } 192 193 if (result.length()) { 194 return v8::String::New( 195 reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()); 196 } 197 198 return v8::Undefined(); 199 } 200 201 v8::Handle<v8::Value> I18NExtension::JSDisplayLanguage( 202 const v8::Arguments& args) { 203 return GetDisplayItem(args, "language"); 204 } 205 206 v8::Handle<v8::Value> I18NExtension::JSDisplayScript( 207 const v8::Arguments& args) { 208 return GetDisplayItem(args, "script"); 209 } 210 211 v8::Handle<v8::Value> I18NExtension::JSDisplayRegion( 212 const v8::Arguments& args) { 213 return GetDisplayItem(args, "region"); 214 } 215 216 v8::Handle<v8::Value> I18NExtension::JSDisplayName(const v8::Arguments& args) { 217 return GetDisplayItem(args, "name"); 218 } 219 220 I18NExtension* I18NExtension::get() { 221 if (!extension_) { 222 extension_ = new I18NExtension(); 223 } 224 return extension_; 225 } 226 227 void I18NExtension::Register() { 228 static v8::DeclareExtension i18n_extension_declaration(I18NExtension::get()); 229 } 230 231 } } // namespace v8::internal 232