diff --git a/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx b/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx
index fd01444d7..e5e867815 100644
--- a/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx
+++ b/apps/nextjs/src/app/[locale]/_client-providers/mantine.tsx
@@ -3,7 +3,7 @@
import type { PropsWithChildren } from "react";
import { useState } from "react";
import type { MantineColorScheme, MantineColorSchemeManager } from "@mantine/core";
-import { createTheme, isMantineColorScheme, MantineProvider } from "@mantine/core";
+import { createTheme, DirectionProvider, isMantineColorScheme, MantineProvider } from "@mantine/core";
import dayjs from "dayjs";
import { clientApi } from "@homarr/api/client";
@@ -14,16 +14,18 @@ export const CustomMantineProvider = ({ children }: PropsWithChildren) => {
const manager = useColorSchemeManager();
return (
-
- {children}
-
+
+
+ {children}
+
+
);
};
diff --git a/apps/nextjs/src/app/[locale]/layout.tsx b/apps/nextjs/src/app/[locale]/layout.tsx
index 92826b989..6adc5aaef 100644
--- a/apps/nextjs/src/app/[locale]/layout.tsx
+++ b/apps/nextjs/src/app/[locale]/layout.tsx
@@ -12,6 +12,7 @@ import { env } from "@homarr/auth/env.mjs";
import { auth } from "@homarr/auth/next";
import { ModalProvider } from "@homarr/modals";
import { Notifications } from "@homarr/notifications";
+import { getScopedI18n } from "@homarr/translation/server";
import { Analytics } from "~/components/layout/analytics";
import { SearchEngineOptimization } from "~/components/layout/search-engine-optimization";
@@ -56,6 +57,8 @@ export const viewport: Viewport = {
export default async function Layout(props: { children: React.ReactNode; params: { locale: string } }) {
const session = await auth();
const colorScheme = cookies().get("homarr-color-scheme")?.value ?? "light";
+ const tCommon = await getScopedI18n("common");
+ const direction = tCommon("direction");
const StackedProvider = composeWrappers([
(innerProps) => {
@@ -70,7 +73,7 @@ export default async function Layout(props: { children: React.ReactNode; params:
return (
// Instead of ColorSchemScript we use data-mantine-color-scheme to prevent flickering
-
+
diff --git a/apps/nextjs/src/app/[locale]/manage/search-engines/page.tsx b/apps/nextjs/src/app/[locale]/manage/search-engines/page.tsx
index 2951e31a9..436380990 100644
--- a/apps/nextjs/src/app/[locale]/manage/search-engines/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/search-engines/page.tsx
@@ -31,7 +31,6 @@ export default async function SearchEnginesPage(props: SearchEnginesPageProps) {
const searchParams = searchParamsSchema.parse(props.searchParams);
const { items: searchEngines, totalCount } = await api.searchEngine.getPaginated(searchParams);
- const t = await getI18n();
const tEngine = await getScopedI18n("search.engine");
return (
@@ -40,13 +39,7 @@ export default async function SearchEnginesPage(props: SearchEnginesPageProps) {
{tEngine("page.list.title")}
-
+
{tEngine("page.create.title")}
diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx
index adad026ee..972b62d99 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx
@@ -48,13 +48,7 @@ export default async function GroupsDetailPage({ params, searchParams }: GroupsD
)}
-
+
{isProviderEnabled("credentials") && (
member.id)} />
)}
diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/page.tsx
index da8ce6967..01614c186 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/groups/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/groups/page.tsx
@@ -36,13 +36,7 @@ export default async function GroupsListPage(props: GroupsListPageProps) {
{t("group.title")}
-
+
diff --git a/apps/nextjs/src/components/layout/header/search.tsx b/apps/nextjs/src/components/layout/header/search.tsx
index 9b2edd78f..6b5fe883e 100644
--- a/apps/nextjs/src/components/layout/header/search.tsx
+++ b/apps/nextjs/src/components/layout/header/search.tsx
@@ -21,10 +21,7 @@ export const DesktopSearchInput = () => {
leftSection={}
onClick={openSpotlight}
>
- {t("common.rtl", {
- value: t("search.placeholder"),
- symbol: "...",
- })}
+ {`${t("search.placeholder")}...`}
);
};
diff --git a/packages/spotlight/src/components/spotlight.tsx b/packages/spotlight/src/components/spotlight.tsx
index d10d4d69c..f84af4add 100644
--- a/packages/spotlight/src/components/spotlight.tsx
+++ b/packages/spotlight/src/components/spotlight.tsx
@@ -51,10 +51,7 @@ export const Spotlight = () => {
store={spotlightStore}
>
{options.pingEnabled && app.href ? (
-
- }
- >
+ }>
) : null}
diff --git a/packages/widgets/src/dns-hole/summary/component.tsx b/packages/widgets/src/dns-hole/summary/component.tsx
index 708256629..513ecc712 100644
--- a/packages/widgets/src/dns-hole/summary/component.tsx
+++ b/packages/widgets/src/dns-hole/summary/component.tsx
@@ -100,14 +100,11 @@ const stats = [
},
{
icon: IconPercentage,
- value: (data, t) =>
- t("common.rtl", {
- value: formatNumber(
- data.reduce((count, { adsBlockedTodayPercentage }) => count + adsBlockedTodayPercentage, 0),
- 2,
- ),
- symbol: "%",
- }),
+ value: (data) =>
+ `${formatNumber(
+ data.reduce((count, { adsBlockedTodayPercentage }) => count + adsBlockedTodayPercentage, 0),
+ 2,
+ )}%`,
label: (t) => t("widget.dnsHoleSummary.data.adsBlockedTodayPercentage"),
color: "rgba(255, 165, 20, 0.4)", // YELLOW
},
@@ -135,7 +132,7 @@ const stats = [
interface StatItem {
icon: TablerIcon;
- value: (x: DnsHoleSummary[], t: TranslationFunction) => string;
+ value: (x: DnsHoleSummary[]) => string;
label: stringOrTranslation;
color: string;
}
@@ -184,14 +181,14 @@ const StatCard = ({ item, data, usePiHoleColors, t }: StatCardProps) => {
gap="1cqmin"
>
- {item.value(data, t)}
+ {item.value(data)}
{item.label && (
diff --git a/packages/widgets/src/downloads/component.tsx b/packages/widgets/src/downloads/component.tsx
index 982f55109..7fafc932a 100644
--- a/packages/widgets/src/downloads/component.tsx
+++ b/packages/widgets/src/downloads/component.tsx
@@ -641,8 +641,6 @@ export default function DownloadClientsWidget({
},
});
- const isLangRtl = tCommon("rtl", { value: "0", symbol: "1" }).startsWith("1");
-
//Used for Global Torrent Ratio
const globalTraffic = clients
.filter(({ integration: { kind } }) =>
@@ -676,13 +674,12 @@ export default function DownloadClientsWidget({
px="var(--space-size)"
justify={integrationTypes.includes("torrent") ? "space-between" : "end"}
style={{
- flexDirection: isLangRtl ? "row-reverse" : "row",
borderTop: "0.0625rem solid var(--border-color)",
}}
>
{integrationTypes.includes("torrent") && (
-
- {tCommon("rtl", { value: t("globalRatio"), symbol: tCommon("symbols.colon") })}
+
+ {`${t("globalRatio")}:`}
{(globalTraffic.up / globalTraffic.down).toFixed(2)}
)}
@@ -758,21 +755,10 @@ const NormalizedLine = ({
values?: number | string | string[];
}) => {
const t = useScopedI18n("widget.downloads.items");
- const tCommon = useScopedI18n("common");
- const translatedKey = t(`${itemKey}.detailsTitle`);
- const isLangRtl = tCommon("rtl", { value: "0", symbol: "1" }).startsWith("1"); //Maybe make a common "isLangRtl" somewhere
- const keyString = tCommon("rtl", { value: translatedKey, symbol: tCommon("symbols.colon") });
if (typeof values !== "number" && (values === undefined || values.length === 0)) return null;
return (
-
- {keyString}
+
+ {`${t(`${itemKey}.detailsTitle`)}:`}
{Array.isArray(values) ? (
{values.map((value) => (
diff --git a/packages/widgets/src/media-requests/stats/component.tsx b/packages/widgets/src/media-requests/stats/component.tsx
index a38a73853..75296856a 100644
--- a/packages/widgets/src/media-requests/stats/component.tsx
+++ b/packages/widgets/src/media-requests/stats/component.tsx
@@ -31,7 +31,6 @@ export default function MediaServerWidget({
itemId,
}: WidgetComponentProps<"mediaRequests-requestStats">) {
const t = useScopedI18n("widget.mediaRequests-requestStats");
- const tCommon = useScopedI18n("common");
const isQueryEnabled = Boolean(itemId);
const { data: requestStats, isError: _isError } = clientApi.widget.mediaRequests.getStats.useQuery(
{
@@ -188,8 +187,7 @@ export default function MediaServerWidget({
{user.displayName}
- {tCommon("rtl", { value: t("titles.users.requests"), symbol: tCommon("symbols.colon") }) +
- user.requestCount}
+ {`${t("titles.users.requests")}: ${user.requestCount}`}
diff --git a/packages/widgets/src/weather/icon.tsx b/packages/widgets/src/weather/icon.tsx
index 25a25b34d..fa44f3d26 100644
--- a/packages/widgets/src/weather/icon.tsx
+++ b/packages/widgets/src/weather/icon.tsx
@@ -62,18 +62,8 @@ export const WeatherDescription = ({ weatherOnly, time, weatherCode, maxTemp, mi
{dayjs(time).format("dddd MMMM D YYYY")}
{t(`kind.${name}`)}
-
- {tCommon("rtl", {
- value: tCommon("information.max"),
- symbol: tCommon("symbols.colon"),
- }) + maxTemp}
-
-
- {tCommon("rtl", {
- value: tCommon("information.min"),
- symbol: tCommon("symbols.colon"),
- }) + minTemp}
-
+ {`${tCommon("information.max")}: ${maxTemp}`}
+ {`${tCommon("information.min")}: ${minTemp}`}
);
};