Building a Translation CLI Took Forever 2026

Frequently Asked Questions:

Summary

What should have been a tiny command-line utility to translate a phrase became a multi-day debugging expedition. The author initially experimented with Zig, discovered macOS's hidden Translation API, and then learned Swift the hard way: package manifest quirks, unhelpful API docs, async entry-point traps, and language-model installation oddities. The solution combined language autodetection (NLLanguageRecognizer), checking LanguageAvailability, installing local translation models via System Settings, and switching to AsyncParsableCommand. The final working CLI demonstrates practical error handling and exposes surprising gaps between macOS UI conveniences (right-click Translate, Spotlight) and programmatic access. This is a pragmatic narrative for anyone building small tooling: expect surprises, read WWDC sessions, and test model availability before assuming a translation API will 'just work.'

Highlights:

The author set out to build a tiny translator: a CLI that runs translate 你好 and prints Hello. Initial attempts with Zig hit a hard limitation: the macOS Translation API expects Swift async functions, so Zig needed a Swift shim. Switching to Swift felt natural but uncovered package and tooling quirks—specifically that the swift-tools-version comment at the top of Package.swift determines which platform enum constants (like .v26) are available. Once the package manifest and dependencies were sorted, passing arguments and invoking the command-line entrypoint revealed more subtleties: ArgumentParser's run function was originally synchronous, and naive async wrapping with Task plus DispatchSemaphore either hung or produced inconsistent results.

The core async problem was resolved by using AsyncParsableCommand (from swift-argument-parser), which lets run be async so await calls behave correctly. Language autodetection was added using NaturalLanguage's NLLanguageRecognizer to avoid hardcoding source locales. However, the seemingly simple translate call still returned "Unable to Translate" with zero diagnostic help. Investigating the WWDC Translation API material revealed that you must check LanguageAvailability and prepareTranslation, and crucially that the translation models must be installed for the specific source/target pairing. The models exposed to the API are not always the same as the right-click Translate UI or Spotlight conveniences, so you may need to manually install models via System Settings > General > Language & Region > Translation Languages.

The final working solution combines ArgumentParser (AsyncParsableCommand), NaturalLanguage for detection, Translation API session creation, LanguageAvailability checks with an @unknown default arm, and prepareTranslation before translate. The author includes explicit error cases (recognition failed, pairing unsupported, pairing not installed) and shows sample usage: swift run -q MyCLI 你好 yields the expected Hello after installing the correct models (including different Mandarin variants). The narrative closes with a rueful note: Spotlight already handled this use case, and Swift/library documentation gaps made the journey longer than necessary. The takeaway: small tooling often surfaces platform and API rough edges—read WWDC material, validate model availability, and prefer documented async entry points.


Read Full Article