mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-11 16:05:47 +01:00
✨ Add translation to user settings
This commit is contained in:
@@ -1,2 +1,4 @@
|
|||||||
export const REPO_URL = 'ajnart/homarr';
|
export const REPO_URL = 'ajnart/homarr';
|
||||||
export const ICON_PICKER_SLICE_LIMIT = 36;
|
export const ICON_PICKER_SLICE_LIMIT = 36;
|
||||||
|
export const COOKIE_LOCALE_KEY = 'config-locale';
|
||||||
|
export const COOKIE_COLOR_SCHEME_KEY = 'color-scheme';
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
import { Group, Select, Stack, Text } from '@mantine/core';
|
import { Group, Select, Stack, Text } from '@mantine/core';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { getCookie, setCookie } from 'cookies-next';
|
import { getCookie, setCookie } from 'cookies-next';
|
||||||
|
import { useSession } from 'next-auth/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { forwardRef, useState } from 'react';
|
import { forwardRef, useState } from 'react';
|
||||||
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
|
import { COOKIE_LOCALE_KEY } from '../../../../../data/constants';
|
||||||
import { Language, getLanguageByCode } from '../../../../tools/language';
|
import { Language, getLanguageByCode } from '../../../../tools/language';
|
||||||
|
|
||||||
export default function LanguageSelect() {
|
export default function LanguageSelect() {
|
||||||
|
const { data: sessionData } = useSession();
|
||||||
const { t, i18n } = useTranslation('settings/general/internationalization');
|
const { t, i18n } = useTranslation('settings/general/internationalization');
|
||||||
const { changeLanguage } = i18n;
|
const { changeLanguage } = i18n;
|
||||||
const configLocale = getCookie('config-locale');
|
const configLocale = getCookie(COOKIE_LOCALE_KEY);
|
||||||
const { locale, locales, pathname, query, asPath, push } = useRouter();
|
const { locale, locales, pathname, query, asPath, push } = useRouter();
|
||||||
const [selectedLanguage, setSelectedLanguage] = useState<string>(
|
const [selectedLanguage, setSelectedLanguage] = useState<string>(
|
||||||
(configLocale as string) ?? locale ?? 'en'
|
sessionData?.user.language ?? (configLocale as string) ?? locale ?? 'en'
|
||||||
);
|
);
|
||||||
|
const { mutateAsync } = api.user.changeLanguage.useMutation();
|
||||||
|
|
||||||
const data = locales
|
const data = locales
|
||||||
? locales.map((localeItem) => ({
|
? locales.map((localeItem) => ({
|
||||||
@@ -30,12 +35,18 @@ export default function LanguageSelect() {
|
|||||||
|
|
||||||
const newLanguage = getLanguageByCode(value);
|
const newLanguage = getLanguageByCode(value);
|
||||||
changeLanguage(value)
|
changeLanguage(value)
|
||||||
.then(() => {
|
.then(async () => {
|
||||||
setCookie('config-locale', value, {
|
setCookie(COOKIE_LOCALE_KEY, value, {
|
||||||
maxAge: 60 * 60 * 24 * 30,
|
maxAge: 60 * 60 * 24 * 30,
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (sessionData?.user && new Date(sessionData.expires) > new Date()) {
|
||||||
|
await mutateAsync({
|
||||||
|
language: value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
push(
|
push(
|
||||||
{
|
{
|
||||||
pathname,
|
pathname,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { Session } from 'next-auth';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
|
|
||||||
|
import { COOKIE_COLOR_SCHEME_KEY } from '../../data/constants';
|
||||||
|
|
||||||
export const useColorScheme = (defaultValue: ColorScheme, session: Session) => {
|
export const useColorScheme = (defaultValue: ColorScheme, session: Session) => {
|
||||||
const [colorScheme, setColorScheme] = useState(defaultValue);
|
const [colorScheme, setColorScheme] = useState(defaultValue);
|
||||||
const { mutateAsync } = api.user.changeColorScheme.useMutation();
|
const { mutateAsync } = api.user.changeColorScheme.useMutation();
|
||||||
@@ -12,7 +14,7 @@ export const useColorScheme = (defaultValue: ColorScheme, session: Session) => {
|
|||||||
const toggleColorScheme = async () => {
|
const toggleColorScheme = async () => {
|
||||||
const newColorScheme = colorScheme === 'dark' ? 'light' : 'dark';
|
const newColorScheme = colorScheme === 'dark' ? 'light' : 'dark';
|
||||||
setColorScheme(newColorScheme);
|
setColorScheme(newColorScheme);
|
||||||
setCookie('color-scheme', newColorScheme);
|
setCookie(COOKIE_COLOR_SCHEME_KEY, newColorScheme);
|
||||||
if (session && new Date(session.expires) > new Date()) {
|
if (session && new Date(session.expires) > new Date()) {
|
||||||
await mutateAsync({ colorScheme: newColorScheme });
|
await mutateAsync({ colorScheme: newColorScheme });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { ConfigType } from '~/types/config';
|
|||||||
import { api } from '~/utils/api';
|
import { api } from '~/utils/api';
|
||||||
import { colorSchemeParser } from '~/validations/user';
|
import { colorSchemeParser } from '~/validations/user';
|
||||||
|
|
||||||
|
import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../data/constants';
|
||||||
import nextI18nextConfig from '../../next-i18next.config.js';
|
import nextI18nextConfig from '../../next-i18next.config.js';
|
||||||
import { ChangeAppPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal';
|
import { ChangeAppPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeAppPositionModal';
|
||||||
import { ChangeWidgetPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal';
|
import { ChangeWidgetPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeWidgetPositionModal';
|
||||||
@@ -162,6 +163,12 @@ App.getInitialProps = async ({ ctx }: { ctx: GetServerSidePropsContext }) => {
|
|||||||
|
|
||||||
const session = await getSession(ctx);
|
const session = await getSession(ctx);
|
||||||
|
|
||||||
|
// Set the cookie language to the user language if it is not set correctly
|
||||||
|
const cookieLanguage = getCookie(COOKIE_LOCALE_KEY, ctx);
|
||||||
|
if (session?.user && session.user.language != cookieLanguage) {
|
||||||
|
setCookie(COOKIE_LOCALE_KEY, session.user.language, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pageProps: {
|
pageProps: {
|
||||||
colorScheme: getActiveColorScheme(session, ctx),
|
colorScheme: getActiveColorScheme(session, ctx),
|
||||||
@@ -175,13 +182,13 @@ export default appWithTranslation<any>(api.withTRPC(App), nextI18nextConfig as a
|
|||||||
|
|
||||||
const getActiveColorScheme = (session: Session | null, ctx: GetServerSidePropsContext) => {
|
const getActiveColorScheme = (session: Session | null, ctx: GetServerSidePropsContext) => {
|
||||||
const environmentColorScheme = env.DEFAULT_COLOR_SCHEME ?? 'light';
|
const environmentColorScheme = env.DEFAULT_COLOR_SCHEME ?? 'light';
|
||||||
const cookieValue = getCookie('color-scheme', ctx);
|
const cookieColorScheme = getCookie(COOKIE_COLOR_SCHEME_KEY, ctx);
|
||||||
const activeColorScheme = colorSchemeParser.parse(
|
const activeColorScheme = colorSchemeParser.parse(
|
||||||
session?.user?.colorScheme ?? cookieValue ?? environmentColorScheme
|
session?.user?.colorScheme ?? cookieColorScheme ?? environmentColorScheme
|
||||||
);
|
);
|
||||||
|
|
||||||
if (cookieValue !== activeColorScheme) {
|
if (cookieColorScheme !== activeColorScheme) {
|
||||||
setCookie('color-scheme', activeColorScheme, ctx);
|
setCookie(COOKIE_COLOR_SCHEME_KEY, activeColorScheme, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return activeColorScheme === 'environment' ? environmentColorScheme : activeColorScheme;
|
return activeColorScheme === 'environment' ? environmentColorScheme : activeColorScheme;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { z } from 'zod';
|
|||||||
import { hashPassword } from '~/utils/security';
|
import { hashPassword } from '~/utils/security';
|
||||||
import { colorSchemeParser, signUpFormSchema } from '~/validations/user';
|
import { colorSchemeParser, signUpFormSchema } from '~/validations/user';
|
||||||
|
|
||||||
|
import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../../../data/constants';
|
||||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
|
||||||
|
|
||||||
export const userRouter = createTRPCRouter({
|
export const userRouter = createTRPCRouter({
|
||||||
@@ -52,8 +53,8 @@ export const userRouter = createTRPCRouter({
|
|||||||
salt: salt,
|
salt: salt,
|
||||||
settings: {
|
settings: {
|
||||||
create: {
|
create: {
|
||||||
colorScheme: colorSchemeParser.parse(ctx.cookies['color-scheme']),
|
colorScheme: colorSchemeParser.parse(ctx.cookies[COOKIE_COLOR_SCHEME_KEY]),
|
||||||
language: ctx.cookies['config-locale'] ?? 'en',
|
language: ctx.cookies[COOKIE_LOCALE_KEY] ?? 'en',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -89,4 +90,24 @@ export const userRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
changeLanguage: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
language: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
await ctx.prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: ctx.session?.user?.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
settings: {
|
||||||
|
update: {
|
||||||
|
language: input.language,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { getCookie } from 'cookies-next';
|
|||||||
import { IncomingMessage, ServerResponse } from 'http';
|
import { IncomingMessage, ServerResponse } from 'http';
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||||
|
|
||||||
|
import { COOKIE_LOCALE_KEY } from '../../../data/constants';
|
||||||
|
|
||||||
export const getServerSideTranslations = async (
|
export const getServerSideTranslations = async (
|
||||||
namespaces: string[],
|
namespaces: string[],
|
||||||
requestLocale?: string,
|
requestLocale?: string,
|
||||||
@@ -12,7 +14,7 @@ export const getServerSideTranslations = async (
|
|||||||
return serverSideTranslations(requestLocale ?? 'en', namespaces);
|
return serverSideTranslations(requestLocale ?? 'en', namespaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
const configLocale = getCookie('config-locale', { req, res });
|
const configLocale = getCookie(COOKIE_LOCALE_KEY, { req, res });
|
||||||
|
|
||||||
return serverSideTranslations((configLocale ?? requestLocale ?? 'en') as string, namespaces);
|
return serverSideTranslations((configLocale ?? requestLocale ?? 'en') as string, namespaces);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user