diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json
index 2ccf1c168..2751dd8ca 100644
--- a/apps/nextjs/package.json
+++ b/apps/nextjs/package.json
@@ -16,6 +16,7 @@
"@alparr/api": "workspace:^0.1.0",
"@alparr/auth": "workspace:^0.1.0",
"@alparr/db": "workspace:^0.1.0",
+ "@alparr/translation": "workspace:^",
"@alparr/ui": "workspace:^0.1.0",
"@alparr/validation": "workspace:^0.1.0",
"@mantine/core": "^7.3.1",
diff --git a/apps/nextjs/src/app/[locale]/_client-providers/next-international.tsx b/apps/nextjs/src/app/[locale]/_client-providers/next-international.tsx
new file mode 100644
index 000000000..27bfdea5e
--- /dev/null
+++ b/apps/nextjs/src/app/[locale]/_client-providers/next-international.tsx
@@ -0,0 +1,15 @@
+import type { PropsWithChildren } from "react";
+
+import { defaultLocale } from "@alparr/translation";
+import { I18nProviderClient } from "@alparr/translation/client";
+
+export const NextInternationalProvider = ({
+ children,
+ locale,
+}: PropsWithChildren<{ locale: string }>) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/apps/nextjs/src/app/providers.tsx b/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx
similarity index 100%
rename from apps/nextjs/src/app/providers.tsx
rename to apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx
diff --git a/apps/nextjs/src/app/auth/login/_components/login-form.tsx b/apps/nextjs/src/app/[locale]/auth/login/_components/login-form.tsx
similarity index 81%
rename from apps/nextjs/src/app/auth/login/_components/login-form.tsx
rename to apps/nextjs/src/app/[locale]/auth/login/_components/login-form.tsx
index 15f8f904d..a556848cc 100644
--- a/apps/nextjs/src/app/auth/login/_components/login-form.tsx
+++ b/apps/nextjs/src/app/[locale]/auth/login/_components/login-form.tsx
@@ -15,9 +15,11 @@ import { IconAlertTriangle } from "@tabler/icons-react";
import type { z } from "zod";
import { signIn } from "@alparr/auth/client";
+import { useScopedI18n } from "@alparr/translation/client";
import { signInSchema } from "@alparr/validation";
export const LoginForm = () => {
+ const t = useScopedI18n("user");
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState();
@@ -54,10 +56,16 @@ export const LoginForm = () => {
diff --git a/apps/nextjs/src/app/auth/login/page.tsx b/apps/nextjs/src/app/[locale]/auth/login/page.tsx
similarity index 73%
rename from apps/nextjs/src/app/auth/login/page.tsx
rename to apps/nextjs/src/app/[locale]/auth/login/page.tsx
index 56bcbb96e..ec70bac1a 100644
--- a/apps/nextjs/src/app/auth/login/page.tsx
+++ b/apps/nextjs/src/app/[locale]/auth/login/page.tsx
@@ -1,19 +1,23 @@
import { Card, Center, Stack, Text, Title } from "@mantine/core";
+import { getScopedI18n } from "@alparr/translation/server";
+
import { LogoWithTitle } from "~/components/layout/logo";
import { LoginForm } from "./_components/login-form";
-export default function Login() {
+export default async function Login() {
+ const t = await getScopedI18n("user.page.login");
+
return (
- Log in to your account
+ {t("title")}
- Welcome back! Please enter your credentials
+ {t("subtitle")}
diff --git a/apps/nextjs/src/app/init/user/_components/init-user-form.tsx b/apps/nextjs/src/app/[locale]/init/user/_components/init-user-form.tsx
similarity index 75%
rename from apps/nextjs/src/app/init/user/_components/init-user-form.tsx
rename to apps/nextjs/src/app/[locale]/init/user/_components/init-user-form.tsx
index 72f697013..9afd37a30 100644
--- a/apps/nextjs/src/app/init/user/_components/init-user-form.tsx
+++ b/apps/nextjs/src/app/[locale]/init/user/_components/init-user-form.tsx
@@ -5,6 +5,7 @@ import { Button, PasswordInput, Stack, TextInput } from "@mantine/core";
import { useForm, zodResolver } from "@mantine/form";
import type { z } from "zod";
+import { useScopedI18n } from "@alparr/translation/client";
import { initUserSchema } from "@alparr/validation";
import { showErrorNotification, showSuccessNotification } from "~/notification";
@@ -12,6 +13,7 @@ import { api } from "~/utils/api";
export const InitUserForm = () => {
const router = useRouter();
+ const t = useScopedI18n("user");
const { mutateAsync, error, isPending } = api.user.initUser.useMutation();
const form = useForm({
validate: zodResolver(initUserSchema),
@@ -20,7 +22,7 @@ export const InitUserForm = () => {
initialValues: {
username: "",
password: "",
- repeatPassword: "",
+ confirmPassword: "",
},
});
@@ -52,14 +54,20 @@ export const InitUserForm = () => {
)}
>
-
-
+
+
diff --git a/apps/nextjs/src/app/init/user/page.tsx b/apps/nextjs/src/app/[locale]/init/user/page.tsx
similarity index 82%
rename from apps/nextjs/src/app/init/user/page.tsx
rename to apps/nextjs/src/app/[locale]/init/user/page.tsx
index 8d8dccdcd..594c8c2b8 100644
--- a/apps/nextjs/src/app/init/user/page.tsx
+++ b/apps/nextjs/src/app/[locale]/init/user/page.tsx
@@ -2,6 +2,7 @@ import { notFound } from "next/navigation";
import { Card, Center, Stack, Text, Title } from "@mantine/core";
import { db } from "@alparr/db";
+import { getScopedI18n } from "@alparr/translation/server";
import { LogoWithTitle } from "~/components/layout/logo";
import { InitUserForm } from "./_components/init-user-form";
@@ -14,19 +15,21 @@ export default async function InitUser() {
});
if (firstUser) {
- return notFound();
+ notFound();
}
+ const t = await getScopedI18n("user.page.init");
+
return (
- New Alparr installation
+ {t("title")}
- Please create the initial administator user.
+ {t("subtitle")}
diff --git a/apps/nextjs/src/app/layout.tsx b/apps/nextjs/src/app/[locale]/layout.tsx
similarity index 68%
rename from apps/nextjs/src/app/layout.tsx
rename to apps/nextjs/src/app/[locale]/layout.tsx
index f116aba0b..39182a7f5 100644
--- a/apps/nextjs/src/app/layout.tsx
+++ b/apps/nextjs/src/app/[locale]/layout.tsx
@@ -11,7 +11,8 @@ import { Notifications } from "@mantine/notifications";
import { uiConfiguration } from "@alparr/ui";
-import { TRPCReactProvider } from "./providers";
+import { NextInternationalProvider } from "./_client-providers/next-international";
+import { TRPCReactProvider } from "./_client-providers/trpc";
const fontSans = Inter({
subsets: ["latin"],
@@ -30,7 +31,10 @@ export const metadata: Metadata = {
description: "Simple monorepo with shared backend for web & mobile apps",
};
-export default function Layout(props: { children: React.ReactNode }) {
+export default function Layout(props: {
+ children: React.ReactNode;
+ params: { locale: string };
+}) {
const colorScheme = "dark";
return (
@@ -40,13 +44,15 @@ export default function Layout(props: { children: React.ReactNode }) {
-
-
- {props.children}
-
+
+
+
+ {props.children}
+
+