Fix hreflang canonical + LangToggle: use centralized ES/EN slug map
- Add src/i18n/hreflang-map.ts with getHreflangUrl() for translated slugs - Fix BaseLayout.astro hreflang tags (was hardcoding ES='/', 404 URLs) - Fix LangToggle.astro to use hreflang map instead of naive path swap - Fix htaccess.conf: remove !-f / !-d that blocked .html redirects
This commit is contained in:
@@ -12,9 +12,7 @@
|
|||||||
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
|
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
|
||||||
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
|
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
|
||||||
|
|
||||||
# Quitar .html de la URL (limpieza opcional)
|
# Quitar .html de la URL — evita duplicados index.html vs /
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
|
||||||
RewriteCond %{REQUEST_URI} ^(.+)\.html$ [NC]
|
RewriteCond %{REQUEST_URI} ^(.+)\.html$ [NC]
|
||||||
RewriteRule ^(.+)\.html$ /$1 [R=301,L]
|
RewriteRule ^(.+)\.html$ /$1 [R=301,L]
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { LANGUAGES, DEFAULT_LANG, type Lang } from '@/i18n/utils';
|
import { LANGUAGES, type Lang } from '@/i18n/utils';
|
||||||
|
import { getHreflangUrl } from '@/i18n/hreflang-map';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
lang: Lang;
|
lang: Lang;
|
||||||
@@ -7,19 +8,17 @@ interface Props {
|
|||||||
const { lang } = Astro.props;
|
const { lang } = Astro.props;
|
||||||
const path = Astro.url.pathname;
|
const path = Astro.url.pathname;
|
||||||
|
|
||||||
function otherLangUrl(): string {
|
const otherLang = lang === 'en' ? 'es' : 'en';
|
||||||
const cleanPath = path.replace(/^\/(en|es)/, '') || '/';
|
const fullUrl = getHreflangUrl(path, lang, otherLang);
|
||||||
if (lang === DEFAULT_LANG) {
|
const href = fullUrl
|
||||||
return `/en${cleanPath === '/' ? '' : cleanPath}`;
|
? fullUrl.replace('https://hostingdelsur.net', '')
|
||||||
}
|
: otherLang === 'en' ? '/en/' : '/';
|
||||||
return cleanPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const other = LANGUAGES.find((l) => l.code !== lang);
|
const other = LANGUAGES.find((l) => l.code !== lang);
|
||||||
const isEn = lang === 'en';
|
const isEn = lang === 'en';
|
||||||
---
|
---
|
||||||
<a
|
<a
|
||||||
href={otherLangUrl()}
|
href={href}
|
||||||
hreflang={other?.code}
|
hreflang={other?.code}
|
||||||
class="text-xs font-semibold tracking-wider px-2.5 py-1.5 rounded border transition-colors"
|
class="text-xs font-semibold tracking-wider px-2.5 py-1.5 rounded border transition-colors"
|
||||||
style="border-color: var(--hds-line); color: var(--hds-fg-soft);"
|
style="border-color: var(--hds-line); color: var(--hds-fg-soft);"
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import type { Lang } from './ui';
|
||||||
|
|
||||||
|
const esToEnOverrides: Record<string, string | null> = {
|
||||||
|
'/nosotros/': '/en/about/',
|
||||||
|
'/contacto/': '/en/contact/',
|
||||||
|
'/planes/': '/en/plans/',
|
||||||
|
'/instructivos/': '/en/tutorials/',
|
||||||
|
'/legal/privacidad/': '/en/legal/privacy/',
|
||||||
|
'/legal/terminos/': '/en/legal/terms/',
|
||||||
|
'/preguntas/': null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const enToEsMap: Record<string, string> = {};
|
||||||
|
for (const [esPath, enPath] of Object.entries(esToEnOverrides)) {
|
||||||
|
if (enPath) enToEsMap[enPath] = esPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SITE_URL = 'https://hostingdelsur.net';
|
||||||
|
|
||||||
|
export function getHreflangUrl(
|
||||||
|
pathname: string,
|
||||||
|
currentLang: Lang,
|
||||||
|
targetLang: Lang
|
||||||
|
): string | null {
|
||||||
|
if (currentLang === targetLang) {
|
||||||
|
return `${SITE_URL}${pathname}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentLang === 'es' && targetLang === 'en') {
|
||||||
|
const override = esToEnOverrides[pathname];
|
||||||
|
if (override === null) return null;
|
||||||
|
if (override) return `${SITE_URL}${override}`;
|
||||||
|
return `${SITE_URL}/en${pathname}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentLang === 'en' && targetLang === 'es') {
|
||||||
|
const override = enToEsMap[pathname];
|
||||||
|
if (override) return `${SITE_URL}${override}`;
|
||||||
|
const esPath = pathname.replace(/^\/en/, '') || '/';
|
||||||
|
return `${SITE_URL}${esPath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { getLangFromUrl, useTranslations, type Lang } from '@/i18n/utils';
|
import { getLangFromUrl, useTranslations, type Lang } from '@/i18n/utils';
|
||||||
|
import { getHreflangUrl } from '@/i18n/hreflang-map';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -15,6 +16,8 @@ const currentLang = lang ?? getLangFromUrl(Astro.url);
|
|||||||
const t = useTranslations(currentLang);
|
const t = useTranslations(currentLang);
|
||||||
const siteUrl = Astro.site?.toString().replace(/\/$/, '') ?? 'https://hostingdelsur.net';
|
const siteUrl = Astro.site?.toString().replace(/\/$/, '') ?? 'https://hostingdelsur.net';
|
||||||
const canonicalUrl = canonical ? `${siteUrl}${canonical}` : `${siteUrl}${Astro.url.pathname}`;
|
const canonicalUrl = canonical ? `${siteUrl}${canonical}` : `${siteUrl}${Astro.url.pathname}`;
|
||||||
|
const esHreflang = getHreflangUrl(Astro.url.pathname, currentLang, 'es');
|
||||||
|
const enHreflang = getHreflangUrl(Astro.url.pathname, currentLang, 'en');
|
||||||
|
|
||||||
import '@/styles/global.css';
|
import '@/styles/global.css';
|
||||||
import Navbar from '@/components/Navbar.astro';
|
import Navbar from '@/components/Navbar.astro';
|
||||||
@@ -95,8 +98,8 @@ const localBusinessJsonLd = {
|
|||||||
<meta name="twitter:description" content={description} />
|
<meta name="twitter:description" content={description} />
|
||||||
<meta name="twitter:image" content={`${siteUrl}${ogImage}`} />
|
<meta name="twitter:image" content={`${siteUrl}${ogImage}`} />
|
||||||
|
|
||||||
<link rel="alternate" hreflang="es" href={`${siteUrl}${currentLang === 'en' ? '/' : Astro.url.pathname.replace(/^\/en/, '')}`} />
|
{esHreflang && <link rel="alternate" hreflang="es" href={esHreflang} />}
|
||||||
<link rel="alternate" hreflang="en" href={`${siteUrl}/en${Astro.url.pathname.replace(/^\/en/, '') || ''}`} />
|
{enHreflang && <link rel="alternate" hreflang="en" href={enHreflang} />}
|
||||||
|
|
||||||
<script type="application/ld+json" set:html={JSON.stringify(orgJsonLd)} />
|
<script type="application/ld+json" set:html={JSON.stringify(orgJsonLd)} />
|
||||||
<script type="application/ld+json" set:html={JSON.stringify(localBusinessJsonLd)} />
|
<script type="application/ld+json" set:html={JSON.stringify(localBusinessJsonLd)} />
|
||||||
|
|||||||
Reference in New Issue
Block a user