@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
@@ -0,0 +1,103 @@
1
+ import type { CollectionSlug, Payload } from 'payload';
2
+ export type AutoTranslateConfig = {
3
+ /**
4
+ * Auto-inject translation control UI into all localized fields (default: true)
5
+ * Note: This is ignored if enableExclusions is false
6
+ */
7
+ autoInjectUI?: boolean;
8
+ /**
9
+ * List of collections to enable auto-translation
10
+ */
11
+ collections?: Partial<Record<CollectionSlug, boolean | CollectionTranslateConfig>>;
12
+ /**
13
+ * Enable field-level translation exclusions (default: true)
14
+ * When disabled:
15
+ * - Translation exclusions collection is hidden
16
+ * - Translation control buttons are not added to fields
17
+ * - All localized fields are always translated
18
+ */
19
+ enableExclusions?: boolean;
20
+ /**
21
+ * Show debug logs
22
+ */
23
+ debugging?: boolean;
24
+ /**
25
+ * Disable the plugin entirely
26
+ */
27
+ disabled?: boolean;
28
+ /**
29
+ * Enable deduplication of identical strings (default: true)
30
+ * When enabled, identical strings are only translated once and reused
31
+ * This significantly speeds up translation for documents with repeated content
32
+ */
33
+ enableDeduplication?: boolean;
34
+ /**
35
+ * Enable translation sync by default
36
+ */
37
+ enableTranslationSyncByDefault?: boolean;
38
+ /**
39
+ * Fields to exclude from translation globally
40
+ */
41
+ excludeFields?: string[];
42
+ /**
43
+ * Minimum string length to translate (default: 3)
44
+ * Strings shorter than this (after trimming) will be skipped
45
+ * This helps avoid translating single characters, dashes, spaces, etc.
46
+ */
47
+ minStringLength?: number;
48
+ /**
49
+ * Use optimized translation that extracts only translatable strings (default: true)
50
+ * This dramatically reduces API payload size and improves translation speed for large documents
51
+ */
52
+ optimizeTranslation?: boolean;
53
+ /**
54
+ * Translation provider settings
55
+ */
56
+ provider?: {
57
+ apiKey?: string;
58
+ baseURL?: string;
59
+ customTranslate?: (options: TranslateOptions) => Promise<any>;
60
+ model?: string;
61
+ type: 'custom' | 'openai';
62
+ };
63
+ /**
64
+ * Collection slug for storing translation exclusions metadata
65
+ */
66
+ translationExclusionsSlug?: string;
67
+ /**
68
+ * Global slug for translation settings UI
69
+ */
70
+ translationSettingsSlug?: string;
71
+ };
72
+ export type CollectionTranslateConfig = {
73
+ /**
74
+ * Enable translation for this collection
75
+ */
76
+ enabled?: boolean;
77
+ /**
78
+ * Fields to exclude from translation for this specific collection
79
+ */
80
+ excludeFields?: string[];
81
+ };
82
+ export type TranslateOptions = {
83
+ collection: CollectionSlug;
84
+ data: any;
85
+ excludedPaths?: string[];
86
+ fromLocale: string;
87
+ payload: Payload;
88
+ toLocale: string;
89
+ };
90
+ export type TranslationExclusion = {
91
+ collection: CollectionSlug;
92
+ createdAt?: string;
93
+ documentId: string;
94
+ excludedPaths: string[];
95
+ id?: string;
96
+ locale: string;
97
+ updatedAt?: string;
98
+ };
99
+ export type FieldPath = {
100
+ parentPath?: string;
101
+ path: string;
102
+ value: any;
103
+ };
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/index.ts"],"sourcesContent":["import type { CollectionSlug, Payload } from 'payload'\n\nexport type AutoTranslateConfig = {\n /**\n * Auto-inject translation control UI into all localized fields (default: true)\n * Note: This is ignored if enableExclusions is false\n */\n autoInjectUI?: boolean\n\n /**\n * List of collections to enable auto-translation\n */\n collections?: Partial<Record<CollectionSlug, boolean | CollectionTranslateConfig>>\n\n /**\n * Enable field-level translation exclusions (default: true)\n * When disabled:\n * - Translation exclusions collection is hidden\n * - Translation control buttons are not added to fields\n * - All localized fields are always translated\n */\n enableExclusions?: boolean\n\n /**\n * Show debug logs\n */\n debugging?: boolean\n\n /**\n * Disable the plugin entirely\n */\n disabled?: boolean\n\n /**\n * Enable deduplication of identical strings (default: true)\n * When enabled, identical strings are only translated once and reused\n * This significantly speeds up translation for documents with repeated content\n */\n enableDeduplication?: boolean\n\n /**\n * Enable translation sync by default\n */\n enableTranslationSyncByDefault?: boolean\n\n /**\n * Fields to exclude from translation globally\n */\n excludeFields?: string[]\n\n /**\n * Minimum string length to translate (default: 3)\n * Strings shorter than this (after trimming) will be skipped\n * This helps avoid translating single characters, dashes, spaces, etc.\n */\n minStringLength?: number\n\n /**\n * Use optimized translation that extracts only translatable strings (default: true)\n * This dramatically reduces API payload size and improves translation speed for large documents\n */\n optimizeTranslation?: boolean\n\n /**\n * Translation provider settings\n */\n provider?: {\n apiKey?: string\n baseURL?: string\n customTranslate?: (options: TranslateOptions) => Promise<any>\n model?: string\n type: 'custom' | 'openai'\n }\n\n /**\n * Collection slug for storing translation exclusions metadata\n */\n translationExclusionsSlug?: string\n\n /**\n * Global slug for translation settings UI\n */\n translationSettingsSlug?: string\n}\n\nexport type CollectionTranslateConfig = {\n /**\n * Enable translation for this collection\n */\n enabled?: boolean\n\n /**\n * Fields to exclude from translation for this specific collection\n */\n excludeFields?: string[]\n}\n\nexport type TranslateOptions = {\n collection: CollectionSlug\n data: any\n excludedPaths?: string[]\n fromLocale: string\n payload: Payload\n toLocale: string\n}\n\nexport type TranslationExclusion = {\n collection: CollectionSlug\n createdAt?: string\n documentId: string\n excludedPaths: string[] // Field paths like 'title', 'content.0.description'\n id?: string\n locale: string\n updatedAt?: string\n}\n\nexport type FieldPath = {\n parentPath?: string\n path: string\n value: any\n}\n"],"names":[],"mappings":"AAoHA,WAIC"}
@@ -0,0 +1,34 @@
1
+ import type { Field } from 'payload';
2
+ import type { FieldPath } from '../types/index.js';
3
+ /**
4
+ * Recursively extracts all field paths and their values from a document
5
+ */
6
+ export declare function extractFieldPaths(data: any, parentPath?: string, fields?: Field[]): FieldPath[];
7
+ /**
8
+ * Filters out excluded paths from data before translation
9
+ */
10
+ export declare function filterExcludedPaths(data: any, excludedPaths: string[]): any;
11
+ /**
12
+ * Merges translated data back, respecting excluded paths
13
+ */
14
+ export declare function mergeTranslatedData(originalData: any, translatedData: any, excludedPaths: string[]): any;
15
+ /**
16
+ * Checks if a path is excluded or if any parent path is excluded
17
+ */
18
+ export declare function isPathExcluded(path: string, excludedPaths: string[]): boolean;
19
+ /**
20
+ * Gets value at path using dot notation
21
+ */
22
+ export declare function getValueAtPath(obj: any, path: string): any;
23
+ /**
24
+ * Sets value at path using dot notation
25
+ */
26
+ export declare function setValueAtPath(obj: any, path: string, value: any): void;
27
+ /**
28
+ * Checks if a field is a localized field
29
+ */
30
+ export declare function isLocalizedField(field: Field): boolean;
31
+ /**
32
+ * Gets all localized field paths from a collection config
33
+ */
34
+ export declare function getLocalizedFieldPaths(fields: Field[], parentPath?: string): string[];
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Recursively extracts all field paths and their values from a document
3
+ */ export function extractFieldPaths(data, parentPath = '', fields) {
4
+ const paths = [];
5
+ if (!data || typeof data !== 'object') {
6
+ return paths;
7
+ }
8
+ for (const [key, value] of Object.entries(data)){
9
+ const currentPath = parentPath ? `${parentPath}.${key}` : key;
10
+ // Skip internal fields
11
+ if (key === 'id' || key === '_id' || key === 'createdAt' || key === 'updatedAt' || key === 'translationSync' || key === '__v') {
12
+ continue;
13
+ }
14
+ // Add current field
15
+ paths.push({
16
+ parentPath,
17
+ path: currentPath,
18
+ value
19
+ });
20
+ // Recursively process nested objects
21
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
22
+ paths.push(...extractFieldPaths(value, currentPath, fields));
23
+ }
24
+ // Process arrays
25
+ if (Array.isArray(value)) {
26
+ value.forEach((item, index)=>{
27
+ const arrayPath = `${currentPath}.${index}`;
28
+ if (item && typeof item === 'object') {
29
+ paths.push(...extractFieldPaths(item, arrayPath, fields));
30
+ }
31
+ });
32
+ }
33
+ }
34
+ return paths;
35
+ }
36
+ /**
37
+ * Filters out excluded paths from data before translation
38
+ */ export function filterExcludedPaths(data, excludedPaths) {
39
+ if (!data || typeof data !== 'object' || excludedPaths.length === 0) {
40
+ return data;
41
+ }
42
+ const filtered = JSON.parse(JSON.stringify(data)) // Deep clone
43
+ ;
44
+ for (const excludedPath of excludedPaths){
45
+ deletePath(filtered, excludedPath);
46
+ }
47
+ return filtered;
48
+ }
49
+ /**
50
+ * Merges translated data back, respecting excluded paths
51
+ */ export function mergeTranslatedData(originalData, translatedData, excludedPaths) {
52
+ if (!translatedData || typeof translatedData !== 'object') {
53
+ return originalData;
54
+ }
55
+ const merged = JSON.parse(JSON.stringify(originalData)) // Deep clone
56
+ ;
57
+ function merge(target, source, currentPath = '') {
58
+ for(const key in source){
59
+ const fullPath = currentPath ? `${currentPath}.${key}` : key;
60
+ // Skip if this path is excluded
61
+ if (isPathExcluded(fullPath, excludedPaths)) {
62
+ continue;
63
+ }
64
+ // Skip internal fields
65
+ if (key === 'id' || key === '_id' || key === 'createdAt' || key === 'updatedAt' || key === 'translationSync' || key === '__v') {
66
+ continue;
67
+ }
68
+ if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
69
+ if (!target[key]) {
70
+ target[key] = {};
71
+ }
72
+ merge(target[key], source[key], fullPath);
73
+ } else {
74
+ target[key] = source[key];
75
+ }
76
+ }
77
+ }
78
+ merge(merged, translatedData);
79
+ return merged;
80
+ }
81
+ /**
82
+ * Checks if a path is excluded or if any parent path is excluded
83
+ */ export function isPathExcluded(path, excludedPaths) {
84
+ return excludedPaths.some((excludedPath)=>{
85
+ // Exact match
86
+ if (path === excludedPath) {
87
+ return true;
88
+ }
89
+ // Check if path is a child of excluded path
90
+ if (path.startsWith(`${excludedPath}.`)) {
91
+ return true;
92
+ }
93
+ // Check if excluded path is a pattern match for arrays (e.g., content.0.title)
94
+ const pathParts = path.split('.');
95
+ const excludedParts = excludedPath.split('.');
96
+ for(let i = 0; i < Math.min(pathParts.length, excludedParts.length); i++){
97
+ if (excludedParts[i] !== pathParts[i]) {
98
+ // Check if it's an array index difference
99
+ if (!isNaN(Number(pathParts[i])) && !isNaN(Number(excludedParts[i]))) {
100
+ continue;
101
+ }
102
+ return false;
103
+ }
104
+ }
105
+ return excludedParts.length <= pathParts.length;
106
+ });
107
+ }
108
+ /**
109
+ * Deletes a path from an object using dot notation
110
+ */ function deletePath(obj, path) {
111
+ const parts = path.split('.');
112
+ let current = obj;
113
+ for(let i = 0; i < parts.length - 1; i++){
114
+ if (!current[parts[i]]) {
115
+ return;
116
+ }
117
+ current = current[parts[i]];
118
+ }
119
+ delete current[parts[parts.length - 1]];
120
+ }
121
+ /**
122
+ * Gets value at path using dot notation
123
+ */ export function getValueAtPath(obj, path) {
124
+ return path.split('.').reduce((current, part)=>current?.[part], obj);
125
+ }
126
+ /**
127
+ * Sets value at path using dot notation
128
+ */ export function setValueAtPath(obj, path, value) {
129
+ const parts = path.split('.');
130
+ let current = obj;
131
+ for(let i = 0; i < parts.length - 1; i++){
132
+ if (!current[parts[i]]) {
133
+ current[parts[i]] = {};
134
+ }
135
+ current = current[parts[i]];
136
+ }
137
+ current[parts[parts.length - 1]] = value;
138
+ }
139
+ /**
140
+ * Checks if a field is a localized field
141
+ */ export function isLocalizedField(field) {
142
+ return 'localized' in field && field.localized === true;
143
+ }
144
+ /**
145
+ * Gets all localized field paths from a collection config
146
+ */ export function getLocalizedFieldPaths(fields, parentPath = '') {
147
+ const paths = [];
148
+ for (const field of fields){
149
+ if (!('name' in field)) {
150
+ continue;
151
+ }
152
+ const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name;
153
+ if (isLocalizedField(field)) {
154
+ paths.push(fieldPath);
155
+ }
156
+ // Recursively check nested fields
157
+ if ('fields' in field && Array.isArray(field.fields)) {
158
+ paths.push(...getLocalizedFieldPaths(field.fields, fieldPath));
159
+ }
160
+ // Check blocks
161
+ if (field.type === 'blocks' && 'blocks' in field && Array.isArray(field.blocks)) {
162
+ for (const block of field.blocks){
163
+ if ('fields' in block && Array.isArray(block.fields)) {
164
+ paths.push(...getLocalizedFieldPaths(block.fields, fieldPath));
165
+ }
166
+ }
167
+ }
168
+ // Check group fields
169
+ if (field.type === 'group' && 'fields' in field && Array.isArray(field.fields)) {
170
+ paths.push(...getLocalizedFieldPaths(field.fields, fieldPath));
171
+ }
172
+ // Check array fields
173
+ if (field.type === 'array' && 'fields' in field && Array.isArray(field.fields)) {
174
+ paths.push(...getLocalizedFieldPaths(field.fields, fieldPath));
175
+ }
176
+ }
177
+ return paths;
178
+ }
179
+
180
+ //# sourceMappingURL=fieldHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/fieldHelpers.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nimport type { FieldPath } from '../types/index.js'\n\n/**\n * Recursively extracts all field paths and their values from a document\n */\nexport function extractFieldPaths(\n data: any,\n parentPath: string = '',\n fields?: Field[],\n): FieldPath[] {\n const paths: FieldPath[] = []\n\n if (!data || typeof data !== 'object') {\n return paths\n }\n\n for (const [key, value] of Object.entries(data)) {\n const currentPath = parentPath ? `${parentPath}.${key}` : key\n\n // Skip internal fields\n if (\n key === 'id' ||\n key === '_id' ||\n key === 'createdAt' ||\n key === 'updatedAt' ||\n key === 'translationSync' ||\n key === '__v'\n ) {\n continue\n }\n\n // Add current field\n paths.push({\n parentPath,\n path: currentPath,\n value,\n })\n\n // Recursively process nested objects\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n paths.push(...extractFieldPaths(value, currentPath, fields))\n }\n\n // Process arrays\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n const arrayPath = `${currentPath}.${index}`\n if (item && typeof item === 'object') {\n paths.push(...extractFieldPaths(item, arrayPath, fields))\n }\n })\n }\n }\n\n return paths\n}\n\n/**\n * Filters out excluded paths from data before translation\n */\nexport function filterExcludedPaths(data: any, excludedPaths: string[]): any {\n if (!data || typeof data !== 'object' || excludedPaths.length === 0) {\n return data\n }\n\n const filtered = JSON.parse(JSON.stringify(data)) // Deep clone\n\n for (const excludedPath of excludedPaths) {\n deletePath(filtered, excludedPath)\n }\n\n return filtered\n}\n\n/**\n * Merges translated data back, respecting excluded paths\n */\nexport function mergeTranslatedData(\n originalData: any,\n translatedData: any,\n excludedPaths: string[],\n): any {\n if (!translatedData || typeof translatedData !== 'object') {\n return originalData\n }\n\n const merged = JSON.parse(JSON.stringify(originalData)) // Deep clone\n\n function merge(target: any, source: any, currentPath: string = ''): void {\n for (const key in source) {\n const fullPath = currentPath ? `${currentPath}.${key}` : key\n\n // Skip if this path is excluded\n if (isPathExcluded(fullPath, excludedPaths)) {\n continue\n }\n\n // Skip internal fields\n if (\n key === 'id' ||\n key === '_id' ||\n key === 'createdAt' ||\n key === 'updatedAt' ||\n key === 'translationSync' ||\n key === '__v'\n ) {\n continue\n }\n\n if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {\n if (!target[key]) {\n target[key] = {}\n }\n merge(target[key], source[key], fullPath)\n } else {\n target[key] = source[key]\n }\n }\n }\n\n merge(merged, translatedData)\n return merged\n}\n\n/**\n * Checks if a path is excluded or if any parent path is excluded\n */\nexport function isPathExcluded(path: string, excludedPaths: string[]): boolean {\n return excludedPaths.some((excludedPath) => {\n // Exact match\n if (path === excludedPath) {\n return true\n }\n\n // Check if path is a child of excluded path\n if (path.startsWith(`${excludedPath}.`)) {\n return true\n }\n\n // Check if excluded path is a pattern match for arrays (e.g., content.0.title)\n const pathParts = path.split('.')\n const excludedParts = excludedPath.split('.')\n\n for (let i = 0; i < Math.min(pathParts.length, excludedParts.length); i++) {\n if (excludedParts[i] !== pathParts[i]) {\n // Check if it's an array index difference\n if (!isNaN(Number(pathParts[i])) && !isNaN(Number(excludedParts[i]))) {\n continue\n }\n return false\n }\n }\n\n return excludedParts.length <= pathParts.length\n })\n}\n\n/**\n * Deletes a path from an object using dot notation\n */\nfunction deletePath(obj: any, path: string): void {\n const parts = path.split('.')\n let current = obj\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!current[parts[i]]) {\n return\n }\n current = current[parts[i]]\n }\n\n delete current[parts[parts.length - 1]]\n}\n\n/**\n * Gets value at path using dot notation\n */\nexport function getValueAtPath(obj: any, path: string): any {\n return path.split('.').reduce((current, part) => current?.[part], obj)\n}\n\n/**\n * Sets value at path using dot notation\n */\nexport function setValueAtPath(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 if (!current[parts[i]]) {\n current[parts[i]] = {}\n }\n current = current[parts[i]]\n }\n\n current[parts[parts.length - 1]] = value\n}\n\n/**\n * Checks if a field is a localized field\n */\nexport function isLocalizedField(field: Field): boolean {\n return 'localized' in field && field.localized === true\n}\n\n/**\n * Gets all localized field paths from a collection config\n */\nexport function getLocalizedFieldPaths(fields: Field[], parentPath: string = ''): string[] {\n const paths: string[] = []\n\n for (const field of fields) {\n if (!('name' in field)) {\n continue\n }\n\n const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name\n\n if (isLocalizedField(field)) {\n paths.push(fieldPath)\n }\n\n // Recursively check nested fields\n if ('fields' in field && Array.isArray(field.fields)) {\n paths.push(...getLocalizedFieldPaths(field.fields, fieldPath))\n }\n\n // Check blocks\n if (field.type === 'blocks' && 'blocks' in field && Array.isArray(field.blocks)) {\n for (const block of field.blocks) {\n if ('fields' in block && Array.isArray(block.fields)) {\n paths.push(...getLocalizedFieldPaths(block.fields, fieldPath))\n }\n }\n }\n\n // Check group fields\n if (field.type === 'group' && 'fields' in field && Array.isArray(field.fields)) {\n paths.push(...getLocalizedFieldPaths(field.fields, fieldPath))\n }\n\n // Check array fields\n if (field.type === 'array' && 'fields' in field && Array.isArray(field.fields)) {\n paths.push(...getLocalizedFieldPaths(field.fields, fieldPath))\n }\n }\n\n return paths\n}\n"],"names":["extractFieldPaths","data","parentPath","fields","paths","key","value","Object","entries","currentPath","push","path","Array","isArray","forEach","item","index","arrayPath","filterExcludedPaths","excludedPaths","length","filtered","JSON","parse","stringify","excludedPath","deletePath","mergeTranslatedData","originalData","translatedData","merged","merge","target","source","fullPath","isPathExcluded","some","startsWith","pathParts","split","excludedParts","i","Math","min","isNaN","Number","obj","parts","current","getValueAtPath","reduce","part","setValueAtPath","isLocalizedField","field","localized","getLocalizedFieldPaths","fieldPath","name","type","blocks","block"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { Field } from 'payload';
2
+ /**
3
+ * Recursively injects TranslationControl component into all localized fields
4
+ */
5
+ export declare function injectTranslationControls(fields: Field[], defaultLocale: string, parentPath?: string): Field[];
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Recursively injects TranslationControl component into all localized fields
3
+ */ export function injectTranslationControls(fields, defaultLocale, parentPath = '') {
4
+ return fields.map((field)=>{
5
+ // Skip fields without names
6
+ if (!('name' in field)) {
7
+ return field;
8
+ }
9
+ const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name;
10
+ // Clone the field to avoid mutations
11
+ const clonedField = {
12
+ ...field
13
+ };
14
+ // Inject TranslationControl if the field is localized
15
+ // Skip for container fields - they're just UI/structural, not actual data fields
16
+ // Only their nested fields should have translation controls
17
+ const shouldSkipControl = clonedField.type === 'group' || // Groups are containers
18
+ clonedField.type === 'blocks' || // Blocks are containers
19
+ clonedField.type === 'array' || // Arrays are containers
20
+ clonedField.type === 'tabs' // Tabs are UI containers
21
+ ;
22
+ if ('localized' in clonedField && clonedField.localized === true && !shouldSkipControl) {
23
+ // Initialize admin if not present
24
+ if (!clonedField.admin) {
25
+ clonedField.admin = {};
26
+ }
27
+ // Initialize components if not present
28
+ if (!clonedField.admin.components) {
29
+ clonedField.admin.components = {};
30
+ }
31
+ // Initialize afterInput if not present
32
+ if (!clonedField.admin.components.afterInput) {
33
+ clonedField.admin.components.afterInput = [];
34
+ }
35
+ // Ensure afterInput is an array
36
+ if (!Array.isArray(clonedField.admin.components.afterInput)) {
37
+ clonedField.admin.components.afterInput = [
38
+ clonedField.admin.components.afterInput
39
+ ];
40
+ }
41
+ // Check if TranslationControl is already added
42
+ const hasTranslationControl = clonedField.admin.components.afterInput.some((component)=>typeof component === 'object' && component.path === '@pigment/auto-translate/client#TranslationControl');
43
+ // Add TranslationControl if not already present
44
+ if (!hasTranslationControl) {
45
+ clonedField.admin.components.afterInput.push({
46
+ clientProps: {
47
+ defaultLocale,
48
+ fieldPath
49
+ },
50
+ path: '@pigment/auto-translate/client#TranslationControl'
51
+ });
52
+ }
53
+ }
54
+ // Recursively inject into nested fields
55
+ if ('fields' in clonedField && Array.isArray(clonedField.fields)) {
56
+ clonedField.fields = injectTranslationControls(clonedField.fields, defaultLocale, fieldPath);
57
+ }
58
+ // Recursively inject into tabs
59
+ // Note: Tabs fields themselves don't create a path segment
60
+ // Named tabs (with 'name' property) create their own path segment
61
+ // Unnamed tabs use the parent path
62
+ if ('tabs' in clonedField && Array.isArray(clonedField.tabs)) {
63
+ clonedField.tabs = clonedField.tabs.map((tab)=>{
64
+ if (tab.fields) {
65
+ // If the tab has a name, use it as the path segment
66
+ // Otherwise, use the parent path (tabs field itself doesn't create a path)
67
+ const tabPath = tab.name ? parentPath ? `${parentPath}.${tab.name}` : tab.name : parentPath;
68
+ return {
69
+ ...tab,
70
+ fields: injectTranslationControls(tab.fields, defaultLocale, tabPath)
71
+ };
72
+ }
73
+ return tab;
74
+ });
75
+ }
76
+ // Recursively inject into blocks
77
+ if (clonedField.type === 'blocks' && 'blocks' in clonedField) {
78
+ clonedField.blocks = clonedField.blocks.map((block)=>{
79
+ if (block.fields) {
80
+ return {
81
+ ...block,
82
+ fields: injectTranslationControls(block.fields, defaultLocale, fieldPath)
83
+ };
84
+ }
85
+ return block;
86
+ });
87
+ }
88
+ return clonedField;
89
+ });
90
+ }
91
+
92
+ //# sourceMappingURL=injectTranslationControls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/injectTranslationControls.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * Recursively injects TranslationControl component into all localized fields\n */\nexport function injectTranslationControls(\n fields: Field[],\n defaultLocale: string,\n parentPath: string = '',\n): Field[] {\n return fields.map((field) => {\n // Skip fields without names\n if (!('name' in field)) {\n return field\n }\n\n const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name\n\n // Clone the field to avoid mutations\n const clonedField: any = { ...field }\n\n // Inject TranslationControl if the field is localized\n // Skip for container fields - they're just UI/structural, not actual data fields\n // Only their nested fields should have translation controls\n const shouldSkipControl =\n clonedField.type === 'group' || // Groups are containers\n clonedField.type === 'blocks' || // Blocks are containers\n clonedField.type === 'array' || // Arrays are containers\n clonedField.type === 'tabs' // Tabs are UI containers\n\n if ('localized' in clonedField && clonedField.localized === true && !shouldSkipControl) {\n // Initialize admin if not present\n if (!clonedField.admin) {\n clonedField.admin = {}\n }\n\n // Initialize components if not present\n if (!clonedField.admin.components) {\n clonedField.admin.components = {}\n }\n\n // Initialize afterInput if not present\n if (!clonedField.admin.components.afterInput) {\n clonedField.admin.components.afterInput = []\n }\n\n // Ensure afterInput is an array\n if (!Array.isArray(clonedField.admin.components.afterInput)) {\n clonedField.admin.components.afterInput = [clonedField.admin.components.afterInput]\n }\n\n // Check if TranslationControl is already added\n const hasTranslationControl = clonedField.admin.components.afterInput.some(\n (component: any) =>\n typeof component === 'object' &&\n component.path === '@pigment/auto-translate/client#TranslationControl',\n )\n\n // Add TranslationControl if not already present\n if (!hasTranslationControl) {\n clonedField.admin.components.afterInput.push({\n clientProps: {\n defaultLocale, // Pass default locale to component\n fieldPath,\n },\n path: '@pigment/auto-translate/client#TranslationControl',\n })\n }\n }\n\n // Recursively inject into nested fields\n if ('fields' in clonedField && Array.isArray(clonedField.fields)) {\n clonedField.fields = injectTranslationControls(clonedField.fields, defaultLocale, fieldPath)\n }\n\n // Recursively inject into tabs\n // Note: Tabs fields themselves don't create a path segment\n // Named tabs (with 'name' property) create their own path segment\n // Unnamed tabs use the parent path\n if ('tabs' in clonedField && Array.isArray(clonedField.tabs)) {\n clonedField.tabs = clonedField.tabs.map((tab: any) => {\n if (tab.fields) {\n // If the tab has a name, use it as the path segment\n // Otherwise, use the parent path (tabs field itself doesn't create a path)\n const tabPath = tab.name \n ? (parentPath ? `${parentPath}.${tab.name}` : tab.name)\n : parentPath\n \n return {\n ...tab,\n fields: injectTranslationControls(tab.fields, defaultLocale, tabPath),\n }\n }\n return tab\n })\n }\n\n // Recursively inject into blocks\n if (clonedField.type === 'blocks' && 'blocks' in clonedField) {\n clonedField.blocks = clonedField.blocks.map((block: any) => {\n if (block.fields) {\n return {\n ...block,\n fields: injectTranslationControls(block.fields, defaultLocale, fieldPath),\n }\n }\n return block\n })\n }\n\n return clonedField as Field\n })\n}\n"],"names":["injectTranslationControls","fields","defaultLocale","parentPath","map","field","fieldPath","name","clonedField","shouldSkipControl","type","localized","admin","components","afterInput","Array","isArray","hasTranslationControl","some","component","path","push","clientProps","tabs","tab","tabPath","blocks","block"],"mappings":"AAEA;;CAEC,GACD,OAAO,SAASA,0BACdC,MAAe,EACfC,aAAqB,EACrBC,aAAqB,EAAE;IAEvB,OAAOF,OAAOG,GAAG,CAAC,CAACC;QACjB,4BAA4B;QAC5B,IAAI,CAAE,CAAA,UAAUA,KAAI,GAAI;YACtB,OAAOA;QACT;QAEA,MAAMC,YAAYH,aAAa,GAAGA,WAAW,CAAC,EAAEE,MAAME,IAAI,EAAE,GAAGF,MAAME,IAAI;QAEzE,qCAAqC;QACrC,MAAMC,cAAmB;YAAE,GAAGH,KAAK;QAAC;QAEpC,sDAAsD;QACtD,iFAAiF;QACjF,4DAA4D;QAC5D,MAAMI,oBACJD,YAAYE,IAAI,KAAK,WAAa,wBAAwB;QAC1DF,YAAYE,IAAI,KAAK,YAAa,wBAAwB;QAC1DF,YAAYE,IAAI,KAAK,WAAa,wBAAwB;QAC1DF,YAAYE,IAAI,KAAK,OAAa,yBAAyB;;QAE7D,IAAI,eAAeF,eAAeA,YAAYG,SAAS,KAAK,QAAQ,CAACF,mBAAmB;YACtF,kCAAkC;YAClC,IAAI,CAACD,YAAYI,KAAK,EAAE;gBACtBJ,YAAYI,KAAK,GAAG,CAAC;YACvB;YAEA,uCAAuC;YACvC,IAAI,CAACJ,YAAYI,KAAK,CAACC,UAAU,EAAE;gBACjCL,YAAYI,KAAK,CAACC,UAAU,GAAG,CAAC;YAClC;YAEA,uCAAuC;YACvC,IAAI,CAACL,YAAYI,KAAK,CAACC,UAAU,CAACC,UAAU,EAAE;gBAC5CN,YAAYI,KAAK,CAACC,UAAU,CAACC,UAAU,GAAG,EAAE;YAC9C;YAEA,gCAAgC;YAChC,IAAI,CAACC,MAAMC,OAAO,CAACR,YAAYI,KAAK,CAACC,UAAU,CAACC,UAAU,GAAG;gBAC3DN,YAAYI,KAAK,CAACC,UAAU,CAACC,UAAU,GAAG;oBAACN,YAAYI,KAAK,CAACC,UAAU,CAACC,UAAU;iBAAC;YACrF;YAEA,+CAA+C;YAC/C,MAAMG,wBAAwBT,YAAYI,KAAK,CAACC,UAAU,CAACC,UAAU,CAACI,IAAI,CACxE,CAACC,YACC,OAAOA,cAAc,YACrBA,UAAUC,IAAI,KAAK;YAGvB,gDAAgD;YAChD,IAAI,CAACH,uBAAuB;gBAC1BT,YAAYI,KAAK,CAACC,UAAU,CAACC,UAAU,CAACO,IAAI,CAAC;oBAC3CC,aAAa;wBACXpB;wBACAI;oBACF;oBACAc,MAAM;gBACR;YACF;QACF;QAEA,wCAAwC;QACxC,IAAI,YAAYZ,eAAeO,MAAMC,OAAO,CAACR,YAAYP,MAAM,GAAG;YAChEO,YAAYP,MAAM,GAAGD,0BAA0BQ,YAAYP,MAAM,EAAEC,eAAeI;QACpF;QAEA,+BAA+B;QAC/B,2DAA2D;QAC3D,kEAAkE;QAClE,mCAAmC;QACnC,IAAI,UAAUE,eAAeO,MAAMC,OAAO,CAACR,YAAYe,IAAI,GAAG;YAC5Df,YAAYe,IAAI,GAAGf,YAAYe,IAAI,CAACnB,GAAG,CAAC,CAACoB;gBACvC,IAAIA,IAAIvB,MAAM,EAAE;oBACd,oDAAoD;oBACpD,2EAA2E;oBAC3E,MAAMwB,UAAUD,IAAIjB,IAAI,GACnBJ,aAAa,GAAGA,WAAW,CAAC,EAAEqB,IAAIjB,IAAI,EAAE,GAAGiB,IAAIjB,IAAI,GACpDJ;oBAEJ,OAAO;wBACL,GAAGqB,GAAG;wBACNvB,QAAQD,0BAA0BwB,IAAIvB,MAAM,EAAEC,eAAeuB;oBAC/D;gBACF;gBACA,OAAOD;YACT;QACF;QAEA,iCAAiC;QACjC,IAAIhB,YAAYE,IAAI,KAAK,YAAY,YAAYF,aAAa;YAC5DA,YAAYkB,MAAM,GAAGlB,YAAYkB,MAAM,CAACtB,GAAG,CAAC,CAACuB;gBAC3C,IAAIA,MAAM1B,MAAM,EAAE;oBAChB,OAAO;wBACL,GAAG0B,KAAK;wBACR1B,QAAQD,0BAA0B2B,MAAM1B,MAAM,EAAEC,eAAeI;oBACjE;gBACF;gBACA,OAAOqB;YACT;QACF;QAEA,OAAOnB;IACT;AACF"}
package/package.json ADDED
@@ -0,0 +1,114 @@
1
+ {
2
+ "name": "@pigment/auto-translate",
3
+ "version": "1.2.0",
4
+ "description": "Automatic translation plugin for Payload CMS with field-level exclusion controls and performance optimizations",
5
+ "keywords": [
6
+ "payload",
7
+ "payload-plugin",
8
+ "payloadcms",
9
+ "translation",
10
+ "auto-translate",
11
+ "i18n",
12
+ "localization",
13
+ "openai",
14
+ "gpt"
15
+ ],
16
+ "author": {
17
+ "name": "Team Pigment",
18
+ "email": "dev@pigment.se",
19
+ "url": "https://pigment.se"
20
+ },
21
+ "homepage": "https://pigment.se",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/pigment-se/auto-translate"
25
+ },
26
+ "license": "MIT",
27
+ "type": "module",
28
+ "exports": {
29
+ ".": {
30
+ "import": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.js"
33
+ },
34
+ "./client": {
35
+ "import": "./dist/exports/client.js",
36
+ "types": "./dist/exports/client.d.ts",
37
+ "default": "./dist/exports/client.js"
38
+ },
39
+ "./rsc": {
40
+ "import": "./dist/exports/rsc.js",
41
+ "types": "./dist/exports/rsc.d.ts",
42
+ "default": "./dist/exports/rsc.js"
43
+ }
44
+ },
45
+ "main": "./dist/index.js",
46
+ "types": "./dist/index.d.ts",
47
+ "files": [
48
+ "dist"
49
+ ],
50
+ "devDependencies": {
51
+ "@eslint/eslintrc": "^3.2.0",
52
+ "@payloadcms/db-mongodb": "3.37.0",
53
+ "@payloadcms/db-postgres": "3.37.0",
54
+ "@payloadcms/db-sqlite": "3.37.0",
55
+ "@payloadcms/eslint-config": "3.9.0",
56
+ "@payloadcms/next": "3.37.0",
57
+ "@payloadcms/richtext-lexical": "3.37.0",
58
+ "@payloadcms/ui": "3.37.0",
59
+ "@playwright/test": "^1.52.0",
60
+ "@swc-node/register": "1.10.9",
61
+ "@swc/cli": "0.6.0",
62
+ "@types/node": "^22.5.4",
63
+ "@types/react": "19.1.8",
64
+ "@types/react-dom": "19.1.6",
65
+ "copyfiles": "2.4.1",
66
+ "cross-env": "^7.0.3",
67
+ "eslint": "^9.23.0",
68
+ "eslint-config-next": "15.4.4",
69
+ "graphql": "^16.8.1",
70
+ "mongodb-memory-server": "10.1.4",
71
+ "next": "15.4.4",
72
+ "open": "^10.1.0",
73
+ "payload": "3.37.0",
74
+ "prettier": "^3.4.2",
75
+ "qs-esm": "7.0.2",
76
+ "react": "19.1.0",
77
+ "react-dom": "19.1.0",
78
+ "rimraf": "3.0.2",
79
+ "sharp": "0.34.2",
80
+ "sort-package-json": "^2.10.0",
81
+ "typescript": "5.7.3",
82
+ "vite-tsconfig-paths": "^5.1.4",
83
+ "vitest": "^3.1.2"
84
+ },
85
+ "peerDependencies": {
86
+ "payload": "^3.37.0"
87
+ },
88
+ "engines": {
89
+ "node": "^18.20.2 || >=20.9.0",
90
+ "pnpm": "^9 || ^10"
91
+ },
92
+ "registry": "https://registry.npmjs.org/",
93
+ "dependencies": {
94
+ "openai": "^6.8.0"
95
+ },
96
+ "scripts": {
97
+ "build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
98
+ "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
99
+ "build:types": "tsc --outDir dist --rootDir ./src",
100
+ "clean": "rimraf {dist,*.tsbuildinfo}",
101
+ "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
102
+ "dev": "next dev dev --turbo",
103
+ "dev:generate-importmap": "pnpm dev:payload generate:importmap",
104
+ "dev:generate-types": "pnpm dev:payload generate:types",
105
+ "dev:payload": "cross-env PAYLOAD_CONFIG_PATH=./dev/payload.config.ts payload",
106
+ "generate:importmap": "pnpm dev:generate-importmap",
107
+ "generate:types": "pnpm dev:generate-types",
108
+ "lint": "eslint",
109
+ "lint:fix": "eslint ./src --fix",
110
+ "test": "pnpm test:int && pnpm test:e2e",
111
+ "test:e2e": "playwright test",
112
+ "test:int": "vitest"
113
+ }
114
+ }