mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
Reorganize translation interfaces/files
This commit is contained in:
parent
874da1baad
commit
d639d9a14d
8 changed files with 71 additions and 76 deletions
|
|
@ -120,6 +120,7 @@ import { indexController } from '@/controllers/site.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 { cspMiddleware } from '@/middleware/cspMiddleware.ts';
|
||||
import { metricsMiddleware } from '@/middleware/metricsMiddleware.ts';
|
||||
|
|
@ -129,7 +130,6 @@ import { requireSigner } from '@/middleware/requireSigner.ts';
|
|||
import { signerMiddleware } from '@/middleware/signerMiddleware.ts';
|
||||
import { storeMiddleware } from '@/middleware/storeMiddleware.ts';
|
||||
import { uploaderMiddleware } from '@/middleware/uploaderMiddleware.ts';
|
||||
import { DittoTranslator } from '@/translators/translator.ts';
|
||||
import { translatorMiddleware } from '@/middleware/translatorMiddleware.ts';
|
||||
|
||||
interface AppEnv extends HonoEnv {
|
||||
|
|
|
|||
16
src/caches/translationCache.ts
Normal file
16
src/caches/translationCache.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { LanguageCode } from 'iso-639-1';
|
||||
import { LRUCache } from 'lru-cache';
|
||||
|
||||
import { MastodonTranslation } from '@/entities/MastodonTranslation.ts';
|
||||
import { Time } from '@/utils/time.ts';
|
||||
|
||||
/** Entity returned by DittoTranslator and LRUCache */
|
||||
interface DittoTranslation {
|
||||
data: MastodonTranslation;
|
||||
}
|
||||
|
||||
/** Translations LRU cache. */
|
||||
export const translationCache = new LRUCache<`${LanguageCode}-${string}`, DittoTranslation>({
|
||||
max: 1000,
|
||||
ttl: Time.hours(6),
|
||||
});
|
||||
|
|
@ -2,10 +2,11 @@ import { LanguageCode } from 'iso-639-1';
|
|||
import { z } from 'zod';
|
||||
|
||||
import { AppController } from '@/app.ts';
|
||||
import { localeSchema } from '@/schema.ts';
|
||||
import { dittoTranslations, dittoTranslationsKey, MastodonTranslation } from '@/translators/translator.ts';
|
||||
import { parseBody } from '@/utils/api.ts';
|
||||
import { translationCache } from '@/caches/translationCache.ts';
|
||||
import { MastodonTranslation } from '@/entities/MastodonTranslation.ts';
|
||||
import { getEvent } from '@/queries.ts';
|
||||
import { localeSchema } from '@/schema.ts';
|
||||
import { parseBody } from '@/utils/api.ts';
|
||||
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
||||
|
||||
const translateSchema = z.object({
|
||||
|
|
@ -45,11 +46,11 @@ const translateController: AppController = async (c) => {
|
|||
return c.json({ error: 'Bad request.', schema: result.error }, 400);
|
||||
}
|
||||
|
||||
const translatedId = `${lang}-${id}` as dittoTranslationsKey;
|
||||
const translationCache = dittoTranslations.get(translatedId);
|
||||
const cacheKey: `${LanguageCode}-${string}` = `${lang}-${id}`;
|
||||
const cached = translationCache.get(cacheKey);
|
||||
|
||||
if (translationCache) {
|
||||
return c.json(translationCache.data, 200);
|
||||
if (cached) {
|
||||
return c.json(cached.data, 200);
|
||||
}
|
||||
|
||||
const mediaAttachments = status?.media_attachments.map((value) => {
|
||||
|
|
@ -68,7 +69,7 @@ const translateController: AppController = async (c) => {
|
|||
media_attachments: [],
|
||||
poll: null,
|
||||
detected_source_language: event.language ?? 'en',
|
||||
provider: translator.getProvider(),
|
||||
provider: translator.provider,
|
||||
};
|
||||
|
||||
if ((status?.poll as MastodonTranslation['poll'])?.options) {
|
||||
|
|
@ -130,10 +131,10 @@ const translateController: AppController = async (c) => {
|
|||
|
||||
mastodonTranslation.detected_source_language = data.source_lang;
|
||||
|
||||
dittoTranslations.set(translatedId, { data: mastodonTranslation });
|
||||
translationCache.set(cacheKey, { data: mastodonTranslation });
|
||||
return c.json(mastodonTranslation, 200);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message?.includes('not supported')) {
|
||||
if (e instanceof Error && e.message.includes('not supported')) {
|
||||
return c.json({ error: `Translation of source language '${event.language}' not supported` }, 422);
|
||||
}
|
||||
return c.json({ error: 'Service Unavailable' }, 503);
|
||||
|
|
|
|||
17
src/entities/MastodonTranslation.ts
Normal file
17
src/entities/MastodonTranslation.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { LanguageCode } from 'iso-639-1';
|
||||
|
||||
/** https://docs.joinmastodon.org/entities/Translation/ */
|
||||
export interface MastodonTranslation {
|
||||
/** HTML-encoded translated content of the status. */
|
||||
content: string;
|
||||
/** The translated spoiler warning of the status. */
|
||||
spoiler_text: string;
|
||||
/** The translated media descriptions of the status. */
|
||||
media_attachments: { id: string; description: string }[];
|
||||
/** The translated poll of the status. */
|
||||
poll: { id: string; options: { title: string }[] } | null;
|
||||
//** The language of the source text, as auto-detected by the machine translation provider. */
|
||||
detected_source_language: LanguageCode;
|
||||
/** The service that provided the machine translation. */
|
||||
provider: string;
|
||||
}
|
||||
18
src/interfaces/DittoTranslator.ts
Normal file
18
src/interfaces/DittoTranslator.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import type { LanguageCode } from 'iso-639-1';
|
||||
|
||||
/** DittoTranslator class, used for status translation. */
|
||||
export interface DittoTranslator {
|
||||
/** Translate the 'content' into 'targetLanguage'. */
|
||||
translate(
|
||||
/** Texts to translate. */
|
||||
texts: string[],
|
||||
/** The language of the source texts. */
|
||||
sourceLanguage: LanguageCode | undefined,
|
||||
/** The texts will be translated into this language. */
|
||||
targetLanguage: LanguageCode,
|
||||
/** Custom options. */
|
||||
opts?: { signal?: AbortSignal },
|
||||
): Promise<{ results: string[]; source_lang: LanguageCode }>;
|
||||
/** Provider name, eg `DeepL.com` */
|
||||
provider: string;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { LanguageCode } from 'iso-639-1';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { DittoTranslator, SourceLanguage, TargetLanguage } from '@/translators/translator.ts';
|
||||
import { DittoTranslator } from '@/interfaces/DittoTranslator.ts';
|
||||
import { languageSchema } from '@/schema.ts';
|
||||
|
||||
interface DeepLTranslatorOpts {
|
||||
|
|
@ -28,8 +28,8 @@ export class DeepLTranslator implements DittoTranslator {
|
|||
|
||||
async translate(
|
||||
texts: string[],
|
||||
source: SourceLanguage | undefined,
|
||||
dest: TargetLanguage,
|
||||
source: LanguageCode | undefined,
|
||||
dest: LanguageCode,
|
||||
opts?: { signal?: AbortSignal },
|
||||
) {
|
||||
const { translations } = await this.translateMany(texts, source, dest, opts);
|
||||
|
|
@ -43,8 +43,8 @@ export class DeepLTranslator implements DittoTranslator {
|
|||
/** DeepL translate request. */
|
||||
private async translateMany(
|
||||
texts: string[],
|
||||
source: SourceLanguage | undefined,
|
||||
targetLanguage: TargetLanguage,
|
||||
source: LanguageCode | undefined,
|
||||
targetLanguage: LanguageCode,
|
||||
opts?: { signal?: AbortSignal },
|
||||
) {
|
||||
const body: any = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { LanguageCode } from 'iso-639-1';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { DittoTranslator, SourceLanguage, TargetLanguage } from '@/translators/translator.ts';
|
||||
import { DittoTranslator } from '@/interfaces/DittoTranslator.ts';
|
||||
import { languageSchema } from '@/schema.ts';
|
||||
|
||||
interface LibreTranslateTranslatorOpts {
|
||||
|
|
@ -28,8 +28,8 @@ export class LibreTranslateTranslator implements DittoTranslator {
|
|||
|
||||
async translate(
|
||||
texts: string[],
|
||||
source: SourceLanguage | undefined,
|
||||
dest: TargetLanguage,
|
||||
source: LanguageCode | undefined,
|
||||
dest: LanguageCode,
|
||||
opts?: { signal?: AbortSignal },
|
||||
) {
|
||||
const translations = await Promise.all(
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
import { LanguageCode } from 'iso-639-1';
|
||||
import { LRUCache } from 'lru-cache';
|
||||
|
||||
import { Time } from '@/utils/time.ts';
|
||||
|
||||
/** Original language of the post */
|
||||
export type SourceLanguage = LanguageCode;
|
||||
|
||||
/** Content will be translated to this language */
|
||||
export type TargetLanguage = LanguageCode;
|
||||
|
||||
/** Entity returned by DittoTranslator and LRUCache */
|
||||
type DittoTranslation = {
|
||||
data: MastodonTranslation;
|
||||
};
|
||||
|
||||
export type MastodonTranslation = {
|
||||
/** HTML-encoded translated content of the status. */
|
||||
content: string;
|
||||
/** The translated spoiler warning of the status. */
|
||||
spoiler_text: string;
|
||||
/** The translated media descriptions of the status. */
|
||||
media_attachments: { id: string; description: string }[];
|
||||
/** The translated poll of the status. */
|
||||
poll: { id: string; options: { title: string }[] } | null;
|
||||
//** The language of the source text, as auto-detected by the machine translation provider. */
|
||||
detected_source_language: SourceLanguage;
|
||||
/** The service that provided the machine translation. */
|
||||
provider: string;
|
||||
};
|
||||
|
||||
/** DittoTranslator class, used for status translation. */
|
||||
export interface DittoTranslator {
|
||||
/** Translate the 'content' into 'targetLanguage'. */
|
||||
translate(
|
||||
texts: string[],
|
||||
/** The language of the source text/status. */
|
||||
sourceLanguage: SourceLanguage | undefined,
|
||||
/** The status content will be translated into this language. */
|
||||
targetLanguage: TargetLanguage,
|
||||
/** Custom options. */
|
||||
opts?: { signal?: AbortSignal },
|
||||
): Promise<{ results: string[]; source_lang: SourceLanguage }>;
|
||||
/** Provider name, eg `DeepL.com` */
|
||||
provider: string;
|
||||
}
|
||||
|
||||
/** Includes the TARGET language and the status id.
|
||||
* Example: en-390f5b01b49a8ee6e13fe917420c023d889b3da8e983a14c9e84587e43d12c15
|
||||
* The example above means:
|
||||
* I want the status 390f5b01b49a8ee6e13fe917420c023d889b3da8e983a14c9e84587e43d12c15 translated to english (if it exists in the LRUCache). */
|
||||
export type dittoTranslationsKey = `${TargetLanguage}-${string}`;
|
||||
|
||||
export const dittoTranslations = new LRUCache<dittoTranslationsKey, DittoTranslation>({
|
||||
max: 1000,
|
||||
ttl: Time.hours(6),
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue