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 7ead1200..966f574a 100644 --- a/src/main/java/pl/allegro/finance/tradukisto/internal/Container.java +++ b/src/main/java/pl/allegro/finance/tradukisto/internal/Container.java @@ -20,6 +20,10 @@ import pl.allegro.finance.tradukisto.internal.languages.german.GermanIntegerToWordsConverter; import pl.allegro.finance.tradukisto.internal.languages.german.GermanThousandToWordsConverter; import pl.allegro.finance.tradukisto.internal.languages.german.GermanValues; +import pl.allegro.finance.tradukisto.internal.languages.greek.GreekBigNumberToWordsConverter; +import pl.allegro.finance.tradukisto.internal.languages.greek.GreekIntegerToWordsConverter; +import pl.allegro.finance.tradukisto.internal.languages.greek.GreekValues; +import pl.allegro.finance.tradukisto.internal.languages.greek.GreekValuesForSmallNumbers; import pl.allegro.finance.tradukisto.internal.languages.hindi.HindiBigDecimalToBankingMoneyConverter; import pl.allegro.finance.tradukisto.internal.languages.hindi.HindiValues; import pl.allegro.finance.tradukisto.internal.languages.hindi.IndianNumberToWordsConverter; @@ -131,6 +135,23 @@ public static Container czechContainer() { return new Container(integerConverter, null, bigDecimalBankingMoneyValueConverter); } + public static Container greekContainer() { + GreekValues values = new GreekValues(); + GreekBigNumberToWordsConverter containerForBigNumbers = new GreekBigNumberToWordsConverter(values.baseNumbers()); + Container containerForSmallNumbers = new Container(new GreekValuesForSmallNumbers()); + + IntegerToStringConverter integerConverter = new GreekIntegerToWordsConverter( + new NumberToWordsConverter(containerForBigNumbers, values.pluralForms()), + containerForSmallNumbers.getIntegerConverter(), + values.exceptions() + ); + + BigDecimalToStringConverter bigDecimalBankingMoneyValueConverter = + new BigDecimalToBankingMoneyConverter(integerConverter, values.currency()); + + return new Container(integerConverter, null, bigDecimalBankingMoneyValueConverter); + } + public static Container slovakContainer() { SlovakValues slovakValues = new SlovakValues(); Container containerForBigNumbers = new Container(slovakValues); diff --git a/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekBigNumberToWordsConverter.java b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekBigNumberToWordsConverter.java new file mode 100644 index 00000000..81919c27 --- /dev/null +++ b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekBigNumberToWordsConverter.java @@ -0,0 +1,62 @@ +package pl.allegro.finance.tradukisto.internal.languages.greek; + +import pl.allegro.finance.tradukisto.internal.GenderAwareIntegerToStringConverter; +import pl.allegro.finance.tradukisto.internal.languages.GenderForms; +import pl.allegro.finance.tradukisto.internal.languages.GenderType; +import pl.allegro.finance.tradukisto.internal.support.Range; + +import java.util.Map; + +import static java.lang.String.format; + +public class GreekBigNumberToWordsConverter implements GenderAwareIntegerToStringConverter { + + private final Map baseValues; + + public GreekBigNumberToWordsConverter(Map baseValues) { + this.baseValues = baseValues; + } + + @Override + public String asWords(Integer value, GenderType genderType) { + if (baseValues.containsKey(value)) { + return baseValues.get(value).formFor(genderType); + } + if (Range.closed(21, 99).contains(value)) { + return twoDigitsNumberAsString(value, genderType); + } + if (Range.closed(101, 999).contains(value)) { + return threeDigitsNumberAsString(value, genderType); + } + if (Range.closed(1000, 999999).contains(value)) { + return thousandsAsString(value, genderType); + } + + throw new IllegalArgumentException(format("Can't convert %d", value)); + } + + private String twoDigitsNumberAsString(Integer value, GenderType genderType) { + Integer units = value % 10; + Integer tens = value - units; + return format("%s %s", asWords(tens, genderType), asWords(units, genderType)); + } + + private String threeDigitsNumberAsString(Integer value, GenderType genderType) { + Integer tensWithUnits = value % 100; + Integer hundreds = value - tensWithUnits; + GenderType thousandsGenderType = genderType; + if (hundreds / 100 == 1 && tensWithUnits > 0) + thousandsGenderType = GenderType.FEMININE; + return format("%s %s", asWords(hundreds, thousandsGenderType), asWords(tensWithUnits, genderType)); + } + + private String thousandsAsString(Integer value, GenderType genderType) { + Integer thousands = value / 1000; + Integer other = value % 1000; + + if (other == 0) { + return format("%s", asWords(thousands, genderType)); + } + return format("%s %s", asWords(thousands, genderType), asWords(other, genderType)); + } +} diff --git a/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekIntegerToWordsConverter.java b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekIntegerToWordsConverter.java new file mode 100644 index 00000000..601684b8 --- /dev/null +++ b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekIntegerToWordsConverter.java @@ -0,0 +1,46 @@ +package pl.allegro.finance.tradukisto.internal.languages.greek; + +import pl.allegro.finance.tradukisto.internal.IntegerToStringConverter; +import pl.allegro.finance.tradukisto.internal.LongToStringConverter; +import pl.allegro.finance.tradukisto.internal.NumberProcessor; + +import java.util.Map; + +public class GreekIntegerToWordsConverter implements IntegerToStringConverter, LongToStringConverter { + private final Map exceptions; + + private final NumberProcessor numberProcessor; + + public GreekIntegerToWordsConverter( + IntegerToStringConverter bigNumbersConverter, + IntegerToStringConverter smallNumbersConverter, + Map exceptions + ) { + this.numberProcessor = new NumberProcessor(bigNumbersConverter, smallNumbersConverter); + this.exceptions = exceptions; + } + + @Override + public String asWords(Integer value) { + if (exceptions.containsKey(value)) { + return exceptions.get(value); + } + + Integer bigNumber = value / 1000; + Integer smallNumber = value % 1000; + + return numberProcessor.process(bigNumber, smallNumber); + } + + @Override + public String asWords(Long value) { + if (exceptions.containsKey(value)) { + return exceptions.get(value); + } + + Integer bigNumber = value.intValue() / 1000; + Integer smallNumber = value.intValue() % 1000; + + return numberProcessor.process(bigNumber, smallNumber); + } +} diff --git a/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekPluralForms.java b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekPluralForms.java new file mode 100644 index 00000000..5b6e4377 --- /dev/null +++ b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekPluralForms.java @@ -0,0 +1,40 @@ +package pl.allegro.finance.tradukisto.internal.languages.greek; + +import pl.allegro.finance.tradukisto.internal.languages.GenderType; +import pl.allegro.finance.tradukisto.internal.languages.PluralForms; +import pl.allegro.finance.tradukisto.internal.support.Range; + +public class GreekPluralForms implements PluralForms { + + private final String singularForm; + private final String pluralForm; + private final String genitivePluralForm; + private final GenderType genderType; + + public GreekPluralForms(String singularForm, String pluralForm, String genitivePluralForm) { + this(singularForm, pluralForm, genitivePluralForm, GenderType.NON_APPLICABLE); + } + + public GreekPluralForms(String singularForm, String pluralForm, String genitivePluralForm, GenderType genderType) { + this.singularForm = singularForm; + this.pluralForm = pluralForm; + this.genitivePluralForm = genitivePluralForm; + this.genderType = genderType; + } + + @Override + public String formFor(Integer value) { + if (value == 1) { + return singularForm; + } + if (Range.closed(3, 4).contains(value)) { + return genitivePluralForm; + } + return pluralForm; + } + + @Override + public GenderType genderType() { + return genderType; + } +} diff --git a/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekValues.java b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekValues.java new file mode 100644 index 00000000..f5ec6eae --- /dev/null +++ b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekValues.java @@ -0,0 +1,89 @@ +package pl.allegro.finance.tradukisto.internal.languages.greek; + +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.HashMap; +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 GreekValues implements BaseValues { + + @Override + public Map baseNumbers() { + return baseNumbersBuilder() + .put(0, "μηδέν") + .put(1, genderForms("ένα", "μία", "ένας", "μία")) + .put(2, "δύο") + .put(3, genderForms("τρία", "τρεις", "τρεις", "τρεις")) + .put(4, genderForms("τέσσερα", "τέσσερεις", "τέσσερεις", "τέσσερεις")) + .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, genderForms("εκατό", "εκατόν", "εκατόν", "εκατόν")) + .put(200, genderForms("διακόσια", "διακόσιες", "διακόσιες", "διακόσια")) + .put(300, genderForms("τριακόσια", "τριακόσιες", "τριακόσιες", "τριακόσια")) + .put(400, genderForms("τετρακόσια", "τετρακόσιες", "τετρακόσιες", "τετρακόσια")) + .put(500, genderForms("πεντακόσια", "πεντακόσιες", "πεντακόσιες", "πεντακόσια")) + .put(600, genderForms("εξακόσια", "εξακόσιες", "εξακόσιες", "εξακόσια")) + .put(700, genderForms("επτακόσια", "επτακόσιες", "επτακόσιες", "επτακόσια")) + .put(800, genderForms("οκτακόσια", "οκτακόσιες", "οκτακόσιες", "οκτακόσια")) + .put(900, genderForms("εννιακόσια", "εννιακόσιες", "εννιακόσιες", "εννιακόσια")) + .build(); + } + + @Override + public List pluralForms() { + return Arrays.asList( + new GreekPluralForms("χίλια", "χιλιάδες", "χιλιάδες", GenderType.FEMININE), + new GreekPluralForms("εκατομμύριο", "εκατομμύρια", "εκατομμύρια", GenderType.MASCULINE), + new GreekPluralForms("δισεκατομμύριο", "δισεκατομμύρια", "εκατομμύρια", GenderType.MASCULINE), + new GreekPluralForms("τρισεκατομμύριο", "τρισεκατομμύρια", "εκατομμύρια", GenderType.MASCULINE), + new GreekPluralForms("τετράκις εκατομμύριο", "τετράκις εκατομμύρια", "τετράκις εκατομμύρια", GenderType.MASCULINE), + new GreekPluralForms("πεντάκις εκατομμύριο", "πεντάκις εκατομμύρια", "πεντάκις εκατομμύρια", GenderType.MASCULINE) + ); + } + + @Override + public String currency() { + return "€"; + } + + @Override + public char twoDigitsNumberSeparator() { + return ' '; + } + + public Map exceptions() { + Map result = new HashMap<>(); + result.put(100, "εκατό"); + result.put(1000, "χίλια"); + return result; + } +} diff --git a/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekValuesForSmallNumbers.java b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekValuesForSmallNumbers.java new file mode 100644 index 00000000..3ee4bc3c --- /dev/null +++ b/src/main/java/pl/allegro/finance/tradukisto/internal/languages/greek/GreekValuesForSmallNumbers.java @@ -0,0 +1,42 @@ +package pl.allegro.finance.tradukisto.internal.languages.greek; + +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 pl.allegro.finance.tradukisto.internal.languages.czech.CzechPluralForms; +import pl.allegro.finance.tradukisto.internal.languages.czech.CzechValues; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static pl.allegro.finance.tradukisto.internal.languages.GenderForms.genderForm; + +public class GreekValuesForSmallNumbers implements BaseValues { + private final GreekValues originalValues = new GreekValues(); + + @Override + public Map baseNumbers() { + Map baseNumbers = originalValues.baseNumbers(); + baseNumbers.put(1, genderForm(baseNumbers.get(1).formFor(GenderType.MASCULINE))); + baseNumbers.put(3, genderForm(baseNumbers.get(3).formFor(GenderType.MASCULINE))); + baseNumbers.put(4, genderForm(baseNumbers.get(4).formFor(GenderType.MASCULINE))); + return baseNumbers; + } + + @Override + public List pluralForms() { + return Arrays.asList(new GreekPluralForms("", "", "", GenderType.NON_APPLICABLE)); + } + + @Override + public String currency() { + return originalValues.currency(); + } + + @Override + public char twoDigitsNumberSeparator() { + return ' '; + } +} diff --git a/src/test/groovy/pl/allegro/finance/tradukisto/internal/languages/greek/GreekPluralFormsTest.groovy b/src/test/groovy/pl/allegro/finance/tradukisto/internal/languages/greek/GreekPluralFormsTest.groovy new file mode 100644 index 00000000..2c92e778 --- /dev/null +++ b/src/test/groovy/pl/allegro/finance/tradukisto/internal/languages/greek/GreekPluralFormsTest.groovy @@ -0,0 +1,24 @@ +package pl.allegro.finance.tradukisto.internal.languages.greek + +import pl.allegro.finance.tradukisto.internal.languages.GenderType +import spock.lang.Specification +import spock.lang.Unroll + +class GreekPluralFormsTest extends Specification { + + def pluralForms = new GreekPluralForms("χίλια", "χιλιάδες", "χιλιάδες", GenderType.FEMININE) + + def "should provide value for singular form"() { + expect: + pluralForms.formFor(1) == "χίλια" + } + + @Unroll + def "should provide value for plural form: #value"() { + expect: + pluralForms.formFor(value) == "χιλιάδες" + + where: + value << (2..4) + } +} diff --git a/src/test/groovy/pl/allegro/finance/tradukisto/internal/languages/greek/GreekValuesTest.groovy b/src/test/groovy/pl/allegro/finance/tradukisto/internal/languages/greek/GreekValuesTest.groovy new file mode 100644 index 00000000..abf3ae71 --- /dev/null +++ b/src/test/groovy/pl/allegro/finance/tradukisto/internal/languages/greek/GreekValuesTest.groovy @@ -0,0 +1,133 @@ +package pl.allegro.finance.tradukisto.internal.languages.greek + +import spock.lang.Ignore +import spock.lang.Specification +import spock.lang.Unroll + +import static pl.allegro.finance.tradukisto.internal.Container.greekContainer + +class GreekValuesTest extends Specification { + + static intConverter = greekContainer().getIntegerConverter() + static longConverter = greekContainer().getLongConverter() + + def "ATest"() { + expect: + intConverter.asWords(100) == "εκατό" + } + + @Unroll + def "should convert #value to '#words' in Greek"() { + expect: + intConverter.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 | "δεκαεννέα" + 20 | "είκοσι" + 10 | "δέκα" + 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| "δύο δισεκατομμύρια εκατόν σαράντα επτά εκατομμύρια τετρακόσιες ογδόντα τρεις χιλιάδες εξακόσια σαράντα επτά" + } + + @Ignore("Needs Greek long converter is broken and values for trillion, quadrillion, quintillion") + @Unroll + def "should convert long #value to '#words' in Greek"() { + expect: + longConverter.asWords(value) == words + + where: + value | words + 5_000_000_000 | "πέντε δισεκατομμύρια" + + 1_000_000_000_000 | "ένα τρισεκατομμύριο" + 2_000_000_000_000 | "δύο τρισεκατομμύρια" + 5_000_000_000_000 | "πέντε τρισεκατομμύρια" + + 1_000_000_000_000_000 | "ένα τετράκις εκατομμύριο" + 2_000_000_000_000_000 | "δύο τετράκις εκατομμύρια" + 5_000_000_000_000_000 | "πέντε τετράκις εκατομμύρια" + + 1_000_000_000_000_000_000 | "ένα πεντάκις εκατομμύριο" + 2_000_000_000_000_000_000 | "δύο πεντάκις εκατομμύριο" + } +}