diff --git a/src/core/core-definitions.ts b/src/core/core-definitions.ts index 43cc56e..9664920 100644 --- a/src/core/core-definitions.ts +++ b/src/core/core-definitions.ts @@ -1,5 +1,6 @@ import { TServiceType } from "../services/service-definitions"; import { TMatcherType } from "../matchers/matcher-definitions"; +import { TMiddlewareType } from "../middleware/middleware-definitions"; export type TSet = Map; @@ -12,6 +13,7 @@ export interface CoreArgs { serviceConfig: string | null; matcher: TMatcherType; prompt: string; + middleware: TMiddlewareType[] } export interface TChangeSet { @@ -42,4 +44,5 @@ export interface CliArgs extends Record { service: string; serviceConfig?: string; matcher: string; + middleware?: string } diff --git a/src/core/invoke-translation-service.ts b/src/core/invoke-translation-service.ts index 4997375..f000360 100644 --- a/src/core/invoke-translation-service.ts +++ b/src/core/invoke-translation-service.ts @@ -11,6 +11,9 @@ import { TServiceArgs, TString, } from "../services/service-definitions"; +import { + instantiateTMiddleware +} from "../middleware/middleware-definitions"; export async function invokeTranslationService( serviceInputs: TSet, @@ -84,7 +87,8 @@ async function runTranslationService( `Invoke '${args.service}' from '${args.srcLng}' to '${args.targetLng}' with ${serviceArgs.strings.length} inputs...` ); const translationService = await instantiateTService(args.service); - const rawResults = await translationService.translateStrings(serviceArgs); + const translationMiddleware = await instantiateTMiddleware(args.middleware, translationService) + const rawResults = await translationMiddleware.processTranslation(serviceArgs); return rawResults.map((rawResult) => { const cleanResult = reInsertInterpolations( rawResult.translated, diff --git a/src/core/translate-cli.ts b/src/core/translate-cli.ts index a339632..a81d340 100644 --- a/src/core/translate-cli.ts +++ b/src/core/translate-cli.ts @@ -11,6 +11,7 @@ import { } from "../file-formats/file-format-definitions"; import { getTServiceList, TServiceType } from "../services/service-definitions"; import { getTMatcherList, TMatcherType } from "../matchers/matcher-definitions"; +import { TMiddlewareType } from "../middleware/middleware-definitions"; async function resolveOldTarget( args: CliArgs, @@ -98,6 +99,9 @@ export async function translateCli(cliArgs: CliArgs) { serviceConfig: cliArgs.serviceConfig ?? null, matcher: cliArgs.matcher as TMatcherType, prompt: cliArgs.prompt ?? "", + middleware: (cliArgs.middleware ?? "") + .split(",") + .filter((s) => s) as TMiddlewareType[] }; const result = await translateCore(coreArgs); diff --git a/src/index.ts b/src/index.ts index 3219b69..67f4347 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import { getTFileFormatList } from "./file-formats/file-format-definitions"; import { getTMatcherList } from "./matchers/matcher-definitions"; import { getTServiceList } from "./services/service-definitions"; import { extractVersion } from "./util/extract-version"; +import { getTMiddlewareList } from "./middleware/middleware-definitions"; process.on("unhandledRejection", (error) => { console.error("[fatal]", error); @@ -15,6 +16,10 @@ function formatOneOfOptions(options: string[]): string { return `One of ${formatCliOptions(options)}`; } +function formatManyOfOptions(options: string[]): string { + return `Comma-separated list of ${formatCliOptions(options)}` +} + export function run(process: NodeJS.Process, cliBinDir: string): void { commander.storeOptionsAsProperties(false); commander.addHelpCommand(false); @@ -60,6 +65,10 @@ export function run(process: NodeJS.Process, cliBinDir: string): void { "--prompt ", "supply a prompt for the AI translation service" ) + .option( + "--middleware ", + formatManyOfOptions(getTMiddlewareList()) + ) .version(extractVersion({ cliBinDir }), "-v, --version") .parse(process.argv); @@ -79,6 +88,7 @@ export function run(process: NodeJS.Process, cliBinDir: string): void { serviceConfig: commander.opts().serviceConfig, matcher: commander.opts().matcher, prompt: commander.opts().prompt, + middleware: commander.opts().middleware, }; translateCli(args) .then(() => { diff --git a/src/middleware/middleware-definitions.ts b/src/middleware/middleware-definitions.ts new file mode 100644 index 0000000..e8532a2 --- /dev/null +++ b/src/middleware/middleware-definitions.ts @@ -0,0 +1,38 @@ +import { TServiceArgs, TResult, TService } from '../services/service-definitions' + +export interface TMiddleware { + next?: TMiddleware + processTranslation(args: TServiceArgs): Promise +} + +const middlewareMap = { + 'sanitize-key': null, +}; + +export type TMiddlewareType = keyof typeof middlewareMap; + +export function getTMiddlewareList(): TMiddlewareType[] { + return Object.keys(middlewareMap) as TMiddlewareType[]; +} + +export async function instantiateTMiddleware( + middlewares: TMiddlewareType[], + service: TService, +): Promise { + let next: TMiddleware = { + processTranslation(args) { + return service.translateStrings(args) + }, + } + + while(middlewares.length > 0) { + let middlewareType = middlewares.pop()! + switch(middlewareType) { + case "sanitize-key": + next = new (await import("./sanitize-key")).SanitizeKey(next) + break; + } + } + + return next +} \ No newline at end of file diff --git a/src/middleware/sanitize-key.ts b/src/middleware/sanitize-key.ts new file mode 100644 index 0000000..ac8fb01 --- /dev/null +++ b/src/middleware/sanitize-key.ts @@ -0,0 +1,37 @@ +import { TServiceArgs, TResult } from "../services/service-definitions"; +import { TMiddleware } from "./middleware-definitions"; + +interface SKey { + new: string + old: string +} + +export class SanitizeKey implements TMiddleware { + next: TMiddleware + _sanitizedKeys: SKey[] = [] + + constructor(next: TMiddleware) { + this.next = next + } + + async processTranslation(args: TServiceArgs): Promise { + this._sanitizedKeys = args.strings.map(({key}) => { + return { + new: key.replace(/[^a-zA-Z0-9_]+/g, '_'), + old: key, + } + }) + + for (let i = 0; i < this._sanitizedKeys.length; i++) { + args.strings[i].key = this._sanitizedKeys[i].new + } + + const res = await this.next.processTranslation(args) + + for (let i = 0; i < this._sanitizedKeys.length; i++) { + res[i].key = this._sanitizedKeys[i].old + } + + return res + } +} \ No newline at end of file diff --git a/test/core/core-test-util.ts b/test/core/core-test-util.ts index 2e8e9ed..10b6766 100644 --- a/test/core/core-test-util.ts +++ b/test/core/core-test-util.ts @@ -89,6 +89,7 @@ export const commonArgs: Omit = { srcLng: "en", targetLng: "de", prompt: "", + middleware: [], }; export async function translateCoreAssert(