Compare commits
70 Commits
Author | SHA1 | Date | |
---|---|---|---|
0f5aa1a6d2 | |||
801191a72b | |||
be361b5a2c | |||
03619c894f | |||
d517decd2c | |||
876959da10 | |||
b05c3b9f81 | |||
aa980d6e32 | |||
9f016f6810 | |||
bd3ad29eea | |||
d997c2ae71 | |||
c172715773 | |||
8b16188061 | |||
2d5038f221 | |||
133fc501c2 | |||
a79520218a | |||
0f4e141af0 | |||
0134348744 | |||
60f25e0a17 | |||
163f9501b8 | |||
b5ce60fe2d | |||
66177c384e | |||
0b26fac931 | |||
d04b5731f4 | |||
5283e36bdf | |||
fd638eddca | |||
b4e6df40f5 | |||
fdcd77ed5d | |||
afa72e6757 | |||
4055fed3c4 | |||
891a5721c4 | |||
8ee1ed22f6 | |||
fb01f2f1ac | |||
f6a9695dc9 | |||
c992f0f0dc | |||
8a58f72c64 | |||
18db1c10e9 | |||
9f368245f6 | |||
ea782e8cbb | |||
80b467d9de | |||
8143d69537 | |||
38c20a1b25 | |||
eb42eff61e | |||
15bd56890d | |||
8946007616 | |||
512eafcc02 | |||
7557af402e | |||
61fd25da39 | |||
0d01502405 | |||
e4d76aac52 | |||
f2d327416f | |||
df545d8597 | |||
c28db4d91a | |||
4c5168e0ac | |||
8696d8c176 | |||
f47fe95757 | |||
380a16c158 | |||
ebd73f39f5 | |||
1fe7d8c176 | |||
c624023018 | |||
2ba0774163 | |||
cecdf49355 | |||
c39ec433b5 | |||
74c085c442 | |||
39241ee741 | |||
1ad4a3b20b | |||
57aca4f189 | |||
25e01d8bf2 | |||
453e375772 | |||
e130b8f379 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,8 +2,9 @@ cache/*
|
|||||||
output/*
|
output/*
|
||||||
pelican-plugins/*
|
pelican-plugins/*
|
||||||
pelican-themes/*
|
pelican-themes/*
|
||||||
content
|
content/Enseignements/2*
|
||||||
__pycache__
|
__pycache__
|
||||||
*.pid
|
*.pid
|
||||||
venv/
|
venv/
|
||||||
.vim/
|
.vim/
|
||||||
|
.python-version
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
|||||||
[submodule "pelican-plugins"]
|
|
||||||
path = pelican-plugins
|
|
||||||
url = https://github.com/getpelican/pelican-plugins
|
|
60
Makefile
60
Makefile
@ -2,29 +2,32 @@ PY?=python3
|
|||||||
PELICAN?=pelican
|
PELICAN?=pelican
|
||||||
PELICANOPTS=
|
PELICANOPTS=
|
||||||
|
|
||||||
YEARSUBFOLDER=enseignements/2019-2020/
|
YEARSUBFOLDER=
|
||||||
|
|
||||||
BASEDIR=$(CURDIR)
|
BASEDIR=$(CURDIR)
|
||||||
INPUTDIR=$(BASEDIR)/content
|
INPUTDIR=$(BASEDIR)/content
|
||||||
OUTPUTDIR=$(BASEDIR)/output
|
OUTPUTDIR=$(BASEDIR)/output
|
||||||
CONFFILE=$(BASEDIR)/pelicanconf.py
|
CONFFILE=$(BASEDIR)/pelicanconf.py
|
||||||
PUBLISHCONF=$(BASEDIR)/publishconf.py
|
PUBLISHCONF=$(BASEDIR)/publishconf.py
|
||||||
FAKEDIR=../../output/
|
|
||||||
|
|
||||||
FTP_HOST=localhost
|
FAKEDIR=../output/
|
||||||
FTP_USER=anonymous
|
|
||||||
FTP_TARGET_DIR=/
|
|
||||||
|
|
||||||
SSH_HOST=localhost
|
SSH_HOST=Embrevade
|
||||||
SSH_CONF=Embrevade
|
SSH_PORT=22
|
||||||
#SSH_TARGET_DIR=/var/docker/opytex.org/www/
|
SSH_USER=sshcontent
|
||||||
SSH_TARGET_DIR=/home/sshcontent/opytex.org/www/$(YEARSUBFOLDER)
|
SSH_TARGET_DIR=/home/sshcontent/opytex.org/www/
|
||||||
|
|
||||||
|
RSYNC_EXCLUDE=--exclude "pymath" --exclude "opytex" --exclude "enseignements"
|
||||||
|
|
||||||
DEBUG ?= 0
|
DEBUG ?= 0
|
||||||
ifeq ($(DEBUG), 1)
|
ifeq ($(DEBUG), 1)
|
||||||
PELICANOPTS += -D
|
PELICANOPTS += -D
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
RELATIVE ?= 1
|
||||||
|
ifeq ($(RELATIVE), 1)
|
||||||
|
PELICANOPTS += --relative-urls
|
||||||
|
endif
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo 'Makefile for a pelican Web site '
|
@echo 'Makefile for a pelican Web site '
|
||||||
@echo ' '
|
@echo ' '
|
||||||
@ -34,17 +37,13 @@ help:
|
|||||||
@echo ' make regenerate regenerate files upon modification '
|
@echo ' make regenerate regenerate files upon modification '
|
||||||
@echo ' make publish generate using production settings '
|
@echo ' make publish generate using production settings '
|
||||||
@echo ' make serve [PORT=8000] serve site at http://localhost:8000'
|
@echo ' make serve [PORT=8000] serve site at http://localhost:8000'
|
||||||
@echo ' make devserver [PORT=8000] start/restart develop_server.sh '
|
@echo ' make serve-global [SERVER=0.0.0.0] serve (as root) to $(SERVER):80 '
|
||||||
@echo ' make stopserver stop local server '
|
@echo ' make devserver [PORT=8000] serve and regenerate together '
|
||||||
@echo ' make ssh_upload upload the web site via SSH '
|
@echo ' make ssh_upload upload the web site via SSH '
|
||||||
@echo ' make rsync_upload upload the web site via rsync+ssh '
|
@echo ' make rsync_upload upload the web site via rsync+ssh '
|
||||||
@echo ' make dropbox_upload upload the web site via Dropbox '
|
|
||||||
@echo ' make ftp_upload upload the web site via FTP '
|
|
||||||
@echo ' make s3_upload upload the web site via S3 '
|
|
||||||
@echo ' make cf_upload upload the web site via Cloud Files'
|
|
||||||
@echo ' make github upload the web site via gh-pages '
|
|
||||||
@echo ' '
|
@echo ' '
|
||||||
@echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html '
|
@echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html '
|
||||||
|
@echo 'Set the RELATIVE variable to 1 to enable relative urls '
|
||||||
@echo ' '
|
@echo ' '
|
||||||
|
|
||||||
html:
|
html:
|
||||||
@ -59,28 +58,29 @@ regenerate:
|
|||||||
|
|
||||||
serve:
|
serve:
|
||||||
ifdef PORT
|
ifdef PORT
|
||||||
cd $(OUTPUTDIR) && $(PY) -m pelican.server $(PORT)
|
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT)
|
||||||
else
|
else
|
||||||
cd $(OUTPUTDIR) && $(PY) -m pelican.server
|
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
serve-global:
|
||||||
|
ifdef SERVER
|
||||||
|
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b $(SERVER)
|
||||||
|
else
|
||||||
|
$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b 0.0.0.0
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
devserver:
|
devserver:
|
||||||
ifdef PORT
|
ifdef PORT
|
||||||
$(BASEDIR)/develop_server.sh restart $(PORT)
|
$(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT)
|
||||||
else
|
else
|
||||||
$(BASEDIR)/develop_server.sh restart
|
$(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
stopserver:
|
|
||||||
kill -9 `cat pelican.pid`
|
|
||||||
kill -9 `cat srv.pid`
|
|
||||||
@echo 'Stopped Pelican and SimpleHTTPServer processes running in background.'
|
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS)
|
$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS)
|
||||||
|
|
||||||
ssh_upload: publish
|
|
||||||
scp -r $(OUTPUTDIR)/* $(SSH_CONF):$(SSH_TARGET_DIR)
|
|
||||||
|
|
||||||
rsync_upload: publish
|
rsync_upload: publish
|
||||||
#rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --cvs-exclude --delete $(OUTPUTDIR)/ $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
|
#rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --cvs-exclude --delete $(OUTPUTDIR)/ $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
|
||||||
@ -88,6 +88,6 @@ rsync_upload: publish
|
|||||||
|
|
||||||
fake_upload: html
|
fake_upload: html
|
||||||
mkdir -p $(FAKEDIR)$(YEARSUBFOLDER)
|
mkdir -p $(FAKEDIR)$(YEARSUBFOLDER)
|
||||||
rsync -P -rvzc --delete $(OUTPUTDIR)/ $(FAKEDIR)$(YEARSUBFOLDER) --cvs-exclude
|
rsync -P -rvzc --delete $(RSYNC_EXCLUDE) $(OUTPUTDIR)/ $(FAKEDIR)$(YEARSUBFOLDER) --cvs-exclude
|
||||||
|
|
||||||
.PHONY: html help clean regenerate serve devserver publish ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload cf_upload github import_ens
|
.PHONY: html help clean regenerate serve serve-global devserver publish ssh_upload rsync_upload
|
||||||
|
77
content/blog/2201_questions_flashs.rst
Normal file
77
content/blog/2201_questions_flashs.rst
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
Questions flashs
|
||||||
|
################
|
||||||
|
|
||||||
|
:date: 2022-02-02
|
||||||
|
:modified: 2022-02-02
|
||||||
|
:tags: Partage de pratiques, pédagogie
|
||||||
|
:category: Enseignement
|
||||||
|
:authors: Benjamin Bertrand
|
||||||
|
:summary: Compte rendu d'une présentation des questions flashs que j'ai réalisé dans le groupe partage de pratiques.
|
||||||
|
|
||||||
|
Notes prises pas Audrey lors de ma présentation de ma pratique des questions flashs au groupe "partage de pratiques".
|
||||||
|
|
||||||
|
Objectifs
|
||||||
|
=========
|
||||||
|
|
||||||
|
- **Mise en place de la classe :** Les élèves savent en rentrant que le
|
||||||
|
cours commence avec des questions flashs. Ils sont tout de suite mis
|
||||||
|
en activité.
|
||||||
|
|
||||||
|
- **Échauffement :** Les questions flashs sont conçues pour mettre en
|
||||||
|
les élèves en situation de réussite. Comme un échauffement sportif,
|
||||||
|
les élèves commencent par quelque chose qu’ils savent qu’ils vont
|
||||||
|
réussir.
|
||||||
|
|
||||||
|
- **Révision et préparation :** les questions posées servent à préparer
|
||||||
|
un chapitre qui arrivera dans un future proche, à remobiliser des
|
||||||
|
connaissances étudiées que l’enseignant juge fondamental.
|
||||||
|
L’enseignant gère le rythme pour que les élèves ancrent les savoirs.
|
||||||
|
|
||||||
|
Quoi
|
||||||
|
====
|
||||||
|
|
||||||
|
**4 questions** appelant à des réponses courtes, axées sur
|
||||||
|
les **bases de la matière** et sur les points susceptibles de générer
|
||||||
|
des difficultés chez les élèves. Les 4 questions doivent aborder des
|
||||||
|
**notions différentes** . L’élève ne doit pas après la première question
|
||||||
|
flash se dire qu’il ne sera pas faire les autres. Ces 4 notions pourront
|
||||||
|
être reprises à chaque séance sur une semaine ou deux.
|
||||||
|
|
||||||
|
Exemple :
|
||||||
|
---------
|
||||||
|
|
||||||
|
- Question 1 sur les pourcentages
|
||||||
|
- Question 2 sur les fractions
|
||||||
|
- Question 3 sur les vecteurs
|
||||||
|
- Question 4 sur les conversions
|
||||||
|
|
||||||
|
Sur la semaine les quatre thèmes sont repris. On ajuste la difficulté pour qu'ils soient en réussite mais qu'ils renforce une connaissance.
|
||||||
|
|
||||||
|
`Diapositives du jour 1 <https://opytex.org/enseignements/2021-2022/2nd/Questions_flashs/P3/QF_S01-1.pdf>`_
|
||||||
|
|
||||||
|
`Diapositives du jour 2 <https://opytex.org/enseignements/2021-2022/2nd/Questions_flashs/P3/QF_S01-2.pdf>`_
|
||||||
|
|
||||||
|
`Diapositives du jour 3 <https://opytex.org/enseignements/2021-2022/2nd/Questions_flashs/P3/QF_S01-3.pdf>`_
|
||||||
|
|
||||||
|
Quand
|
||||||
|
=====
|
||||||
|
|
||||||
|
A **chaque début de cours** (sauf les jours de DS). Même quand on a deux heures dans la même journée (ça fait toujours tiquer les élèves ça!)
|
||||||
|
|
||||||
|
Support
|
||||||
|
=======
|
||||||
|
|
||||||
|
Le professeur projette les questions au tableau durant 30 secondes à 1 minute, les élèves répondent individuellement à l’écrit, soit sur un brouillon (quand le travail n’est pas ramassé), soit sur la fiche-réponse donnée par l’enseignant (les jours où le travail est ramassé/noté).
|
||||||
|
|
||||||
|
Correction – notation
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Le professeur ramasse et note le travail environ une fois sur quatre (à ajuster selon le volume horaire dont il bénéficie avec la classe).
|
||||||
|
|
||||||
|
Le reste du temps, les questions-flash sont corrigées immédiatement après avoir répondu aux 4 questions : les élèves indiquent leurs résultats. On ne va pas plus loin dès lors que tous ont la bonne réponse. Dans le cas contraire, un élève ou le professeur donne les détails de la correction. C’est l’occasion d’insister sur la notion qui pose problème (notation, rédaction, erreurs fréquentes...)
|
||||||
|
|
||||||
|
Variante
|
||||||
|
========
|
||||||
|
|
||||||
|
Selon les matières, on peut imaginer des questions-flash sous forme de phrase à trou, de questions donnant lieu à une courte énumération, de traduction, de définition… l’essentiel étant d’appeler à une **réponse courte** sans explication écrite et qui ne demande pas un raisonnement trop complexe.
|
||||||
|
|
120
content/blog/2205_expose_renverse.rst
Normal file
120
content/blog/2205_expose_renverse.rst
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
Fonctions de référence en mode renversé
|
||||||
|
########################################
|
||||||
|
|
||||||
|
:date: 2022-05-25
|
||||||
|
:modified: 2022-05-25
|
||||||
|
:tags: Partage de pratiques, pédagogie, Classe renversée, Évaluation, 2nd
|
||||||
|
:category: Enseignement
|
||||||
|
:authors: Benjamin Bertrand
|
||||||
|
:summary: Compte rendu d'un travail fait en classe autour des fonctions de référence.
|
||||||
|
|
||||||
|
Au départ, on était partis, avec Camille, pour faire travailler nos élèves de 2nd sur les fonctions de référence avec des exposés. On leur donne des fonctions avec le nom et la formule. Ils étudient en groupe une seule fonction pour répondre à une série de questions. Enfin ils présentent à l'oral cette fonction.
|
||||||
|
|
||||||
|
.. image:: {attach}2205_expose_renverse/2P_exposes.pdf
|
||||||
|
:height: 200px
|
||||||
|
:alt: consignes initiales pour les exposés.
|
||||||
|
|
||||||
|
Plusieurs problèmes sont ressortis après cette planification. Mais pour l'essentiel, faire passer à l'oral 8 groupes prend beaucoup de temps et c'est du temps où les autres élèves ne font rien et le plus souvent n'écoutent pas. Pas très satisfaisant quand le programme veut que toutes les fonctions de référence soient étudiées.
|
||||||
|
|
||||||
|
Récemment, j'ai découvert le concept de classe renversée de Jean-Charles Caillez. Son propos peut être un peu trop rapidement résumé à "dans ma classe, il y a un élève et 80 enseignants". Il pose plusieurs questions aux élèves qui, en groupe, sont en charge d'y répondre. En parallèle de cette idée bousculante, il a développé de nombreuses "technologies" pour rendre la restitution un peu plus interactive.
|
||||||
|
|
||||||
|
J'ai donc adapté notre idée de départ en m'inspirant de l'approche renversée.
|
||||||
|
|
||||||
|
Redéfinition du l'activité: première séance
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
La première fois que je leur parle de cette activité, il reste 15 minutes de cours. Ils ont déjà fait des questions flashs. Je viens de leur expliquer le concept de domaine de définition et ils l'ont travaillé en parallèle d'études graphiques de fonctions. Je leur explique qu'ils vont commencer une activité de groupe qui conduira à la création d'un poster et qui sera notée. Je suis honnête avec eux, je n'ai pas encore fixé le cadre de l'évolution, je ne peux pas encore tout leur expliquer.
|
||||||
|
|
||||||
|
Les élèves sont déjà en îlots, les groupes sont donc déjà constitués. J'attribue à chaque groupe une fonction en ne leur donnant que son nom. Il y a 4 fonctions de référence à étudier en 2nd: la fonction carrée, la fonction cube, la fonction racine carrée et la fonction inverse. En amont, j'ai veillé à avoir 8 ilots. C'est parfait: deux groupes par fonction.
|
||||||
|
|
||||||
|
La première phase de recherche commence et voilà la consigne: "avec l'aide de ce que vous voulez, décrivez moi votre fonction. À la fin, vous devrez produire un poster mais je n'ai pas de feuille A3 alors notez ce qui vous semble important, vous réaliserez le poster plus tard". Ils commencent à travailler, beaucoup ne savent pas à quoi correspond leur fonction. Un élève demande si dans "tout ce que vous voulez", ils peuvent utiliser leur téléphone portable. Je réponds oui, les écrans s'allument et les partages de connection s'activent.
|
||||||
|
|
||||||
|
Le temps d'ouvrir les premiers sites internet, d'avoir un premier contact avec leur fonction, la fin du cours sonne.
|
||||||
|
|
||||||
|
Explication du mode d'évaluation: deuxième séance
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
Comme à la première séance, il reste 15 minutes quand on se replonge dans cette activité. Je rappelle le cadre de cette activité et comme j'ai fait mes devoirs, je peux leur expliquer comment ils seront évalués.
|
||||||
|
|
||||||
|
Dans deux cours, les posters devront être terminés. Je leur donnerai un QCM qu'ils devront réaliser individuellement avec l'aide unique des posters réalisés par les autres élèves. Leur note sera la somme de leur réussite au QCM et de la moyenne des résultats qu'auront eu leur camarade sur les questions qui portent sur leur fonction. Ils demandent quels types de questions je pourrai leur poser. Je ne réponds que très vaguement en leur disant que ce sera des questions d'un niveau 2nd sur des concepts déjà étudiés cette année.
|
||||||
|
|
||||||
|
Ils se remettent au travail. À part deux élèves en gros décrochage et que je n'arrive pas à raccrocher, tous les élèves sont actifs. Ils trouvent rapidement toutes les informations dont ils ont besoin. Malheureusement presque personne ne prend sa calculatrice pour vérifier les graphiques qu'ils trouvent. Mais le temps est vite écoulé.
|
||||||
|
|
||||||
|
Je me rends compte que si je veux que les posters soient terminés et qu'il y ait du contenu, je vais devoir leur donner plus de temps que ce que j'avais initialement planifié (c'est toujours comme ça...).
|
||||||
|
|
||||||
|
Réalisation des posters: troisième séance
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
Cette fois, je suis bien décidé à leur donner le temps dont ils auront besoin. Ils font les questions flashs, je rappelle le cadre et je leur distribue une feuille A3. Je leur dis quand même qu'ils ont du temps mais qu'ils ne faut pas qu'ils s'endorment, les posters doivent être terminés au prochain cours pour les QCM. C'est la dernière fois que je les verrai avant le conseil de classe.
|
||||||
|
|
||||||
|
Comme au cours précédent, ils se mettent volontairement au travail. Très vite, le problème du tri des informations est soulevé par certain groupe. Que doivent-ils faire apparaitre sur leur poster? Ils ne comprennent pas tout ce qu'ils lisent. Je les incite à n'écrire que ce qu'ils comprennent. Je pousse aussi les élèves les moins à l'aise à se montrer critique vis à vis de ce qu'ils écrivent. S'ils ne comprennent pas, d'autres ne comprendront pas, il est alors nécessaire d'améliorer les explications.
|
||||||
|
|
||||||
|
Je ne l'avais pas anticipé, mais plusieurs groupes ne veulent pas dessiner le graphique des fonctions, ils disent qu'il l'imprimeront chez eux. Je précise donc les règles: "rien n'a le droit d'être imprimé tout doit être fait à la main". Mais ils ont le droit d'écrire sur une autre feuille puis coller le papier sur le poster.
|
||||||
|
|
||||||
|
C'est alors que dans certains groupes, une belle dynamique se crée. Les élèves se partagent les tâches et travaillent en parallèle. Un élève poursuit les recherches sur internet, deux autres se lancent dans la réalisation d'un graphique et s'interrogent sur la méthode à adopter et un quatrième s'occupe de rendre le poster lisible et agréable.
|
||||||
|
|
||||||
|
Mais la grosse demi-heure passe vite, les posters ne sont pas terminés. Je ramasse les productions pour être sûr qu'aucune ne soit perdue ou restée dans le sac d'un élève absent!
|
||||||
|
|
||||||
|
Finitions et évaluation: quatrième séance
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
Dernières 15 minutes de préparation, je mets un gros chrono au tableau en mode TopChef, les équipes s'activent. Certain ont travaillé chez eux et sont arrivés avec des morceaux de papier près à coller. Le stress monte. Un groupe est en phase d'abandon. Il estime qu'il n'aura pas le temps de terminer et donc que ça ne sert à rien de continuer. J'essaie de mettre une pièce pour relancer la motivation. J'explique qu'ils ne sont pas obligé de faire un beau poster, qu'ils doivent se montrer efficaces et sélectionner uniquement les informations les plus importante. Je leur donne les astuces d'organisation des autres groupes et ils se relancent.
|
||||||
|
|
||||||
|
Les 15 minutes se terminent, c'est l'heure de l'évaluation
|
||||||
|
|
||||||
|
.. image:: {attach}2205_expose_renverse/sujets_QCM.pdf
|
||||||
|
:height: 200px
|
||||||
|
:alt: 2 exemples de sujets du QCM
|
||||||
|
|
||||||
|
|
||||||
|
On dispose 4 posters avec 4 fonctions différentes dans le fond de la classe et les 4 autres vers le tableau. Les élèves vont là où leur poster n'est pas. Je distribue les sujets. Ils doivent répondre individuellement avec les posters qu'ils ont à leur disposition. Pas facile de les empêcher de communiquer mais en 15minutes tout le monde a terminé.
|
||||||
|
|
||||||
|
Réalisations
|
||||||
|
============
|
||||||
|
|
||||||
|
Voici les posters réalisés par les groupes
|
||||||
|
|
||||||
|
.. image:: {attach}2205_expose_renverse/poster_carre1.pdf
|
||||||
|
:height: 200px
|
||||||
|
:alt: Présentation de la fonction carrée 1
|
||||||
|
|
||||||
|
.. image:: {attach}2205_expose_renverse/poster_carre2.pdf
|
||||||
|
:height: 200px
|
||||||
|
:alt: Présentation de la fonction carrée 2
|
||||||
|
|
||||||
|
.. image:: {attach}2205_expose_renverse/poster_cube1.pdf
|
||||||
|
:height: 200px
|
||||||
|
:alt: Présentation de la fonction cube 1
|
||||||
|
|
||||||
|
.. image:: {attach}2205_expose_renverse/poster_cube2.pdf
|
||||||
|
:height: 200px
|
||||||
|
:alt: Présentation de la fonction cube 2
|
||||||
|
|
||||||
|
.. image:: {attach}2205_expose_renverse/poster_inverse1.pdf
|
||||||
|
:height: 200px
|
||||||
|
:alt: Présentation de la fonction inverse 1
|
||||||
|
|
||||||
|
.. image:: {attach}2205_expose_renverse/poster_inverse2.pdf
|
||||||
|
:height: 200px
|
||||||
|
:alt: Présentation de la fonction inverse 2
|
||||||
|
|
||||||
|
.. image:: {attach}2205_expose_renverse/poster_racine1.pdf
|
||||||
|
:height: 200px
|
||||||
|
:alt: Présentation de la fonction racine carré 1
|
||||||
|
|
||||||
|
.. image:: {attach}2205_expose_renverse/poster_racine2.pdf
|
||||||
|
:height: 200px
|
||||||
|
:alt: Présentation de la fonction racine carré 2
|
||||||
|
|
||||||
|
|
||||||
|
Les plus pertinents sont sélectionnés et imprimés. Ils sont, quand c'est nécessaire, corrigés. C'est le cours pour cette séquence.
|
||||||
|
|
||||||
|
|
||||||
|
Bilan
|
||||||
|
=====
|
||||||
|
|
||||||
|
J'ai du mal à me rendre compte de ce que les élèves garderont comme connaissance des fonctions étudiées par eux-mêmes et par les autres. Ils ont surtout recopié les informations qu'ils trouvaient sur Internet sans se dire qu'ils étaient capables eux aussi de réaliser seuls les tableaux de valeurs des fonctions ou les tableaux de signes et de variations.
|
||||||
|
|
||||||
|
Par contre, ils ont été presque tous actifs du début à la fin sur cette activité. À aucun moment, ils ne se sont montrés passifs vis à vis du travail réalisé.
|
||||||
|
|
||||||
|
Et puis ça fait du bien de faire une séquence qui sort un peu de l'ordinaire. Toute l'année, j'ai du mal à les faire travailler de façon constructive en groupe. J'ai essayé les cahiers de groupe, les exercices de recherche en groupe mais peu de choses ont fonctionné. Là, j'ai pu constater de belles collaborations et un vrai travail de groupe auto-organisé.
|
BIN
content/blog/2205_expose_renverse/2P_exposes.pdf
Normal file
BIN
content/blog/2205_expose_renverse/2P_exposes.pdf
Normal file
Binary file not shown.
BIN
content/blog/2205_expose_renverse/poster_carre1.pdf
Normal file
BIN
content/blog/2205_expose_renverse/poster_carre1.pdf
Normal file
Binary file not shown.
BIN
content/blog/2205_expose_renverse/poster_carre2.pdf
Normal file
BIN
content/blog/2205_expose_renverse/poster_carre2.pdf
Normal file
Binary file not shown.
BIN
content/blog/2205_expose_renverse/poster_cube1.pdf
Normal file
BIN
content/blog/2205_expose_renverse/poster_cube1.pdf
Normal file
Binary file not shown.
BIN
content/blog/2205_expose_renverse/poster_cube2.pdf
Normal file
BIN
content/blog/2205_expose_renverse/poster_cube2.pdf
Normal file
Binary file not shown.
BIN
content/blog/2205_expose_renverse/poster_inverse1.pdf
Normal file
BIN
content/blog/2205_expose_renverse/poster_inverse1.pdf
Normal file
Binary file not shown.
BIN
content/blog/2205_expose_renverse/poster_inverse2.pdf
Normal file
BIN
content/blog/2205_expose_renverse/poster_inverse2.pdf
Normal file
Binary file not shown.
BIN
content/blog/2205_expose_renverse/poster_racine1.pdf
Normal file
BIN
content/blog/2205_expose_renverse/poster_racine1.pdf
Normal file
Binary file not shown.
BIN
content/blog/2205_expose_renverse/poster_racine2.pdf
Normal file
BIN
content/blog/2205_expose_renverse/poster_racine2.pdf
Normal file
Binary file not shown.
BIN
content/blog/2205_expose_renverse/sujets_QCM.pdf
Normal file
BIN
content/blog/2205_expose_renverse/sujets_QCM.pdf
Normal file
Binary file not shown.
13
content/pages/404.rst
Normal file
13
content/pages/404.rst
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
404: Not Found
|
||||||
|
##############
|
||||||
|
|
||||||
|
:date: 2023-07-25
|
||||||
|
:modified: 2023-07-25
|
||||||
|
:slug: 404
|
||||||
|
:status: hidden
|
||||||
|
:authors: Benjamin Bertrand
|
||||||
|
:save_as: 404.html
|
||||||
|
|
||||||
|
Désolé, cette page n'existe pas.
|
||||||
|
|
||||||
|
`Retour en terrain connu </index.html>`_
|
24
content/pages/About.rst
Normal file
24
content/pages/About.rst
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
À propos
|
||||||
|
#########
|
||||||
|
|
||||||
|
:date: 2020-06-04
|
||||||
|
:modified: 2020-06-04
|
||||||
|
:authors: Benjamin Bertrand
|
||||||
|
:summary: À propos de ce site et de l'auteur.
|
||||||
|
|
||||||
|
Ce site
|
||||||
|
=======
|
||||||
|
|
||||||
|
Ce site est né après ma première année d'enseignement. Je quittais l'établissement où j'avais été stagiaire et où j'avais beaucoup travaillé avec mes collègues. L'idée était d'avoir un lieu où ce qui avait été fait pourrait être disponible et réutilisé.
|
||||||
|
|
||||||
|
Aujourd'hui, il ne reste malheureusement pas grand chose de cette période à cause d'un travail d'édition trop important à réaliser pour le rendre publiable. Par contre la volonté d'avoir un lieu où tout ce que je produis pour mes cours est accessible à n'important qui est resté.
|
||||||
|
|
||||||
|
Il m'a fallut plusieurs années avant de réussir à trouver une façon de m'organiser pour que l'édition ne soit pas un frein à la production documents mais une façon efficace de structurer et pérenniser mon travail. Ce site en est le résultat.
|
||||||
|
|
||||||
|
Il repose sur une série de fichiers `reStructuredText <http://docutils.sourceforge.net/>`_ passé à la moulinette de `Pelican <https://blog.getpelican.com/>`_ pour produire le code HTML que vous voyez. Le tout est géré avec git et auto-hébergé pour garder un maximum d'indépendance.
|
||||||
|
|
||||||
|
L'auteur
|
||||||
|
========
|
||||||
|
|
||||||
|
|
||||||
|
|
55
content/pages/Enseignements.rst
Normal file
55
content/pages/Enseignements.rst
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
Tout sur mes cours
|
||||||
|
##################
|
||||||
|
|
||||||
|
:date: 2016-01-23
|
||||||
|
:modified: 2016-01-24
|
||||||
|
:authors: Benjamin Bertrand
|
||||||
|
:summary: Résumé sur ma façon d'enseigner depuis le début ou presque.
|
||||||
|
|
||||||
|
|
||||||
|
Les années qui s'écoulent
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Au lycée de Bellegarde.
|
||||||
|
|
||||||
|
- `2022/2023 </enseignements/2022-2023/>`_ Lycée de Bellegarde (Secondes, SNT, spécialité NSI, Première ST et enseignements scientifique)
|
||||||
|
- `2021/2022 </enseignements/2021-2022/>`_ Lycée de Bellegarde (Secondes et enseignements scientifique), Établissement de santé pour adolescents à Chanay (ST et 4e)
|
||||||
|
- `2020/2021 </enseignements/2020-2021/>`_ (SNT, Terminale ST, Terminale ST sti2d, Enseignement scientifique en terminale)
|
||||||
|
- `2019/2020 </enseignements/2019-2020/>`_ (SNT, Première ST, Première ST sti2d, TES-L, Tsti2d)
|
||||||
|
|
||||||
|
Un an de TZR dans le Jura à chercher comment m'occuper.
|
||||||
|
|
||||||
|
- `2018/2019 </enseignements/2018-2019/>`_
|
||||||
|
|
||||||
|
Trois ans au collège de Kaweni 1 à Mayotte.
|
||||||
|
|
||||||
|
- `2017/2018 </enseignements/2017-2018/>`_ (3e, 3e passerelles, 6e)
|
||||||
|
- `2016/2017 </enseignements/2016-2017/>`_ (3e, 5e)
|
||||||
|
- `2015/2016 </enseignements/2015-2016/>`_ (3e, 5e, 5e passerelles)
|
||||||
|
|
||||||
|
Mes outils pour faire mes cours
|
||||||
|
===============================
|
||||||
|
|
||||||
|
- `Zoologie des workflows </pages/a-bas-le-bon-vieux-classe-eleve-classe.html>`_
|
||||||
|
|
||||||
|
Méthodes, pratiques et inspirations
|
||||||
|
===================================
|
||||||
|
|
||||||
|
En classe, j’aime me souvenir de cette phrase *Plus le professeur travaille, moins l’élève travaille*. Je crois que l’élève doit, le plus possible, **faire** et pas seulement écouter. Ainsi, l’élève est source de contenu pour faire avancer le cours. Il n’attend pas passivement que la bonne parole du prof lui soit donnée.
|
||||||
|
|
||||||
|
J’ai donc abandonné le cahier de cours au profit d’un **cahier de bord**, où l’avancée et les résultats trouvés par la classe sont inscrits. Les théorèmes ne pouvant plus être donnés, ils sont découverts par la classe et, quand c’est possible, démontrés. En relisant ce cahier, l’élève peut refaire le cheminement qui nous a permis de trouver un résultat. Il a donc à nouveau la possibilité de **comprendre** et plus seulement d’apprendre.
|
||||||
|
|
||||||
|
Pour que cela soit possible, il me faut choisir avec attention chaque activité. Elle a besoin d’être fractale ou riche. C’est à dire dotée de plusieurs niveaux de lecture. Chaque élève doit avoir la possibilité de faire quelque chose. Certains comprendront le sens profond de l’activité tandis que d’autres pourront simplement revoir et solidifier des acquis. L’activité elle-même apporte de la **différenciation**.
|
||||||
|
|
||||||
|
Comme exemple, je donnerais l’activité de découverte de `cosinus <./2017-2018/3e/Geometrie/Triangle_rectangle/>`_ . Elle permet de construire des triangles, de revoir la notion d’angle, de manipuler le rapporteur, de spiraler sur la notion d’agrandissement, de remobiliser les notions de proportionnalité, de définir le cosinus et enfin de ne pas enfermer ce cosinus dans une seule formule.
|
||||||
|
|
||||||
|
Ce genre d’activité, exigeante pour les élèves, peut difficilement se faire sur une heure complète. C’est pourquoi, mes heures de classe sont découpées en 2 ou 3 parties distinctes.
|
||||||
|
|
||||||
|
Une partie peut être dédiée à la découverte d’une notion et la suivante à la consolidation d’une notion dans un autre domaine. Ainsi les élèves ne restent pas toute l’heure en situation d’échec et ont, à chaque cours, l’opportunité de montrer qu’ils savent faire des choses. De plus, cette organisation étale d’avantage dans le temps l’apprentissage d’une notion pour la rendre plus pérenne.
|
||||||
|
|
||||||
|
Certains thèmes sont traités tout au long de l’année à raison d’une heure par semaine. C’est le cas du calcul littéral et de la programmation avec Scratch. Au collège, nous avons désormais accès à la salle informatique une heure par semaine. J’en profite pour travailler régulièrement la programmation avec Scratch au travers de **projets** à réaliser en équipe. La progression sur l’année est spiralée. L’objectif étant d’avoir fait le tour du programme aux vacances de Noël et ensuite, de revoir et d’approfondir les notions. Cette organisation me laisse le temps de tester plusieurs approches et de désamorcer les problèmes.
|
||||||
|
|
||||||
|
L’organisation de la classe a aussi été transformée afin de tirer profit de plus de diversité. Les élèves sont amenés à travailler individuellement, en groupe et en plénière. À chaque mode d’organisation son support. Quand les élèves travaillent individuellement, ils utilisent leur **cahier de recherche**. En groupe, les traces collectives sont inscrites dans le **cahier de groupe**. Et enfin, les plénières aboutissent généralement à une trace écrite dans le **cahier de bord**. Les cahiers de groupe sont ramassés à chaque fin d’heure pour que je puisse préparer la plénière de mise en commun et utiliser les productions d’élèves.
|
||||||
|
|
||||||
|
Ma pratique et mes cours sont très largement inspirés du livre Les maths ensemble et pour chacun. Je m’inspire aussi de ce que font `Christian den Hartigh <https://pedagogieagile.com/>`_ , `Dan Meyer <http://mrmeyer.com/>`_ ou encore `Arnaud Durand et Julien Durand <http://mathix.org/>`_.
|
||||||
|
|
102
content/pages/Workflows.rst
Normal file
102
content/pages/Workflows.rst
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
À bas le bon vieux: Classe -> eleve -> classe
|
||||||
|
#############################################
|
||||||
|
|
||||||
|
:date: 2018-07-07
|
||||||
|
:modified: 2018-07-07
|
||||||
|
:authors: Benjamin Bertrand
|
||||||
|
:category: Autres
|
||||||
|
:summary: Zoologie des workflows rencontrés, expérimentés ou imaginé pour la classe.
|
||||||
|
|
||||||
|
Ici j'essaie de faire une zoologie des façons de travailler que j'ai pu rencontrer, expérimenter ou imaginé. J'espère qu'elle va s'aggrandir avec le temps. J'espérais que cette première énumération m'aiderai à en découvrir d'autre, raté pour le moment..!
|
||||||
|
|
||||||
|
L'ancien
|
||||||
|
========
|
||||||
|
|
||||||
|
Le seul l'unique l'indetrônable, le workflow que l'on a tous subit dans notre scolarité.
|
||||||
|
|
||||||
|
Pleinière -> Seul -> Pleinière
|
||||||
|
|
||||||
|
**Description**: Le prof ecrit le cours (propriété/théorème, exemples), les élèves font les exercices seul dans leur coin (le plus grand silence est imposé) et enfin une correction est donnée au tableau (par les élèves ou le prof).
|
||||||
|
|
||||||
|
**Variantes**: Avant le cours, une activité d'introduction ou des révisions sont proposés aux élèves.
|
||||||
|
|
||||||
|
Le cours discuté
|
||||||
|
================
|
||||||
|
|
||||||
|
On écrit le cours en posant des micros questions aux élèves. On a donc des micro boucles du genre
|
||||||
|
|
||||||
|
Pleinière -> Seul -> Pleinière -> Seul ...
|
||||||
|
|
||||||
|
**Description**: Ce cours se fait à partir d'une alternance entre le prof qui fait avancer le cours et les élèves qui réfléchissent sur des temps plutôt court à des questions. Un veritable dialogue entre classe et l'enseignant s'organise.
|
||||||
|
|
||||||
|
Le classique
|
||||||
|
============
|
||||||
|
|
||||||
|
Celui là est celui qu'on utilise le plus souvent
|
||||||
|
|
||||||
|
Seul -> Groupe -> Pleinière
|
||||||
|
|
||||||
|
**Description**: Une activité est donnée au élèves. Ils commencent à plancher dessus seul (sans interactions avec les autres élèves ou avec l'enseignant). Quand ils semblent tous se l'être appropriée (signal de l'enseignant), un travail de groupe démarre. Il se termine sur une production de groupe. Enfin, l'enseignant séléctionne quelques productions pour en faire un bilan en pleinière où la classe toute ensemble reprend les productions les critiques et les améliore pour laisser finalement une trace "parfaite" (point de vue de la classe) sur le cahier de bord.
|
||||||
|
|
||||||
|
**Variantes**:
|
||||||
|
|
||||||
|
- Avant le travail individuel, il peut être interessant de faire une lecture collective de l'activité pour s'assurer que tout le monde à le vocabulaire ou les notions traitées (à Mayotte c'est la problématique permanante).
|
||||||
|
- Pour sortir du syndrome de la page blanche, on peut fixer le temps de sortie du travail individuel uniquement une fois que tous les élèves ont commencé à écrire quelque chose d'interessant sur leur cahier de recherche.
|
||||||
|
- Le temps de travail de groupe peut être coupé par des rapides retours en pleinières pour que les groupes les plus avancés partagent leurs découvertes (surtout celles non terminées).
|
||||||
|
- Il peut être interessant de changer la composition des groupes surtout vers la fin. Les élèves devront alors expliquer ce qui avait été découvert dans leur ancien groupe et integrer les découvertes des autres groupes.
|
||||||
|
- Dans le même style, on peut designer un temps où des butineurs (un par équipe) va pouvoir aller voir (sans moyen de prendre de notes) les autres groupes.
|
||||||
|
- La pleinière peut aussi être annimée par les élèves eux même qui viendront expliquer leur travail aux autres.
|
||||||
|
|
||||||
|
DMTC
|
||||||
|
====
|
||||||
|
|
||||||
|
J'aime ce workflow quand il s'ajit de faire faire des taches complexes aux élèves.
|
||||||
|
|
||||||
|
Seul -> Groupe -> Seul
|
||||||
|
|
||||||
|
**Description**: L'activité débute comme *le classique*. Les élèves décrouvrent le sujet, font leurs première expérimentations puis commencent le travail de groupe. Par contre au lieu de faire un pleinière de mise en commun, les élèves terminent la rédaction seul à la maison. Le but c'est que les difficultés techniques aient été résoluent en classe et avec l'aide des autres et que le travail de rédaction, de représentation (...) soit fait individuellement.
|
||||||
|
|
||||||
|
**Variantes**:
|
||||||
|
|
||||||
|
- Pour la partie Seul et Groupe, on retrouve les même que pour le *classique*.
|
||||||
|
- Dan Meyer dans ses 3acts proposent que les élèves cherchent eux même la question et les informations dont ils voudraient avoir accès avant le travail individuel. Cette étape peut être faire sous forme d'un petit *cours discuté*.
|
||||||
|
|
||||||
|
La TC prépréparé
|
||||||
|
================
|
||||||
|
|
||||||
|
Le workflow précendent est particulièrement difficile à mettre en place avec les petites classes (6e, 5e). On peut alors plutôt expérimenter celui là
|
||||||
|
|
||||||
|
Pleinière -> Pleinière -> individuel/groupe
|
||||||
|
|
||||||
|
**Description**: L'activité est donnée relativement en avance. À chaque cours on en parle en pleinière et les élèves sont invités la commencer. Ensuite, on faire l'activité en classe, individuellement ou en groupe.
|
||||||
|
|
||||||
|
Je n'ai jamais expérimenté ce workflow mais je sais que Maxime Dupont en était adepte (en espérant ne pas l'avoir trop dénaturé) et arrivait à faire des choses interessantes avec.
|
||||||
|
|
||||||
|
La technique/le bachotage
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Workflow parfait pour les séances de travail technique ou les séances de révisions.
|
||||||
|
|
||||||
|
Seul -> Groupe -> Seul -> Groupe ...
|
||||||
|
|
||||||
|
**Description**: Une série d'exercices techniques est données. Les élèves planchent individuellement dessus. Quand les membres du groupe en ont fait quelques uns, ils mettent leurs résultats en commun et discute des différences constatés pour aboutir à la réponse "parfaite". Si un point de désaccord n'arrive pas à trouver consensus, une correction est sur le bureau qu'ils penvent consulter. Pendant ce genre de scéance, l'enseignant doit être peu solicité, c'est lui qui décide qui il veut voir.
|
||||||
|
|
||||||
|
**Variantes**: Organisation du travail avec un kanban. Les élèves y indiquent ce qu'ils prévoient de travailler et là où ils en sont.
|
||||||
|
|
||||||
|
Pair à pair
|
||||||
|
===========
|
||||||
|
|
||||||
|
Workflow trop peu exploité. Les élèves produisent des documents à destinations des autres élèves.
|
||||||
|
|
||||||
|
Seul -> Groupe -> Seul
|
||||||
|
|
||||||
|
**Description**: Les élèves font un premier exercice seul (ou en suivant n'importe quel workflow). Puis en groupe, ils doivent produire un ou plusieurs exercices du même type avec la correction. Les exercices produits sont redonnés aux autres élèves.
|
||||||
|
|
||||||
|
On peut demander aux groupes de produire plusieurs exercices du même type mais de difficultés différentes.
|
||||||
|
|
||||||
|
**Variantes**:
|
||||||
|
|
||||||
|
- Dire aux élèves que le meileur exercice (pas forcement le plus dure) sera dans la prochaine évaluation.
|
||||||
|
- Echange de travaux de reproduction/codage géométrique.
|
||||||
|
|
||||||
|
|
54
content/pages/home.rst
Normal file
54
content/pages/home.rst
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
Karibu Opytex
|
||||||
|
##############
|
||||||
|
|
||||||
|
:date: 2015-05-16
|
||||||
|
:modified: 2020-08-16
|
||||||
|
:slug: index
|
||||||
|
:authors: Benjamin Bertrand
|
||||||
|
:summary: Page d'accueil
|
||||||
|
:save_as: index.html
|
||||||
|
|
||||||
|
`Mes cours </pages/tout-sur-mes-cours.html>`_
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
Ce site regroupe tout ce que j'ai pu écrire pour mes cours de math au collège et au lycée. Vous y trouverez des notes de cours (souvent incomplètes), des fiches d'exercices, des devoirs et quelques réflexions ou commentaires à leur sujet.
|
||||||
|
|
||||||
|
Les année s'écoulent tranquillement au lycée de Bellegarde
|
||||||
|
|
||||||
|
- `2022/2023 </enseignements/2022-2023/>`_ Lycée de Bellegarde (Secondes, SNT, spécialité NSI, Première ST et enseignements scientifique)
|
||||||
|
- `2021/2022 </enseignements/2021-2022/>`_ Lycée de Bellegarde (Secondes et enseignements scientifique), Établissement de santé pour adolescents à Chanay (ST et 4e)
|
||||||
|
- `2020/2021 </enseignements/2020-2021/>`_ (SNT, Terminale ST, Terminale ST sti2d, Enseignement scientifique en terminale)
|
||||||
|
- `2019/2020 </enseignements/2019-2020/>`_ (SNT, Première ST, Première ST sti2d, TES-L, Tsti2d)
|
||||||
|
|
||||||
|
Un an de TZR dans le Jura à chercher comment m'occuper.
|
||||||
|
|
||||||
|
- `2018/2019 </enseignements/2018-2019/>`_
|
||||||
|
|
||||||
|
Trois ans au collège de Kaweni 1 à Mayotte.
|
||||||
|
|
||||||
|
- `2017/2018 </enseignements/2017-2018/>`_ (3e, 3e passerelles, 6e)
|
||||||
|
- `2016/2017 </enseignements/2016-2017/>`_ (3e, 5e)
|
||||||
|
- `2015/2016 </enseignements/2015-2016/>`_ (3e, 5e, 5e passerelles)
|
||||||
|
|
||||||
|
`Un blog </blog_index.html>`_
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Tentative de faire un blog! On verra si j'arrive prendre le temps rédiger quelques articles où si la nécessité de communiquer avec les élèves devient trop forte!
|
||||||
|
|
||||||
|
`Projets informatiques </pages/projets-informatiques.html>`_
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
Des programmes pour enseigner.
|
||||||
|
|
||||||
|
- `Mapytex <https://git.opytex.org/lafrite/Mapytex>`_: calculer comme un élève
|
||||||
|
- `Bopytex <https://git.opytex.org/lafrite/Bopytex>`_: Intègrer du python dans du latex avant compilation (utilisé pour faire mes devoirs aléatoires)
|
||||||
|
|
||||||
|
De l'administration système.
|
||||||
|
|
||||||
|
- `Gérer mes machines <https://git.opytex.org/lafrite/Ansible_workstation>`_
|
||||||
|
|
||||||
|
Des outils en tout genre pour regagner le temps perdu à les coder!
|
||||||
|
|
||||||
|
- `Ce site <https://git.opytex.org/lafrite/site_opytex>`_
|
||||||
|
- `Photobook <https://git.opytex.org/lafrite/photobook>`_: créer des bouquins de photos
|
||||||
|
- `Pralo <https://git.opytex.org/lafrite/Pralo>`_: faire ses comptes entre amis
|
43
content/pages/projet_info.rst
Normal file
43
content/pages/projet_info.rst
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
Projets informatiques
|
||||||
|
#####################
|
||||||
|
|
||||||
|
:date: 2020-08-16
|
||||||
|
:modified: 2020-08-16
|
||||||
|
:authors: Benjamin Bertrand
|
||||||
|
:summary: Projets informatiques que je mène en parallèle de mon activité de prof.
|
||||||
|
|
||||||
|
À côté de mon activité de prof de mathématiques, je passe une belle partie de mon temps libre à programmer ou administrer des machines.
|
||||||
|
|
||||||
|
La plupart d'entre eux sont hébergés sur mon `serveur Gitea <https://git.opytex.org/lafrite>`_.
|
||||||
|
|
||||||
|
`Mapytex <https://git.opytex.org/lafrite/Mapytex>`_: calculer comme un élève
|
||||||
|
============================================================================
|
||||||
|
|
||||||
|
Librairie Python qui permet de créer aléatoirement des objets mathématiques (calculs, fonctions, fractions...) et de simplifier ces objets avec les explications pour qu'un élève les comprenne.
|
||||||
|
|
||||||
|
`Bopytex <https://git.opytex.org/lafrite/Bopytex>`_: intégrer python dans latex pour créer des DM aléatoires
|
||||||
|
=============================================================================================================
|
||||||
|
|
||||||
|
Librairie et outils python pour intégrer des commandes python dans du latex et de compiler les documents produits.
|
||||||
|
|
||||||
|
Bopytex est un outil python qui transforme un modèle de devoir en sujet individuels avec éventuellement un correction.
|
||||||
|
|
||||||
|
`Photobook <https://git.opytex.org/lafrite/photobook>`_: créer des bouquins de photos
|
||||||
|
=====================================================================================
|
||||||
|
|
||||||
|
Outil pour créer des livres photos prêts à être imprimés.
|
||||||
|
|
||||||
|
`Pralo <https://git.opytex.org/lafrite/Pralo>`_: faire ses comptes entre amis
|
||||||
|
=============================================================================
|
||||||
|
|
||||||
|
Quand on part avec les copains, il y a toujours le moment où il faut faire les comptes. Avec cet outils, chacun écrit ce qu'il a payé pendant le séjour et hop, on sait qui doit quoi à qui!
|
||||||
|
|
||||||
|
`Ce site <https://git.opytex.org/lafrite/site_opytex>`_: site statique avec pelican
|
||||||
|
===================================================================================
|
||||||
|
|
||||||
|
Sources pour créer ce site internet à partir de fichers rst et de Pelican.
|
||||||
|
|
||||||
|
`Gérer mes machines <https://git.opytex.org/lafrite/Ansible_workstation>`_: configuration avec ansible-pull
|
||||||
|
===========================================================================================================
|
||||||
|
|
||||||
|
J'administre essentiellement mes machines avec Ansible et Ansible-pull en particulier.
|
143
hooks/pre-commit
Executable file
143
hooks/pre-commit
Executable file
@ -0,0 +1,143 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim:fenc=utf-8
|
||||||
|
#
|
||||||
|
# Copyright © 2017 lafrite <lafrite@Poivre>
|
||||||
|
#
|
||||||
|
# Distributed under terms of the MIT license.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Git hook to ensure validity of all rst files
|
||||||
|
"""
|
||||||
|
|
||||||
|
from git import Repo
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import restructuredtext_lint
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
formatter = logging.Formatter('%(name)s :: %(levelname)s :: %(message)s')
|
||||||
|
steam_handler = logging.StreamHandler()
|
||||||
|
#steam_handler = logging.FileHandler('logging.conf')
|
||||||
|
steam_handler.setLevel(logging.DEBUG)
|
||||||
|
steam_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# création de l'objet logger qui va nous servir à écrire dans les logs
|
||||||
|
logger = logging.getLogger("precommit")
|
||||||
|
# on met le niveau du logger à DEBUG, comme ça il écrit tout
|
||||||
|
logger.setLevel(logging.WARNING)
|
||||||
|
logger.addHandler(steam_handler)
|
||||||
|
|
||||||
|
# Files selection
|
||||||
|
|
||||||
|
def get_commited_files(repo):
|
||||||
|
hdiff = repo.head.commit.diff()
|
||||||
|
diff = {"A":[], "M":[]}
|
||||||
|
for f in hdiff.iter_change_type("A"):
|
||||||
|
diff["A"].append(f.b_path)
|
||||||
|
for f in hdiff.iter_change_type("M"):
|
||||||
|
diff["M"].append(f.b_path)
|
||||||
|
|
||||||
|
return diff
|
||||||
|
|
||||||
|
def select_by_extension(files, ext="rst"):
|
||||||
|
return [i for i in files if i.split(".")[-1] == ext]
|
||||||
|
|
||||||
|
# Rst linter
|
||||||
|
|
||||||
|
def rst_lint(filename):
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
errors = restructuredtext_lint.lint(f.read())
|
||||||
|
for e in errors:
|
||||||
|
logger.warning(f"{filename} \n{e.full_message}\n")
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
# Rst parameters normalize
|
||||||
|
|
||||||
|
def normalize_file(filename, normalizers = {}):
|
||||||
|
logger.debug(f"Normalizing {filename}")
|
||||||
|
logger.debug(f"With {normalizers}")
|
||||||
|
new_file = ""
|
||||||
|
modified_lines = []
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
for l in f.readlines():
|
||||||
|
new_line = run_normalizers(l, normalizers)
|
||||||
|
if new_line != l:
|
||||||
|
modified_lines.append(f"{l}")
|
||||||
|
logger.warning(f"{filename}\n\t{l}\t{new_line}")
|
||||||
|
new_file += new_line
|
||||||
|
|
||||||
|
with open(filename, "w") as f:
|
||||||
|
f.write(new_file)
|
||||||
|
logger.debug(f"{filename} written")
|
||||||
|
|
||||||
|
return modified_lines
|
||||||
|
|
||||||
|
def run_normalizers(line, normalizers):
|
||||||
|
for c in normalizers:
|
||||||
|
obs = re.search(c, line)
|
||||||
|
if obs:
|
||||||
|
logger.debug(f"Find for {c}")
|
||||||
|
return normalizers[c](line)
|
||||||
|
return line
|
||||||
|
|
||||||
|
# Rst function tools
|
||||||
|
|
||||||
|
def update_date(line):
|
||||||
|
date = time.strftime("%Y-%m-%d")
|
||||||
|
logger.debug(f"Update Date to: {date}")
|
||||||
|
return f":date: {date}\n"
|
||||||
|
|
||||||
|
def update_modified(line):
|
||||||
|
modified = time.strftime("%Y-%m-%d")
|
||||||
|
logger.debug(f"Update modified to: {modified}")
|
||||||
|
return f":modified: {modified}\n"
|
||||||
|
|
||||||
|
def normalize_tags(line):
|
||||||
|
logger.debug(f"Normaizing tags")
|
||||||
|
tags = line.split(":")[-1]
|
||||||
|
tags = [i.strip().capitalize() for i in tags.split(",")]
|
||||||
|
tags_str = ", ".join(tags)
|
||||||
|
return f":tags: {tags_str}\n"
|
||||||
|
|
||||||
|
NORMALIZERS_MODIFIED = {":modified:.*": update_modified,
|
||||||
|
":tags:.*": normalize_tags,
|
||||||
|
}
|
||||||
|
|
||||||
|
NORMALIZERS_NEW = {":date:.*": update_date,
|
||||||
|
":modified:.*": update_modified,
|
||||||
|
":tags:.*": normalize_tags,
|
||||||
|
}
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
r = Repo()
|
||||||
|
diff = get_commited_files(r)
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
modified = []
|
||||||
|
|
||||||
|
# New files
|
||||||
|
for f in select_by_extension(diff["A"], "rst"):
|
||||||
|
errors += rst_lint(f)
|
||||||
|
modified += normalize_file(f, NORMALIZERS_NEW)
|
||||||
|
r.index.add([f])
|
||||||
|
# Modified files
|
||||||
|
for f in select_by_extension(diff["M"], "rst"):
|
||||||
|
errors += rst_lint(f)
|
||||||
|
modified += normalize_file(f, NORMALIZERS_MODIFIED)
|
||||||
|
r.index.add([f])
|
||||||
|
|
||||||
|
if len(errors) > 0:
|
||||||
|
logger.warning("Errors in rst formating, commit aborted")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Reglages pour 'vim'
|
||||||
|
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
|
||||||
|
# cursor: 16 del
|
@ -1 +0,0 @@
|
|||||||
Subproject commit f8394e39df71f697c6879ef88e16f4d6d4bce614
|
|
103
pelicanconf.py
103
pelicanconf.py
@ -3,40 +3,53 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path.append(os.curdir)
|
sys.path.append(os.curdir)
|
||||||
from globalconf import *
|
from globalconf import *
|
||||||
|
|
||||||
AUTHOR = 'Benjamin Bertrand'
|
AUTHOR = "Benjamin Bertrand"
|
||||||
SITENAME = 'OpyTex'
|
SITENAME = "Opytex"
|
||||||
SITETITLE = 'OpyTex'
|
SITETITLE = "Opytex"
|
||||||
SITESUBTITLE = "2019-2020"
|
SITESUBTITLE = ""
|
||||||
SITEURL = ''
|
SITEURL = ""
|
||||||
|
SITEDESCRIPTION = ""
|
||||||
|
|
||||||
CC_LICENSE_COMMERCIAL = True
|
CC_LICENSE_COMMERCIAL = True
|
||||||
CC_LICENSE = True
|
CC_LICENSE = True
|
||||||
|
|
||||||
PATH = './content'
|
PATH = "./content"
|
||||||
|
|
||||||
TIMEZONE = 'Europe/Paris'
|
TIMEZONE = "Europe/Paris"
|
||||||
|
|
||||||
DEFAULT_LANG = 'fr'
|
DEFAULT_LANG = "fr"
|
||||||
|
|
||||||
# theme
|
# theme
|
||||||
THEME = "./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
|
USE_GOOGLE_FONTS = False
|
||||||
|
|
||||||
IGNORE_FILES = ['venv', '.git', 'tools']
|
|
||||||
# Pages, articles and static
|
# Pages, articles and static
|
||||||
PAGE_PATHS = ['pages']
|
PAGE_PATHS = ["pages"]
|
||||||
# ARTICLE_PATHS = ['pages/Enseignement', 'Blog']
|
# ARTICLE_PATHS = ['pages/Enseignement', 'Blog']
|
||||||
ARTICLE_PATHS = ['.']
|
ARTICLE_PATHS = ["blog"]
|
||||||
STATIC_PATHS = ['.']
|
STATIC_PATHS = ["blog", "."]
|
||||||
|
INDEX_SAVE_AS = "blog_index.html"
|
||||||
INDEX_SAVE_AS = 'blog_index.html'
|
|
||||||
|
|
||||||
# Mirror source structure
|
|
||||||
PATH_METADATA = '(?P<path_no_ext>.*)\..*'
|
|
||||||
ARTICLE_URL = ARTICLE_SAVE_AS = PAGE_URL = PAGE_SAVE_AS = '{path_no_ext}.html'
|
|
||||||
|
|
||||||
# Feed generation is usually not desired when developing
|
# Feed generation is usually not desired when developing
|
||||||
FEED_ALL_ATOM = None
|
FEED_ALL_ATOM = None
|
||||||
@ -46,48 +59,20 @@ AUTHOR_FEED_ATOM = None
|
|||||||
AUTHOR_FEED_RSS = None
|
AUTHOR_FEED_RSS = None
|
||||||
|
|
||||||
# Blogroll
|
# Blogroll
|
||||||
LINKS = (
|
LINKS = [
|
||||||
#('2019/2020', "/Enseignements/2019-2020/"),
|
("2015-2016", "/enseignements/2015-2016/"),
|
||||||
#('2018/2019', "/Enseignements/2018-2019/"),
|
("2016-2017", "/enseignements/2016-2017/"),
|
||||||
#('2017/2018', "/Enseignements/2017-2018/"),
|
("2017-2018", "/enseignements/2017-2018/"),
|
||||||
#('2016/2017', "/Enseignements/2016-2017/"),
|
("2018-2019", "/enseignements/2018-2019/"),
|
||||||
#('2015/2016', "/Enseignements/2015-2016/"),
|
("2019-2020", "/enseignements/2019-2020/"),
|
||||||
)
|
("2020-2021", "/enseignements/2020-2021/"),
|
||||||
|
("2021-2022", "/enseignements/2021-2022/"),
|
||||||
|
("2022-2023", "/enseignements/2022-2023/"),
|
||||||
|
]
|
||||||
# Social widget
|
# Social widget
|
||||||
#SOCIAL = (('You can add links in your config file', '#'),
|
SOCIAL = ()
|
||||||
# )
|
|
||||||
|
|
||||||
DEFAULT_PAGINATION = 20
|
DEFAULT_PAGINATION = 10
|
||||||
|
|
||||||
# Date
|
|
||||||
SHOW_DATE_MODIFIED = True
|
|
||||||
ARTICLE_ORDER_BY = "modified"
|
|
||||||
|
|
||||||
DISPLAY_ARTICLE_INFO_ON_INDEX = True
|
|
||||||
|
|
||||||
# Uncomment following line if you want document-relative URLs when developing
|
# Uncomment following line if you want document-relative URLs when developing
|
||||||
RELATIVE_URLS = True
|
# RELATIVE_URLS = True
|
||||||
|
|
||||||
BOOTSTRAP_THEME = "flatly"
|
|
||||||
PLUGIN_PATHS = ['./plugins', './pelican-plugins']
|
|
||||||
|
|
||||||
PLUGINS = [#'hierarchy',
|
|
||||||
'tag_cloud',
|
|
||||||
"list_files",
|
|
||||||
"render_math",
|
|
||||||
"always_modified",
|
|
||||||
"pdf-img",
|
|
||||||
]
|
|
||||||
|
|
||||||
READERS = {"html": None}
|
|
||||||
|
|
||||||
# hierarchy plugin config
|
|
||||||
# ARTICLE_URL = 'Enseignements/{slug}/'
|
|
||||||
# ARTICLE_SAVE_AS = 'Enseignements/{slug}/index.html'
|
|
||||||
# #SLUGIFY_SOURCE = 'basename'
|
|
||||||
# ARTICLE_NAVIGATION = True
|
|
||||||
|
|
||||||
TAGS_URL = "tags.html"
|
|
||||||
DISPLAY_TAGS_INLINE = True
|
|
||||||
DISPLAY_CATEGORIES_ON_SIDEBAR = True
|
|
||||||
|
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))
|
@ -11,7 +11,7 @@ sys.path.append(os.curdir)
|
|||||||
from pelicanconf import *
|
from pelicanconf import *
|
||||||
|
|
||||||
# If your site is available via HTTPS, make sure SITEURL begins with https://
|
# If your site is available via HTTPS, make sure SITEURL begins with https://
|
||||||
SITEURL = 'https://raw.opytex.org/enseignements/2019-2020/'
|
SITEURL = ''
|
||||||
RELATIVE_URLS = False
|
RELATIVE_URLS = False
|
||||||
|
|
||||||
FEED_ALL_ATOM = 'feeds/all.atom.xml'
|
FEED_ALL_ATOM = 'feeds/all.atom.xml'
|
||||||
|
6
tasks.py
6
tasks.py
@ -21,12 +21,12 @@ CONFIG = {
|
|||||||
'settings_base': SETTINGS_FILE_BASE,
|
'settings_base': SETTINGS_FILE_BASE,
|
||||||
'settings_publish': 'publishconf.py',
|
'settings_publish': 'publishconf.py',
|
||||||
|
|
||||||
'year_subfolder': 'enseignements/2019-2020/',
|
'year_subfolder': '',
|
||||||
|
|
||||||
# Output path. Can be absolute or relative to tasks.py. Default: 'output'
|
# Output path. Can be absolute or relative to tasks.py. Default: 'output'
|
||||||
'deploy_path': SETTINGS['OUTPUT_PATH'],
|
'deploy_path': SETTINGS['OUTPUT_PATH'],
|
||||||
|
|
||||||
'fake_path': '../../output/',
|
'fake_path': '../output/',
|
||||||
|
|
||||||
# Remote server configuration
|
# Remote server configuration
|
||||||
'ssh_user': 'sshcontent',
|
'ssh_user': 'sshcontent',
|
||||||
@ -35,7 +35,7 @@ CONFIG = {
|
|||||||
'ssh_path': '/home/sshcontent/opytex.org/www/',
|
'ssh_path': '/home/sshcontent/opytex.org/www/',
|
||||||
|
|
||||||
# Rsync config
|
# Rsync config
|
||||||
'rsync_exclude': '',
|
'rsync_exclude': '--exclude "pymath" --exclude "opytex" --exclude "enseignements"',
|
||||||
|
|
||||||
# Port for `serve`
|
# Port for `serve`
|
||||||
'port': 8000,
|
'port': 8000,
|
||||||
|
@ -16,11 +16,13 @@ h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1{
|
h1{
|
||||||
font-size: 2.4em
|
font-size: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 2em
|
font-size: 2em;
|
||||||
|
background-color: @light-grey;
|
||||||
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
@ -76,6 +78,15 @@ aside {
|
|||||||
color: @sidebar-text-color;
|
color: @sidebar-text-color;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
background-color: @sidebar-bg;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: @sidebar-link-color;
|
color: @sidebar-link-color;
|
||||||
}
|
}
|
||||||
@ -94,7 +105,11 @@ aside {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
margin: 15px 0 5px 0;
|
margin: 15px 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 25px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@ -104,57 +119,16 @@ aside {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
ul {
|
ul.list {
|
||||||
|
padding: 0;
|
||||||
li {
|
li {
|
||||||
display: inline;
|
display: block;
|
||||||
line-height: 1.6em;
|
line-height: 1.6em;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
text-transform: lowercase;
|
text-transform: lowercase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagcloud {
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
background-color: @sidebar-bg;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.tag-0 {
|
|
||||||
font-size: 170%;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.tag-1 {
|
|
||||||
font-size: 150%;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.tag-2 {
|
|
||||||
font-size: 120%;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.tag-3 {
|
|
||||||
font-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.tag-4 {
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -424,92 +398,6 @@ ul.social {
|
|||||||
line-height: 36px;
|
line-height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.sc-facebook {
|
|
||||||
background-color: @facebook-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-google {
|
|
||||||
background-color: @google-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-instagram {
|
|
||||||
background-color: @instagram-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-pinterest {
|
|
||||||
background-color: @pinterest-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-linkedin {
|
|
||||||
background-color: @linkedin-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-medium {
|
|
||||||
background-color: @medium-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-rss {
|
|
||||||
background-color: @rss-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-stack-overflow {
|
|
||||||
background-color: @stack-overflow-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-tumblr {
|
|
||||||
background-color: @tumblr-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-twitter {
|
|
||||||
background-color: @twitter-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-youtube {
|
|
||||||
background-color: @youtube-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-github, a.sc-github-alt {
|
|
||||||
background-color: @github-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-envelope-o {
|
|
||||||
background-color: @email-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-reddit {
|
|
||||||
background-color: @reddit-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-soundcloud {
|
|
||||||
background-color: @soundcloud-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-gitlab {
|
|
||||||
background-color: @gitlab-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-xing {
|
|
||||||
background-color: @xing-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sc-bitbucket {
|
|
||||||
background-color: @bitbucket-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.ads-aside {
|
|
||||||
display: inline-block;
|
|
||||||
height: 90px;
|
|
||||||
width: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ads-responsive {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 6px 0 6px 0;
|
|
||||||
width: 90%;
|
|
||||||
height: 70px;
|
|
||||||
margin-left: 5%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -518,7 +406,8 @@ ul.social {
|
|||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
aside {
|
aside {
|
||||||
width: 25%;
|
width: 20%;
|
||||||
|
max-width: 250px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -548,7 +437,7 @@ ul.social {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 25%;
|
right: 2.5%;
|
||||||
|
|
||||||
article {
|
article {
|
||||||
|
|
||||||
@ -564,11 +453,4 @@ ul.social {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ads-aside {
|
|
||||||
height: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ads-responsive {
|
|
||||||
height: 90px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
2
theme/static/stylesheet/style.min.css
vendored
2
theme/static/stylesheet/style.min.css
vendored
File diff suppressed because one or more lines are too long
@ -23,7 +23,7 @@
|
|||||||
@sidebar-bg: @grey;
|
@sidebar-bg: @grey;
|
||||||
@sidebar-text-color: @white;
|
@sidebar-text-color: @white;
|
||||||
@sidebar-link-color: @white;
|
@sidebar-link-color: @white;
|
||||||
@sidebar-link-hover-color: @light-orange;
|
@sidebar-link-hover-color: @light-grey;
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
@btn-bg: @orange;
|
@btn-bg: @orange;
|
||||||
|
@ -17,10 +17,6 @@
|
|||||||
<meta name="robots" content="{{ ROBOTS }}" />
|
<meta name="robots" content="{{ ROBOTS }}" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if USE_GOOGLE_FONTS != False %}
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro|Source+Sans+Pro:300,400,400i,700" rel="stylesheet">
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if USE_LESS %}
|
{% if USE_LESS %}
|
||||||
<link rel="stylesheet/less" type="text/css" href="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/stylesheet/style.less">
|
<link rel="stylesheet/less" type="text/css" href="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/stylesheet/style.less">
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/less.js/2.5.1/less.min.js" type="text/javascript"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/less.js/2.5.1/less.min.js" type="text/javascript"></script>
|
||||||
@ -68,11 +64,11 @@
|
|||||||
|
|
||||||
<title>{{ SITENAME }}{% block title %}{% endblock %}</title>
|
<title>{{ SITENAME }}{% block title %}{% endblock %}</title>
|
||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<aside>
|
<aside>
|
||||||
<div>
|
<div>
|
||||||
<!--
|
|
||||||
<a href="{{ SITEURL }}/">
|
<a href="{{ SITEURL }}/">
|
||||||
{% if SITELOGO %}
|
{% if SITELOGO %}
|
||||||
<img src="{{ SITELOGO }}" alt="{{ SITETITLE }}" title="{{ SITETITLE }}">
|
<img src="{{ SITELOGO }}" alt="{{ SITETITLE }}" title="{{ SITETITLE }}">
|
||||||
@ -80,12 +76,11 @@
|
|||||||
<img src="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/img/profile.png" alt="{{ SITETITLE }}" title="{{ SITETITLE }}">
|
<img src="{{ SITEURL }}/{{ THEME_STATIC_DIR }}/img/profile.png" alt="{{ SITETITLE }}" title="{{ SITETITLE }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
-->
|
<h1><a href="{{ SITEURL }}/">{{ SITETITLE }}</a></h1>
|
||||||
<h1><a href="/">{{ SITETITLE }}</a></h1>
|
|
||||||
|
|
||||||
{% if SITESUBTITLE %}<p><a href="{{ SITEURL }}/">{{ SITESUBTITLE }}</a></p>{% endif %}
|
{% if SITESUBTITLE %}<p>{{ SITESUBTITLE }}</p>{% endif %}
|
||||||
|
|
||||||
<!--
|
<h2><a href="/blog_index.html">Blog</a></h2>
|
||||||
<h2>Années</h2>
|
<h2>Années</h2>
|
||||||
<nav>
|
<nav>
|
||||||
<ul class="list">
|
<ul class="list">
|
||||||
@ -94,34 +89,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
-->
|
|
||||||
<h2>Niveaux</h2>
|
|
||||||
<nav>
|
|
||||||
<ul class="list">
|
|
||||||
{% for category, articles in categories|sort %}
|
|
||||||
<li><a href="{{ SITEURL }}/{{ category.url }}">{{ category }} ({{ articles|count }})</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
</nav>
|
|
||||||
<h2><a href="{{ SITEURL }}/tags.html">
|
|
||||||
Thèmes
|
|
||||||
</a></h2>
|
|
||||||
<nav>
|
|
||||||
|
|
||||||
<ul class="tagcloud">
|
|
||||||
{% for tag in tag_cloud %}
|
|
||||||
<li class="tag-{{ tag.1 }}">
|
|
||||||
<a href="{{ SITEURL }}/{{ tag.0.url }}">
|
|
||||||
{{ tag.0 }}
|
|
||||||
{% if TAG_CLOUD_BADGE %}
|
|
||||||
<span class="badge">{{ tag.2 }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
@ -173,6 +140,7 @@
|
|||||||
{% if GITHUB_CORNER_URL %}
|
{% if GITHUB_CORNER_URL %}
|
||||||
{% include 'partial/github.html' %}
|
{% include 'partial/github.html' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<script type="text/javascript" async defer data-website-id="2cb9cd4c-66da-4e4f-9e84-a2d53c7b07bc" src="https://stat.opytex.org/umami.js"></script>
|
<script type="text/javascript" async defer data-website-id="2cb9cd4c-66da-4e4f-9e84-a2d53c7b07bc" src="https://stat.opytex.org/umami.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user