Back to index

lightning-sunbird  0.9+nobinonly
nsLocaleService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsCOMPtr.h"
00039 #include "nsILocale.h"
00040 #include "nsILocaleService.h"
00041 #include "nsLocale.h"
00042 #include "nsLocaleCID.h"
00043 #include "nsServiceManagerUtils.h"
00044 #include "nsReadableUtils.h"
00045 #include "nsCRT.h"
00046 #include "prprf.h"
00047 #include "nsAutoBuffer.h"
00048 
00049 #include <ctype.h>
00050 
00051 #if defined(XP_WIN)
00052 #  include "nsIWin32Locale.h"
00053 #elif defined(XP_OS2)
00054 #  include "unidef.h"
00055 #  include "nsIOS2Locale.h"
00056 #elif defined(XP_MAC) || defined(XP_MACOSX)
00057 #  include <Script.h>
00058 #  include "nsIMacLocale.h"
00059 #elif defined(XP_UNIX) || defined(XP_BEOS)
00060 #  include <locale.h>
00061 #  include <stdlib.h>
00062 #  include "nsIPosixLocale.h"
00063 #endif
00064 
00065 //
00066 // implementation constants
00067 const int LocaleListLength = 6;
00068 const char* LocaleList[LocaleListLength] = 
00069 {
00070        NSILOCALE_COLLATE,
00071        NSILOCALE_CTYPE,
00072        NSILOCALE_MONETARY,
00073        NSILOCALE_NUMERIC,
00074        NSILOCALE_TIME,
00075        NSILOCALE_MESSAGE
00076 };
00077 
00078 #define NSILOCALE_MAX_ACCEPT_LANGUAGE     16
00079 #define NSILOCALE_MAX_ACCEPT_LENGTH              18
00080 
00081 #if (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS) || defined(XP_OS2)
00082 static int posix_locale_category[LocaleListLength] =
00083 {
00084   LC_COLLATE,
00085   LC_CTYPE,
00086   LC_MONETARY,
00087   LC_NUMERIC,
00088   LC_TIME,
00089 #ifdef HAVE_I18N_LC_MESSAGES
00090   LC_MESSAGES
00091 #else
00092   LC_CTYPE
00093 #endif
00094 };
00095 #endif
00096 
00097 #ifdef XP_MACOSX
00098 #if !defined(__COREFOUNDATION_CFLOCALE__)
00099 typedef void* CFLocaleRef;
00100 #endif
00101 typedef CFLocaleRef (*fpCFLocaleCopyCurrent_type) (void);
00102 typedef CFStringRef (*fpCFLocaleGetIdentifier_type) (CFLocaleRef);
00103 #endif
00104 
00105 //
00106 // nsILocaleService implementation
00107 //
00108 class nsLocaleService: public nsILocaleService {
00109 
00110 public:
00111        
00112        //
00113        // nsISupports
00114        //
00115        NS_DECL_ISUPPORTS
00116 
00117        //
00118        // nsILocaleService
00119        //
00120     NS_DECL_NSILOCALESERVICE
00121 
00122 
00123        nsLocaleService(void);
00124        virtual ~nsLocaleService(void);
00125 
00126 protected:
00127 
00128        nsresult SetSystemLocale(void);
00129        nsresult SetApplicationLocale(void);
00130 
00131        nsCOMPtr<nsILocale>                       mSystemLocale;
00132        nsCOMPtr<nsILocale>                       mApplicationLocale;
00133 
00134 };
00135 
00136 //
00137 // nsILocaleDefinition implementation
00138 //
00139 class nsLocaleDefinition: public nsILocaleDefinition {
00140        friend class nsLocaleService;
00141 
00142 public:
00143 
00144        //
00145        // nsISupports
00146        //
00147        NS_DECL_ISUPPORTS
00148 
00149        //
00150        // nsILocaleDefintion
00151        //
00152        NS_IMETHOD SetLocaleCategory(const nsAString &category, const nsAString &value);
00153 
00154 protected:
00155 
00156        nsLocaleDefinition();
00157        virtual ~nsLocaleDefinition();
00158 
00159        nsLocale*     mLocaleDefinition;
00160 };
00161 
00162 
00163 
00164 
00165 //
00166 // nsLocaleService methods
00167 //
00168 nsLocaleService::nsLocaleService(void) 
00169     : mSystemLocale(0), mApplicationLocale(0)
00170 {
00171 #if defined(XP_WIN)
00172     nsCOMPtr<nsIWin32Locale> win32Converter = do_GetService(NS_WIN32LOCALE_CONTRACTID);
00173 
00174     NS_ASSERTION(win32Converter, "nsLocaleService: can't get win32 converter\n");
00175 
00176     nsAutoString        xpLocale;
00177     if (win32Converter) {
00178         
00179         nsresult result;
00180         //
00181         // get the system LCID
00182         //
00183         LCID win_lcid = GetSystemDefaultLCID();
00184         if (win_lcid==0) { return;}
00185             result = win32Converter->GetXPLocale(win_lcid, xpLocale);
00186         if (NS_FAILED(result)) { return;}
00187             result = NewLocale(xpLocale, getter_AddRefs(mSystemLocale));
00188         if (NS_FAILED(result)) { return;}
00189 
00190         //
00191         // get the application LCID
00192         //
00193         win_lcid = GetUserDefaultLCID();
00194         if (win_lcid==0) { return;}
00195             result = win32Converter->GetXPLocale(win_lcid, xpLocale);
00196         if (NS_FAILED(result)) { return;}
00197             result = NewLocale(xpLocale, getter_AddRefs(mApplicationLocale));
00198         if (NS_FAILED(result)) { return;}
00199     }
00200 #endif
00201 #if (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS)
00202     nsCOMPtr<nsIPosixLocale> posixConverter = do_GetService(NS_POSIXLOCALE_CONTRACTID);
00203 
00204     nsAutoString xpLocale, platformLocale;
00205     if (posixConverter) {
00206         nsAutoString category, category_platform;
00207         nsLocale* resultLocale;
00208         int i;
00209 
00210         resultLocale = new nsLocale();
00211         if ( resultLocale == NULL ) { 
00212             return; 
00213         }
00214         for( i = 0; i < LocaleListLength; i++ ) {
00215             nsresult result;
00216             char* lc_temp = setlocale(posix_locale_category[i], "");
00217             CopyASCIItoUTF16(LocaleList[i], category);
00218             category_platform = category; 
00219             category_platform.AppendLiteral("##PLATFORM");
00220             if (lc_temp != nsnull) {
00221                 result = posixConverter->GetXPLocale(lc_temp, xpLocale);
00222                 CopyASCIItoUTF16(lc_temp, platformLocale);
00223             } else {
00224                 char* lang = getenv("LANG");
00225                 if ( lang == nsnull ) {
00226                     platformLocale.AssignLiteral("en_US");
00227                     result = posixConverter->GetXPLocale("en-US", xpLocale);
00228                 }
00229                 else {
00230                     CopyASCIItoUTF16(lang, platformLocale);
00231                     result = posixConverter->GetXPLocale(lang, xpLocale); 
00232                 }
00233             }
00234             if (NS_FAILED(result)) {
00235                 return;
00236             }
00237             resultLocale->AddCategory(category, xpLocale);
00238             resultLocale->AddCategory(category_platform, platformLocale);
00239         }
00240         mSystemLocale = do_QueryInterface(resultLocale);
00241         mApplicationLocale = do_QueryInterface(resultLocale);
00242     }  // if ( NS_SUCCEEDED )...
00243        
00244 #endif // XP_UNIX || XP_BEOS
00245 #if defined(XP_OS2)
00246     nsCOMPtr<nsIOS2Locale> os2Converter = do_GetService(NS_OS2LOCALE_CONTRACTID);
00247     nsAutoString xpLocale;
00248     if (os2Converter) {
00249         nsAutoString category;
00250         nsLocale* resultLocale;
00251         int i;
00252 
00253         resultLocale = new nsLocale();
00254         if ( resultLocale == NULL ) { 
00255             return; 
00256         }
00257 
00258         LocaleObject locale_object = NULL;
00259         int result = UniCreateLocaleObject(UNI_UCS_STRING_POINTER,
00260                                            (UniChar *)L"", &locale_object);
00261         if (result != ULS_SUCCESS) {
00262             int result = UniCreateLocaleObject(UNI_UCS_STRING_POINTER,
00263                                                (UniChar *)L"en_US", &locale_object);
00264         }
00265         char* lc_temp;
00266         for( i = 0; i < LocaleListLength; i++ ) {
00267             lc_temp = nsnull;
00268             UniQueryLocaleObject(locale_object,
00269                                  posix_locale_category[i],
00270                                  UNI_MBS_STRING_POINTER,
00271                                  (void **)&lc_temp);
00272             category.AssignWithConversion(LocaleList[i]);
00273             nsresult result;
00274             if (lc_temp != nsnull)
00275                 result = os2Converter->GetXPLocale(lc_temp, xpLocale);
00276             else {
00277                 char* lang = getenv("LANG");
00278                 if ( lang == nsnull ) {
00279                     result = os2Converter->GetXPLocale("en-US", xpLocale);
00280                 }
00281                 else
00282                     result = os2Converter->GetXPLocale(lang, xpLocale); 
00283             }
00284             if (NS_FAILED(result)) {
00285                 UniFreeMem(lc_temp);
00286                 UniFreeLocaleObject(locale_object);
00287                 return;
00288             }
00289             resultLocale->AddCategory(category, xpLocale);
00290             UniFreeMem(lc_temp);
00291         }
00292         UniFreeLocaleObject(locale_object);
00293         mSystemLocale = do_QueryInterface(resultLocale);
00294         mApplicationLocale = do_QueryInterface(resultLocale);
00295     }  // if ( NS_SUCCEEDED )...
00296 
00297 #endif
00298 
00299 #if defined(XP_MAC) || defined(XP_MACOSX)
00300     // On MacOSX, the recommended way to get the user's current locale is to use
00301     // the CFLocale APIs.  However, these are only available on 10.3 and later.
00302     // So for the older systems, we have to keep using the Script Manager APIs.
00303 
00304     static PRBool checked = PR_FALSE;
00305     static fpCFLocaleCopyCurrent_type fpCFLocaleCopyCurrent = NULL;
00306     static fpCFLocaleGetIdentifier_type fpCFLocaleGetIdentifier = NULL;
00307 
00308     if (!checked)
00309     {
00310         CFBundleRef bundle =
00311             ::CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon"));
00312         if (bundle)
00313         {
00314             // We dynamically load these two functions and only use them if
00315             // they are available (OS 10.3+).
00316             fpCFLocaleCopyCurrent = (fpCFLocaleCopyCurrent_type)
00317                 ::CFBundleGetFunctionPointerForName(bundle,
00318                                                     CFSTR("CFLocaleCopyCurrent"));
00319             fpCFLocaleGetIdentifier = (fpCFLocaleGetIdentifier_type)
00320                 ::CFBundleGetFunctionPointerForName(bundle,
00321                                                     CFSTR("CFLocaleGetIdentifier"));
00322         }
00323         checked = PR_TRUE;
00324     }
00325 
00326     if (fpCFLocaleCopyCurrent)
00327     {
00328         // Get string representation of user's current locale
00329         CFLocaleRef userLocaleRef = fpCFLocaleCopyCurrent();
00330         CFStringRef userLocaleStr = fpCFLocaleGetIdentifier(userLocaleRef);
00331         ::CFRetain(userLocaleStr);
00332 
00333         nsAutoBuffer<UniChar, 32> buffer;
00334         int size = ::CFStringGetLength(userLocaleStr);
00335         if (buffer.EnsureElemCapacity(size))
00336         {
00337             CFRange range = ::CFRangeMake(0, size);
00338             ::CFStringGetCharacters(userLocaleStr, range, buffer.get());
00339             buffer.get()[size] = 0;
00340 
00341             // Convert the locale string to the format that Mozilla expects
00342             nsAutoString xpLocale(buffer.get());
00343             xpLocale.ReplaceChar('_', '-');
00344 
00345             nsresult rv = NewLocale(xpLocale, getter_AddRefs(mSystemLocale));
00346             if (NS_SUCCEEDED(rv)) {
00347               mApplicationLocale = mSystemLocale;
00348             }
00349         }
00350 
00351         ::CFRelease(userLocaleStr);
00352         ::CFRelease(userLocaleRef);
00353 
00354         NS_ASSERTION(mApplicationLocale, "Failed to create locale objects");
00355     }
00356     else
00357     {
00358         // Legacy MacOSX locale code
00359         long script = GetScriptManagerVariable(smSysScript);
00360         long lang = GetScriptVariable(smSystemScript,smScriptLang);
00361         long region = GetScriptManagerVariable(smRegionCode);
00362         nsCOMPtr<nsIMacLocale> macConverter = do_GetService(NS_MACLOCALE_CONTRACTID);
00363         if (macConverter) {
00364             nsresult result;
00365             nsAutoString xpLocale;
00366             result = macConverter->GetXPLocale((short)script,(short)lang,(short)region, xpLocale);
00367             if (NS_SUCCEEDED(result)) {
00368                 result = NewLocale(xpLocale, getter_AddRefs(mSystemLocale));
00369                 if (NS_SUCCEEDED(result)) {
00370                     mApplicationLocale = mSystemLocale;
00371                 }
00372             }
00373         }
00374     }
00375 #endif
00376 }
00377 
00378 nsLocaleService::~nsLocaleService(void)
00379 {
00380 }
00381 
00382 NS_IMPL_THREADSAFE_ISUPPORTS1(nsLocaleService, nsILocaleService)
00383 
00384 NS_IMETHODIMP
00385 nsLocaleService::NewLocale(const nsAString &aLocale, nsILocale **_retval)
00386 {
00387        int           i;
00388        nsresult result;
00389 
00390        *_retval = (nsILocale*)nsnull;
00391        
00392        nsLocale* resultLocale = new nsLocale();
00393        if (!resultLocale) return NS_ERROR_OUT_OF_MEMORY;
00394 
00395        for(i=0;i<LocaleListLength;i++) {
00396               nsString category; category.AssignWithConversion(LocaleList[i]);
00397               result = resultLocale->AddCategory(category, aLocale);
00398               if (NS_FAILED(result)) { delete resultLocale; return result;}
00399        }
00400 
00401        return resultLocale->QueryInterface(NS_GET_IID(nsILocale),(void**)_retval);
00402 }
00403 
00404 
00405 NS_IMETHODIMP
00406 nsLocaleService::NewLocaleObject(nsILocaleDefinition *localeDefinition, nsILocale **_retval)
00407 {
00408        if (!localeDefinition || !_retval) return NS_ERROR_INVALID_ARG;
00409 
00410        nsLocale* new_locale = new nsLocale(NS_STATIC_CAST(nsLocaleDefinition*,localeDefinition)->mLocaleDefinition);
00411        if (!new_locale) return NS_ERROR_OUT_OF_MEMORY;
00412 
00413        return new_locale->QueryInterface(NS_GET_IID(nsILocale),(void**)_retval);
00414 }
00415 
00416 
00417 NS_IMETHODIMP
00418 nsLocaleService::GetSystemLocale(nsILocale **_retval)
00419 {
00420        if (mSystemLocale) {
00421               NS_ADDREF(*_retval = mSystemLocale);
00422               return NS_OK;
00423        }
00424 
00425        *_retval = (nsILocale*)nsnull;
00426        return NS_ERROR_FAILURE;
00427 }
00428 
00429 NS_IMETHODIMP
00430 nsLocaleService::GetApplicationLocale(nsILocale **_retval)
00431 {
00432        if (mApplicationLocale) {
00433               NS_ADDREF(*_retval = mApplicationLocale);
00434               return NS_OK;
00435        }
00436 
00437        *_retval=(nsILocale*)nsnull;
00438        return NS_ERROR_FAILURE;
00439 }
00440 
00441 NS_IMETHODIMP
00442 nsLocaleService::GetLocaleFromAcceptLanguage(const char *acceptLanguage, nsILocale **_retval)
00443 {
00444   char* input;
00445   char* cPtr;
00446   char* cPtr1;
00447   char* cPtr2;
00448   int i;
00449   int j;
00450   int countLang = 0;
00451   char acceptLanguageList[NSILOCALE_MAX_ACCEPT_LANGUAGE][NSILOCALE_MAX_ACCEPT_LENGTH];
00452   nsresult    result;
00453 
00454   input = new char[strlen(acceptLanguage)+1];
00455   NS_ASSERTION(input!=nsnull,"nsLocaleFactory::GetLocaleFromAcceptLanguage: memory allocation failed.");
00456   if (input == (char*)NULL){ return NS_ERROR_OUT_OF_MEMORY; }
00457 
00458   strcpy(input, acceptLanguage);
00459   cPtr1 = input-1;
00460   cPtr2 = input;
00461 
00462   /* put in standard form */
00463   while (*(++cPtr1)) {
00464     if      (isalpha(*cPtr1))  *cPtr2++ = tolower(*cPtr1); /* force lower case */
00465     else if (isspace(*cPtr1))  ;                           /* ignore any space */
00466     else if (*cPtr1=='-')      *cPtr2++ = '_';             /* "-" -> "_"       */
00467     else if (*cPtr1=='*')      ;                           /* ignore "*"       */
00468     else                       *cPtr2++ = *cPtr1;          /* else unchanged   */
00469   }
00470   *cPtr2 = '\0';
00471 
00472   countLang = 0;
00473 
00474   if (strchr(input,';')) {
00475     /* deal with the quality values */
00476 
00477     float qvalue[NSILOCALE_MAX_ACCEPT_LANGUAGE];
00478     float qSwap;
00479     float bias = 0.0f;
00480     char* ptrLanguage[NSILOCALE_MAX_ACCEPT_LANGUAGE];
00481     char* ptrSwap;
00482 
00483     cPtr = nsCRT::strtok(input,",",&cPtr2);
00484     while (cPtr) {
00485       qvalue[countLang] = 1.0f;
00486       /* add extra parens to get rid of warning */
00487       if ((cPtr1 = strchr(cPtr,';')) != nsnull) {
00488         PR_sscanf(cPtr1,";q=%f",&qvalue[countLang]);
00489         *cPtr1 = '\0';
00490       }
00491       if (strlen(cPtr)<NSILOCALE_MAX_ACCEPT_LANGUAGE) {     /* ignore if too long */
00492         qvalue[countLang] -= (bias += 0.0001f); /* to insure original order */
00493         ptrLanguage[countLang++] = cPtr;
00494         if (countLang>=NSILOCALE_MAX_ACCEPT_LANGUAGE) break; /* quit if too many */
00495       }
00496       cPtr = nsCRT::strtok(cPtr2,",",&cPtr2);
00497     }
00498 
00499     /* sort according to decending qvalue */
00500     /* not a very good algorithm, but count is not likely large */
00501     for ( i=0 ; i<countLang-1 ; i++ ) {
00502       for ( j=i+1 ; j<countLang ; j++ ) {
00503         if (qvalue[i]<qvalue[j]) {
00504           qSwap     = qvalue[i];
00505           qvalue[i] = qvalue[j];
00506           qvalue[j] = qSwap;
00507           ptrSwap        = ptrLanguage[i];
00508           ptrLanguage[i] = ptrLanguage[j];
00509           ptrLanguage[j] = ptrSwap;
00510         }
00511       }
00512     }
00513     for ( i=0 ; i<countLang ; i++ ) {
00514       PL_strncpyz(acceptLanguageList[i],ptrLanguage[i],NSILOCALE_MAX_ACCEPT_LENGTH);
00515     }
00516 
00517   } else {
00518     /* simple case: no quality values */
00519 
00520     cPtr = nsCRT::strtok(input,",",&cPtr2);
00521     while (cPtr) {
00522       if (strlen(cPtr)<NSILOCALE_MAX_ACCEPT_LENGTH) {        /* ignore if too long */
00523         PL_strncpyz(acceptLanguageList[countLang++],cPtr,NSILOCALE_MAX_ACCEPT_LENGTH);
00524         if (countLang>=NSILOCALE_MAX_ACCEPT_LENGTH) break; /* quit if too many */
00525       }
00526       cPtr = nsCRT::strtok(cPtr2,",",&cPtr2);
00527     }
00528   }
00529 
00530   //
00531   // now create the locale 
00532   //
00533   result = NS_ERROR_FAILURE;
00534   if (countLang>0) {
00535          result = NewLocale(NS_ConvertASCIItoUTF16(acceptLanguageList[0]), _retval);
00536   }
00537 
00538   //
00539   // clean up
00540   //
00541   delete[] input;
00542   return result;
00543 }
00544 
00545 
00546 nsresult
00547 nsLocaleService::GetLocaleComponentForUserAgent(nsAString& retval)
00548 {
00549     nsCOMPtr<nsILocale>     system_locale;
00550     nsresult                result;
00551 
00552     result = GetSystemLocale(getter_AddRefs(system_locale));
00553     if (NS_SUCCEEDED(result))
00554     {
00555         result = system_locale->
00556                  GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), retval);
00557         return result;
00558     }
00559 
00560     return result;
00561 }
00562 
00563 
00564 
00565 nsresult
00566 NS_NewLocaleService(nsILocaleService** result)
00567 {
00568   if(!result)
00569     return NS_ERROR_NULL_POINTER;
00570   *result = new nsLocaleService();
00571   if (! *result)
00572     return NS_ERROR_OUT_OF_MEMORY;
00573   NS_ADDREF(*result);
00574   return NS_OK;
00575 }
00576 
00577 
00578 //
00579 // nsLocaleDefinition methods
00580 //
00581 
00582 NS_IMPL_ISUPPORTS1(nsLocaleDefinition,nsILocaleDefinition)
00583 
00584 nsLocaleDefinition::nsLocaleDefinition(void)
00585 {
00586        mLocaleDefinition = new nsLocale;
00587        if (mLocaleDefinition)
00588               mLocaleDefinition->AddRef();
00589 }
00590 
00591 nsLocaleDefinition::~nsLocaleDefinition(void)
00592 {
00593        if (mLocaleDefinition)
00594               mLocaleDefinition->Release();
00595 }
00596 
00597 NS_IMETHODIMP
00598 nsLocaleDefinition::SetLocaleCategory(const nsAString &category, const nsAString &value)
00599 {
00600        if (mLocaleDefinition)
00601               return mLocaleDefinition->AddCategory(category,value);
00602        
00603        return NS_ERROR_FAILURE;
00604 }