Code

Ticket #15013: ru_localflavor.diff

File ru_localflavor.diff, 16.1 KB (added by blackraven, 3 years ago)

Improved Russian localflavor patch

Line 
1Index: django/contrib/localflavor/ru/ru_regions.py
2===================================================================
3--- django/contrib/localflavor/ru/ru_regions.py (revision 0)
4+++ django/contrib/localflavor/ru/ru_regions.py (revision 0)
5@@ -0,0 +1,104 @@
6+"""
7+Sources:
8+    http://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%B4%D1%8B_%D1%81%D1%83%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BE%D0%B2_%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D0%B9%D1%81%D0%BA%D0%BE%D0%B9_%D0%A4%D0%B5%D0%B4%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B8
9+    http://ru.wikipedia.org/wiki/%D0%A4%D0%B5%D0%B4%D0%B5%D1%80%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D0%BE%D0%BA%D1%80%D1%83%D0%B3%D0%B0_%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D0%B9%D1%81%D0%BA%D0%BE%D0%B9_%D0%A4%D0%B5%D0%B4%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B8
10+"""
11+from django.utils.translation import ugettext_lazy as _
12+
13+RU_COUNTY_CHOICES = (
14+    ("Central Federal County", _("Central Federal County")),
15+    ("South Federal County", _("South Federal County")),
16+    ("North-West Federal County", _("North-West Federal County")),
17+    ("Far-East Federal County", _("Far-East Federal County")),
18+    ("Siberian Federal County", _("Siberian Federal County")),
19+    ("Ural Federal County", _("Ural Federal County")),
20+    ("Privolzhsky Federal County", _("Privolzhsky Federal County")),
21+    ("North-Caucasian Federal County", _("North-Caucasian Federal County"))
22+)
23+
24+RU_REGIONS_CHOICES = (
25+    ("77", _("Moskva")),
26+    ("78", _("Saint-Peterburg")),
27+    ("50", _("Moskovskaya oblast'")),
28+    ("01", _("Adygeya, Respublika")),
29+    ("02", _("Bashkortostan, Respublika")),
30+    ("03", _("Buryatia, Respublika")),
31+    ("04", _("Altay, Respublika")),
32+    ("05", _("Dagestan, Respublika")),
33+    ("06", _("Ingushskaya Respublika")),
34+    ("07", _("Kabardino-Balkarskaya Respublika")),
35+    ("08", _("Kalmykia, Respublika")),
36+    ("09", _("Karachaevo-Cherkesskaya Respublika")),
37+    ("10", _("Karelia, Respublika")),
38+    ("11", _("Komi, Respublika")),
39+    ("12", _("Mariy Ehl, Respublika")),
40+    ("13", _("Mordovia, Respublika")),
41+    ("14", _("Sakha, Respublika (Yakutiya)")),
42+    ("15", _("Severnaya Osetia, Respublika (Alania)")),
43+    ("16", _("Tatarstan, Respublika")),
44+    ("17", _("Tyva, Respublika (Tuva)")),
45+    ("18", _("Udmurtskaya Respublika")),
46+    ("19", _("Khakassiya, Respublika")),
47+    ("95", _("Chechenskaya Respublika")),
48+    ("21", _("Chuvashskaya Respublika")),
49+    ("22", _("Altayskiy Kray")),
50+    ("80", _("Zabaykalskiy Kray")),
51+    ("82", _("Kamchatskiy Kray")),
52+    ("23", _("Krasnodarskiy Kray")),
53+    ("24", _("Krasnoyarskiy Kray")),
54+    ("81", _("Permskiy Kray")),
55+    ("25", _("Primorskiy Kray")),
56+    ("26", _("Stavropol'siyy Kray")),
57+    ("27", _("Khabarovskiy Kray")),
58+    ("28", _("Amurskaya oblast'")),
59+    ("29", _("Arkhangel'skaya oblast'")),
60+    ("30", _("Astrakhanskaya oblast'")),
61+    ("31", _("Belgorodskaya oblast'")),
62+    ("32", _("Bryanskaya oblast'")),
63+    ("33", _("Vladimirskaya oblast'")),
64+    ("34", _("Volgogradskaya oblast'")),
65+    ("35", _("Vologodskaya oblast'")),
66+    ("36", _("Voronezhskaya oblast'")),
67+    ("37", _("Ivanovskaya oblast'")),
68+    ("38", _("Irkutskaya oblast'")),
69+    ("39", _("Kaliningradskaya oblast'")),
70+    ("40", _("Kaluzhskaya oblast'")),
71+    ("42", _("Kemerovskaya oblast'")),
72+    ("43", _("Kirovskaya oblast'")),
73+    ("44", _("Kostromskaya oblast'")),
74+    ("45", _("Kurganskaya oblast'")),
75+    ("46", _("Kurskaya oblast'")),
76+    ("47", _("Leningradskaya oblast'")),
77+    ("48", _("Lipeckaya oblast'")),
78+    ("49", _("Magadanskaya oblast'")),
79+    ("51", _("Murmanskaya oblast'")),
80+    ("52", _("Nizhegorodskaja oblast'")),
81+    ("53", _("Novgorodskaya oblast'")),
82+    ("54", _("Novosibirskaya oblast'")),
83+    ("55", _("Omskaya oblast'")),
84+    ("56", _("Orenburgskaya oblast'")),
85+    ("57", _("Orlovskaya oblast'")),
86+    ("58", _("Penzenskaya oblast'")),
87+    ("60", _("Pskovskaya oblast'")),
88+    ("61", _("Rostovskaya oblast'")),
89+    ("62", _("Rjazanskaya oblast'")),
90+    ("63", _("Samarskaya oblast'")),
91+    ("64", _("Saratovskaya oblast'")),
92+    ("65", _("Sakhalinskaya oblast'")),
93+    ("66", _("Sverdlovskaya oblast'")),
94+    ("67", _("Smolenskaya oblast'")),
95+    ("68", _("Tambovskaya oblast'")),
96+    ("69", _("Tverskaya oblast'")),
97+    ("70", _("Tomskaya oblast'")),
98+    ("71", _("Tul'skaya oblast'")),
99+    ("72", _("Tyumenskaya oblast'")),
100+    ("73", _("Ul'ianovskaya oblast'")),
101+    ("74", _("Chelyabinskaya oblast'")),
102+    ("76", _("Yaroslavskaya oblast'")),
103+    ("79", _("Evreyskaya avtonomnaja oblast'")),
104+    ("83", _("Neneckiy autonomnyy okrug")),
105+    ("86", _("Khanty-Mansiyskiy avtonomnyy okrug - Yugra")),
106+    ("87", _("Chukotskiy avtonomnyy okrug")),
107+    ("89", _("Yamalo-Neneckiy avtonomnyy okrug"))
108+)
109+
110Index: django/contrib/localflavor/ru/__init__.py
111===================================================================
112Index: django/contrib/localflavor/ru/forms.py
113===================================================================
114--- django/contrib/localflavor/ru/forms.py      (revision 0)
115+++ django/contrib/localflavor/ru/forms.py      (revision 0)
116@@ -0,0 +1,63 @@
117+"""
118+Russian-specific forms helpers
119+"""
120+from django.core.validators import EMPTY_VALUES
121+from django.forms import ValidationError
122+from django.forms.fields import CharField, Select, RegexField
123+from django.utils.translation import ugettext_lazy as _
124+
125+import re
126+
127+phone_digits_re = re.compile(r'^(?:[78]-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$')
128+
129+class RUCountySelect(Select):
130+    """
131+    A Select widget that uses a list of Russian Counties as its choices.
132+    """
133+    def __init__(self, attrs=None):
134+        from ru_regions import RU_COUNTY_CHOICES
135+        super(RUCountySelect, self).__init__(attrs, choices=RU_COUNTY_CHOICES)
136+
137+class RURegionSelect(Select):
138+    """
139+    A Select widget that uses a list of Russian Regions as its choices.
140+    """
141+    def __init__(self, attrs=None):
142+        from ru_regions import RU_REGIONS_CHOICES
143+        super(RURegionSelect, self).__init__(attrs, choices=RU_REGIONS_CHOICES)
144+
145+class RUPostalCodeField(RegexField):
146+    """
147+    Russian Postal code field.
148+    Format: XXXXXX, where X is any digit, and first digit is not zero.
149+    """
150+    default_error_messages = {
151+        'invalid': _(u'Enter a postal code in the format XXXXXX.'),
152+    }
153+    def __init__(self, *args, **kwargs):
154+        super(RUPostalCodeField, self).__init__(r'^\d{6}$',
155+            max_length=None, min_length=None, *args, **kwargs)
156+
157+class RUPassportNumberField(RegexField):
158+    """
159+    Russian internal passport number format:
160+    XXXX XXXXXX where X - any digit.
161+    """
162+    default_error_messages = {
163+        'invalid': _(u'Enter a passport number in the format XXXX XXXXXX.'),
164+    }
165+    def __init__(self, *args, **kwargs):
166+        super(RUPassportNumberField, self).__init__(r'^\d{4} \d{6}$',
167+            max_length=None, min_length=None, *args, **kwargs)
168+
169+class RUAlienPassportNumberField(RegexField):
170+    """
171+    Russian alien's passport number format:
172+    XX XXXXXXX where X - any digit.
173+    """
174+    default_error_messages = {
175+        'invalid': _(u'Enter a passport number in the format XX XXXXXXX.'),
176+    }
177+    def __init__(self, *args, **kwargs):
178+        super(RUAlienPassportNumberField, self).__init__(r'^\d{2} \d{7}$',
179+            max_length=None, min_length=None, *args, **kwargs)
180+
181Index: tests/regressiontests/forms/localflavor/ru.py
182===================================================================
183--- tests/regressiontests/forms/localflavor/ru.py       (revision 0)
184+++ tests/regressiontests/forms/localflavor/ru.py       (revision 0)
185@@ -0,0 +1,148 @@
186+from django.contrib.localflavor.ru.forms import *
187+
188+from utils import LocalFlavorTestCase
189+
190+
191+class RULocalFlavorTests(LocalFlavorTestCase):
192+
193+    def test_RUPassportNumberField(self):
194+        error = [u'Enter a passport number in the format XXXX XXXXXX.']
195+        valid = {
196+            '1981 211204': '1981 211204',
197+            '0305 967876': '0305 967876',
198+        }
199+        invalid = {
200+            '1981 2112044': error,
201+            '1981 23220': error,
202+            '9981211201': error,
203+        }
204+        self.assertFieldOutput(RUPassportNumberField, valid, invalid)
205+
206+    def test_RUAlienPassportNumberField(self):
207+        error = [u'Enter a passport number in the format XX XXXXXXX.']
208+        valid = {
209+            '19 8111204': '19 8111204',
210+            '03 0567876': '03 0567876',
211+        }
212+        invalid = {
213+            '198 1112044': error,
214+            '19 81123220': error,
215+            '99 812112': error,
216+        }
217+        self.assertFieldOutput(RUAlienPassportNumberField, valid, invalid)
218+
219+    def test_RUPostalCodeField(self):
220+        error = [u'Enter a postal code in the format XXXXXX.']
221+        valid = {
222+            '987654': '987654',
223+            '123456': '123456'
224+        }
225+        invalid = {
226+            '123 34': error,
227+            '1234567': error,
228+            '12345': error
229+        }
230+        self.assertFieldOutput(RUPostalCodeField, valid, invalid)
231+
232+    def test_RUCountySelect(self):
233+        f = RUCountySelect()
234+        out = u'''<select name="county">
235+<option value="Central Federal County">Central Federal County</option>
236+<option value="South Federal County">South Federal County</option>
237+<option value="North-West Federal County">North-West Federal County</option>
238+<option value="Far-East Federal County">Far-East Federal County</option>
239+<option value="Siberian Federal County">Siberian Federal County</option>
240+<option value="Ural Federal County">Ural Federal County</option>
241+<option value="Privolzhsky Federal County">Privolzhsky Federal County</option>
242+<option value="North-Caucasian Federal County">North-Caucasian Federal County</option>
243+</select>'''
244+        self.assertEqual(f.render('county', None), out)
245+
246+    def test_RURegionSelect(self):
247+        f = RURegionSelect()
248+        out = u'''<select name="region">
249+<option value="77">Moskva</option>
250+<option value="78">Saint-Peterburg</option>
251+<option value="50">Moskovskaya oblast&#39;</option>
252+<option value="01">Adygeya, Respublika</option>
253+<option value="02">Bashkortostan, Respublika</option>
254+<option value="03">Buryatia, Respublika</option>
255+<option value="04">Altay, Respublika</option>
256+<option value="05">Dagestan, Respublika</option>
257+<option value="06">Ingushskaya Respublika</option>
258+<option value="07">Kabardino-Balkarskaya Respublika</option>
259+<option value="08">Kalmykia, Respublika</option>
260+<option value="09">Karachaevo-Cherkesskaya Respublika</option>
261+<option value="10">Karelia, Respublika</option>
262+<option value="11">Komi, Respublika</option>
263+<option value="12">Mariy Ehl, Respublika</option>
264+<option value="13">Mordovia, Respublika</option>
265+<option value="14">Sakha, Respublika (Yakutiya)</option>
266+<option value="15">Severnaya Osetia, Respublika (Alania)</option>
267+<option value="16">Tatarstan, Respublika</option>
268+<option value="17">Tyva, Respublika (Tuva)</option>
269+<option value="18">Udmurtskaya Respublika</option>
270+<option value="19">Khakassiya, Respublika</option>
271+<option value="95">Chechenskaya Respublika</option>
272+<option value="21">Chuvashskaya Respublika</option>
273+<option value="22">Altayskiy Kray</option>
274+<option value="80">Zabaykalskiy Kray</option>
275+<option value="82">Kamchatskiy Kray</option>
276+<option value="23">Krasnodarskiy Kray</option>
277+<option value="24">Krasnoyarskiy Kray</option>
278+<option value="81">Permskiy Kray</option>
279+<option value="25">Primorskiy Kray</option>
280+<option value="26">Stavropol&#39;siyy Kray</option>
281+<option value="27">Khabarovskiy Kray</option>
282+<option value="28">Amurskaya oblast&#39;</option>
283+<option value="29">Arkhangel&#39;skaya oblast&#39;</option>
284+<option value="30">Astrakhanskaya oblast&#39;</option>
285+<option value="31">Belgorodskaya oblast&#39;</option>
286+<option value="32">Bryanskaya oblast&#39;</option>
287+<option value="33">Vladimirskaya oblast&#39;</option>
288+<option value="34">Volgogradskaya oblast&#39;</option>
289+<option value="35">Vologodskaya oblast&#39;</option>
290+<option value="36">Voronezhskaya oblast&#39;</option>
291+<option value="37">Ivanovskaya oblast&#39;</option>
292+<option value="38">Irkutskaya oblast&#39;</option>
293+<option value="39">Kaliningradskaya oblast&#39;</option>
294+<option value="40">Kaluzhskaya oblast&#39;</option>
295+<option value="42">Kemerovskaya oblast&#39;</option>
296+<option value="43">Kirovskaya oblast&#39;</option>
297+<option value="44">Kostromskaya oblast&#39;</option>
298+<option value="45">Kurganskaya oblast&#39;</option>
299+<option value="46">Kurskaya oblast&#39;</option>
300+<option value="47">Leningradskaya oblast&#39;</option>
301+<option value="48">Lipeckaya oblast&#39;</option>
302+<option value="49">Magadanskaya oblast&#39;</option>
303+<option value="51">Murmanskaya oblast&#39;</option>
304+<option value="52">Nizhegorodskaja oblast&#39;</option>
305+<option value="53">Novgorodskaya oblast&#39;</option>
306+<option value="54">Novosibirskaya oblast&#39;</option>
307+<option value="55">Omskaya oblast&#39;</option>
308+<option value="56">Orenburgskaya oblast&#39;</option>
309+<option value="57">Orlovskaya oblast&#39;</option>
310+<option value="58">Penzenskaya oblast&#39;</option>
311+<option value="60">Pskovskaya oblast&#39;</option>
312+<option value="61">Rostovskaya oblast&#39;</option>
313+<option value="62">Rjazanskaya oblast&#39;</option>
314+<option value="63">Samarskaya oblast&#39;</option>
315+<option value="64">Saratovskaya oblast&#39;</option>
316+<option value="65">Sakhalinskaya oblast&#39;</option>
317+<option value="66">Sverdlovskaya oblast&#39;</option>
318+<option value="67" selected="selected">Smolenskaya oblast&#39;</option>
319+<option value="68">Tambovskaya oblast&#39;</option>
320+<option value="69">Tverskaya oblast&#39;</option>
321+<option value="70">Tomskaya oblast&#39;</option>
322+<option value="71">Tul&#39;skaya oblast&#39;</option>
323+<option value="72">Tyumenskaya oblast&#39;</option>
324+<option value="73">Ul&#39;ianovskaya oblast&#39;</option>
325+<option value="74">Chelyabinskaya oblast&#39;</option>
326+<option value="76">Yaroslavskaya oblast&#39;</option>
327+<option value="79">Evreyskaya avtonomnaja oblast&#39;</option>
328+<option value="83">Neneckiy autonomnyy okrug</option>
329+<option value="86">Khanty-Mansiyskiy avtonomnyy okrug - Yugra</option>
330+<option value="87">Chukotskiy avtonomnyy okrug</option>
331+<option value="89">Yamalo-Neneckiy avtonomnyy okrug</option>
332+</select>'''
333+        self.assertEqual(f.render('region', '67'), out)
334Index: tests/regressiontests/forms/localflavortests.py
335===================================================================
336--- tests/regressiontests/forms/localflavortests.py     (revision 15142)
337+++ tests/regressiontests/forms/localflavortests.py     (working copy)
338@@ -23,6 +23,7 @@
339 from localflavor.pl import PLLocalFlavorTests
340 from localflavor.pt import PTLocalFlavorTests
341 from localflavor.ro import ROLocalFlavorTests
342+from localflavor.ru import RULocalFlavorTests
343 from localflavor.se import SELocalFlavorTests
344 from localflavor.sk import SKLocalFlavorTests
345 from localflavor.tr import TRLocalFlavorTests
346Index: docs/ref/contrib/localflavor.txt
347===================================================================
348--- docs/ref/contrib/localflavor.txt    (revision 15142)
349+++ docs/ref/contrib/localflavor.txt    (working copy)
350@@ -62,6 +62,7 @@
351     * Poland_
352     * Portugal_
353     * Romania_
354+    * Russia_
355     * Slovakia_
356     * `South Africa`_
357     * Spain_
358@@ -111,6 +112,7 @@
359 .. _Poland: `Poland (pl)`_
360 .. _Portugal: `Portugal (pt)`_
361 .. _Romania: `Romania (ro)`_
362+.. _Russia: `Russia (ru)`_
363 .. _Slovakia: `Slovakia (sk)`_
364 .. _South Africa: `South Africa (za)`_
365 .. _Spain: `Spain (es)`_
366@@ -710,6 +712,32 @@
367 
368     A form field that validates Romanian postal codes.
369 
370+Russia (``ru``)
371+===============
372+
373+.. class:: ru.forms.RUPostalCodeField
374+
375+    Russian Postal code field.
376+    Format: XXXXXX, where X is any digit, and first digit is not zero.
377+
378+.. class:: ru.forms.RUCountySelect
379+
380+    A ``Select`` widget that uses a list of Russian Counties as its choices.
381+
382+.. class:: ru.forms.RURegionSelect
383+
384+    A ``Select`` widget that uses a list of Russian Regions as its choices.
385+
386+.. class:: ru.forms.RUPassportNumberField
387+
388+    Russian internal passport number format:
389+    XXXX XXXXXX where X - any digit.
390+
391+.. class:: ru.forms.RUAlienPassportNumberField
392+
393+    Russian alien's passport number format:
394+    XX XXXXXXX where X - any digit.
395+
396 Slovakia (``sk``)
397 =================
398