关于 natural-language
面向应用开发者的 Apple NaturalLanguage 与 Translation 框架技能指南,涵盖文本分析和应用内翻译,包括语言标注、嵌入及批量翻译。
主要功能
- 01词语、句子和段落分词
- 02语言识别与语言学标注
- 03命名实体和情感分析
- 04词语与句子嵌入
- 05应用内及批量翻译
适用场景
- 01拆分文本并识别语言
- 02提取名称或词性
- 03评估文本情感或语义相似度
- 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