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 "locale_config.h"
16 #include "ohos/init_data.h"
17 #include "relative_time_format.h"
18
19 namespace OHOS {
20 namespace Global {
21 namespace I18n {
22 std::unordered_map<std::string, UDateRelativeDateTimeFormatterStyle> RelativeTimeFormat::relativeFormatStyle = {
23 { "long", UDAT_STYLE_LONG },
24 { "short", UDAT_STYLE_SHORT },
25 { "narrow", UDAT_STYLE_NARROW }
26 };
27
28 std::unordered_map<std::string, URelativeDateTimeUnit> RelativeTimeFormat::relativeUnits = {
29 { "second", UDAT_REL_UNIT_SECOND },
30 { "seconds", UDAT_REL_UNIT_SECOND },
31 { "minute", UDAT_REL_UNIT_MINUTE },
32 { "minutes", UDAT_REL_UNIT_MINUTE },
33 { "hour", UDAT_REL_UNIT_HOUR },
34 { "hours", UDAT_REL_UNIT_HOUR },
35 { "day", UDAT_REL_UNIT_DAY },
36 { "days", UDAT_REL_UNIT_DAY },
37 { "week", UDAT_REL_UNIT_WEEK },
38 { "weeks", UDAT_REL_UNIT_WEEK },
39 { "month", UDAT_REL_UNIT_MONTH },
40 { "months", UDAT_REL_UNIT_MONTH },
41 { "quarter", UDAT_REL_UNIT_QUARTER },
42 { "quarters", UDAT_REL_UNIT_QUARTER },
43 { "year", UDAT_REL_UNIT_YEAR },
44 { "years", UDAT_REL_UNIT_YEAR },
45 };
46
RelativeTimeFormat(const std::vector<std::string> & localeTags,std::map<std::string,std::string> & configs)47 RelativeTimeFormat::RelativeTimeFormat(const std::vector<std::string> &localeTags,
48 std::map<std::string, std::string> &configs)
49 {
50 UErrorCode status = U_ZERO_ERROR;
51 ParseConfigs(configs);
52 for (size_t i = 0; i < localeTags.size(); i++) {
53 std::string curLocale = localeTags[i];
54 locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status);
55 if (status != U_ZERO_ERROR) {
56 status = U_ZERO_ERROR;
57 continue;
58 }
59 if (LocaleInfo::allValidLocales.count(locale.getLanguage()) > 0) {
60 localeInfo = std::make_unique<LocaleInfo>(curLocale, configs);
61 if (!localeInfo->InitSuccess()) {
62 continue;
63 }
64 locale = localeInfo->GetLocale();
65 localeBaseName = localeInfo->GetBaseName();
66 relativeTimeFormat = std::make_unique<icu::RelativeDateTimeFormatter>(locale, nullptr, style,
67 UDISPCTX_CAPITALIZATION_NONE, status);
68 if (!U_SUCCESS(status)) {
69 status = U_ZERO_ERROR;
70 continue;
71 }
72 createSuccess = true;
73 break;
74 }
75 }
76 if (!createSuccess) {
77 localeInfo = std::make_unique<LocaleInfo>(LocaleConfig::GetSystemLocale(), configs);
78 if (localeInfo->InitSuccess()) {
79 locale = localeInfo->GetLocale();
80 localeBaseName = localeInfo->GetBaseName();
81 relativeTimeFormat = std::make_unique<icu::RelativeDateTimeFormatter>(locale, nullptr, style,
82 UDISPCTX_CAPITALIZATION_NONE, status);
83 if (U_SUCCESS(status)) {
84 createSuccess = true;
85 }
86 }
87 }
88 numberingSystem = localeInfo->GetNumberingSystem();
89 if (numberingSystem == "") {
90 numberingSystem = "latn";
91 }
92 }
93
~RelativeTimeFormat()94 RelativeTimeFormat::~RelativeTimeFormat()
95 {
96 }
97
ParseConfigs(std::map<std::string,std::string> & configs)98 void RelativeTimeFormat::ParseConfigs(std::map<std::string, std::string> &configs)
99 {
100 if (configs.count("style") > 0) {
101 styleString = configs["style"];
102 if (relativeFormatStyle.count(styleString) > 0) {
103 style = relativeFormatStyle[styleString];
104 }
105 }
106 if (configs.count("numeric") > 0) {
107 numeric = configs["numeric"];
108 }
109 }
110
Format(double number,const std::string & unit)111 std::string RelativeTimeFormat::Format(double number, const std::string &unit)
112 {
113 if (!createSuccess || !relativeUnits.count(unit)) {
114 return "";
115 }
116 UErrorCode status = U_ZERO_ERROR;
117 icu::UnicodeString formattedTime;
118 std::string result;
119 if (!strcmp(numeric.c_str(), "always")) {
120 formattedTime = relativeTimeFormat->formatNumericToValue(number, relativeUnits[unit], status).toString(status);
121 } else {
122 formattedTime = relativeTimeFormat->formatToValue(number, relativeUnits[unit], status).toString(status);
123 }
124 formattedTime.toUTF8String(result);
125 return result;
126 }
127
InsertInfo(std::vector<std::vector<std::string>> & timeVector,const std::string & unit,bool isInteger,const std::string & value)128 void RelativeTimeFormat::InsertInfo(std::vector<std::vector<std::string>> &timeVector,
129 const std::string &unit, bool isInteger, const std::string &value)
130 {
131 std::vector<std::string> info;
132 if (isInteger) {
133 info.push_back("integer");
134 info.push_back(value);
135 info.push_back(unit);
136 } else {
137 info.push_back("literal");
138 info.push_back(value);
139 }
140 timeVector.push_back(info);
141 }
142
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)143 void RelativeTimeFormat::ProcessIntegerField(const std::map<size_t, size_t> &indexMap,
144 std::vector<std::vector<std::string>> &timeVector, size_t &startIndex, const std::string &unit,
145 const std::string &result)
146 {
147 for (auto iter = indexMap.begin(); iter != indexMap.end(); iter++) {
148 if (iter->first > startIndex) {
149 InsertInfo(timeVector, unit, true, result.substr(startIndex, iter->first - startIndex));
150 InsertInfo(timeVector, unit, true, result.substr(iter->first, iter->second - iter->first));
151 startIndex = iter->second;
152 }
153 }
154 }
155
FormatToParts(double number,const std::string & unit,std::vector<std::vector<std::string>> & timeVector)156 void RelativeTimeFormat::FormatToParts(double number, const std::string &unit,
157 std::vector<std::vector<std::string>> &timeVector)
158 {
159 if (!createSuccess || !relativeUnits.count(unit)) {
160 return;
161 }
162 UErrorCode status = U_ZERO_ERROR;
163 std::string result;
164 icu::FormattedRelativeDateTime fmtRelativeTime;
165 if (numeric.empty() || !strcmp(numeric.c_str(), "always")) {
166 fmtRelativeTime = relativeTimeFormat->formatNumericToValue(number, relativeUnits[unit], status);
167 } else {
168 fmtRelativeTime = relativeTimeFormat->formatToValue(number, relativeUnits[unit], status);
169 }
170 fmtRelativeTime.toString(status).toUTF8String(result);
171 icu::ConstrainedFieldPosition constrainedPos;
172 constrainedPos.constrainCategory(UFIELD_CATEGORY_NUMBER);
173 size_t prevIndex = 0;
174 size_t length = result.length();
175 std::map<size_t, size_t> indexMap;
176 while (fmtRelativeTime.nextPosition(constrainedPos, status)) {
177 size_t startIndex = (size_t)constrainedPos.getStart();
178 if (constrainedPos.getCategory() == UFIELD_CATEGORY_NUMBER) {
179 if (constrainedPos.getField() == UNUM_GROUPING_SEPARATOR_FIELD) {
180 indexMap.insert(std::make_pair(startIndex, (size_t)constrainedPos.getLimit()));
181 continue;
182 }
183 if (startIndex > prevIndex) {
184 InsertInfo(timeVector, unit, false, result.substr(prevIndex, startIndex - prevIndex));
185 }
186 if (constrainedPos.getField() == UNUM_INTEGER_FIELD) {
187 ProcessIntegerField(indexMap, timeVector, startIndex, unit, result);
188 }
189 InsertInfo(timeVector, unit, true, result.substr(startIndex,
190 (size_t)constrainedPos.getLimit() - startIndex));
191 prevIndex = (size_t)constrainedPos.getLimit();
192 }
193 }
194 if (prevIndex < length) {
195 InsertInfo(timeVector, unit, false, result.substr(prevIndex, length - prevIndex));
196 }
197 }
198
GetResolvedOptions(std::map<std::string,std::string> & map)199 void RelativeTimeFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
200 {
201 map.insert(std::make_pair("locale", localeBaseName));
202 if (!styleString.empty()) {
203 map.insert(std::make_pair("style", styleString));
204 }
205 if (!numeric.empty()) {
206 map.insert(std::make_pair("numeric", numeric));
207 }
208 if (!numberingSystem.empty()) {
209 map.insert(std::make_pair("numberingSystem", numberingSystem));
210 }
211 }
212 } // namespace I18n
213 } // namespace Global
214 } // namespace OHOS
215