feat(i18n): add Traditional Chinese localization (#3093)

* Add Traditional Chinese localization

* fix: address zh-TW formatting and token units

- Format `zh-TW.json` with Prettier.
- Use Traditional Chinese `萬` and `億` units for zh-TW token summaries.
- Add usage formatting coverage for Traditional Chinese locale aliases.

---------

Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
滅ü
2026-05-27 00:05:03 +08:00
committed by GitHub
parent 8cdaf90d8d
commit 5fd3ec0d6a
16 changed files with 2795 additions and 21 deletions
+6 -1
View File
@@ -18,7 +18,12 @@ export function useDragSort(providers: Record<string, Provider>, appId: AppId) {
const { t, i18n } = useTranslation();
const sortedProviders = useMemo(() => {
const locale = i18n.language === "zh" ? "zh-CN" : "en-US";
const locale =
i18n.language === "zh"
? "zh-CN"
: i18n.language === "zh-TW"
? "zh-TW"
: "en-US";
return Object.values(providers).sort((a, b) => {
if (a.sortIndex !== undefined && b.sortIndex !== undefined) {
return a.sortIndex - b.sortIndex;
+35 -5
View File
@@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
import { useSettingsQuery } from "@/lib/query";
import type { Settings } from "@/types";
type Language = "zh" | "en" | "ja";
type Language = "zh" | "zh-TW" | "en" | "ja";
export type SettingsFormState = Omit<Settings, "language"> & {
language: Language;
@@ -11,8 +11,38 @@ export type SettingsFormState = Omit<Settings, "language"> & {
const normalizeLanguage = (lang?: string | null): Language => {
if (!lang) return "zh";
const normalized = lang.toLowerCase();
return normalized === "en" || normalized === "ja" ? normalized : "zh";
const normalized = lang.toLowerCase().replace(/_/g, "-");
if (normalized === "zh") {
return "zh";
}
if (
normalized === "zh-tw" ||
normalized.startsWith("zh-hant") ||
normalized.startsWith("zh-hk") ||
normalized.startsWith("zh-mo")
) {
return "zh-TW";
}
if (normalized === "en" || normalized === "ja") {
return normalized;
}
if (normalized.startsWith("zh")) {
return "zh";
}
return "zh";
};
const isSupportedLanguage = (lang?: string | null): boolean => {
if (!lang) return false;
const normalized = lang.toLowerCase().replace(/_/g, "-");
return (
normalized === "en" || normalized === "ja" || normalized.startsWith("zh")
);
};
const sanitizeDir = (value?: string | null): string | undefined => {
@@ -52,8 +82,8 @@ export function useSettingsForm(): UseSettingsFormResult {
const readPersistedLanguage = useCallback((): Language => {
if (typeof window !== "undefined") {
const stored = window.localStorage.getItem("language");
if (stored === "en" || stored === "zh" || stored === "ja") {
return stored as Language;
if (isSupportedLanguage(stored)) {
return normalizeLanguage(stored);
}
}
return normalizeLanguage(i18n.language);