@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.
- package/LICENSE +22 -0
- package/README.md +405 -0
- package/dist/collections/translationExclusions.d.ts +2 -0
- package/dist/collections/translationExclusions.js +78 -0
- package/dist/collections/translationExclusions.js.map +1 -0
- package/dist/components/TranslationControl.css +92 -0
- package/dist/components/TranslationControl.d.ts +18 -0
- package/dist/components/TranslationControl.js +274 -0
- package/dist/components/TranslationControl.js.map +1 -0
- package/dist/exports/client.d.ts +5 -0
- package/dist/exports/client.js +5 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +5 -0
- package/dist/exports/rsc.js +5 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/globals/translationSettings.d.ts +2 -0
- package/dist/globals/translationSettings.js +78 -0
- package/dist/globals/translationSettings.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +254 -0
- package/dist/index.js.map +1 -0
- package/dist/services/translationService.d.ts +60 -0
- package/dist/services/translationService.js +533 -0
- package/dist/services/translationService.js.map +1 -0
- package/dist/types/index.d.ts +103 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utilities/fieldHelpers.d.ts +34 -0
- package/dist/utilities/fieldHelpers.js +180 -0
- package/dist/utilities/fieldHelpers.js.map +1 -0
- package/dist/utilities/injectTranslationControls.d.ts +5 -0
- package/dist/utilities/injectTranslationControls.js +92 -0
- package/dist/utilities/injectTranslationControls.js.map +1 -0
- 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 @@
|
|
|
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,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":"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"}
|
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
|
+
}
|