@strapi/i18n 0.0.0-experimental.da85533897155e719d784f0271223c866d2f69ab → 0.0.0-experimental.dd1d47ef78ef6cfec4ed62576108500bd9f13740

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 (47) hide show
  1. package/dist/_chunks/{SettingsPage-DW0GwDcD.mjs → SettingsPage-BHvunuIF.mjs} +4 -4
  2. package/dist/_chunks/SettingsPage-BHvunuIF.mjs.map +1 -0
  3. package/dist/_chunks/{SettingsPage-a96ZyFLy.js → SettingsPage-Bcj7380u.js} +4 -4
  4. package/dist/_chunks/SettingsPage-Bcj7380u.js.map +1 -0
  5. package/dist/_chunks/{en-BsOU9o5z.js → en-BKBz3tro.js} +10 -3
  6. package/dist/_chunks/en-BKBz3tro.js.map +1 -0
  7. package/dist/_chunks/{en-CM6Pjfyv.mjs → en-DlXfy6Gy.mjs} +10 -3
  8. package/dist/_chunks/en-DlXfy6Gy.mjs.map +1 -0
  9. package/dist/_chunks/{index-sfNkjx75.js → index-BKZbxhpm.js} +383 -94
  10. package/dist/_chunks/index-BKZbxhpm.js.map +1 -0
  11. package/dist/_chunks/{index-4KJn181Q.mjs → index-DUdrr5PR.mjs} +386 -97
  12. package/dist/_chunks/index-DUdrr5PR.mjs.map +1 -0
  13. package/dist/admin/index.js +1 -1
  14. package/dist/admin/index.mjs +1 -1
  15. package/dist/admin/src/components/BulkLocaleActionModal.d.ts +2 -1
  16. package/dist/admin/src/components/CMHeaderActions.d.ts +29 -3
  17. package/dist/admin/src/components/CreateLocale.d.ts +6 -6
  18. package/dist/admin/src/utils/clean.d.ts +4 -0
  19. package/dist/server/index.js +389 -485
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +391 -487
  22. package/dist/server/index.mjs.map +1 -1
  23. package/dist/server/src/bootstrap.d.ts +1 -4
  24. package/dist/server/src/bootstrap.d.ts.map +1 -1
  25. package/dist/server/src/index.d.ts +7 -11
  26. package/dist/server/src/index.d.ts.map +1 -1
  27. package/dist/server/src/register.d.ts.map +1 -1
  28. package/dist/server/src/services/index.d.ts +6 -8
  29. package/dist/server/src/services/index.d.ts.map +1 -1
  30. package/dist/server/src/services/sanitize/index.d.ts +11 -0
  31. package/dist/server/src/services/sanitize/index.d.ts.map +1 -0
  32. package/dist/server/src/utils/index.d.ts +2 -2
  33. package/dist/server/src/utils/index.d.ts.map +1 -1
  34. package/package.json +10 -10
  35. package/dist/_chunks/SettingsPage-DW0GwDcD.mjs.map +0 -1
  36. package/dist/_chunks/SettingsPage-a96ZyFLy.js.map +0 -1
  37. package/dist/_chunks/en-BsOU9o5z.js.map +0 -1
  38. package/dist/_chunks/en-CM6Pjfyv.mjs.map +0 -1
  39. package/dist/_chunks/index-4KJn181Q.mjs.map +0 -1
  40. package/dist/_chunks/index-sfNkjx75.js.map +0 -1
  41. package/dist/server/src/migrations/content-type/disable/index.d.ts +0 -3
  42. package/dist/server/src/migrations/content-type/disable/index.d.ts.map +0 -1
  43. package/dist/server/src/migrations/content-type/enable/index.d.ts +0 -3
  44. package/dist/server/src/migrations/content-type/enable/index.d.ts.map +0 -1
  45. package/dist/server/src/services/entity-service-decorator.d.ts +0 -29
  46. package/dist/server/src/services/entity-service-decorator.d.ts.map +0 -1
  47. package/strapi-server.js +0 -3
@@ -1,13 +1,13 @@
1
1
  import get from "lodash/get";
2
2
  import * as yup from "yup";
3
- import { jsxs, jsx } from "react/jsx-runtime";
3
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
4
4
  import * as React from "react";
5
5
  import { Typography, Dialog, Field, Checkbox, Flex, Button, Modal, Box, Status, IconButton, Tooltip, SingleSelect, SingleSelectOption, VisuallyHidden, useCollator, Popover } from "@strapi/design-system";
