@pigment/auto-translate 1.2.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.
Files changed (34) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +405 -0
  3. package/dist/collections/translationExclusions.d.ts +2 -0
  4. package/dist/collections/translationExclusions.js +78 -0
  5. package/dist/collections/translationExclusions.js.map +1 -0
  6. package/dist/components/TranslationControl.css +92 -0
  7. package/dist/components/TranslationControl.d.ts +18 -0
  8. package/dist/components/TranslationControl.js +274 -0
  9. package/dist/components/TranslationControl.js.map +1 -0
  10. package/dist/exports/client.d.ts +5 -0
  11. package/dist/exports/client.js +5 -0
  12. package/dist/exports/client.js.map +1 -0
  13. package/dist/exports/rsc.d.ts +5 -0
  14. package/dist/exports/rsc.js +5 -0
  15. package/dist/exports/rsc.js.map +1 -0
  16. package/dist/globals/translationSettings.d.ts +2 -0
  17. package/dist/globals/translationSettings.js +78 -0
  18. package/dist/globals/translationSettings.js.map +1 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.js +254 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/services/translationService.d.ts +60 -0
  23. package/dist/services/translationService.js +533 -0
  24. package/dist/services/translationService.js.map +1 -0
  25. package/dist/types/index.d.ts +103 -0
  26. package/dist/types/index.js +3 -0
  27. package/dist/types/index.js.map +1 -0
  28. package/dist/utilities/fieldHelpers.d.ts +34 -0
  29. package/dist/utilities/fieldHelpers.js +180 -0
  30. package/dist/utilities/fieldHelpers.js.map +1 -0
  31. package/dist/utilities/injectTranslationControls.d.ts +5 -0
  32. package/dist/utilities/injectTranslationControls.js +92 -0
  33. package/dist/utilities/injectTranslationControls.js.map +1 -0
  34. package/package.json +114 -0
