Skip to content

Electron.app copy missing .lproj folders forces English UI on first launch (macOS) #52

Description

@EurFelux

Symptom

On a macOS system whose first preferred language is Simplified Chinese (AppleLanguages = [zh-Hans-CN, en-CN, ...]), a fresh dev instance (clean userData, no stored language preference) launches with an English UI.

Root cause (verified 2026-06-07)

macOS resolves an app's preferred language as system language list ∩ localizations declared by the app bundle (.lproj folders in Contents/Resources). When the bundle declares zero .lproj folders, macOS decides the app only supports its development language and prepends en-US to the app's language list — even though the user's raw AppleLanguages contains no English variant at all. app.getLocale() then returns en-US, and matchSystemLanguage("en-US") → "en".

Evidence chain:

Copy .lproj count Result
Official electron-v41.7.1-darwin-arm64.zip (cache) 495 entries upstream is fine
Main repo node_modules/electron/dist/Electron.app 55 fine → dev + packaged releases show Chinese correctly
Worktree node_modules/electron/dist/Electron.app 0 broken → English first launch

.lproj folders are empty directories; some unzip/copy step in this worktree's install dropped all empty directories. Per-app language overrides and userData Local State were ruled out (both empty). The app-side language chain (app.getLocale() → sync IPC → resolveInitialLanguage) works as designed at every step.

Why this matters beyond one broken worktree

  • Any environment that loses the empty .lproj dirs (fresh clone, CI, another worktree) silently gets English-only first launch in dev.
  • If a release is ever packaged from such an environment, every non-English macOS user gets an English app. No app-side defense is possible: the injected en-US is indistinguishable from a genuine user preference.

Suggested direction

  1. Find which step drops empty directories for the worktree copy (electron's postinstall install.js unzip vs pnpm hoisted copy) and fix or work around it.
  2. Add a packaging-time guard: assert Contents/Resources/*.lproj count > 0 in the package verification step (alongside the existing native-module smoke), so a poisoned build can never ship.

Source: user-confirmed investigation 2026-06-07 (smoke testing #29 surfaced the symptom)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:buildPackaging, release, build infrabugSomething isn't working

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions