El endpoint más pequeño de la API y el que tu selector de idioma llevaba tiempo esperando. Responde a una sola pregunta, “¿qué idiomas habla este sitio ahora mismo?”, y lo hace exactamente en el orden en que tus editores los colocaron en el CRM.
Tira de él cuando necesites:
- pintar un selector de idioma en la cabecera sin hardcodear
enydeen cinco sitios distintos; - comprobar que el idioma de la URL (
/de/pricing) existe de verdad antes de pasarlo a?lang; - generar rutas localizadas, etiquetas
hreflango un sitemap por idioma.
Listar los idiomas activos
/v1/langshttps://back.sitecog.com/content/v1/langs
?lang.Parámetros
Aquí no hay nada que ajustar: ni parámetros de ruta ni parámetros de query. Solo la clave.
x-crm-keyheaderstringobligatorio?key= si no puedes usar cabeceras. Mira Claves del sitio.Ejemplo de petición
curl https://back.sitecog.com/content/v1/langs \
-H "x-crm-key: pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40"const res = await fetch('https://back.sitecog.com/content/v1/langs', {
headers: { 'x-crm-key': 'pk_3f9c2a7e1b4d4c0e8a6f5b2d9c1e7a40' },
});
if (!res.ok) throw new Error(`Content API: ${res.status}`);
const langs = await res.json();
console.log(langs.map((l) => l.key)); // ["en", "de"]// lib/langs.ts — helper de servidor, con caché de un minuto
export type Lang = { key: string; title: string; index: number };
export async function getLangs(): Promise<Lang[]> {
const res = await fetch('https://back.sitecog.com/content/v1/langs', {
headers: { 'x-crm-key': process.env.CRM_KEY! },
next: { revalidate: 60 },
});
if (!res.ok) throw new Error(`Content API: ${res.status}`);
return res.json();
}Ejemplo de respuesta
[
{ "key": "en", "title": "English", "index": 0 },
{ "key": "de", "title": "German", "index": 1 }
]Campos de la respuesta
Un array de objetos, uno por cada idioma activo. Tres campos cada uno: ya te dijimos que era pequeño.
keystringen, de) o una variante regional como pt-BR. Es justo el valor que pasas en ?lang. Distingue mayúsculas: pt-BR y pt-br son códigos distintos.titlestringindexnumberUn selector de idioma en React
El montaje habitual: el idioma vive en la URL (/en/…, /de/…), el selector se construye a partir de /v1/langs y cada petición de contenido lleva el mismo código en ?lang. Aquí tienes un selector que no sabe nada de idiomas concretos: añade el italiano en el CRM mañana y aparecerá sin más.
type Lang = { key: string; title: string; index: number };
export function LangSwitcher({ langs, current, path }: {
langs: Lang[]; // directamente de GET /v1/langs
current: string; // el idioma de la página que se muestra
path: string; // el resto de la URL, p. ej. "/pricing"
}) {
return (
<nav aria-label="Language">
{langs.map((lang) => (
<a
key={lang.key}
href={`/${lang.key}${path}`}
hrefLang={lang.key}
aria-current={lang.key === current ? 'true' : undefined}
>
{lang.title}
</a>
))}
</nav>
);
}Cómo combinarlo con ?lang
La clave que obtienes aquí es la misma que envías en todas partes. Compara primero el código de la URL con la lista: un código desconocido en ?lang es un 400 unknown_lang, y nadie quiere una página de error porque alguien escribió /fr/ a mano.
import { notFound } from 'next/navigation';
import { getLangs } from '@/lib/langs';
import { LangSwitcher } from '@/components/LangSwitcher';
export default async function Home({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params;
const langs = await getLangs();
// Idioma desconocido en la URL → un 404 normal en vez de un 400 de la API
if (!langs.some((l) => l.key === lang)) notFound();
const res = await fetch(`https://back.sitecog.com/content/v1/pages/home?lang=${lang}`, {
headers: { 'x-crm-key': process.env.CRM_KEY! },
next: { revalidate: 60 },
});
const page = await res.json();
return (
<>
<LangSwitcher langs={langs} current={lang} path="" />
<h1>{page.content.hero.content.hero_title.content[lang]}</h1>
</>
);
}¿Necesitas un idioma por defecto para quien entra en /? Eso lo decides tú: muchas webs simplemente toman el primero de la lista. La API nunca elige un idioma por ti ni sustituye una traducción que falta; cómo gestionar esos huecos con elegancia lo cuenta Idiomas y fallbacks.
Errores
| Estado | Cuerpo | Qué ha pasado |
|---|---|---|
| 401 | {"message":"invalid_key"} | Falta la clave, está mal formada o se ha revocado. |
| 405 | {"message":"method_not_allowed"} | Solo se aceptan GET (y HEAD). La API es de solo lectura. |
| 429 | {"message":"rate_limit_exceeded"} | Demasiadas peticiones en este minuto. Mira Límites de peticiones. |
La lista completa, con soluciones, está en la página de Errores.