AcceptLanguage - Lightweight Accept-Language header parser with RFC 4647 Basic Filtering

AcceptLanguage is a lightweight, zero-dependency Elixir library for parsing the Accept-Language HTTP header field and matching user preferences against your application’s available locales.

It implements the Basic Filtering matching scheme defined in RFC 4647 Section 3.3.1, with full support for BCP 47 language tags.

Quick example

AcceptLanguage.negotiate("da, en-GB;q=0.8, en;q=0.7", [:en, :da])
# => :da

AcceptLanguage.negotiate("*, en;q=0", [:en, :fr])
# => :fr  (any language except English)

AcceptLanguage.negotiate("zh-Hant", [:"zh-Hant-TW", :"zh-Hans-CN"])
# => :"zh-Hant-TW"

What it supports

  • Quality values — ranks preferences by q-value, with declaration order as tie-breaker
  • Prefix matchingen matches en-US, en-GB, etc., respecting hyphen boundaries
  • Wildcards* matches any language not explicitly listed
  • Exclusionsq=0 marks languages as not acceptable
  • BCP 47 — script subtags (zh-Hant), region codes (es-419), variant subtags (de-1996)
  • Case-insensitive matching — preserves the original casing of your atoms

Standards compliance

  • RFC 7231 §5.3.5 — Accept-Language header field
  • RFC 7231 §5.3.1 — Quality values
  • RFC 4647 §3.3.1 — Basic Filtering
  • BCP 47 — Language tag structure

Other details

  • Single public function: AcceptLanguage.negotiate/2
  • Zero dependencies
  • Defensive limits against adversarial headers (4 KB field size, 50 range cap)
  • Elixir ~> 1.14

Hex | HexDocs

17 Likes

Good job!

For future reference, if someone needs a library which parses more than a single header value, there’s cowlib. It’s the most extensive library for such tasks.

2 Likes