diff options
| author | Jannis Leidel <jannis@leidel.info> | 2011-04-22 12:02:38 +0000 |
|---|---|---|
| committer | Jannis Leidel <jannis@leidel.info> | 2011-04-22 12:02:38 +0000 |
| commit | 8b588747ed302fcdef0b79171ed3cf129cd32a38 (patch) | |
| tree | fbc885f1508079bdc643e5fb1a37750279bd26f1 /django | |
| parent | 6c17190bf8c6c23f21dd49b029106dd5cb41ae2e (diff) | |
Fixed #12379 -- Added Chinese (cn) localflavor package. Thanks, Xia Kai, Daniel Duan, DaNmarner and Łukasz Rekucki.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@16070 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
| -rw-r--r-- | django/contrib/localflavor/cn/__init__.py | 0 | ||||
| -rw-r--r-- | django/contrib/localflavor/cn/cn_provinces.py | 49 | ||||
| -rw-r--r-- | django/contrib/localflavor/cn/forms.py | 212 |
3 files changed, 261 insertions, 0 deletions
diff --git a/django/contrib/localflavor/cn/__init__.py b/django/contrib/localflavor/cn/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/django/contrib/localflavor/cn/__init__.py diff --git a/django/contrib/localflavor/cn/cn_provinces.py b/django/contrib/localflavor/cn/cn_provinces.py new file mode 100644 index 0000000000..fe0aa37cb6 --- /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 0000000000..3d8d45c53e --- /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) |
