Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #5871 -- Factored out the validation errors in localflavor form…

… fields. Brings them into line with the standard newforms fields. Patch from Jan Rademaker.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6926 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 1fcb4e4bcdb99b44561b7b70c1563e059daadbd2 1 parent 4d7aa81
Malcolm Tredinnick authored December 17, 2007
28  django/contrib/localflavor/ar/forms.py
@@ -24,18 +24,20 @@ class ARPostalCodeField(RegexField):
24 24
 
25 25
     See http://www.correoargentino.com.ar/consulta_cpa/home.php
26 26
     """
  27
+    default_error_messages = {
  28
+        'invalid': ugettext("Enter a postal code in the format NNNN or ANNNNAAA."),
  29
+    }
  30
+
27 31
     def __init__(self, *args, **kwargs):
28 32
         super(ARPostalCodeField, self).__init__(r'^\d{4}$|^[A-HJ-NP-Za-hj-np-z]\d{4}\D{3}$',
29  
-            min_length=4, max_length=8,
30  
-            error_message=ugettext("Enter a postal code in the format NNNN or ANNNNAAA."),
31  
-                    *args, **kwargs)
  33
+            min_length=4, max_length=8, *args, **kwargs)
32 34
 
33 35
     def clean(self, value):
34 36
         value = super(ARPostalCodeField, self).clean(value)
35 37
         if value in EMPTY_VALUES:
36 38
             return u''
37 39
         if len(value) not in (4, 8):
38  
-            raise ValidationError(ugettext("Enter a postal code in the format NNNN or ANNNNAAA."))
  40
+            raise ValidationError(self.error_messages['invalid'])
39 41
         if len(value) == 8:
40 42
             return u'%s%s%s' % (value[0].upper(), value[1:5], value[5:].upper())
41 43
         return value
@@ -44,6 +46,11 @@ class ARDNIField(CharField):
44 46
     """
