Code

Ticket #9289: django_localflavor_se_r6.diff

File django_localflavor_se_r6.diff, 23.1 KB (added by toxik, 6 years ago)

Marked strings for translation, because some languages have non-roman alphabets.

Line 
1Index: django/contrib/localflavor/se/se_counties.py
2===================================================================
3--- django/contrib/localflavor/se/se_counties.py        (revision 0)
4+++ django/contrib/localflavor/se/se_counties.py        (revision 0)
5@@ -0,0 +1,36 @@
6+# -*- coding: utf-8 -*-
7+"""
8+An alphabetical list of Swedish counties, sorted by codes.
9+
10+http://en.wikipedia.org/wiki/Counties_of_Sweden
11+
12+This exists in this standalone file so that it's only imported into memory
13+when explicitly needed.
14+
15+"""
16+
17+from django.utils.translation import ugettext_lazy as _
18+
19+COUNTY_CHOICES = (
20+    ('AB', _(u'Stockholm')),
21+    ('AC', _(u'Västerbotten')),
22+    ('BD', _(u'Norrbotten')),
23+    ('C', _(u'Uppsala')),
24+    ('D', _(u'Södermanland')),
25+    ('E', _(u'Östergötland')),
26+    ('F', _(u'Jönköping')),
27+    ('G', _(u'Kronoberg')),
28+    ('H', _(u'Kalmar')),
29+    ('I', _(u'Gotland')),
30+    ('K', _(u'Blekinge')),
31+    ('M', _(u'Skåne')),
32+    ('N', _(u'Halland')),
33+    ('O', _(u'Västra Götaland')),
34+    ('S', _(u'Värmland')),
35+    ('T', _(u'Örebro')),
36+    ('U', _(u'Västmanland')),
37+    ('W', _(u'Dalarna')),
38+    ('X', _(u'Gävleborg')),
39+    ('Y', _(u'Västernorrland')),
40+    ('Z', _(u'Jämtland')),
41+)
42Index: django/contrib/localflavor/se/utils.py
43===================================================================
44--- django/contrib/localflavor/se/utils.py      (revision 0)
45+++ django/contrib/localflavor/se/utils.py      (revision 0)
46@@ -0,0 +1,87 @@
47+import re
48+import datetime
49+
50+SWEDISH_ID_NUMBER = re.compile(r'^(?P<century>\d{2})?(?P<year>\d{2})(?P<month>\d{2})(?P<day>\d{2})(?P<sign>[\-+])?(?P<serial>\d{3})(?P<checksum>\d)$')
51+SE_POSTAL_CODE = re.compile(r'^[1-9]\d{2} ?\d{2}$')
52+
53+def id_number_checksum(gd):
54+    """
55+    Calculates a Swedish ID number checksum, using the
56+    "Luhn"-algoritm
57+    """
58+    n = s = 0
59+    for c in (gd['year'] + gd['month'] + gd['day'] + gd['serial']):
60+        tmp = ((n % 2) and 1 or 2) * int(c)
61+
62+        if tmp > 9:
63+            tmp = sum([int(i) for i in str(tmp)])
64+
65+        s += tmp
66+        n += 1
67+
68+    if (s % 10) == 0:
69+        return 0
70+
71+    return (((s / 10) + 1) * 10) - s
72+
73+def validate_id_birthday(gd, fix_coordination_number_day=True):
74+    """
75+    Validates the birth_day and returns the datetime.date object for
76+    the birth_day.
77+
78+    If the date is an invalid birth day, a ValueError will be raised.
79+    """
80+   
81+    today = datetime.date.today()
82+   
83+    day = int(gd['day'])
84+    if fix_coordination_number_day and day > 60:
85+        day -= 60
86+
87+    if gd['century'] is None:
88+
89+        # The century was not specified, and need to be calculated from todays date
90+        current_year = today.year
91+        year = int(today.strftime('%Y')) - int(today.strftime('%y')) + int(gd['year'])
92+   
93+        if ('%s%s%02d' % (gd['year'], gd['month'], day)) > today.strftime('%y%m%d'):
94+            year -= 100
95+
96+        # If the person is older than 100 years
97+        if gd['sign'] == '+':
98+            year -= 100
99+    else:
100+        year = int(gd['century'] + gd['year'])
101+       
102+        # Make sure the year is valid
103+        # There are no swedish personal identity numbers where year < 1800
104+        if year < 1800:
105+            raise ValueError
106+
107+    # ValueError will be raise for invalid dates
108+    birth_day = datetime.date(year, int(gd['month']), day)
109+   
110+    # birth_day must not be in the future
111+    if birth_day > today:
112+        raise ValueError
113+   
114+    return birth_day
115+
116+def format_personal_id_number(birth_day, gd):
117+    # birth_day.strftime cannot be used, since it does not support dates < 1900
118+    return unicode(str(birth_day.year) + gd['month'] + gd['day'] + gd['serial'] + gd['checksum'])
119+
120+def format_organisation_number(gd):
121+    if gd['century'] is None:
122+        century = ''
123+    else:
124+        century = gd['century']
125+
126+    return unicode(century + gd['year'] + gd['month'] + gd['day'] + gd['serial'] + gd['checksum'])
127+
128+def valid_organisation(gd):
129+    return gd['century'] in (None, 16) and \
130+        int(gd['month']) >= 20 and \
131+        gd['sign'] in (None, '-') and \
132+        gd['year'][0] in ('2', '5', '7', '8', '9') # group identifier
133+
134Index: django/contrib/localflavor/se/forms.py
135===================================================================
136--- django/contrib/localflavor/se/forms.py      (revision 0)
137+++ django/contrib/localflavor/se/forms.py      (revision 0)
138@@ -0,0 +1,152 @@
139+# -*- coding: utf-8 -*-
140+"""
141+Swedish specific Form helpers
142+"""
143+
144+from django import forms
145+from django.utils.translation import ugettext_lazy as _
146+from django.forms.fields import EMPTY_VALUES
147+
148+from utils import *
149+
150+__all__ = ('SECountySelect', 'SEOrganisationNumberField', 'SEPersonalIdentityNumberField', 'SEPostalCodeField')
151+
152+class SECountySelect(forms.Select):
153+    """
154+    A Select form widget that uses a list of the Swedish counties (län) as its
155+    choices.
156+
157+    The cleaned value is the official county code -- see
158+    http://en.wikipedia.org/wiki/Counties_of_Sweden for a list.
159+    """
160+
161+    def __init__(self, attrs=None):
162+        from se_counties import COUNTY_CHOICES
163+        super(SECountySelect, self).__init__(attrs=attrs,
164+                                             choices=COUNTY_CHOICES)
165+
166+class SEOrganisationNumberField(forms.CharField):
167+    """
168+    A form field that validates input as a Swedish organisation number
169+    (organisationsnummer).
170+
171+    It accepts the same input as SEPersonalIdentityField (for sole
172+    proprietorships (enskild firma). However, co-ordination numbers are not
173+    accepted.
174+
175+    It also accepts ordinary Swedish organisation numbers with the format
176+    NNNNNNNNNN.
177+
178+    The return value will be YYYYMMDDXXXX for sole proprietors, and NNNNNNNNNN
179+    for other organisations.
180+    """
181+
182+    default_error_messages = {
183+        'invalid': _('Enter a valid Swedish organisation number.'),
184+    }
185+
186+    def clean(self, value):
187+        value = super(SEOrganisationNumberField, self).clean(value)
188+       
189+        if value in EMPTY_VALUES:
190+            return u''
191+       
192+        match = SWEDISH_ID_NUMBER.match(value)
193+        if not match:
194+            raise forms.ValidationError(self.error_messages['invalid'])
195+
196+        gd = match.groupdict()
197+       
198+        # Compare the calculated value with the checksum
199+        if id_number_checksum(gd) != int(gd['checksum']):
200+            raise forms.ValidationError(self.error_messages['invalid'])
201+       
202+        # First: check if this is a real organisation_number
203+        if valid_organisation(gd):
204+            return format_organisation_number(gd)
205+
206+        # Is this a single properitor (enskild firma)?
207+        try:
208+            birth_day = validate_id_birthday(gd, False)
209+            return format_personal_id_number(birth_day, gd)
210+        except ValueError:
211+            raise forms.ValidationError(self.error_messages['invalid'])
212+
213+
214+class SEPersonalIdentityNumberField(forms.CharField):
215+    """
216+    A form field that validates input as a Swedish personal identity number
217+    (personnummer).
218+
219+    The correct formats are YYYYMMDD-XXXX, YYYYMMDDXXXX, YYMMDD-XXXX,
220+    YYMMDDXXXX and YYMMDD+XXXX.
221+
222+    A + indicates that the person is older than 100 years, which will be taken
223+    into consideration when the date is validated.
224+   
225+    The checksum will be calculated and checked. The birth date is checked to
226+    be a valid date.
227+
228+    By default, co-ordination numbers (samordningsnummer) will be accepted. To
229+    only allow real personal identity numbers, pass the keyword argument
230+    coordination_number=False to the constructor.
231+
232+    The cleaned value will always have the format YYYYMMDDXXXX.
233+    """
234+
235+    def __init__(self, coordination_number=True, *args, **kwargs):
236+        self.coordination_number = coordination_number
237+        super(SEPersonalIdentityNumberField, self).__init__(*args, **kwargs)
238+
239+    default_error_messages = {
240+        'invalid': _('Enter a valid Swedish personal identity number.'),
241+        'coordination_number': _('Co-ordination numbers are not allowed.'),
242+    }
243+
244+    def clean(self, value):
245+        value = super(SEPersonalIdentityNumberField, self).clean(value)
246+
247+        if value in EMPTY_VALUES:
248+            return u''
249+
250+        match = SWEDISH_ID_NUMBER.match(value)
251+        if match is None:
252+            raise forms.ValidationError(self.error_messages['invalid'])
253+
254+        gd = match.groupdict()
255+
256+        # compare the calculated value with the checksum
257+        if id_number_checksum(gd) != int(gd['checksum']):
258+            raise forms.ValidationError(self.error_messages['invalid'])
259+
260+        # check for valid birthday
261+        try:
262+            birth_day = validate_id_birthday(gd)
263+        except ValueError:
264+            raise forms.ValidationError(self.error_messages['invalid'])
265+
266+        # make sure that co-ordination numbers do not pass if not allowed
267+        if not self.coordination_number and int(gd['day']) > 60:
268+            raise forms.ValidationError(self.error_messages['coordination_number'])
269
270+        return format_personal_id_number(birth_day, gd)
271+
272+
273+class SEPostalCodeField(forms.RegexField):
274+    """
275+    A form field that validates input as a Swedish postal code (postnummer).
276+    Valid codes consist of five digits (XXXXX). The number can optionally be
277+    formatted with a space after the third digit (XXX XX).
278+
279+    The cleaned value will never contain the space.
280+    """
281+
282+    default_error_messages = {
283+        'invalid': _('Enter a Swedish postal code in the format XXXXX.'),
284+    }
285+
286+    def __init__(self, *args, **kwargs):
287+        super(SEPostalCodeField, self).__init__(SE_POSTAL_CODE, *args, **kwargs)
288+
289+    def clean(self, value):
290+        return super(SEPostalCodeField, self).clean(value).replace(' ', '')
291Index: tests/regressiontests/forms/localflavor/se.py
292===================================================================
293--- tests/regressiontests/forms/localflavor/se.py       (revision 0)
294+++ tests/regressiontests/forms/localflavor/se.py       (revision 0)
295@@ -0,0 +1,332 @@
296+# -*- coding: utf-8 -*-
297+# Tests for the contrib/localflavor/se form fields.
298+
299+tests = r"""
300+# Monkey-patch datetime.date
301+>>> import datetime
302+>>> class MockDate(datetime.date):
303+...     def today(cls):
304+...         return datetime.date(2008, 5, 14)
305+...     today = classmethod(today)
306+...
307+>>> olddate = datetime.date
308+>>> datetime.date = MockDate
309+>>> datetime.date.today()
310+MockDate(2008, 5, 14)
311+
312+
313+# SECountySelect #####################################################
314+>>> from django.contrib.localflavor.se.forms import SECountySelect
315+
316+>>> w = SECountySelect()
317+>>> w.render('swedish_county', 'E')
318+u'<select name="swedish_county">\n<option value="AB">Stockholm</option>\n<option value="AC">V\xe4sterbotten</option>\n<option value="BD">Norrbotten</option>\n<option value="C">Uppsala</option>\n<option value="D">S\xf6dermanland</option>\n<option value="E" selected="selected">\xd6sterg\xf6tland</option>\n<option value="F">J\xf6nk\xf6ping</option>\n<option value="G">Kronoberg</option>\n<option value="H">Kalmar</option>\n<option value="I">Gotland</option>\n<option value="K">Blekinge</option>\n<option value="M">Sk\xe5ne</option>\n<option value="N">Halland</option>\n<option value="O">V\xe4stra G\xf6taland</option>\n<option value="S">V\xe4rmland</option>\n<option value="T">\xd6rebro</option>\n<option value="U">V\xe4stmanland</option>\n<option value="W">Dalarna</option>\n<option value="X">G\xe4vleborg</option>\n<option value="Y">V\xe4sternorrland</option>\n<option value="Z">J\xe4mtland</option>\n</select>'
319+
320+# SEOrganisationNumberField #######################################
321+
322+>>> from django.contrib.localflavor.se.forms import SEOrganisationNumberField
323+
324+>>> f = SEOrganisationNumberField()
325+
326+# Ordinary personal identity numbers for sole proprietors
327+# The same rules as for SEPersonalIdentityField applies here
328+>>> f.clean('870512-1989')
329+u'198705121989'
330+>>> f.clean('19870512-1989')
331+u'198705121989'
332+>>> f.clean('870512-2128')
333+u'198705122128'
334+>>> f.clean('081015-6315')
335+u'190810156315'
336+>>> f.clean('081015+6315')
337+u'180810156315'
338+>>> f.clean('0810156315')
339+u'190810156315'
340+
341+>>> f.clean('081015 6315')
342+Traceback (most recent call last):
343+...
344+ValidationError: [u'Enter a valid Swedish organisation number.']
345+>>> f.clean('950231-4496')
346+Traceback (most recent call last):
347+...
348+ValidationError: [u'Enter a valid Swedish organisation number.']
349+>>> f.clean('6914104499')
350+Traceback (most recent call last):
351+...
352+ValidationError: [u'Enter a valid Swedish organisation number.']
353+>>> f.clean('950d314496')
354+Traceback (most recent call last):
355+...
356+ValidationError: [u'Enter a valid Swedish organisation number.']
357+>>> f.clean('invalid!!!')
358+Traceback (most recent call last):
359+...
360+ValidationError: [u'Enter a valid Swedish organisation number.']
361+>>> f.clean('870514-1111')
362+Traceback (most recent call last):
363+...
364+ValidationError: [u'Enter a valid Swedish organisation number.']
365+
366+
367+# Empty values
368+>>> f.clean('')
369+Traceback (most recent call last):
370+...
371+ValidationError: [u'This field is required.']
372+
373+>>> f.clean(None)
374+Traceback (most recent call last):
375+...
376+ValidationError: [u'This field is required.']
377+
378+# Co-ordination number checking
379+# Co-ordination numbers are not valid organisation numbers
380+>>> f.clean('870574-1315')
381+Traceback (most recent call last):
382+...
383+ValidationError: [u'Enter a valid Swedish organisation number.']
384+
385+>>> f.clean('870573-1311')
386+Traceback (most recent call last):
387+...
388+ValidationError: [u'Enter a valid Swedish organisation number.']
389+
390+# Test some different organisation numbers
391+>>> f.clean('556074-7569') # IKEA Linköping
392+u'5560747569'
393+
394+>>> f.clean('556074-3089') # Volvo Personvagnar
395+u'5560743089'
396+
397+>>> f.clean('822001-5476') # LJS (organisation)
398+u'8220015476'
399+
400+>>> f.clean('8220015476') # LJS (organisation)
401+u'8220015476'
402+
403+>>> f.clean('2120000449') # Katedralskolan Linköping (school)
404+u'2120000449'
405+
406+# Faux organisation number, which tests that the checksum can be 0
407+>>> f.clean('232518-5060')
408+u'2325185060'
409+
410+>>> f.clean('556074+3089') # Volvo Personvagnar, bad format
411+Traceback (most recent call last):
412+...
413+ValidationError: [u'Enter a valid Swedish organisation number.']
414+
415+
416+# Invalid checksum
417+>>> f.clean('2120000441')
418+Traceback (most recent call last):
419+...
420+ValidationError: [u'Enter a valid Swedish organisation number.']
421+
422+# Valid checksum but invalid organisation type
423+f.clean('1120000441')
424+Traceback (most recent call last):
425+...
426+ValidationError: [u'Enter a valid Swedish organisation number.']
427+
428+# Empty values with required=False
429+>>> f = SEOrganisationNumberField(required=False)
430+
431+>>> f.clean(None)
432+u''
433+
434+>>> f.clean('')
435+u''
436+
437+
438+# SEPersonalIdentityNumberField #######################################
439+
440+>>> from django.contrib.localflavor.se.forms import SEPersonalIdentityNumberField
441+
442+>>> f = SEPersonalIdentityNumberField()
443+
444+# Valid id numbers
445+>>> f.clean('870512-1989')
446+u'198705121989'
447+
448+>>> f.clean('870512-2128')
449+u'198705122128'
450+
451+>>> f.clean('19870512-1989')
452+u'198705121989'
453+
454+>>> f.clean('198705121989')
455+u'198705121989'
456+
457+>>> f.clean('081015-6315')
458+u'190810156315'
459+
460+>>> f.clean('0810156315')
461+u'190810156315'
462+
463+# This is a "special-case" in the checksum calculation,
464+# where the sum is divisible by 10 (the checksum digit == 0)
465+>>> f.clean('8705141060')
466+u'198705141060'
467+
468+# + means that the person is older than 100 years
469+>>> f.clean('081015+6315')
470+u'180810156315'
471+
472+# Bogus values
473+>>> f.clean('081015 6315')
474+Traceback (most recent call last):
475+...
476+ValidationError: [u'Enter a valid Swedish personal identity number.']
477+
478+>>> f.clean('950d314496')
479+Traceback (most recent call last):
480+...
481+ValidationError: [u'Enter a valid Swedish personal identity number.']
482+
483+>>> f.clean('invalid!!!')
484+Traceback (most recent call last):
485+...
486+ValidationError: [u'Enter a valid Swedish personal identity number.']
487+
488+
489+# Invalid dates
490+
491+# February 31st does not exist
492+>>> f.clean('950231-4496')
493+Traceback (most recent call last):
494+...
495+ValidationError: [u'Enter a valid Swedish personal identity number.']
496+
497+# Month 14 does not exist
498+>>> f.clean('6914104499')
499+Traceback (most recent call last):
500+...
501+ValidationError: [u'Enter a valid Swedish personal identity number.']
502+
503+# There are no Swedish personal id numbers where year < 1800
504+>>> f.clean('17430309-7135')
505+Traceback (most recent call last):
506+...
507+ValidationError: [u'Enter a valid Swedish personal identity number.']
508+
509+# Invalid checksum
510+>>> f.clean('870514-1111')
511+Traceback (most recent call last):
512+...
513+ValidationError: [u'Enter a valid Swedish personal identity number.']
514+
515+# Empty values
516+>>> f.clean('')
517+Traceback (most recent call last):
518+...
519+ValidationError: [u'This field is required.']
520+
521+>>> f.clean(None)
522+Traceback (most recent call last):
523+...
524+ValidationError: [u'This field is required.']
525+
526+# Co-ordination number checking
527+>>> f.clean('870574-1315')
528+u'198705741315'
529+
530+>>> f.clean('870574+1315')
531+u'188705741315'
532+
533+>>> f.clean('198705741315')
534+u'198705741315'
535+
536+# Co-ordination number with bad checksum
537+>>> f.clean('870573-1311')
538+Traceback (most recent call last):
539+...
540+ValidationError: [u'Enter a valid Swedish personal identity number.']
541+
542+
543+# Check valid co-ordination numbers, that should not be accepted
544+# because of coordination_number=False
545+>>> f = SEPersonalIdentityNumberField(coordination_number=False)
546+
547+>>> f.clean('870574-1315')
548+Traceback (most recent call last):
549+...
550+ValidationError: [u'Co-ordination numbers are not allowed.']
551+
552+>>> f.clean('870574+1315')
553+Traceback (most recent call last):
554+...
555+ValidationError: [u'Co-ordination numbers are not allowed.']
556+
557+>>> f.clean('8705741315')
558+Traceback (most recent call last):
559+...
560+ValidationError: [u'Co-ordination numbers are not allowed.']
561+
562+# Invalid co-ordination numbers should be treated as invalid, and not
563+# as co-ordination numbers
564+>>> f.clean('870573-1311')
565+Traceback (most recent call last):
566+...
567+ValidationError: [u'Enter a valid Swedish personal identity number.']
568+
569+# Empty values with required=False
570+>>> f = SEPersonalIdentityNumberField(required=False)
571+
572+>>> f.clean(None)
573+u''
574+
575+>>> f.clean('')
576+u''
577+
578+# SEPostalCodeField ###############################################
579+>>> from django.contrib.localflavor.se.forms import SEPostalCodeField
580+>>> f = SEPostalCodeField()
581+>>>
582+Postal codes can have spaces
583+>>> f.clean('589 37')
584+u'58937'
585+
586+... but the dont have to
587+>>> f.clean('58937')
588+u'58937'
589+>>> f.clean('abcasfassadf')
590+Traceback (most recent call last):
591+...
592+ValidationError: [u'Enter a Swedish postal code in the format XXXXX.']
593+
594+# Only one space is allowed for separation
595+>>> f.clean('589  37')
596+Traceback (most recent call last):
597+...
598+ValidationError: [u'Enter a Swedish postal code in the format XXXXX.']
599+
600+# The postal code must not start with 0
601+>>> f.clean('01234')
602+Traceback (most recent call last):
603+...
604+ValidationError: [u'Enter a Swedish postal code in the format XXXXX.']
605+
606+# Empty values
607+>>> f.clean('')
608+Traceback (most recent call last):
609+...
610+ValidationError: [u'This field is required.']
611+
612+>>> f.clean(None)
613+Traceback (most recent call last):
614+...
615+ValidationError: [u'This field is required.']
616+
617+# Empty values, required=False
618+>>> f = SEPostalCodeField(required=False)
619+>>> f.clean('')
620+u''
621+>>> f.clean(None)
622+u''
623+
624+# Revert the monkey patching
625+>>> datetime.date = olddate
626+
627+"""
628Index: tests/regressiontests/forms/tests.py
629===================================================================
630--- tests/regressiontests/forms/tests.py        (revision 9230)
631+++ tests/regressiontests/forms/tests.py        (working copy)
632@@ -21,6 +21,7 @@
633 from localflavor.nl import tests as localflavor_nl_tests
634 from localflavor.pl import tests as localflavor_pl_tests
635 from localflavor.ro import tests as localflavor_ro_tests
636+from localflavor.se import tests as localflavor_se_tests
637 from localflavor.sk import tests as localflavor_sk_tests
638 from localflavor.uk import tests as localflavor_uk_tests
639 from localflavor.us import tests as localflavor_us_tests
640@@ -54,6 +55,7 @@
641     'localflavor_nl_tests': localflavor_nl_tests,
642     'localflavor_pl_tests': localflavor_pl_tests,
643     'localflavor_ro_tests': localflavor_ro_tests,
644+    'localflavor_se_tests': localflavor_se_tests,
645     'localflavor_sk_tests': localflavor_sk_tests,
646     'localflavor_uk_tests': localflavor_uk_tests,
647     'localflavor_us_tests': localflavor_us_tests,
648Index: AUTHORS
649===================================================================
650--- AUTHORS     (revision 9230)
651+++ AUTHORS     (working copy)
652@@ -300,6 +300,7 @@
653     Carlos Eduardo de Paula <carlosedp@gmail.com>
654     pavithran s <pavithran.s@gmail.com>
655     Barry Pederson <bp@barryp.org>
656+    Andreas Pelme <andreas@pelme.se>
657     permonik@mesias.brnonet.cz
658     peter@mymart.com
659     pgross@thoughtworks.com
660Index: docs/ref/contrib/localflavor.txt
661===================================================================
662--- docs/ref/contrib/localflavor.txt    (revision 9230)
663+++ docs/ref/contrib/localflavor.txt    (working copy)
664@@ -60,6 +60,7 @@
665     * Slovakia_
666     * `South Africa`_
667     * Spain_
668+    * Sweden_
669     * Switzerland_
670     * `United Kingdom`_
671     * `United States of America`_
672@@ -99,6 +100,7 @@
673 .. _Slovakia: `Slovakia (sk)`_
674 .. _South Africa: `South Africa (za)`_
675 .. _Spain: `Spain (es)`_
676+.. _Sweden: `Sweden (se)`_
677 .. _Switzerland: `Switzerland (ch)`_
678 .. _United Kingdom: `United Kingdom (uk)`_
679 .. _United States of America: `United States of America (us)`_
680@@ -573,6 +575,60 @@
681 
682     A ``Select`` widget that uses a list of Spanish regions as its choices.
683 
684+Sweden (``se``)
685+===============
686+
687+.. class:: se.forms.SECountySelect
688+
689+    A Select form widget that uses a list of the Swedish counties (län) as its
690+    choices.
691+
692+    The cleaned value is the official county code -- see
693+    http://en.wikipedia.org/wiki/Counties_of_Sweden for a list.
694+
695+.. class:: se.forms.SEOrganisationNumber
696+
697+    A form field that validates input as a Swedish organisation number
698+    (organisationsnummer).
699+
700+    It accepts the same input as SEPersonalIdentityField (for sole
701+    proprietorships (enskild firma). However, co-ordination numbers are not
702+    accepted.
703+
704+    It also accepts ordinary Swedish organisation numbers with the format
705+    NNNNNNNNNN.
706+
707+    The return value will be YYYYMMDDXXXX for sole proprietors, and NNNNNNNNNN
708+    for other organisations.
709+
710+.. class:: se.forms.SEPersonalIdentityNumber
711+
712+    A form field that validates input as a Swedish personal identity number
713+    (personnummer).
714+
715+    The correct formats are YYYYMMDD-XXXX, YYYYMMDDXXXX, YYMMDD-XXXX,
716+    YYMMDDXXXX and YYMMDD+XXXX.
717+
718+    A \+ indicates that the person is older than 100 years, which will be taken
719+    into consideration when the date is validated.
720+   
721+    The checksum will be calculated and checked. The birth date is checked
722+    to be a valid date.
723+
724+    By default, co-ordination numbers (samordningsnummer) will be accepted. To
725+    only allow real personal identity numbers, pass the keyword argument
726+    coordination_number=False to the constructor.
727+
728+    The cleaned value will always have the format YYYYMMDDXXXX.
729+
730+.. class:: se.forms.SEPostalCodeField
731+
732+    A form field that validates input as a Swedish postal code (postnummer).
733+    Valid codes consist of five digits (XXXXX). The number can optionally be
734+    formatted with a space after the third digit (XXX XX).
735+
736+    The cleaned value will never contain the space.
737+
738 Switzerland (``ch``)
739 ====================
740