payload-ai 0.0.53 → 0.0.67

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 (79) hide show
  1. package/README.md +77 -221
  2. package/dev/src/collections/Examples.ts +3 -0
  3. package/dev/src/payload.config.ts +17 -0
  4. package/dev/src/server.ts +0 -2
  5. package/package.json +2 -1
  6. package/src/access/validateAccess.ts +13 -0
  7. package/src/aiTranslate.ts +18 -10
  8. package/src/components/Translator/index.tsx +21 -11
  9. package/src/countries.json +193 -0
  10. package/src/generateText.ts +3 -3
  11. package/src/handleTranslate.ts +13 -6
  12. package/src/plugin.ts +1 -3
  13. package/src/stringTranslations.ts +119 -97
  14. package/src/translateTextAndObjects.ts +53 -13
  15. package/dist/access/admins.d.ts +0 -4
  16. package/dist/access/admins.js +0 -12
  17. package/dist/access/admins.js.map +0 -1
  18. package/dist/access/adminsOrPublished.d.ts +0 -2
  19. package/dist/access/adminsOrPublished.js +0 -18
  20. package/dist/access/adminsOrPublished.js.map +0 -1
  21. package/dist/access/anyone.d.ts +0 -2
  22. package/dist/access/anyone.js +0 -6
  23. package/dist/access/anyone.js.map +0 -1
  24. package/dist/access/checkRole.d.ts +0 -1
  25. package/dist/access/checkRole.js +0 -18
  26. package/dist/access/checkRole.js.map +0 -1
  27. package/dist/aiTranslate.d.ts +0 -7
  28. package/dist/aiTranslate.js +0 -145
  29. package/dist/aiTranslate.js.map +0 -1
  30. package/dist/components/AfterDashboard/index.d.ts +0 -3
  31. package/dist/components/AfterDashboard/index.js +0 -17
  32. package/dist/components/AfterDashboard/index.js.map +0 -1
  33. package/dist/components/AfterDashboard/index.scss +0 -10
  34. package/dist/components/Metadata/index.d.ts +0 -2
  35. package/dist/components/Metadata/index.js +0 -101
  36. package/dist/components/Metadata/index.js.map +0 -1
  37. package/dist/components/Metadata/index.scss +0 -10
  38. package/dist/components/Translator/Translator.scss +0 -6
  39. package/dist/components/Translator/index.d.ts +0 -4
  40. package/dist/components/Translator/index.js +0 -202
  41. package/dist/components/Translator/index.js.map +0 -1
  42. package/dist/deepCompareAndMerge.d.ts +0 -5
  43. package/dist/deepCompareAndMerge.js +0 -101
  44. package/dist/deepCompareAndMerge.js.map +0 -1
  45. package/dist/generateImage.d.ts +0 -1
  46. package/dist/generateImage.js +0 -80
  47. package/dist/generateImage.js.map +0 -1
  48. package/dist/generateText.d.ts +0 -5
  49. package/dist/generateText.js +0 -90
  50. package/dist/generateText.js.map +0 -1
  51. package/dist/handleTranslate.d.ts +0 -3
  52. package/dist/handleTranslate.js +0 -85
  53. package/dist/handleTranslate.js.map +0 -1
  54. package/dist/index.d.ts +0 -3
  55. package/dist/index.js.map +0 -1
  56. package/dist/mocks/mockFile.d.ts +0 -1
  57. package/dist/mocks/mockFile.js +0 -3
  58. package/dist/mocks/mockFile.js.map +0 -1
  59. package/dist/onInitExtension.d.ts +0 -3
  60. package/dist/onInitExtension.js +0 -17
  61. package/dist/onInitExtension.js.map +0 -1
  62. package/dist/plugin.d.ts +0 -3
  63. package/dist/plugin.js +0 -174
  64. package/dist/plugin.js.map +0 -1
  65. package/dist/seoTools.d.ts +0 -2
  66. package/dist/seoTools.js +0 -135
  67. package/dist/seoTools.js.map +0 -1
  68. package/dist/stringTranslations.d.ts +0 -3
  69. package/dist/stringTranslations.js +0 -174
  70. package/dist/stringTranslations.js.map +0 -1
  71. package/dist/translateTextAndObjects.d.ts +0 -1
  72. package/dist/translateTextAndObjects.js +0 -243
  73. package/dist/translateTextAndObjects.js.map +0 -1
  74. package/dist/types.d.ts +0 -20
  75. package/dist/types.js +0 -3
  76. package/dist/types.js.map +0 -1
  77. package/dist/webpack.d.ts +0 -3
  78. package/dist/webpack.js +0 -32
  79. package/dist/webpack.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Payload AI
2
2
 
3
- Translate content to different languages using OpenAI's GPT.
3
+ Translate content to different languages using [OpenAI's GPT](https://openai.com/).
4
4
 
5
5
 
6
6
  ### How to install the plugin
@@ -32,7 +32,7 @@ export const config = buildConfig({
32
32
  });
33
33
  ```
34
34
 
35
- ### Collection translation
35
+ ### Collection translation 📦
36
36
 
37
37
  Add the `collections` where you want to enable the translation and the `fields`. It will translate each field (also nested fields) on every update of the default language.
38
38
 
@@ -41,15 +41,10 @@ plugins: [
41
41
  aiTranslatorPlugin({
42
42
  enabled: true,
43
43
  collections: {
44
- examples: {
45
- // prompts: [myCollectionPrompt]
44
+ examples: { // Name of the collection you want to add translations
46
45
  fields: [
47
- 'stringText',
46
+ 'stringText', // Keys of fields you want to translate (wil also translate nested fields)
48
47
  'richText',
49
- {
50
- "fieldWithCustomPrompt":
51
- {prompt: myPromptFunction}
52
- }
53
48
  ],
54
49
  },
55
50
  },
@@ -57,21 +52,38 @@ plugins: [
57
52
  ],
58
53
  ```
59
54
 
60
- #### Use in hooks
61
55
 
62
- TODO: add documentation
63
-
64
- myCollectionPrompt = ({source}) => {
65
-
66
- source()
56
+ #### Custom prompts by Field
67
57
 
68
- return
69
- }
58
+ Use `promptFunc` for each field to customize the prompt.
70
59
 
71
- #### Custom prompts by Field
60
+ ```jsx
61
+ plugins: [
62
+ aiTranslatorPlugin({
63
+ enabled: true,
64
+ collections: {
65
+ examples: {
66
+ settings: {
67
+ model: 'gpt-4',
68
+ promptFunc: ({ messages, namespace }) => {
69
+ return [
70
+ {
71
+ role: 'system',
72
+ content:
73
+ 'Important: Add a smily face at the end of the message to make the AI more friendly. 😊',
74
+ },
75
+ ...messages,
76
+ ]
77
+ },
78
+ },
79
+ },
80
+ }
81
+ }
82
+ ]
83
+ ```
72
84
 
73
- Use the `prompt` function for each field to use a customized prompt. The function will allow you to use the following
74
85
 
86
+ The function will allow you to use the following
75
87
 
76
88
  - `req`: Request
77
89
  - `doc` Document in languages
@@ -87,9 +99,6 @@ Use the `prompt` function for each field to use a customized prompt. The functio
87
99
  - sourceField
88
100
 
89
101
 
90
- ```jsx
91
- customPrompt = ({sourceField}) => `Translate ${sourceField} to ${targetLanguage}.`
92
- ```
93
102
 
94
103
  ### Use with [payload-seo](https://payloadcms.com/docs/plugins/seo)
95
104
 
@@ -121,222 +130,69 @@ plugins: [
121
130
  }),
122
131
  ],
