Skip to content

perf(packaging): strip unused Electron locale .lproj dirs (-36 MB) - #47

Merged
dbbaskette merged 1 commit into
mainfrom
perf/electron-en-only
Apr 25, 2026
Merged

perf(packaging): strip unused Electron locale .lproj dirs (-36 MB)#47
dbbaskette merged 1 commit into
mainfrom
perf/electron-en-only

Conversation

@dbbaskette

Copy link
Copy Markdown
Owner

Summary

  • Add an electron-builder afterPack hook (build/strip-electron-locales.cjs) that removes Chromium's unused .lproj strings dirs from the staged Electron Framework.framework. Keeps only en.lproj and en_GB.lproj.
  • electronLanguages doesn't reach these — they're macOS-style strings directories, not the per-locale .pak files electronLanguages filters.
  • Hook is .cjs because the package's "type": "module" would otherwise load it as ESM and fail on require/module.exports.

Why this is safe

  • Chromium falls back to en.lproj cleanly when a requested locale isn't present (Apple's standard missing-locale fallback chain).
  • Every user-facing string we render comes through Vite's bundle, not Chromium's strings files, so our UI is unaffected regardless of host language.
  • Hook is a no-op if the Resources/ path is missing (defensive).

Result on the latest local dist build

• stripped 53 unused .lproj dirs (36.4 MB)
.app:               751M -> 695M  (-56 MB)
Electron Framework: 223M -> 185M  (-38 MB)
dmg:                ~250 MB
zip:                ~244 MB

Test plan

  • npm run dist completes (exit 0)
  • Hook log line appears in build output
  • Resources/ contains only en.lproj and en_GB.lproj post-build
  • App is code-signed after the hook runs (afterPack runs pre-sign)
  • Smoke-launch the signed .app to confirm Electron starts and the renderer comes up
  • Briefly switch macOS preferred language to a non-English locale and confirm the app still renders (Chromium falls back to en.lproj)

🤖 Generated with Claude Code

Chromium's Electron Framework ships ~55 .lproj directories with
translated UI strings (menus, error pages, accessibility labels)
for every locale it supports. We're English-only, so all but
en.lproj/en_GB.lproj are dead weight inside the .app.

electronLanguages can't filter these — they're macOS-style strings
dirs, not the per-locale .pak files that flag targets. Add a small
afterPack hook that walks the framework's Resources/ and removes
non-English .lproj dirs after Electron is staged but before signing.

.cjs extension because the package's "type": "module" would
otherwise load the hook as ESM and fail on require/module.exports.

Result on the latest dist build:
  • stripped 53 unused .lproj dirs (36.4 MB)
  .app:               751M -> 695M  (-56 MB)
  Electron Framework: 223M -> 185M  (-38 MB)
  dmg:                ~250 MB
  zip:                ~244 MB

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dbbaskette
dbbaskette merged commit e581823 into main Apr 25, 2026
1 check passed
@dbbaskette
dbbaskette deleted the perf/electron-en-only branch April 25, 2026 05:01
dbbaskette added a commit that referenced this pull request Apr 25, 2026
…52)

Three categories of dead weight identified from a fresh inventory of
the post-#47 build, all verified safe via the existing /diarize smoke
on pyannote's bundled sample.wav.

Sidecar (PyInstaller post-build prune)
- strip **/*.pyi type stubs (4+ MB across torch / numpy / scipy)
- strip *.dist-info/{RECORD,WHEEL,INSTALLER,REQUESTED,top_level.txt}
  pip-bookkeeping files (torch's RECORD alone was 2.1 MB). LICENSE /
  NOTICE / AUTHORS files inside dist-info are kept — MIT/Apache-2/BSD
  require redistributing them.
- drop torch/bin/protoc + torch/bin/protoc-3.13.0.0 (~8 MB). Both
  are the protobuf compiler — a build-time codegen tool. Torch ships
  pre-generated message classes, so inference never invokes protoc.

Sidecar (additional --exclude-module entries)
- scipy.io._fast_matrix_market — Matrix Market reader, never called
- hf_xet — Xet large-file backend; HF falls back to plain HTTP
- uvloop — alternative asyncio loop; unused on our path

Tried but reverted (documented in build-bundle.sh):
- scipy.optimize._highspy — caught by /diarize. scipy.optimize.__init__
  unconditionally imports _linprog → _linprog_highs → _highspy via
  the lightning → torchmetrics → scipy.signal → scipy.interpolate
  chain. Restored.

app.asar
- Drop **/*.map, **/*.d.ts, **/*.d.ts.map from prod node_modules
  (the remark/mdast tree alone leaks 263 .map files). README,
  CHANGELOG, HISTORY also dropped — pure docs, no license clause
  requires redistributing them. LICENSE / NOTICE / AUTHORS kept.

Verification: /health 200, /diarize 200 with 13 segments + two
speakers on the bundled sample.wav.

Result vs post-#47 build:
  .app:  695M -> 669M  (-26 MB)
  dmg:   250M -> 242M  (-8 MB)
  zip:   244M -> 237M  (-7 MB)

Follow-ups filed: #48 (PyInstaller --strip), #49 (drop SwiftShader/
GLESv2 from Electron), #50 (compression: maximum).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant