Возникла задача сделать многоязычное приложение на Java с выводом чисел и указанием того, что, собственно, выводится, например: 2 кирпича, 10 лет, 5 подушек и т.д. Известные решения (gettext, ChoiceFormat) меня не воодушевили, так как в одном случае надо порождать кучу файлов (по числу языков) и цеплять объёмную библиотеку, а во втором - некорректно обрабатывается русский язык. Решение было принято в пользу создания своего велосипеда и написании небольшого класса, который осуществляет выбор формы существительного при числительном в зависимости от языка и значения числительного.

Информация для "мяса" класса была взята отсюда: http://translate.sourceforge.net/wiki/l10n/pluralforms. Использовать, правда, её с ходу не получилось, так как она содержит некоторые ошибки в синтаксисе, впрочем, не фатальные.

Использование класса осуществляется следующим образом: при создании объекта вызывается конструктор, который либо сам пытается выяснить текущую локаль, либо ему её указывают принудительно. Далее, в нём производится сопоставление кода локали ISO и внутреннего "кода языка". Собственно говоря, это самая долгая операция, поэтому лучше её делать как можно реже. После этого можно вызывать метод convert(int value, "текст1|текст2|..."); на вход которого подаётся число и несколько форм существительного, которые представляют собой одну строку и разделяются вертикальной чертой.

 

 

// Data about plural forms get here:
// http://translate.sourceforge.net/wiki/l10n/pluralforms

import java.util.Locale;

public class i10n_pluralforms {
	private int lang_id = 0;
	private int nplurals = 0;
	private String language = "";

