import type { Request, Response, NextFunction } from 'express' import parser from 'accept-language-parser' import type { Language as parserLanguage } from 'accept-language-parser' import languages, { languageKeys } from '@/languages/lib/languages-server' import { USER_LANGUAGE_COOKIE_NAME } from '@/frame/lib/constants' import type { ExtendedRequest, Languages } from '@/types' import { updateLoggerContext } from '@/observability/logger/lib/logger-context' const chineseRegions = [ 'CN', // Mainland 'HK', // Hong Kong 'SG', // Singapore 'TW', // Taiwan ] function translationExists(language: parserLanguage) { if (language.code === 'zh') { return language.region && chineseRegions.includes(language.region) } return languageKeys.includes(language.code) } function getLanguageCode(language: parserLanguage) { return language.code === 'cn' && language.region && chineseRegions.includes(language.region) ? 'zh' : language.code } 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) { numTopPreferences++ } if (translationExists(browserLanguages[lang]) && numTopPreferences < 3) { return getLanguageCode(browserLanguages[lang]) } } } catch { return undefined } } function getUserLanguageFromCookie(req: Request) { const value: undefined | string = req.cookies[USER_LANGUAGE_COOKIE_NAME] if (value && (languages as Languages)[value]) { return value } } // determine language code from a path. Default to en if no valid match 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: Request) { const browserLanguages = parser.parse(req.headers['accept-language']) return getUserLanguage(browserLanguages) } 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) if (!req.userLanguage) { req.userLanguage = getLanguageCodeFromHeader(req) } updateLoggerContext({ language: req.language, userLanguage: req.userLanguage, }) return next() }