niacin 1.2 — now speaking French, German, Spanish, and Italian
niacin 1.2 is out, and it’s an entirely user-facing release: every string in the menu, the Settings window, and the About window now reads in your system language across French, German, Spanish, and Italian, alongside the existing English. The functional surface is unchanged — same MDM keys, same caffeinate wrapper, same menu bar behaviour — the app just stops looking out of place when macOS is set to Français.
Five locales, one catalog
The translations live in an Xcode 15+ Localizable.xcstrings String Catalog. 42 entries per locale, all marked translated, including pluralised duration strings (“1 minute” vs “5 minutes”) that previously lived as inline ternaries in Swift. Coverage was spot-checked by reading the compiled .lproj/Localizable.strings inside the built bundle to confirm each locale resolves the expected translation at runtime.
Why these four (and not the others, yet)
French, German, Spanish, and Italian were picked first because their plural rules are simple one / other — the same shape as English — so the catalog work is mostly typing rather than rule design. They cover a meaningful share of the European Mac install base without committing to layout or font work.
Japanese and Simplified Chinese are deliberately deferred to a separate pass. They benefit from a native review, and CJK strings have layout and font implications — particularly in the menu bar, where the countdown is space-constrained — that are worth handling on their own rather than bundling into a release that’s mostly about getting the infrastructure right. They’re tracked and on the roadmap.
Translator notes worth surfacing
- Proper nouns are preserved verbatim. “Niacin” and “GitHub” read the same in every locale.
- Compact countdown abbreviations stay symbolic. The menu bar countdown (
%lldh/%lldm/%llds/%lldh %lldm) is unchanged across locales. They’re widely understood, and translating them would cost menu bar real estate without adding clarity. The infinity glyph (∞) shown during indefinite sessions is universal too. - Positional placeholders for future flexibility. Strings with two arguments — “Awake until %@ · %@”, “Version %@ (%@)” — use
%1$@/%2$@so a future translator can reorder them if a language demands it. None of the current four needed to. - Natural rephrasing where literal would jar. The French “Niacin — il reste %@” intentionally mirrors the way native French expresses “X remaining”, rather than the word-for-word “%@ restant”.
- UK English source preserved through the translations. “Managed by Organisation” carries through as organisation (fr/de), organización (es), and organizzazione (it). UK vs US English in the source is a separate decision — not forking it now.
Logs stay in English — on purpose
One conscious omission: the os.Logger messages under the com.oldsalt.niacin subsystem are not localised and won’t be. IT teams troubleshooting an MDM deployment, especially across a fleet with mixed user locales, want grep-able log lines that look the same on every machine. Localising operational logs trades a marginal user benefit (admins rarely read their own logs) for real ops friction.
Under the hood: a couple of small SwiftUI gotchas
Phase 1 of the work was wiring the catalog without changing English-locale behaviour. Most of the SwiftUI surface needed nothing — Text("…"), Button("…", …), Section("…"), Toggle("…") already pick the LocalizedStringKey overload by default. Two things did need attention:
- Stored
Stringproperties bypass localisation.ManagedToggle.title,MenuManagedToggle.title, andPolicyRow.textwere typed asString, which meantText(title)andLabel(text, systemImage:)resolved to the verbatim init and skipped the catalog entirely. Switching them toLocalizedStringKeyfixed it without touching any call site —LocalizedStringKeyaccepts string literals and interpolations directly. - Runtime-built strings need
String(localized:). The tooltip text, the countdown formatter, the menu bar status label, and the About-window version line are all assembled at runtime from format strings. Routing them throughString(localized:)gets the format strings into the catalog and resolved per-locale at use.
Existing tests stay green — the English plural rules in the catalog produce the same “1 minute” / “5 minutes” / “1 hour” / “2 hours” output the tests assert on.
Upgrading
The Mac App Store update will roll out as soon as Apple’s review completes. From a signed GitHub release, replace the app and relaunch — managed policy and user preferences carry over untouched. There are no changes to the MDM key surface, and English-locale users will not see a single string change.
Source and release notes are on GitHub at github.com/just-an-oldsalt/niacin. If you spot a translation that reads awkwardly in your locale, please open an issue on the tracker or email niacin@dort.zone — native-speaker corrections are the most welcome bug reports we get.