Feat: french translation
This commit is contained in:
parent
f47fe95757
commit
8696d8c176
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "pelican-plugins"]
|
||||
path = pelican-plugins
|
||||
url = https://github.com/getpelican/pelican-plugins
|
@ -1 +0,0 @@
|
||||
Subproject commit 5c5f965c984d9cb9324c1c7b251d448992c4bda5
|
@ -24,6 +24,23 @@ DEFAULT_LANG = 'fr'
|
||||
|
||||
# theme
|
||||
THEME = "./theme/"
|
||||
# Plugins
|
||||
PLUGIN_PATHS = ["plugins"]
|
||||
PLUGINS = [
|
||||
'i18n_subsites',
|
||||
# 'pdf-img',
|
||||
]
|
||||
# Readers
|
||||
READERS = {"html": None}
|
||||
# Everythings in french
|
||||
JINJA_ENVIRONMENT = {'extensions': ['jinja2.ext.i18n']}
|
||||
# Default theme language.
|
||||
I18N_TEMPLATES_LANG = "en"
|
||||
# Your language.
|
||||
DEFAULT_LANG = "fr"
|
||||
OG_LOCALE = "fr"
|
||||
LOCALE = ("fr", "fr_FR.utf8")
|
||||
|
||||
USE_GOOGLE_FONTS = False
|
||||
|
||||
# Pages, articles and static
|
||||
|
165
plugins/i18n_subsites/README.rst
Normal file
165
plugins/i18n_subsites/README.rst
Normal file
@ -0,0 +1,165 @@
|
||||
=======================
|
||||
I18N Sub-sites Plugin
|
||||
=======================
|
||||
|
||||
This plugin extends the translations functionality by creating
|
||||
internationalized sub-sites for the default site.
|
||||
|
||||
This plugin is designed for Pelican 3.4 and later.
|
||||
|
||||
What it does
|
||||
============
|
||||
|
||||
1. When the content of the main site is being generated, the settings
|
||||
are saved and the generation stops when content is ready to be
|
||||
written. While reading source files and generating content objects,
|
||||
the output queue is modified in certain ways:
|
||||
|
||||
- translations that will appear as native in a different (sub-)site
|
||||
will be removed
|
||||
- untranslated articles will be transformed to drafts if
|
||||
``I18N_UNTRANSLATED_ARTICLES`` is ``'hide'`` (default), removed if
|
||||
``'remove'`` or kept as they are if ``'keep'``.
|
||||
- untranslated pages will be transformed into hidden pages if
|
||||
``I18N_UNTRANSLATED_PAGES`` is ``'hide'`` (default), removed if
|
||||
``'remove'`` or kept as they are if ``'keep'``.''
|
||||
- additional content manipulation similar to articles and pages can
|
||||
be specified for custom generators in the ``I18N_GENERATOR_INFO``
|
||||
setting.
|
||||
|
||||
2. For each language specified in the ``I18N_SUBSITES`` dictionary the
|
||||
settings overrides are applied to the settings from the main site
|
||||
and a new sub-site is generated in the same way as with the main
|
||||
site until content is ready to be written.
|
||||
3. When all (sub-)sites are waiting for content writing, all removed
|
||||
contents, translations and static files are interlinked across the
|
||||
(sub-)sites.
|
||||
4. Finally, all the output is written.
|
||||
|
||||
Setting it up
|
||||
=============
|
||||
|
||||
For each extra used language code, a language-specific settings overrides
|
||||
dictionary must be given (but can be empty) in the ``I18N_SUBSITES`` dictionary
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
PLUGINS = ['i18n_subsites', ...]
|
||||
|
||||
# mapping: language_code -> settings_overrides_dict
|
||||
I18N_SUBSITES = {
|
||||
'cz': {
|
||||
'SITENAME': 'Hezkej blog',
|
||||
}
|
||||
}
|
||||
|
||||
You must also have the following in your pelican configuration
|
||||
|
||||
.. code-block:: python
|
||||
JINJA_ENVIRONMENT = {
|
||||
'extensions': ['jinja2.ext.i18n'],
|
||||
}
|
||||
|
||||
|
||||
|
||||
Default and special overrides
|
||||
-----------------------------
|
||||
The settings overrides may contain arbitrary settings, however, there
|
||||
are some that are handled in a special way:
|
||||
|
||||
``SITEURL``
|
||||
Any overrides to this setting should ensure that there is some level
|
||||
of hierarchy between all (sub-)sites, because Pelican makes all URLs
|
||||
relative to ``SITEURL`` and the plugin can only cross-link between
|
||||
the sites using this hierarchy. For instance, with the main site
|
||||
``http://example.com`` a sub-site ``http://example.com/de`` will
|
||||
work, but ``http://de.example.com`` will not. If not overridden, the
|
||||
language code (the language identifier used in the ``lang``
|
||||
metadata) is appended to the main ``SITEURL`` for each sub-site.
|
||||
``OUTPUT_PATH``, ``CACHE_PATH``
|
||||
If not overridden, the language code is appended as with ``SITEURL``.
|
||||
Separate cache paths are required as parser results depend on the locale.
|
||||
``STATIC_PATHS``, ``THEME_STATIC_PATHS``
|
||||
If not overridden, they are set to ``[]`` and all links to static
|
||||
files are cross-linked to the main site.
|
||||
``THEME``, ``THEME_STATIC_DIR``
|
||||
If overridden, the logic with ``THEME_STATIC_PATHS`` does not apply.
|
||||
``DEFAULT_LANG``
|
||||
This should not be overridden as the plugin changes it to the
|
||||
language code of each sub-site to change what is perceived as translations.
|
||||
|
||||
Localizing templates
|
||||
--------------------
|
||||
|
||||
Most importantly, this plugin can use localized templates for each
|
||||
sub-site. There are two approaches to having the templates localized:
|
||||
|
||||
- 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>`_.
|
||||
|
||||
Additional context variables
|
||||
............................
|
||||
|
||||
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:
|
||||
|
||||
``main_lang``
|
||||
The language of the main site — the original ``DEFAULT_LANG``
|
||||
``main_siteurl``
|
||||
The ``SITEURL`` of the main 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.
|
||||
``relpath_to_site``
|
||||
A function that returns a relative path from the first (sub-)site to
|
||||
the second (sub-)site where the (sub-)sites are identified by the
|
||||
language codes given as two arguments.
|
||||
|
||||
If you don't like the default ordering of the ordered dictionaries,
|
||||
use a Jinja2 filter to alter the ordering.
|
||||
|
||||
All the siteurls above are always absolute even in the case of
|
||||
``RELATIVE_URLS == True`` (it would be to complicated to replicate the
|
||||
Pelican internals for local siteurls), so you may rather use something
|
||||
like ``{{ SITEURL }}/{{ relpath_to_site(DEFAULT_LANG, main_lang }}``
|
||||
to link to the main site.
|
||||
|
||||
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, so
|
||||
content without ``lang`` metadata would 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. You could also
|
||||
give articles e.g. ``name`` metadata and use it in ``ARTICLE_URL =
|
||||
'{name}.html'``.
|
||||
|
||||
Development
|
||||
===========
|
||||
|
||||
- A demo and a test site is in the ``gh-pages`` branch and can be seen
|
||||
at http://smartass101.github.io/pelican-plugins/
|
1
plugins/i18n_subsites/__init__.py
Normal file
1
plugins/i18n_subsites/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .i18n_subsites import *
|
462
plugins/i18n_subsites/i18n_subsites.py
Normal file
462
plugins/i18n_subsites/i18n_subsites.py
Normal file
@ -0,0 +1,462 @@
|
||||
"""i18n_subsites plugin creates i18n-ized subsites of the default site
|
||||
|
||||
This plugin is designed for Pelican 3.4 and later
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import six
|
||||
import logging
|
||||
import posixpath
|
||||
|
||||
from copy import copy
|
||||
from itertools import chain
|
||||
from operator import attrgetter
|
||||
try:
|
||||
from collections.abc import OrderedDict
|
||||
except ImportError:
|
||||
from collections import OrderedDict
|
||||
from contextlib import contextmanager
|
||||
from six.moves.urllib.parse import urlparse
|
||||
|
||||
import gettext
|
||||
import locale
|
||||
|
||||
from pelican import signals
|
||||
from pelican.generators import ArticlesGenerator, PagesGenerator
|
||||
from pelican.settings import configure_settings
|
||||
try:
|
||||
from pelican.contents import Draft
|
||||
except ImportError:
|
||||
from pelican.contents import Article as Draft
|
||||
|
||||
|
||||
# Global vars
|
||||
_MAIN_SETTINGS = None # settings dict of the main Pelican instance
|
||||
_MAIN_LANG = None # lang of the main Pelican instance
|
||||
_MAIN_SITEURL = None # siteurl of the main Pelican instance
|
||||
_MAIN_STATIC_FILES = None # list of Static instances the main Pelican instance
|
||||
_SUBSITE_QUEUE = {} # map: lang -> settings overrides
|
||||
_SITE_DB = OrderedDict() # OrderedDict: lang -> siteurl
|
||||
_SITES_RELPATH_DB = {} # map: (lang, base_lang) -> relpath
|
||||
# map: generator -> list of removed contents that need interlinking
|
||||
_GENERATOR_DB = {}
|
||||
_NATIVE_CONTENT_URL_DB = {} # map: source_path -> content in its native lang
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def temporary_locale(temp_locale=None):
|
||||
'''Enable code to run in a context with a temporary locale
|
||||
|
||||
Resets the locale back when exiting context.
|
||||
Can set a temporary locale if provided
|
||||
'''
|
||||
orig_locale = locale.setlocale(locale.LC_ALL)
|
||||
if temp_locale is not None:
|
||||
locale.setlocale(locale.LC_ALL, temp_locale)
|
||||
yield
|
||||
locale.setlocale(locale.LC_ALL, orig_locale)
|
||||
|
||||
|
||||
def initialize_dbs(settings):
|
||||
'''Initialize internal DBs using the Pelican settings dict
|
||||
|
||||
This clears the DBs for e.g. autoreload mode to work
|
||||
'''
|
||||
global _MAIN_SETTINGS, _MAIN_SITEURL, _MAIN_LANG, _SUBSITE_QUEUE
|
||||
_MAIN_SETTINGS = settings
|
||||
_MAIN_LANG = settings['DEFAULT_LANG']
|
||||
_MAIN_SITEURL = settings['SITEURL']
|
||||
_SUBSITE_QUEUE = settings.get('I18N_SUBSITES', {}).copy()
|
||||
prepare_site_db_and_overrides()
|
||||
# clear databases in case of autoreload mode
|
||||
_SITES_RELPATH_DB.clear()
|
||||
_NATIVE_CONTENT_URL_DB.clear()
|
||||
_GENERATOR_DB.clear()
|
||||
|
||||
|
||||
def prepare_site_db_and_overrides():
|
||||
'''Prepare overrides and create _SITE_DB
|
||||
|
||||
_SITE_DB.keys() need to be ready for filter_translations
|
||||
'''
|
||||
_SITE_DB.clear()
|
||||
_SITE_DB[_MAIN_LANG] = _MAIN_SITEURL
|
||||
# make sure it works for both root-relative and absolute
|
||||
main_siteurl = '/' if _MAIN_SITEURL == '' else _MAIN_SITEURL
|
||||
for lang, overrides in _SUBSITE_QUEUE.items():
|
||||
if 'SITEURL' not in overrides:
|
||||
overrides['SITEURL'] = posixpath.join(main_siteurl, lang)
|
||||
_SITE_DB[lang] = overrides['SITEURL']
|
||||
# default subsite hierarchy
|
||||
if 'OUTPUT_PATH' not in overrides:
|
||||
overrides['OUTPUT_PATH'] = os.path.join(
|
||||
_MAIN_SETTINGS['OUTPUT_PATH'], lang)
|
||||
if 'CACHE_PATH' not in overrides:
|
||||
overrides['CACHE_PATH'] = os.path.join(
|
||||
_MAIN_SETTINGS['CACHE_PATH'], lang)
|
||||
if 'STATIC_PATHS' not in overrides:
|
||||
overrides['STATIC_PATHS'] = []
|
||||
if ('THEME' not in overrides and 'THEME_STATIC_DIR' not in overrides and
|
||||
'THEME_STATIC_PATHS' not in overrides):
|
||||
relpath = relpath_to_site(lang, _MAIN_LANG)
|
||||
overrides['THEME_STATIC_DIR'] = posixpath.join(
|
||||
relpath, _MAIN_SETTINGS['THEME_STATIC_DIR'])
|
||||
overrides['THEME_STATIC_PATHS'] = []
|
||||
# to change what is perceived as translations
|
||||
overrides['DEFAULT_LANG'] = lang
|
||||
|
||||
|
||||
def subscribe_filter_to_signals(settings):
|
||||
'''Subscribe content filter to requested signals'''
|
||||
for sig in settings.get('I18N_FILTER_SIGNALS', []):
|
||||
sig.connect(filter_contents_translations)
|
||||
|
||||
|
||||
def initialize_plugin(pelican_obj):
|
||||
'''Initialize plugin variables and Pelican settings'''
|
||||
if _MAIN_SETTINGS is None:
|
||||
initialize_dbs(pelican_obj.settings)
|
||||
subscribe_filter_to_signals(pelican_obj.settings)
|
||||
|
||||
|
||||
def get_site_path(url):
|
||||
'''Get the path component of an url, excludes siteurl
|
||||
|
||||
also normalizes '' to '/' for relpath to work,
|
||||
otherwise it could be interpreted as a relative filesystem path
|
||||
'''
|
||||
path = urlparse(url).path
|
||||
if path == '':
|
||||
path = '/'
|
||||
return path
|
||||
|
||||
|
||||
def relpath_to_site(lang, target_lang):
|
||||
'''Get relative path from siteurl of lang to siteurl of base_lang
|
||||
|
||||
the output is cached in _SITES_RELPATH_DB
|
||||
'''
|
||||
path = _SITES_RELPATH_DB.get((lang, target_lang), None)
|
||||
if path is None:
|
||||
siteurl = _SITE_DB.get(lang, _MAIN_SITEURL)
|
||||
target_siteurl = _SITE_DB.get(target_lang, _MAIN_SITEURL)
|
||||
path = posixpath.relpath(get_site_path(target_siteurl),
|
||||
get_site_path(siteurl))
|
||||
_SITES_RELPATH_DB[(lang, target_lang)] = path
|
||||
return path
|
||||
|
||||
|
||||
def save_generator(generator):
|
||||
'''Save the generator for later use
|
||||
|
||||
initialize the removed content list
|
||||
'''
|
||||
_GENERATOR_DB[generator] = []
|
||||
|
||||
|
||||
def article2draft(article):
|
||||
'''Transform an Article to Draft'''
|
||||
draft = Draft(article._content, article.metadata, article.settings,
|
||||
article.source_path, article._context)
|
||||
draft.status = 'draft'
|
||||
return draft
|
||||
|
||||
|
||||
def page2hidden_page(page):
|
||||
'''Transform a Page to a hidden Page'''
|
||||
page.status = 'hidden'
|
||||
return page
|
||||
|
||||
|
||||
class GeneratorInspector(object):
|
||||
'''Inspector of generator instances'''
|
||||
|
||||
generators_info = {
|
||||
ArticlesGenerator: {
|
||||
'translations_lists': ['translations', 'drafts_translations'],
|
||||
'contents_lists': [('articles', 'drafts')],
|
||||
'hiding_func': article2draft,
|
||||
'policy': 'I18N_UNTRANSLATED_ARTICLES',
|
||||
},
|
||||
PagesGenerator: {
|
||||
'translations_lists': ['translations', 'hidden_translations'],
|
||||
'contents_lists': [('pages', 'hidden_pages')],
|
||||
'hiding_func': page2hidden_page,
|
||||
'policy': 'I18N_UNTRANSLATED_PAGES',
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, generator):
|
||||
'''Identify the best known class of the generator instance
|
||||
|
||||
The class '''
|
||||
self.generator = generator
|
||||
self.generators_info.update(generator.settings.get(
|
||||
'I18N_GENERATORS_INFO', {}))
|
||||
for cls in generator.__class__.__mro__:
|
||||
if cls in self.generators_info:
|
||||
self.info = self.generators_info[cls]
|
||||
break
|
||||
else:
|
||||
self.info = {}
|
||||
|
||||
def translations_lists(self):
|
||||
'''Iterator over lists of content translations'''
|
||||
return (getattr(self.generator, name) for name in
|
||||
self.info.get('translations_lists', []))
|
||||
|
||||
def contents_list_pairs(self):
|
||||
'''Iterator over pairs of normal and hidden contents'''
|
||||
return (tuple(getattr(self.generator, name) for name in names)
|
||||
for names in self.info.get('contents_lists', []))
|
||||
|
||||
def hiding_function(self):
|
||||
'''Function for transforming content to a hidden version'''
|
||||
hiding_func = self.info.get('hiding_func', lambda x: x)
|
||||
return hiding_func
|
||||
|
||||
def untranslated_policy(self, default):
|
||||
'''Get the policy for untranslated content'''
|
||||
return self.generator.settings.get(self.info.get('policy', None),
|
||||
default)
|
||||
|
||||
def all_contents(self):
|
||||
'''Iterator over all contents'''
|
||||
translations_iterator = chain(*self.translations_lists())
|
||||
return chain(translations_iterator,
|
||||
*(pair[i] for pair in self.contents_list_pairs()
|
||||
for i in (0, 1)))
|
||||
|
||||
|
||||
def filter_contents_translations(generator):
|
||||
'''Filter the content and translations lists of a generator
|
||||
|
||||
Filters out
|
||||
1) translations which will be generated in a different site
|
||||
2) content that is not in the language of the currently
|
||||
generated site but in that of a different site, content in a
|
||||
language which has no site is generated always. The filtering
|
||||
method bay be modified by the respective untranslated policy
|
||||
'''
|
||||
inspector = GeneratorInspector(generator)
|
||||
current_lang = generator.settings['DEFAULT_LANG']
|
||||
langs_with_sites = _SITE_DB.keys()
|
||||
removed_contents = _GENERATOR_DB[generator]
|
||||
|
||||
for translations in inspector.translations_lists():
|
||||
for translation in translations[:]: # copy to be able to remove
|
||||
if translation.lang in langs_with_sites:
|
||||
translations.remove(translation)
|
||||
removed_contents.append(translation)
|
||||
|
||||
hiding_func = inspector.hiding_function()
|
||||
untrans_policy = inspector.untranslated_policy(default='hide')
|
||||
for (contents, other_contents) in inspector.contents_list_pairs():
|
||||
for content in other_contents: # save any hidden native content first
|
||||
if content.lang == current_lang: # in native lang
|
||||
# save the native URL attr formatted in the current locale
|
||||
_NATIVE_CONTENT_URL_DB[content.source_path] = content.url
|
||||
for content in contents[:]: # copy for removing in loop
|
||||
if content.lang == current_lang: # in native lang
|
||||
# save the native URL attr formatted in the current locale
|
||||
_NATIVE_CONTENT_URL_DB[content.source_path] = content.url
|
||||
elif content.lang in langs_with_sites and untrans_policy != 'keep':
|
||||
contents.remove(content)
|
||||
if untrans_policy == 'hide':
|
||||
other_contents.append(hiding_func(content))
|
||||
elif untrans_policy == 'remove':
|
||||
removed_contents.append(content)
|
||||
|
||||
|
||||
def install_templates_translations(generator):
|
||||
'''Install gettext translations in the jinja2.Environment
|
||||
|
||||
Only if the 'jinja2.ext.i18n' jinja2 extension is enabled
|
||||
the translations for the current DEFAULT_LANG are installed.
|
||||
'''
|
||||
if 'JINJA_ENVIRONMENT' in generator.settings: # pelican 3.7+
|
||||
jinja_extensions = generator.settings['JINJA_ENVIRONMENT'].get(
|
||||
'extensions', [])
|
||||
else:
|
||||
jinja_extensions = generator.settings['JINJA_EXTENSIONS']
|
||||
|
||||
if 'jinja2.ext.i18n' in jinja_extensions:
|
||||
domain = generator.settings.get('I18N_GETTEXT_DOMAIN', 'messages')
|
||||
localedir = generator.settings.get('I18N_GETTEXT_LOCALEDIR')
|
||||
if localedir is None:
|
||||
localedir = os.path.join(generator.theme, 'translations')
|
||||
current_lang = generator.settings['DEFAULT_LANG']
|
||||
if current_lang == generator.settings.get('I18N_TEMPLATES_LANG',
|
||||
_MAIN_LANG):
|
||||
translations = gettext.NullTranslations()
|
||||
else:
|
||||
langs = [current_lang]
|
||||
try:
|
||||
translations = gettext.translation(domain, localedir, langs)
|
||||
except (IOError, OSError):
|
||||
_LOGGER.error((
|
||||
"Cannot find translations for language '{}' in '{}' with "
|
||||
"domain '{}'. Installing NullTranslations.").format(
|
||||
langs[0], localedir, domain))
|
||||
translations = gettext.NullTranslations()
|
||||
newstyle = generator.settings.get('I18N_GETTEXT_NEWSTYLE', True)
|
||||
generator.env.install_gettext_translations(translations, newstyle)
|
||||
|
||||
|
||||
def add_variables_to_context(generator):
|
||||
'''Adds useful iterable variables to template context'''
|
||||
context = generator.context # minimize attr lookup
|
||||
context['relpath_to_site'] = relpath_to_site
|
||||
context['main_siteurl'] = _MAIN_SITEURL
|
||||
context['main_lang'] = _MAIN_LANG
|
||||
context['lang_siteurls'] = _SITE_DB
|
||||
current_lang = generator.settings['DEFAULT_LANG']
|
||||
extra_siteurls = _SITE_DB.copy()
|
||||
extra_siteurls.pop(current_lang)
|
||||
context['extra_siteurls'] = extra_siteurls
|
||||
|
||||
|
||||
def interlink_translations(content):
|
||||
'''Link content to translations in their main language
|
||||
|
||||
so the URL (including localized month names) of the different subsites
|
||||
will be honored
|
||||
'''
|
||||
lang = content.lang
|
||||
# sort translations by lang
|
||||
content.translations.sort(key=attrgetter('lang'))
|
||||
for translation in content.translations:
|
||||
relpath = relpath_to_site(lang, translation.lang)
|
||||
url = _NATIVE_CONTENT_URL_DB[translation.source_path]
|
||||
translation.override_url = posixpath.join(relpath, url)
|
||||
|
||||
|
||||
def interlink_translated_content(generator):
|
||||
'''Make translations link to the native locations
|
||||
|
||||
for generators that may contain translated content
|
||||
'''
|
||||
inspector = GeneratorInspector(generator)
|
||||
for content in inspector.all_contents():
|
||||
interlink_translations(content)
|
||||
|
||||
|
||||
def interlink_removed_content(generator):
|
||||
'''For all contents removed from generation queue update interlinks
|
||||
|
||||
link to the native location
|
||||
'''
|
||||
current_lang = generator.settings['DEFAULT_LANG']
|
||||
for content in _GENERATOR_DB[generator]:
|
||||
url = _NATIVE_CONTENT_URL_DB[content.source_path]
|
||||
relpath = relpath_to_site(current_lang, content.lang)
|
||||
content.override_url = posixpath.join(relpath, url)
|
||||
|
||||
|
||||
def interlink_static_files(generator):
|
||||
'''Add links to static files in the main site if necessary'''
|
||||
if generator.settings['STATIC_PATHS'] != []:
|
||||
return # customized STATIC_PATHS
|
||||
try: # minimize attr lookup
|
||||
static_content = generator.context['static_content']
|
||||
except KeyError:
|
||||
static_content = generator.context['filenames']
|
||||
relpath = relpath_to_site(generator.settings['DEFAULT_LANG'], _MAIN_LANG)
|
||||
for staticfile in _MAIN_STATIC_FILES:
|
||||
if staticfile.get_relative_source_path() not in static_content:
|
||||
staticfile = copy(staticfile) # prevent override in main site
|
||||
staticfile.override_url = posixpath.join(relpath, staticfile.url)
|
||||
try:
|
||||
generator.add_source_path(staticfile, static=True)
|
||||
except TypeError:
|
||||
generator.add_source_path(staticfile)
|
||||
|
||||
|
||||
def save_main_static_files(static_generator):
|
||||
'''Save the static files generated for the main site'''
|
||||
global _MAIN_STATIC_FILES
|
||||
# test just for current lang as settings change in autoreload mode
|
||||
if static_generator.settings['DEFAULT_LANG'] == _MAIN_LANG:
|
||||
_MAIN_STATIC_FILES = static_generator.staticfiles
|
||||
|
||||
|
||||
def update_generators():
|
||||
'''Update the context of all generators
|
||||
|
||||
Ads useful variables and translations into the template context
|
||||
and interlink translations
|
||||
'''
|
||||
for generator in _GENERATOR_DB.keys():
|
||||
install_templates_translations(generator)
|
||||
add_variables_to_context(generator)
|
||||
interlink_static_files(generator)
|
||||
interlink_removed_content(generator)
|
||||
interlink_translated_content(generator)
|
||||
|
||||
|
||||
def get_pelican_cls(settings):
|
||||
'''Get the Pelican class requested in settings'''
|
||||
cls = settings['PELICAN_CLASS']
|
||||
if isinstance(cls, six.string_types):
|
||||
module, cls_name = cls.rsplit('.', 1)
|
||||
module = __import__(module)
|
||||
cls = getattr(module, cls_name)
|
||||
return cls
|
||||
|
||||
|
||||
def create_next_subsite(pelican_obj):
|
||||
'''Create the next subsite using the lang-specific config
|
||||
|
||||
If there are no more subsites in the generation queue, update all
|
||||
the generators (interlink translations and removed content, add
|
||||
variables and translations to template context). Otherwise get the
|
||||
language and overrides for next the subsite in the queue and apply
|
||||
overrides. Then generate the subsite using a PELICAN_CLASS
|
||||
instance and its run method. Finally, restore the previous locale.
|
||||
'''
|
||||
global _MAIN_SETTINGS
|
||||
if len(_SUBSITE_QUEUE) == 0:
|
||||
_LOGGER.debug(
|
||||
'i18n: Updating cross-site links and context of all generators.')
|
||||
update_generators()
|
||||
_MAIN_SETTINGS = None # to initialize next time
|
||||
else:
|
||||
with temporary_locale():
|
||||
settings = _MAIN_SETTINGS.copy()
|
||||
lang, overrides = _SUBSITE_QUEUE.popitem()
|
||||
settings.update(overrides)
|
||||
settings = configure_settings(settings) # to set LOCALE, etc.
|
||||
cls = get_pelican_cls(settings)
|
||||
|
||||
new_pelican_obj = cls(settings)
|
||||
_LOGGER.debug(("Generating i18n subsite for language '{}' "
|
||||
"using class {}").format(lang, cls))
|
||||
new_pelican_obj.run()
|
||||
|
||||
|
||||
# map: signal name -> function name
|
||||
_SIGNAL_HANDLERS_DB = {
|
||||
'get_generators': initialize_plugin,
|
||||
'article_generator_pretaxonomy': filter_contents_translations,
|
||||
'page_generator_finalized': filter_contents_translations,
|
||||
'get_writer': create_next_subsite,
|
||||
'static_generator_finalized': save_main_static_files,
|
||||
'generator_init': save_generator,
|
||||
}
|
||||
|
||||
|
||||
def register():
|
||||
'''Register the plugin only if required signals are available'''
|
||||
for sig_name in _SIGNAL_HANDLERS_DB.keys():
|
||||
if not hasattr(signals, sig_name):
|
||||
_LOGGER.error((
|
||||
'The i18n_subsites plugin requires the {} '
|
||||
'signal available for sure in Pelican 3.4.0 and later, '
|
||||
'plugin will not be used.').format(sig_name))
|
||||
return
|
||||
|
||||
for sig_name, handler in _SIGNAL_HANDLERS_DB.items():
|
||||
sig = getattr(signals, sig_name)
|
||||
sig.connect(handler)
|
128
plugins/i18n_subsites/implementing_language_buttons.rst
Normal file
128
plugins/i18n_subsites/implementing_language_buttons.rst
Normal file
@ -0,0 +1,128 @@
|
||||
-----------------------------
|
||||
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 -->
|
||||
|
||||
Notice the slash ``/`` after ``{{ url }}``, this makes sure it works
|
||||
with local development when ``SITEURL == ''``.
|
||||
|
||||
Language buttons showing all available languages, current is active
|
||||
...................................................................
|
||||
|
||||
The ``lang_subsites`` 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>`_.
|
202
plugins/i18n_subsites/localizing_using_jinja2.rst
Normal file
202
plugins/i18n_subsites/localizing_using_jinja2.rst
Normal file
@ -0,0 +1,202 @@
|
||||
-----------------------------
|
||||
Localizing themes with Jinja2
|
||||
-----------------------------
|
||||
|
||||
1. Localize templates
|
||||
---------------------
|
||||
|
||||
To enable the |ext| extension in your templates, you must add it to
|
||||
``JINJA_ENVIRONMENT`` in your Pelican configuration
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
JINJA_ENVIRONMENT = {
|
||||
'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
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% trans %}translatable content{% endtrans %}
|
||||
{{ gettext('a translatable string') }}
|
||||
|
||||
For pluralization support, etc. consult the documentation.
|
||||
|
||||
To enable `newstyle gettext calls
|
||||
<http://jinja.pocoo.org/docs/extensions/#newstyle-gettext>`_ the
|
||||
``I18N_GETTEXT_NEWSTYLE`` config variable must be set to ``True``
|
||||
(default).
|
||||
|
||||
.. |ext| replace:: ``jinja2.ext.i18n``
|
||||
|
||||
2. Specify translations location
|
||||
--------------------------------
|
||||
|
||||
The |ext| extension uses the `Python gettext library
|
||||
<http://docs.python.org/library/gettext.html>`_ for translating
|
||||
strings.
|
||||
|
||||
In your Pelican config you can give the path in which to look for
|
||||
translations in the ``I18N_GETTEXT_LOCALEDIR`` variable. If not given,
|
||||
it is assumed to be the ``translations`` subfolder in the top folder
|
||||
of the theme specified by ``THEME``.
|
||||
|
||||
The domain of the translations (the name of each translation file is
|
||||
``domain.mo``) is controlled by the ``I18N_GETTEXT_DOMAIN`` config
|
||||
variable (defaults to ``messages``).
|
||||
|
||||
Example
|
||||
.......
|
||||
|
||||
With the following in your Pelican settings file
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
I18N_GETTEXT_LOCALEDIR = 'some/path/'
|
||||
I18N_GETTEXT_DOMAIN = 'my_domain'
|
||||
|
||||
the translation for language 'cz' will be expected to be in
|
||||
``some/path/cz/LC_MESSAGES/my_domain.mo``
|
||||
|
||||
|
||||
3. Extract translatable strings and translate them
|
||||
--------------------------------------------------
|
||||
|
||||
There are many ways to extract translatable strings and create
|
||||
``gettext`` compatible translations. You can create the ``*.po`` and
|
||||
``*.mo`` message catalog files yourself, or you can use some helper
|
||||
tool as described in `the Python gettext library tutorial
|
||||
<http://docs.python.org/library/gettext.html#internationalizing-your-programs-and-modules>`_.
|
||||
|
||||
You of course don't need to provide a translation for the language in
|
||||
which the templates are written which is assumed to be the original
|
||||
``DEFAULT_LANG``. This can be overridden in the
|
||||
``I18N_TEMPLATES_LANG`` variable.
|
||||
|
||||
Recommended tool: babel
|
||||
.......................
|
||||
|
||||
`Babel <http://babel.pocoo.org/>`_ makes it easy to extract
|
||||
translatable strings from the localized Jinja2 templates and assists
|
||||
with creating translations as documented in this `Jinja2-Babel
|
||||
tutorial
|
||||
<http://pythonhosted.org/Flask-Babel/#translating-applications>`_
|
||||
[#flask]_ on which the following is based.
|
||||
|
||||
1. Add babel mapping
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Let's assume that you are localizing a theme in ``themes/my_theme/``
|
||||
and that you use the default settings, i.e. the default domain
|
||||
``messages`` and will put the translations in the ``translations``
|
||||
subdirectory of the theme directory as
|
||||
``themes/my_theme/translations/``.
|
||||
|
||||
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
|
||||
|
||||
.. code-block:: cfg
|
||||
|
||||
[jinja2: templates/**.html]
|
||||
|
||||
|
||||
2. Extract translatable strings from templates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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 ./
|
||||
|
||||
|
||||
3. Initialize message catalogs
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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``
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pybabel init --input-file messages.pot --output-dir translations/ --locale lang --domain messages
|
||||
|
||||
babel expects ``lang`` to be a valid locale identifier, so if e.g. you
|
||||
are translating for language ``cz`` but the corresponding locale is
|
||||
``cs``, you have to use the locale identifier. Nevertheless, the
|
||||
gettext infrastructure should later correctly find the locale for a
|
||||
given language.
|
||||
|
||||
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
|
||||
|
||||
|
||||
.. code-block:: po
|
||||
|
||||
msgid "just a simple string"
|
||||
msgstr "jenom jednoduchý řetězec"
|
||||
|
||||
msgid ""
|
||||
"some multiline string"
|
||||
"looks like this"
|
||||
msgstr ""
|
||||
"nějaký více řádkový řetězec"
|
||||
"vypadá takto"
|
||||
|
||||
You might also want to remove ``#,fuzzy`` flags once the translation
|
||||
is complete and reviewed to show that it can be compiled.
|
||||
|
||||
5. Compile the message catalogs
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The message catalogs must be compiled into binary format using this
|
||||
command
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pybabel compile --directory translations/ --domain messages
|
||||
|
||||
This command might complain about "fuzzy" translations, which means
|
||||
you should review the translations and once done, remove the fuzzy
|
||||
flag line.
|
||||
|
||||
(6.) Update the catalogs when templates change
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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]_
|
||||
|
||||
.. 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.
|
||||
|
||||
.. [#flask] Although the tutorial is focused on Flask-based web
|
||||
applications, the linked translation tutorial is not
|
||||
Flask-specific.
|
||||
.. [#pybabel_error] If you get an error ``TypeError: must be str, not
|
||||
bytes`` with Python 3.3, it is likely you are
|
||||
suffering from this `bug
|
||||
<https://github.com/mitsuhiko/flask-babel/issues/43>`_.
|
||||
Until the fix is released, you can use babel with
|
||||
Python 2.7.
|
@ -0,0 +1,7 @@
|
||||
404 stránka
|
||||
===========
|
||||
:slug: 404
|
||||
:lang: cz
|
||||
:status: hidden
|
||||
|
||||
Jednoduchá 404 stránka.
|
@ -0,0 +1,7 @@
|
||||
Eine 404 Seite
|
||||
==============
|
||||
:slug: 404
|
||||
:lang: de
|
||||
:status: hidden
|
||||
|
||||
Eine einfache 404 Seite.
|
@ -0,0 +1,7 @@
|
||||
A 404 page
|
||||
==========
|
||||
:slug: 404
|
||||
:lang: en
|
||||
:status: hidden
|
||||
|
||||
A simple 404 page.
|
@ -0,0 +1,5 @@
|
||||
Untranslated page
|
||||
=================
|
||||
:lang: en
|
||||
|
||||
This page has no translation.
|
@ -0,0 +1,8 @@
|
||||
Přeložený článek
|
||||
================
|
||||
:slug: translated-article
|
||||
:lang: cz
|
||||
:date: 2014-09-15
|
||||
|
||||
Jednoduchý článek s překlady.
|
||||
Zde je odkaz na `nějaký obrázek <{filename}/images/img.png>`_.
|
@ -0,0 +1,8 @@
|
||||
Ein übersetzter Artikel
|
||||
=======================
|
||||
:slug: translated-article
|
||||
:lang: de
|
||||
:date: 2014-09-14
|
||||
|
||||
Ein einfacher Artikel mit einer Übersetzung.
|
||||
Hier ist ein Link zur `einigem Bild <{filename}/images/img.png>`_.
|
@ -0,0 +1,8 @@
|
||||
A translated article
|
||||
====================
|
||||
:slug: translated-article
|
||||
:lang: en
|
||||
:date: 2014-09-13
|
||||
|
||||
A simple article with a translation.
|
||||
Here is a link to `some image <{filename}/images/img.png>`_.
|
@ -0,0 +1,9 @@
|
||||
An untranslated article
|
||||
=======================
|
||||
:date: 2014-07-14
|
||||
:lang: en
|
||||
|
||||
An article without a translation.
|
||||
Here is a link to an `untranslated page`_
|
||||
|
||||
.. _`untranslated page`: {filename}/pages/untranslated-page.rst
|
@ -0,0 +1,2 @@
|
||||
[jinja2: templates/**.html]
|
||||
|
23
plugins/i18n_subsites/test_data/localized_theme/messages.pot
Normal file
23
plugins/i18n_subsites/test_data/localized_theme/messages.pot
Normal file
@ -0,0 +1,23 @@
|
||||
# Translations template for PROJECT.
|
||||
# Copyright (C) 2014 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2014-07-13 12:25+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 1.3\n"
|
||||
|
||||
#: templates/base.html:3
|
||||
msgid "Welcome to our"
|
||||
msgstr ""
|
||||
|
@ -0,0 +1,7 @@
|
||||
{% extends "!simple/base.html" %}
|
||||
|
||||
{% block title %}{% trans %}Welcome to our{% endtrans %} {{ SITENAME }}{% endblock %}
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<link rel="stylesheet" href="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/style.css" />
|
||||
{% endblock %}
|
Binary file not shown.
@ -0,0 +1,23 @@
|
||||
# German translations for PROJECT.
|
||||
# Copyright (C) 2014 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2014-07-13 12:25+0200\n"
|
||||
"PO-Revision-Date: 2014-07-13 12:26+0200\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: de <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 1.3\n"
|
||||
|
||||
#: templates/base.html:3
|
||||
msgid "Welcome to our"
|
||||
msgstr "Willkommen Sie zur unserer"
|
||||
|
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Testing site - An untranslated article</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testing site Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/theme/style.css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/">Testing site <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li><a href="http://example.com/test/pages/untranslated-page.html">Untranslated page</a></li>
|
||||
<li class="active"><a href="http://example.com/test/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<section id="content" class="body">
|
||||
<header>
|
||||
<h2 class="entry-title">
|
||||
<a href="http://example.com/test/an-untranslated-article.html" rel="bookmark"
|
||||
title="Permalink to An untranslated article">An untranslated article</a></h2>
|
||||
|
||||
</header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-07-14T00:00:00+00:00">
|
||||
Mon 14 July 2014
|
||||
</time>
|
||||
<address class="vcard author">
|
||||
By <a class="url fn" href="http://example.com/test/author/the-tester.html">The Tester</a>
|
||||
</address>
|
||||
<div class="category">
|
||||
Category: <a href="http://example.com/test/category/misc.html">misc</a>
|
||||
</div>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content">
|
||||
<p>An article without a translation.
|
||||
Here is a link to an <a class="reference external" href="http://example.com/test/pages/untranslated-page.html">untranslated page</a></p>
|
||||
|
||||
</div><!-- /.entry-content -->
|
||||
</section>
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Testovací stránka - An untranslated article</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testovací stránka Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/cz/../theme/style.css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/cz/">Testovací stránka <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li class="active"><a href="http://example.com/test/cz/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<section id="content" class="body">
|
||||
<header>
|
||||
<h2 class="entry-title">
|
||||
<a href="http://example.com/test/cz/an-untranslated-article-en.html" rel="bookmark"
|
||||
title="Permalink to An untranslated article">An untranslated article</a></h2>
|
||||
|
||||
</header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-07-14T00:00:00+00:00">
|
||||
Mon 14 July 2014
|
||||
</time>
|
||||
<address class="vcard author">
|
||||
By <a class="url fn" href="http://example.com/test/cz/author/test-testovic.html">Test Testovič</a>
|
||||
</address>
|
||||
<div class="category">
|
||||
Category: <a href="http://example.com/test/cz/category/misc.html">misc</a>
|
||||
</div>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content">
|
||||
<p>An article without a translation.
|
||||
Here is a link to an <a class="reference external" href="http://example.com/test/pages/untranslated-page.html">untranslated page</a></p>
|
||||
|
||||
</div><!-- /.entry-content -->
|
||||
</section>
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
10
plugins/i18n_subsites/test_data/output/cz/feeds_all.atom.xml
Normal file
10
plugins/i18n_subsites/test_data/output/cz/feeds_all.atom.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Testovací stránka</title><link href="http://example.com/test/cz/" rel="alternate"></link><link href="http://example.com/test/feeds_all.atom.xml" rel="self"></link><id>http://example.com/test/cz/</id><updated>2014-09-15T00:00:00+00:00</updated><entry><title>Přeložený článek</title><link href="http://example.com/test/cz/translated-article.html" rel="alternate"></link><published>2014-09-15T00:00:00+00:00</published><updated>2014-09-15T00:00:00+00:00</updated><author><name>Test Testovič</name></author><id>tag:example.com,2014-09-15:/test/cz/translated-article.html</id><content type="html"><p>Jednoduchý článek s překlady.
|
||||
Zde je odkaz na <a class="reference external" href="http://example.com/test/images/img.png">nějaký obrázek</a>.</p>
|
||||
</content></entry><entry><title>Ein übersetzter Artikel</title><link href="http://example.com/test/de/translated-article.html" rel="alternate"></link><published>2014-09-14T00:00:00+00:00</published><updated>2014-09-14T00:00:00+00:00</updated><author><name>Test Testovič</name></author><id>tag:example.com,2014-09-14:/test/de/translated-article.html</id><content type="html"><p>Ein einfacher Artikel mit einer Übersetzung.
|
||||
Hier ist ein Link zur <a class="reference external" href="http://example.com/test/images/img.png">einigem Bild</a>.</p>
|
||||
</content></entry><entry><title>A translated article</title><link href="http://example.com/test/translated-article.html" rel="alternate"></link><published>2014-09-13T00:00:00+00:00</published><updated>2014-09-13T00:00:00+00:00</updated><author><name>Test Testovič</name></author><id>tag:example.com,2014-09-13:/test/translated-article.html</id><content type="html"><p>A simple article with a translation.
|
||||
Here is a link to <a class="reference external" href="http://example.com/test/images/img.png">some image</a>.</p>
|
||||
</content></entry><entry><title>An untranslated article</title><link href="http://example.com/test/cz/an-untranslated-article-en.html" rel="alternate"></link><published>2014-07-14T00:00:00+00:00</published><updated>2014-07-14T00:00:00+00:00</updated><author><name>Test Testovič</name></author><id>tag:example.com,2014-07-14:/test/cz/an-untranslated-article-en.html</id><content type="html"><p>An article without a translation.
|
||||
Here is a link to an <a class="reference external" href="http://example.com/test/pages/untranslated-page.html">untranslated page</a></p>
|
||||
</content></entry></feed>
|
55
plugins/i18n_subsites/test_data/output/cz/index.html
Normal file
55
plugins/i18n_subsites/test_data/output/cz/index.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="cz">
|
||||
<head>
|
||||
<title>Welcome to our Testovací stránka</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testovací stránka Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/cz/../theme/style.css" />
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/cz/">Testovací stránka <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li><a href="http://example.com/test/cz/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<section id="content">
|
||||
<h2>All articles</h2>
|
||||
|
||||
<ol id="post-list">
|
||||
<li><article class="hentry">
|
||||
<header> <h2 class="entry-title"><a href="http://example.com/test/cz/translated-article.html" rel="bookmark" title="Permalink to Přeložený článek">Přeložený článek</a></h2> </header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-09-15T00:00:00+00:00"> Mon 15 September 2014 </time>
|
||||
<address class="vcard author">By
|
||||
<a class="url fn" href="http://example.com/test/cz/author/test-testovic.html">Test Testovič</a>
|
||||
</address>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content"> <p>Jednoduchý článek s překlady.
|
||||
Zde je odkaz na <a class="reference external" href="http://example.com/test/images/img.png">nějaký obrázek</a>.</p>
|
||||
</div><!-- /.entry-content -->
|
||||
</article></li>
|
||||
<li><article class="hentry">
|
||||
<header> <h2 class="entry-title"><a href="http://example.com/test/cz/an-untranslated-article-en.html" rel="bookmark" title="Permalink to An untranslated article">An untranslated article</a></h2> </header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-07-14T00:00:00+00:00"> Mon 14 July 2014 </time>
|
||||
<address class="vcard author">By
|
||||
<a class="url fn" href="http://example.com/test/cz/author/test-testovic.html">Test Testovič</a>
|
||||
</address>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content"> <p>An article without a translation.
|
||||
Here is a link to an <a class="reference external" href="http://example.com/test/pages/untranslated-page.html">untranslated page</a></p>
|
||||
</div><!-- /.entry-content -->
|
||||
</article></li>
|
||||
</ol><!-- /#posts-list -->
|
||||
</section><!-- /#content -->
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
39
plugins/i18n_subsites/test_data/output/cz/pages/404.html
Normal file
39
plugins/i18n_subsites/test_data/output/cz/pages/404.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="cz">
|
||||
<head>
|
||||
<title>Testovací stránka - 404 stránka</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testovací stránka Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/cz/../theme/style.css" />
|
||||
|
||||
|
||||
<link rel="alternate" hreflang="de" href="http://example.com/test/cz/../de/pages/404.html">
|
||||
<link rel="alternate" hreflang="en" href="http://example.com/test/cz/../pages/404.html">
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/cz/">Testovací stránka <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li><a href="http://example.com/test/cz/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<h1>404 stránka</h1>
|
||||
Translations:
|
||||
<a href="http://example.com/test/cz/../de/pages/404.html" hreflang="de">de</a>
|
||||
<a href="http://example.com/test/cz/../pages/404.html" hreflang="en">en</a>
|
||||
|
||||
|
||||
<p>Jednoduchá 404 stránka.</p>
|
||||
|
||||
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="cz">
|
||||
<head>
|
||||
<title>Testovací stránka - Přeložený článek</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testovací stránka Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/cz/../theme/style.css" />
|
||||
|
||||
|
||||
<link rel="alternate" hreflang="de" href="http://example.com/test/cz/../de/translated-article.html">
|
||||
<link rel="alternate" hreflang="en" href="http://example.com/test/cz/../translated-article.html">
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/cz/">Testovací stránka <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li class="active"><a href="http://example.com/test/cz/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<section id="content" class="body">
|
||||
<header>
|
||||
<h2 class="entry-title">
|
||||
<a href="http://example.com/test/cz/translated-article.html" rel="bookmark"
|
||||
title="Permalink to Přeložený článek">Přeložený článek</a></h2>
|
||||
Translations:
|
||||
<a href="http://example.com/test/cz/../de/translated-article.html" hreflang="de">de</a>
|
||||
<a href="http://example.com/test/cz/../translated-article.html" hreflang="en">en</a>
|
||||
|
||||
</header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-09-15T00:00:00+00:00">
|
||||
Mon 15 September 2014
|
||||
</time>
|
||||
<address class="vcard author">
|
||||
By <a class="url fn" href="http://example.com/test/cz/author/test-testovic.html">Test Testovič</a>
|
||||
</address>
|
||||
<div class="category">
|
||||
Category: <a href="http://example.com/test/cz/category/misc.html">misc</a>
|
||||
</div>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content">
|
||||
<p>Jednoduchý článek s překlady.
|
||||
Zde je odkaz na <a class="reference external" href="http://example.com/test/images/img.png">nějaký obrázek</a>.</p>
|
||||
|
||||
</div><!-- /.entry-content -->
|
||||
</section>
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Testseite - An untranslated article</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testseite Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/de/../theme/style.css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/de/">Testseite <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li class="active"><a href="http://example.com/test/de/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<section id="content" class="body">
|
||||
<header>
|
||||
<h2 class="entry-title">
|
||||
<a href="http://example.com/test/de/drafts/an-untranslated-article-en.html" rel="bookmark"
|
||||
title="Permalink to An untranslated article">An untranslated article</a></h2>
|
||||
|
||||
</header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-07-14T00:00:00+00:00">
|
||||
Mo 14 Juli 2014
|
||||
</time>
|
||||
<address class="vcard author">
|
||||
By <a class="url fn" href="http://example.com/test/de/author/der-tester.html">Der Tester</a>
|
||||
</address>
|
||||
<div class="category">
|
||||
Category: <a href="http://example.com/test/de/category/misc.html">misc</a>
|
||||
</div>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content">
|
||||
<p>An article without a translation.
|
||||
Here is a link to an <a class="reference external" href="http://example.com/test/de/pages/untranslated-page-en.html">untranslated page</a></p>
|
||||
|
||||
</div><!-- /.entry-content -->
|
||||
</section>
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Testseite</title><link href="http://example.com/test/de/" rel="alternate"></link><link href="http://example.com/test/feeds_all.atom.xml" rel="self"></link><id>http://example.com/test/de/</id><updated>2014-09-15T00:00:00+00:00</updated><entry><title>Přeložený článek</title><link href="http://example.com/test/cz/translated-article.html" rel="alternate"></link><published>2014-09-15T00:00:00+00:00</published><updated>2014-09-15T00:00:00+00:00</updated><author><name>Der Tester</name></author><id>tag:example.com,2014-09-15:/test/cz/translated-article.html</id><content type="html"><p>Jednoduchý článek s překlady.
|
||||
Zde je odkaz na <a class="reference external" href="http://example.com/test/images/img.png">nějaký obrázek</a>.</p>
|
||||
</content></entry><entry><title>Ein übersetzter Artikel</title><link href="http://example.com/test/de/translated-article.html" rel="alternate"></link><published>2014-09-14T00:00:00+00:00</published><updated>2014-09-14T00:00:00+00:00</updated><author><name>Der Tester</name></author><id>tag:example.com,2014-09-14:/test/de/translated-article.html</id><content type="html"><p>Ein einfacher Artikel mit einer Übersetzung.
|
||||
Hier ist ein Link zur <a class="reference external" href="http://example.com/test/images/img.png">einigem Bild</a>.</p>
|
||||
</content></entry><entry><title>A translated article</title><link href="http://example.com/test/translated-article.html" rel="alternate"></link><published>2014-09-13T00:00:00+00:00</published><updated>2014-09-13T00:00:00+00:00</updated><author><name>Der Tester</name></author><id>tag:example.com,2014-09-13:/test/translated-article.html</id><content type="html"><p>A simple article with a translation.
|
||||
Here is a link to <a class="reference external" href="http://example.com/test/images/img.png">some image</a>.</p>
|
||||
</content></entry></feed>
|
43
plugins/i18n_subsites/test_data/output/de/index.html
Normal file
43
plugins/i18n_subsites/test_data/output/de/index.html
Normal file
@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<title>Willkommen Sie zur unserer Testseite</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testseite Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/de/../theme/style.css" />
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/de/">Testseite <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li><a href="http://example.com/test/de/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<section id="content">
|
||||
<h2>All articles</h2>
|
||||
|
||||
<ol id="post-list">
|
||||
<li><article class="hentry">
|
||||
<header> <h2 class="entry-title"><a href="http://example.com/test/de/translated-article.html" rel="bookmark" title="Permalink to Ein übersetzter Artikel">Ein übersetzter Artikel</a></h2> </header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-09-14T00:00:00+00:00"> So 14 September 2014 </time>
|
||||
<address class="vcard author">By
|
||||
<a class="url fn" href="http://example.com/test/de/author/der-tester.html">Der Tester</a>
|
||||
</address>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content"> <p>Ein einfacher Artikel mit einer Übersetzung.
|
||||
Hier ist ein Link zur <a class="reference external" href="http://example.com/test/images/img.png">einigem Bild</a>.</p>
|
||||
</div><!-- /.entry-content -->
|
||||
</article></li>
|
||||
</ol><!-- /#posts-list -->
|
||||
</section><!-- /#content -->
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
39
plugins/i18n_subsites/test_data/output/de/pages/404.html
Normal file
39
plugins/i18n_subsites/test_data/output/de/pages/404.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<title>Testseite - Eine 404 Seite</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testseite Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/de/../theme/style.css" />
|
||||
|
||||
|
||||
<link rel="alternate" hreflang="cz" href="http://example.com/test/de/../cz/pages/404.html">
|
||||
<link rel="alternate" hreflang="en" href="http://example.com/test/de/../pages/404.html">
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/de/">Testseite <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li><a href="http://example.com/test/de/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<h1>Eine 404 Seite</h1>
|
||||
Translations:
|
||||
<a href="http://example.com/test/de/../cz/pages/404.html" hreflang="cz">cz</a>
|
||||
<a href="http://example.com/test/de/../pages/404.html" hreflang="en">en</a>
|
||||
|
||||
|
||||
<p>Eine einfache 404 Seite.</p>
|
||||
|
||||
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Testseite - Untranslated page</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testseite Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/de/../theme/style.css" />
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/de/">Testseite <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li><a href="http://example.com/test/de/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<h1>Untranslated page</h1>
|
||||
|
||||
|
||||
<p>This page has no translation.</p>
|
||||
|
||||
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<title>Testseite - Ein übersetzter Artikel</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testseite Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/de/../theme/style.css" />
|
||||
|
||||
|
||||
<link rel="alternate" hreflang="cz" href="http://example.com/test/de/../cz/translated-article.html">
|
||||
<link rel="alternate" hreflang="en" href="http://example.com/test/de/../translated-article.html">
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/de/">Testseite <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li class="active"><a href="http://example.com/test/de/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<section id="content" class="body">
|
||||
<header>
|
||||
<h2 class="entry-title">
|
||||
<a href="http://example.com/test/de/translated-article.html" rel="bookmark"
|
||||
title="Permalink to Ein übersetzter Artikel">Ein übersetzter Artikel</a></h2>
|
||||
Translations:
|
||||
<a href="http://example.com/test/de/../cz/translated-article.html" hreflang="cz">cz</a>
|
||||
<a href="http://example.com/test/de/../translated-article.html" hreflang="en">en</a>
|
||||
|
||||
</header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-09-14T00:00:00+00:00">
|
||||
So 14 September 2014
|
||||
</time>
|
||||
<address class="vcard author">
|
||||
By <a class="url fn" href="http://example.com/test/de/author/der-tester.html">Der Tester</a>
|
||||
</address>
|
||||
<div class="category">
|
||||
Category: <a href="http://example.com/test/de/category/misc.html">misc</a>
|
||||
</div>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content">
|
||||
<p>Ein einfacher Artikel mit einer Übersetzung.
|
||||
Hier ist ein Link zur <a class="reference external" href="http://example.com/test/images/img.png">einigem Bild</a>.</p>
|
||||
|
||||
</div><!-- /.entry-content -->
|
||||
</section>
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
10
plugins/i18n_subsites/test_data/output/feeds_all.atom.xml
Normal file
10
plugins/i18n_subsites/test_data/output/feeds_all.atom.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom"><title>Testing site</title><link href="http://example.com/test/" rel="alternate"></link><link href="http://example.com/test/feeds_all.atom.xml" rel="self"></link><id>http://example.com/test/</id><updated>2014-09-15T00:00:00+00:00</updated><entry><title>Přeložený článek</title><link href="http://example.com/test/cz/translated-article.html" rel="alternate"></link><published>2014-09-15T00:00:00+00:00</published><updated>2014-09-15T00:00:00+00:00</updated><author><name>The Tester</name></author><id>tag:example.com,2014-09-15:/test/cz/translated-article.html</id><content type="html"><p>Jednoduchý článek s překlady.
|
||||
Zde je odkaz na <a class="reference external" href="http://example.com/test/images/img.png">nějaký obrázek</a>.</p>
|
||||
</content></entry><entry><title>Ein übersetzter Artikel</title><link href="http://example.com/test/de/translated-article.html" rel="alternate"></link><published>2014-09-14T00:00:00+00:00</published><updated>2014-09-14T00:00:00+00:00</updated><author><name>The Tester</name></author><id>tag:example.com,2014-09-14:/test/de/translated-article.html</id><content type="html"><p>Ein einfacher Artikel mit einer Übersetzung.
|
||||
Hier ist ein Link zur <a class="reference external" href="http://example.com/test/images/img.png">einigem Bild</a>.</p>
|
||||
</content></entry><entry><title>A translated article</title><link href="http://example.com/test/translated-article.html" rel="alternate"></link><published>2014-09-13T00:00:00+00:00</published><updated>2014-09-13T00:00:00+00:00</updated><author><name>The Tester</name></author><id>tag:example.com,2014-09-13:/test/translated-article.html</id><content type="html"><p>A simple article with a translation.
|
||||
Here is a link to <a class="reference external" href="http://example.com/test/images/img.png">some image</a>.</p>
|
||||
</content></entry><entry><title>An untranslated article</title><link href="http://example.com/test/an-untranslated-article.html" rel="alternate"></link><published>2014-07-14T00:00:00+00:00</published><updated>2014-07-14T00:00:00+00:00</updated><author><name>The Tester</name></author><id>tag:example.com,2014-07-14:/test/an-untranslated-article.html</id><content type="html"><p>An article without a translation.
|
||||
Here is a link to an <a class="reference external" href="http://example.com/test/pages/untranslated-page.html">untranslated page</a></p>
|
||||
</content></entry></feed>
|
56
plugins/i18n_subsites/test_data/output/index.html
Normal file
56
plugins/i18n_subsites/test_data/output/index.html
Normal file
@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Welcome to our Testing site</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testing site Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/theme/style.css" />
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/">Testing site <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li><a href="http://example.com/test/pages/untranslated-page.html">Untranslated page</a></li>
|
||||
<li><a href="http://example.com/test/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<section id="content">
|
||||
<h2>All articles</h2>
|
||||
|
||||
<ol id="post-list">
|
||||
<li><article class="hentry">
|
||||
<header> <h2 class="entry-title"><a href="http://example.com/test/translated-article.html" rel="bookmark" title="Permalink to A translated article">A translated article</a></h2> </header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-09-13T00:00:00+00:00"> Sat 13 September 2014 </time>
|
||||
<address class="vcard author">By
|
||||
<a class="url fn" href="http://example.com/test/author/the-tester.html">The Tester</a>
|
||||
</address>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content"> <p>A simple article with a translation.
|
||||
Here is a link to <a class="reference external" href="http://example.com/test/images/img.png">some image</a>.</p>
|
||||
</div><!-- /.entry-content -->
|
||||
</article></li>
|
||||
<li><article class="hentry">
|
||||
<header> <h2 class="entry-title"><a href="http://example.com/test/an-untranslated-article.html" rel="bookmark" title="Permalink to An untranslated article">An untranslated article</a></h2> </header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-07-14T00:00:00+00:00"> Mon 14 July 2014 </time>
|
||||
<address class="vcard author">By
|
||||
<a class="url fn" href="http://example.com/test/author/the-tester.html">The Tester</a>
|
||||
</address>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content"> <p>An article without a translation.
|
||||
Here is a link to an <a class="reference external" href="http://example.com/test/pages/untranslated-page.html">untranslated page</a></p>
|
||||
</div><!-- /.entry-content -->
|
||||
</article></li>
|
||||
</ol><!-- /#posts-list -->
|
||||
</section><!-- /#content -->
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
40
plugins/i18n_subsites/test_data/output/pages/404.html
Normal file
40
plugins/i18n_subsites/test_data/output/pages/404.html
Normal file
@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Testing site - A 404 page</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testing site Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/theme/style.css" />
|
||||
|
||||
|
||||
<link rel="alternate" hreflang="cz" href="http://example.com/test/cz/pages/404.html">
|
||||
<link rel="alternate" hreflang="de" href="http://example.com/test/de/pages/404.html">
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/">Testing site <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li><a href="http://example.com/test/pages/untranslated-page.html">Untranslated page</a></li>
|
||||
<li><a href="http://example.com/test/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<h1>A 404 page</h1>
|
||||
Translations:
|
||||
<a href="http://example.com/test/cz/pages/404.html" hreflang="cz">cz</a>
|
||||
<a href="http://example.com/test/de/pages/404.html" hreflang="de">de</a>
|
||||
|
||||
|
||||
<p>A simple 404 page.</p>
|
||||
|
||||
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Testing site - Untranslated page</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testing site Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/theme/style.css" />
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/">Testing site <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li class="active"><a href="http://example.com/test/pages/untranslated-page.html">Untranslated page</a></li>
|
||||
<li><a href="http://example.com/test/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<h1>Untranslated page</h1>
|
||||
|
||||
|
||||
<p>This page has no translation.</p>
|
||||
|
||||
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Testing site - A translated article</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="http://example.com/test/feeds_all.atom.xml" type="application/atom+xml" rel="alternate" title="Testing site Full Atom Feed" />
|
||||
|
||||
<link rel="stylesheet" href="http://example.com/test/theme/style.css" />
|
||||
|
||||
|
||||
<link rel="alternate" hreflang="cz" href="http://example.com/test/cz/translated-article.html">
|
||||
<link rel="alternate" hreflang="de" href="http://example.com/test/de/translated-article.html">
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body id="index" class="home">
|
||||
<header id="banner" class="body">
|
||||
<h1><a href="http://example.com/test/">Testing site <strong></strong></a></h1>
|
||||
</header><!-- /#banner -->
|
||||
<nav id="menu"><ul>
|
||||
<li><a href="http://example.com/test/pages/untranslated-page.html">Untranslated page</a></li>
|
||||
<li class="active"><a href="http://example.com/test/category/misc.html">misc</a></li>
|
||||
</ul></nav><!-- /#menu -->
|
||||
<section id="content" class="body">
|
||||
<header>
|
||||
<h2 class="entry-title">
|
||||
<a href="http://example.com/test/translated-article.html" rel="bookmark"
|
||||
title="Permalink to A translated article">A translated article</a></h2>
|
||||
Translations:
|
||||
<a href="http://example.com/test/cz/translated-article.html" hreflang="cz">cz</a>
|
||||
<a href="http://example.com/test/de/translated-article.html" hreflang="de">de</a>
|
||||
|
||||
</header>
|
||||
<footer class="post-info">
|
||||
<time class="published" datetime="2014-09-13T00:00:00+00:00">
|
||||
Sat 13 September 2014
|
||||
</time>
|
||||
<address class="vcard author">
|
||||
By <a class="url fn" href="http://example.com/test/author/the-tester.html">The Tester</a>
|
||||
</address>
|
||||
<div class="category">
|
||||
Category: <a href="http://example.com/test/category/misc.html">misc</a>
|
||||
</div>
|
||||
</footer><!-- /.post-info -->
|
||||
<div class="entry-content">
|
||||
<p>A simple article with a translation.
|
||||
Here is a link to <a class="reference external" href="http://example.com/test/images/img.png">some image</a>.</p>
|
||||
|
||||
</div><!-- /.entry-content -->
|
||||
</section>
|
||||
<footer id="contentinfo" class="body">
|
||||
<address id="about" class="vcard body">
|
||||
Proudly powered by <a href="http://getpelican.com/">Pelican</a>,
|
||||
which takes great advantage of <a href="http://python.org">Python</a>.
|
||||
</address><!-- /#about -->
|
||||
</footer><!-- /#contentinfo -->
|
||||
</body>
|
||||
</html>
|
53
plugins/i18n_subsites/test_data/pelicanconf.py
Normal file
53
plugins/i18n_subsites/test_data/pelicanconf.py
Normal file
@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*- #
|
||||
from __future__ import unicode_literals
|
||||
|
||||
AUTHOR = 'The Tester'
|
||||
SITENAME = 'Testing site'
|
||||
SITEURL = 'http://example.com/test'
|
||||
|
||||
# to make the test suite portable
|
||||
TIMEZONE = 'UTC'
|
||||
|
||||
DEFAULT_LANG = 'en'
|
||||
LOCALE = 'en_US.UTF-8'
|
||||
|
||||
# Generate only one feed
|
||||
FEED_ALL_ATOM = 'feeds_all.atom.xml'
|
||||
CATEGORY_FEED_ATOM = None
|
||||
TRANSLATION_FEED_ATOM = None
|
||||
AUTHOR_FEED_ATOM = None
|
||||
AUTHOR_FEED_RSS = None
|
||||
|
||||
# Disable unnecessary pages
|
||||
CATEGORY_SAVE_AS = ''
|
||||
TAG_SAVE_AS = ''
|
||||
AUTHOR_SAVE_AS = ''
|
||||
ARCHIVES_SAVE_AS = ''
|
||||
AUTHORS_SAVE_AS = ''
|
||||
CATEGORIES_SAVE_AS = ''
|
||||
TAGS_SAVE_AS = ''
|
||||
|
||||
PLUGIN_PATHS = ['../../']
|
||||
PLUGINS = ['i18n_subsites']
|
||||
|
||||
THEME = 'localized_theme'
|
||||
JINJA_ENVIRONMENT = {'extensions': ['jinja2.ext.i18n']}
|
||||
|
||||
from blinker import signal
|
||||
tmpsig = signal('tmpsig')
|
||||
I18N_FILTER_SIGNALS = [tmpsig]
|
||||
|
||||
I18N_SUBSITES = {
|
||||
'de': {
|
||||
'SITENAME': 'Testseite',
|
||||
'AUTHOR': 'Der Tester',
|
||||
'LOCALE': 'de_DE.UTF-8',
|
||||
},
|
||||
'cz': {
|
||||
'SITENAME': 'Testovací stránka',
|
||||
'AUTHOR': 'Test Testovič',
|
||||
'I18N_UNTRANSLATED_PAGES': 'remove',
|
||||
'I18N_UNTRANSLATED_ARTICLES': 'keep',
|
||||
},
|
||||
}
|
139
plugins/i18n_subsites/test_i18n_subsites.py
Normal file
139
plugins/i18n_subsites/test_i18n_subsites.py
Normal file
@ -0,0 +1,139 @@
|
||||
'''Unit tests for the i18n_subsites plugin'''
|
||||
|
||||
import os
|
||||
import locale
|
||||
import unittest
|
||||
import subprocess
|
||||
from tempfile import mkdtemp
|
||||
from shutil import rmtree
|
||||
|
||||
from . import i18n_subsites as i18ns
|
||||
from pelican import Pelican
|
||||
from pelican.tests.support import get_settings
|
||||
from pelican.settings import read_settings
|
||||
|
||||
|
||||
class TestTemporaryLocale(unittest.TestCase):
|
||||
'''Test the temporary locale context manager'''
|
||||
|
||||
def test_locale_restored(self):
|
||||
'''Test that the locale is restored after exiting context'''
|
||||
orig_locale = locale.setlocale(locale.LC_ALL)
|
||||
with i18ns.temporary_locale():
|
||||
locale.setlocale(locale.LC_ALL, 'C')
|
||||
self.assertEqual(locale.setlocale(locale.LC_ALL), 'C')
|
||||
self.assertEqual(locale.setlocale(locale.LC_ALL), orig_locale)
|
||||
|
||||
def test_temp_locale_set(self):
|
||||
'''Test that the temporary locale is set'''
|
||||
with i18ns.temporary_locale('C'):
|
||||
self.assertEqual(locale.setlocale(locale.LC_ALL), 'C')
|
||||
|
||||
|
||||
class TestSettingsManipulation(unittest.TestCase):
|
||||
'''Test operations on settings dict'''
|
||||
|
||||
def setUp(self):
|
||||
'''Prepare default settings'''
|
||||
self.settings = get_settings()
|
||||
|
||||
def test_get_pelican_cls_class(self):
|
||||
'''Test that we get class given as an object'''
|
||||
self.settings['PELICAN_CLASS'] = object
|
||||
cls = i18ns.get_pelican_cls(self.settings)
|
||||
self.assertIs(cls, object)
|
||||
|
||||
def test_get_pelican_cls_str(self):
|
||||
'''Test that we get correct class given by string'''
|
||||
cls = i18ns.get_pelican_cls(self.settings)
|
||||
self.assertIs(cls, Pelican)
|
||||
|
||||
|
||||
class TestSitesRelpath(unittest.TestCase):
|
||||
'''Test relative path between sites generation'''
|
||||
|
||||
def setUp(self):
|
||||
'''Generate some sample siteurls'''
|
||||
self.siteurl = 'http://example.com'
|
||||
i18ns._SITE_DB['en'] = self.siteurl
|
||||
i18ns._SITE_DB['de'] = self.siteurl + '/de'
|
||||
|
||||
def tearDown(self):
|
||||
'''Remove sites from db'''
|
||||
i18ns._SITE_DB.clear()
|
||||
|
||||
def test_get_site_path(self):
|
||||
'''Test getting the path within a site'''
|
||||
self.assertEqual(i18ns.get_site_path(self.siteurl), '/')
|
||||
self.assertEqual(i18ns.get_site_path(self.siteurl + '/de'), '/de')
|
||||
|
||||
def test_relpath_to_site(self):
|
||||
'''Test getting relative paths between sites'''
|
||||
self.assertEqual(i18ns.relpath_to_site('en', 'de'), 'de')
|
||||
self.assertEqual(i18ns.relpath_to_site('de', 'en'), '..')
|
||||
|
||||
|
||||
class TestRegistration(unittest.TestCase):
|
||||
'''Test plugin registration'''
|
||||
|
||||
def test_return_on_missing_signal(self):
|
||||
'''Test return on missing required signal'''
|
||||
i18ns._SIGNAL_HANDLERS_DB['tmp_sig'] = None
|
||||
i18ns.register()
|
||||
self.assertNotIn(id(i18ns.save_generator),
|
||||
i18ns.signals.generator_init.receivers)
|
||||
|
||||
def test_registration(self):
|
||||
'''Test registration of all signal handlers'''
|
||||
i18ns.register()
|
||||
for sig_name, handler in i18ns._SIGNAL_HANDLERS_DB.items():
|
||||
sig = getattr(i18ns.signals, sig_name)
|
||||
self.assertIn(id(handler), sig.receivers)
|
||||
# clean up
|
||||
sig.disconnect(handler)
|
||||
|
||||
|
||||
class TestFullRun(unittest.TestCase):
|
||||
'''Test running Pelican with the Plugin'''
|
||||
|
||||
def setUp(self):
|
||||
'''Create temporary output and cache folders'''
|
||||
self.temp_path = mkdtemp(prefix='pelicantests.')
|
||||
self.temp_cache = mkdtemp(prefix='pelican_cache.')
|
||||
|
||||
def tearDown(self):
|
||||
'''Remove output and cache folders'''
|
||||
rmtree(self.temp_path)
|
||||
rmtree(self.temp_cache)
|
||||
|
||||
def test_sites_generation(self):
|
||||
'''Test generation of sites with the plugin
|
||||
|
||||
Compare with recorded output via ``git diff``.
|
||||
To generate output for comparison run the command
|
||||
``pelican -o test_data/output -s test_data/pelicanconf.py \
|
||||
test_data/content``
|
||||
Remember to remove the output/ folder before that.
|
||||
'''
|
||||
base_path = os.path.dirname(os.path.abspath(__file__))
|
||||
base_path = os.path.join(base_path, 'test_data')
|
||||
content_path = os.path.join(base_path, 'content')
|
||||
output_path = os.path.join(base_path, 'output')
|
||||
settings_path = os.path.join(base_path, 'pelicanconf.py')
|
||||
settings = read_settings(path=settings_path, override={
|
||||
'PATH': content_path,
|
||||
'OUTPUT_PATH': self.temp_path,
|
||||
'CACHE_PATH': self.temp_cache,
|
||||
'PLUGINS': [i18ns],
|
||||
}
|
||||
)
|
||||
pelican = Pelican(settings)
|
||||
pelican.run()
|
||||
|
||||
# compare output
|
||||
out, err = subprocess.Popen(
|
||||
['git', 'diff', '--no-ext-diff', '--exit-code', '-w', output_path,
|
||||
self.temp_path], env={'PAGER': ''},
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
||||
self.assertFalse(out, 'non-empty `diff` stdout:\n{}'.format(out))
|
||||
self.assertFalse(err, 'non-empty `diff` stderr:\n{}'.format(out))
|
Loading…
Reference in New Issue
Block a user