-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(providers): translation providers from extensions (#18)
- Loading branch information
1 parent
4d2501d
commit a9f9aeb
Showing
10 changed files
with
184 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import { computed, effect, reactive, shallowReactive } from '@vue/reactivity' | ||
import { extensions } from 'vscode' | ||
import type { TranslationParameters, TranslationProviderInfo, TranslationResult } from './types' | ||
import type { Context } from '~/context' | ||
import { invoke } from '~/utils' | ||
import { config } from '~/config' | ||
|
||
export interface ITranslateExtensionConfig { | ||
extensionId: string | ||
title: string | ||
category?: string | ||
Ctor?: new () => any | ||
translate: string | ||
instance?: any | ||
|
||
promise?: Promise<any> | ||
} | ||
|
||
export interface ITranslateRegistry { | ||
(translation: string, translate: new () => any): void | ||
} | ||
|
||
const translateConfig: Map<string, ITranslateExtensionConfig> = reactive(new Map()) | ||
|
||
export const externalTranslators = computed(() => { | ||
return Object.fromEntries(Array.from(translateConfig.entries()) | ||
.map(([name, item]) => <[string, TranslationProviderInfo]>[name, { | ||
name, | ||
label: item.title, | ||
needs: [], | ||
supportLanguage: { | ||
'zh-CN': 'zh-CN', | ||
}, | ||
translate: options => translateWithConf(name, item, options), | ||
}])) | ||
}) | ||
|
||
// eslint-disable-next-line unused-imports/no-unused-vars | ||
export function registerExtensionTranslate(ctx: Context) { | ||
loadExtensionTranslate() | ||
extensions.onDidChange(() => loadExtensionTranslate()) | ||
} | ||
|
||
export function loadExtensionTranslate() { | ||
const currentKeys = new Set<string>() | ||
extensions.all | ||
.filter(ext => ext?.packageJSON?.contributes?.translates?.length > 0) | ||
.forEach((extension) => { | ||
const translates = extension.packageJSON.contributes.translates | ||
|
||
for (const { title, translate, category } of translates) { | ||
if (!title || !translate) | ||
return | ||
const key = `${extension.id}-${translate}` | ||
currentKeys.add(key) | ||
|
||
if (!translateConfig.get(key)) { | ||
translateConfig.set(key, shallowReactive({ | ||
extensionId: extension.id, | ||
translate, | ||
title, | ||
category, | ||
})) | ||
} | ||
} | ||
}) | ||
|
||
for (const key of translateConfig.keys()) { | ||
if (!currentKeys.has(key)) | ||
translateConfig.delete(key) | ||
} | ||
} | ||
|
||
async function translateWithConf(name: string, conf: ITranslateExtensionConfig, { text, from, to }: TranslationParameters): Promise<TranslationResult> { | ||
function msgPerfix(text: string) { | ||
return `[Interline Translate] ${conf.title} (${name}) / ${text}` | ||
} | ||
|
||
try { | ||
if (!conf.instance) | ||
await activateWithConf(name, conf) | ||
} | ||
catch (e) { | ||
return { | ||
ok: false, | ||
message: msgPerfix('Activate Failed'), | ||
originalError: e, | ||
} | ||
} | ||
|
||
try { | ||
const res = await conf.instance.translate(text, { from, to }) | ||
return { | ||
ok: true, | ||
text: res, | ||
} | ||
} | ||
catch (e) { | ||
return { | ||
ok: false, | ||
message: msgPerfix(typeof e === 'object' ? (e as any)?.message : 'Translate Failed: Unknown Error'), | ||
originalError: e, | ||
} | ||
} | ||
} | ||
|
||
async function activateWithConf(name: string, conf: ITranslateExtensionConfig) { | ||
if (conf.promise) | ||
return conf.promise | ||
|
||
const extension = extensions.all.find(extension => extension.id === conf.extensionId) | ||
if (!extension) | ||
return | ||
try { | ||
conf.promise = invoke(async () => { | ||
await extension.activate() | ||
if (!extension.exports || !extension.exports.extendTranslate) | ||
throw new Error(`Invalid extension: ${name}`) | ||
|
||
await extension | ||
.exports | ||
.extendTranslate((_: any, Translate: new () => any) => { | ||
conf.Ctor = Translate | ||
conf.instance = new conf.Ctor() | ||
}) | ||
}) | ||
await conf.promise | ||
} | ||
finally { | ||
conf.promise = undefined | ||
} | ||
} | ||
|
||
// clear instance | ||
let oldTranslator: string | undefined | ||
effect(() => { | ||
const name = config.translator | ||
if (name !== oldTranslator && translateConfig.has(name)) | ||
translateConfig.get(name)!.instance = undefined | ||
oldTranslator = name | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,14 @@ | ||
import { computed } from '@vue/reactivity' | ||
import { info as googleInfo } from './google' | ||
import { info as bingInfo } from './bing' | ||
import { externalTranslators } from './extensions' | ||
|
||
export const translators = { | ||
const builtInTranslators = { | ||
google: googleInfo, | ||
bing: bingInfo, | ||
} | ||
export const translatorOptions = Object.values(translators) | ||
|
||
export const translators = computed(() => { | ||
return Object.assign({}, builtInTranslators, externalTranslators.value) | ||
}) | ||
export const translatorOptions = computed(() => Object.values(translators.value)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters