Skip to content

Instantly share code, notes, and snippets.

@Recursing
Created August 30, 2023 16:43
Show Gist options
  • Save Recursing/f739303a3fc3397132b2ae7e06cfe5ed to your computer and use it in GitHub Desktop.
Save Recursing/f739303a3fc3397132b2ae7e06cfe5ed to your computer and use it in GitHub Desktop.
Parse accept-language header. Somehow I couldn't find a good enough implementation.
// Adapted from https://github.com/vercel/next.js/blob/dda62b693e3e9cf9fd566085e7372c46c16fa14a/packages/next/src/server/accept-header.ts#L13
interface Selection {
pos: number
q: number
token: string
}
export function acceptedLanguages(rawHeader: string) {
const header = rawHeader.replace(/[ \t]/g, '')
const parts = header.split(',')
const selections: Selection[] = []
for (let i = 0; i < parts.length; ++i) {
const part = parts[i]
if (!part) {
continue
}
const params = part.split(';')
if (params.length > 2) {
console.error(`Invalid 'accept-language' header`, rawHeader)
continue
}
const token = params[0].toLowerCase()
if (!token) {
console.error(`Invalid 'accept-language' header`, rawHeader)
continue
}
const selection: Selection = { token, pos: i, q: 1 }
if (params.length === 2) {
const q = params[1]
const [key, value] = q.split('=')
if (!value || (key !== 'q' && key !== 'Q')) {
console.error(`Invalid 'accept-language' header`, rawHeader)
continue
}
const score = parseFloat(value)
if (score === 0) {
continue
}
if (Number.isFinite(score) && score <= 1 && score >= 0.001) {
selection.q = score
}
}
selections.push(selection)
}
selections.sort((a, b) => {
if (b.q !== a.q) {
return b.q - a.q
}
return a.pos - b.pos
})
return selections.map((selection) => selection.token)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment