32b4460b66
Добавляет zh-CN, fa, ar, tr, uk, be, vi, uz словари (170 ключей, идентичная
вложенная структура) к существующим en/ru. lib/locales.ts — единый источник
списка локалей/дефолтной локали/RTL-набора вместо захардкоженного ["en","ru"]
в proxy.ts. language-switcher.tsx: переключение языка резало только
двухбуквенные коды регэкспом (/^\/[a-z]{2}/), из-за чего zh-CN никогда не
матчился и путь задваивался — заменено на honest-поиск по списку locales.
app/[lang]/layout.tsx — dir="rtl"/"ltr" на <html> по локали (SSR, в отличие
от реактивного клиентского переключения в netrunner-app). Header.tsx/
Footer.tsx — разбитое на три <span> имя бренда визуально переворачивалось
Flexbox'ом под dir=rtl ("VPNrunnerNet"), исправлено через dir="ltr" на
самом враппере.
BlogPreview/Faq/Pricing и обе blog-страницы: fallback на *_en-коллекцию
PocketBase, если для языка ещё нет переведённого контента (FAQ-ответы,
детали тарифов, посты блога не тянутся автоматически — это отдельный от
статических словарей CMS-контент), плюс i18n-ключи period1m/period6m/
period1y вместо захардкоженных ru/en-подписей периодов в Pricing.tsx.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
222 lines
4.7 KiB
TypeScript
222 lines
4.7 KiB
TypeScript
// Форма JSON-словарей из dictionaries/en.json и dictionaries/ru.json — оба файла
|
|
// должны точно соответствовать этой структуре, иначе TypeScript подсветит расхождение
|
|
// в местах, где used dict.<section>.<key> (см. lib/dictionaries.ts).
|
|
export interface Dictionary {
|
|
nav: {
|
|
features: string;
|
|
pricing: string;
|
|
faq: string;
|
|
profile: string;
|
|
blog: string;
|
|
terminal: string;
|
|
docs: string;
|
|
cta: string;
|
|
menu: string;
|
|
speedtest: string; // Новое
|
|
doom: string; // Новое
|
|
};
|
|
hero: {
|
|
badge: string;
|
|
badgeSecure: string;
|
|
titleStart: string;
|
|
titleHighlight: string;
|
|
subtitle: string;
|
|
switchLabel: string;
|
|
ctaPrimary: string;
|
|
ctaSecondary: string;
|
|
};
|
|
features: {
|
|
title: string;
|
|
items: { title: string; description: string }[];
|
|
};
|
|
pricing: {
|
|
badge: string;
|
|
title: string;
|
|
perMonth: string;
|
|
recommend: string;
|
|
syncing: string;
|
|
empty: string;
|
|
discount: string;
|
|
mo: string;
|
|
billedOnce: string;
|
|
period1m: string;
|
|
period6m: string;
|
|
period1y: string;
|
|
};
|
|
faq: {
|
|
title: string;
|
|
empty: string;
|
|
};
|
|
blog: {
|
|
badge: string;
|
|
title: string;
|
|
more: string;
|
|
readMore: string;
|
|
empty: string;
|
|
archive: string;
|
|
systemLogs: string;
|
|
emptySector: string;
|
|
decrypt: string;
|
|
backBtn: string;
|
|
readTime: string;
|
|
verified: string;
|
|
endTransmission: string;
|
|
};
|
|
footer: {
|
|
brand: string;
|
|
tagline: string;
|
|
sections: {
|
|
product: string;
|
|
resources: string;
|
|
legal: string;
|
|
};
|
|
links: {
|
|
features: string;
|
|
pricing: string;
|
|
faq: string;
|
|
protocol: string;
|
|
comms: string;
|
|
privacy: string;
|
|
terms: string;
|
|
profile: string;
|
|
};
|
|
rights: string;
|
|
protocolStatus: string;
|
|
uplinkStatus: string;
|
|
activeNode: string;
|
|
latency: string;
|
|
};
|
|
download: {
|
|
title: string;
|
|
subtitle: string;
|
|
badges: { active: string; dev: string };
|
|
buttons: {
|
|
direct: string;
|
|
store: string;
|
|
github: string;
|
|
notify: string;
|
|
soon: string;
|
|
pending: string;
|
|
};
|
|
platforms: {
|
|
windows: string;
|
|
android: string;
|
|
linux: string;
|
|
macos: string;
|
|
ios: string;
|
|
};
|
|
accessingBuild: string;
|
|
};
|
|
terminal: {
|
|
title: string;
|
|
description: string;
|
|
alias: string;
|
|
message: string;
|
|
send: string;
|
|
systemReady: string;
|
|
loadingHistory: string;
|
|
noMoreLogs: string;
|
|
encrypting: string;
|
|
cooldown: string;
|
|
manifestoTitle: string;
|
|
manifestoText: string;
|
|
rulesTitle: string;
|
|
rule1: string;
|
|
rule2: string;
|
|
rule3: string;
|
|
statusReady: string;
|
|
};
|
|
loading: {
|
|
establishing: string;
|
|
};
|
|
notFound: {
|
|
errorBadge: string;
|
|
message: string;
|
|
returnHome: string;
|
|
};
|
|
nodes: {
|
|
title: string;
|
|
subtitle: string;
|
|
status: {
|
|
online: string;
|
|
full: string;
|
|
maintenance: string;
|
|
};
|
|
labels: {
|
|
load: string;
|
|
latency: string;
|
|
};
|
|
};
|
|
guestbook: {
|
|
title: string;
|
|
subtitle: string;
|
|
description: string;
|
|
philosophyTitle: string;
|
|
philosophyText: string;
|
|
liveFeed: string;
|
|
noTransmissions: string;
|
|
labelAlias: string;
|
|
labelPayload: string;
|
|
placeholderAlias: string;
|
|
placeholderPayload: string;
|
|
statusSuccess: string;
|
|
statusErrorBreach: string;
|
|
statusErrorEmpty: string;
|
|
statusErrorSize: string;
|
|
statusErrorFirewall: string;
|
|
statusErrorOffline: string;
|
|
statusErrorFilter?: string;
|
|
btnEncrypting: string;
|
|
btnBroadcast: string;
|
|
cooldown: string;
|
|
};
|
|
auth: {
|
|
title: string;
|
|
processing: string;
|
|
verifying: string;
|
|
granted: string;
|
|
denied: string;
|
|
noToken: string;
|
|
redirecting: string;
|
|
toBot: string;
|
|
awaiting: string;
|
|
authPrompt: string;
|
|
logs: {
|
|
waitInit: string;
|
|
sigValid: string;
|
|
apiRejected: string;
|
|
coreOffline: string;
|
|
authPayload: string;
|
|
verifyHmac: string;
|
|
invalidSig: string;
|
|
networkError: string;
|
|
};
|
|
};
|
|
docs: {
|
|
establishing: string;
|
|
vaultTitle: string;
|
|
vaultSubtitle: string;
|
|
searchPlaceholder: string;
|
|
empty: string;
|
|
closeSession: string;
|
|
noContent: string;
|
|
eof: string;
|
|
};
|
|
speedtest: {
|
|
title: string;
|
|
mockMode: string;
|
|
prodMode: string;
|
|
btnError: string;
|
|
btnDone: string;
|
|
btnIdle: string;
|
|
statusPinging: string;
|
|
statusDownloading: string;
|
|
statusUploading: string;
|
|
latency: string;
|
|
download: string;
|
|
upload: string;
|
|
unitMs: string;
|
|
unitMbps: string;
|
|
};
|
|
}
|