english version update
This commit is contained in:
3
ospabhost/frontend/src/i18n/index.ts
Normal file
3
ospabhost/frontend/src/i18n/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { useTranslation, getTranslation } from './useTranslation';
|
||||
export type { TranslationKey, TranslationKeys } from './useTranslation';
|
||||
export { ru, en } from './translations';
|
||||
342
ospabhost/frontend/src/i18n/translations/en.ts
Normal file
342
ospabhost/frontend/src/i18n/translations/en.ts
Normal file
@@ -0,0 +1,342 @@
|
||||
import type { TranslationKeys } from './ru';
|
||||
|
||||
export const en: TranslationKeys = {
|
||||
// Navigation
|
||||
nav: {
|
||||
home: 'Home',
|
||||
about: 'About',
|
||||
tariffs: 'Pricing',
|
||||
blog: 'Blog',
|
||||
login: 'Sign In',
|
||||
register: 'Sign Up',
|
||||
dashboard: 'Dashboard',
|
||||
logout: 'Sign Out',
|
||||
},
|
||||
|
||||
// Home page
|
||||
home: {
|
||||
hero: {
|
||||
title: 'Cloud S3 Storage',
|
||||
subtitle: 'Reliable S3-compatible storage in Veliky Novgorod',
|
||||
description: 'Store files, backups and media content. 24/7 support, flexible plans.',
|
||||
cta: 'Get Started Free',
|
||||
learnMore: 'Learn More',
|
||||
},
|
||||
features: {
|
||||
title: 'Why Choose Us',
|
||||
s3Compatible: {
|
||||
title: 'S3 Compatible',
|
||||
description: 'Fully compatible with AWS S3 API. Use your favorite tools.',
|
||||
},
|
||||
reliability: {
|
||||
title: 'Reliability',
|
||||
description: 'Data replication, backups and 24/7 monitoring.',
|
||||
},
|
||||
speed: {
|
||||
title: 'Speed',
|
||||
description: 'Fast SSD drives and optimized network.',
|
||||
},
|
||||
support: {
|
||||
title: 'Support',
|
||||
description: 'Quick response support tickets. Help available in English.',
|
||||
},
|
||||
},
|
||||
pricing: {
|
||||
title: 'Pricing',
|
||||
subtitle: 'Choose the right plan',
|
||||
perMonth: '/month',
|
||||
perGb: 'per GB',
|
||||
storage: 'Storage',
|
||||
traffic: 'Traffic',
|
||||
support: 'Support',
|
||||
selectPlan: 'Select',
|
||||
},
|
||||
cta: {
|
||||
title: 'Ready to get started?',
|
||||
description: 'Join developers who trust us with their data.',
|
||||
},
|
||||
},
|
||||
|
||||
// About page
|
||||
about: {
|
||||
title: 'About Us',
|
||||
subtitle: 'Ospab.host — modern cloud storage platform',
|
||||
story: {
|
||||
title: 'Our Story',
|
||||
text: 'We created ospab.host to provide reliable and affordable cloud storage for businesses and developers.',
|
||||
},
|
||||
team: {
|
||||
title: 'Our Team',
|
||||
founder: 'Founder',
|
||||
},
|
||||
location: {
|
||||
title: 'Location',
|
||||
text: 'Our servers are located in Veliky Novgorod, Russia.',
|
||||
},
|
||||
},
|
||||
|
||||
// Authentication
|
||||
auth: {
|
||||
login: {
|
||||
title: 'Sign In',
|
||||
email: 'Email',
|
||||
password: 'Password',
|
||||
submit: 'Sign In',
|
||||
noAccount: "Don't have an account?",
|
||||
register: 'Sign Up',
|
||||
forgotPassword: 'Forgot password?',
|
||||
orContinueWith: 'or continue with',
|
||||
},
|
||||
register: {
|
||||
title: 'Sign Up',
|
||||
username: 'Username',
|
||||
usernamePlaceholder: 'Username',
|
||||
email: 'Email',
|
||||
emailPlaceholder: 'Email address',
|
||||
password: 'Password',
|
||||
passwordPlaceholder: 'Password',
|
||||
confirmPassword: 'Confirm Password',
|
||||
submit: 'Sign Up',
|
||||
loading: 'Signing up...',
|
||||
hasAccount: 'Already have an account?',
|
||||
haveAccount: 'Already have an account?',
|
||||
login: 'Sign In',
|
||||
loginLink: 'Sign In',
|
||||
orRegisterWith: 'Or sign up with',
|
||||
terms: 'By signing up, you agree to our',
|
||||
termsLink: 'Terms of Service',
|
||||
and: 'and',
|
||||
privacyLink: 'Privacy Policy',
|
||||
success: 'Registration successful! You can now sign in.',
|
||||
captchaRequired: 'Please confirm that you are not a robot.',
|
||||
captchaError: 'Captcha loading error. Please refresh the page.',
|
||||
unknownError: 'Unknown registration error.',
|
||||
networkError: 'Network error. Please try again later.',
|
||||
invalidEmail: 'Please enter a valid email address',
|
||||
// Email validation
|
||||
emailValidation: {
|
||||
invalidFormat: 'Invalid email format',
|
||||
disposableEmail: 'Disposable email addresses are not allowed',
|
||||
suggestion: 'Did you mean: {email}?',
|
||||
},
|
||||
},
|
||||
errors: {
|
||||
invalidCredentials: 'Invalid email or password',
|
||||
emailRequired: 'Email is required',
|
||||
passwordRequired: 'Password is required',
|
||||
passwordTooShort: 'Password must be at least 6 characters',
|
||||
passwordsDoNotMatch: 'Passwords do not match',
|
||||
usernameRequired: 'Username is required',
|
||||
emailInvalid: 'Invalid email address',
|
||||
},
|
||||
},
|
||||
|
||||
// Dashboard
|
||||
dashboard: {
|
||||
title: 'Dashboard',
|
||||
welcome: 'Welcome',
|
||||
sidebar: {
|
||||
overview: 'Overview',
|
||||
storage: 'Storage',
|
||||
buckets: 'Buckets',
|
||||
tickets: 'Tickets',
|
||||
billing: 'Billing',
|
||||
settings: 'Settings',
|
||||
admin: 'Admin',
|
||||
},
|
||||
overview: {
|
||||
balance: 'Balance',
|
||||
storage: 'Used',
|
||||
buckets: 'Buckets',
|
||||
tickets: 'Open Tickets',
|
||||
},
|
||||
storage: {
|
||||
title: 'Storage',
|
||||
createBucket: 'Create Bucket',
|
||||
bucketName: 'Bucket Name',
|
||||
bucketNamePlaceholder: 'my-bucket',
|
||||
create: 'Create',
|
||||
cancel: 'Cancel',
|
||||
empty: 'You have no buckets yet',
|
||||
emptyDescription: 'Create your first bucket to store files',
|
||||
},
|
||||
bucket: {
|
||||
files: 'Files',
|
||||
upload: 'Upload',
|
||||
uploadFiles: 'Upload Files',
|
||||
uploadFolder: 'Upload Folder',
|
||||
uploadFromUrl: 'Upload from URL',
|
||||
createFolder: 'Create Folder',
|
||||
delete: 'Delete',
|
||||
download: 'Download',
|
||||
rename: 'Rename',
|
||||
copy: 'Copy',
|
||||
move: 'Move',
|
||||
share: 'Share',
|
||||
properties: 'Properties',
|
||||
emptyBucket: 'Bucket is empty',
|
||||
emptyBucketDescription: 'Upload files or create a folder',
|
||||
dropFilesHere: 'Drop files here',
|
||||
orClickToUpload: 'or click to upload',
|
||||
accessKey: 'Access Key',
|
||||
secretKey: 'Secret Key',
|
||||
endpoint: 'Endpoint',
|
||||
region: 'Region',
|
||||
},
|
||||
tickets: {
|
||||
title: 'Tickets',
|
||||
create: 'Create Ticket',
|
||||
subject: 'Subject',
|
||||
message: 'Message',
|
||||
priority: 'Priority',
|
||||
status: 'Status',
|
||||
created: 'Created',
|
||||
updated: 'Updated',
|
||||
open: 'Open',
|
||||
closed: 'Closed',
|
||||
pending: 'Pending',
|
||||
inProgress: 'In Progress',
|
||||
low: 'Low',
|
||||
medium: 'Medium',
|
||||
high: 'High',
|
||||
urgent: 'Urgent',
|
||||
noTickets: 'You have no tickets',
|
||||
noTicketsDescription: 'Create a ticket if you need help',
|
||||
reply: 'Reply',
|
||||
close: 'Close Ticket',
|
||||
reopen: 'Reopen',
|
||||
},
|
||||
billing: {
|
||||
title: 'Billing',
|
||||
balance: 'Balance',
|
||||
topUp: 'Top Up',
|
||||
history: 'Transaction History',
|
||||
date: 'Date',
|
||||
description: 'Description',
|
||||
amount: 'Amount',
|
||||
noTransactions: 'No transactions',
|
||||
uploadCheck: 'Upload Receipt',
|
||||
checkPending: 'Pending Review',
|
||||
checkApproved: 'Approved',
|
||||
checkRejected: 'Rejected',
|
||||
},
|
||||
settings: {
|
||||
title: 'Settings',
|
||||
profile: 'Profile',
|
||||
security: 'Security',
|
||||
notifications: 'Notifications',
|
||||
appearance: 'Appearance',
|
||||
language: 'Language',
|
||||
timezone: 'Timezone',
|
||||
save: 'Save',
|
||||
changePassword: 'Change Password',
|
||||
currentPassword: 'Current Password',
|
||||
newPassword: 'New Password',
|
||||
confirmNewPassword: 'Confirm New Password',
|
||||
deleteAccount: 'Delete Account',
|
||||
deleteAccountWarning: 'This action is irreversible. All your data will be deleted.',
|
||||
},
|
||||
},
|
||||
|
||||
// Common
|
||||
common: {
|
||||
loading: 'Loading...',
|
||||
error: 'Error',
|
||||
success: 'Success',
|
||||
save: 'Save',
|
||||
cancel: 'Cancel',
|
||||
delete: 'Delete',
|
||||
edit: 'Edit',
|
||||
create: 'Create',
|
||||
close: 'Close',
|
||||
confirm: 'Confirm',
|
||||
back: 'Back',
|
||||
next: 'Next',
|
||||
previous: 'Previous',
|
||||
search: 'Search',
|
||||
filter: 'Filter',
|
||||
sort: 'Sort',
|
||||
refresh: 'Refresh',
|
||||
download: 'Download',
|
||||
upload: 'Upload',
|
||||
copy: 'Copy',
|
||||
copied: 'Copied',
|
||||
yes: 'Yes',
|
||||
no: 'No',
|
||||
or: 'or',
|
||||
and: 'and',
|
||||
of: 'of',
|
||||
items: 'items',
|
||||
bytes: 'bytes',
|
||||
kb: 'KB',
|
||||
mb: 'MB',
|
||||
gb: 'GB',
|
||||
tb: 'TB',
|
||||
openMenu: 'Open menu',
|
||||
closeMenu: 'Close menu',
|
||||
},
|
||||
|
||||
// Errors
|
||||
errors: {
|
||||
notFound: 'Page Not Found',
|
||||
notFoundDescription: 'The page you are looking for does not exist or has been removed.',
|
||||
unauthorized: 'Unauthorized',
|
||||
unauthorizedDescription: 'You need to sign in to access this page.',
|
||||
forbidden: 'Access Denied',
|
||||
forbiddenDescription: 'You do not have permission to view this page.',
|
||||
serverError: 'Server Error',
|
||||
serverErrorDescription: 'An internal server error occurred. Please try again later.',
|
||||
badGateway: 'Bad Gateway',
|
||||
badGatewayDescription: 'The server is temporarily unavailable. Please try again later.',
|
||||
serviceUnavailable: 'Service Unavailable',
|
||||
serviceUnavailableDescription: 'The service is temporarily unavailable. Maintenance in progress.',
|
||||
gatewayTimeout: 'Gateway Timeout',
|
||||
gatewayTimeoutDescription: 'The server did not respond in time. Please try again later.',
|
||||
goHome: 'Go Home',
|
||||
tryAgain: 'Try Again',
|
||||
},
|
||||
|
||||
// Footer
|
||||
footer: {
|
||||
description: 'Reliable cloud S3 storage',
|
||||
links: 'Links',
|
||||
legal: 'Legal',
|
||||
terms: 'Terms of Service',
|
||||
privacy: 'Privacy Policy',
|
||||
contact: 'Contact',
|
||||
copyright: '© 2024 ospab.host. All rights reserved.',
|
||||
},
|
||||
|
||||
// Blog
|
||||
blog: {
|
||||
title: 'Blog',
|
||||
subtitle: 'Articles about hosting, S3 and cloud technologies',
|
||||
readMore: 'Read More',
|
||||
published: 'Published',
|
||||
author: 'Author',
|
||||
tags: 'Tags',
|
||||
relatedPosts: 'Related Posts',
|
||||
noPosts: 'No posts yet',
|
||||
},
|
||||
|
||||
// Tariffs
|
||||
tariffs: {
|
||||
title: 'S3 Storage Pricing',
|
||||
subtitle: 'Choose the right plan for your needs',
|
||||
popular: 'Popular',
|
||||
features: 'Features',
|
||||
storage: 'Storage',
|
||||
traffic: 'Outbound Traffic',
|
||||
requests: 'Requests',
|
||||
support: 'Support',
|
||||
api: 'S3 API',
|
||||
included: 'Included',
|
||||
unlimited: 'Unlimited',
|
||||
perMonth: '/month',
|
||||
selectPlan: 'Select Plan',
|
||||
currentPlan: 'Current Plan',
|
||||
contactUs: 'Contact Us',
|
||||
customPlan: 'Need a custom plan?',
|
||||
customPlanDescription: 'Contact us to discuss special requirements.',
|
||||
},
|
||||
};
|
||||
2
ospabhost/frontend/src/i18n/translations/index.ts
Normal file
2
ospabhost/frontend/src/i18n/translations/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { ru, type TranslationKeys } from './ru';
|
||||
export { en } from './en';
|
||||
342
ospabhost/frontend/src/i18n/translations/ru.ts
Normal file
342
ospabhost/frontend/src/i18n/translations/ru.ts
Normal file
@@ -0,0 +1,342 @@
|
||||
export const ru = {
|
||||
// Навигация
|
||||
nav: {
|
||||
home: 'Главная',
|
||||
about: 'О нас',
|
||||
tariffs: 'Тарифы',
|
||||
blog: 'Блог',
|
||||
login: 'Войти',
|
||||
register: 'Регистрация',
|
||||
dashboard: 'Панель управления',
|
||||
logout: 'Выйти',
|
||||
},
|
||||
|
||||
// Главная страница
|
||||
home: {
|
||||
hero: {
|
||||
title: 'Облачное S3 хранилище',
|
||||
subtitle: 'Надёжное S3-совместимое хранилище в Великом Новгороде',
|
||||
description: 'Храните файлы, резервные копии и медиа-контент. Поддержка 24/7, гибкие тарифы.',
|
||||
cta: 'Начать бесплатно',
|
||||
learnMore: 'Узнать больше',
|
||||
},
|
||||
features: {
|
||||
title: 'Почему выбирают нас',
|
||||
s3Compatible: {
|
||||
title: 'S3-совместимость',
|
||||
description: 'Полная совместимость с AWS S3 API. Используйте привычные инструменты.',
|
||||
},
|
||||
reliability: {
|
||||
title: 'Надёжность',
|
||||
description: 'Репликация данных, резервное копирование и мониторинг 24/7.',
|
||||
},
|
||||
speed: {
|
||||
title: 'Скорость',
|
||||
description: 'Быстрые SSD-накопители и оптимизированная сеть.',
|
||||
},
|
||||
support: {
|
||||
title: 'Поддержка',
|
||||
description: 'Тикеты поддержки с быстрым откликом. Помощь на русском языке.',
|
||||
},
|
||||
},
|
||||
pricing: {
|
||||
title: 'Тарифы',
|
||||
subtitle: 'Выберите подходящий план',
|
||||
perMonth: '/месяц',
|
||||
perGb: 'за ГБ',
|
||||
storage: 'Хранилище',
|
||||
traffic: 'Трафик',
|
||||
support: 'Поддержка',
|
||||
selectPlan: 'Выбрать',
|
||||
},
|
||||
cta: {
|
||||
title: 'Готовы начать?',
|
||||
description: 'Присоединяйтесь к разработчикам, которые доверяют нам свои данные.',
|
||||
},
|
||||
},
|
||||
|
||||
// Страница о нас
|
||||
about: {
|
||||
title: 'О компании',
|
||||
subtitle: 'Ospab.host — современная платформа облачного хранилища',
|
||||
story: {
|
||||
title: 'Наша история',
|
||||
text: 'Мы создали ospab.host чтобы предоставить надёжное и доступное облачное хранилище для бизнеса и разработчиков.',
|
||||
},
|
||||
team: {
|
||||
title: 'Наша команда',
|
||||
founder: 'Основатель',
|
||||
},
|
||||
location: {
|
||||
title: 'Расположение',
|
||||
text: 'Наши серверы расположены в Великом Новгороде, Россия.',
|
||||
},
|
||||
},
|
||||
|
||||
// Авторизация
|
||||
auth: {
|
||||
login: {
|
||||
title: 'Вход в аккаунт',
|
||||
email: 'Email',
|
||||
password: 'Пароль',
|
||||
submit: 'Войти',
|
||||
noAccount: 'Нет аккаунта?',
|
||||
register: 'Зарегистрироваться',
|
||||
forgotPassword: 'Забыли пароль?',
|
||||
orContinueWith: 'или продолжить с',
|
||||
},
|
||||
register: {
|
||||
title: 'Регистрация',
|
||||
username: 'Имя пользователя',
|
||||
usernamePlaceholder: 'Имя пользователя',
|
||||
email: 'Email',
|
||||
emailPlaceholder: 'Электронная почта',
|
||||
password: 'Пароль',
|
||||
passwordPlaceholder: 'Пароль',
|
||||
confirmPassword: 'Подтвердите пароль',
|
||||
submit: 'Зарегистрироваться',
|
||||
loading: 'Регистрируем...',
|
||||
hasAccount: 'Уже есть аккаунт?',
|
||||
haveAccount: 'Уже есть аккаунт?',
|
||||
login: 'Войти',
|
||||
loginLink: 'Войти',
|
||||
orRegisterWith: 'Или зарегистрироваться через',
|
||||
terms: 'Регистрируясь, вы соглашаетесь с',
|
||||
termsLink: 'условиями использования',
|
||||
and: 'и',
|
||||
privacyLink: 'политикой конфиденциальности',
|
||||
success: 'Регистрация прошла успешно! Теперь вы можете войти.',
|
||||
captchaRequired: 'Пожалуйста, подтвердите, что вы не робот.',
|
||||
captchaError: 'Ошибка загрузки капчи. Попробуйте обновить страницу.',
|
||||
unknownError: 'Неизвестная ошибка регистрации.',
|
||||
networkError: 'Произошла ошибка сети. Пожалуйста, попробуйте позже.',
|
||||
invalidEmail: 'Введите корректный email адрес',
|
||||
// Email validation
|
||||
emailValidation: {
|
||||
invalidFormat: 'Неверный формат email адреса',
|
||||
disposableEmail: 'Временные email адреса не допускаются',
|
||||
suggestion: 'Возможно, вы имели в виду: {email}?',
|
||||
},
|
||||
},
|
||||
errors: {
|
||||
invalidCredentials: 'Неверный email или пароль',
|
||||
emailRequired: 'Email обязателен',
|
||||
passwordRequired: 'Пароль обязателен',
|
||||
passwordTooShort: 'Пароль должен быть минимум 6 символов',
|
||||
passwordsDoNotMatch: 'Пароли не совпадают',
|
||||
usernameRequired: 'Имя пользователя обязательно',
|
||||
emailInvalid: 'Некорректный email',
|
||||
},
|
||||
},
|
||||
|
||||
// Дашборд
|
||||
dashboard: {
|
||||
title: 'Панель управления',
|
||||
welcome: 'Добро пожаловать',
|
||||
sidebar: {
|
||||
overview: 'Обзор',
|
||||
storage: 'Хранилище',
|
||||
buckets: 'Бакеты',
|
||||
tickets: 'Тикеты',
|
||||
billing: 'Биллинг',
|
||||
settings: 'Настройки',
|
||||
admin: 'Админ',
|
||||
},
|
||||
overview: {
|
||||
balance: 'Баланс',
|
||||
storage: 'Использовано',
|
||||
buckets: 'Бакетов',
|
||||
tickets: 'Открытых тикетов',
|
||||
},
|
||||
storage: {
|
||||
title: 'Хранилище',
|
||||
createBucket: 'Создать бакет',
|
||||
bucketName: 'Название бакета',
|
||||
bucketNamePlaceholder: 'my-bucket',
|
||||
create: 'Создать',
|
||||
cancel: 'Отмена',
|
||||
empty: 'У вас пока нет бакетов',
|
||||
emptyDescription: 'Создайте первый бакет для хранения файлов',
|
||||
},
|
||||
bucket: {
|
||||
files: 'Файлы',
|
||||
upload: 'Загрузить',
|
||||
uploadFiles: 'Загрузить файлы',
|
||||
uploadFolder: 'Загрузить папку',
|
||||
uploadFromUrl: 'Загрузить по URL',
|
||||
createFolder: 'Создать папку',
|
||||
delete: 'Удалить',
|
||||
download: 'Скачать',
|
||||
rename: 'Переименовать',
|
||||
copy: 'Копировать',
|
||||
move: 'Переместить',
|
||||
share: 'Поделиться',
|
||||
properties: 'Свойства',
|
||||
emptyBucket: 'Бакет пуст',
|
||||
emptyBucketDescription: 'Загрузите файлы или создайте папку',
|
||||
dropFilesHere: 'Перетащите файлы сюда',
|
||||
orClickToUpload: 'или нажмите для выбора',
|
||||
accessKey: 'Ключ доступа',
|
||||
secretKey: 'Секретный ключ',
|
||||
endpoint: 'Endpoint',
|
||||
region: 'Регион',
|
||||
},
|
||||
tickets: {
|
||||
title: 'Тикеты',
|
||||
create: 'Создать тикет',
|
||||
subject: 'Тема',
|
||||
message: 'Сообщение',
|
||||
priority: 'Приоритет',
|
||||
status: 'Статус',
|
||||
created: 'Создан',
|
||||
updated: 'Обновлён',
|
||||
open: 'Открыт',
|
||||
closed: 'Закрыт',
|
||||
pending: 'В ожидании',
|
||||
inProgress: 'В работе',
|
||||
low: 'Низкий',
|
||||
medium: 'Средний',
|
||||
high: 'Высокий',
|
||||
urgent: 'Срочный',
|
||||
noTickets: 'У вас нет тикетов',
|
||||
noTicketsDescription: 'Создайте тикет если вам нужна помощь',
|
||||
reply: 'Ответить',
|
||||
close: 'Закрыть тикет',
|
||||
reopen: 'Открыть заново',
|
||||
},
|
||||
billing: {
|
||||
title: 'Биллинг',
|
||||
balance: 'Баланс',
|
||||
topUp: 'Пополнить',
|
||||
history: 'История операций',
|
||||
date: 'Дата',
|
||||
description: 'Описание',
|
||||
amount: 'Сумма',
|
||||
noTransactions: 'Нет транзакций',
|
||||
uploadCheck: 'Загрузить чек',
|
||||
checkPending: 'На проверке',
|
||||
checkApproved: 'Одобрен',
|
||||
checkRejected: 'Отклонён',
|
||||
},
|
||||
settings: {
|
||||
title: 'Настройки',
|
||||
profile: 'Профиль',
|
||||
security: 'Безопасность',
|
||||
notifications: 'Уведомления',
|
||||
appearance: 'Внешний вид',
|
||||
language: 'Язык',
|
||||
timezone: 'Часовой пояс',
|
||||
save: 'Сохранить',
|
||||
changePassword: 'Сменить пароль',
|
||||
currentPassword: 'Текущий пароль',
|
||||
newPassword: 'Новый пароль',
|
||||
confirmNewPassword: 'Подтвердите новый пароль',
|
||||
deleteAccount: 'Удалить аккаунт',
|
||||
deleteAccountWarning: 'Это действие необратимо. Все ваши данные будут удалены.',
|
||||
},
|
||||
},
|
||||
|
||||
// Общие
|
||||
common: {
|
||||
loading: 'Загрузка...',
|
||||
error: 'Ошибка',
|
||||
success: 'Успешно',
|
||||
save: 'Сохранить',
|
||||
cancel: 'Отмена',
|
||||
delete: 'Удалить',
|
||||
edit: 'Редактировать',
|
||||
create: 'Создать',
|
||||
close: 'Закрыть',
|
||||
confirm: 'Подтвердить',
|
||||
back: 'Назад',
|
||||
next: 'Далее',
|
||||
previous: 'Назад',
|
||||
search: 'Поиск',
|
||||
filter: 'Фильтр',
|
||||
sort: 'Сортировка',
|
||||
refresh: 'Обновить',
|
||||
download: 'Скачать',
|
||||
upload: 'Загрузить',
|
||||
copy: 'Копировать',
|
||||
copied: 'Скопировано',
|
||||
yes: 'Да',
|
||||
no: 'Нет',
|
||||
or: 'или',
|
||||
and: 'и',
|
||||
of: 'из',
|
||||
items: 'элементов',
|
||||
bytes: 'байт',
|
||||
kb: 'КБ',
|
||||
mb: 'МБ',
|
||||
gb: 'ГБ',
|
||||
tb: 'ТБ',
|
||||
openMenu: 'Открыть меню',
|
||||
closeMenu: 'Закрыть меню',
|
||||
},
|
||||
|
||||
// Ошибки
|
||||
errors: {
|
||||
notFound: 'Страница не найдена',
|
||||
notFoundDescription: 'Запрашиваемая страница не существует или была удалена.',
|
||||
unauthorized: 'Не авторизован',
|
||||
unauthorizedDescription: 'Для доступа к этой странице необходимо войти в аккаунт.',
|
||||
forbidden: 'Доступ запрещён',
|
||||
forbiddenDescription: 'У вас нет прав для просмотра этой страницы.',
|
||||
serverError: 'Ошибка сервера',
|
||||
serverErrorDescription: 'Произошла внутренняя ошибка сервера. Попробуйте позже.',
|
||||
badGateway: 'Плохой шлюз',
|
||||
badGatewayDescription: 'Сервер временно недоступен. Попробуйте позже.',
|
||||
serviceUnavailable: 'Сервис недоступен',
|
||||
serviceUnavailableDescription: 'Сервис временно недоступен. Ведутся технические работы.',
|
||||
gatewayTimeout: 'Превышено время ожидания',
|
||||
gatewayTimeoutDescription: 'Сервер не ответил вовремя. Попробуйте позже.',
|
||||
goHome: 'На главную',
|
||||
tryAgain: 'Попробовать снова',
|
||||
},
|
||||
|
||||
// Футер
|
||||
footer: {
|
||||
description: 'Надёжное облачное S3 хранилище',
|
||||
links: 'Ссылки',
|
||||
legal: 'Правовая информация',
|
||||
terms: 'Условия использования',
|
||||
privacy: 'Политика конфиденциальности',
|
||||
contact: 'Контакты',
|
||||
copyright: '© 2024 ospab.host. Все права защищены.',
|
||||
},
|
||||
|
||||
// Блог
|
||||
blog: {
|
||||
title: 'Блог',
|
||||
subtitle: 'Статьи о хостинге, S3 и облачных технологиях',
|
||||
readMore: 'Читать далее',
|
||||
published: 'Опубликовано',
|
||||
author: 'Автор',
|
||||
tags: 'Теги',
|
||||
relatedPosts: 'Похожие статьи',
|
||||
noPosts: 'Пока нет статей',
|
||||
},
|
||||
|
||||
// Тарифы
|
||||
tariffs: {
|
||||
title: 'Тарифы S3 хранилища',
|
||||
subtitle: 'Выберите подходящий план для ваших задач',
|
||||
popular: 'Популярный',
|
||||
features: 'Возможности',
|
||||
storage: 'Хранилище',
|
||||
traffic: 'Исходящий трафик',
|
||||
requests: 'Запросов',
|
||||
support: 'Поддержка',
|
||||
api: 'S3 API',
|
||||
included: 'Включено',
|
||||
unlimited: 'Безлимитно',
|
||||
perMonth: '/месяц',
|
||||
selectPlan: 'Выбрать план',
|
||||
currentPlan: 'Текущий план',
|
||||
contactUs: 'Связаться с нами',
|
||||
customPlan: 'Нужен индивидуальный план?',
|
||||
customPlanDescription: 'Свяжитесь с нами для обсуждения особых условий.',
|
||||
},
|
||||
};
|
||||
|
||||
export type TranslationKeys = typeof ru;
|
||||
92
ospabhost/frontend/src/i18n/useTranslation.ts
Normal file
92
ospabhost/frontend/src/i18n/useTranslation.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useLocale } from '../middleware';
|
||||
import { ru, en, type TranslationKeys } from './translations';
|
||||
import type { Locale } from '../middleware/locale.utils';
|
||||
|
||||
// Словарь переводов
|
||||
const translations: Record<Locale, TranslationKeys> = {
|
||||
ru,
|
||||
en,
|
||||
};
|
||||
|
||||
type NestedKeyOf<T> = T extends object
|
||||
? {
|
||||
[K in keyof T]: K extends string
|
||||
? T[K] extends object
|
||||
? `${K}` | `${K}.${NestedKeyOf<T[K]>}`
|
||||
: `${K}`
|
||||
: never;
|
||||
}[keyof T]
|
||||
: never;
|
||||
|
||||
type TranslationKey = NestedKeyOf<TranslationKeys>;
|
||||
|
||||
/**
|
||||
* Получить значение по вложенному ключу
|
||||
*/
|
||||
function getNestedValue(obj: Record<string, unknown>, path: string): string {
|
||||
const keys = path.split('.');
|
||||
let current: unknown = obj;
|
||||
|
||||
for (const key of keys) {
|
||||
if (current && typeof current === 'object' && key in current) {
|
||||
current = (current as Record<string, unknown>)[key];
|
||||
} else {
|
||||
return path; // Возвращаем ключ если перевод не найден
|
||||
}
|
||||
}
|
||||
|
||||
return typeof current === 'string' ? current : path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Хук для получения переводов
|
||||
*/
|
||||
export function useTranslation() {
|
||||
const { locale, setLocale } = useLocale();
|
||||
|
||||
const t = useCallback(
|
||||
(key: TranslationKey, params?: Record<string, string | number>): string => {
|
||||
const translation = getNestedValue(
|
||||
translations[locale] as unknown as Record<string, unknown>,
|
||||
key
|
||||
);
|
||||
|
||||
if (!params) return translation;
|
||||
|
||||
// Замена параметров {{param}}
|
||||
return translation.replace(/\{\{(\w+)\}\}/g, (_, paramKey) => {
|
||||
return params[paramKey]?.toString() ?? `{{${paramKey}}}`;
|
||||
});
|
||||
},
|
||||
[locale]
|
||||
);
|
||||
|
||||
// Получить объект переводов для секции
|
||||
const tSection = useCallback(
|
||||
<K extends keyof TranslationKeys>(section: K): TranslationKeys[K] => {
|
||||
return translations[locale][section];
|
||||
},
|
||||
[locale]
|
||||
);
|
||||
|
||||
// Текущие переводы
|
||||
const translations_current = useMemo(() => translations[locale], [locale]);
|
||||
|
||||
return {
|
||||
t,
|
||||
tSection,
|
||||
locale,
|
||||
setLocale,
|
||||
translations: translations_current,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить перевод без хука (для использования вне компонентов)
|
||||
*/
|
||||
export function getTranslation(locale: Locale, key: string): string {
|
||||
return getNestedValue(translations[locale] as unknown as Record<string, unknown>, key);
|
||||
}
|
||||
|
||||
export type { TranslationKey, TranslationKeys };
|
||||
Reference in New Issue
Block a user