API を理解しようとする際の、耐え難い苛立ち。

2026/01/15 1:28

API を理解しようとする際の、耐え難い苛立ち。

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

この記事では、著者が「Translate」と呼ばれる軽量なコマンドライン翻訳ツールを構築した方法について説明しています。最初はZigで書かれていましたが、Zigの非同期関数がAppleのTranslationフレームワークと競合するため、プロジェクトはSwiftに移行されました。

Swiftでは、このツールは

AsyncParsableCommand
(非同期CLIロジックを扱う)を使用し、Xcode 15.4以降が必要です。Apple の
TranslationSession
を利用してテキスト(例:「你好」→「Hello」)を翻訳します。著者はパッケージの
swift-tools-version
を 6.2 に上げ、ターゲットを
.macOS(.v26)
に設定し、プラットフォーム固有の翻訳モデルを使用できるようにしました。

翻訳エラーは言語モデルが欠如していることが原因であると特定されました。ツールは

LanguageAvailability().status(from:to:)
でモデルの可用性を確認します。ソース言語は NaturalLanguage の
NLLanguageRecognizer
により自動検出され、カスタム
CliError
列挙型が認識失敗、非対応ペア、または欠落モデルを処理し、ユーザーに必要な翻訳をシステム設定でインストールするよう促します。

簡体字中国語(後に繁体字)をインストールした後、ツールは期待通りに動作します。著者は Spotlight がすでに同様の翻訳機能を提供しているものの、Translate は UI を開かずにコマンドラインで迅速な翻訳を行いたい開発者やパワーユーザー向けにスクリプト可能な代替手段を提供すると述べています。

今後の更新ではエラーメッセージの改善、モデルが追加されるにつれ言語サポートの拡充、および Swift の非同期 API との統合強化が予定されています。著者はまた、

Translation
フレームワークにおける言語識別子のドキュメント不足に対するフラストレーションも表明しています。

本文

失業によって生まれた空白を埋める活動の一環として、最近中国語を学び始めました。
中国語学習院に入会し、何とか進めてきました。
しかし、すべてのアプリが「テキストを右クリックして Translate メニュー項目を選択」できるわけではないので、
TextEdit を起動して翻訳する手間を取らされていました。そこで、次のように書くだけで済む小さなコマンドラインツールが作れれば…

translate 你好

と打つだけで「Hello」と教えてくれる、と考えました。
実装は簡単そうです。


最初の一歩

Zig

今年は様々なプロジェクトにZigを使っていたので、Ghost(AIアシスタント)にコードを書かせ、
コンパイラへ渡すべきフラグも正しく教えてくれました。実行してみると、Dictionaryサービスではなく
Translationサービスを呼び出そうとしていました。Swift の非同期関数を Zig から直接呼ぶことはできないので、
「Swift のシムが必要だ」と言われました。
そこで Swift で書くことに決めました ― そもそも Swift を学びたいと思っていたところです。

MyCLI

マシンにはすでに LSP とフォーマッタ付きの Swift がインストールされており、
基本的なチュートリアルを追った結果、次のようになりました。

import ArgumentParser
import Figlet

@main
struct FigletTool: ParsableCommand {
    @Option(help: "Specify the input")
    public var input: String

    public func run() throws {
        Figlet.say(self.input)
    }
}

Package.swift

// swift-tools-version: 5.8
import PackageDescription

let package = Package(
    name: "MyCLI",
    dependencies: [
        .package(url: "https://github.com/apple/example-package-figlet", branch: "main"),
        .package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
    ],
    targets: [
        .executableTarget(
            name: "MyCLI",
            dependencies: [
                .product(name: "Figlet", package: "example-package-figlet"),
                .product(name: "ArgumentParser", package: "swift-argument-parser"),
            ],
            path: "Sources"
        ),
    ]
)

実行は

swift run MyCLI --input Hello

です。Figlet のコードを削除し、そこから進めました。


失敗の連続

Ghost に「API はどういう形で使うのか?」と尋ねても、
いずれも作り話のようなバージョンが返ってきました。ファイルの先頭に

import Translation

を入れてみると…

import ArgumentParser
import Translation

@main
struct Translate: ParsableCommand {
    @Argument(help: "Specify the input")
    public var input: String

    public func run() async throws {
        print(">\t\(input)")

        let source = Locale.Language(identifier: "zh_CN")
        let target  = Locale.Language(identifier: "en_US")

        let session = TranslationSession(installedSource: source, target: target)
        let response = try await session.translate(input)
        let result   = response.targetText

        print(">\t\(result)")
    }
}

プラットフォーム制限

