Monolingual: removing the language files macOS never uses
Remove unnecessary language resources from macOS.
At a glance
- What is it?
- Monolingual is a Swift application that deletes unneeded language localization files from a macOS install, and its 2.0 release had to give up the app sandbox to make that work at all. The project is a good example of how much of macOS housekeeping depends on a privileged helper and the exact registration mechanism the system allows.
- Who is it for?
- Adopt Monolingual if your macOS install carries a large set of language resources you never use and you want the space back with a visible, cancellable removal. Do not expect it to be a supported system tool, because it removes files macOS still owns and the upstream macOS version requirements are strict.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the app does, and the one file type it refuses to touch
Monolingual is a tool for removing unneeded language localization files for macOS, and the homepage is ingmarstein.github.io/Monolingual.
The operation is simple in principle. macOS ships application resources in every language it supports, a French and a German copy of the same strings, and on a machine that only uses English, all of them are dead weight. Monolingual finds those directories and deletes them.
The safety rule is stated in the 2.0 release notes and it is absolute: the English language files can no longer be selected for removal, because deleting them breaks a macOS installation beyond repair. That is a strong claim and a sensible one, since the English strings are the fallback that the system uses when it cannot find a localisation, and losing them is not a recoverable state.
The interface is a SwiftUI window with a language list and an architecture list, and 2.0.2 added a search field in the title bar that filters the list of the tab you are on. The release notes describe the behaviour precisely, down to the fact that the field holds the keyboard focus as the window opens, that a list matching nothing says so, that what is checked stays checked, and that a removal still covers the whole list whether or not it is on screen.
That last point is a small design decision with real consequences. The search narrows what you see, not what the removal acts on, so a filter cannot accidentally become a scope change.
The project is GPL-3.0 licensed, written in Swift 6, and the last push was on 2026-10-19 with the newest release 2.0.2 on 2026-10-10.
The sandbox and the helper, and why 2.0 could not remove anything
The 2.0.1 release notes contain the most instructive failure in the project, and they are worth reading as a case study in how macOS application registration works.
The symptom was that Monolingual could not remove files again. Version 2.0 asked macOS for something it does not allow, so the privileged helper that does the removing was never installed and the app could not free a single byte.
The cause: macOS 27 refuses to register an unsandboxed launch daemon from a sandboxed app, with the error SMAppService target executable must be sandboxed because the app is sandboxed. Monolingual was on both sides of that rule at once. The app had been sandboxed since 2015, and the helper has to run unconfined because it deletes files anywhere on disk. SMJobBless, which 2.0 replaced with SMAppService, had no such requirement, which is why the two coexisted for a decade without anyone noticing.
The fix was to give up the sandbox. The app is no longer sandboxed, and the 2.0 release notes explain the knock-on effect: preferences lived in the sandbox container, which an unsandboxed app cannot read, so the folders to thin fall back to /Applications and /Library. If you had added a folder of your own, you have to add it again under Preferences. Existing settings start over.
That is a significant trade for a system-level utility. Sandboxing is the mechanism that keeps a bug in the app from touching the rest of the disk, and giving it up is what makes the helper able to delete files. The 2.0 notes put it plainly: the helper is registered with Service Management instead of the deprecated SMJobBless, you approve it once under System Settings, General, Login Items and Extensions, and it verifies that the processes talking to it are Monolingual and no longer follows symbolic links.
The architecture is two parts, the app and the privileged helper, both written in Swift, communicating over XPC. An administrator has to allow the helper in the system settings before it can run.
A removal you can watch and stop
The 2.0 interface is built around a different assumption from the 1.x versions, and it is the right one for an operation that deletes system files.
A removal reports what it is doing in a sheet, showing the file it is working on and the space freed so far, and it can be cancelled. A notification tells you when it is done, with the space it saved. Before 2.0, the operation was presumably a batch job that ran and reported at the end.
The reasoning is straightforward. Deleting thousands of language resource directories is an operation you cannot undo, and the only way to make it safe is to make it observable and interruptible. The per-file progress and the running total of freed space are what tell you the operation is doing what you expected, and the cancel button is what stops a misclick from becoming an afternoon of restoring files.
The 2.0.1 notes also record a crash fix in the same area: thinning a very large universal binary no longer crashes, because the architecture offsets of a fat file are added in 64 bits, and a file whose slices would not fit a 32-bit header is now refused instead of trapping. The architecture list in the interface is what lets you target the slices you want to keep, and the bug was in the arithmetic behind it.
There is also a fix for a blocklist download that failed and left the blocklist empty, which crashed the next removal. That is a failure mode worth noting in this kind of tool, since a remote list that silently becomes empty and then causes damage is the sort of thing that should be handled defensively rather than optimistically.
Releases, signing, notarization and the update path
The release history here has a shape you rarely see in a small utility, and the 2.0 notes explain it.
The 2.0 release is the first since 1.8.2, and 1.8.2 dates from 2019-02-09, so there was a seven year gap between the two lines. 2.0.1 followed on 2026-10-02 and 2.0.2 on 2026-10-10, so the current line has been moving quickly since it restarted.
What 2.0 brought is worth listing because it is a rebuild rather than an increment. The application was rewritten in Swift 6 with a SwiftUI interface. Monolingual uses Sparkle 2 with EdDSA signed updates, and the app is notarized. Translations were reworked into a String Catalog so that no string can go stale, and Turkish was added with all 16 translations complete at that point, a number that reached 17 with Simplified Chinese in 2.0.2.
The 2.0.1 release also fixed the disk image install without a network. The app inside the image had no notarization ticket stapled to it, only the zip did, so Gatekeeper had to ask Apple about the app before it would run, which fails on a Mac that is offline or behind a restrictive network. The app is now stapled before the disk image is built.
That is a small detail with a large audience, because a network dependency in the installer means a portion of users simply cannot install the app at all, and the failure looks like a broken download rather than a missing ticket.
The dependency list is short. Sparkle handles the app updates, and swift-argument-parser is there for argument parsing for the bundled lipo tool, which is the command line utility for inspecting and manipulating the architecture slices of a fat binary. That lipo tool is the reason the architecture list in the interface exists, and it is why the fat binary arithmetic matters.
Building it, and the project layout
The build system is a Makefile that wraps shell scripts, and the targets are exactly what you would expect from a project that ships a signed, notarized disk image.
Building in Debug is make development, building in Release is make deployment, and release packaging, which covers signing, notarization and the disk image, is make release. The tests run with swift test, or with xcodebuild -scheme Helper -destination 'platform=macOS' test.
The Makefile itself is thin, which is the right shape for it. The default target is deployment, the development and deployment targets each run a clean first and then a build script with the configuration name, and release runs a separate script that takes the version from Info.plist, so you bump the version there before tagging, and it writes a release-<version> artefact. The clean target removes the build directory.
The repository layout reflects the two-part architecture. There is a Monolingual/ directory for the application and a Helper/ directory for the privileged helper, plus Sources/ and util/ for shared code, Resources/ for strings and images, and a lipo/ directory for the bundled tool. The packaging assets are at the top level, with dmg-bg.png, [email protected], dmg.js and make-diskimage.sh, alongside an appcast.xml.tmpl which is the template for the Sparkle update feed, and a release.markdown for the release notes.
There is also a Package.swift and a Package.resolved, since the project uses Swift Package Manager to manage its dependencies, and the resolved file is committed so that a build is reproducible. An AGENTS.md sits at the root, and a .swiftlint.yml and .swiftformat are checked in as the SwiftLint and SwiftFormat configurations.
One more detail from the developers section: the project is written in Swift 6 and requires macOS 27 on Apple silicon, with Xcode 27.0 or above to build it. Those are strict requirements, and they are the practical reason the 2.0 line could be rebuilt at all, since the modern macOS application lifecycle APIs are only available on recent systems.
Editorial conclusion
Adopt Monolingual if your macOS install carries a large set of language resources you never use and you want the space back with a visible, cancellable removal. Do not expect it to be a supported system tool, because it removes files macOS still owns and the upstream macOS version requirements are strict. Before you run a removal, read the interface notes about the English files being off limits, check the folder list under Preferences since 2.0.1 reset the saved settings, and note that the app is no longer sandboxed, which is a trade the maintainer made deliberately to make the helper work.
Frequently asked questions
What does Monolingual do on macOS?
It removes unneeded language localization files from a macOS installation, deleting the resource directories for languages the machine does not use. The English language files can no longer be selected for removal, because deleting them breaks a macOS installation beyond repair.
Why did Monolingual 2.0 stop being able to remove files?
macOS 27 refuses to register an unsandboxed launch daemon from a sandboxed app, so the privileged helper was never installed. The app had been sandboxed since 2015 while the helper has to run unconfined to delete files anywhere on disk. The fix in 2.0.1 was to remove the sandbox, which also reset the saved preferences.
How does Monolingual's privileged helper work?
The app and a privileged helper are both written in Swift and communicate over XPC. The helper is registered with Service Management using SMAppService rather than the deprecated SMJobBless, you approve it once under System Settings, General, Login Items and Extensions, and it verifies that the processes talking to it are Monolingual.
How do I build Monolingual from source?
Use make development for a debug build, make deployment for a release build and make release for signing, notarization and disk image packaging, where the version comes from Info.plist. Run the tests with swift test or xcodebuild -scheme Helper. The project needs Swift 6, macOS 27 on Apple silicon and Xcode 27.0 or above.
What are the dependencies used by Monolingual?
Two, managed with Swift Package Manager: Sparkle for app updates, using EdDSA signed updates, and swift-argument-parser for argument parsing in the bundled lipo tool, which inspects and manipulates the architecture slices of a fat binary.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ingmarstein-monolingual)