Merge pull request #134 from ktz-dev/internationalization
feat: Multi-language support
This commit is contained in:
@@ -3,6 +3,8 @@ import { Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import StyledComponentsRegistry from "@/lib/styled-components-registry";
|
||||
import {NextIntlClientProvider} from 'next-intl';
|
||||
import {getLocale, getMessages} from 'next-intl/server';
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
@@ -12,26 +14,32 @@ export const metadata: Metadata = {
|
||||
keywords: ["Zen", "Browser", "Zen Browser", "Web", "Internet", "Fast"],
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const locale = await getLocale();
|
||||
|
||||
const messages = await getMessages();
|
||||
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<html lang={locale} suppressHydrationWarning>
|
||||
<head>
|
||||
<link rel="me" href="https://fosstodon.org/@zenbrowser"></link>
|
||||
<link rel="alternate" type="application/rss+xml" title="Zen Browser Release Notes" href="https://www.zen-browser.app/feed.xml" />
|
||||
</head>
|
||||
<body className={inter.className}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="dark"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
|
||||
</ThemeProvider>
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="dark"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
|
||||
</ThemeProvider>
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -18,6 +18,7 @@ import { ModeToggle } from "./mode-toggle"
|
||||
import { MobileNav } from "./mobile-nav"
|
||||
import { HeartIcon } from "lucide-react"
|
||||
import { HeartFilledIcon } from "@radix-ui/react-icons"
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export const components: { title: string; href: string; description: string }[] = [
|
||||
{
|
||||
@@ -48,6 +49,9 @@ export const components: { title: string; href: string; description: string }[]
|
||||
]
|
||||
|
||||
export function Navigation() {
|
||||
|
||||
const t = useTranslations('navigation');
|
||||
|
||||
return (
|
||||
<div className="bg-background z-40 top-0 left-0 w-full flex fixed border-b border-grey p-2 items-center justify-center">
|
||||
<MobileNav />
|
||||
@@ -59,7 +63,7 @@ export function Navigation() {
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>Getting started</NavigationMenuTrigger>
|
||||
<NavigationMenuTrigger>{t('getting-started')}</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
|
||||
<li className="row-span-3">
|
||||
@@ -94,7 +98,7 @@ export function Navigation() {
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>
|
||||
<HeartFilledIcon className="text-red-500" />
|
||||
<span className="ml-2">Donate</span>
|
||||
<span className="ml-2">{t('donate')}</span>
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] ">
|
||||
@@ -114,7 +118,7 @@ export function Navigation() {
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>Useful Links</NavigationMenuTrigger>
|
||||
<NavigationMenuTrigger>{t('useful-links')}</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] ">
|
||||
{components.map((component) => (
|
||||
|
||||
35
src/i18n.ts
Normal file
35
src/i18n.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { getRequestConfig } from "next-intl/server";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
const SUPPORTED_LANGUAGES = ['en', 'de'];
|
||||
|
||||
export default getRequestConfig(async () => {
|
||||
const headersList = headers();
|
||||
|
||||
const acceptLanguage = headersList.get("accept-language") || "en";
|
||||
|
||||
const [primaryLanguage] = acceptLanguage
|
||||
.split(",")
|
||||
.map((lang) => lang.split(";")[0])
|
||||
.map((lang) => lang.toLowerCase());
|
||||
|
||||
const locale = SUPPORTED_LANGUAGES.includes(primaryLanguage) ? primaryLanguage : 'en';
|
||||
|
||||
try {
|
||||
const messages = (await import(`../messages/${locale}.json`)).default;
|
||||
|
||||
return {
|
||||
locale,
|
||||
messages,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Failed to load messages for locale: ${locale}`, error);
|
||||
|
||||
const fallbackMessages = (await import(`../messages/en.json`)).default;
|
||||
|
||||
return {
|
||||
locale: "en",
|
||||
messages: fallbackMessages,
|
||||
};
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user