package i18n type Lang string const ( RU Lang = "ru" EN Lang = "en" ) func Normalize(code string) Lang { switch code { case "en", "EN": return EN default: return RU } } var translations = map[Lang]map[string]string{ RU: { // Навигация и общее "app.tagline": "Лёгкий self-hosted Git-хостинг", "nav.home": "Главная", "nav.repositories": "Репозитории", "nav.explore": "Обзор", "nav.settings": "Настройки", "nav.admin": "Админка", "nav.agents": "Для AI-агентов", "nav.login": "Войти", "nav.register": "Регистрация", "nav.logout": "Выйти", "nav.new_repo": "Создать", "nav.command_palette": "Поиск и команды", "nav.search_placeholder": "Поиск...", "nav.theme": "Тема", "nav.lang": "Язык", "common.copy": "Копировать", "common.copied": "Скопировано", "common.cancel": "Отмена", "common.save": "Сохранить", "common.delete": "Удалить", "common.close": "Закрыть", "common.never": "Никогда", "common.newer": "Новее", "common.older": "Старее", "common.up": "Наверх", // Темы (только 3) "theme.light": "Светлая", "theme.dark": "Тёмная", "theme.system": "Авто", // Лендинг "landing.badge": "Один бинарник · SQLite · Ноль зависимостей", "landing.hero.title": "Лёгкий self-hosted Git-хостинг", "landing.hero.subtitle": "Один бинарник на Go со встроенным SQLite. Поднимается за минуту, потребляет минимум памяти и не перегружен лишним.", "landing.hero.cta_start": "Начать", "landing.features.title": "Принципы", "landing.feat1.title": "Один бинарник", "landing.feat1.desc": "Шаблоны, стили, шрифты, миграции и иконки вшиты через go:embed.", "landing.feat2.title": "SQLite (WAL)", "landing.feat2.desc": "Вся база — один файл data/polar.db без CGO. Бэкап простым копированием.", "landing.feat3.title": "Быстрый отклик", "landing.feat3.desc": "Мгновенный старт, серверный рендеринг и < 30 МБ памяти в простое.", "landing.feat4.title": "Приватность", "landing.feat4.desc": "Публичные и приватные репозитории, Argon2id и токены доступа для Git HTTP.", // Авторизация "auth.login.title": "Вход", "auth.register.title": "Регистрация", "auth.register.disabled": "Публичная регистрация отключена администратором.", "auth.username": "Имя пользователя", "auth.username_hint": "2–32 символа: латиница, цифры, дефис, подчёркивание", "auth.username_or_email": "Логин или Email", "auth.email": "Email", "auth.password": "Пароль", "auth.password_hint": "Минимум 8 символов", "auth.submit_login": "Войти", "auth.submit_register": "Создать аккаунт", "auth.no_account": "Нет аккаунта?", "auth.have_account": "Уже есть аккаунт?", "auth.invalid_creds": "Неверный логин или пароль", // Репозитории "repo.new.title": "Новый репозиторий", "repo.name": "Название", "repo.name_hint": "Латинские буквы, цифры, точка, дефис, подчёркивание", "repo.description": "Описание", "repo.description_ph": "Краткое описание проекта (необязательно)", "repo.visibility": "Доступ", "repo.public": "Публичный", "repo.public_desc": "Виден всем, клонирование без авторизации", "repo.private": "Приватный", "repo.private_desc": "Доступен только владельцу", "repo.create_btn": "Создать репозиторий", "repo.filter_ph": "Фильтр репозиториев...", "repo.empty_list": "Репозиториев пока нет", "repo.empty_list_sub": "Создайте первый репозиторий, чтобы отправить код.", "repo.empty.title": "Репозиторий пуст", "repo.empty.new_cmd": "Создать новый репозиторий в командной строке", "repo.empty.push_cmd": "Отправить существующий репозиторий", "repo.tab.code": "Код", "repo.tab.commits": "Коммиты", "repo.tab.branches": "Ветки и теги", "repo.tab.issues": "Задачи", "repo.tab.pulls": "Пул-реквесты", "repo.tab.releases": "Релизы", "repo.tab.activity": "Активность", "repo.tab.settings": "Настройки", "repo.features": "Возможности репозитория", "repo.clone": "Клонировать", "repo.download_zip": "Скачать ZIP", "repo.branches": "Ветки", "repo.tags": "Теги", "repo.default_branch": "Ветка по умолчанию", "repo.find_file": "Поиск файла...", "repo.files": "Файлы", "repo.readme": "README", "repo.raw": "Raw", "repo.history": "История", "repo.lines": "строк", "repo.lines_one": "строка", "repo.lines_few": "строки", "repo.lines_many": "строк", "repo.binary_notice": "Бинарный файл — предпросмотр недоступен", "repo.download_raw": "Скачать файл", "repo.browse_at_commit": "Файлы на момент коммита", "repo.files_changed": "файлов изменено", "repo.files_changed_one": "файл изменён", "repo.files_changed_few": "файла изменено", "repo.files_changed_many": "файлов изменено", "repo.no_commits": "Коммитов пока нет", "repo.danger_zone": "Опасная зона", "repo.delete": "Удалить репозиторий", "repo.delete_desc": "Удаление необратимо: все файлы и история git будут стёрты.", "repo.delete_confirm": "Введите название репозитория для подтверждения", "repo.rename": "Переименовать", "repo.save": "Сохранить", "repo.branch.new": "Новая ветка", "repo.branch.name_ph": "Имя ветки (напр. feat/ui)", "repo.branch.from": "От ветки / коммита", "repo.branch.create": "Создать ветку", "repo.branch.default": "по умолчанию", "repo.tag.new": "Новый тег / релиз", "repo.tag.name_ph": "Тег (напр. v1.0.0)", "repo.tag.msg_ph": "Описание тега (необязательно)", "repo.tag.create": "Создать тег", "repo.tags.empty": "Тегов пока нет", // Pull Requests "pulls.title": "Пул-реквесты", "pulls.new": "Новый пул-реквест", "pulls.empty": "Пул-реквестов пока нет", "pulls.open": "Открыт", "pulls.closed": "Закрыт", "pulls.merged": "Слит", "pulls.compare": "Сравнение веток", "pulls.base": "Базовая ветка", "pulls.head": "Ветка изменений", "pulls.title_ph": "Заголовок пул-реквеста...", "pulls.desc_ph": "Опишите предлагаемые изменения (Markdown)...", "pulls.create_btn": "Создать пул-реквест", "pulls.merge_btn": "Слить пул-реквест", "pulls.can_merge": "Ветки можно объединить автоматически.", "pulls.cannot_merge": "Невозможно объединить автоматически: обнаружены конфликты.", "pulls.already_merged": "Ветка изменений уже объединена с базовой.", "pulls.close_btn": "Закрыть PR", "pulls.reopen_btn": "Открыть заново", "pulls.comment_btn": "Прокомментировать", "pulls.conversation": "Обсуждение", "pulls.files_changed": "Изменения", "pulls.commits": "Коммиты", "pulls.able_to_merge": "Можно слить автоматически", "pulls.body": "Описание", "pulls.body_ph": "Опишите предлагаемые изменения (Markdown)...", "pulls.branches_identical": "Ветки идентичны — сравнивать нечего", "pulls.cannot_merge_warning": "Обнаружены конфликты — автоматическое слияние невозможно", "pulls.closed_desc": "Ветка изменений не была объединена с базовой.", "pulls.closed_message": "Пул-реквест закрыт", "pulls.comment_ph": "Напишите комментарий (поддерживается Markdown)...", "pulls.compare_branches": "Сравнение веток", "pulls.conflicts_desc": "Разрешите конфликты локально и запушьте изменения в ветку.", "pulls.conflicts_title": "Есть конфликты слияния", "pulls.into": "в", "pulls.merge_clean": "Конфликтов нет — изменения можно объединить.", "pulls.merged_message": "Пул-реквест слит", "pulls.no_changes": "Файлы не изменены", "pulls.no_commits": "В этом пул-реквесте пока нет коммитов", "pulls.only_write_merge": "Сливать пул-реквесты могут только пользователи с правом записи.", "pulls.status.closed": "Закрыт", "pulls.status.merged": "Слит", "pulls.status.open": "Открыт", "pulls.wants_to_merge": "хочет слить", // Releases "releases.title": "Релизы", "releases.new": "Новый релиз", "releases.assets_upload": "Файлы релиза", "releases.assets_upload_hint": "Необязательно. До 20 файлов, каждый до 100 МБ.", "releases.asset_delete": "Удалить файл", "releases.asset_delete_confirm": "Удалить этот файл? Действие необратимо.", "releases.empty": "Релизов пока нет", "releases.tag_select": "Тег", "releases.title_ph": "Название релиза", "releases.desc_ph": "Описание релиза, список изменений (Markdown)...", "releases.prerelease": "Пре-релиз (нестабильная сборка)", "releases.publish": "Опубликовать релиз", "releases.delete": "Удалить релиз", "releases.assets": "Файлы", "releases.source_zip": "Исходный код (zip)", "releases.source_tar": "Исходный код (tar.gz)", "releases.body": "Описание", "releases.body_ph": "Опишите релиз, список изменений (Markdown)...", "releases.count": "релизов", "releases.count_one": "релиз", "releases.count_few": "релиза", "releases.count_many": "релизов", "releases.delete_confirm": "Удалить этот релиз? Действие необратимо.", "releases.is_prerelease": "Пре-релиз (нестабильная сборка)", "releases.publish_btn": "Опубликовать релиз", "releases.tag_desc": "Существующий тег или новый — он будет создан автоматически.", "releases.tag_name": "Тег", "releases.tags": "тегов", "releases.tags_one": "тег", "releases.tags_few": "тега", "releases.tags_many": "тегов", "releases.target": "Ветка", "releases.target_desc": "Ветка или коммит, на который будет указывать тег.", // Activity "activity.title": "Активность", "activity.pulse": "Сводка активности", "activity.empty": "Активности пока нет", "activity.commits": "коммитов", "activity.open_prs": "открытых PR", "activity.merged_prs": "слитых PR", "activity.open_issues": "открытых задач", "activity.releases": "релизов", "activity.issues": "открыто и закрыто", "activity.on_default_branch": "в ветке по умолчанию", "activity.published_releases": "опубликованные релизы", "activity.pull_requests": "открыто и слито", "activity.recent_feed": "Лента активности", "activity.badge_commit": "коммит", "activity.badge_release": "релиз", // Задачи (Issues) "issue.new": "Новая задача", "issue.title": "Заголовок", "issue.title_ph": "Кратко опишите задачу или проблему", "issue.body": "Описание", "issue.body_ph": "Подробное описание (поддерживается Markdown)...", "issue.create": "Создать задачу", "issue.open": "Открытые", "issue.closed": "Закрытые", "issue.status.open": "Открыта", "issue.status.closed": "Закрыта", "issue.close_btn": "Закрыть задачу", "issue.reopen_btn": "Переоткрыть", "issue.comment_ph": "Напишите комментарий (поддерживается Markdown)...", "issue.comment_btn": "Отправить", "issue.empty": "Задач в этом списке нет", // Настройки пользователя, профиль и токены "settings.title": "Настройки", "settings.profile": "Профиль", "settings.avatar": "Аватар", "settings.avatar_upload": "Выбрать изображение", "settings.avatar_remove": "Удалить аватар", "settings.avatar_hint": "PNG, JPG, WebP или GIF — автоматически кадрируется до 256×256", "settings.full_name": "Отображаемое имя", "settings.full_name_ph": "Ваше имя или ник", "settings.bio": "О себе", "settings.bio_ph": "Короткая подпись в профиле", "settings.location": "Локация", "settings.location_ph": "Город, страна", "settings.website": "Сайт", "settings.website_ph": "https://example.com", "profile.edit": "Редактировать профиль", "profile.joined": "Регистрация", "profile.public_repos": "Публичные", "profile.private_repos": "Приватные", "profile.total_repos": "Репозитории", "settings.tokens": "Токены доступа", "settings.tokens.desc": "Используйте токен вместо пароля для git clone и git push по HTTP.", "settings.token.name": "Название токена (например, Ноутбук)", "settings.token.create": "Создать", "settings.token.revoke": "Отозвать", "settings.token.created_once": "Скопируйте токен сейчас — он показывается только один раз:", "settings.token.col_name": "Название", "settings.token.col_prefix": "Префикс", "settings.token.col_created": "Создан", "settings.token.col_used": "Использован", "settings.token.empty": "Токенов доступа пока нет", "settings.password.current": "Текущий пароль", "settings.password.new": "Новый пароль", "settings.password.update": "Обновить пароль", // Админка "admin.title": "Администрирование", "admin.features": "Модули и доступ", "admin.users": "Пользователи", "admin.registration": "Публичная регистрация", "admin.registration.desc": "Разрешить самостоятельную регистрацию новых аккаунтов", "admin.explore": "Раздел «Обзор» (Explore)", "admin.explore.desc": "Показывать каталог публичных репозиториев в сайдбаре и по адресу /explore", "admin.landing": "Приветственная страница", "admin.landing.desc": "Показывать лендинг на главной странице (если отключено — переход сразу к репозиториям или входу)", "admin.anon_read": "Гостевой просмотр", "admin.anon_read.desc": "Разрешить неавторизованным пользователям просматривать публичные репозитории и профили", "admin.issues": "Трекер задач (Issues)", "admin.issues.desc": "Включить вкладку «Задачи» в репозиториях", "admin.enabled": "Включено", "admin.disabled": "Отключено", "admin.enable_btn": "Включить", "admin.disable_btn": "Отключить", "admin.col.user": "Пользователь", "admin.col.repos": "Репозитории", "admin.col.role": "Роль", "admin.col.joined": "Дата", "admin.role.admin": "Админ", "admin.role.user": "Участник", // Командная палитра и хоткеи "palette.placeholder": "Поиск репозитория, файла или команды...", "palette.group.nav": "Навигация", "palette.group.themes": "Тема оформления", "palette.group.repos": "Репозитории", "shortcuts.title": "Горячие клавиши", "shortcuts.palette": "Командная палитра", "shortcuts.search": "Фокус на поиск", "shortcuts.home": "На главную", "shortcuts.files": "Фильтр файлов", "shortcuts.sidebar": "Скрыть / показать сайдбар", "nav.toggle_sidebar": "Сайдбар (Ctrl+B)", // Флеш-сообщения "flash.welcome": "Добро пожаловать в Polar, @%s!", "flash.profile_updated": "Профиль обновлён.", "flash.password_updated": "Пароль успешно изменён.", "flash.token_revoked": "Токен доступа отозван.", "flash.settings_updated": "Настройки сохранены.", "flash.error": "Ошибка: %s", "flash.comment_error": "Не удалось отправить комментарий: %s", "flash.pr_created": "Пул-реквест #%d создан.", "flash.pr_closed": "Пул-реквест #%d закрыт.", "flash.pr_reopened": "Пул-реквест #%d открыт заново.", "flash.pr_merged": "Пул-реквест #%d слит.", "flash.pr_merged_state": "Нельзя изменить состояние слитого пул-реквеста.", "flash.pr_merge_open_only": "Слить можно только открытый пул-реквест.", "flash.pr_merge_failed": "Не удалось слить: %s", "flash.release_tag_empty": "Имя тега не может быть пустым.", "flash.release_tag_failed": "Не удалось создать тег: %s", "flash.release_create_failed": "Не удалось создать релиз: %s", "flash.release_published": "Релиз %s опубликован.", "flash.release_delete_failed": "Не удалось удалить релиз: %s", "flash.release_deleted": "Релиз удалён.", "flash.release_published_with_assets": "Релиз %s опубликован, файлов прикреплено: %d.", "flash.release_published_assets_failed": "Релиз %s опубликован, но часть файлов не удалось сохранить.", "flash.release_asset_too_many": "Слишком много файлов: максимум %d.", "flash.release_asset_too_big": "Файл слишком большой — лимит 100 МБ на файл.", "flash.release_asset_deleted": "Файл удалён.", "flash.release_asset_delete_failed": "Не удалось удалить файл: %s", "file.new_file": "Новый файл", "file.new_folder": "Новая папка", "file.path": "Путь", "file.path_ph": "напр. src/main.go", "file.folder_ph": "напр. docs", "file.branch": "Ветка", "file.content": "Содержимое", "file.content_ph": "Текст файла…", "file.message": "Сообщение коммита", "file.create_file": "Создать файл", "file.create_folder": "Создать папку", "file.folder_hint": "Git не хранит пустые папки — Polar добавит туда .gitkeep.", "flash.file_created": "Файл создан.", "flash.folder_created": "Папка создана.", "flash.file_exists": "Файл с таким путём уже существует.", "flash.path_invalid": "Некорректный путь.", "flash.file_create_failed": "Не удалось создать файл.", "flash.folder_create_failed": "Не удалось создать папку.", "file.upload_file": "Загрузить файлы", "file.upload_dest": "Папка назначения", "file.upload_dest_ph": "напр. docs/images (необязательно)", "file.upload_pick": "Выберите файлы или перетащите сюда", "file.upload_hint": "До 25 файлов, до 50 МБ суммарно.", "file.upload_submit": "Загрузить", "file.upload_replace_hint": "Существующие файлы будут перезаписаны.", "flash.upload_done": "Загружено файлов: %d.", "flash.upload_none": "Файлы не выбраны.", "flash.upload_failed": "Не удалось загрузить файлы.", "flash.upload_too_big": "Слишком большой объём — лимит 50 МБ.", "flash.upload_too_many": "Слишком много файлов: максимум %d.", "flash.repo_settings_saved": "Настройки репозитория сохранены.", "flash.repo_rename_failed": "Не удалось переименовать: %s", "flash.repo_renamed": "Репозиторий переименован в %s.", "flash.repo_confirm_mismatch": "Введённое имя не совпадает с названием репозитория.", "flash.repo_deleted": "Репозиторий %s удалён.", "flash.branch_created": "Ветка %s создана.", "flash.branch_create_failed": "Не удалось создать ветку: %s", "flash.branch_default_delete": "Нельзя удалить ветку по умолчанию.", "flash.branch_deleted": "Ветка %s удалена.", "flash.branch_delete_failed": "Не удалось удалить ветку: %s", "flash.tag_created": "Тег %s создан.", "flash.tag_create_failed": "Не удалось создать тег: %s", "flash.tag_deleted": "Тег %s удалён.", "flash.tag_delete_failed": "Не удалось удалить тег: %s", "flash.issue_closed": "Задача #%d закрыта.", "flash.issue_reopened": "Задача #%d открыта заново.", // Ошибочные страницы "err.not_found": "Не найдено", "err.forbidden": "Доступ запрещён", "err.session_error": "Ошибка сессии", "err.session_create": "Не удалось создать сессию.", "err.settings_error": "Ошибка настроек", "err.settings_save": "Не удалось сохранить настройку.", "err.explore_disabled": "Раздел «Обзор» отключён на этом сервере.", "err.activity_disabled": "Активность отключена для этого репозитория.", "err.pulls_disabled": "Пул-реквесты отключены для этого репозитория.", "err.releases_disabled": "Релизы отключены для этого репозитория.", "err.release_not_found": "Релиз не найден", "err.release_not_found_desc": "Не удалось найти релиз.", "err.asset_not_found": "Файл не найден", "err.asset_not_found_desc": "Запрошенный файл релиза не найден.", "err.issues_disabled": "Задачи отключены для этого репозитория.", "err.repo_not_found": "Репозиторий не найден", "err.repo_not_found_desc": "Репозиторий не существует или у вас нет к нему доступа.", "err.repo_private_desc": "Репозиторий не существует или является приватным.", "err.access_denied": "Доступ запрещён", "err.repo_write_denied": "У вас нет прав на изменение этого репозитория.", "err.invalid_repo_path": "Некорректный путь репозитория", "err.path_not_found": "Путь не найден", "err.path_not_found_desc": "Не удалось найти путь в репозитории для ссылки %s", "err.file_not_found": "Файл не найден", "err.file_not_found_desc": "Запрошенный файл не найден в %s", "err.commit_not_found": "Коммит не найден", "err.commit_not_found_desc": "Не удалось найти коммит %s", "err.pr_not_found": "Пул-реквест не найден", "err.pr_not_found_desc": "Не удалось найти пул-реквест.", "err.pr_state_forbidden": "Вы не можете изменить состояние этого пул-реквеста.", "err.pr_merge_forbidden": "У вас нет прав на слияние пул-реквестов.", "err.issue_not_found": "Задача не найдена", "err.issue_not_found_desc": "Не удалось найти задачу.", "err.issue_state_forbidden": "Вы не можете изменить состояние этой задачи.", "err.delete_error": "Ошибка удаления", "err.repo_delete_failed": "Не удалось удалить репозиторий.", "err.user_not_found": "Пользователь @%s не существует.", "err.archive_error": "Ошибка архива", "err.archive_tar_failed": "Не удалось создать tar.gz архив для %s", "err.archive_zip_failed": "Не удалось создать zip архив для %s", // Форки "fork.forked_from": "форк от", "fork.forks": "Форки", "fork.forks_btn": "Форки", "fork.fork_btn": "Форк", "fork.fork_btn_desc": "Создать форк этого репозитория", "fork.your_fork": "Ваш форк", "fork.your_fork_desc": "Перейти к вашему форку", "forks.title": "Форки", "forks.empty": "Пока никто не форкал этот репозиторий.", // Коллабораторы "collab.title": "Коллабораторы", "collab.desc": "Люди с доступом к репозиторию", "collab.owner": "Владелец", "collab.role": "Роль", "collab.role_read": "Чтение", "collab.role_write": "Запись", "collab.username": "Пользователь", "collab.username_ph": "username", "collab.add": "Добавить", "collab.remove": "Убрать доступ", "collab.empty": "Коллабораторов пока нет.", // Ревью "reviews.title": "Ревью", "reviews.approved": "Одобрено", "reviews.changes_requested": "Запрошены изменения", "reviews.body_ph": "Комментарий к ревью (необязательно)", "reviews.approve": "Одобрить", "reviews.request_changes": "Запросить изменения", // Device flow (RFC 8628) "device.title": "Подключение устройства", "device.badge": "Git", "device.desc": "Введите код, который показал git или скрипт-помощник, чтобы выдать устройству доступ к вашему аккаунту.", "device.check": "Проверить", "device.authorize_for": "Выдать доступ по коду", "device.scope_hint": "Доступ даётся на чтение и запись репозиториев, к которым у вас есть права, и действует только для этого устройства.", "device.approve": "Разрешить", "device.deny": "Отклонить", "device.signin_to_approve": "Войдите, чтобы подтвердить доступ", "device.helper_hint": "Подсказка: настройте credential helper, чтобы код запрашивался автоматически — см. README.", "device.code_unknown": "Код не найден. Проверьте, что он введён верно.", "device.code_used": "Этот код уже использован или истёк.", "device.approved_now": "Готово! Устройство подключено — вернитесь в терминал.", "device.denied_now": "Запрос отклонён. Устройство не получит доступ.", // Flash: форки, коллабораторы, ревью, device flow "flash.fork_self": "Это ваш репозиторий — форк не нужен.", "flash.fork_exists": "У вас уже есть форк этого репозитория.", "flash.fork_created": "Форк создан.", "flash.fork_failed": "Не удалось создать форк: %s", "flash.collab_added": "@%s добавлен в коллабораторы.", "flash.collab_removed": "Доступ коллаборатора отозван.", "flash.collab_failed": "Не удалось изменить коллаборатора: %s", "flash.review_self": "Нельзя ревьюить собственный пул-реквест.", "flash.review_closed": "Ревью доступно только для открытых пул-реквестов.", "flash.review_failed": "Не удалось сохранить ревью: %s", "flash.review_approved": "Ревью сохранено: одобрено.", "flash.review_changes_requested": "Ревью сохранено: запрошены изменения.", "flash.device_code_required": "Введите код устройства.", "flash.device_code_invalid": "Код не найден, истёк или уже использован.", "flash.device_approved": "Устройство авторизовано — можно возвращаться к git.", "flash.device_denied": "Запрос отклонён.", // Прочее "issues.new": "Новая задача", "pulls.head_repo": "Из репозитория", "repo.about": "О репозитории", "repo.clone_title": "Клонирование", "repo.created": "Создан", "repo.updated": "Обновлён", "repo.view_file": "Открыть", "err.collab_forbidden": "Управлять коллабораторами может только владелец или администратор.", "err.pr_head_repo_invalid": "Выбран недопустимый репозиторий-источник."}, EN: { // Navigation & Common "app.tagline": "Lightweight self-hosted Git hosting", "nav.home": "Home", "nav.repositories": "Repositories", "nav.explore": "Explore", "nav.settings": "Settings", "nav.admin": "Admin", "nav.agents": "For AI agents", "nav.login": "Sign in", "nav.register": "Sign up", "nav.logout": "Sign out", "nav.new_repo": "New", "nav.command_palette": "Search & commands", "nav.search_placeholder": "Search...", "nav.theme": "Theme", "nav.lang": "Language", "common.copy": "Copy", "common.copied": "Copied", "common.cancel": "Cancel", "common.save": "Save", "common.delete": "Delete", "common.close": "Close", "common.never": "Never", "common.newer": "Newer", "common.older": "Older", "common.up": "Up", // Themes (only 3) "theme.light": "Light", "theme.dark": "Dark", "theme.system": "System", // Landing "landing.badge": "Single Binary · SQLite · Zero Dependencies", "landing.hero.title": "Lightweight self-hosted Git hosting", "landing.hero.subtitle": "A single Go binary backed by SQLite. Deploys in a minute, uses minimal memory, and stays out of your way.", "landing.hero.cta_start": "Get Started", "landing.features.title": "Principles", "landing.feat1.title": "Single Binary", "landing.feat1.desc": "Templates, styles, fonts, migrations, and icons are compiled in via go:embed.", "landing.feat2.title": "SQLite (WAL)", "landing.feat2.desc": "Your entire database is a single data/polar.db file without CGO. Back up by copying.", "landing.feat3.title": "Fast & Quiet", "landing.feat3.desc": "Instant cold start, server-side rendering, and < 30 MB idle memory.", "landing.feat4.title": "Private by Default", "landing.feat4.desc": "Public and private repositories, Argon2id passwords, and Git HTTP access tokens.", // Auth "auth.login.title": "Sign in", "auth.register.title": "Create account", "auth.register.disabled": "Public registration is disabled by the administrator.", "auth.username": "Username", "auth.username_hint": "2–32 characters: letters, digits, hyphen, underscore", "auth.username_or_email": "Username or Email", "auth.email": "Email", "auth.password": "Password", "auth.password_hint": "At least 8 characters", "auth.submit_login": "Sign in", "auth.submit_register": "Create account", "auth.no_account": "No account?", "auth.have_account": "Have an account?", "auth.invalid_creds": "Invalid username/email or password", // Repositories "repo.new.title": "New repository", "repo.name": "Name", "repo.name_hint": "Letters, digits, '.', '-', '_'", "repo.description": "Description", "repo.description_ph": "Short description (optional)", "repo.visibility": "Visibility", "repo.public": "Public", "repo.public_desc": "Anyone can view and clone this repository", "repo.private": "Private", "repo.private_desc": "Accessible only to you", "repo.create_btn": "Create repository", "repo.filter_ph": "Filter repositories...", "repo.empty_list": "No repositories yet", "repo.empty_list_sub": "Create your first repository to push code.", "repo.empty.title": "Repository is empty", "repo.empty.new_cmd": "Create a new repository on the command line", "repo.empty.push_cmd": "Push an existing repository", "repo.tab.code": "Code", "repo.tab.commits": "Commits", "repo.tab.branches": "Branches & Tags", "repo.tab.issues": "Issues", "repo.tab.pulls": "Pull requests", "repo.tab.releases": "Releases", "repo.tab.activity": "Activity", "repo.tab.settings": "Settings", "repo.features": "Repository Features", "repo.clone": "Clone", "repo.download_zip": "Download ZIP", "repo.branches": "Branches", "repo.tags": "Tags", "repo.default_branch": "Default branch", "repo.find_file": "Filter files...", "repo.files": "Files", "repo.readme": "README", "repo.raw": "Raw", "repo.history": "History", "repo.lines": "lines", "repo.lines_one": "line", "repo.lines_few": "lines", "repo.lines_many": "lines", "repo.binary_notice": "Binary file — inline preview unavailable", "repo.download_raw": "Download file", "repo.browse_at_commit": "Browse files", "repo.files_changed": "files changed", "repo.files_changed_one": "file changed", "repo.files_changed_few": "files changed", "repo.files_changed_many": "files changed", "repo.no_commits": "No commits yet", "repo.danger_zone": "Danger Zone", "repo.delete": "Delete repository", "repo.delete_desc": "Once deleted, the repository and its git history cannot be recovered.", "repo.delete_confirm": "Type the repository name to confirm", "repo.rename": "Rename", "repo.save": "Save", "repo.branch.new": "New branch", "repo.branch.name_ph": "Branch name (e.g. feat/ui)", "repo.branch.from": "From branch / ref", "repo.branch.create": "Create branch", "repo.branch.default": "default", "repo.tag.new": "New tag / release", "repo.tag.name_ph": "Tag (e.g. v1.0.0)", "repo.tag.msg_ph": "Tag note (optional)", "repo.tag.create": "Create tag", "repo.tags.empty": "No tags yet", // Pull Requests "pulls.title": "Pull requests", "pulls.new": "New pull request", "pulls.empty": "No pull requests yet", "pulls.open": "Open", "pulls.closed": "Closed", "pulls.merged": "Merged", "pulls.compare": "Comparing changes", "pulls.base": "base", "pulls.head": "compare", "pulls.title_ph": "Pull request title...", "pulls.desc_ph": "Describe your changes (Markdown supported)...", "pulls.create_btn": "Create pull request", "pulls.merge_btn": "Merge pull request", "pulls.can_merge": "Able to merge automatically.", "pulls.cannot_merge": "Can't automatically merge: conflicts detected.", "pulls.already_merged": "The head branch is already merged into base.", "pulls.close_btn": "Close PR", "pulls.reopen_btn": "Reopen PR", "pulls.comment_btn": "Comment", "pulls.conversation": "Conversation", "pulls.files_changed": "Files changed", "pulls.commits": "Commits", "pulls.able_to_merge": "Able to merge automatically", "pulls.body": "Description", "pulls.body_ph": "Describe your changes (Markdown supported)...", "pulls.branches_identical": "Branches are identical — nothing to compare", "pulls.cannot_merge_warning": "Conflicts detected — automatic merge is not possible", "pulls.closed_desc": "The head branch was not merged into base.", "pulls.closed_message": "Pull request closed", "pulls.comment_ph": "Write a comment (Markdown supported)...", "pulls.compare_branches": "Comparing branches", "pulls.conflicts_desc": "Resolve the conflicts locally and push to the head branch.", "pulls.conflicts_title": "Merge conflicts detected", "pulls.into": "into", "pulls.merge_clean": "No conflicts — changes can be merged.", "pulls.merged_message": "Pull request merged", "pulls.no_changes": "No files changed", "pulls.no_commits": "No commits in this pull request yet", "pulls.only_write_merge": "Only users with write access can merge pull requests.", "pulls.status.closed": "Closed", "pulls.status.merged": "Merged", "pulls.status.open": "Open", "pulls.wants_to_merge": "wants to merge", // Releases "releases.title": "Releases", "releases.new": "Draft a new release", "releases.assets_upload": "Release files", "releases.assets_upload_hint": "Optional. Up to 20 files, 100 MB each.", "releases.asset_delete": "Delete file", "releases.asset_delete_confirm": "Delete this file? This cannot be undone.", "releases.empty": "No releases yet", "releases.tag_select": "Tag", "releases.title_ph": "Release title", "releases.desc_ph": "Describe this release, release notes (Markdown)...", "releases.prerelease": "This is a pre-release", "releases.publish": "Publish release", "releases.delete": "Delete release", "releases.assets": "Assets", "releases.source_zip": "Source code (zip)", "releases.source_tar": "Source code (tar.gz)", "releases.body": "Description", "releases.body_ph": "Describe this release, release notes (Markdown)...", "releases.count": "releases", "releases.count_one": "release", "releases.count_few": "releases", "releases.count_many": "releases", "releases.delete_confirm": "Delete this release? This cannot be undone.", "releases.is_prerelease": "This is a pre-release", "releases.publish_btn": "Publish release", "releases.tag_desc": "An existing tag or a new one — it will be created automatically.", "releases.tag_name": "Tag", "releases.tags": "tags", "releases.tags_one": "tag", "releases.tags_few": "tags", "releases.tags_many": "tags", "releases.target": "Branch", "releases.target_desc": "Branch or commit the tag will point to.", // Activity "activity.title": "Activity", "activity.pulse": "Activity overview", "activity.empty": "No recent activity recorded.", "activity.commits": "commits", "activity.open_prs": "open PRs", "activity.merged_prs": "merged PRs", "activity.open_issues": "open issues", "activity.releases": "releases", "activity.issues": "opened and closed", "activity.on_default_branch": "on the default branch", "activity.published_releases": "published releases", "activity.pull_requests": "opened and merged", "activity.recent_feed": "Activity feed", "activity.badge_commit": "commit", "activity.badge_release": "release", // Issues "issue.new": "New issue", "issue.title": "Title", "issue.title_ph": "Brief summary of the issue", "issue.body": "Description", "issue.body_ph": "Detailed description (Markdown supported)...", "issue.create": "Create issue", "issue.open": "Open", "issue.closed": "Closed", "issue.status.open": "Open", "issue.status.closed": "Closed", "issue.close_btn": "Close issue", "issue.reopen_btn": "Reopen", "issue.comment_ph": "Write a comment (Markdown supported)...", "issue.comment_btn": "Comment", "issue.empty": "No issues in this list", // Settings, Profile & Tokens "settings.title": "Settings", "settings.profile": "Profile", "settings.avatar": "Avatar", "settings.avatar_upload": "Choose image", "settings.avatar_remove": "Remove avatar", "settings.avatar_hint": "PNG, JPG, WebP, or GIF — automatically cropped to 256×256", "settings.full_name": "Display name", "settings.full_name_ph": "Your name", "settings.bio": "Bio", "settings.bio_ph": "Short profile bio", "settings.location": "Location", "settings.location_ph": "City, Country", "settings.website": "Website", "settings.website_ph": "https://example.com", "profile.edit": "Edit profile", "profile.joined": "Joined", "profile.public_repos": "Public", "profile.private_repos": "Private", "profile.total_repos": "Repositories", "settings.tokens": "Access Tokens", "settings.tokens.desc": "Use a token instead of your password for git clone and git push over HTTP.", "settings.token.name": "Token name (e.g., Laptop)", "settings.token.create": "Create", "settings.token.revoke": "Revoke", "settings.token.created_once": "Copy this token now — it will only be shown once:", "settings.token.col_name": "Name", "settings.token.col_prefix": "Prefix", "settings.token.col_created": "Created", "settings.token.col_used": "Last used", "settings.token.empty": "No access tokens yet", "settings.password.current": "Current password", "settings.password.new": "New password", "settings.password.update": "Update password", // Admin "admin.title": "Administration", "admin.features": "Features & Access", "admin.users": "Users", "admin.registration": "Public registration", "admin.registration.desc": "Allow new users to create accounts", "admin.explore": "Explore page", "admin.explore.desc": "Show public repository directory in sidebar and at /explore", "admin.landing": "Landing page", "admin.landing.desc": "Show landing page on root URL (if disabled, redirects to repos or sign in)", "admin.anon_read": "Anonymous read access", "admin.anon_read.desc": "Allow unauthenticated guests to browse public repositories and profiles", "admin.issues": "Issue tracker", "admin.issues.desc": "Enable the Issues tab inside repositories", "admin.enabled": "Enabled", "admin.disabled": "Disabled", "admin.enable_btn": "Enable", "admin.disable_btn": "Disable", "admin.col.user": "User", "admin.col.repos": "Repos", "admin.col.role": "Role", "admin.col.joined": "Joined", "admin.role.admin": "Admin", "admin.role.user": "User", // Command Palette & Shortcuts "palette.placeholder": "Search repositories, files, or commands...", "palette.group.nav": "Navigation", "palette.group.themes": "Theme", "palette.group.repos": "Repositories", "shortcuts.title": "Keyboard shortcuts", "shortcuts.palette": "Command palette", "shortcuts.search": "Focus search", "shortcuts.home": "Go home", "shortcuts.files": "Filter files", "shortcuts.sidebar": "Toggle sidebar", "nav.toggle_sidebar": "Sidebar (Ctrl+B)", // Flash messages "flash.welcome": "Welcome to Polar, @%s!", "flash.profile_updated": "Profile updated.", "flash.password_updated": "Password changed successfully.", "flash.token_revoked": "Access token revoked.", "flash.settings_updated": "Settings saved.", "flash.error": "Error: %s", "flash.comment_error": "Could not post the comment: %s", "flash.pr_created": "Pull request #%d created.", "flash.pr_closed": "Pull request #%d closed.", "flash.pr_reopened": "Pull request #%d reopened.", "flash.pr_merged": "Pull request #%d merged.", "flash.pr_merged_state": "Cannot change the state of a merged pull request.", "flash.pr_merge_open_only": "Only an open pull request can be merged.", "flash.pr_merge_failed": "Could not merge: %s", "flash.release_tag_empty": "Tag name cannot be empty.", "flash.release_tag_failed": "Could not create the tag: %s", "flash.release_create_failed": "Could not create the release: %s", "flash.release_published": "Release %s published.", "flash.release_delete_failed": "Could not delete the release: %s", "flash.release_deleted": "Release deleted.", "flash.release_published_with_assets": "Release %s published with %d attached file(s).", "flash.release_published_assets_failed": "Release %s published, but some files could not be saved.", "flash.release_asset_too_many": "Too many files: %d max.", "flash.release_asset_too_big": "File is too large — 100 MB per-file limit.", "flash.release_asset_deleted": "File deleted.", "flash.release_asset_delete_failed": "Could not delete the file: %s", "file.new_file": "New file", "file.new_folder": "New folder", "file.path": "Path", "file.path_ph": "e.g. src/main.go", "file.folder_ph": "e.g. docs", "file.branch": "Branch", "file.content": "Content", "file.content_ph": "File contents…", "file.message": "Commit message", "file.create_file": "Create file", "file.create_folder": "Create folder", "file.folder_hint": "Git does not track empty folders — Polar adds a .gitkeep placeholder.", "flash.file_created": "File created.", "flash.folder_created": "Folder created.", "flash.file_exists": "A file with that path already exists.", "flash.path_invalid": "Invalid path.", "flash.file_create_failed": "Could not create the file.", "flash.folder_create_failed": "Could not create the folder.", "file.upload_file": "Upload files", "file.upload_dest": "Destination folder", "file.upload_dest_ph": "e.g. docs/images (optional)", "file.upload_pick": "Choose files or drag them here", "file.upload_hint": "Up to 25 files, 50 MB in total.", "file.upload_submit": "Upload", "file.upload_replace_hint": "Existing files will be overwritten.", "flash.upload_done": "Uploaded %d file(s).", "flash.upload_none": "No files selected.", "flash.upload_failed": "Could not upload the files.", "flash.upload_too_big": "Upload is too large — 50 MB limit.", "flash.upload_too_many": "Too many files: %d max.", "flash.repo_settings_saved": "Repository settings saved.", "flash.repo_rename_failed": "Could not rename: %s", "flash.repo_renamed": "Repository renamed to %s.", "flash.repo_confirm_mismatch": "The name you typed does not match the repository name.", "flash.repo_deleted": "Repository %s deleted.", "flash.branch_created": "Branch %s created.", "flash.branch_create_failed": "Could not create the branch: %s", "flash.branch_default_delete": "The default branch cannot be deleted.", "flash.branch_deleted": "Branch %s deleted.", "flash.branch_delete_failed": "Could not delete the branch: %s", "flash.tag_created": "Tag %s created.", "flash.tag_create_failed": "Could not create the tag: %s", "flash.tag_deleted": "Tag %s deleted.", "flash.tag_delete_failed": "Could not delete the tag: %s", "flash.issue_closed": "Issue #%d closed.", "flash.issue_reopened": "Issue #%d reopened.", // Error pages "err.not_found": "Not found", "err.forbidden": "Access denied", "err.session_error": "Session error", "err.session_create": "Could not create a session.", "err.settings_error": "Settings error", "err.settings_save": "Could not save the setting.", "err.explore_disabled": "Explore is disabled on this server.", "err.activity_disabled": "Activity is disabled for this repository.", "err.pulls_disabled": "Pull requests are disabled for this repository.", "err.releases_disabled": "Releases are disabled for this repository.", "err.release_not_found": "Release not found", "err.release_not_found_desc": "Could not find the release.", "err.asset_not_found": "File not found", "err.asset_not_found_desc": "The requested release file was not found.", "err.issues_disabled": "Issues are disabled for this repository.", "err.repo_not_found": "Repository not found", "err.repo_not_found_desc": "The repository does not exist or you do not have access to it.", "err.repo_private_desc": "The repository does not exist or is private.", "err.access_denied": "Access denied", "err.repo_write_denied": "You do not have permission to modify this repository.", "err.invalid_repo_path": "Invalid repository path", "err.path_not_found": "Path not found", "err.path_not_found_desc": "Could not find the path in the repository for ref %s", "err.file_not_found": "File not found", "err.file_not_found_desc": "The requested file was not found in %s", "err.commit_not_found": "Commit not found", "err.commit_not_found_desc": "Could not find commit %s", "err.pr_not_found": "Pull request not found", "err.pr_not_found_desc": "Could not find the pull request.", "err.pr_state_forbidden": "You cannot change the state of this pull request.", "err.pr_merge_forbidden": "You do not have permission to merge pull requests.", "err.issue_not_found": "Issue not found", "err.issue_not_found_desc": "Could not find the issue.", "err.issue_state_forbidden": "You cannot change the state of this issue.", "err.delete_error": "Delete error", "err.repo_delete_failed": "Could not delete the repository.", "err.user_not_found": "User @%s does not exist.", "err.archive_error": "Archive error", "err.archive_tar_failed": "Could not create a tar.gz archive for %s", "err.archive_zip_failed": "Could not create a zip archive for %s", // Forks "fork.forked_from": "forked from", "fork.forks": "Forks", "fork.forks_btn": "Forks", "fork.fork_btn": "Fork", "fork.fork_btn_desc": "Create a fork of this repository", "fork.your_fork": "Your fork", "fork.your_fork_desc": "Go to your fork", "forks.title": "Forks", "forks.empty": "Nobody has forked this repository yet.", // Collaborators "collab.title": "Collaborators", "collab.desc": "People with access to this repository", "collab.owner": "Owner", "collab.role": "Role", "collab.role_read": "Read", "collab.role_write": "Write", "collab.username": "User", "collab.username_ph": "username", "collab.add": "Add", "collab.remove": "Revoke access", "collab.empty": "No collaborators yet.", // Reviews "reviews.title": "Reviews", "reviews.approved": "Approved", "reviews.changes_requested": "Changes requested", "reviews.body_ph": "Review comment (optional)", "reviews.approve": "Approve", "reviews.request_changes": "Request changes", // Device flow (RFC 8628) "device.title": "Device authorization", "device.badge": "Git", "device.desc": "Enter the code shown by git or the credential helper to grant this device access to your account.", "device.check": "Check", "device.authorize_for": "Grant access to code", "device.scope_hint": "Access covers repositories you can already reach and is bound to this device only.", "device.approve": "Approve", "device.deny": "Deny", "device.signin_to_approve": "Sign in to approve access", "device.helper_hint": "Tip: configure the credential helper so the code is requested automatically — see the README.", "device.code_unknown": "Code not found. Double-check the code you entered.", "device.code_used": "This code has already been used or expired.", "device.approved_now": "All set! The device is authorized — return to your terminal.", "device.denied_now": "Request denied. The device will not get access.", // Flash: forks, collaborators, reviews, device flow "flash.fork_self": "This repository is yours — no fork needed.", "flash.fork_exists": "You already have a fork of this repository.", "flash.fork_created": "Fork created.", "flash.fork_failed": "Could not create fork: %s", "flash.collab_added": "@%s added as a collaborator.", "flash.collab_removed": "Collaborator access revoked.", "flash.collab_failed": "Could not update collaborator: %s", "flash.review_self": "You cannot review your own pull request.", "flash.review_closed": "Reviews are only available on open pull requests.", "flash.review_failed": "Could not save the review: %s", "flash.review_approved": "Review saved: approved.", "flash.review_changes_requested": "Review saved: changes requested.", "flash.device_code_required": "Enter the device code.", "flash.device_code_invalid": "Code not found, expired, or already used.", "flash.device_approved": "Device authorized — you can return to git.", "flash.device_denied": "Request denied.", // Misc "issues.new": "New issue", "pulls.head_repo": "From repository", "repo.about": "About", "repo.clone_title": "Clone", "repo.created": "Created", "repo.updated": "Updated", "repo.view_file": "Open", "err.collab_forbidden": "Only the owner or an administrator can manage collaborators.", "err.pr_head_repo_invalid": "The selected source repository is not allowed."}, } func T(lang Lang, key string) string { dict, ok := translations[Normalize(string(lang))] if !ok { dict = translations[RU] } if val, ok := dict[key]; ok { return val } if val, ok := translations[RU][key]; ok { return val } return key }