45 47
     A field that validates `Documento Nacional de Identidad´ (DNI) numbers.
46 48
     """
  49
+    default_error_messages = {
  50
+        'invalid': ugettext("This field requires only numbers."),
  51
+        'max_digits': ugettext("This field requires 7 or 8 digits."),
  52
+    }
  53
+
47 54
     def __init__(self, *args, **kwargs):
48 55
         super(ARDNIField, self).__init__(max_length=10, min_length=7, *args,
49 56
                 **kwargs)
@@ -58,10 +65,9 @@ def clean(self, value):
58 65
         if not value.isdigit():
59 66
             value = value.replace('.', '')
60 67
         if not value.isdigit():
61  
-            raise ValidationError(ugettext("This field requires only numbers."))
  68
+            raise ValidationError(self.error_messages['invalid'])
62 69
         if len(value) not in (7, 8):
63  
-            raise ValidationError(
64  
-                    ugettext("This field requires 7 or 8 digits."))
  70
+            raise ValidationError(self.error_messages['max_digits'])
65 71
 
66 72
         return value
67 73
 
@@ -70,9 +76,13 @@ class ARCUITField(RegexField):
70 76
     This field validates a CUIT (Código Único de Identificación Tributaria). A
71 77
     CUIT is of the form XX-XXXXXXXX-V. The last digit is a check digit.
72 78
     """
  79
+    default_error_messages = {
  80
+        'invalid': ugettext('Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'),
  81
+        'checksum': ugettext("Invalid CUIT."),
  82
+    }
  83
+
73 84
     def __init__(self, *args, **kwargs):
74 85
         super(ARCUITField, self).__init__(r'^\d{2}-?\d{8}-?\d$',
75  
-            error_message=ugettext('Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'),
76 86
             *args, **kwargs)
77 87
 
78 88
     def clean(self, value):
@@ -85,7 +95,7 @@ def clean(self, value):
85 95
             return u''
86 96
         value, cd = self._canon(value)
87 97
         if self._calc_cd(value) != cd:
88  
-            raise ValidationError(ugettext("Invalid CUIT."))
  98
+            raise ValidationError(self.error_messages['checksum'])
89 99
         return self._format(value, cd)
90 100
 
91 101
     def _canon(self, cuit):
14  django/contrib/localflavor/au/forms.py
@@ -12,14 +12,20 @@
12 12
 
13 13
 class AUPostCodeField(RegexField):
14 14
     """Australian post code field."""
  15
+    default_error_messages = {
  16
+        'invalid': ugettext('Enter a 4 digit post code.'),
  17
+    }
  18
+
15 19
     def __init__(self, *args, **kwargs):
16 20
         super(AUPostCodeField, self).__init__(r'^\d{4}$',
17  
-            max_length=None, min_length=None,
18  
-            error_message=ugettext('Enter a 4 digit post code.'),
19  
-                    *args, **kwargs)
  21
+            max_length=None, min_length=None, *args, **kwargs)
20 22
 
21 23
 class AUPhoneNumberField(Field):
22 24
     """Australian phone number field."""
  25
+    default_error_messages = {
  26
+        'invalid': u'Phone numbers must contain 10 digits.',
  27
+    }
  28
+
23 29
     def clean(self, value):
24 30
         """
25 31
         Validate a phone number. Strips parentheses, whitespace and hyphens.
@@ -31,7 +37,7 @@ def clean(self, value):
31 37
         phone_match = PHONE_DIGITS_RE.search(value)
32 38
         if phone_match:
33 39
             return u'%s' % phone_match.group(1)
34  
-        raise ValidationError(u'Phone numbers must contain 10 digits.')
  40
+        raise ValidationError(self.error_messages['invalid'])
35 41
 
36 42
 class AUStateSelect(Select):
37 43
     """
47  django/contrib/localflavor/br/forms.py
@@ -17,13 +17,19 @@
17 17
 phone_digits_re = re.compile(r'^(\d{2})[-\.]?(\d{4})[-\.]?(\d{4})$')
18 18
 
19 19
 class BRZipCodeField(RegexField):
  20
+    default_error_messages = {
  21
+        'invalid': _('Enter a zip code in the format XXXXX-XXX.'),
  22
+    }
  23
+
20 24
     def __init__(self, *args, **kwargs):
21 25
         super(BRZipCodeField, self).__init__(r'^\d{5}-\d{3}$',
22  
-            max_length=None, min_length=None,
23  
-            error_message=_('Enter a zip code in the format XXXXX-XXX.'),
24  
-                    *args, **kwargs)
  26
+            max_length=None, min_length=None, *args, **kwargs)
25 27
 
26 28
 class BRPhoneNumberField(Field):
  29
+    default_error_messages = {
  30
+        'invalid': _('Phone numbers must be in XX-XXXX-XXXX format.'),
  31
+    }
  32
+
27 33
     def clean(self, value):
28 34
         super(BRPhoneNumberField, self).clean(value)
29 35
         if value in EMPTY_VALUES:
@@ -32,7 +38,7 @@ def clean(self, value):
32 38
         m = phone_digits_re.search(value)
33 39
         if m:
34 40
             return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
35  
-        raise ValidationError(_('Phone numbers must be in XX-XXXX-XXXX format.'))
  41
+        raise ValidationError(self.error_messages['invalid'])
36 42
 
37 43
 class BRStateSelect(Select):
38 44
     """
@@ -48,6 +54,9 @@ class BRStateChoiceField(Field):
48 54
     A choice field that uses a list of Brazilian states as its choices.
49 55
     """
50 56
     widget = Select
  57
+    default_error_messages = {
  58
+        'invalid': _(u'Select a valid brazilian state. That state is not one of the available states.'),
  59
+    }
51 60
 
52 61
     def __init__(self, required=True, widget=None, label=None,
53 62
                  initial=None, help_text=None):
@@ -65,9 +74,7 @@ def clean(self, value):
65 74
             return value
66 75
         valid_values = set([smart_unicode(k) for k, v in self.widget.choices])
67 76
         if value not in valid_values:
68  
-            raise ValidationError(_(u'Select a valid brazilian state.'
69  
-                                           u' That state is not one'
70  
-                                           u' of the available states.'))
  77
+            raise ValidationError(self.error_messages['invalid'])
71 78
         return value
72 79
 
73 80
 def DV_maker(v):
@@ -83,6 +90,12 @@ class BRCPFField(CharField):
83 90
     More information:
84 91
     http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas
85 92
     """
  93
+    default_error_messages = {
  94
+        'invalid': _("Invalid CPF number."),
  95
+        'max_digits': _("This field requires at most 11 digits or 14 characters."),
  96
+        'digits_only': _("This field requires only numbers."),
  97
+    }
  98
+
86 99
     def __init__(self, *args, **kwargs):
87 100
         super(BRCPFField, self).__init__(max_length=14, min_length=11, *args, **kwargs)
88 101
 
@@ -100,9 +113,9 @@ def clean(self, value):
100 113
         try:
101 114
             int(value)
102 115
         except ValueError:
103  
-            raise ValidationError(_("This field requires only numbers."))
  116
+            raise ValidationError(self.error_messages['digits_only'])
104 117
         if len(value) != 11:
105  
-            raise ValidationError(_("This field requires at most 11 digits or 14 characters."))
  118
+            raise ValidationError(self.error_messages['max_digits'])
106 119
         orig_dv = value[-2:]
107 120
 
108 121
         new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(10, 1, -1))])
@@ -112,11 +125,17 @@ def clean(self, value):
112 125
         new_2dv = DV_maker(new_2dv % 11)
113 126
         value = value[:-1] + str(new_2dv)
114 127
         if value[-2:] != orig_dv:
115  
-            raise ValidationError(_("Invalid CPF number."))
  128
+            raise ValidationError(self.error_messages['invalid'])
116 129
 
117 130
         return orig_value
118 131
 
119 132
 class BRCNPJField(Field):
  133
+    default_error_messages = {
  134
+        'invalid': _("Invalid CNPJ number."),
  135
+        'digits_only': _("This field requires only numbers."),
  136
+        'max_digits': _("This field requires at least 14 digits"),
  137
+    }
  138
+
120 139
     def clean(self, value):
121 140
         """
122 141
         Value can be either a string in the format XX.XXX.XXX/XXXX-XX or a
@@ -131,10 +150,9 @@ def clean(self, value):
131 150
         try:
132 151
             int(value)
133 152
         except ValueError:
134  
-            raise ValidationError("This field requires only numbers.")
  153
+            raise ValidationError(self.error_messages['digits_only'])
135 154
         if len(value) != 14:
136  
-            raise ValidationError(
137  
-                _("This field requires at least 14 digits"))
  155
+            raise ValidationError(self.error_messages['max_digits'])
138 156
         orig_dv = value[-2:]
139 157
 
140 158
         new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(5, 1, -1) + range(9, 1, -1))])
@@ -144,7 +162,6 @@ def clean(self, value):
144 162
         new_2dv = DV_maker(new_2dv % 11)
145 163
         value = value[:-1] + str(new_2dv)
146 164
         if value[-2:] != orig_dv:
147  
-            raise ValidationError(_("Invalid CNPJ number."))
  165
+            raise ValidationError(self.error_messages['invalid'])
148 166
 
149 167
         return orig_value
150  
-
100  django/contrib/localflavor/ca/forms.py
... ...
@@ -1,37 +1,43 @@
1  
-""" 
2  
-Canada-specific Form helpers 
3  
-""" 
4  
- 
5  
-from django.newforms import ValidationError 
6  
-from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES 
7  
-from django.newforms.util import smart_unicode 
  1
+"""
  2
+Canada-specific Form helpers
  3
+"""
  4
+
  5
+from django.newforms import ValidationError
  6
+from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
  7
+from django.newforms.util import smart_unicode
8 8
 from django.utils.translation import gettext, ugettext
9  
-import re 
10  
- 
  9
+import re
  10
+
11 11
 phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$')
12 12
 sin_re = re.compile(r"^(\d{3})-(\d{3})-(\d{3})$")
13  
- 
14  
-class CAPostalCodeField(RegexField): 
15  
-    """Canadian postal code field.""" 
16  
-    def __init__(self, *args, **kwargs): 
17  
-        super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$', 
18  
-            max_length=None, min_length=None, 
19  
-            error_message=gettext(u'Enter a postal code in the format XXX XXX.'), 
20  
-            *args, **kwargs) 
21  
- 
22  
-class CAPhoneNumberField(Field): 
23  
-    """Canadian phone number field.""" 
24  
-    def clean(self, value): 
25  
-        """Validate a phone number. 
26  
-        """ 
27  
-        super(CAPhoneNumberField, self).clean(value) 
  13
+
  14
+class CAPostalCodeField(RegexField):
  15
+    """Canadian postal code field."""
  16
+    default_error_messages = {
  17
+        'invalid': gettext(u'Enter a postal code in the format XXX XXX.'),
  18
+    }
  19
+
  20
+    def __init__(self, *args, **kwargs):
  21
+        super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$',
  22
+            max_length=None, min_length=None, *args, **kwargs)
  23
+
  24
+class CAPhoneNumberField(Field):
  25
+    """Canadian phone number field."""
  26
+    default_error_messages = {
  27
+        'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.',
  28
+    }
  29
+
  30
+    def clean(self, value):
  31
+        """Validate a phone number.
  32
+        """
  33
+        super(CAPhoneNumberField, self).clean(value)
28 34
         if value in EMPTY_VALUES:
29 35
             return u''
30 36
         value = re.sub('(\(|\)|\s+)', '', smart_unicode(value))
31 37
         m = phone_digits_re.search(value)
32 38
         if m:
33 39
             return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
34  
-        raise ValidationError(u'Phone numbers must be in XXX-XXX-XXXX format.') 
  40
+        raise ValidationError(self.error_messages['invalid'])
35 41
 
36 42
 class CAProvinceField(Field):
37 43
     """
@@ -39,6 +45,10 @@ class CAProvinceField(Field):
39 45
     It normalizes the input to the standard two-leter postal service
40 46
     abbreviation for the given province.
41 47
     """
  48
+    default_error_messages = {
  49
+        'invalid': u'Enter a Canadian province or territory.',
  50
+    }
  51
+
42 52
     def clean(self, value):
43 53
         from ca_provinces import PROVINCES_NORMALIZED
44 54
         super(CAProvinceField, self).clean(value)
@@ -53,17 +63,17 @@ def clean(self, value):
53 63
                 return PROVINCES_NORMALIZED[value.strip().lower()].decode('ascii')
54 64
             except KeyError:
55 65
                 pass
56  
-        raise ValidationError(u'Enter a Canadian province or territory.')
57  
- 
58  
-class CAProvinceSelect(Select): 
59  
-    """ 
60  
-    A Select widget that uses a list of Canadian provinces and 
61  
-    territories as its choices. 
62  
-    """ 
63  
-    def __init__(self, attrs=None): 
64  
-        from ca_provinces import PROVINCE_CHOICES # relative import 
  66
+        raise ValidationError(self.error_messages['invalid'])
  67
+
  68
+class CAProvinceSelect(Select):
  69
+    """
  70
+    A Select widget that uses a list of Canadian provinces and
  71
+    territories as its choices.
  72
+    """
  73
+    def __init__(self, attrs=None):
  74
+        from ca_provinces import PROVINCE_CHOICES # relative import
65 75
         super(CAProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
66  
-            
  76
+
67 77
 class CASocialInsuranceNumberField(Field):
68 78
     """
69 79
     A Canadian Social Insurance Number (SIN).
@@ -74,24 +84,28 @@ class CASocialInsuranceNumberField(Field):
74 84
         * Passes the check digit process "Luhn Algorithm"
75 85
              See: http://en.wikipedia.org/wiki/Social_Insurance_Number
76 86
     """
  87
+    default_error_messages = {
  88
+        'invalid': ugettext('Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.'),
  89
+    }
  90
+
77 91
     def clean(self, value):
78 92
         super(CASocialInsuranceNumberField, self).clean(value)
79 93
         if value in EMPTY_VALUES:
80 94
             return u''
81  
-        msg = ugettext('Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.')
  95
+
82 96
         match = re.match(sin_re, value)
83 97
         if not match:
84  
-            raise ValidationError(msg)
85  
-            
86  
-        number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3))    
  98
+            raise ValidationError(self.error_messages['invalid'])
  99
+
  100
+        number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3))
87 101
         check_number = u'%s%s%s' % (match.group(1), match.group(2), match.group(3))
88 102
         if not self.luhn_checksum_is_valid(check_number):
89  
-            raise ValidationError(msg)
  103
+            raise ValidationError(self.error_messages['invalid'])
90 104
         return number
91  
-        
  105
+
92 106
     def luhn_checksum_is_valid(self, number):
93 107
         """
94  
-        Checks to make sure that the SIN passes a luhn mod-10 checksum 
  108
+        Checks to make sure that the SIN passes a luhn mod-10 checksum
95 109
         See: http://en.wikipedia.org/wiki/Luhn_algorithm
96 110
         """
97 111
 
@@ -109,4 +123,4 @@ def luhn_checksum_is_valid(self, number):
109 123
 
110 124
             sum = sum + digit
111 125
 
112  
-        return ( (sum % 10) == 0 )
  126
+        return ( (sum % 10) == 0 )
25  django/contrib/localflavor/ch/forms.py
@@ -12,11 +12,13 @@
12 12
 phone_digits_re = re.compile(r'^0([1-9]{1})\d{8}$')
13 13
 
14 14
 class CHZipCodeField(RegexField):
  15
+    default_error_messages = {
  16
+        'invalid': ugettext('Enter a zip code in the format XXXX.'),
  17
+    }
  18
+
15 19
     def __init__(self, *args, **kwargs):
16 20
         super(CHZipCodeField, self).__init__(r'^\d{4}$',
17  
-        max_length=None, min_length=None,
18  
-        error_message=ugettext('Enter a zip code in the format XXXX.'),
19  
-        *args, **kwargs)
  21
+        max_length=None, min_length=None, *args, **kwargs)
20 22
 
21 23
 class CHPhoneNumberField(Field):
22 24
     """
@@ -25,6 +27,10 @@ class CHPhoneNumberField(Field):
25 27
     '0XX.XXX.XX.XX' and '0XXXXXXXXX' validate but are corrected to
26 28
     '0XX XXX XX XX'.
27 29
     """
  30
+    default_error_messages = {
  31
+        'invalid': 'Phone numbers must be in 0XX XXX XX XX format.',
  32
+    }
  33
+
28 34
     def clean(self, value):
29 35
         super(CHPhoneNumberField, self).clean(value)
30 36
         if value in EMPTY_VALUES:
@@ -33,7 +39,7 @@ def clean(self, value):
33 39
         m = phone_digits_re.search(value)
34 40
         if m:
35 41
             return u'%s %s %s %s' % (value[0:3], value[3:6], value[6:8], value[8:10])
36  
-        raise ValidationError('Phone numbers must be in 0XX XXX XX XX format.')
  42
+        raise ValidationError(self.error_messages['invalid'])
37 43
 
38 44
 class CHStateSelect(Select):
39 45
     """
@@ -54,6 +60,10 @@ class CHIdentityCardNumberField(Field):
54 60
 
55 61
     Algorithm is documented at http://adi.kousz.ch/artikel/IDCHE.htm
56 62
     """
  63
+    default_error_messages = {
  64
+        'invalid': ugettext('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'),
  65
+    }
  66
+
57 67
     def has_valid_checksum(self, number):
58 68
         given_number, given_checksum = number[:-1], number[-1]
59 69
         new_number = given_number
@@ -87,23 +97,22 @@ def has_valid_checksum(self, number):
87 97
 
88 98
     def clean(self, value):
89 99
         super(CHIdentityCardNumberField, self).clean(value)
90  
-        error_msg = ugettext('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.')
91 100
         if value in EMPTY_VALUES:
92 101
             return u''
93 102
 
94 103
         match = re.match(id_re, value)
95 104
         if not match:
96  
-            raise ValidationError(error_msg)
  105
+            raise ValidationError(self.error_messages['invalid'])
97 106
 
98 107
         idnumber, pos9, checksum = match.groupdict()['idnumber'], match.groupdict()['pos9'], match.groupdict()['checksum']
99 108
 
100 109
         if idnumber == '00000000' or \
101 110
            idnumber == 'A0000000':
102  
-            raise ValidationError(error_msg)
  111
+            raise ValidationError(self.error_messages['invalid'])
103 112
 
104 113
         all_digits = "%s%s%s" % (idnumber, pos9, checksum)
105 114
         if not self.has_valid_checksum(all_digits):
106  
-            raise ValidationError(error_msg)
  115
+            raise ValidationError(self.error_messages['invalid'])
107 116
 
108 117
         return u'%s%s%s' % (idnumber, pos9, checksum)
109 118
 
13  django/contrib/localflavor/cl/forms.py
@@ -25,16 +25,21 @@ class CLRutField(RegexField):
25 25
     Samples for testing are available from
26 26
     https://palena.sii.cl/cvc/dte/ee_empresas_emisoras.html
27 27
     """
  28
+    default_error_messages = {
  29
+        'invalid': ugettext('Enter a valid Chilean RUT.'),
  30
+        'strict': ugettext('Enter a valid Chilean RUT. The format is XX.XXX.XXX-X.'),
  31
+        'checksum': ugettext('The Chilean RUT is not valid.'),
  32
+    }
  33
+
28 34
     def __init__(self, *args, **kwargs):
29 35
         if 'strict' in kwargs:
30 36
             del kwargs['strict']
31 37
             super(CLRutField, self).__init__(r'^(\d{1,2}\.)?\d{3}\.\d{3}-[\dkK]$',
32  
-                error_message=ugettext('Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'),
33  
-                        *args, **kwargs)
  38
+                error_message=self.default_error_messages['strict'], *args, **kwargs)
34 39
         else:
35 40
             # In non-strict mode, accept RUTs that validate but do not exist in
36 41
             # the real world.
37  
-            super(CLRutField, self).__init__(r'^[\d\.]{1,11}-?[\dkK]$', error_message=ugettext('Enter valid a Chilean RUT'), *args, **kwargs)
  42
+            super(CLRutField, self).__init__(r'^[\d\.]{1,11}-?[\dkK]$', *args, **kwargs)
38 43
 
39 44
     def clean(self, value):
40 45
         """
@@ -47,7 +52,7 @@ def clean(self, value):
47 52
         if self._algorithm(rut) == verificador:
48 53
             return self._format(rut, verificador)
49 54
         else:
50  
-            raise ValidationError(u'The Chilean RUT is not valid.')
  55
+            raise ValidationError(self.error_messages['checksum'])
51 56
 
52 57
     def _algorithm(self, rut):
53 58
         """
18  django/contrib/localflavor/de/forms.py
@@ -10,11 +10,12 @@
10 10
 id_re = re.compile(r"^(?P<residence>\d{10})(?P<origin>\w{1,3})[-\ ]?(?P<birthday>\d{7})[-\ ]?(?P<validity>\d{7})[-\ ]?(?P<checksum>\d{1})$")
11 11
 
12 12
 class DEZipCodeField(RegexField):
  13
+    default_error_messages = {
  14
+        'invalid': ugettext('Enter a zip code in the format XXXXX.'),
  15
+    }
13 16
     def __init__(self, *args, **kwargs):
14 17
         super(DEZipCodeField, self).__init__(r'^\d{5}$',
15  
-            max_length=None, min_length=None,
16  
-            error_message=ugettext('Enter a zip code in the format XXXXX.'),
17  
-                    *args, **kwargs)
  18
+            max_length=None, min_length=None, *args, **kwargs)
18 19
 
19 20
 class DEStateSelect(Select):
20 21
     """
@@ -36,6 +37,10 @@ class DEIdentityCardNumberField(Field):
36 37
 
37 38
     Algorithm is documented at http://de.wikipedia.org/wiki/Personalausweis
38 39
     """
  40
+    default_error_messages = {
  41
+        'invalid': ugettext('Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.'),
  42
+    }
  43
+
39 44
     def has_valid_checksum(self, number):
40 45
         given_number, given_checksum = number[:-1], number[-1]
41 46
         calculated_checksum = 0
@@ -57,23 +62,22 @@ def has_valid_checksum(self, number):
57 62
 
58 63
     def clean(self, value):
59 64
         super(DEIdentityCardNumberField, self).clean(value)
60  
-        error_msg = ugettext('Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.')
61 65
         if value in EMPTY_VALUES:
62 66
             return u''
63 67
         match = re.match(id_re, value)
64 68
         if not match:
65  
-            raise ValidationError(error_msg)
  69
+            raise ValidationError(self.error_messages['invalid'])
66 70
 
67 71
         gd = match.groupdict()
68 72
         residence, origin = gd['residence'], gd['origin']
69 73
         birthday, validity, checksum = gd['birthday'], gd['validity'], gd['checksum']
70 74
 
71 75
         if residence == '0000000000' or birthday == '0000000' or validity == '0000000':
72  
-            raise ValidationError(error_msg)
  76
+            raise ValidationError(self.error_messages['invalid'])
73 77
 
74 78
         all_digits = u"%s%s%s%s" % (residence, birthday, validity, checksum)
75 79
         if not self.has_valid_checksum(residence) or not self.has_valid_checksum(birthday) or \
76 80
             not self.has_valid_checksum(validity) or not self.has_valid_checksum(all_digits):
77  
-                raise ValidationError(error_msg)
  81
+                raise ValidationError(self.error_messages['invalid'])
78 82
 
79 83
         return u'%s%s-%s-%s-%s' % (residence, origin, birthday, validity, checksum)
49  django/contrib/localflavor/es/forms.py
@@ -15,12 +15,14 @@ class ESPostalCodeField(RegexField):
15 15
     Spanish postal code is a five digits string, with two first digits
16 16
     between 01 and 52, assigned to provinces code.
17 17
     """
  18
+    default_error_messages = {
  19
+        'invalid': _('Enter a valid postal code in the range and format 01XXX - 52XXX.'),
  20
+    }
  21
+
18 22
     def __init__(self, *args, **kwargs):
19 23
         super(ESPostalCodeField, self).__init__(
20 24
                 r'^(0[1-9]|[1-4][0-9]|5[0-2])\d{3}$',
21  
-                max_length=None, min_length=None,
22  
-                error_message=_('Enter a valid postal code in the range and format 01XXX - 52XXX.'),
23  
-                *args, **kwargs)
  25
+                max_length=None, min_length=None, *args, **kwargs)
24 26
 
25 27
 class ESPhoneNumberField(RegexField):
26 28
     """
@@ -33,11 +35,13 @@ class ESPhoneNumberField(RegexField):
33 35
 
34 36
     TODO: accept and strip characters like dot, hyphen... in phone number
35 37
     """
  38
+    default_error_messages = {
  39
+        'invalid': _('Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.'),
  40
+    }
  41
+
36 42
     def __init__(self, *args, **kwargs):
37 43
         super(ESPhoneNumberField, self).__init__(r'^(6|8|9)\d{8}$',
38  
-                max_length=None, min_length=None,
39  
-                error_message=_('Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.'),
40  
-                *args, **kwargs)
  44
+                max_length=None, min_length=None, *args, **kwargs)
41 45
 
42 46
 class ESIdentityCardNumberField(RegexField):
43 47
     """
@@ -58,19 +62,23 @@ class ESIdentityCardNumberField(RegexField):
58 62
     public, and different authors have different opinions on which ones allows
59 63
     letters, so both validations are assumed true for all types.
60 64
     """
  65
+    default_error_messages = {
  66
+        'invalid': _('Please enter a valid NIF, NIE, or CIF.'),
  67
+        'invalid_only_nif': _('Please enter a valid NIF or NIE.'),
  68
+        'invalid_nif': _('Invalid checksum for NIF.'),
  69
+        'invalid_nie': _('Invalid checksum for NIE.'),
  70
+        'invalid_cif': _('Invalid checksum for CIF.'),
  71
+    }
  72
+
61 73
     def __init__(self, only_nif=False, *args, **kwargs):
62 74
         self.only_nif = only_nif
63 75
         self.nif_control = 'TRWAGMYFPDXBNJZSQVHLCKE'
64 76
         self.cif_control = 'JABCDEFGHI'
65 77
         self.cif_types = 'ABCDEFGHKLMNPQS'
66 78
         self.nie_types = 'XT'
67  
-        if self.only_nif:
68  
-            self.id_types = 'NIF or NIE'
69  
-        else:
70  
-            self.id_types = 'NIF, NIE, or CIF'
71 79
         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()),
72 80
                 max_length=None, min_length=None,
73  
-                error_message=_('Please enter a valid %s.' % self.id_types),
  81
+                error_message=self.default_error_messages['invalid%s' % (self.only_nif and '_only_nif' or '')],
74 82
                 *args, **kwargs)
75 83
 
76 84
     def clean(self, value):
@@ -88,13 +96,13 @@ def clean(self, value):
88 96
             if letter2 == nif_get_checksum(number):
89 97
                 return value
90 98
             else:
91  
-                raise ValidationError, _('Invalid checksum for NIF.')
  99
+                raise ValidationError, self.error_messages['invalid_nif']
92 100
         elif letter1 in self.nie_types and letter2:
93 101
             # NIE
94 102
             if letter2 == nif_get_checksum(number):
95 103
                 return value
96 104
             else:
97  
-                raise ValidationError, _('Invalid checksum for NIE.')
  105
+                raise ValidationError, self.error_messages['invalid_nie']
98 106
         elif not self.only_nif and letter1 in self.cif_types and len(number) in [7, 8]:
99 107
             # CIF
100 108
             if not letter2:
@@ -103,9 +111,9 @@ def clean(self, value):
103 111
             if letter2 in [checksum, self.cif_control[checksum]]:
104 112
                 return value
105 113
             else:
106  
-                raise ValidationError, _('Invalid checksum for CIF.')
  114
+                raise ValidationError, self.error_messages['invalid_cif']
107 115
         else:
108  
-            raise ValidationError, _('Please enter a valid %s.' % self.id_types)
  116
+            raise ValidationError, self.error_messages['invalid']
109 117
 
110 118
 class ESCCCField(RegexField):
111 119
     """
@@ -130,11 +138,14 @@ class ESCCCField(RegexField):
130 138
 
131 139
         TODO: allow IBAN validation too
132 140
     """
  141
+    default_error_messages = {
  142
+        'invalid': _('Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.'),
  143
+        'checksum': _('Invalid checksum for bank account number.'),
  144
+    }
  145
+
133 146
     def __init__(self, *args, **kwargs):
134 147
         super(ESCCCField, self).__init__(r'^\d{4}[ -]?\d{4}[ -]?\d{2}[ -]?\d{10}$',
135  
-            max_length=None, min_length=None,
136  
-            error_message=_('Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.'),
137  
-            *args, **kwargs)
  148
+            max_length=None, min_length=None, *args, **kwargs)
138 149
 
139 150
     def clean(self, value):
140 151
         super(ESCCCField, self).clean(value)
@@ -147,7 +158,7 @@ def clean(self, value):
147 158
         if get_checksum('00' + entity + office) + get_checksum(account) == checksum:
148 159
             return value
149 160
         else:
150  
-            raise ValidationError, _('Invalid checksum for bank account number.')
  161
+            raise ValidationError, self.error_messages['checksum']
151 162
 
152 163
 class ESRegionSelect(Select):
153 164
     """
15  django/contrib/localflavor/fi/forms.py
@@ -8,11 +8,12 @@
8 8
 from django.utils.translation import ugettext
9 9
 
10 10
 class FIZipCodeField(RegexField):
  11
+    default_error_messages = {
  12
+        'invalid': ugettext('Enter a zip code in the format XXXXX.'),
  13
+    }
11 14
     def __init__(self, *args, **kwargs):
12 15
         super(FIZipCodeField, self).__init__(r'^\d{5}$',
13  
-            max_length=None, min_length=None,
14  
-            error_message=ugettext('Enter a zip code in the format XXXXX.'),
15  
-                    *args, **kwargs)
  16
+            max_length=None, min_length=None, *args, **kwargs)
16 17
 
17 18
 class FIMunicipalitySelect(Select):
18 19
     """
@@ -23,6 +24,10 @@ def __init__(self, attrs=None):
23 24
         super(FIMunicipalitySelect, self).__init__(attrs, choices=MUNICIPALITY_CHOICES)
24 25
 
25 26
 class FISocialSecurityNumber(Field):
  27
+    default_error_messages = {
  28
+        'invalid': ugettext('Enter a valid Finnish social security number.'),
  29
+    }
  30
+
26 31
     def clean(self, value):
27 32
         super(FISocialSecurityNumber, self).clean(value)
28 33
         if value in EMPTY_VALUES:
@@ -37,9 +42,9 @@ def clean(self, value):
37 42
             (?P<serial>(\d{3}))
38 43
             (?P<checksum>[%s])$""" % checkmarks, value, re.VERBOSE | re.IGNORECASE)
39 44
         if not result:
40  
-            raise ValidationError(ugettext('Enter a valid Finnish social security number.'))
  45
+            raise ValidationError(self.error_messages['invalid'])
41 46
         gd = result.groupdict()
42 47
         checksum = int(gd['date'] + gd['serial'])
43 48
         if checkmarks[checksum % len(checkmarks)] == gd['checksum'].upper():
44 49
             return u'%s' % value.upper()
45  
-        raise ValidationError(ugettext('Enter a valid Finnish social security number.'))
  50
+        raise ValidationError(self.error_messages['invalid'])
14  django/contrib/localflavor/fr/forms.py
@@ -11,11 +11,13 @@
11 11
 phone_digits_re = re.compile(r'^0\d(\s|\.)?(\d{2}(\s|\.)?){3}\d{2}$')
12 12
 
13 13
 class FRZipCodeField(RegexField):
  14
+    default_error_messages = {
  15
+        'invalid': ugettext('Enter a zip code in the format XXXXX.'),
  16
+    }
  17
+
14 18
     def __init__(self, *args, **kwargs):
15 19
         super(FRZipCodeField, self).__init__(r'^\d{5}$',
16  
-            max_length=None, min_length=None,
17  
-            error_message=ugettext('Enter a zip code in the format XXXXX.'),
18  
-                    *args, **kwargs)
  20
+            max_length=None, min_length=None, *args, **kwargs)
19 21
 
20 22
 class FRPhoneNumberField(Field):
21 23
     """
@@ -24,6 +26,10 @@ class FRPhoneNumberField(Field):
24 26
     '0X.XX.XX.XX.XX' and '0XXXXXXXXX' validate but are corrected to
25 27
     '0X XX XX XX XX'.
26 28
     """
  29
+    default_error_messages = {
  30
+        'invalid': u'Phone numbers must be in 0X XX XX XX XX format.',
  31
+    }
  32
+
27 33
     def clean(self, value):
28 34
         super(FRPhoneNumberField, self).clean(value)
29 35
         if value in EMPTY_VALUES:
@@ -32,7 +38,7 @@ def clean(self, value):
32 38
         m = phone_digits_re.search(value)
33 39
         if m:
34 40
             return u'%s %s %s %s %s' % (value[0:2], value[2:4], value[4:6], value[6:8], value[8:10])
35  
-        raise ValidationError(u'Phone numbers must be in 0X XX XX XX XX format.')
  41
+        raise ValidationError(self.error_messages['invalid'])
36 42
 
37 43
 class FRDepartmentSelect(Select):
38 44
     """
14  django/contrib/localflavor/in_/forms.py
@@ -10,11 +10,13 @@
10 10
 
11 11
 
12 12
 class INZipCodeField(RegexField):
  13
+    default_error_messages = {
  14
+        'invalid': gettext(u'Enter a zip code in the format XXXXXXX.'),
  15
+    }
  16
+
13 17
     def __init__(self, *args, **kwargs):
14 18
         super(INZipCodeField, self).__init__(r'^\d{6}$',
15  
-            max_length=None, min_length=None,
16  
-            error_message=gettext(u'Enter a zip code in the format XXXXXXX.'),
17  
-            *args, **kwargs)
  19
+            max_length=None, min_length=None, *args, **kwargs)
18 20
 
19 21
 class INStateField(Field):
20 22
     """
@@ -22,6 +24,10 @@ class INStateField(Field):
22 24
     abbreviation. It normalizes the input to the standard two-letter vehicle
23 25
     registration abbreviation for the given state or union territory
24 26
     """
  27
+    default_error_messages = {
  28
+        'invalid': u'Enter a Indian state or territory.',
  29
+    }
  30
+
25 31
     def clean(self, value):
26 32
         from in_states import STATES_NORMALIZED
27 33
         super(INStateField, self).clean(value)
@@ -36,7 +42,7 @@ def clean(self, value):
36 42
                 return smart_unicode(STATES_NORMALIZED[value.strip().lower()])
37 43
             except KeyError:
38 44
                 pass
39  
-        raise ValidationError(u'Enter a Indian state or territory.')
  45
+        raise ValidationError(self.error_messages['invalid'])
40 46
 
41 47
 class INStateSelect(Select):
42 48
     """
10  django/contrib/localflavor/is_/forms.py
@@ -13,10 +13,14 @@ class ISIdNumberField(RegexField):
13 13
     Icelandic identification number (kennitala). This is a number every citizen
14 14
     of Iceland has.
15 15
     """
  16
+    default_error_messages = {
  17
+        'invalid': ugettext('Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.'),
  18
+        'checksum': ugettext(u'The Icelandic identification number is not valid.'),
  19
+    }
  20
+
16 21
     def __init__(self, *args, **kwargs):
17  
-        error_msg = ugettext('Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.')
18 22
         kwargs['min_length'],kwargs['max_length'] = 10,11
19  
-        super(ISIdNumberField, self).__init__(r'^\d{6}(-| )?\d{4}$', error_message=error_msg, *args, **kwargs)
  23
+        super(ISIdNumberField, self).__init__(r'^\d{6}(-| )?\d{4}$', *args, **kwargs)
20 24
 
21 25
     def clean(self, value):
22 26
         value = super(ISIdNumberField, self).clean(value)
@@ -28,7 +32,7 @@ def clean(self, value):
28 32
         if self._validate(value):
29 33
             return self._format(value)
30 34
         else:
31  
-            raise ValidationError(ugettext(u'The Icelandic identification number is not valid.'))
  35
+            raise ValidationError(self.error_messages['checksum'])
32 36
 
33 37
     def _canonify(self, value):
34 38
         """
28  django/contrib/localflavor/it/forms.py
@@ -10,11 +10,12 @@
10 10
 import re
11 11
 
12 12
 class ITZipCodeField(RegexField):
  13
+    default_error_messages = {
  14
+        'invalid': ugettext('Enter a valid zip code.'),
  15
+    }
13 16
     def __init__(self, *args, **kwargs):
14 17
         super(ITZipCodeField, self).__init__(r'^\d{5}$',
15  
-        max_length=None, min_length=None,
16  
-        error_message=ugettext('Enter a valid zip code.'),
17  
-                *args, **kwargs)
  18
+        max_length=None, min_length=None, *args, **kwargs)
18 19
 
19 20
 class ITRegionSelect(Select):
20 21
     """
@@ -38,11 +39,13 @@ class ITSocialSecurityNumberField(RegexField):
38 39
     For reference see http://www.agenziaentrate.it/ and search for
39 40
     'Informazioni sulla codificazione delle persone fisiche'.
40 41
     """
41  
-    err_msg = ugettext(u'Enter a valid Social Security number.')
  42
+    default_error_messages = {
  43
+        'invalid': ugettext(u'Enter a valid Social Security number.'),
  44
+    }
  45
+
42 46
     def __init__(self, *args, **kwargs):
43 47
         super(ITSocialSecurityNumberField, self).__init__(r'^\w{3}\s*\w{3}\s*\w{5}\s*\w{5}$',
44  
-        max_length=None, min_length=None, error_message=self.err_msg,
45  
-        *args, **kwargs)
  48
+        max_length=None, min_length=None, *args, **kwargs)
46 49
 
47 50
     def clean(self, value):
48 51
         value = super(ITSocialSecurityNumberField, self).clean(value)
@@ -52,26 +55,29 @@ def clean(self, value):
52 55
         try:
53 56
             check_digit = ssn_check_digit(value)
54 57
         except ValueError:
55  
-            raise ValidationError(self.err_msg)
  58
+            raise ValidationError(self.error_messages['invalid'])
56 59
         if not value[15] == check_digit:
57  
-            raise ValidationError(self.err_msg)
  60
+            raise ValidationError(self.error_messages['invalid'])
58 61
         return value
59 62
 
60 63
 class ITVatNumberField(Field):
61 64
     """
62 65
     A form field that validates Italian VAT numbers (partita IVA).
63 66
     """
  67
+    default_error_messages = {
  68
+        'invalid': ugettext(u'Enter a valid VAT number.'),
  69
+    }
  70
+
64 71
     def clean(self, value):
65 72
         value = super(ITVatNumberField, self).clean(value)
66 73
         if value == u'':
67 74
             return value
68  
-        err_msg = ugettext(u'Enter a valid VAT number.')
69 75
         try:
70 76
             vat_number = int(value)
71 77
         except ValueError:
72  
-            raise ValidationError(err_msg)
  78
+            raise ValidationError(self.error_messages['invalid'])
73 79
         vat_number = str(vat_number).zfill(11)
74 80
         check_digit = vat_number_check_digit(vat_number[0:10])
75 81
         if not vat_number[10] == check_digit:
76  
-            raise ValidationError(err_msg)
  82
+            raise ValidationError(self.error_messages['invalid'])
77 83
         return smart_unicode(vat_number)
8  django/contrib/localflavor/jp/forms.py
@@ -15,11 +15,13 @@ class JPPostalCodeField(RegexField):
15 15
 
16 16
     Accepts 7 digits, with or without a hyphen.
17 17
     """
  18
+    default_error_messages = {
  19
+        'invalid': ugettext('Enter a postal code in the format XXXXXXX or XXX-XXXX.'),
  20
+    }
  21
+
18 22
     def __init__(self, *args, **kwargs):
19 23
         super(JPPostalCodeField, self).__init__(r'^\d{3}-\d{4}$|^\d{7}$',
20  
-            max_length=None, min_length=None,
21  
-            error_message=ugettext('Enter a postal code in the format XXXXXXX or XXX-XXXX.'),
22  
-                    *args, **kwargs)
  24
+            max_length=None, min_length=None, *args, **kwargs)
23 25
 
24 26
     def clean(self, value):
25 27
         """
56  django/contrib/localflavor/nl/forms.py
@@ -17,24 +17,27 @@ class NLZipCodeField(Field):
17 17
     """
18 18
     A Dutch postal code field.
19 19
     """
  20
+    default_error_messages = {
  21
+        'invalid': _('Enter a valid postal code'),
  22
+    }
  23
+
20 24
     def clean(self, value):
21 25
         super(NLZipCodeField, self).clean(value)
22 26
         if value in EMPTY_VALUES:
23 27
             return u''
24  
-        
25  
-        msg = _('Enter a valid postal code')
  28
+
26 29
         value = value.strip().upper().replace(' ', '')
27 30
         if not pc_re.search(value):
28  
-            raise ValidationError(msg)
29  
-        
  31
+            raise ValidationError(self.error_messages['invalid'])
  32
+
30 33
         if int(value[:4]) < 1000:
31  
-            raise ValidationError(msg)
32  
-        
  34
+            raise ValidationError(self.error_messages['invalid'])
  35
+
33 36
         return u'%s %s' % (value[:4], value[4:])
34 37
 
35 38
 class NLProvinceSelect(Select):
36 39
     """
37  
-    A Select widget that uses a list of provinces of the Netherlands as its 
  40
+    A Select widget that uses a list of provinces of the Netherlands as its
38 41
     choices.
39 42
     """
40 43
     def __init__(self, attrs=None):
@@ -45,48 +48,53 @@ class NLPhoneNumberField(Field):
45 48
     """
46 49
     A Dutch telephone number field.
47 50
     """
  51
+    default_error_messages = {
  52
+        'invalid': _('Enter a valid phone number'),
  53
+    }
  54
+
48 55
     def clean(self, value):
49 56
         super(NLPhoneNumberField, self).clean(value)
50 57
         if value in EMPTY_VALUES:
51 58
             return u''
52  
-        
53  
-        msg = _('Enter a valid phone number')
  59
+
54 60
         phone_nr = re.sub('[\-\s\(\)]', '', smart_unicode(value))
55  
-        
  61
+
56 62
         if len(phone_nr) == 10 and numeric_re.search(phone_nr):
57 63
             return value
58  
-        
  64
+
59 65
         if phone_nr[:3] == '+31' and len(phone_nr) == 12 and \
60 66
            numeric_re.search(phone_nr[3:]):
61 67
             return value
62  
-        
63  
-        raise ValidationError(msg)
  68
+
  69
+        raise ValidationError(self.error_messages['invalid'])
64 70
 
65 71
 class NLSoFiNumberField(Field):
66 72
     """
67 73
     A Dutch social security number (SoFi/BSN) field.
68  
- 
  74
+
69 75
     http://nl.wikipedia.org/wiki/Sofinummer
70 76
     """
  77
+    default_error_messages = {
  78
+        'invalid': _('Enter a valid SoFi number'),
  79
+    }
  80
+
71 81
     def clean(self, value):
72 82
         super(NLSoFiNumberField, self).clean(value)
73 83
         if value in EMPTY_VALUES:
74 84
             return u''
75  
-        
76  
-        msg = _('Enter a valid SoFi number')
77  
-        
  85
+
78 86
         if not sofi_re.search(value):
79  
-            raise ValidationError(msg)
80  
-        
  87
+            raise ValidationError(self.error_messages['invalid'])
  88
+
81 89
         if int(value) == 0:
82  
-            raise ValidationError(msg)
83  
-        
  90
+            raise ValidationError(self.error_messages['invalid'])
  91
+
84 92
         checksum = 0
85 93
         for i in range(9, 1, -1):
86 94
             checksum += int(value[9-i]) * i
87 95
         checksum -= int(value[-1])
88  
-        
  96
+
89 97
         if checksum % 11 != 0:
90  
-            raise ValidationError(msg)
91  
-        
  98
+            raise ValidationError(self.error_messages['invalid'])
  99
+
92 100
         return value
21  django/contrib/localflavor/no/forms.py
@@ -8,11 +8,13 @@
8 8
 from django.utils.translation import ugettext
9 9
 
10 10
 class NOZipCodeField(RegexField):
  11
+    default_error_messages = {
  12
+        'invalid': ugettext('Enter a zip code in the format XXXX.'),
  13
+    }
  14
+
11 15
     def __init__(self, *args, **kwargs):
12 16
         super(NOZipCodeField, self).__init__(r'^\d{4}$',
13  
-            max_length=None, min_length=None,
14  
-            error_message=ugettext('Enter a zip code in the format XXXX.'),
15  
-                    *args, **kwargs)
  17
+            max_length=None, min_length=None, *args, **kwargs)
16 18
 
17 19
 class NOMunicipalitySelect(Select):
18 20
     """
@@ -27,14 +29,17 @@ class NOSocialSecurityNumber(Field):
27 29
     """
28 30
     Algorithm is documented at http://no.wikipedia.org/wiki/Personnummer
29 31
     """
  32
+    default_error_messages = {
  33
+        'invalid': ugettext(u'Enter a valid Norwegian social security number.'),
  34
+    }
  35
+
30 36
     def clean(self, value):
31 37
         super(NOSocialSecurityNumber, self).clean(value)
32 38
         if value in EMPTY_VALUES:
33 39
             return u''
34 40
 
35  
-        msg = ugettext(u'Enter a valid Norwegian social security number.')
36 41
         if not re.match(r'^\d{11}$', value):