diff --git a/AUTHORS b/AUTHORS
index a19e49b..4462c34 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -151,6 +151,7 @@ answer newbie questions, and generally made Django that much better:
     dne@mayonnaise.net
     dready <wil@mojipage.com>
     Maximillian Dornseif <md@hudora.de>
+    Daniel Duan <DaNmarner@gmail.com>
     Jeremy Dunck <http://dunck.us/>
     Andrew Durdin <adurdin@gmail.com>
     dusk@woofle.net
@@ -255,6 +256,7 @@ answer newbie questions, and generally made Django that much better:
     Michael Josephson <http://www.sdjournal.com/>
     jpellerin@gmail.com
     junzhang.jn@gmail.com
+    Xia Kai <http://blog.xiaket.org/>
     Antti Kaihola <http://djangopeople.net/akaihola/>
     Bahadır Kandemir <bahadir@pardus.org.tr>
     Karderio <karderio@gmail.com>
diff --git a/django/contrib/localflavor/cn/__init__.py b/django/contrib/localflavor/cn/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/django/contrib/localflavor/cn/cn_provinces.py b/django/contrib/localflavor/cn/cn_provinces.py
new file mode 100644
index 0000000..fe0aa37
--- /dev/null
+++ b/django/contrib/localflavor/cn/cn_provinces.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+
+"""
+An alphabetical list of provinces for use as `choices` in a formfield.
+
+Reference:
+http://en.wikipedia.org/wiki/ISO_3166-2:CN
+http://en.wikipedia.org/wiki/Province_%28China%29
+http://en.wikipedia.org/wiki/Direct-controlled_municipality
+http://en.wikipedia.org/wiki/Autonomous_regions_of_China
+"""
+
+
+CN_PROVINCE_CHOICES = (
+    ("anhui", u"安徽"),
+    ("beijing", u"北京"),
+    ("chongqing", u"重庆"),
+    ("fujian", u"福建"),
+    ("gansu", u"甘肃"),
+    ("guangdong", u"广东"),
+    ("guangxi", u"广西壮族自治区"),
+    ("guizhou", u"贵州"),
+    ("hainan", u"海南"),
+    ("hebei", u"河北"),
+    ("heilongjiang", u"黑龙江"),
+    ("henan", u"河南"),
+    ("hongkong", u"香港"),
+    ("hubei", u"湖北"),
+    ("hunan", u"湖南"),
+    ("jiangsu", u"江苏"),
+    ("jiangxi", u"江西"),
+    ("jilin", u"吉林"),
+    ("liaoning", u"辽宁"),
+    ("macao", u"澳门"),
+    ("neimongol", u"内蒙古自治区"),
+    ("ningxia", u"宁夏回族自治区"),
+    ("qinghai", u"青海"),
+    ("shaanxi", u"陕西"),
+    ("shandong", u"山东"),
+    ("shanghai", u"上海"),
+    ("shanxi", u"山西"),
+    ("sichuan", u"四川"),
+    ("taiwan", u"台湾"),
+    ("tianjin", u"天津"),
+    ("xinjiang", u"新疆维吾尔自治区"),
+    ("xizang", u"西藏自治区"),
+    ("yunnan", u"云南"),
+    ("zhejiang", u"浙江"),
+)
diff --git a/django/contrib/localflavor/cn/forms.py b/django/contrib/localflavor/cn/forms.py
new file mode 100644
index 0000000..9749349
--- /dev/null
+++ b/django/contrib/localflavor/cn/forms.py
@@ -0,0 +1,212 @@
+# -*- coding: utf-8 -*-
+
+"""
+Chinese-specific form helpers
+"""
+import re
+
+from django.forms import ValidationError
+from django.forms.fields import CharField, RegexField, Select
+from django.utils.translation import ugettext_lazy as _
+
+
+__all__ = (
+    'CNProvinceSelect',
+    'CNPostCodeField',
+    'CNIDCardField',
+    'CNPhoneNumberField',
+    'CNCellNumberField',
+)
+
+
+ID_CARD_RE = r'^\d{15}(\d{2}[0-9xX])?$'
+POST_CODE_RE = r'^\d{6}$'
+PHONE_RE = r'^\d{3,4}-\d{7,8}(-\d+)?$'
+CELL_RE = r'^1[358]\d{9}$'
+
+# Valid location code used in id card checking algorithm
+CN_LOCATION_CODES = (
+     11 ,       # Beijing
+     12 ,       # Tianjin
+     13 ,       # Hebei
+     14 ,       # Shanxi
+     15 ,       # Nei Mongol
+     21 ,       # Liaoning
+     22 ,       # Jilin
+     23 ,       # Heilongjiang
+     31 ,       # Shanghai
+     32 ,       # Jiangsu
+     33 ,       # Zhejiang
+     34 ,       # Anhui
+     35 ,       # Fujian
+     36 ,       # Jiangxi
+     37 ,       # Shandong
+     41 ,       # Henan
+     42 ,       # Hubei
+     43 ,       # Hunan
+     44 ,       # Guangdong
+     45 ,       # Guangxi
+     46 ,       # Hainan
+     50 ,       # Chongqing
+     51 ,       # Sichuan
+     52 ,       # Guizhou
+     53 ,       # Yunnan
+     54 ,       # Xizang
+     61 ,       # Shaanxi
+     62 ,       # Gansu
+     63 ,       # Qinghai
+     64 ,       # Ningxia
+     65 ,       # Xinjiang
+     71 ,       # Taiwan
+     81 ,       # Hong Kong
+     91 ,       # Macao
+)
+
+class CNProvinceSelect(Select):
+    """
+    A select widget with list of Chinese provinces as choices.
+    """
+    def __init__(self, attrs=None):
+        from cn_provinces import CN_PROVINCE_CHOICES
+        super(CNProvinceSelect, self).__init__(
+            attrs, choices=CN_PROVINCE_CHOICES,
+        )
+
+
+class CNPostCodeField(RegexField):
+    """
+    A form field that validates as Chinese post code.
+    Valid code is XXXXXX where X is digit.
+    """
+    default_error_messages = {
+        'invalid': _(u'Enter a post code in the format XXXXXX.'),
+    }
+
+    def __init__(self, *args, **kwargs):
+        super(CNPostCodeField, self).__init__(POST_CODE_RE, *args, **kwargs)
+
+
+class CNIDCardField(CharField):
+    """
+    A form field that validates as Chinese Identification Card Number.
+
+    This field would check the following restrictions:
+        * the length could only be 15 or 18.
+        * if the length is 18, the last digit could be x or X.
+        * has a valid checksum.(length 18 only)
+        * has a valid birthdate.
+        * has a valid location.
+
+    The checksum algorithm is described in GB11643-1999.
+    """
+    default_error_messages = {
+        'invalid': _(u'ID Card Number consists of 15 or 18 digits.'),
+        'checksum': _(u'Invalid ID Card Number: Wrong checksum'),
+        'birthday': _(u'Invalid ID Card Number: Wrong birthdate'),
+        'location': _(u'Invalid ID Card Number: Wrong location code'),
+    }
+
+    def __init__(self, max_length=18, min_length=15, *args, **kwargs):
+        super(CNIDCardField, self).__init__(max_length, min_length, *args,
+                                         **kwargs)
+
+    def clean(self, value):
+        """
+        Check whether the input is a valid ID Card Number.
+        """
+        # Check the length of the ID card number.
+        super(CNIDCardField, self).clean(value)
+        if not value:
+            return u""
+        # Check whether this ID card number has valid format
+        if not re.match(ID_CARD_RE, value):
+            raise ValidationError(self.error_messages['invalid'])
+        # Check the birthday of the ID card number.
+        if not self.has_valid_birthday(value):
+            raise ValidationError(self.error_messages['birthday'])
+        # Check the location of the ID card number.
+        if not self.has_valid_location(value):
+            raise ValidationError(self.error_messages['location'])
+        # Check the checksum of the ID card number.
+        value = value.upper()
+        if not self.has_valid_checksum(value):
+            raise ValidationError(self.error_messages['checksum'])
+        return u'%s' % value
+
+    def has_valid_birthday(self, value):
+        """
+        This function would grab the birthdate from the ID card number and test
+        whether it is a valid date.
+        """
+        from datetime import datetime
+        if len(value) == 15:
+            # 1st generation ID card
+            time_string = value[6:12]
+            format_string = "%y%m%d"
+        else:
+            # 2nd generation ID card
+            time_string = value[6:14]
+            format_string = "%Y%m%d"
+        try:
+            datetime.strptime(time_string, format_string)
+            return True
+        except ValueError:
+            # invalid date
+            return False
+
+    def has_valid_location(self, value):
+        """
+        This method checks if the first two digits in the ID Card are valid.
+        """
+        return int(value[:2]) in CN_LOCATION_CODES
+
+    def has_valid_checksum(self, value):
+        """
+        This method checks if the last letter/digit in value is valid
+        according to the algorithm the ID Card follows.
+        """
+        # If the length of the number is not 18, then the number is a 1st
+        # generation ID card number, and there is no checksum to be checked.
+        if len(value) != 18:
+            return True
+        checksum_index = sum(
+            map(
+                lambda a,b:a*(ord(b)-ord('0')),
+                (7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2),
+                value[:17],
+            ),
+        ) % 11
+        return '10X98765432'[checksum_index] == value[-1]
+
+
+class CNPhoneNumberField(RegexField):
+    """
+    A form field that validates as Chinese phone number
+    A valid phone number could be like:
+        010-55555555
+    Considering there might be extension phone numbers, so this could also be:
+        010-55555555-35
+    """
+    default_error_messages = {
+        'invalid': _(u'Enter a valid phone number.'),
+    }
+
+    def __init__(self, *args, **kwargs):
+        super(CNPhoneNumberField, self).__init__(PHONE_RE, *args, **kwargs)
+
+
+class CNCellNumberField(RegexField):
+    """
+    A form field that validates as Chinese cell number
+    A valid cell number could be like:
+        13012345678
+    We used a rough rule here, the first digit should be 1, the second could be
+    3, 5 and 8, the rest could be what so ever.
+    The length of the cell number should be 11.
+    """
+    default_error_messages = {
+        'invalid': _(u'Enter a valid cell number.'),
+    }
+
+    def __init__(self, *args, **kwargs):
+        super(CNCellNumberField, self).__init__(CELL_RE, *args, **kwargs)
diff --git a/docs/ref/contrib/localflavor.txt b/docs/ref/contrib/localflavor.txt
index f54341e..08773b8 100644
--- a/docs/ref/contrib/localflavor.txt
+++ b/docs/ref/contrib/localflavor.txt
@@ -43,6 +43,7 @@ Countries currently supported by :mod:`~django.contrib.localflavor` are:
     * Brazil_
     * Canada_
     * Chile_
