Merge pull request #507 from hthienloc/add-vietnamese-i18n-1022783609047552672

Add Vietnamese i18n support (vi locale)
This commit is contained in:
Sid
2026-05-09 14:48:31 -07:00
committed by GitHub
10 changed files with 465 additions and 2 deletions
+6 -2
View File
@@ -15,12 +15,14 @@ import commonKo from "../src/i18n/locales/ko-KR/common.json";
import dialogsKo from "../src/i18n/locales/ko-KR/dialogs.json";
import commonTr from "../src/i18n/locales/tr/common.json";
import dialogsTr from "../src/i18n/locales/tr/dialogs.json";
import commonVi from "../src/i18n/locales/vi/common.json";
import dialogsVi from "../src/i18n/locales/vi/dialogs.json";
import commonZh from "../src/i18n/locales/zh-CN/common.json";
import dialogsZh from "../src/i18n/locales/zh-CN/dialogs.json";
import commonZhTw from "../src/i18n/locales/zh-TW/common.json";
import dialogsZhTw from "../src/i18n/locales/zh-TW/dialogs.json";
type Locale = "en" | "zh-CN" | "zh-TW" | "es" | "fr" | "ja-JP" | "ko-KR" | "tr" | "ar";
type Locale = "en" | "zh-CN" | "zh-TW" | "es" | "fr" | "ja-JP" | "ko-KR" | "tr" | "ar" | "vi";
type Namespace = "common" | "dialogs";
type MessageMap = Record<string, unknown>;
@@ -34,6 +36,7 @@ const messages: Record<Locale, Record<Namespace, MessageMap>> = {
"ko-KR": { common: commonKo, dialogs: dialogsKo },
tr: { common: commonTr, dialogs: dialogsTr },
ar: { common: commonAr, dialogs: dialogsAr },
vi: { common: commonVi, dialogs: dialogsVi },
};
let currentLocale: Locale = "en";
@@ -48,7 +51,8 @@ export function setMainLocale(locale: string) {
locale === "ja-JP" ||
locale === "ko-KR" ||
locale === "tr" ||
locale === "ar"
locale === "ar" ||
locale === "vi"
) {
currentLocale = locale;
}
@@ -8,6 +8,7 @@ import jaJPDialogs from "@/i18n/locales/ja-JP/dialogs.json";
import koKRDialogs from "@/i18n/locales/ko-KR/dialogs.json";
import ruDialogs from "@/i18n/locales/ru/dialogs.json";
import trDialogs from "@/i18n/locales/tr/dialogs.json";
import viDialogs from "@/i18n/locales/vi/dialogs.json";
import zhCNDialogs from "@/i18n/locales/zh-CN/dialogs.json";
import zhTWDialogs from "@/i18n/locales/zh-TW/dialogs.json";
@@ -47,6 +48,7 @@ const dialogsByLocale = {
ru: ruDialogs,
"ja-JP": jaJPDialogs,
ar: arDialogs,
vi: viDialogs,
} satisfies Record<Locale, { tutorial: Record<string, unknown> }>;
describe("TutorialHelp translations", () => {
+1
View File
@@ -10,6 +10,7 @@ export const SUPPORTED_LOCALES = [
"ja-JP",
"ar",
"ru",
"vi",
] as const;
export const I18N_NAMESPACES = [
"common",
+30
View File
@@ -0,0 +1,30 @@
{
"actions": {
"cancel": "Hủy",
"save": "Lưu",
"delete": "Xóa",
"close": "Đóng",
"share": "Chia sẻ",
"done": "Hoàn tất",
"open": "Mở",
"upload": "Tải lên",
"export": "Xuất",
"showInFolder": "Hiển thị trong thư mục",
"file": "Tệp",
"edit": "Chỉnh sửa",
"view": "Xem",
"window": "Cửa sổ",
"quit": "Thoát",
"stopRecording": "Dừng ghi hình"
},
"playback": {
"play": "Phát",
"pause": "Tạm dừng",
"fullscreen": "Toàn màn hình",
"exitFullscreen": "Thoát toàn màn hình"
},
"locale": {
"name": "Tiếng Việt",
"short": "VI"
}
}
+70
View File
@@ -0,0 +1,70 @@
{
"export": {
"complete": "Xuất hoàn tất",
"yourFormatReady": "{{format}} của bạn đã sẵn sàng",
"showInFolder": "Hiển thị trong thư mục",
"finalizingVideo": "Đang hoàn tất xuất video...",
"compilingGifProgress": "Đang biên dịch GIF... {{progress}}%",
"compilingGifWait": "Đang biên dịch GIF... Có thể mất một lúc",
"takeMoment": "Có thể mất một chút thời gian...",
"failed": "Xuất thất bại",
"tryAgain": "Vui lòng thử lại",
"finalizingVideoTitle": "Hoàn tất video",
"compilingGif": "Biên dịch GIF",
"exportingFormat": "Đang xuất {{format}}",
"compiling": "Đang biên dịch",
"renderingFrames": "Kết xuất khung hình",
"processing": "Đang xử lý...",
"finalizing": "Đang hoàn tất...",
"compilingStatus": "Đang biên dịch...",
"status": "Trạng thái",
"format": "Định dạng",
"frames": "Khung hình",
"cancelExport": "Hủy xuất",
"savedSuccessfully": "Đã lưu {{format}} thành công!"
},
"tutorial": {
"triggerLabel": "Cách hoạt động của công cụ cắt",
"title": "Cách cắt video",
"description": "Hiểu cách cắt bỏ các phần không mong muốn trong video của bạn.",
"explanationBefore": "Công cụ Cắt hoạt động bằng cách xác định các đoạn bạn muốn",
"remove": "xóa",
"explanationMiddle": " — bất cứ thứ gì",
"covered": "được bao phủ",
"explanationAfter": "bởi một đoạn cắt màu đỏ sẽ bị loại bỏ khi bạn xuất.",
"visualExample": "Ví dụ trực quan",
"removed": "ĐÃ XÓA",
"kept": "Giữ lại",
"part1": "Phần 1",
"part2": "Phần 2",
"part3": "Phần 3",
"finalVideo": "Video cuối cùng",
"step1Title": "1. Thêm đoạn cắt",
"step1DescriptionBefore": "Nhấn ",
"step1DescriptionAfter": " hoặc nhấp vào biểu tượng cái kéo để đánh dấu một phần cần xóa.",
"step2Title": "2. Điều chỉnh",
"step2Description": "Kéo các cạnh của vùng màu đỏ để bao phủ chính xác những gì bạn muốn cắt bỏ."
},
"unsavedChanges": {
"title": "Thay đổi chưa được lưu",
"message": "Bạn có các thay đổi chưa được lưu.",
"detail": "Bạn có muốn lưu dự án của mình trước khi đóng không?",
"saveAndClose": "Lưu & Đóng",
"discardAndClose": "Bỏ qua & Đóng",
"loadProject": "Tải dự án…",
"saveProject": "Lưu dự án…",
"saveProjectAs": "Lưu dự án thành…"
},
"fileDialogs": {
"saveGif": "Lưu GIF đã xuất",
"saveVideo": "Lưu Video đã xuất",
"selectVideo": "Chọn tệp video",
"saveProject": "Lưu dự án OpenScreen",
"openProject": "Mở dự án OpenScreen",
"gifImage": "Hình ảnh GIF",
"mp4Video": "Video MP4",
"videoFiles": "Tệp Video",
"openscreenProject": "Dự án OpenScreen",
"allFiles": "Tất cả các tệp"
}
}
+45
View File
@@ -0,0 +1,45 @@
{
"newRecording": {
"title": "Quay lại Trình ghi",
"description": "Phiên hiện tại của bạn đã được lưu.",
"cancel": "Hủy",
"confirm": "Xác nhận"
},
"loadingVideo": "Đang tải video...",
"errors": {
"noVideoLoaded": "Chưa tải video nào",
"videoNotReady": "Video chưa sẵn sàng",
"unableToDetermineSourcePath": "Không thể xác định đường dẫn video gốc",
"failedToSaveGif": "Không thể lưu GIF",
"gifExportFailed": "Xuất GIF thất bại",
"failedToSaveVideo": "Không thể lưu video",
"exportFailed": "Xuất thất bại",
"exportFailedWithError": "Xuất thất bại: {{error}}",
"exportBackgroundLoadFailed": "Xuất thất bại: không thể tải hình nền ({{url}})",
"failedToSaveExport": "Không thể lưu bản xuất",
"failedToSaveExportedVideo": "Không thể lưu video đã xuất",
"failedToRevealInFolder": "Lỗi khi hiển thị trong thư mục: {{error}}"
},
"export": {
"canceled": "Đã hủy xuất",
"exportedSuccessfully": "Đã xuất {{format}} thành công"
},
"project": {
"saveCanceled": "Đã hủy lưu dự án",
"failedToSave": "Lưu dự án thất bại",
"savedTo": "Đã lưu dự án vào {{path}}",
"failedToLoad": "Tải dự án thất bại",
"invalidFormat": "Định dạng tệp dự án không hợp lệ",
"loadedFrom": "Đã tải dự án từ {{path}}"
},
"recording": {
"failedCameraAccess": "Yêu cầu quyền truy cập máy ảnh thất bại.",
"cameraBlocked": "Quyền truy cập máy ảnh bị chặn. Hãy bật nó trong cài đặt hệ thống để sử dụng webcam.",
"systemAudioUnavailable": "Âm thanh hệ thống không khả dụng. Ghi hình không có âm thanh hệ thống.",
"microphoneDenied": "Quyền truy cập micro bị từ chối. Sẽ tiếp tục ghi hình không có âm thanh.",
"cameraDenied": "Quyền truy cập máy ảnh bị từ chối. Sẽ tiếp tục ghi hình không có webcam.",
"cameraDisconnected": "Webcam bị ngắt kết nối.",
"cameraNotFound": "Không tìm thấy máy ảnh.",
"permissionDenied": "Quyền ghi hình bị từ chối. Vui lòng cho phép ghi màn hình."
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"tooltips": {
"hideHUD": "Ẩn HUD",
"closeApp": "Đóng ứng dụng",
"restartRecording": "Khởi động lại ghi hình",
"cancelRecording": "Hủy ghi hình",
"pauseRecording": "Tạm dừng ghi hình",
"resumeRecording": "Tiếp tục ghi hình",
"openVideoFile": "Mở tệp video",
"openProject": "Mở dự án"
},
"audio": {
"enableSystemAudio": "Bật âm thanh hệ thống",
"disableSystemAudio": "Tắt âm thanh hệ thống",
"enableMicrophone": "Bật micro",
"disableMicrophone": "Tắt micro",
"defaultMicrophone": "Micro Mặc định"
},
"webcam": {
"enableWebcam": "Bật webcam",
"disableWebcam": "Tắt webcam",
"defaultCamera": "Máy ảnh Mặc định",
"searching": "Đang tìm kiếm...",
"noneFound": "Không tìm thấy máy ảnh",
"unavailable": "Máy ảnh không khả dụng"
},
"sourceSelector": {
"loading": "Đang tải nguồn...",
"screens": "Màn hình ({{count}})",
"windows": "Cửa sổ ({{count}})",
"defaultSourceName": "Màn hình"
},
"recording": {
"selectSource": "Vui lòng chọn một nguồn để ghi"
},
"language": "Ngôn ngữ",
"systemLanguagePrompt": {
"title": "Sử dụng ngôn ngữ hệ thống của bạn?",
"description": "Chúng tôi phát hiện {{language}} là ngôn ngữ hệ thống của bạn. Bạn có muốn chuyển OpenScreen sang {{language}} không?",
"switch": "Chuyển sang {{language}}",
"keepDefault": "Giữ ngôn ngữ hiện tại"
}
}
+176
View File
@@ -0,0 +1,176 @@
{
"zoom": {
"level": "Mức độ thu phóng",
"selectRegion": "Chọn vùng thu phóng để điều chỉnh",
"deleteZoom": "Xóa thu phóng",
"focusMode": {
"title": "Chế độ lấy nét",
"manual": "Thủ công",
"auto": "Tự động",
"autoDescription": "Máy ảnh đi theo vị trí con trỏ đã ghi"
}
},
"speed": {
"playbackSpeed": "Tốc độ phát",
"selectRegion": "Chọn vùng tốc độ để điều chỉnh",
"deleteRegion": "Xóa vùng tốc độ",
"customPlaybackSpeed": "Tốc độ phát tùy chỉnh",
"maxSpeedError": "Tốc độ không thể cao hơn 16×"
},
"trim": {
"deleteRegion": "Xóa vùng cắt"
},
"layout": {
"title": "Bố cục",
"preset": "Cài đặt sẵn",
"selectPreset": "Chọn cài đặt sẵn",
"pictureInPicture": "Hình trong hình",
"verticalStack": "Xếp chồng dọc",
"dualFrame": "Khung kép",
"webcamShape": "Hình dạng máy ảnh",
"webcamSize": "Kích thước Webcam"
},
"effects": {
"title": "Hiệu ứng video",
"blurBg": "Làm mờ nền",
"motionBlur": "Làm mờ chuyển động",
"off": "tắt",
"shadow": "Bóng đổ",
"roundness": "Độ bo tròn",
"padding": "Phần đệm"
},
"background": {
"title": "Nền",
"image": "Hình ảnh",
"color": "Màu sắc",
"gradient": "Dải màu",
"uploadCustom": "Tải lên tùy chỉnh",
"gradientLabel": "Dải màu {{index}}"
},
"crop": {
"title": "Cắt xén",
"cropVideo": "Cắt xén video",
"dragInstruction": "Kéo ở mỗi cạnh để điều chỉnh vùng cắt xén",
"ratio": "Tỷ lệ",
"free": "Tự do",
"done": "Hoàn tất",
"lockAspectRatio": "Khóa tỷ lệ khung hình",
"unlockAspectRatio": "Mở khóa tỷ lệ khung hình"
},
"exportFormat": {
"mp4": "MP4",
"gif": "GIF",
"mp4Video": "Video MP4",
"mp4Description": "Tệp video chất lượng cao",
"gifAnimation": "Ảnh động GIF",
"gifDescription": "Hình ảnh động để chia sẻ"
},
"exportQuality": {
"title": "Chất lượng xuất",
"low": "Thấp",
"medium": "Trung bình",
"high": "Cao"
},
"gifSettings": {
"frameRate": "Tốc độ khung hình GIF",
"size": "Kích thước GIF",
"loop": "Lặp lại GIF"
},
"project": {
"save": "Lưu dự án",
"load": "Tải dự án"
},
"export": {
"videoButton": "Xuất Video",
"gifButton": "Xuất GIF",
"chooseSaveLocation": "Chọn vị trí lưu"
},
"links": {
"reportBug": "Báo cáo lỗi",
"starOnGithub": "Đánh giá sao trên GitHub"
},
"imageUpload": {
"invalidFileType": "Loại tệp không hợp lệ",
"jpgOnly": "Vui lòng tải lên tệp hình ảnh JPG hoặc JPEG.",
"uploadSuccess": "Tải lên hình ảnh tùy chỉnh thành công!",
"failedToUpload": "Tải lên hình ảnh thất bại",
"errorReading": "Đã xảy ra lỗi khi đọc tệp."
},
"annotation": {
"title": "Cài đặt chú thích",
"active": "Hoạt động",
"typeText": "Văn bản",
"typeImage": "Hình ảnh",
"typeArrow": "Mũi tên",
"typeBlur": "Làm mờ",
"textContent": "Nội dung văn bản",
"textPlaceholder": "Nhập văn bản của bạn...",
"fontStyle": "Kiểu phông chữ",
"selectStyle": "Chọn kiểu",
"size": "Kích thước",
"customFonts": "Phông chữ tùy chỉnh",
"textColor": "Màu văn bản",
"background": "Nền",
"none": "Không có",
"color": "Màu sắc",
"clearBackground": "Xóa nền",
"uploadImage": "Tải lên hình ảnh",
"supportedFormats": "Định dạng hỗ trợ: JPG, PNG, GIF, WebP",
"arrowDirection": "Hướng mũi tên",
"strokeWidth": "Độ dày nét: {{width}}px",
"arrowColor": "Màu mũi tên",
"blurType": "Loại làm mờ",
"blurTypeBlur": "Làm mờ",
"blurTypeMosaic": "Khảm",
"blurColor": "Màu làm mờ",
"blurColorWhite": "Trắng",
"blurColorBlack": "Đen",
"blurShape": "Hình dạng làm mờ",
"blurIntensity": "Cường độ làm mờ",
"mosaicBlockSize": "Kích thước khối khảm",
"blurShapeRectangle": "Chữ nhật",
"blurShapeOval": "Bầu dục",
"blurShapeFreehand": "Vẽ tự do",
"deleteAnnotation": "Xóa chú thích",
"shortcutsAndTips": "Phím tắt & Mẹo",
"tipMovePlayhead": "Di chuyển đầu phát đến phần chú thích chồng chéo và chọn một mục.",
"tipTabCycle": "Sử dụng Tab để chuyển qua các mục chồng chéo.",
"tipShiftTabCycle": "Sử dụng Shift+Tab để chuyển ngược lại.",
"invalidImageType": "Loại tệp không hợp lệ",
"imageFormatsOnly": "Vui lòng tải lên tệp hình ảnh JPG, PNG, GIF hoặc WebP.",
"imageUploadSuccess": "Tải lên hình ảnh thành công!",
"failedImageUpload": "Tải lên hình ảnh thất bại"
},
"fontStyles": {
"classic": "Cổ điển",
"editor": "Trình chỉnh sửa",
"strong": "Đậm",
"typewriter": "Máy đánh chữ",
"deco": "Trang trí",
"simple": "Đơn giản",
"modern": "Hiện đại",
"clean": "Sạch sẽ"
},
"customFont": {
"dialogTitle": "Thêm Google Font",
"urlLabel": "URL nhập Google Fonts",
"urlPlaceholder": "https://fonts.googleapis.com/css2?family=Roboto&display=swap",
"urlHelp": "Lấy từ Google Fonts: Chọn một phông chữ → Nhấp \"Get font\" → Sao chép URL @import",
"nameLabel": "Tên hiển thị",
"namePlaceholder": "Phông chữ tùy chỉnh của tôi",
"nameHelp": "Đây là cách phông chữ sẽ xuất hiện trong bộ chọn phông chữ",
"addButton": "Thêm phông chữ",
"addingButton": "Đang thêm...",
"errorEmptyUrl": "Vui lòng nhập URL nhập Google Fonts",
"errorInvalidUrl": "Vui lòng nhập URL Google Fonts hợp lệ",
"errorEmptyName": "Vui lòng nhập tên phông chữ",
"errorExtractFailed": "Không thể trích xuất họ phông chữ từ URL",
"successMessage": "Thêm phông chữ \"{{fontName}}\" thành công",
"failedToAdd": "Thêm phông chữ thất bại",
"errorTimeout": "Tải phông chữ mất quá nhiều thời gian. Vui lòng kiểm tra URL và thử lại.",
"errorLoadFailed": "Không thể tải phông chữ. Vui lòng xác minh URL Google Fonts là chính xác."
},
"language": {
"title": "Ngôn ngữ"
}
}
+37
View File
@@ -0,0 +1,37 @@
{
"title": "Phím tắt",
"customize": "Tùy chỉnh",
"configurable": "Có thể định cấu hình",
"fixed": "Cố định",
"pressKey": "Nhấn một phím…",
"clickToChange": "Nhấp để thay đổi",
"pressEscToCancel": "Nhấn Esc để hủy",
"helpText": "Nhấp vào một phím tắt rồi nhấn tổ hợp phím mới. Nhấn Esc để hủy.",
"resetToDefaults": "Khôi phục mặc định",
"alreadyUsedBy": "Đã được sử dụng bởi {{action}}",
"swap": "Hoán đổi",
"reservedShortcut": "Phím tắt này được dành riêng cho \"{{label}}\" và không thể gán lại.",
"savedToast": "Đã lưu phím tắt",
"resetToast": "Đã đặt lại về phím tắt mặc định — nhấp Lưu để áp dụng",
"actions": {
"addZoom": "Thêm Thu phóng",
"addTrim": "Thêm Cắt",
"addSpeed": "Thêm Tốc độ",
"addAnnotation": "Thêm Chú thích",
"addBlur": "Thêm Làm mờ",
"addKeyframe": "Thêm Khung hình chính",
"deleteSelected": "Xóa mục đã chọn",
"playPause": "Phát / Tạm dừng"
},
"fixedActions": {
"undo": "Hoàn tác",
"redo": "Làm lại",
"cycleAnnotationsForward": "Chuyển tiếp qua các chú thích",
"cycleAnnotationsBackward": "Chuyển lùi qua các chú thích",
"deleteSelectedAlt": "Xóa mục đã chọn (alt)",
"panTimeline": "Xoay Trục thời gian",
"zoomTimeline": "Thu phóng Trục thời gian",
"frameBack": "Lùi một khung hình",
"frameForward": "Tiến một khung hình"
}
}
+55
View File
@@ -0,0 +1,55 @@
{
"buttons": {
"addZoom": "Thêm Thu phóng (Z)",
"suggestZooms": "Đề xuất Thu phóng từ Con trỏ",
"addTrim": "Thêm Cắt (T)",
"addAnnotation": "Thêm Chú thích (A)",
"addBlur": "Thêm Làm mờ (B)",
"addSpeed": "Thêm Tốc độ (S)"
},
"hints": {
"pressZoom": "Nhấn Z để thêm thu phóng",
"pressTrim": "Nhấn T để thêm cắt",
"pressAnnotation": "Nhấn A để thêm chú thích",
"pressBlur": "Nhấn B để thêm vùng làm mờ",
"pressSpeed": "Nhấn S để thêm tốc độ"
},
"labels": {
"pan": "Xoay",
"zoom": "Thu phóng",
"trim": "Cắt",
"speed": "Tốc độ",
"zoomItem": "Thu phóng {{index}}",
"trimItem": "Cắt {{index}}",
"speedItem": "Tốc độ {{index}}",
"annotationItem": "Chú thích",
"blurItem": "Làm mờ {{index}}",
"imageItem": "Hình ảnh",
"emptyText": "Văn bản trống"
},
"emptyState": {
"noVideo": "Chưa tải video",
"dragAndDrop": "Kéo và thả video để bắt đầu chỉnh sửa"
},
"errors": {
"cannotPlaceZoom": "Không thể đặt thu phóng ở đây",
"zoomExistsAtLocation": "Thu phóng đã tồn tại ở vị trí này hoặc không có đủ không gian.",
"zoomSuggestionUnavailable": "Trình xử lý đề xuất thu phóng không khả dụng",
"noCursorTelemetry": "Không có dữ liệu từ xa của con trỏ",
"noCursorTelemetryDescription": "Ghi hình màn hình trước để tạo các đề xuất dựa trên con trỏ.",
"noUsableTelemetry": "Không có dữ liệu từ xa của con trỏ có thể sử dụng",
"noUsableTelemetryDescription": "Bản ghi không chứa đủ dữ liệu chuyển động của con trỏ.",
"noDwellMoments": "Không tìm thấy khoảnh khắc dừng con trỏ rõ ràng",
"noDwellMomentsDescription": "Thử ghi hình với các lần tạm dừng con trỏ chậm hơn ở các thao tác quan trọng.",
"noAutoZoomSlots": "Không có khe thu phóng tự động nào",
"noAutoZoomSlotsDescription": "Các điểm dừng được phát hiện chồng chéo với các vùng thu phóng hiện có.",
"cannotPlaceTrim": "Không thể đặt cắt ở đây",
"trimExistsAtLocation": "Cắt đã tồn tại ở vị trí này hoặc không có đủ không gian.",
"cannotPlaceSpeed": "Không thể đặt tốc độ ở đây",
"speedExistsAtLocation": "Vùng tốc độ đã tồn tại ở vị trí này hoặc không có đủ không gian."
},
"success": {
"addedZoomSuggestions": "Đã thêm {{count}} đề xuất thu phóng dựa trên con trỏ",
"addedZoomSuggestionsPlural": "Đã thêm {{count}} đề xuất thu phóng dựa trên con trỏ"
}
}