diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/NFRule.java b/icu4j/main/classes/core/src/com/ibm/icu/text/NFRule.java index 623844eb842..d5fb5c4829f 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/NFRule.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/NFRule.java @@ -381,12 +381,14 @@ final class NFRule { sub2 = extractSubstitution(owner, predecessor); } ruleText = this.ruleText; - if (ruleText.startsWith("$(") && ruleText.endsWith(")")) { + int pluralRuleStart = ruleText.indexOf("$("); + int pluralRuleEnd = (pluralRuleStart >= 0 ? ruleText.indexOf(')', pluralRuleStart) : -1); + if (pluralRuleEnd >= 0) { int endType = ruleText.indexOf(','); if (endType < 0) { throw new IllegalArgumentException("Rule \"" + ruleText + "\" does not have a defined type"); } - String type = this.ruleText.substring(2, endType); + String type = this.ruleText.substring(pluralRuleStart + 2, endType); PluralRules.PluralType pluralType; if ("cardinal".equals(type)) { pluralType = PluralRules.PluralType.CARDINAL; @@ -398,7 +400,7 @@ final class NFRule { throw new IllegalArgumentException(type + " is an unknown type"); } rulePatternFormat = formatter.createPluralFormat(pluralType, - ruleText.substring(endType + 1, ruleText.length() - 1)); + ruleText.substring(endType + 1, pluralRuleEnd)); } } @@ -688,17 +690,29 @@ final class NFRule { // into the right places in toInsertInto (notice we do the // substitutions in reverse order so that the offsets don't get // messed up) + int pluralRuleStart = ruleText.length(); + int lengthOffset = 0; if (rulePatternFormat == null) { toInsertInto.insert(pos, ruleText); } else { - toInsertInto.insert(pos, rulePatternFormat.format(baseValue == 0 ? number : number/baseValue)); + pluralRuleStart = ruleText.indexOf("$("); + int pluralRuleEnd = ruleText.indexOf(')', pluralRuleStart); + int initialLength = toInsertInto.length(); + if (pluralRuleEnd < ruleText.length() - 1) { + toInsertInto.insert(pos, ruleText.substring(pluralRuleEnd + 1)); + } + toInsertInto.insert(pos, rulePatternFormat.format((long)(number/Math.pow(radix, exponent)))); + if (pluralRuleStart > 0) { + toInsertInto.insert(pos, ruleText.substring(0, pluralRuleStart)); + } + lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength); } if (!sub2.isNullSubstitution()) { - sub2.doSubstitution(number, toInsertInto, pos); + sub2.doSubstitution(number, toInsertInto, pos - (sub2.getPos() > pluralRuleStart ? lengthOffset : 0)); } if (!sub1.isNullSubstitution()) { - sub1.doSubstitution(number, toInsertInto, pos); + sub1.doSubstitution(number, toInsertInto, pos - (sub1.getPos() > pluralRuleStart ? lengthOffset : 0)); } } @@ -718,17 +732,29 @@ final class NFRule { // [again, we have two copies of this routine that do the same thing // so that we don't sacrifice precision in a long by casting it // to a double] + int pluralRuleStart = ruleText.length(); + int lengthOffset = 0; if (rulePatternFormat == null) { toInsertInto.insert(pos, ruleText); } else { - toInsertInto.insert(pos, rulePatternFormat.format(number)); + pluralRuleStart = ruleText.indexOf("$("); + int pluralRuleEnd = ruleText.indexOf(')', pluralRuleStart); + int initialLength = toInsertInto.length(); + if (pluralRuleEnd < ruleText.length() - 1) { + toInsertInto.insert(pos, ruleText.substring(pluralRuleEnd + 1)); + } + toInsertInto.insert(pos, rulePatternFormat.format((long)(number/Math.pow(radix, exponent)))); + if (pluralRuleStart > 0) { + toInsertInto.insert(pos, ruleText.substring(0, pluralRuleStart)); + } + lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength); } if (!sub2.isNullSubstitution()) { - sub2.doSubstitution(number, toInsertInto, pos); + sub2.doSubstitution(number, toInsertInto, pos - (sub2.getPos() > pluralRuleStart ? lengthOffset : 0)); } if (!sub1.isNullSubstitution()) { - sub1.doSubstitution(number, toInsertInto, pos); + sub1.doSubstitution(number, toInsertInto, pos - (sub1.getPos() > pluralRuleStart ? lengthOffset : 0)); } } @@ -1137,7 +1163,16 @@ final class NFRule { pluralFormatKey.parseType(str, scanner, position); int start = position.getBeginIndex(); if (start >= 0) { - return new int[]{start, position.getEndIndex() - start}; + int pluralRuleStart = ruleText.indexOf("$("); + int pluralRuleSuffix = ruleText.indexOf(')', pluralRuleStart) + 1; + int matchLen = position.getEndIndex() - start; + String prefix = ruleText.substring(0, pluralRuleStart); + String suffix = ruleText.substring(pluralRuleSuffix); + if (str.regionMatches(start - prefix.length(), prefix, 0, prefix.length()) + && str.regionMatches(start + matchLen, suffix, 0, suffix.length())) + { + return new int[]{start - prefix.length(), matchLen + prefix.length() + suffix.length()}; + } } return new int[]{-1, 0}; } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/RbnfTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/RbnfTest.java index 89366c9b2f0..e951a452bb8 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/RbnfTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/RbnfTest.java @@ -612,7 +612,8 @@ public class RbnfTest extends TestFmwk { + "200: <<сти[ >>];" + "300: <<ста[ >>];" + "500: <<сот[ >>];" - + "1000: <<$(cardinal,one{ тысяча}few{ тысячи}other{ тысяч})[ >>];"; + + "1000: << $(cardinal,one{тысяча}few{тысячи}other{тысяч})[ >>];" + + "1000000: << $(cardinal,one{миллион}few{миллионы}other{миллионов})[ >>];"; RuleBasedNumberFormat ruFormatter = new RuleBasedNumberFormat(ruRules, new ULocale("ru")); String[][] ruTestData = { { "1", "один" }, @@ -620,8 +621,13 @@ public class RbnfTest extends TestFmwk { { "125", "сто двадцать пять" }, { "399", "триста девяносто девять" }, { "1,000", "один тысяча" }, + { "1,001", "один тысяча один" }, { "2,000", "два тысячи" }, + { "2,001", "два тысячи один" }, + { "2,002", "два тысячи два" }, + { "3,333", "три тысячи триста тридцать три" }, { "5,000", "пять тысяч" }, + { "11,000", "одиннадцать тысяч" }, { "21,000", "двадцать один тысяча" }, { "22,000", "двадцать два тысячи" }, };