+    * China_
     * Czech_
     * Finland_
     * France_
@@ -92,6 +93,7 @@ Here's an example of how to use them::
 .. _Brazil: `Brazil (br)`_
 .. _Canada: `Canada (ca)`_
 .. _Chile: `Chile (cl)`_
+.. _China: `China (cn)`_
 .. _Czech: `Czech (cz)`_
 .. _Finland: `Finland (fi)`_
 .. _France: `France (fr)`_
@@ -322,6 +324,35 @@ Chile (``cl``)
     A ``Select`` widget that uses a list of Chilean regions (Regiones) as its
     choices.
 
+China (``cn``)
+==============
+
+.. class:: cn.forms.CNProvinceSelect
+
+    A ``Select`` widget that uses a list of Chinese regions as its choices.
+
+.. class:: cn.forms.CNPostCodeField
+
+    A form field that validates input as a Chinese post code.
+    Valid formats are XXXXXX where X is digit.
+
+.. class:: cn.forms.CNIDCardField
+
+    A form field that validates input as a Chinese Identification Card Number.
+    Both 1st and 2nd generation ID Card Number are validated.
+
+.. class:: cn.forms.CNPhoneNumberField
+
+    A form field that validates input as a Chinese phone number.
+    Valid formats are 0XX-XXXXXXXX, composed of 3 or 4 digits of region code
+    and 7 or 8 digits of phone number.
+
+.. class:: cn.forms.CNCellNumberField
+
+    A form field that validates input as a Chinese mobile phone number.
+    Valid formats are like 1XXXXXXXXXX, where X is digit.
+    The second digit could only be 3, 5 and 8.
+
 Czech (``cz``)
 ==============
 
