Skip to content

Conversation

@nixvy-13
Copy link
Member

@nixvy-13 nixvy-13 commented Sep 24, 2025

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.

Summary by CodeRabbit

  • New Features

    • Added full internationalization: Spanish (default) and English across pages, navigation, meta tags, and content.
    • Introduced a language picker with locale-aware URLs.
    • Localized Contact Form with language-specific placeholders, validation messages, and captcha language.
    • Added new localized pages: /es (index, contacto, redes-sociales, sobre-nosotros) and /en (index, contact, social-networks, about-us).
  • Changes

    • Root path now redirects to /es.
    • Replaced legacy standalone contacto page with localized routes.
  • Chores

    • Updated app configuration to support server rendering and i18n routing.

…nglish 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.
Copilot AI review requested due to automatic review settings September 24, 2025 04:12
@coderabbitai
Copy link

coderabbitai bot commented Sep 24, 2025

Walkthrough

Enabled site-wide i18n (es/en) with locale-prefixed routing, switched Astro output to server, localized layout/meta, header, navigation, and contact form. Added LanguagePicker and Navigation components. Created localized ES/EN pages, redirected root to /es, and removed the old Spanish contact page. Introduced shared i18n utilities for Astro and React.

Changes

Cohort / File(s) Summary of changes
Astro config
astro.config.mjs
Set output: "server". Added i18n with locales ["es","en"], defaultLocale: "es", and routing.prefixDefaultLocale: true.
i18n core utilities
src/i18n/ui.ts, src/i18n/utils.ts, src/i18n/react-utils.ts
Added language maps, default locale, route translations, and UI strings. Introduced helpers: getLangFromUrl/useTranslations/useTranslatedPath/getRouteFromUrl for Astro; React counterparts for translations and URL lang extraction.
Layout and head localization
src/layouts/Layout.astro, src/components/Head.astro
Detect lang from URL and use translations for titles/meta and nav labels. Inject LanguagePicker; localize links via getRelativeLocaleUrl.
New UI components
src/components/LanguagePicker.astro, src/components/Navigation.astro
Added fixed language switcher with route translation and client navigation. Added locale-aware vertical navigation with translated labels and links.
Contact form i18n
src/components/ContactForm.tsx
Added ContactFormProps { lang?: string }. Localized all strings via React i18n helpers; lang from prop or URL. Synced Turnstile language and phone input defaults with selected lang.
Spanish pages (ES)
src/pages/es/index.astro, src/pages/es/contacto.astro, src/pages/es/redes-sociales.astro, src/pages/es/sobre-nosotros.astro
Added/updated ES pages to derive lang from URL, use translations, and render localized titles/content.
English pages (EN)
src/pages/en/index.astro, src/pages/en/contact.astro, src/pages/en/social-networks.astro, src/pages/en/about-us.astro
Added EN pages with localized titles/content, locale-aware routing, and i18n usage. social-networks sets prerender = false.
Root routing and cleanup
src/pages/index.astro, src/pages/contacto.astro
Root page now immediately Astro.redirect('/es/'). Removed legacy src/pages/contacto.astro in favor of localized routes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Browser
  participant Router as Astro Router
  participant i18n as i18n/utils
  participant UI as Layout/Components

  User->>Browser: Request /en/... or /es/...
  Browser->>Router: GET /{lang}/{route}
  Router->>i18n: getLangFromUrl(URL)
  i18n-->>Router: lang
  Router->>UI: Render with lang
  UI->>i18n: useTranslations(lang)
  i18n-->>UI: t(key)
  UI-->>Browser: Localized HTML (titles, nav, content)

  Note over UI,i18n: Links built with getRelativeLocaleUrl / useTranslatedPath
Loading
sequenceDiagram
  autonumber
  actor User
  participant Browser
  participant Router as Astro Router

  User->>Browser: Request /
  Browser->>Router: GET /
  Router-->>Browser: 302 Redirect to /es/
  Browser->>Router: GET /es/
  Router-->>Browser: 200 Localized page
