mcpField
N
natural-language

natural-language

github.com/dpearson2699开发1.2k3.7k 次安装13 周前更新
代码仓库

关于 natural-language

面向应用开发者的 Apple NaturalLanguage 与 Translation 框架技能指南,涵盖文本分析和应用内翻译,包括语言标注、嵌入及批量翻译。

主要功能

  1. 01词语、句子和段落分词
  2. 02语言识别与语言学标注
  3. 03命名实体和情感分析
  4. 04词语与句子嵌入
  5. 05应用内及批量翻译

适用场景

  1. 01拆分文本并识别语言
  2. 02提取名称或词性
  3. 03评估文本情感或语义相似度
  4. 04在应用中翻译文本

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dpearson2699/swift-ios-skills --skill natural-language

安装后在 Claude Code 中输入/natural-language确认已加载。

附带文件evals/evals.jsonreferences/translation-patterns.md

详细介绍

NaturalLanguage + Translation

使用 Translation 框架分析自然语言文本,执行分词、词性标注、命名实体识别、情感分析、语言识别,以及词语和句子嵌入。还可在不同语言之间翻译文本。

此技能涵盖两个相关框架:NaturalLanguage(NLTokenizer、NLTagger、NLEmbedding),用于在设备端分析文本;以及 Translation(TranslationSession、LanguageAvailability),用于语言翻译。

范围界定: 在已有文本后使用此技能。它负责分词、语言识别、词性/命名实体标注、情感分析、嵌入、自定义 NLModel 分类器/标注器,以及应用内翻译。OCR 请交由 vision-framework,语音转文本请交由 speech-recognition,UI 字符串和区域格式化请交由 ios-localization,生成式摘要或 Apple Intelligence 工作流请交由 apple-on-device-ai。

目录

设置

导入 NaturalLanguage 用于文本分析,导入 Translation 用于语言翻译。NaturalLanguage 无需特殊授权或功能。Translation 的可用性分为两部分:系统翻译界面支持 iOS 17.4+ / macOS 14.4+,而 TranslationSession、.translationTask()、LanguageAvailability 和批量翻译需要 iOS 18+ / macOS 15+。 直接使用 TranslationSession(installedSource:target:) 是非 UI 选项,但仅适用于源语言和目标语言已安装在设备上的情况。

import NaturalLanguage
import Translation

NaturalLanguage 类(NLTokenizer、NLTagger)不是线程安全的。每次仅在一个线程或一个调度队列中使用每个实例。

分词

使用 NLTokenizer 将文本划分为单词、句子或段落。

import NaturalLanguage

func tokenizeWords(in text: String) -> [String] {
    let tokenizer = NLTokenizer(unit: .word)
    tokenizer.string = text

    let range = text.startIndex..<text.endIndex
    return tokenizer.tokens(for: range).map { String(text[$0]) }
}
分词单位
单位说明
.word单个单词
.sentence句子
.paragraph段落
.document整篇文档
使用属性进行枚举

使用 enumerateTokens(in:using:) 检测数字或表情符号词元。

let tokenizer = NLTokenizer(unit: .word)
tokenizer.string = text

tokenizer.enumerateTokens(in: text.startIndex..<text.endIndex) { range, attributes in
    if attributes.contains(.numeric) {
        print("Number: \(text[range])")
    }
    return true // continue enumeration
}

语言识别

使用 NLLanguageRecognizer 检测字符串的主要语言。

func detectLanguage(for text: String) -> NLLanguage? {
    NLLanguageRecognizer.dominantLanguage(for: text)
}

// Multiple hypotheses with confidence scores
func languageHypotheses(for text: String, max: Int = 5) -> [NLLanguage: Double] {
    let recognizer = NLLanguageRecognizer()
    recognizer.processString(text)
    return recognizer.languageHypotheses(withMaximum: max)
}

将识别器限制为预期语言,可提高短文本的识别准确度。

let recognizer = NLLanguageRecognizer()
recognizer.languageConstraints = [.english, .french, .spanish]
recognizer.processString(text)
let detected = recognizer.dominantLanguage

词性标注

使用 NLTagger 识别名词、动词、形容词及其他词类。