diff --git a/tests/regressiontests/forms/localflavor/cn.py b/tests/regressiontests/forms/localflavor/cn.py
new file mode 100644
index 0000000..6579d95
--- /dev/null
+++ b/tests/regressiontests/forms/localflavor/cn.py
@@ -0,0 +1,113 @@
+# Tests for contrib/localflavor/ CN Form Fields
+
+from django.contrib.localflavor.cn.forms import (CNProvinceSelect,
+        CNPostCodeField, CNIDCardField, CNPhoneNumberField, CNCellNumberField)
+from utils import LocalFlavorTestCase
+
+class CNLocalFlavorTests(LocalFlavorTestCase):
+    def test_CNProvinceSelect(self):
+        f = CNProvinceSelect()
+        correct_output = u'''<select name="provinces">
+<option value="anhui">\u5b89\u5fbd</option>
+<option value="beijing">\u5317\u4eac</option>
+<option value="chongqing">\u91cd\u5e86</option>
+<option value="fujian">\u798f\u5efa</option>
+<option value="gansu">\u7518\u8083</option>
+<option value="guangdong">\u5e7f\u4e1c</option>
+<option value="guangxi">\u5e7f\u897f\u58ee\u65cf\u81ea\u6cbb\u533a</option>
+<option value="guizhou">\u8d35\u5dde</option>
+<option value="hainan">\u6d77\u5357</option>
+<option value="hebei">\u6cb3\u5317</option>
+<option value="heilongjiang">\u9ed1\u9f99\u6c5f</option>
+<option value="henan">\u6cb3\u5357</option>
+<option value="hongkong">\u9999\u6e2f</option>
+<option value="hubei" selected="selected">\u6e56\u5317</option>
+<option value="hunan">\u6e56\u5357</option>
+<option value="jiangsu">\u6c5f\u82cf</option>
+<option value="jiangxi">\u6c5f\u897f</option>
+<option value="jilin">\u5409\u6797</option>
+<option value="liaoning">\u8fbd\u5b81</option>
+<option value="macao">\u6fb3\u95e8</option>
+<option value="neimongol">\u5185\u8499\u53e4\u81ea\u6cbb\u533a</option>
+<option value="ningxia">\u5b81\u590f\u56de\u65cf\u81ea\u6cbb\u533a</option>
+<option value="qinghai">\u9752\u6d77</option>
+<option value="shaanxi">\u9655\u897f</option>
+<option value="shandong">\u5c71\u4e1c</option>
+<option value="shanghai">\u4e0a\u6d77</option>
+<option value="shanxi">\u5c71\u897f</option>
+<option value="sichuan">\u56db\u5ddd</option>
+<option value="taiwan">\u53f0\u6e7e</option>
+<option value="tianjin">\u5929\u6d25</option>
+<option value="xinjiang">\u65b0\u7586\u7ef4\u543e\u5c14\u81ea\u6cbb\u533a</option>
+<option value="xizang">\u897f\u85cf\u81ea\u6cbb\u533a</option>
+<option value="yunnan">\u4e91\u5357</option>
+<option value="zhejiang">\u6d59\u6c5f</option>
+</select>'''
+        self.assertEqual(f.render('provinces', 'hubei'), correct_output)
+
+    def test_CNPostCodeField(self):
+        error_format = [u'Enter a post code in the format XXXXXX.']
+        valid = {
+                '091209': u'091209'
+        }
+        invalid = {
+                '09120': error_format,
+                '09120916': error_format
+        }
+        self.assertFieldOutput(CNPostCodeField, valid, invalid)
+
+    def test_CNIDCardField(self):
+        valid = {
+                # A valid 1st generation ID Card Number.
+                '110101491001001': u'110101491001001',
+                # A valid 2nd generation ID Card number.
+                '11010119491001001X': u'11010119491001001X',
+                # Another valid 2nd gen ID Number with a case change
+                '11010119491001001x': u'11010119491001001X'
+        }
+
+        wrong_format = [u'ID Card Number consists of 15 or 18 digits.']
+        wrong_location = [u'Invalid ID Card Number: Wrong location code']
+        wrong_bday = [u'Invalid ID Card Number: Wrong birthdate']
+        wrong_checksum = [u'Invalid ID Card Number: Wrong checksum']
+
+        invalid = {
+                'abcdefghijklmnop': wrong_format,
+                '1010101010101010': wrong_format,
+                '010101491001001' : wrong_location, # 1st gen, 01 is invalid
+                '110101491041001' : wrong_bday, # 1st gen. There wasn't day 41
+                '92010119491001001X': wrong_location, # 2nd gen, 92 is invalid
+                '91010119491301001X': wrong_bday, # 2nd gen, 19491301 is invalid date
+                '910101194910010014': wrong_checksum #2nd gen
+        }
+        self.assertFieldOutput(CNIDCardField, valid, invalid)
+
+    def test_CNPhoneNumberField(self):
+        error_format = [u'Enter a valid phone number.']
+        valid = {
+                '010-12345678': u'010-12345678',
+                '010-1234567': u'010-1234567',
+                '0101-12345678': u'0101-12345678',
+                '0101-1234567': u'0101-1234567',
+                '010-12345678-020':u'010-12345678-020'
+        }
+        invalid = {
+                '01x-12345678': error_format,
+                '12345678': error_format,
+                '01123-12345678': error_format,
+                '010-123456789': error_format,
+                '010-12345678-': error_format
+        }
+        self.assertFieldOutput(CNPhoneNumberField, valid, invalid)
+
+    def test_CNCellNumberField(self):
+        error_format = [u'Enter a valid cell number.']
+        valid = {
+                '13012345678': u'13012345678',
+        }
+        invalid = {
+                '130123456789': error_format,
+                '14012345678': error_format
+        }
+        self.assertFieldOutput(CNCellNumberField, valid, invalid)
+
diff --git a/tests/regressiontests/forms/localflavortests.py b/tests/regressiontests/forms/localflavortests.py
index 5ee1c32..94abed1 100644
--- a/tests/regressiontests/forms/localflavortests.py
+++ b/tests/regressiontests/forms/localflavortests.py
@@ -7,6 +7,7 @@ from localflavor.ca import CALocalFlavorTests
 from localflavor.ch import CHLocalFlavorTests
 from localflavor.cl import CLLocalFlavorTests
 from localflavor.cz import CZLocalFlavorTests
