aboutsummaryrefslogtreecommitdiffstats
path: root/names/server.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-09 10:36:39 -0700
committerAdam Soltys <[email protected]>2026-10-09 10:36:39 -0700
commit41d7b9d4e5d0e10cdac708d9c9cd4838e47240ec (patch)
tree767d6dffc8dc14b90c368606d8fc39c68b050fb2 /names/server.js
parentad028dd1c61e41913e4628046b96bf162bc4ba4a (diff)
downloadcoinosv3-41d7b9d4e5d0e10cdac708d9c9cd4838e47240ec.tar.gz
coinosv3-41d7b9d4e5d0e10cdac708d9c9cd4838e47240ec.zip
Translation: posts in another language are shown in yours — "Translated from Spanish · Show original" under the words, automatic by default for posts the language guess calls foreign (Settings → Nostr → Translation switches it off and leaves a Translate link instead), and ⋯ → Translate for any post. The browser's on-device Translator does it when the language pair is already installed (desktop Chrome); otherwise the registrar's new POST /translate (names/translate.js): a cached, 4-at-a-time front for a LibreTranslate container on cs (names/compose-libretranslate.yml, v1.9.6, 32 languages; none for sw/mr/ta/te, so those readers get no offer). Links, nostr: mentions, :emoji:, #tags and long codes are never sent to the model — only the prose between them, batched in one call (src/translate-split.js, shared). Translations are remembered (coinos-translations, 300). tools/translate-test.js
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
Diffstat (limited to 'names/server.js')
-rw-r--r--names/server.js17
1 files changed, 17 insertions, 0 deletions
diff --git a/names/server.js b/names/server.js
index 6cd82a1..463ab8e 100644
--- a/names/server.js
+++ b/names/server.js
@@ -28,6 +28,7 @@ import { mnemonicToSeedSync } from '@scure/bip39';
import { lnBackend } from '../bridge/ln.js';
import { ArkManager } from '../src/ark/manager.js';
import { fetchPreview } from './preview.js';
+import { translate, languages } from './translate.js';
import { decodeAddress } from '../src/ark/proto.js';
import { decodeNoffer } from '../src/noffer.js';
import { decodeBolt11 } from '../src/ark/lightning.js';
@@ -1036,6 +1037,22 @@ Bun.serve({
});
}
+ // A post in another language, in the reader's (LibreTranslate behind a
+ // cache — see translate.js). POST { text, target, source? } →
+ // { text, source }; GET /translate/languages lists what it can do.
+ if (url.pathname === '/translate/languages' && req.method === 'GET') {
+ try { return new Response(JSON.stringify(await languages()), { headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*', 'cache-control': 'public, max-age=3600' } }); }
+ catch { return json({ error: 'translation unavailable' }, 503); }
+ }
+ if (url.pathname === '/translate' && req.method === 'POST') {
+ if (!rateOk(ip, 60)) return json({ error: 'rate limited' }, 429);
+ let body = null;
+ try { body = await req.json(); } catch {}
+ if (!body || typeof body.text !== 'string' || typeof body.target !== 'string') return json({ error: 'bad request' }, 400);
+ try { return json(await translate(body.text, body.target, body.source)); }
+ catch (e) { return json({ error: e.message || 'translation failed' }, e.status || 503); }
+ }
+
if (url.pathname === '/health') {
return json({
ok: true, domain: DOMAIN, names: Object.keys(state.names).length,