refactor: consolidate English translations (#6640)
This commit is contained in:
1 parent
81aa704537
commit
9e14a07e64
449 files changed
+3515
-3922
No files matched your search
@@ -5,7 +5,7 @@ import { IconBox, IconLayoutDashboard, IconPlug, IconResize } from "@tabler/icon
|
||||
|
||||
import { useRequiredBoard } from "@homarr/boards/context";
|
||||
import { useEditMode } from "@homarr/boards/edit-mode";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import { useBoardAddActions } from "./use-board-add-actions";
|
||||
import classes from "./board-empty-state.module.css";
|
||||
@@ -14,7 +14,7 @@ export const BoardEmptyState = () => {
|
||||
const board = useRequiredBoard();
|
||||
const [isEditMode] = useEditMode();
|
||||
const { addWidget, addApp, connectService, canConnectService } = useBoardAddActions();
|
||||
const t = useScopedI18n("board.emptyState");
|
||||
const t = useI18n("board.emptyState");
|
||||
const hasBoardContent = board.items.length > 0 || board.sections.some((section) => section.kind !== "empty");
|
||||
|
||||
if (!isEditMode || hasBoardContent) return null;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IconCheck, IconChevronDown, IconChecklist } from "@tabler/icons-react";
|
||||
import { useIntegrationsWithUseAccess } from "@homarr/auth/client";
|
||||
import { useRequiredBoard } from "@homarr/boards/context";
|
||||
import { useEditMode } from "@homarr/boards/edit-mode";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import { useSetupAnalytics } from "~/components/create/setup-analytics";
|
||||
import { getBoardSetupProgress } from "./board-setup-progress";
|
||||
@@ -23,7 +23,8 @@ export const BoardSetupChecklist = () => {
|
||||
key: `homarr-board-setup-checklist-${board.id}`,
|
||||
defaultValue: false,
|
||||
});
|
||||
const t = useScopedI18n("board.setupChecklist");
|
||||
const t = useI18n("board.setupChecklist");
|
||||
const tCommon = useI18n("common.action");
|
||||
const trackSetup = useSetupAnalytics();
|
||||
const progress = getBoardSetupProgress({
|
||||
itemKinds: board.items.map((item) => item.kind),
|
||||
@@ -74,14 +75,14 @@ export const BoardSetupChecklist = () => {
|
||||
complete={progress.steps.content}
|
||||
label={t("step.content.label")}
|
||||
description={t("step.content.description")}
|
||||
actionLabel={t("step.content.action")}
|
||||
actionLabel={tCommon("add")}
|
||||
onAction={addWidget}
|
||||
/>
|
||||
<ChecklistStep
|
||||
complete={progress.steps.app}
|
||||
label={t("step.app.label")}
|
||||
description={t("step.app.description")}
|
||||
actionLabel={t("step.app.action")}
|
||||
actionLabel={tCommon("add")}
|
||||
onAction={addApp}
|
||||
/>
|
||||
<ChecklistStep
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -17,7 +17,7 @@ interface InnerProps {
|
||||
|
||||
export const BoardRenameModal = createModal<InnerProps>(({ actions, innerProps }) => {
|
||||
const utils = clientApi.useUtils();
|
||||
const t = useI18n();
|
||||
const tCommon = useI18n("common");
|
||||
const { mutate, isPending } = clientApi.board.renameBoard.useMutation({
|
||||
onSettled() {
|
||||
void utils.board.getBoardByName.invalidate({
|
||||
@@ -50,20 +50,20 @@ export const BoardRenameModal = createModal<InnerProps>(({ actions, innerProps }
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack>
|
||||
<TextInput label={t("board.field.name.label")} {...form.getInputProps("name")} data-autofocus />
|
||||
<TextInput label={tCommon("field.name")} {...form.getInputProps("name")} data-autofocus />
|
||||
<Group justify="end">
|
||||
<Button variant="subtle" color="gray" onClick={actions.closeModal}>
|
||||
{t("common.action.cancel")}
|
||||
{tCommon("action.cancel")}
|
||||
</Button>
|
||||
<Button type="submit" loading={isPending}>
|
||||
{t("common.action.confirm")}
|
||||
{tCommon("action.confirm")}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
}).withOptions({
|
||||
defaultTitle: (t) => t("board.setting.section.dangerZone.action.rename.modal.title"),
|
||||
defaultTitle: (t) => t("board.setting.section.dangerZone.action.rename.label"),
|
||||
});
|
||||
|
||||
type FormType = Omit<z.infer<typeof boardRenameSchema>, "id">;
|
||||
@@ -5,7 +5,7 @@ import combineClasses from "clsx";
|
||||
|
||||
import { useRequiredBoard } from "@homarr/boards/context";
|
||||
import { useEditMode } from "@homarr/boards/edit-mode";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { ContainerSectionItem } from "~/app/[locale]/boards/_types";
|
||||
import { SectionGrid } from "./grid/section-grid";
|
||||
@@ -25,8 +25,8 @@ interface Props {
|
||||
export const BoardContainerSection = ({ section }: Props) => {
|
||||
const board = useRequiredBoard();
|
||||
const [isEditMode] = useEditMode();
|
||||
const t = useScopedI18n("section.container");
|
||||
const tAll = useI18n();
|
||||
const t = useI18n("section.container");
|
||||
const tSection = useI18n("section");
|
||||
const options = section.options;
|
||||
const { open: openAllInNewTabs, isLoading: areAppsLoading } = useOpenSectionApps(
|
||||
section.id,
|
||||
@@ -137,7 +137,7 @@ export const BoardContainerSection = ({ section }: Props) => {
|
||||
radius="sm"
|
||||
loading={areAppsLoading}
|
||||
onClick={openAllInNewTabs}
|
||||
aria-label={tAll("section.action.openAllInNewTabsFor", { name: label })}
|
||||
aria-label={tSection("action.openAllInNewTabsFor", { name: label })}
|
||||
>
|
||||
<IconExternalLink size={16} />
|
||||
</ActionIcon>
|
||||
|
||||
@@ -15,7 +15,7 @@ interface ModalProps {
|
||||
}
|
||||
|
||||
export const ContainerEditModal = createModal<ModalProps>(({ actions, innerProps }) => {
|
||||
const t = useI18n();
|
||||
const tSection = useI18n("section");
|
||||
const theme = useMantineTheme();
|
||||
const form = useZodForm(containerSectionOptionsSchema.unwrap(), {
|
||||
mode: "controlled",
|
||||
@@ -30,28 +30,28 @@ export const ContainerEditModal = createModal<ModalProps>(({ actions, innerProps
|
||||
})}
|
||||
>
|
||||
<Stack>
|
||||
<TextInput label={t("section.container.option.title.label")} data-autofocus {...form.getInputProps("title")} />
|
||||
<TextInput label={tSection("container.option.title.label")} data-autofocus {...form.getInputProps("title")} />
|
||||
<Switch
|
||||
label={t("section.option.showLabel.label")}
|
||||
description={t("section.option.showLabel.description")}
|
||||
label={tSection("option.showLabel.label")}
|
||||
description={tSection("option.showLabel.description")}
|
||||
{...form.getInputProps("showLabel", { type: "checkbox" })}
|
||||
/>
|
||||
<Switch
|
||||
label={t("section.option.collapsible.label")}
|
||||
description={t("section.option.collapsible.description")}
|
||||
label={tSection("option.collapsible.label")}
|
||||
description={tSection("option.collapsible.description")}
|
||||
{...form.getInputProps("collapsible", { type: "checkbox" })}
|
||||
/>
|
||||
<Switch
|
||||
label={t("section.option.showOpenAll.label")}
|
||||
description={t("section.option.showOpenAll.description")}
|
||||
label={tSection("option.showOpenAll.label")}
|
||||
description={tSection("option.showOpenAll.description")}
|
||||
{...form.getInputProps("showOpenAll", { type: "checkbox" })}
|
||||
/>
|
||||
<TextMultiSelect
|
||||
label={t("section.container.option.customCssClasses.label")}
|
||||
label={tSection("container.option.customCssClasses.label")}
|
||||
{...form.getInputProps("customCssClasses")}
|
||||
/>
|
||||
<ColorInput
|
||||
label={t("section.container.option.borderColor.label")}
|
||||
label={tSection("container.option.borderColor.label")}
|
||||
format="hex"
|
||||
swatches={Object.values(theme.colors).map((color) => color[6])}
|
||||
rightSection={
|
||||
|
||||
@@ -3,7 +3,7 @@ import { IconArrowsMove, IconLayoutKanban, IconPencil } from "@tabler/icons-reac
|
||||
|
||||
import { useEditMode } from "@homarr/boards/edit-mode";
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { ContainerSectionItem } from "~/app/[locale]/boards/_types";
|
||||
import { BoardRemoveConfirmationMenuItem } from "../../remove-confirmation-menu-item";
|
||||
@@ -13,8 +13,8 @@ import { useContainerActions } from "./container-actions";
|
||||
import { ContainerEditModal } from "./container-edit-modal";
|
||||
|
||||
export const BoardContainerMenu = ({ section }: { section: ContainerSectionItem }) => {
|
||||
const tContainer = useScopedI18n("section.container");
|
||||
const tItem = useScopedI18n("item");
|
||||
const tContainer = useI18n("section.container");
|
||||
const tItem = useI18n("item");
|
||||
const { openModal } = useModalAction(ContainerEditModal);
|
||||
const openMoveModal = useOpenItemMoveModal();
|
||||
const { updateContainer, removeContainer } = useContainerActions();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEditMode } from "@homarr/boards/edit-mode";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { EmptySection } from "~/app/[locale]/boards/_types";
|
||||
import { SectionGrid } from "./grid/section-grid";
|
||||
@@ -16,7 +16,7 @@ export const BoardEmptySection = ({ section, columnCount, requestedRowCount, rai
|
||||
const { items, innerSections } = useSectionItems(section.id);
|
||||
const totalLength = items.length + innerSections.length;
|
||||
const [isEditMode] = useEditMode();
|
||||
const t = useScopedI18n("board.landmark");
|
||||
const t = useI18n("board.landmark");
|
||||
|
||||
if (totalLength === 0 && !isEditMode && requestedRowCount === 0 && railPlacement !== "main") return null;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import combineClasses from "clsx";
|
||||
|
||||
import { useCurrentLayout } from "@homarr/boards/context";
|
||||
import { useEditMode } from "@homarr/boards/edit-mode";
|
||||
import { getWidgetName } from "@homarr/definitions";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { ContainerSectionItem, SectionItem } from "~/app/[locale]/boards/_types";
|
||||
@@ -76,7 +77,7 @@ export const FixedGridItem = ({
|
||||
});
|
||||
const displayName =
|
||||
item.type === "item"
|
||||
? item.advancedOptions.title?.trim() || t(`widget.${item.kind}.name`)
|
||||
? item.advancedOptions.title?.trim() || getWidgetName(item.kind, t)
|
||||
: item.options.title.trim() || t("section.container.untitled");
|
||||
const accessibleLabel = t("item.moveResize.entryLabel", {
|
||||
name: displayName,
|
||||
|
||||
@@ -36,6 +36,7 @@ import { DragDropProvider, DragOverlay, useDragDropManager, useDraggable, useDro
|
||||
import { IconGripHorizontal, IconGripVertical } from "@tabler/icons-react";
|
||||
|
||||
import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context";
|
||||
import { getWidgetName } from "@homarr/definitions";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import { getLogicalTrackSize, LOGICAL_GRID_PITCH } from "~/components/board/layout";
|
||||
@@ -743,7 +744,7 @@ export default function GridEditor({
|
||||
[...items, ...innerSections].map((entry) => [
|
||||
entry.id,
|
||||
entry.type === "item"
|
||||
? entry.advancedOptions.title?.trim() || t(`widget.${entry.kind}.name`)
|
||||
? entry.advancedOptions.title?.trim() || getWidgetName(entry.kind, t)
|
||||
: entry.options.title.trim() || t("section.container.untitled"),
|
||||
]),
|
||||
),
|
||||
|
||||
@@ -80,7 +80,7 @@ export const useOpenSectionApps = (sectionId: string, enabled: boolean) => {
|
||||
const currentLayoutId = useCurrentLayout();
|
||||
const settings = useSettings();
|
||||
const { openConfirmModal } = useConfirmModal();
|
||||
const t = useI18n();
|
||||
const t = useI18n("section");
|
||||
const appDefinition = use(loadWidgetDefinition("app"));
|
||||
const appIds = useMemo(() => {
|
||||
const items: Item[] = getSectionItemsForLayout(board, sectionId, currentLayoutId);
|
||||
@@ -115,8 +115,8 @@ export const useOpenSectionApps = (sectionId: string, enabled: boolean) => {
|
||||
if (openedWindow) continue;
|
||||
|
||||
openConfirmModal({
|
||||
title: t("section.openAllInNewTabs.title"),
|
||||
children: t("section.openAllInNewTabs.text"),
|
||||
title: t("openAllInNewTabs.title"),
|
||||
children: t("openAllInNewTabs.text"),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { usePersistBoard } from "@homarr/boards/updater";
|
||||
import { createId } from "@homarr/common";
|
||||
import { getBoardLaneColumnCount, getRootSectionLane } from "@homarr/definitions";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { ContainerSection, ItemLayout } from "~/app/[locale]/boards/_types";
|
||||
import { createItemCallback } from "~/components/board/items/actions/create-item";
|
||||
@@ -42,8 +42,8 @@ export const BoardSelectionProvider = ({ children }: PropsWithChildren) => {
|
||||
const board = useRequiredBoard();
|
||||
const currentLayoutId = useCurrentLayout();
|
||||
const { updateAndPersistBoard } = usePersistBoard(board);
|
||||
const t = useI18n();
|
||||
const tSelection = useScopedI18n("item.selection");
|
||||
const tCommon = useI18n("common");
|
||||
const tSelection = useI18n("item.selection");
|
||||
|
||||
const persistBoard = useCallback(
|
||||
(updater: (previous: typeof board) => typeof board) =>
|
||||
@@ -205,7 +205,7 @@ export const BoardSelectionProvider = ({ children }: PropsWithChildren) => {
|
||||
const notificationId = `batch-remove-${Date.now()}`;
|
||||
showSuccessNotification({
|
||||
id: notificationId,
|
||||
title: t("common.success"),
|
||||
title: tCommon("success"),
|
||||
message: (
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Text size="sm">{tSelection("removed", { count })}</Text>
|
||||
@@ -220,13 +220,13 @@ export const BoardSelectionProvider = ({ children }: PropsWithChildren) => {
|
||||
notifications.hide(notificationId);
|
||||
}}
|
||||
>
|
||||
{t("common.action.undo")}
|
||||
{tCommon("action.undo")}
|
||||
</Button>
|
||||
</Group>
|
||||
),
|
||||
autoClose: 10_000,
|
||||
});
|
||||
}, [board.items, clearSelection, persistBoard, selectedItemIds, t, tSelection]);
|
||||
}, [board.items, clearSelection, persistBoard, selectedItemIds, tCommon, tSelection]);
|
||||
|
||||
const moveSelectedItemsToSection = useCallback(
|
||||
(targetSectionId: string) => {
|
||||
|
||||
@@ -20,8 +20,8 @@ import { IconCheck, IconClipboard, IconCopy, IconFolderShare, IconTrash, IconX }
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context";
|
||||
import { useEditMode } from "@homarr/boards/edit-mode";
|
||||
import { getBoardLaneColumnCount, getRootSectionLane } from "@homarr/definitions";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import { getBoardLaneColumnCount, getRootSectionLane, getWidgetName } from "@homarr/definitions";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { widgetCatalogIcons } from "@homarr/widgets/catalog";
|
||||
|
||||
import type { EmptySection } from "~/app/[locale]/boards/_types";
|
||||
@@ -43,7 +43,7 @@ export const BoardSelectionToolbar = () => {
|
||||
const currentLayoutId = useCurrentLayout();
|
||||
const currentLayout = board.layouts.find((layout) => layout.id === currentLayoutId);
|
||||
const t = useI18n();
|
||||
const tSelection = useScopedI18n("item.selection");
|
||||
const tSelection = useI18n("item.selection");
|
||||
|
||||
const selectedItems = board.items.filter((item) => selectedItemIds.has(item.id));
|
||||
const hasAppWidgets = selectedItems.some((item) => item.kind === "app");
|
||||
@@ -71,7 +71,7 @@ export const BoardSelectionToolbar = () => {
|
||||
const appId = (item.options as { appId?: string })?.appId;
|
||||
const app = appId ? appMap.get(appId) : undefined;
|
||||
if (!displayName) {
|
||||
displayName = app?.name || t("widget.app.name");
|
||||
displayName = app?.name || getWidgetName("app", t);
|
||||
}
|
||||
if (app?.iconUrl) {
|
||||
iconUrl = app.iconUrl;
|
||||
@@ -79,7 +79,7 @@ export const BoardSelectionToolbar = () => {
|
||||
}
|
||||
|
||||
if (!displayName) {
|
||||
displayName = t(`widget.${item.kind}.name`);
|
||||
displayName = getWidgetName(item.kind, t);
|
||||
}
|
||||
|
||||
return { displayName, iconUrl, IconComponent, kind: item.kind };
|
||||
|
||||
@@ -16,7 +16,6 @@ import { BoardSelectionProvider, useBoardSelection } from "./board-selection-con
|
||||
|
||||
vi.mock("@homarr/translation/client", () => ({
|
||||
useI18n: () => (key: string) => key,
|
||||
useScopedI18n: () => (key: string) => key,
|
||||
}));
|
||||
|
||||
vi.mock("../audio/board-sounds", () => ({
|
||||
|
||||
Reference in new issue
Block a user