Commits

Chris Beaven  committed f6a6319

Initial code commit

  • Participants
  • Parent commits f7483f5

Comments (0)

Files changed (11)

File django_countries/__init__.py

+from django_countries.fields import CountryField

File django_countries/bin/regenerate.py

+#!/usr/bin/env python
+import os
+import re
+import string
+import urllib2
+from titlecase import titlecase
+
+TEMPLATE = u'''from django.utils.translation import ugettext_lazy as _
+
+# Nicely titled (and translatable) country names.
+COUNTRIES = (
+%(countries)s
+)
+
+# Nicely titled country names with duplicates for those which contain a comma
+# (containing the non-comma'd version).
+COUNTRIES_PLUS = (
+%(countries_plus)s
+)
+
+# Official capitalized country names.
+OFFICIAL_COUNTRIES = {
+%(official_countries)s
+}
+'''
+OFFICIAL_COUNTRIES_LINE = u'    %(code)r: %(name)r,'
+COUNTRIES_LINE = u'    (%(code)r, _(%(name)r)),'
+RE_VALID_LINE = re.compile(r'\s*(?P<name>.+);(?P<code>[A-Z]{2})\s*$')
+RE_ACRONYM = re.compile('\b[A-Z](\.[A-Z])+\b')
+
+
+def _cmp_value(value):
+    """
+    Ensure the countries are sorted correctly by replacing unicode characters
+    with the basic English letter equivalent.
+
+    """
+    value = value.replace(u'\u0439', 'e').replace(u'\u0444', 'o')
+    if value.startswith(u'\u0415'):
+        value = 'A%s' % value[1:]
+    if value.startswith(u'\u0415'):
+        value = 'A%s' % value[1:]
+    return value
+
+
+def regenerate(location='http://www.iso.org/iso/list-en1-semic-3.txt',
+               filename=None, default_encoding='windows-1251'):
+    """
+    Generate the countries Python module from a semicolon delimited file.
+    
+    """
+    # Get the country list.
+    data = urllib2.urlopen(location)
+    official_countries = []
+    if ('content-type' in data.headers and
+                'charset=' in data.headers['content-type']):
+        encoding = data.headers['content-type'].split('charset=')[-1]
+    else:
+        encoding = default_encoding
+    for line in data:
+        match = RE_VALID_LINE.match(unicode(line, encoding))
+        if not match:
+            continue
+        country = match.groupdict()
+        official_countries.append((str(country['code']), country['name']))
+
+    # Generate template output (and the nicely titled country names).
+    official_countries_lines = []
+    countries_lines = []
+    countries_plus = []
+    for code, name in official_countries:
+        country_data = {'code': code, 'name': name}
+        official_countries_lines.append(OFFICIAL_COUNTRIES_LINE % country_data)
+        name = string.capwords(name)
+        name = RE_ACRONYM.sub(lambda match: match.group().upper(), name)
+        if ', ' in name:
+            important, rest = name.split(', ', 1)
+            important = titlecase(important)
+            # Temporarily add on a space so titlecase doesn't think that ending
+            # shortwords should be titled.
+            rest = titlecase('%s ' % rest)[:-1]
+            countries_plus.append(('%s %s' % (rest, important), code))
+            name = '%s, %s' % (important, rest)
+        else:
+            name = titlecase(name)
+        country_data['name'] = name
+        countries_lines.append(COUNTRIES_LINE % country_data)
+        countries_plus.append((name, code))
+    # Order the countries_plus list of countries.
+    countries_plus.sort(cmp=lambda a, b: cmp((_cmp_value(a[0]), a[1]),
+                                             (_cmp_value(b[0]), b[1])))
+    countries_plus_lines = []
+    for name, code in countries_plus:
+        country_data = {'code': code, 'name': name}
+        countries_plus_lines.append(COUNTRIES_LINE % country_data)
+
+    # Generate and save the file.
+    if not filename:
+        filename = os.path.join(os.path.dirname(os.path.dirname(
+            os.path.realpath(__file__))), 'countries.py')
+    # TODO: first make a backup of the file if it exists already.
+    f = open(filename, 'w')
+    f.write(TEMPLATE % {
+        'official_countries': '\n'.join(official_countries_lines),
+        'countries': '\n'.join(countries_lines),
+        'countries_plus': '\n'.join(countries_plus_lines)
+    })
+    f.close()
+
+
+if __name__ == '__main__':
+    # TODO: use parseopt for location / filename
+    regenerate()

File django_countries/bin/titlecase.py

