Skip to content

Commit

Permalink
Merge pull request #111 from luismateoh/spanish-support
Browse files Browse the repository at this point in the history
Add spanish support
  • Loading branch information
bartlomiejgraczyk committed Oct 13, 2023
2 parents 8e022d0 + 759771c commit 28b4397
Show file tree
Hide file tree
Showing 12 changed files with 636 additions and 5 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,19 @@ With Tradukisto, you can now transform numbers into their word equivalents easy
* 🇬🇧 English
* 🇫🇷 French
* 🇩🇪 German
* 🇮🇳 Hindi
* 🇮🇹 Italian
* 🇰🇿 Kazakh
* 🇱🇻 Latvian
* 🇵🇱 Polish
* 🇷🇺 Russian
* 🇷🇸 Serbian (Cyrillic)
* 🇷🇸 Serbian (Latin)
* 🇸🇰 Slovak
* 🇪🇸 Spanish
* 🇸🇪 Swedish
* 🇺🇦 Ukrainian
* 🇷🇸 Serbian (Latin)
* 🇷🇸 Serbian (Cyrillic)
* 🇹🇷 Turkish
* 🇮🇳 Hindi

* 🇺🇦 Ukrainian

Is your language missing from the list? We encourage you to contribute to our project.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static pl.allegro.finance.tradukisto.internal.Container.serbianCyrillicContainer;
import static pl.allegro.finance.tradukisto.internal.Container.serbianContainer;
import static pl.allegro.finance.tradukisto.internal.Container.slovakContainer;
import static pl.allegro.finance.tradukisto.internal.Container.spanishContainer;
import static pl.allegro.finance.tradukisto.internal.Container.swedishContainer;
import static pl.allegro.finance.tradukisto.internal.Container.turkishContainer;
import static pl.allegro.finance.tradukisto.internal.Container.ukrainianContainer;
Expand Down Expand Up @@ -48,6 +49,7 @@ public enum MoneyConverters {
FRENCH_BANKING_MONEY_VALUE(frenchContainer().getBankingMoneyConverter()),
BULGARIAN_BANKING_MONEY_VALUE(bulgarianContainer().getBankingMoneyConverter()),
DUTCH_BANKING_MONEY_VALUE(dutchContainer().getBankingMoneyConverter()),
SPANISH_BANKING_MONEY_VALUE(spanishContainer().getBankingMoneyConverter()),
HINDI_BANKING_MONEY_VALUE(Container.hindiContainer().getBankingMoneyConverter()),
SWEDISH_BANKING_MONEY_VALUE(swedishContainer().getBankingMoneyConverter());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public enum ValueConverters {
FRENCH_INTEGER(Container.frenchContainer().getIntegerConverter(), "fr"),
TURKISH_INTEGER(Container.turkishContainer().getIntegerConverter(), "tr"),
DUTCH_INTEGER(Container.dutchContainer().getIntegerConverter(), "nl"),
SPANISH_INTEGER(Container.spanishContainer().getIntegerConverter(), "es"),
HINDI_INTEGER(Container.hindiContainer().getIntegerConverter(), "hi"),
SWEDISH_INTEGER(Container.swedishContainer().getIntegerConverter(), "sv");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
import pl.allegro.finance.tradukisto.internal.languages.serbian.SerbianValues;
import pl.allegro.finance.tradukisto.internal.languages.slovak.SlovakValues;
import pl.allegro.finance.tradukisto.internal.languages.slovak.SlovakValuesForSmallNumbers;
import pl.allegro.finance.tradukisto.internal.languages.spanish.SpanishIntegerToWordsConverter;
import pl.allegro.finance.tradukisto.internal.languages.spanish.SpanishIntegerToWordsConverterAdapter;
import pl.allegro.finance.tradukisto.internal.languages.spanish.SpanishThousandToWordsConverter;
import pl.allegro.finance.tradukisto.internal.languages.spanish.SpanishValues;
import pl.allegro.finance.tradukisto.internal.languages.swedish.SwedishHundredToWordsConverter;
import pl.allegro.finance.tradukisto.internal.languages.swedish.SwedishValues;
import pl.allegro.finance.tradukisto.internal.languages.turkish.TurkishBigDecimalToBankingMoneyConverter;
Expand Down Expand Up @@ -246,7 +250,23 @@ public static Container hindiContainer() {

return new Container(integerToStringConverter, longValueConverters, bigDecimalConverter);
}

public static Container spanishContainer() {
SpanishValues values = new SpanishValues();

SpanishThousandToWordsConverter spanishThousandToWordsConverter = new SpanishThousandToWordsConverter(
values.baseNumbers(), values.exceptions());

IntegerToStringConverter converter = new SpanishIntegerToWordsConverter(
new SpanishIntegerToWordsConverterAdapter(spanishThousandToWordsConverter, values.pluralForms()),
values.exceptions(), spanishThousandToWordsConverter);

BigDecimalToStringConverter bigDecimalBankingMoneyValueConverter = new BigDecimalToBankingMoneyConverter(
converter, values.currency());

return new Container(converter, null, bigDecimalBankingMoneyValueConverter);
}

public static Container swedishContainer() {
SwedishValues swedishBaseValues = new SwedishValues();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package pl.allegro.finance.tradukisto.internal.languages.spanish;

import pl.allegro.finance.tradukisto.internal.IntegerToStringConverter;
import pl.allegro.finance.tradukisto.internal.MultiFormNumber;
import pl.allegro.finance.tradukisto.internal.NumberProcessor;

import java.util.Map;

public class SpanishIntegerToWordsConverter implements IntegerToStringConverter {

private final IntegerToStringConverter bigNumbersConverter;
private final Map<Integer, MultiFormNumber> exceptions;
private final IntegerToStringConverter smallNumbersConverter;

public SpanishIntegerToWordsConverter(IntegerToStringConverter bigNumbersConverter,
Map<Integer, MultiFormNumber> exceptions,
IntegerToStringConverter smallNumbersConverter) {
this.bigNumbersConverter = bigNumbersConverter;
this.exceptions = exceptions;
this.smallNumbersConverter = smallNumbersConverter;
}

@Override
public String asWords(Integer value) {

if (exceptions.containsKey(value)) {
return exceptions.get(value).getAloneForm();
}

Integer bigNumber = value / 1000000;
Integer smallNumber = value % 1000000;

String numberProcessor = new NumberProcessor(bigNumbersConverter, smallNumbersConverter).process(bigNumber, smallNumber);

if (value.toString().endsWith("1") && !value.toString().endsWith("11")) {
numberProcessor += "o";
}
if (value == 1_000_000_000) {
numberProcessor = "mil millones";
}
if (value > 1_000_000_000) {
numberProcessor = numberProcessor.replace("un mil millones", "mil");
}
if (value == 2_000_000_000) {
numberProcessor = "dos mil millones";
}
if (value > 2_000_000_000) {
numberProcessor = numberProcessor.replace("un millón", "un millones")
.replace("dos mil millones", "dos mil");
}
return numberProcessor;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package pl.allegro.finance.tradukisto.internal.languages.spanish;

import pl.allegro.finance.tradukisto.internal.IntegerToStringConverter;
import pl.allegro.finance.tradukisto.internal.converters.NumberToWordsConverter;
import pl.allegro.finance.tradukisto.internal.languages.GenderType;
import pl.allegro.finance.tradukisto.internal.languages.PluralForms;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

public class SpanishIntegerToWordsConverterAdapter extends NumberToWordsConverter {

public SpanishIntegerToWordsConverterAdapter(IntegerToStringConverter hundredsToWordsConverter,
List<PluralForms> pluralForms) {
super(hundredsToWordsConverter, pluralForms);
}

@Override
protected String joinValueChunksWithForms(Iterator<Integer> chunks, Iterator<PluralForms> formsToUse) {
AtomicReference<List<String>> result = new AtomicReference<>(new ArrayList<>());

while (chunks.hasNext() && formsToUse.hasNext()) {
Integer currentChunkValue = chunks.next();
PluralForms currentForms = formsToUse.next();

if (currentChunkValue > 0) {
result.get().add(hundredsToWordsConverter.asWords(currentChunkValue, GenderType.NEUTER));
result.get().add(currentForms.formFor(currentChunkValue));
}
}

return joinParts(result.get());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package pl.allegro.finance.tradukisto.internal.languages.spanish;

import pl.allegro.finance.tradukisto.internal.IntegerToStringConverter;
import pl.allegro.finance.tradukisto.internal.MultiFormNumber;
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 SpanishThousandToWordsConverter implements IntegerToStringConverter {

private static final String MIL = "%s mil";
private static final String MIL_UN = "mil %s";
private static final String S_MIL_S = "%s mil %s";
private static final String IUN_MIL = "%siún mil";
private static final String IUN_MIL_WITH_HUNDRED = "%siún mil %s";
private static final String Y_UN_MIL = "%s y un mil";
private static final String Y_UN_MIL_WITH_HUNDRED = "%s y un mil %s";

private static final boolean HAS_NEXT_VALUE = true;
private static final boolean HAS_NOT_NEXT_VALUE = false;
private static final int HUNDRED = 100;
private static final GenderType genderType = GenderType.NEUTER;

private final Map<Integer, GenderForms> baseValues;
private final Map<Integer, MultiFormNumber> exceptions;

public SpanishThousandToWordsConverter(Map<Integer, GenderForms> baseValues, Map<Integer, MultiFormNumber> exceptions) {
this.baseValues = baseValues;
this.exceptions = exceptions;
}

@Override
public String asWords(Integer value) {
return asWords(value, HAS_NEXT_VALUE);
}

private String asWords(Integer value, boolean hasNextNumber) {
if (baseValues.containsKey(value)) {
return baseValues.get(value).formFor(genderType);
}
if (exceptions.containsKey(value)) {
if (hasNextNumber) {
return exceptions.get(value).getRegularForm();
} else {
return exceptions.get(value).getAloneForm();
}
}
if (Range.closed(31, 99).contains(value)) {
return twoDigitsNumberAsString(value);
}
if (Range.closed(101, 999).contains(value)) {
return threeDigitsNumberAsString(value);
}
if (Range.closed(1000, 999999).contains(value)) {
return thousandsAsString(value);
}
throw new IllegalArgumentException(format("Can't convert %d", value));
}

private String twoDigitsNumberAsString(Integer value) {
Integer units = value % 10;
Integer tens = value - units;
return format("%s y %s", asWords(tens), asWords(units));
}

private String threeDigitsNumberAsString(Integer value) {
Integer tensWithUnits = value % 100;
Integer hundreds = value - tensWithUnits;
boolean hasNextNumber = tensWithUnits != 0;
return format("%s %s", asWords(hundreds, hasNextNumber), asWords(tensWithUnits));
}

private String thousandsAsString(Integer value) {
Integer thousands = value / 1000;
Integer other = value % 1000;

if (isOneThousand(thousands)) {
return getOneThousandAsWords(other);
}
if (isEndsWithOne(thousands)) {
return getEndWithOneAsWords(thousands, other);
} else {
return getThousandsAsWords(thousands, other);
}
}

private String getThousandsAsWords(Integer thousands, Integer other) {
if (nothingComesAfter(other)) {
return format(MIL, asWords(thousands));
}
if (other == HUNDRED) {
return format(S_MIL_S, asWords(thousands, HAS_NOT_NEXT_VALUE), asWords(other, HAS_NOT_NEXT_VALUE));
}
return format(S_MIL_S, asWords(thousands), asWords(other, HAS_NEXT_VALUE));
}

private String getOneThousandAsWords(Integer other) {
if (nothingComesAfter(other)) {
return "mil";
}
if (other == HUNDRED) {
return format(MIL_UN, asWords(other, HAS_NOT_NEXT_VALUE));
}
return format(MIL_UN, asWords(other, HAS_NEXT_VALUE));
}

private String getEndWithOneAsWords(Integer thousands, Integer other) {
Integer units = thousands % 10;
Integer tens = thousands - units;
if (thousands == 21) {
if (nothingComesAfter(other)) {
return format(IUN_MIL, asWords(tens).substring(0, 5));
}
if (other == HUNDRED) {
return format(IUN_MIL_WITH_HUNDRED, asWords(tens, HAS_NOT_NEXT_VALUE).substring(0, 5), asWords(other, HAS_NOT_NEXT_VALUE));
}
return format(IUN_MIL_WITH_HUNDRED, asWords(tens).substring(0, 5), asWords(other, HAS_NEXT_VALUE));

} else {
if (nothingComesAfter(other)) {
return format(Y_UN_MIL, asWords(tens));
}
if (other == HUNDRED) {
return format(Y_UN_MIL_WITH_HUNDRED, asWords(tens, HAS_NOT_NEXT_VALUE), asWords(other, HAS_NOT_NEXT_VALUE));
}
return format(Y_UN_MIL_WITH_HUNDRED, asWords(tens), asWords(other, HAS_NEXT_VALUE));
}
}

private boolean nothingComesAfter(Integer other) {
return other == 0;
}

private boolean isOneThousand(Integer thousands) {
return thousands == 1;
}

private boolean isEndsWithOne(Integer number) {
if (number > 20 && number < 100) {
return (Math.abs(number) % 10) == 1;
}
return false;
}
}
Loading

0 comments on commit 28b4397

Please sign in to comment.