feat(i18n): add Azerbaijani locale

This commit is contained in:
Jamal Ali authored and GitHub committed 2026-09-09 12:30:53 +08:00
1 parent ef1d7ae9c4
commit 3408ddbb65
29 files changed
+9867 -18

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 = ["en", "es", "it", "ja", "ko", "pt-BR", "tr", "zh-CN", "zh-TW"];
const localePaths = ["az", "en", "es", "it", "ja", "ko", "pt-BR", "tr", "zh-CN", "zh-TW"];
describe("AI assistant write confirmation replacement", () => {
it("renders a localized confirmation from the backend's semantic event", () => {
@@ -104,7 +104,7 @@ describe("EditorSettingsDialog live preview placement", () => {
it("describes the execute shortcut below the mode selector in every supported locale", () => {
expect(editorSection.indexOf("executeModeDescription")).toBeGreaterThan(editorSection.indexOf("executeModeLabel"));
for (const locale of ["zh-CN", "zh-TW", "en", "es", "it", "ja", "ko", "pt-BR", "tr"]) {
for (const locale of ["zh-CN", "zh-TW", "en", "es", "it", "ja", "ko", "pt-BR", "tr", "az"]) {
const source = readFileSync(new URL(`../../../i18n/locales/${locale}.ts`, import.meta.url), "utf8");
expect(source, locale).toContain("executeModeDescription:");
}
@@ -115,7 +115,7 @@ describe("EditorSettingsDialog live preview placement", () => {
expect(editorSection).toContain("{{ completionTriggerModeDescription }}");
expect(editorSection).not.toContain('t("settings.completionTriggerModeDescription")');
for (const locale of ["zh-CN", "zh-TW", "en", "es", "it", "ja", "ko", "pt-BR", "tr"]) {
for (const locale of ["zh-CN", "zh-TW", "en", "es", "it", "ja", "ko", "pt-BR", "tr", "az"]) {
const source = readFileSync(new URL(`../../../i18n/locales/${locale}.ts`, import.meta.url), "utf8");
for (const key of ["completionTriggerModeManualDescription", "completionTriggerModeRequirePrefixDescription", "completionTriggerModePositionalDescription"]) {
expect(source, `${locale}:${key}`).toContain(`${key}:`);
@@ -174,7 +174,7 @@ describe("EditorSettingsDialog live preview placement", () => {
expect(dataSection.match(/editDataGridFilterEditorView = '(quick|conditions|text)'/g)).toHaveLength(3);
expect(dataSection).not.toContain('<SelectTrigger id="data-grid-filter-view"');
for (const locale of ["zh-CN", "zh-TW", "en", "es", "it", "ja", "ko", "pt-BR", "tr"]) {
for (const locale of ["zh-CN", "zh-TW", "en", "es", "it", "ja", "ko", "pt-BR", "tr", "az"]) {
const source = readFileSync(new URL(`../../../i18n/locales/${locale}.ts`, import.meta.url), "utf8");
for (const key of ["dataGridFilterViewPreview", "dataGridFilterViewPreviewExpand", "dataGridFilterViewPreviewCollapse"]) {
expect(source, `${locale}:${key}`).toContain(`${key}:`);
@@ -215,7 +215,7 @@ describe("EditorSettingsDialog live preview placement", () => {
expect(editorPreview.indexOf('t("settings.previewSyntaxErrorHint")')).toBeGreaterThan(preview);
});
it.each(["en", "zh-CN", "zh-TW", "es", "pt-BR", "it", "ja", "ko", "tr"])("locale %s translates settings.previewSyntaxErrorHint", (locale) => {
it.each(["en", "zh-CN", "zh-TW", "es", "pt-BR", "it", "ja", "ko", "tr", "az"])("locale %s translates settings.previewSyntaxErrorHint", (locale) => {
const localeSource = readFileSync(new URL(`../../../i18n/locales/${locale}.ts`, import.meta.url), "utf8");
expect(localeSource).toMatch(/previewSyntaxErrorHint: ".+"/);
});
@@ -5,6 +5,7 @@ import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import "dayjs/locale/zh-cn";
import "dayjs/locale/zh-tw";
import "dayjs/locale/az";
import "dayjs/locale/es";
import "dayjs/locale/it";
import "dayjs/locale/ja";
@@ -23,6 +24,7 @@ import MeilisearchTasksPage from "./MeilisearchTasksPage.vue";
dayjs.extend(relativeTime);
const DAYJS_LOCALES: Record<string, string> = {
az: "az",
"zh-CN": "zh-cn",
"zh-TW": "zh-tw",
"pt-BR": "pt-br",
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import az from "../locales/az";
import en from "../locales/en";
import es from "../locales/es";
import itLocale from "../locales/it";
@@ -12,6 +13,7 @@ import zhTW from "../locales/zh-TW";
type Messages = Record<string, unknown>;
const locales: Array<[string, Messages]> = [
["az", az],
["es", es],
["it", itLocale],
["ja", ja],
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import az from "../locales/az";
import en from "../locales/en";
import es from "../locales/es";
import it_ from "../locales/it";
@@ -10,6 +11,7 @@ import zhCN from "../locales/zh-CN";
import zhTW from "../locales/zh-TW";
const locales: Array<[string, Record<string, unknown>]> = [
["az", az],
["en", en],
["es", es],
["it", it_],
+2
View File
@@ -3,6 +3,7 @@ import { describe, expect, it as test } from "vitest";
import { createI18n } from "vue-i18n";
import { translateBackendError, type BackendErrorTranslate } from "@/i18n/backend-errors";
import { BackendErrorException, formatError, normalizeBackendError, sanitizeBackendErrorMessage } from "@/lib/backend/errorUtils";
import az from "@/i18n/locales/az";
import en from "@/i18n/locales/en";
import es from "@/i18n/locales/es";
import it from "@/i18n/locales/it";
@@ -15,6 +16,7 @@ import zhTW from "@/i18n/locales/zh-TW";
import { PHOENIX_DRIVER_NOT_INSTALLED_ERROR, PHOENIX_JDBC_PLUGIN_NOT_INSTALLED_ERROR } from "@/lib/database/phoenixConnection";
const LOCALES = {
az,
en,
es,
it,
@@ -1,5 +1,6 @@
import { readFileSync } from "node:fs";
import { describe, expect, it as test } from "vitest";
import az from "@/i18n/locales/az";
import en from "@/i18n/locales/en";
import es from "@/i18n/locales/es";
import it from "@/i18n/locales/it";
@@ -18,6 +19,7 @@ function cachedResultMessages(messages: Record<string, unknown>): Record<string,
describe("cached result messages", () => {
test.each([
["Azerbaijani", az],
["English", en],
["Spanish", es],
["Italian", it],
@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import az from "../locales/az";
import en from "../locales/en";
import es from "../locales/es";
import it_ from "../locales/it";
@@ -11,6 +12,7 @@ import zhCN from "../locales/zh-CN";
import zhTW from "../locales/zh-TW";
const locales: Array<[string, Record<string, unknown>]> = [
["az", az],
["en", en],
["es", es],
["it", it_],
+2 -1
View File
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import az from "@/i18n/locales/az";
import en from "@/i18n/locales/en";
import es from "@/i18n/locales/es";
import itLocale from "@/i18n/locales/it";
@@ -14,7 +15,7 @@ type Messages = {
contextMenu: typeof en.contextMenu;
};
const locales = { es, it: itLocale, ja, ko, "pt-BR": ptBR, tr, "zh-CN": zhCN, "zh-TW": zhTW } as unknown as Record<string, Messages>;
const locales = { az, es, it: itLocale, ja, ko, "pt-BR": ptBR, tr, "zh-CN": zhCN, "zh-TW": zhTW } as unknown as Record<string, Messages>;
describe("custom type translations", () => {
it("provides localized labels in every supported non-English locale", () => {
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import az from "../locales/docs/az";
import en from "../locales/docs/en";
import es from "../locales/docs/es";
import it_ from "../locales/docs/it";
@@ -17,6 +18,7 @@ import zhTW from "../locales/docs/zh-TW";
// have every key and this test would pass while translations were missing —
// the fallback would silently defeat the test written to catch it.
const locales: Array<[string, Record<string, unknown>]> = [
["az", az],
["es", es],
["it", it_],
["ja", ja],
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import az from "../locales/az";
import en from "../locales/en";
import es from "../locales/es";
import it_ from "../locales/it";
@@ -12,6 +13,7 @@ import zhTW from "../locales/zh-TW";
type Messages = Record<string, unknown>;
const locales: Array<[string, Messages]> = [
["az", az],
["es", es],
["it", it_],
["ja", ja],
@@ -1,4 +1,5 @@
import { describe, expect, it as test } from "vitest";
import az from "@/i18n/locales/az";
import en from "@/i18n/locales/en";
import es from "@/i18n/locales/es";
import it from "@/i18n/locales/it";
@@ -16,6 +17,7 @@ function sqliteRebuildNotice(messages: Record<string, unknown>): string {
describe("SQLite rebuild notice", () => {
test.each([
["Azerbaijani", az],
["English", en],
["Spanish", es],
["Italian", it],
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import az from "../locales/az";
import en from "../locales/en";
import es from "../locales/es";
import it_ from "../locales/it";
@@ -23,6 +24,7 @@ const tabMenuKeys = ["changeOrientation", "closeAllTabs", "closeLeftTabs", "clos
// real text for every UI language, while the en test above is the one that
// actually catches a missing-from-everywhere key.
const locales: Array<[string, Record<string, unknown>]> = [
["az", az as Record<string, unknown>],
["en", en as Record<string, unknown>],
["es", es],
["it", it_],
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import az from "../locales/az";
import en from "../locales/en";
import es from "../locales/es";
import it_ from "../locales/it";
@@ -11,6 +12,7 @@ import zhTW from "../locales/zh-TW";
const locales: Array<[string, Record<string, unknown>]> = [
["en", en as Record<string, unknown>],
["az", az as Record<string, unknown>],
["es", es],
["it", it_],
["ja", ja],
@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import az from "@/i18n/locales/az";
import en from "@/i18n/locales/en";
import es from "@/i18n/locales/es";
import itLocale from "@/i18n/locales/it";
@@ -16,7 +17,7 @@ describe("user administration Host translations", () => {
});
it("provides localized Host-change labels in every non-English locale", () => {
const locales = { es, it: itLocale, ja, ko, "pt-BR": ptBR, tr, "zh-CN": zhCN, "zh-TW": zhTW };
const locales = { az, es, it: itLocale, ja, ko, "pt-BR": ptBR, tr, "zh-CN": zhCN, "zh-TW": zhTW };
for (const [locale, messages] of Object.entries(locales)) {
expect(messages.userAdmin.changeHost, locale).not.toBe(en.userAdmin.changeHost);
expect(messages.userAdmin.newHost, locale).not.toBe(en.userAdmin.newHost);
+4 -2
View File
@@ -3,17 +3,18 @@ import en from "./locales/en";
import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage";
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
export type Locale = "en" | "es" | "it" | "ja" | "ko" | "pt-BR" | "tr" | "zh-CN" | "zh-TW";
export type Locale = "az" | "en" | "es" | "it" | "ja" | "ko" | "pt-BR" | "tr" | "zh-CN" | "zh-TW";
type LocaleMessages = Record<string, unknown>;
type I18nGlobal = {
locale: { value: Locale };
setLocaleMessage: (locale: Locale, messages: LocaleMessages) => void;
};
const supportedLocales: Locale[] = ["en", "es", "it", "ja", "ko", "pt-BR", "tr", "zh-CN", "zh-TW"];
const supportedLocales: Locale[] = ["az", "en", "es", "it", "ja", "ko", "pt-BR", "tr", "zh-CN", "zh-TW"];
const defaultLocale: Locale = "en";
const loadedLocales = new Set<Locale>([defaultLocale]);
const localeLoaders: Record<Exclude<Locale, "en">, () => Promise<{ default: LocaleMessages }>> = {
az: () => import("./locales/az"),
es: () => import("./locales/es"),
it: () => import("./locales/it"),
ja: () => import("./locales/ja"),
@@ -40,6 +41,7 @@ export function localeFromLanguageTag(value: string | null | undefined): Locale
}
return "zh-CN";
}
if (normalized === "az" || normalized.startsWith("az-")) return "az";
if (normalized === "en" || normalized.startsWith("en-")) return "en";
if (normalized === "es" || normalized.startsWith("es-")) return "es";
if (normalized === "it" || normalized.startsWith("it-")) return "it";
File diff suppressed because it is too large. Load diff
+69
View File
@@ -0,0 +1,69 @@
export default {
title: "Sənədləşdirmə",
groupBySchema: "Sxemlər",
groupByTableGroup: "Cədvəl qrupları",
noGroup: "(qrup yoxdur)",
noSchema: "(sxem yoxdur)",
search: "Cədvəlləri, sütunları, qrupları axtar…",
columns: "Sütunlar",
indexes: "İndekslər",
references: "İstinadlar",
referencedBy: "İstinad edənlər",
localNote: "Yerli",
shadowedComment: "Verilənlər bazasındakı şərh: {comment}",
addNote: "Qeyd əlavə et",
editNote: "Qeydi redaktə et",
newGroup: "Yeni qrup…",
groupName: "Qrupun adı",
groupColour: "Rəng",
deleteGroup: "Qrupu sil",
enumValues: "Dəyərlər",
usedBy: "İstifadə edənlər",
openDiagram: "Sxem diaqramını aç",
saving: "Yadda saxlanılır…",
saved: "Yadda saxlanıldı",
saveFailed: "Qeydləri yadda saxlamaq mümkün olmadı: {error}",
overview: "Ümumi baxış",
groupBy: "Qruplaşdırma meyarı",
searchLabel: "Axtarış",
groups: "Qruplar",
relationships: "Əlaqələr",
columnHeader: "Sütun",
typeHeader: "Növ",
settingsHeader: "Parametrlər",
noteHeader: "Qeyd",
nameHeader: "Ad",
definitionHeader: "Tərif",
noOutgoingRelationships: "Bu cədvəl başqa heç bir cədvələ istinad etmir.",
noIncomingRelationships: "Heç bir cədvəl bu cədvələ istinad etmir.",
diagram: "Diaqram",
language: "Dil",
theme: "Tema",
themeLight: "Açıq",
themeDark: "Tünd",
exportHtml: "HTML-ə ixrac et…",
exporting: "İxrac edilir…",
exportFailed: "İxrac etmək mümkün olmadı: {error}",
warnings: {
tableSkipped: {
title: "Bir cədvəli sənədləşdirmək mümkün olmadı",
detail: "{table} ötürüldü: {reason}. Bu sənədləşdirmədə yer almır.",
},
noForeignKeyMetadata: {
title: "Əlaqələr mövcud deyil",
detail: "{engine} xarici açar metaverilənlərini təqdim etmir, buna görə əlaqə xətlərini müəyyən etmək mümkün olmadı. Bu mühərrik üçün diaqram tamdır.",
},
commentsUnsupported: {
title: "Verilənlər bazasının şərhləri əlçatan deyil",
detail: "{engine} cədvəl və ya sütun şərhlərini dəstəkləmir, buna görə buradakı bütün təsvirlər bu layihənin öz qeydlərindən götürülüb.",
},
orphanedNotes: {
title: "Bəzi qeydlər artıq heç nəyə uyğun gəlmir",
detail: "{count} qeyd artıq mövcud olmayan cədvələ və ya sütuna istinad edir. Heç nə silinməyib — qeydlər faylında onları yenidən uyğunlaşdırın və ya çıxarın.",
},
dbmlOmitted: {
title: "DBML-də ifadə etmək mümkün deyil",
detail: "{table} cədvəlindəki {item} burada sənədləşdirilib, lakin ixrac edilmiş DBML-ə daxil edilməyib: {reason}.",
},
},
};
@@ -220,6 +220,34 @@ const zhCN = {
};
export const meilisearchManagementMessages = {
az: {
...en,
systemManagement: "Sistemin idarə edilməsi",
overview: "Ümumi baxış",
apiKeys: "API açarları",
tasks: "Tapşırıqlar",
health: "İşlək vəziyyət",
version: "Versiya",
indexCount: "İndekslər",
totalDocuments: "Sənədlərin ümumi sayı",
keyCount: "Açarlar",
refresh: "Yenilə",
rowActions: "Əməliyyatlar",
previous: "Əvvəlki",
next: "Növbəti",
createKey: "Açar yarat",
editKey: "Açarı redaktə et",
deleteKey: "Açarı sil",
noKeys: "API açarları tapılmadı",
filters: "Süzgəclər",
applyFilters: "Süzgəcləri tətbiq et",
receivedDocuments: "Qəbul edilmiş sənədlər",
indexedDocuments: "İndekslənmiş sənədlər",
noTasks: "Tapşırıqlar tapılmadı",
cancelTask: "Tapşırığı ləğv et",
deleteTaskHistory: "Tapşırıq tarixçəsini sil",
readOnlyDisabled: "Bu əlaqə yalnız oxumaq üçündür; idarəetmə əməliyyatları deaktivdir.",
},
en,
"zh-CN": zhCN,
"zh-TW": {
@@ -415,6 +443,7 @@ export const meilisearchManagementMessages = {
},
} as const;
export const meilisearchManagementAz = meilisearchManagementMessages.az;
export const meilisearchManagementEn = meilisearchManagementMessages.en;
export const meilisearchManagementZhCN = meilisearchManagementMessages["zh-CN"];
export const meilisearchManagementZhTW = meilisearchManagementMessages["zh-TW"];
@@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest";
const dialogSource = readFileSync(new URL("../../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
const enSource = readFileSync(new URL("../../../i18n/locales/en.ts", import.meta.url), "utf8");
const zhCnSource = readFileSync(new URL("../../../i18n/locales/zh-CN.ts", import.meta.url), "utf8");
const fallbackLocaleSources = ["es", "it", "ja", "ko", "pt-BR", "tr", "zh-TW"].map((locale) => readFileSync(new URL(`../../../i18n/locales/${locale}.ts`, import.meta.url), "utf8"));
const fallbackLocaleSources = ["az", "es", "it", "ja", "ko", "pt-BR", "tr", "zh-TW"].map((locale) => readFileSync(new URL(`../../../i18n/locales/${locale}.ts`, import.meta.url), "utf8"));
describe("ZooKeeper connection dialog", () => {
it("binds the existing Select component to the ZooKeeper auth_scheme helper", () => {
@@ -2,6 +2,7 @@ import type { Locale } from "@/i18n";
export const LOCALE_OPTIONS: { value: Locale; flag: string; label: string }[] = [
{ value: "en", flag: "🇺🇸", label: "English" },
{ value: "az", flag: "🇦🇿", label: "Azərbaycan" },
{ value: "es", flag: "🇪🇸", label: "Español" },
{ value: "it", flag: "🇮🇹", label: "Italiano" },
{ value: "ja", flag: "🇯🇵", label: "日本語" },
@@ -146,7 +146,7 @@ describe("settings search", () => {
});
it("defines the WebDAV Web-runtime notice in every supported locale", () => {
for (const locale of ["zh-CN", "zh-TW", "en", "es", "it", "ja", "ko", "pt-BR", "tr"]) {
for (const locale of ["zh-CN", "zh-TW", "en", "es", "it", "ja", "ko", "pt-BR", "tr", "az"]) {
const source = readFileSync(new URL(`../../../i18n/locales/${locale}.ts`, import.meta.url), "utf8");
expect(source, locale).toContain("syncWebDavWebDescription:");
}
@@ -257,7 +257,7 @@ describe("settings search", () => {
});
it("defines the performance section title in every supported locale", () => {
for (const locale of ["zh-CN", "zh-TW", "en", "es", "it", "ja", "ko", "pt-BR", "tr"]) {
for (const locale of ["zh-CN", "zh-TW", "en", "es", "it", "ja", "ko", "pt-BR", "tr", "az"]) {
const source = readFileSync(new URL(`../../../i18n/locales/${locale}.ts`, import.meta.url), "utf8");
expect(source, locale).toContain("performanceSection:");
}