[GH-ISSUE #1547] Bug: Web Dashboard forgets language settings after a day #6748

Closed
opened 2026-04-25 15:39:18 -05:00 by GiteaMirror · 11 comments
Owner

Originally created by @FabianBartl on GitHub (Sep 26, 2025).
Original GitHub issue: https://github.com/fosrl/pangolin/issues/1547

When I change the language setting in the web dashboard and reload the page it remembers it. But after a day or so, the dashboard unfortunately forgets it.

It would be nice, if the set language is stored per user on the server; so if a user logs in, the last set language is automatically selected indepent of the device.

Edit: I use the latest pangolin version v1.10.3

Originally created by @FabianBartl on GitHub (Sep 26, 2025). Original GitHub issue: https://github.com/fosrl/pangolin/issues/1547 When I change the language setting in the web dashboard and reload the page it remembers it. But after a day or so, the dashboard unfortunately forgets it. It would be nice, if the set language is stored per user on the server; so if a user logs in, the last set language is automatically selected indepent of the device. Edit: I use the latest pangolin version v1.10.3
GiteaMirror added the good first issuehelp wanted labels 2026-04-25 15:39:18 -05:00
Author
Owner

@hhftechnology commented on GitHub (Sep 28, 2025):

i think bug occurs because the language setting is likely stored in a cookie that expires after a day. to fix this, i think we need to store the selected language on the server and retrieve it when the user logs in. i don't know how much work this will cause and what impact will it be on migrations, but this is the only stable method i can see.

1. update the user schema

first, add a locale field to your user schema in server/db/pg/schema.ts or server/db/sqlite/schema.ts

// server/db/pg/schema.ts or server/db/sqlite/schema.ts
export const users = pgTable('users', {
  // ... other fields
  locale: text('locale'),
});

2. create an API Endpoint to update the Language

create a new API endpoint to update the users language preference in the db.

a new file server/routers/user/updateUserLocale.ts:

// server/routers/user/updateUserLocale.ts
import { db } from '~/server/db';
import { users } from '~/server/db/pg/schema';
import { eq } from 'drizzle-orm';
import { protectedProcedure } from '~/server/trpc';
import { z } from 'zod';

export const updateUserLocale = protectedProcedure
  .input(z.object({ locale: z.string() }))
  .mutation(async ({ ctx, input }) => {
    await db
      .update(users)
      .set({ locale: input.locale })
      .where(eq(users.id, ctx.session.user.id));
  });

add a new route to your user router in server/routers/user/index.ts:

// server/routers/user/index.ts
import { router } from '~/server/trpc';
// ... other imports
import { updateUserLocale } from './updateUserLocale';

export const userRouter = router({
  // ... other routes
  updateUserLocale,
});

3. update the LocaleSwitcher component

modify the LocaleSwitcherSelect component to call the new API endpoint whenever the user selects a new language. we use trpc to make the API call.

in src/components/LocaleSwitcherSelect.tsx:

'use client';

import { useLocale } from 'next-intl';
import { useRouter } from 'next/navigation';
import { useTransition } from 'react';
import { api } from '~/lib/api';
import { locales } from '~/i18n/config';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '~/components/ui/select';

export default function LocaleSwitcherSelect() {
  const locale = useLocale();
  const router = useRouter();
  const [isPending, startTransition] = useTransition();
  const updateUserLocale = api.user.updateUserLocale.useMutation();

  const onChange = (value: string) => {
    updateUserLocale.mutate({ locale: value });
    startTransition(() => {
      router.replace(`/${value}`);
    });
  };

  return (
    <Select defaultValue={locale} onValueChange={onChange} disabled={isPending}>
      <SelectTrigger className="w-[180px]">
        <SelectValue placeholder="Select language" />
      </SelectTrigger>
      <SelectContent>
        {locales.map((cur) => (
          <SelectItem key={cur} value={cur}>
            {cur}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  );
}

4. load the Users locale on login

to load the users preferred language from the db when they log in and set it as the current locale.

we need to modify the i18n/request.ts to get the locale from the users session.

// src/i18n/request.ts
import { getRequestConfig } from 'next-intl/server';
import { cookies } from 'next/headers';
import { verifySession } from '~/lib/auth/verifySession';

export default getRequestConfig(async () => {
  const session = await verifySession();
  const locale = session?.user?.locale ?? cookies().get('NEXT_LOCALE')?.value ?? 'en-US';

  return {
    locale,
    messages: (await import(`../messages/${locale}.json`)).default,
  };
});

i think these changes will make sure that the users language preference is saved to their profile and automatically applied when they log in, regardless of the device they are using.

<!-- gh-comment-id:3342306450 --> @hhftechnology commented on GitHub (Sep 28, 2025): i think bug occurs because the language setting is likely stored in a cookie that expires after a day. to fix this, i think we need to store the selected language on the server and retrieve it when the user logs in. i don't know how much work this will cause and what impact will it be on migrations, but this is the only stable method i can see. ### 1\. update the user schema first, add a `locale` field to your user schema in `server/db/pg/schema.ts` or `server/db/sqlite/schema.ts` ```typescript // server/db/pg/schema.ts or server/db/sqlite/schema.ts export const users = pgTable('users', { // ... other fields locale: text('locale'), }); ``` ### 2\. create an API Endpoint to update the Language create a new API endpoint to update the users language preference in the db. a new file `server/routers/user/updateUserLocale.ts`: ```typescript // server/routers/user/updateUserLocale.ts import { db } from '~/server/db'; import { users } from '~/server/db/pg/schema'; import { eq } from 'drizzle-orm'; import { protectedProcedure } from '~/server/trpc'; import { z } from 'zod'; export const updateUserLocale = protectedProcedure .input(z.object({ locale: z.string() })) .mutation(async ({ ctx, input }) => { await db .update(users) .set({ locale: input.locale }) .where(eq(users.id, ctx.session.user.id)); }); ``` add a new route to your user router in `server/routers/user/index.ts`: ```typescript // server/routers/user/index.ts import { router } from '~/server/trpc'; // ... other imports import { updateUserLocale } from './updateUserLocale'; export const userRouter = router({ // ... other routes updateUserLocale, }); ``` ### 3\. update the `LocaleSwitcher` component modify the `LocaleSwitcherSelect` component to call the new API endpoint whenever the user selects a new language. we use `trpc` to make the API call. in `src/components/LocaleSwitcherSelect.tsx`: ```typescript 'use client'; import { useLocale } from 'next-intl'; import { useRouter } from 'next/navigation'; import { useTransition } from 'react'; import { api } from '~/lib/api'; import { locales } from '~/i18n/config'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '~/components/ui/select'; export default function LocaleSwitcherSelect() { const locale = useLocale(); const router = useRouter(); const [isPending, startTransition] = useTransition(); const updateUserLocale = api.user.updateUserLocale.useMutation(); const onChange = (value: string) => { updateUserLocale.mutate({ locale: value }); startTransition(() => { router.replace(`/${value}`); }); }; return ( <Select defaultValue={locale} onValueChange={onChange} disabled={isPending}> <SelectTrigger className="w-[180px]"> <SelectValue placeholder="Select language" /> </SelectTrigger> <SelectContent> {locales.map((cur) => ( <SelectItem key={cur} value={cur}> {cur} </SelectItem> ))} </SelectContent> </Select> ); } ``` ### 4\. load the Users locale on login to load the users preferred language from the db when they log in and set it as the current locale. we need to modify the `i18n/request.ts` to get the locale from the users session. ```typescript // src/i18n/request.ts import { getRequestConfig } from 'next-intl/server'; import { cookies } from 'next/headers'; import { verifySession } from '~/lib/auth/verifySession'; export default getRequestConfig(async () => { const session = await verifySession(); const locale = session?.user?.locale ?? cookies().get('NEXT_LOCALE')?.value ?? 'en-US'; return { locale, messages: (await import(`../messages/${locale}.json`)).default, }; }); ``` i think these changes will make sure that the users language preference is saved to their profile and automatically applied when they log in, regardless of the device they are using.
Author
Owner

@hhftechnology commented on GitHub (Sep 28, 2025):

@Pallavikumarimdb can you look into this if this makes sense?

<!-- gh-comment-id:3342675367 --> @hhftechnology commented on GitHub (Sep 28, 2025): @Pallavikumarimdb can you look into this if this makes sense?
Author
Owner

@Pallavikumarimdb commented on GitHub (Sep 29, 2025):

Hey @hhftechnology , Thanks for the suggestion! You’re right - saving the locale per user on the server would make it persistent across devices, unlike the current cookie/browser-heade approach. The tRPC-style query/mutation example you shared doesn’t quite fit Pengolin though, since we don’t use that pattern here.

<!-- gh-comment-id:3344676697 --> @Pallavikumarimdb commented on GitHub (Sep 29, 2025): Hey @hhftechnology , Thanks for the suggestion! You’re right - saving the locale per user on the server would make it persistent across devices, unlike the current cookie/browser-heade approach. The tRPC-style query/mutation example you shared doesn’t quite fit Pengolin though, since we don’t use that pattern here.
Author
Owner

@hhftechnology commented on GitHub (Sep 29, 2025):

Not an expert so I tagged an expert @Pallavikumarimdb . Hehehehehe you can take it from here

<!-- gh-comment-id:3344748885 --> @hhftechnology commented on GitHub (Sep 29, 2025): Not an expert so I tagged an expert @Pallavikumarimdb . Hehehehehe you can take it from here
Author
Owner

@17AnuragMishra commented on GitHub (Oct 5, 2025):

So, is anyone working on this? Or can I contribute!

<!-- gh-comment-id:3369095868 --> @17AnuragMishra commented on GitHub (Oct 5, 2025): So, is anyone working on this? Or can I contribute!
Author
Owner

@oschwartz10612 commented on GitHub (Oct 5, 2025):

@17AnuragMishra feel free to give it a shot!

<!-- gh-comment-id:3369231451 --> @oschwartz10612 commented on GitHub (Oct 5, 2025): @17AnuragMishra feel free to give it a shot!
Author
Owner

@17AnuragMishra commented on GitHub (Oct 5, 2025):

@17AnuragMishra feel free to give it a shot!

Thank you, I'll do my part!

<!-- gh-comment-id:3369242066 --> @17AnuragMishra commented on GitHub (Oct 5, 2025): > @17AnuragMishra feel free to give it a shot! Thank you, I'll do my part!
Author
Owner

@tim-van-dijkhuizen commented on GitHub (Oct 11, 2025):

+1 would be great of language selection was stored in a user's profile

<!-- gh-comment-id:3393474078 --> @tim-van-dijkhuizen commented on GitHub (Oct 11, 2025): +1 would be great of language selection was stored in a user's profile
Author
Owner

@rdev99 commented on GitHub (Oct 29, 2025):

@17AnuragMishra are you working on it, if not can I try ?

<!-- gh-comment-id:3461895708 --> @rdev99 commented on GitHub (Oct 29, 2025): @17AnuragMishra are you working on it, if not can I try ?
Author
Owner

@17AnuragMishra commented on GitHub (Oct 29, 2025):

Yeah working on it, will raise a pr on this upcoming weekend. > @17AnuragMishra are you working on it, if not can I try ?

<!-- gh-comment-id:3461901500 --> @17AnuragMishra commented on GitHub (Oct 29, 2025): Yeah working on it, will raise a pr on this upcoming weekend. > @17AnuragMishra are you working on it, if not can I try ?
Author
Owner

@Fizza-Mukhtar commented on GitHub (Mar 2, 2026):

Hey! @oschwartz10612 I noticed this issue has been open for a while with no PR, so I went ahead and submitted one.
The fix extends the cookie lifetime to 1 year and also persists the locale to the database via a new endpoint. Happy to make any changes if needed!

<!-- gh-comment-id:3983393230 --> @Fizza-Mukhtar commented on GitHub (Mar 2, 2026): Hey! @oschwartz10612 I noticed this issue has been open for a while with no PR, so I went ahead and submitted one. The fix extends the cookie lifetime to 1 year and also persists the locale to the database via a new endpoint. Happy to make any changes if needed!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/pangolin#6748