diff --git a/apps/nextjs/src/app/[locale]/auth/invite/[id]/_registration-form.tsx b/apps/nextjs/src/app/[locale]/auth/invite/[id]/_registration-form.tsx index 9bfe3bfd5..677356dd4 100644 --- a/apps/nextjs/src/app/[locale]/auth/invite/[id]/_registration-form.tsx +++ b/apps/nextjs/src/app/[locale]/auth/invite/[id]/_registration-form.tsx @@ -7,7 +7,7 @@ import type { z } from "zod/v4"; import { clientApi } from "@homarr/api/client"; import { useZodForm } from "@homarr/form"; import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import { CustomPasswordInput } from "@homarr/ui"; import { userRegistrationSchema } from "@homarr/validation/user"; @@ -19,7 +19,7 @@ interface RegistrationFormProps { } export const RegistrationForm = ({ invite }: RegistrationFormProps) => { - const t = useScopedI18n("user"); + const t = useI18n("user"); const router = useRouter(); const { mutate, isPending } = clientApi.user.register.useMutation(); const form = useZodForm(userRegistrationSchema, { diff --git a/apps/nextjs/src/app/[locale]/auth/invite/[id]/page.tsx b/apps/nextjs/src/app/[locale]/auth/invite/[id]/page.tsx index 475464aff..0f7ddefc7 100644 --- a/apps/nextjs/src/app/[locale]/auth/invite/[id]/page.tsx +++ b/apps/nextjs/src/app/[locale]/auth/invite/[id]/page.tsx @@ -6,7 +6,7 @@ import { isProviderEnabled } from "@homarr/auth/server"; import { and, db, eq } from "@homarr/db"; import { invites } from "@homarr/db/schema"; import { OnboardingAuthShell } from "@homarr/onboarding"; -import { getScopedI18n } from "@homarr/translation/server"; +import { getI18n } from "@homarr/translation/server"; import { RegistrationForm } from "./_registration-form"; @@ -45,7 +45,7 @@ export default async function InviteUsagePage(props: InviteUsagePageProps) { if (!invite || invite.expirationDate < new Date()) notFound(); - const t = await getScopedI18n("user.page.invite"); + const t = await getI18n("user.page.invite"); return ( { - const t = useScopedI18n("user"); + const t = useI18n("user"); const searchParams = useSearchParams(); const isError = searchParams.has("error"); const router = useRouter(); @@ -196,7 +196,7 @@ interface PasswordForgottenCollapseProps { } const PasswordForgottenCollapse = ({ username }: PasswordForgottenCollapseProps) => { const [visible, { toggle }] = useDisclosure(false); - const tForgotPassword = useScopedI18n("user.action.login.forgotPassword"); + const tForgotPassword = useI18n("user.action.login.forgotPassword"); const commandUsername = username.trim().length >= 1 ? username.trim() : ""; diff --git a/apps/nextjs/src/app/[locale]/auth/login/page.tsx b/apps/nextjs/src/app/[locale]/auth/login/page.tsx index 8286c6205..b1c0a1d1b 100644 --- a/apps/nextjs/src/app/[locale]/auth/login/page.tsx +++ b/apps/nextjs/src/app/[locale]/auth/login/page.tsx @@ -5,7 +5,7 @@ import { IconLogin } from "@tabler/icons-react"; import { env } from "@homarr/auth/env"; import { auth } from "@homarr/auth/next"; import { OnboardingAuthShell } from "@homarr/onboarding"; -import { getScopedI18n } from "@homarr/translation/server"; +import { getI18n } from "@homarr/translation/server"; import { sanitizeRedirectionUrl } from "@homarr/validation/redirection-url"; import { env as appEnv } from "~/env"; @@ -26,7 +26,7 @@ export default async function Login(props: LoginProps) { redirect(sanitizeRedirectionUrl(searchParams.callbackUrl)); } - const t = await getScopedI18n("user.page.login"); + const t = await getI18n("user.page.login"); return ( }> diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/[name]/not-found.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/[name]/not-found.tsx index 9c398669b..12128b7e4 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/[name]/not-found.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/[name]/not-found.tsx @@ -1,11 +1,11 @@ import { IconLayoutOff } from "@tabler/icons-react"; -import { getScopedI18n } from "@homarr/translation/server"; +import { getI18n } from "@homarr/translation/server"; import { BoardNotFound } from "~/components/board/not-found"; export default async function BoardNotFoundPage() { - const tNotFound = await getScopedI18n("board.error.notFound"); + const tNotFound = await getI18n("board.error.notFound"); return ( { const board = useRequiredBoard(); - const t = useScopedI18n("board.landmark"); - const tPreview = useScopedI18n("board.setting.section.layout.preview"); - const tTips = useScopedI18n("tips"); + const t = useI18n("board.landmark"); + const tPreview = useI18n("board.setting.section.layout.preview"); + const tTips = useI18n("tips"); const [isEditMode] = useEditMode(); const currentLayoutId = useCurrentLayout(); const initialViewportWidth = useInitialViewportWidth(); diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx index 9833cc4e2..9cf907b5e 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_creator.tsx @@ -104,10 +104,10 @@ export const createBoardContentPage = >( generateMetadataAsync: async ({ params }: { params: Promise }): Promise => { try { const board = await getInitialBoard(await params); - const t = await getI18n(); + const t = await getI18n("board"); return { - title: board.metaTitle ?? createMetaTitle(t("board.content.metaTitle", { boardName: board.name })), + title: board.metaTitle ?? createMetaTitle(t("content.metaTitle", { boardName: board.name })), icons: { icon: !isNullOrWhitespace(board.faviconImageUrl) ? board.faviconImageUrl : undefined, apple: !isNullOrWhitespace(board.faviconImageUrl) ? board.faviconImageUrl : undefined, diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_edit-actions.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_edit-actions.tsx index 0c9404897..29dbc8ac0 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_edit-actions.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_edit-actions.tsx @@ -29,7 +29,9 @@ const AddMenu = () => { const { openModal: openIntegrationSelectModal } = useModalAction(IntegrationSelectModal); const { addContainer } = useContainerActions(); const { createItem } = useItemActions(); - const t = useI18n(); + const tBoard = useI18n("board"); + const tIntegration = useI18n("integration"); + const tContainer = useI18n("section.container"); const pathname = usePathname(); const router = useRouter(); const searchParams = useSearchParams(); @@ -77,7 +79,7 @@ const AddMenu = () => { return ( - + @@ -86,19 +88,19 @@ const AddMenu = () => { } onClick={handleSelectItem}> - {t("item.action.create")} + {tBoard("emptyState.addWidget")} } onClick={handleSelectApp}> - {t("app.action.add")} + {tBoard("emptyState.addApp")} {session?.user.permissions.includes("integration-create") && ( } onClick={handleAddIntegration}> - {t("integration.action.create")} + {tIntegration("action.create")} )} } onClick={addContainer}> - {t("section.container.action.create")} + {tContainer("action.create")} diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_header-actions.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_header-actions.tsx index 397d5274f..f85d78d27 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_header-actions.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_header-actions.tsx @@ -24,7 +24,7 @@ import { env } from "@homarr/common/env"; import { hotkeys } from "@homarr/definitions"; import { useConfirmModal } from "@homarr/modals"; import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; -import { useI18n, useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import { Link } from "@homarr/ui"; import { useBoardPermissions } from "~/components/board/permissions/client"; @@ -53,7 +53,7 @@ const BoardEditActions = dynamic(loadEditActionsAsync, { ssr: false }); export const BoardContentHeaderActions = ({ demoReadOnly }: { demoReadOnly: boolean }) => { const [isEditMode] = useEditMode(); const board = useRequiredBoard(); - const t = useI18n(); + const t = useI18n("board"); const { hasChangeAccess } = useBoardPermissions(board); if (!hasChangeAccess) { @@ -68,7 +68,7 @@ export const BoardContentHeaderActions = ({ demoReadOnly }: { demoReadOnly: bool {!demoReadOnly && ( - + @@ -88,8 +88,9 @@ const EditModeMenu = ({ demoReadOnly }: { demoReadOnly: boolean }) => { const router = useRouter(); const searchParams = useSearchParams(); const { openConfirmModal } = useConfirmModal(); - const commonT = useI18n(); - const t = useScopedI18n("board.action.edit"); + const tCommon = useI18n("common"); + const tBoardSetting = useI18n("board.setting"); + const t = useI18n("board.action.edit"); const returnToLayoutSettings = isEditMode && searchParams.get("edit") === "true" && @@ -104,7 +105,7 @@ const EditModeMenu = ({ demoReadOnly }: { demoReadOnly: boolean }) => { }, onSuccess() { showSuccessNotification({ - title: t("notification.success.title"), + title: tCommon("notification.update.success"), message: t("notification.success.message"), }); void utils.board.getBoardByName.invalidate({ name: board.name }); @@ -115,7 +116,7 @@ const EditModeMenu = ({ demoReadOnly }: { demoReadOnly: boolean }) => { }, onError() { showErrorNotification({ - title: t("notification.error.title"), + title: tCommon("notification.update.error"), message: t("notification.error.message"), }); }, @@ -178,9 +179,9 @@ const EditModeMenu = ({ demoReadOnly }: { demoReadOnly: boolean }) => { const cancelLayoutEdit = useCallback(() => { openConfirmModal({ - title: commonT("board.setting.section.layout.edit.cancel.title"), - children: commonT("board.setting.section.layout.edit.cancel.message"), - confirmProps: { children: commonT("common.action.discard"), color: "red" }, + title: tBoardSetting("section.layout.edit.cancel.title"), + children: tBoardSetting("section.layout.edit.cancel.message"), + confirmProps: { children: tCommon("action.discard"), color: "red" }, onConfirm() { void (async () => { await utils.board.getBoardByName.invalidate({ name: board.name }); @@ -189,7 +190,7 @@ const EditModeMenu = ({ demoReadOnly }: { demoReadOnly: boolean }) => { })(); }, }); - }, [board.name, close, commonT, openConfirmModal, router, utils.board.getBoardByName]); + }, [board.name, close, openConfirmModal, router, tBoardSetting, tCommon, utils.board.getBoardByName]); useHotkeys([[hotkeys.toggleBoardEdit, () => void toggle()]]); usePreventLeaveWithDirty(isEditMode); @@ -200,14 +201,14 @@ const EditModeMenu = ({ demoReadOnly }: { demoReadOnly: boolean }) => { saveBoard(board)} loading={isPending} - aria-label={commonT("board.setting.section.layout.edit.save")} + aria-label={tBoardSetting("section.layout.edit.save")} > @@ -235,7 +236,7 @@ const EditModeMenu = ({ demoReadOnly }: { demoReadOnly: boolean }) => { }; const SelectBoardsMenu = () => { - const t = useI18n(); + const t = useI18n("board"); const [isOpen, setIsOpen] = useState(false); const utils = clientApi.useUtils(); const { data: boards = [], isPending } = clientApi.board.getAllBoards.useQuery(undefined, { enabled: isOpen }); @@ -246,7 +247,7 @@ const SelectBoardsMenu = () => { - + @@ -277,7 +278,8 @@ const SelectBoardsMenu = () => { const anchorSelector = "a[href]:not([target='_blank'])"; const usePreventLeaveWithDirty = (isDirty: boolean) => { - const t = useI18n(); + const tBoard = useI18n("board"); + const tCommon = useI18n("common"); const { openConfirmModal } = useConfirmModal(); const router = useRouter(); @@ -293,13 +295,13 @@ const usePreventLeaveWithDirty = (isDirty: boolean) => { event.stopPropagation(); openConfirmModal({ - title: t("board.action.edit.confirmLeave.title"), - children: t("board.action.edit.confirmLeave.message"), + title: tBoard("action.edit.confirmLeave.title"), + children: tBoard("action.edit.confirmLeave.message"), onConfirm() { router.push(target.href); }, confirmProps: { - children: t("common.action.discard"), + children: tCommon("action.discard"), }, }); }; @@ -325,5 +327,5 @@ const usePreventLeaveWithDirty = (isDirty: boolean) => { window.removeEventListener("popstate", handlePopState); window.removeEventListener("beforeunload", handleBeforeUnload); }; - }, [isDirty, openConfirmModal, router, t]); + }, [isDirty, openConfirmModal, router, tBoard, tCommon]); }; diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/not-found.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/not-found.tsx index 5ab6c8c61..016d0163e 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/not-found.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/not-found.tsx @@ -16,15 +16,15 @@ export default async function NotFoundBoardHomePage() { const getPropsAsync = async (): Promise => { const boardCount = await db.$count(boards); - const t = await getI18n(); + const t = await getI18n("board"); if (boardCount === 0) { return { icon: { src: "/favicon.ico", alt: "Homarr logo" }, - title: t("board.error.noBoard.title"), - description: t("board.error.noBoard.description"), - link: { label: t("board.error.noBoard.link"), href: "/manage/boards" }, - notice: t("board.error.noBoard.notice"), + title: t("error.noBoard.title"), + description: t("error.noBoard.description"), + link: { label: t("error.noBoard.link"), href: "/manage/boards" }, + notice: t("error.noBoard.notice"), }; } @@ -39,9 +39,9 @@ const getPropsAsync = async (): Promise => { return { icon: IconHomeOff, - title: t(`board.error.homeBoard.title`), - description: t(`board.error.homeBoard.${type}.description`), - link: { label: t(`board.error.homeBoard.${type}.link`), href }, - notice: t(`board.error.homeBoard.${type}.notice`), + title: t("error.homeBoard.title"), + description: t(`error.homeBoard.${type}.description` as never), + link: { label: t(`error.homeBoard.${type}.link` as never), href }, + notice: t(`error.homeBoard.${type}.notice` as never), }; }; diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_appereance.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_appereance.tsx index b0e75d34d..674e5f29b 100644 --- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_appereance.tsx +++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_appereance.tsx @@ -37,22 +37,23 @@ const progressPercentageLabel = (value: number) => `${value}%`; export const ColorSettingsContent = ({ form }: Props) => { const [showPreview, { toggle }] = useDisclosure(false); - const t = useI18n(); + const tBoard = useI18n("board"); + const tCommon = useI18n("common"); const theme = useMantineTheme(); return ( - + - + - + - {showPreview ? t("common.preview.hide") : t("common.preview.show")} + {showPreview ? tCommon("preview.hide") : tCommon("preview.show")} @@ -63,7 +64,7 @@ export const ColorSettingsContent = ({ form }: Props) => { - + { color[6])} flex={1} @@ -90,20 +91,20 @@ export const ColorSettingsContent = ({ form }: Props) => { onClick={() => form.setFieldValue("iconColor", "")} disabled={!form.values.iconColor} > - {t("board.field.clearColor.label")} + {tBoard("field.clearColor.label")} } value={values[kind] ?? ""} onChange={(event) => setValues((current) => ({ ...current, [kind]: event.currentTarget.value }))} diff --git a/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx b/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx index e4ea2fc02..52340a72e 100644 --- a/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx +++ b/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx @@ -1,12 +1,12 @@ import { Box, Group, Image, Stack, Title } from "@mantine/core"; -import { getScopedI18n } from "@homarr/translation/server"; +import { getI18n } from "@homarr/translation/server"; import { IntegrationMarquee } from "@homarr/ui"; import classes from "./hero-banner.module.css"; export const HeroBanner = async () => { - const t = await getScopedI18n("management.page.home"); + const t = await getI18n("management.page.home"); return ( diff --git a/apps/nextjs/src/app/[locale]/manage/about/page.tsx b/apps/nextjs/src/app/[locale]/manage/about/page.tsx index 9577d39a0..d0388f427 100644 --- a/apps/nextjs/src/app/[locale]/manage/about/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/about/page.tsx @@ -35,7 +35,7 @@ import { import { capitalize, objectEntries } from "@homarr/common"; import { hotkeys } from "@homarr/definitions"; -import { getScopedI18n } from "@homarr/translation/server"; +import { getI18n } from "@homarr/translation/server"; import { homarrLogoPath } from "~/components/layout/logo/homarr-logo"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; @@ -47,7 +47,7 @@ import crowdinContributors from "@static-data/translators.json"; import classes from "./about.module.css"; export async function generateMetadata() { - const t = await getScopedI18n("management"); + const t = await getI18n("management"); return { title: createMetaTitle(t("metaTitle")), @@ -55,7 +55,7 @@ export async function generateMetadata() { } export default async function AboutPage() { - const t = await getScopedI18n("management.page.about"); + const t = await getI18n("management.page.about"); const version = getPackageVersion(); const dependencies = await getDependenciesAsync(); diff --git a/apps/nextjs/src/app/[locale]/manage/apps/_app-delete-button.tsx b/apps/nextjs/src/app/[locale]/manage/apps/_app-delete-button.tsx index 190737611..477dffa24 100644 --- a/apps/nextjs/src/app/[locale]/manage/apps/_app-delete-button.tsx +++ b/apps/nextjs/src/app/[locale]/manage/apps/_app-delete-button.tsx @@ -9,15 +9,15 @@ import { clientApi } from "@homarr/api/client"; import { revalidatePathActionAsync } from "@homarr/common/client"; import { useConfirmModal } from "@homarr/modals"; import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; interface AppDeleteButtonProps { app: RouterOutputs["app"]["all"][number]; } export const AppDeleteButton = ({ app }: AppDeleteButtonProps) => { - const t = useScopedI18n("app.page.delete"); - const tList = useScopedI18n("app.page.list.action"); + const t = useI18n("app.page.delete"); + const tCommon = useI18n("common"); const { openConfirmModal } = useConfirmModal(); const { mutate, isPending } = clientApi.app.delete.useMutation(); @@ -33,14 +33,14 @@ export const AppDeleteButton = ({ app }: AppDeleteButtonProps) => { { onSuccess: () => { showSuccessNotification({ - title: t("notification.success.title"), + title: tCommon("notification.delete.success"), message: t("notification.success.message"), }); void revalidatePathActionAsync("/manage/apps"); }, onError: () => { showErrorNotification({ - title: t("notification.error.title"), + title: tCommon("notification.delete.error"), message: t("notification.error.message"), }); }, @@ -48,7 +48,7 @@ export const AppDeleteButton = ({ app }: AppDeleteButtonProps) => { ); }, }); - }, [app, mutate, t, openConfirmModal]); + }, [app, mutate, t, tCommon, openConfirmModal]); return ( { color="red" size={44} onClick={onClick} - aria-label={tList("delete", { name: app.name })} + aria-label={tCommon("action.deleteNamed", { name: app.name })} > diff --git a/apps/nextjs/src/app/[locale]/manage/apps/edit/[id]/_app-edit-form.tsx b/apps/nextjs/src/app/[locale]/manage/apps/edit/[id]/_app-edit-form.tsx index 0f14be6b4..cf4be14b8 100644 --- a/apps/nextjs/src/app/[locale]/manage/apps/edit/[id]/_app-edit-form.tsx +++ b/apps/nextjs/src/app/[locale]/manage/apps/edit/[id]/_app-edit-form.tsx @@ -9,7 +9,7 @@ import { clientApi } from "@homarr/api/client"; import { revalidatePathActionAsync } from "@homarr/common/client"; import { AppForm } from "@homarr/forms-collection"; import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; -import { useI18n, useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import type { appManageSchema } from "@homarr/validation/app"; interface AppEditFormProps { @@ -17,14 +17,14 @@ interface AppEditFormProps { } export const AppEditForm = ({ app }: AppEditFormProps) => { - const tScoped = useScopedI18n("app.page.edit.notification"); - const t = useI18n(); + const tScoped = useI18n("app.page.edit.notification"); + const tCommon = useI18n("common"); const router = useRouter(); const { mutate, isPending } = clientApi.app.update.useMutation({ onSuccess: () => { showSuccessNotification({ - title: tScoped("success.title"), + title: tCommon("notification.update.success"), message: tScoped("success.message"), }); void revalidatePathActionAsync("/manage/apps").then(() => { @@ -33,7 +33,7 @@ export const AppEditForm = ({ app }: AppEditFormProps) => { }, onError: () => { showErrorNotification({ - title: tScoped("error.title"), + title: tCommon("notification.update.error"), message: tScoped("error.message"), }); }, @@ -52,7 +52,7 @@ export const AppEditForm = ({ app }: AppEditFormProps) => { return ( ) : ( ))} diff --git a/apps/nextjs/src/app/[locale]/manage/assistant/_components/assistant-configuration.tsx b/apps/nextjs/src/app/[locale]/manage/assistant/_components/assistant-configuration.tsx index 4c1b86132..9c757048b 100644 --- a/apps/nextjs/src/app/[locale]/manage/assistant/_components/assistant-configuration.tsx +++ b/apps/nextjs/src/app/[locale]/manage/assistant/_components/assistant-configuration.tsx @@ -51,7 +51,7 @@ import { } from "@homarr/definitions"; import type { AssistantProvider, AssistantProviderCategory } from "@homarr/definitions"; import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import { getWorkshopAssistantProviderUrl } from "~/components/workshop/workshop-client"; @@ -140,7 +140,8 @@ const ConfigurationSkeleton = ({ label }: { label: string }) => ( ); export const AssistantConfiguration = () => { - const t = useScopedI18n("management.page.settings.section.assistant"); + const t = useI18n("management.page.settings.section.assistant"); + const tCommon = useI18n("common"); const router = useRouter(); const utils = clientApi.useUtils(); const { data: configuration, isLoading } = clientApi.assistant.getAdminConfiguration.useQuery(); @@ -208,7 +209,7 @@ export const AssistantConfiguration = () => { .filter((providerId) => assistantProviderPresets[providerId].category === category) .map((providerId) => ({ value: providerId, - label: t(`provider.options.${providerId}.label`), + label: assistantProviderPresets[providerId].label ?? t("provider.options.custom.label"), })); return { group: t(`provider.groups.${category}`), items }; @@ -372,7 +373,7 @@ export const AssistantConfiguration = () => { - {t(`provider.options.${provider}.label`)} + {assistantProviderPresets[provider].label ?? t("provider.options.custom.label")} { description={t("connection.description")} status={ - {t(`provider.options.${provider}.label`)} + {assistantProviderPresets[provider].label ?? t("provider.options.custom.label")} } > @@ -758,7 +759,7 @@ export const AssistantConfiguration = () => { {t("serverTools.webSearch.label")} - {t("serverTools.beta")} + {tCommon("beta")} diff --git a/apps/nextjs/src/app/[locale]/manage/assistant/page.tsx b/apps/nextjs/src/app/[locale]/manage/assistant/page.tsx index d8482fcfc..479beeec1 100644 --- a/apps/nextjs/src/app/[locale]/manage/assistant/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/assistant/page.tsx @@ -2,13 +2,13 @@ import { notFound } from "next/navigation"; import { Text } from "@mantine/core"; import { auth } from "@homarr/auth/next"; -import { getScopedI18n } from "@homarr/translation/server"; +import { getI18n } from "@homarr/translation/server"; import { ManagePageLayout } from "~/components/manage/manage-page-layout"; import { AssistantConfiguration } from "./_components/assistant-configuration"; export async function generateMetadata() { - const t = await getScopedI18n("management.page.settings.section.assistant"); + const t = await getI18n("management.page.settings.section.assistant"); return { title: `${t("title")} • Homarr`, }; @@ -21,7 +21,7 @@ export default async function AssistantPage() { notFound(); } - const t = await getScopedI18n("management.page.settings.section.assistant"); + const t = await getI18n("management.page.settings.section.assistant"); return ( diff --git a/apps/nextjs/src/app/[locale]/manage/boards/_components/board-card-menu-dropdown.tsx b/apps/nextjs/src/app/[locale]/manage/boards/_components/board-card-menu-dropdown.tsx index 744f998d5..0d15e68a1 100644 --- a/apps/nextjs/src/app/[locale]/manage/boards/_components/board-card-menu-dropdown.tsx +++ b/apps/nextjs/src/app/[locale]/manage/boards/_components/board-card-menu-dropdown.tsx @@ -10,7 +10,7 @@ import { useSession } from "@homarr/auth/client"; import { revalidatePathActionAsync } from "@homarr/common/client"; import { useConfirmModal, useModalAction } from "@homarr/modals"; import { DuplicateBoardModal } from "@homarr/modals-collection"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import { Link } from "@homarr/ui"; import { useBoardPermissions } from "~/components/board/permissions/client"; @@ -28,8 +28,9 @@ interface BoardCardMenuDropdownProps { } export const BoardCardMenuDropdown = ({ board }: BoardCardMenuDropdownProps) => { - const t = useScopedI18n("management.page.board.action"); - const tCommon = useScopedI18n("common"); + const t = useI18n("management.page.board.action"); + const tRoot = useI18n(); + const tCommon = useI18n("common"); const { hasFullAccess, hasChangeAccess } = useBoardPermissions(board); const { data: session } = useSession(); @@ -57,7 +58,7 @@ export const BoardCardMenuDropdown = ({ board }: BoardCardMenuDropdownProps) => const handleDeletion = useCallback(() => { openConfirmModal({ - title: t("delete.confirm.title"), + title: tCommon("action.deleteNamed", { name: board.name }), children: t("delete.confirm.description", { name: board.name, }), @@ -68,7 +69,7 @@ export const BoardCardMenuDropdown = ({ board }: BoardCardMenuDropdownProps) => }); }, }); - }, [board.id, board.name, deleteBoardMutation, openConfirmModal, t]); + }, [board.id, board.name, deleteBoardMutation, openConfirmModal, t, tCommon]); const handleSetHomeBoard = useCallback(async () => { await setHomeBoardMutation.mutateAsync({ id: board.id }); @@ -97,7 +98,7 @@ export const BoardCardMenuDropdown = ({ board }: BoardCardMenuDropdownProps) => {session?.user.permissions.includes("board-create") && ( }> - {t("duplicate.label")} + {tRoot("board.action.duplicate.title")} )} {hasChangeAccess && ( @@ -122,7 +123,7 @@ export const BoardCardMenuDropdown = ({ board }: BoardCardMenuDropdownProps) => onClick={handleDeletion} disabled={deleteBoardMutation.isPending} > - {t("delete.label")} + {tCommon("action.delete")} )} diff --git a/apps/nextjs/src/app/[locale]/manage/boards/_components/create-board-button.tsx b/apps/nextjs/src/app/[locale]/manage/boards/_components/create-board-button.tsx index 77fed0399..f6c9ed735 100644 --- a/apps/nextjs/src/app/[locale]/manage/boards/_components/create-board-button.tsx +++ b/apps/nextjs/src/app/[locale]/manage/boards/_components/create-board-button.tsx @@ -10,13 +10,13 @@ import { useI18n } from "@homarr/translation/client"; import { ManageMobilePrimaryAction } from "~/components/manage/manage-mobile-primary-action"; export const CreateBoardButton = () => { - const t = useI18n(); + const t = useI18n("management.page.board.action"); const { openModal } = useModalAction(AddBoardModal); return ( ); diff --git a/apps/nextjs/src/app/[locale]/manage/boards/page.tsx b/apps/nextjs/src/app/[locale]/manage/boards/page.tsx index 4fb20c461..c20cf6c74 100644 --- a/apps/nextjs/src/app/[locale]/manage/boards/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/boards/page.tsx @@ -25,7 +25,7 @@ import type { RouterOutputs } from "@homarr/api"; import { api } from "@homarr/api/server"; import { auth } from "@homarr/auth/next"; import { constructBoardPermissions } from "@homarr/auth/shared"; -import { getScopedI18n } from "@homarr/translation/server"; +import { getI18n } from "@homarr/translation/server"; import { Link, UserAvatar } from "@homarr/ui"; import { BoardLayoutThumbnail } from "~/components/board/board-layout-thumbnail"; @@ -36,7 +36,7 @@ import { BoardCardMenuDropdown } from "./_components/board-card-menu-dropdown"; import { CreateBoardButton } from "./_components/create-board-button"; export default async function ManageBoardsPage() { - const t = await getScopedI18n("management.page.board"); + const t = await getI18n("management.page.board"); const session = await auth(); const boards = await api.board.getManageOverview(); const canCreateBoards = session?.user.permissions.includes("board-create"); diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_beta-banner.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_beta-banner.tsx index c26960c4d..81e38ec33 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_beta-banner.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_beta-banner.tsx @@ -3,12 +3,12 @@ import { Alert, Anchor, Text } from "@mantine/core"; import { IconFlask } from "@tabler/icons-react"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; const FEEDBACK_URL = "https://github.com/homarr-labs/homarr/discussions/categories/custom-widgets"; export function CustomWidgetBetaBanner() { - const t = useScopedI18n("customWidget.betaBanner"); + const t = useI18n("customWidget.betaBanner"); return ( }> diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_code-editor.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_code-editor.tsx index 82d9db64a..275ab1897 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_code-editor.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_code-editor.tsx @@ -3,20 +3,22 @@ import type { ComponentProps } from "react"; import { CustomWidgetCodeEditor } from "@homarr/custom-widgets/workbench"; -import { useScopedI18n } from "@homarr/translation/client"; +import { invariantTechnicalLabels } from "@homarr/definitions"; +import { useI18n } from "@homarr/translation/client"; type SharedEditorProps = ComponentProps; type CodeEditorProps = Omit; export function CodeEditor(props: CodeEditorProps) { - const t = useScopedI18n("customWidget.editor"); + const t = useI18n("customWidget.editor"); + const tCommon = useI18n("common"); return ( t("componentReference.count", { count }), insertStarter: t("action.insertStarter"), format: t("action.format"), - copy: t("action.copy"), + copy: tCommon("action.copy"), copied: t("action.copied"), schema: t("action.schema"), - schemaTab: t("reference.schema"), + schemaTab: invariantTechnicalLabels.jsonSchema, minimalTab: t("reference.minimal"), fullTab: t("reference.full"), errors: (count) => t("status.errors", { count }), diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_copy-ai-prompt-button.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_copy-ai-prompt-button.tsx index 2cc4cb1e4..2e05917c7 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_copy-ai-prompt-button.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_copy-ai-prompt-button.tsx @@ -5,7 +5,7 @@ import { IconCopy, IconRobot } from "@tabler/icons-react"; import { buildCustomWidgetAiPrompt } from "@homarr/custom-widgets/authoring-prompt"; import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import { useOptionalHomarrAssistant } from "~/components/assistant/assistant-context"; @@ -22,7 +22,7 @@ export const CopyAiPromptButton = ({ request, documentationUrl, }: CopyAiPromptButtonProps) => { - const t = useScopedI18n("customWidget"); + const t = useI18n("customWidget"); const assistant = useOptionalHomarrAssistant(); const prompt = () => buildCustomWidgetAiPrompt(undefined, rawResponse, currentConfig, request, documentationUrl); diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-actions.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-actions.tsx index 02ee78c46..8e376330b 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-actions.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-actions.tsx @@ -22,7 +22,7 @@ import { formatCustomWidgetImportIssues, parseCustomWidgetClipboardDetailed } fr import type { HomarrCustomWidgetV2 } from "@homarr/custom-widgets/core"; import { useConfirmModal } from "@homarr/modals"; import { showErrorNotification, showSuccessNotification } from "@homarr/notifications"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import { Link } from "@homarr/ui"; import { CustomWidgetImportDialog } from "~/components/custom-widgets/custom-widget-import-dialog"; @@ -39,7 +39,7 @@ interface WidgetRef { } export const CustomWidgetRowActions = ({ widget }: { widget: WidgetRef }) => { - const t = useScopedI18n("customWidget"); + const t = useI18n("customWidget"); const { openConfirmModal } = useConfirmModal(); const deleteMutation = clientApi.customWidget.delete.useMutation(); const duplicateMutation = clientApi.customWidget.duplicate.useMutation(); diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-advanced-manifest.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-advanced-manifest.tsx index 2f2f42e21..4e558f880 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-advanced-manifest.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-advanced-manifest.tsx @@ -4,14 +4,14 @@ import { useEffect, useState } from "react"; import { Accordion } from "@mantine/core"; import { customWidgetDefinitionSchema } from "@homarr/custom-widgets/core"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import { CodeEditor } from "./_code-editor"; import { applyDefinition, parseJson } from "./_custom-widget-form-utils"; import type { CustomWidgetWorkbenchForm } from "./_custom-widget-form-utils"; export function CustomWidgetAdvancedManifest({ form }: { form: CustomWidgetWorkbenchForm }) { - const t = useScopedI18n("customWidget.workbench.builder"); + const t = useI18n("customWidget.workbench.builder"); const serialized = JSON.stringify( { $schema: "homarr-custom-widget-v2", diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-ai-card.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-ai-card.tsx index 3e817a595..0d41ce0ee 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-ai-card.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-ai-card.tsx @@ -4,7 +4,7 @@ import { Accordion, Button, Group, Stack, Text, TextInput, Textarea } from "@man import { IconClipboard, IconSparkles } from "@tabler/icons-react"; import type { HomarrCustomWidgetV2 } from "@homarr/custom-widgets/core"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import { CopyAiPromptButton } from "./_copy-ai-prompt-button"; @@ -18,7 +18,7 @@ interface AiCardProps { } export function CustomWidgetAiCard(props: AiCardProps) { - const t = useScopedI18n("customWidget.workbench.ai"); + const t = useI18n("customWidget.workbench.ai"); return ( diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form-layout.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form-layout.tsx index 90829066b..85ab7c6e5 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form-layout.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form-layout.tsx @@ -5,7 +5,7 @@ import type { Icon } from "@tabler/icons-react"; import { Button, Card, Group, Stack, Text, ThemeIcon } from "@mantine/core"; import { IconAlertTriangle, IconCheck, IconPlayerPlay } from "@tabler/icons-react"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import classes from "./_custom-widget-form.module.css"; @@ -52,7 +52,8 @@ export function SaveActions({ mode: "create" | "edit"; onPreview(): void; }) { - const t = useScopedI18n("customWidget.workbench"); + const t = useI18n("customWidget.workbench"); + const tCommon = useI18n("common"); return ( @@ -77,7 +78,7 @@ export function SaveActions({ {previewPending ? t("action.previewLoading") : t("action.preview")} diff --git a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form.tsx b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form.tsx index c1cb2b876..3a416a250 100644 --- a/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form.tsx +++ b/apps/nextjs/src/app/[locale]/manage/custom-widgets/_custom-widget-form.tsx @@ -22,7 +22,7 @@ import { customWidgetOptionsSchema, getCustomWidgetDefaultOptions } from "@homar import type { CustomWidgetFormValues } from "@homarr/custom-widgets/workbench"; import { useZodForm } from "@homarr/form"; import { IconPicker } from "@homarr/forms-collection"; -import { useScopedI18n } from "@homarr/translation/client"; +import { useI18n } from "@homarr/translation/client"; import { CodeEditor } from "./_code-editor"; import { CustomWidgetAiCard } from "./_custom-widget-ai-card"; @@ -57,8 +57,9 @@ const sectionLinks = [ ] as const; export function CustomWidgetForm({ mode, initialValues, definitionId }: CustomWidgetFormProps) { - const t = useScopedI18n("customWidget"); - const w = useScopedI18n("customWidget.workbench"); + const t = useI18n("customWidget"); + const tCommon = useI18n("common"); + const w = useI18n("customWidget.workbench"); const formInitialValues: CustomWidgetFormValues = { ...DEFAULT_CUSTOM_WIDGET_FORM_VALUES, ...initialValues }; const form = useZodForm(customWidgetFormSchema, { initialValues: formInitialValues }); const [mobilePane, setMobilePane] = useState<"configure" | "preview">("configure"); @@ -169,7 +170,7 @@ export function CustomWidgetForm({ mode, initialValues, definitionId }: CustomWi /> - +