├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── languages ├── __init__.py ├── fields.py ├── languages.py └── regenerate.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Ryan Castner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README 3 | recursive-include languages *.py 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | django-language-field 3 | ======================== 4 | 5 | Language Field for Django apps. Includes all language choices from the IANA Language Subtag Registry. 6 | 7 | Included is: 8 | 9 | * ``LanguageField``, a model field 10 | * ``LanguageField``, a form field (Not yet implemented) 11 | * ``regenerate.py``, a script to parse the latest language list from IANA 12 | 13 | Installation 14 | ============ 15 | 16 | :: 17 | 18 | pip install django-language-field 19 | 20 | 21 | Basic usage 22 | =========== 23 | 24 | Add ``languages`` to the list of the installed apps in 25 | your ``settings.py`` file:: 26 | 27 | INSTALLED_APPS = [ 28 | ... 29 | 'languages', 30 | ... 31 | ] 32 | 33 | Then, you can use it like any regular model field:: 34 | 35 | from languages.fields import LanguageField 36 | 37 | class MyModel(models.Model): 38 | .. 39 | language = LanguageField() 40 | .. 41 | 42 | Internally, LanguageField is based upon ``CharField`` and by default 43 | represents the as a string. 44 | 45 | As with ``CharField``'s, it is discouraged to use ``null=True`` use ``blank=True`` if you want to make it a non-required field. 46 | -------------------------------------------------------------------------------- /languages/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = '0.0.3' 3 | -------------------------------------------------------------------------------- /languages/fields.py: -------------------------------------------------------------------------------- 1 | from django.db.models.fields import CharField 2 | 3 | class LanguageField(CharField): 4 | """ 5 | A language field for Django models. 6 | """ 7 | def __init__(self, *args, **kwargs): 8 | # Local import so the languages aren't loaded unless they are needed. 9 | from .languages import LANGUAGES 10 | 11 | kwargs.setdefault('max_length', 3) 12 | kwargs.setdefault('choices', LANGUAGES) 13 | kwargs.setdefault('db_collation', None) 14 | 15 | super().__init__(*args, **kwargs) 16 | -------------------------------------------------------------------------------- /languages/regenerate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | To regenerate that module to contain the latest list of IANA Language 4 | Subtag Registry, either call this module directly from the command line 5 | (``python regenenerate.py``), or call the ``regenerate`` method. 6 | 7 | """ 8 | import os 9 | import re 10 | import codecs 11 | from urllib.request import urlopen 12 | 13 | TEMPLATE = u'''# -*- coding: utf-8 -*- 14 | from django.utils.translation import gettext_lazy as _ 15 | 16 | LANGUAGES = ( 17 | %(languages)s 18 | ) 19 | 20 | ''' 21 | 22 | def regenerate(location='http://www.iana.org/assignments/language-subtag-registry', 23 | filename=None, default_encoding='utf-8'): 24 | """ 25 | Generate the languages Python module. 26 | """ 27 | paren = re.compile('\([^)]*\)') 28 | 29 | # Get the language list. 30 | data = urlopen(location) 31 | if ('content-type' in data.headers and 32 | 'charset=' in data.headers['content-type']): 33 | encoding = data.headers['content-type'].split('charset=')[-1] 34 | else: 35 | encoding = default_encoding 36 | content = data.read().decode(encoding) 37 | languages = [] 38 | info = {} 39 | p = None 40 | for line in content.splitlines(): 41 | if line == '%%': 42 | if 'Type' in info and info['Type'] == 'language' and 'Subtag' in info and len(info['Subtag']) <= 3: 43 | languages.append(info) 44 | info = {} 45 | elif ':' not in line and p: 46 | info[p[0]] = paren.sub('', p[2]+line).strip() 47 | else: 48 | p = line.partition(':') 49 | if not p[0] in info: # Keep the first description as it should be the most common 50 | info[p[0]] = paren.sub('', p[2]).strip() 51 | 52 | languages_lines = map(lambda x:'("%s", _(u"%s")),'%(x['Subtag'],x['Description']), languages) 53 | 54 | # Generate and save the file. 55 | if not filename: 56 | filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'languages.py') 57 | # TODO: first make a backup of the file if it exists already. 58 | f = codecs.open(filename, 'w', 'utf-8') 59 | f.write(TEMPLATE % { 60 | 'languages': '\n '.join(languages_lines), 61 | }) 62 | f.close() 63 | 64 | if __name__ == '__main__': 65 | regenerate() 66 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import setup, find_packages 4 | import languages as app 5 | 6 | def read(fname): 7 | try: 8 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 9 | except IOError: 10 | return '' 11 | 12 | setup( 13 | name = 'django-language-field', 14 | version = app.__version__, 15 | description = 'A pluggable django app that provides a comprehensive language choices field', 16 | long_description=read('README.rst'), 17 | author = 'Ryan Castner', 18 | author_email = 'ryancastner@gmail.com', 19 | url = 'https://github.com/audiolion/django-language-field', # use the URL to the github repo 20 | license='MIT', 21 | platforms=['OS Independent'], 22 | packages = ['languages'], # this must be the same as the name above 23 | zip_safe=False, 24 | keywords = ['django','language','languages','field'], # arbitrary keywords 25 | classifiers=[ 26 | 'Development Status :: 3 - Alpha', 27 | 'Framework :: Django :: 1.8', 28 | 'Framework :: Django :: 1.9', 29 | 'Environment :: Web Environment', 30 | 'Operating System :: OS Independent', 31 | 'Programming Language :: Python :: 2.7', 32 | 'Programming Language :: Python :: 3.5', 33 | 'Intended Audience :: Developers', 34 | 'License :: OSI Approved :: MIT License', 35 | ], 36 | ) 37 | --------------------------------------------------------------------------------