1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "displaynames.h"
16 #include "i18n_hilog.h"
17 #include "locale_config.h"
18 #include "locale_helper.h"
19 #include "utils.h"
20
21 namespace OHOS {
22 namespace Global {
23 namespace I18n {
24 const std::unordered_map<std::string, UDisplayContext> DisplayNames::ICU_STYLE_OPTIONS = {
25 { "long", UDISPCTX_LENGTH_FULL },
26 { "short", UDISPCTX_LENGTH_SHORT },
27 { "narrow", UDISPCTX_LENGTH_SHORT }
28 };
29
30 const std::unordered_map<std::string, UDisplayContext> DisplayNames::ICU_LANGUAGE_DISPLAY_OPTIONS = {
31 { "dialect", UDISPCTX_DIALECT_NAMES },
32 { "standard", UDISPCTX_STANDARD_NAMES }
33 };
34
35 const std::unordered_map<std::string, UDateTimePatternField> DisplayNames::ICU_DATE_PATTERN_FIELD = {
36 { "day", UDATPG_DAY_FIELD },
37 { "dayPeriod", UDATPG_DAYPERIOD_FIELD },
38 { "era", UDATPG_ERA_FIELD },
39 { "hour", UDATPG_HOUR_FIELD },
40 { "minute", UDATPG_MINUTE_FIELD },
41 { "month", UDATPG_MONTH_FIELD },
42 { "quarter", UDATPG_QUARTER_FIELD },
43 { "second", UDATPG_SECOND_FIELD },
44 { "timeZoneName", UDATPG_ZONE_FIELD },
45 { "weekOfYear", UDATPG_WEEK_OF_YEAR_FIELD },
46 { "weekday", UDATPG_WEEKDAY_FIELD },
47 { "year", UDATPG_YEAR_FIELD },
48 };
49
50 const std::unordered_map<std::string, UDateTimePGDisplayWidth> DisplayNames::ICU_DATE_DISPLAY_WIDTH = {
51 { "long", UDATPG_WIDE },
52 { "short", UDATPG_ABBREVIATED },
53 { "narrow", UDATPG_NARROW }
54 };
55
SupportedLocalesOf(const std::vector<std::string> & requestLocales,const std::map<std::string,std::string> & configs,I18nErrorCode & status)56 std::vector<std::string> DisplayNames::SupportedLocalesOf(const std::vector<std::string> &requestLocales,
57 const std::map<std::string, std::string> &configs,
58 I18nErrorCode &status)
59 {
60 std::vector<std::string> undefined = {};
61 auto requestedLocales = LocaleHelper::CanonicalizeLocaleList(requestLocales, status);
62 if (status != I18nErrorCode::SUCCESS) {
63 return undefined;
64 }
65
66 std::string localeMatcher = LocaleHelper::ParseOption(configs, "localeMatcher", "best fit", true, status);
67 if (status != I18nErrorCode::SUCCESS) {
68 return undefined;
69 }
70 std::set<std::string> availableLocales = LocaleInfo::GetValidLocales();
71 return LocaleHelper::LookupSupportedLocales(availableLocales, requestedLocales);
72 }
73
DisplayNames(const std::vector<std::string> & localeTags,const std::map<std::string,std::string> & options)74 DisplayNames::DisplayNames(const std::vector<std::string> &localeTags,
75 const std::map<std::string, std::string> &options)
76 {
77 status = I18nErrorCode::SUCCESS;
78 auto requestedLocales = LocaleHelper::CanonicalizeLocaleList(localeTags, status);
79 if (status != I18nErrorCode::SUCCESS) {
80 return;
81 }
82
83 ParseAllOptions(options);
84 if (status != I18nErrorCode::SUCCESS) {
85 return;
86 }
87 for (size_t i = 0; i < requestedLocales.size(); i++) {
88 std::string curLocale = requestedLocales[i];
89 UErrorCode icuStatus = U_ZERO_ERROR;
90 locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), icuStatus);
91 if (U_FAILURE(icuStatus)) {
92 continue;
93 }
94 if (LocaleInfo::allValidLocales.count(locale.getLanguage()) > 0) {
95 InitDisplayNames(curLocale, options);
96 if (status != I18nErrorCode::SUCCESS) {
97 continue;
98 }
99 break;
100 }
101 }
102 if (status != I18nErrorCode::SUCCESS || icuDisplaynames == nullptr) {
103 InitDisplayNames("en-US", options);
104 }
105 }
106
InitDisplayNames(const std::string & curLocale,const std::map<std::string,std::string> & options)107 void DisplayNames::InitDisplayNames(const std::string &curLocale, const std::map<std::string, std::string> &options)
108 {
109 status = I18nErrorCode::SUCCESS;
110 localeInfo = std::make_unique<LocaleInfo>(curLocale, options);
111 if (localeInfo == nullptr) {
112 status = I18nErrorCode::FAILED;
113 return;
114 }
115 if (!localeInfo->InitSuccess()) {
116 status = I18nErrorCode::INVALID_PARAM;
117 return;
118 }
119 locale = localeInfo->GetLocale();
120 localeStr = localeInfo->GetBaseName();
121
122 UDisplayContext icuStyle = UDISPCTX_LENGTH_FULL;
123 auto styleIter = ICU_STYLE_OPTIONS.find(style);
124 if (styleIter != ICU_STYLE_OPTIONS.end()) {
125 icuStyle = styleIter->second;
126 }
127
128 UDisplayContext icuLanguageDisplay = UDISPCTX_DIALECT_NAMES;
129 auto languageDisplayIter = ICU_LANGUAGE_DISPLAY_OPTIONS.find(languageDisplay);
130 if (languageDisplayIter != ICU_LANGUAGE_DISPLAY_OPTIONS.end()) {
131 icuLanguageDisplay = languageDisplayIter->second;
132 }
133
134 UDisplayContext displayContext[] = { icuStyle, icuLanguageDisplay, UDISPCTX_NO_SUBSTITUTE };
135 // 3 indicates the array length
136 icuDisplaynames.reset(icu::LocaleDisplayNames::createInstance(locale, displayContext, 3));
137 if (icuDisplaynames == nullptr) {
138 status = I18nErrorCode::FAILED;
139 HILOG_ERROR_I18N("DisplayNames::InitDisplayNames: icu createInstance failed.");
140 return;
141 }
142 createSuccess = true;
143 }
144
Display(const std::string & code)145 std::string DisplayNames::Display(const std::string &code)
146 {
147 if (!createSuccess || icuDisplaynames == nullptr) {
148 return "";
149 }
150 status = I18nErrorCode::SUCCESS;
151 std::string displayName = "";
152 if (type == "language") {
153 displayName = DisplayLanguage(code);
154 } else if (type == "region") {
155 displayName = DisplayRegion(code);
156 } else if (type == "script") {
157 displayName = DisplayScript(code);
158 } else if (type == "currency") {
159 displayName = DisplayCurrency(code);
160 } else if (type == "calendar") {
161 displayName = DisplayCalendar(code);
162 } else if (type == "dateTimeField") {
163 displayName = DisplayDatetimefield(code);
164 } else {
165 HILOG_ERROR_I18N("DisplayNames::Display: this branch is unreachable");
166 }
167
168 if (displayName.empty() && status == I18nErrorCode::SUCCESS) {
169 displayName = code;
170 }
171 return displayName;
172 }
173
ParseAllOptions(const std::map<std::string,std::string> & options)174 void DisplayNames::ParseAllOptions(const std::map<std::string, std::string> &options)
175 {
176 type = LocaleHelper::ParseOption(options, "type", "", false, status);
177 localeMatcher = LocaleHelper::ParseOption(options, "localeMatcher", "best fit", true, status);
178 style = LocaleHelper::ParseOption(options, "style", "long", true, status);
179 fallback = LocaleHelper::ParseOption(options, "fallback", "code", true, status);
180 languageDisplay = LocaleHelper::ParseOption(options, "languageDisplay", "dialect", true, status);
181 }
182
DisplayLanguage(const std::string & code)183 std::string DisplayNames::DisplayLanguage(const std::string &code)
184 {
185 // Check the legality of the code
186 UErrorCode icuStatus = U_ZERO_ERROR;
187 icu::Locale codeLocale = icu::Locale(icu::Locale::forLanguageTag(code, icuStatus).getBaseName());
188 if (U_FAILURE(icuStatus)) {
189 HILOG_ERROR_I18N("DisplayNames::DisplayLanguage: unicode error, code %{public}s.", code.c_str());
190 status = I18nErrorCode::INVALID_PARAM;
191 return "";
192 }
193 std::string checked = codeLocale.toLanguageTag<std::string>(icuStatus);
194 if (checked.size() == 0 || U_FAILURE(icuStatus) || !LocaleHelper::IsStructurallyValidLanguageTag(code)) {
195 HILOG_ERROR_I18N("DisplayNames::DisplayLanguage: error code %{public}s.", code.c_str());
196 status = I18nErrorCode::INVALID_PARAM;
197 return "";
198 }
199 icu::UnicodeString displayname;
200 icuDisplaynames->languageDisplayName(code.c_str(), displayname);
201 std::string result = "";
202 if (!displayname.isBogus()) {
203 displayname.toUTF8String(result);
204 }
205 return result;
206 }
207
DisplayRegion(const std::string & code)208 std::string DisplayNames::DisplayRegion(const std::string &code)
209 {
210 if (!LocaleHelper::IsUnicodeRegionSubtag(code)) {
211 HILOG_ERROR_I18N("DisplayNames::DisplayRegion: error code %{public}s.", code.c_str());
212 status = I18nErrorCode::INVALID_PARAM;
213 return "";
214 }
215 icu::UnicodeString displayname;
216 icuDisplaynames->regionDisplayName(code.c_str(), displayname);
217 std::string result = "";
218 if (!displayname.isBogus()) {
219 displayname.toUTF8String(result);
220 }
221 return result;
222 }
223
DisplayScript(const std::string & code)224 std::string DisplayNames::DisplayScript(const std::string &code)
225 {
226 if (!LocaleHelper::IsUnicodeScriptSubtag(code)) {
227 HILOG_ERROR_I18N("DisplayNames::DisplayScript: error code %{public}s.", code.c_str());
228 status = I18nErrorCode::INVALID_PARAM;
229 return "";
230 }
231 icu::UnicodeString displayname;
232 icuDisplaynames->scriptDisplayName(code.c_str(), displayname);
233 std::string result = "";
234 if (!displayname.isBogus()) {
235 displayname.toUTF8String(result);
236 }
237 return result;
238 }
239
DisplayCurrency(const std::string & code)240 std::string DisplayNames::DisplayCurrency(const std::string &code)
241 {
242 if (!LocaleHelper::IsWellFormedCurrencyCode(code)) {
243 HILOG_ERROR_I18N("DisplayNames::DisplayCurrency: error code %{public}s.", code.c_str());
244 status = I18nErrorCode::INVALID_PARAM;
245 return "";
246 }
247 icu::UnicodeString displayname;
248 icuDisplaynames->keyValueDisplayName("currency", code.c_str(), displayname);
249 std::string result = "";
250 if (!displayname.isBogus()) {
251 displayname.toUTF8String(result);
252 }
253 return result;
254 }
255
DisplayCalendar(const std::string & code)256 std::string DisplayNames::DisplayCalendar(const std::string &code)
257 {
258 if (!LocaleHelper::IsWellFormedCalendarCode(code)) {
259 HILOG_ERROR_I18N("DisplayNames::DisplayCalendar: error code %{public}s.", code.c_str());
260 status = I18nErrorCode::INVALID_PARAM;
261 return "";
262 }
263 std::string calendarStrCode = std::strcmp(code.c_str(), "gregory") == 0
264 ? "gregorian"
265 : std::strcmp(code.c_str(), "ethioaa") == 0
266 ? "ethiopic-amete-alem"
267 : code;
268 icu::UnicodeString displayname;
269 icuDisplaynames->keyValueDisplayName("calendar", calendarStrCode.c_str(), displayname);
270 std::string result = "";
271 if (!displayname.isBogus()) {
272 displayname.toUTF8String(result);
273 }
274 return result;
275 }
276
DisplayDatetimefield(const std::string & code)277 std::string DisplayNames::DisplayDatetimefield(const std::string &code)
278 {
279 auto fieldIter = ICU_DATE_PATTERN_FIELD.find(code);
280 if (fieldIter == ICU_DATE_PATTERN_FIELD.end()) {
281 HILOG_ERROR_I18N("DisplayNames::DisplayDatetimefield: error code %{public}s.", code.c_str());
282 status = I18nErrorCode::INVALID_PARAM;
283 return "";
284 }
285 UDateTimePatternField field = fieldIter->second;
286 UDateTimePGDisplayWidth width = UDATPG_WIDE;
287 auto icuDateWidthIter = ICU_DATE_DISPLAY_WIDTH.find(style);
288 if (icuDateWidthIter != ICU_DATE_DISPLAY_WIDTH.end()) {
289 width = icuDateWidthIter->second;
290 }
291
292 UErrorCode icuStatus = U_ZERO_ERROR;
293 icu::Locale locales = icuDisplaynames->getLocale();
294 std::unique_ptr<icu::DateTimePatternGenerator> generator(
295 icu::DateTimePatternGenerator::createInstance(locales, icuStatus));
296 if (U_FAILURE(icuStatus)) {
297 HILOG_ERROR_I18N("DisplayNames::DisplayDatetimefield: create DateTimePatternGenerator fail");
298 return "";
299 }
300 icu::UnicodeString displayname = generator->getFieldDisplayName(field, width);
301 std::string result = "";
302 displayname.toUTF8String(result);
303 return result;
304 }
305
ResolvedOptions()306 std::map<std::string, std::string> DisplayNames::ResolvedOptions()
307 {
308 std::map<std::string, std::string> options = {
309 {"locale", localeStr},
310 {"style", style},
311 {"type", type},
312 {"fallback", fallback},
313 {"languageDisplay", languageDisplay}
314 };
315 return options;
316 }
317
GetError() const318 I18nErrorCode DisplayNames::GetError() const
319 {
320 return status;
321 }
322
GetErrorMessage() const323 std::string DisplayNames::GetErrorMessage() const
324 {
325 if (status == I18nErrorCode::SUCCESS) {
326 return "";
327 }
328 if (type == "language") {
329 return "not match the language id";
330 } else if (type == "region") {
331 return "invalid region";
332 } else if (type == "script") {
333 return "invalid script";
334 } else if (type == "currency") {
335 return "not a wellformed currency code";
336 } else if (type == "calendar") {
337 return "invalid calendar";
338 } else if (type == "dateTimeField") {
339 return "invalid datetimefield";
340 }
341 HILOG_ERROR_I18N("DisplayNames::GetErrorMessage: this branch is unreachable");
342 return "";
343 }
344 } // namespace I18n
345 } // namespace Global
346 } // namespace OHOS