package/dist/index.js ADDED
@@ -0,0 +1,254 @@
1
+ import { getTranslationExclusionsCollection } from './collections/translationExclusions.js';
2
+ import { getTranslationSettingsGlobal } from './globals/translationSettings.js';
3
+ import { TranslationService } from './services/translationService.js';
4
+ import { injectTranslationControls } from './utilities/injectTranslationControls.js';
5
+ export * from './types/index.js';
6
+ export { getTranslationExclusionsCollection } from './collections/translationExclusions.js';
7
+ export { getTranslationSettingsGlobal } from './globals/translationSettings.js';
8
+ export const autoTranslate = (pluginOptions)=>(config)=>{
9
+ // Validate configuration
10
+ if (!config.collections) {
11
+ config.collections = [];
12
+ }
13
+ if (!config.localization) {
14
+ console.warn('[Auto-Translate Plugin] No localization config found. Plugin will not function properly.');
15
+ return config;
16
+ }
17
+ const localizationConfig = config.localization;
18
+ const defaultLocale = localizationConfig.defaultLocale;
19
+ const allLocales = Array.isArray(localizationConfig.locales) ? localizationConfig.locales.map((l)=>typeof l === 'string' ? l : l.code) : [];
20
+ // Default enableExclusions to true for backward compatibility
21
+ const enableExclusions = pluginOptions.enableExclusions !== false;
22
+ if (pluginOptions.debugging) {
23
+ console.log('[Auto-Translate Plugin] Configuration:');
24
+ console.log('- Default locale:', defaultLocale);
25
+ console.log('- All locales:', allLocales);
26
+ console.log('- Enabled collections:', Object.keys(pluginOptions.collections || {}));
27
+ console.log('- Exclusions enabled:', enableExclusions);
28
+ }
29
+ // Add translation exclusions collection (only if exclusions are enabled)
30
+ if (enableExclusions) {
31
+ const exclusionsSlug = pluginOptions.translationExclusionsSlug || 'translation-exclusions';
32
+ config.collections.push(getTranslationExclusionsCollection(exclusionsSlug));
33
+ }
34
+ // Add translation settings global
35
+ if (!config.globals) {
36
+ config.globals = [];
37
+ }
38
+ const settingsSlug = pluginOptions.translationSettingsSlug || 'translation-settings';
39
+ config.globals.push(getTranslationSettingsGlobal(settingsSlug));
40
+ // Initialize translation service
41
+ const translationService = new TranslationService(pluginOptions);
42
+ // Configure collections with auto-translate
43
+ if (pluginOptions.collections) {
44
+ for(const collectionSlug in pluginOptions.collections){
45
+ const collectionConfig = pluginOptions.collections[collectionSlug];
46
+ // Skip if disabled
47
+ if (collectionConfig === false || typeof collectionConfig === 'object' && collectionConfig.enabled === false) {
48
+ continue;
49
+ }
50
+ const collection = config.collections.find((c)=>c.slug === collectionSlug);
51
+ if (!collection) {
52
+ console.warn(`[Auto-Translate Plugin] Collection "${collectionSlug}" not found in config`);
53
+ continue;
54
+ }
55
+ // Add translationSync field to collection
56
+ collection.fields.push({
57
+ name: 'translationSync',
58
+ type: 'checkbox',
59
+ admin: {
60
+ description: 'When enabled, changes in the default language will automatically translate to other languages',
61
+ position: 'sidebar'
62
+ },
63
+ defaultValue: pluginOptions.enableTranslationSyncByDefault ?? true,
64
+ label: 'Enable Auto-Translation'
65
+ });
66
+ // Auto-inject TranslationControl component into all localized fields
67
+ // Only inject if exclusions are enabled (otherwise there's nothing to control)
68
+ if (enableExclusions && pluginOptions.autoInjectUI !== false) {
69
+ collection.fields = injectTranslationControls(collection.fields, defaultLocale);
70
+ if (pluginOptions.debugging) {
71
+ console.log(`[Auto-Translate Plugin] Auto-injected UI controls for: ${collectionSlug}`);
72
+ }
73
+ }
74
+ // Add hooks for translation
75
+ if (!collection.hooks) {
76
+ collection.hooks = {};
77
+ }
78
+ if (!collection.hooks.afterChange) {
79
+ collection.hooks.afterChange = [];
80
+ }
81
+ // Main translation hook
82
+ collection.hooks.afterChange.push(async ({ doc, operation, previousDoc, req })=>{
83
+ // Only process create and update operations
84
+ if (operation !== 'create' && operation !== 'update') {
85
+ return doc;
86
+ }
87
+ // Only translate if editing from default locale
88
+ if (req.locale !== defaultLocale) {
89
+ if (pluginOptions.debugging) {
90
+ req.payload.logger.info(`[Auto-Translate Plugin] Skipping translation - not default locale (current: ${req.locale}, default: ${defaultLocale})`);
91
+ }
92
+ return doc;
93
+ }
94
+ // Skip translation for drafts when autosave is enabled
95
+ // Only translate when document is published
96
+ if (doc._status && doc._status !== 'published') {
97
+ if (pluginOptions.debugging) {
98
+ req.payload.logger.info(`[Auto-Translate Plugin] Skipping translation - document is a draft (status: ${doc._status})`);
99
+ }
100
+ return doc;
101
+ }
102
+ // Check if translation sync is enabled
103
+ if (!doc.translationSync) {
104
+ if (pluginOptions.debugging) {
105
+ req.payload.logger.info(`[Auto-Translate Plugin] Skipping translation - translationSync disabled for ${collectionSlug}:${doc.id}`);
106
+ }
107
+ return doc;
108
+ }
109
+ if (pluginOptions.debugging) {
110
+ req.payload.logger.info(`[Auto-Translate Plugin] Processing ${collectionSlug} document ${operation}: ${doc.id}`);
111
+ }
112
+ // Get secondary locales (all locales except default)
113
+ const secondaryLocales = allLocales.filter((locale)=>locale !== defaultLocale);
114
+ // Translate to each secondary locale
115
+ for (const targetLocale of secondaryLocales){
116
+ try {
117
+ if (pluginOptions.debugging) {
118
+ req.payload.logger.info(`[Auto-Translate Plugin] Translating ${collectionSlug}:${doc.id} from ${defaultLocale} to ${targetLocale}`);
119
+ }
120
+ // Get field-level exclusions for this locale (only if exclusions are enabled)
121
+ let excludedPaths = [];
122
+ if (enableExclusions) {
123
+ excludedPaths = await translationService.getExclusions(req.payload, collectionSlug, doc.id, targetLocale);
124
+ }
125
+ // Get global/collection-level excluded fields
126
+ const configExcludedFields = translationService.getConfigExcludedFields(collectionSlug);
127
+ const allExcludedPaths = [
128
+ ...excludedPaths,
129
+ ...configExcludedFields
130
+ ];
131
+ if (pluginOptions.debugging && allExcludedPaths.length > 0) {
132
+ req.payload.logger.info(`[Auto-Translate Plugin] Excluded paths for ${targetLocale}: ${allExcludedPaths.join(', ')}`);
133
+ }
134
+ // Get existing document in target locale to preserve excluded fields
135
+ // Only needed if exclusions are enabled
136
+ let existingDoc = null;
137
+ if (enableExclusions && allExcludedPaths.length > 0) {
138
+ try {
139
+ const existingResult = await req.payload.findByID({
140
+ id: doc.id,
141
+ collection: collectionSlug,
142
+ fallbackLocale: false,
143
+ locale: targetLocale
144
+ });
145
+ existingDoc = existingResult;
146
+ } catch (error) {
147
+ // Document doesn't exist in this locale yet, that's okay
148
+ if (pluginOptions.debugging) {
149
+ req.payload.logger.info(`[Auto-Translate Plugin] No existing document for ${targetLocale}, will create new`);
150
+ }
151
+ }
152
+ }
153
+ // Translate the document
154
+ const translatedData = await translationService.translate({
155
+ collection: collectionSlug,
156
+ data: doc,
157
+ excludedPaths: allExcludedPaths,
158
+ fromLocale: defaultLocale,
159
+ payload: req.payload,
160
+ toLocale: targetLocale
161
+ });
162
+ // Merge translated data with existing, preserving excluded fields
163
+ const finalData = translatedData;
164
+ if (existingDoc && allExcludedPaths.length > 0) {
165
+ // Preserve excluded fields from existing document
166
+ for (const excludedPath of allExcludedPaths){
167
+ const existingValue = getNestedValue(existingDoc, excludedPath);
168
+ if (existingValue !== undefined) {
169
+ setNestedValue(finalData, excludedPath, existingValue);
170
+ }
171
+ }
172
+ }
173
+ // Update the document in the target locale
174
+ await req.payload.update({
175
+ id: doc.id,
176
+ collection: collectionSlug,
177
+ data: finalData,
178
+ locale: targetLocale,
179
+ // Prevent infinite loop - don't trigger hooks
180
+ context: {
181
+ skipAutoTranslate: true
182
+ }
183
+ });
184
+ if (pluginOptions.debugging) {
185
+ req.payload.logger.info(`[Auto-Translate Plugin] Successfully translated ${collectionSlug}:${doc.id} to ${targetLocale}`);
186
+ }
187
+ } catch (error) {
188
+ req.payload.logger.error(`[Auto-Translate Plugin] Error translating to ${targetLocale}:`, error);
189
+ // Continue with other locales even if one fails
190
+ }
191
+ }
192
+ return doc;
193
+ });
194
+ // Prevent infinite loops - skip translation if triggered by our own update
195
+ const originalAfterChangeHooks = [
196
+ ...collection.hooks.afterChange || []
197
+ ];
198
+ collection.hooks.afterChange = [
199
+ async (args)=>{
200
+ // Skip if this update was triggered by auto-translate
201
+ if (args.context?.skipAutoTranslate) {
202
+ return args.doc;
203
+ }
204
+ // Run all hooks including translation
205
+ for (const hook of originalAfterChangeHooks){
206
+ const result = await hook(args);
207
+ if (result !== undefined) {
208
+ args.doc = result;
209
+ }
210
+ }
211
+ return args.doc;
212
+ }
213
+ ];
214
+ if (pluginOptions.debugging) {
215
+ console.log(`[Auto-Translate Plugin] Configured collection: ${collectionSlug}`);
216
+ }
217
+ }
218
+ }
219
+ /**
220
+ * If the plugin is disabled, we still want to keep added collections/fields
221
+ * so the database schema is consistent which is important for migrations.
222
+ */ if (pluginOptions.disabled) {
223
+ return config;
224
+ }
225
+ return config;
226
+ };
227
+ /**
228
+ * Helper function to get nested value from object using dot notation
229
+ */ function getNestedValue(obj, path) {
230
+ return path.split('.').reduce((current, part)=>{
231
+ if (current === null || current === undefined) {
232
+ return undefined;
233
+ }
234
+ return current[part];
235
+ }, obj);
236
+ }
237
+ /**
238
+ * Helper function to set nested value in object using dot notation
239
+ */ function setNestedValue(obj, path, value) {
240
+ const parts = path.split('.');
241
+ let current = obj;
242
+ for(let i = 0; i < parts.length - 1; i++){
243
+ const part = parts[i];
244
+ if (!(part in current) || current[part] === null || typeof current[part] !== 'object') {
245
+ // Check if next part is a number (array index)
246
+ const nextPart = parts[i + 1];
247
+ current[part] = /^\d+$/.test(nextPart) ? [] : {};
248
+ }
249
+ current = current[part];
250
+ }
251
+ current[parts[parts.length - 1]] = value;
252
+ }
253
+
254
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport type { AutoTranslateConfig } from './types/index.js'\n\nimport { getTranslationExclusionsCollection } from './collections/translationExclusions.js'\nimport { getTranslationSettingsGlobal } from './globals/translationSettings.js'\nimport { TranslationService } from './services/translationService.js'\nimport { injectTranslationControls } from './utilities/injectTranslationControls.js'\n\nexport * from './types/index.js'\nexport { getTranslationExclusionsCollection } from './collections/translationExclusions.js'\nexport { getTranslationSettingsGlobal } from './globals/translationSettings.js'\n\nexport const autoTranslate =\n (pluginOptions: AutoTranslateConfig) =>\n (config: Config): Config => {\n // Validate configuration\n if (!config.collections) {\n config.collections = []\n }\n\n if (!config.localization) {\n console.warn(\n '[Auto-Translate Plugin] No localization config found. Plugin will not function properly.',\n )\n return config\n }\n\n const localizationConfig = config.localization\n const defaultLocale = localizationConfig.defaultLocale\n const allLocales = Array.isArray(localizationConfig.locales)\n ? localizationConfig.locales.map((l) => (typeof l === 'string' ? l : l.code))\n : []\n\n // Default enableExclusions to true for backward compatibility\n const enableExclusions = pluginOptions.enableExclusions !== false\n\n if (pluginOptions.debugging) {\n console.log('[Auto-Translate Plugin] Configuration:')\n console.log('- Default locale:', defaultLocale)\n console.log('- All locales:', allLocales)\n console.log('- Enabled collections:', Object.keys(pluginOptions.collections || {}))\n console.log('- Exclusions enabled:', enableExclusions)\n }\n\n // Add translation exclusions collection (only if exclusions are enabled)\n if (enableExclusions) {\n const exclusionsSlug = pluginOptions.translationExclusionsSlug || 'translation-exclusions'\n config.collections.push(getTranslationExclusionsCollection(exclusionsSlug))\n }\n\n // Add translation settings global\n if (!config.globals) {\n config.globals = []\n }\n const settingsSlug = pluginOptions.translationSettingsSlug || 'translation-settings'\n config.globals.push(getTranslationSettingsGlobal(settingsSlug))\n\n // Initialize translation service\n const translationService = new TranslationService(pluginOptions)\n\n // Configure collections with auto-translate\n if (pluginOptions.collections) {\n for (const collectionSlug in pluginOptions.collections) {\n const collectionConfig = pluginOptions.collections[collectionSlug]\n\n // Skip if disabled\n if (\n collectionConfig === false ||\n (typeof collectionConfig === 'object' && collectionConfig.enabled === false)\n ) {\n continue\n }\n\n const collection = config.collections.find((c) => c.slug === collectionSlug)\n\n if (!collection) {\n console.warn(`[Auto-Translate Plugin] Collection \"${collectionSlug}\" not found in config`)\n continue\n }\n\n // Add translationSync field to collection\n collection.fields.push({\n name: 'translationSync',\n type: 'checkbox',\n admin: {\n description:\n 'When enabled, changes in the default language will automatically translate to other languages',\n position: 'sidebar',\n },\n defaultValue: pluginOptions.enableTranslationSyncByDefault ?? true,\n label: 'Enable Auto-Translation',\n })\n\n // Auto-inject TranslationControl component into all localized fields\n // Only inject if exclusions are enabled (otherwise there's nothing to control)\n if (enableExclusions && pluginOptions.autoInjectUI !== false) {\n collection.fields = injectTranslationControls(collection.fields, defaultLocale)\n\n if (pluginOptions.debugging) {\n console.log(`[Auto-Translate Plugin] Auto-injected UI controls for: ${collectionSlug}`)\n }\n }\n\n // Add hooks for translation\n if (!collection.hooks) {\n collection.hooks = {}\n }\n\n if (!collection.hooks.afterChange) {\n collection.hooks.afterChange = []\n }\n\n // Main translation hook\n collection.hooks.afterChange.push(async ({ doc, operation, previousDoc, req }) => {\n // Only process create and update operations\n if (operation !== 'create' && operation !== 'update') {\n return doc\n }\n\n // Only translate if editing from default locale\n if (req.locale !== defaultLocale) {\n if (pluginOptions.debugging) {\n req.payload.logger.info(\n `[Auto-Translate Plugin] Skipping translation - not default locale (current: ${req.locale}, default: ${defaultLocale})`,\n )\n }\n return doc\n }\n\n // Skip translation for drafts when autosave is enabled\n // Only translate when document is published\n if (doc._status && doc._status !== 'published') {\n if (pluginOptions.debugging) {\n req.payload.logger.info(\n `[Auto-Translate Plugin] Skipping translation - document is a draft (status: ${doc._status})`,\n )\n }\n return doc\n }\n\n // Check if translation sync is enabled\n if (!doc.translationSync) {\n if (pluginOptions.debugging) {\n req.payload.logger.info(\n `[Auto-Translate Plugin] Skipping translation - translationSync disabled for ${collectionSlug}:${doc.id}`,\n )\n }\n return doc\n }\n\n if (pluginOptions.debugging) {\n req.payload.logger.info(\n `[Auto-Translate Plugin] Processing ${collectionSlug} document ${operation}: ${doc.id}`,\n )\n }\n\n // Get secondary locales (all locales except default)\n const secondaryLocales = allLocales.filter((locale) => locale !== defaultLocale)\n\n // Translate to each secondary locale\n for (const targetLocale of secondaryLocales) {\n try {\n if (pluginOptions.debugging) {\n req.payload.logger.info(\n `[Auto-Translate Plugin] Translating ${collectionSlug}:${doc.id} from ${defaultLocale} to ${targetLocale}`,\n )\n }\n\n // Get field-level exclusions for this locale (only if exclusions are enabled)\n let excludedPaths: string[] = []\n if (enableExclusions) {\n excludedPaths = await translationService.getExclusions(\n req.payload,\n collectionSlug,\n doc.id,\n targetLocale,\n )\n }\n\n // Get global/collection-level excluded fields\n const configExcludedFields =\n translationService.getConfigExcludedFields(collectionSlug)\n const allExcludedPaths = [...excludedPaths, ...configExcludedFields]\n\n if (pluginOptions.debugging && allExcludedPaths.length > 0) {\n req.payload.logger.info(\n `[Auto-Translate Plugin] Excluded paths for ${targetLocale}: ${allExcludedPaths.join(', ')}`,\n )\n }\n\n // Get existing document in target locale to preserve excluded fields\n // Only needed if exclusions are enabled\n let existingDoc: any = null\n if (enableExclusions && allExcludedPaths.length > 0) {\n try {\n const existingResult = await req.payload.findByID({\n id: doc.id,\n collection: collectionSlug,\n fallbackLocale: false,\n locale: targetLocale,\n })\n existingDoc = existingResult\n } catch (error) {\n // Document doesn't exist in this locale yet, that's okay\n if (pluginOptions.debugging) {\n req.payload.logger.info(\n `[Auto-Translate Plugin] No existing document for ${targetLocale}, will create new`,\n )\n }\n }\n }\n\n // Translate the document\n const translatedData = await translationService.translate({\n collection: collectionSlug,\n data: doc,\n excludedPaths: allExcludedPaths,\n fromLocale: defaultLocale,\n payload: req.payload,\n toLocale: targetLocale,\n })\n\n // Merge translated data with existing, preserving excluded fields\n const finalData = translatedData\n if (existingDoc && allExcludedPaths.length > 0) {\n // Preserve excluded fields from existing document\n for (const excludedPath of allExcludedPaths) {\n const existingValue = getNestedValue(existingDoc, excludedPath)\n if (existingValue !== undefined) {\n setNestedValue(finalData, excludedPath, existingValue)\n }\n }\n }\n\n // Update the document in the target locale\n await req.payload.update({\n id: doc.id,\n collection: collectionSlug,\n data: finalData,\n locale: targetLocale,\n // Prevent infinite loop - don't trigger hooks\n context: {\n skipAutoTranslate: true,\n },\n })\n\n if (pluginOptions.debugging) {\n req.payload.logger.info(\n `[Auto-Translate Plugin] Successfully translated ${collectionSlug}:${doc.id} to ${targetLocale}`,\n )\n }\n } catch (error) {\n req.payload.logger.error(\n `[Auto-Translate Plugin] Error translating to ${targetLocale}:`,\n error,\n )\n // Continue with other locales even if one fails\n }\n }\n\n return doc\n })\n\n // Prevent infinite loops - skip translation if triggered by our own update\n const originalAfterChangeHooks = [...(collection.hooks.afterChange || [])]\n collection.hooks.afterChange = [\n async (args) => {\n // Skip if this update was triggered by auto-translate\n if (args.context?.skipAutoTranslate) {\n return args.doc\n }\n\n // Run all hooks including translation\n for (const hook of originalAfterChangeHooks) {\n const result = await hook(args)\n if (result !== undefined) {\n args.doc = result\n }\n }\n\n return args.doc\n },\n ]\n\n if (pluginOptions.debugging) {\n console.log(`[Auto-Translate Plugin] Configured collection: ${collectionSlug}`)\n }\n }\n }\n\n /**\n * If the plugin is disabled, we still want to keep added collections/fields\n * so the database schema is consistent which is important for migrations.\n */\n if (pluginOptions.disabled) {\n return config\n }\n\n return config\n }\n\n/**\n * Helper function to get nested value from object using dot notation\n */\nfunction getNestedValue(obj: any, path: string): any {\n return path.split('.').reduce((current, part) => {\n if (current === null || current === undefined) {\n return undefined\n }\n return current[part]\n }, obj)\n}\n\n/**\n * Helper function to set nested value in object using dot notation\n */\nfunction setNestedValue(obj: any, path: string, value: any): void {\n const parts = path.split('.')\n let current = obj\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]\n if (!(part in current) || current[part] === null || typeof current[part] !== 'object') {\n // Check if next part is a number (array index)\n const nextPart = parts[i + 1]\n current[part] = /^\\d+$/.test(nextPart) ? [] : {}\n }\n current = current[part]\n }\n\n current[parts[parts.length - 1]] = value\n}\n"],"names":["getTranslationExclusionsCollection","getTranslationSettingsGlobal","TranslationService","injectTranslationControls","autoTranslate","pluginOptions","config","collections","localization","console","warn","localizationConfig","defaultLocale","allLocales","Array","isArray","locales","map","l","code","enableExclusions","debugging","log","Object","keys","exclusionsSlug","translationExclusionsSlug","push","globals","settingsSlug","translationSettingsSlug","translationService","collectionSlug","collectionConfig","enabled","collection","find","c","slug","fields","name","type","admin","description","position","defaultValue","enableTranslationSyncByDefault","label","autoInjectUI","hooks","afterChange","doc","operation","previousDoc","req","locale","payload","logger","info","_status","translationSync","id","secondaryLocales","filter","targetLocale","excludedPaths","getExclusions","configExcludedFields","getConfigExcludedFields","allExcludedPaths","length","join","existingDoc","existingResult","findByID","fallbackLocale","error","translatedData","translate","data","fromLocale","toLocale","finalData","excludedPath","existingValue","getNestedValue","undefined","setNestedValue","update","context","skipAutoTranslate","originalAfterChangeHooks","args","hook","result","disabled","obj","path","split","reduce","current","part","value","parts","i","nextPart","test"],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { Payload } from 'payload';
2
+ import type { AutoTranslateConfig, TranslateOptions } from '../types/index.js';
3
+ export declare class TranslationService {
4
+ private client?;
5
+ private config;
6
+ constructor(config: AutoTranslateConfig);
7
+ /**
8
+ * Extracts translatable text from lexical editor nodes
9
+ */
10
+ private extractFromLexicalNode;
11
+ /**
12
+ * Extracts translatable strings from data structure
13
+ * Returns a map of paths to translatable values and metadata for reconstruction
14
+ */
15
+ private extractTranslatableStrings;
16
+ /**
17
+ * Lazily initialize OpenAI client only when needed
18
+ */
19
+ private getOpenAIClient;
20
+ /**
21
+ * Gets the original value at a path in metadata (helper for deduplication)
22
+ */
23
+ private getOriginalValue;
24
+ /**
25
+ * Checks if an object is a lexical editor node
26
+ */
27
+ private isLexicalEditorNode;
28
+ /**
29
+ * Reconstructs data with translated strings, applying deduplicated translations
30
+ */
31
+ private reconstructWithTranslations;
32
+ /**
33
+ * Determines if a string should be skipped from translation
34
+ */
35
+ private shouldSkipString;
36
+ /**
37
+ * Translates using OpenAI API (optimized version)
38
+ */
39
+ private translateWithOpenAI;
40
+ /**
41
+ * Legacy translation method (sends entire structure)
42
+ */
43
+ private translateWithOpenAILegacy;
44
+ /**
45
+ * Gets global and collection-specific excluded fields
46
+ */
47
+ getConfigExcludedFields(collection: string): string[];
48
+ /**
49
+ * Gets translation exclusions for a document
50
+ */
51
+ getExclusions(payload: Payload, collection: string, documentId: string, locale: string): Promise<string[]>;
52
+ /**
53
+ * Main translation method
54
+ */
55
+ translate(options: TranslateOptions): Promise<any>;
56
+ /**
57
+ * Updates translation exclusions for a document
58
+ */
59
+ updateExclusions(payload: Payload, collection: string, documentId: string, locale: string, excludedPaths: string[]): Promise<void>;
60
+ }