Loading
sequenceDiagram
  autonumber
  actor User
  participant Picker as LanguagePicker
  participant i18n as i18n/utils
  participant Router as Browser Location

  User->>Picker: Change <select> to target lang
  Picker->>i18n: translatePath(currentRoute, targetLang)
  i18n-->>Picker: /{targetLang}/{translatedRoute}
  Picker->>Router: window.location = translated URL
  Router-->>User: Navigated to localized page
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I hop between “es” and “en” with glee,
A bilingual breeze beneath my tree.
New routes sprout up, old ones hop away,
Forms now chirp in what you say.
With tails of links that neatly bend—
¡Hola! Hello!—from start to end. 🐇🌐

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The PR title "YU_10" is an opaque identifier and does not describe the primary work in this changeset; it gives no context about the added i18n support, config change, or page/component refactors. Because it is not descriptive, a reviewer or teammate cannot quickly understand the main change from the title alone. Rename the pull request to a concise, descriptive sentence that highlights the main change, for example: "Add i18n support (es/en), update astro.config and localize pages/components". Keep it short and specific so reviewers can understand the primary scope at a glance.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/YU_10/idiomas

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
yellowumbrella-web fc786e4 Commit Preview URL

Branch Preview URL
Sep 24 2025, 04:12 AM

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements internationalization (i18n) support for the Yellow Umbrella website, enabling Spanish and English locales with proper routing and content translation.

  • Restructured the site to use language-prefixed routes (e.g., /es/, /en/)
  • Created comprehensive translation system with utilities for both Astro and React components
  • Added language switching functionality with automatic route translation

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
astro.config.mjs Configured i18n settings and changed output to server mode
src/pages/index.astro Converted to redirect page for default language routing
src/pages/es/* Created Spanish localized pages with translation integration
src/pages/en/* Created English localized pages with translation integration
src/i18n/ui.ts Defined translation keys and route mappings for both languages
src/i18n/utils.ts Implemented language detection and translation utilities
src/i18n/react-utils.ts Created React-specific translation utilities
src/components/* Updated components to use translations and language-aware routing
src/layouts/Layout.astro Integrated language picker and dynamic meta tags

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

---
import Layout from "../layouts/Layout.astro";
import Head from "../components/Head.astro";
//Esta pagina es necesaria para que i18n funcione.
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment contains a grammatical error. It should be 'Esta página es necesaria para que i18n funcione.' (missing accent on 'página').

Suggested change
//Esta pagina es necesaria para que i18n funcione.
//Esta página es necesaria para que i18n funcione.

Copilot uses AI. Check for mistakes.
import Layout from "../layouts/Layout.astro";
import Head from "../components/Head.astro";
//Esta pagina es necesaria para que i18n funcione.
// Redirección automática al idioma predeterminado (español)
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment contains a spelling error. It should be 'Redirección automática al idioma predeterminado (español)' (missing accent on 'Redirección').

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +25
const currentLang = lang || getLangFromUrlReact(window.location.pathname);
const t = useTranslationsReact(currentLang as 'es' | 'en');

Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessing window.location.pathname during server-side rendering will cause an error since window is undefined on the server. This should be handled with proper client-side detection or the lang prop should be required.

Suggested change
const currentLang = lang || getLangFromUrlReact(window.location.pathname);
const t = useTranslationsReact(currentLang as 'es' | 'en');
const [currentLang, setCurrentLang] = React.useState<string>(lang || 'es');
React.useEffect(() => {
if (!lang && typeof window !== 'undefined') {
setCurrentLang(getLangFromUrlReact(window.location.pathname));
}
}, [lang]);
const t = useTranslationsReact(currentLang as 'es' | 'en');

Copilot uses AI. Check for mistakes.
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
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment contains spelling errors. It should be 'más' (with accent) and 'documentación' (with accent).

Suggested change
output: "server", //El output server es necesario para que i18n funcione, para mas info mirar la documentacion del mismo
output: "server", //El output server es necesario para que i18n funcione, para más info mirar la documentación del mismo

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (17)
src/i18n/ui.ts (3)

24-35: Spanish strings contain untranslated English; fix the ES copy.

Translate the ES values for better UX.

Apply:

-    'site.description': 'The Yellow Umbrella Website ☂️',
+    'site.description': 'Sitio web de Yellow Umbrella ☂️',
@@
-    'home.subtitle': 'It\'s raining outside, take this',
+    'home.subtitle': 'Está lloviendo fuera, toma esto',

6-7: Remove unused showDefaultLang or wire it in.

It’s defined but unused and could confuse future readers, especially since config uses prefixDefaultLocale: true.

-export const showDefaultLang = false;
+// If needed, consume this flag where routes are generated; otherwise, remove:
+// export const showDefaultLang = false;

9-20: Add const assertion to routes for stronger typing.

Helps catch key typos across utilities and consumers.

-export const routes = {
+export const routes = {
   en: {
     'sobre-nosotros': 'about-us',
     'contacto': 'contact',
     'redes-sociales': 'social-networks',
   },
   es: {
     'about-us': 'sobre-nosotros',
     'contact': 'contacto',
     'social-networks': 'redes-sociales',
   },
-};
+} as const;
src/i18n/react-utils.ts (1)

3-7: Avoid false fallback when a translation is an empty string. Use nullish coalescing.

|| treats empty strings as falsy. Prefer ??.

 export function useTranslationsReact(lang: keyof typeof ui) {
   return function t(key: keyof typeof ui[typeof defaultLang]) {
-    return ui[lang][key] || ui[defaultLang][key];
+    return ui[lang][key] ?? ui[defaultLang][key];
   }
 }
src/pages/en/index.astro (1)

12-14: Remove empty wrapper div.

Slight cleanup; no behavior change.

-    <div class="">
-      <Head/>
-    </div>
+    <Head />
src/pages/index.astro (1)

2-4: Derive redirect target from default locale to avoid drift.

Keeps the redirect aligned with config if default locale changes.

-// Redirección automática al idioma predeterminado (español)
-return Astro.redirect('/es/');
+// Redirección automática al idioma predeterminado
+import { defaultLang } from '../i18n/ui';
+return Astro.redirect(`/${defaultLang}/`);
src/pages/es/sobre-nosotros.astro (1)

17-29: Fix minor Spanish grammar and spelling.

“Para ti” (no tilde) and “paraguas” (sin diéresis).

-            <span class="text-justify text-white font-monaspace">
+            <span class="text-justify text-white font-mono">
                 Yellow Umbrella significa muchas cosas. Para tí, si eres el dueño 
                 de un negocio emergente, significa resguardo y cobertura de todas tus necesidades 
                 de cloud, ciberseguridad, y desarrollo.<br><br>
@@
                 Nuestros valores son claros: Estamos comprometidos con el
                 código abierto y la accesibilidad a nuestros servicios y proyectos. Nuestro futuro juntos no implica dependencia. 
-                Buscamos cubrirte y entrenar a tu equipo para que llegado el momento, puedas cerrar el paragüas 
+                Buscamos cubrirte y entrenar a tu equipo para que llegado el momento, puedas cerrar el paraguas 
                 y continuar tu camino sin nosotros.

Confirm font-mono exists in your Tailwind config, or keep the original if you have a custom font-monaspace utility.

src/pages/en/contact.astro (1)

21-21: Defer React hydration for better UX.

Use client:idle (or client:visible) instead of client:load to reduce main-thread contention on page load.

-      <ContactForm client:load lang={lang} />
+      <ContactForm client:idle lang={lang} />

If the form must be usable immediately above the fold, keep client:load; otherwise prefer idle or visible.

src/pages/es/contacto.astro (1)

21-21: Defer React hydration for better UX.

Mirror the EN page suggestion.

-      <ContactForm client:load lang={lang} />
+      <ContactForm client:idle lang={lang} />
src/components/Navigation.astro (2)

28-50: Fix invalid Tailwind utilities and remove redundant inline CSS

  • Classes top-20px, left-20px, z-1000 aren’t valid Tailwind. Use arbitrary values.
  • Inline <style> duplicates/conflicts the position/z-index.

Apply this diff:

-<nav class="fixed top-20px left-20px z-1000 bg-white/5 backdrop-blur-md rounded-lg p-4">
+<nav class="fixed top-[20px] left-[20px] z-[1000] bg-white/5 backdrop-blur-md rounded-lg p-4">
@@
-<style>
-  nav {
-    position: fixed;
-    top: 20px;
-    left: 20px;
-    z-index: 999;
-  }
-</style>

8-25: Avoid hardcoding per-lang paths in menuItems

You already have route translation utilities; consider generating paths via a single source of truth to avoid drift.

src/pages/es/redes-sociales.astro (1)

33-41: Guard against unknown icons in data

If data.socials contains an unexpected icon key, SocialCard will receive undefined. Add a safe fallback:

-                    icon={iconMap[output.icon]}
+                    icon={iconMap[output.icon] ?? twitter}
src/pages/en/social-networks.astro (2)

33-41: Add fallback for missing/unknown icons

Mirror the ES page safeguard:

-                    icon={iconMap[output.icon]}
+                    icon={iconMap[output.icon] ?? twitter}

14-20: Nit: keep comments in English within en/ pages

Change “Crear mapeo de iconos” to an English comment for consistency.

src/components/LanguagePicker.astro (1)

13-31: Accessibility and cleanup nits

  • Add an accessible label to the select (e.g., aria-label) using your translations.
  • getRelativeLocaleUrl import is unused; remove.

Example:

-  <select 
+  <select
+    aria-label="Change language"
src/components/Head.astro (1)

16-17: Head renders home content on all pages that include it

If Head is intended as a generic header, consider accepting children/props to customize or hide the hero (subtitle/title) so subpages don’t duplicate headings.

src/i18n/utils.ts (1)

15-39: Route translation utility looks solid

Handles root, leading slashes, and route mapping both ways. Consider documenting that only single-segment routes are supported today.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 991c0db and fc786e4.

📒 Files selected for processing (19)
  • astro.config.mjs (1 hunks)
  • src/components/ContactForm.tsx (9 hunks)
  • src/components/Head.astro (1 hunks)
  • src/components/LanguagePicker.astro (1 hunks)
  • src/components/Navigation.astro (1 hunks)
  • src/i18n/react-utils.ts (1 hunks)
  • src/i18n/ui.ts (1 hunks)
  • src/i18n/utils.ts (1 hunks)
  • src/layouts/Layout.astro (2 hunks)
  • src/pages/contacto.astro (0 hunks)
  • src/pages/en/about-us.astro (1 hunks)
  • src/pages/en/contact.astro (1 hunks)
  • src/pages/en/index.astro (1 hunks)
  • src/pages/en/social-networks.astro (1 hunks)
  • src/pages/es/contacto.astro (1 hunks)
  • src/pages/es/index.astro (1 hunks)
  • src/pages/es/redes-sociales.astro (1 hunks)
  • src/pages/es/sobre-nosotros.astro (1 hunks)
  • src/pages/index.astro (1 hunks)
💤 Files with no reviewable changes (1)
  • src/pages/contacto.astro
🧰 Additional context used
🧬 Code graph analysis (3)
src/i18n/react-utils.ts (1)
src/i18n/ui.ts (2)
  • ui (22-107)
  • defaultLang (6-6)
src/i18n/utils.ts (1)
src/i18n/ui.ts (3)
  • ui (22-107)
  • defaultLang (6-6)
  • routes (9-20)
src/components/ContactForm.tsx (1)
src/i18n/react-utils.ts (2)
  • getLangFromUrlReact (9-13)
  • useTranslationsReact (3-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Workers Builds: yellowumbrella-web
🔇 Additional comments (22)
astro.config.mjs (2)

10-12: Cloudflare SSR alignment looks good.

output: "server" with @astrojs/cloudflare matches the SSR requirement for Workers. No issues spotted.

If you previously relied on static generation, verify there are no implicit assumptions (e.g., file-based redirects) that break under SSR-only deployment.


16-22: Astro i18n config is valid — requires Astro >= 3.7.0

i18n.locales and i18n.defaultLocale were added in Astro 3.5.0; i18n.routing.prefixDefaultLocale was added in 3.7.0. If the project uses Astro >= 3.7.0, this config is supported.

src/pages/es/index.astro (1)

10-16: LGTM for the ES home page scaffold

Uses i18n helpers correctly and wires Layout/Head as intended.

src/pages/en/about-us.astro (1)

10-32: Confirm Head usage on subpages

Including renders the home hero (home.subtitle/home.title). On About this will create a second H1 and duplicate hero content. If unintentional, remove here or make Head accept a “variant” prop to hide the hero on non-home pages.

src/i18n/utils.ts (2)

3-7: LGTM: robust lang derivation with sane fallback

getLangFromUrl handles missing/unknown prefixes and falls back to defaultLang.


41-62: Note on getRouteFromUrl limitations

It returns only the last segment; multi-segment pages would not translate fully. Fine for current pages; revisit if you add nested routes.

src/pages/en/social-networks.astro (1)

5-21: No action required — socials.json icon keys match iconMap

Verified src/api/socials.json contains icons github-circle, linkedin, twitter — all are covered by the iconMap in src/pages/en/social-networks.astro.

src/pages/es/redes-sociales.astro (1)

5-21: socials.json shape and icon keys — no action required

src/api/socials.json has a top-level "socials" array (3 items) and the only icon values are: twitter, github-circle, linkedin.

src/layouts/Layout.astro (5)

10-11: LGTM!

The imports are correctly structured and follow proper i18n implementation patterns.


13-14: LGTM!

The language detection and translation initialization follows best practices for Astro i18n implementation.


18-18: LGTM!

Setting the lang attribute dynamically based on the detected language is essential for proper SEO and accessibility.


32-32: LGTM!

The translation-driven meta tags implementation is correct and ensures proper localization of SEO elements including description, Open Graph, and Twitter Card metadata.

Also applies to: 38-42, 47-48


54-54: LGTM!

Adding the LanguagePicker component to provide users with language selection functionality is appropriate for the i18n implementation.

src/components/ContactForm.tsx (9)

7-7: LGTM!

The import statement correctly brings in the React-specific i18n utilities for client-side translation handling.


17-24: LGTM!

The interface definition and language detection logic is well-structured. The fallback to URL-based detection when no lang prop is provided ensures robust language handling.


53-53: LGTM!

Error messages are properly localized using the translation system, ensuring consistent user experience across languages.

Also applies to: 76-76, 86-86


100-100: LGTM!

Form labels, placeholders, and validation error messages are properly internationalized, ensuring a fully localized user experience.

Also applies to: 105-105, 109-109, 115-115, 120-120, 124-124, 130-130, 135-135, 142-142, 148-148, 155-155


160-165: LGTM!

The phone input configuration correctly adapts the default country based on the selected language, providing a better user experience by setting appropriate defaults.


175-175: LGTM!

Phone validation messages and form field labels are properly localized.

Also applies to: 182-182, 188-188


199-199: LGTM!

The Turnstile language configuration correctly uses the detected language, ensuring the CAPTCHA appears in the appropriate language for better user accessibility.


223-223: LGTM!

Button text and success messages are properly internationalized for consistent user experience.

Also applies to: 229-229


23-23: No SSR issue — ContactForm is client-only and receives lang prop

Both src/pages/es/contacto.astro and src/pages/en/contact.astro render ContactForm with client:load and pass lang, so window access happens only in the browser; no change required.

Comment on lines +20 to 47
<a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'sobre-nosotros' : 'about-us')}>
<button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-lg lg:text-base md:text-sm sm:text-xs text-center leading-tight">
¿Quiénes somos?
{t('home.nav.about')}
</span>
</button>
</a>
<a href="https://blog.yellowumbrella.dev" target="_blank" rel="noreferrer">
<button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-xl lg:text-lg md:text-base sm:text-sm text-center leading-tight">
Blog
{t('home.nav.blog')}
</span>
</button>
</a>
<a href="/redes-sociales">
<a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'redes-sociales' : 'social-networks')}>
<button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-lg lg:text-base md:text-sm sm:text-xs text-center leading-tight">
Redes sociales
{t('home.nav.social')}
</span>
</button>
</a>
<a href="/contacto">
<a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'contacto' : 'contact')}>
<button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-xl lg:text-lg md:text-base sm:text-sm text-center leading-tight">
Contacto
{t('home.nav.contact')}
</span>
</button>
</a>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid nested interactive elements (button inside anchor)

An anchor containing a button is invalid and harms accessibility. Style the anchor as a button instead.

Apply this diff:

-            <a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'sobre-nosotros' : 'about-us')}>
-                <button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2  mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
-                    <span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-lg lg:text-base md:text-sm sm:text-xs text-center leading-tight">
+            <a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'sobre-nosotros' : 'about-us')}
+               class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
+                    <span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-lg lg:text-base md:text-sm sm:text-xs text-center leading-tight">
                         {t('home.nav.about')}
                     </span>
-                </button>
             </a>
-            <a href="https://blog.yellowumbrella.dev" target="_blank" rel="noreferrer">
-                <button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
-                    <span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-xl lg:text-lg md:text-base sm:text-sm text-center leading-tight">
+            <a href="https://blog.yellowumbrella.dev" target="_blank" rel="noopener noreferrer"
+               class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
+                    <span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-xl lg:text-lg md:text-base sm:text-sm text-center leading-tight">
                         {t('home.nav.blog')}
                     </span>
-                </button>
             </a>
-            <a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'redes-sociales' : 'social-networks')}>
-                <button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
-                    <span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-lg lg:text-base md:text-sm sm:text-xs text-center leading-tight">
+            <a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'redes-sociales' : 'social-networks')}
+               class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
+                    <span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-lg lg:text-base md:text-sm sm:text-xs text-center leading-tight">
                         {t('home.nav.social')}
                     </span>
-                </button>
             </a>
-            <a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'contacto' : 'contact')}>
-                <button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
-                    <span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-xl lg:text-lg md:text-base sm:text-sm text-center leading-tight">
+            <a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'contacto' : 'contact')}
+               class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
+                    <span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-xl lg:text-lg md:text-base sm:text-sm text-center leading-tight">
                         {t('home.nav.contact')}
                     </span>
-                </button>
             </a>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'sobre-nosotros' : 'about-us')}>
<button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-lg lg:text-base md:text-sm sm:text-xs text-center leading-tight">
¿Quiénes somos?
{t('home.nav.about')}
</span>
</button>
</a>
<a href="https://blog.yellowumbrella.dev" target="_blank" rel="noreferrer">
<button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-xl lg:text-lg md:text-base sm:text-sm text-center leading-tight">
Blog
{t('home.nav.blog')}
</span>
</button>
</a>
<a href="/redes-sociales">
<a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'redes-sociales' : 'social-networks')}>
<button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-lg lg:text-base md:text-sm sm:text-xs text-center leading-tight">
Redes sociales
{t('home.nav.social')}
</span>
</button>
</a>
<a href="/contacto">
<a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'contacto' : 'contact')}>
<button class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-xl lg:text-lg md:text-base sm:text-sm text-center leading-tight">
Contacto
{t('home.nav.contact')}
</span>
</button>
</a>
<a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'sobre-nosotros' : 'about-us')}
class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-lg lg:text-base md:text-sm sm:text-xs text-center leading-tight">
{t('home.nav.about')}
</span>
</a>
<a href="https://blog.yellowumbrella.dev" target="_blank" rel="noopener noreferrer"
class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-xl lg:text-lg md:text-base sm:text-sm text-center leading-tight">
{t('home.nav.blog')}
</span>
</a>
<a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'redes-sociales' : 'social-networks')}
class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-lg lg:text-base md:text-sm sm:text-xs text-center leading-tight">
{t('home.nav.social')}
</span>
</a>
<a href={getRelativeLocaleUrl(lang, lang === 'es' ? 'contacto' : 'contact')}
class="relative inline-flex items-center justify-center w-32 h-16 p-[2px] mb-2 mr-4 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-[#ffd300] to-[#773376] group-hover:from-[#ffd300] group-hover:to-[#773376] hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-[#773376] dark:focus:ring-[#773376]">
<span class="relative w-full h-full px-2 py-1 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-[6px] group-hover:bg-opacity-0 flex items-center justify-center text-xl lg:text-lg md:text-base sm:text-sm text-center leading-tight">
{t('home.nav.contact')}
</span>
</a>
🤖 Prompt for AI Agents
In src/components/Head.astro lines 20-47: the markup nests <button> inside <a>,
which is invalid and inaccessible; remove the inner <button> elements and move
their classes/attributes to the parent <a> so each anchor is styled like a
button (keep href, target and rel for external links), preserve the inner <span>
content and classes, and ensure the anchor retains the focus/hover styles (add
any focus-related attributes to the <a> as needed) so anchors remain
keyboard-focusable and visually identical to the previous button styling.

Comment on lines +33 to +57
<script>
function initLanguagePicker() {
const select = document.getElementById('language-select') as HTMLSelectElement;

if (select) {
// Remover event listeners previos para evitar duplicados
const newSelect = select.cloneNode(true) as HTMLSelectElement;
select.parentNode?.replaceChild(newSelect, select);

newSelect.addEventListener('change', function() {
const selectedOption = this.options[this.selectedIndex];
const url = selectedOption.getAttribute('data-url');
if (url) {
window.location.href = url;
}
});
}
}

// Inicializar cuando se carga la página
document.addEventListener('DOMContentLoaded', initLanguagePicker);

// Reinicializar después de cada transición de página de Astro
document.addEventListener('astro:page-load', initLanguagePicker);
</script>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove TypeScript from browser script to avoid runtime errors

The script tag runs in the browser; TS assertions (as HTMLSelectElement) will break. Use plain JS and narrow via instanceof.

Apply this diff:

 <script>
-  function initLanguagePicker() {
-    const select = document.getElementById('language-select') as HTMLSelectElement;
-    
-    if (select) {
-      // Remover event listeners previos para evitar duplicados
-      const newSelect = select.cloneNode(true) as HTMLSelectElement;
-      select.parentNode?.replaceChild(newSelect, select);
-      
-      newSelect.addEventListener('change', function() {
-        const selectedOption = this.options[this.selectedIndex];
-        const url = selectedOption.getAttribute('data-url');
-        if (url) {
-          window.location.href = url;
-        }
-      });
-    }
-  }
-  
-  // Inicializar cuando se carga la página
-  document.addEventListener('DOMContentLoaded', initLanguagePicker);
-  
-  // Reinicializar después de cada transición de página de Astro
-  document.addEventListener('astro:page-load', initLanguagePicker);
+  function initLanguagePicker() {
+    const select = document.getElementById('language-select');
+    if (select && select instanceof HTMLSelectElement) {
+      // Remove previous listeners by cloning
+      const newSelect = select.cloneNode(true);
+      select.parentNode?.replaceChild(newSelect, select);
+      newSelect.addEventListener('change', (e) => {
+        const target = e.currentTarget;
+        if (target && target instanceof HTMLSelectElement) {
+          const option = target.options[target.selectedIndex];
+          const url = option.getAttribute('data-url');
+          if (url) window.location.assign(url);
+        }
+      });
+    }
+  }
+  document.addEventListener('DOMContentLoaded', initLanguagePicker);
+  document.addEventListener('astro:page-load', initLanguagePicker);
 </script>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<script>
function initLanguagePicker() {
const select = document.getElementById('language-select') as HTMLSelectElement;
if (select) {
// Remover event listeners previos para evitar duplicados
const newSelect = select.cloneNode(true) as HTMLSelectElement;
select.parentNode?.replaceChild(newSelect, select);
newSelect.addEventListener('change', function() {
const selectedOption = this.options[this.selectedIndex];
const url = selectedOption.getAttribute('data-url');
if (url) {
window.location.href = url;
}
});
}
}
// Inicializar cuando se carga la página
document.addEventListener('DOMContentLoaded', initLanguagePicker);
// Reinicializar después de cada transición de página de Astro
document.addEventListener('astro:page-load', initLanguagePicker);
</script>
<script>
function initLanguagePicker() {
const select = document.getElementById('language-select');
if (select && select instanceof HTMLSelectElement) {
// Remove previous listeners by cloning
const newSelect = select.cloneNode(true);
select.parentNode?.replaceChild(newSelect, select);
newSelect.addEventListener('change', (e) => {
const target = e.currentTarget;
if (target && target instanceof HTMLSelectElement) {
const option = target.options[target.selectedIndex];
const url = option.getAttribute('data-url');
if (url) window.location.assign(url);
}
});
}
}
document.addEventListener('DOMContentLoaded', initLanguagePicker);
document.addEventListener('astro:page-load', initLanguagePicker);
</script>
🤖 Prompt for AI Agents
In src/components/LanguagePicker.astro around lines 33 to 57, the inline script
uses TypeScript-only syntax (type assertions like "as HTMLSelectElement") which
will cause runtime errors in the browser; remove all TypeScript syntax and use
plain JavaScript: get the element with document.getElementById, check it with
instanceof HTMLSelectElement before using it, avoid TS casts on cloneNode (just
treat the result as an Element and guard with instanceof), and inside the change
handler reference the element via the captured newSelect variable or
event.target (narrowed with instanceof) to read options and data-url; keep the
DOMContentLoaded and astro:page-load listeners but ensure the script contains
only valid JS and proper instanceof checks.

Comment on lines +9 to +13
export function getLangFromUrlReact(url: string) {
const [, lang] = url.split('/');
if (lang in ui) return lang as keyof typeof ui;
return defaultLang;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Make URL language extraction robust for absolute URLs.

url.split('/') breaks for absolute URLs (https://…). Parse pathname first.

-export function getLangFromUrlReact(url: string) {
-  const [, lang] = url.split('/');
-  if (lang in ui) return lang as keyof typeof ui;
-  return defaultLang;
-}
+export function getLangFromUrlReact(url: string) {
+  let pathname = url;
+  try {
+    // If absolute URL, extract pathname; otherwise assume it's already a path
+    if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) {
+      pathname = new URL(url).pathname;
+    }
+  } catch {
+    // noop; fallback to given string
+  }
+  const first = pathname.replace(/^\/+/, '').split('/')[0] ?? '';
+  if (first && first in ui) return first as keyof typeof ui;
+  return defaultLang;
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function getLangFromUrlReact(url: string) {
const [, lang] = url.split('/');
if (lang in ui) return lang as keyof typeof ui;
return defaultLang;
}
export function getLangFromUrlReact(url: string) {
let pathname = url;
try {
// If absolute URL, extract pathname; otherwise assume it's already a path
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) {
pathname = new URL(url).pathname;
}
} catch {
// noop; fallback to given string
}
const first = pathname.replace(/^\/+/, '').split('/')[0] ?? '';
if (first && first in ui) return first as keyof typeof ui;
return defaultLang;
}
🤖 Prompt for AI Agents
In src/i18n/react-utils.ts around lines 9 to 13, the current url.split('/')
approach fails for absolute URLs; instead parse the URL's pathname before
splitting — use the URL constructor when input looks like an absolute URL
(catching errors) or fallback to treating the input as a relative path, get
pathname (or the original string), trim leading/trailing slashes, split on '/'
and take the first segment as lang; then check if that lang exists in ui and
return it as keyof typeof ui or return defaultLang if not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants