Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/include/unotools/localedatawrapper.hxx
Line
Count
Source
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
#ifndef INCLUDED_UNOTOOLS_LOCALEDATAWRAPPER_HXX
21
#define INCLUDED_UNOTOOLS_LOCALEDATAWRAPPER_HXX
22
23
#include <com/sun/star/uno/Reference.hxx>
24
#include <com/sun/star/uno/Sequence.hxx>
25
#include <com/sun/star/i18n/LocaleItem.hpp>
26
#include <com/sun/star/i18n/LocaleDataItem2.hpp>
27
#include <com/sun/star/i18n/reservedWords.hpp>
28
#include <rtl/ustring.hxx>
29
#include <rtl/math.h>
30
#include <i18nlangtag/languagetag.hxx>
31
#include <unotools/unotoolsdllapi.h>
32
#include <memory>
33
#include <string_view>
34
35
namespace com::sun::star::uno { class XComponentContext; }
36
namespace com::sun::star::i18n { class XLocaleData5; }
37
namespace com::sun::star::i18n { struct Calendar2; }
38
namespace com::sun::star::i18n { struct Currency2; }
39
namespace com::sun::star::i18n { struct ForbiddenCharacters; }
40
namespace com::sun::star::i18n { struct FormatElement; }
41
namespace com::sun::star::i18n { struct CalendarItem2; }
42
namespace com::sun::star::i18n { struct LanguageCountryInfo; }
43
44
class Date;
45
namespace tools
46
{
47
class Time;
48
class Duration;
49
}
50
51
enum class DateOrder {
52
    Invalid = -1,
53
    MDY = 0,
54
    DMY,
55
    YMD,
56
};
57
58
enum class LongDateOrder {
59
    Invalid = -1,
60
    MDY = 0,
61
    DMY,
62
    YMD,
63
    YDM
64
};
65
66
enum class MeasurementSystem {
67
    Metric,
68
    US
69
};
70
71
/**
72
 * This class can be accessed without locking because we load
73
 * all of the data in the constructor.
74
 */
