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