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 (status != U_ZERO_ERROR) {
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)) {
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)) {
95                 createSuccess = true;
96             }
97         }
98     }
99     numberingSystem = localeInfo->GetNumberingSystem();
100     if (numberingSystem == "") {
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     if (!createSuccess || !relativeUnits.count(unit)) {
125         return PseudoLocalizationProcessor("");
126     }
127     UErrorCode status = U_ZERO_ERROR;
128     icu::UnicodeString formattedTime;
129     std::string result;
130     if (!strcmp(numeric.c_str(), "always")) {
131         formattedTime = relativeTimeFormat->formatNumericToValue(number, relativeUnits[unit], status).toString(status);
132     } else {
133         formattedTime = relativeTimeFormat->formatToValue(number, relativeUnits[unit], status).toString(status);
134     }
135     formattedTime.toUTF8String(result);
136     return PseudoLocalizationProcessor(result);
137 }
138 
InsertInfo(std::vector<std::vector<std::string>> & timeVector,const std::string & unit,bool isInteger,const std::string & value)139 void RelativeTimeFormat::InsertInfo(std::vector<std::vector<std::string>> &timeVector,
140     const std::string &unit, bool isInteger, const std::string &value)
141 {
142     std::vector<std::string> info;
143     if (isInteger) {
144         info.push_back("integer");
145         info.push_back(value);
146         info.push_back(unit);
147     } else {
148         info.push_back("literal");
149         info.push_back(value);
150     }
151     timeVector.push_back(info);
152 }
153 
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)154 void RelativeTimeFormat::ProcessIntegerField(const std::map<size_t, size_t> &indexMap,
155     std::vector<std::vector<std::string>> &timeVector, size_t &startIndex, const std::string &unit,
156     const std::string &result)
157 {
158     for (auto iter = indexMap.begin(); iter != indexMap.end(); iter++) {
159         if (iter->first > startIndex) {
160             InsertInfo(timeVector, unit, true, result.substr(startIndex, iter->first - startIndex));
161             InsertInfo(timeVector, unit, true, result.substr(iter->first, iter->second - iter->first));
162             startIndex = iter->second;
163         }
164     }
165 }
166 
FormatToParts(double number,const std::string & unit,std::vector<std::vector<std::string>> & timeVector)167 void RelativeTimeFormat::FormatToParts(double number, const std::string &unit,
168     std::vector<std::vector<std::string>> &timeVector)
169 {
170     if (!createSuccess || !relativeUnits.count(unit)) {
171         return;
172     }
173     UErrorCode status = U_ZERO_ERROR;
174     std::string result;
175     icu::FormattedRelativeDateTime fmtRelativeTime;
176     if (numeric.empty() || !strcmp(numeric.c_str(), "always")) {
177         fmtRelativeTime = relativeTimeFormat->formatNumericToValue(number, relativeUnits[unit], status);
178     } else {
179         fmtRelativeTime = relativeTimeFormat->formatToValue(number, relativeUnits[unit], status);
180     }
181     fmtRelativeTime.toString(status).toUTF8String(result);
182     icu::ConstrainedFieldPosition constrainedPos;
183     constrainedPos.constrainCategory(UFIELD_CATEGORY_NUMBER);
184     size_t prevIndex = 0;
185     size_t length = result.length();
186     std::map<size_t, size_t> indexMap;
187     while (fmtRelativeTime.nextPosition(constrainedPos, status)) {
188         size_t startIndex = (size_t)constrainedPos.getStart();
189         if (constrainedPos.getCategory() == UFIELD_CATEGORY_NUMBER) {
190             if (constrainedPos.getField() == UNUM_GROUPING_SEPARATOR_FIELD) {
191                 indexMap.insert(std::make_pair(startIndex, (size_t)constrainedPos.getLimit()));
192                 continue;
193             }
194             if (startIndex > prevIndex) {
195                 InsertInfo(timeVector, unit, false, result.substr(prevIndex, startIndex - prevIndex));
196             }
197             if (constrainedPos.getField() == UNUM_INTEGER_FIELD) {
198                 ProcessIntegerField(indexMap, timeVector, startIndex, unit, result);
199             }
200             InsertInfo(timeVector, unit, true, result.substr(startIndex,
201                 (size_t)constrainedPos.getLimit() - startIndex));
202             prevIndex = (size_t)constrainedPos.getLimit();
203         }
204     }
205     if (prevIndex < length) {
206         InsertInfo(timeVector, unit, false, result.substr(prevIndex, length - prevIndex));
207     }
208 }
209 
GetResolvedOptions(std::map<std::string,std::string> & map)210 void RelativeTimeFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
211 {
212     map.insert(std::make_pair("locale", localeBaseName));
213     if (!styleString.empty()) {
214         map.insert(std::make_pair("style", styleString));
215     }
216     if (!numeric.empty()) {
217         map.insert(std::make_pair("numeric", numeric));
218     }
219     if (!numberingSystem.empty()) {
220         map.insert(std::make_pair("numberingSystem", numberingSystem));
221     }
222 }
223 
SetDefaultStyle()224 void RelativeTimeFormat::SetDefaultStyle()
225 {
226     char value[BUFFER_LEN];
227     int code = GetParameter(DEVICE_TYPE_NAME, "", value, BUFFER_LEN);
228     if (code > 0) {
229         std::string deviceType = value;
230         if (defaultFormatStyle.find(deviceType) != defaultFormatStyle.end()) {
231             styleString = defaultFormatStyle[deviceType];
232         }
233     }
234 }
235 } // namespace I18n
236 } // namespace Global
237 } // namespace OHOS
238