Use when implementing language negotiation or locale fallback chains with @fluent/langneg. Prevents wrong strategy selection and broken fallback behavior. Covers negotiateLanguages() strategies, BCP 47 locale handling, acceptedLanguages() for HTTP headers, and fallback chain construction. Keywords: negotiateLanguages, @fluent/langneg, BCP 47, Accept-Language, locale fallback, filtering, matching, lookup, detect user language, browser language, language fallback, pick best language.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Use when implementing language negotiation or locale fallback chains with @fluent/langneg. Prevents wrong strategy selection and broken fallback behavior. Covers negotiateLanguages() strategies, BCP 47 locale handling, acceptedLanguages() for HTTP headers, and fallback chain construction. Keywords: negotiateLanguages, @fluent/langneg, BCP 47, Accept-Language, locale fallback, filtering, matching, lookup, detect user language, browser language, language fallback, pick best language.
license
MIT
compatibility
Designed for Claude Code. Requires @fluent/langneg 0.7+.
metadata
{"author":"OpenAEC-Foundation","version":"1.0"}
fluent-core-langneg
Quick Reference
Package Overview
Property
Value
Package
@fluent/langneg
Version
0.7.0
License
Apache-2.0
Dependencies
None (zero runtime dependencies)
Install
npm install @fluent/langneg
Exports
Export
Type
Purpose
negotiateLanguages
Function
Match user-requested locales against available locales
acceptedLanguages
Function
Parse HTTP Accept-Language header into sorted locale array
filterMatches
Function
Low-level matching engine (exported but rarely used directly)
Strategy Comparison
Given requested ["de-DE", "fr-FR"] and available ["it", "de", "en-US", "fr-CA", "de-DE", "fr", "de-AU"]:
Strategy
Result
Behavior
"filtering" (default)
["de-DE", "de", "fr", "fr-CA"]
ALL matches for ALL requested locales
"matching"
["de-DE", "fr"]
BEST single match per requested locale
"lookup"
["de-DE"]
SINGLE best match across all requested locales
Critical Warnings
NEVER use the "lookup" strategy without providing defaultLocale — the function throws an Error if defaultLocale is undefined with lookup strategy.
NEVER pass non-BCP 47 locale strings (e.g., "english", "ENUS") — the negotiation algorithm expects standard BCP 47 tags like "en-US", "fr-CA", "sr-Latn".
ALWAYS provide defaultLocale in production code — without it, negotiation can return an empty array if no matches are found, leaving the user with no translations.
Returns ALL available locales that match ANY requested locale. Most permissive — produces the longest fallback chain.
const result = negotiateLanguages(
["de-DE", "fr-FR"],
["it", "de", "en-US", "fr-CA", "de-DE", "fr", "de-AU"],
{ strategy: "filtering", defaultLocale: "en-US" }
);
// Result: ["de-DE", "de", "fr", "fr-CA", "en-US"]// "de-AU" excluded — no "de-AU" in requested; "en-US" appended as defaultLocale
ALWAYS use filtering when building a fallback chain for FluentBundle sequences — it maximizes translation coverage.
Matching
Returns the BEST single match for EACH requested locale. One result per request entry.
const result = negotiateLanguages(
["de-DE", "fr-FR"],
["it", "de", "en-US", "fr-CA", "de-DE", "fr", "de-AU"],
{ strategy: "matching", defaultLocale: "en-US" }
);
// Result: ["de-DE", "fr", "en-US"]// One match per requested locale; defaultLocale appended
ALWAYS use matching when you need a concise preference list without redundant variants.
Lookup
Returns the SINGLE best match across ALL requested locales. Most restrictive — exactly one result.
const result = negotiateLanguages(
["de-DE", "fr-FR"],
["it", "de", "en-US", "fr-CA", "de-DE", "fr", "de-AU"],
{ strategy: "lookup", defaultLocale: "en-US" }
);
// Result: ["de-DE"]// Single best match; defaultLocale used only if zero matches found
ALWAYS use lookup when you need exactly one locale (e.g., selecting a date format library, choosing a single UI direction).
Strategy Selection Decision Tree
Need to select locales?
├── Need a full fallback chain for translations?
│ └── YES → Use "filtering" (default)
│ Returns all matching locales for maximum coverage
│
├── Need one best locale per user preference?
│ └── YES → Use "matching"
│ Returns best match per requested locale
│
└── Need exactly one locale for the entire app?
└── YES → Use "lookup"
Returns single best match
⚠ REQUIRES defaultLocale or throws Error
acceptedLanguages()
Parses HTTP Accept-Language headers into a sorted array of locale strings.
Sorts by descending quality, preserving order for equal weights
ALWAYS use on the server instead of navigator.languages (which is unavailable in Node.js)
Throws TypeError if the argument is not a string
BCP 47 Locale Identifiers
All locale strings in @fluent/langneg follow BCP 47 (IETF language tag standard):
Format
Example
Components
Language
en, fr, de
ISO 639 language code
Language + Region
en-US, fr-CA, de-DE
Language + ISO 3166 country code
Language + Script
sr-Latn, zh-Hans
Language + ISO 15924 script code
Subtag Matching
The negotiation algorithms perform subtag matching — a requested "de-DE" will match an available "de" as a fallback. The library includes minimal likely-subtags data to resolve generic locales (e.g., requesting "en" can match both "en-GB" and "en-US").
Input locales are coerced to strings internally via Array.from(...).map(String).
Integration with @fluent/react
ALWAYS use negotiateLanguages to determine the locale order before creating FluentBundle instances:
The order returned by negotiateLanguages determines the fallback chain — the first bundle is the preferred locale, subsequent bundles serve as fallbacks for missing translations.
Reference Links
references/methods.md — Full API signatures for negotiateLanguages, acceptedLanguages, filterMatches