6
- import { WarningCircle, Pencil, CrossCircle, CheckCircle, ArrowsCounterClockwise, Trash, ListPlus, Earth, EarthStriked, CaretDown } from "@strapi/icons";
6
+ import { WarningCircle, Pencil, CrossCircle, CheckCircle, ArrowsCounterClockwise, Trash, Plus, Download, ListPlus, Cross, Earth, EarthStriked, CaretDown } from "@strapi/icons";
7
7
  import { useIntl } from "react-intl";
8
8
  import { styled } from "styled-components";
9
9
  import { skipToken } from "@reduxjs/toolkit/query";
10
- import { useAuth, adminApi, useTable, Table, useQueryParams, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
10
+ import { useAuth, adminApi, useTable, Table, useQueryParams, useForm, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
11
11
  import { unstable_useDocument, unstable_useDocumentActions, buildValidParams } from "@strapi/content-manager/strapi-admin";
12
12
  import { useParams, Link, useNavigate, matchPath } from "react-router-dom";
13
13
  import * as qs from "qs";
@@ -217,10 +217,94 @@ const relationsApi = i18nApi.injectEndpoints({
217
217
  })
218
218
  });
219
219
  const { useGetManyDraftRelationCountQuery } = relationsApi;
220
+ const cleanData = (data, schema, components) => {
221
+ const cleanedData = removeFields(data, [
222
+ "createdAt",
223
+ "createdBy",
224
+ "updatedAt",
225
+ "updatedBy",
226
+ "id",
227
+ "documentId",
228
+ "publishedAt",
229
+ "strapi_stage",
230
+ "strapi_assignee",
231
+ "locale",
232
+ "status"
233
+ ]);
234
+ const cleanedDataWithoutPasswordAndRelation = recursiveRemoveFieldTypes(
235
+ cleanedData,
236
+ schema,
237
+ components,
238
+ ["relation", "password"]
239
+ );
240
+ return cleanedDataWithoutPasswordAndRelation;
241
+ };
242
+ const removeFields = (data, fields) => {
243
+ return Object.keys(data).reduce((acc, current) => {
244
+ if (fields.includes(current)) {
245
+ return acc;
246
+ }
247
+ acc[current] = data[current];
248
+ return acc;
249
+ }, {});
250
+ };
251
+ const recursiveRemoveFieldTypes = (data, schema, components, fields) => {
252
+ return Object.keys(data).reduce((acc, current) => {
253
+ const attribute = schema.attributes[current] ?? { type: void 0 };
254
+ if (fields.includes(attribute.type)) {
255
+ return acc;
256
+ }
257
+ if (attribute.type === "dynamiczone") {
258
+ acc[current] = data[current].map((componentValue, index2) => {
259
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
260
+ componentValue,
261
+ components[componentValue.__component],
262
+ components,
263
+ fields
264
+ );
265
+ return {
266
+ ...rest,
267
+ __temp_key__: index2 + 1
268
+ };
269
+ });
270
+ } else if (attribute.type === "component") {
271
+ const { repeatable, component } = attribute;
272
+ if (repeatable) {
273
+ acc[current] = (data[current] ?? []).map((compoData, index2) => {
274
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
275
+ compoData,
276
+ components[component],
277
+ components,
278
+ fields
279
+ );
280
+ return {
281
+ ...rest,
282
+ __temp_key__: index2 + 1
283
+ };
284
+ });
285
+ } else {
286
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
287
+ data[current] ?? {},
288
+ components[component],
289
+ components,
290
+ fields
291
+ );
292
+ acc[current] = rest;
293
+ }
294
+ } else {
295
+ acc[current] = data[current];
296
+ }
297
+ return acc;
298
+ }, {});
299
+ };
220
300
  const isErrorMessageDescriptor = (object) => {
221
301
  return typeof object === "object" && object !== null && "id" in object && "defaultMessage" in object;
222
302
  };
223
- const EntryValidationText = ({ status = "draft", validationErrors }) => {
303
+ const EntryValidationText = ({
304
+ status = "draft",
305
+ validationErrors,
306
+ action
307
+ }) => {
224
308
  const { formatMessage } = useIntl();
225
309
  const getErrorStr = (key, value) => {
226
310
  if (typeof value === "string") {
@@ -254,30 +338,63 @@ const EntryValidationText = ({ status = "draft", validationErrors }) => {
254
338
  ) })
255
339
  ] });
256
340
  }
