npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@fastkit/accept-language

v0.15.0

Published

Small parser that parses Accept-Language headers and sorts by quality.

Readme

@fastkit/accept-language

🌐 English | 日本語

A lightweight parser library that analyzes HTTP Accept-Language headers and sorts them by quality order. Used in multilingual web applications to properly determine the language preferred by clients.

Features

  • Accept-Language Header Parsing: Full compatibility with standard HTTP header format
  • Quality Value Sorting: Prioritization based on q-values (quality values)
  • Language Code Analysis: Detailed parsing of code, script, and region
  • Optimal Language Selection: Automatic selection of the best language from supported language list
  • Loose Matching Option: Support for partial language matching
  • Full TypeScript Support: Type safety through strict type definitions
  • Lightweight Design: Efficient implementation with minimal dependencies
  • Error Handling: Robust handling of malformed headers

Installation

npm install @fastkit/accept-language

Basic Usage

Accept-Language Header Parsing

import { parse } from '@fastkit/accept-language'

// Parse typical Accept-Language header
const acceptLanguage = 'en-US,en;q=0.9,ja;q=0.8,zh-CN;q=0.7'
const parsed = parse(acceptLanguage)

console.log(parsed)
// [
//   { code: 'en', script: null, region: 'US', quality: 1.0 },
//   { code: 'en', script: null, region: '', quality: 0.9 },
//   { code: 'ja', script: null, region: '', quality: 0.8 },
//   { code: 'zh', script: null, region: 'CN', quality: 0.7 }
// ]

// Parse language codes with scripts
const complexHeader = 'zh-Hans-CN;q=0.9,zh-Hant-TW;q=0.8,en;q=0.7'
const complexParsed = parse(complexHeader)

console.log(complexParsed)
// [
//   { code: 'zh', script: 'Hans', region: 'CN', quality: 0.9 },
//   { code: 'zh', script: 'Hant', region: 'TW', quality: 0.8 },
//   { code: 'en', script: null, region: '', quality: 0.7 }
// ]

Optimal Language Selection

import { pick } from '@fastkit/accept-language'

// List of supported languages
const supportedLanguages = ['en-US', 'ja-JP', 'zh-CN', 'fr-FR']

// Client's Accept-Language header
const clientLanguages = 'ja;q=0.9,en-US;q=0.8,zh-CN;q=0.7'

// Select the best language
const bestLanguage = pick(supportedLanguages, clientLanguages)
console.log(bestLanguage) // 'ja-JP'

// Can also select from parsed language array
const parsedLanguages = parse(clientLanguages)
const bestFromParsed = pick(supportedLanguages, parsedLanguages)
console.log(bestFromParsed) // 'ja-JP'

Language Selection with Loose Matching

import { pick } from '@fastkit/accept-language'

const supportedLanguages = ['en', 'ja', 'zh-CN']
const clientLanguages = 'en-US,ja-JP,zh-TW'

// Strict matching (default)
const strictMatch = pick(supportedLanguages, clientLanguages)
console.log(strictMatch) // null (no exact match)

// Enable loose matching
const looseMatch = pick(supportedLanguages, clientLanguages, { loose: true })
console.log(looseMatch) // 'en' (matches by language code only)

Practical Usage Examples

Multilingual Support with Express.js

import express from 'express'
import { pick } from '@fastkit/accept-language'

const app = express()

// Supported language configuration
const SUPPORTED_LANGUAGES = ['en-US', 'ja-JP', 'ko-KR', 'zh-CN']
const DEFAULT_LANGUAGE = 'en-US'

// Language detection middleware
app.use((req, res, next) => {
  const acceptLanguage = req.headers['accept-language']

  // Determine the optimal language
  const preferredLanguage = pick(
    SUPPORTED_LANGUAGES,
    acceptLanguage,
    { loose: true }
  ) || DEFAULT_LANGUAGE

  // Add language information to request
  req.locale = preferredLanguage
  res.locals.locale = preferredLanguage

  next()
})

