diff --git a/deno.json b/deno.json index 33c8e1d6..888db8cf 100644 --- a/deno.json +++ b/deno.json @@ -7,6 +7,7 @@ "./packages/ditto", "./packages/lang", "./packages/metrics", + "./packages/translators", "./packages/uploaders" ], "tasks": { diff --git a/packages/ditto/app.ts b/packages/ditto/app.ts index 6a54f66f..88bfa7f9 100644 --- a/packages/ditto/app.ts +++ b/packages/ditto/app.ts @@ -1,6 +1,7 @@ import { confMw } from '@ditto/api/middleware'; import { type DittoConf } from '@ditto/conf'; import { DittoTables } from '@ditto/db'; +import { type DittoTranslator } from '@ditto/translators'; import { type Context, Env as HonoEnv, Handler, Hono, Input as HonoInput, MiddlewareHandler } from '@hono/hono'; import { every } from '@hono/hono/combine'; import { cors } from '@hono/hono/cors'; @@ -134,7 +135,6 @@ import { metricsController } from '@/controllers/metrics.ts'; import { manifestController } from '@/controllers/manifest.ts'; import { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well-known/nodeinfo.ts'; import { nostrController } from '@/controllers/well-known/nostr.ts'; -import { DittoTranslator } from '@/interfaces/DittoTranslator.ts'; import { auth98Middleware, requireProof, requireRole } from '@/middleware/auth98Middleware.ts'; import { cacheControlMiddleware } from '@/middleware/cacheControlMiddleware.ts'; import { cspMiddleware } from '@/middleware/cspMiddleware.ts'; diff --git a/packages/ditto/middleware/translatorMiddleware.ts b/packages/ditto/middleware/translatorMiddleware.ts index eb97ae44..478c2fb9 100644 --- a/packages/ditto/middleware/translatorMiddleware.ts +++ b/packages/ditto/middleware/translatorMiddleware.ts @@ -1,8 +1,7 @@ +import { DeepLTranslator, LibreTranslateTranslator } from '@ditto/translators'; import { safeFetch } from '@soapbox/safe-fetch'; import { AppMiddleware } from '@/app.ts'; -import { DeepLTranslator } from '@/translators/DeepLTranslator.ts'; -import { LibreTranslateTranslator } from '@/translators/LibreTranslateTranslator.ts'; /** Set the translator used for translating posts. */ export const translatorMiddleware: AppMiddleware = async (c, next) => { diff --git a/packages/ditto/test.ts b/packages/ditto/test.ts index bc9a6787..47052b8d 100644 --- a/packages/ditto/test.ts +++ b/packages/ditto/test.ts @@ -1,6 +1,4 @@ import { DittoDB } from '@ditto/db'; -import ISO6391, { LanguageCode } from 'iso-639-1'; -import lande from 'lande'; import { NostrEvent } from '@nostrify/nostrify'; import { finalizeEvent, generateSecretKey } from 'nostr-tools'; @@ -68,15 +66,3 @@ export async function createTestDB(opts?: { pure?: boolean }) { export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } - -export function getLanguage(text: string): LanguageCode | undefined { - const [topResult] = lande(text); - if (topResult) { - const [iso6393] = topResult; - const locale = new Intl.Locale(iso6393); - if (ISO6391.validate(locale.language)) { - return locale.language; - } - } - return; -} diff --git a/packages/ditto/translators/DeepLTranslator.test.ts b/packages/translators/DeepLTranslator.test.ts similarity index 79% rename from packages/ditto/translators/DeepLTranslator.test.ts rename to packages/translators/DeepLTranslator.test.ts index 08f16a66..ae1565c9 100644 --- a/packages/ditto/translators/DeepLTranslator.test.ts +++ b/packages/translators/DeepLTranslator.test.ts @@ -1,14 +1,14 @@ +import { DittoConf } from '@ditto/conf'; +import { detectLanguage } from '@ditto/lang'; import { assert, assertEquals } from '@std/assert'; -import { Conf } from '@/config.ts'; -import { DeepLTranslator } from '@/translators/DeepLTranslator.ts'; -import { getLanguage } from '@/test.ts'; +import { DeepLTranslator } from './DeepLTranslator.ts'; const { deeplBaseUrl: baseUrl, deeplApiKey: apiKey, translationProvider, -} = Conf; +} = new DittoConf(Deno.env); const deepl = 'deepl'; @@ -28,9 +28,9 @@ Deno.test('DeepL translation with source language omitted', { ); assertEquals(data.source_lang, 'pt'); - assertEquals(getLanguage(data.results[0]), 'en'); - assertEquals(getLanguage(data.results[1]), 'en'); - assertEquals(getLanguage(data.results[2]), 'en'); + assertEquals(detectLanguage(data.results[0], 0), 'en'); + assertEquals(detectLanguage(data.results[1], 0), 'en'); + assertEquals(detectLanguage(data.results[2], 0), 'en'); }); Deno.test('DeepL translation with source language set', { @@ -49,9 +49,9 @@ Deno.test('DeepL translation with source language set', { ); assertEquals(data.source_lang, 'pt'); - assertEquals(getLanguage(data.results[0]), 'en'); - assertEquals(getLanguage(data.results[1]), 'en'); - assertEquals(getLanguage(data.results[2]), 'en'); + assertEquals(detectLanguage(data.results[0], 0), 'en'); + assertEquals(detectLanguage(data.results[1], 0), 'en'); + assertEquals(detectLanguage(data.results[2], 0), 'en'); }); Deno.test("DeepL translation doesn't alter Nostr URIs", { diff --git a/packages/ditto/translators/DeepLTranslator.ts b/packages/translators/DeepLTranslator.ts similarity index 91% rename from packages/ditto/translators/DeepLTranslator.ts rename to packages/translators/DeepLTranslator.ts index d1cefaaa..f4b6f918 100644 --- a/packages/ditto/translators/DeepLTranslator.ts +++ b/packages/translators/DeepLTranslator.ts @@ -1,8 +1,9 @@ -import { LanguageCode } from 'iso-639-1'; import { z } from 'zod'; -import { DittoTranslator } from '@/interfaces/DittoTranslator.ts'; -import { languageSchema } from '@/schema.ts'; +import { languageSchema } from './schema.ts'; + +import type { LanguageCode } from 'iso-639-1'; +import type { DittoTranslator } from './DittoTranslator.ts'; interface DeepLTranslatorOpts { /** DeepL base URL to use. Default: 'https://api.deepl.com' */ @@ -31,7 +32,7 @@ export class DeepLTranslator implements DittoTranslator { source: LanguageCode | undefined, dest: LanguageCode, opts?: { signal?: AbortSignal }, - ) { + ): Promise<{ results: string[]; source_lang: LanguageCode }> { const { translations } = await this.translateMany(texts, source, dest, opts); return { diff --git a/packages/ditto/interfaces/DittoTranslator.ts b/packages/translators/DittoTranslator.ts similarity index 100% rename from packages/ditto/interfaces/DittoTranslator.ts rename to packages/translators/DittoTranslator.ts diff --git a/packages/ditto/translators/LibreTranslateTranslator.test.ts b/packages/translators/LibreTranslateTranslator.test.ts similarity index 70% rename from packages/ditto/translators/LibreTranslateTranslator.test.ts rename to packages/translators/LibreTranslateTranslator.test.ts index edda3039..fc6c0a55 100644 --- a/packages/ditto/translators/LibreTranslateTranslator.test.ts +++ b/packages/translators/LibreTranslateTranslator.test.ts @@ -1,14 +1,14 @@ +import { DittoConf } from '@ditto/conf'; +import { detectLanguage } from '@ditto/lang'; import { assertEquals } from '@std/assert'; -import { Conf } from '@/config.ts'; -import { LibreTranslateTranslator } from '@/translators/LibreTranslateTranslator.ts'; -import { getLanguage } from '@/test.ts'; +import { LibreTranslateTranslator } from './LibreTranslateTranslator.ts'; const { libretranslateBaseUrl: baseUrl, libretranslateApiKey: apiKey, translationProvider, -} = Conf; +} = new DittoConf(Deno.env); const libretranslate = 'libretranslate'; @@ -28,9 +28,9 @@ Deno.test('LibreTranslate translation with source language omitted', { ); assertEquals(data.source_lang, 'pt'); - assertEquals(getLanguage(data.results[0]), 'ca'); - assertEquals(getLanguage(data.results[1]), 'ca'); - assertEquals(getLanguage(data.results[2]), 'ca'); + assertEquals(detectLanguage(data.results[0], 0), 'ca'); + assertEquals(detectLanguage(data.results[1], 0), 'ca'); + assertEquals(detectLanguage(data.results[2], 0), 'ca'); }); Deno.test('LibreTranslate translation with source language set', { @@ -49,7 +49,7 @@ Deno.test('LibreTranslate translation with source language set', { ); assertEquals(data.source_lang, 'pt'); - assertEquals(getLanguage(data.results[0]), 'ca'); - assertEquals(getLanguage(data.results[1]), 'ca'); - assertEquals(getLanguage(data.results[2]), 'ca'); + assertEquals(detectLanguage(data.results[0], 0), 'ca'); + assertEquals(detectLanguage(data.results[1], 0), 'ca'); + assertEquals(detectLanguage(data.results[2], 0), 'ca'); }); diff --git a/packages/ditto/translators/LibreTranslateTranslator.ts b/packages/translators/LibreTranslateTranslator.ts similarity index 91% rename from packages/ditto/translators/LibreTranslateTranslator.ts rename to packages/translators/LibreTranslateTranslator.ts index ef7fb1f8..b75f9b54 100644 --- a/packages/ditto/translators/LibreTranslateTranslator.ts +++ b/packages/translators/LibreTranslateTranslator.ts @@ -1,8 +1,9 @@ -import { LanguageCode } from 'iso-639-1'; import { z } from 'zod'; -import { DittoTranslator } from '@/interfaces/DittoTranslator.ts'; -import { languageSchema } from '@/schema.ts'; +import { languageSchema } from './schema.ts'; + +import type { LanguageCode } from 'iso-639-1'; +import type { DittoTranslator } from './DittoTranslator.ts'; interface LibreTranslateTranslatorOpts { /** Libretranslate endpoint to use. Default: 'https://libretranslate.com' */ @@ -31,7 +32,7 @@ export class LibreTranslateTranslator implements DittoTranslator { source: LanguageCode | undefined, dest: LanguageCode, opts?: { signal?: AbortSignal }, - ) { + ): Promise<{ results: string[]; source_lang: LanguageCode }> { const translations = await Promise.all( texts.map((text) => this.translateOne(text, source, dest, 'html', { signal: opts?.signal })), ); diff --git a/packages/translators/deno.json b/packages/translators/deno.json new file mode 100644 index 00000000..5d603f3a --- /dev/null +++ b/packages/translators/deno.json @@ -0,0 +1,7 @@ +{ + "name": "@ditto/translators", + "version": "1.1.0", + "exports": { + ".": "./mod.ts" + } +} diff --git a/packages/translators/mod.ts b/packages/translators/mod.ts new file mode 100644 index 00000000..e60f19c7 --- /dev/null +++ b/packages/translators/mod.ts @@ -0,0 +1,4 @@ +export { DeepLTranslator } from './DeepLTranslator.ts'; +export { LibreTranslateTranslator } from './LibreTranslateTranslator.ts'; + +export type { DittoTranslator } from './DittoTranslator.ts'; diff --git a/packages/translators/schema.ts b/packages/translators/schema.ts new file mode 100644 index 00000000..803ef1b0 --- /dev/null +++ b/packages/translators/schema.ts @@ -0,0 +1,8 @@ +import ISO6391 from 'iso-639-1'; +import z from 'zod'; + +/** Value is a ISO-639-1 language code. */ +export const languageSchema = z.string().refine( + (val) => ISO6391.validate(val), + { message: 'Not a valid language in ISO-639-1 format' }, +);