feat: add Traditional Chinese locale support

Adds Traditional Chinese locale support and updates locale-related tests.
This commit is contained in:
Hank 2026-06-01 18:04:58 +08:00 committed by GitHub
parent 6da367a47b
commit 28b1215efa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1742 additions and 13 deletions

View File

@ -1142,7 +1142,11 @@ watch(
<SelectContent>
<SelectItem v-for="locale in LOCALE_OPTIONS" :key="locale.value" :value="locale.value">
<div class="flex items-center gap-2">
<span>{{ locale.flag }}</span>
<span
class="inline-flex h-5 w-6 shrink-0 items-center justify-center text-sm font-medium leading-none"
>
{{ locale.flag }}
</span>
<span>{{ locale.label }}</span>
</div>
</SelectItem>

View File

@ -201,7 +201,12 @@ onBeforeUnmount(close);
class="h-3 w-3 shrink-0"
:class="[isItemSelected(item) ? selectedCheckClass : 'opacity-0']"
/>
<span v-if="item.leadingText" class="text-base leading-none">{{ item.leadingText }}</span>
<span
v-if="item.leadingText"
class="inline-flex h-5 w-6 shrink-0 items-center justify-center text-sm font-medium leading-none"
>
{{ item.leadingText }}
</span>
<component
:is="item.icon"
v-if="item.icon"

View File

@ -2,19 +2,20 @@ import { createI18n } from "vue-i18n";
import zhCN from "./locales/zh-CN";
import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/safeStorage";
export type Locale = "en" | "es" | "zh-CN";
export type Locale = "en" | "es" | "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", "zh-CN"];
const supportedLocales: Locale[] = ["en", "es", "zh-CN", "zh-TW"];
const defaultLocale: Locale = "zh-CN";
const loadedLocales = new Set<Locale>([defaultLocale]);
const localeLoaders: Record<Exclude<Locale, "zh-CN">, () => Promise<{ default: LocaleMessages }>> = {
en: () => import("./locales/en"),
es: () => import("./locales/es"),
"zh-TW": () => import("./locales/zh-TW"),
};
export function normalizeLocale(value: string | null): Locale | null {
@ -27,8 +28,17 @@ export function normalizeLocale(value: string | null): Locale | null {
export function localeFromLanguageTag(value: string | null | undefined): Locale | null {
if (!value) return null;
const normalized = value.replace("_", "-").toLowerCase();
if (normalized === "zh-cn" || normalized.startsWith("zh-")) return "zh-CN";
if (normalized === "zh") return "zh-CN";
if (normalized === "zh" || normalized.startsWith("zh-")) {
if (
normalized.includes("hant") ||
normalized.startsWith("zh-tw") ||
normalized.startsWith("zh-hk") ||
normalized.startsWith("zh-mo")
) {
return "zh-TW";
}
return "zh-CN";
}
if (normalized === "en" || normalized.startsWith("en-")) return "en";
if (normalized === "es" || normalized.startsWith("es-")) return "es";
return null;

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ import type {
QueryTab,
} from "@/types/database";
import * as api from "@/lib/api";
import { currentLocale } from "@/i18n";
import { currentLocale, type Locale } from "@/i18n";
import { aiTableMentionKey, type AiTableMention } from "@/lib/aiTableMentions";
import { aiSkillForAction } from "@/lib/aiSkills";
import { isSchemaAware } from "@/lib/databaseCapabilities";
@ -18,6 +18,10 @@ import { isSchemaAware } from "@/lib/databaseCapabilities";
export type AiAction = "generate" | "explain" | "optimize" | "fix" | "convert" | "sampleData";
export type AiAssistantMode = "ask" | "agent";
function isChineseLocale(locale: Locale): boolean {
return locale === "zh-CN" || locale === "zh-TW";
}
export interface AiSchemaTable {
schema?: string;
name: string;
@ -47,7 +51,7 @@ export interface AiRequestInput {
}
export async function runAiAction(input: AiRequestInput, history?: api.AiMessage[]): Promise<string> {
const isZh = currentLocale() === "zh-CN";
const isZh = isChineseLocale(currentLocale());
const skill = aiSkillForAction(input.action);
const systemPrompt = buildSystemPrompt(input.action, input.context, input.mode);
const instruction = isZh ? skill.userInstruction.zh : skill.userInstruction.en;
@ -78,7 +82,7 @@ export async function runAiStream(
sessionId?: string,
onReasoningDelta?: (delta: string) => void,
): Promise<void> {
const isZh = currentLocale() === "zh-CN";
const isZh = isChineseLocale(currentLocale());
const skill = aiSkillForAction(input.action);
const systemPrompt = buildSystemPrompt(input.action, input.context, input.mode);
const instruction = isZh ? skill.userInstruction.zh : skill.userInstruction.en;
@ -136,7 +140,7 @@ export function buildSystemPrompt(action: AiAction, context: AiContext, mode: Ai
const resultPreview = context.lastResultPreview ? `\nLast result preview:\n${context.lastResultPreview}\n` : "";
const lastError = context.lastError ? `\nLast error:\n${context.lastError}\n` : "";
const isZh = currentLocale() === "zh-CN";
const isZh = isChineseLocale(currentLocale());
const lines: string[] = [
...buildBasePromptLines(isZh),

View File

@ -4,4 +4,5 @@ export const LOCALE_OPTIONS: { value: Locale; flag: string; label: string }[] =
{ value: "en", flag: "🇺🇸", label: "English" },
{ value: "es", flag: "🇪🇸", label: "Español" },
{ value: "zh-CN", flag: "🇨🇳", label: "简体中文" },
{ value: "zh-TW", flag: "繁", label: "繁體中文" },
];

View File

@ -22,8 +22,11 @@ class MemoryStorage {
}
}
const localStorage = new MemoryStorage();
localStorage.setItem("dbx-locale", "zh-CN");
Object.defineProperty(globalThis, "localStorage", {
value: new MemoryStorage(),
value: localStorage,
configurable: true,
});

View File

@ -22,8 +22,11 @@ class MemoryStorage {
}
}
const localStorage = new MemoryStorage();
localStorage.setItem("dbx-locale", "zh-CN");
Object.defineProperty(globalThis, "localStorage", {
value: new MemoryStorage(),
value: localStorage,
configurable: true,
});

View File

@ -10,13 +10,15 @@ test("normalizes exact supported locales", () => {
assert.equal(normalizeLocale("en"), "en");
assert.equal(normalizeLocale("es"), "es");
assert.equal(normalizeLocale("zh-CN"), "zh-CN");
assert.equal(normalizeLocale("zh-TW"), "zh-TW");
assert.equal(normalizeLocale("fr-FR"), null);
assert.equal(normalizeLocale(null), null);
});
test("maps user language tags to supported locales", () => {
assert.equal(localeFromLanguageTag("zh-Hans-CN"), "zh-CN");
assert.equal(localeFromLanguageTag("zh_TW"), "zh-CN");
assert.equal(localeFromLanguageTag("zh_TW"), "zh-TW");
assert.equal(localeFromLanguageTag("zh-Hant-HK"), "zh-TW");
assert.equal(localeFromLanguageTag("en-US"), "en");
assert.equal(localeFromLanguageTag("es-MX"), "es");
assert.equal(localeFromLanguageTag("fr-FR"), null);