+from localflavor.cn import CNLocalFlavorTests
 from localflavor.de import DELocalFlavorTests
 from localflavor.es import ESLocalFlavorTests
 from localflavor.fi import FILocalFlavorTests
diff --git a/tests/regressiontests/forms/tests/__init__.py b/tests/regressiontests/forms/tests/__init__.py
index 2d96b2f..38671fa 100644
--- a/tests/regressiontests/forms/tests/__init__.py
+++ b/tests/regressiontests/forms/tests/__init__.py
@@ -14,13 +14,13 @@ from widgets import *
 from regressiontests.forms.localflavortests import (
     ARLocalFlavorTests, ATLocalFlavorTests, AULocalFlavorTests,
     BELocalFlavorTests, BRLocalFlavorTests, CALocalFlavorTests,
-    CHLocalFlavorTests, CLLocalFlavorTests, CZLocalFlavorTests,
-    DELocalFlavorTests, ESLocalFlavorTests, FILocalFlavorTests,
-    FRLocalFlavorTests, GenericLocalFlavorTests, IDLocalFlavorTests,
-    IELocalFlavorTests, ILLocalFlavorTests, ISLocalFlavorTests,
-    ITLocalFlavorTests, JPLocalFlavorTests, KWLocalFlavorTests,
-    NLLocalFlavorTests, PLLocalFlavorTests, PTLocalFlavorTests,
-    ROLocalFlavorTests, SELocalFlavorTests, SKLocalFlavorTests,
-    TRLocalFlavorTests, UKLocalFlavorTests, USLocalFlavorTests,
-    UYLocalFlavorTests, ZALocalFlavorTests
+    CHLocalFlavorTests, CLLocalFlavorTests, CNLocalFlavorTests,
+    CZLocalFlavorTests, DELocalFlavorTests, ESLocalFlavorTests,
+    FILocalFlavorTests, FRLocalFlavorTests, GenericLocalFlavorTests,
+    IDLocalFlavorTests, IELocalFlavorTests, ILLocalFlavorTests,
+    ISLocalFlavorTests, ITLocalFlavorTests, JPLocalFlavorTests,
+    KWLocalFlavorTests, NLLocalFlavorTests, PLLocalFlavorTests,
+    PTLocalFlavorTests, ROLocalFlavorTests, SELocalFlavorTests,
+    SKLocalFlavorTests, TRLocalFlavorTests, UKLocalFlavorTests,
+    USLocalFlavorTests, UYLocalFlavorTests, ZALocalFlavorTests
 )
