Kaynağa Gözat

refactor(admin-ui): Refactoring & code cleanup

Michael Bromley 3 yıl önce
ebeveyn
işleme
47c18a1b99
28 değiştirilmiş dosya ile 329 ekleme ve 112 silme
  1. 27 27
      packages/admin-ui/i18n-coverage.json
  2. 2 0
      packages/admin-ui/src/lib/catalog/src/catalog.module.ts
  3. 3 0
      packages/admin-ui/src/lib/catalog/src/components/assign-products-to-channel-dialog/assign-products-to-channel-dialog.component.ts
  4. 1 1
      packages/admin-ui/src/lib/catalog/src/components/assign-to-channel-dialog/assign-to-channel-dialog.component.html
  5. 41 27
      packages/admin-ui/src/lib/catalog/src/components/facet-list/facet-list-bulk-actions.ts
  6. 32 17
      packages/admin-ui/src/lib/catalog/src/components/product-detail/product-detail.component.ts
  7. 79 2
      packages/admin-ui/src/lib/catalog/src/components/product-list/product-list-bulk-actions.ts
  8. 1 1
      packages/admin-ui/src/lib/catalog/src/components/product-variants-list/product-variants-list.component.html
  9. 13 0
      packages/admin-ui/src/lib/core/src/common/utilities/get-channel-code-from-user-status.ts
  10. 24 22
      packages/admin-ui/src/lib/core/src/providers/bulk-action-registry/bulk-action-types.ts
  11. 1 0
      packages/admin-ui/src/lib/core/src/providers/modal/modal.service.ts
  12. 1 1
      packages/admin-ui/src/lib/core/src/shared/components/bulk-action-menu/bulk-action-menu.component.html
  13. 21 9
      packages/admin-ui/src/lib/core/src/shared/components/bulk-action-menu/bulk-action-menu.component.ts
  14. 1 1
      packages/admin-ui/src/lib/core/src/shared/components/language-selector/language-selector.component.html
  15. 1 1
      packages/admin-ui/src/lib/core/src/shared/components/simple-dialog/simple-dialog.component.html
  16. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/cs.json
  17. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/de.json
  18. 9 3
      packages/admin-ui/src/lib/static/i18n-messages/en.json
  19. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/es.json
  20. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/fr.json
  21. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/it.json
  22. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/pl.json
  23. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json
  24. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/pt_PT.json
  25. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/ru.json
  26. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/uk.json
  27. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json
  28. 6 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json

+ 27 - 27
packages/admin-ui/i18n-coverage.json

@@ -1,71 +1,71 @@
 {
-  "generatedOn": "2022-09-26T19:12:40.780Z",
-  "lastCommit": "6ee74e423d52d0f228e0bff5696975282d67fb25",
+  "generatedOn": "2022-09-27T19:19:06.936Z",
+  "lastCommit": "647857ce5a6927afa80efe00182a3c25858eeaea",
   "translationStatus": {
     "cs": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 593,
-      "percentage": 90
+      "percentage": 89
     },
     "de": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 572,
-      "percentage": 87
+      "percentage": 86
     },
     "en": {
-      "tokenCount": 659,
-      "translatedCount": 657,
+      "tokenCount": 665,
+      "translatedCount": 665,
       "percentage": 100
     },
     "es": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 624,
-      "percentage": 95
+      "percentage": 94
     },
     "fr": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 614,
-      "percentage": 93
+      "percentage": 92
     },
     "it": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 622,
       "percentage": 94
     },
     "pl": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 407,
-      "percentage": 62
+      "percentage": 61
     },
     "pt_BR": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 591,
-      "percentage": 90
+      "percentage": 89
     },
     "pt_PT": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 635,
-      "percentage": 96
+      "percentage": 95
     },
     "ru": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 621,
-      "percentage": 94
+      "percentage": 93
     },
     "uk": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 621,
-      "percentage": 94
+      "percentage": 93
     },
     "zh_Hans": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 559,
-      "percentage": 85
+      "percentage": 84
     },
     "zh_Hant": {
-      "tokenCount": 659,
+      "tokenCount": 665,
       "translatedCount": 387,
-      "percentage": 59
+      "percentage": 58
     }
   }
 }

+ 2 - 0
packages/admin-ui/src/lib/catalog/src/catalog.module.ts

@@ -30,6 +30,7 @@ import {
     assignFacetValuesToProductsBulkAction,
     assignProductsToChannelBulkAction,
     deleteProductsBulkAction,
+    removeProductsFromChannelBulkAction,
 } from './components/product-list/product-list-bulk-actions';
 import { ProductListComponent } from './components/product-list/product-list.component';
 import { ProductOptionsEditorComponent } from './components/product-options-editor/product-options-editor.component';
