feat: localize all hardcoded strings in Root CA Management and Archive UI

This commit is contained in:
dyzulk
2026-01-07 10:58:15 +07:00
parent 8ff136d086
commit a1c38e4ab2
6 changed files with 92 additions and 36 deletions

View File

@@ -62,25 +62,21 @@ export default function RootCaManagementClient() {
setActiveSync(syncKey); setActiveSync(syncKey);
try { try {
let endpoint = "/api/admin/ca-certificates/sync-cdn"; let endpoint = "/api/admin/ca-certificates/sync-cdn";
let msg = `Sync successful (Mode: ${mode})`;
if (type === 'crt') { if (type === 'crt') {
endpoint = "/api/admin/ca-certificates/sync-crt"; endpoint = "/api/admin/ca-certificates/sync-crt";
msg = `CRT Files Sync successful (${mode})`;
} else if (type === 'installers') { } else if (type === 'installers') {
endpoint = "/api/admin/ca-certificates/sync-installers"; endpoint = "/api/admin/ca-certificates/sync-installers";
msg = `Individual Installers Sync successful (${mode})`;
} else if (type === 'bundles') { } else if (type === 'bundles') {
endpoint = "/api/admin/ca-certificates/sync-bundles"; endpoint = "/api/admin/ca-certificates/sync-bundles";
msg = "Global Bundles Sync successful";
} }
const response = await axios.post(endpoint, { mode }); const response = await axios.post(endpoint, { mode });
addToast(response.data.message || msg, "success"); addToast(response.data.message || t("toast_sync_success"), "success");
mutate(); mutate();
} catch (err: any) { } catch (err: any) {
console.error(err); console.error(err);
addToast(err.response?.data?.message || "Sync failed", "error"); addToast(err.response?.data?.message || t("toast_sync_failed"), "error");
} finally { } finally {
setActiveSync(null); setActiveSync(null);
} }
@@ -90,11 +86,11 @@ export default function RootCaManagementClient() {
setIsPromoting(true); setIsPromoting(true);
try { try {
const response = await axios.post(`/api/admin/ca-certificates/${uuid}/promote`); const response = await axios.post(`/api/admin/ca-certificates/${uuid}/promote`);
addToast(response.data.message || "Promoted successfully", "success"); addToast(response.data.message || t("toast_promote_success"), "success");
mutate(); mutate();
} catch (err: any) { } catch (err: any) {
console.error(err); console.error(err);
addToast(err.response?.data?.message || "Promotion failed", "error"); addToast(err.response?.data?.message || t("toast_promote_failed"), "error");
} finally { } finally {
setIsPromoting(false); setIsPromoting(false);
} }
@@ -192,8 +188,8 @@ export default function RootCaManagementClient() {
{/* Full-width Archive History */} {/* Full-width Archive History */}
<ComponentCard <ComponentCard
title="Version Archives" title={t("archive_management_title")}
desc="Browse historical versions and promote them back to Latest if needed. This table provides full visibility into your CDN audit trail." desc={t("archive_management_desc")}
> >
<ArchiveManagementTable <ArchiveManagementTable
certificates={certificates} certificates={certificates}

View File

@@ -72,10 +72,10 @@ export default function ArchiveManagementTable({
<div className="space-y-4"> <div className="space-y-4">
{/* Table Header Controls */} {/* Table Header Controls */}
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between px-1"> <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between px-1">
<h4 className="text-sm font-bold text-gray-800 dark:text-white/90">CDN Archive History</h4> <h4 className="text-sm font-bold text-gray-800 dark:text-white/90">{t("archive_history_title")}</h4>
<div className="w-full sm:w-64"> <div className="w-full sm:w-64">
<InputField <InputField
placeholder="Search version/CN..." placeholder={t("archive_search_placeholder")}
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="!py-2" className="!py-2"
@@ -89,10 +89,10 @@ export default function ArchiveManagementTable({
<TableHeader className="border-b border-gray-100 dark:border-white/[0.05]"> <TableHeader className="border-b border-gray-100 dark:border-white/[0.05]">
<TableRow> <TableRow>
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"> <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
Version (UUID) {t("th_version_uuid")}
</TableCell> </TableCell>
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"> <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
Family {t("th_family")}
</TableCell> </TableCell>
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"> <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
{t("common_name_th")} {t("common_name_th")}
@@ -104,7 +104,7 @@ export default function ArchiveManagementTable({
{t("validity_th")} {t("validity_th")}
</TableCell> </TableCell>
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"> <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
Status {t("th_status")}
</TableCell> </TableCell>
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-center text-theme-xs dark:text-gray-400"> <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-center text-theme-xs dark:text-gray-400">
{t("actions_th")} {t("actions_th")}
@@ -121,12 +121,12 @@ export default function ArchiveManagementTable({
{cert.last_synced_at ? ( {cert.last_synced_at ? (
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<span className="flex h-1.5 w-1.5 rounded-full bg-success-500"></span> <span className="flex h-1.5 w-1.5 rounded-full bg-success-500"></span>
<span className="text-[9px] text-success-600 font-bold uppercase tracking-wider">Synced to CDN</span> <span className="text-[9px] text-success-600 font-bold uppercase tracking-wider">{t("status_synced")}</span>
</div> </div>
) : ( ) : (
<div className="flex items-center gap-1.5 opacity-60"> <div className="flex items-center gap-1.5 opacity-60">
<span className="flex h-1.5 w-1.5 rounded-full bg-warning-500"></span> <span className="flex h-1.5 w-1.5 rounded-full bg-warning-500"></span>
<span className="text-[9px] text-warning-600 font-bold uppercase tracking-wider">Local Only</span> <span className="text-[9px] text-warning-600 font-bold uppercase tracking-wider">{t("status_local")}</span>
</div> </div>
)} )}
</div> </div>
@@ -181,11 +181,11 @@ export default function ArchiveManagementTable({
onClick={() => onPromote(cert.uuid)} onClick={() => onPromote(cert.uuid)}
loading={isPromoting} loading={isPromoting}
> >
Set as Latest {t("btn_set_latest")}
</Button> </Button>
)} )}
{cert.is_latest && ( {cert.is_latest && (
<span className="text-[10px] text-gray-400 font-medium">Currently Public</span> <span className="text-[10px] text-gray-400 font-medium">{t("status_currently_public")}</span>
)} )}
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -193,7 +193,7 @@ export default function ArchiveManagementTable({
{filteredAndSortedCertificates.length === 0 && ( {filteredAndSortedCertificates.length === 0 && (
<TableRow> <TableRow>
<TableCell colSpan={7} className="px-5 py-10 text-center text-gray-500 dark:text-gray-400"> <TableCell colSpan={7} className="px-5 py-10 text-center text-gray-500 dark:text-gray-400">
No versions found. {t("no_archives_found")}
</TableCell> </TableCell>
</TableRow> </TableRow>
)} )}

View File

@@ -60,7 +60,7 @@ export default function CdnManagementCard({ onSync, activeSync, disabled }: CdnM
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
</svg> </svg>
)} )}
{isLoading ? "Syncing..." : "Sync Now"} {isLoading ? t("syncing_label") : t("sync_now_btn")}
</button> </button>
</div> </div>
</div> </div>
@@ -79,22 +79,22 @@ export default function CdnManagementCard({ onSync, activeSync, disabled }: CdnM
<SyncButton <SyncButton
type="crt" type="crt"
mode="both" mode="both"
label="Update CRT Only" label={t("label_update_crt")}
desc="Hanya unggah file sertifikat publik (.crt, .der)." desc={t("desc_update_crt")}
variant="blue" variant="blue"
/> />
<SyncButton <SyncButton
type="installers" type="installers"
mode="both" mode="both"
label="Update Scripts Only" label={t("label_update_scripts")}
desc="Hanya perbarui skrip installer individual (.sh, .bat, .mac)." desc={t("desc_update_scripts")}
variant="purple" variant="purple"
/> />
<SyncButton <SyncButton
type="bundles" type="bundles"
mode="latest" mode="latest"
label="Update Global Bundles" label={t("label_update_bundles")}
desc="Perbarui paket installer sapujagat (Recommended)." desc={t("desc_update_bundles")}
variant="success" variant="success"
/> />
</div> </div>
@@ -109,22 +109,22 @@ export default function CdnManagementCard({ onSync, activeSync, disabled }: CdnM
<SyncButton <SyncButton
type="all" type="all"
mode="both" mode="both"
label="Dual Sync (Latest & Archive)" label={t("label_dual_sync")}
desc="Sinkronisasi massal ke kedua jalur. Paling aman untuk integritas data." desc={t("desc_dual_sync")}
variant="indigo" variant="indigo"
/> />
<SyncButton <SyncButton
type="all" type="all"
mode="latest" mode="latest"
label="Latest Sync (Clean URLs)" label={t("label_latest_sync")}
desc="Hanya perbarui link publik utama pendukung installer." desc={t("desc_latest_sync")}
variant="blue" variant="blue"
/> />
<SyncButton <SyncButton
type="all" type="all"
mode="archive" mode="archive"
label="Archive Sync (Versioned)" label={t("label_archive_sync")}
desc="Hanya simpan arsip permanen tanpa mengubah link publik." desc={t("desc_archive_sync")}
variant="gray" variant="gray"
/> />
</div> </div>

View File

@@ -80,7 +80,7 @@ export default function RootCaTable({
{t("type_th")} {t("type_th")}
</TableCell> </TableCell>
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"> <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
Family {t("th_family")}
</TableCell> </TableCell>
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"> <TableCell isHeader className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400">
{t("common_name_th")} {t("common_name_th")}

View File

@@ -676,7 +676,37 @@
"self_signed": "Self-Signed", "self_signed": "Self-Signed",
"renew_button": "Renew", "renew_button": "Renew",
"no_ca_search": "No CAs matched \"{term}\"", "no_ca_search": "No CAs matched \"{term}\"",
"no_ca_found": "No Root CA certificates found. Start by initializing the Certification Authority." "no_ca_found": "No Root CA certificates found. Start by initializing the Certification Authority.",
"archive_history_title": "CDN Archive History",
"archive_search_placeholder": "Search version/CN...",
"th_version_uuid": "Version (UUID)",
"th_family": "Family",
"th_status": "Status",
"status_synced": "Synced to CDN",
"status_local": "Local Only",
"btn_set_latest": "Set as Latest",
"status_currently_public": "Currently Public",
"no_archives_found": "No versions found.",
"archive_management_title": "Version Archives",
"archive_management_desc": "Browse historical versions and promote them back to Latest if needed. This table provides full visibility into your CDN audit trail.",
"toast_sync_success": "Synchronization successful",
"toast_sync_failed": "Synchronization failed",
"toast_promote_success": "Promoted to latest successfully",
"toast_promote_failed": "Failed to promote version",
"syncing_label": "Syncing...",
"sync_now_btn": "Sync Now",
"label_update_crt": "Update CRT Only",
"desc_update_crt": "Only upload public certificate files (.crt, .der).",
"label_update_scripts": "Update Scripts Only",
"desc_update_scripts": "Only update individual installer scripts (.sh, .bat, .mac).",
"label_update_bundles": "Update Global Bundles",
"desc_update_bundles": "Update all-in-one installer packages (Recommended).",
"label_dual_sync": "Dual Sync (Latest & Archive)",
"desc_dual_sync": "Bulk sync to both paths. Safest for data integrity.",
"label_latest_sync": "Latest Sync (Clean URLs)",
"desc_latest_sync": "Only update the primary public links for installers.",
"label_archive_sync": "Archive Sync (Versioned)",
"desc_archive_sync": "Only save permanent archives without changing public links."
}, },
"SmtpTester": { "SmtpTester": {
"page_title": "SMTP Tester", "page_title": "SMTP Tester",

View File

@@ -676,7 +676,37 @@
"self_signed": "Self-Signed", "self_signed": "Self-Signed",
"renew_button": "Perbarui", "renew_button": "Perbarui",
"no_ca_search": "Tidak ada CA yang cocok dengan \"{term}\"", "no_ca_search": "Tidak ada CA yang cocok dengan \"{term}\"",
"no_ca_found": "Sertifikat Root CA tidak ditemukan. Mulailah dengan menginisialisasi Certification Authority." "no_ca_found": "Sertifikat Root CA tidak ditemukan. Mulailah dengan menginisialisasi Certification Authority.",
"archive_history_title": "Riwayat Arsip CDN",
"archive_search_placeholder": "Cari versi/CN...",
"th_version_uuid": "Versi (UUID)",
"th_family": "Keluarga",
"th_status": "Status",
"status_synced": "Tersinkronisasi ke CDN",
"status_local": "Hanya Lokal",
"btn_set_latest": "Jadikan Terbaru",
"status_currently_public": "Publik Saat Ini",
"no_archives_found": "Versi tidak ditemukan.",
"archive_management_title": "Arsip Versi",
"archive_management_desc": "Telusuri versi historis dan jadikan kembali sebagai Versi Terbaru jika diperlukan. Tabel ini memberikan visibilitas penuh terhadap jejak audit CDN Anda.",
"toast_sync_success": "Sinkronisasi berhasil",
"toast_sync_failed": "Sinkronisasi gagal",
"toast_promote_success": "Berhasil dijadikan versi terbaru",
"toast_promote_failed": "Gagal menjadikan versi terbaru",
"syncing_label": "Menyinkronkan...",
"sync_now_btn": "Sinkronisasi",
"label_update_crt": "Perbarui CRT Saja",
"desc_update_crt": "Hanya unggah file sertifikat publik (.crt, .der).",
"label_update_scripts": "Perbarui Skrip Saja",
"desc_update_scripts": "Hanya perbarui skrip installer individual (.sh, .bat, .mac).",
"label_update_bundles": "Perbarui Paket Global",
"desc_update_bundles": "Perbarui paket installer sapujagat (Disarankan).",
"label_dual_sync": "Sinkronisasi Ganda (Terbaru & Arsip)",
"desc_dual_sync": "Sinkronisasi massal ke kedua jalur. Paling aman untuk integritas data.",
"label_latest_sync": "Sinkronisasi Terbaru (URL Bersih)",
"desc_latest_sync": "Hanya perbarui link publik utama pendukung installer.",
"label_archive_sync": "Sinkronisasi Arsip (Versi)",
"desc_archive_sync": "Hanya simpan arsip permanen tanpa mengubah link publik."
}, },
"SmtpTester": { "SmtpTester": {
"page_title": "Pengetes SMTP", "page_title": "Pengetes SMTP",