1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
26 #include "localebackend.hxx"
27 #include <com/sun/star/beans/Optional.hpp>
28 #include <com/sun/star/uno/XComponentContext.hpp>
29 #include <cppuhelper/supportsservice.hxx>
30 #include <rtl/character.hxx>
31 #include <o3tl/char16_t2wchar_t.hxx>
32 #include <i18nlangtag/languagetag.hxx>
33 #include <i18nlangtag/mslangid.hxx>
36 #if !defined WIN32_LEAN_AND_MEAN
37 # define WIN32_LEAN_AND_MEAN
41 static css::beans::Optional
<css::uno::Any
> ImplGetLocale(LCID lcid
)
46 cp
+= GetLocaleInfoW( lcid
, LOCALE_SISO639LANGNAME
, buffer
, 4 );
49 if( 0 < GetLocaleInfoW( lcid
, LOCALE_SISO3166CTRYNAME
, cp
, buffer
+ 8 - cp
) )
50 // #i50822# minus character must be written before cp
53 return {true, css::uno::Any(OUString(o3tl::toU(buffer
)))};
61 #include <rtl/ustrbuf.hxx>
66 #include <CoreServices/CoreServices.h>
67 #include <CoreFoundation/CoreFoundation.h>
70 namespace /* private */
73 void OUStringBufferAppendCFString(OUStringBuffer
& buffer
, const CFStringRef s
)
75 CFIndex lstr
= CFStringGetLength(s
);
76 for (CFIndex i
= 0; i
< lstr
; i
++)
77 buffer
.append(sal_Unicode(CFStringGetCharacterAtIndex(s
, i
)));
84 explicit CFGuard(T
& rT
) : rT_(rT
) {}
85 ~CFGuard() { if (rT_
) CFRelease(rT_
); }
90 typedef CFGuard
<CFArrayRef
> CFArrayGuard
;
91 typedef CFGuard
<CFStringRef
> CFStringGuard
;
92 typedef CFGuard
<CFTypeRef
> CFTypeRefGuard
;
94 /* For more information on the Apple locale concept please refer to
95 http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFLocales/Articles/CFLocaleConcepts.html
96 According to this documentation a locale identifier has the format: language[_country][_variant]*
97 e.g. es_ES_PREEURO -> spain prior Euro support
98 Note: The calling code should be able to handle locales with only language information e.g. 'en' for certain
99 UI languages just the language code will be returned.
102 CFStringRef
ImplGetAppPreference(const char* pref
)
104 CFStringRef csPref
= CFStringCreateWithCString(nullptr, pref
, kCFStringEncodingASCII
);
105 CFStringGuard
csRefGuard(csPref
);
107 CFTypeRef ref
= CFPreferencesCopyAppValue(csPref
, kCFPreferencesCurrentApplication
);
108 CFTypeRefGuard
refGuard(ref
);
113 CFStringRef sref
= (CFGetTypeID(ref
) == CFArrayGetTypeID()) ? static_cast<CFStringRef
>(CFArrayGetValueAtIndex(static_cast<CFArrayRef
>(ref
), 0)) : static_cast<CFStringRef
>(ref
);
115 // NOTE: this API is only available with macOS >=10.3. We need to use it because
116 // Apple used non-ISO values on systems <10.2 like "German" for instance but didn't
117 // upgrade those values during upgrade to newer macOS versions. See also #i54337#
118 return CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault
, sref
);
121 css::beans::Optional
<css::uno::Any
> ImplGetLocale(const char* pref
)
123 CFStringRef sref
= ImplGetAppPreference(pref
);
124 CFStringGuard
srefGuard(sref
);
126 OUStringBuffer aLocaleBuffer
;
127 aLocaleBuffer
.append("en-US"); // initialize with fallback value
131 // split the string into substrings; the first two (if there are two) substrings
132 // are language and country
133 CFArrayRef subs
= CFStringCreateArrayBySeparatingStrings(nullptr, sref
, CFSTR("_"));
134 CFArrayGuard
subsGuard(subs
);
138 aLocaleBuffer
.setLength(0); // clear buffer which still contains fallback value
140 CFStringRef lang
= static_cast<CFStringRef
>(CFArrayGetValueAtIndex(subs
, 0));
141 OUStringBufferAppendCFString(aLocaleBuffer
, lang
);
143 // country also available? Assumption: if the array contains more than one
144 // value the second value is always the country!
145 if (CFArrayGetCount(subs
) > 1)
147 aLocaleBuffer
.append("-");
148 CFStringRef country
= static_cast<CFStringRef
>(CFArrayGetValueAtIndex(subs
, 1));
149 OUStringBufferAppendCFString(aLocaleBuffer
, country
);
153 return {true, css::uno::Any(aLocaleBuffer
.makeStringAndClear())};
156 } // namespace /* private */
160 #include <rtl/ustrbuf.hxx>
164 static css::beans::Optional
<css::uno::Any
> ImplGetLocale(char const * category
)
166 const char *locale
= std::getenv("LC_ALL");
167 if (locale
== nullptr || *locale
== '\0') {
168 locale
= std::getenv(category
);
169 if (locale
== nullptr || *locale
== '\0') {
170 locale
= std::getenv("LANG");
174 // Return "en-US" for C locales
175 if( (locale
== nullptr) || *locale
== '\0' || std::strcmp(locale
, "C") == 0
176 || std::strcmp(locale
, "POSIX") == 0 )
177 return {true, css::uno::Any(OUString("en-US"))};
181 const char *uscore
= nullptr;
182 const char *end
= nullptr;
184 // locale string have the format lang[_ctry][.encoding][@modifier]
185 // Let LanguageTag handle all conversion, but do a sanity and length check
187 // For the fallback we are only interested in the first two items, so we
188 // handle '.' and '@' as string end for that.
189 for (cp
= locale
; *cp
; cp
++)
191 if (*cp
== '_' && !uscore
)
193 if ((*cp
== '.' || *cp
== '@') && !end
)
195 if (!rtl::isAscii(static_cast<unsigned char>(*cp
))) {
196 SAL_INFO("shell", "locale env var with non-ASCII content");
200 assert(cp
>= locale
);
201 if (cp
- locale
> std::numeric_limits
<sal_Int32
>::max()) {
202 SAL_INFO("shell", "locale env var content too long");
206 // This is a tad awkward... but the easiest way to obtain what we're
207 // actually interested in. For example this also converts
208 // "ca_ES.UTF-8@valencia" to "ca-ES-valencia".
209 const OString
aLocaleStr(locale
);
210 const LanguageType nLang
= MsLangId::convertUnxByteStringToLanguage( aLocaleStr
);
211 if (nLang
!= LANGUAGE_DONTKNOW
)
213 const OUString
aLangTagStr( LanguageTag::convertToBcp47( nLang
));
214 return {true, css::uno::Any(aLangTagStr
)};
217 // As a fallback, strip encoding and modifier and return just a
218 // language-country combination and let the caller handle unknowns.
219 OUStringBuffer aLocaleBuffer
;
222 if( uscore
!= nullptr )
224 aLocaleBuffer
.appendAscii(locale
, uscore
++ - locale
);
225 aLocaleBuffer
.append("-");
226 aLocaleBuffer
.appendAscii(uscore
, end
- uscore
);
230 aLocaleBuffer
.appendAscii(locale
, end
- locale
);
233 return {true, css::uno::Any(aLocaleBuffer
.makeStringAndClear())};
239 LocaleBackend::LocaleBackend()
244 LocaleBackend::~LocaleBackend()
250 css::beans::Optional
<css::uno::Any
> LocaleBackend::getLocale()
253 return ImplGetLocale( GetUserDefaultLCID() );
254 #elif defined (MACOSX)
255 return ImplGetLocale("AppleLocale");
257 return ImplGetLocale("LC_CTYPE");
262 css::beans::Optional
<css::uno::Any
> LocaleBackend::getUILocale()
265 return ImplGetLocale( MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT
) );
266 #elif defined(MACOSX)
267 return ImplGetLocale("AppleLanguages");
269 return ImplGetLocale("LC_MESSAGES");
274 css::beans::Optional
<css::uno::Any
> LocaleBackend::getSystemLocale()
276 // note: the implementation differs from getLocale() only on Windows
278 return ImplGetLocale( GetSystemDefaultLCID() );
285 void LocaleBackend::setPropertyValue(
286 OUString
const &, css::uno::Any
const &)
288 throw css::lang::IllegalArgumentException(
289 "setPropertyValue not supported",
290 static_cast< cppu::OWeakObject
* >(this), -1);
293 css::uno::Any
LocaleBackend::getPropertyValue(
294 OUString
const & PropertyName
)
296 if ( PropertyName
== "Locale" ) {
297 return css::uno::Any(getLocale());
298 } else if (PropertyName
== "SystemLocale")
300 return css::uno::Any(getSystemLocale());
301 } else if (PropertyName
== "UILocale")
303 return css::uno::Any(getUILocale());
305 throw css::beans::UnknownPropertyException(
306 PropertyName
, static_cast< cppu::OWeakObject
* >(this));
311 OUString SAL_CALL
LocaleBackend::getImplementationName()
313 return "com.sun.star.comp.configuration.backend.LocaleBackend" ;
316 sal_Bool SAL_CALL
LocaleBackend::supportsService(const OUString
& aServiceName
)
318 return cppu::supportsService(this, aServiceName
);
321 uno::Sequence
<OUString
> SAL_CALL
LocaleBackend::getSupportedServiceNames()
323 return { "com.sun.star.configuration.backend.LocaleBackend" };
326 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
327 shell_LocaleBackend_get_implementation(
328 css::uno::XComponentContext
* , css::uno::Sequence
<css::uno::Any
> const&)
330 return cppu::acquire(new LocaleBackend());
333 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */