mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 06:25:30 +00:00
parent
0e6a6f6fff
commit
6942013a38
16 changed files with 2728 additions and 104 deletions
|
@ -88,6 +88,7 @@ ucnv_u7.o ucnv_u8.o ucnv_u16.o ucnv_u32.o ucnvscsu.o ucnvbocu.o \
|
|||
ucnv_ext.o ucnvmbcs.o ucnv2022.o ucnvhz.o ucnv_lmb.o ucnvisci.o ucnvdisp.o ucnv_set.o ucnv_ct.o \
|
||||
resource.o uresbund.o ures_cnv.o uresdata.o resbund.o resbund_cnv.o \
|
||||
ucurr.o \
|
||||
localebuilder.o \
|
||||
messagepattern.o ucat.o locmap.o uloc.o locid.o locutil.o locavailable.o locdispnames.o locdspnm.o loclikely.o locresdata.o \
|
||||
bytestream.o stringpiece.o bytesinkutil.o \
|
||||
stringtriebuilder.o bytestriebuilder.o \
|
||||
|
|
|
@ -256,6 +256,7 @@
|
|||
<ClCompile Include="uresdata.cpp" />
|
||||
<ClCompile Include="resource.cpp" />
|
||||
<ClCompile Include="ucurr.cpp" />
|
||||
<ClCompile Include="localebuilder.cpp" />
|
||||
<ClCompile Include="caniter.cpp" />
|
||||
<ClCompile Include="filterednormalizer2.cpp" />
|
||||
<ClCompile Include="loadednormalizer2impl.cpp" />
|
||||
|
@ -445,6 +446,7 @@
|
|||
<ClInclude Include="ustr_imp.h" />
|
||||
<ClInclude Include="static_unicode_sets.h" />
|
||||
<ClInclude Include="capi_helper.h" />
|
||||
<ClInclude Include="unicode\localebuilder.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="common.rc" />
|
||||
|
|
|
@ -361,6 +361,9 @@
|
|||
<ClCompile Include="resource.cpp">
|
||||
<Filter>locales & resources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="localebuilder.cpp">
|
||||
<Filter>locales & resources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="caniter.cpp">
|
||||
<Filter>normalization</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1225,5 +1228,8 @@
|
|||
<CustomBuild Include="unicode\stringoptions.h">
|
||||
<Filter>strings</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="unicode\localebuilder.h">
|
||||
<Filter>locales & resources</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -383,6 +383,7 @@
|
|||
<ClCompile Include="uresdata.cpp" />
|
||||
<ClCompile Include="resource.cpp" />
|
||||
<ClCompile Include="ucurr.cpp" />
|
||||
<ClCompile Include="localebuilder.cpp" />
|
||||
<ClCompile Include="caniter.cpp" />
|
||||
<ClCompile Include="filterednormalizer2.cpp" />
|
||||
<ClCompile Include="loadednormalizer2impl.cpp" />
|
||||
|
@ -572,6 +573,7 @@
|
|||
<ClInclude Include="ustr_imp.h" />
|
||||
<ClInclude Include="static_unicode_sets.h" />
|
||||
<ClInclude Include="capi_helper.h" />
|
||||
<ClInclude Include="unicode\localebuilder.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="common.rc" />
|
||||
|
|
436
icu4c/source/common/localebuilder.cpp
Normal file
436
icu4c/source/common/localebuilder.cpp
Normal file
|
@ -0,0 +1,436 @@
|
|||
// © 2019 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "bytesinkutil.h" // CharStringByteSink
|
||||
#include "charstr.h"
|
||||
#include "cstring.h"
|
||||
#include "ulocimp.h"
|
||||
#include "unicode/localebuilder.h"
|
||||
#include "unicode/locid.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
#define UPRV_ISDIGIT(c) (((c) >= '0') && ((c) <= '9'))
|
||||
#define UPRV_ISALPHANUM(c) (uprv_isASCIILetter(c) || UPRV_ISDIGIT(c) )
|
||||
|
||||
const char* kAttributeKey = "attribute";
|
||||
|
||||
static bool _isExtensionSubtags(char key, const char* s, int32_t len) {
|
||||
switch (uprv_tolower(key)) {
|
||||
case 'u':
|
||||
return ultag_isUnicodeExtensionSubtags(s, len);
|
||||
case 't':
|
||||
return ultag_isTransformedExtensionSubtags(s, len);
|
||||
case 'x':
|
||||
return ultag_isPrivateuseValueSubtags(s, len);
|
||||
default:
|
||||
return ultag_isExtensionSubtags(s, len);
|
||||
}
|
||||
}
|
||||
|
||||
LocaleBuilder::LocaleBuilder() : UObject(), status_(U_ZERO_ERROR), language_(),
|
||||
script_(), region_(), variant_(nullptr), extensions_(nullptr)
|
||||
{
|
||||
language_[0] = 0;
|
||||
script_[0] = 0;
|
||||
region_[0] = 0;
|
||||
}
|
||||
|
||||
LocaleBuilder::~LocaleBuilder()
|
||||
{
|
||||
delete variant_;
|
||||
delete extensions_;
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::setLocale(const Locale& locale)
|
||||
{
|
||||
clear();
|
||||
setLanguage(locale.getLanguage());
|
||||
setScript(locale.getScript());
|
||||
setRegion(locale.getCountry());
|
||||
setVariant(locale.getVariant());
|
||||
extensions_ = locale.clone();
|
||||
if (extensions_ == nullptr) {
|
||||
status_ = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::setLanguageTag(StringPiece tag)
|
||||
{
|
||||
Locale l = Locale::forLanguageTag(tag, status_);
|
||||
if (U_FAILURE(status_)) { return *this; }
|
||||
// Because setLocale will reset status_ we need to return
|
||||
// first if we have error in forLanguageTag.
|
||||
setLocale(l);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static void setField(StringPiece input, char* dest, UErrorCode& errorCode,
|
||||
UBool (*test)(const char*, int32_t)) {
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
if (input.empty()) {
|
||||
dest[0] = '\0';
|
||||
} else if (test(input.data(), input.length())) {
|
||||
uprv_memcpy(dest, input.data(), input.length());
|
||||
dest[input.length()] = '\0';
|
||||
} else {
|
||||
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::setLanguage(StringPiece language)
|
||||
{
|
||||
setField(language, language_, status_, &ultag_isLanguageSubtag);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::setScript(StringPiece script)
|
||||
{
|
||||
setField(script, script_, status_, &ultag_isScriptSubtag);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::setRegion(StringPiece region)
|
||||
{
|
||||
setField(region, region_, status_, &ultag_isRegionSubtag);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static void transform(char* data, int32_t len) {
|
||||
for (int32_t i = 0; i < len; i++, data++) {
|
||||
if (*data == '_') {
|
||||
*data = '-';
|
||||
} else {
|
||||
*data = uprv_tolower(*data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::setVariant(StringPiece variant)
|
||||
{
|
||||
if (U_FAILURE(status_)) { return *this; }
|
||||
if (variant.empty()) {
|
||||
delete variant_;
|
||||
variant_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
CharString* new_variant = new CharString(variant, status_);
|
||||
if (U_FAILURE(status_)) { return *this; }
|
||||
if (new_variant == nullptr) {
|
||||
status_ = U_MEMORY_ALLOCATION_ERROR;
|
||||
return *this;
|
||||
}
|
||||
transform(new_variant->data(), new_variant->length());
|
||||
if (!ultag_isVariantSubtags(new_variant->data(), new_variant->length())) {
|
||||
delete new_variant;
|
||||
status_ = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return *this;
|
||||
}
|
||||
delete variant_;
|
||||
variant_ = new_variant;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static bool
|
||||
_isKeywordValue(const char* key, const char* value, int32_t value_len)
|
||||
{
|
||||
if (key[1] == '\0') {
|
||||
// one char key
|
||||
return (UPRV_ISALPHANUM(uprv_tolower(key[0])) &&
|
||||
_isExtensionSubtags(key[0], value, value_len));
|
||||
} else if (uprv_strcmp(key, kAttributeKey) == 0) {
|
||||
// unicode attributes
|
||||
return ultag_isUnicodeLocaleAttributes(value, value_len);
|
||||
}
|
||||
// otherwise: unicode extension value
|
||||
// We need to convert from legacy key/value to unicode
|
||||
// key/value
|
||||
const char* unicode_locale_key = uloc_toUnicodeLocaleKey(key);
|
||||
const char* unicode_locale_type = uloc_toUnicodeLocaleType(key, value);
|
||||
|
||||
return unicode_locale_key && unicode_locale_type &&
|
||||
ultag_isUnicodeLocaleKey(unicode_locale_key, -1) &&
|
||||
ultag_isUnicodeLocaleType(unicode_locale_type, -1);
|
||||
}
|
||||
|
||||
static void
|
||||
_copyExtensions(const Locale& from, Locale* to, bool validate, UErrorCode& errorCode)
|
||||
{
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
LocalPointer<icu::StringEnumeration> iter(from.createKeywords(errorCode));
|
||||
if (U_FAILURE(errorCode) || iter.isNull()) { return; }
|
||||
const char* key;
|
||||
while ((key = iter->next(nullptr, errorCode)) != nullptr) {
|
||||
CharString value;
|
||||
CharStringByteSink sink(&value);
|
||||
from.getKeywordValue(key, sink, errorCode);
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
if (uprv_strcmp(key, kAttributeKey) == 0) {
|
||||
transform(value.data(), value.length());
|
||||
}
|
||||
if (validate &&
|
||||
!_isKeywordValue(key, value.data(), value.length())) {
|
||||
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
to->setKeywordValue(key, value.data(), errorCode);
|
||||
if (U_FAILURE(errorCode)) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
void static
|
||||
_clearUAttributesAndKeyType(Locale* locale, UErrorCode& errorCode)
|
||||
{
|
||||
// Clear Unicode attributes
|
||||
locale->setKeywordValue(kAttributeKey, "", errorCode);
|
||||
|
||||
// Clear all Unicode keyword values
|
||||
LocalPointer<icu::StringEnumeration> iter(locale->createUnicodeKeywords(errorCode));
|
||||
if (U_FAILURE(errorCode) || iter.isNull()) { return; }
|
||||
const char* key;
|
||||
while ((key = iter->next(nullptr, errorCode)) != nullptr) {
|
||||
locale->setUnicodeKeywordValue(key, nullptr, errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_setUnicodeExtensions(Locale* locale, const CharString& value, UErrorCode& errorCode)
|
||||
{
|
||||
// Add the unicode extensions to extensions_
|
||||
CharString locale_str("und-u-", errorCode);
|
||||
locale_str.append(value, errorCode);
|
||||
_copyExtensions(
|
||||
Locale::forLanguageTag(locale_str.data(), errorCode),
|
||||
locale, false, errorCode);
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::setExtension(char key, StringPiece value)
|
||||
{
|
||||
if (U_FAILURE(status_)) { return *this; }
|
||||
if (!UPRV_ISALPHANUM(key)) {
|
||||
status_ = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return *this;
|
||||
}
|
||||
CharString value_str(value, status_);
|
||||
if (U_FAILURE(status_)) { return *this; }
|
||||
transform(value_str.data(), value_str.length());
|
||||
if (!value_str.isEmpty() &&
|
||||
!_isExtensionSubtags(key, value_str.data(), value_str.length())) {
|
||||
status_ = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return *this;
|
||||
}
|
||||
if (extensions_ == nullptr) {
|
||||
extensions_ = new Locale();
|
||||
if (extensions_ == nullptr) {
|
||||
status_ = U_MEMORY_ALLOCATION_ERROR;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
if (uprv_tolower(key) != 'u') {
|
||||
// for t, x and others extension.
|
||||
extensions_->setKeywordValue(StringPiece(&key, 1), value_str.data(),
|
||||
status_);
|
||||
return *this;
|
||||
}
|
||||
_clearUAttributesAndKeyType(extensions_, status_);
|
||||
if (U_FAILURE(status_)) { return *this; }
|
||||
if (!value.empty()) {
|
||||
_setUnicodeExtensions(extensions_, value_str, status_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::setUnicodeLocaleKeyword(
|
||||
StringPiece key, StringPiece type)
|
||||
{
|
||||
if (U_FAILURE(status_)) { return *this; }
|
||||
if (!ultag_isUnicodeLocaleKey(key.data(), key.length()) ||
|
||||
(!type.empty() &&
|
||||
!ultag_isUnicodeLocaleType(type.data(), type.length()))) {
|
||||
status_ = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return *this;
|
||||
}
|
||||
if (extensions_ == nullptr) {
|
||||
extensions_ = new Locale();
|
||||
}
|
||||
if (extensions_ == nullptr) {
|
||||
status_ = U_MEMORY_ALLOCATION_ERROR;
|
||||
return *this;
|
||||
}
|
||||
extensions_->setUnicodeKeywordValue(key, type, status_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::addUnicodeLocaleAttribute(
|
||||
StringPiece value)
|
||||
{
|
||||
CharString value_str(value, status_);
|
||||
if (U_FAILURE(status_)) { return *this; }
|
||||
transform(value_str.data(), value_str.length());
|
||||
if (!ultag_isUnicodeLocaleAttribute(value_str.data(), value_str.length())) {
|
||||
status_ = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return *this;
|
||||
}
|
||||
if (extensions_ == nullptr) {
|
||||
extensions_ = new Locale();
|
||||
if (extensions_ == nullptr) {
|
||||
status_ = U_MEMORY_ALLOCATION_ERROR;
|
||||
return *this;
|
||||
}
|
||||
extensions_->setKeywordValue(kAttributeKey, value_str.data(), status_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CharString attributes;
|
||||
CharStringByteSink sink(&attributes);
|
||||
UErrorCode localErrorCode = U_ZERO_ERROR;
|
||||
extensions_->getKeywordValue(kAttributeKey, sink, localErrorCode);
|
||||
if (U_FAILURE(localErrorCode)) {
|
||||
CharString new_attributes(value_str.data(), status_);
|
||||
// No attributes, set the attribute.
|
||||
extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
transform(attributes.data(),attributes.length());
|
||||
const char* start = attributes.data();
|
||||
const char* limit = attributes.data() + attributes.length();
|
||||
CharString new_attributes;
|
||||
bool inserted = false;
|
||||
while (start < limit) {
|
||||
if (!inserted) {
|
||||
int cmp = uprv_strcmp(start, value_str.data());
|
||||
if (cmp == 0) { return *this; } // Found it in attributes: Just return
|
||||
if (cmp > 0) {
|
||||
if (!new_attributes.isEmpty()) new_attributes.append('_', status_);
|
||||
new_attributes.append(value_str.data(), status_);
|
||||
inserted = true;
|
||||
}
|
||||
}
|
||||
if (!new_attributes.isEmpty()) {
|
||||
new_attributes.append('_', status_);
|
||||
}
|
||||
new_attributes.append(start, status_);
|
||||
start += uprv_strlen(start) + 1;
|
||||
}
|
||||
if (!inserted) {
|
||||
if (!new_attributes.isEmpty()) {
|
||||
new_attributes.append('_', status_);
|
||||
}
|
||||
new_attributes.append(value_str.data(), status_);
|
||||
}
|
||||
// Not yet in the attributes, set the attribute.
|
||||
extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::removeUnicodeLocaleAttribute(
|
||||
StringPiece value)
|
||||
{
|
||||
CharString value_str(value, status_);
|
||||
if (U_FAILURE(status_)) { return *this; }
|
||||
transform(value_str.data(), value_str.length());
|
||||
if (!ultag_isUnicodeLocaleAttribute(value_str.data(), value_str.length())) {
|
||||
status_ = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return *this;
|
||||
}
|
||||
if (extensions_ == nullptr) { return *this; }
|
||||
UErrorCode localErrorCode = U_ZERO_ERROR;
|
||||
CharString attributes;
|
||||
CharStringByteSink sink(&attributes);
|
||||
extensions_->getKeywordValue(kAttributeKey, sink, localErrorCode);
|
||||
// get failure, just return
|
||||
if (U_FAILURE(localErrorCode)) { return *this; }
|
||||
// Do not have any attributes, just return.
|
||||
if (attributes.isEmpty()) { return *this; }
|
||||
|
||||
char* p = attributes.data();
|
||||
// Replace null terminiator in place for _ and - so later
|
||||
// we can use uprv_strcmp to compare.
|
||||
for (int32_t i = 0; i < attributes.length(); i++, p++) {
|
||||
*p = (*p == '_' || *p == '-') ? '\0' : uprv_tolower(*p);
|
||||
}
|
||||
|
||||
const char* start = attributes.data();
|
||||
const char* limit = attributes.data() + attributes.length();
|
||||
CharString new_attributes;
|
||||
bool found = false;
|
||||
while (start < limit) {
|
||||
if (uprv_strcmp(start, value_str.data()) == 0) {
|
||||
found = true;
|
||||
} else {
|
||||
if (!new_attributes.isEmpty()) {
|
||||
new_attributes.append('_', status_);
|
||||
}
|
||||
new_attributes.append(start, status_);
|
||||
}
|
||||
start += uprv_strlen(start) + 1;
|
||||
}
|
||||
// Found the value in attributes, set the attribute.
|
||||
if (found) {
|
||||
extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::clear()
|
||||
{
|
||||
status_ = U_ZERO_ERROR;
|
||||
language_[0] = 0;
|
||||
script_[0] = 0;
|
||||
region_[0] = 0;
|
||||
delete variant_;
|
||||
variant_ = nullptr;
|
||||
clearExtensions();
|
||||
return *this;
|
||||
}
|
||||
|
||||
LocaleBuilder& LocaleBuilder::clearExtensions()
|
||||
{
|
||||
delete extensions_;
|
||||
extensions_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Locale makeBogusLocale() {
|
||||
Locale bogus;
|
||||
bogus.setToBogus();
|
||||
return bogus;
|
||||
}
|
||||
|
||||
Locale LocaleBuilder::build(UErrorCode& errorCode)
|
||||
{
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return makeBogusLocale();
|
||||
}
|
||||
if (U_FAILURE(status_)) {
|
||||
errorCode = status_;
|
||||
return makeBogusLocale();
|
||||
}
|
||||
CharString locale_str(language_, errorCode);
|
||||
if (uprv_strlen(script_) > 0) {
|
||||
locale_str.append('-', errorCode).append(StringPiece(script_), errorCode);
|
||||
}
|
||||
if (uprv_strlen(region_) > 0) {
|
||||
locale_str.append('-', errorCode).append(StringPiece(region_), errorCode);
|
||||
}
|
||||
if (variant_ != nullptr) {
|
||||
locale_str.append('-', errorCode).append(StringPiece(variant_->data()), errorCode);
|
||||
}
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return makeBogusLocale();
|
||||
}
|
||||
Locale product(locale_str.data());
|
||||
if (extensions_ != nullptr) {
|
||||
_copyExtensions(*extensions_, &product, true, errorCode);
|
||||
}
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return makeBogusLocale();
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
|
@ -406,13 +406,22 @@ _isAlphaNumericString(const char* s, int32_t len) {
|
|||
}
|
||||
|
||||
static UBool
|
||||
_isLanguageSubtag(const char* s, int32_t len) {
|
||||
_isAlphaNumericStringLimitedLength(const char* s, int32_t len, int32_t min, int32_t max) {
|
||||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
}
|
||||
if (len >= min && len <= max && _isAlphaNumericString(s, len)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isLanguageSubtag(const char* s, int32_t len) {
|
||||
/*
|
||||
* language = 2*3ALPHA ; shortest ISO 639 code
|
||||
* ["-" extlang] ; sometimes followed by
|
||||
* ; extended language subtags
|
||||
* / 4ALPHA ; or reserved for future use
|
||||
* / 5*8ALPHA ; or registered language subtag
|
||||
* unicode_language_subtag = alpha{2,3} | alpha{5,8};
|
||||
* NOTE: Per ICUTC 2019/01/23- accepting alpha 4
|
||||
* See ICU-20372
|
||||
*/
|
||||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
|
@ -438,8 +447,8 @@ _isExtlangSubtag(const char* s, int32_t len) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static UBool
|
||||
_isScriptSubtag(const char* s, int32_t len) {
|
||||
U_CFUNC UBool
|
||||
ultag_isScriptSubtag(const char* s, int32_t len) {
|
||||
/*
|
||||
* script = 4ALPHA ; ISO 15924 code
|
||||
*/
|
||||
|
@ -452,8 +461,8 @@ _isScriptSubtag(const char* s, int32_t len) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static UBool
|
||||
_isRegionSubtag(const char* s, int32_t len) {
|
||||
U_CFUNC UBool
|
||||
ultag_isRegionSubtag(const char* s, int32_t len) {
|
||||
/*
|
||||
* region = 2ALPHA ; ISO 3166-1 code
|
||||
* / 3DIGIT ; UN M.49 code
|
||||
|
@ -479,7 +488,7 @@ _isVariantSubtag(const char* s, int32_t len) {
|
|||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
}
|
||||
if (len >= 5 && len <= 8 && _isAlphaNumericString(s, len)) {
|
||||
if (_isAlphaNumericStringLimitedLength(s, len, 5, 8)) {
|
||||
return TRUE;
|
||||
}
|
||||
if (len == 4 && ISNUMERIC(*s) && _isAlphaNumericString(s + 1, 3)) {
|
||||
|
@ -488,19 +497,48 @@ _isVariantSubtag(const char* s, int32_t len) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static UBool
|
||||
_isSepListOf(UBool (*test)(const char*, int32_t), const char* s, int32_t len) {
|
||||
const char *p = s;
|
||||
const char *pSubtag = NULL;
|
||||
|
||||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
}
|
||||
|
||||
while ((p - s) < len) {
|
||||
if (*p == SEP) {
|
||||
if (pSubtag == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
if (!test(pSubtag, (int32_t)(p - pSubtag))) {
|
||||
return FALSE;
|
||||
}
|
||||
pSubtag = NULL;
|
||||
} else if (pSubtag == NULL) {
|
||||
pSubtag = p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
if (pSubtag == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
return test(pSubtag, (int32_t)(p - pSubtag));
|
||||
}
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isVariantSubtags(const char* s, int32_t len) {
|
||||
return _isSepListOf(&_isVariantSubtag, s, len);
|
||||
}
|
||||
|
||||
// This is for the ICU-specific "lvariant" handling.
|
||||
static UBool
|
||||
_isPrivateuseVariantSubtag(const char* s, int32_t len) {
|
||||
/*
|
||||
* variant = 1*8alphanum ; registered variants
|
||||
* / (DIGIT 3alphanum)
|
||||
*/
|
||||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
}
|
||||
if (len >= 1 && len <= 8 && _isAlphaNumericString(s, len)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
return _isAlphaNumericStringLimitedLength(s, len , 1, 8);
|
||||
}
|
||||
|
||||
static UBool
|
||||
|
@ -528,42 +566,12 @@ _isExtensionSubtag(const char* s, int32_t len) {
|
|||
/*
|
||||
* extension = singleton 1*("-" (2*8alphanum))
|
||||
*/
|
||||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
}
|
||||
if (len >= 2 && len <= 8 && _isAlphaNumericString(s, len)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
return _isAlphaNumericStringLimitedLength(s, len, 2, 8);
|
||||
}
|
||||
|
||||
static UBool
|
||||
_isExtensionSubtags(const char* s, int32_t len) {
|
||||
const char *p = s;
|
||||
const char *pSubtag = NULL;
|
||||
|
||||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
}
|
||||
|
||||
while ((p - s) < len) {
|
||||
if (*p == SEP) {
|
||||
if (pSubtag == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
if (!_isExtensionSubtag(pSubtag, (int32_t)(p - pSubtag))) {
|
||||
return FALSE;
|
||||
}
|
||||
pSubtag = NULL;
|
||||
} else if (pSubtag == NULL) {
|
||||
pSubtag = p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
if (pSubtag == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
return _isExtensionSubtag(pSubtag, (int32_t)(p - pSubtag));
|
||||
U_CFUNC UBool
|
||||
ultag_isExtensionSubtags(const char* s, int32_t len) {
|
||||
return _isSepListOf(&_isExtensionSubtag, s, len);
|
||||
}
|
||||
|
||||
static UBool
|
||||
|
@ -571,46 +579,32 @@ _isPrivateuseValueSubtag(const char* s, int32_t len) {
|
|||
/*
|
||||
* privateuse = "x" 1*("-" (1*8alphanum))
|
||||
*/
|
||||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
}
|
||||
if (len >= 1 && len <= 8 && _isAlphaNumericString(s, len)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
return _isAlphaNumericStringLimitedLength(s, len, 1, 8);
|
||||
}
|
||||
|
||||
static UBool
|
||||
_isPrivateuseValueSubtags(const char* s, int32_t len) {
|
||||
const char *p = s;
|
||||
const char *pSubtag = NULL;
|
||||
U_CFUNC UBool
|
||||
ultag_isPrivateuseValueSubtags(const char* s, int32_t len) {
|
||||
return _isSepListOf(&_isPrivateuseValueSubtag, s, len);
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
}
|
||||
U_CFUNC UBool
|
||||
ultag_isUnicodeLocaleAttribute(const char* s, int32_t len) {
|
||||
/*
|
||||
* attribute = alphanum{3,8} ;
|
||||
*/
|
||||
return _isAlphaNumericStringLimitedLength(s, len , 3, 8);
|
||||
}
|
||||
|
||||
while ((p - s) < len) {
|
||||
if (*p == SEP) {
|
||||
if (pSubtag == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
if (!_isPrivateuseValueSubtag(pSubtag, (int32_t)(p - pSubtag))) {
|
||||
return FALSE;
|
||||
}
|
||||
pSubtag = NULL;
|
||||
} else if (pSubtag == NULL) {
|
||||
pSubtag = p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
if (pSubtag == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
return _isPrivateuseValueSubtag(pSubtag, (int32_t)(p - pSubtag));
|
||||
U_CFUNC UBool
|
||||
ultag_isUnicodeLocaleAttributes(const char* s, int32_t len) {
|
||||
return _isSepListOf(&ultag_isUnicodeLocaleAttribute, s, len);
|
||||
}
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isUnicodeLocaleKey(const char* s, int32_t len) {
|
||||
/*
|
||||
* key = alphanum alpha ;
|
||||
*/
|
||||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
}
|
||||
|
@ -620,9 +614,160 @@ ultag_isUnicodeLocaleKey(const char* s, int32_t len) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
U_CFUNC UBool
|
||||
_isUnicodeLocaleTypeSubtag(const char*s, int32_t len) {
|
||||
/*
|
||||
* alphanum{3,8}
|
||||
*/
|
||||
return _isAlphaNumericStringLimitedLength(s, len , 3, 8);
|
||||
}
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isUnicodeLocaleType(const char*s, int32_t len) {
|
||||
/*
|
||||
* type = alphanum{3,8} (sep alphanum{3,8})* ;
|
||||
*/
|
||||
return _isSepListOf(&_isUnicodeLocaleTypeSubtag, s, len);
|
||||
}
|
||||
|
||||
static UBool
|
||||
_isTKey(const char* s, int32_t len)
|
||||
{
|
||||
/*
|
||||
* tkey = alpha digit ;
|
||||
*/
|
||||
if (len < 0) {
|
||||
len = (int32_t)uprv_strlen(s);
|
||||
}
|
||||
if (len == 2 && ISALPHA(*s) && ISNUMERIC(*(s + 1))) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static UBool
|
||||
_isTValue(const char* s, int32_t len)
|
||||
{
|
||||
/*
|
||||
* tvalue = (sep alphanum{3,8})+ ;
|
||||
*/
|
||||
return _isAlphaNumericStringLimitedLength(s, len , 3, 8);
|
||||
}
|
||||
|
||||
static UBool
|
||||
_isTransformedExtensionSubtag(int32_t& state, const char* s, int32_t len)
|
||||
{
|
||||
const int32_t kStart = 0; // Start, wait for unicode_language_subtag, tkey or end
|
||||
const int32_t kGotLanguage = 1; // Got unicode_language_subtag, wait for unicode_script_subtag,
|
||||
// unicode_region_subtag, unicode_variant_subtag, tkey or end
|
||||
const int32_t kGotScript = 2; // Got unicode_script_subtag, wait for unicode_region_subtag,
|
||||
// unicode_variant_subtag, tkey, or end
|
||||
const int32_t kGotRegion = 3; // Got unicode_region_subtag, wait for unicode_variant_subtag,
|
||||
// tkey, or end.
|
||||
const int32_t kGotVariant = 4; // Got unicode_variant_subtag, wait for unicode_variant_subtag
|
||||
// tkey or end.
|
||||
const int32_t kGotTKey = -1; // Got tkey, wait for tvalue. ERROR if stop here.
|
||||
const int32_t kGotTValue = 6; // Got tvalue, wait for tkey, tvalue or end
|
||||
|
||||
switch (state) {
|
||||
case kStart:
|
||||
if (ultag_isLanguageSubtag(s, len)) {
|
||||
state = kGotLanguage;
|
||||
return TRUE;
|
||||
}
|
||||
if (_isTKey(s, len)) {
|
||||
state = kGotTKey;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
case kGotLanguage:
|
||||
if (ultag_isScriptSubtag(s, len)) {
|
||||
state = kGotScript;
|
||||
return TRUE;
|
||||
}
|
||||
U_FALLTHROUGH;
|
||||
case kGotScript:
|
||||
if (ultag_isRegionSubtag(s, len)) {
|
||||
state = kGotRegion;
|
||||
return TRUE;
|
||||
}
|
||||
U_FALLTHROUGH;
|
||||
case kGotRegion:
|
||||
U_FALLTHROUGH;
|
||||
case kGotVariant:
|
||||
if (_isVariantSubtag(s, len)) {
|
||||
state = kGotVariant;
|
||||
return TRUE;
|
||||
}
|
||||
if (_isTKey(s, len)) {
|
||||
state = kGotTKey;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
case kGotTKey:
|
||||
if (_isTValue(s, len)) {
|
||||
state = kGotTValue;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
case kGotTValue:
|
||||
if (_isTKey(s, len)) {
|
||||
state = kGotTKey;
|
||||
return TRUE;
|
||||
}
|
||||
if (_isTValue(s, len)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static UBool
|
||||
_isUnicodeExtensionSubtag(int32_t& state, const char* s, int32_t len)
|
||||
{
|
||||
const int32_t kStart = 0; // Start, wait for a key or attribute or end
|
||||
const int32_t kGotKey = 1; // Got a key, wait for type or key or end
|
||||
const int32_t kGotType = 2; // Got a type, wait for key or end
|
||||
|
||||
switch (state) {
|
||||
case kStart:
|
||||
if (ultag_isUnicodeLocaleKey(s, len)) {
|
||||
state = kGotKey;
|
||||
return TRUE;
|
||||
}
|
||||
if (ultag_isUnicodeLocaleAttribute(s, len)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
case kGotKey:
|
||||
if (ultag_isUnicodeLocaleKey(s, len)) {
|
||||
return TRUE;
|
||||
}
|
||||
if (_isUnicodeLocaleTypeSubtag(s, len)) {
|
||||
state = kGotType;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
case kGotType:
|
||||
if (ultag_isUnicodeLocaleKey(s, len)) {
|
||||
state = kGotKey;
|
||||
return TRUE;
|
||||
}
|
||||
if (_isUnicodeLocaleTypeSubtag(s, len)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static UBool
|
||||
_isStatefulSepListOf(UBool (*test)(int32_t&, const char*, int32_t), const char* s, int32_t len)
|
||||
{
|
||||
int32_t state = 0;
|
||||
const char* p;
|
||||
const char* start = s;
|
||||
int32_t subtagLen = 0;
|
||||
|
||||
if (len < 0) {
|
||||
|
@ -631,22 +776,34 @@ ultag_isUnicodeLocaleType(const char*s, int32_t len) {
|
|||
|
||||
for (p = s; len > 0; p++, len--) {
|
||||
if (*p == SEP) {
|
||||
if (subtagLen < 3) {
|
||||
if (!test(state, start, subtagLen)) {
|
||||
return FALSE;
|
||||
}
|
||||
subtagLen = 0;
|
||||
} else if (ISALPHA(*p) || ISNUMERIC(*p)) {
|
||||
subtagLen++;
|
||||
if (subtagLen > 8) {
|
||||
return FALSE;
|
||||
}
|
||||
start = p + 1;
|
||||
} else {
|
||||
return FALSE;
|
||||
subtagLen++;
|
||||
}
|
||||
}
|
||||
|
||||
return (subtagLen >= 3);
|
||||
if (test(state, start, subtagLen) && state >= 0) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isTransformedExtensionSubtags(const char* s, int32_t len)
|
||||
{
|
||||
return _isStatefulSepListOf(&_isTransformedExtensionSubtag, s, len);
|
||||
}
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isUnicodeExtensionSubtags(const char* s, int32_t len) {
|
||||
return _isStatefulSepListOf(&_isUnicodeExtensionSubtag, s, len);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* -------------------------------------------------
|
||||
*
|
||||
|
@ -856,7 +1013,7 @@ _appendLanguageToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool st
|
|||
|
||||
if (len == 0) {
|
||||
sink.Append(LANG_UND, LANG_UND_LEN);
|
||||
} else if (!_isLanguageSubtag(buf, len)) {
|
||||
} else if (!ultag_isLanguageSubtag(buf, len)) {
|
||||
/* invalid language code */
|
||||
if (strict) {
|
||||
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
@ -900,7 +1057,7 @@ _appendScriptToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool stri
|
|||
}
|
||||
|
||||
if (len > 0) {
|
||||
if (!_isScriptSubtag(buf, len)) {
|
||||
if (!ultag_isScriptSubtag(buf, len)) {
|
||||
/* invalid script code */
|
||||
if (strict) {
|
||||
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
@ -932,7 +1089,7 @@ _appendRegionToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool stri
|
|||
}
|
||||
|
||||
if (len > 0) {
|
||||
if (!_isRegionSubtag(buf, len)) {
|
||||
if (!ultag_isRegionSubtag(buf, len)) {
|
||||
/* invalid region code */
|
||||
if (strict) {
|
||||
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
@ -1252,7 +1409,7 @@ _appendKeywordsToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool st
|
|||
}
|
||||
} else {
|
||||
if (*key == PRIVATEUSE) {
|
||||
if (!_isPrivateuseValueSubtags(buf.data(), len)) {
|
||||
if (!ultag_isPrivateuseValueSubtags(buf.data(), len)) {
|
||||
if (strict) {
|
||||
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
break;
|
||||
|
@ -1260,7 +1417,7 @@ _appendKeywordsToLanguageTag(const char* localeID, icu::ByteSink& sink, UBool st
|
|||
continue;
|
||||
}
|
||||
} else {
|
||||
if (!_isExtensionSingleton(key, keylen) || !_isExtensionSubtags(buf.data(), len)) {
|
||||
if (!_isExtensionSingleton(key, keylen) || !ultag_isExtensionSubtags(buf.data(), len)) {
|
||||
if (strict) {
|
||||
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
break;
|
||||
|
@ -1997,7 +2154,7 @@ ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* sta
|
|||
subtagLen = (int32_t)(pSep - pSubtag);
|
||||
|
||||
if (next & LANG) {
|
||||
if (_isLanguageSubtag(pSubtag, subtagLen)) {
|
||||
if (ultag_isLanguageSubtag(pSubtag, subtagLen)) {
|
||||
*pSep = 0; /* terminate */
|
||||
// TODO: move deprecated language code handling here.
|
||||
t->language = T_CString_toLowerCase(pSubtag);
|
||||
|
@ -2024,7 +2181,7 @@ ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* sta
|
|||
}
|
||||
}
|
||||
if (next & SCRT) {
|
||||
if (_isScriptSubtag(pSubtag, subtagLen)) {
|
||||
if (ultag_isScriptSubtag(pSubtag, subtagLen)) {
|
||||
char *p = pSubtag;
|
||||
|
||||
*pSep = 0;
|
||||
|
@ -2044,7 +2201,7 @@ ultag_parse(const char* tag, int32_t tagLen, int32_t* parsedLen, UErrorCode* sta
|
|||
}
|
||||
}
|
||||
if (next & REGN) {
|
||||
if (_isRegionSubtag(pSubtag, subtagLen)) {
|
||||
if (ultag_isRegionSubtag(pSubtag, subtagLen)) {
|
||||
*pSep = 0;
|
||||
// TODO: move deprecated region code handling here.
|
||||
t->region = T_CString_toUpperCase(pSubtag);
|
||||
|
@ -2535,7 +2692,7 @@ ulocimp_toLanguageTag(const char* localeID,
|
|||
buf[1] = SEP;
|
||||
len = uloc_getKeywordValue(localeID, key, &buf[2], sizeof(buf) - 2, &tmpStatus);
|
||||
if (U_SUCCESS(tmpStatus)) {
|
||||
if (_isPrivateuseValueSubtags(&buf[2], len)) {
|
||||
if (ultag_isPrivateuseValueSubtags(&buf[2], len)) {
|
||||
/* return private use only tag */
|
||||
sink.Append(buf, len + 2);
|
||||
done = TRUE;
|
||||
|
|
|
@ -148,6 +148,32 @@ ulocimp_getRegionForSupplementalData(const char *localeID, UBool inferRegion,
|
|||
U_CAPI const char * U_EXPORT2
|
||||
locale_getKeywordsStart(const char *localeID);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isExtensionSubtags(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isLanguageSubtag(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isPrivateuseValueSubtags(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isRegionSubtag(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isScriptSubtag(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isTransformedExtensionSubtags(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isUnicodeExtensionSubtags(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isUnicodeLocaleAttribute(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isUnicodeLocaleAttributes(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isUnicodeLocaleKey(const char* s, int32_t len);
|
||||
|
@ -155,6 +181,9 @@ ultag_isUnicodeLocaleKey(const char* s, int32_t len);
|
|||
U_CFUNC UBool
|
||||
ultag_isUnicodeLocaleType(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC UBool
|
||||
ultag_isVariantSubtags(const char* s, int32_t len);
|
||||
|
||||
U_CFUNC const char*
|
||||
ulocimp_toBcpKey(const char* key);
|
||||
|
||||
|
|
288
icu4c/source/common/unicode/localebuilder.h
Normal file
288
icu4c/source/common/unicode/localebuilder.h
Normal file
|
@ -0,0 +1,288 @@
|
|||
// © 2018 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
#ifndef __LOCALEBUILDER_H__
|
||||
#define __LOCALEBUILDER_H__
|
||||
|
||||
#include "unicode/locid.h"
|
||||
#include "unicode/stringpiece.h"
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief C++ API: Builder API for Locale
|
||||
*/
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
class CharString;
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* <code>LocaleBuilder</code> is used to build instances of <code>Locale</code>
|
||||
* from values configured by the setters. Unlike the <code>Locale</code>
|
||||
* constructors, the <code>LocaleBuilder</code> checks if a value configured by a
|
||||
* setter satisfies the syntax requirements defined by the <code>Locale</code>
|
||||
* class. A <code>Locale</code> object created by a <code>LocaleBuilder</code> is
|
||||
* well-formed and can be transformed to a well-formed IETF BCP 47 language tag
|
||||
* without losing information.
|
||||
*
|
||||
* <p>The following example shows how to create a <code>Locale</code> object
|
||||
* with the <code>LocaleBuilder</code>.
|
||||
* <blockquote>
|
||||
* <pre>
|
||||
* UErrorCode status = U_ZERO_ERROR;
|
||||
* Locale aLocale = LocaleBuilder()
|
||||
* .setLanguage("sr")
|
||||
* .setScript("Latn")
|
||||
* .setRegion("RS")
|
||||
* .build(status);
|
||||
* if (U_SUCCESS(status)) {
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* </blockquote>
|
||||
*
|
||||
* <p>LocaleBuilders can be reused; <code>clear()</code> resets all
|
||||
* fields to their default values.
|
||||
*
|
||||
* <p>LocaleBuilder tracks errors in an internal UErrorCode. For all setters,
|
||||
* except setLanguageTag and setLocale, LocaleBuilder will return immediately
|
||||
* if the internal UErrorCode is in error state.
|
||||
* To reset internal state and error code, call clear method.
|
||||
* The setLanguageTag and setLocale method will first clear the internal
|
||||
* UErrorCode, then track the error of the validation of the input parameter
|
||||
* into the internal UErrorCode.
|
||||
*
|
||||
* @draft ICU 64
|
||||
*/
|
||||
class U_COMMON_API LocaleBuilder : public UObject {
|
||||
public:
|
||||
/**
|
||||
* Constructs an empty LocaleBuilder. The default value of all
|
||||
* fields, extensions, and private use information is the
|
||||
* empty string.
|
||||
*
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder();
|
||||
|
||||
virtual ~LocaleBuilder();
|
||||
|
||||
/**
|
||||
* Resets the <code>LocaleBuilder</code> to match the provided
|
||||
* <code>locale</code>. Existing state is discarded.
|
||||
*
|
||||
* <p>All fields of the locale must be well-formed.
|
||||
* <p>This method clears the internal UErrorCode.
|
||||
*
|
||||
* @param locale the locale
|
||||
* @return This builder.
|
||||
*
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& setLocale(const Locale& locale);
|
||||
|
||||
/**
|
||||
* Resets the LocaleBuilder to match the provided
|
||||
* [Unicode Locale Identifier](http://www.unicode.org/reports/tr35/tr35.html#unicode_locale_id) .
|
||||
* Discards the existing state. the empty string cause the builder to be
|
||||
* reset, like {@link #clear}. Grandfathered tags are converted to their
|
||||
* canonical form before being processed. Otherwise, the <code>language
|
||||
* tag</code> must be well-formed, or else the build() method will later
|
||||
* report an U_ILLEGAL_ARGUMENT_ERROR.
|
||||
*
|
||||
* <p>This method clears the internal UErrorCode.
|
||||
*
|
||||
* @param tag the language tag, defined as
|
||||
* [unicode_locale_id](http://www.unicode.org/reports/tr35/tr35.html#unicode_locale_id).
|
||||
* @return This builder.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& setLanguageTag(StringPiece tag);
|
||||
|
||||
/**
|
||||
* Sets the language. If <code>language</code> is the empty string, the
|
||||
* language in this <code>LocaleBuilder</code> is removed. Otherwise, the
|
||||
* <code>language</code> must be well-formed, or else the build() method will
|
||||
* later report an U_ILLEGAL_ARGUMENT_ERROR.
|
||||
*
|
||||
* <p>The syntax of language value is defined as
|
||||
* [unicode_language_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_language_subtag).
|
||||
*
|
||||
* @param language the language
|
||||
* @return This builder.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& setLanguage(StringPiece language);
|
||||
|
||||
/**
|
||||
* Sets the script. If <code>script</code> is the empty string, the script in
|
||||
* this <code>LocaleBuilder</code> is removed.
|
||||
* Otherwise, the <code>script</code> must be well-formed, or else the build()
|
||||
* method will later report an U_ILLEGAL_ARGUMENT_ERROR.
|
||||
*
|
||||
* <p>The script value is a four-letter script code as
|
||||
* [unicode_script_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_script_subtag)
|
||||
* defined by ISO 15924
|
||||
*
|
||||
* @param script the script
|
||||
* @return This builder.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& setScript(StringPiece script);
|
||||
|
||||
/**
|
||||
* Sets the region. If region is the empty string, the region in this
|
||||
* <code>LocaleBuilder</code> is removed. Otherwise, the <code>region</code>
|
||||
* must be well-formed, or else the build() method will later report an
|
||||
* U_ILLEGAL_ARGUMENT_ERROR.
|
||||
*
|
||||
* <p>The region value is defined by
|
||||
* [unicode_region_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_region_subtag)
|
||||
* as a two-letter ISO 3166 code or a three-digit UN M.49 area code.
|
||||
*
|
||||
* <p>The region value in the <code>Locale</code> created by the
|
||||
* <code>LocaleBuilder</code> is always normalized to upper case.
|
||||
*
|
||||
* @param region the region
|
||||
* @return This builder.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& setRegion(StringPiece region);
|
||||
|
||||
/**
|
||||
* Sets the variant. If variant is the empty string, the variant in this
|
||||
* <code>LocaleBuilder</code> is removed. Otherwise, the <code>variant</code>
|
||||
* must be well-formed, or else the build() method will later report an
|
||||
* U_ILLEGAL_ARGUMENT_ERROR.
|
||||
*
|
||||
* <p><b>Note:</b> This method checks if <code>variant</code>
|
||||
* satisfies the
|
||||
* [unicode_variant_subtag](http://www.unicode.org/reports/tr35/tr35.html#unicode_variant_subtag)
|
||||
* syntax requirements, and normalizes the value to lowercase letters. However,
|
||||
* the <code>Locale</code> class does not impose any syntactic
|
||||
* restriction on variant. To set an ill-formed variant, use a Locale constructor.
|
||||
* If there are multiple unicode_variant_subtag, the caller must concatenate
|
||||
* them with '-' as separator (ex: "foobar-fibar").
|
||||
*
|
||||
* @param variant the variant
|
||||
* @return This builder.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& setVariant(StringPiece variant);
|
||||
|
||||
/**
|
||||
* Sets the extension for the given key. If the value is the empty string,
|
||||
* the extension is removed. Otherwise, the <code>key</code> and
|
||||
* <code>value</code> must be well-formed, or else the build() method will
|
||||
* later report an U_ILLEGAL_ARGUMENT_ERROR.
|
||||
*
|
||||
* <p><b>Note:</b> The key ('u') is used for the Unicode locale extension.
|
||||
* Setting a value for this key replaces any existing Unicode locale key/type
|
||||
* pairs with those defined in the extension.
|
||||
*
|
||||
* <p><b>Note:</b> The key ('x') is used for the private use code. To be
|
||||
* well-formed, the value for this key needs only to have subtags of one to
|
||||
* eight alphanumeric characters, not two to eight as in the general case.
|
||||
*
|
||||
* @param key the extension key
|
||||
* @param value the extension value
|
||||
* @return This builder.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& setExtension(char key, StringPiece value);
|
||||
|
||||
/**
|
||||
* Sets the Unicode locale keyword type for the given key. If the type
|
||||
* StringPiece is constructed with a nullptr, the keyword is removed.
|
||||
* If the type is the empty string, the keyword is set without type subtags.
|
||||
* Otherwise, the key and type must be well-formed, or else the build()
|
||||
* method will later report an U_ILLEGAL_ARGUMENT_ERROR.
|
||||
*
|
||||
* <p>Keys and types are converted to lower case.
|
||||
*
|
||||
* <p><b>Note</b>:Setting the 'u' extension via {@link #setExtension}
|
||||
* replaces all Unicode locale keywords with those defined in the
|
||||
* extension.
|
||||
*
|
||||
* @param key the Unicode locale key
|
||||
* @param type the Unicode locale type
|
||||
* @return This builder.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& setUnicodeLocaleKeyword(
|
||||
StringPiece key, StringPiece type);
|
||||
|
||||
/**
|
||||
* Adds a unicode locale attribute, if not already present, otherwise
|
||||
* has no effect. The attribute must not be empty string and must be
|
||||
* well-formed or U_ILLEGAL_ARGUMENT_ERROR will be set to status
|
||||
* during the build() call.
|
||||
*
|
||||
* @param attribute the attribute
|
||||
* @return This builder.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& addUnicodeLocaleAttribute(StringPiece attribute);
|
||||
|
||||
/**
|
||||
* Removes a unicode locale attribute, if present, otherwise has no
|
||||
* effect. The attribute must not be empty string and must be well-formed
|
||||
* or U_ILLEGAL_ARGUMENT_ERROR will be set to status during the build() call.
|
||||
*
|
||||
* <p>Attribute comparison for removal is case-insensitive.
|
||||
*
|
||||
* @param attribute the attribute
|
||||
* @return This builder.
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& removeUnicodeLocaleAttribute(StringPiece attribute);
|
||||
|
||||
/**
|
||||
* Resets the builder to its initial, empty state.
|
||||
* <p>This method clears the internal UErrorCode.
|
||||
*
|
||||
* @return this builder
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& clear();
|
||||
|
||||
/**
|
||||
* Resets the extensions to their initial, empty state.
|
||||
* Language, script, region and variant are unchanged.
|
||||
*
|
||||
* @return this builder
|
||||
* @draft ICU 64
|
||||
*/
|
||||
LocaleBuilder& clearExtensions();
|
||||
|
||||
/**
|
||||
* Returns an instance of <code>Locale</code> created from the fields set
|
||||
* on this builder.
|
||||
* If any set methods or during the build() call require memory allocation
|
||||
* but fail U_MEMORY_ALLOCATION_ERROR will be set to status.
|
||||
* If any of the fields set by the setters are not well-formed, the status
|
||||
* will be set to U_ILLEGAL_ARGUMENT_ERROR. The state of the builder will
|
||||
* not change after the build() call and the caller is free to keep using
|
||||
* the same builder to build more locales.
|
||||
*
|
||||
* @return a new Locale
|
||||
* @draft ICU 64
|
||||
*/
|
||||
Locale build(UErrorCode& status);
|
||||
|
||||
private:
|
||||
UErrorCode status_;
|
||||
char language_[9];
|
||||
char script_[5];
|
||||
char region_[4];
|
||||
CharString *variant_; // Pointer not object so we need not #include internal charstr.h.
|
||||
icu::Locale *extensions_; // Pointer not object. Storage for all other fields.
|
||||
|
||||
};
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __LOCALEBUILDER_H__
|
|
@ -1109,6 +1109,16 @@
|
|||
#define ulocimp_toLegacyType U_ICU_ENTRY_POINT_RENAME(ulocimp_toLegacyType)
|
||||
#define ultag_isUnicodeLocaleKey U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleKey)
|
||||
#define ultag_isUnicodeLocaleType U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleType)
|
||||
#define ultag_isExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isExtensionSubtags)
|
||||
#define ultag_isLanguageSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isLanguageSubtag)
|
||||
#define ultag_isPrivateuseValueSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isPrivateuseValueSubtags)
|
||||
#define ultag_isRegionSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isRegionSubtag)
|
||||
#define ultag_isScriptSubtag U_ICU_ENTRY_POINT_RENAME(ultag_isScriptSubtag)
|
||||
#define ultag_isTransformedExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isTransformedExtensionSubtags)
|
||||
#define ultag_isUnicodeExtensionSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeExtensionSubtags)
|
||||
#define ultag_isUnicodeLocaleAttribute U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleAttribute)
|
||||
#define ultag_isUnicodeLocaleAttributes U_ICU_ENTRY_POINT_RENAME(ultag_isUnicodeLocaleAttributes)
|
||||
#define ultag_isVariantSubtags U_ICU_ENTRY_POINT_RENAME(ultag_isVariantSubtags)
|
||||
#define umsg_applyPattern U_ICU_ENTRY_POINT_RENAME(umsg_applyPattern)
|
||||
#define umsg_autoQuoteApostrophe U_ICU_ENTRY_POINT_RENAME(umsg_autoQuoteApostrophe)
|
||||
#define umsg_clone U_ICU_ENTRY_POINT_RENAME(umsg_clone)
|
||||
|
|
|
@ -188,6 +188,7 @@ library: common
|
|||
uinit utypes errorcode
|
||||
icuplug
|
||||
platform
|
||||
localebuilder
|
||||
|
||||
group: pluralmap
|
||||
# TODO: Move to i18n library, ticket #11926.
|
||||
|
@ -643,6 +644,11 @@ group: resourcebundle
|
|||
uscript_props propname
|
||||
bytesinkutil
|
||||
|
||||
group: localebuilder
|
||||
localebuilder.o
|
||||
deps
|
||||
resourcebundle
|
||||
|
||||
group: udata
|
||||
udata.o ucmndata.o udatamem.o
|
||||
umapfile.o
|
||||
|
|
|
@ -44,7 +44,7 @@ caltztst.o canittst.o citrtest.o colldata.o convtest.o currcoll.o collationtest.
|
|||
fldset.o dadrfmt.o dadrcal.o dcfmapts.o decoll.o dtfmapts.o dtfmrgts.o dtfmtrtts.o dtfmttst.o \
|
||||
dtptngts.o encoll.o escoll.o ficoll.o frcoll.o g7coll.o intltest.o \
|
||||
itercoll.o itformat.o itmajor.o itutil.o jacoll.o lcukocol.o \
|
||||
loctest.o miscdtfm.o mnkytst.o msfmrgts.o nmfmapts.o nmfmtrt.o \
|
||||
loctest.o localebuildertest.o miscdtfm.o mnkytst.o msfmrgts.o nmfmapts.o nmfmtrt.o \
|
||||
numfmtst.o numrgts.o plurults.o plurfmts.o pptest.o regcoll.o restest.o restsnew.o \
|
||||
sdtfmtts.o svccoll.o tchcfmt.o selfmts.o \
|
||||
tfsmalls.o tmsgfmt.o trcoll.o tscoll.o tsdate.o tsdcfmsy.o tsdtfmsy.o \
|
||||
|
|
|
@ -364,6 +364,7 @@
|
|||
<ClCompile Include="bidiconf.cpp" />
|
||||
<ClCompile Include="listformattertest.cpp" />
|
||||
<ClCompile Include="formattedvaluetest.cpp" />
|
||||
<ClCompile Include="localebuildertest.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="colldata.h" />
|
||||
|
@ -494,8 +495,9 @@
|
|||
<ClInclude Include="convtest.h" />
|
||||
<ClInclude Include="csdetest.h" />
|
||||
<ClInclude Include="listformattertest.h" />
|
||||
<ClInclude Include="localebuildertest.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -540,6 +540,8 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="formattedvaluetest.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
<ClCompile Include="localebuildertest.cpp">
|
||||
<Filter>locales & resources</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -927,5 +929,8 @@
|
|||
<ClInclude Include="erarulestest.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="localebuildertest.h">
|
||||
<Filter>locales & resources</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "itutil.h"
|
||||
#include "strtest.h"
|
||||
#include "loctest.h"
|
||||
#include "localebuildertest.h"
|
||||
#include "citrtest.h"
|
||||
#include "ustrtest.h"
|
||||
#include "ucdtest.h"
|
||||
|
@ -149,6 +150,7 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* &
|
|||
}
|
||||
#endif
|
||||
break;
|
||||
CASE(25, LocaleBuilderTest);
|
||||
default: name = ""; break; //needed to end loop
|
||||
}
|
||||
}
|
||||
|
|
1627
icu4c/source/test/intltest/localebuildertest.cpp
Normal file
1627
icu4c/source/test/intltest/localebuildertest.cpp
Normal file
File diff suppressed because it is too large
Load diff
51
icu4c/source/test/intltest/localebuildertest.h
Normal file
51
icu4c/source/test/intltest/localebuildertest.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
// © 2018 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
|
||||
#include "intltest.h"
|
||||
#include "unicode/localebuilder.h"
|
||||
|
||||
|
||||
/**
|
||||
* Tests for the LocaleBuilder class
|
||||
**/
|
||||
class LocaleBuilderTest: public IntlTest {
|
||||
public:
|
||||
LocaleBuilderTest();
|
||||
virtual ~LocaleBuilderTest();
|
||||
|
||||
void runIndexedTest( int32_t index, UBool exec, const char* &name, char* par = NULL );
|
||||
|
||||
void TestAddRemoveUnicodeLocaleAttribute(void);
|
||||
void TestAddRemoveUnicodeLocaleAttributeWellFormed(void);
|
||||
void TestAddUnicodeLocaleAttributeIllFormed(void);
|
||||
void TestLocaleBuilder(void);
|
||||
void TestLocaleBuilderBasic(void);
|
||||
void TestPosixCases(void);
|
||||
void TestSetExtensionOthers(void);
|
||||
void TestSetExtensionPU(void);
|
||||
void TestSetExtensionT(void);
|
||||
void TestSetExtensionU(void);
|
||||
void TestSetExtensionValidateOthersIllFormed(void);
|
||||
void TestSetExtensionValidateOthersWellFormed(void);
|
||||
void TestSetExtensionValidatePUIllFormed(void);
|
||||
void TestSetExtensionValidatePUWellFormed(void);
|
||||
void TestSetExtensionValidateTIllFormed(void);
|
||||
void TestSetExtensionValidateTWellFormed(void);
|
||||
void TestSetExtensionValidateUIllFormed(void);
|
||||
void TestSetExtensionValidateUWellFormed(void);
|
||||
void TestSetLanguageIllFormed(void);
|
||||
void TestSetLanguageWellFormed(void);
|
||||
void TestSetLocale(void);
|
||||
void TestSetRegionIllFormed(void);
|
||||
void TestSetRegionWellFormed(void);
|
||||
void TestSetScriptIllFormed(void);
|
||||
void TestSetScriptWellFormed(void);
|
||||
void TestSetUnicodeLocaleKeywordIllFormedKey(void);
|
||||
void TestSetUnicodeLocaleKeywordIllFormedValue(void);
|
||||
void TestSetUnicodeLocaleKeywordWellFormed(void);
|
||||
void TestSetVariantIllFormed(void);
|
||||
void TestSetVariantWellFormed(void);
|
||||
|
||||
private:
|
||||
void Verify(LocaleBuilder& bld, const char* expected, const char* msg);
|
||||
};
|
Loading…
Add table
Reference in a new issue