@form-engine-ts/translator-google-v3 2.9.0 → 2.9.1

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 CHANGED
@@ -28,6 +28,12 @@ const translator = createGoogleV3Translator({
28
28
  const japanese = await translator.translateText("Thank you", "ja", "en");
29
29
  ```
30
30
 
31
+ Set `glossaryConfig` to a glossary ID or full resource name. An ID is expanded using the configured project and
32
+ location. Use `glossaryResolver` to select a glossary per source/target locale pair; returning `undefined` omits the
33
+ glossary from that request. Glossary responses prefer `glossaryTranslations` and fall back to `translations` per item.
34
+ When this adapter is wrapped with `@form-engine-ts/translator-cache`, the applied glossary resource is included in the
35
+ cache variant automatically.
36
+
31
37
  Keep OAuth access tokens on a trusted server. `translateBatch` defaults to at most 250 items and 25,000 UTF-8 bytes per
32
38
  request (hard limits: 1,024 items and 30,000 bytes). Network errors, HTTP 429, and HTTP 5xx responses are retried; customize
33
39
  batch and retry behavior with `batchLimits` and `retry`.
package/dist/index.cjs CHANGED
@@ -84,27 +84,39 @@ function endpoint(value) {
84
84
  throw new TypeError("apiEndpoint must be a valid absolute URL.", { cause });
85
85
  }
86
86
  }
87
- function parseTranslations(body, expected, useGlossary) {
87
+ function parseTranslations(body, expected) {
88
88
  let parsed;
89
89
  try {
90
90
  parsed = JSON.parse(body);
91
91
  } catch (cause) {
92
92
  throw new Error("Google Translation Advanced returned invalid JSON.", { cause });
93
93
  }
94
- const preferredKey = useGlossary ? "glossaryTranslations" : "translations";
95
- const fallbackKey = useGlossary ? "translations" : "glossaryTranslations";
96
- const values = isRecord(parsed) ? Array.isArray(parsed[preferredKey]) ? parsed[preferredKey] : parsed[fallbackKey] : void 0;
97
- if (!Array.isArray(values)) throw new Error("Google Translation Advanced response is missing translations.");
98
- if (values.length !== expected) {
99
- throw new Error(`Google Translation Advanced returned ${values.length} translations for ${expected} texts.`);
94
+ const glossaryTranslations = isRecord(parsed) && Array.isArray(parsed.glossaryTranslations) ? parsed.glossaryTranslations : [];
95
+ const translations = isRecord(parsed) && Array.isArray(parsed.translations) ? parsed.translations : [];
96
+ if (glossaryTranslations.length === 0 && translations.length === 0) {
97
+ throw new Error("Google Translation Advanced response is missing translations.");
100
98
  }
101
- return values.map((value, index) => {
102
- if (!isRecord(value) || typeof value.translatedText !== "string") {
103
- throw new Error(`Google Translation Advanced result at index ${index} is invalid.`);
99
+ return Array.from({ length: expected }, (_, index) => {
100
+ const glossaryValue = glossaryTranslations[index];
101
+ if (isRecord(glossaryValue) && typeof glossaryValue.translatedText === "string") {
102
+ return glossaryValue.translatedText;
104
103
  }
105
- return value.translatedText;
104
+ const value = translations[index];
105
+ if (isRecord(value) && typeof value.translatedText === "string") return value.translatedText;
106
+ throw new Error(`Google Translation Advanced result at index ${index} is invalid.`);
106
107
  });
107
108
  }
109
+ function normalizeGlossary(value, projectId, location, name) {
110
+ const config = typeof value === "string" ? { glossary: value } : value;
111
+ const glossary = requireNonEmpty(config.glossary, `${name}.glossary`);
112
+ if (config.ignoreCase !== void 0 && typeof config.ignoreCase !== "boolean") {
113
+ throw new TypeError(`${name}.ignoreCase must be a boolean.`);
114
+ }
115
+ return {
116
+ glossary: glossary.startsWith("projects/") ? glossary : `projects/${projectId}/locations/${location}/glossaries/${glossary}`,
117
+ ...config.ignoreCase === void 0 ? {} : { ignoreCase: config.ignoreCase }
118
+ };
119
+ }
108
120
  function validateLabels(labels) {
109
121
  if (labels === void 0) return void 0;
110
122
  for (const [key, value] of Object.entries(labels)) {
@@ -123,11 +135,14 @@ function retryAfterMilliseconds(value, now) {
123
135
  function createGoogleV3Translator(options) {
124
136
  const projectId = requireNonEmpty(options?.projectId, "projectId");
125
137
  const location = requireNonEmpty(options.location ?? "global", "location");
126
- if (typeof options.getAccessToken !== "function") throw new TypeError("getAccessToken must be a function.");
127
138
  const apiEndpoint = endpoint(options.apiEndpoint);
128
- const fetchImpl = options.fetchFn ?? globalThis.fetch;
139
+ const fetchImpl = options.fetchFn ?? options.fetchImpl ?? globalThis.fetch;
129
140
  if (typeof fetchImpl !== "function")
130
141
  throw new Error("Fetch is unavailable. Pass fetchFn when creating the translator.");
142
+ if (typeof options.getAccessToken !== "function" && options.apiKey === void 0) {
143
+ throw new TypeError("getAccessToken or apiKey must be provided.");
144
+ }
145
+ const apiKey = options.apiKey === void 0 ? void 0 : requireNonEmpty(options.apiKey, "apiKey");
131
146
  const batchLimits = {
132
147
  maxItems: options.batchLimits?.maxItems ?? options.maxBatchSize ?? DEFAULT_MAX_ITEMS,
133
148
  maxCharacters: options.batchLimits?.maxCharacters ?? DEFAULT_MAX_CHARACTERS
@@ -147,13 +162,26 @@ function createGoogleV3Translator(options) {
147
162
  const random = options.random ?? Math.random;
148
163
  const now = options.now ?? Date.now;
149
164
  const labels = validateLabels(options.labels);
150
- const glossaryConfig = options.glossaryConfig === void 0 ? void 0 : {
151
- glossary: requireNonEmpty(options.glossaryConfig.glossary, "glossaryConfig.glossary"),
152
- ...options.glossaryConfig.ignoreCase === void 0 ? {} : { ignoreCase: options.glossaryConfig.ignoreCase }
165
+ if (options.glossaryResolver !== void 0 && typeof options.glossaryResolver !== "function") {
166
+ throw new TypeError("glossaryResolver must be a function.");
167
+ }
168
+ const staticGlossaryConfig = options.glossaryConfig === void 0 ? void 0 : normalizeGlossary(options.glossaryConfig, projectId, location, "glossaryConfig");
169
+ const baseRequestUrl = `${apiEndpoint}/projects/${encodeURIComponent(projectId)}/locations/${encodeURIComponent(location)}:translateText`;
170
+ const requestUrl = apiKey === void 0 ? baseRequestUrl : `${baseRequestUrl}?key=${encodeURIComponent(apiKey)}`;
171
+ const resolveGlossary = (sourceLocale, targetLocale) => {
172
+ if (options.glossaryResolver !== void 0) {
173
+ const resolved = options.glossaryResolver({ sourceLocale: sourceLocale ?? "auto", targetLocale });
174
+ return resolved === void 0 ? void 0 : normalizeGlossary(resolved, projectId, location, "glossaryResolver result");
175
+ }
176
+ return staticGlossaryConfig;
153
177
  };
154
- const requestUrl = `${apiEndpoint}/projects/${encodeURIComponent(projectId)}/locations/${encodeURIComponent(location)}:translateText`;
155
- const translateChunk = async (texts, targetLocale, sourceLocale) => {
156
- const token = requireNonEmpty(await options.getAccessToken(), "accessToken");
178
+ const getCacheVariant = (targetLocale, sourceLocale) => {
179
+ const target = requireNonEmpty(targetLocale, "targetLocale");
180
+ const source = sourceLocale === void 0 ? void 0 : requireNonEmpty(sourceLocale, "sourceLocale");
181
+ return resolveGlossary(source, target)?.glossary;
182
+ };
183
+ const translateChunk = async (texts, targetLocale, sourceLocale, glossaryConfig) => {
184
+ const token = options.getAccessToken === void 0 ? void 0 : requireNonEmpty(await options.getAccessToken(), "accessToken");
157
185
  const body = JSON.stringify({
158
186
  contents: texts,
159
187
  mimeType: "text/plain",
@@ -167,7 +195,10 @@ function createGoogleV3Translator(options) {
167
195
  try {
168
196
  response = await fetchImpl(requestUrl, {
169
197
  method: "POST",
170
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
198
+ headers: {
199
+ ...token === void 0 ? {} : { Authorization: `Bearer ${token}` },
200
+ "Content-Type": "application/json"
201
+ },
171
202
  body
172
203
  });
173
204
  } catch (cause) {
@@ -177,7 +208,7 @@ function createGoogleV3Translator(options) {
177
208
  }
178
209
  if (response?.ok === true) {
179
210
  return {
180
- translations: parseTranslations(await response.text(), texts.length, glossaryConfig !== void 0),
211
+ translations: parseTranslations(await response.text(), texts.length),
181
212
  retryAttempts: attempt
182
213
  };
183
214
  }
@@ -191,7 +222,8 @@ function createGoogleV3Translator(options) {
191
222
  continue;
192
223
  }
193
224
  if (response === void 0) throw new Error("Google Translation Advanced request failed.");
194
- const detail = (await response.text()).replaceAll(token, "[redacted]");
225
+ const responseText = await response.text();
226
+ const detail = token === void 0 ? responseText : responseText.replaceAll(token, "[redacted]");
195
227
  throw new Error(
196
228
  `Google Translation Advanced request failed with HTTP ${response.status}${detail.length === 0 ? "." : `: ${detail}`}`
197
229
  );
@@ -203,6 +235,7 @@ function createGoogleV3Translator(options) {
203
235
  }
204
236
  const target = requireNonEmpty(targetLocale, "targetLocale");
205
237
  const source = sourceLocale === void 0 ? void 0 : requireNonEmpty(sourceLocale, "sourceLocale");
238
+ const glossary = resolveGlossary(source, target);
206
239
  const nonEmptyEntries = texts.flatMap((text, index) => text.trim().length === 0 ? [] : [{ text, index }]);
207
240
  const nonEmptyTexts = nonEmptyEntries.map((entry) => entry.text);
208
241
  const chunks = splitTranslationBatch(nonEmptyTexts, batchLimits);
@@ -210,7 +243,7 @@ function createGoogleV3Translator(options) {
210
243
  const translated = [];
211
244
  let retryAttempts = 0;
212
245
  for (const chunk of chunks) {
213
- const result = await translateChunk(chunk, target, source);
246
+ const result = await translateChunk(chunk, target, source, glossary);
214
247
  translated.push(...result.translations);
215
248
  retryAttempts += result.retryAttempts;
216
249
  }
@@ -227,11 +260,13 @@ function createGoogleV3Translator(options) {
227
260
  durationMs: Math.max(0, now() - startedAt),
228
261
  cacheHitCount: 0,
229
262
  cacheMissCount: nonEmptyTexts.length,
230
- evictionCount: 0
263
+ evictionCount: 0,
264
+ ...glossary === void 0 ? {} : { glossary: glossary.glossary }
231
265
  });
232
266
  return restored;
233
267
  };
234
- return {
268
+ const translator = {
269
+ getCacheVariant,
235
270
  async translateText(text, targetLocale, sourceLocale) {
236
271
  if (typeof text !== "string") throw new TypeError("text must be a string.");
237
272
  const translated = await translateBatch([text], targetLocale, sourceLocale);
@@ -241,6 +276,7 @@ function createGoogleV3Translator(options) {
241
276
  },
242
277
  translateBatch
243
278
  };
279
+ return translator;
244
280
  }
245
281
  // Annotate the CommonJS export names for ESM import in node:
246
282
  0 && (module.exports = {
package/dist/index.d.cts CHANGED
@@ -1,16 +1,29 @@
1
1
  import { AsyncTranslationAdapter } from '@form-engine-ts/core';
2
2
 
3
3
  interface GoogleV3GlossaryConfig {
4
+ /** Glossary ID or fully qualified glossary resource name. */
4
5
  readonly glossary: string;
6
+ /** Whether glossary matching should ignore case. */
5
7
  readonly ignoreCase?: boolean;
6
8
  }
9
+ type GlossaryResolver = (context: {
10
+ readonly sourceLocale: string;
11
+ readonly targetLocale: string;
12
+ }) => string | GoogleV3GlossaryConfig | undefined;
13
+ interface GoogleV3TranslationAdapter extends AsyncTranslationAdapter {
14
+ /** Returns the glossary resource used for a locale pair, for cache-key isolation. */
15
+ getCacheVariant(targetLocale: string, sourceLocale?: string): string | undefined;
16
+ }
7
17
  interface GoogleV3TranslatorOptions {
8
18
  readonly projectId: string;
9
19
  readonly location?: string;
10
- readonly getAccessToken: () => Promise<string> | string;
11
- readonly glossaryConfig?: GoogleV3GlossaryConfig;
20
+ readonly getAccessToken?: () => Promise<string> | string;
21
+ readonly apiKey?: string;
22
+ readonly glossaryConfig?: GoogleV3GlossaryConfig | string;
23
+ readonly glossaryResolver?: GlossaryResolver;
12
24
  readonly labels?: Readonly<Record<string, string>>;
13
25
  readonly fetchFn?: typeof fetch;
26
+ readonly fetchImpl?: typeof fetch;
14
27
  readonly apiEndpoint?: string;
15
28
  readonly batchLimits?: BatchSplitLimits;
16
29
  readonly retry?: RetryConfig;
@@ -41,8 +54,9 @@ interface TranslationBatchReport {
41
54
  readonly cacheHitCount: number;
42
55
  readonly cacheMissCount: number;
43
56
  readonly evictionCount: number;
57
+ readonly glossary?: string;
44
58
  }
45
59
  declare function splitTranslationBatch(texts: readonly string[], limits?: BatchSplitLimits): string[][];
46
60
  declare function createGoogleV3Translator(options: GoogleV3TranslatorOptions): AsyncTranslationAdapter;
47
61
 
48
- export { type BatchSplitLimits, type GoogleV3GlossaryConfig, type GoogleV3TranslatorOptions, type RetryConfig, type TranslationBatchReport, createGoogleV3Translator, splitTranslationBatch };
62
+ export { type BatchSplitLimits, type GlossaryResolver, type GoogleV3GlossaryConfig, type GoogleV3TranslationAdapter, type GoogleV3TranslatorOptions, type RetryConfig, type TranslationBatchReport, createGoogleV3Translator, splitTranslationBatch };
package/dist/index.d.ts CHANGED
@@ -1,16 +1,29 @@
1
1
  import { AsyncTranslationAdapter } from '@form-engine-ts/core';
2
2
 
3
3
  interface GoogleV3GlossaryConfig {
4
+ /** Glossary ID or fully qualified glossary resource name. */
4
5
  readonly glossary: string;
6
+ /** Whether glossary matching should ignore case. */
5
7
  readonly ignoreCase?: boolean;
6
8
  }
9
+ type GlossaryResolver = (context: {
10
+ readonly sourceLocale: string;
11
+ readonly targetLocale: string;
12
+ }) => string | GoogleV3GlossaryConfig | undefined;
13
+ interface GoogleV3TranslationAdapter extends AsyncTranslationAdapter {
14
+ /** Returns the glossary resource used for a locale pair, for cache-key isolation. */
15
+ getCacheVariant(targetLocale: string, sourceLocale?: string): string | undefined;
16
+ }
7
17
  interface GoogleV3TranslatorOptions {
8
18
  readonly projectId: string;
9
19
  readonly location?: string;
10
- readonly getAccessToken: () => Promise<string> | string;
11
- readonly glossaryConfig?: GoogleV3GlossaryConfig;
20
+ readonly getAccessToken?: () => Promise<string> | string;
21
+ readonly apiKey?: string;
22
+ readonly glossaryConfig?: GoogleV3GlossaryConfig | string;
23
+ readonly glossaryResolver?: GlossaryResolver;
12
24
  readonly labels?: Readonly<Record<string, string>>;
13
25
  readonly fetchFn?: typeof fetch;
26
+ readonly fetchImpl?: typeof fetch;
14
27
  readonly apiEndpoint?: string;
15
28
  readonly batchLimits?: BatchSplitLimits;
16
29
  readonly retry?: RetryConfig;
@@ -41,8 +54,9 @@ interface TranslationBatchReport {
41
54
  readonly cacheHitCount: number;
42
55
  readonly cacheMissCount: number;
43
56
  readonly evictionCount: number;
57
+ readonly glossary?: string;
44
58
  }
45
59
  declare function splitTranslationBatch(texts: readonly string[], limits?: BatchSplitLimits): string[][];
46
60
  declare function createGoogleV3Translator(options: GoogleV3TranslatorOptions): AsyncTranslationAdapter;
47
61
 
48
- export { type BatchSplitLimits, type GoogleV3GlossaryConfig, type GoogleV3TranslatorOptions, type RetryConfig, type TranslationBatchReport, createGoogleV3Translator, splitTranslationBatch };
62
+ export { type BatchSplitLimits, type GlossaryResolver, type GoogleV3GlossaryConfig, type GoogleV3TranslationAdapter, type GoogleV3TranslatorOptions, type RetryConfig, type TranslationBatchReport, createGoogleV3Translator, splitTranslationBatch };
package/dist/index.js CHANGED
@@ -59,27 +59,39 @@ function endpoint(value) {
59
59
  throw new TypeError("apiEndpoint must be a valid absolute URL.", { cause });
60
60
  }
61
61
  }
62
- function parseTranslations(body, expected, useGlossary) {
62
+ function parseTranslations(body, expected) {
63
63
  let parsed;
64
64
  try {
65
65
  parsed = JSON.parse(body);
66
66
  } catch (cause) {
67
67
  throw new Error("Google Translation Advanced returned invalid JSON.", { cause });
68
68
  }
69
- const preferredKey = useGlossary ? "glossaryTranslations" : "translations";
70
- const fallbackKey = useGlossary ? "translations" : "glossaryTranslations";
71
- const values = isRecord(parsed) ? Array.isArray(parsed[preferredKey]) ? parsed[preferredKey] : parsed[fallbackKey] : void 0;
72
- if (!Array.isArray(values)) throw new Error("Google Translation Advanced response is missing translations.");
73
- if (values.length !== expected) {
74
- throw new Error(`Google Translation Advanced returned ${values.length} translations for ${expected} texts.`);
69
+ const glossaryTranslations = isRecord(parsed) && Array.isArray(parsed.glossaryTranslations) ? parsed.glossaryTranslations : [];
70
+ const translations = isRecord(parsed) && Array.isArray(parsed.translations) ? parsed.translations : [];
71
+ if (glossaryTranslations.length === 0 && translations.length === 0) {
72
+ throw new Error("Google Translation Advanced response is missing translations.");
75
73
  }
76
- return values.map((value, index) => {
77
- if (!isRecord(value) || typeof value.translatedText !== "string") {
78
- throw new Error(`Google Translation Advanced result at index ${index} is invalid.`);
74
+ return Array.from({ length: expected }, (_, index) => {
75
+ const glossaryValue = glossaryTranslations[index];
76
+ if (isRecord(glossaryValue) && typeof glossaryValue.translatedText === "string") {
77
+ return glossaryValue.translatedText;
79
78
  }
80
- return value.translatedText;
79
+ const value = translations[index];
80
+ if (isRecord(value) && typeof value.translatedText === "string") return value.translatedText;
81
+ throw new Error(`Google Translation Advanced result at index ${index} is invalid.`);
81
82
  });
82
83
  }
84
+ function normalizeGlossary(value, projectId, location, name) {
85
+ const config = typeof value === "string" ? { glossary: value } : value;
86
+ const glossary = requireNonEmpty(config.glossary, `${name}.glossary`);
87
+ if (config.ignoreCase !== void 0 && typeof config.ignoreCase !== "boolean") {
88
+ throw new TypeError(`${name}.ignoreCase must be a boolean.`);
89
+ }
90
+ return {
91
+ glossary: glossary.startsWith("projects/") ? glossary : `projects/${projectId}/locations/${location}/glossaries/${glossary}`,
92
+ ...config.ignoreCase === void 0 ? {} : { ignoreCase: config.ignoreCase }
93
+ };
94
+ }
83
95
  function validateLabels(labels) {
84
96
  if (labels === void 0) return void 0;
85
97
  for (const [key, value] of Object.entries(labels)) {
@@ -98,11 +110,14 @@ function retryAfterMilliseconds(value, now) {
98
110
  function createGoogleV3Translator(options) {
99
111
  const projectId = requireNonEmpty(options?.projectId, "projectId");
100
112
  const location = requireNonEmpty(options.location ?? "global", "location");
101
- if (typeof options.getAccessToken !== "function") throw new TypeError("getAccessToken must be a function.");
102
113
  const apiEndpoint = endpoint(options.apiEndpoint);
103
- const fetchImpl = options.fetchFn ?? globalThis.fetch;
114
+ const fetchImpl = options.fetchFn ?? options.fetchImpl ?? globalThis.fetch;
104
115
  if (typeof fetchImpl !== "function")
105
116
  throw new Error("Fetch is unavailable. Pass fetchFn when creating the translator.");
117
+ if (typeof options.getAccessToken !== "function" && options.apiKey === void 0) {
118
+ throw new TypeError("getAccessToken or apiKey must be provided.");
119
+ }
120
+ const apiKey = options.apiKey === void 0 ? void 0 : requireNonEmpty(options.apiKey, "apiKey");
106
121
  const batchLimits = {
107
122
  maxItems: options.batchLimits?.maxItems ?? options.maxBatchSize ?? DEFAULT_MAX_ITEMS,
108
123
  maxCharacters: options.batchLimits?.maxCharacters ?? DEFAULT_MAX_CHARACTERS
@@ -122,13 +137,26 @@ function createGoogleV3Translator(options) {
122
137
  const random = options.random ?? Math.random;
123
138
  const now = options.now ?? Date.now;
124
139
  const labels = validateLabels(options.labels);
125
- const glossaryConfig = options.glossaryConfig === void 0 ? void 0 : {
126
- glossary: requireNonEmpty(options.glossaryConfig.glossary, "glossaryConfig.glossary"),
127
- ...options.glossaryConfig.ignoreCase === void 0 ? {} : { ignoreCase: options.glossaryConfig.ignoreCase }
140
+ if (options.glossaryResolver !== void 0 && typeof options.glossaryResolver !== "function") {
141
+ throw new TypeError("glossaryResolver must be a function.");
142
+ }
143
+ const staticGlossaryConfig = options.glossaryConfig === void 0 ? void 0 : normalizeGlossary(options.glossaryConfig, projectId, location, "glossaryConfig");
144
+ const baseRequestUrl = `${apiEndpoint}/projects/${encodeURIComponent(projectId)}/locations/${encodeURIComponent(location)}:translateText`;
145
+ const requestUrl = apiKey === void 0 ? baseRequestUrl : `${baseRequestUrl}?key=${encodeURIComponent(apiKey)}`;
146
+ const resolveGlossary = (sourceLocale, targetLocale) => {
147
+ if (options.glossaryResolver !== void 0) {
148
+ const resolved = options.glossaryResolver({ sourceLocale: sourceLocale ?? "auto", targetLocale });
149
+ return resolved === void 0 ? void 0 : normalizeGlossary(resolved, projectId, location, "glossaryResolver result");
150
+ }
151
+ return staticGlossaryConfig;
128
152
  };
129
- const requestUrl = `${apiEndpoint}/projects/${encodeURIComponent(projectId)}/locations/${encodeURIComponent(location)}:translateText`;
130
- const translateChunk = async (texts, targetLocale, sourceLocale) => {
131
- const token = requireNonEmpty(await options.getAccessToken(), "accessToken");
153
+ const getCacheVariant = (targetLocale, sourceLocale) => {
154
+ const target = requireNonEmpty(targetLocale, "targetLocale");
155
+ const source = sourceLocale === void 0 ? void 0 : requireNonEmpty(sourceLocale, "sourceLocale");
156
+ return resolveGlossary(source, target)?.glossary;
157
+ };
158
+ const translateChunk = async (texts, targetLocale, sourceLocale, glossaryConfig) => {
159
+ const token = options.getAccessToken === void 0 ? void 0 : requireNonEmpty(await options.getAccessToken(), "accessToken");
132
160
  const body = JSON.stringify({
133
161
  contents: texts,
134
162
  mimeType: "text/plain",
@@ -142,7 +170,10 @@ function createGoogleV3Translator(options) {
142
170
  try {
143
171
  response = await fetchImpl(requestUrl, {
144
172
  method: "POST",
145
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
173
+ headers: {
174
+ ...token === void 0 ? {} : { Authorization: `Bearer ${token}` },
175
+ "Content-Type": "application/json"
176
+ },
146
177
  body
147
178
  });
148
179
  } catch (cause) {
@@ -152,7 +183,7 @@ function createGoogleV3Translator(options) {
152
183
  }
153
184
  if (response?.ok === true) {
154
185
  return {
155
- translations: parseTranslations(await response.text(), texts.length, glossaryConfig !== void 0),
186
+ translations: parseTranslations(await response.text(), texts.length),
156
187
  retryAttempts: attempt
157
188
  };
158
189
  }
@@ -166,7 +197,8 @@ function createGoogleV3Translator(options) {
166
197
  continue;
167
198
  }
168
199
  if (response === void 0) throw new Error("Google Translation Advanced request failed.");
169
- const detail = (await response.text()).replaceAll(token, "[redacted]");
200
+ const responseText = await response.text();
201
+ const detail = token === void 0 ? responseText : responseText.replaceAll(token, "[redacted]");
170
202
  throw new Error(
171
203
  `Google Translation Advanced request failed with HTTP ${response.status}${detail.length === 0 ? "." : `: ${detail}`}`
172
204
  );
@@ -178,6 +210,7 @@ function createGoogleV3Translator(options) {
178
210
  }
179
211
  const target = requireNonEmpty(targetLocale, "targetLocale");
180
212
  const source = sourceLocale === void 0 ? void 0 : requireNonEmpty(sourceLocale, "sourceLocale");
213
+ const glossary = resolveGlossary(source, target);
181
214
  const nonEmptyEntries = texts.flatMap((text, index) => text.trim().length === 0 ? [] : [{ text, index }]);
182
215
  const nonEmptyTexts = nonEmptyEntries.map((entry) => entry.text);
183
216
  const chunks = splitTranslationBatch(nonEmptyTexts, batchLimits);
@@ -185,7 +218,7 @@ function createGoogleV3Translator(options) {
185
218
  const translated = [];
186
219
  let retryAttempts = 0;
187
220
  for (const chunk of chunks) {
188
- const result = await translateChunk(chunk, target, source);
221
+ const result = await translateChunk(chunk, target, source, glossary);
189
222
  translated.push(...result.translations);
190
223
  retryAttempts += result.retryAttempts;
191
224
  }
@@ -202,11 +235,13 @@ function createGoogleV3Translator(options) {
202
235
  durationMs: Math.max(0, now() - startedAt),
203
236
  cacheHitCount: 0,
204
237
  cacheMissCount: nonEmptyTexts.length,
205
- evictionCount: 0
238
+ evictionCount: 0,
239
+ ...glossary === void 0 ? {} : { glossary: glossary.glossary }
206
240
  });
207
241
  return restored;
208
242
  };
209
- return {
243
+ const translator = {
244
+ getCacheVariant,
210
245
  async translateText(text, targetLocale, sourceLocale) {
211
246
  if (typeof text !== "string") throw new TypeError("text must be a string.");
212
247
  const translated = await translateBatch([text], targetLocale, sourceLocale);
@@ -216,6 +251,7 @@ function createGoogleV3Translator(options) {
216
251
  },
217
252
  translateBatch
218
253
  };
254
+ return translator;
219
255
  }
220
256
  export {
221
257
  createGoogleV3Translator,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@form-engine-ts/translator-google-v3",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -40,7 +40,7 @@
40
40
  "typescript"
41
41
  ],
42
42
  "dependencies": {
43
- "@form-engine-ts/core": "2.9.0"
43
+ "@form-engine-ts/core": "2.9.1"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup src/index.ts --format esm,cjs --dts --clean --external @form-engine-ts/core",