Edgewall Software

Changes between Version 42 and Version 43 of TracDev/PortingFromGenshiToJinja


Ignore:
Timestamp:
Jan 29, 2017, 8:57:26 PM (7 years ago)
Author:
Christian Boos
Comment:

add #i18n section for plugins, with the SpamFilter taken as example

Legend:

Unmodified
Added
Removed
Modified
  • TracDev/PortingFromGenshiToJinja

    v42 v43  
    8585In this small example, there's no common Trac layout used (as the index is a bit special).
    8686For how a "normal" template looks like, see for example
    87 [source:cboos.git/trac/templates/diff_view.html@jinja2-trunk-r15379 diff_form.html], another small template. The generic templates in [source:cboos.git/trac/templates@jinja2-trunk-r15379 trac/templates] are also interesting because we still have their Genshi equivalent close by, in [source:cboos.git/trac/templates/genshi@jinja2-trunk-r15379 trac/templates/genshi], so you can easily compare them if you're stuck during the migration of your own templates.
     87[source:cboos.git/trac/templates/diff_view.html@jinja2-trunk-r15379 diff_view.html], another small template. The generic templates in [source:cboos.git/trac/templates@jinja2-trunk-r15379 trac/templates] are also interesting because we still have their Genshi equivalent close by, in [source:cboos.git/trac/templates/genshi@jinja2-trunk-r15379 trac/templates/genshi], so you can easily compare them if you're stuck during the migration of your own templates.
    8888
    8989Note that a Jinja2 .html template can usually be rendered directly in the browser, to have a rough idea about how it will look like:
     
    132132== Changes in the controllers ==
    133133
    134 === Implementing the `IRequestHandler` interface
     134=== Implementing the `IRequestHandler` interface #IRequestHandler
    135135
    136136With Genshi, the data for the template is basically a `dict`, which has to be returned by `process_request`
     
    452452See the full changeset, [bf49f871/cboos.git] (and [be0ff94a/cboos.git] for a look to the generated .js).
    453453
     454=== i18n support for plugins #i18n
     455
     456The basics remain the same, we use Babel for extracting the source strings from Jinja2 templates and for performing the translations at runtime. One notable differences though is how the //domain// used by the plugin specific catalogs is specified. With Genshi, the templates themselves contained a directive which specified the name of the domain, while Jinja2 doesn't provide this facility. It is therefore up to the controller to do this job, and we use for that the //metadata// `dict` returned by [#IRequestHandler IRequestHandler.process_request].
     457
     458The specification for the extraction is slightly more verbose than it use to be with Genshi. As we use a syntax for the Jinja2 templates which is different than the default ([#TheJinja2syntaxusedbyTrac see below]), it has to be specified for the extractor as well.
     459
     460In what follows, we'll take the example of the SpamFilter plugin.
     461
     462In the `setup.cfg`, we added the `mapping_file` parameter to the `[extract_messages]` section.
     463{{{#!ini
     464[extract_messages]
     465add_comments = TRANSLATOR:
     466msgid_bugs_address = trac@dstoecker.de
     467output_file = tracspamfilter/locale/messages.pot
     468keywords = _ ngettext:1,2 N_ tag_ cleandoc_ Option:4 BoolOption:4 IntOption:4 ChoiceOption:4 ListOption:6 ExtensionOption:5 ConfigSection:2
     469width = 72
     470mapping_file = messages.cfg
     471}}}
     472The other i18n sections are unchanged.
     473
     474This makes it possible to specify all the extraction details in a specific configuration file, `messages.cfg`:
     475{{{#!ini
     476# mapping file for extracting messages from Jinja2 templates into
     477# trac/locale/messages.pot (see setup.cfg)
     478
     479[extractors]
     480python = trac.dist:extract_python
     481html = trac.dist:extract_html
     482text = trac.dist:extract_text
     483
     484[python: **.py]
     485
     486[html: **/templates/**.html]
     487extensions = jinja2.ext.with_
     488variable_start_string = ${
     489variable_end_string = }
     490line_statement_prefix = #
     491line_comment_prefix = ##
     492trim_blocks = yes
     493lstrip_blocks = yes
     494newstyle_gettext = yes
     495
     496[text: **/templates/**.txt]
     497extensions = jinja2.ext.with_
     498variable_start_string = ${
     499variable_end_string = }
     500line_statement_prefix = #
     501line_comment_prefix = ##
     502trim_blocks = yes
     503lstrip_blocks = yes
     504newstyle_gettext = yes
     505}}}
     506The `html` extractor (i.e. the `trac.dist.extract_html` function) will be applied to all HTML files `**/templates/**.html`, while the `text` extractor (the `trac.dist.extract_text` function). wil be applied on the "text" files `**/templates/**.txt`.
     507
     508The HTML extractor "auto-detects" in a simple but effective way the presence of Genshi i18n directives, and will use the legacy Genshi extractor in that case. It is therefore safe to use this new way even before the integrality of the templates has been migrated to Jinja2.
     509
     510Note that as we have a dedicated mapping file anyway, we specify the extractors directly there, so we no longer needs to do that in the setup.py file:
     511{{{#!diff
     512Index: setup.py
     513===================================================================
     514--- setup.py    (revision 15351)
     515+++ setup.py    (revision 15352)
     516@@ -23,13 +23,6 @@
     517     cmdclass = get_l10n_cmdclass()
     518     if cmdclass:
     519         extra['cmdclass'] = cmdclass
     520-        extractors = [
     521-            ('**.py',                'trac.dist:extract_python', None),
     522-            ('**/templates/**.html', 'genshi', None)
     523-        ]
     524-        extra['message_extractors'] = {
     525-            'tracspamfilter': extractors,
     526-        }
     527 except ImportError:
     528     pass
     529}}}
     530
     531See r15352 for the full changeset.
     532
     533Finally, as mentioned earlier, we need to specify the domain from the Python code:
     534{{{#!diff
     535Index: tracspamfilter/admin.py
     536===================================================================
     537--- tracspamfilter/admin.py     (revision 15352)
     538+++ tracspamfilter/admin.py     (revision 15353)
     539@@ -113,6 +113,8 @@
     540 
     541         add_stylesheet(req, 'spamfilter/admin.css')
     542         data['accmgr'] = 'ACCTMGR_USER_ADMIN' in req.perm
     543+        if page == 'config':
     544+            return 'admin_spamconfig.html', data, {'domain': 'tracspamfilter'}
     545         return 'admin_spam%s.html' % page, data, None
     546 
     547     # ITemplateProvider methods
     548}}}
     549In that case, only the `'admin_spamconfig.html'` template has been converted to Jinja2, the other templates remain Genshi templates, hence the value of `None` as third member of the return tuple for them.
     550
     551See r15353 for the full changeset.
     552
    454553
    455554== Migrating to Jinja2 templates
     
    768867If you wonder why the `if all(...)` expression morphed into `if not components|...`, it's because Jinja2 expressions are similar to Python expressions, but not quite the same.
    769868
    770 === set complex variables   #set
     869
     870==== set complex variables   #set
    771871Note that Jinja2 expressions are a subset of Python expressions, and for the sake of simplicity the generator expressions are not part of that subset. This limitation often requires one to make creative use of [http://jinja.pocoo.org/docs/dev/templates/#filters filters], [http://jinja.pocoo.org/docs/dev/templates/#builtin-filters built-in] or custom (`min`, `max`, `trim`, `flatten`).
    772872