func tagPartsOfSpeech(in text: String) -> [(String, NLTag)] {
    let tagger = NLTagger(tagSchemes: [.lexicalClass])
    tagger.string = text

    var results: [(String, NLTag)] = []
    let range = text.startIndex..<text.endIndex
    let options: NLTagger.Options = [.omitPunctuation, .omitWhitespace]

    tagger.enumerateTags(in: range, unit: .word, scheme: .lexicalClass, options: options) { tag, tokenRange in
        if let tag {
            results.append((String(text[tokenRange]), tag))
        }
        return true
    }
    return results
}
常见标注方案
方案输出
.lexicalClass词性(名词、动词、形容词)
.nameType命名实体类型(人物、地点、组织)
.nameTypeOrLexicalClass组合式命名实体识别 + 词性
.lemma单词的基本形式
.language每个词元的语言
.sentimentScore情感极性分数

命名实体识别

提取人物、地点和组织。

func extractEntities(from text: String) -> [(String, NLTag)] {
    let tagger = NLTagger(tagSchemes: [.nameType])
    tagger.string = text

    var entities: [(String, NLTag)] = []
    let options: NLTagger.Options = [.omitPunctuation, .omitWhitespace, .joinNames]

    tagger.enumerateTags(
        in: text.startIndex..<text.endIndex,
        unit: .word,
        scheme: .nameType,
        options: options
    ) { tag, tokenRange in
        if let tag, tag != .other {
            entities.append((String(text[tokenRange]), tag))
        }
        return true
    }
    return entities
}
// NLTag values: .personalName, .placeName, .organizationName

情感分析

为文本情感打分,范围从 -1.0(负面)到 +1.0(正面)。

func sentimentScore(for text: String) -> Double? {
    let tagger = NLTagger(tagSchemes: [.sentimentScore])
    tagger.string = text

    let (tag, _) = tagger.tag(
        at: text.startIndex,
        unit: .paragraph,
        scheme: .sentimentScore
    )
    return tag.flatMap { Double($0.rawValue) }
}

文本嵌入

使用 NLEmbedding 衡量单词或句子之间的语义相似度。

func wordSimilarity(_ word1: String, _ word2: String) -> Double? {
    guard let embedding = NLEmbedding.wordEmbedding(for: .english) else { return nil }
    return embedding.distance(between: word1, and: word2, distanceType: .cosine)
}

func findSimilarWords(to word: String, count: Int = 5) -> [(String, Double)] {
    guard let embedding = NLEmbedding.wordEmbedding(for: .english) else { return [] }
    return embedding.neighbors(for: word, maximumCount: count, distanceType: .cosine)
}

句子嵌入用于比较完整句子。

func sentenceSimilarity(_ s1: String, _ s2: String) -> Double? {
    guard let embedding = NLEmbedding.sentenceEmbedding(for: .english) else { return nil }
    return embedding.distance(between: s1, and: s2, distanceType: .cosine)
}

翻译

系统翻译浮层

使用 .translationPresentation() 显示内置翻译界面。

import SwiftUI
import Translation

struct TranslatableView: View {
    @State private var showTranslation = false
    let text = "Hello, how are you?"

    var body: some View {
        Button { showTranslation = true } label: {
            Text(text)
        }
        .buttonStyle(.plain)
        .translationPresentation(
            isPresented: $showTranslation,
            text: text
        )
    }
}
编程式翻译

在视图上下文中使用 .translationTask() 执行编程式翻译。

struct TranslatingView: View {
    @State private var translatedText = ""
    @State private var translationErrorMessage: String?
    @State private var configuration: TranslationSession.Configuration?

    var body: some View {
        VStack {
            Text(translatedText)
            Button("Translate") {
                configuration = .init(source: Locale.Language(identifier: "en"),
                                      target: Locale.Language(identifier: "es"))
            }
        }
        .translationTask(configuration) { session in
            do {
                let response = try await session.translate("Hello, world!")
                await MainActor.run {
                    translatedText = response.targetText
                    translationErrorMessage = nil
                }
            } catch {
                let message = error.localizedDescription
                await MainActor.run {
                    translationErrorMessage = message
                }
            }
        }
    }
}
批量翻译

