]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add missing frontend I18N 25654/head
authorTiago Melo <tmelo@suse.com>
Thu, 20 Dec 2018 13:23:36 +0000 (12:23 -0100)
committerTiago Melo <tmelo@suse.com>
Wed, 16 Jan 2019 12:03:05 +0000 (12:03 +0000)
Fixes: http://tracker.ceph.com/issues/36719
Signed-off-by: Tiago Melo <tmelo@suse.com>
24 files changed:
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-actions.model.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-details/osd-details.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.priorities.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-reweight-modal/osd-reweight-modal.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form-tooltips.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form-data.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/erasure-code-profile.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/erasure-code-profile.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/osd.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/select-badges/select-badges-messages.model.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/select-badges/select-badges.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/select-badges/select-badges.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-message.service.ts
src/pybind/mgr/dashboard/frontend/src/locale/messages.xlf

index 92cd89c122f030dbb5a74849fe9c378904373c87..28546d0d048567bd6379585ee5eb7328c6d7f063 100644 (file)
@@ -1,5 +1,5 @@
 <ng-template #usageNotAvailableTooltipTpl>
-  <div [innerHtml]="'Only available for RBD images with <strong>fast-diff</strong> enabled'"></div>
+  <ng-container i18n>Only available for RBD images with <strong>fast-diff</strong> enabled</ng-container>
 </ng-template>
 
 
        heading="Details">
     <table class="table table-striped table-bordered">
       <tbody>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Name</td>
-        <td class="col-sm-3">{{ selectedItem.name }}</td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Pool</td>
-        <td class="col-sm-3">{{ selectedItem.pool_name }}</td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Data Pool</td>
-        <td class="col-sm-3">{{ selectedItem.data_pool | empty }}</td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Created</td>
-        <td class="col-sm-3">{{ selectedItem.timestamp | cdDate }}</td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Size</td>
-        <td class="col-sm-3">{{ selectedItem.size | dimlessBinary }}</td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Objects</td>
-        <td class="col-sm-3">{{ selectedItem.num_objs | dimless }}</td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Object size</td>
-        <td class="col-sm-3">{{ selectedItem.obj_size | dimlessBinary }}</td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Features</td>
-        <td class="col-sm-3">
-          <span *ngFor="let feature of selectedItem.features_name">
-            <span class="badge badge-pill badge-primary margin-right-sm">{{ feature }}</span>
-          </span>
-        </td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Provisioned</td>
-        <td class="col-sm-3">
-          <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') === -1">
-          <span class="text-muted"
-                [tooltip]="usageNotAvailableTooltipTpl"
-                placement="right">N/A</span>
-        </span>
-          <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') !== -1">
-          {{ selectedItem.disk_usage | dimlessBinary }}
-        </span>
-        </td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Total provisioned</td>
-        <td class="col-sm-3">
-          <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') === -1">
-          <span class="text-muted"
-                [tooltip]="usageNotAvailableTooltipTpl"
-                placement="right">N/A</span>
-        </span>
-          <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') !== -1">
-          {{ selectedItem.total_disk_usage | dimlessBinary }}
-        </span>
-        </td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Striping unit</td>
-        <td class="col-sm-3">{{ selectedItem.stripe_unit | dimlessBinary }}</td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Striping count</td>
-        <td class="col-sm-3">{{ selectedItem.stripe_count }}</td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Parent</td>
-        <td class="col-sm-3">
-          <span *ngIf="selectedItem.parent">{{ selectedItem.parent.pool_name }}/{{ selectedItem.parent.image_name }}@{{ selectedItem.parent.snap_name }}</span>
-          <span *ngIf="!selectedItem.parent">-</span>
-        </td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Block name prefix</td>
-        <td class="col-sm-3">{{ selectedItem.block_name_prefix }}</td>
-      </tr>
-      <tr>
-        <td i18n
-            class="bold col-sm-1">Order</td>
-        <td class="col-sm-3">{{ selectedItem.order }}</td>
-      </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Name</td>
+          <td class="col-sm-3">{{ selectedItem.name }}</td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Pool</td>
+          <td class="col-sm-3">{{ selectedItem.pool_name }}</td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Data Pool</td>
+          <td class="col-sm-3">{{ selectedItem.data_pool | empty }}</td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Created</td>
+          <td class="col-sm-3">{{ selectedItem.timestamp | cdDate }}</td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Size</td>
+          <td class="col-sm-3">{{ selectedItem.size | dimlessBinary }}</td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Objects</td>
+          <td class="col-sm-3">{{ selectedItem.num_objs | dimless }}</td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Object size</td>
+          <td class="col-sm-3">{{ selectedItem.obj_size | dimlessBinary }}</td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Features</td>
+          <td class="col-sm-3">
+            <span *ngFor="let feature of selectedItem.features_name">
+              <span class="badge badge-pill badge-primary margin-right-sm">{{ feature }}</span>
+            </span>
+          </td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Provisioned</td>
+          <td class="col-sm-3">
+            <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') === -1">
+              <span class="text-muted"
+                    [tooltip]="usageNotAvailableTooltipTpl"
+                    placement="right"
+                    i18n>N/A</span>
+            </span>
+            <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') !== -1">
+              {{ selectedItem.disk_usage | dimlessBinary }}
+            </span>
+          </td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Total provisioned</td>
+          <td class="col-sm-3">
+            <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') === -1">
+              <span class="text-muted"
+                    [tooltip]="usageNotAvailableTooltipTpl"
+                    placement="right"
+                    i18n>N/A</span>
+            </span>
+            <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') !== -1">
+              {{ selectedItem.total_disk_usage | dimlessBinary }}
+            </span>
+          </td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Striping unit</td>
+          <td class="col-sm-3">{{ selectedItem.stripe_unit | dimlessBinary }}</td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Striping count</td>
+          <td class="col-sm-3">{{ selectedItem.stripe_count }}</td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Parent</td>
+          <td class="col-sm-3">
+            <span *ngIf="selectedItem.parent">{{ selectedItem.parent.pool_name }}
+              /{{ selectedItem.parent.image_name }}
+              @{{ selectedItem.parent.snap_name }}</span>
+            <span *ngIf="!selectedItem.parent">-</span>
+          </td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Block name prefix</td>
+          <td class="col-sm-3">{{ selectedItem.block_name_prefix }}</td>
+        </tr>
+        <tr>
+          <td i18n
+              class="bold col-sm-1">Order</td>
+          <td class="col-sm-3">{{ selectedItem.order }}</td>
+        </tr>
       </tbody>
     </table>
   </tab>
index 1494bc625491734e95c338ee961c02e0fb3c06ed..eb408eb5f2d0f151d325e6fd84d0672daa73e057 100644 (file)
@@ -1,69 +1,88 @@
+import { I18n } from '@ngx-translate/i18n-polyfill';
+
 import { CdTableAction } from '../../../shared/models/cd-table-action';
 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
 
 export class RbdSnapshotActionsModel {
-  create: CdTableAction = {
-    permission: 'create',
-    icon: 'fa-plus',
-    name: 'Create'
-  };
-  rename: CdTableAction = {
-    permission: 'update',
-    icon: 'fa-pencil',
-    name: 'Rename'
-  };
-  protect: CdTableAction = {
-    permission: 'update',
-    icon: 'fa-lock',
-    visible: (selection: CdTableSelection) =>
-      selection.hasSingleSelection && !selection.first().is_protected,
-    name: 'Protect'
-  };
-  unprotect: CdTableAction = {
-    permission: 'update',
-    icon: 'fa-unlock',
-    visible: (selection: CdTableSelection) =>
-      selection.hasSingleSelection && selection.first().is_protected,
-    name: 'Unprotect'
-  };
-  clone: CdTableAction = {
-    permission: 'create',
-    canBePrimary: (selection: CdTableSelection) => selection.hasSingleSelection,
-    disable: (selection: CdTableSelection) =>
-      !selection.hasSingleSelection || selection.first().cdExecuting,
-    icon: 'fa-clone',
-    name: 'Clone'
-  };
-  copy: CdTableAction = {
-    permission: 'create',
-    canBePrimary: (selection: CdTableSelection) => selection.hasSingleSelection,
-    disable: (selection: CdTableSelection) =>
-      !selection.hasSingleSelection || selection.first().cdExecuting,
-    icon: 'fa-copy',
-    name: 'Copy'
-  };
-  rollback: CdTableAction = {
-    permission: 'update',
-    icon: 'fa-undo',
-    name: 'Rollback'
-  };
-  deleteSnap: CdTableAction = {
-    permission: 'delete',
-    icon: 'fa-times',
-    disable: (selection: CdTableSelection) => {
-      const first = selection.first();
-      return !selection.hasSingleSelection || first.cdExecuting || first.is_protected;
-    },
-    name: 'Delete'
-  };
-  ordering = [
-    this.create,
-    this.rename,
-    this.protect,
-    this.unprotect,
-    this.clone,
-    this.copy,
-    this.rollback,
-    this.deleteSnap
-  ];
+  i18n: I18n;
+
+  create: CdTableAction;
+  rename: CdTableAction;
+  protect: CdTableAction;
+  unprotect: CdTableAction;
+  clone: CdTableAction;
+  copy: CdTableAction;
+  rollback: CdTableAction;
+  deleteSnap: CdTableAction;
+  ordering: CdTableAction[];
+
+  constructor(i18n: I18n) {
+    this.i18n = i18n;
+
+    this.create = {
+      permission: 'create',
+      icon: 'fa-plus',
+      name: this.i18n('Create')
+    };
+    this.rename = {
+      permission: 'update',
+      icon: 'fa-pencil',
+      name: this.i18n('Rename')
+    };
+    this.protect = {
+      permission: 'update',
+      icon: 'fa-lock',
+      visible: (selection: CdTableSelection) =>
+        selection.hasSingleSelection && !selection.first().is_protected,
+      name: this.i18n('Protect')
+    };
+    this.unprotect = {
+      permission: 'update',
+      icon: 'fa-unlock',
+      visible: (selection: CdTableSelection) =>
+        selection.hasSingleSelection && selection.first().is_protected,
+      name: this.i18n('Unprotect')
+    };
+    this.clone = {
+      permission: 'create',
+      canBePrimary: (selection: CdTableSelection) => selection.hasSingleSelection,
+      disable: (selection: CdTableSelection) =>
+        !selection.hasSingleSelection || selection.first().cdExecuting,
+      icon: 'fa-clone',
+      name: this.i18n('Clone')
+    };
+    this.copy = {
+      permission: 'create',
+      canBePrimary: (selection: CdTableSelection) => selection.hasSingleSelection,
+      disable: (selection: CdTableSelection) =>
+        !selection.hasSingleSelection || selection.first().cdExecuting,
+      icon: 'fa-copy',
+      name: this.i18n('Copy')
+    };
+    this.rollback = {
+      permission: 'update',
+      icon: 'fa-undo',
+      name: this.i18n('Rollback')
+    };
+    this.deleteSnap = {
+      permission: 'delete',
+      icon: 'fa-times',
+      disable: (selection: CdTableSelection) => {
+        const first = selection.first();
+        return !selection.hasSingleSelection || first.cdExecuting || first.is_protected;
+      },
+      name: this.i18n('Delete')
+    };
+
+    this.ordering = [
+      this.create,
+      this.rename,
+      this.protect,
+      this.unprotect,
+      this.clone,
+      this.copy,
+      this.rollback,
+      this.deleteSnap
+    ];
+  }
 }