257
- if (status === "published") {
258
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
259
- /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
260
- /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
261
- id: "content-manager.bulk-publish.already-published",
262
- defaultMessage: "Already Published"
263
- }) })
264
- ] });
265
- }
266
- if (status === "modified") {
267
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
268
- /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
269
- /* @__PURE__ */ jsx(Typography, { children: formatMessage({
270
- id: "app.utils.ready-to-publish-changes",
271
- defaultMessage: "Ready to publish changes"
272
- }) })
273
- ] });
274
- }
341
+ const getStatusMessage = () => {
342
+ if (action === "bulk-publish") {
343
+ if (status === "published") {
344
+ return {
345
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
346
+ text: formatMessage({
347
+ id: "content-manager.bulk-publish.already-published",
348
+ defaultMessage: "Already Published"
349
+ }),
350
+ textColor: "success600",
351
+ fontWeight: "bold"
352
+ };
353
+ } else if (status === "modified") {
354
+ return {
355
+ icon: /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
356
+ text: formatMessage({
357
+ id: "app.utils.ready-to-publish-changes",
358
+ defaultMessage: "Ready to publish changes"
359
+ })
360
+ };
361
+ } else {
362
+ return {
363
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
364
+ text: formatMessage({
365
+ id: "app.utils.ready-to-publish",
366
+ defaultMessage: "Ready to publish"
367
+ })
368
+ };
369
+ }
370
+ } else {
371
+ if (status === "draft") {
372
+ return {
373
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
374
+ text: formatMessage({
375
+ id: "content-manager.bulk-unpublish.already-unpublished",
376
+ defaultMessage: "Already Unpublished"
377
+ }),
378
+ textColor: "success600",
379
+ fontWeight: "bold"
380
+ };
381
+ } else {
382
+ return {
383
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
384
+ text: formatMessage({
385
+ id: "app.utils.ready-to-unpublish-changes",
386
+ defaultMessage: "Ready to unpublish"
387
+ }),
388
+ textColor: "success600",
389
+ fontWeight: "bold"
390
+ };
391
+ }
392
+ }
393
+ };
394
+ const { icon, text, textColor = "success600", fontWeight = "normal" } = getStatusMessage();
275
395
  return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
276
- /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
277
- /* @__PURE__ */ jsx(Typography, { children: formatMessage({
278
- id: "app.utils.ready-to-publish",
279
- defaultMessage: "Ready to publish"
280
- }) })
396
+ icon,
397
+ /* @__PURE__ */ jsx(Typography, { textColor, fontWeight, children: text })
281
398
  ] });
282
399
  };
283
400
  const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
@@ -285,7 +402,8 @@ const BulkLocaleActionModal = ({
285
402
  headers,
286
403
  rows,
287
404
  localesMetadata,
288
- validationErrors = {}
405
+ validationErrors = {},
406
+ action
289
407
  }) => {
290
408
  const { formatMessage } = useIntl();
291
409
  const selectedRows = useTable(
@@ -298,22 +416,24 @@ const BulkLocaleActionModal = ({
298
416
  return acc;
299
417
  }, {});
300
418
  const localesWithErrors = Object.keys(validationErrors);
301
- const alreadyPublishedCount = selectedRows.filter(
419
+ const publishedCount = selectedRows.filter(
302
420
  ({ locale }) => currentStatusByLocale[locale] === "published"
303
421
  ).length;
304
- const readyToPublishCount = selectedRows.filter(
422
+ const draftCount = selectedRows.filter(
305
423
  ({ locale }) => (currentStatusByLocale[locale] === "draft" || currentStatusByLocale[locale] === "modified") && !localesWithErrors.includes(locale)
306
424
  ).length;
307
425
  const withErrorsCount = localesWithErrors.length;
426
+ const messageId = action === "bulk-publish" ? "content-manager.containers.list.selectedEntriesModal.selectedCount.publish" : "content-manager.containers.list.selectedEntriesModal.selectedCount.unpublish";
427
+ const defaultMessage = action === "bulk-publish" ? "<b>{publishedCount}</b> {publishedCount, plural, =0 {entries} one {entry} other {entries}} already published. <b>{draftCount}</b> {draftCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. <b>{withErrorsCount}</b> {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action." : "<b>{draftCount}</b> {draftCount, plural, =0 {entries} one {entry} other {entries}} already unpublished. <b>{publishedCount}</b> {publishedCount, plural, =0 {entries} one {entry} other {entries}} ready to unpublish.";
308
428
  return formatMessage(
309
429
  {
310
- id: "content-manager.containers.list.selectedEntriesModal.selectedCount",
311
- defaultMessage: "<b>{alreadyPublishedCount}</b> {alreadyPublishedCount, plural, =0 {entries} one {entry} other {entries}} already published. <b>{readyToPublishCount}</b> {readyToPublishCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. <b>{withErrorsCount}</b> {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action."
430
+ id: messageId,
431
+ defaultMessage
312
432
  },
313
433
  {
314
434
  withErrorsCount,
315
- readyToPublishCount,
316
- alreadyPublishedCount,
435
+ draftCount,
436
+ publishedCount,
317
437
  b: BoldChunk
318
438
  }
319
439
  );
@@ -345,7 +465,7 @@ const BulkLocaleActionModal = ({
345
465
  children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
346
466
  }
347
467
  ) }) }),
