Idioma por defecto sin prefijo
Con
strategy: 'prefix_except_default', la opción por defecto, las URL del idioma por defecto no cambian y cada otro idioma vive bajo su prefijo. El módulo genera las rutas
/en/…
a partir de las páginas existentes, con el mismo archivo, layouts y opciones: no hay que duplicar páginas.
i18n: {
defaultLocale: 'es',
catalogs: { es: { common: 'app/locales/es/common.json' }, en: { common: 'app/locales/en/common.json' } },
// Por defecto: es sin prefijo y en con /en/…
strategy: 'prefix_except_default',
// Sin versiones /en/… ni hreflang:
exclude: ['/admin/*'],
// Un idioma con traducción parcial sólo tiene estas rutas:
partial: { ar: ['/playground'] },
}
// app/pages/guide.meeko -> /guide y /en/guide
// app/pages/docs/[...slug].meeko -> /docs/… y /en/docs/…
// app/pages/en/legal.meeko -> sustituye a la /en/legal generada
Una página del proyecto en la URL de otro idioma, como
app/pages/en/legal.meeko, sustituye a la ruta generada: así se escribe la prosa de una página en cada idioma.
exclude
deja una ruta sin versiones y
partial
limita las de un idioma con traducción parcial. Una URL con el prefijo del idioma por defecto, como
/es/guide, no existe.
Enlaces localizados
<script lang="ts">
const i18n = useI18n()
</script>
<template>
<!-- /guide en español, /en/guide en inglés -->
<a bind:href="i18n.localePath('/guide')">{i18n.t('nav.guide')}</a>
<UILink bind:href="i18n.localePath('/docs/intro#setup')">Intro</UILink>
</template>
localePath
añade el prefijo del idioma del documento, o del idioma que recibe, y conserva la búsqueda y el fragmento. Úsalo en los enlaces internos de la interfaz.
Selector de idioma
<script lang="ts">
const i18n = useI18n()
</script>
<template>
<nav aria-label="Idioma">
<a
each:of="i18n.pageLocales()"
each:as="code"
each:key="code"
bind:href="i18n.switchLocalePath(code)"
bind:hreflang="code"
bind:lang="code"
bind:aria-current="code === i18n.locale ? 'page' : undefined"
>
{i18n.localeName(code)}
</a>
</nav>
</template>
<!-- /playground?tab=demo#messages -> /en/playground?tab=demo#messages -->
switchLocalePath
devuelve la ruta equivalente de la página actual en otro idioma y
pageLocales
los idiomas que tienen versión de ella. Usa enlaces reales: funcionan sin JavaScript y los buscadores los siguen. Cambiar de idioma es una navegación completa que guarda la elección en una cookie;
switchLocale
hace lo mismo desde un script.
hreflang y sitemap
<!-- /en/guide, con site: 'https://example.com' -->
<html lang="en" dir="ltr">
<link rel="alternate" href="https://example.com/guide" hreflang="es">
<link rel="alternate" href="https://example.com/en/guide" hreflang="en">
<link rel="alternate" href="https://example.com/guide" hreflang="x-default">
<!-- sitemap.xml (sitemap: true) -->
<url>
<loc>https://example.com/en/guide</loc>
<xhtml:link rel="alternate" hreflang="es" href="https://example.com/guide"/>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/guide"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/guide"/>
</url>
Cada página localizada declara
lang,
dir
y un
link rel="alternate"
por idioma más
x-default, con URL absolutas a partir de
site. Con
sitemap: true, el
sitemap.xml
de Meeko lista cada versión con sus alternativas.
Sugerir el idioma, sin redirigir
<script lang="ts">
const i18n = useI18n()
const suggestion = signal<string | undefined>(undefined)
// Sólo en el navegador, después de hidratar: nunca redirige.
onMounted(() => { suggestion.value = i18n.suggestedLocale() })
</script>
<template>
<p if:when="suggestion.value" role="status">
<a bind:href="i18n.switchLocalePath(suggestion.value!)">
{i18n.localeName(suggestion.value!)}
</a>
<button type="button" on:click="() => { i18n.rememberLocale(i18n.locale); suggestion.value = undefined }">
{i18n.localeName(i18n.locale)}
</button>
</p>
</template>
Las URL sin prefijo siempre muestran el idioma por defecto: el módulo nunca redirige según
Accept-Language
ni la cookie. Así cada URL tiene un único contenido que los buscadores pueden indexar y las cachés pueden compartir.
suggestedLocale
propone, sólo en el navegador, el idioma de la cookie o de
navigator.languages
para que muestres un aviso.
Utilidades de URL
import { localeOf, localizedPath, negotiateLocale, stripLocale } from '@meekojs/i18n'
const locales = ['es', 'en', 'ar']
localeOf('/en/playground', locales, 'es') // 'en'
localeOf('/playground', locales, 'es') // 'es'
localizedPath('/playground?q=1#form', 'en', locales, 'es')
// '/en/playground?q=1#form'
localizedPath('/en/playground', 'es', locales, 'es') // '/playground'
stripLocale('/en/playground', locales) // '/playground'
negotiateLocale('en-US,en;q=0.9,es;q=0.8', locales) // 'en'
localeOf
devuelve el idioma del primer segmento o el fallback recibido.
localizedPath
sustituye un prefijo conocido y omite el del idioma por defecto;
negotiateLocale
elige un idioma configurado a partir de una cabecera
Accept-Language
o de una lista de preferencias.