Skip to content

Commit 07f941b

Browse files
authored
Merge pull request #292 from RWS/DET-392-term-recognition-api
DET-827: Recognizing Terms in Text (term recognition API, Trados Studio 2026 CU1)
2 parents 8090d22 + b81812d commit 07f941b

2 files changed

Lines changed: 253 additions & 0 deletions

File tree

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
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)

‎apiconcepts/toc.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,8 @@
813813
href: terminology/creating_a_file_based_termbase.md
814814
- name: Managing Terms in a File-based Termbase
815815
href: terminology/managing_terms_in_a_file_based_termbase.md
816+
- name: Recognizing Terms in Text
817+
href: terminology/recognizing_terms.md
816818
- name: API Reference
817819
href: ../api/terminology/toc.yml
818820
- name: Release Notes

0 commit comments

Comments
 (0)