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 "i18n_hilog.h"
16 #include "locale_config.h"
17 #include "locale_helper.h"
18 #include "ohos/init_data.h"
19 #include "utils.h"
20 #include "parameter.h"
21 #include "relative_time_format.h"
22 #include "format_utils.h"
23
24 namespace OHOS {
25 namespace Global {
26 namespace I18n {
27 const char* RelativeTimeFormat::DEVICE_TYPE_NAME = "const.product.devicetype";
28
29 std::unordered_map<std::string, UDateRelativeDateTimeFormatterStyle> RelativeTimeFormat::relativeFormatStyle = {
30 { "long", UDAT_STYLE_LONG },
31 { "short", UDAT_STYLE_SHORT },
32 { "narrow", UDAT_STYLE_NARROW }
33 };
34
35 std::unordered_map<std::string, std::string> RelativeTimeFormat::defaultFormatStyle = {
36 { "wearable", "narrow" },
37 { "liteWearable", "narrow" },
38 { "watch", "narrow" }
39 };
40
41 std::unordered_map<std::string, URelativeDateTimeUnit> RelativeTimeFormat::relativeUnits = {
42 { "second", UDAT_REL_UNIT_SECOND },
43 { "seconds", UDAT_REL_UNIT_SECOND },
44 { "minute", UDAT_REL_UNIT_MINUTE },
45 { "minutes", UDAT_REL_UNIT_MINUTE },
46 { "hour", UDAT_REL_UNIT_HOUR },
47 { "hours", UDAT_REL_UNIT_HOUR },
48 { "day", UDAT_REL_UNIT_DAY },
49 { "days", UDAT_REL_UNIT_DAY },
50 { "week", UDAT_REL_UNIT_WEEK },
51 { "weeks", UDAT_REL_UNIT_WEEK },
52 { "month", UDAT_REL_UNIT_MONTH },
53 { "months", UDAT_REL_UNIT_MONTH },
54 { "quarter", UDAT_REL_UNIT_QUARTER },
55 { "quarters", UDAT_REL_UNIT_QUARTER },
56 { "year", UDAT_REL_UNIT_YEAR },
57 { "years", UDAT_REL_UNIT_YEAR },
58 };
59
60 std::unordered_map<std::string, std::string> RelativeTimeFormat::pluralUnitMap = {
61 { "seconds", "second" },
62 { "minutes", "minute" },
63 { "hours", "hour" },
64 { "days", "day" },
65 { "weeks", "week" },
66 { "months", "month" },
67 { "quarters", "quarter" },
68 { "years", "year" },
69 };
70
RelativeTimeFormat(const std::vector<std::string> & localeTags,std::map<std::string,std::string> & configs,bool fromArkTs)71 RelativeTimeFormat::RelativeTimeFormat(const std::vector<std::string> &localeTags,
72 std::map<std::string, std::string> &configs, bool fromArkTs)
73 {
74 createFromArkTs = fromArkTs;
75 I18nErrorCode errorCode = I18nErrorCode::SUCCESS;
76 std::vector<std::string> resolvedLocales =
77 LocaleHelper::SupportedLocalesOf(localeTags, configs, errorCode);
78 if (errorCode != I18nErrorCode::SUCCESS) {
79 HILOG_ERROR_I18N("RelativeTimeFormat: Get Supported Locales failed");
80 return;
81 }
82 SetRelativeTimeFormat(resolvedLocales, configs, fromArkTs);
83 }
84
CanonicalizeLocales(const std::vector<std::string> & localeTags,ErrorMessage & errorMsg)85 std::vector<std::string> RelativeTimeFormat::CanonicalizeLocales(
86 const std::vector<std::string>& localeTags, ErrorMessage& errorMsg)
87 {
88 if (localeTags.size() == 0) {
89 return {"en-US"};
90 }
91 if (localeTags.size() == 1 && localeTags[0] == "default") {
92 return {"en-US"};
93 }
94 I18nErrorCode status = I18nErrorCode::SUCCESS;
95 std::vector<std::string> resolvedLocales =
96 LocaleHelper::CanonicalizeLocaleList(localeTags, status);
97 if (status != I18nErrorCode::SUCCESS) {
98 errorMsg.type = ErrorType::RANGE_ERROR;
99 errorMsg.message = "invalid locale";
100 HILOG_ERROR_I18N("RelativeTimeFormat::CanonicalizeLocales CanonicalizeLocaleList failed.");
101 return {};
102 }
103 return resolvedLocales;
104 }
105
RelativeTimeFormat(const std::vector<std::string> & localeTags,std::map<std::string,std::string> & configs)106 RelativeTimeFormat::RelativeTimeFormat(const std::vector<std::string> &localeTags,
107 std::map<std::string, std::string> &configs)
108 {
109 SetDefaultStyle();
110 SetRelativeTimeFormat(localeTags, configs, false);
111 }
112
~RelativeTimeFormat()113 RelativeTimeFormat::~RelativeTimeFormat()
114 {
115 }
116
SetRelativeTimeFormat(const std::vector<std::string> & localeTags,std::map<std::string,std::string> & configs,bool fromArkTs)117 void RelativeTimeFormat::SetRelativeTimeFormat(const std::vector<std::string> &localeTags,
118 std::map<std::string, std::string> &configs, bool fromArkTs)
119 {
120 UErrorCode status = U_ZERO_ERROR;
121 ParseConfigs(configs);
122 for (size_t i = 0; i < localeTags.size(); i++) {
123 std::string curLocale = LocaleConfig::RemoveCustExtParam(localeTags[i]);
124 locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status);
125 if (U_FAILURE(status)) {
126 status = U_ZERO_ERROR;
127 continue;
128 }
129 localeBaseName = fromArkTs ? localeTags[i] : locale.getBaseName();
130 std::replace(localeBaseName.begin(), localeBaseName.end(), '_', '-');
131 relativeTimeFormat = std::make_unique<icu::RelativeDateTimeFormatter>(locale, nullptr, style,
132 UDISPCTX_CAPITALIZATION_NONE, status);
133 if (U_FAILURE(status) || !relativeTimeFormat) {
134 status = U_ZERO_ERROR;
135 continue;
136 }
137 createSuccess = true;
138 std::string value = LocaleConfig::QueryExtParam(curLocale, "nu", "-u-");
139 if (!value.empty()) {
140 numberingSystem = value;
141 intlNumberingSystem = value;
142 }
143 break;
144 }
145 CreateRelativeTimeFormatWithDefaultLocale(fromArkTs);
146 }
147
CreateRelativeTimeFormatWithDefaultLocale(bool fromArkTs)148 void RelativeTimeFormat::CreateRelativeTimeFormatWithDefaultLocale(bool fromArkTs)
149 {
150 if (!createSuccess) {
151 UErrorCode status = U_ZERO_ERROR;
152 std::string systemLocale = fromArkTs ?
153 LocaleHelper::DefaultLocale() : LocaleConfig::GetEffectiveLocale();
154 std::string curLocale = LocaleConfig::RemoveCustExtParam(systemLocale);
155 locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status);
156 if (U_FAILURE(status)) {
157 status = U_ZERO_ERROR;
158 return;
159 }
160 localeBaseName = locale.getBaseName();
161 std::replace(localeBaseName.begin(), localeBaseName.end(), '_', '-');
162 relativeTimeFormat = std::make_unique<icu::RelativeDateTimeFormatter>(locale, nullptr, style,
163 UDISPCTX_CAPITALIZATION_NONE, status);
164 if (U_SUCCESS(status) && relativeTimeFormat) {
165 createSuccess = true;
166 std::string value = LocaleConfig::QueryExtParam(curLocale, "nu", "-u-");
167 if (!value.empty()) {
168 numberingSystem = value;
169 intlNumberingSystem = value;
170 }
171 }
172 }
173 }
174
ParseConfigs(std::map<std::string,std::string> & configs)175 void RelativeTimeFormat::ParseConfigs(std::map<std::string, std::string> &configs)
176 {
177 if (configs.count("style") > 0) {
178 styleString = configs["style"];
179 }
180 if (relativeFormatStyle.count(styleString) > 0) {
181 style = relativeFormatStyle[styleString];
182 }
183 if (configs.count("numeric") > 0) {
184 numeric = configs["numeric"];
185 }
186 if (configs.count("localeMatcher") > 0) {
187 localeMatcher = configs["localeMatcher"];
188 }
189 }
190
Format(double number,const std::string & unit)191 std::string RelativeTimeFormat::Format(double number, const std::string &unit)
192 {
193 icu::FormattedRelativeDateTime formattedTime = FormatToFormattedValue(number, unit);
194 UErrorCode status = U_ZERO_ERROR;
195 std::string result;
196 icu::UnicodeString formatResult = formattedTime.toString(status);
197 if (U_SUCCESS(status)) {
198 formatResult.toUTF8String(result);
199 }
200 return PseudoLocalizationProcessor(result);
201 }
202
GetSingularUnit(const std::string & unit)203 std::string RelativeTimeFormat::GetSingularUnit(const std::string &unit)
204 {
205 std::string singularUnit(unit);
206 if (createFromArkTs && pluralUnitMap.find(unit) != pluralUnitMap.end()) {
207 singularUnit = pluralUnitMap[unit];
208 }
209 return singularUnit;
210 }
211
InsertInfo(std::vector<std::vector<std::string>> & timeVector,const std::string & unit,bool isInteger,const std::string & value)212 void RelativeTimeFormat::InsertInfo(std::vector<std::vector<std::string>> &timeVector,
213 const std::string &unit, bool isInteger, const std::string &value)
214 {
215 std::vector<std::string> info;
216 std::string singularUnit = GetSingularUnit(unit);
217 if (isInteger) {
218 info.push_back("integer");
219 info.push_back(value);
220 info.push_back(singularUnit);
221 } else {
222 info.push_back("literal");
223 info.push_back(value);
224 }
225 timeVector.push_back(info);
226 }
227
ProcessIntegerField(const std::map<size_t,size_t> & indexMap,std::vector<std::vector<std::string>> & timeVector,size_t & startIndex,const std::string & unit,const std::string & result)228 void RelativeTimeFormat::ProcessIntegerField(const std::map<size_t, size_t> &indexMap,
229 std::vector<std::vector<std::string>> &timeVector, size_t &startIndex, const std::string &unit,
230 const std::string &result)
231 {
232 for (auto iter = indexMap.begin(); iter != indexMap.end(); iter++) {
233 if (iter->first > startIndex) {
234 InsertInfo(timeVector, unit, true, result.substr(startIndex, iter->first - startIndex));
235 InsertInfo(timeVector, unit, true, result.substr(iter->first, iter->second - iter->first));
236 startIndex = iter->second;
237 }
238 }
239 }
240
FormatToParts(double number,const std::string & unit,std::vector<std::vector<std::string>> & timeVector)241 void RelativeTimeFormat::FormatToParts(double number, const std::string &unit,
242 std::vector<std::vector<std::string>> &timeVector)
243 {
244 icu::FormattedRelativeDateTime fmtRelativeTime = FormatToFormattedValue(number, unit);
245 UErrorCode status = U_ZERO_ERROR;
246 icu::UnicodeString fmtRelativeTimeStr = fmtRelativeTime.toString(status);
247 if (U_FAILURE(status)) {
248 HILOG_ERROR_I18N("RelativeTimeFormat::FormatToParts: Get formated string failed.");
249 return;
250 }
251 std::string result;
252 fmtRelativeTimeStr.toUTF8String(result);
253 if (createFromArkTs) {
254 IntlFormatToParts(fmtRelativeTime, result, timeVector, number, unit);
255 return;
256 }
257 icu::ConstrainedFieldPosition constrainedPos;
258 constrainedPos.constrainCategory(UFIELD_CATEGORY_NUMBER);
259 size_t prevIndex = 0;
260 size_t length = result.length();
261 std::map<size_t, size_t> indexMap;
262 while (fmtRelativeTime.nextPosition(constrainedPos, status)) {
263 if (U_FAILURE(status)) {
264 HILOG_ERROR_I18N("RelativeTimeFormat::FormatToParts: Get next position failed.");
265 return;
266 }
267 size_t startIndex = (size_t)constrainedPos.getStart();
268 if (constrainedPos.getCategory() == UFIELD_CATEGORY_NUMBER) {
269 if (constrainedPos.getField() == UNUM_GROUPING_SEPARATOR_FIELD) {
270 indexMap.insert(std::make_pair(startIndex, (size_t)constrainedPos.getLimit()));
271 continue;
272 }
273 if (startIndex > prevIndex) {
274 InsertInfo(timeVector, unit, false, result.substr(prevIndex, startIndex - prevIndex));
275 }
276 if (constrainedPos.getField() == UNUM_INTEGER_FIELD) {
277 ProcessIntegerField(indexMap, timeVector, startIndex, unit, result);
278 }
279 InsertInfo(timeVector, unit, true, result.substr(startIndex,
280 (size_t)constrainedPos.getLimit() - startIndex));
281 prevIndex = (size_t)constrainedPos.getLimit();
282 }
283 }
284 if (prevIndex < length) {
285 InsertInfo(timeVector, unit, false, result.substr(prevIndex, length - prevIndex));
286 }
287 }
288
IntlFormatToParts(icu::FormattedValue & fmtRelativeTime,const std::string & result,std::vector<std::vector<std::string>> & timeVector,double number,const std::string & unit)289 void RelativeTimeFormat::IntlFormatToParts(icu::FormattedValue &fmtRelativeTime, const std::string& result,
290 std::vector<std::vector<std::string>> &timeVector, double number, const std::string &unit)
291 {
292 UErrorCode status = U_ZERO_ERROR;
293 icu::ConstrainedFieldPosition constrainedPos;
294 constrainedPos.constrainCategory(UFIELD_CATEGORY_NUMBER);
295 int32_t prevIndex = 0;
296 size_t length = result.length();
297 std::map<int32_t, int32_t> indexMap;
298 while (fmtRelativeTime.nextPosition(constrainedPos, status)) {
299 if (U_FAILURE(status)) {
300 HILOG_ERROR_I18N("RelativeTimeFormat::FormatToParts: Get next position failed.");
301 return;
302 }
303 int32_t fieldId = constrainedPos.getField();
304 int32_t startIndex = constrainedPos.getStart();
305 int32_t limit = constrainedPos.getLimit();
306 if (constrainedPos.getField() == UNUM_GROUPING_SEPARATOR_FIELD) {
307 indexMap.insert(std::make_pair(startIndex, limit));
308 continue;
309 }
310 if (startIndex > prevIndex) {
311 std::string val = result.substr(prevIndex, startIndex - prevIndex);
312 timeVector.push_back(FormatPart("literal", val, ""));
313 }
314 for (auto iter = indexMap.begin(); iter != indexMap.end(); iter++) {
315 if (iter->first > startIndex) {
316 std::string typeString = FormatUtils::GetNumberFieldType("number", fieldId, number);
317 std::string valStr = result.substr(startIndex, iter->first - startIndex);
318 timeVector.push_back(FormatPart(typeString, valStr, unit));
319 std::string value = result.substr(iter->first, iter->second - iter->first);
320 timeVector.push_back(FormatPart("group", value, unit));
321 startIndex = iter->second;
322 }
323 }
324 std::string typeString = FormatUtils::GetNumberFieldType("number", fieldId, number);
325 std::string value = result.substr(startIndex, limit - startIndex);
326 timeVector.push_back(FormatPart(typeString, value, unit));
327 prevIndex = limit;
328 }
329 if (length > static_cast<size_t>(prevIndex)) {
330 std::string value = result.substr(prevIndex, length - static_cast<size_t>(prevIndex));
331 timeVector.push_back(FormatPart("literal", value, ""));
332 }
333 }
334
FormatPart(const std::string & type,const std::string & value,const std::string & unit)335 std::vector<std::string> RelativeTimeFormat::FormatPart(const std::string& type,
336 const std::string& value, const std::string& unit)
337 {
338 std::vector<std::string> info;
339 info.push_back(type);
340 info.push_back(value);
341 if (!unit.empty()) {
342 std::string singularUnit = GetSingularUnit(unit);
343 info.push_back(singularUnit);
344 }
345 return info;
346 }
347
FormatToFormattedValue(double number,const std::string & unit)348 icu::FormattedRelativeDateTime RelativeTimeFormat::FormatToFormattedValue(double number, const std::string &unit)
349 {
350 if (!createSuccess || relativeTimeFormat == nullptr || !relativeUnits.count(unit)) {
351 return {};
352 }
353 UErrorCode status = U_ZERO_ERROR;
354 icu::FormattedRelativeDateTime fmtRelativeTime;
355 if (numeric.compare("always") == 0) {
356 fmtRelativeTime = relativeTimeFormat->formatNumericToValue(number, relativeUnits[unit], status);
357 } else {
358 fmtRelativeTime = relativeTimeFormat->formatToValue(number, relativeUnits[unit], status);
359 }
360
361 if (U_FAILURE(status)) {
362 return {};
363 }
364 return fmtRelativeTime;
365 }
366
GetResolvedOptions(std::map<std::string,std::string> & map)367 void RelativeTimeFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
368 {
369 map.insert(std::make_pair("locale", localeBaseName));
370 if (!styleString.empty()) {
371 map.insert(std::make_pair("style", styleString));
372 }
373 if (!numeric.empty()) {
374 map.insert(std::make_pair("numeric", numeric));
375 }
376 if (createFromArkTs) {
377 std::string numSystem = intlNumberingSystem.empty() ? GetNumberSystemFromLocale() : intlNumberingSystem;
378 map.insert(std::make_pair("numberingSystem", numSystem));
379 } else if (!numberingSystem.empty()) {
380 map.insert(std::make_pair("numberingSystem", numberingSystem));
381 }
382 }
383
GetNumberSystemFromLocale()384 std::string RelativeTimeFormat::GetNumberSystemFromLocale()
385 {
386 UErrorCode status = U_ZERO_ERROR;
387 icu::Locale inLocale = icu::Locale::forLanguageTag(localeBaseName.data(), status);
388 if (U_FAILURE(status)) {
389 HILOG_ERROR_I18N("GetNumberSystemFromLocale: Create locale for %{public}s failed", localeBaseName.c_str());
390 return "";
391 }
392 std::unique_ptr<icu::NumberingSystem> numSystem =
393 std::unique_ptr<icu::NumberingSystem>(icu::NumberingSystem::createInstance(inLocale, status));
394 if (U_FAILURE(status) || numSystem == nullptr) {
395 HILOG_ERROR_I18N("GetNumberSystemFromLocale: Create icu::NumberingSystem failed");
396 return "";
397 }
398 const char* name = numSystem->getName();
399 if (name == nullptr) {
400 HILOG_ERROR_I18N("GetNumberSystemFromLocale: icu::NumberingSystem name is nullptr");
401 return "";
402 }
403 std::string numberSystem(name);
404 return numberSystem;
405 }
406
SupportedLocalesOf(const std::vector<std::string> & requestLocales,const std::map<std::string,std::string> & configs,I18nErrorCode & status)407 std::vector<std::string> RelativeTimeFormat::SupportedLocalesOf(
408 const std::vector<std::string> &requestLocales,
409 const std::map<std::string, std::string> &configs, I18nErrorCode &status)
410 {
411 return LocaleHelper::SupportedLocalesOf(requestLocales, configs, status);
412 }
413
SetDefaultStyle()414 void RelativeTimeFormat::SetDefaultStyle()
415 {
416 char value[CONFIG_LEN];
417 int code = GetParameter(DEVICE_TYPE_NAME, "", value, CONFIG_LEN);
418 if (code > 0) {
419 std::string deviceType = value;
420 auto iter = defaultFormatStyle.find(deviceType);
421 styleString = iter != defaultFormatStyle.end() ? defaultFormatStyle[deviceType] : styleString;
422 }
423 }
424 } // namespace I18n
425 } // namespace Global
426 } // namespace OHOS
427