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