aboutsummaryrefslogtreecommitdiffstats
path: root/tools/deepl-translate.test.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-10 08:39:24 -0700
committerAdam Soltys <[email protected]>2026-10-10 08:39:24 -0700
commit336fd6da53ace71af2c949e35edd9f0aac664df7 (patch)
treee2212759231fd6f02a397e1816bd1c5646d28614 /tools/deepl-translate.test.js
parent2338345063b0f1c816469401264b0cd0d7b7636b (diff)
downloadcoinosv3-336fd6da53ace71af2c949e35edd9f0aac664df7.tar.gz
coinosv3-336fd6da53ace71af2c949e35edd9f0aac664df7.zip
Translation: into the phone's language, and DeepL with your own key. isolabellart saw the Translate button but nothing became Italian: the target was the app's language, coinos has no Italian UI (so it ran in English), and the language guess didn't know Italian. Posts now translate into the reader's choice (Settings → Nostr → Translation, any of 31 languages), else the phone's language when the app has no UI for it, else the app's; the guess knows Italian, Dutch and Polish. And, as Jumble offers: a DeepL API key of your own, kept on the device and sent per request as x-deepl-key — the registrar passes it to DeepL (api-free for ":fx" keys; DeepL refuses browsers) and never stores or logs it; a refused key or used-up quota says so once. names: translateDeepl (prose only, links/tags kept, detected source back, 401/402/422 statuses), CORS allows the header. Deployed to cs names. tools/translate-italian-test.js, tools/deepl-translate.test.js.
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
Diffstat (limited to 'tools/deepl-translate.test.js')
-rw-r--r--tools/deepl-translate.test.js38
1 files changed, 38 insertions, 0 deletions
diff --git a/tools/deepl-translate.test.js b/tools/deepl-translate.test.js
new file mode 100644
index 0000000..39975d0
--- /dev/null
+++ b/tools/deepl-translate.test.js
@@ -0,0 +1,38 @@
+// names/translate.js translateDeepl: the reader's key goes to the right
+// DeepL host as a DeepL-Auth-Key header, only the prose is sent (links and
+// tags kept), the detected language comes back, and DeepL's refusals turn
+// into statuses the client can word (401 bad key, 402 quota).
+// Run: bun test tools/deepl-translate.test.js
+import { test, expect } from 'bun:test';
+import { translateDeepl } from '../names/translate.js';
+
+const fake = (status = 200, seen = []) => async (url, init) => {
+ const body = JSON.parse(init.body);
+ seen.push({ url, auth: init.headers.authorization, body });
+ if (status !== 200) return new Response('{}', { status });
+ return new Response(JSON.stringify({ translations: body.text.map((t) => ({ text: '[it] ' + t, detected_source_language: 'EN' })) }), { status: 200 });
+};
+
+test('a free key goes to api-free, as DeepL-Auth-Key, prose only', async () => {
+ const seen = [];
+ const out = await translateDeepl('Good morning everyone, look at this https://example.com/a.png #nostr', 'it', 'abcdef01-2345-6789-abcd-ef0123456789:fx', fake(200, seen));
+ expect(seen[0].url).toBe('https://api-free.deepl.com/v2/translate');
+ expect(seen[0].auth).toBe('DeepL-Auth-Key abcdef01-2345-6789-abcd-ef0123456789:fx');
+ expect(seen[0].body.target_lang).toBe('IT');
+ expect(seen[0].body.text.join(' ')).not.toContain('https://');
+ expect(out.source).toBe('en');
+ expect(out.text).toContain('[it] ');
+ expect(out.text).toContain('https://example.com/a.png');
+ expect(out.text).toContain('#nostr');
+});
+test('a paid key goes to api.deepl.com; English means EN-US', async () => {
+ const seen = [];
+ await translateDeepl('Buongiorno a tutti, come state oggi?', 'en', 'abcdef01-2345-6789-abcd-ef0123456780', fake(200, seen));
+ expect(seen[0].url).toBe('https://api.deepl.com/v2/translate');
+ expect(seen[0].body.target_lang).toBe('EN-US');
+});
+test('refusals: bad key 401, quota 402', async () => {
+ await expect(translateDeepl('Hello there my friends, how are you', 'it', 'abcdef01-2345-6789-abcd-ef0123456781:fx', fake(403))).rejects.toMatchObject({ status: 401 });
+ await expect(translateDeepl('Hello there my friends, how are you all', 'it', 'abcdef01-2345-6789-abcd-ef0123456781:fx', fake(456))).rejects.toMatchObject({ status: 402 });
+ await expect(translateDeepl('Hello', 'it', 'not a key')).rejects.toMatchObject({ status: 401 });
+});