ICU-20949 Fix compound unit in "ar", "ne" locales

Do not assume the "one" pattern always contains "{0}"
This commit is contained in:
Frank Tang 2020-04-21 21:04:29 -07:00 committed by Markus Scherer
parent b5973eee64
commit f0ada59042
6 changed files with 229 additions and 3 deletions

View file

@ -246,7 +246,8 @@ LongNameHandler::forCompoundUnit(const Locale &loc, const MeasureUnit &unit, con
if (U_FAILURE(status)) { return result; }
UnicodeString secondaryFormat = getWithPlural(secondaryData, StandardPlural::Form::ONE, status);
if (U_FAILURE(status)) { return result; }
SimpleFormatter secondaryCompiled(secondaryFormat, 1, 1, status);
// Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale.
SimpleFormatter secondaryCompiled(secondaryFormat, 0, 1, status);
if (U_FAILURE(status)) { return result; }
UnicodeString secondaryString = secondaryCompiled.getTextWithNoArguments().trim();
// TODO: Why does UnicodeString need to be explicit in the following line?

View file

@ -9,9 +9,11 @@
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
#include <stdio.h>
#include "unicode/unumberformatter.h"
#include "unicode/umisc.h"
#include "unicode/unum.h"
#include "unicode/ustring.h"
#include "cformtst.h"
#include "cintltst.h"
#include "cmemory.h"
@ -26,6 +28,8 @@ static void TestFormattedValue(void);
static void TestSkeletonParseError(void);
static void TestPerUnitInArabic(void);
void addUNumberFormatterTest(TestNode** root);
#define TESTCASE(x) addTest(root, &x, "tsformat/unumberformatter/" #x)
@ -36,6 +40,7 @@ void addUNumberFormatterTest(TestNode** root) {
TESTCASE(TestExampleCode);
TESTCASE(TestFormattedValue);
TESTCASE(TestSkeletonParseError);
TESTCASE(TestPerUnitInArabic);
}
@ -254,5 +259,88 @@ static void TestSkeletonParseError() {
unumf_close(uformatter);
}
static void TestPerUnitInArabic() {
const char* simpleMeasureUnits[] = {
"area-acre",
"digital-bit",
"digital-byte",
"temperature-celsius",
"length-centimeter",
"duration-day",
"angle-degree",
"temperature-fahrenheit",
"volume-fluid-ounce",
"length-foot",
"volume-gallon",
"digital-gigabit",
"digital-gigabyte",
"mass-gram",
"area-hectare",
"duration-hour",
"length-inch",
"digital-kilobit",
"digital-kilobyte",
"mass-kilogram",
"length-kilometer",
"volume-liter",
"digital-megabit",
"digital-megabyte",
"length-meter",
"length-mile",
"length-mile-scandinavian",
"volume-milliliter",
"length-millimeter",
"duration-millisecond",
"duration-minute",
"duration-month",
"mass-ounce",
"concentr-percent",
"digital-petabyte",
"mass-pound",
"duration-second",
"mass-stone",
"digital-terabit",
"digital-terabyte",
"duration-week",
"length-yard",
"duration-year"
};
#define BUFFER_LEN 256
char buffer[BUFFER_LEN];
UChar ubuffer[BUFFER_LEN];
const char* locale = "ar";
UErrorCode status = U_ZERO_ERROR;
UFormattedNumber* formatted = unumf_openResult(&status);
if (U_FAILURE(status)) {
log_err("FAIL: unumf_openResult failed");
return;
}
for(int32_t i=0; i < UPRV_LENGTHOF(simpleMeasureUnits); ++i) {
for(int32_t j=0; j < UPRV_LENGTHOF(simpleMeasureUnits); ++j) {
status = U_ZERO_ERROR;
sprintf(buffer, "measure-unit/%s per-measure-unit/%s",
simpleMeasureUnits[i], simpleMeasureUnits[j]);
int32_t outputlen = 0;
u_strFromUTF8(ubuffer, BUFFER_LEN, &outputlen, buffer, strlen(buffer), &status);
if (U_FAILURE(status)) {
log_err("FAIL u_strFromUTF8: %s = %s ( %s )\n", locale, buffer,
u_errorName(status));
}
UNumberFormatter* nf = unumf_openForSkeletonAndLocale(
ubuffer, outputlen, locale, &status);
if (U_FAILURE(status)) {
log_err("FAIL unumf_openForSkeletonAndLocale: %s = %s ( %s )\n",
locale, buffer, u_errorName(status));
} else {
unumf_formatDouble(nf, 1, formatted, &status);
if (U_FAILURE(status)) {
log_err("FAIL unumf_formatDouble: %s = %s ( %s )\n",
locale, buffer, u_errorName(status));
}
}
unumf_close(nf);
}
}
unumf_closeResult(formatted);
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -258,6 +258,7 @@ class NumberSkeletonTest : public IntlTest {
void defaultTokens();
void flexibleSeparators();
void wildcardCharacters();
void perUnitInArabic();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);

View file

@ -30,6 +30,7 @@ void NumberSkeletonTest::runIndexedTest(int32_t index, UBool exec, const char*&
TESTCASE_AUTO(defaultTokens);
TESTCASE_AUTO(flexibleSeparators);
TESTCASE_AUTO(wildcardCharacters);
TESTCASE_AUTO(perUnitInArabic);
TESTCASE_AUTO_END;
}
@ -362,5 +363,77 @@ void NumberSkeletonTest::expectedErrorSkeleton(const char16_t** cases, int32_t c
}
}
void NumberSkeletonTest::perUnitInArabic() {
IcuTestErrorCode status(*this, "perUnitInArabic");
struct TestCase {
const char16_t* type;
const char16_t* subtype;
} cases[] = {
{u"area", u"acre"},
{u"digital", u"bit"},
{u"digital", u"byte"},
{u"temperature", u"celsius"},
{u"length", u"centimeter"},
{u"duration", u"day"},
{u"angle", u"degree"},
{u"temperature", u"fahrenheit"},
{u"volume", u"fluid-ounce"},
{u"length", u"foot"},
{u"volume", u"gallon"},
{u"digital", u"gigabit"},
{u"digital", u"gigabyte"},
{u"mass", u"gram"},
{u"area", u"hectare"},
{u"duration", u"hour"},
{u"length", u"inch"},
{u"digital", u"kilobit"},
{u"digital", u"kilobyte"},
{u"mass", u"kilogram"},
{u"length", u"kilometer"},
{u"volume", u"liter"},
{u"digital", u"megabit"},
{u"digital", u"megabyte"},
{u"length", u"meter"},
{u"length", u"mile"},
{u"length", u"mile-scandinavian"},
{u"volume", u"milliliter"},
{u"length", u"millimeter"},
{u"duration", u"millisecond"},
{u"duration", u"minute"},
{u"duration", u"month"},
{u"mass", u"ounce"},
{u"concentr", u"percent"},
{u"digital", u"petabyte"},
{u"mass", u"pound"},
{u"duration", u"second"},
{u"mass", u"stone"},
{u"digital", u"terabit"},
{u"digital", u"terabyte"},
{u"duration", u"week"},
{u"length", u"yard"},
{u"duration", u"year"},
};
for (const auto& cas1 : cases) {
for (const auto& cas2 : cases) {
UnicodeString skeleton(u"measure-unit/");
skeleton += cas1.type;
skeleton += u"-";
skeleton += cas1.subtype;
skeleton += u" ";
skeleton += u"per-measure-unit/";
skeleton += cas2.type;
skeleton += u"-";
skeleton += cas2.subtype;
status.setScope(skeleton);
UnicodeString actual = NumberFormatter::forSkeleton(skeleton, status).locale("ar")
.formatDouble(5142.3, status)
.toString(status);
status.errIfFailureAndReset();
}
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -241,8 +241,10 @@ public class LongNameHandler implements MicroPropsGenerator, ModifierStore {
String compiled = SimpleFormatterImpl
.compileToStringMinMaxArguments(rawPerUnitFormat, sb, 2, 2);
String secondaryFormat = getWithPlural(secondaryData, StandardPlural.ONE);
// Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale.
String secondaryCompiled = SimpleFormatterImpl
.compileToStringMinMaxArguments(secondaryFormat, sb, 1, 1);
.compileToStringMinMaxArguments(secondaryFormat, sb, 0, 1);
String secondaryString = SimpleFormatterImpl.getTextWithNoArguments(secondaryCompiled)
.trim();
perUnitFormat = SimpleFormatterImpl.formatCompiledPattern(compiled, "{0}", secondaryString);

View file

@ -349,4 +349,65 @@ public class NumberSkeletonTest {
assertEquals(mode.toString(), modeString, skeleton.substring(14));
}
}
@Test
public void perUnitInArabic() {
String[][] cases = {
{"area", "acre"},
{"digital", "bit"},
{"digital", "byte"},
{"temperature", "celsius"},
{"length", "centimeter"},
{"duration", "day"},
{"angle", "degree"},
{"temperature", "fahrenheit"},
{"volume", "fluid-ounce"},
{"length", "foot"},
{"volume", "gallon"},
{"digital", "gigabit"},
{"digital", "gigabyte"},
{"mass", "gram"},
{"area", "hectare"},
{"duration", "hour"},
{"length", "inch"},
{"digital", "kilobit"},
{"digital", "kilobyte"},
{"mass", "kilogram"},
{"length", "kilometer"},
{"volume", "liter"},
{"digital", "megabit"},
{"digital", "megabyte"},
{"length", "meter"},
{"length", "mile"},
{"length", "mile-scandinavian"},
{"volume", "milliliter"},
{"length", "millimeter"},
{"duration", "millisecond"},
{"duration", "minute"},
{"duration", "month"},
{"mass", "ounce"},
{"concentr", "percent"},
{"digital", "petabyte"},
{"mass", "pound"},
{"duration", "second"},
{"mass", "stone"},
{"digital", "terabit"},
{"digital", "terabyte"},
{"duration", "week"},
{"length", "yard"},
{"duration", "year"},
};
ULocale arabic = new ULocale("ar");
for (String[] cas1 : cases) {
for (String[] cas2 : cases) {
String skeleton = "measure-unit/";
skeleton += cas1[0] + "-" + cas1[1] + " per-measure-unit/" + cas2[0] + "-" + cas2[1];
String actual = NumberFormatter.forSkeleton(skeleton).locale(arabic).format(5142.3)
.toString();
// Just make sure it won't throw exception
}
}
}
}