i18n_subsites: add lang_subsites template var, improve docs
- the new variable makes it easier to implement language buttons - a short howto on language buttons is also provided - the dictionary mapping template vars are now OrderedDict for consistent-looking language buttons
This commit is contained in:
parent
aa5c414d0b
commit
4c33944bb9
22
README.rst
22
README.rst
@ -8,7 +8,7 @@ What it does
|
||||
============
|
||||
1. The *\*_LANG_URL* and *\*_LANG_SAVE_AS* variables are set to their normal counterparts (e.g. *ARTICLE_URL*) so they don't conflict with this scheme.
|
||||
2. While building the site for *DEFAULT_LANG* the translations of pages and articles are not generated, but their relations to the original content is kept via links to them.
|
||||
3. For each non-default language a "sub-site" with a modified config [#conf]_ is created [#run]_, linking the translations to the originals (if available). The configured language code is appended to the *OUTPUT_PATH* and *SITEURL* of each sub-site.
|
||||
3. For each non-default language a "sub-site" with a modified config [#conf]_ is created [#run]_, linking the translations to the originals (if available). The configured language code is appended to the *OUTPUT_PATH* and *SITEURL* of each sub-site. For each sub-site, *DEFAULT_LANG* is changed to the language of the sub-site so that articles in a different language are treated as translations.
|
||||
|
||||
If *HIDE_UNTRANSLATED_CONTENT* is True (default), content without a translation for a language is generated as hidden (for pages) or draft (for articles) for the corresponding language sub-site.
|
||||
|
||||
@ -18,7 +18,9 @@ If *HIDE_UNTRANSLATED_CONTENT* is True (default), content without a translation
|
||||
Setting it up
|
||||
=============
|
||||
|
||||
For each extra used language code, a language-specific variables overrides dictionary must be given (but can be empty) in the *I18N_SUBSITES* dictionary::
|
||||
For each extra used language code, a language-specific variables overrides dictionary must be given (but can be empty) in the *I18N_SUBSITES* dictionary
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
PLUGINS = ['i18n_subsites', ...]
|
||||
|
||||
@ -40,18 +42,24 @@ Most importantly, this plugin can use localized templates for each sub-site. The
|
||||
- You can set a different *THEME* override for each language in *I18N_SUBSITES*, e.g. by making a copy of a theme ``my_theme`` to ``my_theme_lang`` and then editing the templates in the new localized theme. This approach means you don't have to deal with gettext ``*.po`` files, but it is harder to maintain over time.
|
||||
- You use only one theme and localize the templates using the `jinja2.ext.i18n Jinja2 extension <http://jinja.pocoo.org/docs/templates/#i18n>`_. For a kickstart read this `guide <./localizing_using_jinja2.rst>`_.
|
||||
|
||||
It may be convenient to add language buttons to your theme in addition to the translation links. These buttons could, for example, point to the *SITEURL* of each (sub-)site. For this reason the plugin adds these variables to the template context:
|
||||
It may be convenient to add language buttons to your theme in addition to the translation links of articles and pages. These buttons could, for example, point to the *SITEURL* of each (sub-)site. For this reason the plugin adds these variables to the template context:
|
||||
|
||||
extra_siteurls
|
||||
A dictionary mapping languages to their *SITEURL*. The *DEFAULT_LANG* language of the current sub-site is not included, so this dictionary serves as a complement to current *DEFAULT_LANG* and *SITEURL*. This dictionary is useful for implementing global language buttons.
|
||||
main_lang
|
||||
The language of the top-level site — the original *DEFAULT_LANG*
|
||||
main_siteurl
|
||||
The *SITEURL* of the top-level site — the original *SITEURL*
|
||||
lang_siteurls
|
||||
An ordered dictionary, mapping all used languages to their *SITEURL*. The ``main_lang`` is the first key with ``main_siteurl`` as the value. This dictionary is useful for implementing global language buttons that show the language of the currently viewed (sub-)site too.
|
||||
extra_siteurls
|
||||
An ordered dictionary, subset of ``lang_siteurls``, the current *DEFAULT_LANG* of the rendered (sub-)site is not included, so for each (sub-)site ``set(extra_siteurls) == set(lang_siteurls) - set([DEFAULT_LANG])``. This dictionary is useful for implementing global language buttons that do not show the current language.
|
||||
|
||||
If you don't like the default ordering of the ordered dictionaries, use a Jinja2 filter to alter the ordering.
|
||||
|
||||
This short `howto <./implementing_language_buttons.rst>`_ shows two example implementations of language buttons.
|
||||
|
||||
Usage notes
|
||||
===========
|
||||
- It is **mandatory** to specify *lang* metadata for each article and page as *DEFAULT_LANG* is later changed for each sub-site.
|
||||
- It is **mandatory** to specify *lang* metadata for each article and page as *DEFAULT_LANG* is later changed for each sub-site, so content without *lang* metadata woudl be rendered in every (sub-)site.
|
||||
- As with the original translations functionality, *slug* metadata is used to group translations. It is therefore often convenient to compensate for this by overriding the content URL (which defaults to slug) using the *URL* and *Save_as* metadata.
|
||||
|
||||
Future plans
|
||||
@ -63,5 +71,3 @@ Development
|
||||
===========
|
||||
|
||||
- A demo and test site is in the ``gh-pages`` branch and can be seen at http://smartass101.github.io/pelican-plugins/
|
||||
|
||||
.. LocalWords: lang metadata
|
||||
|
@ -6,7 +6,7 @@ import os
|
||||
import six
|
||||
import logging
|
||||
from itertools import chain
|
||||
from collections import defaultdict
|
||||
from collections import defaultdict, OrderedDict
|
||||
|
||||
import gettext
|
||||
|
||||
@ -22,6 +22,7 @@ from ._regenerate_context_helpers import regenerate_context_articles
|
||||
_main_site_generated = False
|
||||
_main_site_lang = "en"
|
||||
_main_siteurl = ''
|
||||
_lang_siteurls = None
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -32,7 +33,7 @@ def disable_lang_vars(pelican_obj):
|
||||
e.g. ARTICLE_LANG_URL = ARTICLE_URL
|
||||
They would conflict with this plugin otherwise
|
||||
"""
|
||||
global _main_site_lang, _main_siteurl
|
||||
global _main_site_lang, _main_siteurl, _lang_siteurls
|
||||
s = pelican_obj.settings
|
||||
for content in ['ARTICLE', 'PAGE']:
|
||||
for meta in ['_URL', '_SAVE_AS']:
|
||||
@ -40,6 +41,10 @@ def disable_lang_vars(pelican_obj):
|
||||
if not _main_site_generated:
|
||||
_main_site_lang = s['DEFAULT_LANG']
|
||||
_main_siteurl = s['SITEURL']
|
||||
_lang_siteurls = [(lang, _main_siteurl + '/' + lang) for lang in s.get('I18N_SUBSITES', {}).keys()]
|
||||
# To be able to use url for main site root when SITEURL == '' (e.g. when developing)
|
||||
_lang_siteurls = [(_main_site_lang, ('/' if _main_siteurl == '' else _main_siteurl))] + _lang_siteurls
|
||||
_lang_siteurls = OrderedDict(_lang_siteurls)
|
||||
|
||||
|
||||
|
||||
@ -61,7 +66,7 @@ def create_lang_subsites(pelican_obj):
|
||||
for lang, overrides in orig_settings.get('I18N_SUBSITES', {}).items():
|
||||
settings = orig_settings.copy()
|
||||
settings.update(overrides)
|
||||
settings['SITEURL'] = _main_siteurl + '/' + lang
|
||||
settings['SITEURL'] = _lang_siteurls[lang]
|
||||
settings['OUTPUT_PATH'] = os.path.join(orig_settings['OUTPUT_PATH'], lang, '')
|
||||
settings['DEFAULT_LANG'] = lang # to change what is perceived as translations
|
||||
settings['DELETE_OUTPUT_DIRECTORY'] = False # prevent deletion of previous runs
|
||||
@ -150,10 +155,9 @@ def install_templates_translations(generator):
|
||||
"""
|
||||
generator.context['main_siteurl'] = _main_siteurl
|
||||
generator.context['main_lang'] = _main_site_lang
|
||||
extra_siteurls = { lang: _main_siteurl + '/' + lang for lang in generator.settings.get('I18N_SUBSITES', {}).keys() }
|
||||
# To be able to use url for main site root when SITEURL == '' (e.g. when developing)
|
||||
extra_siteurls[_main_site_lang] = '/' if _main_siteurl == '' else _main_siteurl
|
||||
generator.context['lang_siteurls'] = _lang_siteurls
|
||||
current_def_lang = generator.settings['DEFAULT_LANG']
|
||||
extra_siteurls = _lang_siteurls.copy()
|
||||
extra_siteurls.pop(current_def_lang)
|
||||
generator.context['extra_siteurls'] = extra_siteurls
|
||||
|
||||
|
113
implementing_language_buttons.rst
Normal file
113
implementing_language_buttons.rst
Normal file
@ -0,0 +1,113 @@
|
||||
-----------------------------
|
||||
Implementing language buttons
|
||||
-----------------------------
|
||||
|
||||
Each article with translations has translations links, but that's the only way to switch between language subsites.
|
||||
|
||||
For this reason it is convenient to add language buttons to the top menu bar to make it simple to switch between the language subsites on all pages.
|
||||
|
||||
Example designs
|
||||
---------------
|
||||
|
||||
Language buttons showing other available languages
|
||||
..................................................
|
||||
|
||||
The ``extra_siteurls`` dictionary is a mapping of all other (not the *DEFAULT_LANG* of the current (sub-)site) languages to the *SITEURL* of the respective (sub-)sites
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
<!-- SNIP -->
|
||||
<nav><ul>
|
||||
{% if extra_siteurls %}
|
||||
{% for lang, url in extra_siteurls.items() %}
|
||||
<li><a href="{{ url }}">{{ lang }}</a></li>
|
||||
{% endfor %}
|
||||
<!-- separator -->
|
||||
<li style="background-color: white; padding: 5px;"> </li>
|
||||
{% endif %}
|
||||
{% for title, link in MENUITEMS %}
|
||||
<!-- SNIP -->
|
||||
|
||||
Language buttons showing all available languages, current is active
|
||||
..................................................................
|
||||
|
||||
The ``extra_siteurls`` dictionary is a mapping of all languages to the *SITEURL* of the respective (sub-)sites. This template sets the language of the current (sub-)site as active.
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
<!-- SNIP -->
|
||||
<nav><ul>
|
||||
{% if lang_siteurls %}
|
||||
{% for lang, url in lang_siteurls.items() %}
|
||||
<li{% if lang == DEFAULT_LANG %} class="active"{% endif %}><a href="{{ url }}">{{ lang }}</a></li>
|
||||
{% endfor %}
|
||||
<!-- separator -->
|
||||
<li style="background-color: white; padding: 5px;"> </li>
|
||||
{% endif %}
|
||||
{% for title, link in MENUITEMS %}
|
||||
<!-- SNIP -->
|
||||
|
||||
|
||||
Tips and tricks
|
||||
---------------
|
||||
|
||||
Showing more than language codes
|
||||
................................
|
||||
|
||||
If you want the language buttons to show e.g. the names of the languages or flags [#flags]_, not just the language codes, you can use a jinja filter to translate the language codes
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
languages_lookup = {
|
||||
'en': 'English',
|
||||
'cz': 'Čeština',
|
||||
}
|
||||
|
||||
def lookup_lang_name(lang_code):
|
||||
return languages_lookup[lang_code]
|
||||
|
||||
JINJA_FILTERS = {
|
||||
...
|
||||
'lookup_lang_name': lookup_lang_name,
|
||||
}
|
||||
|
||||
And then the link content becomes
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
<!-- SNIP -->
|
||||
{% for lang, siteurl in lang_siteurls.items() %}
|
||||
<li{% if lang == DEFAULT_LANG %} class="active"{% endif %}><a href="{{ siteurl }}">{{ lang | lookup_lang_name }}</a></li>
|
||||
{% endfor %}
|
||||
<!-- SNIP -->
|
||||
|
||||
|
||||
Changing the order of language buttons
|
||||
......................................
|
||||
|
||||
Because ``lang_siteurls`` and ``extra_siteurls`` are instances of ``OrderedDict`` with ``main_lang`` being always the first key, you can change the order through a jinja filter.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def my_ordered_items(ordered_dict):
|
||||
items = list(ordered_dict.items())
|
||||
# swap first and last using tuple unpacking
|
||||
items[0], items[-1] = items[-1], items[0]
|
||||
return items
|
||||
|
||||
JINJA_FILTERS = {
|
||||
...
|
||||
'my_ordered_items': my_ordered_items,
|
||||
}
|
||||
|
||||
And then the ``for`` loop line in the template becomes
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
<!-- SNIP -->
|
||||
{% for lang, siteurl in lang_siteurls | my_ordered_items %}
|
||||
<!-- SNIP -->
|
||||
|
||||
|
||||
.. [#flags] Although it may look nice, `w3 discourages it <http://www.w3.org/TR/i18n-html-tech-lang/#ri20040808.173208643>`_.
|
@ -6,11 +6,15 @@ Localizing themes with Jinja2
|
||||
---------------------
|
||||
|
||||
To enable the |ext| extension in your templates, you must add it to
|
||||
*JINJA_EXTENSIONS* in your Pelican configuration::
|
||||
*JINJA_EXTENSIONS* in your Pelican configuration
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
JINJA_EXTENSIONS = ['jinja2.ext.i18n', ...]
|
||||
|
||||
Then follow the `Jinja2 templating documentation for the I18N plugin <http://jinja.pocoo.org/docs/templates/#i18n>`_ to make your templates localizable. This usually means surrounding strings with the ``{% trans %}`` directive or using ``gettext()`` in expressions::
|
||||
Then follow the `Jinja2 templating documentation for the I18N plugin <http://jinja.pocoo.org/docs/templates/#i18n>`_ to make your templates localizable. This usually means surrounding strings with the ``{% trans %}`` directive or using ``gettext()`` in expressions
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% trans %}translatable content{% endtrans %}
|
||||
{{ gettext('a translatable string') }}
|
||||
@ -33,7 +37,9 @@ The domain of the translations (the name of each translation file is ``domain.mo
|
||||
Example
|
||||
.......
|
||||
|
||||
With the following in your Pelican settings file::
|
||||
With the following in your Pelican settings file
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
I18N_GETTEXT_LOCALEDIR = 'some/path/'
|
||||
I18N_GETTEXT_DOMAIN = 'my_domain'
|
||||
@ -60,7 +66,9 @@ Let's assume that you are localizing a theme in ``themes/my_theme/`` and that yo
|
||||
|
||||
It is up to you where to store babel mappings and translation files templates (``*.pot``), but a convenient place is to put them in ``themes/my_theme/`` and work in that directory. From now on let's assume that it will be our current working directory (CWD).
|
||||
|
||||
To tell babel to extract translatable strings from the templates create a mapping file ``babel.cfg`` with the following line::
|
||||
To tell babel to extract translatable strings from the templates create a mapping file ``babel.cfg`` with the following line
|
||||
|
||||
.. code-block:: cfg
|
||||
|
||||
[jinja2: ./templates/**.html]
|
||||
|
||||
@ -68,7 +76,9 @@ To tell babel to extract translatable strings from the templates create a mappin
|
||||
2. Extract translatable strings from templates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Run the following command to create a ``messages.pot`` message catalog template file from extracted translatable strings::
|
||||
Run the following command to create a ``messages.pot`` message catalog template file from extracted translatable strings
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pybabel extract --mapping babel.cfg --output messages.pot ./
|
||||
|
||||
@ -77,7 +87,9 @@ Run the following command to create a ``messages.pot`` message catalog template
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to translate the template to language ``lang``, run the following command to create a message catalog
|
||||
``translations/lang/LC_MESSAGES/messages.po`` using the template ``messages.pot``::
|
||||
``translations/lang/LC_MESSAGES/messages.po`` using the template ``messages.pot``
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pybabel init --input-file messages.pot --output-dir translations/ --locale lang --domain messages
|
||||
|
||||
@ -86,7 +98,10 @@ babel expects ``lang`` to be a valid locale identifier, so if e.g. you are trans
|
||||
4. Fill the message catalogs
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The message catalog files format is quite intuitive, it is fully documented in the `GNU gettext manual <http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files>`_. Essentially, you fill in the ``msgstr`` strings::
|
||||
The message catalog files format is quite intuitive, it is fully documented in the `GNU gettext manual <http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files>`_. Essentially, you fill in the ``msgstr`` strings
|
||||
|
||||
|
||||
.. code-block:: po
|
||||
|
||||
msgid "just a simple string"
|
||||
msgstr "jenom jednoduchý řetězec"
|
||||
@ -103,7 +118,9 @@ You might also want to remove ``#,fuzzy`` flags once the translation is complete
|
||||
5. Compile the message catalogs
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The message catalogs must be compiled into binary format using this command::
|
||||
The message catalogs must be compiled into binary format using this command
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pybabel compile --directory translations/ --domain messages
|
||||
|
||||
@ -113,9 +130,11 @@ This command might complain about "fuzzy" translations, which means you should r
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you add any translatable patterns into your templates, you have to update your message catalogs too.
|
||||
First you extract a new message catalog template as described in the 2. step. Then you run the following command [#pybabel_error]_::
|
||||
First you extract a new message catalog template as described in the 2. step. Then you run the following command [#pybabel_error]_
|
||||
|
||||
pybabel update --input-file messages.pot --output-dir translations/ --domain messages
|
||||
.. code-block:: bash
|
||||
|
||||
pybabel update --input-file messages.pot --output-dir translations/ --domain messages
|
||||
|
||||
This will merge the new patterns with the old ones. Once you review and fill them, you have to recompile them as described in the 5. step.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user