feat(landing): английская версия сайта с переключателем языков

Нужна для проверки компании иностранными партнёрами и магазинами приложений:
проверяющий должен понять, кто мы и чем заняты, не зная русского.

- переводы лежат в тех же строках БД (products.*_en, approach_items.*_en,
  sections.content_json_en, settings.company_name_en/company_address_en),
  админка остаётся одна;
- фронт откатывается на русский ПО КАЖДОМУ ПОЛЮ отдельно — недопереведённая
  секция не оставляет пустого места на странице;
- выбор языка запоминается в localStorage, проставляется <html lang>.
This commit is contained in:
2026-08-19 23:55:45 +03:00
parent ad901c1ee4
commit 0c295386fa
5 changed files with 2822 additions and 25 deletions
+10 -3
View File
@@ -7,20 +7,22 @@ publicRouter.get('/content', async (_req, res) => {
try {
const [sections, products, approach, settings] = await Promise.all([
query<{ key: string; title: string; content_json: unknown; sort_order: number }>(
`SELECT key, title, content_json, sort_order
`SELECT key, title, content_json, content_json_en, sort_order
FROM sections
WHERE is_active = true
ORDER BY sort_order ASC`
),
query<unknown>(
`SELECT id, slug, title, subtitle, description,
status, audience, icon_key, tags, sort_order, site_url
status, audience, icon_key, tags, sort_order, site_url,
title_en, subtitle_en, description_en
FROM products
WHERE is_published = true
ORDER BY sort_order ASC`
),
query<unknown>(
`SELECT id, title, description, icon_key, sort_order
`SELECT id, title, description, icon_key, sort_order,
title_en, description_en
FROM approach_items
WHERE is_active = true
ORDER BY sort_order ASC`
@@ -30,9 +32,13 @@ publicRouter.get('/content', async (_req, res) => {
),
]);
// Секции отдаём двумя наборами: русский и английский. Фронт выбирает
// язык на лету и откатывается на русский там, где перевода ещё нет.
const sectionsMap: Record<string, unknown> = {};
const sectionsMapEn: Record<string, unknown> = {};
for (const s of sections) {
sectionsMap[s.key] = s.content_json;
sectionsMapEn[s.key] = s.content_json_en || {};
}
const settingsMap: Record<string, string> = {};
@@ -41,6 +47,7 @@ publicRouter.get('/content', async (_req, res) => {
}
res.json({
sections_en: sectionsMapEn,
sections: sectionsMap,
products,
approach,