diff --git a/package.json b/package.json
index 0ce0152..059fed2 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"ra-language-german": "^3.13.4",
"ra-language-italian": "^3.13.1",
"ra-language-farsi": "^4.2.0",
+ "ra-language-russian": "^4.14.2",
"react": "^18.0.0",
"react-admin": "^4.16.15",
"react-dom": "^18.0.0",
diff --git a/src/App.jsx b/src/App.jsx
index 1eff2a7..352fb0a 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -23,6 +23,7 @@ import englishMessages from "./i18n/en";
import frenchMessages from "./i18n/fr";
import chineseMessages from "./i18n/zh";
import italianMessages from "./i18n/it";
+import russianMessages from "./i18n/ru";
// TODO: Can we use lazy loading together with browser locale?
const messages = {
@@ -31,6 +32,7 @@ const messages = {
fr: frenchMessages,
it: italianMessages,
zh: chineseMessages,
+ ru: russianMessages
};
const i18nProvider = polyglotI18nProvider(
locale => (messages[locale] ? messages[locale] : messages.en),
diff --git a/src/components/LoginPage.jsx b/src/components/LoginPage.jsx
index b540515..82a8772 100644
--- a/src/components/LoginPage.jsx
+++ b/src/components/LoginPage.jsx
@@ -293,6 +293,7 @@ const LoginPage = () => {
+
{formDataProps => }
diff --git a/src/i18n/ru.js b/src/i18n/ru.js
new file mode 100644
index 0000000..c14439d
--- /dev/null
+++ b/src/i18n/ru.js
@@ -0,0 +1,390 @@
+import russianMessages from "ra-language-russian";
+
+const ru = {
+ ...russianMessages,
+ synapseadmin: {
+ auth: {
+ base_url: "Домашняя страница",
+ welcome: "Добро пожаловать в Synapse-admin",
+ server_version: "Версия Synapse",
+ supports_specs: "поддерживает спецификации Matrix",
+ username_error: "Введите полный идентификатор пользователя: '@user:domain'",
+ protocol_error: "Адрес должен начинаться с 'http://' или 'https://'",
+ url_error: "Некорректный сервер Matrix",
+ sso_sign_in: "Присоединиться с помощью технологии единого входа",
+ },
+ users: {
+ invalid_user_id: "Локальная часть идентификатора пользователя Matrix без домашнего сервера.",
+ tabs: { sso: "Технология единого входа" },
+ },
+ rooms: {
+ tabs: {
+ basic: "Основное",
+ members: "Участники",
+ detail: "Подробности",
+ permission: "Права",
+ },
+ },
+ reports: { tabs: { basic: "Основное", detail: "Подробности" } },
+ },
+ import_users: {
+ error: {
+ at_entry: "При входе %{entry}: %{message}",
+ error: "Ошибка",
+ required_field: "Обязательное поле '%{field}' не представленно",
+ invalid_value:
+ "Недопустимое значение в строке %{row}. '%{field}' поле может быть только 'true' или 'false'",
+ unreasonably_big:
+ "Отказался загружать неоправданно большой файл %{size} Мбайт",
+ already_in_progress: "Импорт уже запущен",
+ id_exits: "Идентификатор %{id} уже существует",
+ },
+ title: "Импорт пользователей из CSV",
+ goToPdf: "В PDF",
+ cards: {
+ importstats: {
+ header: "Импорт пользователей",
+ users_total:
+ "%{smart_count} пользователь в CSV файл |||| %{smart_count} пользователей в CSV файл",
+ guest_count: "%{smart_count} гость |||| %{smart_count} гостей",
+ admin_count: "%{smart_count} администратор |||| %{smart_count} администраторов",
+ },
+ conflicts: {
+ header: "Решение конфликтов",
+ mode: {
+ stop: "Остановиться, если конфликт произошел",
+ skip: "Вывести ошибку и пропустить конфликт",
+ },
+ },
+ ids: {
+ header: "Идентификаторы",
+ all_ids_present: "Идентификаторы представлены для каждой записи",
+ count_ids_present:
+ "%{smart_count} запись с идентификатором |||| %{smart_count} записей с идентификатором",
+ mode: {
+ ignore: "Игнорировать идентификаторы в CSV и создавать новые",
+ update: "Обновлять существующие записи",
+ },
+ },
+ passwords: {
+ header: "Пароли",
+ all_passwords_present: "Пароли представлены для каждой записи",
+ count_passwords_present:
+ "%{smart_count} запись с паролем |||| %{smart_count} записей с паролем",
+ use_passwords: "Использовать пароли из CSV",
+ },
+ upload: {
+ header: "Загрузка CSV файла",
+ explanation:
+ "Здесь вы можете загрузить файл со значениями, разделенными запятыми, который будет обработан для создания или обновления пользователей. Файл должен содержать поля 'id' и 'displayname'. Вы можете загрузить пример здесь:",
+ },
+ startImport: {
+ simulate_only: "Не выполнять реальных действий",
+ run_import: "Импорт",
+ },
+ results: {
+ header: "Импорт результатов",
+ total:
+ "Всего %{smart_count} запись |||| Всего %{smart_count} записей",
+ successful: "%{smart_count} записей успешно импортировано",
+ skipped: "%{smart_count} записей пропущено",
+ download_skipped: "Загрузить пропущенные записи",
+ with_error:
+ "%{smart_count} запись с ошибками ||| %{smart_count} записей с ошибками",
+ simulated_only: "Результат не будет сохранен",
+ },
+ },
+ },
+ resources: {
+ users: {
+ name: "Пользователь |||| Пользователи",
+ email: "Почта",
+ msisdn: "Телефон",
+ threepid: "Почта / Телефон",
+ fields: {
+ avatar: "Аватар",
+ id: "Идентификатор пользователя",
+ name: "Имя",
+ is_guest: "Гость",
+ admin: "Администратор",
+ deactivated: "Деактивирован",
+ guests: "Показать гостей",
+ show_deactivated: "Показать деактивированных пользователей",
+ user_id: "Найти пользователя",
+ displayname: "Отображаемое имя",
+ password: "Пароль",
+ avatar_url: "Ссылка на аватар",
+ avatar_src: "Аватар",
+ medium: "Тип",
+ threepids: "Иной идентификатор",
+ address: "Адрес",
+ creation_ts_ms: "Время создания",
+ consent_version: "Версия соглашения",
+ auth_provider: "Поставщик",
+ user_type: "Тип пользователя",
+ },
+ helper: {
+ password: "Изменение пароля приведет к выходу пользователя из всех сеансов.",
+ deactivate: "Вы должны ввести пароль для повторной активации учетной записи.",
+ erase: "Пометить пользователя как удаленного в связи с защитой персональных данных",
+ },
+ action: {
+ erase: "Удаление пользовательских данных",
+ },
+ },
+ rooms: {
+ name: "Комната |||| Комнаты",
+ fields: {
+ room_id: "Идентификатор комнаты",
+ name: "Название",
+ canonical_alias: "Псевдоним",
+ joined_members: "Участники",
+ joined_local_members: "Внутренние участники",
+ joined_local_devices: "Используемые устройства",
+ state_events: "События изменения состояния",
+ version: "Версия",
+ is_encrypted: "Зашифрованно",
+ encryption: "Шифрование",
+ federatable: "Федерация",
+ public: "Видимость в списке комнат",
+ creator: "Создатель",
+ join_rules: "Правила присоединения",
+ guest_access: "Гостевой доступ",
+ history_visibility: "Видимость истории",
+ topic: "Тема",
+ avatar: "Аватар",
+ },
+ helper: {
+ forward_extremities:
+ "Перенаправленные заключения, это такие события, которые не имеют потомков в рамках графа, отвечающего за их репрезентацию. Чем больше пользователей находится в комнате, тем больше операций требуется выполнить Synapse для разрешения коллиций, которые возникают при их проверке наступления событий (это дорогостоящая операция). Хотя в Synapse есть код, предотвращающий одновременное присутствие слишком большого количества таких объектов в комнате, ошибки иногда могут привести к их повторному появлению. Если в комнате содержится >10 перенаправленных заключений, имеет смысл выяснить, какая комната стала причиной их появления и, возможно, удалить их, используя SQL-запросы, упомянутые issue #1760.",
+ },
+ enums: {
+ join_rules: {
+ public: "Публичный",
+ knock: "По запросу",
+ invite: "По приглашению",
+ private: "Приватный",
+ },
+ guest_access: {
+ can_join: "Гости могут присоединиться",
+ forbidden: "Гости не могут присоединиться",
+ },
+ history_visibility: {
+ invited: "С момента приглашения",
+ joined: "С момента присоединения",
+ shared: "С момента разрешения",
+ world_readable: "Всегда",
+ },
+ unencrypted: "Не зашифрованно",
+ },
+ action: {
+ erase: {
+ title: "Удалить комнату",
+ content:
+ "Вы уверены, что хотите удалить комнату? Это невозможно отменить. Все сообщения и медиафайлы, находящиеся в общем доступе в комнате, будут удалены с сервера!",
+ },
+ },
+ },
+ reports: {
+ name: "Жалоба |||| Жалобы",
+ fields: {
+ id: "идентификатор",
+ received_ts: "время жалобы",
+ user_id: "коментатор",
+ name: "название комнаты",
+ score: "оценка",
+ reason: "причина",
+ event_id: "идентификатор события",
+ event_json: {
+ origin: "сервер",
+ origin_server_ts: "время отправки",
+ type: "тип события",
+ content: {
+ msgtype: "тип содержания",
+ body: "содержание",
+ format: "формат",
+ formatted_body: "форматированное содержание",
+ algorithm: "алгоритм",
+ },
+ },
+ },
+ action: {
+ erase: {
+ title: "Удалить жалобу",
+ content:
+ "Вы уверены, что хотите удалить жалобу? Это невозможно отменить.",
+ },
+ },
+ },
+ connections: {
+ name: "Подключения",
+ fields: {
+ last_seen: "Дата",
+ ip: "IP адрес",
+ user_agent: "User agent",
+ },
+ },
+ devices: {
+ name: "Устройство |||| Устройства",
+ fields: {
+ device_id: "Идентификатор устройства",
+ display_name: "Название устройства",
+ last_seen_ts: "Метка времени",
+ last_seen_ip: "IP адрес",
+ },
+ action: {
+ erase: {
+ title: "Удаление %{id}",
+ content: 'Вы уверены, что хотите удалить устройство "%{name}"?',
+ success: "Устройство успешно удалено.",
+ failure: "Произошла ошибка.",
+ },
+ },
+ },
+ users_media: {
+ name: "Медиа файлы",
+ fields: {
+ media_id: "Идентификатор медиа файла",
+ media_length: "Размер файла (в байтах)",
+ media_type: "Тип",
+ upload_name: "Имя файла",
+ quarantined_by: "Отправлен в карантин",
+ safe_from_quarantine: "Защищен от карантина",
+ created_ts: "Создан",
+ last_access_ts: "Последнее обращение",
+ },
+ },
+ delete_media: {
+ name: "Медиа файлы",
+ fields: {
+ before_ts: "последнее обращение",
+ size_gt: "Больше чем (в байтах)",
+ keep_profiles: "Сохранить аватары",
+ },
+ action: {
+ send: "Удалить медиа файлы",
+ send_success: "Запрос отправлен.",
+ send_failure: "Произошла ошибка.",
+ },
+ helper: {
+ send: "Этот метод удаляет медиа файлы с диска сервера. Это включает в себя любые миниатюры и копии загруженных файлов. Этот метод не повлияет на файлы, загруженные во внешние хранилища.",
+ },
+ },
+ protect_media: {
+ action: {
+ create: "Не защищено, установить защиту",
+ delete: "Защищено, удалить защиту",
+ none: "В карантине",
+ send_success: "Успешно изменен статус защиты.",
+ send_failure: "Произошла ошибка.",
+ },
+ },
+ quarantine_media: {
+ action: {
+ name: "Карантин",
+ create: "Добавить в карантин",
+ delete: "В карантине, вывести из карантина",
+ none: "Защищено от карантина",
+ send_success: "Успешно изменен статус карантина.",
+ send_failure: "Произошла ошибка.",
+ },
+ },
+ pushers: {
+ name: "Уведомление |||| Уведомления",
+ fields: {
+ app: "Приложение",
+ app_display_name: "Отображаемое имя приложения",
+ app_id: "Идентификатор приложения",
+ device_display_name: "Отображаемое имя устройства",
+ kind: "Тип",
+ lang: "Язык",
+ profile_tag: "Тег профиля",
+ pushkey: "Токен доступа",
+ data: { url: "URL" },
+ },
+ },
+ servernotices: {
+ name: "Уведомления",
+ send: "Отправить уведомление",
+ fields: {
+ body: "Сообщение",
+ },
+ action: {
+ send: "Отправить",
+ send_success: "Уведомление успешно отправлено.",
+ send_failure: "Произошла ошибка.",
+ },
+ helper: {
+ send: 'Отправляет уведомление выбранным пользователям. Функция "Server Notices" должна быть активирована на сервере.',
+ },
+ },
+ user_media_statistics: {
+ name: "Пользовательские медиа файлы",
+ fields: {
+ media_count: "Количество медиа файлов",
+ media_length: "Длина медиа файлов",
+ },
+ },
+ forward_extremities: {
+ name: "Перенаправленные заключения",
+ fields: {
+ id: "Идентификатор события",
+ received_ts: "Время",
+ depth: "Глубина",
+ state_group: "Группа состояния",
+ },
+ },
+ room_state: {
+ name: "События изменения состояния",
+ fields: {
+ type: "Тип",
+ content: "Содержание",
+ origin_server_ts: "время отправления",
+ sender: "Отправитель",
+ },
+ },
+ room_directory: {
+ name: "Публичные комнаты",
+ fields: {
+ world_readable: "Видимо для гостей",
+ guest_can_join: "Гости могут присоединиться",
+ },
+ action: {
+ title:
+ "Удалить комнату |||| Удалить %{smart_count} комнат",
+ content:
+ "Вы уверены, что хотите удалить комнату? |||| Вы уверены, что хотите удалить %{smart_count} комнат?",
+ erase: "Удалить комнату",
+ create: "Опубликовать комнату",
+ send_success: "Комната успешно опубликована.",
+ send_failure: "Произошла ошибка.",
+ },
+ },
+ destinations: {
+ name: "Федерация",
+ fields: {
+ destination: "Назначение",
+ failure_ts: "Время сбоя",
+ retry_last_ts: "Время последней попытки",
+ retry_interval: "Интервал повторения",
+ last_successful_stream_ordering: "Последнее успешное соединение",
+ stream_ordering: "Соединение",
+ },
+ action: { reconnect: "Переподключиться" },
+ },
+ },
+ registration_tokens: {
+ name: "Токены регистрации",
+ fields: {
+ token: "Токен",
+ valid: "Допустимый токен",
+ uses_allowed: "Разрешено",
+ pending: "Ожидается",
+ completed: "Использован",
+ expiry_time: "Время истечения срока действия",
+ length: "Длина",
+ },
+ helper: { length: "Длина токена, если токен не указан." },
+ },
+};
+export default ru;