LibreOffice Module unotools (master) 1
localedatawrapper.cxx
Go to the documentation of this file.
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#include <limits>
21#include <stdio.h>
22#include <string>
23
24#include <sal/log.hxx>
28#include <tools/debug.hxx>
30#include <o3tl/safeint.hxx>
31
32#include <com/sun/star/i18n/KNumberFormatUsage.hpp>
33#include <com/sun/star/i18n/KNumberFormatType.hpp>
34#include <com/sun/star/i18n/LocaleData2.hpp>
35#include <com/sun/star/i18n/NumberFormatIndex.hpp>
36#include <com/sun/star/i18n/NumberFormatMapper.hpp>
37
40#include <rtl/ustrbuf.hxx>
41#include <rtl/math.hxx>
42#include <tools/date.hxx>
43#include <tools/time.hxx>
44#include <tools/duration.hxx>
45#include <o3tl/string_view.hxx>
46#include <utility>
47
48const sal_uInt16 nCurrFormatDefault = 0;
49
50using namespace ::com::sun::star;
51using namespace ::com::sun::star::i18n;
52using namespace ::com::sun::star::uno;
53
54namespace
55{
56 uno::Sequence< lang::Locale > gInstalledLocales;
57 std::vector< LanguageType > gInstalledLanguageTypes;
58}
59
61
63 const Reference< uno::XComponentContext > & rxContext,
64 LanguageTag aLanguageTag
65 )
66 :
67 m_xContext( rxContext ),
68 xLD( LocaleData2::create(rxContext) ),
69 maLanguageTag(std::move( aLanguageTag ))
70{
71 loadData();
72 loadDateAcceptancePatterns({});
73}
74
76 LanguageTag aLanguageTag,
77 const std::vector<OUString> & rOverrideDateAcceptancePatterns
78 )
79 :
81 xLD( LocaleData2::create(m_xContext) ),
82 maLanguageTag(std::move( aLanguageTag ))
83{
84 loadData();
85 loadDateAcceptancePatterns(rOverrideDateAcceptancePatterns);
86}
87
89{
90}
91
93{
94 return maLanguageTag;
95}
96
97const css::lang::Locale& LocaleDataWrapper::getMyLocale() const
98{
99 return maLanguageTag.getLocale();
100}
101
103{
104 const css::lang::Locale& rMyLocale = maLanguageTag.getLocale();
105
106 {
107 const Sequence< Currency2 > aCurrSeq = getAllCurrencies();
108 if ( !aCurrSeq.hasElements() )
109 {
110 if (areChecksEnabled())
111 outputCheckMessage("LocaleDataWrapper::getCurrSymbolsImpl: no currency at all, using ShellsAndPebbles");
112 aCurrSymbol = "ShellsAndPebbles";
115 nCurrDigits = 2;
116 }
117 else
118 {
119 auto pCurr = std::find_if(aCurrSeq.begin(), aCurrSeq.end(),
120 [](const Currency2& rCurr) { return rCurr.Default; });
121 if ( pCurr == aCurrSeq.end() )
122 {
123 if (areChecksEnabled())
124 {
125 outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrSymbolsImpl: no default currency" ) );
126 }
127 pCurr = aCurrSeq.begin();
128 }
129 aCurrSymbol = pCurr->Symbol;
130 aCurrBankSymbol = pCurr->BankSymbol;
131 nCurrDigits = pCurr->DecimalPlaces;
132 }
133 }
134
136
137 {
138 xDefaultCalendar.reset();
139 xSecondaryCalendar.reset();
140 const Sequence< Calendar2 > xCals = getAllCalendars();
141 if (xCals.getLength() > 1)
142 {
143 auto pCal = std::find_if(xCals.begin(), xCals.end(),
144 [](const Calendar2& rCal) { return !rCal.Default; });
145 if (pCal != xCals.end())
146 xSecondaryCalendar = std::make_shared<Calendar2>( *pCal);
147 }
148 auto pCal = xCals.begin();
149 if (xCals.getLength() > 1)
150 {
151 pCal = std::find_if(xCals.begin(), xCals.end(),
152 [](const Calendar2& rCal) { return rCal.Default; });
153 if (pCal == xCals.end())
154 pCal = xCals.begin();
155 }
156 xDefaultCalendar = std::make_shared<Calendar2>( *pCal);
157 }
158
160
161 try
162 {
163 aDateAcceptancePatterns = xLD->getDateAcceptancePatterns( rMyLocale );
164 }
165 catch (const Exception&)
166 {
167 TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDateAcceptancePatterns" );
169 }
170
171
173
174 try
175 {
176 aReservedWords = comphelper::sequenceToContainer<std::vector<OUString>>(xLD->getReservedWord( rMyLocale ));
177 }
178 catch ( const Exception& )
179 {
180 TOOLS_WARN_EXCEPTION( "unotools.i18n", "getReservedWord" );
181 }
182
183 try
184 {
185 aLocaleDataItem = xLD->getLocaleItem2( rMyLocale );
186 }
187 catch (const Exception&)
188 {
189 TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLocaleItem" );
190 static const css::i18n::LocaleDataItem2 aEmptyItem;
191 aLocaleDataItem = aEmptyItem;
192 }
193
194 aLocaleItem[LocaleItem::DATE_SEPARATOR] = aLocaleDataItem.dateSeparator;
195 aLocaleItem[LocaleItem::THOUSAND_SEPARATOR] = aLocaleDataItem.thousandSeparator;
196 aLocaleItem[LocaleItem::DECIMAL_SEPARATOR] = aLocaleDataItem.decimalSeparator;
197 aLocaleItem[LocaleItem::TIME_SEPARATOR] = aLocaleDataItem.timeSeparator;
198 aLocaleItem[LocaleItem::TIME_100SEC_SEPARATOR] = aLocaleDataItem.time100SecSeparator;
199 aLocaleItem[LocaleItem::LIST_SEPARATOR] = aLocaleDataItem.listSeparator;
200 aLocaleItem[LocaleItem::SINGLE_QUOTATION_START] = aLocaleDataItem.quotationStart;
201 aLocaleItem[LocaleItem::SINGLE_QUOTATION_END] = aLocaleDataItem.quotationEnd;
202 aLocaleItem[LocaleItem::DOUBLE_QUOTATION_START] = aLocaleDataItem.doubleQuotationStart;
203 aLocaleItem[LocaleItem::DOUBLE_QUOTATION_END] = aLocaleDataItem.doubleQuotationEnd;
204 aLocaleItem[LocaleItem::MEASUREMENT_SYSTEM] = aLocaleDataItem.measurementSystem;
205 aLocaleItem[LocaleItem::TIME_AM] = aLocaleDataItem.timeAM;
206 aLocaleItem[LocaleItem::TIME_PM] = aLocaleDataItem.timePM;
207 aLocaleItem[LocaleItem::LONG_DATE_DAY_OF_WEEK_SEPARATOR] = aLocaleDataItem.LongDateDayOfWeekSeparator;
208 aLocaleItem[LocaleItem::LONG_DATE_DAY_SEPARATOR] = aLocaleDataItem.LongDateDaySeparator;
209 aLocaleItem[LocaleItem::LONG_DATE_MONTH_SEPARATOR] = aLocaleDataItem.LongDateMonthSeparator;
210 aLocaleItem[LocaleItem::LONG_DATE_YEAR_SEPARATOR] = aLocaleDataItem.LongDateYearSeparator;
211 aLocaleItem[LocaleItem::DECIMAL_SEPARATOR_ALTERNATIVE] = aLocaleDataItem.decimalSeparatorAlternative;
212}
213
214/* FIXME-BCP47: locale data should provide a language tag instead that could be
215 * passed on. */
216css::i18n::LanguageCountryInfo LocaleDataWrapper::getLanguageCountryInfo() const
217{
218 try
219 {
220 return xLD->getLanguageCountryInfo( getMyLocale() );
221 }
222 catch (const Exception&)
223 {
224 TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLanguageCountryInfo" );
225 }
226 return css::i18n::LanguageCountryInfo();
227}
228
229const css::i18n::LocaleDataItem2& LocaleDataWrapper::getLocaleItem() const
230{
231 return aLocaleDataItem;
232}
233
234css::uno::Sequence< css::i18n::Currency2 > LocaleDataWrapper::getAllCurrencies() const
235{
236 try
237 {
238 return xLD->getAllCurrencies2( getMyLocale() );
239 }
240 catch (const Exception&)
241 {
242 TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllCurrencies" );
243 }
244 return {};
245}
246
247css::uno::Sequence< css::i18n::FormatElement > LocaleDataWrapper::getAllFormats() const
248{
249 try
250 {
251 return xLD->getAllFormats( getMyLocale() );
252 }
253 catch (const Exception&)
254 {
255 TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllFormats" );
256 }
257 return {};
258}
259
260css::i18n::ForbiddenCharacters LocaleDataWrapper::getForbiddenCharacters() const
261{
262 try
263 {
264 return xLD->getForbiddenCharacters( getMyLocale() );
265 }
266 catch (const Exception&)
267 {
268 TOOLS_WARN_EXCEPTION( "unotools.i18n", "getForbiddenCharacters" );
269 }
270 return css::i18n::ForbiddenCharacters();
271}
272
273const css::uno::Sequence< css::lang::Locale > & LocaleDataWrapper::getAllInstalledLocaleNames() const
274{
275 uno::Sequence< lang::Locale > &rInstalledLocales = gInstalledLocales;
276
277 if ( rInstalledLocales.hasElements() )
278 return rInstalledLocales;
279
280 try
281 {
282 rInstalledLocales = xLD->getAllInstalledLocaleNames();
283 }
284 catch ( const Exception& )
285 {
286 TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllInstalledLocaleNames" );
287 }
288 return rInstalledLocales;
289}
290
291// --- Impl and helpers ----------------------------------------------------
292
293// static
294const css::uno::Sequence< css::lang::Locale >& LocaleDataWrapper::getInstalledLocaleNames()
295{
296 const uno::Sequence< lang::Locale > &rInstalledLocales = gInstalledLocales;
297
298 if ( !rInstalledLocales.hasElements() )
299 {
300 LocaleDataWrapper aLDW( ::comphelper::getProcessComponentContext(), LanguageTag( LANGUAGE_SYSTEM) );
302 }
303 return rInstalledLocales;
304}
305
306// static
307const std::vector< LanguageType >& LocaleDataWrapper::getInstalledLanguageTypes()
308{
309 std::vector< LanguageType > &rInstalledLanguageTypes = gInstalledLanguageTypes;
310
311 if ( !rInstalledLanguageTypes.empty() )
312 return rInstalledLanguageTypes;
313
314 const css::uno::Sequence< css::lang::Locale > xLoc = getInstalledLocaleNames();
315 sal_Int32 nCount = xLoc.getLength();
316 std::vector< LanguageType > xLang;
317 xLang.reserve(nCount);
318 for ( const auto& rLoc : xLoc )
319 {
320 LanguageTag aLanguageTag( rLoc );
321 OUString aDebugLocale;
322 if (areChecksEnabled())
323 {
324 aDebugLocale = aLanguageTag.getBcp47( false);
325 }
326
327 LanguageType eLang = aLanguageTag.getLanguageType( false);
328 if (areChecksEnabled() && eLang == LANGUAGE_DONTKNOW)
329 {
330 OUString aMsg = "ConvertIsoNamesToLanguage: unknown MS-LCID for locale\n" +
331 aDebugLocale;
332 outputCheckMessage(aMsg);
333 }
334
335 if ( eLang == LANGUAGE_NORWEGIAN) // no_NO, not Bokmal (nb_NO), not Nynorsk (nn_NO)
336 eLang = LANGUAGE_DONTKNOW; // don't offer "Unknown" language
337 if ( eLang != LANGUAGE_DONTKNOW )
338 {
339 LanguageTag aBackLanguageTag( eLang);
340 if ( aLanguageTag != aBackLanguageTag )
341 {
342 // In checks, exclude known problems because no MS-LCID defined
343 // and default for Language found.
344 if ( areChecksEnabled()
345 && aDebugLocale != "ar-SD" // Sudan/ar
346 && aDebugLocale != "en-CB" // Caribbean is not a country
347// && aDebugLocale != "en-BG" // ?!? Bulgaria/en
348// && aDebugLocale != "es-BR" // ?!? Brazil/es
349 )
350 {
351 outputCheckMessage(Concat2View(
352 "ConvertIsoNamesToLanguage/ConvertLanguageToIsoNames: ambiguous locale (MS-LCID?)\n"
353 + aDebugLocale
354 + " -> 0x"
355 + OUString::number(static_cast<sal_Int32>(static_cast<sal_uInt16>(eLang)), 16)
356 + " -> "
357 + aBackLanguageTag.getBcp47() ));
358 }
359 eLang = LANGUAGE_DONTKNOW;
360 }
361 }
362 if ( eLang != LANGUAGE_DONTKNOW )
363 xLang.push_back(eLang);
364 }
365 rInstalledLanguageTypes = xLang;
366
367 return rInstalledLanguageTypes;
368}
369
370const OUString& LocaleDataWrapper::getOneLocaleItem( sal_Int16 nItem ) const
371{
372 if ( nItem >= LocaleItem::COUNT2 )
373 {
374 SAL_WARN( "unotools.i18n", "getOneLocaleItem: bounds" );
375 return aLocaleItem[0];
376 }
377 return aLocaleItem[nItem];
378}
379
380const OUString& LocaleDataWrapper::getOneReservedWord( sal_Int16 nWord ) const
381{
382 if ( nWord < 0 || o3tl::make_unsigned(nWord) >= aReservedWords.size() )
383 {
384 SAL_WARN( "unotools.i18n", "getOneReservedWord: bounds" );
385 static const OUString EMPTY;
386 return EMPTY;
387 }
388 return aReservedWords[nWord];
389}
390
392{
394 if ( o3tl::equalsIgnoreAsciiCase( rMS, u"metric" ) )
398}
399
400bool LocaleDataWrapper::doesSecondaryCalendarUseEC( std::u16string_view rName ) const
401{
402 if (rName.empty())
403 return false;
404
405 // Check language tag first to avoid loading all calendars of this locale.
407 const OUString& aBcp47( aLoaded.getBcp47());
408 // So far determine only by locale, we know for a few.
409 /* TODO: check date format codes? or add to locale data? */
410 if ( aBcp47 != "ja-JP" &&
411 aBcp47 != "lo-LA" &&
412 aBcp47 != "zh-TW")
413 return false;
414
416 return false;
417 if (!xSecondaryCalendar->Name.equalsIgnoreAsciiCase( rName))
418 return false;
419
420 return true;
421}
422
423const std::shared_ptr< css::i18n::Calendar2 >& LocaleDataWrapper::getDefaultCalendar() const
424{
425 return xDefaultCalendar;
426}
427
428css::uno::Sequence< css::i18n::CalendarItem2 > const & LocaleDataWrapper::getDefaultCalendarDays() const
429{
430 return getDefaultCalendar()->Days;
431}
432
433css::uno::Sequence< css::i18n::CalendarItem2 > const & LocaleDataWrapper::getDefaultCalendarMonths() const
434{
435 return getDefaultCalendar()->Months;
436}
437
438// --- currencies -----------------------------------------------------
439
440const OUString& LocaleDataWrapper::getCurrSymbol() const
441{
442 return aCurrSymbol;
443}
444
446{
447 return aCurrBankSymbol;
448}
449
451{
452 return nCurrPositiveFormat;
453}
454
456{
457 return nCurrNegativeFormat;
458}
459
461{
462 return nCurrDigits;
463}
464
465void LocaleDataWrapper::scanCurrFormatImpl( std::u16string_view rCode,
466 sal_Int32 nStart, sal_Int32& nSign, sal_Int32& nPar,
467 sal_Int32& nNum, sal_Int32& nBlank, sal_Int32& nSym ) const
468{
469 nSign = nPar = nNum = nBlank = nSym = -1;
470 const sal_Unicode* const pStr = rCode.data();
471 const sal_Unicode* const pStop = pStr + rCode.size();
472 const sal_Unicode* p = pStr + nStart;
473 int nInSection = 0;
474 bool bQuote = false;
475 while ( p < pStop )
476 {
477 if ( bQuote )
478 {
479 if ( *p == '"' && *(p-1) != '\\' )
480 bQuote = false;
481 }
482 else
483 {
484 switch ( *p )
485 {
486 case '"' :
487 if ( pStr == p || *(p-1) != '\\' )
488 bQuote = true;
489 break;
490 case '-' :
491 if (!nInSection && nSign == -1)
492 nSign = p - pStr;
493 break;
494 case '(' :
495 if (!nInSection && nPar == -1)
496 nPar = p - pStr;
497 break;
498 case '0' :
499 case '#' :
500 if (!nInSection && nNum == -1)
501 nNum = p - pStr;
502 break;
503 case '[' :
504 nInSection++;
505 break;
506 case ']' :
507 if ( nInSection )
508 {
509 nInSection--;
510 if (!nInSection && nBlank == -1
511 && nSym != -1 && p < pStop-1 && *(p+1) == ' ' )
512 nBlank = p - pStr + 1;
513 }
514 break;
515 case '$' :
516 if (nSym == -1 && nInSection && *(p-1) == '[')
517 {
518 nSym = p - pStr + 1;
519 if (nNum != -1 && *(p-2) == ' ')
520 nBlank = p - pStr - 2;
521 }
522 break;
523 case ';' :
524 if ( !nInSection )
525 p = pStop;
526 break;
527 default:
528 if (!nInSection && nSym == -1 && o3tl::starts_with(rCode.substr(static_cast<sal_Int32>(p - pStr)), aCurrSymbol))
529 { // currency symbol not surrounded by [$...]
530 nSym = p - pStr;
531 if (nBlank == -1 && pStr < p && *(p-1) == ' ')
532 nBlank = p - pStr - 1;
533 p += aCurrSymbol.getLength() - 1;
534 if (nBlank == -1 && p < pStop-2 && *(p+2) == ' ')
535 nBlank = p - pStr + 2;
536 }
537 }
538 }
539 p++;
540 }
541}
542
544{
545 css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext );
546 uno::Sequence< NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( KNumberFormatUsage::CURRENCY, maLanguageTag.getLocale() );
547 sal_Int32 nCnt = aFormatSeq.getLength();
548 if ( !nCnt )
549 { // bad luck
550 if (areChecksEnabled())
551 {
552 outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrFormatsImpl: no currency formats" ) );
553 }
555 return;
556 }
557 // find a negative code (medium preferred) and a default (medium preferred) (not necessarily the same)
558 NumberFormatCode const * const pFormatArr = aFormatSeq.getArray();
559 sal_Int32 nElem, nDef, nNeg, nMedium;
560 nDef = nNeg = nMedium = -1;
561 for ( nElem = 0; nElem < nCnt; nElem++ )
562 {
563 if ( pFormatArr[nElem].Type == KNumberFormatType::MEDIUM )
564 {
565 if ( pFormatArr[nElem].Default )
566 {
567 nDef = nElem;
568 nMedium = nElem;
569 if ( pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
570 nNeg = nElem;
571 }
572 else
573 {
574 if ( (nNeg == -1 || nMedium == -1) && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
575 nNeg = nElem;
576 if ( nMedium == -1 )
577 nMedium = nElem;
578 }
579 }
580 else
581 {
582 if ( nDef == -1 && pFormatArr[nElem].Default )
583 nDef = nElem;
584 if ( nNeg == -1 && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
585 nNeg = nElem;
586 }
587 }
588
589 sal_Int32 nSign, nPar, nNum, nBlank, nSym;
590
591 // positive format
592 nElem = (nDef >= 0 ? nDef : (nNeg >= 0 ? nNeg : 0));
593 scanCurrFormatImpl( pFormatArr[nElem].Code, 0, nSign, nPar, nNum, nBlank, nSym );
594 if (areChecksEnabled() && (nNum == -1 || nSym == -1))
595 {
596 outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrFormatsImpl: CurrPositiveFormat?" ) );
597 }
598 if (nBlank == -1)
599 {
600 if ( nSym < nNum )
601 nCurrPositiveFormat = 0; // $1
602 else
603 nCurrPositiveFormat = 1; // 1$
604 }
605 else
606 {
607 if ( nSym < nNum )
608 nCurrPositiveFormat = 2; // $ 1
609 else
610 nCurrPositiveFormat = 3; // 1 $
611 }
612
613 // negative format
614 if ( nNeg < 0 )
616 else
617 {
618 const OUString& rCode = pFormatArr[nNeg].Code;
619 sal_Int32 nDelim = rCode.indexOf(';');
620 scanCurrFormatImpl( rCode, nDelim+1, nSign, nPar, nNum, nBlank, nSym );
621 if (areChecksEnabled() && (nNum == -1 || nSym == -1 || (nPar == -1 && nSign == -1)))
622 {
623 outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrFormatsImpl: CurrNegativeFormat?" ) );
624 }
625 // NOTE: one of nPar or nSign are allowed to be -1
626 if (nBlank == -1)
627 {
628 if ( nSym < nNum )
629 {
630 if ( -1 < nPar && nPar < nSym )
631 nCurrNegativeFormat = 0; // ($1)
632 else if ( -1 < nSign && nSign < nSym )
633 nCurrNegativeFormat = 1; // -$1
634 else if ( nNum < nSign )
635 nCurrNegativeFormat = 3; // $1-
636 else
637 nCurrNegativeFormat = 2; // $-1
638 }
639 else
640 {
641 if ( -1 < nPar && nPar < nNum )
642 nCurrNegativeFormat = 4; // (1$)
643 else if ( -1 < nSign && nSign < nNum )
644 nCurrNegativeFormat = 5; // -1$
645 else if ( nSym < nSign )
646 nCurrNegativeFormat = 7; // 1$-
647 else
648 nCurrNegativeFormat = 6; // 1-$
649 }
650 }
651 else
652 {
653 if ( nSym < nNum )
654 {
655 if ( -1 < nPar && nPar < nSym )
656 nCurrNegativeFormat = 14; // ($ 1)
657 else if ( -1 < nSign && nSign < nSym )
658 nCurrNegativeFormat = 9; // -$ 1
659 else if ( nNum < nSign )
660 nCurrNegativeFormat = 12; // $ 1-
661 else
662 nCurrNegativeFormat = 11; // $ -1
663 }
664 else
665 {
666 if ( -1 < nPar && nPar < nNum )
667 nCurrNegativeFormat = 15; // (1 $)
668 else if ( -1 < nSign && nSign < nNum )
669 nCurrNegativeFormat = 8; // -1 $
670 else if ( nSym < nSign )
671 nCurrNegativeFormat = 10; // 1 $-
672 else
673 nCurrNegativeFormat = 13; // 1- $
674 }
675 }
676 }
677}
678
679// --- date -----------------------------------------------------------
680
682{
683 return nDateOrder;
684}
685
687{
688 return nLongDateOrder;
689}
690
691LongDateOrder LocaleDataWrapper::scanDateOrderImpl( std::u16string_view rCode ) const
692{
693 // Only some european versions were translated, the ones with different
694 // keyword combinations are:
695 // English DMY, German TMJ, Spanish DMA, French JMA, Italian GMA,
696 // Dutch DMJ, Finnish PKV
697
698 // default is English keywords for every other language
699 size_t nDay = rCode.find('D');
700 size_t nMonth = rCode.find('M');
701 size_t nYear = rCode.find('Y');
702 if (nDay == std::u16string_view::npos || nMonth == std::u16string_view::npos || nYear == std::u16string_view::npos)
703 { // This algorithm assumes that all three parts (DMY) are present
704 if (nMonth == std::u16string_view::npos)
705 { // only Finnish has something else than 'M' for month
706 nMonth = rCode.find('K');
707 if (nMonth != std::u16string_view::npos)
708 {
709 nDay = rCode.find('P');
710 nYear = rCode.find('V');
711 }
712 }
713 else if (nDay == std::u16string_view::npos)
714 { // We have a month 'M' if we reach this branch.
715 // Possible languages containing 'M' but no 'D':
716 // German, French, Italian
717 nDay = rCode.find('T'); // German
718 if (nDay != std::u16string_view::npos)
719 nYear = rCode.find('J');
720 else
721 {
722 nYear = rCode.find('A'); // French, Italian
723 if (nYear != std::u16string_view::npos)
724 {
725 nDay = rCode.find('J'); // French
726 if (nDay == std::u16string_view::npos)
727 nDay = rCode.find('G'); // Italian
728 }
729 }
730 }
731 else
732 { // We have a month 'M' and a day 'D'.
733 // Possible languages containing 'D' and 'M' but not 'Y':
734 // Spanish, Dutch
735 nYear = rCode.find('A'); // Spanish
736 if (nYear == std::u16string_view::npos)
737 nYear = rCode.find('J'); // Dutch
738 }
739 if (nDay == std::u16string_view::npos || nMonth == std::u16string_view::npos || nYear == std::u16string_view::npos)
740 {
741 if (areChecksEnabled())
742 {
743 outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::scanDateOrder: not all DMY present" ) );
744 }
745 if (nDay == std::u16string_view::npos)
746 nDay = rCode.size();
747 if (nMonth == std::u16string_view::npos)
748 nMonth = rCode.size();
749 if (nYear == std::u16string_view::npos)
750 nYear = rCode.size();
751 }
752 }
753 // compare with <= because each position may equal rCode.getLength()
754 if ( nDay <= nMonth && nMonth <= nYear )
755 return LongDateOrder::DMY; // also if every position equals rCode.getLength()
756 else if ( nMonth <= nDay && nDay <= nYear )
757 return LongDateOrder::MDY;
758 else if ( nYear <= nMonth && nMonth <= nDay )
759 return LongDateOrder::YMD;
760 else if ( nYear <= nDay && nDay <= nMonth )
761 return LongDateOrder::YDM;
762 else
763 {
764 if (areChecksEnabled())
765 {
766 outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::scanDateOrder: no magic applicable" ) );
767 }
768 return LongDateOrder::DMY;
769 }
770}
771
773{
774 switch (eLong)
775 {
777 return DateOrder::YMD;
778 break;
780 return DateOrder::DMY;
781 break;
783 return DateOrder::MDY;
784 break;
786 default:
787 assert(!"unhandled LongDateOrder to DateOrder");
788 return DateOrder::DMY;
789 }
790}
791
793{
794 css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext );
795 uno::Sequence< NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( KNumberFormatUsage::DATE, maLanguageTag.getLocale() );
796 sal_Int32 nCnt = aFormatSeq.getLength();
797 if ( !nCnt )
798 { // bad luck
799 if (areChecksEnabled())
800 {
801 outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no date formats" ) );
802 }
805 return;
806 }
807 // find the edit (21), a default (medium preferred),
808 // a medium (default preferred), and a long (default preferred)
809 NumberFormatCode const * const pFormatArr = aFormatSeq.getArray();
810 sal_Int32 nEdit, nDef, nMedium, nLong;
811 nEdit = nDef = nMedium = nLong = -1;
812 for ( sal_Int32 nElem = 0; nElem < nCnt; nElem++ )
813 {
814 if ( nEdit == -1 && pFormatArr[nElem].Index == NumberFormatIndex::DATE_SYS_DDMMYYYY )
815 nEdit = nElem;
816 if ( nDef == -1 && pFormatArr[nElem].Default )
817 nDef = nElem;
818 switch ( pFormatArr[nElem].Type )
819 {
820 case KNumberFormatType::MEDIUM :
821 {
822 if ( pFormatArr[nElem].Default )
823 {
824 nDef = nElem;
825 nMedium = nElem;
826 }
827 else if ( nMedium == -1 )
828 nMedium = nElem;
829 }
830 break;
831 case KNumberFormatType::LONG :
832 {
833 if ( pFormatArr[nElem].Default )
834 nLong = nElem;
835 else if ( nLong == -1 )
836 nLong = nElem;
837 }
838 break;
839 }
840 }
841 if ( nEdit == -1 )
842 {
843 if (areChecksEnabled())
844 {
845 outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no edit" ) );
846 }
847 if ( nDef == -1 )
848 {
849 if (areChecksEnabled())
850 {
851 outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no default" ) );
852 }
853 if ( nMedium != -1 )
854 nDef = nMedium;
855 else if ( nLong != -1 )
856 nDef = nLong;
857 else
858 nDef = 0;
859 }
860 nEdit = nDef;
861 }
862 LongDateOrder nDO = scanDateOrderImpl( pFormatArr[nEdit].Code );
863 if ( pFormatArr[nEdit].Type == KNumberFormatType::LONG )
864 { // normally this is not the case
865 nLongDateOrder = nDO;
867 }
868 else
869 {
870 // YDM should not occur in a short/medium date (i.e. no locale has
871 // that) and is nowhere handled.
873 if ( nLong == -1 )
874 nLongDateOrder = nDO;
875 else
876 nLongDateOrder = scanDateOrderImpl( pFormatArr[nLong].Code );
877 }
878}
879
880// --- digit grouping -------------------------------------------------
881
883{
884 /* TODO: This is a very simplified grouping setup that only serves its
885 * current purpose for Indian locales. A free-form flexible one would
886 * obtain grouping from locale data where it could be specified using, for
887 * example, codes like #,### and #,##,### that would generate the integer
888 * sequence. Needed additional API and a locale data element.
889 */
890
891 if (aGrouping.hasElements() && aGrouping[0])
892 return;
893
894 i18n::LanguageCountryInfo aLCInfo( getLanguageCountryInfo());
895 if (aLCInfo.Country.equalsIgnoreAsciiCase("IN") || // India
896 aLCInfo.Country.equalsIgnoreAsciiCase("BT") ) // Bhutan
897 {
898 aGrouping = { 3, 2, 0 };
899 }
900 else
901 {
902 aGrouping = { 3, 0, 0 };
903 }
904}
905
906const css::uno::Sequence< sal_Int32 >& LocaleDataWrapper::getDigitGrouping() const
907{
908 return aGrouping;
909}
910
911// --- simple number formatting helpers -------------------------------
912
913// The ImplAdd... methods are taken from class International and modified to
914// suit the needs.
915
916static void ImplAddUNum( OUStringBuffer& rBuf, sal_uInt64 nNumber )
917{
918 // fill temp buffer with digits
919 sal_Unicode aTempBuf[64];
920 sal_Unicode* pTempBuf = aTempBuf;
921 do
922 {
923 *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0';
924 pTempBuf++;
925 nNumber /= 10;
926 }
927 while ( nNumber );
928
929 // copy temp buffer to buffer passed
930 do
931 {
932 pTempBuf--;
933 rBuf.append(*pTempBuf);
934 }
935 while ( pTempBuf != aTempBuf );
936}
937
938static void ImplAddUNum( OUStringBuffer& rBuf, sal_uInt64 nNumber, int nMinLen )
939{
940 // fill temp buffer with digits
941 sal_Unicode aTempBuf[64];
942 sal_Unicode* pTempBuf = aTempBuf;
943 do
944 {
945 *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0';
946 pTempBuf++;
947 nNumber /= 10;
948 nMinLen--;
949 }
950 while ( nNumber );
951
952 // fill with zeros up to the minimal length
953 while ( nMinLen > 0 )
954 {
955 rBuf.append('0');
956 nMinLen--;
957 }
958
959 // copy temp buffer to real buffer
960 do
961 {
962 pTempBuf--;
963 rBuf.append(*pTempBuf);
964 }
965 while ( pTempBuf != aTempBuf );
966}
967
968static void ImplAddNum( OUStringBuffer& rBuf, sal_Int64 nNumber, int nMinLen )
969{
970 if (nNumber < 0)
971 {
972 rBuf.append('-');
973 nNumber = -nNumber;
974 }
975 return ImplAddUNum( rBuf, nNumber, nMinLen);
976}
977
978static void ImplAdd2UNum( OUStringBuffer& rBuf, sal_uInt16 nNumber )
979{
980 DBG_ASSERT( nNumber < 100, "ImplAdd2UNum() - Number >= 100" );
981
982 if ( nNumber < 10 )
983 {
984 rBuf.append('0');
985 rBuf.append(static_cast<char>(nNumber + '0'));
986 }
987 else
988 {
989 sal_uInt16 nTemp = nNumber % 10;
990 nNumber /= 10;
991 rBuf.append(static_cast<char>(nNumber + '0'));
992 rBuf.append(static_cast<char>(nTemp + '0'));
993 }
994}
995
996static void ImplAdd9UNum( OUStringBuffer& rBuf, sal_uInt32 nNumber )
997{
998 DBG_ASSERT( nNumber < 1000000000, "ImplAdd9UNum() - Number >= 1000000000" );
999
1000 std::ostringstream ostr;
1001 ostr.fill('0');
1002 ostr.width(9);
1003 ostr << nNumber;
1004 std::string aStr = ostr.str();
1005 rBuf.appendAscii(aStr.c_str(), aStr.size());
1006}
1007
1008void LocaleDataWrapper::ImplAddFormatNum( OUStringBuffer& rBuf,
1009 sal_Int64 nNumber, sal_uInt16 nDecimals, bool bUseThousandSep,
1010 bool bTrailingZeros ) const
1011{
1012 OUStringBuffer aNumBuf(64);
1013 sal_uInt16 nNumLen;
1014
1015 // negative number
1016 sal_uInt64 abs;
1017 if ( nNumber < 0 )
1018 {
1019 // Avoid overflow, map -2^63 -> 2^63 explicitly:
1020 abs = nNumber == std::numeric_limits<sal_Int64>::min()
1021 ? static_cast<sal_uInt64>(std::numeric_limits<sal_Int64>::min()) : nNumber * -1;
1022 rBuf.append('-');
1023 }
1024 else
1025 {
1026 abs = nNumber;
1027 }
1028
1029 // convert number
1030 ImplAddUNum( aNumBuf, abs );
1031 nNumLen = static_cast<sal_uInt16>(aNumBuf.getLength());
1032
1033 if ( nNumLen <= nDecimals )
1034 {
1035 // strip .0 in decimals?
1036 if ( !nNumber && !bTrailingZeros )
1037 {
1038 rBuf.append('0');
1039 }
1040 else
1041 {
1042 // LeadingZero, insert 0
1043 if ( isNumLeadingZero() )
1044 {
1045 rBuf.append('0');
1046 }
1047
1048 // append decimal separator
1049 rBuf.append( aLocaleDataItem.decimalSeparator );
1050
1051 // fill with zeros
1052 sal_uInt16 i = 0;
1053 while ( i < (nDecimals-nNumLen) )
1054 {
1055 rBuf.append('0');
1056 i++;
1057 }
1058
1059 // append decimals
1060 rBuf.append(aNumBuf);
1061 }
1062 }
1063 else
1064 {
1065 const OUString& rThoSep = aLocaleDataItem.thousandSeparator;
1066
1067 // copy number to buffer (excluding decimals)
1068 sal_uInt16 nNumLen2 = nNumLen-nDecimals;
1069 uno::Sequence< sal_Bool > aGroupPos;
1070 if (bUseThousandSep)
1072 nNumLen2, getDigitGrouping());
1073 sal_uInt16 i = 0;
1074 for (; i < nNumLen2; ++i )
1075 {
1076 rBuf.append(aNumBuf[i]);
1077
1078 // add thousand separator?
1079 if ( bUseThousandSep && aGroupPos[i] )
1080 rBuf.append( rThoSep );
1081 }
1082
1083 // append decimals
1084 if ( nDecimals )
1085 {
1086 rBuf.append( aLocaleDataItem.decimalSeparator );
1087
1088 bool bNullEnd = true;
1089 while ( i < nNumLen )
1090 {
1091 if ( aNumBuf[i] != '0' )
1092 bNullEnd = false;
1093
1094 rBuf.append(aNumBuf[i]);
1095 i++;
1096 }
1097
1098 // strip .0 in decimals?
1099 if ( bNullEnd && !bTrailingZeros )
1100 rBuf.setLength( rBuf.getLength() - (nDecimals + 1) );
1101 }
1102 }
1103}
1104
1105// --- simple date and time formatting --------------------------------
1106
1107OUString LocaleDataWrapper::getDate( const Date& rDate ) const
1108{
1110 OUStringBuffer aBuf(128);
1111 sal_uInt16 nDay = rDate.GetDay();
1112 sal_uInt16 nMonth = rDate.GetMonth();
1113 sal_Int16 nYear = rDate.GetYear();
1114 sal_uInt16 nYearLen;
1115
1116 if ( (true) /* IsDateCentury() */ )
1117 nYearLen = 4;
1118 else
1119 {
1120 nYearLen = 2;
1121 nYear %= 100;
1122 }
1123
1124 switch ( getDateOrder() )
1125 {
1126 case DateOrder::DMY :
1127 ImplAdd2UNum( aBuf, nDay );
1128 aBuf.append( aLocaleDataItem.dateSeparator );
1129 ImplAdd2UNum( aBuf, nMonth );
1130 aBuf.append( aLocaleDataItem.dateSeparator );
1131 ImplAddNum( aBuf, nYear, nYearLen );
1132 break;
1133 case DateOrder::MDY :
1134 ImplAdd2UNum( aBuf, nMonth );
1135 aBuf.append( aLocaleDataItem.dateSeparator );
1136 ImplAdd2UNum( aBuf, nDay );
1137 aBuf.append( aLocaleDataItem.dateSeparator );
1138 ImplAddNum( aBuf, nYear, nYearLen );
1139 break;
1140 default:
1141 ImplAddNum( aBuf, nYear, nYearLen );
1142 aBuf.append( aLocaleDataItem.dateSeparator );
1143 ImplAdd2UNum( aBuf, nMonth );
1144 aBuf.append( aLocaleDataItem.dateSeparator );
1145 ImplAdd2UNum( aBuf, nDay );
1146 }
1147
1148 return aBuf.makeStringAndClear();
1149}
1150
1151OUString LocaleDataWrapper::getTime( const tools::Time& rTime, bool bSec, bool b100Sec ) const
1152{
1154 OUStringBuffer aBuf(128);
1155 sal_uInt16 nHour = rTime.GetHour();
1156
1157 nHour %= 24;
1158
1159 ImplAdd2UNum( aBuf, nHour );
1160 aBuf.append( aLocaleDataItem.timeSeparator );
1161 ImplAdd2UNum( aBuf, rTime.GetMin() );
1162 if ( bSec )
1163 {
1164 aBuf.append( aLocaleDataItem.timeSeparator );
1165 ImplAdd2UNum( aBuf, rTime.GetSec() );
1166
1167 if ( b100Sec )
1168 {
1169 aBuf.append( aLocaleDataItem.time100SecSeparator );
1170 ImplAdd9UNum( aBuf, rTime.GetNanoSec() );
1171 }
1172 }
1173
1174 return aBuf.makeStringAndClear();
1175}
1176
1177OUString LocaleDataWrapper::getDuration( const tools::Duration& rDuration, bool bSec, bool b100Sec ) const
1178{
1179 OUStringBuffer aBuf(128);
1180
1181 if ( rDuration.IsNegative() )
1182 aBuf.append(' ');
1183
1184 sal_Int64 nHours = static_cast<sal_Int64>(rDuration.GetDays()) * 24 +
1185 (rDuration.IsNegative() ?
1186 -static_cast<sal_Int64>(rDuration.GetTime().GetHour()) :
1187 rDuration.GetTime().GetHour());
1188 if ( (true) /* IsTimeLeadingZero() */ )
1189 ImplAddNum( aBuf, nHours, 2 );
1190 else
1191 ImplAddNum( aBuf, nHours, 1 );
1192 aBuf.append( aLocaleDataItem.timeSeparator );
1193 ImplAdd2UNum( aBuf, rDuration.GetTime().GetMin() );
1194 if ( bSec )
1195 {
1196 aBuf.append( aLocaleDataItem.timeSeparator );
1197 ImplAdd2UNum( aBuf, rDuration.GetTime().GetSec() );
1198
1199 if ( b100Sec )
1200 {
1201 aBuf.append( aLocaleDataItem.time100SecSeparator );
1202 ImplAdd9UNum( aBuf, rDuration.GetTime().GetNanoSec() );
1203 }
1204 }
1205
1206 return aBuf.makeStringAndClear();
1207}
1208
1209// --- simple number formatting ---------------------------------------
1210
1211static size_t ImplGetNumberStringLengthGuess( const css::i18n::LocaleDataItem2& rLocaleDataItem, sal_uInt16 nDecimals )
1212{
1213 // approximately 3.2 bits per digit
1214 const size_t nDig = ((sizeof(sal_Int64) * 8) / 3) + 1;
1215 // digits, separators (pessimized for insane "every digit may be grouped"), leading zero, sign
1216 size_t nGuess = ((nDecimals < nDig) ?
1217 (((nDig - nDecimals) * rLocaleDataItem.thousandSeparator.getLength()) + nDig) :
1218 nDecimals) + rLocaleDataItem.decimalSeparator.getLength() + 3;
1219 return nGuess;
1220}
1221
1222OUString LocaleDataWrapper::getNum( sal_Int64 nNumber, sal_uInt16 nDecimals,
1223 bool bUseThousandSep, bool bTrailingZeros ) const
1224{
1225 // check if digits and separators will fit into fixed buffer or allocate
1226 size_t nGuess = ImplGetNumberStringLengthGuess( aLocaleDataItem, nDecimals );
1227 OUStringBuffer aBuf(int(nGuess + 16));
1228
1229 ImplAddFormatNum( aBuf, nNumber, nDecimals,
1230 bUseThousandSep, bTrailingZeros );
1231
1232 return aBuf.makeStringAndClear();
1233}
1234
1235OUString LocaleDataWrapper::getCurr( sal_Int64 nNumber, sal_uInt16 nDecimals,
1236 std::u16string_view rCurrencySymbol, bool bUseThousandSep ) const
1237{
1238 sal_Unicode cZeroChar = getCurrZeroChar();
1239
1240 // check if digits and separators will fit into fixed buffer or allocate
1241 size_t nGuess = ImplGetNumberStringLengthGuess( aLocaleDataItem, nDecimals );
1242 OUStringBuffer aNumBuf(sal_Int32(nGuess + 16));
1243
1244 bool bNeg;
1245 if ( nNumber < 0 )
1246 {
1247 bNeg = true;
1248 nNumber *= -1;
1249 }
1250 else
1251 bNeg = false;
1252
1253 // convert number
1254 ImplAddFormatNum( aNumBuf, nNumber, nDecimals,
1255 bUseThousandSep, true );
1256 const sal_Int32 nNumLen = aNumBuf.getLength();
1257
1258 // replace zeros with zero character
1259 if ( (cZeroChar != '0') && nDecimals /* && IsNumTrailingZeros() */ )
1260 {
1261 sal_uInt16 i;
1262 bool bZero = true;
1263
1264 sal_uInt16 nNumBufIndex = nNumLen-nDecimals;
1265 i = 0;
1266 do
1267 {
1268 if ( aNumBuf[nNumBufIndex] != '0' )
1269 {
1270 bZero = false;
1271 break;
1272 }
1273
1274 nNumBufIndex++;
1275 i++;
1276 }
1277 while ( i < nDecimals );
1278
1279 if ( bZero )
1280 {
1281 nNumBufIndex = nNumLen-nDecimals;
1282 i = 0;
1283 do
1284 {
1285 aNumBuf[nNumBufIndex] = cZeroChar;
1286 nNumBufIndex++;
1287 i++;
1288 }
1289 while ( i < nDecimals );
1290 }
1291 }
1292
1293 OUString aCur;
1294 if ( !bNeg )
1295 {
1296 switch( getCurrPositiveFormat() )
1297 {
1298 case 0:
1299 aCur = rCurrencySymbol + aNumBuf;
1300 break;
1301 case 1:
1302 aCur = aNumBuf + rCurrencySymbol;
1303 break;
1304 case 2:
1305 aCur = OUString::Concat(rCurrencySymbol) + " " + aNumBuf;
1306 break;
1307 case 3:
1308 aCur = aNumBuf + " " + rCurrencySymbol;
1309 break;
1310 }
1311 }
1312 else
1313 {
1314 switch( getCurrNegativeFormat() )
1315 {
1316 case 0:
1317 aCur = OUString::Concat("(") + rCurrencySymbol + aNumBuf + ")";
1318 break;
1319 case 1:
1320 aCur = OUString::Concat("-") + rCurrencySymbol + aNumBuf;
1321 break;
1322 case 2:
1323 aCur = OUString::Concat(rCurrencySymbol) + "-" + aNumBuf;
1324 break;
1325 case 3:
1326 aCur = rCurrencySymbol + aNumBuf + "-";
1327 break;
1328 case 4:
1329 aCur = "(" + aNumBuf + rCurrencySymbol + ")";
1330 break;
1331 case 5:
1332 aCur = "-" + aNumBuf + rCurrencySymbol;
1333 break;
1334 case 6:
1335 aCur = aNumBuf + "-" + rCurrencySymbol;
1336 break;
1337 case 7:
1338 aCur = aNumBuf + rCurrencySymbol + "-";
1339 break;
1340 case 8:
1341 aCur = "-" + aNumBuf + " " + rCurrencySymbol;
1342 break;
1343 case 9:
1344 aCur = OUString::Concat("-") + rCurrencySymbol + " " + aNumBuf;
1345 break;
1346 case 10:
1347 aCur = aNumBuf + " " + rCurrencySymbol + "-";
1348 break;
1349 case 11:
1350 aCur = OUString::Concat(rCurrencySymbol) + " -" + aNumBuf;
1351 break;
1352 case 12:
1353 aCur = OUString::Concat(rCurrencySymbol) + " " + aNumBuf + "-";
1354 break;
1355 case 13:
1356 aCur = aNumBuf + "- " + rCurrencySymbol;
1357 break;
1358 case 14:
1359 aCur = OUString::Concat("(") + rCurrencySymbol + " " + aNumBuf + ")";
1360 break;
1361 case 15:
1362 aCur = "(" + aNumBuf + " " + rCurrencySymbol + ")";
1363 break;
1364 }
1365 }
1366
1367 return aCur;
1368}
1369
1370// --- number parsing -------------------------------------------------
1371
1372double LocaleDataWrapper::stringToDouble( std::u16string_view aString, bool bUseGroupSep,
1373 rtl_math_ConversionStatus* pStatus, sal_Int32* pParseEnd ) const
1374{
1375 const sal_Unicode* pParseEndChar;
1376 double fValue = stringToDouble(aString.data(), aString.data() + aString.size(), bUseGroupSep, pStatus, &pParseEndChar);
1377 if (pParseEnd)
1378 *pParseEnd = pParseEndChar - aString.data();
1379 return fValue;
1380}
1381
1382double LocaleDataWrapper::stringToDouble( const sal_Unicode* pBegin, const sal_Unicode* pEnd, bool bUseGroupSep,
1383 rtl_math_ConversionStatus* pStatus, const sal_Unicode** ppParseEnd ) const
1384{
1385 const sal_Unicode cGroupSep = (bUseGroupSep ? aLocaleDataItem.thousandSeparator[0] : 0);
1386 rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
1387 const sal_Unicode* pParseEnd = nullptr;
1388 double fValue = rtl_math_uStringToDouble( pBegin, pEnd, aLocaleDataItem.decimalSeparator[0], cGroupSep, &eStatus, &pParseEnd);
1389 bool bTryAlt = (pParseEnd < pEnd && !aLocaleDataItem.decimalSeparatorAlternative.isEmpty() &&
1390 *pParseEnd == aLocaleDataItem.decimalSeparatorAlternative.toChar());
1391 // Try re-parsing with alternative if that was the reason to stop.
1392 if (bTryAlt)
1393 fValue = rtl_math_uStringToDouble( pBegin, pEnd, aLocaleDataItem.decimalSeparatorAlternative.toChar(), cGroupSep, &eStatus, &pParseEnd);
1394 if (pStatus)
1395 *pStatus = eStatus;
1396 if (ppParseEnd)
1397 *ppParseEnd = pParseEnd;
1398 return fValue;
1399}
1400
1401// --- mixed ----------------------------------------------------------
1402
1404{
1405 LanguageCountryInfo aLCInfo = getLanguageCountryInfo();
1406 return LanguageTag( lang::Locale( aLCInfo.Language, aLCInfo.Country, aLCInfo.Variant ));
1407}
1408
1409OUString LocaleDataWrapper::appendLocaleInfo(std::u16string_view rDebugMsg) const
1410{
1412 return OUString::Concat(rDebugMsg) + "\n" + maLanguageTag.getBcp47() + " requested\n"
1413 + aLoaded.getBcp47() + " loaded";
1414}
1415
1416// static
1417void LocaleDataWrapper::outputCheckMessage( std::u16string_view rMsg )
1418{
1419 outputCheckMessage(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8).getStr());
1420}
1421
1422// static
1424{
1425 fprintf( stderr, "\n%s\n", pStr);
1426 fflush( stderr);
1427 SAL_WARN("unotools.i18n", pStr);
1428}
1429
1430// static
1432{
1433 // Using the rtl_Instance template here wouldn't solve all threaded write
1434 // accesses, since we want to assign the result to the static member
1435 // variable and would need to dereference the pointer returned and assign
1436 // the value unguarded. This is the same pattern manually coded.
1438 if (!nCheck)
1439 {
1440 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex());
1441 nCheck = nLocaleDataChecking;
1442 if (!nCheck)
1443 {
1444#ifdef DBG_UTIL
1445 nCheck = 1;
1446#else
1447 const char* pEnv = getenv( "OOO_ENABLE_LOCALE_DATA_CHECKS");
1448 if (pEnv && (pEnv[0] == 'Y' || pEnv[0] == 'y' || pEnv[0] == '1'))
1449 nCheck = 1;
1450 else
1451 nCheck = 2;
1452#endif
1453 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1454 nLocaleDataChecking = nCheck;
1455 }
1456 }
1457 else {
1458 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1459 }
1460}
1461
1462// --- XLocaleData3 ----------------------------------------------------------
1463
1464css::uno::Sequence< css::i18n::Calendar2 > LocaleDataWrapper::getAllCalendars() const
1465{
1466 try
1467 {
1468 return xLD->getAllCalendars2( getMyLocale() );
1469 }
1470 catch (const Exception&)
1471 {
1472 TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllCalendars" );
1473 }
1474 return {};
1475}
1476
1477// --- XLocaleData4 ----------------------------------------------------------
1478
1479const css::uno::Sequence< OUString > & LocaleDataWrapper::getDateAcceptancePatterns() const
1480{
1482}
1483
1484// --- Override layer --------------------------------------------------------
1485
1487 const std::vector<OUString> & rPatterns )
1488{
1489 if (!aDateAcceptancePatterns.hasElements() || rPatterns.empty())
1490 {
1491 try
1492 {
1493 aDateAcceptancePatterns = xLD->getDateAcceptancePatterns( maLanguageTag.getLocale() );
1494 }
1495 catch (const Exception&)
1496 {
1497 TOOLS_WARN_EXCEPTION( "unotools.i18n", "setDateAcceptancePatterns" );
1498 }
1499 if (rPatterns.empty())
1500 return; // just a reset
1501 if (!aDateAcceptancePatterns.hasElements())
1502 {
1504 return;
1505 }
1506 }
1507
1508 // Earlier versions checked for presence of the full date pattern with
1509 // aDateAcceptancePatterns[0] == rPatterns[0] and prepended that if not.
1510 // This lead to confusion if the patterns were intentionally specified
1511 // without, giving entirely a different DMY order, see tdf#150288.
1512 // Not checking this and accepting the given patterns as is may result in
1513 // the user shooting themself in the foot, but we can't have both.
1515}
1516
1517/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
struct _ADOIndex Index
Reference< XComponentContext > m_xContext
sal_Int16 GetYear() const
sal_uInt16 GetDay() const
sal_uInt16 GetMonth() const
LanguageType getLanguageType(bool bResolveSystem=true) const
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
const OUString & getBcp47(bool bResolveSystem=true) const
This class can be accessed without locking because we load all of the data in the constructor.
const OUString & getOneLocaleItem(sal_Int16 nItem) const
css::uno::Sequence< css::i18n::Calendar2 > getAllCalendars() const
NOTE: this wraps XLocaleData3::getAllCalendars2() in fact.
sal_uInt16 getCurrNegativeFormat() const
OUString getCurr(sal_Int64 nNumber, sal_uInt16 nDecimals, std::u16string_view rCurrencySymbol, bool bUseThousandSep=true) const
"Secure" currency formatted string.
sal_Unicode getCurrZeroChar() const
OUString getDuration(const tools::Duration &rDuration, bool bSec=true, bool b100Sec=false) const
OUString getDate(const Date &rDate) const
only numerical values of Gregorian calendar
static sal_uInt8 nLocaleDataChecking
OUString getNum(sal_Int64 nNumber, sal_uInt16 nDecimals, bool bUseThousandSep=true, bool bTrailingZeros=true) const
Simple number formatting.
std::vector< OUString > aReservedWords
const OUString & getCurrBankSymbol() const
static const std::vector< LanguageType > & getInstalledLanguageTypes()
Get LanguageTypes for all installed locales which are unambiguous convertible back and forth between ...
LocaleDataWrapper(const css::uno::Reference< css::uno::XComponentContext > &rxContext, LanguageTag aLanguageTag)
css::uno::Sequence< css::i18n::Currency2 > getAllCurrencies() const
NOTE: this wraps XLocaleData2::getAllCurrencies2() in fact.
css::uno::Sequence< css::i18n::FormatElement > getAllFormats() const
LanguageTag getLoadedLanguageTag() const
get current loaded Locale, which might differ from the requested Locale
css::i18n::ForbiddenCharacters getForbiddenCharacters() const
void ImplAddFormatNum(rtl::OUStringBuffer &rBuf, sal_Int64 nNumber, sal_uInt16 nDecimals, bool bUseThousandSep, bool bTrailingZeros) const
const OUString & getCurrSymbol() const
static const css::uno::Sequence< css::lang::Locale > & getInstalledLocaleNames()
same as the wrapper implementation but static
LongDateOrder scanDateOrderImpl(std::u16string_view rCode) const
void loadDateAcceptancePatterns(const std::vector< OUString > &rOverrideDateAcceptancePatterns)
MeasurementSystem mapMeasurementStringToEnum(std::u16string_view rMS) const
maps the LocaleData string to the International enum
const css::lang::Locale & getMyLocale() const
css::uno::Sequence< css::i18n::CalendarItem2 > const & getDefaultCalendarDays() const
Convenience method to obtain the day names of the default calendar.
css::uno::Sequence< sal_Int32 > aGrouping
const css::uno::Sequence< OUString > & getDateAcceptancePatterns() const
sal_uInt16 getCurrPositiveFormat() const
const css::uno::Sequence< css::lang::Locale > & getAllInstalledLocaleNames() const
LongDateOrder nLongDateOrder
const std::shared_ptr< css::i18n::Calendar2 > & getDefaultCalendar() const
Convenience method to obtain the default calendar.
const OUString & getOneReservedWord(sal_Int16 nWord) const
sal_uInt16 nCurrPositiveFormat
double stringToDouble(std::u16string_view aString, bool bUseGroupSep, rtl_math_ConversionStatus *pStatus, sal_Int32 *pParseEnd) const
A wrapper around rtl::math::stringToDouble() using the locale dependent decimal separator,...
void scanCurrFormatImpl(std::u16string_view rCode, sal_Int32 nStart, sal_Int32 &nSign, sal_Int32 &nPar, sal_Int32 &nNum, sal_Int32 &nBlank, sal_Int32 &nSym) const
static void evaluateLocaleDataChecking()
const css::i18n::LocaleDataItem2 & getLocaleItem() const
NOTE: this wraps XLocaleData5::getLocaleItem2() in fact.
DateOrder getDateOrder() const
OUString getTime(const tools::Time &rTime, bool bSec=true, bool b100Sec=false) const
static bool areChecksEnabled()
Return whether locale data checks are enabled.
LongDateOrder getLongDateOrder() const
css::i18n::LocaleDataItem2 aLocaleDataItem
bool doesSecondaryCalendarUseEC(std::u16string_view rName) const
If the secondary calendar, if any, is of the name passed AND number formats using it usually use the ...
sal_uInt16 getCurrDigits() const
OUString appendLocaleInfo(std::u16string_view rDebugMsg) const
Append locale info to string, used with locale data checking.
static void outputCheckMessage(std::u16string_view rMsg)
Output a message during locale data checking.
static bool isNumLeadingZero()
css::uno::Reference< css::uno::XComponentContext > m_xContext
const css::uno::Sequence< sal_Int32 > & getDigitGrouping() const
Obtain digit grouping.
sal_uInt16 nCurrNegativeFormat
css::uno::Sequence< OUString > aDateAcceptancePatterns
std::shared_ptr< css::i18n::Calendar2 > xSecondaryCalendar
const LanguageTag & getLanguageTag() const
get current requested Locale
std::shared_ptr< css::i18n::Calendar2 > xDefaultCalendar
css::i18n::LanguageCountryInfo getLanguageCountryInfo() const
css::uno::Reference< css::i18n::XLocaleData5 > xLD
css::uno::Sequence< css::i18n::CalendarItem2 > const & getDefaultCalendarMonths() const
Convenience method to obtain the month names of the default calendar.
OUString aLocaleItem[css::i18n::LocaleItem::COUNT2]
bool IsNegative() const
const Time & GetTime() const
sal_Int32 GetDays() const
sal_uInt16 GetSec() const
sal_uInt16 GetMin() const
sal_uInt16 GetHour() const
sal_uInt32 GetNanoSec() const
static css::uno::Sequence< sal_Bool > createForwardSequence(sal_Int32 nIntegerDigits, const css::uno::Sequence< sal_Int32 > &rGroupings)
Create a sequence of bool values containing positions where to add a separator when iterating forward...
int nCount
#define DBG_ASSERT(sCon, aError)
#define TOOLS_WARN_EXCEPTION(area, stream)
float u
void * p
#define LANGUAGE_SYSTEM
#define LANGUAGE_DONTKNOW
#define LANGUAGE_NORWEGIAN
static void ImplAdd9UNum(OUStringBuffer &rBuf, sal_uInt32 nNumber)
static void ImplAdd2UNum(OUStringBuffer &rBuf, sal_uInt16 nNumber)
const sal_uInt16 nCurrFormatDefault
static size_t ImplGetNumberStringLengthGuess(const css::i18n::LocaleDataItem2 &rLocaleDataItem, sal_uInt16 nDecimals)
static void ImplAddUNum(OUStringBuffer &rBuf, sal_uInt64 nNumber)
static void ImplAddNum(OUStringBuffer &rBuf, sal_Int64 nNumber, int nMinLen)
static DateOrder getDateOrderFromLongDateOrder(LongDateOrder eLong)
MeasurementSystem
LongDateOrder
#define SAL_WARN(area, stream)
LanguageTag maLanguageTag
aStr
aBuf
@ Exception
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
Reference< XComponentContext > getProcessComponentContext()
Type
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
bool equalsIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
constexpr OUStringLiteral EMPTY
SwNodeOffset abs(const SwNodeOffset &a)
unsigned char sal_uInt8
sal_uInt16 sal_Unicode