.. highlight:: xml
.. _language-specification:
Language specification
----------------------
A Curtain template specifies a way of **transforming XML documents into XML
documents**, much like :term:`TAL` and XSLT do. A Curtain template is an XML
document with some more extra attributes and tags which specify how the
computation is made. Most of the values of the extra attributes contain Python
expressions.
The namespace of these Python expressions is initialized through the
:term:`environment`. An **environment** is a Python dictionary between string
(names) and arbitrary objects. When the execution begins, this mapping is
copied as-is in the namespace, which can then be modified by some instructions
(like :ref:`cdefn` or :ref:`cloop`). So, for example, if you have an
environment like ``{'name': u'Joe', 'surname': u'Black'}``, all the Python
expressions will see a variable ``name`` and a variable ``surname``
[#expression]_.
The `namespace `_ for Curtain
attributes and tags is ``http://curtain.sourceforge.net/NS/curtain``. The
default **namespace** prefix used all through the documentation will be simply
``c``.
The value of the Curtain attributes is considered as the argument(s) of the
processing instruction. The value of attributes can be parsed in three
different ways, and all processing instructions use one of these (see the
glossary for their definition and how they work):
- :term:`simple attribute`
- :term:`single attribute`
- :term:`list attribute`
In the following section the various attributes are described.
Base language
^^^^^^^^^^^^^
Here we have the list of tags and attributes which contains the core of the language.
.. _ccont:
c:cont
""""""
:term:`simple attribute`
Replace the content of the tag on which this attribute is applied. The
attribute value is evaluated as a Python expression, and the result replaces
the content of the attribute as character data. All previous content is
replaced by this character data. E.g., if you use this Curtain template::
With the environment ``{'words': [u'Hello', u'world']}``, the result is::
Hello world
.. _cdefn:
c:defn
""""""
:term:`list attribute`
Adds new names (or temporarily overrides existing one's values) in the
namespace. For every piece of the attribute, this processing instruction
evaluates the second part as a Python expression and assign the result value to
the variable whose name is the first part of the expression. The new names will
be visible only for the expressions evaluated between the start and end of the
tag on which it's used the ``c:defn`` attribute and will shadow any previous
name (unshadowing it again when the tag is closed). E.g., if you use this
Curtain template::
And call it with an environment of ``{'x': u'oldvalue'}``, the result will be::
oldvaluenewvalueother valueoldvalue
.. _cloop:
c:loop
""""""
:term:`single attribute`
This attribute causes the subtree rooted at current tag to be produce many
times in the output. The Python expression which is in the second part of the
attribute value is evaluated and must return an iterable (list, generator,
tuple). This iterable is then looped over using the variable whose name is the
first part of the attribute value. E.g., if you evaluate the following
template::
The name is !
Over the environment ``{'all_names': [u'Joe', u'Mark', u'Albert']}``, the
result is [#formatted]_::
The name is Joe!
The name is Mark!
The name is Albert!
.. _ccond:
c:cond
""""""
:term:`simple attribute`
The attribute value is evaluated as a Python expression, converted to ``bool``,
and if it evaluates to a ``False`` value, the tag and all its children are
removed from the output. E.g., if you have a template like::
Hello !Welcome visitor!
And an environment of ``{'username': u'Bobby'}``, the result will be::
Hello Bobby!
Whereas with an environment of ``{'username': None}`` you would have::
Welcome visitor!
.. _cattr:
c:attr
""""""
:term:`list attribute`
Whereas :ref:`ccont` defines the content of a tag, :ref:`cattr` defines the
attributes. For each piece of the attribute value, the first part is
interpreted as the attribute name, and the second part as a Python expression
which, once converted to unicode, produces the attribute value. E.g., this
template::
value
square
With an empty environment produces the following XML [#formatted]_::
value
square
0
0
1
1
2
4
3
9
4
16
5
25
6
36
7
49
8
64
9
81
.. _cskip:
c:skip
""""""
:term:`simple attribute`
The skip processing instruction is substantially the same of :ref:`ccond`, but
removes just the tag itself in case the condition evaluates to ``False``, not
all the subtree. So, with a template like::
.. _cc:
c:c
"""
This is the only tag of Curtain. This tag is simply omitted, and is useful
just for attaching other attributes to it so to apply transformations to parts
of text instead of just the content of whole tags. E.g., this template::
Welcome !
With the environment ``{'username': u'Joe'}``, produce this XML::
Welcome Joe!
Macro system
^^^^^^^^^^^^
Most of the time, and especially when developing non-trivial websites, you have
a common structure for many web pages where some parts of them, called
:term:`slots `, change according to the section. A tipical example of
changing slots would be the central and left column of a web page, whereas the
header and footer of it remains the same all throughout the site.
:term:`Macros ` are a convenient mechanism to implement the automatisms needed
for such a task. A macro is just a template like any other which marks some of
its subtrees as :term:`slots `. Any other template which has a reference to
this template object through its environment can then use the macro specifying
how to fill some or all of the slots.
.. _cslot:
c:slot
""""""
:term:`simple attribute`
This attribute mark the subtree of the element where it is applied as a slot,
and the value of the attribute is the symbolic name through which the slot is
referred to. This means that when a slot substitution will be performed, this
element and all its children will be removed and replaced by the value given.
If no slot substitution is performed, the tree rooted at this element will be
kept.
.. _cuse:
c:use
"""""
:term:`simple attribute`
The attribute value is evaluated as a Python expression, and must return a
template. Through this tag you declared that the element and all its content
are ignored (except for the subtrees whose roots are tagged with the
:ref:`cfill` attribute) and replaced by the content of the macro. The slots of
the macro can be filled thanks to the :ref:`cfill` attribute.
.. _cfill:
c:fill
""""""
:term:`simple attribute`
This attribute is valid only in an element which is a descendant of some
element tagged with a :ref:`cuse` attribute. It specifies that this element and
all its children will replace the slot of the macro in use with the same name
as the value of the attribute :ref:`cfill`.
Example
"""""""
The use of the :ref:`cuse`, :ref:`cslot` and :ref:`cfill` tags it's much
clearer through an example. Let's say you have ``macro.ct``, which is this
(macro) template::
Welcome to The Page!left column
And also this other template, ``home.ct``::
IndexWelcome to our site.
Then you could wire together this code:
.. code-block:: python
from xml.sax.saxutils import XMLGenerator
from curtain import Template
macro = Template('macro.ct')
home = Template('home.ct')
xml_generator = XMLGenerator('home.xml')
home(xml_generator, env = {'macro': macro})
Which would produce this XML::
Welcome to The Page!IndexWelcome to our site.
But then, if you have another page, e.g. ``credits.ct``::
The authors are Tom, Jerry and
Mark.
You can just add a couple more lines of code:
.. code-block:: python
credits = Template('credits.ct')
xml_generator = XMLGenerator('credits.xml')
credits(xml_generator, env = {'macro': macro})
And this would produce the ``credits.xml``::
Welcome to The Page!left columnThe authors are Tom, Jerry and
Mark.
(Notice that the ``leftcolumn`` slot was not specified, so it was left
unchanged from the master macro)
Internationalization
^^^^^^^^^^^^^^^^^^^^
When an application must be used by a number of different people coming from
various cultures (and thus often different languages), the development has a
difficult additional task. The main problem regards the internationalization of
the application - that is, the task of engineering and factoring the various
aspects of the application input/output which can change according to the
user's culture: strings, dates, lengths, ...
Curtain tackles the problem of internationalization just for what regards the
translation of text. To do so, it uses Zope's i18n machinery, which is mostly
based on the `ITranslationDomain
`_
interface and the `translate
`_ method.
Curtain offers some tags to mark text which must be translated, and then the
correct ``ITranslationDomain`` is queried for the translation of the message
itself.
.. _ctranslate:
c:translate
"""""""""""
:term:`simple attribute`
The value of this attribute is ignored. The tag just marks the fact that the
characters contents of this element need to be translated. E.g., if we write
this template::
Welcome to our site!
We are expressing the fact that the string ``u'Welcome to our site!'`` must be
given to the translation service, which will return the translated string to
put in there. To know how the translation service is queried, see
:ref:`translationprocedure`.
The situation gets more complicated when the data inside the translation unit
is not just characters data, but there are also some tags. In this case, see
:ref:`cname`.
.. _cname:
c:name
""""""
:term:`simple attribute`
The :ref:`cname` attribute resolves the problem of having tags in the middle of
the translation unit's characters data. E.g.::
Welcome back, !
Here, the translation unit has some tag in the middle, but it would be good to
translate the message as a whole, without splitting it in two parts and
translating the two parts separately. Also because we could have more complex
cases, like::
I am sure that was in here the last time.
In this case, according to the language, it could happen that the position of
the two tags gets inverted. How can we manage all these cases? The solution is
to put a name on the tags which are in the middle of a translation unit::
I am sure that was in
here the .
If you do that, the message passed to the translation system will be ``u'I am
sure that ${username} was in here the ${date}'``. Translations are required to
return back these special tags (called *interpolations*), and they will be
substituted by the corresponding XML trees. Translation units can also be
nested this way::
Welcome back, Mr.
.
In this case, we will ask the translation service a translation for the string
``u'Mr. ${username}'``, and then another one for ``u'Welcome back,
${usertag}'``.
.. _cdomain:
c:domain
""""""""
:term:`simple attribute`
The value of this attribute sets the current :term:`translation domain`. This
translation domain will be used until the end of the element with this
attribute, where the old translation domain will be restored.
.. _translationprocedure:
Translation procedure
"""""""""""""""""""""
Every time there's a message to translate, be it a plain message or containing
some interpolation, the Zope's i18n machinery is queried through the `translate
`_ method. The
arguments are:
- ``msgid``: the string to translate, containing interpolations or not.
- ``domain``: the last domain set by a :ref:`cdomain` attribute, or ``None`` if
no translation domain has been set (which is something to avoid).
- ``context``: the translation context as passed from the template. This is
tipically a "request" object of the web framework in use which is then
translated (adapted) to obtain a language.
As you can notice, no value is given to ``mapping``, so tipically no
substitution of the mapping values is performed. This is then internally
executed by the Curtain system, and the translation system has not to worry
about it.
.. rubric:: Footnotes
.. [#expression] Since only Python *expressions* are allowed and no Python
*statements*, the only way to change a variable's value is by
"overriding" it through some processing instruction like
:ref:`cdefn`.
.. [#formatted] The XML has been formatted to improve legibility.