Major changes: - Add Go backend (backend/) with microservices architecture - Enhanced master-agents-svc: reranker, content-classifier, stealth-crawler, proxy-manager, media-search, fastClassifier, language detection - New web-svc widgets: KnowledgeCard, ProductCard, ProfileCard, VideoCard, UnifiedCard, CardGallery, InlineImageGallery, SourcesPanel, RelatedQuestions - Improved discover-svc with discover-db integration - Docker deployment improvements (Caddyfile, vendor.sh, BUILD.md) - Library-svc: project_id schema migration - Remove deprecated finance-svc and travel-svc - Localization improvements across services Made-with: Cursor
99 lines
2.6 KiB
TypeScript
99 lines
2.6 KiB
TypeScript
/**
|
||
* Клиент для Localization Service.
|
||
* Принудительно русский язык везде.
|
||
*/
|
||
|
||
const LOCALE_API = '/api/locale';
|
||
const TRANSLATIONS_API = '/api/translations';
|
||
|
||
export interface LocalizationContext {
|
||
locale: string;
|
||
language: string;
|
||
region: string | null;
|
||
countryCode: string | null;
|
||
timezone: string | null;
|
||
source: 'geo' | 'accept-language' | 'client' | 'fallback';
|
||
}
|
||
|
||
const collectClientData = () => {
|
||
if (typeof window === 'undefined') return {};
|
||
const nav = navigator;
|
||
return {
|
||
client: {
|
||
language: nav.language,
|
||
languages: nav.languages ? [...nav.languages] : undefined,
|
||
},
|
||
};
|
||
};
|
||
|
||
/**
|
||
* Получить locale (серверный вызов — без client data).
|
||
*/
|
||
export async function fetchLocale(): Promise<LocalizationContext> {
|
||
const res = await fetch(LOCALE_API);
|
||
if (!res.ok) throw new Error('Failed to fetch locale');
|
||
return res.json();
|
||
}
|
||
|
||
/**
|
||
* Получить locale с данными клиента (браузер language).
|
||
*/
|
||
export async function fetchLocaleWithClient(): Promise<LocalizationContext> {
|
||
const clientData = collectClientData();
|
||
const res = await fetch(LOCALE_API, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(clientData),
|
||
});
|
||
if (!res.ok) throw new Error('Failed to fetch locale');
|
||
return res.json();
|
||
}
|
||
|
||
/** Принудительно русский язык везде */
|
||
export async function fetchLocaleWithGeoFirst(): Promise<LocalizationContext> {
|
||
return {
|
||
locale: 'ru',
|
||
language: 'ru',
|
||
region: null,
|
||
countryCode: null,
|
||
timezone: null,
|
||
source: 'fallback',
|
||
};
|
||
}
|
||
|
||
import {
|
||
getEmbeddedTranslations,
|
||
translationLocaleFor,
|
||
} from './localization/embeddedTranslations';
|
||
|
||
/**
|
||
* Получить переводы для locale.
|
||
* При недоступности API использует встроенные переводы.
|
||
*/
|
||
export async function fetchTranslations(
|
||
locale: string,
|
||
): Promise<Record<string, string>> {
|
||
const fetchLocale = translationLocaleFor(locale);
|
||
try {
|
||
const res = await fetch(
|
||
`${TRANSLATIONS_API}/${encodeURIComponent(fetchLocale)}`,
|
||
);
|
||
if (res.ok) return res.json();
|
||
} catch {
|
||
/* API недоступен */
|
||
}
|
||
return getEmbeddedTranslations(locale);
|
||
}
|
||
|
||
/**
|
||
* Получить locale и переводы одним вызовом.
|
||
*/
|
||
export async function fetchLocalization(): Promise<{
|
||
locale: LocalizationContext;
|
||
translations: Record<string, string>;
|
||
}> {
|
||
const locale = await fetchLocaleWithClient();
|
||
const translations = await fetchTranslations(locale.locale);
|
||
return { locale, translations };
|
||
}
|