mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-11 20:02:34 +00:00
implement language switcher
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
const i18nConfig = {
|
||||
locales: ["en"],
|
||||
locales: ["en", "de", "it"],
|
||||
defaultLocale: "en",
|
||||
prefixDefault: false,
|
||||
};
|
||||
|
||||
4
apps/login/locales/de/loginname.json
Normal file
4
apps/login/locales/de/loginname.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Willkommen zurück!",
|
||||
"description": "Geben Sie Ihre Anmeldedaten ein."
|
||||
}
|
||||
4
apps/login/locales/it/loginname.json
Normal file
4
apps/login/locales/it/loginname.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Welcome back!",
|
||||
"description": "Enter your login data."
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
"*": "prettier --write --ignore-unknown"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.18",
|
||||
"@headlessui/react": "^2.1.9",
|
||||
"@heroicons/react": "2.1.3",
|
||||
"@tailwindcss/forms": "0.5.7",
|
||||
"@vercel/analytics": "^1.2.2",
|
||||
|
||||
@@ -2,6 +2,7 @@ import "@/styles/globals.scss";
|
||||
|
||||
import { AddressBar } from "@/components/address-bar";
|
||||
import { GlobalNav } from "@/components/global-nav";
|
||||
import { LanguageSwitcher } from "@/components/language-switcher";
|
||||
import { Theme } from "@/components/theme";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import { TranslationsProvider } from "@/components/translations-provider";
|
||||
@@ -65,7 +66,8 @@ export default async function RootLayout({
|
||||
{showNav ? (
|
||||
<GlobalNav />
|
||||
) : (
|
||||
<div className="absolute bottom-0 right-0 flex flex-row p-4">
|
||||
<div className="absolute bottom-0 right-0 flex flex-row p-4 items-center space-x-4">
|
||||
<LanguageSwitcher locale={locale} />
|
||||
<Theme />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { Listbox, Transition } from "@headlessui/react";
|
||||
import {
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
ListboxOption,
|
||||
ListboxOptions,
|
||||
Transition,
|
||||
} from "@headlessui/react";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/24/outline";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { Fragment, useState } from "react";
|
||||
@@ -39,7 +45,7 @@ type Props = {
|
||||
locale: string;
|
||||
};
|
||||
|
||||
export default function LanguageSwitcher({ locale }: Props) {
|
||||
export function LanguageSwitcher({ locale }: Props) {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const currentLocale = locale || i18n.language || i18nConfig.defaultLocale;
|
||||
@@ -77,10 +83,10 @@ export default function LanguageSwitcher({ locale }: Props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-4 w-32">
|
||||
<div className="w-32">
|
||||
<Listbox value={selected} onChange={handleChange}>
|
||||
<div className="relative mt-1">
|
||||
<Listbox.Button className="relative w-full cursor-default rounded-lg border border-divider-light bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm">
|
||||
<div className="relative">
|
||||
<ListboxButton className="relative w-full cursor-default rounded-lg border border-divider-light bg-background-light-500 dark:bg-background-dark-500 py-2 pl-3 pr-10 text-left focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm">
|
||||
<span className="block truncate">{selected.name}</span>
|
||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<ChevronUpDownIcon
|
||||
@@ -88,20 +94,25 @@ export default function LanguageSwitcher({ locale }: Props) {
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</Listbox.Button>
|
||||
</ListboxButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black/5 focus:outline-none sm:text-sm">
|
||||
<ListboxOptions
|
||||
anchor="bottom"
|
||||
className="absolute mt-1 max-h-60 w-[var(--button-width)] w-full overflow-auto rounded-md text-text-light-500 dark:text-text-dark-500 bg-background-light-500 dark:bg-background-dark-500 py-1 text-base shadow-lg ring-1 ring-black/5 focus:outline-none sm:text-sm"
|
||||
>
|
||||
{LANGS.map((lang, index) => (
|
||||
<Listbox.Option
|
||||
<ListboxOption
|
||||
key={lang.code}
|
||||
className={({ active }) =>
|
||||
`relative cursor-default select-none py-2 pl-10 pr-4 ${
|
||||
active ? "bg-amber-100 text-amber-900" : "text-gray-900"
|
||||
active
|
||||
? "bg-background-light-300 dark:bg-background-dark-300"
|
||||
: ""
|
||||
}`
|
||||
}
|
||||
value={lang}
|
||||
@@ -116,15 +127,15 @@ export default function LanguageSwitcher({ locale }: Props) {
|
||||
{lang.name}
|
||||
</span>
|
||||
{selected ? (
|
||||
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600">
|
||||
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-primary-light-500 dark:text-primary-dark-500">
|
||||
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
</ListboxOption>
|
||||
))}
|
||||
</Listbox.Options>
|
||||
</ListboxOptions>
|
||||
</Transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
|
||||
@@ -21,7 +21,7 @@ export function Theme() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative grid grid-cols-2 rounded-full border border-divider-light dark:border-divider-dark p-1`}
|
||||
className={`h-fit relative grid grid-cols-2 rounded-full border border-divider-light dark:border-divider-dark p-1`}
|
||||
>
|
||||
<button
|
||||
className={`h-8 w-8 rounded-full flex flex-row items-center justify-center hover:opacity-100 transition-all ${
|
||||
|
||||
146
pnpm-lock.yaml
generated
146
pnpm-lock.yaml
generated
@@ -51,8 +51,8 @@ importers:
|
||||
apps/login:
|
||||
dependencies:
|
||||
'@headlessui/react':
|
||||
specifier: ^1.7.18
|
||||
version: 1.7.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
specifier: ^2.1.9
|
||||
version: 2.1.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@heroicons/react':
|
||||
specifier: 2.1.3
|
||||
version: 2.1.3(react@18.3.1)
|
||||
@@ -851,6 +851,27 @@ packages:
|
||||
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@floating-ui/core@1.6.8':
|
||||
resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==}
|
||||
|
||||
'@floating-ui/dom@1.6.11':
|
||||
resolution: {integrity: sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==}
|
||||
|
||||
'@floating-ui/react-dom@2.1.2':
|
||||
resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
|
||||
'@floating-ui/react@0.26.24':
|
||||
resolution: {integrity: sha512-2ly0pCkZIGEQUq5H8bBK0XJmc1xIK/RM3tvVzY3GBER7IOD1UgmC2Y2tjj4AuS+TC+vTE1KJv2053290jua0Sw==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
|
||||
'@floating-ui/utils@0.2.8':
|
||||
resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==}
|
||||
|
||||
'@formatjs/intl-localematcher@0.5.4':
|
||||
resolution: {integrity: sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==}
|
||||
|
||||
@@ -869,12 +890,12 @@ packages:
|
||||
'@hapi/topo@5.1.0':
|
||||
resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
|
||||
|
||||
'@headlessui/react@1.7.19':
|
||||
resolution: {integrity: sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==}
|
||||
'@headlessui/react@2.1.9':
|
||||
resolution: {integrity: sha512-ckWw7vlKtnoa1fL2X0fx1a3t/Li9MIKDVXn3SgG65YlxvDAsNrY39PPCxVM7sQRA7go2fJsuHSSauKFNaJHH7A==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
react: ^16 || ^17 || ^18
|
||||
react-dom: ^16 || ^17 || ^18
|
||||
react: ^18
|
||||
react-dom: ^18
|
||||
|
||||
'@heroicons/react@2.1.3':
|
||||
resolution: {integrity: sha512-fEcPfo4oN345SoqdlCDdSa4ivjaKbk0jTd+oubcgNxnNgAfzysfwWfQUr+51wigiWHQQRiZNd1Ao0M5Y3M2EGg==}
|
||||
@@ -1039,6 +1060,37 @@ packages:
|
||||
'@protobufjs/utf8@1.1.0':
|
||||
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
||||
|
||||
'@react-aria/focus@3.18.3':
|
||||
resolution: {integrity: sha512-WKUElg+5zS0D3xlVn8MntNnkzJql2J6MuzAMP8Sv5WTgFDse/XGR842dsxPTIyKKdrWVCRegCuwa4m3n/GzgJw==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@react-aria/interactions@3.22.3':
|
||||
resolution: {integrity: sha512-RRUb/aG+P0IKTIWikY/SylB6bIbLZeztnZY2vbe7RAG5MgVaCgn5HQ45SI15GlTmhsFG8CnF6slJsUFJiNHpbQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@react-aria/ssr@3.9.6':
|
||||
resolution: {integrity: sha512-iLo82l82ilMiVGy342SELjshuWottlb5+VefO3jOQqQRNYnJBFpUSadswDPbRimSgJUZuFwIEYs6AabkP038fA==}
|
||||
engines: {node: '>= 12'}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@react-aria/utils@3.25.3':
|
||||
resolution: {integrity: sha512-PR5H/2vaD8fSq0H/UB9inNbc8KDcVmW6fYAfSWkkn+OAdhTTMVKqXXrZuZBWyFfSD5Ze7VN6acr4hrOQm2bmrA==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@react-stately/utils@3.10.4':
|
||||
resolution: {integrity: sha512-gBEQEIMRh5f60KCm7QKQ2WfvhB2gLUr9b72sqUdIZ2EG+xuPgaIlCBeSicvjmjBvYZwOjoOEnmIkcx2GHp/HWw==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@react-types/shared@3.25.0':
|
||||
resolution: {integrity: sha512-OZSyhzU6vTdW3eV/mz5i6hQwQUhkRs7xwY2d1aqPvTdMe0+2cY7Fwp45PAiwYLEj73i9ro2FxF9qC4DvHGSCgQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.21.3':
|
||||
resolution: {integrity: sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==}
|
||||
cpu: [arm]
|
||||
@@ -1706,6 +1758,10 @@ packages:
|
||||
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
clsx@2.1.1:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
color-convert@1.9.3:
|
||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||
|
||||
@@ -4054,6 +4110,9 @@ packages:
|
||||
symbol-tree@3.2.4:
|
||||
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
||||
|
||||
tabbable@6.2.0:
|
||||
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
|
||||
|
||||
tailwindcss@3.4.12:
|
||||
resolution: {integrity: sha512-Htf/gHj2+soPb9UayUNci/Ja3d8pTmu9ONTfh4QY8r3MATTZOzmv6UYWF7ZwikEIC8okpfqmGqrmDehua8mF8w==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@@ -5119,6 +5178,31 @@ snapshots:
|
||||
|
||||
'@fastify/busboy@2.1.1': {}
|
||||
|
||||
'@floating-ui/core@1.6.8':
|
||||
dependencies:
|
||||
'@floating-ui/utils': 0.2.8
|
||||
|
||||
'@floating-ui/dom@1.6.11':
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.6.8
|
||||
'@floating-ui/utils': 0.2.8
|
||||
|
||||
'@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.6.11
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
'@floating-ui/react@0.26.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@floating-ui/utils': 0.2.8
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
tabbable: 6.2.0
|
||||
|
||||
'@floating-ui/utils@0.2.8': {}
|
||||
|
||||
'@formatjs/intl-localematcher@0.5.4':
|
||||
dependencies:
|
||||
tslib: 2.7.0
|
||||
@@ -5141,10 +5225,12 @@ snapshots:
|
||||
dependencies:
|
||||
'@hapi/hoek': 9.3.0
|
||||
|
||||
'@headlessui/react@1.7.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
'@headlessui/react@2.1.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@floating-ui/react': 0.26.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@react-aria/focus': 3.18.3(react@18.3.1)
|
||||
'@react-aria/interactions': 3.22.3(react@18.3.1)
|
||||
'@tanstack/react-virtual': 3.10.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
client-only: 0.0.1
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
@@ -5296,6 +5382,46 @@ snapshots:
|
||||
|
||||
'@protobufjs/utf8@1.1.0': {}
|
||||
|
||||
'@react-aria/focus@3.18.3(react@18.3.1)':
|
||||
dependencies:
|
||||
'@react-aria/interactions': 3.22.3(react@18.3.1)
|
||||
'@react-aria/utils': 3.25.3(react@18.3.1)
|
||||
'@react-types/shared': 3.25.0(react@18.3.1)
|
||||
'@swc/helpers': 0.5.5
|
||||
clsx: 2.1.1
|
||||
react: 18.3.1
|
||||
|
||||
'@react-aria/interactions@3.22.3(react@18.3.1)':
|
||||
dependencies:
|
||||
'@react-aria/ssr': 3.9.6(react@18.3.1)
|
||||
'@react-aria/utils': 3.25.3(react@18.3.1)
|
||||
'@react-types/shared': 3.25.0(react@18.3.1)
|
||||
'@swc/helpers': 0.5.5
|
||||
react: 18.3.1
|
||||
|
||||
'@react-aria/ssr@3.9.6(react@18.3.1)':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
react: 18.3.1
|
||||
|
||||
'@react-aria/utils@3.25.3(react@18.3.1)':
|
||||
dependencies:
|
||||
'@react-aria/ssr': 3.9.6(react@18.3.1)
|
||||
'@react-stately/utils': 3.10.4(react@18.3.1)
|
||||
'@react-types/shared': 3.25.0(react@18.3.1)
|
||||
'@swc/helpers': 0.5.5
|
||||
clsx: 2.1.1
|
||||
react: 18.3.1
|
||||
|
||||
'@react-stately/utils@3.10.4(react@18.3.1)':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.5
|
||||
react: 18.3.1
|
||||
|
||||
'@react-types/shared@3.25.0(react@18.3.1)':
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.21.3':
|
||||
optional: true
|
||||
|
||||
@@ -5972,6 +6098,8 @@ snapshots:
|
||||
|
||||
clsx@1.2.1: {}
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
color-convert@1.9.3:
|
||||
dependencies:
|
||||
color-name: 1.1.3
|
||||
@@ -8542,6 +8670,8 @@ snapshots:
|
||||
|
||||
symbol-tree@3.2.4: {}
|
||||
|
||||
tabbable@6.2.0: {}
|
||||
|
||||
tailwindcss@3.4.12:
|
||||
dependencies:
|
||||
'@alloc/quick-lru': 5.2.0
|
||||
|
||||
Reference in New Issue
Block a user