Home | History | Annotate | Download | only in experimental
      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