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