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

Added support for es_MX language #161

Closed
wants to merge 4 commits into from
Closed

Conversation

agb80
Copy link

@agb80 agb80 commented Mar 17, 2018

Changes proposed in this pull request:

  • Added support for es_MX currency (MXN)

Status

  • READY
  • HOLD
  • WIP (Work-In-Progress)

How to verify this change

import num2words
a = 1234.01
print num2words.num2words(a, to='currency', lang='es_MX')

Additional notes

This replace @chavamm work done in #152

@agb80 agb80 force-pushed the es_MX branch 4 times, most recently from a099fed to 7460901 Compare March 18, 2018 00:15
@coveralls
Copy link

Coverage Status

Coverage increased (+0.05%) to 89.679% when pulling e63b238 on agb80:es_MX into 551a980 on savoirfairelinux:master.

1 similar comment
@coveralls
Copy link

coveralls commented Mar 18, 2018

Coverage Status

Coverage increased (+0.05%) to 89.679% when pulling e63b238 on agb80:es_MX into 551a980 on savoirfairelinux:master.

@agb80
Copy link
Author

agb80 commented Mar 18, 2018

@erozqba the is_int_with_cents argument added on the to_currency function from Num2Word_Base class is because in Mexico, and I think in Spanish speaking countries in general, we expected that the integer number 100 means 100 pesos (main currency) not 100 cents (fractional currency).

Without this change on the function to_currency from Num2Word_Base class you were calling function parse_currency_parts with default value for argument is_int_with_cents and forcing consider an integer number as if was entirely expressed on fractional currency.

I don't want to copy/paste to_currency function from base class just to change the default value for is_int_with_cents argument and I think the way I add the named argument using the default value on True does not affect any current implementation.

@erozqba
Copy link
Collaborator

erozqba commented Apr 18, 2018

Hi @agb80!
Thanks a lot for this PR and sorry for the delay in my response.
I think the idea of having a currency param in the to_currency method is to avoid having this es_MX, es_VNZ, and other languages. Using this new implementation we only need to add the new currency form into the CURRENCY_FORM parametre of the Num2Word_ES class.
There is another PR related with this same code, it will be great to have your comments and review #167

@erozqba erozqba mentioned this pull request Apr 18, 2018
3 tasks
@erozqba
Copy link
Collaborator

erozqba commented Apr 19, 2018

@agb80 I would like to suggest some changes and to rebase your branch over master so there are no conflicts.
I have just merged another PR related to ES, and have some ideas, what do you think about the following changes? The main idea is stopping to making derivates of ES, only because the to_currency converter uses a different currency:

diff --git a/num2words/lang_ES.py b/num2words/lang_ES.py
index a123776..9ad7d20 100644
--- a/num2words/lang_ES.py
+++ b/num2words/lang_ES.py
@@ -20,12 +20,45 @@ from __future__ import print_function, unicode_literals
 
 from .lang_EU import Num2Word_EU
 
