Skip to content

Commit aa891a4

Browse files
committed
Merge branch 'develop' into feature/riscv-specialized-hardware-section
2 parents 020c8b7 + 496131a commit aa891a4

23 files changed

+3008
-2632
lines changed

app/[locale]/Footer.tsx

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useTranslations } from "next-intl";
22
import Link from "next/link";
3-
import { JSX, SVGProps } from "react";
3+
import { JSX, SVGProps, Suspense } from "react";
4+
import LanguagePickerWrapper from "@/components/LanguagePickerWrapper";
45

56
export default function Footer() {
67
const tFooter = useTranslations("footer");
@@ -172,19 +173,21 @@ export default function Footer() {
172173
className="flex flex-wrap justify-center items-center gap-10"
173174
aria-label={tFooter("footer")}
174175
>
175-
{navigation.main.map((item) => (
176-
<div
177-
key={item.name}
178-
className="pb-6"
179-
>
180-
<Link
181-
href={item.href}
182-
className="text-sm leading-6"
176+
<div className="flex items-center gap-4">
177+
{navigation.main.map((item) => (
178+
<div
179+
key={item.name}
180+
className="pb-6"
183181
>
184-
{item.name}
185-
</Link>
186-
</div>
187-
))}
182+
<Link
183+
href={item.href}
184+
className="text-sm leading-6"
185+
>
186+
{item.name}
187+
</Link>
188+
</div>
189+
))}
190+
</div>
188191
</nav>
189192
<div className="mt-6 flex flex-wrap justify-center items-center gap-y-6 gap-x-6">
190193
{navigation.social.map((item) => (
@@ -198,6 +201,11 @@ export default function Footer() {
198201
</Link>
199202
))}
200203
</div>
204+
<div className="mt-12 flex justify-center items-center">
205+
<Suspense fallback={<div className="w-[180px] h-[40px]" />}>
206+
<LanguagePickerWrapper />
207+
</Suspense>
208+
</div>
201209
<p className="mt-10 text-center text-xs leading-5">
202210
{tFooter("disclaimer")}
203211
</p>

app/[locale]/globals.css

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
@custom-variant dark (&:is(.dark *));
55

66
@theme {
7-
--font-sans: var(--font-sans), ui-sans-serif, system-ui, sans-serif,
8-
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
9-
--font-display: var(--font-display), ui-sans-serif, system-ui, sans-serif,
7+
--font-sans:
8+
var(--font-sans), ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
9+
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
10+
--font-display:
11+
var(--font-display), ui-sans-serif, system-ui, sans-serif,
1012
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
1113

1214
--color-border: hsl(var(--border));

app/[locale]/layout.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { cn } from "@/lib/utils";
1212
import { ThemeProvider } from "@/components/ThemeProvider";
1313
import Header from "./Header";
1414
import Footer from "./Footer";
15+
import { NextIntlClientProvider } from "next-intl";
16+
import { getMessages } from "next-intl/server";
1517

1618
import type { Metadata } from "next";
1719
import type { ReactNode } from "react";
@@ -48,6 +50,8 @@ export default async function RootLayout({
4850

4951
if (!availableLanguages.includes(locale)) notFound();
5052

53+
const messages = await getMessages();
54+
5155
return (
5256
<html
5357
lang={locale}
@@ -74,15 +78,20 @@ export default async function RootLayout({
7478
fontDisplay.variable
7579
)}
7680
>
77-
<ThemeProvider
78-
attribute="class"
79-
defaultTheme="system"
80-
enableSystem
81+
<NextIntlClientProvider
82+
locale={locale}
83+
messages={messages}
8184
>
82-
<Header />
83-
<main>{children}</main>
84-
<Footer />
85-
</ThemeProvider>
85+
<ThemeProvider
86+
attribute="class"
87+
defaultTheme="system"
88+
enableSystem
89+
>
90+
<Header />
91+
<main>{children}</main>
92+
<Footer />
93+
</ThemeProvider>
94+
</NextIntlClientProvider>
8695
</body>
8796
</html>
8897
);

components/LanguagePicker.tsx

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"use client";
2+
3+
import { useLocale } from "next-intl";
4+
import { usePathname } from "next/navigation";
5+
import {
6+
Select,
7+
SelectContent,
8+
SelectItem,
9+
SelectTrigger,
10+
SelectValue,
11+
} from "@/components/ui/select";
12+
import Cookies from "js-cookie";
13+
import { Globe } from "lucide-react";
14+
15+
type LanguagePickerProps = {
16+
availableLanguages: string[];
17+
};
18+
19+
const languageNames: Record<string, string> = {
20+
"af-ZA": "Afrikaans",
21+
"ar-SA": "العربية",
22+
"ca-ES": "Català",
23+
"cs-CZ": "Čeština",
24+
"da-DK": "Dansk",
25+
"de-DE": "Deutsch",
26+
"el-GR": "Ελληνικά",
27+
en: "English",
28+
"es-ES": "Español",
29+
"fa-IR": "فارسی",
30+
"fi-FI": "Suomi",
31+
"fr-FR": "Français",
32+
"he-IL": "עברית",
33+
"hi-IN": "हिन्दी",
34+
"hu-HU": "Magyar",
35+
"id-ID": "Bahasa Indonesia",
36+
"it-IT": "Italiano",
37+
"ja-JP": "日本語",
38+
"ko-KR": "한국어",
39+
"nl-NL": "Nederlands",
40+
"no-NO": "Norsk",
41+
"pl-PL": "Polski",
42+
"pt-BR": "Português (Brasil)",
43+
"pt-PT": "Português",
44+
"ro-RO": "Română",
45+
"ru-RU": "Русский",
46+
"sr-SP": "Српски",
47+
"sv-SE": "Svenska",
48+
"ta-IN": "தமிழ்",
49+
"tr-TR": "Türkçe",
50+
"uk-UA": "Українська",
51+
"vi-VN": "Tiếng Việt",
52+
"zh-CN": "简体中文",
53+
"zh-TW": "繁體中文",
54+
};
55+
56+
export default function LanguagePicker({
57+
availableLanguages,
58+
}: LanguagePickerProps) {
59+
const locale = useLocale();
60+
const pathname = usePathname();
61+
62+
const handleLanguageChange = (newLocale: string) => {
63+
Cookies.set("NEXT_LOCALE", newLocale, { path: "/" });
64+
65+
const segments = pathname.split("/").filter(Boolean);
66+
const pathWithoutLocale =
67+
segments.length > 1 ? `/${segments.slice(1).join("/")}` : "/";
68+
69+
const newPath =
70+
newLocale === "en"
71+
? pathWithoutLocale
72+
: `/${newLocale}${pathWithoutLocale}`;
73+
74+
window.location.href = newPath;
75+
};
76+
77+
return (
78+
<Select
79+
defaultValue={locale}
80+
onValueChange={handleLanguageChange}
81+
>
82+
<SelectTrigger className="w-[180px]">
83+
<Globe className="mr-2 h-4 w-4" />
84+
<SelectValue placeholder="Select language" />
85+
</SelectTrigger>
86+
<SelectContent>
87+
{availableLanguages.map((lang) => (
88+
<SelectItem
89+
key={lang}
90+
value={lang}
91+
>
92+
{languageNames[lang] || lang}
93+
</SelectItem>
94+
))}
95+
</SelectContent>
96+
</Select>
97+
);
98+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { getAvailableLanguages } from "@/lib/i18n/getAvailableLanguages";
2+
import LanguagePicker from "./LanguagePicker";
3+
4+
export default async function LanguagePickerWrapper() {
5+
const availableLanguages = await getAvailableLanguages();
6+
7+
return <LanguagePicker availableLanguages={availableLanguages} />;
8+
}

components/ui/select.tsx

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import * as SelectPrimitive from "@radix-ui/react-select";
5+
import { cn } from "@/lib/utils";
6+
import {
7+
CheckIcon,
8+
ChevronDownIcon,
9+
ChevronUpIcon,
10+
} from "@radix-ui/react-icons";
11+
12+
const Select = SelectPrimitive.Root;
13+
14+
const SelectGroup = SelectPrimitive.Group;
15+
16+
const SelectValue = SelectPrimitive.Value;
17+
18+
const SelectTrigger = React.forwardRef<
19+
React.ElementRef<typeof SelectPrimitive.Trigger>,
20+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
21+
>(({ className, children, ...props }, ref) => (
22+
<SelectPrimitive.Trigger
23+
ref={ref}
24+
className={cn(
25+
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
26+
className
27+
)}
28+
{...props}
29+
>
30+
{children}
31+
<SelectPrimitive.Icon asChild>
32+
<ChevronDownIcon className="h-4 w-4 opacity-50" />
33+
</SelectPrimitive.Icon>
34+
</SelectPrimitive.Trigger>
35+
));
36+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
37+
38+
const SelectScrollUpButton = React.forwardRef<
39+
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
40+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
41+
>(({ className, ...props }, ref) => (
42+
<SelectPrimitive.ScrollUpButton
43+
ref={ref}
44+
className={cn(
45+
"flex cursor-default items-center justify-center py-1",
46+
className
47+
)}
48+
{...props}
49+
>
50+
<ChevronUpIcon className="h-4 w-4" />
51+
</SelectPrimitive.ScrollUpButton>
52+
));
53+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
54+
55+
const SelectScrollDownButton = React.forwardRef<
56+
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
57+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
58+
>(({ className, ...props }, ref) => (
59+
<SelectPrimitive.ScrollDownButton
60+
ref={ref}
61+
className={cn(
62+
"flex cursor-default items-center justify-center py-1",
63+
className
64+
)}
65+
{...props}
66+
>
67+
<ChevronDownIcon className="h-4 w-4" />
68+
</SelectPrimitive.ScrollDownButton>
69+
));
70+
SelectScrollDownButton.displayName =
71+
SelectPrimitive.ScrollDownButton.displayName;
72+
73+
const SelectContent = React.forwardRef<
74+
React.ElementRef<typeof SelectPrimitive.Content>,
75+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
76+
>(({ className, children, position = "popper", ...props }, ref) => (
77+
<SelectPrimitive.Portal>
78+
<SelectPrimitive.Content
79+
ref={ref}
80+
className={cn(
81+
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
82+
position === "popper" &&
83+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
84+
className
85+
)}
86+
position={position}
87+
{...props}
88+
>
89+
<SelectScrollUpButton />
90+
<SelectPrimitive.Viewport
91+
className={cn(
92+
"p-1",
93+
position === "popper" &&
94+
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
95+
)}
96+
>
97+
{children}
98+
</SelectPrimitive.Viewport>
99+
<SelectScrollDownButton />
100+
</SelectPrimitive.Content>
101+
</SelectPrimitive.Portal>
102+
));
103+
SelectContent.displayName = SelectPrimitive.Content.displayName;
104+
105+
const SelectLabel = React.forwardRef<
106+
React.ElementRef<typeof SelectPrimitive.Label>,
107+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
108+
>(({ className, ...props }, ref) => (
109+
<SelectPrimitive.Label
110+
ref={ref}
111+
className={cn("px-2 py-1.5 text-sm font-semibold", className)}
112+
{...props}
113+
/>
114+
));
115+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
116+
117+
const SelectItem = React.forwardRef<
118+
React.ElementRef<typeof SelectPrimitive.Item>,
119+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
120+
>(({ className, children, ...props }, ref) => (
121+
<SelectPrimitive.Item
122+
ref={ref}
123+
className={cn(
124+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
125+
className
126+
)}
127+
{...props}
128+
>
129+
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
130+
<SelectPrimitive.ItemIndicator>
131+
<CheckIcon className="h-4 w-4" />
132+
</SelectPrimitive.ItemIndicator>
133+
</span>
134+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
135+
</SelectPrimitive.Item>
136+
));
137+
SelectItem.displayName = SelectPrimitive.Item.displayName;
138+
139+
const SelectSeparator = React.forwardRef<
140+
React.ElementRef<typeof SelectPrimitive.Separator>,
141+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
142+
>(({ className, ...props }, ref) => (
143+
<SelectPrimitive.Separator
144+
ref={ref}
145+
className={cn("-mx-1 my-1 h-px bg-muted", className)}
146+
{...props}
147+
/>
148+
));
149+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
150+
151+
export {
152+
Select,
153+
SelectGroup,
154+
SelectValue,
155+
SelectTrigger,
156+
SelectContent,
157+
SelectLabel,
158+
SelectItem,
159+
SelectSeparator,
160+
SelectScrollUpButton,
161+
SelectScrollDownButton,
162+
};

0 commit comments

Comments
 (0)