Environment locale does not propagate into .sheet / .fullScreenCover content
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 330
- Forks
- 76
- Avg merge
- 3h 6m
- Merged PRs (30d)
- 1
Description
.environment(\.locale, …) set on an ancestor does not reach the content of a .sheet or .fullScreenCover on Android. The presented content resolves Text catalog keys — and reports @Environment(\.locale) — in the device locale instead.
It is specifically and only \.locale. Every other environment value crosses the presentation boundary correctly.
Reproduction
Device language en-US, locale overridden once at the root:
struct ContentView: View {
var body: some View {
RootView()
.environment(\.locale, Locale(identifier: "fr"))
.environment(\.probeKey, "set-at-root") // custom EnvironmentKey
.environment(\.layoutDirection, .rightToLeft)
.environment(\.timeZone, TimeZone(identifier: "Asia/Tokyo")!)
}
}
struct RootView: View {
@State var isSheetPresented = false
var body: some View {
NavigationStack {
List {
LocaleProbe(context: "inline")
NavigationLink("Push") { LocaleProbe(context: "navigationDestination") }
Button("Present a sheet") { isSheetPresented = true }
}
}
.sheet(isPresented: $isSheetPresented) {
LocaleProbe(context: "sheet")
}
}
}
struct LocaleProbe: View {
let context: String
@Environment(\.locale) var locale
@Environment(\.probeKey) var probeKey
@Environment(\.layoutDirection) var layoutDirection
@Environment(\.timeZone) var timeZone
var body: some View {
VStack(alignment: .leading) {
Text(verbatim: "locale: \(locale.identifier)")
Text("Welcome") // "Bienvenue" in fr, "Welcome" in the en base language
}
.task {
logger.info("context=\(context) locale=\(locale.identifier) customKey=\(probeKey) layoutDirection=\(layoutDirection) timeZone=\(timeZone.identifier)")
}
}
}
Measured on an Android emulator (API 36, device language en-US), Skip Lite:
| context | \.locale |
custom key | \.layoutDirection |
\.timeZone |
|---|---|---|---|---|
| inline | fr |
set-at-root |
rightToLeft |
Asia/Tokyo |
navigationDestination |
fr |
set-at-root |
rightToLeft |
Asia/Tokyo |
.sheet |
en_US |
set-at-root |
rightToLeft |
Asia/Tokyo |
.fullScreenCover |
en_US |
set-at-root |
rightToLeft |
Asia/Tokyo |
.sheet + .environment(\.locale, …) re-applied on its own content |
fr |
set-at-root |
rightToLeft |
Asia/Tokyo |
The Text("Welcome") in the sheet renders "Welcome"; inline it renders "Bienvenue".
Cause
EnvironmentValues.locale is the only environment value that is not stored in one of SkipUI's own composition locals. It reads and writes Compose's LocalConfiguration (Sources/SkipUI/SkipUI/Environment/EnvironmentValues.swift:481):
public var locale: Locale {
get { Locale(LocalConfiguration.current.locales[0]) }
set { /* copies LocalConfiguration.current, sets the locale, provides it */ }
}
.sheet and .fullScreenCover render through Material3's ModalBottomSheet, whose content composes in a separate Android window. Compose's ProvideAndroidCompositionLocals re-provides LocalConfiguration for that window from the platform, so the ancestor's overridden Configuration is replaced by the device one. SkipUI's own composition locals are unaffected, because they flow in through the parent composition context — which is why the custom key, layoutDirection, and timeZone all survive while \.locale does not.
Neither Presentation.swift nor PresentationRoot.swift resets the environment; they only add values. The loss happens entirely inside Compose, one layer below.
.alert is not affected — measured: an alert's message resolves against the presenter's locale both before and after the fix. Material3's ModalBottomSheet hosts its content in its own AbstractComposeView, whereas the Dialog an alert uses does not re-provide LocalConfiguration. So this is specific to the sheet/cover path. I did not measure menus or popovers.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in Sources/SkipUI/SkipUI/Environment/EnvironmentValues.swift around the locale implementation, then inspect Presentation.swift and PresentationRoot.swift and the sheet/fullScreenCover entry points. Trace how ModalBottomSheet provides its separate Android composition locals and compare the sheet and alert paths. Done means an overridden locale reaches sheet and fullScreenCover content without regressing custom environment values or alert behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, swift
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100