refactor: consolidate English translations (#6640)

This commit is contained in:
Thomas authored and GitHub committed 2026-08-21 19:00:27 +02:00
1 parent 81aa704537
commit 9e14a07e64
449 files changed
+3515 -3922

No files matched your search

@@ -3,7 +3,7 @@
import { Badge, Group, Paper, Stack, Table, Text, ThemeIcon, Title } from "@mantine/core";
import { IconCheck, IconDatabase, IconGitBranch } from "@tabler/icons-react";
import { useScopedI18n } from "@homarr/translation/client";
import { useI18n } from "@homarr/translation/client";
import type { BackupAnalysis } from "./types";
import { PREVIEW_TABLE_KEYS } from "./types";
@@ -13,10 +13,25 @@ interface BackupPreviewPanelProps {
}
export const BackupPreviewPanel = ({ analysis }: BackupPreviewPanelProps) => {
const t = useScopedI18n("management.page.tool.backup.restore.preview");
const t = useI18n("management.page.tool.backup.restore.preview");
const tEntities = useI18n("common.entity");
const exportDate = new Date(analysis.metadata.exportedAt).toLocaleString();
const pendingCount = analysis.migrations.pending.length;
const hasPendingMigrations = pendingCount > 0;
const entityKeys = {
app: "apps",
board: "boards",
integration: "integrations",
item: undefined,
media: "media",
search_engine: "searchEngines",
user: "users",
} as const;
const getEntityLabel = (key: (typeof PREVIEW_TABLE_KEYS)[number]) => {
const entityKey = entityKeys[key];
if (entityKey) return tEntities(entityKey);
return t(`entities.${key}` as never);
};
return (
<Stack gap="md">
@@ -47,7 +62,7 @@ export const BackupPreviewPanel = ({ analysis }: BackupPreviewPanelProps) => {
<Table.Tbody>
{PREVIEW_TABLE_KEYS.map((key) => (
<Table.Tr key={key}>
<Table.Td>{t(`entities.${key}` as never)}</Table.Td>
<Table.Td>{getEntityLabel(key)}</Table.Td>
<Table.Td ta="right">
<Group gap={4} justify="flex-end">
{(analysis.counts[key] ?? 0) > 0 && <IconCheck size={14} color="var(--mantine-color-green-6)" />}
@@ -64,7 +79,7 @@ export const BackupPreviewPanel = ({ analysis }: BackupPreviewPanelProps) => {
{analysis.boardNames.length > 0 && (
<Group gap="xs" wrap="wrap">
<Text size="sm" fw={500}>
{t("boards")}:
{tEntities("boards")}:
</Text>
{analysis.boardNames.map((name) => (
<Badge key={name} variant="light" size="sm">
@@ -8,7 +8,7 @@ import { IconAlertTriangle, IconArrowRight, IconFileZip, IconUpload, IconX } fro
import "@mantine/dropzone/styles.css"; // oxlint-disable-line import/no-unassigned-import
import { useScopedI18n } from "@homarr/translation/client";
import { useI18n } from "@homarr/translation/client";
import type { RestoreStep } from "./types";
import { BackupPreviewPanel } from "./backup-preview-panel";
@@ -103,7 +103,8 @@ export const waitForServerReadinessAsync = async ({
};
export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: DatabaseRestoreFlowProps) => {
const t = useScopedI18n("management.page.tool.backup.restore");
const t = useI18n("management.page.tool.backup.restore");
const tCommon = useI18n("common");
const [file, setFile] = useState<FileWithPath | null>(null);
const [step, setStep] = useState<RestoreStep>("upload");
const [importError, setImportError] = useState<string | null>(null);
@@ -330,7 +331,7 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
</Alert>
<Group>
<Button variant="subtle" onClick={handleClear}>
{t("tryAgain")}
{tCommon("action.tryAgain")}
</Button>
</Group>
</Stack>
@@ -3,7 +3,7 @@
import { Box, Group, Loader, Paper, Stack, Text, ThemeIcon, Timeline } from "@mantine/core";
import { IconCheck, IconDatabase, IconLoader } from "@tabler/icons-react";
import { useScopedI18n } from "@homarr/translation/client";
import { useI18n } from "@homarr/translation/client";
import type { MigrationProgress } from "./use-backup-analysis";
@@ -12,7 +12,7 @@ interface MigrationProgressPanelProps {
}
export const MigrationProgressPanel = ({ progress }: MigrationProgressPanelProps) => {
const t = useScopedI18n("management.page.tool.backup.restore.migration");
const t = useI18n("management.page.tool.backup.restore.migration");
return (
<Paper p="md" radius="md" bg="var(--mantine-color-dark-7)">
@@ -2,7 +2,7 @@
import { IconDatabaseImport } from "@tabler/icons-react";
import { useScopedI18n } from "@homarr/translation/client";
import { useI18n } from "@homarr/translation/client";
import { DangerousActionConfirmation } from "./dangerous-action-confirmation";
@@ -13,17 +13,19 @@ interface RestoreConfirmationProps {
}
export const RestoreConfirmation = ({ onConfirm, onCancel, disabled }: RestoreConfirmationProps) => {
const t = useScopedI18n("management.page.tool.backup.restore.confirm");
const t = useI18n("management.page.tool.backup.restore.confirm");
const tBackup = useI18n("management.page.tool.backup");
const tCommon = useI18n("common");
return (
<DangerousActionConfirmation
title={t("title")}
warningTitle={t("warningTitle")}
warningBody={t("warningBody")}
typePrompt={t("typePrompt", { phrase: "I understand" })}
typePrompt={tBackup("confirm.typePrompt", { phrase: "I understand" })}
submitLabel={t("submit")}
submitIcon={<IconDatabaseImport size={16} />}
cancelLabel={t("cancel")}
cancelLabel={tCommon("action.cancel")}
onConfirm={onConfirm}
onCancel={onCancel}
disabled={disabled}
@@ -7,16 +7,18 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("@homarr/translation/client", () => {
const translations: Record<string, string> = {
"progress.title": "Restoring database...",
"progress.restarting": "Restarting server",
"timeout.title": "Server did not come back online",
"timeout.message": "Retry the connection check or refresh this page manually.",
tryAgain: "Try again",
"docker.action.refresh.label": "Refresh",
"management.page.tool.backup.restore.progress.title": "Restoring database...",
"management.page.tool.backup.restore.progress.restarting": "Restarting server",
"management.page.tool.backup.restore.timeout.title": "Server did not come back online",
"management.page.tool.backup.restore.timeout.message": "Retry the connection check or refresh this page manually.",
"common.action.tryAgain": "Try again",
"common.action.refresh": "Refresh",
};
return {
useScopedI18n: () => (key: string) => translations[key] ?? key,
useI18n: () => (key: string) => translations[key] ?? key,
useI18n: (namespace?: string) => (key: string) => {
const fullKey = namespace ? `${namespace}.${key}` : key;
return translations[fullKey] ?? key;
},
};
});
@@ -4,7 +4,7 @@ import { Button, Group, Loader, Paper, Stack, Text, ThemeIcon } from "@mantine/c
import { useReducedMotion } from "@mantine/hooks";
import { IconAlertTriangle, IconCheck, IconDatabaseImport, IconRefresh } from "@tabler/icons-react";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { useI18n } from "@homarr/translation/client";
export type RestoreProgressStatus = "restoring" | "restarting" | "timedOut";
@@ -16,8 +16,8 @@ interface RestoreProgressPanelProps {
}
export const RestoreProgressPanel = ({ active, status, onRetry, onReload }: RestoreProgressPanelProps) => {
const t = useScopedI18n("management.page.tool.backup.restore");
const tRoot = useI18n();
const t = useI18n("management.page.tool.backup.restore");
const tCommon = useI18n("common");
const reduceMotion = useReducedMotion();
if (!active) return null;
@@ -66,10 +66,10 @@ export const RestoreProgressPanel = ({ active, status, onRetry, onReload }: Rest
{isTimedOut ? (
<Group>
<Button variant="light" onClick={onRetry} leftSection={<IconRefresh size={16} />}>
{t("tryAgain")}
{tCommon("action.tryAgain")}
</Button>
<Button variant="default" onClick={onReload}>
{tRoot("docker.action.refresh.label")}
{tCommon("action.refresh")}
</Button>
</Group>
) : null}
@@ -3,7 +3,7 @@
import { useCallback, useRef, useState } from "react";
import { useTiks } from "@rexa-developer/tiks/react";
import { useScopedI18n } from "@homarr/translation/client";
import { useI18n } from "@homarr/translation/client";
import type { BackupAnalysis, MigrationFile, MigrationStatus } from "./types";
import { PREVIEW_TABLE_KEYS } from "./types";
@@ -60,7 +60,7 @@ export interface MigrationProgress {
}
export const useBackupAnalysis = () => {
const t = useScopedI18n("management.page.tool.backup.restore");
const t = useI18n("management.page.tool.backup.restore");
const sounds = useTiks({ theme: "soft", volume: 0.12, respectReducedMotion: true });
const [analysis, setAnalysis] = useState<BackupAnalysis | null>(null);
const [loading, setLoading] = useState(false);