From c08f53ca6914df28435e8ce300de8d820017950f Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Tue, 25 Aug 2026 14:23:09 +0200 Subject: [PATCH] Add a language switcher with English, Italian, Spanish and French MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interface is translated through @nuxtjs/i18n: strings live in i18n/locales/*.json, routes are prefixed except for the default English, and useLocaleHead emits the lang attribute, the hreflang alternates and og:locale. The switcher is a native select rather than a custom dropdown: keyboard support, the platform picker on mobile and correct labelling come for free. Article content stays single-language — this translates the site chrome only. --- CLAUDE.md | 5 + docker-compose.dev.yml | 1 + docker-compose.yml | 2 + frontend/app/app.vue | 5 + frontend/app/components/ArticleCard.vue | 16 +- frontend/app/components/LanguageSwitcher.vue | 71 + frontend/app/error.vue | 11 +- frontend/app/layouts/default.vue | 67 +- frontend/app/pages/blog/[slug].vue | 15 +- frontend/app/pages/blog/index.vue | 29 +- frontend/app/pages/category/[slug].vue | 22 +- frontend/app/pages/index.vue | 39 +- frontend/i18n/locales/en.json | 50 + frontend/i18n/locales/es.json | 50 + frontend/i18n/locales/fr.json | 50 + frontend/i18n/locales/it.json | 50 + frontend/nuxt.config.ts | 24 +- frontend/package-lock.json | 2767 +++++++++++++++++- frontend/package.json | 1 + frontend/shared/utils/format.ts | 4 +- frontend/shared/utils/site.ts | 5 +- 21 files changed, 3197 insertions(+), 87 deletions(-) create mode 100644 frontend/app/components/LanguageSwitcher.vue create mode 100644 frontend/i18n/locales/en.json create mode 100644 frontend/i18n/locales/es.json create mode 100644 frontend/i18n/locales/fr.json create mode 100644 frontend/i18n/locales/it.json diff --git a/CLAUDE.md b/CLAUDE.md index a57ff76..87a277a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,6 +29,11 @@ Browser → Caddy ─┬─ dominio pubblico → Nuxt 4 (SSR) → REST Strapi Se aggiungi una vista, aggiungi l'endpoint lì e tipizza il ritorno in `shared/types/blog.ts`. - Il Markdown dell'articolo è convertito in HTML **nell'endpoint**, non nel componente: il contenuto è già nell'HTML SSR e `marked` resta fuori dal bundle client. +- L'**interfaccia** è tradotta in en/it/es/fr con `@nuxtjs/i18n`: stringhe in + `frontend/i18n/locales/*.json`, rotte `prefix_except_default` (inglese senza prefisso), + `hreflang` e `og:locale` generati da `useLocaleHead()` in `app.vue`. Nessuna stringa + visibile va scritta a mano in un componente: aggiungi la chiave nei quattro file. + I **contenuti** invece restano monolingua — la localizzazione di Strapi è disattivata. - `cms/src/index.ts` (`bootstrap`) dà al ruolo Public solo `find`/`findOne` su Article e Category, e disattiva la registrazione pubblica: non esistono utenti front-end, solo amministratori. Qualsiasi permesso in più va motivato. diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 53b5b03..d450a12 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -18,3 +18,4 @@ services: environment: NUXT_PUBLIC_SITE_URL: http://localhost:3000 NUXT_PUBLIC_STRAPI_URL: http://localhost:1337 + NUXT_PUBLIC_I18N_BASE_URL: http://localhost:3000 diff --git a/docker-compose.yml b/docker-compose.yml index cb680b4..56fe666 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,6 +52,8 @@ services: NUXT_STRAPI_URL: ${STRAPI_URL} NUXT_PUBLIC_SITE_URL: ${PUBLIC_SITE_URL} NUXT_PUBLIC_STRAPI_URL: ${PUBLIC_STRAPI_URL} + # Absolute origin for the alternate-language links. + NUXT_PUBLIC_I18N_BASE_URL: ${PUBLIC_SITE_URL} caddy: image: caddy:2-alpine diff --git a/frontend/app/app.vue b/frontend/app/app.vue index ed9e94c..e7aa252 100644 --- a/frontend/app/app.vue +++ b/frontend/app/app.vue @@ -1,3 +1,8 @@ + +