// API response example
app.get('/api/messages', (req, res) => {
  const messages = getLocalizedMessages(req.locale)
  res.json({
    locale: req.locale,
    messages
  })
})

Internationalization with Next.js

// middleware.ts
import { NextRequest, NextResponse } from 'next/server'
import { pick } from '@fastkit/accept-language'

const SUPPORTED_LOCALES = ['en', 'ja', 'ko', 'zh']
const DEFAULT_LOCALE = 'en'

export function middleware(request: NextRequest) {
  // Select optimal language from Accept-Language header
  const acceptLanguage = request.headers.get('accept-language')
  const preferredLocale = pick(
    SUPPORTED_LOCALES,
    acceptLanguage,
    { loose: true }
  ) || DEFAULT_LOCALE

  // Redirect if path doesn't include language code
  const pathname = request.nextUrl.pathname
  if (!SUPPORTED_LOCALES.some(locale => pathname.startsWith(`/${locale}`))) {
    const url = request.nextUrl.clone()
    url.pathname = `/${preferredLocale}${pathname}`
    return NextResponse.redirect(url)
  }
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
}

Automatic Language Setup in Vue.js Applications

// plugins/i18n.ts
import { createI18n } from 'vue-i18n'
import { pick } from '@fastkit/accept-language'

// Get browser language settings
const browserLanguages = navigator.language || navigator.languages?.[0]
const supportedLocales = ['en-US', 'ja-JP', 'ko-KR']

// Auto-select optimal language
const defaultLocale = pick(
  supportedLocales,
  browserLanguages,
  { loose: true }
) || 'en-US'

export const i18n = createI18n({
  legacy: false,
  locale: defaultLocale,
  fallbackLocale: 'en-US',
  messages: {
    'en-US': enMessages,
    'ja-JP': jaMessages,
    'ko-KR': koMessages
  }
})

Custom Language Detection Function

import { parse, pick, type ParsedLanguage } from '@fastkit/accept-language'

class LanguageDetector {
  private supportedLanguages: string[]
  private fallbackLanguage: string

  constructor(
    supportedLanguages: string[],
    fallbackLanguage: string
  ) {
    this.supportedLanguages = supportedLanguages
    this.fallbackLanguage = fallbackLanguage
  }

  /**
   * Determine optimal language (combining multiple strategies)
   */
  detectBestLanguage(
    acceptLanguageHeader?: string | null,
    userPreference?: string,
    sessionLanguage?: string
  ): string {
    // 1. User preference takes highest priority
    if (userPreference && this.isSupported(userPreference)) {
      return userPreference
    }

    // 2. Consider session language next
    if (sessionLanguage && this.isSupported(sessionLanguage)) {
      return sessionLanguage
    }

    // 3. Determine from Accept-Language header
    if (acceptLanguageHeader) {
      const detected = pick(
        this.supportedLanguages,
        acceptLanguageHeader,
        { loose: true }
      )
      if (detected) return detected
    }

    // 4. Return fallback language
    return this.fallbackLanguage
  }

  /**
   * Detailed language analysis
   */
  analyzeLanguagePreferences(acceptLanguageHeader: string) {
    const parsed = parse(acceptLanguageHeader)

    return {
      languages: parsed,
      hasHighQuality: parsed.some(lang => lang.quality >= 0.8),
      primaryLanguage: parsed[0]?.code,
      supportedMatches: parsed.filter(lang =>
        this.supportedLanguages.some(supported =>
          supported.toLowerCase().startsWith(lang.code.toLowerCase())
        )
      )
    }
  }

  private isSupported(language: string): boolean {
    return this.supportedLanguages.includes(language)
  }
}

// Usage example
const detector = new LanguageDetector(
  ['en-US', 'ja-JP', 'ko-KR', 'zh-CN'],
  'en-US'
)

