# v0.0.26 Plan — Accept-Language Frankenlocale Fix

**Date**: 2026-05-19
**Branch**: fix/v0.0.26-accept-language

## Root Cause

Camoufox's `geoip=True` (a Python/Camoufox option) auto-resolves the proxy IP's country and joins it with the random identity profile's base language. This produces frankenlocale BCP-47 strings like `en-DE` (English-in-Germany) or `fr-DE` (French-in-Germany). No real browser user has these. Anti-bot vendors (Google pre-WAF, PerimeterX) detect this trivially.

Confirmed in Phase 6 (Google) and Phase 6b (Walmart) HAR captures:
- Camoufox emits: `en-DE,en;q=0.5` or `fr-DE,fr;q=0.5`
- Expected: `de-DE,de;q=0.9,en;q=0.8` for a DE proxy with German identity

## Fix

Add `Accept-Language` to `BuildCamoufoxConfig()` in `identity/profile.go`, using a canonical BCP-47 string derived from the profile's `Languages` field.

The CAMOU_CONFIG key is exactly `"Accept-Language"` (per reference_camoufox.md HTTP Headers section).

### Canonical Accept-Language Lookup Table

The identity's `Languages` field (set by geo table or explicit) already has correct values:
- DE → `["de-DE", "de"]` → needs → `de-DE,de;q=0.9,en;q=0.8` (German users include English)
- US → `["en-US", "en"]` → needs → `en-US,en;q=0.9`
- FR → `["fr-FR", "fr"]` → needs → `fr-FR,fr;q=0.9,en;q=0.8`
- JP → `["ja-JP", "ja"]` → needs → `ja-JP,ja;q=0.9,en;q=0.8`

The issue: `buildAcceptLanguage(["de-DE","de"])` in stealth_default.go produces `de-DE,de;q=0.5` — correct for the stealth HTTP client. But for Camoufox, we want the more natural `de-DE,de;q=0.9,en;q=0.8` pattern that real Firefox users produce.

New function `canonicalAcceptLanguage(languages []string) string` in `identity/profile.go`:
- If languages is empty → `en-US,en;q=0.9`
- If languages[0] is `en-US` or `en-*` (English) → `<lang>,en;q=0.9` (or just `en-US,en;q=0.9`)  
- If primary language is non-English → `<primary>,<base>;q=0.9,en;q=0.8`
- Special case: if profile already has English in the list (e.g. NL has nl-NL,nl,en) → use profile's own list

Actually simpler: just use the identity's Languages list as-is with Firefox-accurate quality factors, but then CHECK that Languages[0] is a valid BCP-47 locale (lang-REGION format, both components internally consistent). The real problem is that Camoufox's geoip is OVERRIDING our CAMOU_CONFIG-set values with its own resolution. By setting `Accept-Language` in CAMOU_CONFIG, we pin the value before Camoufox's geoip logic.

Since `p.Languages` is already correctly set by `applyGeoToConfig` (using the builtin geo table), we simply need to emit those languages as the Accept-Language value in CAMOU_CONFIG.

The function uses a lookup table of language → canonical Accept-Language:
```
"en"  → "en-US,en;q=0.9"         (English speakers want en-US)
"de"  → "de-DE,de;q=0.9,en;q=0.8"
"fr"  → "fr-FR,fr;q=0.9,en;q=0.8"
"ja"  → "ja-JP,ja;q=0.9,en;q=0.8"
"ko"  → "ko-KR,ko;q=0.9,en;q=0.8"
"zh"  → "zh-CN,zh;q=0.9,en;q=0.8"
"ru"  → "ru-RU,ru;q=0.9,en;q=0.8"
"es"  → "es-ES,es;q=0.9,en;q=0.8"
"pt"  → "pt-BR,pt;q=0.9,en;q=0.8"
"nl"  → "nl-NL,nl;q=0.9,en;q=0.8"
"sv"  → "sv-SE,sv;q=0.9,en;q=0.8"
"nb"  → "nb-NO,no;q=0.9,en;q=0.8"
"pl"  → "pl-PL,pl;q=0.9,en;q=0.8"
"id"  → "id-ID,id;q=0.9,en;q=0.8"
"th"  → "th-TH,th;q=0.9,en;q=0.8"
"vi"  → "vi-VN,vi;q=0.9,en;q=0.8"
"it"  → "it-IT,it;q=0.9,en;q=0.8"
```

But actually, we should use `p.Languages` directly since those are already correct. The canonical function should build from the profile's Languages, not from a separate lookup. If we just use `p.Languages`, for DE that's `["de-DE","de"]` — and we should add `"en;q=0.8"` as most non-English Firefox users have English as fallback.

**Final approach**: use a small `canonicalAcceptLanguage(langs []string) string` that:
1. Takes the profile's Languages slice (already geo-correct)
2. Builds a Firefox-accurate quality-factor string with English fallback for non-English primaries
3. Special case: if all langs start with "en-" or "en", omit English fallback (it's already there)

## Camoufox Config Key

`"Accept-Language"` (string) — per reference_camoufox.md HTTP Headers section.

## Files Changed

- `identity/profile.go`: Add `canonicalAcceptLanguage()` function. Add `"Accept-Language"` key to `BuildCamoufoxConfig()`.
- `identity/identity_test.go`: Add unit tests for `canonicalAcceptLanguage` logic and for the CAMOU_CONFIG key being correct.

## Acceptance Criteria

1. `BuildCamoufoxConfig()["Accept-Language"]` exists for all profiles
2. For German proxy (WithCountry("DE")): value starts with `de-DE,de` not `en-DE` or `fr-DE`
3. For US proxy (WithCountry("US")): value is `en-US,en;q=0.9`
4. For LocalePolicyEnglishDefault + WithCountry("DE"): value is `en-US,en;q=0.9`
5. `go test ./identity/...` passes
6. Phase 6 re-run: Camoufox Accept-Language is clean BCP-47
7. Phase 6b re-run: Camoufox request count climbs from 11 → 100+ on Walmart
