Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .githooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Repo git hooks

This directory holds repo-tracked git hooks. They are **not** active by default
on a fresh clone — git looks at `.git/hooks/` first, which is local and untracked.

## One-time activation per clone

Run from the repo root:

```bash
git config core.hooksPath .githooks
```

That's it. From now on, hooks in this directory will fire on the appropriate
events.

## What's here

- **`pre-commit`** — i18n parity gate. Blocks any commit that adds/modifies a
`content/it/*.md` or `content/en/*.md` page without a matching sibling in the
other language tree carrying the same `translationKey:` frontmatter value.
Mirrors the server-side `.github/workflows/i18n-parity.yml` check.

## Bypassing

In a real emergency: `git commit --no-verify`. The server-side workflow is the
non-bypassable gate, so the only thing `--no-verify` buys you is faster local
iteration before push.
72 changes: 72 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
# i18n parity gate — block commits where the staged content/it/* and
# content/en/* files don't pair up via translationKey.
#
# Dir-split layout: content/it/foo.md (Italian) and content/en/bar.md
# (English) are paired via the `translationKey:` frontmatter field. Both
# siblings must declare the same key. The hook scans the staged tree and
# fails if a touched page lacks a key or its counterpart key is missing
# from the OTHER language tree.
#
# Bypass for emergencies: git commit --no-verify (CI parity check is the
# non-bypassable gate — see .github/workflows/i18n-parity.yml).

set -euo pipefail

# Files staged for commit (Added/Modified/Renamed), filtered to content/{it,en}/*.md
mapfile -t staged < <(
git diff --cached --name-only --diff-filter=AMR \
| grep -E '^content/(it|en)/.*\.md$' || true
)