348
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status }) }),
468
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status, action }) }),
349
469
  /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
350
470
  IconButton,
351
471
  {
@@ -371,6 +491,48 @@ const BulkLocaleActionModal = ({
371
491
  ] }) })
372
492
  ] });
373
493
  };
494
+ const statusVariants = {
495
+ draft: "secondary",
496
+ published: "success",
497
+ modified: "alternative"
498
+ };
499
+ const LocaleOption = ({
500
+ isDraftAndPublishEnabled,
501
+ locale,
502
+ status,
503
+ entryExists
504
+ }) => {
505
+ const { formatMessage } = useIntl();
506
+ if (!entryExists) {
507
+ return formatMessage(
508
+ {
509
+ id: getTranslation("CMEditViewLocalePicker.locale.create"),
510
+ defaultMessage: "Create <bold>{locale}</bold> locale"
511
+ },
512
+ {
513
+ bold: (locale2) => /* @__PURE__ */ jsx("b", { children: locale2 }),
514
+ locale: locale.name
515
+ }
516
+ );
517
+ }
518
+ return /* @__PURE__ */ jsxs(Flex, { width: "100%", gap: 1, justifyContent: "space-between", children: [
519
+ /* @__PURE__ */ jsx(Typography, { children: locale.name }),
520
+ isDraftAndPublishEnabled ? /* @__PURE__ */ jsx(
521
+ Status,
522
+ {
523
+ display: "flex",
524
+ paddingLeft: "6px",
525
+ paddingRight: "6px",
526
+ paddingTop: "2px",
527
+ paddingBottom: "2px",
528
+ showBullet: false,
529
+ size: "S",
530
+ variant: statusVariants[status],
531
+ children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
532
+ }
533
+ ) : null
534
+ ] });
535
+ };
374
536
  const LocalePickerAction = ({
375
537
  document,
376
538
  meta,
@@ -382,7 +544,13 @@ const LocalePickerAction = ({
382
544
  const [{ query }, setQuery] = useQueryParams();
383
545
  const { hasI18n, canCreate, canRead } = useI18n();
384
546
  const { data: locales = [] } = useGetLocalesQuery();
385
- const { schema } = unstable_useDocument({ model, collectionType, documentId });
547
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
548
+ const { schema } = unstable_useDocument({
549
+ model,
550
+ collectionType,
551
+ documentId,
552
+ params: { locale: currentDesiredLocale }
553
+ });
386
554
  const handleSelect = React.useCallback(
387
555
  (value) => {
388
556
  setQuery({
@@ -400,53 +568,47 @@ const LocalePickerAction = ({
400
568
  if (!Array.isArray(locales) || !hasI18n) {
401
569
  return;
402
570
  }
403
- const currentDesiredLocale = query.plugins?.i18n?.locale;
404
571
  const doesLocaleExist = locales.find((loc) => loc.code === currentDesiredLocale);
405
572
  const defaultLocale = locales.find((locale) => locale.isDefault);
406
573
  if (!doesLocaleExist && defaultLocale?.code) {
407
574
  handleSelect(defaultLocale.code);
408
575
  }
409
- }, [handleSelect, hasI18n, locales, query.plugins?.i18n?.locale]);
410
- if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
411
- return null;
412
- }
413
- const currentLocale = query.plugins?.i18n?.locale || locales.find((loc) => loc.isDefault)?.code;
576
+ }, [handleSelect, hasI18n, locales, currentDesiredLocale]);
577
+ const currentLocale = Array.isArray(locales) ? locales.find((locale) => locale.code === currentDesiredLocale) : void 0;
414
578
  const allCurrentLocales = [
415
- { status: getDocumentStatus(document, meta), locale: currentLocale },
579
+ { status: getDocumentStatus(document, meta), locale: currentLocale?.code },
416
580
  ...meta?.availableLocales ?? []
417
581
  ];
582
+ if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
583
+ return null;
584
+ }
418
585
  return {
419
586
  label: formatMessage({
420
587
  id: getTranslation("Settings.locales.modal.locales.label"),
421
588
  defaultMessage: "Locales"
422
589
  }),
423
590
  options: locales.map((locale) => {
591
+ const entryWithLocaleExists = allCurrentLocales.some((doc) => doc.locale === locale.code);
424
592
  const currentLocaleDoc = allCurrentLocales.find(
425
593
  (doc) => "locale" in doc ? doc.locale === locale.code : false
426
594
  );
427
- const status = currentLocaleDoc?.status ?? "draft";
428
- const permissionsToCheck = currentLocaleDoc ? canCreate : canRead;
429
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
595
+ const permissionsToCheck = currentLocaleDoc ? canRead : canCreate;
430
596
  return {
431
597
  disabled: !permissionsToCheck.includes(locale.code),
432
598
  value: locale.code,
433
- label: locale.name,
434
- startIcon: schema?.options?.draftAndPublish ? /* @__PURE__ */ jsx(
435
- Status,
599
+ label: /* @__PURE__ */ jsx(
600
+ LocaleOption,
436
601
  {
437
- display: "flex",
438
- paddingLeft: "6px",
439
- paddingRight: "6px",
440
- paddingTop: "2px",
441
- paddingBottom: "2px",
442
- showBullet: false,
443
- size: "S",
444
- variant: statusVariant,
445
- children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
602
+ isDraftAndPublishEnabled: !!schema?.options?.draftAndPublish,
603
+ locale,
604
+ status: currentLocaleDoc?.status,
605
+ entryExists: entryWithLocaleExists
446
606
  }
447
- ) : null
607
+ ),
608
+ startIcon: !entryWithLocaleExists ? /* @__PURE__ */ jsx(Plus, {}) : null
448
609
  };
449
610
  }),
611
+ customizeContent: () => currentLocale?.name,
450
612
  onSelect: handleSelect,
451
613
  value: currentLocale
452
614
  };
@@ -462,6 +624,99 @@ const getDocumentStatus = (document, meta) => {
462
624
  }
463
625
  return docStatus;
464
626
  };
627
+ const FillFromAnotherLocaleAction = ({
628
+ documentId,
629
+ meta,
630
+ model,
631
+ collectionType
632
+ }) => {
633
+ const { formatMessage } = useIntl();
634
+ const [{ query }] = useQueryParams();
635
+ const { hasI18n } = useI18n();
636
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
637
+ const [localeSelected, setLocaleSelected] = React.useState(null);
638
+ const setValues = useForm("FillFromAnotherLocale", (state) => state.setValues);
639
+ const { getDocument } = unstable_useDocumentActions();
640
+ const { schema, components } = unstable_useDocument({
641
+ model,
642
+ documentId,
643
+ collectionType,
644
+ params: { locale: currentDesiredLocale }
645
+ });
646
+ const { data: locales = [] } = useGetLocalesQuery();
647
+ const availableLocales = Array.isArray(locales) ? locales.filter((locale) => meta?.availableLocales.some((l) => l.locale === locale.code)) : [];
648
+ const fillFromLocale = (onClose) => async () => {
649
+ const response = await getDocument({
650
+ collectionType,
651
+ model,
652
+ documentId,
653
+ params: { locale: localeSelected }
654
+ });
655
+ if (!response || !schema) {
656
+ return;
657
+ }
658
+ const { data } = response;
659
+ const cleanedData = cleanData(data, schema, components);
660
+ setValues(cleanedData);
661
+ onClose();
662
+ };
663
+ if (!hasI18n) {
664
+ return null;
665
+ }
666
+ return {
667
+ type: "icon",
668
+ icon: /* @__PURE__ */ jsx(Download, {}),
669
+ disabled: availableLocales.length === 0,
670
+ label: formatMessage({
671
+ id: getTranslation("CMEditViewCopyLocale.copy-text"),
672
+ defaultMessage: "Fill in from another locale"
673
+ }),
674
+ dialog: {
675
+ type: "dialog",
676
+ title: formatMessage({
677
+ id: getTranslation("CMEditViewCopyLocale.dialog.title"),
678
+ defaultMessage: "Confirmation"
679
+ }),
680
+ content: ({ onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [
681
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
682
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
683
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
684
+ id: getTranslation("CMEditViewCopyLocale.dialog.body"),
685
+ defaultMessage: "Your current content will be erased and filled by the content of the selected locale:"
686
+ }) }),
687
+ /* @__PURE__ */ jsxs(Field.Root, { width: "100%", children: [
688
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
689
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.label"),
690
+ defaultMessage: "Locale"
691
+ }) }),
692
+ /* @__PURE__ */ jsx(
693
+ SingleSelect,
694
+ {
695
+ value: localeSelected,
696
+ placeholder: formatMessage({
697
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.placeholder"),
698
+ defaultMessage: "Select one locale..."
699
+ }),
700
+ onChange: (value) => setLocaleSelected(value),
701
+ children: availableLocales.map((locale) => /* @__PURE__ */ jsx(SingleSelectOption, { value: locale.code, children: locale.name }, locale.code))
702
+ }
703
+ )
704
+ ] })
705
+ ] }) }),
706
+ /* @__PURE__ */ jsx(Dialog.Footer, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
707
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "tertiary", onClick: onClose, children: formatMessage({
708
+ id: getTranslation("CMEditViewCopyLocale.cancel-text"),
709
+ defaultMessage: "No, cancel"
710
+ }) }),
711
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "success", onClick: fillFromLocale(onClose), children: formatMessage({
712
+ id: getTranslation("CMEditViewCopyLocale.submit-text"),
713
+ defaultMessage: "Yes, fill in"
714
+ }) })
715
+ ] }) })
716
+ ] })
717
+ }
718
+ };
719
+ };
465
720
  const DeleteLocaleAction = ({
466
721
  document,
467
722
  documentId,
@@ -473,16 +728,23 @@ const DeleteLocaleAction = ({
473
728
  const { toggleNotification } = useNotification();
474
729
  const { delete: deleteAction } = unstable_useDocumentActions();
475
730
  const { hasI18n, canDelete } = useI18n();
731
+ const [{ query }] = useQueryParams();
732
+ const { data: locales = [] } = useGetLocalesQuery();
733
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
734
+ const locale = !("error" in locales) && locales.find((loc) => loc.code === currentDesiredLocale);
476
735
  if (!hasI18n) {
477
736
  return null;
478
737
  }
479
738
  return {
480
739
  disabled: document?.locale && !canDelete.includes(document.locale) || !document || !document.id,
481
740
  position: ["header", "table-row"],
482
- label: formatMessage({
483
- id: getTranslation("actions.delete.label"),
484
- defaultMessage: "Delete locale"
485
- }),
741
+ label: formatMessage(
742
+ {
743
+ id: getTranslation("actions.delete.label"),
744
+ defaultMessage: "Delete entry ({locale})"
745
+ },
746
+ { locale: locale && locale.name }
747
+ ),
486
748
  icon: /* @__PURE__ */ jsx(StyledTrash, {}),
487
749
  variant: "danger",
488
750
  dialog: {
@@ -499,7 +761,12 @@ const DeleteLocaleAction = ({
499
761
  }) })
500
762
  ] }),
501
763
  onConfirm: async () => {
502
- if (!documentId || !document?.locale) {
764
+ const unableToDelete = (
765
+ // We are unable to delete a collection type without a document ID
766
+ // & unable to delete generally if there is no document locale
767
+ collectionType !== "single-types" && !documentId || !document?.locale
768
+ );
769
+ if (unableToDelete) {
503
770
  console.error(
504
771
  "You're trying to delete a document without an id or locale, this is likely a bug with Strapi. Please open an issue."
505
772
  );
@@ -525,23 +792,24 @@ const DeleteLocaleAction = ({
525
792
  }
526
793
  };
527
794
  };
528
- const BulkLocalePublishAction = ({
795
+ const BulkLocaleAction = ({
529
796
  document: baseDocument,
530
797
  documentId,
531
798
  model,
532
- collectionType
799
+ collectionType,
800
+ action
533
801
  }) => {
534
802
  const baseLocale = baseDocument?.locale ?? null;
535
803
  const [{ query }] = useQueryParams();
536
804
  const params = React.useMemo(() => buildValidParams(query), [query]);
537
- const isPublishedTab = query.status === "published";
805
+ const isOnPublishedTab = query.status === "published";
538
806
  const { formatMessage } = useIntl();
539
807
  const { hasI18n, canPublish } = useI18n();
540
808
  const { toggleNotification } = useNotification();
541
809
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
542
810
  const [selectedRows, setSelectedRows] = React.useState([]);
543
811
  const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
544
- const { publishMany: publishManyAction } = unstable_useDocumentActions();
812
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = unstable_useDocumentActions();
545
813
  const {
546
814
  document,
547
815
  meta: documentMeta,
@@ -557,7 +825,7 @@ const BulkLocalePublishAction = ({
557
825
  }
558
826
  },
559
827
  {
560
- skip: !hasI18n
828
+ skip: !hasI18n || !baseLocale
561
829
  }
562
830
  );
563
831
  const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
@@ -609,12 +877,19 @@ const BulkLocalePublishAction = ({
609
877
  }, {});
610
878
  return [rowsFromMeta, errors];
611
879
  }, [document, documentMeta?.availableLocales, validate]);
612
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
613
- if (selectedRow.status !== "published" && !Object.keys(validationErrors).includes(selectedRow.locale)) {
880
+ const isBulkPublish = action === "bulk-publish";
881
+ const localesForAction = selectedRows.reduce((acc, selectedRow) => {
882
+ const isValidLocale = (
883
+ // Validation errors are irrelevant if we are trying to unpublish
884
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale)
885
+ );
886
+ const shouldAddLocale = isBulkPublish ? selectedRow.status !== "published" && isValidLocale : selectedRow.status !== "draft" && isValidLocale;
887
+ if (shouldAddLocale) {
614
888
  acc.push(selectedRow.locale);
615
889
  }
616
890
  return acc;
617
891
  }, []);
892
+ const enableDraftRelationsCount = false;
618
893
  const {
619
894
  data: draftRelationsCount = 0,
620
895
  isLoading: isDraftRelationsLoading,
@@ -623,10 +898,10 @@ const BulkLocalePublishAction = ({
623
898
  {
624
899
  model,
625
900
  documentIds: [documentId],
626
- locale: localesToPublish
901
+ locale: localesForAction
627
902
  },
628
903
  {
629
- skip: !documentId || localesToPublish.length === 0
904
+ skip: !enableDraftRelationsCount
630
905
  }
631
906
  );
632
907
  React.useEffect(() => {
@@ -652,7 +927,18 @@ const BulkLocalePublishAction = ({
652
927
  documentIds: [documentId],
653
928
  params: {
654
929
  ...params,
655
- locale: localesToPublish
930
+ locale: localesForAction
931
+ }
932
+ });
933
+ setSelectedRows([]);
934
+ };
935
+ const unpublish = async () => {
936
+ await unpublishManyAction({
937
+ model,
938
+ documentIds: [documentId],
939
+ params: {
940
+ ...params,
941
+ locale: localesForAction
656
942
  }
657
943
  });
658
944
  setSelectedRows([]);
@@ -660,14 +946,12 @@ const BulkLocalePublishAction = ({
660
946
  const handleAction = async () => {
661
947
  if (draftRelationsCount > 0) {
662
948
  setIsDraftRelationConfirmationOpen(true);
663
- } else {
949
+ } else if (isBulkPublish) {
664
950
  await publish();
951
+ } else {
952
+ await unpublish();
665
953
  }
666
954
  };
667
- const isUnpublish = document?.status === "published";
668
- if (isUnpublish) {
669
- console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
670
- }
671
955
  if (isDraftRelationConfirmationOpen) {
672
956
  return {
673
957
  label: formatMessage({
@@ -705,18 +989,18 @@ const BulkLocalePublishAction = ({
705
989
  const hasPermission = selectedRows.map(({ locale }) => locale).every((locale) => canPublish.includes(locale));
706
990
  return {
707
991
  label: formatMessage({
708
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
709
- defaultMessage: "Publish Multiple Locales"
992
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
993
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
710
994
  }),
711
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
712
- disabled: isPublishedTab || canPublish.length === 0,
995
+ variant: isBulkPublish ? "secondary" : "danger",
996
+ icon: isBulkPublish ? /* @__PURE__ */ jsx(ListPlus, {}) : /* @__PURE__ */ jsx(Cross, {}),
997
+ disabled: isOnPublishedTab || canPublish.length === 0,
713
998
  position: ["panel"],
714
- variant: "secondary",
715
999
  dialog: {
716
1000
  type: "modal",
717
1001
  title: formatMessage({
718
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
719
- defaultMessage: "Publish Multiple Locales"
1002
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
1003
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
720
1004
  }),
721
1005
  content: () => {
722
1006
  return /* @__PURE__ */ jsx(
@@ -735,7 +1019,8 @@ const BulkLocalePublishAction = ({
735
1019
  validationErrors,
736
1020
  headers,
737
1021
  rows,
738
- localesMetadata
1022
+ localesMetadata,
1023
+ action: action ?? "bulk-publish"
739
1024
  }
740
1025
  )
741
1026
  }
@@ -745,18 +1030,24 @@ const BulkLocalePublishAction = ({
745
1030
  Button,
746
1031
  {
747
1032
  loading: isDraftRelationsLoading,
748
- disabled: !hasPermission || localesToPublish.length === 0,
1033
+ disabled: !hasPermission || localesForAction.length === 0,
749
1034
  variant: "default",
750
1035
  onClick: handleAction,
751
1036
  children: formatMessage({
752
- id: "app.utils.publish",
753
- defaultMessage: "Publish"
1037
+ id: isBulkPublish ? "app.utils.publish" : "app.utils.unpublish",
1038
+ defaultMessage: isBulkPublish ? "Publish" : "Unpublish"
754
1039
  })
755
1040
  }
756
1041
  ) })
757
1042
  }
758
1043
  };
759
1044
  };
1045
+ const BulkLocalePublishAction = (props) => {
1046
+ return BulkLocaleAction({ action: "bulk-publish", ...props });
1047
+ };
1048
+ const BulkLocaleUnpublishAction = (props) => {
1049
+ return BulkLocaleAction({ action: "bulk-unpublish", ...props });
1050
+ };
760
1051
  const StyledTrash = styled(Trash)`
761
1052
  path {
762
1053
  fill: currentColor;
@@ -1108,9 +1399,6 @@ const localeMiddleware = (ctx) => (next) => (permissions) => {
1108
1399
  return next(revisedPermissions);
1109
1400
  };
1110
1401
  const prefixPluginTranslations = (trad, pluginId2) => {
1111
- if (!pluginId2) {
1112
- throw new TypeError("pluginId can't be empty");
1113
- }
1114
1402
  return Object.keys(trad).reduce((acc, current) => {
1115
1403
  acc[`${pluginId2}.${current}`] = trad[current];
1116
1404
  return acc;
@@ -1180,11 +1468,11 @@ const index = {
1180
1468
  },
1181
1469
  id: "internationalization",
1182
1470
  to: "internationalization",
1183
- Component: () => import("./SettingsPage-DW0GwDcD.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1471
+ Component: () => import("./SettingsPage-BHvunuIF.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1184
1472
  permissions: PERMISSIONS.accessMain
1185
1473
  });
1186
1474
  const contentManager = app.getPlugin("content-manager");
1187
- contentManager.apis.addDocumentHeaderAction([LocalePickerAction]);
1475
+ contentManager.apis.addDocumentHeaderAction([LocalePickerAction, FillFromAnotherLocaleAction]);
1188
1476
  contentManager.apis.addDocumentAction((actions) => {
1189
1477
  const indexOfDeleteAction = actions.findIndex((action) => action.type === "delete");
1190
1478
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
@@ -1192,6 +1480,7 @@ const index = {
1192
1480
  });
1193
1481
  contentManager.apis.addDocumentAction((actions) => {
1194
1482
  actions.splice(2, 0, BulkLocalePublishAction);
1483
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1195
1484
  return actions;
1196
1485
  });
1197
1486
  contentManager.injectComponent("listView", "actions", {
@@ -1297,7 +1586,7 @@ const index = {
1297
1586
  async registerTrads({ locales }) {
1298
1587
  const importedTrads = await Promise.all(
1299
1588
  locales.map((locale) => {
1300
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/de.json": () => import("./de-9eCAqqrB.mjs"), "./translations/dk.json": () => import("./dk-2qBjxt-P.mjs"), "./translations/en.json": () => import("./en-CM6Pjfyv.mjs"), "./translations/es.json": () => import("./es-DlmMVaBG.mjs"), "./translations/fr.json": () => import("./fr-3S6ke71d.mjs"), "./translations/ko.json": () => import("./ko-qTjQ8IMw.mjs"), "./translations/pl.json": () => import("./pl-B67TSHqT.mjs"), "./translations/ru.json": () => import("./ru-hagMa57T.mjs"), "./translations/tr.json": () => import("./tr-Dw_jmkG-.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-Dyc-aR-h.mjs"), "./translations/zh.json": () => import("./zh-57YM4amO.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
1589
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/de.json": () => import("./de-9eCAqqrB.mjs"), "./translations/dk.json": () => import("./dk-2qBjxt-P.mjs"), "./translations/en.json": () => import("./en-DlXfy6Gy.mjs"), "./translations/es.json": () => import("./es-DlmMVaBG.mjs"), "./translations/fr.json": () => import("./fr-3S6ke71d.mjs"), "./translations/ko.json": () => import("./ko-qTjQ8IMw.mjs"), "./translations/pl.json": () => import("./pl-B67TSHqT.mjs"), "./translations/ru.json": () => import("./ru-hagMa57T.mjs"), "./translations/tr.json": () => import("./tr-Dw_jmkG-.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-Dyc-aR-h.mjs"), "./translations/zh.json": () => import("./zh-57YM4amO.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
1301
1590
  return {
1302
1591
  data: prefixPluginTranslations(data, pluginId),
1303
1592
  locale
@@ -1323,4 +1612,4 @@ export {
1323
1612
  index as i,
1324
1613
  useCreateLocaleMutation as u
1325
1614
  };
1326
- //# sourceMappingURL=index-4KJn181Q.mjs.map
1615
+ //# sourceMappingURL=index-DUdrr5PR.mjs.map