From ed98cac76ec3c395c689e1a5befb4b97c9b46e78 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2008 08:42:11 +0200 Subject: [PATCH] some documentation updates --HG-- branch : trunk --- docs/_static/style.css | 46 +++++++++++-------- docs/_templates/layout.html | 1 + docs/_templates/search.html | 8 ++-- docs/api.rst | 21 ++++++--- docs/extensions.rst | 88 +++++++++++++++++++++++++++++++++++++ docs/index.rst | 6 +++ docs/integration.rst | 68 ++++++++++++++++++++++++++++ docs/intro.rst | 58 ++++++++++++++++++++++-- docs/templates.rst | 72 +++++++++++++++++++++++++++++- jinja2/environment.py | 6 ++- jinja2/ext.py | 15 ++++--- tests/test_i18n.py | 2 +- 12 files changed, 353 insertions(+), 38 deletions(-) create mode 100644 docs/extensions.rst create mode 100644 docs/integration.rst diff --git a/docs/_static/style.css b/docs/_static/style.css index 41877f7..3cce344 100644 --- a/docs/_static/style.css +++ b/docs/_static/style.css @@ -81,17 +81,6 @@ h1.heading span { display: none; } -h2.subheading { - margin: -55px 0 35px 200px; - font-weight: normal; - font-size: 30px; - color: #444; -} - -h2.plain { - margin: 0; -} - #jinjalogo { background-image: url(jinjalogo.png); background-repeat: no-repeat; @@ -108,15 +97,15 @@ h2.plain { background: url(watermark_blur.png) -120px -114px; } -#contentwrapper h3, -#contentwrapper h3 a { - color: #b41717; - font-size: 26px; - margin: 20px 0 0 -5px; +#contentwrapper h2, +#contentwrapper h2 a { + color: #222; + font-size: 24px; + margin: 20px 0 0 0; } -#contentwrapper h4, -#contentwrapper h4 a { +#contentwrapper h3, +#contentwrapper h3 a { color: #b41717; font-size: 20px; margin: 20px 0 0 0; @@ -324,3 +313,24 @@ dt .descclassname { dl dt big { font-size: 100%; } + +ul.search { + margin: 10px 0 0 30px; + padding: 0; +} + +ul.search li { + margin: 10px 0 0 0; + padding: 0; +} + +ul.search div.context { + font-size: 12px; + padding: 4px 0 0 20px; + color: #888; +} + +span.highlight { + background-color: #eee; + border: 1px solid #ccc; +} diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html index 0e80308..c2add7d 100644 --- a/docs/_templates/layout.html +++ b/docs/_templates/layout.html @@ -41,6 +41,7 @@ {%- if prev %} {%- endif %} + {% block extrahead %}{% endblock %}
diff --git a/docs/_templates/search.html b/docs/_templates/search.html index 82a78d0..ab93b75 100644 --- a/docs/_templates/search.html +++ b/docs/_templates/search.html @@ -11,10 +11,10 @@ function will automatically search for all of the words. Pages containing less words won't appear in the result list.

-
- - -
+

+ + +

{% if search_performed %}

Search Results

{% if not search_results %} diff --git a/docs/api.rst b/docs/api.rst index 0b33a43..24e2710 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -47,8 +47,8 @@ To render it with some variables, just call the :meth:`render` method:: High Level API -------------- -.. autoclass:: jinja2.environment.Environment - :members: from_string, get_template, join_path, overlay +.. autoclass:: jinja2.environment.Environment([options]) + :members: from_string, get_template, join_path, parse .. attribute:: shared @@ -83,17 +83,28 @@ High Level API overridden by templates. As long as no template was loaded it's safe to modify this dict. For more details see :ref:`global-namespace`. + .. automethod:: overlay([options]) + .. autoclass:: jinja2.Template - :members: render, stream, generate, make_module, module + :members: make_module, module, new_context .. attribute:: globals - foo + The dict with the globals of that template. It's unsafe to modify + this dict as it may be shared with other templates or the environment + that loaded the template. .. attribute:: name - foo + The loading name of the template. If the template was loaded from a + string this is `None`. + + .. automethod:: render([context]) + + .. automethod:: generate([context]) + + .. automethod:: stream([context]) .. autoclass:: jinja2.environment.TemplateStream diff --git a/docs/extensions.rst b/docs/extensions.rst new file mode 100644 index 0000000..3ad4914 --- /dev/null +++ b/docs/extensions.rst @@ -0,0 +1,88 @@ +.. _jinja-extensions: + +Extensions +========== + +.. module:: jinja2.ext + +Jinja2 supports extensions that can add extra filters, tests, globals or even +extend the parser. The main motivation of extensions is it to move often used +code into a reusable class like adding support for internationalization. + + +Adding Extensions +----------------- + +Extensions are added to the Jinja2 environment at creation time. Once the +environment is created additional extensions cannot be added. To add an +extension pass a list of extension classes or import paths to the +`environment` parameter of the :class:`Environment` constructor. The following +example creates a Jinja2 environment with the i18n extension loaded:: + + jinja_env = Environment(extensions=['jinja.ext.i18n']) + + +Built-in Extensions +------------------- + +.. _i18n-extension: + +i18n +~~~~ + +The i18n extension can be used in combination with `gettext`_ or `babel`_. +If the i18n extension is enabled Jinja2 provides a `trans` statement that +marks the wrapped string as translatable and calls `gettext`. + +After enabling dummy `_`, `gettext` and `ngettext` functions are added to +the template globals. A internationalized application has to override those +methods with more useful versions. + +For a web application that is available in multiple languages but gives all +the users the same language (for example a multilingual forum software +installed for a French community) may load the translations once and add the +translation methods to the environment at environment generation time:: + + translations = get_gettext_translations() + env = Environment(extensions=['jinja.ext.i18n']) + env.globals.update( + gettext=translations.ugettext, + ngettext=translations.ungettext + ) + +The `get_gettext_translations` function would return the translator for the +current configuration. Keep in mind that Jinja2 uses unicode internally so +you must pass the `ugettext` and `ungettext` functions to the template. + +The default `_` function injected by the extension calls `gettext` +automatically. + +If you want to pass the gettext function into the context at render time +because you don't know the language/translations earlier and the optimizer +is enabled (which it is per default), you have to unregister the `gettext` +and `ugettext` functions first:: + + del env.globals['gettext'], env.globals['ugettext'] + +Jinja2 also provides a way to extract recognized strings. For one the +`jinja.ext` module provides a function that can return all the occurences +of gettext calls in a node (as returned by :meth:`Environment.parse`): + +.. autofunction:: extract_from_ast + +If `babel`_ is installed :ref:`the babel integration ` +can be used to. + +The usage of the `i18n` extension for template designers is covered as part +:ref:`of the template documentation `. + + +.. _gettext: http://docs.python.org/dev/library/gettext +.. _babel: http://babel.edgewall.org/ + +.. _writing-extensions: + +Writing Extensions +------------------ + +TODO diff --git a/docs/index.rst b/docs/index.rst index a6c8bf8..eb442cb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,7 +11,13 @@ fast and secure. intro api templates + extensions + integration changelog +If you can't find the information you're looking for, have a look at the +index of try to find it using the search function: + * :ref:`genindex` +* :ref:`search` diff --git a/docs/integration.rst b/docs/integration.rst new file mode 100644 index 0000000..e5c2cb7 --- /dev/null +++ b/docs/integration.rst @@ -0,0 +1,68 @@ +Integration +=========== + +Jinja2 provides some code for integration into other tools such as frameworks, +the `Babel`_ library or your favourite editor for fancy code highlighting. +This is a brief description of whats included. + +.. _babel-integration: + +Babel Integration +----------------- + +Jinja provides support for extracting gettext messages from templates via a +`Babel`_ extractor entry point called `jinja2.ext.babel_extract`. The Babel +support is implemented as part of the :ref:`i18n-extension` extension. + +Gettext messages extracted from both `trans` tags and code expressions. + +To extract gettext messages from templates, the project needs a Jinja2 section +in its Babel extraction method `mapping file`_: + +.. sourcecode:: ini + + [jinja: **/templates/**.html] + encoding = utf-8 + +The syntax related options of the :class:`Environment` are also available as +configuration values in the mapping file. For example to tell the extraction +that templates use ``%`` as `line_statement_prefix` you can use this code: + +.. sourcecode:: ini + + [jinja: **/templates/**.html] + encoding = utf-8 + line_statement_prefix = % + +:ref:`jinja-extensions` may also be defined by passing a comma separated list +of import paths as `extensions` value. The i18n extension is added +automatically. + +.. _mapping file: http://babel.edgewall.org/wiki/Documentation/messages.html#extraction-method-mapping-and-configuration + +Django +------ + +TODO + +Pylons +------ + +TODO + +WSGI +---- + +TODO + +TextMate +-------- + +TODO + +Vim +--- + +TODO + +.. _Babel: http://babel.edgewall.org/ diff --git a/docs/intro.rst b/docs/intro.rst index 981401c..e1d07eb 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -13,7 +13,7 @@ useful for templating environments. The key-features are... - ... **configurable syntax**. If you are generating LaTeX or other formats - with Jinja you can change the delimiters to something that integrates better + with Jinja2 you can change the delimiters to something that integrates better into the LaTeX markup. - ... **fast**. While performance is not the primarily target of Jinja2 it's @@ -21,7 +21,7 @@ The key-features are... to the very minimum. - ... **easy to debug**. Jinja2 integrates directly into the python traceback - system which allows you to debug Jinja templates with regular python + system which allows you to debug Jinja2 templates with regular python debugging helpers. - ... **secure**. It's possible to evaluate untrusted template code if the @@ -39,10 +39,62 @@ C-compiler is available the `ctypes`_ module should be installed. .. _ctypes: http://python.net/crew/theller/ctypes/ +Installation +------------ + +You have multiple ways to install Jinja2. If you are unsure what to do, go +with the Python egg or tarball. + +As a Python egg (via easy_install) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can install the most recent Jinja2 version using `easy_install`_:: + + sudo easy_install Jinja2 + +This will install a Jinja2 egg in your Python installation's site-packages +directory. + +From the tarball release +~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Download the most recent tarball from the `download page`_ +2. Unpack the tarball +3. ``sudo python setup.py install`` + +Note that the last command will automatically download and install +`setuptools`_ if you don't already have it installed. This requires a working +internet connection. + +This will install Jinja2 into your Python installation's site-packages directory. + +Installing the development version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Install `mercurial`_ +2. ``svn co http://dev.pocoo.org/hg/jinja2-main jinja2`` +3. ``cd jinja2`` +4. ``ln -s jinja2 /usr/lib/python2.X/site-packages`` + +As an alternative to steps 4 you can also do ``python setup.py develop`` +which will install the package via setuptools in development mode. This also +has the advantage that the C extensions are compiled. + +Alternative you can use `easy_install`_ to install the current development +snapshot:: + + sudo easy_install Jinja2==dev + +.. _download page: http://jinja.pocoo.org/2/download +.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools +.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall +.. _mercurial: http://www.selenic.com/mercurial/ + + Basic API Usage --------------- -This section gives you a brief introduction to the Python API for Jinja templates. +This section gives you a brief introduction to the Python API for Jinja2 templates. The most basic way to create a template and render it is through :class:`Template`. This however is not the recommended way to work with it, diff --git a/docs/templates.rst b/docs/templates.rst index 615884a..d7a0f91 100644 --- a/docs/templates.rst +++ b/docs/templates.rst @@ -928,7 +928,77 @@ The following functions are available in the global scope by default: is disabled regular text is returned. This is useful to generate simple contents for layout testing. -.. function:: dict(**items) +.. function:: dict(\**items) A convenient alternative to dict literals. ``{'foo': 'bar'}`` is the same as ``dict(foo='bar')``. + + +Extensions +---------- + +The following sections cover the built-in Jinja2 extensions that may be +enabled by the application. The application could also provide further +extensions not covered by this documentation. In that case there should +be a separate document explaining the extensions. + +.. _i18n-in-templates: + +i18n +~~~~ + +If the i18n extension is enabled it's possible to mark parts in the template +as translatable. To mark a section as translatable you can use `trans`:: + +

{% trans %}Hello {{ user }}!{% endtrans %}

+ +To translate a template expression --- say, using template filters or just +accessing an attribute of an object --- you need to bind the expression to a +name for use within the translation block:: + +

{% trans user=user.username %}Hello {{ user }}!{% endtrans %}

+ +If you need to bind more than one expression inside a `trans` tag, separate +the pieces with a comma (``,``):: + + {% trans book_title=book.title, author=author.name %} + This is {{ book_title }} by {{ author }} + {% endtrans %} + +Inside trans tags no statements are allowed, only variable tags are. + +To pluralize, specify both the singular and plural forms with the `pluralize` +tag, which appears between `trans` and `endtrans`:: + + {% trans count=list|length %} + There is {{ count }} {{ name }} object. + {% pluralize %} + There are {{ count }} {{ name }} objects. + {% endtrans %} + +Per default the first variable in a block is used to determine the correct +singular or plural form. If that doesn't work out you can specify the name +which should be used for pluralizing by adding it as parameter to `pluralize`:: + + {% trans ..., user_count=users|length %}... + {% pluralize user_count %}...{% endtrans %} + +It's also possible to translate strings in expressions. For that purpose +three functions exist: + +_ `gettext`: translate a single string +- `ngettext`: translate a pluralizable string +- `_`: alias for `gettext` + +For example you can print a translated string easily this way:: + + {{ _('Hello World!') }} + +To use placeholders you can use the `format` filter:: + + {{ _('Hello %(user)s!')|format(user=user.username) }} + or + {{ _('Hello %s')|format(user.username) }} + +For multiple placeholders always use keyword arguments to `format` as other +languages may not use the words in the same order. diff --git a/jinja2/environment.py b/jinja2/environment.py index 92ab578..23e77b6 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -112,7 +112,8 @@ class Environment(object): `extensions` List of Jinja extensions to use. This can either be import paths - as strings or extension classes. + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation `. `optimized` should the optimizer be enabled? Default is `True`. @@ -282,6 +283,9 @@ class Environment(object): tree of nodes is used by the compiler to convert the template into executable source- or bytecode. This is useful for debugging or to extract information from templates. + + If you are :ref:`developing Jinja2 extensions ` + this gives you a good overview of the node tree generated. """ try: return Parser(self, source, filename).parse() diff --git a/jinja2/ext.py b/jinja2/ext.py index c35a9c2..5d2251d 100644 --- a/jinja2/ext.py +++ b/jinja2/ext.py @@ -15,7 +15,7 @@ from jinja2 import nodes from jinja2.environment import get_spontaneous_environment from jinja2.runtime import Undefined, concat from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError -from jinja2.utils import import_string, Markup +from jinja2.utils import contextfunction, import_string, Markup # the only real useful gettext functions for a Jinja template. Note @@ -74,14 +74,14 @@ class CacheExtension(Extension): ) -class TransExtension(Extension): +class InternationalizationExtension(Extension): """This extension adds gettext support to Jinja.""" tags = set(['trans']) def __init__(self, environment): Extension.__init__(self, environment) environment.globals.update({ - '_': lambda x: x, + '_': contextfunction(lambda c, x: c['gettext'](x)), 'gettext': lambda x: x, 'ngettext': lambda s, p, n: (s, p)[n != 1] }) @@ -284,11 +284,11 @@ def babel_extract(fileobj, keywords, comment_tags, options): if not extension: continue extension = import_string(extension) - if extension is TransExtension: + if extension is InternationalizationExtension: have_trans_extension = True extensions.append(extension) if not have_trans_extension: - extensions.append(TransExtension) + extensions.append(InternationalizationExtension) environment = get_spontaneous_environment( options.get('block_start_string', '{%'), @@ -310,3 +310,8 @@ def babel_extract(fileobj, keywords, comment_tags, options): node = environment.parse(fileobj.read().decode(encoding)) for lineno, func, message in extract_from_ast(node, keywords): yield lineno, func, message, [] + + +#: nicer import names +i18n = InternationalizationExtension +cache = CacheExtension diff --git a/tests/test_i18n.py b/tests/test_i18n.py index d405c9c..3b72283 100644 --- a/tests/test_i18n.py +++ b/tests/test_i18n.py @@ -46,7 +46,7 @@ def ngettext(context, s, p, n): i18n_env = Environment( loader=DictLoader(templates), - extensions=['jinja2.ext.TransExtension'] + extensions=['jinja2.ext.i18n'] ) i18n_env.globals.update({ '_': gettext, -- 2.26.2