Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/vietnamese-locale.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions gui/src/components/QuotaBars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ function bcp47(locale: Locale): string {
return "ja-JP";
case "tr":
return "tr-TR";
case "vi":
return "vi-VN";
default: {
const _exhaustive: never = locale;
return _exhaustive;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function bcp47(locale: Locale): string {
case "ru": return "ru-RU";
case "ja": return "ja-JP";
case "tr": return "tr-TR";
case "vi": return "vi-VN";
default: {
const _exhaustive: never = locale;
return _exhaustive;
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/catalogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { zhTW } from "./zh-TW";
import { ru } from "./ru";
import { ja } from "./ja";
import { tr } from "./tr";
import { vi } from "./vi";
import { LAB_CATALOG_OVERRIDES, type LabLocale } from "./lab-translations";

/** React-free locale catalog registry for formatters and other shared helpers. */
Expand All @@ -31,6 +32,7 @@ export const DICTS: Record<Locale, Record<TKey, string>> = {
ru: withLabTranslations("ru", ru),
ja: withLabTranslations("ja", ja),
tr: withLabTranslations("tr", tr),
vi: withLabTranslations("vi", vi),
};

/** Native language names shown by the language picker, kept inside i18n rather than UI metadata. */
Expand Down
56 changes: 55 additions & 1 deletion gui/src/i18n/lab-translations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { TKey } from "./en";

export type LabLocale = "en" | "de" | "fr" | "ko" | "zh" | "zh-TW" | "ru" | "ja" | "tr";
export type LabLocale = "en" | "de" | "fr" | "ko" | "zh" | "zh-TW" | "ru" | "ja" | "tr" | "vi";
export type LabCatalogKey = Exclude<Extract<TKey, `lab.${string}`>, `lab.production.${string}`>;
export type LabSupplementKey =
| "subjectKindUnknown"
Expand Down Expand Up @@ -464,6 +464,56 @@ const zhTW: Record<LabCatalogKey, string> = {
"lab.layer.task_effectiveness": "任務有效性",
};

const vi: Record<LabCatalogKey, string> = {
"lab.title": "Phòng thí nghiệm tương thích",
"lab.subtitle": "Ma trận kết quả tương thích chỉ đọc từ bằng chứng dự báo của phòng thí nghiệm.",
"lab.loadFailed": "Không thể tải dữ liệu phòng thí nghiệm tương thích",
"lab.projectionUnavailable": "Dự báo của phòng thí nghiệm không khả dụng. Hãy chạy đánh giá sự phù hợp hoặc đầu dò trực tiếp trước.",
"lab.projectionIncompatible": "Lược đồ dự báo của phòng thí nghiệm không tương thích. Xây dựng lại dự báo.",
"lab.statusTitle": "Trạng thái dự báo",
"lab.matrixTitle": "Ma trận tương thích",
"lab.verdictsTitle": "Bản ghi kết quả",
"lab.filter.layer": "Lớp bằng chứng",
"lab.filter.verdict": "Kết quả",
"lab.filter.subject": "ID Chủ đề",
"lab.filter.all": "Tất cả",
"lab.col.subject": "Chủ đề",
"lab.col.layer": "Layer",
"lab.col.suite": "Suite",
"lab.col.verdict": "Kết quả",
"lab.col.asOf": "As of",
"lab.col.protocol": "Protocol conformance",
"lab.col.live": "Live route compatibility",
"lab.col.task": "Task effectiveness",
"lab.empty": "Chưa có kết quả tương thích nào trong projection.",
"lab.subjectKind": "Loại",
"lab.observationCount": "Số quan sát",
"lab.eventCount": "Số sự kiện",
"lab.verdictCount": "Số kết quả",
"lab.subjectCount": "Số đối tượng",
"lab.builtAt": "Thời điểm xây dựng",
"lab.loading": "Đang tải bằng chứng tương thích…",
"lab.loadMore": "Tải thêm",
"lab.detailTitle": "Chi tiết kết quả",
"lab.detailClose": "Đóng",
"lab.detailSubject": "Chủ đề",
"lab.detailObservations": "Các quan sát",
"lab.detailEvents": "Sự kiện bằng chứng",
"lab.detailArtifacts": "Metadata artifact",
"lab.detailLoadFailed": "Không thể tải chi tiết kết quả",
"lab.refresh": "Làm mới",
"lab.verdict.UNKNOWN": "Không xác định",
"lab.verdict.CLAIMED": "Đã khai báo",
"lab.verdict.PROBED": "Đã thăm dò",
"lab.verdict.VERIFIED": "Đã xác minh",
"lab.verdict.DEGRADED": "Suy giảm",
"lab.verdict.BLOCKED": "Bị chặn",
"lab.verdict.UNSUPPORTED": "Không được hỗ trợ",
"lab.layer.protocol_conformance": "Mức tuân thủ protocol",
"lab.layer.live_route_compatibility": "Khả năng tương thích live route",
"lab.layer.task_effectiveness": "Hiệu quả tác vụ",
};

export const LAB_CATALOG_OVERRIDES: Record<LabLocale, Record<LabCatalogKey, string>> = {
en,
de,
Expand All @@ -474,6 +524,7 @@ export const LAB_CATALOG_OVERRIDES: Record<LabLocale, Record<LabCatalogKey, stri
ru,
ja,
tr,
vi,
};

const supplements: Record<LabLocale, Record<LabSupplementKey, string>> = {
Expand Down Expand Up @@ -585,6 +636,9 @@ const supplements: Record<LabLocale, Record<LabSupplementKey, string>> = {
"community.activeRecords": "Etkin kayıtlar",
"community.revokedRecords": "Geri çekilen kayıtlar",
},
vi: {
subjectKindUnknown: "Không xác định", "artifact.present": "Có sẵn", "artifact.corrupt": "Hỏng", "artifact.purged_unavailable": "Đã xóa / không khả dụng", selectVerdict: "Xem kết quả cho {subject}", "community.title": "Bằng chứng cộng đồng", "community.notLocalVerdict": "Ngữ cảnh chỉ đọc không đáng tin cậy. Không được đưa vào kết quả cục bộ này.", "community.bundles": "Gói bằng chứng", "community.activeRecords": "Bản ghi đang hoạt động", "community.revokedRecords": "Bản ghi đã thu hồi",
},
};

export function labSupplement(
Expand Down
27 changes: 27 additions & 0 deletions gui/src/i18n/log-guard-labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,33 @@ const LABELS: Record<Locale, Record<LogGuardLabelKey, string>> = {
"error.database_error": "Codex günlük veritabanı güncellenemedi.",
"error.config_write_failed": "Veritabanı değişti ancak OpenCodex koruma ayarını kaydedemedi. Yapılandırma depolamasını düzeltip ardından korumayı onarın.",
},
vi: {
compact: 'Nén',
compactComplete: "Hoàn tất nén (logic / thu hồi trên đĩa)",
pagesUnit: "trang",
compactPartial: "Nén một phần (logic / thu hồi trên đĩa)",
confirmCompact: 'Xác nhận nén',
cancel: 'Hủy',
inspectionOnly: 'Chỉ kiểm tra',
externalSqliteHome: 'Lưu trữ SQLite bên ngoài',
inspectionUnavailable: "Tính năng kiểm tra nhật ký chẩn đoán không khả dụng.",
metricsSkippedLarge: "Đã bỏ qua số liệu hàng: cơ sở dữ liệu lớn hơn {threshold} và việc quét sẽ làm treo proxy.",
protection: "Bảo vệ",
compat: "Tương thích",
quiet: "Yên tĩnh",
disable: "Tắt bảo vệ",
repair: "Sửa chữa bảo vệ",
applying: "Đang áp dụng bảo vệ…",
"error.generic": "Không thể thay đổi bảo vệ nhật ký Codex.",
"error.codex_running": "Hãy thoát Codex trước khi thay đổi bảo vệ nhật ký.",
"error.process_enumeration_failed": "Không thể xác minh rằng Codex đã dừng. Bảo vệ chưa được thay đổi.",
"error.busy": "Cơ sở dữ liệu nhật ký Codex đang bận. Hãy thoát Codex và thử lại.",
"error.unsupported_schema": "Lược đồ nhật ký Codex này không được hỗ trợ để bảo vệ.",
"error.trigger_collision": "Tên trình kích hoạt Log Guard dành riêng đã được sử dụng. Bảo vệ chưa được thay đổi.",
"error.unsafe_path": "Đường dẫn cơ sở dữ liệu nhật ký Codex không vượt qua được kiểm tra an toàn.",
"error.database_error": "Không thể cập nhật cơ sở dữ liệu nhật ký Codex.",
"error.config_write_failed": "Cơ sở dữ liệu đã thay đổi nhưng OpenCodex không thể lưu cài đặt bảo vệ. Vui lòng sửa lỗi lưu trữ cấu hình, sau đó chạy Sửa chữa.",
},
};

export function logGuardLabel(locale: Locale, key: LogGuardLabelKey): string {
Expand Down
12 changes: 12 additions & 0 deletions gui/src/i18n/log-guard-operation-labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ const LABELS: Record<Locale, Record<LogGuardOperationLabelKey, string>> = {
"error.auto_vacuum_not_incremental": "Bu Codex gunluk veritabani artimli vacuum icin yapilandirilmamis; tam yeniden olusturma olmadan alan geri kazanilamaz.",
"error.integrity_check_failed": "Codex gunluk veritabani butunluk denetiminden gecemedi. Alan geri kazanilmadi.",
},
vi: {
applying: "Đang áp dụng thay đổi Log Guard…",
"error.generic": "Không thể cập nhật lưu trữ nhật ký Codex.",
"error.codex_running": "Hãy thoát Codex trước khi thay đổi lưu trữ nhật ký Codex.",
"error.process_enumeration_failed": "Không thể xác minh rằng Codex đã dừng. Thao tác Log Guard chưa được bắt đầu.",
"error.busy": "Cơ sở dữ liệu nhật ký Codex đang bận. Hãy thoát Codex và thử lại.",
"error.unsupported_schema": "Lược đồ nhật ký Codex này không được hỗ trợ cho thao tác này.",
"error.unsafe_path": "Đường dẫn cơ sở dữ liệu nhật ký Codex không vượt qua được kiểm tra an toàn.",
"error.database_error": "Không thể cập nhật cơ sở dữ liệu nhật ký Codex.",
"error.auto_vacuum_not_incremental": "Cơ sở dữ liệu nhật ký Codex này không được định cấu hình cho tính năng incremental vacuum, do đó không thể thu hồi dung lượng nếu không xây dựng lại hoàn toàn.",
"error.integrity_check_failed": "Cơ sở dữ liệu nhật ký Codex không vượt qua được kiểm tra tính toàn vẹn. Không có dung lượng nào được thu hồi.",
},
};

export function logGuardOperationLabel(locale: Locale, key: LogGuardOperationLabelKey): string {
Expand Down
5 changes: 5 additions & 0 deletions gui/src/i18n/log-guard-state-labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ const LABELS: Record<Locale, StateLabels> = {
protection: { off: "Kapalı", active: "Etkin", drifted: "Onarım gerekli", unsupported: "Desteklenmiyor", unknown: "Bilinmiyor" },
mode: { off: "Kapalı", compat: "Uyumluluk", quiet: "Sessiz", collision: "Bilinmiyor" },
},
vi: {
schema: { compatible: "Tương thích", missing: "Không tìm thấy cơ sở dữ liệu", unreadable: "Cơ sở dữ liệu không khả dụng", unsupported: "Không được hỗ trợ" },
protection: { off: "Tắt", active: "Đang hoạt động", drifted: "Cần sửa chữa", unsupported: "Không được hỗ trợ", unknown: "Không xác định" },
mode: { off: "Tắt", compat: "Tương thích", quiet: "Yên tĩnh", collision: "Không xác định" },
},
};

export function logGuardSchemaStateLabel(locale: Locale, state: LogGuardSchemaState): string {
Expand Down
5 changes: 5 additions & 0 deletions gui/src/i18n/routing-compatibility-labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,9 @@ export const ROUTING_COMPATIBILITY_FIELD_LABELS: Record<Locale, CompatibilityFie
unknownEvidence: "Bilinmeyen kanıt",
degradedEvidence: "Bozulmuş kanıt",
},
vi: {
maxEvidenceAgeMs: "Tuổi bằng chứng tối đa (ms)",
unknownEvidence: "Bằng chứng không xác định",
degradedEvidence: "Bằng chứng bị suy giảm",
},
};
4 changes: 3 additions & 1 deletion gui/src/i18n/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const LOCALES: { code: Locale; htmlLang: string }[] = [
{ code: "ru", htmlLang: "ru" },
{ code: "ja", htmlLang: "ja" },
{ code: "tr", htmlLang: "tr" },
{ code: "vi", htmlLang: "vi" },
];

const LANG_KEY = "ocx-lang";
Expand All @@ -22,7 +23,7 @@ let activeLocale: Locale | null = null;
export function detectInitial(): Locale {
try {
const stored = localStorage.getItem(LANG_KEY);
if (stored === "en" || stored === "de" || stored === "fr" || stored === "ko" || stored === "zh" || stored === "zh-TW" || stored === "ru" || stored === "ja" || stored === "tr") return stored;
if (stored === "en" || stored === "de" || stored === "fr" || stored === "ko" || stored === "zh" || stored === "zh-TW" || stored === "ru" || stored === "ja" || stored === "tr" || stored === "vi") return stored;
} catch { /* ignore */ }
const nav = typeof navigator !== "undefined" && navigator?.language ? navigator.language.toLowerCase() : "en";
if (nav.startsWith("de")) return "de";
Expand All @@ -43,6 +44,7 @@ export function detectInitial(): Locale {
if (nav.startsWith("ru")) return "ru";
if (nav.startsWith("ja")) return "ja";
if (nav.startsWith("tr")) return "tr";
if (nav.startsWith("vi")) return "vi";
return "en";
}

Expand Down
Loading
Loading