• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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