From 1644df574a5370a4d6fe1dc04d7bc295bac3b2bf Mon Sep 17 00:00:00 2001 From: Nikolay Gavrilov <83949760+gavrilovniki@users.noreply.github.com> Date: Fri, 22 Jul 2022 10:51:48 +0300 Subject: [PATCH] Add bulgarian support (#63) --- .../finance/tradukisto/MoneyConverters.java | 4 +- .../finance/tradukisto/ValueConverters.java | 2 + .../tradukisto/internal/Container.java | 20 ++++ .../BulgarianIntegerToWordsConverter.java | 47 ++++++++ .../bulgarian/BulgarianPluralForms.java | 27 +++++ .../languages/bulgarian/BulgarianValues.java | 81 +++++++++++++ .../tradukisto/MoneyConvertersTest.groovy | 6 + .../tradukisto/ValueConvertersTest.groovy | 10 ++ .../bulgarian/BulgarianValuesTest.groovy | 106 ++++++++++++++++++ 9 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianIntegerToWordsConverter.java create mode 100644 src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianPluralForms.java create mode 100644 src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianValues.java create mode 100644 src/test/groovy/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianValuesTest.groovy diff --git a/src/main/java/pl/allegro/finance/tradukisto/MoneyConverters.java b/src/main/java/pl/allegro/finance/tradukisto/MoneyConverters.java index 9b21e63c..7ac137ce 100644 --- a/src/main/java/pl/allegro/finance/tradukisto/MoneyConverters.java +++ b/src/main/java/pl/allegro/finance/tradukisto/MoneyConverters.java @@ -21,6 +21,7 @@ import static pl.allegro.finance.tradukisto.internal.Container.slovakContainer; import static pl.allegro.finance.tradukisto.internal.Container.turkishContainer; import static pl.allegro.finance.tradukisto.internal.Container.ukrainianContainer; +import static pl.allegro.finance.tradukisto.internal.Container.bulgarianContainer; public enum MoneyConverters { @@ -39,7 +40,8 @@ public enum MoneyConverters { TURKISH_BANKING_MONEY_VALUE(turkishContainer().getBankingMoneyConverter()), SERBIAN_BANKING_MONEY_VALUE(serbianContainer().getBankingMoneyConverter()), SERBIAN_CYRILLIC_BANKING_MONEY_VALUE(serbianCyrillicContainer().getBankingMoneyConverter()), - FRENCH_BANKING_MONEY_VALUE(frenchContainer().getBankingMoneyConverter()); + FRENCH_BANKING_MONEY_VALUE(frenchContainer().getBankingMoneyConverter()), + BULGARIAN_BANKING_MONEY_VALUE(bulgarianContainer().getBankingMoneyConverter()); private final BigDecimalToStringConverter converter; diff --git a/src/main/java/pl/allegro/finance/tradukisto/ValueConverters.java b/src/main/java/pl/allegro/finance/tradukisto/ValueConverters.java index 6c3f9dec..5cab34a9 100644 --- a/src/main/java/pl/allegro/finance/tradukisto/ValueConverters.java +++ b/src/main/java/pl/allegro/finance/tradukisto/ValueConverters.java @@ -25,6 +25,7 @@ import static pl.allegro.finance.tradukisto.internal.Container.slovakContainer; import static pl.allegro.finance.tradukisto.internal.Container.turkishContainer; import static pl.allegro.finance.tradukisto.internal.Container.ukrainianContainer; +import static pl.allegro.finance.tradukisto.internal.Container.bulgarianContainer; public enum ValueConverters { @@ -41,6 +42,7 @@ public enum ValueConverters { UKRAINIAN_INTEGER(ukrainianContainer().getNumbersConverter(), "uk"), SERBIAN_INTEGER(serbianContainer().getNumbersConverter(), Arrays.asList("sr", getLanguageCodeFor("sr", "Latn"))), SERBIAN_CYRILLIC_INTEGER(serbianCyrillicContainer().getNumbersConverter(), getLanguageCodeFor("sr", "Cyrl")), + BULGARIAN_INTEGER(bulgarianContainer().getNumbersConverter(), "bg"), FRENCH_INTEGER(frenchContainer().getNumbersConverter(), "fr"), TURKISH_INTEGER(turkishContainer().getNumbersConverter(), "tr"); diff --git a/src/main/java/pl/allegro/finance/tradukisto/internal/Container.java b/src/main/java/pl/allegro/finance/tradukisto/internal/Container.java index 79b8a4d8..156905ab 100644 --- a/src/main/java/pl/allegro/finance/tradukisto/internal/Container.java +++ b/src/main/java/pl/allegro/finance/tradukisto/internal/Container.java @@ -3,6 +3,8 @@ import pl.allegro.finance.tradukisto.internal.converters.BigDecimalToBankingMoneyConverter; import pl.allegro.finance.tradukisto.internal.converters.HundredsToWordsConverter; import pl.allegro.finance.tradukisto.internal.converters.IntegerToWordsConverter; +import pl.allegro.finance.tradukisto.internal.languages.bulgarian.BulgarianIntegerToWordsConverter; +import pl.allegro.finance.tradukisto.internal.languages.bulgarian.BulgarianValues; import pl.allegro.finance.tradukisto.internal.languages.czech.CzechIntegerToWordsConverter; import pl.allegro.finance.tradukisto.internal.languages.czech.CzechValues; import pl.allegro.finance.tradukisto.internal.languages.czech.CzechValuesForSmallNumbers; @@ -52,6 +54,24 @@ public static Container serbianCyrillicContainer() { return new Container(new SerbianCyrillicValues()); } + public static Container bulgarianContainer() { + BulgarianValues bulgarianValues = new BulgarianValues(); + + HundredsToWordsConverter hundredsToStringConverter = new HundredsToWordsConverter(bulgarianValues.baseNumbers(), + bulgarianValues.twoDigitsNumberSeparator()); + + IntegerToStringConverter integerToStringConverter = new BulgarianIntegerToWordsConverter( + hundredsToStringConverter, + bulgarianValues.pluralForms(), + bulgarianValues.oneThousandException()); + + BigDecimalToStringConverter bigDecimalConverter = new BigDecimalToBankingMoneyConverter( + integerToStringConverter, + bulgarianValues.currency()); + + return new Container(integerToStringConverter, bigDecimalConverter); + } + public static Container ukrainianContainer() { return new Container(new UkrainianValues()); } diff --git a/src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianIntegerToWordsConverter.java b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianIntegerToWordsConverter.java new file mode 100644 index 00000000..a88c730b --- /dev/null +++ b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianIntegerToWordsConverter.java @@ -0,0 +1,47 @@ +package pl.allegro.finance.tradukisto.internal.languages.bulgarian; + +import pl.allegro.finance.tradukisto.internal.converters.HundredsToWordsConverter; +import pl.allegro.finance.tradukisto.internal.converters.IntegerToWordsConverter; +import pl.allegro.finance.tradukisto.internal.languages.PluralForms; +import pl.allegro.finance.tradukisto.internal.support.NumberChunking; + +import java.util.List; + +import static com.google.common.collect.Lists.reverse; +import static java.lang.String.format; + +public class BulgarianIntegerToWordsConverter extends IntegerToWordsConverter { + + private final String oneThousand; + + private static final String MERGE_PATTERN = "%s %s"; + private static final int ONE_THOUSAND = 1000; + private static final int TWO_THOUSAND = 2000; + + public BulgarianIntegerToWordsConverter(HundredsToWordsConverter hundredsToStringConverter, List pluralForms, String oneThousand) { + super(hundredsToStringConverter, pluralForms); + this.oneThousand = oneThousand; + } + + @Override + public String asWords(Integer value) { + if (value == ONE_THOUSAND) { + return oneThousand; + } + + if (isBetweenOneThousandAndTwoThousand(value)) { + int remainder = getNumbersSmallerThanOneThousand(value); + return format(MERGE_PATTERN, oneThousand, super.asWords(remainder)); + } else { + return super.asWords(value); + } + } + + private boolean isBetweenOneThousandAndTwoThousand(Integer value) { + return value >= ONE_THOUSAND && value < TWO_THOUSAND; + } + + private Integer getNumbersSmallerThanOneThousand(Integer values) { + return values % ONE_THOUSAND; + } +} diff --git a/src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianPluralForms.java b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianPluralForms.java new file mode 100644 index 00000000..39df7d89 --- /dev/null +++ b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianPluralForms.java @@ -0,0 +1,27 @@ +package pl.allegro.finance.tradukisto.internal.languages.bulgarian; + +import pl.allegro.finance.tradukisto.internal.languages.GenderType; +import pl.allegro.finance.tradukisto.internal.languages.SlavonicPluralForms; + +public class BulgarianPluralForms extends SlavonicPluralForms { + + private final String singularForm; + + public BulgarianPluralForms(String singularForm, String pluralForm, String genitivePluralForm, GenderType genderType) { + super(singularForm, pluralForm, genitivePluralForm, genderType); + this.singularForm = singularForm; + } + + @Override + public String formFor(Integer value) { + if (useSingular(value)) { + return singularForm; + } else { + return super.formFor(value); + } + } + + private boolean useSingular(Integer value) { + return value == 1 || (value % 100 != 11 && value % 10 == 1); + } +} diff --git a/src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianValues.java b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianValues.java new file mode 100644 index 00000000..8a71e6bd --- /dev/null +++ b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianValues.java @@ -0,0 +1,81 @@ +package pl.allegro.finance.tradukisto.internal.languages.bulgarian; + +import pl.allegro.finance.tradukisto.internal.BaseValues; +import pl.allegro.finance.tradukisto.internal.languages.GenderForms; +import pl.allegro.finance.tradukisto.internal.languages.GenderType; +import pl.allegro.finance.tradukisto.internal.languages.PluralForms; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static pl.allegro.finance.tradukisto.internal.languages.GenderForms.genderForms; +import static pl.allegro.finance.tradukisto.internal.support.BaseNumbersBuilder.baseNumbersBuilder; + +public class BulgarianValues implements BaseValues { + @Override + public Map baseNumbers() { + return baseNumbersBuilder() + .put(0, "нула") + .put(1, genderForms("един", "една", "едно", "един")) + .put(2, genderForms("два", "две", "две", "два")) + .put(3, "три") + .put(4, "четири") + .put(5, "пет") + .put(6, "шест") + .put(7, "седем") + .put(8, "осем") + .put(9, "девет") + .put(10, "десет") + .put(11, "единадесет") + .put(12, "дванадесет") + .put(13, "тринадесет") + .put(14, "четиринадесет") + .put(15, "петнадесет") + .put(16, "шестнадесет") + .put(17, "седемнадесет") + .put(18, "осемнадесет") + .put(19, "деветнадесет") + .put(20, "двадесет") + .put(30, "тридесет") + .put(40, "четиридесет") + .put(50, "петдесет") + .put(60, "шестдесет") + .put(70, "седемдесет") + .put(80, "осемдесет") + .put(90, "деветдесет") + .put(100, "сто") + .put(200, "двеста") + .put(300, "триста") + .put(400, "четиристотин") + .put(500, "петстотин") + .put(600, "шестстотин") + .put(700, "седемстотин") + .put(800, "осемстотин") + .put(900, "деветстотин") + .build(); + } + + @Override + public List pluralForms() { + return Arrays.asList( + new BulgarianPluralForms("", "", "", GenderType.MASCULINE), + new BulgarianPluralForms("хиляди", "хиляди", "хиляди", GenderType.FEMININE), + new BulgarianPluralForms("милион", "милиона", "милиона", GenderType.MASCULINE), + new BulgarianPluralForms("милиард", "милиарда", "милиарда", GenderType.MASCULINE)); + } + + @Override + public String currency() { + return "лв"; + } + + @Override + public char twoDigitsNumberSeparator() { + return ' '; + } + + public String oneThousandException() { + return "хиляда"; + } +} diff --git a/src/test/groovy/pl/allegro/finance/tradukisto/MoneyConvertersTest.groovy b/src/test/groovy/pl/allegro/finance/tradukisto/MoneyConvertersTest.groovy index 02d9bea8..d783e789 100644 --- a/src/test/groovy/pl/allegro/finance/tradukisto/MoneyConvertersTest.groovy +++ b/src/test/groovy/pl/allegro/finance/tradukisto/MoneyConvertersTest.groovy @@ -5,6 +5,7 @@ import spock.lang.Specification import static pl.allegro.finance.tradukisto.MoneyConverters.AMERICAN_ENGLISH_BANKING_MONEY_VALUE import static pl.allegro.finance.tradukisto.MoneyConverters.BRAZILIAN_PORTUGUESE_BANKING_MONEY_VALUE +import static pl.allegro.finance.tradukisto.MoneyConverters.BULGARIAN_BANKING_MONEY_VALUE import static pl.allegro.finance.tradukisto.MoneyConverters.CZECH_BANKING_MONEY_VALUE import static pl.allegro.finance.tradukisto.MoneyConverters.ENGLISH_BANKING_MONEY_VALUE import static pl.allegro.finance.tradukisto.MoneyConverters.FRENCH_BANKING_MONEY_VALUE @@ -42,6 +43,11 @@ class MoneyConvertersTest extends Specification { RUSSIAN_BANKING_MONEY_VALUE.asWords(1_234.56) == "одна тысяча двести тридцать четыре руб. 56/100" } + def "should convert money in Bulgarian"() { + expect: + BULGARIAN_BANKING_MONEY_VALUE.asWords(1_234.56) == "хиляда двеста тридесет четири лв 56/100" + } + def "should convert money in Polish"() { expect: POLISH_BANKING_MONEY_VALUE.asWords(1_234.56) == "jeden tysiąc dwieście trzydzieści cztery PLN 56/100" diff --git a/src/test/groovy/pl/allegro/finance/tradukisto/ValueConvertersTest.groovy b/src/test/groovy/pl/allegro/finance/tradukisto/ValueConvertersTest.groovy index 65eca4aa..5fe4c213 100644 --- a/src/test/groovy/pl/allegro/finance/tradukisto/ValueConvertersTest.groovy +++ b/src/test/groovy/pl/allegro/finance/tradukisto/ValueConvertersTest.groovy @@ -27,6 +27,11 @@ class ValueConvertersTest extends Specification { RUSSIAN_INTEGER.asWords(1_234) == "одна тысяча двести тридцать четыре" } + def "should convert numbers in Bulgarian"() { + expect: + BULGARIAN_INTEGER.asWords(1_234) == "хиляда двеста тридесет четири" + } + def "should convert numbers in Italian"() { expect: ITALIAN_INTEGER.asWords(1_234) == "milleduecentotrentaquattro" @@ -115,6 +120,11 @@ class ValueConvertersTest extends Specification { getByLocaleOrDefault(new Locale("ru"), null).asWords(1_234) == "одна тысяча двести тридцать четыре" } + def "getByLocaleOrDefault for Bulgarian"() { + expect: + getByLocaleOrDefault(new Locale("bg"), null).asWords(1_234) == "хиляда двеста тридесет четири" + } + def "getByLocaleOrDefault for Italian"() { expect: getByLocaleOrDefault(new Locale("it"), null).asWords(1_234) == "milleduecentotrentaquattro" diff --git a/src/test/groovy/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianValuesTest.groovy b/src/test/groovy/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianValuesTest.groovy new file mode 100644 index 00000000..97ce6cb9 --- /dev/null +++ b/src/test/groovy/pl/allegro/finance/tradukisto/internal/languages/bulgarian/BulgarianValuesTest.groovy @@ -0,0 +1,106 @@ +package pl.allegro.finance.tradukisto.internal.languages.bulgarian + +import spock.lang.Specification +import spock.lang.Unroll + +import static pl.allegro.finance.tradukisto.internal.Container.bulgarianContainer + +class BulgarianValuesTest extends Specification { + + static converter = bulgarianContainer().getNumbersConverter() + + @Unroll + def "should convert #value to '#words' in Bulgarian"() { + expect: + converter.asWords(value) == words + + where: + value | words + 0 | "нула" + 1 | "един" + 2 | "два" + 3 | "три" + 4 | "четири" + 5 | "пет" + 6 | "шест" + 7 | "седем" + 8 | "осем" + 9 | "девет" + + 11 | "единадесет" + 12 | "дванадесет" + 13 | "тринадесет" + 14 | "четиринадесет" + 15 | "петнадесет" + 16 | "шестнадесет" + 17 | "седемнадесет" + 18 | "осемнадесет" + 19 | "деветнадесет" + + 10 | "десет" + 20 | "двадесет" + 30 | "тридесет" + 40 | "четиридесет" + 50 | "петдесет" + 60 | "шестдесет" + 70 | "седемдесет" + 80 | "осемдесет" + 90 | "деветдесет" + + 21 | "двадесет един" + 37 | "тридесет седем" + 43 | "четиридесет три" + 58 | "петдесет осем" + 69 | "шестдесет девет" + 76 | "седемдесет шест" + 82 | "осемдесет два" + 95 | "деветдесет пет" + + 100 | "сто" + 200 | "двеста" + 300 | "триста" + 400 | "четиристотин" + 500 | "петстотин" + 600 | "шестстотин" + 700 | "седемстотин" + 800 | "осемстотин" + 900 | "деветстотин" + + 111 | "сто единадесет" + 272 | "двеста седемдесет два" + 387 | "триста осемдесет седем" + 448 | "четиристотин четиридесет осем" + 569 | "петстотин шестдесет девет" + 625 | "шестстотин двадесет пет" + 782 | "седемстотин осемдесет два" + 895 | "осемстотин деветдесет пет" + 999 | "деветстотин деветдесет девет" + + 1_000 | "хиляда" + 2_000 | "две хиляди" + 3_000 | "три хиляди" + 4_000 | "четири хиляди" + 5_000 | "пет хиляди" + 7_634 | "седем хиляди шестстотин тридесет четири" + 11_000 | "единадесет хиляди" + 15_000 | "петнадесет хиляди" + 21_000 | "двадесет една хиляди" + 24_190 | "двадесет четири хиляди сто деветдесет" + 653_000 | "шестстотин петдесет три хиляди" + 123_454 | "сто двадесет три хиляди четиристотин петдесет четири" + 700_000 | "седемстотин хиляди" + 999_999 | "деветстотин деветдесет девет хиляди деветстотин деветдесет девет" + + 1_000_000 | "един милион" + 2_000_000 | "два милиона" + 5_000_000 | "пет милиона" + 23_437_219 | "двадесет три милиона четиристотин тридесет седем хиляди двеста деветнадесет" + 100_000_000 | "сто милиона" + 123_456_789 | "сто двадесет три милиона четиристотин петдесет шест хиляди седемстотин осемдесет девет" + 322_089_890 | "триста двадесет два милиона осемдесет девет хиляди осемстотин деветдесет" + + 1_000_000_000 | "един милиард" + 2_147_483_647 | "два милиарда сто четиридесет седем милиона четиристотин осемдесет три хиляди шестстотин четиридесет седем" + } + +}