Index: django/contrib/localflavor/at/__init__.py
===================================================================
Index: django/contrib/localflavor/at/forms.py
===================================================================
--- django/contrib/localflavor/at/forms.py	(revision 0)
+++ django/contrib/localflavor/at/forms.py	(revision 0)
@@ -0,0 +1,28 @@
+"""
+AT-specific Form helpers
+"""
+
+from django.utils.translation import ugettext_lazy as _
+from django.newforms.fields import Field, RegexField, Select
+import re
+
+class ATZipCodeField(RegexField):
+    """
+    A form field that validates its input is a Austrian postcode.
+    
+    Accepts 4 digits
+    """
+    default_error_messages = {
+        'invalid': _('Enter a zip code in the format XXXX.'),
+    }
+    def __init__(self, *args, **kwargs):
+        super(ATZipCodeField, self).__init__(r'^\d{4}$',
+            max_length=None, min_length=None, *args, **kwargs)
+
+class ATStateSelect(Select):
+    """
+    A Select widget that uses a list of AT states as its choices.
+    """
+    def __init__(self, attrs=None):
+        from at_states import STATE_CHOICES
+        super(ATStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
\ No newline at end of file
Index: django/contrib/localflavor/at/at_states.py
===================================================================
--- django/contrib/localflavor/at/at_states.py	(revision 0)
+++ django/contrib/localflavor/at/at_states.py	(revision 0)
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*
+from django.utils.translation import ugettext_lazy as _
+
+STATE_CHOICES = (
+    ('BL', _('Burgenland')),
+    ('KA', _('Carinthia')),
+    ('NO', _('Lower Austria')),
+    ('OO', _('Upper Austria')),
+    ('SA', _('Salzburg')),
+    ('ST', _('Styria')),
+    ('TI', _('Tyrol')),
+    ('VO', _('Vorarlberg')),
+    ('WI', _('Vienna')),
+)
\ No newline at end of file
Index: tests/regressiontests/forms/localflavor/at.py
===================================================================
--- tests/regressiontests/forms/localflavor/at.py	(revision 0)
+++ tests/regressiontests/forms/localflavor/at.py	(revision 0)
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+# Tests for the contrib/localflavor/ AT form fields.
+
+tests = r"""
+# ATZipCodeField ###########################################################
+
+>>> from django.contrib.localflavor.at.forms import ATZipCodeField 
+>>> f = ATZipCodeField()
+>>> f.clean('1150')
+u'1150'
+>>> f.clean('4020')
+u'4020'
+>>> f.clean('8020')
+u'8020'
+>>> f.clean('111222')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a zip code in the format XXXX.']
+>>> f.clean('eeffee')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a zip code in the format XXXX.']
+>>> f.clean(u'')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+
+>>> f = ATZipCodeField(required=False)
+>>> f.clean('1150')
+u'1150'
+>>> f.clean('4020')
+u'4020'
+>>> f.clean('8020')
+u'8020'
+>>> f.clean('111222')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a zip code in the format XXXX.']
+>>> f.clean('eeffee')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a zip code in the format XXXX.']
+>>> f.clean(None)
+u''
+>>> f.clean('')
+u''
+>>> f.clean(u'')
+u''
+
+# ATStateSelect ##################################################################
+
+>>> from django.contrib.localflavor.at.forms import ATStateSelect
+>>> f = ATStateSelect()
+>>> f.render('bundesland', 'WI')
+u'<select name="bundesland">\n<option value="BL">Burgenland</option>\n<option value="KA">Carinthia</option>\n<option value="NO">Lower Austria</option>\n<option value="OO">Upper Austria</option>\n<option value="SA">Salzburg</option>\n<option value="ST">Styria</option>\n<option value="TI">Tyrol</option>\n<option value="VO">Vorarlberg</option>\n<option value="WI" selected="selected">Vienna</option>\n</select>'
+
+"""