+GENERIC_DOLLARS = ('dolar', 'dolares')
+GENERIC_PESOS = ('peso', 'pesos')
+GENERIC_CENTS = ('centavo', 'centavos')
+
 
 class Num2Word_ES(Num2Word_EU):
     CURRENCY_FORMS = {
-        'EUR': (('euro', 'euros'), ('centimo', 'centimos')),
-        'ESP': (('peseta', 'pesetas'), ('centimo', 'centimos')),
-        'USD': (('dolar', 'dolares'), ('centavo', 'centavos')),
+        'EUR': (('euro', 'euros'), ('céntimo', 'céntimos')),
+        'USD': (GENERIC_DOLLARS, GENERIC_CENTS),
+        'AUD': (GENERIC_DOLLARS, GENERIC_CENTS),
+        'CAD': (GENERIC_DOLLARS, GENERIC_CENTS),
+        'VEF': (('bolívar fuerte', 'bolívares fuertes'), GENERIC_CENTS),
+        'COP': (GENERIC_PESOS, GENERIC_CENTS),
+        'CUP': (GENERIC_PESOS, GENERIC_CENTS),
+        'CUC': (GENERIC_PESOS, GENERIC_CENTS),
+
+        # repalced by EUR
+        'ESP': (('peseta', 'pesetas'), ('céntimo', 'céntimos')),
+        'EEK': (('corona', 'coronas'), ('céntimo', 'céntimos')),
+        'GBP': (('libra esterlina', 'libras esterlinas'), ('penique', 'peniques')),
+        'LTL': (('lita', 'litas'), GENERIC_CENTS),
+        'LVL': (('lat', 'lats'), ('santims', 'santimu')),
+        'RUB': (('rublo', 'rublos'), ('kopek', 'kopeks')),
+        'SEK': (('corona', 'coronas'), ('öre', 'öre')),
+        'NOK': (('corona', 'coronas'), ('øre', 'øre')),
+        'PLN': (('zloty', 'zlotys'), ('grosz', 'groszy')),
+
+        # replaced by VEF
+        'VEB': (('bolívar', 'bolívares'), GENERIC_CENTS),
+    }
+
+    CURRENCY_ADJECTIVES = {
+        'USD': 'Americano',
+        'AUD': 'Australiano',
+        'CAD': 'Canadiense',
+        'EEK': 'Estonia',
+        'RUB': 'Ruso',
+        'SEK': 'Sueca',
+        'NOK': 'Noruega',
     }
 
     # //CHECK: Is this sufficient??
@@ -43,7 +76,7 @@ class Num2Word_ES(Num2Word_EU):
         self.errmsg_nonnum = "Solo números pueden ser convertidos a palabras."
         self.errmsg_toobig = (
             "Numero muy grande para ser convertido a palabras."
-            )
+        )
         self.gender_stem = "o"
         self.exclude_title = ["y", "menos", "punto"]
         self.mid_numwords = [(1000, "mil"), (100, "cien"), (90, "noventa"),
@@ -119,7 +152,7 @@ class Num2Word_ES(Num2Word_EU):
         else:
             ntext = " " + ntext
 
-        return (ctext + ntext, cnum * nnum)
+        return ctext + ntext, cnum * nnum
 
     def to_ordinal(self, value):
         self.verify_ordinal(value)
@@ -130,21 +163,21 @@ class Num2Word_ES(Num2Word_EU):
                 text = "%s%s" % (self.ords[value], self.gender_stem)
             elif value <= 12:
                 text = (
-                    "%s%s%s" % (self.ords[10], self.gender_stem,
-                                self.to_ordinal(value - 10))
-                        )
+                        "%s%s%s" % (self.ords[10], self.gender_stem,
+                                    self.to_ordinal(value - 10))
+                )
             elif value <= 100:
                 dec = (value // 10) * 10
                 text = (
-                    "%s%s %s" % (self.ords[dec], self.gender_stem,
-                                 self.to_ordinal(value - dec))
-                        )
+                        "%s%s %s" % (self.ords[dec], self.gender_stem,
+                                     self.to_ordinal(value - dec))
+                )
             elif value <= 1e3:
                 cen = (value // 100) * 100
                 text = (
-                    "%s%s %s" % (self.ords[cen], self.gender_stem,
-                                 self.to_ordinal(value - cen))
-                        )
+                        "%s%s %s" % (self.ords[cen], self.gender_stem,
+                                     self.to_ordinal(value - cen))
+                )
             elif value < 1e18:
                 # dec contains the following:
                 # [ 1e3,  1e6): 1e3
@@ -156,11 +189,11 @@ class Num2Word_ES(Num2Word_EU):
                 part = int(float(value / dec) * dec)
                 cardinal = (
                     self.to_cardinal(part / dec) if part / dec != 1 else ""
-                    )
+                )
                 text = (
-                    "%s%s%s %s" % (cardinal, self.ords[dec], self.gender_stem,
-                                   self.to_ordinal(value - part))
-                        )
+                        "%s%s%s %s" % (cardinal, self.ords[dec], self.gender_stem,
+                                       self.to_ordinal(value - part))
+                )
             else:
                 text = self.to_cardinal(value)
         except KeyError:
@@ -176,5 +209,13 @@ class Num2Word_ES(Num2Word_EU):
         result = super(Num2Word_ES, self).to_currency(
             val, currency=currency, cents=cents, seperator=seperator,
             adjective=adjective)
-        # Handle exception, in spanish is "un euro" and not "uno euro"
+
+        # Handle exception for currency genders
+        if self.CURRENCY_FORMS[currency][0][0][-1:] == 'a':
+            # gender feminine, ex: peseta
+            result = result.replace("uno", "una")
+        else:
+            # gender masculine, ex: euro
+            result = result.replace("uno", "un")
+
         return result.replace("uno", "un")
diff --git a/tests/test_es.py b/tests/test_es.py
index d905bac..ec7ba3e 100644
--- a/tests/test_es.py
+++ b/tests/test_es.py
@@ -110,25 +110,25 @@ TEST_CASES_ORDINAL_NUM = (
 )
 
 TEST_CASES_TO_CURRENCY = (
-    (1.00, 'un euro con cero centimos'),
-    (2.00, 'dos euros con cero centimos'),
-    (8.00, 'ocho euros con cero centimos'),
-    (12.00, 'doce euros con cero centimos'),
-    (21.00, 'veintiun euros con cero centimos'),
-    (81.25, 'ochenta y un euros con veinticinco centimos'),
-    (350.90, 'trescientos cincuenta euros con noventa centimos'),
-    (100.00, 'cien euros con cero centimos'),
+    (1.00, 'un euro con cero céntimos'),
+    (2.00, 'dos euros con cero céntimos'),
+    (8.00, 'ocho euros con cero céntimos'),
+    (12.00, 'doce euros con cero céntimos'),
+    (21.00, 'veintiun euros con cero céntimos'),
+    (81.25, 'ochenta y un euros con veinticinco céntimos'),
+    (350.90, 'trescientos cincuenta euros con noventa céntimos'),
+    (100.00, 'cien euros con cero céntimos'),
 )
 
 TEST_CASES_TO_CURRENCY_ESP = (
-    (1.00, 'un peseta con cero centimos'),
-    (2.00, 'dos pesetas con cero centimos'),
-    (8.00, 'ocho pesetas con cero centimos'),
-    (12.00, 'doce pesetas con cero centimos'),
-    (21.00, 'veintiun pesetas con cero centimos'),
-    (81.25, 'ochenta y un pesetas con veinticinco centimos'),
-    (350.90, 'trescientos cincuenta pesetas con noventa centimos'),
-    (100.00, 'cien pesetas con cero centimos'),
+    (1.00, 'una peseta con cero céntimos'),
+    (2.00, 'dos pesetas con cero céntimos'),
+    (8.00, 'ocho pesetas con cero céntimos'),
+    (12.00, 'doce pesetas con cero céntimos'),
+    (21.00, 'veintiuna pesetas con cero céntimos'),
+    (81.25, 'ochenta y una pesetas con veinticinco céntimos'),
+    (350.90, 'trescientos cincuenta pesetas con noventa céntimos'),
+    (100.00, 'cien pesetas con cero céntimos'),
 )
 
 TEST_CASES_TO_CURRENCY_USD = (
@@ -142,6 +142,86 @@ TEST_CASES_TO_CURRENCY_USD = (
     (100.00, 'cien dolares con cero centavos'),
 )
 
+TEST_CASES_TO_CURRENCY_CAD = (
+    (1.00, 'un dolar con cero centavos'),
+    (2.00, 'dos dolares con cero centavos'),
+    (8.00, 'ocho dolares con cero centavos'),
+    (12.00, 'doce dolares con cero centavos'),
+    (21.00, 'veintiun dolares con cero centavos'),
+    (81.25, 'ochenta y un dolares con veinticinco centavos'),
+    (350.90, 'trescientos cincuenta dolares con noventa centavos'),
+    (100.00, 'cien dolares con cero centavos'),
+)
+
+TEST_CASES_TO_CURRENCY_AUD = (
+    (1.00, 'un dolar con cero centavos'),
+    (2.00, 'dos dolares con cero centavos'),
+    (8.00, 'ocho dolares con cero centavos'),
+    (12.00, 'doce dolares con cero centavos'),
+    (21.00, 'veintiun dolares con cero centavos'),
+    (81.25, 'ochenta y un dolares con veinticinco centavos'),
+    (350.90, 'trescientos cincuenta dolares con noventa centavos'),
+    (100.00, 'cien dolares con cero centavos'),
+)
+
+TEST_CASES_TO_CURRENCY_VEB = (
+    (1.00, 'un bolívar con cero centavos'),
+    (2.00, 'dos bolívares con cero centavos'),
+    (8.00, 'ocho bolívares con cero centavos'),
+    (12.00, 'doce bolívares con cero centavos'),
+    (21.00, 'veintiun bolívares con cero centavos'),
+    (81.25, 'ochenta y un bolívares con veinticinco centavos'),
+    (350.90, 'trescientos cincuenta bolívares con noventa centavos'),
+    (100.00, 'cien bolívares con cero centavos'),
+)
+
+TEST_CASES_TO_CURRENCY_VEF = (
+    (1.00, 'un bolívar fuerte con cero centavos'),
+    (2.00, 'dos bolívares fuertes con cero centavos'),
+    (8.00, 'ocho bolívares fuertes con cero centavos'),
+    (12.00, 'doce bolívares fuertes con cero centavos'),
+    (21.00, 'veintiun bolívares fuertes con cero centavos'),
+    (81.25, 'ochenta y un bolívares fuertes con veinticinco centavos'),
+    (350.90, 'trescientos cincuenta bolívares fuertes con noventa centavos'),
+    (100.00, 'cien bolívares fuertes con cero centavos'),
+)
+
+TEST_CASES_TO_CURRENCY_COP = (
+    (1.00, 'un peso con cero centavos'),
+    (2.00, 'dos pesos con cero centavos'),
+    (8.00, 'ocho pesos con cero centavos'),
+    (12.00, 'doce pesos con cero centavos'),
+    (21.00, 'veintiun pesos con cero centavos'),
+    (81.25, 'ochenta y un pesos con veinticinco centavos'),
+    (350.90, 'trescientos cincuenta pesos con noventa centavos'),
+    (100.00, 'cien pesos con cero centavos'),
+)
+
+TEST_CASES_TO_CURRENCY_CUP = TEST_CASES_TO_CURRENCY_COP
+TEST_CASES_TO_CURRENCY_CUC = TEST_CASES_TO_CURRENCY_COP
+
+TEST_CASES_TO_CURRENCY_EEK = (
+    (1.00, 'una corona con cero céntimos'),
+    (2.00, 'dos coronas con cero céntimos'),
+    (8.00, 'ocho coronas con cero céntimos'),
+    (12.00, 'doce coronas con cero céntimos'),
+    (21.00, 'veintiuna coronas con cero céntimos'),
+    (81.25, 'ochenta y una coronas con veinticinco céntimos'),
+    (350.90, 'trescientos cincuenta coronas con noventa céntimos'),
+    (100.00, 'cien coronas con cero céntimos'),
+)
+
+TEST_CASES_TO_CURRENCY_GBP = (
+    (1.00, 'una libra esterlina con cero peniques'),
+    (2.00, 'dos libras esterlinas con cero peniques'),
+    (8.00, 'ocho libras esterlinas con cero peniques'),
+    (12.00, 'doce libras esterlinas con cero peniques'),
+    (21.00, 'veintiuna libras esterlinas con cero peniques'),
+    (81.25, 'ochenta y una libras esterlinas con veinticinco peniques'),
+    (350.90, 'trescientos cincuenta libras esterlinas con noventa peniques'),
+    (100.00, 'cien libras esterlinas con cero peniques'),
+)
+
 
 class Num2WordsESTest(TestCase):
 
@@ -183,3 +263,66 @@ class Num2WordsESTest(TestCase):
                 num2words(test[0], lang='es', to='currency', currency='USD'),
                 test[1]
             )
