@xen-orchestra/web-core 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/lib/assets/css/typography/_utils.pcss +11 -0
  2. package/lib/assets/no-result.svg +276 -80
  3. package/lib/components/console/VtsRemoteConsole.vue +6 -2
  4. package/lib/components/data-table/VtsDataTable.vue +11 -6
  5. package/lib/components/quick-info-card/VtsQuickInfoCard.vue +2 -2
  6. package/lib/components/quick-info-row/VtsQuickInfoRow.vue +1 -1
  7. package/lib/components/select/VtsSelect.vue +1 -1
  8. package/lib/components/state-hero/VtsStateHero.vue +107 -47
  9. package/lib/components/task/VtsQuickTaskList.vue +1 -1
  10. package/lib/components/ui/card/UiCard.vue +6 -1
  11. package/lib/components/ui/collapsible-list/UiCollapsibleList.vue +1 -2
  12. package/lib/composables/tree-filter.composable.ts +4 -2
  13. package/lib/icons/index.ts +1 -1
  14. package/lib/icons/object-icons.ts +33 -2
  15. package/lib/locales/cs.json +1 -1
  16. package/lib/locales/de.json +1 -1
  17. package/lib/locales/en.json +23 -2
  18. package/lib/locales/es.json +1 -1
  19. package/lib/locales/fa.json +1 -1
  20. package/lib/locales/fr.json +23 -2
  21. package/lib/locales/it.json +1 -1
  22. package/lib/locales/nl.json +1 -1
  23. package/lib/locales/sv.json +1 -1
  24. package/package.json +1 -1
  25. package/lib/components/state-hero/VtsAllDoneHero.vue +0 -16
  26. package/lib/components/state-hero/VtsAllGoodHero.vue +0 -16
  27. package/lib/components/state-hero/VtsComingSoonHero.vue +0 -16
  28. package/lib/components/state-hero/VtsErrorNoDataHero.vue +0 -14
  29. package/lib/components/state-hero/VtsLoadingHero.vue +0 -57
  30. package/lib/components/state-hero/VtsNoDataHero.vue +0 -14
  31. package/lib/components/state-hero/VtsNoSelectionHero.vue +0 -16
  32. package/lib/components/state-hero/VtsObjectNotFoundHero.vue +0 -17
  33. package/lib/components/state-hero/VtsOfflineHero.vue +0 -16
  34. package/lib/components/state-hero/VtsPageNotFoundHero.vue +0 -33
@@ -1,7 +1,7 @@
1
1
  <template>
2
- <div :class="[type, { error }]" class="vts-state-hero">
2
+ <div :class="[className, { horizontal, error, success, 'no-background': noBackground }]" class="vts-state-hero">
3
3
  <UiLoader v-if="busy" class="loader" />
4
- <img v-else-if="imageSrc" :src="imageSrc" alt="" class="image" />
4
+ <img v-else-if="imageSrc" :src="imageSrc" :alt="type" class="image" />
5
5
  <div v-if="slots.default" :class="typoClass" class="content">
6
6
  <slot />
7
7
  </div>
@@ -10,23 +10,28 @@
10
10
 
11
11
  <script lang="ts" setup>
12
12
  import UiLoader from '@core/components/ui/loader/UiLoader.vue'
13
+ import { toVariants } from '@core/utils/to-variants.util.ts'
13
14
  import { computed } from 'vue'
14
15
 
