{ "info": { "author": "Climapulse NV", "author_email": "kevin.wetzels@climapulse.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Framework :: Django", "Framework :: Django :: 1.8", "Framework :: Django :: 1.9", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5" ], "description": "=======\nLabeler\n=======\n\nThe most annoying thing about Django models is their verbosity when you want to do things right. As soon as you\nhave an international audience, you'll need to start marking strings for translation. Labeler was created to reduce\nthe noise by externalizing a model's labels, help texts and error messages. It even provides the same functionality\nfor any Django form.\n\nIt's expected to work with Django 1.8, 1.9 and up.\n\nInstallation\n------------\n\nLabeler is available on Pypi as ``dj-labeler``::\n\n pip install dj-labeler\n\n\nExample\n-------\n\nImagine our ``bookstore`` models look like this::\n\n from django.db import models\n\n class Author(models.Model):\n name = models.CharField(max_length=200)\n published = models.BooleanField(default=False)\n birthdate = models.DateField(blank=True, null=True)\n\n\n class Book(models.Model):\n title = models.CharField(max_length=200)\n published_on = models.DateField(blank=True, null=True)\n isbn = models.CharField(max_length=50, unique=True)\n authors = models.ManyToManyField(Author)\n\n\nNow you want to branch out into a Dutch-speaking market. Instead of relying on Django's automagical label creation\nbased on the field name, you'll need to explicitly state your verbose name for each field *and* mark it as a\ntranslatable string. And to avoid any confusion for the people performing the Dutch translation, you want to\nprovide as much context as possible, because an author's name might not require the same label as the name of\na category.\n\nSo you end up with this::\n\n\n from django.db import models\n from django.utils.translation import pgettext_lazy\n\n class Author(models.Model):\n name = models.CharField(pgettext_lazy('author', 'name'), max_length=200)\n published = models.BooleanField(pgettext_lazy('author', 'published'), editable=False)\n birthdate = models.DateField(pgettext_lazy('author', 'birthdate'), blank=True, null=True)\n\n class Meta:\n verbose_name = pgettext_lazy('author model', 'Author')\n verbose_name_plural = pgettext_lazy('author model (plural)', 'Authors')\n\n\n class Book(models.Model):\n title = models.CharField(pgettext_lazy('book', 'title'), max_length=200)\n published_on = models.DateField(pgettext_lazy('book (date)', 'published'), blank=True, null=True)\n isbn = models.CharField(pgettext_lazy('book', 'isbn'), max_length=50, unique=True)\n authors = models.ManyToManyField(Author, verbose_name=pgettext_lazy('book authors', 'authors'))\n\n class Meta:\n verbose_name = pgettext_lazy('author model', 'Book')\n verbose_name_plural = pgettext_lazy('author model (plural)', 'Books')\n\n\nNow add in help text and you've got a lot of noise, making it hard to discern the attributes you as a programmer\ncare about most when developing, like the maximum length and whether a field is optional or unique.\n\nLabeler will enable apps to use translatable strings with less noise. Let's move the strings to a separate file\nwe'll call `i18n.py` (but any name will do) and use Labeler's ``ModelTranslations``::\n\n # i18n.py\n from django.utils.translation import pgettext_lazy\n from labeler import ModelTranslations\n\n author = ModelTranslations(\n labels=dict(\n name=pgettext_lazy('author', 'name'),\n published=pgettext_lazy('author', 'published'),\n birthdate=pgettext_lazy('author', 'birthdate')\n )\n help_texts=dict(\n birthdate=pgettext_lazy('author', 'When was the author born?')\n ),\n name=pgettext_lazy('author model', 'Author'),\n name_plural=pgettext_lazy('author model (plural)', 'Authors')\n )\n\n book = ModelTranslations(\n labels=dict(\n title=pgettext_lazy('book', 'title'),\n published_on=pgettext_lazy('book (date)', 'published'),\n isbn=pgettext_lazy('book', 'isbn'),\n authors=pgettext_lazy('book authors', 'authors')\n ),\n help_texts=dict(\n isbn=pgettext_lazy('book', 'The ISBN will be validated against XYZ database')\n ),\n name=pgettext_lazy('author model', 'Book'),\n name_plural=pgettext_lazy('author model (plural)', 'Books')\n )\n\nThat's still a lot of noise, but at least we've got it isolated to a single file in our app. Now, because\n``ModelTranslations`` is simply an extension of ``dict``, you could start doing things like this::\n\n # models.py\n from django.db import models\n from . import i18n\n\n class Author(models.Model):\n # as above\n\n class Meta:\n verbose_name = i18n.author['name']\n verbose_name_plural = i18n.author['name_plural']\n\nBut that doesn't cut down on the noise. Instead you should use the ``inject`` method/decorator of ``ModelTranslations``\n(or ``FormTranslations`` when dealing with a form). This will make our models lean and mean::\n\n # models.py\n from django.db import models\n from . import i18n\n\n @i18n.author.inject\n class Author(models.Model):\n name = models.CharField(max_length=200)\n published = models.BooleanField(default=False)\n birthdate = models.DateField(blank=True, null=True)\n\n\n @i18n.book.inject\n class Book(models.Model):\n title = models.CharField(max_length=200)\n published_on = models.DateField(blank=True, null=True, unique=True)\n isbn = models.CharField(max_length=50)\n authors = models.ManyToManyField(Author)\n\n\nSpot the difference with our initial version? This version uses translatable strings simply by decorating our models\nwith our ModelTranslations' ``inject``.\n\n\nUsage\n-----\n\n\nTranslating models using ModelTranslations\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n``ModelTranslations`` is a simple dict with some useful methods and properties added on top. Nothing is required,\nbut if you specify ``labels``, ``help_texts`` or ``error_messages``, the keys of those dictionaries should refer\nto existing model fields.\n\n+-------------------------+--------+-----------+-----------------------------+\n| ModelTranslations key | Type | Maps to | Attribute |\n+=========================+========+===========+=============================+\n| ``labels`` | dict | field | ``verbose_name`` |\n+-------------------------+--------+-----------+-----------------------------+\n| ``help_texts`` | dict | field | ``help_text`` |\n+-------------------------+--------+-----------+-----------------------------+\n| ``error_messages`` | dict | field | Updates ``error_messages`` |\n+-------------------------+--------+-----------+-----------------------------+\n| ``name`` | str | Meta | ``verbose_name`` |\n+-------------------------+--------+-----------+-----------------------------+\n| ``name_plural`` | str | Meta | ``verbose_name_plural`` |\n+-------------------------+--------+-----------+-----------------------------+\n\n\nExample::\n\n from django.utils.translation import ugettext_lazy as _\n from labeler import ModelTranslations\n\n article = ModelTranslations(\n # verbose_name of the model's fields\n labels=dict(\n title=_('Title'),\n body=_('Body')\n ),\n # help_text of the model's fields\n help_texts=dict(\n title=_('No clickbait titles please!')\n ),\n # update to the listed fields' error_messages\n error_messages=dict(\n title=dict(\n unique=_('Title already exists')\n )\n ),\n # verbose_name of the model\n name=_('article'),\n # verbose_name_plural of the model\n name_plural=_('articles'),\n # Handy dict of error messages for this model, not field-specific\n errors=dict(\n too_clickbaity=_('Please review the article.')\n ),\n # Handy dict for other kinds of messages\n messages=dict(\n first_publication=_('Congratulations! Your first article has been published')\n ),\n # It's just a dict; add whatever you want\n something_else='abc',\n publication_state={\n 'published': _('Published'),\n 'draft': _('Draft'),\n 'trashed': _('Trashed')\n }\n )\n\nWhen everything is good and ready to go, simply inject this into your model::\n\n from . import i18n\n\n @i18n.article.inject\n class Article(models.Model):\n # Fields and stuff\n\nThe nested labels, error_messages, errors, messages, and help_texts dictionaries are also available as properties.\nThis means custom validation might look like this::\n\n def clean_fields(self, exclude=None):\n super(MyModel, self).clean_fields(exclude)\n if 'title' not in exclude and calculate_clickbait_level(self.title) > 50:\n raise ValidationError({'title': i18n.article.errors['too_clickbaity']})\n\nIf you're dealing with lots of nested dicts, you can use the ``resolve`` method::\n\n hard_way = i18n.article.get('errors', {}).get('fieldname', {}).get('invalid', {}).get('state')\n easier_way = i18n.article.resolve('errors.fieldname.invalid.state')\n easier_way == hard_way\n\n\nTranslating forms using FormTranslations\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n``FormTranslations`` works exactly like ``ModelTranslations``, but it also supports a nested dictionary\n``empty_labels`` to override the default empty label on form fields.\n\n+-------------------------+--------+-----------+----------------------------+\n| FormTranslations key | Type | Maps to | Attribute |\n+=========================+========+===========+============================+\n| ``labels`` | dict | field | ``label`` |\n+-------------------------+--------+-----------+----------------------------+\n| ``help_texts`` | dict | field | ``help_text`` |\n+-------------------------+--------+-----------+----------------------------+\n| ``empty_labels`` | dict | field | ``empty_label`` |\n+-------------------------+--------+-----------+----------------------------+\n| ``error_messages`` | dict | field | Updates ``error_messages`` |\n+-------------------------+--------+-----------+----------------------------+\n\n\nUsage::\n\n\n # i18n.py\n from django.utils.translation import ugettext_lazy as _\n from labeler import FormTranslations\n\n article_form = FormTranslations(\n labels=dict(\n title=_('Title'),\n body=_('Body'),\n published=_('When to publish this article'),\n author=_('Author'),\n ),\n help_texts=dict(\n title=_('Limit to 100 characters please'),\n body=_('Formatting is not supported')\n ),\n empty_labels=dict(\n author=_('Please select an author')\n ),\n error_messages=dict(\n title=dict(\n unique=_('That title has already been used. Be more original!')\n )\n )\n )\n\n # forms.py\n from django import forms\n from . import i18n\n from .models import Article\n\n @i18n.article_form.inject\n class ArticleForm(forms.ModelForm):\n\n class Meta:\n model = Article\n fields = ('title', 'body', 'published', 'author')\n\n\nThat's all there is to it.\n\nChangelog\n---------\n\nv1.0.1\n^^^^^^\n\n- Fixes to code in the README and project information", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/climapulse/dj-labeler", "keywords": "dj-labeler,django,i18n", "license": "BSD", "maintainer": "", "maintainer_email": "", "name": "dj-labeler", "package_url": "https://pypi.org/project/dj-labeler/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/dj-labeler/", "project_urls": { "Homepage": "https://github.com/climapulse/dj-labeler" }, "release_url": "https://pypi.org/project/dj-labeler/1.1.0/", "requires_dist": [ "Django" ], "requires_python": "", "summary": "A Django library to externalize translation strings from models and forms.", "version": "1.1.0" }, "last_serial": 2108378, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "dce16c19453d4ce4d82d02add8435fa4", "sha256": "3576be476205a1f7fc76884c7960c2a420709a67a374fa07539d0c01afe148f1" }, "downloads": -1, "filename": "dj_labeler-0.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "dce16c19453d4ce4d82d02add8435fa4", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10976, "upload_time": "2016-03-14T13:00:20", "url": "https://files.pythonhosted.org/packages/ef/f4/22ac6f666ed46216152e9963ade76c2f7cdb60197d74ce19535d971620bf/dj_labeler-0.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1a50c51f0f957aa897c032465fd39bf1", "sha256": "9dfd6f17ccf000c26c50f9f15bea803e69290a24adff46c8d4d3f34e747b841d" }, "downloads": -1, "filename": "dj-labeler-0.1.0.tar.gz", "has_sig": false, "md5_digest": "1a50c51f0f957aa897c032465fd39bf1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10243, "upload_time": "2016-03-14T13:00:28", "url": "https://files.pythonhosted.org/packages/10/d5/ad116f4408522753581c60d61f17c567b5da2d31873839c5dcaf2102583d/dj-labeler-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "bc2e76437e552161ccc71d04f21f7243", "sha256": "dfb2c9168d319a073a472f17229cc4a10f4254bd14b3cf6945e47afd0754b107" }, "downloads": -1, "filename": "dj_labeler-0.2.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "bc2e76437e552161ccc71d04f21f7243", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 11204, "upload_time": "2016-03-14T13:33:16", "url": "https://files.pythonhosted.org/packages/44/e5/f26df4c92bd765cb90e35df353c60dfdd4af5c2b2260d2f7149710e02e76/dj_labeler-0.2.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "74d4b7f66326c92c94ef5fa9eca63ee4", "sha256": "9a5b54a66bdbf347910f0aa04cae1add89d80e5d3f937804fc6d786d364a660b" }, "downloads": -1, "filename": "dj-labeler-0.2.0.tar.gz", "has_sig": false, "md5_digest": "74d4b7f66326c92c94ef5fa9eca63ee4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10433, "upload_time": "2016-03-14T13:33:30", "url": "https://files.pythonhosted.org/packages/ca/41/abae838a57d801c762e3603276bed7f2444f590a61ea27356720b109f9a8/dj-labeler-0.2.0.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "3b54b59c8deb51f216e8e106b4de6249", "sha256": "a50a53094ce1e34ae6c3c3022d8ff329e3b7df0c0299af24d2f6e159ca81de74" }, "downloads": -1, "filename": "dj_labeler-1.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "3b54b59c8deb51f216e8e106b4de6249", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 11988, "upload_time": "2016-03-14T17:36:54", "url": "https://files.pythonhosted.org/packages/ae/59/5db3a5366a63e42cad521810c98511794b2fea6163131afba069e736ea62/dj_labeler-1.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "aee525aad813b59380d9e37b32f589be", "sha256": "212b6bd80dc4a9577e4754d9626d813a25f8639a2784e175dc5868d23e0976d2" }, "downloads": -1, "filename": "dj-labeler-1.0.0.tar.gz", "has_sig": false, "md5_digest": "aee525aad813b59380d9e37b32f589be", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11217, "upload_time": "2016-03-14T17:37:23", "url": "https://files.pythonhosted.org/packages/fa/06/d8a0460a50b39956c3b749c36bcbc647fa522799af30b951801f8807af38/dj-labeler-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "5a8a0ae60caa1b49b50a164ca1e2f787", "sha256": "358014dbd1c7d18ac2bb1184bd3639e312f02bf3104f934753c8177cf94b0a30" }, "downloads": -1, "filename": "dj_labeler-1.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "5a8a0ae60caa1b49b50a164ca1e2f787", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12150, "upload_time": "2016-03-15T20:25:18", "url": "https://files.pythonhosted.org/packages/19/c4/6df49ed6c08d72dfd8b80e078915cdfc9e2964b3e6dff71e16cb408ff133/dj_labeler-1.0.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c8d0a9dda321dbf992f4e1feeda2e92f", "sha256": "060c6fe2e042bf2a52540255853f73d0d9cbe796bff662ff2c7317d617b44d69" }, "downloads": -1, "filename": "dj-labeler-1.0.1.tar.gz", "has_sig": false, "md5_digest": "c8d0a9dda321dbf992f4e1feeda2e92f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13876, "upload_time": "2016-03-15T20:25:26", "url": "https://files.pythonhosted.org/packages/af/e5/27c4c492431092493001d4e252875591d52a0030a4a7031a60eaa8d5e57f/dj-labeler-1.0.1.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "008e3b9bc418b333b396215720c70e10", "sha256": "d101b8a641aaf115a0de4279c58a8d28b56b0b02d55ee1de0f166ed2e947880c" }, "downloads": -1, "filename": "dj_labeler-1.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "008e3b9bc418b333b396215720c70e10", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12151, "upload_time": "2016-05-10T11:58:23", "url": "https://files.pythonhosted.org/packages/1c/2f/763793d6b8f017c352af08dff0ab94e4e32efadd245bf8a2ba4a904e9c6c/dj_labeler-1.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2855ea575148467ef030f0f196533552", "sha256": "40d06678c68befe773ad241b640984024262dd5e110a92ee61bbf641fd6175a0" }, "downloads": -1, "filename": "dj-labeler-1.1.0.tar.gz", "has_sig": false, "md5_digest": "2855ea575148467ef030f0f196533552", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13875, "upload_time": "2016-05-10T11:58:35", "url": "https://files.pythonhosted.org/packages/f8/7d/5d034a591dfb2c534f17eb9df47a7b2c90f5cf242ab3cc970f172bc84477/dj-labeler-1.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "008e3b9bc418b333b396215720c70e10", "sha256": "d101b8a641aaf115a0de4279c58a8d28b56b0b02d55ee1de0f166ed2e947880c" }, "downloads": -1, "filename": "dj_labeler-1.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "008e3b9bc418b333b396215720c70e10", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 12151, "upload_time": "2016-05-10T11:58:23", "url": "https://files.pythonhosted.org/packages/1c/2f/763793d6b8f017c352af08dff0ab94e4e32efadd245bf8a2ba4a904e9c6c/dj_labeler-1.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2855ea575148467ef030f0f196533552", "sha256": "40d06678c68befe773ad241b640984024262dd5e110a92ee61bbf641fd6175a0" }, "downloads": -1, "filename": "dj-labeler-1.1.0.tar.gz", "has_sig": false, "md5_digest": "2855ea575148467ef030f0f196533552", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13875, "upload_time": "2016-05-10T11:58:35", "url": "https://files.pythonhosted.org/packages/f8/7d/5d034a591dfb2c534f17eb9df47a7b2c90f5cf242ab3cc970f172bc84477/dj-labeler-1.1.0.tar.gz" } ] }