$npx -y skills add dpearson2699/swift-ios-skills --skill natural-languageTokenize, tag, and analyze natural language text using Apple's NaturalLanguage framework and translate between languages with the Translation framework. Use when adding language identification, sentiment analysis, named entity recognition, part-of-speech tagging, text embeddings,
| 1 | # NaturalLanguage + Translation |
| 2 | |
| 3 | Analyze natural language text for tokenization, part-of-speech tagging, named |
| 4 | entity recognition, sentiment analysis, language identification, and word/sentence |
| 5 | embeddings. Translate text between languages with the Translation framework. |
| 6 | |
| 7 | > This skill covers two related frameworks: **NaturalLanguage** (`NLTokenizer`, `NLTagger`, `NLEmbedding`) for on-device text analysis, and **Translation** (`TranslationSession`, `LanguageAvailability`) for language translation. |
| 8 | |
| 9 | **Scope boundary:** Use this skill after you already have text. It owns |
| 10 | tokenization, language identification, POS/NER tagging, sentiment, embeddings, |
| 11 | custom `NLModel` classifiers/taggers, and in-app translation. Hand off OCR to |
| 12 | `vision-framework`, speech-to-text to `speech-recognition`, UI strings and |
| 13 | locale formatting to `ios-localization`, and generative summarization or Apple |
| 14 | Intelligence workflows to `apple-on-device-ai`. |
| 15 | |
| 16 | ## Contents |
| 17 | |
| 18 | - [Setup](#setup) |
| 19 | - [Tokenization](#tokenization) |
| 20 | - [Language Identification](#language-identification) |
| 21 | - [Part-of-Speech Tagging](#part-of-speech-tagging) |
| 22 | - [Named Entity Recognition](#named-entity-recognition) |
| 23 | - [Sentiment Analysis](#sentiment-analysis) |
| 24 | - [Text Embeddings](#text-embeddings) |
| 25 | - [Translation](#translation) |
| 26 | - [Common Mistakes](#common-mistakes) |
| 27 | - [Review Checklist](#review-checklist) |
| 28 | - [References](#references) |
| 29 | |
| 30 | ## Setup |
| 31 | |
| 32 | Import `NaturalLanguage` for text analysis and `Translation` for language |
| 33 | translation. No special entitlements or capabilities are required for |
| 34 | NaturalLanguage. Translation has split availability: system translation |
| 35 | presentation is iOS 17.4+ / macOS 14.4+, while `TranslationSession`, |
| 36 | `.translationTask()`, `LanguageAvailability`, and batch translation require |
| 37 | iOS 18+ / macOS 15+. |
| 38 | Direct `TranslationSession(installedSource:target:)` is the non-UI option, but |
| 39 | only when the source and target languages are already installed on device. |
| 40 | |
| 41 | ```swift |
| 42 | import NaturalLanguage |
| 43 | import Translation |
| 44 | ``` |
| 45 | |
| 46 | NaturalLanguage classes (`NLTokenizer`, `NLTagger`) are **not thread-safe**. |
| 47 | Use each instance from one thread or dispatch queue at a time. |
| 48 | |
| 49 | ## Tokenization |
| 50 | |
| 51 | Segment text into words, sentences, or paragraphs with `NLTokenizer`. |
| 52 | |
| 53 | ```swift |
| 54 | import NaturalLanguage |
| 55 | |
| 56 | func tokenizeWords(in text: String) -> [String] { |
| 57 | let tokenizer = NLTokenizer(unit: .word) |
| 58 | tokenizer.string = text |
| 59 | |
| 60 | let range = text.startIndex..<text.endIndex |
| 61 | return tokenizer.tokens(for: range).map { String(text[$0]) } |
| 62 | } |
| 63 | ``` |
| 64 | |
| 65 | ### Token Units |
| 66 | |
| 67 | | Unit | Description | |
| 68 | |---|---| |
| 69 | | `.word` | Individual words | |
| 70 | | `.sentence` | Sentences | |
| 71 | | `.paragraph` | Paragraphs | |
| 72 | | `.document` | Entire document | |
| 73 | |
| 74 | ### Enumerating with Attributes |
| 75 | |
| 76 | Use `enumerateTokens(in:using:)` to detect numeric or emoji tokens. |
| 77 | |
| 78 | ```swift |
| 79 | let tokenizer = NLTokenizer(unit: .word) |
| 80 | tokenizer.string = text |
| 81 | |
| 82 | tokenizer.enumerateTokens(in: text.startIndex..<text.endIndex) { range, attributes in |
| 83 | if attributes.contains(.numeric) { |
| 84 | print("Number: \(text[range])") |
| 85 | } |
| 86 | return true // continue enumeration |
| 87 | } |
| 88 | ``` |
| 89 | |
| 90 | ## Language Identification |
| 91 | |
| 92 | Detect the dominant language of a string with `NLLanguageRecognizer`. |
| 93 | |
| 94 | ```swift |
| 95 | func detectLanguage(for text: String) -> NLLanguage? { |
| 96 | NLLanguageRecognizer.dominantLanguage(for: text) |
| 97 | } |
| 98 | |
| 99 | // Multiple hypotheses with confidence scores |
| 100 | func languageHypotheses(for text: String, max: Int = 5) -> [NLLanguage: Double] { |
| 101 | let recognizer = NLLanguageRecognizer() |
| 102 | recognizer.processString(text) |
| 103 | return recognizer.languageHypotheses(withMaximum: max) |
| 104 | } |
| 105 | ``` |
| 106 | |
| 107 | Constrain the recognizer to expected languages for better accuracy on short text. |
| 108 | |
| 109 | ```swift |
| 110 | let recognizer = NLLanguageRecognizer() |
| 111 | recognizer.languageConstraints = [.english, .french, .spanish] |
| 112 | recognizer.processString(text) |
| 113 | let detected = recognizer.dominantLanguage |
| 114 | ``` |
| 115 | |
| 116 | ## Part-of-Speech Tagging |
| 117 | |
| 118 | Identify nouns, verbs, adjectives, and other lexical classes with `NLTagger`. |
| 119 | |
| 120 | ```swift |
| 121 | func tagPartsOfSpeech(in text: String) -> [(String, NLTag)] { |
| 122 | let tagger = NLTagger(tagSchemes: [.lexicalClass]) |
| 123 | tagger.string = text |
| 124 | |
| 125 | var results: [(String, NLTag)] = [] |
| 126 | let range = text.startIndex..<text.endIndex |
| 127 | let options: NLTagger.Options = [.omitPunctuation, .omitWhitespace] |
| 128 | |
| 129 | tagger.enumerateTags(in: range, unit: .word, scheme: .lexicalClass, options: options) { tag, tokenRange in |
| 130 | if let tag { |
| 131 | results.append((String(text[tokenRange]), tag)) |
| 132 | } |
| 133 | return true |
| 134 | } |
| 135 | return results |
| 136 | } |
| 137 | ``` |
| 138 | |
| 139 | ### Common Tag Schemes |
| 140 | |
| 141 | | Scheme | Output | |
| 142 | |---|---| |
| 143 | | `.lexicalClass` | Part of speech (noun, verb, adjective) | |
| 144 | | `.nameType |