Самый маленький запрос в API — и именно его ждёт ваш переключатель языка. Он отвечает на один вопрос: «на каких языках сайт говорит прямо сейчас?» — и перечисляет их ровно в том порядке, в каком их расставили редакторы в CRM.
Он пригодится, когда нужно:
- показать переключатель языка в шапке, не прописывая
enиdeруками в пяти местах; - убедиться, что язык из адреса (
/de/pricing) вообще существует, прежде чем отправлять его в?lang; - собрать адреса страниц на каждом языке, теги
hreflangили карту сайта.
Список активных языков
/v1/langshttps://back.sitecog.com/content/v1/langs
?lang API его тоже не примет.Параметры
Настраивать нечего: ни параметров пути, ни параметров строки запроса. Только ключ.
x-crm-keyheaderstringобязательно?key=. Подробнее — в разделе Ключ сайта.Пример запроса
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 — серверный помощник, кэш на минуту
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();
}Пример ответа
[
{ "key": "en", "title": "English", "index": 0 },
{ "key": "de", "title": "German", "index": 1 }
]Поля ответа
Массив объектов, по одному на каждый активный язык. В каждом три поля — мы же говорили, что запрос маленький.
keystringen, de) или региональный вариант вроде pt-BR. Именно это значение передаётся в ?lang. Регистр важен: pt-BR и pt-br — разные коды.titlestringindexnumberПереключатель языка на React
Обычная схема такая: язык живёт в адресе (/en/…, /de/…), переключатель строится по /v1/langs, а каждый запрос за контентом получает тот же код в ?lang. Вот переключатель, который ничего не знает о конкретных языках: добавите завтра в CRM итальянский — он просто появится.
type Lang = { key: string; title: string; index: number };
export function LangSwitcher({ langs, current, path }: {
langs: Lang[]; // прямо из GET /v1/langs
current: string; // язык открытой страницы
path: string; // остаток адреса, например "/pricing"
}) {
return (
<nav aria-label="Язык">
{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>
);
}Связка с ?lang
Код отсюда — тот самый код, который вы отправляете во все остальные запросы. Сначала сверьте язык из адреса со списком: незнакомый код в ?lang — это 400 unknown_lang, а показывать страницу ошибки только потому, что кто-то вручную набрал /fr/, никому не хочется.
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();
// Незнакомый язык в адресе → обычная 404 вместо 400 от 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>
</>
);
}Какой язык показать тем, кто зашёл просто на /, — решать вам; многие сайты берут первый язык из списка. Сам API язык за вас не выбирает и недостающий перевод ничем не подменяет. Как аккуратно закрывать такие дыры, рассказано в разделе Языки и запасной язык.
Ошибки
| Статус | Тело ответа | Что случилось |
|---|---|---|
| 401 | {"message":"invalid_key"} | Ключа нет, он неправильного вида или отозван. |
| 405 | {"message":"method_not_allowed"} | Принимаются только GET (и HEAD): API работает только на чтение. |
| 429 | {"message":"rate_limit_exceeded"} | Слишком много запросов за эту минуту. См. Лимиты. |
Полный список с подсказками, что делать, — на странице Ошибки.