Skip to content

Commit

Permalink
Add "DeepL" provider (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
fabasoad authored Feb 12, 2023
1 parent 180cb34 commit ce256a8
Show file tree
Hide file tree
Showing 13 changed files with 262 additions and 76 deletions.
31 changes: 18 additions & 13 deletions .github/workflows/functional-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,31 @@ jobs:
fail-fast: false
matrix:
include:
- provider: linguatools
- provider: 'deepl'
lang: 'en-uk'
source: 'Love'
expected: 'Любов'
api_key: 'DEEPL_API_KEY' # pragma: allowlist secret
- provider: 'linguatools'
lang: 'en-de'
source: Love
source: 'Love'
expected: 'Love'
api_key: N_A # pragma: allowlist secret
- provider: mymemory
api_key: 'N_A' # pragma: allowlist secret
- provider: 'mymemory'
lang: 'en|it'
source: Love
source: 'Love'
expected: 'AMORE'
api_key: N_A # pragma: allowlist secret
- provider: mymemory
api_key: 'N_A' # pragma: allowlist secret
- provider: 'mymemory'
lang: 'en|pt'
source: Love
source: 'Love'
expected: 'Com gratidão,'
api_key: MYMEMORY_API_KEY # pragma: allowlist secret
- provider: funtranslations
api_key: 'MYMEMORY_API_KEY' # pragma: allowlist secret
- provider: 'funtranslations'
lang: 'klingon'
source: Love
expected: Parmaq
api_key: N_A # pragma: allowlist secret
source: 'Love'
expected: 'Parmaq'
api_key: 'N_A' # pragma: allowlist secret
steps:
- uses: actions/checkout@v3
- uses: ./
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,20 @@ jobs:
- name: Compile
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: yarn install
shell: bash
shell: sh
- name: Lint
run: yarn run lint
shell: sh
- name: Unit tests
run: yarn test
if: github.ref != 'refs/heads/main'
env:
DEEPL_API_KEY: ${{ secrets.DEEPL_API_KEY }}
MYMEMORY_API_KEY: ${{ secrets.MYMEMORY_API_KEY }}
run: yarn test
shell: sh
- name: Unit tests with coverage
uses: paambaati/codeclimate-action@v3.2.0
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: paambaati/codeclimate-action@v3.2.0
env:
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
with:
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ repos:
entry: yarn run lint
language: system
pass_filenames: false
verbose: true
verbose: false
stages: ["push"]
- id: test
name: Unit tests
Expand All @@ -27,7 +27,7 @@ repos:
verbose: true
stages: ["push"]
- id: audit
name: yarn audit
name: Yarn audit
entry: yarn npm audit --all
language: system
pass_filenames: false
Expand Down
Empty file added .yarn/versions/7e96cfb7.yml
Empty file.
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ more details for each provider below.
## Contents

- [Providers](#providers)
- [DeepL](#deepl)
- [Linguatools](#linguatools)
- [Microsoft](#microsoft)
- [MyMemory](#mymemory)
Expand All @@ -27,6 +28,48 @@ more details for each provider below.

## Providers

### DeepL

- Identifier is `deepl`.
- Supported translation directions can be found [here](https://www.deepl.com/docs-api/general/get-languages/).
- Be aware that source and target languages should be separated by `-` (hyphen)
character while using them in `lang` input. For example, `en-pt` should be used
in case you want to translate text from English into Portugal. See [Example](#deepl-example)
for more details.
- How to get API key:
- Sign up to [DeepL](https://www.deepl.com) (free plan is fine).
- Go to `Account -> Account -> Authentication Key for DeepL API` section

#### DeepL Example

Example of translating "Love" word from English into Ukrainian:

```yaml
jobs:
deepl:
name: DeepL
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: fabasoad/translation-action@main
id: deepl-step
with:
provider: deepl
lang: en-uk
source: Love
api_key: ${{ secrets.DEEPL_API_KEY }}
- name: Print the result
run: echo "Translation is '${{ steps.deepl-step.outputs.text }}'"
shell: sh
```
Output is the following:
```text
> echo "Translation is 'Любов'"
Translation is 'Любов'
```

### Linguatools

- Identifier is `linguatools`. API Key is not needed for this provider.
Expand Down
24 changes: 12 additions & 12 deletions dist/index.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions jest.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
],
"coverageThreshold": {
"global": {
"branches": 26.92,
"functions": 35,
"lines": 35,
"statements": 35
"branches": 40,
"functions": 40,
"lines": 40,
"statements": 40
}
},
"moduleFileExtensions": [
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "translation-action",
"version": "1.4.15",
"version": "1.5.0",
"description": "This GitHub action translates any text from any language to any language.",
"main": "dist/index.js",
"scripts": {
Expand All @@ -26,6 +26,7 @@
"homepage": "https://github.com/fabasoad/translation-action#readme",
"dependencies": {
"@actions/core": "1.10.0",
"deepl-node": "1.8.0",
"typed-rest-client": "1.8.9",
"yandex-translate": "2.1.3"
},
Expand Down
26 changes: 26 additions & 0 deletions src/__tests__/providers/DeeplProvider.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import ProviderBase from '../../providers/ProviderBase'
import DeeplProvider from '../../providers/DeeplProvider'

require('dotenv').config();

describe('DeeplProvider', () => {
test('should get correct translation', async () => {
const provider: ProviderBase =
new DeeplProvider(process.env.DEEPL_API_KEY || '')
const translations = await provider.translate('Poem', 'en-uk')
expect(translations.length).toEqual(1)
expect(translations[0]).toEqual('Вірш')
})

test('should fail because of invalid lang', async () => {
const provider: ProviderBase =
new DeeplProvider(process.env.DEEPL_API_KEY || '')
try {
await provider.translate('Evening', 'en-abc123')
} catch (e) {
expect(e).toBeTruthy()
return
}
throw new Error('Request should fail due to unknown target language')
})
})
57 changes: 18 additions & 39 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,31 @@
import * as core from '@actions/core';
import extract from './extract';
import FunTranslationsProvider from './providers/FunTranslationsProvider';
import LinguaToolsProvider from './providers/LinguaToolsProvider';
import MicrosoftProvider from './providers/MicrosoftProvider';
import MyMemoryProvider from './providers/MyMemoryProvider';
import ProviderBase, { ProviderError } from './providers/ProviderBase';
import YandexProvider from './providers/YandexProvider';

type ProviderType =
'funtranslations' | 'linguatools' | 'microsoft' | 'mymemory' | 'yandex';

const getProvider = (): ProviderBase => {
const provider: ProviderType = core.getInput('provider') as ProviderType;
switch (provider) {
case 'funtranslations':
return new FunTranslationsProvider();
case 'linguatools':
return new LinguaToolsProvider();
case 'microsoft':
return new MicrosoftProvider(
core.getInput('api_key'), core.getInput('api_additional_parameter'));
case 'mymemory':
return new MyMemoryProvider(core.getInput('api_key'));
case 'yandex':
return new YandexProvider(core.getInput('api_key'));
default:
throw new Error(`${provider} is not supported`);
}
}
import * as core from '@actions/core'
import extract from './extract'
import ProviderBase, { ProviderError } from './providers/ProviderBase'
import ProviderFactory, { ProviderType } from './providers/ProviderFactory'

async function run() {
try {
const source: string = extract(core.getInput('source'));
const provider: ProviderBase = getProvider();
let text: string;
const source: string = extract(core.getInput('source'))
const providerFactory: ProviderFactory = new ProviderFactory()
const provider: ProviderBase = providerFactory.getProvider(
core.getInput('provider') as ProviderType,
core.getInput('api_key'),
core.getInput('api_additional_parameter')
)
let text: string
try {
text = (await provider.translate(source, core.getInput('lang')))[0];
text = (await provider.translate(source, core.getInput('lang')))[0]
} catch (e) {
if (e instanceof ProviderError) {
text = source;
text = source
} else {
throw e;
throw e
}
}
core.setOutput('text', text);
core.setOutput('text', text)
} catch (e) {
core.setFailed((<Error>e).message);
core.setFailed((<Error>e).message)
}
}

run();
run()
19 changes: 19 additions & 0 deletions src/providers/DeeplProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import ProviderBase from './ProviderBase'
import {SourceLanguageCode, TargetLanguageCode, Translator} from 'deepl-node'

export default class DeeplProvider extends ProviderBase {
private translator: Translator

constructor(apiKey: string) {
super()
this.translator = new Translator(apiKey)
}

async translate(text: string, lang: string): Promise<string[]> {
const l: string[] = lang.split('-')
const result = await this.translator.translateText<string>(
text, l[0] as SourceLanguageCode, l[1] as TargetLanguageCode
)
return Promise.resolve([result.text])
}
}
38 changes: 38 additions & 0 deletions src/providers/ProviderFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import ProviderBase from './ProviderBase'
import FunTranslationsProvider from './FunTranslationsProvider'
import LinguaToolsProvider from './LinguaToolsProvider'
import MicrosoftProvider from './MicrosoftProvider'
import MyMemoryProvider from './MyMemoryProvider'
import YandexProvider from './YandexProvider'
import DeeplProvider from './DeeplProvider';

export type ProviderType =
'deepl' |
'funtranslations' |
'linguatools' |
'microsoft' |
'mymemory' |
'yandex'

export default class ProviderFactory {
getProvider(
type: ProviderType, apiKey: string, apiAdditionalParam: string
): ProviderBase {
switch (type) {
case 'deepl':
return new DeeplProvider(apiKey)
case 'funtranslations':
return new FunTranslationsProvider()
case 'linguatools':
return new LinguaToolsProvider()
case 'microsoft':
return new MicrosoftProvider(apiKey, apiAdditionalParam)
case 'mymemory':
return new MyMemoryProvider(apiKey)
case 'yandex':
return new YandexProvider(apiKey)
default:
throw new Error(`${type} is not supported`)
}
}
}
Loading

0 comments on commit ce256a8

Please sign in to comment.