15
- export type StateHeroType = 'page' | 'card' | 'panel' | 'table'
16
-
17
- const { type, busy, image, noBackground } = defineProps<{
18
- type: StateHeroType
16
+ export type StateHeroFormat = 'page' | 'card' | 'panel' | 'table'
17
+
18
+ type StateHeroType =
19
+ | 'no-result'
20
+ | 'under-construction'
21
+ | 'no-data'
22
+ | 'no-selection'
23
+ | 'error'
24
+ | 'not-found'
25
+ | 'offline'
26
+ | 'all-good'
27
+ | 'all-done'
28
+
29
+ const { format, type, size, busy } = defineProps<{
30
+ format: StateHeroFormat
31
+ type?: StateHeroType
32
+ size: 'extra-small' | 'small' | 'medium' | 'large'
33
+ horizontal?: boolean
19
34
  busy?: boolean
20
- image?:
21
- | 'no-result'
22
- | 'under-construction'
23
- | 'no-data'
24
- | 'no-selection'
25
- | 'error'
26
- | 'not-found'
27
- | 'offline'
28
- | 'all-good'
29
- | 'all-done'
30
35
  noBackground?: boolean
31
36
  }>()
32
37
 
@@ -34,15 +39,20 @@ const slots = defineSlots<{
34
39
  default?(): any
35
40
  }>()
36
41
 
37
- const typoClass = computed(() => (type === 'page' ? 'typo-h2' : 'typo-h4'))
38
- const error = computed(() => !noBackground && !busy && image === 'error')
42
+ const typoClass = computed(() => (format === 'page' ? 'typo-h2' : 'typo-h4'))
43
+
44
+ const className = computed(() => toVariants({ size, format }))
45
+
46
+ const error = computed(() => !busy && type === 'error')
47
+
48
+ const success = computed(() => !busy && (type === 'all-good' || type === 'all-done'))
39
49
 
40
50
  const imageSrc = computed(() => {
41
- if (!image) {
51
+ if (!type) {
42
52
  return undefined
43
53
  }
44
54
 
45
- return new URL(`../../assets/${image}.svg`, import.meta.url).href
55
+ return new URL(`../../assets/${type}.svg`, import.meta.url).href
46
56
  })
47
57
  </script>
48
58
 
@@ -50,33 +60,54 @@ const imageSrc = computed(() => {
50
60
  .vts-state-hero {
51
61
  flex: 1;
52
62
  display: flex;
53
- flex-direction: column;
54
63
  align-items: center;
55
64
  justify-content: center;
65
+ gap: 2.4rem;
56
66
 
57
- &.error {
58
- background-color: var(--color-danger-background-selected);
67
+ &:not(.horizontal) {
68
+ flex-direction: column;
59
69
 
60
70
  .content {
61
- color: var(--color-danger-txt-base);
71
+ align-items: center;
62
72
  }
63
73
  }
64
74
 
75
+ .image {
76
+ order: 2;
77
+ }
78
+
79
+ .content {
80
+ display: flex;
81
+ flex-direction: column;
82
+ gap: 1.6rem;
83
+ }
84
+
65
85
  .loader,
66
86
  .content {
87
+ order: 3;
67
88
  color: var(--color-brand-txt-base);
68
89
  }
69
90
 
70
- .image {
71
- order: 2;
91
+ &.success {
92
+ .content {
93
+ color: var(--color-success-txt-base);
94
+ }
72
95
  }
73
96
 
74
- .content {
75
- order: 3;
97
+ &.error {
98
+ background-color: var(--color-danger-background-selected);
99
+
100
+ &.no-background {
101
+ background-color: transparent;
102
+ }
103
+
104
+ .content {
105
+ color: var(--color-danger-txt-base);
106
+ }
76
107
  }
77
108
 
78
- &.page {
79
- gap: 2.4rem;
109
+ &.format--card {
110
+ gap: 2rem;
80
111
 
81
112
  .content {
82
113
  order: 3;
@@ -84,36 +115,31 @@ const imageSrc = computed(() => {
84
115
 
85
116
  .loader {
86
117
  order: 1;
87
- font-size: 10rem;
88
118
  }
89
119
 
90
120
  .image {
91
121
  order: 2;
92
- width: 90%;
93
- max-height: none;
94
122
  }
95
123
  }
96
124
 
97
- &.card {
98
- gap: 2rem;
125
+ &.format--table {
126
+ padding: 4rem;
127
+ gap: 2.4rem;
99
128
 
100
129
  .content {
101
130
  order: 3;
102
131
  }
103
132
 
104
133
  .loader {
105
- font-size: 6rem;
106
134
  order: 1;
107
135
  }
108
136
 
109
137
  .image {
110
138
  order: 2;
111
- width: 70%;
112
- max-height: 20rem;
113
139
  }
114
140
  }
115
141
 
116
- &.panel {
142
+ &.format--panel {
117
143
  gap: 4rem;
118
144
  justify-content: unset;
119
145
  padding-top: 8rem;
@@ -124,31 +150,65 @@ const imageSrc = computed(() => {
124
150
 
125
151
  .loader {
126
152
  order: 3;
127
- font-size: 6.4rem;
128
153
  }
129
154
 
130
155
  .image {
131
156
  order: 2;
132
- width: 80%;
133
157
  }
134
158
  }
135
159
 
136
- &.table {
137
- padding: 4rem;
138
- gap: 2.4rem;
160
+ &.format--page {
161
+ gap: 10rem;
139
162
 
140
163
  .content {
141
164
  order: 3;
142
165
  }
143
166
 
167
+ .loader {
168
+ order: 1;
169
+ }
170
+
144
171
  .image {
145
172
  order: 2;
146
- max-height: 20rem;
147
173
  }
174
+ }
148
175
 
176
+ &.size--extra-small {
149
177
  .loader {
150
- order: 1;
151
- font-size: 10rem;
178
+ font-size: 1.6rem;
179
+ }
180
+
181
+ .image {
182
+ max-height: 14rem;
183
+ }
184
+ }
185
+
186
+ &.size--small {
187
+ .loader {
188
+ font-size: 2.4rem;
189
+ }
190
+
191
+ .image {
192
+ max-height: 18rem;
193
+ }
194
+ }
195
+
196
+ &.size--medium {
197
+ .loader {
198
+ font-size: 6.4rem;
199
+ }
200
+
201
+ .image {
202
+ max-height: 30rem;
203
+ }
204
+ }
205
+
206
+ &.size--large {
207
+ .loader {
208
+ font-size: 9.6rem;
209
+ }
210
+ .image {
211
+ max-height: 50rem;
152
212
  }
153
213
  }
154
214
  }
@@ -3,7 +3,7 @@
3
3
  <li v-if="loading">
4
4
  <div class="loading">
5
5
  <UiLoader />
6
- <div>{{ t('loading-in-progress') }}</div>
6
+ <div>{{ t('loading') }}</div>
7
7
  </div>
8
8
  </li>
9
9
  <template v-else>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="ui-card" :class="{ horizontal }">
2
+ <div class="ui-card" :class="{ horizontal, 'has-error': hasError }">
3
3
  <slot />
4
4
  </div>
5
5
  </template>
@@ -7,6 +7,7 @@
7
7
  <script lang="ts" setup>
8
8
  defineProps<{
9
9
  horizontal?: boolean
10
+ hasError?: boolean
10
11
  }>()
11
12
 
12
13
  defineSlots<{
@@ -39,5 +40,9 @@ defineSlots<{
39
40
  &.horizontal {
40
41
  flex-direction: row;
41
42
  }
43
+
44
+ &.has-error {
45
+ background-color: var(--color-danger-background-selected);
46
+ }
42
47
  }
43
48
  </style>
@@ -5,9 +5,8 @@
5
5
  <slot />
6
6
  </component>
7
7
  <div v-if="hasMoreItems" class="footer">
8
- <span v-if="!isExpanded" class="typo-body-regular-small">{{ t('n-more', { n: remainingItems }) }}</span>
9
8
  <UiButton size="small" accent="brand" variant="tertiary" @click="isExpanded = !isExpanded">
10
- {{ isExpanded ? t('see-less') : t('see-all') }}
9
+ {{ isExpanded ? t('see-less') : t('see-n-more', { n: remainingItems }) }}
11
10
  </UiButton>
12
11
  </div>
13
12
  </div>
@@ -1,12 +1,14 @@
1
1
  import type { TreeNodeBase } from '@core/composables/tree/tree-node-base'
2
+ import { refDebounced } from '@vueuse/shared'
2
3
  import { computed, ref } from 'vue'
3
4
 
4
5
  export function useTreeFilter() {
5
6
  const filter = ref('')
6
- const hasFilter = computed(() => filter.value.trim().length > 0)
7
+ const debouncedFilter = refDebounced(filter, 500)
8
+ const hasFilter = computed(() => debouncedFilter.value.trim().length > 0)
7
9
 
8
10
  const predicate = (node: TreeNodeBase) =>
9
- hasFilter.value ? node.label.toLocaleLowerCase().includes(filter.value.toLocaleLowerCase()) : undefined
11
+ hasFilter.value ? node.label.toLocaleLowerCase().includes(debouncedFilter.value.toLocaleLowerCase()) : undefined
10
12
 
11
13
  return { filter, predicate }
12
14
  }
@@ -12,7 +12,7 @@ export const icons = defineIconPack({
12
12
 
13
13
  export type IconName = Exclude<keyof typeof icons, typeof ICON_SYMBOL>
14
14
 
15
- export type ObjectIconName = Extract<IconName, `object:${string}`>
15
+ export type ObjectIconName = Extract<IconName, `object:${string}:${string}`>
16
16
 
17
17
  export function icon<TName extends IconName>(name: TName): TName {
18
18
  return name
@@ -3,9 +3,12 @@ import { defineIcon } from '@core/packages/icon/define-icon.ts'
3
3
  import type { IconTransforms } from '@core/packages/icon/types.ts'
4
4
  import { createMapper } from '@core/packages/mapper/create-mapper.ts'
5
5
  import {
6
+ faArrowLeft,
6
7
  faBan,
8
+ faBoxArchive,
7
9
  faCheck,
8
10
  faCircle,
11
+ faClock,
9
12
  faDatabase,
10
13
  faDesktop,
11
14
  faMinus,
@@ -17,7 +20,6 @@ import {
17
20
  faServer,
18
21
  faStop,
19
22
  faTriangleExclamation,
20
- faWarehouse,
21
23
  faXmark,
22
24
  } from '@fortawesome/free-solid-svg-icons'
23
25
 
@@ -148,6 +150,32 @@ function getMainColor(state: string) {
148
150
  : 'var(--color-neutral-txt-primary)'
149
151
  }
150
152
 
153
+ const backupIcon = defineIcon({ icon: faBoxArchive })
154
+
155
+ const backupRepository = defineIcon([
156
+ { icon: backupIcon, translate: [0, -4], borderColor: defaultTransforms.borderColor, size: 10 },
157
+ { icon: backupIcon, translate: [-4, 4], borderColor: defaultTransforms.borderColor, size: 10 },
158
+ { icon: backupIcon, translate: [4, 4], borderColor: defaultTransforms.borderColor, size: 10 },
159
+ ])
160
+
161
+ const backupJob = defineIcon([
162
+ { icon: backupIcon, borderColor: defaultTransforms.borderColor },
163
+ { icon: faCircle, translate: [6, 6], borderColor: defaultTransforms.borderColor, size: 12 },
164
+ { icon: faArrowLeft, translate: [6, 6], color: 'var(--color-neutral-background-primary)', size: 8 },
165
+ ])
166
+
167
+ const backupSchedule = defineIcon([
168
+ { icon: backupIcon, borderColor: defaultTransforms.borderColor },
169
+ { icon: faCircle, translate: [6, 6], size: 12, color: 'var(--color-neutral-background-primary)' },
170
+ { icon: faClock, translate: [6, 6], size: 11 },
171
+ ])
172
+
173
+ const backupLog = defineIcon([
174
+ { icon: backupIcon, borderColor: defaultTransforms.borderColor },
175
+ { icon: faCircle, translate: [6, 6], borderColor: defaultTransforms.borderColor, size: 12 },
176
+ { icon: faPlay, translate: [6.5, 6], color: 'var(--color-neutral-background-primary)', size: 6 },
177
+ ])
178
+
151
179
  export const objectIcons = defineIconPack({
152
180
  vm: defineIcon([['running', 'halted', 'suspended', 'paused', 'muted']], state => [
153
181
  {
@@ -179,9 +207,12 @@ export const objectIcons = defineIconPack({
179
207
  ]),
180
208
  'backup-repository': defineIcon([['connected', 'disconnected']], state => [
181
209
  {
182
- icon: faWarehouse,
210
+ icon: backupRepository,
183
211
  color: getMainColor(state),
184
212
  },
185
213
  { icon: getStatusIcon(state) },
186
214
  ]),
215
+ 'backup-job': backupJob,
216
+ 'backup-schedule': backupSchedule,
217
+ 'backup-log': backupLog,
187
218
  })
@@ -313,7 +313,7 @@
313
313
  "load-average": "Průměrné vytížení",
314
314
  "load-now": "Načíst nyní",
315
315
  "loading-hosts": "Načítání hostitelů…",
316
- "loading-in-progress": "Probíhá načítání…",
316
+ "loading": "Probíhá načítání…",
317
317
  "locking-mode": "Režim zamykání",
318
318
  "locking-mode-default": "Výchozí režim zamykání",
319
319
  "log-out": "Odhlásit",
@@ -310,7 +310,7 @@
310
310
  "load-average": "Durchschnittsauslastung",
311
311
  "load-now": "Jetzt laden",
312
312
  "loading-hosts": "Lade Hosts…",
313
- "loading-in-progress": "Ladevorgang läuft…",
313
+ "loading": "Ladevorgang läuft…",
314
314
  "locking-mode": "Sperrmodus",
315
315
  "locking-mode-default": "Standard-Sperrmodus",
316
316
  "log-out": "Abmelden",
@@ -27,6 +27,7 @@
27
27
  "alarms": "Alarms",
28
28
  "all-done": "All done!",
29
29
  "all-good": "All good!",
30
+ "all-quiet-launchpad": "All quiet on the launchpad",
30
31
  "allow-self-signed-ssl": "You may need to allow self-signed SSL certificates in your browser",
31
32
  "api-error-details": "API Error details",
32
33
  "appearance": "Appearance",
@@ -105,7 +106,9 @@
105
106
  "console": "Console",
106
107
  "console-actions": "Console actions",
107
108
  "console-clipboard": "Console clipboard",
109
+ "console-offline": "Console offline",
108
110
  "console-unavailable": "Console unavailable",
111
+ "console-unavailable-reason": "The console is currently unavailable because the {type} is powered off.",
109
112
  "control-domain-memory": "Control domain memory",
110
113
  "copy": "Copy",
111
114
  "copy-all": "Copy all",
@@ -153,6 +156,7 @@
153
156
  "cpus": "CPUs",
154
157
  "crash-dump-storage-repository": "Crash dump storage repository",
155
158
  "create": "Create",
159
+ "created-by": "Created by",
156
160
  "created-on": "Created on",
157
161
  "cron-pattern": "Cron pattern",
158
162
  "custom-config": "Custom config",
@@ -193,6 +197,7 @@
193
197
  "edit-config": "Edit config",
194
198
  "enabled": "Enabled",
195
199
  "end-of-life": "End of life",
200
+ "engines-off": "Engines off, orbit stable",
196
201
  "eol": "EOL",
197
202
  "error": "Error",
198
203
  "error-no-data": "Error, can't collect data.",
@@ -236,6 +241,7 @@
236
241
  "go-back": "Go back",
237
242
  "gpus": "GPUs",
238
243
  "graphics-display": "Graphics & Display",
244
+ "guest-tools": "Guest tools",
239
245
  "gzip": "gzip",
240
246
  "hardware": "Hardware",
241
247
  "hardware-specifications": "Hardware specifications",
@@ -243,8 +249,11 @@
243
249
  "here": "Here",
244
250
  "high-availability": "High availability (HA)",
245
251
  "host": "Host",
252
+ "host-currently-shutdown": "This host is currently shut down, so we can’t retrieve any data.",
246
253
  "host-description": "Host description",
247
254
  "host-internal-networks": "Host internal networks",
255
+ "host-not-running": "Host not running",
256
+ "host-off": "Host is powered off",
248
257
  "host-status.halted": "Stopped",
249
258
  "host-status.running": "Running",
250
259
  "host-status.unknown": "Unknown",
@@ -322,6 +331,7 @@
322
331
  "language": "Language",
323
332
  "last": "Last",
324
333
  "last-n-runs": "Last {n} runs",
334
+ "last-run-number": "Last run #{n}",
325
335
  "last-week": "Last week",
326
336
  "learn-more": "Learn more",
327
337
  "license-socket": "License socket",
@@ -329,8 +339,8 @@
329
339
  "licensing": "Licensing",
330
340
  "load-average": "Load average",
331
341
  "load-now": "Load now",
342
+ "loading": "Loading…",
332
343
  "loading-hosts": "Loading hosts…",
333
- "loading-in-progress": "Loading in progress…",
334
344
  "locking-mode": "Locking mode",
335
345
  "locking-mode-default": "Default locking mode",
336
346
  "log-out": "Log out",
@@ -365,7 +375,6 @@
365
375
  "n-hosts": "1 host | {n} hosts",
366
376
  "n-hosts-awaiting-patch": "{n} host is awaiting this patch | {n} hosts are awaiting this patch",
367
377
  "n-missing": "{n} missing",
368
- "n-more": "{n} more",
369
378
  "n-percent": "{n}%",
370
379
  "n-vms": "1 VM | {n} VMs",
371
380
  "name": "Name",
@@ -391,11 +400,14 @@
391
400
  "news-name": "{name} news",
392
401
  "nic-type": "NIC type",
393
402
  "no-alarm-triggered": "No alarm triggered",
403
+ "no-alarms-detected": "No alarms detected",
394
404
  "no-backup-available": "No backup available",
395
405
  "no-config": "No configuration",
396
406
  "no-data": "No data",
407
+ "no-data-to-calculate": "No data to calculate",
397
408
  "no-network-detected": "No network detected",
398
409
  "no-pif-detected": "No PIF detected",
410
+ "no-pools-detected": "No pools detected",
399
411
  "no-result": "No result",
400
412
  "no-results": "No results",
401
413
  "no-selected-vm-can-be-exported": "No selected VM can be exported",
@@ -403,6 +415,7 @@
403
415
  "no-server-detected": "No server detected",
404
416
  "no-tasks": "No tasks",
405
417
  "no-vif-detected": "No VIF detected",
418
+ "no-vm-detected": "No VM detected",
406
419
  "none": "None",
407
420
  "not-found": "Not found",
408
421
  "not-running": "Not running",
@@ -425,6 +438,7 @@
425
438
  "password": "Password",
426
439
  "password-invalid": "Password invalid",
427
440
  "patches": "Patches",
441
+ "patches-up-to-date": "Patches up to date",
428
442
  "pause": "Pause",
429
443
  "physical-interface-status": "Physical interface status",
430
444
  "pick-template": "Pick template",
@@ -494,6 +508,7 @@
494
508
  "secure-boot": "Secure boot",
495
509
  "see-all": "See all",
496
510
  "see-less": "See less",
511
+ "see-n-more": "See {n} more",
497
512
  "select-compression": "Select a compression",
498
513
  "select-destination-host": "Select a destination host",
499
514
  "select-host": "Select host",
@@ -526,8 +541,11 @@
526
541
  "stacked-cpu-usage": "Stacked CPU usage",
527
542
  "stacked-ram-usage": "Stacked RAM usage",
528
543
  "start": "Start",
544
+ "start-console": "Start your {type} to access the console and resume operations!",
529
545
  "start-delay": "Start delay",
546
+ "start-host": "Start the host to resume operations, or check back later once it’s powered on.",
530
547
  "start-on-host": "Start on specific host",
548
+ "start-vm": "Start it to access its console, and configuration details.",
531
549
  "started": "Started",
532
550
  "state": "State",
533
551
  "static": "Static",
@@ -614,6 +632,9 @@
614
632
  "vm-is-running": "The VM is running",
615
633
  "vm-limit-topology": "VM limit topology",
616
634
  "vm-management": "VM management",
635
+ "vm-not-running": "VM not running",
636
+ "vm-off": "This virtual machine is powered off.",
637
+ "vm-shutdown": "VM is currently shut down",
617
638
  "vm-status.halted": "Halted",
618
639
  "vm-status.paused": "Paused",
619
640
  "vm-status.running": "Running",
@@ -306,7 +306,7 @@
306
306
  "load-average": "Carga media del sistema",
307
307
  "load-now": "Cargar ahora",
308
308
  "loading-hosts": "Cargando servidores…",
309
- "loading-in-progress": "Cargando…",
309
+ "loading": "Cargando…",
310
310
  "locking-mode": "Modo de bloqueo",
311
311
  "locking-mode-default": "Modo de bloqueo por defecto",
312
312
  "log-out": "Desconectarse",
@@ -265,7 +265,7 @@
265
265
  "load-average": "میانگین مصرف",
266
266
  "load-now": "اکنون بارگذاری کنید",
267
267
  "loading-hosts": "در حال بارگذاری میزبان ها…",
268
- "loading-in-progress": "بارگیری در حال انجام است…",
268
+ "loading": "بارگیری در حال انجام است…",
269
269
  "locking-mode": "حالت قفل کردن",
270
270
  "locking-mode-default": "حالت قفل کردن پیش‌فرض",
271
271
  "log-out": "خروج",