rin
Публичный Open-source lightweight git hosting.
i18n.go · 999 строк · 56.3 KB
Raw
1 package i18n
2
3 type Lang string
4
5 const (
6 RU Lang = "ru"
7 EN Lang = "en"
8 )
9
10 func Normalize(code string) Lang {
11 switch code {
12 case "en", "EN":
13 return EN
14 default:
15 return RU
16 }
17 }
18
19 var translations = map[Lang]map[string]string{
20 RU: {
21 // Навигация и общее
22 "app.tagline": "Лёгкий self-hosted Git-хостинг",
23 "nav.home": "Главная",
24 "nav.repositories": "Репозитории",
25 "nav.explore": "Обзор",
26 "nav.settings": "Настройки",
27 "nav.admin": "Админка",
28 "nav.login": "Войти",
29 "nav.register": "Регистрация",
30 "nav.logout": "Выйти",
31 "nav.new_repo": "Создать",
32 "nav.command_palette": "Поиск и команды",
33 "nav.search_placeholder": "Поиск...",
34 "nav.theme": "Тема",
35 "nav.lang": "Язык",
36 "common.copy": "Копировать",
37 "common.copied": "Скопировано",
38 "common.cancel": "Отмена",
39 "common.save": "Сохранить",
40 "common.delete": "Удалить",
41 "common.close": "Закрыть",
42 "common.never": "Никогда",
43 "common.newer": "Новее",
44 "common.older": "Старее",
45 "common.up": "Наверх",
46
47 // Темы (только 3)
48 "theme.light": "Светлая",
49 "theme.dark": "Тёмная",
50 "theme.system": "Авто",
51
52 // Лендинг
53 "landing.badge": "Один бинарник · SQLite · Ноль зависимостей",
54 "landing.hero.title": "Лёгкий self-hosted Git-хостинг",
55 "landing.hero.subtitle": "Один бинарник на Go со встроенным SQLite. Поднимается за минуту, потребляет минимум памяти и не перегружен лишним.",
56 "landing.hero.cta_start": "Начать",
57 "landing.features.title": "Принципы",
58 "landing.feat1.title": "Один бинарник",
59 "landing.feat1.desc": "Шаблоны, стили, шрифты, миграции и иконки вшиты через go:embed.",
60 "landing.feat2.title": "SQLite (WAL)",
61 "landing.feat2.desc": "Вся база — один файл data/polar.db без CGO. Бэкап простым копированием.",
62 "landing.feat3.title": "Быстрый отклик",
63 "landing.feat3.desc": "Мгновенный старт, серверный рендеринг и < 30 МБ памяти в простое.",
64 "landing.feat4.title": "Приватность",
65 "landing.feat4.desc": "Публичные и приватные репозитории, Argon2id и токены доступа для Git HTTP.",
66 // Авторизация
67 "auth.login.title": "Вход",
68 "auth.register.title": "Регистрация",
69 "auth.register.disabled": "Публичная регистрация отключена администратором.",
70 "auth.username": "Имя пользователя",
71 "auth.username_hint": "2–32 символа: латиница, цифры, дефис, подчёркивание",
72 "auth.username_or_email": "Логин или Email",
73 "auth.email": "Email",
74 "auth.password": "Пароль",
75 "auth.password_hint": "Минимум 8 символов",
76 "auth.submit_login": "Войти",
77 "auth.submit_register": "Создать аккаунт",
78 "auth.no_account": "Нет аккаунта?",
79 "auth.have_account": "Уже есть аккаунт?",
80 "auth.invalid_creds": "Неверный логин или пароль",
81
82 // Репозитории
83 "repo.new.title": "Новый репозиторий",
84 "repo.name": "Название",
85 "repo.name_hint": "Латинские буквы, цифры, точка, дефис, подчёркивание",
86 "repo.description": "Описание",
87 "repo.description_ph": "Краткое описание проекта (необязательно)",
88 "repo.visibility": "Доступ",
89 "repo.public": "Публичный",
90 "repo.public_desc": "Виден всем, клонирование без авторизации",
91 "repo.private": "Приватный",
92 "repo.private_desc": "Доступен только владельцу",
93 "repo.create_btn": "Создать репозиторий",
94 "repo.filter_ph": "Фильтр репозиториев...",
95 "repo.empty_list": "Репозиториев пока нет",
96 "repo.empty_list_sub": "Создайте первый репозиторий, чтобы отправить код.",
97 "repo.empty.title": "Репозиторий пуст",
98 "repo.empty.new_cmd": "Создать новый репозиторий в командной строке",
99 "repo.empty.push_cmd": "Отправить существующий репозиторий",
100 "repo.tab.code": "Код",
101 "repo.tab.commits": "Коммиты",
102 "repo.tab.branches": "Ветки и теги",
103 "repo.tab.issues": "Задачи",
104 "repo.tab.pulls": "Пул-реквесты",
105 "repo.tab.releases": "Релизы",
106 "repo.tab.activity": "Активность",
107 "repo.tab.settings": "Настройки",
108 "repo.features": "Возможности репозитория",
109 "repo.clone": "Клонировать",
110 "repo.download_zip": "Скачать ZIP",
111 "repo.branches": "Ветки",
112 "repo.tags": "Теги",
113 "repo.default_branch": "Ветка по умолчанию",
114 "repo.find_file": "Поиск файла...",
115 "repo.files": "Файлы",
116 "repo.readme": "README",
117 "repo.raw": "Raw",
118 "repo.history": "История",
119 "repo.lines": "строк",
120 "repo.lines_one": "строка",
121 "repo.lines_few": "строки",
122 "repo.lines_many": "строк",
123 "repo.binary_notice": "Бинарный файл — предпросмотр недоступен",
124 "repo.download_raw": "Скачать файл",
125 "repo.browse_at_commit": "Файлы на момент коммита",
126 "repo.files_changed": "файлов изменено",
127 "repo.files_changed_one": "файл изменён",
128 "repo.files_changed_few": "файла изменено",
129 "repo.files_changed_many": "файлов изменено",
130 "repo.no_commits": "Коммитов пока нет",
131 "repo.danger_zone": "Опасная зона",
132 "repo.delete": "Удалить репозиторий",
133 "repo.delete_desc": "Удаление необратимо: все файлы и история git будут стёрты.",
134 "repo.delete_confirm": "Введите название репозитория для подтверждения",
135 "repo.rename": "Переименовать",
136 "repo.save": "Сохранить",
137 "repo.branch.new": "Новая ветка",
138 "repo.branch.name_ph": "Имя ветки (напр. feat/ui)",
139 "repo.branch.from": "От ветки / коммита",
140 "repo.branch.create": "Создать ветку",
141 "repo.branch.default": "по умолчанию",
142 "repo.tag.new": "Новый тег / релиз",
143 "repo.tag.name_ph": "Тег (напр. v1.0.0)",
144 "repo.tag.msg_ph": "Описание тега (необязательно)",
145 "repo.tag.create": "Создать тег",
146 "repo.tags.empty": "Тегов пока нет",
147
148 // Pull Requests
149 "pulls.title": "Пул-реквесты",
150 "pulls.new": "Новый пул-реквест",
151 "pulls.empty": "Пул-реквестов пока нет",
152 "pulls.open": "Открыт",
153 "pulls.closed": "Закрыт",
154 "pulls.merged": "Слит",
155 "pulls.compare": "Сравнение веток",
156 "pulls.base": "Базовая ветка",
157 "pulls.head": "Ветка изменений",
158 "pulls.title_ph": "Заголовок пул-реквеста...",
159 "pulls.desc_ph": "Опишите предлагаемые изменения (Markdown)...",
160 "pulls.create_btn": "Создать пул-реквест",
161 "pulls.merge_btn": "Слить пул-реквест",
162 "pulls.can_merge": "Ветки можно объединить автоматически.",
163 "pulls.cannot_merge": "Невозможно объединить автоматически: обнаружены конфликты.",
164 "pulls.already_merged": "Ветка изменений уже объединена с базовой.",
165 "pulls.close_btn": "Закрыть PR",
166 "pulls.reopen_btn": "Открыть заново",
167 "pulls.comment_btn": "Прокомментировать",
168 "pulls.conversation": "Обсуждение",
169 "pulls.files_changed": "Изменения",
170 "pulls.commits": "Коммиты",
171 "pulls.able_to_merge": "Можно слить автоматически",
172 "pulls.body": "Описание",
173 "pulls.body_ph": "Опишите предлагаемые изменения (Markdown)...",
174 "pulls.branches_identical": "Ветки идентичны — сравнивать нечего",
175 "pulls.cannot_merge_warning": "Обнаружены конфликты — автоматическое слияние невозможно",
176 "pulls.closed_desc": "Ветка изменений не была объединена с базовой.",
177 "pulls.closed_message": "Пул-реквест закрыт",
178 "pulls.comment_ph": "Напишите комментарий (поддерживается Markdown)...",
179 "pulls.compare_branches": "Сравнение веток",
180 "pulls.conflicts_desc": "Разрешите конфликты локально и запушьте изменения в ветку.",
181 "pulls.conflicts_title": "Есть конфликты слияния",
182 "pulls.into": "в",
183 "pulls.merge_clean": "Конфликтов нет — изменения можно объединить.",
184 "pulls.merged_message": "Пул-реквест слит",
185 "pulls.no_changes": "Файлы не изменены",
186 "pulls.no_commits": "В этом пул-реквесте пока нет коммитов",
187 "pulls.only_write_merge": "Сливать пул-реквесты могут только пользователи с правом записи.",
188 "pulls.status.closed": "Закрыт",
189 "pulls.status.merged": "Слит",
190 "pulls.status.open": "Открыт",
191 "pulls.wants_to_merge": "хочет слить",
192
193 // Releases
194 "releases.title": "Релизы",
195 "releases.new": "Новый релиз",
196 "releases.empty": "Релизов пока нет",
197 "releases.tag_select": "Тег",
198 "releases.title_ph": "Название релиза",
199 "releases.desc_ph": "Описание релиза, список изменений (Markdown)...",
200 "releases.prerelease": "Пре-релиз (нестабильная сборка)",
201 "releases.publish": "Опубликовать релиз",
202 "releases.delete": "Удалить релиз",
203 "releases.assets": "Файлы",
204 "releases.source_zip": "Исходный код (zip)",
205 "releases.source_tar": "Исходный код (tar.gz)",
206 "releases.body": "Описание",
207 "releases.body_ph": "Опишите релиз, список изменений (Markdown)...",
208 "releases.count": "релизов",
209 "releases.count_one": "релиз",
210 "releases.count_few": "релиза",
211 "releases.count_many": "релизов",
212 "releases.delete_confirm": "Удалить этот релиз? Действие необратимо.",
213 "releases.is_prerelease": "Пре-релиз (нестабильная сборка)",
214 "releases.publish_btn": "Опубликовать релиз",
215 "releases.tag_desc": "Существующий тег или новый — он будет создан автоматически.",
216 "releases.tag_name": "Тег",
217 "releases.tags": "тегов",
218 "releases.tags_one": "тег",
219 "releases.tags_few": "тега",
220 "releases.tags_many": "тегов",
221 "releases.target": "Ветка",
222 "releases.target_desc": "Ветка или коммит, на который будет указывать тег.",
223
224 // Activity
225 "activity.title": "Активность",
226 "activity.pulse": "Сводка активности",
227 "activity.empty": "Активности пока нет",
228 "activity.commits": "коммитов",
229 "activity.open_prs": "открытых PR",
230 "activity.merged_prs": "слитых PR",
231 "activity.open_issues": "открытых задач",
232 "activity.releases": "релизов",
233 "activity.issues": "открыто и закрыто",
234 "activity.on_default_branch": "в ветке по умолчанию",
235 "activity.published_releases": "опубликованные релизы",
236 "activity.pull_requests": "открыто и слито",
237 "activity.recent_feed": "Лента активности",
238 "activity.badge_commit": "коммит",
239 "activity.badge_release": "релиз",
240
241 // Задачи (Issues)
242 "issue.new": "Новая задача",
243 "issue.title": "Заголовок",
244 "issue.title_ph": "Кратко опишите задачу или проблему",
245 "issue.body": "Описание",
246 "issue.body_ph": "Подробное описание (поддерживается Markdown)...",
247 "issue.create": "Создать задачу",
248 "issue.open": "Открытые",
249 "issue.closed": "Закрытые",
250 "issue.status.open": "Открыта",
251 "issue.status.closed": "Закрыта",
252 "issue.close_btn": "Закрыть задачу",
253 "issue.reopen_btn": "Переоткрыть",
254 "issue.comment_ph": "Напишите комментарий (поддерживается Markdown)...",
255 "issue.comment_btn": "Отправить",
256 "issue.empty": "Задач в этом списке нет",
257
258 // Настройки пользователя, профиль и токены
259 "settings.title": "Настройки",
260 "settings.profile": "Профиль",
261 "settings.avatar": "Аватар",
262 "settings.avatar_upload": "Выбрать изображение",
263 "settings.avatar_remove": "Удалить аватар",
264 "settings.avatar_hint": "PNG, JPG, WebP или GIF — автоматически кадрируется до 256×256",
265 "settings.full_name": "Отображаемое имя",
266 "settings.full_name_ph": "Ваше имя или ник",
267 "settings.bio": "О себе",
268 "settings.bio_ph": "Короткая подпись в профиле",
269 "settings.location": "Локация",
270 "settings.location_ph": "Город, страна",
271 "settings.website": "Сайт",
272 "settings.website_ph": "https://example.com",
273 "profile.edit": "Редактировать профиль",
274 "profile.joined": "Регистрация",
275 "profile.public_repos": "Публичные",
276 "profile.private_repos": "Приватные",
277 "profile.total_repos": "Репозитории",
278 "settings.tokens": "Токены доступа",
279 "settings.tokens.desc": "Используйте токен вместо пароля для git clone и git push по HTTP.",
280 "settings.token.name": "Название токена (например, Ноутбук)",
281 "settings.token.create": "Создать",
282 "settings.token.revoke": "Отозвать",
283 "settings.token.created_once": "Скопируйте токен сейчас — он показывается только один раз:",
284 "settings.token.col_name": "Название",
285 "settings.token.col_prefix": "Префикс",
286 "settings.token.col_created": "Создан",
287 "settings.token.col_used": "Использован",
288 "settings.token.empty": "Токенов доступа пока нет",
289 "settings.password.current": "Текущий пароль",
290 "settings.password.new": "Новый пароль",
291 "settings.password.update": "Обновить пароль",
292
293 // Админка
294 "admin.title": "Администрирование",
295 "admin.features": "Модули и доступ",
296 "admin.users": "Пользователи",
297 "admin.registration": "Публичная регистрация",
298 "admin.registration.desc": "Разрешить самостоятельную регистрацию новых аккаунтов",
299 "admin.explore": "Раздел «Обзор» (Explore)",
300 "admin.explore.desc": "Показывать каталог публичных репозиториев в сайдбаре и по адресу /explore",
301 "admin.landing": "Приветственная страница",
302 "admin.landing.desc": "Показывать лендинг на главной странице (если отключено — переход сразу к репозиториям или входу)",
303 "admin.anon_read": "Гостевой просмотр",
304 "admin.anon_read.desc": "Разрешить неавторизованным пользователям просматривать публичные репозитории и профили",
305 "admin.issues": "Трекер задач (Issues)",
306 "admin.issues.desc": "Включить вкладку «Задачи» в репозиториях",
307 "admin.enabled": "Включено",
308 "admin.disabled": "Отключено",
309 "admin.enable_btn": "Включить",
310 "admin.disable_btn": "Отключить",
311 "admin.col.user": "Пользователь",
312 "admin.col.repos": "Репозитории",
313 "admin.col.role": "Роль",
314 "admin.col.joined": "Дата",
315 "admin.role.admin": "Админ",
316 "admin.role.user": "Участник",
317
318 // Командная палитра и хоткеи
319 "palette.placeholder": "Поиск репозитория, файла или команды...",
320 "palette.group.nav": "Навигация",
321 "palette.group.themes": "Тема оформления",
322 "palette.group.repos": "Репозитории",
323 "shortcuts.title": "Горячие клавиши",
324 "shortcuts.palette": "Командная палитра",
325 "shortcuts.search": "Фокус на поиск",
326 "shortcuts.home": "На главную",
327 "shortcuts.repos": "Мои репозитории",
328 "shortcuts.files": "Фильтр файлов",
329 "shortcuts.sidebar": "Скрыть / показать сайдбар",
330 "nav.toggle_sidebar": "Сайдбар (Ctrl+B)",
331
332 // Флеш-сообщения
333 "flash.welcome": "Добро пожаловать в Polar, @%s!",
334 "flash.profile_updated": "Профиль обновлён.",
335 "flash.password_updated": "Пароль успешно изменён.",
336 "flash.token_revoked": "Токен доступа отозван.",
337 "flash.settings_updated": "Настройки сохранены.",
338 "flash.error": "Ошибка: %s",
339 "flash.comment_error": "Не удалось отправить комментарий: %s",
340 "flash.pr_created": "Пул-реквест #%d создан.",
341 "flash.pr_closed": "Пул-реквест #%d закрыт.",
342 "flash.pr_reopened": "Пул-реквест #%d открыт заново.",
343 "flash.pr_merged": "Пул-реквест #%d слит.",
344 "flash.pr_merged_state": "Нельзя изменить состояние слитого пул-реквеста.",
345 "flash.pr_merge_open_only": "Слить можно только открытый пул-реквест.",
346 "flash.pr_merge_failed": "Не удалось слить: %s",
347 "flash.release_tag_empty": "Имя тега не может быть пустым.",
348 "flash.release_tag_failed": "Не удалось создать тег: %s",
349 "flash.release_create_failed": "Не удалось создать релиз: %s",
350 "flash.release_published": "Релиз %s опубликован.",
351 "flash.release_delete_failed": "Не удалось удалить релиз: %s",
352 "flash.release_deleted": "Релиз удалён.",
353 "file.new_file": "Новый файл",
354 "file.new_folder": "Новая папка",
355 "file.path": "Путь",
356 "file.path_ph": "напр. src/main.go",
357 "file.folder_ph": "напр. docs",
358 "file.branch": "Ветка",
359 "file.content": "Содержимое",
360 "file.content_ph": "Текст файла…",
361 "file.message": "Сообщение коммита",
362 "file.create_file": "Создать файл",
363 "file.create_folder": "Создать папку",
364 "file.folder_hint": "Git не хранит пустые папки — Polar добавит туда .gitkeep.",
365 "flash.file_created": "Файл создан.",
366 "flash.folder_created": "Папка создана.",
367 "flash.file_exists": "Файл с таким путём уже существует.",
368 "flash.path_invalid": "Некорректный путь.",
369 "flash.file_create_failed": "Не удалось создать файл.",
370 "flash.folder_create_failed": "Не удалось создать папку.",
371 "flash.repo_settings_saved": "Настройки репозитория сохранены.",
372 "flash.repo_rename_failed": "Не удалось переименовать: %s",
373 "flash.repo_renamed": "Репозиторий переименован в %s.",
374 "flash.repo_confirm_mismatch": "Введённое имя не совпадает с названием репозитория.",
375 "flash.repo_deleted": "Репозиторий %s удалён.",
376 "flash.branch_created": "Ветка %s создана.",
377 "flash.branch_create_failed": "Не удалось создать ветку: %s",
378 "flash.branch_default_delete": "Нельзя удалить ветку по умолчанию.",
379 "flash.branch_deleted": "Ветка %s удалена.",
380 "flash.branch_delete_failed": "Не удалось удалить ветку: %s",
381 "flash.tag_created": "Тег %s создан.",
382 "flash.tag_create_failed": "Не удалось создать тег: %s",
383 "flash.tag_deleted": "Тег %s удалён.",
384 "flash.tag_delete_failed": "Не удалось удалить тег: %s",
385 "flash.issue_closed": "Задача #%d закрыта.",
386 "flash.issue_reopened": "Задача #%d открыта заново.",
387
388 // Ошибочные страницы
389 "err.not_found": "Не найдено",
390 "err.forbidden": "Доступ запрещён",
391 "err.session_error": "Ошибка сессии",
392 "err.session_create": "Не удалось создать сессию.",
393 "err.settings_error": "Ошибка настроек",
394 "err.settings_save": "Не удалось сохранить настройку.",
395 "err.explore_disabled": "Раздел «Обзор» отключён на этом сервере.",
396 "err.activity_disabled": "Активность отключена для этого репозитория.",
397 "err.pulls_disabled": "Пул-реквесты отключены для этого репозитория.",
398 "err.releases_disabled": "Релизы отключены для этого репозитория.",
399 "err.issues_disabled": "Задачи отключены для этого репозитория.",
400 "err.repo_not_found": "Репозиторий не найден",
401 "err.repo_not_found_desc": "Репозиторий не существует или у вас нет к нему доступа.",
402 "err.repo_private_desc": "Репозиторий не существует или является приватным.",
403 "err.access_denied": "Доступ запрещён",
404 "err.repo_write_denied": "У вас нет прав на изменение этого репозитория.",
405 "err.invalid_repo_path": "Некорректный путь репозитория",
406 "err.path_not_found": "Путь не найден",
407 "err.path_not_found_desc": "Не удалось найти путь в репозитории для ссылки %s",
408 "err.file_not_found": "Файл не найден",
409 "err.file_not_found_desc": "Запрошенный файл не найден в %s",
410 "err.commit_not_found": "Коммит не найден",
411 "err.commit_not_found_desc": "Не удалось найти коммит %s",
412 "err.pr_not_found": "Пул-реквест не найден",
413 "err.pr_not_found_desc": "Не удалось найти пул-реквест.",
414 "err.pr_state_forbidden": "Вы не можете изменить состояние этого пул-реквеста.",
415 "err.pr_merge_forbidden": "У вас нет прав на слияние пул-реквестов.",
416 "err.issue_not_found": "Задача не найдена",
417 "err.issue_not_found_desc": "Не удалось найти задачу.",
418 "err.issue_state_forbidden": "Вы не можете изменить состояние этой задачи.",
419 "err.delete_error": "Ошибка удаления",
420 "err.repo_delete_failed": "Не удалось удалить репозиторий.",
421 "err.user_not_found": "Пользователь @%s не существует.",
422 "err.archive_error": "Ошибка архива",
423 "err.archive_tar_failed": "Не удалось создать tar.gz архив для %s",
424 "err.archive_zip_failed": "Не удалось создать zip архив для %s",
425
426 // Форки
427 "fork.forked_from": "форк от",
428 "fork.forks": "Форки",
429 "fork.forks_btn": "Форки",
430 "fork.fork_btn": "Форк",
431 "fork.fork_btn_desc": "Создать форк этого репозитория",
432 "fork.your_fork": "Ваш форк",
433 "fork.your_fork_desc": "Перейти к вашему форку",
434 "forks.title": "Форки",
435 "forks.empty": "Пока никто не форкал этот репозиторий.",
436
437 // Коллабораторы
438 "collab.title": "Коллабораторы",
439 "collab.desc": "Люди с доступом к репозиторию",
440 "collab.owner": "Владелец",
441 "collab.role": "Роль",
442 "collab.role_read": "Чтение",
443 "collab.role_write": "Запись",
444 "collab.username": "Пользователь",
445 "collab.username_ph": "username",
446 "collab.add": "Добавить",
447 "collab.remove": "Убрать доступ",
448 "collab.empty": "Коллабораторов пока нет.",
449
450 // Ревью
451 "reviews.title": "Ревью",
452 "reviews.approved": "Одобрено",
453 "reviews.changes_requested": "Запрошены изменения",
454 "reviews.body_ph": "Комментарий к ревью (необязательно)",
455 "reviews.approve": "Одобрить",
456 "reviews.request_changes": "Запросить изменения",
457
458 // Device flow (RFC 8628)
459 "device.title": "Подключение устройства",
460 "device.badge": "Git",
461 "device.desc": "Введите код, который показал git или скрипт-помощник, чтобы выдать устройству доступ к вашему аккаунту.",
462 "device.check": "Проверить",
463 "device.authorize_for": "Выдать доступ по коду",
464 "device.scope_hint": "Доступ даётся на чтение и запись репозиториев, к которым у вас есть права, и действует только для этого устройства.",
465 "device.approve": "Разрешить",
466 "device.deny": "Отклонить",
467 "device.signin_to_approve": "Войдите, чтобы подтвердить доступ",
468 "device.helper_hint": "Подсказка: настройте credential helper, чтобы код запрашивался автоматически — см. README.",
469 "device.code_unknown": "Код не найден. Проверьте, что он введён верно.",
470 "device.code_used": "Этот код уже использован или истёк.",
471 "device.approved_now": "Готово! Устройство подключено — вернитесь в терминал.",
472 "device.denied_now": "Запрос отклонён. Устройство не получит доступ.",
473
474 // Flash: форки, коллабораторы, ревью, device flow
475 "flash.fork_self": "Это ваш репозиторий — форк не нужен.",
476 "flash.fork_exists": "У вас уже есть форк этого репозитория.",
477 "flash.fork_created": "Форк создан.",
478 "flash.fork_failed": "Не удалось создать форк: %s",
479 "flash.collab_added": "@%s добавлен в коллабораторы.",
480 "flash.collab_removed": "Доступ коллаборатора отозван.",
481 "flash.collab_failed": "Не удалось изменить коллаборатора: %s",
482 "flash.review_self": "Нельзя ревьюить собственный пул-реквест.",
483 "flash.review_closed": "Ревью доступно только для открытых пул-реквестов.",
484 "flash.review_failed": "Не удалось сохранить ревью: %s",
485 "flash.review_approved": "Ревью сохранено: одобрено.",
486 "flash.review_changes_requested": "Ревью сохранено: запрошены изменения.",
487 "flash.device_code_required": "Введите код устройства.",
488 "flash.device_code_invalid": "Код не найден, истёк или уже использован.",
489 "flash.device_approved": "Устройство авторизовано — можно возвращаться к git.",
490 "flash.device_denied": "Запрос отклонён.",
491
492 // Прочее
493 "issues.new": "Новая задача",
494 "pulls.head_repo": "Из репозитория",
495 "repo.about": "О репозитории",
496 "repo.clone_title": "Клонирование",
497 "repo.created": "Создан",
498 "repo.updated": "Обновлён",
499 "repo.view_file": "Открыть",
500 "err.collab_forbidden": "Управлять коллабораторами может только владелец или администратор.",
501 "err.pr_head_repo_invalid": "Выбран недопустимый репозиторий-источник."},
502 EN: {
503 // Navigation & Common
504 "app.tagline": "Lightweight self-hosted Git hosting",
505 "nav.home": "Home",
506 "nav.repositories": "Repositories",
507 "nav.explore": "Explore",
508 "nav.settings": "Settings",
509 "nav.admin": "Admin",
510 "nav.login": "Sign in",
511 "nav.register": "Sign up",
512 "nav.logout": "Sign out",
513 "nav.new_repo": "New",
514 "nav.command_palette": "Search & commands",
515 "nav.search_placeholder": "Search...",
516 "nav.theme": "Theme",
517 "nav.lang": "Language",
518 "common.copy": "Copy",
519 "common.copied": "Copied",
520 "common.cancel": "Cancel",
521 "common.save": "Save",
522 "common.delete": "Delete",
523 "common.close": "Close",
524 "common.never": "Never",
525 "common.newer": "Newer",
526 "common.older": "Older",
527 "common.up": "Up",
528
529 // Themes (only 3)
530 "theme.light": "Light",
531 "theme.dark": "Dark",
532 "theme.system": "System",
533
534 // Landing
535 "landing.badge": "Single Binary · SQLite · Zero Dependencies",
536 "landing.hero.title": "Lightweight self-hosted Git hosting",
537 "landing.hero.subtitle": "A single Go binary backed by SQLite. Deploys in a minute, uses minimal memory, and stays out of your way.",
538 "landing.hero.cta_start": "Get Started",
539 "landing.features.title": "Principles",
540 "landing.feat1.title": "Single Binary",
541 "landing.feat1.desc": "Templates, styles, fonts, migrations, and icons are compiled in via go:embed.",
542 "landing.feat2.title": "SQLite (WAL)",
543 "landing.feat2.desc": "Your entire database is a single data/polar.db file without CGO. Back up by copying.",
544 "landing.feat3.title": "Fast & Quiet",
545 "landing.feat3.desc": "Instant cold start, server-side rendering, and < 30 MB idle memory.",
546 "landing.feat4.title": "Private by Default",
547 "landing.feat4.desc": "Public and private repositories, Argon2id passwords, and Git HTTP access tokens.",
548
549 // Auth
550 "auth.login.title": "Sign in",
551 "auth.register.title": "Create account",
552 "auth.register.disabled": "Public registration is disabled by the administrator.",
553 "auth.username": "Username",
554 "auth.username_hint": "2–32 characters: letters, digits, hyphen, underscore",
555 "auth.username_or_email": "Username or Email",
556 "auth.email": "Email",
557 "auth.password": "Password",
558 "auth.password_hint": "At least 8 characters",
559 "auth.submit_login": "Sign in",
560 "auth.submit_register": "Create account",
561 "auth.no_account": "No account?",
562 "auth.have_account": "Have an account?",
563 "auth.invalid_creds": "Invalid username/email or password",
564
565 // Repositories
566 "repo.new.title": "New repository",
567 "repo.name": "Name",
568 "repo.name_hint": "Letters, digits, '.', '-', '_'",
569 "repo.description": "Description",
570 "repo.description_ph": "Short description (optional)",
571 "repo.visibility": "Visibility",
572 "repo.public": "Public",
573 "repo.public_desc": "Anyone can view and clone this repository",
574 "repo.private": "Private",
575 "repo.private_desc": "Accessible only to you",
576 "repo.create_btn": "Create repository",
577 "repo.filter_ph": "Filter repositories...",
578 "repo.empty_list": "No repositories yet",
579 "repo.empty_list_sub": "Create your first repository to push code.",
580 "repo.empty.title": "Repository is empty",
581 "repo.empty.new_cmd": "Create a new repository on the command line",
582 "repo.empty.push_cmd": "Push an existing repository",
583 "repo.tab.code": "Code",
584 "repo.tab.commits": "Commits",
585 "repo.tab.branches": "Branches & Tags",
586 "repo.tab.issues": "Issues",
587 "repo.tab.pulls": "Pull requests",
588 "repo.tab.releases": "Releases",
589 "repo.tab.activity": "Activity",
590 "repo.tab.settings": "Settings",
591 "repo.features": "Repository Features",
592 "repo.clone": "Clone",
593 "repo.download_zip": "Download ZIP",
594 "repo.branches": "Branches",
595 "repo.tags": "Tags",
596 "repo.default_branch": "Default branch",
597 "repo.find_file": "Filter files...",
598 "repo.files": "Files",
599 "repo.readme": "README",
600 "repo.raw": "Raw",
601 "repo.history": "History",
602 "repo.lines": "lines",
603 "repo.lines_one": "line",
604 "repo.lines_few": "lines",
605 "repo.lines_many": "lines",
606 "repo.binary_notice": "Binary file — inline preview unavailable",
607 "repo.download_raw": "Download file",
608 "repo.browse_at_commit": "Browse files",
609 "repo.files_changed": "files changed",
610 "repo.files_changed_one": "file changed",
611 "repo.files_changed_few": "files changed",
612 "repo.files_changed_many": "files changed",
613 "repo.no_commits": "No commits yet",
614 "repo.danger_zone": "Danger Zone",
615 "repo.delete": "Delete repository",
616 "repo.delete_desc": "Once deleted, the repository and its git history cannot be recovered.",
617 "repo.delete_confirm": "Type the repository name to confirm",
618 "repo.rename": "Rename",
619 "repo.save": "Save",
620 "repo.branch.new": "New branch",
621 "repo.branch.name_ph": "Branch name (e.g. feat/ui)",
622 "repo.branch.from": "From branch / ref",
623 "repo.branch.create": "Create branch",
624 "repo.branch.default": "default",
625 "repo.tag.new": "New tag / release",
626 "repo.tag.name_ph": "Tag (e.g. v1.0.0)",
627 "repo.tag.msg_ph": "Tag note (optional)",
628 "repo.tag.create": "Create tag",
629 "repo.tags.empty": "No tags yet",
630
631 // Pull Requests
632 "pulls.title": "Pull requests",
633 "pulls.new": "New pull request",
634 "pulls.empty": "No pull requests yet",
635 "pulls.open": "Open",
636 "pulls.closed": "Closed",
637 "pulls.merged": "Merged",
638 "pulls.compare": "Comparing changes",
639 "pulls.base": "base",
640 "pulls.head": "compare",
641 "pulls.title_ph": "Pull request title...",
642 "pulls.desc_ph": "Describe your changes (Markdown supported)...",
643 "pulls.create_btn": "Create pull request",
644 "pulls.merge_btn": "Merge pull request",
645 "pulls.can_merge": "Able to merge automatically.",
646 "pulls.cannot_merge": "Can't automatically merge: conflicts detected.",
647 "pulls.already_merged": "The head branch is already merged into base.",
648 "pulls.close_btn": "Close PR",
649 "pulls.reopen_btn": "Reopen PR",
650 "pulls.comment_btn": "Comment",
651 "pulls.conversation": "Conversation",
652 "pulls.files_changed": "Files changed",
653 "pulls.commits": "Commits",
654 "pulls.able_to_merge": "Able to merge automatically",
655 "pulls.body": "Description",
656 "pulls.body_ph": "Describe your changes (Markdown supported)...",
657 "pulls.branches_identical": "Branches are identical — nothing to compare",
658 "pulls.cannot_merge_warning": "Conflicts detected — automatic merge is not possible",
659 "pulls.closed_desc": "The head branch was not merged into base.",
660 "pulls.closed_message": "Pull request closed",
661 "pulls.comment_ph": "Write a comment (Markdown supported)...",
662 "pulls.compare_branches": "Comparing branches",
663 "pulls.conflicts_desc": "Resolve the conflicts locally and push to the head branch.",
664 "pulls.conflicts_title": "Merge conflicts detected",
665 "pulls.into": "into",
666 "pulls.merge_clean": "No conflicts — changes can be merged.",
667 "pulls.merged_message": "Pull request merged",
668 "pulls.no_changes": "No files changed",
669 "pulls.no_commits": "No commits in this pull request yet",
670 "pulls.only_write_merge": "Only users with write access can merge pull requests.",
671 "pulls.status.closed": "Closed",
672 "pulls.status.merged": "Merged",
673 "pulls.status.open": "Open",
674 "pulls.wants_to_merge": "wants to merge",
675
676 // Releases
677 "releases.title": "Releases",
678 "releases.new": "Draft a new release",
679 "releases.empty": "No releases yet",
680 "releases.tag_select": "Tag",
681 "releases.title_ph": "Release title",
682 "releases.desc_ph": "Describe this release, release notes (Markdown)...",
683 "releases.prerelease": "This is a pre-release",
684 "releases.publish": "Publish release",
685 "releases.delete": "Delete release",
686 "releases.assets": "Assets",
687 "releases.source_zip": "Source code (zip)",
688 "releases.source_tar": "Source code (tar.gz)",
689 "releases.body": "Description",
690 "releases.body_ph": "Describe this release, release notes (Markdown)...",
691 "releases.count": "releases",
692 "releases.count_one": "release",
693 "releases.count_few": "releases",
694 "releases.count_many": "releases",
695 "releases.delete_confirm": "Delete this release? This cannot be undone.",
696 "releases.is_prerelease": "This is a pre-release",
697 "releases.publish_btn": "Publish release",
698 "releases.tag_desc": "An existing tag or a new one — it will be created automatically.",
699 "releases.tag_name": "Tag",
700 "releases.tags": "tags",
701 "releases.tags_one": "tag",
702 "releases.tags_few": "tags",
703 "releases.tags_many": "tags",
704 "releases.target": "Branch",
705 "releases.target_desc": "Branch or commit the tag will point to.",
706
707 // Activity
708 "activity.title": "Activity",
709 "activity.pulse": "Activity overview",
710 "activity.empty": "No recent activity recorded.",
711 "activity.commits": "commits",
712 "activity.open_prs": "open PRs",
713 "activity.merged_prs": "merged PRs",
714 "activity.open_issues": "open issues",
715 "activity.releases": "releases",
716 "activity.issues": "opened and closed",
717 "activity.on_default_branch": "on the default branch",
718 "activity.published_releases": "published releases",
719 "activity.pull_requests": "opened and merged",
720 "activity.recent_feed": "Activity feed",
721 "activity.badge_commit": "commit",
722 "activity.badge_release": "release",
723
724 // Issues
725 "issue.new": "New issue",
726 "issue.title": "Title",
727 "issue.title_ph": "Brief summary of the issue",
728 "issue.body": "Description",
729 "issue.body_ph": "Detailed description (Markdown supported)...",
730 "issue.create": "Create issue",
731 "issue.open": "Open",
732 "issue.closed": "Closed",
733 "issue.status.open": "Open",
734 "issue.status.closed": "Closed",
735 "issue.close_btn": "Close issue",
736 "issue.reopen_btn": "Reopen",
737 "issue.comment_ph": "Write a comment (Markdown supported)...",
738 "issue.comment_btn": "Comment",
739 "issue.empty": "No issues in this list",
740
741 // Settings, Profile & Tokens
742 "settings.title": "Settings",
743 "settings.profile": "Profile",
744 "settings.avatar": "Avatar",
745 "settings.avatar_upload": "Choose image",
746 "settings.avatar_remove": "Remove avatar",
747 "settings.avatar_hint": "PNG, JPG, WebP, or GIF — automatically cropped to 256×256",
748 "settings.full_name": "Display name",
749 "settings.full_name_ph": "Your name",
750 "settings.bio": "Bio",
751 "settings.bio_ph": "Short profile bio",
752 "settings.location": "Location",
753 "settings.location_ph": "City, Country",
754 "settings.website": "Website",
755 "settings.website_ph": "https://example.com",
756 "profile.edit": "Edit profile",
757 "profile.joined": "Joined",
758 "profile.public_repos": "Public",
759 "profile.private_repos": "Private",
760 "profile.total_repos": "Repositories",
761 "settings.tokens": "Access Tokens",
762 "settings.tokens.desc": "Use a token instead of your password for git clone and git push over HTTP.",
763 "settings.token.name": "Token name (e.g., Laptop)",
764 "settings.token.create": "Create",
765 "settings.token.revoke": "Revoke",
766 "settings.token.created_once": "Copy this token now — it will only be shown once:",
767 "settings.token.col_name": "Name",
768 "settings.token.col_prefix": "Prefix",
769 "settings.token.col_created": "Created",
770 "settings.token.col_used": "Last used",
771 "settings.token.empty": "No access tokens yet",
772 "settings.password.current": "Current password",
773 "settings.password.new": "New password",
774 "settings.password.update": "Update password",
775
776 // Admin
777 "admin.title": "Administration",
778 "admin.features": "Features & Access",
779 "admin.users": "Users",
780 "admin.registration": "Public registration",
781 "admin.registration.desc": "Allow new users to create accounts",
782 "admin.explore": "Explore page",
783 "admin.explore.desc": "Show public repository directory in sidebar and at /explore",
784 "admin.landing": "Landing page",
785 "admin.landing.desc": "Show landing page on root URL (if disabled, redirects to repos or sign in)",
786 "admin.anon_read": "Anonymous read access",
787 "admin.anon_read.desc": "Allow unauthenticated guests to browse public repositories and profiles",
788 "admin.issues": "Issue tracker",
789 "admin.issues.desc": "Enable the Issues tab inside repositories",
790 "admin.enabled": "Enabled",
791 "admin.disabled": "Disabled",
792 "admin.enable_btn": "Enable",
793 "admin.disable_btn": "Disable",
794 "admin.col.user": "User",
795 "admin.col.repos": "Repos",
796 "admin.col.role": "Role",
797 "admin.col.joined": "Joined",
798 "admin.role.admin": "Admin",
799 "admin.role.user": "User",
800
801 // Command Palette & Shortcuts
802 "palette.placeholder": "Search repositories, files, or commands...",
803 "palette.group.nav": "Navigation",
804 "palette.group.themes": "Theme",
805 "palette.group.repos": "Repositories",
806 "shortcuts.title": "Keyboard shortcuts",
807 "shortcuts.palette": "Command palette",
808 "shortcuts.search": "Focus search",
809 "shortcuts.home": "Go home",
810 "shortcuts.repos": "Repositories",
811 "shortcuts.files": "Filter files",
812 "shortcuts.sidebar": "Toggle sidebar",
813 "nav.toggle_sidebar": "Sidebar (Ctrl+B)",
814
815 // Flash messages
816 "flash.welcome": "Welcome to Polar, @%s!",
817 "flash.profile_updated": "Profile updated.",
818 "flash.password_updated": "Password changed successfully.",
819 "flash.token_revoked": "Access token revoked.",
820 "flash.settings_updated": "Settings saved.",
821 "flash.error": "Error: %s",
822 "flash.comment_error": "Could not post the comment: %s",
823 "flash.pr_created": "Pull request #%d created.",
824 "flash.pr_closed": "Pull request #%d closed.",
825 "flash.pr_reopened": "Pull request #%d reopened.",
826 "flash.pr_merged": "Pull request #%d merged.",
827 "flash.pr_merged_state": "Cannot change the state of a merged pull request.",
828 "flash.pr_merge_open_only": "Only an open pull request can be merged.",
829 "flash.pr_merge_failed": "Could not merge: %s",
830 "flash.release_tag_empty": "Tag name cannot be empty.",
831 "flash.release_tag_failed": "Could not create the tag: %s",
832 "flash.release_create_failed": "Could not create the release: %s",
833 "flash.release_published": "Release %s published.",
834 "flash.release_delete_failed": "Could not delete the release: %s",
835 "flash.release_deleted": "Release deleted.",
836 "file.new_file": "New file",
837 "file.new_folder": "New folder",
838 "file.path": "Path",
839 "file.path_ph": "e.g. src/main.go",
840 "file.folder_ph": "e.g. docs",
841 "file.branch": "Branch",
842 "file.content": "Content",
843 "file.content_ph": "File contents…",
844 "file.message": "Commit message",
845 "file.create_file": "Create file",
846 "file.create_folder": "Create folder",
847 "file.folder_hint": "Git does not track empty folders — Polar adds a .gitkeep placeholder.",
848 "flash.file_created": "File created.",
849 "flash.folder_created": "Folder created.",
850 "flash.file_exists": "A file with that path already exists.",
851 "flash.path_invalid": "Invalid path.",
852 "flash.file_create_failed": "Could not create the file.",
853 "flash.folder_create_failed": "Could not create the folder.",
854 "flash.repo_settings_saved": "Repository settings saved.",
855 "flash.repo_rename_failed": "Could not rename: %s",
856 "flash.repo_renamed": "Repository renamed to %s.",
857 "flash.repo_confirm_mismatch": "The name you typed does not match the repository name.",
858 "flash.repo_deleted": "Repository %s deleted.",
859 "flash.branch_created": "Branch %s created.",
860 "flash.branch_create_failed": "Could not create the branch: %s",
861 "flash.branch_default_delete": "The default branch cannot be deleted.",
862 "flash.branch_deleted": "Branch %s deleted.",
863 "flash.branch_delete_failed": "Could not delete the branch: %s",
864 "flash.tag_created": "Tag %s created.",
865 "flash.tag_create_failed": "Could not create the tag: %s",
866 "flash.tag_deleted": "Tag %s deleted.",
867 "flash.tag_delete_failed": "Could not delete the tag: %s",
868 "flash.issue_closed": "Issue #%d closed.",
869 "flash.issue_reopened": "Issue #%d reopened.",
870
871 // Error pages
872 "err.not_found": "Not found",
873 "err.forbidden": "Access denied",
874 "err.session_error": "Session error",
875 "err.session_create": "Could not create a session.",
876 "err.settings_error": "Settings error",
877 "err.settings_save": "Could not save the setting.",
878 "err.explore_disabled": "Explore is disabled on this server.",
879 "err.activity_disabled": "Activity is disabled for this repository.",
880 "err.pulls_disabled": "Pull requests are disabled for this repository.",
881 "err.releases_disabled": "Releases are disabled for this repository.",
882 "err.issues_disabled": "Issues are disabled for this repository.",
883 "err.repo_not_found": "Repository not found",
884 "err.repo_not_found_desc": "The repository does not exist or you do not have access to it.",
885 "err.repo_private_desc": "The repository does not exist or is private.",
886 "err.access_denied": "Access denied",
887 "err.repo_write_denied": "You do not have permission to modify this repository.",
888 "err.invalid_repo_path": "Invalid repository path",
889 "err.path_not_found": "Path not found",
890 "err.path_not_found_desc": "Could not find the path in the repository for ref %s",
891 "err.file_not_found": "File not found",
892 "err.file_not_found_desc": "The requested file was not found in %s",
893 "err.commit_not_found": "Commit not found",
894 "err.commit_not_found_desc": "Could not find commit %s",
895 "err.pr_not_found": "Pull request not found",
896 "err.pr_not_found_desc": "Could not find the pull request.",
897 "err.pr_state_forbidden": "You cannot change the state of this pull request.",
898 "err.pr_merge_forbidden": "You do not have permission to merge pull requests.",
899 "err.issue_not_found": "Issue not found",
900 "err.issue_not_found_desc": "Could not find the issue.",
901 "err.issue_state_forbidden": "You cannot change the state of this issue.",
902 "err.delete_error": "Delete error",
903 "err.repo_delete_failed": "Could not delete the repository.",
904 "err.user_not_found": "User @%s does not exist.",
905 "err.archive_error": "Archive error",
906 "err.archive_tar_failed": "Could not create a tar.gz archive for %s",
907 "err.archive_zip_failed": "Could not create a zip archive for %s",
908
909 // Forks
910 "fork.forked_from": "forked from",
911 "fork.forks": "Forks",
912 "fork.forks_btn": "Forks",
913 "fork.fork_btn": "Fork",
914 "fork.fork_btn_desc": "Create a fork of this repository",
915 "fork.your_fork": "Your fork",
916 "fork.your_fork_desc": "Go to your fork",
917 "forks.title": "Forks",
918 "forks.empty": "Nobody has forked this repository yet.",
919
920 // Collaborators
921 "collab.title": "Collaborators",
922 "collab.desc": "People with access to this repository",
923 "collab.owner": "Owner",
924 "collab.role": "Role",
925 "collab.role_read": "Read",
926 "collab.role_write": "Write",
927 "collab.username": "User",
928 "collab.username_ph": "username",
929 "collab.add": "Add",
930 "collab.remove": "Revoke access",
931 "collab.empty": "No collaborators yet.",
932
933 // Reviews
934 "reviews.title": "Reviews",
935 "reviews.approved": "Approved",
936 "reviews.changes_requested": "Changes requested",
937 "reviews.body_ph": "Review comment (optional)",
938 "reviews.approve": "Approve",
939 "reviews.request_changes": "Request changes",
940
941 // Device flow (RFC 8628)
942 "device.title": "Device authorization",
943 "device.badge": "Git",
944 "device.desc": "Enter the code shown by git or the credential helper to grant this device access to your account.",
945 "device.check": "Check",
946 "device.authorize_for": "Grant access to code",
947 "device.scope_hint": "Access covers repositories you can already reach and is bound to this device only.",
948 "device.approve": "Approve",
949 "device.deny": "Deny",
950 "device.signin_to_approve": "Sign in to approve access",
951 "device.helper_hint": "Tip: configure the credential helper so the code is requested automatically — see the README.",
952 "device.code_unknown": "Code not found. Double-check the code you entered.",
953 "device.code_used": "This code has already been used or expired.",
954 "device.approved_now": "All set! The device is authorized — return to your terminal.",
955 "device.denied_now": "Request denied. The device will not get access.",
956
957 // Flash: forks, collaborators, reviews, device flow
958 "flash.fork_self": "This repository is yours — no fork needed.",
959 "flash.fork_exists": "You already have a fork of this repository.",
960 "flash.fork_created": "Fork created.",
961 "flash.fork_failed": "Could not create fork: %s",
962 "flash.collab_added": "@%s added as a collaborator.",
963 "flash.collab_removed": "Collaborator access revoked.",
964 "flash.collab_failed": "Could not update collaborator: %s",
965 "flash.review_self": "You cannot review your own pull request.",
966 "flash.review_closed": "Reviews are only available on open pull requests.",
967 "flash.review_failed": "Could not save the review: %s",
968 "flash.review_approved": "Review saved: approved.",
969 "flash.review_changes_requested": "Review saved: changes requested.",
970 "flash.device_code_required": "Enter the device code.",
971 "flash.device_code_invalid": "Code not found, expired, or already used.",
972 "flash.device_approved": "Device authorized — you can return to git.",
973 "flash.device_denied": "Request denied.",
974
975 // Misc
976 "issues.new": "New issue",
977 "pulls.head_repo": "From repository",
978 "repo.about": "About",
979 "repo.clone_title": "Clone",
980 "repo.created": "Created",
981 "repo.updated": "Updated",
982 "repo.view_file": "Open",
983 "err.collab_forbidden": "Only the owner or an administrator can manage collaborators.",
984 "err.pr_head_repo_invalid": "The selected source repository is not allowed."},
985 }
986
987 func T(lang Lang, key string) string {
988 dict, ok := translations[Normalize(string(lang))]
989 if !ok {
990 dict = translations[RU]
991 }
992 if val, ok := dict[key]; ok {
993 return val
994 }
995 if val, ok := translations[RU][key]; ok {
996 return val
997 }
998 return key
999 }
1000