@pigment/auto-translate 1.3.0 → 1.3.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/dist/exports/rsc.d.ts +1 -0
- package/dist/exports/rsc.js +1 -0
- package/dist/exports/rsc.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +33 -19
- package/dist/index.js.map +1 -1
- package/dist/services/translationService.d.ts +5 -4
- package/dist/services/translationService.js +116 -115
- package/dist/services/translationService.js.map +1 -1
- package/package.json +1 -1
package/dist/exports/rsc.d.ts
CHANGED
package/dist/exports/rsc.js
CHANGED
package/dist/exports/rsc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["/**\n * React Server Component exports for the auto-translate plugin\n */\n\nexport { getTranslationSettingsGlobal } from '../globals/translationSettings.js'\nexport type * from '../types/index.js'\n"],"names":["getTranslationSettingsGlobal"],"mappings":"AAAA;;CAEC,GAED,SAASA,4BAA4B,QAAQ,oCAAmC"}
|
|
1
|
+
{"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["/**\n * React Server Component exports for the auto-translate plugin\n */\n\nexport { getTranslationSettingsGlobal } from '../globals/translationSettings.js'\nexport { TranslationService } from '../services/translationService.js'\nexport type * from '../types/index.js'\n"],"names":["getTranslationSettingsGlobal","TranslationService"],"mappings":"AAAA;;CAEC,GAED,SAASA,4BAA4B,QAAQ,oCAAmC;AAChF,SAASC,kBAAkB,QAAQ,oCAAmC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,6 @@ import type { Config } from 'payload';
|
|
|
2
2
|
import type { AutoTranslateConfig } from './types/index.js';
|
|
3
3
|
export { getTranslationExclusionsCollection } from './collections/translationExclusions.js';
|
|
4
4
|
export { getTranslationSettingsGlobal } from './globals/translationSettings.js';
|
|
5
|
+
export { TranslationService } from './services/translationService.js';
|
|
5
6
|
export * from './types/index.js';
|
|
6
7
|
export declare const autoTranslate: (pluginOptions: AutoTranslateConfig) => (config: Config) => Config;
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { TranslationService } from './services/translationService.js';
|
|
|
4
4
|
import { injectTranslationControls } from './utilities/injectTranslationControls.js';
|
|
5
5
|
export { getTranslationExclusionsCollection } from './collections/translationExclusions.js';
|
|
6
6
|
export { getTranslationSettingsGlobal } from './globals/translationSettings.js';
|
|
7
|
+
export { TranslationService } from './services/translationService.js';
|
|
7
8
|
export * from './types/index.js';
|
|
8
9
|
export const autoTranslate = (pluginOptions)=>(config)=>{
|
|
9
10
|
// Validate configuration
|
|
@@ -75,21 +76,32 @@ export const autoTranslate = (pluginOptions)=>(config)=>{
|
|
|
75
76
|
if (!collection.hooks) {
|
|
76
77
|
collection.hooks = {};
|
|
77
78
|
}
|
|
78
|
-
if (!collection.hooks.
|
|
79
|
-
collection.hooks.
|
|
79
|
+
if (!collection.hooks.afterOperation) {
|
|
80
|
+
collection.hooks.afterOperation = [];
|
|
80
81
|
}
|
|
81
82
|
// Main translation hook
|
|
82
|
-
collection.hooks.
|
|
83
|
+
collection.hooks.afterOperation.push(async ({ operation, req, result })=>{
|
|
83
84
|
// Only process create and update operations
|
|
84
|
-
if (operation !== 'create' && operation !== '
|
|
85
|
-
|
|
85
|
+
if (operation !== 'create' && operation !== 'updateByID') {
|
|
86
|
+
if (pluginOptions.debugging) {
|
|
87
|
+
req.payload.logger.error(`[Auto-Translate Plugin] Skipping translation - not create or update operation: ${operation}`);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
// For create/update operations, result should have a id property
|
|
92
|
+
if (!result || typeof result !== 'object' || !('id' in result)) {
|
|
93
|
+
if (pluginOptions.debugging) {
|
|
94
|
+
req.payload.logger.error(`[Auto-Translate Plugin] No document found in result: ${JSON.stringify(result)}`);
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
86
97
|
}
|
|
98
|
+
const doc = result;
|
|
87
99
|
// Only translate if editing from default locale
|
|
88
100
|
if (req.locale !== defaultLocale) {
|
|
89
101
|
if (pluginOptions.debugging) {
|
|
90
102
|
req.payload.logger.info(`[Auto-Translate Plugin] Skipping translation - not default locale (current: ${req.locale}, default: ${defaultLocale})`);
|
|
91
103
|
}
|
|
92
|
-
return
|
|
104
|
+
return result;
|
|
93
105
|
}
|
|
94
106
|
// Skip translation for drafts when autosave is enabled
|
|
95
107
|
// Only translate when document is published
|
|
@@ -97,14 +109,14 @@ export const autoTranslate = (pluginOptions)=>(config)=>{
|
|
|
97
109
|
if (pluginOptions.debugging) {
|
|
98
110
|
req.payload.logger.info(`[Auto-Translate Plugin] Skipping translation - document is a draft (status: ${doc._status})`);
|
|
99
111
|
}
|
|
100
|
-
return
|
|
112
|
+
return result;
|
|
101
113
|
}
|
|
102
114
|
// Check if translation sync is enabled
|
|
103
115
|
if (!doc.translationSync) {
|
|
104
116
|
if (pluginOptions.debugging) {
|
|
105
117
|
req.payload.logger.info(`[Auto-Translate Plugin] Skipping translation - translationSync disabled for ${collectionSlug}:${doc.id}`);
|
|
106
118
|
}
|
|
107
|
-
return
|
|
119
|
+
return result;
|
|
108
120
|
}
|
|
109
121
|
if (pluginOptions.debugging) {
|
|
110
122
|
req.payload.logger.info(`[Auto-Translate Plugin] Processing ${collectionSlug} document ${operation}: ${doc.id}`);
|
|
@@ -120,7 +132,7 @@ export const autoTranslate = (pluginOptions)=>(config)=>{
|
|
|
120
132
|
// Get field-level exclusions for this locale (only if exclusions are enabled)
|
|
121
133
|
let excludedPaths = [];
|
|
122
134
|
if (enableExclusions) {
|
|
123
|
-
excludedPaths = await translationService.getExclusions(req.payload, collectionSlug, doc.id, targetLocale);
|
|
135
|
+
excludedPaths = await translationService.getExclusions(req.payload, collectionSlug, doc.id.toString(), targetLocale);
|
|
124
136
|
}
|
|
125
137
|
// Get global/collection-level excluded fields
|
|
126
138
|
const configExcludedFields = translationService.getConfigExcludedFields(collectionSlug);
|
|
@@ -179,7 +191,8 @@ export const autoTranslate = (pluginOptions)=>(config)=>{
|
|
|
179
191
|
// Prevent infinite loop - don't trigger hooks
|
|
180
192
|
context: {
|
|
181
193
|
skipAutoTranslate: true
|
|
182
|
-
}
|
|
194
|
+
},
|
|
195
|
+
req
|
|
183
196
|
});
|
|
184
197
|
if (pluginOptions.debugging) {
|
|
185
198
|
req.payload.logger.info(`[Auto-Translate Plugin] Successfully translated ${collectionSlug}:${doc.id} to ${targetLocale}`);
|
|
@@ -209,26 +222,27 @@ export const autoTranslate = (pluginOptions)=>(config)=>{
|
|
|
209
222
|
// Continue with other locales even if one fails
|
|
210
223
|
}
|
|
211
224
|
}
|
|
212
|
-
return
|
|
225
|
+
return result;
|
|
213
226
|
});
|
|
214
227
|
// Prevent infinite loops - skip translation if triggered by our own update
|
|
215
|
-
const
|
|
216
|
-
...collection.hooks.
|
|
228
|
+
const originalAfterOperationHooks = [
|
|
229
|
+
...collection.hooks.afterOperation || []
|
|
217
230
|
];
|
|
218
|
-
collection.hooks.
|
|
231
|
+
collection.hooks.afterOperation = [
|
|
219
232
|
async (args)=>{
|
|
220
233
|
// Skip if this update was triggered by auto-translate
|
|
221
|
-
|
|
222
|
-
|
|
234
|
+
// Context might not be available on all operations
|
|
235
|
+
if ('req' in args && args.req?.context?.skipAutoTranslate) {
|
|
236
|
+
return args.result;
|
|
223
237
|
}
|
|
224
238
|
// Run all hooks including translation
|
|
225
|
-
for (const hook of
|
|
239
|
+
for (const hook of originalAfterOperationHooks){
|
|
226
240
|
const result = await hook(args);
|
|
227
241
|
if (result !== undefined) {
|
|
228
|
-
args.
|
|
242
|
+
args.result = result;
|
|
229
243
|
}
|
|
230
244
|
}
|
|
231
|
-
return args.
|
|
245
|
+
return args.result;
|
|
232
246
|
}
|
|
233
247
|
];
|
|
234
248
|
if (pluginOptions.debugging) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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 { getTranslationExclusionsCollection } from './collections/translationExclusions.js'\nexport { getTranslationSettingsGlobal } from './globals/translationSettings.js'\nexport * from './types/index.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 // Log detailed error information\n const errorMessage = error instanceof Error ? error.message : String(error)\n const errorStack = error instanceof Error ? error.stack : undefined\n const errorDetails = {\n collection: collectionSlug,\n documentId: doc.id,\n fromLocale: defaultLocale,\n message: errorMessage,\n stack: errorStack,\n toLocale: targetLocale,\n }\n\n req.payload.logger.error(\n `[Auto-Translate Plugin] Error translating ${collectionSlug}:${doc.id} to ${targetLocale}:`,\n )\n req.payload.logger.error(errorMessage)\n\n if (pluginOptions.debugging && errorStack) {\n req.payload.logger.error('Stack trace:')\n req.payload.logger.error(errorStack)\n }\n\n // Log additional context if it's an OpenAI error\n if (error && typeof error === 'object' && 'error' in error) {\n req.payload.logger.error('OpenAI error details:', JSON.stringify(error, null, 2))\n }\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","errorMessage","Error","message","String","errorStack","stack","errorDetails","documentId","JSON","stringify","originalAfterChangeHooks","args","hook","result","disabled","obj","path","split","reduce","current","part","value","parts","i","nextPart","test"],"mappings":"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"}
|
|
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 { getTranslationExclusionsCollection } from './collections/translationExclusions.js'\nexport { getTranslationSettingsGlobal } from './globals/translationSettings.js'\nexport { TranslationService } from './services/translationService.js'\nexport * from './types/index.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.afterOperation) {\n collection.hooks.afterOperation = []\n }\n\n // Main translation hook\n collection.hooks.afterOperation.push(async ({ operation, req, result }) => {\n // Only process create and update operations\n if (operation !== 'create' && operation !== 'updateByID') {\n if (pluginOptions.debugging) {\n req.payload.logger.error(\n `[Auto-Translate Plugin] Skipping translation - not create or update operation: ${operation}`,\n )\n }\n return result\n }\n\n // For create/update operations, result should have a id property\n if (!result || typeof result !== 'object' || !('id' in result)) {\n if (pluginOptions.debugging) {\n req.payload.logger.error(\n `[Auto-Translate Plugin] No document found in result: ${JSON.stringify(result)}`,\n )\n }\n return result\n }\n\n const doc = result\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 result\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 result\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 result\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.toString(),\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 req,\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 // Log detailed error information\n const errorMessage = error instanceof Error ? error.message : String(error)\n const errorStack = error instanceof Error ? error.stack : undefined\n const errorDetails = {\n collection: collectionSlug,\n documentId: doc.id,\n fromLocale: defaultLocale,\n message: errorMessage,\n stack: errorStack,\n toLocale: targetLocale,\n }\n\n req.payload.logger.error(\n `[Auto-Translate Plugin] Error translating ${collectionSlug}:${doc.id} to ${targetLocale}:`,\n )\n req.payload.logger.error(errorMessage)\n\n if (pluginOptions.debugging && errorStack) {\n req.payload.logger.error('Stack trace:')\n req.payload.logger.error(errorStack)\n }\n\n // Log additional context if it's an OpenAI error\n if (error && typeof error === 'object' && 'error' in error) {\n req.payload.logger.error('OpenAI error details:', JSON.stringify(error, null, 2))\n }\n\n // Continue with other locales even if one fails\n }\n }\n\n return result\n })\n\n // Prevent infinite loops - skip translation if triggered by our own update\n const originalAfterOperationHooks = [...(collection.hooks.afterOperation || [])]\n collection.hooks.afterOperation = [\n async (args) => {\n // Skip if this update was triggered by auto-translate\n // Context might not be available on all operations\n if ('req' in args && args.req?.context?.skipAutoTranslate) {\n return args.result\n }\n\n // Run all hooks including translation\n for (const hook of originalAfterOperationHooks) {\n const result = await hook(args)\n if (result !== undefined) {\n args.result = result\n }\n }\n\n return args.result\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","afterOperation","operation","req","result","payload","logger","error","JSON","stringify","doc","locale","info","_status","translationSync","id","secondaryLocales","filter","targetLocale","excludedPaths","getExclusions","toString","configExcludedFields","getConfigExcludedFields","allExcludedPaths","length","join","existingDoc","existingResult","findByID","fallbackLocale","translatedData","translate","data","fromLocale","toLocale","finalData","excludedPath","existingValue","getNestedValue","undefined","setNestedValue","update","context","skipAutoTranslate","errorMessage","Error","message","String","errorStack","stack","errorDetails","documentId","originalAfterOperationHooks","args","hook","disabled","obj","path","split","reduce","current","part","value","parts","i","nextPart","test"],"mappings":"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"}
|
|
@@ -37,10 +37,6 @@ export declare class TranslationService {
|
|
|
37
37
|
* Determines if a string should be skipped from translation
|
|
38
38
|
*/
|
|
39
39
|
private shouldSkipString;
|
|
40
|
-
/**
|
|
41
|
-
* Translates using OpenAI API (optimized version)
|
|
42
|
-
*/
|
|
43
|
-
private translateWithOpenAI;
|
|
44
40
|
/**
|
|
45
41
|
* Legacy translation method (sends entire structure)
|
|
46
42
|
*/
|
|
@@ -57,6 +53,11 @@ export declare class TranslationService {
|
|
|
57
53
|
* Main translation method
|
|
58
54
|
*/
|
|
59
55
|
translate(options: TranslateOptions): Promise<any>;
|
|
56
|
+
/**
|
|
57
|
+
* Translates using OpenAI API (optimized version)
|
|
58
|
+
* This method is now public and can be used directly in your application
|
|
59
|
+
*/
|
|
60
|
+
translateWithOpenAI(data: any, fromLocale: string, toLocale: string, payload: Payload): Promise<any>;
|
|
60
61
|
/**
|
|
61
62
|
* Updates translation exclusions for a document
|
|
62
63
|
*/
|
|
@@ -315,7 +315,123 @@ export class TranslationService {
|
|
|
315
315
|
return false;
|
|
316
316
|
}
|
|
317
317
|
/**
|
|
318
|
+
* Legacy translation method (sends entire structure)
|
|
319
|
+
*/ async translateWithOpenAILegacy(data, fromLocale, toLocale, payload) {
|
|
320
|
+
const client = this.getOpenAIClient();
|
|
321
|
+
const timeout = this.config.provider?.timeout || 30000;
|
|
322
|
+
try {
|
|
323
|
+
// Get translation settings from global
|
|
324
|
+
const settings = await this.getTranslationSettings(payload);
|
|
325
|
+
// Build system message from settings
|
|
326
|
+
const systemPrompt = settings.systemPrompt.replace('{fromLocale}', fromLocale).replace('{toLocale}', toLocale);
|
|
327
|
+
const systemMessage = `${systemPrompt}\n\n${settings.translationRules}`;
|
|
328
|
+
const requestParams = {
|
|
329
|
+
messages: [
|
|
330
|
+
{
|
|
331
|
+
content: systemMessage,
|
|
332
|
+
role: 'system'
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
content: JSON.stringify(data, null, 2),
|
|
336
|
+
role: 'user'
|
|
337
|
+
}
|
|
338
|
+
],
|
|
339
|
+
model: settings.model,
|
|
340
|
+
response_format: {
|
|
341
|
+
type: 'json_object'
|
|
342
|
+
},
|
|
343
|
+
temperature: settings.temperature
|
|
344
|
+
};
|
|
345
|
+
// Add maxTokens if specified
|
|
346
|
+
if (settings.maxTokens) {
|
|
347
|
+
requestParams.max_tokens = settings.maxTokens;
|
|
348
|
+
}
|
|
349
|
+
const response = await client.chat.completions.create(requestParams, {
|
|
350
|
+
timeout
|
|
351
|
+
});
|
|
352
|
+
const translatedText = response.choices[0]?.message?.content;
|
|
353
|
+
if (!translatedText) {
|
|
354
|
+
throw new Error('No translation received from OpenAI');
|
|
355
|
+
}
|
|
356
|
+
return JSON.parse(translatedText);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error('[Auto-Translate] Translation error:', error);
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Gets global and collection-specific excluded fields
|
|
364
|
+
*/ getConfigExcludedFields(collection) {
|
|
365
|
+
const globalExclusions = this.config.excludeFields || [];
|
|
366
|
+
const collectionConfig = this.config.collections?.[collection];
|
|
367
|
+
if (typeof collectionConfig === 'object' && collectionConfig.excludeFields) {
|
|
368
|
+
return [
|
|
369
|
+
...globalExclusions,
|
|
370
|
+
...collectionConfig.excludeFields
|
|
371
|
+
];
|
|
372
|
+
}
|
|
373
|
+
return globalExclusions;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Gets translation exclusions for a document
|
|
377
|
+
*/ async getExclusions(payload, collection, documentId, locale) {
|
|
378
|
+
const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions';
|
|
379
|
+
try {
|
|
380
|
+
const result = await payload.find({
|
|
381
|
+
collection: exclusionsSlug,
|
|
382
|
+
limit: 1,
|
|
383
|
+
where: {
|
|
384
|
+
and: [
|
|
385
|
+
{
|
|
386
|
+
collection: {
|
|
387
|
+
equals: collection
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
documentId: {
|
|
392
|
+
equals: documentId
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
locale: {
|
|
397
|
+
equals: locale
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
]
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
if (result.docs.length > 0) {
|
|
404
|
+
const exclusion = result.docs[0];
|
|
405
|
+
return exclusion.excludedPaths?.map((item)=>item.path) || [];
|
|
406
|
+
}
|
|
407
|
+
return [];
|
|
408
|
+
} catch (error) {
|
|
409
|
+
if (this.config.debugging) {
|
|
410
|
+
payload.logger.error(`[Auto-Translate] Error fetching exclusions: ${error}`);
|
|
411
|
+
}
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Main translation method
|
|
417
|
+
*/ async translate(options) {
|
|
418
|
+
const { collection, data, excludedPaths = [], fromLocale, payload, toLocale } = options;
|
|
419
|
+
// Filter out excluded paths before translation
|
|
420
|
+
const dataToTranslate = filterExcludedPaths(data, excludedPaths);
|
|
421
|
+
if (this.config.debugging) {
|
|
422
|
+
payload.logger.info(`[Auto-Translate] Translating from ${fromLocale} to ${toLocale} for collection ${collection}`);
|
|
423
|
+
payload.logger.info(`[Auto-Translate] Excluded paths: ${excludedPaths.join(', ')}`);
|
|
424
|
+
}
|
|
425
|
+
// Use custom translator if provided
|
|
426
|
+
if (this.config.provider?.customTranslate) {
|
|
427
|
+
return await this.config.provider.customTranslate(options);
|
|
428
|
+
}
|
|
429
|
+
// Use OpenAI by default
|
|
430
|
+
return await this.translateWithOpenAI(dataToTranslate, fromLocale, toLocale, payload);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
318
433
|
* Translates using OpenAI API (optimized version)
|
|
434
|
+
* This method is now public and can be used directly in your application
|
|
319
435
|
*/ async translateWithOpenAI(data, fromLocale, toLocale, payload) {
|
|
320
436
|
const client = this.getOpenAIClient();
|
|
321
437
|
// Use optimization by default (can be disabled via config)
|
|
@@ -436,121 +552,6 @@ export class TranslationService {
|
|
|
436
552
|
}
|
|
437
553
|
}
|
|
438
554
|
/**
|
|
439
|
-
* Legacy translation method (sends entire structure)
|
|
440
|
-
*/ async translateWithOpenAILegacy(data, fromLocale, toLocale, payload) {
|
|
441
|
-
const client = this.getOpenAIClient();
|
|
442
|
-
const timeout = this.config.provider?.timeout || 30000;
|
|
443
|
-
try {
|
|
444
|
-
// Get translation settings from global
|
|
445
|
-
const settings = await this.getTranslationSettings(payload);
|
|
446
|
-
// Build system message from settings
|
|
447
|
-
const systemPrompt = settings.systemPrompt.replace('{fromLocale}', fromLocale).replace('{toLocale}', toLocale);
|
|
448
|
-
const systemMessage = `${systemPrompt}\n\n${settings.translationRules}`;
|
|
449
|
-
const requestParams = {
|
|
450
|
-
messages: [
|
|
451
|
-
{
|
|
452
|
-
content: systemMessage,
|
|
453
|
-
role: 'system'
|
|
454
|
-
},
|
|
455
|
-
{
|
|
456
|
-
content: JSON.stringify(data, null, 2),
|
|
457
|
-
role: 'user'
|
|
458
|
-
}
|
|
459
|
-
],
|
|
460
|
-
model: settings.model,
|
|
461
|
-
response_format: {
|
|
462
|
-
type: 'json_object'
|
|
463
|
-
},
|
|
464
|
-
temperature: settings.temperature
|
|
465
|
-
};
|
|
466
|
-
// Add maxTokens if specified
|
|
467
|
-
if (settings.maxTokens) {
|
|
468
|
-
requestParams.max_tokens = settings.maxTokens;
|
|
469
|
-
}
|
|
470
|
-
const response = await client.chat.completions.create(requestParams, {
|
|
471
|
-
timeout
|
|
472
|
-
});
|
|
473
|
-
const translatedText = response.choices[0]?.message?.content;
|
|
474
|
-
if (!translatedText) {
|
|
475
|
-
throw new Error('No translation received from OpenAI');
|
|
476
|
-
}
|
|
477
|
-
return JSON.parse(translatedText);
|
|
478
|
-
} catch (error) {
|
|
479
|
-
console.error('[Auto-Translate] Translation error:', error);
|
|
480
|
-
throw error;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Gets global and collection-specific excluded fields
|
|
485
|
-
*/ getConfigExcludedFields(collection) {
|
|
486
|
-
const globalExclusions = this.config.excludeFields || [];
|
|
487
|
-
const collectionConfig = this.config.collections?.[collection];
|
|
488
|
-
if (typeof collectionConfig === 'object' && collectionConfig.excludeFields) {
|
|
489
|
-
return [
|
|
490
|
-
...globalExclusions,
|
|
491
|
-
...collectionConfig.excludeFields
|
|
492
|
-
];
|
|
493
|
-
}
|
|
494
|
-
return globalExclusions;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Gets translation exclusions for a document
|
|
498
|
-
*/ async getExclusions(payload, collection, documentId, locale) {
|
|
499
|
-
const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions';
|
|
500
|
-
try {
|
|
501
|
-
const result = await payload.find({
|
|
502
|
-
collection: exclusionsSlug,
|
|
503
|
-
limit: 1,
|
|
504
|
-
where: {
|
|
505
|
-
and: [
|
|
506
|
-
{
|
|
507
|
-
collection: {
|
|
508
|
-
equals: collection
|
|
509
|
-
}
|
|
510
|
-
},
|
|
511
|
-
{
|
|
512
|
-
documentId: {
|
|
513
|
-
equals: documentId
|
|
514
|
-
}
|
|
515
|
-
},
|
|
516
|
-
{
|
|
517
|
-
locale: {
|
|
518
|
-
equals: locale
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
]
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
|
-
if (result.docs.length > 0) {
|
|
525
|
-
const exclusion = result.docs[0];
|
|
526
|
-
return exclusion.excludedPaths?.map((item)=>item.path) || [];
|
|
527
|
-
}
|
|
528
|
-
return [];
|
|
529
|
-
} catch (error) {
|
|
530
|
-
if (this.config.debugging) {
|
|
531
|
-
payload.logger.error(`[Auto-Translate] Error fetching exclusions: ${error}`);
|
|
532
|
-
}
|
|
533
|
-
return [];
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Main translation method
|
|
538
|
-
*/ async translate(options) {
|
|
539
|
-
const { collection, data, excludedPaths = [], fromLocale, payload, toLocale } = options;
|
|
540
|
-
// Filter out excluded paths before translation
|
|
541
|
-
const dataToTranslate = filterExcludedPaths(data, excludedPaths);
|
|
542
|
-
if (this.config.debugging) {
|
|
543
|
-
payload.logger.info(`[Auto-Translate] Translating from ${fromLocale} to ${toLocale} for collection ${collection}`);
|
|
544
|
-
payload.logger.info(`[Auto-Translate] Excluded paths: ${excludedPaths.join(', ')}`);
|
|
545
|
-
}
|
|
546
|
-
// Use custom translator if provided
|
|
547
|
-
if (this.config.provider?.customTranslate) {
|
|
548
|
-
return await this.config.provider.customTranslate(options);
|
|
549
|
-
}
|
|
550
|
-
// Use OpenAI by default
|
|
551
|
-
return await this.translateWithOpenAI(dataToTranslate, fromLocale, toLocale, payload);
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
555
|
* Updates translation exclusions for a document
|
|
555
556
|
*/ async updateExclusions(payload, collection, documentId, locale, excludedPaths) {
|
|
556
557
|
const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/services/translationService.ts"],"sourcesContent":["import type { Payload } from 'payload'\n\nimport OpenAI from 'openai'\n\nimport type { AutoTranslateConfig, TranslateOptions } from '../types/index.js'\n\nimport { filterExcludedPaths } from '../utilities/fieldHelpers.js'\n\nexport class TranslationService {\n private client?: OpenAI\n private config: AutoTranslateConfig\n\n constructor(config: AutoTranslateConfig) {\n this.config = config\n }\n\n /**\n * Extracts translatable text from lexical editor nodes\n */\n private extractFromLexicalNode(\n node: any,\n path: string,\n strings: Map<string, string>,\n deduplicationMap: Map<string, string[]>,\n ): any {\n const enableDeduplication = this.config.enableDeduplication !== false // Default to true\n\n // Handle text nodes - skip whitespace-only or very short text\n if (node.type === 'text' && node.text && typeof node.text === 'string') {\n const trimmed = node.text.trim()\n\n // Skip if empty, whitespace-only, or too short\n if (trimmed.length === 0 || this.shouldSkipString(node.text, `${path}.text`)) {\n return node\n }\n\n const textPath = `${path}.text`\n\n if (enableDeduplication) {\n // Check for deduplication\n if (deduplicationMap.has(trimmed)) {\n // This string already exists, just store the path mapping\n const existingPaths = deduplicationMap.get(trimmed)!\n existingPaths.push(textPath)\n return { ...node, text: `__TRANSLATE_${textPath}__` }\n } else {\n // New unique string\n strings.set(textPath, node.text)\n deduplicationMap.set(trimmed, [textPath])\n return { ...node, text: `__TRANSLATE_${textPath}__` }\n }\n } else {\n // No deduplication - add every string\n strings.set(textPath, node.text)\n deduplicationMap.set(trimmed, [textPath])\n return { ...node, text: `__TRANSLATE_${textPath}__` }\n }\n }\n\n // Handle nodes with children\n if (node.children && Array.isArray(node.children)) {\n return {\n ...node,\n children: node.children.map((child: any, index: number) =>\n this.extractFromLexicalNode(\n child,\n `${path}.children[${index}]`,\n strings,\n deduplicationMap,\n ),\n ),\n }\n }\n\n return node\n }\n\n /**\n * Extracts translatable strings from data structure\n * Returns a map of paths to translatable values and metadata for reconstruction\n */\n private extractTranslatableStrings(\n data: any,\n path: string = '',\n ): { deduplicationMap: Map<string, string[]>; metadata: any; strings: Map<string, string> } {\n const strings = new Map<string, string>()\n const deduplicationMap = new Map<string, string[]>() // value -> [paths]\n const enableDeduplication = this.config.enableDeduplication !== false // Default to true\n\n const extract = (obj: any, currentPath: string): any => {\n if (obj === null || obj === undefined) {\n return obj\n }\n\n // Handle lexical editor format\n if (this.isLexicalEditorNode(obj)) {\n return this.extractFromLexicalNode(obj, currentPath, strings, deduplicationMap)\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item, index) => extract(item, `${currentPath}[${index}]`))\n }\n\n // Handle objects\n if (typeof obj === 'object') {\n const result: any = {}\n for (const [key, value] of Object.entries(obj)) {\n const newPath = currentPath ? `${currentPath}.${key}` : key\n result[key] = extract(value, newPath)\n }\n return result\n }\n\n // Handle strings\n if (typeof obj === 'string' && obj.trim().length > 0) {\n // Skip IDs and other non-translatable strings\n if (!this.shouldSkipString(obj, currentPath)) {\n if (enableDeduplication) {\n // Check for deduplication\n const trimmedValue = obj.trim()\n if (deduplicationMap.has(trimmedValue)) {\n // This string already exists, just store the path mapping\n const existingPaths = deduplicationMap.get(trimmedValue)!\n existingPaths.push(currentPath)\n return `__TRANSLATE_${currentPath}__`\n } else {\n // New unique string\n strings.set(currentPath, obj)\n deduplicationMap.set(trimmedValue, [currentPath])\n return `__TRANSLATE_${currentPath}__`\n }\n } else {\n // No deduplication - add every string\n strings.set(currentPath, obj)\n deduplicationMap.set(obj.trim(), [currentPath])\n return `__TRANSLATE_${currentPath}__`\n }\n }\n }\n\n return obj\n }\n\n const metadata = extract(data, path)\n return { deduplicationMap, metadata, strings }\n }\n\n /**\n * Lazily initialize OpenAI client only when needed\n */\n private getOpenAIClient(): OpenAI {\n if (!this.client) {\n const apiKey = this.config.provider?.apiKey || process.env.OPENAI_API_KEY\n if (!apiKey) {\n throw new Error(\n 'OpenAI API key is required. Set OPENAI_API_KEY environment variable or provide it in plugin config.',\n )\n }\n\n this.client = new OpenAI({\n apiKey,\n baseURL: this.config.provider?.baseURL || process.env.OPENAI_BASE_URL,\n })\n }\n return this.client\n }\n\n /**\n * Gets the original value at a path in metadata (helper for deduplication)\n */\n private getOriginalValue(metadata: any, path: string): null | string {\n try {\n const parts = path.split(/[.[\\]]/).filter(Boolean)\n let current = metadata\n for (const part of parts) {\n if (current === null || current === undefined) {\n return null\n }\n current = current[part]\n }\n return typeof current === 'string' ? current : null\n } catch {\n return null\n }\n }\n\n /**\n * Gets translation settings from the global or returns defaults\n */\n private async getTranslationSettings(payload: Payload): Promise<{\n maxTokens?: number\n model: string\n systemPrompt: string\n temperature: number\n translationRules: string\n }> {\n const settingsSlug = this.config.translationSettingsSlug || 'translation-settings'\n\n // Default values\n const defaults = {\n maxTokens: undefined,\n model: this.config.provider?.model || 'gpt-4o',\n systemPrompt:\n 'You are a professional translator. Translate the JSON object values from {fromLocale} to {toLocale}.',\n temperature: 0.3,\n translationRules: `Rules:\n - Only translate the values, never the keys\n - Preserve the exact JSON structure\n - Maintain formatting, HTML tags, and special characters\n - Return only valid JSON without any markdown formatting or code blocks\n - If a value is already in the target language or is a proper noun, keep it as is`,\n }\n\n try {\n const settings = await payload.findGlobal({\n slug: settingsSlug,\n })\n\n if (settings) {\n return {\n maxTokens: settings.maxTokens || defaults.maxTokens,\n model: settings.model || defaults.model,\n systemPrompt: settings.systemPrompt || defaults.systemPrompt,\n temperature:\n typeof settings.temperature === 'number' ? settings.temperature : defaults.temperature,\n translationRules: settings.translationRules || defaults.translationRules,\n }\n }\n } catch (error) {\n if (this.config.debugging) {\n console.warn(\n '[Auto-Translate] Could not fetch translation settings, using defaults:',\n error,\n )\n }\n }\n\n return defaults\n }\n\n /**\n * Checks if an object is a lexical editor node\n */\n private isLexicalEditorNode(obj: any): boolean {\n return (\n obj &&\n typeof obj === 'object' &&\n 'type' in obj &&\n 'version' in obj &&\n ('children' in obj || 'text' in obj)\n )\n }\n\n /**\n * Reconstructs data with translated strings, applying deduplicated translations\n */\n private reconstructWithTranslations(\n metadata: any,\n translations: Map<string, string>,\n deduplicationMap: Map<string, string[]>,\n ): any {\n // Build a comprehensive translation map including deduplicated paths\n const fullTranslations = new Map<string, string>()\n\n // For each unique string that was translated\n translations.forEach((translatedValue, originalPath) => {\n fullTranslations.set(originalPath, translatedValue)\n\n // Find all paths that had the same original value\n const originalValue = this.getOriginalValue(metadata, originalPath)\n if (originalValue) {\n const trimmed = originalValue.replace(/^__TRANSLATE_(.+)__$/, '$1')\n // Look through deduplication map to find all paths with same value\n for (const [value, paths] of deduplicationMap.entries()) {\n if (paths.includes(originalPath)) {\n // Apply the same translation to all paths with this value\n paths.forEach((path) => {\n fullTranslations.set(path, translatedValue)\n })\n break\n }\n }\n }\n })\n\n const reconstruct = (obj: any): any => {\n if (obj === null || obj === undefined) {\n return obj\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item) => reconstruct(item))\n }\n\n // Handle objects\n if (typeof obj === 'object') {\n const result: any = {}\n for (const [key, value] of Object.entries(obj)) {\n result[key] = reconstruct(value)\n }\n return result\n }\n\n // Replace translation placeholders\n if (typeof obj === 'string' && obj.startsWith('__TRANSLATE_')) {\n const path = obj.slice(12, -2) // Remove __TRANSLATE_ prefix and __ suffix\n return fullTranslations.get(path) || obj\n }\n\n return obj\n }\n\n return reconstruct(metadata)\n }\n\n /**\n * Determines if a string should be skipped from translation\n */\n private shouldSkipString(str: string, path: string): boolean {\n // Skip IDs (MongoDB ObjectIds and similar)\n if (/^[a-f0-9]{24}$/i.test(str)) {\n return true\n }\n\n // Skip URLs\n if (/^https?:\\/\\//.test(str)) {\n return true\n }\n\n // Skip file paths\n if (/^\\/\\S*\\.(jpg|jpeg|png|gif|webp|svg|pdf|mp4|webm|ogg|mp3|wav)$/i.test(str)) {\n return true\n }\n\n // Skip email addresses\n if (/^[^\\s@]+@[^\\s@][^\\s.@]*\\.[^\\s@]+$/.test(str)) {\n return true\n }\n\n // Skip ISO date strings\n if (/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(str)) {\n return true\n }\n\n // Skip date-time strings like \"2019-01-31 12:05:04\"\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/.test(str)) {\n return true\n }\n\n // Skip percentages like \"100%\"\n if (/^\\d+%$/.test(str)) {\n return true\n }\n\n // Skip pure numbers\n if (/^\\d+$/.test(str)) {\n return true\n }\n\n // Skip whitespace-only strings (including single spaces)\n if (str.trim().length === 0) {\n return true\n }\n\n // Skip very short strings based on config (default: 3 characters)\n const minLength = this.config.minStringLength !== undefined ? this.config.minStringLength : 3\n if (str.trim().length < minLength) {\n return true\n }\n\n // Skip status values\n if (['archived', 'draft', 'pending', 'published'].includes(str.toLowerCase())) {\n return true\n }\n\n // Skip paths ending with id, createdAt, updatedAt, etc.\n const pathLower = path.toLowerCase()\n if (\n pathLower.endsWith('id') ||\n pathLower.endsWith('_id') ||\n pathLower.includes('createdat') ||\n pathLower.includes('updatedat')\n ) {\n return true\n }\n\n return false\n }\n\n /**\n * Translates using OpenAI API (optimized version)\n */\n private async translateWithOpenAI(\n data: any,\n fromLocale: string,\n toLocale: string,\n payload: Payload,\n ): Promise<any> {\n const client = this.getOpenAIClient()\n\n // Use optimization by default (can be disabled via config)\n const useOptimization = this.config.optimizeTranslation !== false\n\n if (!useOptimization) {\n // Use legacy approach: send entire structure\n return this.translateWithOpenAILegacy(data, fromLocale, toLocale, payload)\n }\n\n // Extract only translatable strings with deduplication\n const { deduplicationMap, metadata, strings } = this.extractTranslatableStrings(data)\n\n if (strings.size === 0) {\n // Nothing to translate\n return data\n }\n\n // Create a simple object with just the strings to translate\n const stringsToTranslate: Record<string, string> = {}\n strings.forEach((value, key) => {\n stringsToTranslate[key] = value\n })\n\n if (this.config.debugging) {\n const originalSize = JSON.stringify(data).length\n const optimizedSize = JSON.stringify(stringsToTranslate).length\n const reduction = ((1 - optimizedSize / originalSize) * 100).toFixed(1)\n\n // Calculate deduplication stats\n let totalPaths = 0\n deduplicationMap.forEach((paths) => {\n totalPaths += paths.length\n })\n const deduplicationSavings = totalPaths - strings.size\n const deduplicationPercent =\n totalPaths > 0 ? ((deduplicationSavings / totalPaths) * 100).toFixed(1) : '0'\n\n console.log('[Auto-Translate] ✨ Optimization Stats:')\n console.log(` 📊 Unique strings to translate: ${strings.size}`)\n console.log(` 🔄 Total string instances: ${totalPaths}`)\n console.log(\n ` 💾 Deduplication savings: ${deduplicationSavings} strings (${deduplicationPercent}%)`,\n )\n console.log(` 📦 Original JSON size: ${originalSize.toLocaleString()} bytes`)\n console.log(` 📦 Optimized JSON size: ${optimizedSize.toLocaleString()} bytes`)\n console.log(` 🎯 Total size reduction: ${reduction}%`)\n }\n\n try {\n // Get translation settings from global\n const settings = await this.getTranslationSettings(payload)\n\n // Add timeout configuration (default 30 seconds, configurable via plugin options)\n const timeout = this.config.provider?.timeout || 30000\n\n if (this.config.debugging) {\n console.log(\n `[Auto-Translate] Calling OpenAI API (timeout: ${timeout}ms, model: ${settings.model})`,\n )\n console.log(\n `[Auto-Translate] Payload size: ${JSON.stringify(stringsToTranslate).length} bytes`,\n )\n }\n\n // Build system message from settings\n const systemPrompt = settings.systemPrompt\n .replace('{fromLocale}', fromLocale)\n .replace('{toLocale}', toLocale)\n\n const systemMessage = `${systemPrompt}\\n\\n${settings.translationRules}`\n\n const requestParams: any = {\n messages: [\n {\n content: systemMessage,\n role: 'system',\n },\n {\n content: JSON.stringify(stringsToTranslate, null, 2),\n role: 'user',\n },\n ],\n model: settings.model,\n response_format: { type: 'json_object' },\n temperature: settings.temperature,\n }\n\n // Add maxTokens if specified\n if (settings.maxTokens) {\n requestParams.max_tokens = settings.maxTokens\n }\n\n const response = await client.chat.completions.create(requestParams, { timeout })\n\n const translatedText = response.choices[0]?.message?.content\n\n if (!translatedText) {\n throw new Error('No translation received from OpenAI')\n }\n\n if (this.config.debugging) {\n console.log(\n `[Auto-Translate] Received response from OpenAI (${translatedText.length} chars)`,\n )\n }\n\n let translatedStrings: any\n try {\n translatedStrings = JSON.parse(translatedText)\n } catch (parseError) {\n console.error('[Auto-Translate] Failed to parse OpenAI response as JSON')\n console.error('[Auto-Translate] Response text:', translatedText.substring(0, 500))\n throw new Error(\n `Invalid JSON response from OpenAI: ${parseError instanceof Error ? parseError.message : String(parseError)}`,\n )\n }\n\n // Convert back to Map\n const translationsMap = new Map<string, string>()\n for (const [key, value] of Object.entries(translatedStrings)) {\n if (typeof value === 'string') {\n translationsMap.set(key, value)\n }\n }\n\n // Reconstruct the full data structure with translations, applying deduplication\n return this.reconstructWithTranslations(metadata, translationsMap, deduplicationMap)\n } catch (error) {\n console.error('[Auto-Translate] Translation error:', error)\n\n // Provide more context about the error\n if (error && typeof error === 'object') {\n const err = error as any\n if (err.status) {\n console.error(`[Auto-Translate] OpenAI API status: ${err.status}`)\n }\n if (err.code) {\n console.error(`[Auto-Translate] Error code: ${err.code}`)\n }\n if (err.message) {\n console.error(`[Auto-Translate] Error message: ${err.message}`)\n }\n }\n\n // Add context to the error before re-throwing\n const contextualError = new Error(\n `Translation failed from ${fromLocale} to ${toLocale}: ${error instanceof Error ? error.message : String(error)}`,\n )\n contextualError.cause = error\n throw contextualError\n }\n }\n\n /**\n * Legacy translation method (sends entire structure)\n */\n private async translateWithOpenAILegacy(\n data: any,\n fromLocale: string,\n toLocale: string,\n payload: Payload,\n ): Promise<any> {\n const client = this.getOpenAIClient()\n const timeout = this.config.provider?.timeout || 30000\n\n try {\n // Get translation settings from global\n const settings = await this.getTranslationSettings(payload)\n\n // Build system message from settings\n const systemPrompt = settings.systemPrompt\n .replace('{fromLocale}', fromLocale)\n .replace('{toLocale}', toLocale)\n\n const systemMessage = `${systemPrompt}\\n\\n${settings.translationRules}`\n\n const requestParams: any = {\n messages: [\n {\n content: systemMessage,\n role: 'system',\n },\n {\n content: JSON.stringify(data, null, 2),\n role: 'user',\n },\n ],\n model: settings.model,\n response_format: { type: 'json_object' },\n temperature: settings.temperature,\n }\n\n // Add maxTokens if specified\n if (settings.maxTokens) {\n requestParams.max_tokens = settings.maxTokens\n }\n\n const response = await client.chat.completions.create(requestParams, { timeout })\n\n const translatedText = response.choices[0]?.message?.content\n\n if (!translatedText) {\n throw new Error('No translation received from OpenAI')\n }\n\n return JSON.parse(translatedText)\n } catch (error) {\n console.error('[Auto-Translate] Translation error:', error)\n throw error\n }\n }\n\n /**\n * Gets global and collection-specific excluded fields\n */\n getConfigExcludedFields(collection: string): string[] {\n const globalExclusions = this.config.excludeFields || []\n const collectionConfig = this.config.collections?.[collection]\n\n if (typeof collectionConfig === 'object' && collectionConfig.excludeFields) {\n return [...globalExclusions, ...collectionConfig.excludeFields]\n }\n\n return globalExclusions\n }\n\n /**\n * Gets translation exclusions for a document\n */\n async getExclusions(\n payload: Payload,\n collection: string,\n documentId: string,\n locale: string,\n ): Promise<string[]> {\n const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions'\n\n try {\n const result = await payload.find({\n collection: exclusionsSlug,\n limit: 1,\n where: {\n and: [\n { collection: { equals: collection } },\n { documentId: { equals: documentId } },\n { locale: { equals: locale } },\n ],\n },\n })\n\n if (result.docs.length > 0) {\n const exclusion = result.docs[0] as any\n return exclusion.excludedPaths?.map((item: any) => item.path) || []\n }\n\n return []\n } catch (error) {\n if (this.config.debugging) {\n payload.logger.error(`[Auto-Translate] Error fetching exclusions: ${error}`)\n }\n return []\n }\n }\n\n /**\n * Main translation method\n */\n async translate(options: TranslateOptions): Promise<any> {\n const { collection, data, excludedPaths = [], fromLocale, payload, toLocale } = options\n\n // Filter out excluded paths before translation\n const dataToTranslate = filterExcludedPaths(data, excludedPaths)\n\n if (this.config.debugging) {\n payload.logger.info(\n `[Auto-Translate] Translating from ${fromLocale} to ${toLocale} for collection ${collection}`,\n )\n payload.logger.info(`[Auto-Translate] Excluded paths: ${excludedPaths.join(', ')}`)\n }\n\n // Use custom translator if provided\n if (this.config.provider?.customTranslate) {\n return await this.config.provider.customTranslate(options)\n }\n\n // Use OpenAI by default\n return await this.translateWithOpenAI(dataToTranslate, fromLocale, toLocale, payload)\n }\n\n /**\n * Updates translation exclusions for a document\n */\n async updateExclusions(\n payload: Payload,\n collection: string,\n documentId: string,\n locale: string,\n excludedPaths: string[],\n ): Promise<void> {\n const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions'\n\n try {\n const existing = await payload.find({\n collection: exclusionsSlug,\n limit: 1,\n where: {\n and: [\n { collection: { equals: collection } },\n { documentId: { equals: documentId } },\n { locale: { equals: locale } },\n ],\n },\n })\n\n const exclusionsData = {\n collection,\n documentId,\n excludedPaths: excludedPaths.map((path) => ({ path })),\n locale,\n }\n\n if (existing.docs.length > 0) {\n await payload.update({\n id: existing.docs[0].id,\n collection: exclusionsSlug,\n data: exclusionsData,\n })\n } else {\n await payload.create({\n collection: exclusionsSlug,\n data: exclusionsData,\n })\n }\n\n if (this.config.debugging) {\n payload.logger.info(\n `[Auto-Translate] Updated exclusions for ${collection}:${documentId}:${locale}`,\n )\n }\n } catch (error) {\n if (this.config.debugging) {\n payload.logger.error(`[Auto-Translate] Error updating exclusions: ${error}`)\n }\n }\n }\n}\n"],"names":["OpenAI","filterExcludedPaths","TranslationService","client","config","extractFromLexicalNode","node","path","strings","deduplicationMap","enableDeduplication","type","text","trimmed","trim","length","shouldSkipString","textPath","has","existingPaths","get","push","set","children","Array","isArray","map","child","index","extractTranslatableStrings","data","Map","extract","obj","currentPath","undefined","isLexicalEditorNode","item","result","key","value","Object","entries","newPath","trimmedValue","metadata","getOpenAIClient","apiKey","provider","process","env","OPENAI_API_KEY","Error","baseURL","OPENAI_BASE_URL","getOriginalValue","parts","split","filter","Boolean","current","part","getTranslationSettings","payload","settingsSlug","translationSettingsSlug","defaults","maxTokens","model","systemPrompt","temperature","translationRules","settings","findGlobal","slug","error","debugging","console","warn","reconstructWithTranslations","translations","fullTranslations","forEach","translatedValue","originalPath","originalValue","replace","paths","includes","reconstruct","startsWith","slice","str","test","minLength","minStringLength","toLowerCase","pathLower","endsWith","translateWithOpenAI","fromLocale","toLocale","useOptimization","optimizeTranslation","translateWithOpenAILegacy","size","stringsToTranslate","originalSize","JSON","stringify","optimizedSize","reduction","toFixed","totalPaths","deduplicationSavings","deduplicationPercent","log","toLocaleString","timeout","systemMessage","requestParams","messages","content","role","response_format","max_tokens","response","chat","completions","create","translatedText","choices","message","translatedStrings","parse","parseError","substring","String","translationsMap","err","status","code","contextualError","cause","getConfigExcludedFields","collection","globalExclusions","excludeFields","collectionConfig","collections","getExclusions","documentId","locale","exclusionsSlug","translationExclusionsSlug","find","limit","where","and","equals","docs","exclusion","excludedPaths","logger","translate","options","dataToTranslate","info","join","customTranslate","updateExclusions","existing","exclusionsData","update","id"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/services/translationService.ts"],"sourcesContent":["import type { Payload } from 'payload'\n\nimport OpenAI from 'openai'\n\nimport type { AutoTranslateConfig, TranslateOptions } from '../types/index.js'\n\nimport { filterExcludedPaths } from '../utilities/fieldHelpers.js'\n\nexport class TranslationService {\n private client?: OpenAI\n private config: AutoTranslateConfig\n\n constructor(config: AutoTranslateConfig) {\n this.config = config\n }\n\n /**\n * Extracts translatable text from lexical editor nodes\n */\n private extractFromLexicalNode(\n node: any,\n path: string,\n strings: Map<string, string>,\n deduplicationMap: Map<string, string[]>,\n ): any {\n const enableDeduplication = this.config.enableDeduplication !== false // Default to true\n\n // Handle text nodes - skip whitespace-only or very short text\n if (node.type === 'text' && node.text && typeof node.text === 'string') {\n const trimmed = node.text.trim()\n\n // Skip if empty, whitespace-only, or too short\n if (trimmed.length === 0 || this.shouldSkipString(node.text, `${path}.text`)) {\n return node\n }\n\n const textPath = `${path}.text`\n\n if (enableDeduplication) {\n // Check for deduplication\n if (deduplicationMap.has(trimmed)) {\n // This string already exists, just store the path mapping\n const existingPaths = deduplicationMap.get(trimmed)!\n existingPaths.push(textPath)\n return { ...node, text: `__TRANSLATE_${textPath}__` }\n } else {\n // New unique string\n strings.set(textPath, node.text)\n deduplicationMap.set(trimmed, [textPath])\n return { ...node, text: `__TRANSLATE_${textPath}__` }\n }\n } else {\n // No deduplication - add every string\n strings.set(textPath, node.text)\n deduplicationMap.set(trimmed, [textPath])\n return { ...node, text: `__TRANSLATE_${textPath}__` }\n }\n }\n\n // Handle nodes with children\n if (node.children && Array.isArray(node.children)) {\n return {\n ...node,\n children: node.children.map((child: any, index: number) =>\n this.extractFromLexicalNode(\n child,\n `${path}.children[${index}]`,\n strings,\n deduplicationMap,\n ),\n ),\n }\n }\n\n return node\n }\n\n /**\n * Extracts translatable strings from data structure\n * Returns a map of paths to translatable values and metadata for reconstruction\n */\n private extractTranslatableStrings(\n data: any,\n path: string = '',\n ): { deduplicationMap: Map<string, string[]>; metadata: any; strings: Map<string, string> } {\n const strings = new Map<string, string>()\n const deduplicationMap = new Map<string, string[]>() // value -> [paths]\n const enableDeduplication = this.config.enableDeduplication !== false // Default to true\n\n const extract = (obj: any, currentPath: string): any => {\n if (obj === null || obj === undefined) {\n return obj\n }\n\n // Handle lexical editor format\n if (this.isLexicalEditorNode(obj)) {\n return this.extractFromLexicalNode(obj, currentPath, strings, deduplicationMap)\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item, index) => extract(item, `${currentPath}[${index}]`))\n }\n\n // Handle objects\n if (typeof obj === 'object') {\n const result: any = {}\n for (const [key, value] of Object.entries(obj)) {\n const newPath = currentPath ? `${currentPath}.${key}` : key\n result[key] = extract(value, newPath)\n }\n return result\n }\n\n // Handle strings\n if (typeof obj === 'string' && obj.trim().length > 0) {\n // Skip IDs and other non-translatable strings\n if (!this.shouldSkipString(obj, currentPath)) {\n if (enableDeduplication) {\n // Check for deduplication\n const trimmedValue = obj.trim()\n if (deduplicationMap.has(trimmedValue)) {\n // This string already exists, just store the path mapping\n const existingPaths = deduplicationMap.get(trimmedValue)!\n existingPaths.push(currentPath)\n return `__TRANSLATE_${currentPath}__`\n } else {\n // New unique string\n strings.set(currentPath, obj)\n deduplicationMap.set(trimmedValue, [currentPath])\n return `__TRANSLATE_${currentPath}__`\n }\n } else {\n // No deduplication - add every string\n strings.set(currentPath, obj)\n deduplicationMap.set(obj.trim(), [currentPath])\n return `__TRANSLATE_${currentPath}__`\n }\n }\n }\n\n return obj\n }\n\n const metadata = extract(data, path)\n return { deduplicationMap, metadata, strings }\n }\n\n /**\n * Lazily initialize OpenAI client only when needed\n */\n private getOpenAIClient(): OpenAI {\n if (!this.client) {\n const apiKey = this.config.provider?.apiKey || process.env.OPENAI_API_KEY\n if (!apiKey) {\n throw new Error(\n 'OpenAI API key is required. Set OPENAI_API_KEY environment variable or provide it in plugin config.',\n )\n }\n\n this.client = new OpenAI({\n apiKey,\n baseURL: this.config.provider?.baseURL || process.env.OPENAI_BASE_URL,\n })\n }\n return this.client\n }\n\n /**\n * Gets the original value at a path in metadata (helper for deduplication)\n */\n private getOriginalValue(metadata: any, path: string): null | string {\n try {\n const parts = path.split(/[.[\\]]/).filter(Boolean)\n let current = metadata\n for (const part of parts) {\n if (current === null || current === undefined) {\n return null\n }\n current = current[part]\n }\n return typeof current === 'string' ? current : null\n } catch {\n return null\n }\n }\n\n /**\n * Gets translation settings from the global or returns defaults\n */\n private async getTranslationSettings(payload: Payload): Promise<{\n maxTokens?: number\n model: string\n systemPrompt: string\n temperature: number\n translationRules: string\n }> {\n const settingsSlug = this.config.translationSettingsSlug || 'translation-settings'\n\n // Default values\n const defaults = {\n maxTokens: undefined,\n model: this.config.provider?.model || 'gpt-4o',\n systemPrompt:\n 'You are a professional translator. Translate the JSON object values from {fromLocale} to {toLocale}.',\n temperature: 0.3,\n translationRules: `Rules:\n - Only translate the values, never the keys\n - Preserve the exact JSON structure\n - Maintain formatting, HTML tags, and special characters\n - Return only valid JSON without any markdown formatting or code blocks\n - If a value is already in the target language or is a proper noun, keep it as is`,\n }\n\n try {\n const settings = await payload.findGlobal({\n slug: settingsSlug,\n })\n\n if (settings) {\n return {\n maxTokens: settings.maxTokens || defaults.maxTokens,\n model: settings.model || defaults.model,\n systemPrompt: settings.systemPrompt || defaults.systemPrompt,\n temperature:\n typeof settings.temperature === 'number' ? settings.temperature : defaults.temperature,\n translationRules: settings.translationRules || defaults.translationRules,\n }\n }\n } catch (error) {\n if (this.config.debugging) {\n console.warn(\n '[Auto-Translate] Could not fetch translation settings, using defaults:',\n error,\n )\n }\n }\n\n return defaults\n }\n\n /**\n * Checks if an object is a lexical editor node\n */\n private isLexicalEditorNode(obj: any): boolean {\n return (\n obj &&\n typeof obj === 'object' &&\n 'type' in obj &&\n 'version' in obj &&\n ('children' in obj || 'text' in obj)\n )\n }\n\n /**\n * Reconstructs data with translated strings, applying deduplicated translations\n */\n private reconstructWithTranslations(\n metadata: any,\n translations: Map<string, string>,\n deduplicationMap: Map<string, string[]>,\n ): any {\n // Build a comprehensive translation map including deduplicated paths\n const fullTranslations = new Map<string, string>()\n\n // For each unique string that was translated\n translations.forEach((translatedValue, originalPath) => {\n fullTranslations.set(originalPath, translatedValue)\n\n // Find all paths that had the same original value\n const originalValue = this.getOriginalValue(metadata, originalPath)\n if (originalValue) {\n const trimmed = originalValue.replace(/^__TRANSLATE_(.+)__$/, '$1')\n // Look through deduplication map to find all paths with same value\n for (const [value, paths] of deduplicationMap.entries()) {\n if (paths.includes(originalPath)) {\n // Apply the same translation to all paths with this value\n paths.forEach((path) => {\n fullTranslations.set(path, translatedValue)\n })\n break\n }\n }\n }\n })\n\n const reconstruct = (obj: any): any => {\n if (obj === null || obj === undefined) {\n return obj\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item) => reconstruct(item))\n }\n\n // Handle objects\n if (typeof obj === 'object') {\n const result: any = {}\n for (const [key, value] of Object.entries(obj)) {\n result[key] = reconstruct(value)\n }\n return result\n }\n\n // Replace translation placeholders\n if (typeof obj === 'string' && obj.startsWith('__TRANSLATE_')) {\n const path = obj.slice(12, -2) // Remove __TRANSLATE_ prefix and __ suffix\n return fullTranslations.get(path) || obj\n }\n\n return obj\n }\n\n return reconstruct(metadata)\n }\n\n /**\n * Determines if a string should be skipped from translation\n */\n private shouldSkipString(str: string, path: string): boolean {\n // Skip IDs (MongoDB ObjectIds and similar)\n if (/^[a-f0-9]{24}$/i.test(str)) {\n return true\n }\n\n // Skip URLs\n if (/^https?:\\/\\//.test(str)) {\n return true\n }\n\n // Skip file paths\n if (/^\\/\\S*\\.(jpg|jpeg|png|gif|webp|svg|pdf|mp4|webm|ogg|mp3|wav)$/i.test(str)) {\n return true\n }\n\n // Skip email addresses\n if (/^[^\\s@]+@[^\\s@][^\\s.@]*\\.[^\\s@]+$/.test(str)) {\n return true\n }\n\n // Skip ISO date strings\n if (/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(str)) {\n return true\n }\n\n // Skip date-time strings like \"2019-01-31 12:05:04\"\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/.test(str)) {\n return true\n }\n\n // Skip percentages like \"100%\"\n if (/^\\d+%$/.test(str)) {\n return true\n }\n\n // Skip pure numbers\n if (/^\\d+$/.test(str)) {\n return true\n }\n\n // Skip whitespace-only strings (including single spaces)\n if (str.trim().length === 0) {\n return true\n }\n\n // Skip very short strings based on config (default: 3 characters)\n const minLength = this.config.minStringLength !== undefined ? this.config.minStringLength : 3\n if (str.trim().length < minLength) {\n return true\n }\n\n // Skip status values\n if (['archived', 'draft', 'pending', 'published'].includes(str.toLowerCase())) {\n return true\n }\n\n // Skip paths ending with id, createdAt, updatedAt, etc.\n const pathLower = path.toLowerCase()\n if (\n pathLower.endsWith('id') ||\n pathLower.endsWith('_id') ||\n pathLower.includes('createdat') ||\n pathLower.includes('updatedat')\n ) {\n return true\n }\n\n return false\n }\n\n /**\n * Legacy translation method (sends entire structure)\n */\n private async translateWithOpenAILegacy(\n data: any,\n fromLocale: string,\n toLocale: string,\n payload: Payload,\n ): Promise<any> {\n const client = this.getOpenAIClient()\n const timeout = this.config.provider?.timeout || 30000\n\n try {\n // Get translation settings from global\n const settings = await this.getTranslationSettings(payload)\n\n // Build system message from settings\n const systemPrompt = settings.systemPrompt\n .replace('{fromLocale}', fromLocale)\n .replace('{toLocale}', toLocale)\n\n const systemMessage = `${systemPrompt}\\n\\n${settings.translationRules}`\n\n const requestParams: any = {\n messages: [\n {\n content: systemMessage,\n role: 'system',\n },\n {\n content: JSON.stringify(data, null, 2),\n role: 'user',\n },\n ],\n model: settings.model,\n response_format: { type: 'json_object' },\n temperature: settings.temperature,\n }\n\n // Add maxTokens if specified\n if (settings.maxTokens) {\n requestParams.max_tokens = settings.maxTokens\n }\n\n const response = await client.chat.completions.create(requestParams, { timeout })\n\n const translatedText = response.choices[0]?.message?.content\n\n if (!translatedText) {\n throw new Error('No translation received from OpenAI')\n }\n\n return JSON.parse(translatedText)\n } catch (error) {\n console.error('[Auto-Translate] Translation error:', error)\n throw error\n }\n }\n\n /**\n * Gets global and collection-specific excluded fields\n */\n getConfigExcludedFields(collection: string): string[] {\n const globalExclusions = this.config.excludeFields || []\n const collectionConfig = this.config.collections?.[collection]\n\n if (typeof collectionConfig === 'object' && collectionConfig.excludeFields) {\n return [...globalExclusions, ...collectionConfig.excludeFields]\n }\n\n return globalExclusions\n }\n\n /**\n * Gets translation exclusions for a document\n */\n async getExclusions(\n payload: Payload,\n collection: string,\n documentId: string,\n locale: string,\n ): Promise<string[]> {\n const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions'\n\n try {\n const result = await payload.find({\n collection: exclusionsSlug,\n limit: 1,\n where: {\n and: [\n { collection: { equals: collection } },\n { documentId: { equals: documentId } },\n { locale: { equals: locale } },\n ],\n },\n })\n\n if (result.docs.length > 0) {\n const exclusion = result.docs[0] as any\n return exclusion.excludedPaths?.map((item: any) => item.path) || []\n }\n\n return []\n } catch (error) {\n if (this.config.debugging) {\n payload.logger.error(`[Auto-Translate] Error fetching exclusions: ${error}`)\n }\n return []\n }\n }\n\n /**\n * Main translation method\n */\n async translate(options: TranslateOptions): Promise<any> {\n const { collection, data, excludedPaths = [], fromLocale, payload, toLocale } = options\n\n // Filter out excluded paths before translation\n const dataToTranslate = filterExcludedPaths(data, excludedPaths)\n\n if (this.config.debugging) {\n payload.logger.info(\n `[Auto-Translate] Translating from ${fromLocale} to ${toLocale} for collection ${collection}`,\n )\n payload.logger.info(`[Auto-Translate] Excluded paths: ${excludedPaths.join(', ')}`)\n }\n\n // Use custom translator if provided\n if (this.config.provider?.customTranslate) {\n return await this.config.provider.customTranslate(options)\n }\n\n // Use OpenAI by default\n return await this.translateWithOpenAI(dataToTranslate, fromLocale, toLocale, payload)\n }\n\n /**\n * Translates using OpenAI API (optimized version)\n * This method is now public and can be used directly in your application\n */\n async translateWithOpenAI(\n data: any,\n fromLocale: string,\n toLocale: string,\n payload: Payload,\n ): Promise<any> {\n const client = this.getOpenAIClient()\n\n // Use optimization by default (can be disabled via config)\n const useOptimization = this.config.optimizeTranslation !== false\n\n if (!useOptimization) {\n // Use legacy approach: send entire structure\n return this.translateWithOpenAILegacy(data, fromLocale, toLocale, payload)\n }\n\n // Extract only translatable strings with deduplication\n const { deduplicationMap, metadata, strings } = this.extractTranslatableStrings(data)\n\n if (strings.size === 0) {\n // Nothing to translate\n return data\n }\n\n // Create a simple object with just the strings to translate\n const stringsToTranslate: Record<string, string> = {}\n strings.forEach((value, key) => {\n stringsToTranslate[key] = value\n })\n\n if (this.config.debugging) {\n const originalSize = JSON.stringify(data).length\n const optimizedSize = JSON.stringify(stringsToTranslate).length\n const reduction = ((1 - optimizedSize / originalSize) * 100).toFixed(1)\n\n // Calculate deduplication stats\n let totalPaths = 0\n deduplicationMap.forEach((paths) => {\n totalPaths += paths.length\n })\n const deduplicationSavings = totalPaths - strings.size\n const deduplicationPercent =\n totalPaths > 0 ? ((deduplicationSavings / totalPaths) * 100).toFixed(1) : '0'\n\n console.log('[Auto-Translate] ✨ Optimization Stats:')\n console.log(` 📊 Unique strings to translate: ${strings.size}`)\n console.log(` 🔄 Total string instances: ${totalPaths}`)\n console.log(\n ` 💾 Deduplication savings: ${deduplicationSavings} strings (${deduplicationPercent}%)`,\n )\n console.log(` 📦 Original JSON size: ${originalSize.toLocaleString()} bytes`)\n console.log(` 📦 Optimized JSON size: ${optimizedSize.toLocaleString()} bytes`)\n console.log(` 🎯 Total size reduction: ${reduction}%`)\n }\n\n try {\n // Get translation settings from global\n const settings = await this.getTranslationSettings(payload)\n\n // Add timeout configuration (default 30 seconds, configurable via plugin options)\n const timeout = this.config.provider?.timeout || 30000\n\n if (this.config.debugging) {\n console.log(\n `[Auto-Translate] Calling OpenAI API (timeout: ${timeout}ms, model: ${settings.model})`,\n )\n console.log(\n `[Auto-Translate] Payload size: ${JSON.stringify(stringsToTranslate).length} bytes`,\n )\n }\n\n // Build system message from settings\n const systemPrompt = settings.systemPrompt\n .replace('{fromLocale}', fromLocale)\n .replace('{toLocale}', toLocale)\n\n const systemMessage = `${systemPrompt}\\n\\n${settings.translationRules}`\n\n const requestParams: any = {\n messages: [\n {\n content: systemMessage,\n role: 'system',\n },\n {\n content: JSON.stringify(stringsToTranslate, null, 2),\n role: 'user',\n },\n ],\n model: settings.model,\n response_format: { type: 'json_object' },\n temperature: settings.temperature,\n }\n\n // Add maxTokens if specified\n if (settings.maxTokens) {\n requestParams.max_tokens = settings.maxTokens\n }\n\n const response = await client.chat.completions.create(requestParams, { timeout })\n\n const translatedText = response.choices[0]?.message?.content\n\n if (!translatedText) {\n throw new Error('No translation received from OpenAI')\n }\n\n if (this.config.debugging) {\n console.log(\n `[Auto-Translate] Received response from OpenAI (${translatedText.length} chars)`,\n )\n }\n\n let translatedStrings: any\n try {\n translatedStrings = JSON.parse(translatedText)\n } catch (parseError) {\n console.error('[Auto-Translate] Failed to parse OpenAI response as JSON')\n console.error('[Auto-Translate] Response text:', translatedText.substring(0, 500))\n throw new Error(\n `Invalid JSON response from OpenAI: ${parseError instanceof Error ? parseError.message : String(parseError)}`,\n )\n }\n\n // Convert back to Map\n const translationsMap = new Map<string, string>()\n for (const [key, value] of Object.entries(translatedStrings)) {\n if (typeof value === 'string') {\n translationsMap.set(key, value)\n }\n }\n\n // Reconstruct the full data structure with translations, applying deduplication\n return this.reconstructWithTranslations(metadata, translationsMap, deduplicationMap)\n } catch (error) {\n console.error('[Auto-Translate] Translation error:', error)\n\n // Provide more context about the error\n if (error && typeof error === 'object') {\n const err = error as any\n if (err.status) {\n console.error(`[Auto-Translate] OpenAI API status: ${err.status}`)\n }\n if (err.code) {\n console.error(`[Auto-Translate] Error code: ${err.code}`)\n }\n if (err.message) {\n console.error(`[Auto-Translate] Error message: ${err.message}`)\n }\n }\n\n // Add context to the error before re-throwing\n const contextualError = new Error(\n `Translation failed from ${fromLocale} to ${toLocale}: ${error instanceof Error ? error.message : String(error)}`,\n )\n contextualError.cause = error\n throw contextualError\n }\n }\n\n /**\n * Updates translation exclusions for a document\n */\n async updateExclusions(\n payload: Payload,\n collection: string,\n documentId: string,\n locale: string,\n excludedPaths: string[],\n ): Promise<void> {\n const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions'\n\n try {\n const existing = await payload.find({\n collection: exclusionsSlug,\n limit: 1,\n where: {\n and: [\n { collection: { equals: collection } },\n { documentId: { equals: documentId } },\n { locale: { equals: locale } },\n ],\n },\n })\n\n const exclusionsData = {\n collection,\n documentId,\n excludedPaths: excludedPaths.map((path) => ({ path })),\n locale,\n }\n\n if (existing.docs.length > 0) {\n await payload.update({\n id: existing.docs[0].id,\n collection: exclusionsSlug,\n data: exclusionsData,\n })\n } else {\n await payload.create({\n collection: exclusionsSlug,\n data: exclusionsData,\n })\n }\n\n if (this.config.debugging) {\n payload.logger.info(\n `[Auto-Translate] Updated exclusions for ${collection}:${documentId}:${locale}`,\n )\n }\n } catch (error) {\n if (this.config.debugging) {\n payload.logger.error(`[Auto-Translate] Error updating exclusions: ${error}`)\n }\n }\n }\n}\n"],"names":["OpenAI","filterExcludedPaths","TranslationService","client","config","extractFromLexicalNode","node","path","strings","deduplicationMap","enableDeduplication","type","text","trimmed","trim","length","shouldSkipString","textPath","has","existingPaths","get","push","set","children","Array","isArray","map","child","index","extractTranslatableStrings","data","Map","extract","obj","currentPath","undefined","isLexicalEditorNode","item","result","key","value","Object","entries","newPath","trimmedValue","metadata","getOpenAIClient","apiKey","provider","process","env","OPENAI_API_KEY","Error","baseURL","OPENAI_BASE_URL","getOriginalValue","parts","split","filter","Boolean","current","part","getTranslationSettings","payload","settingsSlug","translationSettingsSlug","defaults","maxTokens","model","systemPrompt","temperature","translationRules","settings","findGlobal","slug","error","debugging","console","warn","reconstructWithTranslations","translations","fullTranslations","forEach","translatedValue","originalPath","originalValue","replace","paths","includes","reconstruct","startsWith","slice","str","test","minLength","minStringLength","toLowerCase","pathLower","endsWith","translateWithOpenAILegacy","fromLocale","toLocale","timeout","systemMessage","requestParams","messages","content","role","JSON","stringify","response_format","max_tokens","response","chat","completions","create","translatedText","choices","message","parse","getConfigExcludedFields","collection","globalExclusions","excludeFields","collectionConfig","collections","getExclusions","documentId","locale","exclusionsSlug","translationExclusionsSlug","find","limit","where","and","equals","docs","exclusion","excludedPaths","logger","translate","options","dataToTranslate","info","join","customTranslate","translateWithOpenAI","useOptimization","optimizeTranslation","size","stringsToTranslate","originalSize","optimizedSize","reduction","toFixed","totalPaths","deduplicationSavings","deduplicationPercent","log","toLocaleString","translatedStrings","parseError","substring","String","translationsMap","err","status","code","contextualError","cause","updateExclusions","existing","exclusionsData","update","id"],"mappings":"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"}
|
package/package.json
CHANGED