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("en-US"); // initialize with fallback value
130 // split the string into substrings; the first two (if there are two) substrings
131 // are language and country
132 CFArrayRef subs
= CFStringCreateArrayBySeparatingStrings(nullptr, sref
, CFSTR("_"));
133 CFArrayGuard
subsGuard(subs
);
137 aLocaleBuffer
.setLength(0); // clear buffer which still contains fallback value
139 CFStringRef lang
= static_cast<CFStringRef
>(CFArrayGetValueAtIndex(subs
, 0));
140 OUStringBufferAppendCFString(aLocaleBuffer
, lang
);
142 // country also available? Assumption: if the array contains more than one
143 // value the second value is always the country!
144 if (CFArrayGetCount(subs
) > 1)
146 aLocaleBuffer
.append("-");
147 CFStringRef country
= static_cast<CFStringRef
>(CFArrayGetValueAtIndex(subs
, 1));
148 OUStringBufferAppendCFString(aLocaleBuffer
, country
);
152 return {true, css::uno::Any(aLocaleBuffer
.makeStringAndClear())};
155 } // namespace /* private */
159 #include <rtl/ustrbuf.hxx>
163 static css::beans::Optional
<css::uno::Any
> ImplGetLocale(char const * category
)
165 const char *locale
= std::getenv("LC_ALL");
166 if (locale
== nullptr || *locale
== '\0') {
167 locale
= std::getenv(category
);
168 if (locale
== nullptr || *locale
== '\0') {
169 locale
= std::getenv("LANG");
173 // Return "en-US" for C locales
174 if( (locale
== nullptr) || *locale
== '\0' || std::strcmp(locale
, "C") == 0
175 || std::strcmp(locale
, "POSIX") == 0 )
176 return {true, css::uno::Any(u
"en-US"_ustr
)};
180 const char *uscore
= nullptr;
181 const char *end
= nullptr;
183 // locale string have the format lang[_ctry][.encoding][@modifier]
184 // Let LanguageTag handle all conversion, but do a sanity and length check
186 // For the fallback we are only interested in the first two items, so we
187 // handle '.' and '@' as string end for that.
188 for (cp
= locale
; *cp
; cp
++)
190 if (*cp
== '_' && !uscore
)
192 if ((*cp
== '.' || *cp
== '@') && !end
)
194 if (!rtl::isAscii(static_cast<unsigned char>(*cp
))) {
195 SAL_INFO("shell", "locale env var with non-ASCII content");
199 assert(cp
>= locale
);
200 if (cp
- locale
> std::numeric_limits
<sal_Int32
>::max()) {
201 SAL_INFO("shell", "locale env var content too long");
205 // This is a tad awkward... but the easiest way to obtain what we're
206 // actually interested in. For example this also converts
207 // "ca_ES.UTF-8@valencia" to "ca-ES-valencia".
208 const OString
aLocaleStr(locale
);
209 const LanguageType nLang
= MsLangId::convertUnxByteStringToLanguage( aLocaleStr
);
210 if (nLang
!= LANGUAGE_DONTKNOW
)
212 const OUString
aLangTagStr( LanguageTag::convertToBcp47( nLang
));
213 return {true, css::uno::Any(aLangTagStr
)};
216 // As a fallback, strip encoding and modifier and return just a
217 // language-country combination and let the caller handle unknowns.
218 OUStringBuffer aLocaleBuffer
;
221 if( uscore
!= nullptr )
223 aLocaleBuffer
.appendAscii(locale
, uscore
++ - locale
);
224 aLocaleBuffer
.append("-");
225 aLocaleBuffer
.appendAscii(uscore
, end
- uscore
);
229 aLocaleBuffer
.appendAscii(locale
, end
- locale
);
232 return {true, css::uno::Any(aLocaleBuffer
.makeStringAndClear())};
238 LocaleBackend::LocaleBackend()
243 LocaleBackend::~LocaleBackend()
249 css::beans::Optional
<css::uno::Any
> LocaleBackend::getLocale()
252 return ImplGetLocale( GetUserDefaultLCID() );
253 #elif defined (MACOSX)
254 return ImplGetLocale("AppleLocale");
256 return ImplGetLocale("LC_CTYPE");
261 css::beans::Optional
<css::uno::Any
> LocaleBackend::getUILocale()
264 return ImplGetLocale( MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT
) );
265 #elif defined(MACOSX)
266 return ImplGetLocale("AppleLanguages");
268 return ImplGetLocale("LC_MESSAGES");
273 css::beans::Optional
<css::uno::Any
> LocaleBackend::getSystemLocale()
275 // note: the implementation differs from getLocale() only on Windows
277 return ImplGetLocale( GetSystemDefaultLCID() );
284 void LocaleBackend::setPropertyValue(
285 OUString
const &, css::uno::Any
const &)
287 throw css::lang::IllegalArgumentException(
288 u
"setPropertyValue not supported"_ustr
,
292 css::uno::Any
LocaleBackend::getPropertyValue(
293 OUString
const & PropertyName
)
295 if ( PropertyName
== "Locale" ) {
296 return css::uno::Any(getLocale());
297 } else if (PropertyName
== "SystemLocale")
299 return css::uno::Any(getSystemLocale());
300 } else if (PropertyName
== "UILocale")
302 return css::uno::Any(getUILocale());
304 throw css::beans::UnknownPropertyException(
305 PropertyName
, getXWeak());
310 OUString SAL_CALL
LocaleBackend::getImplementationName()
312 return u
"com.sun.star.comp.configuration.backend.LocaleBackend"_ustr
;
315 sal_Bool SAL_CALL
LocaleBackend::supportsService(const OUString
& aServiceName
)
317 return cppu::supportsService(this, aServiceName
);
320 uno::Sequence
<OUString
> SAL_CALL
LocaleBackend::getSupportedServiceNames()
322 return { u
"com.sun.star.configuration.backend.LocaleBackend"_ustr
};
325 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
326 shell_LocaleBackend_get_implementation(
327 css::uno::XComponentContext
* , css::uno::Sequence
<css::uno::Any
> const&)
329 return cppu::acquire(new LocaleBackend());
332 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */