@ecosplay/e-translate 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -0
- package/dist/index.cjs +134 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +108 -0
- package/dist/index.d.ts +108 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
- package/src/index.ts +228 -0
package/README.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# @ecosplay/e-translate
|
|
2
|
+
|
|
3
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-translate-sdk-js)
|
|
4
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-translate-sdk-js)
|
|
5
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-translate-sdk-js)
|
|
6
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-translate-sdk-js)
|
|
7
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-translate-sdk-js)
|
|
8
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-translate-sdk-js)
|
|
9
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-translate-sdk-js)
|
|
10
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-translate-sdk-js)
|
|
11
|
+
|
|
12
|
+
Client **TypeScript / JavaScript** pour l'API [e-translate](https://translation.e-cosplay.fr)
|
|
13
|
+
(traduction via une API authentifiée par clé, avec mise en cache).
|
|
14
|
+
|
|
15
|
+
- ✅ Fonctionne dans **Node.js ≥ 18** (fetch natif) et dans le **navigateur**
|
|
16
|
+
- ✅ **TypeScript** (types fournis) — sorties **ESM + CJS**
|
|
17
|
+
- ✅ Zéro dépendance runtime
|
|
18
|
+
- ✅ Disponible sur **npm** (`@ecosplay/e-translate`) et installable **par git**
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 📦 Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @ecosplay/e-translate
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Avec pnpm ou yarn :
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add @ecosplay/e-translate
|
|
32
|
+
yarn add @ecosplay/e-translate
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Épingler une version** (conseillé en production) :
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @ecosplay/e-translate@1.0.0
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
> Publié automatiquement sur npm par la CI à chaque tag `vX.Y.Z` (voir
|
|
42
|
+
> `.gitea/workflows/publish.yml`).
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 🚀 Utilisation
|
|
47
|
+
|
|
48
|
+
### ESM / TypeScript
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { ETranslateClient } from "@ecosplay/e-translate";
|
|
52
|
+
|
|
53
|
+
const client = new ETranslateClient({
|
|
54
|
+
baseUrl: "https://translation.e-cosplay.fr",
|
|
55
|
+
apiKey: "etk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const res = await client.translate({ message: "Bonjour le monde", from: "fr", to: "en" });
|
|
59
|
+
console.log(res.translatedText); // "Hello world"
|
|
60
|
+
console.log(res.cached); // false puis true au 2e appel identique
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### CommonJS
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
const { ETranslateClient } = require("@ecosplay/e-translate");
|
|
67
|
+
|
|
68
|
+
const client = new ETranslateClient({
|
|
69
|
+
baseUrl: "https://translation.e-cosplay.fr",
|
|
70
|
+
apiKey: process.env.ETRANSLATE_KEY,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
client.translate({ message: "Hello", from: "en", to: "fr" }).then((r) => console.log(r.translatedText));
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Détection automatique, tableaux, HTML
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// from: "auto" -> la langue source est détectée
|
|
80
|
+
const r1 = await client.translate({ message: "Guten Tag", from: "auto", to: "fr" });
|
|
81
|
+
console.log(r1.detectedLanguage); // { confidence: 90, language: "de" }
|
|
82
|
+
|
|
83
|
+
// Traduire plusieurs textes d'un coup
|
|
84
|
+
const r2 = await client.translate({ message: ["Cat", "Dog"], from: "en", to: "fr" });
|
|
85
|
+
console.log(r2.translatedText); // ["Chat", "Chien"]
|
|
86
|
+
|
|
87
|
+
// Conserver le balisage HTML
|
|
88
|
+
await client.translate({ message: "<b>Bonjour</b>", from: "fr", to: "en", format: "html" });
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 📚 API
|
|
94
|
+
|
|
95
|
+
### `new ETranslateClient(options)` / `createClient(options)`
|
|
96
|
+
|
|
97
|
+
| Option | Type | Défaut | Description |
|
|
98
|
+
| ----------- | ------------------------ | ------------------ | -------------------------------------------- |
|
|
99
|
+
| `baseUrl` | `string` | — | URL du serveur (requis) |
|
|
100
|
+
| `apiKey` | `string` | — | Clé `etk_…` (requise sauf pour `health()`) |
|
|
101
|
+
| `timeoutMs` | `number` | `20000` | Timeout par requête |
|
|
102
|
+
| `fetch` | `typeof fetch` | `globalThis.fetch` | Implémentation fetch (Node < 18, tests) |
|
|
103
|
+
| `headers` | `Record<string,string>` | `{}` | En-têtes additionnels |
|
|
104
|
+
|
|
105
|
+
### Méthodes
|
|
106
|
+
|
|
107
|
+
| Méthode | Endpoint | Retour |
|
|
108
|
+
| ----------------------------- | ------------------- | --------------------------- |
|
|
109
|
+
| `translate(params)` | `POST /translate` | `Promise<TranslateResult>` |
|
|
110
|
+
| `detect(text)` | `POST /detect` | `Promise<DetectedLanguage[]>` |
|
|
111
|
+
| `languages()` | `GET /languages` | `Promise<Language[]>` |
|
|
112
|
+
| `health()` | `GET /health` | `Promise<HealthResult>` |
|
|
113
|
+
|
|
114
|
+
`translate(params)` — `params` : `{ message: string | string[]; from: string; to: string; format?: "text" | "html" }`.
|
|
115
|
+
(`from` accepte `"auto"`. Les alias serveur `source`/`target`/`q` sont gérés côté serveur.)
|
|
116
|
+
|
|
117
|
+
### Gestion des erreurs
|
|
118
|
+
|
|
119
|
+
Toute réponse non-2xx (ou un échec réseau) lève une **`ETranslateError`** :
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { ETranslateError } from "@ecosplay/e-translate";
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
await client.translate({ message: "x", from: "fr", to: "en" });
|
|
126
|
+
} catch (e) {
|
|
127
|
+
if (e instanceof ETranslateError) {
|
|
128
|
+
console.error(e.status); // 401 (clé invalide), 429 (rate limit), 502 (upstream), 0 (réseau/timeout)
|
|
129
|
+
console.error(e.message);
|
|
130
|
+
console.error(e.body); // corps JSON renvoyé par le serveur, si présent
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 🛠️ Build depuis les sources
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git clone ssh://git@code.e-cosplay.fr:222/shoko/e-translate-sdk-js.git
|
|
141
|
+
cd e-translate-sdk-js
|
|
142
|
+
npm install # exécute aussi le build via "prepare"
|
|
143
|
+
npm run build # -> dist/index.js (ESM), dist/index.cjs (CJS), dist/index.d.ts
|
|
144
|
+
npm run typecheck # vérification de types sans émettre
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Licence
|
|
148
|
+
|
|
149
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ETranslateClient: () => ETranslateClient,
|
|
24
|
+
ETranslateError: () => ETranslateError,
|
|
25
|
+
createClient: () => createClient,
|
|
26
|
+
default: () => index_default
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
var ETranslateError = class _ETranslateError extends Error {
|
|
30
|
+
constructor(message, status, body) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "ETranslateError";
|
|
33
|
+
this.status = status;
|
|
34
|
+
this.body = body;
|
|
35
|
+
Object.setPrototypeOf(this, _ETranslateError.prototype);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var ETranslateClient = class {
|
|
39
|
+
constructor(options) {
|
|
40
|
+
if (!options || typeof options.baseUrl !== "string" || options.baseUrl.length === 0) {
|
|
41
|
+
throw new Error("ETranslate: l'option 'baseUrl' est requise.");
|
|
42
|
+
}
|
|
43
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
44
|
+
this.apiKey = options.apiKey ?? "";
|
|
45
|
+
this.timeoutMs = options.timeoutMs ?? 2e4;
|
|
46
|
+
this.extraHeaders = options.headers ?? {};
|
|
47
|
+
const f = options.fetch ?? globalThis.fetch;
|
|
48
|
+
if (typeof f !== "function") {
|
|
49
|
+
throw new Error(
|
|
50
|
+
"ETranslate: aucune impl\xE9mentation `fetch` disponible. Utilisez Node >= 18 ou passez `options.fetch`."
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
this.fetchImpl = f;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Traduit un texte (ou un tableau de textes).
|
|
57
|
+
* Envoie `lang_src` / `lang_dest` / `message` à `POST /translate`.
|
|
58
|
+
*/
|
|
59
|
+
async translate(params) {
|
|
60
|
+
if (!params || params.message == null) {
|
|
61
|
+
throw new Error("translate: 'message' est requis.");
|
|
62
|
+
}
|
|
63
|
+
if (!params.from) throw new Error("translate: 'from' est requis (ex. 'fr' ou 'auto').");
|
|
64
|
+
if (!params.to) throw new Error("translate: 'to' est requis (ex. 'en').");
|
|
65
|
+
return this.request("POST", "/translate", {
|
|
66
|
+
lang_src: params.from,
|
|
67
|
+
lang_dest: params.to,
|
|
68
|
+
message: params.message,
|
|
69
|
+
format: params.format ?? "text"
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** Détecte la langue d'un texte via `POST /detect`. */
|
|
73
|
+
async detect(text) {
|
|
74
|
+
if (!text) throw new Error("detect: 'text' est requis.");
|
|
75
|
+
return this.request("POST", "/detect", { q: text });
|
|
76
|
+
}
|
|
77
|
+
/** Liste les langues supportées via `GET /languages`. */
|
|
78
|
+
async languages() {
|
|
79
|
+
return this.request("GET", "/languages");
|
|
80
|
+
}
|
|
81
|
+
/** État du service via `GET /health` (ne nécessite pas de clé API). */
|
|
82
|
+
async health() {
|
|
83
|
+
return this.request("GET", "/health", void 0, { auth: false });
|
|
84
|
+
}
|
|
85
|
+
async request(method, path, body, opts) {
|
|
86
|
+
const useAuth = opts?.auth !== false;
|
|
87
|
+
const url = `${this.baseUrl}${path}`;
|
|
88
|
+
const headers = { Accept: "application/json", ...this.extraHeaders };
|
|
89
|
+
if (useAuth) headers["X-API-Key"] = this.apiKey;
|
|
90
|
+
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
91
|
+
const controller = new AbortController();
|
|
92
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
93
|
+
let resp;
|
|
94
|
+
try {
|
|
95
|
+
resp = await this.fetchImpl(url, {
|
|
96
|
+
method,
|
|
97
|
+
headers,
|
|
98
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
99
|
+
signal: controller.signal
|
|
100
|
+
});
|
|
101
|
+
} catch (err) {
|
|
102
|
+
const e = err;
|
|
103
|
+
const msg = e?.name === "AbortError" ? `Requ\xEAte expir\xE9e apr\xE8s ${this.timeoutMs} ms (${path}).` : `\xC9chec r\xE9seau vers ${url}: ${e?.message ?? String(err)}`;
|
|
104
|
+
throw new ETranslateError(msg, 0);
|
|
105
|
+
} finally {
|
|
106
|
+
clearTimeout(timer);
|
|
107
|
+
}
|
|
108
|
+
const raw = await resp.text();
|
|
109
|
+
let data = void 0;
|
|
110
|
+
if (raw) {
|
|
111
|
+
try {
|
|
112
|
+
data = JSON.parse(raw);
|
|
113
|
+
} catch {
|
|
114
|
+
data = raw;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!resp.ok) {
|
|
118
|
+
const message = data && typeof data === "object" && "error" in data ? String(data.error) : `HTTP ${resp.status} sur ${path}`;
|
|
119
|
+
throw new ETranslateError(message, resp.status, data);
|
|
120
|
+
}
|
|
121
|
+
return data;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
function createClient(options) {
|
|
125
|
+
return new ETranslateClient(options);
|
|
126
|
+
}
|
|
127
|
+
var index_default = ETranslateClient;
|
|
128
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
129
|
+
0 && (module.exports = {
|
|
130
|
+
ETranslateClient,
|
|
131
|
+
ETranslateError,
|
|
132
|
+
createClient
|
|
133
|
+
});
|
|
134
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * e-translate SDK — client TypeScript/JavaScript pour l'API e-translate.\n *\n * Fonctionne dans Node 18+ (fetch global) et dans le navigateur.\n *\n * @example\n * ```ts\n * import { ETranslateClient } from \"e-translate-sdk\";\n *\n * const client = new ETranslateClient({\n * baseUrl: \"https://translation.e-cosplay.fr\",\n * apiKey: \"etk_xxxxxxxx\",\n * });\n *\n * const res = await client.translate({ message: \"Bonjour\", from: \"fr\", to: \"en\" });\n * console.log(res.translatedText); // \"Hello\"\n * ```\n */\n\n/** Format du texte à traduire. */\nexport type TranslateFormat = \"text\" | \"html\";\n\n/** Méthode HTTP interne. */\ntype HttpMethod = \"GET\" | \"POST\";\n\n/** Options de construction du client. */\nexport interface ETranslateOptions {\n /** URL de base du serveur, ex. \"https://translation.e-cosplay.fr\". */\n baseUrl: string;\n /** Clé d'API (`etk_...`). Requise pour /translate, /detect, /languages. */\n apiKey: string;\n /** Timeout par requête en millisecondes (défaut : 20000). */\n timeoutMs?: number;\n /** Implémentation `fetch` personnalisée (Node < 18, tests). Défaut : `globalThis.fetch`. */\n fetch?: typeof fetch;\n /** En-têtes HTTP supplémentaires envoyés à chaque requête. */\n headers?: Record<string, string>;\n}\n\n/** Paramètres de traduction. */\nexport interface TranslateParams {\n /** Texte (ou tableau de textes) à traduire. */\n message: string | string[];\n /** Langue source (code ISO, ex. \"fr\") ou \"auto\" pour la détection automatique. */\n from: string;\n /** Langue cible (code ISO, ex. \"en\"). */\n to: string;\n /** Format du texte (défaut : \"text\"). */\n format?: TranslateFormat;\n}\n\n/** Langue détectée renvoyée par /detect ou en mode \"auto\". */\nexport interface DetectedLanguage {\n /** Indice de confiance (0–100). */\n confidence: number;\n /** Code ISO de la langue détectée. */\n language: string;\n}\n\n/** Résultat d'une traduction. */\nexport interface TranslateResult {\n /** Texte traduit (string si `message` était une string, sinon string[]). */\n translatedText: string | string[];\n /** `true` si la réponse provient du cache Redis. */\n cached: boolean;\n /** Présent uniquement si `from` valait \"auto\". */\n detectedLanguage?: DetectedLanguage;\n}\n\n/** Une langue supportée par le serveur. */\nexport interface Language {\n code: string;\n name: string;\n targets?: string[];\n}\n\n/** Réponse de /health (aucune clé requise). */\nexport interface HealthResult {\n status: string;\n redis: boolean;\n /** Disponibilité du moteur de traduction en amont. */\n engine: boolean;\n time: string;\n}\n\n/**\n * Erreur levée pour toute réponse non-2xx ou tout échec réseau.\n * `status` vaut 0 en cas d'échec réseau / timeout.\n */\nexport class ETranslateError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = \"ETranslateError\";\n this.status = status;\n this.body = body;\n // Restaure la chaîne de prototype (TS ciblant ES5/ES2015).\n Object.setPrototypeOf(this, ETranslateError.prototype);\n }\n}\n\n/** Client de l'API e-translate. */\nexport class ETranslateClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n private readonly extraHeaders: Record<string, string>;\n\n constructor(options: ETranslateOptions) {\n if (!options || typeof options.baseUrl !== \"string\" || options.baseUrl.length === 0) {\n throw new Error(\"ETranslate: l'option 'baseUrl' est requise.\");\n }\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.apiKey = options.apiKey ?? \"\";\n this.timeoutMs = options.timeoutMs ?? 20000;\n this.extraHeaders = options.headers ?? {};\n\n const f = options.fetch ?? (globalThis as { fetch?: typeof fetch }).fetch;\n if (typeof f !== \"function\") {\n throw new Error(\n \"ETranslate: aucune implémentation `fetch` disponible. \" +\n \"Utilisez Node >= 18 ou passez `options.fetch`.\",\n );\n }\n this.fetchImpl = f;\n }\n\n /**\n * Traduit un texte (ou un tableau de textes).\n * Envoie `lang_src` / `lang_dest` / `message` à `POST /translate`.\n */\n async translate(params: TranslateParams): Promise<TranslateResult> {\n if (!params || params.message == null) {\n throw new Error(\"translate: 'message' est requis.\");\n }\n if (!params.from) throw new Error(\"translate: 'from' est requis (ex. 'fr' ou 'auto').\");\n if (!params.to) throw new Error(\"translate: 'to' est requis (ex. 'en').\");\n\n return this.request<TranslateResult>(\"POST\", \"/translate\", {\n lang_src: params.from,\n lang_dest: params.to,\n message: params.message,\n format: params.format ?? \"text\",\n });\n }\n\n /** Détecte la langue d'un texte via `POST /detect`. */\n async detect(text: string): Promise<DetectedLanguage[]> {\n if (!text) throw new Error(\"detect: 'text' est requis.\");\n return this.request<DetectedLanguage[]>(\"POST\", \"/detect\", { q: text });\n }\n\n /** Liste les langues supportées via `GET /languages`. */\n async languages(): Promise<Language[]> {\n return this.request<Language[]>(\"GET\", \"/languages\");\n }\n\n /** État du service via `GET /health` (ne nécessite pas de clé API). */\n async health(): Promise<HealthResult> {\n return this.request<HealthResult>(\"GET\", \"/health\", undefined, { auth: false });\n }\n\n private async request<T>(\n method: HttpMethod,\n path: string,\n body?: Record<string, unknown>,\n opts?: { auth?: boolean },\n ): Promise<T> {\n const useAuth = opts?.auth !== false;\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = { Accept: \"application/json\", ...this.extraHeaders };\n if (useAuth) headers[\"X-API-Key\"] = this.apiKey;\n if (body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this.fetchImpl(url, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n const e = err as { name?: string; message?: string };\n const msg =\n e?.name === \"AbortError\"\n ? `Requête expirée après ${this.timeoutMs} ms (${path}).`\n : `Échec réseau vers ${url}: ${e?.message ?? String(err)}`;\n throw new ETranslateError(msg, 0);\n } finally {\n clearTimeout(timer);\n }\n\n const raw = await resp.text();\n let data: unknown = undefined;\n if (raw) {\n try {\n data = JSON.parse(raw);\n } catch {\n data = raw;\n }\n }\n\n if (!resp.ok) {\n const message =\n data && typeof data === \"object\" && \"error\" in data\n ? String((data as { error: unknown }).error)\n : `HTTP ${resp.status} sur ${path}`;\n throw new ETranslateError(message, resp.status, data);\n }\n\n return data as T;\n }\n}\n\n/** Fabrique pratique équivalente à `new ETranslateClient(options)`. */\nexport function createClient(options: ETranslateOptions): ETranslateClient {\n return new ETranslateClient(options);\n}\n\nexport default ETranslateClient;\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* e-translate SDK — client TypeScript/JavaScript pour l'API e-translate.
|
|
3
|
+
*
|
|
4
|
+
* Fonctionne dans Node 18+ (fetch global) et dans le navigateur.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { ETranslateClient } from "e-translate-sdk";
|
|
9
|
+
*
|
|
10
|
+
* const client = new ETranslateClient({
|
|
11
|
+
* baseUrl: "https://translation.e-cosplay.fr",
|
|
12
|
+
* apiKey: "etk_xxxxxxxx",
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* const res = await client.translate({ message: "Bonjour", from: "fr", to: "en" });
|
|
16
|
+
* console.log(res.translatedText); // "Hello"
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
/** Format du texte à traduire. */
|
|
20
|
+
type TranslateFormat = "text" | "html";
|
|
21
|
+
/** Options de construction du client. */
|
|
22
|
+
interface ETranslateOptions {
|
|
23
|
+
/** URL de base du serveur, ex. "https://translation.e-cosplay.fr". */
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
/** Clé d'API (`etk_...`). Requise pour /translate, /detect, /languages. */
|
|
26
|
+
apiKey: string;
|
|
27
|
+
/** Timeout par requête en millisecondes (défaut : 20000). */
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
/** Implémentation `fetch` personnalisée (Node < 18, tests). Défaut : `globalThis.fetch`. */
|
|
30
|
+
fetch?: typeof fetch;
|
|
31
|
+
/** En-têtes HTTP supplémentaires envoyés à chaque requête. */
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
}
|
|
34
|
+
/** Paramètres de traduction. */
|
|
35
|
+
interface TranslateParams {
|
|
36
|
+
/** Texte (ou tableau de textes) à traduire. */
|
|
37
|
+
message: string | string[];
|
|
38
|
+
/** Langue source (code ISO, ex. "fr") ou "auto" pour la détection automatique. */
|
|
39
|
+
from: string;
|
|
40
|
+
/** Langue cible (code ISO, ex. "en"). */
|
|
41
|
+
to: string;
|
|
42
|
+
/** Format du texte (défaut : "text"). */
|
|
43
|
+
format?: TranslateFormat;
|
|
44
|
+
}
|
|
45
|
+
/** Langue détectée renvoyée par /detect ou en mode "auto". */
|
|
46
|
+
interface DetectedLanguage {
|
|
47
|
+
/** Indice de confiance (0–100). */
|
|
48
|
+
confidence: number;
|
|
49
|
+
/** Code ISO de la langue détectée. */
|
|
50
|
+
language: string;
|
|
51
|
+
}
|
|
52
|
+
/** Résultat d'une traduction. */
|
|
53
|
+
interface TranslateResult {
|
|
54
|
+
/** Texte traduit (string si `message` était une string, sinon string[]). */
|
|
55
|
+
translatedText: string | string[];
|
|
56
|
+
/** `true` si la réponse provient du cache Redis. */
|
|
57
|
+
cached: boolean;
|
|
58
|
+
/** Présent uniquement si `from` valait "auto". */
|
|
59
|
+
detectedLanguage?: DetectedLanguage;
|
|
60
|
+
}
|
|
61
|
+
/** Une langue supportée par le serveur. */
|
|
62
|
+
interface Language {
|
|
63
|
+
code: string;
|
|
64
|
+
name: string;
|
|
65
|
+
targets?: string[];
|
|
66
|
+
}
|
|
67
|
+
/** Réponse de /health (aucune clé requise). */
|
|
68
|
+
interface HealthResult {
|
|
69
|
+
status: string;
|
|
70
|
+
redis: boolean;
|
|
71
|
+
/** Disponibilité du moteur de traduction en amont. */
|
|
72
|
+
engine: boolean;
|
|
73
|
+
time: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Erreur levée pour toute réponse non-2xx ou tout échec réseau.
|
|
77
|
+
* `status` vaut 0 en cas d'échec réseau / timeout.
|
|
78
|
+
*/
|
|
79
|
+
declare class ETranslateError extends Error {
|
|
80
|
+
readonly status: number;
|
|
81
|
+
readonly body: unknown;
|
|
82
|
+
constructor(message: string, status: number, body?: unknown);
|
|
83
|
+
}
|
|
84
|
+
/** Client de l'API e-translate. */
|
|
85
|
+
declare class ETranslateClient {
|
|
86
|
+
private readonly baseUrl;
|
|
87
|
+
private readonly apiKey;
|
|
88
|
+
private readonly timeoutMs;
|
|
89
|
+
private readonly fetchImpl;
|
|
90
|
+
private readonly extraHeaders;
|
|
91
|
+
constructor(options: ETranslateOptions);
|
|
92
|
+
/**
|
|
93
|
+
* Traduit un texte (ou un tableau de textes).
|
|
94
|
+
* Envoie `lang_src` / `lang_dest` / `message` à `POST /translate`.
|
|
95
|
+
*/
|
|
96
|
+
translate(params: TranslateParams): Promise<TranslateResult>;
|
|
97
|
+
/** Détecte la langue d'un texte via `POST /detect`. */
|
|
98
|
+
detect(text: string): Promise<DetectedLanguage[]>;
|
|
99
|
+
/** Liste les langues supportées via `GET /languages`. */
|
|
100
|
+
languages(): Promise<Language[]>;
|
|
101
|
+
/** État du service via `GET /health` (ne nécessite pas de clé API). */
|
|
102
|
+
health(): Promise<HealthResult>;
|
|
103
|
+
private request;
|
|
104
|
+
}
|
|
105
|
+
/** Fabrique pratique équivalente à `new ETranslateClient(options)`. */
|
|
106
|
+
declare function createClient(options: ETranslateOptions): ETranslateClient;
|
|
107
|
+
|
|
108
|
+
export { type DetectedLanguage, ETranslateClient, ETranslateError, type ETranslateOptions, type HealthResult, type Language, type TranslateFormat, type TranslateParams, type TranslateResult, createClient, ETranslateClient as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* e-translate SDK — client TypeScript/JavaScript pour l'API e-translate.
|
|
3
|
+
*
|
|
4
|
+
* Fonctionne dans Node 18+ (fetch global) et dans le navigateur.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { ETranslateClient } from "e-translate-sdk";
|
|
9
|
+
*
|
|
10
|
+
* const client = new ETranslateClient({
|
|
11
|
+
* baseUrl: "https://translation.e-cosplay.fr",
|
|
12
|
+
* apiKey: "etk_xxxxxxxx",
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* const res = await client.translate({ message: "Bonjour", from: "fr", to: "en" });
|
|
16
|
+
* console.log(res.translatedText); // "Hello"
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
/** Format du texte à traduire. */
|
|
20
|
+
type TranslateFormat = "text" | "html";
|
|
21
|
+
/** Options de construction du client. */
|
|
22
|
+
interface ETranslateOptions {
|
|
23
|
+
/** URL de base du serveur, ex. "https://translation.e-cosplay.fr". */
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
/** Clé d'API (`etk_...`). Requise pour /translate, /detect, /languages. */
|
|
26
|
+
apiKey: string;
|
|
27
|
+
/** Timeout par requête en millisecondes (défaut : 20000). */
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
/** Implémentation `fetch` personnalisée (Node < 18, tests). Défaut : `globalThis.fetch`. */
|
|
30
|
+
fetch?: typeof fetch;
|
|
31
|
+
/** En-têtes HTTP supplémentaires envoyés à chaque requête. */
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
}
|
|
34
|
+
/** Paramètres de traduction. */
|
|
35
|
+
interface TranslateParams {
|
|
36
|
+
/** Texte (ou tableau de textes) à traduire. */
|
|
37
|
+
message: string | string[];
|
|
38
|
+
/** Langue source (code ISO, ex. "fr") ou "auto" pour la détection automatique. */
|
|
39
|
+
from: string;
|
|
40
|
+
/** Langue cible (code ISO, ex. "en"). */
|
|
41
|
+
to: string;
|
|
42
|
+
/** Format du texte (défaut : "text"). */
|
|
43
|
+
format?: TranslateFormat;
|
|
44
|
+
}
|
|
45
|
+
/** Langue détectée renvoyée par /detect ou en mode "auto". */
|
|
46
|
+
interface DetectedLanguage {
|
|
47
|
+
/** Indice de confiance (0–100). */
|
|
48
|
+
confidence: number;
|
|
49
|
+
/** Code ISO de la langue détectée. */
|
|
50
|
+
language: string;
|
|
51
|
+
}
|
|
52
|
+
/** Résultat d'une traduction. */
|
|
53
|
+
interface TranslateResult {
|
|
54
|
+
/** Texte traduit (string si `message` était une string, sinon string[]). */
|
|
55
|
+
translatedText: string | string[];
|
|
56
|
+
/** `true` si la réponse provient du cache Redis. */
|
|
57
|
+
cached: boolean;
|
|
58
|
+
/** Présent uniquement si `from` valait "auto". */
|
|
59
|
+
detectedLanguage?: DetectedLanguage;
|
|
60
|
+
}
|
|
61
|
+
/** Une langue supportée par le serveur. */
|
|
62
|
+
interface Language {
|
|
63
|
+
code: string;
|
|
64
|
+
name: string;
|
|
65
|
+
targets?: string[];
|
|
66
|
+
}
|
|
67
|
+
/** Réponse de /health (aucune clé requise). */
|
|
68
|
+
interface HealthResult {
|
|
69
|
+
status: string;
|
|
70
|
+
redis: boolean;
|
|
71
|
+
/** Disponibilité du moteur de traduction en amont. */
|
|
72
|
+
engine: boolean;
|
|
73
|
+
time: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Erreur levée pour toute réponse non-2xx ou tout échec réseau.
|
|
77
|
+
* `status` vaut 0 en cas d'échec réseau / timeout.
|
|
78
|
+
*/
|
|
79
|
+
declare class ETranslateError extends Error {
|
|
80
|
+
readonly status: number;
|
|
81
|
+
readonly body: unknown;
|
|
82
|
+
constructor(message: string, status: number, body?: unknown);
|
|
83
|
+
}
|
|
84
|
+
/** Client de l'API e-translate. */
|
|
85
|
+
declare class ETranslateClient {
|
|
86
|
+
private readonly baseUrl;
|
|
87
|
+
private readonly apiKey;
|
|
88
|
+
private readonly timeoutMs;
|
|
89
|
+
private readonly fetchImpl;
|
|
90
|
+
private readonly extraHeaders;
|
|
91
|
+
constructor(options: ETranslateOptions);
|
|
92
|
+
/**
|
|
93
|
+
* Traduit un texte (ou un tableau de textes).
|
|
94
|
+
* Envoie `lang_src` / `lang_dest` / `message` à `POST /translate`.
|
|
95
|
+
*/
|
|
96
|
+
translate(params: TranslateParams): Promise<TranslateResult>;
|
|
97
|
+
/** Détecte la langue d'un texte via `POST /detect`. */
|
|
98
|
+
detect(text: string): Promise<DetectedLanguage[]>;
|
|
99
|
+
/** Liste les langues supportées via `GET /languages`. */
|
|
100
|
+
languages(): Promise<Language[]>;
|
|
101
|
+
/** État du service via `GET /health` (ne nécessite pas de clé API). */
|
|
102
|
+
health(): Promise<HealthResult>;
|
|
103
|
+
private request;
|
|
104
|
+
}
|
|
105
|
+
/** Fabrique pratique équivalente à `new ETranslateClient(options)`. */
|
|
106
|
+
declare function createClient(options: ETranslateOptions): ETranslateClient;
|
|
107
|
+
|
|
108
|
+
export { type DetectedLanguage, ETranslateClient, ETranslateError, type ETranslateOptions, type HealthResult, type Language, type TranslateFormat, type TranslateParams, type TranslateResult, createClient, ETranslateClient as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var ETranslateError = class _ETranslateError extends Error {
|
|
3
|
+
constructor(message, status, body) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "ETranslateError";
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.body = body;
|
|
8
|
+
Object.setPrototypeOf(this, _ETranslateError.prototype);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var ETranslateClient = class {
|
|
12
|
+
constructor(options) {
|
|
13
|
+
if (!options || typeof options.baseUrl !== "string" || options.baseUrl.length === 0) {
|
|
14
|
+
throw new Error("ETranslate: l'option 'baseUrl' est requise.");
|
|
15
|
+
}
|
|
16
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
17
|
+
this.apiKey = options.apiKey ?? "";
|
|
18
|
+
this.timeoutMs = options.timeoutMs ?? 2e4;
|
|
19
|
+
this.extraHeaders = options.headers ?? {};
|
|
20
|
+
const f = options.fetch ?? globalThis.fetch;
|
|
21
|
+
if (typeof f !== "function") {
|
|
22
|
+
throw new Error(
|
|
23
|
+
"ETranslate: aucune impl\xE9mentation `fetch` disponible. Utilisez Node >= 18 ou passez `options.fetch`."
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
this.fetchImpl = f;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Traduit un texte (ou un tableau de textes).
|
|
30
|
+
* Envoie `lang_src` / `lang_dest` / `message` à `POST /translate`.
|
|
31
|
+
*/
|
|
32
|
+
async translate(params) {
|
|
33
|
+
if (!params || params.message == null) {
|
|
34
|
+
throw new Error("translate: 'message' est requis.");
|
|
35
|
+
}
|
|
36
|
+
if (!params.from) throw new Error("translate: 'from' est requis (ex. 'fr' ou 'auto').");
|
|
37
|
+
if (!params.to) throw new Error("translate: 'to' est requis (ex. 'en').");
|
|
38
|
+
return this.request("POST", "/translate", {
|
|
39
|
+
lang_src: params.from,
|
|
40
|
+
lang_dest: params.to,
|
|
41
|
+
message: params.message,
|
|
42
|
+
format: params.format ?? "text"
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** Détecte la langue d'un texte via `POST /detect`. */
|
|
46
|
+
async detect(text) {
|
|
47
|
+
if (!text) throw new Error("detect: 'text' est requis.");
|
|
48
|
+
return this.request("POST", "/detect", { q: text });
|
|
49
|
+
}
|
|
50
|
+
/** Liste les langues supportées via `GET /languages`. */
|
|
51
|
+
async languages() {
|
|
52
|
+
return this.request("GET", "/languages");
|
|
53
|
+
}
|
|
54
|
+
/** État du service via `GET /health` (ne nécessite pas de clé API). */
|
|
55
|
+
async health() {
|
|
56
|
+
return this.request("GET", "/health", void 0, { auth: false });
|
|
57
|
+
}
|
|
58
|
+
async request(method, path, body, opts) {
|
|
59
|
+
const useAuth = opts?.auth !== false;
|
|
60
|
+
const url = `${this.baseUrl}${path}`;
|
|
61
|
+
const headers = { Accept: "application/json", ...this.extraHeaders };
|
|
62
|
+
if (useAuth) headers["X-API-Key"] = this.apiKey;
|
|
63
|
+
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
64
|
+
const controller = new AbortController();
|
|
65
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
66
|
+
let resp;
|
|
67
|
+
try {
|
|
68
|
+
resp = await this.fetchImpl(url, {
|
|
69
|
+
method,
|
|
70
|
+
headers,
|
|
71
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
72
|
+
signal: controller.signal
|
|
73
|
+
});
|
|
74
|
+
} catch (err) {
|
|
75
|
+
const e = err;
|
|
76
|
+
const msg = e?.name === "AbortError" ? `Requ\xEAte expir\xE9e apr\xE8s ${this.timeoutMs} ms (${path}).` : `\xC9chec r\xE9seau vers ${url}: ${e?.message ?? String(err)}`;
|
|
77
|
+
throw new ETranslateError(msg, 0);
|
|
78
|
+
} finally {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
}
|
|
81
|
+
const raw = await resp.text();
|
|
82
|
+
let data = void 0;
|
|
83
|
+
if (raw) {
|
|
84
|
+
try {
|
|
85
|
+
data = JSON.parse(raw);
|
|
86
|
+
} catch {
|
|
87
|
+
data = raw;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!resp.ok) {
|
|
91
|
+
const message = data && typeof data === "object" && "error" in data ? String(data.error) : `HTTP ${resp.status} sur ${path}`;
|
|
92
|
+
throw new ETranslateError(message, resp.status, data);
|
|
93
|
+
}
|
|
94
|
+
return data;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
function createClient(options) {
|
|
98
|
+
return new ETranslateClient(options);
|
|
99
|
+
}
|
|
100
|
+
var index_default = ETranslateClient;
|
|
101
|
+
export {
|
|
102
|
+
ETranslateClient,
|
|
103
|
+
ETranslateError,
|
|
104
|
+
createClient,
|
|
105
|
+
index_default as default
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * e-translate SDK — client TypeScript/JavaScript pour l'API e-translate.\n *\n * Fonctionne dans Node 18+ (fetch global) et dans le navigateur.\n *\n * @example\n * ```ts\n * import { ETranslateClient } from \"e-translate-sdk\";\n *\n * const client = new ETranslateClient({\n * baseUrl: \"https://translation.e-cosplay.fr\",\n * apiKey: \"etk_xxxxxxxx\",\n * });\n *\n * const res = await client.translate({ message: \"Bonjour\", from: \"fr\", to: \"en\" });\n * console.log(res.translatedText); // \"Hello\"\n * ```\n */\n\n/** Format du texte à traduire. */\nexport type TranslateFormat = \"text\" | \"html\";\n\n/** Méthode HTTP interne. */\ntype HttpMethod = \"GET\" | \"POST\";\n\n/** Options de construction du client. */\nexport interface ETranslateOptions {\n /** URL de base du serveur, ex. \"https://translation.e-cosplay.fr\". */\n baseUrl: string;\n /** Clé d'API (`etk_...`). Requise pour /translate, /detect, /languages. */\n apiKey: string;\n /** Timeout par requête en millisecondes (défaut : 20000). */\n timeoutMs?: number;\n /** Implémentation `fetch` personnalisée (Node < 18, tests). Défaut : `globalThis.fetch`. */\n fetch?: typeof fetch;\n /** En-têtes HTTP supplémentaires envoyés à chaque requête. */\n headers?: Record<string, string>;\n}\n\n/** Paramètres de traduction. */\nexport interface TranslateParams {\n /** Texte (ou tableau de textes) à traduire. */\n message: string | string[];\n /** Langue source (code ISO, ex. \"fr\") ou \"auto\" pour la détection automatique. */\n from: string;\n /** Langue cible (code ISO, ex. \"en\"). */\n to: string;\n /** Format du texte (défaut : \"text\"). */\n format?: TranslateFormat;\n}\n\n/** Langue détectée renvoyée par /detect ou en mode \"auto\". */\nexport interface DetectedLanguage {\n /** Indice de confiance (0–100). */\n confidence: number;\n /** Code ISO de la langue détectée. */\n language: string;\n}\n\n/** Résultat d'une traduction. */\nexport interface TranslateResult {\n /** Texte traduit (string si `message` était une string, sinon string[]). */\n translatedText: string | string[];\n /** `true` si la réponse provient du cache Redis. */\n cached: boolean;\n /** Présent uniquement si `from` valait \"auto\". */\n detectedLanguage?: DetectedLanguage;\n}\n\n/** Une langue supportée par le serveur. */\nexport interface Language {\n code: string;\n name: string;\n targets?: string[];\n}\n\n/** Réponse de /health (aucune clé requise). */\nexport interface HealthResult {\n status: string;\n redis: boolean;\n /** Disponibilité du moteur de traduction en amont. */\n engine: boolean;\n time: string;\n}\n\n/**\n * Erreur levée pour toute réponse non-2xx ou tout échec réseau.\n * `status` vaut 0 en cas d'échec réseau / timeout.\n */\nexport class ETranslateError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = \"ETranslateError\";\n this.status = status;\n this.body = body;\n // Restaure la chaîne de prototype (TS ciblant ES5/ES2015).\n Object.setPrototypeOf(this, ETranslateError.prototype);\n }\n}\n\n/** Client de l'API e-translate. */\nexport class ETranslateClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n private readonly extraHeaders: Record<string, string>;\n\n constructor(options: ETranslateOptions) {\n if (!options || typeof options.baseUrl !== \"string\" || options.baseUrl.length === 0) {\n throw new Error(\"ETranslate: l'option 'baseUrl' est requise.\");\n }\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.apiKey = options.apiKey ?? \"\";\n this.timeoutMs = options.timeoutMs ?? 20000;\n this.extraHeaders = options.headers ?? {};\n\n const f = options.fetch ?? (globalThis as { fetch?: typeof fetch }).fetch;\n if (typeof f !== \"function\") {\n throw new Error(\n \"ETranslate: aucune implémentation `fetch` disponible. \" +\n \"Utilisez Node >= 18 ou passez `options.fetch`.\",\n );\n }\n this.fetchImpl = f;\n }\n\n /**\n * Traduit un texte (ou un tableau de textes).\n * Envoie `lang_src` / `lang_dest` / `message` à `POST /translate`.\n */\n async translate(params: TranslateParams): Promise<TranslateResult> {\n if (!params || params.message == null) {\n throw new Error(\"translate: 'message' est requis.\");\n }\n if (!params.from) throw new Error(\"translate: 'from' est requis (ex. 'fr' ou 'auto').\");\n if (!params.to) throw new Error(\"translate: 'to' est requis (ex. 'en').\");\n\n return this.request<TranslateResult>(\"POST\", \"/translate\", {\n lang_src: params.from,\n lang_dest: params.to,\n message: params.message,\n format: params.format ?? \"text\",\n });\n }\n\n /** Détecte la langue d'un texte via `POST /detect`. */\n async detect(text: string): Promise<DetectedLanguage[]> {\n if (!text) throw new Error(\"detect: 'text' est requis.\");\n return this.request<DetectedLanguage[]>(\"POST\", \"/detect\", { q: text });\n }\n\n /** Liste les langues supportées via `GET /languages`. */\n async languages(): Promise<Language[]> {\n return this.request<Language[]>(\"GET\", \"/languages\");\n }\n\n /** État du service via `GET /health` (ne nécessite pas de clé API). */\n async health(): Promise<HealthResult> {\n return this.request<HealthResult>(\"GET\", \"/health\", undefined, { auth: false });\n }\n\n private async request<T>(\n method: HttpMethod,\n path: string,\n body?: Record<string, unknown>,\n opts?: { auth?: boolean },\n ): Promise<T> {\n const useAuth = opts?.auth !== false;\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = { Accept: \"application/json\", ...this.extraHeaders };\n if (useAuth) headers[\"X-API-Key\"] = this.apiKey;\n if (body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let resp: Response;\n try {\n resp = await this.fetchImpl(url, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n } catch (err) {\n const e = err as { name?: string; message?: string };\n const msg =\n e?.name === \"AbortError\"\n ? `Requête expirée après ${this.timeoutMs} ms (${path}).`\n : `Échec réseau vers ${url}: ${e?.message ?? String(err)}`;\n throw new ETranslateError(msg, 0);\n } finally {\n clearTimeout(timer);\n }\n\n const raw = await resp.text();\n let data: unknown = undefined;\n if (raw) {\n try {\n data = JSON.parse(raw);\n } catch {\n data = raw;\n }\n }\n\n if (!resp.ok) {\n const message =\n data && typeof data === \"object\" && \"error\" in data\n ? String((data as { error: unknown }).error)\n : `HTTP ${resp.status} sur ${path}`;\n throw new ETranslateError(message, resp.status, data);\n }\n\n return data as T;\n }\n}\n\n/** Fabrique pratique équivalente à `new ETranslateClient(options)`. */\nexport function createClient(options: ETranslateOptions): ETranslateClient {\n return new ETranslateClient(options);\n}\n\nexport default ETranslateClient;\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ecosplay/e-translate",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Client SDK (TypeScript) pour l'API e-translate (translation.e-cosplay.fr).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
},
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"prepare": "tsup",
|
|
28
|
+
"typecheck": "tsc --noEmit"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"translate",
|
|
32
|
+
"translation",
|
|
33
|
+
"libretranslate",
|
|
34
|
+
"e-translate",
|
|
35
|
+
"sdk",
|
|
36
|
+
"i18n"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public",
|
|
41
|
+
"registry": "https://registry.npmjs.org/"
|
|
42
|
+
},
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "ssh://git@code.e-cosplay.fr:222/shoko/e-translate-sdk-js.git"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"tsup": "^8.3.0",
|
|
49
|
+
"typescript": "^5.6.0"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* e-translate SDK — client TypeScript/JavaScript pour l'API e-translate.
|
|
3
|
+
*
|
|
4
|
+
* Fonctionne dans Node 18+ (fetch global) et dans le navigateur.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { ETranslateClient } from "e-translate-sdk";
|
|
9
|
+
*
|
|
10
|
+
* const client = new ETranslateClient({
|
|
11
|
+
* baseUrl: "https://translation.e-cosplay.fr",
|
|
12
|
+
* apiKey: "etk_xxxxxxxx",
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* const res = await client.translate({ message: "Bonjour", from: "fr", to: "en" });
|
|
16
|
+
* console.log(res.translatedText); // "Hello"
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Format du texte à traduire. */
|
|
21
|
+
export type TranslateFormat = "text" | "html";
|
|
22
|
+
|
|
23
|
+
/** Méthode HTTP interne. */
|
|
24
|
+
type HttpMethod = "GET" | "POST";
|
|
25
|
+
|
|
26
|
+
/** Options de construction du client. */
|
|
27
|
+
export interface ETranslateOptions {
|
|
28
|
+
/** URL de base du serveur, ex. "https://translation.e-cosplay.fr". */
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
/** Clé d'API (`etk_...`). Requise pour /translate, /detect, /languages. */
|
|
31
|
+
apiKey: string;
|
|
32
|
+
/** Timeout par requête en millisecondes (défaut : 20000). */
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
/** Implémentation `fetch` personnalisée (Node < 18, tests). Défaut : `globalThis.fetch`. */
|
|
35
|
+
fetch?: typeof fetch;
|
|
36
|
+
/** En-têtes HTTP supplémentaires envoyés à chaque requête. */
|
|
37
|
+
headers?: Record<string, string>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Paramètres de traduction. */
|
|
41
|
+
export interface TranslateParams {
|
|
42
|
+
/** Texte (ou tableau de textes) à traduire. */
|
|
43
|
+
message: string | string[];
|
|
44
|
+
/** Langue source (code ISO, ex. "fr") ou "auto" pour la détection automatique. */
|
|
45
|
+
from: string;
|
|
46
|
+
/** Langue cible (code ISO, ex. "en"). */
|
|
47
|
+
to: string;
|
|
48
|
+
/** Format du texte (défaut : "text"). */
|
|
49
|
+
format?: TranslateFormat;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Langue détectée renvoyée par /detect ou en mode "auto". */
|
|
53
|
+
export interface DetectedLanguage {
|
|
54
|
+
/** Indice de confiance (0–100). */
|
|
55
|
+
confidence: number;
|
|
56
|
+
/** Code ISO de la langue détectée. */
|
|
57
|
+
language: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Résultat d'une traduction. */
|
|
61
|
+
export interface TranslateResult {
|
|
62
|
+
/** Texte traduit (string si `message` était une string, sinon string[]). */
|
|
63
|
+
translatedText: string | string[];
|
|
64
|
+
/** `true` si la réponse provient du cache Redis. */
|
|
65
|
+
cached: boolean;
|
|
66
|
+
/** Présent uniquement si `from` valait "auto". */
|
|
67
|
+
detectedLanguage?: DetectedLanguage;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Une langue supportée par le serveur. */
|
|
71
|
+
export interface Language {
|
|
72
|
+
code: string;
|
|
73
|
+
name: string;
|
|
74
|
+
targets?: string[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Réponse de /health (aucune clé requise). */
|
|
78
|
+
export interface HealthResult {
|
|
79
|
+
status: string;
|
|
80
|
+
redis: boolean;
|
|
81
|
+
/** Disponibilité du moteur de traduction en amont. */
|
|
82
|
+
engine: boolean;
|
|
83
|
+
time: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Erreur levée pour toute réponse non-2xx ou tout échec réseau.
|
|
88
|
+
* `status` vaut 0 en cas d'échec réseau / timeout.
|
|
89
|
+
*/
|
|
90
|
+
export class ETranslateError extends Error {
|
|
91
|
+
readonly status: number;
|
|
92
|
+
readonly body: unknown;
|
|
93
|
+
|
|
94
|
+
constructor(message: string, status: number, body?: unknown) {
|
|
95
|
+
super(message);
|
|
96
|
+
this.name = "ETranslateError";
|
|
97
|
+
this.status = status;
|
|
98
|
+
this.body = body;
|
|
99
|
+
// Restaure la chaîne de prototype (TS ciblant ES5/ES2015).
|
|
100
|
+
Object.setPrototypeOf(this, ETranslateError.prototype);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Client de l'API e-translate. */
|
|
105
|
+
export class ETranslateClient {
|
|
106
|
+
private readonly baseUrl: string;
|
|
107
|
+
private readonly apiKey: string;
|
|
108
|
+
private readonly timeoutMs: number;
|
|
109
|
+
private readonly fetchImpl: typeof fetch;
|
|
110
|
+
private readonly extraHeaders: Record<string, string>;
|
|
111
|
+
|
|
112
|
+
constructor(options: ETranslateOptions) {
|
|
113
|
+
if (!options || typeof options.baseUrl !== "string" || options.baseUrl.length === 0) {
|
|
114
|
+
throw new Error("ETranslate: l'option 'baseUrl' est requise.");
|
|
115
|
+
}
|
|
116
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
117
|
+
this.apiKey = options.apiKey ?? "";
|
|
118
|
+
this.timeoutMs = options.timeoutMs ?? 20000;
|
|
119
|
+
this.extraHeaders = options.headers ?? {};
|
|
120
|
+
|
|
121
|
+
const f = options.fetch ?? (globalThis as { fetch?: typeof fetch }).fetch;
|
|
122
|
+
if (typeof f !== "function") {
|
|
123
|
+
throw new Error(
|
|
124
|
+
"ETranslate: aucune implémentation `fetch` disponible. " +
|
|
125
|
+
"Utilisez Node >= 18 ou passez `options.fetch`.",
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
this.fetchImpl = f;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Traduit un texte (ou un tableau de textes).
|
|
133
|
+
* Envoie `lang_src` / `lang_dest` / `message` à `POST /translate`.
|
|
134
|
+
*/
|
|
135
|
+
async translate(params: TranslateParams): Promise<TranslateResult> {
|
|
136
|
+
if (!params || params.message == null) {
|
|
137
|
+
throw new Error("translate: 'message' est requis.");
|
|
138
|
+
}
|
|
139
|
+
if (!params.from) throw new Error("translate: 'from' est requis (ex. 'fr' ou 'auto').");
|
|
140
|
+
if (!params.to) throw new Error("translate: 'to' est requis (ex. 'en').");
|
|
141
|
+
|
|
142
|
+
return this.request<TranslateResult>("POST", "/translate", {
|
|
143
|
+
lang_src: params.from,
|
|
144
|
+
lang_dest: params.to,
|
|
145
|
+
message: params.message,
|
|
146
|
+
format: params.format ?? "text",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Détecte la langue d'un texte via `POST /detect`. */
|
|
151
|
+
async detect(text: string): Promise<DetectedLanguage[]> {
|
|
152
|
+
if (!text) throw new Error("detect: 'text' est requis.");
|
|
153
|
+
return this.request<DetectedLanguage[]>("POST", "/detect", { q: text });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Liste les langues supportées via `GET /languages`. */
|
|
157
|
+
async languages(): Promise<Language[]> {
|
|
158
|
+
return this.request<Language[]>("GET", "/languages");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** État du service via `GET /health` (ne nécessite pas de clé API). */
|
|
162
|
+
async health(): Promise<HealthResult> {
|
|
163
|
+
return this.request<HealthResult>("GET", "/health", undefined, { auth: false });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private async request<T>(
|
|
167
|
+
method: HttpMethod,
|
|
168
|
+
path: string,
|
|
169
|
+
body?: Record<string, unknown>,
|
|
170
|
+
opts?: { auth?: boolean },
|
|
171
|
+
): Promise<T> {
|
|
172
|
+
const useAuth = opts?.auth !== false;
|
|
173
|
+
const url = `${this.baseUrl}${path}`;
|
|
174
|
+
|
|
175
|
+
const headers: Record<string, string> = { Accept: "application/json", ...this.extraHeaders };
|
|
176
|
+
if (useAuth) headers["X-API-Key"] = this.apiKey;
|
|
177
|
+
if (body !== undefined) headers["Content-Type"] = "application/json";
|
|
178
|
+
|
|
179
|
+
const controller = new AbortController();
|
|
180
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
181
|
+
|
|
182
|
+
let resp: Response;
|
|
183
|
+
try {
|
|
184
|
+
resp = await this.fetchImpl(url, {
|
|
185
|
+
method,
|
|
186
|
+
headers,
|
|
187
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
188
|
+
signal: controller.signal,
|
|
189
|
+
});
|
|
190
|
+
} catch (err) {
|
|
191
|
+
const e = err as { name?: string; message?: string };
|
|
192
|
+
const msg =
|
|
193
|
+
e?.name === "AbortError"
|
|
194
|
+
? `Requête expirée après ${this.timeoutMs} ms (${path}).`
|
|
195
|
+
: `Échec réseau vers ${url}: ${e?.message ?? String(err)}`;
|
|
196
|
+
throw new ETranslateError(msg, 0);
|
|
197
|
+
} finally {
|
|
198
|
+
clearTimeout(timer);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const raw = await resp.text();
|
|
202
|
+
let data: unknown = undefined;
|
|
203
|
+
if (raw) {
|
|
204
|
+
try {
|
|
205
|
+
data = JSON.parse(raw);
|
|
206
|
+
} catch {
|
|
207
|
+
data = raw;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!resp.ok) {
|
|
212
|
+
const message =
|
|
213
|
+
data && typeof data === "object" && "error" in data
|
|
214
|
+
? String((data as { error: unknown }).error)
|
|
215
|
+
: `HTTP ${resp.status} sur ${path}`;
|
|
216
|
+
throw new ETranslateError(message, resp.status, data);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return data as T;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Fabrique pratique équivalente à `new ETranslateClient(options)`. */
|
|
224
|
+
export function createClient(options: ETranslateOptions): ETranslateClient {
|
|
225
|
+
return new ETranslateClient(options);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export default ETranslateClient;
|