+
+    def test_currency_cad(self):
+        for test in TEST_CASES_TO_CURRENCY_CAD:
+            self.assertEqual(
+                num2words(test[0], lang='es', to='currency', currency='CAD'),
+                test[1]
+            )
+
+    def test_currency_aud(self):
+        for test in TEST_CASES_TO_CURRENCY_AUD:
+            self.assertEqual(
+                num2words(test[0], lang='es', to='currency', currency='AUD'),
+                test[1]
+            )
+
+    def test_currency_veb(self):
+        for test in TEST_CASES_TO_CURRENCY_VEB:
+            self.assertEqual(
+                num2words(test[0], lang='es', to='currency', currency='VEB'),
+                test[1]
+            )
+
+    def test_currency_vef(self):
+        for test in TEST_CASES_TO_CURRENCY_VEF:
+            self.assertEqual(
+                num2words(test[0], lang='es', to='currency', currency='VEF'),
+                test[1]
+            )
+
+    def test_currency_cop(self):
+        for test in TEST_CASES_TO_CURRENCY_COP:
+            self.assertEqual(
+                num2words(test[0], lang='es', to='currency', currency='COP'),
+                test[1]
+            )
+
+    def test_currency_cup(self):
+        for test in TEST_CASES_TO_CURRENCY_COP:
+            self.assertEqual(
+                num2words(test[0], lang='es', to='currency', currency='CUP'),
+                test[1]
+            )
+
+    def test_currency_eek(self):
+        for test in TEST_CASES_TO_CURRENCY_EEK:
+            self.assertEqual(
+                num2words(test[0], lang='es', to='currency', currency='EEK'),
+                test[1]
+            )
+
+    def test_currency_GBP(self):
+        for test in TEST_CASES_TO_CURRENCY_GBP:
+            self.assertEqual(
+                num2words(test[0], lang='es', to='currency', currency='GBP'),
+                test[1]
+            )
+
+    def test_currency_cuc(self):
+        for test in TEST_CASES_TO_CURRENCY_COP:
+            self.assertEqual(
+                num2words(test[0], lang='es', to='currency', currency='CUC'),
+                test[1]
+            )

