diff --git a/AUTHORS b/AUTHORS
index 825ba8e658d..363c7df100a 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -257,6 +257,7 @@ answer newbie questions, and generally made Django that much better:
Brian Ray
remco@diji.biz
rhettg@gmail.com
+ ricardojbarrios@gmail.com
Matt Riggott
Henrique Romano
Armin Ronacher
diff --git a/django/contrib/localflavor/es/__init__.py b/django/contrib/localflavor/es/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/django/contrib/localflavor/es/es_provinces.py b/django/contrib/localflavor/es/es_provinces.py
new file mode 100644
index 00000000000..9f5e12680b8
--- /dev/null
+++ b/django/contrib/localflavor/es/es_provinces.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+from django.utils.translation import ugettext_lazy as _
+
+PROVINCE_CHOICES = (
+ ('01', _('Arava')),
+ ('02', _('Albacete')),
+ ('03', _('Alacant')),
+ ('04', _('Almeria')),
+ ('05', _('Avila')),
+ ('06', _('Badajoz')),
+ ('07', _('Illes Balears')),
+ ('08', _('Barcelona')),
+ ('09', _('Burgos')),
+ ('10', _('Caceres')),
+ ('11', _('Cadiz')),
+ ('12', _('Castello')),
+ ('13', _('Ciudad Real')),
+ ('14', _('Cordoba')),
+ ('15', _('A Coruna')),
+ ('16', _('Cuenca')),
+ ('17', _('Girona')),
+ ('18', _('Granada')),
+ ('19', _('Guadalajara')),
+ ('20', _('Guipuzkoa')),
+ ('21', _('Huelva')),
+ ('22', _('Huesca')),
+ ('23', _('Jaen')),
+ ('24', _('Leon')),
+ ('25', _('Lleida')),
+ ('26', _('La Rioja')),
+ ('27', _('Lugo')),
+ ('28', _('Madrid')),
+ ('29', _('Malaga')),
+ ('30', _('Murcia')),
+ ('31', _('Navarre')),
+ ('32', _('Ourense')),
+ ('33', _('Asturias')),
+ ('34', _('Palencia')),
+ ('35', _('Las Palmas')),
+ ('36', _('Pontevedra')),
+ ('37', _('Salamanca')),
+ ('38', _('Santa Cruz de Tenerife')),
+ ('39', _('Cantabria')),
+ ('40', _('Segovia')),
+ ('41', _('Seville')),
+ ('42', _('Soria')),
+ ('43', _('Tarragona')),
+ ('44', _('Teruel')),
+ ('45', _('Toledo')),
+ ('46', _('Valencia')),
+ ('47', _('Valladolid')),
+ ('48', _('Bizkaia')),
+ ('49', _('Zamora')),
+ ('50', _('Zaragoza')),
+ ('51', _('Ceuta')),
+ ('52', _('Melilla')),
+)
+
diff --git a/django/contrib/localflavor/es/es_regions.py b/django/contrib/localflavor/es/es_regions.py
new file mode 100644
index 00000000000..3c1ea0e9744
--- /dev/null
+++ b/django/contrib/localflavor/es/es_regions.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+from django.utils.translation import ugettext_lazy as _
+
+REGION_CHOICES = (
+ ('AN', _('Andalusia')),
+ ('AR', _('Aragon')),
+ ('O', _('Principality of Asturias')),
+ ('IB', _('Balearic Islands')),
+ ('PV', _('Basque Country')),
+ ('CN', _('Canary Islands')),
+ ('S', _('Cantabria')),
+ ('CM', _('Castile-La Mancha')),
+ ('CL', _('Castile and Leon')),
+ ('CT', _('Catalonia')),
+ ('EX', _('Extremadura')),
+ ('GA', _('Galicia')),
+ ('LO', _('La Rioja')),
+ ('M', _('Madrid')),
+ ('MU', _('Region of Murcia')),
+ ('NA', _('Foral Community of Navarre')),
+ ('VC', _('Valencian Community')),
+)
+
diff --git a/django/contrib/localflavor/es/forms.py b/django/contrib/localflavor/es/forms.py
new file mode 100644
index 00000000000..29b41828f6f
--- /dev/null
+++ b/django/contrib/localflavor/es/forms.py
@@ -0,0 +1,173 @@
+# -*- coding: utf-8 -*-
+"""
+Spanish-specific Form helpers
+"""
+
+from django.newforms import ValidationError
+from django.newforms.fields import RegexField, Select, EMPTY_VALUES
+from django.utils.translation import ugettext as _
+import re
+
+class ESPostalCodeField(RegexField):
+ """
+ A form field that validates its input as a spanish postal code.
+
+ Spanish postal code is a five digits string, with two first digits
+ between 01 and 52, assigned to provinces code.
+ """
+ def __init__(self, *args, **kwargs):
+ super(ESPostalCodeField, self).__init__(
+ r'^(0[1-9]|[1-4][0-9]|5[0-2])\d{3}$',
+ max_length=None, min_length=None,
+ error_message=_('Enter a valid postal code in the range and format 01XXX - 52XXX.'),
+ *args, **kwargs)
+
+class ESPhoneNumberField(RegexField):
+ """
+ A form field that validates its input as a Spanish phone number.
+ Information numbers are ommited.
+
+ Spanish phone numbers are nine digit numbers, where first digit is 6 (for
+ cell phones), 8 (for special phones), or 9 (for landlines and special
+ phones)
+
+ TODO: accept and strip characters like dot, hyphen... in phone number
+ """
+ def __init__(self, *args, **kwargs):
+ super(ESPhoneNumberField, self).__init__(r'^(6|8|9)\d{8}$',
+ max_length=None, min_length=None,
+ error_message=_('Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.'),
+ *args, **kwargs)
+
+class ESIdentityCardNumberField(RegexField):
+ """
+ Spanish NIF/NIE/CIF (Fiscal Identification Number) code.
+
+ Validates three diferent formats:
+
+ NIF (individuals): 12345678A
+ CIF (companies): A12345678
+ NIE (foreigners): X12345678A
+
+ according to a couple of simple checksum algorithms.
+
+ Value can include a space or hyphen separator between number and letters.
+ Number length is not checked for NIF (or NIE), old values start with a 1,
+ and future values can contain digits greater than 8. The CIF control digit
+ can be a number or a letter depending on company type. Algorithm is not
+ public, and different authors have different opinions on which ones allows
+ letters, so both validations are assumed true for all types.
+ """
+ def __init__(self, only_nif=False, *args, **kwargs):
+ self.only_nif = only_nif
+ self.nif_control = 'TRWAGMYFPDXBNJZSQVHLCKE'
+ self.cif_control = 'JABCDEFGHI'
+ self.cif_types = 'ABCDEFGHKLMNPQS'
+ self.nie_types = 'XT'
+ if self.only_nif:
+ self.id_types = 'NIF or NIE'
+ else:
+ self.id_types = 'NIF, NIE, or CIF'
+ super(ESIdentityCardNumberField, self).__init__(r'^([%s]?)[ -]?(\d+)[ -]?([%s]?)$' % (self.cif_types + self.nie_types + self.cif_types.lower() + self.nie_types.lower(), self.nif_control + self.nif_control.lower()),
+ max_length=None, min_length=None,
+ error_message=_('Please enter a valid %s.' % self.id_types),
+ *args, **kwargs)
+
+ def clean(self, value):
+ super(ESIdentityCardNumberField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+ nif_get_checksum = lambda d: self.nif_control[int(d)%23]
+
+ value = value.upper().replace(' ', '').replace('-', '')
+ m = re.match(r'^([%s]?)[ -]?(\d+)[ -]?([%s]?)$' % (self.cif_types + self.nie_types, self.nif_control), value)
+ letter1, number, letter2 = m.groups()
+
+ if not letter1 and letter2:
+ # NIF
+ if letter2 == nif_get_checksum(number):
+ return value
+ else:
+ raise ValidationError, _('Invalid checksum for NIF.')
+ elif letter1 in self.nie_types and letter2:
+ # NIE
+ if letter2 == nif_get_checksum(number):
+ return value
+ else:
+ raise ValidationError, _('Invalid checksum for NIE.')
+ elif not self.only_nif and letter1 in self.cif_types and len(number) in [7, 8]:
+ # CIF
+ if not letter2:
+ number, letter2 = number[:-1], int(number[-1])
+ checksum = cif_get_checksum(number)
+ if letter2 in [checksum, self.cif_control[checksum]]:
+ return value
+ else:
+ raise ValidationError, _('Invalid checksum for CIF.')
+ else:
+ raise ValidationError, _('Please enter a valid %s.' % self.id_types)
+
+class ESCCCField(RegexField):
+ """
+ A form field that validates its input as a Spanish bank account or CCC
+ (Codigo Cuenta Cliente).
+
+ Spanish CCC is in format EEEE-OOOO-CC-AAAAAAAAAA where:
+
+ E = entity
+ O = office
+ C = checksum
+ A = account
+
+ It's also valid to use a space as delimiter, or to use no delimiter.
+
+ First checksum digit validates entity and office, and last one
+ validates account. Validation is done multiplying every digit of 10
+ digit value (with leading 0 if necessary) by number in its position in
+ string 1, 2, 4, 8, 5, 10, 9, 7, 3, 6. Sum resulting numbers and extract
+ it from 11. Result is checksum except when 10 then is 1, or when 11
+ then is 0.
+
+ TODO: allow IBAN validation too
+ """
+ def __init__(self, *args, **kwargs):
+ super(ESCCCField, self).__init__(r'^\d{4}[ -]?\d{4}[ -]?\d{2}[ -]?\d{10}$',
+ max_length=None, min_length=None,
+ error_message=_('Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.'),
+ *args, **kwargs)
+
+ def clean(self, value):
+ super(ESCCCField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+ control_str = [1, 2, 4, 8, 5, 10, 9, 7, 3, 6]
+ m = re.match(r'^(\d{4})[ -]?(\d{4})[ -]?(\d{2})[ -]?(\d{10})$', value)
+ entity, office, checksum, account = m.groups()
+ get_checksum = lambda d: str(11 - sum([int(digit) * int(control) for digit, control in zip(d, control_str)]) % 11).replace('10', '1').replace('11', '0')
+ if get_checksum('00' + entity + office) + get_checksum(account) == checksum:
+ return value
+ else:
+ raise ValidationError, _('Invalid checksum for bank account number.')
+
+class ESRegionSelect(Select):
+ """
+ A Select widget that uses a list of spanish regions as its choices.
+ """
+ def __init__(self, attrs=None):
+ from es_regions import REGION_CHOICES
+ super(ESRegionSelect, self).__init__(attrs, choices=REGION_CHOICES)
+
+class ESProvinceSelect(Select):
+ """
+ A Select widget that uses a list of spanish provinces as its choices.
+ """
+ def __init__(self, attrs=None):
+ from es_provinces import PROVINCE_CHOICES
+ super(ESProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
+
+
+def cif_get_checksum(number):
+ s1 = sum([int(digit) for pos, digit in enumerate(number) if int(pos) % 2])
+ s2 = sum([sum([int(unit) for unit in str(int(digit) * 2)]) for pos, digit in enumerate(number) if not int(pos) % 2])
+ return 10 - ((s1 + s2) % 10)
+
diff --git a/tests/regressiontests/forms/localflavor/es.py b/tests/regressiontests/forms/localflavor/es.py
new file mode 100644
index 00000000000..f149aa9cbe9
--- /dev/null
+++ b/tests/regressiontests/forms/localflavor/es.py
@@ -0,0 +1,343 @@
+# -*- coding: utf-8 -*-
+# Tests for the contrib/localflavor/ ES form fields.
+
+tests = r"""
+# ESPostalCodeField ##############################################################
+
+ESPostalCodeField validates that data is a five-digit spanish postal code.
+>>> from django.contrib.localflavor.es.forms import ESPostalCodeField
+>>> f = ESPostalCodeField()
+>>> f.clean('08028')
+u'08028'
+>>> f.clean('28080')
+u'28080'
+>>> f.clean('53001')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('0801')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('080001')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('00999')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('08 01')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('08A01')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f = ESPostalCodeField(required=False)
+>>> f.clean('08028')
+u'08028'
+>>> f.clean('28080')
+u'28080'
+>>> f.clean('53001')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('0801')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('080001')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('00999')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('08 01')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('08A01')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
+>>> f.clean('')
+u''
+
+# ESPhoneNumberField ##############################################################
+
+ESPhoneNumberField validates that data is a nine-digit spanish phone number.
+>>> from django.contrib.localflavor.es.forms import ESPhoneNumberField
+>>> f = ESPhoneNumberField()
+>>> f.clean('650010101')
+u'650010101'
+>>> f.clean('931234567')
+u'931234567'
+>>> f.clean('800123123')
+u'800123123'
+>>> f.clean('555555555')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
+>>> f.clean('789789789')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
+>>> f.clean('99123123')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
+>>> f.clean('9999123123')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f = ESPhoneNumberField(required=False)
+>>> f.clean('650010101')
+u'650010101'
+>>> f.clean('931234567')
+u'931234567'
+>>> f.clean('800123123')
+u'800123123'
+>>> f.clean('555555555')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
+>>> f.clean('789789789')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
+>>> f.clean('99123123')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
+>>> f.clean('9999123123')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
+>>> f.clean('')
+u''
+
+# ESIdentityCardNumberField ##############################################################
+
+ESIdentityCardNumberField validates that data is a identification spanish code for companies or individuals (CIF, NIF or NIE).
+>>> from django.contrib.localflavor.es.forms import ESIdentityCardNumberField
+>>> f = ESIdentityCardNumberField()
+>>> f.clean('78699688J')
+'78699688J'
+>>> f.clean('78699688-J')
+'78699688J'
+>>> f.clean('78699688 J')
+'78699688J'
+>>> f.clean('78699688 j')
+'78699688J'
+>>> f.clean('78699688T')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for NIF.']
+>>> f.clean('X0901797J')
+'X0901797J'
+>>> f.clean('X-6124387-Q')
+'X6124387Q'
+>>> f.clean('X 0012953 G')
+'X0012953G'
+>>> f.clean('x-3287690-r')
+'X3287690R'
+>>> f.clean('t-03287690r')
+'T03287690R'
+>>> f.clean('X-03287690')
+Traceback (most recent call last):
+...
+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
+>>> f.clean('X-03287690-T')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for NIE.']
+>>> f.clean('B38790911')
+'B38790911'
+>>> f.clean('B-3879091A')
+'B3879091A'
+>>> f.clean('B 38790917')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for CIF.']
+>>> f.clean('B 38790911')
+'B38790911'
+>>> f.clean('P-3900800-H')
+'P3900800H'
+>>> f.clean('P 39008008')
+'P39008008'
+>>> f.clean('C-28795565')
+'C28795565'
+>>> f.clean('C 2879556E')
+'C2879556E'
+>>> f.clean('C28795567')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for CIF.']
+>>> f.clean('I38790911')
+Traceback (most recent call last):
+...
+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
+>>> f.clean('78699688-2')
+Traceback (most recent call last):
+...
+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f = ESIdentityCardNumberField(required=False)
+>>> f.clean('78699688J')
+'78699688J'
+>>> f.clean('78699688-J')
+'78699688J'
+>>> f.clean('78699688 J')
+'78699688J'
+>>> f.clean('78699688 j')
+'78699688J'
+>>> f.clean('78699688T')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for NIF.']
+>>> f.clean('X0901797J')
+'X0901797J'
+>>> f.clean('X-6124387-Q')
+'X6124387Q'
+>>> f.clean('X 0012953 G')
+'X0012953G'
+>>> f.clean('x-3287690-r')
+'X3287690R'
+>>> f.clean('t-03287690r')
+'T03287690R'
+>>> f.clean('X-03287690')
+Traceback (most recent call last):
+...
+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
+>>> f.clean('X-03287690-T')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for NIE.']
+>>> f.clean('B38790911')
+'B38790911'
+>>> f.clean('B-3879091A')
+'B3879091A'
+>>> f.clean('B 38790917')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for CIF.']
+>>> f.clean('B 38790911')
+'B38790911'
+>>> f.clean('P-3900800-H')
+'P3900800H'
+>>> f.clean('P 39008008')
+'P39008008'
+>>> f.clean('C-28795565')
+'C28795565'
+>>> f.clean('C 2879556E')
+'C2879556E'
+>>> f.clean('C28795567')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for CIF.']
+>>> f.clean('I38790911')
+Traceback (most recent call last):
+...
+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
+>>> f.clean('78699688-2')
+Traceback (most recent call last):
+...
+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
+>>> f.clean('')
+u''
+
+# ESCCCField ##############################################################
+
+ESCCCField validates that data is a spanish bank account number (codigo cuenta cliente).
+
+>>> from django.contrib.localflavor.es.forms import ESCCCField
+>>> f = ESCCCField()
+>>> f.clean('20770338793100254321')
+'20770338793100254321'
+>>> f.clean('2077 0338 79 3100254321')
+'2077 0338 79 3100254321'
+>>> f.clean('2077-0338-79-3100254321')
+'2077-0338-79-3100254321'
+>>> f.clean('2077.0338.79.3100254321')
+Traceback (most recent call last):
+...
+ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
+>>> f.clean('2077-0338-78-3100254321')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for bank account number.']
+>>> f.clean('2077-0338-89-3100254321')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for bank account number.']
+>>> f.clean('2077-03-3879-3100254321')
+Traceback (most recent call last):
+...
+ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f = ESCCCField(required=False)
+>>> f.clean('20770338793100254321')
+'20770338793100254321'
+>>> f.clean('2077 0338 79 3100254321')
+'2077 0338 79 3100254321'
+>>> f.clean('2077-0338-79-3100254321')
+'2077-0338-79-3100254321'
+>>> f.clean('2077.0338.79.3100254321')
+Traceback (most recent call last):
+...
+ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
+>>> f.clean('2077-0338-78-3100254321')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for bank account number.']
+>>> f.clean('2077-0338-89-3100254321')
+Traceback (most recent call last):
+...
+ValidationError: [u'Invalid checksum for bank account number.']
+>>> f.clean('2077-03-3879-3100254321')
+Traceback (most recent call last):
+...
+ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
+>>> f.clean('')
+u''
+
+# ESRegionSelect ##############################################################
+
+ESRegionSelect is a Select widget that uses a list of Spain regions as its choices.
+>>> from django.contrib.localflavor.es.forms import ESRegionSelect
+>>> w = ESRegionSelect()
+>>> w.render('regions', 'CT')
+u''
+
+# ESProvincenSelect ##############################################################
+
+ESProvinceSelect is a Select widget that uses a list of Spain provinces as its choices.
+>>> from django.contrib.localflavor.es.forms import ESProvinceSelect
+>>> w = ESProvinceSelect()
+>>> w.render('provinces', '08')
+u''
+
+"""
+
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index a732f9fdb0a..aa33386d095 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -9,6 +9,7 @@ from localflavor.ca import tests as localflavor_ca_tests
from localflavor.ch import tests as localflavor_ch_tests
from localflavor.cl import tests as localflavor_cl_tests
from localflavor.de import tests as localflavor_de_tests
+from localflavor.es import tests as localflavor_es_tests
from localflavor.fi import tests as localflavor_fi_tests
from localflavor.fr import tests as localflavor_fr_tests
from localflavor.generic import tests as localflavor_generic_tests
@@ -35,6 +36,7 @@ __test__ = {
'localflavor_ch_tests': localflavor_ch_tests,
'localflavor_cl_tests': localflavor_cl_tests,
'localflavor_de_tests': localflavor_de_tests,
+ 'localflavor_es_tests': localflavor_es_tests,
'localflavor_fi_tests': localflavor_fi_tests,
'localflavor_fr_tests': localflavor_fr_tests,
'localflavor_generic_tests': localflavor_generic_tests,