* Copyright (c) 2021-2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "plural_rules.h"
#include <stringpiece.h>
#include "algorithm"
#include "locale_config.h"
#include "locid.h"
#include "log.h"
#include "plural_rules.h"
#include "map"
#include "set"
#include "string"
#include "unicode/unistr.h"
#include "unicode/upluralrules.h"
#include "utility"
#include "utils.h"
#include "utypes.h"
#include "vector"
namespace OHOS {
namespace Global {
namespace I18n {
std::string PluralRules::ParseOption(std::map<std::string, std::string> &options, const std::string &key)
{
std::map<std::string, std::string>::iterator it = options.find(key);
if (it != options.end()) {
return it->second;
} else {
return "";
}
}
int PluralRules::GetValidInteger(std::string &integerStr, int minValue, int maxValue, int defaultValue)
{
int status = 0;
int validInteger = ConvertString2Int(integerStr, status);
if (status < 0) {
validInteger = defaultValue;
}
if (validInteger < minValue) {
validInteger = minValue;
}
if (validInteger > maxValue) {
validInteger = maxValue;
}
return validInteger;
}
void PluralRules::ParseAllOptions(std::map<std::string, std::string> &options)
{
localeMatcher = ParseOption(options, "localeMatcher");
localeMatcher = (localeMatcher == "") ? "best fit" : localeMatcher;
type = ParseOption(options, "type");
type = (type == "") ? "cardinal" : type;
std::string minIntegerStr = ParseOption(options, "minimumIntegerDigits");
minInteger = GetValidInteger(minIntegerStr, 1, 21, 1);
minFraction = 0;
maxFraction = 0;
std::string minFractionStr = ParseOption(options, "minimumFractionDigits");
std::string maxFractionStr = ParseOption(options, "maximumFractionDigits");
std::string minSignificantStr = ParseOption(options, "minimumSignificantDigits");
std::string maxSignificantStr = ParseOption(options, "maximumSignificantDigits");
if (minSignificantStr != "" || maxSignificantStr != "") {
minSignificant = GetValidInteger(minSignificantStr, 1, 21, 1);
maxSignificant = GetValidInteger(maxSignificantStr, 1, 21, 21);
} else {
minSignificant = 0;
maxSignificant = 0;
if (minFractionStr != "" || maxFractionStr != "") {
minFraction = GetValidInteger(minFractionStr, 0, 20, 0);
int maxFractionDefault = std::max(3, minFraction);
int maxFractionMin = std::max(1, minFraction);
maxFraction = GetValidInteger(maxFractionStr, maxFractionMin, 21, maxFractionDefault);
} else {
minFraction = 0;
maxFraction = 3;
}
}
}
void PluralRules::InitPluralRules(std::vector<std::string> &localeTags,
std::map<std::string, std::string> &options)
{
UPluralType uPluralType = (type == "cardinal") ? UPLURAL_TYPE_CARDINAL : UPLURAL_TYPE_ORDINAL;
UErrorCode status = UErrorCode::U_ZERO_ERROR;
localeTags.push_back(LocaleConfig::GetSystemLocale());
for (size_t i = 0; i < localeTags.size(); i++) {
std::string curLocale = localeTags[i];
locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status);
if (status != U_ZERO_ERROR) {
status = U_ZERO_ERROR;
continue;
}
if (LocaleInfo::GetValidLocales().count(locale.getLanguage()) > 0) {
localeInfo = std::make_unique<LocaleInfo>(curLocale, options);
if (!localeInfo->InitSuccess()) {
continue;
}
locale = localeInfo->GetLocale();
localeStr = localeInfo->GetBaseName();
pluralRules = icu::PluralRules::forLocale(locale, uPluralType, status);
if (status != UErrorCode::U_ZERO_ERROR || !pluralRules) {
continue;
}
createSuccess = true;
break;
}
}
if (status != UErrorCode::U_ZERO_ERROR || !pluralRules) {
LOGE("PluralRules object created failed");
return;
}
}
void PluralRules::InitNumberFormatter()
{
numberFormatter = icu::number::NumberFormatter::withLocale(locale).roundingMode(UNUM_ROUND_HALFUP);
if (minInteger > 1) {
numberFormatter = numberFormatter.integerWidth(icu::number::IntegerWidth::zeroFillTo(minInteger));
}
if (minSignificant >= 0) {
if (minSignificant > 0) {
icu::number::Precision precision = icu::number::Precision::minMaxSignificantDigits(minSignificant,
maxSignificant);
numberFormatter = numberFormatter.precision(precision);
} else {
icu::number::Precision precision = icu::number::Precision::minMaxFraction(minFraction, maxFraction);
numberFormatter = numberFormatter.precision(precision);
}
}
}
PluralRules::PluralRules(std::vector<std::string> &localeTags, std::map<std::string, std::string> &options)
{
ParseAllOptions(options);
InitPluralRules(localeTags, options);
InitNumberFormatter();
}
PluralRules::~PluralRules()
{
if (!pluralRules) {
delete pluralRules;
pluralRules = nullptr;
}
}
std::string PluralRules::Select(double number)
{
if (!createSuccess || pluralRules == nullptr) {
return "other";
}
UErrorCode status = UErrorCode::U_ZERO_ERROR;
icu::number::FormattedNumber formattedNumber = numberFormatter.formatDouble(number, status);
if (status != UErrorCode::U_ZERO_ERROR) {
status = UErrorCode::U_ZERO_ERROR;
formattedNumber = numberFormatter.formatDouble(number, status);
}
icu::UnicodeString unicodeString = pluralRules->select(formattedNumber, status);
std::string result;
unicodeString.toUTF8String(result);
return result;
}
}
}
}