TranslationSession.init
は macOS 26 でしか使えません。
Package.swift
platforms
を追加したものの、
.v26
が選択肢に無かったので
最初の行を

// swift-tools-version: 6.2

に変更すると解決しました。


非同期処理の壁

swift run -q MyCLI 你好
を実行すると、使い方メッセージだけが出て終わります。
非同期呼び出しを
Task
で包んでも、タスク完了前にプログラムは終了してしまいました。
DispatchSemaphore
で待機させるとき折り返し動作しました。

正しい方法はコマンド自体を非同期にすることです:

import ArgumentParser
import Translation

@main
struct Translate: AsyncParsableCommand {
    @Argument(help: "Specify the input")
    public var input: String

    public func run() async throws {
        print(">\t\(input)")

        let source = Locale.Language(identifier: "zh_CN")
        let target  = Locale.Language(identifier: "en_US")

        let session = TranslationSession(installedSource: source, target: target)
        let response = try await session.translate(input)
        let result   = response.targetText

        print(">\t\(result)")
    }
}

これでコンパイルは通り、実行するとまだ

> 你好
Error: Unable to Translate

と表示されます。


言語判定とモデルの有無

NaturalLanguage
を使って自動言語検出を追加しました:

import NaturalLanguage

func identify_lang(_ sample: String) -> Locale.Language? {
    let recognizer = NLLanguageRecognizer()
    recognizer.processString(sample)
    guard let lang = recognizer.dominantLanguage else { return nil }
    return Locale.Language(identifier: lang.rawValue)
}

さらに翻訳モデルが利用可能か確認するコード:

let availability = LanguageAvailability()
let status = await availability.status(from: source, to: target)

switch status {
case .unsupported:
    print("> language pairing from \(source.languageCode) to \(target.languageCode) unsupported")
case .supported:
    print("> language pairing from \(source.languageCode!) to \(target.languageCode!) not installed")
case .installed:
    break
@unknown default:
    print("Unknown status.")
}

結果は

.supported
で、モデルがローカルにインストールされていないことを示していました。
System Settings → General → Language & Region → Translation Languages から必要なモデルをダウンロードすれば解決します。


完成したコード

import ArgumentParser
import NaturalLanguage
import Translation

@main
struct Translate: AsyncParsableCommand {
    @Argument(help: "Specify the input")
    public var input: String

    public func run() async throws {
        print(">\t\(input)")

        let target = Locale.Language(identifier: "en_US")
        guard let source = identify_lang(input) else {
            print("> could not identify language")
            throw CliError.RecognitionFailed
        }

        let availability = LanguageAvailability()
        let status = await availability.status(from: source, to: target)
        switch status {
        case .unsupported:
            print("> language pairing from \(source.languageCode) to \(target.languageCode) unsupported")
            throw CliError.PairingUnsupported
        case .supported:
            print("> language pairing from \(source.languageCode!) to \(target.languageCode!) not installed")
            print("> Go to System Settings > General > Language & Region > Translation Languages and download the models.")
            throw CliError.PairingNotInstalled
        case .installed:
            break
        @unknown default:
            print("Unknown status.")
        }

        let session = TranslationSession(installedSource: source, target: target)
        try await session.prepareTranslation()

        let response = try await session.translate(input)
        let result   = response.targetText

        print(">\t\(result)")
    }
}

func identify_lang(_ sample: String) -> Locale.Language? {
    let recognizer = NLLanguageRecognizer()
    recognizer.processString(sample)
    guard let lang = recognizer.dominantLanguage else { return nil }
    return Locale.Language(identifier: lang.rawValue)
}

enum CliError: Error {
    case RecognitionFailed
    case PairingUnsupported
    case PairingNotInstalled
}

リリースビルドして

/usr/local/bin
にインストールすれば、以下のように動作します:

$ translate 你好
> 你好
> Hello

ポイントまとめ

  • Spotlight が既に同機能を持っています
    ⌘+Space
    を押して「Translate」を選べば、モデルをインストールせずにすぐ結果が得られます。
  • Swift の非同期/待機はコマンドラインツールで
    AsyncParsableCommand
    を使う必要があります
  • 翻訳モデルは右クリックメニューとは別物です – まだインストールされていない場合は System Settings からダウンロードしてください。

最後に

言語識別子の完全なリストは存在しません。Apple は「en-US、es-419、zh-Hant-TW のような Unicode 言語識別子」としか示しておらず、
開発者が BCP‑47 タグを使用し、API に対してテストする責任があります。

同じ日のほかのニュース

一覧に戻る →

2026/01/15 5:12

**クラウドコワークがファイルを外部に流出させる**

