You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

90 lines
2.9 KiB

import { ExtendedKind } from '@/constants'
import { isAsciidocMarkupKind } from '@/lib/advanced-event-lab-kinds'
import {
translateAdvancedLabMarkup,
type AdvancedLabMarkupMode
} from '@/lib/advanced-lab-markup-protect'
import { getLongFormArticleMetadataFromEvent } from '@/lib/event-metadata'
import { normalizeTranslateLangCode } from '@/lib/translate-client'
import type { Event } from 'nostr-tools'
const CHUNK_MAX = 2500
function looksLikeStringifiedJsonObject(content: string): boolean {
const trimmed = content.trim()
if (
!(trimmed.startsWith('{') && trimmed.endsWith('}')) &&
!(trimmed.startsWith('[') && trimmed.endsWith(']'))
) {
return false
}
try {
const parsed = JSON.parse(trimmed) as unknown
return parsed !== null && typeof parsed === 'object'
} catch {
return false
}
}
export function eventHasTranslatableTextBody(event: Event): boolean {
const c = event.content?.trim() ?? ''
if (!c) return false
if (event.kind === ExtendedKind.VOICE || event.kind === ExtendedKind.VOICE_COMMENT) {
return false
}
if (looksLikeStringifiedJsonObject(c)) return false
return true
}
export function articleHasTranslatableTitle(event: Event): boolean {
return Boolean(getLongFormArticleMetadataFromEvent(event).title?.trim())
}
/** Same exclusions as the advanced lab (`translateAdvancedLabMarkup`). Chunk large bodies for the API. */
async function translateLongProtectedBody(
text: string,
target: string,
markupMode: AdvancedLabMarkupMode
): Promise<string> {
const t = text.trim()
if (!t) return text
if (t.length <= CHUNK_MAX) {
return translateAdvancedLabMarkup(t, target, 'auto', markupMode)
}
const blocks: string[] = []
let rest = t
while (rest.length) {
let slice = rest.slice(0, CHUNK_MAX)
const nl = slice.lastIndexOf('\n')
if (nl > 600) {
slice = rest.slice(0, nl + 1)
}
const part = slice.trimEnd()
if (part) {
blocks.push(await translateAdvancedLabMarkup(part, target, 'auto', markupMode))
}
rest = rest.slice(slice.length).trimStart()
}
return blocks.join('\n')
}
/**
* @param targetCode LibreTranslate target as returned by `/languages` (e.g. `tr`, `zh-CN`).
*/
export async function translateNoteForDisplay(
event: Event,
targetCode: string
): Promise<{ content: string; title?: string }> {
const target = normalizeTranslateLangCode(targetCode)
const markupMode: AdvancedLabMarkupMode = isAsciidocMarkupKind(event.kind) ? 'asciidoc' : 'markdown'
const meta = getLongFormArticleMetadataFromEvent(event)
const origTitle = meta.title?.trim()
const title = origTitle
? await translateAdvancedLabMarkup(origTitle, target, 'auto', markupMode)
: undefined
const rawContent = event.content ?? ''
const content = rawContent.trim()
? await translateLongProtectedBody(rawContent, target, markupMode)
: rawContent
return { content: content || rawContent, title }
}