Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add spanish support #111

Merged
merged 8 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
jglaszka marked this conversation as resolved.
Show resolved Hide resolved
* 🇸🇪 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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ru sure you need this class? you're not implementing longConverter within this PR, aren't you?


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