Files
forkmessager/client-web/src/core/infrastructure/i18n.ts
2026-04-07 12:29:41 +03:00

812 lines
32 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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: 'Имя пользователя',
chats: 'Чаты',
calls: 'Звонки',
contacts: 'Контакты',
archive: 'Архив',
aboutMe: 'О себе',
birthday: 'Дата рождения',
enterName: 'Введите имя',
tellAboutYourself: 'Расскажите о себе',
removePhoto: 'Удалить фото',
cropPhoto: 'Обрезать фото',
saveChanges: 'Сохранить изменения',
displayName: 'Отображаемое имя',
// Settings
language: 'Язык / Language',
interfaceLang: 'Язык интерфейса',
theme: 'Оформление',
russian: 'Русский',
english: 'English',
about: 'О приложении',
version: 'Версия',
modernMessenger: 'Современный мессенджер с фокусом',
onPrivacy: 'на приватность и удобство',
modernMessengerShort: 'Современный мессенджер',
settingsDesc: 'Управляйте своим профилем и настройками приложения',
appSettings: 'Настройки приложения',
enterDisplayName: 'Как вас называть?',
tellAboutBio: 'Расскажите немного о себе...',
storageAndData: 'Хранилище и данные',
importTelegram: 'Импорт истории Telegram',
importTelegramDesc: 'Перенести сообщения и медиа из Telegram',
importSuccess: 'Импорт завершен',
importStarted: 'Импорт запущен в фоновом режиме',
importSelectArchive: 'Загрузите архив',
importSelectArchiveDesc: 'Экспортируйте чат из Telegram (HTML формат, вкл. медиа) и загрузите полученный ZIP архив.',
importLimit: 'Лимит: до 1 ГБ',
importSelectFile: 'Выбрать ZIP-архив',
importParticipants: 'Соответствие участников',
importParticipantsDesc: 'Назначьте участников из архива пользователям в Knot.',
importInArchive: 'В архиве',
importSelectContact: '-- Выберите контакт --',
importGroupName: 'Название чата',
importGroupNameHint: 'Например, Моя группа',
importStart: 'Запустить импорт',
importRestart: 'Начать заново',
importCompletedDesc: 'Мы успешно создали чат и начали перенос сообщений в фоновом режиме.',
// Chat
chat: 'Чат',
group: 'Группа',
searchChats: 'Поиск чатов...',
noChats: 'Нет чатов — создайте первый!',
nothingFound: 'Ничего не найдено',
selectChat: 'Выберите чат для начала общения',
selectChatTitle: 'Выберите чат',
selectChatSubtext: 'Начните общение прямо сейчас. Все ваши сообщения защищены сквозным шифрованием.',
newMessage: 'Новое сообщение',
noMessages: 'Нет сообщений — напишите первое!',
typing: 'печатает...',
online: 'в сети',
wasRecently: 'был(а) недавно',
members: 'участников',
files: 'файлов',
clearAll: 'Очистить всё',
maxFilesLimit: 'Максимум 20 файлов',
searchingHistory: 'Поиск сообщения в истории...',
messageNotFound: 'Сообщение не найдено',
messagePlaceholder: 'Сообщение...',
message: 'Сообщение...',
addCaption: 'Добавьте подпись...',
searchMessages: 'Поиск сообщений...',
searchMessagesBtn: 'Поиск сообщений',
userProfile: 'Профиль пользователя',
userProfileUpper: 'ПРОФИЛЬ ПОЛЬЗОВАТЕЛЯ',
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: 'Вызов...',
isCalling: 'звонит...',
groupCall: 'Групповой звонок',
comingSoon: 'Скоро будет',
accept: 'Принять',
decline: 'Отклонить',
endCall: 'Завершить',
callEnded: 'Звонок завершён',
callDeclined: 'Звонок отклонён',
audioCall: 'Голосовой звонок',
missedCall: 'Пропущенный звонок',
declinedCall: 'Отклонённый звонок',
cancelledCall: 'Отменённый звонок',
completedCall: 'Вызов завершён',
// Photo/video
photoVideo: 'Фото / видео',
fileBtn: 'Файл',
sendError: 'Ошибка отправки',
// New chat
newChat: 'Новый чат',
add: 'Добавить',
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: 'Закреплённое сообщение',
poll: 'Опрос',
pollTab: 'Опросы',
createPoll: 'Создать опрос',
pollQuestion: 'Вопрос',
pollQuestionPlaceholder: 'Задайте вопрос...',
pollOptions: 'Варианты ответа',
pollOption: 'Вариант',
addOption: 'Добавить вариант',
pollSettings: 'Настройки',
anonymousVoting: 'Анонимное голосование',
multipleAnswers: 'Выбор нескольких вариантов',
singleAnswer: 'Одиночный выбор',
anonymous: 'Анонимно',
votes: 'голосов',
pollButton: 'Опрос',
forwardMessage: 'Переслать сообщение',
forward: 'Переслать',
forwardedFrom: 'Переслано от',
replyWithQuote: 'Ответить с цитатой',
select: 'Выбрать',
sending: 'Отправка...',
scheduleMessage: 'Запланировать',
scheduleSend: 'Отправить по расписанию',
scheduled: 'Запланировано',
myStory: 'Моя история',
newStory: 'Новая история',
textStory: 'Текст',
imageStory: 'Фото',
mediaStory: 'Медиа',
typeYourStory: 'Напишите историю...',
publishStory: 'Опубликовать',
stories: 'Истории',
clearChat: 'Очистить чат',
clearChatConfirm: 'Очистить историю чата для себя? Собеседник сохранит свою историю.',
clearHistory: 'Очистить историю',
clearHistoryConfirm: 'Очистить историю?',
deleteChatConfirm: 'Удалить чат? Это действие нельзя отменить.',
pinChat: 'Закрепить чат',
unpinChat: 'Открепить чат',
chatCleared: 'Очищено',
typeYourStoryPlaceholder: 'Напишите историю...',
uploadMedia: 'Загрузить медиа',
chooseBackground: 'Цвет фона',
viewedBy: 'Просмотрели',
storyDetails: 'Информация',
viewCountSuffix: 'посмотрели эту историю',
storyPrivacyNote: 'Истории зашифрованы и исчезнут через 24 часа. Сообщения о прочтении видны только автору.',
storiesEditor: 'Редактор историй',
tools: 'Инструменты',
privacyNote: 'Выберите, кто может просматривать вашу новую историю.',
allUsers: 'Все пользователи',
onlyContacts: 'Только контакты',
selectedContacts: 'Выбранные контакты',
yourStory: 'Ваша история',
stickers: 'Стикеры',
brush: 'Кисть',
filters: 'Фильтры',
report: 'Пожаловаться',
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: 'вы',
// Stories Editor
cropVideo: 'Обрезка видео',
cropTool: 'Обрезка',
trimVideo: 'Обрезать видео',
trim: 'Обрезать',
reset: 'Сбросить',
startCreation: 'Начать создание',
brushTool: 'Кисти',
textTool: 'Текст',
stickersTool: 'Стикеры',
filtersTool: 'Коррекция',
textContent: 'Контент',
typography: 'Типографика',
textSize: 'Размер',
textColor: 'Цвет текста',
brushThickness: 'Толщина',
clearCanvas: 'Очистить холст',
correctionMaster: 'Мастер коррекции',
brightness: 'Яркость',
contrast: 'Контраст',
saturation: 'Насыщенность',
sepia: 'Сепия',
bgColor: 'Цвет фона',
clickToChooseColor: 'Нажмите для выбора цвета',
done: 'Готово',
stop: 'Остановить',
zoomHint: 'Колесико мыши для зума',
secSelected: 'сек выбрано',
muted: 'Без звука',
unmuted: 'Со звуком',
muteVideo: 'Отключить звук',
unmuteVideo: 'Включить звук',
interactive: 'Интерактив',
apply: 'Применить',
zoom: 'Масштаб',
rotation: 'Поворот',
// User profile
mediaTab: 'Медиа',
gifs: 'GIF',
filesTab: 'Файлы',
linksTab: 'Ссылки',
profileTitle: 'Профиль',
removeAvatar: 'Удалить аватар',
namePlaceholder: 'Имя',
notSpecified: 'Не указано',
onKnotSince: 'На Knot с',
joined: 'С нами с',
cancel: 'Отмена',
confirm: 'Подтвердить',
save: 'Сохранить',
sharedPhotos: 'Общие фото и видео будут здесь',
sharedFiles: 'Общие файлы будут здесь',
sharedLinks: 'Общие ссылки будут здесь',
profileNotFound: 'Профиль не найден',
storiesTab: 'Истории',
publicationsTab: 'Публикации',
noStories: 'Публикаций пока нет',
noBio: 'Информация отсутствует',
goToMessage: 'Перейти к сообщению',
story: 'История',
commonGroups: 'Общие группы',
more: 'Ещё',
empty: 'Пусто',
acceptRequest: 'Принять запрос',
// 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 символа)',
searchResults: 'Результаты поиска',
noSearchResults: 'Пользователи не найдены',
minCharsHint: 'Введите минимум 3 символа после @',
selectContactToChat: 'Выберите контакт из списка, чтобы начать общение, или воспользуйтесь поиском для поиска новых людей.',
backToChats: 'Назад к чатам',
// 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: 'Непрочитанные сообщения',
attachmentDiscardConfirm: 'У вас есть прикрепленные вложения. Если вы перейдете в другой чат, они будут потеряны. Продолжить?',
},
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',
cropPhoto: 'Crop Photo',
saveChanges: 'Save Changes',
displayName: 'Display Name',
language: 'Language',
chats: 'Chats',
calls: 'Calls',
contacts: 'Contacts',
archive: 'Archive',
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',
settingsDesc: 'Manage your profile and app settings',
appSettings: 'App Settings',
enterDisplayName: 'How should we call you?',
tellAboutBio: 'Tell us a bit about yourself...',
storageAndData: 'Storage and Data',
importTelegram: 'Import Telegram History',
importTelegramDesc: 'Move messages and media from Telegram',
importSuccess: 'Import completed',
importStarted: 'Import started in background',
importSelectArchive: 'Upload archive',
importSelectArchiveDesc: 'Export chat from Telegram (HTML format, incl. media) and upload the resulting ZIP archive.',
importLimit: 'Limit: up to 1 GB',
importSelectFile: 'Select ZIP archive',
importParticipants: 'Participant mapping',
importParticipantsDesc: 'Assign participants from the archive to Knot users.',
importInArchive: 'In archive',
importSelectContact: '-- Select contact --',
importGroupName: 'Chat name',
importGroupNameHint: 'e.g., My Group',
importStart: 'Start import',
importRestart: 'Start over',
importCompletedDesc: 'We successfully created the chat and started importing messages in the background.',
searchChats: 'Search chats...',
chat: 'Chat',
group: 'Group',
noChats: 'No chats — create one!',
nothingFound: 'Nothing found',
selectChat: 'Select a chat to start messaging',
selectChatTitle: 'Select a chat',
selectChatSubtext: 'Start communicating right now. All your messages are protected with end-to-end encryption.',
newMessage: 'New message',
noMessages: 'No messages — write the first one!',
typing: 'typing...',
online: 'online',
wasRecently: 'was recently',
members: 'members',
files: 'files',
clearAll: 'Clear all',
maxFilesLimit: 'Maximum 20 files',
searchingHistory: 'Searching history...',
messageNotFound: 'Message not found',
messagePlaceholder: 'Message...',
message: 'Message...',
addCaption: 'Add a caption...',
searchMessages: 'Search messages...',
searchMessagesBtn: 'Search messages',
userProfile: 'User profile',
userProfileUpper: '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...',
isCalling: 'is calling...',
groupCall: 'Group call',
comingSoon: 'Coming soon',
accept: 'Accept',
decline: 'Decline',
endCall: 'End call',
callEnded: 'Call ended',
callDeclined: 'Call declined',
audioCall: 'Audio call',
missedCall: 'Missed call',
declinedCall: 'Declined call',
cancelledCall: 'Cancelled call',
completedCall: 'Call completed',
photoVideo: 'Photo / video',
fileBtn: 'File',
sendError: 'Send error',
newChat: 'New chat',
add: 'Add',
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.',
clearHistory: 'Clear history',
clearHistoryConfirm: 'Clear history?',
deleteChatConfirm: 'Delete this chat? This action cannot be undone.',
pinChat: 'Pin chat',
unpinChat: 'Unpin chat',
chatCleared: 'Chat cleared',
poll: 'Poll',
pollTab: 'Polls',
createPoll: 'Create Poll',
pollQuestion: 'Question',
pollQuestionPlaceholder: 'Ask a question...',
pollOptions: 'Options',
pollOption: 'Option',
addOption: 'Add Option',
pollSettings: 'Settings',
anonymousVoting: 'Anonymous Voting',
multipleAnswers: 'Multiple Answers',
singleAnswer: 'Single Answer',
anonymous: 'Anonymous',
votes: 'votes',
pollButton: 'Poll',
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',
// Stories Editor
cropVideo: 'Video Crop',
cropTool: 'Crop',
trimVideo: 'Trim Video',
trim: 'Trim',
reset: 'Reset',
startCreation: 'Start Creation',
brushTool: 'Brush',
textTool: 'Text',
stickersTool: 'Stickers',
filtersTool: 'Filters',
textContent: 'Content',
typography: 'Typography',
textSize: 'Size',
textColor: 'Color',
brushThickness: 'Thickness',
clearCanvas: 'Clear Canvas',
correctionMaster: 'Correction Master',
brightness: 'Brightness',
contrast: 'Contrast',
saturation: 'Saturation',
sepia: 'Sepia',
bgColor: 'Background',
clickToChooseColor: 'Click to choose color',
done: 'Done',
stop: 'Stop',
zoomHint: 'Mouse wheel to zoom',
secSelected: 'sec selected',
muted: 'Muted',
unmuted: 'Unmuted',
muteVideo: 'Mute Video',
unmuteVideo: 'Unmute Video',
interactive: 'Interactive',
apply: 'Apply',
zoom: 'Zoom',
rotation: 'Rotation',
// User profile
mediaTab: 'Media',
gifs: 'GIF',
filesTab: 'Files',
linksTab: 'Links',
profileTitle: 'Profile',
removeAvatar: 'Remove avatar',
namePlaceholder: 'Name',
notSpecified: 'Not specified',
onKnotSince: 'On Knot since',
joined: 'Joined',
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',
noBio: 'No information shared yet',
goToMessage: 'Go to message',
story: 'Story',
commonGroups: 'Common groups',
more: 'More',
empty: 'Empty',
acceptRequest: 'Accept Request',
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: 'mm/dd/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)',
searchResults: 'Search Results',
noSearchResults: 'No users found',
minCharsHint: 'Enter at least 3 characters after @',
selectContactToChat: 'Select a contact from the list to start a conversation or use search to find new people.',
backToChats: 'Back to Chats',
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',
typeYourStoryPlaceholder: 'Write your story...',
uploadMedia: 'Upload media',
chooseBackground: 'Background',
viewedBy: 'Viewed by',
storyDetails: 'Details',
viewCountSuffix: 'people viewed this story',
storyPrivacyNote: 'Stories are encrypted and disappear in 24 hours. View receipts are only visible to you.',
storiesEditor: 'Stories Editor',
tools: 'Tools',
privacyNote: 'Choose who can view your new story.',
allUsers: 'All users',
onlyContacts: 'Only contacts',
selectedContacts: 'Selected contacts',
yourStory: 'Your story',
stickers: 'Stickers',
brush: 'Brush',
filters: 'Filters',
report: 'Report',
attachmentDiscardConfirm: 'You have attached files. If you switch to another chat, they will be lost. Continue?',
},
} as const;
interface LangState {
lang: Lang;
setLang: (lang: Lang) => void;
t: (key: keyof typeof translations['ru']) => any;
}
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;
},
}));