index 41861135fe271c975fc68e1c17e0481b6c7091e4..0294daf2b09f5a9d8bd0fbe85a448e63d1833c26 100644 (file)
@@ -3,6 +3,7 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testin
 import { By } from '@angular/platform-browser';
 import { RouterTestingModule } from '@angular/router/testing';
 
+import { I18n } from '@ngx-translate/i18n-polyfill';
 import { ToastModule } from 'ng2-toastr';
 import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
 import { Subject, throwError as observableThrowError } from 'rxjs';
@@ -80,11 +81,12 @@ describe('RbdSnapshotListComponent', () => {
 
     beforeEach(() => {
       fixture.detectChanges();
+      const i18n = TestBed.get(I18n);
       called = false;
       rbdService = new RbdService(null);
       notificationService = new NotificationService(null, null);
       authStorageService = new AuthStorageService();
-      authStorageService.set('user', { 'rbd-image': ['create', 'read', 'update', 'delete'] });
+      authStorageService.set('user', '', { 'rbd-image': ['create', 'read', 'update', 'delete'] });
       component = new RbdSnapshotListComponent(
         authStorageService,
         null,
@@ -94,7 +96,8 @@ describe('RbdSnapshotListComponent', () => {
         null,
         notificationService,
         null,
-        null
+        null,
+        i18n
       );
       spyOn(rbdService, 'deleteSnapshot').and.returnValue(observableThrowError({ status: 500 }));
       spyOn(notificationService, 'notifyTask').and.stub();
index 8958dea84b2dc6539c840a1f8a3b156059ea0f10..e186195c5c71f2e378d5e74eeac6cc84031b22b5 100644 (file)
@@ -77,7 +77,7 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges {
     private i18n: I18n
   ) {
     this.permission = this.authStorageService.getPermissions().rbdImage;
-    const actions = new RbdSnapshotActionsModel();
+    const actions = new RbdSnapshotActionsModel(this.i18n);
     actions.create.click = () => this.openCreateSnapshotModal();
     actions.rename.click = () => this.openEditSnapshotModal();
     actions.protect.click = () => this.toggleProtection();
index 0be5c92ddfbc74f11f03803db3e42b81adf95578..d19a2adfa5c4c01df0b398c06d83f8236ae50a7a 100644 (file)
@@ -9,13 +9,8 @@
           [formGroup]="moveForm"
           novalidate>
       <div class="modal-body">
-        <p>
-          <ng-container i18n>To move</ng-container>&nbsp;
-          <kbd>{{ poolName }}/{{ imageName }}</kbd>&nbsp;
-          <ng-container i18n>to trash, click</ng-container>&nbsp;
-          <kbd i18n>Move Image</kbd>.&nbsp;
-          <ng-container i18n>Optionally, you can pick an expiration date.</ng-container>
-        </p>
+        <p i18n>To move <kbd>{{ poolName }}/{{ imageName }}</kbd> to trash,
+          click <kbd>Move Image</kbd>. Optionally, you can pick an expiration date.</p>
 
         <div class="form-group"
              [ngClass]="{'has-error': moveForm.showError('expiresAt', formDir)}">
index ecd5b5fd1818fdff9f83ea44a34068647ee0f6bf..1bcecc679c4eaea04d59c49afbd03761208ddd79 100644 (file)
@@ -6,7 +6,7 @@ import { of } from 'rxjs';
 
 import { TabsModule } from 'ngx-bootstrap/tabs';
 
-import { configureTestBed } from '../../../../../testing/unit-test-helper';
+import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
 import { OsdService } from '../../../../shared/api/osd.service';
 import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
 import { SharedModule } from '../../../../shared/shared.module';
@@ -28,7 +28,8 @@ describe('OsdDetailsComponent', () => {
       PerformanceCounterModule,
       SharedModule
     ],
-    declarations: [OsdDetailsComponent, OsdPerformanceHistogramComponent]
+    declarations: [OsdDetailsComponent, OsdPerformanceHistogramComponent],
+    providers: i18nProviders
   });
 
   beforeEach(() => {
index f6823557e9abeb6b14eecc4ef26c79be006142dc..26c738f226945ba6ade3d38919e892b658851add 100755 (executable)
@@ -8,10 +8,10 @@ import { forkJoin as observableForkJoin, of } from 'rxjs';
 import { mergeMap } from 'rxjs/operators';
 
 import { ConfigurationService } from '../../../../shared/api/configuration.service';
+import { OsdService } from '../../../../shared/api/osd.service';
 import { NotificationType } from '../../../../shared/enum/notification-type.enum';
 import { CdFormGroup } from '../../../../shared/forms/cd-form-group';
 import { NotificationService } from '../../../../shared/services/notification.service';
-import { OsdRecvSpeedModalPriorities } from './osd-recv-speed-modal.priorities';
 
 @Component({
   selector: 'cd-osd-recv-speed-modal',
@@ -20,15 +20,17 @@ import { OsdRecvSpeedModalPriorities } from './osd-recv-speed-modal.priorities';
 })
 export class OsdRecvSpeedModalComponent implements OnInit {
   osdRecvSpeedForm: CdFormGroup;
-  priorities = OsdRecvSpeedModalPriorities.KNOWN_PRIORITIES;
+  priorities = [];
   priorityAttrs = [];
 
   constructor(
     public bsModalRef: BsModalRef,
     private configService: ConfigurationService,
     private notificationService: NotificationService,
-    private i18n: I18n
+    private i18n: I18n,
+    private osdService: OsdService
   ) {
+    this.priorities = this.osdService.osdRecvSpeedModalPriorities.KNOWN_PRIORITIES;
     this.osdRecvSpeedForm = new CdFormGroup({
       priority: new FormControl(null, { validators: [Validators.required] }),
       customizePriority: new FormControl(false)
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.priorities.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.priorities.ts
deleted file mode 100755 (executable)
index 7b1bacd..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-export class OsdRecvSpeedModalPriorities {
-  public static KNOWN_PRIORITIES = [
-    // TODO: I18n
-    {
-      name: null,
-      text: '-- Select the priority --',
-      values: {
-        osd_max_backfills: null,
-        osd_recovery_max_active: null,
-        osd_recovery_max_single_start: null,
-        osd_recovery_sleep: null
-      }
-    },
-    {
-      name: 'low',
-      text: 'Low',
-      values: {
-        osd_max_backfills: 1,
-        osd_recovery_max_active: 1,
-        osd_recovery_max_single_start: 1,
-        osd_recovery_sleep: 0.5
-      }
-    },
-    {
-      name: 'default',
-      text: 'Default',
-      values: {
-        osd_max_backfills: 1,
-        osd_recovery_max_active: 3,
-        osd_recovery_max_single_start: 1,
-        osd_recovery_sleep: 0
-      }
-    },
-    {
-      name: 'high',
-      text: 'High',
-      values: {
-        osd_max_backfills: 4,
-        osd_recovery_max_active: 4,
-        osd_recovery_max_single_start: 4,
-        osd_recovery_sleep: 0
-      }
-    }
-  ];
-}
index 93d19341c9a83ebed767bf32868012bcb4133f24..9ffc34ead129549016ba18f56654d30620895593 100644 (file)
@@ -5,7 +5,7 @@ import { ReactiveFormsModule } from '@angular/forms';
 import { BsModalRef } from 'ngx-bootstrap/modal';
 import { of } from 'rxjs';
 
-import { configureTestBed } from '../../../../../testing/unit-test-helper';
+import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
 import { OsdService } from '../../../../shared/api/osd.service';
 import { ModalComponent } from '../../../../shared/components/modal/modal.component';
 import { SubmitButtonComponent } from '../../../../shared/components/submit-button/submit-button.component';
@@ -19,7 +19,7 @@ describe('OsdReweightModalComponent', () => {
   configureTestBed({
     imports: [ReactiveFormsModule, HttpClientTestingModule],
     declarations: [OsdReweightModalComponent, ModalComponent, SubmitButtonComponent],
-    providers: [OsdService, BsModalRef, CdFormBuilder]
+    providers: [OsdService, BsModalRef, CdFormBuilder, i18nProviders]
   });
 
   beforeEach(() => {
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form-tooltips.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form-tooltips.ts
deleted file mode 100644 (file)
index 358354d..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-export class ErasureCodeProfileFormTooltips {
-  // TODO: I18N
-  // Copied from /srv/cephmgr/ceph-dev/doc/rados/operations/erasure-code.*.rst
-  k = `Each object is split in data-chunks parts, each stored on a different OSD.`;
-
-  m = `Compute coding chunks for each object and store them on different OSDs.
-    The number of coding chunks is also the number of OSDs that can be down without losing data.`;
-
-  plugins = {
-    jerasure: {
-      description: `The jerasure plugin is the most generic and flexible plugin,
-        it is also the default for Ceph erasure coded pools.`,
-      technique: `The more flexible technique is reed_sol_van : it is enough to set k and m.
-        The cauchy_good technique can be faster but you need to chose the packetsize carefully.
-        All of reed_sol_r6_op, liberation, blaum_roth, liber8tion are RAID6 equivalents in the
-        sense that they can only be configured with m=2.`,
-      packetSize: `The encoding will be done on packets of bytes size at a time.
-        Chosing the right packet size is difficult.
-        The jerasure documentation contains extensive information on this topic.`
-    },
-    lrc: {
-      description: `With the jerasure plugin, when an erasure coded object is stored
-        on multiple OSDs, recovering from the loss of one OSD requires reading from all the others.
-        For instance if jerasure is configured with k=8 and m=4, losing one OSD requires
-        reading from the eleven others to repair.
-
-        The lrc erasure code plugin creates local parity chunks to be able to recover using
-        less OSDs. For instance if lrc is configured with k=8, m=4 and l=4, it will create
-        an additional parity chunk for every four OSDs. When a single OSD is lost, it can be
-        recovered with only four OSDs instead of eleven.`,
-      l: `Group the coding and data chunks into sets of size locality. For instance,
-        for k=4 and m=2, when locality=3 two groups of three are created. Each set can
-        be recovered without reading chunks from another set.`,
-      crushLocality: `The type of the crush bucket in which each set of chunks defined by l
-        will be stored. For instance, if it is set to rack, each group of l chunks will be placed
-        in a different rack. It is used to create a CRUSH rule step such as step choose rack.
-        If it is not set, no such grouping is done.`
-    },
-    isa: {
-      description: `The isa plugin encapsulates the ISA library. It only runs on Intel processors.`,
-      technique: `The ISA plugin comes in two Reed Solomon forms.
-        If reed_sol_van is set, it is Vandermonde, if cauchy is set, it is Cauchy.`
-    },
-    shec: {
-      description: `The shec plugin encapsulates the multiple SHEC library.
-        It allows ceph to recover data more efficiently than Reed Solomon codes.`,
-      c: `The number of parity chunks each of which includes each data chunk in its calculation
-        range. The number is used as a durability estimator. For instance, if c=2, 2 OSDs can
-        be down without losing data.`
-    }
-  };
-
-  crushRoot = `The name of the crush bucket used for the first step of the CRUSH rule.
-    For instance step take default.`;
-
-  crushFailureDomain = `Ensure that no two chunks are in a bucket with the same failure domain.
-    For instance, if the failure domain is host no two chunks will be stored on the same host.
-    It is used to create a CRUSH rule step such as step chooseleaf host.`;
-
-  crushDeviceClass = `Restrict placement to devices of a specific class (e.g., ssd or hdd),
-    using the crush device class names in the CRUSH map.`;
-
-  directory = `Set the directory name from which the erasure code plugin is loaded.`;
-}
index f0e7c720c35f134e3e3f5aa488cff019baadf88d..1fd5879aab42c549b3b627eee4e4df63768e84c8 100644 (file)
@@ -33,9 +33,8 @@
               *ngIf="form.showError('name', frm, 'required')"
               i18n>This field is required!</span>
         <span class="help-block"
-              *ngIf="form.showError('name', frm, 'pattern')">
-          The name can only consist of alphanumeric characters, dashes and underscores.
-        </span>
+              *ngIf="form.showError('name', frm, 'pattern')"
+              i18n>The name can only consist of alphanumeric characters, dashes and underscores.</span>
         <span class="help-block"
               *ngIf="form.showError('name', frm, 'uniqueName')"
               i18n>The chosen erasure code profile name is already in use.</span>
index 6e3218b47f7210162ddb9a2ac51a63d3f6473a61..7599833bc2ad8de9301d60a683355861a6ebb5ab 100644 (file)
@@ -10,7 +10,6 @@ import { CdValidators } from '../../../shared/forms/cd-validators';
 import { ErasureCodeProfile } from '../../../shared/models/erasure-code-profile';
 import { FinishedTask } from '../../../shared/models/finished-task';
 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
-import { ErasureCodeProfileFormTooltips } from './erasure-code-profile-form-tooltips';
 
 @Component({
   selector: 'cd-erasure-code-profile-form',
@@ -28,7 +27,7 @@ export class ErasureCodeProfileFormComponent implements OnInit {
   techniques: string[];
   requiredControls: string[] = [];
   devices: string[] = [];
-  tooltips = new ErasureCodeProfileFormTooltips();
+  tooltips = this.ecpService.formTooltips;
 
   PLUGIN = {
     LRC: 'lrc', // Locally Repairable Erasure Code
index 0c0e1390ea1eeaad3c09252297f5870545221457..9e1a60764572b101259cebe71cf05e4db0d5edc3 100644 (file)
@@ -22,19 +22,22 @@ export class PoolFormData {
         new SelectBadgesOption(false, 'rgw', '')
       ],
       validators: [Validators.pattern('[A-Za-z0-9_]+'), Validators.maxLength(128)],
-      messages: new SelectBadgesMessages({
-        empty: i18n('No applications added'),
-        selectionLimit: {
-          text: i18n('Applications limit reached'),
-          tooltip: i18n('A pool can only have up to four applications definitions.')
+      messages: new SelectBadgesMessages(
+        {
+          empty: i18n('No applications added'),
+          selectionLimit: {
+            text: i18n('Applications limit reached'),
+            tooltip: i18n('A pool can only have up to four applications definitions.')
+          },
+          customValidations: {
+            pattern: i18n(`Allowed characters '_a-zA-Z0-9'`),
+            maxlength: i18n('Maximum length is 128 characters')
+          },
+          filter: i18n('Filter or add applications'),
+          add: i18n('Add application')
         },
-        customValidations: {
-          pattern: i18n(`Allowed characters '_a-zA-Z0-9'`),
-          maxlength: i18n('Maximum length is 128 characters')
-        },
-        filter: i18n('Filter or add applications'),
-        add: i18n('Add application')
-      })
+        i18n
+      )
     };
   }
 
index ab5f22f738721775580c99e38122ed06f596e2cf..0e23d16b44e502954d49fa1f8d834acfc8cd2298 100644 (file)
@@ -27,7 +27,8 @@
           <tr>
             <td i18n
                 class="bold col-sm-1">System</td>
-            <td class="col-sm-3">{{ user.system ? "Yes" : "No" }}</td>
+            <td class="col-sm-3"
+                i18n>{user.system, select, 1 {Yes} 0 {No}}</td>
           </tr>
           <tr>
             <td i18n
index bc0da5fd27a320394d16b3d7c03f0a247e273138..8d2204e25f62486ef631c7f82bbe17c7be673850 100644 (file)
@@ -37,7 +37,7 @@ export class UserFormComponent implements OnInit {
   userFormMode = UserFormMode;
   mode: UserFormMode;
   allRoles: Array<UserFormRoleModel>;
-  messages = new SelectBadgesMessages({ empty: 'There are no roles.' });
+  messages: SelectBadgesMessages;
 
   constructor(
     private authService: AuthService,
@@ -51,6 +51,7 @@ export class UserFormComponent implements OnInit {
     private i18n: I18n
   ) {
     this.createForm();
+    this.messages = new SelectBadgesMessages({ empty: 'There are no roles.' }, this.i18n);
   }
 
   createForm() {
index 180a35073474097dfdb68cc5399d83705c2161d5..227af54c43c80565f9ef8e9b91ea2735d80fdf35 100644 (file)
@@ -1,7 +1,7 @@
 import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
 import { TestBed } from '@angular/core/testing';
 
-import { configureTestBed } from '../../../testing/unit-test-helper';
+import { configureTestBed, i18nProviders } from '../../../testing/unit-test-helper';
 import { ErasureCodeProfile } from '../models/erasure-code-profile';
 import { ErasureCodeProfileService } from './erasure-code-profile.service';
 
@@ -13,7 +13,7 @@ describe('ErasureCodeProfileService', () => {
 
   configureTestBed({
     imports: [HttpClientTestingModule],
-    providers: [ErasureCodeProfileService]
+    providers: [ErasureCodeProfileService, i18nProviders]
   });
 
   beforeEach(() => {
index 7599caaa60d6177a1dd30d6fe02b73eb033f5f6c..df7527727aab038a66316bb5840637e333777e0e 100644 (file)
@@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
 
 import { Observable } from 'rxjs';
 
+import { I18n } from '@ngx-translate/i18n-polyfill';
 import { ErasureCodeProfile } from '../models/erasure-code-profile';
 import { ApiModule } from './api.module';
 
@@ -12,7 +13,73 @@ import { ApiModule } from './api.module';
 export class ErasureCodeProfileService {
   apiPath = 'api/erasure_code_profile';
 
-  constructor(private http: HttpClient) {}
+  formTooltips = {
+    // Copied from /srv/cephmgr/ceph-dev/doc/rados/operations/erasure-code.*.rst
+    k: this.i18n(`Each object is split in data-chunks parts, each stored on a different OSD.`),
+
+    m: this.i18n(`Compute coding chunks for each object and store them on different OSDs.
+      The number of coding chunks is also the number of OSDs that can be down without losing data.`),
+
+    plugins: {
+      jerasure: {
+        description: this.i18n(`The jerasure plugin is the most generic and flexible plugin,
+          it is also the default for Ceph erasure coded pools.`),
+        technique: this.i18n(`The more flexible technique is reed_sol_van : it is enough to set k
+          and m. The cauchy_good technique can be faster but you need to chose the packetsize
+          carefully. All of reed_sol_r6_op, liberation, blaum_roth, liber8tion are RAID6 equivalents
+          in the sense that they can only be configured with m=2.`),
+        packetSize: this.i18n(`The encoding will be done on packets of bytes size at a time.
+          Chosing the right packet size is difficult.
+          The jerasure documentation contains extensive information on this topic.`)
+      },
+      lrc: {
+        description: this.i18n(`With the jerasure plugin, when an erasure coded object is stored on
+          multiple OSDs, recovering from the loss of one OSD requires reading from all the others.
+          For instance if jerasure is configured with k=8 and m=4, losing one OSD requires reading
+          from the eleven others to repair.
+
+          The lrc erasure code plugin creates local parity chunks to be able to recover using
+          less OSDs. For instance if lrc is configured with k=8, m=4 and l=4, it will create
+          an additional parity chunk for every four OSDs. When a single OSD is lost, it can be
+          recovered with only four OSDs instead of eleven.`),
+        l: this.i18n(`Group the coding and data chunks into sets of size locality. For instance,
+          for k=4 and m=2, when locality=3 two groups of three are created. Each set can
+          be recovered without reading chunks from another set.`),
+        crushLocality: this.i18n(`The type of the crush bucket in which each set of chunks defined
+          by l will be stored. For instance, if it is set to rack, each group of l chunks will be
+          placed in a different rack. It is used to create a CRUSH rule step such as step choose
+          rack. If it is not set, no such grouping is done.`)
+      },
+      isa: {
+        description: this.i18n(
+          `The isa plugin encapsulates the ISA library. It only runs on Intel processors.`
+        ),
+        technique: this.i18n(`The ISA plugin comes in two Reed Solomon forms.
+          If reed_sol_van is set, it is Vandermonde, if cauchy is set, it is Cauchy.`)
+      },
+      shec: {
+        description: this.i18n(`The shec plugin encapsulates the multiple SHEC library.
+          It allows ceph to recover data more efficiently than Reed Solomon codes.`),
+        c: this.i18n(`The number of parity chunks each of which includes each data chunk in its
+          calculation range. The number is used as a durability estimator. For instance, if c=2,
+          2 OSDs can be down without losing data.`)
+      }
+    },
+
+    crushRoot: this.i18n(`The name of the crush bucket used for the first step of the CRUSH rule.
+      For instance step take default.`),
+
+    crushFailureDomain: this.i18n(`Ensure that no two chunks are in a bucket with the same failure
+      domain. For instance, if the failure domain is host no two chunks will be stored on the same
+      host. It is used to create a CRUSH rule step such as step chooseleaf host.`),
+
+    crushDeviceClass: this.i18n(`Restrict placement to devices of a specific class
+      (e.g., ssd or hdd), using the crush device class names in the CRUSH map.`),
+
+    directory: this.i18n(`Set the directory name from which the erasure code plugin is loaded.`)
+  };
+
+  constructor(private http: HttpClient, private i18n: I18n) {}
 
   list(): Observable<ErasureCodeProfile[]> {
     return this.http.get<ErasureCodeProfile[]>(this.apiPath);
index 08c87c3e4369f91bfa80ddb7b1c14bd1fb72da30..a442642a06bbb7dfea0b67a0bd6ecc88d883e397 100644 (file)
@@ -1,7 +1,7 @@
 import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
 import { TestBed } from '@angular/core/testing';
 
-import { configureTestBed } from '../../../testing/unit-test-helper';
+import { configureTestBed, i18nProviders } from '../../../testing/unit-test-helper';
 import { OsdService } from './osd.service';
 
 describe('OsdService', () => {
@@ -9,7 +9,7 @@ describe('OsdService', () => {
   let httpTesting: HttpTestingController;
 
   configureTestBed({
-    providers: [OsdService],
+    providers: [OsdService, i18nProviders],
     imports: [HttpClientTestingModule]
   });
 
index 03e056d17514b7a5cd17b484bf9594300a4de9e6..a98d503ae0f2d28b1f948056c0b2042642e7a383 100644 (file)
@@ -1,6 +1,8 @@
 import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 
+import { I18n } from '@ngx-translate/i18n-polyfill';
+
 import { ApiModule } from './api.module';
 
 @Injectable({
@@ -9,7 +11,52 @@ import { ApiModule } from './api.module';
 export class OsdService {
   private path = 'api/osd';
 
-  constructor(private http: HttpClient) {}
+  osdRecvSpeedModalPriorities = {
+    KNOWN_PRIORITIES: [
+      {
+        name: null,
+        text: this.i18n('-- Select the priority --'),
+        values: {
+          osd_max_backfills: null,
+          osd_recovery_max_active: null,
+          osd_recovery_max_single_start: null,
+          osd_recovery_sleep: null
+        }
+      },
+      {
+        name: 'low',
+        text: this.i18n('Low'),
+        values: {
+          osd_max_backfills: 1,
+          osd_recovery_max_active: 1,
+          osd_recovery_max_single_start: 1,
+          osd_recovery_sleep: 0.5
+        }
+      },
+      {
+        name: 'default',
+        text: this.i18n('Default'),
+        values: {
+          osd_max_backfills: 1,
+          osd_recovery_max_active: 3,
+          osd_recovery_max_single_start: 1,
+          osd_recovery_sleep: 0
+        }
+      },
+      {
+        name: 'high',
+        text: this.i18n('High'),
+        values: {
+          osd_max_backfills: 4,
+          osd_recovery_max_active: 4,
+          osd_recovery_max_single_start: 4,
+          osd_recovery_sleep: 0
+        }
+      }
+    ]
+  };
+
+  constructor(private http: HttpClient, private i18n: I18n) {}
 
   getList() {
     return this.http.get(`${this.path}`);
index cbe298861af6de3d15fff02e5deac93866d643da..c5306411826c32e45eb87b267fb9c5fc311d023d 100644 (file)
@@ -1,16 +1,26 @@
+import { I18n } from '@ngx-translate/i18n-polyfill';
+
 import * as _ from 'lodash';
 
 export class SelectBadgesMessages {
-  empty = 'There are no items.';
-  selectionLimit = {
-    tooltip: 'Deselect item to select again',
-    text: 'Selection limit reached'
-  };
+  i18n: I18n;
+  empty: string;
+  selectionLimit: any;
   customValidations = {};
-  filter = 'Filter tags';
-  add = 'Add badge'; // followed by " '{{filter.value}}'"
+  filter: string;
+  add: string;
+
+  constructor(messages: {}, i18n: I18n) {
+    this.i18n = i18n;
+
+    this.empty = this.i18n('There are no items.');
+    this.selectionLimit = {
+      tooltip: this.i18n('Deselect item to select again'),
+      text: this.i18n('Selection limit reached')
+    };
+    this.filter = this.i18n('Filter tags');
+    this.add = this.i18n('Add badge'); // followed by " '{{filter.value}}'"
 
-  constructor(messages: {}) {
     _.merge(this, messages);
   }
 }
index 08f9e5eb6978a3aa92f0b95bfb25aa9d3fe0b9b8..cc539353ee8dca48a2ebab395ef99e58b2ee9bf7 100644 (file)
@@ -4,7 +4,7 @@ import { ReactiveFormsModule, Validators } from '@angular/forms';
 import { PopoverModule } from 'ngx-bootstrap/popover';
 import { TooltipModule } from 'ngx-bootstrap/tooltip';
 
-import { configureTestBed } from '../../../../testing/unit-test-helper';
+import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
 import { SelectBadgesOption } from './select-badges-option.model';
 import { SelectBadgesComponent } from './select-badges.component';
 
@@ -20,7 +20,8 @@ describe('SelectBadgesComponent', () => {
 
   configureTestBed({
     declarations: [SelectBadgesComponent],
-    imports: [PopoverModule.forRoot(), TooltipModule, ReactiveFormsModule]
+    imports: [PopoverModule.forRoot(), TooltipModule, ReactiveFormsModule],
+    providers: i18nProviders
   });
 
   beforeEach(() => {
index 0d3bec8bb6ac23cdf8c270c9eec18de79dc68339..fcfbf34f1d3ef23375a711646667a4e299d39025 100644 (file)
@@ -1,6 +1,7 @@
 import { Component, Input, OnChanges, OnInit } from '@angular/core';
 import { FormControl, ValidatorFn } from '@angular/forms';
 
+import { I18n } from '@ngx-translate/i18n-polyfill';
 import * as _ from 'lodash';
 
 import { CdFormGroup } from '../../forms/cd-form-group';
@@ -18,7 +19,7 @@ export class SelectBadgesComponent implements OnInit, OnChanges {
   @Input()
   options: Array<SelectBadgesOption> = [];
   @Input()
-  messages = new SelectBadgesMessages({});
+  messages = new SelectBadgesMessages({}, this.i18n);
   @Input()
   selectionLimit: number;
   @Input()
@@ -30,7 +31,7 @@ export class SelectBadgesComponent implements OnInit, OnChanges {
   Object = Object;
   filteredOptions: Array<SelectBadgesOption> = [];
 
-  constructor() {}
+  constructor(private i18n: I18n) {}
 
   ngOnInit() {
     this.initFilter();
index 455fbc6f93d106c61c66b47b76eac72a69045324..209f5ca7b14edeba9148ddf711bacc681ac54e2a 100644 (file)
@@ -20,13 +20,17 @@ export class TaskMessageOperation {
 }
 
 class TaskMessage {
+  i18n: I18n;
+
   operation: TaskMessageOperation;
   involves: (object) => string;
   errors: (metadata) => object;
 
   failure(metadata): string {
-    // TODO: I18N
-    return `Failed to ${this.operation.failure} ${this.involves(metadata)}`;
+    return this.i18n('Failed to {{failure}} {{metadata}}', {
+      failure: this.operation.failure,
+      metadata: this.involves(metadata)
+    });
   }
 
   running(metadata): string {
@@ -38,10 +42,12 @@ class TaskMessage {
   }
 
   constructor(
+    i18n: I18n,
     operation: TaskMessageOperation,
     involves: (metadata) => string,
     errors?: (metadata) => object
   ) {
+    this.i18n = i18n;
     this.operation = operation;
     this.involves = involves;
     this.errors = errors || (() => ({}));
@@ -54,7 +60,7 @@ class TaskMessage {
 export class TaskMessageService {
   constructor(private i18n: I18n) {}
 
-  defaultMessage = new TaskMessage(
+  defaultMessage = this.newTaskMessage(
     new TaskMessageOperation(this.i18n('Executing'), this.i18n('execute'), this.i18n('Executed')),
     (metadata) => {
       return (
@@ -117,7 +123,7 @@ export class TaskMessageService {
 
   messages = {
     // Pool tasks
-    'pool/create': new TaskMessage(
+    'pool/create': this.newTaskMessage(
       this.commonOperations.create,
       (metadata) => this.pool(metadata),
       (metadata) => ({
@@ -126,7 +132,7 @@ export class TaskMessageService {
         })
       })
     ),
-    'pool/edit': new TaskMessage(
+    'pool/edit': this.newTaskMessage(
       this.commonOperations.update,
       (metadata) => this.pool(metadata),
       (metadata) => ({
@@ -135,9 +141,11 @@ export class TaskMessageService {
         })
       })
     ),
-    'pool/delete': new TaskMessage(this.commonOperations.delete, (metadata) => this.pool(metadata)),
+    'pool/delete': this.newTaskMessage(this.commonOperations.delete, (metadata) =>
+      this.pool(metadata)
+    ),
     // Erasure code profile tasks
-    'ecp/create': new TaskMessage(
+    'ecp/create': this.newTaskMessage(
       this.commonOperations.create,
       (metadata) => this.ecp(metadata),
       (metadata) => ({
@@ -146,24 +154,34 @@ export class TaskMessageService {
         })
       })
     ),
-    'ecp/delete': new TaskMessage(this.commonOperations.delete, (metadata) => this.ecp(metadata)),
+    'ecp/delete': this.newTaskMessage(this.commonOperations.delete, (metadata) =>
+      this.ecp(metadata)
+    ),
     // RBD tasks
-    'rbd/create': new TaskMessage(this.commonOperations.create, this.rbd.default, (metadata) => ({
-      '17': this.i18n('Name is already used by {{rbd_name}}.', {
-        rbd_name: this.rbd.default(metadata)
+    'rbd/create': this.newTaskMessage(
+      this.commonOperations.create,
+      this.rbd.default,
+      (metadata) => ({
+        '17': this.i18n('Name is already used by {{rbd_name}}.', {
+          rbd_name: this.rbd.default(metadata)
+        })
       })
-    })),
-    'rbd/edit': new TaskMessage(this.commonOperations.update, this.rbd.default, (metadata) => ({
+    ),
+    'rbd/edit': this.newTaskMessage(this.commonOperations.update, this.rbd.default, (metadata) => ({
       '17': this.i18n('Name is already used by {{rbd_name}}.', {
         rbd_name: this.rbd.default(metadata)
       })
     })),
-    'rbd/delete': new TaskMessage(this.commonOperations.delete, this.rbd.default, (metadata) => ({
-      '39': this.i18n('{{rbd_name}} contains snapshots.', {
-        rbd_name: this.rbd.default(metadata)
+    'rbd/delete': this.newTaskMessage(
+      this.commonOperations.delete,
+      this.rbd.default,
+      (metadata) => ({
+        '39': this.i18n('{{rbd_name}} contains snapshots.', {
+          rbd_name: this.rbd.default(metadata)
+        })
       })
-    })),
-    'rbd/clone': new TaskMessage(
+    ),
+    'rbd/clone': this.newTaskMessage(
       new TaskMessageOperation(this.i18n('Cloning'), this.i18n('clone'), this.i18n('Cloned')),
       this.rbd.child,
       (metadata) => ({
@@ -175,7 +193,7 @@ export class TaskMessageService {
         })
       })
     ),
-    'rbd/copy': new TaskMessage(
+    'rbd/copy': this.newTaskMessage(
       new TaskMessageOperation(this.i18n('Copying'), this.i18n('copy'), this.i18n('Copied')),
       this.rbd.destination,
       (metadata) => ({
@@ -184,7 +202,7 @@ export class TaskMessageService {
         })
       })
     ),
-    'rbd/flatten': new TaskMessage(
+    'rbd/flatten': this.newTaskMessage(
       new TaskMessageOperation(
         this.i18n('Flattening'),
         this.i18n('flatten'),
@@ -193,7 +211,7 @@ export class TaskMessageService {
       this.rbd.default
     ),
     // RBD snapshot tasks
-    'rbd/snap/create': new TaskMessage(
+    'rbd/snap/create': this.newTaskMessage(
       this.commonOperations.create,
       this.rbd.snapshot,
       (metadata) => ({
@@ -202,7 +220,7 @@ export class TaskMessageService {
         })
       })
     ),
-    'rbd/snap/edit': new TaskMessage(
+    'rbd/snap/edit': this.newTaskMessage(
       this.commonOperations.update,
       this.rbd.snapshot,
       (metadata) => ({
@@ -211,7 +229,7 @@ export class TaskMessageService {
         })
       })
     ),
-    'rbd/snap/delete': new TaskMessage(
+    'rbd/snap/delete': this.newTaskMessage(
       this.commonOperations.delete,
       this.rbd.snapshot,
       (metadata) => ({
@@ -220,7 +238,7 @@ export class TaskMessageService {
         })
       })
     ),
-    'rbd/snap/rollback': new TaskMessage(
+    'rbd/snap/rollback': this.newTaskMessage(
       new TaskMessageOperation(
         this.i18n('Rolling back'),
         this.i18n('rollback'),
@@ -229,7 +247,7 @@ export class TaskMessageService {
       this.rbd.snapshot
     ),
     // RBD trash tasks
-    'rbd/trash/move': new TaskMessage(
+    'rbd/trash/move': this.newTaskMessage(
       new TaskMessageOperation(this.i18n('Moving'), this.i18n('move'), this.i18n('Moved')),
       (metadata) =>
         this.i18n(`image '{{id}}' to trash`, {
@@ -239,7 +257,7 @@ export class TaskMessageService {
         2: this.i18n('Could not find image.')
       })
     ),
-    'rbd/trash/restore': new TaskMessage(
+    'rbd/trash/restore': this.newTaskMessage(
       new TaskMessageOperation(this.i18n('Restoring'), this.i18n('restore'), this.i18n('Restored')),
       (metadata) =>
         this.i18n(`image '{{id}}' into '{{new_id}}'`, {
@@ -252,14 +270,14 @@ export class TaskMessageService {
         })
       })
     ),
-    'rbd/trash/remove': new TaskMessage(
+    'rbd/trash/remove': this.newTaskMessage(
       new TaskMessageOperation(this.i18n('Deleting'), this.i18n('delete'), this.i18n('Deleted')),
       (metadata) =>
         this.i18n(`image '{{id}}'`, {
           id: `${metadata.pool_name}/${metadata.image_name}@${metadata.image_id}`
         })
     ),
-    'rbd/trash/purge': new TaskMessage(
+    'rbd/trash/purge': this.newTaskMessage(
       new TaskMessageOperation(this.i18n('Purging'), this.i18n('purge'), this.i18n('Purged')),
       (metadata) => {
         let message = this.i18n('all pools');
@@ -272,30 +290,38 @@ export class TaskMessageService {
       }
     ),
     // RBD mirroring tasks
-    'rbd/mirroring/pool/edit': new TaskMessage(
+    'rbd/mirroring/pool/edit': this.newTaskMessage(
       this.commonOperations.update,
       this.rbd_mirroring.pool,
       (metadata) => ({
         16: this.i18n('Cannot disable mirroring because it contains a peer.')
       })
     ),
-    'rbd/mirroring/peer/add': new TaskMessage(
+    'rbd/mirroring/peer/add': this.newTaskMessage(
       this.commonOperations.create,
       this.rbd_mirroring.pool_peer,
       (metadata) => ({})
     ),
-    'rbd/mirroring/peer/edit': new TaskMessage(
+    'rbd/mirroring/peer/edit': this.newTaskMessage(
       this.commonOperations.update,
       this.rbd_mirroring.pool_peer,
       (metadata) => ({})
     ),
-    'rbd/mirroring/peer/delete': new TaskMessage(
+    'rbd/mirroring/peer/delete': this.newTaskMessage(
       this.commonOperations.delete,
       this.rbd_mirroring.pool_peer,
       (metadata) => ({})
     )
   };
 
+  newTaskMessage(
+    operation: TaskMessageOperation,
+    involves: (metadata) => string,
+    errors?: (metadata) => object
+  ) {
+    return new TaskMessage(this.i18n, operation, involves, errors);
+  }
+
   pool(metadata) {
     return this.i18n(`pool '{{pool_name}}'`, {
       pool_name: metadata.pool_name
index 9774dc4c8e58fa864b82cf410bd164804819a6a8..40894634647654afb209bbf166160503ef3fc4c3 100644 (file)
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">60</context>
+          <context context-type="linenumber">59</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">187</context>
+          <context context-type="linenumber">186</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">211</context>
+          <context context-type="linenumber">210</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/pool-form/pool-form.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
-          <context context-type="linenumber">49</context>
+          <context context-type="linenumber">44</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-trash-purge-modal/rbd-trash-purge-modal.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">68</context>
+          <context context-type="linenumber">67</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">92</context>
+          <context context-type="linenumber">91</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">118</context>
+          <context context-type="linenumber">117</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">166</context>
+          <context context-type="linenumber">165</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/pool-form/pool-form.component.html</context>
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
           <context context-type="linenumber">3</context>
         </context-group>
+      </trans-unit><trans-unit id="490e15ecc922965b6d8194754c87c5583aa071f3" datatype="html">
+        <source>The name can only consist of alphanumeric characters, dashes and underscores.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
+          <context context-type="linenumber">37</context>
+        </context-group>
       </trans-unit><trans-unit id="9edc2b494e660618af3e5225f68d40b7ca67629c" datatype="html">
         <source>The chosen erasure code profile name is already in use.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">41</context>
+          <context context-type="linenumber">40</context>
         </context-group>
       </trans-unit><trans-unit id="ef9ff0e6227947b48dfab4ac39ade04af758913b" datatype="html">
         <source>Plugin</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">48</context>
+          <context context-type="linenumber">47</context>
         </context-group>
       </trans-unit><trans-unit id="dd69b31bce8f630eac1d4762b0bbcf72ce19d193" datatype="html">
         <source>Data chunks (k)</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">76</context>
+          <context context-type="linenumber">75</context>
         </context-group>
       </trans-unit><trans-unit id="b0d26a6172d32cb81218fe2103c54a818cbc1189" datatype="html">
         <source>Must be equal to or greater than 2.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">95</context>
+          <context context-type="linenumber">94</context>
         </context-group>
       </trans-unit><trans-unit id="dab3a299ead121169b8e08ed618c3b6a2f66691b" datatype="html">
         <source>Coding chunks (m)</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">103</context>
+          <context context-type="linenumber">102</context>
         </context-group>
       </trans-unit><trans-unit id="1e2773e5bd4948193f18f2361d663ecc3988c656" datatype="html">
         <source>Must be equal to or greater than 1.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">121</context>
+          <context context-type="linenumber">120</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">143</context>
+          <context context-type="linenumber">142</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">169</context>
+          <context context-type="linenumber">168</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">262</context>
+          <context context-type="linenumber">261</context>
         </context-group>
       </trans-unit><trans-unit id="6cde4c945a49a260c0a47bcc7cd956846930a5f7" datatype="html">
         <source>Durability estimator (c)</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">130</context>
+          <context context-type="linenumber">129</context>
         </context-group>
       </trans-unit><trans-unit id="af668c2a095a979ea2b4e43cd82c2120ab56c21c" datatype="html">
         <source>Locality (l)</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">152</context>
+          <context context-type="linenumber">151</context>
         </context-group>
       </trans-unit><trans-unit id="d455a110bf6d2235e314e295ce1dfeee93d3dff2" datatype="html">
         <source>Crush failure domain</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">176</context>
+          <context context-type="linenumber">175</context>
         </context-group>
       </trans-unit><trans-unit id="b74a495f041f7dd102eee5c0bbc9e03083b538ae" datatype="html">
         <source>Crush Locality</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">200</context>
+          <context context-type="linenumber">199</context>
         </context-group>
       </trans-unit><trans-unit id="a2f14a73f7a6e94479f67423cc51102da8d6f524" datatype="html">
         <source>None</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">214</context>
+          <context context-type="linenumber">213</context>
         </context-group>
       </trans-unit><trans-unit id="2981733b912b693a9dd9d915d6d34f4692cc874a" datatype="html">
         <source>Technique</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">227</context>
+          <context context-type="linenumber">226</context>
         </context-group>
       </trans-unit><trans-unit id="e0098b6e47b04ec817361f384ce81d454ba5c0bb" datatype="html">
         <source>Packetsize</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">249</context>
+          <context context-type="linenumber">248</context>
         </context-group>
       </trans-unit><trans-unit id="c0252cd81ca54d0a2f69ec9ccf4248e73df5aa4a" datatype="html">
         <source>Crush root</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">270</context>
+          <context context-type="linenumber">269</context>
         </context-group>
       </trans-unit><trans-unit id="1548d5c76f0406ddd1ba3c557e1e6db22e95b340" datatype="html">
         <source>Crush device class</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">287</context>
+          <context context-type="linenumber">286</context>
         </context-group>
       </trans-unit><trans-unit id="5e85feb6f9f0334366e46ee09ca6b8df52397432" datatype="html">
         <source>any</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">297</context>
+          <context context-type="linenumber">296</context>
         </context-group>
       </trans-unit><trans-unit id="03d84645f6e019c5a43909bbf2ea1696ee88332c" datatype="html">
         <source>Directory</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">309</context>
+          <context context-type="linenumber">308</context>
         </context-group>
       </trans-unit><trans-unit id="f6755cff4957d5c3c89bafce5651f1b6fa2b1fd9" datatype="html">
         <source>Add</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">327</context>
+          <context context-type="linenumber">326</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-s3-key-modal/rgw-user-s3-key-modal.component.html</context>
         <source>Close</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
-          <context context-type="linenumber">331</context>
+          <context context-type="linenumber">330</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-capability-modal/rgw-user-capability-modal.component.html</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">39</context>
+          <context context-type="linenumber">40</context>
         </context-group>
       </trans-unit><trans-unit id="128d6efb51d9ddc7c0cc695a2deeca5b9523f6e4" datatype="html">
         <source>There are no subusers.</source>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">133</context>
+          <context context-type="linenumber">134</context>
         </context-group>
       </trans-unit><trans-unit id="67c746c1ba9dab4351fedc4c7cba4e6d6b0dbc47" datatype="html">
         <source>S3</source>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">148</context>
+          <context context-type="linenumber">149</context>
         </context-group>
       </trans-unit><trans-unit id="a5193a8d59cd045c18ef9521f486a091dad1f513" datatype="html">
         <source>Add S3 key</source>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">48</context>
+          <context context-type="linenumber">49</context>
         </context-group>
       </trans-unit><trans-unit id="1d01eccdda47fc907c5be35bcb16d2dcd02b0270" datatype="html">
         <source>There are no capabilities.</source>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">60</context>
+          <context context-type="linenumber">61</context>
         </context-group>
       </trans-unit><trans-unit id="f50a33d3c339f8f4a465141f8caa5d2d8c005251" datatype="html">
         <source>Enabled</source>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">65</context>
+          <context context-type="linenumber">66</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">101</context>
+          <context context-type="linenumber">102</context>
         </context-group>
       </trans-unit><trans-unit id="6146e13ceca5fa5cc17b771b282fe5955f3d19fa" datatype="html">
         <source>Unlimited size</source>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">96</context>
+          <context context-type="linenumber">97</context>
         </context-group>
       </trans-unit><trans-unit id="449dca3d3a93ead418b0541ea81b0dda7e6483c2" datatype="html">
         <source>Welcome to Ceph!</source>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">70</context>
+          <context context-type="linenumber">71</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">106</context>
+          <context context-type="linenumber">107</context>
         </context-group>
       </trans-unit><trans-unit id="aa6fb95c355f172bda303de1ce2f38c251a149cf" datatype="html">
         <source>Unlimited</source>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">73</context>
+          <context context-type="linenumber">74</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">84</context>
+          <context context-type="linenumber">85</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">109</context>
+          <context context-type="linenumber">110</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">120</context>
+          <context context-type="linenumber">121</context>
         </context-group>
       </trans-unit><trans-unit id="ee862a800364b4d11f9b8cb9955a28a60f840a45" datatype="html">
         <source>Maximum objects</source>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">81</context>
+          <context context-type="linenumber">82</context>
         </context-group>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">117</context>
+          <context context-type="linenumber">118</context>
         </context-group>
       </trans-unit><trans-unit id="8011e20c5bbe51602d459a860fbf29b599b55edd" datatype="html">
         <source>System</source>
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
           <context context-type="linenumber">29</context>
         </context-group>
+      </trans-unit><trans-unit id="f120423a48cf96636bc415daf5ac041ea8c6503e" datatype="html">
+        <source>{VAR_SELECT, select, 0 {No} 1 {Yes} }</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
+          <context context-type="linenumber">31</context>
+        </context-group>
       </trans-unit><trans-unit id="db18a2772988415466a7f75dc42663ce78c9c1d3" datatype="html">
         <source>Maximum buckets</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/rgw/rgw-user-details/rgw-user-details.component.html</context>
-          <context context-type="linenumber">34</context>
+          <context context-type="linenumber">35</context>
         </context-group>
       </trans-unit><trans-unit id="ca53d681a9892d6fdbb57ee9676582186515e961" datatype="html">
         <source>Performance counters not available</source>
           <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
           <context context-type="linenumber">3</context>
         </context-group>
-      </trans-unit><trans-unit id="9a7ad2a407f63dbedcc82158886b8475dfd33114" datatype="html">
-        <source>To move</source>
-        <context-group purpose="location">
-          <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
-          <context context-type="linenumber">13</context>
-        </context-group>
-      </trans-unit><trans-unit id="be91c2ed9701d56ce78104953e56631139df0e9a" datatype="html">
-        <source>to trash, click</source>
-        <context-group purpose="location">
-          <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
-          <context context-type="linenumber">15</context>
-        </context-group>
-      </trans-unit><trans-unit id="536b3205c0c0d4d21685ec06feccfcf2fe14deb1" datatype="html">
-        <source>Move Image</source>
-        <context-group purpose="location">
-          <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
-          <context context-type="linenumber">16</context>
-        </context-group>
-        <context-group purpose="location">
-          <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
-          <context context-type="linenumber">45</context>
-        </context-group>
-      </trans-unit><trans-unit id="3f69ac1e65136ebfcc3e5eb31810da33e0489ad6" datatype="html">
-        <source>Optionally, you can pick an expiration date.</source>
+      </trans-unit><trans-unit id="86301cf2044b1eda218483365f6b9de8dde5040b" datatype="html">
+        <source>To move <x id="START_TAG_KBD" ctype="x-kbd" equiv-text="&lt;kbd&gt;"/><x id="INTERPOLATION" equiv-text="{{ poolName }}"/>/<x id="INTERPOLATION_1" equiv-text="{{ imageName }}"/><x id="CLOSE_TAG_KBD" ctype="x-kbd" equiv-text="&lt;/kbd&gt;"/> to trash,
+          click <x id="START_TAG_KBD" ctype="x-kbd" equiv-text="&lt;kbd&gt;"/>Move Image<x id="CLOSE_TAG_KBD" ctype="x-kbd" equiv-text="&lt;/kbd&gt;"/>. Optionally, you can pick an expiration date.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
-          <context context-type="linenumber">17</context>
+          <context context-type="linenumber">12</context>
         </context-group>
       </trans-unit><trans-unit id="88f27d390844aad53b4240360e928156c5f0d326" datatype="html">
         <source>Protection expires at</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
-          <context context-type="linenumber">23</context>
+          <context context-type="linenumber">18</context>
         </context-group>
       </trans-unit><trans-unit id="da166e9a0d27322f6ba8916d71ecc0f9905bb4b1" datatype="html">
         <source>NOT PROTECTED</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
-          <context context-type="linenumber">25</context>
+          <context context-type="linenumber">20</context>
         </context-group>
       </trans-unit><trans-unit id="a1506e5f2ca22cad14502ec7a20fb6113ace145d" datatype="html">
         <source>Wrong date format. Please use &quot;YYYY-MM-DD HH:mm:ss&quot;.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
-          <context context-type="linenumber">34</context>
+          <context context-type="linenumber">29</context>
         </context-group>
       </trans-unit><trans-unit id="aa7ea0bb7495281e0b3258467ac7d90a1e44a1a1" datatype="html">
         <source>Protection has already expired. Please pick a future date or leave it empty.</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
-          <context context-type="linenumber">37</context>
+          <context context-type="linenumber">32</context>
+        </context-group>
+      </trans-unit><trans-unit id="536b3205c0c0d4d21685ec06feccfcf2fe14deb1" datatype="html">
+        <source>Move Image</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">app/ceph/block/rbd-trash-move-modal/rbd-trash-move-modal.component.html</context>
+          <context context-type="linenumber">40</context>
+        </context-group>
+      </trans-unit><trans-unit id="0f6e8f6094b180eaf1f11bc0ffe383f1cdcd059e" datatype="html">
+        <source>Only available for RBD images with <x id="START_TAG_STRONG" ctype="x-strong" equiv-text="&lt;strong&gt;"/>fast-diff<x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="&lt;/strong&gt;"/> enabled</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
+          <context context-type="linenumber">2</context>
         </context-group>
       </trans-unit><trans-unit id="e70fcca5a99575cffef3ff8cbd5e69f06ffd0f1c" datatype="html">
         <source>Pool</source>
           <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
           <context context-type="linenumber">57</context>
         </context-group>
+      </trans-unit><trans-unit id="84a36cb75660b736773fe36ffa3d54f0f0fe363e" datatype="html">
+        <source>N/A</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
+          <context context-type="linenumber">63</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
+          <context context-type="linenumber">78</context>
+        </context-group>
       </trans-unit><trans-unit id="e5c009342a4e8381f64341d0bb61c2e4685f5a4b" datatype="html">
         <source>Total provisioned</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
-          <context context-type="linenumber">71</context>
+          <context context-type="linenumber">72</context>
         </context-group>
       </trans-unit><trans-unit id="7f6bf8a43ae415f527ac961ea62471b983aaa97b" datatype="html">
         <source>Striping unit</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
-          <context context-type="linenumber">85</context>
+          <context context-type="linenumber">87</context>
         </context-group>
       </trans-unit><trans-unit id="db710e8a8f011923f2d15d713fbae49c38b02b26" datatype="html">
         <source>Striping count</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
-          <context context-type="linenumber">90</context>
+          <context context-type="linenumber">92</context>
         </context-group>
       </trans-unit><trans-unit id="3a4c2a9e76634ff14a60d52a718296f722d47c67" datatype="html">
         <source>Parent</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
-          <context context-type="linenumber">95</context>
+          <context context-type="linenumber">97</context>
         </context-group>
       </trans-unit><trans-unit id="6a209e68d78ffc2cc9c53d2e76158624efab71ad" datatype="html">
         <source>Block name prefix</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
-          <context context-type="linenumber">103</context>
+          <context context-type="linenumber">107</context>
         </context-group>
       </trans-unit><trans-unit id="5704ec2049d007c5f5fb495a5d8b607e68d58081" datatype="html">
         <source>Order</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
-          <context context-type="linenumber">108</context>
+          <context context-type="linenumber">112</context>
         </context-group>
       </trans-unit><trans-unit id="f21b1d17b6c5042bb5805516eee37fde33739dd8" datatype="html">
         <source>Snapshots</source>
         <context-group purpose="location">
           <context context-type="sourcefile">app/ceph/block/rbd-details/rbd-details.component.html</context>
-          <context context-type="linenumber">115</context>
+          <context context-type="linenumber">119</context>
         </context-group>
       </trans-unit><trans-unit id="3f67f30568e9ae47507d46e28e1e82a7dca772e2" datatype="html">
         <source><x id="ICU" equiv-text="{ editing, select, true {...} other {...}}"/> RBD Snapshot</source>
           <context context-type="sourcefile">src/app/ceph/block/rbd-list/rbd-list.component.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-actions.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
       </trans-unit>
       <trans-unit id="3747fff7faf52b436ecff5fb8555091736d92c70" datatype="html">
         <source>Flatten</source>
           <context context-type="linenumber">1</context>
         </context-group>
       </trans-unit>
-      <trans-unit id="7eb984588f4835f6d0187b33769b4b0f62821640" datatype="html">
-        <source>RBD snapshot rollback</source>
+      <trans-unit id="70a67e04629f6d412db0a12d51820b480788d795" datatype="html">
+        <source>Create</source>
         <context-group purpose="location">
-          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts</context>
+          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-actions.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/core/auth/role-details/role-details.component.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/core/auth/role-form/role-form.component.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="e26f9cf3666aace4e524b1505c9795d76d87738d" datatype="html">
+        <source>Rename</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-actions.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="45480c03b0a3a37ff8f6a5600a6a034b40fbaea4" datatype="html">
+        <source>Protect</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-actions.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="3ea9b96a21a499d296a7f25407da0f3df6c2d5ed" datatype="html">
+        <source>Unprotect</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-actions.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="54a4a3132b693575728a45a5f3154ad4c9af404b" datatype="html">
+        <source>Clone</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-actions.model.ts</context>
           <context context-type="linenumber">1</context>
         </context-group>
       </trans-unit>
       <trans-unit id="4be22bf20ef82614ed53bcd3b912a85590aa71c9" datatype="html">
         <source>Rollback</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-actions.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="7eb984588f4835f6d0187b33769b4b0f62821640" datatype="html">
+        <source>RBD snapshot rollback</source>
         <context-group purpose="location">
           <context context-type="sourcefile">src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts</context>
           <context context-type="linenumber">1</context>
           <context context-type="linenumber">1</context>
         </context-group>
       </trans-unit>
-      <trans-unit id="70a67e04629f6d412db0a12d51820b480788d795" datatype="html">
-        <source>Create</source>
-        <context-group purpose="location">
-          <context context-type="sourcefile">src/app/core/auth/role-details/role-details.component.ts</context>
-          <context context-type="linenumber">1</context>
-        </context-group>
-        <context-group purpose="location">
-          <context context-type="sourcefile">src/app/core/auth/role-form/role-form.component.ts</context>
-          <context context-type="linenumber">1</context>
-        </context-group>
-      </trans-unit>
       <trans-unit id="fe6ee93173884201ec62d62ba173c3de85ae177e" datatype="html">
         <source>Created role &apos;<x id="INTERPOLATION" equiv-text="{{role_name}}"/>&apos;</source>
         <context-group purpose="location">
           <context context-type="linenumber">1</context>
         </context-group>
       </trans-unit>
+      <trans-unit id="1d7fbcc3d5efc946ffbcf86fed04c4e20dda20fb" datatype="html">
+        <source>Each object is split in data-chunks parts, each stored on a different OSD.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="b1f8ed4e216585f1f9134740002ef3c8501debd8" datatype="html">
+        <source>Compute coding chunks for each object and store them on different OSDs.
+      The number of coding chunks is also the number of OSDs that can be down without losing data.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="c437ee74a9a8e781e1380153f742a685e3b8cc5e" datatype="html">
+        <source>The jerasure plugin is the most generic and flexible plugin,
+          it is also the default for Ceph erasure coded pools.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="8c7e19322851402afa67e41f4f1d4e531caf1bf5" datatype="html">
+        <source>The more flexible technique is reed_sol_van : it is enough to set k
+          and m. The cauchy_good technique can be faster but you need to chose the packetsize
+          carefully. All of reed_sol_r6_op, liberation, blaum_roth, liber8tion are RAID6 equivalents
+          in the sense that they can only be configured with m=2.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="b1b11f49281b4e79219b4eecfbcb033cb15bd880" datatype="html">
+        <source>The encoding will be done on packets of bytes size at a time.
+          Chosing the right packet size is difficult.
+          The jerasure documentation contains extensive information on this topic.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="d9cb3bc07a25b26fbea457e0a7b543340bc13c49" datatype="html">
+        <source>With the jerasure plugin, when an erasure coded object is stored on
+          multiple OSDs, recovering from the loss of one OSD requires reading from all the others.
+          For instance if jerasure is configured with k=8 and m=4, losing one OSD requires reading
+          from the eleven others to repair.
+
+          The lrc erasure code plugin creates local parity chunks to be able to recover using
+          less OSDs. For instance if lrc is configured with k=8, m=4 and l=4, it will create
+          an additional parity chunk for every four OSDs. When a single OSD is lost, it can be
+          recovered with only four OSDs instead of eleven.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="813959d46e63a29bbd266b3af2ff9414c2ef553c" datatype="html">
+        <source>Group the coding and data chunks into sets of size locality. For instance,
+          for k=4 and m=2, when locality=3 two groups of three are created. Each set can
+          be recovered without reading chunks from another set.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="f0ff63d92e0a1b4d4b51e31b9133802d160a5f4c" datatype="html">
+        <source>The type of the crush bucket in which each set of chunks defined
+          by l will be stored. For instance, if it is set to rack, each group of l chunks will be
+          placed in a different rack. It is used to create a CRUSH rule step such as step choose
+          rack. If it is not set, no such grouping is done.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="3ece48df9d1e9a98f98bf3c5cc66e8b637020a4d" datatype="html">
+        <source>The isa plugin encapsulates the ISA library. It only runs on Intel processors.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="4472c9e31bfc7d96a83dc4afce0a6e885c99e839" datatype="html">
+        <source>The ISA plugin comes in two Reed Solomon forms.
+          If reed_sol_van is set, it is Vandermonde, if cauchy is set, it is Cauchy.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="fa385f5ecf63083ec848dca8dc5e52bdc72ac6f6" datatype="html">
+        <source>The shec plugin encapsulates the multiple SHEC library.
+          It allows ceph to recover data more efficiently than Reed Solomon codes.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="687b3cd94b3475c24ff4481d64d4739f902a377c" datatype="html">
+        <source>The number of parity chunks each of which includes each data chunk in its
+          calculation range. The number is used as a durability estimator. For instance, if c=2,
+          2 OSDs can be down without losing data.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="f3c4af6df91bdaabdb715f8f0b61a24faf73a3bc" datatype="html">
+        <source>The name of the crush bucket used for the first step of the CRUSH rule.
+      For instance step take default.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="e1059955a5ce65cdabf0c0a8207377e1f9bae57e" datatype="html">
+        <source>Ensure that no two chunks are in a bucket with the same failure
+      domain. For instance, if the failure domain is host no two chunks will be stored on the same
+      host. It is used to create a CRUSH rule step such as step chooseleaf host.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="ac392ac9c6da5fde6da80fc57b9c09402fb6e73d" datatype="html">
+        <source>Restrict placement to devices of a specific class
+      (e.g., ssd or hdd), using the crush device class names in the CRUSH map.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="62bcb9ae5583a088649b1adf5787a86ca3334f89" datatype="html">
+        <source>Set the directory name from which the erasure code plugin is loaded.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/erasure-code-profile.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="a436c6a4025a749198e93cac239de8deede72211" datatype="html">
+        <source>-- Select the priority --</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/osd.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="9556266f0a2b1762a44b686f2bb21dbfefb01c12" datatype="html">
+        <source>Low</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/osd.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="72279141a67cc042d9864102b703216cc8a428a3" datatype="html">
+        <source>High</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/api/osd.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
       <trans-unit id="c43dc1967a603fefb886149ac35863a06d5b4ce5" datatype="html">
         <source>Information</source>
         <context-group purpose="location">
           <context context-type="linenumber">1</context>
         </context-group>
       </trans-unit>
+      <trans-unit id="5c2114c69efd7a972c62db45f99b8b7550a5b673" datatype="html">
+        <source>There are no items.</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/components/select-badges/select-badges-messages.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="2c1e52ee832661b4a0f570877d24661736b16af1" datatype="html">
+        <source>Deselect item to select again</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/components/select-badges/select-badges-messages.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="c8c9c6e5918659336824bbdda3501c66eaa79a4c" datatype="html">
+        <source>Selection limit reached</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/components/select-badges/select-badges-messages.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="02d184c288f567825a1fcbf83bcd3099a10853d5" datatype="html">
+        <source>Filter tags</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/components/select-badges/select-badges-messages.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="aa00748e49c269956837d6f3acdd8d218796a8d8" datatype="html">
+        <source>Add badge</source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/components/select-badges/select-badges-messages.model.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
+      <trans-unit id="24c4d50fe8567de381a20a1745f1b6d37eacaa90" datatype="html">
+        <source>Failed to <x id="INTERPOLATION" equiv-text="{{failure}}"/> <x id="INTERPOLATION_1" equiv-text="{{metadata}}"/></source>
+        <context-group purpose="location">
+          <context context-type="sourcefile">src/app/shared/services/task-message.service.ts</context>
+          <context context-type="linenumber">1</context>
+        </context-group>
+      </trans-unit>
       <trans-unit id="f54b6f09b390d5ade0e354f6c7d4743c10a315cc" datatype="html">
         <source>Executing</source>
         <context-group purpose="location">