Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1042,7 +1042,8 @@
"deepl",
"libretranslate",
"baidu",
"openai"
"openai",
"languagewire"
]
},
"default": [
Expand Down Expand Up @@ -1105,6 +1106,16 @@
"default": null,
"description": "%config.openai_api_key%"
},
"i18n-ally.translate.languagewire.apiKey": {
"type": "string",
"default": null,
"description": "%config.languagewire_api_key%"
},
"i18n-ally.translate.languagewire.apiRoot": {
"type": "string",
"default": "https://lwt.languagewire.com/p/api/v1",
"description": "%config.languagewire_api_root%"
},
"i18n-ally.translate.openai.apiRoot": {
"type": "string",
"default": "https://api.openai.com",
Expand Down
10 changes: 9 additions & 1 deletion src/core/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export class Config {
return this.getConfig<SortCompare>('sortCompare') || 'binary'
}

static get sortLocale(): string | undefined{
static get sortLocale(): string | undefined {
return this.getConfig<string>('sortLocale')
}

Expand Down Expand Up @@ -572,6 +572,14 @@ export class Config {
return this.getConfig<string | null | undefined>('translate.libre.apiRoot')
}

static get languageWireApiKey() {
return this.getConfig<string | null | undefined>('translate.languagewire.apiKey')
}

static get languageWireApiRoot() {
return this.getConfig<string | null | undefined>('translate.languagewire.apiRoot')
}

static get openaiApiKey() {
return this.getConfig<string | null | undefined>('translate.openai.apiKey')
}
Expand Down
61 changes: 61 additions & 0 deletions src/translators/engines/languagewire.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import axios from 'axios'
import TranslateEngine, { TranslateOptions, TranslateResult } from './base'
import { Config } from '~/core'

export default class LanguageWireTranslate extends TranslateEngine {
apiRoot = 'https://lwt.languagewire.com/p/api/v1'

async translate(options: TranslateOptions) {
const apiKey = Config.languageWireApiKey
let apiRoot = this.apiRoot
if (Config.languageWireApiRoot) apiRoot = Config.languageWireApiRoot.replace(/\/$/, '')

Comment on lines +8 to +12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard against missing API key and undefined target language.

Calling the API without a key or target language will fail with a 401/validation error and yields a poor UX. Throw early with a clear message.

Apply:

   async translate(options: TranslateOptions) {
     const apiKey = Config.languageWireApiKey
     let apiRoot = this.apiRoot
-    if (Config.languageWireApiRoot) apiRoot = Config.languageWireApiRoot.replace(/\/$/, '')
+    if (Config.languageWireApiRoot) apiRoot = Config.languageWireApiRoot.replace(/\/$/, '')
+
+    if (!apiKey) {
+      throw new Error('LanguageWire: missing API key. Configure i18n-ally.translate.languagewire.apiKey in settings.')
+    }
+    if (!options.to) {
+      throw new Error('LanguageWire: target language "to" is required.')
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async translate(options: TranslateOptions) {
const apiKey = Config.languageWireApiKey
let apiRoot = this.apiRoot
if (Config.languageWireApiRoot) apiRoot = Config.languageWireApiRoot.replace(/\/$/, '')
async translate(options: TranslateOptions) {
const apiKey = Config.languageWireApiKey
let apiRoot = this.apiRoot
if (Config.languageWireApiRoot) apiRoot = Config.languageWireApiRoot.replace(/\/$/, '')
if (!apiKey) {
throw new Error('LanguageWire: missing API key. Configure i18n-ally.translate.languagewire.apiKey in settings.')
}
if (!options.to) {
throw new Error('LanguageWire: target language "to" is required.')
}
// …rest of implementation…
}
🤖 Prompt for AI Agents
In src/translators/engines/languagewire.ts around lines 8 to 12, add early
guards to validate Config.languageWireApiKey and the requested target language
before making the API call: if the API key is missing/empty throw a clear Error
like "LanguageWire API key is missing: set Config.languageWireApiKey", and if
the TranslateOptions does not include a target language (e.g.,
options.targetLanguage or options.target) throw "Missing target language in
translate options"; do these checks immediately after reading apiKey and
options, so the function fails fast with descriptive messages rather than
letting the request go to the API and return a 401/validation error.

if (!apiKey) throw new Error('LanguageWire: missing API key. Configure i18n-ally.translate.languagewire.apiKey in settings.')
if (!options.to) throw new Error('LanguageWire: target language "to" is required.')

const response = await axios.post(
`${apiRoot}/translations/text`,
{
sourceText: options.text,
reference: {
source: 'USER',
},
targetLanguage: options.to,
// If source language is specified, include it
...(options.from !== 'auto' && { sourceLanguage: options.from }),
},
{
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
timeout: 15000,
},
)

return this.transform(response, options)
}

transform(response: any, options: TranslateOptions): TranslateResult {
const { text, from = 'auto', to = 'auto' } = options

// Get the translated text from the response
const raw = response?.data?.translation
const translatedText = typeof raw === 'string' ? raw.trim() : undefined

// Determine the detected source language
const detectedSource = response?.data?.detectedSourceLanguage?.mmtCode || from

const r: TranslateResult = {
text,
to,
from: detectedSource,
response,
result: translatedText ? [translatedText] : undefined,
linkToResult: '',
}

return r
}
}
3 changes: 3 additions & 0 deletions src/translators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import DeepLTranslateEngine from './engines/deepl'
import LibreTranslateEngine from './engines/libretranslate'
import BaiduTranslate from './engines/baidu'
import OpenAITranslateEngine from './engines/openai'
import LanguageWireTranslate from './engines/languagewire'

export class Translator {
engines: Record<string, TranslateEngine> ={
Expand All @@ -14,6 +15,7 @@ export class Translator {
'libretranslate': new LibreTranslateEngine(),
'baidu': new BaiduTranslate(),
'openai': new OpenAITranslateEngine(),
'languagewire': new LanguageWireTranslate(),
}

async translate(options: TranslateOptions & { engine: string }) {
Expand All @@ -30,6 +32,7 @@ export {
LibreTranslateEngine,
BaiduTranslate,
OpenAITranslateEngine,
LanguageWireTranslate,
}

export * from './engines/base'