Repository navigation
docs(capture): define $locale as the device language and add $app_language - #172
Draft
posthog[bot] wants to merge 1 commit into
Draft
posthog[bot] wants to merge 1 commit into
posthog[bot] wants to merge 1 commit into
Conversation
…guage posthog-ios 4.0 and posthog-android 4.0 redefine $locale as the device's first preferred language, as a full language tag, and add $app_language for the language the app displays plus a setAppLanguage() override. The capture spec said nothing about either property. Proposed, applied and archived on this branch per AGENTS.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: 07d78af0-3d76-41ed-985e-0a27b54b645c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Nothing in
openspec/specs/says what$localemeans. It is one of the oldest client auto-properties and no spec names it, so each client SDK invented its own meaning and the meanings disagreed:Locale.current.languageCode— a bare language subtag,defor ade-CHdevice.Locale.getDefault(), which is the app's locale: a per-app language, a secondary device language, or whatever the app last passed toLocale.setDefault(). It also dropped the script (zh-TWforzh-Hant-TW) and sent the legacy codesiw,in,ji.languageTag; posthog_flutter's pure-Dart desktop client sends the language code only; posthog-kmp's JVM context composes"${language}-${country}"offLocale.getDefault().So
$locale = dematched an iOS user and missed the Android one, and nothing reported the language the app actually displays — the thing most people are reaching for when they break down by$locale.PostHog/posthog-ios#1009 and PostHog/posthog-android#927 (closing posthog-android#822) settle it together, as one cross-SDK change for the 4.0 majors. Both are breaking changes to a property's meaning, which is the kind of contract this repo exists to hold.
What changed
One new requirement in
capture, Device language and app display language:$localeis the first language in the device's own preferences, as a full BCP 47 tag with script and region (de-CH,zh-Hant-TW), with Unicode extension subtags (-u-fw-mon) stripped. It is not read from a process-global default the app can set on itself.$app_languageis the language the app's UI is displayed in, as the platform names that localization (de,pt-BR,zh-Hans).und/C/POSIXfor$locale, undetectable display language for$app_language, which never falls back to$locale. Detection may be limited by platform version (Android 13+).setAppLanguage()overrides$app_language: trimmed and normalized (" pt_BR "→pt-BR), cleared by null or blank, held in memory only and not cleared byreset().$localefollows it, Android keeps$localedevice-wide and reports the choice through$app_languageonly.Proposed, applied and archived on this branch per AGENTS.md:
openspec/changes/archive/2026-11-04-record-device-and-app-language/.Impact on other SDKs
posthog-react-native already sends a full language tag but has no
$app_languageor setter. posthog_flutter's desktop client sends a bare language code. posthog-kmp's JVM context readsLocale.getDefault(), the app-influenced source the requirement rules out. Those three have to change. An SDK that reports no language today (posthog-js browser) is not obliged to start.Uncertain / for human review
captureis where client ambient enrichment is specified, so the requirement went there. A separateevent-contextcapability for the static/dynamic context properties as a whole may be the better long-run shape — none exists today and creating one is a bigger call than this change.$localeis not cross-platform comparable for those users. Whether that is the intended end state is a question for a human.$app_languagenever falls back to$locale". Both are the behavior the PRs describe, but neither SDK states them as a contract.acceptance/is untouched. The harness has no steps for device language, so the eleven scenarios live inspec.mdonly and have not been run against an adapter.compliance/is untouched — no row is re-scored here.Validation
openspec validate --specs --strict --no-interactive: 32 passed, 36 failed, identical tomain's totals.validate capture --type spec --strict --jsonreports no issue other than the pre-existing "requirement text is very long" warnings that every long requirement in the repo already carries.Created with PostHog Desktop
🤖 Generated with Claude Code