1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_desktop.hxx" 30 31 #include "app.hxx" 32 #include "langselect.hxx" 33 #include "cmdlineargs.hxx" 34 #include <stdio.h> 35 36 #include <rtl/string.hxx> 37 #include <rtl/bootstrap.hxx> 38 #include <unotools/pathoptions.hxx> 39 #include <tools/resid.hxx> 40 #include <tools/config.hxx> 41 #include <i18npool/mslangid.hxx> 42 #include <comphelper/processfactory.hxx> 43 #include <com/sun/star/container/XNameAccess.hpp> 44 #include <com/sun/star/lang/XComponent.hpp> 45 #include <com/sun/star/beans/NamedValue.hpp> 46 #include <com/sun/star/util/XChangesBatch.hpp> 47 #include <com/sun/star/uno/Any.hxx> 48 #include <com/sun/star/lang/XLocalizable.hpp> 49 #include <com/sun/star/lang/Locale.hpp> 50 #include "com/sun/star/util/XFlushable.hpp" 51 #include <rtl/locale.hxx> 52 #include <rtl/instance.hxx> 53 #include <osl/process.h> 54 #include <osl/file.hxx> 55 56 using namespace rtl; 57 using namespace com::sun::star::uno; 58 using namespace com::sun::star::lang; 59 using namespace com::sun::star::container; 60 using namespace com::sun::star::beans; 61 using namespace com::sun::star::util; 62 63 namespace desktop { 64 65 static char const SOFFICE_BOOTSTRAP[] = "Bootstrap"; 66 static char const SOFFICE_STARTLANG[] = "STARTLANG"; 67 68 sal_Bool LanguageSelection::bFoundLanguage = sal_False; 69 OUString LanguageSelection::aFoundLanguage; 70 LanguageSelection::LanguageSelectionStatus LanguageSelection::m_eStatus = LS_STATUS_OK; 71 72 const OUString LanguageSelection::usFallbackLanguage = OUString::createFromAscii("en-US"); 73 74 static sal_Bool existsURL( OUString const& sURL ) 75 { 76 using namespace osl; 77 DirectoryItem aDirItem; 78 79 if (sURL.getLength() != 0) 80 return ( DirectoryItem::get( sURL, aDirItem ) == DirectoryItem::E_None ); 81 82 return sal_False; 83 } 84 85 // locate soffice.ini/.rc file 86 static OUString locateSofficeIniFile() 87 { 88 OUString aUserDataPath; 89 OUString aSofficeIniFileURL; 90 91 // Retrieve the default file URL for the soffice.ini/rc 92 rtl::Bootstrap().getIniName( aSofficeIniFileURL ); 93 94 if ( utl::Bootstrap::locateUserData( aUserDataPath ) == utl::Bootstrap::PATH_EXISTS ) 95 { 96 const char CONFIG_DIR[] = "/config"; 97 98 sal_Int32 nIndex = aSofficeIniFileURL.lastIndexOf( '/'); 99 if ( nIndex > 0 ) 100 { 101 OUString aUserSofficeIniFileURL; 102 OUStringBuffer aBuffer( aUserDataPath ); 103 aBuffer.appendAscii( CONFIG_DIR ); 104 aBuffer.append( aSofficeIniFileURL.copy( nIndex )); 105 aUserSofficeIniFileURL = aBuffer.makeStringAndClear(); 106 107 if ( existsURL( aUserSofficeIniFileURL )) 108 return aUserSofficeIniFileURL; 109 } 110 } 111 // Fallback try to use the soffice.ini/rc from program folder 112 return aSofficeIniFileURL; 113 } 114 115 Locale LanguageSelection::IsoStringToLocale(const OUString& str) 116 { 117 Locale l; 118 sal_Int32 index=0; 119 l.Language = str.getToken(0, '-', index); 120 if (index >= 0) l.Country = str.getToken(0, '-', index); 121 if (index >= 0) l.Variant = str.getToken(0, '-', index); 122 return l; 123 } 124 125 bool LanguageSelection::prepareLanguage() 126 { 127 m_eStatus = LS_STATUS_OK; 128 OUString sConfigSrvc = OUString::createFromAscii("com.sun.star.configuration.ConfigurationProvider"); 129 Reference< XMultiServiceFactory > theMSF = comphelper::getProcessServiceFactory(); 130 Reference< XLocalizable > theConfigProvider; 131 try 132 { 133 theConfigProvider = Reference< XLocalizable >(theMSF->createInstance( sConfigSrvc ),UNO_QUERY_THROW ); 134 } 135 catch(const Exception&) 136 { 137 m_eStatus = LS_STATUS_CONFIGURATIONACCESS_BROKEN; 138 } 139 140 if(!theConfigProvider.is()) 141 return false; 142 143 sal_Bool bSuccess = sal_False; 144 145 // #i42730#get the windows 16Bit locale - it should be preferred over the UI language 146 try 147 { 148 Reference< XPropertySet > xProp(getConfigAccess("org.openoffice.System/L10N/", sal_False), UNO_QUERY_THROW); 149 Any aWin16SysLocale = xProp->getPropertyValue(OUString::createFromAscii("SystemLocale")); 150 ::rtl::OUString sWin16SysLocale; 151 aWin16SysLocale >>= sWin16SysLocale; 152 if( sWin16SysLocale.getLength()) 153 setDefaultLanguage(sWin16SysLocale); 154 } 155 catch(const Exception&) 156 { 157 m_eStatus = LS_STATUS_CONFIGURATIONACCESS_BROKEN; 158 } 159 160 // #i32939# use system locale to set document default locale 161 try 162 { 163 OUString usLocale; 164 Reference< XPropertySet > xLocaleProp(getConfigAccess( 165 "org.openoffice.System/L10N", sal_True), UNO_QUERY_THROW); 166 xLocaleProp->getPropertyValue(OUString::createFromAscii("Locale")) >>= usLocale; 167 setDefaultLanguage(usLocale); 168 } 169 catch (Exception&) 170 { 171 m_eStatus = LS_STATUS_CONFIGURATIONACCESS_BROKEN; 172 } 173 174 // get the selected UI language as string 175 bool bCmdLanguage( false ); 176 bool bIniLanguage( false ); 177 OUString aEmpty; 178 OUString aLocaleString = getUserUILanguage(); 179 180 if ( aLocaleString.getLength() == 0 ) 181 { 182 CommandLineArgs* pCmdLineArgs = Desktop::GetCommandLineArgs(); 183 if ( pCmdLineArgs ) 184 { 185 pCmdLineArgs->GetLanguage(aLocaleString); 186 if (isInstalledLanguage(aLocaleString, sal_False)) 187 { 188 bCmdLanguage = true; 189 bFoundLanguage = true; 190 aFoundLanguage = aLocaleString; 191 } 192 else 193 aLocaleString = aEmpty; 194 } 195 196 if ( !bCmdLanguage ) 197 { 198 OUString aSOfficeIniURL = locateSofficeIniFile(); 199 Config aConfig(aSOfficeIniURL); 200 aConfig.SetGroup( SOFFICE_BOOTSTRAP ); 201 OString sLang = aConfig.ReadKey( SOFFICE_STARTLANG ); 202 aLocaleString = OUString( sLang.getStr(), sLang.getLength(), RTL_TEXTENCODING_ASCII_US ); 203 if (isInstalledLanguage(aLocaleString, sal_False)) 204 { 205 bIniLanguage = true; 206 bFoundLanguage = true; 207 aFoundLanguage = aLocaleString; 208 } 209 else 210 aLocaleString = aEmpty; 211 } 212 } 213 214 // user further fallbacks for the UI language 215 if ( aLocaleString.getLength() == 0 ) 216 aLocaleString = getLanguageString(); 217 218 if ( aLocaleString.getLength() > 0 ) 219 { 220 try 221 { 222 // prepare default config provider by localizing it to the selected locale 223 // this will ensure localized configuration settings to be selected accoring to the 224 // UI language. 225 Locale loc = LanguageSelection::IsoStringToLocale(aLocaleString); 226 // flush any data already written to the configuration (which 227 // currently uses independent caches for different locales and thus 228 // would ignore data written to another cache): 229 Reference< XFlushable >(theConfigProvider, UNO_QUERY_THROW)-> 230 flush(); 231 theConfigProvider->setLocale(loc); 232 233 Reference< XPropertySet > xProp(getConfigAccess("org.openoffice.Setup/L10N/", sal_True), UNO_QUERY_THROW); 234 if ( !bCmdLanguage ) 235 { 236 // Store language only 237 xProp->setPropertyValue(OUString::createFromAscii("ooLocale"), makeAny(aLocaleString)); 238 Reference< XChangesBatch >(xProp, UNO_QUERY_THROW)->commitChanges(); 239 } 240 241 if ( bIniLanguage ) 242 { 243 // Store language only 244 Reference< XPropertySet > xProp2(getConfigAccess("org.openoffice.Office.Linguistic/General/", sal_True), UNO_QUERY_THROW); 245 xProp2->setPropertyValue(OUString::createFromAscii("UILocale"), makeAny(aLocaleString)); 246 Reference< XChangesBatch >(xProp2, UNO_QUERY_THROW)->commitChanges(); 247 } 248 249 MsLangId::setConfiguredSystemUILanguage( MsLangId::convertLocaleToLanguage(loc) ); 250 251 OUString sLocale; 252 xProp->getPropertyValue(OUString::createFromAscii("ooSetupSystemLocale")) >>= sLocale; 253 if ( sLocale.getLength() ) 254 { 255 loc = LanguageSelection::IsoStringToLocale(sLocale); 256 MsLangId::setConfiguredSystemLanguage( MsLangId::convertLocaleToLanguage(loc) ); 257 } 258 else 259 MsLangId::setConfiguredSystemLanguage( MsLangId::getSystemLanguage() ); 260 261 bSuccess = sal_True; 262 } 263 catch ( PropertyVetoException& ) 264 { 265 // we are not allowed to change this 266 } 267 catch (Exception& e) 268 { 269 OString aMsg = OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US); 270 OSL_ENSURE(sal_False, aMsg.getStr()); 271 272 } 273 } 274 275 // #i32939# setting of default document locale 276 // #i32939# this should not be based on the UI language 277 setDefaultLanguage(aLocaleString); 278 279 return bSuccess; 280 } 281 282 void LanguageSelection::setDefaultLanguage(const OUString& sLocale) 283 { 284 // #i32939# setting of default document language 285 // 286 // See #i42730# for rules for determining source of settings 287 288 // determine script type of locale 289 LanguageType nLang = MsLangId::convertIsoStringToLanguage(sLocale); 290 sal_uInt16 nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage(nLang); 291 292 switch (nScriptType) 293 { 294 case SCRIPTTYPE_ASIAN: 295 MsLangId::setConfiguredAsianFallback( nLang ); 296 break; 297 case SCRIPTTYPE_COMPLEX: 298 MsLangId::setConfiguredComplexFallback( nLang ); 299 break; 300 default: 301 MsLangId::setConfiguredWesternFallback( nLang ); 302 break; 303 } 304 } 305 306 OUString LanguageSelection::getUserUILanguage() 307 { 308 // check whether the user has selected a specific language 309 OUString aUserLanguage = getUserLanguage(); 310 if (aUserLanguage.getLength() > 0 ) 311 { 312 if (isInstalledLanguage(aUserLanguage)) 313 { 314 // all is well 315 bFoundLanguage = sal_True; 316 aFoundLanguage = aUserLanguage; 317 return aFoundLanguage; 318 } 319 else 320 { 321 // selected language is not/no longer installed 322 resetUserLanguage(); 323 } 324 } 325 326 return aUserLanguage; 327 } 328 329 OUString LanguageSelection::getLanguageString() 330 { 331 // did we already find a language? 332 if (bFoundLanguage) 333 return aFoundLanguage; 334 335 // check whether the user has selected a specific language 336 OUString aUserLanguage = getUserUILanguage(); 337 if (aUserLanguage.getLength() > 0 ) 338 return aUserLanguage ; 339 340 // try to use system default 341 aUserLanguage = getSystemLanguage(); 342 if (aUserLanguage.getLength() > 0 ) 343 { 344 if (isInstalledLanguage(aUserLanguage, sal_False)) 345 { 346 // great, system default language is available 347 bFoundLanguage = sal_True; 348 aFoundLanguage = aUserLanguage; 349 return aFoundLanguage; 350 } 351 } 352 // fallback 1: en-US 353 OUString usFB = usFallbackLanguage; 354 if (isInstalledLanguage(usFB)) 355 { 356 bFoundLanguage = sal_True; 357 aFoundLanguage = usFallbackLanguage; 358 return aFoundLanguage; 359 } 360 361 // fallback didn't work use first installed language 362 aUserLanguage = getFirstInstalledLanguage(); 363 364 bFoundLanguage = sal_True; 365 aFoundLanguage = aUserLanguage; 366 return aFoundLanguage; 367 } 368 369 Reference< XNameAccess > LanguageSelection::getConfigAccess(const sal_Char* pPath, sal_Bool bUpdate) 370 { 371 Reference< XNameAccess > xNameAccess; 372 try{ 373 OUString sConfigSrvc = OUString::createFromAscii("com.sun.star.configuration.ConfigurationProvider"); 374 OUString sAccessSrvc; 375 if (bUpdate) 376 sAccessSrvc = OUString::createFromAscii("com.sun.star.configuration.ConfigurationUpdateAccess"); 377 else 378 sAccessSrvc = OUString::createFromAscii("com.sun.star.configuration.ConfigurationAccess"); 379 380 OUString sConfigURL = OUString::createFromAscii(pPath); 381 382 // get configuration provider 383 Reference< XMultiServiceFactory > theMSF = comphelper::getProcessServiceFactory(); 384 if (theMSF.is()) { 385 Reference< XMultiServiceFactory > theConfigProvider = Reference< XMultiServiceFactory > ( 386 theMSF->createInstance( sConfigSrvc ),UNO_QUERY_THROW ); 387 388 // access the provider 389 Sequence< Any > theArgs(1); 390 theArgs[ 0 ] <<= sConfigURL; 391 xNameAccess = Reference< XNameAccess > ( 392 theConfigProvider->createInstanceWithArguments( 393 sAccessSrvc, theArgs ), UNO_QUERY_THROW ); 394 } 395 } catch (com::sun::star::uno::Exception& e) 396 { 397 OString aMsg = OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US); 398 OSL_ENSURE(sal_False, aMsg.getStr()); 399 } 400 return xNameAccess; 401 } 402 403 Sequence< OUString > LanguageSelection::getInstalledLanguages() 404 { 405 Sequence< OUString > seqLanguages; 406 Reference< XNameAccess > xAccess = getConfigAccess("org.openoffice.Setup/Office/InstalledLocales", sal_False); 407 if (!xAccess.is()) return seqLanguages; 408 seqLanguages = xAccess->getElementNames(); 409 return seqLanguages; 410 } 411 412 // FIXME 413 // it's not very clever to handle language fallbacks here, but 414 // right now, there is no place that handles those fallbacks globally 415 static Sequence< OUString > _getFallbackLocales(const OUString& aIsoLang) 416 { 417 Sequence< OUString > seqFallbacks; 418 if (aIsoLang.equalsAscii("zh-HK")) { 419 seqFallbacks = Sequence< OUString >(1); 420 seqFallbacks[0] = OUString::createFromAscii("zh-TW"); 421 } 422 return seqFallbacks; 423 } 424 425 sal_Bool LanguageSelection::isInstalledLanguage(OUString& usLocale, sal_Bool bExact) 426 { 427 sal_Bool bInstalled = sal_False; 428 Sequence< OUString > seqLanguages = getInstalledLanguages(); 429 for (sal_Int32 i=0; i<seqLanguages.getLength(); i++) 430 { 431 if (usLocale.equals(seqLanguages[i])) 432 { 433 bInstalled = sal_True; 434 break; 435 } 436 } 437 438 if (!bInstalled && !bExact) 439 { 440 // try fallback locales 441 Sequence< OUString > seqFallbacks = _getFallbackLocales(usLocale); 442 for (sal_Int32 j=0; j<seqFallbacks.getLength(); j++) 443 { 444 for (sal_Int32 i=0; i<seqLanguages.getLength(); i++) 445 { 446 if (seqFallbacks[j].equals(seqLanguages[i])) 447 { 448 bInstalled = sal_True; 449 usLocale = seqFallbacks[j]; 450 break; 451 } 452 } 453 } 454 } 455 456 if (!bInstalled && !bExact) 457 { 458 // no exact match was found, well try to find a substitute 459 OUString aInstalledLocale; 460 for (sal_Int32 i=0; i<seqLanguages.getLength(); i++) 461 { 462 if (usLocale.indexOf(seqLanguages[i]) == 0) 463 { 464 // requested locale starts with the installed locale 465 // (i.e. installed locale has index 0 in requested locale) 466 bInstalled = sal_True; 467 usLocale = seqLanguages[i]; 468 break; 469 } 470 } 471 } 472 return bInstalled; 473 } 474 475 OUString LanguageSelection::getFirstInstalledLanguage() 476 { 477 OUString aLanguage; 478 Sequence< OUString > seqLanguages = getInstalledLanguages(); 479 if (seqLanguages.getLength() > 0) 480 aLanguage = seqLanguages[0]; 481 return aLanguage; 482 } 483 484 OUString LanguageSelection::getUserLanguage() 485 { 486 OUString aUserLanguage; 487 Reference< XNameAccess > xAccess(getConfigAccess("org.openoffice.Office.Linguistic/General", sal_False)); 488 if (xAccess.is()) 489 { 490 try 491 { 492 xAccess->getByName(OUString::createFromAscii("UILocale")) >>= aUserLanguage; 493 } 494 catch ( NoSuchElementException const & ) 495 { 496 m_eStatus = LS_STATUS_CONFIGURATIONACCESS_BROKEN; 497 return OUString(); 498 } 499 catch ( WrappedTargetException const & ) 500 { 501 m_eStatus = LS_STATUS_CONFIGURATIONACCESS_BROKEN; 502 return OUString(); 503 } 504 } 505 return aUserLanguage; 506 } 507 508 OUString LanguageSelection::getSystemLanguage() 509 { 510 OUString aUserLanguage; 511 Reference< XNameAccess > xAccess(getConfigAccess("org.openoffice.System/L10N", sal_False)); 512 if (xAccess.is()) 513 { 514 try 515 { 516 xAccess->getByName(OUString::createFromAscii("UILocale")) >>= aUserLanguage; 517 } 518 catch ( NoSuchElementException const & ) 519 { 520 m_eStatus = LS_STATUS_CONFIGURATIONACCESS_BROKEN; 521 return OUString(); 522 } 523 catch ( WrappedTargetException const & ) 524 { 525 m_eStatus = LS_STATUS_CONFIGURATIONACCESS_BROKEN; 526 return OUString(); 527 } 528 } 529 return aUserLanguage; 530 } 531 532 533 void LanguageSelection::resetUserLanguage() 534 { 535 try 536 { 537 Reference< XPropertySet > xProp(getConfigAccess("org.openoffice.Office.Linguistic/General", sal_True), UNO_QUERY_THROW); 538 xProp->setPropertyValue(OUString::createFromAscii("UILocale"), makeAny(OUString::createFromAscii(""))); 539 Reference< XChangesBatch >(xProp, UNO_QUERY_THROW)->commitChanges(); 540 } 541 catch ( PropertyVetoException& ) 542 { 543 // we are not allowed to change this 544 } 545 catch ( Exception& e) 546 { 547 OString aMsg = OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US); 548 OSL_ENSURE(sal_False, aMsg.getStr()); 549 m_eStatus = LS_STATUS_CONFIGURATIONACCESS_BROKEN; 550 } 551 } 552 553 LanguageSelection::LanguageSelectionStatus LanguageSelection::getStatus() 554 { 555 return m_eStatus; 556 } 557 558 } // namespace desktop 559