first commit

This commit is contained in:
julien
2026-08-05 13:53:18 +02:00
commit 91772b907c
70 changed files with 3762 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
# Seuls le Dockerfile, le validateur et les sources Zola
# sont nécessaires à la construction.
*
!Dockerfile
!scripts/
!scripts/check-content.py
!scripts/_schema.py
!src/
!src/**
# Ne jamais envoyer le site déjà généré
src/public/
+6
View File
@@ -0,0 +1,6 @@
# Site généré par Zola
src/public/
# Caches Python
__pycache__/
*.py[cod]
+17
View File
@@ -0,0 +1,17 @@
FROM python:3.13-alpine AS validator
COPY ./scripts/check-content.py ./scripts/_schema.py /project/scripts/
COPY ./src /project/src
WORKDIR /project
RUN ["python3", "scripts/check-content.py"]
FROM ghcr.io/getzola/zola:v0.22.1 AS zola
COPY --from=validator /project/src /project
WORKDIR /project
RUN ["zola", "build"]
FROM ghcr.io/static-web-server/static-web-server:2
ENV SERVER_ROOT=/public
COPY --from=zola /project/public /public
+424
View File
@@ -0,0 +1,424 @@
# Guide de rédaction
Ce guide décrit la manière dajouter et de maintenir les contenus du site **Les Carnets dAkkadien**.
Le principe général est le suivant :
- le front matter TOML contient les données brèves et structurées ;
- le corps Markdown contient les explications, tableaux, exemples et commentaires ;
- les champs prévus par un modèle restent présents, même lorsquils sont vides ;
- les champs vides ne sont pas affichés sur le site.
## Organisation des contenus
| Type | Répertoire | Taxonomie |
|---|---|---|
| Article | `src/content/articles/` | aucune |
| Grammaire | `src/content/grammaire/` | `themes` |
| Signe | `src/content/signes/` | `lectures` |
| Texte | `src/content/textes/` | `genres` |
| Vocabulaire | `src/content/vocabulaire/` | `natures` |
La bibliographie et les mentions légales sont des pages autonomes placées directement dans `src/content/`.
Chaque section contient un fichier `_index.md` qui définit son titre, sa description, son tri et ses templates. Ces fichiers sont maintenus manuellement ; le générateur crée uniquement des pages de contenu.
## Conventions générales
### Période et terminologie
Le site est principalement consacré au paléo-babylonien. Une forme, une graphie ou une analyse relevant dune autre période ou dun autre dialecte est signalée dans la fiche concernée.
Les champs structurés propres aux fiches utilisent des clés anglaises. Les noms des taxonomies sont en français ; leurs termes suivent la donnée affichée, en français pour les catégories et en translittération pour les lectures.
### Unicode et translittération
Tous les fichiers de contenu sont enregistrés en UTF-8 et normalisés en Unicode NFC, y compris leurs noms.
Les lemmes, lectures et formes akkadiennes conservent leurs diacritiques :
```text
bītum
parāsum
ṭim
```
Dans le corps Markdown, les formes linguistiques sont généralement écrites en *italique*. Le code en ligne est réservé aux commandes, chemins et noms de champs.
### Noms de fichiers
Pour les articles, la grammaire, les textes et le vocabulaire, lidentifiant fourni au générateur devient le nom du fichier. Il doit être écrit sous sa forme finale :
- en minuscules ;
- sans espace ni underscore ;
- avec seulement des lettres Unicode, des chiffres et des tirets ;
- sans double tiret ni tiret final ;
- sans extension `.md`.
Exemples :
```text
src/content/vocabulaire/bītum.md
src/content/vocabulaire/parāsum.md
src/content/grammaire/forme-liee.md
src/content/articles/les-carnets-sont-en-ligne.md
```
Un tiret initial est accepté uniquement pour le vocabulaire, par exemple `-ma.md`.
Les signes utilisent toujours un numéro MZL sur trois chiffres :
```text
src/content/signes/mzl-001.md
src/content/signes/mzl-113.md
src/content/signes/mzl-167.md
```
### Front matter commun
Les pages générées partagent quatre champs communs :
```toml
title = "Titre"
date = 2026-08-05T12:30:00+02:00
description = ""
draft = true
```
Les tables propres à chaque type de contenu viennent ensuite dans le même front matter.
- `title` est le titre affiché ;
- `date` est créée dans le fuseau `Europe/Paris` et doit refléter la date de publication ;
- `description` est un résumé court, rédigé manuellement lorsquil est utile ;
- `draft` détermine si la page est publiée.
`date` est une valeur temporelle TOML écrite sans guillemets. Le champ facultatif `updated` peut être ajouté après une modification substantielle :
```toml
updated = 2026-08-05
```
Lorsque `description` reste vide :
- une fiche de vocabulaire affiche une phrase construite à partir de sa nature, de son genre et de son sens ;
- une fiche de signe affiche une phrase construite à partir de son titre et de son numéro MZL ;
- un article, une note de grammaire ou un texte naffiche pas de résumé de remplacement.
La métadescription HTML utilise alors, en dernier recours, la description générale du site.
### Brouillons et publication
Un brouillon peut rester incomplet et être affiché localement avec :
```sh
zola --root src serve --drafts
```
Pour publier une fiche :
1. compléter les données disponibles sans supprimer les champs de son modèle ;
2. rédiger le corps Markdown ;
3. vérifier la date ;
4. passer `draft` à `false` ;
5. lancer les contrôles.
```sh
./scripts/check-content.py
zola --root src check --drafts --skip-external-links
```
## Générateur de fiches
La commande générale est :
```sh
./scripts/new-content.py SECTION IDENTIFIANT [--nature NATURE] [--title TITRE]
```
Exemples :
```sh
./scripts/new-content.py vocabulaire šarrum --nature nom
./scripts/new-content.py vocabulaire epēšum --nature verbe
./scripts/new-content.py vocabulaire --nature particule -- -ma
./scripts/new-content.py signes 113 --title BE
./scripts/new-content.py grammaire suffixes-pronominaux
./scripts/new-content.py textes lettre-au-marchand
./scripts/new-content.py articles nouvel-article
```
Pour le vocabulaire, les natures reconnues sont :
```text
nom, adjectif, verbe, pronom, préposition,
adverbe, conjonction, particule, numéral
```
Lorsque `--nature` est omise dans un terminal interactif, le générateur propose un menu. Elle est obligatoire dans un contexte non interactif.
Le générateur normalise lidentifiant et le titre en NFC, prépare tous les champs nécessaires et refuse tout écrasement. Il ne renseigne pas les données linguistiques et ne rédige pas le corps Markdown.
Pour un article, un texte ou une note de grammaire, le titre est déduit de lidentifiant en remplaçant les tirets par des espaces et en mettant linitiale en majuscule. Pour le vocabulaire, il reprend lidentifiant. Pour un signe, il reste vide sauf si `--title` est fourni.
## Taxonomies
Les taxonomies publiques sont :
- `natures` pour la nature grammaticale du vocabulaire ;
- `lectures` pour les lectures des signes ;
- `themes` pour les notions grammaticales ;
- `genres` pour les types documentaires des textes.
Les termes sont écrits sous leur forme destinée à laffichage :
```toml
natures = ["préposition"]
lectures = ["tim", "ṭim"]
themes = ["morphologie nominale"]
genres = ["lettre"]
```
La graphie doit rester stable. Les diacritiques sont conservés dans les URL : `tim` et `ṭim` correspondent donc à deux lectures distinctes.
Une fiche de vocabulaire contient exactement une nature. Les autres taxonomies peuvent contenir plusieurs termes lorsque cela est pertinent.
## Fiches de vocabulaire
Toutes les fiches lexicales utilisent la même structure générale :
```toml
+++
title = "lemme"
date = 2026-08-05T12:30:00+02:00
description = ""
draft = true
[extra]
meaning = ""
logograms = []
[taxonomies]
natures = ["nature"]
+++
```
`meaning` contient le sens principal ou une courte série de sens. Il doit être renseigné avant publication.
`logograms` contient les écritures logographiques associées au lemme. Il reste toujours un tableau de chaînes :
```toml
logograms = ["LUGAL"]
```
Chaque nature ajoute les champs suivants, dans lordre produit par le générateur :
| Nature | Champs supplémentaires |
|---|---|
| Nom | `gender`, `bound`, `plural` |
| Adjectif | `bound`, `feminine`, `masculine_plural`, `feminine_plural`, `predicative` |
| Verbe | `root`, `stem`, `verb_class`, `vowel_class`, `preterite`, `durative`, `perfect`, `imperative`, `participle`, `verbal_adjective` |
| Pronom | `pronoun_type`, `person`, `gender`, `number` |
| Préposition | `governs` |
| Adverbe | `function` |
| Conjonction | `function` |
| Particule | `function` |
| Numéral | `numeral_type`, `value`, `gender`, `feminine` |
Tous les champs simples restent des chaînes, même lorsquils sont vides. Une fiche publiée doit contenir exactement les champs de sa nature : un champ manquant, supplémentaire ou mal typé est rejeté par le validateur.
Pour un verbe, `stem` est obligatoire avant publication. Le titre fournit déjà linfinitif et ne le répète donc pas dans `[extra]`.
### Sens des principaux champs
- `gender` : genre grammatical ;
- `bound` : forme liée ;
- `plural` : pluriel lexical ;
- `predicative` : forme prédicative de référence, affichée comme « Prédicatif (3 m. s.) » ;
- `root` : racine, par exemple `p-r-s` ;
- `stem` : thème verbal, par exemple `G`, `D`, `Š` ou `N` ;
- `verb_class` : classe morphologique, par exemple `fort`, `I-n` ou `III-faible` ;
- `vowel_class` : classe vocalique, par exemple `a/u` ;
- `pronoun_type` : type de pronom ;
- `governs` : cas régi par une préposition ;
- `function` : fonction dun adverbe, dune conjonction ou dune particule ;
- `numeral_type` : cardinal, ordinal ou autre type de numéral ;
- `value` : valeur numérique.
Le corps Markdown reste libre. Les rubriques suivantes peuvent servir de repères lorsquelles sont utiles :
```markdown
## Emploi
## Morphologie
## Déclinaison
## Thèmes dérivés
## Occurrences
## Voir aussi
## Sources
```
Les paradigmes, exceptions, constructions et commentaires détaillés appartiennent au corps plutôt quau front matter. Les rubriques vides ne sont pas ajoutées.
## Fiches de signes
La commande suivante :
```sh
./scripts/new-content.py signes 113 --title BE
```
produit :
```toml
+++
title = "BE"
date = 2026-08-05T12:30:00+02:00
description = ""
draft = true
[extra]
sign = ""
mzl = "113"
[taxonomies]
lectures = []
+++
```
- le fichier est nommé `mzl-113.md` ;
- `title` contient le nom usuel du signe ;
- `sign` contient le caractère cunéiforme Unicode ;
- `mzl` reprend exactement les trois chiffres du nom de fichier ;
- `lectures` contient les lectures translittérées.
Le nom `mzl-XXX.md` est contrôlé même lorsque la fiche est encore en brouillon. Avant publication, le titre, le glyphe et le numéro MZL doivent être renseignés, et le numéro doit correspondre au nom du fichier.
Le corps peut accueillir, selon le besoin :
```markdown
## Valeurs syllabiques
## Emplois logographiques
## Emploi comme déterminatif
## Variantes graphiques
## Notes
```
Une fiche de signe peut rester sans corps si son front matter suffit.
## Notes de grammaire
```toml
+++
title = "Titre de la fiche"
date = 2026-08-05T12:30:00+02:00
description = ""
draft = true
[taxonomies]
themes = []
+++
```
`themes` regroupe les fiches par notion grammaticale. Le corps est organisé librement selon le sujet.
## Textes étudiés
```toml
+++
title = "Titre du texte"
date = 2026-08-05T12:30:00+02:00
description = ""
draft = true
[taxonomies]
genres = []
+++
```
`genres` décrit le type documentaire, par exemple `lettre` ou `inscription commémorative`.
Une structure fréquente est :
```markdown
## Présentation
## Translittération
## Normalisation
## Traduction
## Commentaire philologique
## Vocabulaire
```
Elle peut être adaptée à chaque texte.
## Articles
```toml
+++
title = "Titre de larticle"
date = 2026-08-05T12:30:00+02:00
description = ""
draft = true
[extra]
banner = ""
banner_alt = ""
banner_credit = ""
+++
```
- `description` est affichée dans la liste des articles, sur la page daccueil et dans la page elle-même ;
- `banner` indique un chemin relatif à `src/static/`, par exemple `images/articles/article.webp` ;
- `banner_alt` contient le texte alternatif de limage ;
- `banner_credit` contient une attribution ou une légende facultative.
Lorsque `banner` reste vide, aucune image ni aucun espace réservé ne sont générés. Lorsquune image est ajoutée, son texte alternatif doit décrire utilement son contenu ; une image purement décorative peut utiliser une chaîne vide.
## Liens internes
Les liens entre contenus utilisent les chemins internes de Zola :
```markdown
[Voir bītum](@/vocabulaire/bītum.md)
[Voir la forme liée](@/grammaire/forme-liee.md)
```
Ils sont ajoutés lorsquils apportent un contexte réel : occurrence dans un texte, exemple grammatical, signe observé ou relation lexicale utile.
## Ce que contrôle le validateur
Le validateur vérifie pour tous les fichiers Markdown :
- lencodage UTF-8 ;
- la normalisation Unicode NFC du contenu et du chemin ;
- la présence et la validité du front matter TOML ;
- lappartenance à une section connue lorsque le fichier est placé dans un sous-répertoire de `src/content/`.
Pour les fichiers `_index.md`, il exige un titre non vide.
Pour les autres pages, `draft` doit être un booléen. Les brouillons ne sont pas soumis aux contrôles de complétude, à lexception du nom des fiches de signes.
Pour toute page publiée, il exige un titre non vide. Une date TOML valide est également obligatoire lorsque la page appartient à une section. Les pages permanentes placées directement dans `src/content/`, comme la bibliographie et les mentions légales, nont pas besoin de date.
Pour une fiche de vocabulaire publiée, il contrôle en plus :
- une seule nature grammaticale connue ;
- la présence exacte des champs prévus pour cette nature ;
- des chaînes pour les champs simples ;
- un tableau de chaînes non vides pour `logograms` ;
- un `meaning` non vide ;
- un `stem` non vide pour les verbes.
Pour une fiche de signe publiée, il contrôle le glyphe, le numéro MZL sur trois chiffres et sa correspondance avec le nom du fichier.
Zola reste responsable de la construction, des liens internes, des taxonomies et du rendu des templates.
## Principes de cohérence
- conserver tous les champs prévus par le modèle, même vides ;
- ne renseigner que des informations établies ou utiles ;
- garder une graphie stable pour chaque lemme, lecture et terme de taxonomie ;
- employer `draft = true` pour un contenu incomplet ou temporairement hors ligne ;
- najouter `updated` quaprès une modification substantielle ;
- éviter de répéter dans le corps une donnée déjà claire dans le front matter, sauf lorsquune explication apporte quelque chose ;
- rédiger des descriptions courtes et informatives lorsquun résumé spécifique est utile ;
- ne pas ajouter de rubriques vides ni de textes dattente.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Les Carnets dAkkadien
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+28
View File
@@ -0,0 +1,28 @@
Les Carnets dAkkadien — Licence des contenus
Sauf mention contraire, les contenus originaux publiés dans le répertoire
src/content/ et sur le site sont mis à disposition sous le nom
Les Carnets dAkkadien selon les termes de la licence :
Creative Commons Attribution Partage dans les mêmes conditions 4.0
International (CC BY-SA 4.0)
Résumé de la licence :
https://creativecommons.org/licenses/by-sa/4.0/deed.fr
Code juridique :
https://creativecommons.org/licenses/by-sa/4.0/legalcode
Attribution recommandée :
« Les Carnets dAkkadien — CC BY-SA 4.0 »
Cette licence ne couvre pas :
- le code du site, distribué séparément sous licence MIT dans LICENSE-CODE ;
- les polices, images, citations, reproductions et autres ressources provenant
de tiers, qui restent soumises à leurs propres droits et licences ;
- les éléments explicitement accompagnés dune autre mention de licence.
La licence CC BY-SA 4.0 autorise le partage et ladaptation du contenu, y compris
à des fins commerciales, sous réserve de créditer Les Carnets dAkkadien, de signaler les
modifications et de diffuser les adaptations sous la même licence.
+217
View File
@@ -0,0 +1,217 @@
# Les Carnets dAkkadien
**Les Carnets dAkkadien** est un site personnel consacré à lapprentissage de lakkadien et de l’écriture cunéiforme. Il réunit des fiches de vocabulaire, des signes, des notes de grammaire, des textes étudiés et des articles de parcours.
Site public : <https://lescarnetsdakkadien.netig.net>
Le site est construit avec [Zola](https://www.getzola.org/) à partir de fichiers Markdown. Il ne nécessite ni base de données ni interface dadministration : les contenus restent lisibles et modifiables comme de simples fichiers texte.
## Contenu du site
| Section | Contenu | Classement complémentaire |
|---|---|---|
| `articles` | articles sur lapprentissage et le projet | — |
| `grammaire` | notes consacrées aux notions grammaticales | thèmes |
| `signes` | signes cunéiformes classés par numéro MZL | lectures |
| `textes` | translittérations, traductions et commentaires | genres |
| `vocabulaire` | fiches lexicales adaptées à leur nature grammaticale | natures |
La page daccueil présente le dernier article et les trois fiches datées les plus récentes parmi les signes, le vocabulaire, la grammaire et les textes. Des flux Atom et RSS sont également générés.
Les conventions de rédaction et les champs disponibles sont décrits dans le [guide de rédaction](GUIDE_REDACTION.md).
## Prérequis
Pour travailler directement sur le projet :
- Python 3.11 ou ultérieur ;
- Zola 0.22.1 ;
- Git, si le dépôt est versionné avec Git.
Pour le déploiement conteneurisé :
- Docker Engine ;
- Docker Compose.
Toutes les commandes ci-dessous sont exécutées depuis la racine du dépôt.
## Développement local
Valider les contenus :
```sh
./scripts/check-content.py
```
Vérifier la construction Zola, brouillons compris :
```sh
zola --root src check --drafts --skip-external-links
```
`--skip-external-links` évite de dépendre du réseau. Retirer cette option pour contrôler aussi les liens externes.
Lancer le serveur de développement :
```sh
zola --root src serve
```
Afficher également les brouillons :
```sh
zola --root src serve --drafts
```
Construire le site statique :
```sh
zola --root src build
```
Le résultat est écrit dans `src/public/`, répertoire ignoré par Git.
## Créer un contenu
Le générateur crée un brouillon horodaté avec le front matter adapté à la section :
```sh
./scripts/new-content.py vocabulaire šarrum --nature nom
```
Exemples :
```sh
./scripts/new-content.py signes 113 --title BE
./scripts/new-content.py grammaire suffixes-pronominaux
./scripts/new-content.py textes lettre-au-marchand
./scripts/new-content.py articles nouvel-article
./scripts/new-content.py vocabulaire --nature particule -- -ma
```
Chaque fichier est créé avec :
- une date au format TOML dans le fuseau `Europe/Paris` ;
- `draft = true` ;
- tous les champs prévus par son modèle ;
- des chaînes vides ou des listes vides pour les données à compléter.
Le générateur refuse d’écraser un fichier existant. Son aide complète est disponible avec :
```sh
./scripts/new-content.py --help
```
## Publier un contenu
Le cycle de rédaction recommandé est simple :
1. créer le brouillon avec `new-content.py` ;
2. compléter le front matter et le corps Markdown ;
3. vérifier que `date` correspond à la date de publication souhaitée ;
4. remplacer `draft = true` par `draft = false` ;
5. lancer le validateur et Zola.
```sh
./scripts/check-content.py
zola --root src check --drafts --skip-external-links
```
Le validateur contrôle les invariants qui doivent rester vrais indépendamment du rendu :
- encodage UTF-8 et normalisation Unicode NFC ;
- front matter TOML valide ;
- section connue et statut de brouillon valide ;
- titre des index et des pages publiées ;
- date des pages publiées dans une section ;
- nom et numéro MZL des fiches de signes ;
- nature, champs et types des fiches de vocabulaire publiées.
Les brouillons peuvent rester incomplets. Le nom `mzl-XXX.md` dune fiche de signe reste toutefois contrôlé même en brouillon, car il détermine son identité et son URL future.
Une erreur renvoie un code de sortie non nul et bloque également la construction Docker.
## Organisation du dépôt
```text
.
├── Dockerfile
├── compose.yml
├── README.md
├── GUIDE_REDACTION.md
├── LICENSE-CODE
├── LICENSE-CONTENT
├── scripts
│ ├── _schema.py
│ ├── check-content.py
│ └── new-content.py
└── src
├── content
├── sass
├── static
├── templates
└── zola.toml
```
- `src/content/` contient les pages Markdown ;
- `src/templates/` contient les templates Tera ;
- `src/sass/main.scss` contient les styles ;
- `src/static/` contient les ressources servies telles quelles ;
- `src/zola.toml` contient la configuration de Zola ;
- `scripts/new-content.py` crée les brouillons ;
- `scripts/check-content.py` valide les contenus ;
- `scripts/_schema.py` contient les définitions communes aux deux scripts.
Les chemins et les taxonomies utilisent la stratégie de slugification `safe` de Zola. Les diacritiques sont donc conservés : des lectures comme `tim` et `ṭim` restent distinctes.
## Déploiement avec Docker Compose
Limage est construite en trois étapes : validation des contenus, construction avec Zola 0.22.1, puis service du site statique.
Vérifier la configuration :
```sh
docker compose config
```
Construire et lancer le service :
```sh
docker compose up -d --build
```
Consulter son état et ses journaux :
```sh
docker compose ps
docker compose logs --tail=100 zola
```
Le service écoute uniquement sur linterface locale de lhôte :
```text
http://127.0.0.1:8002
```
Le domaine public et le certificat TLS sont pris en charge par un proxy inverse placé devant le conteneur.
Mettre à jour le déploiement :
```sh
git pull --ff-only
docker compose up -d --build
```
Arrêter le service :
```sh
docker compose down
```
## Licences
Le code propre au projet est distribué sous licence MIT ; voir [`LICENSE-CODE`](LICENSE-CODE).
Sauf mention contraire, les contenus originaux de `src/content/` sont publiés sous licence CC BY-SA 4.0 ; voir [`LICENSE-CONTENT`](LICENSE-CONTENT).
Les polices, photographies et autres ressources tierces restent soumises à leurs propres droits et licences.
+8
View File
@@ -0,0 +1,8 @@
name: les-carnets-d-akkadien
services:
zola:
build: .
ports:
- "127.0.0.1:8002:80"
restart: unless-stopped
+61
View File
@@ -0,0 +1,61 @@
"""Définitions communes au générateur et au validateur de contenus."""
SECTION_ALIASES = {
"article": "articles",
"articles": "articles",
"grammaire": "grammaire",
"signe": "signes",
"signes": "signes",
"texte": "textes",
"textes": "textes",
"vocabulaire": "vocabulaire",
}
VOCABULARY_FIELDS = {
"nom": ("meaning", "logograms", "gender", "bound", "plural"),
"adjectif": (
"meaning",
"logograms",
"bound",
"feminine",
"masculine_plural",
"feminine_plural",
"predicative",
),
"verbe": (
"meaning",
"logograms",
"root",
"stem",
"verb_class",
"vowel_class",
"preterite",
"durative",
"perfect",
"imperative",
"participle",
"verbal_adjective",
),
"pronom": (
"meaning",
"logograms",
"pronoun_type",
"person",
"gender",
"number",
),
"préposition": ("meaning", "logograms", "governs"),
"adverbe": ("meaning", "logograms", "function"),
"conjonction": ("meaning", "logograms", "function"),
"particule": ("meaning", "logograms", "function"),
"numéral": (
"meaning",
"logograms",
"numeral_type",
"value",
"gender",
"feminine",
),
}
KNOWN_SECTIONS = frozenset(SECTION_ALIASES.values())
+286
View File
@@ -0,0 +1,286 @@
#!/usr/bin/env python3
"""Vérifie la structure des contenus, le vocabulaire et les signes."""
import re
import sys
import tomllib
import unicodedata
from datetime import date
from pathlib import Path
from typing import Any
from _schema import KNOWN_SECTIONS, VOCABULARY_FIELDS
ROOT_DIR = Path(__file__).resolve().parent.parent
CONTENT_DIR = ROOT_DIR / "src" / "content"
MZL_FILENAME_RE = re.compile(r"mzl-([0-9]{3})\.md")
MZL_VALUE_RE = re.compile(r"[0-9]{3}")
class Validator:
def __init__(self) -> None:
self.errors = 0
def error(self, path: Path | None, message: str) -> None:
if path is None:
label = ""
else:
label = f"{path.relative_to(ROOT_DIR).as_posix()} : "
print(f"ERREUR : {label}{message}", file=sys.stderr)
self.errors += 1
def finish(self) -> int:
print(f"\nRésultat : {self.errors} erreur(s).")
return 1 if self.errors else 0
def is_non_empty_string(value: Any) -> bool:
return isinstance(value, str) and bool(value.strip())
def read_front_matter(path: Path, validator: Validator) -> dict[str, Any] | None:
try:
text = path.read_text(encoding="utf-8")
except UnicodeDecodeError as exc:
validator.error(path, f"fichier non valide en UTF-8 ({exc})")
return None
except OSError as exc:
validator.error(path, f"lecture impossible ({exc})")
return None
if not unicodedata.is_normalized("NFC", text):
validator.error(path, "contenu non normalisé en Unicode NFC")
lines = text.splitlines()
if not lines or lines[0] != "+++":
validator.error(path, "front matter TOML initial absent")
return None
try:
closing_index = lines.index("+++", 1)
except ValueError:
validator.error(path, "délimiteur final +++ absent")
return None
try:
return tomllib.loads("\n".join(lines[1:closing_index]))
except tomllib.TOMLDecodeError as exc:
validator.error(path, f"front matter TOML invalide ({exc})")
return None
def require_table(
metadata: dict[str, Any],
key: str,
path: Path,
validator: Validator,
) -> dict[str, Any] | None:
value = metadata.get(key)
if not isinstance(value, dict):
validator.error(path, f"{key} doit être une table TOML")
return None
return value
def require_string(
metadata: dict[str, Any],
key: str,
path: Path,
validator: Validator,
) -> None:
if not is_non_empty_string(metadata.get(key)):
validator.error(path, f"{key} doit être une chaîne non vide")
def require_date(
metadata: dict[str, Any],
path: Path,
validator: Validator,
) -> None:
if not isinstance(metadata.get("date"), date):
validator.error(path, "date doit être une date TOML valide")
def check_structure(
path: Path,
metadata: dict[str, Any],
validator: Validator,
) -> tuple[str, bool] | None:
relative_path = path.relative_to(CONTENT_DIR)
section = relative_path.parts[0] if len(relative_path.parts) > 1 else ""
if not unicodedata.is_normalized("NFC", relative_path.as_posix()):
validator.error(path, "chemin non normalisé en Unicode NFC")
if section and section not in KNOWN_SECTIONS:
validator.error(path, f"section inconnue « {section} »")
if path.name == "_index.md":
require_string(metadata, "title", path, validator)
return None
draft = metadata.get("draft")
if not isinstance(draft, bool):
validator.error(path, "draft doit être un booléen")
return None
if draft:
return section, True
require_string(metadata, "title", path, validator)
if section:
require_date(metadata, path, validator)
return section, False
def vocabulary_nature(
metadata: dict[str, Any],
path: Path,
validator: Validator,
) -> str | None:
taxonomies = require_table(metadata, "taxonomies", path, validator)
if taxonomies is None:
return None
natures = taxonomies.get("natures")
if not isinstance(natures, list) or len(natures) != 1:
validator.error(path, "taxonomies.natures doit contenir exactement une nature")
return None
nature = natures[0]
if not isinstance(nature, str):
validator.error(path, "taxonomies.natures doit contenir une chaîne")
return None
if nature not in VOCABULARY_FIELDS:
validator.error(path, f"nature grammaticale inconnue « {nature} »")
return None
return nature
def check_vocabulary(
path: Path,
metadata: dict[str, Any],
validator: Validator,
) -> None:
nature = vocabulary_nature(metadata, path, validator)
extra = require_table(metadata, "extra", path, validator)
if nature is None or extra is None:
return
expected_fields = VOCABULARY_FIELDS[nature]
expected_keys = set(expected_fields)
actual_keys = set(extra)
missing_fields = [field for field in expected_fields if field not in actual_keys]
if missing_fields:
validator.error(
path,
"champs extra manquants pour "
f"la nature « {nature} » : {', '.join(missing_fields)}",
)
unexpected_fields = sorted(actual_keys - expected_keys)
if unexpected_fields:
validator.error(
path,
"champs extra inattendus pour "
f"la nature « {nature} » : {', '.join(unexpected_fields)}",
)
for field in expected_fields:
if field not in extra:
continue
value = extra[field]
if field == "logograms":
if not isinstance(value, list) or not all(
is_non_empty_string(item) for item in value
):
validator.error(
path,
"extra.logograms doit être un tableau de chaînes non vides",
)
elif not isinstance(value, str):
validator.error(path, f"extra.{field} doit être une chaîne")
required_fields = ("meaning", "stem") if nature == "verbe" else ("meaning",)
for field in required_fields:
value = extra.get(field)
if isinstance(value, str) and not value.strip():
validator.error(path, f"extra.{field} doit être une chaîne non vide")
def filename_mzl(path: Path, validator: Validator) -> str | None:
match = MZL_FILENAME_RE.fullmatch(path.name)
if match is None:
validator.error(path, "le nom du fichier doit suivre le format mzl-XXX.md")
return None
value = match.group(1)
if int(value) == 0:
validator.error(path, "le numéro MZL doit être compris entre 001 et 999")
return None
return value
def check_sign(
path: Path,
metadata: dict[str, Any],
expected_mzl: str | None,
validator: Validator,
) -> None:
extra = require_table(metadata, "extra", path, validator)
if extra is None:
return
require_string(extra, "sign", path, validator)
declared_mzl = extra.get("mzl")
if not isinstance(declared_mzl, str) or MZL_VALUE_RE.fullmatch(declared_mzl) is None:
validator.error(path, "extra.mzl doit être une chaîne de trois chiffres ASCII")
return
if int(declared_mzl) == 0:
validator.error(path, "extra.mzl doit être compris entre 001 et 999")
return
if expected_mzl is not None and declared_mzl != expected_mzl:
validator.error(
path,
f'extra.mzl vaut "{declared_mzl}", mais le fichier indique MZL {expected_mzl}',
)
def main() -> int:
validator = Validator()
if not CONTENT_DIR.is_dir():
validator.error(None, f"répertoire introuvable : {CONTENT_DIR}")
return validator.finish()
print("Vérification des contenus…", flush=True)
for path in sorted(CONTENT_DIR.rglob("*.md")):
metadata = read_front_matter(path, validator)
if metadata is None:
continue
structure = check_structure(path, metadata, validator)
if structure is None:
continue
section, is_draft = structure
expected_mzl = filename_mzl(path, validator) if section == "signes" else None
if is_draft:
continue
if section == "vocabulaire":
check_vocabulary(path, metadata, validator)
elif section == "signes":
check_sign(path, metadata, expected_mzl, validator)
return validator.finish()
if __name__ == "__main__":
raise SystemExit(main())
+268
View File
@@ -0,0 +1,268 @@
#!/usr/bin/env python3
"""Crée un brouillon Markdown adapté à une section du site."""
import argparse
import json
import re
import sys
import unicodedata
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from _schema import SECTION_ALIASES, VOCABULARY_FIELDS
ROOT_DIR = Path(__file__).resolve().parent.parent
CONTENT_DIR = ROOT_DIR / "src" / "content"
TIMEZONE_NAME = "Europe/Paris"
MZL_RE = re.compile(r"(?:mzl-)?([0-9]{1,3})")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Créer un brouillon Markdown horodaté et prêt à compléter."
)
parser.add_argument(
"section",
choices=sorted(SECTION_ALIASES),
help="section de destination",
)
parser.add_argument(
"identifiant",
help="nom du fichier sans .md ; pour un signe, numéro MZL ou mzl-XXX",
)
parser.add_argument(
"--title",
help="titre initial ; déduit de lidentifiant par défaut",
)
parser.add_argument(
"--nature",
choices=tuple(VOCABULARY_FIELDS),
help="nature grammaticale dune fiche de vocabulaire",
)
return parser.parse_args()
def normalize(value: str) -> str:
return unicodedata.normalize("NFC", value.strip())
def toml_string(value: str) -> str:
return json.dumps(value, ensure_ascii=False)
def current_timestamp() -> str:
try:
timezone = ZoneInfo(TIMEZONE_NAME)
except ZoneInfoNotFoundError as exc:
raise RuntimeError(
f"fuseau {TIMEZONE_NAME} indisponible ; installer les données de fuseaux horaires"
) from exc
return datetime.now(timezone).isoformat(timespec="seconds")
def validate_identifier(identifier: str, *, allow_leading_hyphen: bool = False) -> str:
"""Valide un identifiant déjà écrit sous sa forme de nom de fichier."""
value = normalize(identifier)
if value.startswith("-") and not allow_leading_hyphen:
raise ValueError("seul le vocabulaire accepte un identifiant commençant par un tiret")
body = value[1:] if value.startswith("-") else value
if not body or value in {".", "..", "_index"}:
raise ValueError("identifiant de fichier invalide")
if value != value.lower():
raise ValueError("lidentifiant doit être écrit en minuscules")
if value.endswith("-") or "--" in value:
raise ValueError(
"lidentifiant ne doit pas finir par un tiret ni en contenir deux de suite"
)
if not all(character.isalnum() or character == "-" for character in body):
raise ValueError(
"lidentifiant ne doit contenir que des lettres, des chiffres et des tirets"
)
return value
def parse_mzl(identifier: str) -> tuple[str, str]:
value = normalize(identifier)
match = MZL_RE.fullmatch(value)
if match is None:
raise ValueError(
"un signe doit être identifié par un numéro MZL de un à trois chiffres ASCII"
)
number = int(match.group(1))
if not 1 <= number <= 999:
raise ValueError("le numéro MZL doit être compris entre 1 et 999")
mzl = f"{number:03d}"
return f"mzl-{mzl}", mzl
def choose_nature(nature: str | None) -> str:
if nature:
return nature
if not sys.stdin.isatty():
raise ValueError("--nature est requis pour une fiche de vocabulaire")
choices = tuple(VOCABULARY_FIELDS)
print("Nature grammaticale :")
for index, choice in enumerate(choices, start=1):
print(f" {index}. {choice}")
try:
answer = input("Choix : ").strip()
except (EOFError, KeyboardInterrupt) as exc:
raise ValueError("sélection interrompue") from exc
if not answer.isdigit() or not 1 <= int(answer) <= len(choices):
raise ValueError("nature grammaticale invalide")
return choices[int(answer) - 1]
def title_from_identifier(identifier: str) -> str:
title = identifier.replace("-", " ")
return title[:1].upper() + title[1:]
def build_vocabulary(title: str, timestamp: str, nature: str) -> str:
fields = "\n".join(
f"{field} = []" if field == "logograms" else f'{field} = ""'
for field in VOCABULARY_FIELDS[nature]
)
return f"""+++
title = {toml_string(title)}
date = {timestamp}
description = ""
draft = true
[extra]
{fields}
[taxonomies]
natures = [{toml_string(nature)}]
+++
"""
def build_sign(title: str, timestamp: str, mzl: str) -> str:
return f"""+++
title = {toml_string(title)}
date = {timestamp}
description = ""
draft = true
[extra]
sign = ""
mzl = "{mzl}"
[taxonomies]
lectures = []
+++
"""
def build_page(section: str, title: str, timestamp: str) -> str:
if section == "articles":
return f"""+++
title = {toml_string(title)}
date = {timestamp}
description = ""
draft = true
[extra]
banner = ""
banner_alt = ""
banner_credit = ""
+++
"""
if section == "grammaire":
return f"""+++
title = {toml_string(title)}
date = {timestamp}
description = ""
draft = true
[taxonomies]
themes = []
+++
"""
if section == "textes":
return f"""+++
title = {toml_string(title)}
date = {timestamp}
description = ""
draft = true
[taxonomies]
genres = []
+++
"""
raise ValueError(f"section non prise en charge : {section}")
def main() -> int:
args = parse_args()
section = SECTION_ALIASES[args.section]
try:
if section != "vocabulaire" and args.nature is not None:
raise ValueError("--nature est réservé aux fiches de vocabulaire")
timestamp = current_timestamp()
custom_title = normalize(args.title) if args.title is not None else None
if section == "signes":
filename, mzl = parse_mzl(args.identifiant)
title = custom_title if custom_title is not None else ""
content = build_sign(title, timestamp, mzl)
else:
filename = validate_identifier(
args.identifiant,
allow_leading_hyphen=section == "vocabulaire",
)
if section == "vocabulaire":
nature = choose_nature(args.nature)
title = custom_title if custom_title is not None else filename
content = build_vocabulary(title, timestamp, nature)
else:
title = (
custom_title
if custom_title is not None
else title_from_identifier(filename)
)
content = build_page(section, title, timestamp)
except (ValueError, RuntimeError) as exc:
print(f"Erreur : {exc}", file=sys.stderr)
return 2
destination = CONTENT_DIR / section / f"{filename}.md"
try:
destination.parent.mkdir(parents=True, exist_ok=True)
with destination.open("x", encoding="utf-8") as stream:
stream.write(content)
except FileExistsError:
print(
f"Erreur : le fichier existe déjà : {destination.relative_to(ROOT_DIR)}",
file=sys.stderr,
)
return 1
except OSError as exc:
print(
f"Erreur : impossible de créer {destination.relative_to(ROOT_DIR)} ({exc})",
file=sys.stderr,
)
return 1
print(f"Brouillon créé : {destination.relative_to(ROOT_DIR)}")
print("Compléter la fiche, passer draft à false, puis lancer :")
print(" ./scripts/check-content.py")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+14
View File
@@ -0,0 +1,14 @@
+++
title = "Les Carnets dAkkadien"
description = "Un site consacré à lapprentissage de la langue akkadienne et de l’écriture cunéiforme."
+++
## Lire largile
Restées enfouies pendant des millénaires, les tablettes cunéiformes ont conservé, dans plusieurs langues, des lettres, des contrats, des récits et dinnombrables traces de la vie quotidienne en Mésopotamie.
Parmi ces langues, lakkadien occupe une place majeure.
Lapprendre, cest entrer peu à peu dans la lecture de ces textes, retrouver des voix venues du passé et découvrir ce quelles ont encore à nous raconter.
Les Carnets dAkkadien tiennent le journal de mon apprentissage et de mes découvertes, au fil des signes, des mots et des textes — bonne lecture !
+10
View File
@@ -0,0 +1,10 @@
+++
title = "Articles"
description = "Réflexions et retours dexpérience sur lapprentissage de lakkadien."
sort_by = "date"
template = "articles.html"
page_template = "page.html"
[extra]
back_label = "Retour aux articles"
+++
@@ -0,0 +1,19 @@
+++
title = "Les Carnets sont en ligne !"
date = 2026-07-29T10:09:28+02:00
description = "Ou comment le bazar de mes notes a donné naissance à ce site."
draft = false
[extra]
banner = "images/articles/le-carnet-est-en-ligne.webp"
banner_alt = "Une pile de cahiers et de feuilles aux pages usées"
banner_credit = "Photographie : John-Mark Kuznietsov — Pexels"
+++
Jai commencé à apprendre lakkadien à la mi-avril avec *A Grammar of Akkadian* de John Huehnergard. Leçon après leçon, me voici arrivé, trois mois plus tard, à la neuvième leçon tant attendue : celle des premiers signes cunéiformes.
Une nouvelle étape souvre, faite de signes à reconnaître, à mémoriser et à tracer — et de nouvelles notes qui viennent sajouter aux feuilles et aux cahiers déjà bien encombrés !
Doù lidée de créer *Les Carnets dAkkadien* : un endroit où rassembler mes notes pour mieux my retrouver et pouvoir les consulter où que je sois. Le contenu est encore modeste, mais le site est fonctionnel. Il ne me reste plus qu’à replonger dans mes papiers et à remplir peu à peu les fiches.
Le site senrichira ainsi au fil de mon apprentissage. Jespère quavec le temps, ces carnets pourront aussi être utiles à dautres. Laventure ne fait que commencer !
+25
View File
@@ -0,0 +1,25 @@
+++
title = "Bibliographie"
draft = false
description = "Ouvrages de référence utilisés pour l’étude de lakkadien."
+++
Cette bibliographie rassemble les principaux ouvrages utilisés pour préparer
les fiches et les notes du site. Elle na pas vocation à être exhaustive.
## Grammaire
- **Huehnergard, John.** *A Grammar of Akkadian*. 3e éd.
Harvard Semitic Studies 45. Winona Lake (Indiana) : Eisenbrauns, 2011.
## Signes cunéiformes
- **Borger, Rykle.** *Mesopotamisches Zeichenlexikon* (**MZL**).
2e éd. revue et mise à jour. Alter Orient und Altes Testament 305.
Münster : Ugarit-Verlag, 2010.
## Dictionnaire
- **Black, Jeremy, Andrew George et Nicholas Postgate (dir.).**
*A Concise Dictionary of Akkadian* (**CDA**). 2e éd. corrigée.
SANTAG 5. Wiesbaden : Harrassowitz Verlag, 2000.
+10
View File
@@ -0,0 +1,10 @@
+++
title = "Grammaire"
description = "Notions de grammaire étudiées, avec leurs règles et leurs exemples."
sort_by = "title"
template = "grammaire.html"
page_template = "page.html"
[extra]
back_label = "Retour à la grammaire"
+++
+27
View File
@@ -0,0 +1,27 @@
+++
title = "La forme liée"
date = 2026-07-28T11:35:06+02:00
description = "Fiche dexemple consacrée à la forme liée du nom akkadien."
draft = true
[taxonomies]
themes = ["morphologie nominale"]
+++
> Cette fiche factice sert à vérifier la présentation des notes de grammaire.
## Principe
La forme liée, ou *status constructus*, est employée lorsquun nom est directement
suivi de son complément au génitif.
## Exemple
| Forme indépendante | Forme liée | Groupe nominal |
|---|---|---|
| *bītum* | *bīt* | *bīt šarrim* — « la maison du roi » |
## Remarque
La formation exacte dépend du type de nom. Cette fiche pourra être remplacée par une
note plus complète au fil de lapprentissage.
+18
View File
@@ -0,0 +1,18 @@
+++
title = "Mentions légales"
draft = false
description = "Informations légales relatives au site web Les Carnets dAkkadien."
+++
## Édition
Les Carnets dAkkadien est un site personnel édité à titre non professionnel.
## Hébergement
Le site est hébergé par :
**Hetzner Online GmbH**
Industriestr. 25
91710 Gunzenhausen
Allemagne
+10
View File
@@ -0,0 +1,10 @@
+++
title = "Signes cunéiformes"
description = "Signes étudiés, avec leurs lectures et leurs principaux usages."
sort_by = "slug"
template = "signes.html"
page_template = "signe.html"
[extra]
back_label = "Retour aux signes cunéiformes"
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "AŠ"
date = 2026-07-28T13:23:24+02:00
description = ""
draft = false
[extra]
sign = "𒀸"
mzl = "001"
[taxonomies]
lectures = ["aš", "rum"]
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "ḪAL"
date = 2026-07-30T14:29:32+02:00
description = ""
draft = false
[extra]
sign = "𒄬"
mzl = "003"
[taxonomies]
lectures = ["ḫal"]
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "AN"
date = 2026-07-31T14:04:03+02:00
description = ""
draft = false
[extra]
sign = "𒀭"
mzl = "010"
[taxonomies]
lectures = ["an"]
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "BA"
date = 2026-07-30T15:48:16+02:00
description = ""
draft = false
[extra]
sign = "𒁀"
mzl = "014"
[taxonomies]
lectures = ["ba"]
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "LA"
date = 2026-07-31T12:20:49+02:00
description = ""
draft = false
[extra]
sign = "𒆷"
mzl = "089"
[taxonomies]
lectures = ["la"]
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "MAḪ"
date = 2026-07-30T15:00:43+02:00
description = ""
draft = false
[extra]
sign = "𒈤"
mzl = "091"
[taxonomies]
lectures = ["maḫ"]
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "MU"
date = 2026-07-31T10:17:36+02:00
description = ""
draft = false
[extra]
sign = "𒈬"
mzl = "098"
[taxonomies]
lectures = ["mu"]
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "NA"
date = 2026-07-31T11:36:48+02:00
description = ""
draft = false
[extra]
sign = "𒈾"
mzl = "110"
[taxonomies]
lectures = ["na"]
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "NU"
date = 2026-07-31T12:41:08+02:00
description = ""
draft = false
[extra]
sign = "𒉡"
mzl = "112"
[taxonomies]
lectures = ["nu"]
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "BE"
date = 2026-07-31T19:43:12+02:00
description = ""
draft = false
[extra]
sign = "𒁁"
mzl = "113"
[taxonomies]
lectures = ["be", "bad", "bat", "baṭ", "til"]
+++
+14
View File
@@ -0,0 +1,14 @@
+++
title = "TIM"
date = 2026-07-31T19:25:30+02:00
description = ""
draft = false
[extra]
sign = "𒁴"
mzl = "167"
[taxonomies]
lectures = ["dim", "tim", "ṭim"]
+++
+10
View File
@@ -0,0 +1,10 @@
+++
title = "Textes"
description = "Textes étudiés, avec leurs translittérations, traductions et commentaires."
sort_by = "title"
template = "textes.html"
page_template = "texte.html"
[extra]
back_label = "Retour aux textes"
+++
+28
View File
@@ -0,0 +1,28 @@
+++
title = "Lettre dexemple"
date = 2026-07-28T18:40:02+02:00
description = "Court texte fictif destiné à tester la présentation des translittérations, traductions et commentaires."
draft = true
[taxonomies]
genres = ["lettre"]
+++
> Ce texte est un exemple de mise en page et ne correspond pas à une tablette publiée.
## Translittération
1. *a-na be-lí-ia*
2. *qí-bi-ma*
3. *um-ma PN-ma*
4. […]
## Traduction
À mon seigneur, dis : ainsi parle Untel. […]
## Commentaire
Cet exemple reprend la structure conventionnelle dune lettre paléo-babylonienne :
le destinataire est introduit par *ana … qibīma*, puis lexpéditeur par *umma …-ma*.
Il sert uniquement à vérifier le rendu des différentes parties dune fiche de texte.
+26
View File
@@ -0,0 +1,26 @@
+++
title = "-ma"
date = 2026-08-03T19:37:21+02:00
description = ""
draft = false
[extra]
meaning = "et ; et alors ; puis"
logograms = []
function = "coordination de propositions logiquement liées"
[taxonomies]
natures = ["particule"]
+++
## Emploi
*-ma* est une particule enclitique attachée à une forme verbale finie ou à un autre prédicat. Elle relie normalement deux propositions dont les verbes sont au même mode.
Les propositions ainsi reliées entretiennent un rapport logique : la première présente souvent les circonstances ou les conditions conduisant à laction exprimée par la seconde. L’élément principal de l’énoncé se trouve donc généralement dans la seconde proposition, et lordre des deux propositions ne peut pas être librement inversé.
Selon le contexte, *-ma* peut notamment être traduit par « et », « et alors », « puis », « et donc » ou parfois « mais ».
## Morphologie
Lorsquil est ajouté, *-ma* entraîne lallongement dune voyelle finale brève. Un *n* final sassimile normalement à son *m* ; les consonnes *b* et, plus rarement, *p* peuvent également sassimiler.
+10
View File
@@ -0,0 +1,10 @@
+++
title = "Vocabulaire"
description = "Mots étudiés, avec leurs formes et leurs principaux sens."
sort_by = "title"
template = "vocabulaire.html"
page_template = "mot.html"
[extra]
back_label = "Retour au vocabulaire"
+++
+23
View File
@@ -0,0 +1,23 @@
+++
title = "anāku"
date = 2026-08-03T09:19:49+02:00
description = ""
draft = false
[extra]
meaning = "je ; moi"
logograms = []
pronoun_type = "personnel indépendant"
person = "1re personne"
gender = "commun"
number = "singulier"
[taxonomies]
natures = ["pronom"]
+++
## Emploi
*anāku* est le pronom personnel indépendant de première personne du singulier, commun aux deux genres.
Il semploie comme sujet dune proposition sans verbe. Dans une proposition verbale, où la personne est déjà indiquée par la forme du verbe, il peut notamment servir à mettre le sujet en relief.
+24
View File
@@ -0,0 +1,24 @@
+++
title = "bītum"
date = 2026-07-28T15:32:28+02:00
description = ""
draft = false
[extra]
meaning = "maison"
logograms = []
gender = "masculin"
bound = "bīt(i)"
plural = "bītātum"
[taxonomies]
natures = ["nom"]
+++
## Déclinaison
| Cas | Singulier | Pluriel |
|---|---|---|
| Nominatif | *bītum* | *bītātum* |
| Génitif | *bītim* | *bītātim* |
| Accusatif | *bītam* | *bītātim* |
+31
View File
@@ -0,0 +1,31 @@
+++
title = "damqum"
date = 2026-08-03T19:13:17+02:00
updated = 2026-08-05
description = ""
draft = false
[extra]
meaning = "bon, de bonne qualité ; beau ; favorable"
logograms = []
bound = "damiq"
feminine = "damiqtum"
masculine_plural = "damqūtum"
feminine_plural = "damqātum"
predicative = "damiq"
[taxonomies]
natures = ["adjectif"]
+++
## Emploi
*damqum* signifie principalement « bon » ou « de bonne qualité ». Selon le contexte, il peut aussi qualifier ce qui est beau, favorable ou approprié.
Appliqué à une personne, il peut exprimer une qualité positive ou la compétence ; appliqué à une chose ou à une situation, il indique notamment la bonne qualité ou un caractère favorable.
## Morphologie
*damqum* est ladjectif verbal de *damāqum*, « devenir bon, saméliorer ». Sa base est *damiq-*.
La forme *damiq* correspond à la forme liée ainsi quau prédicatif de troisième personne masculine du singulier.
+23
View File
@@ -0,0 +1,23 @@
+++
title = "ina"
date = 2026-08-03T10:58:47+02:00
updated = 2026-08-05
description = ""
draft = false
[extra]
meaning = "dans, sur ; parmi ; avec, par ; de, depuis"
logograms = []
governs = "génitif"
[taxonomies]
natures = ["préposition"]
+++
## Emploi
*ina* est une préposition très fréquente. Elle peut exprimer le lieu ou la position, avec les sens « dans » ou « sur », ainsi que lappartenance à un ensemble, « parmi ».
Selon le contexte, elle peut également indiquer le moyen ou linstrument, « avec » ou « par », lorigine ou le point de départ, « de » ou « depuis », ainsi quune circonstance temporelle ou causale.
Le nom ou le groupe nominal qui suit *ina* est au génitif.
+23
View File
@@ -0,0 +1,23 @@
+++
title = "ištēn"
date = 2026-08-03T16:43:23+02:00
description = ""
draft = false
[extra]
meaning = "un"
logograms = []
numeral_type = "cardinal"
value = "1"
gender = "masculin"
feminine = "išteat"
[taxonomies]
natures = ["numéral"]
+++
## Emploi
*ištēn* est un numéral cardinal. Il semploie avec un nom masculin et *išteat* avec un nom féminin.
Le numéral « un » apparaît presque toujours à l’état absolu et le nom compté reste au singulier.
+29
View File
@@ -0,0 +1,29 @@
+++
title = "narkabtum"
date = 2026-07-31T15:46:34+02:00
updated = 2026-08-05
description = ""
draft = false
[extra]
meaning = "char, notamment char de guerre"
logograms = []
gender = "féminin"
bound = "narkabti"
plural = "narkabātum"
[taxonomies]
natures = ["nom"]
+++
## Emploi
*narkabtum* désigne un char. Le mot semploie notamment pour le char de guerre, mais peut aussi désigner un véhicule utilisé pour la chasse, les cérémonies ou le culte selon le contexte.
## Déclinaison
| Cas | Singulier | Pluriel |
|---|---|---|
| Nominatif | *narkabtum* | *narkabātum* |
| Génitif | *narkabtim* | *narkabātim* |
| Accusatif | *narkabtam* | *narkabātim* |
+34
View File
@@ -0,0 +1,34 @@
+++
title = "narûm"
date = 2026-08-03T20:00:00+02:00
description = ""
draft = false
[extra]
meaning = "stèle"
logograms = []
gender = "masculin"
bound = "naru"
plural = "narû"
[taxonomies]
natures = ["nom"]
+++
## Emploi
*narûm* désigne une stèle portant une inscription destinée à être rendue publique, notamment un décret ou une inscription royale. À des périodes plus tardives, le terme peut aussi désigner une borne inscrite.
## Morphologie
*narûm* est un nom masculin à base finale faible *naru- / narā-*. Sa forme liée est *naru* et son pluriel nominatif *narû*.
Il sagit dun emprunt au sumérien. Le mot ne doit pas être confondu avec *nārum*, nom féminin signifiant « rivière, cours deau, canal ».
## Déclinaison
| Cas | Singulier | Pluriel |
|---|---|---|
| Nominatif | *narûm* | *narû* |
| Génitif | *narêm* | *narê* |
| Accusatif | *narâm* | *narê* |
+40
View File
@@ -0,0 +1,40 @@
+++
title = "nasākum"
date = 2026-08-03T19:40:00+02:00
updated = 2026-08-05
description = ""
draft = false
[extra]
meaning = "jeter, lancer, projeter ; entasser (du grain)"
logograms = []
root = "n-s-k"
stem = "G"
verb_class = "I-n"
vowel_class = "u/u"
preterite = "issuk"
durative = "inassuk"
perfect = "ittasuk"
imperative = "usuk"
participle = "nāsikum"
verbal_adjective = "nasikum"
[taxonomies]
natures = ["verbe"]
+++
## Emploi
*nasākum* signifie principalement « jeter », « lancer » ou « projeter ». Il peut notamment semployer pour jeter quelque chose à terre ou dans un lieu, laisser tomber ou déposer une charge, lancer un projectile ou rejeter un objet.
Le verbe peut aussi signifier « mettre en tas » ou « entasser », en particulier à propos du grain. Avec *ana*, il indique souvent la direction ou la destination de ce qui est jeté. Lexpression *kirbānam nasākum*, littéralement « jeter une motte », peut décrire l’érosion dun terrain.
## Morphologie
Verbe I-n de classe vocalique u/u. La première radicale *n* sassimile dans plusieurs formes : *issuk* au prétérit et *ittasuk* au parfait ; elle demeure visible au duratif *inassuk*.
Le participe *nāsikum* et ladjectif verbal *nasikum* se distinguent par la longueur de la première voyelle. Le participe désigne lauteur de laction, tandis que ladjectif verbal exprime l’état résultant.
## Thèmes dérivés
Au thème Š, *šussukum* signifie notamment « enlever », « rejeter » ou « annuler ».
+40
View File
@@ -0,0 +1,40 @@
+++
title = "parāsum"
date = 2026-08-03T13:12:16+02:00
updated = 2026-08-05
description = ""
draft = false
[extra]
meaning = "couper, diviser, séparer ; sélectionner ; décider"
logograms = []
root = "p-r-s"
stem = "G"
verb_class = "fort"
vowel_class = "a/u"
preterite = "iprus"
durative = "iparras"
perfect = "iptaras"
imperative = "purus"
participle = "pārisum"
verbal_adjective = "parsum"
[taxonomies]
natures = ["verbe"]
+++
## Emploi
*parāsum* signifie dabord « couper », « diviser » ou « séparer ». Selon le contexte, il peut exprimer le fait de détacher une partie dun ensemble, de sélectionner une personne, un animal ou un bien, de tenir quelquun ou quelque chose à l’écart, ou encore dinterrompre ou de bloquer une action.
Dans les contextes administratifs et juridiques, le verbe signifie « décider », « trancher » ou « rendre une décision ». Lexpression *warkatam parāsum* désigne lexamen ou l’établissement des faits dune affaire ; *purussam parāsum* signifie « prononcer une décision » ou « rendre un verdict ».
## Morphologie
Verbe fort au thème G, de classe vocalique a/u. Sa conjugaison est régulière : le duratif *iparras* présente la voyelle *a*, tandis que le prétérit *iprus* présente la voyelle *u* ; le parfait est *iptaras* et limpératif *purus*.
Le participe *pārisum* désigne celui qui sépare ou qui décide. Ladjectif verbal *parsum* exprime l’état de ce qui est séparé, divisé ou distinct.
## Thèmes dérivés
Au thème D, *purrusum* peut signifier « couper, séparer », « diviser » ou « interrompre », mais aussi « établir les faits » ou « décider » selon le contexte.
+22
View File
@@ -0,0 +1,22 @@
+++
title = "u"
date = 2026-08-03T10:06:00+02:00
description = ""
draft = false
[extra]
meaning = "et"
logograms = []
function = "coordination"
[taxonomies]
natures = ["conjonction"]
+++
## Emploi
*u* est une conjonction indépendante servant à relier des groupes nominaux ou des propositions.
Entre deux propositions, *u* coordonne généralement des éléments de même importance. Leur ordre peut normalement être inversé sans modifier leur relation essentielle.
Selon le contexte, *u* peut aussi être traduit par « et aussi », « de plus » ou parfois « mais ».
+20
View File
@@ -0,0 +1,20 @@
+++
title = "ul"
date = 2026-08-03T11:13:23+02:00
description = ""
draft = false
[extra]
meaning = "ne… pas"
logograms = []
function = "négation dune proposition principale"
[taxonomies]
natures = ["adverbe"]
+++
## Emploi
*ul* est un adverbe négatif servant à nier le prédicat dune proposition principale. La forme habituelle est *ul* ; *ula* est moins fréquente.
Dans une proposition verbale, il se place immédiatement avant le verbe. Dans une proposition sans verbe, il précède le noyau du prédicat.
+40
View File
@@ -0,0 +1,40 @@
+++
title = "zakûm"
date = 2026-08-03T19:50:00+02:00
updated = 2026-08-05
description = ""
draft = false
[extra]
meaning = "être ou devenir clair, propre ou pur ; être ou devenir libre"
logograms = []
root = "z-k-w"
stem = "G"
verb_class = "III-faible (III-u)"
vowel_class = "u/u"
preterite = "izku"
durative = "izakku"
perfect = "izzaku"
imperative = "zuku"
participle = ""
verbal_adjective = "zakûm"
[taxonomies]
natures = ["verbe"]
+++
## Emploi
*zakûm* signifie « être ou devenir clair, propre ou pur ». Il peut notamment semployer à propos de leau, du ciel ou du temps, ainsi que du grain lorsquil est nettoyé de son enveloppe.
Le verbe signifie aussi « être ou devenir libre » dune revendication, dune obligation ou dune charge. Il peut ainsi qualifier une personne, un bien ou une marchandise libre de toute prétention.
## Morphologie
Verbe III-faible de type III-u, issu dune racine historique *z-k-w*. La disparition de la troisième radicale explique les formes terminées par *u*, notamment le prétérit *izku*, le duratif *izakku* et limpératif *zuku*. Au parfait, linfixe *-t-* sassimile au *z* initial, d*izzaku*.
Linfinitif et ladjectif verbal ont tous deux la forme *zakûm* au nominatif masculin singulier, mais ils reposent sur des bases différentes. Ladjectif verbal a pour base *zaku-* et signifie « clair, propre, pur ; libre ». Aucun participe nest renseigné dans les références retenues pour cette fiche.
## Thèmes dérivés
Au thème D, *zukkûm* signifie notamment « nettoyer, purifier, clarifier ; vanner » et « libérer, dégager dune obligation ».
+837
View File
@@ -0,0 +1,837 @@
// Couleurs
$paper: #fbf8f0;
$paper-soft: #f3eee4;
$paper-edge: #e7dfcf;
$ink: #28231d;
$heading: #3d2c20;
$muted: #6c6257;
$cuneiform: #5b5046;
$accent: #7a3f22;
$rule: #d8cdbb;
// Typographie et mise en page
$font-text: Georgia, "Times New Roman", serif;
$font-cuneiform: "Assurbanipal", "Noto Sans Cuneiform", "Segoe UI Historic", sans-serif;
$font-code: ui-monospace, "Cascadia Code", "Liberation Mono", monospace;
$content-width: 50rem;
// Police cunéiforme
@font-face {
font-family: "Assurbanipal";
src: url("fonts/assurbanipal.woff2") format("woff2");
font-style: normal;
font-weight: 400;
font-display: swap;
unicode-range: U+12000-123FF, U+12400-1247F;
}
// Base
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
color-scheme: light;
}
body {
display: flex;
min-height: 100vh;
min-height: 100svh;
margin: 0;
flex-direction: column;
color: $ink;
background: $paper;
font-family: $font-text;
font-size: clamp(1.0625rem, 1.02rem + 0.18vw, 1.125rem);
line-height: 1.7;
text-rendering: optimizeLegibility;
}
::selection {
color: $ink;
background: $paper-edge;
}
a {
color: $accent;
text-decoration-thickness: 0.08em;
text-underline-offset: 0.16em;
&:hover {
text-decoration-thickness: 0.14em;
}
&:focus-visible {
outline: 3px solid rgba($accent, 0.35);
outline-offset: 3px;
}
}
[id] {
scroll-margin-top: 2rem;
}
img,
svg {
display: block;
max-width: 100%;
height: auto;
}
// Mise en page générale
.container {
width: min(100% - 2rem, $content-width);
margin-inline: auto;
}
.skip-link {
position: fixed;
left: 0.75rem;
top: 0.75rem;
z-index: 100;
padding: 0.65rem 0.95rem;
color: $paper;
background: $ink;
font-weight: 700;
text-decoration: none;
transform: translateY(-180%);
&:focus {
transform: translateY(0);
}
}
.site-main {
padding-block: 2.75rem 4.25rem;
flex: 1 0 auto;
}
// En-tête et navigation
.site-header {
border-bottom: 1px solid $rule;
background: rgba(255, 255, 255, 0.35);
&__inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1.5rem;
padding-block: 1.15rem;
}
}
.site-title {
display: inline-flex;
align-items: flex-start;
flex-direction: column;
gap: 0.2rem;
color: $ink;
line-height: 1.08;
text-decoration: none;
white-space: nowrap;
&__text {
font-size: 1.35rem;
font-weight: 700;
letter-spacing: -0.01em;
}
&__cuneiform {
color: $cuneiform;
font-family: $font-cuneiform;
font-size: 1.52rem;
font-synthesis: none;
font-weight: 400;
letter-spacing: 0.1em;
line-height: 1;
}
&:hover &__cuneiform,
&:focus-visible &__cuneiform {
color: $accent;
}
}
.site-nav {
ul {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.25rem 1rem;
margin: 0;
padding: 0;
list-style: none;
}
a {
display: inline-block;
padding-block: 0.3rem;
border-bottom: 2px solid transparent;
color: $muted;
font-size: 0.95rem;
font-weight: 600;
line-height: 1.25;
text-decoration: none;
&:hover,
&:focus-visible {
color: $accent;
}
&[aria-current] {
border-bottom-color: $accent;
color: $ink;
}
}
}
// Titres et contenu courant
h1,
h2,
h3 {
color: $heading;
line-height: 1.22;
}
h1 {
margin: 0;
font-size: clamp(2.15rem, 5vw, 3rem);
letter-spacing: -0.025em;
}
h2 {
margin: 2.4rem 0 0;
font-size: clamp(1.4rem, 2vw, 1.58rem);
letter-spacing: -0.012em;
}
h3 {
margin: 1.85rem 0 0;
font-size: 1.24rem;
}
p,
ul,
ol,
dl,
table,
pre,
blockquote {
margin-block: 1rem;
}
ul,
ol {
padding-left: 1.45rem;
}
li + li {
margin-top: 0.18rem;
}
hr {
height: 1px;
margin-block: 2.25rem;
border: 0;
background: $rule;
}
// En-têtes de page et métadonnées
.entry-header {
margin-bottom: 2rem;
padding-bottom: 1.15rem;
border-bottom: 1px solid $rule;
> :last-child {
margin-bottom: 0;
}
}
.entry-description,
.section-description {
max-width: 72ch;
color: $muted;
font-size: 1.12rem;
line-height: 1.62;
}
.entry-date,
.entry-list__meta {
color: $muted;
font-size: 0.92rem;
font-variant-numeric: tabular-nums;
}
.metadata {
display: grid;
grid-template-columns: max-content minmax(0, 1fr);
gap: 0.38rem 1rem;
margin-block: 1.3rem 0;
dt {
color: $muted;
font-weight: 700;
}
dd {
min-width: 0;
margin: 0;
}
}
// Bannière facultative des articles
.article-cover {
margin: 0 0 1.6rem;
img {
width: 100%;
aspect-ratio: 16 / 7;
border: 1px solid $paper-edge;
object-fit: cover;
object-position: center;
}
figcaption {
margin-top: 0.25rem;
color: $muted;
font-size: 0.8rem;
text-align: right;
}
}
// Listes de contenus
.entry-list {
margin: 1.25rem 0 0;
padding: 0;
list-style: none;
li {
margin: 0;
padding-block: 0.82rem;
border-bottom: 1px solid $paper-edge;
}
a {
font-weight: 700;
}
&__meta {
margin-top: 0.12rem;
a {
color: inherit;
font-weight: 600;
&:hover,
&:focus-visible {
color: $accent;
}
}
}
}
// Page daccueil
.home-intro {
display: grid;
grid-template-columns: minmax(0, 1fr) 13.75rem;
gap: 2.25rem;
align-items: start;
&__text > :first-child {
margin-top: 0;
}
&__figure {
margin: 0;
text-align: center;
}
&__image {
width: 100%;
filter: drop-shadow(0 0.4rem 0.7rem rgba($ink, 0.2));
}
&__caption {
margin-top: 0.55rem;
color: $muted;
font-size: 0.78rem;
line-height: 1.4;
}
}
.home-latest,
.home-directory,
.home-about {
margin-top: 2.4rem;
> h2 {
margin-top: 0;
}
}
.home-latest {
&__entry {
margin-top: 1.15rem;
padding: 1.1rem 1.25rem;
border: 1px solid $rule;
background: rgba($accent, 0.035);
}
h3 {
margin: 0;
}
.entry-date {
margin: 0.2rem 0 0;
}
&__description {
margin: 0.75rem 0 0;
}
&__link {
display: inline-block;
margin-top: 0.75rem;
font-weight: 700;
}
}
.home-directory {
&__list {
margin: 1.15rem 0 0;
padding: 0;
border-top: 1px solid $paper-edge;
list-style: none;
li {
display: grid;
grid-template-columns: minmax(8rem, 11rem) minmax(0, 1fr);
gap: 1rem;
margin: 0;
padding-block: 0.82rem;
border-bottom: 1px solid $paper-edge;
}
a {
font-weight: 700;
}
p {
margin: 0;
color: $muted;
}
}
}
// Navigation secondaire et liens de contexte
.section-tools {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1.5rem;
margin-block: 2rem 2.5rem;
padding: 1.1rem 1.25rem;
border: 1px solid $rule;
background: rgba($accent, 0.035);
h2 {
margin: 0 0 0.2rem;
font-size: 1.2rem;
}
p {
margin: 0;
color: $muted;
}
&__link {
flex: 0 0 auto;
font-weight: 700;
}
}
.context-link {
margin: 0 0 1rem;
font-size: 0.95rem;
a {
color: $muted;
text-decoration: none;
&:hover,
&:focus-visible {
color: $accent;
text-decoration: underline;
}
}
}
// Cunéiforme
.cuneiform {
display: inline-block;
margin-right: 0.25em;
font-family: $font-cuneiform;
font-synthesis: none;
font-size: 1.2em;
font-weight: 400;
line-height: 1;
vertical-align: -0.08em;
}
// Tableaux, citations et code
table {
width: 100%;
border-collapse: collapse;
font-variant-numeric: tabular-nums;
}
th,
td {
padding: 0.58rem 0.72rem;
border: 1px solid $rule;
text-align: left;
vertical-align: top;
}
th {
color: $heading;
background: rgba($accent, 0.06);
}
tbody tr:nth-child(even) {
background: rgba($accent, 0.018);
}
blockquote {
margin-inline: 0;
padding: 0.05rem 0 0.05rem 1rem;
border-left: 0.2rem solid $rule;
color: $muted;
> :first-child {
margin-top: 0;
}
> :last-child {
margin-bottom: 0;
}
}
code {
padding: 0.08em 0.28em;
border: 1px solid $paper-edge;
background: $paper-soft;
font-family: $font-code;
font-size: 0.9em;
overflow-wrap: anywhere;
}
pre {
overflow-x: auto;
padding: 1rem 1.1rem;
border: 1px solid $paper-edge;
background: $paper-soft;
line-height: 1.55;
tab-size: 4;
code {
padding: 0;
border: 0;
background: transparent;
font-size: 0.88rem;
overflow-wrap: normal;
}
}
// Pied de page
.site-footer {
padding-block: 1.5rem 2rem;
border-top: 1px solid $rule;
color: $muted;
font-size: 0.9rem;
&__inner {
display: flex;
align-items: flex-start;
flex-direction: column;
gap: 0.45rem;
}
&__links {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
gap: 1rem 2rem;
nav {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 1rem;
text-align: right;
}
}
p,
&__contact {
margin: 0;
}
&__contact {
font-style: normal;
> a {
display: inline-flex;
align-items: center;
padding: 0.32rem 0.72rem;
border: 1px solid rgba($accent, 0.5);
border-radius: 0.3rem;
color: $accent;
background: rgba($accent, 0.05);
font-size: 0.95rem;
font-weight: 700;
line-height: 1.2;
text-decoration: none;
&:hover,
&:focus-visible {
border-color: $accent;
color: $heading;
background: rgba($accent, 0.12);
}
}
}
small {
font-size: inherit;
}
}
// Numéros de ligne générés par la coloration syntaxique de Zola
.giallo-l {
display: inline-block;
min-height: 1lh;
width: 100%;
}
.giallo-ln {
display: inline-block;
min-width: 3ch;
margin-right: 0.4em;
padding-right: 0.4em;
user-select: none;
text-align: right;
opacity: 0.72;
}
// Petits écrans
@media (max-width: 45rem) {
.container {
width: min(100% - 1.5rem, $content-width);
}
.site-header__inner {
align-items: center;
flex-direction: column;
gap: 0.55rem;
padding-block: 1rem;
text-align: center;
}
.site-title {
align-items: center;
}
.site-nav {
width: 100%;
ul {
justify-content: center;
gap: 0.1rem 0.85rem;
}
}
.site-main {
padding-block: 2.15rem 3.25rem;
}
h1 {
font-size: clamp(2rem, 10vw, 2.55rem);
}
.section-tools {
align-items: flex-start;
flex-direction: column;
gap: 0.75rem;
}
.home-intro {
grid-template-columns: 1fr;
gap: 1.5rem;
&__figure {
width: min(100%, 13.75rem);
margin-inline: auto;
}
}
.home-directory__list li {
grid-template-columns: 1fr;
gap: 0.1rem;
}
.metadata {
grid-template-columns: 1fr;
gap: 0.1rem;
dd + dt {
margin-top: 0.65rem;
}
}
table {
display: block;
max-width: 100%;
overflow-x: auto;
white-space: nowrap;
}
.site-footer__inner {
align-items: center;
text-align: center;
}
.site-footer__links {
align-items: center;
flex-direction: column;
gap: 0.75rem;
nav {
justify-content: center;
text-align: center;
}
}
.site-footer__license {
width: 100%;
text-align: center;
}
}
// Impression
@media print {
@page {
margin: 18mm;
}
html,
body {
color: #000;
background: #fff;
}
body {
display: block;
min-height: auto;
font-size: 11pt;
line-height: 1.5;
}
.container {
width: 100%;
max-width: none;
margin: 0;
}
.skip-link,
.site-nav,
.site-footer,
.section-tools,
.context-link {
display: none !important;
}
.home-intro__image {
filter: none;
}
.site-header {
border-bottom: 1px solid #999;
background: transparent;
&__inner {
display: block;
padding-block: 0 0.8rem;
}
}
.site-title {
align-items: flex-start;
color: #000;
&__cuneiform {
color: #333;
}
}
.site-main {
padding-block: 1.2rem 0;
}
h1,
h2,
h3 {
color: #000;
break-after: avoid-page;
}
p,
li,
blockquote {
orphans: 3;
widows: 3;
}
.entry-header,
.article-cover,
.entry-list li,
table,
blockquote,
pre,
img {
break-inside: avoid;
}
a {
color: inherit;
text-decoration: underline;
}
table {
display: table;
max-width: none;
overflow: visible;
white-space: normal;
}
thead {
display: table-header-group;
}
th {
background: #eee;
print-color-adjust: exact;
}
pre {
overflow: visible;
border-color: #aaa;
background: #fff;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
}
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

+9
View File
@@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block title %}Page introuvable · {{ config.title }}{% endblock title %}
{% block content %}
<h1>Page introuvable</h1>
<p>La page demandée nexiste pas ou a été déplacée.</p>
<p><a href="{{ get_url(path='@/_index.md') | safe }}">Revenir à laccueil</a></p>
{% endblock content %}
+29
View File
@@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block title %}{{ section.title }} · {{ config.title }}{% endblock title %}
{% block content %}
{% include "partials/section-header.html" %}
{{ section.content | safe }}
{% if section.pages %}
<h2>Liste des articles</h2>
<ul class="entry-list">
{% for page in section.pages %}
<li>
<a href="{{ page.permalink | safe }}">{{ page.title }}</a>
{% if page.date or page.description %}
<div class="entry-list__meta">
{% if page.date %}
<time datetime="{{ page.date }}">{{ page.date | date(format="%d/%m/%Y") }}</time>
{% endif %}
{% if page.date and page.description %} — {% endif %}
{% if page.description %}{{ page.description }}{% endif %}
</div>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock content %}
+122
View File
@@ -0,0 +1,122 @@
<!doctype html>
<html lang="{{ lang }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% if page is defined and page.description %}
<meta name="description" content="{{ page.description }}">
{% elif section is defined and section.description %}
<meta name="description" content="{{ section.description }}">
{% elif term is defined %}
<meta name="description" content="Contenus associés à « {{ term.name }} » dans {{ config.title }}.">
{% elif taxonomy is defined %}
<meta name="description" content="Index « {{ taxonomy.name }} » de {{ config.title }}.">
{% else %}
<meta name="description" content="{{ config.description }}">
{% endif %}
<meta name="author" content="{{ config.extra.author }}">
{% if current_url is defined %}
<link rel="canonical" href="{{ current_url | safe }}">
{% endif %}
<meta property="og:site_name" content="{{ config.title }}">
<link rel="alternate" type="application/atom+xml" title="{{ config.title }} — Atom"
href="{{ get_url(path='atom.xml', trailing_slash=false) | safe }}">
<link rel="alternate" type="application/rss+xml" title="{{ config.title }} — RSS"
href="{{ get_url(path='rss.xml', trailing_slash=false) | safe }}">
<title>{% block title %}{{ config.title }}{% endblock title %}</title>
<link rel="icon" type="image/png" sizes="64x64" href="{{ get_url(path='images/favicon.png') | safe }}">
<link rel="stylesheet" href="{{ get_url(path='main.css') | safe }}">
</head>
<body>
{% set nav_path = current_path | default(value="") %}
{% if nav_path == "/" %}
<div itemscope itemtype="https://schema.org/WebSite">
<link itemprop="url" href="{{ get_url(path='@/_index.md') | safe }}">
<meta itemprop="name" content="{{ config.title }}">
<meta itemprop="alternateName" content="lescarnetsdakkadien.netig.net">
</div>
{% endif %}
<a class="skip-link" href="#contenu">Aller au contenu</a>
<header class="site-header">
<div class="container site-header__inner">
<a class="site-title" href="{{ get_url(path='@/_index.md') | safe }}">
<span class="site-title__text">{{ config.title }}</span>
<span class="site-title__cuneiform" lang="akk-Xsux" aria-hidden="true">𒀝𒅗𒁺𒌑𒌝</span>
</a>
<nav class="site-nav" aria-label="Navigation principale">
<ul>
<li>
<a href="{{ get_url(path='@/signes/_index.md') | safe }}" {% if "/signes/" in nav_path
or "/lectures/" in nav_path %} aria-current="location" {% endif %}>Signes</a>
</li>
<li>
<a href="{{ get_url(path='@/vocabulaire/_index.md') | safe }}" {% if "/vocabulaire/" in nav_path
or "/natures/" in nav_path %} aria-current="location" {% endif %}>Vocabulaire</a>
</li>
<li>
<a href="{{ get_url(path='@/grammaire/_index.md') | safe }}" {% if "/grammaire/" in nav_path
or "/themes/" in nav_path %} aria-current="location" {% endif %}>Grammaire</a>
</li>
<li>
<a href="{{ get_url(path='@/textes/_index.md') | safe }}" {% if "/textes/" in nav_path
or "/genres/" in nav_path %} aria-current="location" {% endif %}>Textes</a>
</li>
<li>
<a href="{{ get_url(path='@/articles/_index.md') | safe }}" {% if "/articles/" in nav_path %}
aria-current="location" {% endif %}>Articles</a>
</li>
</ul>
</nav>
</div>
</header>
<main id="contenu" class="container site-main">
{% block content %}{% endblock content %}
</main>
<footer class="site-footer">
<div class="container site-footer__inner">
<div class="site-footer__links">
<address class="site-footer__contact">
<a href="mailto:{{ config.extra.email }}">Me contacter</a>
</address>
<nav aria-label="Liens complémentaires">
<a href="{{ get_url(path='@/bibliographie.md') | safe }}">
Bibliographie
</a>
<a href="{{ get_url(path='@/mentions-legales.md') | safe }}">
Mentions légales
</a>
<a href="{{ get_url(path='atom.xml', trailing_slash=false) | safe }}" type="application/atom+xml">
Flux Atom
</a>
<a href="{{ get_url(path='rss.xml', trailing_slash=false) | safe }}" type="application/rss+xml">
Flux RSS
</a>
</nav>
</div>
<p class="site-footer__license">
<small>
Sauf mention contraire, le contenu original du site web
{{ config.extra.author }} est placé sous licence
<a href="https://creativecommons.org/licenses/by-sa/4.0/deed.fr" rel="license">
CC BY-SA 4.0
</a>
</small>
</p>
</div>
</footer>
</body>
</html>
+38
View File
@@ -0,0 +1,38 @@
{% extends "base.html" %}
{% import "macros.html" as macros %}
{% block title %}{{ section.title }} · {{ config.title }}{% endblock title %}
{% block content %}
{% include "partials/section-header.html" %}
{{ section.content | safe }}
{{ macros::taxonomy_nav(
kind="themes",
heading_id="grammaire-index-title",
title="Explorer la grammaire",
description="Retrouver les fiches par notion grammaticale.",
path="themes",
link_label="Par thème grammatical"
) }}
{% if section.pages %}
<h2>Liste des fiches</h2>
<ul class="entry-list">
{% for page in section.pages %}
<li>
<a href="{{ page.permalink | safe }}">{{ page.title }}</a>
{% if page.description %} — {{ page.description }}{% endif %}
{% if page.taxonomies.themes %}
<div class="entry-list__meta">
{{ macros::taxonomy_links(terms=page.taxonomies.themes, kind="themes") }}
</div>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>Aucune fiche nest encore publiée dans cette section.</p>
{% endif %}
{% endblock content %}
+134
View File
@@ -0,0 +1,134 @@
{% extends "base.html" %}
{% block content %}
{% set signes = get_section(path="signes/_index.md") %}
{% set vocabulaire = get_section(path="vocabulaire/_index.md") %}
{% set grammaire = get_section(path="grammaire/_index.md") %}
{% set textes = get_section(path="textes/_index.md") %}
{% set articles = get_section(path="articles/_index.md") %}
{% set recent = signes.pages | concat(with=vocabulaire.pages) %}
{% set recent = recent | concat(with=grammaire.pages) %}
{% set recent = recent | concat(with=textes.pages) %}
{% set recent = recent | filter(attribute="date") | sort(attribute="date") | reverse %}
{% include "partials/section-header.html" %}
<section class="home-intro">
<div class="home-intro__text">
{{ section.content | safe }}
</div>
<figure class="home-intro__figure">
<img class="home-intro__image" src="{{ get_url(path='images/DP-12499-013.webp') | safe }}" width="640"
height="1120" alt="Tablette cunéiforme portant une lettre privée en akkadien paléo-babylonien"
decoding="async">
<figcaption class="home-intro__caption">
Lettre privée paléo-babylonienne, vers 1632 av. J.-C. —
<a href="https://www.metmuseum.org/art/collection/search/321706" target="_blank" rel="noopener noreferrer">
The Metropolitan Museum of Art, 86.11.111
</a>.
</figcaption>
</figure>
</section>
{% if articles.pages %}
{% set article = articles.pages | first %}
<section class="home-latest" aria-labelledby="home-latest-title">
<h2 id="home-latest-title">Dernier article</h2>
<article class="home-latest__entry">
<h3>
<a href="{{ article.permalink | safe }}">{{ article.title }}</a>
</h3>
<p class="entry-date">
<time datetime="{{ article.date }}">{{ article.date | date(format="%d/%m/%Y") }}</time>
</p>
{% if article.description %}
<p class="home-latest__description">{{ article.description }}</p>
{% endif %}
<a class="home-latest__link" href="{{ article.permalink | safe }}">Lire larticle</a>
</article>
</section>
{% endif %}
{% set directory_sections = [
signes,
vocabulaire,
grammaire,
textes,
articles
] %}
<nav class="home-directory" aria-labelledby="home-directory-title">
<h2 id="home-directory-title">Explorer les carnets</h2>
<ul class="home-directory__list">
{% for directory_section in directory_sections %}
<li>
<a href="{{ directory_section.permalink | safe }}">
{{ directory_section.title }}
</a>
{% if directory_section.description %}
<p>{{ directory_section.description }}</p>
{% endif %}
</li>
{% endfor %}
</ul>
</nav>
{% if recent %}
<section aria-labelledby="recent-title">
<h2 id="recent-title">Dernières fiches</h2>
<ul class="entry-list">
{% for item in recent | slice(end=3) %}
<li>
<a href="{{ item.permalink | safe }}">
{% if "/signes/" in item.path %}
{% set sign = item.extra | get(key="sign", default="") %}
{% if sign %}
<span class="cuneiform" lang="und-Xsux" aria-hidden="true">{{ sign }}</span>
{% endif %}
{% endif %}
{% if "/vocabulaire/" in item.path %}<span lang="akk-Latn">{{ item.title }}</span>{% else %}{{
item.title }}{% endif %}
</a>
<div class="entry-list__meta">
{% if "/signes/" in item.path %}
Signe
{% elif "/vocabulaire/" in item.path %}
Vocabulaire
{% elif "/grammaire/" in item.path %}
Grammaire
{% elif "/textes/" in item.path %}
Texte
{% endif %}
· <time datetime="{{ item.date }}">{{ item.date | date(format="%d/%m/%Y") }}</time>
</div>
</li>
{% endfor %}
</ul>
</section>
{% endif %}
<section class="home-about" aria-labelledby="home-about-title">
<h2 id="home-about-title">À propos de ce site</h2>
<p>
Ce site évolue au rythme de mon apprentissage et ne prétend pas se substituer à une grammaire ou à un
dictionnaire spécialisé. Japprends pour linstant le paléo-babylonien : les formes et les analyses présentées
ici concernent donc principalement cette période. Les autres périodes ou dialectes seront précisés au fil de mes
découvertes. Les principaux ouvrages sur lesquels je mappuie sont présentés dans la bibliographie.
</p>
<p>
Les fiches peuvent être consultées à l’écran, imprimées ou enregistrées au format PDF.
</p>
<p>
Suggestions, remarques et corrections sont les bienvenues ; un lien de contact est disponible ci-dessous.
</p>
</section>
{% endblock content %}
+43
View File
@@ -0,0 +1,43 @@
{% macro taxonomy_links(terms, kind) -%}
{%- for term in terms -%}
{%- if not loop.first %}, {% endif -%}
<a href="{{ get_taxonomy_url(kind=kind, name=term) | safe }}">{{ term }}</a>
{%- endfor -%}
{%- endmacro taxonomy_links %}
{% macro taxonomy_nav(kind, heading_id, title, description, path, link_label) %}
{% set taxonomy = get_taxonomy(kind=kind, required=false) %}
{% if taxonomy and taxonomy.items %}
<nav class="section-tools" aria-labelledby="{{ heading_id }}">
<div>
<h2 id="{{ heading_id }}">{{ title }}</h2>
<p>{{ description }}</p>
</div>
<a class="section-tools__link" href="{{ get_url(path=path, trailing_slash=true) | safe }}">
{{ link_label }}
</a>
</nav>
{% endif %}
{% endmacro taxonomy_nav %}
{% macro metadata_field(extra, key, label, lang="") -%}
{%- set value = extra | get(key=key, default="") -%}
{%- if value -%}
<dt>{{ label }}</dt>
<dd{% if lang %} lang="{{ lang }}"{% endif %}>{{ value }}</dd>
{%- endif -%}
{%- endmacro metadata_field %}
{% macro metadata_list(extra, key, label, lang="") -%}
{%- set values = extra | get(key=key, default="") -%}
{%- if values -%}
<dt>{{ label }}</dt>
<dd{% if lang %} lang="{{ lang }}"{% endif %}>
{%- for value in values -%}
{%- if not loop.first %}, {% endif -%}
{{- value -}}
{%- endfor -%}
</dd>
{%- endif -%}
{%- endmacro metadata_list %}
+66
View File
@@ -0,0 +1,66 @@
{% extends "base.html" %}
{% import "macros.html" as macros %}
{% block title %}{{ page.title }} · {{ config.title }}{% endblock title %}
{% block content %}
{% include "partials/back-to-section.html" %}
<article>
<header class="entry-header">
<h1 lang="akk-Latn">{{ page.title }}</h1>
{% if page.description %}
<p class="entry-description">{{ page.description }}</p>
{% else %}
<p class="entry-description">
{% if page.taxonomies.natures %}
{% for nature in page.taxonomies.natures %}
{% if loop.first %}{{ nature | capitalize }}{% endif %}
{% endfor %}
{% else %}
Mot
{% endif %}
{% if page.extra.gender %} {{ page.extra.gender }}{% endif %}{% if page.extra.meaning %} signifiant « {{ page.extra.meaning }} »{% endif %}.
</p>
{% endif %}
{% include "partials/entry-dates.html" %}
<dl class="metadata">
<dt>Nature grammaticale</dt>
<dd>{{ macros::taxonomy_links(terms=page.taxonomies.natures, kind="natures") }}</dd>
{# Tera 1.x naccepte pas les tableaux imbriqués dans un littéral set.
Les champs restent donc explicites et leur balisage est factorisé par macro. #}
{{ macros::metadata_field(extra=page.extra, key="meaning", label="Sens") }}
{{ macros::metadata_list(extra=page.extra, key="logograms", label="Logogrammes", lang="sux-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="gender", label="Genre") }}
{{ macros::metadata_field(extra=page.extra, key="pronoun_type", label="Type de pronom") }}
{{ macros::metadata_field(extra=page.extra, key="person", label="Personne") }}
{{ macros::metadata_field(extra=page.extra, key="number", label="Nombre") }}
{{ macros::metadata_field(extra=page.extra, key="numeral_type", label="Type de numéral") }}
{{ macros::metadata_field(extra=page.extra, key="value", label="Valeur") }}
{{ macros::metadata_field(extra=page.extra, key="governs", label="Cas régi") }}
{{ macros::metadata_field(extra=page.extra, key="function", label="Fonction") }}
{{ macros::metadata_field(extra=page.extra, key="bound", label="Forme liée", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="plural", label="Pluriel", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="feminine", label="Féminin", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="masculine_plural", label="Pluriel masculin", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="feminine_plural", label="Pluriel féminin", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="predicative", label="Prédicatif (3 m. s.)", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="root", label="Racine", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="stem", label="Thème") }}
{{ macros::metadata_field(extra=page.extra, key="verb_class", label="Classe verbale") }}
{{ macros::metadata_field(extra=page.extra, key="vowel_class", label="Classe vocalique") }}
{{ macros::metadata_field(extra=page.extra, key="preterite", label="Prétérit", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="durative", label="Duratif", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="perfect", label="Parfait", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="imperative", label="Impératif", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="participle", label="Participe", lang="akk-Latn") }}
{{ macros::metadata_field(extra=page.extra, key="verbal_adjective", label="Adjectif verbal", lang="akk-Latn") }}
</dl>
</header>
{{ page.content | safe }}
</article>
{% endblock content %}
+47
View File
@@ -0,0 +1,47 @@
{% extends "base.html" %}
{% import "macros.html" as macros %}
{% block title %}{{ page.title }} · {{ config.title }}{% endblock title %}
{% block content %}
{% include "partials/back-to-section.html" %}
<article>
<header class="entry-header">
<h1>{{ page.title }}</h1>
{% if page.description %}
<p class="entry-description">{{ page.description }}</p>
{% endif %}
{% include "partials/entry-dates.html" %}
{% if page.taxonomies.themes %}
<dl class="metadata">
<dt>Thème</dt>
<dd>
{{ macros::taxonomy_links(terms=page.taxonomies.themes, kind="themes") }}
</dd>
</dl>
{% endif %}
</header>
{% set banner = page.extra | get(key="banner", default="") %}
{% if banner %}
{% set banner_alt = page.extra | get(key="banner_alt", default="") %}
{% set banner_credit = page.extra | get(key="banner_credit", default="") %}
<figure class="article-cover">
<img
src="{{ get_url(path=banner) | safe }}"
alt="{{ banner_alt }}"
>
{% if banner_credit %}
<figcaption>{{ banner_credit }}</figcaption>
{% endif %}
</figure>
{% endif %}
{{ page.content | safe }}
</article>
{% endblock content %}
@@ -0,0 +1,13 @@
{% if page.ancestors %}
{% set parent_path = page.ancestors | last %}
{% set parent = get_section(path=parent_path, metadata_only=true) %}
{% set back_label = parent.extra | get(key="back_label", default="") %}
{% if back_label %}
<p class="context-link">
<a href="{{ parent.permalink | safe }}">
← {{ back_label }}
</a>
</p>
{% endif %}
{% endif %}
+13
View File
@@ -0,0 +1,13 @@
{% if page.date or page.updated %}
<p class="entry-date">
{% if page.date %}
Mis en ligne le
<time datetime="{{ page.date }}">{{ page.date | date(format="%d/%m/%Y") }}</time>
{% endif %}
{% if page.updated %}
{% if page.date %} · {% endif %}
Mis à jour le
<time datetime="{{ page.updated }}">{{ page.updated | date(format="%d/%m/%Y") }}</time>
{% endif %}
</p>
{% endif %}
@@ -0,0 +1,6 @@
<header class="entry-header">
<h1>{{ section.title }}</h1>
{% if section.description %}
<p class="section-description">{{ section.description }}</p>
{% endif %}
</header>
+39
View File
@@ -0,0 +1,39 @@
{% extends "base.html" %}
{% import "macros.html" as macros %}
{% block title %}{{ page.title }} · {{ config.title }}{% endblock title %}
{% block content %}
{% include "partials/back-to-section.html" %}
<article>
<header class="entry-header">
<h1><span class="cuneiform" lang="und-Xsux" aria-hidden="true">{{ page.extra.sign }}</span> {{ page.title }}
</h1>
{% if page.description %}
<p class="entry-description">{{ page.description }}</p>
{% else %}
<p class="entry-description">
Signe cunéiforme {{ page.title }}{% if page.extra.mzl %} — MZL {{ page.extra.mzl }}{% endif %}.
</p>
{% endif %}
{% include "partials/entry-dates.html" %}
<dl class="metadata">
<dt>MZL</dt>
<dd>{{ page.extra.mzl }}</dd>
{% if page.taxonomies.lectures %}
<dt>Lectures</dt>
<dd>
{{ macros::taxonomy_links(terms=page.taxonomies.lectures, kind="lectures") }}
</dd>
{% endif %}
</dl>
</header>
{{ page.content | safe }}
</article>
{% endblock content %}
+39
View File
@@ -0,0 +1,39 @@
{% extends "base.html" %}
{% import "macros.html" as macros %}
{% block title %}{{ section.title }} · {{ config.title }}{% endblock title %}
{% block content %}
{% include "partials/section-header.html" %}
{{ section.content | safe }}
{{ macros::taxonomy_nav(
kind="lectures",
heading_id="signes-index-title",
title="Explorer les signes",
description="Retrouver les signes par lecture.",
path="lectures",
link_label="Index des lectures"
) }}
{% if section.pages %}
<h2>Liste des signes</h2>
<ul class="entry-list">
{% for page in section.pages %}
<li>
<a href="{{ page.permalink | safe }}">
<span class="cuneiform" lang="und-Xsux" aria-hidden="true">{{ page.extra.sign }}</span> {{ page.title }}
</a>
<div class="entry-list__meta">
MZL {{ page.extra.mzl }}
{% if page.taxonomies.lectures %}
·
{{ macros::taxonomy_links(terms=page.taxonomies.lectures, kind="lectures") }}
{% endif %}
</div>
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock content %}
+59
View File
@@ -0,0 +1,59 @@
{% extends "base.html" %}
{% block title %}{% if taxonomy.name == "natures" %}Vocabulaire par nature grammaticale{% elif taxonomy.name == "lectures" %}Index des lectures des signes{% elif taxonomy.name == "genres" %}Textes par type{% elif taxonomy.name == "themes" %}Grammaire par thème{% else %}{{ taxonomy.name | capitalize }}{% endif %} · {{ config.title }}{% endblock title %}
{% block content %}
{% set heading = taxonomy.name | capitalize %}
{% set description = "Index des entrées de cette taxonomie." %}
{% set back_url = get_url(path='@/_index.md') %}
{% set back_label = "Retour à laccueil" %}
{% set item_label = "entrée" %}
{% if taxonomy.name == "natures" %}
{% set heading = "Vocabulaire par nature grammaticale" %}
{% set description = "Index des fiches lexicales regroupées selon leur nature grammaticale." %}
{% set back_url = get_url(path='@/vocabulaire/_index.md') %}
{% set back_label = "Retour au vocabulaire" %}
{% set item_label = "mot" %}
{% elif taxonomy.name == "lectures" %}
{% set heading = "Index des lectures des signes" %}
{% set description = "Index permettant de retrouver les signes cunéiformes à partir dune lecture." %}
{% set back_url = get_url(path='@/signes/_index.md') %}
{% set back_label = "Retour aux signes cunéiformes" %}
{% set item_label = "signe" %}
{% elif taxonomy.name == "genres" %}
{% set heading = "Types de textes" %}
{% set description = "Index des textes étudiés regroupés selon leur type documentaire." %}
{% set back_url = get_url(path='@/textes/_index.md') %}
{% set back_label = "Retour aux textes" %}
{% set item_label = "texte" %}
{% elif taxonomy.name == "themes" %}
{% set heading = "Thèmes grammaticaux" %}
{% set description = "Index des fiches de grammaire regroupées selon la notion étudiée." %}
{% set back_url = get_url(path='@/grammaire/_index.md') %}
{% set back_label = "Retour à la grammaire" %}
{% set item_label = "fiche" %}
{% endif %}
<p class="context-link">
<a href="{{ back_url | safe }}">← {{ back_label }}</a>
</p>
<header class="entry-header">
<h1>{{ heading }}</h1>
<p class="section-description">{{ description }}</p>
</header>
{% if terms %}
<ul class="entry-list">
{% for term in terms %}
<li>
<a href="{{ term.permalink | safe }}">{{ term.name }}</a>
<span class="entry-list__meta">
— {{ term.page_count }} {{ item_label }}{% if term.page_count > 1 %}s{% endif %}
</span>
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock content %}
+75
View File
@@ -0,0 +1,75 @@
{% extends "base.html" %}
{% block title %}{% if taxonomy.name == "natures" %}Mots : {{ term.name }}{% elif taxonomy.name == "lectures" %}Lecture « {{ term.name }} »{% elif taxonomy.name == "genres" %}Textes : {{ term.name }}{% elif taxonomy.name == "themes" %}Grammaire : {{ term.name }}{% else %}{{ term.name }}{% endif %} · {{ config.title }}{% endblock title %}
{% block content %}
{% set heading = term.name %}
{% set description = "Entrées classées dans cet index." %}
{% set back_url = get_url(path=taxonomy.name, trailing_slash=true) %}
{% set back_label = "Retour à lindex" %}
{% if taxonomy.name == "natures" %}
{% set heading = "Mots : " ~ term.name %}
{% set description = "Fiches de vocabulaire classées comme « " ~ term.name ~ " »." %}
{% set back_url = get_url(path='natures', trailing_slash=true) %}
{% set back_label = "Toutes les natures grammaticales" %}
{% elif taxonomy.name == "lectures" %}
{% set heading = "Lecture « " ~ term.name ~ " »" %}
{% set description = "Signes cunéiformes pouvant recevoir la lecture « " ~ term.name ~ " »." %}
{% set back_url = get_url(path='lectures', trailing_slash=true) %}
{% set back_label = "Toutes les lectures" %}
{% elif taxonomy.name == "genres" %}
{% set heading = "Textes : " ~ term.name %}
{% set description = "Textes classés dans le type « " ~ term.name ~ " »." %}
{% set back_url = get_url(path='genres', trailing_slash=true) %}
{% set back_label = "Tous les types de textes" %}
{% elif taxonomy.name == "themes" %}
{% set heading = "Thème : " ~ term.name %}
{% set description = "Fiches de grammaire classées dans le thème « " ~ term.name ~ " »." %}
{% set back_url = get_url(path='themes', trailing_slash=true) %}
{% set back_label = "Tous les thèmes grammaticaux" %}
{% endif %}
<p class="context-link">
<a href="{{ back_url | safe }}">← {{ back_label }}</a>
</p>
<header class="entry-header">
<h1>{{ heading }}</h1>
<p class="section-description">{{ description }}</p>
</header>
{% if term.pages %}
{% if taxonomy.name == "lectures" %}
{% set taxonomy_pages = term.pages | sort(attribute="slug") %}
{% else %}
{% set taxonomy_pages = term.pages | sort(attribute="title") %}
{% endif %}
<ul class="entry-list">
{% for page in taxonomy_pages %}
<li>
{% if taxonomy.name == "lectures" %}
<a href="{{ page.permalink | safe }}">
<span class="cuneiform" lang="und-Xsux" aria-hidden="true">{{ page.extra.sign }}</span> {{ page.title }}
</a>
<div class="entry-list__meta">MZL {{ page.extra.mzl }}</div>
{% elif taxonomy.name == "natures" %}
<a href="{{ page.permalink | safe }}"><span lang="akk-Latn">{{ page.title }}</span></a>
{% if page.extra.meaning %} — {{ page.extra.meaning }}{% endif %}
{% if page.extra.gender %}
<div class="entry-list__meta">{{ page.extra.gender }}</div>
{% endif %}
{% else %}
<a href="{{ page.permalink | safe }}">{{ page.title }}</a>
{% if page.description %}
<div class="entry-list__meta">{{ page.description }}</div>
{% endif %}
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>Aucune fiche nest encore associée à cette entrée.</p>
{% endif %}
{% endblock content %}
+30
View File
@@ -0,0 +1,30 @@
{% extends "base.html" %}
{% import "macros.html" as macros %}
{% block title %}{{ page.title }} · {{ config.title }}{% endblock title %}
{% block content %}
{% include "partials/back-to-section.html" %}
<article>
<header class="entry-header">
<h1>{{ page.title }}</h1>
{% if page.description %}
<p class="entry-description">{{ page.description }}</p>
{% endif %}
{% include "partials/entry-dates.html" %}
{% if page.taxonomies.genres %}
<dl class="metadata">
<dt>Type de texte</dt>
<dd>
{{ macros::taxonomy_links(terms=page.taxonomies.genres, kind="genres") }}
</dd>
</dl>
{% endif %}
</header>
{{ page.content | safe }}
</article>
{% endblock content %}
+38
View File
@@ -0,0 +1,38 @@
{% extends "base.html" %}
{% import "macros.html" as macros %}
{% block title %}{{ section.title }} · {{ config.title }}{% endblock title %}
{% block content %}
{% include "partials/section-header.html" %}
{{ section.content | safe }}
{{ macros::taxonomy_nav(
kind="genres",
heading_id="textes-index-title",
title="Explorer les textes",
description="Retrouver les textes par type documentaire.",
path="genres",
link_label="Par type de texte"
) }}
{% if section.pages %}
<h2>Liste des textes</h2>
<ul class="entry-list">
{% for page in section.pages %}
<li>
<a href="{{ page.permalink | safe }}">{{ page.title }}</a>
{% if page.description %} — {{ page.description }}{% endif %}
{% if page.taxonomies.genres %}
<div class="entry-list__meta">
{{ macros::taxonomy_links(terms=page.taxonomies.genres, kind="genres") }}
</div>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>Aucun texte nest encore publié dans cette section.</p>
{% endif %}
{% endblock content %}
+35
View File
@@ -0,0 +1,35 @@
{% extends "base.html" %}
{% import "macros.html" as macros %}
{% block title %}{{ section.title }} · {{ config.title }}{% endblock title %}
{% block content %}
{% include "partials/section-header.html" %}
{{ section.content | safe }}
{{ macros::taxonomy_nav(
kind="natures",
heading_id="vocabulaire-index-title",
title="Explorer le vocabulaire",
description="Retrouver les mots par nature grammaticale.",
path="natures",
link_label="Par nature grammaticale"
) }}
{% if section.pages %}
<h2>Liste des mots</h2>
<ul class="entry-list">
{% for page in section.pages %}
<li>
<a href="{{ page.permalink | safe }}"><span lang="akk-Latn">{{ page.title }}</span></a>
{% if page.extra.meaning %} — {{ page.extra.meaning }}{% endif %}
<div class="entry-list__meta">
{{ macros::taxonomy_links(terms=page.taxonomies.natures, kind="natures") }}
{% if page.extra.gender %} · {{ page.extra.gender }}{% endif %}
</div>
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock content %}
+32
View File
@@ -0,0 +1,32 @@
base_url = "https://lescarnetsdakkadien.netig.net"
title = "Les Carnets dAkkadien"
description = "Un site consacré à lapprentissage de la langue akkadienne et de l’écriture cunéiforme."
default_language = "fr"
compile_sass = true
build_search_index = false
generate_feeds = true
feed_filenames = ["atom.xml", "rss.xml"]
taxonomies = [
{ name = "natures" },
{ name = "lectures" },
{ name = "themes" },
{ name = "genres" },
]
[markdown]
[markdown.highlighting]
theme = "github-light"
[slugify]
# Conserver les diacritiques dans les termes de taxonomie afin de
# distinguer des lectures comme « tim » et « ṭim ».
taxonomies = "safe"
paths = "safe"
[extra]
author = "Les Carnets dAkkadien"
email = "julien.deb@netig.net"