Kaynağa Gözat

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
Ondrej Grover 10 yıl önce
ebeveyn
işleme
6f7dafaee6

+ 14 - 8
i18n_subsites/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

+ 11 - 7
i18n_subsites/i18n_subsites.py

@@ -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,7 +41,11 @@ 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)
+        
 
     
 def create_lang_subsites(pelican_obj):
@@ -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 - 0
i18n_subsites/implementing_language_buttons.rst

@@ -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;">&nbsp</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;">&nbsp</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>`_.

+ 29 - 10
i18n_subsites/localizing_using_jinja2.rst

@@ -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]_
+
+.. code-block:: bash
 
-  pybabel update --input-file messages.pot --output-dir translations/ --domain messages
+   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.