Files
rss-ntfy/app/static/i18n.js
T

449 lines
19 KiB
JavaScript
Raw Normal View History

2026-06-02 21:11:57 +08:00
"use strict";
/* Lightweight i18n: dictionaries + t() + applyI18n(). Shared by login & app. */
const I18N = {
ru: {
"nav.feeds": "Ленты",
"nav.history": "История",
"nav.users": "Пользователи",
"nav.settings": "Настройки",
"topbar.logout": "Выйти",
"theme.toggle": "Сменить тему",
"stats.feeds": "лент",
"stats.enabled": "активных",
"stats.failing": "с ошибками",
"stats.sent": "отправлено",
"stats.failed": "сбоев",
"chart.title": "Активность за 14 дней",
"chart.sent": "Отправлено",
"chart.failed": "Сбои",
"chart.empty": "Нет данных за период",
"feeds.heading": "RSS-ленты",
"feeds.checkAll": "↻ Проверить все",
"feeds.import": "⬆ Импорт OPML",
"feeds.export": "⬇ Экспорт OPML",
"feeds.add": "+ Добавить ленту",
"feeds.empty": "Пока нет ни одной ленты. Добавьте первую, чтобы начать получать уведомления.",
"feeds.never": "ещё не проверялась",
"feeds.noTopic": "— тема не задана —",
"history.heading": "История уведомлений",
"history.refresh": "↻ Обновить",
"history.clear": "Очистить",
"history.search": "Поиск по заголовку или ленте…",
"history.onlyErrors": "Только ошибки",
"history.empty": "История пуста.",
"users.heading": "Пользователи",
"users.add": "+ Добавить пользователя",
"users.admin": "👑 администратор",
"users.viewer": "👁 наблюдатель",
"settings.heading": "Настройки",
"settings.ntfy": "ntfy",
"settings.defaultServer": "Сервер ntfy по умолчанию",
"settings.defaultServerHint": "Используется для лент, у которых не задан собственный сервер.",
"settings.ntfyAuth": "Авторизация на сервере ntfy",
"settings.ntfyAuthHint": "Нужно, если на сервере включён контроль доступа (ошибка 403 при отправке). Применяется к тесту, алертам и лентам без собственной авторизации.",
2026-06-02 21:11:57 +08:00
"settings.testPh": "тема для теста, напр. my-news",
"settings.testBtn": "Отправить тест",
"settings.check": "Проверка",
"settings.interval": "Интервал проверки по умолчанию (минуты)",
"settings.intervalHint": "Можно переопределить для каждой ленты отдельно.",
"settings.telegram": "Telegram",
"settings.tgEnable": "Включить доставку в Telegram",
"settings.tgToken": "Bot Token",
"settings.tgChat": "Chat ID",
"settings.tgHint": "Создайте бота через @BotFather, добавьте его в чат и укажите chat_id. Затем включите канал в нужных лентах.",
"settings.webhook": "Webhook",
"settings.whEnable": "Включить доставку через webhook",
"settings.whUrl": "URL webhook",
"settings.whHint": "POST с JSON: feed, feed_url, title, body, link, image.",
"settings.alerts": "Оповещения администратора",
"settings.alertEnable": "Уведомлять, если лента «упала»",
"settings.alertTopic": "Тема ntfy для алертов",
"settings.alertThreshold": "Порог (ошибок подряд)",
"settings.auth": "Авторизация",
"settings.authRequire": "Требовать вход в веб-панель",
"settings.authHint": "Учётные записи управляются во вкладке «Пользователи».",
"settings.save": "Сохранить настройки",
"modal.addFeed": "Добавить ленту",
"modal.editFeed": "Редактировать ленту",
"modal.cancel": "Отмена",
"modal.save": "Сохранить",
"feed.url": "URL ленты *",
"feed.title": "Название",
"feed.titleOpt": "(необязательно, определится автоматически)",
"feed.server": "Сервер ntfy",
"feed.serverHint": "(пусто = по умолчанию)",
"feed.topic": "Тема ntfy",
"feed.priv": "Приватный ntfy-сервер (авторизация)",
"feed.token": "Access token",
"feed.tokenHint": "(tk_…, приоритетнее логина)",
"feed.login": "Логин",
"feed.password": "Пароль",
"feed.priority": "Приоритет",
"feed.p1": "1 — минимальный",
"feed.p2": "2 — низкий",
"feed.p3": "3 — обычный",
"feed.p4": "4 — высокий",
"feed.p5": "5 — максимальный",
"feed.intervalMin": "Интервал, мин",
"feed.intervalHint": "(0 = общий)",
"feed.tags": "Теги / эмодзи",
"feed.commaHint": "(через запятую)",
"feed.filterInc": "Фильтр: только с этими словами",
"feed.filterExc": "Фильтр: исключить слова",
"feed.attach": "Прикреплять картинку",
"feed.dupTg": "Дублировать в Telegram",
"feed.toWebhook": "Отправлять в webhook",
"feed.enabled": "Лента включена",
"feed.preview": "👁 Предпросмотр",
"feed.previewLoading": "Загрузка…",
"feed.previewHint": "Введите URL и нажмите «Предпросмотр», чтобы увидеть последнюю запись.",
"user.addTitle": "Добавить пользователя",
"user.editTitle": "Редактировать пользователя",
"user.login": "Логин *",
"user.password": "Пароль",
"user.pwReq": "*",
"user.pwKeep": "(пусто = не менять)",
"user.role": "Роль",
"user.roleAdmin": "Администратор (полный доступ)",
"user.roleViewer": "Наблюдатель (только просмотр)",
"toast.feedDeleted": "Лента удалена",
"toast.feedAdded": "Лента добавлена",
"toast.feedUpdated": "Лента обновлена",
"toast.saved": "Сохранено",
"toast.deleted": "Удалён",
"toast.checkDone": "Проверка завершена",
"toast.historyCleared": "История очищена",
"toast.settingsSaved": "Настройки сохранены",
"toast.sentTo": "Отправлено в {dest}",
"toast.imported": "Импортировано {added} из {total}",
"toast.needTestTopic": "Укажите тему для теста",
"toast.needUrl": "Сначала укажите URL ленты",
"confirm.deleteFeed": "Удалить ленту «{name}»?",
"confirm.deleteUser": "Удалить пользователя «{name}»?",
"confirm.clearHistory": "Очистить всю историю?",
"status.init": "Инициализировано ({n} записей)",
"status.sent": "Отправлено {n} новых",
"status.sentSkip": "Отправлено {n} новых, пропущено {s}",
"status.filtered": "Без изменений (отфильтровано {s})",
"status.nochange": "Без изменений",
"status.parseError": "Ошибка: {msg}",
"status.sendError": "Ошибка отправки: {msg}",
"status.dash": "—",
"nav.categories": "Категории",
"categories.heading": "Категории",
"categories.add": "+ Добавить категорию",
"categories.empty": "Категорий пока нет.",
"cat.addTitle": "Добавить категорию",
"cat.editTitle": "Редактировать категорию",
"cat.name": "Название *",
"cat.sortOrder": "Порядок сортировки",
"feed.category": "Категория",
"feed.categoryNone": "— без категории —",
"feed.fullContent": "Отправлять полный контент",
"feed.fullContentHint": "Весь текст, все картинки и видео. Для ntfy — Markdown.",
"settings.feedDefaults": "Значения по умолчанию для новых лент",
"confirm.deleteCategory": "Удалить категорию «{name}»?",
"confirm.deleteCategoryFeeds": "Удалить категорию «{name}»? {n} лент будут откреплены.",
"toast.categoryAdded": "Категория добавлена",
"toast.categoryUpdated": "Категория обновлена",
"toast.categoryDeleted": "Категория удалена",
"nav.reader": "Чтение",
"reader.all": "Все",
"reader.markAll": "Отметить все прочитанными",
"reader.back": "← Назад",
"reader.open": "Открыть оригинал →",
"reader.empty": "Статей пока нет. Добавьте ленты, чтобы начать читать.",
"feed.digest": "Дайджест",
"feed.digestEnable": "Накапливать записи (дайджест)",
"feed.digestPeriod": "Период дайджеста (часы)",
"feed.fetchArticle": "Загружать полную статью (trafilatura)",
"feed.fetchArticleHint": "Загружает страницу статьи и извлекает основной текст.",
"settings.template": "Шаблон уведомлений",
"settings.templateHint": "Переменные: {title}, {body}, {link}, {source}, {image_url}",
"settings.proxyUrl": "URL прокси",
"settings.proxyHint": "Например: http://proxy:8080 или socks5://proxy:1080",
"feeds.backup": "💾 Бэкап",
"feeds.restore": "📥 Восстановить",
"confirm.restore": "Восстановление заменит всю текущую базу данных. Продолжить?",
"toast.restored": "База восстановлена. Перезагрузка...",
"toast.articlesMarked": "Все статьи отмечены прочитанными",
2026-06-02 21:11:57 +08:00
"role.admin": "админ",
"role.viewer": "наблюдатель",
"login.subtitle": "Войдите, чтобы продолжить",
"login.user": "Логин",
"login.pass": "Пароль",
"login.submit": "Войти",
"login.error": "Неверный логин или пароль",
},
en: {
"nav.feeds": "Feeds",
"nav.history": "History",
"nav.users": "Users",
"nav.settings": "Settings",
"topbar.logout": "Log out",
"theme.toggle": "Toggle theme",
"stats.feeds": "feeds",
"stats.enabled": "active",
"stats.failing": "failing",
"stats.sent": "sent",
"stats.failed": "failed",
"chart.title": "Activity (last 14 days)",
"chart.sent": "Sent",
"chart.failed": "Failed",
"chart.empty": "No data for this period",
"feeds.heading": "RSS feeds",
"feeds.checkAll": "↻ Check all",
"feeds.import": "⬆ Import OPML",
"feeds.export": "⬇ Export OPML",
"feeds.add": "+ Add feed",
"feeds.empty": "No feeds yet. Add your first one to start receiving notifications.",
"feeds.never": "not checked yet",
"feeds.noTopic": "— no topic set —",
"history.heading": "Notification history",
"history.refresh": "↻ Refresh",
"history.clear": "Clear",
"history.search": "Search by title or feed…",
"history.onlyErrors": "Errors only",
"history.empty": "History is empty.",
"users.heading": "Users",
"users.add": "+ Add user",
"users.admin": "👑 administrator",
"users.viewer": "👁 viewer",
"settings.heading": "Settings",
"settings.ntfy": "ntfy",
"settings.defaultServer": "Default ntfy server",
"settings.defaultServerHint": "Used for feeds that don't define their own server.",
"settings.ntfyAuth": "ntfy server authentication",
"settings.ntfyAuthHint": "Needed if the server has access control enabled (403 on publish). Applies to the test, alerts and feeds without their own auth.",
2026-06-02 21:11:57 +08:00
"settings.testPh": "topic to test, e.g. my-news",
"settings.testBtn": "Send test",
"settings.check": "Polling",
"settings.interval": "Default poll interval (minutes)",
"settings.intervalHint": "Can be overridden per feed.",
"settings.telegram": "Telegram",
"settings.tgEnable": "Enable Telegram delivery",
"settings.tgToken": "Bot Token",
"settings.tgChat": "Chat ID",
"settings.tgHint": "Create a bot via @BotFather, add it to a chat and set the chat_id. Then enable the channel on the feeds you want.",
"settings.webhook": "Webhook",
"settings.whEnable": "Enable webhook delivery",
"settings.whUrl": "Webhook URL",
"settings.whHint": "POST with JSON: feed, feed_url, title, body, link, image.",
"settings.alerts": "Admin alerts",
"settings.alertEnable": "Notify when a feed keeps failing",
"settings.alertTopic": "ntfy topic for alerts",
"settings.alertThreshold": "Threshold (consecutive errors)",
"settings.auth": "Authentication",
"settings.authRequire": "Require login to the web panel",
"settings.authHint": "Accounts are managed on the «Users» tab.",
"settings.save": "Save settings",
"modal.addFeed": "Add feed",
"modal.editFeed": "Edit feed",
"modal.cancel": "Cancel",
"modal.save": "Save",
"feed.url": "Feed URL *",
"feed.title": "Title",
"feed.titleOpt": "(optional, detected automatically)",
"feed.server": "ntfy server",
"feed.serverHint": "(empty = default)",
"feed.topic": "ntfy topic",
"feed.priv": "Private ntfy server (authentication)",
"feed.token": "Access token",
"feed.tokenHint": "(tk_…, takes precedence over login)",
"feed.login": "Username",
"feed.password": "Password",
"feed.priority": "Priority",
"feed.p1": "1 — min",
"feed.p2": "2 — low",
"feed.p3": "3 — default",
"feed.p4": "4 — high",
"feed.p5": "5 — max",
"feed.intervalMin": "Interval, min",
"feed.intervalHint": "(0 = global)",
"feed.tags": "Tags / emojis",
"feed.commaHint": "(comma separated)",
"feed.filterInc": "Filter: only with these words",
"feed.filterExc": "Filter: exclude words",
"feed.attach": "Attach image",
"feed.dupTg": "Mirror to Telegram",
"feed.toWebhook": "Send to webhook",
"feed.enabled": "Feed enabled",
"feed.preview": "👁 Preview",
"feed.previewLoading": "Loading…",
"feed.previewHint": "Enter a URL and click «Preview» to see the latest entry.",
"user.addTitle": "Add user",
"user.editTitle": "Edit user",
"user.login": "Username *",
"user.password": "Password",
"user.pwReq": "*",
"user.pwKeep": "(empty = keep current)",
"user.role": "Role",
"user.roleAdmin": "Administrator (full access)",
"user.roleViewer": "Viewer (read-only)",
"toast.feedDeleted": "Feed deleted",
"toast.feedAdded": "Feed added",
"toast.feedUpdated": "Feed updated",
"toast.saved": "Saved",
"toast.deleted": "Deleted",
"toast.checkDone": "Check complete",
"toast.historyCleared": "History cleared",
"toast.settingsSaved": "Settings saved",
"toast.sentTo": "Sent to {dest}",
"toast.imported": "Imported {added} of {total}",
"toast.needTestTopic": "Enter a topic to test",
"toast.needUrl": "Enter the feed URL first",
"confirm.deleteFeed": "Delete feed «{name}»?",
"confirm.deleteUser": "Delete user «{name}»?",
"confirm.clearHistory": "Clear the entire history?",
"status.init": "Initialized ({n} entries)",
"status.sent": "Sent {n} new",
"status.sentSkip": "Sent {n} new, skipped {s}",
"status.filtered": "No changes (filtered out {s})",
"status.nochange": "No changes",
"status.parseError": "Error: {msg}",
"status.sendError": "Send error: {msg}",
"status.dash": "—",
"nav.categories": "Categories",
"categories.heading": "Categories",
"categories.add": "+ Add category",
"categories.empty": "No categories yet.",
"cat.addTitle": "Add category",
"cat.editTitle": "Edit category",
"cat.name": "Name *",
"cat.sortOrder": "Sort order",
"feed.category": "Category",
"feed.categoryNone": "— no category —",
"feed.fullContent": "Send full content",
"feed.fullContentHint": "Full text, all images and videos. For ntfy — Markdown.",
"settings.feedDefaults": "Default values for new feeds",
"confirm.deleteCategory": "Delete category «{name}»?",
"confirm.deleteCategoryFeeds": "Delete category «{name}»? {n} feeds will be uncategorized.",
"toast.categoryAdded": "Category added",
"toast.categoryUpdated": "Category updated",
"toast.categoryDeleted": "Category deleted",
"nav.reader": "Reader",
"reader.all": "All",
"reader.markAll": "Mark all read",
"reader.back": "← Back",
"reader.open": "Open original →",
"reader.empty": "No articles yet. Add feeds to start reading.",
"feed.digest": "Digest",
"feed.digestEnable": "Accumulate entries (digest)",
"feed.digestPeriod": "Digest period (hours)",
"feed.fetchArticle": "Fetch full article (trafilatura)",
"feed.fetchArticleHint": "Fetches the article page and extracts main text.",
"settings.template": "Notification template",
"settings.templateHint": "Variables: {title}, {body}, {link}, {source}, {image_url}",
"settings.proxyUrl": "Proxy URL",
"settings.proxyHint": "Example: http://proxy:8080 or socks5://proxy:1080",
"feeds.backup": "💾 Backup",
"feeds.restore": "📥 Restore",
"confirm.restore": "Restore will replace the entire database. Continue?",
"toast.restored": "Database restored. Reloading...",
"toast.articlesMarked": "All articles marked read",
2026-06-02 21:11:57 +08:00
"role.admin": "admin",
"role.viewer": "viewer",
"login.subtitle": "Sign in to continue",
"login.user": "Username",
"login.pass": "Password",
"login.submit": "Sign in",
"login.error": "Wrong username or password",
},
};
function getLang() {
const l = localStorage.getItem("lang");
if (l === "ru" || l === "en") return l;
return (navigator.language || "en").startsWith("ru") ? "ru" : "en";
}
function setLang(lang) {
localStorage.setItem("lang", lang);
document.documentElement.lang = lang;
}
function t(key, params) {
let s = (I18N[getLang()] || I18N.en)[key] ?? key;
if (params) for (const k in params) s = s.replaceAll(`{${k}}`, params[k]);
return s;
}
function applyI18n(root = document) {
root.querySelectorAll("[data-i18n]").forEach(el => {
el.textContent = t(el.getAttribute("data-i18n"));
});
root.querySelectorAll("[data-i18n-ph]").forEach(el => {
el.setAttribute("placeholder", t(el.getAttribute("data-i18n-ph")));
});
root.querySelectorAll("[data-i18n-title]").forEach(el => {
el.setAttribute("title", t(el.getAttribute("data-i18n-title")));
});
}
/* Theme + locale helpers shared across pages. */
function getTheme() {
return localStorage.getItem("theme") === "light" ? "light" : "dark";
}
function setTheme(theme) {
localStorage.setItem("theme", theme);
document.documentElement.setAttribute("data-theme", theme);
}
function localeTag() {
return getLang() === "ru" ? "ru-RU" : "en-US";
}