@strapi/i18n 0.0.0-next.f698d55751345c4ca87477ef683475c1a68f310a → 0.0.0-next.f86041c89a8c1545c6437a881dc613e98bc52bd7

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 (459) hide show
  1. package/dist/admin/components/BulkLocaleActionModal.js +262 -0
  2. package/dist/admin/components/BulkLocaleActionModal.js.map +1 -0
  3. package/dist/admin/components/BulkLocaleActionModal.mjs +260 -0
  4. package/dist/admin/components/BulkLocaleActionModal.mjs.map +1 -0
  5. package/dist/admin/components/CMHeaderActions.js +995 -0
  6. package/dist/admin/components/CMHeaderActions.js.map +1 -0
  7. package/dist/admin/components/CMHeaderActions.mjs +969 -0
  8. package/dist/admin/components/CMHeaderActions.mjs.map +1 -0
  9. package/dist/admin/components/CMListViewModalsAdditionalInformation.js +69 -0
  10. package/dist/admin/components/CMListViewModalsAdditionalInformation.js.map +1 -0
  11. package/dist/admin/components/CMListViewModalsAdditionalInformation.mjs +65 -0
  12. package/dist/admin/components/CMListViewModalsAdditionalInformation.mjs.map +1 -0
  13. package/dist/admin/components/CheckboxConfirmation.js +155 -0
  14. package/dist/admin/components/CheckboxConfirmation.js.map +1 -0
  15. package/dist/admin/components/CheckboxConfirmation.mjs +134 -0
  16. package/dist/admin/components/CheckboxConfirmation.mjs.map +1 -0
  17. package/dist/admin/components/CreateLocale.js +401 -0
  18. package/dist/admin/components/CreateLocale.js.map +1 -0
  19. package/dist/admin/components/CreateLocale.mjs +375 -0
  20. package/dist/admin/components/CreateLocale.mjs.map +1 -0
  21. package/dist/admin/components/DeleteLocale.js +90 -0
  22. package/dist/admin/components/DeleteLocale.js.map +1 -0
  23. package/dist/admin/components/DeleteLocale.mjs +69 -0
  24. package/dist/admin/components/DeleteLocale.mjs.map +1 -0
  25. package/dist/admin/components/EditLocale.js +212 -0
  26. package/dist/admin/components/EditLocale.js.map +1 -0
  27. package/dist/admin/components/EditLocale.mjs +190 -0
  28. package/dist/admin/components/EditLocale.mjs.map +1 -0
  29. package/dist/admin/components/LocaleListCell.js +102 -0
  30. package/dist/admin/components/LocaleListCell.js.map +1 -0
  31. package/dist/admin/components/LocaleListCell.mjs +100 -0
  32. package/dist/admin/components/LocaleListCell.mjs.map +1 -0
  33. package/dist/admin/components/LocalePicker.js +105 -0
  34. package/dist/admin/components/LocalePicker.js.map +1 -0
  35. package/dist/admin/components/LocalePicker.mjs +84 -0
  36. package/dist/admin/components/LocalePicker.mjs.map +1 -0
  37. package/dist/admin/components/LocaleTable.js +143 -0
  38. package/dist/admin/components/LocaleTable.js.map +1 -0
  39. package/dist/admin/components/LocaleTable.mjs +122 -0
  40. package/dist/admin/components/LocaleTable.mjs.map +1 -0
  41. package/dist/admin/constants.js +37 -0
  42. package/dist/admin/constants.js.map +1 -0
  43. package/dist/admin/constants.mjs +35 -0
  44. package/dist/admin/constants.mjs.map +1 -0
  45. package/dist/admin/contentManagerHooks/editView.js +93 -0
  46. package/dist/admin/contentManagerHooks/editView.js.map +1 -0
  47. package/dist/admin/contentManagerHooks/editView.mjs +91 -0
  48. package/dist/admin/contentManagerHooks/editView.mjs.map +1 -0
  49. package/dist/admin/contentManagerHooks/listView.js +44 -0
  50. package/dist/admin/contentManagerHooks/listView.js.map +1 -0
  51. package/dist/admin/contentManagerHooks/listView.mjs +42 -0
  52. package/dist/admin/contentManagerHooks/listView.mjs.map +1 -0
  53. package/dist/admin/contentReleasesHooks/releaseDetailsView.js +20 -0
  54. package/dist/admin/contentReleasesHooks/releaseDetailsView.js.map +1 -0
  55. package/dist/admin/contentReleasesHooks/releaseDetailsView.mjs +18 -0
  56. package/dist/admin/contentReleasesHooks/releaseDetailsView.mjs.map +1 -0
  57. package/dist/admin/hooks/useAILocalizationJobsPolling.js +110 -0
  58. package/dist/admin/hooks/useAILocalizationJobsPolling.js.map +1 -0
  59. package/dist/admin/hooks/useAILocalizationJobsPolling.mjs +89 -0
  60. package/dist/admin/hooks/useAILocalizationJobsPolling.mjs.map +1 -0
  61. package/dist/admin/hooks/useI18n.js +78 -0
  62. package/dist/admin/hooks/useI18n.js.map +1 -0
  63. package/dist/admin/hooks/useI18n.mjs +57 -0
  64. package/dist/admin/hooks/useI18n.mjs.map +1 -0
  65. package/dist/admin/index.js +226 -5
  66. package/dist/admin/index.js.map +1 -1
  67. package/dist/admin/index.mjs +202 -5
  68. package/dist/admin/index.mjs.map +1 -1
  69. package/dist/admin/middlewares/extendCTBAttributeInitialData.js +71 -0
  70. package/dist/admin/middlewares/extendCTBAttributeInitialData.js.map +1 -0
  71. package/dist/admin/middlewares/extendCTBAttributeInitialData.mjs +69 -0
  72. package/dist/admin/middlewares/extendCTBAttributeInitialData.mjs.map +1 -0
  73. package/dist/admin/middlewares/extendCTBInitialData.js +40 -0
  74. package/dist/admin/middlewares/extendCTBInitialData.js.map +1 -0
  75. package/dist/admin/middlewares/extendCTBInitialData.mjs +38 -0
  76. package/dist/admin/middlewares/extendCTBInitialData.mjs.map +1 -0
  77. package/dist/admin/middlewares/rbac-middleware.js +49 -0
  78. package/dist/admin/middlewares/rbac-middleware.js.map +1 -0
  79. package/dist/admin/middlewares/rbac-middleware.mjs +28 -0
  80. package/dist/admin/middlewares/rbac-middleware.mjs.map +1 -0
  81. package/dist/admin/pages/SettingsPage.js +182 -0
  82. package/dist/admin/pages/SettingsPage.js.map +1 -0
  83. package/dist/admin/pages/SettingsPage.mjs +179 -0
  84. package/dist/admin/pages/SettingsPage.mjs.map +1 -0
  85. package/dist/admin/pluginId.js +6 -0
  86. package/dist/admin/pluginId.js.map +1 -0
  87. package/dist/admin/pluginId.mjs +4 -0
  88. package/dist/admin/pluginId.mjs.map +1 -0
  89. package/dist/admin/services/aiLocalizationJobs.js +26 -0
  90. package/dist/admin/services/aiLocalizationJobs.js.map +1 -0
  91. package/dist/admin/services/aiLocalizationJobs.mjs +24 -0
  92. package/dist/admin/services/aiLocalizationJobs.mjs.map +1 -0
  93. package/dist/admin/services/api.js +15 -0
  94. package/dist/admin/services/api.js.map +1 -0
  95. package/dist/admin/services/api.mjs +13 -0
  96. package/dist/admin/services/api.mjs.map +1 -0
  97. package/dist/admin/services/locales.js +72 -0
  98. package/dist/admin/services/locales.js.map +1 -0
  99. package/dist/admin/services/locales.mjs +66 -0
  100. package/dist/admin/services/locales.mjs.map +1 -0
  101. package/dist/admin/services/relations.js +23 -0
  102. package/dist/admin/services/relations.js.map +1 -0
  103. package/dist/admin/services/relations.mjs +21 -0
  104. package/dist/admin/services/relations.mjs.map +1 -0
  105. package/dist/admin/services/settings.js +30 -0
  106. package/dist/admin/services/settings.js.map +1 -0
  107. package/dist/admin/services/settings.mjs +27 -0
  108. package/dist/admin/services/settings.mjs.map +1 -0
  109. package/dist/admin/src/components/CMHeaderActions.d.ts +18 -5
  110. package/dist/admin/src/components/LocaleListCell.d.ts +5 -4
  111. package/dist/admin/src/components/tests/CreateLocale.test.d.ts +1 -0
  112. package/dist/admin/src/components/tests/DeleteLocale.test.d.ts +1 -0
  113. package/dist/admin/src/components/tests/EditLocale.test.d.ts +1 -0
  114. package/dist/admin/src/components/tests/LocaleListCell.test.d.ts +1 -0
  115. package/dist/admin/src/contentManagerHooks/tests/EditView.test.d.ts +1 -0
  116. package/dist/admin/src/contentReleasesHooks/releaseDetailsView.d.ts +1 -1
  117. package/dist/admin/src/hooks/useAILocalizationJobsPolling.d.ts +9 -0
  118. package/dist/admin/src/pages/tests/SettingsPage.test.d.ts +1 -0
  119. package/dist/admin/src/services/aiLocalizationJobs.d.ts +6 -0
  120. package/dist/admin/src/services/api.d.ts +1 -1
  121. package/dist/admin/src/services/locales.d.ts +1 -1
  122. package/dist/admin/src/services/relations.d.ts +1 -1
  123. package/dist/admin/src/services/settings.d.ts +5 -0
  124. package/dist/admin/src/utils/schemas.d.ts +642 -16
  125. package/dist/admin/tests/server.d.ts +1 -0
  126. package/dist/admin/tests/utils.d.ts +6 -0
  127. package/dist/admin/translations/de.json.js +66 -0
  128. package/dist/admin/translations/de.json.js.map +1 -0
  129. package/dist/admin/translations/de.json.mjs +64 -0
  130. package/dist/admin/translations/de.json.mjs.map +1 -0
  131. package/dist/admin/translations/dk.json.js +66 -0
  132. package/dist/admin/translations/dk.json.js.map +1 -0
  133. package/dist/admin/translations/dk.json.mjs +64 -0
  134. package/dist/admin/translations/dk.json.mjs.map +1 -0
  135. package/dist/admin/translations/en.json.js +91 -0
  136. package/dist/admin/translations/en.json.js.map +1 -0
  137. package/dist/admin/translations/en.json.mjs +89 -0
  138. package/dist/admin/translations/en.json.mjs.map +1 -0
  139. package/dist/admin/translations/es.json.js +66 -0
  140. package/dist/admin/translations/es.json.js.map +1 -0
  141. package/dist/admin/translations/es.json.mjs +64 -0
  142. package/dist/admin/translations/es.json.mjs.map +1 -0
  143. package/dist/admin/translations/fr.json.js +66 -0
  144. package/dist/admin/translations/fr.json.js.map +1 -0
  145. package/dist/admin/translations/fr.json.mjs +64 -0
  146. package/dist/admin/translations/fr.json.mjs.map +1 -0
  147. package/dist/admin/translations/ko.json.js +65 -0
  148. package/dist/admin/translations/ko.json.js.map +1 -0
  149. package/dist/admin/translations/ko.json.mjs +63 -0
  150. package/dist/admin/translations/ko.json.mjs.map +1 -0
  151. package/dist/admin/translations/pl.json.js +66 -0
  152. package/dist/admin/translations/pl.json.js.map +1 -0
  153. package/dist/admin/translations/pl.json.mjs +64 -0
  154. package/dist/admin/translations/pl.json.mjs.map +1 -0
  155. package/dist/admin/translations/ru.json.js +68 -0
  156. package/dist/admin/translations/ru.json.js.map +1 -0
  157. package/dist/admin/translations/ru.json.mjs +66 -0
  158. package/dist/admin/translations/ru.json.mjs.map +1 -0
  159. package/dist/admin/translations/tr.json.js +66 -0
  160. package/dist/admin/translations/tr.json.js.map +1 -0
  161. package/dist/admin/translations/tr.json.mjs +64 -0
  162. package/dist/admin/translations/tr.json.mjs.map +1 -0
  163. package/dist/admin/translations/uk.json.js +82 -0
  164. package/dist/admin/translations/uk.json.js.map +1 -0
  165. package/dist/admin/translations/uk.json.mjs +80 -0
  166. package/dist/admin/translations/uk.json.mjs.map +1 -0
  167. package/dist/admin/translations/zh-Hans.json.js +57 -0
  168. package/dist/admin/translations/zh-Hans.json.js.map +1 -0
  169. package/dist/admin/translations/zh-Hans.json.mjs +55 -0
  170. package/dist/admin/translations/zh-Hans.json.mjs.map +1 -0
  171. package/dist/admin/translations/zh.json.js +66 -0
  172. package/dist/admin/translations/zh.json.js.map +1 -0
  173. package/dist/admin/translations/zh.json.mjs +64 -0
  174. package/dist/admin/translations/zh.json.mjs.map +1 -0
  175. package/dist/admin/utils/baseQuery.js +8 -0
  176. package/dist/admin/utils/baseQuery.js.map +1 -0
  177. package/dist/admin/utils/baseQuery.mjs +6 -0
  178. package/dist/admin/utils/baseQuery.mjs.map +1 -0
  179. package/dist/admin/utils/clean.js +70 -0
  180. package/dist/admin/utils/clean.js.map +1 -0
  181. package/dist/admin/utils/clean.mjs +68 -0
  182. package/dist/admin/utils/clean.mjs.map +1 -0
  183. package/dist/admin/utils/fields.js +34 -0
  184. package/dist/admin/utils/fields.js.map +1 -0
  185. package/dist/admin/utils/fields.mjs +31 -0
  186. package/dist/admin/utils/fields.mjs.map +1 -0
  187. package/dist/admin/utils/getTranslation.js +8 -0
  188. package/dist/admin/utils/getTranslation.js.map +1 -0
  189. package/dist/admin/utils/getTranslation.mjs +6 -0
  190. package/dist/admin/utils/getTranslation.mjs.map +1 -0
  191. package/dist/admin/utils/prefixPluginTranslations.js +11 -0
  192. package/dist/admin/utils/prefixPluginTranslations.js.map +1 -0
  193. package/dist/admin/utils/prefixPluginTranslations.mjs +9 -0
  194. package/dist/admin/utils/prefixPluginTranslations.mjs.map +1 -0
  195. package/dist/admin/utils/schemas.js +74 -0
  196. package/dist/admin/utils/schemas.js.map +1 -0
  197. package/dist/admin/utils/schemas.mjs +72 -0
  198. package/dist/admin/utils/schemas.mjs.map +1 -0
  199. package/dist/admin/utils/strings.js +6 -0
  200. package/dist/admin/utils/strings.js.map +1 -0
  201. package/dist/admin/utils/strings.mjs +4 -0
  202. package/dist/admin/utils/strings.mjs.map +1 -0
  203. package/dist/server/bootstrap.js +95 -0
  204. package/dist/server/bootstrap.js.map +1 -0
  205. package/dist/server/bootstrap.mjs +93 -0
  206. package/dist/server/bootstrap.mjs.map +1 -0
  207. package/dist/server/constants/index.js +29 -0
  208. package/dist/server/constants/index.js.map +1 -0
  209. package/dist/server/constants/index.mjs +25 -0
  210. package/dist/server/constants/index.mjs.map +1 -0
  211. package/dist/server/constants/iso-locales.json.js +2495 -0
  212. package/dist/server/constants/iso-locales.json.js.map +1 -0
  213. package/dist/server/constants/iso-locales.json.mjs +2493 -0
  214. package/dist/server/constants/iso-locales.json.mjs.map +1 -0
  215. package/dist/server/content-types/index.js +10 -0
  216. package/dist/server/content-types/index.js.map +1 -0
  217. package/dist/server/content-types/index.mjs +8 -0
  218. package/dist/server/content-types/index.mjs.map +1 -0
  219. package/dist/server/content-types/locale/index.js +10 -0
  220. package/dist/server/content-types/locale/index.js.map +1 -0
  221. package/dist/server/content-types/locale/index.mjs +8 -0
  222. package/dist/server/content-types/locale/index.mjs.map +1 -0
  223. package/dist/server/content-types/locale/schema.json.js +46 -0
  224. package/dist/server/content-types/locale/schema.json.js.map +1 -0
  225. package/dist/server/content-types/locale/schema.json.mjs +38 -0
  226. package/dist/server/content-types/locale/schema.json.mjs.map +1 -0
  227. package/dist/server/controllers/ai-localization-jobs.js +47 -0
  228. package/dist/server/controllers/ai-localization-jobs.js.map +1 -0
  229. package/dist/server/controllers/ai-localization-jobs.mjs +45 -0
  230. package/dist/server/controllers/ai-localization-jobs.mjs.map +1 -0
  231. package/dist/server/controllers/content-types.js +76 -0
  232. package/dist/server/controllers/content-types.js.map +1 -0
  233. package/dist/server/controllers/content-types.mjs +74 -0
  234. package/dist/server/controllers/content-types.mjs.map +1 -0
  235. package/dist/server/controllers/index.js +18 -0
  236. package/dist/server/controllers/index.js.map +1 -0
  237. package/dist/server/controllers/index.mjs +16 -0
  238. package/dist/server/controllers/index.mjs.map +1 -0
  239. package/dist/server/controllers/iso-locales.js +13 -0
  240. package/dist/server/controllers/iso-locales.js.map +1 -0
  241. package/dist/server/controllers/iso-locales.mjs +11 -0
  242. package/dist/server/controllers/iso-locales.mjs.map +1 -0
  243. package/dist/server/controllers/locales.js +108 -0
  244. package/dist/server/controllers/locales.js.map +1 -0
  245. package/dist/server/controllers/locales.mjs +87 -0
  246. package/dist/server/controllers/locales.mjs.map +1 -0
  247. package/dist/server/controllers/settings.js +24 -0
  248. package/dist/server/controllers/settings.js.map +1 -0
  249. package/dist/server/controllers/settings.mjs +22 -0
  250. package/dist/server/controllers/settings.mjs.map +1 -0
  251. package/dist/server/controllers/validate-locale-creation.js +47 -0
  252. package/dist/server/controllers/validate-locale-creation.js.map +1 -0
  253. package/dist/server/controllers/validate-locale-creation.mjs +45 -0
  254. package/dist/server/controllers/validate-locale-creation.mjs.map +1 -0
  255. package/dist/server/domain/locale.js +11 -0
  256. package/dist/server/domain/locale.js.map +1 -0
  257. package/dist/server/domain/locale.mjs +9 -0
  258. package/dist/server/domain/locale.mjs.map +1 -0
  259. package/dist/server/graphql.js +115 -0
  260. package/dist/server/graphql.js.map +1 -0
  261. package/dist/server/graphql.mjs +113 -0
  262. package/dist/server/graphql.mjs.map +1 -0
  263. package/dist/server/index.js +18 -3460
  264. package/dist/server/index.js.map +1 -1
  265. package/dist/server/index.mjs +17 -3443
  266. package/dist/server/index.mjs.map +1 -1
  267. package/dist/server/models/ai-localization-job.js +60 -0
  268. package/dist/server/models/ai-localization-job.js.map +1 -0
  269. package/dist/server/models/ai-localization-job.mjs +57 -0
  270. package/dist/server/models/ai-localization-job.mjs.map +1 -0
  271. package/dist/server/register.js +81 -0
  272. package/dist/server/register.js.map +1 -0
  273. package/dist/server/register.mjs +79 -0
  274. package/dist/server/register.mjs.map +1 -0
  275. package/dist/server/routes/admin.js +142 -0
  276. package/dist/server/routes/admin.js.map +1 -0
  277. package/dist/server/routes/admin.mjs +140 -0
  278. package/dist/server/routes/admin.mjs.map +1 -0
  279. package/dist/server/routes/content-api.js +19 -0
  280. package/dist/server/routes/content-api.js.map +1 -0
  281. package/dist/server/routes/content-api.mjs +17 -0
  282. package/dist/server/routes/content-api.mjs.map +1 -0
  283. package/dist/server/routes/index.js +12 -0
  284. package/dist/server/routes/index.js.map +1 -0
  285. package/dist/server/routes/index.mjs +10 -0
  286. package/dist/server/routes/index.mjs.map +1 -0
  287. package/dist/server/routes/validation/locale.js +57 -0
  288. package/dist/server/routes/validation/locale.js.map +1 -0
  289. package/dist/server/routes/validation/locale.mjs +36 -0
  290. package/dist/server/routes/validation/locale.mjs.map +1 -0
  291. package/dist/server/services/ai-localization-jobs.js +64 -0
  292. package/dist/server/services/ai-localization-jobs.js.map +1 -0
  293. package/dist/server/services/ai-localization-jobs.mjs +62 -0
  294. package/dist/server/services/ai-localization-jobs.mjs.map +1 -0
  295. package/dist/server/services/ai-localizations.js +269 -0
  296. package/dist/server/services/ai-localizations.js.map +1 -0
  297. package/dist/server/services/ai-localizations.mjs +267 -0
  298. package/dist/server/services/ai-localizations.mjs.map +1 -0
  299. package/dist/server/services/content-types.js +157 -0
  300. package/dist/server/services/content-types.js.map +1 -0
  301. package/dist/server/services/content-types.mjs +155 -0
  302. package/dist/server/services/content-types.mjs.map +1 -0
  303. package/dist/server/services/index.js +28 -0
  304. package/dist/server/services/index.js.map +1 -0
  305. package/dist/server/services/index.mjs +26 -0
  306. package/dist/server/services/index.mjs.map +1 -0
  307. package/dist/server/services/iso-locales.js +12 -0
  308. package/dist/server/services/iso-locales.js.map +1 -0
  309. package/dist/server/services/iso-locales.mjs +10 -0
  310. package/dist/server/services/iso-locales.mjs.map +1 -0
  311. package/dist/server/services/locales.js +114 -0
  312. package/dist/server/services/locales.js.map +1 -0
  313. package/dist/server/services/locales.mjs +112 -0
  314. package/dist/server/services/locales.mjs.map +1 -0
  315. package/dist/server/services/localizations.js +68 -0
  316. package/dist/server/services/localizations.js.map +1 -0
  317. package/dist/server/services/localizations.mjs +66 -0
  318. package/dist/server/services/localizations.mjs.map +1 -0
  319. package/dist/server/services/metrics.js +30 -0
  320. package/dist/server/services/metrics.js.map +1 -0
  321. package/dist/server/services/metrics.mjs +28 -0
  322. package/dist/server/services/metrics.mjs.map +1 -0
  323. package/dist/server/services/permissions/actions.js +136 -0
  324. package/dist/server/services/permissions/actions.js.map +1 -0
  325. package/dist/server/services/permissions/actions.mjs +134 -0
  326. package/dist/server/services/permissions/actions.mjs.map +1 -0
  327. package/dist/server/services/permissions/engine.js +52 -0
  328. package/dist/server/services/permissions/engine.js.map +1 -0
  329. package/dist/server/services/permissions/engine.mjs +50 -0
  330. package/dist/server/services/permissions/engine.mjs.map +1 -0
  331. package/dist/server/services/permissions/sections-builder.js +45 -0
  332. package/dist/server/services/permissions/sections-builder.js.map +1 -0
  333. package/dist/server/services/permissions/sections-builder.mjs +43 -0
  334. package/dist/server/services/permissions/sections-builder.mjs.map +1 -0
  335. package/dist/server/services/permissions.js +14 -0
  336. package/dist/server/services/permissions.js.map +1 -0
  337. package/dist/server/services/permissions.mjs +12 -0
  338. package/dist/server/services/permissions.mjs.map +1 -0
  339. package/dist/server/services/sanitize/index.js +34 -0
  340. package/dist/server/services/sanitize/index.js.map +1 -0
  341. package/dist/server/services/sanitize/index.mjs +32 -0
  342. package/dist/server/services/sanitize/index.mjs.map +1 -0
  343. package/dist/server/services/settings.js +25 -0
  344. package/dist/server/services/settings.js.map +1 -0
  345. package/dist/server/services/settings.mjs +23 -0
  346. package/dist/server/services/settings.mjs.map +1 -0
  347. package/dist/server/src/bootstrap.d.ts.map +1 -1
  348. package/dist/server/src/controllers/ai-localization-jobs.d.ts +17 -0
  349. package/dist/server/src/controllers/ai-localization-jobs.d.ts.map +1 -0
  350. package/dist/server/src/controllers/index.d.ts +10 -0
  351. package/dist/server/src/controllers/index.d.ts.map +1 -1
  352. package/dist/server/src/controllers/settings.d.ts +7 -0
  353. package/dist/server/src/controllers/settings.d.ts.map +1 -0
  354. package/dist/server/src/index.d.ts +47 -8
  355. package/dist/server/src/index.d.ts.map +1 -1
  356. package/dist/server/src/models/ai-localization-job.d.ts +5 -0
  357. package/dist/server/src/models/ai-localization-job.d.ts.map +1 -0
  358. package/dist/server/src/models/index.d.ts +5 -0
  359. package/dist/server/src/models/index.d.ts.map +1 -0
  360. package/dist/server/src/register.d.ts +1 -1
  361. package/dist/server/src/register.d.ts.map +1 -1
  362. package/dist/server/src/routes/admin.d.ts.map +1 -1
  363. package/dist/server/src/routes/content-api.d.ts +5 -8
  364. package/dist/server/src/routes/content-api.d.ts.map +1 -1
  365. package/dist/server/src/routes/index.d.ts +3 -7
  366. package/dist/server/src/routes/index.d.ts.map +1 -1
  367. package/dist/server/src/routes/validation/index.d.ts +2 -0
  368. package/dist/server/src/routes/validation/index.d.ts.map +1 -0
  369. package/dist/server/src/routes/validation/locale.d.ts +41 -0
  370. package/dist/server/src/routes/validation/locale.d.ts.map +1 -0
  371. package/dist/server/src/services/ai-localization-jobs.d.ts +26 -0
  372. package/dist/server/src/services/ai-localization-jobs.d.ts.map +1 -0
  373. package/dist/server/src/services/ai-localizations.d.ts +18 -0
  374. package/dist/server/src/services/ai-localizations.d.ts.map +1 -0
  375. package/dist/server/src/services/index.d.ts +33 -0
  376. package/dist/server/src/services/index.d.ts.map +1 -1
  377. package/dist/server/src/services/localizations.d.ts +1 -1
  378. package/dist/server/src/services/localizations.d.ts.map +1 -1
  379. package/dist/server/src/services/settings.d.ts +13 -0
  380. package/dist/server/src/services/settings.d.ts.map +1 -0
  381. package/dist/server/src/utils/index.d.ts +7 -1
  382. package/dist/server/src/utils/index.d.ts.map +1 -1
  383. package/dist/server/src/validation/settings.d.ts +12 -0
  384. package/dist/server/src/validation/settings.d.ts.map +1 -0
  385. package/dist/server/utils/index.js +16 -0
  386. package/dist/server/utils/index.js.map +1 -0
  387. package/dist/server/utils/index.mjs +13 -0
  388. package/dist/server/utils/index.mjs.map +1 -0
  389. package/dist/server/validation/content-types.js +18 -0
  390. package/dist/server/validation/content-types.js.map +1 -0
  391. package/dist/server/validation/content-types.mjs +16 -0
  392. package/dist/server/validation/content-types.mjs.map +1 -0
  393. package/dist/server/validation/locales.js +23 -0
  394. package/dist/server/validation/locales.js.map +1 -0
  395. package/dist/server/validation/locales.mjs +20 -0
  396. package/dist/server/validation/locales.mjs.map +1 -0
  397. package/dist/server/validation/settings.js +11 -0
  398. package/dist/server/validation/settings.js.map +1 -0
  399. package/dist/server/validation/settings.mjs +9 -0
  400. package/dist/server/validation/settings.mjs.map +1 -0
  401. package/dist/shared/contracts/ai-localization-jobs.d.ts +27 -0
  402. package/dist/shared/contracts/ai-localization-jobs.d.ts.map +1 -0
  403. package/dist/shared/contracts/content-manager.d.ts +1 -1
  404. package/dist/shared/contracts/settings.d.ts +40 -0
  405. package/dist/shared/contracts/shared.d.ts +1 -1
  406. package/dist/shared/contracts/shared.d.ts.map +1 -0
  407. package/package.json +22 -17
  408. package/dist/_chunks/SettingsPage-BAx9nmep.mjs +0 -554
  409. package/dist/_chunks/SettingsPage-BAx9nmep.mjs.map +0 -1
  410. package/dist/_chunks/SettingsPage-BTgjb2KS.js +0 -573
  411. package/dist/_chunks/SettingsPage-BTgjb2KS.js.map +0 -1
  412. package/dist/_chunks/de-9eCAqqrB.mjs +0 -66
  413. package/dist/_chunks/de-9eCAqqrB.mjs.map +0 -1
  414. package/dist/_chunks/de-DtWiGdHl.js +0 -66
  415. package/dist/_chunks/de-DtWiGdHl.js.map +0 -1
  416. package/dist/_chunks/dk-2qBjxt-P.mjs +0 -66
  417. package/dist/_chunks/dk-2qBjxt-P.mjs.map +0 -1
  418. package/dist/_chunks/dk-D8C-casx.js +0 -66
  419. package/dist/_chunks/dk-D8C-casx.js.map +0 -1
  420. package/dist/_chunks/en-BKBz3tro.js +0 -81
  421. package/dist/_chunks/en-BKBz3tro.js.map +0 -1
  422. package/dist/_chunks/en-DlXfy6Gy.mjs +0 -81
  423. package/dist/_chunks/en-DlXfy6Gy.mjs.map +0 -1
  424. package/dist/_chunks/es-DS-XFGSw.js +0 -66
  425. package/dist/_chunks/es-DS-XFGSw.js.map +0 -1
  426. package/dist/_chunks/es-DlmMVaBG.mjs +0 -66
  427. package/dist/_chunks/es-DlmMVaBG.mjs.map +0 -1
  428. package/dist/_chunks/fr-3S6ke71d.mjs +0 -66
  429. package/dist/_chunks/fr-3S6ke71d.mjs.map +0 -1
  430. package/dist/_chunks/fr-BTjekDpq.js +0 -66
  431. package/dist/_chunks/fr-BTjekDpq.js.map +0 -1
  432. package/dist/_chunks/index-3yyF237r.js +0 -1644
  433. package/dist/_chunks/index-3yyF237r.js.map +0 -1
  434. package/dist/_chunks/index-B0NijiBB.mjs +0 -1623
  435. package/dist/_chunks/index-B0NijiBB.mjs.map +0 -1
  436. package/dist/_chunks/ko-DmcGUBQ3.js +0 -65
  437. package/dist/_chunks/ko-DmcGUBQ3.js.map +0 -1
  438. package/dist/_chunks/ko-qTjQ8IMw.mjs +0 -65
  439. package/dist/_chunks/ko-qTjQ8IMw.mjs.map +0 -1
  440. package/dist/_chunks/pl-B67TSHqT.mjs +0 -66
  441. package/dist/_chunks/pl-B67TSHqT.mjs.map +0 -1
  442. package/dist/_chunks/pl-Cn5RYonZ.js +0 -66
  443. package/dist/_chunks/pl-Cn5RYonZ.js.map +0 -1
  444. package/dist/_chunks/ru-BMBgVL3s.js +0 -68
  445. package/dist/_chunks/ru-BMBgVL3s.js.map +0 -1
  446. package/dist/_chunks/ru-hagMa57T.mjs +0 -68
  447. package/dist/_chunks/ru-hagMa57T.mjs.map +0 -1
  448. package/dist/_chunks/tr-CarUU76c.js +0 -66
  449. package/dist/_chunks/tr-CarUU76c.js.map +0 -1
  450. package/dist/_chunks/tr-Dw_jmkG-.mjs +0 -66
  451. package/dist/_chunks/tr-Dw_jmkG-.mjs.map +0 -1
  452. package/dist/_chunks/zh-57YM4amO.mjs +0 -66
  453. package/dist/_chunks/zh-57YM4amO.mjs.map +0 -1
  454. package/dist/_chunks/zh-CukOviB0.js +0 -66
  455. package/dist/_chunks/zh-CukOviB0.js.map +0 -1
  456. package/dist/_chunks/zh-Hans-DSHIXAa3.js +0 -57
  457. package/dist/_chunks/zh-Hans-DSHIXAa3.js.map +0 -1
  458. package/dist/_chunks/zh-Hans-Dyc-aR-h.mjs +0 -57
  459. package/dist/_chunks/zh-Hans-Dyc-aR-h.mjs.map +0 -1