+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+titlecase.py v0.3
+Original Perl version by: John Gruber http://daringfireball.net/ 10 May 2008
+Python version by Stuart Colville http://muffinresearch.co.uk
+License: http://www.opensource.org/licenses/mit-license.php
+"""
+
+import sys
+import re
+
+__all__ = ['titlecase']
+
+
+SMALL = 'a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v\.?|via|vs\.?'
+PUNCT = "[!\"#$%&'‘()*+,-./:;?@[\\\\\\]_`{|}~]"
+
+SMALL_WORDS = re.compile(r'^(%s)$' % SMALL, re.I)
+INLINE_PERIOD = re.compile(r'[a-zA-Z][.][a-zA-Z]')
+UC_ELSEWHERE = re.compile(r'%s*?[a-zA-Z]+[A-Z]+?' % PUNCT)
+CAPFIRST = re.compile(r"^%s*?([A-Za-z])" % PUNCT)
+SMALL_FIRST = re.compile(r'^(%s*)(%s)\b' % (PUNCT, SMALL), re.I)
+SMALL_LAST = re.compile(r'\b(%s)%s?$' % (SMALL, PUNCT), re.I)
+SUBPHRASE = re.compile(r'([:.;?!][ ])(%s)' % SMALL)
+
+def titlecase(text):
+
+    """
+    Titlecases input text
+
+    This filter changes all words to Title Caps, and attempts to be clever
+    about *un*capitalizing SMALL words like a/an/the in the input.
+
+    The list of "SMALL words" which are not capped comes from
+    the New York Times Manual of Style, plus 'vs' and 'v'.
+
+    """
+
+    words = re.split('\s', text)
+    line = []
+    for word in words:
+        if INLINE_PERIOD.search(word) or UC_ELSEWHERE.match(word):
+            line.append(word)
+            continue
+        if SMALL_WORDS.match(word):
+            line.append(word.lower())
+            continue
+        line.append(CAPFIRST.sub(lambda m: m.group(0).upper(), word))
+
+    line = " ".join(line)
+
+    line = SMALL_FIRST.sub(lambda m: '%s%s' % (
+        m.group(1),
+        m.group(2).capitalize()
+    ), line)
+
+    line = SMALL_LAST.sub(lambda m: m.group(0).capitalize(), line)
+
+    line = SUBPHRASE.sub(lambda m: '%s%s' % (
+        m.group(1),
+        m.group(2).capitalize()
+    ), line)
+
+    return line
+
+
+if __name__ == '__main__':
+    if not sys.stdin.isatty():
+        for line in sys.stdin:
+            print titlecase(line)
+
+

File django_countries/countries.py

+from django.utils.translation import ugettext_lazy as _
+
+# Nicely titled (and translatable) country names.
+COUNTRIES = (
+    ('AF', _(u'Afghanistan')),
+    ('AX', _(u'\u0415land Islands')),
+    ('AL', _(u'Albania')),
+    ('DZ', _(u'Algeria')),
+    ('AS', _(u'American Samoa')),
+    ('AD', _(u'Andorra')),
+    ('AO', _(u'Angola')),
+    ('AI', _(u'Anguilla')),
+    ('AQ', _(u'Antarctica')),
+    ('AG', _(u'Antigua and Barbuda')),
+    ('AR', _(u'Argentina')),
+    ('AM', _(u'Armenia')),
+    ('AW', _(u'Aruba')),
+    ('AU', _(u'Australia')),
+    ('AT', _(u'Austria')),
+    ('AZ', _(u'Azerbaijan')),
+    ('BS', _(u'Bahamas')),
+    ('BH', _(u'Bahrain')),
+    ('BD', _(u'Bangladesh')),
+    ('BB', _(u'Barbados')),
+    ('BY', _(u'Belarus')),
+    ('BE', _(u'Belgium')),
+    ('BZ', _(u'Belize')),
+    ('BJ', _(u'Benin')),
+    ('BM', _(u'Bermuda')),
+    ('BT', _(u'Bhutan')),
+    ('BO', _(u'Bolivia, Plurinational State of')),
+    ('BA', _(u'Bosnia and Herzegovina')),
+    ('BW', _(u'Botswana')),
+    ('BV', _(u'Bouvet Island')),
+    ('BR', _(u'Brazil')),
+    ('IO', _(u'British Indian Ocean Territory')),
+    ('BN', _(u'Brunei Darussalam')),
+    ('BG', _(u'Bulgaria')),
+    ('BF', _(u'Burkina Faso')),
+    ('BI', _(u'Burundi')),
+    ('KH', _(u'Cambodia')),
+    ('CM', _(u'Cameroon')),
+    ('CA', _(u'Canada')),
+    ('CV', _(u'Cape Verde')),
+    ('KY', _(u'Cayman Islands')),
+    ('CF', _(u'Central African Republic')),
+    ('TD', _(u'Chad')),
+    ('CL', _(u'Chile')),
+    ('CN', _(u'China')),
+    ('CX', _(u'Christmas Island')),
+    ('CC', _(u'Cocos (Keeling) Islands')),
+    ('CO', _(u'Colombia')),
+    ('KM', _(u'Comoros')),
+    ('CG', _(u'Congo')),
+    ('CD', _(u'Congo, The Democratic Republic of the')),
+    ('CK', _(u'Cook Islands')),
+    ('CR', _(u'Costa Rica')),
+    ('CI', _(u"C\u0444te D'ivoire")),
+    ('HR', _(u'Croatia')),
+    ('CU', _(u'Cuba')),
+    ('CY', _(u'Cyprus')),
+    ('CZ', _(u'Czech Republic')),
+    ('DK', _(u'Denmark')),
+    ('DJ', _(u'Djibouti')),
+    ('DM', _(u'Dominica')),
+    ('DO', _(u'Dominican Republic')),
+    ('EC', _(u'Ecuador')),
+    ('EG', _(u'Egypt')),
+    ('SV', _(u'El Salvador')),
+    ('GQ', _(u'Equatorial Guinea')),
+    ('ER', _(u'Eritrea')),
+    ('EE', _(u'Estonia')),
+    ('ET', _(u'Ethiopia')),
+    ('FK', _(u'Falkland Islands (Malvinas)')),
+    ('FO', _(u'Faroe Islands')),
+    ('FJ', _(u'Fiji')),
+    ('FI', _(u'Finland')),
+    ('FR', _(u'France')),
+    ('GF', _(u'French Guiana')),
+    ('PF', _(u'French Polynesia')),
+    ('TF', _(u'French Southern Territories')),
+    ('GA', _(u'Gabon')),
+    ('GM', _(u'Gambia')),
+    ('GE', _(u'Georgia')),
+    ('DE', _(u'Germany')),
+    ('GH', _(u'Ghana')),
+    ('GI', _(u'Gibraltar')),
+    ('GR', _(u'Greece')),
+    ('GL', _(u'Greenland')),
+    ('GD', _(u'Grenada')),
+    ('GP', _(u'Guadeloupe')),
+    ('GU', _(u'Guam')),
+    ('GT', _(u'Guatemala')),
+    ('GG', _(u'Guernsey')),
+    ('GN', _(u'Guinea')),
+    ('GW', _(u'Guinea-bissau')),
+    ('GY', _(u'Guyana')),
+    ('HT', _(u'Haiti')),
+    ('HM', _(u'Heard Island and Mcdonald Islands')),
+    ('VA', _(u'Holy See (Vatican City State)')),
+    ('HN', _(u'Honduras')),
+    ('HK', _(u'Hong Kong')),
+    ('HU', _(u'Hungary')),
+    ('IS', _(u'Iceland')),
+    ('IN', _(u'India')),
+    ('ID', _(u'Indonesia')),
+    ('IR', _(u'Iran, Islamic Republic of')),
+    ('IQ', _(u'Iraq')),
+    ('IE', _(u'Ireland')),
+    ('IM', _(u'Isle of Man')),
+    ('IL', _(u'Israel')),
+    ('IT', _(u'Italy')),
+    ('JM', _(u'Jamaica')),
+    ('JP', _(u'Japan')),
+    ('JE', _(u'Jersey')),
+    ('JO', _(u'Jordan')),
+    ('KZ', _(u'Kazakhstan')),
+    ('KE', _(u'Kenya')),
+    ('KI', _(u'Kiribati')),
+    ('KP', _(u"Korea, Democratic People's Republic of")),
+    ('KR', _(u'Korea, Republic of')),
+    ('KW', _(u'Kuwait')),
+    ('KG', _(u'Kyrgyzstan')),
+    ('LA', _(u"Lao People's Democratic Republic")),
+    ('LV', _(u'Latvia')),
+    ('LB', _(u'Lebanon')),
+    ('LS', _(u'Lesotho')),
+    ('LR', _(u'Liberia')),
+    ('LY', _(u'Libyan Arab Jamahiriya')),
+    ('LI', _(u'Liechtenstein')),
+    ('LT', _(u'Lithuania')),
+    ('LU', _(u'Luxembourg')),
+    ('MO', _(u'Macao')),
+    ('MK', _(u'Macedonia, The Former Yugoslav Republic of')),
+    ('MG', _(u'Madagascar')),
+    ('MW', _(u'Malawi')),
+    ('MY', _(u'Malaysia')),
+    ('MV', _(u'Maldives')),
+    ('ML', _(u'Mali')),
+    ('MT', _(u'Malta')),
+    ('MH', _(u'Marshall Islands')),
+    ('MQ', _(u'Martinique')),
+    ('MR', _(u'Mauritania')),
+    ('MU', _(u'Mauritius')),
+    ('YT', _(u'Mayotte')),
+    ('MX', _(u'Mexico')),
+    ('FM', _(u'Micronesia, Federated States of')),
+    ('MD', _(u'Moldova, Republic of')),
+    ('MC', _(u'Monaco')),
+    ('MN', _(u'Mongolia')),
+    ('ME', _(u'Montenegro')),
+    ('MS', _(u'Montserrat')),
+    ('MA', _(u'Morocco')),
+    ('MZ', _(u'Mozambique')),
+    ('MM', _(u'Myanmar')),
+    ('NA', _(u'Namibia')),
+    ('NR', _(u'Nauru')),
+    ('NP', _(u'Nepal')),
+    ('NL', _(u'Netherlands')),
+    ('AN', _(u'Netherlands Antilles')),
+    ('NC', _(u'New Caledonia')),
+    ('NZ', _(u'New Zealand')),
+    ('NI', _(u'Nicaragua')),
+    ('NE', _(u'Niger')),
+    ('NG', _(u'Nigeria')),
+    ('NU', _(u'Niue')),
+    ('NF', _(u'Norfolk Island')),
+    ('MP', _(u'Northern Mariana Islands')),
+    ('NO', _(u'Norway')),
+    ('OM', _(u'Oman')),
+    ('PK', _(u'Pakistan')),
+    ('PW', _(u'Palau')),
+    ('PS', _(u'Palestinian Territory, Occupied')),
+    ('PA', _(u'Panama')),
+    ('PG', _(u'Papua New Guinea')),
+    ('PY', _(u'Paraguay')),
+    ('PE', _(u'Peru')),
+    ('PH', _(u'Philippines')),
+    ('PN', _(u'Pitcairn')),
+    ('PL', _(u'Poland')),
+    ('PT', _(u'Portugal')),
+    ('PR', _(u'Puerto Rico')),
+    ('QA', _(u'Qatar')),
+    ('RE', _(u'R\u0439union')),
+    ('RO', _(u'Romania')),
+    ('RU', _(u'Russian Federation')),
+    ('RW', _(u'Rwanda')),
+    ('BL', _(u'Saint Barth\u0439lemy')),
+    ('SH', _(u'Saint Helena')),
+    ('KN', _(u'Saint Kitts and Nevis')),
+    ('LC', _(u'Saint Lucia')),
+    ('MF', _(u'Saint Martin')),
+    ('PM', _(u'Saint Pierre and Miquelon')),
+    ('VC', _(u'Saint Vincent and the Grenadines')),
+    ('WS', _(u'Samoa')),
+    ('SM', _(u'San Marino')),
+    ('ST', _(u'Sao Tome and Principe')),
+    ('SA', _(u'Saudi Arabia')),
+    ('SN', _(u'Senegal')),
+    ('RS', _(u'Serbia')),
+    ('SC', _(u'Seychelles')),
+    ('SL', _(u'Sierra Leone')),
+    ('SG', _(u'Singapore')),
+    ('SK', _(u'Slovakia')),
+    ('SI', _(u'Slovenia')),
+    ('SB', _(u'Solomon Islands')),
+    ('SO', _(u'Somalia')),
+    ('ZA', _(u'South Africa')),
+    ('GS', _(u'South Georgia and the South Sandwich Islands')),
+    ('ES', _(u'Spain')),
+    ('LK', _(u'Sri Lanka')),
+    ('SD', _(u'Sudan')),
+    ('SR', _(u'Suriname')),
+    ('SJ', _(u'Svalbard and Jan Mayen')),
+    ('SZ', _(u'Swaziland')),
+    ('SE', _(u'Sweden')),
+    ('CH', _(u'Switzerland')),
+    ('SY', _(u'Syrian Arab Republic')),
+    ('TW', _(u'Taiwan, Province of China')),
+    ('TJ', _(u'Tajikistan')),
+    ('TZ', _(u'Tanzania, United Republic of')),
+    ('TH', _(u'Thailand')),
+    ('TL', _(u'Timor-leste')),
+    ('TG', _(u'Togo')),
+    ('TK', _(u'Tokelau')),
+    ('TO', _(u'Tonga')),
+    ('TT', _(u'Trinidad and Tobago')),
+    ('TN', _(u'Tunisia')),
+    ('TR', _(u'Turkey')),
+    ('TM', _(u'Turkmenistan')),
+    ('TC', _(u'Turks and Caicos Islands')),
+    ('TV', _(u'Tuvalu')),
+    ('UG', _(u'Uganda')),
+    ('UA', _(u'Ukraine')),
+    ('AE', _(u'United Arab Emirates')),
+    ('GB', _(u'United Kingdom')),
+    ('US', _(u'United States')),
+    ('UM', _(u'United States Minor Outlying Islands')),
+    ('UY', _(u'Uruguay')),
+    ('UZ', _(u'Uzbekistan')),
+    ('VU', _(u'Vanuatu')),
+    ('VE', _(u'Venezuela, Bolivarian Republic of')),
+    ('VN', _(u'Viet Nam')),
+    ('VG', _(u'Virgin Islands, British')),
+    ('VI', _(u'Virgin Islands, U.s.')),
+    ('WF', _(u'Wallis and Futuna')),
+    ('EH', _(u'Western Sahara')),
+    ('YE', _(u'Yemen')),
+    ('ZM', _(u'Zambia')),
+    ('ZW', _(u'Zimbabwe')),
+)
+
+# Nicely titled country names with duplicates for those which contain a comma
+# (containing the non-comma'd version).
+COUNTRIES_PLUS = (
+    ('AF', _(u'Afghanistan')),
+    ('AX', _(u'\u0415land Islands')),
+    ('AL', _(u'Albania')),
+    ('DZ', _(u'Algeria')),
+    ('AS', _(u'American Samoa')),
+    ('AD', _(u'Andorra')),
+    ('AO', _(u'Angola')),
+    ('AI', _(u'Anguilla')),
+    ('AQ', _(u'Antarctica')),
+    ('AG', _(u'Antigua and Barbuda')),
+    ('AR', _(u'Argentina')),
+    ('AM', _(u'Armenia')),
+    ('AW', _(u'Aruba')),
+    ('AU', _(u'Australia')),
+    ('AT', _(u'Austria')),
+    ('AZ', _(u'Azerbaijan')),
+    ('BS', _(u'Bahamas')),
+    ('BH', _(u'Bahrain')),
+    ('BD', _(u'Bangladesh')),
+    ('BB', _(u'Barbados')),
+    ('BY', _(u'Belarus')),
+    ('BE', _(u'Belgium')),
+    ('BZ', _(u'Belize')),
+    ('BJ', _(u'Benin')),
+    ('BM', _(u'Bermuda')),
+    ('BT', _(u'Bhutan')),
+    ('VE', _(u'Bolivarian Republic of Venezuela')),
+    ('BO', _(u'Bolivia, Plurinational State of')),
+    ('BA', _(u'Bosnia and Herzegovina')),
+    ('BW', _(u'Botswana')),
+    ('BV', _(u'Bouvet Island')),
+    ('BR', _(u'Brazil')),
+    ('IO', _(u'British Indian Ocean Territory')),
+    ('VG', _(u'British Virgin Islands')),
+    ('BN', _(u'Brunei Darussalam')),
+    ('BG', _(u'Bulgaria')),
+    ('BF', _(u'Burkina Faso')),
+    ('BI', _(u'Burundi')),
+    ('KH', _(u'Cambodia')),
+    ('CM', _(u'Cameroon')),
+    ('CA', _(u'Canada')),
+    ('CV', _(u'Cape Verde')),
+    ('KY', _(u'Cayman Islands')),
+    ('CF', _(u'Central African Republic')),
+    ('TD', _(u'Chad')),
+    ('CL', _(u'Chile')),
+    ('CN', _(u'China')),
+    ('CX', _(u'Christmas Island')),
+    ('CC', _(u'Cocos (Keeling) Islands')),
+    ('CO', _(u'Colombia')),
+    ('KM', _(u'Comoros')),
+    ('CG', _(u'Congo')),
+    ('CD', _(u'Congo, The Democratic Republic of the')),
+    ('CK', _(u'Cook Islands')),
+    ('CR', _(u'Costa Rica')),
+    ('CI', _(u"C\u0444te D'ivoire")),
+    ('HR', _(u'Croatia')),
+    ('CU', _(u'Cuba')),
+    ('CY', _(u'Cyprus')),
+    ('CZ', _(u'Czech Republic')),
+    ('KP', _(u"Democratic People's Republic of Korea")),
+    ('DK', _(u'Denmark')),
+    ('DJ', _(u'Djibouti')),
+    ('DM', _(u'Dominica')),
+    ('DO', _(u'Dominican Republic')),
+    ('EC', _(u'Ecuador')),
+    ('EG', _(u'Egypt')),
+    ('SV', _(u'El Salvador')),
+    ('GQ', _(u'Equatorial Guinea')),
+    ('ER', _(u'Eritrea')),
+    ('EE', _(u'Estonia')),
+    ('ET', _(u'Ethiopia')),
+    ('FK', _(u'Falkland Islands (Malvinas)')),
+    ('FO', _(u'Faroe Islands')),
+    ('FM', _(u'Federated States of Micronesia')),
+    ('FJ', _(u'Fiji')),
+    ('FI', _(u'Finland')),
+    ('FR', _(u'France')),
+    ('GF', _(u'French Guiana')),
+    ('PF', _(u'French Polynesia')),
+    ('TF', _(u'French Southern Territories')),
+    ('GA', _(u'Gabon')),
+    ('GM', _(u'Gambia')),
+    ('GE', _(u'Georgia')),
+    ('DE', _(u'Germany')),
+    ('GH', _(u'Ghana')),
+    ('GI', _(u'Gibraltar')),
+    ('GR', _(u'Greece')),
+    ('GL', _(u'Greenland')),
+    ('GD', _(u'Grenada')),
+    ('GP', _(u'Guadeloupe')),
+    ('GU', _(u'Guam')),
+    ('GT', _(u'Guatemala')),
+    ('GG', _(u'Guernsey')),
+    ('GN', _(u'Guinea')),
+    ('GW', _(u'Guinea-bissau')),
+    ('GY', _(u'Guyana')),
+    ('HT', _(u'Haiti')),
+    ('HM', _(u'Heard Island and Mcdonald Islands')),
+    ('VA', _(u'Holy See (Vatican City State)')),
+    ('HN', _(u'Honduras')),
+    ('HK', _(u'Hong Kong')),
+    ('HU', _(u'Hungary')),
+    ('IS', _(u'Iceland')),
+    ('IN', _(u'India')),
+    ('ID', _(u'Indonesia')),
+    ('IR', _(u'Iran, Islamic Republic of')),
+    ('IQ', _(u'Iraq')),
+    ('IE', _(u'Ireland')),
+    ('IR', _(u'Islamic Republic of Iran')),
+    ('IM', _(u'Isle of Man')),
+    ('IL', _(u'Israel')),
+    ('IT', _(u'Italy')),
+    ('JM', _(u'Jamaica')),
+    ('JP', _(u'Japan')),
+    ('JE', _(u'Jersey')),
+    ('JO', _(u'Jordan')),
+    ('KZ', _(u'Kazakhstan')),
+    ('KE', _(u'Kenya')),
+    ('KI', _(u'Kiribati')),
+    ('KP', _(u"Korea, Democratic People's Republic of")),
+    ('KR', _(u'Korea, Republic of')),
+    ('KW', _(u'Kuwait')),
+    ('KG', _(u'Kyrgyzstan')),
+    ('LA', _(u"Lao People's Democratic Republic")),
+    ('LV', _(u'Latvia')),
+    ('LB', _(u'Lebanon')),
+    ('LS', _(u'Lesotho')),
+    ('LR', _(u'Liberia')),
+    ('LY', _(u'Libyan Arab Jamahiriya')),
+    ('LI', _(u'Liechtenstein')),
+    ('LT', _(u'Lithuania')),
+    ('LU', _(u'Luxembourg')),
+    ('MO', _(u'Macao')),
+    ('MK', _(u'Macedonia, The Former Yugoslav Republic of')),
+    ('MG', _(u'Madagascar')),
+    ('MW', _(u'Malawi')),
+    ('MY', _(u'Malaysia')),
+    ('MV', _(u'Maldives')),
+    ('ML', _(u'Mali')),
+    ('MT', _(u'Malta')),
+    ('MH', _(u'Marshall Islands')),
+    ('MQ', _(u'Martinique')),
+    ('MR', _(u'Mauritania')),
+    ('MU', _(u'Mauritius')),
+    ('YT', _(u'Mayotte')),
+    ('MX', _(u'Mexico')),
+    ('FM', _(u'Micronesia, Federated States of')),
+    ('MD', _(u'Moldova, Republic of')),
+    ('MC', _(u'Monaco')),
+    ('MN', _(u'Mongolia')),
+    ('ME', _(u'Montenegro')),
+    ('MS', _(u'Montserrat')),
+    ('MA', _(u'Morocco')),
+    ('MZ', _(u'Mozambique')),
+    ('MM', _(u'Myanmar')),
+    ('NA', _(u'Namibia')),
+    ('NR', _(u'Nauru')),
+    ('NP', _(u'Nepal')),
+    ('NL', _(u'Netherlands')),
+    ('AN', _(u'Netherlands Antilles')),
+    ('NC', _(u'New Caledonia')),
+    ('NZ', _(u'New Zealand')),
+    ('NI', _(u'Nicaragua')),
+    ('NE', _(u'Niger')),
+    ('NG', _(u'Nigeria')),
+    ('NU', _(u'Niue')),
+    ('NF', _(u'Norfolk Island')),
+    ('MP', _(u'Northern Mariana Islands')),
+    ('NO', _(u'Norway')),
+    ('PS', _(u'Occupied Palestinian Territory')),
+    ('OM', _(u'Oman')),
+    ('PK', _(u'Pakistan')),
+    ('PW', _(u'Palau')),
+    ('PS', _(u'Palestinian Territory, Occupied')),
+    ('PA', _(u'Panama')),
+    ('PG', _(u'Papua New Guinea')),
+    ('PY', _(u'Paraguay')),
+    ('PE', _(u'Peru')),
+    ('PH', _(u'Philippines')),
+    ('PN', _(u'Pitcairn')),
+    ('BO', _(u'Plurinational State of Bolivia')),
+    ('PL', _(u'Poland')),
+    ('PT', _(u'Portugal')),
+    ('TW', _(u'Province of China Taiwan')),
+    ('PR', _(u'Puerto Rico')),
+    ('QA', _(u'Qatar')),
+    ('KR', _(u'Republic of Korea')),
+    ('MD', _(u'Republic of Moldova')),
+    ('RE', _(u'R\u0439union')),
+    ('RO', _(u'Romania')),
+    ('RU', _(u'Russian Federation')),
+    ('RW', _(u'Rwanda')),
+    ('BL', _(u'Saint Barth\u0439lemy')),
+    ('SH', _(u'Saint Helena')),
+    ('KN', _(u'Saint Kitts and Nevis')),
+    ('LC', _(u'Saint Lucia')),
+    ('MF', _(u'Saint Martin')),
+    ('PM', _(u'Saint Pierre and Miquelon')),
+    ('VC', _(u'Saint Vincent and the Grenadines')),
+    ('WS', _(u'Samoa')),
+    ('SM', _(u'San Marino')),
+    ('ST', _(u'Sao Tome and Principe')),
+    ('SA', _(u'Saudi Arabia')),
+    ('SN', _(u'Senegal')),
+    ('RS', _(u'Serbia')),
+    ('SC', _(u'Seychelles')),
+    ('SL', _(u'Sierra Leone')),
+    ('SG', _(u'Singapore')),
+    ('SK', _(u'Slovakia')),
+    ('SI', _(u'Slovenia')),
+    ('SB', _(u'Solomon Islands')),
+    ('SO', _(u'Somalia')),
+    ('ZA', _(u'South Africa')),
+    ('GS', _(u'South Georgia and the South Sandwich Islands')),
+    ('ES', _(u'Spain')),
+    ('LK', _(u'Sri Lanka')),
+    ('SD', _(u'Sudan')),
+    ('SR', _(u'Suriname')),
+    ('SJ', _(u'Svalbard and Jan Mayen')),
+    ('SZ', _(u'Swaziland')),
+    ('SE', _(u'Sweden')),
+    ('CH', _(u'Switzerland')),
+    ('SY', _(u'Syrian Arab Republic')),
+    ('TW', _(u'Taiwan, Province of China')),
+    ('TJ', _(u'Tajikistan')),
+    ('TZ', _(u'Tanzania, United Republic of')),
+    ('TH', _(u'Thailand')),
+    ('CD', _(u'The Democratic Republic of the Congo')),
+    ('MK', _(u'The Former Yugoslav Republic of Macedonia')),
+    ('TL', _(u'Timor-leste')),
+    ('TG', _(u'Togo')),
+    ('TK', _(u'Tokelau')),
+    ('TO', _(u'Tonga')),
+    ('TT', _(u'Trinidad and Tobago')),
+    ('TN', _(u'Tunisia')),
+    ('TR', _(u'Turkey')),
+    ('TM', _(u'Turkmenistan')),
+    ('TC', _(u'Turks and Caicos Islands')),
+    ('TV', _(u'Tuvalu')),
+    ('VI', _(u'U.s. Virgin Islands')),
+    ('UG', _(u'Uganda')),
+    ('UA', _(u'Ukraine')),
+    ('AE', _(u'United Arab Emirates')),
+    ('GB', _(u'United Kingdom')),
+    ('TZ', _(u'United Republic of Tanzania')),
+    ('US', _(u'United States')),
+    ('UM', _(u'United States Minor Outlying Islands')),
+    ('UY', _(u'Uruguay')),
+    ('UZ', _(u'Uzbekistan')),
+    ('VU', _(u'Vanuatu')),
+    ('VE', _(u'Venezuela, Bolivarian Republic of')),
+    ('VN', _(u'Viet Nam')),
+    ('VG', _(u'Virgin Islands, British')),
+    ('VI', _(u'Virgin Islands, U.s.')),
+    ('WF', _(u'Wallis and Futuna')),
+    ('EH', _(u'Western Sahara')),
+    ('YE', _(u'Yemen')),
+    ('ZM', _(u'Zambia')),
+    ('ZW', _(u'Zimbabwe')),
+)
+
+# Official capitalized country names.
+OFFICIAL_COUNTRIES = {
+    'AF': u'AFGHANISTAN',
+    'AX': u'\u0415LAND ISLANDS',
+    'AL': u'ALBANIA',
+    'DZ': u'ALGERIA',
+    'AS': u'AMERICAN SAMOA',
+    'AD': u'ANDORRA',
+    'AO': u'ANGOLA',
+    'AI': u'ANGUILLA',
+    'AQ': u'ANTARCTICA',
+    'AG': u'ANTIGUA AND BARBUDA',
+    'AR': u'ARGENTINA',
+    'AM': u'ARMENIA',
+    'AW': u'ARUBA',
+    'AU': u'AUSTRALIA',
+    'AT': u'AUSTRIA',
+    'AZ': u'AZERBAIJAN',
+    'BS': u'BAHAMAS',
+    'BH': u'BAHRAIN',
+    'BD': u'BANGLADESH',
+    'BB': u'BARBADOS',
+    'BY': u'BELARUS',
+    'BE': u'BELGIUM',
+    'BZ': u'BELIZE',
+    'BJ': u'BENIN',
+    'BM': u'BERMUDA',
+    'BT': u'BHUTAN',
+    'BO': u'BOLIVIA, PLURINATIONAL STATE OF',
+    'BA': u'BOSNIA AND HERZEGOVINA',
+    'BW': u'BOTSWANA',
+    'BV': u'BOUVET ISLAND',
+    'BR': u'BRAZIL',
+    'IO': u'BRITISH INDIAN OCEAN TERRITORY',
+    'BN': u'BRUNEI DARUSSALAM',
+    'BG': u'BULGARIA',
+    'BF': u'BURKINA FASO',
+    'BI': u'BURUNDI',
+    'KH': u'CAMBODIA',
+    'CM': u'CAMEROON',
+    'CA': u'CANADA',
+    'CV': u'CAPE VERDE',
+    'KY': u'CAYMAN ISLANDS',
+    'CF': u'CENTRAL AFRICAN REPUBLIC',
+    'TD': u'CHAD',
+    'CL': u'CHILE',
+    'CN': u'CHINA',
+    'CX': u'CHRISTMAS ISLAND',
+    'CC': u'COCOS (KEELING) ISLANDS',
+    'CO': u'COLOMBIA',
+    'KM': u'COMOROS',
+    'CG': u'CONGO',
+    'CD': u'CONGO, THE DEMOCRATIC REPUBLIC OF THE',
+    'CK': u'COOK ISLANDS',
+    'CR': u'COSTA RICA',
+    'CI': u"C\u0424TE D'IVOIRE",
+    'HR': u'CROATIA',
+    'CU': u'CUBA',
+    'CY': u'CYPRUS',
+    'CZ': u'CZECH REPUBLIC',
+    'DK': u'DENMARK',
+    'DJ': u'DJIBOUTI',
+    'DM': u'DOMINICA',
+    'DO': u'DOMINICAN REPUBLIC',
+    'EC': u'ECUADOR',
+    'EG': u'EGYPT',
+    'SV': u'EL SALVADOR',
+    'GQ': u'EQUATORIAL GUINEA',
+    'ER': u'ERITREA',
+    'EE': u'ESTONIA',
+    'ET': u'ETHIOPIA',
+    'FK': u'FALKLAND ISLANDS (MALVINAS)',
+    'FO': u'FAROE ISLANDS',
+    'FJ': u'FIJI',
+    'FI': u'FINLAND',
+    'FR': u'FRANCE',
+    'GF': u'FRENCH GUIANA',
+    'PF': u'FRENCH POLYNESIA',
+    'TF': u'FRENCH SOUTHERN TERRITORIES',
+    'GA': u'GABON',
+    'GM': u'GAMBIA',
+    'GE': u'GEORGIA',
+    'DE': u'GERMANY',
+    'GH': u'GHANA',
+    'GI': u'GIBRALTAR',
+    'GR': u'GREECE',
+    'GL': u'GREENLAND',
+    'GD': u'GRENADA',
+    'GP': u'GUADELOUPE',
+    'GU': u'GUAM',
+    'GT': u'GUATEMALA',
+    'GG': u'GUERNSEY',
+    'GN': u'GUINEA',
+    'GW': u'GUINEA-BISSAU',
+    'GY': u'GUYANA',
+    'HT': u'HAITI',
+    'HM': u'HEARD ISLAND AND MCDONALD ISLANDS',
+    'VA': u'HOLY SEE (VATICAN CITY STATE)',
+    'HN': u'HONDURAS',
+    'HK': u'HONG KONG',
+    'HU': u'HUNGARY',
+    'IS': u'ICELAND',
+    'IN': u'INDIA',
+    'ID': u'INDONESIA',
+    'IR': u'IRAN, ISLAMIC REPUBLIC OF',
+    'IQ': u'IRAQ',
+    'IE': u'IRELAND',
+    'IM': u'ISLE OF MAN',
+    'IL': u'ISRAEL',
+    'IT': u'ITALY',
+    'JM': u'JAMAICA',
+    'JP': u'JAPAN',
+    'JE': u'JERSEY',
+    'JO': u'JORDAN',
+    'KZ': u'KAZAKHSTAN',
+    'KE': u'KENYA',
+    'KI': u'KIRIBATI',
+    'KP': u"KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF",
+    'KR': u'KOREA, REPUBLIC OF',
+    'KW': u'KUWAIT',
+    'KG': u'KYRGYZSTAN',
+    'LA': u"LAO PEOPLE'S DEMOCRATIC REPUBLIC",
+    'LV': u'LATVIA',
+    'LB': u'LEBANON',
+    'LS': u'LESOTHO',
+    'LR': u'LIBERIA',
+    'LY': u'LIBYAN ARAB JAMAHIRIYA',
+    'LI': u'LIECHTENSTEIN',
+    'LT': u'LITHUANIA',
+    'LU': u'LUXEMBOURG',
+    'MO': u'MACAO',
+    'MK': u'MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF',
+    'MG': u'MADAGASCAR',
+    'MW': u'MALAWI',
+    'MY': u'MALAYSIA',
+    'MV': u'MALDIVES',
+    'ML': u'MALI',
+    'MT': u'MALTA',
+    'MH': u'MARSHALL ISLANDS',
+    'MQ': u'MARTINIQUE',
+    'MR': u'MAURITANIA',
+    'MU': u'MAURITIUS',
+    'YT': u'MAYOTTE',
+    'MX': u'MEXICO',
+    'FM': u'MICRONESIA, FEDERATED STATES OF',
+    'MD': u'MOLDOVA, REPUBLIC OF',
+    'MC': u'MONACO',
+    'MN': u'MONGOLIA',
+    'ME': u'MONTENEGRO',
+    'MS': u'MONTSERRAT',
+    'MA': u'MOROCCO',
+    'MZ': u'MOZAMBIQUE',
+    'MM': u'MYANMAR',
+    'NA': u'NAMIBIA',
+    'NR': u'NAURU',
+    'NP': u'NEPAL',
+    'NL': u'NETHERLANDS',
+    'AN': u'NETHERLANDS ANTILLES',
+    'NC': u'NEW CALEDONIA',
+    'NZ': u'NEW ZEALAND',
+    'NI': u'NICARAGUA',
+    'NE': u'NIGER',
+    'NG': u'NIGERIA',
+    'NU': u'NIUE',
+    'NF': u'NORFOLK ISLAND',
+    'MP': u'NORTHERN MARIANA ISLANDS',
+    'NO': u'NORWAY',
+    'OM': u'OMAN',
+    'PK': u'PAKISTAN',
+    'PW': u'PALAU',
+    'PS': u'PALESTINIAN TERRITORY, OCCUPIED',
+    'PA': u'PANAMA',
+    'PG': u'PAPUA NEW GUINEA',
+    'PY': u'PARAGUAY',
+    'PE': u'PERU',
+    'PH': u'PHILIPPINES',
+    'PN': u'PITCAIRN',
+    'PL': u'POLAND',
+    'PT': u'PORTUGAL',
+    'PR': u'PUERTO RICO',
+    'QA': u'QATAR',
+    'RE': u'R\u0419UNION',
+    'RO': u'ROMANIA',
+    'RU': u'RUSSIAN FEDERATION',
+    'RW': u'RWANDA',
+    'BL': u'SAINT BARTH\u0419LEMY',
+    'SH': u'SAINT HELENA',
+    'KN': u'SAINT KITTS AND NEVIS',
+    'LC': u'SAINT LUCIA',
+    'MF': u'SAINT MARTIN',
+    'PM': u'SAINT PIERRE AND MIQUELON',
+    'VC': u'SAINT VINCENT AND THE GRENADINES',
+    'WS': u'SAMOA',
+    'SM': u'SAN MARINO',
+    'ST': u'SAO TOME AND PRINCIPE',
+    'SA': u'SAUDI ARABIA',
+    'SN': u'SENEGAL',
+    'RS': u'SERBIA',
+    'SC': u'SEYCHELLES',
+    'SL': u'SIERRA LEONE',
+    'SG': u'SINGAPORE',
+    'SK': u'SLOVAKIA',
+    'SI': u'SLOVENIA',
+    'SB': u'SOLOMON ISLANDS',
+    'SO': u'SOMALIA',
+    'ZA': u'SOUTH AFRICA',
+    'GS': u'SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS',
+    'ES': u'SPAIN',
+    'LK': u'SRI LANKA',
+    'SD': u'SUDAN',
+    'SR': u'SURINAME',
+    'SJ': u'SVALBARD AND JAN MAYEN',
+    'SZ': u'SWAZILAND',
+    'SE': u'SWEDEN',
+    'CH': u'SWITZERLAND',
+    'SY': u'SYRIAN ARAB REPUBLIC',
+    'TW': u'TAIWAN, PROVINCE OF CHINA',
+    'TJ': u'TAJIKISTAN',
+    'TZ': u'TANZANIA, UNITED REPUBLIC OF',
+    'TH': u'THAILAND',
+    'TL': u'TIMOR-LESTE',
+    'TG': u'TOGO',
+    'TK': u'TOKELAU',
+    'TO': u'TONGA',
+    'TT': u'TRINIDAD AND TOBAGO',
+    'TN': u'TUNISIA',
+    'TR': u'TURKEY',
+    'TM': u'TURKMENISTAN',
+    'TC': u'TURKS AND CAICOS ISLANDS',
+    'TV': u'TUVALU',
+    'UG': u'UGANDA',
+    'UA': u'UKRAINE',
+    'AE': u'UNITED ARAB EMIRATES',
+    'GB': u'UNITED KINGDOM',
+    'US': u'UNITED STATES',
+    'UM': u'UNITED STATES MINOR OUTLYING ISLANDS',
+    'UY': u'URUGUAY',
+    'UZ': u'UZBEKISTAN',
+    'VU': u'VANUATU',
+    'VE': u'VENEZUELA, BOLIVARIAN REPUBLIC OF',
+    'VN': u'VIET NAM',
+    'VG': u'VIRGIN ISLANDS, BRITISH',
+    'VI': u'VIRGIN ISLANDS, U.S.',
+    'WF': u'WALLIS AND FUTUNA',
+    'EH': u'WESTERN SAHARA',
+    'YE': u'YEMEN',
+    'ZM': u'ZAMBIA',
+    'ZW': u'ZIMBABWE',
+}

File django_countries/fields.py

+from django.db.models.fields import CharField
+from django.utils.encoding import force_unicode
+from django_countries import settings
+
+
+class FieldCountry(object):
+    def __init__(self, code):
+        self.code = code
+    
+    def __unicode__(self):
+        return force_unicode(self.code or u'')
+
+    def __eq__(self, other):
+        return self.code == force_unicode(other)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __cmp__(self, other):
+        return cmp(self.code, force_unicode(other))
+
+    def __hash__(self):
+        return hash(self.code)
+
+    def __repr__(self):
+        return "%s(code=%r)" % (self.__class__.__name__, self.code)
+
+    def __nonzero__(self):
+        return bool(self.code)
+
+    def __len__(self):
+        return self.code.size
+    
+    @property
+    def name(self):
+        # Local import so the countries aren't loaded unless they are needed. 
+        from django_countries.countries import COUNTRIES
+        for code, name in COUNTRIES:
+            if self.code == code:
+                return name
+        return ''
+    
+    @property
+    def flag(self):
+        if not self.code:
+            return ''
+        return settings.FLAG_URL % {'code_upper': self.code,
+                                    'code': self.code.lower()}
+
+
+class CountryDescriptor(object):
+    """
+    A descriptor for country fields on a model instance. Returns a FieldCountry
+    when accessed so you can do stuff like::
+
+        >>> instance.country.name
+        u'New Zealand'
+        
+        >>> instance.country.flag
+        '/static/flags/nz.gif'
+    """
+    def __init__(self, field):
+        self.field = field
+
+    def __get__(self, instance=None, owner=None):
+        if instance is None:
+            raise AttributeError(
+                "The '%s' attribute can only be accessed from %s instances."
+                % (self.field.name, owner.__name__))
+        return FieldCountry(code=instance.__dict__[self.field.name])
+
+    def __set__(self, instance, value):
+        instance.__dict__[self.field.name] = force_unicode(value)
+
+
+class CountryField(CharField):
+    """
+    A country field for Django models that provides all ISO 3166-1 countries as
+    choices.
+    
+    """
+    descriptor_class = CountryDescriptor
+ 
+    def __init__(self, *args, **kwargs):
+        # Local import so the countries aren't loaded unless they are needed. 
+        from django_countries.countries import COUNTRIES 
+
+        kwargs.setdefault('max_length', 2) 
+        kwargs.setdefault('choices', COUNTRIES) 
+
+        super(CharField, self).__init__(*args, **kwargs) 
+
+    def get_internal_type(self): 
+        return "CharField"
+
+    def contribute_to_class(self, cls, name):
+        super(CountryField, self).contribute_to_class(cls, name)
+        setattr(cls, self.name, self.descriptor_class(self))
+
+    def get_prep_lookup(self, lookup_type, value):
+        if hasattr(value, 'code'):
+            value = value.code
+        return super(CountryField, self).get_prep_lookup(lookup_type, value)
+
+    def pre_save(self, *args, **kwargs):
+        "Returns field's value just before saving."
+        value = super(CharField, self).pre_save(*args, **kwargs)
+        return self.get_prep_value(value)
+
+    def get_prep_value(self, value):
+        "Returns field's value prepared for saving into a database."
+        # Convert the FieldCountry to unicode for database insertion.
+        if value is None:
+            return None
+        return unicode(value)

File django_countries/tests/__init__.py

+from django_countries import settings
+from django_countries.utils.tests import TempAppTestCase
+
+
+class TestCountryField(TempAppTestCase):
+    test_apps = (
+        'django_countries.tests.countries_test',
+    )
+
+    def create_person(self):
+        from django_countries.tests.countries_test.models import Person
+        return Person.objects.create(name='Chris Beaven', country='NZ')
+
+    def test_logic(self):
+        person = self.create_person()
+        
+        self.assertEqual(person.country, 'NZ')
+        self.assertNotEqual(person.country, 'ZZ')
+        
+        self.assert_(person.country < 'OA')
+        self.assert_(person.country > 'NY')
+
+        self.assert_(person.country)
+        person.country = ''
+        self.assertFalse(person.country)
+
+    def test_unicode(self):
+        person = self.create_person()
+        self.assertEqual(unicode(person.country), 'NZ')
+
+    def test_name(self):
+        person = self.create_person()
+        self.assertEqual(person.country.name, u'New Zealand')
+
+    def test_flag(self):
+        person = self.create_person()
+        expected_url = settings.FLAG_URL % {'code': 'nz', 'code_upper': 'NZ'}
+        self.assertEqual(person.country.flag, expected_url)

File django_countries/tests/countries_test/__init__.py

Empty file added.

File django_countries/tests/countries_test/models.py

+from django.db import models
+from django_countries import CountryField
+
+
+class Person(models.Model):
+    name = models.CharField(max_length=50)
+    country = CountryField()

File django_countries/utils/__init__.py

Empty file added.

File django_countries/utils/tests.py

+from django.test import TestCase
+from django.conf import settings
+from django.core.management import call_command
+from django.db.models.loading import load_app
+
+
+class TempAppTestCase(TestCase):
+    """
+    A Django test case which also handles test-only applications.
+
+    """
+    test_apps = ()
+
+    def setUp(self):
+        self.old_INSTALLED_APPS = settings.INSTALLED_APPS
+        settings.INSTALLED_APPS = (tuple(settings.INSTALLED_APPS) +
+                                   self.test_apps)
+        for app in self.test_apps:
+            load_app(app)
+        if self.test_apps:
+            # Create tables for any test-only applications.
+            call_command('syncdb', verbosity=0, interactive=False)
+
+    def tearDown(self):
+        settings.INSTALLED_APPS = self.old_INSTALLED_APPS  
+#!/usr/bin/env python
+from setuptools import setup, find_packages
+
+setup(name='django-countries-2',
+      version='1.0',
+      author='Chris Beaven',
+      author_email='smileychris@gmail.com',
+      packages=find_packages(),
+      package_data={'django_countries': ['bin/*.py', 'media/*.*']},
+      # titlecase PYPI is broken, copied the module directly for now (in /bin)
+#      requires=['titlecase']
+)
+