Index: django/contrib/localflavor/kr/kr_city_and_province.py
===================================================================
--- django/contrib/localflavor/kr/kr_city_and_province.py	(revision 0)
+++ django/contrib/localflavor/kr/kr_city_and_province.py	(revision 0)
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*
+
+CITY_AND_PROVINCE_CHOICES = (
+    ('서울', '서울특별시'),
+    ('부산', '부산광역시'),
+    ('대구', '대구광역시'),
+    ('인천', '인천광역시'),
+    ('광주', '광주광역시'),
+    ('대전', '대전광역시'),
+    ('울산', '울산광역시'),
+    ('경기', '경기도'),
+    ('강원', '강원도'),
+    ('충북', '충청북도'),
+    ('충남', '충청남도'),
+    ('전북', '전라북도'),
+    ('전남', '전라남도'),
+    ('경북', '경상북도'),
+    ('경남', '경상남도'),
+    ('제주', '제주도'),
+)
Index: django/contrib/localflavor/kr/__init__.py
===================================================================
Index: django/contrib/localflavor/kr/forms.py
===================================================================
--- django/contrib/localflavor/kr/forms.py	(revision 0)
+++ django/contrib/localflavor/kr/forms.py	(revision 0)
@@ -0,0 +1,104 @@
+"""
+Korea-specific Form helpers
+"""
+
+from django.core.validators import EMPTY_VALUES
+from django.forms import ValidationError
+from django.forms.fields import Field, RegexField, Select, CharField
+from django.utils.encoding import smart_unicode
+from django.utils.translation import ugettext_lazy as _
+import time
+import re
+
+phone_digits_re = re.compile(r'^(0\d{1,2})[-\.]?(\d{3,4})[-\.]?(\d{4})$')
+
+class KRZipCodeField(RegexField):
+    default_error_messages = {
+        'invalid': _('Enter a zip code in the format XXXXXX or XXX-XXX.'),
+    }
+
+    def __init__(self, *args, **kwargs):
+        super(KRZipCodeField, self).__init__(r'^\d{3}-?\d{3}$',
+            max_length=None, min_length=None, *args, **kwargs)
+
+class KRPhoneNumberField(CharField):
+    """
+    Korea Phone Number
+    refer http://ko.wikipedia.org/wiki/%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD%EC%9D%98_%EC%A0%84%ED%99%94%EB%B2%88%ED%98%B8_%EC%B2%B4%EA%B3%84
+
+    valid examples)
+        phone)
+        02-000-0000 or 02000000
+        055-000-0000 or 0550000000
+        mobile)
+        010-0000-0000 or 01000000000
+        011-000-0000 or 0100000000
+    """
+    default_error_messages = {
+        'invalid': _('Phone numbers must be in 0XX-XXXX-XXXX format.'),
+    }
+
+    def clean(self, value):
+        super(KRPhoneNumberField, self).clean(value)
+        if value in EMPTY_VALUES:
+            return u''
+        value = re.sub('(\(|\)|\s+)', '', smart_unicode(value))
+        m = phone_digits_re.search(value)
+        if m:
+            return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
+        raise ValidationError(self.error_messages['invalid'])
+
+rrn_re = re.compile(r"^(?P<year>\d{2})(?P<month>\d{2})(?P<day>\d{2})[-\ ]?(?P<sex>[012349])(?P<others>\d{5})(?P<checksum>\d{1})$")
+class KRResidentRegistrationNumberField(Field):
+    """
+    A Korea Resident Registration Number
+
+    refer http://ko.wikipedia.org/wiki/%EC%A3%BC%EB%AF%BC%EB%93%B1%EB%A1%9D%EB%B2%88%ED%98%B8
+
+    Checks the following rules to determine whether the number is valid:
+
+        * Conforms to the %y%m%d-XXXXXXX format.
+        * First two digit means birth year without century as a decimal number [00,99]
+        * Second two digit means birth month as a decimal number [01,12].
+        * Third two digit means birth day of the month as a decimal number [01,31].
+        * First digit of second part can be [012349] which was mixed with sex and century.
+        * the other parts are not certain.
+        * last digit is checksum which can be calcurated by algorithm
+        * these rules checks format valid only.
+    """
+    default_error_messages = {
+        'invalid': _('Enter a valid Korea Resident Registration Number in XXXXXX-XXXXXXX format.'),
+    }
+
+    def clean(self, value):
+        super(KRResidentRegistrationNumberField, self).clean(value)
+        if value in EMPTY_VALUES:
+            return u''
+        match = re.match(rrn_re, value)
+        if not match:
+            raise ValidationError(self.error_messages['invalid'])
+        year , month , day , sex , others , checksum = match.groups()
+
+        # First pass : year month day combinations
+        try:
+            birthday = time.strptime("%s%s%s" % ( year , month , day ) , "%y%m%d")
+        except ValueError:
+            raise ValidationError(self.error_messages['invalid'])
+
+        # Second pass: checksums
+        try:
+            sumsource = zip( map(int,list(match.group().replace("-",""))[:-1]) , [2,3,4,5,6,7,8,9,2,3,4,5] )
+            new_checksum = ( 11 - ( reduce(lambda x , y : x + y[0] * y[1] , sumsource , 0) % 11 ))%10
+            if checksum != new_checksum:
+                raise ValidationError(self.error_messages['invalid'])
+        except:
+            raise ValidationError(self.error_messages['invalid'])
+        return u'%s%s%s-%s%s%s' % ( year , month , day , sex , others , checksum )
+
+class KRSpecialCityAndProvinceSelect(Select):
+    """
+    A Select widget that uses a list of Korea Special City and provinces as its choices.
+    """
+    def __init__(self, attrs=None):
+        from kr_city_and_province import CITY_AND_PROVINCE_CHOICES
+        super(KRSpecialCityAndProvinceSelect, self).__init__(attrs, choices=CITY_AND_PROVINCE_CHOICES)