	public i10n_pluralforms (Locale locale){
		language = locale.getLanguage();
		lang_id = languageToIntId(language);
		return;
	}
	public i10n_pluralforms (){
		Locale locale = Locale.getDefault();
		language = locale.getLanguage();
		lang_id = languageToIntId(language);
		return;
	}

private int languageToIntId(String language)
{
		 if (language.compareToIgnoreCase("ach") == 0){lang_id = 1;}
	else if (language.compareToIgnoreCase("af") == 0){lang_id = 2;}
	else if (language.compareToIgnoreCase("ak") == 0){lang_id = 3;}
	else if (language.compareToIgnoreCase("sq") == 0){lang_id = 4;}
	else if (language.compareToIgnoreCase("am") == 0){lang_id = 5;}
	else if (language.compareToIgnoreCase("ar") == 0){lang_id = 6;}
	else if (language.compareToIgnoreCase("an") == 0){lang_id = 7;}
	else if (language.compareToIgnoreCase("es_AR") == 0){lang_id = 8;}
	else if (language.compareToIgnoreCase("hy") == 0){lang_id = 9;}
	else if (language.compareToIgnoreCase("ast") == 0){lang_id = 10;}
	else if (language.compareToIgnoreCase("ay") == 0){lang_id = 11;}
	else if (language.compareToIgnoreCase("az") == 0){lang_id = 12;}
	else if (language.compareToIgnoreCase("eu") == 0){lang_id = 13;}
	else if (language.compareToIgnoreCase("be") == 0){lang_id = 14;}
	else if (language.compareToIgnoreCase("bn") == 0){lang_id = 15;}
	else if (language.compareToIgnoreCase("bs") == 0){lang_id = 16;}
	else if (language.compareToIgnoreCase("pt_BR") == 0){lang_id = 17;}
	else if (language.compareToIgnoreCase("br") == 0){lang_id = 18;}
	else if (language.compareToIgnoreCase("bg") == 0){lang_id = 19;}
	else if (language.compareToIgnoreCase("ca") == 0){lang_id = 20;}
	else if (language.compareToIgnoreCase("cgg") == 0){lang_id = 21;}
	else if (language.compareToIgnoreCase("zh") == 0){lang_id = 22;}
	else if (language.compareToIgnoreCase("kw") == 0){lang_id = 23;}
	else if (language.compareToIgnoreCase("hr") == 0){lang_id = 24;}
	else if (language.compareToIgnoreCase("cs") == 0){lang_id = 25;}
	else if (language.compareToIgnoreCase("da") == 0){lang_id = 26;}
	else if (language.compareToIgnoreCase("nl") == 0){lang_id = 27;}
	else if (language.compareToIgnoreCase("dz") == 0){lang_id = 28;}
	else if (language.compareToIgnoreCase("en") == 0){lang_id = 29;}
	else if (language.compareToIgnoreCase("eo") == 0){lang_id = 30;}
	else if (language.compareToIgnoreCase("et") == 0){lang_id = 31;}
	else if (language.compareToIgnoreCase("fo") == 0){lang_id = 32;}
	else if (language.compareToIgnoreCase("fil") == 0){lang_id = 33;}
	else if (language.compareToIgnoreCase("fi") == 0){lang_id = 34;}
	else if (language.compareToIgnoreCase("fr") == 0){lang_id = 35;}
	else if (language.compareToIgnoreCase("fy") == 0){lang_id = 36;}
	else if (language.compareToIgnoreCase("fur") == 0){lang_id = 37;}
	else if (language.compareToIgnoreCase("ff") == 0){lang_id = 38;}
	else if (language.compareToIgnoreCase("gl") == 0){lang_id = 39;}
	else if (language.compareToIgnoreCase("ka") == 0){lang_id = 40;}
	else if (language.compareToIgnoreCase("de") == 0){lang_id = 41;}
	else if (language.compareToIgnoreCase("el") == 0){lang_id = 42;}
	else if (language.compareToIgnoreCase("gu") == 0){lang_id = 43;}
	else if (language.compareToIgnoreCase("gun") == 0){lang_id = 44;}
	else if (language.compareToIgnoreCase("ha") == 0){lang_id = 45;}
	else if (language.compareToIgnoreCase("he") == 0){lang_id = 46;}
	else if (language.compareToIgnoreCase("hi") == 0){lang_id = 47;}
	else if (language.compareToIgnoreCase("hu") == 0){lang_id = 48;}
	else if (language.compareToIgnoreCase("is") == 0){lang_id = 49;}
	else if (language.compareToIgnoreCase("id") == 0){lang_id = 50;}
	else if (language.compareToIgnoreCase("ia") == 0){lang_id = 51;}
	else if (language.compareToIgnoreCase("ga") == 0){lang_id = 52;}
	else if (language.compareToIgnoreCase("it") == 0){lang_id = 53;}
	else if (language.compareToIgnoreCase("ja") == 0){lang_id = 54;}
	else if (language.compareToIgnoreCase("jv") == 0){lang_id = 55;}
	else if (language.compareToIgnoreCase("kn") == 0){lang_id = 56;}
	else if (language.compareToIgnoreCase("csb") == 0){lang_id = 57;}
	else if (language.compareToIgnoreCase("kk") == 0){lang_id = 58;}
	else if (language.compareToIgnoreCase("km") == 0){lang_id = 59;}
	else if (language.compareToIgnoreCase("ko") == 0){lang_id = 60;}
	else if (language.compareToIgnoreCase("ku") == 0){lang_id = 61;}
	else if (language.compareToIgnoreCase("ky") == 0){lang_id = 62;}
	else if (language.compareToIgnoreCase("lo") == 0){lang_id = 63;}
	else if (language.compareToIgnoreCase("lv") == 0){lang_id = 64;}
	else if (language.compareToIgnoreCase("lb") == 0){lang_id = 65;}
	else if (language.compareToIgnoreCase("ln") == 0){lang_id = 66;}
	else if (language.compareToIgnoreCase("lt") == 0){lang_id = 67;}
	else if (language.compareToIgnoreCase("jbo") == 0){lang_id = 68;}
	else if (language.compareToIgnoreCase("mk") == 0){lang_id = 69;}
	else if (language.compareToIgnoreCase("mai") == 0){lang_id = 70;}
	else if (language.compareToIgnoreCase("mg") == 0){lang_id = 71;}
	else if (language.compareToIgnoreCase("ms") == 0){lang_id = 72;}
	else if (language.compareToIgnoreCase("ml") == 0){lang_id = 73;}
	else if (language.compareToIgnoreCase("mt") == 0){lang_id = 74;}
	else if (language.compareToIgnoreCase("mnk") == 0){lang_id = 75;}
	else if (language.compareToIgnoreCase("mi") == 0){lang_id = 76;}
	else if (language.compareToIgnoreCase("arn") == 0){lang_id = 77;}
	else if (language.compareToIgnoreCase("mr") == 0){lang_id = 78;}
	else if (language.compareToIgnoreCase("mfe") == 0){lang_id = 79;}
	else if (language.compareToIgnoreCase("mn") == 0){lang_id = 80;}
	else if (language.compareToIgnoreCase("nah") == 0){lang_id = 81;}
	else if (language.compareToIgnoreCase("nap") == 0){lang_id = 82;}
	else if (language.compareToIgnoreCase("ne") == 0){lang_id = 83;}
	else if (language.compareToIgnoreCase("se") == 0){lang_id = 84;}
	else if (language.compareToIgnoreCase("nso") == 0){lang_id = 85;}
	else if (language.compareToIgnoreCase("no") == 0){lang_id = 86;}
	else if (language.compareToIgnoreCase("nb") == 0){lang_id = 87;}
	else if (language.compareToIgnoreCase("nn") == 0){lang_id = 88;}
	else if (language.compareToIgnoreCase("oc") == 0){lang_id = 89;}
	else if (language.compareToIgnoreCase("or") == 0){lang_id = 90;}
	else if (language.compareToIgnoreCase("pap") == 0){lang_id = 91;}
	else if (language.compareToIgnoreCase("ps") == 0){lang_id = 92;}
	else if (language.compareToIgnoreCase("fa") == 0){lang_id = 93;}
	else if (language.compareToIgnoreCase("pms") == 0){lang_id = 94;}
	else if (language.compareToIgnoreCase("pl") == 0){lang_id = 95;}
	else if (language.compareToIgnoreCase("pt") == 0){lang_id = 96;}
	else if (language.compareToIgnoreCase("pa") == 0){lang_id = 97;}
	else if (language.compareToIgnoreCase("ro") == 0){lang_id = 98;}
	else if (language.compareToIgnoreCase("rm") == 0){lang_id = 99;}
	else if (language.compareToIgnoreCase("ru") == 0){lang_id = 100;}
	else if (language.compareToIgnoreCase("sco") == 0){lang_id = 101;}
	else if (language.compareToIgnoreCase("gd") == 0){lang_id = 102;}
	else if (language.compareToIgnoreCase("sr") == 0){lang_id = 103;}
	else if (language.compareToIgnoreCase("si") == 0){lang_id = 104;}
	else if (language.compareToIgnoreCase("sk") == 0){lang_id = 105;}
	else if (language.compareToIgnoreCase("sl") == 0){lang_id = 106;}
	else if (language.compareToIgnoreCase("so") == 0){lang_id = 107;}
	else if (language.compareToIgnoreCase("son") == 0){lang_id = 108;}
	else if (language.compareToIgnoreCase("es") == 0){lang_id = 109;}
	else if (language.compareToIgnoreCase("su") == 0){lang_id = 110;}
	else if (language.compareToIgnoreCase("sw") == 0){lang_id = 111;}
	else if (language.compareToIgnoreCase("sv") == 0){lang_id = 112;}
	else if (language.compareToIgnoreCase("tg") == 0){lang_id = 113;}
	else if (language.compareToIgnoreCase("ta") == 0){lang_id = 114;}
	else if (language.compareToIgnoreCase("tt") == 0){lang_id = 115;}
	else if (language.compareToIgnoreCase("te") == 0){lang_id = 116;}
	else if (language.compareToIgnoreCase("th") == 0){lang_id = 117;}
	else if (language.compareToIgnoreCase("bo") == 0){lang_id = 118;}
	else if (language.compareToIgnoreCase("ti") == 0){lang_id = 119;}
	else if (language.compareToIgnoreCase("tr") == 0){lang_id = 120;}
	else if (language.compareToIgnoreCase("tk") == 0){lang_id = 121;}
	else if (language.compareToIgnoreCase("uk") == 0){lang_id = 122;}
	else if (language.compareToIgnoreCase("ur") == 0){lang_id = 123;}
	else if (language.compareToIgnoreCase("ug") == 0){lang_id = 124;}
	else if (language.compareToIgnoreCase("uz") == 0){lang_id = 125;}
	else if (language.compareToIgnoreCase("vi") == 0){lang_id = 126;}
	else if (language.compareToIgnoreCase("wa") == 0){lang_id = 127;}
	else if (language.compareToIgnoreCase("cy") == 0){lang_id = 128;}
	else if (language.compareToIgnoreCase("wo") == 0){lang_id = 129;}
	else if (language.compareToIgnoreCase("sah") == 0){lang_id = 130;}
	else if (language.compareToIgnoreCase("yo") == 0){lang_id = 131;}
	else lang_id = 0;
	return lang_id;
}

public String convert (int integer, String pluralforms){
	int plural = 0;
	int n = Math.abs(integer);

	switch (lang_id)
	{
	case 1: nplurals=2; plural=(n > 1?1:0); break;
	case 2: nplurals=2; plural=(n == 1?0:1); break;
	case 3: nplurals=2; plural=(n > 1?1:0); break;
	case 4: nplurals=2; plural=(n == 1?0:1); break;
	case 5: nplurals=2; plural=(n > 1?1:0); break;
	case 6: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5); break;
	case 7: nplurals=2; plural=(n == 1?0:1); break;
	case 8: nplurals=2; plural=(n == 1?0:1); break;
	case 9: nplurals=2; plural=(n == 1?0:1); break;
	case 10: nplurals=2; plural=(n == 1?0:1); break;
	case 11: nplurals=1; plural=0; break;
	case 12: nplurals=2; plural=(n == 1?0:1); break;
	case 13: nplurals=2; plural=(n == 1?0:1); break;
	case 14: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 15: nplurals=2; plural=(n == 1?0:1); break;
	case 16: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 17: nplurals=2; plural=(n == 1?0:1); break;
	case 18: nplurals=2; plural=(n > 1?1:0); break;
	case 19: nplurals=2; plural=(n == 1?0:1); break;
	case 20: nplurals=2; plural=(n == 1?0:1); break;
	case 21: nplurals=1; plural=0; break;
	case 22: nplurals=1; plural=0; break;
	case 23: nplurals=4; plural= ((n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : 3); break;
	case 24: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 25: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2); break;
	case 26: nplurals=2; plural=(n == 1?0:1); break;
	case 27: nplurals=2; plural=(n == 1?0:1); break;
	case 28: nplurals=1; plural=0; break;
	case 29: nplurals=2; plural=(n == 1?0:1); break;
	case 30: nplurals=2; plural=(n == 1?0:1); break;
	case 31: nplurals=2; plural=(n == 1?0:1); break;
	case 32: nplurals=2; plural=(n == 1?0:1); break;
	case 33: nplurals=2; plural=(n > 1?1:0); break;
	case 34: nplurals=2; plural=(n == 1?0:1); break;
	case 35: nplurals=2; plural=(n > 1?1:0); break;
	case 36: nplurals=2; plural=(n == 1?0:1); break;
	case 37: nplurals=2; plural=(n == 1?0:1); break;
	case 38: nplurals=2; plural=(n == 1?0:1); break;
	case 39: nplurals=2; plural=(n == 1?0:1); break;
	case 40: nplurals=1; plural=0; break;
	case 41: nplurals=2; plural=(n == 1?0:1); break;
	case 42: nplurals=2; plural=(n == 1?0:1); break;
	case 43: nplurals=2; plural=(n == 1?0:1); break;
	case 44: nplurals=2; plural =(n > 1?1:0); break;
	case 45: nplurals=2; plural=(n == 1?0:1); break;
	case 46: nplurals=2; plural=(n == 1?0:1); break;
	case 47: nplurals=2; plural=(n == 1?0:1); break;
	case 48: nplurals=2; plural=(n == 1?0:1); break;
	case 49: nplurals=2; plural=((n%10!=1 || n%100==11)?1:0); break;
	case 50: nplurals=1; plural=0; break;
	case 51: nplurals=2; plural=(n == 1?0:1); break;
	case 52: nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4); break;
	case 53: nplurals=2; plural=(n == 1?0:1); break;
	case 54: nplurals=1; plural=0; break;
	case 55: nplurals=2; plural=(n == 0?0:1); break;
	case 56: nplurals=2; plural=(n == 1?0:1); break;
	case 57: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 58: nplurals=1; plural=0; break;
	case 59: nplurals=1; plural=0; break;
	case 60: nplurals=1; plural=0; break;
	case 61: nplurals=2; plural=(n == 1?0:1); break;
	case 62: nplurals=1; plural=0; break;
	case 63: nplurals=1; plural=0; break;
	case 64: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2); break;
	case 65: nplurals=2; plural=(n == 1?0:1); break;
	case 66: nplurals=2; plural=(n > 1?1:0); break;
	case 67: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 68: nplurals=1; plural=0; break;
	case 69: nplurals=2; plural= (n==1 || n%10==1 ? 0 : 1); break;
	case 70: nplurals=2; plural=(n == 1?0:1); break;
	case 71: nplurals=2; plural=(n > 1?1:0); break;
	case 72: nplurals=1; plural=0; break;
	case 73: nplurals=2; plural=(n == 1?0:1); break;
	case 74: nplurals=4; plural=(n==1 ? 0 : n==0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3); break;
	case 75: nplurals=3; plural=(n==0 ? 0 : n==1 ? 1 : 2); break;
	case 76: nplurals=2; plural=(n > 1?1:0); break;
	case 77: nplurals=2; plural=(n > 1?1:0); break;
	case 78: nplurals=2; plural=(n == 1?0:1); break;
	case 79: nplurals=2; plural=(n > 1?1:0); break;
	case 80: nplurals=2; plural=(n == 1?0:1); break;
	case 81: nplurals=2; plural=(n == 1?0:1); break;
	case 82: nplurals=2; plural=(n == 1?0:1); break;
	case 83: nplurals=2; plural=(n == 1?0:1); break;
	case 84: nplurals=2; plural=(n == 1?0:1); break;
	case 85: nplurals=2; plural=(n == 1?0:1); break;
	case 86: nplurals=2; plural=(n == 1?0:1); break;
	case 87: nplurals=2; plural=(n == 1?0:1); break;
	case 88: nplurals=2; plural=(n == 1?0:1); break;
	case 89: nplurals=2; plural=(n > 1?1:0); break;
	case 90: nplurals=2; plural=(n == 1?0:1); break;
	case 91: nplurals=2; plural=(n == 1?0:1); break;
	case 92: nplurals=2; plural=(n == 1?0:1); break;
	case 93: nplurals=1; plural=0; break;
	case 94: nplurals=2; plural=(n == 1?0:1); break;
	case 95: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 96: nplurals=2; plural=(n == 1?0:1); break;
	case 97: nplurals=2; plural=(n == 1?0:1); break;
	case 98: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2); break;
	case 99: nplurals=2; plural=(n == 1?0:1); break;
	case 100: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 101: nplurals=2; plural=(n == 1?0:1); break;
	case 102: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3; break;
	case 103: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 104: nplurals=2; plural=(n == 1?0:1); break;
	case 105: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2; break;
	case 106: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0); break;
	case 107: nplurals=2; plural=(n == 1?0:1); break;
	case 108: nplurals=2; plural=(n == 1?0:1); break;
	case 109: nplurals=2; plural=(n == 1?0:1); break;
	case 110: nplurals=1; plural=0; break;
	case 111: nplurals=2; plural=(n == 1?0:1); break;
	case 112: nplurals=2; plural=(n == 1?0:1); break;
	case 113: nplurals=2; plural=(n > 1?1:0); break;
	case 114: nplurals=2; plural=(n == 1?0:1); break;
	case 115: nplurals=1; plural=0; break;
	case 116: nplurals=2; plural=(n == 1?0:1); break;
	case 117: nplurals=1; plural=0; break;
	case 118: nplurals=1; plural=0; break;
	case 119: nplurals=2; plural=(n > 1?1:0); break;
	case 120: nplurals=2; plural=(n > 1?1:0); break;
	case 121: nplurals=2; plural=(n == 1?0:1); break;
	case 122: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 123: nplurals=2; plural=(n == 1?0:1); break;
	case 124: nplurals=1; plural=0;; break;
	case 125: nplurals=2; plural=(n > 1?1:0); break;
	case 126: nplurals=1; plural=0; break;
	case 127: nplurals=2; plural=(n > 1?1:0); break;
	case 128: nplurals=4; plural= ((n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3); break;
	case 129: nplurals=1; plural=0; break;
	case 130: nplurals=1; plural=0; break;
	case 131: nplurals=2; plural=(n == 1?0:1); break;

	}
	if(nplurals == 0) return "i10n_pluralforms: Oops! Unknown language " + language;
	String parsedString[] = pluralforms.split("\\|");
	if(parsedString.length < nplurals) return "i10n_pluralforms: Oops! Not enough plural forms in \"" + pluralforms + "\" for language " + language;

    return parsedString[plural];	
}

}