[[ ${#staged[@]} -eq 0 ]] && exit 0

# extract_key <path>: print translationKey value, empty if missing.
extract_key() {
awk '
/^---$/ { if (in_fm) exit; in_fm=1; next }
in_fm && /^translationKey:[[:space:]]*/ {
sub(/^translationKey:[[:space:]]*/, ""); gsub(/["'\''`]/, ""); print; exit
}
' "$1"
}

# has_key_in <key> <lang>: 0 if any content/<lang>/**/*.md declares translationKey=<key>.
has_key_in() {
local key="$1" lang="$2" f
[[ -d "content/$lang" ]] || return 1
while IFS= read -r f; do
[[ "$(extract_key "$f")" == "$key" ]] && return 0
done < <(find "content/$lang" -type f -name '*.md')
return 1
}

missing=()
for f in "${staged[@]}"; do
key=$(extract_key "$f")
# No translationKey = page declares no pairing yet; pre-translation
# half-step is allowed (the gate only enforces well-formed pairs, it
# does not force every page to be translated).
[[ -z "$key" ]] && continue

case "$f" in
content/it/*) other="en" ;;
content/en/*) other="it" ;;
*) continue ;;
esac

has_key_in "$key" "$other" || \
missing+=("$f → translationKey '$key' missing in content/$other/")
done

if [[ ${#missing[@]} -gt 0 ]]; then
echo "✗ i18n parity check failed:" >&2
printf ' - %s\n' "${missing[@]}" >&2
echo "" >&2
echo "Fix: add the missing sibling under content/<other-lang>/ with the same" >&2
echo "translationKey, or run 'git commit --no-verify' if you are intentionally" >&2
echo "staging a half-step (the CI parity check will still gate the PR)." >&2
exit 1
fi

exit 0
86 changes: 86 additions & 0 deletions .github/workflows/i18n-parity.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: i18n parity

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
parity:
name: IT/EN parity check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Verify every content/{it,en}/*.md touched in this PR pairs via translationKey
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail

# Delta-based parity gate — mirrors .githooks/pre-commit. Only
# files touched in THIS PR are checked. For each touched
# content/{it,en}/*.md the resulting tree must contain a
# counterpart in the OTHER language tree carrying the same
# `translationKey:` frontmatter value.
if [[ -n "${BASE_SHA:-}" && -n "${HEAD_SHA:-}" ]]; then
range="$BASE_SHA..$HEAD_SHA"
else
range="HEAD^..HEAD"
fi

mapfile -t touched < <(
git diff --name-only --diff-filter=AMR "$range" \
| grep -E '^content/(it|en)/.*\.md$' || true
)

if [[ ${#touched[@]} -eq 0 ]]; then
echo "✓ no content/{it,en}/*.md touched in this range — parity OK"
exit 0
fi

extract_key() {
awk '
/^---$/ { if (in_fm) exit; in_fm=1; next }
in_fm && /^translationKey:[[:space:]]*/ {
sub(/^translationKey:[[:space:]]*/, ""); gsub(/["'"'"'`]/, ""); print; exit
}
' "$1"
}

has_key_in() {
local key="$1" lang="$2" f
[[ -d "content/$lang" ]] || return 1
while IFS= read -r f; do
[[ "$(extract_key "$f")" == "$key" ]] && return 0
done < <(find "content/$lang" -type f -name '*.md')
return 1
}

missing=()
for f in "${touched[@]}"; do
key=$(extract_key "$f")
# No translationKey = page declares no pairing yet.
[[ -z "$key" ]] && continue

case "$f" in
content/it/*) other="en" ;;
content/en/*) other="it" ;;
*) continue ;;
esac

has_key_in "$key" "$other" || \
missing+=("$f → translationKey '$key' missing in content/$other/")
done

if [[ ${#missing[@]} -gt 0 ]]; then
echo "::error::i18n parity broken:"
printf ' - %s\n' "${missing[@]}"
exit 1
fi

echo "✓ i18n parity OK (${#touched[@]} touched file(s) verified)"
5 changes: 5 additions & 0 deletions content/en/news/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: "News Archive"
lead: "All the official news and updates from the Azzurra network."
translationKey: section-news
---
39 changes: 39 additions & 0 deletions content/en/news/gamebot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
title: "GameBot has landed on Azzurra!"
date: 2026-05-10
author: "Azzurra Staff"
tags: ["announcement", "bot", "games"]
summary: "Card games, poker, trivia and UNO right inside your channel: GameBot brings tabletop fun to Azzurra."
translationKey: news-gamebot
---

## A new companion in chat 🎲
We're happy to introduce **GameBot**, the new official Azzurra bot dedicated to in-channel games. Built to liven up evenings and recreate that virtual-tabletop atmosphere that's always been part of IRC communities, GameBot brings four timeless classics into our rooms.

## Available games
- 🃏 **Sette e Mezzo**: the classic Italian card game where luck and steady nerves make the difference. Get as close to seven-and-a-half as you can without busting.
- ♠️ **Texas Hold'em**: the world's most popular poker, channel edition. Bluff, raise and all-in with friends — no real bets, but all the ego on the line.
- 🧠 **Trivia**: rapid-fire questions on general knowledge, cinema, music, sports and much more.
- 🎴 **UNO**: the card game that needs no introduction. Draw two, skip a turn, swap colors — and never forget to call "UNO!".

## How to start playing
To get GameBot in your channel, just invite it:

```
/invite GameBot #yourchannel
```

Once it joins, the full list of available games is one command away:

```
!listgames
```

From there, every match is started with the dedicated commands GameBot will explain on the fly.

## Why GameBot
We believe IRC is much more than a chat: it's a place to socialise. GameBot was born to give you one more reason to stay connected, challenge your friends and make new acquaintances around a virtual table. No app to install, no extra accounts: just the commands you already know.

Have fun, and may the best player win!

See you in chat 👋
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions content/news/_index.md → content/it/news/_index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
title: "Archivio News"
lead: "Tutte le notizie e gli aggiornamenti ufficiali della rete Azzurra."
translationKey: section-news
---
1 change: 1 addition & 0 deletions content/news/gamebot.md → content/it/news/gamebot.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ date: 2026-05-10
author: "Staff Azzurra"
tags: ["annuncio", "bot", "giochi"]
summary: "Sfide di carte, poker, quiz e UNO direttamente in canale: GameBot porta i giochi da tavolo su Azzurra."
translationKey: news-gamebot
---

## Una nuova compagnia in chat 🎲
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading
Loading