Skip to content

Commit

Permalink
Port detect-language.js to TypeScript (#50623)
Browse files Browse the repository at this point in the history
  • Loading branch information
peterbe authored May 15, 2024
1 parent a468992 commit 38ceff4
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 16 deletions.
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@
"@graphql-tools/load": "^8.0.0",
"@octokit/rest": "^20.1.0",
"@playwright/test": "1.44.0",
"@types/accept-language-parser": "1.5.6",
"@types/connect-datadog": "0.0.10",
"@types/connect-timeout": "0.0.39",
"@types/cookie": "0.6.0",
Expand Down
2 changes: 1 addition & 1 deletion src/frame/middleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from './set-fastly-surrogate-key.js'
import handleErrors from '@/observability/middleware/handle-errors'
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 context from './context/context.js'
import shortVersions from '@/versions/middleware/short-versions.js'
Expand Down
Original file line number Diff line number Diff line change
@@ -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 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 = [
'CN', // Mainland
Expand All @@ -10,25 +13,28 @@ const chineseRegions = [
'TW', // Taiwan
]

function translationExists(language) {
function translationExists(language: parserLanguage) {
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
return languageKeys.includes(language.code) && !languages[language.code].wip
return languageKeys.includes(language.code) && !(languages as Languages)[language.code].wip
}

function getLanguageCode(language) {
return language.code === 'cn' && chineseRegions.includes(language.region) ? 'zh' : language.code
function getLanguageCode(language: parserLanguage) {
return language.code === 'cn' && language.region && chineseRegions.includes(language.region)
? 'zh'
: language.code
}

function getUserLanguage(browserLanguages) {
function getUserLanguage(browserLanguages: parserLanguage[]) {
try {
let numTopPreferences = 1
for (let lang = 0; lang < browserLanguages.length; lang++) {
// 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++
}
if (translationExists(browserLanguages[lang]) && numTopPreferences < 3) {
return getLanguageCode(browserLanguages[lang])
}
Expand All @@ -38,26 +44,27 @@ function getUserLanguage(browserLanguages) {
}
}

function getUserLanguageFromCookie(req) {
const value = req.cookies[USER_LANGUAGE_COOKIE_NAME]
function getUserLanguageFromCookie(req: Request) {
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
if (value && languages[value] && !languages[value].wip) {
if (value && (languages as Languages)[value] && !(languages as Languages)[value].wip) {
return value
}
}

// 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)
return languageKeys.includes(maybeLanguage) ? maybeLanguage : 'en'
}

export function getLanguageCodeFromHeader(req) {
export function getLanguageCodeFromHeader(req: Request) {
const browserLanguages = parser.parse(req.headers['accept-language'])
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)
// Detecting browser language by user preference
req.userLanguage = getUserLanguageFromCookie(req)
Expand Down
14 changes: 14 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,19 @@ export type ExtendedRequest = Request & {
currentCategory?: string
error?: Error
}
language?: string
userLanguage?: string
// Add more properties here as needed
}

type Language = {
name: string
code: string
hreflang: string
dir: string
wip: boolean
}

export type Languages = {
[key: string]: Language
}

0 comments on commit 38ceff4

Please sign in to comment.