If you are OK with this, could you please add some other tests using the Adjetives too?

Thanks a lot in advance!

@ventilooo ventilooo added this to In progress in Release Jun 15, 2018
@ventilooo ventilooo requested a review from erozqba July 25, 2018 14:35
@ventilooo
Copy link
Contributor

@erozqba Is that PR still valuable ?

@erozqba
Copy link
Collaborator

erozqba commented Jul 27, 2018

@ventilooo I will finish this myself because @agb80 never give us a feedback about the suggested changes.

@erozqba erozqba self-assigned this Oct 13, 2018
@sarahberanek
Copy link
Contributor

@erozqba the is_int_with_cents argument added on the to_currency function from Num2Word_Base class is because in Mexico, and I think in Spanish speaking countries in general, we expected that the integer number 100 means 100 pesos (main currency) not 100 cents (fractional currency).

This is just perfect! I think the ordering is also true for almost all European languages! I was about to do the change myself, but if this is merged anyways - the changes can just be propagated to the other languages by setting is_int_with_cents to False there as well!

@mromdhane mromdhane self-requested a review January 22, 2021 14:54
@erozqba erozqba closed this Jan 24, 2021
Release automation moved this from In progress to Done Jan 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Release
  
Done
Development

Successfully merging this pull request may close these issues.

None yet

6 participants