@@ -0,0 +1,269 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@strapi/utils');
4
+ var index = require('../utils/index.js');
5
+
6
+ const isLocalizedAttribute = (attribute)=>{
7
+ return attribute?.pluginOptions?.i18n?.localized === true;
8
+ };
9
+ const createAILocalizationsService = ({ strapi })=>{
10
+ // TODO: add a helper function to get the AI server URL
11
+ const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';
12
+ const aiLocalizationJobsService = index.getService('ai-localization-jobs');
13
+ const UNSUPPORTED_ATTRIBUTE_TYPES = [
14
+ 'media',
15
+ 'relation',
16
+ 'boolean',
17
+ 'enumeration'
18
+ ];
19
+ const IGNORED_FIELDS = [
20
+ 'id',
21
+ 'documentId',
22
+ 'createdAt',
23
+ 'updatedAt',
24
+ 'updatedBy',
25
+ 'localizations'
26
+ ];
27
+ return {
28
+ // Async to avoid changing the signature later (there will be a db check in the future)
29
+ async isEnabled () {
30
+ // Check if user disabled AI features globally
31
+ const isAIEnabled = strapi.config.get('admin.ai.enabled', true);
32
+ if (!isAIEnabled) {
33
+ return false;
34
+ }
35
+ // Check if the user's license grants access to AI features
36
+ const hasAccess = strapi.ee.features.isEnabled('cms-ai');
37
+ if (!hasAccess) {
38
+ return false;
39
+ }
40
+ const settings = index.getService('settings');
41
+ const aiSettings = await settings.getSettings();
42
+ if (!aiSettings?.aiLocalizations) {
43
+ return false;
44
+ }
45
+ return true;
46
+ },
47
+ /**
48
+ * Checks if there are localizations that need to be generated for the given document,
49
+ * and if so, calls the AI service and saves the results to the database.
50
+ * Works for both single and collection types, on create and update.
51
+ */ async generateDocumentLocalizations ({ model, document }) {
52
+ const isFeatureEnabled = await this.isEnabled();
53
+ if (!isFeatureEnabled) {
54
+ return;
55
+ }
56
+ const schema = strapi.getModel(model);
57
+ const localeService = index.getService('locales');
58
+ // No localizations needed for content types with i18n disabled
59
+ const isLocalizedContentType = index.getService('content-types').isLocalizedContentType(schema);
60
+ if (!isLocalizedContentType) {
61
+ return;
62
+ }
63
+ // Don't trigger localizations if the update is on a derived locale, only do it on the default
64
+ const defaultLocale = await localeService.getDefaultLocale();
65
+ if (document?.locale !== defaultLocale) {
66
+ return;
67
+ }
68
+ const documentId = document.documentId;
69
+ if (!documentId) {
70
+ strapi.log.warn(`AI Localizations: missing documentId for ${schema.uid}`);
71
+ return;
72
+ }
73
+ const localizedRoots = new Set();
74
+ const translateableContent = await utils.traverseEntity(({ key, attribute, parent, path }, { remove })=>{
75
+ if (IGNORED_FIELDS.includes(key)) {
76
+ remove(key);
77
+ return;
78
+ }
79
+ const hasLocalizedOption = attribute && isLocalizedAttribute(attribute);
80
+ if (attribute && UNSUPPORTED_ATTRIBUTE_TYPES.includes(attribute.type)) {
81
+ remove(key);
82
+ return;
83
+ }
84
+ // If this field is localized, keep it (and mark as localized root if component/dz)
85
+ if (hasLocalizedOption) {
86
+ // If it's a component/dynamiczone, add to the set
87
+ if ([
88
+ 'component',
89
+ 'dynamiczone'
90
+ ].includes(attribute.type)) {
91
+ localizedRoots.add(path.raw);
92
+ }
93
+ return; // keep
94
+ }
95
+ if (parent && localizedRoots.has(parent.path.raw)) {
96
+ // If parent exists in the localized roots set, keep it
97
+ // If this is also a component/dz, propagate the localized root flag
98
+ if ([
99
+ 'component',
100
+ 'dynamiczone'
101
+ ].includes(attribute?.type ?? '')) {
102
+ localizedRoots.add(path.raw);
103
+ }
104
+ return; // keep
105
+ }
106
+ // Otherwise, remove the field
107
+ remove(key);
108
+ }, {
109
+ schema,
110
+ getModel: strapi.getModel.bind(strapi)
111
+ }, document);
112
+ if (Object.keys(translateableContent).length === 0) {
113
+ strapi.log.info(`AI Localizations: no translatable content for ${schema.uid} document ${documentId}`);
114
+ return;
115
+ }
116
+ const localesList = await localeService.find();
117
+ const targetLocales = localesList.filter((l)=>l.code !== document.locale).map((l)=>l.code);
118
+ if (targetLocales.length === 0) {
119
+ strapi.log.info(`AI Localizations: no target locales for ${schema.uid} document ${documentId}`);
120
+ return;
121
+ }
122
+ await aiLocalizationJobsService.upsertJobForDocument({
123
+ contentType: model,
124
+ documentId,
125
+ sourceLocale: document.locale,
126
+ targetLocales,
127
+ status: 'processing'
128
+ });
129
+ let token;
130
+ try {
131
+ const tokenData = await strapi.get('ai').getAiToken();
132
+ token = tokenData.token;
133
+ } catch (error) {
134
+ await aiLocalizationJobsService.upsertJobForDocument({
135
+ documentId,
136
+ contentType: model,
137
+ sourceLocale: document.locale,
138
+ targetLocales,
139
+ status: 'failed'
140
+ });
141
+ throw new Error('Failed to retrieve AI token', {
142
+ cause: error instanceof Error ? error : undefined
143
+ });
144
+ }
145
+ /**
146
+ * Provide a schema to the LLM so that we can give it instructions about how to handle each
147
+ * type of attribute. Only keep essential schema data to avoid cluttering the context.
148
+ * Ignore fields that don't need to be localized.
149
+ * TODO: also provide a schema of all the referenced components
150
+ */ const minimalContentTypeSchema = Object.fromEntries(Object.entries(schema.attributes)// eslint-disable-next-line @typescript-eslint/no-unused-vars
151
+ .filter(([_, attr])=>{
152
+ const isLocalized = isLocalizedAttribute(attr);
153
+ const isSupportedType = !UNSUPPORTED_ATTRIBUTE_TYPES.includes(attr.type);
154
+ return isLocalized && isSupportedType;
155
+ }).map(([key, attr])=>{
156
+ const minimalAttribute = {
157
+ type: attr.type
158
+ };
159
+ if (attr.type === 'component') {
160
+ minimalAttribute.repeatable = attr.repeatable ?? false;
161
+ }
162
+ return [
163
+ key,
164
+ minimalAttribute
165
+ ];
166
+ }));
167
+ strapi.log.http('Contacting AI Server for localizations generation');
168
+ const response = await fetch(`${aiServerUrl}/i18n/generate-localizations`, {
169
+ method: 'POST',
170
+ headers: {
171
+ 'Content-Type': 'application/json',
172
+ Authorization: `Bearer ${token}`
173
+ },
174
+ body: JSON.stringify({
175
+ content: translateableContent,
176
+ sourceLocale: document.locale,
177
+ targetLocales,
178
+ contentTypeSchema: minimalContentTypeSchema
179
+ })
180
+ });
181
+ if (!response.ok) {
182
+ strapi.log.error(`AI Localizations request failed: ${response.status} ${response.statusText}`);
183
+ await aiLocalizationJobsService.upsertJobForDocument({
184
+ documentId,
185
+ contentType: model,
186
+ sourceLocale: document.locale,
187
+ targetLocales,
188
+ status: 'failed'
189
+ });
190
+ throw new Error(`AI Localizations request failed: ${response.statusText}`);
191
+ }
192
+ const aiResult = await response.json();
193
+ // Get all media field names dynamically from the schema
194
+ const mediaFields = Object.entries(schema.attributes)// eslint-disable-next-line @typescript-eslint/no-unused-vars
195
+ .filter(([_, attr])=>attr.type === 'media').map(([key])=>key);
196
+ try {
197
+ await Promise.all(aiResult.localizations.map(async (localization)=>{
198
+ const { content, locale } = localization;
199
+ // Fetch the derived locale document
200
+ const derivedDoc = await strapi.documents(model).findOne({
201
+ documentId,
202
+ locale,
203
+ populate: mediaFields
204
+ });
205
+ // Merge AI content and media fields, works only on first level media fields (root level)
206
+ const mergedData = structuredClone(content);
207
+ for (const field of mediaFields){
208
+ // Only copy media if not already set in derived locale
209
+ if (!derivedDoc || !derivedDoc[field]) {
210
+ mergedData[field] = document[field];
211
+ } else {
212
+ mergedData[field] = derivedDoc[field];
213
+ }
214
+ }
215
+ await strapi.documents(model).update({
216
+ documentId,
217
+ locale,
218
+ fields: [],
219
+ data: mergedData
220
+ });
221
+ await aiLocalizationJobsService.upsertJobForDocument({
222
+ documentId,
223
+ contentType: model,
224
+ sourceLocale: document.locale,
225
+ targetLocales,
226
+ status: 'completed'
227
+ });
228
+ }));
229
+ } catch (error) {
230
+ await aiLocalizationJobsService.upsertJobForDocument({
231
+ documentId,
232
+ contentType: model,
233
+ sourceLocale: document.locale,
234
+ targetLocales,
235
+ status: 'failed'
236
+ });
237
+ strapi.log.error('AI Localizations generation failed', error);
238
+ }
239
+ },
240
+ setupMiddleware () {
241
+ strapi.documents.use(async (context, next)=>{
242
+ const result = await next();
243
+ // Only trigger for the allowed actions
244
+ if (![
245
+ 'create',
246
+ 'update'
247
+ ].includes(context.action)) {
248
+ return result;
249
+ }
250
+ // Check if AI localizations are enabled before triggering
251
+ const isEnabled = await this.isEnabled();
252
+ if (!isEnabled) {
253
+ return result;
254
+ }
255
+ // Don't await since localizations should be done in the background without blocking the request
256
+ strapi.plugin('i18n').service('ai-localizations').generateDocumentLocalizations({
257
+ model: context.contentType.uid,
258
+ document: result
259
+ }).catch((error)=>{
260
+ strapi.log.error('AI Localizations generation failed', error);
261
+ });
262
+ return result;
263
+ });
264
+ }
265
+ };
266
+ };
267
+
268
+ exports.createAILocalizationsService = createAILocalizationsService;
269
+ //# sourceMappingURL=ai-localizations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-localizations.js","sources":["../../../server/src/services/ai-localizations.ts"],"sourcesContent":["import type { Core, Modules, Schema, UID } from '@strapi/types';\nimport { traverseEntity } from '@strapi/utils';\nimport { getService } from '../utils';\n\nconst isLocalizedAttribute = (attribute: Schema.Attribute.Attribute | undefined): boolean => {\n return (attribute?.pluginOptions as any)?.i18n?.localized === true;\n};\n\nconst createAILocalizationsService = ({ strapi }: { strapi: Core.Strapi }) => {\n // TODO: add a helper function to get the AI server URL\n const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';\n const aiLocalizationJobsService = getService('ai-localization-jobs');\n\n const UNSUPPORTED_ATTRIBUTE_TYPES: Schema.Attribute.Kind[] = [\n 'media',\n 'relation',\n 'boolean',\n 'enumeration',\n ];\n const IGNORED_FIELDS = [\n 'id',\n 'documentId',\n 'createdAt',\n 'updatedAt',\n 'updatedBy',\n 'localizations',\n ];\n\n return {\n // Async to avoid changing the signature later (there will be a db check in the future)\n async isEnabled() {\n // Check if user disabled AI features globally\n const isAIEnabled = strapi.config.get('admin.ai.enabled', true);\n if (!isAIEnabled) {\n return false;\n }\n\n // Check if the user's license grants access to AI features\n const hasAccess = strapi.ee.features.isEnabled('cms-ai');\n if (!hasAccess) {\n return false;\n }\n\n const settings = getService('settings');\n const aiSettings = await settings.getSettings();\n if (!aiSettings?.aiLocalizations) {\n return false;\n }\n\n return true;\n },\n\n /**\n * Checks if there are localizations that need to be generated for the given document,\n * and if so, calls the AI service and saves the results to the database.\n * Works for both single and collection types, on create and update.\n */\n async generateDocumentLocalizations({\n model,\n document,\n }: {\n model: UID.ContentType;\n document: Modules.Documents.AnyDocument;\n }) {\n const isFeatureEnabled = await this.isEnabled();\n if (!isFeatureEnabled) {\n return;\n }\n\n const schema = strapi.getModel(model);\n const localeService = getService('locales');\n\n // No localizations needed for content types with i18n disabled\n const isLocalizedContentType = getService('content-types').isLocalizedContentType(schema);\n if (!isLocalizedContentType) {\n return;\n }\n\n // Don't trigger localizations if the update is on a derived locale, only do it on the default\n const defaultLocale = await localeService.getDefaultLocale();\n if (document?.locale !== defaultLocale) {\n return;\n }\n\n const documentId = document.documentId;\n\n if (!documentId) {\n strapi.log.warn(`AI Localizations: missing documentId for ${schema.uid}`);\n return;\n }\n\n const localizedRoots = new Set();\n\n const translateableContent = await traverseEntity(\n ({ key, attribute, parent, path }, { remove }) => {\n if (IGNORED_FIELDS.includes(key)) {\n remove(key);\n return;\n }\n const hasLocalizedOption = attribute && isLocalizedAttribute(attribute);\n if (attribute && UNSUPPORTED_ATTRIBUTE_TYPES.includes(attribute.type)) {\n remove(key);\n return;\n }\n\n // If this field is localized, keep it (and mark as localized root if component/dz)\n if (hasLocalizedOption) {\n // If it's a component/dynamiczone, add to the set\n if (['component', 'dynamiczone'].includes(attribute.type)) {\n localizedRoots.add(path.raw);\n }\n return; // keep\n }\n\n if (parent && localizedRoots.has(parent.path.raw)) {\n // If parent exists in the localized roots set, keep it\n // If this is also a component/dz, propagate the localized root flag\n if (['component', 'dynamiczone'].includes(attribute?.type ?? '')) {\n localizedRoots.add(path.raw);\n }\n return; // keep\n }\n\n // Otherwise, remove the field\n remove(key);\n },\n { schema, getModel: strapi.getModel.bind(strapi) },\n document\n );\n\n if (Object.keys(translateableContent).length === 0) {\n strapi.log.info(\n `AI Localizations: no translatable content for ${schema.uid} document ${documentId}`\n );\n return;\n }\n\n const localesList = await localeService.find();\n const targetLocales = localesList\n .filter((l) => l.code !== document.locale)\n .map((l) => l.code);\n\n if (targetLocales.length === 0) {\n strapi.log.info(\n `AI Localizations: no target locales for ${schema.uid} document ${documentId}`\n );\n return;\n }\n\n await aiLocalizationJobsService.upsertJobForDocument({\n contentType: model,\n documentId,\n sourceLocale: document.locale,\n targetLocales,\n status: 'processing',\n });\n\n let token: string;\n try {\n const tokenData = await strapi.get('ai').getAiToken();\n token = tokenData.token;\n } catch (error) {\n await aiLocalizationJobsService.upsertJobForDocument({\n documentId,\n contentType: model,\n sourceLocale: document.locale,\n targetLocales,\n status: 'failed',\n });\n\n throw new Error('Failed to retrieve AI token', {\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n /**\n * Provide a schema to the LLM so that we can give it instructions about how to handle each\n * type of attribute. Only keep essential schema data to avoid cluttering the context.\n * Ignore fields that don't need to be localized.\n * TODO: also provide a schema of all the referenced components\n */\n const minimalContentTypeSchema = Object.fromEntries(\n Object.entries(schema.attributes)\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n .filter(([_, attr]) => {\n const isLocalized = isLocalizedAttribute(attr);\n const isSupportedType = !UNSUPPORTED_ATTRIBUTE_TYPES.includes(attr.type);\n return isLocalized && isSupportedType;\n })\n .map(([key, attr]) => {\n const minimalAttribute = { type: attr.type };\n if (attr.type === 'component') {\n (\n minimalAttribute as Schema.Attribute.Component<`${string}.${string}`, boolean>\n ).repeatable = attr.repeatable ?? false;\n }\n return [key, minimalAttribute];\n })\n );\n\n strapi.log.http('Contacting AI Server for localizations generation');\n const response = await fetch(`${aiServerUrl}/i18n/generate-localizations`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({\n content: translateableContent,\n sourceLocale: document.locale,\n targetLocales,\n contentTypeSchema: minimalContentTypeSchema,\n }),\n });\n\n if (!response.ok) {\n strapi.log.error(\n `AI Localizations request failed: ${response.status} ${response.statusText}`\n );\n\n await aiLocalizationJobsService.upsertJobForDocument({\n documentId,\n contentType: model,\n sourceLocale: document.locale,\n targetLocales,\n status: 'failed',\n });\n\n throw new Error(`AI Localizations request failed: ${response.statusText}`);\n }\n\n const aiResult = await response.json();\n // Get all media field names dynamically from the schema\n const mediaFields = Object.entries(schema.attributes)\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n .filter(([_, attr]) => attr.type === 'media')\n .map(([key]) => key);\n\n try {\n await Promise.all(\n aiResult.localizations.map(async (localization: any) => {\n const { content, locale } = localization;\n\n // Fetch the derived locale document\n const derivedDoc = await strapi.documents(model).findOne({\n documentId,\n locale,\n populate: mediaFields,\n });\n\n // Merge AI content and media fields, works only on first level media fields (root level)\n const mergedData = structuredClone(content);\n for (const field of mediaFields) {\n // Only copy media if not already set in derived locale\n if (!derivedDoc || !derivedDoc[field]) {\n mergedData[field] = document[field];\n } else {\n mergedData[field] = derivedDoc[field];\n }\n }\n\n await strapi.documents(model).update({\n documentId,\n locale,\n fields: [],\n data: mergedData,\n });\n\n await aiLocalizationJobsService.upsertJobForDocument({\n documentId,\n contentType: model,\n sourceLocale: document.locale,\n targetLocales,\n status: 'completed',\n });\n })\n );\n } catch (error) {\n await aiLocalizationJobsService.upsertJobForDocument({\n documentId,\n contentType: model,\n sourceLocale: document.locale,\n targetLocales,\n status: 'failed',\n });\n strapi.log.error('AI Localizations generation failed', error);\n }\n },\n setupMiddleware() {\n strapi.documents.use(async (context, next) => {\n const result = await next();\n\n // Only trigger for the allowed actions\n if (!['create', 'update'].includes(context.action)) {\n return result;\n }\n\n // Check if AI localizations are enabled before triggering\n const isEnabled = await this.isEnabled();\n if (!isEnabled) {\n return result;\n }\n\n // Don't await since localizations should be done in the background without blocking the request\n strapi\n .plugin('i18n')\n .service('ai-localizations')\n .generateDocumentLocalizations({\n model: context.contentType.uid,\n document: result,\n })\n .catch((error: any) => {\n strapi.log.error('AI Localizations generation failed', error);\n });\n\n return result;\n });\n },\n };\n};\n\nexport { createAILocalizationsService };\n"],"names":["isLocalizedAttribute","attribute","pluginOptions","i18n","localized","createAILocalizationsService","strapi","aiServerUrl","process","env","STRAPI_AI_URL","aiLocalizationJobsService","getService","UNSUPPORTED_ATTRIBUTE_TYPES","IGNORED_FIELDS","isEnabled","isAIEnabled","config","get","hasAccess","ee","features","settings","aiSettings","getSettings","aiLocalizations","generateDocumentLocalizations","model","document","isFeatureEnabled","schema","getModel","localeService","isLocalizedContentType","defaultLocale","getDefaultLocale","locale","documentId","log","warn","uid","localizedRoots","Set","translateableContent","traverseEntity","key","parent","path","remove","includes","hasLocalizedOption","type","add","raw","has","bind","Object","keys","length","info","localesList","find","targetLocales","filter","l","code","map","upsertJobForDocument","contentType","sourceLocale","status","token","tokenData","getAiToken","error","Error","cause","undefined","minimalContentTypeSchema","fromEntries","entries","attributes","_","attr","isLocalized","isSupportedType","minimalAttribute","repeatable","http","response","fetch","method","headers","Authorization","body","JSON","stringify","content","contentTypeSchema","ok","statusText","aiResult","json","mediaFields","Promise","all","localizations","localization","derivedDoc","documents","findOne","populate","mergedData","structuredClone","field","update","fields","data","setupMiddleware","use","context","next","result","action","plugin","service","catch"],"mappings":"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"}
@@ -0,0 +1,267 @@
1
+ import { traverseEntity } from '@strapi/utils';
2
+ import { getService } from '../utils/index.mjs';
3
+
4
+ const isLocalizedAttribute = (attribute)=>{
5
+ return attribute?.pluginOptions?.i18n?.localized === true;
6
+ };
7
+ const createAILocalizationsService = ({ strapi })=>{
8
+ // TODO: add a helper function to get the AI server URL
9
+ const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';
10
+ const aiLocalizationJobsService = getService('ai-localization-jobs');
11
+ const UNSUPPORTED_ATTRIBUTE_TYPES = [
12
+ 'media',
13
+ 'relation',
14
+ 'boolean',
15
+ 'enumeration'
16
+ ];
17
+ const IGNORED_FIELDS = [
18
+ 'id',
19
+ 'documentId',
20
+ 'createdAt',
21
+ 'updatedAt',
22
+ 'updatedBy',
23
+ 'localizations'
24
+ ];
25
+ return {
26
+ // Async to avoid changing the signature later (there will be a db check in the future)
27
+ async isEnabled () {
28
+ // Check if user disabled AI features globally
29
+ const isAIEnabled = strapi.config.get('admin.ai.enabled', true);
30
+ if (!isAIEnabled) {
31
+ return false;
32
+ }
33
+ // Check if the user's license grants access to AI features
34
+ const hasAccess = strapi.ee.features.isEnabled('cms-ai');
35
+ if (!hasAccess) {
36
+ return false;
37
+ }
38
+ const settings = getService('settings');
39
+ const aiSettings = await settings.getSettings();
40
+ if (!aiSettings?.aiLocalizations) {
41
+ return false;
42
+ }
43
+ return true;
44
+ },
45
+ /**
46
+ * Checks if there are localizations that need to be generated for the given document,
47
+ * and if so, calls the AI service and saves the results to the database.
48
+ * Works for both single and collection types, on create and update.
49
+ */ async generateDocumentLocalizations ({ model, document }) {
50
+ const isFeatureEnabled = await this.isEnabled();
51
+ if (!isFeatureEnabled) {
52
+ return;
53
+ }
54
+ const schema = strapi.getModel(model);
55
+ const localeService = getService('locales');
56
+ // No localizations needed for content types with i18n disabled
57
+ const isLocalizedContentType = getService('content-types').isLocalizedContentType(schema);
58
+ if (!isLocalizedContentType) {
59
+ return;
60
+ }
61
+ // Don't trigger localizations if the update is on a derived locale, only do it on the default
62
+ const defaultLocale = await localeService.getDefaultLocale();
63
+ if (document?.locale !== defaultLocale) {
64
+ return;
65
+ }
66
+ const documentId = document.documentId;
67
+ if (!documentId) {
68
+ strapi.log.warn(`AI Localizations: missing documentId for ${schema.uid}`);
69
+ return;
70
+ }
71
+ const localizedRoots = new Set();
72
+ const translateableContent = await traverseEntity(({ key, attribute, parent, path }, { remove })=>{
73
+ if (IGNORED_FIELDS.includes(key)) {
74
+ remove(key);
75
+ return;
76
+ }
77
+ const hasLocalizedOption = attribute && isLocalizedAttribute(attribute);
78
+ if (attribute && UNSUPPORTED_ATTRIBUTE_TYPES.includes(attribute.type)) {
79
+ remove(key);
80
+ return;
81
+ }
82
+ // If this field is localized, keep it (and mark as localized root if component/dz)
83
+ if (hasLocalizedOption) {
84
+ // If it's a component/dynamiczone, add to the set
85
+ if ([
86
+ 'component',
87
+ 'dynamiczone'
88
+ ].includes(attribute.type)) {
89
+ localizedRoots.add(path.raw);
90
+ }
91
+ return; // keep
92
+ }
93
+ if (parent && localizedRoots.has(parent.path.raw)) {
94
+ // If parent exists in the localized roots set, keep it
95
+ // If this is also a component/dz, propagate the localized root flag
96
+ if ([
97
+ 'component',
98
+ 'dynamiczone'
99
+ ].includes(attribute?.type ?? '')) {
100
+ localizedRoots.add(path.raw);
101
+ }
102
+ return; // keep
103
+ }
104
+ // Otherwise, remove the field
105
+ remove(key);
106
+ }, {
107
+ schema,
108
+ getModel: strapi.getModel.bind(strapi)
109
+ }, document);
110
+ if (Object.keys(translateableContent).length === 0) {
111
+ strapi.log.info(`AI Localizations: no translatable content for ${schema.uid} document ${documentId}`);
112
+ return;
113
+ }
114
+ const localesList = await localeService.find();
115
+ const targetLocales = localesList.filter((l)=>l.code !== document.locale).map((l)=>l.code);
116
+ if (targetLocales.length === 0) {
117
+ strapi.log.info(`AI Localizations: no target locales for ${schema.uid} document ${documentId}`);
118
+ return;
119
+ }
120
+ await aiLocalizationJobsService.upsertJobForDocument({
121
+ contentType: model,
122
+ documentId,
123
+ sourceLocale: document.locale,
124
+ targetLocales,
125
+ status: 'processing'
126
+ });
127
+ let token;
128
+ try {
129
+ const tokenData = await strapi.get('ai').getAiToken();
130
+ token = tokenData.token;
131
+ } catch (error) {
132
+ await aiLocalizationJobsService.upsertJobForDocument({
133
+ documentId,
134
+ contentType: model,
135
+ sourceLocale: document.locale,
136
+ targetLocales,
137
+ status: 'failed'
138
+ });
139
+ throw new Error('Failed to retrieve AI token', {
140
+ cause: error instanceof Error ? error : undefined
141
+ });
142
+ }
143
+ /**
144
+ * Provide a schema to the LLM so that we can give it instructions about how to handle each
145
+ * type of attribute. Only keep essential schema data to avoid cluttering the context.
146
+ * Ignore fields that don't need to be localized.
147
+ * TODO: also provide a schema of all the referenced components
148
+ */ const minimalContentTypeSchema = Object.fromEntries(Object.entries(schema.attributes)// eslint-disable-next-line @typescript-eslint/no-unused-vars
149
+ .filter(([_, attr])=>{
150
+ const isLocalized = isLocalizedAttribute(attr);
151
+ const isSupportedType = !UNSUPPORTED_ATTRIBUTE_TYPES.includes(attr.type);
152
+ return isLocalized && isSupportedType;
153
+ }).map(([key, attr])=>{
154
+ const minimalAttribute = {
155
+ type: attr.type
156
+ };
157
+ if (attr.type === 'component') {
158
+ minimalAttribute.repeatable = attr.repeatable ?? false;
159
+ }
160
+ return [
161
+ key,
162
+ minimalAttribute
163
+ ];
164
+ }));
165
+ strapi.log.http('Contacting AI Server for localizations generation');
166
+ const response = await fetch(`${aiServerUrl}/i18n/generate-localizations`, {
167
+ method: 'POST',
168
+ headers: {
169
+ 'Content-Type': 'application/json',
170
+ Authorization: `Bearer ${token}`
171
+ },
172
+ body: JSON.stringify({
173
+ content: translateableContent,
174
+ sourceLocale: document.locale,
175
+ targetLocales,
176
+ contentTypeSchema: minimalContentTypeSchema
177
+ })
178
+ });
179
+ if (!response.ok) {
180
+ strapi.log.error(`AI Localizations request failed: ${response.status} ${response.statusText}`);
181
+ await aiLocalizationJobsService.upsertJobForDocument({
182
+ documentId,
183
+ contentType: model,
184
+ sourceLocale: document.locale,
185
+ targetLocales,
186
+ status: 'failed'
187
+ });
188
+ throw new Error(`AI Localizations request failed: ${response.statusText}`);
189
+ }
190
+ const aiResult = await response.json();
191
+ // Get all media field names dynamically from the schema
192
+ const mediaFields = Object.entries(schema.attributes)// eslint-disable-next-line @typescript-eslint/no-unused-vars
193
+ .filter(([_, attr])=>attr.type === 'media').map(([key])=>key);
194
+ try {
195
+ await Promise.all(aiResult.localizations.map(async (localization)=>{
196
+ const { content, locale } = localization;
197
+ // Fetch the derived locale document
198
+ const derivedDoc = await strapi.documents(model).findOne({
199
+ documentId,
200
+ locale,
201
+ populate: mediaFields
202
+ });
203
+ // Merge AI content and media fields, works only on first level media fields (root level)
204
+ const mergedData = structuredClone(content);
205
+ for (const field of mediaFields){
206
+ // Only copy media if not already set in derived locale
207
+ if (!derivedDoc || !derivedDoc[field]) {
208
+ mergedData[field] = document[field];
209
+ } else {
210
+ mergedData[field] = derivedDoc[field];
211
+ }
212
+ }
213
+ await strapi.documents(model).update({
214
+ documentId,
215
+ locale,
216
+ fields: [],
217
+ data: mergedData
218
+ });
219
+ await aiLocalizationJobsService.upsertJobForDocument({
220
+ documentId,
221
+ contentType: model,
222
+ sourceLocale: document.locale,
223
+ targetLocales,
224
+ status: 'completed'
225
+ });
226
+ }));
227
+ } catch (error) {
228
+ await aiLocalizationJobsService.upsertJobForDocument({
229
+ documentId,
230
+ contentType: model,
231
+ sourceLocale: document.locale,
232
+ targetLocales,
233
+ status: 'failed'
234
+ });
235
+ strapi.log.error('AI Localizations generation failed', error);
236
+ }
237
+ },
238
+ setupMiddleware () {
239
+ strapi.documents.use(async (context, next)=>{
240
+ const result = await next();
241
+ // Only trigger for the allowed actions
242
+ if (![
243
+ 'create',
244
+ 'update'
245
+ ].includes(context.action)) {
246
+ return result;
247
+ }
248
+ // Check if AI localizations are enabled before triggering
249
+ const isEnabled = await this.isEnabled();
250
+ if (!isEnabled) {
251
+ return result;
252
+ }
253
+ // Don't await since localizations should be done in the background without blocking the request
254
+ strapi.plugin('i18n').service('ai-localizations').generateDocumentLocalizations({
255
+ model: context.contentType.uid,
256
+ document: result
257
+ }).catch((error)=>{
258
+ strapi.log.error('AI Localizations generation failed', error);
259
+ });
260
+ return result;
261
+ });
262
+ }
263
+ };
264
+ };
265
+
266
+ export { createAILocalizationsService };
267
+ //# sourceMappingURL=ai-localizations.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-localizations.mjs","sources":["../../../server/src/services/ai-localizations.ts"],"sourcesContent":["import type { Core, Modules, Schema, UID } from '@strapi/types';\nimport { traverseEntity } from '@strapi/utils';\nimport { getService } from '../utils';\n\nconst isLocalizedAttribute = (attribute: Schema.Attribute.Attribute | undefined): boolean => {\n return (attribute?.pluginOptions as any)?.i18n?.localized === true;\n};\n\nconst createAILocalizationsService = ({ strapi }: { strapi: Core.Strapi }) => {\n // TODO: add a helper function to get the AI server URL\n const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';\n const aiLocalizationJobsService = getService('ai-localization-jobs');\n\n const UNSUPPORTED_ATTRIBUTE_TYPES: Schema.Attribute.Kind[] = [\n 'media',\n 'relation',\n 'boolean',\n 'enumeration',\n ];\n const IGNORED_FIELDS = [\n 'id',\n 'documentId',\n 'createdAt',\n 'updatedAt',\n 'updatedBy',\n 'localizations',\n ];\n\n return {\n // Async to avoid changing the signature later (there will be a db check in the future)\n async isEnabled() {\n // Check if user disabled AI features globally\n const isAIEnabled = strapi.config.get('admin.ai.enabled', true);\n if (!isAIEnabled) {\n return false;\n }\n\n // Check if the user's license grants access to AI features\n const hasAccess = strapi.ee.features.isEnabled('cms-ai');\n if (!hasAccess) {\n return false;\n }\n\n const settings = getService('settings');\n const aiSettings = await settings.getSettings();\n if (!aiSettings?.aiLocalizations) {\n return false;\n }\n\n return true;\n },\n\n /**\n * Checks if there are localizations that need to be generated for the given document,\n * and if so, calls the AI service and saves the results to the database.\n * Works for both single and collection types, on create and update.\n */\n async generateDocumentLocalizations({\n model,\n document,\n }: {\n model: UID.ContentType;\n document: Modules.Documents.AnyDocument;\n }) {\n const isFeatureEnabled = await this.isEnabled();\n if (!isFeatureEnabled) {\n return;\n }\n\n const schema = strapi.getModel(model);\n const localeService = getService('locales');\n\n // No localizations needed for content types with i18n disabled\n const isLocalizedContentType = getService('content-types').isLocalizedContentType(schema);\n if (!isLocalizedContentType) {\n return;\n }\n\n // Don't trigger localizations if the update is on a derived locale, only do it on the default\n const defaultLocale = await localeService.getDefaultLocale();\n if (document?.locale !== defaultLocale) {\n return;\n }\n\n const documentId = document.documentId;\n\n if (!documentId) {\n strapi.log.warn(`AI Localizations: missing documentId for ${schema.uid}`);\n return;\n }\n\n const localizedRoots = new Set();\n\n const translateableContent = await traverseEntity(\n ({ key, attribute, parent, path }, { remove }) => {\n if (IGNORED_FIELDS.includes(key)) {\n remove(key);\n return;\n }\n const hasLocalizedOption = attribute && isLocalizedAttribute(attribute);\n if (attribute && UNSUPPORTED_ATTRIBUTE_TYPES.includes(attribute.type)) {\n remove(key);\n return;\n }\n\n // If this field is localized, keep it (and mark as localized root if component/dz)\n if (hasLocalizedOption) {\n // If it's a component/dynamiczone, add to the set\n if (['component', 'dynamiczone'].includes(attribute.type)) {\n localizedRoots.add(path.raw);\n }\n return; // keep\n }\n\n if (parent && localizedRoots.has(parent.path.raw)) {\n // If parent exists in the localized roots set, keep it\n // If this is also a component/dz, propagate the localized root flag\n if (['component', 'dynamiczone'].includes(attribute?.type ?? '')) {\n localizedRoots.add(path.raw);\n }\n return; // keep\n }\n\n // Otherwise, remove the field\n remove(key);\n },\n { schema, getModel: strapi.getModel.bind(strapi) },\n document\n );\n\n if (Object.keys(translateableContent).length === 0) {\n strapi.log.info(\n `AI Localizations: no translatable content for ${schema.uid} document ${documentId}`\n );\n return;\n }\n\n const localesList = await localeService.find();\n const targetLocales = localesList\n .filter((l) => l.code !== document.locale)\n .map((l) => l.code);\n\n if (targetLocales.length === 0) {\n strapi.log.info(\n `AI Localizations: no target locales for ${schema.uid} document ${documentId}`\n );\n return;\n }\n\n await aiLocalizationJobsService.upsertJobForDocument({\n contentType: model,\n documentId,\n sourceLocale: document.locale,\n targetLocales,\n status: 'processing',\n });\n\n let token: string;\n try {\n const tokenData = await strapi.get('ai').getAiToken();\n token = tokenData.token;\n } catch (error) {\n await aiLocalizationJobsService.upsertJobForDocument({\n documentId,\n contentType: model,\n sourceLocale: document.locale,\n targetLocales,\n status: 'failed',\n });\n\n throw new Error('Failed to retrieve AI token', {\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n /**\n * Provide a schema to the LLM so that we can give it instructions about how to handle each\n * type of attribute. Only keep essential schema data to avoid cluttering the context.\n * Ignore fields that don't need to be localized.\n * TODO: also provide a schema of all the referenced components\n */\n const minimalContentTypeSchema = Object.fromEntries(\n Object.entries(schema.attributes)\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n .filter(([_, attr]) => {\n const isLocalized = isLocalizedAttribute(attr);\n const isSupportedType = !UNSUPPORTED_ATTRIBUTE_TYPES.includes(attr.type);\n return isLocalized && isSupportedType;\n })\n .map(([key, attr]) => {\n const minimalAttribute = { type: attr.type };\n if (attr.type === 'component') {\n (\n minimalAttribute as Schema.Attribute.Component<`${string}.${string}`, boolean>\n ).repeatable = attr.repeatable ?? false;\n }\n return [key, minimalAttribute];\n })\n );\n\n strapi.log.http('Contacting AI Server for localizations generation');\n const response = await fetch(`${aiServerUrl}/i18n/generate-localizations`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({\n content: translateableContent,\n sourceLocale: document.locale,\n targetLocales,\n contentTypeSchema: minimalContentTypeSchema,\n }),\n });\n\n if (!response.ok) {\n strapi.log.error(\n `AI Localizations request failed: ${response.status} ${response.statusText}`\n );\n\n await aiLocalizationJobsService.upsertJobForDocument({\n documentId,\n contentType: model,\n sourceLocale: document.locale,\n targetLocales,\n status: 'failed',\n });\n\n throw new Error(`AI Localizations request failed: ${response.statusText}`);\n }\n\n const aiResult = await response.json();\n // Get all media field names dynamically from the schema\n const mediaFields = Object.entries(schema.attributes)\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n .filter(([_, attr]) => attr.type === 'media')\n .map(([key]) => key);\n\n try {\n await Promise.all(\n aiResult.localizations.map(async (localization: any) => {\n const { content, locale } = localization;\n\n // Fetch the derived locale document\n const derivedDoc = await strapi.documents(model).findOne({\n documentId,\n locale,\n populate: mediaFields,\n });\n\n // Merge AI content and media fields, works only on first level media fields (root level)\n const mergedData = structuredClone(content);\n for (const field of mediaFields) {\n // Only copy media if not already set in derived locale\n if (!derivedDoc || !derivedDoc[field]) {\n mergedData[field] = document[field];\n } else {\n mergedData[field] = derivedDoc[field];\n }\n }\n\n await strapi.documents(model).update({\n documentId,\n locale,\n fields: [],\n data: mergedData,\n });\n\n await aiLocalizationJobsService.upsertJobForDocument({\n documentId,\n contentType: model,\n sourceLocale: document.locale,\n targetLocales,\n status: 'completed',\n });\n })\n );\n } catch (error) {\n await aiLocalizationJobsService.upsertJobForDocument({\n documentId,\n contentType: model,\n sourceLocale: document.locale,\n targetLocales,\n status: 'failed',\n });\n strapi.log.error('AI Localizations generation failed', error);\n }\n },\n setupMiddleware() {\n strapi.documents.use(async (context, next) => {\n const result = await next();\n\n // Only trigger for the allowed actions\n if (!['create', 'update'].includes(context.action)) {\n return result;\n }\n\n // Check if AI localizations are enabled before triggering\n const isEnabled = await this.isEnabled();\n if (!isEnabled) {\n return result;\n }\n\n // Don't await since localizations should be done in the background without blocking the request\n strapi\n .plugin('i18n')\n .service('ai-localizations')\n .generateDocumentLocalizations({\n model: context.contentType.uid,\n document: result,\n })\n .catch((error: any) => {\n strapi.log.error('AI Localizations generation failed', error);\n });\n\n return result;\n });\n },\n };\n};\n\nexport { createAILocalizationsService };\n"],"names":["isLocalizedAttribute","attribute","pluginOptions","i18n","localized","createAILocalizationsService","strapi","aiServerUrl","process","env","STRAPI_AI_URL","aiLocalizationJobsService","getService","UNSUPPORTED_ATTRIBUTE_TYPES","IGNORED_FIELDS","isEnabled","isAIEnabled","config","get","hasAccess","ee","features","settings","aiSettings","getSettings","aiLocalizations","generateDocumentLocalizations","model","document","isFeatureEnabled","schema","getModel","localeService","isLocalizedContentType","defaultLocale","getDefaultLocale","locale","documentId","log","warn","uid","localizedRoots","Set","translateableContent","traverseEntity","key","parent","path","remove","includes","hasLocalizedOption","type","add","raw","has","bind","Object","keys","length","info","localesList","find","targetLocales","filter","l","code","map","upsertJobForDocument","contentType","sourceLocale","status","token","tokenData","getAiToken","error","Error","cause","undefined","minimalContentTypeSchema","fromEntries","entries","attributes","_","attr","isLocalized","isSupportedType","minimalAttribute","repeatable","http","response","fetch","method","headers","Authorization","body","JSON","stringify","content","contentTypeSchema","ok","statusText","aiResult","json","mediaFields","Promise","all","localizations","localization","derivedDoc","documents","findOne","populate","mergedData","structuredClone","field","update","fields","data","setupMiddleware","use","context","next","result","action","plugin","service","catch"],"mappings":"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"}