This commit is contained in:
mauro-balades
2024-08-30 13:59:34 +02:00
11 changed files with 206 additions and 27 deletions

View File

@@ -1,7 +1,7 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://www.zen-browser.app/logos/zen-alpha-white.svg">
<img src="https://www.zen-browser.app/logos/zen-alpha-black.svg" width="64px">
<img src="https://cdn.jsdelivr.net/gh/zen-browser/www/public/favicon.ico" width="64px">
</picture>
</p>
<h1 align="center">

View File

@@ -0,0 +1,18 @@
# Contributing Translations
To contribute to the translation of your language you must modify the json in `/messages` that is named corresponding to the ISO Language Code of your given language.
If you do not see a JSON for your language then add the language.
## Adding a language
1. To add a language you must add the language to the `const SUPPORTED_LANGUAGES = ['en', 'de'];` variable in the `./src/i18n.ts` file.
2. You must create a new `.json` file in the `./messages` directory
3. Copy the contents of the `en.json` file, make your way down the key-value pairs and change **only the values** to the translated equivalent.

7
messages/de.json Normal file
View File

@@ -0,0 +1,7 @@
{
"navigation": {
"getting-started": "Erste Schritte",
"donate": "Spenden",
"useful-links": "Nützliche Links"
}
}

7
messages/en.json Normal file
View File

@@ -0,0 +1,7 @@
{
"navigation": {
"getting-started": "Getting Started",
"donate": "Donate",
"useful-links": "Useful Links"
}
}

View File

