{ "info": { "author": "Gilles Fabio", "author_email": "gilles.fabio@gmail.com", "bugtrack_url": null, "classifiers": [ "Environment :: Web Environment", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Topic :: Software Development :: Internationalization" ], "description": "django-linguist\n===============\n\n.. image:: https://secure.travis-ci.org/ulule/django-linguist.png?branch=master\n :alt: Build Status\n :target: http://travis-ci.org/ulule/django-linguist\n\n`django-linguist`_ is a Django_ application for flexible model translations.\n\nHere a few principles that define this application in comparaison to others applications:\n\n* Translations are stored in single one table and you can also use a different one per model\n* No \"one i18n table per model\", say \"goodbye\" to nightmares :)\n* No more painful migrations\n* Not tied to model class names, you are free to use your own identifiers\n* No ORM query hacks, it does not patch anything and it will be easier for you to upgrade your Django\n* No magic, it uses metaclasses and mixins and everything is explicit\n* Dead simple to plug in an existing project\n* Django admin ready\n\nIf you are looking for a \"one-i18n-table-per-model\" way, `django-parler`_ is\nan awesome alternative.\n\nInstallation\n------------\n\n.. code-block:: bash\n\n $ pip install django-linguist\n\nIn your ``settings.py``, add ``linguist`` to ``INSTALLED_APPS``:\n\n.. code-block:: python\n\n INSTALLED_APPS = (\n # Your other apps here\n 'linguist',\n )\n\nThen synchronize database:\n\n.. code-block:: bash\n\n # >= Django 1.7\n $ python manage.py migrate linguist\n\n # < Django 1.7\n $ python manage.py syncdb\n\nThat's all.\n\nConfiguration\n-------------\n\nModels\n~~~~~~\n\nIn three steps:\n\n1. Add ``linguist.metaclasses.ModelMeta`` to your model as metaclass\n2. Add ``linguist.mixins.ManagerMixin`` to your model manager\n3. Add ``linguist`` settings in your model's Meta\n\nDon't worry, it's fairly simple:\n\n.. code-block:: python\n\n from django.db import models\n from django.utils.six import with_metaclass\n from django.utils.translation import ugettext_lazy as _\n\n from linguist.metaclasses import ModelMeta as LinguistMeta\n from linguist.mixins import ManagerMixin as LinguistManagerMixin\n\n\n class PostManager(LinguistManagerMixin, models.Manager):\n pass\n\n\n class Post(with_metaclass(LinguistMeta, models.Model)):\n title = models.CharField(max_length=255)\n body = models.TextField()\n created_at = models.DateTimeField(auto_now_add=True)\n objects = PostManager()\n\n class Meta:\n verbose_name = _('post')\n verbose_name_plural = _('posts')\n linguist = {\n 'identifier': 'can-be-anything-you-want',\n 'fields': ('title', 'body'),\n 'default_language': 'fr',\n }\n\nThe ``linguist`` meta requires:\n\n* ``identifier``: a unique identifier for your model (can be anything you want)\n* ``fields``: list or tuple of model fields to translate\n\nAnd optionally requires:\n\n* ``default_language``: the default language to use\n* ``default_language_field``: the field that contains the default language to use (see below)\n* ``decider``: the translation model to use instead of the default one (see below)\n\nThat's all. You're ready.\n\nDefault language per instance\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSometimes, you need to define default language at instance level. Linguist\nsupports this feature via the ``default_language_field`` option. Add a field\nin your model that will store the default language then simply give the field\nname to Linguist.\n\nLet's take an example:\n\n.. code-block:: python\n\n from django.db import models\n from django.utils.six import with_metaclass\n from django.utils.translation import ugettext_lazy as _\n\n from linguist.metaclasses import ModelMeta as LinguistMeta\n from linguist.mixins import ManagerMixin as LinguistManagerMixin\n\n\n class PostManager(LinguistManagerMixin, models.Manager):\n pass\n\n\n class Post(with_metaclass(LinguistMeta, models.Model)):\n title = models.CharField(max_length=255)\n body = models.TextField()\n created_at = models.DateTimeField(auto_now_add=True)\n lang = models.CharField(max_length=5, default='en')\n objects = PostManager()\n\n class Meta:\n verbose_name = _('post')\n verbose_name_plural = _('posts')\n linguist = {\n 'identifier': 'can-be-anything-you-want',\n 'fields': ('title', 'body'),\n 'default_language': 'en',\n 'default_language_field': 'lang',\n }\n\nCustom table for translations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBy default, Linguist stores translations into ``linguist.models.Translation``\ntable. So in a single one table. If you need to use another table for a specific\nmodel, Linguist provides a way to override this behavior: use *deciders*.\n\nThat's really easy to implement.\n\nYou can do it in three steps:\n\n* Create a model that inherits from ``linguist.models.base.Translation``\n* Don't forget to define it as concrete (``abstract = False`` in Meta)\n* Give this model to Linguist meta ``decider`` option\n\nThis example will show you the light:\n\n.. code-block:: python\n\n\n from django.db import models\n from django.utils.six import with_metaclass\n from django.utils.translation import ugettext_lazy as _\n\n from linguist.metaclasses import ModelMeta as LinguistMeta\n from linguist.mixins import ManagerMixin as LinguistManagerMixin\n from linguist.models.base import Translation\n\n\n # Our Post model decider\n class PostTranslation(Translation):\n class Meta:\n abstract = False\n\n\n class PostManager(LinguistManagerMixin, models.Manager):\n pass\n\n\n class Post(with_metaclass(LinguistMeta, models.Model)):\n title = models.CharField(max_length=255)\n body = models.TextField()\n created_at = models.DateTimeField(auto_now_add=True)\n objects = PostManager()\n\n class Meta:\n verbose_name = _('post')\n verbose_name_plural = _('posts')\n linguist = {\n 'identifier': 'can-be-anything-you-want',\n 'fields': ('title', 'body'),\n 'default_language': 'fr',\n 'decider': PostTranslation,\n }\n\ndjango.contrib.admin\n~~~~~~~~~~~~~~~~~~~~\n\nSimply use ``linguist.admin.TranslatableModelAdmin`` class:\n\n.. code-block:: python\n\n from django.contrib import admin\n from linguist.admin import TranslatableModelAdmin\n from .models import Post\n\n\n class PostAdmin(TranslatableModelAdmin):\n list_display = ('title', 'body', 'created_at')\n\n admin.site.register(Post, PostAdmin)\n\n\nBonus! You can display instance's languages in ``list_display`` via the\n``languages_column`` property provided by the admin class:\n\n.. code-block:: python\n\n from django.contrib import admin\n from linguist.admin import TranslatableModelAdmin\n from .models import Post\n\n\n class PostAdmin(TranslatableModelAdmin):\n list_display = ('title', 'body', 'languages_column', 'created_at')\n\n admin.site.register(Post, PostAdmin)\n\nHow it works\n------------\n\nLinguist adds virtual language fields to your models. For the example above, if\nwe have ``en``, ``fr`` and ``it`` in ``settings.LANGUAGES``, it\ndynamically adds the following fields in ``Post`` model:\n\n* ``Post.title_en``\n* ``Post.title_fr``\n* ``Post.title_it``\n* ``Post.body_en``\n* ``Post.body_fr``\n* ``Post.body_it``\n\nThese fields are virtuals. They don't exist in ``Post`` table. There are\nwrappers around ``linguist.Translation`` model. All translations will be stored\nin this table.\n\nWhen you set/get ``post.title``, Linguist will use the current active language\nand will set/get the correct field for this language. For example, if your\ndefault language is English (``en``), then ``Post.title`` will refer to ``post.title_en``.\n\nThe ``ModelMixin`` enhance your model with the following properties and methods:\n\n``instance.linguist_identifier`` (*read-only* property)\n Your model identifier defined in the related translation class.\n Shortcut pointing on ``instance._linguist.identifier``.\n\n``instance.default_language`` (*read-write* property)\n The default language to use.\n Shortcut pointing on ``instance._linguist.default_language``.\n\n``instance.translatable_fields`` (*read-only* property)\n Translatable fields defined in the related translation class.\n Shorcut pointing on ``instance._linguist.fields``.\n\n``instance.available_languages`` (*read-only* property)\n Available languages for this instance (content translated in these languages).\n\n``instance.cached_translations_count`` (*read-only* property)\n Returns the number of cached translations. Each time you set a new language\n and set content on translatable fields, a cache is created for each language\n and field. It will be used to create ``Translation`` objets at instance saving.\n\n``instance.active_language()``\n Set the current active language for the instance.\n\n``instance.clear_translations_cache()``\n Remove all cached translations. Be aware, any content you set will be dropped.\n So no translation will be created/updated at saving.\n\n.. code-block:: python\n\n # Let's create a new Post\n >>> post = Post()\n\n # Set English content\n >>> post.activate_language('en')\n >>> post.title = 'Hello'\n\n # Now set French content\n >>> post.activate_language('fr')\n >>> post.title = 'Bonjour'\n\n # Be sure everything works as expected for English\n >>> post.activate_language('en')\n >>> post.title\n Hello\n\n # And now for French\n >>> post.activate_language('fr')\n >>> post.title\n Bonjour\n\n # Sweet! Save translations!\n >>> post.save()\n\nPreloading\n----------\n\nTo improve performances, you can preload/prefetch translations.\n\nFor a queryset (your queryset must inherit from Linguist manager/queryset):\n\n.. code-block:: python\n\n >>> Post.objects.with_translations()\n\nFor a list of objects (all your objects must inherit from Linguist model):\n\n.. code-block:: python\n\n >>> from linguist.helpers import prefetch_translations\n >>> posts = list(Post.objects.all())\n >>> prefetch_translations(posts)\n\nFor an instance (it must inherit from Linguist model):\n\n.. code-block:: python\n\n >>> post = Post.objects.first()\n >>> post.prefetch_translations()\n\nAll translations will be cached in instances. Database won't be hit anymore.\n\nThis preloading system takes three parameters:\n\n* ``field_names``: list of translatable field names to filter on\n* ``languages``: list of languages to filter on\n* ``populate_missing``: boolean if you want to populate cache for missing translations (defaults to ``True``)\n* ``chunks_length``: chunk limit for SELECT IN ids for translations\n\nFor example, we only want to prefetch post titles in English without populating missing\ntranslations with an empty string:\n\n.. code-block:: python\n\n >>> Post.objects.with_translations(field_names=['title'], languages=['en'], populate_missing=False)\n\nIt works the same for:\n\n* QuerySet ``with_translations()``\n* Helper ``prefetch_translations()``\n* Instance method ``prefetch_translations()``\n\n**What does \"populating missing translations\" mean?**\n\nSimple. By default, when you prefetch translations, instances cache will be populated\nwith empty strings for all supported languages (see ``settings``). For example, if\nyou have ``en``, ``fr`` and ``it`` as supported languages and only have English\ntranslations, if you try to access other languages, an empty string will be returned\nwithout any database hit:\n\n.. code-block:: python\n\n >>> Post.objects.with_translations()\n >>> post.title_fr # no database hit here because\n ''\n\nNow, if you explicitly set ``populate_missing`` to ``False``, if a translation\nis not found, it will be fetched from database.\n\n.. code-block:: python\n\n >>> Post.objects.with_translations(populate_missing=False)\n >>> post.title_fr # database hit here\n ''\n\nDevelopment\n-----------\n\n.. code-block:: bash\n\n # Don't have pip?\n $ sudo easy_install pip\n\n # Don't already have virtualenv?\n $ sudo pip install virtualenv\n\n # Clone and install dependencies\n $ git clone https://github.com/ulule/django-linguist.git\n $ cd django-linguist\n $ make devenv\n\n # Enable virtual environment.\n $ source .venv/bin/activate\n\n # Launch tests\n $ make test\n\n # Launch example project\n $ make serve\n\n.. _django-linguist: https://github.com/ulule/django-linguist\n.. _Django: http://djangoproject.com\n.. _django-parler: https://github.com/edoburu/django-parler\n\nCompatibility\n-------------\n\n- python 2.7: Django 1.8, 1.9, 1.10\n- Python 3.4: Django 1.8, 1.9, 1.10\n- Python 3.5: Django 1.8, 1.9, 1.10\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/ulule/django-linguist", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "django-linguist", "package_url": "https://pypi.org/project/django-linguist/", "platform": "", "project_url": "https://pypi.org/project/django-linguist/", "project_urls": { "Homepage": "http://github.com/ulule/django-linguist" }, "release_url": "https://pypi.org/project/django-linguist/0.4.2/", "requires_dist": null, "requires_python": "", "summary": "An application to manage translations in Django models", "version": "0.4.2" }, "last_serial": 5470964, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "e41ca6d8ef8330c57be5da6e8dfc3044", "sha256": "369158aaf616e9f63b4982f65913b59c1596bc25a75fd688aeeed593e8da403e" }, "downloads": -1, "filename": "django-linguist-0.1.0.tar.gz", "has_sig": true, "md5_digest": "e41ca6d8ef8330c57be5da6e8dfc3044", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23812, "upload_time": "2015-03-11T14:53:28", "url": "https://files.pythonhosted.org/packages/8f/9f/9989c57edd9387ccd3f3976b1a496b8684a0070a26e7f18d1feb3f2ee182/django-linguist-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "62d4821ddea8999196dafa95c3a7ae5a", "sha256": "9299bb9b34c823edb154da770e1ff021196277f1a9caa1c596740ebf93e180bb" }, "downloads": -1, "filename": "django-linguist-0.1.1.tar.gz", "has_sig": true, "md5_digest": "62d4821ddea8999196dafa95c3a7ae5a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23262, "upload_time": "2015-04-02T15:42:17", "url": "https://files.pythonhosted.org/packages/ee/9c/fa29569de2e5a4de2580d9b0ee3c01942d10d7e9a8c8fa5d32eb541139fe/django-linguist-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "662ede317c4ecb45e023a89545f3bd2f", "sha256": "34563818714a79ab353fd1a0b79eaa9e19bce64ee236637c09fdda4a47698205" }, "downloads": -1, "filename": "django-linguist-0.1.2.tar.gz", "has_sig": true, "md5_digest": "662ede317c4ecb45e023a89545f3bd2f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23412, "upload_time": "2015-04-06T21:14:02", "url": "https://files.pythonhosted.org/packages/ec/ed/b4c8fc15fbbbff3a44a9b2787c7b18930dce853a580399f1ecd6c6998d84/django-linguist-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "ff077f0314f16994fe11b8ef606ea047", "sha256": "08f3cf2be2829f0fa960eb84b42ea5a8046a02194d2175cc7e729bad89eb739e" }, "downloads": -1, "filename": "django-linguist-0.1.3.tar.gz", "has_sig": true, "md5_digest": "ff077f0314f16994fe11b8ef606ea047", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23494, "upload_time": "2015-04-10T13:21:06", "url": "https://files.pythonhosted.org/packages/8c/d1/ee8343495460e9beead34804ce4afe8bcd8699b7e36cacb19da0c2b209fa/django-linguist-0.1.3.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "9e12a07923f3736aed4798ffb1fce620", "sha256": "054e1a796d8cb7bf097565b0378662e4124ff44441b73627a68db7e1e366654f" }, "downloads": -1, "filename": "django-linguist-0.2.0.tar.gz", "has_sig": true, "md5_digest": "9e12a07923f3736aed4798ffb1fce620", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31059, "upload_time": "2017-05-22T10:22:48", "url": "https://files.pythonhosted.org/packages/dc/0a/88275b789a93d8b2b23acd1e954fae1635541c3fb9d04c74526df6e20fe1/django-linguist-0.2.0.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "d93252f9e3a2d250f178ae4f3f549690", "sha256": "ba43f9fa26279e7d1df347e80876cb9d0c888cb5e578b41919b62abca9686b6b" }, "downloads": -1, "filename": "django-linguist-0.4.0.tar.gz", "has_sig": true, "md5_digest": "d93252f9e3a2d250f178ae4f3f549690", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34568, "upload_time": "2018-07-13T15:26:55", "url": "https://files.pythonhosted.org/packages/c8/67/b9fe28073391b0bea6c515d982cbea3c56f43739cb0438ca580083e00529/django-linguist-0.4.0.tar.gz" } ], "0.4.1": [ { "comment_text": "", "digests": { "md5": "67bbd684a6bd422d88d95ff92cfab703", "sha256": "9ff215547b7c577d5043ba6d08014a5316c450edb1eb8bec5729ae38d9fb5cb3" }, "downloads": -1, "filename": "django-linguist-0.4.1.tar.gz", "has_sig": false, "md5_digest": "67bbd684a6bd422d88d95ff92cfab703", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34525, "upload_time": "2019-06-25T09:12:02", "url": "https://files.pythonhosted.org/packages/4e/fc/08297bbabfc74f68ad0ffe92fa45ec7fca58cf460d110d354180d69df03a/django-linguist-0.4.1.tar.gz" } ], "0.4.2": [ { "comment_text": "", "digests": { "md5": "b737c0e51188d5daff6d7eff9f21a7ed", "sha256": "46f31d7244b405282769b7e175cd5230103cde23106b14dd5fc30dffa92926c5" }, "downloads": -1, "filename": "django-linguist-0.4.2.tar.gz", "has_sig": true, "md5_digest": "b737c0e51188d5daff6d7eff9f21a7ed", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34551, "upload_time": "2019-07-01T13:10:17", "url": "https://files.pythonhosted.org/packages/c0/82/f420be49fc2657f849fa2102edad35ada24f8668b05c8c1bc49a31419742/django-linguist-0.4.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b737c0e51188d5daff6d7eff9f21a7ed", "sha256": "46f31d7244b405282769b7e175cd5230103cde23106b14dd5fc30dffa92926c5" }, "downloads": -1, "filename": "django-linguist-0.4.2.tar.gz", "has_sig": true, "md5_digest": "b737c0e51188d5daff6d7eff9f21a7ed", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34551, "upload_time": "2019-07-01T13:10:17", "url": "https://files.pythonhosted.org/packages/c0/82/f420be49fc2657f849fa2102edad35ada24f8668b05c8c1bc49a31419742/django-linguist-0.4.2.tar.gz" } ] }