@@ -76,6 +77,7 @@ export class CatalogModule {
     constructor(private bulkActionRegistryService: BulkActionRegistryService) {
         bulkActionRegistryService.registerBulkAction(assignFacetValuesToProductsBulkAction);
         bulkActionRegistryService.registerBulkAction(assignProductsToChannelBulkAction);
+        bulkActionRegistryService.registerBulkAction(removeProductsFromChannelBulkAction);
         bulkActionRegistryService.registerBulkAction(deleteProductsBulkAction);
 
         bulkActionRegistryService.registerBulkAction(assignFacetsToChannelBulkAction);

+ 3 - 0
packages/admin-ui/src/lib/catalog/src/components/assign-products-to-channel-dialog/assign-products-to-channel-dialog.component.ts

@@ -82,6 +82,7 @@ export class AssignProductsToChannelDialogComponent implements OnInit, Dialog<an
                     .subscribe(() => {
                         this.notificationService.success(_('catalog.assign-product-to-channel-success'), {
                             channel: selectedChannel.code,
+                            count: this.productIds.length,
                         });
                         this.resolveWith(true);
                     });
@@ -95,6 +96,8 @@ export class AssignProductsToChannelDialogComponent implements OnInit, Dialog<an
                     .subscribe(() => {
                         this.notificationService.success(_('catalog.assign-variant-to-channel-success'), {
                             channel: selectedChannel.code,
+                            // tslint:disable-next-line:no-non-null-assertion
+                            count: this.productVariantIds!.length,
                         });
                         this.resolveWith(true);
                     });

+ 1 - 1
packages/admin-ui/src/lib/catalog/src/components/assign-to-channel-dialog/assign-to-channel-dialog.component.html

@@ -1,5 +1,5 @@
 <ng-template vdrDialogTitle>
-    {{ 'common.assign-to-channel' | translate }}
+    {{ 'catalog.assign-to-channel' | translate }}
 </ng-template>
 <clr-input-container class="mb4">
     <label>{{ 'common.channel' | translate }}</label>

+ 41 - 27
packages/admin-ui/src/lib/catalog/src/components/facet-list/facet-list-bulk-actions.ts

@@ -13,6 +13,7 @@ import { unique } from '@vendure/common/lib/unique';
 import { EMPTY, of } from 'rxjs';
 import { map, mapTo, switchMap } from 'rxjs/operators';
 
+import { getChannelCodeFromUserStatus } from '../../../../core/src/common/utilities/get-channel-code-from-user-status';
 import { AssignToChannelDialogComponent } from '../assign-to-channel-dialog/assign-to-channel-dialog.component';
 
 export const deleteFacetsBulkAction: BulkAction<GetFacetList.Items, FacetListComponent> = {
@@ -133,7 +134,8 @@ export const assignFacetsToChannelBulkAction: BulkAction<GetFacetList.Items, Fac
             )
             .subscribe(result => {
                 notificationService.success(_('catalog.assign-facets-to-channel-success'), {
-                    channel: result.code,
+                    count: selection.length,
+                    channelCode: result.code,
                 });
                 clearSelection();
             });
@@ -142,7 +144,8 @@ export const assignFacetsToChannelBulkAction: BulkAction<GetFacetList.Items, Fac
 
 export const removeFacetsFromChannelBulkAction: BulkAction<GetFacetList.Items, FacetListComponent> = {
     location: 'facet-list',
-    label: _('common.remove-from-active-channel'),
+    label: _('catalog.remove-from-channel'),
+    getTranslationVars: ({ injector }) => getChannelCodeFromUserStatus(injector.get(DataService)),
     icon: 'layers',
     iconClass: 'is-warning',
     isVisible: ({ injector }) => {
@@ -163,10 +166,14 @@ export const removeFacetsFromChannelBulkAction: BulkAction<GetFacetList.Items, F
         const dataService = injector.get(DataService);
         const notificationService = injector.get(NotificationService);
 
+        const activeChannelId$ = dataService.client
+            .userStatus()
+            .mapSingle(({ userStatus }) => userStatus.activeChannelId);
+
         function showModalAndDelete(facetIds: string[], message?: string) {
             return modalService
                 .dialog({
-                    title: _('catalog.confirm-remove-facets-from-channel'),
+                    title: _('catalog.remove-from-channel'),
                     translationVars: {
                         count: selection.length,
                     },
@@ -184,22 +191,19 @@ export const removeFacetsFromChannelBulkAction: BulkAction<GetFacetList.Items, F
                 .pipe(
                     switchMap(res =>
                         res
-                            ? dataService.client
-                                  .userStatus()
-                                  .mapSingle(({ userStatus }) => userStatus.activeChannelId)
-                                  .pipe(
-                                      switchMap(activeChannelId =>
-                                          activeChannelId
-                                              ? dataService.facet.removeFacetsFromChannel({
-                                                    channelId: activeChannelId,
-                                                    facetIds,
-                                                    force: !!message,
-                                                })
-                                              : EMPTY,
-                                      ),
-                                      map(res2 => res2.removeFacetsFromChannel),
-                                  )
-                            : of([]),
+                            ? activeChannelId$.pipe(
+                                  switchMap(activeChannelId =>
+                                      activeChannelId
+                                          ? dataService.facet.removeFacetsFromChannel({
+                                                channelId: activeChannelId,
+                                                facetIds,
+                                                force: !!message,
+                                            })
+                                          : EMPTY,
+                                  ),
+                                  map(res2 => res2.removeFacetsFromChannel),
+                              )
+                            : EMPTY,
                     ),
                 );
         }
@@ -207,37 +211,47 @@ export const removeFacetsFromChannelBulkAction: BulkAction<GetFacetList.Items, F
         showModalAndDelete(unique(selection.map(f => f.id)))
             .pipe(
                 switchMap(result => {
-                    let removedCount = 0;
+                    let removedCount = selection.length;
                     const errors: string[] = [];
                     const errorIds: string[] = [];
                     let i = 0;
                     for (const item of result) {
-                        if (item.__typename === 'Facet') {
-                            removedCount++;
-                        } else if (item.__typename === 'FacetInUseError') {
+                        if (item.__typename === 'FacetInUseError') {
                             errors.push(item.message);
                             errorIds.push(selection[i]?.id);
+                            removedCount--;
                         }
                         i++;
                     }
                     if (0 < errorIds.length) {
                         return showModalAndDelete(errorIds, errors.join('\n')).pipe(
                             map(result2 => {
-                                const deletedCount2 = result2.filter(r => r.__typename === 'Facet').length;
-                                return removedCount + deletedCount2;
+                                const notRemovedCount = result2.filter(
+                                    r => r.__typename === 'FacetInUseError',
+                                ).length;
+                                return selection.length - notRemovedCount;
                             }),
                         );
                     } else {
                         return of(removedCount);
                     }
                 }),
+                switchMap(removedCount =>
+                    removedCount
+                        ? getChannelCodeFromUserStatus(dataService).then(({ channelCode }) => ({
+                              channelCode,
+                              removedCount,
+                          }))
+                        : EMPTY,
+                ),
             )
-            .subscribe(removedCount => {
+            .subscribe(({ removedCount, channelCode }) => {
                 if (removedCount) {
                     hostComponent.refresh();
                     clearSelection();
-                    notificationService.success(_('common.notify-remove-facets-from-channel-success'), {
+                    notificationService.success(_('catalog.notify-remove-facets-from-channel-success'), {
                         count: removedCount,
+                        channelCode,
                     });
                 }
             });

+ 32 - 17
packages/admin-ui/src/lib/catalog/src/components/product-detail/product-detail.component.ts

@@ -35,7 +35,7 @@ import { normalizeString } from '@vendure/common/lib/normalize-string';
 import { DEFAULT_CHANNEL_CODE } from '@vendure/common/lib/shared-constants';
 import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
 import { unique } from '@vendure/common/lib/unique';
-import { BehaviorSubject, combineLatest, EMPTY, merge, Observable } from 'rxjs';
+import { BehaviorSubject, combineLatest, EMPTY, from, merge, Observable } from 'rxjs';
 import {
     debounceTime,
     distinctUntilChanged,
@@ -51,6 +51,7 @@ import {
     tap,
     withLatestFrom,
 } from 'rxjs/operators';
+import { getChannelCodeFromUserStatus } from '../../../../core/src/common/utilities/get-channel-code-from-user-status';
 
 import { ProductDetailService } from '../../providers/product-detail/product-detail.service';
 import { ApplyFacetDialogComponent } from '../apply-facet-dialog/apply-facet-dialog.component';
@@ -292,15 +293,22 @@ export class ProductDetailComponent
     }
 
     removeFromChannel(channelId: string) {
-        this.modalService
-            .dialog({
-                title: _('catalog.remove-product-from-channel'),
-                buttons: [
-                    { type: 'secondary', label: _('common.cancel') },
-                    { type: 'danger', label: _('catalog.remove-from-channel'), returnValue: true },
-                ],
-            })
+        from(getChannelCodeFromUserStatus(this.dataService, channelId))
             .pipe(
+                switchMap(({ channelCode }) => {
+                    return this.modalService.dialog({
+                        title: _('catalog.remove-product-from-channel'),
+                        buttons: [
+                            { type: 'secondary', label: _('common.cancel') },
+                            {
+                                type: 'danger',
+                                label: _('catalog.remove-from-channel'),
+                                translationVars: { channelCode },
+                                returnValue: true,
+                            },
+                        ],
+                    });
+                }),
                 switchMap(response =>
                     response
                         ? this.dataService.product.removeProductsFromChannel({
@@ -340,15 +348,22 @@ export class ProductDetailComponent
         channelId: string;
         variant: ProductVariant.Fragment;
     }) {
-        this.modalService
-            .dialog({
-                title: _('catalog.remove-product-variant-from-channel'),
-                buttons: [
-                    { type: 'secondary', label: _('common.cancel') },
-                    { type: 'danger', label: _('catalog.remove-from-channel'), returnValue: true },
-                ],
-            })
+        from(getChannelCodeFromUserStatus(this.dataService, channelId))
             .pipe(
+                switchMap(({ channelCode }) => {
+                    return this.modalService.dialog({
+                        title: _('catalog.remove-product-variant-from-channel'),
+                        buttons: [
+                            { type: 'secondary', label: _('common.cancel') },
+                            {
+                                type: 'danger',
+                                label: _('catalog.remove-from-channel'),
+                                translationVars: { channelCode },
+                                returnValue: true,
+                            },
+                        ],
+                    });
+                }),
                 switchMap(response =>
                     response
                         ? this.dataService.product.removeVariantsFromChannel({

+ 79 - 2
packages/admin-ui/src/lib/catalog/src/components/product-list/product-list-bulk-actions.ts

@@ -3,16 +3,20 @@ import {
     BulkAction,
     DataService,
     DeletionResult,
+    GetFacetList,
     ModalService,
     NotificationService,
     SearchProducts,
 } from '@vendure/admin-ui/core';
+import { DEFAULT_CHANNEL_CODE } from '@vendure/common/lib/shared-constants';
 import { unique } from '@vendure/common/lib/unique';
-import { EMPTY } from 'rxjs';
-import { switchMap } from 'rxjs/operators';
+import { EMPTY, from, of } from 'rxjs';
+import { map, mapTo, switchMap } from 'rxjs/operators';
+import { getChannelCodeFromUserStatus } from '../../../../core/src/common/utilities/get-channel-code-from-user-status';
 
 import { AssignProductsToChannelDialogComponent } from '../assign-products-to-channel-dialog/assign-products-to-channel-dialog.component';
 import { BulkAddFacetValuesDialogComponent } from '../bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component';
+import { FacetListComponent } from '../facet-list/facet-list.component';
 
 import { ProductListComponent } from './product-list.component';
 
@@ -99,6 +103,79 @@ export const assignProductsToChannelBulkAction: BulkAction<SearchProducts.Items,
     },
 };
 
+export const removeProductsFromChannelBulkAction: BulkAction<SearchProducts.Items, ProductListComponent> = {
+    location: 'product-list',
+    label: _('catalog.remove-from-channel'),
+    getTranslationVars: ({ injector }) => getChannelCodeFromUserStatus(injector.get(DataService)),
+    icon: 'layers',
+    iconClass: 'is-warning',
+    isVisible: ({ injector }) => {
+        return injector
+            .get(DataService)
+            .client.userStatus()
+            .mapSingle(({ userStatus }) => {
+                if (userStatus.channels.length === 1) {
+                    return false;
+                }
+                const defaultChannelId = userStatus.channels.find(c => c.code === DEFAULT_CHANNEL_CODE)?.id;
+                return userStatus.activeChannelId !== defaultChannelId;
+            })
+            .toPromise();
+    },
+    onClick: ({ injector, selection, hostComponent, clearSelection }) => {
+        const modalService = injector.get(ModalService);
+        const dataService = injector.get(DataService);
+        const notificationService = injector.get(NotificationService);
+        const activeChannelId$ = dataService.client
+            .userStatus()
+            .mapSingle(({ userStatus }) => userStatus.activeChannelId);
+
+        from(getChannelCodeFromUserStatus(injector.get(DataService)))
+            .pipe(
+                switchMap(({ channelCode }) =>
+                    modalService.dialog({
+                        title: _('catalog.remove-from-channel'),
+                        translationVars: {
+                            channelCode,
+                        },
+                        buttons: [
+                            { type: 'secondary', label: _('common.cancel') },
+                            {
+                                type: 'danger',
+                                label: _('common.remove'),
+                                returnValue: true,
+                            },
+                        ],
+                    }),
+                ),
+                switchMap(res =>
+                    res
+                        ? activeChannelId$.pipe(
+                              switchMap(activeChannelId =>
+                                  activeChannelId
+                                      ? dataService.product.removeProductsFromChannel({
+                                            channelId: activeChannelId,
+                                            productIds: selection.map(p => p.productId),
+                                        })
+                                      : EMPTY,
+                              ),
+                              mapTo(true),
+                          )
+                        : of(false),
+                ),
+            )
+            .subscribe(removed => {
+                if (removed) {
+                    clearSelection();
+                    notificationService.success(_('common.notify-remove-products-from-channel-success'), {
+                        count: selection.length,
+                    });
+                    setTimeout(() => hostComponent.refresh(), 1000);
+                }
+            });
+    },
+};
+
 export const assignFacetValuesToProductsBulkAction: BulkAction<SearchProducts.Items, ProductListComponent> = {
     location: 'product-list',
     label: _('catalog.edit-facet-values'),

+ 1 - 1
packages/admin-ui/src/lib/catalog/src/components/product-variants-list/product-variants-list.component.html

@@ -281,7 +281,7 @@
                             <vdr-chip
                                 *ngIf="!isDefaultChannel(channel.code)"
                                 icon="times-circle"
-                                [title]="'catalog.remove-from-channel' | translate"
+                                [title]="'catalog.remove-from-channel' | translate: { channelCode: channel.code }"
                                 (iconClick)="
                                     removeFromChannel.emit({ channelId: channel.id, variant: variant })
                                 "

+ 13 - 0
packages/admin-ui/src/lib/core/src/common/utilities/get-channel-code-from-user-status.ts

@@ -0,0 +1,13 @@
+import { DataService } from '../../data/providers/data.service';
+
+export function getChannelCodeFromUserStatus(dataService: DataService, channelId?: string) {
+    return dataService.client
+        .userStatus()
+        .mapSingle(({ userStatus }) => {
+            const channelCode =
+                userStatus.channels.find(c => c.id === (channelId ?? userStatus.activeChannelId))?.code ??
+                'undefined';
+            return { channelCode };
+        })
+        .toPromise();
+}

+ 24 - 22
packages/admin-ui/src/lib/core/src/providers/bulk-action-registry/bulk-action-types.ts

@@ -11,7 +11,16 @@ import { ActivatedRoute } from '@angular/router';
  */
 export type BulkActionLocationId = 'product-list' | 'facet-list' | 'order-list' | string;
 
-export interface BulkActionIsVisibleContext<ItemType, ComponentType> {
+/**
+ * @description
+ * This is the argument which gets passed to the `getTranslationVars` and `isVisible` functions
+ * of the BulkAction definition.
+ *
+ * @since 1.8.0
+ * @docsCategory bulk-actions
+ * @docsPage BulkAction
+ */
+export interface BulkActionFunctionContext<ItemType, ComponentType> {
     /**
      * @description
      * An array of the selected items from the list.
@@ -42,33 +51,18 @@ export interface BulkActionIsVisibleContext<ItemType, ComponentType> {
  * @docsCategory bulk-actions
  * @docsPage BulkAction
  */
-export interface BulkActionClickContext<ItemType, ComponentType> {
-    /**
-     * @description
-     * An array of the selected items from the list.
-     */
-    selection: ItemType[];
+export interface BulkActionClickContext<ItemType, ComponentType>
+    extends BulkActionFunctionContext<ItemType, ComponentType> {
     /**
      * @description
-     * The component instance that is hosting the list view. For instance,
-     * `ProductListComponent`. This can be used to call methods on the instance,
-     * e.g. calling `hostComponent.refresh()` to force a list refresh after
-     * deleting the selected items.
-     */
-    hostComponent: ComponentType;
-    /**
-     * @description
-     * The Angular [Injector](https://angular.io/api/core/Injector) which can be used
-     * to get service instances which might be needed in the click handler.
+     * Clears the selection in the active list view.
      */
-    injector: Injector;
+    clearSelection: () => void;
     /**
      * @description
-     * Clears the selection in the active list view.
+     * The click event itself.
      */
-    clearSelection: () => void;
     event: MouseEvent;
-    route: ActivatedRoute;
 }
 
 /**
@@ -85,6 +79,14 @@ export interface BulkActionClickContext<ItemType, ComponentType> {
 export interface BulkAction<ItemType = any, ComponentType = any> {
     location: BulkActionLocationId;
     label: string;
+    /**
+     * @description
+     * An optional function that should resolve to a map of translation variables which can be
+     * used when translating the `label` string.
+     */
+    getTranslationVars?: (
+        context: BulkActionFunctionContext<ItemType, ComponentType>,
+    ) => Record<string, string | number> | Promise<Record<string, string | number>>;
     /**
      * @description
      * A valid [Clarity Icons](https://clarity.design/icons) icon shape, e.g.
@@ -115,7 +117,7 @@ export interface BulkAction<ItemType = any, ComponentType = any> {
      * This function will be invoked each time the selection is changed, so try to avoid expensive code
      * running here.
      */
-    isVisible?: (context: BulkActionIsVisibleContext<ItemType, ComponentType>) => boolean | Promise<boolean>;
+    isVisible?: (context: BulkActionFunctionContext<ItemType, ComponentType>) => boolean | Promise<boolean>;
     /**
      * @description
      * Control the display of this item based on the user permissions.

+ 1 - 0
packages/admin-ui/src/lib/core/src/providers/modal/modal.service.ts

@@ -28,6 +28,7 @@ export interface Dialog<R = any> {
 export interface DialogButtonConfig<T> {
     label: string;
     type: 'secondary' | 'primary' | 'danger';
+    translationVars?: Record<string, string | number>;
     returnValue?: T;
 }
 

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/bulk-action-menu/bulk-action-menu.component.html

@@ -17,7 +17,7 @@
                     [attr.shape]="action.icon"
                     [ngClass]="action.iconClass || ''"
                 ></clr-icon>
-                {{ action.label | translate }}
+                {{ action.label | translate: action.translationVars }}
             </button>
         </ng-container>
     </vdr-dropdown-menu>

+ 21 - 9
packages/admin-ui/src/lib/core/src/shared/components/bulk-action-menu/bulk-action-menu.component.ts

@@ -14,7 +14,11 @@ import { switchMap } from 'rxjs/operators';
 import { SelectionManager } from '../../../common/utilities/selection-manager';
 import { DataService } from '../../../data/providers/data.service';
 import { BulkActionRegistryService } from '../../../providers/bulk-action-registry/bulk-action-registry.service';
-import { BulkAction, BulkActionLocationId } from '../../../providers/bulk-action-registry/bulk-action-types';
+import {
+    BulkAction,
+    BulkActionFunctionContext,
+    BulkActionLocationId,
+} from '../../../providers/bulk-action-registry/bulk-action-types';
 
 @Component({
     selector: 'vdr-bulk-action-menu',
@@ -26,7 +30,9 @@ export class BulkActionMenuComponent<T = any> implements OnInit, OnDestroy {
     @Input() locationId: BulkActionLocationId;
     @Input() selectionManager: SelectionManager<T>;
     @Input() hostComponent: any;
-    actions$: Observable<Array<BulkAction<T> & { display: boolean }>>;
+    actions$: Observable<
+        Array<BulkAction<T> & { display: boolean; translationVars: Record<string, string | number> }>
+    >;
     userPermissions: string[] = [];
 
     private subscription: Subscription;
@@ -46,16 +52,22 @@ export class BulkActionMenuComponent<T = any> implements OnInit, OnDestroy {
                 return Promise.all(
                     actionsForLocation.map(async action => {
                         let display = true;
+                        let translationVars = {};
                         const isVisibleFn = action.isVisible;
+                        const getTranslationVarsFn = action.getTranslationVars;
+                        const functionContext: BulkActionFunctionContext<T, any> = {
+                            injector: this.injector,
+                            hostComponent: this.hostComponent,
+                            route: this.route,
+                            selection,
+                        };
                         if (typeof isVisibleFn === 'function') {
-                            display = await isVisibleFn({
-                                injector: this.injector,
-                                hostComponent: this.hostComponent,
-                                route: this.route,
-                                selection,
-                            });
+                            display = await isVisibleFn(functionContext);
+                        }
+                        if (typeof getTranslationVarsFn === 'function') {
+                            translationVars = await getTranslationVarsFn(functionContext);
                         }
-                        return { ...action, display };
+                        return { ...action, display, translationVars };
                     }),
                 );
             }),

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/language-selector/language-selector.component.html

@@ -12,7 +12,7 @@
                 (click)="languageCodeChange.emit(code)"
                 vdrDropdownItem
             >
-                {{ code | localeLanguageName }} <span class="code">{{ code }}</span>
+                <span>{{ code | localeLanguageName }}</span> <span class="code ml2">{{ code }}</span>
             </button>
         </vdr-dropdown-menu>
     </vdr-dropdown>

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/components/simple-dialog/simple-dialog.component.html

@@ -8,7 +8,7 @@
             [class.btn-danger]="button.type === 'danger'"
             (click)="resolveWith(button.returnValue)"
         >
-            {{ button.label | translate:translationVars }}
+            {{ button.label | translate: (button.translationVars || {}) }}
         </button>
     </ng-container>
 </ng-template>

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/cs.json

@@ -56,6 +56,7 @@
     "add-facets": "Přidat atribut",
     "add-option": "Přidat možnost",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "Produkt byl úspěšně přiřazen do \"{ channel }\"",
     "assign-products-to-channel": "Přiřadit produkty do kanálu",
     "assign-to-channel": "Přiřadit do kanálu",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "Automaticky aktualizovat jména variant",
     "channel-price-preview": "Náhled ceny v kanálu",
     "collection-contents": "Obsah kolekce",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "Smazat administrátora?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "Žádný kanál nevybrán",
     "no-featured-asset": "Žádné zvýrazněné médium",
     "no-selection": "Žádný výběr",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "Produkt se nepovedlo odebrat z kanálu",
     "notify-remove-product-from-channel-success": "Produkt byl úspěšně odebrán z kanálu",
     "notify-remove-variant-from-channel-error": "Variantu se nepovedlo odebrat z kanálu",
@@ -210,6 +213,8 @@
     "expand-entries": "Otevřít vstupy",
     "extension-running-in-separate-window": "Rozšíření běží v novém okně",
     "filter": "",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "Host",
     "items-per-page-option": "{ count } na stránku",
@@ -235,6 +240,7 @@
     "notify-create-success": "Vytvořeno: { entity }",
     "notify-delete-error": "Vyskytla se chyba, nebylo smazáno: { entity }",
     "notify-delete-success": "Smazáno: { entity }",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "Vyskytla se chyba, nebylo možné uložit změny",
     "notify-saved-changes": "Změny uloženy",
     "notify-update-error": "Vyskytla se chyba, nebylo aktualizováno: { entity }",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/de.json

@@ -56,6 +56,7 @@
     "add-facets": "Facetten hinzufügen",
     "add-option": "Option hinzufügen",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "Produkt erfolgreich an \"{ channel }\" zugewiesen",
     "assign-products-to-channel": "Produkte dem Kanal zuweisen",
     "assign-to-channel": "Zuweisung an Kanal",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "Automatisch Namen der Produktvariante aktualisieren",
     "channel-price-preview": "Kanal-Preisvorschau",
     "collection-contents": "Inhalt der Sammlung",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "Administrator löschen?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "Kein Kanal ausgewählt",
     "no-featured-asset": "Kein \"Featured Asset\"",
     "no-selection": "Keine Auswahl",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "Das Produkt konnte nicht aus dem Kanal entfernt werden",
     "notify-remove-product-from-channel-success": "Das Produkt wurde erfolgreich aus dem Kanal entfernt",
     "notify-remove-variant-from-channel-error": "Die Produktvariante konnte nicht aus dem Kanal entfernt werden",
@@ -210,6 +213,8 @@
     "expand-entries": "Einträge erweitern",
     "extension-running-in-separate-window": "Die Erweiterung läuft in einem separaten Fenster",
     "filter": "Filter",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "Gast",
     "items-per-page-option": "{ count } pro Seite",
@@ -235,6 +240,7 @@
     "notify-create-success": "{ entity } erstellt",
     "notify-delete-error": "Ein Fehler ist aufgetreten, { entity } konnte nicht gelöscht werden",
     "notify-delete-success": "{ entity } gelöscht",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "Ein Fehler ist aufgetreten, die Änderungen konnten nicht gespeichert werden",
     "notify-saved-changes": "Änderungen gespeichert",
     "notify-update-error": "Ein Fehler ist aufgetreten, { entity } konnte nicht aktualisiert werden",

+ 9 - 3
packages/admin-ui/src/lib/static/i18n-messages/en.json

@@ -56,16 +56,18 @@
     "add-facets": "Add facets",
     "add-option": "Add option",
     "asset-preview-links": "Asset preview links",
-    "assign-product-to-channel-success": "Successfully assigned product to \"{ channel }\"",
+    "assign-facets-to-channel-success": "Successfully assigned {count, plural, one {1 facet} other {{count} facets}} to { channelCode }",
+    "assign-product-to-channel-success": "Successfully assigned {count, plural, one {1 product} other {{count} products}} to { channel }",
     "assign-products-to-channel": "Assign products to channel",
     "assign-to-channel": "Assign to channel",
     "assign-to-named-channel": "Assign to { channelCode }",
-    "assign-variant-to-channel-success": "Successfully assigned product variant to \"{ channel }\"",
+    "assign-variant-to-channel-success": "Successfully assigned {count, plural, one {1 product variant} other {{count} product variants}} to { channel }",
     "assign-variants-to-channel": "Assign product variants to channel",
     "auto-update-option-variant-name": "Automatically update the names of ProductVariants using this option",
     "auto-update-product-variant-name": "Automatically update the names of ProductVariants",
     "channel-price-preview": "Channel price preview",
     "collection-contents": "Collection contents",
+    "confirm-bulk-delete-facets": "Delete {count} facets?",
     "confirm-bulk-delete-products": "Delete {count} products?",
     "confirm-cancel": "Cancel?",
     "confirm-delete-administrator": "Delete administrator?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "No channel selected",
     "no-featured-asset": "No featured asset",
     "no-selection": "No selection",
+    "notify-remove-facets-from-channel-success": "Successfully removed {count, plural, one {1 facet} other {{count} facets}} from { channelCode }",
     "notify-remove-product-from-channel-error": "Could not remove product from channel",
     "notify-remove-product-from-channel-success": "Successfully removed product from channel",
     "notify-remove-variant-from-channel-error": "Could not remove product variant from channel",
@@ -135,7 +138,7 @@
     "reindex-error": "An error occurred while rebuilding search index",
     "reindex-successful": "Indexed {count, plural, one {product variant} other {{count} product variants}} in {time}ms",
     "reindexing": "Rebuilding search index",
-    "remove-from-channel": "Remove from channel",
+    "remove-from-channel": "Remove from {channelCode, select, undefined{channel} other{{channelCode}}}",
     "remove-option": "Remove option",
     "remove-product-from-channel": "Remove product from channel",
     "remove-product-variant-from-channel": "Remove product variant from channel",
@@ -210,6 +213,8 @@
     "expand-entries": "Expand entries",
     "extension-running-in-separate-window": "Extension is running in a separate window",
     "filter": "Filter",
+    "force-delete": "Force delete",
+    "force-remove": "Force remove",
     "general": "General",
     "guest": "Guest",
     "items-per-page-option": "{ count } per page",
@@ -235,6 +240,7 @@
     "notify-create-success": "Created new { entity }",
     "notify-delete-error": "An error occurred, could not delete { entity }",
     "notify-delete-success": "Deleted { entity }",
+    "notify-remove-products-from-channel-success": "Successfully removed { count } products from channel",
     "notify-save-changes-error": "An error occurred, could not save changes",
     "notify-saved-changes": "Saved changes",
     "notify-update-error": "An error occurred, could not update { entity }",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/es.json

@@ -56,6 +56,7 @@
     "add-facets": "Añadir facetas",
     "add-option": "Añadir opción",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "Producto asignado a \"{ channel }\" con éxito",
     "assign-products-to-channel": "Asignar productos a canal de ventas",
     "assign-to-channel": "Asignar a canal de ventas",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "Actualiza los nombres de las variantes de producto automáticamente",
     "channel-price-preview": "Vista previa de precio para el canal de ventas",
     "collection-contents": "Contenidos de la colección",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "¿Eliminar administrador?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "Ninún canal seleccionado",
     "no-featured-asset": "Sin recurso destacado",
     "no-selection": "Sin selección",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "No fue posible eliminar el producto del canal",
     "notify-remove-product-from-channel-success": "Producto eliminado del canal con éxito",
     "notify-remove-variant-from-channel-error": "No fue posible eliminar la variante de producto del canal",
@@ -210,6 +213,8 @@
     "expand-entries": "Mostrar entradas",
     "extension-running-in-separate-window": "La extensión se está ejecutando en una nueva ventana",
     "filter": "Filtro",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "Invitado",
     "items-per-page-option": "{ count } por página",
@@ -235,6 +240,7 @@
     "notify-create-success": "Creado nuevo { entity }",
     "notify-delete-error": "Ha ocurrido un problema, imposible de eliminar { entity }",
     "notify-delete-success": "Eliminado { entity }",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "Ha ocurrido un problema, imposible de guardar cambios",
     "notify-saved-changes": "Cambios guardados",
     "notify-update-error": "Ha ocurrido un problema, imposible de actualizar{ entity }",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/fr.json

@@ -56,6 +56,7 @@
     "add-facets": "Ajout composant",
     "add-option": "Ajout option",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "produit attribué au canal \"{ channel }\"",
     "assign-products-to-channel": "Attribuer les produits au canal",
     "assign-to-channel": "Attribuer au canal",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "Mettre à jour automatiquement les noms de variations du produit ",
     "channel-price-preview": "Prévisualisation du prix du canal",
     "collection-contents": "Contenu de la Collection",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "Supprimer administrateur?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "Pas de canal sélectionné",
     "no-featured-asset": "Pas de fichier vedette",
     "no-selection": "Pas de sélection",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "Retrait du produit du canal échoué",
     "notify-remove-product-from-channel-success": "Retrait du produit du canal réussi",
     "notify-remove-variant-from-channel-error": "Retrait de la variation du produit du canal échoué",
@@ -210,6 +213,8 @@
     "expand-entries": "Développer les éléments",
     "extension-running-in-separate-window": "Extension fonctionne dans une fenêtre à part",
     "filter": "Filtre",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "Invité",
     "items-per-page-option": "{ count } par page",
@@ -235,6 +240,7 @@
     "notify-create-success": "Nouveau { entity } créé",
     "notify-delete-error": "Une erreur est survenue, suppression de { entity } échouée",
     "notify-delete-success": "{ entity } supprimé",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "Une erreur est survenue, Changements non enregistrés",
     "notify-saved-changes": "Changements enregistrés",
     "notify-update-error": "Une erreur est survenue, mise à jour de { entity } échouée",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/it.json

@@ -56,6 +56,7 @@
     "add-facets": "Aggiungi attributi",
     "add-option": "Aggiungi opzione",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "Prodotto assegnato correttamente a \"{ channel }\"",
     "assign-products-to-channel": "Assegna prodotto al canale",
     "assign-to-channel": "Assegna a un canale",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "Aggiorna automaticamente i nomi delle Varianti",
     "channel-price-preview": "Anteprima prezzo canale",
     "collection-contents": "Contenuti della Collezione",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "Eliminare l'amministratore?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "Nessun canale selezionato",
     "no-featured-asset": "Nessun media in evidenza",
     "no-selection": "Nessuna selezione",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "Impossibile rimuovere il prodotto dal canale",
     "notify-remove-product-from-channel-success": "Prodotto rimosso dal canale",
     "notify-remove-variant-from-channel-error": "Impossibile rimuovere la variante dal canale",
@@ -210,6 +213,8 @@
     "expand-entries": "Espandi elementi",
     "extension-running-in-separate-window": "L'estensione è in esecuzione in un'altra finestra",
     "filter": "Filtra",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "Ospite",
     "items-per-page-option": "{ count } per pagina",
@@ -235,6 +240,7 @@
     "notify-create-success": "Creato nuovo { entity }",
     "notify-delete-error": "Si è verificato un errore, impossibile cancellare { entity }",
     "notify-delete-success": "Cancellato { entity }",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "Si è verificato un errore, impossibile salvare le modifiche",
     "notify-saved-changes": "Modifiche salvate",
     "notify-update-error": "Si è verificato un errore, impossibile aggiornare { entity }",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/pl.json

@@ -56,6 +56,7 @@
     "add-facets": "Dodaj faset",
     "add-option": "Dodaj opcje",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "Pomyślnie przypisano produkt do \"{ channel }\"",
     "assign-products-to-channel": "Przypisz produkt do kanału",
     "assign-to-channel": "Przypisz do kanału",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "",
     "channel-price-preview": "Podgląd cen kanału",
     "collection-contents": "Zawartość kolekcji",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "",
@@ -111,6 +113,7 @@
     "no-channel-selected": "Brak zaznaczonego kanału",
     "no-featured-asset": "Brak polecanego zasobu",
     "no-selection": "Brak zaznaczenia",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "Błąd usuwania produktu z kanału",
     "notify-remove-product-from-channel-success": "Produkt pomyślnie usunięty z kanału",
     "notify-remove-variant-from-channel-error": "",
@@ -210,6 +213,8 @@
     "expand-entries": "",
     "extension-running-in-separate-window": "Rozszerzenie jest włączone w innym oknie",
     "filter": "",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "Gość",
     "items-per-page-option": "{ count } na stronę",
@@ -235,6 +240,7 @@
     "notify-create-success": "Utworzono { entity }",
     "notify-delete-error": "Wystąpił błąd, nie można usunąć { entity }",
     "notify-delete-success": "Usunięto { entity }",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "Wystąpił błąd, nie można zapisać zmian",
     "notify-saved-changes": "Zapisano zmiany",
     "notify-update-error": "Wystąpił błąd, nie można zaktualizować { entity }",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json

@@ -56,6 +56,7 @@
     "add-facets": "Adiciona etiqueta",
     "add-option": "Adiciona opção",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "Produto atribuído com sucesso a \"{ channel }\"",
     "assign-products-to-channel": "Atribuir produtos ao canal",
     "assign-to-channel": "Atribuir ao canal",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "Atualizar automaticamente os nomes das variações do produto",
     "channel-price-preview": "Visualizar preço do canal",
     "collection-contents": "Conteúdo da categoria",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "Excluir administrador?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "Nenhum canal selecionado",
     "no-featured-asset": "Nenhum recurso em destaque",
     "no-selection": "Nenhuma seleção",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "Não foi possível remover o produto do canal",
     "notify-remove-product-from-channel-success": "Produto removido com sucesso do canal",
     "notify-remove-variant-from-channel-error": "Erro ao remover variação do canal",
@@ -210,6 +213,8 @@
     "expand-entries": "Expandir entradas",
     "extension-running-in-separate-window": "A extensão está sendo executada em uma janela separada",
     "filter": "Filtro",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "Convidado",
     "items-per-page-option": "{ count } por página",
@@ -235,6 +240,7 @@
     "notify-create-success": "Criado novo { entity }",
     "notify-delete-error": "Ocorreu um erro, não foi possível excluir { entity }",
     "notify-delete-success": "Excluído { entity }",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "Ocorreu um erro, não foi possível salvar as alterações",
     "notify-saved-changes": "Alterações salvas",
     "notify-update-error": "Ocorreu um erro, não foi possível atualizar { entity }",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/pt_PT.json

@@ -56,6 +56,7 @@
     "add-facets": "Adicionar etiqueta",
     "add-option": "Adicionar opção",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "Produto atribuído com sucesso a \"{ channel }\"",
     "assign-products-to-channel": "Atribuir produtos ao canal",
     "assign-to-channel": "Atribuir ao canal",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "Actualizar automaticamente os nomes das variantes do produto",
     "channel-price-preview": "Visualizar preço do canal",
     "collection-contents": "Conteúdo da categoria",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "Eliminar administrador?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "Nenhum canal seleccionado",
     "no-featured-asset": "Nenhum recurso em destaque",
     "no-selection": "Nenhuma imagem seleccionada",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "Não foi possível remover o produto do canal",
     "notify-remove-product-from-channel-success": "Produto removido do canal com sucesso",
     "notify-remove-variant-from-channel-error": "Erro ao remover a variante do canal",
@@ -210,6 +213,8 @@
     "expand-entries": "Expandir entradas",
     "extension-running-in-separate-window": "A extensão está a ser executada em uma janela separada",
     "filter": "Filtro",
+    "force-delete": "",
+    "force-remove": "",
     "general": "Geral",
     "guest": "Convidado",
     "items-per-page-option": "{ count } por página",
@@ -235,6 +240,7 @@
     "notify-create-success": "Novo(a) { entity } adicionado(a)",
     "notify-delete-error": "Ocorreu um erro, não foi possível eliminar { entity }",
     "notify-delete-success": "{ entity } excluído(a)",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "Ocorreu um erro. Não foi possível guardar as alterações",
     "notify-saved-changes": "Alterações guardadas",
     "notify-update-error": "Ocorreu um erro. Não foi possível actualizar a entidade { entity }",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/ru.json

@@ -56,6 +56,7 @@
     "add-facets": "Добавить тег",
     "add-option": "Добавить опции",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "Товар успешно добавлен в канал \"{ channel }\"",
     "assign-products-to-channel": "Добавить товары в канал",
     "assign-to-channel": "Добавить в канал",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "Автоматически обновлять названия вариантов товара",
     "channel-price-preview": "Предварительный просмотр цен канала",
     "collection-contents": "Содержание коллекции",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "Удалить администратора?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "Канал не выбран",
     "no-featured-asset": "Нет избранного медиа-объекта",
     "no-selection": "Не выбрано",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "Не удалось удалить товар из канала",
     "notify-remove-product-from-channel-success": "Товар успешно удален из канала",
     "notify-remove-variant-from-channel-error": "Не удалось удалить вариант товара из канала",
@@ -210,6 +213,8 @@
     "expand-entries": "Развернуть записи",
     "extension-running-in-separate-window": "Расширение работает в отдельном окне",
     "filter": "Фильтр",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "Гость",
     "items-per-page-option": "{ count } на странице",
@@ -235,6 +240,7 @@
     "notify-create-success": "Создано новое { entity }",
     "notify-delete-error": "Ошибка, не удалось удалить { entity }",
     "notify-delete-success": "Удалено { entity }",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "Произошла ошибка, не удалось сохранить изменения",
     "notify-saved-changes": "Сохранены изменения",
     "notify-update-error": "Произошла ошибка, не удалось обновить { entity }",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/uk.json

@@ -56,6 +56,7 @@
     "add-facets": "Додати тег",
     "add-option": "Додати опцію",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "Товар успішно доданий в канал \"{ channel }\"",
     "assign-products-to-channel": "Додати товари в канал",
     "assign-to-channel": "Додати в канал",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "Автоматично оновлювати назви варіантів товару",
     "channel-price-preview": "Попередній перегляд цін каналу",
     "collection-contents": "Зміст колекції",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "Видалити адміністратора?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "Канал не вибрано",
     "no-featured-asset": "Немає обраного медіа-об'єкта",
     "no-selection": "Не вибрано",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "Не вдалося видалити товар з каналу",
     "notify-remove-product-from-channel-success": "Товар успішно видалений з каналу",
     "notify-remove-variant-from-channel-error": "Не вдалося видалити варіант товару з каналу",
@@ -210,6 +213,8 @@
     "expand-entries": "Розгорнути записи",
     "extension-running-in-separate-window": "Розширення працює в окремому вікні",
     "filter": "Фільтр",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "Гість",
     "items-per-page-option": "{ count } на сторінці",
@@ -235,6 +240,7 @@
     "notify-create-success": "Створено нове { entity }",
     "notify-delete-error": "Помилка, не вдалося видалити { entity }",
     "notify-delete-success": "Видалено { entity }",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "Сталася помилка, не вдалося зберегти зміни",
     "notify-saved-changes": "Збережені зміни",
     "notify-update-error": "Сталася помилка, не вдалося оновити { entity }",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json

@@ -56,6 +56,7 @@
     "add-facets": "添加特征",
     "add-option": "添加规格组",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "成功将产品添加至销售渠道\"{ channel }\"",
     "assign-products-to-channel": "分配产品到销售渠道",
     "assign-to-channel": "分配至销售渠道",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "自动更新不同商品变体名称",
     "channel-price-preview": "渠道价格预览",
     "collection-contents": "系列产品",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "确认删除管理员吗?",
@@ -111,6 +113,7 @@
     "no-channel-selected": "未选择销售渠道",
     "no-featured-asset": "无特征图片",
     "no-selection": "尚未选择",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "从渠道中移除商品失败",
     "notify-remove-product-from-channel-success": "成功从渠道中移除商品",
     "notify-remove-variant-from-channel-error": "从渠道中移除商品变体失败",
@@ -210,6 +213,8 @@
     "expand-entries": "展开",
     "extension-running-in-separate-window": "扩展已在另一个窗口启动",
     "filter": "过滤",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "游客",
     "items-per-page-option": "每页显示 { count } 条",
@@ -235,6 +240,7 @@
     "notify-create-success": "{ entity }已添加",
     "notify-delete-error": "删除{ entity }失败",
     "notify-delete-success": "{ entity }已删除",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "保存失败",
     "notify-saved-changes": "修改已保存",
     "notify-update-error": "更新{ entity }失败",

+ 6 - 0
packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json

@@ -56,6 +56,7 @@
     "add-facets": "新增特徵",
     "add-option": "新增規格選項",
     "asset-preview-links": "",
+    "assign-facets-to-channel-success": "",
     "assign-product-to-channel-success": "成功將產品新增至渠道\"{ channel }\"",
     "assign-products-to-channel": "分配產品到渠道",
     "assign-to-channel": "分配至渠道",
@@ -66,6 +67,7 @@
     "auto-update-product-variant-name": "",
     "channel-price-preview": "渠道價格覽",
     "collection-contents": "系列產品",
+    "confirm-bulk-delete-facets": "",
     "confirm-bulk-delete-products": "",
     "confirm-cancel": "",
     "confirm-delete-administrator": "",
@@ -111,6 +113,7 @@
     "no-channel-selected": "並未選擇渠道",
     "no-featured-asset": "並無特徵圖片",
     "no-selection": "尚未選擇",
+    "notify-remove-facets-from-channel-success": "",
     "notify-remove-product-from-channel-error": "從渠道中移除商品失敗",
     "notify-remove-product-from-channel-success": "成功從渠道中移除商品",
     "notify-remove-variant-from-channel-error": "",
@@ -210,6 +213,8 @@
     "expand-entries": "",
     "extension-running-in-separate-window": "扩展已在另一個窗口启動",
     "filter": "",
+    "force-delete": "",
+    "force-remove": "",
     "general": "",
     "guest": "游客",
     "items-per-page-option": "每页顯示 { count } 條",
@@ -235,6 +240,7 @@
     "notify-create-success": "{ entity }已新增",
     "notify-delete-error": "移除{ entity }失敗",
     "notify-delete-success": "{ entity }已移除",
+    "notify-remove-products-from-channel-success": "",
     "notify-save-changes-error": "保存失敗",
     "notify-saved-changes": "修改已保存",
     "notify-update-error": "更新{ entity }失敗",