From af8865327a534a62efd8f9bfbe8b48e27792edd5 Mon Sep 17 00:00:00 2001 From: HUi <32211782+huynguyeexn@users.noreply.github.com> Date: Sat, 20 Dec 2025 02:17:34 +0700 Subject: [PATCH] feat: Add Vietnamese translation (#1459) --- client/src/locales/index.js | 2 + client/src/locales/vi-VN/core.js | 501 ++++++++++++++++++ client/src/locales/vi-VN/index.js | 8 + client/src/locales/vi-VN/login.js | 34 ++ client/src/locales/vi-VN/markdown-editor.json | 169 ++++++ .../controllers/access-tokens/accept-terms.js | 2 +- server/api/controllers/users/create.js | 2 +- server/api/controllers/users/update.js | 2 +- server/api/models/User.js | 3 +- server/config/i18n.js | 1 + server/config/locales/vi-VN.json | 19 + 11 files changed, 739 insertions(+), 4 deletions(-) create mode 100644 client/src/locales/vi-VN/core.js create mode 100644 client/src/locales/vi-VN/index.js create mode 100644 client/src/locales/vi-VN/login.js create mode 100644 client/src/locales/vi-VN/markdown-editor.json create mode 100644 server/config/locales/vi-VN.json diff --git a/client/src/locales/index.js b/client/src/locales/index.js index 19c4a50f..8cec4da8 100644 --- a/client/src/locales/index.js +++ b/client/src/locales/index.js @@ -37,6 +37,7 @@ import svSE from './sv-SE'; import trTR from './tr-TR'; import ukUA from './uk-UA'; import uzUZ from './uz-UZ'; +import viVN from './vi-VN'; import zhCN from './zh-CN'; import zhTW from './zh-TW'; @@ -73,6 +74,7 @@ const locales = [ trTR, ukUA, uzUZ, + viVN, zhCN, zhTW, ]; diff --git a/client/src/locales/vi-VN/core.js b/client/src/locales/vi-VN/core.js new file mode 100644 index 00000000..4911cd58 --- /dev/null +++ b/client/src/locales/vi-VN/core.js @@ -0,0 +1,501 @@ +import dateFns from 'date-fns/locale/vi'; +import timeAgo from 'javascript-time-ago/locale/vi'; + +import markdownEditor from './markdown-editor.json'; + +export default { + dateFns, + timeAgo, + markdownEditor, + + format: { + date: 'dd/MM/yyyy', + time: 'HH:mm', + dateTime: '$t(format:date) $t(format:time)', + longDate: 'd MMM', + longDateTime: "d MMMM 'lúc' HH:mm", + fullDate: 'd MMM, y', + fullDateTime: "d MMMM, y 'lúc' HH:mm", + }, + + translation: { + common: { + aboutApp_title: 'Về ứng dụng', + aboutPlanka_title: 'Về PLANKA', + accessToken: 'Access token', + account: 'Tài khoản', + actions: 'Thao tác', + activateUser_title: 'Kích hoạt người dùng', + active: 'Đang hoạt động', + addAttachment_title: 'Thêm tệp đính kèm', + addCustomFieldGroup_title: 'Thêm nhóm trường tùy chỉnh', + addCustomField_title: 'Thêm trường tùy chỉnh', + addManager_title: 'Thêm quản lý', + addMember_title: 'Thêm thành viên', + addTaskList_title: 'Thêm danh sách công việc', + addUser_title: 'Thêm người dùng', + admin: 'Quản trị viên', + administration: 'Quản trị hệ thống', + all: 'Tất cả', + allChangesWillBeAutomaticallySavedAfterConnectionRestored: + 'Mọi thay đổi sẽ tự động được lưu
sau khi kết nối được khôi phục.', + alphabetically: 'Theo bảng chữ cái', + alwaysDisplayCardCreator: 'Luôn hiển thị trình tạo thẻ', + apiKeyCreated_title: 'Đã tạo API Key', + apiKey_title: 'API Key', + archive: 'Lưu trữ', + archiveCard_title: 'Lưu trữ thẻ', + archiveCards_title: 'Lưu trữ các thẻ', + areYouSureYouWantToActivateThisUser: 'Bạn có chắc chắn muốn kích hoạt người dùng này không?', + areYouSureYouWantToArchiveCards: 'Bạn có chắc chắn muốn lưu trữ các thẻ này không?', + areYouSureYouWantToArchiveThisCard: 'Bạn có chắc chắn muốn lưu trữ thẻ này không?', + areYouSureYouWantToAssignThisProjectManagerAsOwner: + 'Bạn có chắc chắn muốn chỉ định quản lý dự án này làm chủ sở hữu không?', + areYouSureYouWantToDeactivateThisUser: + 'Bạn có chắc chắn muốn hủy kích hoạt người dùng này không?', + areYouSureYouWantToDeleteThisApiKey: 'Bạn có chắc chắn muốn xóa API Key này không?', + areYouSureYouWantToDeleteThisAttachment: 'Bạn có chắc chắn muốn xóa tệp đính kèm này không?', + areYouSureYouWantToDeleteThisBackgroundImage: 'Bạn có chắc chắn muốn xóa ảnh nền này không?', + areYouSureYouWantToDeleteThisBoard: 'Bạn có chắc chắn muốn xóa bảng này không?', + areYouSureYouWantToDeleteThisCard: 'Bạn có chắc chắn muốn xóa thẻ này không?', + areYouSureYouWantToDeleteThisCardForever: + 'Bạn có chắc chắn muốn xóa vĩnh viễn thẻ này không?', + areYouSureYouWantToDeleteThisComment: 'Bạn có chắc chắn muốn xóa bình luận này không?', + areYouSureYouWantToDeleteThisCustomField: + 'Bạn có chắc chắn muốn xóa trường tùy chỉnh này không?', + areYouSureYouWantToDeleteThisCustomFieldGroup: + 'Bạn có chắc chắn muốn xóa nhóm trường tùy chỉnh này không?', + areYouSureYouWantToDeleteThisLabel: 'Bạn có chắc chắn muốn xóa nhãn này không?', + areYouSureYouWantToDeleteThisList: + 'Bạn có chắc chắn muốn xóa danh sách này? Tất cả các thẻ sẽ được chuyển vào thùng rác.', + areYouSureYouWantToDeleteThisNotificationService: + 'Bạn có chắc chắn muốn xóa dịch vụ thông báo này không?', + areYouSureYouWantToDeleteThisProject: 'Bạn có chắc chắn muốn xóa dự án này không?', + areYouSureYouWantToDeleteThisTask: 'Bạn có chắc chắn muốn xóa nhiệm vụ này không?', + areYouSureYouWantToDeleteThisTaskList: + 'Bạn có chắc chắn muốn xóa danh sách công việc này không?', + areYouSureYouWantToDeleteThisUser: 'Bạn có chắc chắn muốn xóa người dùng này không?', + areYouSureYouWantToDeleteThisWebhook: 'Bạn có chắc chắn muốn xóa webhook này không?', + areYouSureYouWantToEmptyTrash: 'Bạn có chắc chắn muốn dọn sạch thùng rác không?', + areYouSureYouWantToLeaveBoard: 'Bạn có chắc chắn muốn rời khỏi bảng này không?', + areYouSureYouWantToLeaveProject: 'Bạn có chắc chắn muốn rời khỏi dự án này không?', + areYouSureYouWantToMakeThisProjectPrivate: + 'Bạn có chắc chắn muốn chuyển dự án này sang riêng tư không?', + areYouSureYouWantToMakeThisProjectShared: + 'Bạn có chắc chắn muốn chuyển dự án này sang công khai không?', + areYouSureYouWantToRegenerateThisApiKey: + 'Bạn có chắc chắn muốn tạo lại API Key này không? Key cũ sẽ không còn hiệu lực.', + areYouSureYouWantToRemoveThisManagerFromProject: + 'Bạn có chắc chắn muốn xóa quản lý này khỏi dự án không?', + areYouSureYouWantToRemoveThisMemberFromBoard: + 'Bạn có chắc chắn muốn xóa thành viên này khỏi bảng không?', + assignAsOwner_title: 'Chỉ định làm chủ sở hữu', + atLeastOneListMustBePresent: 'Phải có ít nhất một danh sách', + attachment: 'Tệp đính kèm', + attachments: 'Các tệp đính kèm', + authentication: 'Xác thực', + background: 'Nền', + baseCustomFields_title: 'Các trường tùy chỉnh cơ sở', + baseGroup: 'Nhóm cơ sở', + board: 'Bảng', + boardActions_title: 'Thao tác với bảng', + boardNotFound_title: 'Không tìm thấy bảng', + boardSubscribed: 'Đã đăng ký theo dõi bảng', + boardUser: 'Người dùng bảng', + byCreationTime: 'Theo thời gian tạo', + byDefault: 'Theo mặc định', + byDueDate: 'Theo hạn chót', + canBeInvitedToWorkInBoards: 'Có thể được mời làm việc trong các bảng.', + canComment: 'Có thể bình luận', + canCreateOwnProjectsAndBeInvitedToWorkInOthers: + 'Có thể tạo dự án riêng và được mời làm việc trong các dự án khác.', + canEditBoardLayoutAndAssignMembersToCards: + 'Có thể chỉnh sửa bố cục bảng và chỉ định thành viên cho các thẻ.', + canManageSystemWideSettingsAndActAsProjectOwner: + 'Có thể quản lý các thiết lập hệ thống và đóng vai trò là chủ sở hữu dự án.', + canOnlyViewBoard: 'Chỉ có thể xem bảng.', + cardActions_title: 'Thao tác với thẻ', + cardNotFound_title: 'Không tìm thấy thẻ', + cardsOnThisListAreAvailableToAllBoardMembers: + 'Các thẻ trong danh sách này khả dụng cho tất cả thành viên trong bảng.', + cardsOnThisListAreCompleteAndReadyToBeArchived: + 'Các thẻ trong danh sách này đã hoàn thành và sẵn sàng để lưu trữ.', + cardsOnThisListAreReadyToBeWorkedOn: 'Các thẻ trong danh sách này đã sẵn sàng để thực hiện.', + clickHereOrRefreshPageToUpdate: '<0>Nhấp vào đây hoặc tải lại trang để cập nhật.', + clientHostnameInEhlo: 'Tên máy chủ của máy khách (EHLO)', + closed: 'Đã đóng', + color: 'Màu sắc', + comments: 'Bình luận', + contentExceedsLimit: 'Nội dung vượt quá giới hạn {{limit}}', + contentOfThisAttachmentIsTooBigToDisplay: + 'Nội dung của tệp đính kèm này quá lớn để hiển thị.', + copy_inline: 'sao chép', + createBoard_title: 'Tạo bảng', + createCustomFieldGroup_title: 'Tạo nhóm trường tùy chỉnh', + createLabel_title: 'Tạo nhãn', + createNewOneOrSelectExistingOne: 'Tạo cái mới hoặc chọn
cái đã có.', + createProject_title: 'Tạo dự án', + createTextFile_title: 'Tạo tệp văn bản', + creator: 'Người tạo', + currentPassword: 'Mật khẩu hiện tại', + currentUser: 'Người dùng hiện tại', + customFieldGroup_title: 'Nhóm trường tùy chỉnh', + customFieldGroups_title: 'Các nhóm trường tùy chỉnh', + customField_title: 'Trường tùy chỉnh', + customFields_title: 'Các trường tùy chỉnh', + customerPanel_title: 'Bảng điều khiển khách hàng', + dangerZone_title: 'Khu vực nguy hiểm', + date: 'Ngày', + deactivateUser_title: 'Hủy kích hoạt người dùng', + defaultCardType_title: 'Loại thẻ mặc định', + defaultFrom: 'Người gửi mặc định ("from")', + defaultView_title: 'Chế độ xem mặc định', + deleteAllBoardsToBeAbleToDeleteThisProject: 'Xóa tất cả các bảng để có thể xóa dự án này', + deleteApiKey_title: 'Xóa API Key', + deleteAttachment_title: 'Xóa tệp đính kèm', + deleteBackgroundImage_title: 'Xóa ảnh nền', + deleteBoard_title: 'Xóa bảng', + deleteCardForever_title: 'Xóa thẻ vĩnh viễn', + deleteCard_title: 'Xóa thẻ', + deleteComment_title: 'Xóa bình luận', + deleteCustomFieldGroup_title: 'Xóa nhóm trường tùy chỉnh', + deleteCustomField_title: 'Xóa trường tùy chỉnh', + deleteLabel_title: 'Xóa nhãn', + deleteList_title: 'Xóa danh sách', + deleteNotificationService_title: 'Xóa dịch vụ thông báo', + deleteProject_title: 'Xóa dự án', + deleteTaskList_title: 'Xóa danh sách công việc', + deleteTask_title: 'Xóa nhiệm vụ', + deleteUser_title: 'Xóa người dùng', + deleteWebhook_title: 'Xóa webhook', + deletedUser_title: 'Người dùng đã bị xóa', + description: 'Mô tả', + display: 'Hiển thị', + dropFileToUpload: 'Thả tệp vào đây để tải lên', + dueDate_title: 'Hạn chót', + dynamicAndUnevenlySpacedLayout: 'Bố cục động và khoảng cách không đều.', + editAttachment_title: 'Chỉnh sửa tệp đính kèm', + editAvatar_title: 'Chỉnh sửa ảnh đại diện', + editColor_title: 'Chỉnh sửa màu sắc', + editCustomFieldGroup_title: 'Chỉnh sửa nhóm trường tùy chỉnh', + editCustomField_title: 'Chỉnh sửa trường tùy chỉnh', + editDueDate_title: 'Chỉnh sửa hạn chót', + editEmail_title: 'Chỉnh sửa E-mail', + editInformation_title: 'Chỉnh sửa thông tin', + editLabel_title: 'Chỉnh sửa nhãn', + editPassword_title: 'Chỉnh sửa mật khẩu', + editPermissions_title: 'Chỉnh sửa quyền hạn', + editRole_title: 'Chỉnh sửa vai trò', + editStopwatch_title: 'Chỉnh sửa đồng hồ bấm giờ', + editType_title: 'Chỉnh sửa loại', + editUsername_title: 'Chỉnh sửa tên người dùng', + editor: 'Trình chỉnh sửa', + editors: 'Những người chỉnh sửa', + email: 'E-mail', + emptyTrash_title: 'Dọn sạch thùng rác', + enterCardTitle: 'Nhập tiêu đề thẻ...', + enterDescription: 'Nhập mô tả...', + enterFilename: 'Nhập tên tệp', + enterListTitle: 'Nhập tiêu đề danh sách...', + enterTaskDescription: 'Nhập mô tả nhiệm vụ...', + events: 'Sự kiện', + excludedEvents: 'Các sự kiện loại trừ', + expandTaskListsByDefault: 'Mặc định mở rộng danh sách nhiệm vụ', + filterByLabels_title: 'Lọc theo nhãn', + filterByMembers_title: 'Lọc theo thành viên', + forPersonalProjects: 'Dành cho các dự án cá nhân.', + forTeamBasedProjects: 'Dành cho các dự án nhóm.', + fromComputer_title: 'Từ máy tính', + fromTrello: 'Từ Trello', + fullKeyIsHiddenForSecurityReasons: + 'Mã đầy đủ bị ẩn vì lý do bảo mật. Hãy tạo lại để có mã mới.', + general: 'Chung', + gradients: 'Màu gradient', + grid: 'Lưới', + hideCompletedTasks: 'Ẩn các nhiệm vụ đã hoàn thành', + hideFromProjectListAndFavorites: 'Ẩn khỏi danh sách dự án và mục yêu thích', + host: 'Máy chủ (Host)', + hours: 'Giờ', + identity: 'Danh tính', + importBoard_title: 'Nhập bảng', + information: 'Thông tin', + invalidCurrentPassword: 'Mật khẩu hiện tại không hợp lệ', + kanban: 'Kanban', + labels: 'Nhãn', + language: 'Ngôn ngữ', + leaveBoard_title: 'Rời khỏi bảng', + leaveProject_title: 'Rời khỏi dự án', + limitCardTypesToDefaultOne: 'Giới hạn các loại thẻ về loại mặc định', + linkToCard: 'Liên kết tới thẻ', + list: 'Danh sách', + listActions_title: 'Thao tác với danh sách', + lists: 'Danh sách', + makeProjectPrivate_title: 'Chuyển dự án sang riêng tư', + makeProjectShared_title: 'Chuyển dự án sang công khai', + managers: 'Quản lý', + memberActions_title: 'Thao tác với thành viên', + members: 'Thành viên', + minutes: 'Phút', + moreActions: 'Thêm thao tác', + moreActions_title: 'Thêm thao tác', + moveCard_title: 'Di chuyển thẻ', + moveList_title: 'Di chuyển danh sách', + myOwn_title: 'Của tôi', + name: 'Tên', + newEmail: 'E-mail mới', + newPassword: 'Mật khẩu mới', + newUsername: 'Tên người dùng mới', + newVersionAvailable: 'Có phiên bản mới', + newestFirst: 'Mới nhất trước', + noApiKeyCreated: 'Chưa có API Key nào được tạo.', + noBoards: 'Không có bảng nào', + noCardsFound: 'Không tìm thấy thẻ nào.', + noConnectionToServer: 'Không có kết nối tới máy chủ', + noLists: 'Không có danh sách nào', + noProjects: 'Không có dự án nào', + noUnreadNotifications: 'Không có thông báo chưa đọc.', + notifications: 'Thông báo', + oldestFirst: 'Cũ nhất trước', + onlyOneManagerShouldRemainToMakeThisProjectPrivate: + 'Chỉ được để lại một quản lý để chuyển dự án này sang riêng tư', + openBoard_title: 'Mở bảng', + optional_inline: 'tùy chọn', + organization: 'Tổ chức', + others: 'Khác', + passwordIsSet: 'Mật khẩu đã được thiết lập', + phone: 'Điện thoại', + plankaUsesAppriseToSendNotificationsToOver100PopularServices: + 'PLANKA sử dụng <1><0>Apprise để gửi thông báo tới hơn 100 dịch vụ phổ biến.', + port: 'Cổng', + preferences: 'Tùy chọn', + pressPasteShortcutToAddAttachmentFromClipboard: + 'Mẹo: nhấn Ctrl-V (Cmd-V trên Mac) để thêm tệp đính kèm từ khay nhớ tạm.', + private: 'Riêng tư', + project: 'Dự án', + projectNotFound_title: 'Không tìm thấy dự án', + projectOwner: 'Chủ sở hữu dự án', + referenceDataAndKnowledgeStorage: 'Lưu trữ dữ liệu tham khảo và kiến thức.', + regenerateApiKey_title: 'Tạo lại API Key', + rejectUnauthorizedTlsCertificates: 'Từ chối chứng chỉ TLS không được xác thực', + removeManager_title: 'Xóa quản lý', + removeMember_title: 'Xóa thành viên', + role: 'Vai trò', + saveThisKeyItWillNotBeShownAgain: 'Lưu mã này — nó sẽ không được hiển thị lại!', + searchCards: 'Tìm kiếm thẻ...', + searchCustomFieldGroups: 'Tìm kiếm nhóm trường tùy chỉnh...', + searchCustomFields: 'Tìm kiếm trường tùy chỉnh...', + searchLabels: 'Tìm kiếm nhãn...', + searchLists: 'Tìm kiếm danh sách...', + searchMembers: 'Tìm kiếm thành viên...', + searchProjects: 'Tìm kiếm dự án...', + searchUsers: 'Tìm kiếm người dùng...', + seconds: 'Giây', + selectAssignee_title: 'Chọn người thực hiện', + selectBoard: 'Chọn bảng', + selectList: 'Chọn danh sách', + selectListToRestoreThisCard: 'Chọn danh sách để khôi phục thẻ này', + selectOrder_title: 'Chọn thứ tự', + selectPermissions_title: 'Chọn quyền hạn', + selectProject: 'Chọn dự án', + selectRole_title: 'Chọn vai trò', + selectType_title: 'Chọn loại', + sequentialDisplayOfCards: 'Hiển thị các thẻ theo trình tự.', + settings: 'Cài đặt', + shared: 'Đã chia sẻ', + sharedWithMe_title: 'Được chia sẻ với tôi', + showOnFrontOfCard: 'Hiển thị trên mặt trước của thẻ', + smtp: 'SMTP', + sortList_title: 'Sắp xếp danh sách', + sourceCardIsNoLongerAvailableForCopying: 'Thẻ nguồn không còn khả dụng để sao chép.', + sourceCardIsNoLongerAvailableForMoving: 'Thẻ nguồn không còn khả dụng để di chuyển.', + stopwatch: 'Đồng hồ bấm giờ', + story: 'Câu chuyện', + subscribeToCardWhenCommenting: 'Đăng ký theo dõi thẻ khi bình luận', + subscribeToMyOwnCardsByDefault: 'Mặc định đăng ký theo dõi các thẻ của tôi', + taskActions_title: 'Thao tác với nhiệm vụ', + taskAssignmentAndProjectCompletion: 'Phân công nhiệm vụ và hoàn thành dự án.', + taskListActions_title: 'Thao tác với danh sách nhiệm vụ', + taskList_title: 'Danh sách nhiệm vụ', + team: 'Nhóm', + termsOfService_title: 'Điều khoản dịch vụ', + testLog_title: 'Test log', + thereIsNoPreviewAvailableForThisAttachment: 'Không có bản xem trước cho tệp đính kèm này.', + time: 'Thời gian', + title: 'Tiêu đề', + trash: 'Thùng rác', + trashHasBeenSuccessfullyEmptied: 'Thùng rác đã được dọn sạch thành công.', + turnOffRecentCardHighlighting: 'Tắt làm nổi bật thẻ gần đây', + typeNameToConfirm: 'Nhập tên để xác nhận.', + typeTitleToConfirm: 'Nhập tiêu đề để xác nhận.', + unsavedChanges: 'Thay đổi chưa được lưu', + uploadFailedFileIsTooBig: 'Tải lên thất bại: Tệp quá lớn.', + uploadFailedNotEnoughStorageSpace: 'Tải lên thất bại: Không đủ không gian lưu trữ.', + uploadedImages: 'Ảnh đã tải lên', + url: 'URL', + useSecureConnection: 'Sử dụng kết nối bảo mật', + userActions_title: 'Thao tác với người dùng', + userAddedCardToList: '<0>{{user}} đã thêm <2>{{card}} vào {{list}}', + userAddedThisCardToList: '<0>{{user}} đã thêm thẻ này vào {{list}}', + userAddedUserToCard: '<0>{{actorUser}} đã thêm {{addedUser}} vào <4>{{card}}', + userAddedUserToThisCard: '<0>{{actorUser}} đã thêm {{addedUser}} vào thẻ này', + userAddedYouToCard: '<0>{{user}} đã thêm bạn vào <2>{{card}}', + userCompletedTaskOnCard: '<0>{{user}} đã hoàn thành {{task}} trên <4>{{card}}', + userCompletedTaskOnThisCard: '<0>{{user}} đã hoàn thành {{task}} trên thẻ này', + userJoinedCard: '<0>{{user}} đã tham gia <2>{{card}}', + userJoinedThisCard: '<0>{{user}} đã tham gia thẻ này', + userLeftCard: '<0>{{user}} đã rời khỏi <2>{{card}}', + userLeftNewCommentToCard: + '<0>{{user}} đã để lại bình luận mới «{{comment}}» tại <2>{{card}}', + userLeftThisCard: '<0>{{user}} đã rời khỏi thẻ này', + userMarkedTaskIncompleteOnCard: + '<0>{{user}} đã đánh dấu {{task}} chưa hoàn thành trên <4>{{card}}', + userMarkedTaskIncompleteOnThisCard: + '<0>{{user}} đã đánh dấu {{task}} chưa hoàn thành trên thẻ này', + userMentionedYouInCommentOnCard: + '<0>{{user}} đã nhắc đến bạn trong một bình luận «{{comment}}» tại <2>{{card}}', + userMovedCardFromListToList: + '<0>{{user}} đã di chuyển <2>{{card}} từ {{fromList}} sang {{toList}}', + userMovedThisCardFromListToList: + '<0>{{user}} đã di chuyển thẻ này từ {{fromList}} sang {{toList}}', + userRemovedUserFromCard: '<0>{{actorUser}} đã xóa {{removedUser}} khỏi <4>{{card}}', + userRemovedUserFromThisCard: '<0>{{actorUser}} đã xóa {{removedUser}} khỏi thẻ này', + username: 'Tên đăng nhập', + users: 'Người dùng', + viewer: 'Người xem', + viewers: 'Người xem', + visualTaskManagementWithLists: 'Quản lý nhiệm vụ trực quan bằng danh sách.', + webhooks: 'Webhooks', + whatsNew_title: 'Có gì mới', + withoutBaseGroup: 'Không kèm nhóm cơ sở', + writeComment: 'Viết bình luận...', + }, + + action: { + activateUser: 'Kích hoạt người dùng', + activateUser_title: 'Kích hoạt Người dùng', + addAnotherCard: 'Thêm thẻ khác', + addAnotherList: 'Thêm danh sách khác', + addAnotherTask: 'Thêm nhiệm vụ khác', + addCard: 'Thêm thẻ', + addCard_title: 'Thêm Thẻ', + addComment: 'Thêm bình luận', + addCustomField: 'Thêm trường tùy chỉnh', + addCustomFieldGroup: 'Thêm nhóm trường tùy chỉnh', + addList: 'Thêm danh sách', + addMember: 'Thêm thành viên', + addMoreDetailedDescription: 'Thêm mô tả chi tiết hơn', + addTask: 'Thêm nhiệm vụ', + addTaskList: 'Thêm danh sách nhiệm vụ', + addToCard: 'Thêm vào thẻ', + addUser: 'Thêm người dùng', + addWebhook: 'Thêm webhook', + archive: 'Lưu trữ', + archiveCard: 'Lưu trữ thẻ', + archiveCard_title: 'Lưu trữ Thẻ', + archiveCards: 'Lưu trữ các thẻ', + archiveCards_title: 'Lưu trữ Các thẻ', + assignAsOwner: 'Chỉ định làm chủ sở hữu', + cancel: 'Hủy', + copy: 'Sao chép', + copyCard_title: 'Sao chép Thẻ', + createApiKey: 'Tạo API Key', + createBoard: 'Tạo bảng', + createCustomFieldGroup: 'Tạo nhóm trường tùy chỉnh', + createFile: 'Tạo tệp', + createLabel: 'Tạo nhãn', + createNewLabel: 'Tạo nhãn mới', + createProject: 'Tạo dự án', + cut: 'Cắt', + cutCard_title: 'Cắt Thẻ', + deactivateUser: 'Hủy kích hoạt người dùng', + deactivateUser_title: 'Hủy kích hoạt Người dùng', + delete: 'Xóa', + deleteApiKey: 'Xóa API Key', + deleteAttachment: 'Xóa tệp đính kèm', + deleteAvatar: 'Xóa ảnh đại diện', + deleteBackgroundImage: 'Xóa ảnh nền', + deleteBoard: 'Xóa bảng', + deleteBoard_title: 'Xóa Bảng', + deleteCard: 'Xóa thẻ', + deleteCardForever: 'Xóa thẻ vĩnh viễn', + deleteCard_title: 'Xóa Thẻ', + deleteComment: 'Xóa bình luận', + deleteCustomField: 'Xóa trường tùy chỉnh', + deleteCustomFieldGroup: 'Xóa nhóm trường tùy chỉnh', + deleteForever_title: 'Xóa Vĩnh viễn', + deleteGroup: 'Xóa nhóm', + deleteLabel: 'Xóa nhãn', + deleteList: 'Xóa danh sách', + deleteList_title: 'Xóa Danh sách', + deleteNotificationService: 'Xóa dịch vụ thông báo', + deleteProject: 'Xóa dự án', + deleteProject_title: 'Xóa Dự án', + deleteTask: 'Xóa nhiệm vụ', + deleteTaskList: 'Xóa danh sách nhiệm vụ', + deleteTask_title: 'Xóa Nhiệm vụ', + deleteUser: 'Xóa người dùng', + deleteUser_title: 'Xóa Người dùng', + deleteWebhook: 'Xóa webhook', + dismissAll: 'Bỏ qua tất cả', + download: 'Tải xuống', + duplicateCard_title: 'Nhân bản Thẻ', + edit: 'Chỉnh sửa', + editColor_title: 'Chỉnh sửa Màu sắc', + editDescription_title: 'Chỉnh sửa Mô tả', + editDueDate_title: 'Chỉnh sửa Hạn chót', + editEmail_title: 'Chỉnh sửa E-mail', + editGroup: 'Chỉnh sửa nhóm', + editInformation_title: 'Chỉnh sửa Thông tin', + editPassword_title: 'Chỉnh sửa Mật khẩu', + editPermissions: 'Chỉnh sửa quyền hạn', + editRole_title: 'Chỉnh sửa Vai trò', + editStopwatch_title: 'Chỉnh sửa Đồng hồ bấm giờ', + editTitle_title: 'Chỉnh sửa Tiêu đề', + editType_title: 'Chỉnh sửa Loại', + editUsername_title: 'Chỉnh sửa Tên người dùng', + emptyTrash: 'Dọn sạch thùng rác', + emptyTrash_title: 'Dọn sạch Thùng rác', + import: 'Nhập', + join: 'Tham gia', + leave: 'Rời khỏi', + leaveBoard: 'Rời khỏi bảng', + leaveProject: 'Rời khỏi dự án', + logOut_title: 'Đăng xuất', + makeCover_title: 'Đặt làm ảnh bìa', + makeProjectPrivate: 'Chuyển dự án sang riêng tư', + makeProjectPrivate_title: 'Chuyển Dự án sang Riêng tư', + makeProjectShared: 'Chuyển dự án sang công khai', + makeProjectShared_title: 'Chuyển Dự án sang Công khai', + move: 'Di chuyển', + moveCard_title: 'Di chuyển Thẻ', + moveList_title: 'Di chuyển Danh sách', + regenerateApiKey: 'Tạo lại API Key', + remove: 'Gỡ bỏ', + removeAssignee: 'Gỡ bỏ người thực hiện', + removeColor: 'Gỡ bỏ màu sắc', + removeCover_title: 'Gỡ bỏ ảnh bìa', + removeFromBoard: 'Gỡ bỏ khỏi bảng', + removeFromProject: 'Gỡ bỏ khỏi dự án', + removeManager: 'Gỡ bỏ quản lý', + removeMember: 'Gỡ bỏ thành viên', + restoreToList: 'Khôi phục về {{list}}', + returnToBoard: 'Quay lại bảng', + save: 'Lưu', + sendTestEmail: 'Gửi thử e-mail', + showActive: 'Hiển thị đang hoạt động', + showAllAttachments: 'Hiển thị tất cả tệp đính kèm ({{hidden}} bị ẩn)', + showCardsWithThisUser: 'Hiển thị các thẻ của người dùng này', + showDeactivated: 'Hiển thị đã hủy kích hoạt', + showFewerAttachments: 'Hiển thị ít tệp đính kèm hơn', + showLess: 'Ẩn bớt', + showMore: 'Xem thêm', + sortList_title: 'Sắp xếp Danh sách', + start: 'Bắt đầu', + stop: 'Dừng', + subscribe: 'Đăng ký theo dõi', + unsubscribe: 'Hủy đăng ký theo dõi', + uploadNewAvatar: 'Tải lên ảnh đại diện mới', + uploadNewImage: 'Tải lên ảnh mới', + }, + }, +}; diff --git a/client/src/locales/vi-VN/index.js b/client/src/locales/vi-VN/index.js new file mode 100644 index 00000000..a4d67526 --- /dev/null +++ b/client/src/locales/vi-VN/index.js @@ -0,0 +1,8 @@ +import login from './login'; + +export default { + language: 'vi-VN', + country: 'vn', + name: 'Tiếng Việt', + embeddedLocale: login, +}; diff --git a/client/src/locales/vi-VN/login.js b/client/src/locales/vi-VN/login.js new file mode 100644 index 00000000..e4de4afc --- /dev/null +++ b/client/src/locales/vi-VN/login.js @@ -0,0 +1,34 @@ +export default { + translation: { + common: { + activeUsersLimitReached: 'Đã đạt giới hạn người dùng hoạt động', + adminLoginRequiredToInitializeInstance: 'Cần đăng nhập quản trị để khởi tạo phiên bản', + emailAlreadyInUse: 'E-mail đã được sử dụng', + emailOrUsername: 'E-mail hoặc tên đăng nhập', + iHaveReadAndAgreeToTheseTerms: 'Tôi đã đọc và đồng ý với các điều khoản này', + invalidCredentials: 'Thông tin đăng nhập không hợp lệ', + invalidEmailOrUsername: 'E-mail hoặc tên đăng nhập không hợp lệ', + invalidPassword: 'Mật khẩu không hợp lệ', + logIn_title: 'Đăng nhập', + noInternetConnection: 'Không có kết nối Internet', + or: 'Hoặc', + pageNotFound_title: 'Không tìm thấy trang', + password: 'Mật khẩu', + poweredByPlanka: 'Được phát triển bởi <1>PLANKA', + serverConnectionFailed: 'Không kết nối được tới máy chủ', + unknownError: 'Lỗi không xác định, thử lại sau', + useSingleSignOn: 'Sử dụng đăng nhập một lần (SSO)', + usernameAlreadyInUse: 'Tên đăng nhập đã được sử dụng', + whoops_title: 'Ôi thôi chếcccc!', + }, + + action: { + cancelAndClose: 'Hủy và đóng', + continue: 'Tiếp tục', + goBack: 'Quay lại', + goHome: 'Về trang chủ', + logIn: 'Đăng nhập', + logInWithSso: 'Đăng nhập bằng SSO', + }, + }, +}; diff --git a/client/src/locales/vi-VN/markdown-editor.json b/client/src/locales/vi-VN/markdown-editor.json new file mode 100644 index 00000000..6072201a --- /dev/null +++ b/client/src/locales/vi-VN/markdown-editor.json @@ -0,0 +1,169 @@ +{ + "action-previews": { + "text": "Đây là văn bản không có tiêu đề.\nCả tiêu đề và nội dung\ncó thể được in đậm, in nghiêng, đổi màu,\ngạch ngang và gạch dưới.", + "text-with-head": "Đây là văn bản có tiêu đề.\nCả tiêu đề và nội dung\ncó thể được in đậm, in nghiêng, đổi màu,\ngạch ngang và gạch dưới.", + "heading": "Tiêu đề" + }, + "bundle": { + "error-title": "Lỗi trong trình soạn thảo markdown", + "settings_wysiwyg": "Trình soạn thảo trực quan (WYSIWYG)", + "settings_markup": "Markdown", + "markup_placeholder": "Nhập markdown..." + }, + "codeblock": { + "remove": "Xóa", + "empty_option": "Không tìm thấy kết quả", + "show_line_numbers": "Số dòng" + }, + "common": { + "delete": "Xóa", + "edit": "Chỉnh sửa", + "toolbar_action_disabled": "Phần tử đánh dấu không tương thích" + }, + "forms": { + "common_action_cancel": "Hủy", + "common_action_submit": "Gửi", + "common_action_upload": "Chọn", + "common_tab_attach": "Thêm từ thiết bị", + "common_tab_link": "Thêm theo liên kết", + "common_link": "Liên kết", + "common_sizes": "Kích thước, px", + "image_name": "Tiêu đề", + "image_link_href": "Liên kết ảnh", + "image_link_href_help": "Địa chỉ mà liên kết ảnh dẫn tới.", + "image_alt": "Văn bản thay thế (alt)", + "image_alt_help": "Văn bản thay thế hiển thị khi ảnh không thể tải.", + "image_upload_help": "Ảnh JPEG, GIF hoặc PNG không lớn hơn 1 MB.", + "image_upload_failed": "Thêm ảnh thất bại", + "image_size_width": "Chiều rộng", + "image_size_height": "Chiều cao", + "link_url_help": "Địa chỉ mà liên kết dẫn tới.", + "link_text": "Văn bản liên kết", + "link_text_help": "Văn bản hiển thị dưới dạng liên kết.", + "link_open_help": "Mở liên kết trong tab mới" + }, + "md-hints": { + "header_title": "Tiêu đề", + "header_hint": "# Văn bản của bạn", + "italic_title": "In nghiêng", + "italic_hint": "_Văn bản của bạn_", + "bold_title": "In đậm", + "bold_hint": "**Văn bản của bạn**", + "strikethrough_title": "Gạch ngang", + "strikethrough_hint": "~~Văn bản của bạn~~", + "blockquote_title": "Trích dẫn", + "blockquote_hint": "> Văn bản của bạn", + "code_title": "Mã nguồn", + "code_hint": "```Văn bản của bạn```", + "link_title": "Liên kết", + "link_hint": "[Văn bản của bạn](url)", + "image_title": "Ảnh", + "image_hint": "![Văn bản của bạn](url)", + "list_title": "Mục danh sách", + "list_hint": "- Văn bản của bạn", + "numbered-list_title": "Danh sách đánh số", + "numbered-list_hint": "1. Văn bản của bạn", + "documentation": "Tài liệu", + "documentation_link": "https://diplodoc.com/docs/en/syntax/" + }, + "menubar": { + "bold": "In đậm", + "code": "Mã nguồn", + "code_inline": "Mã nội dòng", + "codeblock": "Khối mã", + "colorify": "Màu chữ", + "colorify__color_blue": "Xanh dương", + "colorify__color_default": "Mặc định", + "colorify__color_gray": "Xám", + "colorify__color_green": "Xanh lá", + "colorify__color_orange": "Cam", + "colorify__color_red": "Đỏ", + "colorify__color_violet": "Tím", + "colorify__color_yellow": "Vàng", + "colorify__group_text": "Văn bản", + "cut": "Cắt", + "emoji": "Biểu tượng cảm xúc", + "emoji__hint": "Biểu tượng có thể thêm ở WYSIWYG hoặc bằng markup", + "heading": "Tiêu đề", + "heading1": "Tiêu đề 1", + "heading2": "Tiêu đề 2", + "heading3": "Tiêu đề 3", + "heading4": "Tiêu đề 4", + "heading5": "Tiêu đề 5", + "heading6": "Tiêu đề 6", + "hrule": "Ngăn cách", + "image": "Ảnh", + "italic": "In nghiêng", + "link": "Liên kết", + "list": "Danh sách", + "list__action_lift": "Nâng mục", + "list__action_sink": "Hạ mục", + "list_action_disabled": "Mâu thuẫn với logic danh sách", + "mark": "Đánh dấu", + "mono": "Chữ monospace", + "more_action": "Thêm hành động", + "note": "Ghi chú", + "olist": "Danh sách đánh số", + "quote": "Trích dẫn", + "redo": "Làm lại", + "strike": "Gạch ngang", + "table": "Bảng", + "text": "Văn bản", + "ulist": "Danh sách gạch đầu dòng", + "underline": "Gạch dưới", + "undo": "Hoàn tác" + }, + "placeholder": { + "doc_empty": "Gõ / để dùng lệnh nhanh...", + "checkbox": "Nhập mô tả nhiệm vụ...", + "deflist_term": "Thuật ngữ", + "deflist_desc": "Mô tả định nghĩa", + "heading": "Tiêu đề", + "cut_title": "Tiêu đề", + "cut_content": "Nội dung hiển thị khi nhấp", + "note_title": "Tiêu đề", + "note_content": "Nội dung ghi chú", + "table_cell": "Nội dung ô", + "select_filter": "Tìm ngôn ngữ..." + }, + "search": { + "label_case-sensitive": "Phân biệt chữ hoa/thường", + "label_whole-word": "Nguyên từ", + "title": "Tìm trong mã" + }, + "suggest": { + "empty-msg": "Không tìm thấy" + }, + "widgets": { + "image": "Thêm ảnh", + "link": "Thêm liên kết" + }, + "yfm-note": { + "info": "Ghi chú", + "tip": "Mẹo", + "warning": "Cảnh báo", + "alert": "Thông báo", + "remove": "Xóa" + }, + "yfm-table": { + "column.add.before": "Thêm cột trước", + "column.add.after": "Thêm cột sau", + "column.remove": "Xóa cột", + "column.remove.multiple": "Xóa các cột", + "row.add.before": "Thêm hàng trước", + "row.add.after": "Thêm hàng sau", + "row.remove": "Xóa hàng", + "row.remove.multiple": "Xóa các hàng", + "cells.clear": "Xóa ô", + "table.remove": "Xóa bảng", + "table.menu.cell.align.left": "Căn trái nội dung ô", + "table.menu.cell.align.right": "Căn phải nội dung ô", + "table.menu.cell.align.center": "Căn giữa nội dung ô", + "table.menu.row.add": "Thêm hàng sau", + "table.menu.row.remove": "Xóa hàng", + "table.menu.column.add": "Thêm cột sau", + "table.menu.column.remove": "Xóa cột", + "table.menu.convert.yfm": "Chuyển sang bảng YFM", + "table.menu.table.remove": "Xóa bảng" + } +} diff --git a/server/api/controllers/access-tokens/accept-terms.js b/server/api/controllers/access-tokens/accept-terms.js index 9e21a9f0..b8bf10f6 100644 --- a/server/api/controllers/access-tokens/accept-terms.js +++ b/server/api/controllers/access-tokens/accept-terms.js @@ -35,7 +35,7 @@ * example: 940226c4c41f51afe3980ceb63704e752636526f4c52a4ea579e85b247493d94 * initialLanguage: * type: string - * enum: [ar-YE, bg-BG, ca-ES, cs-CZ, da-DK, de-DE, el-GR, en-GB, en-US, es-ES, et-EE, fa-IR, fi-FI, fr-FR, hu-HU, id-ID, it-IT, ja-JP, ko-KR, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, ru-RU, sk-SK, sr-Cyrl-RS, sr-Latn-RS, sv-SE, tr-TR, uk-UA, uz-UZ, zh-CN, zh-TW] + * enum: [ar-YE, bg-BG, ca-ES, cs-CZ, da-DK, de-DE, el-GR, en-GB, en-US, es-ES, et-EE, fa-IR, fi-FI, fr-FR, hu-HU, id-ID, it-IT, ja-JP, ko-KR, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, ru-RU, sk-SK, sr-Cyrl-RS, sr-Latn-RS, sv-SE, tr-TR, uk-UA, uz-UZ, vi-VN, zh-CN, zh-TW] * nullable: true * description: Preferred language for user interface and notifications (used only if user language is not set) * example: en-US diff --git a/server/api/controllers/users/create.js b/server/api/controllers/users/create.js index 2382b79a..747ae2c3 100755 --- a/server/api/controllers/users/create.js +++ b/server/api/controllers/users/create.js @@ -67,7 +67,7 @@ * example: Acme Corporation * language: * type: string - * enum: [ar-YE, bg-BG, ca-ES, cs-CZ, da-DK, de-DE, el-GR, en-GB, en-US, es-ES, et-EE, fa-IR, fi-FI, fr-FR, hu-HU, id-ID, it-IT, ja-JP, ko-KR, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, ru-RU, sk-SK, sr-Cyrl-RS, sr-Latn-RS, sv-SE, tr-TR, uk-UA, uz-UZ, zh-CN, zh-TW] + * enum: [ar-YE, bg-BG, ca-ES, cs-CZ, da-DK, de-DE, el-GR, en-GB, en-US, es-ES, et-EE, fa-IR, fi-FI, fr-FR, hu-HU, id-ID, it-IT, ja-JP, ko-KR, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, ru-RU, sk-SK, sr-Cyrl-RS, sr-Latn-RS, sv-SE, tr-TR, uk-UA, uz-UZ, vi-VN, zh-CN, zh-TW] * nullable: true * description: Preferred language for user interface and notifications (if null - will be set automatically on the first login) * example: en-US diff --git a/server/api/controllers/users/update.js b/server/api/controllers/users/update.js index 19cebcb4..415f9c1b 100755 --- a/server/api/controllers/users/update.js +++ b/server/api/controllers/users/update.js @@ -56,7 +56,7 @@ * example: Acme Corporation * language: * type: string - * enum: [ar-YE, bg-BG, ca-ES, cs-CZ, da-DK, de-DE, el-GR, en-GB, en-US, es-ES, et-EE, fa-IR, fi-FI, fr-FR, hu-HU, id-ID, it-IT, ja-JP, ko-KR, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, ru-RU, sk-SK, sr-Cyrl-RS, sr-Latn-RS, sv-SE, tr-TR, uk-UA, uz-UZ, zh-CN, zh-TW] + * enum: [ar-YE, bg-BG, ca-ES, cs-CZ, da-DK, de-DE, el-GR, en-GB, en-US, es-ES, et-EE, fa-IR, fi-FI, fr-FR, hu-HU, id-ID, it-IT, ja-JP, ko-KR, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, ru-RU, sk-SK, sr-Cyrl-RS, sr-Latn-RS, sv-SE, tr-TR, uk-UA, uz-UZ, vi-VN, zh-CN, zh-TW] * description: Preferred language for user interface and notifications * example: en-US * apiKey: diff --git a/server/api/models/User.js b/server/api/models/User.js index 818235b0..df949494 100755 --- a/server/api/models/User.js +++ b/server/api/models/User.js @@ -96,7 +96,7 @@ * example: Acme Corporation * language: * type: string - * enum: [ar-YE, bg-BG, ca-ES, cs-CZ, da-DK, de-DE, el-GR, en-GB, en-US, es-ES, et-EE, fa-IR, fi-FI, fr-FR, hu-HU, id-ID, it-IT, ja-JP, ko-KR, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, ru-RU, sk-SK, sr-Cyrl-RS, sr-Latn-RS, sv-SE, tr-TR, uk-UA, uz-UZ, zh-CN, zh-TW] + * enum: [ar-YE, bg-BG, ca-ES, cs-CZ, da-DK, de-DE, el-GR, en-GB, en-US, es-ES, et-EE, fa-IR, fi-FI, fr-FR, hu-HU, id-ID, it-IT, ja-JP, ko-KR, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, ru-RU, sk-SK, sr-Cyrl-RS, sr-Latn-RS, sv-SE, tr-TR, uk-UA, uz-UZ, vi-VN, zh-CN, zh-TW] * nullable: true * description: Preferred language for user interface and notifications (personal field) * example: en-US @@ -237,6 +237,7 @@ const LANGUAGES = [ 'tr-TR', 'uk-UA', 'uz-UZ', + 'vi-VN', 'zh-CN', 'zh-TW', ]; diff --git a/server/config/i18n.js b/server/config/i18n.js index 6eede354..8efcff5b 100644 --- a/server/config/i18n.js +++ b/server/config/i18n.js @@ -52,6 +52,7 @@ module.exports.i18n = { 'tr-TR', 'uk-UA', 'uz-UZ', + 'vi-VN', 'zh-CN', 'zh-TW', ], diff --git a/server/config/locales/vi-VN.json b/server/config/locales/vi-VN.json new file mode 100644 index 00000000..e68bb900 --- /dev/null +++ b/server/config/locales/vi-VN.json @@ -0,0 +1,19 @@ +{ + "Archive": "Lưu trữ", + "Card Created": "Thẻ đã được tạo", + "Card Moved": "Thẻ đã được di chuyển", + "copy": "bản sao", + "New Comment": "Bình luận mới", + "Test Title": "Tiêu đề thử nghiệm", + "This is a test text message!": "Đây là tin nhắn văn bản thử nghiệm!", + "This is a *test* **markdown** `message`!": "Đây là *tin nhắn* **markdown** `thử nghiệm`!", + "This is a test html message!": "Đây là tin nhắn html thử nghiệm!", + "Trash": "Thùng rác", + "You Were Added to Card": "Bạn đã được thêm vào thẻ", + "You Were Mentioned in Comment": "Bạn đã được nhắc đến trong bình luận", + "%s added you to %s on %s": "%s đã thêm bạn vào %s vào %s", + "%s created %s in %s on %s": "%s đã tạo %s trong %s vào %s", + "%s left a new comment to %s on %s": "%s đã để lại bình luận mới cho %s vào %s", + "%s mentioned you in %s on %s": "%s đã nhắc đến bạn trong %s vào %s", + "%s moved %s from %s to %s on %s": "%s đã di chuyển %s từ %s đến %s vào %s" +}