在单个会话中翻译多个字符串。

.translationTask(configuration) { session in
    do {
        let requests = texts.enumerated().map { index, text in
            TranslationSession.Request(sourceText: text,
                                       clientIdentifier: "\(index)")
        }
        let responses = try await session.translations(from: requests)
        for response in responses {
            print("\(response.sourceText) -> \(response.targetText)")
        }
    } catch {
        // Handle cancellation, unsupported languages, or download refusal.
    }
}
检查语言可用性
let availability = LanguageAvailability()
let status = await availability.status(
    from: Locale.Language(identifier: "en"),
    to: Locale.Language(identifier: "ja")
)
switch status {
case .installed: break    // Ready to translate offline
case .supported: break    // Needs download
case .unsupported: break  // Language pair not available
}

常见错误

不要:跨线程共享 NLTagger/NLTokenizer

这些类不是线程安全的,会产生错误结果或导致崩溃。

// WRONG
let sharedTagger = NLTagger(tagSchemes: [.lexicalClass])
DispatchQueue.concurrentPerform(iterations: 10) { _ in
    sharedTagger.string = someText  // Data race
}

// CORRECT
await withTaskGroup(of: Void.self) { group in
    for _ in 0..<10 {
        group.addTask {
            let tagger = NLTagger(tagSchemes: [.lexicalClass])
            tagger.string = someText
            // process...
        }
    }
}
不要:混淆 NaturalLanguage 与 Core ML

NaturalLanguage 提供内置的语言分析功能。自定义训练模型请使用 Core ML。它们可通过 NLModel 相互补充。

// WRONG: Trying to do NER with raw Core ML
let coreMLModel = try MLModel(contentsOf: modelURL)

// CORRECT: Use NLTagger for built-in NER
let tagger = NLTagger(tagSchemes: [.nameType])

// Or load a custom Core ML model via NLModel
let nlModel = try NLModel(mlModel: coreMLModel)
tagger.setModels([nlModel], forTagScheme: .nameType)
不要:假设所有语言都有嵌入

并非所有语言的词嵌入或句子嵌入都可在设备上使用。

// WRONG: Force unwrap
let embedding = NLEmbedding.wordEmbedding(for: .japanese)!

// CORRECT: Handle nil
guard let embedding = NLEmbedding.wordEmbedding(for: .japanese) else {
    // Embedding not available for this language
    return
}
不要:为每个词元创建新的标注器

创建和配置标注器的开销很大。处理同一段文本时应重复使用。

// WRONG: New tagger per word
for word in words {
    let tagger = NLTagger(tagSchemes: [.lexicalClass])
    tagger.string = word
}

// CORRECT: Set string once, enumerate
let tagger = NLTagger(tagSchemes: [.lexicalClass])
tagger.string = fullText
tagger.enumerateTags(in: fullText.startIndex..<fullText.endIndex,
                     unit: .word, scheme: .lexicalClass, options: []) { tag, range in
    return true
}
不要:忽略短文本的语言提示

短字符串(少于约 20 个字符)的语言检测结果并不可靠。设置约束或提示可提高准确性。

// WRONG: Detect language of a single word
let lang = NLLanguageRecognizer.dominantLanguage(for: "chat")  // French or English?

// CORRECT: Provide context
let recognizer = NLLanguageRecognizer()
recognizer.languageHints = [.english: 0.8, .french: 0.2]
recognizer.processString("chat")

审查清单

  • NLTokenizer 和 NLTagger 实例仅在单个线程中使用
  • 每段文本只创建一次标注器,而不是为每个词元创建
  • 对短文本进行语言检测时使用约束/提示
  • 使用前检查 NLEmbedding 是否可用(不可用时会返回 nil)
  • 尝试翻译前检查 LanguageAvailability
  • 在 SwiftUI 视图层级中使用 .translationTask()
  • 批量翻译使用 clientIdentifier,将响应与请求对应起来
  • 将情感分数视为可选值处理(不支持的语言可能返回 nil)
  • 使用 NER 时使用 .joinNames 选项,将多词姓名保留在一起
  • 通过 NLModel 加载自定义 ML 模型,而非直接使用 Core ML

参考资料

评论

支持 Markdown