Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
*
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "OSPreferences.h"
8
#include "mozilla/intl/LocaleService.h"
9
#include "nsReadableUtils.h"
10
11
#include <windows.h>
12
13
using namespace mozilla::intl;
14
15
OSPreferences::OSPreferences() {}
16
17
OSPreferences::~OSPreferences() {}
18
19
bool OSPreferences::ReadSystemLocales(nsTArray<nsCString>& aLocaleList) {
20
MOZ_ASSERT(aLocaleList.IsEmpty());
21
22
ULONG numLanguages = 0;
23
DWORD cchLanguagesBuffer = 0;
24
BOOL ok = GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numLanguages,
25
nullptr, &cchLanguagesBuffer);
26
if (ok) {
27
AutoTArray<WCHAR, 64> locBuffer;
28
locBuffer.SetCapacity(cchLanguagesBuffer);
29
ok = GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numLanguages,
30
locBuffer.Elements(), &cchLanguagesBuffer);
31
if (ok) {
32
NS_LossyConvertUTF16toASCII loc(locBuffer.Elements());
33
34
// We will only take the first locale from the returned list, because
35
// we do not support real fallback chains for RequestedLocales yet.
36
if (CanonicalizeLanguageTag(loc)) {
37
aLocaleList.AppendElement(loc);
38
return true;
39
}
40
}
41
}
42
43
return false;
44
}
45
46
bool OSPreferences::ReadRegionalPrefsLocales(nsTArray<nsCString>& aLocaleList) {
47
MOZ_ASSERT(aLocaleList.IsEmpty());
48
49
WCHAR locale[LOCALE_NAME_MAX_LENGTH];
50
if (NS_WARN_IF(!LCIDToLocaleName(LOCALE_USER_DEFAULT, locale,
51
LOCALE_NAME_MAX_LENGTH, 0))) {
52
return false;
53
}
54
55
NS_LossyConvertUTF16toASCII loc(locale);
56
57
if (CanonicalizeLanguageTag(loc)) {
58
aLocaleList.AppendElement(loc);
59
return true;
60
}
61
return false;
62
}
63
64
static LCTYPE ToDateLCType(OSPreferences::DateTimeFormatStyle aFormatStyle) {
65
switch (aFormatStyle) {
66
case OSPreferences::DateTimeFormatStyle::None:
67
return LOCALE_SLONGDATE;
68
case OSPreferences::DateTimeFormatStyle::Short:
69
return LOCALE_SSHORTDATE;
70
case OSPreferences::DateTimeFormatStyle::Medium:
71
return LOCALE_SSHORTDATE;
72
case OSPreferences::DateTimeFormatStyle::Long:
73
return LOCALE_SLONGDATE;
74
case OSPreferences::DateTimeFormatStyle::Full:
75
return LOCALE_SLONGDATE;
76
case OSPreferences::DateTimeFormatStyle::Invalid:
77
default:
78
MOZ_ASSERT_UNREACHABLE("invalid date format");
79
return LOCALE_SLONGDATE;
80
}
81
}
82
83
static LCTYPE ToTimeLCType(OSPreferences::DateTimeFormatStyle aFormatStyle) {
84
switch (aFormatStyle) {
85
case OSPreferences::DateTimeFormatStyle::None:
86
return LOCALE_STIMEFORMAT;
87
case OSPreferences::DateTimeFormatStyle::Short:
88
return LOCALE_SSHORTTIME;
89
case OSPreferences::DateTimeFormatStyle::Medium:
90
return LOCALE_SSHORTTIME;
91
case OSPreferences::DateTimeFormatStyle::Long:
92
return LOCALE_STIMEFORMAT;
93
case OSPreferences::DateTimeFormatStyle::Full:
94
return LOCALE_STIMEFORMAT;
95
case OSPreferences::DateTimeFormatStyle::Invalid:
96
default:
97
MOZ_ASSERT_UNREACHABLE("invalid time format");
98
return LOCALE_STIMEFORMAT;
99
}
100
}
101
102
/**
103
* Windows API includes regional preferences from the user only
104
* if we pass empty locale string or if the locale string matches
105
* the current locale.
106
*
107
* Since Windows API only allows us to retrieve two options - short/long
108
* we map it to our four options as:
109
*
110
* short -> short
111
* medium -> short
112
* long -> long
113
* full -> long
114
*
115
* In order to produce a single date/time format, we use CLDR pattern
116
* for combined date/time string, since Windows API does not provide an
117
* option for this.
118
*/
119
bool OSPreferences::ReadDateTimePattern(DateTimeFormatStyle aDateStyle,
120
DateTimeFormatStyle aTimeStyle,
121
const nsACString& aLocale,
122
nsAString& aRetVal) {
123
nsAutoString localeName;
124
CopyASCIItoUTF16(aLocale, localeName);
125
126
bool isDate = aDateStyle != DateTimeFormatStyle::None &&
127
aDateStyle != DateTimeFormatStyle::Invalid;
128
bool isTime = aTimeStyle != DateTimeFormatStyle::None &&
129
aTimeStyle != DateTimeFormatStyle::Invalid;
130
131
// If both date and time are wanted, we'll initially read them into a
132
// local string, and then insert them into the overall date+time pattern;
133
// but if only one is needed we'll work directly with the return value.
134
// Set 'str' to point to the string we will use to retrieve patterns
135
// from Windows.
136
nsAutoString tmpStr;
137
nsAString* str;
138
if (isDate && isTime) {
139
if (!GetDateTimeConnectorPattern(aLocale, aRetVal)) {
140
NS_WARNING("failed to get date/time connector");
141
aRetVal.AssignLiteral(u"{1} {0}");
142
}
143
str = &tmpStr;
144
} else if (isDate || isTime) {
145
str = &aRetVal;
146
} else {
147
aRetVal.Truncate(0);
148
return true;
149
}
150
151
if (isDate) {
152
LCTYPE lcType = ToDateLCType(aDateStyle);
153
size_t len = GetLocaleInfoEx(
154
reinterpret_cast<const wchar_t*>(localeName.BeginReading()), lcType,
155
nullptr, 0);
156
if (len == 0) {
157
return false;
158
}
159
160
// We're doing it to ensure the terminator will fit when Windows writes the
161
// data to its output buffer. See bug 1358159 for details.
162
str->SetLength(len);
163
GetLocaleInfoEx(reinterpret_cast<const wchar_t*>(localeName.BeginReading()),
164
lcType, (WCHAR*)str->BeginWriting(), len);
165
str->SetLength(len - 1); // -1 because len counts the null terminator
166
167
// Windows uses "ddd" and "dddd" for abbreviated and full day names
168
// respectively,
170
// but in a CLDR/ICU-style pattern these should be "EEE" and "EEEE".
172
// So we fix that up here.
173
nsAString::const_iterator start, pos, end;
174
start = str->BeginReading(pos);
175
str->EndReading(end);
176
if (FindInReadable(NS_LITERAL_STRING("dddd"), pos, end)) {
177
str->ReplaceLiteral(pos - start, 4, u"EEEE");
178
} else {
179
pos = start;
180
if (FindInReadable(NS_LITERAL_STRING("ddd"), pos, end)) {
181
str->ReplaceLiteral(pos - start, 3, u"EEE");
182
}
183
}
184
185
// Also, Windows uses lowercase "g" or "gg" for era, but ICU wants uppercase
186
// "G" (it would interpret "g" as "modified Julian day"!). So fix that.
187
int32_t index = str->FindChar('g');
188
if (index >= 0) {
189
str->Replace(index, 1, 'G');
190
// If it was a double "gg", just drop the second one.
191
index++;
192
if (str->CharAt(index) == 'g') {
193
str->Cut(index, 1);
194
}
195
}
196
197
// If time was also requested, we need to substitute the date pattern from
198
// Windows into the date+time format that we have in aRetVal.
199
if (isTime) {
200
nsAString::const_iterator start, pos, end;
201
start = aRetVal.BeginReading(pos);
202
aRetVal.EndReading(end);
203
if (FindInReadable(NS_LITERAL_STRING("{1}"), pos, end)) {
204
aRetVal.Replace(pos - start, 3, tmpStr);
205
}
206
}
207
}
208
209
if (isTime) {
210
LCTYPE lcType = ToTimeLCType(aTimeStyle);
211
size_t len = GetLocaleInfoEx(
212
reinterpret_cast<const wchar_t*>(localeName.BeginReading()), lcType,
213
nullptr, 0);
214
if (len == 0) {
215
return false;
216
}
217
218
// We're doing it to ensure the terminator will fit when Windows writes the
219
// data to its output buffer. See bug 1358159 for details.
220
str->SetLength(len);
221
GetLocaleInfoEx(reinterpret_cast<const wchar_t*>(localeName.BeginReading()),
222
lcType, (WCHAR*)str->BeginWriting(), len);
223
str->SetLength(len - 1);
224
225
// Windows uses "t" or "tt" for a "time marker" (am/pm indicator),
227
// but in a CLDR/ICU-style pattern that should be "a".
229
// So we fix that up here.
230
int32_t index = str->FindChar('t');
231
if (index >= 0) {
232
str->Replace(index, 1, 'a');
233
index++;
234
if (str->CharAt(index) == 't') {
235
str->Cut(index, 1);
236
}
237
}
238
239
if (isDate) {
240
nsAString::const_iterator start, pos, end;
241
start = aRetVal.BeginReading(pos);
242
aRetVal.EndReading(end);
243
if (FindInReadable(NS_LITERAL_STRING("{0}"), pos, end)) {
244
aRetVal.Replace(pos - start, 3, tmpStr);
245
}
246
}
247
}
248
249
return true;
250
}