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 "utils.h"
18 #include "parameter.h"
19 #include "relative_time_format.h"
20
21 namespace OHOS {
22 namespace Global {
23 namespace I18n {
24 const char* RelativeTimeFormat::DEVICE_TYPE_NAME = "const.product.devicetype";
25
26 std::unordered_map<std::string, UDateRelativeDateTimeFormatterStyle> RelativeTimeFormat::relativeFormatStyle = {
27 { "long", UDAT_STYLE_LONG },
28 { "short", UDAT_STYLE_SHORT },
29 { "narrow", UDAT_STYLE_NARROW }
30 };
31
32 std::unordered_map<std::string, std::string> RelativeTimeFormat::defaultFormatStyle = {
33 { "wearable", "narrow" },
34 { "liteWearable", "narrow" },
35 { "watch", "narrow" }
36 };
37
38 std::unordered_map<std::string, URelativeDateTimeUnit> RelativeTimeFormat::relativeUnits = {
39 { "second", UDAT_REL_UNIT_SECOND },
40 { "seconds", UDAT_REL_UNIT_SECOND },
41 { "minute", UDAT_REL_UNIT_MINUTE },
42 { "minutes", UDAT_REL_UNIT_MINUTE },
43 { "hour", UDAT_REL_UNIT_HOUR },
44 { "hours", UDAT_REL_UNIT_HOUR },
45 { "day", UDAT_REL_UNIT_DAY },
46 { "days", UDAT_REL_UNIT_DAY },
47 { "week", UDAT_REL_UNIT_WEEK },
48 { "weeks", UDAT_REL_UNIT_WEEK },
49 { "month", UDAT_REL_UNIT_MONTH },
50 { "months", UDAT_REL_UNIT_MONTH },
51 { "quarter", UDAT_REL_UNIT_QUARTER },
52 { "quarters", UDAT_REL_UNIT_QUARTER },
53 { "year", UDAT_REL_UNIT_YEAR },
54 { "years", UDAT_REL_UNIT_YEAR },
55 };
56
RelativeTimeFormat(const std::vector<std::string> & localeTags,std::map<std::string,std::string> & configs)57 RelativeTimeFormat::RelativeTimeFormat(const std::vector<std::string> &localeTags,
58 std::map<std::string, std::string> &configs)
59 {
60 SetDefaultStyle();
61 UErrorCode status = U_ZERO_ERROR;
62 ParseConfigs(configs);
63 for (size_t i = 0; i < localeTags.size(); i++) {
64 std::string curLocale = localeTags[i];
65 locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status);
66 if (U_FAILURE(status)) {
67 status = U_ZERO_ERROR;
68 continue;
69 }
70 if (LocaleInfo::allValidLocales.count(locale.getLanguage()) > 0) {
71 localeInfo = std::make_unique<LocaleInfo>(curLocale, configs);
72 if (!localeInfo->InitSuccess()) {
73 continue;
74 }
75 locale = localeInfo->GetLocale();
76 localeBaseName = localeInfo->GetBaseName();
77 relativeTimeFormat = std::make_unique<icu::RelativeDateTimeFormatter>(locale, nullptr, style,
78 UDISPCTX_CAPITALIZATION_NONE, status);
79 if (!U_SUCCESS(status) || !relativeTimeFormat) {
80 status = U_ZERO_ERROR;
81 continue;
82 }
83 createSuccess = true;
84 break;
85 }
86 }
87 if (!createSuccess) {
88 localeInfo = std::make_unique<LocaleInfo>(LocaleConfig::GetSystemLocale(), configs);
89 if (localeInfo->InitSuccess()) {
90 locale = localeInfo->GetLocale();
91 localeBaseName = localeInfo->GetBaseName();
92 relativeTimeFormat = std::make_unique<icu::RelativeDateTimeFormatter>(locale, nullptr, style,
93 UDISPCTX_CAPITALIZATION_NONE, status);
94 if (U_SUCCESS(status) && relativeTimeFormat) {
95 createSuccess = true;
96 }
97 }
98 }
99 numberingSystem = localeInfo->GetNumberingSystem();
100 if (numberingSystem.empty()) {
101 numberingSystem = "latn";
102 }
103 }
104
~RelativeTimeFormat()105 RelativeTimeFormat::~RelativeTimeFormat()
106 {
107 }
108
ParseConfigs(std::map<std::string,std::string> & configs)109 void RelativeTimeFormat::ParseConfigs(std::map<std::string, std::string> &configs)
110 {
111 if (configs.count("style") > 0) {
112 styleString = configs["style"];
113 }
114 if (relativeFormatStyle.count(styleString) > 0) {
115 style = relativeFormatStyle[styleString];
116 }
117 if (configs.count("numeric") > 0) {
118 numeric = configs["numeric"];
119 }
120 }
121
Format(double number,const std::string & unit)122 std::string RelativeTimeFormat::Format(double number, const std::string &unit)
123 {
124 icu::FormattedRelativeDateTime formattedTime = FormatToFormattedValue(number, unit);
125 UErrorCode status = U_ZERO_ERROR;
126 std::string result;
127 icu::UnicodeString formatResult = formattedTime.toString(status);
128 if (U_FAILURE(status)) {
129 result = "";
130 } else {
131 formatResult.toUTF8String(result);
132 }
133 return PseudoLocalizationProcessor(result);
134 }
135
InsertInfo(std::vector<std::vector<std::string>> & timeVector,const std::string & unit,bool isInteger,const std::string & value)136 void RelativeTimeFormat::InsertInfo(std::vector<std::vector<std::string>> &timeVector,
137 const std::string &unit, bool isInteger, const std::string &value)
138 {
139 std::vector<std::string> info;
140 if (isInteger) {
141 info.push_back("integer");
142 info.push_back(value);
143 info.push_back(unit);
144 } else {
145 info.push_back("literal");
146 info.push_back(value);
147 }
148 timeVector.push_back(info);
149 }
150
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)151 void RelativeTimeFormat::ProcessIntegerField(const std::map<size_t, size_t> &indexMap,
152 std::vector<std::vector<std::string>> &timeVector, size_t &startIndex, const std::string &unit,
153 const std::string &result)
154 {
155 for (auto iter = indexMap.begin(); iter != indexMap.end(); iter++) {
156 if (iter->first > startIndex) {
157 InsertInfo(timeVector, unit, true, result.substr(startIndex, iter->first - startIndex));
158 InsertInfo(timeVector, unit, true, result.substr(iter->first, iter->second - iter->first));
159 startIndex = iter->second;
160 }
161 }
162 }
163
FormatToParts(double number,const std::string & unit,std::vector<std::vector<std::string>> & timeVector)164 void RelativeTimeFormat::FormatToParts(double number, const std::string &unit,
165 std::vector<std::vector<std::string>> &timeVector)
166 {
167 icu::FormattedRelativeDateTime fmtRelativeTime = FormatToFormattedValue(number, unit);
168 UErrorCode status = U_ZERO_ERROR;
169 std::string result;
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
FormatToFormattedValue(double number,const std::string & unit)199 icu::FormattedRelativeDateTime RelativeTimeFormat::FormatToFormattedValue(double number, const std::string &unit)
200 {
201 if (!createSuccess || !relativeUnits.count(unit)) {
202 return {};
203 }
204 UErrorCode status = U_ZERO_ERROR;
205 icu::FormattedRelativeDateTime fmtRelativeTime;
206 if (numeric.compare("always") == 0) {
207 fmtRelativeTime = relativeTimeFormat->formatNumericToValue(number, relativeUnits[unit], status);
208 } else {
209 fmtRelativeTime = relativeTimeFormat->formatToValue(number, relativeUnits[unit], status);
210 }
211
212 if (U_FAILURE(status)) {
213 return {};
214 }
215
216 return fmtRelativeTime;
217 }
218
GetResolvedOptions(std::map<std::string,std::string> & map)219 void RelativeTimeFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
220 {
221 map.insert(std::make_pair("locale", localeBaseName));
222 if (!styleString.empty()) {
223 map.insert(std::make_pair("style", styleString));
224 }
225 if (!numeric.empty()) {
226 map.insert(std::make_pair("numeric", numeric));
227 }
228 if (!numberingSystem.empty()) {
229 map.insert(std::make_pair("numberingSystem", numberingSystem));
230 }
231 }
232
SetDefaultStyle()233 void RelativeTimeFormat::SetDefaultStyle()
234 {
235 char value[CONFIG_LEN];
236 int code = GetParameter(DEVICE_TYPE_NAME, "", value, CONFIG_LEN);
237 if (code > 0) {
238 std::string deviceType = value;
239 auto iter = defaultFormatStyle.find(deviceType);
240 styleString = iter != defaultFormatStyle.end() ? defaultFormatStyle[deviceType] : styleString;
241 }
242 }
243 } // namespace I18n
244 } // namespace Global
245 } // namespace OHOS
246