From d9016af2fabbeaa34f19b4f968f97cf485ec66dc Mon Sep 17 00:00:00 2001 From: "Schoenberger, Philipp" Date: Tue, 2 Jul 2019 17:12:28 +0200 Subject: [PATCH] Initial commit --- .gitignore | 6 + README.md | 8 + content/pages/about.md | 46 + content/pages/gallery/index.md | 26 + content/pages/impressum.md | 24 + content/pages/index.md | 21 + content/pages/projects/01_3d_printer.md | 11 + content/pages/projects/projects.md | 13 + develop_server.sh | 109 ++ pelicanconf.py | 175 ++ plugins/i18n_subsites/README.rst | 165 ++ plugins/i18n_subsites/__init__.py | 1 + plugins/i18n_subsites/i18n_subsites.py | 462 +++++ .../implementing_language_buttons.rst | 128 ++ .../i18n_subsites/localizing_using_jinja2.rst | 200 +++ .../test_data/content/images/img.png | 0 .../content/pages/hidden-page-cz.rst | 7 + .../content/pages/hidden-page-de.rst | 7 + .../content/pages/hidden-page-en.rst | 7 + .../content/pages/untranslated-page.rst | 5 + .../content/translated_article-cz.rst | 8 + .../content/translated_article-de.rst | 8 + .../content/translated_article-en.rst | 8 + .../content/untranslated_article-en.rst | 9 + .../test_data/localized_theme/babel.cfg | 2 + .../test_data/localized_theme/messages.pot | 23 + .../localized_theme/static/style.css | 0 .../localized_theme/templates/base.html | 7 + .../translations/de/LC_MESSAGES/messages.mo | Bin 0 -> 486 bytes .../translations/de/LC_MESSAGES/messages.po | 23 + .../i18n_subsites/test_data/pelicanconf.py | 53 + plugins/i18n_subsites/test_i18n_subsites.py | 139 ++ plugins/lightgallery/__init__.py | 1 + plugins/lightgallery/lightgallery.py | 217 +++ themes/minimal/static/css/bitter.css | 17 + themes/minimal/static/css/bootstrap.min.css | 7 + themes/minimal/static/css/lightgallery.css | 978 ++++++++++ .../minimal/static/css/lightgallery_setup.css | 115 ++ themes/minimal/static/css/minimal.css | 686 +++++++ themes/minimal/static/css/pygments.css | 84 + .../minimal/static/fonts/Bold/Bitter-Bold.eot | Bin 0 -> 28286 bytes .../minimal/static/fonts/Bold/Bitter-Bold.svg | 710 ++++++++ .../minimal/static/fonts/Bold/Bitter-Bold.ttf | Bin 0 -> 75484 bytes .../static/fonts/Bold/Bitter-Bold.woff | Bin 0 -> 35716 bytes .../fonts/BoldItalic/Bitter-BoldItalic.eot | Bin 0 -> 32383 bytes .../fonts/BoldItalic/Bitter-BoldItalic.svg | 1067 +++++++++++ .../fonts/BoldItalic/Bitter-BoldItalic.ttf | Bin 0 -> 81104 bytes .../fonts/BoldItalic/Bitter-BoldItalic.woff | Bin 0 -> 40116 bytes .../static/fonts/Italic/Bitter-Italic.eot | Bin 0 -> 31641 bytes .../static/fonts/Italic/Bitter-Italic.svg | 810 +++++++++ .../static/fonts/Italic/Bitter-Italic.ttf | Bin 0 -> 81764 bytes .../static/fonts/Italic/Bitter-Italic.woff | Bin 0 -> 39256 bytes .../static/fonts/Regular/Bitter-Regular.eot | Bin 0 -> 29326 bytes .../static/fonts/Regular/Bitter-Regular.svg | 1358 ++++++++++++++ .../static/fonts/Regular/Bitter-Regular.ttf | Bin 0 -> 79104 bytes .../static/fonts/Regular/Bitter-Regular.woff | Bin 0 -> 36316 bytes themes/minimal/static/fonts/lg.eot | Bin 0 -> 4024 bytes themes/minimal/static/fonts/lg.svg | 47 + themes/minimal/static/fonts/lg.ttf | Bin 0 -> 3880 bytes themes/minimal/static/fonts/lg.woff | Bin 0 -> 3956 bytes .../minimal/static/images/fileicons/file.png | Bin 0 -> 720 bytes themes/minimal/static/images/fileicons/gz.png | Bin 0 -> 716 bytes .../minimal/static/images/fileicons/pdf.png | Bin 0 -> 663 bytes themes/minimal/static/images/loading.gif | Bin 0 -> 3801 bytes ...s-initial-monogram-logo-450w-343390823.jpg | Bin 0 -> 8803 bytes themes/minimal/static/images/zoom.png | Bin 0 -> 29498 bytes themes/minimal/static/js/bitter.zip | Bin 0 -> 135423 bytes .../static/js/jquery.mousewheel.min.js | 8 + themes/minimal/static/js/lg-autoplay.js | 216 +++ themes/minimal/static/js/lg-fullscreen.js | 131 ++ themes/minimal/static/js/lg-hash.js | 97 + themes/minimal/static/js/lg-pager.js | 120 ++ themes/minimal/static/js/lg-share.js | 134 ++ themes/minimal/static/js/lg-zoom.js | 568 ++++++ themes/minimal/static/js/lightgallery.js | 1588 +++++++++++++++++ themes/minimal/templates/alternate_index.html | 20 + themes/minimal/templates/archives.html | 14 + themes/minimal/templates/article.html | 23 + themes/minimal/templates/author.html | 17 + themes/minimal/templates/authors.html | 0 themes/minimal/templates/base.html | 80 + themes/minimal/templates/galery.html | 11 + themes/minimal/templates/index.html | 17 + themes/minimal/templates/page.html | 12 + themes/minimal/templates/page_discussion.html | 9 + themes/minimal/templates/page_notitle.html | 9 + themes/minimal/templates/projects.html | 15 + themes/minimal/templates/tag.html | 18 + themes/minimal/templates/tags.html | 14 + 89 files changed, 10919 insertions(+) create mode 100644 .gitignore create mode 100644 content/pages/about.md create mode 100644 content/pages/gallery/index.md create mode 100644 content/pages/impressum.md create mode 100644 content/pages/index.md create mode 100644 content/pages/projects/01_3d_printer.md create mode 100644 content/pages/projects/projects.md create mode 100755 develop_server.sh create mode 100644 pelicanconf.py create mode 100644 plugins/i18n_subsites/README.rst create mode 100644 plugins/i18n_subsites/__init__.py create mode 100644 plugins/i18n_subsites/i18n_subsites.py create mode 100644 plugins/i18n_subsites/implementing_language_buttons.rst create mode 100644 plugins/i18n_subsites/localizing_using_jinja2.rst create mode 100644 plugins/i18n_subsites/test_data/content/images/img.png create mode 100644 plugins/i18n_subsites/test_data/content/pages/hidden-page-cz.rst create mode 100644 plugins/i18n_subsites/test_data/content/pages/hidden-page-de.rst create mode 100644 plugins/i18n_subsites/test_data/content/pages/hidden-page-en.rst create mode 100644 plugins/i18n_subsites/test_data/content/pages/untranslated-page.rst create mode 100644 plugins/i18n_subsites/test_data/content/translated_article-cz.rst create mode 100644 plugins/i18n_subsites/test_data/content/translated_article-de.rst create mode 100644 plugins/i18n_subsites/test_data/content/translated_article-en.rst create mode 100644 plugins/i18n_subsites/test_data/content/untranslated_article-en.rst create mode 100644 plugins/i18n_subsites/test_data/localized_theme/babel.cfg create mode 100644 plugins/i18n_subsites/test_data/localized_theme/messages.pot create mode 100644 plugins/i18n_subsites/test_data/localized_theme/static/style.css create mode 100644 plugins/i18n_subsites/test_data/localized_theme/templates/base.html create mode 100644 plugins/i18n_subsites/test_data/localized_theme/translations/de/LC_MESSAGES/messages.mo create mode 100644 plugins/i18n_subsites/test_data/localized_theme/translations/de/LC_MESSAGES/messages.po create mode 100644 plugins/i18n_subsites/test_data/pelicanconf.py create mode 100644 plugins/i18n_subsites/test_i18n_subsites.py create mode 100644 plugins/lightgallery/__init__.py create mode 100644 plugins/lightgallery/lightgallery.py create mode 100644 themes/minimal/static/css/bitter.css create mode 100644 themes/minimal/static/css/bootstrap.min.css create mode 100644 themes/minimal/static/css/lightgallery.css create mode 100644 themes/minimal/static/css/lightgallery_setup.css create mode 100644 themes/minimal/static/css/minimal.css create mode 100644 themes/minimal/static/css/pygments.css create mode 100755 themes/minimal/static/fonts/Bold/Bitter-Bold.eot create mode 100755 themes/minimal/static/fonts/Bold/Bitter-Bold.svg create mode 100755 themes/minimal/static/fonts/Bold/Bitter-Bold.ttf create mode 100755 themes/minimal/static/fonts/Bold/Bitter-Bold.woff create mode 100755 themes/minimal/static/fonts/BoldItalic/Bitter-BoldItalic.eot create mode 100755 themes/minimal/static/fonts/BoldItalic/Bitter-BoldItalic.svg create mode 100755 themes/minimal/static/fonts/BoldItalic/Bitter-BoldItalic.ttf create mode 100755 themes/minimal/static/fonts/BoldItalic/Bitter-BoldItalic.woff create mode 100755 themes/minimal/static/fonts/Italic/Bitter-Italic.eot create mode 100755 themes/minimal/static/fonts/Italic/Bitter-Italic.svg create mode 100755 themes/minimal/static/fonts/Italic/Bitter-Italic.ttf create mode 100755 themes/minimal/static/fonts/Italic/Bitter-Italic.woff create mode 100755 themes/minimal/static/fonts/Regular/Bitter-Regular.eot create mode 100755 themes/minimal/static/fonts/Regular/Bitter-Regular.svg create mode 100755 themes/minimal/static/fonts/Regular/Bitter-Regular.ttf create mode 100755 themes/minimal/static/fonts/Regular/Bitter-Regular.woff create mode 100644 themes/minimal/static/fonts/lg.eot create mode 100644 themes/minimal/static/fonts/lg.svg create mode 100644 themes/minimal/static/fonts/lg.ttf create mode 100644 themes/minimal/static/fonts/lg.woff create mode 100644 themes/minimal/static/images/fileicons/file.png create mode 100644 themes/minimal/static/images/fileicons/gz.png create mode 100644 themes/minimal/static/images/fileicons/pdf.png create mode 100644 themes/minimal/static/images/loading.gif create mode 100644 themes/minimal/static/images/ps-initial-monogram-logo-450w-343390823.jpg create mode 100644 themes/minimal/static/images/zoom.png create mode 100644 themes/minimal/static/js/bitter.zip create mode 100644 themes/minimal/static/js/jquery.mousewheel.min.js create mode 100644 themes/minimal/static/js/lg-autoplay.js create mode 100644 themes/minimal/static/js/lg-fullscreen.js create mode 100644 themes/minimal/static/js/lg-hash.js create mode 100644 themes/minimal/static/js/lg-pager.js create mode 100644 themes/minimal/static/js/lg-share.js create mode 100644 themes/minimal/static/js/lg-zoom.js create mode 100644 themes/minimal/static/js/lightgallery.js create mode 100644 themes/minimal/templates/alternate_index.html create mode 100644 themes/minimal/templates/archives.html create mode 100644 themes/minimal/templates/article.html create mode 100644 themes/minimal/templates/author.html create mode 100644 themes/minimal/templates/authors.html create mode 100644 themes/minimal/templates/base.html create mode 100644 themes/minimal/templates/galery.html create mode 100644 themes/minimal/templates/index.html create mode 100644 themes/minimal/templates/page.html create mode 100644 themes/minimal/templates/page_discussion.html create mode 100644 themes/minimal/templates/page_notitle.html create mode 100644 themes/minimal/templates/projects.html create mode 100644 themes/minimal/templates/tag.html create mode 100644 themes/minimal/templates/tags.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0b43490 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +output +__pycache__ +*.pid +*.pid +*.pyc +*~ diff --git a/README.md b/README.md index 3a295f2..d963ea3 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,11 @@ phschoen.de install requirements: pip install -r requirements.txt + +resize images + + make img + +start devserver + + make devserver diff --git a/content/pages/about.md b/content/pages/about.md new file mode 100644 index 0000000..d69c548 --- /dev/null +++ b/content/pages/about.md @@ -0,0 +1,46 @@ +title: About me +date: 2013-03-25 10:20 +authors: Philipp Schönberger +url: about.html +save_as: about.html + +## Personal Links ## +me cartooniced + +[github](https://www.github.com/phschoen)
+[linkedin](https://www.linkedin.com/in/phschoen)
+[xing](https://www.xing.com/profile/Philipp_Schoenberger5)
+[stackoverflow](https://stackoverflow.com/users/1016564)
+ +## Contact Details ## + +Email: mail@phschoen.de
+Address: Check the WHOIS record. + +## Press ## + +| year | article | +|------|---------| +| 2019 | [Loeten-bis-zum-Abheben-So-bauen-Maedchen-in-Markdorf-einen-flugfaehigen-Quadrocopter](https://www.suedkurier.de/region/bodenseekreis/markdorf/Loeten-bis-zum-Abheben-So-bauen-Maedchen-in-Markdorf-einen-flugfaehigen-Quadrocopter;art372484,10125455) | +| 2019 | [Vier Mädchen bauen ein Fluggerät](https://www.schwaebische.de/landkreis/bodenseekreis/markdorf_artikel,-vier-m%C3%A4dchen-bauen-ein-flugger%C3%A4t-_arid,11042245.html) | + + +## Interests ## + +photography, +programming, +3d printing and CAD, +electrical engeneering, +gardening, + +## CV ## + +| year | working title | +|-------------------|------------------------------------------------------| +| 01/2018 - now | embedded software architect - employee | +| 07/2015 - 12/2017 | embedded Linux engineer - employee | +| 03/2014 - 05/2016 | embedded Linux engineer - working student & employee | +| 05/2013 - 09/2013 | embedded Linux engineer - employee | +| 01/2013 - 04/2013 | embedded Linux engineer - bachelor thesis | +| 07/2012 - 12/2012 | embedded Linux engineer - working student | +| 09/2011 - 03/2012 | embedded Linux engineer - intern | diff --git a/content/pages/gallery/index.md b/content/pages/gallery/index.md new file mode 100644 index 0000000..0c6726b --- /dev/null +++ b/content/pages/gallery/index.md @@ -0,0 +1,26 @@ +title: Gallery +author: Philipp Schönberger +url: gallery/index.html +save_as: gallery/index.html +template: page + +Here you find latest pictures i am willing to share. Enjoy the views. + + +[lightgallery + /images/gallery/IMG_3184.jpg, Neighbour cat ; + /images/gallery/IMG_3364.jpg, Steinhummel an der Kornblume ; + /images/gallery/IMG_3386.jpg, Gartenhummel an der Kornblume ; + /images/gallery/IMG_3414.jpg, Gartenhummel an der Kornblume ; + /images/gallery/IMG_3477.jpg ; + /images/gallery/IMG_3483.jpg, Distel ; + /images/gallery/IMG_3559.jpg, Windrose in Blüte ; + /images/gallery/IMG_8722.jpg, Dänische Südseehaus ; + /images/gallery/IMG_9037.jpg, Fisherman Sailingboat ; + /images/gallery/IMG_9097.jpg, Sailingboat ; + /images/gallery/IMG_9136.jpg, Sunrise in Denmark ; + /images/gallery/IMG_9137.jpg, Sunrise in Denmark ; + /images/gallery/IMG_9393.jpg, Harbour Grömitz at night; +lightgalleryend] + +this is some gallery: diff --git a/content/pages/impressum.md b/content/pages/impressum.md new file mode 100644 index 0000000..03f845d --- /dev/null +++ b/content/pages/impressum.md @@ -0,0 +1,24 @@ +title: Impressum +authors: Philipp Schönberger +url: impressum.html +save_as: impressum.html + +Haftungsausschluss (Disclaimer) +Haftung für Inhalte + +Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. + +Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen. +Haftung für Links + +Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. + +Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen. +Urheberrecht + +Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet. + +Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen. + +Quelle: http://www.e-recht24.de + diff --git a/content/pages/index.md b/content/pages/index.md new file mode 100644 index 0000000..0fe4a73 --- /dev/null +++ b/content/pages/index.md @@ -0,0 +1,21 @@ +title: home +author: Philipp Schönberger +url: index.html +save_as: index.html +template: page + +Welcome to my webpage. + +It contains things I work or worked on and lot of other things I had fun with. + +I’m Philipp Schönberger born in in Zeitz(Germany) within the 1988. +In Constance I made my bachelor degree within ‘computer engineering‘. +Afterwards I absolved my master degree within the ‘robotics and intelligent embedded systems‘ in Lübeck. +Currently I am employed as software engineer/architect for a satellite aided router in Germany near the Lake Constance. + +Enjoy your stay. + +## Contact ## + +* Email: mail@phschoen.de +* Address: Check the WHOIS record. diff --git a/content/pages/projects/01_3d_printer.md b/content/pages/projects/01_3d_printer.md new file mode 100644 index 0000000..938482c --- /dev/null +++ b/content/pages/projects/01_3d_printer.md @@ -0,0 +1,11 @@ +title: Projects +date: 2019-05-20 +author: Philipp Schönberger +slug: 3d_printer_en +template: page +category: projects +url: projects/01_3d_printer.html +save_as: projects/01_3d_printer.html + +Here you can view the.. latest projects i've started. + diff --git a/content/pages/projects/projects.md b/content/pages/projects/projects.md new file mode 100644 index 0000000..d9e295b --- /dev/null +++ b/content/pages/projects/projects.md @@ -0,0 +1,13 @@ +title: Projects +date: 2019-05-20 +author: Philipp Schönberger +slug: projects +category: projects +url: projects/index.html +save_as: projects/index.html + +Here you can view the latest projects i've started. + +This page contains tutorials, rough drafts I work on. The articles in here aren't necessarily finished and some of them aren't open to discussion, because they are work in progress (or chaos). Some of them might be turned into self-contained blog posts, some of them will never get out of this dungeon. + + diff --git a/develop_server.sh b/develop_server.sh new file mode 100755 index 0000000..9e92dc5 --- /dev/null +++ b/develop_server.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +## +# This section should match your Makefile +## +IS_UBUNTU_1404=$(lsb_release -r | grep 14.04 > /dev/null && echo yes || echo no) +echo is damn old ubuntu $IS_UBUNTU_1404 +if [[ "$IS_UBUNTU_1404" == "yes" ]] +then + PY=python2.7 +else + PY=python3 +fi +echo $PY + +PELICAN=pelican +PELICANOPTS= + +BASEDIR=$(pwd) +INPUTDIR=$BASEDIR/content +OUTPUTDIR=$BASEDIR/output +CONFFILE=$BASEDIR/pelicanconf.py + +### +# Don't change stuff below here unless you are sure +### + +SRV_PID=$BASEDIR/srv.pid +PELICAN_PID=$BASEDIR/pelican.pid + +function usage(){ + echo "usage: $0 (stop) (start) (restart)" + echo "This starts pelican in debug and reload mode and then launches" + echo "A pelican.server to help site development. It doesn't read" + echo "your pelican options so you edit any paths in your Makefile" + echo "you will need to edit it as well" + exit 3 +} + +function alive() { + kill -0 $1 >/dev/null 2>&1 +} + +function shut_down(){ + PID=$(cat $SRV_PID) + if [[ $? -eq 0 ]]; then + if alive $PID; then + echo "Killing pelican.server" + kill $PID + else + echo "Stale PID, deleting" + fi + rm $SRV_PID + else + echo "pelican.server PIDFile not found" + fi + + PID=$(cat $PELICAN_PID) + if [[ $? -eq 0 ]]; then + if alive $PID; then + echo "Killing Pelican" + kill $PID + else + echo "Stale PID, deleting" + fi + rm $PELICAN_PID + else + echo "Pelican PIDFile not found" + fi +} + +function start_up(){ + echo "Starting up Pelican and pelican.server" + shift + mkdir -p $(OUTPUTDIR) + $PELICAN --debug --autoreload -r $INPUTDIR -o $OUTPUTDIR -s $CONFFILE $PELICANOPTS & + pelican_pid=$! + echo $pelican_pid > $PELICAN_PID + cd $OUTPUTDIR + $PY -m pelican.server & + srv_pid=$! + echo $srv_pid > $SRV_PID + cd $BASEDIR + sleep 1 + if ! alive $pelican_pid ; then + echo "Pelican didn't start. Is the pelican package installed?" + return 1 + elif ! alive $srv_pid ; then + echo "pelican.server didn't start. Is the pelican package installed?" + return 1 + fi + echo 'Pelican and pelican.server processes now running in background.' +} + +### +# MAIN +### +[[ $# -ne 1 ]] && usage +if [[ $1 == "stop" ]]; then + shut_down +elif [[ $1 == "restart" ]]; then + shut_down + start_up +elif [[ $1 == "start" ]]; then + if ! start_up; then + shut_down + fi +else + usage +fi diff --git a/pelicanconf.py b/pelicanconf.py new file mode 100644 index 0000000..eac4b16 --- /dev/null +++ b/pelicanconf.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- # + +# Probably replace those with simpler methods: +from __future__ import unicode_literals +from operator import itemgetter, methodcaller + +AUTHOR = 'Philipp Schönberger' +EMAIL = 'mail AT phschoen.de' +SITENAME = 'https://phschoen.de' +OUTPUT_PATH = 'output/DEV/' + +# Base URL this page is hosted at: +SITENAME = 'phschoen.de' +# SITEURL = 'http://skylx125.ndsatcom.com:8000' +SITEURL = 'http://localhost:8000' + +# Timezone is GMT+1: +TIMEZONE = 'Europe/Paris' +# Using a simple date format: +DEFAULT_DATE_FORMAT = ('%d %b %Y') +DEFAULT_LANG = 'en' +DATE_FORMATS = { + 'en': '%-d/%b/%Y', + 'de': '%-d/%-m/%Y', +} +LOCALE = ('usa', 'de', # On Windows + 'en_US', 'de_DE' # On Unix/Linux + ) + +# We are using the custom minimal theme: +THEME = 'themes/minimal' +# THEME = 'themes/pelican-chunk' +#THEME = 'themes/pelican-themes/bootstrap4-standard' + +DELETE_OUTPUT_DIRECTORY = True +# MARKUP = ('md',) +# # Markdown Configuration: +# MARKDOWN = { +# 'extension_configs': { +# 'markdown.extensions.codehilite': {'css_class': 'highlight'}, +# 'markdown.extensions.toc' : {}, +# 'markdown.extensions.extra': {}, +# 'markdown.extensions.meta': {}, +# }, +# 'output_format': 'html5', +# } +# We don't use relative URLs: +RELATIVE_URLS = False + +# Edit predefined pathes: +ARCHIVES_SAVE_AS = 'pages/index.html' + +# Generates nice URLs for pages: +PAGE_URL = '{slug}' +PAGE_SAVE_AS = '{slug}/index.html' + +# Generate nice URLs for articles: +ARTICLE_EXCLUDES = (('pages',)) +ARTICLE_URL = 'blog/{slug}' +ARTICLE_SAVE_AS = 'blog/{slug}/index.html' + +# Generate nice URLs for tags: +TAG_URL = 'tag/{name}/' +TAG_SAVE_AS = 'tag/{name}/index.html' +TAGS_SAVE_AS = 'tags/index.html' + +# Generate nice URLs for categories: +CATEGORY_URL = 'category/{name}/' +CATEGORY_SAVE_AS = 'category/{name}/index.html' + +# Setup the RSS/ATOM feeds: +FEED_DOMAIN = SITEURL +FEED_MAX_ITEMS = 10 +# We only want RSS/ATOM Feeds for all articles, exclude categories: +FEED_RSS = 'feeds/rss.xml' +TAG_FEED_RSS = 'feeds/{slug}.rss.xml' +CATEGORY_FEED_RSS = None + +# Feed generation is usually not desired when developing +FEED_ATOM = 'feeds/atom.xml' +FEED_ALL_ATOM = None +TAG_FEED_ATOM = None +CATEGORY_FEED_ATOM = None +TRANSLATION_FEED_ATOM = None + + +# Separate page directory and articles directory: +PAGE_PATHS = ['pages'] +ARTICLE_PATHS = ['blog'] + +# Save index as blog/index.html instead of index.html: +INDEX_SAVE_AS = 'blog/index.html' + +# Navigation menu: +SECTIONS = [('blog', '/blog'), + ('Projects', '/projects'), + ('Gallery', '/gallery'), + ('about', '/about'), + ('Impressum', '/impressum'), + ] + +# Links to display in the footer: +LINKS = [('bsd', 'http://www.opensource.org/licenses/BSD-3-Clause'), + ('xhtml', 'http://validator.w3.org/check/referer'), + ('css3', 'http://jigsaw.w3.org/css-validator/check/referer?profile=css'), + ('pelican', 'https://github.com/getpelican'), + ] + +# Set some default category: +DEFAULT_CATEGORY = 'uncategorized' + +# Social widget +# SOCIAL = (('You can add links in your config file', '#'), +# ('Another social link', '#'),) + +# DEFAULT_PAGINATION = 10 + +PLUGIN_PATHS = ['plugins', ] +PLUGINS = ['lightgallery', 'i18n_subsites'] +# PLUGIN_PATHS = ['./pelican-plugins/', ] + +# PLUGINS = ['i18n_subsites', ] +# JINJA_ENVIRONMENT = { +# 'extensions': ['jinja2.ext.i18n'], +# } + + +GITHUB_SOURCE_PATH = "wooot" + +I18N_SUBSITES = {'en': {'SITENAME': 'phschoen.com', + }, + 'de': {'SITENAME': 'phschoen.de', + }, + } + +languages_lookup = {'en': 'English', + 'de': 'Deutsch', + } + + +def lookup_lang_name(lang_code): + return languages_lookup[lang_code] + + +def getGitHubPage(source_file): + filename = getBasename(source_file) + return '{0}/{1}'.format(GITHUB_SOURCE_PATH, filename) + + +def getBasename(path): + return "name " + + +def month_name(month_number): + return "month_name" + + +def sortTupleByIndex(items, index=0, reverse=True): + return sorted(items, key=lambda tup: len(tup[index]), reverse=reverse) + + +def sortDictByKey(items, key, reverse=True, default=None): + if default is None: + return sorted(items, key=itemgetter(key), reverse=reverse) + return sorted(items, key=methodcaller('get', key, default), reverse=reverse) + + +JINJA_FILTERS = {'month_name': month_name, + 'sortTupleByIndex': sortTupleByIndex, + 'sortDictByKey': sortDictByKey, + 'basename': getBasename, + 'asGitHubPage': getGitHubPage, + 'lookup_lang_name': lookup_lang_name, + } diff --git a/plugins/i18n_subsites/README.rst b/plugins/i18n_subsites/README.rst new file mode 100644 index 0000000..340109b --- /dev/null +++ b/plugins/i18n_subsites/README.rst @@ -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 + `_. 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/ diff --git a/plugins/i18n_subsites/__init__.py b/plugins/i18n_subsites/__init__.py new file mode 100644 index 0000000..7dfbde0 --- /dev/null +++ b/plugins/i18n_subsites/__init__.py @@ -0,0 +1 @@ +from .i18n_subsites import * diff --git a/plugins/i18n_subsites/i18n_subsites.py b/plugins/i18n_subsites/i18n_subsites.py new file mode 100644 index 0000000..dc27799 --- /dev/null +++ b/plugins/i18n_subsites/i18n_subsites.py @@ -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) diff --git a/plugins/i18n_subsites/implementing_language_buttons.rst b/plugins/i18n_subsites/implementing_language_buttons.rst new file mode 100644 index 0000000..55b7bf3 --- /dev/null +++ b/plugins/i18n_subsites/implementing_language_buttons.rst @@ -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 + + +