xref: /trunk/main/desktop/source/app/langselect.cxx (revision cdf0e10c)
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