[platform] supports 'number,ordinal' format in message bundles (IDEA-202990)

This commit is contained in:
Roman Shevchenko
2018-12-07 13:55:29 +01:00
parent 5827d6cc1e
commit 6f2035a2fe
5 changed files with 132 additions and 24 deletions
@@ -2,10 +2,12 @@
package com.intellij;
import com.intellij.openapi.util.SystemInfoRt;
import com.intellij.util.text.OrdinalFormat;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
@@ -18,9 +20,9 @@ public abstract class BundleBase {
public static boolean assertKeyIsFound = false;
public static String messageOrDefault(@Nullable final ResourceBundle bundle,
public static String messageOrDefault(@Nullable ResourceBundle bundle,
@NotNull String key,
@Nullable final String defaultValue,
@Nullable String defaultValue,
@NotNull Object... params) {
if (bundle == null) return defaultValue;
@@ -42,7 +44,14 @@ public abstract class BundleBase {
value = replaceMnemonicAmpersand(value);
return format(value, params);
if (params.length > 0 && value.indexOf('{') >= 0) {
Locale locale = bundle.getLocale();
MessageFormat format = locale != null ? new MessageFormat(value, locale) : new MessageFormat(value);
OrdinalFormat.apply(format);
value = format.format(params);
}
return value;
}
@NotNull
@@ -0,0 +1,62 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.text;
import java.text.*;
import java.util.Locale;
public class OrdinalFormat {
/**
* Replaces all instances of {@code "{?,number,ordinal}"} format elements with the ordinal format for the locale.
*/
public static void apply(MessageFormat format) {
Format[] formats = format.getFormats();
NumberFormat ordinal = null;
for (int i = 0; i < formats.length; i++) {
Format element = formats[i];
if (element instanceof DecimalFormat && "ordinal".equals(((DecimalFormat)element).getPositivePrefix())) {
if (ordinal == null) ordinal = getOrdinalFormat(format.getLocale());
format.setFormat(i, ordinal);
}
}
}
private static NumberFormat getOrdinalFormat(Locale locale) {
if (locale != null) {
String language = locale.getLanguage();
if ("en".equals(language) ||
language != null && language.length() == 0 /*bundle fallback locale*/) {
return new EnglishOrdinalFormat();
}
}
return new DecimalFormat();
}
private static class EnglishOrdinalFormat extends NumberFormat {
@Override
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
return new MessageFormat("{0}").format(new Object[]{formatEnglish(number)}, toAppendTo, pos);
}
@Override
public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
throw new IllegalArgumentException("Cannot format non-integer number");
}
@Override
public Number parse(String source, ParsePosition parsePosition) {
throw new UnsupportedOperationException();
}
}
public static String formatEnglish(long num) {
long mod = Math.abs(num) % 100;
if (mod < 11 || mod > 13) {
mod = mod % 10;
if (mod == 1) return num + "st";
if (mod == 2) return num + "nd";
if (mod == 3) return num + "rd";
}
return num + "th";
}
}
@@ -8,10 +8,7 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.text.CharSequenceSubSequence;
import com.intellij.util.text.MergingCharSequence;
import com.intellij.util.text.StringFactory;
import com.intellij.util.text.*;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -25,6 +22,7 @@ import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.*;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -1634,23 +1632,11 @@ public class StringUtil extends StringUtilRt {
return sb.toString();
}
public static String formatOrdinal(int i) {
int mod = i % 100;
if (mod == 11 || mod == 12 || mod == 13) {
return i + "th";
}
mod = mod % 10;
if (mod == 1) {
return i + "st";
}
else if (mod == 2) {
return i + "nd";
}
else if (mod == 3) {
return i + "rd";
}
return i + "th";
/**
* Appends English ordinal suffix to the given number.
*/
public static String formatOrdinal(long num) {
return OrdinalFormat.formatEnglish(num);
}
/**
@@ -0,0 +1,40 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.text;
import com.intellij.BundleBase;
import org.junit.Test;
import java.util.ListResourceBundle;
import java.util.Locale;
import java.util.ResourceBundle;
import static org.junit.Assert.assertEquals;
public class OrdinalFormatTest {
@Test
public void locales() {
// the language bundle is present, but the language is not supported by the formatter - should fall back to plain decimal
ResourceBundle fr = ResourceBundle.getBundle("com.intellij.util.text.OrdinalFormatTest$TestBundle", Locale.FRENCH);
assertEquals("Blah blah 1 blah blah 'whatever' blah blah 33 [fr]", BundleBase.message(fr, "ordinals", 1, "whatever", 33));
// the language bundle isn't present - should fall back to the default (i.e. English)
ResourceBundle de = ResourceBundle.getBundle("com.intellij.util.text.OrdinalFormatTest$TestBundle", Locale.GERMAN);
assertEquals("Blah blah 1st blah blah 'whatever' blah blah 33rd [en]", BundleBase.message(de, "ordinals", 1, "whatever", 33));
}
@SuppressWarnings("unused")
public static class TestBundle extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][]{{"ordinals", "Blah blah {0,number,ordinal} blah blah ''{1}'' blah blah {2,number,ordinal} [en]"}};
}
}
@SuppressWarnings("unused")
public static class TestBundle_fr extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][]{{"ordinals", "Blah blah {0,number,ordinal} blah blah ''{1}'' blah blah {2,number,ordinal} [fr]"}};
}
}
}
@@ -768,4 +768,15 @@ public class StringUtilTest {
assertTrue(String.valueOf(last.getClass()), last instanceof CharSequenceSubSequence);
assertEquals("t", last.toString());
}
@Test
public void testEnglishOrdinals() {
assertEquals("100th", StringUtil.formatOrdinal(100));
assertEquals("101st", StringUtil.formatOrdinal(101));
assertEquals("111th", StringUtil.formatOrdinal(111));
assertEquals("122nd", StringUtil.formatOrdinal(122));
assertEquals("-3rd", StringUtil.formatOrdinal(-3));
assertEquals("-9223372036854775808th", StringUtil.formatOrdinal(Long.MIN_VALUE));
}
}