From fc786e459a51e061a02318047cc3658390b63079 Mon Sep 17 00:00:00 2001 From: monokrome Date: Wed, 24 Sep 2025 06:11:13 +0200 Subject: [PATCH] feat(i18n): Implement internationalization support with Spanish and English locales - Updated Astro configuration to enable server output for i18n functionality. - Added i18n configuration with locales and default locale settings. - Refactored ContactForm component to utilize translations based on the current language. - Enhanced Head component to dynamically set titles and descriptions based on the selected language. - Created LanguagePicker component for language selection and routing. - Updated Navigation component to reflect translated menu items. - Removed old pages and replaced them with new localized versions for both English and Spanish. - Added utility functions for language detection and translation handling. --- astro.config.mjs | 9 +- src/components/ContactForm.tsx | 56 +++++++------ src/components/Head.astro | 25 +++--- src/components/LanguagePicker.astro | 57 +++++++++++++ src/components/Navigation.astro | 50 +++++++++++ src/i18n/react-utils.ts | 13 +++ src/i18n/ui.ts | 107 ++++++++++++++++++++++++ src/i18n/utils.ts | 62 ++++++++++++++ src/layouts/Layout.astro | 18 ++-- src/pages/contacto.astro | 21 ----- src/pages/en/about-us.astro | 32 +++++++ src/pages/en/contact.astro | 24 ++++++ src/pages/en/index.astro | 16 ++++ src/pages/en/social-networks.astro | 45 ++++++++++ src/pages/es/contacto.astro | 24 ++++++ src/pages/es/index.astro | 16 ++++ src/pages/{ => es}/redes-sociales.astro | 86 ++++++++++--------- src/pages/{ => es}/sobre-nosotros.astro | 63 +++++++------- src/pages/index.astro | 13 +-- 19 files changed, 592 insertions(+), 145 deletions(-) create mode 100644 src/components/LanguagePicker.astro create mode 100644 src/components/Navigation.astro create mode 100644 src/i18n/react-utils.ts create mode 100644 src/i18n/ui.ts create mode 100644 src/i18n/utils.ts delete mode 100644 src/pages/contacto.astro create mode 100644 src/pages/en/about-us.astro create mode 100644 src/pages/en/contact.astro create mode 100644 src/pages/en/index.astro create mode 100644 src/pages/en/social-networks.astro create mode 100644 src/pages/es/contacto.astro create mode 100644 src/pages/es/index.astro rename src/pages/{ => es}/redes-sociales.astro (52%) rename src/pages/{ => es}/sobre-nosotros.astro (74%) diff --git a/astro.config.mjs b/astro.config.mjs index 0758b46..6ba1f0d 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -7,10 +7,17 @@ import tailwind from "@astrojs/tailwind"; // https://astro.build/config export default defineConfig({ integrations: [react(), tailwind()], - output: "hybrid", + output: "server", //El output server es necesario para que i18n funcione, para mas info mirar la documentacion del mismo adapter: cloudflare({ }), image: { domains: ["yellowumbrella.dev"], + }, + i18n: { + locales: ["es", "en"], + defaultLocale: "es", + routing: { + prefixDefaultLocale: true + } } }); \ No newline at end of file diff --git a/src/components/ContactForm.tsx b/src/components/ContactForm.tsx index af28291..f05899a 100644 --- a/src/components/ContactForm.tsx +++ b/src/components/ContactForm.tsx @@ -4,6 +4,7 @@ import PhoneInput, { isValidPhoneNumber } from "react-phone-number-input"; import { Turnstile } from "@marsidev/react-turnstile"; import type { TurnstileInstance } from "@marsidev/react-turnstile"; import "react-phone-number-input/style.css"; +import { useTranslationsReact, getLangFromUrlReact } from '../i18n/react-utils'; type FormInputs = { empresa: string; @@ -13,8 +14,15 @@ type FormInputs = { mensaje: string; }; +interface ContactFormProps { + lang?: string; +} -export default function ContactForm() { +export default function ContactForm({ lang }: ContactFormProps = {}) { + // Detectar idioma desde la URL del navegador si no se proporciona + const currentLang = lang || getLangFromUrlReact(window.location.pathname); + const t = useTranslationsReact(currentLang as 'es' | 'en'); + const { register, handleSubmit, @@ -42,7 +50,7 @@ export default function ContactForm() { } if (!token) { - setStatus({ type: "error", message: "Por favor, completa el captcha antes de enviar" }); + setStatus({ type: "error", message: t('contact.form.captcha.required') }); return; } @@ -65,7 +73,7 @@ export default function ContactForm() { if (!res.ok) { const text = await res.text(); - throw new Error(text || "Error al enviar el formulario"); + throw new Error(text || t('contact.form.error')); } setStatus({ type: "success" }); @@ -75,7 +83,7 @@ export default function ContactForm() { // Reset Turnstile widget using ref turnstileRef.current?.reset(); } catch (err: unknown) { - setStatus({ type: "error", message: (err instanceof Error ? err.message : String(err)) || "Error inesperado" }); + setStatus({ type: "error", message: (err instanceof Error ? err.message : String(err)) || t('contact.form.error') }); } }; @@ -89,72 +97,72 @@ export default function ContactForm() { >
{errors.empresa && ( -

Este campo es obligatorio

+

{t('contact.form.company.required')}

)}
{errors.nombreCompleto && ( -

Introduce tu nombre completo

+

{t('contact.form.name.required')}

)}
{errors.email && ( -

Introduce un email válido

+

{t('contact.form.email.required')}

)}
- +
{telefono && typeof telefono === "string" && !isValidPhoneNumber(telefono) && (

- El número puede no ser válido. Revisa el país y formato. + {t('contact.form.phone.invalid')}

)}