Code

Ticket #4036: 4036_3.diff

File 4036_3.diff, 23.8 KB (added by garcia_marc, 7 years ago)

spanish localflavor files

Line 
1Index: django/contrib/localflavor/es/es_regions.py
2===================================================================
3--- django/contrib/localflavor/es/es_regions.py (revision 0)
4+++ django/contrib/localflavor/es/es_regions.py (revision 0)
5@@ -0,0 +1,23 @@
6+# -*- coding: utf-8 -*-
7+from django.utils.translation import ugettext_lazy as _
8+
9+REGION_CHOICES = (
10+       ('AN', _('Andalusia')),
11+       ('AR', _('Aragon')),
12+       ('O', _('Principality of Asturias')),
13+       ('IB', _('Balearic Islands')),
14+       ('PV', _('Basque Country')),
15+       ('CN', _('Canary Islands')),
16+       ('S', _('Cantabria')),
17+       ('CM', _('Castile-La Mancha')),
18+       ('CL', _('Castile and Leon')),
19+       ('CT', _('Catalonia')),
20+       ('EX', _('Extremadura')),
21+       ('GA', _('Galicia')),
22+       ('LO', _('La Rioja')),
23+       ('M', _('Madrid')),
24+       ('MU', _('Region of Murcia')),
25+       ('NA', _('Foral Community of Navarre')),
26+       ('VC', _('Valencian Community')),
27+)
28+
29Index: django/contrib/localflavor/es/es_provinces.py
30===================================================================
31--- django/contrib/localflavor/es/es_provinces.py       (revision 0)
32+++ django/contrib/localflavor/es/es_provinces.py       (revision 0)
33@@ -0,0 +1,58 @@
34+# -*- coding: utf-8 -*-
35+from django.utils.translation import ugettext_lazy as _
36+
37+PROVINCE_CHOICES = (
38+       ('01', _('Arava')),
39+       ('02', _('Albacete')),
40+       ('03', _('Alacant')),
41+       ('04', _('Almeria')),
42+       ('05', _('Avila')),
43+       ('06', _('Badajoz')),
44+       ('07', _('Illes Balears')),
45+       ('08', _('Barcelona')),
46+       ('09', _('Burgos')),
47+       ('10', _('Caceres')),
48+       ('11', _('Cadiz')),
49+       ('12', _('Castello')),
50+       ('13', _('Ciudad Real')),
51+       ('14', _('Cordoba')),
52+       ('15', _('A Coruna')),
53+       ('16', _('Cuenca')),
54+       ('17', _('Girona')),
55+       ('18', _('Granada')),
56+       ('19', _('Guadalajara')),
57+       ('20', _('Guipuzkoa')),
58+       ('21', _('Huelva')),
59+       ('22', _('Huesca')),
60+       ('23', _('Jaen')),
61+       ('24', _('Leon')),
62+       ('25', _('Lleida')),
63+       ('26', _('La Rioja')),
64+       ('27', _('Lugo')),
65+       ('28', _('Madrid')),
66+       ('29', _('Malaga')),
67+       ('30', _('Murcia')),
68+       ('31', _('Navarre')),
69+       ('32', _('Ourense')),
70+       ('33', _('Asturias')),
71+       ('34', _('Palencia')),
72+       ('35', _('Las Palmas')),
73+       ('36', _('Pontevedra')),
74+       ('37', _('Salamanca')),
75+       ('38', _('Santa Cruz de Tenerife')),
76+       ('39', _('Cantabria')),
77+       ('40', _('Segovia')),
78+       ('41', _('Seville')),
79+       ('42', _('Soria')),
80+       ('43', _('Tarragona')),
81+       ('44', _('Teruel')),
82+       ('45', _('Toledo')),
83+       ('46', _('Valencia')),
84+       ('47', _('Valladolid')),
85+       ('48', _('Bizkaia')),
86+       ('49', _('Zamora')),
87+       ('50', _('Zaragoza')),
88+       ('51', _('Ceuta')),
89+       ('52', _('Melilla')),
90+)
91+
92Index: django/contrib/localflavor/es/__init__.py
93===================================================================
94Index: django/contrib/localflavor/es/forms.py
95===================================================================
96--- django/contrib/localflavor/es/forms.py      (revision 0)
97+++ django/contrib/localflavor/es/forms.py      (revision 0)
98@@ -0,0 +1,154 @@
99+# -*- coding: utf-8 -*-
100+"""
101+Spanish-specific Form helpers
102+"""
103+
104+from django.newforms import ValidationError
105+from django.newforms.fields import RegexField, Select, EMPTY_VALUES
106+from django.utils.translation import ugettext as _
107+import re
108+
109+class ESPostalCodeField(RegexField):
110+    """
111+    A form field that validates its input as a spanish postal code.
112+   
113+    Spanish postal code is a five digits string, with two first digits
114+    between 01 and 52, assigned to provinces code.
115+    """
116+    def __init__(self, *args, **kwargs):
117+        super(ESPostalCodeField, self).__init__(r'^(0[1-9]|[1-4][0-9]|5[0-2])\d{3}$',
118+            max_length=None, min_length=None,
119+            error_message=_('Enter a valid postal code in the range and format 01XXX - 52XXX.'),
120+            *args, **kwargs)
121+
122+class ESPhoneNumberField(RegexField):
123+       """
124+       A form field that validates its input as a spanish Phone Number. Information numbers are ommited.
125+
126+       Spanish phone numbers are 9 digit numbers, where first digit is 6 (for cell phones), 8 (for special
127+       phones), or 9 (for landlines and special phones)
128+
129+       TODO: accept and strip characters like dot, hyphen... in phone number
130+       """
131+       def __init__(self, *args, **kwargs):
132+               super(ESPhoneNumberField, self).__init__(r'^(6|8|9)\d{8}$',
133+                               max_length=None, min_length=None,
134+                               error_message=_('Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.'),
135+                               *args, **kwargs)
136+
137+class ESIdentityCardNumberField(RegexField):
138+       """
139+       Spanish NIF/NIE/CIF (Fiscal Identification Number) code.
140+
141+       Validates three diferent formats,
142+               NIF (individuals): 12345678A
143+               CIF (companies): A12345678
144+               NIE (foreigners): X12345678A
145+       according to a couple of simple checksum algorithms.
146+       Value can include a space or hyphen separator between number and letters.
147+       Number length is not checked for nif (or nie), old values started by 1, and future values can be more than 8.
148+       CIF control digit can be a number or a letter depending on company type. Algorithm is not public, and different
149+       authors have different opinions on which ones allows letters, so both validations are assumed true for all types.
150+       """
151+       def __init__(self, only_nif=False, *args, **kwargs):
152+               self.only_nif = only_nif
153+               self.nif_control = 'TRWAGMYFPDXBNJZSQVHLCKE'
154+               self.cif_control = 'JABCDEFGHI'
155+               self.cif_types = 'ABCDEFGHKLMNPQS'
156+               self.nie_types = 'XT'
157+               if self.only_nif:
158+                       self.id_types = 'NIF or NIE'
159+               else:
160+                       self.id_types = 'NIF, NIE, or CIF'
161+               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()),
162+                               max_length=None, min_length=None,
163+                               error_message=_('Please enter a valid %s.' % self.id_types),
164+                               *args, **kwargs)
165+
166+       def clean(self, value):
167+               super(ESIdentityCardNumberField, self).clean(value)       
168+               if value in EMPTY_VALUES:
169+                       return u''
170+               nif_get_checksum = lambda d: self.nif_control[int(d)%23]
171+               def cif_get_checksum(number):
172+                       s1 = sum([int(digit) for pos, digit in enumerate(number) if int(pos) % 2])
173+                       s2 = sum([sum([int(unit) for unit in str(int(digit) * 2)]) for pos, digit in enumerate(number) if not int(pos) % 2])
174+                       return 10 - ((s1 + s2) % 10)
175+
176+               value = value.upper().replace(' ', '').replace('-', '')
177+               m = re.match(r'^([%s]?)[ -]?(\d+)[ -]?([%s]?)$' % (self.cif_types + self.nie_types, self.nif_control), value)
178+               letter1, number, letter2 = m.groups()
179+
180+               if not letter1 and letter2: # NIF
181+                       if letter2 == nif_get_checksum(number):
182+                               return value
183+                       else:
184+                               raise ValidationError, _('Invalid checksum for NIF.')
185+               elif letter1 in self.nie_types and letter2: # NIE
186+                       if letter2 == nif_get_checksum(number):
187+                               return value
188+                       else:
189+                               raise ValidationError, _('Invalid checksum for NIE.')
190+               elif not self.only_nif and letter1 in self.cif_types and len(number) in [7, 8]: # CIF
191+                       if not letter2:
192+                               number, letter2 = number[:-1], int(number[-1])
193+                       checksum = cif_get_checksum(number)
194+                       if letter2 in [checksum, self.cif_control[checksum]]:
195+                               return value
196+                       else:
197+                               raise ValidationError, _('Invalid checksum for CIF.')
198+               else:
199+                       raise ValidationError, _('Please enter a valid %s.' % self.id_types)
200+
201+class ESCCCField(RegexField):
202+       """
203+    A form field that validates its input as a spanish bank account or CCC (Codigo Cuenta Cliente)
204+
205+               Spanish CCC is in format EEEE-OOOO-CC-AAAAAAAAAA where
206+                       E = entity
207+                       O = office
208+                       C = checksum
209+                       A = account
210+               It's also valid using space as delimiter, or using no delimiter
211+
212+               First checksum digit validates entity and office, and last one validates account. Validation is
213+               done multiplying every digit of 10 digit value (with leading 0 if necessary) by number in its
214+               position in string 1, 2, 4, 8, 5, 10, 9, 7, 3, 6. Sum resulting numbers and extract it from 11.
215+               Result is checksum except when 10 then is 1, or when 11 then is 0.
216+
217+               TODO: allow IBAN validation too
218+       """
219+       def __init__(self, nif=True, cif=True, *args, **kwargs):
220+               super(ESCCCField, self).__init__(r'^\d{4}[ -]?\d{4}[ -]?\d{2}[ -]?\d{10}$',
221+                       max_length=None, min_length=None,
222+                       error_message=_('Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.'),
223+                       *args, **kwargs)
224+       def clean(self, value):
225+               super(ESCCCField, self).clean(value)       
226+               if value in EMPTY_VALUES:
227+                       return u''
228+               control_str = [1, 2, 4, 8, 5, 10, 9, 7, 3, 6]
229+               m = re.match(r'^(\d{4})[ -]?(\d{4})[ -]?(\d{2})[ -]?(\d{10})$', value)
230+               entity, office, checksum, account = m.groups()
231+               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')
232+               if get_checksum('00' + entity + office) + get_checksum(account) == checksum:
233+                       return value
234+               else:
235+                       raise ValidationError, _('Invalid checksum for bank account number.')
236+
237+class ESRegionSelect(Select):
238+    """
239+    A Select widget that uses a list of spanish regions as its choices.
240+    """
241+    def __init__(self, attrs=None):
242+        from es_regions import REGION_CHOICES
243+        super(ESRegionSelect, self).__init__(attrs, choices=REGION_CHOICES)
244+
245+class ESProvinceSelect(Select):
246+    """
247+    A Select widget that uses a list of spanish provinces as its choices.
248+    """
249+    def __init__(self, attrs=None):
250+        from es_provinces import PROVINCE_CHOICES
251+        super(ESProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
252+
253Index: tests/regressiontests/forms/localflavor/es.py
254===================================================================
255--- tests/regressiontests/forms/localflavor/es.py       (revision 0)
256+++ tests/regressiontests/forms/localflavor/es.py       (revision 0)
257@@ -0,0 +1,343 @@
258+# -*- coding: utf-8 -*-
259+# Tests for the contrib/localflavor/ ES form fields.
260+
261+tests = r"""
262+# ESPostalCodeField ##############################################################
263+
264+ESPostalCodeField validates that data is a five-digit spanish postal code.
265+>>> from django.contrib.localflavor.es.forms import ESPostalCodeField
266+>>> f = ESPostalCodeField()
267+>>> f.clean('08028')
268+u'08028'
269+>>> f.clean('28080')
270+u'28080'
271+>>> f.clean('53001')
272+Traceback (most recent call last):
273+...
274+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
275+>>> f.clean('0801')
276+Traceback (most recent call last):
277+...
278+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
279+>>> f.clean('080001')
280+Traceback (most recent call last):
281+...
282+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
283+>>> f.clean('00999')
284+Traceback (most recent call last):
285+...
286+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
287+>>> f.clean('08 01')
288+Traceback (most recent call last):
289+...
290+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
291+>>> f.clean('08A01')
292+Traceback (most recent call last):
293+...
294+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
295+>>> f.clean('')
296+Traceback (most recent call last):
297+...
298+ValidationError: [u'This field is required.']
299+
300+>>> f = ESPostalCodeField(required=False)
301+>>> f.clean('08028')
302+u'08028'
303+>>> f.clean('28080')
304+u'28080'
305+>>> f.clean('53001')
306+Traceback (most recent call last):
307+...
308+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
309+>>> f.clean('0801')
310+Traceback (most recent call last):
311+...
312+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
313+>>> f.clean('080001')
314+Traceback (most recent call last):
315+...
316+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
317+>>> f.clean('00999')
318+Traceback (most recent call last):
319+...
320+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
321+>>> f.clean('08 01')
322+Traceback (most recent call last):
323+...
324+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
325+>>> f.clean('08A01')
326+Traceback (most recent call last):
327+...
328+ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
329+>>> f.clean('')
330+u''
331+
332+# ESPhoneNumberField ##############################################################
333+
334+ESPhoneNumberField validates that data is a nine-digit spanish phone number.
335+>>> from django.contrib.localflavor.es.forms import ESPhoneNumberField
336+>>> f = ESPhoneNumberField()
337+>>> f.clean('650010101')
338+u'650010101'
339+>>> f.clean('931234567')
340+u'931234567'
341+>>> f.clean('800123123')
342+u'800123123'
343+>>> f.clean('555555555')
344+Traceback (most recent call last):
345+...
346+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
347+>>> f.clean('789789789')
348+Traceback (most recent call last):
349+...
350+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
351+>>> f.clean('99123123')
352+Traceback (most recent call last):
353+...
354+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
355+>>> f.clean('9999123123')
356+Traceback (most recent call last):
357+...
358+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
359+>>> f.clean('')
360+Traceback (most recent call last):
361+...
362+ValidationError: [u'This field is required.']
363+
364+>>> f = ESPhoneNumberField(required=False)
365+>>> f.clean('650010101')
366+u'650010101'
367+>>> f.clean('931234567')
368+u'931234567'
369+>>> f.clean('800123123')
370+u'800123123'
371+>>> f.clean('555555555')
372+Traceback (most recent call last):
373+...
374+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
375+>>> f.clean('789789789')
376+Traceback (most recent call last):
377+...
378+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
379+>>> f.clean('99123123')
380+Traceback (most recent call last):
381+...
382+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
383+>>> f.clean('9999123123')
384+Traceback (most recent call last):
385+...
386+ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
387+>>> f.clean('')
388+u''
389+
390+# ESIdentityCardNumberField ##############################################################
391+
392+ESIdentityCardNumberField validates that data is a identification spanish code for companies or individuals (CIF, NIF or NIE).
393+>>> from django.contrib.localflavor.es.forms import ESIdentityCardNumberField
394+>>> f = ESIdentityCardNumberField()
395+>>> f.clean('78699688J')
396+'78699688J'
397+>>> f.clean('78699688-J')
398+'78699688J'
399+>>> f.clean('78699688 J')
400+'78699688J'
401+>>> f.clean('78699688 j')
402+'78699688J'
403+>>> f.clean('78699688T')
404+Traceback (most recent call last):
405+...
406+ValidationError: [u'Invalid checksum for NIF.']
407+>>> f.clean('X0901797J')
408+'X0901797J'
409+>>> f.clean('X-6124387-Q')
410+'X6124387Q'
411+>>> f.clean('X 0012953 G')
412+'X0012953G'
413+>>> f.clean('x-3287690-r')
414+'X3287690R'
415+>>> f.clean('t-03287690r')
416+'T03287690R'
417+>>> f.clean('X-03287690')
418+Traceback (most recent call last):
419+...
420+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
421+>>> f.clean('X-03287690-T')
422+Traceback (most recent call last):
423+...
424+ValidationError: [u'Invalid checksum for NIE.']
425+>>> f.clean('B38790911')
426+'B38790911'
427+>>> f.clean('B-3879091A')
428+'B3879091A'
429+>>> f.clean('B 38790917')
430+Traceback (most recent call last):
431+...
432+ValidationError: [u'Invalid checksum for CIF.']
433+>>> f.clean('B 38790911')
434+'B38790911'
435+>>> f.clean('P-3900800-H')
436+'P3900800H'
437+>>> f.clean('P 39008008')
438+'P39008008'
439+>>> f.clean('C-28795565')
440+'C28795565'
441+>>> f.clean('C 2879556E')
442+'C2879556E'
443+>>> f.clean('C28795567')
444+Traceback (most recent call last):
445+...
446+ValidationError: [u'Invalid checksum for CIF.']
447+>>> f.clean('I38790911')
448+Traceback (most recent call last):
449+...
450+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
451+>>> f.clean('78699688-2')
452+Traceback (most recent call last):
453+...
454+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
455+>>> f.clean('')
456+Traceback (most recent call last):
457+...
458+ValidationError: [u'This field is required.']
459+
460+>>> f = ESIdentityCardNumberField(required=False)
461+>>> f.clean('78699688J')
462+'78699688J'
463+>>> f.clean('78699688-J')
464+'78699688J'
465+>>> f.clean('78699688 J')
466+'78699688J'
467+>>> f.clean('78699688 j')
468+'78699688J'
469+>>> f.clean('78699688T')
470+Traceback (most recent call last):
471+...
472+ValidationError: [u'Invalid checksum for NIF.']
473+>>> f.clean('X0901797J')
474+'X0901797J'
475+>>> f.clean('X-6124387-Q')
476+'X6124387Q'
477+>>> f.clean('X 0012953 G')
478+'X0012953G'
479+>>> f.clean('x-3287690-r')
480+'X3287690R'
481+>>> f.clean('t-03287690r')
482+'T03287690R'
483+>>> f.clean('X-03287690')
484+Traceback (most recent call last):
485+...
486+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
487+>>> f.clean('X-03287690-T')
488+Traceback (most recent call last):
489+...
490+ValidationError: [u'Invalid checksum for NIE.']
491+>>> f.clean('B38790911')
492+'B38790911'
493+>>> f.clean('B-3879091A')
494+'B3879091A'
495+>>> f.clean('B 38790917')
496+Traceback (most recent call last):
497+...
498+ValidationError: [u'Invalid checksum for CIF.']
499+>>> f.clean('B 38790911')
500+'B38790911'
501+>>> f.clean('P-3900800-H')
502+'P3900800H'
503+>>> f.clean('P 39008008')
504+'P39008008'
505+>>> f.clean('C-28795565')
506+'C28795565'
507+>>> f.clean('C 2879556E')
508+'C2879556E'
509+>>> f.clean('C28795567')
510+Traceback (most recent call last):
511+...
512+ValidationError: [u'Invalid checksum for CIF.']
513+>>> f.clean('I38790911')
514+Traceback (most recent call last):
515+...
516+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
517+>>> f.clean('78699688-2')
518+Traceback (most recent call last):
519+...
520+ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
521+>>> f.clean('')
522+u''
523+
524+# ESCCCField ##############################################################
525+
526+ESCCCField validates that data is a spanish bank account number (codigo cuenta cliente).
527+
528+>>> from django.contrib.localflavor.es.forms import ESCCCField
529+>>> f = ESCCCField()
530+>>> f.clean('20770338793100254321')
531+'20770338793100254321'
532+>>> f.clean('2077 0338 79 3100254321')
533+'2077 0338 79 3100254321'
534+>>> f.clean('2077-0338-79-3100254321')
535+'2077-0338-79-3100254321'
536+>>> f.clean('2077.0338.79.3100254321')
537+Traceback (most recent call last):
538+...
539+ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
540+>>> f.clean('2077-0338-78-3100254321')
541+Traceback (most recent call last):
542+...
543+ValidationError: [u'Invalid checksum for bank account number.']
544+>>> f.clean('2077-0338-89-3100254321')
545+Traceback (most recent call last):
546+...
547+ValidationError: [u'Invalid checksum for bank account number.']
548+>>> f.clean('2077-03-3879-3100254321')
549+Traceback (most recent call last):
550+...
551+ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
552+>>> f.clean('')
553+Traceback (most recent call last):
554+...
555+ValidationError: [u'This field is required.']
556+
557+>>> f = ESCCCField(required=False)
558+>>> f.clean('20770338793100254321')
559+'20770338793100254321'
560+>>> f.clean('2077 0338 79 3100254321')
561+'2077 0338 79 3100254321'
562+>>> f.clean('2077-0338-79-3100254321')
563+'2077-0338-79-3100254321'
564+>>> f.clean('2077.0338.79.3100254321')
565+Traceback (most recent call last):
566+...
567+ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
568+>>> f.clean('2077-0338-78-3100254321')
569+Traceback (most recent call last):
570+...
571+ValidationError: [u'Invalid checksum for bank account number.']
572+>>> f.clean('2077-0338-89-3100254321')
573+Traceback (most recent call last):
574+...
575+ValidationError: [u'Invalid checksum for bank account number.']
576+>>> f.clean('2077-03-3879-3100254321')
577+Traceback (most recent call last):
578+...
579+ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
580+>>> f.clean('')
581+u''
582+
583+# ESRegionSelect ##############################################################
584+
585+ESRegionSelect is a Select widget that uses a list of Spain regions as its choices.
586+>>> from django.contrib.localflavor.es.forms import ESRegionSelect
587+>>> w = ESRegionSelect()
588+>>> w.render('regions', 'CT')
589+u'<select name="regions">\n<option value="AN">Andalusia</option>\n<option value="AR">Aragon</option>\n<option value="O">Principality of Asturias</option>\n<option value="IB">Balearic Islands</option>\n<option value="PV">Basque Country</option>\n<option value="CN">Canary Islands</option>\n<option value="S">Cantabria</option>\n<option value="CM">Castile-La Mancha</option>\n<option value="CL">Castile and Leon</option>\n<option value="CT" selected="selected">Catalonia</option>\n<option value="EX">Extremadura</option>\n<option value="GA">Galicia</option>\n<option value="LO">La Rioja</option>\n<option value="M">Madrid</option>\n<option value="MU">Region of Murcia</option>\n<option value="NA">Foral Community of Navarre</option>\n<option value="VC">Valencian Community</option>\n</select>'
590+
591+# ESProvincenSelect ##############################################################
592+
593+ESProvinceSelect is a Select widget that uses a list of Spain provinces as its choices.
594+>>> from django.contrib.localflavor.es.forms import ESProvinceSelect
595+>>> w = ESProvinceSelect()
596+>>> w.render('provinces', '08')
597+u'<select name="provinces">\n<option value="01">Arava</option>\n<option value="02">Albacete</option>\n<option value="03">Alacant</option>\n<option value="04">Almeria</option>\n<option value="05">Avila</option>\n<option value="06">Badajoz</option>\n<option value="07">Illes Balears</option>\n<option value="08" selected="selected">Barcelona</option>\n<option value="09">Burgos</option>\n<option value="10">Caceres</option>\n<option value="11">Cadiz</option>\n<option value="12">Castello</option>\n<option value="13">Ciudad Real</option>\n<option value="14">Cordoba</option>\n<option value="15">A Coruna</option>\n<option value="16">Cuenca</option>\n<option value="17">Girona</option>\n<option value="18">Granada</option>\n<option value="19">Guadalajara</option>\n<option value="20">Guipuzkoa</option>\n<option value="21">Huelva</option>\n<option value="22">Huesca</option>\n<option value="23">Jaen</option>\n<option value="24">Leon</option>\n<option value="25">Lleida</option>\n<option value="26">La Rioja</option>\n<option value="27">Lugo</option>\n<option value="28">Madrid</option>\n<option value="29">Malaga</option>\n<option value="30">Murcia</option>\n<option value="31">Navarre</option>\n<option value="32">Ourense</option>\n<option value="33">Asturias</option>\n<option value="34">Palencia</option>\n<option value="35">Las Palmas</option>\n<option value="36">Pontevedra</option>\n<option value="37">Salamanca</option>\n<option value="38">Santa Cruz de Tenerife</option>\n<option value="39">Cantabria</option>\n<option value="40">Segovia</option>\n<option value="41">Seville</option>\n<option value="42">Soria</option>\n<option value="43">Tarragona</option>\n<option value="44">Teruel</option>\n<option value="45">Toledo</option>\n<option value="46">Valencia</option>\n<option value="47">Valladolid</option>\n<option value="48">Bizkaia</option>\n<option value="49">Zamora</option>\n<option value="50">Zaragoza</option>\n<option value="51">Ceuta</option>\n<option value="52">Melilla</option>\n</select>'
598+
599+"""
600+
601Index: tests/regressiontests/forms/tests.py
602===================================================================
603--- tests/regressiontests/forms/tests.py        (revision 6468)
604+++ tests/regressiontests/forms/tests.py        (working copy)
605@@ -9,6 +9,7 @@
606 from localflavor.ch import tests as localflavor_ch_tests
607 from localflavor.cl import tests as localflavor_cl_tests
608 from localflavor.de import tests as localflavor_de_tests
609+from localflavor.es import tests as localflavor_es_tests
610 from localflavor.fi import tests as localflavor_fi_tests
611 from localflavor.fr import tests as localflavor_fr_tests
612 from localflavor.generic import tests as localflavor_generic_tests
613@@ -35,6 +36,7 @@
614     'localflavor_ch_tests': localflavor_ch_tests,
615     'localflavor_cl_tests': localflavor_cl_tests,
616     'localflavor_de_tests': localflavor_de_tests,
617+    'localflavor_es_tests': localflavor_es_tests,
618     'localflavor_fi_tests': localflavor_fi_tests,
619     'localflavor_fr_tests': localflavor_fr_tests,
620     'localflavor_generic_tests': localflavor_generic_tests,