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