Skip to content

Commit

Permalink
Allow to automatically determine an existing glossary for DeepL (#124)
Browse files Browse the repository at this point in the history
* allow to automatically determine an existing glossary for DeepL

* pass parameter
  • Loading branch information
fjaeger authored Sep 12, 2024
1 parent 11a3b07 commit e4e4008
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 122 deletions.
39 changes: 22 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,10 @@ Azure account if you don't have one already and
You'll get an API key soon after that which you can pass to json-autotranslate
using the `-c` or `--config` flag.

Unless you configure a global translator instance you will need to provide a region by adding it to the config string after the API
key, separated by a comma: `--config apiKey,region`. As of this version, the
following regions are available:
Unless you configure a global translator instance you will need to provide a
region by adding it to the config string after the API key, separated by a
comma: `--config apiKey,region`. As of this version, the following regions are
available:

> australiaeast, brazilsouth, canadacentral, centralindia, centralus,
> centraluseuap, eastasia, eastus, eastus2, francecentral, japaneast, japanwest,
Expand Down Expand Up @@ -298,20 +299,24 @@ available matchers.

```
Options:
-i, --input <inputDir> the directory containing language directories (default: ".")
-l, --source-language <sourceLang> specify the source language (default: "en")
-t, --type <key-based|natural|auto> specify the file structure type (default: "auto")
-s, --service <service> selects the service to be used for translation (default: "google-translate")
--list-services outputs a list of available services
-m, --matcher <matcher> selects the matcher to be used for interpolations (default: "icu")
-a, --with-arrays enables support for arrays in files, but removes support for keys named 0, 1, 2, etc.
--list-matchers outputs a list of available matchers
-c, --config <value> supply a config parameter (e.g. path to key file) to the translation service
-f, --fix-inconsistencies automatically fixes inconsistent key-value pairs by setting the value to the key
-d, --delete-unused-strings deletes strings in translation files that don't exist in the template
-h, --help output usage information
--directory-structure <default|ngx-translate> the locale directory structure (default: "default")
--decode-escapes decodes escaped HTML entities like &#39; into normal UTF-8 characters
-i, --input <inputDir> the directory containing language directories (default: ".")
--cache <cacheDir> set the cache directory (default: ".json-autotranslate-cache")
-l, --source-language <sourceLang> specify the source language (default: "en")
-t, --type <key-based|natural|auto> specify the file structure type (default: "auto")
-a, --with-arrays enables support for arrays in files, but removes support for keys named 0, 1, 2, etc.
-s, --service <service> selects the service to be used for translation (default: "google-translate")
-g, --glossaries [glossariesDir] set the glossaries folder to be used by DeepL. Keep empty for automatic determination of matching glossary
-a, --appName <appName> specify the name of your app to distinguish DeepL glossaries (if sharing an API key between multiple projects) (default: "json-autotranslate")
--context <context> set the context that is used by DeepL for translations
--list-services outputs a list of available services
-m, --matcher <matcher> selects the matcher to be used for interpolations (default: "icu")
--list-matchers outputs a list of available matchers
-c, --config <value> supply a config parameter (e.g. path to key file) to the translation service
-f, --fix-inconsistencies automatically fixes inconsistent key-value pairs by setting the value to the key
-d, --delete-unused-strings deletes strings in translation files that don't exist in the template
--directory-structure <default|ngx-translate> the locale directory structure
--decode-escapes decodes escaped HTML entities like &#39; into normal UTF-8 characters
-h, --help display help for command
```

## Contributing
Expand Down
6 changes: 3 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ commander
'google-translate',
)
.option(
'-g, --glossaries <glossariesDir>',
`set the glossaries folder to be used by DeepL`,
'-g, --glossaries [glossariesDir]',
`set the glossaries folder to be used by DeepL. Keep empty for automatic determination of matching glossary`,
)
.option(
'-a, --appName <appName>',
Expand Down Expand Up @@ -109,7 +109,7 @@ const translate = async (
matcher: keyof typeof matcherMap = 'icu',
decodeEscapes = false,
config?: string,
glossariesDir?: string,
glossariesDir?: string | boolean,
appName?: string,
context?: string,
) => {
Expand Down
63 changes: 30 additions & 33 deletions src/services/deepl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class DeepL implements TranslationService {
public name: string;
private apiEndpoint: string;
private glossariesDir: string;
private automaticGlossary: boolean;
private appName: string;
private context: string;
private apiKey: string;
Expand Down Expand Up @@ -45,7 +46,7 @@ export class DeepL implements TranslationService {
config?: string,
interpolationMatcher?: Matcher,
decodeEscapes?: boolean,
glossariesDir?: string,
glossariesDir?: string | boolean,
appName?: string,
context?: string,
) {
Expand All @@ -62,7 +63,9 @@ export class DeepL implements TranslationService {
this.supportedLanguages = this.formatLanguages(languages);
this.formalityLanguages = this.getFormalityLanguages(languages);
this.decodeEscapes = decodeEscapes;
this.glossariesDir = glossariesDir;
this.glossariesDir =
typeof glossariesDir === 'string' ? glossariesDir : undefined;
this.automaticGlossary = glossariesDir === true;
this.appName = appName;
this.context = context;
}
Expand Down Expand Up @@ -138,28 +141,6 @@ export class DeepL implements TranslationService {
return responses;
}

/**
* Delete the existing glossary and re-create it. (DeepL does not allow editing glossaries.)
*/
async recreateGlossary(from: string, to: string) {
// Delete the existing glossary if it exists:
const allGlossaries = await this.listGlossaries();
const glossary = allGlossaries
.filter((g) => g.name === this.appName) // Only of this app.
.find(
(g) =>
g.source_lang === from.toLowerCase() &&
g.target_lang === to.toLowerCase(), // Only of this translation.
);
if (glossary) {
await this.deleteGlossary(glossary.glossary_id);
}
// Add the glossary:
const filePath = path.join(this.glossariesDir, `${from}-${to}.json`);
const response = await this.createGlossaryFromFile(filePath);
return response;
}

/**
* Delete a glossary.
*/
Expand Down Expand Up @@ -247,14 +228,26 @@ export class DeepL implements TranslationService {
return glossary as DeepLGlossary;
}

async getGlossary(from: string, to: string) {
const glossary = await this.recreateGlossary(from, to);
if (!glossary) {
return null;
}
if (!glossary.ready) {
throw new Error(`${from} -> ${to} glossary is not ready yet.`);
async getGlossary(from: string, to: string, recreate: boolean) {
const allGlossaries = await this.listGlossaries();
let glossary = allGlossaries
.filter((g) => (!!this.appName ? g.name === this.appName : true)) // Only of this app, if defined
.find(
(g) =>
g.source_lang === from.toLowerCase() &&
g.target_lang === to.toLowerCase(), // Only of this translation.
);

if (recreate) {
if (glossary) {
await this.deleteGlossary(glossary.glossary_id);
}

// Add the glossary:
const filePath = path.join(this.glossariesDir, `${from}-${to}.json`);
glossary = await this.createGlossaryFromFile(filePath);
}

return glossary;
}

Expand All @@ -281,9 +274,13 @@ export class DeepL implements TranslationService {
};

// Should a glossary be used?
if (this.glossariesDir) {
if (this.glossariesDir || this.automaticGlossary) {
// Find the glossary that matches the source and target language:
const glossary = await this.getGlossary(from, to);
const glossary = await this.getGlossary(
from,
to,
!this.automaticGlossary,
);
if (glossary) {
// Add it to the options body:
body['glossary_id'] = glossary.glossary_id;
Expand Down
2 changes: 1 addition & 1 deletion src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface TranslationService {
config?: string,
interpolationMatcher?: Matcher,
decodeEscapes?: boolean,
glossariesDir?: string,
glossariesDir?: string | boolean,
appName?: string,
context?: string,
) => Promise<void>;
Expand Down
Loading

0 comments on commit e4e4008

Please sign in to comment.