Reorganize web folder structurally
This commit is contained in:
@@ -0,0 +1,565 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export type Lang = 'ru' | 'en';
|
||||
|
||||
const LANG_KEY = 'knot_language';
|
||||
|
||||
const translations = {
|
||||
ru: {
|
||||
// Side menu
|
||||
myProfile: 'Мой профиль',
|
||||
settings: 'Настройки',
|
||||
aboutApp: 'О приложении',
|
||||
logout: 'Выйти из аккаунта',
|
||||
// Profile
|
||||
name: 'Имя',
|
||||
username: 'Имя пользователя',
|
||||
aboutMe: 'О себе',
|
||||
birthday: 'Дата рождения',
|
||||
enterName: 'Введите имя',
|
||||
tellAboutYourself: 'Расскажите о себе',
|
||||
removePhoto: 'Удалить фото',
|
||||
// Settings
|
||||
language: 'Язык / Language',
|
||||
interfaceLang: 'Язык интерфейса',
|
||||
theme: 'Оформление',
|
||||
russian: 'Русский',
|
||||
english: 'English',
|
||||
about: 'О приложении',
|
||||
version: 'Версия',
|
||||
modernMessenger: 'Современный мессенджер с фокусом',
|
||||
onPrivacy: 'на приватность и удобство',
|
||||
modernMessengerShort: 'Современный мессенджер',
|
||||
// Chat
|
||||
chat: 'Чат',
|
||||
group: 'Группа',
|
||||
searchChats: 'Поиск чатов...',
|
||||
noChats: 'Нет чатов — создайте первый!',
|
||||
nothingFound: 'Ничего не найдено',
|
||||
selectChat: 'Выберите чат для начала общения',
|
||||
noMessages: 'Нет сообщений — напишите первое!',
|
||||
typing: 'печатает...',
|
||||
online: 'в сети',
|
||||
wasRecently: 'был(а) недавно',
|
||||
members: 'участников',
|
||||
files: 'файлов',
|
||||
clearAll: 'Очистить всё',
|
||||
messagePlaceholder: 'Сообщение...',
|
||||
message: 'Сообщение...',
|
||||
addCaption: 'Добавьте подпись...',
|
||||
searchMessages: 'Поиск сообщений...',
|
||||
searchMessagesBtn: 'Поиск сообщений',
|
||||
userProfile: 'Профиль пользователя',
|
||||
enableSound: 'Включить звук',
|
||||
disableSound: 'Выключить звук',
|
||||
deleteChat: 'Удалить чат',
|
||||
// Messages
|
||||
messageDeleted: 'Сообщение удалено',
|
||||
reply: 'Ответить',
|
||||
copy: 'Копировать',
|
||||
edit: 'Редактировать',
|
||||
delete: 'Удалить',
|
||||
deleteForMe: 'Удалить у меня',
|
||||
deleteForAll: 'Удалить у всех',
|
||||
deleteAlsoFor: 'Удалить также для',
|
||||
editing: 'Редактирование',
|
||||
replyTo: 'Ответ',
|
||||
media: 'Медиа',
|
||||
download: 'Скачать',
|
||||
edited: 'ред.',
|
||||
selected: 'выбрано',
|
||||
fileLabel: 'Файл',
|
||||
kb: 'КБ',
|
||||
voice: '🎤 Голосовое',
|
||||
photo: '🖼 Фото',
|
||||
video: '🎬 Видео',
|
||||
file: 'Файл',
|
||||
// Call
|
||||
call: 'Звонок',
|
||||
videoCall: 'Видеозвонок',
|
||||
incomingCall: 'Входящий звонок',
|
||||
incomingVideoCall: 'Входящий видеозвонок',
|
||||
calling: 'Вызов...',
|
||||
accept: 'Принять',
|
||||
decline: 'Отклонить',
|
||||
endCall: 'Завершить',
|
||||
callEnded: 'Звонок завершён',
|
||||
callDeclined: 'Звонок отклонён',
|
||||
// Photo/video
|
||||
photoVideo: 'Фото / видео',
|
||||
fileBtn: 'Файл',
|
||||
sendError: 'Ошибка отправки',
|
||||
// New chat
|
||||
newChat: 'Новый чат',
|
||||
menu: 'Меню',
|
||||
// Date picker
|
||||
months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
|
||||
weekDays: ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'],
|
||||
clear: 'Очистить',
|
||||
today: 'Сегодня',
|
||||
// New chat
|
||||
newChatTitle: 'Новый чат',
|
||||
selectMembers: 'Выберите участников',
|
||||
newGroup: 'Новая группа',
|
||||
groupNamePlaceholder: 'Название группы...',
|
||||
membersCount: 'Участники',
|
||||
createGroup: 'Создать группу',
|
||||
upTo200: 'До 200 участников',
|
||||
findUser: 'Найти пользователя по имени или @username...',
|
||||
addMembers: 'Добавить участников...',
|
||||
usersNotFound: 'Пользователи не найдены',
|
||||
enterNameOrUsername: 'Введите имя или @username',
|
||||
next: 'Далее',
|
||||
pinMessage: 'Закрепить',
|
||||
unpinMessage: 'Открепить',
|
||||
pinnedMessage: 'Закреплённое сообщение',
|
||||
forwardMessage: 'Переслать сообщение',
|
||||
forward: 'Переслать',
|
||||
forwardedFrom: 'Переслано от',
|
||||
replyWithQuote: 'Ответить с цитатой',
|
||||
select: 'Выбрать',
|
||||
sending: 'Отправка...',
|
||||
scheduleMessage: 'Запланировать',
|
||||
scheduleSend: 'Отправить по расписанию',
|
||||
scheduled: 'Запланировано',
|
||||
myStory: 'Моя история',
|
||||
newStory: 'Новая история',
|
||||
textStory: 'Текст',
|
||||
imageStory: 'Фото',
|
||||
mediaStory: 'Медиа',
|
||||
typeYourStory: 'Напишите историю...',
|
||||
publishStory: 'Опубликовать',
|
||||
stories: 'Истории',
|
||||
clearChat: 'Очистить чат',
|
||||
clearChatConfirm: 'Очистить историю чата для себя? Собеседник сохранит свою историю.',
|
||||
deleteChatConfirm: 'Удалить чат? Это действие нельзя отменить.',
|
||||
pinChat: 'Закрепить чат',
|
||||
unpinChat: 'Открепить чат',
|
||||
chatCleared: 'Чат очищен',
|
||||
groupSettings: 'Настройки группы',
|
||||
editGroupName: 'Изменить название',
|
||||
addMember: 'Добавить участника',
|
||||
removeMember: 'Удалить из группы',
|
||||
leaveGroup: 'Покинуть группу',
|
||||
adminBadge: 'Админ',
|
||||
groupDescription: 'Описание',
|
||||
noDescription: 'Нет описания',
|
||||
memberBadge: 'Участник',
|
||||
screenShare: 'Демонстрация экрана',
|
||||
stopScreenShare: 'Остановить демонстрацию',
|
||||
switchCamera: 'Выбрать камеру',
|
||||
showInChat: 'Показать в чате',
|
||||
minimize: 'Свернуть',
|
||||
volume: 'Громкость',
|
||||
mute: 'Выключить микрофон',
|
||||
unmute: 'Включить микрофон',
|
||||
noiseSuppressionOn: 'Шумоподавление включено',
|
||||
noiseSuppressionOff: 'Шумоподавление выключено',
|
||||
selectMicrophone: 'Выбрать микрофон',
|
||||
microphone: 'Микрофон',
|
||||
rightClickVolume: 'ПКМ для регулировки громкости',
|
||||
participants: 'участников',
|
||||
joinCall: 'Присоединиться к звонку',
|
||||
activeCall: 'Активный звонок',
|
||||
groupNameRequired: 'Введите название группы',
|
||||
confirmRemoveMember: 'Удалить этого участника из группы?',
|
||||
yes: 'Да',
|
||||
no: 'Нет',
|
||||
you: 'вы',
|
||||
// User profile
|
||||
mediaTab: 'Медиа',
|
||||
gifs: 'GIF',
|
||||
filesTab: 'Файлы',
|
||||
linksTab: 'Ссылки',
|
||||
profileTitle: 'Профиль',
|
||||
removeAvatar: 'Удалить аватар',
|
||||
namePlaceholder: 'Имя',
|
||||
notSpecified: 'Не указано',
|
||||
onKnotSince: 'На Knot с',
|
||||
cancel: 'Отмена',
|
||||
confirm: 'Подтвердить',
|
||||
save: 'Сохранить',
|
||||
sharedPhotos: 'Общие фото и видео будут здесь',
|
||||
sharedFiles: 'Общие файлы будут здесь',
|
||||
sharedLinks: 'Общие ссылки будут здесь',
|
||||
profileNotFound: 'Профиль не найден',
|
||||
storiesTab: 'Истории',
|
||||
publicationsTab: 'Публикации',
|
||||
noStories: 'Публикаций пока нет',
|
||||
goToMessage: 'Перейти к сообщению',
|
||||
story: 'История',
|
||||
commonGroups: 'Общие группы',
|
||||
more: 'Ещё',
|
||||
// Typing
|
||||
typingText: 'печатает',
|
||||
// Emoji
|
||||
emojiFrequent: 'Часто',
|
||||
emojiGestures: 'Жесты',
|
||||
emojiEmotions: 'Эмоции',
|
||||
emojiObjects: 'Предметы',
|
||||
emojiFood: 'Еда',
|
||||
emojiNature: 'Природа',
|
||||
// Auth
|
||||
login: 'Вход',
|
||||
register: 'Регистрация',
|
||||
latinOnly: '(латиница, нельзя изменить)',
|
||||
displayNameLabel: 'Отображаемое имя',
|
||||
displayNamePlaceholder: 'Ваше имя (любой язык)',
|
||||
password: 'Пароль',
|
||||
passwordPlaceholder: 'Введите пароль',
|
||||
bioPlaceholder: 'Расскажите о себе (необязательно)',
|
||||
loginBtn: 'Войти',
|
||||
createAccount: 'Создать аккаунт',
|
||||
testAccounts: 'Тестовые аккаунты: evgeniy, anastasia, artem, polina',
|
||||
passwordForAll: 'Пароль для всех:',
|
||||
dontHaveAccount: 'Нет аккаунта?',
|
||||
alreadyHaveAccount: 'Уже есть аккаунт?',
|
||||
passwordRequirements: 'Минимум 8 символов, буквы и цифры',
|
||||
registerNow: 'Зарегистрироваться',
|
||||
loginNow: 'Войти',
|
||||
// File/upload
|
||||
fileTooLarge: 'Файл слишком большой (макс. 200МБ)',
|
||||
dropFileHere: 'Отпустите файл здесь',
|
||||
uploading: 'Загрузка...',
|
||||
uploadError: 'Ошибка загрузки файла',
|
||||
changePhoto: 'Сменить фото',
|
||||
remove: 'Удалить',
|
||||
// Formatting
|
||||
formatBold: 'Жирный',
|
||||
formatBoldHint: '**текст**',
|
||||
formatItalic: 'Курсив',
|
||||
formatItalicHint: '_текст_',
|
||||
formatStrike: 'Зачёркнут',
|
||||
formatStrikeHint: '~текст~',
|
||||
formatMono: 'Моноширинный',
|
||||
formatMonoHint: '`текст`',
|
||||
// Draft
|
||||
draft: 'Черновик:',
|
||||
// Date
|
||||
datePlaceholder: 'дд.мм.гггг',
|
||||
// GIF / Emoji
|
||||
tenorKeyRequired: 'Для GIF нужен Tenor API ключ',
|
||||
openConsoleRun: 'Откройте консоль и выполните:',
|
||||
searchGifs: 'Поиск GIF...',
|
||||
trending: 'Популярные',
|
||||
// Misc
|
||||
error: 'Ошибка',
|
||||
// Friends
|
||||
friends: 'Контакты',
|
||||
friendRequests: 'Заявки в контакты',
|
||||
friendsList: 'Список контактов',
|
||||
noFriends: 'Пока нет контактов',
|
||||
addFriend: 'Добавить в контакты',
|
||||
removeFriend: 'Удалить из контактов',
|
||||
requestSent: 'Заявка отправлена',
|
||||
searchFriends: 'Поиск по @username (мин. 3 символа)',
|
||||
noSearchResults: 'Пользователи не найдены',
|
||||
minCharsHint: 'Введите минимум 3 символа после @',
|
||||
// Story viewers
|
||||
storyViewers: 'Кто просмотрел',
|
||||
noViewers: 'Пока никто не посмотрел',
|
||||
replyToStory: 'Ответить на историю...',
|
||||
send: 'Отправить',
|
||||
// Favorites
|
||||
favorites: 'Избранное',
|
||||
favoritesDescription: 'Сохраняйте важные сообщения здесь',
|
||||
// Privacy
|
||||
privacy: 'Приватность',
|
||||
hideStoryViews: 'Скрывать просмотры историй',
|
||||
hideStoryViewsDesc: 'Другие не увидят, что вы смотрели их историю',
|
||||
// Scheduled
|
||||
scheduledFor: 'Запланировано на',
|
||||
messageScheduled: 'Сообщение запланировано',
|
||||
scheduledDelivered: 'Сообщение отправлено для',
|
||||
scheduledDeliveredAt: 'в',
|
||||
scheduleIn1h: 'Через 1 час',
|
||||
scheduleIn3h: 'Через 3 часа',
|
||||
scheduleTomorrow: 'Завтра в 9:00',
|
||||
scheduleCustom: 'Выбрать дату и время',
|
||||
scheduleTime: 'Время',
|
||||
scheduleDate: 'Дата',
|
||||
// Last seen
|
||||
lastSeenAt: 'был(а)',
|
||||
justNow: 'только что',
|
||||
loadStoriesError: 'Ошибка загрузки историй',
|
||||
loadChatsError: 'Ошибка загрузки чатов',
|
||||
loadMessagesError: 'Ошибка загрузки сообщений',
|
||||
unreadMessages: 'Непрочитанные сообщения',
|
||||
},
|
||||
en: {
|
||||
myProfile: 'My Profile',
|
||||
settings: 'Settings',
|
||||
aboutApp: 'About',
|
||||
logout: 'Log Out',
|
||||
name: 'Name',
|
||||
username: 'Username',
|
||||
aboutMe: 'About me',
|
||||
birthday: 'Birthday',
|
||||
enterName: 'Enter name',
|
||||
tellAboutYourself: 'Tell about yourself',
|
||||
removePhoto: 'Remove photo',
|
||||
language: 'Language',
|
||||
interfaceLang: 'Interface language',
|
||||
theme: 'Theme',
|
||||
russian: 'Русский',
|
||||
english: 'English',
|
||||
about: 'About',
|
||||
version: 'Version',
|
||||
modernMessenger: 'A modern messenger focused',
|
||||
onPrivacy: 'on privacy and convenience',
|
||||
modernMessengerShort: 'Knot Messenger',
|
||||
searchChats: 'Search chats...',
|
||||
chat: 'Chat',
|
||||
group: 'Group',
|
||||
noChats: 'No chats — create one!',
|
||||
nothingFound: 'Nothing found',
|
||||
selectChat: 'Select a chat to start messaging',
|
||||
noMessages: 'No messages — write the first one!',
|
||||
typing: 'typing...',
|
||||
online: 'online',
|
||||
wasRecently: 'was recently',
|
||||
members: 'members',
|
||||
files: 'files',
|
||||
clearAll: 'Clear all',
|
||||
messagePlaceholder: 'Message...',
|
||||
message: 'Message...',
|
||||
addCaption: 'Add a caption...',
|
||||
searchMessages: 'Search messages...',
|
||||
searchMessagesBtn: 'Search messages',
|
||||
userProfile: 'User profile',
|
||||
enableSound: 'Enable sound',
|
||||
disableSound: 'Mute',
|
||||
deleteChat: 'Delete chat',
|
||||
messageDeleted: 'Message deleted',
|
||||
reply: 'Reply',
|
||||
copy: 'Copy',
|
||||
edit: 'Edit',
|
||||
delete: 'Delete',
|
||||
deleteForMe: 'Delete for me',
|
||||
deleteForAll: 'Delete for everyone',
|
||||
deleteAlsoFor: 'Delete also for',
|
||||
editing: 'Editing',
|
||||
replyTo: 'Reply',
|
||||
media: 'Media',
|
||||
download: 'Download',
|
||||
edited: 'edit.',
|
||||
selected: 'selected',
|
||||
fileLabel: 'File',
|
||||
kb: 'KB',
|
||||
voice: '🎤 Voice',
|
||||
photo: '🖼 Photo',
|
||||
video: '🎬 Video',
|
||||
file: 'File',
|
||||
call: 'Call',
|
||||
videoCall: 'Video call',
|
||||
incomingCall: 'Incoming call',
|
||||
incomingVideoCall: 'Incoming video call',
|
||||
calling: 'Calling...',
|
||||
accept: 'Accept',
|
||||
decline: 'Decline',
|
||||
endCall: 'End call',
|
||||
callEnded: 'Call ended',
|
||||
callDeclined: 'Call declined',
|
||||
photoVideo: 'Photo / video',
|
||||
fileBtn: 'File',
|
||||
sendError: 'Send error',
|
||||
newChat: 'New chat',
|
||||
menu: 'Menu',
|
||||
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
weekDays: ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
|
||||
clear: 'Clear',
|
||||
today: 'Today',
|
||||
newChatTitle: 'New Chat',
|
||||
selectMembers: 'Select members',
|
||||
newGroup: 'New Group',
|
||||
groupNamePlaceholder: 'Group name...',
|
||||
membersCount: 'Members',
|
||||
createGroup: 'Create Group',
|
||||
upTo200: 'Up to 200 members',
|
||||
findUser: 'Find user by name or @username...',
|
||||
addMembers: 'Add members...',
|
||||
usersNotFound: 'Users not found',
|
||||
enterNameOrUsername: 'Enter name or @username',
|
||||
next: 'Next',
|
||||
pinMessage: 'Pin',
|
||||
unpinMessage: 'Unpin',
|
||||
pinnedMessage: 'Pinned message',
|
||||
forwardMessage: 'Forward message',
|
||||
forward: 'Forward',
|
||||
forwardedFrom: 'Forwarded from',
|
||||
replyWithQuote: 'Reply with quote',
|
||||
select: 'Select',
|
||||
sending: 'Sending...',
|
||||
scheduleMessage: 'Schedule',
|
||||
scheduleSend: 'Send scheduled',
|
||||
scheduled: 'Scheduled',
|
||||
myStory: 'My story',
|
||||
newStory: 'New story',
|
||||
textStory: 'Text',
|
||||
imageStory: 'Photo',
|
||||
mediaStory: 'Media',
|
||||
typeYourStory: 'Write your story...',
|
||||
publishStory: 'Publish',
|
||||
stories: 'Stories',
|
||||
clearChat: 'Clear chat',
|
||||
clearChatConfirm: 'Clear chat history for yourself? The other person will keep their history.',
|
||||
deleteChatConfirm: 'Delete this chat? This action cannot be undone.',
|
||||
pinChat: 'Pin chat',
|
||||
unpinChat: 'Unpin chat',
|
||||
chatCleared: 'Chat cleared',
|
||||
groupSettings: 'Group settings',
|
||||
editGroupName: 'Edit name',
|
||||
addMember: 'Add member',
|
||||
removeMember: 'Remove from group',
|
||||
leaveGroup: 'Leave group',
|
||||
adminBadge: 'Admin',
|
||||
groupDescription: 'Description',
|
||||
noDescription: 'No description',
|
||||
memberBadge: 'Member',
|
||||
screenShare: 'Screen share',
|
||||
stopScreenShare: 'Stop sharing',
|
||||
switchCamera: 'Switch camera',
|
||||
showInChat: 'Show in chat',
|
||||
minimize: 'Minimize',
|
||||
volume: 'Volume',
|
||||
mute: 'Mute',
|
||||
unmute: 'Unmute',
|
||||
noiseSuppressionOn: 'Noise suppression on',
|
||||
noiseSuppressionOff: 'Noise suppression off',
|
||||
selectMicrophone: 'Select microphone',
|
||||
microphone: 'Microphone',
|
||||
rightClickVolume: 'Right-click to adjust volume',
|
||||
participants: 'participants',
|
||||
joinCall: 'Join call',
|
||||
activeCall: 'Active call',
|
||||
groupNameRequired: 'Enter a group name',
|
||||
confirmRemoveMember: 'Remove this member from the group?',
|
||||
yes: 'Yes',
|
||||
no: 'No',
|
||||
you: 'you',
|
||||
mediaTab: 'Media',
|
||||
gifs: 'GIF',
|
||||
filesTab: 'Files',
|
||||
linksTab: 'Links',
|
||||
profileTitle: 'Profile',
|
||||
removeAvatar: 'Remove avatar',
|
||||
namePlaceholder: 'Name',
|
||||
notSpecified: 'Not specified',
|
||||
onKnotSince: 'On Knot since',
|
||||
cancel: 'Cancel',
|
||||
confirm: 'Confirm',
|
||||
save: 'Save',
|
||||
sharedPhotos: 'Shared photos and videos will appear here',
|
||||
sharedFiles: 'Shared files will appear here',
|
||||
sharedLinks: 'Shared links will appear here',
|
||||
profileNotFound: 'Profile not found',
|
||||
storiesTab: 'Stories',
|
||||
publicationsTab: 'Publications',
|
||||
noStories: 'No stories yet',
|
||||
goToMessage: 'Go to message',
|
||||
story: 'Story',
|
||||
commonGroups: 'Common groups',
|
||||
more: 'More',
|
||||
typingText: 'typing',
|
||||
emojiFrequent: 'Frequent',
|
||||
emojiGestures: 'Gestures',
|
||||
emojiEmotions: 'Emotions',
|
||||
emojiObjects: 'Objects',
|
||||
emojiFood: 'Food',
|
||||
emojiNature: 'Nature',
|
||||
login: 'Login',
|
||||
register: 'Register',
|
||||
latinOnly: '(latin only, cannot be changed)',
|
||||
displayNameLabel: 'Display name',
|
||||
displayNamePlaceholder: 'Your name (any language)',
|
||||
password: 'Password',
|
||||
passwordPlaceholder: 'Enter password',
|
||||
bioPlaceholder: 'Tell about yourself (optional)',
|
||||
loginBtn: 'Login',
|
||||
createAccount: 'Create account',
|
||||
testAccounts: 'Test accounts: evgeniy, anastasia, artem, polina',
|
||||
passwordForAll: 'Password for all:',
|
||||
dontHaveAccount: "Don't have an account?",
|
||||
alreadyHaveAccount: 'Already have an account?',
|
||||
passwordRequirements: 'At least 8 characters, letters and numbers',
|
||||
registerNow: 'Register',
|
||||
loginNow: 'Login',
|
||||
fileTooLarge: 'File too large (max 200MB)',
|
||||
dropFileHere: 'Drop file here',
|
||||
uploading: 'Uploading...',
|
||||
uploadError: 'File upload error',
|
||||
changePhoto: 'Change photo',
|
||||
remove: 'Remove',
|
||||
formatBold: 'Bold',
|
||||
formatBoldHint: '**text**',
|
||||
formatItalic: 'Italic',
|
||||
formatItalicHint: '_text_',
|
||||
formatStrike: 'Strikethrough',
|
||||
formatStrikeHint: '~text~',
|
||||
formatMono: 'Monospace',
|
||||
formatMonoHint: '`text`',
|
||||
draft: 'Draft:',
|
||||
datePlaceholder: 'dd.mm.yyyy',
|
||||
tenorKeyRequired: 'Tenor API key required for GIFs',
|
||||
openConsoleRun: 'Open console and run:',
|
||||
searchGifs: 'Search GIFs...',
|
||||
trending: 'Trending',
|
||||
error: 'Error',
|
||||
friends: 'Contacts',
|
||||
friendRequests: 'Contact requests',
|
||||
friendsList: 'Contacts list',
|
||||
noFriends: 'No contacts yet',
|
||||
addFriend: 'Add to contacts',
|
||||
removeFriend: 'Remove from contacts',
|
||||
requestSent: 'Request sent',
|
||||
searchFriends: 'Search by @username (min. 3 chars)',
|
||||
noSearchResults: 'No users found',
|
||||
minCharsHint: 'Enter at least 3 characters after @',
|
||||
storyViewers: 'Who viewed',
|
||||
noViewers: 'No one viewed yet',
|
||||
replyToStory: 'Reply to story...',
|
||||
send: 'Send',
|
||||
favorites: 'Favorites',
|
||||
favoritesDescription: 'Save important messages here',
|
||||
privacy: 'Privacy',
|
||||
hideStoryViews: 'Hide story views',
|
||||
hideStoryViewsDesc: 'Others won\'t see that you viewed their story',
|
||||
scheduledFor: 'Scheduled for',
|
||||
messageScheduled: 'Message scheduled',
|
||||
scheduledDelivered: 'Message sent to',
|
||||
scheduledDeliveredAt: 'at',
|
||||
scheduleIn1h: 'In 1 hour',
|
||||
scheduleIn3h: 'In 3 hours',
|
||||
scheduleTomorrow: 'Tomorrow at 9:00',
|
||||
scheduleCustom: 'Choose date & time',
|
||||
scheduleTime: 'Time',
|
||||
scheduleDate: 'Date',
|
||||
lastSeenAt: 'was',
|
||||
justNow: 'just now',
|
||||
loadStoriesError: 'Error loading stories',
|
||||
loadChatsError: 'Error loading chats',
|
||||
loadMessagesError: 'Error loading messages',
|
||||
unreadMessages: 'Unread messages',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type TranslationKey = keyof typeof translations.ru;
|
||||
type TranslationValue<K extends TranslationKey> = (typeof translations.ru)[K];
|
||||
|
||||
interface LangState {
|
||||
lang: Lang;
|
||||
setLang: (lang: Lang) => void;
|
||||
t: <K extends TranslationKey>(key: K) => TranslationValue<K>;
|
||||
}
|
||||
|
||||
export const useLang = create<LangState>((set, get) => ({
|
||||
lang: (localStorage.getItem(LANG_KEY) as Lang) || 'ru',
|
||||
setLang: (lang) => {
|
||||
localStorage.setItem(LANG_KEY, lang);
|
||||
set({ lang });
|
||||
},
|
||||
t: (key) => {
|
||||
const { lang } = get();
|
||||
return (translations[lang][key] ?? translations.ru[key] ?? key) as TranslationValue<typeof key>;
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user