75
class UNOTOOLS_DLLPUBLIC LocaleDataWrapper
76
{
77
    static  sal_uInt8                nLocaleDataChecking;    // 0:=dontknow, 1:=yes, 2:=no
78
79
    css::uno::Reference< css::uno::XComponentContext > m_xContext;
80
    css::uno::Reference< css::i18n::XLocaleData5 >     xLD;
81
    LanguageTag                                        maLanguageTag;
82
    std::shared_ptr< css::i18n::Calendar2 >            xDefaultCalendar;
83
    std::shared_ptr< css::i18n::Calendar2 >            xSecondaryCalendar;
84
    css::i18n::LocaleDataItem2                         aLocaleDataItem;
85
    css::uno::Sequence< OUString >                     aDateAcceptancePatterns;
86
    css::uno::Sequence< sal_Int32 >                    aGrouping;
87
    // cached items
88
    OUString                aLocaleItem[css::i18n::LocaleItem::COUNT2];
89
    std::vector<OUString>   aReservedWords;
90
    OUString                aCurrSymbol;
91
    OUString                aCurrBankSymbol;
92
    DateOrder               nDateOrder;
93
    LongDateOrder           nLongDateOrder;
94
    sal_uInt16              nCurrPositiveFormat;
95
    sal_uInt16              nCurrNegativeFormat;
96
    sal_uInt16              nCurrDigits;
97
98
    SAL_DLLPRIVATE void loadData();
99
    SAL_DLLPRIVATE void loadDateAcceptancePatterns(const std::vector<OUString> & rOverrideDateAcceptancePatterns);
100
101
    const OUString&     getOneLocaleItem( sal_Int16 nItem ) const;
102
103
    const OUString&     getOneReservedWord( sal_Int16 nWord ) const;
104
105
    SAL_DLLPRIVATE void loadCurrencyFormats();
106
107
    SAL_DLLPRIVATE void scanCurrFormatImpl( std::u16string_view rCode,
108
                            sal_Int32 nStart, sal_Int32& nSign,
109
                            sal_Int32& nPar, sal_Int32& nNum,
110
                            sal_Int32& nBlank, sal_Int32& nSym ) const;
111
112
    SAL_DLLPRIVATE void loadDateOrders();
113
    SAL_DLLPRIVATE LongDateOrder scanDateOrderImpl( std::u16string_view rCode ) const;
114
115
    SAL_DLLPRIVATE void ImplAddFormatNum( rtl::OUStringBuffer& rBuf,
116
                            sal_Int64 nNumber, sal_uInt16 nDecimals,
117
                            bool bUseThousandSep, bool bTrailingZeros ) const;
118
119
    SAL_DLLPRIVATE void loadDigitGrouping();
120
121
    LocaleDataWrapper(
122
        const css::uno::Reference< css::uno::XComponentContext > & rxContext,
123
        LanguageTag aLanguageTag
124
        );
125
126
public:
127
128
    /**
129
     * retrieve a cached LocaleDataWrapper
130
     */
131
    static const LocaleDataWrapper* get(const LanguageTag& aLanguageTag);
132
133
    /**
134
        @param rOverrideDateAcceptancePatterns Override locale's date acceptance patterns.
135
            An empty sequence resets the patterns to the locale's pattern sequence.
136
     */
137
    LocaleDataWrapper(
138
        LanguageTag aLanguageTag,
139
        const std::vector<OUString> & rOverrideDateAcceptancePatterns = {}
140
        );
141
    ~LocaleDataWrapper();
142
143
    /** Get the service factory, meant to be able to create a CalendarWrapper
144
        from a LocaleDataWrapper. Note that the service factory may be
145
        non-existent if this LocaleDataWrapper was created without one and
146
        lives "on the grassland". The CalendarWrapper ctor can handle that
147
        though. */
148
    const css::uno::Reference<
149
        css::uno::XComponentContext > & getComponentContext()
150
0
        const { return m_xContext; }
151
152
    /// get current requested Locale
153
    const   LanguageTag&        getLanguageTag() const;
154
155
    /// get current loaded Locale, which might differ from the requested Locale
156
    LanguageTag                 getLoadedLanguageTag() const;
157
158
    // Wrapper implementations of service LocaleData
159
160
    SAL_DLLPRIVATE css::i18n::LanguageCountryInfo getLanguageCountryInfo() const;
161
    /// NOTE: this wraps XLocaleData5::getLocaleItem2() in fact.
162
    const css::i18n::LocaleDataItem2& getLocaleItem() const;
163
    /// NOTE: this wraps XLocaleData3::getAllCalendars2() in fact.
164
    css::uno::Sequence< css::i18n::Calendar2 > getAllCalendars() const;
165
    /// NOTE: this wraps XLocaleData2::getAllCurrencies2() in fact.
166
    css::uno::Sequence< css::i18n::Currency2 > getAllCurrencies() const;
167
    css::uno::Sequence< css::i18n::FormatElement > getAllFormats() const;
168
    css::i18n::ForbiddenCharacters getForbiddenCharacters() const;
169
    SAL_DLLPRIVATE const css::uno::Sequence< css::lang::Locale > & getAllInstalledLocaleNames() const;
170
    const css::uno::Sequence< OUString > & getDateAcceptancePatterns() const;
171
172
173
    /// same as the wrapper implementation but static
174
    static const css::uno::Sequence< css::lang::Locale > & getInstalledLocaleNames();
175
176
    /** Get LanguageTypes for all installed locales which are unambiguous
177
        convertible back and forth between locale ISO strings and MS-LCID
178
        LanguageType. Upon the first time the function is called when
179
        locale data checking is enabled, messages are shown for locales not
180
        matching, excluding already known problems.
181
        (e.g. used in number formatter dialog init)
182
     */
183
    static const std::vector< LanguageType > & getInstalledLanguageTypes();
184
185
    /// maps the LocaleData string to the International enum
186
    static MeasurementSystem mapMeasurementStringToEnum( std::u16string_view rMS );
187
188
    /// Convenience method to obtain the default calendar.
189
    const std::shared_ptr< css::i18n::Calendar2 >& getDefaultCalendar() const;
190
191
    /// Convenience method to obtain the day names of the default calendar.
192
    css::uno::Sequence< css::i18n::CalendarItem2 > const & getDefaultCalendarDays() const;
193
194
    /// Convenience method to obtain the month names of the default calendar.
195
    css::uno::Sequence< css::i18n::CalendarItem2 > const & getDefaultCalendarMonths() const;
196
197
    /** If the secondary calendar, if any, is of the name passed AND number
198
        formats using it usually use the E or EE keyword (EC|EEC). */
199
    bool doesSecondaryCalendarUseEC( std::u16string_view rName ) const;
200
201
    /** Obtain digit grouping. The usually known grouping by thousands (#,###)
202
        is actually only one of possible groupings. Another one, for example,
203
        used in India is group by 3 and then by 2 indefinitely (#,##,###). The
204
        integer sequence returned here specifies grouping from right to left
205
        (!), with a 0 entry designating the end of rules and the previous value
206
        to be repeated indefinitely. Hence the sequence {3,0} specifies the
207
        usual grouping by thousands, whereas the sequence {3,2,0} specifies
208
        Indian grouping. The sal_Int32* getConstArray() can be passed directly
209
        to the ::rtl::math::doubleToString() methods as argument for the
210
        pGroups parameter. */
211
    const css::uno::Sequence< sal_Int32 >& getDigitGrouping() const;
212
213
    // Functionality of class International methods, LocaleItem
214
215
    const OUString&       getDateSep() const
216
1.79M
                                    { return getOneLocaleItem( css::i18n::LocaleItem::DATE_SEPARATOR ); }
217
    const OUString&       getNumThousandSep() const
218
3.77M
                                    { return getOneLocaleItem( css::i18n::LocaleItem::THOUSAND_SEPARATOR ); }
219
    const OUString&       getNumDecimalSep() const
220
107M
                                    { return getOneLocaleItem( css::i18n::LocaleItem::DECIMAL_SEPARATOR ); }
221
    const OUString&       getNumDecimalSepAlt() const
222
6.92M
                                    { return getOneLocaleItem( css::i18n::LocaleItem::DECIMAL_SEPARATOR_ALTERNATIVE ); }
223
    const OUString&       getTimeSep() const
224
86.3M
                                    { return getOneLocaleItem( css::i18n::LocaleItem::TIME_SEPARATOR ); }
225
    const OUString&       getTime100SecSep() const
226
85.3M
                                    { return getOneLocaleItem( css::i18n::LocaleItem::TIME_100SEC_SEPARATOR ); }
227
    const OUString&       getListSep() const
228
5
                            { return getOneLocaleItem( css::i18n::LocaleItem::LIST_SEPARATOR ); }
229
    const OUString&       getQuotationMarkStart() const
230
0
                            { return getOneLocaleItem( css::i18n::LocaleItem::SINGLE_QUOTATION_START ); }
231
    const OUString&       getQuotationMarkEnd() const
232
0
                            { return getOneLocaleItem( css::i18n::LocaleItem::SINGLE_QUOTATION_END ); }
233
    const OUString&       getDoubleQuotationMarkStart() const
234
0
                            { return getOneLocaleItem( css::i18n::LocaleItem::DOUBLE_QUOTATION_START ); }
235
    const OUString&       getDoubleQuotationMarkEnd() const
236
0
                            { return getOneLocaleItem( css::i18n::LocaleItem::DOUBLE_QUOTATION_END ); }
237
    MeasurementSystem     getMeasurementSystemEnum() const
238
51.7k
                            { return mapMeasurementStringToEnum( getOneLocaleItem( css::i18n::LocaleItem::MEASUREMENT_SYSTEM ) ); }
239
    const OUString&       getTimeAM() const
240
409k
                            { return getOneLocaleItem( css::i18n::LocaleItem::TIME_AM ); }
241
    const OUString&       getTimePM() const
242
409k
                            { return getOneLocaleItem( css::i18n::LocaleItem::TIME_PM ); }
243
    const OUString&       getLongDateDayOfWeekSep() const
244
53.2k
                            { return getOneLocaleItem( css::i18n::LocaleItem::LONG_DATE_DAY_OF_WEEK_SEPARATOR ); }
245
    const OUString&       getLongDateDaySep() const
246
5.25k
                            { return getOneLocaleItem( css::i18n::LocaleItem::LONG_DATE_DAY_SEPARATOR ); }
247
    const OUString&       getLongDateMonthSep() const
248
16.4k
                            { return getOneLocaleItem( css::i18n::LocaleItem::LONG_DATE_MONTH_SEPARATOR ); }
249
250
    /** A wrapper around rtl::math::stringToDouble() using the locale dependent
251
        decimal separator, group separator, and if needed decimal separator
252
        alternative.
253
254
        The decimal separator is tried first, if the conversion does not match
255
        the entire string then the decimal separator alternative is tried if it
256
        occurs in the string and was the reason to stop.
257
258
        Leading blanks are skipped, trailing blanks are not skipped. The number
259
        is parsed up to the first non-floating point number character, same as
260
        rtl::math::stringToDouble() does. The caller is responsible for proper
261
        error checking and end comparison.
262
263
        @param  aString
264
                The string to parse as floating point number.
265
        @param  bUseGroupSep
266
                Whether group separator is used/accepted during parsing.
267
        @param  pStatus
268
                Pointer to receive the conversion status as in
269
                rtl::math::stringToDouble().
270
        @param  pParseEnd
271
                Pointer to receive the parse end (exclusive) as in
272
                rtl::math::stringToDouble().
273
        @return The floating point number as parsed.
274
     */
275
    double              stringToDouble( std::u16string_view aString, bool bUseGroupSep,
276
                                        rtl_math_ConversionStatus* pStatus, sal_Int32* pParseEnd ) const;
277
278
    /** A wrapper around rtl_math_uStringToDouble() using the locale dependent
279
        decimal separator, group separator, and if needed decimal separator
280
        alternative.
281
282
        The decimal separator is tried first, if the conversion does not match
283
        the entire string then the decimal separator alternative is tried if it
284
        occurs in the string and was the reason to stop.
285
286
        Leading blanks are skipped, trailing blanks are not skipped. The number
287
        is parsed up to the first non-floating point number character, same as
288
        rtl_math_uStringToDouble() does. The caller is responsible for proper
289
        error checking and end comparison.
290
291
        @param  pBegin
292
                The string position to start parsing a floating point number.
293
        @param  pEnd
294
                The string position to stop parsing, exclusive.
295
        @param  bUseGroupSep
296
                Whether group separator is used/accepted during parsing.
297
        @param  pStatus
298
                Pointer to receive the conversion status as in
299
                rtl_math_uStringToDouble().
300
        @param  pParseEnd
301
                Pointer to receive the parse end (exclusive) as in
302
                rtl_math_uStringToDouble().
303
        @return The floating point number as parsed.
304
     */
305
    double              stringToDouble( const sal_Unicode* pBegin, const sal_Unicode* pEnd, bool bUseGroupSep,
306
                                        rtl_math_ConversionStatus* pStatus, const sal_Unicode** ppParseEnd ) const;
307
308
    // currency
309
    const OUString&    getCurrSymbol() const;
310
    const OUString&    getCurrBankSymbol() const;
311
    sal_uInt16              getCurrPositiveFormat() const;
312
    sal_uInt16              getCurrNegativeFormat() const;
313
    sal_uInt16              getCurrDigits() const;
314
315
    // simple date and time formatting
316
    DateOrder          getDateOrder() const;
317
    LongDateOrder      getLongDateOrder() const;
318
    /// only numerical values of Gregorian calendar
319
    OUString       getDate( const Date& rDate ) const;
320
    OUString       getTime( const tools::Time& rTime, bool bSec = true,
321
                            bool b100Sec = false ) const;
322
    OUString       getDuration( const tools::Duration& rDuration,
323
                            bool bSec = true, bool b100Sec = false ) const;
324
325
                        /** Simple number formatting
326
                            @param nNumber
327
                                value * 10**nDecimals
328
                            @param bTrailingZeros
329
                            </sal_True>  := always display trailing zeros in
330
                                decimal places, even if integer value.
331
                            </sal_False> := trailing zeros are only displayed
332
                                if the value is not an integer value.
333
                         */
334
    OUString       getNum( sal_Int64 nNumber, sal_uInt16 nDecimals,
335
                            bool bUseThousandSep = true,
336
                            bool bTrailingZeros = true ) const;
337
338
                        /// "Secure" currency formatted string.
339
    OUString       getCurr( sal_Int64 nNumber, sal_uInt16 nDecimals,
340
                            std::u16string_view rCurrencySymbol,
341
                            bool bUseThousandSep = true ) const;
342
343
    // dummy returns, to be implemented
344
    sal_Unicode         getCurrZeroChar() const
345
555
                                    { return '0'; }
346
    static bool         isNumLeadingZero()
347
0
                                    { return true; }
348
                                /// standard decimal places
349
    static sal_uInt16    getNumDigits()
350
0
                                    { return 2; }
351
    static bool         isNumTrailingZeros()
352
0
                                    { return true; }
353
354
    // reserved words
355
356
    const OUString&        getTrueWord() const
357
1.60M
                                    { return getOneReservedWord( css::i18n::reservedWords::TRUE_WORD ); }
358
    const OUString&        getFalseWord() const
359
1.60M
                                    { return getOneReservedWord( css::i18n::reservedWords::FALSE_WORD ); }
360
    const OUString&        getAboveWord() const
361
0
                                    { return getOneReservedWord( css::i18n::reservedWords::ABOVE_WORD ); }
362
    const OUString&        getBelowWord() const
363
0
                                    { return getOneReservedWord( css::i18n::reservedWords::BELOW_WORD ); }
364
    /// return a quarter abbreviation string matching nQuarter (0..3) => "Q1" .. "Q2"
365
    const OUString&        getQuarterAbbreviation( sal_Int16 nQuarter ) const
366
0
                                    { return getOneReservedWord( css::i18n::reservedWords::QUARTER1_ABBREVIATION + nQuarter ); }
367
368
    /** Return whether locale data checks are enabled.
369
        Checks are enabled if the environment variable
370
        OOO_ENABLE_LOCALE_DATA_CHECKS is set to 'Y' or 'Yes' (or any other
371
        string starting with 'Y') or '1'.
372
        Also used in conjunction with the number formatter. */
373
    static  bool        areChecksEnabled()
374
2.10M
                                    {
375
2.10M
                                        if (nLocaleDataChecking == 0)
376
30
                                            evaluateLocaleDataChecking();
377
2.10M
                                        return nLocaleDataChecking == 1;
378
2.10M
                                    }
379
380
    /** Append locale info to string, used with locale data checking.
381
        A string similar to "de_DE requested\n en_US loaded" is appended. */
382
    OUString appendLocaleInfo(std::u16string_view rDebugMsg) const;
383
384
    /** Output a message during locale data checking. The (UTF-8) string is
385
        written to stderr and in a non-product build or if DBG_UTIL is enabled
386
        also raised as an assertion message box. */
387
    static  void                outputCheckMessage( std::u16string_view rMsg );
388
    static  void                outputCheckMessage( const char* pStr);
389
390
    LocaleDataWrapper(const LocaleDataWrapper&) = delete;
391
    LocaleDataWrapper& operator=(const LocaleDataWrapper&) = delete;
392
393
private:
394
395
    SAL_DLLPRIVATE const css::lang::Locale &  getMyLocale() const;
396
397
    static  void                evaluateLocaleDataChecking();
398
};
399
400
#endif // INCLUDED_UNOTOOLS_LOCALEDATAWRAPPER_HXX
401
402
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */