@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,274 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useDocumentInfo, useLocale } from '@payloadcms/ui';
4
+ import React, { useCallback, useEffect, useState } from 'react';
5
+ import './TranslationControl.css';
6
+ /**
7
+ * UI component that allows users to toggle "do not translate" for specific fields
8
+ * Only shows on secondary locales (not the default locale)
9
+ *
10
+ * The component can receive the field path in two ways:
11
+ * 1. From Payload's `path` prop (preferred - includes runtime array/block indices)
12
+ * 2. From the `fieldPath` clientProp (fallback - static path from field definition)
13
+ */ export const TranslationControl = ({ collectionSlug, defaultLocale, fieldPath: clientFieldPath, path: payloadPath })=>{
14
+ // Use Payload's runtime path if available (includes array/block indices like "layout.0.heading")
15
+ // Otherwise fall back to the static path from clientProps
16
+ const fieldPath = payloadPath || clientFieldPath;
17
+ const { id, collectionSlug: docCollectionSlug } = useDocumentInfo();
18
+ const { code: currentLocale } = useLocale();
19
+ const [isExcluded, setIsExcluded] = useState(false);
20
+ const [isLoading, setIsLoading] = useState(false);
21
+ // Use collectionSlug from props or from document context
22
+ const effectiveCollectionSlug = collectionSlug || docCollectionSlug;
23
+ // Don't show on default locale - you can only lock fields in secondary locales
24
+ if (currentLocale === defaultLocale) {
25
+ return null;
26
+ }
27
+ // Don't show if we don't have a valid field path
28
+ if (!fieldPath) {
29
+ console.warn('[TranslationControl] No field path available');
30
+ return null;
31
+ }
32
+ // Load exclusion state on mount and when locale changes
33
+ useEffect(()=>{
34
+ // Reset state when switching documents or when there's no ID (new document)
35
+ if (!id || !effectiveCollectionSlug) {
36
+ setIsExcluded(false); // Reset to default state
37
+ return;
38
+ }
39
+ const loadExclusionState = async ()=>{
40
+ try {
41
+ console.log('[TranslationControl] Loading exclusion state for:', {
42
+ collection: effectiveCollectionSlug,
43
+ documentId: id,
44
+ fieldPath,
45
+ locale: currentLocale
46
+ });
47
+ // Build query for this specific locale AND document ID
48
+ const whereQuery = {
49
+ and: [
50
+ {
51
+ collection: {
52
+ equals: effectiveCollectionSlug
53
+ }
54
+ },
55
+ {
56
+ documentId: {
57
+ equals: id
58
+ }
59
+ },
60
+ {
61
+ locale: {
62
+ equals: currentLocale
63
+ }
64
+ }
65
+ ]
66
+ };
67
+ const queryString = new URLSearchParams({
68
+ limit: '1',
69
+ where: JSON.stringify(whereQuery)
70
+ }).toString();
71
+ const fullUrl = `/api/translation-exclusions?${queryString}`;
72
+ console.log('[TranslationControl] Query URL:', fullUrl);
73
+ console.log('[TranslationControl] Where clause:', whereQuery);
74
+ const response = await fetch(fullUrl);
75
+ if (response.ok) {
76
+ const data = await response.json();
77
+ if (data.docs && data.docs.length > 0) {
78
+ const exclusion = data.docs[0];
79
+ // CRITICAL: Verify this exclusion belongs to THIS document AND locale
80
+ if (exclusion.locale === currentLocale && exclusion.documentId === id) {
81
+ const excludedPaths = exclusion.excludedPaths?.map((item)=>item.path) || [];
82
+ const isFieldExcluded = excludedPaths.includes(fieldPath);
83
+ console.log('[TranslationControl] Loaded exclusions for document', id, 'locale', currentLocale, ':', {
84
+ excludedPaths,
85
+ fieldPath,
86
+ isFieldExcluded
87
+ });
88
+ setIsExcluded(isFieldExcluded);
89
+ } else {
90
+ console.warn('[TranslationControl] Document/Locale mismatch in loaded exclusion!', {
91
+ expectedLocale: currentLocale,
92
+ expectedDocId: id,
93
+ gotLocale: exclusion.locale,
94
+ gotDocId: exclusion.documentId
95
+ });
96
+ // This exclusion is for a different document - ignore it
97
+ setIsExcluded(false);
98
+ }
99
+ } else {
100
+ // No exclusions found for this document/locale - that's fine
101
+ console.log('[TranslationControl] No exclusions found for document', id, 'locale', currentLocale);
102
+ setIsExcluded(false);
103
+ }
104
+ }
105
+ } catch (error) {
106
+ console.error('[TranslationControl] Failed to load exclusion state:', error);
107
+ }
108
+ };
109
+ loadExclusionState();
110
+ }, [
111
+ id,
112
+ effectiveCollectionSlug,
113
+ currentLocale,
114
+ fieldPath
115
+ ]);
116
+ const toggleExclusion = useCallback(async ()=>{
117
+ if (!id || !effectiveCollectionSlug) {
118
+ return;
119
+ }
120
+ setIsLoading(true);
121
+ try {
122
+ // Build query parameters for Payload REST API
123
+ const whereQuery = {
124
+ and: [
125
+ {
126
+ collection: {
127
+ equals: effectiveCollectionSlug
128
+ }
129
+ },
130
+ {
131
+ documentId: {
132
+ equals: id
133
+ }
134
+ },
135
+ {
136
+ locale: {
137
+ equals: currentLocale
138
+ }
139
+ }
140
+ ]
141
+ };
142
+ // Debug: Log the query we're making
143
+ console.log('[TranslationControl] Fetching exclusions for:', {
144
+ collection: effectiveCollectionSlug,
145
+ documentId: id,
146
+ fieldPath,
147
+ locale: currentLocale
148
+ });
149
+ // Properly format the where clause for Payload's REST API
150
+ const queryString = new URLSearchParams({
151
+ limit: '1',
152
+ where: JSON.stringify(whereQuery)
153
+ }).toString();
154
+ const fullUrl = `/api/translation-exclusions?${queryString}`;
155
+ console.log('[TranslationControl] Toggle - Query URL:', fullUrl);
156
+ console.log('[TranslationControl] Toggle - Where clause:', whereQuery);
157
+ const findResponse = await fetch(fullUrl);
158
+ let currentExcludedPaths = [];
159
+ let existingId = null;
160
+ if (findResponse.ok) {
161
+ const data = await findResponse.json();
162
+ console.log('[TranslationControl] Found exclusions:', data.docs);
163
+ if (data.docs && data.docs.length > 0) {
164
+ const exclusion = data.docs[0];
165
+ // CRITICAL: Verify this exclusion belongs to THIS document AND locale
166
+ if (exclusion.locale === currentLocale && exclusion.documentId === id) {
167
+ existingId = exclusion.id;
168
+ currentExcludedPaths = exclusion.excludedPaths?.map((item)=>item.path) || [];
169
+ console.log('[TranslationControl] Current excluded paths for document', id, 'locale', currentLocale, ':', currentExcludedPaths);
170
+ } else {
171
+ console.warn('[TranslationControl] Found exclusion for wrong document/locale!', {
172
+ expectedLocale: currentLocale,
173
+ expectedDocId: id,
174
+ gotLocale: exclusion.locale,
175
+ gotDocId: exclusion.documentId
176
+ });
177
+ // Don't use this record - it's for a different document
178
+ existingId = null;
179
+ currentExcludedPaths = [];
180
+ }
181
+ }
182
+ }
183
+ // Update excluded paths for THIS locale only
184
+ if (!isExcluded) {
185
+ // Add path if not already excluded
186
+ if (!currentExcludedPaths.includes(fieldPath)) {
187
+ currentExcludedPaths.push(fieldPath);
188
+ }
189
+ } else {
190
+ // Remove path from exclusions
191
+ currentExcludedPaths = currentExcludedPaths.filter((path)=>path !== fieldPath);
192
+ }
193
+ // Create the exclusion data - ALWAYS include the current locale
194
+ const exclusionsData = {
195
+ collection: effectiveCollectionSlug,
196
+ documentId: id,
197
+ excludedPaths: currentExcludedPaths.map((path)=>({
198
+ path
199
+ })),
200
+ locale: currentLocale
201
+ };
202
+ console.log('[TranslationControl] Saving exclusions:', exclusionsData);
203
+ // Update or create record using Payload's REST API
204
+ if (existingId) {
205
+ const updateResponse = await fetch(`/api/translation-exclusions/${existingId}`, {
206
+ body: JSON.stringify(exclusionsData),
207
+ headers: {
208
+ 'Content-Type': 'application/json'
209
+ },
210
+ method: 'PATCH'
211
+ });
212
+ if (updateResponse.ok) {
213
+ const result = await updateResponse.json();
214
+ console.log('[TranslationControl] Updated exclusions:', result.doc);
215
+ }
216
+ } else {
217
+ const createResponse = await fetch('/api/translation-exclusions', {
218
+ body: JSON.stringify(exclusionsData),
219
+ headers: {
220
+ 'Content-Type': 'application/json'
221
+ },
222
+ method: 'POST'
223
+ });
224
+ if (createResponse.ok) {
225
+ const result = await createResponse.json();
226
+ console.log('[TranslationControl] Created exclusions:', result.doc);
227
+ }
228
+ }
229
+ setIsExcluded(!isExcluded);
230
+ } catch (error) {
231
+ console.error('[TranslationControl] Error toggling exclusion:', error);
232
+ } finally{
233
+ setIsLoading(false);
234
+ }
235
+ }, [
236
+ id,
237
+ effectiveCollectionSlug,
238
+ currentLocale,
239
+ fieldPath,
240
+ isExcluded
241
+ ]);
242
+ // Don't show on create (no id yet)
243
+ if (!id) {
244
+ return null;
245
+ }
246
+ return /*#__PURE__*/ _jsxs("div", {
247
+ className: `translation-control ${isExcluded ? 'is-excluded' : ''}`,
248
+ children: [
249
+ /*#__PURE__*/ _jsxs("button", {
250
+ className: "translation-control__button",
251
+ disabled: isLoading,
252
+ onClick: toggleExclusion,
253
+ title: isExcluded ? 'This field is locked and will not be auto-translated from the default language' : 'Click to lock this field from auto-translation',
254
+ type: "button",
255
+ children: [
256
+ /*#__PURE__*/ _jsx("span", {
257
+ className: "translation-control__icon",
258
+ children: isExcluded ? '🔒' : '🌐'
259
+ }),
260
+ /*#__PURE__*/ _jsx("span", {
261
+ className: "translation-control__label",
262
+ children: isExcluded ? 'Locked' : 'Auto-translate'
263
+ })
264
+ ]
265
+ }),
266
+ isExcluded && /*#__PURE__*/ _jsx("span", {
267
+ className: "translation-control__status",
268
+ children: "This field will not be overwritten when the default language version is updated."
269
+ })
270
+ ]
271
+ });
272
+ };
273
+
274
+ //# sourceMappingURL=TranslationControl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/TranslationControl.tsx"],"sourcesContent":["'use client'\n\nimport { useDocumentInfo, useLocale } from '@payloadcms/ui'\nimport React, { useCallback, useEffect, useState } from 'react'\n\nimport './TranslationControl.css'\n\ntype TranslationControlProps = {\n collectionSlug?: string\n defaultLocale: string\n fieldPath?: string // Optional - will be inferred from Payload's path prop if not provided\n path?: string // Payload provides this at runtime with array/block indices\n}\n\n/**\n * UI component that allows users to toggle \"do not translate\" for specific fields\n * Only shows on secondary locales (not the default locale)\n * \n * The component can receive the field path in two ways:\n * 1. From Payload's `path` prop (preferred - includes runtime array/block indices)\n * 2. From the `fieldPath` clientProp (fallback - static path from field definition)\n */\nexport const TranslationControl: React.FC<TranslationControlProps> = ({\n collectionSlug,\n defaultLocale,\n fieldPath: clientFieldPath,\n path: payloadPath,\n}) => {\n // Use Payload's runtime path if available (includes array/block indices like \"layout.0.heading\")\n // Otherwise fall back to the static path from clientProps\n const fieldPath = payloadPath || clientFieldPath\n const { id, collectionSlug: docCollectionSlug } = useDocumentInfo()\n const { code: currentLocale } = useLocale()\n const [isExcluded, setIsExcluded] = useState(false)\n const [isLoading, setIsLoading] = useState(false)\n\n // Use collectionSlug from props or from document context\n const effectiveCollectionSlug = collectionSlug || docCollectionSlug\n\n // Don't show on default locale - you can only lock fields in secondary locales\n if (currentLocale === defaultLocale) {\n return null\n }\n\n // Don't show if we don't have a valid field path\n if (!fieldPath) {\n console.warn('[TranslationControl] No field path available')\n return null\n }\n\n // Load exclusion state on mount and when locale changes\n useEffect(() => {\n // Reset state when switching documents or when there's no ID (new document)\n if (!id || !effectiveCollectionSlug) {\n setIsExcluded(false) // Reset to default state\n return\n }\n\n const loadExclusionState = async () => {\n try {\n console.log('[TranslationControl] Loading exclusion state for:', {\n collection: effectiveCollectionSlug,\n documentId: id,\n fieldPath,\n locale: currentLocale,\n })\n\n // Build query for this specific locale AND document ID\n const whereQuery = {\n and: [\n { collection: { equals: effectiveCollectionSlug } },\n { documentId: { equals: id } }, // This ensures we only get exclusions for THIS document\n { locale: { equals: currentLocale } },\n ],\n }\n\n const queryString = new URLSearchParams({\n limit: '1',\n where: JSON.stringify(whereQuery),\n }).toString()\n\n const fullUrl = `/api/translation-exclusions?${queryString}`\n console.log('[TranslationControl] Query URL:', fullUrl)\n console.log('[TranslationControl] Where clause:', whereQuery)\n\n const response = await fetch(fullUrl)\n\n if (response.ok) {\n const data = await response.json()\n\n if (data.docs && data.docs.length > 0) {\n const exclusion = data.docs[0]\n\n // CRITICAL: Verify this exclusion belongs to THIS document AND locale\n if (exclusion.locale === currentLocale && exclusion.documentId === id) {\n const excludedPaths = exclusion.excludedPaths?.map((item: any) => item.path) || []\n const isFieldExcluded = excludedPaths.includes(fieldPath)\n\n console.log('[TranslationControl] Loaded exclusions for document', id, 'locale', currentLocale, ':', {\n excludedPaths,\n fieldPath,\n isFieldExcluded,\n })\n\n setIsExcluded(isFieldExcluded)\n } else {\n console.warn('[TranslationControl] Document/Locale mismatch in loaded exclusion!', {\n expectedLocale: currentLocale,\n expectedDocId: id,\n gotLocale: exclusion.locale,\n gotDocId: exclusion.documentId,\n })\n // This exclusion is for a different document - ignore it\n setIsExcluded(false)\n }\n } else {\n // No exclusions found for this document/locale - that's fine\n console.log('[TranslationControl] No exclusions found for document', id, 'locale', currentLocale)\n setIsExcluded(false)\n }\n }\n } catch (error) {\n console.error('[TranslationControl] Failed to load exclusion state:', error)\n }\n }\n\n loadExclusionState()\n }, [id, effectiveCollectionSlug, currentLocale, fieldPath])\n\n const toggleExclusion = useCallback(async () => {\n if (!id || !effectiveCollectionSlug) {\n return\n }\n\n setIsLoading(true)\n try {\n // Build query parameters for Payload REST API\n const whereQuery = {\n and: [\n { collection: { equals: effectiveCollectionSlug } },\n { documentId: { equals: id } },\n { locale: { equals: currentLocale } },\n ],\n }\n\n // Debug: Log the query we're making\n console.log('[TranslationControl] Fetching exclusions for:', {\n collection: effectiveCollectionSlug,\n documentId: id,\n fieldPath,\n locale: currentLocale,\n })\n\n // Properly format the where clause for Payload's REST API\n const queryString = new URLSearchParams({\n limit: '1',\n where: JSON.stringify(whereQuery),\n }).toString()\n\n const fullUrl = `/api/translation-exclusions?${queryString}`\n console.log('[TranslationControl] Toggle - Query URL:', fullUrl)\n console.log('[TranslationControl] Toggle - Where clause:', whereQuery)\n\n const findResponse = await fetch(fullUrl)\n\n let currentExcludedPaths: string[] = []\n let existingId: null | string = null\n\n if (findResponse.ok) {\n const data = await findResponse.json()\n console.log('[TranslationControl] Found exclusions:', data.docs)\n\n if (data.docs && data.docs.length > 0) {\n const exclusion = data.docs[0]\n\n // CRITICAL: Verify this exclusion belongs to THIS document AND locale\n if (exclusion.locale === currentLocale && exclusion.documentId === id) {\n existingId = exclusion.id\n currentExcludedPaths = exclusion.excludedPaths?.map((item: any) => item.path) || []\n console.log(\n '[TranslationControl] Current excluded paths for document',\n id,\n 'locale',\n currentLocale,\n ':',\n currentExcludedPaths,\n )\n } else {\n console.warn('[TranslationControl] Found exclusion for wrong document/locale!', {\n expectedLocale: currentLocale,\n expectedDocId: id,\n gotLocale: exclusion.locale,\n gotDocId: exclusion.documentId,\n })\n // Don't use this record - it's for a different document\n existingId = null\n currentExcludedPaths = []\n }\n }\n }\n\n // Update excluded paths for THIS locale only\n if (!isExcluded) {\n // Add path if not already excluded\n if (!currentExcludedPaths.includes(fieldPath)) {\n currentExcludedPaths.push(fieldPath)\n }\n } else {\n // Remove path from exclusions\n currentExcludedPaths = currentExcludedPaths.filter((path) => path !== fieldPath)\n }\n\n // Create the exclusion data - ALWAYS include the current locale\n const exclusionsData = {\n collection: effectiveCollectionSlug,\n documentId: id,\n excludedPaths: currentExcludedPaths.map((path) => ({ path })),\n locale: currentLocale, // Ensure this is the CURRENT locale\n }\n\n console.log('[TranslationControl] Saving exclusions:', exclusionsData)\n\n // Update or create record using Payload's REST API\n if (existingId) {\n const updateResponse = await fetch(`/api/translation-exclusions/${existingId}`, {\n body: JSON.stringify(exclusionsData),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'PATCH',\n })\n\n if (updateResponse.ok) {\n const result = await updateResponse.json()\n console.log('[TranslationControl] Updated exclusions:', result.doc)\n }\n } else {\n const createResponse = await fetch('/api/translation-exclusions', {\n body: JSON.stringify(exclusionsData),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n if (createResponse.ok) {\n const result = await createResponse.json()\n console.log('[TranslationControl] Created exclusions:', result.doc)\n }\n }\n\n setIsExcluded(!isExcluded)\n } catch (error) {\n console.error('[TranslationControl] Error toggling exclusion:', error)\n } finally {\n setIsLoading(false)\n }\n }, [id, effectiveCollectionSlug, currentLocale, fieldPath, isExcluded])\n\n // Don't show on create (no id yet)\n if (!id) {\n return null\n }\n\n return (\n <div className={`translation-control ${isExcluded ? 'is-excluded' : ''}`}>\n <button\n className=\"translation-control__button\"\n disabled={isLoading}\n onClick={toggleExclusion}\n title={\n isExcluded\n ? 'This field is locked and will not be auto-translated from the default language'\n : 'Click to lock this field from auto-translation'\n }\n type=\"button\"\n >\n <span className=\"translation-control__icon\">{isExcluded ? '🔒' : '🌐'}</span>\n <span className=\"translation-control__label\">\n {isExcluded ? 'Locked' : 'Auto-translate'}\n </span>\n </button>\n {isExcluded && (\n <span className=\"translation-control__status\">\n This field will not be overwritten when the default language version is updated.\n </span>\n )}\n </div>\n )\n}\n"],"names":["useDocumentInfo","useLocale","React","useCallback","useEffect","useState","TranslationControl","collectionSlug","defaultLocale","fieldPath","clientFieldPath","path","payloadPath","id","docCollectionSlug","code","currentLocale","isExcluded","setIsExcluded","isLoading","setIsLoading","effectiveCollectionSlug","console","warn","loadExclusionState","log","collection","documentId","locale","whereQuery","and","equals","queryString","URLSearchParams","limit","where","JSON","stringify","toString","fullUrl","response","fetch","ok","data","json","docs","length","exclusion","excludedPaths","map","item","isFieldExcluded","includes","expectedLocale","expectedDocId","gotLocale","gotDocId","error","toggleExclusion","findResponse","currentExcludedPaths","existingId","push","filter","exclusionsData","updateResponse","body","headers","method","result","doc","createResponse","div","className","button","disabled","onClick","title","type","span"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Client-side exports for the auto-translate plugin
3
+ */
4
+ export { TranslationControl } from '../components/TranslationControl.js';
5
+ export type * from '../types/index.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Client-side exports for the auto-translate plugin
3
+ */ export { TranslationControl } from '../components/TranslationControl.js';
4
+
5
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["/**\n * Client-side exports for the auto-translate plugin\n */\n\nexport { TranslationControl } from '../components/TranslationControl.js'\nexport type * from '../types/index.js'\n"],"names":["TranslationControl"],"mappings":"AAAA;;CAEC,GAED,SAASA,kBAAkB,QAAQ,sCAAqC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * React Server Component exports for the auto-translate plugin
3
+ */
4
+ export { getTranslationSettingsGlobal } from '../globals/translationSettings.js';
5
+ export type * from '../types/index.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * React Server Component exports for the auto-translate plugin
3
+ */ export { getTranslationSettingsGlobal } from '../globals/translationSettings.js';
4
+
5
+ //# sourceMappingURL=rsc.js.map
@@ -0,0 +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"}
@@ -0,0 +1,2 @@
1
+ import type { GlobalConfig } from 'payload';
2
+ export declare const getTranslationSettingsGlobal: (slug?: string) => GlobalConfig;
@@ -0,0 +1,78 @@
1
+ export const getTranslationSettingsGlobal = (slug = 'translation-settings')=>({
2
+ slug,
3
+ access: {
4
+ read: ()=>true,
5
+ update: ({ req })=>{
6
+ // Only admins can update translation settings
7
+ return Boolean(req.user);
8
+ }
9
+ },
10
+ admin: {
11
+ description: 'Configure translation settings including the system prompt and model parameters'
12
+ },
13
+ fields: [
14
+ {
15
+ name: 'systemPrompt',
16
+ type: 'textarea',
17
+ admin: {
18
+ description: 'The main instruction for the AI translator. Use {fromLocale} and {toLocale} as placeholders.',
19
+ rows: 3
20
+ },
21
+ defaultValue: `You are a professional translator. Translate the JSON object values from {fromLocale} to {toLocale}.`,
22
+ label: 'System Prompt',
23
+ required: true
24
+ },
25
+ {
26
+ name: 'translationRules',
27
+ type: 'textarea',
28
+ admin: {
29
+ description: "⚠️ Do not edit if you don't know what you are doing. These rules ensure proper JSON translation behavior.",
30
+ rows: 8
31
+ },
32
+ defaultValue: `Rules:
33
+ - Only translate the values, never the keys
34
+ - Preserve the exact JSON structure
35
+ - Do not translate field names like 'id', 'createdAt', 'updatedAt', etc.
36
+ - Maintain formatting, HTML tags, and special characters
37
+ - Return only valid JSON without any markdown formatting or code blocks
38
+ - If a value is already in the target language or is a proper noun, keep it as is`,
39
+ label: 'Translation Rules',
40
+ required: true
41
+ },
42
+ {
43
+ name: 'model',
44
+ type: 'text',
45
+ admin: {
46
+ description: 'The OpenAI model to use for translations (e.g., gpt-4o, gpt-4o-mini)'
47
+ },
48
+ defaultValue: 'gpt-4o',
49
+ label: 'Model',
50
+ required: true
51
+ },
52
+ {
53
+ name: 'temperature',
54
+ type: 'number',
55
+ admin: {
56
+ description: 'Controls randomness in translation (0.0-2.0). Lower values are more deterministic.',
57
+ step: 0.1
58
+ },
59
+ defaultValue: 0.3,
60
+ label: 'Temperature',
61
+ max: 2,
62
+ min: 0,
63
+ required: true
64
+ },
65
+ {
66
+ name: 'maxTokens',
67
+ type: 'number',
68
+ admin: {
69
+ description: 'Maximum tokens for the response. Leave empty for automatic.'
70
+ },
71
+ label: 'Max Tokens',
72
+ min: 1
73
+ }
74
+ ],
75
+ label: 'Translation Settings'
76
+ });
77
+
78
+ //# sourceMappingURL=translationSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/globals/translationSettings.ts"],"sourcesContent":["import type { GlobalConfig } from 'payload'\n\nexport const getTranslationSettingsGlobal = (\n slug: string = 'translation-settings',\n): GlobalConfig => ({\n slug,\n access: {\n read: () => true,\n update: ({ req }) => {\n // Only admins can update translation settings\n return Boolean(req.user)\n },\n },\n admin: {\n description: 'Configure translation settings including the system prompt and model parameters',\n },\n fields: [\n {\n name: 'systemPrompt',\n type: 'textarea',\n admin: {\n description:\n 'The main instruction for the AI translator. Use {fromLocale} and {toLocale} as placeholders.',\n rows: 3,\n },\n defaultValue: `You are a professional translator. Translate the JSON object values from {fromLocale} to {toLocale}.`,\n label: 'System Prompt',\n required: true,\n },\n {\n name: 'translationRules',\n type: 'textarea',\n admin: {\n description:\n \"⚠️ Do not edit if you don't know what you are doing. These rules ensure proper JSON translation behavior.\",\n rows: 8,\n },\n defaultValue: `Rules:\n- Only translate the values, never the keys\n- Preserve the exact JSON structure\n- Do not translate field names like 'id', 'createdAt', 'updatedAt', etc.\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 label: 'Translation Rules',\n required: true,\n },\n {\n name: 'model',\n type: 'text',\n admin: {\n description: 'The OpenAI model to use for translations (e.g., gpt-4o, gpt-4o-mini)',\n },\n defaultValue: 'gpt-4o',\n label: 'Model',\n required: true,\n },\n {\n name: 'temperature',\n type: 'number',\n admin: {\n description:\n 'Controls randomness in translation (0.0-2.0). Lower values are more deterministic.',\n step: 0.1,\n },\n defaultValue: 0.3,\n label: 'Temperature',\n max: 2,\n min: 0,\n required: true,\n },\n {\n name: 'maxTokens',\n type: 'number',\n admin: {\n description: 'Maximum tokens for the response. Leave empty for automatic.',\n },\n label: 'Max Tokens',\n min: 1,\n },\n ],\n label: 'Translation Settings',\n})\n"],"names":["getTranslationSettingsGlobal","slug","access","read","update","req","Boolean","user","admin","description","fields","name","type","rows","defaultValue","label","required","step","max","min"],"mappings":"AAEA,OAAO,MAAMA,+BAA+B,CAC1CC,OAAe,sBAAsB,GACnB,CAAA;QAClBA;QACAC,QAAQ;YACNC,MAAM,IAAM;YACZC,QAAQ,CAAC,EAAEC,GAAG,EAAE;gBACd,8CAA8C;gBAC9C,OAAOC,QAAQD,IAAIE,IAAI;YACzB;QACF;QACAC,OAAO;YACLC,aAAa;QACf;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNJ,OAAO;oBACLC,aACE;oBACFI,MAAM;gBACR;gBACAC,cAAc,CAAC,oGAAoG,CAAC;gBACpHC,OAAO;gBACPC,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNJ,OAAO;oBACLC,aACE;oBACFI,MAAM;gBACR;gBACAC,cAAc,CAAC;;;;;;iFAM4D,CAAC;gBAC5EC,OAAO;gBACPC,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNJ,OAAO;oBACLC,aAAa;gBACf;gBACAK,cAAc;gBACdC,OAAO;gBACPC,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNJ,OAAO;oBACLC,aACE;oBACFQ,MAAM;gBACR;gBACAH,cAAc;gBACdC,OAAO;gBACPG,KAAK;gBACLC,KAAK;gBACLH,UAAU;YACZ;YACA;gBACEL,MAAM;gBACNC,MAAM;gBACNJ,OAAO;oBACLC,aAAa;gBACf;gBACAM,OAAO;gBACPI,KAAK;YACP;SACD;QACDJ,OAAO;IACT,CAAA,EAAE"}
@@ -0,0 +1,6 @@
1
+ import type { Config } from 'payload';
2
+ import type { AutoTranslateConfig } from './types/index.js';
3
+ export * from './types/index.js';
4
+ export { getTranslationExclusionsCollection } from './collections/translationExclusions.js';
5
+ export { getTranslationSettingsGlobal } from './globals/translationSettings.js';
6
+ export declare const autoTranslate: (pluginOptions: AutoTranslateConfig) => (config: Config) => Config;