Fixed #14563 -- Added Turkish localflavor. Thanks to serkank for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14794 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2010-12-04 05:25:24 +00:00
parent 34a386378f
commit ae7213b593
9 changed files with 296 additions and 6 deletions

View File

@ -0,0 +1,91 @@
"""
TR-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 re
phone_digits_re = re.compile(r'^(\+90|0)? ?(([1-9]\d{2})|\([1-9]\d{2}\)) ?([2-9]\d{2} ?\d{2} ?\d{2})$')
class TRPostalCodeField(RegexField):
default_error_messages = {
'invalid': _(u'Enter a postal code in the format XXXXX.'),
}
def __init__(self, *args, **kwargs):
super(TRPostalCodeField, self).__init__(r'^\d{5}$',
max_length=5, min_length=5, *args, **kwargs)
def clean(self, value):
value = super(TRPostalCodeField, self).clean(value)
if value in EMPTY_VALUES:
return u''
if len(value) != 5:
raise ValidationError(self.error_messages['invalid'])
province_code = int(value[:2])
if province_code == 0 or province_code > 81:
raise ValidationError(self.error_messages['invalid'])
return value
class TRPhoneNumberField(CharField):
default_error_messages = {
'invalid': _(u'Phone numbers must be in 0XXX XXX XXXX format.'),
}
def clean(self, value):
super(TRPhoneNumberField, 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' % (m.group(2), m.group(4))
raise ValidationError(self.error_messages['invalid'])
class TRIdentificationNumberField(Field):
"""
A Turkey Identification Number number.
See: http://tr.wikipedia.org/wiki/T%C3%BCrkiye_Cumhuriyeti_Kimlik_Numaras%C4%B1
Checks the following rules to determine whether the number is valid:
* The number is 11-digits.
* First digit is not 0.
* Conforms to the following two formula:
(sum(1st, 3rd, 5th, 7th, 9th)*7 - sum(2nd,4th,6th,8th)) % 10 = 10th digit
sum(1st to 10th) % 10 = 11th digit
"""
default_error_messages = {
'invalid': _(u'Enter a valid Turkish Identification number.'),
'not_11': _(u'Turkish Identification number must be 11 digits.'),
}
def clean(self, value):
super(TRIdentificationNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
if len(value) != 11:
raise ValidationError(self.error_messages['not_11'])
if not re.match(r'^\d{11}$', value):
raise ValidationError(self.error_messages['invalid'])
if int(value[0]) == 0:
raise ValidationError(self.error_messages['invalid'])
chksum = (sum([int(value[i]) for i in xrange(0,9,2)])*7-
sum([int(value[i]) for i in xrange(1,9,2)])) % 10
if chksum != int(value[9]) or \
(sum([int(value[i]) for i in xrange(10)]) % 10) != int(value[10]):
raise ValidationError(self.error_messages['invalid'])
return value
class TRProvinceSelect(Select):
"""
A Select widget that uses a list of provinces in Turkey as its choices.
"""
def __init__(self, attrs=None):
from tr_provinces import PROVINCE_CHOICES
super(TRProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)

View File

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
"""
This exists in this standalone file so that it's only imported into memory
when explicitly needed.
"""
PROVINCE_CHOICES = (
('01', ('Adana')),
('02', ('Adıyaman')),
('03', ('Afyonkarahisar')),
('04', ('Ağrı')),
('68', ('Aksaray')),
('05', ('Amasya')),
('06', ('Ankara')),
('07', ('Antalya')),
('75', ('Ardahan')),
('08', ('Artvin')),
('09', ('Aydın')),
('10', ('Balıkesir')),
('74', ('Bartın')),
('72', ('Batman')),
('69', ('Bayburt')),
('11', ('Bilecik')),
('12', ('Bingöl')),
('13', ('Bitlis')),
('14', ('Bolu')),
('15', ('Burdur')),
('16', ('Bursa')),
('17', ('Çanakkale')),
('18', ('Çankırı')),
('19', ('Çorum')),
('20', ('Denizli')),
('21', ('Diyarbakır')),
('81', ('Düzce')),
('22', ('Edirne')),
('23', ('Elazığ')),
('24', ('Erzincan')),
('25', ('Erzurum')),
('26', ('Eskişehir')),
('27', ('Gaziantep')),
('28', ('Giresun')),
('29', ('Gümüşhane')),
('30', ('Hakkari')),
('31', ('Hatay')),
('76', ('Iğdır')),
('32', ('Isparta')),
('33', ('Mersin')),
('34', ('İstanbul')),
('35', ('İzmir')),
('78', ('Karabük')),
('36', ('Kars')),
('37', ('Kastamonu')),
('38', ('Kayseri')),
('39', ('Kırklareli')),
('40', ('Kırşehir')),
('41', ('Kocaeli')),
('42', ('Konya')),
('43', ('Kütahya')),
('44', ('Malatya')),
('45', ('Manisa')),
('46', ('Kahramanmaraş')),
('70', ('Karaman')),
('71', ('Kırıkkale')),
('79', ('Kilis')),
('47', ('Mardin')),
('48', ('Muğla')),
('49', ('Muş')),
('50', ('Nevşehir')),
('51', ('Niğde')),
('52', ('Ordu')),
('80', ('Osmaniye')),
('53', ('Rize')),
('54', ('Sakarya')),
('55', ('Samsun')),
('56', ('Siirt')),
('57', ('Sinop')),
('58', ('Sivas')),
('73', ('Şırnak')),
('59', ('Tekirdağ')),
('60', ('Tokat')),
('61', ('Trabzon')),
('62', ('Tunceli')),
('63', ('Şanlıurfa')),
('64', ('Uşak')),
('65', ('Van')),
('77', ('Yalova')),
('66', ('Yozgat')),
('67', ('Zonguldak')),
)

View File

@ -67,6 +67,7 @@ Countries currently supported by :mod:`~django.contrib.localflavor` are:
* Spain_ * Spain_
* Sweden_ * Sweden_
* Switzerland_ * Switzerland_
* Turkey_
* `United Kingdom`_ * `United Kingdom`_
* `United States of America`_ * `United States of America`_
* Uruguay_ * Uruguay_
@ -115,6 +116,7 @@ Here's an example of how to use them::
.. _Spain: `Spain (es)`_ .. _Spain: `Spain (es)`_
.. _Sweden: `Sweden (se)`_ .. _Sweden: `Sweden (se)`_
.. _Switzerland: `Switzerland (ch)`_ .. _Switzerland: `Switzerland (ch)`_
.. _Turkey: `Turkey (tr)`_
.. _United Kingdom: `United Kingdom (uk)`_ .. _United Kingdom: `United Kingdom (uk)`_
.. _United States of America: `United States of America (us)`_ .. _United States of America: `United States of America (us)`_
.. _Uruguay: `Uruguay (uy)`_ .. _Uruguay: `Uruguay (uy)`_
@ -853,6 +855,35 @@ Switzerland (``ch``)
A ``Select`` widget that uses a list of Swiss states as its choices. A ``Select`` widget that uses a list of Swiss states as its choices.
Turkey (``tr``)
===============
.. class:: tr.forms.TRZipCodeField
A form field that validates input as a Turkish zip code. Valid codes
consist of five digits.
.. class:: tr.forms.TRPhoneNumberField
A form field that validates input as a Turkish phone number. The correct
format is 0xxx xxx xxxx. +90xxx xxx xxxx and inputs without spaces also
validates. The result is normalized to xxx xxx xxxx format.
.. class:: tr.forms.TRIdentificationNumberField
A form field that validates input as a TR identification number. A valid
number must satisfy the following:
* The number consist of 11 digits.
* The first digit cannot be 0.
* (sum(1st, 3rd, 5th, 7th, 9th)*7 - sum(2nd,4th,6th,8th)) % 10) must be
equal to the 10th digit.
* (sum(1st to 10th) % 10) must be equal to the 11th digit.
.. class:: tr.forms.TRProvinceSelect
A ``select`` widget that uses a list of Turkish provinces as its choices.
United Kingdom (``uk``) United Kingdom (``uk``)
======================= =======================

View File

@ -4,7 +4,7 @@ from django.forms import *
from django.contrib.localflavor.be.forms import (BEPostalCodeField, from django.contrib.localflavor.be.forms import (BEPostalCodeField,
BEPhoneNumberField, BERegionSelect, BEProvinceSelect) BEPhoneNumberField, BERegionSelect, BEProvinceSelect)
class BETests(TestCase): class BELocalFlavorTests(TestCase):
""" """
Test case to validate BE localflavor Test case to validate BE localflavor
""" """

View File

@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError
from django.utils.unittest import TestCase from django.utils.unittest import TestCase
class IsraelLocalFlavorTests(TestCase): class ILLocalFlavorTests(TestCase):
def test_postal_code_field(self): def test_postal_code_field(self):
f = ILPostalCodeField() f = ILPostalCodeField()
self.assertRaisesRegexp(ValidationError, self.assertRaisesRegexp(ValidationError,

View File

@ -0,0 +1,73 @@
# Tests for the contrib/localflavor/ TR form fields.
from django.contrib.localflavor.tr import forms as trforms
from django.core.exceptions import ValidationError
from django.utils.unittest import TestCase
class TRLocalFlavorTests(TestCase):
def test_TRPostalCodeField(self):
f = trforms.TRPostalCodeField()
self.assertEqual(f.clean("06531"), "06531")
self.assertEqual(f.clean("12345"), "12345")
self.assertRaisesRegexp(ValidationError,
"Enter a postal code in the format XXXXX.",
f.clean, "a1234")
self.assertRaisesRegexp(ValidationError,
"Enter a postal code in the format XXXXX.",
f.clean, "1234")
self.assertRaisesRegexp(ValidationError,
"Enter a postal code in the format XXXXX.",
f.clean, "82123")
self.assertRaisesRegexp(ValidationError,
"Enter a postal code in the format XXXXX.",
f.clean, "00123")
self.assertRaisesRegexp(ValidationError,
"Enter a postal code in the format XXXXX.",
f.clean, "123456")
self.assertRaisesRegexp(ValidationError,
"Enter a postal code in the format XXXXX.",
f.clean, "12 34")
self.assertRaises(ValidationError, f.clean, None)
def test_TRPhoneNumberField(self):
f = trforms.TRPhoneNumberField()
self.assertEqual(f.clean("312 455 56 78"), "3124555678")
self.assertEqual(f.clean("312 4555678"), "3124555678")
self.assertEqual(f.clean("3124555678"), "3124555678")
self.assertEqual(f.clean("0312 455 5678"), "3124555678")
self.assertEqual(f.clean("0 312 455 5678"), "3124555678")
self.assertEqual(f.clean("0 (312) 455 5678"), "3124555678")
self.assertEqual(f.clean("+90 312 455 4567"), "3124554567")
self.assertEqual(f.clean("+90 312 455 45 67"), "3124554567")
self.assertEqual(f.clean("+90 (312) 4554567"), "3124554567")
self.assertRaisesRegexp(ValidationError,
'Phone numbers must be in 0XXX XXX XXXX format.',
f.clean, "1234 233 1234")
self.assertRaisesRegexp(ValidationError,
'Phone numbers must be in 0XXX XXX XXXX format.',
f.clean, "0312 233 12345")
self.assertRaisesRegexp(ValidationError,
'Phone numbers must be in 0XXX XXX XXXX format.',
f.clean, "0312 233 123")
self.assertRaisesRegexp(ValidationError,
'Phone numbers must be in 0XXX XXX XXXX format.',
f.clean, "0312 233 xxxx")
def test_TRIdentificationNumberField(self):
f = trforms.TRIdentificationNumberField()
self.assertEqual(f.clean("10000000146"), "10000000146")
self.assertRaisesRegexp(ValidationError,
'Enter a valid Turkish Identification number.',
f.clean, "10000000136")
self.assertRaisesRegexp(ValidationError,
'Enter a valid Turkish Identification number.',
f.clean, "10000000147")
self.assertRaisesRegexp(ValidationError,
'Turkish Identification number must be 11 digits.',
f.clean, "123456789")
self.assertRaisesRegexp(ValidationError,
'Enter a valid Turkish Identification number.',
f.clean, "1000000014x")
self.assertRaisesRegexp(ValidationError,
'Enter a valid Turkish Identification number.',
f.clean, "x0000000146")

View File

@ -14,7 +14,7 @@ from localflavor.fr import tests as localflavor_fr_tests
from localflavor.generic import tests as localflavor_generic_tests from localflavor.generic import tests as localflavor_generic_tests
from localflavor.id import tests as localflavor_id_tests from localflavor.id import tests as localflavor_id_tests
from localflavor.ie import tests as localflavor_ie_tests from localflavor.ie import tests as localflavor_ie_tests
from localflavor.il import IsraelLocalFlavorTests from localflavor.il import ILLocalFlavorTests
from localflavor.is_ import tests as localflavor_is_tests from localflavor.is_ import tests as localflavor_is_tests
from localflavor.it import tests as localflavor_it_tests from localflavor.it import tests as localflavor_it_tests
from localflavor.jp import tests as localflavor_jp_tests from localflavor.jp import tests as localflavor_jp_tests
@ -25,12 +25,13 @@ from localflavor.pt import tests as localflavor_pt_tests
from localflavor.ro import tests as localflavor_ro_tests from localflavor.ro import tests as localflavor_ro_tests
from localflavor.se import tests as localflavor_se_tests from localflavor.se import tests as localflavor_se_tests
from localflavor.sk import tests as localflavor_sk_tests from localflavor.sk import tests as localflavor_sk_tests
from localflavor.tr import TRLocalFlavorTests
from localflavor.uk import tests as localflavor_uk_tests from localflavor.uk import tests as localflavor_uk_tests
from localflavor.us import tests as localflavor_us_tests from localflavor.us import tests as localflavor_us_tests
from localflavor.uy import tests as localflavor_uy_tests from localflavor.uy import tests as localflavor_uy_tests
from localflavor.za import tests as localflavor_za_tests from localflavor.za import tests as localflavor_za_tests
from localflavor.be import BETests from localflavor.be import BELocalFlavorTests
__test__ = { __test__ = {
'localflavor_ar_tests': localflavor_ar_tests, 'localflavor_ar_tests': localflavor_ar_tests,

View File

@ -11,5 +11,10 @@ from util import *
from validators import TestFieldWithValidators from validators import TestFieldWithValidators
from widgets import * from widgets import *
from regressiontests.forms.localflavortests import (__test__, BETests, from regressiontests.forms.localflavortests import (
DELocalFlavorTests, IsraelLocalFlavorTests) __test__,
BELocalFlavorTests,
DELocalFlavorTests,
ILLocalFlavorTests,
TRLocalFlavorTests
)