1
0
mirror of synced 2025-12-23 11:54:18 -05:00

Port detect-language.js to TypeScript (#50623)

This commit is contained in:
Peter Bengtsson
2024-05-15 11:31:04 -04:00
committed by GitHub
parent a4689920ac
commit 38ceff46f4
5 changed files with 45 additions and 16 deletions

7
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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'

View File

@@ -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)

View File

@@ -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
}