Localisation (#39)

* Localisation

* Update tests

* Fix map markers in English view

* Fix typo
This commit is contained in:
Timothy Armes
2023-07-03 20:14:19 +02:00
committed by GitHub
parent 279439f34d
commit c5a1503684
41 changed files with 881 additions and 426 deletions
+28
View File
@@ -0,0 +1,28 @@
import { useTranslations } from "next-intl";
import Layout from "@/components/Layout";
import ContactForm from "@/components/ContactForm";
// TODO fix page sizing
export default function Contact() {
const t = useTranslations("Contact");
return (
<Layout>
<section className="grid place-items-center bg-white dark:bg-gray-800">
<div className="mx-auto max-w-screen-md px-4 pb-32 pt-8 lg:pt-16">
<h2
className="mb-4 text-center text-4xl font-extrabold tracking-tight text-gray-900 dark:text-white"
data-test="header2"
>
{t("title")}
</h2>
<p className="mb-8 text-center font-light text-gray-500 dark:text-gray-400 sm:text-xl lg:mb-16">
{t("info")}
</p>
<ContactForm />
</div>
</section>
</Layout>
);
}
+59
View File
@@ -0,0 +1,59 @@
import { Analytics } from "@vercel/analytics/react";
import { Inter } from "next/font/google";
import { useLocale, NextIntlClientProvider } from "next-intl";
import { getTranslator } from "next-intl/server";
import { notFound } from "next/navigation";
import { ReactNode } from "react";
import "@/app/globals.css";
const inter = Inter({ subsets: ["latin"] });
export async function generateMetadata({
params: { locale },
}: {
params: { locale?: string };
}) {
// While the `locale` is required, the namespace is optional and
// identical to the parameter that `useTranslations` accepts.
const t = await getTranslator(locale ?? "fr", "Metadata");
return {
title: t("title"),
description: t("description"),
keywords: t("keywords"),
};
}
export default async function RootLayout({
children,
params,
}: {
children: ReactNode;
params: { locale?: string };
}) {
const locale = useLocale();
// Show a 404 error if the user requests an unknown locale
if (params.locale !== undefined && params.locale !== locale) {
notFound();
}
let messages;
try {
messages = (await import(`@/messages/${locale}.json`)).default;
} catch (error) {
notFound();
}
return (
<html lang={locale}>
<body className={inter.className}>
<NextIntlClientProvider locale={locale} messages={messages}>
{children}
</NextIntlClientProvider>
<Analytics />
</body>
</html>
);
}
+46
View File
@@ -0,0 +1,46 @@
import Link from "next/link";
import Image from "next/image";
import { useTranslations } from "next-intl";
import Layout from "@/components/Layout";
import bgImage from "@/public/images/map-bg.jpg";
export default function Home() {
const t = useTranslations("Home");
return (
<Layout>
<header className="grid h-[calc(100%-153px)] place-items-center md:h-[calc(100%-173px)]">
<div className="relative h-full w-full brightness-[0.2]">
<Image
src={bgImage}
alt="Background image of France"
fill={true}
style={{ objectFit: "cover" }}
/>
</div>
<div className="absolute text-center text-white">
<h1 className="p-5 text-5xl" data-test="header1">
{t("title")}
</h1>
<h2 className="p-5 text-3xl">{t("purpose")}</h2>
<h3 className="mb-5 p-5 text-xl">
{t.rich("how", {
link: (chunks) => (
<Link href="http://www.echecs.asso.fr/" target="_blank">
<abbr title="Fédération Française des Échecs">{chunks}</abbr>
</Link>
),
})}
</h3>
<Link
href="/tournois"
className="mb-2 mr-2 rounded-lg bg-gradient-to-r from-teal-400 via-teal-500 to-teal-600 px-5 py-2.5 text-center text-sm font-medium text-white hover:bg-gradient-to-br focus:outline-none focus:ring-4 focus:ring-teal-300 dark:focus:ring-teal-800"
>
{t("mapLink")}
</Link>
</div>
</header>
</Layout>
);
}
+124
View File
@@ -0,0 +1,124 @@
import Link from "next-intl/link";
import { useTranslations } from "next-intl";
import Layout from "@/components/Layout";
export default function About() {
const t = useTranslations("About");
return (
<Layout>
<header className="grid place-items-center bg-white dark:bg-gray-800">
<div className="h-full max-w-5xl bg-white px-4 pb-12 pt-8 dark:bg-gray-800">
<h2
className="mb-8 text-center text-4xl font-extrabold tracking-tight text-gray-900 dark:text-white"
data-test="header2"
>
{t("title")}
</h2>
<p className="mb-4 text-center font-light text-gray-500 dark:text-gray-400 md:text-xl">
{t("p1")}
</p>
<p className="mb-4 text-center font-light text-gray-500 dark:text-gray-400 md:text-xl">
{t.rich("p2", {
homeLink: (chunks) => (
<Link href="/" className="text-teal-600">
{chunks}
</Link>
),
})}
</p>
<p className="mb-4 text-center font-light text-gray-500 dark:text-gray-400 md:text-xl">
{t("p3")}
</p>
<h3
className="mb-4 mt-8 text-center text-2xl font-extrabold tracking-tight text-gray-900 dark:text-white"
data-test="header2"
>
{t("thanksTitle")}
</h3>
<p className="mb-4 text-center font-light text-gray-500 dark:text-gray-400 md:text-xl">
{t("thanksTitle")}
</p>
<ul className="mb-4 text-center text-teal-600">
<li>
<Link href="https://github.com/AlvaroNW" target="_blank">
AlvaroNW
</Link>
</li>
<li>
<Link href="https://github.com/Florifourchette" target="_blank">
Florifourchette
</Link>
</li>
</ul>
<h3
className="mb-4 mt-8 text-center text-2xl font-extrabold tracking-tight text-gray-900 dark:text-white"
data-test="header2"
>
{t("techTitle")}
</h3>
<p className="mb-4 text-center font-light text-gray-500 dark:text-gray-400 md:text-xl">
{t.rich("techInfo", {
homeLink: (chunks) => (
<Link href="/" className="text-teal-600">
{chunks}
</Link>
),
})}
</p>
<ul className="mb-4 flex justify-around text-teal-600">
<li>
<Link href="https://nextjs.org/" target="_blank">
NextJS
</Link>
</li>
<li>
<Link href="#" target="_blank">
Typescript
</Link>
</li>
<li>
<Link href="https://tailwindcss.com/" target="_blank">
Tailwind
</Link>
</li>
<li>
<Link href="https://mongodb.com/" target="_blank">
MongoDB
</Link>
</li>
</ul>
<p className="mb-4 text-center font-light text-gray-500 dark:text-gray-400 md:text-xl">
{t("techWith")}
</p>
<ul className="mb-4 flex justify-around text-teal-600">
<li>
<Link href="https://leafletjs.com/" target="_blank">
Leaflet
</Link>
</li>
<li>
<Link href="https://nodemailer.com/" target="_blank">
Nodemailer
</Link>
</li>
</ul>
<p className="mb-16 text-center font-light text-gray-500 dark:text-gray-400 md:text-xl">
{t.rich("contribInfo", {
link: (chunks) => (
<Link
href="https://github.com/TheRealOwenRees/echecsfrance"
target="_blank"
className="text-teal-600"
>
{chunks}
</Link>
),
})}
</p>
</div>
</header>
</Layout>
);
}
@@ -1,11 +1,22 @@
import clientPromise from "@/lib/mongodb";
import dynamic from "next/dynamic";
import { useTranslations } from "next-intl";
import Layout from "@/components/Layout";
import TournamentTable from "@/components/TournamentTable";
import { dateOrderingFrance } from "@/utils/dbDateOrdering";
import { errorLog } from "@/utils/logger";
export const revalidate = 3600; // revalidate cache every 6 hours
export const revalidate = 3600; // revalidate cache every 6 hours;
const LoadingMap = () => {
const t = useTranslations("Competitions");
return (
<div className="grid h-screen place-items-center bg-white text-gray-900 dark:bg-gray-800 dark:text-white">
<p>{t("loading")}</p>
</div>
);
};
/**
* Imports the tournament map component, ensuring CSR only.
@@ -13,11 +24,7 @@ export const revalidate = 3600; // revalidate cache every 6 hours
*/
const TournamentMap = dynamic(() => import("@/components/TournamentMap"), {
ssr: false,
loading: () => (
<div className="h-screen grid place-items-center text-gray-900 bg-white dark:bg-gray-800 dark:text-white">
<p>Loading map...</p>
</div>
),
loading: LoadingMap,
});
const getTournaments = async () => {
-26
View File
@@ -1,26 +0,0 @@
import Layout from "@/components/Layout";
import ContactForm from "@/components/ContactForm";
// TODO fix page sizing
export default function Contact() {
return (
<Layout>
<section className="grid place-items-center bg-white dark:bg-gray-800">
<div className="pt-8 pb-32 lg:pt-16 px-4 mx-auto max-w-screen-md">
<h2
className="mb-4 text-4xl tracking-tight font-extrabold text-center text-gray-900 dark:text-white"
data-test="header2"
>
Contactez-Nous
</h2>
<p className="mb-8 lg:mb-16 font-light text-center text-gray-500 dark:text-gray-400 sm:text-xl">
Vous souhaitez ajouter les tournois de votre fédération sur ce site?
Vous avez un problème technique? Vous aimeriez participer à ce
projet? Contactez-nous.
</p>
<ContactForm />
</div>
</section>
</Layout>
);
}
-24
View File
@@ -1,24 +0,0 @@
import { Analytics } from "@vercel/analytics/react";
import "./globals.css";
import { Inter } from "next/font/google";
import { ReactNode } from "react";
const inter = Inter({ subsets: ["latin"] });
export const metadata = {
title: "Echecs France- Tournois d'Echecs",
description: "Trouvez Vos Tournois d'Echecs en France Sur Une Carte",
keywords: "echecs, France, tournoi, tournois, FFE, carte",
};
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body className={inter.className}>
{children}
<Analytics />
</body>
</html>
);
}
-42
View File
@@ -1,42 +0,0 @@
import Link from "next/link";
import Image from "next/image";
import Layout from "@/components/Layout";
import bgImage from "@/public/images/map-bg.jpg";
export default function Home() {
return (
<Layout>
<header className="grid h-[calc(100%-153px)] md:h-[calc(100%-173px)] place-items-center">
<div className="relative h-full w-full brightness-[0.2]">
<Image
src={bgImage}
alt="Background image of France"
fill={true}
style={{ objectFit: "cover" }}
/>
</div>
<div className="absolute text-center text-white">
<h1 className="text-5xl p-5" data-test="header1">
Echecs France
</h1>
<h2 className="text-3xl p-5">
Trouvez Vos Tournois d&apos;Echecs en France Sur Une Carte
</h2>
<h3 className="text-xl p-5 mb-5">
Une représentation visuelle de tous les tournois d&apos;échecs
publiés par la{" "}
<Link href="http://www.echecs.asso.fr/" target="_blank">
<abbr title="Fédération Française des Échecs">FFE</abbr>
</Link>
</h3>
<Link
href="/tournois"
className="text-white bg-gradient-to-r from-teal-400 via-teal-500 to-teal-600 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-teal-300 dark:focus:ring-teal-800 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2"
>
Voir La Carte
</Link>
</div>
</header>
</Layout>
);
}
-124
View File
@@ -1,124 +0,0 @@
import Link from "next/link";
import Layout from "@/components/Layout";
export default function About() {
return (
<Layout>
<header className="grid place-items-center bg-white dark:bg-gray-800">
<div className="h-full max-w-5xl px-4 pt-8 pb-12 bg-white dark:bg-gray-800">
<h2
className="mb-8 text-4xl tracking-tight font-extrabold text-center text-gray-900 dark:text-white"
data-test="header2"
>
Qui Sommes-Nous?
</h2>
<p className="mb-4 font-light text-center text-gray-500 dark:text-gray-400 md:text-xl">
Ce projet a vu le jour début 2022 afin de permettre une
visualisation sur une carte des tournois d&apos;échecs en France.
Ayant déménagé en France en 2019, je ne connaissais alors pas la
géographie française et je souhaitais savoir quels tournois avaient
lieu près de chez moi.
</p>
<p className="mb-4 font-light text-center text-gray-500 dark:text-gray-400 md:text-xl">
Le projet a é mis de côté jusqu&apos;au printemps 2023; date à
laquelle je lui ai redonné vie. Reconstruit à partir de zéro,{" "}
<Link href="/" className="text-teal-600">
Echecs France
</Link>{" "}
est désormais open source et ouvert aux contributions.
</p>
<p className="mb-4 font-light text-center text-gray-500 dark:text-gray-400 md:text-xl">
Je compte mettre en place un bouton de don en ligne pour ceux qui
souhaitent participer aux frais associés au site. Une fois les coûts
de fonctionnement déduits, tous les fonds restant seront redirigés
vers le monde des échecs soit en sponsorant des événements ou par la
création de dons.
</p>
<h3
className="mt-8 mb-4 text-2xl tracking-tight font-extrabold text-center text-gray-900 dark:text-white"
data-test="header2"
>
Remerciements
</h3>
<p className="mb-4 font-light text-center text-gray-500 dark:text-gray-400 md:text-xl">
Ce projet est ce qu&apos;il est grâce aux contributions de vous
tous. Je souhaite en particulier remercier les personnes suivantes
pour leurs contributions:
</p>
<ul className="mb-4 text-center text-teal-600">
<li>
<Link href="https://github.com/AlvaroNW" target="_blank">
AlvaroNW
</Link>
</li>
<li>
<Link href="https://github.com/Florifourchette" target="_blank">
Florifourchette
</Link>
</li>
</ul>
<h3
className="mt-8 mb-4 text-2xl tracking-tight font-extrabold text-center text-gray-900 dark:text-white"
data-test="header2"
>
Tech Blurb
</h3>
<p className="mb-4 font-light text-center text-gray-500 dark:text-gray-400 md:text-xl">
<Link href="/" className="text-teal-600">
Echecs France
</Link>{" "}
utilise:
</p>
<ul className="mb-4 flex justify-around text-teal-600">
<li>
<Link href="https://nextjs.org/" target="_blank">
NextJS
</Link>
</li>
<li>
<Link href="#" target="_blank">
Typescript
</Link>
</li>
<li>
<Link href="https://tailwindcss.com/" target="_blank">
Tailwind
</Link>
</li>
<li>
<Link href="https://mongodb.com/" target="_blank">
MongoDB
</Link>
</li>
</ul>
<p className="mb-4 font-light text-center text-gray-500 dark:text-gray-400 md:text-xl">
avec
</p>
<ul className="mb-4 flex justify-around text-teal-600">
<li>
<Link href="https://leafletjs.com/" target="_blank">
Leaflet
</Link>
</li>
<li>
<Link href="https://nodemailer.com/" target="_blank">
Nodemailer
</Link>
</li>
</ul>
<p className="mb-16 font-light text-center text-gray-500 dark:text-gray-400 md:text-xl">
Pour plus d&apos;informations sur les moyens de contribution,
veuillez visiter notre{" "}
<Link
href="https://github.com/TheRealOwenRees/echecsfrance"
target="_blank"
className="text-teal-600"
>
GitHub.
</Link>
</p>
</div>
</header>
</Layout>
);
}
+8 -6
View File
@@ -1,22 +1,24 @@
import { MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
return [
return ['fr', 'en'].flatMap(locale => {
const prefix = `https://echecsfrance.com/${locale === 'fr' ? '' : `${locale}/`}`
return [
{
url: "https://echecsfrance.com",
url: prefix,
lastModified: new Date(),
},
{
url: "https://echecsfrance.com/tournois",
url: `${prefix}tournois`,
lastModified: new Date(),
},
{
url: "https://echecsfrance.com/qui-sommes-nous",
url: `${prefix}qui-sommes-nous`,
lastModified: new Date(),
},
{
url: "https://echecsfrance.com/contactez-nous",
url: `${prefix}contactez-nous`,
lastModified: new Date(),
},
];
]});
}