Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/vi-vn-translations.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/localizations': patch
---

Add missing Vietnamese (Vietnam) (`vi-VN`) translations. Fills in previously untranslated keys across billing, checkout, subscription details, organization switcher, enterprise connections, password reset and two-step verification (MFA) setup tasks, frontend API errors, and the user button menu.
204 changes: 109 additions & 95 deletions packages/localizations/src/vi-VN.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,16 +60,16 @@ export const viVN: LocalizationResource = {
badge__default: 'Mặc định',
badge__endsAt: "Kết thúc {{ date | shortDate('vi-VN') }}",
badge__expired: 'Đã hết hạn',
badge__freeTrial: undefined,
badge__freeTrial: 'Dùng thử miễn phí',
badge__otherImpersonatorDevice: 'Thiết bị giả mạo khác',
badge__pastDueAt: undefined,
badge__pastDuePlan: undefined,
badge__pastDueAt: "Quá hạn {{ date | shortDate('vi-VN') }}",
badge__pastDuePlan: 'Quá hạn',
badge__primary: 'Chính',
badge__renewsAt: "Gia hạn {{ date | shortDate('vi-VN') }}",
badge__requiresAction: 'Yêu cầu hành động',
badge__startsAt: "Bắt đầu {{ date | shortDate('vi-VN') }}",
badge__thisDevice: 'Thiết bị này',
badge__trialEndsAt: undefined,
badge__trialEndsAt: "Kết thúc dùng thử {{ date | shortDate('vi-VN') }}",
badge__unverified: 'Chưa xác minh',
badge__upcomingPlan: 'Sắp tới',
badge__userDevice: 'Thiết bị người dùng',
Expand All@@ -81,18 +81,20 @@ export const viVN: LocalizationResource = {
availableFeatures: 'Tính năng có sẵn',
billedAnnually: 'Tính phí hàng năm',
billedMonthlyOnly: 'Chỉ tính phí hàng tháng',
cancelFreeTrial: undefined,
cancelFreeTrialAccessUntil: undefined,
cancelFreeTrialTitle: undefined,
cancelFreeTrial: 'Hủy dùng thử miễn phí',
cancelFreeTrialAccessUntil:
"Bản dùng thử của bạn sẽ vẫn hoạt động cho đến {{ date | longDate('vi-VN') }}. Sau đó, bạn sẽ mất quyền truy cập vào các tính năng dùng thử. Bạn sẽ không bị tính phí.",
cancelFreeTrialTitle: 'Hủy dùng thử miễn phí cho gói {{plan}}?',
cancelSubscription: 'Hủy đăng ký',
cancelSubscriptionAccessUntil:
"Bạn có thể tiếp tục sử dụng tính năng '{{plan}}' cho đến {{ date | longDate('vi-VN') }}, sau đó bạn sẽ không còn quyền truy cập.",
cancelSubscriptionNoCharge: 'Bạn sẽ không bị tính phí cho đăng ký này.',
cancelSubscriptionPastDue: undefined,
cancelSubscriptionPastDue:
'Gói đăng ký của bạn sẽ kết thúc ngay lập tức và bạn sẽ mất quyền truy cập vào tất cả các tính năng của gói. Bạn sẽ được yêu cầu thanh toán số tiền quá hạn trong lần đăng ký tiếp theo.',
cancelSubscriptionTitle: 'Hủy đăng ký {{plan}}?',
cannotSubscribeMonthly:
'Bạn không thể đăng ký gói này bằng cách thanh toán hàng tháng. Để đăng ký gói này, bạn cần chọn thanh toán hàng năm.',
cannotSubscribeUnrecoverable: undefined,
cannotSubscribeUnrecoverable: 'Bạn không thể đăng ký gói này. Gói đăng ký hiện tại của bạn đắt hơn gói này.',
checkout: {
description__paymentSuccessful: 'Thanh toán của bạn đã thành công.',
description__subscriptionSuccessful: 'Đăng ký mới của bạn đã được thiết lập.',
Expand All@@ -103,7 +105,7 @@ export const viVN: LocalizationResource = {
title: 'Thêm địa chỉ email',
},
lineItems: {
title__freeTrialEndsAt: undefined,
title__freeTrialEndsAt: 'Dùng thử kết thúc vào',
title__paymentMethod: 'Phương thức thanh toán',
title__statementId: 'ID hóa đơn',
title__subscriptionBegins: 'Đăng ký bắt đầu',
Expand All@@ -114,15 +116,15 @@ export const viVN: LocalizationResource = {
title: 'Thanh toán',
title__paymentSuccessful: 'Thanh toán thành công!',
title__subscriptionSuccessful: 'Thành công!',
title__trialSuccess: undefined,
totalDueAfterTrial: undefined,
title__trialSuccess: 'Đã bắt đầu dùng thử thành công!',
totalDueAfterTrial: 'Tổng số tiền phải trả sau khi dùng thử kết thúc trong {{days}} ngày',
},
credit: 'Tín dụng',
creditRemainder: 'Tín dụng cho phần còn lại của đăng ký hiện tại.',
defaultFreePlanActive: 'Bạn hiện đang trên gói Miễn phí',
free: 'Miễn phí',
getStarted: 'Bắt đầu',
keepFreeTrial: undefined,
keepFreeTrial: 'Giữ bản dùng thử miễn phí',
keepSubscription: 'Giữ đăng ký',
manage: 'Quản lý',
manageSubscription: 'Quản lý đăng ký',
Expand DownExpand Up@@ -152,34 +154,34 @@ export const viVN: LocalizationResource = {
},
reSubscribe: 'Đăng ký lại',
seeAllFeatures: 'Xem tất cả tính năng',
startFreeTrial: undefined,
startFreeTrial__days: undefined,
startFreeTrial: 'Bắt đầu dùng thử miễn phí',
startFreeTrial__days: 'Bắt đầu dùng thử miễn phí {{days}} ngày',
subscribe: 'Đăng ký',
subscriptionDetails: {
beginsOn: undefined,
currentBillingCycle: undefined,
endsOn: undefined,
firstPaymentAmount: undefined,
firstPaymentOn: undefined,
nextPaymentAmount: undefined,
nextPaymentOn: undefined,
pastDueAt: undefined,
renewsAt: undefined,
subscribedOn: undefined,
title: undefined,
trialEndsOn: undefined,
trialStartedOn: undefined,
beginsOn: 'Bắt đầu vào',
currentBillingCycle: 'Chu kỳ thanh toán hiện tại',
endsOn: 'Kết thúc vào',
firstPaymentAmount: 'Số tiền thanh toán đầu tiên',
firstPaymentOn: 'Thanh toán đầu tiên vào',
nextPaymentAmount: 'Số tiền thanh toán tiếp theo',
nextPaymentOn: 'Thanh toán tiếp theo vào',
pastDueAt: 'Quá hạn vào',
renewsAt: 'Gia hạn vào',
subscribedOn: 'Đăng ký vào',
title: 'Gói đăng ký',
trialEndsOn: 'Dùng thử kết thúc vào',
trialStartedOn: 'Dùng thử bắt đầu vào',
},
subtotal: 'Tổng cộng',
switchPlan: 'Chuyển sang gói này',
switchToAnnual: 'Chuyển sang hàng năm',
switchToAnnualWithAnnualPrice: undefined,
switchToAnnualWithAnnualPrice: 'Chuyển sang gói năm {{currency}}{{price}} / năm',
switchToMonthly: 'Chuyển sang hàng tháng',
switchToMonthlyWithPrice: undefined,
switchToMonthlyWithPrice: 'Chuyển sang gói tháng {{currency}}{{price}} / tháng',
totalDue: 'Tổng cần thanh toán',
totalDueToday: 'Tổng cần thanh toán hôm nay',
viewFeatures: 'Xem tính năng',
viewPayment: undefined,
viewPayment: 'Xem thanh toán',
year: 'Năm',
},
configureSSO: {
Expand DownExpand Up@@ -271,8 +273,8 @@ export const viVN: LocalizationResource = {
formFieldInputPlaceholder__organizationSlug: 'my-org',
formFieldInputPlaceholder__password: 'Nhập mật khẩu của bạn',
formFieldInputPlaceholder__phoneNumber: 'Nhập số điện thoại của bạn',
formFieldInputPlaceholder__username: undefined,
formFieldInput__emailAddress_format: undefined,
formFieldInputPlaceholder__username: 'Nhập tên người dùng của bạn',
formFieldInput__emailAddress_format: 'Định dạng ví dụ: name@example.com',
formFieldLabel__apiKey: 'Khóa API',
formFieldLabel__apiKeyDescription: 'Mô tả',
formFieldLabel__apiKeyExpiration: 'Hết hạn',
Expand DownExpand Up@@ -373,7 +375,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand DownExpand Up@@ -549,11 +551,11 @@ export const viVN: LocalizationResource = {
},
},
organizationSwitcher: {
action__closeOrganizationSwitcher: undefined,
action__closeOrganizationSwitcher: 'Đóng menu chuyển đổi tổ chức',
action__createOrganization: 'Tạo tổ chức',
action__invitationAccept: 'Tham gia',
action__manageOrganization: 'Quản lý',
action__openOrganizationSwitcher: undefined,
action__openOrganizationSwitcher: 'Mở menu chuyển đổi tổ chức',
action__suggestionsAccept: 'Yêu cầu tham gia',
notSelected: 'Không có tổ chức được chọn',
personalWorkspace: 'Tài khoản cá nhân',
Expand DownExpand Up@@ -716,8 +718,8 @@ export const viVN: LocalizationResource = {
title: 'Kiểm tra email của bạn',
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
forgotPassword: {
formTitle: 'Mã xác minh mật khẩu',
Expand DownExpand Up@@ -750,13 +752,13 @@ export const viVN: LocalizationResource = {
title: 'Nhập mật khẩu',
},
passwordCompromised: {
title: undefined,
title: 'Mật khẩu đã bị xâm phạm',
},
passwordPwned: {
title: 'Mật khẩu bị rò rỉ',
},
passwordUntrusted: {
title: undefined,
title: 'Mật khẩu không đáng tin cậy',
},
phoneCode: {
formTitle: 'Mã xác minh',
Expand DownExpand Up@@ -853,8 +855,8 @@ export const viVN: LocalizationResource = {
},
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
legalConsent: {
checkbox: {
Expand DownExpand Up@@ -942,74 +944,82 @@ export const viVN: LocalizationResource = {
},
},
taskResetPassword: {
formButtonPrimary: undefined,
formButtonPrimary: 'Đặt lại mật khẩu',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
subtitle: undefined,
title: undefined,
subtitle: 'Tài khoản của bạn yêu cầu mật khẩu mới trước khi bạn có thể tiếp tục',
title: 'Đặt lại mật khẩu của bạn',
},
taskSetupMfa: {
badge: undefined,
badge: 'Thiết lập xác minh hai bước',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
smsCode: {
addPhone: {
formButtonPrimary: undefined,
infoText: undefined,
formButtonPrimary: 'Tiếp tục',
infoText:
'Một tin nhắn văn bản chứa mã xác minh sẽ được gửi đến số điện thoại này. Có thể áp dụng cước phí tin nhắn và dữ liệu.',
},
addPhoneNumber: undefined,
cancel: undefined,
subtitle: undefined,
addPhoneNumber: 'Thêm số điện thoại',
cancel: 'Hủy',
subtitle: 'Chọn số điện thoại bạn muốn sử dụng để xác minh hai bước bằng mã SMS',
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
},
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh được gửi đến số điện thoại này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng mã SMS',
},
title: 'Thêm xác minh bằng mã SMS',
verifyPhone: {
formButtonPrimary: undefined,
formTitle: undefined,
resendButton: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formTitle: 'Mã xác minh',
resendButton: 'Không nhận được mã? Gửi lại',
subtitle: 'Nhập mã xác minh được gửi đến',
title: 'Xác minh số điện thoại của bạn',
},
},
start: {
methodSelection: {
phoneCode: undefined,
totp: undefined,
phoneCode: 'Mã SMS',
totp: 'Ứng dụng xác thực',
},
subtitle: undefined,
title: undefined,
subtitle: 'Chọn phương thức bạn muốn sử dụng để bảo vệ tài khoản của mình bằng một lớp bảo mật bổ sung',
title: 'Thiết lập xác minh hai bước',
},
totpCode: {
addAuthenticatorApp: {
buttonAbleToScan__nonPrimary: undefined,
buttonUnableToScan__nonPrimary: undefined,
formButtonPrimary: undefined,
formButtonReset: undefined,
infoText__ableToScan: undefined,
infoText__unableToScan: undefined,
inputLabel__unableToScan1: undefined,
buttonAbleToScan__nonPrimary: 'Thay vào đó, quét mã QR',
buttonUnableToScan__nonPrimary: 'Không thể quét mã QR?',
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
infoText__ableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và quét mã QR sau đây để liên kết nó với tài khoản của bạn.',
infoText__unableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và nhập Khóa được cung cấp bên dưới.',
inputLabel__unableToScan1:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads quite literally in Vietnamese. “Mật khẩu dựa trên thời gian hoặc Mật khẩu một lần” sounds machine-translated and “mật khẩu” is awkward here; “mã” would be
more natural for TOTP/OTP. Can you rephrase this to something more idiomatic and align it with the wording already used elsewhere in the locale?

Can you confirm this review by Codex if it's legit?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — in Vietnamese tech UI, TOTP/OTP are conventionally rendered as "mã" (code), not "mật khẩu" (password). Updating to:

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.'

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.',
},
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh từ ứng dụng xác thực này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng ứng dụng xác thực',
},
title: undefined,
title: 'Thêm ứng dụng xác thực',
verifyTotp: {
formButtonPrimary: undefined,
formButtonReset: undefined,
formTitle: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
formTitle: 'Mã xác minh',
subtitle: 'Nhập mã xác minh được tạo bởi ứng dụng xác thực của bạn',
title: 'Thêm ứng dụng xác thực',
},
},
},
Expand All@@ -1027,7 +1037,7 @@ export const viVN: LocalizationResource = {
form_identifier_exists__phone_number: undefined,
form_identifier_exists__username: undefined,
form_identifier_not_found: undefined,
form_new_password_matches_current: undefined,
form_new_password_matches_current: 'Mật khẩu mới không được trùng với mật khẩu hiện tại.',
form_param_format_invalid: undefined,
form_param_format_invalid__email_address: undefined,
form_param_format_invalid__phone_number: undefined,
Expand All@@ -1050,7 +1060,8 @@ export const viVN: LocalizationResource = {
form_password_pwned__sign_in:
'Mật khẩu này đã được tìm thấy trong một rò rỉ và không thể được sử dụng, vui lòng đặt lại mật khẩu của bạn.',
form_password_size_in_bytes_exceeded: undefined,
form_password_untrusted__sign_in: undefined,
form_password_untrusted__sign_in:
'Mật khẩu của bạn có thể đã bị xâm phạm. Để bảo vệ tài khoản, vui lòng tiếp tục bằng một phương thức đăng nhập khác. Bạn sẽ được yêu cầu đặt lại mật khẩu sau khi đăng nhập.',
form_password_validation_failed: undefined,
form_username_invalid_character: undefined,
form_username_invalid_length: 'Tên người dùng của bạn phải có giữa {{min_length}} và {{max_length}} ký tự.',
Expand All@@ -1060,10 +1071,13 @@ export const viVN: LocalizationResource = {
organization_domain_blocked: undefined,
organization_domain_common: undefined,
organization_domain_exists_for_enterprise_connection: undefined,
organization_membership_quota_exceeded: undefined,
organization_membership_quota_exceeded:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, the source of this is "You have reached your limit of {maxAllowed} organization memberships, including outstanding invitations.".

Can you confirm the translation have the same meaning?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — the meaning is correct, but I dropped the {maxAllowed} interpolation. I worked from packages/localizations/src/en-US.ts, which itself reads "You have reached your limit of organization memberships, including outstanding invitations." with no placeholder, so I missed it. Updating the Vietnamese to:

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.'

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.',
organization_minimum_permissions_needed: undefined,
organization_not_found_or_unauthorized: undefined,
organization_not_found_or_unauthorized_with_create_organization_disabled: undefined,
organization_not_found_or_unauthorized:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn hoặc tạo một tổ chức khác.',
organization_not_found_or_unauthorized_with_create_organization_disabled:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn một tổ chức khác.',
passkey_already_exists: 'Mã passkey đã được đăng ký với thiết bị này.',
passkey_not_supported: 'Mã passkey không được hỗ trợ trên thiết bị này.',
passkey_pa_not_supported: 'Đăng ký yêu cầu một bộ xác thực nền tảng nhưng thiết bị không hỗ trợ nó.',
Expand DownExpand Up@@ -1125,9 +1139,9 @@ export const viVN: LocalizationResource = {
},
userButton: {
action__addAccount: 'Thêm tài khoản',
action__closeUserMenu: undefined,
action__closeUserMenu: 'Đóng menu người dùng',
action__manageAccount: 'Quản lý tài khoản',
action__openUserMenu: undefined,
action__openUserMenu: 'Mở menu người dùng',
action__signOut: 'Đăng xuất',
action__signOutAll: 'Đăng xuất tất cả tài khoản',
},
Expand DownExpand Up@@ -1195,7 +1209,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(localizations): add missing vi-VN (Vietnamese) translations by ANIBIT14 · Pull Request #8663 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/vi-vn-translations.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/localizations': patch
---

Add missing Vietnamese (Vietnam) (`vi-VN`) translations. Fills in previously untranslated keys across billing, checkout, subscription details, organization switcher, enterprise connections, password reset and two-step verification (MFA) setup tasks, frontend API errors, and the user button menu.
204 changes: 109 additions & 95 deletions packages/localizations/src/vi-VN.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,16 +60,16 @@ export const viVN: LocalizationResource = {
badge__default: 'Mặc định',
badge__endsAt: "Kết thúc {{ date | shortDate('vi-VN') }}",
badge__expired: 'Đã hết hạn',
badge__freeTrial: undefined,
badge__freeTrial: 'Dùng thử miễn phí',
badge__otherImpersonatorDevice: 'Thiết bị giả mạo khác',
badge__pastDueAt: undefined,
badge__pastDuePlan: undefined,
badge__pastDueAt: "Quá hạn {{ date | shortDate('vi-VN') }}",
badge__pastDuePlan: 'Quá hạn',
badge__primary: 'Chính',
badge__renewsAt: "Gia hạn {{ date | shortDate('vi-VN') }}",
badge__requiresAction: 'Yêu cầu hành động',
badge__startsAt: "Bắt đầu {{ date | shortDate('vi-VN') }}",
badge__thisDevice: 'Thiết bị này',
badge__trialEndsAt: undefined,
badge__trialEndsAt: "Kết thúc dùng thử {{ date | shortDate('vi-VN') }}",
badge__unverified: 'Chưa xác minh',
badge__upcomingPlan: 'Sắp tới',
badge__userDevice: 'Thiết bị người dùng',
Expand All@@ -81,18 +81,20 @@ export const viVN: LocalizationResource = {
availableFeatures: 'Tính năng có sẵn',
billedAnnually: 'Tính phí hàng năm',
billedMonthlyOnly: 'Chỉ tính phí hàng tháng',
cancelFreeTrial: undefined,
cancelFreeTrialAccessUntil: undefined,
cancelFreeTrialTitle: undefined,
cancelFreeTrial: 'Hủy dùng thử miễn phí',
cancelFreeTrialAccessUntil:
"Bản dùng thử của bạn sẽ vẫn hoạt động cho đến {{ date | longDate('vi-VN') }}. Sau đó, bạn sẽ mất quyền truy cập vào các tính năng dùng thử. Bạn sẽ không bị tính phí.",
cancelFreeTrialTitle: 'Hủy dùng thử miễn phí cho gói {{plan}}?',
cancelSubscription: 'Hủy đăng ký',
cancelSubscriptionAccessUntil:
"Bạn có thể tiếp tục sử dụng tính năng '{{plan}}' cho đến {{ date | longDate('vi-VN') }}, sau đó bạn sẽ không còn quyền truy cập.",
cancelSubscriptionNoCharge: 'Bạn sẽ không bị tính phí cho đăng ký này.',
cancelSubscriptionPastDue: undefined,
cancelSubscriptionPastDue:
'Gói đăng ký của bạn sẽ kết thúc ngay lập tức và bạn sẽ mất quyền truy cập vào tất cả các tính năng của gói. Bạn sẽ được yêu cầu thanh toán số tiền quá hạn trong lần đăng ký tiếp theo.',
cancelSubscriptionTitle: 'Hủy đăng ký {{plan}}?',
cannotSubscribeMonthly:
'Bạn không thể đăng ký gói này bằng cách thanh toán hàng tháng. Để đăng ký gói này, bạn cần chọn thanh toán hàng năm.',
cannotSubscribeUnrecoverable: undefined,
cannotSubscribeUnrecoverable: 'Bạn không thể đăng ký gói này. Gói đăng ký hiện tại của bạn đắt hơn gói này.',
checkout: {
description__paymentSuccessful: 'Thanh toán của bạn đã thành công.',
description__subscriptionSuccessful: 'Đăng ký mới của bạn đã được thiết lập.',
Expand All@@ -103,7 +105,7 @@ export const viVN: LocalizationResource = {
title: 'Thêm địa chỉ email',
},
lineItems: {
title__freeTrialEndsAt: undefined,
title__freeTrialEndsAt: 'Dùng thử kết thúc vào',
title__paymentMethod: 'Phương thức thanh toán',
title__statementId: 'ID hóa đơn',
title__subscriptionBegins: 'Đăng ký bắt đầu',
Expand All@@ -114,15 +116,15 @@ export const viVN: LocalizationResource = {
title: 'Thanh toán',
title__paymentSuccessful: 'Thanh toán thành công!',
title__subscriptionSuccessful: 'Thành công!',
title__trialSuccess: undefined,
totalDueAfterTrial: undefined,
title__trialSuccess: 'Đã bắt đầu dùng thử thành công!',
totalDueAfterTrial: 'Tổng số tiền phải trả sau khi dùng thử kết thúc trong {{days}} ngày',
},
credit: 'Tín dụng',
creditRemainder: 'Tín dụng cho phần còn lại của đăng ký hiện tại.',
defaultFreePlanActive: 'Bạn hiện đang trên gói Miễn phí',
free: 'Miễn phí',
getStarted: 'Bắt đầu',
keepFreeTrial: undefined,
keepFreeTrial: 'Giữ bản dùng thử miễn phí',
keepSubscription: 'Giữ đăng ký',
manage: 'Quản lý',
manageSubscription: 'Quản lý đăng ký',
Expand DownExpand Up@@ -152,34 +154,34 @@ export const viVN: LocalizationResource = {
},
reSubscribe: 'Đăng ký lại',
seeAllFeatures: 'Xem tất cả tính năng',
startFreeTrial: undefined,
startFreeTrial__days: undefined,
startFreeTrial: 'Bắt đầu dùng thử miễn phí',
startFreeTrial__days: 'Bắt đầu dùng thử miễn phí {{days}} ngày',
subscribe: 'Đăng ký',
subscriptionDetails: {
beginsOn: undefined,
currentBillingCycle: undefined,
endsOn: undefined,
firstPaymentAmount: undefined,
firstPaymentOn: undefined,
nextPaymentAmount: undefined,
nextPaymentOn: undefined,
pastDueAt: undefined,
renewsAt: undefined,
subscribedOn: undefined,
title: undefined,
trialEndsOn: undefined,
trialStartedOn: undefined,
beginsOn: 'Bắt đầu vào',
currentBillingCycle: 'Chu kỳ thanh toán hiện tại',
endsOn: 'Kết thúc vào',
firstPaymentAmount: 'Số tiền thanh toán đầu tiên',
firstPaymentOn: 'Thanh toán đầu tiên vào',
nextPaymentAmount: 'Số tiền thanh toán tiếp theo',
nextPaymentOn: 'Thanh toán tiếp theo vào',
pastDueAt: 'Quá hạn vào',
renewsAt: 'Gia hạn vào',
subscribedOn: 'Đăng ký vào',
title: 'Gói đăng ký',
trialEndsOn: 'Dùng thử kết thúc vào',
trialStartedOn: 'Dùng thử bắt đầu vào',
},
subtotal: 'Tổng cộng',
switchPlan: 'Chuyển sang gói này',
switchToAnnual: 'Chuyển sang hàng năm',
switchToAnnualWithAnnualPrice: undefined,
switchToAnnualWithAnnualPrice: 'Chuyển sang gói năm {{currency}}{{price}} / năm',
switchToMonthly: 'Chuyển sang hàng tháng',
switchToMonthlyWithPrice: undefined,
switchToMonthlyWithPrice: 'Chuyển sang gói tháng {{currency}}{{price}} / tháng',
totalDue: 'Tổng cần thanh toán',
totalDueToday: 'Tổng cần thanh toán hôm nay',
viewFeatures: 'Xem tính năng',
viewPayment: undefined,
viewPayment: 'Xem thanh toán',
year: 'Năm',
},
configureSSO: {
Expand DownExpand Up@@ -271,8 +273,8 @@ export const viVN: LocalizationResource = {
formFieldInputPlaceholder__organizationSlug: 'my-org',
formFieldInputPlaceholder__password: 'Nhập mật khẩu của bạn',
formFieldInputPlaceholder__phoneNumber: 'Nhập số điện thoại của bạn',
formFieldInputPlaceholder__username: undefined,
formFieldInput__emailAddress_format: undefined,
formFieldInputPlaceholder__username: 'Nhập tên người dùng của bạn',
formFieldInput__emailAddress_format: 'Định dạng ví dụ: name@example.com',
formFieldLabel__apiKey: 'Khóa API',
formFieldLabel__apiKeyDescription: 'Mô tả',
formFieldLabel__apiKeyExpiration: 'Hết hạn',
Expand DownExpand Up@@ -373,7 +375,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand DownExpand Up@@ -549,11 +551,11 @@ export const viVN: LocalizationResource = {
},
},
organizationSwitcher: {
action__closeOrganizationSwitcher: undefined,
action__closeOrganizationSwitcher: 'Đóng menu chuyển đổi tổ chức',
action__createOrganization: 'Tạo tổ chức',
action__invitationAccept: 'Tham gia',
action__manageOrganization: 'Quản lý',
action__openOrganizationSwitcher: undefined,
action__openOrganizationSwitcher: 'Mở menu chuyển đổi tổ chức',
action__suggestionsAccept: 'Yêu cầu tham gia',
notSelected: 'Không có tổ chức được chọn',
personalWorkspace: 'Tài khoản cá nhân',
Expand DownExpand Up@@ -716,8 +718,8 @@ export const viVN: LocalizationResource = {
title: 'Kiểm tra email của bạn',
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
forgotPassword: {
formTitle: 'Mã xác minh mật khẩu',
Expand DownExpand Up@@ -750,13 +752,13 @@ export const viVN: LocalizationResource = {
title: 'Nhập mật khẩu',
},
passwordCompromised: {
title: undefined,
title: 'Mật khẩu đã bị xâm phạm',
},
passwordPwned: {
title: 'Mật khẩu bị rò rỉ',
},
passwordUntrusted: {
title: undefined,
title: 'Mật khẩu không đáng tin cậy',
},
phoneCode: {
formTitle: 'Mã xác minh',
Expand DownExpand Up@@ -853,8 +855,8 @@ export const viVN: LocalizationResource = {
},
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
legalConsent: {
checkbox: {
Expand DownExpand Up@@ -942,74 +944,82 @@ export const viVN: LocalizationResource = {
},
},
taskResetPassword: {
formButtonPrimary: undefined,
formButtonPrimary: 'Đặt lại mật khẩu',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
subtitle: undefined,
title: undefined,
subtitle: 'Tài khoản của bạn yêu cầu mật khẩu mới trước khi bạn có thể tiếp tục',
title: 'Đặt lại mật khẩu của bạn',
},
taskSetupMfa: {
badge: undefined,
badge: 'Thiết lập xác minh hai bước',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
smsCode: {
addPhone: {
formButtonPrimary: undefined,
infoText: undefined,
formButtonPrimary: 'Tiếp tục',
infoText:
'Một tin nhắn văn bản chứa mã xác minh sẽ được gửi đến số điện thoại này. Có thể áp dụng cước phí tin nhắn và dữ liệu.',
},
addPhoneNumber: undefined,
cancel: undefined,
subtitle: undefined,
addPhoneNumber: 'Thêm số điện thoại',
cancel: 'Hủy',
subtitle: 'Chọn số điện thoại bạn muốn sử dụng để xác minh hai bước bằng mã SMS',
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
},
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh được gửi đến số điện thoại này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng mã SMS',
},
title: 'Thêm xác minh bằng mã SMS',
verifyPhone: {
formButtonPrimary: undefined,
formTitle: undefined,
resendButton: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formTitle: 'Mã xác minh',
resendButton: 'Không nhận được mã? Gửi lại',
subtitle: 'Nhập mã xác minh được gửi đến',
title: 'Xác minh số điện thoại của bạn',
},
},
start: {
methodSelection: {
phoneCode: undefined,
totp: undefined,
phoneCode: 'Mã SMS',
totp: 'Ứng dụng xác thực',
},
subtitle: undefined,
title: undefined,
subtitle: 'Chọn phương thức bạn muốn sử dụng để bảo vệ tài khoản của mình bằng một lớp bảo mật bổ sung',
title: 'Thiết lập xác minh hai bước',
},
totpCode: {
addAuthenticatorApp: {
buttonAbleToScan__nonPrimary: undefined,
buttonUnableToScan__nonPrimary: undefined,
formButtonPrimary: undefined,
formButtonReset: undefined,
infoText__ableToScan: undefined,
infoText__unableToScan: undefined,
inputLabel__unableToScan1: undefined,
buttonAbleToScan__nonPrimary: 'Thay vào đó, quét mã QR',
buttonUnableToScan__nonPrimary: 'Không thể quét mã QR?',
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
infoText__ableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và quét mã QR sau đây để liên kết nó với tài khoản của bạn.',
infoText__unableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và nhập Khóa được cung cấp bên dưới.',
inputLabel__unableToScan1:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads quite literally in Vietnamese. “Mật khẩu dựa trên thời gian hoặc Mật khẩu một lần” sounds machine-translated and “mật khẩu” is awkward here; “mã” would be
more natural for TOTP/OTP. Can you rephrase this to something more idiomatic and align it with the wording already used elsewhere in the locale?

Can you confirm this review by Codex if it's legit?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — in Vietnamese tech UI, TOTP/OTP are conventionally rendered as "mã" (code), not "mật khẩu" (password). Updating to:

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.'

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.',
},
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh từ ứng dụng xác thực này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng ứng dụng xác thực',
},
title: undefined,
title: 'Thêm ứng dụng xác thực',
verifyTotp: {
formButtonPrimary: undefined,
formButtonReset: undefined,
formTitle: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
formTitle: 'Mã xác minh',
subtitle: 'Nhập mã xác minh được tạo bởi ứng dụng xác thực của bạn',
title: 'Thêm ứng dụng xác thực',
},
},
},
Expand All@@ -1027,7 +1037,7 @@ export const viVN: LocalizationResource = {
form_identifier_exists__phone_number: undefined,
form_identifier_exists__username: undefined,
form_identifier_not_found: undefined,
form_new_password_matches_current: undefined,
form_new_password_matches_current: 'Mật khẩu mới không được trùng với mật khẩu hiện tại.',
form_param_format_invalid: undefined,
form_param_format_invalid__email_address: undefined,
form_param_format_invalid__phone_number: undefined,
Expand All@@ -1050,7 +1060,8 @@ export const viVN: LocalizationResource = {
form_password_pwned__sign_in:
'Mật khẩu này đã được tìm thấy trong một rò rỉ và không thể được sử dụng, vui lòng đặt lại mật khẩu của bạn.',
form_password_size_in_bytes_exceeded: undefined,
form_password_untrusted__sign_in: undefined,
form_password_untrusted__sign_in:
'Mật khẩu của bạn có thể đã bị xâm phạm. Để bảo vệ tài khoản, vui lòng tiếp tục bằng một phương thức đăng nhập khác. Bạn sẽ được yêu cầu đặt lại mật khẩu sau khi đăng nhập.',
form_password_validation_failed: undefined,
form_username_invalid_character: undefined,
form_username_invalid_length: 'Tên người dùng của bạn phải có giữa {{min_length}} và {{max_length}} ký tự.',
Expand All@@ -1060,10 +1071,13 @@ export const viVN: LocalizationResource = {
organization_domain_blocked: undefined,
organization_domain_common: undefined,
organization_domain_exists_for_enterprise_connection: undefined,
organization_membership_quota_exceeded: undefined,
organization_membership_quota_exceeded:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, the source of this is "You have reached your limit of {maxAllowed} organization memberships, including outstanding invitations.".

Can you confirm the translation have the same meaning?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — the meaning is correct, but I dropped the {maxAllowed} interpolation. I worked from packages/localizations/src/en-US.ts, which itself reads "You have reached your limit of organization memberships, including outstanding invitations." with no placeholder, so I missed it. Updating the Vietnamese to:

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.'

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.',
organization_minimum_permissions_needed: undefined,
organization_not_found_or_unauthorized: undefined,
organization_not_found_or_unauthorized_with_create_organization_disabled: undefined,
organization_not_found_or_unauthorized:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn hoặc tạo một tổ chức khác.',
organization_not_found_or_unauthorized_with_create_organization_disabled:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn một tổ chức khác.',
passkey_already_exists: 'Mã passkey đã được đăng ký với thiết bị này.',
passkey_not_supported: 'Mã passkey không được hỗ trợ trên thiết bị này.',
passkey_pa_not_supported: 'Đăng ký yêu cầu một bộ xác thực nền tảng nhưng thiết bị không hỗ trợ nó.',
Expand DownExpand Up@@ -1125,9 +1139,9 @@ export const viVN: LocalizationResource = {
},
userButton: {
action__addAccount: 'Thêm tài khoản',
action__closeUserMenu: undefined,
action__closeUserMenu: 'Đóng menu người dùng',
action__manageAccount: 'Quản lý tài khoản',
action__openUserMenu: undefined,
action__openUserMenu: 'Mở menu người dùng',
action__signOut: 'Đăng xuất',
action__signOutAll: 'Đăng xuất tất cả tài khoản',
},
Expand DownExpand Up@@ -1195,7 +1209,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(localizations): add missing vi-VN (Vietnamese) translations by ANIBIT14 · Pull Request #8663 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/vi-vn-translations.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/localizations': patch
---

Add missing Vietnamese (Vietnam) (`vi-VN`) translations. Fills in previously untranslated keys across billing, checkout, subscription details, organization switcher, enterprise connections, password reset and two-step verification (MFA) setup tasks, frontend API errors, and the user button menu.
204 changes: 109 additions & 95 deletions packages/localizations/src/vi-VN.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,16 +60,16 @@ export const viVN: LocalizationResource = {
badge__default: 'Mặc định',
badge__endsAt: "Kết thúc {{ date | shortDate('vi-VN') }}",
badge__expired: 'Đã hết hạn',
badge__freeTrial: undefined,
badge__freeTrial: 'Dùng thử miễn phí',
badge__otherImpersonatorDevice: 'Thiết bị giả mạo khác',
badge__pastDueAt: undefined,
badge__pastDuePlan: undefined,
badge__pastDueAt: "Quá hạn {{ date | shortDate('vi-VN') }}",
badge__pastDuePlan: 'Quá hạn',
badge__primary: 'Chính',
badge__renewsAt: "Gia hạn {{ date | shortDate('vi-VN') }}",
badge__requiresAction: 'Yêu cầu hành động',
badge__startsAt: "Bắt đầu {{ date | shortDate('vi-VN') }}",
badge__thisDevice: 'Thiết bị này',
badge__trialEndsAt: undefined,
badge__trialEndsAt: "Kết thúc dùng thử {{ date | shortDate('vi-VN') }}",
badge__unverified: 'Chưa xác minh',
badge__upcomingPlan: 'Sắp tới',
badge__userDevice: 'Thiết bị người dùng',
Expand All@@ -81,18 +81,20 @@ export const viVN: LocalizationResource = {
availableFeatures: 'Tính năng có sẵn',
billedAnnually: 'Tính phí hàng năm',
billedMonthlyOnly: 'Chỉ tính phí hàng tháng',
cancelFreeTrial: undefined,
cancelFreeTrialAccessUntil: undefined,
cancelFreeTrialTitle: undefined,
cancelFreeTrial: 'Hủy dùng thử miễn phí',
cancelFreeTrialAccessUntil:
"Bản dùng thử của bạn sẽ vẫn hoạt động cho đến {{ date | longDate('vi-VN') }}. Sau đó, bạn sẽ mất quyền truy cập vào các tính năng dùng thử. Bạn sẽ không bị tính phí.",
cancelFreeTrialTitle: 'Hủy dùng thử miễn phí cho gói {{plan}}?',
cancelSubscription: 'Hủy đăng ký',
cancelSubscriptionAccessUntil:
"Bạn có thể tiếp tục sử dụng tính năng '{{plan}}' cho đến {{ date | longDate('vi-VN') }}, sau đó bạn sẽ không còn quyền truy cập.",
cancelSubscriptionNoCharge: 'Bạn sẽ không bị tính phí cho đăng ký này.',
cancelSubscriptionPastDue: undefined,
cancelSubscriptionPastDue:
'Gói đăng ký của bạn sẽ kết thúc ngay lập tức và bạn sẽ mất quyền truy cập vào tất cả các tính năng của gói. Bạn sẽ được yêu cầu thanh toán số tiền quá hạn trong lần đăng ký tiếp theo.',
cancelSubscriptionTitle: 'Hủy đăng ký {{plan}}?',
cannotSubscribeMonthly:
'Bạn không thể đăng ký gói này bằng cách thanh toán hàng tháng. Để đăng ký gói này, bạn cần chọn thanh toán hàng năm.',
cannotSubscribeUnrecoverable: undefined,
cannotSubscribeUnrecoverable: 'Bạn không thể đăng ký gói này. Gói đăng ký hiện tại của bạn đắt hơn gói này.',
checkout: {
description__paymentSuccessful: 'Thanh toán của bạn đã thành công.',
description__subscriptionSuccessful: 'Đăng ký mới của bạn đã được thiết lập.',
Expand All@@ -103,7 +105,7 @@ export const viVN: LocalizationResource = {
title: 'Thêm địa chỉ email',
},
lineItems: {
title__freeTrialEndsAt: undefined,
title__freeTrialEndsAt: 'Dùng thử kết thúc vào',
title__paymentMethod: 'Phương thức thanh toán',
title__statementId: 'ID hóa đơn',
title__subscriptionBegins: 'Đăng ký bắt đầu',
Expand All@@ -114,15 +116,15 @@ export const viVN: LocalizationResource = {
title: 'Thanh toán',
title__paymentSuccessful: 'Thanh toán thành công!',
title__subscriptionSuccessful: 'Thành công!',
title__trialSuccess: undefined,
totalDueAfterTrial: undefined,
title__trialSuccess: 'Đã bắt đầu dùng thử thành công!',
totalDueAfterTrial: 'Tổng số tiền phải trả sau khi dùng thử kết thúc trong {{days}} ngày',
},
credit: 'Tín dụng',
creditRemainder: 'Tín dụng cho phần còn lại của đăng ký hiện tại.',
defaultFreePlanActive: 'Bạn hiện đang trên gói Miễn phí',
free: 'Miễn phí',
getStarted: 'Bắt đầu',
keepFreeTrial: undefined,
keepFreeTrial: 'Giữ bản dùng thử miễn phí',
keepSubscription: 'Giữ đăng ký',
manage: 'Quản lý',
manageSubscription: 'Quản lý đăng ký',
Expand DownExpand Up@@ -152,34 +154,34 @@ export const viVN: LocalizationResource = {
},
reSubscribe: 'Đăng ký lại',
seeAllFeatures: 'Xem tất cả tính năng',
startFreeTrial: undefined,
startFreeTrial__days: undefined,
startFreeTrial: 'Bắt đầu dùng thử miễn phí',
startFreeTrial__days: 'Bắt đầu dùng thử miễn phí {{days}} ngày',
subscribe: 'Đăng ký',
subscriptionDetails: {
beginsOn: undefined,
currentBillingCycle: undefined,
endsOn: undefined,
firstPaymentAmount: undefined,
firstPaymentOn: undefined,
nextPaymentAmount: undefined,
nextPaymentOn: undefined,
pastDueAt: undefined,
renewsAt: undefined,
subscribedOn: undefined,
title: undefined,
trialEndsOn: undefined,
trialStartedOn: undefined,
beginsOn: 'Bắt đầu vào',
currentBillingCycle: 'Chu kỳ thanh toán hiện tại',
endsOn: 'Kết thúc vào',
firstPaymentAmount: 'Số tiền thanh toán đầu tiên',
firstPaymentOn: 'Thanh toán đầu tiên vào',
nextPaymentAmount: 'Số tiền thanh toán tiếp theo',
nextPaymentOn: 'Thanh toán tiếp theo vào',
pastDueAt: 'Quá hạn vào',
renewsAt: 'Gia hạn vào',
subscribedOn: 'Đăng ký vào',
title: 'Gói đăng ký',
trialEndsOn: 'Dùng thử kết thúc vào',
trialStartedOn: 'Dùng thử bắt đầu vào',
},
subtotal: 'Tổng cộng',
switchPlan: 'Chuyển sang gói này',
switchToAnnual: 'Chuyển sang hàng năm',
switchToAnnualWithAnnualPrice: undefined,
switchToAnnualWithAnnualPrice: 'Chuyển sang gói năm {{currency}}{{price}} / năm',
switchToMonthly: 'Chuyển sang hàng tháng',
switchToMonthlyWithPrice: undefined,
switchToMonthlyWithPrice: 'Chuyển sang gói tháng {{currency}}{{price}} / tháng',
totalDue: 'Tổng cần thanh toán',
totalDueToday: 'Tổng cần thanh toán hôm nay',
viewFeatures: 'Xem tính năng',
viewPayment: undefined,
viewPayment: 'Xem thanh toán',
year: 'Năm',
},
configureSSO: {
Expand DownExpand Up@@ -271,8 +273,8 @@ export const viVN: LocalizationResource = {
formFieldInputPlaceholder__organizationSlug: 'my-org',
formFieldInputPlaceholder__password: 'Nhập mật khẩu của bạn',
formFieldInputPlaceholder__phoneNumber: 'Nhập số điện thoại của bạn',
formFieldInputPlaceholder__username: undefined,
formFieldInput__emailAddress_format: undefined,
formFieldInputPlaceholder__username: 'Nhập tên người dùng của bạn',
formFieldInput__emailAddress_format: 'Định dạng ví dụ: name@example.com',
formFieldLabel__apiKey: 'Khóa API',
formFieldLabel__apiKeyDescription: 'Mô tả',
formFieldLabel__apiKeyExpiration: 'Hết hạn',
Expand DownExpand Up@@ -373,7 +375,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand DownExpand Up@@ -549,11 +551,11 @@ export const viVN: LocalizationResource = {
},
},
organizationSwitcher: {
action__closeOrganizationSwitcher: undefined,
action__closeOrganizationSwitcher: 'Đóng menu chuyển đổi tổ chức',
action__createOrganization: 'Tạo tổ chức',
action__invitationAccept: 'Tham gia',
action__manageOrganization: 'Quản lý',
action__openOrganizationSwitcher: undefined,
action__openOrganizationSwitcher: 'Mở menu chuyển đổi tổ chức',
action__suggestionsAccept: 'Yêu cầu tham gia',
notSelected: 'Không có tổ chức được chọn',
personalWorkspace: 'Tài khoản cá nhân',
Expand DownExpand Up@@ -716,8 +718,8 @@ export const viVN: LocalizationResource = {
title: 'Kiểm tra email của bạn',
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
forgotPassword: {
formTitle: 'Mã xác minh mật khẩu',
Expand DownExpand Up@@ -750,13 +752,13 @@ export const viVN: LocalizationResource = {
title: 'Nhập mật khẩu',
},
passwordCompromised: {
title: undefined,
title: 'Mật khẩu đã bị xâm phạm',
},
passwordPwned: {
title: 'Mật khẩu bị rò rỉ',
},
passwordUntrusted: {
title: undefined,
title: 'Mật khẩu không đáng tin cậy',
},
phoneCode: {
formTitle: 'Mã xác minh',
Expand DownExpand Up@@ -853,8 +855,8 @@ export const viVN: LocalizationResource = {
},
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
legalConsent: {
checkbox: {
Expand DownExpand Up@@ -942,74 +944,82 @@ export const viVN: LocalizationResource = {
},
},
taskResetPassword: {
formButtonPrimary: undefined,
formButtonPrimary: 'Đặt lại mật khẩu',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
subtitle: undefined,
title: undefined,
subtitle: 'Tài khoản của bạn yêu cầu mật khẩu mới trước khi bạn có thể tiếp tục',
title: 'Đặt lại mật khẩu của bạn',
},
taskSetupMfa: {
badge: undefined,
badge: 'Thiết lập xác minh hai bước',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
smsCode: {
addPhone: {
formButtonPrimary: undefined,
infoText: undefined,
formButtonPrimary: 'Tiếp tục',
infoText:
'Một tin nhắn văn bản chứa mã xác minh sẽ được gửi đến số điện thoại này. Có thể áp dụng cước phí tin nhắn và dữ liệu.',
},
addPhoneNumber: undefined,
cancel: undefined,
subtitle: undefined,
addPhoneNumber: 'Thêm số điện thoại',
cancel: 'Hủy',
subtitle: 'Chọn số điện thoại bạn muốn sử dụng để xác minh hai bước bằng mã SMS',
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
},
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh được gửi đến số điện thoại này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng mã SMS',
},
title: 'Thêm xác minh bằng mã SMS',
verifyPhone: {
formButtonPrimary: undefined,
formTitle: undefined,
resendButton: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formTitle: 'Mã xác minh',
resendButton: 'Không nhận được mã? Gửi lại',
subtitle: 'Nhập mã xác minh được gửi đến',
title: 'Xác minh số điện thoại của bạn',
},
},
start: {
methodSelection: {
phoneCode: undefined,
totp: undefined,
phoneCode: 'Mã SMS',
totp: 'Ứng dụng xác thực',
},
subtitle: undefined,
title: undefined,
subtitle: 'Chọn phương thức bạn muốn sử dụng để bảo vệ tài khoản của mình bằng một lớp bảo mật bổ sung',
title: 'Thiết lập xác minh hai bước',
},
totpCode: {
addAuthenticatorApp: {
buttonAbleToScan__nonPrimary: undefined,
buttonUnableToScan__nonPrimary: undefined,
formButtonPrimary: undefined,
formButtonReset: undefined,
infoText__ableToScan: undefined,
infoText__unableToScan: undefined,
inputLabel__unableToScan1: undefined,
buttonAbleToScan__nonPrimary: 'Thay vào đó, quét mã QR',
buttonUnableToScan__nonPrimary: 'Không thể quét mã QR?',
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
infoText__ableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và quét mã QR sau đây để liên kết nó với tài khoản của bạn.',
infoText__unableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và nhập Khóa được cung cấp bên dưới.',
inputLabel__unableToScan1:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads quite literally in Vietnamese. “Mật khẩu dựa trên thời gian hoặc Mật khẩu một lần” sounds machine-translated and “mật khẩu” is awkward here; “mã” would be
more natural for TOTP/OTP. Can you rephrase this to something more idiomatic and align it with the wording already used elsewhere in the locale?

Can you confirm this review by Codex if it's legit?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — in Vietnamese tech UI, TOTP/OTP are conventionally rendered as "mã" (code), not "mật khẩu" (password). Updating to:

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.'

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.',
},
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh từ ứng dụng xác thực này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng ứng dụng xác thực',
},
title: undefined,
title: 'Thêm ứng dụng xác thực',
verifyTotp: {
formButtonPrimary: undefined,
formButtonReset: undefined,
formTitle: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
formTitle: 'Mã xác minh',
subtitle: 'Nhập mã xác minh được tạo bởi ứng dụng xác thực của bạn',
title: 'Thêm ứng dụng xác thực',
},
},
},
Expand All@@ -1027,7 +1037,7 @@ export const viVN: LocalizationResource = {
form_identifier_exists__phone_number: undefined,
form_identifier_exists__username: undefined,
form_identifier_not_found: undefined,
form_new_password_matches_current: undefined,
form_new_password_matches_current: 'Mật khẩu mới không được trùng với mật khẩu hiện tại.',
form_param_format_invalid: undefined,
form_param_format_invalid__email_address: undefined,
form_param_format_invalid__phone_number: undefined,
Expand All@@ -1050,7 +1060,8 @@ export const viVN: LocalizationResource = {
form_password_pwned__sign_in:
'Mật khẩu này đã được tìm thấy trong một rò rỉ và không thể được sử dụng, vui lòng đặt lại mật khẩu của bạn.',
form_password_size_in_bytes_exceeded: undefined,
form_password_untrusted__sign_in: undefined,
form_password_untrusted__sign_in:
'Mật khẩu của bạn có thể đã bị xâm phạm. Để bảo vệ tài khoản, vui lòng tiếp tục bằng một phương thức đăng nhập khác. Bạn sẽ được yêu cầu đặt lại mật khẩu sau khi đăng nhập.',
form_password_validation_failed: undefined,
form_username_invalid_character: undefined,
form_username_invalid_length: 'Tên người dùng của bạn phải có giữa {{min_length}} và {{max_length}} ký tự.',
Expand All@@ -1060,10 +1071,13 @@ export const viVN: LocalizationResource = {
organization_domain_blocked: undefined,
organization_domain_common: undefined,
organization_domain_exists_for_enterprise_connection: undefined,
organization_membership_quota_exceeded: undefined,
organization_membership_quota_exceeded:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, the source of this is "You have reached your limit of {maxAllowed} organization memberships, including outstanding invitations.".

Can you confirm the translation have the same meaning?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — the meaning is correct, but I dropped the {maxAllowed} interpolation. I worked from packages/localizations/src/en-US.ts, which itself reads "You have reached your limit of organization memberships, including outstanding invitations." with no placeholder, so I missed it. Updating the Vietnamese to:

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.'

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.',
organization_minimum_permissions_needed: undefined,
organization_not_found_or_unauthorized: undefined,
organization_not_found_or_unauthorized_with_create_organization_disabled: undefined,
organization_not_found_or_unauthorized:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn hoặc tạo một tổ chức khác.',
organization_not_found_or_unauthorized_with_create_organization_disabled:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn một tổ chức khác.',
passkey_already_exists: 'Mã passkey đã được đăng ký với thiết bị này.',
passkey_not_supported: 'Mã passkey không được hỗ trợ trên thiết bị này.',
passkey_pa_not_supported: 'Đăng ký yêu cầu một bộ xác thực nền tảng nhưng thiết bị không hỗ trợ nó.',
Expand DownExpand Up@@ -1125,9 +1139,9 @@ export const viVN: LocalizationResource = {
},
userButton: {
action__addAccount: 'Thêm tài khoản',
action__closeUserMenu: undefined,
action__closeUserMenu: 'Đóng menu người dùng',
action__manageAccount: 'Quản lý tài khoản',
action__openUserMenu: undefined,
action__openUserMenu: 'Mở menu người dùng',
action__signOut: 'Đăng xuất',
action__signOutAll: 'Đăng xuất tất cả tài khoản',
},
Expand DownExpand Up@@ -1195,7 +1209,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(localizations): add missing vi-VN (Vietnamese) translations by ANIBIT14 · Pull Request #8663 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/vi-vn-translations.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/localizations': patch
---

Add missing Vietnamese (Vietnam) (`vi-VN`) translations. Fills in previously untranslated keys across billing, checkout, subscription details, organization switcher, enterprise connections, password reset and two-step verification (MFA) setup tasks, frontend API errors, and the user button menu.
204 changes: 109 additions & 95 deletions packages/localizations/src/vi-VN.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,16 +60,16 @@ export const viVN: LocalizationResource = {
badge__default: 'Mặc định',
badge__endsAt: "Kết thúc {{ date | shortDate('vi-VN') }}",
badge__expired: 'Đã hết hạn',
badge__freeTrial: undefined,
badge__freeTrial: 'Dùng thử miễn phí',
badge__otherImpersonatorDevice: 'Thiết bị giả mạo khác',
badge__pastDueAt: undefined,
badge__pastDuePlan: undefined,
badge__pastDueAt: "Quá hạn {{ date | shortDate('vi-VN') }}",
badge__pastDuePlan: 'Quá hạn',
badge__primary: 'Chính',
badge__renewsAt: "Gia hạn {{ date | shortDate('vi-VN') }}",
badge__requiresAction: 'Yêu cầu hành động',
badge__startsAt: "Bắt đầu {{ date | shortDate('vi-VN') }}",
badge__thisDevice: 'Thiết bị này',
badge__trialEndsAt: undefined,
badge__trialEndsAt: "Kết thúc dùng thử {{ date | shortDate('vi-VN') }}",
badge__unverified: 'Chưa xác minh',
badge__upcomingPlan: 'Sắp tới',
badge__userDevice: 'Thiết bị người dùng',
Expand All@@ -81,18 +81,20 @@ export const viVN: LocalizationResource = {
availableFeatures: 'Tính năng có sẵn',
billedAnnually: 'Tính phí hàng năm',
billedMonthlyOnly: 'Chỉ tính phí hàng tháng',
cancelFreeTrial: undefined,
cancelFreeTrialAccessUntil: undefined,
cancelFreeTrialTitle: undefined,
cancelFreeTrial: 'Hủy dùng thử miễn phí',
cancelFreeTrialAccessUntil:
"Bản dùng thử của bạn sẽ vẫn hoạt động cho đến {{ date | longDate('vi-VN') }}. Sau đó, bạn sẽ mất quyền truy cập vào các tính năng dùng thử. Bạn sẽ không bị tính phí.",
cancelFreeTrialTitle: 'Hủy dùng thử miễn phí cho gói {{plan}}?',
cancelSubscription: 'Hủy đăng ký',
cancelSubscriptionAccessUntil:
"Bạn có thể tiếp tục sử dụng tính năng '{{plan}}' cho đến {{ date | longDate('vi-VN') }}, sau đó bạn sẽ không còn quyền truy cập.",
cancelSubscriptionNoCharge: 'Bạn sẽ không bị tính phí cho đăng ký này.',
cancelSubscriptionPastDue: undefined,
cancelSubscriptionPastDue:
'Gói đăng ký của bạn sẽ kết thúc ngay lập tức và bạn sẽ mất quyền truy cập vào tất cả các tính năng của gói. Bạn sẽ được yêu cầu thanh toán số tiền quá hạn trong lần đăng ký tiếp theo.',
cancelSubscriptionTitle: 'Hủy đăng ký {{plan}}?',
cannotSubscribeMonthly:
'Bạn không thể đăng ký gói này bằng cách thanh toán hàng tháng. Để đăng ký gói này, bạn cần chọn thanh toán hàng năm.',
cannotSubscribeUnrecoverable: undefined,
cannotSubscribeUnrecoverable: 'Bạn không thể đăng ký gói này. Gói đăng ký hiện tại của bạn đắt hơn gói này.',
checkout: {
description__paymentSuccessful: 'Thanh toán của bạn đã thành công.',
description__subscriptionSuccessful: 'Đăng ký mới của bạn đã được thiết lập.',
Expand All@@ -103,7 +105,7 @@ export const viVN: LocalizationResource = {
title: 'Thêm địa chỉ email',
},
lineItems: {
title__freeTrialEndsAt: undefined,
title__freeTrialEndsAt: 'Dùng thử kết thúc vào',
title__paymentMethod: 'Phương thức thanh toán',
title__statementId: 'ID hóa đơn',
title__subscriptionBegins: 'Đăng ký bắt đầu',
Expand All@@ -114,15 +116,15 @@ export const viVN: LocalizationResource = {
title: 'Thanh toán',
title__paymentSuccessful: 'Thanh toán thành công!',
title__subscriptionSuccessful: 'Thành công!',
title__trialSuccess: undefined,
totalDueAfterTrial: undefined,
title__trialSuccess: 'Đã bắt đầu dùng thử thành công!',
totalDueAfterTrial: 'Tổng số tiền phải trả sau khi dùng thử kết thúc trong {{days}} ngày',
},
credit: 'Tín dụng',
creditRemainder: 'Tín dụng cho phần còn lại của đăng ký hiện tại.',
defaultFreePlanActive: 'Bạn hiện đang trên gói Miễn phí',
free: 'Miễn phí',
getStarted: 'Bắt đầu',
keepFreeTrial: undefined,
keepFreeTrial: 'Giữ bản dùng thử miễn phí',
keepSubscription: 'Giữ đăng ký',
manage: 'Quản lý',
manageSubscription: 'Quản lý đăng ký',
Expand DownExpand Up@@ -152,34 +154,34 @@ export const viVN: LocalizationResource = {
},
reSubscribe: 'Đăng ký lại',
seeAllFeatures: 'Xem tất cả tính năng',
startFreeTrial: undefined,
startFreeTrial__days: undefined,
startFreeTrial: 'Bắt đầu dùng thử miễn phí',
startFreeTrial__days: 'Bắt đầu dùng thử miễn phí {{days}} ngày',
subscribe: 'Đăng ký',
subscriptionDetails: {
beginsOn: undefined,
currentBillingCycle: undefined,
endsOn: undefined,
firstPaymentAmount: undefined,
firstPaymentOn: undefined,
nextPaymentAmount: undefined,
nextPaymentOn: undefined,
pastDueAt: undefined,
renewsAt: undefined,
subscribedOn: undefined,
title: undefined,
trialEndsOn: undefined,
trialStartedOn: undefined,
beginsOn: 'Bắt đầu vào',
currentBillingCycle: 'Chu kỳ thanh toán hiện tại',
endsOn: 'Kết thúc vào',
firstPaymentAmount: 'Số tiền thanh toán đầu tiên',
firstPaymentOn: 'Thanh toán đầu tiên vào',
nextPaymentAmount: 'Số tiền thanh toán tiếp theo',
nextPaymentOn: 'Thanh toán tiếp theo vào',
pastDueAt: 'Quá hạn vào',
renewsAt: 'Gia hạn vào',
subscribedOn: 'Đăng ký vào',
title: 'Gói đăng ký',
trialEndsOn: 'Dùng thử kết thúc vào',
trialStartedOn: 'Dùng thử bắt đầu vào',
},
subtotal: 'Tổng cộng',
switchPlan: 'Chuyển sang gói này',
switchToAnnual: 'Chuyển sang hàng năm',
switchToAnnualWithAnnualPrice: undefined,
switchToAnnualWithAnnualPrice: 'Chuyển sang gói năm {{currency}}{{price}} / năm',
switchToMonthly: 'Chuyển sang hàng tháng',
switchToMonthlyWithPrice: undefined,
switchToMonthlyWithPrice: 'Chuyển sang gói tháng {{currency}}{{price}} / tháng',
totalDue: 'Tổng cần thanh toán',
totalDueToday: 'Tổng cần thanh toán hôm nay',
viewFeatures: 'Xem tính năng',
viewPayment: undefined,
viewPayment: 'Xem thanh toán',
year: 'Năm',
},
configureSSO: {
Expand DownExpand Up@@ -271,8 +273,8 @@ export const viVN: LocalizationResource = {
formFieldInputPlaceholder__organizationSlug: 'my-org',
formFieldInputPlaceholder__password: 'Nhập mật khẩu của bạn',
formFieldInputPlaceholder__phoneNumber: 'Nhập số điện thoại của bạn',
formFieldInputPlaceholder__username: undefined,
formFieldInput__emailAddress_format: undefined,
formFieldInputPlaceholder__username: 'Nhập tên người dùng của bạn',
formFieldInput__emailAddress_format: 'Định dạng ví dụ: name@example.com',
formFieldLabel__apiKey: 'Khóa API',
formFieldLabel__apiKeyDescription: 'Mô tả',
formFieldLabel__apiKeyExpiration: 'Hết hạn',
Expand DownExpand Up@@ -373,7 +375,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand DownExpand Up@@ -549,11 +551,11 @@ export const viVN: LocalizationResource = {
},
},
organizationSwitcher: {
action__closeOrganizationSwitcher: undefined,
action__closeOrganizationSwitcher: 'Đóng menu chuyển đổi tổ chức',
action__createOrganization: 'Tạo tổ chức',
action__invitationAccept: 'Tham gia',
action__manageOrganization: 'Quản lý',
action__openOrganizationSwitcher: undefined,
action__openOrganizationSwitcher: 'Mở menu chuyển đổi tổ chức',
action__suggestionsAccept: 'Yêu cầu tham gia',
notSelected: 'Không có tổ chức được chọn',
personalWorkspace: 'Tài khoản cá nhân',
Expand DownExpand Up@@ -716,8 +718,8 @@ export const viVN: LocalizationResource = {
title: 'Kiểm tra email của bạn',
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
forgotPassword: {
formTitle: 'Mã xác minh mật khẩu',
Expand DownExpand Up@@ -750,13 +752,13 @@ export const viVN: LocalizationResource = {
title: 'Nhập mật khẩu',
},
passwordCompromised: {
title: undefined,
title: 'Mật khẩu đã bị xâm phạm',
},
passwordPwned: {
title: 'Mật khẩu bị rò rỉ',
},
passwordUntrusted: {
title: undefined,
title: 'Mật khẩu không đáng tin cậy',
},
phoneCode: {
formTitle: 'Mã xác minh',
Expand DownExpand Up@@ -853,8 +855,8 @@ export const viVN: LocalizationResource = {
},
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
legalConsent: {
checkbox: {
Expand DownExpand Up@@ -942,74 +944,82 @@ export const viVN: LocalizationResource = {
},
},
taskResetPassword: {
formButtonPrimary: undefined,
formButtonPrimary: 'Đặt lại mật khẩu',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
subtitle: undefined,
title: undefined,
subtitle: 'Tài khoản của bạn yêu cầu mật khẩu mới trước khi bạn có thể tiếp tục',
title: 'Đặt lại mật khẩu của bạn',
},
taskSetupMfa: {
badge: undefined,
badge: 'Thiết lập xác minh hai bước',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
smsCode: {
addPhone: {
formButtonPrimary: undefined,
infoText: undefined,
formButtonPrimary: 'Tiếp tục',
infoText:
'Một tin nhắn văn bản chứa mã xác minh sẽ được gửi đến số điện thoại này. Có thể áp dụng cước phí tin nhắn và dữ liệu.',
},
addPhoneNumber: undefined,
cancel: undefined,
subtitle: undefined,
addPhoneNumber: 'Thêm số điện thoại',
cancel: 'Hủy',
subtitle: 'Chọn số điện thoại bạn muốn sử dụng để xác minh hai bước bằng mã SMS',
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
},
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh được gửi đến số điện thoại này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng mã SMS',
},
title: 'Thêm xác minh bằng mã SMS',
verifyPhone: {
formButtonPrimary: undefined,
formTitle: undefined,
resendButton: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formTitle: 'Mã xác minh',
resendButton: 'Không nhận được mã? Gửi lại',
subtitle: 'Nhập mã xác minh được gửi đến',
title: 'Xác minh số điện thoại của bạn',
},
},
start: {
methodSelection: {
phoneCode: undefined,
totp: undefined,
phoneCode: 'Mã SMS',
totp: 'Ứng dụng xác thực',
},
subtitle: undefined,
title: undefined,
subtitle: 'Chọn phương thức bạn muốn sử dụng để bảo vệ tài khoản của mình bằng một lớp bảo mật bổ sung',
title: 'Thiết lập xác minh hai bước',
},
totpCode: {
addAuthenticatorApp: {
buttonAbleToScan__nonPrimary: undefined,
buttonUnableToScan__nonPrimary: undefined,
formButtonPrimary: undefined,
formButtonReset: undefined,
infoText__ableToScan: undefined,
infoText__unableToScan: undefined,
inputLabel__unableToScan1: undefined,
buttonAbleToScan__nonPrimary: 'Thay vào đó, quét mã QR',
buttonUnableToScan__nonPrimary: 'Không thể quét mã QR?',
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
infoText__ableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và quét mã QR sau đây để liên kết nó với tài khoản của bạn.',
infoText__unableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và nhập Khóa được cung cấp bên dưới.',
inputLabel__unableToScan1:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads quite literally in Vietnamese. “Mật khẩu dựa trên thời gian hoặc Mật khẩu một lần” sounds machine-translated and “mật khẩu” is awkward here; “mã” would be
more natural for TOTP/OTP. Can you rephrase this to something more idiomatic and align it with the wording already used elsewhere in the locale?

Can you confirm this review by Codex if it's legit?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — in Vietnamese tech UI, TOTP/OTP are conventionally rendered as "mã" (code), not "mật khẩu" (password). Updating to:

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.'

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.',
},
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh từ ứng dụng xác thực này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng ứng dụng xác thực',
},
title: undefined,
title: 'Thêm ứng dụng xác thực',
verifyTotp: {
formButtonPrimary: undefined,
formButtonReset: undefined,
formTitle: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
formTitle: 'Mã xác minh',
subtitle: 'Nhập mã xác minh được tạo bởi ứng dụng xác thực của bạn',
title: 'Thêm ứng dụng xác thực',
},
},
},
Expand All@@ -1027,7 +1037,7 @@ export const viVN: LocalizationResource = {
form_identifier_exists__phone_number: undefined,
form_identifier_exists__username: undefined,
form_identifier_not_found: undefined,
form_new_password_matches_current: undefined,
form_new_password_matches_current: 'Mật khẩu mới không được trùng với mật khẩu hiện tại.',
form_param_format_invalid: undefined,
form_param_format_invalid__email_address: undefined,
form_param_format_invalid__phone_number: undefined,
Expand All@@ -1050,7 +1060,8 @@ export const viVN: LocalizationResource = {
form_password_pwned__sign_in:
'Mật khẩu này đã được tìm thấy trong một rò rỉ và không thể được sử dụng, vui lòng đặt lại mật khẩu của bạn.',
form_password_size_in_bytes_exceeded: undefined,
form_password_untrusted__sign_in: undefined,
form_password_untrusted__sign_in:
'Mật khẩu của bạn có thể đã bị xâm phạm. Để bảo vệ tài khoản, vui lòng tiếp tục bằng một phương thức đăng nhập khác. Bạn sẽ được yêu cầu đặt lại mật khẩu sau khi đăng nhập.',
form_password_validation_failed: undefined,
form_username_invalid_character: undefined,
form_username_invalid_length: 'Tên người dùng của bạn phải có giữa {{min_length}} và {{max_length}} ký tự.',
Expand All@@ -1060,10 +1071,13 @@ export const viVN: LocalizationResource = {
organization_domain_blocked: undefined,
organization_domain_common: undefined,
organization_domain_exists_for_enterprise_connection: undefined,
organization_membership_quota_exceeded: undefined,
organization_membership_quota_exceeded:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, the source of this is "You have reached your limit of {maxAllowed} organization memberships, including outstanding invitations.".

Can you confirm the translation have the same meaning?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — the meaning is correct, but I dropped the {maxAllowed} interpolation. I worked from packages/localizations/src/en-US.ts, which itself reads "You have reached your limit of organization memberships, including outstanding invitations." with no placeholder, so I missed it. Updating the Vietnamese to:

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.'

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.',
organization_minimum_permissions_needed: undefined,
organization_not_found_or_unauthorized: undefined,
organization_not_found_or_unauthorized_with_create_organization_disabled: undefined,
organization_not_found_or_unauthorized:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn hoặc tạo một tổ chức khác.',
organization_not_found_or_unauthorized_with_create_organization_disabled:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn một tổ chức khác.',
passkey_already_exists: 'Mã passkey đã được đăng ký với thiết bị này.',
passkey_not_supported: 'Mã passkey không được hỗ trợ trên thiết bị này.',
passkey_pa_not_supported: 'Đăng ký yêu cầu một bộ xác thực nền tảng nhưng thiết bị không hỗ trợ nó.',
Expand DownExpand Up@@ -1125,9 +1139,9 @@ export const viVN: LocalizationResource = {
},
userButton: {
action__addAccount: 'Thêm tài khoản',
action__closeUserMenu: undefined,
action__closeUserMenu: 'Đóng menu người dùng',
action__manageAccount: 'Quản lý tài khoản',
action__openUserMenu: undefined,
action__openUserMenu: 'Mở menu người dùng',
action__signOut: 'Đăng xuất',
action__signOutAll: 'Đăng xuất tất cả tài khoản',
},
Expand DownExpand Up@@ -1195,7 +1209,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(localizations): add missing vi-VN (Vietnamese) translations by ANIBIT14 · Pull Request #8663 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/vi-vn-translations.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/localizations': patch
---

Add missing Vietnamese (Vietnam) (`vi-VN`) translations. Fills in previously untranslated keys across billing, checkout, subscription details, organization switcher, enterprise connections, password reset and two-step verification (MFA) setup tasks, frontend API errors, and the user button menu.
204 changes: 109 additions & 95 deletions packages/localizations/src/vi-VN.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,16 +60,16 @@ export const viVN: LocalizationResource = {
badge__default: 'Mặc định',
badge__endsAt: "Kết thúc {{ date | shortDate('vi-VN') }}",
badge__expired: 'Đã hết hạn',
badge__freeTrial: undefined,
badge__freeTrial: 'Dùng thử miễn phí',
badge__otherImpersonatorDevice: 'Thiết bị giả mạo khác',
badge__pastDueAt: undefined,
badge__pastDuePlan: undefined,
badge__pastDueAt: "Quá hạn {{ date | shortDate('vi-VN') }}",
badge__pastDuePlan: 'Quá hạn',
badge__primary: 'Chính',
badge__renewsAt: "Gia hạn {{ date | shortDate('vi-VN') }}",
badge__requiresAction: 'Yêu cầu hành động',
badge__startsAt: "Bắt đầu {{ date | shortDate('vi-VN') }}",
badge__thisDevice: 'Thiết bị này',
badge__trialEndsAt: undefined,
badge__trialEndsAt: "Kết thúc dùng thử {{ date | shortDate('vi-VN') }}",
badge__unverified: 'Chưa xác minh',
badge__upcomingPlan: 'Sắp tới',
badge__userDevice: 'Thiết bị người dùng',
Expand All@@ -81,18 +81,20 @@ export const viVN: LocalizationResource = {
availableFeatures: 'Tính năng có sẵn',
billedAnnually: 'Tính phí hàng năm',
billedMonthlyOnly: 'Chỉ tính phí hàng tháng',
cancelFreeTrial: undefined,
cancelFreeTrialAccessUntil: undefined,
cancelFreeTrialTitle: undefined,
cancelFreeTrial: 'Hủy dùng thử miễn phí',
cancelFreeTrialAccessUntil:
"Bản dùng thử của bạn sẽ vẫn hoạt động cho đến {{ date | longDate('vi-VN') }}. Sau đó, bạn sẽ mất quyền truy cập vào các tính năng dùng thử. Bạn sẽ không bị tính phí.",
cancelFreeTrialTitle: 'Hủy dùng thử miễn phí cho gói {{plan}}?',
cancelSubscription: 'Hủy đăng ký',
cancelSubscriptionAccessUntil:
"Bạn có thể tiếp tục sử dụng tính năng '{{plan}}' cho đến {{ date | longDate('vi-VN') }}, sau đó bạn sẽ không còn quyền truy cập.",
cancelSubscriptionNoCharge: 'Bạn sẽ không bị tính phí cho đăng ký này.',
cancelSubscriptionPastDue: undefined,
cancelSubscriptionPastDue:
'Gói đăng ký của bạn sẽ kết thúc ngay lập tức và bạn sẽ mất quyền truy cập vào tất cả các tính năng của gói. Bạn sẽ được yêu cầu thanh toán số tiền quá hạn trong lần đăng ký tiếp theo.',
cancelSubscriptionTitle: 'Hủy đăng ký {{plan}}?',
cannotSubscribeMonthly:
'Bạn không thể đăng ký gói này bằng cách thanh toán hàng tháng. Để đăng ký gói này, bạn cần chọn thanh toán hàng năm.',
cannotSubscribeUnrecoverable: undefined,
cannotSubscribeUnrecoverable: 'Bạn không thể đăng ký gói này. Gói đăng ký hiện tại của bạn đắt hơn gói này.',
checkout: {
description__paymentSuccessful: 'Thanh toán của bạn đã thành công.',
description__subscriptionSuccessful: 'Đăng ký mới của bạn đã được thiết lập.',
Expand All@@ -103,7 +105,7 @@ export const viVN: LocalizationResource = {
title: 'Thêm địa chỉ email',
},
lineItems: {
title__freeTrialEndsAt: undefined,
title__freeTrialEndsAt: 'Dùng thử kết thúc vào',
title__paymentMethod: 'Phương thức thanh toán',
title__statementId: 'ID hóa đơn',
title__subscriptionBegins: 'Đăng ký bắt đầu',
Expand All@@ -114,15 +116,15 @@ export const viVN: LocalizationResource = {
title: 'Thanh toán',
title__paymentSuccessful: 'Thanh toán thành công!',
title__subscriptionSuccessful: 'Thành công!',
title__trialSuccess: undefined,
totalDueAfterTrial: undefined,
title__trialSuccess: 'Đã bắt đầu dùng thử thành công!',
totalDueAfterTrial: 'Tổng số tiền phải trả sau khi dùng thử kết thúc trong {{days}} ngày',
},
credit: 'Tín dụng',
creditRemainder: 'Tín dụng cho phần còn lại của đăng ký hiện tại.',
defaultFreePlanActive: 'Bạn hiện đang trên gói Miễn phí',
free: 'Miễn phí',
getStarted: 'Bắt đầu',
keepFreeTrial: undefined,
keepFreeTrial: 'Giữ bản dùng thử miễn phí',
keepSubscription: 'Giữ đăng ký',
manage: 'Quản lý',
manageSubscription: 'Quản lý đăng ký',
Expand DownExpand Up@@ -152,34 +154,34 @@ export const viVN: LocalizationResource = {
},
reSubscribe: 'Đăng ký lại',
seeAllFeatures: 'Xem tất cả tính năng',
startFreeTrial: undefined,
startFreeTrial__days: undefined,
startFreeTrial: 'Bắt đầu dùng thử miễn phí',
startFreeTrial__days: 'Bắt đầu dùng thử miễn phí {{days}} ngày',
subscribe: 'Đăng ký',
subscriptionDetails: {
beginsOn: undefined,
currentBillingCycle: undefined,
endsOn: undefined,
firstPaymentAmount: undefined,
firstPaymentOn: undefined,
nextPaymentAmount: undefined,
nextPaymentOn: undefined,
pastDueAt: undefined,
renewsAt: undefined,
subscribedOn: undefined,
title: undefined,
trialEndsOn: undefined,
trialStartedOn: undefined,
beginsOn: 'Bắt đầu vào',
currentBillingCycle: 'Chu kỳ thanh toán hiện tại',
endsOn: 'Kết thúc vào',
firstPaymentAmount: 'Số tiền thanh toán đầu tiên',
firstPaymentOn: 'Thanh toán đầu tiên vào',
nextPaymentAmount: 'Số tiền thanh toán tiếp theo',
nextPaymentOn: 'Thanh toán tiếp theo vào',
pastDueAt: 'Quá hạn vào',
renewsAt: 'Gia hạn vào',
subscribedOn: 'Đăng ký vào',
title: 'Gói đăng ký',
trialEndsOn: 'Dùng thử kết thúc vào',
trialStartedOn: 'Dùng thử bắt đầu vào',
},
subtotal: 'Tổng cộng',
switchPlan: 'Chuyển sang gói này',
switchToAnnual: 'Chuyển sang hàng năm',
switchToAnnualWithAnnualPrice: undefined,
switchToAnnualWithAnnualPrice: 'Chuyển sang gói năm {{currency}}{{price}} / năm',
switchToMonthly: 'Chuyển sang hàng tháng',
switchToMonthlyWithPrice: undefined,
switchToMonthlyWithPrice: 'Chuyển sang gói tháng {{currency}}{{price}} / tháng',
totalDue: 'Tổng cần thanh toán',
totalDueToday: 'Tổng cần thanh toán hôm nay',
viewFeatures: 'Xem tính năng',
viewPayment: undefined,
viewPayment: 'Xem thanh toán',
year: 'Năm',
},
configureSSO: {
Expand DownExpand Up@@ -271,8 +273,8 @@ export const viVN: LocalizationResource = {
formFieldInputPlaceholder__organizationSlug: 'my-org',
formFieldInputPlaceholder__password: 'Nhập mật khẩu của bạn',
formFieldInputPlaceholder__phoneNumber: 'Nhập số điện thoại của bạn',
formFieldInputPlaceholder__username: undefined,
formFieldInput__emailAddress_format: undefined,
formFieldInputPlaceholder__username: 'Nhập tên người dùng của bạn',
formFieldInput__emailAddress_format: 'Định dạng ví dụ: name@example.com',
formFieldLabel__apiKey: 'Khóa API',
formFieldLabel__apiKeyDescription: 'Mô tả',
formFieldLabel__apiKeyExpiration: 'Hết hạn',
Expand DownExpand Up@@ -373,7 +375,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand DownExpand Up@@ -549,11 +551,11 @@ export const viVN: LocalizationResource = {
},
},
organizationSwitcher: {
action__closeOrganizationSwitcher: undefined,
action__closeOrganizationSwitcher: 'Đóng menu chuyển đổi tổ chức',
action__createOrganization: 'Tạo tổ chức',
action__invitationAccept: 'Tham gia',
action__manageOrganization: 'Quản lý',
action__openOrganizationSwitcher: undefined,
action__openOrganizationSwitcher: 'Mở menu chuyển đổi tổ chức',
action__suggestionsAccept: 'Yêu cầu tham gia',
notSelected: 'Không có tổ chức được chọn',
personalWorkspace: 'Tài khoản cá nhân',
Expand DownExpand Up@@ -716,8 +718,8 @@ export const viVN: LocalizationResource = {
title: 'Kiểm tra email của bạn',
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
forgotPassword: {
formTitle: 'Mã xác minh mật khẩu',
Expand DownExpand Up@@ -750,13 +752,13 @@ export const viVN: LocalizationResource = {
title: 'Nhập mật khẩu',
},
passwordCompromised: {
title: undefined,
title: 'Mật khẩu đã bị xâm phạm',
},
passwordPwned: {
title: 'Mật khẩu bị rò rỉ',
},
passwordUntrusted: {
title: undefined,
title: 'Mật khẩu không đáng tin cậy',
},
phoneCode: {
formTitle: 'Mã xác minh',
Expand DownExpand Up@@ -853,8 +855,8 @@ export const viVN: LocalizationResource = {
},
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
legalConsent: {
checkbox: {
Expand DownExpand Up@@ -942,74 +944,82 @@ export const viVN: LocalizationResource = {
},
},
taskResetPassword: {
formButtonPrimary: undefined,
formButtonPrimary: 'Đặt lại mật khẩu',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
subtitle: undefined,
title: undefined,
subtitle: 'Tài khoản của bạn yêu cầu mật khẩu mới trước khi bạn có thể tiếp tục',
title: 'Đặt lại mật khẩu của bạn',
},
taskSetupMfa: {
badge: undefined,
badge: 'Thiết lập xác minh hai bước',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
smsCode: {
addPhone: {
formButtonPrimary: undefined,
infoText: undefined,
formButtonPrimary: 'Tiếp tục',
infoText:
'Một tin nhắn văn bản chứa mã xác minh sẽ được gửi đến số điện thoại này. Có thể áp dụng cước phí tin nhắn và dữ liệu.',
},
addPhoneNumber: undefined,
cancel: undefined,
subtitle: undefined,
addPhoneNumber: 'Thêm số điện thoại',
cancel: 'Hủy',
subtitle: 'Chọn số điện thoại bạn muốn sử dụng để xác minh hai bước bằng mã SMS',
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
},
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh được gửi đến số điện thoại này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng mã SMS',
},
title: 'Thêm xác minh bằng mã SMS',
verifyPhone: {
formButtonPrimary: undefined,
formTitle: undefined,
resendButton: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formTitle: 'Mã xác minh',
resendButton: 'Không nhận được mã? Gửi lại',
subtitle: 'Nhập mã xác minh được gửi đến',
title: 'Xác minh số điện thoại của bạn',
},
},
start: {
methodSelection: {
phoneCode: undefined,
totp: undefined,
phoneCode: 'Mã SMS',
totp: 'Ứng dụng xác thực',
},
subtitle: undefined,
title: undefined,
subtitle: 'Chọn phương thức bạn muốn sử dụng để bảo vệ tài khoản của mình bằng một lớp bảo mật bổ sung',
title: 'Thiết lập xác minh hai bước',
},
totpCode: {
addAuthenticatorApp: {
buttonAbleToScan__nonPrimary: undefined,
buttonUnableToScan__nonPrimary: undefined,
formButtonPrimary: undefined,
formButtonReset: undefined,
infoText__ableToScan: undefined,
infoText__unableToScan: undefined,
inputLabel__unableToScan1: undefined,
buttonAbleToScan__nonPrimary: 'Thay vào đó, quét mã QR',
buttonUnableToScan__nonPrimary: 'Không thể quét mã QR?',
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
infoText__ableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và quét mã QR sau đây để liên kết nó với tài khoản của bạn.',
infoText__unableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và nhập Khóa được cung cấp bên dưới.',
inputLabel__unableToScan1:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads quite literally in Vietnamese. “Mật khẩu dựa trên thời gian hoặc Mật khẩu một lần” sounds machine-translated and “mật khẩu” is awkward here; “mã” would be
more natural for TOTP/OTP. Can you rephrase this to something more idiomatic and align it with the wording already used elsewhere in the locale?

Can you confirm this review by Codex if it's legit?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — in Vietnamese tech UI, TOTP/OTP are conventionally rendered as "mã" (code), not "mật khẩu" (password). Updating to:

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.'

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.',
},
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh từ ứng dụng xác thực này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng ứng dụng xác thực',
},
title: undefined,
title: 'Thêm ứng dụng xác thực',
verifyTotp: {
formButtonPrimary: undefined,
formButtonReset: undefined,
formTitle: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
formTitle: 'Mã xác minh',
subtitle: 'Nhập mã xác minh được tạo bởi ứng dụng xác thực của bạn',
title: 'Thêm ứng dụng xác thực',
},
},
},
Expand All@@ -1027,7 +1037,7 @@ export const viVN: LocalizationResource = {
form_identifier_exists__phone_number: undefined,
form_identifier_exists__username: undefined,
form_identifier_not_found: undefined,
form_new_password_matches_current: undefined,
form_new_password_matches_current: 'Mật khẩu mới không được trùng với mật khẩu hiện tại.',
form_param_format_invalid: undefined,
form_param_format_invalid__email_address: undefined,
form_param_format_invalid__phone_number: undefined,
Expand All@@ -1050,7 +1060,8 @@ export const viVN: LocalizationResource = {
form_password_pwned__sign_in:
'Mật khẩu này đã được tìm thấy trong một rò rỉ và không thể được sử dụng, vui lòng đặt lại mật khẩu của bạn.',
form_password_size_in_bytes_exceeded: undefined,
form_password_untrusted__sign_in: undefined,
form_password_untrusted__sign_in:
'Mật khẩu của bạn có thể đã bị xâm phạm. Để bảo vệ tài khoản, vui lòng tiếp tục bằng một phương thức đăng nhập khác. Bạn sẽ được yêu cầu đặt lại mật khẩu sau khi đăng nhập.',
form_password_validation_failed: undefined,
form_username_invalid_character: undefined,
form_username_invalid_length: 'Tên người dùng của bạn phải có giữa {{min_length}} và {{max_length}} ký tự.',
Expand All@@ -1060,10 +1071,13 @@ export const viVN: LocalizationResource = {
organization_domain_blocked: undefined,
organization_domain_common: undefined,
organization_domain_exists_for_enterprise_connection: undefined,
organization_membership_quota_exceeded: undefined,
organization_membership_quota_exceeded:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, the source of this is "You have reached your limit of {maxAllowed} organization memberships, including outstanding invitations.".

Can you confirm the translation have the same meaning?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — the meaning is correct, but I dropped the {maxAllowed} interpolation. I worked from packages/localizations/src/en-US.ts, which itself reads "You have reached your limit of organization memberships, including outstanding invitations." with no placeholder, so I missed it. Updating the Vietnamese to:

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.'

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.',
organization_minimum_permissions_needed: undefined,
organization_not_found_or_unauthorized: undefined,
organization_not_found_or_unauthorized_with_create_organization_disabled: undefined,
organization_not_found_or_unauthorized:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn hoặc tạo một tổ chức khác.',
organization_not_found_or_unauthorized_with_create_organization_disabled:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn một tổ chức khác.',
passkey_already_exists: 'Mã passkey đã được đăng ký với thiết bị này.',
passkey_not_supported: 'Mã passkey không được hỗ trợ trên thiết bị này.',
passkey_pa_not_supported: 'Đăng ký yêu cầu một bộ xác thực nền tảng nhưng thiết bị không hỗ trợ nó.',
Expand DownExpand Up@@ -1125,9 +1139,9 @@ export const viVN: LocalizationResource = {
},
userButton: {
action__addAccount: 'Thêm tài khoản',
action__closeUserMenu: undefined,
action__closeUserMenu: 'Đóng menu người dùng',
action__manageAccount: 'Quản lý tài khoản',
action__openUserMenu: undefined,
action__openUserMenu: 'Mở menu người dùng',
action__signOut: 'Đăng xuất',
action__signOutAll: 'Đăng xuất tất cả tài khoản',
},
Expand DownExpand Up@@ -1195,7 +1209,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(localizations): add missing vi-VN (Vietnamese) translations by ANIBIT14 · Pull Request #8663 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/vi-vn-translations.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/localizations': patch
---

Add missing Vietnamese (Vietnam) (`vi-VN`) translations. Fills in previously untranslated keys across billing, checkout, subscription details, organization switcher, enterprise connections, password reset and two-step verification (MFA) setup tasks, frontend API errors, and the user button menu.
204 changes: 109 additions & 95 deletions packages/localizations/src/vi-VN.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,16 +60,16 @@ export const viVN: LocalizationResource = {
badge__default: 'Mặc định',
badge__endsAt: "Kết thúc {{ date | shortDate('vi-VN') }}",
badge__expired: 'Đã hết hạn',
badge__freeTrial: undefined,
badge__freeTrial: 'Dùng thử miễn phí',
badge__otherImpersonatorDevice: 'Thiết bị giả mạo khác',
badge__pastDueAt: undefined,
badge__pastDuePlan: undefined,
badge__pastDueAt: "Quá hạn {{ date | shortDate('vi-VN') }}",
badge__pastDuePlan: 'Quá hạn',
badge__primary: 'Chính',
badge__renewsAt: "Gia hạn {{ date | shortDate('vi-VN') }}",
badge__requiresAction: 'Yêu cầu hành động',
badge__startsAt: "Bắt đầu {{ date | shortDate('vi-VN') }}",
badge__thisDevice: 'Thiết bị này',
badge__trialEndsAt: undefined,
badge__trialEndsAt: "Kết thúc dùng thử {{ date | shortDate('vi-VN') }}",
badge__unverified: 'Chưa xác minh',
badge__upcomingPlan: 'Sắp tới',
badge__userDevice: 'Thiết bị người dùng',
Expand All@@ -81,18 +81,20 @@ export const viVN: LocalizationResource = {
availableFeatures: 'Tính năng có sẵn',
billedAnnually: 'Tính phí hàng năm',
billedMonthlyOnly: 'Chỉ tính phí hàng tháng',
cancelFreeTrial: undefined,
cancelFreeTrialAccessUntil: undefined,
cancelFreeTrialTitle: undefined,
cancelFreeTrial: 'Hủy dùng thử miễn phí',
cancelFreeTrialAccessUntil:
"Bản dùng thử của bạn sẽ vẫn hoạt động cho đến {{ date | longDate('vi-VN') }}. Sau đó, bạn sẽ mất quyền truy cập vào các tính năng dùng thử. Bạn sẽ không bị tính phí.",
cancelFreeTrialTitle: 'Hủy dùng thử miễn phí cho gói {{plan}}?',
cancelSubscription: 'Hủy đăng ký',
cancelSubscriptionAccessUntil:
"Bạn có thể tiếp tục sử dụng tính năng '{{plan}}' cho đến {{ date | longDate('vi-VN') }}, sau đó bạn sẽ không còn quyền truy cập.",
cancelSubscriptionNoCharge: 'Bạn sẽ không bị tính phí cho đăng ký này.',
cancelSubscriptionPastDue: undefined,
cancelSubscriptionPastDue:
'Gói đăng ký của bạn sẽ kết thúc ngay lập tức và bạn sẽ mất quyền truy cập vào tất cả các tính năng của gói. Bạn sẽ được yêu cầu thanh toán số tiền quá hạn trong lần đăng ký tiếp theo.',
cancelSubscriptionTitle: 'Hủy đăng ký {{plan}}?',
cannotSubscribeMonthly:
'Bạn không thể đăng ký gói này bằng cách thanh toán hàng tháng. Để đăng ký gói này, bạn cần chọn thanh toán hàng năm.',
cannotSubscribeUnrecoverable: undefined,
cannotSubscribeUnrecoverable: 'Bạn không thể đăng ký gói này. Gói đăng ký hiện tại của bạn đắt hơn gói này.',
checkout: {
description__paymentSuccessful: 'Thanh toán của bạn đã thành công.',
description__subscriptionSuccessful: 'Đăng ký mới của bạn đã được thiết lập.',
Expand All@@ -103,7 +105,7 @@ export const viVN: LocalizationResource = {
title: 'Thêm địa chỉ email',
},
lineItems: {
title__freeTrialEndsAt: undefined,
title__freeTrialEndsAt: 'Dùng thử kết thúc vào',
title__paymentMethod: 'Phương thức thanh toán',
title__statementId: 'ID hóa đơn',
title__subscriptionBegins: 'Đăng ký bắt đầu',
Expand All@@ -114,15 +116,15 @@ export const viVN: LocalizationResource = {
title: 'Thanh toán',
title__paymentSuccessful: 'Thanh toán thành công!',
title__subscriptionSuccessful: 'Thành công!',
title__trialSuccess: undefined,
totalDueAfterTrial: undefined,
title__trialSuccess: 'Đã bắt đầu dùng thử thành công!',
totalDueAfterTrial: 'Tổng số tiền phải trả sau khi dùng thử kết thúc trong {{days}} ngày',
},
credit: 'Tín dụng',
creditRemainder: 'Tín dụng cho phần còn lại của đăng ký hiện tại.',
defaultFreePlanActive: 'Bạn hiện đang trên gói Miễn phí',
free: 'Miễn phí',
getStarted: 'Bắt đầu',
keepFreeTrial: undefined,
keepFreeTrial: 'Giữ bản dùng thử miễn phí',
keepSubscription: 'Giữ đăng ký',
manage: 'Quản lý',
manageSubscription: 'Quản lý đăng ký',
Expand DownExpand Up@@ -152,34 +154,34 @@ export const viVN: LocalizationResource = {
},
reSubscribe: 'Đăng ký lại',
seeAllFeatures: 'Xem tất cả tính năng',
startFreeTrial: undefined,
startFreeTrial__days: undefined,
startFreeTrial: 'Bắt đầu dùng thử miễn phí',
startFreeTrial__days: 'Bắt đầu dùng thử miễn phí {{days}} ngày',
subscribe: 'Đăng ký',
subscriptionDetails: {
beginsOn: undefined,
currentBillingCycle: undefined,
endsOn: undefined,
firstPaymentAmount: undefined,
firstPaymentOn: undefined,
nextPaymentAmount: undefined,
nextPaymentOn: undefined,
pastDueAt: undefined,
renewsAt: undefined,
subscribedOn: undefined,
title: undefined,
trialEndsOn: undefined,
trialStartedOn: undefined,
beginsOn: 'Bắt đầu vào',
currentBillingCycle: 'Chu kỳ thanh toán hiện tại',
endsOn: 'Kết thúc vào',
firstPaymentAmount: 'Số tiền thanh toán đầu tiên',
firstPaymentOn: 'Thanh toán đầu tiên vào',
nextPaymentAmount: 'Số tiền thanh toán tiếp theo',
nextPaymentOn: 'Thanh toán tiếp theo vào',
pastDueAt: 'Quá hạn vào',
renewsAt: 'Gia hạn vào',
subscribedOn: 'Đăng ký vào',
title: 'Gói đăng ký',
trialEndsOn: 'Dùng thử kết thúc vào',
trialStartedOn: 'Dùng thử bắt đầu vào',
},
subtotal: 'Tổng cộng',
switchPlan: 'Chuyển sang gói này',
switchToAnnual: 'Chuyển sang hàng năm',
switchToAnnualWithAnnualPrice: undefined,
switchToAnnualWithAnnualPrice: 'Chuyển sang gói năm {{currency}}{{price}} / năm',
switchToMonthly: 'Chuyển sang hàng tháng',
switchToMonthlyWithPrice: undefined,
switchToMonthlyWithPrice: 'Chuyển sang gói tháng {{currency}}{{price}} / tháng',
totalDue: 'Tổng cần thanh toán',
totalDueToday: 'Tổng cần thanh toán hôm nay',
viewFeatures: 'Xem tính năng',
viewPayment: undefined,
viewPayment: 'Xem thanh toán',
year: 'Năm',
},
configureSSO: {
Expand DownExpand Up@@ -271,8 +273,8 @@ export const viVN: LocalizationResource = {
formFieldInputPlaceholder__organizationSlug: 'my-org',
formFieldInputPlaceholder__password: 'Nhập mật khẩu của bạn',
formFieldInputPlaceholder__phoneNumber: 'Nhập số điện thoại của bạn',
formFieldInputPlaceholder__username: undefined,
formFieldInput__emailAddress_format: undefined,
formFieldInputPlaceholder__username: 'Nhập tên người dùng của bạn',
formFieldInput__emailAddress_format: 'Định dạng ví dụ: name@example.com',
formFieldLabel__apiKey: 'Khóa API',
formFieldLabel__apiKeyDescription: 'Mô tả',
formFieldLabel__apiKeyExpiration: 'Hết hạn',
Expand DownExpand Up@@ -373,7 +375,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand DownExpand Up@@ -549,11 +551,11 @@ export const viVN: LocalizationResource = {
},
},
organizationSwitcher: {
action__closeOrganizationSwitcher: undefined,
action__closeOrganizationSwitcher: 'Đóng menu chuyển đổi tổ chức',
action__createOrganization: 'Tạo tổ chức',
action__invitationAccept: 'Tham gia',
action__manageOrganization: 'Quản lý',
action__openOrganizationSwitcher: undefined,
action__openOrganizationSwitcher: 'Mở menu chuyển đổi tổ chức',
action__suggestionsAccept: 'Yêu cầu tham gia',
notSelected: 'Không có tổ chức được chọn',
personalWorkspace: 'Tài khoản cá nhân',
Expand DownExpand Up@@ -716,8 +718,8 @@ export const viVN: LocalizationResource = {
title: 'Kiểm tra email của bạn',
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
forgotPassword: {
formTitle: 'Mã xác minh mật khẩu',
Expand DownExpand Up@@ -750,13 +752,13 @@ export const viVN: LocalizationResource = {
title: 'Nhập mật khẩu',
},
passwordCompromised: {
title: undefined,
title: 'Mật khẩu đã bị xâm phạm',
},
passwordPwned: {
title: 'Mật khẩu bị rò rỉ',
},
passwordUntrusted: {
title: undefined,
title: 'Mật khẩu không đáng tin cậy',
},
phoneCode: {
formTitle: 'Mã xác minh',
Expand DownExpand Up@@ -853,8 +855,8 @@ export const viVN: LocalizationResource = {
},
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
legalConsent: {
checkbox: {
Expand DownExpand Up@@ -942,74 +944,82 @@ export const viVN: LocalizationResource = {
},
},
taskResetPassword: {
formButtonPrimary: undefined,
formButtonPrimary: 'Đặt lại mật khẩu',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
subtitle: undefined,
title: undefined,
subtitle: 'Tài khoản của bạn yêu cầu mật khẩu mới trước khi bạn có thể tiếp tục',
title: 'Đặt lại mật khẩu của bạn',
},
taskSetupMfa: {
badge: undefined,
badge: 'Thiết lập xác minh hai bước',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
smsCode: {
addPhone: {
formButtonPrimary: undefined,
infoText: undefined,
formButtonPrimary: 'Tiếp tục',
infoText:
'Một tin nhắn văn bản chứa mã xác minh sẽ được gửi đến số điện thoại này. Có thể áp dụng cước phí tin nhắn và dữ liệu.',
},
addPhoneNumber: undefined,
cancel: undefined,
subtitle: undefined,
addPhoneNumber: 'Thêm số điện thoại',
cancel: 'Hủy',
subtitle: 'Chọn số điện thoại bạn muốn sử dụng để xác minh hai bước bằng mã SMS',
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
},
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh được gửi đến số điện thoại này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng mã SMS',
},
title: 'Thêm xác minh bằng mã SMS',
verifyPhone: {
formButtonPrimary: undefined,
formTitle: undefined,
resendButton: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formTitle: 'Mã xác minh',
resendButton: 'Không nhận được mã? Gửi lại',
subtitle: 'Nhập mã xác minh được gửi đến',
title: 'Xác minh số điện thoại của bạn',
},
},
start: {
methodSelection: {
phoneCode: undefined,
totp: undefined,
phoneCode: 'Mã SMS',
totp: 'Ứng dụng xác thực',
},
subtitle: undefined,
title: undefined,
subtitle: 'Chọn phương thức bạn muốn sử dụng để bảo vệ tài khoản của mình bằng một lớp bảo mật bổ sung',
title: 'Thiết lập xác minh hai bước',
},
totpCode: {
addAuthenticatorApp: {
buttonAbleToScan__nonPrimary: undefined,
buttonUnableToScan__nonPrimary: undefined,
formButtonPrimary: undefined,
formButtonReset: undefined,
infoText__ableToScan: undefined,
infoText__unableToScan: undefined,
inputLabel__unableToScan1: undefined,
buttonAbleToScan__nonPrimary: 'Thay vào đó, quét mã QR',
buttonUnableToScan__nonPrimary: 'Không thể quét mã QR?',
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
infoText__ableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và quét mã QR sau đây để liên kết nó với tài khoản của bạn.',
infoText__unableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và nhập Khóa được cung cấp bên dưới.',
inputLabel__unableToScan1:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads quite literally in Vietnamese. “Mật khẩu dựa trên thời gian hoặc Mật khẩu một lần” sounds machine-translated and “mật khẩu” is awkward here; “mã” would be
more natural for TOTP/OTP. Can you rephrase this to something more idiomatic and align it with the wording already used elsewhere in the locale?

Can you confirm this review by Codex if it's legit?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — in Vietnamese tech UI, TOTP/OTP are conventionally rendered as "mã" (code), not "mật khẩu" (password). Updating to:

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.'

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.',
},
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh từ ứng dụng xác thực này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng ứng dụng xác thực',
},
title: undefined,
title: 'Thêm ứng dụng xác thực',
verifyTotp: {
formButtonPrimary: undefined,
formButtonReset: undefined,
formTitle: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
formTitle: 'Mã xác minh',
subtitle: 'Nhập mã xác minh được tạo bởi ứng dụng xác thực của bạn',
title: 'Thêm ứng dụng xác thực',
},
},
},
Expand All@@ -1027,7 +1037,7 @@ export const viVN: LocalizationResource = {
form_identifier_exists__phone_number: undefined,
form_identifier_exists__username: undefined,
form_identifier_not_found: undefined,
form_new_password_matches_current: undefined,
form_new_password_matches_current: 'Mật khẩu mới không được trùng với mật khẩu hiện tại.',
form_param_format_invalid: undefined,
form_param_format_invalid__email_address: undefined,
form_param_format_invalid__phone_number: undefined,
Expand All@@ -1050,7 +1060,8 @@ export const viVN: LocalizationResource = {
form_password_pwned__sign_in:
'Mật khẩu này đã được tìm thấy trong một rò rỉ và không thể được sử dụng, vui lòng đặt lại mật khẩu của bạn.',
form_password_size_in_bytes_exceeded: undefined,
form_password_untrusted__sign_in: undefined,
form_password_untrusted__sign_in:
'Mật khẩu của bạn có thể đã bị xâm phạm. Để bảo vệ tài khoản, vui lòng tiếp tục bằng một phương thức đăng nhập khác. Bạn sẽ được yêu cầu đặt lại mật khẩu sau khi đăng nhập.',
form_password_validation_failed: undefined,
form_username_invalid_character: undefined,
form_username_invalid_length: 'Tên người dùng của bạn phải có giữa {{min_length}} và {{max_length}} ký tự.',
Expand All@@ -1060,10 +1071,13 @@ export const viVN: LocalizationResource = {
organization_domain_blocked: undefined,
organization_domain_common: undefined,
organization_domain_exists_for_enterprise_connection: undefined,
organization_membership_quota_exceeded: undefined,
organization_membership_quota_exceeded:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, the source of this is "You have reached your limit of {maxAllowed} organization memberships, including outstanding invitations.".

Can you confirm the translation have the same meaning?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — the meaning is correct, but I dropped the {maxAllowed} interpolation. I worked from packages/localizations/src/en-US.ts, which itself reads "You have reached your limit of organization memberships, including outstanding invitations." with no placeholder, so I missed it. Updating the Vietnamese to:

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.'

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.',
organization_minimum_permissions_needed: undefined,
organization_not_found_or_unauthorized: undefined,
organization_not_found_or_unauthorized_with_create_organization_disabled: undefined,
organization_not_found_or_unauthorized:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn hoặc tạo một tổ chức khác.',
organization_not_found_or_unauthorized_with_create_organization_disabled:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn một tổ chức khác.',
passkey_already_exists: 'Mã passkey đã được đăng ký với thiết bị này.',
passkey_not_supported: 'Mã passkey không được hỗ trợ trên thiết bị này.',
passkey_pa_not_supported: 'Đăng ký yêu cầu một bộ xác thực nền tảng nhưng thiết bị không hỗ trợ nó.',
Expand DownExpand Up@@ -1125,9 +1139,9 @@ export const viVN: LocalizationResource = {
},
userButton: {
action__addAccount: 'Thêm tài khoản',
action__closeUserMenu: undefined,
action__closeUserMenu: 'Đóng menu người dùng',
action__manageAccount: 'Quản lý tài khoản',
action__openUserMenu: undefined,
action__openUserMenu: 'Mở menu người dùng',
action__signOut: 'Đăng xuất',
action__signOutAll: 'Đăng xuất tất cả tài khoản',
},
Expand DownExpand Up@@ -1195,7 +1209,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(localizations): add missing vi-VN (Vietnamese) translations by ANIBIT14 · Pull Request #8663 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/vi-vn-translations.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/localizations': patch
---

Add missing Vietnamese (Vietnam) (`vi-VN`) translations. Fills in previously untranslated keys across billing, checkout, subscription details, organization switcher, enterprise connections, password reset and two-step verification (MFA) setup tasks, frontend API errors, and the user button menu.
204 changes: 109 additions & 95 deletions packages/localizations/src/vi-VN.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,16 +60,16 @@ export const viVN: LocalizationResource = {
badge__default: 'Mặc định',
badge__endsAt: "Kết thúc {{ date | shortDate('vi-VN') }}",
badge__expired: 'Đã hết hạn',
badge__freeTrial: undefined,
badge__freeTrial: 'Dùng thử miễn phí',
badge__otherImpersonatorDevice: 'Thiết bị giả mạo khác',
badge__pastDueAt: undefined,
badge__pastDuePlan: undefined,
badge__pastDueAt: "Quá hạn {{ date | shortDate('vi-VN') }}",
badge__pastDuePlan: 'Quá hạn',
badge__primary: 'Chính',
badge__renewsAt: "Gia hạn {{ date | shortDate('vi-VN') }}",
badge__requiresAction: 'Yêu cầu hành động',
badge__startsAt: "Bắt đầu {{ date | shortDate('vi-VN') }}",
badge__thisDevice: 'Thiết bị này',
badge__trialEndsAt: undefined,
badge__trialEndsAt: "Kết thúc dùng thử {{ date | shortDate('vi-VN') }}",
badge__unverified: 'Chưa xác minh',
badge__upcomingPlan: 'Sắp tới',
badge__userDevice: 'Thiết bị người dùng',
Expand All@@ -81,18 +81,20 @@ export const viVN: LocalizationResource = {
availableFeatures: 'Tính năng có sẵn',
billedAnnually: 'Tính phí hàng năm',
billedMonthlyOnly: 'Chỉ tính phí hàng tháng',
cancelFreeTrial: undefined,
cancelFreeTrialAccessUntil: undefined,
cancelFreeTrialTitle: undefined,
cancelFreeTrial: 'Hủy dùng thử miễn phí',
cancelFreeTrialAccessUntil:
"Bản dùng thử của bạn sẽ vẫn hoạt động cho đến {{ date | longDate('vi-VN') }}. Sau đó, bạn sẽ mất quyền truy cập vào các tính năng dùng thử. Bạn sẽ không bị tính phí.",
cancelFreeTrialTitle: 'Hủy dùng thử miễn phí cho gói {{plan}}?',
cancelSubscription: 'Hủy đăng ký',
cancelSubscriptionAccessUntil:
"Bạn có thể tiếp tục sử dụng tính năng '{{plan}}' cho đến {{ date | longDate('vi-VN') }}, sau đó bạn sẽ không còn quyền truy cập.",
cancelSubscriptionNoCharge: 'Bạn sẽ không bị tính phí cho đăng ký này.',
cancelSubscriptionPastDue: undefined,
cancelSubscriptionPastDue:
'Gói đăng ký của bạn sẽ kết thúc ngay lập tức và bạn sẽ mất quyền truy cập vào tất cả các tính năng của gói. Bạn sẽ được yêu cầu thanh toán số tiền quá hạn trong lần đăng ký tiếp theo.',
cancelSubscriptionTitle: 'Hủy đăng ký {{plan}}?',
cannotSubscribeMonthly:
'Bạn không thể đăng ký gói này bằng cách thanh toán hàng tháng. Để đăng ký gói này, bạn cần chọn thanh toán hàng năm.',
cannotSubscribeUnrecoverable: undefined,
cannotSubscribeUnrecoverable: 'Bạn không thể đăng ký gói này. Gói đăng ký hiện tại của bạn đắt hơn gói này.',
checkout: {
description__paymentSuccessful: 'Thanh toán của bạn đã thành công.',
description__subscriptionSuccessful: 'Đăng ký mới của bạn đã được thiết lập.',
Expand All@@ -103,7 +105,7 @@ export const viVN: LocalizationResource = {
title: 'Thêm địa chỉ email',
},
lineItems: {
title__freeTrialEndsAt: undefined,
title__freeTrialEndsAt: 'Dùng thử kết thúc vào',
title__paymentMethod: 'Phương thức thanh toán',
title__statementId: 'ID hóa đơn',
title__subscriptionBegins: 'Đăng ký bắt đầu',
Expand All@@ -114,15 +116,15 @@ export const viVN: LocalizationResource = {
title: 'Thanh toán',
title__paymentSuccessful: 'Thanh toán thành công!',
title__subscriptionSuccessful: 'Thành công!',
title__trialSuccess: undefined,
totalDueAfterTrial: undefined,
title__trialSuccess: 'Đã bắt đầu dùng thử thành công!',
totalDueAfterTrial: 'Tổng số tiền phải trả sau khi dùng thử kết thúc trong {{days}} ngày',
},
credit: 'Tín dụng',
creditRemainder: 'Tín dụng cho phần còn lại của đăng ký hiện tại.',
defaultFreePlanActive: 'Bạn hiện đang trên gói Miễn phí',
free: 'Miễn phí',
getStarted: 'Bắt đầu',
keepFreeTrial: undefined,
keepFreeTrial: 'Giữ bản dùng thử miễn phí',
keepSubscription: 'Giữ đăng ký',
manage: 'Quản lý',
manageSubscription: 'Quản lý đăng ký',
Expand DownExpand Up@@ -152,34 +154,34 @@ export const viVN: LocalizationResource = {
},
reSubscribe: 'Đăng ký lại',
seeAllFeatures: 'Xem tất cả tính năng',
startFreeTrial: undefined,
startFreeTrial__days: undefined,
startFreeTrial: 'Bắt đầu dùng thử miễn phí',
startFreeTrial__days: 'Bắt đầu dùng thử miễn phí {{days}} ngày',
subscribe: 'Đăng ký',
subscriptionDetails: {
beginsOn: undefined,
currentBillingCycle: undefined,
endsOn: undefined,
firstPaymentAmount: undefined,
firstPaymentOn: undefined,
nextPaymentAmount: undefined,
nextPaymentOn: undefined,
pastDueAt: undefined,
renewsAt: undefined,
subscribedOn: undefined,
title: undefined,
trialEndsOn: undefined,
trialStartedOn: undefined,
beginsOn: 'Bắt đầu vào',
currentBillingCycle: 'Chu kỳ thanh toán hiện tại',
endsOn: 'Kết thúc vào',
firstPaymentAmount: 'Số tiền thanh toán đầu tiên',
firstPaymentOn: 'Thanh toán đầu tiên vào',
nextPaymentAmount: 'Số tiền thanh toán tiếp theo',
nextPaymentOn: 'Thanh toán tiếp theo vào',
pastDueAt: 'Quá hạn vào',
renewsAt: 'Gia hạn vào',
subscribedOn: 'Đăng ký vào',
title: 'Gói đăng ký',
trialEndsOn: 'Dùng thử kết thúc vào',
trialStartedOn: 'Dùng thử bắt đầu vào',
},
subtotal: 'Tổng cộng',
switchPlan: 'Chuyển sang gói này',
switchToAnnual: 'Chuyển sang hàng năm',
switchToAnnualWithAnnualPrice: undefined,
switchToAnnualWithAnnualPrice: 'Chuyển sang gói năm {{currency}}{{price}} / năm',
switchToMonthly: 'Chuyển sang hàng tháng',
switchToMonthlyWithPrice: undefined,
switchToMonthlyWithPrice: 'Chuyển sang gói tháng {{currency}}{{price}} / tháng',
totalDue: 'Tổng cần thanh toán',
totalDueToday: 'Tổng cần thanh toán hôm nay',
viewFeatures: 'Xem tính năng',
viewPayment: undefined,
viewPayment: 'Xem thanh toán',
year: 'Năm',
},
configureSSO: {
Expand DownExpand Up@@ -271,8 +273,8 @@ export const viVN: LocalizationResource = {
formFieldInputPlaceholder__organizationSlug: 'my-org',
formFieldInputPlaceholder__password: 'Nhập mật khẩu của bạn',
formFieldInputPlaceholder__phoneNumber: 'Nhập số điện thoại của bạn',
formFieldInputPlaceholder__username: undefined,
formFieldInput__emailAddress_format: undefined,
formFieldInputPlaceholder__username: 'Nhập tên người dùng của bạn',
formFieldInput__emailAddress_format: 'Định dạng ví dụ: name@example.com',
formFieldLabel__apiKey: 'Khóa API',
formFieldLabel__apiKeyDescription: 'Mô tả',
formFieldLabel__apiKeyExpiration: 'Hết hạn',
Expand DownExpand Up@@ -373,7 +375,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand DownExpand Up@@ -549,11 +551,11 @@ export const viVN: LocalizationResource = {
},
},
organizationSwitcher: {
action__closeOrganizationSwitcher: undefined,
action__closeOrganizationSwitcher: 'Đóng menu chuyển đổi tổ chức',
action__createOrganization: 'Tạo tổ chức',
action__invitationAccept: 'Tham gia',
action__manageOrganization: 'Quản lý',
action__openOrganizationSwitcher: undefined,
action__openOrganizationSwitcher: 'Mở menu chuyển đổi tổ chức',
action__suggestionsAccept: 'Yêu cầu tham gia',
notSelected: 'Không có tổ chức được chọn',
personalWorkspace: 'Tài khoản cá nhân',
Expand DownExpand Up@@ -716,8 +718,8 @@ export const viVN: LocalizationResource = {
title: 'Kiểm tra email của bạn',
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
forgotPassword: {
formTitle: 'Mã xác minh mật khẩu',
Expand DownExpand Up@@ -750,13 +752,13 @@ export const viVN: LocalizationResource = {
title: 'Nhập mật khẩu',
},
passwordCompromised: {
title: undefined,
title: 'Mật khẩu đã bị xâm phạm',
},
passwordPwned: {
title: 'Mật khẩu bị rò rỉ',
},
passwordUntrusted: {
title: undefined,
title: 'Mật khẩu không đáng tin cậy',
},
phoneCode: {
formTitle: 'Mã xác minh',
Expand DownExpand Up@@ -853,8 +855,8 @@ export const viVN: LocalizationResource = {
},
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
legalConsent: {
checkbox: {
Expand DownExpand Up@@ -942,74 +944,82 @@ export const viVN: LocalizationResource = {
},
},
taskResetPassword: {
formButtonPrimary: undefined,
formButtonPrimary: 'Đặt lại mật khẩu',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
subtitle: undefined,
title: undefined,
subtitle: 'Tài khoản của bạn yêu cầu mật khẩu mới trước khi bạn có thể tiếp tục',
title: 'Đặt lại mật khẩu của bạn',
},
taskSetupMfa: {
badge: undefined,
badge: 'Thiết lập xác minh hai bước',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
smsCode: {
addPhone: {
formButtonPrimary: undefined,
infoText: undefined,
formButtonPrimary: 'Tiếp tục',
infoText:
'Một tin nhắn văn bản chứa mã xác minh sẽ được gửi đến số điện thoại này. Có thể áp dụng cước phí tin nhắn và dữ liệu.',
},
addPhoneNumber: undefined,
cancel: undefined,
subtitle: undefined,
addPhoneNumber: 'Thêm số điện thoại',
cancel: 'Hủy',
subtitle: 'Chọn số điện thoại bạn muốn sử dụng để xác minh hai bước bằng mã SMS',
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
},
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh được gửi đến số điện thoại này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng mã SMS',
},
title: 'Thêm xác minh bằng mã SMS',
verifyPhone: {
formButtonPrimary: undefined,
formTitle: undefined,
resendButton: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formTitle: 'Mã xác minh',
resendButton: 'Không nhận được mã? Gửi lại',
subtitle: 'Nhập mã xác minh được gửi đến',
title: 'Xác minh số điện thoại của bạn',
},
},
start: {
methodSelection: {
phoneCode: undefined,
totp: undefined,
phoneCode: 'Mã SMS',
totp: 'Ứng dụng xác thực',
},
subtitle: undefined,
title: undefined,
subtitle: 'Chọn phương thức bạn muốn sử dụng để bảo vệ tài khoản của mình bằng một lớp bảo mật bổ sung',
title: 'Thiết lập xác minh hai bước',
},
totpCode: {
addAuthenticatorApp: {
buttonAbleToScan__nonPrimary: undefined,
buttonUnableToScan__nonPrimary: undefined,
formButtonPrimary: undefined,
formButtonReset: undefined,
infoText__ableToScan: undefined,
infoText__unableToScan: undefined,
inputLabel__unableToScan1: undefined,
buttonAbleToScan__nonPrimary: 'Thay vào đó, quét mã QR',
buttonUnableToScan__nonPrimary: 'Không thể quét mã QR?',
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
infoText__ableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và quét mã QR sau đây để liên kết nó với tài khoản của bạn.',
infoText__unableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và nhập Khóa được cung cấp bên dưới.',
inputLabel__unableToScan1:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads quite literally in Vietnamese. “Mật khẩu dựa trên thời gian hoặc Mật khẩu một lần” sounds machine-translated and “mật khẩu” is awkward here; “mã” would be
more natural for TOTP/OTP. Can you rephrase this to something more idiomatic and align it with the wording already used elsewhere in the locale?

Can you confirm this review by Codex if it's legit?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — in Vietnamese tech UI, TOTP/OTP are conventionally rendered as "mã" (code), not "mật khẩu" (password). Updating to:

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.'

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.',
},
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh từ ứng dụng xác thực này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng ứng dụng xác thực',
},
title: undefined,
title: 'Thêm ứng dụng xác thực',
verifyTotp: {
formButtonPrimary: undefined,
formButtonReset: undefined,
formTitle: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
formTitle: 'Mã xác minh',
subtitle: 'Nhập mã xác minh được tạo bởi ứng dụng xác thực của bạn',
title: 'Thêm ứng dụng xác thực',
},
},
},
Expand All@@ -1027,7 +1037,7 @@ export const viVN: LocalizationResource = {
form_identifier_exists__phone_number: undefined,
form_identifier_exists__username: undefined,
form_identifier_not_found: undefined,
form_new_password_matches_current: undefined,
form_new_password_matches_current: 'Mật khẩu mới không được trùng với mật khẩu hiện tại.',
form_param_format_invalid: undefined,
form_param_format_invalid__email_address: undefined,
form_param_format_invalid__phone_number: undefined,
Expand All@@ -1050,7 +1060,8 @@ export const viVN: LocalizationResource = {
form_password_pwned__sign_in:
'Mật khẩu này đã được tìm thấy trong một rò rỉ và không thể được sử dụng, vui lòng đặt lại mật khẩu của bạn.',
form_password_size_in_bytes_exceeded: undefined,
form_password_untrusted__sign_in: undefined,
form_password_untrusted__sign_in:
'Mật khẩu của bạn có thể đã bị xâm phạm. Để bảo vệ tài khoản, vui lòng tiếp tục bằng một phương thức đăng nhập khác. Bạn sẽ được yêu cầu đặt lại mật khẩu sau khi đăng nhập.',
form_password_validation_failed: undefined,
form_username_invalid_character: undefined,
form_username_invalid_length: 'Tên người dùng của bạn phải có giữa {{min_length}} và {{max_length}} ký tự.',
Expand All@@ -1060,10 +1071,13 @@ export const viVN: LocalizationResource = {
organization_domain_blocked: undefined,
organization_domain_common: undefined,
organization_domain_exists_for_enterprise_connection: undefined,
organization_membership_quota_exceeded: undefined,
organization_membership_quota_exceeded:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, the source of this is "You have reached your limit of {maxAllowed} organization memberships, including outstanding invitations.".

Can you confirm the translation have the same meaning?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — the meaning is correct, but I dropped the {maxAllowed} interpolation. I worked from packages/localizations/src/en-US.ts, which itself reads "You have reached your limit of organization memberships, including outstanding invitations." with no placeholder, so I missed it. Updating the Vietnamese to:

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.'

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.',
organization_minimum_permissions_needed: undefined,
organization_not_found_or_unauthorized: undefined,
organization_not_found_or_unauthorized_with_create_organization_disabled: undefined,
organization_not_found_or_unauthorized:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn hoặc tạo một tổ chức khác.',
organization_not_found_or_unauthorized_with_create_organization_disabled:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn một tổ chức khác.',
passkey_already_exists: 'Mã passkey đã được đăng ký với thiết bị này.',
passkey_not_supported: 'Mã passkey không được hỗ trợ trên thiết bị này.',
passkey_pa_not_supported: 'Đăng ký yêu cầu một bộ xác thực nền tảng nhưng thiết bị không hỗ trợ nó.',
Expand DownExpand Up@@ -1125,9 +1139,9 @@ export const viVN: LocalizationResource = {
},
userButton: {
action__addAccount: 'Thêm tài khoản',
action__closeUserMenu: undefined,
action__closeUserMenu: 'Đóng menu người dùng',
action__manageAccount: 'Quản lý tài khoản',
action__openUserMenu: undefined,
action__openUserMenu: 'Mở menu người dùng',
action__signOut: 'Đăng xuất',
action__signOutAll: 'Đăng xuất tất cả tài khoản',
},
Expand DownExpand Up@@ -1195,7 +1209,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(localizations): add missing vi-VN (Vietnamese) translations by ANIBIT14 · Pull Request #8663 · clerk/javascript · GitHub
Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/vi-vn-translations.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
'@clerk/localizations': patch
---

Add missing Vietnamese (Vietnam) (`vi-VN`) translations. Fills in previously untranslated keys across billing, checkout, subscription details, organization switcher, enterprise connections, password reset and two-step verification (MFA) setup tasks, frontend API errors, and the user button menu.
204 changes: 109 additions & 95 deletions packages/localizations/src/vi-VN.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,16 +60,16 @@ export const viVN: LocalizationResource = {
badge__default: 'Mặc định',
badge__endsAt: "Kết thúc {{ date | shortDate('vi-VN') }}",
badge__expired: 'Đã hết hạn',
badge__freeTrial: undefined,
badge__freeTrial: 'Dùng thử miễn phí',
badge__otherImpersonatorDevice: 'Thiết bị giả mạo khác',
badge__pastDueAt: undefined,
badge__pastDuePlan: undefined,
badge__pastDueAt: "Quá hạn {{ date | shortDate('vi-VN') }}",
badge__pastDuePlan: 'Quá hạn',
badge__primary: 'Chính',
badge__renewsAt: "Gia hạn {{ date | shortDate('vi-VN') }}",
badge__requiresAction: 'Yêu cầu hành động',
badge__startsAt: "Bắt đầu {{ date | shortDate('vi-VN') }}",
badge__thisDevice: 'Thiết bị này',
badge__trialEndsAt: undefined,
badge__trialEndsAt: "Kết thúc dùng thử {{ date | shortDate('vi-VN') }}",
badge__unverified: 'Chưa xác minh',
badge__upcomingPlan: 'Sắp tới',
badge__userDevice: 'Thiết bị người dùng',
Expand All@@ -81,18 +81,20 @@ export const viVN: LocalizationResource = {
availableFeatures: 'Tính năng có sẵn',
billedAnnually: 'Tính phí hàng năm',
billedMonthlyOnly: 'Chỉ tính phí hàng tháng',
cancelFreeTrial: undefined,
cancelFreeTrialAccessUntil: undefined,
cancelFreeTrialTitle: undefined,
cancelFreeTrial: 'Hủy dùng thử miễn phí',
cancelFreeTrialAccessUntil:
"Bản dùng thử của bạn sẽ vẫn hoạt động cho đến {{ date | longDate('vi-VN') }}. Sau đó, bạn sẽ mất quyền truy cập vào các tính năng dùng thử. Bạn sẽ không bị tính phí.",
cancelFreeTrialTitle: 'Hủy dùng thử miễn phí cho gói {{plan}}?',
cancelSubscription: 'Hủy đăng ký',
cancelSubscriptionAccessUntil:
"Bạn có thể tiếp tục sử dụng tính năng '{{plan}}' cho đến {{ date | longDate('vi-VN') }}, sau đó bạn sẽ không còn quyền truy cập.",
cancelSubscriptionNoCharge: 'Bạn sẽ không bị tính phí cho đăng ký này.',
cancelSubscriptionPastDue: undefined,
cancelSubscriptionPastDue:
'Gói đăng ký của bạn sẽ kết thúc ngay lập tức và bạn sẽ mất quyền truy cập vào tất cả các tính năng của gói. Bạn sẽ được yêu cầu thanh toán số tiền quá hạn trong lần đăng ký tiếp theo.',
cancelSubscriptionTitle: 'Hủy đăng ký {{plan}}?',
cannotSubscribeMonthly:
'Bạn không thể đăng ký gói này bằng cách thanh toán hàng tháng. Để đăng ký gói này, bạn cần chọn thanh toán hàng năm.',
cannotSubscribeUnrecoverable: undefined,
cannotSubscribeUnrecoverable: 'Bạn không thể đăng ký gói này. Gói đăng ký hiện tại của bạn đắt hơn gói này.',
checkout: {
description__paymentSuccessful: 'Thanh toán của bạn đã thành công.',
description__subscriptionSuccessful: 'Đăng ký mới của bạn đã được thiết lập.',
Expand All@@ -103,7 +105,7 @@ export const viVN: LocalizationResource = {
title: 'Thêm địa chỉ email',
},
lineItems: {
title__freeTrialEndsAt: undefined,
title__freeTrialEndsAt: 'Dùng thử kết thúc vào',
title__paymentMethod: 'Phương thức thanh toán',
title__statementId: 'ID hóa đơn',
title__subscriptionBegins: 'Đăng ký bắt đầu',
Expand All@@ -114,15 +116,15 @@ export const viVN: LocalizationResource = {
title: 'Thanh toán',
title__paymentSuccessful: 'Thanh toán thành công!',
title__subscriptionSuccessful: 'Thành công!',
title__trialSuccess: undefined,
totalDueAfterTrial: undefined,
title__trialSuccess: 'Đã bắt đầu dùng thử thành công!',
totalDueAfterTrial: 'Tổng số tiền phải trả sau khi dùng thử kết thúc trong {{days}} ngày',
},
credit: 'Tín dụng',
creditRemainder: 'Tín dụng cho phần còn lại của đăng ký hiện tại.',
defaultFreePlanActive: 'Bạn hiện đang trên gói Miễn phí',
free: 'Miễn phí',
getStarted: 'Bắt đầu',
keepFreeTrial: undefined,
keepFreeTrial: 'Giữ bản dùng thử miễn phí',
keepSubscription: 'Giữ đăng ký',
manage: 'Quản lý',
manageSubscription: 'Quản lý đăng ký',
Expand DownExpand Up@@ -152,34 +154,34 @@ export const viVN: LocalizationResource = {
},
reSubscribe: 'Đăng ký lại',
seeAllFeatures: 'Xem tất cả tính năng',
startFreeTrial: undefined,
startFreeTrial__days: undefined,
startFreeTrial: 'Bắt đầu dùng thử miễn phí',
startFreeTrial__days: 'Bắt đầu dùng thử miễn phí {{days}} ngày',
subscribe: 'Đăng ký',
subscriptionDetails: {
beginsOn: undefined,
currentBillingCycle: undefined,
endsOn: undefined,
firstPaymentAmount: undefined,
firstPaymentOn: undefined,
nextPaymentAmount: undefined,
nextPaymentOn: undefined,
pastDueAt: undefined,
renewsAt: undefined,
subscribedOn: undefined,
title: undefined,
trialEndsOn: undefined,
trialStartedOn: undefined,
beginsOn: 'Bắt đầu vào',
currentBillingCycle: 'Chu kỳ thanh toán hiện tại',
endsOn: 'Kết thúc vào',
firstPaymentAmount: 'Số tiền thanh toán đầu tiên',
firstPaymentOn: 'Thanh toán đầu tiên vào',
nextPaymentAmount: 'Số tiền thanh toán tiếp theo',
nextPaymentOn: 'Thanh toán tiếp theo vào',
pastDueAt: 'Quá hạn vào',
renewsAt: 'Gia hạn vào',
subscribedOn: 'Đăng ký vào',
title: 'Gói đăng ký',
trialEndsOn: 'Dùng thử kết thúc vào',
trialStartedOn: 'Dùng thử bắt đầu vào',
},
subtotal: 'Tổng cộng',
switchPlan: 'Chuyển sang gói này',
switchToAnnual: 'Chuyển sang hàng năm',
switchToAnnualWithAnnualPrice: undefined,
switchToAnnualWithAnnualPrice: 'Chuyển sang gói năm {{currency}}{{price}} / năm',
switchToMonthly: 'Chuyển sang hàng tháng',
switchToMonthlyWithPrice: undefined,
switchToMonthlyWithPrice: 'Chuyển sang gói tháng {{currency}}{{price}} / tháng',
totalDue: 'Tổng cần thanh toán',
totalDueToday: 'Tổng cần thanh toán hôm nay',
viewFeatures: 'Xem tính năng',
viewPayment: undefined,
viewPayment: 'Xem thanh toán',
year: 'Năm',
},
configureSSO: {
Expand DownExpand Up@@ -271,8 +273,8 @@ export const viVN: LocalizationResource = {
formFieldInputPlaceholder__organizationSlug: 'my-org',
formFieldInputPlaceholder__password: 'Nhập mật khẩu của bạn',
formFieldInputPlaceholder__phoneNumber: 'Nhập số điện thoại của bạn',
formFieldInputPlaceholder__username: undefined,
formFieldInput__emailAddress_format: undefined,
formFieldInputPlaceholder__username: 'Nhập tên người dùng của bạn',
formFieldInput__emailAddress_format: 'Định dạng ví dụ: name@example.com',
formFieldLabel__apiKey: 'Khóa API',
formFieldLabel__apiKeyDescription: 'Mô tả',
formFieldLabel__apiKeyExpiration: 'Hết hạn',
Expand DownExpand Up@@ -373,7 +375,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand DownExpand Up@@ -549,11 +551,11 @@ export const viVN: LocalizationResource = {
},
},
organizationSwitcher: {
action__closeOrganizationSwitcher: undefined,
action__closeOrganizationSwitcher: 'Đóng menu chuyển đổi tổ chức',
action__createOrganization: 'Tạo tổ chức',
action__invitationAccept: 'Tham gia',
action__manageOrganization: 'Quản lý',
action__openOrganizationSwitcher: undefined,
action__openOrganizationSwitcher: 'Mở menu chuyển đổi tổ chức',
action__suggestionsAccept: 'Yêu cầu tham gia',
notSelected: 'Không có tổ chức được chọn',
personalWorkspace: 'Tài khoản cá nhân',
Expand DownExpand Up@@ -716,8 +718,8 @@ export const viVN: LocalizationResource = {
title: 'Kiểm tra email của bạn',
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
forgotPassword: {
formTitle: 'Mã xác minh mật khẩu',
Expand DownExpand Up@@ -750,13 +752,13 @@ export const viVN: LocalizationResource = {
title: 'Nhập mật khẩu',
},
passwordCompromised: {
title: undefined,
title: 'Mật khẩu đã bị xâm phạm',
},
passwordPwned: {
title: 'Mật khẩu bị rò rỉ',
},
passwordUntrusted: {
title: undefined,
title: 'Mật khẩu không đáng tin cậy',
},
phoneCode: {
formTitle: 'Mã xác minh',
Expand DownExpand Up@@ -853,8 +855,8 @@ export const viVN: LocalizationResource = {
},
},
enterpriseConnections: {
subtitle: undefined,
title: undefined,
subtitle: 'Chọn tài khoản doanh nghiệp bạn muốn dùng để tiếp tục.',
title: 'Chọn tài khoản doanh nghiệp của bạn',
},
legalConsent: {
checkbox: {
Expand DownExpand Up@@ -942,74 +944,82 @@ export const viVN: LocalizationResource = {
},
},
taskResetPassword: {
formButtonPrimary: undefined,
formButtonPrimary: 'Đặt lại mật khẩu',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
subtitle: undefined,
title: undefined,
subtitle: 'Tài khoản của bạn yêu cầu mật khẩu mới trước khi bạn có thể tiếp tục',
title: 'Đặt lại mật khẩu của bạn',
},
taskSetupMfa: {
badge: undefined,
badge: 'Thiết lập xác minh hai bước',
signOut: {
actionLink: undefined,
actionText: undefined,
actionLink: 'Đăng xuất',
actionText: 'Đã đăng nhập với tư cách {{identifier}}',
},
smsCode: {
addPhone: {
formButtonPrimary: undefined,
infoText: undefined,
formButtonPrimary: 'Tiếp tục',
infoText:
'Một tin nhắn văn bản chứa mã xác minh sẽ được gửi đến số điện thoại này. Có thể áp dụng cước phí tin nhắn và dữ liệu.',
},
addPhoneNumber: undefined,
cancel: undefined,
subtitle: undefined,
addPhoneNumber: 'Thêm số điện thoại',
cancel: 'Hủy',
subtitle: 'Chọn số điện thoại bạn muốn sử dụng để xác minh hai bước bằng mã SMS',
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
},
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh được gửi đến số điện thoại này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng mã SMS',
},
title: 'Thêm xác minh bằng mã SMS',
verifyPhone: {
formButtonPrimary: undefined,
formTitle: undefined,
resendButton: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formTitle: 'Mã xác minh',
resendButton: 'Không nhận được mã? Gửi lại',
subtitle: 'Nhập mã xác minh được gửi đến',
title: 'Xác minh số điện thoại của bạn',
},
},
start: {
methodSelection: {
phoneCode: undefined,
totp: undefined,
phoneCode: 'Mã SMS',
totp: 'Ứng dụng xác thực',
},
subtitle: undefined,
title: undefined,
subtitle: 'Chọn phương thức bạn muốn sử dụng để bảo vệ tài khoản của mình bằng một lớp bảo mật bổ sung',
title: 'Thiết lập xác minh hai bước',
},
totpCode: {
addAuthenticatorApp: {
buttonAbleToScan__nonPrimary: undefined,
buttonUnableToScan__nonPrimary: undefined,
formButtonPrimary: undefined,
formButtonReset: undefined,
infoText__ableToScan: undefined,
infoText__unableToScan: undefined,
inputLabel__unableToScan1: undefined,
buttonAbleToScan__nonPrimary: 'Thay vào đó, quét mã QR',
buttonUnableToScan__nonPrimary: 'Không thể quét mã QR?',
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
infoText__ableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và quét mã QR sau đây để liên kết nó với tài khoản của bạn.',
infoText__unableToScan:
'Thiết lập một phương thức đăng nhập mới trong ứng dụng xác thực của bạn và nhập Khóa được cung cấp bên dưới.',
inputLabel__unableToScan1:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads quite literally in Vietnamese. “Mật khẩu dựa trên thời gian hoặc Mật khẩu một lần” sounds machine-translated and “mật khẩu” is awkward here; “mã” would be
more natural for TOTP/OTP. Can you rephrase this to something more idiomatic and align it with the wording already used elsewhere in the locale?

Can you confirm this review by Codex if it's legit?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — in Vietnamese tech UI, TOTP/OTP are conventionally rendered as "mã" (code), not "mật khẩu" (password). Updating to:

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.'

'Đảm bảo rằng Mã dựa trên thời gian hoặc Mã một lần đã được bật, sau đó hoàn tất việc liên kết tài khoản của bạn.',
},
success: {
finishButton: undefined,
message1: undefined,
message2: undefined,
title: undefined,
finishButton: 'Tiếp tục',
message1:
'Xác minh hai bước hiện đã được bật. Khi đăng nhập, bạn sẽ cần nhập mã xác minh từ ứng dụng xác thực này như một bước bổ sung.',
message2:
'Lưu các mã dự phòng này và cất giữ ở nơi an toàn. Nếu bạn mất quyền truy cập vào thiết bị xác thực, bạn có thể sử dụng mã dự phòng để đăng nhập.',
title: 'Đã bật xác minh bằng ứng dụng xác thực',
},
title: undefined,
title: 'Thêm ứng dụng xác thực',
verifyTotp: {
formButtonPrimary: undefined,
formButtonReset: undefined,
formTitle: undefined,
subtitle: undefined,
title: undefined,
formButtonPrimary: 'Tiếp tục',
formButtonReset: 'Hủy',
formTitle: 'Mã xác minh',
subtitle: 'Nhập mã xác minh được tạo bởi ứng dụng xác thực của bạn',
title: 'Thêm ứng dụng xác thực',
},
},
},
Expand All@@ -1027,7 +1037,7 @@ export const viVN: LocalizationResource = {
form_identifier_exists__phone_number: undefined,
form_identifier_exists__username: undefined,
form_identifier_not_found: undefined,
form_new_password_matches_current: undefined,
form_new_password_matches_current: 'Mật khẩu mới không được trùng với mật khẩu hiện tại.',
form_param_format_invalid: undefined,
form_param_format_invalid__email_address: undefined,
form_param_format_invalid__phone_number: undefined,
Expand All@@ -1050,7 +1060,8 @@ export const viVN: LocalizationResource = {
form_password_pwned__sign_in:
'Mật khẩu này đã được tìm thấy trong một rò rỉ và không thể được sử dụng, vui lòng đặt lại mật khẩu của bạn.',
form_password_size_in_bytes_exceeded: undefined,
form_password_untrusted__sign_in: undefined,
form_password_untrusted__sign_in:
'Mật khẩu của bạn có thể đã bị xâm phạm. Để bảo vệ tài khoản, vui lòng tiếp tục bằng một phương thức đăng nhập khác. Bạn sẽ được yêu cầu đặt lại mật khẩu sau khi đăng nhập.',
form_password_validation_failed: undefined,
form_username_invalid_character: undefined,
form_username_invalid_length: 'Tên người dùng của bạn phải có giữa {{min_length}} và {{max_length}} ký tự.',
Expand All@@ -1060,10 +1071,13 @@ export const viVN: LocalizationResource = {
organization_domain_blocked: undefined,
organization_domain_common: undefined,
organization_domain_exists_for_enterprise_connection: undefined,
organization_membership_quota_exceeded: undefined,
organization_membership_quota_exceeded:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, the source of this is "You have reached your limit of {maxAllowed} organization memberships, including outstanding invitations.".

Can you confirm the translation have the same meaning?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — the meaning is correct, but I dropped the {maxAllowed} interpolation. I worked from packages/localizations/src/en-US.ts, which itself reads "You have reached your limit of organization memberships, including outstanding invitations." with no placeholder, so I missed it. Updating the Vietnamese to:

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.'

'Bạn đã đạt đến giới hạn {{maxAllowed}} thành viên tổ chức, bao gồm cả các lời mời đang chờ xử lý.',
organization_minimum_permissions_needed: undefined,
organization_not_found_or_unauthorized: undefined,
organization_not_found_or_unauthorized_with_create_organization_disabled: undefined,
organization_not_found_or_unauthorized:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn hoặc tạo một tổ chức khác.',
organization_not_found_or_unauthorized_with_create_organization_disabled:
'Bạn không còn là thành viên của tổ chức này. Vui lòng chọn một tổ chức khác.',
passkey_already_exists: 'Mã passkey đã được đăng ký với thiết bị này.',
passkey_not_supported: 'Mã passkey không được hỗ trợ trên thiết bị này.',
passkey_pa_not_supported: 'Đăng ký yêu cầu một bộ xác thực nền tảng nhưng thiết bị không hỗ trợ nó.',
Expand DownExpand Up@@ -1125,9 +1139,9 @@ export const viVN: LocalizationResource = {
},
userButton: {
action__addAccount: 'Thêm tài khoản',
action__closeUserMenu: undefined,
action__closeUserMenu: 'Đóng menu người dùng',
action__manageAccount: 'Quản lý tài khoản',
action__openUserMenu: undefined,
action__openUserMenu: 'Mở menu người dùng',
action__signOut: 'Đăng xuất',
action__signOutAll: 'Đăng xuất tất cả tài khoản',
},
Expand DownExpand Up@@ -1195,7 +1209,7 @@ export const viVN: LocalizationResource = {
totalPaid: 'Tổng thanh toán',
},
subscriptionsListSection: {
actionLabel__manageSubscription: undefined,
actionLabel__manageSubscription: 'Quản lý',
actionLabel__newSubscription: 'Đăng ký gói',
actionLabel__switchPlan: 'Chuyển gói',
tableHeader__edit: 'Sửa',
Expand Down