{ "info": { "author": "Aleksander Chrabaszcz", "author_email": "grkalk@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5" ], "description": "Pyslate i18n library\n====================\n\nA Python library for maintaining grammatically correct i18n\n(internationalization) of texts used in the program: translation of\nmessages, formatting dates and numbers to provide multi-language\nsupport.\n\nWhat is it for?\n---------------\n\nAs you probably know, there already are quite many i18n libraries for\nPython, mostly based on Gettext. The reason I decided to prepare my own\nlibrary was because I wasn't satisfied with any of them. I needed\nfull-features library, having similar capabilities as `Rails\ni18n `__. But it's not just a\nport. I included all features I found necessary, but also many more:\n\n| - i18n of text (tag values) based on their unique names (tag keys)\n| - possibility to use different backends where translation texts are stored\n| - support for special structures to use by translator directly in translation text\n| - powerful fallback capabilities in case some variant of tag is missing\n| - possibility of injecting Python code into translations using decorators and custom functions\n| - support for languages significantly different than English, based on practical knowledge and years of experience\n\nWhat is it not for?\n-------------------\n\n| All advanced features are optional, but it's surely not intended to be used when:\n| - you are sure you don't need anything except literal text i18n\n| - you'd like to use it as a templating engine\n| - you'd like to make some lexical analysis\n| - you'd like to create a natural language generator or a chatterbot\n\nSimple example\n--------------\n\nDefine a translation file ``translations.json``:\n\n.. code:: json\n\n {\n \"hello_world\": {\n \"en\": \"Hello world!\",\n \"pl\": \"Witaj \u015bwiecie!\"\n }\n }\n\nThen you can check that it works in an interactive Python session:\n\n::\n\n >>> from pyslate.pyslate import Pyslate\n >>> from pyslate.backends.json_backend import JsonBackend\n >>> pys_en = Pyslate(\"en\", backend=JsonBackend(\"translations.json\"))\n >>> pys_en.translate(\"hello_world\")\n Hello world!\n >>> pys_pl = Pyslate(\"pl\", backend=JsonBackend(\"translations.json\"))\n >>> pys_pl.translate(\"hello_world\")\n Witaj \u015bwiecie!\n\nIt works!\n\nSo the most basic use is to create a Pyslate object for a selected\nlanguage and then request translation of a specific tag using a\n``Pyslate.translate()`` method. To make it more handy you can use\n``Pyslate.t`` abbreviation. The JSON backend is used as an example,\nthere are other storage options available.\n\nMore complicated example\n------------------------\n\nChange translation file into:\n\n.. code:: json\n\n {\n \"introduction\": {\n \"en\": \"Hello! %{m?His|f?Her} name is %{name}.\"\n }\n }\n\nThen in your Python interpreter you can write:\n\n::\n\n >>> pys = Pyslate(\"en\", backend=JsonBackend(\"translations.json\"))\n >>> pys.t(\"introduction\", name=\"John\", variant=\"m\")\n Hello! His name is John.\n >>> pys.t(\"introduction\", name=\"Judy\", variant=\"f\")\n Hello! Her name is Judy.\n\nThere are two new things here: ``%{name}`` is a variable field where\nactual name (specified as a keyword argument for ``t()`` method) is\ninterpolated. The second is ``%{m?His|f?Her}`` structure, called a\nswitch field, which means: if ``variant`` keyword argument is \"m\", then\nprint \"His\", if ``variant`` keyword argument is \"f\" then print \"Her\". If\nnone of these is true, then the first one is used as fallback. It's\neasily possible to change pieces of translation based on context\nvariables. That's great for English, but it's often even more important\nfor `fusional\nlanguages `__ (like\nPolish) where word suffixes can vary in different forms.\n\nEven more complicated example\n-----------------------------\n\nChange translation file into:\n\n.. code:: json\n\n {\n \"show_off\": {\n \"en\": \"Hello! I'd like to show you ${toy@article}\"\n },\n \"toy\": {\n \"en\": \"wooden toy\"\n }\n }\n\nThen you can write:\n\n::\n\n >>> pys.t(\"show_off\")\n Hello! I'd like to show you a wooden toy.\n\nTwo new things here: ``${}`` specifies an inner tag field. It means\nevaluating a \"toy\" tag and interpolating the contents directly into the\nmain tag value. At the end of the inner tag key there's a ``@article``.\nIt's a decorator, which means \"take the tag value of tag it's used on,\nand then transform the string into something else\". Decorator \"article\"\nis included as specific for English and simply adds a/an article. There\nare also \"upper\" \"lower\" and \"capitalize\" decorators included right\naway. In addition, you can define any new decorator as you like, which is\n`described in the documentation\n`__.\n\nCombo\n-----\n\n.. code:: json\n\n {\n \"show_off\": {\n \"en\": \"Hello! I'd like to show you ${%{toy_name}@article}\"\n },\n \"horse\": {\n \"en\": \"rocking horse\"\n }\n }\n\nThen you can write:\n\n::\n\n >>> pys.t(\"show_off\", toy_name=\"horse\")\n Hello! I'd like to show you a rocking horse.\n\nHow does it work? It's simply evaluating ``%{toy_name}`` variable field\ninto \"horse\", which produces ``${horse@article}`` inner tag field, which\nis evaluated to \"rocking horse\" which is decorated using ``article``,\nand in the end we get \"a rocking horse\".\n\nGrammatical forms\n-----------------\n\n.. code:: json\n\n {\n \"announcement\": {\n \"en\": \"Hello! ${pol:%{policeperson}@article@capitalize} is here. %{pol:m?He|f?She} is going to help us.\"\n },\n \"john\": {\n \"en\": [\"policeman\", \"m\"]\n },\n \"judy\": {\n \"en\": [\"policewoman\", \"f\"]\n }\n }\n\nThen you can write:\n\n::\n\n >>> pys.t(\"announcement\", policeperson=\"john\")\n Hello! A policeman is here. He is going to help us.\n\nFor \"john\" key in specified JSON data there's a list instead of a single\nstring. The first element of the list is a value used for this key, the\nsecond is a grammatical form.\n\nAnother new thing is a \"pol\" identifier followed by a colon - both in an\ninner tag and a switch field. The first is tag's ID, which then can be\nused to specify some special tag options (which will be explained\nlater), but it can also be used as identifier of grammatical form which\ncan be used in switch field. So, in short, \"m\" form is taken from an\ninner tag and used in switch field to print \"He\". The use-case for such\nmechanism look quite slim for English, however it's very important in\nmany languages, where every noun has a grammatical form which can, for\nexample, affect form of adjectives.\n\nTag variants\n------------\n\nIt may happen that one tag is available in more than one form, which can\nfor example mean different suffix based on its context in the sentence.\nIt's hard to be shown in English, so I'll put an example in Polish:\n\n.. code:: json\n\n {\n \"having\": {\n \"en\": \"I have ${item_stone}.\",\n \"pl\": \"Mam ${item_stone}.\"\n },\n \"not_having\": {\n \"en\": \"I don't have ${item_stone}\",\n \"pl\": \"Nie mam ${item_stone#g}\"\n },\n \"item_stone\": {\n \"en\": \"a stone\",\n \"pl\": \"kamie\u0144\"\n },\n \"item_stone#g\": {\n \"pl\": \"kamienia\"\n }\n }\n\n::\n\n >>> pys_en.t(\"not_having\")\n I don't have a stone.\n >>> pys_pl.t(\"having\")\n Mam kamie\u0144.\n >>> pys_pl.t(\"not_having\")\n Nie mam kamienia.\n\nLet's take a look at the tag value of \"not\\_having\". In English it looks\nalmost the same as \"having\", but in Polish inner tag for item\\_stone has\n\"#g\" suffix, which makes it point at different tag. That is the tag's\nvariant, whose value has different suffix. What's the advantage of doing\nit instead of having own tag naming convention (e.g. \"stone\\_g\")? The\nfirst thing is previously highlighted fallback ability. When some tag\nkey contains variant which is unavailable in the database, then the more\nbasic form is used. That's why the most basic form (singular nominative)\nshould be defined without any variant. In case of lack of tag key and\nits basic form for a specified language, the tag or its base form is\nsearched for in the fallback language. Fallback mechanism is big and\ndetails can be found\n`here `__.\nAs you see, it's possible to adapt translations to the specified\nlanguage without any programmer's knowledge what language is going to be\nintroduced. All can be managed in translation system by creating tags\nwith correct variants.\n\nFormatting numbers\n------------------\n\nWhen you translate number being an interpolated variable then you must\ndecide if the used noun should be singular or plural. Pyslate supports\nthat easily by a special ``number`` variable:\n\n.. code:: json\n\n {\n \"having_flower\": {\n \"en\": \"I have a flower\"\n },\n \"having_flower#p\": {\n \"en\": \"I have %{number} flowers\"\n }\n }\n\n::\n\n >>> pys.t(\"having_flower\", number=1)\n I have a flower.\n >>> pys.t(\"having_flower\", number=5)\n I have 5 flowers.\n\nThese two forms are sufficient for English, but for many other languages\nit's not enough. For example words can have different suffixes when\nthere's a few of them and there's many of them. In Polish there are\nthree possibilities: singular (1), a few (2, 3, 4, 102, 103, 104...) and\nmany (all the rest). The word \"kwiat*ka*\" (genitive form of \"kwiat*ek*\"\n[\"a flower\"]) has the following plural forms: \"kwiatka\", \"kwiatki\",\n\"kwiatk\u00f3w\".\n\n.. code:: json\n\n {\n \"having_flower\": {\n \"pl\": \"Mam kwiatka\"\n },\n \"having_flower#w\": {\n \"pl\": \"Mam %{number} kwiatki.\"\n },\n \"having_flower#p\": {\n \"pl\": \"Mam %{number} kwiatk\u00f3w.\"\n }\n }\n\n`Every language can have different\nrules `__,\nso they are already configured for around 80 languages in ``locales.py``\nfile.\n\nCustom functions\n----------------\n\nIf none of previously mentioned options was a solution for your problem,\nthen custom functions come to the rescue. It's possible to create a\nmeta-tag being in fact a custom python function which can do almost\neverything and then return a translated tag.\n\n.. code:: json\n\n {\n \"product_presentation\": {\n \"en\": \"I'd like to present you a new product. It's ${product}.\"\n },\n \"car_personal\": {\n \"en\": \"a personal car\"\n },\n \"car_van\": {\n \"en\": \"a delivery van\"\n },\n \"product_template\": {\n \"en\": \"${%{type}} produced by %{producer}\"\n }\n }\n\nThen we have to create a custom function for a \"product\" inner tag\nfield:\n\n.. code:: python\n\n def product_fun(helper, name, params):\n product_id = params[\"product_id\"]\n product_db = {\n 1: dict(producer='BMW', capacity=1200),\n 7: dict(producer='Audi', capacity=2000)\n }\n product = product_db[product_id]\n if product[\"capacity\"] >= 1000:\n car_type = \"car_van\"\n else:\n car_type = \"car_personal\"\n return helper.translation(\n \"product_template\", \n type=car_type, \n producer=product[\"producer\"])\n\nIt gets keyword argument \"product\\_id\", query the database for a product\nand print some data related to it. Then it uses special helper object\nsupplied by Pyslate to translate a \"product\\_template\" tag, whose\nvariable fields are set by data got inside of the function. This way you\ncan almost be sure that you'll never have to alter custom functions to\nmake it work for some language. In general, every custom function should\nreturn a string which is a value of this pseudo-tag. Let's register that\nfunction:\n\n::\n\n >>> pys.register_function(\"product\", product_fun)\n\nNow let's use it:\n\n::\n\n >>> pys.t(\"product_presentation\", product_id=7)\n I'd like to present you a new product. It's a delivery van produced by Audi.\n\nIt works great. Note that if you need lots of custom functions in your\ncode, then probably you should not use a translation library for this\ntask. You also shouldn't misuse Pyslate as a templating engine, if you\nneed to interpolate variables into large documents, use Jinja2 or\nsimilar library.\n\nIntegration with templating engines\n-----------------------------------\n\nIf you use a templating engine, there are probably lots of static\nmessages in your template files that need to be translated and you need\na way to call Pyslate directly from them. Considering short tag keys and\neasy to use interface it's very simple to integrate with any template\nlanguage. I'll show how to get Pyslate work with Jinja2 and\nFlask-Jinja2, but it's just as easy for any other templating language\nwhich allows defining custom functions.\n\nJinja2 integration\n~~~~~~~~~~~~~~~~~~\n\nFor Jinja integration you need to get access to Jinja's env globals and\nregister two new functions there:\n\n.. code:: python\n\n env = Environment(loader=FileSystemLoader('/path/to/templates'))\n env.globals[\"t\"] = pyslate.t\n env.globals[\"l\"] = pyslate.l\n\nIn Flask it's just as easy. ``app.jinja_env.globals`` contains the dict\nof all global variables of jinja2 being used by Flask application\n``app``. So all you need to do, assuming instance of Pyslate is stored\nin ``g.pys`` is:\n\n::\n\n app.jinja_env.globals.update(t=lambda *args, **kwargs: g.pys.t(*args, **kwargs))\n app.jinja_env.globals.update(l=lambda *args, **kwargs: g.pys.l(*args, **kwargs))\n\nIt registers functions \"t\" and \"l\" which are lambdas delegating all the\ntranslations to pyslate object. I've used lambda, because flask's ``g``\nis accessible only when processing the request while the function\nregistration is better to be done during the application startup.", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/GreeKpl/pyslate", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "pyslate", "package_url": "https://pypi.org/project/pyslate/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/pyslate/", "project_urls": { "Homepage": "https://github.com/GreeKpl/pyslate" }, "release_url": "https://pypi.org/project/pyslate/1.1/", "requires_dist": [ "ply (>=3.4)", "six (>=1.10)" ], "requires_python": "", "summary": "A Python library for maintaining grammatically correct i18n (internationalization) of texts used inthe program: translation of messages, formatting dates and numbers to provide multi-language support.", "version": "1.1" }, "last_serial": 1926443, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "7c78fa806ba4e02263594942f1ef4088", "sha256": "e24edf66c6f440854c28d74c674513fbd1c0bda017ed86a2891c68ec445958ad" }, "downloads": -1, "filename": "pyslate-1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "7c78fa806ba4e02263594942f1ef4088", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 39038, "upload_time": "2015-12-18T23:13:31", "url": "https://files.pythonhosted.org/packages/7a/b1/342b5d0d6300743809ca519c9063892358f13284daa1b2c7659222839a8d/pyslate-1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "9bc80f201fb5cb763d636b1fb6d9ab66", "sha256": "a02cf07763b0d35f384c17f9b8ead3d85a5ff0a564ed536f5328f4d106409c2b" }, "downloads": -1, "filename": "pyslate-1.0.tar.gz", "has_sig": false, "md5_digest": "9bc80f201fb5cb763d636b1fb6d9ab66", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30150, "upload_time": "2015-12-18T23:13:37", "url": "https://files.pythonhosted.org/packages/b8/3f/77732e2e64e9f791d98f7cb9c90529946df57b15d79125500ea14f1e6e08/pyslate-1.0.tar.gz" } ], "1.1": [ { "comment_text": "", "digests": { "md5": "d91800c964c46f085c12d57db99bf8c2", "sha256": "1755866b3982af0972b4bd26306e2332a2763f669ff3dcb9296b3b88593a6624" }, "downloads": -1, "filename": "pyslate-1.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d91800c964c46f085c12d57db99bf8c2", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 39427, "upload_time": "2016-01-27T23:41:04", "url": "https://files.pythonhosted.org/packages/a8/a7/49ae549ff1f215fa81cd91d231ba7dc510150f0792f0334e68a938e8d7b6/pyslate-1.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1faafd5b56b60bb5e1e9756630821ade", "sha256": "c4ddc0cab07d28884078d5f530d2b0934fb36ddb8711d8c9708c8e89775e4a53" }, "downloads": -1, "filename": "pyslate-1.1.tar.gz", "has_sig": false, "md5_digest": "1faafd5b56b60bb5e1e9756630821ade", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30524, "upload_time": "2016-01-27T23:41:09", "url": "https://files.pythonhosted.org/packages/01/4f/f042cfd0aaaef8e74a79ea486aec4aa9e80825dd0bd85ca01f03b8b4f1da/pyslate-1.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "d91800c964c46f085c12d57db99bf8c2", "sha256": "1755866b3982af0972b4bd26306e2332a2763f669ff3dcb9296b3b88593a6624" }, "downloads": -1, "filename": "pyslate-1.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d91800c964c46f085c12d57db99bf8c2", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 39427, "upload_time": "2016-01-27T23:41:04", "url": "https://files.pythonhosted.org/packages/a8/a7/49ae549ff1f215fa81cd91d231ba7dc510150f0792f0334e68a938e8d7b6/pyslate-1.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1faafd5b56b60bb5e1e9756630821ade", "sha256": "c4ddc0cab07d28884078d5f530d2b0934fb36ddb8711d8c9708c8e89775e4a53" }, "downloads": -1, "filename": "pyslate-1.1.tar.gz", "has_sig": false, "md5_digest": "1faafd5b56b60bb5e1e9756630821ade", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30524, "upload_time": "2016-01-27T23:41:09", "url": "https://files.pythonhosted.org/packages/01/4f/f042cfd0aaaef8e74a79ea486aec4aa9e80825dd0bd85ca01f03b8b4f1da/pyslate-1.1.tar.gz" } ] }