|
| 1 | +# Recognizing Terms in Text |
| 2 | + |
| 3 | +This article explains how to find the termbase terms that occur in a text, such as a source segment, with the same terms and scores as the **Term Recognition** window in **Var:ProductName**. |
| 4 | + |
| 5 | +Use term recognition when your plugin *consumes* terminology. For example: |
| 6 | + |
| 7 | +* A translation provider sends the recognized terms and their translations to a machine translation or AI service, so the translation uses the approved terminology. |
| 8 | +* A verification plugin checks that the translation uses the target terms of the source terms in the segment. |
| 9 | + |
| 10 | +> [!NOTE] |
| 11 | +> The term recognition API (`TerminologyRecognition`) is available from Trados Studio 2026 CU1. To support earlier versions as well, see [Supporting earlier versions](#supporting-earlier-versions). |
| 12 | +
|
| 13 | +## Searching versus recognizing terms |
| 14 | + |
| 15 | +The terminology API offers two ways to find terms. They answer different questions. |
| 16 | + |
| 17 | +| | `ITerminologyProvider.Search` | `TerminologyRecognition.RecognizeTerms` | |
| 18 | +|---|---|---| |
| 19 | +| Question it answers | Which entries in this termbase match this text? | Which terms occur in this text, and where? | |
| 20 | +| Comparable window | **Termbase Search** | **Term Recognition** | |
| 21 | +| Termbases | One termbase. | One or more termbases, in a search order. | |
| 22 | +| Score | Not meaningful for file-based termbases. Use `Search` to look up entries. | The score of each term against the part of the text it matches, as shown in the **Term Recognition** window. | |
| 23 | +| Inflected and compound forms | Returned as candidates, without a score. | Recognized and scored. For example, *USB cables* matches the term *USB cable*. | |
| 24 | +| Position in the text | Not returned. | Returned for each term. | |
| 25 | + |
| 26 | +If your plugin needs the terms of a segment, use `TerminologyRecognition`. Use `Search` to look up entries, for example in a search box. |
| 27 | + |
| 28 | +## Recognizing terms in a segment |
| 29 | + |
| 30 | +The term recognition types are in the `Sdl.Terminology.TerminologyProvider.Core.Recognition` namespace of the `Sdl.Terminology.TerminologyProvider.Core` assembly. |
| 31 | + |
| 32 | +1. Create a `TermRecognitionRequest` with the text, the source and target languages, and the URIs of the termbases to search. |
| 33 | +1. Call `RecognizeTerms` on a `TerminologyRecognition` instance. |
| 34 | + |
| 35 | +```cs |
| 36 | +using System; |
| 37 | +using System.Collections.Generic; |
| 38 | +using Sdl.Core.Globalization; |
| 39 | +using Sdl.Terminology.TerminologyProvider.Core; |
| 40 | +using Sdl.Terminology.TerminologyProvider.Core.Recognition; |
| 41 | + |
| 42 | +public List<RecognizedTerm> RecognizeTerms(string sourceText) |
| 43 | +{ |
| 44 | + var request = new TermRecognitionRequest |
| 45 | + { |
| 46 | + Text = sourceText, |
| 47 | + SourceLanguage = new DefinitionLanguage { Locale = new CultureCode("en-US") }, |
| 48 | + TargetLanguage = new DefinitionLanguage { Locale = new CultureCode("de-DE") } |
| 49 | + }; |
| 50 | + request.Termbases.Add(new Uri("ttb.file:///C:/Termbases/Printer.ttb")); |
| 51 | + |
| 52 | + return new TerminologyRecognition().RecognizeTerms(request); |
| 53 | +} |
| 54 | +``` |
| 55 | + |
| 56 | +For example, take this segment from *SamplePhotoPrinter.doc* and the *Printer* termbase, both from the **Var:ProductName** sample project: |
| 57 | + |
| 58 | +*When connecting power or USB cables, keep the cables clear of the paper path to the front and rear of the photo printer.* |
| 59 | + |
| 60 | +`RecognizeTerms` returns two terms, each with its position in the text and its termbase entry: |
| 61 | + |
| 62 | +* *photo printer*, an exact match with a score of 100. Its German translation is *Fotodrucker*. |
| 63 | +* *USB cable*, matched to the plural *USB cables* with a score below 100. Its German translation is *USB-Kabel*. |
| 64 | + |
| 65 | +The default settings of a request match the default termbase settings of a new project. You only need to set the text, the languages and the termbases. |
| 66 | + |
| 67 | +## Choosing the languages and termbase indexes |
| 68 | + |
| 69 | +A termbase stores each language in an *index*, such as *English* or *German*. `RecognizeTerms` finds the source and target index of each termbase from `SourceLanguage` and `TargetLanguage`. You pass these as `ILanguage` objects; `DefinitionLanguage` is a ready-made implementation. |
| 70 | + |
| 71 | +* **Locale**: the language of the text. `RecognizeTerms` looks for an index with the same culture, for example `de-DE`, and then for an index with the same language, for example `de`. |
| 72 | +* **Name** (optional): the name of a termbase index, for example *German*. When a termbase has an index with this name, it is used before the culture is matched. Set it when you follow the language index mapping of a project, as described in [Matching the project settings](#matching-the-project-settings). |
| 73 | + |
| 74 | +`RecognizeTerms` skips a termbase that has no index for the source language. When `TargetRequired` is `true`, it also skips a termbase that has no index for the target language. |
| 75 | + |
| 76 | +## Setting the recognition options |
| 77 | + |
| 78 | +All the options have defaults, so set only the ones you need. |
| 79 | + |
| 80 | +| Property | Default | Description | |
| 81 | +|---|---|---| |
| 82 | +| `MinimumMatchValue` | 70 | The minimum score, from 0 to 100, that a term must have to be recognized. | |
| 83 | +| `SearchDepth` | 200 | The maximum number of candidate terms each termbase returns before scoring. | |
| 84 | +| `SearchOrder` | `Sequential` | How several termbases are searched. `Hierarchical`: only the terms of the first termbase that returns results. `Sequential`: the terms of all termbases. `Parallel`: currently the same as `Sequential`. | |
| 85 | +| `AllowOverlappingTerms` | `false` | Whether recognized terms can overlap in the text, for example *power cable* and *AC power cable*. | |
| 86 | +| `RepetitionThreshold` | 10 | The maximum number of times the same term is recognized in the text. | |
| 87 | +| `EnableTwoLetterTermRecognition` | `false` | Whether terms of two letters are recognized. | |
| 88 | +| `MatchCase` | `false` | Whether the case of a term must match the text. | |
| 89 | +| `TargetRequired` | `false` | Whether only termbases with an index for the target language are searched, and only terms with a translation are returned. | |
| 90 | +| `IncludeHomonyms` | `true` | Whether terms with the same text from different entries or termbases are all returned. See [Homonyms and synonyms](#homonyms-and-synonyms). | |
| 91 | + |
| 92 | +## Reading the results |
| 93 | + |
| 94 | +Each `RecognizedTerm` contains: |
| 95 | + |
| 96 | +| Property | Description | |
| 97 | +|---|---| |
| 98 | +| `Text` | The term, as it is in the termbase. | |
| 99 | +| `MatchedText` | The part of the searched text that matches the term, for example *USB cables* for the term *USB cable*. | |
| 100 | +| `Score` | The score of the term against the matched text, from 0 to 100. This is the score shown in the **Term Recognition** window. | |
| 101 | +| `Positions` | The start (zero-based) and length of each place where the term occurs in the text. | |
| 102 | +| `Entry` | The termbase entry, with the terms and fields in all its languages. | |
| 103 | +| `EntryId`, `TermbaseUri`, `TermbaseName` | The entry and the termbase the term comes from. | |
| 104 | +| `SourceLanguage`, `TargetLanguage` | The termbase indexes that were used. `TargetLanguage` is `null` when the termbase has no index for the target language. | |
| 105 | + |
| 106 | +### Getting the translations |
| 107 | + |
| 108 | +The translations of a term are the terms of its entry in the target index: |
| 109 | + |
| 110 | +```cs |
| 111 | +using System.Linq; |
| 112 | + |
| 113 | +foreach (RecognizedTerm term in terms) |
| 114 | +{ |
| 115 | + EntryLanguage target = term.Entry?.Languages? |
| 116 | + .FirstOrDefault(language => language.Name == term.TargetLanguage?.Name); |
| 117 | + |
| 118 | + if (target is null) |
| 119 | + { |
| 120 | + // The entry has no translation in the target language. |
| 121 | + continue; |
| 122 | + } |
| 123 | + |
| 124 | + foreach (EntryTerm translation in target.Terms) |
| 125 | + { |
| 126 | + EntryField status = translation.Fields?.FirstOrDefault(field => field.Name == "Status"); |
| 127 | + Console.WriteLine($"{term.MatchedText} -> {translation.Value} ({status?.Value}, score {term.Score})"); |
| 128 | + } |
| 129 | +} |
| 130 | +``` |
| 131 | + |
| 132 | +The fields of a term, such as *Status*, depend on the termbase definition. Use them, for example, to prefer *Preferred* translations and to avoid *Forbidden* ones. |
| 133 | + |
| 134 | +### Homonyms and synonyms |
| 135 | + |
| 136 | +* **Synonyms** are several terms of the *same* entry, for example two translations of one concept. They are all in `target.Terms`. Prefer the one with the best status. |
| 137 | +* **Homonyms** are terms with the *same text* in *different* entries, for example *machine distributor* as a dealer and as an electrical distribution unit. `RecognizeTerms` returns one result for each entry. Tell them apart by `TermbaseUri` and `EntryId`, and choose by context. |
| 138 | + |
| 139 | +The same entry can be returned more than once, for example when its term occurs at several positions. To get one result per entry, group the results by `TermbaseUri` and `EntryId`. |
| 140 | + |
| 141 | +When you send terms to a machine translation or AI service, keep the entries apart, for example by numbering them. Otherwise, the service can't tell the translations of two meanings from two translations of one meaning. |
| 142 | + |
| 143 | +## Matching the project settings |
| 144 | + |
| 145 | +To get exactly the terms of the **Term Recognition** window, use the termbases and settings of the project. The project stores them in two places: |
| 146 | + |
| 147 | +* `TermbaseConfiguration`: the termbases, the language index mapping, and the main recognition options. |
| 148 | +* The `TermRecognitionSettings` settings group: the other recognition options. |
| 149 | + |
| 150 | +```cs |
| 151 | +using System; |
| 152 | +using System.Linq; |
| 153 | +using Sdl.Core.Globalization; |
| 154 | +using Sdl.Core.Settings; |
| 155 | +using Sdl.ProjectAutomation.Core; |
| 156 | +using Sdl.ProjectAutomation.FileBased; |
| 157 | +using Sdl.Terminology.TerminologyProvider.Core; |
| 158 | +using Sdl.Terminology.TerminologyProvider.Core.Recognition; |
| 159 | + |
| 160 | +public TermRecognitionRequest CreateRequest(FileBasedProject project, string sourceText, |
| 161 | + CultureCode sourceLanguage, CultureCode targetLanguage) |
| 162 | +{ |
| 163 | + TermbaseConfiguration configuration = project.GetTermbaseConfiguration(); |
| 164 | + TermRecognitionOptions options = configuration.TermRecognitionOptions; |
| 165 | + ISettingsGroup settings = project.GetSettings().GetSettingsGroup("TermRecognitionSettings"); |
| 166 | + |
| 167 | + var request = new TermRecognitionRequest |
| 168 | + { |
| 169 | + Text = sourceText, |
| 170 | + SourceLanguage = new DefinitionLanguage |
| 171 | + { |
| 172 | + Name = GetIndexName(configuration, sourceLanguage), |
| 173 | + Locale = sourceLanguage |
| 174 | + }, |
| 175 | + TargetLanguage = new DefinitionLanguage |
| 176 | + { |
| 177 | + Name = GetIndexName(configuration, targetLanguage), |
| 178 | + Locale = targetLanguage |
| 179 | + }, |
| 180 | + MinimumMatchValue = options.MinimumMatchValue, |
| 181 | + SearchDepth = options.SearchDepth, |
| 182 | + SearchOrder = (TermRecognitionSearchOrder)options.SearchOrder, |
| 183 | + AllowOverlappingTerms = GetSetting(settings, "AllowOverlappingTerms", false), |
| 184 | + RepetitionThreshold = GetSetting(settings, "RepetitionThreshold", TermRecognitionRequest.DefaultRepetitionThreshold), |
| 185 | + EnableTwoLetterTermRecognition = GetSetting(settings, "EnableTwoLetterTermRecognition", false), |
| 186 | + MatchCase = GetSetting(settings, "MatchCaseTerms", false) |
| 187 | + }; |
| 188 | + |
| 189 | + // The enabled termbases, in the project's order. |
| 190 | + foreach (LocalTermbase termbase in configuration.Termbases.OfType<LocalTermbase>().Where(termbase => termbase.Enabled)) |
| 191 | + { |
| 192 | + request.Termbases.Add(new Uri("ttb." + new Uri(termbase.FilePath).AbsoluteUri)); |
| 193 | + } |
| 194 | + |
| 195 | + return request; |
| 196 | +} |
| 197 | + |
| 198 | +// The termbase index that the project maps to a language, for example "German" for de-DE. |
| 199 | +private static string GetIndexName(TermbaseConfiguration configuration, CultureCode language) |
| 200 | +{ |
| 201 | + return configuration.LanguageIndexes |
| 202 | + .FirstOrDefault(index => string.Equals(index.ProjectLanguage.CultureInfo.Name, language.Name, StringComparison.OrdinalIgnoreCase))? |
| 203 | + .TermbaseIndex; |
| 204 | +} |
| 205 | + |
| 206 | +private static T GetSetting<T>(ISettingsGroup settings, string settingId, T defaultValue) |
| 207 | +{ |
| 208 | + return settings != null && settings.ContainsSetting(settingId) |
| 209 | + ? settings.GetSetting<T>(settingId).Value |
| 210 | + : defaultValue; |
| 211 | +} |
| 212 | +``` |
| 213 | + |
| 214 | +The project's language index mapping applies to all its termbases, as in the **Term Recognition** window. If a termbase has no index with the mapped name, `RecognizeTerms` matches the index by language. |
| 215 | + |
| 216 | +> [!NOTE] |
| 217 | +> The example adds the file-based termbases of the project. The project settings describe a server-based termbase by its server and name, not by a termbase URI. For a server-based termbase, use the URI of its terminology provider, for example `ITerminologyProvider.Uri`. |
| 218 | +
|
| 219 | +In a plugin that runs in **Var:ProductName**, get the current project from the `ProjectsController`: |
| 220 | + |
| 221 | +```cs |
| 222 | +FileBasedProject project = SdlTradosStudio.Application.GetController<ProjectsController>().CurrentProject; |
| 223 | +``` |
| 224 | + |
| 225 | +## Performance and behavior |
| 226 | + |
| 227 | +* `RecognizeTerms` searches the termbases and scores the candidates each time you call it. Call it once per segment, and reuse the results. |
| 228 | +* Calls are processed one at a time. Parallel calls, for example from a batch task, wait for each other. |
| 229 | +* Read the project settings once, for example per batch or per document, rather than for each segment. |
| 230 | +* A termbase that can't be opened is logged and skipped, so the other termbases still return their terms. |
| 231 | +* An empty text returns no terms. A `null` request throws an `ArgumentNullException`. |
| 232 | + |
| 233 | +## Supporting earlier versions |
| 234 | + |
| 235 | +`TerminologyRecognition` is available from Trados Studio 2026 CU1. You have two options: |
| 236 | + |
| 237 | +* **Require Trados Studio 2026 CU1.** Set the minimum version in your plugin manifest, for example `<RequiredProduct name="TradosStudio" minversion="19.0.1" maxversion="19.0.9"/>`. |
| 238 | +* **Support earlier versions as well.** Check at run time whether the API is there, and use another way to find terms, such as `ITerminologyProvider.Search`, when it isn't: |
| 239 | + |
| 240 | +```cs |
| 241 | +private static readonly bool IsTermRecognitionAvailable = |
| 242 | + typeof(ITerminologyProviderManager).Assembly |
| 243 | + .GetType("Sdl.Terminology.TerminologyProvider.Core.Recognition.TerminologyRecognition") != null; |
| 244 | +``` |
| 245 | + |
| 246 | +Keep all the code that uses the term recognition types in a separate class, and only use that class when `IsTermRecognitionAvailable` is `true`. On an earlier version these types don't exist, so .NET fails when it compiles a method that uses them, even if that code never runs. |
| 247 | + |
| 248 | +## See Also |
| 249 | + |
| 250 | +* [Searching Terms](searching_terms.md) |
| 251 | +* [Managing Terms in a File-based Termbase](managing_terms_in_a_file_based_termbase.md) |
0 commit comments