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
existsURL(OUString const & sURL)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
locateSofficeIniFile()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
IsoStringToLocale(const OUString & str)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
prepareLanguage()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
setDefaultLanguage(const OUString & sLocale)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
getUserUILanguage()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
getLanguageString()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
getConfigAccess(const sal_Char * pPath,sal_Bool bUpdate)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
getInstalledLanguages()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
_getFallbackLocales(const OUString & aIsoLang)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
isInstalledLanguage(OUString & usLocale,sal_Bool bExact)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
getFirstInstalledLanguage()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
getUserLanguage()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
getSystemLanguage()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
resetUserLanguage()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
getStatus()549 LanguageSelection::LanguageSelectionStatus LanguageSelection::getStatus()
550 {
551 return m_eStatus;
552 }
553
554 } // namespace desktop
555