From ae7213b593b829734d00619ec7a7b45f22bdd000 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Sat, 4 Dec 2010 05:25:24 +0000 Subject: [PATCH] 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 --- django/contrib/localflavor/tr/__init__.py | 0 django/contrib/localflavor/tr/forms.py | 91 +++++++++++++++++++ django/contrib/localflavor/tr/tr_provinces.py | 89 ++++++++++++++++++ docs/ref/contrib/localflavor.txt | 31 +++++++ tests/regressiontests/forms/localflavor/be.py | 2 +- tests/regressiontests/forms/localflavor/il.py | 2 +- tests/regressiontests/forms/localflavor/tr.py | 73 +++++++++++++++ .../regressiontests/forms/localflavortests.py | 5 +- tests/regressiontests/forms/tests/__init__.py | 9 +- 9 files changed, 296 insertions(+), 6 deletions(-) create mode 100644 django/contrib/localflavor/tr/__init__.py create mode 100644 django/contrib/localflavor/tr/forms.py create mode 100644 django/contrib/localflavor/tr/tr_provinces.py create mode 100644 tests/regressiontests/forms/localflavor/tr.py diff --git a/django/contrib/localflavor/tr/__init__.py b/django/contrib/localflavor/tr/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django/contrib/localflavor/tr/forms.py b/django/contrib/localflavor/tr/forms.py new file mode 100644 index 0000000000..d3bccf1667 --- /dev/null +++ b/django/contrib/localflavor/tr/forms.py @@ -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) diff --git a/django/contrib/localflavor/tr/tr_provinces.py b/django/contrib/localflavor/tr/tr_provinces.py new file mode 100644 index 0000000000..060a6cdaf6 --- /dev/null +++ b/django/contrib/localflavor/tr/tr_provinces.py @@ -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')), +) diff --git a/docs/ref/contrib/localflavor.txt b/docs/ref/contrib/localflavor.txt index 692cb7e909..bc5878f935 100644 --- a/docs/ref/contrib/localflavor.txt +++ b/docs/ref/contrib/localflavor.txt @@ -67,6 +67,7 @@ Countries currently supported by :mod:`~django.contrib.localflavor` are: * Spain_ * Sweden_ * Switzerland_ + * Turkey_ * `United Kingdom`_ * `United States of America`_ * Uruguay_ @@ -115,6 +116,7 @@ Here's an example of how to use them:: .. _Spain: `Spain (es)`_ .. _Sweden: `Sweden (se)`_ .. _Switzerland: `Switzerland (ch)`_ +.. _Turkey: `Turkey (tr)`_ .. _United Kingdom: `United Kingdom (uk)`_ .. _United States of America: `United States of America (us)`_ .. _Uruguay: `Uruguay (uy)`_ @@ -853,6 +855,35 @@ Switzerland (``ch``) 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``) ======================= diff --git a/tests/regressiontests/forms/localflavor/be.py b/tests/regressiontests/forms/localflavor/be.py index 2eefc59af9..71b18003c6 100644 --- a/tests/regressiontests/forms/localflavor/be.py +++ b/tests/regressiontests/forms/localflavor/be.py @@ -4,7 +4,7 @@ from django.forms import * from django.contrib.localflavor.be.forms import (BEPostalCodeField, BEPhoneNumberField, BERegionSelect, BEProvinceSelect) -class BETests(TestCase): +class BELocalFlavorTests(TestCase): """ Test case to validate BE localflavor """ diff --git a/tests/regressiontests/forms/localflavor/il.py b/tests/regressiontests/forms/localflavor/il.py index 8eba7a5130..8a327079ac 100644 --- a/tests/regressiontests/forms/localflavor/il.py +++ b/tests/regressiontests/forms/localflavor/il.py @@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError from django.utils.unittest import TestCase -class IsraelLocalFlavorTests(TestCase): +class ILLocalFlavorTests(TestCase): def test_postal_code_field(self): f = ILPostalCodeField() self.assertRaisesRegexp(ValidationError, diff --git a/tests/regressiontests/forms/localflavor/tr.py b/tests/regressiontests/forms/localflavor/tr.py new file mode 100644 index 0000000000..3ec1f5b7c4 --- /dev/null +++ b/tests/regressiontests/forms/localflavor/tr.py @@ -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") diff --git a/tests/regressiontests/forms/localflavortests.py b/tests/regressiontests/forms/localflavortests.py index 9dad13d096..0d6ff2658c 100644 --- a/tests/regressiontests/forms/localflavortests.py +++ b/tests/regressiontests/forms/localflavortests.py @@ -14,7 +14,7 @@ from localflavor.fr import tests as localflavor_fr_tests from localflavor.generic import tests as localflavor_generic_tests from localflavor.id import tests as localflavor_id_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.it import tests as localflavor_it_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.se import tests as localflavor_se_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.us import tests as localflavor_us_tests from localflavor.uy import tests as localflavor_uy_tests from localflavor.za import tests as localflavor_za_tests -from localflavor.be import BETests +from localflavor.be import BELocalFlavorTests __test__ = { 'localflavor_ar_tests': localflavor_ar_tests, diff --git a/tests/regressiontests/forms/tests/__init__.py b/tests/regressiontests/forms/tests/__init__.py index fbd7a0d9a2..caec967dd3 100644 --- a/tests/regressiontests/forms/tests/__init__.py +++ b/tests/regressiontests/forms/tests/__init__.py @@ -11,5 +11,10 @@ from util import * from validators import TestFieldWithValidators from widgets import * -from regressiontests.forms.localflavortests import (__test__, BETests, - DELocalFlavorTests, IsraelLocalFlavorTests) +from regressiontests.forms.localflavortests import ( + __test__, + BELocalFlavorTests, + DELocalFlavorTests, + ILLocalFlavorTests, + TRLocalFlavorTests +)