Skip to content

Commit

Permalink
Respect routing strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
LorisSigrist committed Feb 22, 2024
1 parent 5d79d6f commit 594c403
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import {
isAvailableLanguageTag,
} from "$paraglide/runtime.js"
import { addBasePath } from "./routing/basePath"
import { prefixStrategy } from "./routing/prefix"
import type { RoutingStrategy } from "./routing/prefix"
import NextLink from "next/link"
import React from "react"

const { localiseHref } = prefixStrategy({ availableLanguageTags, sourceLanguageTag, exclude: [] })

/**
* Creates a link component that localises the href based on the current language.
* @param languageTag A function that returns the current language tag.
*/
export function createLink(languageTag: () => string) {
export function createLink(languageTag: () => string, strategy: RoutingStrategy) {
return function Link(props: Parameters<typeof NextLink>[0]): ReturnType<typeof NextLink> {
const currentLanguageTag = languageTag()

Expand All @@ -36,7 +34,7 @@ export function createLink(languageTag: () => string) {
let lang = props.locale || currentLanguageTag
if (!isAvailableLanguageTag(lang)) lang = sourceLanguageTag

const localisedHref = localiseHref(props.href, lang)
const localisedHref = strategy.localiseHref(props.href, lang)

//If the language changes, we don't want client navigation
return lang == currentLanguageTag ? (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { availableLanguageTags, sourceLanguageTag } from "$paraglide/runtime.js"
import { createLink } from "./Link.base"
import { getLanguage } from "./getLanguage.client"
import { prefixStrategy } from "./routing/prefix"

export const Link = createLink(getLanguage)
export const Link = createLink(
getLanguage,
prefixStrategy({
availableLanguageTags,
sourceLanguageTag,
exclude: [],
})
)
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { availableLanguageTags, sourceLanguageTag } from "$paraglide/runtime.js"
import { createLink } from "./Link.base"
import { getLanguage } from "./getLanguage.server"
import { prefixStrategy } from "./routing/prefix"

/**
* React Component that enables client-side transitions between routes.
*
* Automatically localises the href based on the current language.
*/
export const Link = createLink(getLanguage)
export const Link = createLink(
getLanguage,
prefixStrategy({
availableLanguageTags,
sourceLanguageTag,
exclude: [],
})
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { createLink } from "./Link.base"
import { getLanguage } from "./getLanguage.server"
import { availableLanguageTags, sourceLanguageTag } from "$paraglide/runtime.js"
import { prefixStrategy } from "./routing/prefix"

export type I18nOptions = {
/**
Expand All @@ -16,14 +18,29 @@ export type I18nOptions = {
}

export function createI18n(options: I18nOptions = {}) {
const strategy = prefixStrategy({
availableLanguageTags,
sourceLanguageTag,
exclude: normalizeExcludes(options.exclude ?? []),
})

/**
* React Component that enables client-side transitions between routes.
*
* Automatically localises the href based on the current language.
*/
const Link = createLink(getLanguage)
const Link = createLink(getLanguage, strategy)

return {
Link,
}
}

function normalizeExcludes(excludes: (string | RegExp)[]): RegExp[] {
return excludes.map((exclude) => {
if (typeof exclude === "string") {
return new RegExp(`^${exclude}$`)
}
return exclude
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,52 @@ import { NextResponse } from "next/server"
import { NextRequest } from "next/server"
import { sourceLanguageTag, availableLanguageTags } from "$paraglide/runtime.js"
import { LANGUAGE_HEADER } from "../constants"
import { prefixStrategy } from "./routing/prefix"
import type { RoutingStrategy } from "./routing/prefix"

const { getLocaleFromLocalisedPath, translatePath } = prefixStrategy({
availableLanguageTags,
sourceLanguageTag,
exclude: [],
})
export function createMiddleware(strategy: RoutingStrategy) {
/**
* Sets the request headers to resolve the language tag in RSC.
* https://nextjs.org/docs/pages/building-your-application/routing/middleware#setting-headers
*/
return function middleware(request: NextRequest) {
const locale =
strategy.getLocaleFromLocalisedPath(request.nextUrl.pathname) ?? sourceLanguageTag

/**
* Sets the request headers to resolve the language tag in RSC.
* https://nextjs.org/docs/pages/building-your-application/routing/middleware#setting-headers
*/
export function middleware(request: NextRequest) {
const locale = getLocaleFromLocalisedPath(request.nextUrl.pathname) ?? sourceLanguageTag
const headers = new Headers(request.headers)
const canonicalPath = strategy.translatePath(request.nextUrl.pathname, sourceLanguageTag)

headers.set(LANGUAGE_HEADER, locale)
const headers = new Headers(request.headers)

//set Link header for alternate language versions
const linkHeader = generateLinkHeader({
availableLanguageTags,
localisedPathname: request.nextUrl.pathname,
})
headers.set("Link", linkHeader)
headers.set(LANGUAGE_HEADER, locale)

return NextResponse.next({
request: {
headers,
},
})
}
//set Link header for alternate language versions
const linkHeader = generateLinkHeader({
availableLanguageTags,
canonicalPath,
})
headers.set("Link", linkHeader)

/**
* Generates the Link header for the available language versions of the current page.
*/
function generateLinkHeader({
localisedPathname,
availableLanguageTags,
}: {
localisedPathname: string
availableLanguageTags: readonly string[]
}): string {
return availableLanguageTags
.map(
(lang) => `<${translatePath(localisedPathname, lang)}>; rel="alternate"; hreflang="${lang}"`
)
.join(", ")
return NextResponse.next({
request: {
headers,
},
})
}

/**
* Generates the Link header for the available language versions of the current page.
*/
function generateLinkHeader({
canonicalPath,
availableLanguageTags,
}: {
canonicalPath: string
availableLanguageTags: readonly string[]
}): string {
return availableLanguageTags
.map(
(lang) =>
`<${strategy.getLocalisedPath(canonicalPath, lang)}>; rel="alternate"; hreflang="${lang}"`
)
.join(", ")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ describe("translatePath", () => {
expect(translatePath("/some/path?foo=bar#hash", "de-CH")).toBe("/de-CH/some/path?foo=bar#hash")
expect(translatePath("/some/path?foo=bar#hash", "en")).toBe("/some/path?foo=bar#hash")
})

it("leaves excluded paths alone", () => {
expect(translatePath("/api/some/path", "de")).toBe("/api/some/path")
})
})

describe("localiseHref", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,6 @@ function isExternal(href: LinkProps["href"]) {
if (href.startsWith("//")) return true
return false
}


export type RoutingStrategy = ReturnType<typeof prefixStrategy>

0 comments on commit 594c403

Please sign in to comment.