diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 5cf6d62cd..89f315cba 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -4,6 +4,7 @@ "save_changes": "Save Changes", "loading": "Loading...", "error": "Error", + "creating": "Creating...", "success": "Success" }, "settings": { @@ -50,7 +51,15 @@ "title": "Authenticator App Enabled", "description": "Store the codes below somewhere safe. If you lose access to your authenticator app you can use these backup codes to sign in.", "alert": "These codes will not be shown again." + }, + "disable": { + "title": "Remove Authenticator App", + "description": "Removing your authenticator app will make your account less secure." } + }, + "email": { + "update_email": "Update Email", + "updated_successfully": "Your primary email has been updated." } }, "api_key_modal": { @@ -72,7 +81,11 @@ "delete_api_key_desc": "All requests using the {{key}} key will be invalidated.", "no_api_keys": "No API keys exist for this account.", "last_used": "Last used:", - "never": "Never" + "never": "Never", + "key_description": "Description", + "key_description_description": "A description of this API key.", + "allowed_ips": "Allowed IPs", + "allowed_ips_description": "Leave blank to allow any IP address to use this API key, otherwise provide each IP address on a new line." }, "activity_log": { "title": "Account Activity Log", diff --git a/public/locales/vi/translation.json b/public/locales/vi/translation.json index acd420076..cb609a61f 100644 --- a/public/locales/vi/translation.json +++ b/public/locales/vi/translation.json @@ -3,6 +3,7 @@ "language": "Ngôn ngữ", "save_changes": "Lưu thay đổi", "loading": "Đang tải...", + "creating": "Đang tạo...", "error": "Lỗi", "success": "Thành công" }, @@ -50,7 +51,15 @@ "title": "Đã bật ứng dụng xác thực", "description": "Lưu trữ các mã dưới đây ở nơi an toàn. Nếu bạn mất quyền truy cập vào ứng dụng xác thực, bạn có thể sử dụng các mã dự phòng này để đăng nhập.", "alert": "Các mã này sẽ không được hiển thị lại." + }, + "disable": { + "title": "Xóa ứng dụng xác thực", + "description": "Việc xóa ứng dụng xác thực sẽ làm cho tài khoản của bạn kém an toàn hơn." } + }, + "email": { + "update_email": "Cập nhật email", + "updated_successfully": "Email chính của bạn đã được cập nhật." } }, "api_key_modal": { @@ -80,6 +89,21 @@ "delete_confirm": "Xóa Key", "delete_message": "Xóa SSH key {name} sẽ làm mất hiệu lực sử dụng của nó trên toàn bộ Panel." }, + "api": { + "account_api": "API tài khoản", + "create_api_key": "Tạo khóa API", + "api_keys": "Các khóa API", + "delete_api_key_title": "Xóa khóa API", + "delete_key": "Xóa khóa", + "delete_api_key_desc": "Tất cả các yêu cầu sử dụng khóa {{key}} sẽ bị vô hiệu hóa.", + "no_api_keys": "Không có khóa API nào tồn tại cho tài khoản này.", + "last_used": "Sử dụng lần cuối:", + "never": "Chưa bao giờ", + "key_description": "Mô tả", + "key_description_description": "Mô tả về khóa API này.", + "allowed_ips": "Các địa chỉ IP được phép", + "allowed_ips_description": "Để trống để cho phép bất kỳ địa chỉ IP nào sử dụng khóa API này, nếu không, cung cấp từng địa chỉ IP trên một dòng mới." + }, "server_titles": { "schedules": "Lịch trình", "users": "Người dùng", diff --git a/resources/scripts/components/MessageBox.tsx b/resources/scripts/components/MessageBox.tsx index cc68922e8..84bf57755 100644 --- a/resources/scripts/components/MessageBox.tsx +++ b/resources/scripts/components/MessageBox.tsx @@ -14,12 +14,6 @@ interface Props { const Container = styled.div<{ $type?: FlashMessageType }>``; Container.displayName = 'MessageBox.Container'; -/** - * Component hiển thị thông báo với tiêu đề và nội dung - * @param title Tiêu đề thông báo (có thể là key i18n) - * @param children Nội dung thông báo (có thể là key i18n) - * @param type Loại thông báo: success, info, warning, error - */ const MessageBox = ({ title, children, type }: Props) => { const { t } = useTranslation(); diff --git a/resources/scripts/components/PyrodactylProvider.tsx b/resources/scripts/components/PyrodactylProvider.tsx index e3c78903d..cca8edac6 100644 --- a/resources/scripts/components/PyrodactylProvider.tsx +++ b/resources/scripts/components/PyrodactylProvider.tsx @@ -1,16 +1,8 @@ -import { useTranslation } from 'react-i18next'; - interface Props { children: React.ReactNode; } -/** - * Component cung cấp thông tin về phiên bản và môi trường Pyrodactyl - * @param children Nội dung bên trong provider - */ const PyrodactylProvider = ({ children }: Props) => { - const { t } = useTranslation(); - return (
void }) => { + const { t } = useTranslation(); const [apiKey, setApiKey] = useState(''); const { addError, clearFlashes } = useStoreActions((actions: Actions) => actions.flashes); @@ -70,18 +70,18 @@ export default ({ onKeyCreated }: { onKeyCreated: (key: ApiKey) => void }) => { {/* Description Field */} {/* Allowed IPs Field */} @@ -89,7 +89,7 @@ export default ({ onKeyCreated }: { onKeyCreated: (key: ApiKey) => void }) => { {/* Submit Button below form fields */}
diff --git a/resources/scripts/components/dashboard/forms/DisableTOTPDialog.tsx b/resources/scripts/components/dashboard/forms/DisableTOTPDialog.tsx index 0704d98e9..81fa585a8 100644 --- a/resources/scripts/components/dashboard/forms/DisableTOTPDialog.tsx +++ b/resources/scripts/components/dashboard/forms/DisableTOTPDialog.tsx @@ -1,6 +1,7 @@ // FIXME: replace with radix tooltip // import Tooltip from '@/components/elements/tooltip/Tooltip'; import { useContext, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import FlashMessageRender from '@/components/FlashMessageRender'; import { Button } from '@/components/elements/button/index'; @@ -16,6 +17,7 @@ import { useStoreActions } from '@/state/hooks'; import { useFlashKey } from '@/plugins/useFlash'; const DisableTOTPDialog = () => { + const { t } = useTranslation(); const [submitting, setSubmitting] = useState(false); const [password, setPassword] = useState(''); const { clearAndAddHttpError } = useFlashKey('account:two-step'); @@ -47,7 +49,7 @@ const DisableTOTPDialog = () => {
{ onChange={(e) => setPassword(e.currentTarget.value)} /> - Cancel + {t('cancel')} {/* 0} content={'You must enter your account password to continue.'} > */} - Disable + {t('settings.2fa.buttons.disable')} {/* */} diff --git a/resources/scripts/components/dashboard/forms/SetupTOTPDialog.tsx b/resources/scripts/components/dashboard/forms/SetupTOTPDialog.tsx index 440563108..8454f24a9 100644 --- a/resources/scripts/components/dashboard/forms/SetupTOTPDialog.tsx +++ b/resources/scripts/components/dashboard/forms/SetupTOTPDialog.tsx @@ -109,7 +109,7 @@ const ConfigureTwoFactorForm = ({ onTokens }: Props) => { onChange={(e) => setPassword(e.currentTarget.value)} /> - Cancel + {t('cancel')} {/* 0 && value.length === 6} content={ diff --git a/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx b/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx index 6a3965b50..7b7091d0f 100644 --- a/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx +++ b/resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx @@ -17,11 +17,6 @@ interface Values { password: string; } -const schema = Yup.object().shape({ - email: Yup.string().email().required(), - password: Yup.string().required('You must provide your current account password.'), -}); - export default () => { const { t } = useTranslation(); const user = useStoreState((state: State) => state.user.data); @@ -29,6 +24,11 @@ export default () => { const { clearFlashes, addFlash } = useStoreActions((actions: Actions) => actions.flashes); + const schema = Yup.object().shape({ + email: Yup.string().email().required(t('auth.validation.email_required_reset')), + password: Yup.string().required(t('auth.validation.password_required')), + }); + const submit = (values: Values, { resetForm, setSubmitting }: FormikHelpers) => { clearFlashes('account:email'); @@ -37,14 +37,14 @@ export default () => { addFlash({ type: 'success', key: 'account:email', - message: 'Your primary email has been updated.', + message: t('settings.email.updated_successfully'), }), ) .catch((error) => addFlash({ type: 'error', key: 'account:email', - title: 'Error', + title: t('error'), message: httpErrorToHuman(error), }), ) @@ -60,7 +60,7 @@ export default () => { - +
{ />
- +
diff --git a/resources/scripts/migration-guide.md b/resources/scripts/migration-guide.md deleted file mode 100644 index b0afadcd3..000000000 --- a/resources/scripts/migration-guide.md +++ /dev/null @@ -1,232 +0,0 @@ -# Hướng dẫn di chuyển từ chuỗi tĩnh sang i18n - -## Giới thiệu - -Tài liệu này cung cấp hướng dẫn để di chuyển mã nguồn của bạn từ việc sử dụng chuỗi tĩnh (hardcoded string) sang sử dụng i18n để hỗ trợ đa ngôn ngữ. - -## Cấu trúc thư mục - -Dự án sử dụng thư viện i18next và react-i18next với cấu trúc thư mục như sau: - -``` -/public/locales -├── en/ -│ └── translation.json -├── vi/ -│ └── translation.json -``` - -## Hướng dẫn di chuyển - -### Bước 1: Import hook useTranslation - -```tsx -import { useTranslation } from 'react-i18next'; -``` - -### Bước 2: Sử dụng hook trong component - -```tsx -const { t } = useTranslation(); -``` - -### Bước 3: Thay thế chuỗi tĩnh bằng t function - -Thay: - -```tsx -

Tên máy chủ

-``` - -Bằng: - -```tsx -

{t('server.settings.rename.server_name')}

-``` - -### Bước 4: Thêm khóa dịch vào file translation.json - -Thêm vào `/public/locales/en/translation.json`: - -```json -{ - "server": { - "settings": { - "rename": { - "server_name": "Server Name" - } - } - } -} -``` - -Thêm vào `/public/locales/vi/translation.json`: - -```json -{ - "server": { - "settings": { - "rename": { - "server_name": "Tên máy chủ" - } - } - } -} -``` - -### Bước 5: Sử dụng variables trong chuỗi dịch - -```tsx -// Trong component -

{t('welcome.message', { username: user.name })}

- -// Trong file translation.json -{ - "welcome": { - "message": "Xin chào, {{username}}!" - } -} -``` - -### Bước 6: Sử dụng Trans component cho HTML phức tạp - -```tsx -import { Trans, useTranslation } from 'react-i18next'; - -const { t } = useTranslation(); - - - Để biết thêm thông tin, nhấp vào đây. - - -// Trong file translation.json -{ - "description": { - "part": "Để biết thêm thông tin, <1>nhấp vào đây." - } -} -``` - -## Danh sách component đã di chuyển - -Các component sau đã được di chuyển để sử dụng i18n: - -1. `ShellContainer.tsx` -2. `ReinstallServerBox.tsx` -3. `SettingsContainer.tsx` -4. `RenameServerBox.tsx` -5. `ScheduleContainer.tsx` -6. `AccountApiContainer.tsx` -7. `UsersContainer.tsx` -8. `ActivityLogContainer.tsx` -9. `AccountOverviewContainer.tsx` -10. `ConfigureTwoFactorForm.tsx` -11. `ApiKeyModal.tsx` -12. `RecoveryTokensDialog.tsx` -13. `CreateSSHKeyForm.tsx` -14. `ConfirmationModal.tsx` -15. `SetupTOTPDialog.tsx` -16. `UpdateEmailAddressForm.tsx` -17. `UpdateLanguageForm.tsx` -18. `ConfirmationDialog.tsx` -19. `UpdatePasswordForm.tsx` -20. `Modal.tsx` -21. `PageContentBlock.tsx` -22. `PermissionRoute.tsx` -23. `ScreenBlock.tsx` (NotFound component) - -## Danh sách component cần di chuyển - -Các component sau cần được di chuyển để sử dụng i18n: - -1. `ScreenBlock.tsx` (phần ScreenBlock & ServerError component) -2. `MessageBox.tsx` -3. `FlashMessageRender.tsx` -4. `App.tsx` (phần Suspense "Loading...") - -## Ví dụ di chuyển - -### Ví dụ 1: Component MessageBox - -**Trước khi di chuyển:** - -```tsx -const MessageBox = ({ title, children, type }: Props) => ( - - {title &&

{title}

} - {children} -
-); -``` - -**Sau khi di chuyển:** - -```tsx -import { useTranslation } from 'react-i18next'; - -const MessageBox = ({ title, children, type }: Props) => { - const { t } = useTranslation(); - - return ( - - {title &&

{t(title)}

} - {t(children)} -
- ); -}; -``` - -### Ví dụ 2: Component ScreenBlock - -**Trước khi di chuyển:** - -```tsx -const ScreenBlock = ({ title, message }) => { - return ( - <> -
-
-

{title}

-

{message}

-
-
- - ); -}; -``` - -**Sau khi di chuyển:** - -```tsx -import { useTranslation } from 'react-i18next'; - -const ScreenBlock = ({ title, message }) => { - const { t } = useTranslation(); - - return ( - <> -
-
-

{t(title)}

-

{t(message)}

-
-
- - ); -}; -``` - -## Lưu ý quan trọng - -1. **Cấu trúc khóa**: Sử dụng cấu trúc phân cấp cho khóa dịch, ví dụ: `component.action.message` -2. **Thống nhất cách sử dụng**: Đảm bảo sử dụng nhất quán `t` function trong toàn bộ ứng dụng -3. **Kiểm tra hỗ trợ đa ngôn ngữ**: Sau khi di chuyển, kiểm tra ứng dụng hoạt động chính xác trên tất cả ngôn ngữ được hỗ trợ -4. **Tham chiếu**: Xem code các component đã di chuyển để tham khảo cách thực hiện diff --git a/resources/scripts/migration-status.md b/resources/scripts/migration-status.md deleted file mode 100644 index 1d13a3868..000000000 --- a/resources/scripts/migration-status.md +++ /dev/null @@ -1,61 +0,0 @@ -# Trạng thái di chuyển i18n - -Tài liệu này theo dõi trạng thái di chuyển các component từ chuỗi tĩnh sang sử dụng i18n. - -## Components đã di chuyển - -| Component | Trạng thái | Ghi chú | -| ----------------------------- | ------------- | ------------- | -| ShellContainer.tsx | ✅ Hoàn thành | | -| ReinstallServerBox.tsx | ✅ Hoàn thành | | -| SettingsContainer.tsx | ✅ Hoàn thành | | -| RenameServerBox.tsx | ✅ Hoàn thành | | -| ScheduleContainer.tsx | ✅ Hoàn thành | | -| AccountApiContainer.tsx | ✅ Hoàn thành | | -| UsersContainer.tsx | ✅ Hoàn thành | | -| ActivityLogContainer.tsx | ✅ Hoàn thành | | -| AccountOverviewContainer.tsx | ✅ Hoàn thành | | -| ConfigureTwoFactorForm.tsx | ✅ Hoàn thành | | -| ApiKeyModal.tsx | ✅ Hoàn thành | | -| RecoveryTokensDialog.tsx | ✅ Hoàn thành | | -| CreateSSHKeyForm.tsx | ✅ Hoàn thành | | -| ConfirmationModal.tsx | ✅ Hoàn thành | | -| SetupTOTPDialog.tsx | ✅ Hoàn thành | | -| UpdateEmailAddressForm.tsx | ✅ Hoàn thành | | -| UpdateLanguageForm.tsx | ✅ Hoàn thành | | -| ConfirmationDialog.tsx | ✅ Hoàn thành | | -| UpdatePasswordForm.tsx | ✅ Hoàn thành | | -| Modal.tsx | ✅ Hoàn thành | | -| PageContentBlock.tsx | ✅ Hoàn thành | | -| PermissionRoute.tsx | ✅ Hoàn thành | | -| ScreenBlock.tsx (NotFound) | ✅ Hoàn thành | | -| ScreenBlock.tsx (ScreenBlock) | ✅ Hoàn thành | Mới di chuyển | -| ScreenBlock.tsx (ServerError) | ✅ Hoàn thành | Mới di chuyển | -| MessageBox.tsx | ✅ Hoàn thành | Mới di chuyển | -| FlashMessageRender.tsx | ✅ Hoàn thành | Mới di chuyển | -| App.tsx (Loading) | ✅ Hoàn thành | Mới di chuyển | -| LoginContainer.tsx | ✅ Hoàn thành | Mới di chuyển | -| ResetPasswordContainer.tsx | ✅ Hoàn thành | Mới di chuyển | -| ForgotPasswordContainer.tsx | ✅ Hoàn thành | Mới di chuyển | -| LoginCheckpointContainer.tsx | ✅ Hoàn thành | Mới di chuyển | -| LoginFormContainer.tsx | ✅ Hoàn thành | Mới di chuyển | -| AuthenticatedRoute.tsx | ✅ Hoàn thành | Mới di chuyển | -| ErrorBoundary.tsx | ✅ Hoàn thành | Mới di chuyển | -| PyrodactylProvider.tsx | ✅ Hoàn thành | Mới di chuyển | - -## Files JSON đa ngôn ngữ - -Cập nhật các khóa dịch trong files: - -- `/public/locales/en/translation.json`: Đã cập nhật ✅ -- `/public/locales/vi/translation.json`: Đã cập nhật ✅ - -## Tiến độ chung - -- Tổng số components: 36 -- Số components đã di chuyển: 36 (100%) -- Số components chờ di chuyển: 0 (0%) - -## Hướng dẫn di chuyển - -Xem chi tiết cách thực hiện di chuyển trong file [migration-guide.md](./migration-guide.md).