123
132
  ```
133
+ #### Change model for string translation
124
134
 
125
- ### Generate SEO
126
-
127
-
128
- ### Planned features
129
-
130
- - generate image alt text from GPT
131
- - generate SEO Text
132
- - generate structured content
133
- - custom access control
134
- - custom overrides for translation
135
- - generate images based on input
136
- - generate Open Graph based on content
137
-
138
- Payload is built with a robust infrastructure intended to support Plugins with ease. This provides a simple, modular, and reusable way for developers to extend the core capabilities of Payload.
139
-
140
- To build your own Payload plugin, all you need is:
141
-
142
- * An understanding of the basic Payload concepts
143
- * And some JavaScript/Typescript experience
135
+ To update the model you can change the collection settings in the same way as with other collections.
144
136
 
145
-
146
-
147
-
148
-
149
- ### Initialization
150
-
151
- The initialization process goes in the following order:
152
-
153
- 1. Incoming config is validated
154
- 2. **Plugins execute**
155
- 3. Default options are integrated
156
- 4. Sanitization cleans and validates data
157
- 5. Final config gets initialized
158
-
159
- ## Building the Plugin
160
-
161
- When you build a plugin, you are purely building a feature for your project and then abstracting it outside of the project.
162
-
163
- ### Template Files
164
-
165
- In the [payload-plugin-template](https://github.com/payloadcms/payload-plugin-template), you will see a common file structure that is used across all plugins:
166
-
167
- 1. root folder
168
- 2. /src folder
169
- 3. /dev folder
170
-
171
- #### Root
172
-
173
- In the root folder, you will see various files that relate to the configuration of the plugin. We set up our environment in a similar manner in Payload core and across other projects, so hopefully these will look familiar:
174
-
175
- * **README**.md* - This contains instructions on how to use the template. When you are ready, update this to contain instructions on how to use your Plugin.
176
- * **package**.json* - Contains necessary scripts and dependencies. Overwrite the metadata in this file to describe your Plugin.
177
- * .**editorconfig** - Defines settings to maintain consistent coding styles.
178
- * .**eslintrc**.js - Eslint configuration for reporting on problematic patterns.
179
- * .**gitignore** - List specific untracked files to omit from Git.
180
- * .**prettierrc**.js - Configuration for Prettier code formatting.
181
- * **LICENSE** - As part of the open-source community, we ship all plugins with an MIT license but it is not required.
182
- * **tsconfig**.json - Configures the compiler options for TypeScript
183
-
184
- **IMPORTANT***: You will need to modify these files.
185
-
186
- #### Dev
187
-
188
- In the dev folder, you’ll find a basic payload project, created with `npx create-payload-app` and the blank template.
189
-
190
- The `samplePlugin` has already been installed to the `payload.config()` file in this project.
191
-
192
- ```ts
137
+ ```jsx
193
138
  plugins: [
194
- samplePlugin({
195
- enabled: false,
196
- })
197
- ]
198
- ```
199
-
200
- Later when you rename the plugin or add additional options, make sure to update them here.
201
-
202
- You may wish to add collections or expand the test project depending on the purpose of your plugin. Just make sure to keep this dev environment as simplified as possible - users should be able to install your plugin without additional configuration required.
203
-
204
- When you’re ready to start development, navigate into this folder with `cd dev`
205
-
206
- And then start the project with `yarn dev` and pull up [http://localhost:3000/](http://localhost:3000/) in your browser.
207
-
208
- #### Src
209
-
210
- Now that we have our environment setup and we have a dev project ready to - it’s time to build the plugin!
211
-
212
- **index.ts**
213
-
214
- First up, the `src/index.ts` file. It is best practice not to build the plugin directly in this file, instead we use this to export the plugin and types from separate files.
215
-
216
- **Plugin.ts**
217
-
218
- To reiterate, the essence of a payload plugin is simply to extend the payload config - and that is exactly what we are doing in this file.
219
-
220
- ```ts
221
- export const samplePlugin =
222
- (pluginOptions: PluginTypes) =>
223
- (incomingConfig: Config): Config => {
224
- let config = { ...incomingConfig }
225
-
226
- // do something cool with the config here
227
-
228
- return config
139
+ aiTranslatorPlugin({
140
+ enabled: true,
141
+ stringTranslation: {
142
+ enabled: true
229
143
  }
230
-
231
- ```
232
-
233
- First, we receive the existing payload config along with any plugin options.
234
-
235
- Then we set the variable `config` to be equal to the existing config.
236
-
237
- From here, you can extend the config as you wish.
238
-
239
- Finally, you return the config and that is it!
240
-
241
- ##### Spread Syntax
242
-
243
- Spread syntax (or the spread operator) is a feature in JavaScript that uses the dot notation **(...)** to spread elements from arrays, strings, or objects into various contexts.
244
-
245
- We are going to use spread syntax to allow us to add data to existing arrays without losing the existing data. It is crucial to spread the existing data correctly – else this can cause adverse behavior and conflicts with Payload config and other plugins.
246
-
247
- Let’s say you want to build a plugin that adds a new collection:
248
-
249
- ```ts
250
- config.collections = [
251
- ...(config.collections || []),
252
- // Add additional collections here
253
- ]
144
+ collections: {
145
+ translations: {
146
+ settings: {
147
+ model: 'gpt-4',
148
+ },
149
+ }
150
+ }
151
+ }),
152
+ ],
254
153
  ```
255
154
 
256
- First we spread the `config.collections` to ensure that we don’t lose the existing collections, then you can add any additional collections just as you would in a regular payload config.
155
+ ### Access control
257
156
 
258
- This same logic is applied to other properties like admin, hooks, globals:
157
+ By default the plugin will use the [update](https://payloadcms.com/docs/access-control/collections#update) access control of the collection.
259
158
 
260
- ```ts
261
- config.globals = [
262
- ...(config.globals || []),
263
- // Add additional globals here
264
- ]
159
+ To overwrite that behaviour you can add `access` to the collections configuration.
265
160
 
266
- config.hooks = {
267
- ...(incomingConfig.hooks || {}),
268
- // Add additional hooks here
269
- }
270
- ```
271
161
 
272
- Some properties will be slightly different to extend, for instance the onInit property:
273
-
274
- ```ts
275
- import { onInitExtension } from './onInitExtension' // example file
276
-
277
- config.onInit = async payload => {
278
- if (incomingConfig.onInit) await incomingConfig.onInit(payload)
279
- // Add additional onInit code by defining an onInitExtension function
280
- onInitExtension(pluginOptions, payload)
281
- }
162
+ ```jsx
163
+ plugins: [
164
+ aiTranslatorPlugin({
165
+ enabled: true,
166
+ stringTranslation: {
167
+ enabled: true
168
+ }
169
+ collections: {
170
+ examples: {
171
+ access: () => true,
172
+ }
173
+ }
174
+ }),
175
+ ],
282
176
  ```
283
177
 
284
- If you wish to add to the onInit, you must include the async/await. We don’t use spread syntax in this case, instead you must await the existing onInit before running additional functionality.
285
178
 
286
- In the template, we have stubbed out a basic `onInitExtension` file that you can use, if not needed feel free to delete it.
179
+ ### Planned features 🧭
287
180
 
288
- ##### File Aliasing
181
+ - generate image alt text from GPT
182
+ - generate SEO Text
183
+ - generate structured content
184
+ - custom access control
185
+ - custom overrides for translation
186
+ - generate images based on input
187
+ - generate Open Graph based on content
289
188
 
290
- If your plugin uses packages or dependencies that are not browser compatible (fs, stripe, nodemailer, etc), you will need to alias them using your bundler to prevent getting errors in build.
189
+ #### Use in hooks
291
190
 
292
- You can read more about aliasing files with Webpack or Vite in the [excluding server modules](https://payloadcms.com/docs/admin/excluding-server-code#aliasing-server-only-modules) docs.
191
+ TODO: add documentation
293
192
 
294
- ##### Types.ts
193
+ myCollectionPrompt = ({source}) => {
295
194
 
296
- If your plugin has options, you should define and provide types for these options in a separate file which gets exported from the main index.ts.
195
+ source()
297
196
 
298
- ```ts
299
- export interface PluginTypes {
300
- /**
301
- * Enable or disable plugin
302
- * @default false
303
- */
304
- enabled?: boolean
197
+ return
305
198
  }
306
- ```
307
-
308
- If possible, include JSDoc comments to describe the options and their types. This allows a developer to see details about the options in their editor.
309
-
310
- ##### Testing
311
-
312
- Having a test suite for your plugin is essential to ensure quality and stability. Jest is a popular testing framework, widely used for testing JavaScript and particularly for applications built with React.
313
-
314
- Jest organizes tests into test suites and cases. We recommend creating individual tests based on the expected behavior of your plugin from start to finish.
315
-
316
- Writing tests with Jest is very straightforward and you can learn more about how it works in the [Jest documentation.](https://jestjs.io/)
317
-
318
- For this template, we stubbed out `plugin.spec.ts` in the `dev` folder where you can write your tests.
319
-
320
- ```ts
321
- describe('Plugin tests', () => {
322
- // Create tests to ensure expected behavior from the plugin
323
- it('some condition that must be met', () => {
324
- // Write your test logic here
325
- expect(...)
326
- })
327
- })
328
- ```
329
-
330
- ## Best practices
331
-
332
- With this tutorial and the `payload-plugin-template`, you should have everything you need to start building your own plugin.
333
- In addition to the setup, here are other best practices aim we follow:
334
-
335
- * **Providing an enable / disable option:** For a better user experience, provide a way to disable the plugin without uninstalling it. This is especially important if your plugin adds additional webpack aliases, this will allow you to still let the webpack run to prevent errors.
336
- * **Include tests in your GitHub CI workflow**: If you’ve configured tests for your package, integrate them into your workflow to run the tests each time you commit to the plugin repository. Learn more about [how to configure tests into your GitHub CI workflow.](https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs)
337
- * **Publish your finished plugin to NPM**: The best way to share and allow others to use your plugin once it is complete is to publish an NPM package. This process is straightforward and well documented, find out more [creating and publishing a NPM package here.](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
338
- * **Add payload-plugin topic tag**: Apply the tag **payload-plugin **to your GitHub repository. This will boost the visibility of your plugin and ensure it gets listed with [existing payload plugins](https://github.com/topics/payload-plugin).
339
- * **Use [Semantic Versioning](https://semver.org/) (SemVar)** - With the SemVar system you release version numbers that reflect the nature of changes (major, minor, patch). Ensure all major versions reference their Payload compatibility.
340
-
341
- # Questions
342
- Please contact [Payload](mailto:dev@payloadcms.com) with any questions about using this plugin template.
@@ -7,6 +7,9 @@ const Examples: CollectionConfig = {
7
7
  admin: {
8
8
  useAsTitle: 'title',
9
9
  },
10
+ access: {
11
+ update: () => false,
12
+ },
10
13
  fields: [
11
14
  {
12
15
  name: 'title',
@@ -89,6 +89,23 @@ export default buildConfig({
89
89
  'examples-with-versions': {
90
90
  fields: ['title', 'longText', 'jsonContent'],
91
91
  },
92
+ translations: {
93
+ settings: {
94
+ model: 'gpt-4',
95
+ promptFunc: ({ messages, namespace }) => {
96
+ console.log('promptFunc', messages, namespace)
97
+
98
+ return [
99
+ {
100
+ role: 'system',
101
+ content:
102
+ 'Important: Add a smily face at the end of the message to make the AI more friendly. 😊',
103
+ },
104
+ ...messages,
105
+ ]
106
+ },
107
+ },
108
+ },
92
109
  },
93
110
  }),
94
111
  seo({
package/dev/src/server.ts CHANGED
@@ -21,8 +21,6 @@ export const start = async (args?: Partial<InitOptions>) => {
21
21
  ...(args || {}),
22
22
  })
23
23
 
24
- // Add your own express routes here
25
-
26
24
  app.listen(3000)
27
25
  }
28
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payload-ai",
3
- "version": "0.0.53",
3
+ "version": "0.0.67",
4
4
  "homepage:": "https://polyxo.de",
5
5
  "repository": "git@github.com:payloadcms/payload-plugin-template.git",
6
6
  "description": "Payload AI tools",
@@ -49,6 +49,7 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@faceless-ui/modal": "2.0.1",
52
+ "iso-639-1": "^3.1.2",
52
53
  "openai": "^4.24.1",
53
54
  "webpack": "^5.89.0"
54
55
  }
@@ -0,0 +1,13 @@
1
+ import type { Access } from 'payload/config'
2
+
3
+ export const validateAccess = (req: any, res: any, pluginOptions: any) => {
4
+ const collectionOptions = pluginOptions.collections[req.collection.config.slug]
5
+
6
+ const accessControl = collectionOptions.access || req.collection.config.access.update
7
+ const access = accessControl({ req })
8
+
9
+ if (!access) {
10
+ res.status(403).send({ error: 'not allowed' })
11
+ }
12
+ return access
13
+ }
@@ -4,10 +4,16 @@ import { deepCompareTranslateAndMerge } from './deepCompareAndMerge'
4
4
 
5
5
  const aiTranslateHook =
6
6
  (
7
- { collectionOptions, collection }: { collectionOptions: any; collection: object },
7
+ {
8
+ collectionOptions,
9
+ collection,
10
+ pluginOptions,
11
+ }: { collectionOptions: any; collection: object; pluginOptions: any },
8
12
  fallback?: string,
9
13
  ): CollectionAfterChangeHook =>
10
14
  async ({ doc, req, previousDoc, context, collection }) => {
15
+ const settings = pluginOptions.collections?.[collection.slug]?.settings
16
+
11
17
  return await translateCollection({
12
18
  doc,
13
19
  req,
@@ -15,6 +21,7 @@ const aiTranslateHook =
15
21
  context,
16
22
  collection,
17
23
  collectionOptions,
24
+ settings,
18
25
  })
19
26
  }
20
27
 
@@ -30,22 +37,23 @@ export async function translateCollection({
30
37
  onlyMissing,
31
38
  codes,
32
39
  settings,
40
+ sourceLanguage,
33
41
  }: any) {
34
- if (
35
- context.triggerAfterChange === false ||
36
- req.locale !== req.payload.config.localization.defaultLocale
37
- )
38
- return
42
+ const sourceLanguageI =
43
+ sourceLanguage || doc.sourceLanguage || req.payload.config.localization.defaultLocale
44
+
45
+ console.log('Translate', req.locale, sourceLanguageI)
46
+ if (context.triggerAfterChange === false || req.locale !== sourceLanguageI) return
39
47
 
40
48
  const localCodes: string[] = req.payload.config.localization.localeCodes
41
49
 
42
50
  const translationPromises = localCodes
43
51
  .filter(
44
52
  targetLanguage =>
45
- targetLanguage !== req.payload.config.localization.defaultLocale &&
46
- (!codes || codes.includes(targetLanguage)),
53
+ targetLanguage !== sourceLanguageI && (!codes || codes.includes(targetLanguage)),
47
54
  )
48
55
  .map(async (tL: string) => {
56
+ console.log('tL', tL, doc)
49
57
  const targetDoc = await req.payload.findByID({
50
58
  collection: collection.slug,
51
59
  id: doc.id,
@@ -63,8 +71,8 @@ export async function translateCollection({
63
71
  tL,
64
72
  previousDoc.id ? 'update' : 'create',
65
73
  onlyMissing,
66
- req.payload.config.localization.defaultLocale,
67
- settings,
74
+ sourceLanguageI,
75
+ { ...settings, namespace: doc?.namespace },
68
76
  )
69
77
 
70
78
  const { id, _status, updatedAt, createdAt, publishedDate, ...dataNew } =
@@ -61,17 +61,21 @@ export const Translator: React.FC = () => {
61
61
  }
62
62
  setIsLoading(true)
63
63
  try {
64
- const response = await fetch(`/api/${documentInfo.collection.slug}/translate`, {
65
- method: 'POST',
66
- headers: {
67
- 'Content-Type': 'application/json',
64
+ const response = await fetch(
65
+ `/api/${documentInfo.collection.slug}/translate?locale=${locale.code}`,
66
+ {
67
+ method: 'POST',
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ },
71
+ body: JSON.stringify({
72
+ id: documentInfo.id,
73
+ locale: locale.code,
74
+ codes,
75
+ settings,
76
+ }),
68
77
  },
69
- body: JSON.stringify({
70
- id: documentInfo.id,
71
- codes,
72
- settings,
73
- }),
74
- })
78
+ )
75
79
 
76
80
  const translatedValues = await response.json()
77
81
 
@@ -107,7 +111,12 @@ export const Translator: React.FC = () => {
107
111
  value: 'gpt-3.5-turbo-1106',
108
112
  },
109
113
  {
110
- label: 'GPT-4',
114
+ label: 'GPT-4 Turbo (Preview)',
115
+ value: 'gpt-4-turbo-preview',
116
+ },
117
+
118
+ {
119
+ label: 'GPT-4 (most expensive)',
111
120
  value: 'gpt-4',
112
121
  },
113
122
  ]
@@ -133,6 +142,7 @@ export const Translator: React.FC = () => {
133
142
  options={options}
134
143
  />
135
144
  </div>
145
+ <div>Translates from: {locale.code}</div>
136
146
  <div className={`${baseClass}__translation-buttons`}>
137
147
  <Button disabled={isLoading} onClick={() => translate({})}>
138
148
  <span>Translate content to all languages</span>