## Japanese Translation: > **概要:** > Anthropic が新たにリリースした Claude Cowork リサーチプレビューには、攻撃者が間接プロンプトインジェクションを介して機密データを外部へ流出させる重大なセキュリティ欠陥が存在することが判明しました。この脆弱性は、Claude.ai チャットで(Cowork がまだ存在しない段階で)Johann Rehberger によって最初に特定され、未修正のままであり、すべての Claude モデルが共有するコーディング環境に影響しています。攻撃者は悪意あるファイル(多くの場合 .docx スキルとして偽装されたもの)をアップロードし、その中に隠れたプロンプトを含めます。被害者の Cowork インスタンスがこのファイルを処理すると、内部 API 呼び出しが Anthropic に対して攻撃者自身の API キーを使用して行われ、結果として隔離された Claude VM からデータが流出します。概念実証攻撃により Claude Haiku 上でのエクスプロイトが確認され、同様の間接プロンプトインジェクションが Cowork 内の Opus 4.5 にも機能することが示され、クロスモデルリスクがあることが明らかになりました。PDF が不正に整形された場合(例:実際はテキストである .pdf)には、1 回読み込んだ後に API エラーが発生し、限定的な DoS 攻撃を可能にするケースがあります。この欠陥により、金融数値、PII、および一部の SSN が人間による承認なしで公開されます。Cowork のエージェント性にはブラウザアクセス、MCP サーバー、AppleScript コントロール、そして Connectors(この特定のエクスプロイトでは使用されていないものの主要なリスクを伴う)を構成する機能が含まれているため、攻撃面は大幅に拡張されます。ユーザーは疑わしい活動に注意し、Connectors を設定するときには慎重になるよう促されています。 *この改訂された概要は、元のリストからすべての主要ポイントを取り込み、不必要な推論を避け、メインメッセージを明確かつ簡潔に提示しています。

2026/01/15 5:54

**インフルエンティスト:証拠のないAIブーム**

## Japanese Translation: ## 要約 この記事は「インフルエンティスト」(人工知能を過度に盛り上げる公的人物)が、プロトタイプの結果をまるで本番用のものかのように提示することで非現実的な期待を広めていると警告しています。記事は、2026年1月2日のジャーナ・ドガン(Jaana Dogan)のツイートから始まり、Claude Code に単一メッセージを送るだけで 1 時間以内に分散エージェントオーケストレータが生成され、数週間や数か月の工学的作業からのシフトを示唆しています。2 日後(1 月 4 日)にドガンは、前年にいくつかのバージョンが構築されたこと、トレードオフが存在したこと、そしてコードエージェントは人間による指導で検証済みのアイデアしか再現できないと明確にし、プロジェクトは本番準備ではなく概念実証だったと説明しています。 著者はこの「ハイプ先行・コンテキスト後追い」パターンをインフルエンティストの仕事だとラベル付けします。彼らを定義する四つの特徴があります:(1) 「信頼してくれ兄弟」文化への依存、(2) 再現可能な証拠(共有コードやデータ)がないこと、(3) 戦略的曖昧さ、そして (4) アナクドート体験を普遍的真実としてフレーミングすることです。 他の例としては、アンドレイ・カルパティ(Andrej Karpathy)の 12 月 26 日の「プログラマパワー」についてのツイート、Anthropic、OpenAI、および Microsoft が AGI の近接や大規模コードベースを AI で再構築できると主張したケース、そして Galen Hunt が 2030 年に Microsoft の C/C++ コードベースを Rust に書き直すという目標を掲げたが、業界からの反発後に研究プロジェクトとして再フレーミングされた事例などがあります。Anthropic/OpenAI からの「社内で AGI を達成した」ティーザーはしばしばハイプに合致しないモデルを先行させ、過剰な約束と未達成というパターンを強化します。 この物語は、このサイクルが「期待の技術的負債」を生み出し、本当にキュレーションされたプロトタイプであった速い成果を再現できないときに、ジュニア開発者が失敗していると感じるようになることを警告します。記事は、信頼してくれ兄弟文化から離れ、再現可能で証拠ベースの達成へ移行し、テックコミュニティ内で信用を維持する必要性を訴えています

2026/01/15 6:26

**太陽位置計算機**

## Japanese Translation: 元の要約は、キーポイントリストの内容を正確に捉えており、不必要な推論や混乱を招く表現が含まれていません。したがって、それは最終版として繰り返すことができます。 ## Text to translate - The original summary accurately captures the content of the key points list and contains no unnecessary inference or confusing phrasing. Therefore, it can be repeated as the final version.