mirror of
https://github.com/UiO-Gaming/uiogaming.no.git
synced 2026-11-05 13:56:47 +01:00
Define locales once with next-intl's routing config
Locales were listed separately in the middleware, the request config and several type casts. Use defineRouting and createNavigation, type the Locale globally, and let the language selector use the locale-aware router so it keeps the query string and hash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b84781ebe5
commit
5b135ce712
13 files changed
+53
-25
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
||||
import type { NextConfig } from "next"
|
||||
import createNextIntlPlugin from "next-intl/plugin"
|
||||
|
||||
const withNextIntl = createNextIntlPlugin("./src/i18n.ts")
|
||||
const withNextIntl = createNextIntlPlugin()
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
images: {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { getBlogPost } from "@/lib/sanity"
|
||||
|
||||
import { PortableText, PortableTextComponents } from "@portabletext/react"
|
||||
import { Metadata, Viewport } from "next"
|
||||
import type { Locale } from "next-intl"
|
||||
import { notFound } from "next/navigation"
|
||||
import Image from "next/image"
|
||||
|
||||
@@ -15,7 +16,7 @@ import * as styles from "./page.module.css"
|
||||
interface BlogPageParams {
|
||||
params: Promise<{
|
||||
slug: string
|
||||
locale: string
|
||||
locale: Locale
|
||||
}>
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import Back from "@/components/ui/back"
|
||||
import { getEvent } from "@/lib/sanity"
|
||||
|
||||
import { Metadata, Viewport } from "next"
|
||||
import type { Locale } from "next-intl"
|
||||
import { notFound } from "next/navigation"
|
||||
import { getTranslations } from "next-intl/server"
|
||||
import { FaCalendarAlt, FaMapMarkerAlt } from "react-icons/fa"
|
||||
@@ -13,7 +14,7 @@ import * as styles from "./page.module.css"
|
||||
interface EventPageParams {
|
||||
params: Promise<{
|
||||
slug: string
|
||||
locale: string
|
||||
locale: Locale
|
||||
}>
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { notFound } from "next/navigation"
|
||||
|
||||
import LanguageSelector from "@components/ui/languageSelector"
|
||||
import UmamiAnalytics from "@/components/umamiAnalytics"
|
||||
import { locales } from "@/i18n"
|
||||
import { routing } from "@/i18n/routing"
|
||||
import { getAnalyticsConfig } from "@/lib/analyticsConfig"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -21,7 +21,7 @@ export const metadata: Metadata = {
|
||||
}
|
||||
|
||||
export function generateStaticParams() {
|
||||
return locales.map(locale => ({ locale }))
|
||||
return routing.locales.map(locale => ({ locale }))
|
||||
}
|
||||
|
||||
export default async function RootLayout({
|
||||
@@ -34,7 +34,7 @@ export default async function RootLayout({
|
||||
const { locale } = await params
|
||||
|
||||
// [locale] matches any first path segment, e.g. /favicon.txt or /de
|
||||
if (!hasLocale(locales, locale)) notFound()
|
||||
if (!hasLocale(routing.locales, locale)) notFound()
|
||||
|
||||
const messages = (await import(`../../../messages/${locale}.json`)).default
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@ import Office from "@/components/page-sections/office"
|
||||
import { generateSeoMetadata, generateSeoViewport } from "@/components/seo"
|
||||
|
||||
import { Metadata, Viewport } from "next"
|
||||
import type { Locale } from "next-intl"
|
||||
|
||||
export const revalidate = 3600 // 1 hour
|
||||
|
||||
interface HomePageParams {
|
||||
params: Promise<{
|
||||
locale: string
|
||||
locale: Locale
|
||||
}>
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ interface BoardMember {
|
||||
|
||||
const BoardMembers = async () => {
|
||||
const t = await getTranslations("boardMembers")
|
||||
const locale = (await getLocale()) as "no" | "en"
|
||||
const locale = await getLocale()
|
||||
|
||||
const data: BoardMember[] = await getBoardMembers()
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { Locale } from "next-intl"
|
||||
import { getTranslations } from "next-intl/server"
|
||||
import Link from "next/link"
|
||||
|
||||
import * as styles from "./notFound.module.css"
|
||||
|
||||
const NotFound = async ({ locale }: { locale: string }) => {
|
||||
const NotFound = async ({ locale }: { locale: Locale }) => {
|
||||
const t = await getTranslations({ locale, namespace: "404" })
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Metadata, Viewport } from "next"
|
||||
import type { Locale } from "next-intl"
|
||||
import { getTranslations } from "next-intl/server"
|
||||
|
||||
type SeoProps = {
|
||||
locale: string
|
||||
locale: Locale
|
||||
title?: string
|
||||
description?: string
|
||||
imageURL?: string
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
"use client"
|
||||
|
||||
import { GB, NO } from "country-flag-icons/react/3x2"
|
||||
import { useLocale } from "next-intl"
|
||||
import { useRouter, usePathname } from "next/navigation"
|
||||
import { type Locale, useLocale } from "next-intl"
|
||||
|
||||
import { usePathname, useRouter } from "@/i18n/navigation"
|
||||
|
||||
import * as styles from "./languageSelector.module.css"
|
||||
|
||||
const LanguageSelector = () => {
|
||||
const locale = useLocale()
|
||||
const router = useRouter()
|
||||
// Pathname without the locale prefix
|
||||
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}`)
|
||||
const changeLocale = (newLocale: Locale) => {
|
||||
// Read at click time, since useSearchParams() would opt pages out of
|
||||
// static rendering
|
||||
const { search, hash } = window.location
|
||||
router.push(`${pathname}${search}${hash}`, { locale: newLocale })
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { createNavigation } from "next-intl/navigation"
|
||||
|
||||
import { routing } from "./routing"
|
||||
|
||||
// Locale-aware wrappers around Next's navigation APIs
|
||||
export const { Link, redirect, usePathname, useRouter, getPathname } =
|
||||
createNavigation(routing)
|
||||
@@ -2,18 +2,19 @@ import { hasLocale } from "next-intl"
|
||||
import { getRequestConfig } from "next-intl/server"
|
||||
import { locale as rootLocale } from "next/root-params"
|
||||
|
||||
export const locales = ["no", "en"] as const
|
||||
const defaultLocale = "no"
|
||||
import { routing } from "./routing"
|
||||
|
||||
export default getRequestConfig(async ({ locale }) => {
|
||||
// An explicit locale (e.g. getTranslations({ locale })) takes precedence.
|
||||
// The root param can be any first path segment until the layout rejects it,
|
||||
// so fall back to the default instead of throwing
|
||||
const requested = locale ?? (await rootLocale())
|
||||
const resolved = hasLocale(locales, requested) ? requested : defaultLocale
|
||||
const resolved = hasLocale(routing.locales, requested)
|
||||
? requested
|
||||
: routing.defaultLocale
|
||||
|
||||
return {
|
||||
locale: resolved,
|
||||
messages: (await import(`../messages/${resolved}.json`)).default,
|
||||
messages: (await import(`../../messages/${resolved}.json`)).default,
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
import { defineRouting } from "next-intl/routing"
|
||||
|
||||
export const routing = defineRouting({
|
||||
locales: ["no", "en"],
|
||||
defaultLocale: "no",
|
||||
localePrefix: "always",
|
||||
})
|
||||
|
||||
// Types getLocale(), useLocale() etc. as "no" | "en" instead of string
|
||||
declare module "next-intl" {
|
||||
interface AppConfig {
|
||||
Locale: (typeof routing.locales)[number]
|
||||
}
|
||||
}
|
||||
+3
-5
@@ -1,10 +1,8 @@
|
||||
import createMiddleware from "next-intl/middleware"
|
||||
|
||||
export default createMiddleware({
|
||||
locales: ["no", "en"],
|
||||
defaultLocale: "no",
|
||||
localePrefix: "always",
|
||||
})
|
||||
import { routing } from "./i18n/routing"
|
||||
|
||||
export default createMiddleware(routing)
|
||||
|
||||
export const config = {
|
||||
// Match everything except API routes, Next internals and files with an
|
||||
|
||||
Reference in new issue
Block a user