Frequently Asked Questions:
- Why didn't the Zig approach work with the Translation API? The macOS Translation API expects Swift async functions and types; calling those directly from Zig didn't work. The Ghost suggested a Swift shim, so the author switched to implementing the CLI in Swift instead.
- How do you call async translation code from a command-line entrypoint? Use AsyncParsableCommand (from swift-argument-parser) so your run() can be async and you can await TranslationSession.translate directly. Wrapping with Task and semaphores is fragile and unnecessary once AsyncParsableCommand is used.
- How do you autodetect the source language instead of hardcoding it? Use NaturalLanguage's NLLanguageRecognizer: process the input string, read recognizer.dominantLanguage, then convert that to a Locale.Language identifier for the TranslationSession.
- Why did translate return 'Unable to Translate' even though right-click translate worked? The Translation API requires local translation models for specific source/target pairings; the API's model set can differ from the right-click UI. You must install the models in System Settings > General > Language & Region > Translation Languages for the pairing to be 'installed.'
- What package and platform gotchas should I watch for? The top comment swift-tools-version: X in Package.swift controls available platform enums. Bumping to a newer tools version (e.g., 6.2) is required to reference macOS .v26. Also handle non-exhaustive enums with an @unknown default case when switching on LanguageAvailability statuses.
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:
- Zig attempt failed because Translation API requires Swift async calls (need Swift shim).
- Swift Package manifest: the swift-tools-version comment controls available platform enums like .v26.
- Async issues solved by using AsyncParsableCommand instead of wrapping Task + semaphores.
- Language autodetection via NLLanguageRecognizer is necessary for real-world input.
- Translation models must be installed in System Settings; API can report supported but not installed.
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.
