Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/frame/middleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import detectLanguage from '@/languages/middleware/detect-language'
import reloadTree from './reload-tree'
import context from './context/context'
import shortVersions from '@/versions/middleware/short-versions.js'
import languageCodeRedirects from '@/redirects/middleware/language-code-redirects.js'
import languageCodeRedirects from '@/redirects/middleware/language-code-redirects'
import handleRedirects from '@/redirects/middleware/handle-redirects'
import findPage from './find-page.js'
import blockRobots from './block-robots.js'
Expand Down
23 changes: 23 additions & 0 deletions src/languages/lib/languages.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
type Language = {
name: string
nativeName?: string
code: string
hreflang: string
redirectPatterns?: RegExp[]
dir: string
}
type Languages = {
[code: string]: Language
}

export const allLanguageKeys: string[]

export const languageKeys: string[]

export const languagePrefixPathRegex: RegExp

export declare function pathLanguagePrefixed(path: string): boolean

const languages: Languages

export default languages
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import languages from '#src/languages/lib/languages.js'
import { defaultCacheControl } from '#src/frame/middleware/cache-control.js'
import type { NextFunction, Response } from 'express'

import languages from '@/languages/lib/languages.js'
import { defaultCacheControl } from '@/frame/middleware/cache-control.js'
import { ExtendedRequest } from '@/types'

const redirectPatterns = Object.values(languages)
.map((language) => language.redirectPatterns || [])
Expand All @@ -16,14 +19,18 @@ const allRedirectPatterns = Object.values(languages)
.map((language) =>
(language.redirectPatterns || []).map((redirectPattern) => [language.code, redirectPattern]),
)
.flat()
.flat() as [string, RegExp][] // Seems TypeScript didn't understand the .flat()

// This middleware handles redirects for mistyped language codes
//
// Examples:
// /jp* -> /ja*
// /zh-TW* -> /zh*
export default function languageCodeRedirects(req, res, next) {
export default function languageCodeRedirects(
req: ExtendedRequest,
res: Response,
next: NextFunction,
) {
// Only in the unlikely event that the `req.path` starts with one of these
// prefixes do we bother looking up what the redirect should be.
if (req.path.startsWith('/_next/static')) return next()
Expand All @@ -32,10 +39,13 @@ export default function languageCodeRedirects(req, res, next) {

// This loop is almost never ever used so it doesn't have to be
// particularly smart or fast.
const [code, pattern] = allRedirectPatterns.find(([, pattern]) => pattern.test(req.path))
if (code && pattern) {
defaultCacheControl(res)
return res.redirect(301, req.path.replace(pattern, `/${code}`))
const matched = allRedirectPatterns.find(([, pattern]) => pattern.test(req.path))
if (matched) {
const [code, pattern] = matched
if (code && pattern) {
defaultCacheControl(res)
return res.redirect(301, req.path.replace(pattern, `/${code}`))
}
}
return next()
}