• 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 "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