feat: add Polish and English language switcher

This commit is contained in:
zv
2026-06-01 18:54:08 +02:00
parent 840555f4f5
commit 6c2e731c60
29 changed files with 531 additions and 143 deletions

View File

@@ -13,6 +13,11 @@ const themeScript = `
document.documentElement.classList.toggle("dark", storedTheme ? storedTheme === "dark" : prefersDark);
} catch {}
`;
const languageScript = `
try {
document.documentElement.lang = localStorage.getItem("wtr:language") === "en" ? "en" : "pl";
} catch {}
`;
export const metadata: Metadata = {
title: { default: "wtr. | Pogoda z danych IMGW", template: "%s | wtr." },
@@ -41,6 +46,7 @@ export default function RootLayout({ children }: Readonly<{ children: React.Reac
<html lang="pl" suppressHydrationWarning data-scroll-behavior="smooth">
<body className={`${inter.variable} font-sans`}>
<Script id="wtr-theme" strategy="beforeInteractive">{themeScript}</Script>
<Script id="wtr-language" strategy="beforeInteractive">{languageScript}</Script>
<Providers>
<AppShell>{children}</AppShell>
</Providers>

View File

@@ -1,13 +1,17 @@
"use client";
import Link from "next/link";
import { WifiOff } from "lucide-react";
import { useI18n } from "@/lib/i18n";
export default function OfflinePage() {
const { t } = useI18n();
return (
<section className="glass mx-auto mt-12 max-w-lg rounded-[2rem] p-8 text-center">
<div className="mx-auto flex size-14 items-center justify-center rounded-full bg-sky-500/10 text-sky-700 dark:text-sky-300"><WifiOff className="size-6" /></div>
<h1 className="mt-5 text-2xl font-semibold tracking-tight">Brak połączenia</h1>
<p className="mt-2 text-sm leading-6 text-slate-600 dark:text-slate-300">wtr. nie może teraz pobrać aktualnych danych IMGW. Ostatnio odwiedzone widoki mogą być dostępne z pamięci urządzenia.</p>
<Link href="/" className="mt-6 inline-flex rounded-full bg-slate-950 px-4 py-2.5 text-sm font-medium text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500 dark:bg-white dark:text-slate-950">Wróć do aplikacji</Link>
<h1 className="mt-5 text-2xl font-semibold tracking-tight">{t("offline.title")}</h1>
<p className="mt-2 text-sm leading-6 text-slate-600 dark:text-slate-300">{t("offline.description")}</p>
<Link href="/" className="mt-6 inline-flex rounded-full bg-slate-950 px-4 py-2.5 text-sm font-medium text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500 dark:bg-white dark:text-slate-950">{t("offline.back")}</Link>
</section>
);
}

View File

@@ -1,17 +1,8 @@
import type { Metadata } from "next";
import { WarningsPanel } from "@/components/warnings/warnings-panel";
import { WarningsPageContent } from "@/components/warnings/warnings-page-content";
export const metadata: Metadata = { title: "Ostrzeżenia" };
export const metadata: Metadata = { title: "Ostrzeżenia / Warnings" };
export default function WarningsPage() {
return (
<div className="space-y-5">
<div>
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-sky-700 dark:text-sky-300">Komunikaty IMGW</p>
<h1 className="mt-2 text-3xl font-semibold tracking-tight">Ostrzeżenia</h1>
<p className="mt-2 max-w-2xl text-sm leading-6 text-slate-600 dark:text-slate-300">Aktualne ostrzeżenia meteorologiczne i hydrologiczne publikowane przez IMGW. Szczegóły obszaru i czasu obowiązywania pochodzą bezpośrednio z API.</p>
</div>
<WarningsPanel />
</div>
);
return <WarningsPageContent />;
}