feat(i18n): add Russian locale

This commit is contained in:
Alexander authored and GitHub committed 2026-09-24 12:49:11 +08:00
1 parent 84ec5eac74
commit 0b3e79331b
12 files changed
+11853 -7

No files matched your search

@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const source = readFileSync(new URL("../AiAssistant.vue", import.meta.url), "utf8");
const localePaths = ["az", "en", "es", "it", "ja", "ko", "pt-BR", "tr", "zh-CN", "zh-TW"];
const localePaths = ["az", "en", "es", "it", "ja", "ko", "pt-BR", "ru", "tr", "zh-CN", "zh-TW"];
describe("AI assistant write confirmation replacement", () => {
it("renders a localized confirmation from the backend's semantic event", () => {
+2
View File
@@ -10,6 +10,7 @@ import it from "@/i18n/locales/it";
import ja from "@/i18n/locales/ja";
import ko from "@/i18n/locales/ko";
import ptBR from "@/i18n/locales/pt-BR";
import ru from "@/i18n/locales/ru";
import tr from "@/i18n/locales/tr";
import zhCN from "@/i18n/locales/zh-CN";
import zhTW from "@/i18n/locales/zh-TW";
@@ -23,6 +24,7 @@ const LOCALES = {
ja,
ko,
"pt-BR": ptBR,
ru,
tr,
"zh-CN": zhCN,
"zh-TW": zhTW,
@@ -6,6 +6,7 @@ import it_ from "../locales/docs/it";
import ja from "../locales/docs/ja";
import ko from "../locales/docs/ko";
import ptBR from "../locales/docs/pt-BR";
import ru from "../locales/docs/ru";
import tr from "../locales/docs/tr";
import zhCN from "../locales/docs/zh-CN";
import zhTW from "../locales/docs/zh-TW";
@@ -24,6 +25,7 @@ const locales: Array<[string, Record<string, unknown>]> = [
["ja", ja],
["ko", ko],
["pt-BR", ptBR],
["ru", ru],
["tr", tr],
["zh-CN", zhCN],
["zh-TW", zhTW],
+3 -2
View File
@@ -7,6 +7,7 @@ import itLocale from "../locales/it";
import ja from "../locales/ja";
import ko from "../locales/ko";
import ptBR from "../locales/pt-BR";
import ru from "../locales/ru";
import tr from "../locales/tr";
import zhCN from "../locales/zh-CN";
import zhTW from "../locales/zh-TW";
@@ -19,9 +20,9 @@ import zhTW from "../locales/zh-TW";
vi.mock("../locales/fallback", () => ({ withEnglishFallback: (messages: Record<string, unknown>) => messages }));
type Messages = Record<string, unknown>;
type Locale = "az" | "en" | "es" | "it" | "ja" | "ko" | "pt-BR" | "tr" | "zh-CN" | "zh-TW";
type Locale = "az" | "en" | "es" | "it" | "ja" | "ko" | "pt-BR" | "ru" | "tr" | "zh-CN" | "zh-TW";
const declared: Record<Locale, Messages> = { az, en, es, it: itLocale, ja, ko, "pt-BR": ptBR, tr, "zh-CN": zhCN, "zh-TW": zhTW };
const declared: Record<Locale, Messages> = { az, en, es, it: itLocale, ja, ko, "pt-BR": ptBR, ru, tr, "zh-CN": zhCN, "zh-TW": zhTW };
const ALL_LOCALES = Object.keys(declared) as Locale[];
const EXCEPT_AZ_TR = ALL_LOCALES.filter((locale) => locale !== "az" && locale !== "tr");
+4 -2
View File
@@ -3,14 +3,14 @@ import en from "./locales/en";
import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage";
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
export type Locale = "az" | "en" | "es" | "it" | "ja" | "ko" | "pt-BR" | "tr" | "zh-CN" | "zh-TW";
export type Locale = "az" | "en" | "es" | "it" | "ja" | "ko" | "pt-BR" | "ru" | "tr" | "zh-CN" | "zh-TW";
type LocaleMessages = Record<string, unknown>;
type I18nGlobal = {
locale: { value: Locale };
setLocaleMessage: (locale: Locale, messages: LocaleMessages) => void;
};
const supportedLocales: Locale[] = ["az", "en", "es", "it", "ja", "ko", "pt-BR", "tr", "zh-CN", "zh-TW"];
const supportedLocales: Locale[] = ["az", "en", "es", "it", "ja", "ko", "pt-BR", "ru", "tr", "zh-CN", "zh-TW"];
const defaultLocale: Locale = "en";
const loadedLocales = new Set<Locale>([defaultLocale]);
const localeLoaders: Record<Exclude<Locale, "en">, () => Promise<{ default: LocaleMessages }>> = {
@@ -20,6 +20,7 @@ const localeLoaders: Record<Exclude<Locale, "en">, () => Promise<{ default: Loca
ja: () => import("./locales/ja"),
ko: () => import("./locales/ko"),
"pt-BR": () => import("./locales/pt-BR"),
ru: () => import("./locales/ru"),
tr: () => import("./locales/tr"),
"zh-CN": () => import("./locales/zh-CN"),
"zh-TW": () => import("./locales/zh-TW"),
@@ -48,6 +49,7 @@ export function localeFromLanguageTag(value: string | null | undefined): Locale
if (normalized === "ja" || normalized.startsWith("ja-")) return "ja";
if (normalized === "ko" || normalized.startsWith("ko-")) return "ko";
if (normalized === "pt" || normalized.startsWith("pt-")) return "pt-BR";
if (normalized === "ru" || normalized.startsWith("ru-")) return "ru";
if (normalized === "tr" || normalized.startsWith("tr-")) return "tr";
return null;
}
+69
View File
@@ -0,0 +1,69 @@
export default {
title: "Документация",
groupBySchema: "Схемы",
groupByTableGroup: "Группы таблиц",
noGroup: "(без группы)",
noSchema: "(без схемы)",
search: "Поиск таблиц, столбцов, групп…",
columns: "Столбцы",
indexes: "Индексы",
references: "Ссылки",
referencedBy: "Ссылаются на",
localNote: "ЛОКАЛЬНО",
shadowedComment: "Комментарий базы данных: {comment}",
addNote: "Добавить заметку",
editNote: "Редактировать заметку",
newGroup: "Новая группа…",
groupName: "Название группы",
groupColour: "Цвет",
deleteGroup: "Удалить группу",
enumValues: "Значения",
usedBy: "Используется в",
openDiagram: "Открыть схему",
saving: "Сохранение…",
saved: "Сохранено",
saveFailed: "Не удалось сохранить заметки: {error}",
overview: "Обзор",
groupBy: "Группировать по",
searchLabel: "Поиск",
groups: "Группы",
relationships: "Связи",
columnHeader: "Столбец",
typeHeader: "Тип",
settingsHeader: "Настройки",
noteHeader: "Заметка",
nameHeader: "Имя",
definitionHeader: "Определение",
noOutgoingRelationships: "Эта таблица не ссылается на другие таблицы.",
noIncomingRelationships: "Ни одна таблица не ссылается на эту.",
diagram: "Диаграмма",
language: "Язык",
theme: "Тема",
themeLight: "Светлая",
themeDark: "Тёмная",
exportHtml: "Экспорт в HTML…",
exporting: "Экспорт…",
exportFailed: "Не удалось выполнить экспорт: {error}",
warnings: {
tableSkipped: {
title: "Не удалось задокументировать таблицу",
detail: "{table} пропущена: {reason}. Её нет в этой документации.",
},
noForeignKeyMetadata: {
title: "Связи недоступны",
detail: "{engine} не сообщает метаданные внешних ключей, поэтому связи не удалось построить. Для этого движка диаграмма полная.",
},
commentsUnsupported: {
title: "Комментарии базы данных недоступны",
detail: "{engine} не поддерживает комментарии к таблицам и столбцам, поэтому все описания здесь взяты из собственных заметок проекта.",
},
orphanedNotes: {
title: "Часть заметок больше ни с чем не связана",
detail: "Заметок, ссылающихся на несуществующую таблицу или столбец: {count}. Ничего не удалено — измените привязку или удалите их в файле заметок.",
},
dbmlOmitted: {
title: "Невозможно представить в DBML",
detail: "{item} в {table} задокументирован здесь, но пропущен в экспортированном DBML: {reason}.",
},
},
};
@@ -457,6 +457,123 @@ export const meilisearchManagementMessages = {
deleteTaskHistory: "Geçmişi sil",
readOnlyDisabled: "Salt okunur bağlantılarda işlemler devre dışıdır.",
},
ru: {
systemManagement: "Управление системой",
overview: "Обзор",
overviewDescription: "Состояние, ёмкость, индексы, ключи и активность задач в реальном времени.",
apiKeys: "API-ключи",
keysDescription: "Создание и управление ограниченными по области действия API-ключами Meilisearch.",
tasks: "Задачи",
tasksDescription: "Просмотр асинхронных операций, отмена активных задач и очистка завершённой истории.",
health: "Состояние",
version: "Версия",
indexCount: "Индексы",
totalDocuments: "Всего документов",
indexingCount: "Индексация",
keyCount: "Ключи",
storage: "Хранилище",
usedDatabaseSize: "Занято базой данных",
lastUpdate: "Последнее обновление",
taskStatusDistribution: "Распределение статусов задач",
topIndexes: "Индексы с наибольшим числом документов",
status: "Статус",
idle: "Простой",
sectionStatus: { available: "Доступно", forbidden: "Доступ запрещён", unsupported: "Не поддерживается этим сервером", error: "Недоступно" },
refresh: "Обновить",
rowActions: "Действия",
previous: "Назад",
next: "Вперёд",
name: "Название",
key: "Ключ",
description: "Описание",
actions: "Действия",
indexes: "Индексы",
expiresAt: "Срок действия",
never: "Никогда",
createKey: "Создать ключ",
editKey: "Изменить ключ",
deleteKey: "Удалить ключ",
keyDetails: "Сведения о ключе",
keyCreated: "API-ключ создан",
keyUpdated: "API-ключ обновлён",
keyDeleted: "API-ключ удалён",
noKeys: "API-ключи не найдены",
deleteKeyConfirm: "Удалить API-ключ {uid}? Это действие нельзя отменить.",
deleteKeyTypePrompt: "Введите полный UID ключа, чтобы подтвердить удаление.",
deleteKeyTypePlaceholder: "Введите UID ключа",
immutableKeyFields: "Действия, индексы, срок действия, UID и секрет неизменяемы. Чтобы изменить их, создайте ключ заново.",
secretShownOnce: "Скопируйте секрет сейчас. DBX никогда не сохраняет значения API-ключей.",
actionsIndexesRequired: "Требуется указать хотя бы одно действие и один индекс.",
uidPlaceholder: "Оставьте пустым, чтобы сгенерировать случайный UID",
uidHelp: "Необязательно. Если оставить пустым, Meilisearch сгенерирует случайный UID.",
namePlaceholder: "Укажите название ключа",
nameHelp: "Необязательное название для идентификации этого ключа.",
descriptionPlaceholder: "Добавьте необязательное описание",
descriptionHelp: "Необязательная заметка о том, где и для чего используется ключ.",
indexesPlaceholder: "Выберите разрешённые индексы",
indexesHelp: "Выберите один или несколько доступных индексов. Значение * предоставляет доступ ко всем индексам.",
actionsPlaceholder: "Выберите разрешённые действия",
actionsHelp: "Выберите операции API, которые этот ключ разрешено выполнять.",
expiresAtPlaceholder: "Выберите или введите дату и время",
expiresAtHelp: "Необязательно. Оставьте пустым, чтобы срок действия не истекал, либо введите корректные дату и время.",
openDateTimePicker: "Открыть выбор даты и времени",
invalidExpiresAt: "Введите корректные дату и время окончания срока действия.",
copyKeyValue: "Копировать API-ключ",
paginationSummary: "{start}–{end} из {total}",
readOnlyDisabled: "Это подключение доступно только для чтения; управляющие операции отключены.",
filters: "Фильтры",
applyFilters: "Применить фильтры",
resetFilters: "Сбросить фильтры",
uidFilter: "Фильтр по UID",
searchUid: "Поиск по UID",
allIndexes: "Все индексы",
allStatuses: "Все статусы",
indexListUnavailable: "Не удалось загрузить список доступных индексов",
reverseOrder: "Обратный порядок",
columnSettings: "Настроить столбцы",
index: "Индекс",
type: "Тип",
details: "Подробности",
receivedDocuments: "Получено документов",
indexedDocuments: "Проиндексировано документов",
enqueuedAt: "В очереди",
startedAt: "Начато",
finishedAt: "Завершено",
duration: "Длительность",
afterEnqueuedAt: "В очереди после",
beforeEnqueuedAt: "В очереди до",
afterStartedAt: "Начато после",
beforeStartedAt: "Начато до",
afterFinishedAt: "Завершено после",
beforeFinishedAt: "Завершено до",
indexTasks: "Задачи · {index}",
noTasks: "Задачи не найдены",
taskCount: "Подходящих задач: {count}",
taskDetails: "Сведения о задаче",
cancelTask: "Отменить задачу",
cancelTasks: "Отменить задачи",
deleteTaskHistory: "Удалить историю задач",
cancelFilteredTasks: "Отменить отобранные активные задачи",
deleteFilteredHistory: "Удалить отобранную завершённую историю",
explicitFilterRequired: "Перед массовой операцией задайте хотя бы один явный фильтр.",
noAllowedTaskStatuses: "В применённом фильтре нет статусов задач, разрешённых для этой операции.",
countingMatchingTasks: "Подсчёт подходящих задач...",
cancelTasksConfirm: "Отменить активные задачи, соответствующие этому фильтру (сейчас найдено: {count})?",
deleteTasksConfirm: "Удалить завершённую историю задач, соответствующую этому фильтру (сейчас найдено: {count})?",
waitingForTask: "Ожидание задачи управления #{uid}",
taskOperationCompleted: "Операция с задачей завершена",
taskOperationFinished: "Операция с задачей завершена со статусом {status}",
taskPollingTimeout: "Задача #{uid} не завершилась за 60 секунд. Обновите страницу вручную, чтобы проверить её состояние.",
taskOutsideIndex: "Эта задача не относится к текущему индексу.",
createIndex: "Создать индекс",
createIndexUid: "UID индекса",
createIndexPrimaryKey: "Первичный ключ",
indexCreated: "Индекс создан",
createIndexUidRequired: "Укажите UID индекса.",
createIndexUidPlaceholder: "Введите UID индекса",
createIndexPrimaryKeyPlaceholder: "Необязательный первичный ключ",
createIndexPrimaryKeyHelp: "Оставьте пустым, чтобы задать первичный ключ при добавлении первого документа.",
},
} as const;
export const meilisearchManagementAz = meilisearchManagementMessages.az;
@@ -469,3 +586,4 @@ export const meilisearchManagementEs = meilisearchManagementMessages.es;
export const meilisearchManagementIt = meilisearchManagementMessages.it;
export const meilisearchManagementPtBR = meilisearchManagementMessages["pt-BR"];
export const meilisearchManagementTr = meilisearchManagementMessages.tr;
export const meilisearchManagementRu = meilisearchManagementMessages.ru;
File diff suppressed because it is too large. Load diff
@@ -8,6 +8,7 @@ export const LOCALE_OPTIONS: { value: Locale; flag: string; label: string }[] =
{ value: "ja", flag: "🇯🇵", label: "日本語" },
{ value: "ko", flag: "🇰🇷", label: "한국어" },
{ value: "pt-BR", flag: "🇧🇷", label: "Português" },
{ value: "ru", flag: "🇷🇺", label: "Русский" },
{ value: "tr", flag: "🇹🇷", label: "Türkçe" },
{ value: "zh-CN", flag: "🇨🇳", label: "简体中文" },
{ value: "zh-TW", flag: "繁", label: "繁體中文" },