aboutsummaryrefslogtreecommitdiffstats
path: root/names/vendor/translate-split.js
diff options
context:
space:
mode:
Diffstat (limited to 'names/vendor/translate-split.js')
-rw-r--r--names/vendor/translate-split.js61
1 files changed, 0 insertions, 61 deletions
diff --git a/names/vendor/translate-split.js b/names/vendor/translate-split.js
deleted file mode 100644
index 95f6468..0000000
--- a/names/vendor/translate-split.js
+++ /dev/null
@@ -1,61 +0,0 @@
-// What must come back from a translation exactly as it went in: links,
-// nostr: mentions, custom emoji shortcodes, hashtags, @handles, invoices and
-// other long codes. Translation models mangle them ("https: / / example.com
-// / fot.jpg"), so only the prose between them is translated and the text is
-// put back together around the untouched tokens. Shared by the registrar
-// (names/translate.js) and the browser's own translator (messages.js).
-import { scriptOf, LANG_SCRIPT } from './lang-guess.js';
-
-const KEEP = /(https?:\/\/\S+|nostr:[a-z0-9]+|:[a-z0-9_+-]{1,40}:|#[\p{L}\p{N}_]+|@[\w.-]+(?:@[\w.-]+)?|\b(?:ln|lightning:|cashu|bitcoin:|nevent|npub|nprofile|note|naddr)[a-z0-9]{20,}|\S{40,})/giu;
-
-export function splitProse(text) {
- const parts = [];
- let last = 0;
- for (const m of String(text || '').matchAll(KEEP)) {
- if (m.index > last) parts.push({ prose: text.slice(last, m.index) });
- parts.push({ keep: m[0] });
- last = m.index + m[0].length;
- }
- if (last < text.length) parts.push({ prose: text.slice(last) });
- return parts;
-}
-// a piece worth translating: has words, not just spaces and punctuation
-export const hasWords = (s) => /\p{L}{2,}/u.test(s || '');
-
-// A hashtag stays a hashtag (it's a link to its topic), but one written in
-// another script than the reader's means nothing to them: such a tag gets
-// its translation in brackets after it, "#総合部絵チャ (general art chat)".
-const tagWords = (tag) => tag.slice(1).replace(/_/g, ' ');
-export function glossable(tag, target) {
- if (!tag.startsWith('#') || !target) return false;
- const s = scriptOf(tagWords(tag), 1);
- return !!s && !!LANG_SCRIPT[target] && s !== LANG_SCRIPT[target];
-}
-// What a translator gets to see of a post: its prose, then the words of the
-// hashtags the reader can't read (also what language detection runs on).
-export function translatable(text, target) {
- const parts = splitProse(text);
- return {
- parts,
- prose: parts.filter((p) => p.prose && hasWords(p.prose)),
- tags: parts.filter((p) => p.keep && glossable(p.keep, target)),
- };
-}
-
-// Translate the prose pieces and the glossed tags (one call for all of them)
-// and reassemble. Each piece keeps its own leading/trailing whitespace —
-// line breaks matter.
-export async function translatePieces(text, translateMany, target = null) {
- const { parts, prose, tags } = translatable(text, target);
- if (!prose.length && !tags.length) return text;
- const outs = await translateMany([...prose.map((p) => p.prose.trim()), ...tags.map((p) => tagWords(p.keep))]);
- prose.forEach((p, i) => {
- const lead = p.prose.match(/^\s*/)[0], trail = p.prose.match(/\s*$/)[0];
- p.out = lead + String(outs[i] ?? p.prose.trim()) + trail;
- });
- tags.forEach((p, i) => {
- const g = String(outs[prose.length + i] ?? '').trim();
- p.out = g && g.toLowerCase() !== tagWords(p.keep).toLowerCase() ? p.keep + ' (' + g + ')' : p.keep;
- });
- return parts.map((p) => p.out ?? p.keep ?? p.prose).join('');
-}