LCOV - code coverage report
Current view: top level - libreoffice/i18npool/source/languagetag - languagetag.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 424 466 91.0 %
Date: 2012-12-17 Functions: 54 54 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  */
       9             : 
      10             : #include "i18npool/languagetag.hxx"
      11             : #include "i18npool/mslangid.hxx"
      12             : #include <rtl/ustrbuf.hxx>
      13             : #include <rtl/bootstrap.hxx>
      14             : #include <osl/file.hxx>
      15             : #include <rtl/instance.hxx>
      16             : #include <rtl/locale.h>
      17             : 
      18             : //#define erDEBUG
      19             : 
      20             : #if defined(ENABLE_LIBLANGTAG)
      21             : #include <liblangtag/langtag.h>
      22             : #else
      23             : /* Replacement code for LGPL phobic and Android systems.
      24             :  * For iOS we could probably use NSLocale instead, that should have more or
      25             :  * less required functionality. If it is good enough, it could be used for Mac
      26             :  * OS X, too.
      27             :  */
      28             : #include "simple-langtag.cxx"
      29             : #endif
      30             : 
      31             : using rtl::OUString;
      32             : using rtl::OString;
      33             : using rtl::OUStringBuffer;
      34             : using namespace com::sun::star;
      35             : 
      36             : // The actual pointer type of mpImplLangtag that is declared void* to not
      37             : // pollute the entire code base with liblangtag.
      38             : #define LANGTAGCAST(p) (reinterpret_cast<lt_tag_t*>(p))
      39             : #define MPLANGTAG LANGTAGCAST(mpImplLangtag)
      40             : 
      41             : /** Convention to signal presence of BCP 47 language tag in a Locale's Variant
      42             :     field. The Locale's Language field then will contain this ISO 639-2
      43             :     reserved for local use code. */
      44             : #define ISO639_LANGUAGE_TAG "qlt"
      45             : 
      46             : 
      47             : // "statics" to be returned as const reference to an empty locale and string.
      48             : namespace {
      49             : struct theEmptyLocale : public rtl::Static< lang::Locale, theEmptyLocale > {};
      50             : struct theEmptyBcp47 : public rtl::Static< OUString, theEmptyBcp47 > {};
      51             : }
      52             : 
      53             : 
      54             : /** A reference holder for liblangtag data de/initialization, one static
      55             :     instance. Currently implemented such that the first "ref" inits and dtor
      56             :     (our library deinitialized) tears down.
      57             : */
      58             : class LiblantagDataRef
      59             : {
      60             : public:
      61             :     LiblantagDataRef();
      62             :     ~LiblantagDataRef();
      63          20 :     inline void incRef()
      64             :     {
      65          20 :         if (mnRef != SAL_MAX_UINT32 && !mnRef++)
      66           4 :             setup();
      67          20 :     }
      68          24 :     inline void decRef()
      69             :     {
      70          24 :         if (mnRef != SAL_MAX_UINT32 && mnRef && !--mnRef)
      71           4 :             teardown();
      72          24 :     }
      73             : private:
      74             :     rtl::OString maDataPath;   // path to liblangtag data, "|" if system
      75             :     sal_uInt32   mnRef;
      76             : 
      77             :     void setupDataPath();
      78             :     void setup();
      79             :     void teardown();
      80             : };
      81             : 
      82             : namespace {
      83             : struct theDataRef : public rtl::Static< LiblantagDataRef, theDataRef > {};
      84             : }
      85             : 
      86           4 : LiblantagDataRef::LiblantagDataRef()
      87             :     :
      88           4 :         mnRef(0)
      89             : {
      90           4 : }
      91             : 
      92           8 : LiblantagDataRef::~LiblantagDataRef()
      93             : {
      94             :     // When destructed we're tearing down unconditionally.
      95           4 :     if (mnRef)
      96           4 :         mnRef = 1;
      97           4 :     decRef();
      98           4 : }
      99             : 
     100           4 : void LiblantagDataRef::setup()
     101             : {
     102             :     SAL_INFO( "i18npool.langtag", "LiblantagDataRef::setup: initializing database");
     103           4 :     if (maDataPath.isEmpty())
     104           4 :         setupDataPath();
     105           4 :     lt_db_initialize();
     106             :     // Hold ref eternally.
     107           4 :     mnRef = SAL_MAX_UINT32;
     108           4 : }
     109             : 
     110           4 : void LiblantagDataRef::teardown()
     111             : {
     112             :     SAL_INFO( "i18npool.langtag", "LiblantagDataRef::teardown: finalizing database");
     113           4 :     lt_db_finalize();
     114           4 : }
     115             : 
     116           4 : void LiblantagDataRef::setupDataPath()
     117             : {
     118             :     // maDataPath is assumed to be empty here.
     119           4 :     OUString aURL("$BRAND_BASE_DIR/share/liblangtag");
     120           4 :     rtl::Bootstrap::expandMacros(aURL); //TODO: detect failure
     121             : 
     122             :     // Check if data is in our own installation, else assume system
     123             :     // installation.
     124           4 :     OUString aData( aURL);
     125           4 :     aData += "/language-subtag-registry.xml";
     126           4 :     osl::DirectoryItem aDirItem;
     127           4 :     if (osl::DirectoryItem::get( aData, aDirItem) == osl::DirectoryItem::E_None)
     128             :     {
     129           4 :         OUString aPath;
     130           4 :         if (osl::FileBase::getSystemPathFromFileURL( aURL, aPath) == osl::FileBase::E_None)
     131           4 :             maDataPath = OUStringToOString( aPath, RTL_TEXTENCODING_UTF8);
     132             :     }
     133           4 :     if (maDataPath.isEmpty())
     134           0 :         maDataPath = "|";   // assume system
     135             :     else
     136           4 :         lt_db_set_datadir( maDataPath.getStr());
     137           4 : }
     138             : 
     139        1650 : LanguageTag::LanguageTag( const rtl::OUString & rBcp47LanguageTag, bool bCanonicalize )
     140             :     :
     141             :         maBcp47( rBcp47LanguageTag),
     142             :         mpImplLangtag( NULL),
     143             :         mnLangID( LANGUAGE_DONTKNOW),
     144             :         meIsValid( DECISION_DONTKNOW),
     145             :         meIsIsoLocale( DECISION_DONTKNOW),
     146             :         meIsIsoODF( DECISION_DONTKNOW),
     147             :         meIsLiblangtagNeeded( DECISION_DONTKNOW),
     148        1650 :         mbSystemLocale( rBcp47LanguageTag.isEmpty()),
     149        1650 :         mbInitializedBcp47( !mbSystemLocale),
     150             :         mbInitializedLocale( false),
     151             :         mbInitializedLangID( false),
     152             :         mbCachedLanguage( false),
     153             :         mbCachedScript( false),
     154             :         mbCachedCountry( false),
     155        4950 :         mbIsFallback( false)
     156             : {
     157        1650 :     if (bCanonicalize)
     158          13 :         canonicalize();
     159        1650 : }
     160             : 
     161             : 
     162       53509 : LanguageTag::LanguageTag( const com::sun::star::lang::Locale & rLocale )
     163             :     :
     164             :         maLocale( rLocale),
     165             :         mpImplLangtag( NULL),
     166             :         mnLangID( LANGUAGE_DONTKNOW),
     167             :         meIsValid( DECISION_DONTKNOW),
     168             :         meIsIsoLocale( DECISION_DONTKNOW),
     169             :         meIsIsoODF( DECISION_DONTKNOW),
     170             :         meIsLiblangtagNeeded( DECISION_DONTKNOW),
     171       53509 :         mbSystemLocale( rLocale.Language.isEmpty()),
     172             :         mbInitializedBcp47( false),
     173       53509 :         mbInitializedLocale( !mbSystemLocale),
     174             :         mbInitializedLangID( false),
     175             :         mbCachedLanguage( false),
     176             :         mbCachedScript( false),
     177             :         mbCachedCountry( false),
     178      160527 :         mbIsFallback( false)
     179             : {
     180       53509 : }
     181             : 
     182             : 
     183       73109 : LanguageTag::LanguageTag( LanguageType nLanguage )
     184             :     :
     185             :         mpImplLangtag( NULL),
     186             :         mnLangID( nLanguage),
     187             :         meIsValid( DECISION_DONTKNOW),
     188             :         meIsIsoLocale( DECISION_DONTKNOW),
     189             :         meIsIsoODF( DECISION_DONTKNOW),
     190             :         meIsLiblangtagNeeded( DECISION_DONTKNOW),
     191             :         mbSystemLocale( nLanguage == LANGUAGE_SYSTEM),
     192             :         mbInitializedBcp47( false),
     193             :         mbInitializedLocale( false),
     194       73109 :         mbInitializedLangID( !mbSystemLocale),
     195             :         mbCachedLanguage( false),
     196             :         mbCachedScript( false),
     197             :         mbCachedCountry( false),
     198      146218 :         mbIsFallback( false)
     199             : {
     200       73109 : }
     201             : 
     202             : 
     203         290 : LanguageTag::LanguageTag( const rtl::OUString& rLanguage, const rtl::OUString& rCountry )
     204             :     :
     205             :         maLocale( rLanguage, rCountry, ""),
     206             :         mpImplLangtag( NULL),
     207             :         mnLangID( LANGUAGE_DONTKNOW),
     208             :         meIsValid( DECISION_DONTKNOW),
     209             :         meIsIsoLocale( DECISION_DONTKNOW),
     210             :         meIsIsoODF( DECISION_DONTKNOW),
     211             :         meIsLiblangtagNeeded( DECISION_DONTKNOW),
     212         290 :         mbSystemLocale( rLanguage.isEmpty()),
     213             :         mbInitializedBcp47( false),
     214         290 :         mbInitializedLocale( !mbSystemLocale),
     215             :         mbInitializedLangID( false),
     216             :         mbCachedLanguage( false),
     217             :         mbCachedScript( false),
     218             :         mbCachedCountry( false),
     219         870 :         mbIsFallback( false)
     220             : {
     221         290 : }
     222             : 
     223             : 
     224       12796 : LanguageTag::LanguageTag( const rtl_Locale & rLocale )
     225             :     :
     226             :         maLocale( rLocale.Language, rLocale.Country, rLocale.Variant),
     227             :         mpImplLangtag( NULL),
     228             :         mnLangID( LANGUAGE_DONTKNOW),
     229             :         meIsValid( DECISION_DONTKNOW),
     230             :         meIsIsoLocale( DECISION_DONTKNOW),
     231             :         meIsIsoODF( DECISION_DONTKNOW),
     232             :         meIsLiblangtagNeeded( DECISION_DONTKNOW),
     233       12796 :         mbSystemLocale( maLocale.Language.isEmpty()),
     234             :         mbInitializedBcp47( false),
     235       12796 :         mbInitializedLocale( !mbSystemLocale),
     236             :         mbInitializedLangID( false),
     237             :         mbCachedLanguage( false),
     238             :         mbCachedScript( false),
     239             :         mbCachedCountry( false),
     240       38388 :         mbIsFallback( false)
     241             : {
     242       12796 : }
     243             : 
     244             : 
     245       67742 : LanguageTag::LanguageTag( const LanguageTag & rLanguageTag )
     246             :     :
     247             :         maLocale( rLanguageTag.maLocale),
     248             :         maBcp47( rLanguageTag.maBcp47),
     249             :         maCachedLanguage( rLanguageTag.maCachedLanguage),
     250             :         maCachedScript( rLanguageTag.maCachedScript),
     251             :         maCachedCountry( rLanguageTag.maCachedCountry),
     252             :         mpImplLangtag( rLanguageTag.mpImplLangtag ?
     253           0 :                 lt_tag_copy( LANGTAGCAST( rLanguageTag.mpImplLangtag)) : NULL),
     254             :         mnLangID( rLanguageTag.mnLangID),
     255             :         meIsValid( rLanguageTag.meIsValid),
     256             :         meIsIsoLocale( rLanguageTag.meIsIsoLocale),
     257             :         meIsIsoODF( rLanguageTag.meIsIsoODF),
     258             :         meIsLiblangtagNeeded( rLanguageTag.meIsLiblangtagNeeded),
     259             :         mbSystemLocale( rLanguageTag.mbSystemLocale),
     260             :         mbInitializedBcp47( rLanguageTag.mbInitializedBcp47),
     261             :         mbInitializedLocale( rLanguageTag.mbInitializedLocale),
     262             :         mbInitializedLangID( rLanguageTag.mbInitializedLangID),
     263             :         mbCachedLanguage( rLanguageTag.mbCachedLanguage),
     264             :         mbCachedScript( rLanguageTag.mbCachedScript),
     265             :         mbCachedCountry( rLanguageTag.mbCachedCountry),
     266       67742 :         mbIsFallback( rLanguageTag.mbIsFallback)
     267             : {
     268       67742 :     if (mpImplLangtag)
     269           0 :         theDataRef::get().incRef();
     270       67742 : }
     271             : 
     272             : 
     273       34818 : LanguageTag& LanguageTag::operator=( const LanguageTag & rLanguageTag )
     274             : {
     275       34818 :     maLocale            = rLanguageTag.maLocale;
     276       34818 :     maBcp47             = rLanguageTag.maBcp47;
     277       34818 :     maCachedLanguage    = rLanguageTag.maCachedLanguage;
     278       34818 :     maCachedScript      = rLanguageTag.maCachedScript;
     279       34818 :     maCachedCountry     = rLanguageTag.maCachedCountry;
     280       34818 :     mpImplLangtag       = rLanguageTag.mpImplLangtag;
     281             :     mpImplLangtag       = rLanguageTag.mpImplLangtag ?
     282       34818 :                             lt_tag_copy( LANGTAGCAST( rLanguageTag.mpImplLangtag)) : NULL;
     283       34818 :     mnLangID            = rLanguageTag.mnLangID;
     284       34818 :     meIsValid           = rLanguageTag.meIsValid;
     285       34818 :     meIsIsoLocale       = rLanguageTag.meIsIsoLocale;
     286       34818 :     meIsIsoODF          = rLanguageTag.meIsIsoODF;
     287       34818 :     meIsLiblangtagNeeded= rLanguageTag.meIsLiblangtagNeeded;
     288       34818 :     mbSystemLocale      = rLanguageTag.mbSystemLocale;
     289       34818 :     mbInitializedBcp47  = rLanguageTag.mbInitializedBcp47;
     290       34818 :     mbInitializedLocale = rLanguageTag.mbInitializedLocale;
     291       34818 :     mbInitializedLangID = rLanguageTag.mbInitializedLangID;
     292       34818 :     mbCachedLanguage    = rLanguageTag.mbCachedLanguage;
     293       34818 :     mbCachedScript      = rLanguageTag.mbCachedScript;
     294       34818 :     mbCachedCountry     = rLanguageTag.mbCachedCountry;
     295       34818 :     mbIsFallback        = rLanguageTag.mbIsFallback;
     296       34818 :     if (mpImplLangtag)
     297           0 :         theDataRef::get().incRef();
     298       34818 :     return *this;
     299             : }
     300             : 
     301             : 
     302      404720 : LanguageTag::~LanguageTag()
     303             : {
     304      202360 :     if (mpImplLangtag)
     305             :     {
     306          20 :         lt_tag_unref( MPLANGTAG);
     307          20 :         theDataRef::get().decRef();
     308             :     }
     309      202360 : }
     310             : 
     311             : 
     312        9715 : void LanguageTag::resetVars()
     313             : {
     314        9715 :     if (mpImplLangtag)
     315             :     {
     316           0 :         lt_tag_unref( MPLANGTAG);
     317           0 :         mpImplLangtag = NULL;
     318           0 :         theDataRef::get().decRef();
     319             :     }
     320             : 
     321        9715 :     maLocale            = lang::Locale();
     322        9715 :     if (!maBcp47.isEmpty())
     323          14 :         maBcp47         = OUString();
     324        9715 :     if (!maCachedLanguage.isEmpty())
     325          12 :         maCachedLanguage= OUString();
     326        9715 :     if (!maCachedScript.isEmpty())
     327           0 :         maCachedScript  = OUString();
     328        9715 :     if (!maCachedCountry.isEmpty())
     329          12 :         maCachedCountry = OUString();
     330        9715 :     mnLangID            = LANGUAGE_DONTKNOW;
     331        9715 :     meIsValid           = DECISION_DONTKNOW;
     332        9715 :     meIsIsoLocale       = DECISION_DONTKNOW;
     333        9715 :     meIsIsoODF          = DECISION_DONTKNOW;
     334        9715 :     meIsLiblangtagNeeded= DECISION_DONTKNOW;
     335        9715 :     mbSystemLocale      = true;
     336        9715 :     mbInitializedBcp47  = false;
     337        9715 :     mbInitializedLocale = false;
     338        9715 :     mbInitializedLangID = false;
     339        9715 :     mbCachedLanguage    = false;
     340        9715 :     mbCachedScript      = false;
     341        9715 :     mbCachedCountry     = false;
     342        9715 :     mbIsFallback        = false;
     343        9715 : }
     344             : 
     345             : 
     346         186 : void LanguageTag::reset( const rtl::OUString & rBcp47LanguageTag, bool bCanonicalize )
     347             : {
     348         186 :     resetVars();
     349         186 :     maBcp47             = rBcp47LanguageTag;
     350         186 :     mbSystemLocale      = rBcp47LanguageTag.isEmpty();
     351         186 :     mbInitializedBcp47  = !mbSystemLocale;
     352             : 
     353         186 :     if (bCanonicalize)
     354           0 :         canonicalize();
     355         186 : }
     356             : 
     357             : 
     358           2 : void LanguageTag::reset( const com::sun::star::lang::Locale & rLocale )
     359             : {
     360           2 :     resetVars();
     361           2 :     maLocale            = rLocale;
     362           2 :     mbSystemLocale      = rLocale.Language.isEmpty();
     363           2 :     mbInitializedLocale = !mbSystemLocale;
     364           2 : }
     365             : 
     366             : 
     367        9527 : void LanguageTag::reset( LanguageType nLanguage )
     368             : {
     369        9527 :     resetVars();
     370        9527 :     mnLangID            = nLanguage;
     371        9527 :     mbSystemLocale      = nLanguage == LANGUAGE_SYSTEM;
     372        9527 :     mbInitializedLangID = !mbSystemLocale;
     373        9527 : }
     374             : 
     375             : 
     376       18896 : bool LanguageTag::canonicalize()
     377             : {
     378             : #ifdef erDEBUG
     379             :     // dump once
     380             :     struct dumper
     381             :     {
     382             :         void** mpp;
     383             :         dumper( void** pp ) : mpp( *pp ? NULL : pp) {}
     384             :         ~dumper() { if (mpp && *mpp) lt_tag_dump( LANGTAGCAST( *mpp)); }
     385             :     };
     386             :     dumper aDumper( &mpImplLangtag);
     387             : #endif
     388             : 
     389             :     // Side effect: have maBcp47 in any case, resolved system.
     390             :     // Some methods calling canonicalize() (or not calling it due to
     391             :     // meIsLiblangtagNeeded==DECISION_NO) rely on this! Hence do not set
     392             :     // meIsLiblangtagNeeded anywhere else than hereafter.
     393       18896 :     getBcp47( true );
     394             : 
     395             :     // The simple cases and known locales don't need liblangtag processing,
     396             :     // which also avoids loading liblangtag data on startup.
     397       18896 :     if (meIsLiblangtagNeeded == DECISION_DONTKNOW)
     398             :     {
     399       18896 :         bool bTemporaryLocale = false;
     400       18896 :         bool bTemporaryLangID = false;
     401       18896 :         if (!mbInitializedLocale && !mbInitializedLangID)
     402             :         {
     403        1772 :             if (mbSystemLocale)
     404             :             {
     405           0 :                 mnLangID = MsLangId::getRealLanguage( LANGUAGE_SYSTEM);
     406           0 :                 mbInitializedLangID = true;
     407             :             }
     408             :             else
     409             :             {
     410             :                 // Now this is getting funny.. we only have some BCP47 string
     411             :                 // and want to determine if parsing it would be possible
     412             :                 // without using liblangtag just to see if it is a simple known
     413             :                 // locale.
     414        1772 :                 OUString aLanguage, aScript, aCountry;
     415        1772 :                 if (simpleExtract( maBcp47, aLanguage, aScript, aCountry))
     416             :                 {
     417        1756 :                     if (aScript.isEmpty())
     418             :                     {
     419        1752 :                         maLocale.Language = aLanguage;
     420        1752 :                         maLocale.Country  = aCountry;
     421             :                     }
     422             :                     else
     423             :                     {
     424           4 :                         maLocale.Language = ISO639_LANGUAGE_TAG;
     425           4 :                         maLocale.Country  = aCountry;
     426           4 :                         maLocale.Variant  = maBcp47;
     427             :                     }
     428        1756 :                     bTemporaryLocale = mbInitializedLocale = true;
     429        1772 :                 }
     430             :             }
     431             :         }
     432       18896 :         if (mbInitializedLangID && !mbInitializedLocale)
     433             :         {
     434             :             // Do not call getLocale() here because that prefers
     435             :             // convertBcp47ToLocale() which would end up in recursion via
     436             :             // isIsoLocale()!
     437             : 
     438             :             // Prepare to verify that we have a known locale, not just an
     439             :             // arbitrary MS-LangID.
     440           0 :             convertLangToLocale();
     441             :         }
     442       18896 :         if (mbInitializedLocale)
     443             :         {
     444       18880 :             if (maLocale.Variant.isEmpty())
     445        6080 :                 meIsLiblangtagNeeded = DECISION_NO;     // per definition ll[l][-CC]
     446             :             else
     447             :             {
     448       12800 :                 if (!mbInitializedLangID)
     449             :                 {
     450       12800 :                     convertLocaleToLang();
     451       12800 :                     if (bTemporaryLocale)
     452           4 :                         bTemporaryLangID = true;
     453             :                 }
     454       12800 :                 if (mnLangID != LANGUAGE_DONTKNOW && mnLangID != LANGUAGE_SYSTEM)
     455       12796 :                     meIsLiblangtagNeeded = DECISION_NO; // known locale
     456             :             }
     457             :         }
     458       18896 :         if (bTemporaryLocale)
     459             :         {
     460        1756 :             mbInitializedLocale = false;
     461        1756 :             maLocale = lang::Locale();
     462             :         }
     463       18896 :         if (bTemporaryLangID)
     464             :         {
     465           4 :             mbInitializedLangID = false;
     466           4 :             mnLangID = LANGUAGE_DONTKNOW;
     467             :         }
     468             :     }
     469       18896 :     if (meIsLiblangtagNeeded == DECISION_NO)
     470             :     {
     471       18876 :         meIsValid = DECISION_YES;   // really, known must be valid ...
     472       18876 :         return true;                // that's it
     473             :     }
     474          20 :     meIsLiblangtagNeeded = DECISION_YES;
     475             :     SAL_INFO( "i18npool.langtag", "LanguageTag::canonicalize: using liblangtag for " << maBcp47);
     476             : 
     477          20 :     if (!mpImplLangtag)
     478             :     {
     479          20 :         theDataRef::get().incRef();
     480          20 :         mpImplLangtag = lt_tag_new();
     481             :     }
     482             : 
     483             :     // ensure error is free'd
     484             :     struct myerror
     485             :     {
     486             :         lt_error_t* p;
     487          20 :         myerror() : p(NULL) {}
     488          20 :         ~myerror() { if (p) lt_error_unref( p); }
     489          20 :     } aError;
     490             : 
     491          20 :     if (lt_tag_parse( MPLANGTAG, OUStringToOString( maBcp47, RTL_TEXTENCODING_UTF8).getStr(), &aError.p))
     492             :     {
     493           6 :         char* pTag = lt_tag_canonicalize( MPLANGTAG, &aError.p);
     494             :         SAL_WARN_IF( !pTag, "i18npool.langtag", "LanguageTag::canonicalize: could not canonicalize " << maBcp47);
     495           6 :         if (pTag)
     496             :         {
     497           6 :             OUString aOld( maBcp47);
     498           6 :             maBcp47 = OUString::createFromAscii( pTag);
     499             :             // Make the lt_tag_t follow the new string if different, which
     500             :             // removes default script and such.
     501           6 :             if (maBcp47 != aOld)
     502             :             {
     503           4 :                 if (!lt_tag_parse( MPLANGTAG, pTag, &aError.p))
     504             :                 {
     505             :                     SAL_WARN( "i18npool.langtag", "LanguageTag::canonicalize: could not reparse " << maBcp47);
     506           0 :                     free( pTag);
     507           0 :                     meIsValid = DECISION_NO;
     508           0 :                     return false;
     509             :                 }
     510             :             }
     511           6 :             free( pTag);
     512           6 :             meIsValid = DECISION_YES;
     513           6 :             return true;
     514             :         }
     515             :     }
     516             :     else
     517             :     {
     518             :         SAL_INFO( "i18npool.langtag", "LanguageTag::canonicalize: could not parse " << maBcp47);
     519             :     }
     520          14 :     meIsValid = DECISION_NO;
     521          14 :     return false;
     522             : }
     523             : 
     524             : 
     525       17637 : void LanguageTag::convertLocaleToBcp47()
     526             : {
     527       17637 :     if (mbSystemLocale && !mbInitializedLocale)
     528           0 :         convertLangToLocale();
     529             : 
     530       17637 :     if (maLocale.Language == ISO639_LANGUAGE_TAG)
     531             :     {
     532           0 :         maBcp47 = maLocale.Variant;
     533           0 :         meIsIsoLocale = DECISION_NO;
     534             :     }
     535             :     else
     536             :     {
     537             :         /* XXX NOTE: most legacy code never evaluated the Variant field, so for
     538             :          * now just concatenate language and country. In case we stumbled over
     539             :          * variant aware code we'd have to take care of that. */
     540       17637 :         if (maLocale.Country.isEmpty())
     541          11 :             maBcp47 = maLocale.Language;
     542             :         else
     543             :         {
     544       17626 :             OUStringBuffer aBuf( maLocale.Language.getLength() + 1 + maLocale.Country.getLength());
     545       17626 :             aBuf.append( maLocale.Language).append( '-').append( maLocale.Country);
     546       17626 :             maBcp47 = aBuf.makeStringAndClear();
     547             :         }
     548             :     }
     549       17637 :     mbInitializedBcp47 = true;
     550       17637 : }
     551             : 
     552             : 
     553       47703 : void LanguageTag::convertLocaleToLang()
     554             : {
     555       47703 :     if (mbSystemLocale)
     556             :     {
     557         316 :         mnLangID = MsLangId::getRealLanguage( LANGUAGE_SYSTEM);
     558             :     }
     559             :     else
     560             :     {
     561             :         /* FIXME: this is temporary until code base is converted to not use
     562             :          * MsLangId::convert...() anymore. After that, proper new method has to
     563             :          * be implemented to allow ISO639_LANGUAGE_TAG and sript tag and such. */
     564       47387 :         mnLangID = MsLangId::Conversion::convertLocaleToLanguage( maLocale);
     565             :     }
     566       47703 :     mbInitializedLangID = true;
     567       47703 : }
     568             : 
     569             : 
     570        1771 : void LanguageTag::convertBcp47ToLocale()
     571             : {
     572        1771 :     bool bIso = isIsoLocale();
     573        1771 :     if (bIso)
     574             :     {
     575        1767 :         maLocale.Language = getLanguageFromLangtag();
     576        1767 :         maLocale.Country = getRegionFromLangtag();
     577        1767 :         maLocale.Variant = OUString();
     578             :     }
     579             :     else
     580             :     {
     581           4 :         maLocale.Language = ISO639_LANGUAGE_TAG;
     582           4 :         maLocale.Country = getCountry();
     583           4 :         maLocale.Variant = maBcp47;
     584             :     }
     585        1771 :     mbInitializedLocale = true;
     586        1771 : }
     587             : 
     588             : 
     589         575 : void LanguageTag::convertBcp47ToLang()
     590             : {
     591         575 :     if (mbSystemLocale)
     592             :     {
     593           0 :         mnLangID = MsLangId::getRealLanguage( LANGUAGE_SYSTEM);
     594             :     }
     595             :     else
     596             :     {
     597             :         /* FIXME: this is temporary. If we support locales that consist not
     598             :          * only of language and country, e.g. added script, this probably needs
     599             :          * to be adapted. */
     600         575 :         if (!mbInitializedLocale)
     601         529 :             convertBcp47ToLocale();
     602         575 :         convertLocaleToLang();
     603             :     }
     604         575 :     mbInitializedLangID = true;
     605         575 : }
     606             : 
     607             : 
     608       89858 : void LanguageTag::convertLangToLocale()
     609             : {
     610       89858 :     if (mbSystemLocale && !mbInitializedLangID)
     611             :     {
     612        9612 :         mnLangID = MsLangId::getRealLanguage( LANGUAGE_SYSTEM);
     613        9612 :         mbInitializedLangID = true;
     614             :     }
     615             :     /* FIXME: this is temporary until code base is converted to not use
     616             :      * MsLangId::convert...() anymore. After that, proper new method has to be
     617             :      * implemented to allow ISO639_LANGUAGE_TAG and script tag and such. */
     618             :     // Resolve system here!
     619       89858 :     maLocale = MsLangId::Conversion::convertLanguageToLocale( mnLangID, true);
     620       89858 :     mbInitializedLocale = true;
     621       89858 : }
     622             : 
     623             : 
     624        4504 : void LanguageTag::convertLangToBcp47()
     625             : {
     626             :     /* FIXME: this is temporary. If we support locales that consist not only of
     627             :      * language and country, e.g. added script, this probably needs to be
     628             :      * adapted. */
     629        4504 :     if (!mbInitializedLocale)
     630        4504 :         convertLangToLocale();
     631        4504 :     convertLocaleToBcp47();
     632        4504 :     mbInitializedBcp47 = true;
     633        4504 : }
     634             : 
     635             : 
     636       61654 : const rtl::OUString & LanguageTag::getBcp47( bool bResolveSystem ) const
     637             : {
     638       61654 :     if (!bResolveSystem && mbSystemLocale)
     639         240 :         return theEmptyBcp47::get();
     640       61414 :     if (!mbInitializedBcp47)
     641             :     {
     642       17637 :         if (mbInitializedLocale)
     643       13133 :             const_cast<LanguageTag*>(this)->convertLocaleToBcp47();
     644             :         else
     645        4504 :             const_cast<LanguageTag*>(this)->convertLangToBcp47();
     646             :     }
     647       61414 :     return maBcp47;
     648             : }
     649             : 
     650             : 
     651       20660 : rtl::OUString LanguageTag::getLanguageFromLangtag()
     652             : {
     653       20660 :     OUString aLanguage;
     654       20660 :     if (meIsLiblangtagNeeded != DECISION_NO && !mpImplLangtag)
     655       16898 :         canonicalize();
     656       20660 :     if (maBcp47.isEmpty())
     657           0 :         return aLanguage;
     658       20660 :     if (mpImplLangtag)
     659             :     {
     660          34 :         const lt_lang_t* pLangT = lt_tag_get_language( MPLANGTAG);
     661             :         SAL_WARN_IF( !pLangT, "i18npool.langtag", "LanguageTag::getLanguageFromLangtag: pLangT==NULL");
     662          34 :         if (!pLangT)
     663           0 :             return aLanguage;
     664          34 :         const char* pLang = lt_lang_get_tag( pLangT);
     665             :         SAL_WARN_IF( !pLang, "i18npool.langtag", "LanguageTag::getLanguageFromLangtag: pLang==NULL");
     666          34 :         if (pLang)
     667          34 :             aLanguage = OUString::createFromAscii( pLang);
     668             :     }
     669             :     else
     670             :     {
     671       20626 :         if (mbCachedLanguage || cacheSimpleLSC())
     672       20626 :             aLanguage = maCachedLanguage;
     673             :     }
     674       20660 :     return aLanguage;
     675             : }
     676             : 
     677             : 
     678           8 : rtl::OUString LanguageTag::getScriptFromLangtag()
     679             : {
     680           8 :     OUString aScript;
     681           8 :     if (meIsLiblangtagNeeded != DECISION_NO && !mpImplLangtag)
     682           0 :         canonicalize();
     683           8 :     if (maBcp47.isEmpty())
     684           0 :         return aScript;
     685           8 :     if (mpImplLangtag)
     686             :     {
     687           8 :         const lt_script_t* pScriptT = lt_tag_get_script( MPLANGTAG);
     688             :         // pScriptT==NULL is valid for default scripts
     689           8 :         if (!pScriptT)
     690           6 :             return aScript;
     691           2 :         const char* pScript = lt_script_get_tag( pScriptT);
     692             :         SAL_WARN_IF( !pScript, "i18npool.langtag", "LanguageTag::getScriptFromLangtag: pScript==NULL");
     693           2 :         if (pScript)
     694           2 :             aScript = OUString::createFromAscii( pScript);
     695             :     }
     696             :     else
     697             :     {
     698           0 :         if (mbCachedScript || cacheSimpleLSC())
     699           0 :             aScript = maCachedScript;
     700             :     }
     701           2 :     return aScript;
     702             : }
     703             : 
     704             : 
     705        3768 : rtl::OUString LanguageTag::getRegionFromLangtag()
     706             : {
     707        3768 :     OUString aRegion;
     708        3768 :     if (meIsLiblangtagNeeded != DECISION_NO && !mpImplLangtag)
     709           0 :         canonicalize();
     710        3768 :     if (maBcp47.isEmpty())
     711           0 :         return aRegion;
     712        3768 :     if (mpImplLangtag)
     713             :     {
     714          40 :         const lt_region_t* pRegionT = lt_tag_get_region( MPLANGTAG);
     715             :         // pRegionT==NULL is valid for language only tags, rough check here
     716             :         // that does not take sophisticated tags into account that actually
     717             :         // should have a region, check for ll, lll, ll-Ssss and lll-Ssss so
     718             :         // that ll-CC and lll-CC actually fail.
     719             :         SAL_WARN_IF( !pRegionT &&
     720             :                 maBcp47.getLength() != 2 && maBcp47.getLength() != 3 &&
     721             :                 maBcp47.getLength() != 7 && maBcp47.getLength() != 8,
     722             :                 "i18npool.langtag", "LanguageTag::getRegionFromLangtag: pRegionT==NULL");
     723          40 :         if (!pRegionT)
     724          30 :             return aRegion;
     725          10 :         const char* pRegion = lt_region_get_tag( pRegionT);
     726             :         SAL_WARN_IF( !pRegion, "i18npool.langtag", "LanguageTag::getRegionFromLangtag: pRegion==NULL");
     727          10 :         if (pRegion)
     728          10 :             aRegion = OUString::createFromAscii( pRegion);
     729             :     }
     730             :     else
     731             :     {
     732        3728 :         if (mbCachedCountry || cacheSimpleLSC())
     733        3728 :             aRegion = maCachedCountry;
     734             :     }
     735        3738 :     return aRegion;
     736             : }
     737             : 
     738             : 
     739      575307 : const com::sun::star::lang::Locale & LanguageTag::getLocale( bool bResolveSystem ) const
     740             : {
     741      575307 :     if (!bResolveSystem && mbSystemLocale)
     742          24 :         return theEmptyLocale::get();
     743      575283 :     if (!mbInitializedLocale)
     744             :     {
     745       86596 :         if (mbInitializedBcp47)
     746        1242 :             const_cast<LanguageTag*>(this)->convertBcp47ToLocale();
     747             :         else
     748       85354 :             const_cast<LanguageTag*>(this)->convertLangToLocale();
     749             :     }
     750      575283 :     return maLocale;
     751             : }
     752             : 
     753             : 
     754     1026192 : LanguageType LanguageTag::getLanguageType( bool bResolveSystem ) const
     755             : {
     756     1026192 :     if (!bResolveSystem && mbSystemLocale)
     757       10234 :         return LANGUAGE_SYSTEM;
     758     1015958 :     if (!mbInitializedLangID)
     759             :     {
     760       34903 :         if (mbInitializedBcp47)
     761         575 :             const_cast<LanguageTag*>(this)->convertBcp47ToLang();
     762             :         else
     763       34328 :             const_cast<LanguageTag*>(this)->convertLocaleToLang();
     764             :     }
     765     1015958 :     return mnLangID;
     766             : }
     767             : 
     768             : 
     769         226 : void LanguageTag::getIsoLanguageCountry( rtl::OUString& rLanguage, rtl::OUString& rCountry ) const
     770             : {
     771         226 :     if (!isIsoLocale())
     772             :     {
     773           0 :         rLanguage = OUString();
     774           0 :         rCountry = OUString();
     775         226 :         return;
     776             :     }
     777             :     // After isIsoLocale() it's safe to call getLanguage() for ISO code.
     778         226 :     rLanguage = getLanguage();
     779         226 :     rCountry = getCountry();
     780             : }
     781             : 
     782             : 
     783             : namespace
     784             : {
     785             : 
     786        4020 : bool isLowerAscii( sal_Unicode c )
     787             : {
     788        4020 :     return 'a' <= c && c <= 'z';
     789             : }
     790             : 
     791        3956 : bool isUpperAscii( sal_Unicode c )
     792             : {
     793        3956 :     return 'A' <= c && c <= 'Z';
     794             : }
     795             : 
     796             : }
     797             : 
     798             : 
     799             : // static
     800        1995 : bool LanguageTag::isIsoLanguage( const rtl::OUString& rLanguage )
     801             : {
     802             :     /* TODO: ignore case? For now let's see where rubbish is used. */
     803             :     bool b2chars;
     804        6003 :     if (((b2chars = (rLanguage.getLength() == 2)) || rLanguage.getLength() == 3) &&
     805        3990 :             isLowerAscii( rLanguage[0]) && isLowerAscii( rLanguage[1]) &&
     806          18 :             (b2chars || isLowerAscii( rLanguage[2])))
     807        1995 :         return true;
     808             :     SAL_WARN_IF( ((rLanguage.getLength() == 2 || rLanguage.getLength() == 3) &&
     809             :                 (isUpperAscii( rLanguage[0]) || isUpperAscii( rLanguage[1]))) ||
     810             :             (rLanguage.getLength() == 3 && isUpperAscii( rLanguage[2])), "i18npool.langtag",
     811             :             "LanguageTag::isIsoLanguage: rejecting upper case " << rLanguage);
     812           0 :     return false;
     813             : }
     814             : 
     815             : 
     816             : // static
     817        2001 : bool LanguageTag::isIsoCountry( const rtl::OUString& rRegion )
     818             : {
     819             :     /* TODO: ignore case? For now let's see where rubbish is used. */
     820        7929 :     if (rRegion.isEmpty() ||
     821        5928 :             (rRegion.getLength() == 2 && isUpperAscii( rRegion[0]) && isUpperAscii( rRegion[1])))
     822        2001 :         return true;
     823             :     SAL_WARN_IF( rRegion.getLength() == 2 && (isLowerAscii( rRegion[0]) || isLowerAscii( rRegion[1])),
     824             :             "i18npool.langtag", "LanguageTag::isIsoCountry: rejecting lower case " << rRegion);
     825           0 :     return false;
     826             : }
     827             : 
     828             : 
     829             : // static
     830          12 : bool LanguageTag::isIsoScript( const rtl::OUString& rScript )
     831             : {
     832             :     /* TODO: ignore case? For now let's see where rubbish is used. */
     833          32 :     if (rScript.isEmpty() ||
     834           4 :             (rScript.getLength() == 4 &&
     835           8 :              isUpperAscii( rScript[0]) && isLowerAscii( rScript[1]) &&
     836           8 :              isLowerAscii( rScript[2]) && isLowerAscii( rScript[3])))
     837          12 :         return true;
     838             :     SAL_WARN_IF( rScript.getLength() == 4 &&
     839             :             (isLowerAscii( rScript[0]) || isUpperAscii( rScript[1]) ||
     840             :              isUpperAscii( rScript[2]) || isUpperAscii( rScript[3])),
     841             :             "i18npool.langtag", "LanguageTag::isIsoScript: rejecting case mismatch " << rScript);
     842           0 :     return false;
     843             : }
     844             : 
     845             : 
     846       19142 : rtl::OUString LanguageTag::getLanguage() const
     847             : {
     848       19142 :     if (!mbCachedLanguage)
     849             :     {
     850       18893 :         maCachedLanguage = const_cast<LanguageTag*>(this)->getLanguageFromLangtag();
     851       18893 :         mbCachedLanguage = true;
     852             :     }
     853       19142 :     return maCachedLanguage;
     854             : }
     855             : 
     856             : 
     857        4126 : rtl::OUString LanguageTag::getScript() const
     858             : {
     859        4126 :     if (!mbCachedScript)
     860             :     {
     861           8 :         maCachedScript = const_cast<LanguageTag*>(this)->getScriptFromLangtag();
     862           8 :         mbCachedScript = true;
     863             :     }
     864        4126 :     return maCachedScript;
     865             : }
     866             : 
     867             : 
     868        4108 : rtl::OUString LanguageTag::getLanguageAndScript() const
     869             : {
     870        4108 :     OUString aLanguageScript( getLanguage());
     871        4108 :     OUString aScript( getScript());
     872        4108 :     if (!aScript.isEmpty())
     873             :     {
     874           2 :         OUStringBuffer aBuf( aLanguageScript.getLength() + 1 + aScript.getLength());
     875           2 :         aBuf.append( aLanguageScript).append( '-').append( aScript);
     876           2 :         aLanguageScript = aBuf.makeStringAndClear();
     877             :     }
     878        4108 :     return aLanguageScript;
     879             : }
     880             : 
     881             : 
     882       13043 : rtl::OUString LanguageTag::getCountry() const
     883             : {
     884       13043 :     if (!mbCachedCountry)
     885             :     {
     886           6 :         maCachedCountry = const_cast<LanguageTag*>(this)->getRegionFromLangtag();
     887           6 :         if (!isIsoCountry( maCachedCountry))
     888           0 :             maCachedCountry = OUString();
     889           6 :         mbCachedCountry = true;
     890             :     }
     891       13043 :     return maCachedCountry;
     892             : }
     893             : 
     894             : 
     895        1995 : rtl::OUString LanguageTag::getRegion() const
     896             : {
     897        1995 :     return const_cast<LanguageTag*>(this)->getRegionFromLangtag();
     898             : }
     899             : 
     900             : 
     901       18875 : bool LanguageTag::cacheSimpleLSC()
     902             : {
     903       18875 :     OUString aLanguage, aScript, aCountry;
     904       18875 :     bool bRet = simpleExtract( maBcp47, aLanguage, aScript, aCountry);
     905       18875 :     if (bRet)
     906             :     {
     907       18875 :         maCachedLanguage = aLanguage;
     908       18875 :         maCachedScript   = aScript;
     909       18875 :         maCachedCountry  = aCountry;
     910       18875 :         mbCachedLanguage = mbCachedScript = mbCachedCountry = true;
     911             :     }
     912       18875 :     return bRet;
     913             : }
     914             : 
     915             : 
     916        2017 : bool LanguageTag::isIsoLocale() const
     917             : {
     918        2017 :     if (meIsIsoLocale == DECISION_DONTKNOW)
     919             :     {
     920        1997 :         if (meIsLiblangtagNeeded != DECISION_NO && !mpImplLangtag)
     921        1985 :             const_cast<LanguageTag*>(this)->canonicalize();
     922             :         // It must be at most ll-CC or lll-CC
     923             :         // Do not use getCountry() here, use getRegion() instead.
     924        1997 :         meIsIsoLocale = ((maBcp47.isEmpty() ||
     925       11966 :                     (maBcp47.getLength() <= 6 && isIsoLanguage( getLanguage()) && isIsoCountry( getRegion()))) ?
     926       13963 :                 DECISION_YES : DECISION_NO);
     927             :     }
     928        2017 :     return meIsIsoLocale == DECISION_YES;
     929             : }
     930             : 
     931             : 
     932          10 : bool LanguageTag::isIsoODF() const
     933             : {
     934          10 :     if (meIsIsoODF == DECISION_DONTKNOW)
     935             :     {
     936          10 :         if (meIsLiblangtagNeeded != DECISION_NO && !mpImplLangtag)
     937           0 :             const_cast<LanguageTag*>(this)->canonicalize();
     938          10 :         if (!isIsoScript( getScript()))
     939           0 :             return ((meIsIsoODF = DECISION_NO) == DECISION_YES);
     940             :         // The usual case is lll-CC so simply check that first.
     941          10 :         if (isIsoLocale())
     942           6 :             return ((meIsIsoODF = DECISION_YES) == DECISION_YES);
     943             :         // If this is not ISO locale for which script must not exist it can
     944             :         // still be ISO locale plus ISO script lll-Ssss-CC
     945           4 :         meIsIsoODF = ((maBcp47.getLength() <= 11 &&
     946          16 :                     isIsoLanguage( getLanguage()) && isIsoCountry( getRegion()) && isIsoScript( getScript())) ?
     947          20 :                 DECISION_YES : DECISION_NO);
     948             :     }
     949           4 :     return meIsIsoODF == DECISION_YES;
     950             : }
     951             : 
     952             : 
     953          11 : bool LanguageTag::isValidBcp47() const
     954             : {
     955          11 :     if (meIsValid == DECISION_DONTKNOW)
     956             :     {
     957           0 :         if (meIsLiblangtagNeeded != DECISION_NO && !mpImplLangtag)
     958           0 :            const_cast<LanguageTag*>(this)->canonicalize();
     959             :         SAL_WARN_IF( meIsValid == DECISION_DONTKNOW, "i18npool.langtag",
     960             :                 "LanguageTag::isValidBcp47: canonicalize() didn't set meIsValid");
     961             :     }
     962          11 :     return meIsValid == DECISION_YES;
     963             : }
     964             : 
     965             : 
     966      977525 : bool LanguageTag::isSystemLocale() const
     967             : {
     968      977525 :     return mbSystemLocale;
     969             : }
     970             : 
     971             : 
     972          38 : LanguageTag & LanguageTag::makeFallback()
     973             : {
     974          38 :     if (!mbIsFallback)
     975             :     {
     976          38 :         if (mbInitializedLangID)
     977             :         {
     978           0 :             LanguageType nLang1 = getLanguageType();
     979           0 :             LanguageType nLang2 = MsLangId::Conversion::lookupFallbackLanguage( nLang1);
     980           0 :             if (nLang1 != nLang2)
     981           0 :                 reset( nLang2);
     982             :         }
     983             :         else
     984             :         {
     985          38 :             const lang::Locale& rLocale1 = getLocale();
     986          38 :             lang::Locale aLocale2( MsLangId::Conversion::lookupFallbackLocale( rLocale1));
     987         114 :             if (    rLocale1.Language != aLocale2.Language ||
     988          38 :                     rLocale1.Country  != aLocale2.Country ||
     989          38 :                     rLocale1.Variant  != aLocale2.Variant)
     990           0 :                 reset( aLocale2);
     991             :         }
     992          38 :         mbIsFallback = true;
     993             :     }
     994          38 :     return *this;
     995             : }
     996             : 
     997             : 
     998       19099 : bool LanguageTag::operator==( const LanguageTag & rLanguageTag ) const
     999             : {
    1000             :     // Compare full language tag strings but SYSTEM unresolved.
    1001       19099 :     return getBcp47( false) == rLanguageTag.getBcp47( false);
    1002             : }
    1003             : 
    1004             : 
    1005       19099 : bool LanguageTag::operator!=( const LanguageTag & rLanguageTag ) const
    1006             : {
    1007       19099 :     return !operator==( rLanguageTag);
    1008             : }
    1009             : 
    1010             : 
    1011             : // static
    1012       20647 : bool LanguageTag::simpleExtract( const rtl::OUString& rBcp47,
    1013             :                                  rtl::OUString& rLanguage,
    1014             :                                  rtl::OUString& rScript,
    1015             :                                  rtl::OUString& rCountry )
    1016             : {
    1017       20647 :     bool bRet = false;
    1018       20647 :     const sal_Int32 nLen = rBcp47.getLength();
    1019       20647 :     const sal_Int32 nHyph1 = rBcp47.indexOf( '-');
    1020       20647 :     if ((nLen == 2 || nLen == 3) && nHyph1 < 0)     // ll or lll
    1021             :     {
    1022          29 :         rLanguage = rBcp47;
    1023          29 :         rScript = rCountry = OUString();
    1024          29 :         bRet = true;
    1025             :     }
    1026       20618 :     else if (  (nLen == 5 && nHyph1 == 2)           // ll-CC
    1027             :             || (nLen == 6 && nHyph1 == 3))          // lll-CC
    1028             :     {
    1029       20598 :         rLanguage = rBcp47.copy( 0, nHyph1);
    1030       20598 :         rCountry  = rBcp47.copy( nHyph1 + 1, 2);
    1031       20598 :         rScript = OUString();
    1032       20598 :         bRet = true;
    1033             :     }
    1034          20 :     else if (  (nHyph1 == 2 && nLen == 10)          // ll-Ssss-CC check
    1035             :             || (nHyph1 == 3 && nLen == 11))         // lll-Ssss-CC check
    1036             :     {
    1037           4 :         const sal_Int32 nHyph2 = rBcp47.indexOf( '-', nHyph1 + 1);
    1038           4 :         if (nHyph2 == nHyph1 + 5)
    1039             :         {
    1040           4 :             rLanguage = rBcp47.copy( 0, nHyph1);
    1041           4 :             rScript   = rBcp47.copy( nHyph1 + 1, 4);
    1042           4 :             rCountry  = rBcp47.copy( nHyph2 + 1, 2);
    1043           4 :             bRet = true;
    1044             :         }
    1045             :     }
    1046       20647 :     if (!bRet)
    1047          16 :         rLanguage = rScript = rCountry = OUString();
    1048       20647 :     return bRet;
    1049             : }
    1050             : 
    1051             : 
    1052             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10