feat(i18n): move language selector from account menu to Settings > General

- Add LanguageForm card component in GeneralPage following the existing
  Card pattern (ProfilePictureForm), with immediate language switching
- English marked as "(Recommended)" via i18n key
- Remove Language row from AccountMenu dropdown
- Add settings.languageTitle/Description/Recommended keys to en.json
  for upload to Globalization Pipeline
This commit is contained in:
RamGopalSrikar
2026-03-26 15:54:58 -04:00
parent ae7a214998
commit e691da85f3
4 changed files with 66 additions and 8 deletions

View File

@ -23,7 +23,6 @@ import {
HeaderMenuToggle,
} from "../HeaderMenu";
import ThemeButtons from "../ThemeButtons";
import LanguageSelector from "../LanguageSelector";
export const AccountMenu = () => {
const { t } = useTranslation();
@ -172,13 +171,6 @@ export const AccountMenu = () => {
</div>
</div>
<div className="flex items-center justify-between px-4 py-[6.5px] text-sm">
<span>Language</span>
<div className="relative top-[1px] float-right">
<LanguageSelector />
</div>
</div>
{!autoLogin && (
<div>
<HeaderMenuItemButton onClick={handleLogout} icon="log-out">

View File

@ -173,6 +173,9 @@
"settings.passwordRequired": "Please enter your password",
"settings.confirmPasswordRequired": "Please confirm your password",
"settings.saveButton": "Save",
"settings.languageTitle": "Language",
"settings.languageDescription": "Choose the display language for the Langflow interface.",
"settings.languageRecommended": "Recommended",
"settings.profilePictureTitle": "Profile Picture",
"settings.profilePictureDescription": "Choose the image that appears as your profile picture.",
"settings.apiKeysTitle": "Langflow API Keys",

View File

@ -0,0 +1,60 @@
import { useQueryClient } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { useTypesStore } from "@/stores/typesStore";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "../../../../../../components/ui/card";
const LANGUAGES = [
{ code: "en", label: "English" },
{ code: "fr", label: "Français" },
{ code: "es", label: "Español" },
{ code: "de", label: "Deutsch" },
{ code: "pt", label: "Português" },
{ code: "ja", label: "日本語" },
{ code: "zh-Hans", label: "中文" },
];
const LanguageFormComponent = () => {
const { t, i18n } = useTranslation();
const queryClient = useQueryClient();
const setTypes = useTypesStore((state) => state.setTypes);
const handleChange = (code: string) => {
i18n.changeLanguage(code);
localStorage.setItem("languagePreference", code);
setTypes({});
queryClient.invalidateQueries({ queryKey: ["useGetTypes"] });
};
return (
<Card>
<CardHeader>
<CardTitle>{t("settings.languageTitle")}</CardTitle>
<CardDescription>{t("settings.languageDescription")}</CardDescription>
</CardHeader>
<CardContent>
<select
value={i18n.language}
onChange={(e) => handleChange(e.target.value)}
className="rounded border border-border bg-background px-2 py-1.5 text-sm text-foreground"
>
{LANGUAGES.map((lang) => (
<option key={lang.code} value={lang.code}>
{lang.label}
{lang.code === "en"
? ` (${t("settings.languageRecommended")})`
: ""}
</option>
))}
</select>
</CardContent>
</Card>
);
};
export default LanguageFormComponent;

View File

@ -21,6 +21,7 @@ import type {
} from "../../../../types/components";
import useScrollToElement from "../hooks/use-scroll-to-element";
import GeneralPageHeaderComponent from "./components/GeneralPageHeader";
import LanguageFormComponent from "./components/LanguageForm";
import PasswordFormComponent from "./components/PasswordForm";
import ProfilePictureFormComponent from "./components/ProfilePictureForm";
@ -138,6 +139,8 @@ export const GeneralPage = () => {
<GeneralPageHeaderComponent />
<div className="flex w-full flex-col gap-6">
<LanguageFormComponent />
{ENABLE_PROFILE_ICONS && (
<ProfilePictureFormComponent
profilePicture={profilePicture}