Port detect-language.js to TypeScript (#50623)
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@@ -104,6 +104,7 @@
|
|||||||
"@graphql-tools/load": "^8.0.0",
|
"@graphql-tools/load": "^8.0.0",
|
||||||
"@octokit/rest": "^20.1.0",
|
"@octokit/rest": "^20.1.0",
|
||||||
"@playwright/test": "1.44.0",
|
"@playwright/test": "1.44.0",
|
||||||
|
"@types/accept-language-parser": "1.5.6",
|
||||||
"@types/connect-datadog": "0.0.10",
|
"@types/connect-datadog": "0.0.10",
|
||||||
"@types/connect-timeout": "0.0.39",
|
"@types/connect-timeout": "0.0.39",
|
||||||
"@types/cookie": "0.6.0",
|
"@types/cookie": "0.6.0",
|
||||||
@@ -3117,6 +3118,12 @@
|
|||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/accept-language-parser": {
|
||||||
|
"version": "1.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/accept-language-parser/-/accept-language-parser-1.5.6.tgz",
|
||||||
|
"integrity": "sha512-lhSQUsAhAtbKjYgaw3f0c4EQKNQHFXhX87+OXUIqDHMkycvHGaqGskSRtnzysIUiqHPqNJ4BqI5SE++drsxx6A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/body-parser": {
|
"node_modules/@types/body-parser": {
|
||||||
"version": "1.19.5",
|
"version": "1.19.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||||
|
|||||||
@@ -290,6 +290,7 @@
|
|||||||
"@graphql-tools/load": "^8.0.0",
|
"@graphql-tools/load": "^8.0.0",
|
||||||
"@octokit/rest": "^20.1.0",
|
"@octokit/rest": "^20.1.0",
|
||||||
"@playwright/test": "1.44.0",
|
"@playwright/test": "1.44.0",
|
||||||
|
"@types/accept-language-parser": "1.5.6",
|
||||||
"@types/connect-datadog": "0.0.10",
|
"@types/connect-datadog": "0.0.10",
|
||||||
"@types/connect-timeout": "0.0.39",
|
"@types/connect-timeout": "0.0.39",
|
||||||
"@types/cookie": "0.6.0",
|
"@types/cookie": "0.6.0",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
} from './set-fastly-surrogate-key.js'
|
} from './set-fastly-surrogate-key.js'
|
||||||
import handleErrors from '@/observability/middleware/handle-errors'
|
import handleErrors from '@/observability/middleware/handle-errors'
|
||||||
import handleNextDataPath from './handle-next-data-path'
|
import handleNextDataPath from './handle-next-data-path'
|
||||||
import detectLanguage from '@/languages/middleware/detect-language.js'
|
import detectLanguage from '@/languages/middleware/detect-language'
|
||||||
import reloadTree from './reload-tree.js'
|
import reloadTree from './reload-tree.js'
|
||||||
import context from './context/context.js'
|
import context from './context/context.js'
|
||||||
import shortVersions from '@/versions/middleware/short-versions.js'
|
import shortVersions from '@/versions/middleware/short-versions.js'
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import languages, { languageKeys } from '#src/languages/lib/languages.js'
|
import type { Request, Response, NextFunction } from 'express'
|
||||||
import parser from 'accept-language-parser'
|
import parser from 'accept-language-parser'
|
||||||
|
import type { Language as parserLanguage } from 'accept-language-parser'
|
||||||
|
|
||||||
import { USER_LANGUAGE_COOKIE_NAME } from '#src/frame/lib/constants.js'
|
import languages, { languageKeys } from '@/languages/lib/languages.js'
|
||||||
|
import { USER_LANGUAGE_COOKIE_NAME } from '@/frame/lib/constants.js'
|
||||||
|
import type { ExtendedRequest, Languages } from '@/types'
|
||||||
|
|
||||||
const chineseRegions = [
|
const chineseRegions = [
|
||||||
'CN', // Mainland
|
'CN', // Mainland
|
||||||
@@ -10,25 +13,28 @@ const chineseRegions = [
|
|||||||
'TW', // Taiwan
|
'TW', // Taiwan
|
||||||
]
|
]
|
||||||
|
|
||||||
function translationExists(language) {
|
function translationExists(language: parserLanguage) {
|
||||||
if (language.code === 'zh') {
|
if (language.code === 'zh') {
|
||||||
return chineseRegions.includes(language.region)
|
return language.region && chineseRegions.includes(language.region)
|
||||||
}
|
}
|
||||||
// 92BD1212-61B8-4E7A: Remove ` && !languages[language.code].wip` for the public ship of ko, fr, de, ru
|
// 92BD1212-61B8-4E7A: Remove ` && !languages[language.code].wip` for the public ship of ko, fr, de, ru
|
||||||
return languageKeys.includes(language.code) && !languages[language.code].wip
|
return languageKeys.includes(language.code) && !(languages as Languages)[language.code].wip
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLanguageCode(language) {
|
function getLanguageCode(language: parserLanguage) {
|
||||||
return language.code === 'cn' && chineseRegions.includes(language.region) ? 'zh' : language.code
|
return language.code === 'cn' && language.region && chineseRegions.includes(language.region)
|
||||||
|
? 'zh'
|
||||||
|
: language.code
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserLanguage(browserLanguages) {
|
function getUserLanguage(browserLanguages: parserLanguage[]) {
|
||||||
try {
|
try {
|
||||||
let numTopPreferences = 1
|
let numTopPreferences = 1
|
||||||
for (let lang = 0; lang < browserLanguages.length; lang++) {
|
for (let lang = 0; lang < browserLanguages.length; lang++) {
|
||||||
// If language has multiple regions, Chrome adds the non-region language to list
|
// If language has multiple regions, Chrome adds the non-region language to list
|
||||||
if (lang > 0 && browserLanguages[lang].code !== browserLanguages[lang - 1].code)
|
if (lang > 0 && browserLanguages[lang].code !== browserLanguages[lang - 1].code) {
|
||||||
numTopPreferences++
|
numTopPreferences++
|
||||||
|
}
|
||||||
if (translationExists(browserLanguages[lang]) && numTopPreferences < 3) {
|
if (translationExists(browserLanguages[lang]) && numTopPreferences < 3) {
|
||||||
return getLanguageCode(browserLanguages[lang])
|
return getLanguageCode(browserLanguages[lang])
|
||||||
}
|
}
|
||||||
@@ -38,26 +44,27 @@ function getUserLanguage(browserLanguages) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserLanguageFromCookie(req) {
|
function getUserLanguageFromCookie(req: Request) {
|
||||||
const value = req.cookies[USER_LANGUAGE_COOKIE_NAME]
|
const value: undefined | string = req.cookies[USER_LANGUAGE_COOKIE_NAME]
|
||||||
|
|
||||||
// 92BD1212-61B8-4E7A: Remove ` && !languages[value].wip` for the public ship of ko, fr, de, ru
|
// 92BD1212-61B8-4E7A: Remove ` && !languages[value].wip` for the public ship of ko, fr, de, ru
|
||||||
if (value && languages[value] && !languages[value].wip) {
|
if (value && (languages as Languages)[value] && !(languages as Languages)[value].wip) {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine language code from a path. Default to en if no valid match
|
// determine language code from a path. Default to en if no valid match
|
||||||
export function getLanguageCodeFromPath(path) {
|
export function getLanguageCodeFromPath(path: string) {
|
||||||
const maybeLanguage = (path.split('/')[path.startsWith('/_next/data/') ? 4 : 1] || '').slice(0, 2)
|
const maybeLanguage = (path.split('/')[path.startsWith('/_next/data/') ? 4 : 1] || '').slice(0, 2)
|
||||||
return languageKeys.includes(maybeLanguage) ? maybeLanguage : 'en'
|
return languageKeys.includes(maybeLanguage) ? maybeLanguage : 'en'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLanguageCodeFromHeader(req) {
|
export function getLanguageCodeFromHeader(req: Request) {
|
||||||
const browserLanguages = parser.parse(req.headers['accept-language'])
|
const browserLanguages = parser.parse(req.headers['accept-language'])
|
||||||
return getUserLanguage(browserLanguages)
|
return getUserLanguage(browserLanguages)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function detectLanguage(req, res, next) {
|
export default function detectLanguage(req: ExtendedRequest, res: Response, next: NextFunction) {
|
||||||
req.language = getLanguageCodeFromPath(req.path)
|
req.language = getLanguageCodeFromPath(req.path)
|
||||||
// Detecting browser language by user preference
|
// Detecting browser language by user preference
|
||||||
req.userLanguage = getUserLanguageFromCookie(req)
|
req.userLanguage = getUserLanguageFromCookie(req)
|
||||||
14
src/types.ts
14
src/types.ts
@@ -10,5 +10,19 @@ export type ExtendedRequest = Request & {
|
|||||||
currentCategory?: string
|
currentCategory?: string
|
||||||
error?: Error
|
error?: Error
|
||||||
}
|
}
|
||||||
|
language?: string
|
||||||
|
userLanguage?: string
|
||||||
// Add more properties here as needed
|
// Add more properties here as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Language = {
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
hreflang: string
|
||||||
|
dir: string
|
||||||
|
wip: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Languages = {
|
||||||
|
[key: string]: Language
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user