const bestLanguage = detector.detectBestLanguage(
  'ja;q=0.9,en-US;q=0.8,ko;q=0.7',
  undefined, // No user preference
  'zh-CN'    // Session language
)

console.log(bestLanguage) // 'zh-CN'

API Specification

parse(acceptLanguage)

Parses Accept-Language header string and returns language array sorted by quality.

Parameters:

  • acceptLanguage (string | null | undefined): Accept-Language header string

Return Value:

  • ParsedLanguage[]: Array of parsed languages
interface ParsedLanguage {
  code: string;        // Language code (e.g. 'en', 'ja')
  script: string | null; // Script (e.g. 'Hans', 'Hant')
  region: string;      // Region code (e.g. 'US', 'JP')
  quality: number;     // Quality value (0.0-1.0)
}

pick(supportedLanguages, acceptLanguages, options?)

Selects the most suitable language from the list of supported languages based on client preferences.

Parameters:

  • supportedLanguages (string[] | readonly string[]): List of supported languages
  • acceptLanguages (string | ParsedLanguage[] | null | undefined): Accept-Language header or parsed language array
  • options (PickOptions, optional): Selection options

Return Value:

  • string | null: Optimal language (null if not found)
interface PickOptions {
  loose?: boolean; // Allow loose matching (default: false)
}

Advanced Usage Examples

Region-Specific Language Processing

import { parse, pick } from '@fastkit/accept-language'

// Regional language variations
const regionalLanguages = {
  'zh-CN': '简体中文',  // China (Simplified)
  'zh-TW': '繁體中文',  // Taiwan (Traditional)
  'zh-HK': '繁體中文',  // Hong Kong (Traditional)
  'en-US': 'English (US)',
  'en-GB': 'English (UK)',
  'pt-BR': 'Português (Brasil)',
  'pt-PT': 'Português (Portugal)'
}

function getRegionalLanguage(acceptLanguage: string) {
  const supportedLanguages = Object.keys(regionalLanguages)

  // Try strict matching first
  let selected = pick(supportedLanguages, acceptLanguage)

  if (!selected) {
    // Retry with loose matching
    selected = pick(supportedLanguages, acceptLanguage, { loose: true })
  }

  return selected ? {
    code: selected,
    name: regionalLanguages[selected as keyof typeof regionalLanguages],
    isExactMatch: !parse(acceptLanguage).some(lang =>
      `${lang.code}-${lang.region}` === selected
    )
  } : null
}

Performance Optimization

import { parse, pick } from '@fastkit/accept-language'

class OptimizedLanguageDetector {
  private cache = new Map<string, string | null>()
  private readonly maxCacheSize = 1000

  constructor(
    private supportedLanguages: readonly string[],
    private defaultLanguage: string
  ) {}

  detectLanguage(acceptLanguageHeader: string): string {
    // Search from cache
    const cached = this.cache.get(acceptLanguageHeader)
    if (cached !== undefined) {
      return cached || this.defaultLanguage
    }

    // Execute new detection
    const detected = pick(
      this.supportedLanguages,
      acceptLanguageHeader,
      { loose: true }
    )

    // Cache size limit
    if (this.cache.size >= this.maxCacheSize) {
      const firstKey = this.cache.keys().next().value
      this.cache.delete(firstKey)
    }

    // Cache result
    this.cache.set(acceptLanguageHeader, detected)

    return detected || this.defaultLanguage
  }

  clearCache(): void {
    this.cache.clear()
  }
}

Considerations

Browser Support

  • Supported in all modern browsers
  • Works with Internet Explorer 11 and later
  • Recommended for use in Node.js environments

Performance Considerations

  • Parsing is lightweight, but caching results is recommended for high-volume requests
  • Processing time for long Accept-Language headers is proportional to number of languages
  • Memory usage depends on number of languages processed

Security

  • Input validation is performed internally
  • Robust handling of malformed header values
  • Values that could lead to XSS attacks are automatically excluded

License

MIT

Related Packages