mirror of
https://github.com/UiO-Gaming/uiogaming.no.git
synced 2026-11-05 13:56:47 +01:00
Merge pull request #51 from UiO-Gaming/fix/translation-rewrite
fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple fuck apple
This commit is contained in:
22 files changed
+204
-163
No files matched your search
@@ -6,6 +6,11 @@
|
|||||||
"complaint": "Complaint",
|
"complaint": "Complaint",
|
||||||
"documents": "Documents"
|
"documents": "Documents"
|
||||||
},
|
},
|
||||||
|
"metaTags": {
|
||||||
|
"time": "Time",
|
||||||
|
"location": "Location",
|
||||||
|
"description": "A student organisation that aims to create an inclusive environment around gaming and e-sports for the students at UiO"
|
||||||
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"back": "back",
|
"back": "back",
|
||||||
"flagImageAlt": "Norwegian flag"
|
"flagImageAlt": "Norwegian flag"
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
"complaint": "Klage",
|
"complaint": "Klage",
|
||||||
"documents": "Dokumenter"
|
"documents": "Dokumenter"
|
||||||
},
|
},
|
||||||
|
"metaTags": {
|
||||||
|
"time": "Tid",
|
||||||
|
"location": "Sted",
|
||||||
|
"description": "En hobbyforening som ønsker skape et sosialt miljø rundt gaming og e-sport på UiO!"
|
||||||
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"back": "tilbake",
|
"back": "tilbake",
|
||||||
"flagImageAlt": "Britisk flagg"
|
"flagImageAlt": "Britisk flagg"
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import type { NextConfig } from "next"
|
import type { NextConfig } from "next"
|
||||||
import createNextIntlPlugin from "next-intl/plugin"
|
import createNextIntlPlugin from "next-intl/plugin"
|
||||||
|
|
||||||
const withNextIntl = createNextIntlPlugin()
|
const withNextIntl = createNextIntlPlugin("./src/i18n.ts")
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
images: {
|
images: {
|
||||||
|
|||||||
File renamed without changes.
@@ -1,4 +1,4 @@
|
|||||||
import Layout from "@/app/layout"
|
import Layout from "@/app/[locale]/layout"
|
||||||
|
|
||||||
import Footer from "@/components/page-sections/footer"
|
import Footer from "@/components/page-sections/footer"
|
||||||
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
|
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
|
||||||
@@ -9,6 +9,7 @@ import { getBlogPost } from "@/lib/sanity"
|
|||||||
|
|
||||||
import { PortableText, PortableTextComponents } from "@portabletext/react"
|
import { PortableText, PortableTextComponents } from "@portabletext/react"
|
||||||
import { Metadata, Viewport } from "next"
|
import { Metadata, Viewport } from "next"
|
||||||
|
import { notFound } from "next/navigation"
|
||||||
import { getLocale } from "next-intl/server"
|
import { getLocale } from "next-intl/server"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ import * as styles from "./page.module.css"
|
|||||||
interface BlogPageParams {
|
interface BlogPageParams {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
slug: string
|
slug: string
|
||||||
|
locale: string
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,12 +41,6 @@ export async function generateMetadata({
|
|||||||
const { slug } = await params
|
const { slug } = await params
|
||||||
const data = await getBlogPost(slug)
|
const data = await getBlogPost(slug)
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return {
|
|
||||||
title: "Blog Post Not Found | UiO Gaming",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return generateSeoMetadata({
|
return generateSeoMetadata({
|
||||||
title: data.title,
|
title: data.title,
|
||||||
description: data.excerpt,
|
description: data.excerpt,
|
||||||
@@ -103,9 +99,7 @@ const BlogPost = async ({ params }: BlogPageParams) => {
|
|||||||
|
|
||||||
const data = await getBlogPost(slug)
|
const data = await getBlogPost(slug)
|
||||||
|
|
||||||
if (!data) {
|
if (!data) return notFound()
|
||||||
throw new Error("Blog post not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
const createdAt = new Date(data._createdAt).toLocaleDateString(locale, {
|
const createdAt = new Date(data._createdAt).toLocaleDateString(locale, {
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
@@ -136,7 +130,7 @@ const BlogPost = async ({ params }: BlogPageParams) => {
|
|||||||
{data.author?.image && (
|
{data.author?.image && (
|
||||||
<Image
|
<Image
|
||||||
className="circular"
|
className="circular"
|
||||||
alt={`Forfatteren av innlegget, ${data.author.name}`}
|
alt={data.author.name}
|
||||||
src={urlFor(data.author.image).width(50).height(50).url()}
|
src={urlFor(data.author.image).width(50).height(50).url()}
|
||||||
width={50}
|
width={50}
|
||||||
height={50}
|
height={50}
|
||||||
File renamed without changes.
@@ -1,10 +1,13 @@
|
|||||||
|
import Layout from "@/app/[locale]/layout"
|
||||||
|
|
||||||
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
|
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
|
||||||
import Back from "@/components/ui/back"
|
import Back from "@/components/ui/back"
|
||||||
|
|
||||||
import { getEvent } from "@/lib/sanity"
|
import { getEvent } from "@/lib/sanity"
|
||||||
|
|
||||||
import { Metadata, Viewport } from "next"
|
import { Metadata, Viewport } from "next"
|
||||||
import { getLocale } from "next-intl/server"
|
import { notFound } from "next/navigation"
|
||||||
|
import { getLocale, getTranslations } from "next-intl/server"
|
||||||
import { FaCalendarAlt, FaMapMarkerAlt } from "react-icons/fa"
|
import { FaCalendarAlt, FaMapMarkerAlt } from "react-icons/fa"
|
||||||
|
|
||||||
import * as styles from "./page.module.css"
|
import * as styles from "./page.module.css"
|
||||||
@@ -12,6 +15,7 @@ import * as styles from "./page.module.css"
|
|||||||
interface EventPageParams {
|
interface EventPageParams {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
slug: string
|
slug: string
|
||||||
|
locale: string
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,11 +25,8 @@ export async function generateMetadata({
|
|||||||
const { slug } = await params
|
const { slug } = await params
|
||||||
const data = await getEvent(slug)
|
const data = await getEvent(slug)
|
||||||
|
|
||||||
if (!data) {
|
const locale = await getLocale()
|
||||||
return {
|
const t = await getTranslations("metaTags")
|
||||||
title: "Event Not Found | UiO Gaming",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
const date = new Date(dateString)
|
const date = new Date(dateString)
|
||||||
@@ -38,12 +39,12 @@ export async function generateMetadata({
|
|||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
timeZone: "Europe/Oslo",
|
timeZone: "Europe/Oslo",
|
||||||
}
|
}
|
||||||
return date.toLocaleString("no", options)
|
return date.toLocaleString(locale, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
return generateSeoMetadata({
|
return generateSeoMetadata({
|
||||||
title: data.title,
|
title: data.title,
|
||||||
description: `Tid: ${formatDate(data.date)}\nSted: ${data.location}\n\n${data.description}`,
|
description: `${t("time")}: ${formatDate(data.date)}\n${t("location")}: ${data.location}\n\n${data.description}`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,9 +58,7 @@ const Event = async ({ params }: EventPageParams) => {
|
|||||||
|
|
||||||
const data = await getEvent(slug)
|
const data = await getEvent(slug)
|
||||||
|
|
||||||
if (!data) {
|
if (!data) return notFound()
|
||||||
throw new Error("Event not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
const date = new Date(dateString)
|
const date = new Date(dateString)
|
||||||
@@ -77,21 +76,23 @@ const Event = async ({ params }: EventPageParams) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<Layout>
|
||||||
<Back />
|
<div className={styles.container}>
|
||||||
<div className={styles.event}>
|
<Back />
|
||||||
<h1>{data.title}</h1>
|
<div className={styles.event}>
|
||||||
<div className={styles.metadata}>
|
<h1>{data.title}</h1>
|
||||||
<FaMapMarkerAlt />
|
<div className={styles.metadata}>
|
||||||
<p>{data.location}</p>
|
<FaMapMarkerAlt />
|
||||||
|
<p>{data.location}</p>
|
||||||
|
</div>
|
||||||
|
<div className={styles.metadata}>
|
||||||
|
<FaCalendarAlt />
|
||||||
|
<p>{formatDate(data.date)}</p>
|
||||||
|
</div>
|
||||||
|
<p className={styles.description}>{data.description}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.metadata}>
|
|
||||||
<FaCalendarAlt />
|
|
||||||
<p>{formatDate(data.date)}</p>
|
|
||||||
</div>
|
|
||||||
<p className={styles.description}>{data.description}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// app/[locale]/layout.tsx
|
||||||
|
import { NextIntlClientProvider } from "next-intl"
|
||||||
|
import { getLocale } from "next-intl/server"
|
||||||
|
|
||||||
|
import LanguageSelector from "@components/ui/languageSelector"
|
||||||
|
import UmamiAnalytics from "@/components/umamiAnalytics"
|
||||||
|
import { getAnalyticsConfig } from "@/lib/analyticsConfig"
|
||||||
|
|
||||||
|
export default async function LocaleLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
const locale = (await getLocale()) as "no" | "en"
|
||||||
|
const messages = (await import(`../../../messages/${locale}.json`)).default
|
||||||
|
|
||||||
|
const analyticsConfig = getAnalyticsConfig()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NextIntlClientProvider locale={locale} messages={messages}>
|
||||||
|
<LanguageSelector />
|
||||||
|
<main>{children}</main>
|
||||||
|
{analyticsConfig?.enabled && (
|
||||||
|
<UmamiAnalytics
|
||||||
|
websiteId={analyticsConfig.websiteId}
|
||||||
|
src={analyticsConfig.src}
|
||||||
|
domains={analyticsConfig.domains}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</NextIntlClientProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import Layout from "@/app/layout"
|
import Layout from "@/app/[locale]/layout"
|
||||||
|
|
||||||
import About from "@/components/page-sections/about"
|
import About from "@/components/page-sections/about"
|
||||||
import BoardMembers from "@/components/page-sections/boardMembers"
|
import BoardMembers from "@/components/page-sections/boardMembers"
|
||||||
@@ -13,7 +13,7 @@ import { Metadata, Viewport } from "next"
|
|||||||
|
|
||||||
export const revalidate = 3600 // 1 hour
|
export const revalidate = 3600 // 1 hour
|
||||||
|
|
||||||
export function generateMetadata(): Metadata {
|
export async function generateMetadata(): Promise<Metadata> {
|
||||||
return generateSeoMetadata({})
|
return generateSeoMetadata({})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,14 +3,17 @@ import Layout from "@/app/layout"
|
|||||||
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
|
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
|
||||||
|
|
||||||
import { Metadata, Viewport } from "next"
|
import { Metadata, Viewport } from "next"
|
||||||
|
import { getTranslations } from "next-intl/server"
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
|
|
||||||
import * as styles from "./global-not-found.module.css"
|
import * as styles from "./global-not-found.module.css"
|
||||||
|
|
||||||
export function generateMetadata(): Metadata {
|
export async function generateMetadata(): Promise<Metadata> {
|
||||||
|
const t = await getTranslations("404")
|
||||||
|
|
||||||
return generateSeoMetadata({
|
return generateSeoMetadata({
|
||||||
title: "404: Ikke funnet",
|
title: t("title"),
|
||||||
description: "Uh oh :(",
|
description: "Uh oh :(",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-27
@@ -1,12 +1,6 @@
|
|||||||
import "@/app/globals.css"
|
import "@/app/globals.css"
|
||||||
|
|
||||||
import { LocaleProvider } from "@/components/localeProvider"
|
|
||||||
import UmamiAnalytics from "@/components/umamiAnalytics"
|
|
||||||
|
|
||||||
import { getAnalyticsConfig } from "@/lib/analyticsConfig"
|
|
||||||
|
|
||||||
import type { Metadata } from "next"
|
import type { Metadata } from "next"
|
||||||
import { getLocale } from "next-intl/server"
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "UiO Gaming",
|
title: "UiO Gaming",
|
||||||
@@ -19,30 +13,14 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}>) {
|
}) {
|
||||||
const locale = (await getLocale()) as "no" | "en"
|
|
||||||
const messages = (await import(`../../messages/${locale}.json`)).default // TODO: duplicate of request.ts?
|
|
||||||
|
|
||||||
const analyticsConfig = getAnalyticsConfig()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang={locale}>
|
<html>
|
||||||
<body>
|
<body>{children}</body>
|
||||||
<LocaleProvider locale={locale} messages={messages}>
|
|
||||||
<main>{children}</main>
|
|
||||||
{analyticsConfig?.enabled && (
|
|
||||||
<UmamiAnalytics
|
|
||||||
websiteId={analyticsConfig.websiteId}
|
|
||||||
src={analyticsConfig.src}
|
|
||||||
domains={analyticsConfig.domains}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</LocaleProvider>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { redirect } from "next/navigation"
|
||||||
|
|
||||||
|
export default function RootPage() {
|
||||||
|
redirect("/en/404")
|
||||||
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
.button {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.25rem;
|
|
||||||
|
|
||||||
position: fixed;
|
|
||||||
top: 0.5rem;
|
|
||||||
right: 0.5rem;
|
|
||||||
z-index: 0;
|
|
||||||
|
|
||||||
background: pink;
|
|
||||||
color: black;
|
|
||||||
border: 1px solid black;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding-inline: 0.5em;
|
|
||||||
}
|
|
||||||
.button:hover {
|
|
||||||
background: #ff7b7b;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flag {
|
|
||||||
width: 1.2rem;
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { GB, NO } from "country-flag-icons/react/3x2"
|
|
||||||
import { NextIntlClientProvider } from "next-intl"
|
|
||||||
import { ReactNode, useEffect, useState } from "react"
|
|
||||||
|
|
||||||
import * as styles from "./localeProvider.module.css"
|
|
||||||
|
|
||||||
type LocaleProviderProps = {
|
|
||||||
locale: "no" | "en"
|
|
||||||
messages: Record<string, string>
|
|
||||||
children: ReactNode
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LocaleProvider({
|
|
||||||
locale: serverLocale,
|
|
||||||
messages,
|
|
||||||
children,
|
|
||||||
}: LocaleProviderProps) {
|
|
||||||
const [locale, setLocale] = useState<"no" | "en">(serverLocale)
|
|
||||||
const [loadedMessages, setLoadedMessages] =
|
|
||||||
useState<Record<string, string>>(messages)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const match = document.cookie.match(/(?:^|; )locale=([^;]+)/)
|
|
||||||
const saved = match ? decodeURIComponent(match[1]) : null
|
|
||||||
|
|
||||||
if (saved && saved !== locale) {
|
|
||||||
setLocale(saved as "no" | "en")
|
|
||||||
import(`../../messages/${saved}.json`).then(module => {
|
|
||||||
setLoadedMessages(module.default)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [locale])
|
|
||||||
|
|
||||||
const changeLocale = (newLocale: "no" | "en") => {
|
|
||||||
setLocale(newLocale)
|
|
||||||
document.cookie = `locale=${encodeURIComponent(
|
|
||||||
newLocale
|
|
||||||
)}; path=/; max-age=31536000; SameSite=Lax`
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NextIntlClientProvider locale={locale} messages={loadedMessages}>
|
|
||||||
{children}
|
|
||||||
|
|
||||||
<div className={styles.languageSwitcher}>
|
|
||||||
{locale === "no" ? (
|
|
||||||
<button onClick={() => changeLocale("en")} className={styles.button}>
|
|
||||||
<GB className={styles.flag} title="English flag" />
|
|
||||||
English
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button onClick={() => changeLocale("no")} className={styles.button}>
|
|
||||||
<NO className={styles.flag} title="Norwegian flag" />
|
|
||||||
Norsk
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</NextIntlClientProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -50,7 +50,7 @@ const Events = async () => {
|
|||||||
const mapEvents = (events: Event[]) => {
|
const mapEvents = (events: Event[]) => {
|
||||||
if (events.length !== 0) {
|
if (events.length !== 0) {
|
||||||
return events.map((event: Event) => (
|
return events.map((event: Event) => (
|
||||||
<Link key={event._id} href={"/event/" + event.slug.current}>
|
<Link key={event._id} href={`/${locale}/event/${event.slug.current}`}>
|
||||||
<article className={styles.card}>
|
<article className={styles.card}>
|
||||||
<h3>{event.title}</h3>
|
<h3>{event.title}</h3>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
+13
-6
@@ -1,4 +1,5 @@
|
|||||||
import { Metadata, Viewport } from "next"
|
import { Metadata, Viewport } from "next"
|
||||||
|
import { getLocale, getTranslations } from "next-intl/server"
|
||||||
|
|
||||||
type SeoProps = {
|
type SeoProps = {
|
||||||
title?: string
|
title?: string
|
||||||
@@ -7,16 +8,17 @@ type SeoProps = {
|
|||||||
author?: string
|
author?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateSeoMetadata({
|
export async function generateSeoMetadata({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
imageURL,
|
imageURL,
|
||||||
author,
|
author,
|
||||||
}: SeoProps): Metadata {
|
}: SeoProps): Promise<Metadata> {
|
||||||
|
const t = await getTranslations("metaTags")
|
||||||
|
const locale = await getLocale()
|
||||||
|
|
||||||
const defaultTitle = "UiO Gaming"
|
const defaultTitle = "UiO Gaming"
|
||||||
const metaDescription =
|
const metaDescription = description || t("description")
|
||||||
description ||
|
|
||||||
"En hobbyforening som ønsker skape et sosialt miljö rundt gaming og e-sport på UiO!"
|
|
||||||
const siteUrl = "https://uiogaming.no"
|
const siteUrl = "https://uiogaming.no"
|
||||||
const themeColor = "#7d36e7"
|
const themeColor = "#7d36e7"
|
||||||
|
|
||||||
@@ -24,11 +26,16 @@ export function generateSeoMetadata({
|
|||||||
title: title ? `${title} | ${defaultTitle}` : defaultTitle,
|
title: title ? `${title} | ${defaultTitle}` : defaultTitle,
|
||||||
description: metaDescription,
|
description: metaDescription,
|
||||||
authors: author ? [{ name: author }] : [{ name: "UiO Gaming" }],
|
authors: author ? [{ name: author }] : [{ name: "UiO Gaming" }],
|
||||||
|
icons: {
|
||||||
|
icon: "/logo_2024_neon_pink.png",
|
||||||
|
shortcut: "/logo_2024_neon_pink.png",
|
||||||
|
apple: "/logo_2024_neon_pink.png",
|
||||||
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: title || defaultTitle,
|
title: title || defaultTitle,
|
||||||
description: metaDescription,
|
description: metaDescription,
|
||||||
type: "website",
|
type: "website",
|
||||||
locale: "no",
|
locale: locale,
|
||||||
siteName: defaultTitle,
|
siteName: defaultTitle,
|
||||||
images: imageURL
|
images: imageURL
|
||||||
? [
|
? [
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: var(--link-color);
|
color: var(--link-color);
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.button:hover,
|
.button:hover,
|
||||||
.button *:hover {
|
.button *:hover {
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useTranslations } from "next-intl"
|
import { useLocale, useTranslations } from "next-intl"
|
||||||
|
import { useRouter } from "next/navigation"
|
||||||
import { FaArrowLeft } from "react-icons/fa"
|
import { FaArrowLeft } from "react-icons/fa"
|
||||||
|
|
||||||
import * as styles from "./back.module.css"
|
import * as styles from "./back.module.css"
|
||||||
|
|
||||||
const Back = () => {
|
const Back = () => {
|
||||||
|
const locale = useLocale()
|
||||||
|
const router = useRouter()
|
||||||
const t = useTranslations("buttons")
|
const t = useTranslations("buttons")
|
||||||
|
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
@@ -18,7 +21,7 @@ const Back = () => {
|
|||||||
if (window.history.length > 1 && isSameDomain) {
|
if (window.history.length > 1 && isSameDomain) {
|
||||||
window.history.back()
|
window.history.back()
|
||||||
} else {
|
} else {
|
||||||
window.location.href = "/"
|
router.push(`/${locale}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
.languageSwitcher {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: rgba(0, 0, 0, 0.35);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
background: rgba(0, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag {
|
||||||
|
width: 20px;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { GB, NO } from "country-flag-icons/react/3x2"
|
||||||
|
import { useLocale } from "next-intl"
|
||||||
|
import { useRouter, usePathname } from "next/navigation"
|
||||||
|
|
||||||
|
import * as styles from "./languageSelector.module.css"
|
||||||
|
|
||||||
|
const LanguageSelector = () => {
|
||||||
|
const locale = useLocale()
|
||||||
|
const router = useRouter()
|
||||||
|
const pathname = usePathname()
|
||||||
|
|
||||||
|
const changeLocale = (newLocale: "no" | "en") => {
|
||||||
|
// Remove the current locale from the pathname
|
||||||
|
const pathWithoutLocale = pathname.replace(/^\/[a-z]{2}/, "")
|
||||||
|
router.push(`/${newLocale}${pathWithoutLocale}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.languageSwitcher}>
|
||||||
|
{locale === "no" ? (
|
||||||
|
<button onClick={() => changeLocale("en")} className={styles.button}>
|
||||||
|
<GB className={styles.flag} title="English flag" />
|
||||||
|
English
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button onClick={() => changeLocale("no")} className={styles.button}>
|
||||||
|
<NO className={styles.flag} title="Norwegian flag" />
|
||||||
|
Norsk
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LanguageSelector
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
import { notFound } from "next/navigation"
|
||||||
|
import { getRequestConfig } from "next-intl/server"
|
||||||
|
|
||||||
|
const locales = ["no", "en"]
|
||||||
|
|
||||||
|
export default getRequestConfig(async ({ requestLocale }) => {
|
||||||
|
const locale = await requestLocale
|
||||||
|
|
||||||
|
if (!locale || !locales.includes(locale as "no" | "en")) notFound()
|
||||||
|
|
||||||
|
return {
|
||||||
|
locale,
|
||||||
|
messages: (await import(`../messages/${locale}.json`)).default,
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import createMiddleware from "next-intl/middleware"
|
||||||
|
|
||||||
|
export default createMiddleware({
|
||||||
|
locales: ["no", "en"],
|
||||||
|
defaultLocale: "no",
|
||||||
|
localePrefix: "always",
|
||||||
|
})
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
// Match only internationalized pathnames
|
||||||
|
matcher: ["/", "/(no|en)/:path*"],
|
||||||
|
}
|
||||||
Reference in new issue
Block a user