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

@@ -13,6 +13,7 @@ import { ErrorBoundary } from "react-error-boundary";
import { useRequiredBoard } from "@homarr/boards/context";
import { useEditMode } from "@homarr/boards/edit-mode";
import { getWidgetName } from "@homarr/definitions";
import { useSettings } from "@homarr/settings";
import { useI18n } from "@homarr/translation/client";
import { WidgetError } from "@homarr/widgets/errors";
@@ -178,7 +179,7 @@ const LoadedBoardItemContent = ({
const { active, viewportSize, open, close, dismiss, hover, leave } = useAdvancedFocus();
const { width: viewportWidth, height: viewportHeight } = viewportSize;
const supportsAdvancedFocus = definitionSupportsAdvancedFocus(definition);
const widgetName = t(`widget.${item.kind}.name`);
const widgetName = getWidgetName(item.kind, t);
const advancedViewLabel = t("item.advancedFocus.label", { widget: widgetName });
const advancedViewId = `advanced-focus-${item.id}`;
const activeFocus = supportsAdvancedFocus && active?.itemId === item.id ? active : null;
@@ -4,9 +4,10 @@ import { IconCopy, IconDotsVertical, IconLayoutKanban, IconPencil } from "@table
import { useSession } from "@homarr/auth/client";
import { useEditMode } from "@homarr/boards/edit-mode";
import { getWidgetName } from "@homarr/definitions";
import { useModalAction } from "@homarr/modals";
import { useSettings } from "@homarr/settings";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { useI18n } from "@homarr/translation/client";
import type { WidgetDefinition } from "@homarr/widgets/definition";
import type { SectionItem } from "~/app/[locale]/boards/_types";
@@ -34,7 +35,7 @@ const BoardItemMenuInner = ({ item, definition, resetErrorBoundary }: BoardItemM
const { data: session } = useSession();
const canDuplicate = item.kind !== "customApi" || (session?.user.permissions.includes("admin") ?? false);
const refResetErrorBoundaryOnNextRender = useRef(false);
const tItem = useScopedI18n("item");
const tItem = useI18n("item");
const t = useI18n();
const { openModal } = useModalAction(LazyWidgetEditModal);
const openMoveModal = useOpenItemMoveModal();
@@ -43,7 +44,7 @@ const BoardItemMenuInner = ({ item, definition, resetErrorBoundary }: BoardItemM
useItemActions();
const { integrations: integrationData, section } = useSectionContext();
const settings = useSettings();
const label = item.advancedOptions.title?.trim() || t(`widget.${item.kind}.name`);
const label = item.advancedOptions.title?.trim() || getWidgetName(item.kind, t);
// Reset error boundary on next render if item has been edited
useEffect(() => {
@@ -92,7 +93,7 @@ const BoardItemMenuInner = ({ item, definition, resetErrorBoundary }: BoardItemM
},
{
title(translate) {
return `${translate("item.edit.title")} - ${translate(`widget.${item.kind}.name`)}`;
return `${translate("item.edit.title")} - ${getWidgetName(item.kind, translate)}`;
},
},
);
@@ -6,7 +6,7 @@ import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context";
import { getRootSectionLane } from "@homarr/definitions";
import { useZodForm } from "@homarr/form";
import { createModal, ModalFormFooter, modalSizeForm, useModalAction } from "@homarr/modals";
import { useScopedI18n } from "@homarr/translation/client";
import { useI18n } from "@homarr/translation/client";
import type { Board, ContainerSectionItem, SectionItem } from "~/app/[locale]/boards/_types";
import { getLayoutRowCount } from "../layout";
@@ -53,9 +53,9 @@ type MoveTargetCandidate = Omit<MoveTarget, "label"> & {
};
export const ItemMoveModal = createModal<InnerProps>(({ actions, innerProps }) => {
const tLandmark = useScopedI18n("board.landmark");
const tContainer = useScopedI18n("section.container");
const tMoveResize = useScopedI18n("item.moveResize");
const tLandmark = useI18n("board.landmark");
const tContainer = useI18n("section.container");
const tMoveResize = useI18n("item.moveResize");
const { board, commitSectionGrids, currentLayoutId, entry, sourceSectionId } = innerProps;
const minimumSize = useMemo(
() => getEntryMinimumSize(board, currentLayoutId, entry),
@@ -24,7 +24,6 @@ vi.mock("@homarr/translation/client", () => ({
};
return translations[key] ?? key;
},
useScopedI18n: () => (key: string) => key,
}));
vi.mock("@homarr/definitions", async () => {
@@ -28,6 +28,7 @@ import {
getBoardLaneColumnCount,
getIconUrl,
getIntegrationName,
getWidgetName,
getRootSectionLane,
widgetIntegrationSupport,
widgetKinds,
@@ -144,14 +145,24 @@ const ItemSelectModalContent = ({
widgetKinds
.filter((kind) => kind !== "customApi")
.map((kind) => {
let description: string;
if (kind === "mediaMissing") {
description = t("widget.mediaMissing.description", {
radarr: getIntegrationName("radarr"),
sonarr: getIntegrationName("sonarr"),
});
} else {
description = t(`widget.${kind}.description`);
}
return {
kind,
supportedIntegrations: (widgetIntegrationSupport[kind] ?? []).filter(
(integration) => integration !== "mock",
),
icon: widgetCatalogIcons[kind],
name: t(`widget.${kind}.name`),
description: t(`widget.${kind}.description`),
name: getWidgetName(kind, t),
description,
};
})
.sort((itemA, itemB) => {
@@ -324,7 +335,7 @@ const ItemSelectModalContent = ({
targetSectionId: effectiveSectionId ?? undefined,
advancedOptions,
});
if (!notifyCreated(updatedBoard, itemId, t(`widget.${kind}.name`))) return;
if (!notifyCreated(updatedBoard, itemId, getWidgetName(kind, t))) return;
trackSetup("widget-completed", {
entryPoint: "board",
intent: kind,
@@ -337,7 +348,7 @@ const ItemSelectModalContent = ({
settings,
},
{
title: (titleT) => `${titleT("item.edit.title")} - ${titleT(`widget.${kind}.name`)}`,
title: (titleT) => `${titleT("item.edit.title")} - ${getWidgetName(kind, titleT)}`,
},
);
};
@@ -458,7 +469,7 @@ const ItemSelectModalContent = ({
<Group gap="xs">
<IconApi size={14} />
<Text size="xs" fw={600}>
{t("customWidget.page.list.title")}
{t("common.entity.customWidgets")}
</Text>
</Group>
}
@@ -643,7 +654,7 @@ const ItemSelectModalFrame = ({
canCreateIntegration={canCreateIntegration}
initialSearch={
innerProps.initialWidgetKind
? t(`widget.${innerProps.initialWidgetKind}.name`)
? getWidgetName(innerProps.initialWidgetKind, t)
: innerProps.initialIntegrationKind
? getIntegrationName(innerProps.initialIntegrationKind)
: undefined
@@ -16,12 +16,14 @@ import { useTimeAgo } from "@homarr/common";
import { useModalAction } from "@homarr/modals";
import { showErrorNotification } from "@homarr/notifications";
import { useSettings } from "@homarr/settings";
import { getWidgetName } from "@homarr/definitions";
import { translateIfNecessary } from "@homarr/translation";
import type { TranslationFunction } from "@homarr/translation";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import { useI18n } from "@homarr/translation/client";
import type { WidgetDefinition, WidgetRuntimeRef } from "@homarr/widgets/definition";
import { getWidgetQueryKeys, getWidgetRuntimeQueries, supportsAdvancedFocus } from "@homarr/widgets/definition";
import { reduceWidgetOptionsWithDefinition } from "@homarr/widgets/manifest";
import { getWidgetOptionTranslationNamespace } from "@homarr/widgets/option-translation";
import type { SectionItem } from "~/app/[locale]/boards/_types";
import { useAdvancedFocus } from "../advanced-focus/context";
@@ -51,8 +53,9 @@ export const WidgetContextMenu = ({
const board = useRequiredBoard();
const { hasChangeAccess } = useBoardPermissions(board);
const { updateAndPersistBoard } = usePersistBoard(board);
const tMenu = useScopedI18n("item.menu.label");
const t = useI18n();
const tMenu = useI18n("item.menu.label");
const tCommon = useI18n("common.action");
const settings = useSettings();
const { openModal } = useModalAction(LazyWidgetEditModal);
const { updateItemOptions, updateItemAdvancedOptions, updateItemIntegrations } = useItemActions();
@@ -189,7 +192,9 @@ export const WidgetContextMenu = ({
appId: item.kind === "app" ? (item.options.appId as string | undefined) : undefined,
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{ title: (fn: any) => `${fn("item.edit.title")} - ${fn(`widget.${item.kind}.name`)}` },
{
title: (fn: any) => `${fn("item.edit.title")} - ${getWidgetName(item.kind, fn)}`,
},
);
}, [
definition,
@@ -250,8 +255,7 @@ export const WidgetContextMenu = ({
<Menu.Label>{tMenu("options")}</Menu.Label>
{toggleOptions.map(([key]) => (
<Menu.CheckboxItem key={key} checked={Boolean(options[key])} onChange={handleToggle(key)}>
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
{String(translateIfNecessary(t, ((fn: any) => fn(`widget.${item.kind}.option.${key}.label`)) as any))}
{t(`${getWidgetOptionTranslationNamespace(item.kind, key)}.label` as never)}
</Menu.CheckboxItem>
))}
</>
@@ -286,7 +290,7 @@ export const WidgetContextMenu = ({
disabled={isWidgetFetching}
>
<Group justify="space-between" wrap="nowrap" gap="sm">
{tMenu("refresh")}
{tCommon("refresh")}
<WidgetQueryStatus
queryClient={queryClient}
matchesQuery={matchesWidgetQuery}
@@ -5,7 +5,7 @@ import { Avatar, Text, Tooltip } from "@mantine/core";
import type { RouterOutputs } from "@homarr/api";
import { useEditMode } from "@homarr/boards/edit-mode";
import { getIconUrl } from "@homarr/definitions";
import { getIconUrl, getWidgetName } from "@homarr/definitions";
import { useI18n } from "@homarr/translation/client";
import { loadWidgetDefinition } from "@homarr/widgets/manifest";
@@ -34,7 +34,7 @@ const LoadedWidgetHoverOverlay = ({ item, integrations }: WidgetHoverOverlayProp
const definition = use(loadWidgetDefinition(item.kind));
const WidgetIcon = definition.icon;
const displayName = item.advancedOptions.title?.trim() || t(`widget.${item.kind}.name`);
const displayName = item.advancedOptions.title?.trim() || getWidgetName(item.kind, t);
const connectedIntegrations = (integrations ?? []).filter((integration) =>
item.integrationIds.includes(integration.id),