@@ -1,7 +1,10 @@
const createNextIntlPlugin = require('next-intl/plugin');
const { PHASE_DEVELOPMENT_SERVER } = require('next/constants')
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */
module.exports = (phase, { defaultConfig }) => {
const nextConfig = (phase, { defaultConfig }) => {
const defaultConfigWWW = {
images: {
remotePatterns: [
@@ -39,3 +42,4 @@ module.exports = (phase, { defaultConfig }) => {
};
};
module.exports = withNextIntl(nextConfig);

97
package-lock.json generated
View File

@@ -32,6 +32,7 @@
"framer-motion": "^11.3.24",
"lucide-react": "^0.400.0",
"next": "14.2.4",
"next-intl": "^3.18.1",
"next-themes": "^0.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -2456,6 +2457,55 @@
"integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==",
"license": "MIT"
},
"node_modules/@formatjs/ecma402-abstract": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz",
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==",
"license": "MIT",
"dependencies": {
"@formatjs/intl-localematcher": "0.5.4",
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/fast-memoize": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz",
"integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.7.8",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.8.tgz",
"integrity": "sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.0.0",
"@formatjs/icu-skeleton-parser": "1.8.2",
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.2.tgz",
"integrity": "sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.0.0",
"tslib": "^2.4.0"
}
},
"node_modules/@formatjs/intl-localematcher": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
"license": "MIT",
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
@@ -8666,6 +8716,18 @@
"node": ">= 0.4"
}
},
"node_modules/intl-messageformat": {
"version": "10.5.14",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz",
"integrity": "sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==",
"license": "BSD-3-Clause",
"dependencies": {
"@formatjs/ecma402-abstract": "2.0.0",
"@formatjs/fast-memoize": "2.2.0",
"@formatjs/icu-messageformat-parser": "2.7.8",
"tslib": "^2.4.0"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -11390,7 +11452,6 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.6"
}
@@ -11452,6 +11513,27 @@
}
}
},
"node_modules/next-intl": {
"version": "3.18.1",
"resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.18.1.tgz",
"integrity": "sha512-ht8HyroJeiJIte9yhg1f0Nc2rlZmkvSYQ3nhqFVJLzhq7T1Xb8nfjilffrOJc3sA8kEjBOS4bdIrg4YX8REO0Q==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/amannn"
}
],
"license": "MIT",
"dependencies": {
"@formatjs/intl-localematcher": "^0.5.4",
"negotiator": "^0.6.3",
"use-intl": "^3.18.1"
},
"peerDependencies": {
"next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/next-themes": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz",
@@ -15130,6 +15212,19 @@
}
}
},
"node_modules/use-intl": {
"version": "3.18.1",
"resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.18.1.tgz",
"integrity": "sha512-BFNhVnszG1AB04DbNvJ+TLLd1oBDGergAKI8t9xaE4vDJYZaVKQH4zmpdArbegzTu5U9XMen6w14d1P1hBwKOQ==",
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "^2.2.0",
"intl-messageformat": "^10.5.14"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/use-sidecar": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",

View File

@@ -36,6 +36,7 @@
"framer-motion": "^11.3.24",
"lucide-react": "^0.400.0",
"next": "14.2.4",
"next-intl": "^3.18.1",
"next-themes": "^0.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",

View File

@@ -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,18 +14,23 @@ 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}>
<NextIntlClientProvider messages={messages}>
<ThemeProvider
attribute="class"
defaultTheme="dark"
@@ -32,6 +39,7 @@ export default function RootLayout({
>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</ThemeProvider>
</NextIntlClientProvider>
</body>
</html>
);

View File

@@ -54,7 +54,7 @@ import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './
function Checkmark() {
return (
<CheckIcon className="text-black rounded-full bg-green-500 dark:bg-green-400 p-1 w-7 h-7" />
<CheckIcon className="text-black rounded-full bg-green-500 dark:bg-green-400 p-1 w-7 h-7 flex-none" />
);
}
@@ -75,7 +75,7 @@ export default function Features() {
return (
<section className="flex-col w-full" id="features">
<div className='w-full md:w-5/6 lg:w-3/4 flex flex-col lg:flex-row md:rounded-md mx-auto bg-surface mt-16 shadow'>
<div className="md:w-1/2 p-5 lg:p-12">
<div className="p-5 lg:p-12 flex-1">
<div className="flex p-12 flex-col justify-center">
<h3 className='text-4xl font-medium text-gray-800 dark:text-gray-100'>Your Browser, your way <PaintBucket className='inline w-10 h-10'></PaintBucket></h3>
<p className='text-lg mt-4 text-gray-600 dark:text-gray-300'>With Zen's Theme Store, you can customize your browsing experience to reflect your unique style and preferences. Choose from a wide array of themes, colors, and layouts to make Zen truly your own, transforming your browser into a personalized digital space.</p>
@@ -86,7 +86,7 @@ export default function Features() {
</div>
</div>
<div className="border-t lg:border-t-0 lg:border-l h-[1px] lg:h-[unset] lg:w-[1px] mx-2"></div>
<div className="md:w-1/2 p-5 lg:p-12">
<div className="p-5 lg:p-12 flex-1">
<div className="flex p-12 flex-col justify-center">
<h3 className='text-4xl font-medium text-gray-800 dark:text-gray-100'>Community driven and Open Source <Link1Icon className='inline w-10 h-10'></Link1Icon></h3>
<p className='text-lg mt-4 text-gray-600 dark:text-gray-300'>Zen thrives on the contributions of its vibrant community. As an open-source project, Zen encourages collaboration and innovation, allowing users and developers alike to shape the future of the browser.</p>
@@ -104,11 +104,11 @@ export default function Features() {
</div>
<div className='flex items-center mt-5'>
<Checkmark />
<p className='ml-2 text-gray-600 dark:text-gray-300'>Automated Releases, to prove security</p>
<p className='ml-2 text-gray-600 dark:text-gray-300'>Automated Releases to ensure security</p>
</div>
<div className='flex items-center mt-5'>
<Checkmark />
<p className='ml-2 text-gray-600 dark:text-gray-300'>Comunity driven</p>
<p className='ml-2 text-gray-600 dark:text-gray-300'>Community driven</p>
</div>
<div className='flex items-center mt-5'>
<Checkmark />
@@ -139,7 +139,7 @@ export default function Features() {
</div>
<img src="https://cdn.jsdelivr.net/gh/zen-browser/www/public/browser-1.png" alt="Zen Browser" className="rounded-md lg:w-1/2" />
</div>
<div className='w-full md:w-5/6 lg:w-3/4 flex flex-col lg:flex-row md:rounded-md lg:mx-auto bg-surface mt-36 shadow'>
<div className='w-full md:w-5/6 lg:w-3/4 flex flex-col lg:flex-row md:rounded-md mx-auto bg-surface mt-36 shadow'>
<img src="https://cdn.jsdelivr.net/gh/zen-browser/www/public/browser-2.png" alt="Zen Browser" className="rounded-md lg:w-1/2" />
<div className='p-16 lg:w-1/2 flex flex-col justify-center'>
<h1 className='text-4xl font-medium text-gray-800 dark:text-gray-100'>Split Views</h1>
@@ -168,18 +168,18 @@ export default function Features() {
</div>
<div className='flex items-center mt-4'>
<Question />
<p className='ml-2 text-gray-600 dark:text-gray-300'>Tab Groups (Comming Soon)</p>
<p className='ml-2 text-gray-600 dark:text-gray-300'>Tab Groups (Coming Soon)</p>
</div>
</div>
</div>
<div className="border-t lg:border-t-0 lg:border-l h-[1px] lg:h-[unset] lg:w-[1px] mx-2"></div>
<div className="flex p-16 lg:w-1/2 flex-col">
<h3 className='text-4xl font-medium text-gray-800 dark:text-gray-100'>Security And Privacy is <span className='text-purple-500 font-bold'>important</span> to us</h3>
<h3 className='text-4xl font-medium text-gray-800 dark:text-gray-100'>Security and Privacy is <span className='text-purple-500 font-bold'>important</span> to us</h3>
<p className='text-lg mt-4 text-gray-600 dark:text-gray-300'>
Zen is based on Firefox, ensuring that your browsing experience prioritizes security and privacy. With advanced tracking protection and minimal data collection, Zen keeps your online activity safe and secure, giving you peace of mind as you explore the web.
</p>
<div className="relative">
<Button className='mt-8' variant="ghost" onClick={() => window.open('/download', '_blank')}>Security in zen <ExternalLinkIcon className='opacity-50 h-4 w-4 ml-4' /></Button>
<Button className='mt-8' variant="ghost" onClick={() => window.open('/download', '_blank')}>Security in Zen <ExternalLinkIcon className='opacity-50 h-4 w-4 ml-4' /></Button>
<Button className='mt-8' variant="ghost" onClick={() => window.open('/privacy-policy', '_blank')}>Your Privacy <ExternalLinkIcon className='opacity-50 h-4 w-4 ml-4' /></Button>
</div>
</div>

View File

@@ -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
View 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,
};
}
});