feat(i18n): add Spanish (es) locale (#510)

Contributed by @AloePacci on issue #510. Drops their es.json into the
existing locale set, registers Spanish in the language selector with a
flag SVG matching the inline style of the other six locales, and
extends the email pipeline so es-language guests receive a localised
email subject/body where available.

Coverage:
- frontend/src/i18n/locales/es.json — 2132 translated keys. ~824
  EN keys are not yet covered; i18next's `fallbackLng: 'en'` handles
  those at runtime so the UI never renders a missing key. fr/nl/pt/ru
  have a similar (smaller) gap and ship the same way.
- LanguageSelector.tsx — added the ESFlag inline SVG (red/yellow/red
  horizontal bands, official #AA151B + #F1BF00; no coat of arms to
  stay consistent with the other simple flag components) and a new
  entry in SUPPORTED_LANGUAGES.
- emailProcessor.js — added .es to the domain-language heuristic, and
  an `es:` row to the three inline-translated snippets
  (passwordSecurityI18n / noPasswordI18n / passwordSetAtCreationI18n).
- 106_seed_es_email_template_translations.js (new) — idempotent
  seeder for the four customer-facing templates AloePacci translated:
  gallery_created, expiration_warning, gallery_expired, archive_complete.
  Mirrors the pattern from 099. Template keys without an `es` row fall
  back to `en` via the existing resolution chain in
  emailProcessor.processTemplate — no functional gap, just untranslated
  copy until someone fills them in.

What I deliberately did NOT take from the contribution: the proposed
in-place edit of migration 075 (history mutation — won't reseed for
existing installs anyway) and the whitespace/`gallery_list_html`-drop
churn in emailProcessor.js (would have regressed the #354 follow-up).
The semantic additions from those files are preserved via 106 and the
targeted edits above.
This commit is contained in:
Paul Nothaft
2026-05-17 00:50:51 +02:00
parent 98f3c3df41
commit 061712ebf1
4 changed files with 2606 additions and 0 deletions
@@ -54,6 +54,14 @@ const FRFlag: React.FC<{ className?: string }> = ({ className = "w-5 h-5" }) =>
</svg>
);
const ESFlag: React.FC<{ className?: string }> = ({ className = "w-5 h-5" }) => (
<svg className={className} viewBox="0 0 640 480" xmlns="http://www.w3.org/2000/svg">
<path fill="#AA151B" d="M0 0h640v120H0z"/>
<path fill="#F1BF00" d="M0 120h640v240H0z"/>
<path fill="#AA151B" d="M0 360h640v120H0z"/>
</svg>
);
export const SUPPORTED_LANGUAGES = [
{ code: 'en', name: 'English', Flag: GBFlag },
{ code: 'de', name: 'Deutsch', Flag: DEFlag },
@@ -61,6 +69,7 @@ export const SUPPORTED_LANGUAGES = [
{ code: 'pt', name: 'Português', Flag: PTBRFlag },
{ code: 'nl', name: 'Nederlands', Flag: NLFlag },
{ code: 'fr', name: 'Français', Flag: FRFlag },
{ code: 'es', name: 'Español', Flag: ESFlag },
];
export const LanguageSelector: React.FC = () => {