diff --git a/packages/xo-web/.editorconfig b/packages/xo-web/.editorconfig new file mode 100644 index 000000000..da21ef4c5 --- /dev/null +++ b/packages/xo-web/.editorconfig @@ -0,0 +1,65 @@ +# http://EditorConfig.org +# +# Julien Fontanet's configuration +# https://gist.github.com/julien-f/8096213 + +# Top-most EditorConfig file. +root = true + +# Common config. +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespaces = true + +# CoffeeScript +# +# https://github.com/polarmobile/coffeescript-style-guide/blob/master/README.md +[*.{,lit}coffee] +indent_size = 2 +indent_style = space + +# Markdown +[*.{md,mdwn,mdown,markdown}] +indent_size = 4 +indent_style = space + +# Package.json +# +# This indentation style is the one used by npm. +[/package.json] +indent_size = 2 +indent_style = space + +# Jade +[*.jade] +indent_size = 2 +indent_style = space + +# JavaScript +# +# Two spaces seems to be the standard most common style, at least in +# Node.js (http://nodeguide.com/style.html#tabs-vs-spaces). +[*.js] +indent_size = 2 +indent_style = space + +# Less +[*.less] +indent_size = 2 +indent_style = space + +# Sass +# +# Style used for http://libsass.com +[*.s[ac]ss] +indent_size = 2 +indent_style = space + +# YAML +# +# Only spaces are allowed. +[*.yaml] +indent_size = 2 +indent_style = space diff --git a/packages/xo-web/.eslintrc.js b/packages/xo-web/.eslintrc.js new file mode 100644 index 000000000..6a08f9d0a --- /dev/null +++ b/packages/xo-web/.eslintrc.js @@ -0,0 +1,12 @@ +module.exports = { + extends: ['standard', 'standard-jsx'], + globals: { + __DEV__: true, + }, + parser: 'babel-eslint', + rules: { + 'comma-dangle': ['error', 'always-multiline'], + 'no-var': 'error', + 'prefer-const': 'error', + }, +} diff --git a/packages/xo-web/.gitignore b/packages/xo-web/.gitignore new file mode 100644 index 000000000..062102753 --- /dev/null +++ b/packages/xo-web/.gitignore @@ -0,0 +1,9 @@ +/dist/ +/node_modules/ +/src/common/intl/locales/index.js +/src/common/themes/index.js + +npm-debug.log +npm-debug.log.* +pnpm-debug.log +pnpm-debug.log.* diff --git a/packages/xo-web/.npmignore b/packages/xo-web/.npmignore new file mode 100644 index 000000000..c31ee82cb --- /dev/null +++ b/packages/xo-web/.npmignore @@ -0,0 +1,10 @@ +/examples/ +example.js +example.js.map +*.example.js +*.example.js.map + +/test/ +/tests/ +*.spec.js +*.spec.js.map diff --git a/packages/xo-web/.prettierrc.js b/packages/xo-web/.prettierrc.js new file mode 100644 index 000000000..35fcafaf2 --- /dev/null +++ b/packages/xo-web/.prettierrc.js @@ -0,0 +1,4 @@ +module.exports = { + semi: false, + singleQuote: true, +} diff --git a/packages/xo-web/.travis.yml b/packages/xo-web/.travis.yml new file mode 100644 index 000000000..04ee3a2c1 --- /dev/null +++ b/packages/xo-web/.travis.yml @@ -0,0 +1,11 @@ +language: node_js +node_js: + - '6' + #- '4' # npm 3's flat tree is needed because some packages do not + # declare their deps correctly (e.g. chartist-plugin-tooltip) + +cache: yarn + +# Use containers. +# http://docs.travis-ci.com/user/workers/container-based-infrastructure/ +sudo: false diff --git a/packages/xo-web/CHANGELOG.md b/packages/xo-web/CHANGELOG.md new file mode 100644 index 000000000..d701d4748 --- /dev/null +++ b/packages/xo-web/CHANGELOG.md @@ -0,0 +1,1420 @@ +# ChangeLog + +## **5.15.0** (2017-12-29) + +### Enhancements + + * VDI resize online method removed in 7.3 [#2542](https://github.com/vatesfr/xo-web/issues/2542) + * Smart replace VDI.pool_migrate removed from XenServer 7.3 Free [#2541](https://github.com/vatesfr/xo-web/issues/2541) + * New memory constraints in XenServer 7.3 [#2540](https://github.com/vatesfr/xo-web/issues/2540) + * Link to Settings/Logs for admins in error notifications [#2516](https://github.com/vatesfr/xo-web/issues/2516) + * [Self Service] Do not use placehodlers to describe inputs [#2509](https://github.com/vatesfr/xo-web/issues/2509) + * Obfuscate password in log in LDAP plugin test [#2506](https://github.com/vatesfr/xo-web/issues/2506) + * Log rotation [#2492](https://github.com/vatesfr/xo-web/issues/2492) + * Continuous Replication TAG [#2473](https://github.com/vatesfr/xo-web/issues/2473) + * Graphs in VM list view [#2469](https://github.com/vatesfr/xo-web/issues/2469) + * [Delta Backups] Do not include merge duration in transfer speed stat [#2426](https://github.com/vatesfr/xo-web/issues/2426) + * Warning for disperse mode [#2537](https://github.com/vatesfr/xo-web/issues/2537) + +### Bugs + + * VM console doesn't work when using IPv6 in URL [#2530](https://github.com/vatesfr/xo-web/issues/2530) + * Retention issue with failed basic backup [#2524](https://github.com/vatesfr/xo-web/issues/2524) + * [VM/Advanced] Check that the autopower on setting is working [#2489](https://github.com/vatesfr/xo-web/issues/2489) + * Cloud config drive create fail on XenServer < 7 [#2478](https://github.com/vatesfr/xo-web/issues/2478) + * VM create fails due to missing vGPU id [#2466](https://github.com/vatesfr/xo-web/issues/2466) + + +## **5.14.0** (2017-10-31) + +### Enhancements + + * VM snapshot description display [#2458](https://github.com/vatesfr/xo-web/issues/2458) + * [Home] Ability to sort VM by number of snapshots [#2450](https://github.com/vatesfr/xo-web/issues/2450) + * Display XS version in host view [#2439](https://github.com/vatesfr/xo-web/issues/2439) + * [File restore]: Clarify the possibility to select multiple files [#2438](https://github.com/vatesfr/xo-web/issues/2438) + * [Continuous Replication] Time in replicated VMs [#2431](https://github.com/vatesfr/xo-web/issues/2431) + * [SortedTable] Active page in URL param [#2405](https://github.com/vatesfr/xo-web/issues/2405) + * replace all '...' with the UTF-8 equivalent [#2391](https://github.com/vatesfr/xo-web/issues/2391) + * [SortedTable] Explicit when no items [#2388](https://github.com/vatesfr/xo-web/issues/2388) + * Handle patching licenses [#2382](https://github.com/vatesfr/xo-web/issues/2382) + * Credential leaking in logs for messages regarding invalid credentials and "too fast authentication" [#2363](https://github.com/vatesfr/xo-web/issues/2363) + * [SortedTable] Keyboard support [#2330](https://github.com/vatesfr/xo-web/issues/2330) + * token.create should accept an expiration [#1769](https://github.com/vatesfr/xo-web/issues/1769) + * On updater error, display link to documentation [#1610](https://github.com/vatesfr/xo-web/issues/1610) +* Add basic vGPU support [#2413](https://github.com/vatesfr/xo-web/issues/2413) + * Storage View - Disk Tab - real disk usage [#2475](https://github.com/vatesfr/xo-web/issues/2475) + +### Bugs + + * Config drive - Custom config not working properly [#2449](https://github.com/vatesfr/xo-web/issues/2449) + * Snapshot sorted table breaks copyVm [#2446](https://github.com/vatesfr/xo-web/issues/2446) + * [vm/snapshots] Incorrect default sort order [#2442](https://github.com/vatesfr/xo-web/issues/2442) + * [Backups/Jobs] Incorrect months mapping [#2427](https://github.com/vatesfr/xo-web/issues/2427) + * [Xapi#barrier()] Not compatible with XenServer < 6.1 [#2418](https://github.com/vatesfr/xo-web/issues/2418) + * [SortedTable] Change page when no more items on the page [#2401](https://github.com/vatesfr/xo-web/issues/2401) + * Review and fix creating a VM from a snapshot [#2343](https://github.com/vatesfr/xo-web/issues/2343) + * Unable to edit / save restored backup job [#1922](https://github.com/vatesfr/xo-web/issues/1922) + + +## **5.13.0** (2017-09-29) + +### Enhancements + + * replace all '...' with the UTF-8 equivalent [#2391](https://github.com/vatesfr/xo-web/issues/2391) + * [SortedTable] Explicit when no items [#2388](https://github.com/vatesfr/xo-web/issues/2388) + * Auto select iqn or lun if there is only one [#2379](https://github.com/vatesfr/xo-web/issues/2379) + * [Sparklines] Hide points [#2370](https://github.com/vatesfr/xo-web/issues/2370) + * Allow xo-server-recover-account to generate a random password [#2360](https://github.com/vatesfr/xo-web/issues/2360) + * Add disk in existing VM as self user [#2348](https://github.com/vatesfr/xo-web/issues/2348) + * Sorted table for Settings/server [#2340](https://github.com/vatesfr/xo-web/issues/2340) + * Sign in should be case insensitive [#2337](https://github.com/vatesfr/xo-web/issues/2337) + * [SortedTable] Extend checkbox click to whole column [#2329](https://github.com/vatesfr/xo-web/issues/2329) + * [SortedTable] Ability to select all items (across pages) [#2324](https://github.com/vatesfr/xo-web/issues/2324) + * [SortedTable] Range selection [#2323](https://github.com/vatesfr/xo-web/issues/2323) + * Warning on SMB remote creation [#2316](https://github.com/vatesfr/xo-web/issues/2316) + * [Home | SortedTable] Add link to syntax doc in the filter input [#2305](https://github.com/vatesfr/xo-web/issues/2305) + * [SortedTable] Add optional binding of filter to an URL query [#2301](https://github.com/vatesfr/xo-web/issues/2301) + * [Home][Keyboard navigation] Allow selecting the objects [#2214](https://github.com/vatesfr/xo-web/issues/2214) + * SR view / Disks: option to display non managed VDIs [#1724](https://github.com/vatesfr/xo-web/issues/1724) + * Continuous Replication Retention [#1692](https://github.com/vatesfr/xo-web/issues/1692) + +### Bugs + + * iSCSI issue on LUN selector [#2374](https://github.com/vatesfr/xo-web/issues/2374) + * Errors in VM copy are not properly reported [#2347](https://github.com/vatesfr/xo-web/issues/2347) + * Removing a PIF IP fails [#2346](https://github.com/vatesfr/xo-web/issues/2346) + * Review and fix creating a VM from a snapshot [#2343](https://github.com/vatesfr/xo-web/issues/2343) + * iSCSI LUN Detection fails with authentification [#2339](https://github.com/vatesfr/xo-web/issues/2339) + * Fix PoolActionBar to add a new SR [#2307](https://github.com/vatesfr/xo-web/issues/2307) + * [VM migration] Error if default SR not accessible to target host [#2180](https://github.com/vatesfr/xo-web/issues/2180) + * A job shouldn't executable more than once at the same time [#2053](https://github.com/vatesfr/xo-web/issues/2053) + +## **5.12.0** (2017-08-31) + +### Enhancements + + * PIF selector with physical status [#2326](https://github.com/vatesfr/xo-web/issues/2326) + * [SortedTable] Range selection [#2323](https://github.com/vatesfr/xo-web/issues/2323) + * Self service filter for home/VM view [#2303](https://github.com/vatesfr/xo-web/issues/2303) + * SR/Disks Display total of VDIs to coalesce [#2300](https://github.com/vatesfr/xo-web/issues/2300) + * Pool filter in the task view [#2293](https://github.com/vatesfr/xo-web/issues/2293) + * "Loading" while fetching objects [#2285](https://github.com/vatesfr/xo-web/issues/2285) + * [SortedTable] Add grouped actions feature [#2276](https://github.com/vatesfr/xo-web/issues/2276) + * Add a filter to the backups' log [#2246](https://github.com/vatesfr/xo-web/issues/2246) + * It should not be possible to migrate a halted VM. [#2233](https://github.com/vatesfr/xo-web/issues/2233) + * [Home][Keyboard navigation] Allow selecting the objects [#2214](https://github.com/vatesfr/xo-web/issues/2214) + * Allow to set pool master [#2213](https://github.com/vatesfr/xo-web/issues/2213) + * Continuous Replication Retention [#1692](https://github.com/vatesfr/xo-web/issues/1692) + +### Bugs + + * Home pagination bug [#2310](https://github.com/vatesfr/xo-web/issues/2310) + * Fix PoolActionBar to add a new SR [#2307](https://github.com/vatesfr/xo-web/issues/2307) + * VM snapshots are not correctly deleted [#2304](https://github.com/vatesfr/xo-web/issues/2304) + * Parallel deletion of VMs fails [#2297](https://github.com/vatesfr/xo-web/issues/2297) + * Continous replication create multiple zombie disks [#2292](https://github.com/vatesfr/xo-web/issues/2292) + * Add user to Group issue [#2196](https://github.com/vatesfr/xo-web/issues/2196) + * [VM migration] Error if default SR not accessible to target host [#2180](https://github.com/vatesfr/xo-web/issues/2180) + +## **5.11.0** (2017-07-31) + +### Enhancements + +- Storage VHD chain health [\#2178](https://github.com/vatesfr/xo-web/issues/2178) + +### Bug fixes + +- No web VNC console [\#2258](https://github.com/vatesfr/xo-web/issues/2258) +- Patching issues [\#2254](https://github.com/vatesfr/xo-web/issues/2254) +- Advanced button in VM creation for self service user [\#2202](https://github.com/vatesfr/xo-web/issues/2202) +- Hide "new VM" menu entry if not admin or not self service user [\#2191](https://github.com/vatesfr/xo-web/issues/2191) + +## **5.10.0** (2017-06-30) + +### Enhancements + +- Improve backup log display [\#2239](https://github.com/vatesfr/xo-web/issues/2239) +- Patch SR detection improvement [\#2215](https://github.com/vatesfr/xo-web/issues/2215) +- Less strict coalesce detection [\#2207](https://github.com/vatesfr/xo-web/issues/2207) +- IP pool UI improvement [\#2203](https://github.com/vatesfr/xo-web/issues/2203) +- Ability to clear "Auto power on" flag for DR-ed VM [\#2097](https://github.com/vatesfr/xo-web/issues/2097) +- [Delta backup restoration] Choose SR for each VDIs [\#2070](https://github.com/vatesfr/xo-web/issues/2070) +- Ability to forget an host (even if no longer present) [\#1934](https://github.com/vatesfr/xo-web/issues/1934) + +### Bug fixes + +- Cross pool migrate fail [\#2248](https://github.com/vatesfr/xo-web/issues/2248) +- ActionButtons with modals stay in pending state forever [\#2222](https://github.com/vatesfr/xo-web/issues/2222) +- Permission issue for a user on self service VMs [\#2212](https://github.com/vatesfr/xo-web/issues/2212) +- Self-Service resource loophole [\#2198](https://github.com/vatesfr/xo-web/issues/2198) +- Backup log no longer shows the name of destination VM [\#2195](https://github.com/vatesfr/xo-web/issues/2195) +- State not restored when exiting modal dialog [\#2194](https://github.com/vatesfr/xo-web/issues/2194) +- [Xapi#exportDeltaVm] Cannot read property 'managed' of undefined [\#2189](https://github.com/vatesfr/xo-web/issues/2189) +- VNC keyboard layout change [\#404](https://github.com/vatesfr/xo-web/issues/404) + +## **5.9.0** (2017-05-31) + +### Enhancements + +- Allow DR to remove previous backup first [\#2157](https://github.com/vatesfr/xo-web/issues/2157) +- Feature request - add amount of RAM to memory bars [\#2149](https://github.com/vatesfr/xo-web/issues/2149) +- Make the acceptability of invalid certificates configurable [\#2138](https://github.com/vatesfr/xo-web/issues/2138) +- label of VM names in tasks link [\#2135](https://github.com/vatesfr/xo-web/issues/2135) +- Backup report timezone [\#2133](https://github.com/vatesfr/xo-web/issues/2133) +- xo-server-recover-account [\#2129](https://github.com/vatesfr/xo-web/issues/2129) +- Detect disks attached to control domain [\#2126](https://github.com/vatesfr/xo-web/issues/2126) +- Add task description in Tasks view [\#2125](https://github.com/vatesfr/xo-web/issues/2125) +- Host reboot warning after patching for 7.1 [\#2124](https://github.com/vatesfr/xo-web/issues/2124) +- Continuous Replication - possibility run VM without a clone [\#2119](https://github.com/vatesfr/xo-web/issues/2119) +- Unreachable host should be detected [\#2099](https://github.com/vatesfr/xo-web/issues/2099) +- Orange icon when host is is disabled [\#2098](https://github.com/vatesfr/xo-web/issues/2098) +- Enhanced backup report logs [\#2096](https://github.com/vatesfr/xo-web/issues/2096) +- Only show failures when configured to report on failures [\#2095](https://github.com/vatesfr/xo-web/issues/2095) +- "Add all" button in self service [\#2081](https://github.com/vatesfr/xo-web/issues/2081) +- Patch and pack mechanism changed on Ely [\#2058](https://github.com/vatesfr/xo-web/issues/2058) +- Tip or ask people to patch from pool view [\#2057](https://github.com/vatesfr/xo-web/issues/2057) +- File restore - Remind compatible backup [\#1930](https://github.com/vatesfr/xo-web/issues/1930) +- Reporting for halted vm time [\#1613](https://github.com/vatesfr/xo-web/issues/1613) +- Add standalone XS server to a pool and patch it to the pool level [\#878](https://github.com/vatesfr/xo-web/issues/878) +- Add Cores-per-sockets [\#130](https://github.com/vatesfr/xo-web/issues/130) + +### Bug fixes + +- VM creation is broken for non-admins [\#2168](https://github.com/vatesfr/xo-web/issues/2168) +- Can't create cloud config drive [\#2162](https://github.com/vatesfr/xo-web/issues/2162) +- Select is "moving" [\#2142](https://github.com/vatesfr/xo-web/issues/2142) +- Select issue for affinity host [\#2141](https://github.com/vatesfr/xo-web/issues/2141) +- Dashboard Storage Usage incorrect [\#2123](https://github.com/vatesfr/xo-web/issues/2123) +- Detect unmerged *base copy* and prevent too long chains [\#2047](https://github.com/vatesfr/xo-web/issues/2047) + + +## **5.8.0** (2017-04-28) + +### Enhancements + +- Limit About view info for non-admins [\#2109](https://github.com/vatesfr/xo-web/issues/2109) +- Enabling/disabling boot device on HVM VM [\#2105](https://github.com/vatesfr/xo-web/issues/2105) +- Filter: Hide snapshots in SR disk view [\#2102](https://github.com/vatesfr/xo-web/issues/2102) +- Smarter XOSAN install [\#2084](https://github.com/vatesfr/xo-web/issues/2084) +- PL translation [\#2079](https://github.com/vatesfr/xo-web/issues/2079) +- Remove the "share this VM" option if not in self service [\#2061](https://github.com/vatesfr/xo-web/issues/2061) +- "connected" status graphics are not the same on the host storage and networking tabs [\#2060](https://github.com/vatesfr/xo-web/issues/2060) +- Ability to view and edit `vga` and `videoram` fields in VM view [\#158](https://github.com/vatesfr/xo-web/issues/158) +- Performances [\#1](https://github.com/vatesfr/xen-api/issues/1) + +### Bug fixes + +- Dashboard display issues [\#2108](https://github.com/vatesfr/xo-web/issues/2108) +- Dashboard CPUs Usage [\#2105](https://github.com/vatesfr/xo-web/issues/2105) +- [Dashboard/Overview] Warning [\#2090](https://github.com/vatesfr/xo-web/issues/2090) +- VM creation displays all networks [\#2086](https://github.com/vatesfr/xo-web/issues/2086) +- Cannot change HA mode for a VM [\#2080](https://github.com/vatesfr/xo-web/issues/2080) +- [Smart backup] Tags selection does not work [\#2077](https://github.com/vatesfr/xo-web/issues/2077) +- [Backup jobs] Timeout should be in seconds, not milliseconds [\#2076](https://github.com/vatesfr/xo-web/issues/2076) +- Missing VM templates [\#2075](https://github.com/vatesfr/xo-web/issues/2075) +- [transport-email] From header not set [\#2074](https://github.com/vatesfr/xo-web/issues/2074) +- Missing objects should be displayed in backup edition [\#2052](https://github.com/vatesfr/xo-web/issues/2052) + +## **5.7.0** (2017-03-31) + +### Enhancements + +- Improve ActionButton error reporting [\#2048](https://github.com/vatesfr/xo-web/issues/2048) +- Home view master checkbox UI issue [\#2027](https://github.com/vatesfr/xo-web/issues/2027) +- HU Translation [\#2019](https://github.com/vatesfr/xo-web/issues/2019) +- [Usage report] Add name for all objects [\#2017](https://github.com/vatesfr/xo-web/issues/2017) +- [Home] Improve inter-types linkage [\#2012](https://github.com/vatesfr/xo-web/issues/2012) +- Remove bootable checkboxes in VM creation [\#2007](https://github.com/vatesfr/xo-web/issues/2007) +- Do not display bootable toggles for disks of non-PV VMs [\#1996](https://github.com/vatesfr/xo-web/issues/1996) +- Try to match network VLAN for VM migration modal [\#1990](https://github.com/vatesfr/xo-web/issues/1990) +- [Usage reports] Add VM names in addition to UUIDs [\#1984](https://github.com/vatesfr/xo-web/issues/1984) +- Host affinity in "advanced" VM creation [\#1983](https://github.com/vatesfr/xo-web/issues/1983) +- Add job tag in backup logs [\#1982](https://github.com/vatesfr/xo-web/issues/1982) +- Possibility to add a label/description to servers [\#1965](https://github.com/vatesfr/xo-web/issues/1965) +- Possibility to create shared VM in a resource set [\#1964](https://github.com/vatesfr/xo-web/issues/1964) +- Clearer display of disabled (backup) jobs [\#1958](https://github.com/vatesfr/xo-web/issues/1958) +- Job should have a configurable timeout [\#1956](https://github.com/vatesfr/xo-web/issues/1956) +- Sort failed VMs in backup report [\#1950](https://github.com/vatesfr/xo-web/issues/1950) +- Support for UNIX socket path [\#1944](https://github.com/vatesfr/xo-web/issues/1944) +- Interface - Host Patching - Button Verbiage [\#1911](https://github.com/vatesfr/xo-web/issues/1911) +- Display if a VM is in Self Service (and which group) [\#1905](https://github.com/vatesfr/xo-web/issues/1905) +- Install supplemental pack on a whole pool [\#1896](https://github.com/vatesfr/xo-web/issues/1896) +- Allow VM snapshots with ACLs [\#1865](https://github.com/vatesfr/xo-web/issues/1886) +- Icon to indicate if a snapshot is quiesce [\#1858](https://github.com/vatesfr/xo-web/issues/1858) +- Pool Ips input too permissive [\#1731](https://github.com/vatesfr/xo-web/issues/1731) +- Select is going on top after each choice [\#1359](https://github.com/vatesfr/xo-web/issues/1359) + +### Bug fixes + +- Missing objects should be displayed in backup edition [\#2052](https://github.com/vatesfr/xo-web/issues/2052) +- Search bar content changes while typing [\#2035](https://github.com/vatesfr/xo-web/issues/2035) +- VM.$guest_metrics.PV_drivers_up_to_date is deprecated in XS 7.1 [\#2024](https://github.com/vatesfr/xo-web/issues/2024) +- Bootable flag selection checkbox for extra disk not fetched [\#1994](https://github.com/vatesfr/xo-web/issues/1994) +- Home view − Changing type must reset paging [\#1993](https://github.com/vatesfr/xo-web/issues/1993) +- XOSAN menu item should only be displayed to admins [\#1968](https://github.com/vatesfr/xo-web/issues/1968) +- Object type change are not correctly handled in UI [\#1967](https://github.com/vatesfr/xo-web/issues/1967) +- VM creation is stuck when using ISO/DVD as install method [\#1966](https://github.com/vatesfr/xo-web/issues/1966) +- Install pack on whole pool fails [\#1957](https://github.com/vatesfr/xo-web/issues/1957) +- Consoles are broken in next-release [\#1954](https://github.com/vatesfr/xo-web/issues/1954) +- [VHD merge] Increase BAT when necessary [\#1939](https://github.com/vatesfr/xo-web/issues/1939) +- Issue on VM restore time [\#1936](https://github.com/vatesfr/xo-web/issues/1936) +- Two remotes should not be able to have the same name [\#1879](https://github.com/vatesfr/xo-web/issues/1879) +- Selfservice limits not honored after VM creation [\#1695](https://github.com/vatesfr/xo-web/issues/1695) + +## **5.6.0** (2017-01-27) + +Reporting, LVM File level restore. + +### Enhancements + +- Do not stop patches install if already applied [\#1904](https://github.com/vatesfr/xo-web/issues/1904) +- Improve scheduling UI [\#1893](https://github.com/vatesfr/xo-web/issues/1893) +- Smart backup and tag [\#1885](https://github.com/vatesfr/xo-web/issues/1885) +- Missing embeded API documention [\#1882](https://github.com/vatesfr/xo-web/issues/1882) +- Add local DVD in CD selector [\#1880](https://github.com/vatesfr/xo-web/issues/1880) +- File level restore for LVM [\#1878](https://github.com/vatesfr/xo-web/issues/1878) +- Restore multiple files from file level restore [\#1877](https://github.com/vatesfr/xo-web/issues/1877) +- Add a VM tab for host & pool views [\#1864](https://github.com/vatesfr/xo-web/issues/1864) +- Icon to indicate if a snapshot is quiesce [\#1858](https://github.com/vatesfr/xo-web/issues/1858) +- UI for disconnect hosts comp [\#1833](https://github.com/vatesfr/xo-web/issues/1833) +- Eject all xs-guest.iso in a pool [\#1798](https://github.com/vatesfr/xo-web/issues/1798) +- Display installed supplemental pack on host [\#1506](https://github.com/vatesfr/xo-web/issues/1506) +- Install supplemental pack on host comp [\#1460](https://github.com/vatesfr/xo-web/issues/1460) +- Pool-wide combined stats [\#1324](https://github.com/vatesfr/xo-web/issues/1324) + +### Bug fixes + +- IP-address not released when VM removed [\#1906](https://github.com/vatesfr/xo-web/issues/1906) +- Interface broken due to new Bootstrap Alpha [\#1871](https://github.com/vatesfr/xo-web/issues/1871) +- Self service recompute all limits broken [\#1866](https://github.com/vatesfr/xo-web/issues/1866) +- Patch not found error for XS 6.5 [\#1863](https://github.com/vatesfr/xo-web/issues/1863) +- Convert To Template issues [\#1855](https://github.com/vatesfr/xo-web/issues/1855) +- Removing PIF seems to fail [\#1853](https://github.com/vatesfr/xo-web/issues/1853) +- Depth should be >= 1 in backup creation [\#1851](https://github.com/vatesfr/xo-web/issues/1851) +- Wrong link in Dashboard > Health [\#1850](https://github.com/vatesfr/xo-web/issues/1850) +- Incorrect file dates shown in new File Restore feature [\#1840](https://github.com/vatesfr/xo-web/issues/1840) +- IP allocation problem [\#1747](https://github.com/vatesfr/xo-web/issues/1747) +- Selfservice limits not honored after VM creation [\#1695](https://github.com/vatesfr/xo-web/issues/1695) + +## **5.5.0** (2016-12-20) + +File level restore. + +### Enhancements + +- Better auto select network when migrate VM [\#1788](https://github.com/vatesfr/xo-web/issues/1788) +- Plugin for passive backup job reporting in Nagios [\#1664](https://github.com/vatesfr/xo-web/issues/1664) +- File level restore for delta backup [\#1590](https://github.com/vatesfr/xo-web/issues/1590) +- Better select filters for ACLs [\#1515](https://github.com/vatesfr/xo-web/issues/1515) +- All pools and "negative" filters [\#1503](https://github.com/vatesfr/xo-web/issues/1503) +- VM copy with disk selection [\#826](https://github.com/vatesfr/xo-web/issues/826) +- Disable metadata exports [\#1818](https://github.com/vatesfr/xo-web/issues/1818) + +### Bug fixes + +- Tool small selector [\#1832](https://github.com/vatesfr/xo-web/issues/1832) +- Replication does not work from a VM created by a CR or delta backup [\#1811](https://github.com/vatesfr/xo-web/issues/1811) +- Can't add a SSH key in VM creation [\#1805](https://github.com/vatesfr/xo-web/issues/1805) +- Issue when no default SR in a pool [\#1804](https://github.com/vatesfr/xo-web/issues/1804) +- XOA doesn't refresh after an update anymore [\#1801](https://github.com/vatesfr/xo-web/issues/1801) +- Shortcuts not inhibited on inputs on Safari [\#1691](https://github.com/vatesfr/xo-web/issues/1691) + +## **5.4.0** (2016-11-23) + +### Enhancements + +- XML display in alerts [\#1776](https://github.com/vatesfr/xo-web/issues/1776) +- Remove some view for non admin users [\#1773](https://github.com/vatesfr/xo-web/issues/1773) +- Complex matcher should support matching boolean values [\#1768](https://github.com/vatesfr/xo-web/issues/1768) +- Home SR view [\#1764](https://github.com/vatesfr/xo-web/issues/1764) +- Filter on tag click [\#1763](https://github.com/vatesfr/xo-web/issues/1763) +- Testable plugins [\#1749](https://github.com/vatesfr/xo-web/issues/1749) +- Backup/Restore Design fix. [\#1734](https://github.com/vatesfr/xo-web/issues/1734) +- Display the owner of a \(backup\) job [\#1733](https://github.com/vatesfr/xo-web/issues/1733) +- Use paginated table for backup jobs [\#1726](https://github.com/vatesfr/xo-web/issues/1726) +- SR view / Disks: should display snapshot VDIs [\#1723](https://github.com/vatesfr/xo-web/issues/1723) +- Restored VM should have an identifiable name [\#1719](https://github.com/vatesfr/xo-web/issues/1719) +- If host reboot action returns NO\_HOSTS\_AVAILABLE, ask to force [\#1717](https://github.com/vatesfr/xo-web/issues/1717) +- Hide xo-server timezone in backups [\#1706](https://github.com/vatesfr/xo-web/issues/1706) +- Enable hyperlink for Hostname for Issues [\#1700](https://github.com/vatesfr/xo-web/issues/1700) +- Pool/network - Modify column [\#1696](https://github.com/vatesfr/xo-web/issues/1696) +- UI - Plugins - Display a message if no plugins [\#1670](https://github.com/vatesfr/xo-web/issues/1670) +- Display warning/error for delta backup on XS older than 6.5 [\#1647](https://github.com/vatesfr/xo-web/issues/1647) +- XO without internet access doesn't work [\#1629](https://github.com/vatesfr/xo-web/issues/1629) +- Improve backup restore view [\#1609](https://github.com/vatesfr/xo-web/issues/1609) +- UI Enhancement - Acronym for dummy [\#1604](https://github.com/vatesfr/xo-web/issues/1604) +- Slack XO plugin for backup report [\#1593](https://github.com/vatesfr/xo-web/issues/1593) +- Expose XAPI exceptions in the UI [\#1481](https://github.com/vatesfr/xo-web/issues/1481) +- Running VMs in the host overview, all VMs in the pool overview [\#1432](https://github.com/vatesfr/xo-web/issues/1432) +- Move location of NFS mount point [\#1405](https://github.com/vatesfr/xo-web/issues/1405) +- Home: Pool list - additionnal informations for pool [\#1226](https://github.com/vatesfr/xo-web/issues/1226) +- Modify VLAN of an existing network [\#1092](https://github.com/vatesfr/xo-web/issues/1092) +- Wrong instructions for CLI upgrade [\#787](https://github.com/vatesfr/xo-web/issues/787) +- Ability to export/import XO config [\#786](https://github.com/vatesfr/xo-web/issues/786) +- Test button for transport-email plugin [\#697](https://github.com/vatesfr/xo-web/issues/697) +- Merge `scheduler` API into `schedule` [\#664](https://github.com/vatesfr/xo-web/issues/664) + +### Bug fixes + +- Should jobs be accessible to non admins? [\#1759](https://github.com/vatesfr/xo-web/issues/1759) +- Schedules deletion is not working [\#1737](https://github.com/vatesfr/xo-web/issues/1737) +- Editing a job from the jobs overview page does not work [\#1736](https://github.com/vatesfr/xo-web/issues/1736) +- Editing a schedule from jobs overview does not work [\#1728](https://github.com/vatesfr/xo-web/issues/1728) +- ACLs not correctly imported [\#1722](https://github.com/vatesfr/xo-web/issues/1722) +- Some Bootstrap style broken [\#1721](https://github.com/vatesfr/xo-web/issues/1721) +- Not properly sign out on auth token expiration [\#1711](https://github.com/vatesfr/xo-web/issues/1711) +- Hosts//network status is incorrect [\#1702](https://github.com/vatesfr/xo-web/issues/1702) +- Patches application fails "Found : Moved Temporarily" [\#1701](https://github.com/vatesfr/xo-web/issues/1701) +- Password generation for user creation is not working [\#1678](https://github.com/vatesfr/xo-web/issues/1678) +- \#/dashboard/health Remove All Orphaned VDIs [\#1622](https://github.com/vatesfr/xo-web/issues/1622) +- Create a new SR - CIFS/SAMBA Broken [\#1615](https://github.com/vatesfr/xo-web/issues/1615) +- xo-cli --list-objects: truncated output ? 64k buffer limitation ? [\#1356](https://github.com/vatesfr/xo-web/issues/1356) + +## **5.3.0** (2016-10-20) + +### Enhancements + +- Missing favicon [\#1660](https://github.com/vatesfr/xo-web/issues/1660) +- ipPools quota [\#1565](https://github.com/vatesfr/xo-web/issues/1565) +- Dashboard - orphaned VDI [\#1654](https://github.com/vatesfr/xo-web/issues/1654) +- Stats in home/host view when expanded [\#1634](https://github.com/vatesfr/xo-web/issues/1634) +- Bar for used and total RAM on home pool view [\#1625](https://github.com/vatesfr/xo-web/issues/1625) +- Can't translate some text [\#1624](https://github.com/vatesfr/xo-web/issues/1624) +- Dynamic RAM allocation at creation time [\#1603](https://github.com/vatesfr/xo-web/issues/1603) +- Display memory bar in home/host view [\#1616](https://github.com/vatesfr/xo-web/issues/1616) +- Improve keyboard navigation [\#1578](https://github.com/vatesfr/xo-web/issues/1578) +- Strongly suggest to install the guest tools [\#1575](https://github.com/vatesfr/xo-web/issues/1575) +- Missing tooltip [\#1568](https://github.com/vatesfr/xo-web/issues/1568) +- Emphasize already used ips in ipPools [\#1566](https://github.com/vatesfr/xo-web/issues/1566) +- Change "missing feature message" for non-admins [\#1564](https://github.com/vatesfr/xo-web/issues/1564) +- Allow VIF edition [\#1446](https://github.com/vatesfr/xo-web/issues/1446) +- Disable browser autocomplete on credentials on the Update page [\#1304](https://github.com/vatesfr/xo-web/issues/1304) +- keyboard shortcuts [\#1279](https://github.com/vatesfr/xo-web/issues/1279) +- Add network bond creation [\#876](https://github.com/vatesfr/xo-web/issues/876) +- `pool.setDefaultSr\(\)` should not require `pool` param [\#1558](https://github.com/vatesfr/xo-web/issues/1558) +- Select default SR [\#1554](https://github.com/vatesfr/xo-web/issues/1554) +- No error message when I exceed my resource set quota [\#1541](https://github.com/vatesfr/xo-web/issues/1541) +- Hide some buttons for self service VMs [\#1539](https://github.com/vatesfr/xo-web/issues/1539) +- Add Job ID to backup schedules [\#1534](https://github.com/vatesfr/xo-web/issues/1534) +- Correct name for VM selector with templates [\#1530](https://github.com/vatesfr/xo-web/issues/1530) +- Help text when no matches for a filter [\#1517](https://github.com/vatesfr/xo-web/issues/1517) +- Icon or tooltip to allow VDI migration in VM disk view [\#1512](https://github.com/vatesfr/xo-web/issues/1512) +- Create a snapshot before restoring one [\#1445](https://github.com/vatesfr/xo-web/issues/1445) +- Auto power on setting at creation time [\#1444](https://github.com/vatesfr/xo-web/issues/1444) +- local remotes should be avoided if possible [\#1441](https://github.com/vatesfr/xo-web/issues/1441) +- Self service edition unclear [\#1429](https://github.com/vatesfr/xo-web/issues/1429) +- Avoid "\_" char in job tag name [\#1414](https://github.com/vatesfr/xo-web/issues/1414) +- Display message if host reboot needed to apply patches [\#1352](https://github.com/vatesfr/xo-web/issues/1352) +- Color code on host PIF stats can be misleading [\#1265](https://github.com/vatesfr/xo-web/issues/1265) +- Sign in page is not rendered correctly [\#1161](https://github.com/vatesfr/xo-web/issues/1161) +- Template management [\#1091](https://github.com/vatesfr/xo-web/issues/1091) +- On pool view: collapse network list [\#1461](https://github.com/vatesfr/xo-web/issues/1461) +- Alert when trying to reboot/halt the pool master XS [\#1458](https://github.com/vatesfr/xo-web/issues/1458) +- Adding tooltip on Home page [\#1456](https://github.com/vatesfr/xo-web/issues/1456) +- Docker container management functionality missing from v5 [\#1442](https://github.com/vatesfr/xo-web/issues/1442) +- bad error message - delete snapshot [\#1433](https://github.com/vatesfr/xo-web/issues/1433) +- Create tag during VM creation [\#1431](https://github.com/vatesfr/xo-web/issues/1431) + +### Bug fixes + +- Display issues on plugin array edition [\#1663](https://github.com/vatesfr/xo-web/issues/1663) +- Import of delta backups fails [\#1656](https://github.com/vatesfr/xo-web/issues/1656) +- Host - Missing IP config for PIF [\#1651](https://github.com/vatesfr/xo-web/issues/1651) +- Remote copy is always activating compression [\#1645](https://github.com/vatesfr/xo-web/issues/1645) +- LB plugin UI problems [\#1630](https://github.com/vatesfr/xo-web/issues/1630) +- Keyboard shortcuts should not work when a modal is open [\#1589](https://github.com/vatesfr/xo-web/issues/1589) +- UI small bug in drop-down lists [\#1411](https://github.com/vatesfr/xo-web/issues/1411) +- md5 delta backup error [\#1672](https://github.com/vatesfr/xo-web/issues/1672) +- Can't edit VIF network [\#1640](https://github.com/vatesfr/xo-web/issues/1640) +- Do not expose shortcuts while console is focused [\#1614](https://github.com/vatesfr/xo-web/issues/1614) +- All users can see VM templates [\#1621](https://github.com/vatesfr/xo-web/issues/1621) +- Profile page is broken [\#1612](https://github.com/vatesfr/xo-web/issues/1612) +- SR delete should redirect to home [\#1611](https://github.com/vatesfr/xo-web/issues/1611) +- Delta VHD backup checksum is invalidated by chaining [\#1606](https://github.com/vatesfr/xo-web/issues/1606) +- VM with long description break on 2 lines [\#1580](https://github.com/vatesfr/xo-web/issues/1580) +- Network status on VM edition [\#1573](https://github.com/vatesfr/xo-web/issues/1573) +- VM template deletion fails [\#1571](https://github.com/vatesfr/xo-web/issues/1571) +- Template edition - "no such object" [\#1569](https://github.com/vatesfr/xo-web/issues/1569) +- missing links / element not displayed as links [\#1567](https://github.com/vatesfr/xo-web/issues/1567) +- Backup restore stalled on some SMB shares [\#1412](https://github.com/vatesfr/xo-web/issues/1412) +- Wrong bond display [\#1156](https://github.com/vatesfr/xo-web/issues/1156) +- Multiple reboot selection doesn't work [\#1562](https://github.com/vatesfr/xo-web/issues/1562) +- Server logs should be displayed in reverse chonological order [\#1547](https://github.com/vatesfr/xo-web/issues/1547) +- Cannot create resource sets without limits [\#1537](https://github.com/vatesfr/xo-web/issues/1537) +- UI - Weird display when editing long VM desc [\#1528](https://github.com/vatesfr/xo-web/issues/1528) +- Useless iso selector in host console [\#1527](https://github.com/vatesfr/xo-web/issues/1527) +- Pool and Host dummy welcome message [\#1519](https://github.com/vatesfr/xo-web/issues/1519) +- Bug on Network VM tab [\#1518](https://github.com/vatesfr/xo-web/issues/1518) +- Link to home with filter in query does not work [\#1513](https://github.com/vatesfr/xo-web/issues/1513) +- VHD merge fails with "RangeError: index out of range" on SMB remote [\#1511](https://github.com/vatesfr/xo-web/issues/1511) +- DR: previous VDIs are not removed [\#1510](https://github.com/vatesfr/xo-web/issues/1510) +- DR: previous copies not removed when same number as depth [\#1509](https://github.com/vatesfr/xo-web/issues/1509) +- Empty Saved Search doesn't load when set to default filter [\#1354](https://github.com/vatesfr/xo-web/issues/1354) +- Removing a user/group should delete its ACLs [\#899](https://github.com/vatesfr/xo-web/issues/899) +- OVA Import - XO stuck during import [\#1551](https://github.com/vatesfr/xo-web/issues/1551) +- SMB remote empty domain fails [\#1499](https://github.com/vatesfr/xo-web/issues/1499) +- Can't edit a remote password [\#1498](https://github.com/vatesfr/xo-web/issues/1498) +- Issue in VM create with CoreOS [\#1493](https://github.com/vatesfr/xo-web/issues/1493) +- Overlapping months in backup view [\#1488](https://github.com/vatesfr/xo-web/issues/1488) +- No line break for SSH key in user view [\#1475](https://github.com/vatesfr/xo-web/issues/1475) +- Create VIF UI issues [\#1472](https://github.com/vatesfr/xo-web/issues/1472) + +## **5.2.0** (2016-09-09) + +### Enhancements + +- IP management [\#1350](https://github.com/vatesfr/xo-web/issues/1350), [\#988](https://github.com/vatesfr/xo-web/issues/988), [\#1427](https://github.com/vatesfr/xo-web/issues/1427) and [\#240](https://github.com/vatesfr/xo-web/issues/240) +- Update reverse proxy example [\#1474](https://github.com/vatesfr/xo-web/issues/1474) +- Improve log view [\#1467](https://github.com/vatesfr/xo-web/issues/1467) +- Backup Reports: e-mail subject [\#1463](https://github.com/vatesfr/xo-web/issues/1463) +- Backup Reports: report the error [\#1462](https://github.com/vatesfr/xo-web/issues/1462) +- Vif selector: select management network by default [\#1425](https://github.com/vatesfr/xo-web/issues/1425) +- Display when browser disconnected to server [\#1417](https://github.com/vatesfr/xo-web/issues/1417) +- Tooltip on OS icon in VM view [\#1416](https://github.com/vatesfr/xo-web/issues/1416) +- Display pool master [\#1407](https://github.com/vatesfr/xo-web/issues/1407) +- Missing tooltips in VM creation view [\#1402](https://github.com/vatesfr/xo-web/issues/1402) +- Handle VBD disconnect and connect [\#1397](https://github.com/vatesfr/xo-web/issues/1397) +- Eject host from a pool [\#1395](https://github.com/vatesfr/xo-web/issues/1395) +- Improve pool general view [\#1393](https://github.com/vatesfr/xo-web/issues/1393) +- Improve patching system [\#1392](https://github.com/vatesfr/xo-web/issues/1392) +- Pool name modification [\#1390](https://github.com/vatesfr/xo-web/issues/1390) +- Confirmation dialog before destroying VDIs [\#1388](https://github.com/vatesfr/xo-web/issues/1388) +- Tooltips for meter object [\#1387](https://github.com/vatesfr/xo-web/issues/1387) +- New Host assistant [\#1374](https://github.com/vatesfr/xo-web/issues/1374) +- New VM assistant [\#1373](https://github.com/vatesfr/xo-web/issues/1373) +- New SR assistant [\#1372](https://github.com/vatesfr/xo-web/issues/1372) +- Direct access to VDI listing from dashboard's SR usage breakdown [\#1371](https://github.com/vatesfr/xo-web/issues/1371) +- Can't set a network name at pool level [\#1368](https://github.com/vatesfr/xo-web/issues/1368) +- Change a few mouse over descriptions [\#1363](https://github.com/vatesfr/xo-web/issues/1363) +- Hide network install in VM create if template is HVM [\#1362](https://github.com/vatesfr/xo-web/issues/1362) +- SR space left during VM creation [\#1358](https://github.com/vatesfr/xo-web/issues/1358) +- Add destination SR on migration modal in VM view [\#1357](https://github.com/vatesfr/xo-web/issues/1357) +- Ability to create a new VM from a snapshot [\#1353](https://github.com/vatesfr/xo-web/issues/1353) +- Missing explanation/confirmation on Snapshot Page [\#1349](https://github.com/vatesfr/xo-web/issues/1349) +- Log view: expose API errors in the web UI [\#1344](https://github.com/vatesfr/xo-web/issues/1344) +- Registration on update page [\#1341](https://github.com/vatesfr/xo-web/issues/1341) +- Add export snapshot button [\#1336](https://github.com/vatesfr/xo-web/issues/1336) +- Use saved SSH keys in VM create CloudConfig [\#1319](https://github.com/vatesfr/xo-web/issues/1319) +- Collapse header in console view [\#1268](https://github.com/vatesfr/xo-web/issues/1268) +- Two max concurrent jobs in parallel [\#915](https://github.com/vatesfr/xo-web/issues/915) +- Handle OVA import via the web UI [\#709](https://github.com/vatesfr/xo-web/issues/709) + +### Bug fixes + +- Bug on VM console when header is hidden [\#1485](https://github.com/vatesfr/xo-web/issues/1485) +- Disks not removed when deleting multiple VMs [\#1484](https://github.com/vatesfr/xo-web/issues/1484) +- Do not display VDI disconnect button when a VM is not running [\#1470](https://github.com/vatesfr/xo-web/issues/1470) +- Do not display VIF disconnect button when a VM is not running [\#1468](https://github.com/vatesfr/xo-web/issues/1468) +- Error on migration if no default SR \(even when not used\) [\#1466](https://github.com/vatesfr/xo-web/issues/1466) +- DR issue while rotating old backup [\#1464](https://github.com/vatesfr/xo-web/issues/1464) +- Giving resource set to end-user ends with error [\#1448](https://github.com/vatesfr/xo-web/issues/1448) +- Error thrown when cancelling out of Delete User confirmation dialog [\#1439](https://github.com/vatesfr/xo-web/issues/1439) +- Wrong month label shown in Backup and Job scheduler [\#1438](https://github.com/vatesfr/xo-web/issues/1438) +- Bug on Self service creation/edition [\#1428](https://github.com/vatesfr/xo-web/issues/1428) +- ISO selection during VM create is not mounted after [\#1415](https://github.com/vatesfr/xo-web/issues/1415) +- Hosts general view: bad link for storage [\#1408](https://github.com/vatesfr/xo-web/issues/1408) +- Backup Schedule - "Month" and "Day of Week" display error [\#1404](https://github.com/vatesfr/xo-web/issues/1404) +- Migrate dialog doesn't present all available VIF's in new UI interface [\#1403](https://github.com/vatesfr/xo-web/issues/1403) +- NFS mount issues [\#1396](https://github.com/vatesfr/xo-web/issues/1396) +- Select component color [\#1391](https://github.com/vatesfr/xo-web/issues/1391) +- SR created with local path shouldn't be shared [\#1389](https://github.com/vatesfr/xo-web/issues/1389) +- Disks (VBD) are attached to VM in RO mode instead of RW even if RO is unchecked [\#1386](https://github.com/vatesfr/xo-web/issues/1386) +- Re-connection issues between server and XS hosts [\#1384](https://github.com/vatesfr/xo-web/issues/1384) +- Meter object style with Chrome 52 [\#1383](https://github.com/vatesfr/xo-web/issues/1383) +- Editing a rolling snapshot job seems to fail [\#1376](https://github.com/vatesfr/xo-web/issues/1376) +- Dashboard SR usage and total inverted [\#1370](https://github.com/vatesfr/xo-web/issues/1370) +- XenServer connection issue with host while using VGPUs [\#1369](https://github.com/vatesfr/xo-web/issues/1369) +- Job created with v4 are not correctly displayed in v5 [\#1366](https://github.com/vatesfr/xo-web/issues/1366) +- CPU accounting in resource set [\#1365](https://github.com/vatesfr/xo-web/issues/1365) +- Tooltip stay displayed when a button change state [\#1360](https://github.com/vatesfr/xo-web/issues/1360) +- Failure on host reboot [\#1351](https://github.com/vatesfr/xo-web/issues/1351) +- Editing Backup Jobs Without Compression, Slider Always Set To On [\#1339](https://github.com/vatesfr/xo-web/issues/1339) +- Month Selection on Backup Screen Wrong [\#1338](https://github.com/vatesfr/xo-web/issues/1338) +- Delta backup fail when removed VDIs [\#1333](https://github.com/vatesfr/xo-web/issues/1333) + +## **5.1.0** (2016-07-26) + +### Enhancements + +- Improve backups timezone UI [\#1314](https://github.com/vatesfr/xo-web/issues/1314) +- HOME view submenus [\#1306](https://github.com/vatesfr/xo-web/issues/1306) +- Ability for a user to save SSH keys [\#1299](https://github.com/vatesfr/xo-web/issues/1299) +- \[ACLs\] Ability to select all hosts/VMs [\#1296](https://github.com/vatesfr/xo-web/issues/1296) +- Improve scheduling UI [\#1295](https://github.com/vatesfr/xo-web/issues/1295) +- Plugins: Predefined configurations [\#1289](https://github.com/vatesfr/xo-web/issues/1289) +- Button to recompute resource sets limits [\#1287](https://github.com/vatesfr/xo-web/issues/1287) +- Credit scheduler CAP and weight configuration [\#1283](https://github.com/vatesfr/xo-web/issues/1283) +- Migration form problem on the /v5/vms/\_\_UUID\_\_ page when doing xenmotion inside a pool [\#1254](https://github.com/vatesfr/xo-web/issues/1254) +- /v5/\#/pools/\_\_UUID\_\_: patch table improvement [\#1246](https://github.com/vatesfr/xo-web/issues/1246) +- /v5/\#/hosts/\_\_UUID\_\_: patch list improvements ? [\#1245](https://github.com/vatesfr/xo-web/issues/1245) +- F\*cking patches, how do they work? [\#1236](https://github.com/vatesfr/xo-web/issues/1236) +- Change Default Filter [\#1235](https://github.com/vatesfr/xo-web/issues/1235) +- Add a property on jobs to know their state [\#1232](https://github.com/vatesfr/xo-web/issues/1232) +- Spanish translation [\#1231](https://github.com/vatesfr/xo-web/issues/1231) +- Home: "Filter" input and keyboard focus [\#1228](https://github.com/vatesfr/xo-web/issues/1228) +- Display xenserver version [\#1225](https://github.com/vatesfr/xo-web/issues/1225) +- Plugin config: presets & defaults [\#1222](https://github.com/vatesfr/xo-web/issues/1222) +- Allow halted VM migration [\#1216](https://github.com/vatesfr/xo-web/issues/1216) +- Missing confirm dialog on critical button [\#1211](https://github.com/vatesfr/xo-web/issues/1211) +- Backup logs are not sortable [\#1196](https://github.com/vatesfr/xo-web/issues/1196) +- Page title with the name of current object [\#1185](https://github.com/vatesfr/xo-web/issues/1185) +- Existing VIF management [\#1176](https://github.com/vatesfr/xo-web/issues/1176) +- Do not display fast clone option is there isn't template disks [\#1172](https://github.com/vatesfr/xo-web/issues/1172) +- UI issue when adding a user [\#1159](https://github.com/vatesfr/xo-web/issues/1159) +- Combined values on stats [\#1158](https://github.com/vatesfr/xo-web/issues/1158) +- Parallel coordinates graph [\#1157](https://github.com/vatesfr/xo-web/issues/1157) +- VM creation on self-service as user [\#1155](https://github.com/vatesfr/xo-web/issues/1155) +- VM copy bulk action on home view [\#1154](https://github.com/vatesfr/xo-web/issues/1154) +- Better VDI map [\#1151](https://github.com/vatesfr/xo-web/issues/1151) +- Missing tooltips on buttons [\#1150](https://github.com/vatesfr/xo-web/issues/1150) +- Patching from pool view [\#1149](https://github.com/vatesfr/xo-web/issues/1149) +- Missing patches in dashboard [\#1148](https://github.com/vatesfr/xo-web/issues/1148) +- Improve tasks view [\#1147](https://github.com/vatesfr/xo-web/issues/1147) +- Home bulk VM migration [\#1146](https://github.com/vatesfr/xo-web/issues/1146) +- LDAP plugin clear password field [\#1145](https://github.com/vatesfr/xo-web/issues/1145) +- Cron default behavior [\#1144](https://github.com/vatesfr/xo-web/issues/1144) +- Modal for migrate on home [\#1143](https://github.com/vatesfr/xo-web/issues/1143) +- /v5/\#/srs/\_\_UUID\_\_: UI improvements [\#1142](https://github.com/vatesfr/xo-web/issues/1142) +- /v5/\#/pools/: some name should be links [\#1141](https://github.com/vatesfr/xo-web/issues/1141) +- create the page /v5/\#/pools/ [\#1140](https://github.com/vatesfr/xo-web/issues/1140) +- Dashboard: add links to different part of XOA [\#1139](https://github.com/vatesfr/xo-web/issues/1139) +- /v5/\#/dashboard/overview: add link on the "Top 5 SR Usage" graph [\#1135](https://github.com/vatesfr/xo-web/issues/1135) +- /v5/\#/backup/overview: display the error when there is one returned by xenserver on failed job. [\#1134](https://github.com/vatesfr/xo-web/issues/1134) +- /v5/: add an option to set the number of element displayed in tables [\#1133](https://github.com/vatesfr/xo-web/issues/1133) +- Updater refresh page after update [\#1131](https://github.com/vatesfr/xo-web/issues/1131) +- /v5/\#/settings/plugins [\#1130](https://github.com/vatesfr/xo-web/issues/1130) +- /v5/\#/new/sr: layout issue [\#1129](https://github.com/vatesfr/xo-web/issues/1129) +- v5 /v5/\#/vms/new: layout issue [\#1128](https://github.com/vatesfr/xo-web/issues/1128) +- v5 user page missing style [\#1127](https://github.com/vatesfr/xo-web/issues/1127) +- Remote helper/tester [\#1075](https://github.com/vatesfr/xo-web/issues/1075) +- Generate uiSchema from custom schema properties [\#951](https://github.com/vatesfr/xo-web/issues/951) +- Customizing VM names generation during batch creation [\#949](https://github.com/vatesfr/xo-web/issues/949) + +### Bug fixes + +- Plugins: Don't use `default` attributes in presets list [\#1288](https://github.com/vatesfr/xo-web/issues/1288) +- CPU weight must be an integer [\#1286](https://github.com/vatesfr/xo-web/issues/1286) +- Overview of self service is always empty [\#1282](https://github.com/vatesfr/xo-web/issues/1282) +- SR attach/creation issue [\#1281](https://github.com/vatesfr/xo-web/issues/1281) +- Self service resources not modified after a VM deletion [\#1276](https://github.com/vatesfr/xo-web/issues/1276) +- Scheduled jobs seems use GMT since 5.0 [\#1258](https://github.com/vatesfr/xo-web/issues/1258) +- Can't create a VM with disks on 2 different SRs [\#1257](https://github.com/vatesfr/xo-web/issues/1257) +- Graph display bug [\#1247](https://github.com/vatesfr/xo-web/issues/1247) +- /v5/#/hosts/__UUID__: Patch list not limited to the current pool [\#1244](https://github.com/vatesfr/xo-web/issues/1244) +- Replication issues [\#1233](https://github.com/vatesfr/xo-web/issues/1233) +- VM creation install method disabled fields [\#1198](https://github.com/vatesfr/xo-web/issues/1198) +- Update icon shouldn't be displayed when menu is collapsed [\#1188](https://github.com/vatesfr/xo-web/issues/1188) +- /v5/ : Load average graph axis issue [\#1167](https://github.com/vatesfr/xo-web/issues/1167) +- Some remote can't be opened [\#1164](https://github.com/vatesfr/xo-web/issues/1164) +- Bulk action for hosts in home and pool view [\#1153](https://github.com/vatesfr/xo-web/issues/1153) +- New Vif [\#1138](https://github.com/vatesfr/xo-web/issues/1138) +- Missing SRs [\#1123](https://github.com/vatesfr/xo-web/issues/1123) +- Continuous replication email alert does not obey per job setting [\#1121](https://github.com/vatesfr/xo-web/issues/1121) +- Safari XO5 issue [\#1120](https://github.com/vatesfr/xo-web/issues/1120) +- ACLs shoud be available in Enterprise Edition [\#1118](https://github.com/vatesfr/xo-web/issues/1118) +- SR edit name or description doesn't work [\#1116](https://github.com/vatesfr/xo-web/issues/1116) +- Bad RRD parsing for VIFs [\#969](https://github.com/vatesfr/xo-web/issues/969) + +## **5.0.0** (2016-06-24) + +### Enhancements + +- Handle failed quiesce in snapshots [\#1088](https://github.com/vatesfr/xo-web/issues/1088) +- Sparklines stats [\#1061](https://github.com/vatesfr/xo-web/issues/1061) +- Task view [\#1060](https://github.com/vatesfr/xo-web/issues/1060) +- Improved import system [\#1048](https://github.com/vatesfr/xo-web/issues/1048) +- Backup restore view improvements [\#1021](https://github.com/vatesfr/xo-web/issues/1021) +- Restore VM - Wrong VLAN on the VMs interface [\#1016](https://github.com/vatesfr/xo-web/issues/1016) +- Fast Disk Cloning [\#960](https://github.com/vatesfr/xo-web/issues/960) +- Disaster recovery job should target SRs, not pools [\#955](https://github.com/vatesfr/xo-web/issues/955) +- Improve Header/Content interaction in a page [\#926](https://github.com/vatesfr/xo-web/issues/926) +- New default view [\#912](https://github.com/vatesfr/xo-web/issues/912) +- Xen Patching - Restart Pending [\#883](https://github.com/vatesfr/xo-web/issues/883) +- Hide About page for user that are not admin [\#877](https://github.com/vatesfr/xo-web/issues/877) +- ACL: Ability to view/sort/group by User/Group, Objects or Role [\#875](https://github.com/vatesfr/xo-web/issues/875) +- ACL: Ability to select multiple users & group when creating a rule [\#874](https://github.com/vatesfr/xo-web/issues/874) +- Translation [\#839](https://github.com/vatesfr/xo-web/issues/839) +- XO offer useless network interfaces for XenMontion [\#833](https://github.com/vatesfr/xo-web/issues/833) +- Show HVM, PVM, PVHVM modes in guest details [\#806](https://github.com/vatesfr/xo-web/issues/806) +- Tree view: display cpu available/total for each host [\#696](https://github.com/vatesfr/xo-web/issues/696) +- Greenkeeper integration [\#667](https://github.com/vatesfr/xo-web/issues/667) +- Clarify vCPUs and RAM editor [\#658](https://github.com/vatesfr/xo-web/issues/658) +- Backup LZ4 compression [\#647](https://github.com/vatesfr/xo-web/issues/647) +- Support enum in plugins configuration [\#638](https://github.com/vatesfr/xo-web/issues/638) +- Add configuration option to disable xoa-updater [\#535](https://github.com/vatesfr/xo-web/issues/535) +- Use cursors to add more context to actions [\#523](https://github.com/vatesfr/xo-web/issues/523) +- Review UI for flat view [\#354](https://github.com/vatesfr/xo-web/issues/354) +- Review UI for the tree view [\#353](https://github.com/vatesfr/xo-web/issues/353) +- Tag filtering [\#233](https://github.com/vatesfr/xo-web/issues/233) +- GUI review [\#230](https://github.com/vatesfr/xo-web/issues/230) +- Review UI for VM creation [\#214](https://github.com/vatesfr/xo-web/issues/214) +- Ability to collapse pools/hosts in main view [\#173](https://github.com/vatesfr/xo-web/issues/173) +- Issue importing .xva VM via xo-web [\#1022](https://github.com/vatesfr/xo-web/issues/1022) +- Enhancement Proposal - Cancel In Progress Backups [\#1003](https://github.com/vatesfr/xo-web/issues/1003) +- Can't create VM with CloudConfigDrive [\#917](https://github.com/vatesfr/xo-web/issues/917) +- Auth: LDAP User causes problems [\#893](https://github.com/vatesfr/xo-web/issues/893) +- No tags in Continuous Replication [\#838](https://github.com/vatesfr/xo-web/issues/838) +- Delta backup Depth not working [\#802](https://github.com/vatesfr/xo-web/issues/802) +- Update Section - Running version info missing - gui enhancement [\#795](https://github.com/vatesfr/xo-web/issues/795) +- On reboot, vnc console wrongly scaled [\#722](https://github.com/vatesfr/xo-web/issues/722) +- Make the object name \(title\) "sticky" at the top of the page [\#705](https://github.com/vatesfr/xo-web/issues/705) +- pool view: display Local SR from hosts in the current pool [\#692](https://github.com/vatesfr/xo-web/issues/692) +- tree view: display all IPs [\#689](https://github.com/vatesfr/xo-web/issues/689) +- XO5 parallel distribution [\#462](https://github.com/vatesfr/xo-web/issues/462) +- Load balancing with XO [\#423](https://github.com/vatesfr/xo-web/issues/423) + +### Bug fixes + +- vCPUs number when no tools installed [\#1089](https://github.com/vatesfr/xo-web/issues/1089) +- Config Drive textbox disappears when content is deleted [\#1012](https://github.com/vatesfr/xo-web/issues/1012) +- storage status not changed in host view page after disconnect/connect [\#1009](https://github.com/vatesfr/xo-web/issues/1009) +- Cannot Delete Logs From Backup Overview [\#1004](https://github.com/vatesfr/xo-web/issues/1004) +- \[v5.x\] Plugins configuration: optional non-used objects are sent [\#1000](https://github.com/vatesfr/xo-web/issues/1000) +- "@" char in remote password break the remote view [\#997](https://github.com/vatesfr/xo-web/issues/997) +- Handle MEMORY\_CONSTRAINT\_VIOLATION correctly [\#970](https://github.com/vatesfr/xo-web/issues/970) +- VM creation error on XenServer Dundee [\#964](https://github.com/vatesfr/xo-web/issues/964) +- Copy VMs doesn't display all SRs [\#945](https://github.com/vatesfr/xo-web/issues/945) +- Autopower\_on wrong value [\#937](https://github.com/vatesfr/xo-web/issues/937) +- Correctly handle unknown users in group view [\#900](https://github.com/vatesfr/xo-web/issues/900) +- Importing into Dundee [\#887](https://github.com/vatesfr/xo-web/issues/887) +- update status - gui resize issue [\#803](https://github.com/vatesfr/xo-web/issues/803) +- Backup Remote Stores Problem [\#751](https://github.com/vatesfr/xo-web/issues/751) +- VM view is broken when changing a disk SR twice [\#670](https://github.com/vatesfr/xo-web/issues/670) +- console mouse sync [\#280](https://github.com/vatesfr/xo-web/issues/280) + +## **4.16.0** (2016-04-29) + +Maintenance release + +### Enhancements + +- TOO\_MANY\_PENDING\_TASKS [\#861](https://github.com/vatesfr/xo-web/issues/861) + +### Bug fixes + +- Incorrect VM target name with continuous replication [\#904](https://github.com/vatesfr/xo-web/issues/904) +- Error while deleting users [\#901](https://github.com/vatesfr/xo-web/issues/901) +- Use an available path to the SR to create a config drive [\#882](https://github.com/vatesfr/xo-web/issues/882) +- VM autoboot don't set the right pool parameter [\#879](https://github.com/vatesfr/xo-web/issues/879) +- BUG: ACL with NFS ISO Library not working! [\#870](https://github.com/vatesfr/xo-web/issues/870) +- Broken paths in backups in SMB [\#865](https://github.com/vatesfr/xo-web/issues/865) +- Plugins page loads users/groups multiple times [\#829](https://github.com/vatesfr/xo-web/issues/829) +- "Ghost" VM remains after migration [\#769](https://github.com/vatesfr/xo-web/issues/769) + +## **4.15.0** (2016-03-21) + +Load balancing, SMB delta support, advanced network operations... + +### Enhancements + +- Add the job name inside the backup email report [\#819](https://github.com/vatesfr/xo-web/issues/819) +- Delta backup with quiesce [\#812](https://github.com/vatesfr/xo-web/issues/812) +- Hosts: No user feedback when error occurs with SR connect / disconnect [\#810](https://github.com/vatesfr/xo-web/issues/810) +- Expose components versions [\#807](https://github.com/vatesfr/xo-web/issues/807) +- Rework networks/PIFs management [\#805](https://github.com/vatesfr/xo-web/issues/805) +- Displaying all SRs and a list of available hosts for creating VM from a pool [\#790](https://github.com/vatesfr/xo-web/issues/790) +- Add "Source network" on "VM migration" screen [\#785](https://github.com/vatesfr/xo-web/issues/785) +- Migration queue [\#783](https://github.com/vatesfr/xo-web/issues/783) +- Match network names for VM migration [\#782](https://github.com/vatesfr/xo-web/issues/782) +- Disk names [\#780](https://github.com/vatesfr/xo-web/issues/780) +- Self service: should the user be able to set the CPU weight? [\#767](https://github.com/vatesfr/xo-web/issues/767) +- host & pool Citrix license status [\#763](https://github.com/vatesfr/xo-web/issues/763) +- pool view: Provide "updates" section [\#762](https://github.com/vatesfr/xo-web/issues/762) +- XOA ISO image: ambigious root disk label [\#761](https://github.com/vatesfr/xo-web/issues/761) +- Host info: provide system serial number [\#760](https://github.com/vatesfr/xo-web/issues/760) +- CIFS ISO SR Creation [\#731](https://github.com/vatesfr/xo-web/issues/731) +- MAC address not preserved on VM restore [\#707](https://github.com/vatesfr/xo-web/issues/707) +- Failing replication job should send reports [\#659](https://github.com/vatesfr/xo-web/issues/659) +- Display networks in the Pool view [\#226](https://github.com/vatesfr/xo-web/issues/226) + +### Bug fixes + +- Broken link to backup remote [\#821](https://github.com/vatesfr/xo-web/issues/821) +- Issue with self-signed cert for email plugin [\#817](https://github.com/vatesfr/xo-web/issues/817) +- Plugins view, reset form and errors [\#815](https://github.com/vatesfr/xo-web/issues/815) +- HVM recovery mode is broken [\#794](https://github.com/vatesfr/xo-web/issues/794) +- Disk bug when creating vm from template [\#778](https://github.com/vatesfr/xo-web/issues/778) +- Can't mount NFS shares in remote stores [\#775](https://github.com/vatesfr/xo-web/issues/775) +- VM disk name and description not passed during creation [\#774](https://github.com/vatesfr/xo-web/issues/774) +- NFS mount problem for Windows share [\#771](https://github.com/vatesfr/xo-web/issues/771) +- lodash.pluck not installed [\#757](https://github.com/vatesfr/xo-web/issues/757) +- this.\_getAuthenticationTokensForUser is not a function [\#755](https://github.com/vatesfr/xo-web/issues/755) +- CentOS 6.x 64bit template creates a VM that won't boot [\#733](https://github.com/vatesfr/xo-web/issues/733) +- Lot of xo:perf leading to XO crash [\#575](https://github.com/vatesfr/xo-web/issues/575) +- New collection checklist [\#262](https://github.com/vatesfr/xo-web/issues/262) + +## **4.14.0** (2016-02-23) + +Self service, custom CloudInit... + +### Enhancements + +- VM creation self service with quotas [\#285](https://github.com/vatesfr/xo-web/issues/285) +- Cloud config custom user data [\#706](https://github.com/vatesfr/xo-web/issues/706) +- Patches behind a proxy [\#737](https://github.com/vatesfr/xo-web/issues/737) +- Remote store status indicator [\#728](https://github.com/vatesfr/xo-web/issues/728) +- Patch list order [\#724](https://github.com/vatesfr/xo-web/issues/724) +- Enable reporting on additional backup types [\#717](https://github.com/vatesfr/xo-web/issues/717) +- Tooltip name for cancel [\#703](https://github.com/vatesfr/xo-web/issues/703) +- Portable VHD merging [\#646](https://github.com/vatesfr/xo-web/issues/646) + +### Bug fixes + +- Avoid merge between two delta vdi backups [\#702](https://github.com/vatesfr/xo-web/issues/702) +- Text in table is not cut anymore [\#713](https://github.com/vatesfr/xo-web/issues/713) +- Disk size edition issue with float numbers [\#719](https://github.com/vatesfr/xo-web/issues/719) +- Create vm, summary is not refreshed [\#721](https://github.com/vatesfr/xo-web/issues/721) +- Boot order problem [\#726](https://github.com/vatesfr/xo-web/issues/726) + +## **4.13.0** (2016-02-05) + +Backup checksum, SMB remotes... + +### Enhancements + +- Add SMB mount for remote [\#338](https://github.com/vatesfr/xo-web/issues/338) +- Centralize Perm in a lib [\#345](https://github.com/vatesfr/xo-web/issues/345) +- Expose interpool migration details [\#567](https://github.com/vatesfr/xo-web/issues/567) +- Add checksum for delta backup [\#617](https://github.com/vatesfr/xo-web/issues/617) +- Redirect from HTTP to HTTPS [\#626](https://github.com/vatesfr/xo-web/issues/626) +- Expose vCPU weight [\#633](https://github.com/vatesfr/xo-web/issues/633) +- Avoid metadata in delta backup [\#651](https://github.com/vatesfr/xo-web/issues/651) +- Button to clear logs [\#661](https://github.com/vatesfr/xo-web/issues/661) +- Units for RAM and disks [\#666](https://github.com/vatesfr/xo-web/issues/666) +- Remove multiple VDIs at once [\#676](https://github.com/vatesfr/xo-web/issues/676) +- Find orphaned VDI snapshots [\#679](https://github.com/vatesfr/xo-web/issues/679) +- New health view in Dashboard [\#680](https://github.com/vatesfr/xo-web/issues/680) +- Use physical usage for VDI and SR [\#682](https://github.com/vatesfr/xo-web/issues/682) +- TLS configuration [\#685](https://github.com/vatesfr/xo-web/issues/685) +- Better VM info on tree view [\#688](https://github.com/vatesfr/xo-web/issues/688) +- Absolute values in tooltips for tree view [\#690](https://github.com/vatesfr/xo-web/issues/690) +- Absolute values for host memory [\#691](https://github.com/vatesfr/xo-web/issues/691) + +### Bug fixes + +- Issues on host console screen [\#672](https://github.com/vatesfr/xo-web/issues/672) +- NFS remote mount fails in particular case [\#665](https://github.com/vatesfr/xo-web/issues/665) +- Unresponsive pages [\#662](https://github.com/vatesfr/xo-web/issues/662) +- Live migration fail in the same pool with local SR fails [\#655](https://github.com/vatesfr/xo-web/issues/655) + +## **4.12.0** (2016-01-18) + +Continuous Replication, Continuous Delta backup... + +### Enhancements + +- Continuous VM replication [\#582](https://github.com/vatesfr/xo-web/issues/582) +- Continuous Delta Backup [\#576](https://github.com/vatesfr/xo-web/issues/576) +- Scheduler should not run job again if previous instance is not finished [\#642](https://github.com/vatesfr/xo-web/issues/642) +- Boot VM automatically after creation [\#635](https://github.com/vatesfr/xo-web/issues/635) +- Manage existing VIFs in templates [\#630](https://github.com/vatesfr/xo-web/issues/630) +- Support templates with existing install repository [\#627](https://github.com/vatesfr/xo-web/issues/627) +- Remove running VMs [\#616](https://github.com/vatesfr/xo-web/issues/616) +- Prevent a VM to start before delta import is finished [\#613](https://github.com/vatesfr/xo-web/issues/613) +- Spawn multiple VMs at once [\#606](https://github.com/vatesfr/xo-web/issues/606) +- Fixed `suspendVM` in tree view. [\#619](https://github.com/vatesfr/xo-web/pull/619) ([pdonias](https://github.com/pdonias)) + +### Bug fixes + +- User defined MAC address is not fetch in VM install [\#643](https://github.com/vatesfr/xo-web/issues/643) +- CoreOsCloudConfig is not shown with CoreOS [\#639](https://github.com/vatesfr/xo-web/issues/639) +- Plugin activation/deactivation in web UI seems broken [\#637](https://github.com/vatesfr/xo-web/issues/637) +- Issue when creating CloudConfig drive [\#636](https://github.com/vatesfr/xo-web/issues/636) +- CloudConfig hostname shouldn't have space [\#634](https://github.com/vatesfr/xo-web/issues/634) +- Cloned VIFs are not properly deleted on VM creation [\#632](https://github.com/vatesfr/xo-web/issues/632) +- Default PV args missing during VM creation [\#628](https://github.com/vatesfr/xo-web/issues/628) +- VM creation problems from custom templates [\#625](https://github.com/vatesfr/xo-web/issues/625) +- Emergency shutdown race condition [\#622](https://github.com/vatesfr/xo-web/issues/622) +- `vm.delete\(\)` should not delete VDIs attached to other VMs [\#621](https://github.com/vatesfr/xo-web/issues/621) +- VM creation error from template with a disk [\#581](https://github.com/vatesfr/xo-web/issues/581) +- Only delete VDI exports when VM backup is successful [\#644](https://github.com/vatesfr/xo-web/issues/644) +- Change the name of an imported VM during the import process [\#641](https://github.com/vatesfr/xo-web/issues/641) +- Creating a new VIF in view is partially broken [\#652](https://github.com/vatesfr/xo-web/issues/652) +- Grey out the "create button" during VM creation [\#654](https://github.com/vatesfr/xo-web/issues/654) + +## **4.11.0** (2015-12-22) + +Delta backup, CloudInit... + +### Enhancements + +- Visible list of SR inside a VM [\#601](https://github.com/vatesfr/xo-web/issues/601) +- VDI move [\#591](https://github.com/vatesfr/xo-web/issues/591) +- Edit pre-existing disk configuration during VM creation [\#589](https://github.com/vatesfr/xo-web/issues/589) +- Allow disk size edition [\#587](https://github.com/vatesfr/xo-web/issues/587) +- Better VDI resize support [\#585](https://github.com/vatesfr/xo-web/issues/585) +- Remove manual VM export metadata in UI [\#580](https://github.com/vatesfr/xo-web/issues/580) +- Support import VM metadata [\#579](https://github.com/vatesfr/xo-web/issues/579) +- Set a default pool SR [\#572](https://github.com/vatesfr/xo-web/issues/572) +- ISOs should be sorted by name [\#565](https://github.com/vatesfr/xo-web/issues/565) +- Button to boot a VM from a disc once [\#564](https://github.com/vatesfr/xo-web/issues/564) +- Ability to boot a PV VM from a disc [\#563](https://github.com/vatesfr/xo-web/issues/563) +- Add an option to manually run backup jobs [\#562](https://github.com/vatesfr/xo-web/issues/562) +- backups to unmounted storage [\#561](https://github.com/vatesfr/xo-web/issues/561) +- Root integer properties cannot be edited in plugins configuration form [\#550](https://github.com/vatesfr/xo-web/issues/550) +- Generic CloudConfig drive [\#549](https://github.com/vatesfr/xo-web/issues/549) +- Auto-discovery of installed xo-server plugins [\#546](https://github.com/vatesfr/xo-web/issues/546) +- Hide info on flat view [\#545](https://github.com/vatesfr/xo-web/issues/545) +- Config plugin boolean properties must have a default value \(undefined prohibited\) [\#543](https://github.com/vatesfr/xo-web/issues/543) +- Present detailed errors on plugin configuration failures [\#530](https://github.com/vatesfr/xo-web/issues/530) +- Do not reset form on failures in plugins configuration [\#529](https://github.com/vatesfr/xo-web/issues/529) +- XMPP alert plugin [\#518](https://github.com/vatesfr/xo-web/issues/518) +- Hide tag adders depending on ACLs [\#516](https://github.com/vatesfr/xo-web/issues/516) +- Choosing a framework for xo-web 5 [\#514](https://github.com/vatesfr/xo-web/issues/514) +- Prevent adding a host in an existing XAPI connection [\#466](https://github.com/vatesfr/xo-web/issues/466) +- Read only connection to Xen servers/pools [\#439](https://github.com/vatesfr/xo-web/issues/439) +- generic notification system [\#391](https://github.com/vatesfr/xo-web/issues/391) +- Data architecture review [\#384](https://github.com/vatesfr/xo-web/issues/384) +- Make filtering easier to understand/add some "default" filters [\#207](https://github.com/vatesfr/xo-web/issues/207) +- Improve performance [\#148](https://github.com/vatesfr/xo-web/issues/148) + +### Bug fixes + +- VM metadata export should not require a snapshot [\#615](https://github.com/vatesfr/xo-web/issues/615) +- Missing patch for all hosts is continuously refreshed [\#609](https://github.com/vatesfr/xo-web/issues/609) +- Backup import memory issue [\#608](https://github.com/vatesfr/xo-web/issues/608) +- Host list missing patch is buggy [\#604](https://github.com/vatesfr/xo-web/issues/604) +- Servers infos should not been refreshed while a field is being edited [\#595](https://github.com/vatesfr/xo-web/issues/595) +- Servers list should not been re-order while a field is being edited [\#594](https://github.com/vatesfr/xo-web/issues/594) +- Correctly display size in interface \(binary scale\) [\#592](https://github.com/vatesfr/xo-web/issues/592) +- Display failures on VM boot order modification [\#560](https://github.com/vatesfr/xo-web/issues/560) +- `vm.setBootOrder\(\)` should throw errors on failures \(non-HVM VMs\) [\#559](https://github.com/vatesfr/xo-web/issues/559) +- Hide boot order form for non-HVM VMs [\#558](https://github.com/vatesfr/xo-web/issues/558) +- Allow editing PV args even when empty \(but only for PV VMs\) [\#557](https://github.com/vatesfr/xo-web/issues/557) +- Crashes when using legacy event system [\#556](https://github.com/vatesfr/xo-web/issues/556) +- XenServer patches check error for 6.1 [\#555](https://github.com/vatesfr/xo-web/issues/555) +- activation plugin xo-server-transport-email [\#553](https://github.com/vatesfr/xo-web/issues/553) +- Server error with JSON on 32 bits Dom0 [\#552](https://github.com/vatesfr/xo-web/issues/552) +- Cloud Config drive shouldn't be created on default SR [\#548](https://github.com/vatesfr/xo-web/issues/548) +- Deep properties cannot be edited in plugins configuration form [\#521](https://github.com/vatesfr/xo-web/issues/521) +- Aborted VM export should cancel the operation [\#490](https://github.com/vatesfr/xo-web/issues/490) +- VM missing with same UUID after an inter-pool migration [\#284](https://github.com/vatesfr/xo-web/issues/284) + +## **4.10.0** (2015-11-27) + +Job management, email notifications, CoreOS/Docker, Quiesce snapshots... + +### Enhancements + +- Job management ([xo-web#487](https://github.com/vatesfr/xo-web/issues/487)) +- Patch upload on all connected servers ([xo-web#168](https://github.com/vatesfr/xo-web/issues/168)) +- Emergency shutdown ([xo-web#185](https://github.com/vatesfr/xo-web/issues/185)) +- CoreOS/docker template install ([xo-web#246](https://github.com/vatesfr/xo-web/issues/246)) +- Email for backups ([xo-web#308](https://github.com/vatesfr/xo-web/issues/308)) +- Console Clipboard ([xo-web#408](https://github.com/vatesfr/xo-web/issues/408)) +- Logs from CLI ([xo-web#486](https://github.com/vatesfr/xo-web/issues/486)) +- Save disconnected servers ([xo-web#489](https://github.com/vatesfr/xo-web/issues/489)) +- Snapshot with quiesce ([xo-web#491](https://github.com/vatesfr/xo-web/issues/491)) +- Start VM in reovery mode ([xo-web#495](https://github.com/vatesfr/xo-web/issues/495)) +- Username in logs ([xo-web#498](https://github.com/vatesfr/xo-web/issues/498)) +- Delete associated tokens with user ([xo-web#500](https://github.com/vatesfr/xo-web/issues/500)) +- Validate plugin configuration ([xo-web#503](https://github.com/vatesfr/xo-web/issues/503)) +- Avoid non configured plugins to be loaded ([xo-web#504](https://github.com/vatesfr/xo-web/issues/504)) +- Verbose API logs if configured ([xo-web#505](https://github.com/vatesfr/xo-web/issues/505)) +- Better backup overview ([xo-web#512](https://github.com/vatesfr/xo-web/issues/512)) +- VM auto power on ([xo-web#519](https://github.com/vatesfr/xo-web/issues/519)) +- Title property supported in config schema ([xo-web#522](https://github.com/vatesfr/xo-web/issues/522)) +- Start VM export only when necessary ([xo-web#534](https://github.com/vatesfr/xo-web/issues/534)) +- Input type should be number ([xo-web#538](https://github.com/vatesfr/xo-web/issues/538)) + +### Bug fixes + +- Numbers/int support in plugins config ([xo-web#531](https://github.com/vatesfr/xo-web/issues/531)) +- Boolean support in plugins config ([xo-web#528](https://github.com/vatesfr/xo-web/issues/528)) +- Keyboard unusable outside console ([xo-web#513](https://github.com/vatesfr/xo-web/issues/513)) +- UsernameField for SAML ([xo-web#513](https://github.com/vatesfr/xo-web/issues/513)) +- Wrong display of "no plugin found" ([xo-web#508](https://github.com/vatesfr/xo-web/issues/508)) +- Bower build error ([xo-web#488](https://github.com/vatesfr/xo-web/issues/488)) +- VM cloning should require SR permission ([xo-web#472](https://github.com/vatesfr/xo-web/issues/472)) +- Xen tools status ([xo-web#471](https://github.com/vatesfr/xo-web/issues/471)) +- Can't delete ghost user ([xo-web#464](https://github.com/vatesfr/xo-web/issues/464)) +- Stats with old versions of Node ([xo-web#463](https://github.com/vatesfr/xo-web/issues/463)) + +## **4.9.0** (2015-11-13) + +Automated DR, restore backup, VM copy + +### Enhancements + +- DR: schedule VM export on other host ([xo-web#447](https://github.com/vatesfr/xo-web/issues/447)) +- Scheduler logs ([xo-web#390](https://github.com/vatesfr/xo-web/issues/390) and [xo-web#477](https://github.com/vatesfr/xo-web/issues/477)) +- Restore backups ([xo-web#450](https://github.com/vatesfr/xo-web/issues/350)) +- Disable backup compression ([xo-web#467](https://github.com/vatesfr/xo-web/issues/467)) +- Copy VM to another SR (even remote) ([xo-web#475](https://github.com/vatesfr/xo-web/issues/475)) +- VM stats without time sync ([xo-web#460](https://github.com/vatesfr/xo-web/issues/460)) +- Stats perfs for high CPU numbers ([xo-web#461](https://github.com/vatesfr/xo-web/issues/461)) + +### Bug fixes + +- Rolling backup bug ([xo-web#484](https://github.com/vatesfr/xo-web/issues/484)) +- vCPUs/CPUs inversion in dashboard ([xo-web#481](https://github.com/vatesfr/xo-web/issues/481)) +- Machine to template ([xo-web#459](https://github.com/vatesfr/xo-web/issues/459)) + +### Misc + +- Console fix in XenServer ([xo-web#406](https://github.com/vatesfr/xo-web/issues/406)) + +## **4.8.0** (2015-10-29) + +Fully automated patch system, ACLs inheritance, stats performance improved. + +### Enhancements + +- ACLs inheritance ([xo-web#279](https://github.com/vatesfr/xo-web/issues/279)) +- Patch automatically all missing updates ([xo-web#281](https://github.com/vatesfr/xo-web/issues/281)) +- Intelligent stats polling ([xo-web#432](https://github.com/vatesfr/xo-web/issues/432)) +- Cache latest result of stats request ([xo-web#431](https://github.com/vatesfr/xo-web/issues/431)) +- Improve stats polling on multiple objects ([xo-web#433](https://github.com/vatesfr/xo-web/issues/433)) +- Patch upload task should display the patch name ([xo-web#449](https://github.com/vatesfr/xo-web/issues/449)) +- Backup filename for Windows ([xo-web#448](https://github.com/vatesfr/xo-web/issues/448)) +- Specific distro icons ([xo-web#446](https://github.com/vatesfr/xo-web/issues/446)) +- PXE boot for HVM ([xo-web#436](https://github.com/vatesfr/xo-web/issues/436)) +- Favicon display before sign in ([xo-web#428](https://github.com/vatesfr/xo-web/issues/428)) +- Registration renewal ([xo-web#424](https://github.com/vatesfr/xo-web/issues/424)) +- Reconnect to the host if pool merge fails ([xo-web#403](https://github.com/vatesfr/xo-web/issues/403)) +- Avoid brute force login ([xo-web#339](https://github.com/vatesfr/xo-web/issues/339)) +- Missing FreeBSD icon ([xo-web#136](https://github.com/vatesfr/xo-web/issues/136)) +- Hide halted objects in the Health view ([xo-web#457](https://github.com/vatesfr/xo-web/issues/457)) +- Click on "Remember me" label ([xo-web#438](https://github.com/vatesfr/xo-web/issues/438)) + +### Bug fixes + +- Pool patches in multiple pools not displayed ([xo-web#442](https://github.com/vatesfr/xo-web/issues/442)) +- VM Import crashes with Chrome ([xo-web#427](https://github.com/vatesfr/xo-web/issues/427)) +- Cannot open a direct link ([xo-web#371](https://github.com/vatesfr/xo-web/issues/371)) +- Patch display edge case ([xo-web#309](https://github.com/vatesfr/xo-web/issues/309)) +- VM snapshot should require user permission on SR ([xo-web#429](https://github.com/vatesfr/xo-web/issues/429)) + +## **4.7.0** (2015-10-12) + +Plugin config management and browser notifications. + +### Enhancements + +- Plugin management in the web interface ([xo-web#352](https://github.com/vatesfr/xo-web/issues/352)) +- Browser notifications ([xo-web#402](https://github.com/vatesfr/xo-web/issues/402)) +- Graph selector ([xo-web#400](https://github.com/vatesfr/xo-web/issues/400)) +- Circle packing visualization ([xo-web#374](https://github.com/vatesfr/xo-web/issues/374)) +- Password generation ([xo-web#397](https://github.com/vatesfr/xo-web/issues/397)) +- Password reveal during user creation ([xo-web#396](https://github.com/vatesfr/xo-web/issues/396)) +- Add host to a pool ([xo-web#62](https://github.com/vatesfr/xo-web/issues/62)) +- Better modal when removing a host from a pool ([xo-web#405](https://github.com/vatesfr/xo-web/issues/405)) +- Drop focus on CD/ISO selector ([xo-web#290](https://github.com/vatesfr/xo-web/issues/290)) +- Allow non persistent session ([xo-web#243](https://github.com/vatesfr/xo-web/issues/243)) + +### Bug fixes + +- VM export permission corrected ([xo-web#410](https://github.com/vatesfr/xo-web/issues/410)) +- Proper host removal in a pool ([xo-web#402](https://github.com/vatesfr/xo-web/issues/402)) +- Sub-optimal tooltip placement ([xo-web#421](https://github.com/vatesfr/xo-web/issues/421)) +- VM migrate host incorrect target ([xo-web#419](https://github.com/vatesfr/xo-web/issues/419)) +- Alone host can't leave its pool ([xo-web#414](https://github.com/vatesfr/xo-web/issues/414)) + +## **4.6.0** (2015-09-25) + +Tags management and new visualization. + +### Enhancements + +- Multigraph for correlation ([xo-web#358](https://github.com/vatesfr/xo-web/issues/358)) +- Tags management ([xo-web#367](https://github.com/vatesfr/xo-web/issues/367)) +- Google Provider for authentication ([xo-web#363](https://github.com/vatesfr/xo-web/issues/363)) +- Password change for users ([xo-web#362](https://github.com/vatesfr/xo-web/issues/362)) +- Better live migration process ([xo-web#237](https://github.com/vatesfr/xo-web/issues/237)) +- VDI search filter in SR view ([xo-web#222](https://github.com/vatesfr/xo-web/issues/222)) +- PV args during VM creation ([xo-web#112](https://github.com/vatesfr/xo-web/issues/330)) +- PV args management ([xo-web#394](https://github.com/vatesfr/xo-web/issues/394)) +- Confirmation dialog on important actions ([xo-web#350](https://github.com/vatesfr/xo-web/issues/350)) +- New favicon ([xo-web#369](https://github.com/vatesfr/xo-web/issues/369)) +- Filename of VM for exports ([xo-web#370](https://github.com/vatesfr/xo-web/issues/370)) +- ACLs rights edited on the fly ([xo-web#323](https://github.com/vatesfr/xo-web/issues/323)) +- Heatmap values now human readable ([xo-web#342](https://github.com/vatesfr/xo-web/issues/342)) + +### Bug fixes + +- Export backup fails if no tags specified ([xo-web#383](https://github.com/vatesfr/xo-web/issues/383)) +- Wrong login give an obscure error message ([xo-web#373](https://github.com/vatesfr/xo-web/issues/373)) +- Update view is broken during updates ([xo-web#356](https://github.com/vatesfr/xo-web/issues/356)) +- Settings/dashboard menu incorrect display ([xo-web#357](https://github.com/vatesfr/xo-web/issues/357)) +- Console View Not refreshing if the VM restart ([xo-web#107](https://github.com/vatesfr/xo-web/issues/107)) + +## **4.5.1** (2015-09-16) + +An issue in `xo-web` with the VM view. + +### Bug fix + +- Attach disk/new disk/create interface is broken ([xo-web#378](https://github.com/vatesfr/xo-web/issues/378)) + +## **4.5.0** (2015-09-11) + +A new dataviz (parallel coord), a new provider (GitHub) and faster consoles. + +### Enhancements + +- Parallel coordinates view ([xo-web#333](https://github.com/vatesfr/xo-web/issues/333)) +- Faster consoles ([xo-web#337](https://github.com/vatesfr/xo-web/issues/337)) +- Disable/hide button ([xo-web#268](https://github.com/vatesfr/xo-web/issues/268)) +- More details on missing-guest-tools ([xo-web#304](https://github.com/vatesfr/xo-web/issues/304)) +- Scheduler meta data export ([xo-web#315](https://github.com/vatesfr/xo-web/issues/315)) +- Better heatmap ([xo-web#330](https://github.com/vatesfr/xo-web/issues/330)) +- Faster dashboard ([xo-web#331](https://github.com/vatesfr/xo-web/issues/331)) +- Faster sunburst ([xo-web#332](https://github.com/vatesfr/xo-web/issues/332)) +- GitHub provider for auth ([xo-web#334](https://github.com/vatesfr/xo-web/issues/334)) +- Filter networks for users ([xo-web#347](https://github.com/vatesfr/xo-web/issues/347)) +- Add networks in ACLs ([xo-web#348](https://github.com/vatesfr/xo-web/issues/348)) +- Better looking login page ([xo-web#341](https://github.com/vatesfr/xo-web/issues/341)) +- Real time dataviz (dashboard) ([xo-web#349](https://github.com/vatesfr/xo-web/issues/349)) + +### Bug fixes + +- Typo in dashboard ([xo-web#355](https://github.com/vatesfr/xo-web/issues/355)) +- Global RAM usage fix ([xo-web#356](https://github.com/vatesfr/xo-web/issues/356)) +- Re-allowing XO behind a reverse proxy ([xo-web#361](https://github.com/vatesfr/xo-web/issues/361)) + +## **4.4.0** (2015-08-28) + +SSO and Dataviz are the main features for this release. + +### Enhancements + +- Dataviz storage usage ([xo-web#311](https://github.com/vatesfr/xo-web/issues/311)) +- Heatmap in health view ([xo-web#329](https://github.com/vatesfr/xo-web/issues/329)) +- SSO for SAML and other providers ([xo-web#327](https://github.com/vatesfr/xo-web/issues/327)) +- Better UI for ACL objects attribution ([xo-web#320](https://github.com/vatesfr/xo-web/issues/320)) +- Refresh the browser after an update ([xo-web#318](https://github.com/vatesfr/xo-web/issues/318)) +- Clean CSS and Flexbox usage ([xo-web#239](https://github.com/vatesfr/xo-web/issues/239)) + +### Bug fixes + +- Admin only accessible views ([xo-web#328](https://github.com/vatesfr/xo-web/issues/328)) +- Hide "base copy" VDIs ([xo-web#324](https://github.com/vatesfr/xo-web/issues/324)) +- ACLs on VIFs for non-admins ([xo-web#322](https://github.com/vatesfr/xo-web/issues/322)) +- Updater display problems ([xo-web#313](https://github.com/vatesfr/xo-web/issues/313)) + +## **4.3.0** (2015-07-22) + +Scheduler for rolling backups + +### Enhancements + +- Rolling backup scheduler ([xo-web#278](https://github.com/vatesfr/xo-web/issues/278)) +- Clean snapshots of removed VMs ([xo-web#301](https://github.com/vatesfr/xo-web/issues/301)) + +### Bug fixes + +- VM export ([xo-web#307](https://github.com/vatesfr/xo-web/issues/307)) +- Remove VM VDIs ([xo-web#303](https://github.com/vatesfr/xo-web/issues/303)) +- Pagination fails ([xo-web#302](https://github.com/vatesfr/xo-web/issues/302)) + +## **4.2.0** (2015-06-29) + +Huge performance boost, scheduler for rolling snapshots and backward compatibility for XS 5.x series + +### Enhancements + +- Rolling snapshots scheduler ([xo-web#176](https://github.com/vatesfr/xo-web/issues/176)) +- Huge perf boost ([xen-api#1](https://github.com/julien-f/js-xen-api/issues/1)) +- Backward compatibility ([xo-web#296](https://github.com/vatesfr/xo-web/issues/296)) + +### Bug fixes + +- VDI attached on a VM missing in SR view ([xo-web#294](https://github.com/vatesfr/xo-web/issues/294)) +- Better VM creation process ([xo-web#292](https://github.com/vatesfr/xo-web/issues/292)) + +## **4.1.0** (2015-06-10) + +Add the drag'n drop support from VM live migration, better ACLs groups UI. + +### Enhancements + +- Drag'n drop VM in tree view for live migration ([xo-web#277](https://github.com/vatesfr/xo-web/issues/277)) +- Better group view with objects ACLs ([xo-web#276](https://github.com/vatesfr/xo-web/issues/276)) +- Hide non-visible objects ([xo-web#272](https://github.com/vatesfr/xo-web/issues/272)) + +### Bug fixes + +- Convert to template displayed when the VM is not halted ([xo-web#286](https://github.com/vatesfr/xo-web/issues/286)) +- Lost some data when refresh some views ([xo-web#271](https://github.com/vatesfr/xo-web/issues/271)) +- Suspend button don't trigger any permission message ([xo-web#270](https://github.com/vatesfr/xo-web/issues/270)) +- Create network interfaces shouldn't call xoApi directly ([xo-web#269](https://github.com/vatesfr/xo-web/issues/269)) +- Don't plug automatically a disk or a VIF if the VM is not running ([xo-web#287](https://github.com/vatesfr/xo-web/issues/287)) + +## **4.0.2** (2015-06-01) + +An issue in `xo-server` with the password of default admin account and also a UI fix. + +### Bug fixes + +- Cannot modify admin account ([xo-web#265](https://github.com/vatesfr/xo-web/issues/265)) +- Password field seems to keep empty/reset itself after 1-2 seconds ([xo-web#264](https://github.com/vatesfr/xo-web/issues/264)) + +## **4.0.1** (2015-05-30) + +An issue with the updater in HTTPS was left in the *4.0.0*. This patch release fixed +it. + +### Bug fixes + +- allow updater to work in HTTPS ([xo-web#266](https://github.com/vatesfr/xo-web/issues/266)) + +## **4.0.0** (2015-05-29) + +[Blog post of this release](https://xen-orchestra.com/blog/xen-orchestra-4-0). + +### Enhancements + +- advanced ACLs ([xo-web#209](https://github.com/vatesfr/xo-web/issues/209)) +- xenserver update management ([xo-web#174](https://github.com/vatesfr/xo-web/issues/174) & [xo-web#259](https://github.com/vatesfr/xo-web/issues/259)) +- docker control ([xo-web#211](https://github.com/vatesfr/xo-web/issues/211)) +- better responsive design ([xo-web#252](https://github.com/vatesfr/xo-web/issues/252)) +- host stats ([xo-web#255](https://github.com/vatesfr/xo-web/issues/255)) +- pagination ([xo-web#221](https://github.com/vatesfr/xo-web/issues/221)) +- web updater +- better VM creation process([xo-web#256](https://github.com/vatesfr/xo-web/issues/256)) +- VM boot order([xo-web#251](https://github.com/vatesfr/xo-web/issues/251)) +- new mapped collection([xo-server#47](https://github.com/vatesfr/xo-server/issues/47)) +- resource location in ACL view ([xo-web#245](https://github.com/vatesfr/xo-web/issues/245)) + +### Bug fixes + +- wrong calulation of RAM amounts ([xo-web#51](https://github.com/vatesfr/xo-web/issues/51)) +- checkbox not aligned ([xo-web#253](https://github.com/vatesfr/xo-web/issues/253)) +- VM stats behavior more robust ([xo-web#250](https://github.com/vatesfr/xo-web/issues/250)) +- XO not on the root of domain ([xo-web#254](https://github.com/vatesfr/xo-web/issues/254)) + + +## **3.9.1** (2015-04-21) + +A few bugs hve made their way into *3.9.0*, this minor release fixes +them. + +### Bug fixes + +- correctly keep the VM guest metrics up to date ([xo-web#172](https://github.com/vatesfr/xo-web/issues/172)) +- fix edition of a VM snapshot ([b04111c](https://github.com/vatesfr/xo-server/commit/b04111c79ba8937778b84cb861bb7c2431162c11)) +- do not fetch stats if the VM state is transitioning ([a5c9880](https://github.com/vatesfr/xo-web/commit/a5c98803182792d2fe5ceb840ae1e23a8b767923)) +- fix broken Angular due to new version of Babel which breaks ngAnnotate ([0a9c868](https://github.com/vatesfr/xo-web/commit/0a9c868678d239e5b3e54b4d2bc3bd14b5400120)) +- feedback when connecting/disconnecting a server ([027d1e8](https://github.com/vatesfr/xo-web/commit/027d1e8cb1f2431e67042e1eec51690b2bc54ad7)) +- clearer error message if a server is unreachable ([06ca007](https://github.com/vatesfr/xo-server/commit/06ca0079b321e757aaa4112caa6f92a43193e35d)) + +## **3.9.0** (2015-04-20) + +[Blog post of this release](https://xen-orchestra.com/blog/xen-orchestra-3-9). + +### Enhancements + +- ability to manually connect/disconnect a server ([xo-web#88](https://github.com/vatesfr/xo-web/issues/88) & [xo-web#234](https://github.com/vatesfr/xo-web/issues/234)) +- display the connection status of a server ([xo-web#103](https://github.com/vatesfr/xo-web/issues/103)) +- better feedback when connecting to a server ([xo-web#210](https://github.com/vatesfr/xo-web/issues/210)) +- ability to add a local LVM SR ([xo-web#219](https://github.com/vatesfr/xo-web/issues/219)) +- display virtual GPUs in VM view ([xo-web#223](https://github.com/vatesfr/xo-web/issues/223)) +- ability to automatically start a VM with its host ([xo-web#224](https://github.com/vatesfr/xo-web/issues/224)) +- ability to create networks ([xo-web#225](https://github.com/vatesfr/xo-web/issues/225)) +- live charts for a VM CPU/disk/network & RAM ([xo-web#228](https://github.com/vatesfr/xo-web/issues/228) & [xo-server#51](https://github.com/vatesfr/xo-server/issues/51)) +- remove VM import progress notifications (redundant with the tasks list) ([xo-web#235](https://github.com/vatesfr/xo-web/issues/235)) +- XO-Server sources are compiled to JS prior distribution: less bugs & faster startups ([xo-server#50](https://github.com/vatesfr/xo-server/issues/50)) +- use XAPI `event.from()` instead of `event.next()` which leads to faster connection ([xo-server#52](https://github.com/vatesfr/xo-server/issues/52)) + +### Bug fixes + +- removed servers are properly disconnected ([xo-web#61](https://github.com/vatesfr/xo-web/issues/61)) +- fix VM creation with multiple interfaces ([xo-wb#229](https://github.com/vatesfr/xo-wb/issues/229)) +- disconnected servers are properly removed from the interface ([xo-web#234](https://github.com/vatesfr/xo-web/issues/234)) + +## **3.8.0** (2015-03-27) + +[Blog post of this release](https://xen-orchestra.com/blog/xen-orchestra-3-8). + +### Enhancements + +- initial plugin system ([xo-server#37](https://github.com/vatesfr/xo-server/issues/37)) +- new authentication system based on providers ([xo-server#39](https://github.com/vatesfr/xo-server/issues/39)) +- LDAP authentication plugin for XO-Server ([xo-server#40](https://github.com/vatesfr/xo-server/issues/40)) +- disk creation on the VM page ([xo-web#215](https://github.com/vatesfr/xo-web/issues/215)) +- network creation on the VM page ([xo-web#216](https://github.com/vatesfr/xo-web/issues/216)) +- charts on the host and SR pages ([xo-web#217](https://github.com/vatesfr/xo-web/issues/217)) + +### Bug fixes + +- fix *Invalid parameter(s)* message on the settings page ([xo-server#49](https://github.com/vatesfr/xo-server/issues/49)) +- fix mouse clicks in console ([xo-web#205](https://github.com/vatesfr/xo-web/issues/205)) +- fix user editing on the settings page ([xo-web#206](https://github.com/vatesfr/xo-web/issues/206)) + +## **3.7.0** (2015-03-06) + +*Highlights in this release are the [initial ACLs implementation](https://xen-orchestra.com/blog/xen-orchestra-3-7-is-out-acls-in-early-access), [live migration for VDIs](https://xen-orchestra.com/blog/moving-vdi-in-live) and the ability to [create a new storage repository](https://xen-orchestra.com/blog/create-a-storage-repository-with-xen-orchestra/).* + +### Enhancements + +- ability to live migrate a VM between hosts with different CPUs ([xo-web#126](https://github.com/vatesfr/xo-web/issues/126)) +- ability to live migrate a VDI ([xo-web#177](https://github.com/vatesfr/xo-web/issues/177)) +- display a notification on VM creation ([xo-web#178](https://github.com/vatesfr/xo-web/issues/178)) +- ability to create/attach a iSCSI/NFS/ISO SR ([xo-web#179](https://github.com/vatesfr/xo-web/issues/179)) +- display SR available space on VM creation ([xo-web#180](https://github.com/vatesfr/xo-web/issues/180)) +- ability to enable and disable host on the tree view ([xo-web#181](https://github.com/vatesfr/xo-web/issues/181) & [xo-web#182](https://github.com/vatesfr/xo-web/issues/182)) +- ability to suspend/resume a VM ([xo-web#186](https://github.com/vatesfr/xo-web/issues/186)) +- display Linux icon for SUSE Linux Enterprise Server distribution ([xo-web#187](https://github.com/vatesfr/xo-web/issues/187)) +- correctly handle incorrectly formated token in cookies ([xo-web#192](https://github.com/vatesfr/xo-web/issues/192)) +- display host manufacturer in host view ([xo-web#195](https://github.com/vatesfr/xo-web/issues/195)) +- only display task concerning authorized objects ([xo-web#197](https://github.com/vatesfr/xo-web/issues/197)) +- better welcome message ([xo-web#199](https://github.com/vatesfr/xo-web/issues/199)) +- initial ACLs ([xo-web#202](https://github.com/vatesfr/xo-web/issues/202)) +- display an action panel to rescan, remove, attach and forget a SR ([xo-web#203](https://github.com/vatesfr/xo-web/issues/203)) +- display current active tasks in navbar ([xo-web#204](https://github.com/vatesfr/xo-web/issues/204)) + +### Bug fixes + +- implements a proxy which fixes consoles over HTTPs ([xo#14](https://github.com/vatesfr/xo/issues/14)) +- fix tasks listing in host view ([xo-server#43](https://github.com/vatesfr/xo-server/issues/43)) +- fix console view on IE ([xo-web#184](https://github.com/vatesfr/xo-web/issues/184)) +- fix out of sync objects in XO-Web ([xo-web#142](https://github.com/vatesfr/xo-web/issues/142)) +- fix incorrect connection status displayed in login view ([xo-web#193](https://github.com/vatesfr/xo-web/issues/193)) +- fix *flickering* tree view ([xo-web#194](https://github.com/vatesfr/xo-web/issues/194)) +- single host pools should not have a dropdown menu in tree view ([xo-web#198](https://github.com/vatesfr/xo-web/issues/198)) + +## **3.6.0** (2014-11-28) + +### Enhancements + +- upload and apply patches to hosts/pools ([xo-web#150](https://github.com/vatesfr/xo-web/issues/150)) +- import VMs ([xo-web#151](https://github.com/vatesfr/xo-web/issues/151)) +- export VMs ([xo-web#152](https://github.com/vatesfr/xo-web/issues/152)) +- migrate VMs to another pool ([xo-web#153](https://github.com/vatesfr/xo-web/issues/153)) +- display pool even for single host ([xo-web#155](https://github.com/vatesfr/xo-web/issues/155)) +- start halted hosts with wake-on-LAN ([xo-web#154](https://github.com/vatesfr/xo-web/issues/154)) +- list of uploaded/applied patches ([xo-web#156](https://github.com/vatesfr/xo-web/issues/156)) +- use Angular 1.3 from npm ([xo-web#157](https://github.com/vatesfr/xo-web/issues/157) & [xo-web#160](https://github.com/vatesfr/xo-web/issues/160)) +- more feedbacks on actions ([xo-web#165](https://github.com/vatesfr/xo-web/issues/165)) +- only buttons compatible with VM states are displayed ([xo-web#166](https://github.com/vatesfr/xo-web/issues/166)) +- export VM snapshot ([xo-web#167](https://github.com/vatesfr/xo-web/issues/167)) +- plug/unplug a SR to a host ([xo-web#169](https://github.com/vatesfr/xo-web/issues/169)) +- plug a SR to all available hosts ([xo-web#170](https://github.com/vatesfr/xo-web/issues/170)) +- disks editing on SR page ([xo-web#171](https://github.com/vatesfr/xo-web/issues/171)) +- export of running VMs ([xo-server#36](https://github.com/vatesfr/xo-server/issues/36)) + +### Bug fixes + +- disks editing on VM page should work ([xo-web#86](https://github.com/vatesfr/xo-web/issues/86)) +- dropdown menus should close after selecting an item ([xo-web#140](https://github.com/vatesfr/xo-web/issues/140)) +- user creation should require a password ([xo-web#143](https://github.com/vatesfr/xo-web/issues/143)) +- server connection should require a user and a password ([xo-web#144](https://github.com/vatesfr/xo-web/issues/144)) +- snapshot deletion should work ([xo-server#147](https://github.com/vatesfr/xo-server/issues/147)) +- VM console should work in Chrome ([xo-web#149](https://github.com/vatesfr/xo-web/issues/149)) +- tooltips should work ([xo-web#163](https://github.com/vatesfr/xo-web/issues/163)) +- disk plugged status should be automatically refreshed ([xo-web#164](https://github.com/vatesfr/xo-web/issues/164)) +- deleting users without tokens should not trigger an error ([xo-server#34](https://github.com/vatesfr/xo-server/issues/34)) +- live pool master change should work ([xo-server#20](https://github.com/vatesfr/xo-server/issues/20)) + +## **3.5.1** (2014-08-19) + +### Bug fixes + +- pool view works again ([#139](https://github.com/vatesfr/xo-web/issues/139)) + +## **3.5.0** (2014-08-14) + +*[XO-Web](https://www.npmjs.org/package/xo-web) and [XO-Server](https://www.npmjs.org/package/xo-server) are now available as npm packages!* + +### Enhancements + +- XO-Server published on npm ([#26](https://github.com/vatesfr/xo-server/issues/26)) +- XO-Server config is now in `/etc/xo-server/config.yaml` ([#33](https://github.com/vatesfr/xo-server/issues/33)) +- paths in XO-Server's config are now relative to the config file ([#19](https://github.com/vatesfr/xo-server/issues/19)) +- use the Linux icon for Fedora ([#131](https://github.com/vatesfr/xo-web/issues/131)) +- start/stop/reboot buttons on console page ([#121](https://github.com/vatesfr/xo-web/issues/121)) +- settings page now only accessible to admin ([#77](https://github.com/vatesfr/xo-web/issues/77)) +- redirection to the home page when a VM is deleted from its own page ([#56](https://github.com/vatesfr/xo-web/issues/56)) +- XO-Web published on npm ([#123](https://github.com/vatesfr/xo-web/issues/123)) +- buid process now use Browserify (([#125](https://github.com/vatesfr/xo-web/issues/125), [#135](https://github.com/vatesfr/xo-web/issues/135))) +- view are now written in Jade instead of HTML ([#124](https://github.com/vatesfr/xo-web/issues/124)) +- CSS autoprefixer to improve compatibility ([#132](https://github.com/vatesfr/xo-web/issues/132), [#137](https://github.com/vatesfr/xo-web/issues/137)) + +### Bug fixes + +- force shutdown does not attempt a clean shutdown first anymore ([#29](https://github.com/vatesfr/xo-server/issues/29)) +- shutdown hosts are now correctly reported as such ([#31](https://github.com/vatesfr/xo-web/issues/31)) +- incorrect VM metrics ([#54](https://github.com/vatesfr/xo-web/issues/54), [#68](https://github.com/vatesfr/xo-web/issues/68), [#108](https://github.com/vatesfr/xo-web/issues/108)) +- an user cannot delete itself ([#104](https://github.com/vatesfr/xo-web/issues/104)) +- in VM creation, required fields are now marked as such ([#113](https://github.com/vatesfr/xo-web/issues/113), [#114](https://github.com/vatesfr/xo-web/issues/114)) + +## **3.4.0** (2014-05-22) + +*Highlight in this release is the new events system between XO-Web +and XO-Server which results in less bandwidth consumption as well as +better performance and reactivity.* + +### Enhancements + +- events system between XO-Web and XO-Server ([#52](https://github.com/vatesfr/xo-web/issues/52)) +- ability to clone/copy a VM ([#116](https://github.com/vatesfr/xo-web/issues/116)) +- mandatory log in page ([#120](https://github.com/vatesfr/xo-web/issues/120)) + +### Bug fixes + +- failure in VM creation ([#111](https://github.com/vatesfr/xo-web/issues/111)) + +## **3.3.1** (2014-03-28) + +### Enhancements + +- console view is now prettier ([#92](https://github.com/vatesfr/xo-web/issues/92)) + +### Bug fixes + +- VM creation fails to incorrect dependencies ([xo-server/#24](https://github.com/vatesfr/xo-server/issues/24)) +- VDIs list in SR view is blinking ([#109](https://github.com/vatesfr/xo-web/issues/109)) + +## **3.3.0** (2014-03-07) + +### Enhancements + +- [Grunt](http://gruntjs.com/) has been replaced by [gulp.js](http://gulpjs.com/) ([#91](https://github.com/vatesfr/xo-web/issues/91)) +- a host can be detached from a pool ([#98](https://github.com/vatesfr/xo-web/issues/98)) +- snapshots management in VM view ([#99](https://github.com/vatesfr/xo-web/issues/99)) +- log deletion in VM view ([#100](https://github.com/vatesfr/xo-web/issues/100)) + +### Bug fixes + +- *Snapshot* not working in VM view ([#95](https://github.com/vatesfr/xo-web/issues/95)) +- Host *Reboot*/*Restart toolstack*/*Shutdown* not working in main view ([#97](https://github.com/vatesfr/xo-web/issues/97)) +- Bower cannot install `angular` automatically due to a version conflict ([#101](https://github.com/vatesfr/xo-web/issues/101)) +- Bower installs an incorrect version of `angular-animate` ([#102](https://github.com/vatesfr/xo-web/issues/102)) + +## **3.2.0** (2014-02-21) + +### Enhancements + +- dependencies' versions should be fixed to ease deployment ([#93](https://github.com/vatesfr/xo-web/issues/93)) +- badges added to the README to see whether dependencies are up to date ([#90](https://github.com/vatesfr/xo-web/issues/90)) +- an error notification has been added when the connection to XO-Server failed ([#89](https://github.com/vatesfr/xo-web/issues/89)) +- in host view, there is now a link to the host console ([#87](https://github.com/vatesfr/xo-web/issues/87)) +- in VM view, deleting a disk requires a confirmation ([#85](https://github.com/vatesfr/xo-web/issues/85)) +- the VM and console icons are now different ([#80](https://github.com/vatesfr/xo-web/issues/80)) + +### Bug fixes + +- consoles now work in Google Chrome \o/ ([#46](https://github.com/vatesfr/xo-web/issues/46)) +- in host view, many buttons were not working ([#79](https://github.com/vatesfr/xo-web/issues/79)) +- in main view, incorrect icons were fixes ([#81](https://github.com/vatesfr/xo-web/issues/81)) +- MAC addresses should not be ignored during VM creation ([#94](https://github.com/vatesfr/xo-web/issues/94)) + +## **3.1.0** (2014-02-14) + +### Enhancements + +- in VM view, interfaces' network should be displayed ([#64](https://github.com/vatesfr/xo-web/issues/64)) +- middle-click or `Ctrl`+click should open new windows (even on pseudo-links) ([#66](https://github.com/vatesfr/xo-web/issues/66)) +- lists should use natural sorting (e.g. *VM 2* before *VM 10*) ([#69](https://github.com/vatesfr/xo-web/issues/69)) + +### Bug fixes + +- consoles are not implemented for hosts ([#57](https://github.com/vatesfr/xo-web/issues/57)) +- it makes no sense to remove a stand-alone host from a pool (58) +- in VM view, the migrate button is not working ([#59](https://github.com/vatesfr/xo-web/issues/59)) +- pool and host names overflow their box in the main view ([#63](https://github.com/vatesfr/xo-web/issues/63)) +- in host view, interfaces incorrectly named *networks* and VLAN not shown ([#70](https://github.com/vatesfr/xo-web/issues/70)) +- VM suspended state is not properly handled ([#71](https://github.com/vatesfr/xo-web/issues/71)) +- unauthenticated users should not be able to access to consoles ([#73](https://github.com/vatesfr/xo-web/issues/73)) +- incorrect scroll (under the navbar) when the view changes ([#74](https://github.com/vatesfr/xo-web/issues/74)) diff --git a/packages/xo-web/ISSUE_TEMPLATE.md b/packages/xo-web/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..0b71d6d03 --- /dev/null +++ b/packages/xo-web/ISSUE_TEMPLATE.md @@ -0,0 +1,28 @@ + + +### Context + +- **XO version**: XO appliance / `stable` branch / `next-release` branch + +If from the sources: + +- **Component**: xo-web / xo-server / *unknown* +- **Node/npm version**: *just execute `npm version`* + +### Expected behavior + + + +### Current behavior + + diff --git a/packages/xo-web/LICENSE b/packages/xo-web/LICENSE new file mode 100644 index 000000000..dbbe35581 --- /dev/null +++ b/packages/xo-web/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/packages/xo-web/README.md b/packages/xo-web/README.md new file mode 100644 index 000000000..c0b400921 --- /dev/null +++ b/packages/xo-web/README.md @@ -0,0 +1,91 @@ +# Xen Orchestra Web [![Chat with us](https://storage.crisp.im/plugins/images/936925df-f37b-4ba8-bab0-70cd2edcb0be/badge.svg)](https://go.crisp.im/chat/embed/?website_id=-JzqzzwddSV7bKGtEyAQ) [![Build Status](https://travis-ci.org/vatesfr/xo-web.png?branch=master)](https://travis-ci.org/vatesfr/xo-web) + +![](http://i.imgur.com/tRffA5y.png) + +XO-Web is part of [Xen Orchestra](https://github.com/vatesfr/xo), a web interface for XenServer or XAPI enabled hosts. + +It is a web client for [XO-Server](https://github.com/vatesfr/xo-server). + +___ + +## Installation + +XOA or manual install procedure is [available here](https://xen-orchestra.com/docs/installation.html) + +## Compilation + +Production build: + +``` +$ npm run build +``` + +Development build: + +``` +$ npm run dev +``` + +### Environment + +#### `NODE_ENV` + +Set to *production* it disables many checks which result in increased +performance. + +#### `XOA_PLAN` + +- 1: Free +- 2: Starter +- 3: Enterprise +- 4: Premium +- 5: Sources + +```js +if (process.env.XOA_PLAN < 5) { + console.log('included only in XOA') +} + +if (process.env.XOA_PLAN > 3) { + console.log('included only in Premium and Sources') +} +``` + +## How to report a bug? + +If you are certain the bug is exclusively related to XO-Web, you may use the [bugtracker of this repository](https://github.com/vatesfr/xo-web/issues). + +Otherwise, please consider using the [bugtracker of the general repository](https://github.com/vatesfr/xo/issues). + +## Process for new release + +```bash +# Switch to the stable branch. +git checkout stable + +# Fetches latest changes. +git pull --ff-only + +# Merge changes of the next-release branch. +git merge next-release + +# Increment the version (patch, minor or major). +npm version minor + +# Go back to the next-release branch. +git checkout next-release + +# Fetches the last changes (the merge and version bump) from stable to +# next-release. +git merge --ff-only stable + +# Push the changes on git. +git push --follow-tags origin stable next-release + +# Publish this release to npm. +npm publish +``` + +## License + +AGPL3 © [Vates SAS](http://vates.fr) diff --git a/packages/xo-web/gulpfile.js b/packages/xo-web/gulpfile.js new file mode 100644 index 000000000..94ee77124 --- /dev/null +++ b/packages/xo-web/gulpfile.js @@ -0,0 +1,303 @@ +'use strict' + +// =================================================================== + +const SRC_DIR = __dirname + '/src' // eslint-disable-line no-path-concat +const DIST_DIR = __dirname + '/dist' // eslint-disable-line no-path-concat + +// Port to use for the livereload server. +// +// It must be available and if possible unique to not conflict with other projects. +// http://www.random.org/integers/?num=1&min=1024&max=65535&col=1&base=10&format=plain&rnd=new +const LIVERELOAD_PORT = 26242 + +const PRODUCTION = process.env.NODE_ENV === 'production' +const DEVELOPMENT = !PRODUCTION + +if (!process.env.XOA_PLAN) { + process.env.XOA_PLAN = '5' // Open Source +} + +// =================================================================== + +const gulp = require('gulp') + +// =================================================================== + +function lazyFn (factory) { + let fn = function () { + fn = factory() + return fn.apply(this, arguments) + } + + return function () { + return fn.apply(this, arguments) + } +} + +// ------------------------------------------------------------------- + +const livereload = lazyFn(function () { + const livereload = require('gulp-refresh') + livereload.listen({ + port: LIVERELOAD_PORT, + }) + + return livereload +}) + +const pipe = lazyFn(function () { + let current + function pipeCore (streams) { + let i, n, stream + for (i = 0, n = streams.length; i < n; ++i) { + stream = streams[i] + if (!stream) { + // Nothing to do + } else if (stream instanceof Array) { + pipeCore(stream) + } else { + current = current ? current.pipe(stream) : stream + } + } + } + + const push = Array.prototype.push + return function (streams) { + try { + if (!(streams instanceof Array)) { + streams = [] + push.apply(streams, arguments) + } + + pipeCore(streams) + + return current + } finally { + current = null + } + } +}) + +const resolvePath = lazyFn(function () { + return require('path').resolve +}) + +// ------------------------------------------------------------------- + +// Similar to `gulp.src()` but the pattern is relative to `SRC_DIR` +// and files are automatically watched when not in production mode. +const src = lazyFn(function () { + function resolve (path) { + return path ? resolvePath(SRC_DIR, path) : SRC_DIR + } + + return PRODUCTION + ? function src (pattern, opts) { + const base = resolve(opts && opts.base) + + return gulp.src(pattern, { + base: base, + cwd: base, + passthrough: opts && opts.passthrough, + sourcemaps: opts && opts.sourcemaps, + }) + } + : function src (pattern, opts) { + const base = resolve(opts && opts.base) + + return pipe( + gulp.src(pattern, { + base: base, + cwd: base, + passthrough: opts && opts.passthrough, + sourcemaps: opts && opts.sourcemaps, + }), + require('gulp-watch')(pattern, { + base: base, + cwd: base, + }), + require('gulp-plumber')() + ) + } +}) + +// Similar to `gulp.dest()` but the output directory is relative to +// `DIST_DIR` and default to `./`, and files are automatically live- +// reloaded when not in production mode. +const dest = lazyFn(function () { + function resolve (path) { + return path ? resolvePath(DIST_DIR, path) : DIST_DIR + } + + const opts = { + sourcemaps: '.', + } + + return PRODUCTION + ? function dest (path) { + return gulp.dest(resolve(path), opts) + } + : function dest (path) { + const stream = gulp.dest(resolve(path), opts) + stream.pipe(livereload()) + return stream + } +}) + +// =================================================================== + +function browserify (path, opts) { + if (opts == null) { + opts = {} + } + + let bundler = require('browserify')(path, { + basedir: SRC_DIR, + debug: true, + extensions: opts.extensions, + fullPaths: false, + paths: SRC_DIR + '/common', + standalone: opts.standalone, + + // Required by Watchify. + cache: {}, + packageCache: {}, + }) + + const plugins = opts.plugins + for (let i = 0, n = plugins && plugins.length; i < n; ++i) { + const plugin = plugins[i] + bundler.plugin(require(plugin[0]), plugin[1]) + } + + if (PRODUCTION) { + // FIXME: does not work with react-intl (?!) + // bundler.plugin('bundle-collapser/plugin') + } else { + bundler = require('watchify')(bundler, { + // do not watch in `node_modules` + // https://github.com/browserify/watchify#options + ignoreWatch: true, + }) + } + + // Append the extension if necessary. + if (!/\.js$/.test(path)) { + path += '.js' + } + path = resolvePath(SRC_DIR, path) + + let stream = new (require('readable-stream'))({ + objectMode: true, + }) + + let write + function bundle () { + bundler.bundle(function onBundle (error, buffer) { + if (error) { + stream.emit('error', error) + return + } + + write( + new (require('vinyl'))({ + base: SRC_DIR, + contents: buffer, + path: path, + }) + ) + }) + } + + if (PRODUCTION) { + write = function (data) { + stream.push(data) + stream.push(null) + } + } else { + stream = require('gulp-plumber')().pipe(stream) + write = function (data) { + stream.push(data) + } + + bundler.on('update', bundle) + } + + stream._read = function () { + this._read = function () {} + bundle() + } + + return stream +} + +// =================================================================== + +gulp.task(function buildPages () { + return pipe( + src('index.pug'), + require('gulp-pug')(), + DEVELOPMENT && + require('gulp-embedlr')({ + port: LIVERELOAD_PORT, + }), + dest() + ) +}) + +gulp.task(function buildScripts () { + return pipe( + browserify('./index.js', { + plugins: [ + // ['css-modulesify', { + [ + 'modular-cssify', + { + css: DIST_DIR + '/modules.css', + from: undefined, + }, + ], + ], + }), + require('gulp-sourcemaps').init({ loadMaps: true }), + PRODUCTION && require('gulp-uglify/composer')(require('uglify-es'))(), + dest() + ) +}) + +gulp.task(function buildStyles () { + return pipe( + src('index.scss', { sourcemaps: true }), + require('gulp-sass')(), + require('gulp-autoprefixer')(['last 1 version', '> 1%']), + PRODUCTION && require('gulp-csso')(), + dest() + ) +}) + +gulp.task(function copyAssets () { + return pipe( + src(['assets/**/*', 'favicon.*']), + src('fontawesome-webfont.*', { + base: __dirname + '/node_modules/font-awesome/fonts', // eslint-disable-line no-path-concat + passthrough: true, + }), + src(['!*.css', 'font-mfizz.*'], { + base: __dirname + '/node_modules/font-mfizz/dist', // eslint-disable-line no-path-concat + passthrough: true, + }), + dest() + ) +}) + +gulp.task( + 'build', + gulp.parallel('buildPages', 'buildScripts', 'buildStyles', 'copyAssets') +) + +// ------------------------------------------------------------------- + +gulp.task(function clean (done) { + require('rimraf')(DIST_DIR, done) +}) diff --git a/packages/xo-web/package.json b/packages/xo-web/package.json new file mode 100644 index 000000000..bc30c8a5e --- /dev/null +++ b/packages/xo-web/package.json @@ -0,0 +1,229 @@ +{ + "private": false, + "name": "xo-web", + "version": "5.16.0", + "license": "AGPL-3.0", + "description": "Web interface client for Xen-Orchestra", + "keywords": [ + "xen", + "orchestra", + "xen-orchestra", + "web" + ], + "homepage": "https://github.com/vatesfr/xo-web", + "bugs": "https://github.com/vatesfr/xo-web/issues", + "repository": { + "type": "git", + "url": "https://github.com/vatesfr/xo-web" + }, + "author": { + "name": "Julien Fontanet", + "email": "julien.fontanet@vates.fr" + }, + "preferGlobal": false, + "main": "dist/", + "bin": {}, + "files": [ + "dist/" + ], + "engines": { + "node": ">=4", + "npm": ">=3" + }, + "devDependencies": { + "@nraynaud/novnc": "0.6.1", + "ansi_up": "^2.0.2", + "asap": "^2.0.6", + "babel-core": "^6.26.0", + "babel-eslint": "^8.1.2", + "babel-plugin-dev": "^1.0.0", + "babel-plugin-lodash": "^3.2.11", + "babel-plugin-transform-decorators-legacy": "^1.3.4", + "babel-plugin-transform-react-constant-elements": "^6.5.0", + "babel-plugin-transform-react-inline-elements": "^6.6.5", + "babel-plugin-transform-react-jsx-self": "^6.11.0", + "babel-plugin-transform-react-jsx-source": "^6.9.0", + "babel-plugin-transform-runtime": "^6.6.0", + "babel-preset-env": "^1.6.1", + "babel-preset-react": "^6.5.0", + "babel-preset-stage-0": "^6.24.1", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babelify": "^8.0.0", + "benchmark": "^2.1.0", + "bootstrap": "4.0.0-alpha.5", + "browserify": "^15.1.0", + "bundle-collapser": "^1.3.0", + "chartist": "^0.10.1", + "chartist-plugin-legend": "^0.6.1", + "chartist-plugin-tooltip": "0.0.11", + "classnames": "^2.2.3", + "complex-matcher": "^0.2.1", + "cookies-js": "^1.2.2", + "d3": "^4.12.2", + "debounce-input-decorator": "^0.1.0", + "dependency-check": "^3.0.0", + "enzyme": "^3.3.0", + "enzyme-adapter-react-15": "^1.0.5", + "enzyme-to-json": "^3.3.0", + "eslint": "^4.14.0", + "eslint-config-standard": "^10.2.1", + "eslint-config-standard-jsx": "^4.0.2", + "eslint-plugin-import": "^2.8.0", + "eslint-plugin-node": "^5.2.1", + "eslint-plugin-promise": "^3.6.0", + "eslint-plugin-react": "^7.4.0", + "eslint-plugin-standard": "^3.0.1", + "event-to-promise": "^0.8.0", + "font-awesome": "^4.7.0", + "font-mfizz": "^2.4.1", + "get-stream": "^3.0.0", + "globby": "^7.1.1", + "gulp": "^4.0.0", + "gulp-autoprefixer": "^4.1.0", + "gulp-csso": "^3.0.0", + "gulp-embedlr": "^0.5.2", + "gulp-plumber": "^1.1.0", + "gulp-pug": "^3.1.0", + "gulp-refresh": "^1.1.0", + "gulp-sass": "^3.0.0", + "gulp-sourcemaps": "^2.6.2", + "gulp-uglify": "^3.0.0", + "gulp-watch": "^5.0.0", + "human-format": "^0.10.0", + "husky": "^0.14.3", + "immutable": "^3.8.2", + "index-modules": "^0.3.0", + "is-ip": "^2.0.0", + "jest": "^22.0.4", + "jsonrpc-websocket-client": "^0.2.0", + "kindof": "^2.0.0", + "later": "^1.2.0", + "lint-staged": "^6.0.0", + "lodash": "^4.6.1", + "loose-envify": "^1.1.0", + "make-error": "^1.3.2", + "marked": "^0.3.9", + "modular-cssify": "^7.2.0", + "moment": "^2.20.1", + "moment-timezone": "^0.5.14", + "notifyjs": "^3.0.0", + "prettier": "^1.9.2", + "promise-toolbox": "^0.9.5", + "prop-types": "^15.6.0", + "random-password": "^0.1.2", + "react": "^15.4.1", + "react-addons-shallow-compare": "^15.6.2", + "react-addons-test-utils": "^15.6.2", + "react-bootstrap-4": "^0.29.1", + "react-chartist": "^0.13.0", + "react-copy-to-clipboard": "^5.0.1", + "react-dnd": "^2.5.4", + "react-dnd-html5-backend": "^2.5.4", + "react-document-title": "^2.0.2", + "react-dom": "^15.4.1", + "react-dropzone": "^4.2.3", + "react-intl": "^2.4.0", + "react-key-handler": "^1.0.1", + "react-notify": "^3.0.0", + "react-overlays": "^0.8.3", + "react-redux": "^5.0.6", + "react-router": "^3.0.0", + "react-select": "^1.1.0", + "react-shortcuts": "^2.0.0", + "react-sparklines": "1.6.0", + "react-test-renderer": "^15.6.2", + "react-virtualized": "^9.15.0", + "readable-stream": "^2.3.3", + "redux": "^3.7.2", + "redux-thunk": "^2.0.1", + "reselect": "^2.5.4", + "semver": "^5.4.1", + "styled-components": "^3.1.5", + "tar-stream": "^1.5.5", + "uglify-es": "^3.3.4", + "uncontrollable-input": "^0.1.1", + "url-parse": "^1.2.0", + "value-matcher": "^0.0.0", + "vinyl": "^2.1.0", + "watchify": "^3.7.0", + "whatwg-fetch": "^2.0.3", + "xml2js": "^0.4.19", + "xo-acl-resolver": "^0.2.3", + "xo-common": "^0.1.1", + "xo-lib": "^0.8.0", + "xo-remote-parser": "^0.3" + }, + "scripts": { + "benchmarks": "./tools/run-benchmarks.js 'src/**/*.bench.js'", + "build": "npm run build-indexes && NODE_ENV=production gulp build", + "build-indexes": "index-modules --auto src", + "clean": "gulp clean", + "dev": "npm run build-indexes && NODE_ENV=development gulp build", + "dev-test": "jest --watch", + "lint-staged-stash": "touch .lint-staged && git stash save --include-untracked --keep-index && true", + "lint-staged-unstash": "git stash pop && rm -f .lint-staged && true", + "posttest": "eslint --ignore-path .gitignore src/", + "prebuild": "npm run clean", + "precommit": "lint-staged", + "predev": "npm run clean", + "prepublishOnly": "npm run build", + "test": "jest" + }, + "browserify": { + "transform": [ + "babelify", + "loose-envify" + ] + }, + "babel": { + "env": { + "development": { + "plugins": [ + "transform-react-jsx-self", + "transform-react-jsx-source" + ] + }, + "production": { + "plugins": [ + "transform-react-constant-elements", + "transform-react-inline-elements" + ] + } + }, + "plugins": [ + "dev", + "lodash", + "transform-decorators-legacy", + "transform-runtime" + ], + "presets": [ + [ + "env", + { + "targets": { + "browsers": ">2%" + } + } + ], + "react", + "stage-0" + ] + }, + "jest": { + "setupTestFrameworkScriptFile": "./setup-tests.js", + "snapshotSerializers": [ + "enzyme-to-json/serializer" + ] + }, + "lint-staged": { + "*.js": [ + "lint-staged-stash", + "prettier --write", + "eslint --fix", + "jest --findRelatedTests --passWithNoTests", + "git add", + "lint-staged-unstash" + ] + } +} diff --git a/packages/xo-web/setup-tests.js b/packages/xo-web/setup-tests.js new file mode 100644 index 000000000..89def87f6 --- /dev/null +++ b/packages/xo-web/setup-tests.js @@ -0,0 +1,4 @@ +import { configure } from 'enzyme' +import Adapter from 'enzyme-adapter-react-15' + +configure({ adapter: new Adapter() }) diff --git a/packages/xo-web/src/assets/loading.svg b/packages/xo-web/src/assets/loading.svg new file mode 100644 index 000000000..9e0af0779 --- /dev/null +++ b/packages/xo-web/src/assets/loading.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/xo-web/src/assets/logo.png b/packages/xo-web/src/assets/logo.png new file mode 100644 index 000000000..a3f5ce33e Binary files /dev/null and b/packages/xo-web/src/assets/logo.png differ diff --git a/packages/xo-web/src/chartist.scss b/packages/xo-web/src/chartist.scss new file mode 100644 index 000000000..347eb6856 --- /dev/null +++ b/packages/xo-web/src/chartist.scss @@ -0,0 +1,133 @@ + +// CHARTIST =================================================================== + +$ct-series-colors: ( + $brand-success, + $brand-primary, + #f17cb0, + #86797d, + #b276b2, + #f15854, + #b2912f, + #decf3f, + #dda458, + #60bd68, + #4d4d4d, + #eacf7d, + #b2c326, + #6188e2, + #a748ca +) !default; + +@import "../node_modules/chartist/dist/scss/settings/_chartist-settings"; +@import "../node_modules/chartist/dist/scss/chartist"; + +.ct-chart { + display: flex; + flex-direction: column-reverse; +} + +// safari has a bug in flex computing that prevent charts from showing see #1755 +// by fixing the height with a value found in Chrome it seems like it fixes the issue without breaking the layout +// elsewhere +.dashboardItem .ct-chart { + height: 150px; +} + +// Line in charts with only 2px in width +.ct-line { + stroke-width: 2px; +} + +.ct-bar { + stroke-width: 10%; +} + +.ct-point { + stroke-width: 30px; + stroke-opacity: 0!important; +} + +.ct-point:hover { + stroke-opacity: 0.2!important; + stroke-width: 20px; +} + +.ct-tooltip { + position: absolute; + display: inline-block; + min-width: 5em; + padding: 8px 10px; + background: #383838; + color: #fff; + text-align: center; + pointer-events: none; + z-index: 10; + font-weight: 700; + + // Arrow! + &:before { + bottom: -14px; + top: 100%; + left: 50%; + border: solid transparent; + content: ''; + height: 0; + width: 0; + pointer-events: none; + border-color: rgba(251, 249, 228, 0); + border-top-color: #383838; + border-width: 7px; + margin-left: -8px; + } + + &.hide { + display: block; + opacity: 0; + visibility: hidden; + } +} + +// CHARTIST LEGEND ============================================================= + +.ct-legend { + bottom: 0; + margin-bottom: -1em; + + li { + position: relative; + padding-left: 0.5em; + list-style-type: none; + display: inline-block; + margin-right: 0.5em; + font-size: 0.8em; + } + + li:before { + display: inline-block; + width: 1em; + height: 1em; + left: 0; + content: ''; + border: 3px solid transparent; + border-radius: 2px; + margin-right: 0.2em; + } + + li.inactive:before { + background: transparent; + } + + &.ct-legend-inside { + position: absolute; + top: 0; + right: 0; + } + + @for $i from 0 to length($ct-series-colors) { + .ct-series-#{$i}:before { + background-color: nth($ct-series-colors, $i + 1); + border-color: nth($ct-series-colors, $i + 1); + } + } +} diff --git a/packages/xo-web/src/common/__snapshots__/grid.spec.js.snap b/packages/xo-web/src/common/__snapshots__/grid.spec.js.snap new file mode 100644 index 000000000..2397db79d --- /dev/null +++ b/packages/xo-web/src/common/__snapshots__/grid.spec.js.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Col 1`] = ` +
+`; + +exports[`Container 1`] = ` +
+`; + +exports[`Row 1`] = ` +
+`; diff --git a/packages/xo-web/src/common/action-bar.js b/packages/xo-web/src/common/action-bar.js new file mode 100644 index 000000000..3c8bf43ea --- /dev/null +++ b/packages/xo-web/src/common/action-bar.js @@ -0,0 +1,60 @@ +import ActionButton from 'action-button' +import propTypes from 'prop-types-decorator' +import React, { cloneElement } from 'react' +import { noop } from 'lodash' + +import ButtonGroup from './button-group' + +export const Action = ({ + display, + handler, + handlerParam, + icon, + label, + pending, + redirectOnSuccess, +}) => ( + + {display === 'both' && label} + +) + +Action.propTypes = { + display: propTypes.oneOf(['icon', 'both']), + handler: propTypes.func.isRequired, + icon: propTypes.string.isRequired, + label: propTypes.node, + pending: propTypes.bool, + redirectOnSuccess: propTypes.string, +} + +const ActionBar = ({ children, handlerParam = noop, display = 'both' }) => ( + + {React.Children.map(children, (child, key) => { + if (!child) { + return + } + + const { props } = child + return cloneElement(child, { + display: props.display || display, + handlerParam: props.handlerParam || handlerParam, + key, + }) + })} + +) + +ActionBar.propTypes = { + display: propTypes.oneOf(['icon', 'both']), + handlerParam: propTypes.any, +} +export { ActionBar as default } diff --git a/packages/xo-web/src/common/action-button.js b/packages/xo-web/src/common/action-button.js new file mode 100644 index 000000000..71e78a1f5 --- /dev/null +++ b/packages/xo-web/src/common/action-button.js @@ -0,0 +1,151 @@ +import isFunction from 'lodash/isFunction' +import React from 'react' + +import Button from './button' +import Component from './base-component' +import Icon from './icon' +import logError from './log-error' +import propTypes from './prop-types-decorator' +import Tooltip from './tooltip' +import { error as _error } from './notification' + +@propTypes({ + // React element to use as button content + children: propTypes.node, + + // whether this button is disabled (default to false) + disabled: propTypes.bool, + + // form identifier + // + // if provided, this button and its action are associated to this + // form for the submit event + form: propTypes.string, + + // function to call when the action is triggered (via a clik on the + // button or submit on the form) + handler: propTypes.func.isRequired, + + // optional value which will be passed as first param to the handler + handlerParam: propTypes.any, + + // XO icon to use for this button + icon: propTypes.string.isRequired, + + // whether the action of this action is already underway + pending: propTypes.bool, + + // path to redirect to when the triggered action finish successfully + // + // if a function, it will be called with the result of the action to + // compute the path + redirectOnSuccess: propTypes.oneOfType([propTypes.func, propTypes.string]), + + // React element to use tooltip for the component + tooltip: propTypes.node, +}) +export default class ActionButton extends Component { + static contextTypes = { + router: propTypes.object, + } + + async _execute () { + if (this.props.pending || this.state.working) { + return + } + + const { children, handler, handlerParam, tooltip } = this.props + + try { + this.setState({ + error: undefined, + working: true, + }) + + const result = await handler(handlerParam) + + const { redirectOnSuccess } = this.props + if (redirectOnSuccess) { + return this.context.router.push( + isFunction(redirectOnSuccess) + ? redirectOnSuccess(result) + : redirectOnSuccess + ) + } + + this.setState({ + working: false, + }) + } catch (error) { + this.setState({ + error, + working: false, + }) + + // ignore when undefined because it usually means that the action has been canceled + if (error !== undefined) { + logError(error) + _error( + children || tooltip || error.name, + error.message || String(error) + ) + } + } + } + _execute = ::this._execute + + _eventListener = event => { + event.preventDefault() + this._execute() + } + + componentDidMount () { + const { form } = this.props + + if (form) { + document + .getElementById(form) + .addEventListener('submit', this._eventListener) + } + } + + componentWillUnmount () { + const { form } = this.props + + if (form) { + document + .getElementById(form) + .removeEventListener('submit', this._eventListener) + } + } + + render () { + const { + props: { children, icon, pending, tooltip, ...props }, + state: { error, working }, + } = this + + if (error !== undefined) { + props.btnStyle = 'warning' + } + if (pending || working) { + props.disabled = true + } + delete props.handler + delete props.handlerParam + if (props.form === undefined) { + props.onClick = this._execute + } + delete props.redirectOnSuccess + + const button = ( + + ) + + return tooltip ? {button} : button + } +} diff --git a/packages/xo-web/src/common/action-row-button/index.css b/packages/xo-web/src/common/action-row-button/index.css new file mode 100644 index 000000000..e23658595 --- /dev/null +++ b/packages/xo-web/src/common/action-row-button/index.css @@ -0,0 +1,7 @@ +.button { + opacity: 0.5; +} + +tr:hover .button, tr:focus .button { + opacity: 1; +} diff --git a/packages/xo-web/src/common/action-row-button/index.js b/packages/xo-web/src/common/action-row-button/index.js new file mode 100644 index 000000000..be9cc8bb8 --- /dev/null +++ b/packages/xo-web/src/common/action-row-button/index.js @@ -0,0 +1,10 @@ +import React from 'react' + +import ActionButton from '../action-button' + +import styles from './index.css' + +const ActionRowButton = props => ( + +) +export { ActionRowButton as default } diff --git a/packages/xo-web/src/common/action-toggle.js b/packages/xo-web/src/common/action-toggle.js new file mode 100644 index 000000000..7887ab9eb --- /dev/null +++ b/packages/xo-web/src/common/action-toggle.js @@ -0,0 +1,16 @@ +import React from 'react' + +import ActionButton from './action-button' +import propTypes from './prop-types-decorator' + +const ActionToggle = ({ className, value, ...props }) => ( + +) + +export default propTypes({ + value: propTypes.bool, +})(ActionToggle) diff --git a/packages/xo-web/src/common/add-subscriptions.js b/packages/xo-web/src/common/add-subscriptions.js new file mode 100644 index 000000000..ed361ecbf --- /dev/null +++ b/packages/xo-web/src/common/add-subscriptions.js @@ -0,0 +1,29 @@ +import map from 'lodash/map' +import React from 'react' + +const call = fn => fn() + +// `subscriptions` can be a function if we want to ensure that the subscription +// callbacks have been correctly initialized when there are circular dependencies +const addSubscriptions = subscriptions => Component => + class SubscriptionWrapper extends React.PureComponent { + _unsubscribes = null + + componentWillMount () { + this._unsubscribes = map( + typeof subscriptions === 'function' ? subscriptions(this.props) : subscriptions, + (subscribe, prop) => + subscribe(value => this.setState({ [prop]: value })) + ) + } + + componentWillUnmount () { + this._unsubscribes.forEach(call) + this._unsubscribes = null + } + + render () { + return + } + } +export { addSubscriptions as default } diff --git a/packages/xo-web/src/common/base-component.js b/packages/xo-web/src/common/base-component.js new file mode 100644 index 000000000..9ee83aea9 --- /dev/null +++ b/packages/xo-web/src/common/base-component.js @@ -0,0 +1,119 @@ +import { PureComponent } from 'react' +import { cowSet } from 'utils' +import { includes, isArray, forEach, map } from 'lodash' + +import getEventValue from './get-event-value' + +// Should components logs every renders? +// +// Usually set to process.env.NODE_ENV !== 'production'. +const VERBOSE = false + +const get = (object, path, depth) => { + if (object == null || depth >= path.length) { + return object + } + + const prop = path[depth++] + return isArray(object) && prop === '*' + ? map(object, value => get(value, path, depth)) + : get(object[prop], path, depth) +} + +export default class BaseComponent extends PureComponent { + constructor (props, context) { + super(props, context) + + // It really should have been done in React.Component! + this.state = {} + + this._linkedState = null + + if (VERBOSE) { + this.render = (render => () => { + console.log('render', this.constructor.name) + + return render.call(this) + })(this.render) + } + } + + // See https://preactjs.com/guide/linked-state + linkState (name, targetPath) { + const key = targetPath !== undefined ? `${name}##${targetPath}` : name + + let linkedState = this._linkedState + let cb + if (linkedState === null) { + linkedState = this._linkedState = {} + } else if ((cb = linkedState[key]) !== undefined) { + return cb + } + + let getValue + if (targetPath !== undefined) { + const path = targetPath.split('.') + getValue = event => get(getEventValue(event), path, 0) + } else { + getValue = getEventValue + } + + if (includes(name, '.')) { + const path = name.split('.') + return (linkedState[key] = event => { + this.setState(cowSet(this.state, path, getValue(event), 0)) + }) + } + + return (linkedState[key] = event => { + this.setState({ + [name]: getValue(event), + }) + }) + } + + toggleState (name) { + let linkedState = this._linkedState + let cb + if (linkedState === null) { + linkedState = this._linkedState = {} + } else if ((cb = linkedState[name]) !== undefined) { + return cb + } + + if (includes(name, '.')) { + const path = name.split('.') + return (linkedState[path] = event => { + this.setState(cowSet(this.state, path, !get(this.state, path, 0), 0)) + }) + } + + return (linkedState[name] = () => { + this.setState({ + [name]: !this.state[name], + }) + }) + } +} + +if (VERBOSE) { + const diff = (name, old, cur) => { + const keys = [] + + forEach(old, (value, key) => { + if (cur[key] !== value) { + keys.push(key) + } + }) + + if (keys.length) { + console.log(name, keys.sort().join()) + } + } + + BaseComponent.prototype.componentDidUpdate = function (oldProps, oldState) { + const prefix = `${this.constructor.name} updated because of its` + diff(`${prefix} props:`, oldProps, this.props) + diff(`${prefix} state:`, oldState, this.state) + } +} diff --git a/packages/xo-web/src/common/browser-notification.js b/packages/xo-web/src/common/browser-notification.js new file mode 100644 index 000000000..91d20ed39 --- /dev/null +++ b/packages/xo-web/src/common/browser-notification.js @@ -0,0 +1,35 @@ +import { noop } from 'utils' +import Notify from 'notifyjs' + +let notify +export { notify as default } + +const sendNotification = (title, body) => { + new Notify(title, { + body, + timeout: 5, + icon: 'assets/logo.png', + }).show() +} + +const requestPermission = (...args) => { + if (Notify.isSupported()) { + Notify.requestPermission( + () => { + console.log('notifications allowed') + + return (notify = sendNotification)(...args) + }, + () => { + console.log('notifications denied') + + notify = noop + } + ) + } else { + notify = noop + console.warn('notifications are not supported') + } +} + +notify = Notify.needsPermission ? requestPermission : sendNotification diff --git a/packages/xo-web/src/common/button-group.js b/packages/xo-web/src/common/button-group.js new file mode 100644 index 000000000..44a7cf0ee --- /dev/null +++ b/packages/xo-web/src/common/button-group.js @@ -0,0 +1,9 @@ +import React from 'react' + +const ButtonGroup = ({ children }) => ( +
+ {children} +
+) + +export { ButtonGroup as default } diff --git a/packages/xo-web/src/common/button-link.js b/packages/xo-web/src/common/button-link.js new file mode 100644 index 000000000..42ee0becf --- /dev/null +++ b/packages/xo-web/src/common/button-link.js @@ -0,0 +1,28 @@ +import React from 'react' +import { routerShape } from 'react-router/lib/PropTypes' + +import Button from './button' +import propTypes from './prop-types-decorator' + +const ButtonLink = ({ to, ...props }, { router }) => { + props.onClick = () => { + router.push(to) + } + + return +} + +propTypes({ + active: propTypes.bool, + block: propTypes.bool, + + // Bootstrap button style + // + // See https://v4-alpha.getbootstrap.com/components/buttons/#examples + // + // The default value (secondary) is not listed here because it does + // not make sense to explicit it. + btnStyle: propTypes.oneOf([ + 'danger', + 'info', + 'link', + 'primary', + 'success', + 'warning', + ]), + + outline: propTypes.bool, + size: propTypes.oneOf(['large', 'small']), +})(Button) + +export { Button as default } diff --git a/packages/xo-web/src/common/card.js b/packages/xo-web/src/common/card.js new file mode 100644 index 000000000..36bb43650 --- /dev/null +++ b/packages/xo-web/src/common/card.js @@ -0,0 +1,40 @@ +import React from 'react' + +import propTypes from './prop-types-decorator' + +const CARD_STYLE = { + minHeight: '100%', +} + +const CARD_STYLE_WITH_SHADOW = { + ...CARD_STYLE, + boxShadow: '0 10px 6px -6px #777', // https://css-tricks.com/almanac/properties/b/box-shadow/ +} + +const CARD_HEADER_STYLE = { + minHeight: '100%', + textAlign: 'center', +} + +export const Card = propTypes({ + shadow: propTypes.bool, +})(({ shadow, ...props }) => { + props.className = 'card' + props.style = shadow ? CARD_STYLE_WITH_SHADOW : CARD_STYLE + + return
+}) + +export const CardHeader = propTypes({ + className: propTypes.string, +})(({ children, className }) => ( +

+ {children} +

+)) + +export const CardBlock = propTypes({ + className: propTypes.string, +})(({ children, className }) => ( +
{children}
+)) diff --git a/packages/xo-web/src/common/center-panel/index.css b/packages/xo-web/src/common/center-panel/index.css new file mode 100644 index 000000000..f253732a1 --- /dev/null +++ b/packages/xo-web/src/common/center-panel/index.css @@ -0,0 +1,9 @@ +.container { + display: flex; + min-height: 100%; +} + +.content { + margin: auto; + text-align: center; +} diff --git a/packages/xo-web/src/common/center-panel/index.js b/packages/xo-web/src/common/center-panel/index.js new file mode 100644 index 000000000..eb5e15bd1 --- /dev/null +++ b/packages/xo-web/src/common/center-panel/index.js @@ -0,0 +1,11 @@ +import React from 'react' + +import styles from './index.css' + +const CenterPanel = ({ children }) => ( +
+
{children}
+
+) + +export { CenterPanel as default } diff --git a/packages/xo-web/src/common/collapse.js b/packages/xo-web/src/common/collapse.js new file mode 100644 index 000000000..d2819b542 --- /dev/null +++ b/packages/xo-web/src/common/collapse.js @@ -0,0 +1,39 @@ +import React from 'react' + +import Button from './button' +import Component from './base-component' +import Icon from './icon' +import propTypes from './prop-types-decorator' + +@propTypes({ + children: propTypes.any.isRequired, + className: propTypes.string, + buttonText: propTypes.any.isRequired, + defaultOpen: propTypes.bool, +}) +export default class Collapse extends Component { + state = { + isOpened: this.props.defaultOpen, + } + + _onClick = () => { + this.setState({ + isOpened: !this.state.isOpened, + }) + } + + render () { + const { props } = this + const { isOpened } = this.state + + return ( +
+ + {isOpened && props.children} +
+ ) + } +} diff --git a/packages/xo-web/src/common/combobox.js b/packages/xo-web/src/common/combobox.js new file mode 100644 index 000000000..46d98c061 --- /dev/null +++ b/packages/xo-web/src/common/combobox.js @@ -0,0 +1,61 @@ +import React from 'react' +import uncontrollableInput from 'uncontrollable-input' +import { isEmpty, map } from 'lodash' +import { DropdownButton, MenuItem } from 'react-bootstrap-4/lib' + +import Component from './base-component' +import propTypes from './prop-types-decorator' + +@uncontrollableInput({ + defaultValue: '', +}) +@propTypes({ + disabled: propTypes.bool, + options: propTypes.oneOfType([ + propTypes.arrayOf(propTypes.string), + propTypes.objectOf(propTypes.string), + ]), + onChange: propTypes.func.isRequired, + value: propTypes.string.isRequired, +}) +export default class Combobox extends Component { + _handleChange = event => { + this.props.onChange(event.target.value) + } + + _setText (value) { + this.props.onChange(value) + } + + render () { + const { options, ...props } = this.props + + props.className = 'form-control' + props.onChange = this._handleChange + const Input = + + if (isEmpty(options)) { + return Input + } + + return ( +
+
+ + {map(options, option => ( + this._setText(option)}> + {option} + + ))} + +
+ {Input} +
+ ) + } +} diff --git a/packages/xo-web/src/common/copiable/index.css b/packages/xo-web/src/common/copiable/index.css new file mode 100644 index 000000000..5345f4fa3 --- /dev/null +++ b/packages/xo-web/src/common/copiable/index.css @@ -0,0 +1,9 @@ +.container .button { + position: absolute; + margin-left: 1ex; + visibility: hidden; +} + +.container:hover .button { + visibility: visible; +} diff --git a/packages/xo-web/src/common/copiable/index.js b/packages/xo-web/src/common/copiable/index.js new file mode 100644 index 000000000..274732f44 --- /dev/null +++ b/packages/xo-web/src/common/copiable/index.js @@ -0,0 +1,34 @@ +import CopyToClipboard from 'react-copy-to-clipboard' +import classNames from 'classnames' +import React, { createElement } from 'react' + +import _ from '../intl' +import Button from '../button' +import Icon from '../icon' +import propTypes from '../prop-types-decorator' +import Tooltip from '../tooltip' + +import styles from './index.css' + +const Copiable = propTypes({ + data: propTypes.string, + tagName: propTypes.string, +})(({ className, tagName = 'span', ...props }) => + createElement( + tagName, + { + ...props, + className: classNames(styles.container, className), + }, + props.children, + ' ', + + + + + + ) +) +export { Copiable as default } diff --git a/packages/xo-web/src/common/d3-utils.js b/packages/xo-web/src/common/d3-utils.js new file mode 100644 index 000000000..68b475611 --- /dev/null +++ b/packages/xo-web/src/common/d3-utils.js @@ -0,0 +1,9 @@ +import forEach from 'lodash/forEach' + +export function setStyles (style) { + forEach(style, (value, key) => { + this.style(key, value) + }) + + return this +} diff --git a/packages/xo-web/src/common/debug.js b/packages/xo-web/src/common/debug.js new file mode 100644 index 000000000..58800131d --- /dev/null +++ b/packages/xo-web/src/common/debug.js @@ -0,0 +1,60 @@ +import PropTypes from 'prop-types' +import React, { Component } from 'react' +import { isPromise } from 'promise-toolbox' + +const toString = value => + value === undefined ? 'undefined' : JSON.stringify(value, null, 2) + +// This component does not handle changes in its `promise` property. +class DebugAsync extends Component { + static propTypes = { + promise: PropTypes.object.isRequired, + } + + constructor (props) { + super() + + this.state = { + status: 'pending', + } + + props.promise.then( + value => this.setState({ status: 'resolved', value }), + value => this.setState({ status: 'rejected', value }) + ) + } + + shouldComponentUpdate (_, newState) { + return this.state.status !== newState.status + } + + render () { + const { status, value } = this.state + + if (status === 'pending') { + return
{'Promise {  }'}
+ } + + return ( +
+        {'Promise { '}
+        {status === 'rejected' && ' '}
+        {toString(value)}
+        {' }'}
+      
+ ) + } +} + +const Debug = ({ value }) => + isPromise(value) ? ( + + ) : ( +
{toString(value)}
+ ) + +Debug.propTypes = { + value: PropTypes.any.isRequired, +} + +export { Debug as default } diff --git a/packages/xo-web/src/common/dropzone/index.css b/packages/xo-web/src/common/dropzone/index.css new file mode 100644 index 000000000..7b195906a --- /dev/null +++ b/packages/xo-web/src/common/dropzone/index.css @@ -0,0 +1,22 @@ +@value dropzoneColor: #8f8686; + +.dropzone { + border-radius: 4px; + border: 2px dashed dropzoneColor; + cursor: pointer; + display: flex; + height: 12em; + margin-bottom: 20px; + width: 100%; +} + +.activeDropzone { + background: #f0f0f0; + border-style: solid; +} + +.dropzoneText { + color: dropzoneColor; + font-size: 1.2em; + margin: auto; +} diff --git a/packages/xo-web/src/common/dropzone/index.js b/packages/xo-web/src/common/dropzone/index.js new file mode 100644 index 000000000..0e18e7529 --- /dev/null +++ b/packages/xo-web/src/common/dropzone/index.js @@ -0,0 +1,26 @@ +import Component from 'base-component' +import propTypes from 'prop-types-decorator' +import React from 'react' +import ReactDropzone from 'react-dropzone' + +import styles from './index.css' + +@propTypes({ + onDrop: propTypes.func, + message: propTypes.node, +}) +export default class Dropzone extends Component { + render () { + const { onDrop, message } = this.props + + return ( + +
{message}
+
+ ) + } +} diff --git a/packages/xo-web/src/common/editable/index.css b/packages/xo-web/src/common/editable/index.css new file mode 100644 index 000000000..eaaa67e15 --- /dev/null +++ b/packages/xo-web/src/common/editable/index.css @@ -0,0 +1,13 @@ +.clickToEdit * { + cursor: context-menu !important; +} +.shortClick { + border-bottom: 1px dashed #ccc; +} + +.select { + padding: 0px; +} +.size { + width: 10rem; +} diff --git a/packages/xo-web/src/common/editable/index.js b/packages/xo-web/src/common/editable/index.js new file mode 100644 index 000000000..183e06128 --- /dev/null +++ b/packages/xo-web/src/common/editable/index.js @@ -0,0 +1,497 @@ +import classNames from 'classnames' +import findKey from 'lodash/findKey' +import isFunction from 'lodash/isFunction' +import isString from 'lodash/isString' +import map from 'lodash/map' +import pick from 'lodash/pick' +import React from 'react' + +import _ from '../intl' +import Component from '../base-component' +import getEventValue from '../get-event-value' +import Icon from '../icon' +import logError from '../log-error' +import propTypes from '../prop-types-decorator' +import Tooltip from '../tooltip' +import { formatSize } from '../utils' +import { SizeInput } from '../form' +import { + SelectHost, + SelectIp, + SelectNetwork, + SelectPool, + SelectRemote, + SelectResourceSetIp, + SelectSr, + SelectSubject, + SelectTag, + SelectVgpuType, + SelectVm, + SelectVmTemplate, +} from '../select-objects' + +import styles from './index.css' + +const LONG_CLICK = 400 + +@propTypes({ + alt: propTypes.node.isRequired, +}) +class Hover extends Component { + constructor () { + super() + + this.state = { + hover: false, + } + + this._onMouseEnter = () => this.setState({ hover: true }) + this._onMouseLeave = () => this.setState({ hover: false }) + } + + render () { + if (this.state.hover) { + return {this.props.alt} + } + + return {this.props.children} + } +} + +@propTypes({ + onChange: propTypes.func.isRequired, + onUndo: propTypes.oneOfType([propTypes.bool, propTypes.func]), + useLongClick: propTypes.bool, + value: propTypes.any.isRequired, +}) +class Editable extends Component { + get value () { + throw new Error('not implemented') + } + + _onKeyDown = event => { + const { keyCode } = event + if (keyCode === 27) { + return this._closeEdition() + } + + if (keyCode === 13) { + return this._save() + } + } + + _closeEdition = () => { + this.setState({ editing: false }) + } + + _openEdition = () => { + this.setState({ + editing: true, + error: null, + saving: false, + }) + } + + _undo = () => { + const { props } = this + const { onUndo } = props + if (onUndo === false) { + return + } + + return this.__save( + () => this.state.previous, + isFunction(onUndo) ? onUndo : props.onChange + ) + } + + _save () { + return this.__save(() => this.value, this.props.onChange) + } + + async __save (getValue, saveValue) { + const { props } = this + + try { + const value = getValue() + const previous = props.value + if (value === previous) { + return this._closeEdition() + } + + this.setState({ saving: true }) + + await saveValue(value) + + this.setState({ previous }) + this._closeEdition() + } catch (error) { + this.setState({ + // `error` may be undefined if the action has been cancelled + error: error !== undefined && (isString(error) ? error : error.message), + saving: false, + }) + logError(error) + } + } + + __startTimer = event => { + event.persist() + this._timeout = setTimeout(() => { + event.preventDefault() + this._openEdition() + }, LONG_CLICK) + } + __stopTimer = () => clearTimeout(this._timeout) + + render () { + const { state, props } = this + + if (!state.editing) { + const { onUndo, previous } = state + const { useLongClick } = props + + const success = + return ( + + + {this._renderDisplay()} + + {previous != null && + (onUndo !== false ? ( + + + + } + > + {success} + + ) : ( + success + ))} + + ) + } + + const { error, saving } = state + + return ( + + {this._renderEdition()} + {saving && ( + + {' '} + + + )} + {error != null && ( + + {' '} + + + + + )} + + ) + } +} + +@propTypes({ + autoComplete: propTypes.string, + maxLength: propTypes.number, + minLength: propTypes.number, + pattern: propTypes.string, + value: propTypes.string.isRequired, +}) +export class Text extends Editable { + get value () { + const { input } = this.refs + + // FIXME: should be properly forwarded to the user. + const error = input.validationMessage + if (error) { + throw new Error(error) + } + + return input.value + } + + _onInput = ({ target }) => { + target.style.width = `${target.value.length + 1}ex` + } + + _renderDisplay () { + const { children, value } = this.props + + if (children || value) { + return {children || value} + } + + const { placeholder, useLongClick } = this.props + + return ( + + {placeholder || + (useLongClick + ? _('editableLongClickPlaceholder') + : _('editableClickPlaceholder'))} + + ) + } + + _renderEdition () { + const { value } = this.props + const { saving } = this.state + + // Optional props that the user may set on the input. + const extraProps = pick(this.props, [ + 'autoComplete', + 'maxLength', + 'minLength', + 'pattern', + ]) + + return ( + + ) + } +} + +export class Password extends Text { + // TODO: this is a hack, this class should probably have a better + // implementation. + _isPassword = true +} + +@propTypes({ + nullable: propTypes.bool, + value: propTypes.number, +}) +export class Number extends Component { + get value () { + return +this.refs.input.value + } + + _onChange = value => { + if (value === '') { + if (this.props.nullable) { + value = null + } else { + return + } + } else { + value = +value + } + + this.props.onChange(value) + } + + render () { + const { value } = this.props + return ( + + ) + } +} + +@propTypes({ + options: propTypes.oneOfType([propTypes.array, propTypes.object]).isRequired, + renderer: propTypes.func, +}) +export class Select extends Editable { + componentWillReceiveProps (props) { + if ( + props.value !== this.props.value || + props.options !== this.props.options + ) { + this.setState({ + valueKey: findKey(props.options, option => option === props.value), + }) + } + } + + get value () { + return this.props.options[this.state.valueKey] + } + + _onChange = event => { + this.setState({ valueKey: getEventValue(event) }, this._save) + } + + _optionToJsx = (option, key) => { + const { renderer } = this.props + + return ( + + ) + } + + _onEditionMount = ref => { + // Seems to work in Google Chrome (not in Firefox) + ref && ref.dispatchEvent(new window.MouseEvent('mousedown')) + } + + _renderDisplay () { + const { children, renderer, value } = this.props + + return children || {renderer ? renderer(value) : value} + } + + _renderEdition () { + const { saving, valueKey } = this.state + const { options } = this.props + + return ( + + ) + } +} + +const MAP_TYPE_SELECT = { + host: SelectHost, + ip: SelectIp, + network: SelectNetwork, + pool: SelectPool, + remote: SelectRemote, + resourceSetIp: SelectResourceSetIp, + SR: SelectSr, + subject: SelectSubject, + tag: SelectTag, + vgpuType: SelectVgpuType, + VM: SelectVm, + 'VM-template': SelectVmTemplate, +} + +@propTypes({ + value: propTypes.oneOfType([propTypes.string, propTypes.object]), +}) +export class XoSelect extends Editable { + get value () { + return this.state.value + } + + _renderDisplay () { + return ( + this.props.children || ( + {this.props.value[this.props.labelProp]} + ) + ) + } + + _onChange = object => this.setState({ value: object }, object && this._save) + + _renderEdition () { + const { saving, xoType, ...props } = this.props + + const Select = MAP_TYPE_SELECT[xoType] + if (process.env.NODE_ENV !== 'production') { + if (!Select) { + throw new Error(`${xoType} is not a valid XoSelect type.`) + } + } + + // Anchor is needed so that the BlockLink does not trigger a redirection + // when this element is clicked. + return ( + + + + + +
+ ) + } +} + +// =================================================================== + +@propTypes({ + max: propTypes.number.isRequired, + min: propTypes.number.isRequired, + onChange: propTypes.func, + step: propTypes.number, + value: propTypes.number, +}) +export class Range extends Component { + componentDidMount () { + const { min, onChange, value } = this.props + + if (!value) { + onChange && onChange(min) + } + } + + _onChange = value => this.props.onChange(getEventValue(value)) + + render () { + const { max, min, step, value } = this.props + + return ( + + + + {value} + + + + + + + ) + } +} + +export Toggle from './toggle' + +const UNITS = ['kiB', 'MiB', 'GiB'] +const DEFAULT_UNIT = 'GiB' +@propTypes({ + autoFocus: propTypes.bool, + className: propTypes.string, + defaultUnit: propTypes.oneOf(UNITS), + defaultValue: propTypes.number, + onChange: propTypes.func, + placeholder: propTypes.string, + readOnly: propTypes.bool, + required: propTypes.bool, + style: propTypes.object, + value: propTypes.oneOfType([propTypes.number, propTypes.oneOf([null])]), +}) +export class SizeInput extends BaseComponent { + constructor (props) { + super(props) + + this.state = this._createStateFromBytes( + defined(props.value, props.defaultValue, null) + ) + } + + componentWillReceiveProps (props) { + const { value } = props + if (value !== undefined && value !== this.props.value) { + this.setState(this._createStateFromBytes(value)) + } + } + + _createStateFromBytes (bytes) { + if (bytes === this._bytes) { + return { + input: this._input, + unit: this._unit, + } + } + + if (bytes === null) { + return { + input: '', + unit: this.props.defaultUnit || DEFAULT_UNIT, + } + } + + const { prefix, value } = formatSizeRaw(bytes) + return { + input: String(round(value, 2)), + unit: `${prefix}B`, + } + } + + get value () { + const { input, unit } = this.state + + if (!input) { + return null + } + + return parseSize(`${+input} ${unit}`) + } + + set value (value) { + if ( + process.env.NODE_ENV !== 'production' && + this.props.value !== undefined + ) { + throw new Error('cannot set value of controlled SizeInput') + } + this.setState(this._createStateFromBytes(value)) + } + + _onChange (input, unit) { + const { onChange } = this.props + + // Empty input equals null. + const bytes = input ? parseSize(`${+input} ${unit}`) : null + + const isControlled = this.props.value !== undefined + if (isControlled) { + // Store input and unit for this change to update correctly on new + // props. + this._bytes = bytes + this._input = input + this._unit = unit + } else { + this.setState({ input, unit }) + + // onChange is optional in uncontrolled mode. + if (!onChange) { + return + } + } + + onChange(bytes) + } + + _updateNumber = event => { + const input = event.target.value + + if (!input) { + return this._onChange(input, this.state.unit) + } + + const number = +input + + if (Number.isNaN(number)) { + return + } + + // Same numeric value: simply update the input. + const prevInput = this.state.input + if (prevInput && +prevInput === number) { + return this.setState({ input }) + } + + this._onChange(input, this.state.unit) + } + + _updateUnit = unit => { + const { input } = this.state + + // 0 is always 0, no matter the unit. + if (+input) { + this._onChange(input, unit) + } else { + this.setState({ unit }) + } + } + + render () { + const { + autoFocus, + className, + readOnly, + placeholder, + required, + style, + } = this.props + + return ( + + + + + {map(UNITS, unit => ( + this._updateUnit(unit)}> + {unit} + + ))} + + + + ) + } +} diff --git a/packages/xo-web/src/common/form/select.js b/packages/xo-web/src/common/form/select.js new file mode 100644 index 000000000..6f1fefdce --- /dev/null +++ b/packages/xo-web/src/common/form/select.js @@ -0,0 +1,177 @@ +import classNames from 'classnames' +import isEmpty from 'lodash/isEmpty' +import PropTypes from 'prop-types' +import React from 'react' +import ReactSelect from 'react-select' +import uncontrollableInput from 'uncontrollable-input' +import { + AutoSizer, + CellMeasurer, + CellMeasurerCache, + List, +} from 'react-virtualized' + +const SELECT_STYLE = { + minWidth: '10em', +} +const MENU_STYLE = { + overflow: 'hidden', +} + +@uncontrollableInput() +export default class Select extends React.PureComponent { + static defaultProps = { + maxHeight: 200, + + multi: ReactSelect.defaultProps.multi, + options: [], + required: ReactSelect.defaultProps.required, + simpleValue: ReactSelect.defaultProps.simpleValue, + valueKey: ReactSelect.defaultProps.valueKey, + } + + static propTypes = { + autoSelectSingleOption: PropTypes.bool, // default to props.required + maxHeight: PropTypes.number, + options: PropTypes.array.isRequired, // cannot be an object + } + + _cellMeasurerCache = new CellMeasurerCache({ + fixedWidth: true, + }) + + // https://github.com/JedWatson/react-select/blob/dd32c27d7ea338a93159da5e40bc06697d0d86f9/src/utils/defaultMenuRenderer.js#L4 + _renderMenu (opts) { + const { focusOption, options, selectValue } = opts + + const focusFromEvent = event => + focusOption(options[event.currentTarget.dataset.index]) + const selectFromEvent = event => + selectValue(options[event.currentTarget.dataset.index]) + const renderRow = opts2 => + this._renderRow(opts, opts2, focusFromEvent, selectFromEvent) + + let focusedOptionIndex = options.indexOf(opts.focusedOption) + if (focusedOptionIndex === -1) { + focusedOptionIndex = undefined + } + + const { length } = options + const { maxHeight } = this.props + const { rowHeight } = this._cellMeasurerCache + + let height = 0 + for (let i = 0; i < length; ++i) { + height += rowHeight({ index: i }) + if (height > maxHeight) { + height = maxHeight + break + } + } + + return ( + + {({ width }) => ( + + )} + + ) + } + _renderMenu = this._renderMenu.bind(this) + + _renderRow ( + { + focusedOption, + focusOption, + inputValue, + optionClassName, + optionRenderer, + options, + selectValue, + }, + { index, key, parent, style }, + focusFromEvent, + selectFromEvent + ) { + const option = options[index] + const { disabled } = option + + return ( + +
+ {optionRenderer(option, index, inputValue)} +
+
+ ) + } + + componentDidMount () { + this.componentDidUpdate() + } + + componentDidUpdate () { + const { props } = this + const { + autoSelectSingleOption = props.required, + multi, + options, + simpleValue, + value, + } = props + if ( + autoSelectSingleOption && + options != null && + options.length === 1 && + (value == null || + (simpleValue && value === '') || + (multi && value.length === 0)) + ) { + const option = options[0] + props.onChange( + simpleValue ? option[props.valueKey] : multi ? [option] : option + ) + } + } + + render () { + const { props } = this + const { multi } = props + return ( + + ) + } +} diff --git a/packages/xo-web/src/common/form/toggle.js b/packages/xo-web/src/common/form/toggle.js new file mode 100644 index 000000000..f02331482 --- /dev/null +++ b/packages/xo-web/src/common/form/toggle.js @@ -0,0 +1,46 @@ +import React from 'react' +import classNames from 'classnames' +import uncontrollableInput from 'uncontrollable-input' + +import Component from '../base-component' +import Icon from '../icon' +import propTypes from '../prop-types-decorator' + +@uncontrollableInput() +@propTypes({ + className: propTypes.string, + onChange: propTypes.func.isRequired, + icon: propTypes.string, + iconOn: propTypes.string, + iconOff: propTypes.string, + iconSize: propTypes.number, + value: propTypes.bool.isRequired, +}) +export default class Toggle extends Component { + static defaultProps = { + iconOn: 'toggle-on', + iconOff: 'toggle-off', + iconSize: 2, + } + + _toggle = () => { + const { props } = this + props.onChange(!props.value) + } + + render () { + const { props } = this + + return ( + + ) + } +} diff --git a/packages/xo-web/src/common/get-event-value.js b/packages/xo-web/src/common/get-event-value.js new file mode 100644 index 000000000..e1b49355a --- /dev/null +++ b/packages/xo-web/src/common/get-event-value.js @@ -0,0 +1,15 @@ +// If the param is an event, returns the value of it's target, +// otherwise returns the param. +const getEventValue = event => { + let target + if (!event || !(target = event.target)) { + return event + } + + return target.nodeName.toLowerCase() === 'input' && + target.type.toLowerCase() === 'checkbox' + ? target.checked + : target.value +} + +export { getEventValue as default } diff --git a/packages/xo-web/src/common/grid.js b/packages/xo-web/src/common/grid.js new file mode 100644 index 000000000..f799acecf --- /dev/null +++ b/packages/xo-web/src/common/grid.js @@ -0,0 +1,61 @@ +import classNames from 'classnames' +import React from 'react' + +import propTypes from './prop-types-decorator' + +// A column can contain content or a row. +export const Col = propTypes({ + className: propTypes.string, + size: propTypes.number, + smallSize: propTypes.number, + mediumSize: propTypes.number, + largeSize: propTypes.number, + offset: propTypes.number, + smallOffset: propTypes.number, + mediumOffset: propTypes.number, + largeOffset: propTypes.number, +})( + ({ + children, + className, + size = 12, + smallSize = size, + mediumSize, + largeSize, + offset, + smallOffset = offset, + mediumOffset, + largeOffset, + style, + }) => ( +
+ {children} +
+ ) +) + +// This is the root component of the grid layout, containers should not be +// nested. +export const Container = propTypes({ + className: propTypes.string, +})(({ children, className }) => ( +
{children}
+)) + +// Only columns can be children of a row. +export const Row = propTypes({ + className: propTypes.string, +})(({ children, className }) => ( +
{children}
+)) diff --git a/packages/xo-web/src/common/grid.spec.js b/packages/xo-web/src/common/grid.spec.js new file mode 100644 index 000000000..756d55a7f --- /dev/null +++ b/packages/xo-web/src/common/grid.spec.js @@ -0,0 +1,13 @@ +/* eslint-env jest */ + +import React from 'react' +import { forEach } from 'lodash' +import { shallow } from 'enzyme' + +import * as grid from './grid' + +forEach(grid, (Component, name) => { + it(name, () => { + expect(shallow()).toMatchSnapshot() + }) +}) diff --git a/packages/xo-web/src/common/home-filters.js b/packages/xo-web/src/common/home-filters.js new file mode 100644 index 000000000..8b5a8fbf0 --- /dev/null +++ b/packages/xo-web/src/common/home-filters.js @@ -0,0 +1,33 @@ +const common = { + homeFilterNone: '', +} + +export const VM = { + ...common, + homeFilterPendingVms: 'current_operations:"" ', + homeFilterNonRunningVms: '!power_state:running ', + homeFilterHvmGuests: 'virtualizationMode:hvm ', + homeFilterRunningVms: 'power_state:running ', + homeFilterTags: 'tags:', +} + +export const host = { + ...common, + homeFilterRunningHosts: 'power_state:running ', + homeFilterTags: 'tags:', +} + +export const pool = { + ...common, + homeFilterTags: 'tags:', +} + +export const vmTemplate = { + ...common, + homeFilterTags: 'tags:', +} + +export const SR = { + ...common, + homeFilterTags: 'tags:', +} diff --git a/packages/xo-web/src/common/home-tags.js b/packages/xo-web/src/common/home-tags.js new file mode 100644 index 000000000..3048b7c04 --- /dev/null +++ b/packages/xo-web/src/common/home-tags.js @@ -0,0 +1,40 @@ +import * as CM from 'complex-matcher' +import React from 'react' + +import Component from './base-component' +import propTypes from './prop-types-decorator' +import Tags from './tags' + +@propTypes({ + labels: propTypes.arrayOf(React.PropTypes.string).isRequired, + onAdd: propTypes.func, + onChange: propTypes.func, + onDelete: propTypes.func, + type: propTypes.string, +}) +export default class HomeTags extends Component { + static contextTypes = { + router: React.PropTypes.object, + } + + _onClick = label => { + const s = encodeURIComponent( + new CM.Property('tags', new CM.String(label)).toString() + ) + const t = encodeURIComponent(this.props.type) + + this.context.router.push(`/home?t=${t}&s=${s}`) + } + + render () { + return ( + + ) + } +} diff --git a/packages/xo-web/src/common/hosts-patches-table.js b/packages/xo-web/src/common/hosts-patches-table.js new file mode 100644 index 000000000..5bf75b231 --- /dev/null +++ b/packages/xo-web/src/common/hosts-patches-table.js @@ -0,0 +1,232 @@ +import React from 'react' +import { Portal } from 'react-overlays' +import { forEach, isEmpty, keys, map, noop } from 'lodash' + +import _ from './intl' +import ActionButton from './action-button' +import Component from './base-component' +import Link from './link' +import propTypes from './prop-types-decorator' +import SortedTable from './sorted-table' +import TabButton from './tab-button' +import { connectStore } from './utils' +import { + createGetObjectsOfType, + createFilter, + createSelector, +} from './selectors' +import { + installAllHostPatches, + installAllPatchesOnPool, + subscribeHostMissingPatches, +} from './xo' + +// =================================================================== + +const MISSING_PATCHES_COLUMNS = [ + { + name: _('srHost'), + itemRenderer: host => ( + {host.name_label} + ), + sortCriteria: host => host.name_label, + }, + { + name: _('hostDescription'), + itemRenderer: host => host.name_description, + sortCriteria: host => host.name_description, + }, + { + name: _('hostMissingPatches'), + itemRenderer: (host, { missingPatches }) => ( + {missingPatches[host.id]} + ), + sortCriteria: (host, { missingPatches }) => missingPatches[host.id], + }, + { + name: _('patchUpdateButton'), + itemRenderer: (host, { installAllHostPatches }) => ( + + ), + }, +] + +const POOLS_MISSING_PATCHES_COLUMNS = [ + { + name: _('srPool'), + itemRenderer: (host, { pools }) => { + const pool = pools[host.$pool] + return {pool.name_label} + }, + sortCriteria: (host, { pools }) => pools[host.$pool].name_label, + }, +].concat(MISSING_PATCHES_COLUMNS) + +// Small component to homogenize Button usage in HostsPatchesTable +const ActionButton_ = ({ children, labelId, ...props }) => ( + + {children} + +) + +// =================================================================== + +@connectStore({ + hostsById: createGetObjectsOfType('host').groupBy('id'), +}) +class HostsPatchesTable extends Component { + constructor (props) { + super(props) + this.state.missingPatches = {} + } + + _getHosts = createFilter( + () => this.props.hosts, + createSelector( + () => this.state.missingPatches, + missingPatches => host => missingPatches[host.id] + ) + ) + + _subscribeMissingPatches = (hosts = this.props.hosts) => { + const { hostsById } = this.props + + const unsubs = map( + hosts, + host => + hostsById + ? subscribeHostMissingPatches(hostsById[host.id][0], patches => + this.setState({ + missingPatches: { + ...this.state.missingPatches, + [host.id]: patches.length, + }, + }) + ) + : noop + ) + + if (this.unsubscribeMissingPatches !== undefined) { + this.unsubscribeMissingPatches() + } + + this.unsubscribeMissingPatches = () => forEach(unsubs, unsub => unsub()) + } + + _installAllMissingPatches = () => { + const pools = {} + forEach(this._getHosts(), host => { + pools[host.$pool] = true + }) + + return Promise.all(map(keys(pools), installAllPatchesOnPool)) + } + + componentDidMount () { + // Force one Portal refresh. + // Because Portal cannot see the container reference at first rendering. + this.forceUpdate() + this._subscribeMissingPatches() + } + + componentWillReceiveProps (nextProps) { + if (nextProps.hosts !== this.props.hosts) { + this._subscribeMissingPatches(nextProps.hosts) + } + } + + componentWillUnmount () { + this.unsubscribeMissingPatches() + } + + render () { + const { + buttonsGroupContainer, + container, + displayPools, + pools, + useTabButton, + } = this.props + + const hosts = this._getHosts() + const noPatches = isEmpty(hosts) + + const Container = container || 'div' + + const Button = useTabButton ? TabButton : ActionButton_ + + return ( +
+ {!noPatches ? ( + + ) : ( +

{_('patchNothing')}

+ )} + buttonsGroupContainer()}> + +
+ ) + } +} + +// =================================================================== + +@connectStore(() => { + const getPools = createGetObjectsOfType('pool') + + return { + pools: getPools, + } +}) +class HostsPatchesTableByPool extends Component { + render () { + const { props } = this + return + } +} + +// =================================================================== + +export default propTypes({ + buttonsGroupContainer: propTypes.func.isRequired, + container: propTypes.any, + displayPools: propTypes.bool, + hosts: propTypes.oneOfType([ + propTypes.arrayOf(propTypes.object), + propTypes.objectOf(propTypes.object), + ]).isRequired, + useTabButton: propTypes.bool, +})( + props => + props.displayPools ? ( + + ) : ( + + ) +) diff --git a/packages/xo-web/src/common/icon.js b/packages/xo-web/src/common/icon.js new file mode 100644 index 000000000..45f6ce7ef --- /dev/null +++ b/packages/xo-web/src/common/icon.js @@ -0,0 +1,24 @@ +import classNames from 'classnames' +import isInteger from 'lodash/isInteger' +import React from 'react' + +import propTypes from './prop-types-decorator' + +const Icon = ({ icon, size = 1, color, fixedWidth, ...props }) => { + props.className = classNames( + props.className, + icon !== undefined ? `xo-icon-${icon}` : 'fa', // Without icon prop, is a placeholder. + isInteger(size) ? `fa-${size}x` : `fa-${size}`, + color, + fixedWidth && 'fa-fw' + ) + + return +} +propTypes(Icon)({ + color: propTypes.string, + fixedWidth: propTypes.bool, + icon: propTypes.string, + size: propTypes.oneOfType([propTypes.string, propTypes.number]), +}) +export default Icon diff --git a/packages/xo-web/src/common/intl/index.js b/packages/xo-web/src/common/intl/index.js new file mode 100644 index 000000000..124d18101 --- /dev/null +++ b/packages/xo-web/src/common/intl/index.js @@ -0,0 +1,111 @@ +import isFunction from 'lodash/isFunction' +import isString from 'lodash/isString' +import moment from 'moment' +import PropTypes from 'prop-types' +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { FormattedMessage, IntlProvider as IntlProvider_ } from 'react-intl' + +import locales from './locales' +import messages from './messages' +import Tooltip from '.././tooltip' +import { createSelector } from '.././selectors' + +// =================================================================== + +// Params: +// +// - props (optional): properties to add to the FormattedMessage +// - messageId: identifier of the message to format/translate +// - values (optional): values to pass to the message +// - render (optional): a function receiving the React nodes of the +// translated message and returning the React node to render +const getMessage = (props, messageId, values, render) => { + if (isString(props)) { + render = values + values = messageId + messageId = props + props = undefined + } + + const message = messages[messageId] + if (process.env.NODE_ENV !== 'production' && !message) { + throw new Error(`no message defined for ${messageId}`) + } + + if (isFunction(values)) { + render = values + values = undefined + } + + return ( + + {render} + + ) +} +getMessage.keyValue = (key, value) => + getMessage('keyValue', { + key: {key}, + value, + }) + +export { getMessage as default } + +export { messages } + +@connect(({ lang }) => ({ lang })) +export class IntlProvider extends Component { + static propTypes = { + children: PropTypes.node.isRequired, + lang: PropTypes.string.isRequired, + } + + render () { + const { lang, children } = this.props + // Adding a key prop is a work-around suggested by react-intl documentation + // to make sure changes to the locale trigger a re-render of the child components + // https://github.com/yahoo/react-intl/wiki/Components#dynamic-language-selection + // + // FIXME: remove the key prop when React context propagation is fixed (https://github.com/facebook/react/issues/2517) + return ( + + {children} + + ) + } +} + +const parseDuration = milliseconds => { + let seconds = Math.floor(milliseconds / 1e3) + const days = Math.floor(seconds / 86400) + seconds -= days * 86400 + const hours = Math.floor(seconds / 3600) + seconds -= hours * 3600 + const minutes = Math.floor(seconds / 60) + seconds -= minutes * 60 + return { days, hours, minutes, seconds } +} + +@connect(({ lang }) => ({ lang })) +export class FormattedDuration extends Component { + _parseDuration = createSelector(() => this.props.duration, parseDuration) + + _humanizeDuration = createSelector( + () => this.props.duration, + () => this.props.lang, + (duration, lang) => + moment + .duration(duration) + .locale(lang) + .humanize() + ) + + render () { + return ( + + {this._humanizeDuration()} + + ) + } +} diff --git a/packages/xo-web/src/common/intl/locales/.index-modules b/packages/xo-web/src/common/intl/locales/.index-modules new file mode 100644 index 000000000..e69de29bb diff --git a/packages/xo-web/src/common/intl/locales/es.js b/packages/xo-web/src/common/intl/locales/es.js new file mode 100644 index 000000000..c334f38d5 --- /dev/null +++ b/packages/xo-web/src/common/intl/locales/es.js @@ -0,0 +1,3838 @@ +// See http://momentjs.com/docs/#/use-it/browserify/ +import 'moment/locale/es' + +import reactIntlData from 'react-intl/locale-data/es' +import { addLocaleData } from 'react-intl' +addLocaleData(reactIntlData) + +// =================================================================== + +export default { + // Original text: 'Connecting' + statusConnecting: undefined, + + // Original text: 'Disconnected' + statusDisconnected: undefined, + + // Original text: 'Loading…' + statusLoading: undefined, + + // Original text: 'Page not found' + errorPageNotFound: undefined, + + // Original text: 'no such item' + errorNoSuchItem: undefined, + + // Original text: "Long click to edit" + editableLongClickPlaceholder: 'Click largo para editar', + + // Original text: "Click to edit" + editableClickPlaceholder: 'Click para editar', + + // Original text: 'Browse files' + browseFiles: undefined, + + // Original text: "OK" + alertOk: 'OK', + + // Original text: "OK" + confirmOk: 'OK', + + // Original text: "Cancel" + genericCancel: 'Cancelar', + + // Original text: 'On error' + onError: undefined, + + // Original text: 'Successful' + successful: undefined, + + // Original text: 'Managed disks' + filterOnlyManaged: undefined, + + // Original text: 'Orphaned disks' + filterOnlyOrphaned: undefined, + + // Original text: 'Normal disks' + filterOnlyRegular: undefined, + + // Original text: 'Snapshot disks' + filterOnlySnapshots: undefined, + + // Original text: 'Unmanaged disks' + filterOnlyUnmanaged: undefined, + + // Original text: 'Copy to clipboard' + copyToClipboard: undefined, + + // Original text: 'Master' + pillMaster: undefined, + + // Original text: "Home" + homePage: 'Inicio', + + // Original text: 'VMs' + homeVmPage: undefined, + + // Original text: 'Hosts' + homeHostPage: undefined, + + // Original text: 'Pools' + homePoolPage: undefined, + + // Original text: 'Templates' + homeTemplatePage: undefined, + + // Original text: 'Storages' + homeSrPage: undefined, + + // Original text: "Dashboard" + dashboardPage: 'Resumen', + + // Original text: "Overview" + overviewDashboardPage: 'Vista General', + + // Original text: "Visualizations" + overviewVisualizationDashboardPage: 'Visualizaciones', + + // Original text: "Statistics" + overviewStatsDashboardPage: 'Estadísticas', + + // Original text: "Health" + overviewHealthDashboardPage: 'Estado', + + // Original text: "Self service" + selfServicePage: 'Auto servicio', + + // Original text: "Backup" + backupPage: 'Backup', + + // Original text: "Jobs" + jobsPage: 'Trabajos', + + // Original text: "Updates" + updatePage: 'Actualizaciones', + + // Original text: "Settings" + settingsPage: 'Configuración', + + // Original text: "Servers" + settingsServersPage: 'Servidores', + + // Original text: "Users" + settingsUsersPage: 'Usuarios', + + // Original text: "Groups" + settingsGroupsPage: 'Grupos', + + // Original text: "ACLs" + settingsAclsPage: 'Control de acceso', + + // Original text: "Plugins" + settingsPluginsPage: 'Plugins', + + // Original text: 'Logs' + settingsLogsPage: undefined, + + // Original text: 'IPs' + settingsIpsPage: undefined, + + // Original text: 'Config' + settingsConfigPage: undefined, + + // Original text: "About" + aboutPage: 'Acerca de', + + // Original text: 'About XO {xoaPlan}' + aboutXoaPlan: undefined, + + // Original text: "New" + newMenu: 'Nuevo', + + // Original text: "Tasks" + taskMenu: 'Tareas', + + // Original text: 'Tasks' + taskPage: undefined, + + // Original text: "VM" + newVmPage: 'VM', + + // Original text: "Storage" + newSrPage: 'Almacenamiento', + + // Original text: "Server" + newServerPage: 'Servidor', + + // Original text: "Import" + newImport: 'Importar', + + // Original text: 'XOSAN' + xosan: undefined, + + // Original text: "Overview" + backupOverviewPage: 'Visión General', + + // Original text: "New" + backupNewPage: 'Nuevo', + + // Original text: "Remotes" + backupRemotesPage: 'Remotos', + + // Original text: "Restore" + backupRestorePage: 'Restaurar', + + // Original text: 'File restore' + backupFileRestorePage: undefined, + + // Original text: "Schedule" + schedule: 'Programación', + + // Original text: "New VM backup" + newVmBackup: 'Nuevo backup de VM', + + // Original text: "Edit VM backup" + editVmBackup: 'Editar backup de VM', + + // Original text: "Backup" + backup: 'Backup', + + // Original text: "Rolling Snapshot" + rollingSnapshot: 'Snapshot rotatorio', + + // Original text: "Delta Backup" + deltaBackup: 'Backup diferencial', + + // Original text: "Disaster Recovery" + disasterRecovery: 'Recuperación de desastres', + + // Original text: "Continuous Replication" + continuousReplication: 'Replicación continua', + + // Original text: "Overview" + jobsOverviewPage: 'Vistazo', + + // Original text: "New" + jobsNewPage: 'Nuevo', + + // Original text: "Scheduling" + jobsSchedulingPage: 'Programación', + + // Original text: "Custom Job" + customJob: 'Trabajo personalizado', + + // Original text: 'User' + userPage: undefined, + + // Original text: 'No support' + noSupport: undefined, + + // Original text: 'Free upgrade!' + freeUpgrade: undefined, + + // Original text: "Sign out" + signOut: 'Salir', + + // Original text: 'Edit my settings {username}' + editUserProfile: undefined, + + // Original text: "Fetching data…" + homeFetchingData: 'Recuperando datos…', + + // Original text: "Welcome on Xen Orchestra!" + homeWelcome: '¡Bienvenido a Xen Orchestra!', + + // Original text: "Add your XenServer hosts or pools" + homeWelcomeText: 'Añade tus hosts/pools de XenServer', + + // Original text: 'Some XenServers have been registered but are not connected' + homeConnectServerText: undefined, + + // Original text: "Want some help?" + homeHelp: '¿Necesitas ayuda?', + + // Original text: "Add server" + homeAddServer: 'Añadir servidor', + + // Original text: 'Connect servers' + homeConnectServer: undefined, + + // Original text: "Online Doc" + homeOnlineDoc: 'Documentación en línea', + + // Original text: "Pro Support" + homeProSupport: 'Soporte Pro', + + // Original text: "There are no VMs!" + homeNoVms: '¡No hay VMs!', + + // Original text: "Or…" + homeNoVmsOr: 'O…', + + // Original text: "Import VM" + homeImportVm: 'Importar VM', + + // Original text: "Import an existing VM in xva format" + homeImportVmMessage: 'Importar una VM existente en formato xva', + + // Original text: "Restore a backup" + homeRestoreBackup: 'Restaurar un backup', + + // Original text: "Restore a backup from a remote store" + homeRestoreBackupMessage: 'Restaurar un backup de un almacenamiento remoto', + + // Original text: "This will create a new VM" + homeNewVmMessage: 'Esto creará una nueva VM', + + // Original text: "Filters" + homeFilters: 'Filtros', + + // Original text: 'No results! Click here to reset your filters' + homeNoMatches: undefined, + + // Original text: "Pool" + homeTypePool: 'Pool', + + // Original text: "Host" + homeTypeHost: 'Host', + + // Original text: "VM" + homeTypeVm: 'VM', + + // Original text: "SR" + homeTypeSr: 'SR', + + // Original text: 'Template' + homeTypeVmTemplate: undefined, + + // Original text: "Sort" + homeSort: 'Ordenar', + + // Original text: "Pools" + homeAllPools: 'Pools', + + // Original text: "Hosts" + homeAllHosts: 'Hosts', + + // Original text: "Tags" + homeAllTags: 'Etiquetas', + + // Original text: "New VM" + homeNewVm: 'Nueva VM', + + // Original text: 'None' + homeFilterNone: undefined, + + // Original text: "Running hosts" + homeFilterRunningHosts: 'Hosts en funcionamiento', + + // Original text: "Disabled hosts" + homeFilterDisabledHosts: 'Hosts inhabilitados', + + // Original text: "Running VMs" + homeFilterRunningVms: 'VMs en funcionamiento', + + // Original text: "Non running VMs" + homeFilterNonRunningVms: 'VMs paradas', + + // Original text: "Pending VMs" + homeFilterPendingVms: 'VMs pendientes', + + // Original text: "HVM guests" + homeFilterHvmGuests: 'Invitados HVM', + + // Original text: "Tags" + homeFilterTags: 'Etiquetas', + + // Original text: "Sort by" + homeSortBy: 'Ordenar por', + + // Original text: "Name" + homeSortByName: 'Nombre', + + // Original text: "Power state" + homeSortByPowerstate: 'Estado alimentación', + + // Original text: "RAM" + homeSortByRAM: 'RAM', + + // Original text: "vCPUs" + homeSortByvCPUs: 'vCPUs', + + // Original text: 'CPUs' + homeSortByCpus: undefined, + + // Original text: 'Shared/Not shared' + homeSortByShared: undefined, + + // Original text: 'Size' + homeSortBySize: undefined, + + // Original text: 'Usage' + homeSortByUsage: undefined, + + // Original text: 'Type' + homeSortByType: undefined, + + // Original text: "{displayed, number}x {icon} (on {total, number})" + homeDisplayedItems: '{displayed, number}x {icon} (sobre {total, number})', + + // Original text: "{selected, number}x {icon} selected (on {total, number})" + homeSelectedItems: + '{selected, number}x {icon} seleccionados (sobre {total, number})', + + // Original text: "More" + homeMore: 'Más', + + // Original text: "Migrate to…" + homeMigrateTo: 'Migrar a…', + + // Original text: 'Missing patches' + homeMissingPaths: undefined, + + // Original text: 'Master:' + homePoolMaster: undefined, + + // Original text: 'Resource set: {resourceSet}' + homeResourceSet: undefined, + + // Original text: 'High Availability' + highAvailability: undefined, + + // Original text: 'Shared {type}' + srSharedType: undefined, + + // Original text: 'Not shared {type}' + srNotSharedType: undefined, + + // Original text: "Add" + add: 'Añadir', + + // Original text: 'Select all' + selectAll: undefined, + + // Original text: "Remove" + remove: 'Quitar', + + // Original text: "Preview" + preview: 'Vista previa', + + // Original text: "Item" + item: 'Elemento', + + // Original text: "No selected value" + noSelectedValue: 'No se ha seleccionado un valor', + + // Original text: "Choose user(s) and/or group(s)" + selectSubjects: 'Elegir usuario(s) y/o grupo(s)', + + // Original text: "Select Object(s)…" + selectObjects: 'Elegir Objeto(s)…', + + // Original text: "Choose a role" + selectRole: 'Elegir un rol', + + // Original text: "Select Host(s)…" + selectHosts: 'Elegir Host(s)…', + + // Original text: "Select object(s)…" + selectHostsVms: 'Elegir objeto(s)…', + + // Original text: "Select Network(s)…" + selectNetworks: 'Elegir Red(es)…', + + // Original text: "Select PIF(s)…" + selectPifs: 'Elegir PIF(s)…', + + // Original text: "Select Pool(s)…" + selectPools: 'Elegir Pool(s)…', + + // Original text: "Select Remote(s)…" + selectRemotes: 'Elegir almacenamiento(s) remoto(s)…', + + // Original text: 'Select resource set(s)…' + selectResourceSets: undefined, + + // Original text: 'Select template(s)…' + selectResourceSetsVmTemplate: undefined, + + // Original text: 'Select SR(s)…' + selectResourceSetsSr: undefined, + + // Original text: 'Select network(s)…' + selectResourceSetsNetwork: undefined, + + // Original text: 'Select disk(s)…' + selectResourceSetsVdi: undefined, + + // Original text: 'Select SSH key(s)…' + selectSshKey: undefined, + + // Original text: "Select SR(s)…" + selectSrs: 'Elegir SR(s)…', + + // Original text: "Select VM(s)…" + selectVms: 'Elegir VM(s)…', + + // Original text: "Select VM template(s)…" + selectVmTemplates: 'Elegir plantilla(s) de VM…', + + // Original text: "Select tag(s)…" + selectTags: 'Elegir etiqueta(s)…', + + // Original text: "Select disk(s)…" + selectVdis: 'Elegir disco(s)…', + + // Original text: 'Select timezone…' + selectTimezone: undefined, + + // Original text: 'Select IP(s)…' + selectIp: undefined, + + // Original text: 'Select IP pool(s)…' + selectIpPool: undefined, + + // Original text: "Fill required informations." + fillRequiredInformations: 'Introducir la información requerida', + + // Original text: "Fill informations (optional)" + fillOptionalInformations: 'Introducir datos (opcional)', + + // Original text: "Reset" + selectTableReset: 'Reiniciar', + + // Original text: "Month" + schedulingMonth: 'Mes', + + // Original text: 'Every N month' + schedulingEveryNMonth: undefined, + + // Original text: "Each selected month" + schedulingEachSelectedMonth: 'Cada mes seleccionado', + + // Original text: 'Day' + schedulingDay: undefined, + + // Original text: 'Every N day' + schedulingEveryNDay: undefined, + + // Original text: "Each selected day" + schedulingEachSelectedDay: 'Cada día seleccionado', + + // Original text: 'Switch to week days' + schedulingSetWeekDayMode: undefined, + + // Original text: 'Switch to month days' + schedulingSetMonthDayMode: undefined, + + // Original text: "Hour" + schedulingHour: 'Hora', + + // Original text: "Each selected hour" + schedulingEachSelectedHour: 'Cada hora seleccionada', + + // Original text: "Every N hour" + schedulingEveryNHour: 'Cada N horas', + + // Original text: "Minute" + schedulingMinute: 'Minuto', + + // Original text: "Each selected minute" + schedulingEachSelectedMinute: 'Cada minuto seleccionado', + + // Original text: "Every N minute" + schedulingEveryNMinute: 'Cada N minutos', + + // Original text: 'Every month' + selectTableAllMonth: undefined, + + // Original text: 'Every day' + selectTableAllDay: undefined, + + // Original text: 'Every hour' + selectTableAllHour: undefined, + + // Original text: 'Every minute' + selectTableAllMinute: undefined, + + // Original text: "Reset" + schedulingReset: 'Reiniciar', + + // Original text: "Unknown" + unknownSchedule: 'Desconocido', + + // Original text: 'Web browser timezone' + timezonePickerUseLocalTime: undefined, + + // Original text: 'Server timezone ({value})' + serverTimezoneOption: undefined, + + // Original text: 'Cron Pattern:' + cronPattern: undefined, + + // Original text: "Cannot edit backup" + backupEditNotFoundTitle: 'Imposible editar backup', + + // Original text: "Missing required info for edition" + backupEditNotFoundMessage: 'Falta información requerida para la edición', + + // Original text: 'Successful' + successfulJobCall: undefined, + + // Original text: 'Failed' + failedJobCall: undefined, + + // Original text: 'In progress' + jobCallInProgess: undefined, + + // Original text: 'size:' + jobTransferredDataSize: undefined, + + // Original text: 'speed:' + jobTransferredDataSpeed: undefined, + + // Original text: "Job" + job: 'Tarea', + + // Original text: 'Job {job}' + jobModalTitle: undefined, + + // Original text: "ID" + jobId: 'ID de Tarea', + + // Original text: 'Type' + jobType: undefined, + + // Original text: "Name" + jobName: 'Nombre', + + // Original text: 'Name of your job (forbidden: "_")' + jobNamePlaceholder: undefined, + + // Original text: "Start" + jobStart: 'Comenzar', + + // Original text: "End" + jobEnd: 'Finalizar', + + // Original text: "Duration" + jobDuration: 'Duración', + + // Original text: "Status" + jobStatus: 'Estado', + + // Original text: "Action" + jobAction: 'Acción', + + // Original text: "Tag" + jobTag: 'Etiqueta', + + // Original text: "Scheduling" + jobScheduling: 'Programación', + + // Original text: "State" + jobState: 'Estado', + + // Original text: 'Enabled' + jobStateEnabled: undefined, + + // Original text: 'Disabled' + jobStateDisabled: undefined, + + // Original text: 'Timezone' + jobTimezone: undefined, + + // Original text: 'Server' + jobServerTimezone: undefined, + + // Original text: "Run job" + runJob: 'Ejecutar tarea', + + // Original text: "One shot running started. See overview for logs." + runJobVerbose: 'Comienzo del backup manual. Ir a Resumen para ver logs', + + // Original text: "Started" + jobStarted: 'Comenzado', + + // Original text: "Finished" + jobFinished: 'Finalizado', + + // Original text: "Save" + saveBackupJob: 'Guardar', + + // Original text: "Remove backup job" + deleteBackupSchedule: 'Eliminar tarea de backup', + + // Original text: "Are you sure you want to delete this backup job?" + deleteBackupScheduleQuestion: + '¿Estás seguro de querer borrar esta tarea de backup?', + + // Original text: "Enable immediately after creation" + scheduleEnableAfterCreation: 'Activar inmediatamente tras la creación', + + // Original text: "You are editing Schedule {name} ({id}). Saving will override previous schedule state." + scheduleEditMessage: + 'Estás editando la Programación {name} ({id}). Se sobreescribirá el estado actual al guardar.', + + // Original text: "You are editing job {name} ({id}). Saving will override previous job state." + jobEditMessage: + 'Estás editando la Tarea {name} ({id}). Se sobreescribirá el estado actual al guardar.', + + // Original text: "No scheduled jobs." + noScheduledJobs: 'No hay tareas programadas', + + // Original text: "No jobs found." + noJobs: 'No se han encontrado tareas', + + // Original text: "No schedules found" + noSchedules: 'No se han encontrado programaciones', + + // Original text: "Select a xo-server API command" + jobActionPlaceHolder: 'Elige un comando de la API de xo-server', + + // Original text: ' Timeout (number of seconds after which a VM is considered failed)' + jobTimeoutPlaceHolder: undefined, + + // Original text: 'Schedules' + jobSchedules: undefined, + + // Original text: 'Name of your schedule' + jobScheduleNamePlaceHolder: undefined, + + // Original text: 'Select a Job' + jobScheduleJobPlaceHolder: undefined, + + // Original text: 'Job owner' + jobOwnerPlaceholder: undefined, + + // Original text: "This job's creator no longer exists" + jobUserNotFound: undefined, + + // Original text: "This backup's creator no longer exists" + backupUserNotFound: undefined, + + // Original text: 'Backup owner' + backupOwner: undefined, + + // Original text: "Select your backup type:" + newBackupSelection: 'Elige el tipo de backup', + + // Original text: 'Select backup mode:' + smartBackupModeSelection: undefined, + + // Original text: 'Normal backup' + normalBackup: undefined, + + // Original text: 'Smart backup' + smartBackup: undefined, + + // Original text: 'Local remote selected' + localRemoteWarningTitle: undefined, + + // Original text: 'Warning: local remotes will use limited XOA disk space. Only for advanced users.' + localRemoteWarningMessage: undefined, + + // Original text: 'Warning: this feature works only with XenServer 6.5 or newer.' + backupVersionWarning: undefined, + + // Original text: 'VMs' + editBackupVmsTitle: undefined, + + // Original text: 'VMs statuses' + editBackupSmartStatusTitle: undefined, + + // Original text: 'Resident on' + editBackupSmartResidentOn: undefined, + + // Original text: 'Pools' + editBackupSmartPools: undefined, + + // Original text: 'Tags' + editBackupSmartTags: undefined, + + // Original text: 'VMs Tags' + editBackupSmartTagsTitle: undefined, + + // Original text: 'Reverse' + editBackupNot: undefined, + + // Original text: 'Tag' + editBackupTagTitle: undefined, + + // Original text: 'Report' + editBackupReportTitle: undefined, + + // Original text: 'Automatically run as scheduled' + editBackupScheduleEnabled: undefined, + + // Original text: 'Depth' + editBackupDepthTitle: undefined, + + // Original text: 'Remote' + editBackupRemoteTitle: undefined, + + // Original text: 'Delete the old backups first' + deleteOldBackupsFirst: undefined, + + // Original text: "Remote stores for backup" + remoteList: 'Almacenamientos remotos para el backup', + + // Original text: "New File System Remote" + newRemote: 'Nuevo Sistema de Archivos Remoto', + + // Original text: "Local" + remoteTypeLocal: 'Local', + + // Original text: "NFS" + remoteTypeNfs: 'NFS', + + // Original text: "SMB" + remoteTypeSmb: 'Samba', + + // Original text: "Type" + remoteType: 'Tipo', + + // Original text: 'Test your remote' + remoteTestTip: undefined, + + // Original text: 'Test Remote' + testRemote: undefined, + + // Original text: 'Test failed for {name}' + remoteTestFailure: undefined, + + // Original text: 'Test passed for {name}' + remoteTestSuccess: undefined, + + // Original text: 'Error' + remoteTestError: undefined, + + // Original text: 'Test Step' + remoteTestStep: undefined, + + // Original text: 'Test file' + remoteTestFile: undefined, + + // Original text: 'Test name' + remoteTestName: undefined, + + // Original text: 'Remote name already exists!' + remoteTestNameFailure: undefined, + + // Original text: 'The remote appears to work correctly' + remoteTestSuccessMessage: undefined, + + // Original text: 'Connection failed' + remoteConnectionFailed: undefined, + + // Original text: 'Name' + remoteName: undefined, + + // Original text: 'Path' + remotePath: undefined, + + // Original text: 'State' + remoteState: undefined, + + // Original text: 'Device' + remoteDevice: undefined, + + // Original text: 'Share' + remoteShare: undefined, + + // Original text: 'Action' + remoteAction: undefined, + + // Original text: 'Auth' + remoteAuth: undefined, + + // Original text: 'Mounted' + remoteMounted: undefined, + + // Original text: 'Unmounted' + remoteUnmounted: undefined, + + // Original text: 'Connect' + remoteConnectTip: undefined, + + // Original text: 'Disconnect' + remoteDisconnectTip: undefined, + + // Original text: 'Connected' + remoteConnected: undefined, + + // Original text: 'Disconnected' + remoteDisconnected: undefined, + + // Original text: 'Delete' + remoteDeleteTip: undefined, + + // Original text: 'remote name *' + remoteNamePlaceHolder: undefined, + + // Original text: 'Name *' + remoteMyNamePlaceHolder: undefined, + + // Original text: '/path/to/backup' + remoteLocalPlaceHolderPath: undefined, + + // Original text: 'host *' + remoteNfsPlaceHolderHost: undefined, + + // Original text: 'path/to/backup' + remoteNfsPlaceHolderPath: undefined, + + // Original text: 'subfolder [path\\to\\backup]' + remoteSmbPlaceHolderRemotePath: undefined, + + // Original text: 'Username' + remoteSmbPlaceHolderUsername: undefined, + + // Original text: 'Password' + remoteSmbPlaceHolderPassword: undefined, + + // Original text: 'Domain' + remoteSmbPlaceHolderDomain: undefined, + + // Original text: '
\\ *' + remoteSmbPlaceHolderAddressShare: undefined, + + // Original text: 'password(fill to edit)' + remotePlaceHolderPassword: undefined, + + // Original text: 'Create a new SR' + newSrTitle: undefined, + + // Original text: "General" + newSrGeneral: 'General', + + // Original text: "Select Storage Type:" + newSrTypeSelection: 'Elige el tipo de almacenamiento', + + // Original text: "Settings" + newSrSettings: 'Configuración', + + // Original text: "Storage Usage" + newSrUsage: 'Utilización del almacenamiento', + + // Original text: "Summary" + newSrSummary: 'Sumario', + + // Original text: "Host" + newSrHost: 'Host', + + // Original text: "Type" + newSrType: 'Tipo', + + // Original text: "Name" + newSrName: 'Nombre', + + // Original text: "Description" + newSrDescription: 'Descripción', + + // Original text: "Server" + newSrServer: 'Servidor', + + // Original text: "Path" + newSrPath: 'Ruta', + + // Original text: "IQN" + newSrIqn: 'IQN', + + // Original text: "LUN" + newSrLun: 'LUN', + + // Original text: "with auth." + newSrAuth: 'con autenticación', + + // Original text: "User Name" + newSrUsername: 'Nombre de usuario', + + // Original text: "Password" + newSrPassword: 'Clave', + + // Original text: "Device" + newSrDevice: 'Dispositivo', + + // Original text: "in use" + newSrInUse: 'en uso', + + // Original text: "Size" + newSrSize: 'Tamaño', + + // Original text: "Create" + newSrCreate: 'Crear', + + // Original text: 'Storage name' + newSrNamePlaceHolder: undefined, + + // Original text: 'Storage description' + newSrDescPlaceHolder: undefined, + + // Original text: 'Address' + newSrAddressPlaceHolder: undefined, + + // Original text: '[port]' + newSrPortPlaceHolder: undefined, + + // Original text: 'Username' + newSrUsernamePlaceHolder: undefined, + + // Original text: 'Password' + newSrPasswordPlaceHolder: undefined, + + // Original text: 'Device, e.g /dev/sda…' + newSrLvmDevicePlaceHolder: undefined, + + // Original text: '/path/to/directory' + newSrLocalPathPlaceHolder: undefined, + + // Original text: "Users/Groups" + subjectName: 'Usuarios/Grupos', + + // Original text: "Object" + objectName: 'Objeto', + + // Original text: 'No acls found' + aclNoneFound: undefined, + + // Original text: "Role" + roleName: 'Rol', + + // Original text: 'Create' + aclCreate: undefined, + + // Original text: "New Group Name" + newGroupName: 'Nuevo nombre del Grupo', + + // Original text: "Create Group" + createGroup: 'Crear grupo', + + // Original text: "Create" + createGroupButton: 'Crear', + + // Original text: "Delete Group" + deleteGroup: 'Borrar grupo', + + // Original text: "Are you sure you want to delete this group?" + deleteGroupConfirm: '¿Estás seguro de querer borrar este grupo?', + + // Original text: "Remove user from Group" + removeUserFromGroup: '¿Quitar usuario del grupo?', + + // Original text: "Are you sure you want to delete this user?" + deleteUserConfirm: '¿Estás seguro de querer borrar este usuario?', + + // Original text: "Delete User" + deleteUser: 'Borrar usuario', + + // Original text: 'no user' + noUser: undefined, + + // Original text: "unknown user" + unknownUser: 'usuario desconocido', + + // Original text: "No group found" + noGroupFound: 'Grupo no encontrado', + + // Original text: "Name" + groupNameColumn: 'Nombre', + + // Original text: "Users" + groupUsersColumn: 'Usuarios', + + // Original text: "Add User" + addUserToGroupColumn: 'Añadir usuario', + + // Original text: "Email" + userNameColumn: 'Email', + + // Original text: "Permissions" + userPermissionColumn: 'Permisos', + + // Original text: "Password" + userPasswordColumn: 'Clave', + + // Original text: "Email" + userName: 'Email', + + // Original text: "Password" + userPassword: 'Clave', + + // Original text: "Create" + createUserButton: 'Crear', + + // Original text: "No user found" + noUserFound: 'Usuario no encontrado', + + // Original text: "User" + userLabel: 'Usuario', + + // Original text: "Admin" + adminLabel: 'Admin', + + // Original text: "No user in group" + noUserInGroup: 'Grupo sin usuarios', + + // Original text: "{users, number} user{users, plural, one {} other {s}}" + countUsers: '{users, number} user{users, plural, one {} other {s}}', + + // Original text: "Select Permission" + selectPermission: 'Seleccionar permiso', + + // Original text: 'No plugins found' + noPlugins: undefined, + + // Original text: "Auto-load at server start" + autoloadPlugin: 'Cargar al iniciar el servidor', + + // Original text: "Save configuration" + savePluginConfiguration: 'Guardar configuración', + + // Original text: "Delete configuration" + deletePluginConfiguration: 'Borrar configuración', + + // Original text: "Plugin error" + pluginError: 'Error del Plugin', + + // Original text: "Unknown error" + unknownPluginError: 'Error desconocido', + + // Original text: "Purge plugin configuration" + purgePluginConfiguration: 'Purgar la configuración de plugins', + + // Original text: "Are you sure you want to purge this configuration ?" + purgePluginConfigurationQuestion: + '¿Estás seguro de querer purgar esta configuración?', + + // Original text: "Edit" + editPluginConfiguration: 'Editar', + + // Original text: "Cancel" + cancelPluginEdition: 'Cancelar', + + // Original text: "Plugin configuration" + pluginConfigurationSuccess: 'Configuración del Plugin', + + // Original text: "Plugin configuration successfully saved!" + pluginConfigurationChanges: + '¡Configuración del Plugin guardada correctamente!', + + // Original text: 'Predefined configuration' + pluginConfigurationPresetTitle: undefined, + + // Original text: 'Choose a predefined configuration.' + pluginConfigurationChoosePreset: undefined, + + // Original text: 'Apply' + applyPluginPreset: undefined, + + // Original text: 'Save filter error' + saveNewUserFilterErrorTitle: undefined, + + // Original text: 'Bad parameter: name must be given.' + saveNewUserFilterErrorBody: undefined, + + // Original text: 'Name:' + filterName: undefined, + + // Original text: 'Value:' + filterValue: undefined, + + // Original text: 'Save new filter' + saveNewFilterTitle: undefined, + + // Original text: 'Set custom filters' + setUserFiltersTitle: undefined, + + // Original text: 'Are you sure you want to set custom filters?' + setUserFiltersBody: undefined, + + // Original text: 'Remove custom filter' + removeUserFilterTitle: undefined, + + // Original text: 'Are you sure you want to remove custom filter?' + removeUserFilterBody: undefined, + + // Original text: 'Default filter' + defaultFilter: undefined, + + // Original text: 'Default filters' + defaultFilters: undefined, + + // Original text: 'Custom filters' + customFilters: undefined, + + // Original text: 'Customize filters' + customizeFilters: undefined, + + // Original text: 'Save custom filters' + saveCustomFilters: undefined, + + // Original text: "Start" + startVmLabel: 'Iniciar', + + // Original text: "Recovery start" + recoveryModeLabel: 'Inicio de rescate', + + // Original text: "Suspend" + suspendVmLabel: 'Suspender', + + // Original text: "Stop" + stopVmLabel: 'Parar', + + // Original text: "Force shutdown" + forceShutdownVmLabel: 'Forzar apagado', + + // Original text: "Reboot" + rebootVmLabel: 'Reiniciar', + + // Original text: "Force reboot" + forceRebootVmLabel: 'Forzar reinicio', + + // Original text: "Delete" + deleteVmLabel: 'Borrar', + + // Original text: "Migrate" + migrateVmLabel: 'Migrar', + + // Original text: "Snapshot" + snapshotVmLabel: 'Snapshot', + + // Original text: "Export" + exportVmLabel: 'Exportar', + + // Original text: "Resume" + resumeVmLabel: 'Reanudar', + + // Original text: "Copy" + copyVmLabel: 'Copiar', + + // Original text: "Clone" + cloneVmLabel: 'Clonar', + + // Original text: "Fast clone" + fastCloneVmLabel: 'Clonado rápido', + + // Original text: "Convert to template" + convertVmToTemplateLabel: 'Convertir en plantilla', + + // Original text: "Console" + vmConsoleLabel: 'Consola', + + // Original text: "Rescan all disks" + srRescan: 'Releer todos los discos', + + // Original text: "Connect to all hosts" + srReconnectAll: 'Conectar a todos los hosts', + + // Original text: "Disconnect from all hosts" + srDisconnectAll: 'Desconectar de todos los hosts', + + // Original text: "Forget this SR" + srForget: 'Olvidar este SR', + + // Original text: 'Forget SRs' + srsForget: undefined, + + // Original text: "Remove this SR" + srRemoveButton: 'Borrar este SR', + + // Original text: "No VDIs in this storage" + srNoVdis: 'No hay VDIs en éste almancenamiento', + + // Original text: 'Pool RAM usage:' + poolTitleRamUsage: undefined, + + // Original text: '{used} used on {total}' + poolRamUsage: undefined, + + // Original text: 'Master:' + poolMaster: undefined, + + // Original text: 'Display all hosts of this pool' + displayAllHosts: undefined, + + // Original text: 'Display all storages of this pool' + displayAllStorages: undefined, + + // Original text: 'Display all VMs of this pool' + displayAllVMs: undefined, + + // Original text: "Hosts" + hostsTabName: 'Hosts', + + // Original text: 'Vms' + vmsTabName: undefined, + + // Original text: 'Srs' + srsTabName: undefined, + + // Original text: "High Availability" + poolHaStatus: 'Alta Disponibilidad', + + // Original text: "Enabled" + poolHaEnabled: 'Activado', + + // Original text: "Disabled" + poolHaDisabled: 'Desactivado', + + // Original text: "Name" + hostNameLabel: 'Nombre', + + // Original text: "Description" + hostDescription: 'Descripción', + + // Original text: "Memory" + hostMemory: 'Memoria', + + // Original text: "No hosts" + noHost: 'Sin hosts', + + // Original text: '{used}% used ({free} free)' + memoryLeftTooltip: undefined, + + // Original text: 'PIF' + pif: undefined, + + // Original text: "Name" + poolNetworkNameLabel: 'Nombre', + + // Original text: "Description" + poolNetworkDescription: 'Descripción', + + // Original text: "PIFs" + poolNetworkPif: 'PIFs', + + // Original text: "No networks" + poolNoNetwork: 'Sin redes', + + // Original text: "MTU" + poolNetworkMTU: 'MTU', + + // Original text: "Connected" + poolNetworkPifAttached: 'Conectado', + + // Original text: "Disconnected" + poolNetworkPifDetached: 'Desconectado', + + // Original text: 'Show PIFs' + showPifs: undefined, + + // Original text: 'Hide PIFs' + hidePifs: undefined, + + // Original text: 'Show details' + showDetails: undefined, + + // Original text: 'Hide details' + hideDetails: undefined, + + // Original text: 'No stats' + poolNoStats: undefined, + + // Original text: 'All hosts' + poolAllHosts: undefined, + + // Original text: "Add SR" + addSrLabel: 'Añadir SR', + + // Original text: "Add VM" + addVmLabel: 'Añadir VM', + + // Original text: "Add Host" + addHostLabel: 'Añadir host', + + // Original text: 'This host needs to install {patches, number} patch{patches, plural, one {} other {es}} before it can be added to the pool. This operation may be long.' + hostNeedsPatchUpdate: undefined, + + // Original text: "This host cannot be added to the pool because it's missing some patches." + hostNeedsPatchUpdateNoInstall: undefined, + + // Original text: 'Adding host failed' + addHostErrorTitle: undefined, + + // Original text: 'Host patches could not be homogenized.' + addHostNotHomogeneousErrorMessage: undefined, + + // Original text: "Disconnect" + disconnectServer: 'Desconectar', + + // Original text: "Start" + startHostLabel: 'Arrancar', + + // Original text: "Stop" + stopHostLabel: 'Parar', + + // Original text: "Enable" + enableHostLabel: 'Activar', + + // Original text: "Disable" + disableHostLabel: 'Desactivar', + + // Original text: "Restart toolstack" + restartHostAgent: 'Reiniciar toolstack', + + // Original text: "Force reboot" + forceRebootHostLabel: 'Forzar reinicio', + + // Original text: "Reboot" + rebootHostLabel: 'Reiniciar', + + // Original text: 'Error while restarting host' + noHostsAvailableErrorTitle: undefined, + + // Original text: 'Some VMs cannot be migrated before restarting this host. Please try force reboot.' + noHostsAvailableErrorMessage: undefined, + + // Original text: 'Error while restarting hosts' + failHostBulkRestartTitle: undefined, + + // Original text: '{failedHosts, number}/{totalHosts, number} host{failedHosts, plural, one {} other {s}} could not be restarted.' + failHostBulkRestartMessage: undefined, + + // Original text: 'Reboot to apply updates' + rebootUpdateHostLabel: undefined, + + // Original text: "Emergency mode" + emergencyModeLabel: 'Modo de Emergencia', + + // Original text: "Storage" + storageTabName: 'Almacenamiento', + + // Original text: "Patches" + patchesTabName: 'Parches', + + // Original text: "Load average" + statLoad: 'Carga', + + // Original text: 'RAM Usage: {memoryUsed}' + memoryHostState: undefined, + + // Original text: "Hardware" + hardwareHostSettingsLabel: 'Hardware', + + // Original text: "Address" + hostAddress: 'Dirección', + + // Original text: "Status" + hostStatus: 'Estado', + + // Original text: "Build number" + hostBuildNumber: 'Número de compilación', + + // Original text: "iSCSI name" + hostIscsiName: 'Nombre iSCSI', + + // Original text: "Version" + hostXenServerVersion: 'Versión', + + // Original text: "Enabled" + hostStatusEnabled: 'Activado', + + // Original text: "Disabled" + hostStatusDisabled: 'Desactivado', + + // Original text: "Power on mode" + hostPowerOnMode: 'Modo de encendido', + + // Original text: "Host uptime" + hostStartedSince: 'Tiempo encendido', + + // Original text: "Toolstack uptime" + hostStackStartedSince: 'Tiempo de encendido del toolstack', + + // Original text: "CPU model" + hostCpusModel: 'Modelo de CPU', + + // Original text: "Core (socket)" + hostCpusNumber: 'Core (socket)', + + // Original text: "Manufacturer info" + hostManufacturerinfo: 'Información del fabricante', + + // Original text: "BIOS info" + hostBiosinfo: 'Información de BIOS', + + // Original text: "Licence" + licenseHostSettingsLabel: 'Licencia', + + // Original text: "Type" + hostLicenseType: 'Tipo', + + // Original text: "Socket" + hostLicenseSocket: 'Socket', + + // Original text: "Expiry" + hostLicenseExpiry: 'Expiración', + + // Original text: 'Installed supplemental packs' + supplementalPacks: undefined, + + // Original text: 'Install new supplemental pack' + supplementalPackNew: undefined, + + // Original text: 'Install supplemental pack on every host' + supplementalPackPoolNew: undefined, + + // Original text: '{name} (by {author})' + supplementalPackTitle: undefined, + + // Original text: 'Installation started' + supplementalPackInstallStartedTitle: undefined, + + // Original text: 'Installing new supplemental pack…' + supplementalPackInstallStartedMessage: undefined, + + // Original text: 'Installation error' + supplementalPackInstallErrorTitle: undefined, + + // Original text: 'The installation of the supplemental pack failed.' + supplementalPackInstallErrorMessage: undefined, + + // Original text: 'Installation success' + supplementalPackInstallSuccessTitle: undefined, + + // Original text: 'Supplemental pack successfully installed.' + supplementalPackInstallSuccessMessage: undefined, + + // Original text: "Add a network" + networkCreateButton: 'Añadir red', + + // Original text: 'Add a bonded network' + networkCreateBondedButton: undefined, + + // Original text: "Device" + pifDeviceLabel: 'Dispositivo', + + // Original text: "Network" + pifNetworkLabel: 'Red', + + // Original text: "VLAN" + pifVlanLabel: 'VLAN', + + // Original text: "Address" + pifAddressLabel: 'Dirección', + + // Original text: 'Mode' + pifModeLabel: undefined, + + // Original text: "MAC" + pifMacLabel: 'MAC', + + // Original text: "MTU" + pifMtuLabel: 'MTU', + + // Original text: "Status" + pifStatusLabel: 'Estado', + + // Original text: "Connected" + pifStatusConnected: 'Conectado', + + // Original text: "Disconnected" + pifStatusDisconnected: 'Desconectado', + + // Original text: "No physical interface detected" + pifNoInterface: 'No se ha detectado ningún interface físico', + + // Original text: 'This interface is currently in use' + pifInUse: undefined, + + // Original text: 'Action' + pifAction: undefined, + + // Original text: 'Default locking mode' + defaultLockingMode: undefined, + + // Original text: 'Configure IP address' + pifConfigureIp: undefined, + + // Original text: 'Invalid parameters' + configIpErrorTitle: undefined, + + // Original text: 'IP address and netmask required' + configIpErrorMessage: undefined, + + // Original text: 'Static IP address' + staticIp: undefined, + + // Original text: 'Netmask' + netmask: undefined, + + // Original text: 'DNS' + dns: undefined, + + // Original text: 'Gateway' + gateway: undefined, + + // Original text: "Add a storage" + addSrDeviceButton: 'Añadir un almacenamiento', + + // Original text: "Name" + srNameLabel: 'Nombre', + + // Original text: "Type" + srType: 'Tipo', + + // Original text: 'Action' + pbdAction: undefined, + + // Original text: "Status" + pbdStatus: 'Estado', + + // Original text: "Connected" + pbdStatusConnected: 'Conectado', + + // Original text: "Disconnected" + pbdStatusDisconnected: 'Desconectado', + + // Original text: 'Connect' + pbdConnect: undefined, + + // Original text: 'Disconnect' + pbdDisconnect: undefined, + + // Original text: 'Forget' + pbdForget: undefined, + + // Original text: "Shared" + srShared: 'Compartido', + + // Original text: "Not shared" + srNotShared: 'No compartido', + + // Original text: "No storage detected" + pbdNoSr: 'No se han detectado almacenamientos', + + // Original text: "Name" + patchNameLabel: 'Nombre', + + // Original text: "Install all patches" + patchUpdateButton: 'Instalar todos los parches', + + // Original text: "Description" + patchDescription: 'Descripción', + + // Original text: "Applied date" + patchApplied: 'Fecha de publicación', + + // Original text: "Size" + patchSize: 'Tamaño', + + // Original text: "Status" + patchStatus: 'Estado', + + // Original text: "Applied" + patchStatusApplied: 'Aplicado', + + // Original text: "Missing patches" + patchStatusNotApplied: 'Parches pendientes', + + // Original text: "No patch detected" + patchNothing: 'No se ha detectado el parche', + + // Original text: "Release date" + patchReleaseDate: 'Fecha de publicación', + + // Original text: "Guidance" + patchGuidance: 'Guía', + + // Original text: "Action" + patchAction: 'Acción', + + // Original text: 'Applied patches' + hostAppliedPatches: undefined, + + // Original text: "Missing patches" + hostMissingPatches: 'Parches pendientes', + + // Original text: "Host up-to-date!" + hostUpToDate: '¡Host al día!', + + // Original text: 'Non-recommended patch install' + installPatchWarningTitle: undefined, + + // Original text: 'This will install a patch only on this host. This is NOT the recommended way: please go into the Pool patch view and follow instructions there. If you are sure about this, you can continue anyway' + installPatchWarningContent: undefined, + + // Original text: 'Go to pool' + installPatchWarningReject: undefined, + + // Original text: 'Install' + installPatchWarningResolve: undefined, + + // Original text: 'Refresh patches' + refreshPatches: undefined, + + // Original text: 'Install pool patches' + installPoolPatches: undefined, + + // Original text: 'Default SR' + defaultSr: undefined, + + // Original text: 'Set as default SR' + setAsDefaultSr: undefined, + + // Original text: "General" + generalTabName: 'General', + + // Original text: "Stats" + statsTabName: 'Estadísticas', + + // Original text: "Console" + consoleTabName: 'Consola', + + // Original text: 'Container' + containersTabName: undefined, + + // Original text: "Snapshots" + snapshotsTabName: 'Snapshots', + + // Original text: "Logs" + logsTabName: 'Logs', + + // Original text: "Advanced" + advancedTabName: 'Avanzado', + + // Original text: "Network" + networkTabName: 'Red', + + // Original text: "Disk{disks, plural, one {} other {s}}" + disksTabName: 'Disco{disks, plural, one {} other {s}}', + + // Original text: "halted" + powerStateHalted: 'parada', + + // Original text: "running" + powerStateRunning: 'corriendo', + + // Original text: "suspended" + powerStateSuspended: 'suspendida', + + // Original text: "No Xen tools detected" + vmStatus: 'Xen Tools no detectado', + + // Original text: "No IPv4 record" + vmName: 'Sin registro IPv4', + + // Original text: "No IP record" + vmDescription: 'Sin registro IP', + + // Original text: "Started {ago}" + vmSettings: 'Arrancada {ago}', + + // Original text: "Current status:" + vmCurrentStatus: 'Estado actual', + + // Original text: "Not running" + vmNotRunning: 'No está corriendo', + + // Original text: 'Halted {ago}' + vmHaltedSince: undefined, + + // Original text: "No Xen tools detected" + noToolsDetected: 'Xen Tools no detectado', + + // Original text: "No IPv4 record" + noIpv4Record: 'Sin registro IPv4', + + // Original text: "No IP record" + noIpRecord: 'Sin registro IP', + + // Original text: "Started {ago}" + started: 'Arrancada {ago}', + + // Original text: "Paravirtualization (PV)" + paraVirtualizedMode: 'Paravirtualización (PV)', + + // Original text: "Hardware virtualization (HVM)" + hardwareVirtualizedMode: 'Paravirtualización por Hardware (HVM)', + + // Original text: "CPU usage" + statsCpu: 'Uso de CPU', + + // Original text: "Memory usage" + statsMemory: 'Uso de Memoria', + + // Original text: "Network throughput" + statsNetwork: 'Actividad de Red', + + // Original text: 'Stacked values' + useStackedValuesOnStats: undefined, + + // Original text: "Disk throughput" + statDisk: 'Actividad de Disco', + + // Original text: "Last 10 minutes" + statLastTenMinutes: 'Últimos 10 minutos', + + // Original text: "Last 2 hours" + statLastTwoHours: 'Últimas 2 horas', + + // Original text: "Last week" + statLastWeek: 'Última semana', + + // Original text: "Last year" + statLastYear: 'Último año', + + // Original text: "Copy" + copyToClipboardLabel: 'Copiar', + + // Original text: "Ctrl+Alt+Del" + ctrlAltDelButtonLabel: 'Ctrl+Alt+Del', + + // Original text: "Tip:" + tipLabel: 'Consejo:', + + // Original text: 'Hide infos' + hideHeaderTooltip: undefined, + + // Original text: 'Show infos' + showHeaderTooltip: undefined, + + // Original text: 'Name' + containerName: undefined, + + // Original text: 'Command' + containerCommand: undefined, + + // Original text: 'Creation date' + containerCreated: undefined, + + // Original text: 'Status' + containerStatus: undefined, + + // Original text: 'Action' + containerAction: undefined, + + // Original text: 'No existing containers' + noContainers: undefined, + + // Original text: 'Stop this container' + containerStop: undefined, + + // Original text: 'Start this container' + containerStart: undefined, + + // Original text: 'Pause this container' + containerPause: undefined, + + // Original text: 'Resume this container' + containerResume: undefined, + + // Original text: 'Restart this container' + containerRestart: undefined, + + // Original text: "Action" + vdiAction: 'Acción', + + // Original text: "Attach disk" + vdiAttachDeviceButton: 'Adjuntar disco', + + // Original text: "New disk" + vbdCreateDeviceButton: 'Nuevo disco', + + // Original text: "Boot order" + vdiBootOrder: 'Order de arranque', + + // Original text: "Name" + vdiNameLabel: 'Nombre', + + // Original text: "Description" + vdiNameDescription: 'Descripción', + + // Original text: 'Pool' + vdiPool: undefined, + + // Original text: 'Disconnect' + vdiDisconnect: undefined, + + // Original text: "Tags" + vdiTags: 'Tareas', + + // Original text: "Size" + vdiSize: 'Tamaño', + + // Original text: "SR" + vdiSr: 'SR', + + // Original text: 'VM' + vdiVm: undefined, + + // Original text: 'Migrate VDI' + vdiMigrate: undefined, + + // Original text: 'Destination SR:' + vdiMigrateSelectSr: undefined, + + // Original text: 'Migrate all VDIs' + vdiMigrateAll: undefined, + + // Original text: 'No SR' + vdiMigrateNoSr: undefined, + + // Original text: 'A target SR is required to migrate a VDI' + vdiMigrateNoSrMessage: undefined, + + // Original text: 'Forget' + vdiForget: undefined, + + // Original text: 'Remove VDI' + vdiRemove: undefined, + + // Original text: 'No VDIs attached to Control Domain' + noControlDomainVdis: undefined, + + // Original text: "Boot flag" + vbdBootableStatus: 'Etiqueta de Inicio', + + // Original text: "Status" + vbdStatus: 'Estado', + + // Original text: "Connected" + vbdStatusConnected: 'Conectado', + + // Original text: "Disconnected" + vbdStatusDisconnected: 'Desconectado', + + // Original text: "No disks" + vbdNoVbd: 'Sin discos', + + // Original text: 'Connect VBD' + vbdConnect: undefined, + + // Original text: 'Disconnect VBD' + vbdDisconnect: undefined, + + // Original text: 'Bootable' + vbdBootable: undefined, + + // Original text: 'Readonly' + vbdReadonly: undefined, + + // Original text: 'Action' + vbdAction: undefined, + + // Original text: 'Create' + vbdCreate: undefined, + + // Original text: 'Disk name' + vbdNamePlaceHolder: undefined, + + // Original text: 'Size' + vbdSizePlaceHolder: undefined, + + // Original text: 'CD drive not completely installed' + cdDriveNotInstalled: undefined, + + // Original text: 'Stop and start the VM to install the CD drive' + cdDriveInstallation: undefined, + + // Original text: 'Save' + saveBootOption: undefined, + + // Original text: 'Reset' + resetBootOption: undefined, + + // Original text: "New device" + vifCreateDeviceButton: 'Nuevo dispositivo', + + // Original text: "No interface" + vifNoInterface: 'Sin interface', + + // Original text: "Device" + vifDeviceLabel: 'Dispositivo', + + // Original text: "MAC address" + vifMacLabel: 'Dirección MAC', + + // Original text: "MTU" + vifMtuLabel: 'MTU', + + // Original text: "Network" + vifNetworkLabel: 'Red', + + // Original text: "Status" + vifStatusLabel: 'Estado', + + // Original text: "Connected" + vifStatusConnected: 'Conectado', + + // Original text: "Disconnected" + vifStatusDisconnected: 'Desconectado', + + // Original text: 'Connect' + vifConnect: undefined, + + // Original text: 'Disconnect' + vifDisconnect: undefined, + + // Original text: 'Remove' + vifRemove: undefined, + + // Original text: "IP addresses" + vifIpAddresses: 'Dirección IP', + + // Original text: 'Auto-generated if empty' + vifMacAutoGenerate: undefined, + + // Original text: 'Allowed IPs' + vifAllowedIps: undefined, + + // Original text: 'No IPs' + vifNoIps: undefined, + + // Original text: 'Network locked' + vifLockedNetwork: undefined, + + // Original text: 'Network locked and no IPs are allowed for this interface' + vifLockedNetworkNoIps: undefined, + + // Original text: 'Network not locked' + vifUnLockedNetwork: undefined, + + // Original text: 'Unknown network' + vifUnknownNetwork: undefined, + + // Original text: 'Action' + vifAction: undefined, + + // Original text: 'Create' + vifCreate: undefined, + + // Original text: "No snapshots" + noSnapshots: 'Sin snapshots', + + // Original text: "New snapshot" + snapshotCreateButton: 'Nuevo snapshot', + + // Original text: "Just click on the snapshot button to create one!" + tipCreateSnapshotLabel: '¡Haz click en el botón de snapshot para crear uno!', + + // Original text: 'Revert VM to this snapshot' + revertSnapshot: undefined, + + // Original text: 'Remove this snapshot' + deleteSnapshot: undefined, + + // Original text: 'Create a VM from this snapshot' + copySnapshot: undefined, + + // Original text: 'Export this snapshot' + exportSnapshot: undefined, + + // Original text: "Creation date" + snapshotDate: 'Fecha de creación', + + // Original text: "Name" + snapshotName: 'Nombre', + + // Original text: "Action" + snapshotAction: 'Acción', + + // Original text: 'Quiesced snapshot' + snapshotQuiesce: undefined, + + // Original text: "Remove all logs" + logRemoveAll: 'Elimintar todos los logs', + + // Original text: "No logs so far" + noLogs: 'No hay logs', + + // Original text: "Creation date" + logDate: 'Fecha de creación', + + // Original text: "Name" + logName: 'Nombre', + + // Original text: "Content" + logContent: 'Contenido', + + // Original text: "Action" + logAction: 'Acción', + + // Original text: "Remove" + vmRemoveButton: 'Quitar', + + // Original text: "Convert" + vmConvertButton: 'Convertir', + + // Original text: "Xen settings" + xenSettingsLabel: 'Configuraciones de Xen', + + // Original text: "Guest OS" + guestOsLabel: 'SO Invitado', + + // Original text: "Misc" + miscLabel: 'Varuis', + + // Original text: "UUID" + uuid: 'UUID', + + // Original text: "Virtualization mode" + virtualizationMode: 'Modo de virtualización', + + // Original text: "CPU weight" + cpuWeightLabel: 'Peso de CPU', + + // Original text: "Default ({value, number})" + defaultCpuWeight: 'Por defecto', + + // Original text: 'CPU cap' + cpuCapLabel: undefined, + + // Original text: 'Default ({value, number})' + defaultCpuCap: undefined, + + // Original text: "PV args" + pvArgsLabel: 'Argumentos PV', + + // Original text: "Xen tools status" + xenToolsStatus: 'Estado de Xen Tools', + + // Original text: "{status}" + xenToolsStatusValue: '{status}', + + // Original text: "OS name" + osName: 'Nombre del OS', + + // Original text: "OS kernel" + osKernel: 'Kernel del OS', + + // Original text: "Auto power on" + autoPowerOn: 'Auto encendido', + + // Original text: "HA" + ha: 'HA', + + // Original text: 'Affinity host' + vmAffinityHost: undefined, + + // Original text: 'VGA' + vmVga: undefined, + + // Original text: 'Video RAM' + vmVideoram: undefined, + + // Original text: 'None' + noAffinityHost: undefined, + + // Original text: "Original template" + originalTemplate: 'Plantilla original', + + // Original text: "Unknown" + unknownOsName: 'Desconocido', + + // Original text: "Unknown" + unknownOsKernel: 'Desconocido', + + // Original text: "Unknown" + unknownOriginalTemplate: 'Desconocido', + + // Original text: "VM limits" + vmLimitsLabel: 'Límites de VM', + + // Original text: "CPU limits" + vmCpuLimitsLabel: 'Límites de CPU', + + // Original text: 'Topology' + vmCpuTopology: undefined, + + // Original text: 'Default behavior' + vmChooseCoresPerSocket: undefined, + + // Original text: '{nSockets, number} socket{nSockets, plural, one {} other {s}} with {nCores, number} core{nCores, plural, one {} other {s}} per socket' + vmCoresPerSocket: undefined, + + // Original text: 'Incorrect cores per socket value' + vmCoresPerSocketIncorrectValue: undefined, + + // Original text: 'Please change the selected value to fix it.' + vmCoresPerSocketIncorrectValueSolution: undefined, + + // Original text: "Memory limits (min/max)" + vmMemoryLimitsLabel: 'Límites de memoria (min/max)', + + // Original text: "vCPUs max:" + vmMaxVcpus: 'Max vCPUs:', + + // Original text: "Memory max:" + vmMaxRam: 'Max Memoria:', + + // Original text: "Long click to add a name" + vmHomeNamePlaceholder: 'Click largo para poner el nombre', + + // Original text: "Long click to add a description" + vmHomeDescriptionPlaceholder: 'Click largo para poner la descripción', + + // Original text: "Click to add a name" + vmViewNamePlaceholder: 'Click para definir el nombre', + + // Original text: "Click to add a description" + vmViewDescriptionPlaceholder: 'Click para definir la descripción', + + // Original text: 'Click to add a name' + templateHomeNamePlaceholder: undefined, + + // Original text: 'Click to add a description' + templateHomeDescriptionPlaceholder: undefined, + + // Original text: 'Delete template' + templateDelete: undefined, + + // Original text: 'Delete VM template{templates, plural, one {} other {s}}' + templateDeleteModalTitle: undefined, + + // Original text: 'Are you sure you want to delete {templates, plural, one {this} other {these}} template{templates, plural, one {} other {s}}?' + templateDeleteModalBody: undefined, + + // Original text: "Pool{pools, plural, one {} other {s}}" + poolPanel: 'Pool{pools, plural, one {} other {s}}', + + // Original text: "Host{hosts, plural, one {} other {s}}" + hostPanel: 'Host{hosts, plural, one {} other {s}}', + + // Original text: "VM{vms, plural, one {} other {s}}" + vmPanel: 'VM{vms, plural, one {} other {s}}', + + // Original text: "RAM Usage:" + memoryStatePanel: 'Uso de RAM', + + // Original text: "CPUs Usage" + cpuStatePanel: 'Uso de CPU', + + // Original text: "VMs Power state" + vmStatePanel: 'Estado alimentación de las VMs', + + // Original text: "Pending tasks" + taskStatePanel: 'Tareas pendientes', + + // Original text: "Users" + usersStatePanel: 'Usuarios', + + // Original text: "Storage state" + srStatePanel: 'Estado del almacenamiento', + + // Original text: "{usage} (of {total})" + ofUsage: 'de', + + // Original text: "No storage" + noSrs: 'Sin almacenamiento', + + // Original text: "Name" + srName: 'Nombre', + + // Original text: "Pool" + srPool: 'Pool', + + // Original text: "Host" + srHost: 'Host', + + // Original text: "Type" + srFormat: 'Tipo', + + // Original text: "Size" + srSize: 'Tamaño', + + // Original text: "Usage" + srUsage: 'Uso', + + // Original text: "used" + srUsed: 'Usado', + + // Original text: "free" + srFree: 'libre', + + // Original text: "Storage Usage" + srUsageStatePanel: 'Uso del almacenamiento', + + // Original text: "Top 5 SR Usage (in %)" + srTopUsageStatePanel: 'Top 5 de uso de SR (en %)', + + // Original text: '{running, number} running ({halted, number} halted)' + vmsStates: undefined, + + // Original text: 'Clear selection' + dashboardStatsButtonRemoveAll: undefined, + + // Original text: 'Add all hosts' + dashboardStatsButtonAddAllHost: undefined, + + // Original text: 'Add all VMs' + dashboardStatsButtonAddAllVM: undefined, + + // Original text: "{value} {date, date, medium}" + weekHeatmapData: '{value} {date, date, medium}', + + // Original text: "No data." + weekHeatmapNoData: 'Sin datos.', + + // Original text: "Weekly Heatmap" + weeklyHeatmap: 'Heatmap Semanal', + + // Original text: "Weekly Charts" + weeklyCharts: 'Gráficos Semanales', + + // Original text: "Synchronize scale:" + weeklyChartsScaleInfo: 'Sincronizar escala', + + // Original text: "Stats error" + statsDashboardGenericErrorTitle: 'Error de stats', + + // Original text: "There is no stats available for:" + statsDashboardGenericErrorMessage: 'No hay stats disponibles para:', + + // Original text: "No selected metric" + noSelectedMetric: 'Métrica no seleccionada', + + // Original text: "Select" + statsDashboardSelectObjects: 'Seleccionar', + + // Original text: "Loading…" + metricsLoading: 'Cargando…', + + // Original text: "Coming soon!" + comingSoon: '¡Próximamente!', + + // Original text: "Orphaned snapshot VDIs" + orphanedVdis: 'VIDs huérfanos', + + // Original text: "Orphaned VMs snapshot" + orphanedVms: 'VMs huérfanas', + + // Original text: "No orphans" + noOrphanedObject: 'Sin huérfanos', + + // Original text: "Remove all orphaned snapshot VDIs" + removeAllOrphanedObject: 'Eliminar todos los VDIs huérfanos', + + // Original text: 'VDIs attached to Control Domain' + vdisOnControlDomain: undefined, + + // Original text: "Name" + vmNameLabel: 'Nombre', + + // Original text: "Description" + vmNameDescription: 'Descripción', + + // Original text: "Resident on" + vmContainer: 'Residente en', + + // Original text: "Alarms" + alarmMessage: 'Alarmas', + + // Original text: "No alarms" + noAlarms: 'Sin alarmas', + + // Original text: "Date" + alarmDate: 'Fecha', + + // Original text: "Content" + alarmContent: 'Contenido', + + // Original text: "Issue on" + alarmObject: 'Producido el', + + // Original text: "Pool" + alarmPool: 'Pool', + + // Original text: "Remove all alarms" + alarmRemoveAll: 'Eliminar todas las alarmas', + + // Original text: '{used}% used ({free} left)' + spaceLeftTooltip: undefined, + + // Original text: "Create a new VM on {select}" + newVmCreateNewVmOn: 'Crear una nueva VM en {pool}', + + // Original text: 'Create a new VM on {select1} or {select2}' + newVmCreateNewVmOn2: undefined, + + // Original text: 'You have no permission to create a VM' + newVmCreateNewVmNoPermission: undefined, + + // Original text: "Infos" + newVmInfoPanel: 'Información', + + // Original text: "Name" + newVmNameLabel: 'Nombre', + + // Original text: "Template" + newVmTemplateLabel: 'Plantilla', + + // Original text: "Description" + newVmDescriptionLabel: 'Descripción', + + // Original text: "Performances" + newVmPerfPanel: 'Rendimiento', + + // Original text: "vCPUs" + newVmVcpusLabel: 'vCPUs', + + // Original text: "RAM" + newVmRamLabel: 'RAM', + + // Original text: 'Static memory max' + newVmStaticMaxLabel: undefined, + + // Original text: 'Dynamic memory min' + newVmDynamicMinLabel: undefined, + + // Original text: 'Dynamic memory max' + newVmDynamicMaxLabel: undefined, + + // Original text: "Install settings" + newVmInstallSettingsPanel: 'Opciones de instalación', + + // Original text: "ISO/DVD" + newVmIsoDvdLabel: 'ISO/DVD', + + // Original text: "Network" + newVmNetworkLabel: 'Red', + + // Original text: 'e.g: http://httpredir.debian.org/debian' + newVmInstallNetworkPlaceHolder: undefined, + + // Original text: "PV Args" + newVmPvArgsLabel: 'Detalles del PV', + + // Original text: "PXE" + newVmPxeLabel: 'PXE', + + // Original text: "Interfaces" + newVmInterfacesPanel: 'Interfaces', + + // Original text: "MAC" + newVmMacLabel: 'MAC', + + // Original text: "Add interface" + newVmAddInterface: 'Añadir interface', + + // Original text: "Disks" + newVmDisksPanel: 'Discos', + + // Original text: "SR" + newVmSrLabel: 'SR', + + // Original text: "Size" + newVmSizeLabel: 'Tamaño', + + // Original text: "Add disk" + newVmAddDisk: 'Añadir disco', + + // Original text: "Summary" + newVmSummaryPanel: 'Sumario', + + // Original text: "Create" + newVmCreate: 'Crear', + + // Original text: "Reset" + newVmReset: 'Reiniciar', + + // Original text: "Select template" + newVmSelectTemplate: 'Elegir plantilla', + + // Original text: "SSH key" + newVmSshKey: 'Clave SSH', + + // Original text: "Config drive" + newVmConfigDrive: 'Config drive', + + // Original text: "Custom config" + newVmCustomConfig: 'Configuración personalizada', + + // Original text: "Boot VM after creation" + newVmBootAfterCreate: 'Arrancar la VM tras crearla', + + // Original text: "Auto-generated if empty" + newVmMacPlaceholder: 'Auto generada si se deja vacío', + + // Original text: "CPU weight" + newVmCpuWeightLabel: 'Peso de CPU', + + // Original text: 'Default: {value, number}' + newVmDefaultCpuWeight: undefined, + + // Original text: 'CPU cap' + newVmCpuCapLabel: undefined, + + // Original text: 'Default: {value, number}' + newVmDefaultCpuCap: undefined, + + // Original text: "Cloud config" + newVmCloudConfig: 'Cloud config', + + // Original text: "Create VMs" + newVmCreateVms: 'VMs creadas', + + // Original text: "Are you sure you want to create {nbVms, number} VMs?" + newVmCreateVmsConfirm: '¿Estás seguro de querer crear {nbVms, number} VMs?', + + // Original text: "Multiple VMs:" + newVmMultipleVms: 'Múltiple VMs:', + + // Original text: 'Select a resource set:' + newVmSelectResourceSet: undefined, + + // Original text: 'Name pattern:' + newVmMultipleVmsPattern: undefined, + + // Original text: 'e.g.: \\{name\\}_%' + newVmMultipleVmsPatternPlaceholder: undefined, + + // Original text: 'First index:' + newVmFirstIndex: undefined, + + // Original text: 'Recalculate VMs number' + newVmNumberRecalculate: undefined, + + // Original text: 'Refresh VMs name' + newVmNameRefresh: undefined, + + // Original text: 'Affinity host' + newVmAffinityHost: undefined, + + // Original text: 'Advanced' + newVmAdvancedPanel: undefined, + + // Original text: 'Show advanced settings' + newVmShowAdvanced: undefined, + + // Original text: 'Hide advanced settings' + newVmHideAdvanced: undefined, + + // Original text: 'Share this VM' + newVmShare: undefined, + + // Original text: "Resource sets" + resourceSets: 'Conjunto de recursos', + + // Original text: "No resource sets." + noResourceSets: 'No hay conjuntos de recursos', + + // Original text: 'Loading resource sets' + loadingResourceSets: undefined, + + // Original text: "Resource set name" + resourceSetName: 'Nombre del conjunto de recursos', + + // Original text: 'Recompute all limits' + recomputeResourceSets: undefined, + + // Original text: "Save" + saveResourceSet: 'Guardar', + + // Original text: "Reset" + resetResourceSet: 'Reiniciar', + + // Original text: "Edit" + editResourceSet: 'Editar', + + // Original text: "Delete" + deleteResourceSet: 'Bprrar', + + // Original text: "Delete resource set" + deleteResourceSetWarning: 'Borrar conjunto de recursos', + + // Original text: "Are you sure you want to delete this resource set?" + deleteResourceSetQuestion: + '¿Estás seguro de querer borrar este conjunto de recursos?', + + // Original text: "Missing objects:" + resourceSetMissingObjects: 'Objetos perdidos:', + + // Original text: "vCPUs" + resourceSetVcpus: 'vCPUs', + + // Original text: "Memory" + resourceSetMemory: 'Memoria', + + // Original text: "Storage" + resourceSetStorage: 'Almacenamiento', + + // Original text: "Unknown" + unknownResourceSetValue: 'Desconocido', + + // Original text: "Available hosts" + availableHosts: 'Hosts disponibles', + + // Original text: "Excluded hosts" + excludedHosts: 'Hosts excluídos', + + // Original text: "No hosts available." + noHostsAvailable: 'No hay hosts disponibles', + + // Original text: "VMs created from this resource set shall run on the following hosts." + availableHostsDescription: + 'Las VMs creadas con este conjunto de recursos correrán en los siguientes hosts.', + + // Original text: "Maximum CPUs" + maxCpus: 'Máximas CPUs', + + // Original text: "Maximum RAM (GiB)" + maxRam: 'Máxima RAM (GiB)', + + // Original text: "Maximum disk space" + maxDiskSpace: 'Máximo espacio en disco', + + // Original text: 'IP pool' + ipPool: undefined, + + // Original text: 'Quantity' + quantity: undefined, + + // Original text: "No limits." + noResourceSetLimits: 'Sin límites', + + // Original text: "Total:" + totalResource: 'Total:', + + // Original text: "Remaining:" + remainingResource: 'Restante:', + + // Original text: "Used:" + usedResource: 'Utilizado:', + + // Original text: 'New' + resourceSetNew: undefined, + + // Original text: "Try dropping some VMs files here, or click to select VMs to upload. Accept only .xva/.ova files." + importVmsList: + 'Haz drag & drop de backups aquí, o haz click para seleccionar qué backups subir. Sólo se aceptan ficheros .xva', + + // Original text: "No selected VMs." + noSelectedVms: 'No se han seleccionado VMs', + + // Original text: "To Pool:" + vmImportToPool: 'Al Pool:', + + // Original text: "To SR:" + vmImportToSr: 'al SR:', + + // Original text: "VMs to import" + vmsToImport: 'VMs para importar', + + // Original text: "Reset" + importVmsCleanList: 'Reiniciar', + + // Original text: "VM import success" + vmImportSuccess: 'Importación de VM satisfactoria', + + // Original text: "VM import failed" + vmImportFailed: 'Falló la importación de VM', + + // Original text: "Import starting…" + startVmImport: 'Comenzando importación…', + + // Original text: "Export starting…" + startVmExport: 'Comenzando export…', + + // Original text: 'N CPUs' + nCpus: undefined, + + // Original text: 'Memory' + vmMemory: undefined, + + // Original text: 'Disk {position} ({capacity})' + diskInfo: undefined, + + // Original text: 'Disk description' + diskDescription: undefined, + + // Original text: 'No disks.' + noDisks: undefined, + + // Original text: 'No networks.' + noNetworks: undefined, + + // Original text: 'Network {name}' + networkInfo: undefined, + + // Original text: 'No description available' + noVmImportErrorDescription: undefined, + + // Original text: 'Error:' + vmImportError: undefined, + + // Original text: '{type} file:' + vmImportFileType: undefined, + + // Original text: 'Please to check and/or modify the VM configuration.' + vmImportConfigAlert: undefined, + + // Original text: "No pending tasks" + noTasks: 'No hay tareas pendientes', + + // Original text: "Currently, there are not any pending XenServer tasks" + xsTasks: 'Ahora mismo no hay tareas de XenServer pendientes', + + // Original text: 'Schedules' + backupSchedules: undefined, + + // Original text: 'Get remote' + getRemote: undefined, + + // Original text: "List Remote" + listRemote: 'Listar backups remotos', + + // Original text: "simple" + simpleBackup: 'simple', + + // Original text: "delta" + delta: 'diferencial', + + // Original text: "Restore Backups" + restoreBackups: 'Restaurar backups', + + // Original text: 'Click on a VM to display restore options' + restoreBackupsInfo: undefined, + + // Original text: 'Only the files of Delta Backup which are not on a SMB remote can be restored' + restoreDeltaBackupsInfo: undefined, + + // Original text: "Enabled" + remoteEnabled: 'activado', + + // Original text: "Error" + remoteError: 'error', + + // Original text: "No backup available" + noBackup: 'No hay backups disponibles', + + // Original text: "VM Name" + backupVmNameColumn: 'Nombre VM', + + // Original text: 'Tags' + backupTags: undefined, + + // Original text: "Last Backup" + lastBackupColumn: 'Último backup', + + // Original text: "Available Backups" + availableBackupsColumn: 'Backups disponibles', + + // Original text: 'Missing parameters' + backupRestoreErrorTitle: undefined, + + // Original text: 'Choose a SR and a backup' + backupRestoreErrorMessage: undefined, + + // Original text: 'Select default SR…' + backupRestoreSelectDefaultSr: undefined, + + // Original text: 'Choose a SR for each VDI' + backupRestoreChooseSrForEachVdis: undefined, + + // Original text: 'VDI' + backupRestoreVdiLabel: undefined, + + // Original text: 'SR' + backupRestoreSrLabel: undefined, + + // Original text: 'Display backups' + displayBackup: undefined, + + // Original text: "Import VM" + importBackupTitle: 'Importar VM', + + // Original text: "Starting your backup import" + importBackupMessage: 'Comenzando importación del backup', + + // Original text: 'VMs to backup' + vmsToBackup: undefined, + + // Original text: 'List remote backups' + listRemoteBackups: undefined, + + // Original text: 'Restore backup files' + restoreFiles: undefined, + + // Original text: 'Invalid options' + restoreFilesError: undefined, + + // Original text: 'Restore file from {name}' + restoreFilesFromBackup: undefined, + + // Original text: 'Select a backup…' + restoreFilesSelectBackup: undefined, + + // Original text: 'Select a disk…' + restoreFilesSelectDisk: undefined, + + // Original text: 'Select a partition…' + restoreFilesSelectPartition: undefined, + + // Original text: 'Folder path' + restoreFilesSelectFolderPath: undefined, + + // Original text: 'Select a file…' + restoreFilesSelectFiles: undefined, + + // Original text: 'Content not found' + restoreFileContentNotFound: undefined, + + // Original text: 'No files selected' + restoreFilesNoFilesSelected: undefined, + + // Original text: 'Selected files ({files}):' + restoreFilesSelectedFiles: undefined, + + // Original text: 'Error while scanning disk' + restoreFilesDiskError: undefined, + + // Original text: "Select all this folder's files" + restoreFilesSelectAllFiles: undefined, + + // Original text: 'Unselect all files' + restoreFilesUnselectAll: undefined, + + // Original text: 'Emergency shutdown Host{nHosts, plural, one {} other {s}}' + emergencyShutdownHostsModalTitle: undefined, + + // Original text: 'Are you sure you want to shutdown {nHosts, number} Host{nHosts, plural, one {} other {s}}?' + emergencyShutdownHostsModalMessage: undefined, + + // Original text: 'Shutdown host' + stopHostModalTitle: undefined, + + // Original text: "This will shutdown your host. Do you want to continue? If it's the pool master, your connection to the pool will be lost" + stopHostModalMessage: undefined, + + // Original text: 'Add host' + addHostModalTitle: undefined, + + // Original text: 'Are you sure you want to add {host} to {pool}?' + addHostModalMessage: undefined, + + // Original text: 'Restart host' + restartHostModalTitle: undefined, + + // Original text: 'This will restart your host. Do you want to continue?' + restartHostModalMessage: undefined, + + // Original text: 'Restart Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}' + restartHostsAgentsModalTitle: undefined, + + // Original text: 'Are you sure you want to restart {nHosts, number} Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}?' + restartHostsAgentsModalMessage: undefined, + + // Original text: 'Restart Host{nHosts, plural, one {} other {s}}' + restartHostsModalTitle: undefined, + + // Original text: 'Are you sure you want to restart {nHosts, number} Host{nHosts, plural, one {} other {s}}?' + restartHostsModalMessage: undefined, + + // Original text: "Start VM{vms, plural, one {} other {s}}" + startVmsModalTitle: 'Arrancar VM{vms, plural, one {} other {s}}', + + // Original text: 'Start a copy' + cloneAndStartVM: undefined, + + // Original text: 'Force start' + forceStartVm: undefined, + + // Original text: 'Forbidden operation' + forceStartVmModalTitle: undefined, + + // Original text: 'Start operation for this vm is blocked.' + blockedStartVmModalMessage: undefined, + + // Original text: 'Forbidden operation start for {nVms, number} vm{nVms, plural, one {} other {s}}.' + blockedStartVmsModalMessage: undefined, + + // Original text: "Are you sure you want to start {vms, number} VM{vms, plural, one {} other {s}}?" + startVmsModalMessage: + '¿Estás seguro de querar arrancar {vms} VM{vms, plural, one {} other {s}}?', + + // Original text: '{nVms, number} vm{nVms, plural, one {} other {s}} are failed. Please see your logs to get more information' + failedVmsErrorMessage: undefined, + + // Original text: 'Start failed' + failedVmsErrorTitle: undefined, + + // Original text: 'Stop Host{nHosts, plural, one {} other {s}}' + stopHostsModalTitle: undefined, + + // Original text: 'Are you sure you want to stop {nHosts, number} Host{nHosts, plural, one {} other {s}}?' + stopHostsModalMessage: undefined, + + // Original text: "Stop VM{vms, plural, one {} other {s}}" + stopVmsModalTitle: 'Parar VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to stop {vms, number} VM{vms, plural, one {} other {s}}?" + stopVmsModalMessage: + '¿Estás seguro de querer parar {vms, number} VM{vms, plural, one {} other {s}}?', + + // Original text: 'Restart VM' + restartVmModalTitle: undefined, + + // Original text: 'Are you sure you want to restart {name}?' + restartVmModalMessage: undefined, + + // Original text: 'Stop VM' + stopVmModalTitle: undefined, + + // Original text: 'Are you sure you want to stop {name}?' + stopVmModalMessage: undefined, + + // Original text: "Restart VM{vms, plural, one {} other {s}}" + restartVmsModalTitle: 'Reiniciar VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to restart {vms, number} VM{vms, plural, one {} other {s}}?" + restartVmsModalMessage: + '¿Estás seguro de querer reiniciar {vms, number} VM{vms, plural, one {} other {s}}?', + + // Original text: "Snapshot VM{vms, plural, one {} other {s}}" + snapshotVmsModalTitle: 'Snapshot VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to snapshot {vms, number} VM{vms, plural, one {} other {s}}?" + snapshotVmsModalMessage: + '¿Estás seguro de querer hacer snapshot de {vms, number} VM{vms, plural, one {} other {s}}?', + + // Original text: "Delete VM{vms, plural, one {} other {s}}" + deleteVmsModalTitle: 'Borrar VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to delete {vms, number} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED" + deleteVmsModalMessage: + '¿Estás seguro de querar borrar {vms, number} VM{vms, plural, one {} other {s}}? TODOS SUS DISCOS SERAN ELIMINADOS', + + // Original text: "Delete VM" + deleteVmModalTitle: 'Borrar VM', + + // Original text: "Are you sure you want to delete this VM? ALL VM DISKS WILL BE REMOVED" + deleteVmModalMessage: + '¿Estás seguro de querer borrar esta VM? TODOS SUS DISCOS SERAN ELIMINADOS', + + // Original text: "Migrate VM" + migrateVmModalTitle: 'Migrar VM', + + // Original text: 'Select a destination host:' + migrateVmSelectHost: undefined, + + // Original text: 'Select a migration network:' + migrateVmSelectMigrationNetwork: undefined, + + // Original text: 'For each VIF, select a network:' + migrateVmSelectNetworks: undefined, + + // Original text: 'Select a destination SR:' + migrateVmsSelectSr: undefined, + + // Original text: 'Select a destination SR for local disks:' + migrateVmsSelectSrIntraPool: undefined, + + // Original text: 'Select a network on which to connect each VIF:' + migrateVmsSelectNetwork: undefined, + + // Original text: 'Smart mapping' + migrateVmsSmartMapping: undefined, + + // Original text: 'VIF' + migrateVmVif: undefined, + + // Original text: 'Network' + migrateVmNetwork: undefined, + + // Original text: 'No target host' + migrateVmNoTargetHost: undefined, + + // Original text: 'A target host is required to migrate a VM' + migrateVmNoTargetHostMessage: undefined, + + // Original text: 'No default SR' + migrateVmNoDefaultSrError: undefined, + + // Original text: 'Default SR not connected to host' + migrateVmNotConnectedDefaultSrError: undefined, + + // Original text: 'For each VDI, select an SR:' + chooseSrForEachVdisModalSelectSr: undefined, + + // Original text: 'Select main SR…' + chooseSrForEachVdisModalMainSr: undefined, + + // Original text: 'VDI' + chooseSrForEachVdisModalVdiLabel: undefined, + + // Original text: 'SR*' + chooseSrForEachVdisModalSrLabel: undefined, + + // Original text: '* optional' + chooseSrForEachVdisModalOptionalEntry: undefined, + + // Original text: 'Delete VDI' + deleteVdiModalTitle: undefined, + + // Original text: 'Are you sure you want to delete this disk? ALL DATA ON THIS DISK WILL BE LOST' + deleteVdiModalMessage: undefined, + + // Original text: 'Revert your VM' + revertVmModalTitle: undefined, + + // Original text: 'Delete snapshot' + deleteSnapshotModalTitle: undefined, + + // Original text: 'Are you sure you want to delete this snapshot?' + deleteSnapshotModalMessage: undefined, + + // Original text: 'Are you sure you want to revert this VM to the snapshot state? This operation is irreversible.' + revertVmModalMessage: undefined, + + // Original text: 'Snapshot before' + revertVmModalSnapshotBefore: undefined, + + // Original text: "Import a {name} Backup" + importBackupModalTitle: 'Importar un backup {name}', + + // Original text: "Start VM after restore" + importBackupModalStart: 'Arrancar la VM tras la restauración', + + // Original text: "Select your backup…" + importBackupModalSelectBackup: 'Elige el backup…', + + // Original text: "Are you sure you want to remove all orphaned snapshot VDIs?" + removeAllOrphanedModalWarning: + '¿Estás seguro de querer borrar todos los VDIs huérfanos?', + + // Original text: "Remove all logs" + removeAllLogsModalTitle: 'Borrar todos los logs', + + // Original text: "Are you sure you want to remove all logs?" + removeAllLogsModalWarning: '¿Estás seguro de querar borrar todos los logs?', + + // Original text: "This operation is definitive." + definitiveMessageModal: 'Esta operación es definitiva', + + // Original text: "Previous SR Usage" + existingSrModalTitle: 'Uso anterior del SR', + + // Original text: "This path has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingSrModalText: + 'Esta ruta ya ha sido utilizada anteriormente como Almacenamiento por un host XenServer. Todos los datos existentes se perderán si continuas con la creación del SR.', + + // Original text: "Previous LUN Usage" + existingLunModalTitle: 'Uso anterior de la LUN', + + // Original text: "This LUN has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingLunModalText: + 'Esta LUN ya ha sido utilizada anteriormente como Almacenamiento por un host XenServer. Todos los datos existentes se perderán si continuas con la creación del SR.', + + // Original text: "Replace current registration?" + alreadyRegisteredModal: '¿Reemplazar el registro actual?', + + // Original text: "Your XO appliance is already registered to {email}, do you want to forget and replace this registration ?" + alreadyRegisteredModalText: + 'Tu XOA ya está registrado en {email}, ¿quieres olvidar y reemplazar este registro?', + + // Original text: "Ready for trial?" + trialReadyModal: '¿Preparado para el periodo de prueba?', + + // Original text: "During the trial period, XOA need to have a working internet connection. This limitation does not apply for our paid plans!" + trialReadyModalText: + 'Durante el periodo de prueba, XOA necesita conexión a Internet. Esta limitación no aplica a los planes de pago', + + // Original text: 'Label' + serverLabel: undefined, + + // Original text: "Host" + serverHost: 'Host', + + // Original text: "Username" + serverUsername: 'Usuario', + + // Original text: "Password" + serverPassword: 'Clave', + + // Original text: "Action" + serverAction: 'Acción', + + // Original text: "Read Only" + serverReadOnly: 'Sólo lectura', + + // Original text: 'Unauthorized Certificates' + serverUnauthorizedCertificates: undefined, + + // Original text: 'Allow Unauthorized Certificates' + serverAllowUnauthorizedCertificates: undefined, + + // Original text: "Enable it if your certificate is rejected, but it's not recommended because your connection will not be secured." + serverUnauthorizedCertificatesInfo: undefined, + + // Original text: 'Disconnect server' + serverDisconnect: undefined, + + // Original text: 'username' + serverPlaceHolderUser: undefined, + + // Original text: 'password' + serverPlaceHolderPassword: undefined, + + // Original text: 'address[:port]' + serverPlaceHolderAddress: undefined, + + // Original text: 'label' + serverPlaceHolderLabel: undefined, + + // Original text: 'Connect' + serverConnect: undefined, + + // Original text: 'Error' + serverError: undefined, + + // Original text: 'Adding server failed' + serverAddFailed: undefined, + + // Original text: 'Status' + serverStatus: undefined, + + // Original text: 'Connection failed. Click for more information.' + serverConnectionFailed: undefined, + + // Original text: 'Connecting…' + serverConnecting: undefined, + + // Original text: 'Connected' + serverConnected: undefined, + + // Original text: 'Disconnected' + serverDisconnected: undefined, + + // Original text: 'Authentication error' + serverAuthFailed: undefined, + + // Original text: 'Unknown error' + serverUnknownError: undefined, + + // Original text: 'Invalid self-signed certificate' + serverSelfSignedCertError: undefined, + + // Original text: 'Do you want to accept self-signed certificate for this server even though it would decrease security?' + serverSelfSignedCertQuestion: undefined, + + // Original text: "Copy VM" + copyVm: 'Copiar VM', + + // Original text: "Are you sure you want to copy this VM to {SR}?" + copyVmConfirm: '¿Estás seguro de querer copiar esta VM a {SR}?', + + // Original text: "Name" + copyVmName: 'Nombre', + + // Original text: 'Name pattern' + copyVmNamePattern: undefined, + + // Original text: "If empty: name of the copied VM" + copyVmNamePlaceholder: 'Vacío: nombre de la VM copiada', + + // Original text: 'e.g.: "\\{name\\}_COPY"' + copyVmNamePatternPlaceholder: undefined, + + // Original text: "Select SR" + copyVmSelectSr: 'Elegir SR', + + // Original text: "Use compression" + copyVmCompress: 'Usar compresión', + + // Original text: 'No target SR' + copyVmsNoTargetSr: undefined, + + // Original text: 'A target SR is required to copy a VM' + copyVmsNoTargetSrMessage: undefined, + + // Original text: 'Detach host' + detachHostModalTitle: undefined, + + // Original text: 'Are you sure you want to detach {host} from its pool? THIS WILL REMOVE ALL VMs ON ITS LOCAL STORAGE AND REBOOT THE HOST.' + detachHostModalMessage: undefined, + + // Original text: 'Detach' + detachHost: undefined, + + // Original text: 'Forget host' + forgetHostModalTitle: undefined, + + // Original text: "Are you sure you want to forget {host} from its pool? Be sure this host can't be back online, or use detach instead." + forgetHostModalMessage: undefined, + + // Original text: 'Forget' + forgetHost: undefined, + + // Original text: "Create network" + newNetworkCreate: 'Crear red', + + // Original text: 'Create bonded network' + newBondedNetworkCreate: undefined, + + // Original text: "Interface" + newNetworkInterface: 'Interface', + + // Original text: "Name" + newNetworkName: 'Nombre', + + // Original text: "Description" + newNetworkDescription: 'Descripción', + + // Original text: "VLAN" + newNetworkVlan: 'VLAN', + + // Original text: "No VLAN if empty" + newNetworkDefaultVlan: 'Sin VLAN si se deja vacío', + + // Original text: "MTU" + newNetworkMtu: 'MTU', + + // Original text: "Default: 1500" + newNetworkDefaultMtu: 'Por defecto: 1500', + + // Original text: 'Name required' + newNetworkNoNameErrorTitle: undefined, + + // Original text: 'A name is required to create a network' + newNetworkNoNameErrorMessage: undefined, + + // Original text: 'Bond mode' + newNetworkBondMode: undefined, + + // Original text: "Delete network" + deleteNetwork: 'Borrar red', + + // Original text: "Are you sure you want to delete this network?" + deleteNetworkConfirm: '¿Estás seguro de querer borrar esta red?', + + // Original text: 'This network is currently in use' + networkInUse: undefined, + + // Original text: 'Bonded' + pillBonded: undefined, + + // Original text: 'Host' + addHostSelectHost: undefined, + + // Original text: 'No host' + addHostNoHost: undefined, + + // Original text: 'No host selected to be added' + addHostNoHostMessage: undefined, + + // Original text: "Xen Orchestra" + xenOrchestra: 'Xen Orchestra', + + // Original text: "Xen Orchestra server" + xenOrchestraServer: 'servidor', + + // Original text: "Xen Orchestra web client" + xenOrchestraWeb: 'cliente web', + + // Original text: "No pro support provided!" + noProSupport: '¡Sin soporte Pro!', + + // Original text: "Use in production at your own risks" + noProductionUse: 'Usar en producción bajo tu propia cuenta y riesgo', + + // Original text: "You can download our turnkey appliance at {website}"" + downloadXoaFromWebsite: 'Puedes descargar nuestro appliance en {website}', + + // Original text: "Bug Tracker" + bugTracker: 'Bug Tracker', + + // Original text: "Issues? Report it!" + bugTrackerText: '¿Problemas? ¡Repórtalos!', + + // Original text: "Community" + community: 'Comunidad', + + // Original text: "Join our community forum!" + communityText: '¡Únete al foro de nuestra comunidad!', + + // Original text: "Free Trial for Premium Edition!" + freeTrial: 'Prueba gratis de la Edición Premium', + + // Original text: "Request your trial now!" + freeTrialNow: '¡Pide la prueba gratuíta ahora!', + + // Original text: "Any issue?" + issues: '¿Algún problema?', + + // Original text: "Problem? Contact us!" + issuesText: '¿Problemas? ¡Ponte en contacto con nosotros!', + + // Original text: "Documentation" + documentation: 'Documentación', + + // Original text: "Read our official doc" + documentationText: 'Lee nuestra documentación oficial', + + // Original text: "Pro support included" + proSupportIncluded: 'Soporte Pro incluído', + + // Original text: "Access your XO Account" + xoAccount: 'Entra en tu cuenta XO', + + // Original text: "Report a problem" + openTicket: 'Reportar un problema', + + // Original text: "Problem? Open a ticket!" + openTicketText: '¿Problemas? ¡Abre un ticket!', + + // Original text: "Upgrade needed" + upgradeNeeded: 'Actualización necesaria', + + // Original text: "Upgrade now!" + upgradeNow: '¡Actualiza ahora!', + + // Original text: "Or" + or: 'O', + + // Original text: "Try it for free!" + tryIt: '¡Pruébalo gratis!', + + // Original text: "This feature is available starting from {plan} Edition" + availableIn: + 'Esta característica está sólo disponible a partir de la Edición {plan}', + + // Original text: 'This feature is not available in your version, contact your administrator to know more.' + notAvailable: undefined, + + // Original text: 'Updates' + updateTitle: undefined, + + // Original text: "Registration" + registration: 'Registro', + + // Original text: "Trial" + trial: 'Prueba', + + // Original text: "Settings" + settings: 'Configuración', + + // Original text: 'Proxy settings' + proxySettings: undefined, + + // Original text: 'Host (myproxy.example.org)' + proxySettingsHostPlaceHolder: undefined, + + // Original text: 'Port (eg: 3128)' + proxySettingsPortPlaceHolder: undefined, + + // Original text: 'Username' + proxySettingsUsernamePlaceHolder: undefined, + + // Original text: 'Password' + proxySettingsPasswordPlaceHolder: undefined, + + // Original text: 'Your email account' + updateRegistrationEmailPlaceHolder: undefined, + + // Original text: 'Your password' + updateRegistrationPasswordPlaceHolder: undefined, + + // Original text: "Update" + update: 'Refrescar', + + // Original text: 'Refresh' + refresh: undefined, + + // Original text: "Upgrade" + upgrade: 'Actualizar', + + // Original text: "No updater available for Community Edition" + noUpdaterCommunity: 'No hay actualizador para la Edición Community', + + // Original text: "Please consider subscribe and try it with all features for free during 15 days on {link}."" + considerSubscribe: + 'Por favor plantéate la suscripción y pruébala con todas las características gratis durante 15 días {link}', + + // Original text: "Manual update could break your current installation due to dependencies issues, do it with caution" + noUpdaterWarning: + 'La actualización manual podría romper tu instalación actual debido a problemas de dependencias, hazlo con precaución', + + // Original text: "Current version:" + currentVersion: 'Versión actual', + + // Original text: "Register" + register: 'Registrar', + + // Original text: 'Edit registration' + editRegistration: undefined, + + // Original text: "Please, take time to register in order to enjoy your trial." + trialRegistration: + 'Por favor, regístrate para poder disfrutar del periodo de prueba', + + // Original text: "Start trial" + trialStartButton: 'Comenzar prueba', + + // Original text: "You can use a trial version until {date, date, medium}. Upgrade your appliance to get it." + trialAvailableUntil: + 'Puedes usar la versión de prueba hasta {date, date, medium}. Actualiza tu instalación para obtenerla.', + + // Original text: "Your trial has been ended. Contact us or downgrade to Free version" + trialConsumed: + 'Tu periodo de prueba ha terminado. Contacta con nosotros o vuelve a la Edición Libre', + + // Original text: "Your xoa-updater service appears to be down. Your XOA cannot run fully without reaching this service." + trialLocked: + 'Tu servicio xoa-updater parece estar caído. XOA no puede funcionar correctamente sin contactar con este servicio', + + // Original text: "No update information available" + noUpdateInfo: 'No hay información de actualización', + + // Original text: "Update information may be available" + waitingUpdateInfo: 'Podría haber información de actualización disponible', + + // Original text: "Your XOA is up-to-date" + upToDate: 'Tu XOA está al día', + + // Original text: "You need to update your XOA (new version is available)" + mustUpgrade: 'Necesitas actualizar tu XOA (hay disponible una nueva versión)', + + // Original text: "Your XOA is not registered for updates" + registerNeeded: 'Tu XOA no está registrado para recibir actualizaciones', + + // Original text: "Can't fetch update information" + updaterError: 'No se puede obtener la información de actualización', + + // Original text: 'Upgrade successful' + promptUpgradeReloadTitle: undefined, + + // Original text: 'Your XOA has successfully upgraded, and your browser must reload the application. Do you want to reload now ?' + promptUpgradeReloadMessage: undefined, + + // Original text: "Xen Orchestra from the sources" + disclaimerTitle: 'Xen Orchestra desde código fuente', + + // Original text: "You are using XO from the sources! That's great for a personal/non-profit usage." + disclaimerText1: + '¡Estás utilizando XO a partir del código fuente! Estupendo para un uso personal/sin ánimo de lucro', + + // Original text: "If you are a company, it's better to use it with our appliance + pro support included:" + disclaimerText2: + 'Si eres una empresa, es mejor utilizarlo con nuestro appliance con soporte Pro incluído', + + // Original text: "This version is not bundled with any support nor updates. Use it with caution for critical tasks." + disclaimerText3: + 'Esta versión no está creada para recibir soporte ni actualizaciones. Úsala con precaución para tareas críticas.', + + // Original text: "Connect PIF" + connectPif: 'Conectar PIF', + + // Original text: "Are you sure you want to connect this PIF?" + connectPifConfirm: '¿Estás seguro de querer conectar este PIF?', + + // Original text: "Disconnect PIF" + disconnectPif: 'Desconectar PIF', + + // Original text: "Are you sure you want to disconnect this PIF?" + disconnectPifConfirm: '¿Estás seguro de querer desconectar este PIF?', + + // Original text: "Delete PIF" + deletePif: 'Borrar PIF', + + // Original text: "Are you sure you want to delete this PIF?" + deletePifConfirm: '¿Estás seguro de querer borrar este PIF?', + + // Original text: 'Connected' + pifConnected: undefined, + + // Original text: 'Disconnected' + pifDisconnected: undefined, + + // Original text: 'Physically connected' + pifPhysicallyConnected: undefined, + + // Original text: 'Physically disconnected' + pifPhysicallyDisconnected: undefined, + + // Original text: 'Username' + username: undefined, + + // Original text: 'Password' + password: undefined, + + // Original text: 'Language' + language: undefined, + + // Original text: 'Old password' + oldPasswordPlaceholder: undefined, + + // Original text: 'New password' + newPasswordPlaceholder: undefined, + + // Original text: 'Confirm new password' + confirmPasswordPlaceholder: undefined, + + // Original text: 'Confirmation password incorrect' + confirmationPasswordError: undefined, + + // Original text: 'Password does not match the confirm password.' + confirmationPasswordErrorBody: undefined, + + // Original text: 'Password changed' + pwdChangeSuccess: undefined, + + // Original text: 'Your password has been successfully changed.' + pwdChangeSuccessBody: undefined, + + // Original text: 'Incorrect password' + pwdChangeError: undefined, + + // Original text: 'The old password provided is incorrect. Your password has not been changed.' + pwdChangeErrorBody: undefined, + + // Original text: 'OK' + changePasswordOk: undefined, + + // Original text: 'SSH keys' + sshKeys: undefined, + + // Original text: 'New SSH key' + newSshKey: undefined, + + // Original text: 'Delete' + deleteSshKey: undefined, + + // Original text: 'No SSH keys' + noSshKeys: undefined, + + // Original text: 'New SSH key' + newSshKeyModalTitle: undefined, + + // Original text: 'Invalid key' + sshKeyErrorTitle: undefined, + + // Original text: 'An SSH key requires both a title and a key.' + sshKeyErrorMessage: undefined, + + // Original text: 'Title' + title: undefined, + + // Original text: 'Key' + key: undefined, + + // Original text: 'Delete SSH key' + deleteSshKeyConfirm: undefined, + + // Original text: 'Are you sure you want to delete the SSH key {title}?' + deleteSshKeyConfirmMessage: undefined, + + // Original text: 'Others' + others: undefined, + + // Original text: 'Loading logs…' + loadingLogs: undefined, + + // Original text: 'User' + logUser: undefined, + + // Original text: 'Method' + logMethod: undefined, + + // Original text: 'Params' + logParams: undefined, + + // Original text: 'Message' + logMessage: undefined, + + // Original text: 'Error' + logError: undefined, + + // Original text: 'Display details' + logDisplayDetails: undefined, + + // Original text: 'Date' + logTime: undefined, + + // Original text: 'No stack trace' + logNoStackTrace: undefined, + + // Original text: 'No params' + logNoParams: undefined, + + // Original text: 'Delete log' + logDelete: undefined, + + // Original text: 'Delete all logs' + logDeleteAll: undefined, + + // Original text: 'Delete all logs' + logDeleteAllTitle: undefined, + + // Original text: 'Are you sure you want to delete all the logs?' + logDeleteAllMessage: undefined, + + // Original text: 'Click to enable' + logIndicationToEnable: undefined, + + // Original text: 'Click to disable' + logIndicationToDisable: undefined, + + // Original text: 'Report a bug' + reportBug: undefined, + + // Original text: 'Name' + ipPoolName: undefined, + + // Original text: 'IPs' + ipPoolIps: undefined, + + // Original text: 'IPs (e.g.: 1.0.0.12-1.0.0.17;1.0.0.23)' + ipPoolIpsPlaceholder: undefined, + + // Original text: 'Networks' + ipPoolNetworks: undefined, + + // Original text: 'No IP pools' + ipsNoIpPool: undefined, + + // Original text: 'Create' + ipsCreate: undefined, + + // Original text: 'Delete all IP pools' + ipsDeleteAllTitle: undefined, + + // Original text: 'Are you sure you want to delete all the IP pools?' + ipsDeleteAllMessage: undefined, + + // Original text: 'VIFs' + ipsVifs: undefined, + + // Original text: 'Not used' + ipsNotUsed: undefined, + + // Original text: 'unknown VIF' + ipPoolUnknownVif: undefined, + + // Original text: 'Name already exists' + ipPoolNameAlreadyExists: undefined, + + // Original text: 'Keyboard shortcuts' + shortcutModalTitle: undefined, + + // Original text: 'Global' + shortcut_XoApp: undefined, + + // Original text: 'Go to hosts list' + shortcut_GO_TO_HOSTS: undefined, + + // Original text: 'Go to pools list' + shortcut_GO_TO_POOLS: undefined, + + // Original text: 'Go to VMs list' + shortcut_GO_TO_VMS: undefined, + + // Original text: 'Go to SRs list' + shortcut_GO_TO_SRS: undefined, + + // Original text: 'Create a new VM' + shortcut_CREATE_VM: undefined, + + // Original text: 'Unfocus field' + shortcut_UNFOCUS: undefined, + + // Original text: 'Show shortcuts key bindings' + shortcut_HELP: undefined, + + // Original text: 'Home' + shortcut_Home: undefined, + + // Original text: 'Focus search bar' + shortcut_SEARCH: undefined, + + // Original text: 'Next item' + shortcut_NAV_DOWN: undefined, + + // Original text: 'Previous item' + shortcut_NAV_UP: undefined, + + // Original text: 'Select item' + shortcut_SELECT: undefined, + + // Original text: 'Open' + shortcut_JUMP_INTO: undefined, + + // Original text: 'VM' + settingsAclsButtonTooltipVM: undefined, + + // Original text: 'Hosts' + settingsAclsButtonTooltiphost: undefined, + + // Original text: 'Pool' + settingsAclsButtonTooltippool: undefined, + + // Original text: 'SR' + settingsAclsButtonTooltipSR: undefined, + + // Original text: 'Network' + settingsAclsButtonTooltipnetwork: undefined, + + // Original text: 'No config file selected' + noConfigFile: undefined, + + // Original text: 'Try dropping a config file here, or click to select a config file to upload.' + importTip: undefined, + + // Original text: 'Config' + config: undefined, + + // Original text: 'Import' + importConfig: undefined, + + // Original text: 'Config file successfully imported' + importConfigSuccess: undefined, + + // Original text: 'Error while importing config file' + importConfigError: undefined, + + // Original text: 'Export' + exportConfig: undefined, + + // Original text: 'Download current config' + downloadConfig: undefined, + + // Original text: 'No config import available for Community Edition' + noConfigImportCommunity: undefined, + + // Original text: 'Reconnect all hosts' + srReconnectAllModalTitle: undefined, + + // Original text: 'This will reconnect this SR to all its hosts.' + srReconnectAllModalMessage: undefined, + + // Original text: 'This will reconnect each selected SR to its host (local SR) or to every hosts of its pool (shared SR).' + srsReconnectAllModalMessage: undefined, + + // Original text: 'Disconnect all hosts' + srDisconnectAllModalTitle: undefined, + + // Original text: 'This will disconnect this SR from all its hosts.' + srDisconnectAllModalMessage: undefined, + + // Original text: 'This will disconnect each selected SR from its host (local SR) or from every hosts of its pool (shared SR).' + srsDisconnectAllModalMessage: undefined, + + // Original text: 'Forget SR' + srForgetModalTitle: undefined, + + // Original text: 'Forget selected SRs' + srsForgetModalTitle: undefined, + + // Original text: "Are you sure you want to forget this SR? VDIs on this storage won't be removed." + srForgetModalMessage: undefined, + + // Original text: "Are you sure you want to forget all the selected SRs? VDIs on these storages won't be removed." + srsForgetModalMessage: undefined, + + // Original text: 'Disconnected' + srAllDisconnected: undefined, + + // Original text: 'Partially connected' + srSomeConnected: undefined, + + // Original text: 'Connected' + srAllConnected: undefined, + + // Original text: 'XOSAN' + xosanTitle: undefined, + + // Original text: 'Xen Orchestra SAN SR' + xosanSrTitle: undefined, + + // Original text: 'Select local SRs (lvm)' + xosanAvailableSrsTitle: undefined, + + // Original text: 'Suggestions' + xosanSuggestions: undefined, + + // Original text: 'Name' + xosanName: undefined, + + // Original text: 'Host' + xosanHost: undefined, + + // Original text: 'Hosts' + xosanHosts: undefined, + + // Original text: 'Volume ID' + xosanVolumeId: undefined, + + // Original text: 'Size' + xosanSize: undefined, + + // Original text: 'Used space' + xosanUsedSpace: undefined, + + // Original text: 'XOSAN pack needs to be installed on each host of the pool.' + xosanNeedPack: undefined, + + // Original text: 'Install it now!' + xosanInstallIt: undefined, + + // Original text: 'Some hosts need their toolstack to be restarted before you can create an XOSAN' + xosanNeedRestart: undefined, + + // Original text: 'Restart toolstacks' + xosanRestartAgents: undefined, + + // Original text: 'Pool master is not running' + xosanMasterOffline: undefined, + + // Original text: 'Install XOSAN pack on {pool}' + xosanInstallPackTitle: undefined, + + // Original text: 'Select at least 2 SRs' + xosanSelect2Srs: undefined, + + // Original text: 'Layout' + xosanLayout: undefined, + + // Original text: 'Redundancy' + xosanRedundancy: undefined, + + // Original text: 'Capacity' + xosanCapacity: undefined, + + // Original text: 'Available space' + xosanAvailableSpace: undefined, + + // Original text: '* Can fail without data loss' + xosanDiskLossLegend: undefined, + + // Original text: 'Create' + xosanCreate: undefined, + + // Original text: 'Installing XOSAN. Please wait…' + xosanInstalling: undefined, + + // Original text: 'No XOSAN available for Community Edition' + xosanCommunity: undefined, + + // Original text: 'Install cloud plugin first' + xosanInstallCloudPlugin: undefined, + + // Original text: 'Load cloud plugin first' + xosanLoadCloudPlugin: undefined, + + // Original text: 'Loading…' + xosanLoading: undefined, + + // Original text: 'XOSAN is not available at the moment' + xosanNotAvailable: undefined, + + // Original text: 'Register for the XOSAN beta' + xosanRegisterBeta: undefined, + + // Original text: 'You have successfully registered for the XOSAN beta. Please wait until your request has been approved.' + xosanSuccessfullyRegistered: undefined, + + // Original text: 'Install XOSAN pack on these hosts:' + xosanInstallPackOnHosts: undefined, + + // Original text: 'Install {pack} v{version}?' + xosanInstallPack: undefined, + + // Original text: 'No compatible XOSAN pack found for your XenServer versions.' + xosanNoPackFound: undefined, + + // Original text: 'At least one of these version requirements must be satisfied by all the hosts in this pool:' + xosanPackRequirements: undefined, +} diff --git a/packages/xo-web/src/common/intl/locales/fr.js b/packages/xo-web/src/common/intl/locales/fr.js new file mode 100644 index 000000000..1dc61fcfd --- /dev/null +++ b/packages/xo-web/src/common/intl/locales/fr.js @@ -0,0 +1,3937 @@ +// See http://momentjs.com/docs/#/use-it/browserify/ +import 'moment/locale/fr' + +import reactIntlData from 'react-intl/locale-data/fr' +import { addLocaleData } from 'react-intl' +addLocaleData(reactIntlData) + +// =================================================================== + +export default { + // Original text: "{key}: {value}" + keyValue: '{key} : {value}', + + // Original text: "Connecting" + statusConnecting: 'Connexion…', + + // Original text: "Disconnected" + statusDisconnected: 'Déconnecté', + + // Original text: "Loading…" + statusLoading: 'Chargement…', + + // Original text: "Page not found" + errorPageNotFound: 'Page introuvable', + + // Original text: "no such item" + errorNoSuchItem: 'aucune correspondance', + + // Original text: "Long click to edit" + editableLongClickPlaceholder: 'Clic long pour éditer', + + // Original text: "Click to edit" + editableClickPlaceholder: 'Cliquer pour éditer', + + // Original text: "Browse files" + browseFiles: 'Parcourir', + + // Original text: "OK" + alertOk: 'OK', + + // Original text: "OK" + confirmOk: 'OK', + + // Original text: "Cancel" + genericCancel: 'Annuler', + + // Original text: "On error" + onError: 'En erreur', + + // Original text: "Successful" + successful: 'Réussi', + + // Original text: "Managed disks" + filterOnlyManaged: 'Supervisés', + + // Original text: "Orphaned disks" + filterOnlyOrphaned: 'Orphelins', + + // Original text: "Normal disks" + filterOnlyRegular: 'Normaux', + + // Original text: "Snapshot disks" + filterOnlySnapshots: 'Instantanés', + + // Original text: "Unmanaged disks" + filterOnlyUnmanaged: 'Bases delta', + + // Original text: "Copy to clipboard" + copyToClipboard: 'Copier dans le presse-papier', + + // Original text: "Master" + pillMaster: 'Maître', + + // Original text: "Home" + homePage: 'Accueil', + + // Original text: "VMs" + homeVmPage: 'VMs', + + // Original text: "Hosts" + homeHostPage: 'Hôtes', + + // Original text: "Pools" + homePoolPage: 'Pools', + + // Original text: "Templates" + homeTemplatePage: 'Templates', + + // Original text: "Storages" + homeSrPage: 'Stockages', + + // Original text: "Dashboard" + dashboardPage: 'Tableau de bord', + + // Original text: "Overview" + overviewDashboardPage: "Vue d'ensemble", + + // Original text: "Visualizations" + overviewVisualizationDashboardPage: 'Visualisations', + + // Original text: "Statistics" + overviewStatsDashboardPage: 'Statistiques', + + // Original text: "Health" + overviewHealthDashboardPage: 'État de santé', + + // Original text: "Self service" + selfServicePage: 'Libre service', + + // Original text: "Backup" + backupPage: 'Sauvegardes', + + // Original text: "Jobs" + jobsPage: 'Jobs', + + // Original text: "Updates" + updatePage: 'Mises à jour', + + // Original text: "Settings" + settingsPage: 'Paramètres', + + // Original text: "Servers" + settingsServersPage: 'Serveurs', + + // Original text: "Users" + settingsUsersPage: 'Utilisateurs', + + // Original text: "Groups" + settingsGroupsPage: 'Groupes', + + // Original text: "ACLs" + settingsAclsPage: 'Droits', + + // Original text: "Plugins" + settingsPluginsPage: 'Greffons', + + // Original text: "Logs" + settingsLogsPage: 'Journaux', + + // Original text: "IPs" + settingsIpsPage: 'IPs', + + // Original text: "Config" + settingsConfigPage: 'Configuration', + + // Original text: "About" + aboutPage: 'À propos', + + // Original text: "About XO {xoaPlan}" + aboutXoaPlan: 'À propos de XO {xoaPlan}', + + // Original text: "New" + newMenu: 'Nouveau', + + // Original text: "Tasks" + taskMenu: 'Tâches', + + // Original text: "Tasks" + taskPage: 'Tâches', + + // Original text: "VM" + newVmPage: 'VM', + + // Original text: "Storage" + newSrPage: 'Stockage', + + // Original text: "Server" + newServerPage: 'Serveur', + + // Original text: "Import" + newImport: 'Importer', + + // Original text: "XOSAN" + xosan: 'XOSAN', + + // Original text: "Overview" + backupOverviewPage: "Vue d'ensemble", + + // Original text: "New" + backupNewPage: 'Nouveau', + + // Original text: "Remotes" + backupRemotesPage: 'Emplacement', + + // Original text: "Restore" + backupRestorePage: 'Restaurer', + + // Original text: "File restore" + backupFileRestorePage: 'Restauration de fichiers', + + // Original text: "Schedule" + schedule: 'Planifier', + + // Original text: "New VM backup" + newVmBackup: 'Nouvelle sauvegarde de VM', + + // Original text: "Edit VM backup" + editVmBackup: 'Éditer une sauvegarde de VM', + + // Original text: "Backup" + backup: 'Sauvegarder', + + // Original text: "Rolling Snapshot" + rollingSnapshot: 'Rolling Snapshot', + + // Original text: "Delta Backup" + deltaBackup: 'Delta Backup', + + // Original text: "Disaster Recovery" + disasterRecovery: 'Reprise après panne', + + // Original text: "Continuous Replication" + continuousReplication: 'Réplication continue', + + // Original text: "Overview" + jobsOverviewPage: "Vue d'ensemble", + + // Original text: "New" + jobsNewPage: 'Nouveau', + + // Original text: "Scheduling" + jobsSchedulingPage: 'Planifier', + + // Original text: "Custom Job" + customJob: 'Job personnalisé', + + // Original text: "User" + userPage: 'Utilisateur', + + // Original text: "No support" + noSupport: 'Pas de support', + + // Original text: "Free upgrade!" + freeUpgrade: 'Mise à jour gratuite !', + + // Original text: "Sign out" + signOut: 'Se déconnecter', + + // Original text: "Edit my settings {username}" + editUserProfile: 'Éditer mes options {username}', + + // Original text: "Fetching data…" + homeFetchingData: 'Récupération des données…', + + // Original text: "Welcome on Xen Orchestra!" + homeWelcome: 'Bienvenue sur Xen Orchestra !', + + // Original text: "Add your XenServer hosts or pools" + homeWelcomeText: 'Ajouter vos serveurs ou pools XenServer', + + // Original text: "Some XenServers have been registered but are not connected" + homeConnectServerText: + "Des XenServers sont enregistrés mais aucun n'est connecté", + + // Original text: "Want some help?" + homeHelp: "Besoin d'aide ?", + + // Original text: "Add server" + homeAddServer: 'Ajouter un serveur', + + // Original text: "Connect servers" + homeConnectServer: 'Connecter des serveurs', + + // Original text: "Online Doc" + homeOnlineDoc: 'Documentation en ligne', + + // Original text: "Pro Support" + homeProSupport: 'Support professionel', + + // Original text: "There are no VMs!" + homeNoVms: "Il n'y a pas de VMs !", + + // Original text: "Or…" + homeNoVmsOr: 'Ou…', + + // Original text: "Import VM" + homeImportVm: 'Importer une VM', + + // Original text: "Import an existing VM in xva format" + homeImportVmMessage: 'Importer une VM existante au format xva', + + // Original text: "Restore a backup" + homeRestoreBackup: 'Restaurer une sauvegarde', + + // Original text: "Restore a backup from a remote store" + homeRestoreBackupMessage: + 'Restaurer une sauvegarde depuis un stockage distant', + + // Original text: "This will create a new VM" + homeNewVmMessage: 'Cela va créer une nouvelle VM', + + // Original text: "Filters" + homeFilters: 'Filtres', + + // Original text: "No results! Click here to reset your filters" + homeNoMatches: 'Aucun résultat ! Cliquez ici pour réinitialiser vos filtres', + + // Original text: "Pool" + homeTypePool: 'Pool', + + // Original text: "Host" + homeTypeHost: 'Hôte', + + // Original text: "VM" + homeTypeVm: 'VM', + + // Original text: "SR" + homeTypeSr: 'SR', + + // Original text: "Template" + homeTypeVmTemplate: 'Template', + + // Original text: "Sort" + homeSort: 'Trier', + + // Original text: "Pools" + homeAllPools: 'Pools', + + // Original text: "Hosts" + homeAllHosts: 'Hôtes', + + // Original text: "Tags" + homeAllTags: 'Tags', + + // Original text: "New VM" + homeNewVm: 'Nouvelle VM', + + // Original text: "None" + homeFilterNone: 'Aucun', + + // Original text: "Running hosts" + homeFilterRunningHosts: 'Hôtes démarrés', + + // Original text: "Disabled hosts" + homeFilterDisabledHosts: 'Hôtes désactivés', + + // Original text: "Running VMs" + homeFilterRunningVms: 'VMs démarrées', + + // Original text: "Non running VMs" + homeFilterNonRunningVms: 'VMs éteintes', + + // Original text: "Pending VMs" + homeFilterPendingVms: 'VMs en attente', + + // Original text: "HVM guests" + homeFilterHvmGuests: 'Invités HVM', + + // Original text: "Tags" + homeFilterTags: 'Tags', + + // Original text: "Sort by" + homeSortBy: 'Trier par', + + // Original text: "Name" + homeSortByName: 'Nom', + + // Original text: "Power state" + homeSortByPowerstate: 'Alimentation', + + // Original text: "RAM" + homeSortByRAM: 'RAM', + + // Original text: "vCPUs" + homeSortByvCPUs: 'vCPUs', + + // Original text: "CPUs" + homeSortByCpus: 'CPUs', + + // Original text: "Shared/Not shared" + homeSortByShared: 'Partagé/Non partagé', + + // Original text: "Size" + homeSortBySize: 'Taille', + + // Original text: "Usage" + homeSortByUsage: 'Utilisation', + + // Original text: "Type" + homeSortByType: 'Type', + + // Original text: "{displayed, number}x {icon} (on {total, number})" + homeDisplayedItems: '{displayed, number}x {icon} (sur {total, number})', + + // Original text: "{selected, number}x {icon} selected (on {total, number})" + homeSelectedItems: + '{selected, number}x {icon} sélectionné{selected, plural, one {} other {s}} (sur {total, number})', + + // Original text: "More" + homeMore: 'Plus', + + // Original text: "Migrate to…" + homeMigrateTo: 'Migrer vers…', + + // Original text: "Missing patches" + homeMissingPaths: 'Patches manquant', + + // Original text: "Master:" + homePoolMaster: 'Maître :', + + // Original text: "Resource set: {resourceSet}" + homeResourceSet: 'Jeu de ressources : {resourceSet}', + + // Original text: "High Availability" + highAvailability: 'Haute disponibilité', + + // Original text: "Shared {type}" + srSharedType: '{type} partagé', + + // Original text: "Not shared {type}" + srNotSharedType: '{type} non partagé', + + // Original text: 'All of them are selected' + sortedTableAllItemsSelected: 'Toutes sont sélectionnées', + + // Original text: '{nFiltered, number} of {nTotal, number} items' + sortedTableNumberOfFilteredItems: + '{nFiltered, number} entrées sur {nTotal, number}', + + // Original text: '{nTotal, number} items' + sortedTableNumberOfItems: '{nTotal, number} entrées', + + // Original text: '{nSelected, number} selected' + sortedTableNumberOfSelectedItems: '{nSelected, number} sélectionnées', + + // Original text: 'Click here to select all items' + sortedTableSelectAllItems: 'Cliquez ici pour sélectionner toutes les entrées', + + // Original text: "Add" + add: 'Ajouter', + + // Original text: "Select all" + selectAll: 'Tout sélectionner', + + // Original text: "Remove" + remove: 'Supprimer', + + // Original text: "Preview" + preview: 'Aperçu', + + // Original text: "Item" + item: 'Objet', + + // Original text: "No selected value" + noSelectedValue: 'Pas de valeur sélectionnée', + + // Original text: "Choose user(s) and/or group(s)" + selectSubjects: 'Sélectionner un ou des utilisateur(s) et/ou groupe(s)', + + // Original text: "Select Object(s)…" + selectObjects: 'Sélectionner de(s) objet(s)…', + + // Original text: "Choose a role" + selectRole: 'Choisir un rôle', + + // Original text: "Select Host(s)…" + selectHosts: 'Choisir un/des hôte(s)…', + + // Original text: "Select object(s)…" + selectHostsVms: 'Choisir un/des object(s)…', + + // Original text: "Select Network(s)…" + selectNetworks: 'Choisir un/des réseau(x)…', + + // Original text: "Select PIF(s)…" + selectPifs: 'Sélectionner une/des PIF(s)…', + + // Original text: "Select Pool(s)…" + selectPools: 'Sélectionner un/des Pool(s)…', + + // Original text: "Select Remote(s)…" + selectRemotes: 'Choisir un/des emplacement(s)…', + + // Original text: "Select resource set(s)…" + selectResourceSets: 'Choisir un jeu de ressource(s)…', + + // Original text: "Select template(s)…" + selectResourceSetsVmTemplate: 'Sélectionner un/des template(s)…', + + // Original text: "Select SR(s)…" + selectResourceSetsSr: 'Sélectionner un/des SR(s)…', + + // Original text: "Select network(s)…" + selectResourceSetsNetwork: 'Sélectionner un/des réseau(x)…', + + // Original text: "Select disk(s)…" + selectResourceSetsVdi: 'Sélectionner un/des disque(s)…', + + // Original text: "Select SSH key(s)…" + selectSshKey: 'Sélectionner une/des clef(s) SSH…', + + // Original text: "Select SR(s)…" + selectSrs: 'Sélectionner un/des SR(s)…', + + // Original text: "Select VM(s)…" + selectVms: 'Sélectionner une/des VM(s)…', + + // Original text: "Select VM template(s)…" + selectVmTemplates: 'Sélectionner un/des template(s) de VM…', + + // Original text: "Select tag(s)…" + selectTags: 'Sélectionner un/des tag(s)…', + + // Original text: "Select disk(s)…" + selectVdis: 'Sélectionner un/des disque(s)…', + + // Original text: "Select timezone…" + selectTimezone: 'Sélectionner le fuseau horaire…', + + // Original text: "Select IP(s)…" + selectIp: 'Sélectionner une/des IP(s)…', + + // Original text: "Select IP pool(s)…" + selectIpPool: "Sélectionner une/des plage(s) d'IP(s)…", + + // Original text: "Fill required informations." + fillRequiredInformations: 'Saisir les informations obligatoires.', + + // Original text: "Fill informations (optional)" + fillOptionalInformations: 'Saisir les informations (optionnel)', + + // Original text: "Reset" + selectTableReset: 'Réinitialiser', + + // Original text: "Month" + schedulingMonth: 'Mois', + + // Original text: "Every N month" + schedulingEveryNMonth: 'Tous les N mois', + + // Original text: "Each selected month" + schedulingEachSelectedMonth: 'Chaque mois sélectionné', + + // Original text: "Day" + schedulingDay: 'Jour', + + // Original text: "Every N day" + schedulingEveryNDay: 'Tous les N jours', + + // Original text: "Each selected day" + schedulingEachSelectedDay: 'Chaque jour sélectionné', + + // Original text: "Switch to week days" + schedulingSetWeekDayMode: 'Utiliser les jours de la semaine', + + // Original text: "Switch to month days" + schedulingSetMonthDayMode: 'Utiliser les jours du mois', + + // Original text: "Hour" + schedulingHour: 'Heure', + + // Original text: "Each selected hour" + schedulingEachSelectedHour: 'Chaque heure sélectionnée', + + // Original text: "Every N hour" + schedulingEveryNHour: 'Toutes les N heures', + + // Original text: "Minute" + schedulingMinute: 'Minute', + + // Original text: "Each selected minute" + schedulingEachSelectedMinute: 'Chaque minute sélectionnée', + + // Original text: "Every N minute" + schedulingEveryNMinute: 'Toutes les N minutes', + + // Original text: "Every month" + selectTableAllMonth: 'Tous les mois', + + // Original text: "Every day" + selectTableAllDay: 'Tous les jours', + + // Original text: "Every hour" + selectTableAllHour: 'Toutes les heures', + + // Original text: "Every minute" + selectTableAllMinute: 'Toutes les minutes', + + // Original text: "Reset" + schedulingReset: 'Réinitialiser', + + // Original text: "Unknown" + unknownSchedule: 'Inconnu', + + // Original text: "Web browser timezone" + timezonePickerUseLocalTime: 'Fuseau horaire de votre navigateur', + + // Original text: "Server timezone ({value})" + serverTimezoneOption: 'Fuseau horaire du serveur ({value})', + + // Original text: "Cron Pattern:" + cronPattern: 'Motif Cron :', + + // Original text: "Cannot edit backup" + backupEditNotFoundTitle: "Impossible d'éditer la sauvegarde", + + // Original text: "Missing required info for edition" + backupEditNotFoundMessage: + "Il manque les informations nécessaires à l'édition", + + // Original text: "Successful" + successfulJobCall: 'Réussi', + + // Original text: "Failed" + failedJobCall: 'Échoué', + + // Original text: "In progress" + jobCallInProgess: 'En cours', + + // Original text: "size:" + jobTransferredDataSize: 'Taille :', + + // Original text: "speed:" + jobTransferredDataSpeed: 'Vitesse :', + + // Original text: "Job" + job: 'Job', + + // Original text: "Job {job}" + jobModalTitle: 'Job {job}', + + // Original text: "ID" + jobId: 'ID du job', + + // Original text: "Type" + jobType: 'Type', + + // Original text: "Name" + jobName: 'Nom', + + // Original text: "Name of your job (forbidden: \"_\")" + jobNamePlaceholder: 'Nom de votre job (caractère interdit : "_")', + + // Original text: "Start" + jobStart: 'Début', + + // Original text: "End" + jobEnd: 'Fin', + + // Original text: "Duration" + jobDuration: 'Durée', + + // Original text: "Status" + jobStatus: 'État', + + // Original text: "Action" + jobAction: 'Action', + + // Original text: "Tag" + jobTag: 'Tag', + + // Original text: "Scheduling" + jobScheduling: 'Planifié', + + // Original text: "State" + jobState: 'État', + + // Original text: "Enabled" + jobStateEnabled: 'Activé', + + // Original text: "Disabled" + jobStateDisabled: 'Désactivé', + + // Original text: "Timezone" + jobTimezone: 'Fuseau horaire', + + // Original text: "Server" + jobServerTimezone: 'xo-server', + + // Original text: "Run job" + runJob: 'Lancer le job', + + // Original text: "One shot running started. See overview for logs." + runJobVerbose: + "Une exécution a été lancée. Voir l'overview pour plus de détails.", + + // Original text: "Started" + jobStarted: 'Démarré', + + // Original text: "Finished" + jobFinished: 'Terminé', + + // Original text: "Save" + saveBackupJob: 'Enregistrer', + + // Original text: "Remove backup job" + deleteBackupSchedule: 'Supprimer ce job de sauvegarde', + + // Original text: "Are you sure you want to delete this backup job?" + deleteBackupScheduleQuestion: + 'Êtes-vous sûr de vouloir supprimer ce job de sauvegarde ?', + + // Original text: "Enable immediately after creation" + scheduleEnableAfterCreation: 'Activer aussitôt après la création', + + // Original text: "You are editing Schedule {name} ({id}). Saving will override previous schedule state." + scheduleEditMessage: + "Vous êtes en train d'éditer {name} ({id}). Enregistrer écrasera l'état planifié précédent.", + + // Original text: "You are editing job {name} ({id}). Saving will override previous job state." + jobEditMessage: + "Vous êtes en train d'éditer le job {name} ({id}). Enregistrer écrasera l'état du job précédent.", + + // Original text: "No scheduled jobs." + noScheduledJobs: 'Pas de job planifié.', + + // Original text: "No jobs found." + noJobs: 'Pas de job trouvé.', + + // Original text: "No schedules found" + noSchedules: 'Pas de planification trouvée', + + // Original text: "Select a xo-server API command" + jobActionPlaceHolder: "Sélectionnez une commande de l'API xo-server", + + // Original text: "Timeout (number of seconds after which a VM is considered failed)" + jobTimeoutPlaceHolder: + 'Temporisation (nombre de secondes autorisé pour chaque VM)', + + // Original text: "Schedules" + jobSchedules: 'Planning', + + // Original text: "Name of your schedule" + jobScheduleNamePlaceHolder: 'Nom de votre planification', + + // Original text: "Select a Job" + jobScheduleJobPlaceHolder: 'Choisir un Job', + + // Original text: "Job owner" + jobOwnerPlaceholder: 'Propriétaire', + + // Original text: "This job's creator no longer exists" + jobUserNotFound: "Le propriétaire de ce job n'existe plus", + + // Original text: "This backup's creator no longer exists" + backupUserNotFound: "Le propriétaire de cette sauvegarde n'existe plus", + + // Original text: "Backup owner" + backupOwner: 'Propriétaire', + + // Original text: "Select your backup type:" + newBackupSelection: 'Choisissez votre type de sauvegarde :', + + // Original text: "Select backup mode:" + smartBackupModeSelection: 'Sélectionnez votre mode de sauvegarde :', + + // Original text: "Normal backup" + normalBackup: 'Backup normal', + + // Original text: "Smart backup" + smartBackup: 'Backup intelligent', + + // Original text: "Local remote selected" + localRemoteWarningTitle: 'Emplacement local sélectionné', + + // Original text: "Warning: local remotes will use limited XOA disk space. Only for advanced users." + localRemoteWarningMessage: + "Attention : utiliser un emplacement local limite l'espace pour XO. Restreignez ceci aux utilisateurs avancés.", + + // Original text: "Warning: this feature works only with XenServer 6.5 or newer." + backupVersionWarning: + "Attention : cette fonctionnalité ne fonctionne qu'avec XenServer 6.5 et plus récent.", + + // Original text: "VMs" + editBackupVmsTitle: 'VMs', + + // Original text: "VMs statuses" + editBackupSmartStatusTitle: 'État des VMs', + + // Original text: "Resident on" + editBackupSmartResidentOn: 'Situé sur', + + // Original text: "Pools" + editBackupSmartPools: 'Pools', + + // Original text: "Tags" + editBackupSmartTags: 'Tags', + + // Original text: "VMs Tags" + editBackupSmartTagsTitle: 'Tags des VMs', + + // Original text: "Reverse" + editBackupNot: 'Inverser', + + // Original text: "Tag" + editBackupTagTitle: 'Tag', + + // Original text: "Report" + editBackupReportTitle: 'Rapport', + + // Original text: "Automatically run as scheduled" + editBackupScheduleEnabled: 'Executer en fonction de la planification', + + // Original text: "Depth" + editBackupDepthTitle: 'Profondeur', + + // Original text: "Remote" + editBackupRemoteTitle: 'Emplacement', + + // Original text: "Delete the old backups first" + deleteOldBackupsFirst: 'Supprimer les anciennes sauvegardes', + + // Original text: "Remote stores for backup" + remoteList: 'Emplacement de stockage de backup', + + // Original text: "New File System Remote" + newRemote: 'Nouvel emplacement de stockage', + + // Original text: "Local" + remoteTypeLocal: 'Local', + + // Original text: "NFS" + remoteTypeNfs: 'NFS', + + // Original text: "SMB" + remoteTypeSmb: 'SMB', + + // Original text: "Type" + remoteType: 'Type', + + // Original text: "Test your remote" + remoteTestTip: 'Testez votre emplacement', + + // Original text: "Test Remote" + testRemote: "Tester l'emplacement", + + // Original text: "Test failed for {name}" + remoteTestFailure: 'Test réussi pour {name}', + + // Original text: "Test passed for {name}" + remoteTestSuccess: 'Test échoué pour {name}', + + // Original text: "Error" + remoteTestError: 'Erreur', + + // Original text: "Test Step" + remoteTestStep: 'Étape de test', + + // Original text: "Test file" + remoteTestFile: 'Fichier de test', + + // Original text: 'Test name' + remoteTestName: undefined, + + // Original text: "Remote name already exists!" + remoteTestNameFailure: "Le nom de l'emplacement existe déjà", + + // Original text: "The remote appears to work correctly" + remoteTestSuccessMessage: "L'emplacement distant semble marcher correctement", + + // Original text: "Connection failed" + remoteConnectionFailed: 'La connexion a échoué', + + // Original text: "Name" + remoteName: 'Nom', + + // Original text: "Path" + remotePath: 'Chemin', + + // Original text: "State" + remoteState: 'État', + + // Original text: "Device" + remoteDevice: 'Équipement', + + // Original text: "Share" + remoteShare: 'Partage', + + // Original text: "Action" + remoteAction: 'Action', + + // Original text: "Auth" + remoteAuth: 'Accès', + + // Original text: "Mounted" + remoteMounted: 'Monté', + + // Original text: "Unmounted" + remoteUnmounted: 'Démonté', + + // Original text: "Connect" + remoteConnectTip: 'Connecter', + + // Original text: "Disconnect" + remoteDisconnectTip: 'Déconnecter', + + // Original text: "Connected" + remoteConnected: 'Connecté', + + // Original text: "Disconnected" + remoteDisconnected: 'Déconnecté', + + // Original text: "Delete" + remoteDeleteTip: 'Supprimer', + + // Original text: "remote name *" + remoteNamePlaceHolder: 'nom distant*', + + // Original text: "Name *" + remoteMyNamePlaceHolder: 'Nom *', + + // Original text: "/path/to/backup" + remoteLocalPlaceHolderPath: '/chemin/de/la/sauvegarde', + + // Original text: "host *" + remoteNfsPlaceHolderHost: 'hôte *', + + // Original text: "path/to/backup" + remoteNfsPlaceHolderPath: 'chemin/de/la/sauvegarde', + + // Original text: "subfolder [path\\to\\backup]" + remoteSmbPlaceHolderRemotePath: + 'sous-répertoire [chemin\\vers\\la\\sauvegarde]', + + // Original text: "Username" + remoteSmbPlaceHolderUsername: "Nom d'utilisateur", + + // Original text: "Password" + remoteSmbPlaceHolderPassword: 'Mot de passe', + + // Original text: "Domain" + remoteSmbPlaceHolderDomain: 'Domaine', + + // Original text: "
\\ *" + remoteSmbPlaceHolderAddressShare: '\\ *', + + // Original text: "password(fill to edit)" + remotePlaceHolderPassword: 'mot de passe (saisir ici pour éditer)', + + // Original text: "Create a new SR" + newSrTitle: 'Créer un nouvel SR', + + // Original text: "General" + newSrGeneral: 'Général', + + // Original text: "Select Storage Type:" + newSrTypeSelection: 'Sélectionner un type de stockage :', + + // Original text: "Settings" + newSrSettings: 'Paramètres', + + // Original text: "Storage Usage" + newSrUsage: 'Utilisation du stockage', + + // Original text: "Summary" + newSrSummary: 'Récapitulatif', + + // Original text: "Host" + newSrHost: 'Hôtes', + + // Original text: "Type" + newSrType: 'Type', + + // Original text: "Name" + newSrName: 'Nom', + + // Original text: "Description" + newSrDescription: 'Description', + + // Original text: "Server" + newSrServer: 'Serveur', + + // Original text: "Path" + newSrPath: 'Chemin', + + // Original text: "IQN" + newSrIqn: 'IQN', + + // Original text: "LUN" + newSrLun: 'LUN', + + // Original text: "with auth." + newSrAuth: 'avec identification', + + // Original text: "User Name" + newSrUsername: "Nom d'utilisateur", + + // Original text: "Password" + newSrPassword: 'Mot de passe', + + // Original text: "Device" + newSrDevice: 'Équipement', + + // Original text: "in use" + newSrInUse: 'utilisé', + + // Original text: "Size" + newSrSize: 'Taille', + + // Original text: "Create" + newSrCreate: 'Créer', + + // Original text: "Storage name" + newSrNamePlaceHolder: "Nom de l'emplacement", + + // Original text: "Storage description" + newSrDescPlaceHolder: "Description de l'emplacement", + + // Original text: "Address" + newSrAddressPlaceHolder: 'Adresse', + + // Original text: "[port]" + newSrPortPlaceHolder: '[port]', + + // Original text: "Username" + newSrUsernamePlaceHolder: "Nom d'utilisateur", + + // Original text: "Password" + newSrPasswordPlaceHolder: 'Mot de passe', + + // Original text: "Device, e.g /dev/sda…" + newSrLvmDevicePlaceHolder: 'Matériel, par exemple /dev/sda…', + + // Original text: "/path/to/directory" + newSrLocalPathPlaceHolder: '/chemin/du/répertoire', + + // Original text: "Users/Groups" + subjectName: 'Utilisateurs/Groupes', + + // Original text: "Object" + objectName: 'Objet', + + // Original text: "No acls found" + aclNoneFound: 'Aucun droit existant', + + // Original text: "Role" + roleName: 'Rôle', + + // Original text: "Create" + aclCreate: 'Créer', + + // Original text: "New Group Name" + newGroupName: 'Nouveau nom de groupe', + + // Original text: "Create Group" + createGroup: 'Créer un groupe', + + // Original text: "Create" + createGroupButton: 'Créer', + + // Original text: "Delete Group" + deleteGroup: 'Supprimer le groupe', + + // Original text: "Are you sure you want to delete this group?" + deleteGroupConfirm: 'Êtes-vous sûr de vouloir supprimer ce groupe ?', + + // Original text: "Remove user from Group" + removeUserFromGroup: "Supprimer l'utilisateur du groupe", + + // Original text: "Are you sure you want to delete this user?" + deleteUserConfirm: 'Êtes-vous sûr de vouloir supprimer cet utilisateur ?', + + // Original text: "Delete User" + deleteUser: "Supprimer l'utilisateur", + + // Original text: "no user" + noUser: "pas d'utilisateur", + + // Original text: "unknown user" + unknownUser: 'utilisateur inconnu', + + // Original text: "No group found" + noGroupFound: 'Pas de groupe trouvé', + + // Original text: "Name" + groupNameColumn: 'Nom', + + // Original text: "Users" + groupUsersColumn: 'Utilisateur', + + // Original text: "Add User" + addUserToGroupColumn: 'Ajouter un utilisateur', + + // Original text: "Email" + userNameColumn: 'Email', + + // Original text: "Permissions" + userPermissionColumn: 'Droits', + + // Original text: "Password" + userPasswordColumn: 'Mot de passe', + + // Original text: "Email" + userName: 'Email', + + // Original text: "Password" + userPassword: 'Mot de passe', + + // Original text: "Create" + createUserButton: 'Créer', + + // Original text: "No user found" + noUserFound: "Pas d'utilisateur trouvé", + + // Original text: "User" + userLabel: 'Utilisateur', + + // Original text: "Admin" + adminLabel: 'Administrateur', + + // Original text: "No user in group" + noUserInGroup: "Pas d'utilisateur dans ce groupe", + + // Original text: "{users, number} user{users, plural, one {} other {s}}" + countUsers: '{users} utilisateur{users, plural, one {} other {s}}', + + // Original text: "Select Permission" + selectPermission: 'Choisir les droits', + + // Original text: "No plugins found" + noPlugins: 'Aucun plugin trouvé', + + // Original text: "Auto-load at server start" + autoloadPlugin: 'Charger automatiquement au démarrage du serveur', + + // Original text: "Save configuration" + savePluginConfiguration: 'Sauvegarder la configuration', + + // Original text: "Delete configuration" + deletePluginConfiguration: 'Supprimer la configuration', + + // Original text: "Plugin error" + pluginError: 'Erreur du greffon', + + // Original text: "Unknown error" + unknownPluginError: 'Erreur inconnue', + + // Original text: "Purge plugin configuration" + purgePluginConfiguration: 'Purger la configuration du greffon', + + // Original text: "Are you sure you want to purge this configuration ?" + purgePluginConfigurationQuestion: + 'Êtes-vous sûr de vouloir purger la configuration de ce greffon ?', + + // Original text: "Edit" + editPluginConfiguration: 'Éditer', + + // Original text: "Cancel" + cancelPluginEdition: 'Annuler', + + // Original text: "Plugin configuration" + pluginConfigurationSuccess: 'Configuration du greffon', + + // Original text: "Plugin configuration successfully saved!" + pluginConfigurationChanges: + 'La configuration du greffon a été sauvegardée avec succés !', + + // Original text: "Predefined configuration" + pluginConfigurationPresetTitle: 'Configuration pré-définie', + + // Original text: "Choose a predefined configuration." + pluginConfigurationChoosePreset: 'Choisir une configuration pré-définie.', + + // Original text: "Apply" + applyPluginPreset: 'Appliquer', + + // Original text: "Save filter error" + saveNewUserFilterErrorTitle: "Erreur lors de l'enregistrement du filtre", + + // Original text: "Bad parameter: name must be given." + saveNewUserFilterErrorBody: 'Erreur: un nom doit être spécifié.', + + // Original text: "Name:" + filterName: 'Nom :', + + // Original text: "Value:" + filterValue: 'Valeur :', + + // Original text: "Save new filter" + saveNewFilterTitle: 'Enregistrer un nouveau filtre', + + // Original text: "Set custom filters" + setUserFiltersTitle: 'Personnaliser un filtre', + + // Original text: "Are you sure you want to set custom filters?" + setUserFiltersBody: 'Êtes-vous sûr de vouloir personnaliser un filtre ?', + + // Original text: "Remove custom filter" + removeUserFilterTitle: 'Supprimer un filtre personnalisé', + + // Original text: "Are you sure you want to remove custom filter?" + removeUserFilterBody: + 'Êtes-vous sûr de vouloir supprimer ce filtre personnalisé ?', + + // Original text: "Default filter" + defaultFilter: 'Filtre par défaut', + + // Original text: "Default filters" + defaultFilters: 'Filtres par défaut', + + // Original text: "Custom filters" + customFilters: 'Filtre personnalisé', + + // Original text: "Customize filters" + customizeFilters: 'Personnaliser les filtres', + + // Original text: "Save custom filters" + saveCustomFilters: 'Sauvegarder les filtres personnalisés', + + // Original text: "Start" + startVmLabel: 'Créer', + + // Original text: "Recovery start" + recoveryModeLabel: 'Démarrage de dépannage', + + // Original text: "Suspend" + suspendVmLabel: 'Suspendre', + + // Original text: "Stop" + stopVmLabel: 'Arrêter', + + // Original text: "Force shutdown" + forceShutdownVmLabel: "Forcer l'arrêt", + + // Original text: "Reboot" + rebootVmLabel: 'Redémarrer', + + // Original text: "Force reboot" + forceRebootVmLabel: 'Forcer le redémarrage', + + // Original text: "Delete" + deleteVmLabel: 'Supprimer', + + // Original text: "Migrate" + migrateVmLabel: 'Migrer', + + // Original text: "Snapshot" + snapshotVmLabel: 'Instantané', + + // Original text: "Export" + exportVmLabel: 'Exporter', + + // Original text: "Resume" + resumeVmLabel: 'Reprendre', + + // Original text: "Copy" + copyVmLabel: 'Copier', + + // Original text: "Clone" + cloneVmLabel: 'Cloner', + + // Original text: "Fast clone" + fastCloneVmLabel: 'Clonage rapide', + + // Original text: "Convert to template" + convertVmToTemplateLabel: 'Convertir en template', + + // Original text: "Console" + vmConsoleLabel: 'Console', + + // Original text: "Rescan all disks" + srRescan: 'Rescanner tous les disques', + + // Original text: "Connect to all hosts" + srReconnectAll: 'Connecter sur tous les hôtes', + + // Original text: "Disconnect from all hosts" + srDisconnectAll: 'Déconnecter de tous les hôtes', + + // Original text: "Forget this SR" + srForget: 'Oublier ce SR', + + // Original text: "Forget SRs" + srsForget: 'Oublier les stockages', + + // Original text: "Remove this SR" + srRemoveButton: 'Supprimer ce SR', + + // Original text: "No VDIs in this storage" + srNoVdis: 'Pas de VDI sur ce stockage', + + // Original text: "Pool RAM usage:" + poolTitleRamUsage: 'Utilisation RAM du pool :', + + // Original text: "{used} used on {total}" + poolRamUsage: '{used} utilisé sur {total}', + + // Original text: "Master:" + poolMaster: 'Maître :', + + // Original text: "Display all hosts of this pool" + displayAllHosts: 'Afficher les hôtes du pool', + + // Original text: "Display all storages of this pool" + displayAllStorages: 'Afficher les stockages du pool', + + // Original text: "Display all VMs of this pool" + displayAllVMs: 'Afficher les VMs du pool', + + // Original text: "Hosts" + hostsTabName: 'Hôtes', + + // Original text: "Vms" + vmsTabName: 'VMs', + + // Original text: "Srs" + srsTabName: 'stockages', + + // Original text: "High Availability" + poolHaStatus: 'Haute Disponibilité', + + // Original text: "Enabled" + poolHaEnabled: 'Activé', + + // Original text: "Disabled" + poolHaDisabled: 'Désactivé', + + // Original text: "Name" + hostNameLabel: 'Nom', + + // Original text: "Description" + hostDescription: 'Description', + + // Original text: "Memory" + hostMemory: 'Mémoire', + + // Original text: "No hosts" + noHost: "Pas d'hôte", + + // Original text: "{used}% used ({free} free)" + memoryLeftTooltip: '{used}% utilisé ({free} libre)', + + // Original text: "PIF" + pif: 'PIF', + + // Original text: "Name" + poolNetworkNameLabel: 'Nom', + + // Original text: "Description" + poolNetworkDescription: 'Description', + + // Original text: "PIFs" + poolNetworkPif: 'PIFs', + + // Original text: "No networks" + poolNoNetwork: 'Pas de réseaux', + + // Original text: "MTU" + poolNetworkMTU: 'MTU', + + // Original text: "Connected" + poolNetworkPifAttached: 'Connecté', + + // Original text: "Disconnected" + poolNetworkPifDetached: 'Déconnecté', + + // Original text: "Show PIFs" + showPifs: 'Afficher les PIFs', + + // Original text: "Hide PIFs" + hidePifs: 'Cacher les PIFs', + + // Original text: "Show details" + showDetails: 'Afficher les détails', + + // Original text: "Hide details" + hideDetails: 'Cacher les détails', + + // Original text: "No stats" + poolNoStats: 'Pas de statistiques', + + // Original text: "All hosts" + poolAllHosts: 'Tous les hôtes', + + // Original text: "Add SR" + addSrLabel: 'Ajouter un SR', + + // Original text: "Add VM" + addVmLabel: 'Ajouter une VM', + + // Original text: "Add Host" + addHostLabel: 'Ajouter un hôte', + + // Original text: "This host needs to install {patches, number} patch{patches, plural, one {} other {es}} before it can be added to the pool. This operation may be long." + hostNeedsPatchUpdate: + "Cet hôte a besoin d'installer {patches, number} patch{patches, plural, one {} other {es}} avant de pouvoir être ajouté au pool. Cette opération peut être longue.", + + // Original text: "This host cannot be added to the pool because it's missing some patches." + hostNeedsPatchUpdateNoInstall: + 'Cette hôte ne peut pas être ajouté au pool car il lui manque des patches.', + + // Original text: "Adding host failed" + addHostErrorTitle: "L'ajout de l'hôte a échoué.", + + // Original text: "Host patches could not be homogenized." + addHostNotHomogeneousErrorMessage: + "Les patches de l'hôte n'ont pas pu être homogénéisés.", + + // Original text: "Disconnect" + disconnectServer: 'Déconnecter', + + // Original text: "Start" + startHostLabel: 'Démarrer', + + // Original text: "Stop" + stopHostLabel: 'Arrêter', + + // Original text: "Enable" + enableHostLabel: 'Activer', + + // Original text: "Disable" + disableHostLabel: 'Désactiver', + + // Original text: "Restart toolstack" + restartHostAgent: 'Redémarrer la toolstack', + + // Original text: "Force reboot" + forceRebootHostLabel: 'Forcer un redémarrage', + + // Original text: "Reboot" + rebootHostLabel: 'Redémarrer', + + // Original text: "Error while restarting host" + noHostsAvailableErrorTitle: "Erreur lors du redémarrage de l'hôte", + + // Original text: "Some VMs cannot be migrated before restarting this host. Please try force reboot." + noHostsAvailableErrorMessage: + "Certaines VMs ne peuvent pas être migrées avant le redémarrage de l'hôte. Essayez de forcer le redémarrage.", + + // Original text: "Error while restarting hosts" + failHostBulkRestartTitle: 'Erreur lors du redémarrage des hôtes', + + // Original text: "{failedHosts, number}/{totalHosts, number} host{failedHosts, plural, one {} other {s}} could not be restarted." + failHostBulkRestartMessage: + "{failedHosts, number}/{totalHosts, number} {failedHosts, plural, one {hôte n'a pas pu être redémarré} other {n'ont pas pu être redémarrés}} ", + + // Original text: "Reboot to apply updates" + rebootUpdateHostLabel: 'Redémarrer pour appliquer les mises à jour', + + // Original text: "Emergency mode" + emergencyModeLabel: 'Emergency mode', + + // Original text: "Storage" + storageTabName: 'Stockage', + + // Original text: "Patches" + patchesTabName: 'Patches', + + // Original text: "Load average" + statLoad: 'Charge (load) moyenne :', + + // Original text: "RAM Usage: {memoryUsed}" + memoryHostState: 'Mémoire utilisée : {memoryUsed}', + + // Original text: "Hardware" + hardwareHostSettingsLabel: 'Matériel', + + // Original text: "Address" + hostAddress: 'Adresse', + + // Original text: "Status" + hostStatus: 'État', + + // Original text: "Build number" + hostBuildNumber: 'Numéro de build', + + // Original text: "iSCSI name" + hostIscsiName: 'Nom iSCSI', + + // Original text: "Version" + hostXenServerVersion: 'Version', + + // Original text: "Enabled" + hostStatusEnabled: 'Activé', + + // Original text: "Disabled" + hostStatusDisabled: 'Désactivé', + + // Original text: "Power on mode" + hostPowerOnMode: "Mode d'allumage", + + // Original text: "Host uptime" + hostStartedSince: "Temps d'activité", + + // Original text: "Toolstack uptime" + hostStackStartedSince: 'Toolstack uptime', + + // Original text: "CPU model" + hostCpusModel: 'Modèle de CPU', + + // Original text: "Core (socket)" + hostCpusNumber: 'Cœur (socket)', + + // Original text: "Manufacturer info" + hostManufacturerinfo: 'Informations constructeur', + + // Original text: "BIOS info" + hostBiosinfo: 'Informations BIOS', + + // Original text: "Licence" + licenseHostSettingsLabel: 'Licence', + + // Original text: "Type" + hostLicenseType: 'Type', + + // Original text: "Socket" + hostLicenseSocket: 'Socket', + + // Original text: "Expiry" + hostLicenseExpiry: 'Expiration', + + // Original text: "Installed supplemental packs" + supplementalPacks: 'Packs supplémentaires installés', + + // Original text: "Install new supplemental pack" + supplementalPackNew: 'Installer un nouveau pack supplémentaire', + + // Original text: "Install supplemental pack on every host" + supplementalPackPoolNew: + 'Installer un pack supplémentaire sur tous les hôtes', + + // Original text: "{name} (by {author})" + supplementalPackTitle: '{name} (par {author})', + + // Original text: "Installation started" + supplementalPackInstallStartedTitle: 'Installation démarrée', + + // Original text: "Installing new supplemental pack…" + supplementalPackInstallStartedMessage: + "Installation d'un nouveau pack supplémentaire", + + // Original text: "Installation error" + supplementalPackInstallErrorTitle: "Erreur d'installation", + + // Original text: "The installation of the supplemental pack failed." + supplementalPackInstallErrorMessage: + "L'installation du pack supplémentaire a échoué.", + + // Original text: "Installation success" + supplementalPackInstallSuccessTitle: "Succès de l'installation", + + // Original text: "Supplemental pack successfully installed." + supplementalPackInstallSuccessMessage: + 'Le pack supplémentaire a été installé avec succès.', + + // Original text: "Add a network" + networkCreateButton: 'Ajouter un réseau', + + // Original text: "Add a bonded network" + networkCreateBondedButton: 'Ajouter un réseau agrégé', + + // Original text: "Device" + pifDeviceLabel: 'Device', + + // Original text: "Network" + pifNetworkLabel: 'Réseau', + + // Original text: "VLAN" + pifVlanLabel: 'VLAN', + + // Original text: "Address" + pifAddressLabel: 'Adresse', + + // Original text: "Mode" + pifModeLabel: 'Mode', + + // Original text: "MAC" + pifMacLabel: 'MAC', + + // Original text: "MTU" + pifMtuLabel: 'MTU', + + // Original text: "Status" + pifStatusLabel: 'État', + + // Original text: "Connected" + pifStatusConnected: 'Connecté', + + // Original text: "Disconnected" + pifStatusDisconnected: 'Déconnecté', + + // Original text: "No physical interface detected" + pifNoInterface: "Pas d'interface physique détectée", + + // Original text: "This interface is currently in use" + pifInUse: "Cette interface est en cours d'utilisation", + + // Original text: "Action" + pifAction: 'Action', + + // Original text: "Default locking mode" + defaultLockingMode: 'Verrouillage par défaut', + + // Original text: "Configure IP address" + pifConfigureIp: "Configurer l'adresse IP", + + // Original text: "Invalid parameters" + configIpErrorTitle: 'Paramètres invalides', + + // Original text: "IP address and netmask required" + configIpErrorMessage: 'Adresse IP et masque de réseau requis', + + // Original text: "Static IP address" + staticIp: 'Adresse IP statique', + + // Original text: "Netmask" + netmask: 'Masque de réseau', + + // Original text: "DNS" + dns: 'DNS', + + // Original text: "Gateway" + gateway: 'Passerelle', + + // Original text: "Add a storage" + addSrDeviceButton: 'Ajouter un stockage', + + // Original text: "Name" + srNameLabel: 'Nom', + + // Original text: "Type" + srType: 'Type', + + // Original text: "Action" + pbdAction: 'Action', + + // Original text: "Status" + pbdStatus: 'État', + + // Original text: "Connected" + pbdStatusConnected: 'Connecté', + + // Original text: "Disconnected" + pbdStatusDisconnected: 'Déconnecté', + + // Original text: "Connect" + pbdConnect: 'Connecter', + + // Original text: "Disconnect" + pbdDisconnect: 'Déconnecter', + + // Original text: "Forget" + pbdForget: 'Oublier', + + // Original text: "Shared" + srShared: 'Partager', + + // Original text: "Not shared" + srNotShared: 'Non partagé', + + // Original text: "No storage detected" + pbdNoSr: 'Pas de stockage détecté', + + // Original text: "Name" + patchNameLabel: 'Nom', + + // Original text: "Install all patches" + patchUpdateButton: 'installer tous les patchs', + + // Original text: "Description" + patchDescription: 'Description', + + // Original text: "Applied date" + patchApplied: "Date d'installation", + + // Original text: "Size" + patchSize: 'Taille', + + // Original text: "Status" + patchStatus: 'État', + + // Original text: "Applied" + patchStatusApplied: 'Appliqué', + + // Original text: "Missing patches" + patchStatusNotApplied: 'Patches manquants', + + // Original text: "No patch detected" + patchNothing: 'Pas de patch détecté', + + // Original text: "Release date" + patchReleaseDate: 'Date de diffusion', + + // Original text: "Guidance" + patchGuidance: 'Guidance', + + // Original text: "Action" + patchAction: 'Action', + + // Original text: "Applied patches" + hostAppliedPatches: 'Patches appliqués', + + // Original text: "Missing patches" + hostMissingPatches: 'Patches manquants', + + // Original text: "Host up-to-date!" + hostUpToDate: 'Hôte à jour !', + + // Original text: "Non-recommended patch install" + installPatchWarningTitle: 'Installation de patch non recommandée', + + // Original text: "This will install a patch only on this host. This is NOT the recommended way: please go into the Pool patch view and follow instructions there. If you are sure about this, you can continue anyway" + installPatchWarningContent: + "Installer un patch sur un hôte seul est déconseillé. Il est recommandé d'aller sur la page du pool et de faire l'installation sur tous les hôtes.", + + // Original text: "Go to pool" + installPatchWarningReject: 'Aller au pool', + + // Original text: "Install" + installPatchWarningResolve: 'Installer', + + // Original text: "Refresh patches" + refreshPatches: 'Rafraichir patchs', + + // Original text: "Install pool patches" + installPoolPatches: 'Installer les patchs sur le pool', + + // Original text: "Default SR" + defaultSr: 'SR par défaut', + + // Original text: "Set as default SR" + setAsDefaultSr: 'Définir comme SR par défaut', + + // Original text: "General" + generalTabName: 'Général', + + // Original text: "Stats" + statsTabName: 'Stats', + + // Original text: "Console" + consoleTabName: 'Console', + + // Original text: "Container" + containersTabName: 'Conteneur', + + // Original text: "Snapshots" + snapshotsTabName: 'Instantanés', + + // Original text: "Logs" + logsTabName: 'Journaux', + + // Original text: "Advanced" + advancedTabName: 'Avancé', + + // Original text: "Network" + networkTabName: 'Réseaux', + + // Original text: "Disk{disks, plural, one {} other {s}}" + disksTabName: 'Disque{disks, plural, one {} other {s}}', + + // Original text: "halted" + powerStateHalted: 'stoppé', + + // Original text: "running" + powerStateRunning: 'en cours', + + // Original text: "suspended" + powerStateSuspended: 'suspendu', + + // Original text: "No Xen tools detected" + vmStatus: 'Pas de Xen tools détectés', + + // Original text: "No IPv4 record" + vmName: "Pas d'enregistrement IPv4", + + // Original text: "No IP record" + vmDescription: "Pas d'enregistrement IP", + + // Original text: "Started {ago}" + vmSettings: 'Démarré il y a {ago}', + + // Original text: "Current status:" + vmCurrentStatus: 'État actuel :', + + // Original text: "Not running" + vmNotRunning: 'Éteinte', + + // Original text: "Halted {ago}" + vmHaltedSince: 'Stoppée {ago}', + + // Original text: "No Xen tools detected" + noToolsDetected: 'Pas de Xen tools détectés', + + // Original text: "No IPv4 record" + noIpv4Record: "Pas d'enregistrement IPv4", + + // Original text: "No IP record" + noIpRecord: "Pas d'enregistrement IP", + + // Original text: "Started {ago}" + started: 'Démarré {ago}', + + // Original text: "Paravirtualization (PV)" + paraVirtualizedMode: 'Paravirtualisation (PV)', + + // Original text: "Hardware virtualization (HVM)" + hardwareVirtualizedMode: 'Virtualisation matérielle (HVM)', + + // Original text: "CPU usage" + statsCpu: 'Utilisation CPU', + + // Original text: "Memory usage" + statsMemory: 'Utilisation mémoire', + + // Original text: "Network throughput" + statsNetwork: 'Échanges réseaux', + + // Original text: "Stacked values" + useStackedValuesOnStats: 'Valeurs cumulées', + + // Original text: "Disk throughput" + statDisk: 'Échanges disques', + + // Original text: "Last 10 minutes" + statLastTenMinutes: 'Les 10 dernières minutes', + + // Original text: "Last 2 hours" + statLastTwoHours: 'Les 2 dernières heures', + + // Original text: "Last week" + statLastWeek: 'La dernière semaine', + + // Original text: "Last year" + statLastYear: 'La dernière année', + + // Original text: "Copy" + copyToClipboardLabel: 'Copier', + + // Original text: "Ctrl+Alt+Del" + ctrlAltDelButtonLabel: 'Ctrl+Alt+Supp', + + // Original text: "Tip:" + tipLabel: 'Astuces :', + + // Original text: "Hide infos" + hideHeaderTooltip: 'Cacher les infos', + + // Original text: "Show infos" + showHeaderTooltip: 'Afficher les infos', + + // Original text: "Name" + containerName: 'Nom', + + // Original text: "Command" + containerCommand: 'Commande', + + // Original text: "Creation date" + containerCreated: 'Date de création', + + // Original text: "Status" + containerStatus: 'État', + + // Original text: "Action" + containerAction: 'Action', + + // Original text: "No existing containers" + noContainers: 'Aucun conteneur', + + // Original text: "Stop this container" + containerStop: 'Arrêter ce conteneur', + + // Original text: "Start this container" + containerStart: 'Démarrer ce conteneur', + + // Original text: "Pause this container" + containerPause: 'Mettre ce conteneur en pause', + + // Original text: "Resume this container" + containerResume: 'Relancer ce conteneur', + + // Original text: "Restart this container" + containerRestart: 'Redémarrer ce conteneur', + + // Original text: "Action" + vdiAction: 'Action', + + // Original text: "Attach disk" + vdiAttachDeviceButton: 'Attacher un disque', + + // Original text: "New disk" + vbdCreateDeviceButton: 'Nouveau disque', + + // Original text: "Boot order" + vdiBootOrder: 'Séquence de démarrage', + + // Original text: "Name" + vdiNameLabel: 'Nom', + + // Original text: "Description" + vdiNameDescription: 'Description', + + // Original text: "Pool" + vdiPool: 'Pool', + + // Original text: "Disconnect" + vdiDisconnect: 'Déconnecté', + + // Original text: "Tags" + vdiTags: 'Tags', + + // Original text: "Size" + vdiSize: 'Taille', + + // Original text: "SR" + vdiSr: 'SR', + + // Original text: "VM" + vdiVm: 'VM', + + // Original text: "Migrate VDI" + vdiMigrate: 'Migrer le VDI', + + // Original text: "Destination SR:" + vdiMigrateSelectSr: 'SR de destination :', + + // Original text: "Migrate all VDIs" + vdiMigrateAll: 'Migrer tous les VDIs', + + // Original text: "No SR" + vdiMigrateNoSr: 'Pas de SR', + + // Original text: "A target SR is required to migrate a VDI" + vdiMigrateNoSrMessage: 'Un SR cible est nécessaire pour migrer un VDI', + + // Original text: "Forget" + vdiForget: 'Oublier', + + // Original text: "Remove VDI" + vdiRemove: 'Supprimer le VDI', + + // Original text: "No VDIs attached to Control Domain" + noControlDomainVdis: 'Aucun VDI attaché au Control Domain', + + // Original text: "Boot flag" + vbdBootableStatus: 'Boot flag', + + // Original text: "Status" + vbdStatus: 'État', + + // Original text: "Connected" + vbdStatusConnected: 'Connecté', + + // Original text: "Disconnected" + vbdStatusDisconnected: 'Déconnecté', + + // Original text: "No disks" + vbdNoVbd: 'Pas de disque', + + // Original text: "Connect VBD" + vbdConnect: 'Connecter un VBD', + + // Original text: "Disconnect VBD" + vbdDisconnect: 'Déconnecter un VBD', + + // Original text: "Bootable" + vbdBootable: 'Bootable', + + // Original text: "Readonly" + vbdReadonly: 'Lecture seule', + + // Original text: "Action" + vbdAction: 'Action', + + // Original text: "Create" + vbdCreate: 'Créer', + + // Original text: "Disk name" + vbdNamePlaceHolder: 'Nom du disque', + + // Original text: "Size" + vbdSizePlaceHolder: 'Taille', + + // Original text: "CD drive not completely installed" + cdDriveNotInstalled: "Le lecteur CD n'est pas complètement installé", + + // Original text: "Stop and start the VM to install the CD drive" + cdDriveInstallation: 'Arrêtez et démarrez la VM pour installer le lecteur CD', + + // Original text: "Save" + saveBootOption: 'Enregistrer', + + // Original text: "Reset" + resetBootOption: 'Réinitialiser', + + // Original text: "New device" + vifCreateDeviceButton: 'Nouvelle interface', + + // Original text: "No interface" + vifNoInterface: "Pas d'interface", + + // Original text: "Device" + vifDeviceLabel: 'Device', + + // Original text: "MAC address" + vifMacLabel: 'Adresse MAC', + + // Original text: "MTU" + vifMtuLabel: 'MTU', + + // Original text: "Network" + vifNetworkLabel: 'Réseaux', + + // Original text: "Status" + vifStatusLabel: 'État', + + // Original text: "Connected" + vifStatusConnected: 'Connecté', + + // Original text: "Disconnected" + vifStatusDisconnected: 'Déconnecté', + + // Original text: "Connect" + vifConnect: 'Connecter', + + // Original text: "Disconnect" + vifDisconnect: 'Déconnecter', + + // Original text: "Remove" + vifRemove: 'Supprimer', + + // Original text: "IP addresses" + vifIpAddresses: 'Adresses IP', + + // Original text: "Auto-generated if empty" + vifMacAutoGenerate: 'Si vide, généré automatiquement', + + // Original text: "Allowed IPs" + vifAllowedIps: 'IPs autorisées', + + // Original text: "No IPs" + vifNoIps: "Pas d'IP", + + // Original text: "Network locked" + vifLockedNetwork: 'Réseau verrouillé', + + // Original text: "Network locked and no IPs are allowed for this interface" + vifLockedNetworkNoIps: + "Le réseau est verrouillé et aucune IP n'est autorisée sur cette interface", + + // Original text: "Network not locked" + vifUnLockedNetwork: 'Réseau non verrouillé', + + // Original text: "Unknown network" + vifUnknownNetwork: 'Réseau inconnu', + + // Original text: "Action" + vifAction: 'Action', + + // Original text: "Create" + vifCreate: 'Créer', + + // Original text: "No snapshots" + noSnapshots: "Pas d'instantané", + + // Original text: "New snapshot" + snapshotCreateButton: 'Nouvel instantané', + + // Original text: "Just click on the snapshot button to create one!" + tipCreateSnapshotLabel: + "Cliquer simplement sur le bouton d'instantané pour en créer un !", + + // Original text: "Revert VM to this snapshot" + revertSnapshot: 'Restaurer la MV à cet instantané', + + // Original text: "Remove this snapshot" + deleteSnapshot: 'Supprimer cet instantané', + + // Original text: "Create a VM from this snapshot" + copySnapshot: 'Créer une VM depuis cet instantané', + + // Original text: "Export this snapshot" + exportSnapshot: 'Exporter cet instantané', + + // Original text: "Creation date" + snapshotDate: 'Date de création', + + // Original text: "Name" + snapshotName: 'Nom', + + // Original text: "Name" + snapshotDescription: 'Description', + + // Original text: "Action" + snapshotAction: 'Action', + + // Original text: "Quiesced snapshot" + snapshotQuiesce: 'Instantané quiesce', + + // Original text: "Remove all logs" + logRemoveAll: 'Supprimer tous les journaux', + + // Original text: "No logs so far" + noLogs: 'Pas de journaux jusque là', + + // Original text: "Creation date" + logDate: 'Date de création', + + // Original text: "Name" + logName: 'Nom', + + // Original text: "Content" + logContent: 'Contenu', + + // Original text: "Action" + logAction: 'Action', + + // Original text: "Remove" + vmRemoveButton: 'Supprimer', + + // Original text: "Convert" + vmConvertButton: 'Convertir', + + // Original text: "Xen settings" + xenSettingsLabel: 'Configuration Xen', + + // Original text: "Guest OS" + guestOsLabel: 'OS invité', + + // Original text: "Misc" + miscLabel: 'Divers', + + // Original text: "UUID" + uuid: 'UUID', + + // Original text: "Virtualization mode" + virtualizationMode: 'Mode de virtualisation', + + // Original text: "CPU weight" + cpuWeightLabel: 'Pondération CPU', + + // Original text: "Default ({value, number})" + defaultCpuWeight: 'Défaut ({value, number})', + + // Original text: "CPU cap" + cpuCapLabel: 'Fonctionnalités CPU', + + // Original text: "Default ({value, number})" + defaultCpuCap: 'Défaut ({value, number})', + + // Original text: "PV args" + pvArgsLabel: 'PV params', + + // Original text: "Xen tools status" + xenToolsStatus: 'État des Xen tools', + + // Original text: "{status}" + xenToolsStatusValue: '{status}', + + // Original text: "OS name" + osName: "Nom de l'OS", + + // Original text: "OS kernel" + osKernel: "Kernel de l'OS", + + // Original text: "Auto power on" + autoPowerOn: 'Allumage automatique', + + // Original text: "HA" + ha: 'Haute Dispo', + + // Original text: "Affinity host" + vmAffinityHost: 'Hôte préféré', + + // Original text: "VGA" + vmVga: 'VGA', + + // Original text: "Video RAM" + vmVideoram: 'Mémoire vidéo', + + // Original text: "None" + noAffinityHost: 'Aucun', + + // Original text: "Original template" + originalTemplate: "Template d'origine", + + // Original text: "Unknown" + unknownOsName: 'Inconnu', + + // Original text: "Unknown" + unknownOsKernel: 'Inconnu', + + // Original text: "Unknown" + unknownOriginalTemplate: 'Inconnu', + + // Original text: "VM limits" + vmLimitsLabel: 'Limites de la VM', + + // Original text: "CPU limits" + vmCpuLimitsLabel: 'Limites de CPU', + + // Original text: "Topology" + vmCpuTopology: 'Topologie', + + // Original text: "Default behavior" + vmChooseCoresPerSocket: 'Comportement par défaut', + + // Original text: "{nSockets, number} socket{nSockets, plural, one {} other {s}} with {nCores, number} core{nCores, plural, one {} other {s}} per socket" + vmCoresPerSocket: + '{nSockets, number} socket{nSockets, plural, one {} other {s}} avec {nCores, number} cœur{nCores, plural, one {} other {s}} par socket', + + // Original text: "Incorrect cores per socket value" + vmCoresPerSocketIncorrectValue: 'Valeur incorrecte de cœurs par socket', + + // Original text: "Please change the selected value to fix it." + vmCoresPerSocketIncorrectValueSolution: 'Veuillez modifier la valeur.', + + // Original text: "Memory limits (min/max)" + vmMemoryLimitsLabel: 'Limites de mémoire (min/max)', + + // Original text: "vCPUs max:" + vmMaxVcpus: 'vCPUs max :', + + // Original text: "Memory max:" + vmMaxRam: 'Mémoire max :', + + // Original text: "Long click to add a name" + vmHomeNamePlaceholder: 'Clic long pour définir un nom', + + // Original text: "Long click to add a description" + vmHomeDescriptionPlaceholder: 'Clic long pour définir une description', + + // Original text: "Click to add a name" + vmViewNamePlaceholder: 'Cliquer pour définir un nom', + + // Original text: "Click to add a description" + vmViewDescriptionPlaceholder: 'Cliquer pour définir une description', + + // Original text: "Click to add a name" + templateHomeNamePlaceholder: 'Cliquer pour ajouter un nom', + + // Original text: "Click to add a description" + templateHomeDescriptionPlaceholder: 'Cliquer pour ajouter une description', + + // Original text: "Delete template" + templateDelete: 'Supprimer le template', + + // Original text: "Delete VM template{templates, plural, one {} other {s}}" + templateDeleteModalTitle: + 'Supprimer le(s) template{templates, plural, one {} other {s}} de VMs', + + // Original text: "Are you sure you want to delete {templates, plural, one {this} other {these}} template{templates, plural, one {} other {s}}?" + templateDeleteModalBody: + 'Êtes-vous sûr de vouloir supprimer ce(s) template(s) ?', + + // Original text: "Pool{pools, plural, one {} other {s}}" + poolPanel: 'Pool{pools, plural, one {} other {s}}', + + // Original text: "Host{hosts, plural, one {} other {s}}" + hostPanel: 'Hôte{hosts, plural, one {} other {s}}', + + // Original text: "VM{vms, plural, one {} other {s}}" + vmPanel: 'VM{vms, plural, one {} other {s}}', + + // Original text: "RAM Usage:" + memoryStatePanel: 'Utilisation RAM', + + // Original text: "CPUs Usage" + cpuStatePanel: 'Utilisation CPUs', + + // Original text: "VMs Power state" + vmStatePanel: 'Etats des VMs', + + // Original text: "Pending tasks" + taskStatePanel: 'Tâches en attente', + + // Original text: "Users" + usersStatePanel: 'Utilisateurs', + + // Original text: "Storage state" + srStatePanel: 'État du stockage', + + // Original text: "{usage} (of {total})" + ofUsage: '{usage} (sur {total})', + + // Original text: "No storage" + noSrs: 'Pas de stockage', + + // Original text: "Name" + srName: 'Nom', + + // Original text: "Pool" + srPool: 'Pool', + + // Original text: "Host" + srHost: 'Hôte', + + // Original text: "Type" + srFormat: 'Type', + + // Original text: "Size" + srSize: 'Taille', + + // Original text: "Usage" + srUsage: 'Usage', + + // Original text: "used" + srUsed: 'utilisé', + + // Original text: "free" + srFree: 'libre', + + // Original text: "Storage Usage" + srUsageStatePanel: 'Utilisation du stockage', + + // Original text: "Top 5 SR Usage (in %)" + srTopUsageStatePanel: "Top 5 d'utilisation des SRs (en %)", + + // Original text: "{running, number} running ({halted, number} halted)" + vmsStates: + '{running} allumée{halted, plural, one {} other {s}} ({halted} éteinte{halted, plural, one {} other {s}})', + + // Original text: "Clear selection" + dashboardStatsButtonRemoveAll: 'Vider la sélection', + + // Original text: "Add all hosts" + dashboardStatsButtonAddAllHost: 'Ajouter tous les hôtes', + + // Original text: "Add all VMs" + dashboardStatsButtonAddAllVM: 'Ajouter toutes les VMs', + + // Original text: "{value} {date, date, medium}" + weekHeatmapData: '{value} {date, date, medium}', + + // Original text: "No data." + weekHeatmapNoData: 'Pas de données.', + + // Original text: "Weekly Heatmap" + weeklyHeatmap: 'Heatmap hebdomadaire', + + // Original text: "Weekly Charts" + weeklyCharts: 'Graphes hebdomadaires', + + // Original text: "Synchronize scale:" + weeklyChartsScaleInfo: 'Synchroniser les échelles :', + + // Original text: "Stats error" + statsDashboardGenericErrorTitle: 'Erreurs de stats', + + // Original text: "There is no stats available for:" + statsDashboardGenericErrorMessage: 'Pas de statistiques disponibles pour :', + + // Original text: "No selected metric" + noSelectedMetric: 'Pas de métrique sélectionnée.', + + // Original text: "Select" + statsDashboardSelectObjects: 'Sélectionner', + + // Original text: "Loading…" + metricsLoading: 'Chargement en cours…', + + // Original text: "Coming soon!" + comingSoon: "C'est pour bientôt !", + + // Original text: "Orphaned snapshot VDIs" + orphanedVdis: 'Instantanés de VDIs orphelins', + + // Original text: "Orphaned VMs snapshot" + orphanedVms: 'Instantanés VMs orphelins', + + // Original text: "No orphans" + noOrphanedObject: "Pas d'orphelin", + + // Original text: "Remove all orphaned snapshot VDIs" + removeAllOrphanedObject: 'Supprimer tous les snapshots de VDIs orphelins', + + // Original text: "VDIs attached to Control Domain" + vdisOnControlDomain: 'VDIs attachés au Control Domain', + + // Original text: "Name" + vmNameLabel: 'Nom', + + // Original text: "Description" + vmNameDescription: 'Description', + + // Original text: "Resident on" + vmContainer: 'Situé sur', + + // Original text: "Alarms" + alarmMessage: 'Alarmes', + + // Original text: "No alarms" + noAlarms: "Pas d'alarmes", + + // Original text: "Date" + alarmDate: 'Date', + + // Original text: "Content" + alarmContent: 'Contenu', + + // Original text: "Issue on" + alarmObject: 'Concerné', + + // Original text: "Pool" + alarmPool: 'Pool', + + // Original text: "Remove all alarms" + alarmRemoveAll: 'Supprimer toutes les alarmes', + + // Original text: "{used}% used ({free} left)" + spaceLeftTooltip: '{used}% utilisés ({free} restants)', + + // Original text: "Create a new VM on {select}" + newVmCreateNewVmOn: 'Créer une nouvelle VM sur {select}', + + // Original text: "Create a new VM on {select1} or {select2}" + newVmCreateNewVmOn2: 'Créer une nouvelle VM sur {select1} ou {select2}', + + // Original text: "You have no permission to create a VM" + newVmCreateNewVmNoPermission: "Vous n'avez pas les droits pour créer une VM", + + // Original text: "Infos" + newVmInfoPanel: 'Infos', + + // Original text: "Name" + newVmNameLabel: 'Nom', + + // Original text: "Template" + newVmTemplateLabel: 'Template', + + // Original text: "Description" + newVmDescriptionLabel: 'Description', + + // Original text: "Performances" + newVmPerfPanel: 'Performances', + + // Original text: "vCPUs" + newVmVcpusLabel: 'vCPUs', + + // Original text: "RAM" + newVmRamLabel: 'RAM', + + // Original text: "Static memory max" + newVmStaticMaxLabel: 'Mémoire fixe max', + + // Original text: "Dynamic memory min" + newVmDynamicMinLabel: 'Mémoire dynamique min', + + // Original text: "Dynamic memory max" + newVmDynamicMaxLabel: 'Mémoire dynamique max', + + // Original text: "Install settings" + newVmInstallSettingsPanel: "Paramètres d'installation", + + // Original text: "ISO/DVD" + newVmIsoDvdLabel: 'ISO/DVD', + + // Original text: "Network" + newVmNetworkLabel: 'Réseau', + + // Original text: "e.g: http://httpredir.debian.org/debian" + newVmInstallNetworkPlaceHolder: 'ex : http://httpredir.debian.org/debian', + + // Original text: "PV Args" + newVmPvArgsLabel: 'PV Args', + + // Original text: "PXE" + newVmPxeLabel: 'PXE', + + // Original text: "Interfaces" + newVmInterfacesPanel: 'Interfaces', + + // Original text: "MAC" + newVmMacLabel: 'MAC', + + // Original text: "Add interface" + newVmAddInterface: 'Ajouter interface', + + // Original text: "Disks" + newVmDisksPanel: 'Disques', + + // Original text: "SR" + newVmSrLabel: 'SR', + + // Original text: "Size" + newVmSizeLabel: 'Taille', + + // Original text: "Add disk" + newVmAddDisk: 'Ajouter un disque', + + // Original text: "Summary" + newVmSummaryPanel: 'Récapitulatif', + + // Original text: "Create" + newVmCreate: 'Créer', + + // Original text: "Reset" + newVmReset: 'Réinitialiser', + + // Original text: "Select template" + newVmSelectTemplate: 'Sélectionner un template', + + // Original text: "SSH key" + newVmSshKey: 'Clef SSH', + + // Original text: "Config drive" + newVmConfigDrive: 'Config drive', + + // Original text: "Custom config" + newVmCustomConfig: 'Configuration personnalisée', + + // Original text: "Boot VM after creation" + newVmBootAfterCreate: 'Démarrer la VM après sa création', + + // Original text: "Auto-generated if empty" + newVmMacPlaceholder: 'Si vide, généré automatiquement', + + // Original text: "CPU weight" + newVmCpuWeightLabel: 'Pondération CPU', + + // Original text: "Default: {value, number}" + newVmDefaultCpuWeight: 'Par défaut: {value, number}', + + // Original text: "CPU cap" + newVmCpuCapLabel: 'Fonctionnalités CPU', + + // Original text: "Default: {value, number}" + newVmDefaultCpuCap: 'Par défaut : {value, number}', + + // Original text: "Cloud config" + newVmCloudConfig: 'Configuration Cloud', + + // Original text: "Create VMs" + newVmCreateVms: 'Créer les VMs', + + // Original text: "Are you sure you want to create {nbVms, number} VMs?" + newVmCreateVmsConfirm: 'Êtes-vous sûr de vouloir créer {nbVms} VMs ?', + + // Original text: "Multiple VMs:" + newVmMultipleVms: 'Multiples VMs :', + + // Original text: "Select a resource set:" + newVmSelectResourceSet: 'Choisir un jeu de ressources :', + + // Original text: "Name pattern:" + newVmMultipleVmsPattern: 'Motif de nom :', + + // Original text: "e.g.: \\{name\\}_%" + newVmMultipleVmsPatternPlaceholder: 'ex. : \\{name\\}_%', + + // Original text: "First index:" + newVmFirstIndex: 'Première itération :', + + // Original text: "Recalculate VMs number" + newVmNumberRecalculate: 'Recalculer le nombre des VMs', + + // Original text: "Refresh VMs name" + newVmNameRefresh: 'Rafraîchir le nom des VMs', + + // Original text: "Affinity host" + newVmAffinityHost: 'Hôte préféré', + + // Original text: "Advanced" + newVmAdvancedPanel: 'Avancé', + + // Original text: "Show advanced settings" + newVmShowAdvanced: 'Afficher les paramètres avancés', + + // Original text: "Hide advanced settings" + newVmHideAdvanced: 'Cacher les paramètres avancés', + + // Original text: "Share this VM" + newVmShare: 'Partager cette VM', + + // Original text: "Resource sets" + resourceSets: 'Jeu de ressources', + + // Original text: "No resource sets." + noResourceSets: 'Pas de jeu de ressources.', + + // Original text: "Loading resource sets" + loadingResourceSets: 'Chargement des jeux de ressources…', + + // Original text: "Resource set name" + resourceSetName: 'Nom du jeu de ressources', + + // Original text: "Recompute all limits" + recomputeResourceSets: 'Recalculer les limites', + + // Original text: "Save" + saveResourceSet: 'Enregistrer', + + // Original text: "Reset" + resetResourceSet: 'Réinitialiser', + + // Original text: "Edit" + editResourceSet: 'Éditer', + + // Original text: "Delete" + deleteResourceSet: 'Supprimer', + + // Original text: "Delete resource set" + deleteResourceSetWarning: 'Supprimer le jeu de ressources', + + // Original text: "Are you sure you want to delete this resource set?" + deleteResourceSetQuestion: + 'Êtes-vous sûr de vouloir supprimer ce jeu de ressources ?', + + // Original text: "Missing objects:" + resourceSetMissingObjects: 'Objets manquants :', + + // Original text: "vCPUs" + resourceSetVcpus: 'vCPUs', + + // Original text: "Memory" + resourceSetMemory: 'Mémoire', + + // Original text: "Storage" + resourceSetStorage: 'Stockage', + + // Original text: "Unknown" + unknownResourceSetValue: 'Inconnu', + + // Original text: "Available hosts" + availableHosts: 'Hôtes disponibles', + + // Original text: "Excluded hosts" + excludedHosts: 'Hôtes exclus', + + // Original text: "No hosts available." + noHostsAvailable: "Pas d'hôte disponible.", + + // Original text: "VMs created from this resource set shall run on the following hosts." + availableHostsDescription: + 'Les VMs créées sur ce jeu de ressources doivent être démarrées sur les hôtes suivants.', + + // Original text: "Maximum CPUs" + maxCpus: 'CPUs maximum', + + // Original text: "Maximum RAM (GiB)" + maxRam: 'RAM maximum (GiB)', + + // Original text: "Maximum disk space" + maxDiskSpace: 'Espace disque maximum', + + // Original text: "IP pool" + ipPool: "Plages d'IPs", + + // Original text: "Quantity" + quantity: 'Quantité', + + // Original text: "No limits." + noResourceSetLimits: 'Pas de limites.', + + // Original text: "Total:" + totalResource: 'Total :', + + // Original text: "Remaining:" + remainingResource: 'Restant :', + + // Original text: "Used:" + usedResource: 'Utilisé :', + + // Original text: "New" + resourceSetNew: 'Nouvelle', + + // Original text: "Try dropping some VMs files here, or click to select VMs to upload. Accept only .xva/.ova files." + importVmsList: + 'Essayez de déposer des fichiers de VMs ici, ou bien cliquez pour sélectionner des VMs à téléverser. Seuls les fichiers .xva/.ova sont acceptés.', + + // Original text: "No selected VMs." + noSelectedVms: 'Pas de VM sélectionnée.', + + // Original text: "To Pool:" + vmImportToPool: 'Sur le Pool:', + + // Original text: "To SR:" + vmImportToSr: 'Sur le SR:', + + // Original text: "VMs to import" + vmsToImport: 'VMs à importer', + + // Original text: "Reset" + importVmsCleanList: 'Réinitialiser', + + // Original text: "VM import success" + vmImportSuccess: 'Import de VM réussi', + + // Original text: "VM import failed" + vmImportFailed: 'Import de VM échoué', + + // Original text: "Import starting…" + startVmImport: "L'import commence…", + + // Original text: "Export starting…" + startVmExport: "L'export commence…", + + // Original text: "N CPUs" + nCpus: 'N CPUs', + + // Original text: "Memory" + vmMemory: 'Mémoire', + + // Original text: "Disk {position} ({capacity})" + diskInfo: 'Disque {position} ({capacity})', + + // Original text: "Disk description" + diskDescription: 'Description du disque', + + // Original text: "No disks." + noDisks: 'Pas de disque.', + + // Original text: "No networks." + noNetworks: 'Pas de réseau.', + + // Original text: "Network {name}" + networkInfo: 'Réseau {name}', + + // Original text: "No description available" + noVmImportErrorDescription: 'Pas de description disponible', + + // Original text: "Error:" + vmImportError: 'Erreur :', + + // Original text: "{type} file:" + vmImportFileType: '{type} fichier:', + + // Original text: "Please to check and/or modify the VM configuration." + vmImportConfigAlert: + 'Merci de vérifier et/ou modifier la configuration de la VM.', + + // Original text: "No pending tasks" + noTasks: 'Pas de tâche en attente', + + // Original text: "Currently, there are not any pending XenServer tasks" + xsTasks: "Actuellement, il n'y a aucune tâche en attente", + + // Original text: "Schedules" + backupSchedules: 'Planifier', + + // Original text: "Get remote" + getRemote: 'Récupérer les emplacements', + + // Original text: "List Remote" + listRemote: 'Lister les emplacements', + + // Original text: "simple" + simpleBackup: 'simple', + + // Original text: "delta" + delta: 'delta', + + // Original text: "Restore Backups" + restoreBackups: 'Restauration de sauvegardes', + + // Original text: "Click on a VM to display restore options" + restoreBackupsInfo: + 'Cliquez sur une VM pour afficher les options de récupération', + + // Original text: "Only the files of Delta Backup which are not on a SMB remote can be restored" + restoreDeltaBackupsInfo: + 'Seuls les fichiers de Delta Backup qui ne sont pas sur un emplacement SMB peuvent être restaurés', + + // Original text: "Enabled" + remoteEnabled: 'activé', + + // Original text: "Error" + remoteError: 'Erreur', + + // Original text: "No backup available" + noBackup: 'Pas de sauvegarde disponible', + + // Original text: "VM Name" + backupVmNameColumn: 'Nom de la VM', + + // Original text: "Tags" + backupTags: 'Tags', + + // Original text: "Last Backup" + lastBackupColumn: 'Dernière sauvegarde', + + // Original text: "Available Backups" + availableBackupsColumn: 'Sauvegardes disponibles', + + // Original text: "Missing parameters" + backupRestoreErrorTitle: 'Paramètres manquants', + + // Original text: "Choose a SR and a backup" + backupRestoreErrorMessage: 'Choisir un SR et une sauvegarde', + + // Original text: "Select default SR…" + backupRestoreSelectDefaultSr: 'Sélectionner le SR par défaut…', + + // Original text: "Choose a SR for each VDI" + backupRestoreChooseSrForEachVdis: 'Choisir un SR pour chaque VDI', + + // Original text: "VDI" + backupRestoreVdiLabel: 'VDI', + + // Original text: "SR" + backupRestoreSrLabel: 'SR', + + // Original text: "Display backups" + displayBackup: 'Afficher les sauvegardes', + + // Original text: "Import VM" + importBackupTitle: 'Importer une VM', + + // Original text: "Starting your backup import" + importBackupMessage: "Démarrer l'import d'une sauvegarde", + + // Original text: "VMs to backup" + vmsToBackup: 'VMs à sauvegarder', + + // Original text: "List remote backups" + listRemoteBackups: 'Lister les emplacements de sauvegardes', + + // Original text: "Restore backup files" + restoreFiles: 'Restaurer les fichiers de sauvegarde', + + // Original text: "Invalid options" + restoreFilesError: 'Options invalides', + + // Original text: "Restore file from {name}" + restoreFilesFromBackup: 'Restaurer les fichiers depuis {name}', + + // Original text: "Select a backup…" + restoreFilesSelectBackup: 'Sélectionner une sauvegarde…', + + // Original text: "Select a disk…" + restoreFilesSelectDisk: 'Sélectionner un disque…', + + // Original text: "Select a partition…" + restoreFilesSelectPartition: 'Sélectionner un partition…', + + // Original text: "Folder path" + restoreFilesSelectFolderPath: 'Chemin du dossier', + + // Original text: "Select a file…" + restoreFilesSelectFiles: 'Sélectionner un fichier…', + + // Original text: "Content not found" + restoreFileContentNotFound: 'Contenu non trouvé', + + // Original text: "No files selected" + restoreFilesNoFilesSelected: 'Pas de fichier sélectionné', + + // Original text: "Selected files ({files}):" + restoreFilesSelectedFiles: 'Fichiers sélectionnés ({files}) :', + + // Original text: "Error while scanning disk" + restoreFilesDiskError: 'Erreur lors du scan du disque', + + // Original text: "Select all this folder's files" + restoreFilesSelectAllFiles: 'Sélectionner tous les fichiers de ce dossier', + + // Original text: "Unselect all files" + restoreFilesUnselectAll: 'Déselectionner tous les fichiers', + + // Original text: "Emergency shutdown Host{nHosts, plural, one {} other {s}}" + emergencyShutdownHostsModalTitle: + "Extinction d'urgence {nHosts, plural, one {de l'hôte} other {des hôtes}}", + + // Original text: "Are you sure you want to shutdown {nHosts, number} Host{nHosts, plural, one {} other {s}}?" + emergencyShutdownHostsModalMessage: + 'Êtes-vous sûr de vouloir arrêter {nHosts} hôte{nHosts, plural, one {} other {s}}?', + + // Original text: "Shutdown host" + stopHostModalTitle: "Arrêter l'hôte", + + // Original text: "This will shutdown your host. Do you want to continue? If it's the pool master, your connection to the pool will be lost" + stopHostModalMessage: + "Vous allez éteindre cet hôte. Voulez-vous continuer ? Si c'est le Maître du Pool, la connexion à tout le Pool sera perdue.", + + // Original text: "Add host" + addHostModalTitle: 'Ajouter un hôte', + + // Original text: "Are you sure you want to add {host} to {pool}?" + addHostModalMessage: 'Êtes-vous sûr de vouloir ajouter {host} à {pool}?', + + // Original text: "Restart host" + restartHostModalTitle: "Redémarrer l'hôte", + + // Original text: "This will restart your host. Do you want to continue?" + restartHostModalMessage: + 'Votre hôte va devoir redémarrer. Voulez-vous continuer ?', + + // Original text: "Restart Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}" + restartHostsAgentsModalTitle: + "Redémarrer les agents {nHosts, plural, one {de l'hôte} other {des hôtes}}", + + // Original text: "Are you sure you want to restart {nHosts, number} Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}?" + restartHostsAgentsModalMessage: + "Êtes-vous sûr de vouloir redémarrer les agents {nHosts, plural, one {de l'hôte} other {des hôtes}} ?", + + // Original text: "Restart Host{nHosts, plural, one {} other {s}}" + restartHostsModalTitle: + "Redémarrer {nHosts, plural, one {l'} other {les}} hôte{nHosts, plural, one {} other {s}}", + + // Original text: "Are you sure you want to restart {nHosts, number} Host{nHosts, plural, one {} other {s}}?" + restartHostsModalMessage: + "Êtes-vous sûr de vouloir redémarrer {nHosts, plural, one {l'} other {les}} hôte{nHosts, plural, one {} other {s}} ?", + + // Original text: "Start VM{vms, plural, one {} other {s}}" + startVmsModalTitle: + 'Démarrer {vms, plural, one {la} other {les}} VM{vms, plural, one {} other {s}}', + + // Original text: "Start a copy" + cloneAndStartVM: 'Démarrer une copie', + + // Original text: "Force start" + forceStartVm: 'Forcer le démarrage', + + // Original text: "Forbidden operation" + forceStartVmModalTitle: 'Opération non autorisée', + + // Original text: "Start operation for this vm is blocked." + blockedStartVmModalMessage: 'Le démarrage est bloqué pour cette VM.', + + // Original text: "Forbidden operation start for {nVms, number} vm{nVms, plural, one {} other {s}}." + blockedStartVmsModalMessage: + 'Démarrage non autorisé pour {nVms, number} VM{nVms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to start {vms, number} VM{vms, plural, one {} other {s}}?" + startVmsModalMessage: + 'Êtes-vous sûr de vouloir démarrer {vms, plural, one {la} other {les}} {vms} VM{vms, plural, one {} other {s}} ?', + + // Original text: "{nVms, number} vm{nVms, plural, one {} other {s}} are failed. Please see your logs to get more information" + failedVmsErrorMessage: + "{nVms, number} VM{nVms, plural, one {} other {s}} ont échoué. Veuillez consulter les journaux pour plus d'informations", + + // Original text: "Start failed" + failedVmsErrorTitle: 'Echec du démarrage', + + // Original text: "Stop Host{nHosts, plural, one {} other {s}}" + stopHostsModalTitle: + "Arrêter {nHosts, plural, one {l'} other {les}} hôte{nHosts, plural, one {} other {s}}", + + // Original text: "Are you sure you want to stop {nHosts, number} Host{nHosts, plural, one {} other {s}}?" + stopHostsModalMessage: + "Êtes-vous sûr de vouloir arrêter {nHosts, plural, one {l'} other {les}} hôte{nHosts, plural, one {} other {s}} ?", + + // Original text: "Stop VM{vms, plural, one {} other {s}}" + stopVmsModalTitle: + 'Éteindre {vms, plural, one {cette} other {ces}} VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to stop {vms, number} VM{vms, plural, one {} other {s}}?" + stopVmsModalMessage: + 'Êtes-vous sûr de vouloir éteindre {vms, plural, one {cette} other {ces}} {vms} VM{vms, plural, one {} other {s}} ?', + + // Original text: "Restart VM" + restartVmModalTitle: 'Redémarrer la VM', + + // Original text: "Are you sure you want to restart {name}?" + restartVmModalMessage: 'Êtes-vous sûr de vouloir redémarrer {name}?', + + // Original text: "Stop VM" + stopVmModalTitle: 'Arrêter la VM', + + // Original text: "Are you sure you want to stop {name}?" + stopVmModalMessage: 'Êtes-vous sûr de vouloir arrêter {name}?', + + // Original text: "Restart VM{vms, plural, one {} other {s}}" + restartVmsModalTitle: + 'Redémarrer {vms, plural, one {la} other {les}} VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to restart {vms, number} VM{vms, plural, one {} other {s}}?" + restartVmsModalMessage: + 'Êtes-vous sûr de vouloir redémarrer {vms, plural, one {la} other {les}} VM{vms, plural, one {} other {s}} {vms} ?', + + // Original text: "Snapshot VM{vms, plural, one {} other {s}}" + snapshotVmsModalTitle: + 'Faire un instantané {vms, plural, one {de la} other {des}} VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to snapshot {vms, number} VM{vms, plural, one {} other {s}}?" + snapshotVmsModalMessage: + 'Êtes-vous sûr de vouloir faire un instantané {vms, plural, one {de la VM} other {des {vms} VMs}} ?', + + // Original text: "Delete VM{vms, plural, one {} other {s}}" + deleteVmsModalTitle: + 'Supprimer {vms, plural, one {la} other {les}} VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to delete {vms, number} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED" + deleteVmsModalMessage: + 'Êtes-vous sûr de vouloir supprimer {vms, plural, one {la VM} other {les {vms} VMs}} ? TOUS LES DISQUES ASSOCIÉS SERONT SUPPRIMÉS', + + // Original text: "Delete VM" + deleteVmModalTitle: 'Supprimer la VM', + + // Original text: "Are you sure you want to delete this VM? ALL VM DISKS WILL BE REMOVED" + deleteVmModalMessage: + 'Êtes-vous sûr de vouloir supprimer cette VM ? TOUS LES DISQUES DE LA VM SERONT SUPPRIMÉS DEFINITIVEMENT', + + // Original text: "Migrate VM" + migrateVmModalTitle: 'Migrer la VM', + + // Original text: "Select a destination host:" + migrateVmSelectHost: 'Sélectionner un hôte de destination :', + + // Original text: "Select a migration network:" + migrateVmSelectMigrationNetwork: 'Choisir un réseau de migration :', + + // Original text: "For each VIF, select a network:" + migrateVmSelectNetworks: 'Pour chaque VIF, choisir un réseau :', + + // Original text: "Select a destination SR:" + migrateVmsSelectSr: 'Sélectionner un SR de destination :', + + // Original text: "Select a destination SR for local disks:" + migrateVmsSelectSrIntraPool: + 'Choisir un SR de destination pour les disques locaux :', + + // Original text: "Select a network on which to connect each VIF:" + migrateVmsSelectNetwork: 'Choisir un réseau pour chaque VIF :', + + // Original text: "Smart mapping" + migrateVmsSmartMapping: 'Réaffectation intelligente', + + // Original text: "VIF" + migrateVmVif: 'VIF', + + // Original text: "Network" + migrateVmNetwork: 'Réseaux', + + // Original text: "No target host" + migrateVmNoTargetHost: "Pas d'hôte cible", + + // Original text: "A target host is required to migrate a VM" + migrateVmNoTargetHostMessage: + 'Un hôte cible est nécessaire pour migrer une VM', + + // Original text: "No default SR" + migrateVmNoDefaultSrError: 'Pas de SR par défaut', + + // Original text: "Default SR not connected to host" + migrateVmNotConnectedDefaultSrError: + "Le SR par défaut n'est pas connecté à l'hôte", + + // Original text: "For each VDI, select an SR:" + chooseSrForEachVdisModalSelectSr: 'Pour chaque VDI, sélectionner un SR :', + + // Original text: "Select main SR…" + chooseSrForEachVdisModalMainSr: 'Sélectionner le SR principal…', + + // Original text: "VDI" + chooseSrForEachVdisModalVdiLabel: 'VDI', + + // Original text: "SR*" + chooseSrForEachVdisModalSrLabel: 'SR*', + + // Original text: "* optional" + chooseSrForEachVdisModalOptionalEntry: '* optionnel', + + // Original text: "Delete VDI" + deleteVdiModalTitle: 'Supprimer le VDI', + + // Original text: "Are you sure you want to delete this disk? ALL DATA ON THIS DISK WILL BE LOST" + deleteVdiModalMessage: + 'Êtes-vous sûr de vouloir supprimer ce disque ? TOUTES LES DONNÉES CONTENUES SERONT PERDUES IRRÉMÉDIABLEMENT', + + // Original text: "Revert your VM" + revertVmModalTitle: 'Restaurer la VM', + + // Original text: "Delete snapshot" + deleteSnapshotModalTitle: "Supprimer l'instantané", + + // Original text: "Are you sure you want to delete this snapshot?" + deleteSnapshotModalMessage: + 'Êtes-vous sûr de vouloir supprimer cet instantané ?', + + // Original text: "Are you sure you want to revert this VM to the snapshot state? This operation is irreversible." + revertVmModalMessage: + "Êtes-vous sûr de vouloir restaurer cette VM à l'état de cet instantané ? Cette opération est irrévocable.", + + // Original text: "Snapshot before" + revertVmModalSnapshotBefore: 'Faire un instantané avant', + + // Original text: "Import a {name} Backup" + importBackupModalTitle: 'Importer une sauvegarde de {name}', + + // Original text: "Start VM after restore" + importBackupModalStart: 'Démarrer la VM après la restauration', + + // Original text: "Select your backup…" + importBackupModalSelectBackup: 'Sélectionnez votre sauvegarde…', + + // Original text: "Are you sure you want to remove all orphaned snapshot VDIs?" + removeAllOrphanedModalWarning: + 'Êtes-vous sûr de vouloir supprimer tous les instantanés de VDIs orphelins ?', + + // Original text: "Remove all logs" + removeAllLogsModalTitle: 'Supprimer tous les journaux', + + // Original text: "Are you sure you want to remove all logs?" + removeAllLogsModalWarning: + 'Êtes-vous sûr de vouloir supprimer tous les journaux ?', + + // Original text: "This operation is definitive." + definitiveMessageModal: 'Cette action est irréversible.', + + // Original text: "Previous SR Usage" + existingSrModalTitle: 'Emplacement utilisé', + + // Original text: "This path has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingSrModalText: + 'Cet emplacement avait été utilisé auparavant comme un Stockage par un hôte XenServer. Toutes les données présentes seront perdues si vous décidez de continuer la création du SR.', + + // Original text: "Previous LUN Usage" + existingLunModalTitle: 'LUN utilisé', + + // Original text: "This LUN has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingLunModalText: + 'Ce LUN avait été utilisé auparavant comme un Stockage par un hôte XenServer. Toutes les données présentes seront perdues si vous décidez de continuer la création du SR.', + + // Original text: "Replace current registration?" + alreadyRegisteredModal: "Remplacer l'enregistrement actuel ?", + + // Original text: "Your XO appliance is already registered to {email}, do you want to forget and replace this registration ?" + alreadyRegisteredModalText: + 'Votre instance XOA est déjà enregistrée pour {email}, voulez-vous remplacer cet enregistrement ?', + + // Original text: "Ready for trial?" + trialReadyModal: "Prêt pour l'essai ?", + + // Original text: "During the trial period, XOA need to have a working internet connection. This limitation does not apply for our paid plans!" + trialReadyModalText: + "Durant la période de démonstration, XOA nécessite une connexion internet fonctionnelle. Cette limitation disparaît avec la souscription d'une de nos formules.", + + // Original text: "Label" + serverLabel: 'Nom', + + // Original text: "Host" + serverHost: 'Hôte', + + // Original text: "Username" + serverUsername: "Nom d'utilisateur", + + // Original text: "Password" + serverPassword: 'Mot de passe', + + // Original text: "Action" + serverAction: 'Action', + + // Original text: "Read Only" + serverReadOnly: 'Lecture seule', + + // Original text: "Unauthorized Certificates" + serverUnauthorizedCertificates: 'Certificats non approuvés', + + // Original text: "Allow Unauthorized Certificates" + serverAllowUnauthorizedCertificates: + 'Autoriser les certificats non approuvés', + + // Original text: "Enable it if your certificate is rejected, but it's not recommended because your connection will not be secured." + serverUnauthorizedCertificatesInfo: + "Activez ceci si votre certificat est rejeté, mais ce n'est pas recommandé car votre connexion ne sera pas sécurisée.", + + // Original text: "Disconnect server" + serverDisconnect: 'Déconnecter le serveur', + + // Original text: "username" + serverPlaceHolderUser: "nom d'utilisateur", + + // Original text: "password" + serverPlaceHolderPassword: 'mot de passe', + + // Original text: "address[:port]" + serverPlaceHolderAddress: 'adresse[:port]', + + // Original text: "label" + serverPlaceHolderLabel: 'nom', + + // Original text: "Connect" + serverConnect: 'Connecter', + + // Original text: "Error" + serverError: 'Erreur', + + // Original text: "Adding server failed" + serverAddFailed: "Echec de l'ajout du serveur", + + // Original text: "Status" + serverStatus: 'Statut', + + // Original text: "Connection failed. Click for more information." + serverConnectionFailed: + "Echec de connexion. Cliquer pour plus d'informations.", + + // Original text: "Connecting…" + serverConnecting: 'Connexion…', + + // Original text: "Connected" + serverConnected: 'Connecté', + + // Original text: "Disconnected" + serverDisconnected: 'Déconnecté', + + // Original text: "Authentication error" + serverAuthFailed: "Erreur d'authentification", + + // Original text: "Unknown error" + serverUnknownError: 'Erreur inconnue', + + // Original text: "Invalid self-signed certificate" + serverSelfSignedCertError: 'Certificat auto-signé rejeté', + + // Original text: "Do you want to accept self-signed certificate for this server even though it would decrease security?" + serverSelfSignedCertQuestion: + 'Voulez-vous accepter un certificat auto-signé pour ce serveur même si cela réduit la sécurité ?', + + // Original text: "Copy VM" + copyVm: 'Copier la VM', + + // Original text: "Are you sure you want to copy this VM to {SR}?" + copyVmConfirm: 'Êtes-vous sûr de vouloir copier cette VM vers {SR}?', + + // Original text: "Name" + copyVmName: 'Nom', + + // Original text: "Name pattern" + copyVmNamePattern: 'Motif de nom', + + // Original text: "If empty: name of the copied VM" + copyVmNamePlaceholder: 'Si vide : nom de la VM', + + // Original text: "e.g.: \"\\{name\\}_COPY\"" + copyVmNamePatternPlaceholder: 'ex. : "\\{name\\}_COPY"', + + // Original text: "Select SR" + copyVmSelectSr: 'Sélectionner le SR', + + // Original text: "Use compression" + copyVmCompress: 'Utiliser la compression', + + // Original text: "No target SR" + copyVmsNoTargetSr: 'Pas de SR cible', + + // Original text: "A target SR is required to copy a VM" + copyVmsNoTargetSrMessage: 'Un SR cible est nécessaire pour copier une VM', + + // Original text: "Detach host" + detachHostModalTitle: "Détacher l'hôte", + + // Original text: "Are you sure you want to detach {host} from its pool? THIS WILL REMOVE ALL VMs ON ITS LOCAL STORAGE AND REBOOT THE HOST." + detachHostModalMessage: + "Êtes-vous sûr de vouloir détacher {host} de son pool? CELA SUPPRIMERA TOUTES LES VMs DE SON STOCKAGE LOCAL ET REDÉMARRERA L'HÔTE.", + + // Original text: "Detach" + detachHost: 'Détacher', + + // Original text: "Forget host" + forgetHostModalTitle: "Oublier l'hôte", + + // Original text: "Are you sure you want to forget {host} from its pool? Be sure this host can't be back online, or use detach instead." + forgetHostModalMessage: + 'Êtes-vous sûr de vouloir oublier {host} de son pool ? Soyez certain que cet hôte ne peut pas être de retour en ligne ou utilisez "Détacher" à la place.', + + // Original text: "Forget" + forgetHost: 'Oublier', + + // Original text: "Create network" + newNetworkCreate: 'Créer un réseau', + + // Original text: "Create bonded network" + newBondedNetworkCreate: 'Créer un réseau agrégé', + + // Original text: "Interface" + newNetworkInterface: 'Interface', + + // Original text: "Name" + newNetworkName: 'Nom', + + // Original text: "Description" + newNetworkDescription: 'Description', + + // Original text: "VLAN" + newNetworkVlan: 'VLAN', + + // Original text: "No VLAN if empty" + newNetworkDefaultVlan: 'Si vide, pas de VLAN', + + // Original text: "MTU" + newNetworkMtu: 'MTU', + + // Original text: "Default: 1500" + newNetworkDefaultMtu: 'Défaut : 1500', + + // Original text: "Name required" + newNetworkNoNameErrorTitle: 'Un nom est nécessaire', + + // Original text: "A name is required to create a network" + newNetworkNoNameErrorMessage: 'Un nom est nécessaire pour créer un réseau', + + // Original text: "Bond mode" + newNetworkBondMode: 'Mode agrégé', + + // Original text: "Delete network" + deleteNetwork: 'Supprimer le réseau', + + // Original text: "Are you sure you want to delete this network?" + deleteNetworkConfirm: 'Êtes-vous sûr de vouloir supprimer ce réseau ?', + + // Original text: "This network is currently in use" + networkInUse: "Ce réseau est en cours d'utilisation", + + // Original text: "Bonded" + pillBonded: 'Agrégé', + + // Original text: "Host" + addHostSelectHost: 'Hôte', + + // Original text: "No host" + addHostNoHost: "Pas d'hôte", + + // Original text: "No host selected to be added" + addHostNoHostMessage: 'Aucun hôte sélectionné pour ajout', + + // Original text: "Xen Orchestra" + xenOrchestra: 'Xen Orchestra', + + // Original text: "Xen Orchestra server" + xenOrchestraServer: 'Serveur Xen Orchestra', + + // Original text: "Xen Orchestra web client" + xenOrchestraWeb: 'Client web Xen Orchestra', + + // Original text: "No pro support provided!" + noProSupport: 'Pas de support professionel fourni !', + + // Original text: "Use in production at your own risks" + noProductionUse: 'Utilisez en production à vos risques et périls', + + // Original text: "You can download our turnkey appliance at {website}" + downloadXoaFromWebsite: + 'Téléchargez notre édition clef en main sur {website}', + + // Original text: "Bug Tracker" + bugTracker: 'Gestionnaire de tickets', + + // Original text: "Issues? Report it!" + bugTrackerText: 'Un souci ? Dites-le nous !', + + // Original text: "Community" + community: 'Communauté', + + // Original text: "Join our community forum!" + communityText: 'Rejoignez notre communauté !', + + // Original text: "Free Trial for Premium Edition!" + freeTrial: "Démonstration gratuite de l'Édition Premium !", + + // Original text: "Request your trial now!" + freeTrialNow: 'Demandez une démonstration dès maintenant !', + + // Original text: "Any issue?" + issues: 'Des soucis ?', + + // Original text: "Problem? Contact us!" + issuesText: 'Un problème ? Contactez-nous !', + + // Original text: "Documentation" + documentation: 'Documentation', + + // Original text: "Read our official doc" + documentationText: 'Consultez notre documentation officielle', + + // Original text: "Pro support included" + proSupportIncluded: 'Support professionel inclus', + + // Original text: "Access your XO Account" + xoAccount: 'Accéder à votre compte XO', + + // Original text: "Report a problem" + openTicket: 'Signaler un problème', + + // Original text: "Problem? Open a ticket!" + openTicketText: 'Un problème ? Ouvrez un ticket !', + + // Original text: "Upgrade needed" + upgradeNeeded: 'Mise à jour nécessaire', + + // Original text: "Upgrade now!" + upgradeNow: 'Mettre à jour maintenant !', + + // Original text: "Or" + or: 'Ou', + + // Original text: "Try it for free!" + tryIt: 'Essayez gratuitement !', + + // Original text: "This feature is available starting from {plan} Edition" + availableIn: + "Cette fonctionnalité est disponible à partir de l'édition {plan}", + + // Original text: "This feature is not available in your version, contact your administrator to know more." + notAvailable: + "Cette fonctionnalité n'est pas disponible dans cette édtition. Pour plus d'informations, contactez votre administrateur.", + + // Original text: "Updates" + updateTitle: 'Mise à jour', + + // Original text: "Registration" + registration: 'Enregistrement', + + // Original text: "Trial" + trial: 'Démonstration', + + // Original text: "Settings" + settings: 'Paramètres', + + // Original text: "Proxy settings" + proxySettings: 'Configuration du proxy', + + // Original text: "Host (myproxy.example.org)" + proxySettingsHostPlaceHolder: 'Hôte (monproxy.exemple.tld)', + + // Original text: "Port (eg: 3128)" + proxySettingsPortPlaceHolder: 'Port (ex : 3128)', + + // Original text: "Username" + proxySettingsUsernamePlaceHolder: "Nom d'utilisateur", + + // Original text: "Password" + proxySettingsPasswordPlaceHolder: 'Mot de passe', + + // Original text: "Your email account" + updateRegistrationEmailPlaceHolder: 'Email du compte', + + // Original text: "Your password" + updateRegistrationPasswordPlaceHolder: 'Mot de passe', + + // Original text: "Update" + update: 'Actualiser', + + // Original text: "Refresh" + refresh: 'Rafraîchir', + + // Original text: "Upgrade" + upgrade: 'Mettre à jour', + + // Original text: "No updater available for Community Edition" + noUpdaterCommunity: 'Pas de mise à jour sur la version Communautaire', + + // Original text: "Please consider subscribe and try it with all features for free during 15 days on {link}." + considerSubscribe: + 'Envisagez de souscrire, et essayez toutes les fonctionnalités gratuitement pendant 15 jours.', + + // Original text: "Manual update could break your current installation due to dependencies issues, do it with caution" + noUpdaterWarning: + 'Une mise à jour manuelle pourrait corrompre votre installation actuelle à cause des dépendances, soyez prudent.', + + // Original text: "Current version:" + currentVersion: 'Version actuelle :', + + // Original text: "Register" + register: "S'enregistrer", + + // Original text: "Edit registration" + editRegistration: "Éditer l'enregistrement", + + // Original text: "Please, take time to register in order to enjoy your trial." + trialRegistration: + 'Merci de prendre le temps de vous enregistrer afin de profiter de votre essai.', + + // Original text: "Start trial" + trialStartButton: "Commencer la période d'essai", + + // Original text: "You can use a trial version until {date, date, medium}. Upgrade your appliance to get it." + trialAvailableUntil: + "Vous pouvez utiliser une version d'essai jusqu'au {date, date, medium}. Mettez à jour votre XOA pour en profiter.", + + // Original text: "Your trial has been ended. Contact us or downgrade to Free version" + trialConsumed: + "Votre période d'essai est terminé. Contactez-nous, ou régressez sur l'édition gratuite.", + + // Original text: "Your xoa-updater service appears to be down. Your XOA cannot run fully without reaching this service." + trialLocked: + 'Votre service xoa-update semble inaccessible. Votre XOA ne peut pas fonctionner pleinement si elle ne peut pas joindre ce service.', + + // Original text: "No update information available" + noUpdateInfo: "Pas d'informations de mises à jour disponible", + + // Original text: "Update information may be available" + waitingUpdateInfo: + 'Des informations de mises à jour sont peut-être disponibles', + + // Original text: "Your XOA is up-to-date" + upToDate: 'Votre XOA est à jour', + + // Original text: "You need to update your XOA (new version is available)" + mustUpgrade: + 'Vous devez mettre à jour votre XOA (une nouvelle version est disponible)', + + // Original text: "Your XOA is not registered for updates" + registerNeeded: "Votre XOA n'est pas enregistrée pour les mises à jour", + + // Original text: "Can't fetch update information" + updaterError: 'Impossible de récupérer les informations de mise à jour.', + + // Original text: "Upgrade successful" + promptUpgradeReloadTitle: 'Mise à jour réussie', + + // Original text: "Your XOA has successfully upgraded, and your browser must reload the application. Do you want to reload now ?" + promptUpgradeReloadMessage: + "Votre XOA à été mise à jour avec brio, et votre navigateur doit rafraîchir l'application pour en profiter. Voulez-vous rafraîchir dès maintenant ?", + + // Original text: "Xen Orchestra from the sources" + disclaimerTitle: 'Xen Orchestra depuis les sources', + + // Original text: "You are using XO from the sources! That's great for a personal/non-profit usage." + disclaimerText1: + "Vous utilisez XO depuis les sources. C'est parfait pour un usage personnel ou non lucratif.", + + // Original text: "If you are a company, it's better to use it with our appliance + pro support included:" + disclaimerText2: + "Si vous êtes une entrerprise, il est préférable d'utiliser notre applicance qui inclut du support professionel.", + + // Original text: "This version is not bundled with any support nor updates. Use it with caution for critical tasks." + disclaimerText3: + "Cette version n'est fournie avec aucun support ni aucune mise à jour. Soyez prudent en cas d'utilisation pour des tâches importantes.", + + // Original text: "Connect PIF" + connectPif: 'Connecter la PIF', + + // Original text: "Are you sure you want to connect this PIF?" + connectPifConfirm: 'Êtes-vous sûr de vouloir connecter cette PIF ?', + + // Original text: "Disconnect PIF" + disconnectPif: 'Déconnecter la PIF', + + // Original text: "Are you sure you want to disconnect this PIF?" + disconnectPifConfirm: 'Êtes-vous sûr de vouloir déconnecter cette PIF ?', + + // Original text: "Delete PIF" + deletePif: 'Supprimer la PIF', + + // Original text: "Are you sure you want to delete this PIF?" + deletePifConfirm: 'Êtes-vous sûr de vouloir supprimer cette PIF ?', + + // Original text: "Connected" + pifConnected: 'Connecté', + + // Original text: "Disconnected" + pifDisconnected: 'Déconnecté', + + // Original text: "Physically connected" + pifPhysicallyConnected: 'Connecté physiquement', + + // Original text: "Physically disconnected" + pifPhysicallyDisconnected: 'Déconnecté physiquement', + + // Original text: "Username" + username: "Nom d'utilisateur", + + // Original text: "Password" + password: 'Mot de passe', + + // Original text: "Language" + language: 'Langue', + + // Original text: "Old password" + oldPasswordPlaceholder: 'Ancien mot de passe', + + // Original text: "New password" + newPasswordPlaceholder: 'Nouveau mot de passe', + + // Original text: "Confirm new password" + confirmPasswordPlaceholder: 'Confirmer le nouveau mot de passe', + + // Original text: "Confirmation password incorrect" + confirmationPasswordError: 'Confirmation du nouveau mot de passe invalide', + + // Original text: "Password does not match the confirm password." + confirmationPasswordErrorBody: + 'Le mot de passe ne correspond pas à la confirmation du mot de passe.', + + // Original text: "Password changed" + pwdChangeSuccess: 'Mot de passe modifié', + + // Original text: "Your password has been successfully changed." + pwdChangeSuccessBody: 'Votre mot de passe a été modifié avec succés.', + + // Original text: "Incorrect password" + pwdChangeError: 'Mot de passe invalide', + + // Original text: "The old password provided is incorrect. Your password has not been changed." + pwdChangeErrorBody: + "L'ancien mot de passe n'est pas valide. Votre mot de passe n'a pas été changé.", + + // Original text: "OK" + changePasswordOk: 'OK', + + // Original text: "SSH keys" + sshKeys: 'Clefs SSH', + + // Original text: "New SSH key" + newSshKey: 'Nouvelle clef SSH', + + // Original text: "Delete" + deleteSshKey: 'Supprimer', + + // Original text: "No SSH keys" + noSshKeys: 'Pas de clef SSH', + + // Original text: "New SSH key" + newSshKeyModalTitle: 'Nouvelle clef SSH', + + // Original text: "Invalid key" + sshKeyErrorTitle: 'Clef invalide', + + // Original text: "An SSH key requires both a title and a key." + sshKeyErrorMessage: 'Une clef SSH nécessite un titre et une clef', + + // Original text: "Title" + title: 'Titre', + + // Original text: "Key" + key: 'Clef', + + // Original text: "Delete SSH key" + deleteSshKeyConfirm: 'Supprimer la clef SSH', + + // Original text: "Are you sure you want to delete the SSH key {title}?" + deleteSshKeyConfirmMessage: + 'Êtes-vous sûr de vouloir supprimer la clef SSH {title}?', + + // Original text: "Others" + others: 'Autres', + + // Original text: "Loading logs…" + loadingLogs: 'Chargement des journaux…', + + // Original text: "User" + logUser: 'Utilisateur', + + // Original text: "Method" + logMethod: 'Méthode', + + // Original text: "Params" + logParams: 'Paramètres', + + // Original text: "Message" + logMessage: 'Message', + + // Original text: "Error" + logError: 'Erreur', + + // Original text: "Display details" + logDisplayDetails: 'Afficher les détails', + + // Original text: "Date" + logTime: 'Date', + + // Original text: "No stack trace" + logNoStackTrace: 'No stack trace', + + // Original text: "No params" + logNoParams: 'No params', + + // Original text: "Delete log" + logDelete: 'Supprimer le log', + + // Original text: "Delete all logs" + logDeleteAll: 'Supprimer tous les journaux', + + // Original text: "Delete all logs" + logDeleteAllTitle: 'Supprimer tous les journaux', + + // Original text: "Are you sure you want to delete all the logs?" + logDeleteAllMessage: 'Êtes-vous sûr de vouloir supprimer tous les journaux ?', + + // Original text: "Click to enable" + logIndicationToEnable: 'Cliquer pour activer', + + // Original text: "Click to disable" + logIndicationToDisable: 'Cliquer pour désactiver', + + // Original text: "Report a bug" + reportBug: 'Rapporter un bug', + + // Original text: "Name" + ipPoolName: 'Nom', + + // Original text: "IPs" + ipPoolIps: 'IPs', + + // Original text: "IPs (e.g.: 1.0.0.12-1.0.0.17;1.0.0.23)" + ipPoolIpsPlaceholder: 'IPs (e.g.: 1.0.0.12-1.0.0.17;1.0.0.23)', + + // Original text: "Networks" + ipPoolNetworks: 'Réseaux', + + // Original text: "No IP pools" + ipsNoIpPool: "Pas de plages d'IPs", + + // Original text: "Create" + ipsCreate: 'Créer', + + // Original text: "Delete all IP pools" + ipsDeleteAllTitle: "Supprimer toutes les plages d'IPs", + + // Original text: "Are you sure you want to delete all the IP pools?" + ipsDeleteAllMessage: + "Êtes-vous sûr de vouloir supprimer toutes les plages d'IPs ?", + + // Original text: "VIFs" + ipsVifs: 'VIFs', + + // Original text: "Not used" + ipsNotUsed: 'Non utilisé', + + // Original text: "unknown VIF" + ipPoolUnknownVif: 'VIF inconnue', + + // Original text: "Name already exists" + ipPoolNameAlreadyExists: 'Ce nom existe déjà', + + // Original text: "Keyboard shortcuts" + shortcutModalTitle: 'Raccourcis clavier', + + // Original text: "Global" + shortcut_XoApp: 'Global', + + // Original text: "Go to hosts list" + shortcut_GO_TO_HOSTS: 'Aller sur la liste des hôtes', + + // Original text: "Go to pools list" + shortcut_GO_TO_POOLS: 'Aller sur la liste des pools', + + // Original text: "Go to VMs list" + shortcut_GO_TO_VMS: 'Aller sur la liste des VMs', + + // Original text: "Go to SRs list" + shortcut_GO_TO_SRS: 'Aller à la liste des SRs', + + // Original text: "Create a new VM" + shortcut_CREATE_VM: 'Créer une nouvelle VM', + + // Original text: "Unfocus field" + shortcut_UNFOCUS: 'Quitter le champ', + + // Original text: "Show shortcuts key bindings" + shortcut_HELP: 'Afficher les raccourcis clavier', + + // Original text: "Home" + shortcut_Home: 'Accueil', + + // Original text: "Focus search bar" + shortcut_SEARCH: 'Curseur dans la barre de recherche', + + // Original text: "Next item" + shortcut_NAV_DOWN: 'Élément suivant', + + // Original text: "Previous item" + shortcut_NAV_UP: 'Élément précédent', + + // Original text: "Select item" + shortcut_SELECT: "Sélectionner l'élément", + + // Original text: "Open" + shortcut_JUMP_INTO: 'Ouvrir', + + // Original text: "VM" + settingsAclsButtonTooltipVM: 'VM', + + // Original text: "Hosts" + settingsAclsButtonTooltiphost: 'Hôtes', + + // Original text: "Pool" + settingsAclsButtonTooltippool: 'Pool', + + // Original text: "SR" + settingsAclsButtonTooltipSR: 'SR', + + // Original text: "Network" + settingsAclsButtonTooltipnetwork: 'Réseaux', + + // Original text: "No config file selected" + noConfigFile: 'Pas de fichier de configuration sélectionné', + + // Original text: "Try dropping a config file here, or click to select a config file to upload." + importTip: + 'Essayez de déposer un fichier de configuration ou cliquez pour sélectionner un fichier de configuration à importer.', + + // Original text: "Config" + config: 'Configuration', + + // Original text: "Import" + importConfig: 'Importer', + + // Original text: "Config file successfully imported" + importConfigSuccess: 'Fichier de configuration importé avec succès', + + // Original text: "Error while importing config file" + importConfigError: "Erreur lors de l'import du fichier de configuration", + + // Original text: "Export" + exportConfig: 'Exporter', + + // Original text: "Download current config" + downloadConfig: 'Télécharger la configuration actuelle', + + // Original text: "No config import available for Community Edition" + noConfigImportCommunity: + 'Import de configuration non disponible pour la Community Edition', + + // Original text: "Reconnect all hosts" + srReconnectAllModalTitle: 'Reconnecter tous les hôtes', + + // Original text: "This will reconnect this SR to all its hosts." + srReconnectAllModalMessage: 'Ceci reconnectera ce SR à tous ses hôtes', + + // Original text: "This will reconnect each selected SR to its host (local SR) or to every hosts of its pool (shared SR)." + srsReconnectAllModalMessage: + 'Ceci reconnectera tous les SRs sélectionnés à son hôte (SR local) ou à tous les hôtes de son pool (SR partagé).', + + // Original text: "Disconnect all hosts" + srDisconnectAllModalTitle: 'Déconnecter tous les hôtes', + + // Original text: "This will disconnect this SR from all its hosts." + srDisconnectAllModalMessage: 'Ceci déconnectera ce SR de tous ses hôtes.', + + // Original text: "This will disconnect each selected SR from its host (local SR) or from every hosts of its pool (shared SR)." + srsDisconnectAllModalMessage: + 'Ceci déconnectera tous les SRs sélectionnés de leur hôte (SR local) ou de tous les hôtes de leur pool (SR partagé).', + + // Original text: "Forget SR" + srForgetModalTitle: 'Oublier le SR', + + // Original text: "Forget selected SRs" + srsForgetModalTitle: 'Oublier les SRs sélectionnés', + + // Original text: "Are you sure you want to forget this SR? VDIs on this storage won't be removed." + srForgetModalMessage: + 'Êtes-vous sûr de vouloir oublier ce SR ? Les VDIs de ce stockage ne seront pas supprimés.', + + // Original text: "Are you sure you want to forget all the selected SRs? VDIs on these storages won't be removed." + srsForgetModalMessage: + 'Êtes-vous sûr de vouloir oublier tous les SRs sélectionnés ? Les VDIs sur ces stockages ne seront pas supprimés.', + + // Original text: "Disconnected" + srAllDisconnected: 'Déconnectés', + + // Original text: "Partially connected" + srSomeConnected: 'Partiellement connectés', + + // Original text: "Connected" + srAllConnected: 'Connectés', + + // Original text: "XOSAN" + xosanTitle: 'XOSAN', + + // Original text: "Xen Orchestra SAN SR" + xosanSrTitle: 'SR NAS Xen Orchestra', + + // Original text: "Select local SRs (lvm)" + xosanAvailableSrsTitle: 'Sélectionner des SRs locaux (lvm)', + + // Original text: "Suggestions" + xosanSuggestions: 'Suggestions', + + // Original text: "Name" + xosanName: 'Nom', + + // Original text: "Host" + xosanHost: 'Hôte', + + // Original text: "Hosts" + xosanHosts: 'Hôtes', + + // Original text: "Volume ID" + xosanVolumeId: 'ID du volume', + + // Original text: "Size" + xosanSize: 'Taille', + + // Original text: "Used space" + xosanUsedSpace: 'Espace utilisé', + + // Original text: "XOSAN pack needs to be installed on each host of the pool." + xosanNeedPack: 'La pack XOSAN doit être installé sur tous les hôtes du pool.', + + // Original text: "Install it now!" + xosanInstallIt: 'Installer maintenant !', + + // Original text: "Some hosts need their toolstack to be restarted before you can create an XOSAN" + xosanNeedRestart: + 'Certains hôtes ont besoin que leur toolstack soit redémarrée avant de pouvoir créer un XOSAN', + + // Original text: "Restart toolstacks" + xosanRestartAgents: 'Redémarrer les toolstacks', + + // Original text: "Pool master is not running" + xosanMasterOffline: "Le master du pool n'est pas démarré", + + // Original text: "Install XOSAN pack on {pool}" + xosanInstallPackTitle: 'Installer le pack XOSAN sur {pool}', + + // Original text: "Select at least 2 SRs" + xosanSelect2Srs: 'Sélectionner au moins 2 SRs', + + // Original text: "Layout" + xosanLayout: 'Disposition', + + // Original text: "Redundancy" + xosanRedundancy: 'Redondance', + + // Original text: "Capacity" + xosanCapacity: 'Capacité', + + // Original text: "Available space" + xosanAvailableSpace: 'Espace disponible', + + // Original text: "* Can fail without data loss" + xosanDiskLossLegend: '* Peut tomber en panne sans perte de données', + + // Original text: "Create" + xosanCreate: 'Créer', + + // Original text: "Installing XOSAN. Please wait…" + xosanInstalling: 'Installation de XOSAN. Veuillez patienter…', + + // Original text: "No XOSAN available for Community Edition" + xosanCommunity: 'XOSAN non disponible pour la Community Edition', + + // Original text: "Install cloud plugin first" + xosanInstallCloudPlugin: 'Installer le plugin cloud avant', + + // Original text: "Load cloud plugin first" + xosanLoadCloudPlugin: 'Charger le plugin cloud avant', + + // Original text: "Loading…" + xosanLoading: 'Chargement…', + + // Original text: "XOSAN is not available at the moment" + xosanNotAvailable: "XOSAN n'est pas disponible pour le moment", + + // Original text: "Register for the XOSAN beta" + xosanRegisterBeta: 'Inscrivez-vous pour la beta de XOSAN', + + // Original text: "You have successfully registered for the XOSAN beta. Please wait until your request has been approved." + xosanSuccessfullyRegistered: + 'Vous êtes inscrit pour la beta de XOSAN. Veuillez attendre que votre demande soit approuvée.', + + // Original text: "Install XOSAN pack on these hosts:" + xosanInstallPackOnHosts: 'Installer le pack XOSAN sur ces hôtes :', + + // Original text: "Install {pack} v{version}?" + xosanInstallPack: 'Installer {pack} v{version} ?', + + // Original text: "No compatible XOSAN pack found for your XenServer versions." + xosanNoPackFound: + 'Pas de pack XOSAN compatible pour vos versions de XenServers.', + + // Original text: "At least one of these version requirements must be satisfied by all the hosts in this pool:" + xosanPackRequirements: + 'Au moins une de ces condtions de version doit être satisfaite par tous les hôtes de ce pool :', +} diff --git a/packages/xo-web/src/common/intl/locales/he.js b/packages/xo-web/src/common/intl/locales/he.js new file mode 100644 index 000000000..cb02cfaed --- /dev/null +++ b/packages/xo-web/src/common/intl/locales/he.js @@ -0,0 +1,3136 @@ +// See http://momentjs.com/docs/#/use-it/browserify/ +import 'moment/locale/he' + +import reactIntlData from 'react-intl/locale-data/he' +import { addLocaleData } from 'react-intl' +addLocaleData(reactIntlData) + +// =================================================================== + +export default { + // Original text: 'Connecting' + statusConnecting: undefined, + + // Original text: 'Disconnected' + statusDisconnected: undefined, + + // Original text: 'Loading…' + statusLoading: undefined, + + // Original text: 'Page not found' + errorPageNotFound: undefined, + + // Original text: 'no such item' + errorNoSuchItem: undefined, + + // Original text: "Long click to edit" + editableLongClickPlaceholder: 'לחץ כאן לחיצה ערוכה לעריכה', + + // Original text: "Click to edit" + editableClickPlaceholder: 'לחץ לעריכה', + + // Original text: 'OK' + alertOk: undefined, + + // Original text: 'OK' + confirmOk: undefined, + + // Original text: 'Cancel' + confirmCancel: undefined, + + // Original text: 'On error' + onError: undefined, + + // Original text: 'Successful' + successful: undefined, + + // Original text: 'Copy to clipboard' + copyToClipboard: undefined, + + // Original text: 'Master' + pillMaster: undefined, + + // Original text: "Home" + homePage: 'בית', + + // Original text: 'VMs' + homeVmPage: undefined, + + // Original text: 'Hosts' + homeHostPage: undefined, + + // Original text: 'Pools' + homePoolPage: undefined, + + // Original text: 'Templates' + homeTemplatePage: undefined, + + // Original text: "Dashboard" + dashboardPage: 'לוח מכוונים', + + // Original text: "Overview" + overviewDashboardPage: 'איזור אישי', + + // Original text: "Visualizations" + overviewVisualizationDashboardPage: 'וירטואליזציה', + + // Original text: "Statistics" + overviewStatsDashboardPage: 'סטטיסטיקה', + + // Original text: "Health" + overviewHealthDashboardPage: 'בדיקת ביצועים', + + // Original text: "Self service" + selfServicePage: 'שירות עצמי', + + // Original text: "Backup" + backupPage: 'גיבוי', + + // Original text: "Jobs" + jobsPage: 'משימות', + + // Original text: "Updates" + updatePage: 'עדכונים', + + // Original text: "Settings" + settingsPage: 'הגדרות', + + // Original text: "Servers" + settingsServersPage: 'שרתים', + + // Original text: "Users" + settingsUsersPage: 'משתמשים', + + // Original text: "Groups" + settingsGroupsPage: 'קבוצות', + + // Original text: "ACLs" + settingsAclsPage: 'רמות גישה', + + // Original text: "Plugins" + settingsPluginsPage: 'פלאגינים', + + // Original text: 'Logs' + settingsLogsPage: undefined, + + // Original text: 'IPs' + settingsIpsPage: undefined, + + // Original text: "About" + aboutPage: 'אודות', + + // Original text: "New" + newMenu: 'חדש', + + // Original text: "Tasks" + taskMenu: 'משימות', + + // Original text: 'Tasks' + taskPage: undefined, + + // Original text: "VM" + newVmPage: 'מכונה', + + // Original text: "Storage" + newSrPage: 'אחסון', + + // Original text: "Server" + newServerPage: 'שרת', + + // Original text: "Import" + newImport: 'ההלעה', + + // Original text: "Overview" + backupOverviewPage: 'הרחבה', + + // Original text: "New" + backupNewPage: 'חדש', + + // Original text: "Remotes" + backupRemotesPage: 'גישה מרחוק', + + // Original text: "Restore" + backupRestorePage: 'שחזור', + + // Original text: "Schedule" + schedule: 'תזמון', + + // Original text: "New VM backup" + newVmBackup: 'גיבוי חדש למכונה', + + // Original text: "Edit VM backup" + editVmBackup: 'ערוך הגדרות גיבוי למכונה', + + // Original text: "Backup" + backup: 'גיבוי', + + // Original text: "Rolling Snapshot" + rollingSnapshot: 'גיבוי סנאפשוט', + + // Original text: "Delta Backup" + deltaBackup: 'גיבוי חלקי', + + // Original text: "Disaster Recovery" + disasterRecovery: 'חזרה מDR', + + // Original text: "Continuous Replication" + continuousReplication: 'רפליקציה מתממשכת', + + // Original text: "Overview" + jobsOverviewPage: 'רשימת משימות', + + // Original text: "New" + jobsNewPage: 'משימה חדשה', + + // Original text: "Scheduling" + jobsSchedulingPage: 'תזמון משימות', + + // Original text: "Custom Job" + customJob: 'משימה קסטומית', + + // Original text: 'User' + userPage: undefined, + + // Original text: 'No support' + noSupport: undefined, + + // Original text: 'Free upgrade!' + freeUpgrade: undefined, + + // Original text: "Sign out" + signOut: 'יציאה', + + // Original text: 'Edit my settings {username}' + editUserProfile: undefined, + + // Original text: "Fetching data…" + homeFetchingData: 'מקבל נתונים, נא להמתין…', + + // Original text: "Welcome on Xen Orchestra!" + homeWelcome: 'ברוכים הבאים', + + // Original text: "Add your XenServer hosts or pools" + homeWelcomeText: 'נא להוסיף שרתים או משאבים', + + // Original text: "Want some help?" + homeHelp: 'צריך עזרה?', + + // Original text: "Add server" + homeAddServer: 'הוספת שרת', + + // Original text: "Online Doc" + homeOnlineDoc: 'דוקומנטציה', + + // Original text: "Pro Support" + homeProSupport: 'תמיכה מקצועית', + + // Original text: "There are no VMs!" + homeNoVms: 'אין מכונות', + + // Original text: "Or…" + homeNoVmsOr: 'או…', + + // Original text: "Import VM" + homeImportVm: 'ההלעה של מכונה', + + // Original text: "Import an existing VM in xva format" + homeImportVmMessage: 'להלעות מכונה חדשה בפורמת XVA', + + // Original text: "Restore a backup" + homeRestoreBackup: 'שחזור מגיבוי', + + // Original text: "Restore a backup from a remote store" + homeRestoreBackupMessage: 'שחזור מגיבוי ממכונה אחרת', + + // Original text: "This will create a new VM" + homeNewVmMessage: 'זה יצור מכונה חדשה', + + // Original text: "Filters" + homeFilters: 'מסננים', + + // Original text: 'No results! Click here to reset your filters' + homeNoMatches: undefined, + + // Original text: "Pool" + homeTypePool: 'משאבים', + + // Original text: "Host" + homeTypeHost: 'מכונה', + + // Original text: "VM" + homeTypeVm: 'שרת', + + // Original text: "SR" + homeTypeSr: 'SR', + + // Original text: 'Template' + homeTypeVmTemplate: undefined, + + // Original text: "Sort" + homeSort: 'סינון', + + // Original text: "Pools" + homeAllPools: 'POOLS', + + // Original text: "Hosts" + homeAllHosts: 'מכונות', + + // Original text: "Tags" + homeAllTags: 'מילות מפתח', + + // Original text: "New VM" + homeNewVm: 'מכונה חדשה', + + // Original text: "Running hosts" + homeFilterRunningHosts: 'מערכות פעילות', + + // Original text: "Disabled hosts" + homeFilterDisabledHosts: 'מערכות לא פעילות', + + // Original text: "Running VMs" + homeFilterRunningVms: 'מכונות פעילות', + + // Original text: 'Non running VMs' + homeFilterNonRunningVms: undefined, + + // Original text: 'Pending VMs' + homeFilterPendingVms: undefined, + + // Original text: 'HVM guests' + homeFilterHvmGuests: undefined, + + // Original text: "Tags" + homeFilterTags: 'מילות מפתח', + + // Original text: "Sort by" + homeSortBy: 'סנן לפי', + + // Original text: "Name" + homeSortByName: 'שם', + + // Original text: "Power state" + homeSortByPowerstate: 'מצב', + + // Original text: "RAM" + homeSortByRAM: 'זכרון RAM', + + // Original text: "vCPUs" + homeSortByvCPUs: 'כמות המאבדים', + + // Original text: 'CPUs' + homeSortByCpus: undefined, + + // Original text: '{displayed, number}x {icon} (on {total, number})' + homeDisplayedItems: undefined, + + // Original text: '{selected, number}x {icon} selected (on {total, number})' + homeSelectedItems: undefined, + + // Original text: "More" + homeMore: 'עוד', + + // Original text: "Migrate to…" + homeMigrateTo: 'העבר ל…', + + // Original text: 'Missing patches' + homeMissingPaths: undefined, + + // Original text: 'Master:' + homePoolMaster: undefined, + + // Original text: 'High Availability' + highAvailability: undefined, + + // Original text: "Add" + add: 'הוסף', + + // Original text: "Remove" + remove: 'הסר', + + // Original text: 'Preview' + preview: undefined, + + // Original text: "Item" + item: 'פריט', + + // Original text: "No selected value" + noSelectedValue: 'לא נבחר כלום', + + // Original text: "Choose user(s) and/or group(s)" + selectSubjects: 'בחר משתמש או קבוצה להוספה', + + // Original text: 'Select Object(s)…' + selectObjects: undefined, + + // Original text: 'Choose a role' + selectRole: undefined, + + // Original text: 'Select Host(s)…' + selectHosts: undefined, + + // Original text: 'Select object(s)…' + selectHostsVms: undefined, + + // Original text: 'Select Network(s)…' + selectNetworks: undefined, + + // Original text: 'Select PIF(s)…' + selectPifs: undefined, + + // Original text: 'Select Pool(s)…' + selectPools: undefined, + + // Original text: 'Select Remote(s)…' + selectRemotes: undefined, + + // Original text: 'Select resource set(s)…' + selectResourceSets: undefined, + + // Original text: 'Select template(s)…' + selectResourceSetsVmTemplate: undefined, + + // Original text: 'Select SR(s)…' + selectResourceSetsSr: undefined, + + // Original text: 'Select network(s)…' + selectResourceSetsNetwork: undefined, + + // Original text: 'Select disk(s)…' + selectResourceSetsVdi: undefined, + + // Original text: 'Select SSH key(s)…' + selectSshKey: undefined, + + // Original text: 'Select SR(s)…' + selectSrs: undefined, + + // Original text: 'Select VM(s)…' + selectVms: undefined, + + // Original text: 'Select VM template(s)…' + selectVmTemplates: undefined, + + // Original text: 'Select tag(s)…' + selectTags: undefined, + + // Original text: 'Select disk(s)…' + selectVdis: undefined, + + // Original text: 'Select timezone…' + selectTimezone: undefined, + + // Original text: 'Select IP(s)…' + selectIp: undefined, + + // Original text: 'Select IP pool(s)…' + selectIpPool: undefined, + + // Original text: 'Fill required informations.' + fillRequiredInformations: undefined, + + // Original text: 'Fill informations (optional)' + fillOptionalInformations: undefined, + + // Original text: 'Reset' + selectTableReset: undefined, + + // Original text: 'Month' + schedulingMonth: undefined, + + // Original text: 'Each selected month' + schedulingEachSelectedMonth: undefined, + + // Original text: 'Day of the month' + schedulingMonthDay: undefined, + + // Original text: 'Each selected day' + schedulingEachSelectedMonthDay: undefined, + + // Original text: 'Day of the week' + schedulingWeekDay: undefined, + + // Original text: 'Each selected day' + schedulingEachSelectedWeekDay: undefined, + + // Original text: 'Hour' + schedulingHour: undefined, + + // Original text: 'Every N hour' + schedulingEveryNHour: undefined, + + // Original text: 'Each selected hour' + schedulingEachSelectedHour: undefined, + + // Original text: 'Minute' + schedulingMinute: undefined, + + // Original text: 'Every N minute' + schedulingEveryNMinute: undefined, + + // Original text: 'Each selected minute' + schedulingEachSelectedMinute: undefined, + + // Original text: 'Reset' + schedulingReset: undefined, + + // Original text: 'Unknown' + unknownSchedule: undefined, + + // Original text: 'Xo-server timezone:' + timezonePickerServerValue: undefined, + + // Original text: 'Web browser timezone' + timezonePickerUseLocalTime: undefined, + + // Original text: 'Xo-server timezone' + timezonePickerUseServerTime: undefined, + + // Original text: 'Server timezone ({value})' + serverTimezoneOption: undefined, + + // Original text: 'Cron Pattern:' + cronPattern: undefined, + + // Original text: 'Cannot edit backup' + backupEditNotFoundTitle: undefined, + + // Original text: 'Missing required info for edition' + backupEditNotFoundMessage: undefined, + + // Original text: 'Job' + job: undefined, + + // Original text: 'Job ID' + jobId: undefined, + + // Original text: 'Name' + jobName: undefined, + + // Original text: 'Name of your job (forbidden: "_")' + jobNamePlaceholder: undefined, + + // Original text: 'Start' + jobStart: undefined, + + // Original text: 'End' + jobEnd: undefined, + + // Original text: 'Duration' + jobDuration: undefined, + + // Original text: 'Status' + jobStatus: undefined, + + // Original text: 'Action' + jobAction: undefined, + + // Original text: 'Tag' + jobTag: undefined, + + // Original text: 'Scheduling' + jobScheduling: undefined, + + // Original text: 'State' + jobState: undefined, + + // Original text: 'Timezone' + jobTimezone: undefined, + + // Original text: 'xo-server' + jobServerTimezone: undefined, + + // Original text: 'Run job' + runJob: undefined, + + // Original text: 'One shot running started. See overview for logs.' + runJobVerbose: undefined, + + // Original text: 'Started' + jobStarted: undefined, + + // Original text: 'Finished' + jobFinished: undefined, + + // Original text: 'Save' + saveBackupJob: undefined, + + // Original text: 'Remove backup job' + deleteBackupSchedule: undefined, + + // Original text: 'Are you sure you want to delete this backup job?' + deleteBackupScheduleQuestion: undefined, + + // Original text: 'Enable immediately after creation' + scheduleEnableAfterCreation: undefined, + + // Original text: 'You are editing Schedule {name} ({id}). Saving will override previous schedule state.' + scheduleEditMessage: undefined, + + // Original text: 'You are editing job {name} ({id}). Saving will override previous job state.' + jobEditMessage: undefined, + + // Original text: 'No scheduled jobs.' + noScheduledJobs: undefined, + + // Original text: 'No jobs found.' + noJobs: undefined, + + // Original text: 'No schedules found' + noSchedules: undefined, + + // Original text: 'Select a xo-server API command' + jobActionPlaceHolder: undefined, + + // Original text: 'Schedules' + jobSchedules: undefined, + + // Original text: 'Name of your schedule' + jobScheduleNamePlaceHolder: undefined, + + // Original text: 'Select a Job' + jobScheduleJobPlaceHolder: undefined, + + // Original text: 'Select your backup type:' + newBackupSelection: undefined, + + // Original text: 'Select backup mode:' + smartBackupModeSelection: undefined, + + // Original text: 'Normal backup' + normalBackup: undefined, + + // Original text: 'Smart backup' + smartBackup: undefined, + + // Original text: 'Local remote selected' + localRemoteWarningTitle: undefined, + + // Original text: 'Warning: local remotes will use limited XOA disk space. Only for advanced users.' + localRemoteWarningMessage: undefined, + + // Original text: 'VMs' + editBackupVmsTitle: undefined, + + // Original text: 'VMs statuses' + editBackupSmartStatusTitle: undefined, + + // Original text: 'Resident on' + editBackupSmartResidentOn: undefined, + + // Original text: 'VMs Tags' + editBackupSmartTagsTitle: undefined, + + // Original text: 'Tag' + editBackupTagTitle: undefined, + + // Original text: 'Report' + editBackupReportTitle: undefined, + + // Original text: 'Enable immediately after creation' + editBackupScheduleEnabled: undefined, + + // Original text: 'Depth' + editBackupDepthTitle: undefined, + + // Original text: 'Remote' + editBackupRemoteTitle: undefined, + + // Original text: 'Remote stores for backup' + remoteList: undefined, + + // Original text: 'New File System Remote' + newRemote: undefined, + + // Original text: 'Local' + remoteTypeLocal: undefined, + + // Original text: 'NFS' + remoteTypeNfs: undefined, + + // Original text: 'SMB' + remoteTypeSmb: undefined, + + // Original text: 'Type' + remoteType: undefined, + + // Original text: 'Test your remote' + remoteTestTip: undefined, + + // Original text: 'Test Remote' + testRemote: undefined, + + // Original text: 'Test failed for {name}' + remoteTestFailure: undefined, + + // Original text: 'Test passed for {name}' + remoteTestSuccess: undefined, + + // Original text: 'Error' + remoteTestError: undefined, + + // Original text: 'Test Step' + remoteTestStep: undefined, + + // Original text: 'Test file' + remoteTestFile: undefined, + + // Original text: 'The remote appears to work correctly' + remoteTestSuccessMessage: undefined, + + // Original text: 'Name' + remoteName: undefined, + + // Original text: 'Path' + remotePath: undefined, + + // Original text: 'State' + remoteState: undefined, + + // Original text: 'Device' + remoteDevice: undefined, + + // Original text: 'Share' + remoteShare: undefined, + + // Original text: 'Auth' + remoteAuth: undefined, + + // Original text: 'Mounted' + remoteMounted: undefined, + + // Original text: 'Unmounted' + remoteUnmounted: undefined, + + // Original text: 'Connect' + remoteConnectTip: undefined, + + // Original text: 'Disconnect' + remoteDisconnectTip: undefined, + + // Original text: 'Delete' + remoteDeleteTip: undefined, + + // Original text: 'remote name *' + remoteNamePlaceHolder: undefined, + + // Original text: 'Name *' + remoteMyNamePlaceHolder: undefined, + + // Original text: '/path/to/backup' + remoteLocalPlaceHolderPath: undefined, + + // Original text: 'host *' + remoteNfsPlaceHolderHost: undefined, + + // Original text: '/path/to/backup' + remoteNfsPlaceHolderPath: undefined, + + // Original text: 'subfolder [path\\to\\backup]' + remoteSmbPlaceHolderRemotePath: undefined, + + // Original text: 'Username' + remoteSmbPlaceHolderUsername: undefined, + + // Original text: 'Password' + remoteSmbPlaceHolderPassword: undefined, + + // Original text: 'Domain' + remoteSmbPlaceHolderDomain: undefined, + + // Original text: '
\\ *' + remoteSmbPlaceHolderAddressShare: undefined, + + // Original text: 'password(fill to edit)' + remotePlaceHolderPassword: undefined, + + // Original text: 'Create a new SR' + newSrTitle: undefined, + + // Original text: 'General' + newSrGeneral: undefined, + + // Original text: 'Select Storage Type:' + newSrTypeSelection: undefined, + + // Original text: 'Settings' + newSrSettings: undefined, + + // Original text: 'Storage Usage' + newSrUsage: undefined, + + // Original text: 'Summary' + newSrSummary: undefined, + + // Original text: 'Host' + newSrHost: undefined, + + // Original text: 'Type' + newSrType: undefined, + + // Original text: 'Name' + newSrName: undefined, + + // Original text: 'Description' + newSrDescription: undefined, + + // Original text: 'Server' + newSrServer: undefined, + + // Original text: 'Path' + newSrPath: undefined, + + // Original text: 'IQN' + newSrIqn: undefined, + + // Original text: 'LUN' + newSrLun: undefined, + + // Original text: 'with auth.' + newSrAuth: undefined, + + // Original text: 'User Name' + newSrUsername: undefined, + + // Original text: 'Password' + newSrPassword: undefined, + + // Original text: 'Device' + newSrDevice: undefined, + + // Original text: 'in use' + newSrInUse: undefined, + + // Original text: 'Size' + newSrSize: undefined, + + // Original text: 'Create' + newSrCreate: undefined, + + // Original text: 'Storage name' + newSrNamePlaceHolder: undefined, + + // Original text: 'Storage description' + newSrDescPlaceHolder: undefined, + + // Original text: 'Address' + newSrAddressPlaceHolder: undefined, + + // Original text: '[port]' + newSrPortPlaceHolder: undefined, + + // Original text: 'Username' + newSrUsernamePlaceHolder: undefined, + + // Original text: 'Password' + newSrPasswordPlaceHolder: undefined, + + // Original text: 'Device, e.g /dev/sda…' + newSrLvmDevicePlaceHolder: undefined, + + // Original text: '/path/to/directory' + newSrLocalPathPlaceHolder: undefined, + + // Original text: 'Users/Groups' + subjectName: undefined, + + // Original text: 'Object' + objectName: undefined, + + // Original text: 'No acls found' + aclNoneFound: undefined, + + // Original text: 'Role' + roleName: undefined, + + // Original text: 'Create' + aclCreate: undefined, + + // Original text: 'New Group Name' + newGroupName: undefined, + + // Original text: 'Create Group' + createGroup: undefined, + + // Original text: 'Create' + createGroupButton: undefined, + + // Original text: 'Delete Group' + deleteGroup: undefined, + + // Original text: 'Are you sure you want to delete this group?' + deleteGroupConfirm: undefined, + + // Original text: 'Remove user from Group' + removeUserFromGroup: undefined, + + // Original text: 'Are you sure you want to delete this user?' + deleteUserConfirm: undefined, + + // Original text: 'Delete User' + deleteUser: undefined, + + // Original text: 'no user' + noUser: undefined, + + // Original text: 'unknown user' + unknownUser: undefined, + + // Original text: 'No group found' + noGroupFound: undefined, + + // Original text: "Name" + groupNameColumn: 'שם', + + // Original text: "Users" + groupUsersColumn: 'משתמשים', + + // Original text: "Add User" + addUserToGroupColumn: 'הוסף משתמש', + + // Original text: "Email" + userNameColumn: 'מייל', + + // Original text: "Permissions" + userPermissionColumn: 'הרשאות', + + // Original text: "Password" + userPasswordColumn: 'סיסמא', + + // Original text: "Email" + userName: 'שם משתמש', + + // Original text: 'Password' + userPassword: undefined, + + // Original text: 'Create' + createUserButton: undefined, + + // Original text: 'No user found' + noUserFound: undefined, + + // Original text: 'User' + userLabel: undefined, + + // Original text: 'Admin' + adminLabel: undefined, + + // Original text: 'No user in group' + noUserInGroup: undefined, + + // Original text: '{users} user{users, plural, one {} other {s}}' + countUsers: undefined, + + // Original text: 'Select Permission' + selectPermission: undefined, + + // Original text: 'Auto-load at server start' + autoloadPlugin: undefined, + + // Original text: 'Save configuration' + savePluginConfiguration: undefined, + + // Original text: 'Delete configuration' + deletePluginConfiguration: undefined, + + // Original text: 'Plugin error' + pluginError: undefined, + + // Original text: 'Unknown error' + unknownPluginError: undefined, + + // Original text: 'Purge plugin configuration' + purgePluginConfiguration: undefined, + + // Original text: 'Are you sure you want to purge this configuration ?' + purgePluginConfigurationQuestion: undefined, + + // Original text: 'Edit' + editPluginConfiguration: undefined, + + // Original text: 'Cancel' + cancelPluginEdition: undefined, + + // Original text: 'Plugin configuration' + pluginConfigurationSuccess: undefined, + + // Original text: 'Plugin configuration successfully saved!' + pluginConfigurationChanges: undefined, + + // Original text: 'Predefined configuration' + pluginConfigurationPresetTitle: undefined, + + // Original text: 'Choose a predefined configuration.' + pluginConfigurationChoosePreset: undefined, + + // Original text: 'Apply' + applyPluginPreset: undefined, + + // Original text: 'Save filter error' + saveNewUserFilterErrorTitle: undefined, + + // Original text: 'Bad parameter: name must be given.' + saveNewUserFilterErrorBody: undefined, + + // Original text: 'Name:' + filterName: undefined, + + // Original text: 'Value:' + filterValue: undefined, + + // Original text: 'Save new filter' + saveNewFilterTitle: undefined, + + // Original text: 'Set custom filters' + setUserFiltersTitle: undefined, + + // Original text: 'Are you sure you want to set custom filters?' + setUserFiltersBody: undefined, + + // Original text: 'Remove custom filter' + removeUserFilterTitle: undefined, + + // Original text: 'Are you sure you want to remove custom filter?' + removeUserFilterBody: undefined, + + // Original text: 'Default filter' + defaultFilter: undefined, + + // Original text: 'Default filters' + defaultFilters: undefined, + + // Original text: 'Custom filters' + customFilters: undefined, + + // Original text: 'Customize filters' + customizeFilters: undefined, + + // Original text: 'Save custom filters' + saveCustomFilters: undefined, + + // Original text: "Start" + startVmLabel: 'הפעל מכונה', + + // Original text: 'Recovery start' + recoveryModeLabel: undefined, + + // Original text: "Suspend" + suspendVmLabel: 'הקפא מכונה', + + // Original text: "Stop" + stopVmLabel: 'עצור מכונה', + + // Original text: "Force shutdown" + forceShutdownVmLabel: 'עצירה בכוח', + + // Original text: "Reboot" + rebootVmLabel: 'הפעלה מחדש', + + // Original text: "Force reboot" + forceRebootVmLabel: 'הפעלה מחדש בכוח', + + // Original text: "Delete" + deleteVmLabel: 'מחיקה', + + // Original text: 'Migrate' + migrateVmLabel: undefined, + + // Original text: 'Snapshot' + snapshotVmLabel: undefined, + + // Original text: 'Export' + exportVmLabel: undefined, + + // Original text: 'Resume' + resumeVmLabel: undefined, + + // Original text: 'Copy' + copyVmLabel: undefined, + + // Original text: 'Clone' + cloneVmLabel: undefined, + + // Original text: 'Fast clone' + fastCloneVmLabel: undefined, + + // Original text: 'Convert to template' + convertVmToTemplateLabel: undefined, + + // Original text: "Console" + vmConsoleLabel: 'קונסול', + + // Original text: 'Rescan all disks' + srRescan: undefined, + + // Original text: 'Connect to all hosts' + srReconnectAll: undefined, + + // Original text: 'Disconnect to all hosts' + srDisconnectAll: undefined, + + // Original text: 'Forget this SR' + srForget: undefined, + + // Original text: 'Remove this SR' + srRemoveButton: undefined, + + // Original text: 'No VDIs in this storage' + srNoVdis: undefined, + + // Original text: 'Pool RAM usage:' + poolTitleRamUsage: undefined, + + // Original text: '{used} used on {total}' + poolRamUsage: undefined, + + // Original text: 'Master:' + poolMaster: undefined, + + // Original text: 'Hosts' + hostsTabName: undefined, + + // Original text: 'High Availability' + poolHaStatus: undefined, + + // Original text: 'Enabled' + poolHaEnabled: undefined, + + // Original text: 'Disabled' + poolHaDisabled: undefined, + + // Original text: 'Name' + hostNameLabel: undefined, + + // Original text: 'Description' + hostDescription: undefined, + + // Original text: 'Memory' + hostMemory: undefined, + + // Original text: 'No hosts' + noHost: undefined, + + // Original text: '{used}% used ({free} free)' + memoryLeftTooltip: undefined, + + // Original text: 'Name' + poolNetworkNameLabel: undefined, + + // Original text: 'Description' + poolNetworkDescription: undefined, + + // Original text: 'PIFs' + poolNetworkPif: undefined, + + // Original text: 'No networks' + poolNoNetwork: undefined, + + // Original text: 'MTU' + poolNetworkMTU: undefined, + + // Original text: 'Connected' + poolNetworkPifAttached: undefined, + + // Original text: 'Disconnected' + poolNetworkPifDetached: undefined, + + // Original text: 'Show PIFs' + showPifs: undefined, + + // Original text: 'Hide PIFs' + hidePifs: undefined, + + // Original text: 'Add SR' + addSrLabel: undefined, + + // Original text: 'Add VM' + addVmLabel: undefined, + + // Original text: 'Add Host' + addHostLabel: undefined, + + // Original text: 'Disconnect' + disconnectServer: undefined, + + // Original text: 'Start' + startHostLabel: undefined, + + // Original text: 'Stop' + stopHostLabel: undefined, + + // Original text: 'Enable' + enableHostLabel: undefined, + + // Original text: 'Disable' + disableHostLabel: undefined, + + // Original text: 'Restart toolstack' + restartHostAgent: undefined, + + // Original text: 'Force reboot' + forceRebootHostLabel: undefined, + + // Original text: 'Reboot' + rebootHostLabel: undefined, + + // Original text: 'Reboot to apply updates' + rebootUpdateHostLabel: undefined, + + // Original text: 'Emergency mode' + emergencyModeLabel: undefined, + + // Original text: 'Storage' + storageTabName: undefined, + + // Original text: 'Patches' + patchesTabName: undefined, + + // Original text: 'Load average' + statLoad: undefined, + + // Original text: 'Hardware' + hardwareHostSettingsLabel: undefined, + + // Original text: 'Address' + hostAddress: undefined, + + // Original text: 'Status' + hostStatus: undefined, + + // Original text: 'Build number' + hostBuildNumber: undefined, + + // Original text: 'iSCSI name' + hostIscsiName: undefined, + + // Original text: 'Version' + hostXenServerVersion: undefined, + + // Original text: 'Enabled' + hostStatusEnabled: undefined, + + // Original text: 'Disabled' + hostStatusDisabled: undefined, + + // Original text: 'Power on mode' + hostPowerOnMode: undefined, + + // Original text: 'Host uptime' + hostStartedSince: undefined, + + // Original text: 'Toolstack uptime' + hostStackStartedSince: undefined, + + // Original text: 'CPU model' + hostCpusModel: undefined, + + // Original text: 'Core (socket)' + hostCpusNumber: undefined, + + // Original text: 'Manufacturer info' + hostManufacturerinfo: undefined, + + // Original text: 'BIOS info' + hostBiosinfo: undefined, + + // Original text: 'Licence' + licenseHostSettingsLabel: undefined, + + // Original text: 'Type' + hostLicenseType: undefined, + + // Original text: 'Socket' + hostLicenseSocket: undefined, + + // Original text: 'Expiry' + hostLicenseExpiry: undefined, + + // Original text: 'Add a network' + networkCreateButton: undefined, + + // Original text: 'Add a bonded network' + networkCreateBondedButton: undefined, + + // Original text: 'Device' + pifDeviceLabel: undefined, + + // Original text: 'Network' + pifNetworkLabel: undefined, + + // Original text: 'VLAN' + pifVlanLabel: undefined, + + // Original text: 'Address' + pifAddressLabel: undefined, + + // Original text: 'Mode' + pifModeLabel: undefined, + + // Original text: 'MAC' + pifMacLabel: undefined, + + // Original text: 'MTU' + pifMtuLabel: undefined, + + // Original text: 'Status' + pifStatusLabel: undefined, + + // Original text: 'Connected' + pifStatusConnected: undefined, + + // Original text: 'Disconnected' + pifStatusDisconnected: undefined, + + // Original text: 'No physical interface detected' + pifNoInterface: undefined, + + // Original text: 'This interface is currently in use' + pifInUse: undefined, + + // Original text: 'Default locking mode' + defaultLockingMode: undefined, + + // Original text: 'Configure IP address' + pifConfigureIp: undefined, + + // Original text: 'Invalid parameters' + configIpErrorTitle: undefined, + + // Original text: 'IP address and netmask required' + configIpErrorMessage: undefined, + + // Original text: 'Static IP address' + staticIp: undefined, + + // Original text: 'Netmask' + netmask: undefined, + + // Original text: 'DNS' + dns: undefined, + + // Original text: 'Gateway' + gateway: undefined, + + // Original text: 'Add a storage' + addSrDeviceButton: undefined, + + // Original text: 'Name' + srNameLabel: undefined, + + // Original text: 'Type' + srType: undefined, + + // Original text: 'Status' + pbdStatus: undefined, + + // Original text: 'Connected' + pbdStatusConnected: undefined, + + // Original text: 'Disconnected' + pbdStatusDisconnected: undefined, + + // Original text: 'Connect' + pbdConnect: undefined, + + // Original text: 'Disconnect' + pbdDisconnect: undefined, + + // Original text: 'Forget' + pbdForget: undefined, + + // Original text: 'Shared' + srShared: undefined, + + // Original text: 'Not shared' + srNotShared: undefined, + + // Original text: 'No storage detected' + pbdNoSr: undefined, + + // Original text: 'Name' + patchNameLabel: undefined, + + // Original text: 'Install all patches' + patchUpdateButton: undefined, + + // Original text: 'Description' + patchDescription: undefined, + + // Original text: 'Applied date' + patchApplied: undefined, + + // Original text: 'Size' + patchSize: undefined, + + // Original text: 'Status' + patchStatus: undefined, + + // Original text: 'Applied' + patchStatusApplied: undefined, + + // Original text: 'Missing patches' + patchStatusNotApplied: undefined, + + // Original text: 'No patch detected' + patchNothing: undefined, + + // Original text: 'Release date' + patchReleaseDate: undefined, + + // Original text: 'Guidance' + patchGuidance: undefined, + + // Original text: 'Action' + patchAction: undefined, + + // Original text: 'Applied patches' + hostAppliedPatches: undefined, + + // Original text: 'Missing patches' + hostMissingPatches: undefined, + + // Original text: 'Host up-to-date!' + hostUpToDate: undefined, + + // Original text: 'Refresh patches' + refreshPatches: undefined, + + // Original text: 'Install pool patches' + installPoolPatches: undefined, + + // Original text: 'Default SR' + defaultSr: undefined, + + // Original text: 'Set as default SR' + setAsDefaultSr: undefined, + + // Original text: 'General' + generalTabName: undefined, + + // Original text: 'Stats' + statsTabName: undefined, + + // Original text: 'Console' + consoleTabName: undefined, + + // Original text: 'Container' + containersTabName: undefined, + + // Original text: 'Snapshots' + snapshotsTabName: undefined, + + // Original text: 'Logs' + logsTabName: undefined, + + // Original text: 'Advanced' + advancedTabName: undefined, + + // Original text: 'Network' + networkTabName: undefined, + + // Original text: 'Disk{disks, plural, one {} other {s}}' + disksTabName: undefined, + + // Original text: 'halted' + powerStateHalted: undefined, + + // Original text: 'running' + powerStateRunning: undefined, + + // Original text: 'suspended' + powerStateSuspended: undefined, + + // Original text: 'No Xen tools detected' + vmStatus: undefined, + + // Original text: 'No IPv4 record' + vmName: undefined, + + // Original text: 'No IP record' + vmDescription: undefined, + + // Original text: 'Started {ago}' + vmSettings: undefined, + + // Original text: 'Current status:' + vmCurrentStatus: undefined, + + // Original text: 'Not running' + vmNotRunning: undefined, + + // Original text: 'No Xen tools detected' + noToolsDetected: undefined, + + // Original text: 'No IPv4 record' + noIpv4Record: undefined, + + // Original text: 'No IP record' + noIpRecord: undefined, + + // Original text: 'Started {ago}' + started: undefined, + + // Original text: 'Paravirtualization (PV)' + paraVirtualizedMode: undefined, + + // Original text: 'Hardware virtualization (HVM)' + hardwareVirtualizedMode: undefined, + + // Original text: 'CPU usage' + statsCpu: undefined, + + // Original text: 'Memory usage' + statsMemory: undefined, + + // Original text: 'Network throughput' + statsNetwork: undefined, + + // Original text: 'Stacked values' + useStackedValuesOnStats: undefined, + + // Original text: 'Disk throughput' + statDisk: undefined, + + // Original text: 'Last 10 minutes' + statLastTenMinutes: undefined, + + // Original text: 'Last 2 hours' + statLastTwoHours: undefined, + + // Original text: 'Last week' + statLastWeek: undefined, + + // Original text: 'Last year' + statLastYear: undefined, + + // Original text: 'Copy' + copyToClipboardLabel: undefined, + + // Original text: 'Ctrl+Alt+Del' + ctrlAltDelButtonLabel: undefined, + + // Original text: 'Tip:' + tipLabel: undefined, + + // Original text: 'non-US keyboard could have issues with console: switch your own layout to US.' + tipConsoleLabel: undefined, + + // Original text: 'Hide infos' + hideHeaderTooltip: undefined, + + // Original text: 'Show infos' + showHeaderTooltip: undefined, + + // Original text: 'Name' + containerName: undefined, + + // Original text: 'Command' + containerCommand: undefined, + + // Original text: 'Creation date' + containerCreated: undefined, + + // Original text: 'Status' + containerStatus: undefined, + + // Original text: 'Action' + containerAction: undefined, + + // Original text: 'No existing containers' + noContainers: undefined, + + // Original text: 'Stop this container' + containerStop: undefined, + + // Original text: 'Start this container' + containerStart: undefined, + + // Original text: 'Pause this container' + containerPause: undefined, + + // Original text: 'Resume this container' + containerResume: undefined, + + // Original text: 'Restart this container' + containerRestart: undefined, + + // Original text: 'Action' + vdiAction: undefined, + + // Original text: 'Attach disk' + vdiAttachDeviceButton: undefined, + + // Original text: 'New disk' + vbdCreateDeviceButton: undefined, + + // Original text: 'Boot order' + vdiBootOrder: undefined, + + // Original text: 'Name' + vdiNameLabel: undefined, + + // Original text: 'Description' + vdiNameDescription: undefined, + + // Original text: 'Tags' + vdiTags: undefined, + + // Original text: 'Size' + vdiSize: undefined, + + // Original text: 'SR' + vdiSr: undefined, + + // Original text: 'VM' + vdiVm: undefined, + + // Original text: 'Migrate VDI' + vdiMigrate: undefined, + + // Original text: 'Destination SR:' + vdiMigrateSelectSr: undefined, + + // Original text: 'Migrate all VDIs' + vdiMigrateAll: undefined, + + // Original text: 'No SR' + vdiMigrateNoSr: undefined, + + // Original text: 'A target SR is required to migrate a VDI' + vdiMigrateNoSrMessage: undefined, + + // Original text: 'Forget' + vdiForget: undefined, + + // Original text: 'Remove VDI' + vdiRemove: undefined, + + // Original text: 'Boot flag' + vbdBootableStatus: undefined, + + // Original text: 'Status' + vbdStatus: undefined, + + // Original text: 'Connected' + vbdStatusConnected: undefined, + + // Original text: 'Disconnected' + vbdStatusDisconnected: undefined, + + // Original text: 'No disks' + vbdNoVbd: undefined, + + // Original text: 'Connect VBD' + vbdConnect: undefined, + + // Original text: 'Disconnect VBD' + vbdDisconnect: undefined, + + // Original text: 'Bootable' + vbdBootable: undefined, + + // Original text: 'Readonly' + vbdReadonly: undefined, + + // Original text: 'Create' + vbdCreate: undefined, + + // Original text: 'Disk name' + vbdNamePlaceHolder: undefined, + + // Original text: 'Size' + vbdSizePlaceHolder: undefined, + + // Original text: 'Save' + saveBootOption: undefined, + + // Original text: 'Reset' + resetBootOption: undefined, + + // Original text: 'New device' + vifCreateDeviceButton: undefined, + + // Original text: 'No interface' + vifNoInterface: undefined, + + // Original text: 'Device' + vifDeviceLabel: undefined, + + // Original text: 'MAC address' + vifMacLabel: undefined, + + // Original text: 'MTU' + vifMtuLabel: undefined, + + // Original text: 'Network' + vifNetworkLabel: undefined, + + // Original text: 'Status' + vifStatusLabel: undefined, + + // Original text: 'Connected' + vifStatusConnected: undefined, + + // Original text: 'Disconnected' + vifStatusDisconnected: undefined, + + // Original text: 'Connect' + vifConnect: undefined, + + // Original text: 'Disconnect' + vifDisconnect: undefined, + + // Original text: 'Remove' + vifRemove: undefined, + + // Original text: 'IP addresses' + vifIpAddresses: undefined, + + // Original text: 'Auto-generated if empty' + vifMacAutoGenerate: undefined, + + // Original text: 'Allowed IPs' + vifAllowedIps: undefined, + + // Original text: 'No IPs' + vifNoIps: undefined, + + // Original text: 'Network locked' + vifLockedNetwork: undefined, + + // Original text: 'Network locked and no IPs are allowed for this interface' + vifLockedNetworkNoIps: undefined, + + // Original text: 'Network not locked' + vifUnLockedNetwork: undefined, + + // Original text: 'Unknown network' + vifUnknownNetwork: undefined, + + // Original text: 'Create' + vifCreate: undefined, + + // Original text: 'No snapshots' + noSnapshots: undefined, + + // Original text: 'New snapshot' + snapshotCreateButton: undefined, + + // Original text: 'Just click on the snapshot button to create one!' + tipCreateSnapshotLabel: undefined, + + // Original text: 'Revert VM to this snapshot' + revertSnapshot: undefined, + + // Original text: 'Remove this snapshot' + deleteSnapshot: undefined, + + // Original text: 'Create a VM from this snapshot' + copySnapshot: undefined, + + // Original text: 'Export this snapshot' + exportSnapshot: undefined, + + // Original text: 'Creation date' + snapshotDate: undefined, + + // Original text: 'Name' + snapshotName: undefined, + + // Original text: 'Action' + snapshotAction: undefined, + + // Original text: 'Remove all logs' + logRemoveAll: undefined, + + // Original text: 'No logs so far' + noLogs: undefined, + + // Original text: 'Creation date' + logDate: undefined, + + // Original text: 'Name' + logName: undefined, + + // Original text: 'Content' + logContent: undefined, + + // Original text: 'Action' + logAction: undefined, + + // Original text: 'Remove' + vmRemoveButton: undefined, + + // Original text: 'Convert' + vmConvertButton: undefined, + + // Original text: 'Xen settings' + xenSettingsLabel: undefined, + + // Original text: 'Guest OS' + guestOsLabel: undefined, + + // Original text: 'Misc' + miscLabel: undefined, + + // Original text: 'UUID' + uuid: undefined, + + // Original text: 'Virtualization mode' + virtualizationMode: undefined, + + // Original text: 'CPU weight' + cpuWeightLabel: undefined, + + // Original text: 'Default ({value, number})' + defaultCpuWeight: undefined, + + // Original text: 'CPU cap' + cpuCapLabel: undefined, + + // Original text: 'Default ({value, number})' + defaultCpuCap: undefined, + + // Original text: 'PV args' + pvArgsLabel: undefined, + + // Original text: 'Xen tools status' + xenToolsStatus: undefined, + + // Original text: '{status}' + xenToolsStatusValue: undefined, + + // Original text: 'OS name' + osName: undefined, + + // Original text: 'OS kernel' + osKernel: undefined, + + // Original text: 'Auto power on' + autoPowerOn: undefined, + + // Original text: 'HA' + ha: undefined, + + // Original text: 'Original template' + originalTemplate: undefined, + + // Original text: 'Unknown' + unknownOsName: undefined, + + // Original text: 'Unknown' + unknownOsKernel: undefined, + + // Original text: 'Unknown' + unknownOriginalTemplate: undefined, + + // Original text: 'VM limits' + vmLimitsLabel: undefined, + + // Original text: 'CPU limits' + vmCpuLimitsLabel: undefined, + + // Original text: 'Memory limits (min/max)' + vmMemoryLimitsLabel: undefined, + + // Original text: 'vCPUs max:' + vmMaxVcpus: undefined, + + // Original text: 'Memory max:' + vmMaxRam: undefined, + + // Original text: 'Long click to add a name' + vmHomeNamePlaceholder: undefined, + + // Original text: 'Long click to add a description' + vmHomeDescriptionPlaceholder: undefined, + + // Original text: 'Click to add a name' + vmViewNamePlaceholder: undefined, + + // Original text: 'Click to add a description' + vmViewDescriptionPlaceholder: undefined, + + // Original text: 'Click to add a name' + templateHomeNamePlaceholder: undefined, + + // Original text: 'Click to add a description' + templateHomeDescriptionPlaceholder: undefined, + + // Original text: 'Delete template' + templateDelete: undefined, + + // Original text: 'Delete VM template{templates, plural, one {} other {s}}' + templateDeleteModalTitle: undefined, + + // Original text: 'Are you sure you want to delete {templates, plural, one {this} other {these}} template{templates, plural, one {} other {s}}?' + templateDeleteModalBody: undefined, + + // Original text: 'Pool{pools, plural, one {} other {s}}' + poolPanel: undefined, + + // Original text: 'Host{hosts, plural, one {} other {s}}' + hostPanel: undefined, + + // Original text: 'VM{vms, plural, one {} other {s}}' + vmPanel: undefined, + + // Original text: 'RAM Usage' + memoryStatePanel: undefined, + + // Original text: 'CPUs Usage' + cpuStatePanel: undefined, + + // Original text: 'VMs Power state' + vmStatePanel: undefined, + + // Original text: 'Pending tasks' + taskStatePanel: undefined, + + // Original text: 'Users' + usersStatePanel: undefined, + + // Original text: 'Storage state' + srStatePanel: undefined, + + // Original text: '{usage} (of {total})' + ofUsage: undefined, + + // Original text: 'No storage' + noSrs: undefined, + + // Original text: 'Name' + srName: undefined, + + // Original text: 'Pool' + srPool: undefined, + + // Original text: 'Host' + srHost: undefined, + + // Original text: 'Type' + srFormat: undefined, + + // Original text: 'Size' + srSize: undefined, + + // Original text: 'Usage' + srUsage: undefined, + + // Original text: 'used' + srUsed: undefined, + + // Original text: 'free' + srFree: undefined, + + // Original text: 'Storage Usage' + srUsageStatePanel: undefined, + + // Original text: 'Top 5 SR Usage (in %)' + srTopUsageStatePanel: undefined, + + // Original text: '{running} running ({halted} halted)' + vmsStates: undefined, + + // Original text: 'Clear selection' + dashboardStatsButtonRemoveAll: undefined, + + // Original text: 'Add all hosts' + dashboardStatsButtonAddAllHost: undefined, + + // Original text: 'Add all VMs' + dashboardStatsButtonAddAllVM: undefined, + + // Original text: '{value} {date, date, medium}' + weekHeatmapData: undefined, + + // Original text: 'No data.' + weekHeatmapNoData: undefined, + + // Original text: 'Weekly Heatmap' + weeklyHeatmap: undefined, + + // Original text: 'Weekly Charts' + weeklyCharts: undefined, + + // Original text: 'Synchronize scale:' + weeklyChartsScaleInfo: undefined, + + // Original text: 'Stats error' + statsDashboardGenericErrorTitle: undefined, + + // Original text: 'There is no stats available for:' + statsDashboardGenericErrorMessage: undefined, + + // Original text: 'No selected metric' + noSelectedMetric: undefined, + + // Original text: 'Select' + statsDashboardSelectObjects: undefined, + + // Original text: 'Loading…' + metricsLoading: undefined, + + // Original text: 'Coming soon!' + comingSoon: undefined, + + // Original text: 'Orphaned snapshot VDIs' + orphanedVdis: undefined, + + // Original text: 'Orphaned VMs snapshot' + orphanedVms: undefined, + + // Original text: 'No orphans' + noOrphanedObject: undefined, + + // Original text: 'Remove all orphaned snapshot VDIs' + removeAllOrphanedObject: undefined, + + // Original text: 'Name' + vmNameLabel: undefined, + + // Original text: 'Description' + vmNameDescription: undefined, + + // Original text: 'Resident on' + vmContainer: undefined, + + // Original text: 'Alarms' + alarmMessage: undefined, + + // Original text: 'No alarms' + noAlarms: undefined, + + // Original text: 'Date' + alarmDate: undefined, + + // Original text: 'Content' + alarmContent: undefined, + + // Original text: 'Issue on' + alarmObject: undefined, + + // Original text: 'Pool' + alarmPool: undefined, + + // Original text: 'Remove all alarms' + alarmRemoveAll: undefined, + + // Original text: '{used}% used ({free} left)' + spaceLeftTooltip: undefined, + + // Original text: 'Create a new VM on {select}' + newVmCreateNewVmOn: undefined, + + // Original text: 'Create a new VM on {select1} or {select2}' + newVmCreateNewVmOn2: undefined, + + // Original text: 'You have no permission to create a VM' + newVmCreateNewVmNoPermission: undefined, + + // Original text: 'Infos' + newVmInfoPanel: undefined, + + // Original text: 'Name' + newVmNameLabel: undefined, + + // Original text: 'Template' + newVmTemplateLabel: undefined, + + // Original text: 'Description' + newVmDescriptionLabel: undefined, + + // Original text: 'Performances' + newVmPerfPanel: undefined, + + // Original text: 'vCPUs' + newVmVcpusLabel: undefined, + + // Original text: 'RAM' + newVmRamLabel: undefined, + + // Original text: 'Static memory max' + newVmStaticMaxLabel: undefined, + + // Original text: 'Dynamic memory min' + newVmDynamicMinLabel: undefined, + + // Original text: 'Dynamic memory max' + newVmDynamicMaxLabel: undefined, + + // Original text: 'Install settings' + newVmInstallSettingsPanel: undefined, + + // Original text: 'ISO/DVD' + newVmIsoDvdLabel: undefined, + + // Original text: 'Network' + newVmNetworkLabel: undefined, + + // Original text: 'e.g: http://httpredir.debian.org/debian' + newVmInstallNetworkPlaceHolder: undefined, + + // Original text: 'PV Args' + newVmPvArgsLabel: undefined, + + // Original text: 'PXE' + newVmPxeLabel: undefined, + + // Original text: 'Interfaces' + newVmInterfacesPanel: undefined, + + // Original text: 'MAC' + newVmMacLabel: undefined, + + // Original text: 'Add interface' + newVmAddInterface: undefined, + + // Original text: 'Disks' + newVmDisksPanel: undefined, + + // Original text: 'SR' + newVmSrLabel: undefined, + + // Original text: 'Bootable' + newVmBootableLabel: undefined, + + // Original text: 'Size' + newVmSizeLabel: undefined, + + // Original text: 'Add disk' + newVmAddDisk: undefined, + + // Original text: 'Summary' + newVmSummaryPanel: undefined, + + // Original text: 'Create' + newVmCreate: undefined, + + // Original text: 'Reset' + newVmReset: undefined, + + // Original text: 'Select template' + newVmSelectTemplate: undefined, + + // Original text: 'SSH key' + newVmSshKey: undefined, + + // Original text: 'Config drive' + newVmConfigDrive: undefined, + + // Original text: 'Custom config' + newVmCustomConfig: undefined, + + // Original text: 'Boot VM after creation' + newVmBootAfterCreate: undefined, + + // Original text: 'Auto-generated if empty' + newVmMacPlaceholder: undefined, + + // Original text: 'CPU weight' + newVmCpuWeightLabel: undefined, + + // Original text: 'Default: {value, number}' + newVmDefaultCpuWeight: undefined, + + // Original text: 'CPU cap' + newVmCpuCapLabel: undefined, + + // Original text: 'Default: {value, number}' + newVmDefaultCpuCap: undefined, + + // Original text: 'Cloud config' + newVmCloudConfig: undefined, + + // Original text: 'Create VMs' + newVmCreateVms: undefined, + + // Original text: 'Are you sure you want to create {nbVms} VMs?' + newVmCreateVmsConfirm: undefined, + + // Original text: 'Multiple VMs:' + newVmMultipleVms: undefined, + + // Original text: 'Select a resource set:' + newVmSelectResourceSet: undefined, + + // Original text: 'Name pattern:' + newVmMultipleVmsPattern: undefined, + + // Original text: 'e.g.: \\{name\\}_%' + newVmMultipleVmsPatternPlaceholder: undefined, + + // Original text: 'First index:' + newVmFirstIndex: undefined, + + // Original text: 'Recalculate VMs number' + newVmNumberRecalculate: undefined, + + // Original text: 'Refresh VMs name' + newVmNameRefresh: undefined, + + // Original text: 'Advanced' + newVmAdvancedPanel: undefined, + + // Original text: 'Show advanced settings' + newVmShowAdvanced: undefined, + + // Original text: 'Hide advanced settings' + newVmHideAdvanced: undefined, + + // Original text: 'Resource sets' + resourceSets: undefined, + + // Original text: 'No resource sets.' + noResourceSets: undefined, + + // Original text: 'Loading resource sets' + loadingResourceSets: undefined, + + // Original text: 'Resource set name' + resourceSetName: undefined, + + // Original text: 'Recompute all limits' + recomputeResourceSets: undefined, + + // Original text: 'Save' + saveResourceSet: undefined, + + // Original text: 'Reset' + resetResourceSet: undefined, + + // Original text: 'Edit' + editResourceSet: undefined, + + // Original text: 'Delete' + deleteResourceSet: undefined, + + // Original text: 'Delete resource set' + deleteResourceSetWarning: undefined, + + // Original text: 'Are you sure you want to delete this resource set?' + deleteResourceSetQuestion: undefined, + + // Original text: 'Missing objects:' + resourceSetMissingObjects: undefined, + + // Original text: 'vCPUs' + resourceSetVcpus: undefined, + + // Original text: 'Memory' + resourceSetMemory: undefined, + + // Original text: 'Storage' + resourceSetStorage: undefined, + + // Original text: 'Unknown' + unknownResourceSetValue: undefined, + + // Original text: 'Available hosts' + availableHosts: undefined, + + // Original text: 'Excluded hosts' + excludedHosts: undefined, + + // Original text: 'No hosts available.' + noHostsAvailable: undefined, + + // Original text: 'VMs created from this resource set shall run on the following hosts.' + availableHostsDescription: undefined, + + // Original text: 'Maximum CPUs' + maxCpus: undefined, + + // Original text: 'Maximum RAM (GiB)' + maxRam: undefined, + + // Original text: 'Maximum disk space' + maxDiskSpace: undefined, + + // Original text: 'IP pool' + ipPool: undefined, + + // Original text: 'Quantity' + quantity: undefined, + + // Original text: 'No limits.' + noResourceSetLimits: undefined, + + // Original text: 'Total:' + totalResource: undefined, + + // Original text: 'Remaining:' + remainingResource: undefined, + + // Original text: 'Used:' + usedResource: undefined, + + // Original text: 'New' + resourceSetNew: undefined, + + // Original text: 'Try dropping some VMs files here, or click to select VMs to upload. Accept only .xva/.ova files.' + importVmsList: undefined, + + // Original text: 'No selected VMs.' + noSelectedVms: undefined, + + // Original text: 'To Pool:' + vmImportToPool: undefined, + + // Original text: 'To SR:' + vmImportToSr: undefined, + + // Original text: 'VMs to import' + vmsToImport: undefined, + + // Original text: 'Reset' + importVmsCleanList: undefined, + + // Original text: 'VM import success' + vmImportSuccess: undefined, + + // Original text: 'VM import failed' + vmImportFailed: undefined, + + // Original text: 'Import starting…' + startVmImport: undefined, + + // Original text: 'Export starting…' + startVmExport: undefined, + + // Original text: 'N CPUs' + nCpus: undefined, + + // Original text: 'Memory' + vmMemory: undefined, + + // Original text: 'Disk {position} ({capacity})' + diskInfo: undefined, + + // Original text: 'Disk description' + diskDescription: undefined, + + // Original text: 'No disks.' + noDisks: undefined, + + // Original text: 'No networks.' + noNetworks: undefined, + + // Original text: 'Network {name}' + networkInfo: undefined, + + // Original text: 'No description available' + noVmImportErrorDescription: undefined, + + // Original text: 'Error:' + vmImportError: undefined, + + // Original text: '{type} file:' + vmImportFileType: undefined, + + // Original text: 'Please to check and/or modify the VM configuration.' + vmImportConfigAlert: undefined, + + // Original text: 'No pending tasks' + noTasks: undefined, + + // Original text: 'Currently, there are not any pending XenServer tasks' + xsTasks: undefined, + + // Original text: 'Schedules' + backupSchedules: undefined, + + // Original text: 'Get remote' + getRemote: undefined, + + // Original text: 'List Remote' + listRemote: undefined, + + // Original text: 'simple' + simpleBackup: undefined, + + // Original text: 'delta' + delta: undefined, + + // Original text: 'Restore Backups' + restoreBackups: undefined, + + // Original text: 'Click on a VM to display restore options' + restoreBackupsInfo: undefined, + + // Original text: 'Enabled' + remoteEnabled: undefined, + + // Original text: 'Error' + remoteError: undefined, + + // Original text: 'No backup available' + noBackup: undefined, + + // Original text: 'VM Name' + backupVmNameColumn: undefined, + + // Original text: 'Tags' + backupTags: undefined, + + // Original text: 'Last Backup' + lastBackupColumn: undefined, + + // Original text: 'Available Backups' + availableBackupsColumn: undefined, + + // Original text: 'Missing parameters' + backupRestoreErrorTitle: undefined, + + // Original text: 'Choose a SR and a backup' + backupRestoreErrorMessage: undefined, + + // Original text: 'Display backups' + displayBackup: undefined, + + // Original text: 'Import VM' + importBackupTitle: undefined, + + // Original text: 'Starting your backup import' + importBackupMessage: undefined, + + // Original text: 'VMs to backup' + vmsToBackup: undefined, + + // Original text: 'Emergency shutdown Host{nHosts, plural, one {} other {s}}' + emergencyShutdownHostsModalTitle: undefined, + + // Original text: 'Are you sure you want to shutdown {nHosts} Host{nHosts, plural, one {} other {s}}?' + emergencyShutdownHostsModalMessage: undefined, + + // Original text: 'Shutdown host' + stopHostModalTitle: undefined, + + // Original text: "This will shutdown your host. Do you want to continue? If it's the pool master, your connection to the pool will be lost" + stopHostModalMessage: undefined, + + // Original text: 'Add host' + addHostModalTitle: undefined, + + // Original text: 'Are you sure you want to add {host} to {pool}?' + addHostModalMessage: undefined, + + // Original text: 'Restart host' + restartHostModalTitle: undefined, + + // Original text: 'This will restart your host. Do you want to continue?' + restartHostModalMessage: undefined, + + // Original text: 'Restart Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}' + restartHostsAgentsModalTitle: undefined, + + // Original text: 'Are you sure you want to restart {nHosts} Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}?' + restartHostsAgentsModalMessage: undefined, + + // Original text: 'Restart Host{nHosts, plural, one {} other {s}}' + restartHostsModalTitle: undefined, + + // Original text: 'Are you sure you want to restart {nHosts} Host{nHosts, plural, one {} other {s}}?' + restartHostsModalMessage: undefined, + + // Original text: 'Start VM{vms, plural, one {} other {s}}' + startVmsModalTitle: undefined, + + // Original text: 'Are you sure you want to start {vms} VM{vms, plural, one {} other {s}}?' + startVmsModalMessage: undefined, + + // Original text: 'Stop Host{nHosts, plural, one {} other {s}}' + stopHostsModalTitle: undefined, + + // Original text: 'Are you sure you want to stop {nHosts} Host{nHosts, plural, one {} other {s}}?' + stopHostsModalMessage: undefined, + + // Original text: 'Stop VM{vms, plural, one {} other {s}}' + stopVmsModalTitle: undefined, + + // Original text: 'Are you sure you want to stop {vms} VM{vms, plural, one {} other {s}}?' + stopVmsModalMessage: undefined, + + // Original text: 'Restart VM' + restartVmModalTitle: undefined, + + // Original text: 'Are you sure you want to restart {name}?' + restartVmModalMessage: undefined, + + // Original text: 'Stop VM' + stopVmModalTitle: undefined, + + // Original text: 'Are you sure you want to stop {name}?' + stopVmModalMessage: undefined, + + // Original text: 'Restart VM{vms, plural, one {} other {s}}' + restartVmsModalTitle: undefined, + + // Original text: 'Are you sure you want to restart {vms} VM{vms, plural, one {} other {s}}?' + restartVmsModalMessage: undefined, + + // Original text: 'Snapshot VM{vms, plural, one {} other {s}}' + snapshotVmsModalTitle: undefined, + + // Original text: 'Are you sure you want to snapshot {vms} VM{vms, plural, one {} other {s}}?' + snapshotVmsModalMessage: undefined, + + // Original text: 'Delete VM{vms, plural, one {} other {s}}' + deleteVmsModalTitle: undefined, + + // Original text: 'Are you sure you want to delete {vms} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED' + deleteVmsModalMessage: undefined, + + // Original text: 'Delete VM' + deleteVmModalTitle: undefined, + + // Original text: 'Are you sure you want to delete this VM? ALL VM DISKS WILL BE REMOVED' + deleteVmModalMessage: undefined, + + // Original text: 'Migrate VM' + migrateVmModalTitle: undefined, + + // Original text: 'Select a destination host:' + migrateVmSelectHost: undefined, + + // Original text: 'Select a migration network:' + migrateVmSelectMigrationNetwork: undefined, + + // Original text: 'For each VDI, select an SR:' + migrateVmSelectSrs: undefined, + + // Original text: 'For each VIF, select a network:' + migrateVmSelectNetworks: undefined, + + // Original text: 'Select a destination SR:' + migrateVmsSelectSr: undefined, + + // Original text: 'Select a destination SR for local disks:' + migrateVmsSelectSrIntraPool: undefined, + + // Original text: 'Select a network on which to connect each VIF:' + migrateVmsSelectNetwork: undefined, + + // Original text: 'Smart mapping' + migrateVmsSmartMapping: undefined, + + // Original text: 'Name' + migrateVmName: undefined, + + // Original text: 'SR' + migrateVmSr: undefined, + + // Original text: 'VIF' + migrateVmVif: undefined, + + // Original text: 'Network' + migrateVmNetwork: undefined, + + // Original text: 'No target host' + migrateVmNoTargetHost: undefined, + + // Original text: 'A target host is required to migrate a VM' + migrateVmNoTargetHostMessage: undefined, + + // Original text: 'Delete VDI' + deleteVdiModalTitle: undefined, + + // Original text: 'Are you sure you want to delete this disk? ALL DATA ON THIS DISK WILL BE LOST' + deleteVdiModalMessage: undefined, + + // Original text: 'Revert your VM' + revertVmModalTitle: undefined, + + // Original text: 'Delete snapshot' + deleteSnapshotModalTitle: undefined, + + // Original text: 'Are you sure you want to delete this snapshot?' + deleteSnapshotModalMessage: undefined, + + // Original text: 'Are you sure you want to revert this VM to the snapshot state? This operation is irreversible.' + revertVmModalMessage: undefined, + + // Original text: 'Snapshot before' + revertVmModalSnapshotBefore: undefined, + + // Original text: 'Import a {name} Backup' + importBackupModalTitle: undefined, + + // Original text: 'Start VM after restore' + importBackupModalStart: undefined, + + // Original text: 'Select your backup…' + importBackupModalSelectBackup: undefined, + + // Original text: 'Are you sure you want to remove all orphaned snapshot VDIs?' + removeAllOrphanedModalWarning: undefined, + + // Original text: 'Remove all logs' + removeAllLogsModalTitle: undefined, + + // Original text: 'Are you sure you want to remove all logs?' + removeAllLogsModalWarning: undefined, + + // Original text: 'This operation is definitive.' + definitiveMessageModal: undefined, + + // Original text: 'Previous SR Usage' + existingSrModalTitle: undefined, + + // Original text: 'This path has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation.' + existingSrModalText: undefined, + + // Original text: 'Previous LUN Usage' + existingLunModalTitle: undefined, + + // Original text: 'This LUN has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation.' + existingLunModalText: undefined, + + // Original text: 'Replace current registration?' + alreadyRegisteredModal: undefined, + + // Original text: 'Your XO appliance is already registered to {email}, do you want to forget and replace this registration ?' + alreadyRegisteredModalText: undefined, + + // Original text: 'Ready for trial?' + trialReadyModal: undefined, + + // Original text: 'During the trial period, XOA need to have a working internet connection. This limitation does not apply for our paid plans!' + trialReadyModalText: undefined, + + // Original text: 'Host' + serverHost: undefined, + + // Original text: 'Username' + serverUsername: undefined, + + // Original text: 'Password' + serverPassword: undefined, + + // Original text: 'Action' + serverAction: undefined, + + // Original text: 'Read Only' + serverReadOnly: undefined, + + // Original text: 'Disconnect server' + serverDisconnect: undefined, + + // Original text: 'username' + serverPlaceHolderUser: undefined, + + // Original text: 'password' + serverPlaceHolderPassword: undefined, + + // Original text: 'address[:port]' + serverPlaceHolderAddress: undefined, + + // Original text: 'Connect' + serverConnect: undefined, + + // Original text: 'Copy VM' + copyVm: undefined, + + // Original text: 'Are you sure you want to copy this VM to {SR}?' + copyVmConfirm: undefined, + + // Original text: 'Name' + copyVmName: undefined, + + // Original text: 'Name pattern' + copyVmNamePattern: undefined, + + // Original text: 'If empty: name of the copied VM' + copyVmNamePlaceholder: undefined, + + // Original text: 'e.g.: "\\{name\\}_COPY"' + copyVmNamePatternPlaceholder: undefined, + + // Original text: 'Select SR' + copyVmSelectSr: undefined, + + // Original text: 'Use compression' + copyVmCompress: undefined, + + // Original text: 'No target SR' + copyVmsNoTargetSr: undefined, + + // Original text: 'A target SR is required to copy a VM' + copyVmsNoTargetSrMessage: undefined, + + // Original text: 'Detach host' + detachHostModalTitle: undefined, + + // Original text: 'Are you sure you want to detach {host} from its pool? THIS WILL REMOVE ALL VMs ON ITS LOCAL STORAGE AND REBOOT THE HOST.' + detachHostModalMessage: undefined, + + // Original text: 'Detach' + detachHost: undefined, + + // Original text: 'Create network' + newNetworkCreate: undefined, + + // Original text: 'Create bonded network' + newBondedNetworkCreate: undefined, + + // Original text: 'Interface' + newNetworkInterface: undefined, + + // Original text: 'Name' + newNetworkName: undefined, + + // Original text: 'Description' + newNetworkDescription: undefined, + + // Original text: 'VLAN' + newNetworkVlan: undefined, + + // Original text: 'No VLAN if empty' + newNetworkDefaultVlan: undefined, + + // Original text: 'MTU' + newNetworkMtu: undefined, + + // Original text: 'Default: 1500' + newNetworkDefaultMtu: undefined, + + // Original text: 'Name required' + newNetworkNoNameErrorTitle: undefined, + + // Original text: 'A name is required to create a network' + newNetworkNoNameErrorMessage: undefined, + + // Original text: 'Bond mode' + newNetworkBondMode: undefined, + + // Original text: 'Delete network' + deleteNetwork: undefined, + + // Original text: 'Are you sure you want to delete this network?' + deleteNetworkConfirm: undefined, + + // Original text: 'This network is currently in use' + networkInUse: undefined, + + // Original text: 'Bonded' + pillBonded: undefined, + + // Original text: 'Host' + addHostSelectHost: undefined, + + // Original text: 'No host' + addHostNoHost: undefined, + + // Original text: 'No host selected to be added' + addHostNoHostMessage: undefined, + + // Original text: 'Xen Orchestra' + xenOrchestra: undefined, + + // Original text: 'server' + xenOrchestraServer: undefined, + + // Original text: 'web client' + xenOrchestraWeb: undefined, + + // Original text: 'No pro support provided!' + noProSupport: undefined, + + // Original text: 'Use in production at your own risks' + noProductionUse: undefined, + + // Original text: 'You can download our turnkey appliance at' + downloadXoa: undefined, + + // Original text: 'Bug Tracker' + bugTracker: undefined, + + // Original text: 'Issues? Report it!' + bugTrackerText: undefined, + + // Original text: 'Community' + community: undefined, + + // Original text: 'Join our community forum!' + communityText: undefined, + + // Original text: 'Free Trial for Premium Edition!' + freeTrial: undefined, + + // Original text: 'Request your trial now!' + freeTrialNow: undefined, + + // Original text: 'Any issue?' + issues: undefined, + + // Original text: 'Problem? Contact us!' + issuesText: undefined, + + // Original text: 'Documentation' + documentation: undefined, + + // Original text: 'Read our official doc' + documentationText: undefined, + + // Original text: 'Pro support included' + proSupportIncluded: undefined, + + // Original text: 'Acces your XO Account' + xoAccount: undefined, + + // Original text: 'Report a problem' + openTicket: undefined, + + // Original text: 'Problem? Open a ticket!' + openTicketText: undefined, + + // Original text: 'Upgrade needed' + upgradeNeeded: undefined, + + // Original text: 'Upgrade now!' + upgradeNow: undefined, + + // Original text: 'Or' + or: undefined, + + // Original text: 'Try it for free!' + tryIt: undefined, + + // Original text: 'This feature is available starting from {plan} Edition' + availableIn: undefined, + + // Original text: 'This feature is not available in your version, contact your administrator to know more.' + notAvailable: undefined, + + // Original text: 'Updates' + updateTitle: undefined, + + // Original text: 'Registration' + registration: undefined, + + // Original text: 'Trial' + trial: undefined, + + // Original text: 'Settings' + settings: undefined, + + // Original text: 'Proxy settings' + proxySettings: undefined, + + // Original text: 'Host (myproxy.example.org)' + proxySettingsHostPlaceHolder: undefined, + + // Original text: 'Port (eg: 3128)' + proxySettingsPortPlaceHolder: undefined, + + // Original text: 'Username' + proxySettingsUsernamePlaceHolder: undefined, + + // Original text: 'Password' + proxySettingsPasswordPlaceHolder: undefined, + + // Original text: 'Your email account' + updateRegistrationEmailPlaceHolder: undefined, + + // Original text: 'Your password' + updateRegistrationPasswordPlaceHolder: undefined, + + // Original text: 'Update' + update: undefined, + + // Original text: 'Refresh' + refresh: undefined, + + // Original text: 'Upgrade' + upgrade: undefined, + + // Original text: 'No updater available for Community Edition' + noUpdaterCommunity: undefined, + + // Original text: 'Please consider subscribe and try it with all features for free during 15 days on' + noUpdaterSubscribe: undefined, + + // Original text: 'Manual update could break your current installation due to dependencies issues, do it with caution' + noUpdaterWarning: undefined, + + // Original text: 'Current version:' + currentVersion: undefined, + + // Original text: 'Register' + register: undefined, + + // Original text: 'Edit registration' + editRegistration: undefined, + + // Original text: 'Please, take time to register in order to enjoy your trial.' + trialRegistration: undefined, + + // Original text: 'Start trial' + trialStartButton: undefined, + + // Original text: 'You can use a trial version until {date, date, medium}. Upgrade your appliance to get it.' + trialAvailableUntil: undefined, + + // Original text: 'Your trial has been ended. Contact us or downgrade to Free version' + trialConsumed: undefined, + + // Original text: 'Your xoa-updater service appears to be down. Your XOA cannot run fully without reaching this service.' + trialLocked: undefined, + + // Original text: 'No update information available' + noUpdateInfo: undefined, + + // Original text: 'Update information may be available' + waitingUpdateInfo: undefined, + + // Original text: 'Your XOA is up-to-date' + upToDate: undefined, + + // Original text: 'You need to update your XOA (new version is available)' + mustUpgrade: undefined, + + // Original text: 'Your XOA is not registered for updates' + registerNeeded: undefined, + + // Original text: "Can't fetch update information" + updaterError: undefined, + + // Original text: 'Upgrade successful' + promptUpgradeReloadTitle: undefined, + + // Original text: 'Your XOA has successfully upgraded, and your browser must reload the application. Do you want to reload now ?' + promptUpgradeReloadMessage: undefined, + + // Original text: 'Xen Orchestra from the sources' + disclaimerTitle: undefined, + + // Original text: "You are using XO from the sources! That's great for a personal/non-profit usage." + disclaimerText1: undefined, + + // Original text: "If you are a company, it's better to use it with our appliance + pro support included:" + disclaimerText2: undefined, + + // Original text: 'This version is not bundled with any support nor updates. Use it with caution for critical tasks.' + disclaimerText3: undefined, + + // Original text: 'Connect PIF' + connectPif: undefined, + + // Original text: 'Are you sure you want to connect this PIF?' + connectPifConfirm: undefined, + + // Original text: 'Disconnect PIF' + disconnectPif: undefined, + + // Original text: 'Are you sure you want to disconnect this PIF?' + disconnectPifConfirm: undefined, + + // Original text: 'Delete PIF' + deletePif: undefined, + + // Original text: 'Are you sure you want to delete this PIF?' + deletePifConfirm: undefined, + + // Original text: 'Username' + username: undefined, + + // Original text: 'Password' + password: undefined, + + // Original text: 'Language' + language: undefined, + + // Original text: 'Old password' + oldPasswordPlaceholder: undefined, + + // Original text: 'New password' + newPasswordPlaceholder: undefined, + + // Original text: 'Confirm new password' + confirmPasswordPlaceholder: undefined, + + // Original text: 'Confirmation password incorrect' + confirmationPasswordError: undefined, + + // Original text: 'Password does not match the confirm password.' + confirmationPasswordErrorBody: undefined, + + // Original text: 'Password changed' + pwdChangeSuccess: undefined, + + // Original text: 'Your password has been successfully changed.' + pwdChangeSuccessBody: undefined, + + // Original text: 'Incorrect password' + pwdChangeError: undefined, + + // Original text: 'The old password provided is incorrect. Your password has not been changed.' + pwdChangeErrorBody: undefined, + + // Original text: 'OK' + changePasswordOk: undefined, + + // Original text: 'SSH keys' + sshKeys: undefined, + + // Original text: 'New SSH key' + newSshKey: undefined, + + // Original text: 'Delete' + deleteSshKey: undefined, + + // Original text: 'No SSH keys' + noSshKeys: undefined, + + // Original text: 'New SSH key' + newSshKeyModalTitle: undefined, + + // Original text: 'Invalid key' + sshKeyErrorTitle: undefined, + + // Original text: 'An SSH key requires both a title and a key.' + sshKeyErrorMessage: undefined, + + // Original text: 'Title' + title: undefined, + + // Original text: 'Key' + key: undefined, + + // Original text: 'Delete SSH key' + deleteSshKeyConfirm: undefined, + + // Original text: 'Are you sure you want to delete the SSH key {title}?' + deleteSshKeyConfirmMessage: undefined, + + // Original text: 'Others' + others: undefined, + + // Original text: 'Loading logs…' + loadingLogs: undefined, + + // Original text: 'User' + logUser: undefined, + + // Original text: 'Method' + logMethod: undefined, + + // Original text: 'Params' + logParams: undefined, + + // Original text: 'Message' + logMessage: undefined, + + // Original text: 'Error' + logError: undefined, + + // Original text: 'Display details' + logDisplayDetails: undefined, + + // Original text: 'Date' + logTime: undefined, + + // Original text: 'No stack trace' + logNoStackTrace: undefined, + + // Original text: 'No params' + logNoParams: undefined, + + // Original text: 'Delete log' + logDelete: undefined, + + // Original text: 'Delete all logs' + logDeleteAll: undefined, + + // Original text: 'Delete all logs' + logDeleteAllTitle: undefined, + + // Original text: 'Are you sure you want to delete all the logs?' + logDeleteAllMessage: undefined, + + // Original text: 'Name' + ipPoolName: undefined, + + // Original text: 'IPs' + ipPoolIps: undefined, + + // Original text: 'IPs (e.g.: 1.0.0.12-1.0.0.17;1.0.0.23)' + ipPoolIpsPlaceholder: undefined, + + // Original text: 'Networks' + ipPoolNetworks: undefined, + + // Original text: 'No IP pools' + ipsNoIpPool: undefined, + + // Original text: 'Create' + ipsCreate: undefined, + + // Original text: 'Delete all IP pools' + ipsDeleteAllTitle: undefined, + + // Original text: 'Are you sure you want to delete all the IP pools?' + ipsDeleteAllMessage: undefined, + + // Original text: 'VIFs' + ipsVifs: undefined, + + // Original text: 'Not used' + ipsNotUsed: undefined, + + // Original text: 'Keyboard shortcuts' + shortcutModalTitle: undefined, + + // Original text: 'Global' + shortcut_XoApp: undefined, + + // Original text: 'Go to hosts list' + shortcut_GO_TO_HOSTS: undefined, + + // Original text: 'Go to pools list' + shortcut_GO_TO_POOLS: undefined, + + // Original text: 'Go to VMs list' + shortcut_GO_TO_VMS: undefined, + + // Original text: 'Create a new VM' + shortcut_CREATE_VM: undefined, + + // Original text: 'Unfocus field' + shortcut_UNFOCUS: undefined, + + // Original text: 'Show shortcuts key bindings' + shortcut_HELP: undefined, + + // Original text: 'Home' + shortcut_Home: undefined, + + // Original text: 'Focus search bar' + shortcut_SEARCH: undefined, + + // Original text: 'Next item' + shortcut_NAV_DOWN: undefined, + + // Original text: 'Previous item' + shortcut_NAV_UP: undefined, + + // Original text: 'Select item' + shortcut_SELECT: undefined, + + // Original text: 'Open' + shortcut_JUMP_INTO: undefined, + + // Original text: 'VM' + settingsAclsButtonTooltipVM: undefined, + + // Original text: 'Hosts' + settingsAclsButtonTooltiphost: undefined, + + // Original text: 'Pool' + settingsAclsButtonTooltippool: undefined, + + // Original text: 'SR' + settingsAclsButtonTooltipSR: undefined, + + // Original text: 'Network' + settingsAclsButtonTooltipnetwork: undefined, +} diff --git a/packages/xo-web/src/common/intl/locales/hu.js b/packages/xo-web/src/common/intl/locales/hu.js new file mode 100644 index 000000000..711b33aa5 --- /dev/null +++ b/packages/xo-web/src/common/intl/locales/hu.js @@ -0,0 +1,3658 @@ +// See http://momentjs.com/docs/#/use-it/browserify/ +import 'moment/locale/hu' + +import reactIntlData from 'react-intl/locale-data/hu' +import { addLocaleData } from 'react-intl' +addLocaleData(reactIntlData) + +// =================================================================== + +export default { + // Original text: "Connecting" + statusConnecting: 'Kapcsolódás', + + // Original text: "Disconnected" + statusDisconnected: 'Lekapcsolódva', + + // Original text: "Loading…" + statusLoading: 'Töltés…', + + // Original text: "Page not found" + errorPageNotFound: 'Oldal nem található', + + // Original text: "no such item" + errorNoSuchItem: 'nincs ilyen eszköz', + + // Original text: "Long click to edit" + editableLongClickPlaceholder: 'Kiszolgálószan kattints a szerkesztéshez', + + // Original text: "Click to edit" + editableClickPlaceholder: 'Kattints a szerkesztéshez', + + // Original text: "Browse files" + browseFiles: 'Fájlok böngészése', + + // Original text: "OK" + alertOk: 'OK', + + // Original text: "OK" + confirmOk: 'OK', + + // Original text: "Cancel" + confirmCancel: 'Mégsem', + + // Original text: "On error" + onError: 'Hiba', + + // Original text: "Successful" + successful: 'Sikeres', + + // Original text: "Copy to clipboard" + copyToClipboard: 'Másolás a vágólapra', + + // Original text: "Master" + pillMaster: 'Master', + + // Original text: "Home" + homePage: 'Kezdőlap', + + // Original text: "VMs" + homeVmPage: 'VPS', + + // Original text: "Hosts" + homeHostPage: 'Kiszolgáló', + + // Original text: "Pools" + homePoolPage: 'Pool', + + // Original text: "Templates" + homeTemplatePage: 'Sablon', + + // Original text: "Storages" + homeSrPage: 'Adattároló', + + // Original text: "Dashboard" + dashboardPage: 'Dashboard', + + // Original text: "Overview" + overviewDashboardPage: 'Áttekintés', + + // Original text: "Visualizations" + overviewVisualizationDashboardPage: 'Vizualizáció', + + // Original text: "Statistics" + overviewStatsDashboardPage: 'Statisztikák', + + // Original text: "Health" + overviewHealthDashboardPage: 'Állapot', + + // Original text: "Self service" + selfServicePage: 'Pirvát Datacenter', + + // Original text: "Backup" + backupPage: 'Mentés', + + // Original text: "Jobs" + jobsPage: 'Jobok', + + // Original text: "Updates" + updatePage: 'Frissítések', + + // Original text: "Settings" + settingsPage: 'Beállítások', + + // Original text: "Servers" + settingsServersPage: 'Szerverek', + + // Original text: "Users" + settingsUsersPage: 'Felhasználók', + + // Original text: "Groups" + settingsGroupsPage: 'Csoportok', + + // Original text: "ACLs" + settingsAclsPage: 'Jogok', + + // Original text: "Plugins" + settingsPluginsPage: 'Bővítményok', + + // Original text: "Logs" + settingsLogsPage: 'Logok', + + // Original text: "IPs" + settingsIpsPage: 'IP Címek', + + // Original text: "Config" + settingsConfigPage: 'Beállítás', + + // Original text: "About" + aboutPage: 'Információ', + + // Original text: "About XO {xoaPlan}" + aboutXoaPlan: 'Liszensz: {xoaPlan}', + + // Original text: "New" + newMenu: 'Új', + + // Original text: "Tasks" + taskMenu: 'Feladatok', + + // Original text: "Tasks" + taskPage: 'Feladatok', + + // Original text: "VM" + newVmPage: 'VPS', + + // Original text: "Storage" + newSrPage: 'Adattároló', + + // Original text: "Server" + newServerPage: 'Szerver', + + // Original text: "Import" + newImport: 'Importálás', + + // Original text: 'XOSAN' + xosan: undefined, + + // Original text: "Overview" + backupOverviewPage: 'Áttekintés', + + // Original text: "New" + backupNewPage: 'Új', + + // Original text: "Remotes" + backupRemotesPage: 'Távoli Mentés', + + // Original text: "Restore" + backupRestorePage: 'Visszaállítás', + + // Original text: "File restore" + backupFileRestorePage: 'Fájl alapú visszaállítás', + + // Original text: "Schedule" + schedule: 'Időzítés', + + // Original text: "New VM backup" + newVmBackup: 'Új VPS Mentés', + + // Original text: "Edit VM backup" + editVmBackup: 'VPS Mentés Szerkesztése', + + // Original text: "Backup" + backup: 'Adatmentés', + + // Original text: "Rolling Snapshot" + rollingSnapshot: 'Felülíródó Pillanatkép Mentés', + + // Original text: "Delta Backup" + deltaBackup: 'Delta Mentés', + + // Original text: "Disaster Recovery" + disasterRecovery: 'Disaster Recovery', + + // Original text: "Continuous Replication" + continuousReplication: 'Folyamatos Replikáció', + + // Original text: "Overview" + jobsOverviewPage: 'Áttekintés', + + // Original text: "New" + jobsNewPage: 'Új', + + // Original text: "Scheduling" + jobsSchedulingPage: 'Időzítés', + + // Original text: "Custom Job" + customJob: 'Egyedi Job', + + // Original text: "User" + userPage: 'Felhasználó', + + // Original text: "No support" + noSupport: 'Nincs szupport', + + // Original text: "Free upgrade!" + freeUpgrade: 'Ingyenes bővítés!', + + // Original text: "Sign out" + signOut: 'Kijelentkezés', + + // Original text: "Edit my settings {username}" + editUserProfile: 'Felhasználóm szerkesztése {felhasználónév}', + + // Original text: "Fetching data…" + homeFetchingData: 'Adatok betöltése…', + + // Original text: "Welcome on Xen Orchestra!" + homeWelcome: 'Üdvözöljük a Felhőben!', + + // Original text: "Add your XenServer hosts or pools" + homeWelcomeText: 'Hozzáadása your XenServer kiszolgálók or pools', + + // Original text: "Some XenServers have been registered but are not connected" + homeConnectServerText: + 'Some XenServers have been registered but are not Kapcsolódva', + + // Original text: "Want some help?" + homeHelp: 'Segítségre van szüksége?', + + // Original text: "Add server" + homeAddServer: 'Hozzáadása server', + + // Original text: "Connect servers" + homeConnectServer: 'Csatlakozás servers', + + // Original text: "Online Doc" + homeOnlineDoc: 'Online Doc', + + // Original text: "Pro Support" + homeProSupport: 'Pro Support', + + // Original text: "There are no VMs!" + homeNoVms: 'Nincsenek VPS-ek!', + + // Original text: "Or…" + homeNoVmsOr: 'Vagy…', + + // Original text: "Import VM" + homeImportVm: 'VPS Importálása', + + // Original text: "Import an existing VM in xva format" + homeImportVmMessage: 'Meglévő VPS importálása xva formátumban', + + // Original text: "Restore a backup" + homeRestoreBackup: 'Adatmentés visszaállítása', + + // Original text: "Restore a backup from a remote store" + homeRestoreBackupMessage: 'Adatmentés visszaállítása távoli helyről', + + // Original text: "This will create a new VM" + homeNewVmMessage: 'Új VPS készítése', + + // Original text: "Filters" + homeFilters: 'Szűrők', + + // Original text: "No results! Click here to reset your filters" + homeNoMatches: 'Nincs eredmény! Szűrők visszaállításához kattintson ide', + + // Original text: "Pool" + homeTypePool: 'Pool', + + // Original text: "Host" + homeTypeHost: 'Kiszolgáló', + + // Original text: "VM" + homeTypeVm: 'VPS', + + // Original text: "SR" + homeTypeSr: 'Adattároló', + + // Original text: "Template" + homeTypeVmTemplate: 'Sablon', + + // Original text: "Sort" + homeSort: 'Rendezés', + + // Original text: "Pools" + homeAllPools: 'Poolok', + + // Original text: "Hosts" + homeAllHosts: 'Kiszolgálók', + + // Original text: "Tags" + homeAllTags: 'Címke', + + // Original text: "New VM" + homeNewVm: 'Új VPS', + + // Original text: 'None' + homeFilterNone: undefined, + + // Original text: "Running hosts" + homeFilterRunningHosts: 'Futó kiszolgálók', + + // Original text: "Disabled hosts" + homeFilterDisabledHosts: 'Leállított kiszolgálók', + + // Original text: "Running VMs" + homeFilterRunningVms: 'Futó VPS szerverek', + + // Original text: "Non running VMs" + homeFilterNonRunningVms: 'Nem futó VPS szerverek', + + // Original text: "Pending VMs" + homeFilterPendingVms: 'Függőben lévő VPS szerverek', + + // Original text: "HVM guests" + homeFilterHvmGuests: 'HVM guests', + + // Original text: "Tags" + homeFilterTags: 'Címkék', + + // Original text: "Sort by" + homeSortBy: 'Rendezés', + + // Original text: "Name" + homeSortByName: 'Név', + + // Original text: "Power state" + homeSortByPowerstate: 'Állapot', + + // Original text: "RAM" + homeSortByRAM: 'RAM', + + // Original text: "vCPUs" + homeSortByvCPUs: 'vCPUs', + + // Original text: "CPUs" + homeSortByCpus: 'CPUs', + + // Original text: "Shared/Not shared" + homeSortByShared: 'Osztott/Nem osztott', + + // Original text: "Size" + homeSortBySize: 'Méret', + + // Original text: "Usage" + homeSortByUsage: 'Használat', + + // Original text: "Type" + homeSortByType: 'Típus', + + // Original text: "{displayed, number}x {icon} (on {total, number})" + homeDisplayedItems: '{displayed, number}x {icon} (on {total, number})', + + // Original text: "{selected, number}x {icon} selected (on {total, number})" + homeSelectedItems: + '{selected, number}x {icon} kiválasztott (on {total, number})', + + // Original text: "More" + homeMore: 'Több', + + // Original text: "Migrate to…" + homeMigrateTo: 'Migálás ide…', + + // Original text: "Missing patches" + homeMissingPaths: 'Missing patches', + + // Original text: "Master:" + homePoolMaster: 'Master:', + + // Original text: "Resource set: {resourceSet}" + homeResourceSet: 'Resource set: {resourceSet}', + + // Original text: "High Availability" + highAvailability: 'High Availability', + + // Original text: "Shared {type}" + srSharedType: 'Osztott {type}', + + // Original text: "Not shared {type}" + srNotSharedType: 'Nem osztott {type}', + + // Original text: "Add" + add: 'Hozzáadás', + + // Original text: "Select all" + selectAll: 'Mindet kijelöl', + + // Original text: "Remove" + remove: 'Eltávolítás', + + // Original text: "Preview" + preview: 'Előnézet', + + // Original text: "Item" + item: 'Eszköz', + + // Original text: "No selected value" + noSelectedValue: 'Nincs kiválasztott érték', + + // Original text: "Choose user(s) and/or group(s)" + selectSubjects: 'Válasszon felhasználó(ka)t és/vagy csoporto(ka)t', + + // Original text: "Select Object(s)…" + selectObjects: 'Objektum(ok) választása…', + + // Original text: "Choose a role" + selectRole: 'Szerep választása', + + // Original text: "Select Host(s)…" + selectHosts: 'Kiszolgáló(k) választása)…', + + // Original text: "Select object(s)…" + selectHostsVms: 'Objektum(ok) választása…', + + // Original text: "Select Network(s)…" + selectNetworks: 'Hálózat(ok) választása…', + + // Original text: "Select PIF(s)…" + selectPifs: 'PIF választása…', + + // Original text: "Select Pool(s)…" + selectPools: 'Pool(ok) választása…', + + // Original text: "Select Remote(s)…" + selectRemotes: 'Válasszon Remote(s)…', + + // Original text: "Select resource set(s)…" + selectResourceSets: 'Erőforrás készlet választása…', + + // Original text: "Select template(s)…" + selectResourceSetsVmTemplate: 'Sablon(ok) választása…', + + // Original text: "Select SR(s)…" + selectResourceSetsSr: 'Adattároló választása…', + + // Original text: "Select network(s)…" + selectResourceSetsNetwork: 'Hálózat(ok) választása…', + + // Original text: "Select disk(s)…" + selectResourceSetsVdi: 'Diszk(ek) választása…', + + // Original text: "Select SSH key(s)…" + selectSshKey: 'SSH kulcs(ok) választása…', + + // Original text: "Select SR(s)…" + selectSrs: 'Adattároló választása…', + + // Original text: "Select VM(s)…" + selectVms: 'VPS(ek) választása…', + + // Original text: "Select VM template(s)…" + selectVmTemplates: 'VPS sablon(ok)választása…', + + // Original text: "Select tag(s)…" + selectTags: 'Címkék választása…', + + // Original text: "Select disk(s)…" + selectVdis: 'Diszk(ek) választása…', + + // Original text: "Select timezone…" + selectTimezone: 'Időzóna választása…', + + // Original text: "Select IP(s)…" + selectIp: 'IP(k) választása…', + + // Original text: "Select IP pool(s)…" + selectIpPool: 'IP tartomány(ok) választása…', + + // Original text: "Fill required informations." + fillRequiredInformations: 'Szükséges információk kitöltése.', + + // Original text: "Fill informations (optional)" + fillOptionalInformations: 'Információk kitöltése (nem kötelező)', + + // Original text: "Reset" + selectTableReset: 'Visszaállítás', + + // Original text: "Month" + schedulingMonth: 'Hónap', + + // Original text: "Every N month" + schedulingEveryNMonth: 'Minden adott hónapban', + + // Original text: "Each selected month" + schedulingEachSelectedMonth: 'Minden kiválasztott hónapban', + + // Original text: "Day" + schedulingDay: 'Nap', + + // Original text: "Every N day" + schedulingEveryNDay: 'Megadott naponként', + + // Original text: "Each selected day" + schedulingEachSelectedDay: 'Minden kiválasztott napon', + + // Original text: "Switch to week days" + schedulingSetWeekDayMode: 'Váltás a hét napjaira', + + // Original text: "Switch to month days" + schedulingSetMonthDayMode: 'Váltás a hónap napjaira', + + // Original text: "Hour" + schedulingHour: 'Óra', + + // Original text: "Each selected hour" + schedulingEachSelectedHour: 'Minden kiválasztott órában', + + // Original text: "Every N hour" + schedulingEveryNHour: 'Minden X órában', + + // Original text: "Minute" + schedulingMinute: 'Perc', + + // Original text: "Each selected minute" + schedulingEachSelectedMinute: 'Minden kiválasztott percben', + + // Original text: "Every N minute" + schedulingEveryNMinute: 'Minden X-edik percben', + + // Original text: "Every month" + selectTableAllMonth: 'Minden hónapban', + + // Original text: "Every day" + selectTableAllDay: 'Minden napon', + + // Original text: "Every hour" + selectTableAllHour: 'Minden órában', + + // Original text: "Every minute" + selectTableAllMinute: 'Minden percben', + + // Original text: "Reset" + schedulingReset: 'Visszaállítás', + + // Original text: "Unknown" + unknownSchedule: 'Ismeretlen', + + // Original text: "Web browser timezone" + timezonePickerUseLocalTime: 'Böngésző időzóna', + + // Original text: "Server timezone ({value})" + serverTimezoneOption: 'Szerver időzóna ({Value})', + + // Original text: "Cron Pattern:" + cronPattern: 'Cron példa:', + + // Original text: "Cannot edit backup" + backupEditNotFoundTitle: 'Az adatmentés nem szerkeszthető', + + // Original text: "Missing required info for edition" + backupEditNotFoundMessage: 'Hiányzó szükséges információ', + + // Original text: "Job" + job: 'Feladat', + + // Original text: 'Job {job}' + jobModalTitle: undefined, + + // Original text: "ID" + jobId: 'Feladat Azonosító', + + // Original text: 'Type' + jobType: undefined, + + // Original text: "Name" + jobName: 'Név', + + // Original text: "Name of your job (forbidden: \"_\")" + jobNamePlaceholder: 'Feladat neve (forbidden: "_")', + + // Original text: "Start" + jobStart: 'Elindítás', + + // Original text: "End" + jobEnd: 'Befejezés', + + // Original text: "Duration" + jobDuration: 'Időtartam', + + // Original text: "Status" + jobStatus: 'Státusz', + + // Original text: "Action" + jobAction: 'Akció', + + // Original text: "Tag" + jobTag: 'Címke', + + // Original text: "Scheduling" + jobScheduling: 'Időzítés', + + // Original text: "State" + jobState: 'Állapot', + + // Original text: 'Enabled' + jobStateEnabled: undefined, + + // Original text: 'Disabled' + jobStateDisabled: undefined, + + // Original text: "Timezone" + jobTimezone: 'Időzóna', + + // Original text: "Server" + jobServerTimezone: 'Szerver', + + // Original text: "Run job" + runJob: 'Feladat futtatása', + + // Original text: "One shot running started. See overview for logs." + runJobVerbose: + 'Sikeresen elindítva. A logokat kérjük mindneképp nézze meg az eredményekhez.', + + // Original text: "Started" + jobStarted: 'Elindítva', + + // Original text: "Finished" + jobFinished: 'Befejezve', + + // Original text: "Save" + saveBackupJob: 'Mentés', + + // Original text: "Remove backup job" + deleteBackupSchedule: 'Mentési feladat eltávolítása', + + // Original text: "Are you sure you want to delete this backup job?" + deleteBackupScheduleQuestion: + 'Biztos benne, hogy törli ezt a mentési feladatot?', + + // Original text: "Enable immediately after creation" + scheduleEnableAfterCreation: 'Létrehozás utáni bekapcsolás engedélyezése', + + // Original text: "You are editing Schedule {name} ({id}). Saving will override previous schedule state." + scheduleEditMessage: + 'A következő Időzítést szerkeszti: {név} ({id}). A mentés felülírja az előző állapotot.', + + // Original text: "You are editing job {name} ({id}). Saving will override previous job state." + jobEditMessage: + 'A következő Feladatot szerkeszti: {név} ({id}). A mentés felülírja az előző állapotot.', + + // Original text: "No scheduled jobs." + noScheduledJobs: 'Nincsenek időzített feladatok.', + + // Original text: "No jobs found." + noJobs: 'Nincsenek feladatok.', + + // Original text: "No schedules found" + noSchedules: 'Nincsenek időzítések', + + // Original text: "Select a xo-server API command" + jobActionPlaceHolder: 'API parancs kiválasztása', + + // Original text: "Schedules" + jobSchedules: 'Időzítések', + + // Original text: "Name of your schedule" + jobScheduleNamePlaceHolder: 'Időzítés neve', + + // Original text: "Select a Job" + jobScheduleJobPlaceHolder: 'Feladat kiválasztása', + + // Original text: "Job owner" + jobOwnerPlaceholder: 'Feladat tulajdonosa', + + // Original text: "This job's creator no longer exists" + jobUserNotFound: 'A feladat létrehozója már nem érhető el a rendszerben', + + // Original text: "This backup's creator no longer exists" + backupUserNotFound: + 'A mentési feladat létrehozója már nem érhető el a rendszerben', + + // Original text: "Backup owner" + backupOwner: 'Mentés tulajdonosa', + + // Original text: "Select your backup type:" + newBackupSelection: 'Válassza ki a mentési típust:', + + // Original text: "Select backup mode:" + smartBackupModeSelection: 'Válassza ki a mentési módot:', + + // Original text: "Normal backup" + normalBackup: 'Normál mentés', + + // Original text: "Smart backup" + smartBackup: 'Okos mentés', + + // Original text: "Local remote selected" + localRemoteWarningTitle: 'Lokális távoli kiválasztva', + + // Original text: "Warning: local remotes will use limited XOA disk space. Only for advanced users." + localRemoteWarningMessage: + 'Figyelmeztetés: lokális távoli mentés korlátozott rendszer helyet használ. Kizárólag haladó felhasználóknak ajánlott, ha biztos benne, hogy ez a szervere elérhetőségét nem befolyásolja!.', + + // Original text: "Warning: this feature works only with XenServer 6.5 or newer." + backupVersionWarning: + 'Figyelmeztetés: 6.5 vagy újabb Xen támogatás szükséges!', + + // Original text: "VMs" + editBackupVmsTitle: 'VPS-ek', + + // Original text: "VMs statuses" + editBackupSmartStatusTitle: 'VPS sátuszok', + + // Original text: "Resident on" + editBackupSmartResidentOn: 'Helye', + + // Original text: "Pools" + editBackupSmartPools: 'Poolok', + + // Original text: "Tags" + editBackupSmartTags: 'Cimkék', + + // Original text: "VMs Tags" + editBackupSmartTagsTitle: 'VPS Cimkék', + + // Original text: "Reverse" + editBackupNot: 'Reverse', + + // Original text: "Tag" + editBackupTagTitle: 'Cimke', + + // Original text: "Report" + editBackupReportTitle: 'Riport', + + // Original text: "Enable immediately after creation" + editBackupScheduleEnabled: 'Azonnal a létrehozás után', + + // Original text: "Depth" + editBackupDepthTitle: 'Mélység', + + // Original text: "Remote" + editBackupRemoteTitle: 'Távoli', + + // Original text: "Remote stores for backup" + remoteList: 'Távoli Mentési Helyek', + + // Original text: "New File System Remote" + newRemote: 'Új Távoli Fájl Rendszer', + + // Original text: "Local" + remoteTypeLocal: 'Helyi', + + // Original text: "NFS" + remoteTypeNfs: 'NFS', + + // Original text: "SMB" + remoteTypeSmb: 'SMB', + + // Original text: "Type" + remoteType: 'Típus', + + // Original text: "Test your remote" + remoteTestTip: 'Tesztelés', + + // Original text: "Test Remote" + testRemote: 'Távoli Tesztelése', + + // Original text: "Test failed for {name}" + remoteTestFailure: 'Sikertelen tesztelés!!! {név}', + + // Original text: "Test passed for {name}" + remoteTestSuccess: 'Sikeres! {név}', + + // Original text: "Error" + remoteTestError: 'Hiba', + + // Original text: "Test Step" + remoteTestStep: 'Teszt Lépés', + + // Original text: "Test file" + remoteTestFile: 'Teszt Fájl', + + // Original text: 'Test name' + remoteTestName: undefined, + + // Original text: 'Remote name already exists!' + remoteTestNameFailure: undefined, + + // Original text: "The remote appears to work correctly" + remoteTestSuccessMessage: 'A távoli úgy tűnik megfelelően működik', + + // Original text: 'Connection failed' + remoteConnectionFailed: undefined, + + // Original text: "Name" + remoteName: 'Név', + + // Original text: "Path" + remotePath: 'Útvonal', + + // Original text: "State" + remoteState: 'Tátusz', + + // Original text: "Device" + remoteDevice: 'Eszköz', + + // Original text: "Share" + remoteShare: 'Megosztás', + + // Original text: 'Action' + remoteAction: undefined, + + // Original text: "Auth" + remoteAuth: 'Bejelentkezés', + + // Original text: "Mounted" + remoteMounted: 'Felcsatolva', + + // Original text: "Unmounted" + remoteUnmounted: 'Lecsatolva', + + // Original text: "Connect" + remoteConnectTip: 'Csatlakozás', + + // Original text: "Disconnect" + remoteDisconnectTip: 'Lecsatlakozás', + + // Original text: 'Connected' + remoteConnected: undefined, + + // Original text: 'Disconnected' + remoteDisconnected: undefined, + + // Original text: "Delete" + remoteDeleteTip: 'Törlés', + + // Original text: "remote name *" + remoteNamePlaceHolder: 'távoli név *', + + // Original text: "Name *" + remoteMyNamePlaceHolder: 'Név *', + + // Original text: "/path/to/backup" + remoteLocalPlaceHolderPath: '/path/to/backup', + + // Original text: "host *" + remoteNfsPlaceHolderHost: 'kiszolgáló *', + + // Original text: "path/to/backup" + remoteNfsPlaceHolderPath: 'path/to/backup', + + // Original text: "subfolder [path\\to\\backup]" + remoteSmbPlaceHolderRemotePath: 'almappa [path\\to\\backup]', + + // Original text: "Username" + remoteSmbPlaceHolderUsername: 'Felhasználónév', + + // Original text: "Password" + remoteSmbPlaceHolderPassword: 'Jelszó', + + // Original text: "Domain" + remoteSmbPlaceHolderDomain: 'Domain', + + // Original text: "
\\ *" + remoteSmbPlaceHolderAddressShare: '
\\ *', + + // Original text: "password(fill to edit)" + remotePlaceHolderPassword: 'jelszó(kattintson a módosításhoz)', + + // Original text: "Create a new SR" + newSrTitle: 'Új Adattároló készítése', + + // Original text: "General" + newSrGeneral: 'Általános', + + // Original text: "Select Storage Type:" + newSrTypeSelection: 'Adattároló Típus Kiválasztása:', + + // Original text: "Settings" + newSrSettings: 'Beállítások', + + // Original text: "Storage Usage" + newSrUsage: 'Adattároló Használat', + + // Original text: "Summary" + newSrSummary: 'Összesítés', + + // Original text: "Host" + newSrHost: 'Kiszolgáló', + + // Original text: "Type" + newSrType: 'Típus', + + // Original text: "Name" + newSrName: 'Név', + + // Original text: "Description" + newSrDescription: 'Leírás', + + // Original text: "Server" + newSrServer: 'Szerver', + + // Original text: "Path" + newSrPath: 'Útvonal', + + // Original text: "IQN" + newSrIqn: 'IQN', + + // Original text: "LUN" + newSrLun: 'LUN', + + // Original text: "with auth." + newSrAuth: 'with auth.', + + // Original text: "User Name" + newSrUsername: 'Felhasználónév', + + // Original text: "Password" + newSrPassword: 'Jelszó', + + // Original text: "Device" + newSrDevice: 'Eszköz', + + // Original text: "in use" + newSrInUse: 'használatban', + + // Original text: "Size" + newSrSize: 'Méret', + + // Original text: "Create" + newSrCreate: 'Létrehozás', + + // Original text: "Storage name" + newSrNamePlaceHolder: 'Adattároló név', + + // Original text: "Storage description" + newSrDescPlaceHolder: 'Adattároló leírása', + + // Original text: "Address" + newSrAddressPlaceHolder: 'Cím', + + // Original text: "[port]" + newSrPortPlaceHolder: '[port]', + + // Original text: "Username" + newSrUsernamePlaceHolder: 'Felhasználónév', + + // Original text: "Password" + newSrPasswordPlaceHolder: 'Jelszó', + + // Original text: "Device, e.g /dev/sda…" + newSrLvmDevicePlaceHolder: 'Eszköz, pl.: /dev/sda…', + + // Original text: "/path/to/directory" + newSrLocalPathPlaceHolder: '/path/to/directory', + + // Original text: "Users/Groups" + subjectName: 'Felhasználók/Csoportok', + + // Original text: "Object" + objectName: 'Objektum', + + // Original text: "No acls found" + aclNoneFound: 'Jogosultság nem található', + + // Original text: "Role" + roleName: 'Szerepkör', + + // Original text: "Create" + aclCreate: 'Létrehozás', + + // Original text: "New Group Name" + newGroupName: 'Új Csoport Név', + + // Original text: "Create Group" + createGroup: 'Csoport Létrehozás', + + // Original text: "Create" + createGroupButton: 'Létrehozás', + + // Original text: "Delete Group" + deleteGroup: 'Csoport Törlés', + + // Original text: "Are you sure you want to delete this group?" + deleteGroupConfirm: 'Biztos benne, hogy törli a csoportot?', + + // Original text: "Remove user from Group" + removeUserFromGroup: 'Felhasználó törlése a csoportból', + + // Original text: "Are you sure you want to delete this user?" + deleteUserConfirm: 'Biztos benne, hogy törli a felhasználót?', + + // Original text: "Delete User" + deleteUser: 'Felhasználó Törlése', + + // Original text: "no user" + noUser: 'no felhasználó', + + // Original text: "unknown user" + unknownUser: 'ismeretlen felhasználó', + + // Original text: "No group found" + noGroupFound: 'Csoport nem található', + + // Original text: "Name" + groupNameColumn: 'Név', + + // Original text: "Users" + groupUsersColumn: 'Felhasználók', + + // Original text: "Add User" + addUserToGroupColumn: 'Felhasználó Hozzáadása', + + // Original text: "Email" + userNameColumn: 'E-mail', + + // Original text: "Permissions" + userPermissionColumn: 'Jogosultságok', + + // Original text: "Password" + userPasswordColumn: 'Jelszó', + + // Original text: "Email" + userName: 'E-mail', + + // Original text: "Password" + userPassword: 'Jelszó', + + // Original text: "Create" + createUserButton: 'Létrehozás', + + // Original text: "No user found" + noUserFound: 'Felhasználó nem található', + + // Original text: "User" + userLabel: 'Felhasználó', + + // Original text: "Admin" + adminLabel: 'Admin', + + // Original text: "No user in group" + noUserInGroup: 'Nincs felhasználó a csoportban', + + // Original text: "{users} user{users, plural, one {} other {s}}" + countUsers: '{user} felhasználó{users, plural, one {} other {s}}', + + // Original text: "Select Permission" + selectPermission: 'Válasszon Jogosultságot', + + // Original text: "No plugins found" + noPlugins: 'Bővítményok nem találhatóak', + + // Original text: "Auto-load at server start" + autoloadPlugin: 'Automatikus betöltés szerver indításakor', + + // Original text: "Save configuration" + savePluginConfiguration: 'Beállítás Mentése', + + // Original text: "Delete configuration" + deletePluginConfiguration: 'Beállítás Törlése', + + // Original text: "Plugin error" + pluginError: 'Bővítmény hiba', + + // Original text: "Unknown error" + unknownPluginError: 'Ismeretlen hiba', + + // Original text: "Purge plugin configuration" + purgePluginConfiguration: 'Bővítmény beállítás törlése', + + // Original text: "Are you sure you want to purge this configuration ?" + purgePluginConfigurationQuestion: 'Biztos benne hogy törli ezt a beállítást?', + + // Original text: "Edit" + editPluginConfiguration: 'Szerkesztés', + + // Original text: "Cancel" + cancelPluginEdition: 'Mégsem', + + // Original text: "Plugin configuration" + pluginConfigurationSuccess: 'Bővítmény beállítás', + + // Original text: "Plugin configuration successfully saved!" + pluginConfigurationChanges: 'Bővítmény beállítás sikeresen mentve!', + + // Original text: "Predefined configuration" + pluginConfigurationPresetTitle: 'Előre definiált konfiguráció', + + // Original text: "Choose a predefined configuration." + pluginConfigurationChoosePreset: 'Előre definiált konfiguráció választása.', + + // Original text: "Apply" + applyPluginPreset: 'Mehet', + + // Original text: "Save filter error" + saveNewUserFilterErrorTitle: 'Szűrő mentés hiba', + + // Original text: "Bad parameter: name must be given." + saveNewUserFilterErrorBody: 'Rossz paraméter: név megadása kötelező.', + + // Original text: "Name:" + filterName: 'Név:', + + // Original text: "Value:" + filterValue: 'Érték:', + + // Original text: "Save new filter" + saveNewFilterTitle: 'Új szűrő mentése', + + // Original text: "Set custom filters" + setUserFiltersTitle: 'Egyedi szűrők beállítása', + + // Original text: "Are you sure you want to set custom filters?" + setUserFiltersBody: 'Biztos benne, hogy beállítja az egyedi szűrőket?', + + // Original text: "Remove custom filter" + removeUserFilterTitle: 'Egyedi szűrő eltávolítása', + + // Original text: "Are you sure you want to remove custom filter?" + removeUserFilterBody: 'Biztos benne, hogy eltávolítja az egyedi szűrőt?', + + // Original text: "Default filter" + defaultFilter: 'Alapértelmezett szűrő', + + // Original text: "Default filters" + defaultFilters: 'Alapértelmezett szűrők', + + // Original text: "Custom filters" + customFilters: 'Egyedi szűrők', + + // Original text: "Customize filters" + customizeFilters: 'Szűrők testre szabása', + + // Original text: "Save custom filters" + saveCustomFilters: 'Egyedi szűrők mentése', + + // Original text: "Start" + startVmLabel: 'Elindít', + + // Original text: "Recovery start" + recoveryModeLabel: 'Helyreállítás Elindítása', + + // Original text: "Suspend" + suspendVmLabel: 'Felfüggesztés', + + // Original text: "Stop" + stopVmLabel: 'Leállítás', + + // Original text: "Force shutdown" + forceShutdownVmLabel: 'Leállítás Kényszerítése', + + // Original text: "Reboot" + rebootVmLabel: 'Újraindítás', + + // Original text: "Force reboot" + forceRebootVmLabel: 'Újraindítás Kényszerítése', + + // Original text: "Delete" + deleteVmLabel: 'Törlés', + + // Original text: "Migrate" + migrateVmLabel: 'Migrálás', + + // Original text: "Snapshot" + snapshotVmLabel: 'Pillanatkép', + + // Original text: "Export" + exportVmLabel: 'Exportálás', + + // Original text: "Resume" + resumeVmLabel: 'Folytatás', + + // Original text: "Copy" + copyVmLabel: 'Másolás', + + // Original text: "Clone" + cloneVmLabel: 'Klónozás', + + // Original text: "Fast clone" + fastCloneVmLabel: 'Gyors Klónozás', + + // Original text: "Convert to template" + convertVmToTemplateLabel: 'Sablonná konvertálás', + + // Original text: "Console" + vmConsoleLabel: 'Konzol', + + // Original text: "Rescan all disks" + srRescan: 'Összes diszk újraszkennelése', + + // Original text: "Connect to all hosts" + srReconnectAll: 'Csatlakoztatás az összes kiszolgálóhoz', + + // Original text: "Disconnect from all hosts" + srDisconnectAll: 'Lecsatlakoztatás az összes kiszolgálóról', + + // Original text: "Forget this SR" + srForget: 'Adattároló Elfelejtése', + + // Original text: "Forget SRs" + srsForget: 'Adattároló Elfelejtése', + + // Original text: "Remove this SR" + srRemoveButton: 'Adattároló Eltávolítás', + + // Original text: "No VDIs in this storage" + srNoVdis: 'No VDIs in this storage', + + // Original text: "Pool RAM usage:" + poolTitleRamUsage: 'Pool RAM használat:', + + // Original text: "{used} used on {total}" + poolRamUsage: '{used} used on {total}', + + // Original text: "Master:" + poolMaster: 'Master:', + + // Original text: "Display all hosts of this pool" + displayAllHosts: 'Display all kiszolgálók of this pool', + + // Original text: "Display all storages of this pool" + displayAllStorages: 'Display all storages of this pool', + + // Original text: "Display all VMs of this pool" + displayAllVMs: 'Display all VMs of this pool', + + // Original text: "Hosts" + hostsTabName: 'Kiszolgálók', + + // Original text: 'Vms' + vmsTabName: undefined, + + // Original text: 'Srs' + srsTabName: undefined, + + // Original text: "High Availability" + poolHaStatus: 'High Availability', + + // Original text: "Enabled" + poolHaEnabled: 'Bekapcsolva', + + // Original text: "Disabled" + poolHaDisabled: 'Kikapcsolva', + + // Original text: "Name" + hostNameLabel: 'Név', + + // Original text: "Description" + hostDescription: 'Leírás', + + // Original text: "Memory" + hostMemory: 'Memória', + + // Original text: "No hosts" + noHost: 'Nincsenek kiszolgálók', + + // Original text: "{used}% used ({free} free)" + memoryLeftTooltip: '{used}% used ({free} free)', + + // Original text: "PIF" + pif: 'PIF', + + // Original text: "Name" + poolNetworkNameLabel: 'Név', + + // Original text: "Description" + poolNetworkDescription: 'Leírás', + + // Original text: "PIFs" + poolNetworkPif: 'PIFs', + + // Original text: "No networks" + poolNoNetwork: 'Nincsenek hálózatok', + + // Original text: "MTU" + poolNetworkMTU: 'MTU', + + // Original text: "Connected" + poolNetworkPifAttached: 'Kapcsolódva', + + // Original text: "Disconnected" + poolNetworkPifDetached: 'Lekapcsolódva', + + // Original text: "Show PIFs" + showPifs: 'Show PIFs', + + // Original text: "Hide PIFs" + hidePifs: 'Hide PIFs', + + // Original text: "Show details" + showDetails: 'Részletek mutatása', + + // Original text: "Hide details" + hideDetails: 'Részletek elrejtése', + + // Original text: "No stats" + poolNoStats: 'Nincsenek statisztikák', + + // Original text: "All hosts" + poolAllHosts: 'Minden kiszolgáló', + + // Original text: "Add SR" + addSrLabel: 'Adattároló Hozzáadása', + + // Original text: "Add VM" + addVmLabel: 'VPS Hozzáadása', + + // Original text: "Add Host" + addHostLabel: 'Kiszolgáló Hozzáadása', + + // Original text: "Disconnect" + disconnectServer: 'Lecsatol', + + // Original text: "Start" + startHostLabel: 'Elindítás', + + // Original text: "Stop" + stopHostLabel: 'Leállítás', + + // Original text: "Enable" + enableHostLabel: 'Bekapcsol', + + // Original text: "Disable" + disableHostLabel: 'Kikapcsol', + + // Original text: "Restart toolstack" + restartHostAgent: 'Toolstack újraindítása', + + // Original text: "Force reboot" + forceRebootHostLabel: 'Újraindítás Kényszerítése', + + // Original text: "Reboot" + rebootHostLabel: 'Újraindítás', + + // Original text: "Error while restarting host" + noHostsAvailableErrorTitle: 'Hiba a kiszolgáló újraindítása közben', + + // Original text: "Some VMs cannot be migrated before restarting this host. Please try force reboot." + noHostsAvailableErrorMessage: + 'Some VMs cannot be migrated before restarting this Host. Please try force Restart.', + + // Original text: "Error while restarting hosts" + failHostBulkRestartTitle: 'Hiba lépett fel a kiszolgálók újraindítása közben', + + // Original text: "{failedHosts}/{totalHosts} host{failedHosts, plural, one {} other {s}} could not be restarted." + failHostBulkRestartMessage: + '{failedhosts}/{totalHosts} Kiszolgáló újraindítása nem sikerült.', + + // Original text: "Reboot to apply updates" + rebootUpdateHostLabel: + 'A változtatások életbe lépéséhez újraindítás szükséges', + + // Original text: "Emergency mode" + emergencyModeLabel: 'Vészhelyzet üzem', + + // Original text: "Storage" + storageTabName: 'Adattároló', + + // Original text: "Patches" + patchesTabName: 'Javítások', + + // Original text: "Load average" + statLoad: 'Átlagos load', + + // Original text: "RAM Usage: {memoryUsed}" + memoryHostState: 'Memória használat: {MemoryUsed}', + + // Original text: "Hardware" + hardwareHostSettingsLabel: 'Hardver', + + // Original text: "Address" + hostAddress: 'Cím', + + // Original text: "Status" + hostStatus: 'Állapot', + + // Original text: "Build number" + hostBuildNumber: 'Build number', + + // Original text: "iSCSI name" + hostIscsiName: 'iSCSI név', + + // Original text: "Version" + hostXenServerVersion: 'Verzió', + + // Original text: "Enabled" + hostStatusEnabled: 'Bekapcsolva', + + // Original text: "Disabled" + hostStatusDisabled: 'Kikapcsolva', + + // Original text: "Power on mode" + hostPowerOnMode: 'Power on mode', + + // Original text: "Host uptime" + hostStartedSince: 'Kiszolgáló uptime', + + // Original text: "Toolstack uptime" + hostStackStartedSince: 'Toolstack uptime', + + // Original text: "CPU model" + hostCpusModel: 'CPU model', + + // Original text: "Core (socket)" + hostCpusNumber: 'Core (socket)', + + // Original text: "Manufacturer info" + hostManufacturerinfo: 'Gyártó infó', + + // Original text: "BIOS info" + hostBiosinfo: 'BIOS infó', + + // Original text: "Licence" + licenseHostSettingsLabel: 'Liszensz', + + // Original text: "Type" + hostLicenseType: 'Típus', + + // Original text: "Socket" + hostLicenseSocket: 'Foglalat', + + // Original text: "Expiry" + hostLicenseExpiry: 'Lejárat', + + // Original text: "Installed supplemental packs" + supplementalPacks: 'Installed supplemental packs', + + // Original text: "Install new supplemental pack" + supplementalPackNew: 'Install new supplemental pack', + + // Original text: "Install supplemental pack on every host" + supplementalPackPoolNew: 'Install supplemental pack on every host', + + // Original text: "{name} (by {author})" + supplementalPackTitle: '{név} (by {author})', + + // Original text: "Installation started" + supplementalPackInstallStartedTitle: 'Installation Started', + + // Original text: "Installing new supplemental pack…" + supplementalPackInstallStartedMessage: 'Installing new supplemental pack…', + + // Original text: "Installation error" + supplementalPackInstallErrorTitle: 'Installation error', + + // Original text: "The installation of the supplemental pack failed." + supplementalPackInstallErrorMessage: + 'The installation of the supplemental pack failed.', + + // Original text: "Installation success" + supplementalPackInstallSuccessTitle: 'Installation success', + + // Original text: "Supplemental pack successfully installed." + supplementalPackInstallSuccessMessage: + 'Supplemental pack successfully installed.', + + // Original text: "Add a network" + networkCreateButton: 'Add a Hálózat', + + // Original text: "Add a bonded network" + networkCreateBondedButton: 'Add a bonded Hálózat', + + // Original text: "Device" + pifDeviceLabel: 'Eszköz', + + // Original text: "Network" + pifNetworkLabel: 'Hálózat', + + // Original text: "VLAN" + pifVlanLabel: 'VLAN', + + // Original text: "Address" + pifAddressLabel: 'Cím', + + // Original text: "Mode" + pifModeLabel: 'Mode', + + // Original text: "MAC" + pifMacLabel: 'MAC', + + // Original text: "MTU" + pifMtuLabel: 'MTU', + + // Original text: "Status" + pifStatusLabel: 'Állapot', + + // Original text: "Connected" + pifStatusConnected: 'Kapcsolódva', + + // Original text: "Disconnected" + pifStatusDisconnected: 'Lekapcsolódva', + + // Original text: "No physical interface detected" + pifNoInterface: 'No physical interface detected', + + // Original text: "This interface is currently in use" + pifInUse: 'This interface is currently in use', + + // Original text: "Default locking mode" + defaultLockingMode: 'Alapértelmezett locking üzem', + + // Original text: "Configure IP address" + pifConfigureIp: 'Configure IP cím', + + // Original text: "Invalid parameters" + configIpErrorTitle: 'Invalid parameters', + + // Original text: "IP address and netmask required" + configIpErrorMessage: 'IP cím and netmask required', + + // Original text: "Static IP address" + staticIp: 'Static IP cím', + + // Original text: "Netmask" + netmask: 'Netmask', + + // Original text: "DNS" + dns: 'DNS', + + // Original text: "Gateway" + gateway: 'Gateway', + + // Original text: "Add a storage" + addSrDeviceButton: 'Add a storage', + + // Original text: "Name" + srNameLabel: 'Név', + + // Original text: "Type" + srType: 'Típus', + + // Original text: 'Action' + pbdAction: undefined, + + // Original text: "Status" + pbdStatus: 'Állapot', + + // Original text: "Connected" + pbdStatusConnected: 'Kapcsolódva', + + // Original text: "Disconnected" + pbdStatusDisconnected: 'Lekapcsolódva', + + // Original text: "Connect" + pbdConnect: 'Csatlakozás', + + // Original text: "Disconnect" + pbdDisconnect: 'Lecsatlakozás', + + // Original text: "Forget" + pbdForget: 'Elfelejt', + + // Original text: "Shared" + srShared: 'Megosztva', + + // Original text: "Not shared" + srNotShared: 'Nincs megosztva', + + // Original text: "No storage detected" + pbdNoSr: 'No storage detected', + + // Original text: "Name" + patchNameLabel: 'Név', + + // Original text: "Install all patches" + patchUpdateButton: 'Install all patches', + + // Original text: "Description" + patchDescription: 'Leírás', + + // Original text: "Applied date" + patchApplied: 'Applied Dátum', + + // Original text: "Size" + patchSize: 'Méret', + + // Original text: "Status" + patchStatus: 'Állapot', + + // Original text: "Applied" + patchStatusApplied: 'Applied', + + // Original text: "Missing patches" + patchStatusNotApplied: 'Missing patches', + + // Original text: "No patch detected" + patchNothing: 'No patch detected', + + // Original text: "Release date" + patchReleaseDate: 'Release Dátum', + + // Original text: "Guidance" + patchGuidance: 'Guidance', + + // Original text: "Action" + patchAction: 'Művelet', + + // Original text: "Applied patches" + hostAppliedPatches: 'Applied patches', + + // Original text: "Missing patches" + hostMissingPatches: 'Missing patches', + + // Original text: "Host up-to-date!" + hostUpToDate: 'Host up-to-Dátum!', + + // Original text: "Refresh patches" + refreshPatches: 'Refresh patches', + + // Original text: "Install pool patches" + installPoolPatches: 'Install pool patches', + + // Original text: "Default SR" + defaultSr: 'Alapértelmezett Adattároló', + + // Original text: "Set as default SR" + setAsDefaultSr: 'Beállítás Alapértelmezett Adattárolóként', + + // Original text: "General" + generalTabName: 'Általános', + + // Original text: "Stats" + statsTabName: 'Statisztikák', + + // Original text: "Console" + consoleTabName: 'Konzol', + + // Original text: "Container" + containersTabName: 'Container', + + // Original text: "Snapshots" + snapshotsTabName: 'Pillanatképek', + + // Original text: "Logs" + logsTabName: 'Logok', + + // Original text: "Advanced" + advancedTabName: 'Haladó', + + // Original text: "Network" + networkTabName: 'Hálózat', + + // Original text: "Disk{disks, plural, one {} other {s}}" + disksTabName: 'Lemez{disks, plural, one {} other {s}}', + + // Original text: "halted" + powerStateHalted: 'leállítva', + + // Original text: "running" + powerStateRunning: 'fut', + + // Original text: "suspended" + powerStateSuspended: 'Felfüggesztett', + + // Original text: "No Xen tools detected" + vmStatus: 'No Xen tools detected', + + // Original text: "No IPv4 record" + vmName: 'No IPv4 record', + + // Original text: "No IP record" + vmDescription: 'No IP record', + + // Original text: "Started {ago}" + vmSettings: 'Elindítva {ago}', + + // Original text: "Current status:" + vmCurrentStatus: 'Jelenlegi állapot:', + + // Original text: "Not running" + vmNotRunning: 'Nem fut', + + // Original text: "No Xen tools detected" + noToolsDetected: 'No Xen tools detected', + + // Original text: "No IPv4 record" + noIpv4Record: 'No IPv4 record', + + // Original text: "No IP record" + noIpRecord: 'No IP record', + + // Original text: "Started {ago}" + started: 'Elindítva {ago}', + + // Original text: "Paravirtualization (PV)" + paraVirtualizedMode: 'Paravirtualization (PV)', + + // Original text: "Hardware virtualization (HVM)" + hardwareVirtualizedMode: 'Hardware virtualization (HVM)', + + // Original text: "CPU usage" + statsCpu: 'CPU használat', + + // Original text: "Memory usage" + statsMemory: 'Memória használat', + + // Original text: "Network throughput" + statsNetwork: 'Átmenő forgalom', + + // Original text: "Stacked values" + useStackedValuesOnStats: 'Halmozott Értékek', + + // Original text: "Disk throughput" + statDisk: 'Átmenő Diszk Érték', + + // Original text: "Last 10 minutes" + statLastTenMinutes: 'Utóbbi 10 perc', + + // Original text: "Last 2 hours" + statLastTwoHours: 'Utóbbi 2 óra', + + // Original text: "Last week" + statLastWeek: 'Utóbbi 1 hét', + + // Original text: "Last year" + statLastYear: 'Utóbbi 1 év', + + // Original text: "Copy" + copyToClipboardLabel: 'Másolás', + + // Original text: "Ctrl+Alt+Del" + ctrlAltDelButtonLabel: 'Ctrl+Alt+Del', + + // Original text: "Tip:" + tipLabel: 'Tip:', + + // Original text: "Due to a XenServer issue, non-US keyboard layouts aren't well supported. Switch your own layout to US to workaround it." + tipConsoleLabel: + 'Rendszerkompatibilitás miatt egyedül amerikai (US) billentyűzetkiosztás működik a legstabilabban, ennek használata javasolt.', + + // Original text: "Hide infos" + hideHeaderTooltip: 'Információk elrejtése', + + // Original text: "Show infos" + showHeaderTooltip: 'Információk mutatása', + + // Original text: "Name" + containerName: 'Név', + + // Original text: "Command" + containerCommand: 'Parancs', + + // Original text: "Creation date" + containerCreated: 'Létrehozás dátuma', + + // Original text: "Status" + containerStatus: 'Állapot', + + // Original text: "Action" + containerAction: 'Akció', + + // Original text: "No existing containers" + noContainers: 'Jelenleg nincsenek Konténerek', + + // Original text: "Stop this container" + containerStop: 'Konténer Leállítása', + + // Original text: "Start this container" + containerStart: 'Konténer Elindítása', + + // Original text: "Pause this container" + containerPause: 'Konténer Szüneteltetése', + + // Original text: "Resume this container" + containerResume: 'Konténer Folytatása', + + // Original text: "Restart this container" + containerRestart: 'Konténer Újraindítása', + + // Original text: "Action" + vdiAction: 'Művelet', + + // Original text: "Attach disk" + vdiAttachDeviceButton: 'Diszk Hozzácsatolás', + + // Original text: "New disk" + vbdCreateDeviceButton: 'Új diszk', + + // Original text: "Boot order" + vdiBootOrder: 'Boot sorrend', + + // Original text: "Name" + vdiNameLabel: 'Név', + + // Original text: "Description" + vdiNameDescription: 'Leírás', + + // Original text: "Tags" + vdiTags: 'Cimkék', + + // Original text: "Size" + vdiSize: 'Méret', + + // Original text: "SR" + vdiSr: 'Adattároló', + + // Original text: "VM" + vdiVm: 'VPS', + + // Original text: "Migrate VDI" + vdiMigrate: 'Migrate VDI', + + // Original text: "Destination SR:" + vdiMigrateSelectSr: 'Cél Adattároló:', + + // Original text: "Migrate all VDIs" + vdiMigrateAll: 'Migrate all VDIs', + + // Original text: "No SR" + vdiMigrateNoSr: 'Nincs Adattároló', + + // Original text: "A target SR is required to migrate a VDI" + vdiMigrateNoSrMessage: 'Cél Adattároló szükséges a VDI migáláshoz', + + // Original text: "Forget" + vdiForget: 'Elfelejt', + + // Original text: "Remove VDI" + vdiRemove: 'VDI Eltávolítás', + + // Original text: "Boot flag" + vbdBootableStatus: 'Boot flag', + + // Original text: "Status" + vbdStatus: 'Állapot', + + // Original text: "Connected" + vbdStatusConnected: 'Kapcsolódva', + + // Original text: "Disconnected" + vbdStatusDisconnected: 'Lekapcsolódva', + + // Original text: "No disks" + vbdNoVbd: 'Nincsenek Diszkek', + + // Original text: "Connect VBD" + vbdConnect: 'VBD Csatlakozás', + + // Original text: "Disconnect VBD" + vbdDisconnect: 'VBD Lecsatlakozás', + + // Original text: "Bootable" + vbdBootable: 'Bootolható', + + // Original text: "Readonly" + vbdReadonly: 'Csak olvasható', + + // Original text: 'Action' + vbdAction: undefined, + + // Original text: "Create" + vbdCreate: 'Létrehozás', + + // Original text: "Disk name" + vbdNamePlaceHolder: 'Diszk név', + + // Original text: "Size" + vbdSizePlaceHolder: 'Méret', + + // Original text: "Save" + saveBootOption: 'Mentés', + + // Original text: "Reset" + resetBootOption: 'Visszaállítás', + + // Original text: "New device" + vifCreateDeviceButton: 'Új eszköz', + + // Original text: "No interface" + vifNoInterface: 'Nincs interface', + + // Original text: "Device" + vifDeviceLabel: 'Eszköz', + + // Original text: "MAC address" + vifMacLabel: 'MAC cím', + + // Original text: "MTU" + vifMtuLabel: 'MTU', + + // Original text: "Network" + vifNetworkLabel: 'Hálózat', + + // Original text: "Status" + vifStatusLabel: 'Állapot', + + // Original text: "Connected" + vifStatusConnected: 'Kapcsolódva', + + // Original text: "Disconnected" + vifStatusDisconnected: 'Lekapcsolódva', + + // Original text: "Connect" + vifConnect: 'Csatlakozás', + + // Original text: "Disconnect" + vifDisconnect: 'Lecsatlakozás', + + // Original text: "Remove" + vifRemove: 'Eltávolítás', + + // Original text: "IP addresses" + vifIpAddresses: 'IP címek', + + // Original text: "Auto-generated if empty" + vifMacAutoGenerate: 'Auto-generálás ha üres', + + // Original text: "Allowed IPs" + vifAllowedIps: 'Engedélyezett IP címek', + + // Original text: "No IPs" + vifNoIps: 'Nincsenek IP címek', + + // Original text: "Network locked" + vifLockedNetwork: 'Hálózat zárolva', + + // Original text: "Network locked and no IPs are allowed for this interface" + vifLockedNetworkNoIps: + 'Hálózat zárolva és nincsenek engedélyezve IP címek ehhez az interfészhez', + + // Original text: "Network not locked" + vifUnLockedNetwork: 'Hálózat nincs zárolva', + + // Original text: "Unknown network" + vifUnknownNetwork: 'Ismeretlen Hálózat', + + // Original text: 'Action' + vifAction: undefined, + + // Original text: "Create" + vifCreate: 'Létrehozás', + + // Original text: "No snapshots" + noSnapshots: 'Nincsenek Pillanatképek', + + // Original text: "New snapshot" + snapshotCreateButton: 'Új Pillanatkép', + + // Original text: "Just click on the snapshot button to create one!" + tipCreateSnapshotLabel: + 'Csak kattintson a Pillanatkép gombra új pillanatkép készítéséhez!', + + // Original text: "Revert VM to this snapshot" + revertSnapshot: 'VPS visszaállítása erre a pillanatképre', + + // Original text: "Remove this snapshot" + deleteSnapshot: 'Pillanatkép eltávolítása', + + // Original text: "Create a VM from this snapshot" + copySnapshot: 'VPS létrehozása ebből a pillanatképből', + + // Original text: "Export this snapshot" + exportSnapshot: 'Pillanatkép exportálása', + + // Original text: "Creation date" + snapshotDate: 'Létrehozás dátuma', + + // Original text: "Name" + snapshotName: 'Név', + + // Original text: "Action" + snapshotAction: 'Művelet', + + // Original text: "Quiesced snapshot" + snapshotQuiesce: 'Nyugalomban lévő Pillanatkép', + + // Original text: "Remove all logs" + logRemoveAll: 'Logok Eltávolítása', + + // Original text: "No logs so far" + noLogs: 'Nincsenek logok ez idáig', + + // Original text: "Creation date" + logDate: 'Létrehozás dátuma', + + // Original text: "Name" + logName: 'Név', + + // Original text: "Content" + logContent: 'Tartalom', + + // Original text: "Action" + logAction: 'Művelet', + + // Original text: "Remove" + vmRemoveButton: 'Eltávolítás', + + // Original text: "Convert" + vmConvertButton: 'Konvertálás', + + // Original text: "Xen settings" + xenSettingsLabel: 'Xen beállítások', + + // Original text: "Guest OS" + guestOsLabel: 'Vendég OS', + + // Original text: "Misc" + miscLabel: 'Egyéb', + + // Original text: "UUID" + uuid: 'UUID', + + // Original text: "Virtualization mode" + virtualizationMode: 'Virtualizációs üzem', + + // Original text: "CPU weight" + cpuWeightLabel: 'CPU súly', + + // Original text: "Default ({value, number})" + defaultCpuWeight: 'Alapértelmezett ({Value, number})', + + // Original text: "CPU cap" + cpuCapLabel: 'CPU cap', + + // Original text: "Default ({value, number})" + defaultCpuCap: 'Alapértelmezett ({Value, number})', + + // Original text: "PV args" + pvArgsLabel: 'PV args', + + // Original text: "Xen tools status" + xenToolsStatus: 'Xen tools Állapot', + + // Original text: "{status}" + xenToolsStatusValue: '{Állapot}', + + // Original text: "OS name" + osName: 'OS név', + + // Original text: "OS kernel" + osKernel: 'OS kernel', + + // Original text: "Auto power on" + autoPowerOn: 'Auto bekapcsolás', + + // Original text: "HA" + ha: 'HA', + + // Original text: "Original template" + originalTemplate: 'Eredeti sablon', + + // Original text: "Unknown" + unknownOsName: 'Ismeretlen', + + // Original text: "Unknown" + unknownOsKernel: 'Ismeretlen', + + // Original text: "Unknown" + unknownOriginalTemplate: 'Ismeretlen', + + // Original text: "VM limits" + vmLimitsLabel: 'VPS limitek', + + // Original text: "CPU limits" + vmCpuLimitsLabel: 'CPU limitek', + + // Original text: "Memory limits (min/max)" + vmMemoryLimitsLabel: 'Memória limitek (min/max)', + + // Original text: "vCPUs max:" + vmMaxVcpus: 'vCPUs max:', + + // Original text: "Memory max:" + vmMaxRam: 'Memória max:', + + // Original text: "Long click to add a name" + vmHomeNamePlaceholder: 'Kattintson hosszan név hozzáadásához', + + // Original text: "Long click to add a description" + vmHomeDescriptionPlaceholder: 'Kattintson hosszan leírás hozzáadásához', + + // Original text: "Click to add a name" + vmViewNamePlaceholder: 'Kattintson név hozzáadásához', + + // Original text: "Click to add a description" + vmViewDescriptionPlaceholder: 'Kattintson leírás hozzáadásához', + + // Original text: "Click to add a name" + templateHomeNamePlaceholder: 'Kattintson név hozzáadásához', + + // Original text: "Click to add a description" + templateHomeDescriptionPlaceholder: 'Kattintson leírás hozzáadásához', + + // Original text: "Delete template" + templateDelete: 'Sablon törlése', + + // Original text: "Delete VM template{templates, plural, one {} other {s}}" + templateDeleteModalTitle: + 'VPS sablon{Templates, plural, one {} other {ok}} törlése', + + // Original text: "Are you sure you want to delete {templates, plural, one {this} other {these}} template{templates, plural, one {} other {s}}?" + templateDeleteModalBody: + 'Biztos benne, hogy törölni kívánja a kiválasztott {templates, plural, one {this} other {these}} sablon{Templates, plural, one {} other {oka}}t?', + + // Original text: "Pool{pools, plural, one {} other {s}}" + poolPanel: 'Pool{pools, plural, one {} other {ok}}', + + // Original text: "Host{hosts, plural, one {} other {s}}" + hostPanel: 'Kiszolgáló{kiszolgálók, plural, one {} other {k}}', + + // Original text: "VM{vms, plural, one {} other {s}}" + vmPanel: 'VPS{vms, plural, one {} other {ek}}', + + // Original text: "RAM Usage:" + memoryStatePanel: 'Memória használat:', + + // Original text: "CPUs Usage" + cpuStatePanel: 'CPUs használat', + + // Original text: "VMs Power state" + vmStatePanel: 'VPS áram állapot', + + // Original text: "Pending tasks" + taskStatePanel: 'Függőben lévő feladatok', + + // Original text: "Users" + usersStatePanel: 'Felhasználók', + + // Original text: "Storage state" + srStatePanel: 'Adattároló állapot', + + // Original text: "{usage} (of {total})" + ofUsage: '{usage} (of {total})', + + // Original text: "No storage" + noSrs: 'Nincs adattároló', + + // Original text: "Name" + srName: 'Név', + + // Original text: "Pool" + srPool: 'Pool', + + // Original text: "Host" + srHost: 'Kiszolgáló', + + // Original text: "Type" + srFormat: 'Típus', + + // Original text: "Size" + srSize: 'Méret', + + // Original text: "Usage" + srUsage: 'használat', + + // Original text: "used" + srUsed: 'használva', + + // Original text: "free" + srFree: 'szabad', + + // Original text: "Storage Usage" + srUsageStatePanel: 'Adattároló használat', + + // Original text: "Top 5 SR Usage (in %)" + srTopUsageStatePanel: 'Top 5 Adattároló használat (in %)', + + // Original text: "{running} running ({halted} halted)" + vmsStates: '{running} fut ({halted} halted)', + + // Original text: "Clear selection" + dashboardStatsButtonRemoveAll: 'Kiválasztás törlése', + + // Original text: "Add all hosts" + dashboardStatsButtonAddAllHost: 'Összes kiszolgáló hozzáadása', + + // Original text: "Add all VMs" + dashboardStatsButtonAddAllVM: 'Összes VPS hozzáadása', + + // Original text: "{value} {date, date, medium}" + weekHeatmapData: '{Value} {date, Dátum, medium}', + + // Original text: "No data." + weekHeatmapNoData: 'Nincs adata.', + + // Original text: "Weekly Heatmap" + weeklyHeatmap: 'Heti Hőtérkép', + + // Original text: "Weekly Charts" + weeklyCharts: 'Heti Diagram', + + // Original text: "Synchronize scale:" + weeklyChartsScaleInfo: 'Skála szinkronizálása:', + + // Original text: "Stats error" + statsDashboardGenericErrorTitle: 'Statisztikák hiba', + + // Original text: "There is no stats available for:" + statsDashboardGenericErrorMessage: + 'Jelenleg nincs elérhető statisztika a következőhöz:', + + // Original text: "No selected metric" + noSelectedMetric: 'Nincs kiválasztott mérőszám', + + // Original text: "Select" + statsDashboardSelectObjects: 'Válasszon', + + // Original text: "Loading…" + metricsLoading: 'Töltés…', + + // Original text: "Coming soon!" + comingSoon: 'Hamarosan!', + + // Original text: "Orphaned snapshot VDIs" + orphanedVdis: 'Árván maradt Pillanatképek VDI-k', + + // Original text: "Orphaned VMs snapshot" + orphanedVms: 'Árván maradt VPS Pillanatkép', + + // Original text: "No orphans" + noOrphanedObject: 'Nincsenek árván hagyott pillanatképek', + + // Original text: "Remove all orphaned snapshot VDIs" + removeAllOrphanedObject: 'Árván maradt VPS Pillanatkép VDI-k eltávolítása', + + // Original text: "Name" + vmNameLabel: 'Név', + + // Original text: "Description" + vmNameDescription: 'Leírás', + + // Original text: "Resident on" + vmContainer: 'Itt fut:', + + // Original text: "Alarms" + alarmMessage: 'Riasztások', + + // Original text: "No alarms" + noAlarms: 'Nincsenek riasztások', + + // Original text: "Date" + alarmDate: 'Dátum', + + // Original text: "Content" + alarmContent: 'Tartalom', + + // Original text: "Issue on" + alarmObject: 'Probléma itt', + + // Original text: "Pool" + alarmPool: 'Pool', + + // Original text: "Remove all alarms" + alarmRemoveAll: 'Riasztások eltávolítása', + + // Original text: "{used}% used ({free} left)" + spaceLeftTooltip: '{used}% felhasználva ({free} maradt)', + + // Original text: "Create a new VM on {select}" + newVmCreateNewVmOn: 'VPS létrehozása a következőn: {select}', + + // Original text: "Create a new VM on {select1} or {select2}" + newVmCreateNewVmOn2: 'VPS létrehozása a következőn: {select1} vagy {select2}', + + // Original text: "You have no permission to create a VM" + newVmCreateNewVmNoPermission: + 'Sajnáljuk, nincs jogosultsága új VPS készítéséhez', + + // Original text: "Infos" + newVmInfoPanel: 'Információk', + + // Original text: "Name" + newVmNameLabel: 'Név', + + // Original text: "Template" + newVmTemplateLabel: 'Sablon', + + // Original text: "Description" + newVmDescriptionLabel: 'Leírás', + + // Original text: "Performances" + newVmPerfPanel: 'Teljesítmények', + + // Original text: "vCPUs" + newVmVcpusLabel: 'vCPUs', + + // Original text: "RAM" + newVmRamLabel: 'RAM', + + // Original text: "Static memory max" + newVmStaticMaxLabel: 'Max Statikus Memória', + + // Original text: "Dynamic memory min" + newVmDynamicMinLabel: 'Min Dinamikus Memória', + + // Original text: "Dynamic memory max" + newVmDynamicMaxLabel: 'Max Dinamikus Memória', + + // Original text: "Install settings" + newVmInstallSettingsPanel: 'Telepítési beállítások', + + // Original text: "ISO/DVD" + newVmIsoDvdLabel: 'ISO/DVD', + + // Original text: "Network" + newVmNetworkLabel: 'Hálózat', + + // Original text: "e.g: http://httpredir.debian.org/debian" + newVmInstallNetworkPlaceHolder: 'pl.: http://httpredir.debian.org/debian', + + // Original text: "PV Args" + newVmPvArgsLabel: 'PV Args', + + // Original text: "PXE" + newVmPxeLabel: 'PXE', + + // Original text: "Interfaces" + newVmInterfacesPanel: 'Interfészek', + + // Original text: "MAC" + newVmMacLabel: 'MAC', + + // Original text: "Add interface" + newVmAddInterface: 'Interfész Hozzáadása', + + // Original text: "Disks" + newVmDisksPanel: 'Diszkek', + + // Original text: "SR" + newVmSrLabel: 'Adattároló', + + // Original text: "Size" + newVmSizeLabel: 'Méret', + + // Original text: "Add disk" + newVmAddDisk: 'Diszk Hozzáadása', + + // Original text: "Summary" + newVmSummaryPanel: 'Összesítés', + + // Original text: "Create" + newVmCreate: 'Létrehozás', + + // Original text: "Reset" + newVmReset: 'Visszaállít', + + // Original text: "Select template" + newVmSelectTemplate: 'Válasszon sablont', + + // Original text: "SSH key" + newVmSshKey: 'SSH kulcs', + + // Original text: "Config drive" + newVmConfigDrive: 'Meghajtó beállítása', + + // Original text: "Custom config" + newVmCustomConfig: 'egyedi beállítás', + + // Original text: "Boot VM after creation" + newVmBootAfterCreate: 'VPS bootolása létrehozás után', + + // Original text: "Auto-generated if empty" + newVmMacPlaceholder: 'Auto-generálás ha üres', + + // Original text: "CPU weight" + newVmCpuWeightLabel: 'CPU súly', + + // Original text: "Default: {value, number}" + newVmDefaultCpuWeight: 'Alapértelmezett: {Value, number}', + + // Original text: "CPU cap" + newVmCpuCapLabel: 'CPU cap', + + // Original text: "Default: {value, number}" + newVmDefaultCpuCap: 'Alapértelmezett: {Value, number}', + + // Original text: "Cloud config" + newVmCloudConfig: 'Cloud beállítás', + + // Original text: "Create VMs" + newVmCreateVms: 'VPSek Létrehozása', + + // Original text: "Are you sure you want to create {nbVms} VMs?" + newVmCreateVmsConfirm: 'Biztos benne, hogy létrehoz {nbVms} VPS-t?', + + // Original text: "Multiple VMs:" + newVmMultipleVms: 'Több VPS:', + + // Original text: "Select a resource set:" + newVmSelectResourceSet: 'Válasszon egy erőforrás készletet:', + + // Original text: "Name pattern:" + newVmMultipleVmsPattern: 'Minta Név:', + + // Original text: "e.g.: \\{name\\}_%" + newVmMultipleVmsPatternPlaceholder: 'e.g.: \\{név\\}_%', + + // Original text: "First index:" + newVmFirstIndex: 'Első index:', + + // Original text: "Recalculate VMs number" + newVmNumberRecalculate: 'VPS számok újraszámolása', + + // Original text: "Refresh VMs name" + newVmNameRefresh: 'VPS nevek frissítése', + + // Original text: "Advanced" + newVmAdvancedPanel: 'Haladó', + + // Original text: "Show advanced settings" + newVmShowAdvanced: 'Mutassa a Haladó beállításokat', + + // Original text: "Hide advanced settings" + newVmHideAdvanced: 'Haladó beállítások elrejtése', + + // Original text: 'Share this VM' + newVmShare: undefined, + + // Original text: "Resource sets" + resourceSets: 'Erőforrás készletek', + + // Original text: "No resource sets." + noResourceSets: 'Nincsenek erőforrás készletek.', + + // Original text: "Loading resource sets" + loadingResourceSets: 'Erőforrás készletek betöltése', + + // Original text: "Resource set name" + resourceSetName: 'Erőforrás készlet neve', + + // Original text: "Recompute all limits" + recomputeResourceSets: 'Összes limit újraszámolása', + + // Original text: "Save" + saveResourceSet: 'Mentés', + + // Original text: "Reset" + resetResourceSet: 'Visszaállítás', + + // Original text: "Edit" + editResourceSet: 'Szerkesztés', + + // Original text: "Delete" + deleteResourceSet: 'Törlés', + + // Original text: "Delete resource set" + deleteResourceSetWarning: 'Erőforrás készlet törlése', + + // Original text: "Are you sure you want to delete this resource set?" + deleteResourceSetQuestion: 'Biztos benne, hogy törli az Erőforrás készletet?', + + // Original text: "Missing objects:" + resourceSetMissingObjects: 'Hiányzó objektum:', + + // Original text: "vCPUs" + resourceSetVcpus: 'vCPUs', + + // Original text: "Memory" + resourceSetMemory: 'Memória', + + // Original text: "Storage" + resourceSetStorage: 'Adattároló', + + // Original text: "Unknown" + unknownResourceSetValue: 'Ismeretlen', + + // Original text: "Available hosts" + availableHosts: 'Elérhető kiszolgálók', + + // Original text: "Excluded hosts" + excludedHosts: 'Kizárt kiszolgálók', + + // Original text: "No hosts available." + noHostsAvailable: 'Nincs elérhető kiszolgáló.', + + // Original text: "VMs created from this resource set shall run on the following hosts." + availableHostsDescription: + 'Ezzel az erőforrás készlettel létrehozott VPS-ek a következő kiszolgálókon tudnak futni.', + + // Original text: "Maximum CPUs" + maxCpus: 'Maximum CPU', + + // Original text: "Maximum RAM (GiB)" + maxRam: 'Maximum RAM (GiB)', + + // Original text: "Maximum disk space" + maxDiskSpace: 'Maximum tárhely méret', + + // Original text: "IP pool" + ipPool: 'IP pool', + + // Original text: "Quantity" + quantity: 'Mennyiség', + + // Original text: "No limits." + noResourceSetLimits: 'Nincs limit.', + + // Original text: "Total:" + totalResource: 'Összesen:', + + // Original text: "Remaining:" + remainingResource: 'Marad:', + + // Original text: "Used:" + usedResource: 'Felhasznált:', + + // Original text: "New" + resourceSetNew: 'Új', + + // Original text: "Try dropping some VMs files here, or click to select VMs to upload. Accept only .xva/.ova files." + importVmsList: + 'Húzza ide a VPS fájlokat, vagy kattintson a VPS választásra a feltöltésre. Csak .xva/.ova fájlok támogatottak.', + + // Original text: "No selected VMs." + noSelectedVms: 'Nincs kiválasztott VPS.', + + // Original text: "To Pool:" + vmImportToPool: 'Pool-ra:', + + // Original text: "To SR:" + vmImportToSr: 'Adattárolóra:', + + // Original text: "VMs to import" + vmsToImport: 'Importálandó VPS-el', + + // Original text: "Reset" + importVmsCleanList: 'Visszaállít', + + // Original text: "VM import success" + vmImportSuccess: 'VPS importálása sikeres', + + // Original text: "VM import failed" + vmImportFailed: 'VPS importálása nem sikerült', + + // Original text: "Import starting…" + startVmImport: 'Importálás indul…', + + // Original text: "Export starting…" + startVmExport: 'Exportálás indul…', + + // Original text: "N CPUs" + nCpus: 'N CPUs', + + // Original text: "Memory" + vmMemory: 'Memória', + + // Original text: "Disk {position} ({capacity})" + diskInfo: 'Diszk {position} ({capacity})', + + // Original text: "Disk description" + diskDescription: 'Diszk leírása', + + // Original text: "No disks." + noDisks: 'Nincsenek Diszkek.', + + // Original text: "No networks." + noNetworks: 'Nincsenek Hálózatok.', + + // Original text: "Network {name}" + networkInfo: 'Hálózat {name}', + + // Original text: "No description available" + noVmImportErrorDescription: 'Nincs elérhető leírás', + + // Original text: "Error:" + vmImportError: 'Hiba:', + + // Original text: "{type} file:" + vmImportFileType: '{type} fájl:', + + // Original text: "Please to check and/or modify the VM configuration." + vmImportConfigAlert: 'Kérjük ellenőrizze és vagy módosítsa a VPS beállítást.', + + // Original text: "No pending tasks" + noTasks: 'Nincsenek függő feladatok', + + // Original text: "Currently, there are not any pending XenServer tasks" + xsTasks: 'Jelenleg nincsenek függő XenServer feladatok', + + // Original text: "Schedules" + backupSchedules: 'Időzítések', + + // Original text: "Get remote" + getRemote: 'Távoli Mentés Beállítása', + + // Original text: "List Remote" + listRemote: 'Távoli Mentés Listázása', + + // Original text: "simple" + simpleBackup: 'simple', + + // Original text: "delta" + delta: 'delta', + + // Original text: "Restore Backups" + restoreBackups: 'Adatmentések Visszaállítása', + + // Original text: "Click on a VM to display restore options" + restoreBackupsInfo: + 'Kattintson egy VPS-re a visszaállítási lehetőségek megtekintéséhez', + + // Original text: "Enabled" + remoteEnabled: 'Bekapcsolva', + + // Original text: "Error" + remoteError: 'Hiba', + + // Original text: "No backup available" + noBackup: 'Nincs elérhető adatmentés', + + // Original text: "VM Name" + backupVmNameColumn: 'VPS Név', + + // Original text: "Tags" + backupTags: 'Cimkék', + + // Original text: "Last Backup" + lastBackupColumn: 'Legutolsó Mentés', + + // Original text: "Available Backups" + availableBackupsColumn: 'Elérhető Mentések', + + // Original text: "Missing parameters" + backupRestoreErrorTitle: 'Hiányzó paraméterek', + + // Original text: "Choose a SR and a backup" + backupRestoreErrorMessage: 'Válasszon egy Adattárolót és egy Mentést', + + // Original text: "Display backups" + displayBackup: 'Mentések megjelenítése', + + // Original text: "Import VM" + importBackupTitle: 'VPS Importálás', + + // Original text: "Starting your backup import" + importBackupMessage: 'Mentés importálásának indítása', + + // Original text: "VMs to backup" + vmsToBackup: 'Mentendő VPS-ek', + + // Original text: "List remote backups" + listRemoteBackups: 'Távoli adatmentések listázása', + + // Original text: "Restore backup files" + restoreFiles: 'Adatmentési fájlok visszaállítása', + + // Original text: "Invalid options" + restoreFilesError: 'Érévnytelen beállítás', + + // Original text: "Restore file from {name}" + restoreFilesFromBackup: 'Fájl visszaállítás innen: {name}', + + // Original text: "Select a backup…" + restoreFilesSelectBackup: 'Válasszon adatmentést…', + + // Original text: "Select a disk…" + restoreFilesSelectDisk: 'Válasszon diszket…', + + // Original text: "Select a partition…" + restoreFilesSelectPartition: 'Válasszon egy partíciót…', + + // Original text: "Folder path" + restoreFilesSelectFolderPath: 'Mappa útvonal', + + // Original text: "Select a file…" + restoreFilesSelectFiles: 'Válasszon egy fájlt…', + + // Original text: "Content not found" + restoreFileContentNotFound: 'Tartalom nem található', + + // Original text: "No files selected" + restoreFilesNoFilesSelected: 'Nincsenek kiválasztott fájlok', + + // Original text: "Selected files ({files}):" + restoreFilesSelectedFiles: 'kiválasztott fájlok ({files}):', + + // Original text: "Error while scanning disk" + restoreFilesDiskError: 'Hiba a diszk szkennelése közben', + + // Original text: "Select all this folder's files" + restoreFilesSelectAllFiles: 'A mappa összes fájljainak kiválasztása', + + // Original text: "Unselect all files" + restoreFilesUnselectAll: 'Fájlok kijelölésének törlése', + + // Original text: "Emergency shutdown Host{nHosts, plural, one {} other {s}}" + emergencyShutdownHostsModalTitle: 'Vészhelyzet Kiszolgáló Lekapcsolás', + + // Original text: "Are you sure you want to shutdown {nHosts} Host{nHosts, plural, one {} other {s}}?" + emergencyShutdownHostsModalMessage: + 'Biztos benne, hogy lekapcsolja ezeket a kiszolgálókat?', + + // Original text: "Shutdown host" + stopHostModalTitle: 'Kiszolgáló Leállítása', + + // Original text: "This will shutdown your host. Do you want to continue? If it's the pool master, your connection to the pool will be lost" + stopHostModalMessage: + 'Ezzel le fogja kapcsolni a Kiszolgálót. Biztos benne? Amennyiben ez a pool master a kapcsolatot el fogja veszíteni!', + + // Original text: "Add host" + addHostModalTitle: 'Kiszolgáló Hozzáadása', + + // Original text: "Are you sure you want to add {host} to {pool}?" + addHostModalMessage: + 'Biztos benne, hogy hozzádja a(z) {Host} kiszolgálót a következő poolhoz: {pool}?', + + // Original text: "Restart host" + restartHostModalTitle: 'Kiszolgáló Újraindítása', + + // Original text: "This will restart your host. Do you want to continue?" + restartHostModalMessage: + 'Ez újra fogja indítani a Kiszolgálót. Biztosan folytatja?', + + // Original text: "Restart Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}" + restartHostsAgentsModalTitle: 'Kiszolgáló(k) Újraindítása', + + // Original text: "Are you sure you want to restart {nHosts} Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}?" + restartHostsAgentsModalMessage: 'Biztos benne, hogy újraindítja?', + + // Original text: "Restart Host{nHosts, plural, one {} other {s}}" + restartHostsModalTitle: 'Kiszolgáló(k) Újraindítása', + + // Original text: "Are you sure you want to restart {nHosts} Host{nHosts, plural, one {} other {s}}?" + restartHostsModalMessage: 'Biztos benne, hogy újraindítja?', + + // Original text: "Start VM{vms, plural, one {} other {s}}" + startVmsModalTitle: 'VPS Elindítása', + + // Original text: "Are you sure you want to start {vms} VM{vms, plural, one {} other {s}}?" + startVmsModalMessage: 'Biztos benne, hogy elindítja?', + + // Original text: "Stop Host{nHosts, plural, one {} other {s}}" + stopHostsModalTitle: 'Kiszolgáló Leállítása', + + // Original text: "Are you sure you want to stop {nHosts} Host{nHosts, plural, one {} other {s}}?" + stopHostsModalMessage: + 'Biztos benne, hogy leállítja? Ha ez a master, a kapcsolat elveszhet!', + + // Original text: "Stop VM{vms, plural, one {} other {s}}" + stopVmsModalTitle: 'VPS Leállítás', + + // Original text: "Are you sure you want to stop {vms} VM{vms, plural, one {} other {s}}?" + stopVmsModalMessage: 'Biztos benne, hogy leállítja?', + + // Original text: "Restart VM" + restartVmModalTitle: 'VPS Újraindítása', + + // Original text: "Are you sure you want to restart {name}?" + restartVmModalMessage: 'Biztos benne, hogy újraindítja: {name}-t?', + + // Original text: "Stop VM" + stopVmModalTitle: 'VPS Leállítás', + + // Original text: "Are you sure you want to stop {name}?" + stopVmModalMessage: 'Biztos benne, hogy to leállítja: {name}-t?', + + // Original text: "Restart VM{vms, plural, one {} other {s}}" + restartVmsModalTitle: 'VPS Újraindítás', + + // Original text: "Are you sure you want to restart {vms} VM{vms, plural, one {} other {s}}?" + restartVmsModalMessage: 'Biztos benne, hogy újraindítja?', + + // Original text: "Snapshot VM{vms, plural, one {} other {s}}" + snapshotVmsModalTitle: 'VPS Pillanatképek', + + // Original text: "Are you sure you want to snapshot {vms} VM{vms, plural, one {} other {s}}?" + snapshotVmsModalMessage: 'Biztos benne, hogy készít Pillanatképet a VPS-ről?', + + // Original text: "Delete VM{vms, plural, one {} other {s}}" + deleteVmsModalTitle: 'VPS Törlés', + + // Original text: "Are you sure you want to delete {vms} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED" + deleteVmsModalMessage: + 'Biztos benne, hogy törli a VPS-t? ÖSSZES VPS DISZK ELTÁVOLÍTÁSRA KERÜL!', + + // Original text: "Delete VM" + deleteVmModalTitle: 'VPS Törlés', + + // Original text: "Are you sure you want to delete this VM? ALL VM DISKS WILL BE REMOVED" + deleteVmModalMessage: + 'Biztos benne, hogy törli a VPS-t? ÖSSZES VPS DISZK ELTÁVOLÍTÁSRA KERÜL!', + + // Original text: "Migrate VM" + migrateVmModalTitle: 'VPS Migrálása', + + // Original text: "Select a destination host:" + migrateVmSelectHost: 'Válasszon cél Kiszolgálót:', + + // Original text: "Select a migration network:" + migrateVmSelectMigrationNetwork: 'Válasszon egy migrációs hálózatot:', + + // Original text: "For each VDI, select an SR:" + migrateVmSelectSrs: 'Minden VDI számára: válasszon egy Adattárolót:', + + // Original text: "For each VIF, select a network:" + migrateVmSelectNetworks: 'Minden VIF számára, válasszon egy Hálózatot:', + + // Original text: "Select a destination SR:" + migrateVmsSelectSr: 'Válasszon cél Adattárolót:', + + // Original text: "Select a destination SR for local disks:" + migrateVmsSelectSrIntraPool: + 'Válasszon egy cél Adattárolót a helyi diszkek számára:', + + // Original text: "Select a network on which to connect each VIF:" + migrateVmsSelectNetwork: + 'Válasszon egy Hálózatot amelyekhez csatlakoztasson minden VIF-et:', + + // Original text: "Smart mapping" + migrateVmsSmartMapping: 'Okos feltérképezés', + + // Original text: "Name" + migrateVmName: 'Név', + + // Original text: "SR" + migrateVmSr: 'Adattároló', + + // Original text: "VIF" + migrateVmVif: 'VIF', + + // Original text: "Network" + migrateVmNetwork: 'Hálózat', + + // Original text: "No target host" + migrateVmNoTargetHost: 'Nincs cél Kiszolgáló', + + // Original text: "A target host is required to migrate a VM" + migrateVmNoTargetHostMessage: + 'Egy cél Kiszolgáló szükséges a VPS migráláshoz!', + + // Original text: "Delete VDI" + deleteVdiModalTitle: 'VDI Törlése', + + // Original text: "Are you sure you want to delete this disk? ALL DATA ON THIS DISK WILL BE LOST" + deleteVdiModalMessage: + 'Biztos benne, hogy törli a VPS diszkjét? ÖSSZES ADAT ELTÁVOLÍTÁSRA KERÜL!', + + // Original text: "Revert your VM" + revertVmModalTitle: 'VPS Visszaállítása', + + // Original text: "Delete snapshot" + deleteSnapshotModalTitle: 'Pillanatkép Törlése', + + // Original text: "Are you sure you want to delete this snapshot?" + deleteSnapshotModalMessage: + 'Biztos benne, hogy törli a kiválasztott Pillanatképet?', + + // Original text: "Are you sure you want to revert this VM to the snapshot state? This operation is irreversible." + revertVmModalMessage: + 'Biztos benne, hogy visszaállítja a VPS-t a kiválasztott Pillanatkép állapotra? A folyamat visszafordíthatatlan és minden adat elveszik ami a Pillanatkép készítése óta keletkezett!', + + // Original text: "Snapshot before" + revertVmModalSnapshotBefore: 'Pillanatkép ezelőtt', + + // Original text: "Import a {name} Backup" + importBackupModalTitle: '{name} Mentés Importálása', + + // Original text: "Start VM after restore" + importBackupModalStart: 'Visszaállítás után a VPS elindítása', + + // Original text: "Select your backup…" + importBackupModalSelectBackup: 'Válasszon mentést…', + + // Original text: "Are you sure you want to remove all orphaned snapshot VDIs?" + removeAllOrphanedModalWarning: + 'Biztos benne, hogy Eltávolítja az összes árvány hagyott Pillanatkép VDI-t?', + + // Original text: "Remove all logs" + removeAllLogsModalTitle: 'Összes Log Eltávolítása', + + // Original text: "Are you sure you want to remove all logs?" + removeAllLogsModalWarning: 'Biztos benne, hogy Eltávolítja az összes Logot?', + + // Original text: "This operation is definitive." + definitiveMessageModal: 'Ez a művelet végleges.', + + // Original text: "Previous SR Usage" + existingSrModalTitle: 'Előző Adattároló használata', + + // Original text: "This path has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingSrModalText: + 'This path has been previously used as a Storage by a XenServer Host. All data will be lost if you choose to continue the Storage Creation.', + + // Original text: "Previous LUN Usage" + existingLunModalTitle: 'Előző LUN használat', + + // Original text: "This LUN has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingLunModalText: + 'This LUN has been previously used as a Storage by a XenServer Host. All data will be lost if you choose to continue the Storage Creation.', + + // Original text: "Replace current registration?" + alreadyRegisteredModal: 'Replace current registration?', + + // Original text: "Your XO appliance is already registered to {email}, do you want to forget and replace this registration ?" + alreadyRegisteredModalText: + 'Your XO appliance is already registered to {email}, do you want to Elfelejt and replace this registration ?', + + // Original text: "Ready for trial?" + trialReadyModal: 'Ready for trial?', + + // Original text: "During the trial period, XOA need to have a working internet connection. This limitation does not apply for our paid plans!" + trialReadyModalText: + 'During the trial period, XOA need to have a working internet Ceonnection This limitation does not apply for our paid plans!', + + // Original text: "Host" + serverHost: 'Kiszolgáló', + + // Original text: "Username" + serverUsername: 'Felhasználónév', + + // Original text: "Password" + serverPassword: 'Jelszó', + + // Original text: "Action" + serverAction: 'Művelet', + + // Original text: "Read Only" + serverReadOnly: 'Csak Olvasható', + + // Original text: "Disconnect server" + serverDisconnect: 'Szerver Lecsatlakozás', + + // Original text: "username" + serverPlaceHolderUser: 'felhasználónév', + + // Original text: "password" + serverPlaceHolderPassword: 'jelszó', + + // Original text: "address[:port]" + serverPlaceHolderAddress: 'address[:port]', + + // Original text: "Connect" + serverConnect: 'Csatlakozás', + + // Original text: "Error" + serverError: 'Hiba', + + // Original text: "Adding server failed" + serverAddFailed: 'Szerver Hozzáadása Sikertelen', + + // Original text: "Status" + serverStatus: 'Állapot', + + // Original text: "Connection failed" + serverConnectionFailed: 'Csatlakozás Sikertelen', + + // Original text: "Connecting…" + serverConnecting: 'Csatlakozás…', + + // Original text: "Connected" + serverConnected: 'Kapcsolódva', + + // Original text: "Disconnected" + serverDisconnected: 'Lekapcsolódva', + + // Original text: "Authentication error" + serverAuthFailed: 'Bejelentkezési hiba', + + // Original text: "Unknown error" + serverUnknownError: 'Ismeretlen hiba', + + // Original text: "Copy VM" + copyVm: 'VPS Másolás', + + // Original text: "Are you sure you want to copy this VM to {SR}?" + copyVmConfirm: + 'Biztos benne, hogy a VPS-t a következő Adattárolóra másolja? {Storage}?', + + // Original text: "Name" + copyVmName: 'Név', + + // Original text: "Name pattern" + copyVmNamePattern: 'Név minta', + + // Original text: "If empty: name of the copied VM" + copyVmNamePlaceholder: 'Ha üres: a másolt VPS neve', + + // Original text: "e.g.: \"\\{name\\}_COPY\"" + copyVmNamePatternPlaceholder: 'pl.: "\\{name\\}_Masolat"', + + // Original text: "Select SR" + copyVmSelectSr: 'Válasszon Adattárolót', + + // Original text: "Use compression" + copyVmCompress: 'Tömörítés használata', + + // Original text: "No target SR" + copyVmsNoTargetSr: 'Nincs cél Adattároló', + + // Original text: "A target SR is required to copy a VM" + copyVmsNoTargetSrMessage: 'Egy cél Adattároló szükséges a VPS másolásához', + + // Original text: "Detach host" + detachHostModalTitle: 'Detach Host', + + // Original text: "Are you sure you want to detach {host} from its pool? THIS WILL REMOVE ALL VMs ON ITS LOCAL STORAGE AND REBOOT THE HOST." + detachHostModalMessage: + 'Biztos benne?? THIS WILL REMOVE ALL VMs ON ITS LOCAL STORAGE AND RESTART THE HOST.', + + // Original text: "Detach" + detachHost: 'Detach', + + // Original text: "Create network" + newNetworkCreate: 'Hálózat Létrehozása', + + // Original text: "Create bonded network" + newBondedNetworkCreate: 'Bond Hálózat Létrehozás', + + // Original text: "Interface" + newNetworkInterface: 'Interfész', + + // Original text: "Name" + newNetworkName: 'Név', + + // Original text: "Description" + newNetworkDescription: 'Leírás', + + // Original text: "VLAN" + newNetworkVlan: 'VLAN', + + // Original text: "No VLAN if empty" + newNetworkDefaultVlan: 'Nincs VLAN ha üres', + + // Original text: "MTU" + newNetworkMtu: 'MTU', + + // Original text: "Default: 1500" + newNetworkDefaultMtu: 'Alapértelmezett: 1500', + + // Original text: "Name required" + newNetworkNoNameErrorTitle: 'Név szükséges', + + // Original text: "A name is required to create a network" + newNetworkNoNameErrorMessage: 'Egy név szükséges a Hálózat létrehozásához', + + // Original text: "Bond mode" + newNetworkBondMode: 'Bond üzem', + + // Original text: "Delete network" + deleteNetwork: 'Hálózat Törlése', + + // Original text: "Are you sure you want to delete this network?" + deleteNetworkConfirm: 'Biztos benne, hogy törli a Hálózatot?', + + // Original text: "This network is currently in use" + networkInUse: 'Ez a Hálózat jelenleg használatban van', + + // Original text: "Bonded" + pillBonded: 'Bonded', + + // Original text: "Host" + addHostSelectHost: 'Kiszolgáló', + + // Original text: "No host" + addHostNoHost: 'Nincs Kiszolgáló', + + // Original text: "No host selected to be added" + addHostNoHostMessage: + 'Nincs Kiszolgáló kiválasztva amihez hozzá lehetne adni', + + // Original text: "Xen Orchestra" + xenOrchestra: 'CLOUDXO', + + // Original text: "Xen Orchestra server" + xenOrchestraServer: 'Cloudxo szerver', + + // Original text: "Xen Orchestra web client" + xenOrchestraWeb: 'Cloudxo web kliens', + + // Original text: "No pro support provided!" + noProSupport: 'Nincsen pro-szupport!', + + // Original text: "Use in production at your own risks" + noProductionUse: 'Use in production at your own risks', + + // Original text: "You can download our turnkey appliance at {website}" + downloadXoaFromWebsite: 'You can download our turnkey appliance at {website}', + + // Original text: "Bug Tracker" + bugTracker: 'Bug Tracker', + + // Original text: "Issues? Report it!" + bugTrackerText: 'Issues? Report it!', + + // Original text: "Community" + community: 'Community', + + // Original text: "Join our community forum!" + communityText: 'Join our community forum!', + + // Original text: "Free Trial for Premium Edition!" + freeTrial: 'Free Trial for Premium Edition!', + + // Original text: "Request your trial now!" + freeTrialNow: 'Request your trial now!', + + // Original text: "Any issue?" + issues: 'Any issue?', + + // Original text: "Problem? Contact us!" + issuesText: 'Problem? Contact us!', + + // Original text: "Documentation" + documentation: 'Documentation', + + // Original text: "Read our official doc" + documentationText: 'Read our official doc', + + // Original text: "Pro support included" + proSupportIncluded: 'Pro support included', + + // Original text: "Access your XO Account" + xoAccount: 'Access your XO Account', + + // Original text: "Report a problem" + openTicket: 'Report a problem', + + // Original text: "Problem? Open a ticket!" + openTicketText: 'Problem? Open a ticket!', + + // Original text: "Upgrade needed" + upgradeNeeded: 'Upgrade needed', + + // Original text: "Upgrade now!" + upgradeNow: 'Upgrade now!', + + // Original text: "Or" + or: 'Or', + + // Original text: "Try it for free!" + tryIt: 'Try it for free!', + + // Original text: "This feature is available starting from {plan} Edition" + availableIn: 'This feature is available Starting from {plan} Edition', + + // Original text: "This feature is not available in your version, contact your administrator to know more." + notAvailable: + 'This feature is not available in your Version, contact your administrator to know more.', + + // Original text: "Updates" + updateTitle: 'UpDates', + + // Original text: "Registration" + registration: 'Registration', + + // Original text: "Trial" + trial: 'Trial', + + // Original text: "Settings" + settings: 'Beállítások', + + // Original text: "Proxy settings" + proxySettings: 'Proxy settings', + + // Original text: "Host (myproxy.example.org)" + proxySettingsHostPlaceHolder: 'Kiszolgáló (myproxy.example.org)', + + // Original text: "Port (eg: 3128)" + proxySettingsPortPlaceHolder: 'Port (eg: 3128)', + + // Original text: "Username" + proxySettingsUsernamePlaceHolder: 'Felhasználónév', + + // Original text: "Password" + proxySettingsPasswordPlaceHolder: 'Jelszó', + + // Original text: "Your email account" + updateRegistrationEmailPlaceHolder: 'Your email account', + + // Original text: "Your password" + updateRegistrationPasswordPlaceHolder: 'Your jelszó', + + // Original text: "Update" + update: 'UpDates', + + // Original text: "Refresh" + refresh: 'Refresh', + + // Original text: "Upgrade" + upgrade: 'Upgrade', + + // Original text: "No updater available for Community Edition" + noUpdaterCommunity: 'No upDate available for Community Edition', + + // Original text: "Please consider subscribe and try it with all features for free during 15 days on {link}." + considerSubscribe: + 'Please consider subscribe and try it with all features for free during 15 days on {link}.', + + // Original text: "Manual update could break your current installation due to dependencies issues, do it with caution" + noUpdaterWarning: + 'Manual upDate could break your current installation due to dependencies issues, do it with caution', + + // Original text: "Current version:" + currentVersion: 'Jelenlegi Verzió:', + + // Original text: "Register" + register: 'Register', + + // Original text: "Edit registration" + editRegistration: 'Szerkesztés registration', + + // Original text: "Please, take time to register in order to enjoy your trial." + trialRegistration: + 'Please, take time to register in order to enjoy your trial.', + + // Original text: "Start trial" + trialStartButton: 'Elindít trial', + + // Original text: "You can use a trial version until {date, date, medium}. Upgrade your appliance to get it." + trialAvailableUntil: + 'You can use a trial Verzió until {date, Dátum, medium}. Upgrade your appliance to get it.', + + // Original text: "Your trial has been ended. Contact us or downgrade to Free version" + trialConsumed: + 'Your trial has been ended. Contact us or downgrade to Free Verzió', + + // Original text: "Your xoa-updater service appears to be down. Your XOA cannot run fully without reaching this service." + trialLocked: + 'Your xoa-upDátumr service appears to be down. Your XOA cannot run fully without reaching this service.', + + // Original text: "No update information available" + noUpdateInfo: 'No upDátum information available', + + // Original text: "Update information may be available" + waitingUpdateInfo: 'UpDátum information may be available', + + // Original text: "Your XOA is up-to-date" + upToDate: 'Your XOA is up-to-Dátum', + + // Original text: "You need to update your XOA (new version is available)" + mustUpgrade: 'You need to upDate your XOA (new Verzió is available)', + + // Original text: "Your XOA is not registered for updates" + registerNeeded: 'Your XOA is not registered for upDates', + + // Original text: "Can't fetch update information" + updaterError: "Can't fetch upDátum information", + + // Original text: "Upgrade successful" + promptUpgradeReloadTitle: 'Upgrade successful', + + // Original text: "Your XOA has successfully upgraded, and your browser must reload the application. Do you want to reload now ?" + promptUpgradeReloadMessage: + 'Your XOA has successfully upgraded, and your browser must reload the application. Do you want to reload now ?', + + // Original text: "Xen Orchestra from the sources" + disclaimerTitle: 'Xen Orchestra from the sources', + + // Original text: "You are using XO from the sources! That's great for a personal/non-profit usage." + disclaimerText1: + "You are using XO from the sources! That's great for a personal/non-profit használat.", + + // Original text: "If you are a company, it's better to use it with our appliance + pro support included:" + disclaimerText2: + "If you are a company, it's better to use it with our appliance + pro support included:", + + // Original text: "This version is not bundled with any support nor updates. Use it with caution for critical tasks." + disclaimerText3: + 'This Verzió is not bundled with any support nor upDates. Use it with caution for critical tasks.', + + // Original text: "Connect PIF" + connectPif: 'Csatlakozás PIF', + + // Original text: "Are you sure you want to connect this PIF?" + connectPifConfirm: 'Biztos benne, hogyi to Csatlakozás this PIF?', + + // Original text: "Disconnect PIF" + disconnectPif: 'Lecsatlakozás PIF', + + // Original text: "Are you sure you want to disconnect this PIF?" + disconnectPifConfirm: 'Biztos benne, hogyi to Lecsatlakozás this PIF?', + + // Original text: "Delete PIF" + deletePif: 'Törlés PIF', + + // Original text: "Are you sure you want to delete this PIF?" + deletePifConfirm: 'Biztos benne, hogyi to delete this PIF?', + + // Original text: "Username" + username: 'Felhasználónév', + + // Original text: "Password" + password: 'Jelszó', + + // Original text: "Language" + language: 'Nyelv', + + // Original text: "Old password" + oldPasswordPlaceholder: 'Régi jelszó', + + // Original text: "New password" + newPasswordPlaceholder: 'Új jelszó', + + // Original text: "Confirm new password" + confirmPasswordPlaceholder: 'Új Jelszó Megerősítése', + + // Original text: "Confirmation password incorrect" + confirmationPasswordError: 'Megerősítő jelszó helytelen', + + // Original text: "Password does not match the confirm password." + confirmationPasswordErrorBody: 'A megadott jelszavak nem egyeznek.', + + // Original text: "Password changed" + pwdChangeSuccess: 'Jelszó megváltoztatva', + + // Original text: "Your password has been successfully changed." + pwdChangeSuccessBody: 'A jelszó sikeresen megváltoztatva.', + + // Original text: "Incorrect password" + pwdChangeError: 'Helytelen jelszó', + + // Original text: "The old password provided is incorrect. Your password has not been changed." + pwdChangeErrorBody: + 'A megadott régi jelszó helytelen, így a jelszó NEM lett megváltoztatva!', + + // Original text: "OK" + changePasswordOk: 'OK', + + // Original text: "SSH keys" + sshKeys: 'SSH kulcsok', + + // Original text: "New SSH key" + newSshKey: 'Új SSH kulcs', + + // Original text: "Delete" + deleteSshKey: 'Törlés', + + // Original text: "No SSH keys" + noSshKeys: 'Nincsenek SSH kulcsok', + + // Original text: "New SSH key" + newSshKeyModalTitle: 'Új SSH kulcs', + + // Original text: "Invalid key" + sshKeyErrorTitle: 'Helytelen kulcs', + + // Original text: "An SSH key requires both a title and a key." + sshKeyErrorMessage: 'Az SSH kulcshoz szükség van egy címre és egy kulcsra.', + + // Original text: "Title" + title: 'Cím', + + // Original text: "Key" + key: 'kulcs', + + // Original text: "Delete SSH key" + deleteSshKeyConfirm: 'SSH kulcs törlése', + + // Original text: "Are you sure you want to delete the SSH key {title}?" + deleteSshKeyConfirmMessage: + 'Biztos benne, hogy törli a(z) {title} SSH kulcsot?', + + // Original text: "Others" + others: 'Egyebek', + + // Original text: "Loading logs…" + loadingLogs: 'Logok betöltése…', + + // Original text: "User" + logUser: 'Felhasználó', + + // Original text: "Method" + logMethod: 'Módszer', + + // Original text: "Params" + logParams: 'Paraméterek', + + // Original text: "Message" + logMessage: 'Üzenet', + + // Original text: "Error" + logError: 'Hiba', + + // Original text: "Display details" + logDisplayDetails: 'Részletek megjelenítése', + + // Original text: "Date" + logTime: 'Dátum', + + // Original text: "No stack trace" + logNoStackTrace: 'No stack trace', + + // Original text: "No params" + logNoParams: 'No params', + + // Original text: "Delete log" + logDelete: 'Log Törlése', + + // Original text: "Delete all logs" + logDeleteAll: 'Összes Log Törlése', + + // Original text: "Delete all logs" + logDeleteAllTitle: 'Összes Log Törlése', + + // Original text: "Are you sure you want to delete all the logs?" + logDeleteAllMessage: 'Biztos benne, hogy törli az összes Logot?', + + // Original text: 'Click to enable' + logIndicationToEnable: undefined, + + // Original text: 'Click to disable' + logIndicationToDisable: undefined, + + // Original text: "Report a bug" + reportBug: 'Hibabejelentés', + + // Original text: "Name" + ipPoolName: 'Név', + + // Original text: "IPs" + ipPoolIps: 'IP címek', + + // Original text: "IPs (e.g.: 1.0.0.12-1.0.0.17;1.0.0.23)" + ipPoolIpsPlaceholder: 'IP címek (e.g.: 1.0.0.12-1.0.0.17;1.0.0.23)', + + // Original text: "Networks" + ipPoolNetworks: 'Hálózatok', + + // Original text: "No IP pools" + ipsNoIpPool: 'No IP pools', + + // Original text: "Create" + ipsCreate: 'Létrehozás', + + // Original text: "Delete all IP pools" + ipsDeleteAllTitle: 'Delet all IP pools', + + // Original text: "Are you sure you want to delete all the IP pools?" + ipsDeleteAllMessage: 'Are you sure you want to delete all the IP pools?', + + // Original text: "VIFs" + ipsVifs: 'VIFs', + + // Original text: "Not used" + ipsNotUsed: 'Not used', + + // Original text: "unknown VIF" + ipPoolUnknownVif: 'ismeretlen VIF', + + // Original text: "Keyboard shortcuts" + shortcutModalTitle: 'Billentyűzet kiosztások', + + // Original text: "Global" + shortcut_XoApp: 'Globális', + + // Original text: "Go to hosts list" + shortcut_GO_TO_HOSTS: 'Menjen a Kiszolgálók listájához', + + // Original text: "Go to pools list" + shortcut_GO_TO_POOLS: 'Go to pools list', + + // Original text: "Go to VMs list" + shortcut_GO_TO_VMS: 'Go to VMs list', + + // Original text: "Go to SRs list" + shortcut_GO_TO_SRS: 'Go to Storage list', + + // Original text: "Create a new VM" + shortcut_CREATE_VM: 'Új VPS Létrehozása', + + // Original text: "Unfocus field" + shortcut_UNFOCUS: 'Unfocus field', + + // Original text: "Show shortcuts key bindings" + shortcut_HELP: 'Show shortcuts key bindings', + + // Original text: "Home" + shortcut_Home: 'Kezdőlap', + + // Original text: "Focus search bar" + shortcut_SEARCH: 'Focus keresősáv', + + // Original text: "Next item" + shortcut_NAV_DOWN: 'Következő', + + // Original text: "Previous item" + shortcut_NAV_UP: 'Előző', + + // Original text: "Select item" + shortcut_SELECT: 'Válasszon', + + // Original text: "Open" + shortcut_JUMP_INTO: 'Megnyitás', + + // Original text: "VM" + settingsAclsButtonTooltipVM: 'VMPS', + + // Original text: "Hosts" + settingsAclsButtonTooltiphost: 'Kiszolgáló', + + // Original text: "Pool" + settingsAclsButtonTooltippool: 'Pool', + + // Original text: "SR" + settingsAclsButtonTooltipSR: 'Adattároló', + + // Original text: "Network" + settingsAclsButtonTooltipnetwork: 'Hálózat', + + // Original text: "No config file selected" + noConfigFile: 'Nincs kiválasztott konfigurációs fájl', + + // Original text: "Try dropping a config file here, or click to select a config file to upload." + importTip: + 'Try dropping a config file here, or click to choose a config file to upload.', + + // Original text: "Config" + config: 'Konfiguráció', + + // Original text: "Import" + importConfig: 'Importálás', + + // Original text: "Config file successfully imported" + importConfigSuccess: 'Config file successfully imported', + + // Original text: "Error while importing config file" + importConfigError: 'Hiba while importing config file', + + // Original text: "Export" + exportConfig: 'Export', + + // Original text: "Download current config" + downloadConfig: 'Download current config', + + // Original text: "No config import available for Community Edition" + noConfigImportCommunity: + 'No config import available for Community Szerkesztésion', + + // Original text: "Reconnect all hosts" + srReconnectAllModalTitle: 'Reconnect all hosts', + + // Original text: "This will reconnect this SR to all its hosts." + srReconnectAllModalMessage: + 'This will reconnecting this Storage to all its hosts.', + + // Original text: "This will reconnect each selected SR to its host (local SR) or to every hosts of its pool (shared SR)." + srsReconnectAllModalMessage: + 'This will reconnecz each kiválasztott SR to its Kiszolgáló (local SR) or to every kiszolgálók of its pool (Megosztva Adattároló).', + + // Original text: "Disconnect all hosts" + srDisconnectAllModalTitle: 'Lecsatlakozás all kiszolgálók', + + // Original text: "This will disconnect this SR from all its hosts." + srDisconnectAllModalMessage: + 'This will Lecsatlakozás this Adattároló from all its kiszolgálók.', + + // Original text: "This will disconnect each selected SR from its host (local SR) or from every hosts of its pool (shared SR)." + srsDisconnectAllModalMessage: + 'This will Lecsatlakozás each kiválasztott SR from its Kiszolgáló (local SR) or from every kiszolgálók of its pool (Megosztva Adattároló).', + + // Original text: "Forget SR" + srForgetModalTitle: 'Elfelejt Adattároló', + + // Original text: "Forget selected SRs" + srsForgetModalTitle: 'Elfelejt kiválasztott Adattárolók', + + // Original text: "Are you sure you want to forget this SR? VDIs on this storage won't be removed." + srForgetModalMessage: + "Biztos benne, hogyi to Elfelejt this Adattároló? VDIs on this storage won't be Eltávolításd.", + + // Original text: "Are you sure you want to forget all the selected SRs? VDIs on these storages won't be removed." + srsForgetModalMessage: + "Biztos benne, hogyi to Elfelejt all the kiválasztott Adattárolók? VDIs on these storages won't be Eltávolításd.", + + // Original text: "Disconnected" + srAllDisconnected: 'Lekapcsolódva', + + // Original text: "Partially connected" + srSomeConnected: 'Partially Kapcsolódva', + + // Original text: "Connected" + srAllConnected: 'Kapcsolódva', + + // Original text: 'XOSAN' + xosanTitle: undefined, + + // Original text: 'Xen Orchestra SAN SR' + xosanSrTitle: undefined, + + // Original text: 'Select local SRs (lvm)' + xosanAvailableSrsTitle: undefined, + + // Original text: 'Suggestions' + xosanSuggestions: undefined, + + // Original text: 'Name' + xosanName: undefined, + + // Original text: 'Host' + xosanHost: undefined, + + // Original text: 'Hosts' + xosanHosts: undefined, + + // Original text: 'Volume ID' + xosanVolumeId: undefined, + + // Original text: 'Size' + xosanSize: undefined, + + // Original text: 'Used space' + xosanUsedSpace: undefined, + + // Original text: 'XOSAN pack needs to be installed on each host of the pool.' + xosanNeedPack: undefined, + + // Original text: 'Install it now!' + xosanInstallIt: undefined, + + // Original text: 'Install XOSAN pack on {pool}' + xosanInstallPackTitle: undefined, + + // Original text: 'Select at least 2 SRs' + xosanSelect2Srs: undefined, + + // Original text: 'Layout' + xosanLayout: undefined, + + // Original text: 'Redundancy' + xosanRedundancy: undefined, + + // Original text: 'Capacity' + xosanCapacity: undefined, + + // Original text: 'Available space' + xosanAvailableSpace: undefined, + + // Original text: '* Can fail without data loss' + xosanDiskLossLegend: undefined, + + // Original text: 'Create' + xosanCreate: undefined, + + // Original text: 'Installing XOSAN. Please wait…' + xosanInstalling: undefined, + + // Original text: 'You need XenServer 7.0 to install XOSAN' + xosanBadVersion: undefined, + + // Original text: 'No XOSAN available for Community Edition' + xosanCommunity: undefined, + + // Original text: 'Install cloud plugin first' + xosanInstallCloudPlugin: undefined, + + // Original text: 'Load cloud plugin first' + xosanLoadCloudPlugin: undefined, + + // Original text: 'Loading…' + xosanLoading: undefined, + + // Original text: 'XOSAN is not available at the moment' + xosanNotAvailable: undefined, + + // Original text: 'Register for the XOSAN beta' + xosanRegisterBeta: undefined, + + // Original text: 'You have successfully registered for the XOSAN beta. Please wait until your request has been approved.' + xosanSuccessfullyRegistered: undefined, + + // Original text: 'Install XOSAN pack on these hosts:' + xosanInstallPackOnHosts: undefined, + + // Original text: 'Install {pack} v{version}?' + xosanInstallPack: undefined, +} diff --git a/packages/xo-web/src/common/intl/locales/pl.js b/packages/xo-web/src/common/intl/locales/pl.js new file mode 100644 index 000000000..6b436038a --- /dev/null +++ b/packages/xo-web/src/common/intl/locales/pl.js @@ -0,0 +1,3186 @@ +// See http://momentjs.com/docs/#/use-it/browserify/ +import 'moment/locale/pl' + +import reactIntlData from 'react-intl/locale-data/pl' +import { addLocaleData } from 'react-intl' +addLocaleData(reactIntlData) + +// =================================================================== + +export default { + // Original text: "Connecting" + statusConnecting: 'Trwa łączenie…', + + // Original text: "Disconnected" + statusDisconnected: 'Rozłączono', + + // Original text: "Loading…" + statusLoading: 'Ładowanie…', + + // Original text: "Page not found" + errorPageNotFound: 'Nie znaleziono strony', + + // Original text: "no such item" + errorNoSuchItem: 'nie ma takiego elementu', + + // Original text: "Long click to edit" + editableLongClickPlaceholder: 'Przytrzymaj żeby edytować', + + // Original text: "Click to edit" + editableClickPlaceholder: 'Kliknij żeby edytować', + + // Original text: "OK" + alertOk: 'OK', + + // Original text: "OK" + confirmOk: 'OK', + + // Original text: "Cancel" + confirmCancel: 'Anuluj', + + // Original text: "On error" + onError: 'Błąd', + + // Original text: "Successful" + successful: 'Ukończone', + + // Original text: "Copy to clipboard" + copyToClipboard: 'Kopiuj do schowka', + + // Original text: "Master" + pillMaster: 'Master', + + // Original text: "Home" + homePage: 'Start', + + // Original text: "VMs" + homeVmPage: 'VMs', + + // Original text: "Hosts" + homeHostPage: 'Hosty', + + // Original text: "Pools" + homePoolPage: 'Pule', + + // Original text: "Templates" + homeTemplatePage: 'Szablony', + + // Original text: "Dashboard" + dashboardPage: 'Dashboard', + + // Original text: "Overview" + overviewDashboardPage: 'Podgląd', + + // Original text: "Visualizations" + overviewVisualizationDashboardPage: 'Wizualizacje', + + // Original text: "Statistics" + overviewStatsDashboardPage: 'Statystyki', + + // Original text: "Health" + overviewHealthDashboardPage: 'Stan serwera', + + // Original text: "Self service" + selfServicePage: 'Samoobsługa', + + // Original text: "Backup" + backupPage: 'Backup', + + // Original text: "Jobs" + jobsPage: 'Zadania', + + // Original text: "Updates" + updatePage: 'Aktualizacje', + + // Original text: "Settings" + settingsPage: 'Settings', + + // Original text: "Servers" + settingsServersPage: 'Serwery', + + // Original text: "Users" + settingsUsersPage: 'Użytkownicy', + + // Original text: "Groups" + settingsGroupsPage: 'Grupy', + + // Original text: "ACLs" + settingsAclsPage: 'ACLs', + + // Original text: "Plugins" + settingsPluginsPage: 'Dodatki', + + // Original text: "Logs" + settingsLogsPage: 'Logi', + + // Original text: "IPs" + settingsIpsPage: 'IPs', + + // Original text: "About" + aboutPage: 'O oprogramowaniu', + + // Original text: "About {xoaPlan}" + aboutXoaPlan: 'O {xoaPlan}', + + // Original text: "New" + newMenu: 'Nowy', + + // Original text: "Tasks" + taskMenu: 'Zadania', + + // Original text: "Tasks" + taskPage: 'Zadania', + + // Original text: "VM" + newVmPage: 'VM', + + // Original text: "Storage" + newSrPage: 'Przestrzeń dyskowa', + + // Original text: "Server" + newServerPage: 'Serwer', + + // Original text: "Import" + newImport: 'Importuj', + + // Original text: "Overview" + backupOverviewPage: 'Podgląd', + + // Original text: "New" + backupNewPage: 'Nowy', + + // Original text: "Remotes" + backupRemotesPage: 'Zdalne', + + // Original text: "Restore" + backupRestorePage: 'Odtwórz', + + // Original text: "Schedule" + schedule: 'Harmonogram', + + // Original text: "New VM backup" + newVmBackup: 'Nowy backup VM', + + // Original text: "Edit VM backup" + editVmBackup: 'Edytuj backup VM', + + // Original text: "Backup" + backup: 'Backup', + + // Original text: "Rolling Snapshot" + rollingSnapshot: 'Rolling Snapshot', + + // Original text: "Delta Backup" + deltaBackup: 'Delta Backup', + + // Original text: "Disaster Recovery" + disasterRecovery: 'Odzyskiwanie po awarii', + + // Original text: "Continuous Replication" + continuousReplication: 'Continous Replication', + + // Original text: "Overview" + jobsOverviewPage: 'Podgląd', + + // Original text: "New" + jobsNewPage: 'Nowe', + + // Original text: "Scheduling" + jobsSchedulingPage: 'Planowanie', + + // Original text: "Custom Job" + customJob: 'Zadania niestandardowe', + + // Original text: "User" + userPage: 'Użytkownik', + + // Original text: "No support" + noSupport: 'Brak wsparcia', + + // Original text: "Free upgrade!" + freeUpgrade: 'Darmowa aktualizacja!', + + // Original text: "Sign out" + signOut: 'Wyloguj', + + // Original text: "Edit my settings {username}" + editUserProfile: 'Edytuj moje ustawienia {username}', + + // Original text: "Fetching data…" + homeFetchingData: 'Fetching data…', + + // Original text: "Welcome on Xen Orchestra!" + homeWelcome: 'Witaj w Xen Orchestra!', + + // Original text: "Add your XenServer hosts or pools" + homeWelcomeText: 'Dodaj serwery XenServer lub pule', + + // Original text: "Want some help?" + homeHelp: 'Potrzebujesz pomocy?', + + // Original text: "Add server" + homeAddServer: 'Dodaj serwer', + + // Original text: "Online Doc" + homeOnlineDoc: 'Online Doc', + + // Original text: "Pro Support" + homeProSupport: 'Profesjonalne wsparcie', + + // Original text: "There are no VMs!" + homeNoVms: 'Nie masz żadnych VMs!', + + // Original text: "Or…" + homeNoVmsOr: 'Lub…', + + // Original text: "Import VM" + homeImportVm: 'Importuj VM', + + // Original text: "Import an existing VM in xva format" + homeImportVmMessage: 'Importuj istniejącą VM w formacie xva', + + // Original text: "Restore a backup" + homeRestoreBackup: 'Przywróć kopię zapasową', + + // Original text: "Restore a backup from a remote store" + homeRestoreBackupMessage: 'Przywróć kopię zapasową z innego miejsca', + + // Original text: "This will create a new VM" + homeNewVmMessage: 'Kliknij w ikonę żeby utworzyć VM', + + // Original text: "Filters" + homeFilters: 'Filtry', + + // Original text: "No results! Click here to reset your filters" + homeNoMatches: 'Brak wyników! Kliknij tutaj żeby usunąć filtry', + + // Original text: "Pool" + homeTypePool: 'Pula', + + // Original text: "Host" + homeTypeHost: 'Host', + + // Original text: "VM" + homeTypeVm: 'VM', + + // Original text: "SR" + homeTypeSr: 'SR', + + // Original text: "Template" + homeTypeVmTemplate: 'Szablon', + + // Original text: "Sort" + homeSort: 'Sortuj', + + // Original text: "Pools" + homeAllPools: 'Pule', + + // Original text: "Hosts" + homeAllHosts: 'Hosty', + + // Original text: "Tags" + homeAllTags: 'Tagi', + + // Original text: "New VM" + homeNewVm: 'Nowa VM', + + // Original text: "Running hosts" + homeFilterRunningHosts: 'Działające hosty', + + // Original text: "Disabled hosts" + homeFilterDisabledHosts: 'Wyłączone hosty', + + // Original text: "Running VMs" + homeFilterRunningVms: 'Działające VMs', + + // Original text: "Non running VMs" + homeFilterNonRunningVms: 'Niedziałające VMs', + + // Original text: "Pending VMs" + homeFilterPendingVms: 'Oczekujące VMs', + + // Original text: "HVM guests" + homeFilterHvmGuests: 'Gość HVM', + + // Original text: "Tags" + homeFilterTags: 'Tagi', + + // Original text: "Sort by" + homeSortBy: 'Sortuj po', + + // Original text: "Name" + homeSortByName: 'Nazwa', + + // Original text: "Power state" + homeSortByPowerstate: 'Stan zasilania', + + // Original text: "RAM" + homeSortByRAM: 'RAM', + + // Original text: "vCPUs" + homeSortByvCPUs: 'vCPUs', + + // Original text: "CPUs" + homeSortByCpus: 'CPUs', + + // Original text: "{displayed, number}x {icon} (on {total, number})" + homeDisplayedItems: '{displayed, number}x {icon} (w {total, number})', + + // Original text: "{selected, number}x {icon} selected (on {total, number})" + homeSelectedItems: + '{selected, number}x {icon} wybrane {selected, plural, one {} other {s}} (w {total, number})', + + // Original text: "More" + homeMore: 'Więcej', + + // Original text: "Migrate to…" + homeMigrateTo: 'Migruj do…', + + // Original text: "Missing patches" + homeMissingPaths: 'Brakujące łatki', + + // Original text: "Master:" + homePoolMaster: 'Master:', + + // Original text: "High Availability" + highAvailability: 'Wysoka dostępność', + + // Original text: "Add" + add: 'Dodaj', + + // Original text: "Remove" + remove: 'Usuń', + + // Original text: "Preview" + preview: 'Podgląd', + + // Original text: "Item" + item: 'Obiekt', + + // Original text: "No selected value" + noSelectedValue: 'Nie wybrano wartości ', + + // Original text: "Choose user(s) and/or group(s)" + selectSubjects: 'Wybierz użytkownika/użytkowników i/lub grupę/grupy', + + // Original text: "Select Object(s)…" + selectObjects: 'Wybierz obiekt/obiekty…', + + // Original text: "Choose a role" + selectRole: 'Wybierz rolę', + + // Original text: "Select Host(s)…" + selectHosts: 'Wybierz Host/Hosty…', + + // Original text: "Select object(s)…" + selectHostsVms: 'Wybierz obiekt/obiekty', + + // Original text: "Select Network(s)…" + selectNetworks: 'Wybierz sieć/sieci…', + + // Original text: "Select PIF(s)…" + selectPifs: 'Wybierz PIF(s)…', + + // Original text: "Select Pool(s)…" + selectPools: 'Wybierz pulę/pule…', + + // Original text: "Select Remote(s)…" + selectRemotes: 'Select Remote(s)…', + + // Original text: "Select resource set(s)…" + selectResourceSets: 'Wybierz pulę zasobów…', + + // Original text: "Select template(s)…" + selectResourceSetsVmTemplate: 'Wybierz szablon/szablony…', + + // Original text: "Select SR(s)…" + selectResourceSetsSr: 'Wybierz pulę dyskową', + + // Original text: "Select network(s)…" + selectResourceSetsNetwork: 'Wybierz sieć/sieci', + + // Original text: "Select disk(s)…" + selectResourceSetsVdi: 'Wybierz dysk/dyski…', + + // Original text: "Select SSH key(s)…" + selectSshKey: 'Wybierz klucz/klucze SSH…', + + // Original text: "Select SR(s)…" + selectSrs: 'Wybierz SR(s)…', + + // Original text: "Select VM(s)…" + selectVms: 'Wybierz VM(s)…', + + // Original text: "Select VM template(s)…" + selectVmTemplates: 'Wybierz szablon/szablony VM…', + + // Original text: "Select tag(s)…" + selectTags: 'Wybierz tag(i)…', + + // Original text: "Select disk(s)…" + selectVdis: 'Wybierz dysk/dyski…', + + // Original text: "Select timezone…" + selectTimezone: 'Wybierz strefę czasową…', + + // Original text: "Select IP(s)…" + selectIp: 'Wybierz IP(s)…', + + // Original text: "Select IP pool(s)…" + selectIpPool: 'Wybierz pulę/pule IP', + + // Original text: "Fill required informations." + fillRequiredInformations: 'Wypełnij brakujące informacje.', + + // Original text: "Fill informations (optional)" + fillOptionalInformations: 'Wypełnij informacje (opcjonalnie)', + + // Original text: "Reset" + selectTableReset: 'Reset', + + // Original text: "Month" + schedulingMonth: 'Miesiąc', + + // Original text: "Each selected month" + schedulingEachSelectedMonth: 'Każdy wybrany miesiąc', + + // Original text: "Day of the month" + schedulingMonthDay: 'Dzień z miesiąca', + + // Original text: "Each selected day" + schedulingEachSelectedMonthDay: 'Każdy wybrany dzień', + + // Original text: "Day of the week" + schedulingWeekDay: 'Dzień z tygodnia', + + // Original text: "Each selected day" + schedulingEachSelectedWeekDay: 'Każdy wybrany dzień', + + // Original text: "Hour" + schedulingHour: 'Godzina', + + // Original text: "Every N hour" + schedulingEveryNHour: 'Każda N godzina', + + // Original text: "Each selected hour" + schedulingEachSelectedHour: 'Każda wybrana godzina', + + // Original text: "Minute" + schedulingMinute: 'Minuta', + + // Original text: "Every N minute" + schedulingEveryNMinute: 'Każda N minuta', + + // Original text: "Each selected minute" + schedulingEachSelectedMinute: 'Każda wybrana minuta', + + // Original text: "Reset" + schedulingReset: 'Reset', + + // Original text: "Unknown" + unknownSchedule: 'Nieznany', + + // Original text: "Xo-server timezone:" + timezonePickerServerValue: 'Strefa czasowa Xo-server:', + + // Original text: "Web browser timezone" + timezonePickerUseLocalTime: 'Strefa czasowa przeglądarki internetowej', + + // Original text: "Xo-server timezone" + timezonePickerUseServerTime: 'Strefa czasowa Xo-server', + + // Original text: "Server timezone ({value})" + serverTimezoneOption: 'Strefa czasowa serwera({value})', + + // Original text: "Cron Pattern:" + cronPattern: 'Cron Pattern :', + + // Original text: "Cannot edit backup" + backupEditNotFoundTitle: 'Nie można edytować kopii zapasowej', + + // Original text: "Missing required info for edition" + backupEditNotFoundMessage: 'Brakuje wymaganych informacji do edycji', + + // Original text: "Job" + job: 'Job', + + // Original text: "Job ID" + jobId: 'Job ID', + + // Original text: "Name" + jobName: 'Nazwa', + + // Original text: "Name of your job (forbidden: \"_\")" + jobNamePlaceholder: 'Name of your job (forbidden: "_")', + + // Original text: "Start" + jobStart: 'Start', + + // Original text: "End" + jobEnd: 'Koniec', + + // Original text: "Duration" + jobDuration: 'Duration', + + // Original text: "Status" + jobStatus: 'Status', + + // Original text: "Action" + jobAction: 'Akcja', + + // Original text: "Tag" + jobTag: 'Tag', + + // Original text: "Scheduling" + jobScheduling: 'Planowanie', + + // Original text: "State" + jobState: 'Stan', + + // Original text: "Timezone" + jobTimezone: 'Strefa czasowa', + + // Original text: "xo-server" + jobServerTimezone: 'xo-server', + + // Original text: "Run job" + runJob: 'Uruchomione zadanie', + + // Original text: "One shot running started. See overview for logs." + runJobVerbose: 'One shot running started. See overview for logs.', + + // Original text: "Started" + jobStarted: 'Uruchomiono', + + // Original text: "Finished" + jobFinished: 'Zakończono', + + // Original text: "Save" + saveBackupJob: 'Zapisz', + + // Original text: "Remove backup job" + deleteBackupSchedule: 'Usuń zadanie kopii zapasowej', + + // Original text: "Are you sure you want to delete this backup job?" + deleteBackupScheduleQuestion: + 'Jesteś pewny że chcesz usunąć zadanie kopii zapasowej?', + + // Original text: "Enable immediately after creation" + scheduleEnableAfterCreation: 'Enable immediately after creation', + + // Original text: "You are editing Schedule {name} ({id}). Saving will override previous schedule state." + scheduleEditMessage: + 'Edytujesz harmonogram{name} ({id}). Zapisanie zastąpi poprzedni stan harmonogramu', + + // Original text: "You are editing job {name} ({id}). Saving will override previous job state." + jobEditMessage: + 'Edytujesz zadanie {name} ({id}). Zapisanie zastąpi poprzednie zadanie', + + // Original text: "No scheduled jobs." + noScheduledJobs: 'Brak zaplanowanych zadań', + + // Original text: "No jobs found." + noJobs: 'Brak zadań', + + // Original text: "No schedules found" + noSchedules: 'Brak harmonogramów', + + // Original text: "Select a xo-server API command" + jobActionPlaceHolder: 'Wybierz polecenie API dla xo-server', + + // Original text: "Schedules" + jobSchedules: 'Harmonogramy', + + // Original text: "Name of your schedule" + jobScheduleNamePlaceHolder: 'Nazwa twojego harmonogramu', + + // Original text: "Select a Job" + jobScheduleJobPlaceHolder: 'Wybierz zadanie', + + // Original text: "Select your backup type:" + newBackupSelection: 'Wybierz typ kopii zapasowej :', + + // Original text: "Select backup mode:" + smartBackupModeSelection: 'Wybierz tryb kopii zapasowej :', + + // Original text: "Normal backup" + normalBackup: 'Normalna kopia zapasowa', + + // Original text: "Smart backup" + smartBackup: 'Inteligentna kopia zapsowa', + + // Original text: "Local remote selected" + localRemoteWarningTitle: 'Local remote selected', + + // Original text: "Warning: local remotes will use limited XOA disk space. Only for advanced users." + localRemoteWarningMessage: + 'Warning: local remotes will use limited XOA disk space. Only for advanced users.', + + // Original text: "VMs" + editBackupVmsTitle: 'VMs', + + // Original text: "VMs statuses" + editBackupSmartStatusTitle: 'Statusy VMs', + + // Original text: "Resident on" + editBackupSmartResidentOn: 'Resident on', + + // Original text: "VMs Tags" + editBackupSmartTagsTitle: 'Tagi VMs', + + // Original text: "Tag" + editBackupTagTitle: 'Tag', + + // Original text: "Report" + editBackupReportTitle: 'Raport', + + // Original text: "Enable immediately after creation" + editBackupScheduleEnabled: 'Uruchom natychamiast po utworzeniu', + + // Original text: "Depth" + editBackupDepthTitle: 'Depth', + + // Original text: "Remote" + editBackupRemoteTitle: 'Zdalny', + + // Original text: "Remote stores for backup" + remoteList: 'Zdalne przechowywanie kopii zapasowej', + + // Original text: "New File System Remote" + newRemote: 'Nowy zdalny system plików', + + // Original text: "Local" + remoteTypeLocal: 'Lokalny', + + // Original text: "NFS" + remoteTypeNfs: 'NFS', + + // Original text: "SMB" + remoteTypeSmb: 'SMB', + + // Original text: "Type" + remoteType: 'Typ', + + // Original text: "Test your remote" + remoteTestTip: 'Przetestuj swoje zdalne połączenie', + + // Original text: "Test Remote" + testRemote: 'Test Remote', + + // Original text: "Test failed for {name}" + remoteTestFailure: 'Test zakończony niepowodzeniem dla {name}', + + // Original text: "Test passed for {name}" + remoteTestSuccess: 'Test zakończony sukcesem {name}', + + // Original text: "Error" + remoteTestError: 'Błąd', + + // Original text: "Test Step" + remoteTestStep: 'Test Step', + + // Original text: "Test file" + remoteTestFile: 'Testowy plik', + + // Original text: "The remote appears to work correctly" + remoteTestSuccessMessage: 'The remote appears to work correctly', + + // Original text: "Name" + remoteName: 'Name', + + // Original text: "Path" + remotePath: 'Scieżka', + + // Original text: "State" + remoteState: 'Stan', + + // Original text: "Device" + remoteDevice: 'Urządzenie', + + // Original text: "Share" + remoteShare: 'Udostępnij', + + // Original text: "Auth" + remoteAuth: 'Auth', + + // Original text: "Mounted" + remoteMounted: 'Zamontowane', + + // Original text: "Unmounted" + remoteUnmounted: 'Odmontowane', + + // Original text: "Connect" + remoteConnectTip: 'Połącz', + + // Original text: "Disconnect" + remoteDisconnectTip: 'Rozłącz', + + // Original text: "Delete" + remoteDeleteTip: 'Usuń', + + // Original text: "remote name *" + remoteNamePlaceHolder: 'Nazwa zdalna*', + + // Original text: "Name *" + remoteMyNamePlaceHolder: 'Nazwa *', + + // Original text: "/path/to/backup" + remoteLocalPlaceHolderPath: '/ścieżka/do/kopii/zapasowej', + + // Original text: "host *" + remoteNfsPlaceHolderHost: 'Host *', + + // Original text: "/path/to/backup" + remoteNfsPlaceHolderPath: '/ścieżka/do/kopii/zapasowej', + + // Original text: "subfolder [path\\to\\backup]" + remoteSmbPlaceHolderRemotePath: 'podfolder [ścieżka\\do\\kopii\\zapasowej]', + + // Original text: "Username" + remoteSmbPlaceHolderUsername: 'Nazwa użytkownika', + + // Original text: "Password" + remoteSmbPlaceHolderPassword: 'Hasło', + + // Original text: "Domain" + remoteSmbPlaceHolderDomain: 'Domena', + + // Original text: "
\\ *" + remoteSmbPlaceHolderAddressShare: '\\ *', + + // Original text: "password(fill to edit)" + remotePlaceHolderPassword: 'Hasło (wypełnij)', + + // Original text: "Create a new SR" + newSrTitle: 'Stwórz nowy SR', + + // Original text: "General" + newSrGeneral: 'General', + + // Original text: "Select Storage Type:" + newSrTypeSelection: 'Wybierz typ puli dyskowej:', + + // Original text: "Settings" + newSrSettings: 'Ustawienia', + + // Original text: "Storage Usage" + newSrUsage: 'Użycie dysków', + + // Original text: "Summary" + newSrSummary: 'Podsumowanie', + + // Original text: "Host" + newSrHost: 'Host', + + // Original text: "Type" + newSrType: 'Typ', + + // Original text: "Name" + newSrName: 'Nazwa', + + // Original text: "Description" + newSrDescription: 'Opis', + + // Original text: "Server" + newSrServer: 'Serwer', + + // Original text: "Path" + newSrPath: 'Ścieżka', + + // Original text: "IQN" + newSrIqn: 'IQN', + + // Original text: "LUN" + newSrLun: 'LUN', + + // Original text: "with auth." + newSrAuth: 'z autoryzacją', + + // Original text: "User Name" + newSrUsername: 'Nazwa użytkownika', + + // Original text: "Password" + newSrPassword: 'Hasło', + + // Original text: "Device" + newSrDevice: 'Urządzenie', + + // Original text: "in use" + newSrInUse: 'Używane', + + // Original text: "Size" + newSrSize: 'Rozmiar', + + // Original text: "Create" + newSrCreate: 'Utwórz', + + // Original text: "Storage name" + newSrNamePlaceHolder: 'Nazwa puli dyskowej', + + // Original text: "Storage description" + newSrDescPlaceHolder: 'Opis puli dyskowej', + + // Original text: "Address" + newSrAddressPlaceHolder: 'Adres', + + // Original text: "[port]" + newSrPortPlaceHolder: '[port]', + + // Original text: "Username" + newSrUsernamePlaceHolder: 'Nazwa użytkownika', + + // Original text: "Password" + newSrPasswordPlaceHolder: 'Hasło', + + // Original text: "Device, e.g /dev/sda…" + newSrLvmDevicePlaceHolder: 'Urządzenie, np. /dev/sda…', + + // Original text: "/path/to/directory" + newSrLocalPathPlaceHolder: '/ścieżka/do/katalogu', + + // Original text: "Users/Groups" + subjectName: 'Użytkownicy/Grupy', + + // Original text: "Object" + objectName: 'Obiekt', + + // Original text: "No acls found" + aclNoneFound: 'Nie znaleziono ACLs', + + // Original text: "Role" + roleName: 'Rola', + + // Original text: "Create" + aclCreate: 'Utwórz', + + // Original text: "New Group Name" + newGroupName: 'Nazwa nowej grupy', + + // Original text: "Create Group" + createGroup: 'Utwórz grupę', + + // Original text: "Create" + createGroupButton: 'Utwórz', + + // Original text: "Delete Group" + deleteGroup: 'Usuń grupę', + + // Original text: "Are you sure you want to delete this group?" + deleteGroupConfirm: 'Jesteś pewny że chcesz usunąć te grupę?', + + // Original text: "Remove user from Group" + removeUserFromGroup: 'Usuń użytkownika z grupy', + + // Original text: "Are you sure you want to delete this user?" + deleteUserConfirm: 'Jesteś pewny że chcesz usunąć tego użytkownika?', + + // Original text: "Delete User" + deleteUser: 'Usuń użytkownika', + + // Original text: "no user" + noUser: 'Brak użytkownika', + + // Original text: "unknown user" + unknownUser: 'Nieznany użytkownik', + + // Original text: "No group found" + noGroupFound: 'Nie znaleziono grupy', + + // Original text: "Name" + groupNameColumn: 'Nazwa', + + // Original text: "Users" + groupUsersColumn: 'Użytkownicy', + + // Original text: "Add User" + addUserToGroupColumn: 'Dodaj użytkownika', + + // Original text: "Email" + userNameColumn: 'Email', + + // Original text: "Permissions" + userPermissionColumn: 'Uprawnienia', + + // Original text: "Password" + userPasswordColumn: 'Hasło', + + // Original text: "Email" + userName: 'Email', + + // Original text: "Password" + userPassword: 'Hasło', + + // Original text: "Create" + createUserButton: 'Utwórz', + + // Original text: "No user found" + noUserFound: 'Nie znaleziono użytkownika', + + // Original text: "User" + userLabel: 'Użytkownik', + + // Original text: "Admin" + adminLabel: 'Administrator', + + // Original text: "No user in group" + noUserInGroup: 'Brak użytkownika w grupie', + + // Original text: "{users} user{users, plural, one {} other {s}}" + countUsers: '{users} użytkownik{users, plural, one {} other {s}}', + + // Original text: "Select Permission" + selectPermission: 'Wybierz uprawnienia', + + // Original text: "Auto-load at server start" + autoloadPlugin: 'Ładuj automatycznie gdy serwer startuje', + + // Original text: "Save configuration" + savePluginConfiguration: 'Zapisz konfigurację', + + // Original text: "Delete configuration" + deletePluginConfiguration: 'Usuń konfigurację', + + // Original text: "Plugin error" + pluginError: 'Błąd dodatku', + + // Original text: "Unknown error" + unknownPluginError: 'Nieznany błąd', + + // Original text: "Purge plugin configuration" + purgePluginConfiguration: 'Purge plugin configuration', + + // Original text: "Are you sure you want to purge this configuration ?" + purgePluginConfigurationQuestion: 'Czy napewno chcesz usunąć te konfigurację', + + // Original text: "Edit" + editPluginConfiguration: 'Edytuj', + + // Original text: "Cancel" + cancelPluginEdition: 'Anuluj', + + // Original text: "Plugin configuration" + pluginConfigurationSuccess: 'Konfiguracja dodatków', + + // Original text: "Plugin configuration successfully saved!" + pluginConfigurationChanges: 'Konfiguracja dodatków została zapisana !', + + // Original text: "Predefined configuration" + pluginConfigurationPresetTitle: 'Wstępnie zdefiniowana konfiguracja', + + // Original text: "Choose a predefined configuration." + pluginConfigurationChoosePreset: + 'Wybierz wstępnie zdefiniowaną konfigurację.', + + // Original text: "Apply" + applyPluginPreset: 'Akceptuj', + + // Original text: "Save filter error" + saveNewUserFilterErrorTitle: 'Zapisz błąd filtra', + + // Original text: "Bad parameter: name must be given." + saveNewUserFilterErrorBody: 'Zły parametr: nazwa musi byc nadana', + + // Original text: "Name:" + filterName: 'Nazwa:', + + // Original text: "Value:" + filterValue: 'Wartość :', + + // Original text: "Save new filter" + saveNewFilterTitle: 'Zapisz nowy filtr', + + // Original text: "Set custom filters" + setUserFiltersTitle: 'Ustaw niestandardowe filtry', + + // Original text: "Are you sure you want to set custom filters?" + setUserFiltersBody: 'Jesteś pewny że chcesz ustawić niestandardowe filtry?', + + // Original text: "Remove custom filter" + removeUserFilterTitle: 'Usuń niestandardowe filtry', + + // Original text: "Are you sure you want to remove custom filter?" + removeUserFilterBody: 'Jesteś pewny że chcesz usunąć niestandardowe filtry?', + + // Original text: "Default filter" + defaultFilter: 'Filtr domyślny', + + // Original text: "Default filters" + defaultFilters: 'Domyślne filtry', + + // Original text: "Custom filters" + customFilters: 'Filtry niestandardowe', + + // Original text: "Customize filters" + customizeFilters: 'Dostosuj filtry', + + // Original text: "Save custom filters" + saveCustomFilters: 'Zapisz filtry niestandardowe', + + // Original text: "Start" + startVmLabel: 'Start', + + // Original text: "Recovery start" + recoveryModeLabel: 'Rozpocznij odzyskiwanie', + + // Original text: "Suspend" + suspendVmLabel: 'Uśpij', + + // Original text: "Stop" + stopVmLabel: 'Stop', + + // Original text: "Force shutdown" + forceShutdownVmLabel: 'Brtualne wyłączenie', + + // Original text: "Reboot" + rebootVmLabel: 'Reboot', + + // Original text: "Force reboot" + forceRebootVmLabel: 'Brutalny reboot', + + // Original text: "Delete" + deleteVmLabel: 'Usuń', + + // Original text: "Migrate" + migrateVmLabel: 'Migruj', + + // Original text: "Snapshot" + snapshotVmLabel: 'Snapshot', + + // Original text: "Export" + exportVmLabel: 'Eksportuj', + + // Original text: "Resume" + resumeVmLabel: 'Wzów', + + // Original text: "Copy" + copyVmLabel: 'Kopia', + + // Original text: "Clone" + cloneVmLabel: 'Klonuj', + + // Original text: "Fast clone" + fastCloneVmLabel: 'Szybki klon', + + // Original text: "Convert to template" + convertVmToTemplateLabel: 'Konwertuj do szablonu', + + // Original text: "Console" + vmConsoleLabel: 'Konsola', + + // Original text: "Rescan all disks" + srRescan: 'Skanuj wszystkie dyski', + + // Original text: "Connect to all hosts" + srReconnectAll: 'Połącz z wszystkimi hostami', + + // Original text: "Disconnect to all hosts" + srDisconnectAll: 'Rozłącz z wszystkimi hostami', + + // Original text: "Forget this SR" + srForget: 'Zapomnij te pulę dyskową', + + // Original text: "Remove this SR" + srRemoveButton: 'Usuń te pulę dyskową', + + // Original text: "No VDIs in this storage" + srNoVdis: 'Brak VDIs na tej przestrzeni dyskowej', + + // Original text: "Pool RAM usage:" + poolTitleRamUsage: 'Użycie puli RAM:', + + // Original text: "{used} used on {total}" + poolRamUsage: '{used} używane w {total}', + + // Original text: "Master:" + poolMaster: 'Master :', + + // Original text: "Hosts" + hostsTabName: 'Hosty', + + // Original text: "High Availability" + poolHaStatus: 'Wysoka dostępność', + + // Original text: "Enabled" + poolHaEnabled: 'Włączone', + + // Original text: "Disabled" + poolHaDisabled: 'Wyłączone', + + // Original text: "Name" + hostNameLabel: 'Nazwa', + + // Original text: "Description" + hostDescription: 'Opis', + + // Original text: "Memory" + hostMemory: 'Pamieć', + + // Original text: "No hosts" + noHost: 'Brak hostów', + + // Original text: "{used}% used ({free} free)" + memoryLeftTooltip: '{used}% używane ({free} libre)', + + // Original text: "Name" + poolNetworkNameLabel: 'Nazwa', + + // Original text: "Description" + poolNetworkDescription: 'Opis', + + // Original text: "PIFs" + poolNetworkPif: 'PIFs', + + // Original text: "No networks" + poolNoNetwork: 'Brak sieci', + + // Original text: "MTU" + poolNetworkMTU: 'MTU', + + // Original text: "Connected" + poolNetworkPifAttached: 'Połączone', + + // Original text: "Disconnected" + poolNetworkPifDetached: 'Rozłączone', + + // Original text: "Show PIFs" + showPifs: 'Pokaż PIFs', + + // Original text: "Hide PIFs" + hidePifs: 'Ukryj PIFs', + + // Original text: "Add SR" + addSrLabel: 'Dodaj przestrzeń dyskową', + + // Original text: "Add VM" + addVmLabel: 'Dodaj VM', + + // Original text: "Add Host" + addHostLabel: 'Dodaj hosta', + + // Original text: "Disconnect" + disconnectServer: 'Rozłącz', + + // Original text: "Start" + startHostLabel: 'Start', + + // Original text: "Stop" + stopHostLabel: 'Stop', + + // Original text: "Enable" + enableHostLabel: 'Włącz', + + // Original text: "Disable" + disableHostLabel: 'Wyłącz', + + // Original text: "Restart toolstack" + restartHostAgent: 'Restart toolstack', + + // Original text: "Force reboot" + forceRebootHostLabel: 'Brutalny reboot', + + // Original text: "Reboot" + rebootHostLabel: 'Reboot', + + // Original text: "Reboot to apply updates" + rebootUpdateHostLabel: 'Reboot żeby zastosować aktualizacje', + + // Original text: "Emergency mode" + emergencyModeLabel: 'Tryb ratunku', + + // Original text: "Storage" + storageTabName: 'Przestrzeń dyskowa', + + // Original text: "Patches" + patchesTabName: 'Patches', + + // Original text: "Load average" + statLoad: 'Średni load :', + + // Original text: "Hardware" + hardwareHostSettingsLabel: 'Sprzęt', + + // Original text: "Address" + hostAddress: 'Adres', + + // Original text: "Status" + hostStatus: 'Status', + + // Original text: "Build number" + hostBuildNumber: 'Build number', + + // Original text: "iSCSI name" + hostIscsiName: 'Nazwa iSCSI', + + // Original text: "Version" + hostXenServerVersion: 'Wersja', + + // Original text: "Enabled" + hostStatusEnabled: 'Włącz', + + // Original text: "Disabled" + hostStatusDisabled: 'Wyłącz', + + // Original text: "Power on mode" + hostPowerOnMode: 'Tryb włączenia', + + // Original text: "Host uptime" + hostStartedSince: 'Nieprzerwany czas działania hosta', + + // Original text: "Toolstack uptime" + hostStackStartedSince: 'Toolstack uptime', + + // Original text: "CPU model" + hostCpusModel: 'Model CPU', + + // Original text: "Core (socket)" + hostCpusNumber: 'Core (socket)', + + // Original text: "Manufacturer info" + hostManufacturerinfo: 'Informacje o producencie', + + // Original text: "BIOS info" + hostBiosinfo: 'BIOS informacje', + + // Original text: "Licence" + licenseHostSettingsLabel: 'Licencja', + + // Original text: "Type" + hostLicenseType: 'Typ', + + // Original text: "Socket" + hostLicenseSocket: 'Gniazdo', + + // Original text: "Expiry" + hostLicenseExpiry: 'Wygasa', + + // Original text: "Add a network" + networkCreateButton: 'Dodaj sieć', + + // Original text: "Add a bonded network" + networkCreateBondedButton: 'Dodaj bonding dla sieci', + + // Original text: "Device" + pifDeviceLabel: 'Urządzenie', + + // Original text: "Network" + pifNetworkLabel: 'Sieć', + + // Original text: "VLAN" + pifVlanLabel: 'VLAN', + + // Original text: "Address" + pifAddressLabel: 'Adres', + + // Original text: 'Mode' + pifModeLabel: 'Tryb', + + // Original text: "MAC" + pifMacLabel: 'MAC', + + // Original text: "MTU" + pifMtuLabel: 'MTU', + + // Original text: "Status" + pifStatusLabel: 'Status', + + // Original text: "Connected" + pifStatusConnected: 'Połączono', + + // Original text: "Disconnected" + pifStatusDisconnected: 'Rozłączono', + + // Original text: "No physical interface detected" + pifNoInterface: 'Brak fizycznych interfejsów', + + // Original text: "This interface is currently in use" + pifInUse: 'Ten interfejs jest obecnie używany', + + // Original text: "Default locking mode" + defaultLockingMode: 'Domyślny tryb blokowania', + + // Original text: 'Configure IP address' + pifConfigureIp: undefined, + + // Original text: 'Invalid parameters' + configIpErrorTitle: undefined, + + // Original text: 'IP address and netmask required' + configIpErrorMessage: undefined, + + // Original text: 'Static IP address' + staticIp: undefined, + + // Original text: 'Netmask' + netmask: undefined, + + // Original text: 'DNS' + dns: undefined, + + // Original text: 'Gateway' + gateway: undefined, + + // Original text: "Add a storage" + addSrDeviceButton: 'Dodaj przestrzeń dyskową', + + // Original text: "Name" + srNameLabel: 'Nazwa', + + // Original text: "Type" + srType: 'Typ', + + // Original text: "Status" + pbdStatus: 'Status', + + // Original text: "Connected" + pbdStatusConnected: 'Połączone', + + // Original text: "Disconnected" + pbdStatusDisconnected: 'Rozłączone', + + // Original text: "Connect" + pbdConnect: 'Połącz', + + // Original text: "Disconnect" + pbdDisconnect: 'Rozłącz', + + // Original text: "Forget" + pbdForget: 'Zapomnij', + + // Original text: "Shared" + srShared: 'Udostępnione', + + // Original text: "Not shared" + srNotShared: 'Nieudostępnione', + + // Original text: "No storage detected" + pbdNoSr: 'Nie wykryto macierzy dyskowej', + + // Original text: "Name" + patchNameLabel: 'Nazwa', + + // Original text: "Install all patches" + patchUpdateButton: 'Instaluj wszystkie łatki', + + // Original text: "Description" + patchDescription: 'Opis', + + // Original text: "Applied date" + patchApplied: 'Applied date', + + // Original text: "Size" + patchSize: 'Rozmiar', + + // Original text: "Status" + patchStatus: 'Status', + + // Original text: "Applied" + patchStatusApplied: 'Applied', + + // Original text: "Missing patches" + patchStatusNotApplied: 'Brakujące łatki', + + // Original text: "No patch detected" + patchNothing: 'Nie wykryto łatek', + + // Original text: "Release date" + patchReleaseDate: 'Data wydania', + + // Original text: "Guidance" + patchGuidance: 'Guidance', + + // Original text: "Action" + patchAction: 'Akcja', + + // Original text: "Applied patches" + hostAppliedPatches: 'Łatki zostały zastosowane', + + // Original text: "Missing patches" + hostMissingPatches: 'Brakujące łatki', + + // Original text: "Host up-to-date!" + hostUpToDate: 'Host posiada najnowsze łatki!', + + // Original text: "Refresh patches" + refreshPatches: 'Odśwież łatki', + + // Original text: "Install pool patches" + installPoolPatches: 'Instaluj pulę łatek', + + // Original text: "Default SR" + defaultSr: 'Domyślny SR', + + // Original text: "Set as default SR" + setAsDefaultSr: 'Ustaw domyślny SR', + + // Original text: "General" + generalTabName: 'General', + + // Original text: "Stats" + statsTabName: 'Statystyki', + + // Original text: "Console" + consoleTabName: 'Konsola', + + // Original text: "Container" + containersTabName: 'Kontener', + + // Original text: "Snapshots" + snapshotsTabName: 'Snapshoty', + + // Original text: "Logs" + logsTabName: 'Logi', + + // Original text: "Advanced" + advancedTabName: 'Zaawansowane', + + // Original text: "Network" + networkTabName: 'Sieć', + + // Original text: "Disk{disks, plural, one {} other {s}}" + disksTabName: 'Dysk{disks, plural, one {} other {s}}', + + // Original text: "halted" + powerStateHalted: 'Zatrzymany', + + // Original text: "running" + powerStateRunning: 'Działający', + + // Original text: "suspended" + powerStateSuspended: 'Uśpiony', + + // Original text: "No Xen tools detected" + vmStatus: 'Nie wykryto Xen tools', + + // Original text: "No IPv4 record" + vmName: 'Nie zapisano IPv4', + + // Original text: "No IP record" + vmDescription: 'Nie zapisano IP', + + // Original text: "Started {ago}" + vmSettings: 'Uruchomiono {ago}', + + // Original text: "Current status:" + vmCurrentStatus: 'Obecny status:', + + // Original text: "Not running" + vmNotRunning: 'Nie uruchomione', + + // Original text: "No Xen tools detected" + noToolsDetected: 'Nie wykryto Xen tools', + + // Original text: "No IPv4 record" + noIpv4Record: 'Nie zapisano IPv4', + + // Original text: "No IP record" + noIpRecord: 'Nie zapisano IP', + + // Original text: "Started {ago}" + started: 'Uruchomiono {ago}', + + // Original text: "Paravirtualization (PV)" + paraVirtualizedMode: 'Parawirtualizacja (PV)', + + // Original text: "Hardware virtualization (HVM)" + hardwareVirtualizedMode: 'Wirtualizacja sprzętowa (HVM)', + + // Original text: "CPU usage" + statsCpu: 'Użycie CPU', + + // Original text: "Memory usage" + statsMemory: 'Użycie pamięci', + + // Original text: "Network throughput" + statsNetwork: 'Wydajność sieci', + + // Original text: "Stacked values" + useStackedValuesOnStats: 'Skumulowane wartości', + + // Original text: "Disk throughput" + statDisk: 'Wydajność dysku', + + // Original text: "Last 10 minutes" + statLastTenMinutes: 'Ostatnie 10 minut', + + // Original text: "Last 2 hours" + statLastTwoHours: 'Ostatnie 2 godziny', + + // Original text: "Last week" + statLastWeek: 'Ostatni tydzień', + + // Original text: "Last year" + statLastYear: 'Ostatni rok', + + // Original text: "Copy" + copyToClipboardLabel: 'Kopia', + + // Original text: "Ctrl+Alt+Del" + ctrlAltDelButtonLabel: 'Ctrl+Alt+Del', + + // Original text: "Tip:" + tipLabel: 'Wskazówka:', + + // Original text: "non-US keyboard could have issues with console: switch your own layout to US." + tipConsoleLabel: + 'non-US keyboard could have issues with console: switch your own layout to US.', + + // Original text: "Hide infos" + hideHeaderTooltip: 'Ukryj informacje', + + // Original text: "Show infos" + showHeaderTooltip: 'Pokaż informacje', + + // Original text: "Name" + containerName: 'Nazwa', + + // Original text: "Command" + containerCommand: 'Komenda', + + // Original text: "Creation date" + containerCreated: 'Data utworzenia', + + // Original text: "Status" + containerStatus: 'Status', + + // Original text: "Action" + containerAction: 'Akcja', + + // Original text: "No existing containers" + noContainers: 'Brak istniejących kontenerów', + + // Original text: "Stop this container" + containerStop: 'Wyłącz ten kontener', + + // Original text: "Start this container" + containerStart: 'Uruchom ten kontener', + + // Original text: "Pause this container" + containerPause: 'Zatrzymaj ten kontener', + + // Original text: "Resume this container" + containerResume: 'Wznów ten kontener', + + // Original text: "Restart this container" + containerRestart: 'Uruchom ponownie ten kontener', + + // Original text: "Action" + vdiAction: 'Akcja', + + // Original text: "Attach disk" + vdiAttachDeviceButton: 'Dołącz dysk', + + // Original text: "New disk" + vbdCreateDeviceButton: 'Nowy dysk', + + // Original text: "Boot order" + vdiBootOrder: 'Kolejność bootowania', + + // Original text: "Name" + vdiNameLabel: 'Nazwa', + + // Original text: "Description" + vdiNameDescription: 'Opis', + + // Original text: "Tags" + vdiTags: 'Tagi', + + // Original text: "Size" + vdiSize: 'Rozmiar', + + // Original text: "SR" + vdiSr: 'SR', + + // Original text: "VM" + vdiVm: 'VM', + + // Original text: "Migrate VDI" + vdiMigrate: 'Migruj VDI', + + // Original text: "Destination SR:" + vdiMigrateSelectSr: 'Destination SR:', + + // Original text: "Migrate all VDIs" + vdiMigrateAll: 'Migruj wszystkie VDIs', + + // Original text: "No SR" + vdiMigrateNoSr: 'No SR', + + // Original text: "A target SR is required to migrate a VDI" + vdiMigrateNoSrMessage: 'Docelowy SR jest wymagany żeby zmigrować VDI', + + // Original text: "Forget" + vdiForget: 'Zapomnij', + + // Original text: "Remove VDI" + vdiRemove: 'Usuń VDI', + + // Original text: "Boot flag" + vbdBootableStatus: 'Boot flag', + + // Original text: "Status" + vbdStatus: 'Status', + + // Original text: "Connected" + vbdStatusConnected: 'Połączono', + + // Original text: "Disconnected" + vbdStatusDisconnected: 'Rozłączono', + + // Original text: "No disks" + vbdNoVbd: 'Brak dysków', + + // Original text: "Connect VBD" + vbdConnect: 'Połącz z VBD', + + // Original text: "Disconnect VBD" + vbdDisconnect: 'Rozłącz z VBD', + + // Original text: "Bootable" + vbdBootable: 'Bootable', + + // Original text: "Readonly" + vbdReadonly: 'Tylko do odcztu', + + // Original text: "Create" + vbdCreate: 'Utwórz', + + // Original text: "Disk name" + vbdNamePlaceHolder: 'Nazwa dysku', + + // Original text: "Size" + vbdSizePlaceHolder: 'Rozmiar', + + // Original text: "Save" + saveBootOption: 'Zapisz', + + // Original text: "Reset" + resetBootOption: 'Reset', + + // Original text: "New device" + vifCreateDeviceButton: 'Nowe urządzenie', + + // Original text: "No interface" + vifNoInterface: 'Brak interfejsu', + + // Original text: "Device" + vifDeviceLabel: 'Urządzenie', + + // Original text: "MAC address" + vifMacLabel: 'Adres MAC', + + // Original text: "MTU" + vifMtuLabel: 'MTU', + + // Original text: "Network" + vifNetworkLabel: 'Sieć', + + // Original text: "Status" + vifStatusLabel: 'Status', + + // Original text: "Connected" + vifStatusConnected: 'Połączono', + + // Original text: "Disconnected" + vifStatusDisconnected: 'Rozłączono', + + // Original text: "Connect" + vifConnect: 'Połącz', + + // Original text: "Disconnect" + vifDisconnect: 'Rozłącz', + + // Original text: "Remove" + vifRemove: 'Usuń', + + // Original text: "IP addresses" + vifIpAddresses: 'Adres IP', + + // Original text: "Auto-generated if empty" + vifMacAutoGenerate: 'Auto-generated if empty', + + // Original text: "Allowed IPs" + vifAllowedIps: 'Dopuszczone IPs', + + // Original text: "No IPs" + vifNoIps: 'Brak IPs', + + // Original text: "Network locked" + vifLockedNetwork: 'Sieć zablokowana', + + // Original text: "Network locked and no IPs are allowed for this interface" + vifLockedNetworkNoIps: + 'Sieć zablokowana i żadne IPs nie są dopuszczone do tego interfejsu', + + // Original text: "Network not locked" + vifUnLockedNetwork: 'Sieć niezablokowana', + + // Original text: "Unknown network" + vifUnknownNetwork: 'Sieć nieznana', + + // Original text: "Create" + vifCreate: 'Utwórz', + + // Original text: "No snapshots" + noSnapshots: 'Brak snapshotów', + + // Original text: "New snapshot" + snapshotCreateButton: 'Nowy snapshot', + + // Original text: "Just click on the snapshot button to create one!" + tipCreateSnapshotLabel: 'Kliknij na guzik żeby stworzyć snapshota !', + + // Original text: "Revert VM to this snapshot" + revertSnapshot: 'Przywróć VM do tego snapshota', + + // Original text: "Remove this snapshot" + deleteSnapshot: 'Usuń snapshot', + + // Original text: "Create a VM from this snapshot" + copySnapshot: 'Utwórz VM z tego snapshota', + + // Original text: "Export this snapshot" + exportSnapshot: 'Exportuj tego snapshota', + + // Original text: "Creation date" + snapshotDate: 'Data utworzenia', + + // Original text: "Name" + snapshotName: 'Nazwa', + + // Original text: "Action" + snapshotAction: 'Akcja', + + // Original text: "Remove all logs" + logRemoveAll: 'Usuń wszystkie logi', + + // Original text: "No logs so far" + noLogs: 'No logs so far', + + // Original text: "Creation date" + logDate: 'Data utworzenia', + + // Original text: "Name" + logName: 'Nazwa', + + // Original text: "Content" + logContent: 'Zawartość', + + // Original text: "Action" + logAction: 'Akcja', + + // Original text: "Remove" + vmRemoveButton: 'Usuń', + + // Original text: "Convert" + vmConvertButton: 'Konwertuj', + + // Original text: "Xen settings" + xenSettingsLabel: 'Ustawienia Xena', + + // Original text: "Guest OS" + guestOsLabel: 'Geust OS', + + // Original text: "Misc" + miscLabel: 'Misc', + + // Original text: "UUID" + uuid: 'UUID', + + // Original text: "Virtualization mode" + virtualizationMode: 'Tryb wirtualizacji', + + // Original text: "CPU weight" + cpuWeightLabel: 'CPU weight', + + // Original text: "Default ({value, number})" + defaultCpuWeight: 'Domyślnie ({value, number})', + + // Original text: "CPU cap" + cpuCapLabel: 'CPU cap', + + // Original text: "Default ({value, number})" + defaultCpuCap: 'Domyślny ({value, number})', + + // Original text: "PV args" + pvArgsLabel: 'PV args', + + // Original text: "Xen tools status" + xenToolsStatus: 'Status Xen tools', + + // Original text: '{status}' + xenToolsStatusValue: undefined, + + // Original text: "OS name" + osName: 'Nazwa systemu', + + // Original text: "OS kernel" + osKernel: 'Kernel systemu', + + // Original text: "Auto power on" + autoPowerOn: 'Autoamtyczne uruchamianie', + + // Original text: "HA" + ha: 'HA', + + // Original text: "Original template" + originalTemplate: 'Oryginalny szablon', + + // Original text: "Unknown" + unknownOsName: 'Nieznany', + + // Original text: "Unknown" + unknownOsKernel: 'Nieznany', + + // Original text: "Unknown" + unknownOriginalTemplate: 'Nieznany', + + // Original text: "VM limits" + vmLimitsLabel: 'Limity VM', + + // Original text: "CPU limits" + vmCpuLimitsLabel: 'Limity CPU', + + // Original text: "Memory limits (min/max)" + vmMemoryLimitsLabel: 'Limity pamięci (min/max)', + + // Original text: "vCPUs max:" + vmMaxVcpus: 'vCPUs max :', + + // Original text: "Memory max:" + vmMaxRam: 'Pamięć max :', + + // Original text: "Long click to add a name" + vmHomeNamePlaceholder: 'Long click to add a name', + + // Original text: "Long click to add a description" + vmHomeDescriptionPlaceholder: 'Long click to add a description', + + // Original text: "Click to add a name" + vmViewNamePlaceholder: 'Click to add a name', + + // Original text: "Click to add a description" + vmViewDescriptionPlaceholder: 'Kliknij żeby dodać opis', + + // Original text: "Click to add a name" + templateHomeNamePlaceholder: 'Kliknij żeby dodać nazwę', + + // Original text: "Click to add a description" + templateHomeDescriptionPlaceholder: 'Kliknij żeby dodać opis', + + // Original text: "Delete template" + templateDelete: 'Usuń szablon', + + // Original text: "Delete VM template{templates, plural, one {} other {s}}" + templateDeleteModalTitle: + 'Usuń szablon VM{templates, plural, one {} other {s}} de VMs', + + // Original text: "Are you sure you want to delete {templates, plural, one {this} other {these}} template{templates, plural, one {} other {s}}?" + templateDeleteModalBody: 'Jesteś pewien że chcesz usunąć?', + + // Original text: "Pool{pools, plural, one {} other {s}}" + poolPanel: 'Pula{pools, plural, one {} other {s}}', + + // Original text: "Host{hosts, plural, one {} other {s}}" + hostPanel: 'Host{hosts, plural, one {} other {s}}', + + // Original text: "VM{vms, plural, one {} other {s}}" + vmPanel: 'VM{vms, plural, one {} other {s}}', + + // Original text: "RAM Usage" + memoryStatePanel: 'Użycie RAM', + + // Original text: "CPUs Usage" + cpuStatePanel: 'Użycie CPUs', + + // Original text: "VMs Power state" + vmStatePanel: 'Stan zasilania VMs', + + // Original text: "Pending tasks" + taskStatePanel: 'Oczekujące zadania', + + // Original text: "Users" + usersStatePanel: 'Użytkownicy', + + // Original text: "Storage state" + srStatePanel: 'Status przestrzeni dyskowej', + + // Original text: "{usage} (of {total})" + ofUsage: '{usage} (sur {total})', + + // Original text: "No storage" + noSrs: 'No storage', + + // Original text: "Name" + srName: 'Nazwa', + + // Original text: "Pool" + srPool: 'Pula', + + // Original text: "Host" + srHost: 'Host', + + // Original text: "Type" + srFormat: 'Typ', + + // Original text: "Size" + srSize: 'Size', + + // Original text: "Usage" + srUsage: 'Usage', + + // Original text: "used" + srUsed: 'Used', + + // Original text: "free" + srFree: 'free', + + // Original text: "Storage Usage" + srUsageStatePanel: 'Storage Usage', + + // Original text: "Top 5 SR Usage (in %)" + srTopUsageStatePanel: 'Top 5 SRs (w %)', + + // Original text: "{running} running ({halted} halted)" + vmsStates: + '{running} uruchomiona{halted, plural, one {} other {s}} ({halted} zatrzymana{halted, plural, one {} other {s}})', + + // Original text: "Clear selection" + dashboardStatsButtonRemoveAll: 'Clear selection', + + // Original text: "Add all hosts" + dashboardStatsButtonAddAllHost: 'Dodaj wszystkie hosty', + + // Original text: "Add all VMs" + dashboardStatsButtonAddAllVM: 'Dodaj wszystkie VMs', + + // Original text: "{value} {date, date, medium}" + weekHeatmapData: '{value} {date, date, medium}', + + // Original text: "No data." + weekHeatmapNoData: 'No data.', + + // Original text: "Weekly Heatmap" + weeklyHeatmap: 'Tygodniowa mapa cieplna', + + // Original text: "Weekly Charts" + weeklyCharts: 'Wykresy tygodniowe', + + // Original text: "Synchronize scale:" + weeklyChartsScaleInfo: 'Synchronize scale:', + + // Original text: "Stats error" + statsDashboardGenericErrorTitle: 'Błąd statystyk', + + // Original text: "There is no stats available for:" + statsDashboardGenericErrorMessage: 'Nie ma dostępnych statystyk dla:', + + // Original text: "No selected metric" + noSelectedMetric: 'No selected metric', + + // Original text: "Select" + statsDashboardSelectObjects: 'Wybierz', + + // Original text: "Loading…" + metricsLoading: 'Ładowanie…', + + // Original text: "Coming soon!" + comingSoon: 'Coming soon!', + + // Original text: "Orphaned snapshot VDIs" + orphanedVdis: 'Orphaned snapshot VDIs', + + // Original text: "Orphaned VMs snapshot" + orphanedVms: 'Orphaned VMs snapshot', + + // Original text: "No orphans" + noOrphanedObject: 'No orphans', + + // Original text: "Remove all orphaned snapshot VDIs" + removeAllOrphanedObject: 'Remove all orphaned snapshot VDIs', + + // Original text: "Name" + vmNameLabel: 'Nazwa', + + // Original text: "Description" + vmNameDescription: 'Opis', + + // Original text: "Resident on" + vmContainer: 'Resident on', + + // Original text: "Alarms" + alarmMessage: 'Alarmy', + + // Original text: "No alarms" + noAlarms: 'Brak alarmów', + + // Original text: "Date" + alarmDate: 'Data', + + // Original text: "Content" + alarmContent: 'Zawartość', + + // Original text: "Issue on" + alarmObject: 'Issue on', + + // Original text: "Pool" + alarmPool: 'Pula', + + // Original text: "Remove all alarms" + alarmRemoveAll: 'Usuń wszystkie alarmy', + + // Original text: "{used}% used ({free} left)" + spaceLeftTooltip: '{used}% used ({free} left)', + + // Original text: "Create a new VM on {select}" + newVmCreateNewVmOn: 'Stwórz nową VM w {select}', + + // Original text: "Create a new VM on {select1} or {select2}" + newVmCreateNewVmOn2: 'Stwórz nową VM w {select1} lub {select2}', + + // Original text: "You have no permission to create a VM" + newVmCreateNewVmNoPermission: 'Nie masz uprawnień do tworzenia VM', + + // Original text: "Infos" + newVmInfoPanel: 'Informacje', + + // Original text: "Name" + newVmNameLabel: 'Nazwa', + + // Original text: "Template" + newVmTemplateLabel: 'Szablon', + + // Original text: "Description" + newVmDescriptionLabel: 'Opis', + + // Original text: "Performances" + newVmPerfPanel: 'CPU i RAM', + + // Original text: "vCPUs" + newVmVcpusLabel: 'vCPUs', + + // Original text: "RAM" + newVmRamLabel: 'RAM', + + // Original text: "Static memory max" + newVmStaticMaxLabel: 'Pamieć statyczna max', + + // Original text: "Dynamic memory min" + newVmDynamicMinLabel: 'Pamieć dynamiczna min', + + // Original text: "Dynamic memory max" + newVmDynamicMaxLabel: 'Pamieć dynamiczna max', + + // Original text: "Install settings" + newVmInstallSettingsPanel: 'Install settings', + + // Original text: "ISO/DVD" + newVmIsoDvdLabel: 'ISO/DVD', + + // Original text: "Network" + newVmNetworkLabel: 'Sieć', + + // Original text: "e.g: http://httpredir.debian.org/debian" + newVmInstallNetworkPlaceHolder: 'ex : http://httpredir.debian.org/debian', + + // Original text: "PV Args" + newVmPvArgsLabel: 'PV Args', + + // Original text: "PXE" + newVmPxeLabel: 'PXE', + + // Original text: "Interfaces" + newVmInterfacesPanel: 'Interfejsy', + + // Original text: "MAC" + newVmMacLabel: 'MAC', + + // Original text: "Add interface" + newVmAddInterface: 'Dodaj dodatkowy interfejs', + + // Original text: "Disks" + newVmDisksPanel: 'Dyski', + + // Original text: "SR" + newVmSrLabel: 'SR', + + // Original text: "Bootable" + newVmBootableLabel: 'Bootable', + + // Original text: "Size" + newVmSizeLabel: 'Rozmiar', + + // Original text: "Add disk" + newVmAddDisk: 'Dodaj dodatkowy dysk', + + // Original text: "Summary" + newVmSummaryPanel: 'Podsumowanie', + + // Original text: "Create" + newVmCreate: 'Utwórz', + + // Original text: "Reset" + newVmReset: 'Reset', + + // Original text: "Select template" + newVmSelectTemplate: 'Wybierz szablon', + + // Original text: "SSH key" + newVmSshKey: 'Klucz SSH', + + // Original text: "Config drive" + newVmConfigDrive: 'Config drive', + + // Original text: "Custom config" + newVmCustomConfig: 'Niestandardowa konfiguracja', + + // Original text: "Boot VM after creation" + newVmBootAfterCreate: 'Boot VM po utworzeniu', + + // Original text: "Auto-generated if empty" + newVmMacPlaceholder: 'Auto-generated if empty', + + // Original text: "CPU weight" + newVmCpuWeightLabel: 'CPU weight', + + // Original text: "Default: {value, number}" + newVmDefaultCpuWeight: 'Domyślnie: {value, number}', + + // Original text: "CPU cap" + newVmCpuCapLabel: 'CPU cap', + + // Original text: "Default: {value, number}" + newVmDefaultCpuCap: 'Domyślnie : {value, number}', + + // Original text: "Cloud config" + newVmCloudConfig: 'Konfiguracja chmury', + + // Original text: "Create VMs" + newVmCreateVms: 'Utwórz VMs', + + // Original text: "Are you sure you want to create {nbVms} VMs?" + newVmCreateVmsConfirm: 'Jesteś pewny że chcesz utworzyć {nbVms} VMs ?', + + // Original text: "Multiple VMs:" + newVmMultipleVms: 'Multiple VMs :', + + // Original text: "Select a resource set:" + newVmSelectResourceSet: 'Select a resource set:', + + // Original text: "Name pattern:" + newVmMultipleVmsPattern: 'Name pattern:', + + // Original text: "e.g.: \\{name\\}_%" + newVmMultipleVmsPatternPlaceholder: 'np. : \\{name\\}_%', + + // Original text: "First index:" + newVmFirstIndex: 'First index:', + + // Original text: "Recalculate VMs number" + newVmNumberRecalculate: 'Przelicz ilość VMs', + + // Original text: "Refresh VMs name" + newVmNameRefresh: 'Odśwież nazwę VMs', + + // Original text: "Advanced" + newVmAdvancedPanel: 'Zaawansowane', + + // Original text: "Show advanced settings" + newVmShowAdvanced: 'Pokaż ustawienia zaawansowane', + + // Original text: "Hide advanced settings" + newVmHideAdvanced: 'Ukryj ustawienia zaawansowane', + + // Original text: "Resource sets" + resourceSets: 'Resource sets', + + // Original text: "No resource sets." + noResourceSets: 'No resource sets.', + + // Original text: "Loading resource sets" + loadingResourceSets: 'Loading resource sets', + + // Original text: "Resource set name" + resourceSetName: 'Resource set name', + + // Original text: "Recompute all limits" + recomputeResourceSets: 'Przelicz wszystkie limity', + + // Original text: "Save" + saveResourceSet: 'Zapisz', + + // Original text: "Reset" + resetResourceSet: 'Reset', + + // Original text: "Edit" + editResourceSet: 'Edytuj', + + // Original text: "Delete" + deleteResourceSet: 'Usuń', + + // Original text: "Delete resource set" + deleteResourceSetWarning: 'Delete resource set', + + // Original text: "Are you sure you want to delete this resource set?" + deleteResourceSetQuestion: + 'Are you sure you want to delete this resource set?', + + // Original text: "Missing objects:" + resourceSetMissingObjects: 'Brakujące obiekty:', + + // Original text: "vCPUs" + resourceSetVcpus: 'vCPUs', + + // Original text: "Memory" + resourceSetMemory: 'Pamieć', + + // Original text: "Storage" + resourceSetStorage: 'Przestrzeń dyskowa', + + // Original text: "Unknown" + unknownResourceSetValue: 'Nieznany', + + // Original text: "Available hosts" + availableHosts: 'Dostępne hosty', + + // Original text: "Excluded hosts" + excludedHosts: 'Wykluczone hosty', + + // Original text: "No hosts available." + noHostsAvailable: 'Brak dostępnych hostów.', + + // Original text: "VMs created from this resource set shall run on the following hosts." + availableHostsDescription: + 'VMs created from this resource set shall run on the following hosts.', + + // Original text: "Maximum CPUs" + maxCpus: 'Maximum CPUs', + + // Original text: "Maximum RAM (GiB)" + maxRam: 'Maximum RAM (GiB)', + + // Original text: "Maximum disk space" + maxDiskSpace: 'Maximum disk space', + + // Original text: "IP pool" + ipPool: 'Pula IP', + + // Original text: "Quantity" + quantity: 'Quantity', + + // Original text: "No limits." + noResourceSetLimits: 'Brak limitów.', + + // Original text: "Total:" + totalResource: 'Łącznie :', + + // Original text: "Remaining:" + remainingResource: 'Pozostało :', + + // Original text: "Used:" + usedResource: 'Używane:', + + // Original text: "New" + resourceSetNew: 'Nowy', + + // Original text: "Try dropping some VMs files here, or click to select VMs to upload. Accept only .xva/.ova files." + importVmsList: + 'Try dropping some VMs files here, or click to select VMs to upload. Accept only .xva/.ova files.', + + // Original text: "No selected VMs." + noSelectedVms: 'No selected VMs.', + + // Original text: "To Pool:" + vmImportToPool: 'To Pool:', + + // Original text: "To SR:" + vmImportToSr: 'To SR:', + + // Original text: "VMs to import" + vmsToImport: 'VMs to import', + + // Original text: "Reset" + importVmsCleanList: 'Reset', + + // Original text: "VM import success" + vmImportSuccess: 'import VM udany!', + + // Original text: "VM import failed" + vmImportFailed: 'Import VM nieudany', + + // Original text: "Import starting…" + startVmImport: 'Rozpoczęcie importowania…', + + // Original text: "Export starting…" + startVmExport: 'Eksport rozpoczęty…', + + // Original text: "N CPUs" + nCpus: 'N CPUs', + + // Original text: "Memory" + vmMemory: 'Pamieć', + + // Original text: "Disk {position} ({capacity})" + diskInfo: 'Dysk {position} ({capacity})', + + // Original text: "Disk description" + diskDescription: 'Opis dysku', + + // Original text: "No disks." + noDisks: 'Brak dysków.', + + // Original text: "No networks." + noNetworks: 'Brak sieci.', + + // Original text: "Network {name}" + networkInfo: 'Sieć {name}', + + // Original text: "No description available" + noVmImportErrorDescription: 'Opis jest niedostępny', + + // Original text: "Error:" + vmImportError: 'Błąd:', + + // Original text: "{type} file:" + vmImportFileType: '{type} plik:', + + // Original text: "Please to check and/or modify the VM configuration." + vmImportConfigAlert: 'Proszę sprawdzić lub zmodyfikować konfigurację VM.', + + // Original text: "No pending tasks" + noTasks: 'Brak oczekujących zadań', + + // Original text: "Currently, there are not any pending XenServer tasks" + xsTasks: 'Aktualnie, nie ma żadnych oczekujących zadań na HyperVisorze', + + // Original text: "Schedules" + backupSchedules: 'Harmonogramy', + + // Original text: "Get remote" + getRemote: 'Get remote', + + // Original text: "List Remote" + listRemote: 'List Remote', + + // Original text: "simple" + simpleBackup: 'simple', + + // Original text: "delta" + delta: 'delta', + + // Original text: "Restore Backups" + restoreBackups: 'Odtwórz kopie zapasowe', + + // Original text: 'Click on a VM to display restore options' + restoreBackupsInfo: 'Kliknij w VM żeby wyświetlić możliwości odtworzenia', + + // Original text: "Enabled" + remoteEnabled: 'Włączone', + + // Original text: "Error" + remoteError: 'Błąd', + + // Original text: "No backup available" + noBackup: 'Brak dostępnej kopi zapasowej', + + // Original text: "VM Name" + backupVmNameColumn: 'Nazwa VM', + + // Original text: 'Tags' + backupTags: 'Tagi', + + // Original text: "Last Backup" + lastBackupColumn: 'Ostatnia kopia zapasowa', + + // Original text: "Available Backups" + availableBackupsColumn: 'Dostępne kopie zapasowe', + + // Original text: 'Missing parameters' + backupRestoreErrorTitle: 'Brakujące parametry', + + // Original text: 'Choose a SR and a backup' + backupRestoreErrorMessage: 'Wybierz SR i kopię zapasową', + + // Original text: "Display backups" + displayBackup: 'Wyświetl kopie zapasowe', + + // Original text: "Import VM" + importBackupTitle: 'Importuj VM', + + // Original text: "Starting your backup import" + importBackupMessage: 'Rozpoczynanie imortu kopii zapasowej', + + // Original text: "VMs to backup" + vmsToBackup: 'VMs do kopii zapasowej', + + // Original text: "Emergency shutdown Host{nHosts, plural, one {} other {s}}" + emergencyShutdownHostsModalTitle: + 'Wyłączenie awaryjne hosta {nHosts, plural, one {} other {s}}', + + // Original text: "Are you sure you want to shutdown {nHosts} Host{nHosts, plural, one {} other {s}}?" + emergencyShutdownHostsModalMessage: + 'Jesteś peweny że chcesz wyłączyć {nHosts} hosta{nHosts, plural, one {} other {s}}?', + + // Original text: "Shutdown host" + stopHostModalTitle: 'Wyłączenie hosta', + + // Original text: "This will shutdown your host. Do you want to continue? If it's the pool master, your connection to the pool will be lost" + stopHostModalMessage: + 'To wyłączy twojego hosta. Chcesz kontynuować? Jeżeli jest to zarządca puli, twoje połaczenie do puli zostanie utracone', + + // Original text: "Add host" + addHostModalTitle: 'Dodaj hosta', + + // Original text: "Are you sure you want to add {host} to {pool}?" + addHostModalMessage: 'Jesteś pewny że chcesz dodać hosta{host} do {pool}?', + + // Original text: "Restart host" + restartHostModalTitle: 'Restart hosta', + + // Original text: "This will restart your host. Do you want to continue?" + restartHostModalMessage: 'To zrestartuje twojego hosta. Chcesz kontynuować?', + + // Original text: "Restart Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}" + restartHostsAgentsModalTitle: + 'Zrestartuj hosta{nHosts, plural, one {} other {s}}', + + // Original text: "Are you sure you want to restart {nHosts} Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}?" + restartHostsAgentsModalMessage: + "Êtes-vous sûr de vouloir redémarrer les agents {nHosts, plural, one {de l'hôte} other {des hôtes}} ?", + + // Original text: "Restart Host{nHosts, plural, one {} other {s}}" + restartHostsModalTitle: 'Restart hosta{nHosts, plural, one {} other {s}}', + + // Original text: "Are you sure you want to restart {nHosts} Host{nHosts, plural, one {} other {s}}?" + restartHostsModalMessage: + 'Czy na pewno chcesz zrestartować {nHosts} Host{nHosts, plural, one {} other {s}}?', + + // Original text: "Start VM{vms, plural, one {} other {s}}" + startVmsModalTitle: 'Uruchom VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to start {vms} VM{vms, plural, one {} other {s}}?" + startVmsModalMessage: + 'Are you sure you want to start {vms} VM{vms, plural, one {} other {s}}?', + + // Original text: "Stop Host{nHosts, plural, one {} other {s}}" + stopHostsModalTitle: 'Zatrzymaj hosta{nHosts, plural, one {} other {s}}', + + // Original text: "Are you sure you want to stop {nHosts} Host{nHosts, plural, one {} other {s}}?" + stopHostsModalMessage: + 'Jesteś pewny że chcesz zatrzymać {nHosts} Host{nHosts, plural, one {} other {s}}?', + + // Original text: "Stop VM{vms, plural, one {} other {s}}" + stopVmsModalTitle: 'Zatrzymaj VM {vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to stop {vms} VM{vms, plural, one {} other {s}}?" + stopVmsModalMessage: + 'Jesteś pewien że chcesz zatrzymać {vms} VM{vms, plural, one {} other {s}}?', + + // Original text: "Restart VM" + restartVmModalTitle: 'Restart VM', + + // Original text: "Are you sure you want to restart {name}?" + restartVmModalMessage: 'Na pewno chcesz zrestartować {name}?', + + // Original text: "Stop VM" + stopVmModalTitle: 'Zatrzymaj VM', + + // Original text: "Are you sure you want to stop {name}?" + stopVmModalMessage: 'Na pewno chcesz wyłączyć {name}?', + + // Original text: "Restart VM{vms, plural, one {} other {s}}" + restartVmsModalTitle: 'Restart VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to restart {vms} VM{vms, plural, one {} other {s}}?" + restartVmsModalMessage: + 'Are you sure you want to restart {vms} VM{vms, plural, one {} other {s}}?', + + // Original text: "Snapshot VM{vms, plural, one {} other {s}}" + snapshotVmsModalTitle: 'Snapshot VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to snapshot {vms} VM{vms, plural, one {} other {s}}?" + snapshotVmsModalMessage: + 'Jesteś pewny że chcesz zrobić snapshot {vms} VM{vms, plural, one {} other {s}}?', + + // Original text: "Delete VM{vms, plural, one {} other {s}}" + deleteVmsModalTitle: 'Delete VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to delete {vms} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED" + deleteVmsModalMessage: + 'Are you sure you want to delete {vms} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED', + + // Original text: "Delete VM" + deleteVmModalTitle: 'Usuń VM', + + // Original text: "Are you sure you want to delete this VM? ALL VM DISKS WILL BE REMOVED" + deleteVmModalMessage: + 'Are you sure you want to delete this VM? ALL VM DISKS WILL BE REMOVED', + + // Original text: "Migrate VM" + migrateVmModalTitle: 'Migruj VM', + + // Original text: "Select a destination host:" + migrateVmSelectHost: 'Select a destination host:', + + // Original text: "Select a migration network:" + migrateVmSelectMigrationNetwork: 'Select a migration network:', + + // Original text: "For each VDI, select an SR:" + migrateVmSelectSrs: 'For each VDI, select an SR:', + + // Original text: "For each VIF, select a network:" + migrateVmSelectNetworks: 'For each VIF, select a network:', + + // Original text: "Select a destination SR:" + migrateVmsSelectSr: 'Select a destination SR:', + + // Original text: "Select a destination SR for local disks:" + migrateVmsSelectSrIntraPool: 'Select a destination SR for local disks:', + + // Original text: "Select a network on which to connect each VIF:" + migrateVmsSelectNetwork: 'Select a network on which to connect each VIF:', + + // Original text: "Smart mapping" + migrateVmsSmartMapping: 'Smart mapping', + + // Original text: "Name" + migrateVmName: 'Nazwa', + + // Original text: "SR" + migrateVmSr: 'SR', + + // Original text: "VIF" + migrateVmVif: 'VIF', + + // Original text: "Network" + migrateVmNetwork: 'Sieć', + + // Original text: "No target host" + migrateVmNoTargetHost: 'No target host', + + // Original text: "A target host is required to migrate a VM" + migrateVmNoTargetHostMessage: 'A target host is required to migrate a VM', + + // Original text: "Delete VDI" + deleteVdiModalTitle: 'Usuń VDI', + + // Original text: "Are you sure you want to delete this disk? ALL DATA ON THIS DISK WILL BE LOST" + deleteVdiModalMessage: + 'Jesteś pewien że chcesz usunąć dysk? Wszystkie dane na dysku zostaną utracone', + + // Original text: "Revert your VM" + revertVmModalTitle: 'Revert your VM', + + // Original text: "Delete snapshot" + deleteSnapshotModalTitle: 'Usuń snapshot', + + // Original text: "Are you sure you want to delete this snapshot?" + deleteSnapshotModalMessage: 'Are you sure you want to delete this snapshot?', + + // Original text: "Are you sure you want to revert this VM to the snapshot state? This operation is irreversible." + revertVmModalMessage: + 'Are you sure you want to revert this VM to the snapshot state? This operation is irreversible.', + + // Original text: "Snapshot before" + revertVmModalSnapshotBefore: 'Snapshot before', + + // Original text: "Import a {name} Backup" + importBackupModalTitle: 'Import a {name} Backup', + + // Original text: "Start VM after restore" + importBackupModalStart: 'Start VM after restore', + + // Original text: "Select your backup…" + importBackupModalSelectBackup: 'Wybierz swój backup…', + + // Original text: "Are you sure you want to remove all orphaned snapshot VDIs?" + removeAllOrphanedModalWarning: + 'Are you sure you want to remove all orphaned snapshot VDIs?', + + // Original text: "Remove all logs" + removeAllLogsModalTitle: 'Usuń wszystkie logi', + + // Original text: "Are you sure you want to remove all logs?" + removeAllLogsModalWarning: 'Jesteś pewien że chcesz usunąć wszystkie logi?', + + // Original text: "This operation is definitive." + definitiveMessageModal: 'This operation is definitive.', + + // Original text: "Previous SR Usage" + existingSrModalTitle: 'Previous SR Usage', + + // Original text: "This path has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingSrModalText: + 'This path has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation.', + + // Original text: "Previous LUN Usage" + existingLunModalTitle: 'Previous LUN Usage', + + // Original text: "This LUN has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingLunModalText: + 'This LUN has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation.', + + // Original text: "Replace current registration?" + alreadyRegisteredModal: 'Replace current registration?', + + // Original text: "Your XO appliance is already registered to {email}, do you want to forget and replace this registration ?" + alreadyRegisteredModalText: + 'Your XO appliance is already registered to {email}, do you want to forget and replace this registration ?', + + // Original text: "Ready for trial?" + trialReadyModal: 'Ready for trial?', + + // Original text: "During the trial period, XOA need to have a working internet connection. This limitation does not apply for our paid plans!" + trialReadyModalText: + 'During the trial period, XOA need to have a working internet connection. This limitation does not apply for our paid plans!', + + // Original text: "Host" + serverHost: 'Host', + + // Original text: "Username" + serverUsername: 'Nazwa użytkownika', + + // Original text: "Password" + serverPassword: 'Hasło', + + // Original text: "Action" + serverAction: 'Akcja', + + // Original text: "Read Only" + serverReadOnly: 'Tylko do odczytu', + + // Original text: "Disconnect server" + serverDisconnect: 'Rozłącz serwer', + + // Original text: "username" + serverPlaceHolderUser: 'Użytkownik', + + // Original text: "password" + serverPlaceHolderPassword: 'hasło', + + // Original text: "address[:port]" + serverPlaceHolderAddress: 'adres[:port]', + + // Original text: "Connect" + serverConnect: 'Połącz', + + // Original text: "Copy VM" + copyVm: 'Copy VM', + + // Original text: "Are you sure you want to copy this VM to {SR}?" + copyVmConfirm: 'Are you sure you want to copy this VM to {SR}?', + + // Original text: "Name" + copyVmName: 'Nazwa', + + // Original text: "Name pattern" + copyVmNamePattern: 'Name pattern', + + // Original text: "If empty: name of the copied VM" + copyVmNamePlaceholder: 'If empty: name of the copied VM', + + // Original text: "e.g.: \"\\{name\\}_COPY\"" + copyVmNamePatternPlaceholder: 'np. : "\\{name\\}_COPY"', + + // Original text: "Select SR" + copyVmSelectSr: 'Wybierz pulę dyskową', + + // Original text: "Use compression" + copyVmCompress: 'Użyj kompresji', + + // Original text: "No target SR" + copyVmsNoTargetSr: 'No target SR', + + // Original text: "A target SR is required to copy a VM" + copyVmsNoTargetSrMessage: 'A target SR is required to copy a VM', + + // Original text: "Detach host" + detachHostModalTitle: 'Detach host', + + // Original text: "Are you sure you want to detach {host} from its pool? THIS WILL REMOVE ALL VMs ON ITS LOCAL STORAGE AND REBOOT THE HOST." + detachHostModalMessage: + 'Are you sure you want to detach {host} from its pool? THIS WILL REMOVE ALL VMs ON ITS LOCAL STORAGE AND REBOOT THE HOST.', + + // Original text: "Detach" + detachHost: 'Detach', + + // Original text: "Create network" + newNetworkCreate: 'Utwórz sieć', + + // Original text: "Create bonded network" + newBondedNetworkCreate: 'Create bonded network', + + // Original text: "Interface" + newNetworkInterface: 'Interface', + + // Original text: "Name" + newNetworkName: 'Nazwa', + + // Original text: "Description" + newNetworkDescription: 'Opis', + + // Original text: "VLAN" + newNetworkVlan: 'VLAN', + + // Original text: "No VLAN if empty" + newNetworkDefaultVlan: 'No VLAN if empty', + + // Original text: "MTU" + newNetworkMtu: 'MTU', + + // Original text: "Default: 1500" + newNetworkDefaultMtu: 'Domyślnie : 1500', + + // Original text: "Name required" + newNetworkNoNameErrorTitle: 'Nazwa wymagana', + + // Original text: "A name is required to create a network" + newNetworkNoNameErrorMessage: 'Nazwa jest wymagana do utworzenia sieci', + + // Original text: "Bond mode" + newNetworkBondMode: 'Tryb bondingu', + + // Original text: "Delete network" + deleteNetwork: 'Usuń sieć', + + // Original text: "Are you sure you want to delete this network?" + deleteNetworkConfirm: 'Jesteś pewien że chcesz usunąć te sieć ?', + + // Original text: "This network is currently in use" + networkInUse: 'Ta sieć jest obecnie używana ', + + // Original text: "Bonded" + pillBonded: 'Bonded', + + // Original text: "Host" + addHostSelectHost: 'Host', + + // Original text: "No host" + addHostNoHost: 'No host', + + // Original text: "No host selected to be added" + addHostNoHostMessage: 'No host selected to be added', + + // Original text: "Xen Orchestra" + xenOrchestra: 'Xen Orchestra', + + // Original text: "server" + xenOrchestraServer: 'serwer', + + // Original text: "web client" + xenOrchestraWeb: 'web klient', + + // Original text: "No pro support provided!" + noProSupport: 'No pro support provided!', + + // Original text: "Use in production at your own risks" + noProductionUse: 'Używanie produkcyjne na własne ryzyko', + + // Original text: "You can download our turnkey appliance at {website}" + downloadXoaFromWebsite: 'You can download our turnkey appliance at {website}', + + // Original text: "Bug Tracker" + bugTracker: 'Bug Tracker', + + // Original text: "Issues? Report it!" + bugTrackerText: 'Issues? Report it!', + + // Original text: "Community" + community: 'Społeczność', + + // Original text: "Join our community forum!" + communityText: 'Dołącz do forum społeczności !', + + // Original text: "Free Trial for Premium Edition!" + freeTrial: 'Free Trial for Premium Edition!', + + // Original text: "Request your trial now!" + freeTrialNow: 'Request your trial now!', + + // Original text: "Any issue?" + issues: 'Any issue?', + + // Original text: "Problem? Contact us!" + issuesText: 'Masz problem? Zkontaktuj się z nami!', + + // Original text: "Documentation" + documentation: 'Dokumentacja', + + // Original text: "Read our official doc" + documentationText: 'Read our official doc', + + // Original text: "Pro support included" + proSupportIncluded: 'Pro support included', + + // Original text: "Acces your XO Account" + xoAccount: 'Acces your XO Account', + + // Original text: "Report a problem" + openTicket: 'Raportuj problem', + + // Original text: "Problem? Open a ticket!" + openTicketText: 'Masz problem? Utwórz zgłoszenie!', + + // Original text: "Upgrade needed" + upgradeNeeded: 'Upgrade needed', + + // Original text: "Upgrade now!" + upgradeNow: 'Aktualizuj teraz!', + + // Original text: "Or" + or: 'Lub', + + // Original text: "Try it for free!" + tryIt: 'Spróbuj za darmo!', + + // Original text: "This feature is available starting from {plan} Edition" + availableIn: 'This feature is available starting from {plan} Edition', + + // Original text: "This feature is not available in your version, contact your administrator to know more." + notAvailable: + 'This feature is not available in your version, contact your administrator to know more.', + + // Original text: "Updates" + updateTitle: 'Aktualizuj', + + // Original text: "Registration" + registration: 'Rejestracja', + + // Original text: "Trial" + trial: 'Trial', + + // Original text: "Settings" + settings: 'Ustawienia', + + // Original text: "Proxy settings" + proxySettings: 'Ustawienia proxy', + + // Original text: "Host (myproxy.example.org)" + proxySettingsHostPlaceHolder: 'Host (mojeproxy.przyklad.pl)', + + // Original text: "Port (eg: 3128)" + proxySettingsPortPlaceHolder: 'Port (np : 3128)', + + // Original text: "Username" + proxySettingsUsernamePlaceHolder: 'Użytkownik', + + // Original text: "Password" + proxySettingsPasswordPlaceHolder: 'Hasło', + + // Original text: "Your email account" + updateRegistrationEmailPlaceHolder: 'Twoje konto email', + + // Original text: "Your password" + updateRegistrationPasswordPlaceHolder: 'Twoje hasło', + + // Original text: "Update" + update: 'Aktualizuj', + + // Original text: "Refresh" + refresh: 'Odśwież', + + // Original text: "Upgrade" + upgrade: 'Aktualizuj', + + // Original text: "No updater available for Community Edition" + noUpdaterCommunity: 'No updater available for Community Edition', + + // Original text: "Please consider subscribe and try it with all features for free during 15 days on" + considerSubscribe: + 'Please consider subscribe and try it with all features for free during 15 days on', + + // Original text: "Manual update could break your current installation due to dependencies issues, do it with caution" + noUpdaterWarning: + 'Manual update could break your current installation due to dependencies issues, do it with caution', + + // Original text: "Current version:" + currentVersion: 'Obecna wersja:', + + // Original text: "Register" + register: 'Rejestruj', + + // Original text: "Edit registration" + editRegistration: 'Edit registration', + + // Original text: "Please, take time to register in order to enjoy your trial." + trialRegistration: + 'Please, take time to register in order to enjoy your trial.', + + // Original text: "Start trial" + trialStartButton: 'Start trial', + + // Original text: "You can use a trial version until {date, date, medium}. Upgrade your appliance to get it." + trialAvailableUntil: + 'You can use a trial version until {date, date, medium}. Upgrade your appliance to get it.', + + // Original text: "Your trial has been ended. Contact us or downgrade to Free version" + trialConsumed: + 'Twoja wersja demonstracyjna właśnie się zakończyła. Skontaktuj się z nami żeby pobrać darmową wersję', + + // Original text: "Your xoa-updater service appears to be down. Your XOA cannot run fully without reaching this service." + trialLocked: + 'Your xoa-updater service appears to be down. Your XOA cannot run fully without reaching this service.', + + // Original text: "No update information available" + noUpdateInfo: 'No update information available', + + // Original text: "Update information may be available" + waitingUpdateInfo: 'Update information may be available', + + // Original text: "Your XOA is up-to-date" + upToDate: 'Twoje XOA jest aktualne', + + // Original text: "You need to update your XOA (new version is available)" + mustUpgrade: 'You need to update your XOA (new version is available)', + + // Original text: "Your XOA is not registered for updates" + registerNeeded: 'Your XOA is not registered for updates', + + // Original text: "Can't fetch update information" + updaterError: 'Nie mogę pobrać aktualizacji', + + // Original text: "Upgrade successful" + promptUpgradeReloadTitle: 'Aktualizacja zakończona sukcesem', + + // Original text: "Your XOA has successfully upgraded, and your browser must reload the application. Do you want to reload now ?" + promptUpgradeReloadMessage: + 'Your XOA has successfully upgraded, and your browser must reload the application. Do you want to reload now ?', + + // Original text: "Xen Orchestra from the sources" + disclaimerTitle: 'Xen Orchestra z źródeł', + + // Original text: "You are using XO from the sources! That's great for a personal/non-profit usage." + disclaimerText1: + 'Używasz XO z źródeł!. To dobre rozwiązanie tylko do prywatnego/nieprodukcyjnego użytku', + + // Original text: "If you are a company, it's better to use it with our appliance + pro support included:" + disclaimerText2: + "If you are a company, it's better to use it with our appliance + pro support included:", + + // Original text: "This version is not bundled with any support nor updates. Use it with caution for critical tasks." + disclaimerText3: + 'This version is not bundled with any support nor updates. Use it with caution for critical tasks.', + + // Original text: "Connect PIF" + connectPif: 'Connect PIF', + + // Original text: "Are you sure you want to connect this PIF?" + connectPifConfirm: 'Are you sure you want to connect this PIF?', + + // Original text: "Disconnect PIF" + disconnectPif: 'Disconnect PIF', + + // Original text: "Are you sure you want to disconnect this PIF?" + disconnectPifConfirm: 'Are you sure you want to disconnect this PIF ?', + + // Original text: "Delete PIF" + deletePif: 'Delete PIF', + + // Original text: "Are you sure you want to delete this PIF?" + deletePifConfirm: 'Are you sure you want to delete this PIF?', + + // Original text: "Username" + username: 'Użytkownik', + + // Original text: "Password" + password: 'Hasło', + + // Original text: "Language" + language: 'Język', + + // Original text: "Old password" + oldPasswordPlaceholder: 'Stare hasło', + + // Original text: "New password" + newPasswordPlaceholder: 'Nowe hasło', + + // Original text: "Confirm new password" + confirmPasswordPlaceholder: 'Potwierdź nowe hasło', + + // Original text: "Confirmation password incorrect" + confirmationPasswordError: 'Potwierdzenie hasła niepoprawne', + + // Original text: "Password does not match the confirm password." + confirmationPasswordErrorBody: 'Hasło nie zgadza się z potwierdzeniem', + + // Original text: "Password changed" + pwdChangeSuccess: 'Hasło zmienione', + + // Original text: "Your password has been successfully changed." + pwdChangeSuccessBody: 'Twoje hasło zostało pomyślnie zmienione', + + // Original text: "Incorrect password" + pwdChangeError: 'Nieprawidłowe hasło', + + // Original text: "The old password provided is incorrect. Your password has not been changed." + pwdChangeErrorBody: + 'The old password provided is incorrect. Your password has not been changed.', + + // Original text: "OK" + changePasswordOk: 'OK', + + // Original text: "SSH keys" + sshKeys: 'Klucze SSH', + + // Original text: "New SSH key" + newSshKey: 'Nowy klucz SSH', + + // Original text: "Delete" + deleteSshKey: 'Usuń', + + // Original text: "No SSH keys" + noSshKeys: 'Brak kluczy SSH', + + // Original text: "New SSH key" + newSshKeyModalTitle: 'Nowy klucz SSH', + + // Original text: "Invalid key" + sshKeyErrorTitle: 'Nieprawidłowy klucz', + + // Original text: "An SSH key requires both a title and a key." + sshKeyErrorMessage: 'An SSH key requires both a title and a key.', + + // Original text: "Title" + title: 'Title', + + // Original text: "Key" + key: 'Klucz', + + // Original text: "Delete SSH key" + deleteSshKeyConfirm: 'Usuń klucz SSH', + + // Original text: "Are you sure you want to delete the SSH key {title}?" + deleteSshKeyConfirmMessage: + 'Are you sure you want to delete the SSH key {title}?', + + // Original text: "Others" + others: 'Inne', + + // Original text: "Loading logs…" + loadingLogs: 'Ładowanie logów…', + + // Original text: "User" + logUser: 'Użytkownik', + + // Original text: "Method" + logMethod: 'Metoda', + + // Original text: "Params" + logParams: 'Params', + + // Original text: "Message" + logMessage: 'Wiadomość', + + // Original text: "Error" + logError: 'Błąd', + + // Original text: "Display details" + logDisplayDetails: 'Wyświetl szczegóły', + + // Original text: "Date" + logTime: 'Data', + + // Original text: "No stack trace" + logNoStackTrace: 'No stack trace', + + // Original text: "No params" + logNoParams: 'No params', + + // Original text: "Delete log" + logDelete: 'Usuń logi', + + // Original text: "Delete all logs" + logDeleteAll: 'Usuń wszystkie logi', + + // Original text: "Delete all logs" + logDeleteAllTitle: 'Usuń wszystkie logi', + + // Original text: "Are you sure you want to delete all the logs?" + logDeleteAllMessage: 'Are you sure you want to delete all the logs?', + + // Original text: "Name" + ipPoolName: 'Nazwa', + + // Original text: "IPs" + ipPoolIps: 'IPs', + + // Original text: "IPs (e.g.: 1.0.0.12-1.0.0.17;1.0.0.23)" + ipPoolIpsPlaceholder: 'IPs (np.: 1.0.0.12-1.0.0.17;1.0.0.23)', + + // Original text: "Networks" + ipPoolNetworks: 'Sieci', + + // Original text: "No IP pools" + ipsNoIpPool: 'Brak puli IP', + + // Original text: "Create" + ipsCreate: 'Utwórz', + + // Original text: "Delete all IP pools" + ipsDeleteAllTitle: 'Usuń wszystkie pule IP', + + // Original text: "Are you sure you want to delete all the IP pools?" + ipsDeleteAllMessage: 'Jesteś pewien że chcesz usunąć wszystkie pule IP?', + + // Original text: "VIFs" + ipsVifs: 'VIFs', + + // Original text: "Not used" + ipsNotUsed: 'Nieużywany', + + // Original text: "Keyboard shortcuts" + shortcutModalTitle: 'Skróty klawiszowe', + + // Original text: "Global" + shortcut_XoApp: 'Global', + + // Original text: "Go to hosts list" + shortcut_GO_TO_HOSTS: 'Idź do listy hostów', + + // Original text: "Go to pools list" + shortcut_GO_TO_POOLS: 'Idź do listy pul', + + // Original text: "Go to VMs list" + shortcut_GO_TO_VMS: 'Idź do listy VMs', + + // Original text: "Create a new VM" + shortcut_CREATE_VM: 'Utwórz nową VM', + + // Original text: "Unfocus field" + shortcut_UNFOCUS: 'Niepodświetlone pole', + + // Original text: "Show shortcuts key bindings" + shortcut_HELP: 'Show shortcuts key bindings', + + // Original text: "Home" + shortcut_Home: 'Home', + + // Original text: "Focus search bar" + shortcut_SEARCH: 'Focus search bar', + + // Original text: "Next item" + shortcut_NAV_DOWN: 'Next item', + + // Original text: "Previous item" + shortcut_NAV_UP: 'Previous item', + + // Original text: "Select item" + shortcut_SELECT: 'Select item', + + // Original text: "Open" + shortcut_JUMP_INTO: 'Otwarte', + + // Original text: "VM" + settingsAclsButtonTooltipVM: 'VM', + + // Original text: "Hosts" + settingsAclsButtonTooltiphost: 'Hosty', + + // Original text: "Pool" + settingsAclsButtonTooltippool: 'Pule', + + // Original text: "SR" + settingsAclsButtonTooltipSR: 'SR', + + // Original text: "Network" + settingsAclsButtonTooltipnetwork: 'Sieć', +} diff --git a/packages/xo-web/src/common/intl/locales/pt.js b/packages/xo-web/src/common/intl/locales/pt.js new file mode 100644 index 000000000..bace43b65 --- /dev/null +++ b/packages/xo-web/src/common/intl/locales/pt.js @@ -0,0 +1,3170 @@ +// See http://momentjs.com/docs/#/use-it/browserify/ +import 'moment/locale/pt' + +import reactIntlData from 'react-intl/locale-data/pt' +import { addLocaleData } from 'react-intl' +addLocaleData(reactIntlData) + +// =================================================================== + +export default { + // Original text: 'Connecting' + statusConnecting: undefined, + + // Original text: 'Disconnected' + statusDisconnected: undefined, + + // Original text: 'Loading…' + statusLoading: undefined, + + // Original text: 'Page not found' + errorPageNotFound: undefined, + + // Original text: 'no such item' + errorNoSuchItem: undefined, + + // Original text: "Long click to edit" + editableLongClickPlaceholder: 'Longo clique para editar', + + // Original text: "Click to edit" + editableClickPlaceholder: 'Clique para editar', + + // Original text: "OK" + alertOk: 'OK', + + // Original text: "OK" + confirmOk: 'Confirmar', + + // Original text: "Cancel" + confirmCancel: 'Cancelar', + + // Original text: 'On error' + onError: undefined, + + // Original text: 'Successful' + successful: undefined, + + // Original text: 'Copy to clipboard' + copyToClipboard: undefined, + + // Original text: 'Master' + pillMaster: undefined, + + // Original text: "Home" + homePage: 'Principal', + + // Original text: 'VMs' + homeVmPage: undefined, + + // Original text: 'Hosts' + homeHostPage: undefined, + + // Original text: 'Pools' + homePoolPage: undefined, + + // Original text: 'Templates' + homeTemplatePage: undefined, + + // Original text: "Dashboard" + dashboardPage: 'Painel de Controle', + + // Original text: "Overview" + overviewDashboardPage: 'Visão Geral', + + // Original text: "Visualizations" + overviewVisualizationDashboardPage: 'Visualizações', + + // Original text: "Statistics" + overviewStatsDashboardPage: 'Estatisticas', + + // Original text: "Health" + overviewHealthDashboardPage: 'Diagnóstico', + + // Original text: "Self service" + selfServicePage: 'Auto-Serviço', + + // Original text: "Backup" + backupPage: 'Backup', + + // Original text: "Jobs" + jobsPage: 'Tarefas', + + // Original text: "Updates" + updatePage: 'Atualizações', + + // Original text: "Settings" + settingsPage: 'Configurações', + + // Original text: "Servers" + settingsServersPage: 'Servidores', + + // Original text: "Users" + settingsUsersPage: 'Usuários', + + // Original text: "Groups" + settingsGroupsPage: 'Grupos', + + // Original text: "ACLs" + settingsAclsPage: 'Controle de Acessos', + + // Original text: "Plugins" + settingsPluginsPage: 'Plugins', + + // Original text: 'Logs' + settingsLogsPage: undefined, + + // Original text: 'IPs' + settingsIpsPage: undefined, + + // Original text: "About" + aboutPage: 'Sobre', + + // Original text: "New" + newMenu: 'Novo(a)', + + // Original text: "Tasks" + taskMenu: 'Tarefas', + + // Original text: 'Tasks' + taskPage: undefined, + + // Original text: "VM" + newVmPage: 'VM', + + // Original text: "Storage" + newSrPage: 'Armazenamento (Storage)', + + // Original text: "Server" + newServerPage: 'Servidor', + + // Original text: "Import" + newImport: 'Importar', + + // Original text: "Overview" + backupOverviewPage: 'Visão Geral', + + // Original text: "New" + backupNewPage: 'Novo(a)', + + // Original text: "Remotes" + backupRemotesPage: 'Armazenamento a distância', + + // Original text: "Restore" + backupRestorePage: 'Recuperar', + + // Original text: "Schedule" + schedule: 'Agendamento', + + // Original text: "New VM backup" + newVmBackup: 'Criar novo backup VM', + + // Original text: "Edit VM backup" + editVmBackup: 'Editar backup VM', + + // Original text: "Backup" + backup: 'Backup', + + // Original text: "Rolling Snapshot" + rollingSnapshot: 'Snapshots ativos', + + // Original text: "Delta Backup" + deltaBackup: 'Backup Diferencial', + + // Original text: "Disaster Recovery" + disasterRecovery: 'Recuperação de Desastres', + + // Original text: "Continuous Replication" + continuousReplication: 'Replicação Contínua', + + // Original text: "Overview" + jobsOverviewPage: 'Visão Geral', + + // Original text: "New" + jobsNewPage: 'Novo(a)', + + // Original text: "Scheduling" + jobsSchedulingPage: 'Agendamentos', + + // Original text: "Custom Job" + customJob: 'Personalização do Trabalho', + + // Original text: 'User' + userPage: undefined, + + // Original text: 'No support' + noSupport: undefined, + + // Original text: 'Free upgrade!' + freeUpgrade: undefined, + + // Original text: "Sign out" + signOut: 'Sair', + + // Original text: 'Edit my settings {username}' + editUserProfile: undefined, + + // Original text: "Fetching data…" + homeFetchingData: 'Obtendo dados…', + + // Original text: "Welcome on Xen Orchestra!" + homeWelcome: 'Bem-vindo ao Xen Orchestra', + + // Original text: "Add your XenServer hosts or pools" + homeWelcomeText: 'Adicione seu XenServer hosts e pools', + + // Original text: "Want some help?" + homeHelp: 'Posso te ajudar?', + + // Original text: "Add server" + homeAddServer: 'Adicionar Servidor', + + // Original text: "Online Doc" + homeOnlineDoc: 'Documentação Online', + + // Original text: "Pro Support" + homeProSupport: 'Suporte Especializado', + + // Original text: "There are no VMs!" + homeNoVms: 'Não foram encontradas VMs!', + + // Original text: "Or…" + homeNoVmsOr: 'Ou…', + + // Original text: "Import VM" + homeImportVm: 'Importar VM', + + // Original text: "Import an existing VM in xva format" + homeImportVmMessage: 'Importar uma VM existente no formato XVA', + + // Original text: "Restore a backup" + homeRestoreBackup: 'Restaurar um backup', + + // Original text: "Restore a backup from a remote store" + homeRestoreBackupMessage: 'Restaurar um backup remoto', + + // Original text: "This will create a new VM" + homeNewVmMessage: 'Pronto para criar uma nova VM?', + + // Original text: "Filters" + homeFilters: 'Filtros', + + // Original text: 'No results! Click here to reset your filters' + homeNoMatches: undefined, + + // Original text: "Pool" + homeTypePool: 'Pool', + + // Original text: "Host" + homeTypeHost: 'Host', + + // Original text: "VM" + homeTypeVm: 'VM', + + // Original text: "SR" + homeTypeSr: 'SR', + + // Original text: 'Template' + homeTypeVmTemplate: undefined, + + // Original text: "Sort" + homeSort: 'Classificar', + + // Original text: "Pools" + homeAllPools: 'Pools', + + // Original text: "Hosts" + homeAllHosts: 'Hosts', + + // Original text: "Tags" + homeAllTags: 'Etiquetas', + + // Original text: "New VM" + homeNewVm: 'Criar nova VM', + + // Original text: "Running hosts" + homeFilterRunningHosts: 'Hosts Ativos', + + // Original text: "Disabled hosts" + homeFilterDisabledHosts: 'Hosts Desativados', + + // Original text: "Running VMs" + homeFilterRunningVms: 'VMs Ativas', + + // Original text: "Non running VMs" + homeFilterNonRunningVms: 'VMs Paradas', + + // Original text: "Pending VMs" + homeFilterPendingVms: 'VMs pendentes', + + // Original text: "HVM guests" + homeFilterHvmGuests: 'HVM guests', + + // Original text: "Tags" + homeFilterTags: 'Etiquetas', + + // Original text: "Sort by" + homeSortBy: 'Ordenar por', + + // Original text: "Name" + homeSortByName: 'Nome', + + // Original text: "Power state" + homeSortByPowerstate: 'Estado de energia', + + // Original text: "RAM" + homeSortByRAM: 'RAM', + + // Original text: "vCPUs" + homeSortByvCPUs: 'vCPUs', + + // Original text: 'CPUs' + homeSortByCpus: undefined, + + // Original text: '{displayed, number}x {icon} (on {total, number})' + homeDisplayedItems: undefined, + + // Original text: '{selected, number}x {icon} selected (on {total, number})' + homeSelectedItems: undefined, + + // Original text: "More" + homeMore: 'Mais', + + // Original text: "Migrate to…" + homeMigrateTo: 'Migrar para…', + + // Original text: 'Missing patches' + homeMissingPaths: undefined, + + // Original text: 'Master:' + homePoolMaster: undefined, + + // Original text: 'High Availability' + highAvailability: undefined, + + // Original text: "Add" + add: 'Adicionar', + + // Original text: "Remove" + remove: 'Remover', + + // Original text: "Preview" + preview: 'Pré-visualização', + + // Original text: "Item" + item: 'Item', + + // Original text: "No selected value" + noSelectedValue: 'Valor não selecionado', + + // Original text: "Choose user(s) and/or group(s)" + selectSubjects: 'Escolha um usuário(s) e/ou grupo(s)', + + // Original text: "Select Object(s)…" + selectObjects: 'Selecionar Objeto(s)…', + + // Original text: "Choose a role" + selectRole: 'Escolha uma função', + + // Original text: "Select Host(s)…" + selectHosts: 'Selecionar Host(s)…', + + // Original text: "Select object(s)…" + selectHostsVms: 'Selecionar Objeto(s)…', + + // Original text: "Select Network(s)…" + selectNetworks: 'Selecionar Rede(s)…', + + // Original text: "Select PIF(s)…" + selectPifs: 'Selecionar PIF(s)…', + + // Original text: "Select Pool(s)…" + selectPools: 'Selecionar Pool(s)…', + + // Original text: "Select Remote(s)…" + selectRemotes: 'Selecionar Remote(s)…', + + // Original text: 'Select resource set(s)…' + selectResourceSets: undefined, + + // Original text: 'Select template(s)…' + selectResourceSetsVmTemplate: undefined, + + // Original text: 'Select SR(s)…' + selectResourceSetsSr: undefined, + + // Original text: 'Select network(s)…' + selectResourceSetsNetwork: undefined, + + // Original text: 'Select disk(s)…' + selectResourceSetsVdi: undefined, + + // Original text: 'Select SSH key(s)…' + selectSshKey: undefined, + + // Original text: "Select SR(s)…" + selectSrs: 'Selecionar SR(s)…', + + // Original text: "Select VM(s)…" + selectVms: 'Selecionar VM(s)…', + + // Original text: "Select VM template(s)…" + selectVmTemplates: 'Selecionar VM(s) modelo(s)…', + + // Original text: "Select tag(s)…" + selectTags: 'Selecionar etiqueta(s)…', + + // Original text: "Select disk(s)…" + selectVdis: 'Selecionar disco(s)…', + + // Original text: 'Select timezone…' + selectTimezone: undefined, + + // Original text: 'Select IP(s)…' + selectIp: undefined, + + // Original text: 'Select IP pool(s)…' + selectIpPool: undefined, + + // Original text: "Fill required informations." + fillRequiredInformations: 'Preencha as informações necessárias.', + + // Original text: "Fill informations (optional)" + fillOptionalInformations: 'Preencha as informações (opcional)', + + // Original text: "Reset" + selectTableReset: 'Reiniciar', + + // Original text: "Month" + schedulingMonth: 'Agendamento Mensal', + + // Original text: "Each selected month" + schedulingEachSelectedMonth: 'Agendamento escolhido por mês', + + // Original text: "Day of the month" + schedulingMonthDay: 'Dia do mês', + + // Original text: "Each selected day" + schedulingEachSelectedMonthDay: 'Agendamento por dia selecionado', + + // Original text: "Day of the week" + schedulingWeekDay: 'Dia da semana', + + // Original text: "Each selected day" + schedulingEachSelectedWeekDay: 'Cada dia selecionado', + + // Original text: "Hour" + schedulingHour: 'Hora', + + // Original text: "Every N hour" + schedulingEveryNHour: 'Todas N horas', + + // Original text: "Each selected hour" + schedulingEachSelectedHour: 'Cada hora selecionada', + + // Original text: "Minute" + schedulingMinute: 'Minuto', + + // Original text: "Every N minute" + schedulingEveryNMinute: 'Todos N minutos', + + // Original text: "Each selected minute" + schedulingEachSelectedMinute: 'Cada minuto selecionado', + + // Original text: "Reset" + schedulingReset: 'Reiniciar', + + // Original text: "Unknown" + unknownSchedule: 'Desconhecido', + + // Original text: 'Xo-server timezone:' + timezonePickerServerValue: undefined, + + // Original text: 'Web browser timezone' + timezonePickerUseLocalTime: undefined, + + // Original text: 'Xo-server timezone' + timezonePickerUseServerTime: undefined, + + // Original text: 'Server timezone ({value})' + serverTimezoneOption: undefined, + + // Original text: 'Cron Pattern:' + cronPattern: undefined, + + // Original text: 'Cannot edit backup' + backupEditNotFoundTitle: undefined, + + // Original text: 'Missing required info for edition' + backupEditNotFoundMessage: undefined, + + // Original text: "Job" + job: 'Tarefa', + + // Original text: "Job ID" + jobId: 'ID tarefa', + + // Original text: "Name" + jobName: 'Nome', + + // Original text: 'Name of your job (forbidden: "_")' + jobNamePlaceholder: undefined, + + // Original text: "Start" + jobStart: 'Inicia', + + // Original text: "End" + jobEnd: 'Termina', + + // Original text: "Duration" + jobDuration: 'Duração', + + // Original text: "Status" + jobStatus: 'Status', + + // Original text: "Action" + jobAction: 'Ação', + + // Original text: "Tag" + jobTag: 'Etiqueta', + + // Original text: "Scheduling" + jobScheduling: 'Agendamento', + + // Original text: "State" + jobState: 'Estado', + + // Original text: 'Timezone' + jobTimezone: undefined, + + // Original text: 'xo-server' + jobServerTimezone: undefined, + + // Original text: "Run job" + runJob: 'Iniciar tarefa', + + // Original text: "One shot running started. See overview for logs." + runJobVerbose: + 'O backup manual foi executado. Clique em Visão Geral para ver os Logs', + + // Original text: "Started" + jobStarted: 'Iniciado', + + // Original text: "Finished" + jobFinished: 'Terminado', + + // Original text: "Save" + saveBackupJob: 'Salvar', + + // Original text: "Remove backup job" + deleteBackupSchedule: 'Remover tarefa de backup', + + // Original text: "Are you sure you want to delete this backup job?" + deleteBackupScheduleQuestion: + 'Você tem certeza que você quer deletar esta tarefa de backup?', + + // Original text: "Enable immediately after creation" + scheduleEnableAfterCreation: 'Ativar imediatamente após criação', + + // Original text: "You are editing Schedule {name} ({id}). Saving will override previous schedule state." + scheduleEditMessage: + 'Você esta editando o Agendamento {name} ({id}). Este procedimento irá substituir o agendamento atual.', + + // Original text: "You are editing job {name} ({id}). Saving will override previous job state." + jobEditMessage: + 'Você esta editando a Tarefa {name} ({id}). Este procedimento irá substituir a tarefa atual.', + + // Original text: "No scheduled jobs." + noScheduledJobs: 'Sem agendamentos', + + // Original text: "No jobs found." + noJobs: 'Tarefas não encontradas', + + // Original text: "No schedules found" + noSchedules: 'Nenhum agendamento foi encontrado', + + // Original text: "Select a xo-server API command" + jobActionPlaceHolder: 'Selecione um comando para xo-server API', + + // Original text: 'Schedules' + jobSchedules: undefined, + + // Original text: 'Name of your schedule' + jobScheduleNamePlaceHolder: undefined, + + // Original text: 'Select a Job' + jobScheduleJobPlaceHolder: undefined, + + // Original text: "Select your backup type:" + newBackupSelection: 'Selecione seu tipo de backup', + + // Original text: 'Select backup mode:' + smartBackupModeSelection: undefined, + + // Original text: 'Normal backup' + normalBackup: undefined, + + // Original text: 'Smart backup' + smartBackup: undefined, + + // Original text: 'Local remote selected' + localRemoteWarningTitle: undefined, + + // Original text: 'Warning: local remotes will use limited XOA disk space. Only for advanced users.' + localRemoteWarningMessage: undefined, + + // Original text: 'VMs' + editBackupVmsTitle: undefined, + + // Original text: 'VMs statuses' + editBackupSmartStatusTitle: undefined, + + // Original text: 'Resident on' + editBackupSmartResidentOn: undefined, + + // Original text: 'VMs Tags' + editBackupSmartTagsTitle: undefined, + + // Original text: 'Tag' + editBackupTagTitle: undefined, + + // Original text: 'Report' + editBackupReportTitle: undefined, + + // Original text: 'Enable immediately after creation' + editBackupScheduleEnabled: undefined, + + // Original text: 'Depth' + editBackupDepthTitle: undefined, + + // Original text: 'Remote' + editBackupRemoteTitle: undefined, + + // Original text: "Remote stores for backup" + remoteList: 'Backups remotos', + + // Original text: "New File System Remote" + newRemote: 'Novo Arquivo de Sistema Remoto', + + // Original text: "Local" + remoteTypeLocal: 'Local', + + // Original text: "NFS" + remoteTypeNfs: 'NFS', + + // Original text: "SMB" + remoteTypeSmb: 'SMB', + + // Original text: "Type" + remoteType: 'Type', + + // Original text: 'Test your remote' + remoteTestTip: undefined, + + // Original text: 'Test Remote' + testRemote: undefined, + + // Original text: 'Test failed for {name}' + remoteTestFailure: undefined, + + // Original text: 'Test passed for {name}' + remoteTestSuccess: undefined, + + // Original text: 'Error' + remoteTestError: undefined, + + // Original text: 'Test Step' + remoteTestStep: undefined, + + // Original text: 'Test file' + remoteTestFile: undefined, + + // Original text: 'The remote appears to work correctly' + remoteTestSuccessMessage: undefined, + + // Original text: 'Name' + remoteName: undefined, + + // Original text: 'Path' + remotePath: undefined, + + // Original text: 'State' + remoteState: undefined, + + // Original text: 'Device' + remoteDevice: undefined, + + // Original text: 'Share' + remoteShare: undefined, + + // Original text: 'Auth' + remoteAuth: undefined, + + // Original text: 'Mounted' + remoteMounted: undefined, + + // Original text: 'Unmounted' + remoteUnmounted: undefined, + + // Original text: 'Connect' + remoteConnectTip: undefined, + + // Original text: 'Disconnect' + remoteDisconnectTip: undefined, + + // Original text: 'Delete' + remoteDeleteTip: undefined, + + // Original text: 'remote name *' + remoteNamePlaceHolder: undefined, + + // Original text: 'Name *' + remoteMyNamePlaceHolder: undefined, + + // Original text: '/path/to/backup' + remoteLocalPlaceHolderPath: undefined, + + // Original text: 'host *' + remoteNfsPlaceHolderHost: undefined, + + // Original text: '/path/to/backup' + remoteNfsPlaceHolderPath: undefined, + + // Original text: 'subfolder [path\\to\\backup]' + remoteSmbPlaceHolderRemotePath: undefined, + + // Original text: 'Username' + remoteSmbPlaceHolderUsername: undefined, + + // Original text: 'Password' + remoteSmbPlaceHolderPassword: undefined, + + // Original text: 'Domain' + remoteSmbPlaceHolderDomain: undefined, + + // Original text: '
\\ *' + remoteSmbPlaceHolderAddressShare: undefined, + + // Original text: 'password(fill to edit)' + remotePlaceHolderPassword: undefined, + + // Original text: 'Create a new SR' + newSrTitle: undefined, + + // Original text: "General" + newSrGeneral: 'Geral', + + // Original text: "Select Storage Type:" + newSrTypeSelection: 'Selecionar o tipo de armazenamento (storage)', + + // Original text: "Settings" + newSrSettings: 'Configurações', + + // Original text: "Storage Usage" + newSrUsage: 'Uso de armazenamento (storage)', + + // Original text: "Summary" + newSrSummary: 'Sumário', + + // Original text: "Host" + newSrHost: 'Host', + + // Original text: "Type" + newSrType: 'Tipo', + + // Original text: "Name" + newSrName: 'Nome', + + // Original text: "Description" + newSrDescription: 'Descrição', + + // Original text: "Server" + newSrServer: 'Servidor', + + // Original text: "Path" + newSrPath: 'Caminho', + + // Original text: "IQN" + newSrIqn: 'IQN', + + // Original text: "LUN" + newSrLun: 'LUN', + + // Original text: "with auth." + newSrAuth: 'Com autenticação', + + // Original text: "User Name" + newSrUsername: 'Nome de Usuário', + + // Original text: "Password" + newSrPassword: 'Senha', + + // Original text: "Device" + newSrDevice: 'Dispositivo', + + // Original text: "in use" + newSrInUse: 'Em uso', + + // Original text: "Size" + newSrSize: 'Tamanho', + + // Original text: "Create" + newSrCreate: 'Criar', + + // Original text: 'Storage name' + newSrNamePlaceHolder: undefined, + + // Original text: 'Storage description' + newSrDescPlaceHolder: undefined, + + // Original text: 'Address' + newSrAddressPlaceHolder: undefined, + + // Original text: '[port]' + newSrPortPlaceHolder: undefined, + + // Original text: 'Username' + newSrUsernamePlaceHolder: undefined, + + // Original text: 'Password' + newSrPasswordPlaceHolder: undefined, + + // Original text: 'Device, e.g /dev/sda…' + newSrLvmDevicePlaceHolder: undefined, + + // Original text: '/path/to/directory' + newSrLocalPathPlaceHolder: undefined, + + // Original text: "Users/Groups" + subjectName: 'Usuários/Grupos', + + // Original text: "Object" + objectName: 'Objeto', + + // Original text: 'No acls found' + aclNoneFound: undefined, + + // Original text: "Role" + roleName: 'Função', + + // Original text: 'Create' + aclCreate: undefined, + + // Original text: "New Group Name" + newGroupName: 'Novo Nome de Grupo', + + // Original text: "Create Group" + createGroup: 'Criar Grupo', + + // Original text: "Create" + createGroupButton: 'Criar', + + // Original text: "Delete Group" + deleteGroup: 'Deletar Grupo', + + // Original text: "Are you sure you want to delete this group?" + deleteGroupConfirm: 'Você tem certeza que deseja deletar este grupo?', + + // Original text: "Remove user from Group" + removeUserFromGroup: 'Remover usuário do Grupo', + + // Original text: "Are you sure you want to delete this user?" + deleteUserConfirm: 'Você tem certeza que deseja deletar este usuário?', + + // Original text: 'Delete User' + deleteUser: undefined, + + // Original text: 'no user' + noUser: undefined, + + // Original text: "unknown user" + unknownUser: 'Usuário desconhecido', + + // Original text: "No group found" + noGroupFound: 'Grupo não encontrado', + + // Original text: "Name" + groupNameColumn: 'Nome', + + // Original text: "Users" + groupUsersColumn: 'Usuários', + + // Original text: "Add User" + addUserToGroupColumn: 'Adicionar Usuário', + + // Original text: "Email" + userNameColumn: 'e-mail', + + // Original text: "Permissions" + userPermissionColumn: 'Permissões', + + // Original text: "Password" + userPasswordColumn: 'Senha', + + // Original text: "Email" + userName: 'e-mail', + + // Original text: "Password" + userPassword: 'Senha', + + // Original text: "Create" + createUserButton: 'Criar', + + // Original text: "No user found" + noUserFound: 'Usuário não encontrado', + + // Original text: "User" + userLabel: 'Usuário', + + // Original text: "Admin" + adminLabel: 'Administrador', + + // Original text: "No user in group" + noUserInGroup: 'Nenhum usuário neste grupo', + + // Original text: "{users} user{users, plural, one {} other {s}}" + countUsers: '{users} user{users, plural, one {} other {s}}', + + // Original text: "Select Permission" + selectPermission: 'Selecionar Permissão', + + // Original text: "Auto-load at server start" + autoloadPlugin: 'Carregamento automático na inicialização do servidor', + + // Original text: "Save configuration" + savePluginConfiguration: 'Salvar configuração', + + // Original text: "Delete configuration" + deletePluginConfiguration: 'Deletar configuração', + + // Original text: "Plugin error" + pluginError: 'Erro Plugin', + + // Original text: "Unknown error" + unknownPluginError: 'Erro desconhecido', + + // Original text: "Purge plugin configuration" + purgePluginConfiguration: 'Configuração de limpeza do plugin', + + // Original text: "Are you sure you want to purge this configuration ?" + purgePluginConfigurationQuestion: + 'Você tem certeza que deseja executar esta configuração?', + + // Original text: "Edit" + editPluginConfiguration: 'Editar', + + // Original text: "Cancel" + cancelPluginEdition: 'Cancelar', + + // Original text: "Plugin configuration" + pluginConfigurationSuccess: 'Configuração do Plugin', + + // Original text: "Plugin configuration successfully saved!" + pluginConfigurationChanges: + 'Configuração do plugin foi efetuada com sucesso!', + + // Original text: 'Predefined configuration' + pluginConfigurationPresetTitle: undefined, + + // Original text: 'Choose a predefined configuration.' + pluginConfigurationChoosePreset: undefined, + + // Original text: 'Apply' + applyPluginPreset: undefined, + + // Original text: 'Save filter error' + saveNewUserFilterErrorTitle: undefined, + + // Original text: 'Bad parameter: name must be given.' + saveNewUserFilterErrorBody: undefined, + + // Original text: 'Name:' + filterName: undefined, + + // Original text: 'Value:' + filterValue: undefined, + + // Original text: 'Save new filter' + saveNewFilterTitle: undefined, + + // Original text: 'Set custom filters' + setUserFiltersTitle: undefined, + + // Original text: 'Are you sure you want to set custom filters?' + setUserFiltersBody: undefined, + + // Original text: 'Remove custom filter' + removeUserFilterTitle: undefined, + + // Original text: 'Are you sure you want to remove custom filter?' + removeUserFilterBody: undefined, + + // Original text: 'Default filter' + defaultFilter: undefined, + + // Original text: 'Default filters' + defaultFilters: undefined, + + // Original text: 'Custom filters' + customFilters: undefined, + + // Original text: 'Customize filters' + customizeFilters: undefined, + + // Original text: 'Save custom filters' + saveCustomFilters: undefined, + + // Original text: "Start" + startVmLabel: 'Iniciar', + + // Original text: "Recovery start" + recoveryModeLabel: 'Iniciar recuperação', + + // Original text: "Suspend" + suspendVmLabel: 'Suspender', + + // Original text: "Stop" + stopVmLabel: 'Parar', + + // Original text: "Force shutdown" + forceShutdownVmLabel: 'Forçar desligamento', + + // Original text: "Reboot" + rebootVmLabel: 'Reiniciar', + + // Original text: "Force reboot" + forceRebootVmLabel: 'Forçar reinicialização', + + // Original text: "Delete" + deleteVmLabel: 'Deletar', + + // Original text: "Migrate" + migrateVmLabel: 'Migrar', + + // Original text: "Snapshot" + snapshotVmLabel: 'Snapshot', + + // Original text: "Export" + exportVmLabel: 'Exportar', + + // Original text: "Resume" + resumeVmLabel: 'Continuar', + + // Original text: "Copy" + copyVmLabel: 'Copiar', + + // Original text: "Clone" + cloneVmLabel: 'Clonar', + + // Original text: "Fast clone" + fastCloneVmLabel: 'Clonagem rápida', + + // Original text: "Convert to template" + convertVmToTemplateLabel: 'Convertir para template', + + // Original text: "Console" + vmConsoleLabel: 'Console', + + // Original text: "Rescan all disks" + srRescan: 'Examinar novamente todos os discos', + + // Original text: "Connect to all hosts" + srReconnectAll: 'Conectar-se a todos os hosts', + + // Original text: "Disconnect to all hosts" + srDisconnectAll: 'Desconectar-se de todos os hosts', + + // Original text: "Forget this SR" + srForget: 'Esquecer esta SR', + + // Original text: "Remove this SR" + srRemoveButton: 'Remover esta SR', + + // Original text: "No VDIs in this storage" + srNoVdis: 'Nenhuma VDI neste armazenamento', + + // Original text: 'Pool RAM usage:' + poolTitleRamUsage: undefined, + + // Original text: '{used} used on {total}' + poolRamUsage: undefined, + + // Original text: 'Master:' + poolMaster: undefined, + + // Original text: "Hosts" + hostsTabName: 'Hosts', + + // Original text: "High Availability" + poolHaStatus: 'Alta Disponibilidade', + + // Original text: "Enabled" + poolHaEnabled: 'Habilitado', + + // Original text: "Disabled" + poolHaDisabled: 'Desativado', + + // Original text: "Name" + hostNameLabel: 'Nome', + + // Original text: "Description" + hostDescription: 'Descrição', + + // Original text: "Memory" + hostMemory: 'Memória', + + // Original text: "No hosts" + noHost: 'Nenhum host', + + // Original text: '{used}% used ({free} free)' + memoryLeftTooltip: undefined, + + // Original text: "Name" + poolNetworkNameLabel: 'Nome', + + // Original text: "Description" + poolNetworkDescription: 'Descrição', + + // Original text: "PIFs" + poolNetworkPif: 'PIFs', + + // Original text: "No networks" + poolNoNetwork: 'Nenhuma Rede', + + // Original text: "MTU" + poolNetworkMTU: 'MTU', + + // Original text: "Connected" + poolNetworkPifAttached: 'Conectado', + + // Original text: "Disconnected" + poolNetworkPifDetached: 'Desconectado', + + // Original text: 'Show PIFs' + showPifs: undefined, + + // Original text: 'Hide PIFs' + hidePifs: undefined, + + // Original text: "Add SR" + addSrLabel: 'Adicionar SR', + + // Original text: "Add VM" + addVmLabel: 'Adicionar VM', + + // Original text: "Add Host" + addHostLabel: 'Adicionar Host', + + // Original text: "Disconnect" + disconnectServer: 'Desconectar', + + // Original text: "Start" + startHostLabel: 'Iniciar', + + // Original text: "Stop" + stopHostLabel: 'Parar', + + // Original text: "Enable" + enableHostLabel: 'Habilitar', + + // Original text: "Disable" + disableHostLabel: 'Desabilitar', + + // Original text: "Restart toolstack" + restartHostAgent: 'Reiniciar toolstack', + + // Original text: "Force reboot" + forceRebootHostLabel: 'Forçar reinicialização', + + // Original text: "Reboot" + rebootHostLabel: 'Reinicializar', + + // Original text: 'Reboot to apply updates' + rebootUpdateHostLabel: undefined, + + // Original text: "Emergency mode" + emergencyModeLabel: 'Modo de emergência', + + // Original text: "Storage" + storageTabName: 'Armazenamento', + + // Original text: "Patches" + patchesTabName: 'Correções', + + // Original text: "Load average" + statLoad: 'Carregar média', + + // Original text: "Hardware" + hardwareHostSettingsLabel: 'Hardware', + + // Original text: "Address" + hostAddress: 'Endereço', + + // Original text: "Status" + hostStatus: 'Status', + + // Original text: "Build number" + hostBuildNumber: 'Número de compilação', + + // Original text: "iSCSI name" + hostIscsiName: 'Nome iSCSI', + + // Original text: "Version" + hostXenServerVersion: 'Versão', + + // Original text: "Enabled" + hostStatusEnabled: 'Ativado', + + // Original text: "Disabled" + hostStatusDisabled: 'Desativado', + + // Original text: "Power on mode" + hostPowerOnMode: 'Modo de energia', + + // Original text: "Host uptime" + hostStartedSince: 'Tempo de atividade do Host', + + // Original text: "Toolstack uptime" + hostStackStartedSince: 'Tempo de atividade Toolstack', + + // Original text: "CPU model" + hostCpusModel: 'Modelo CPU', + + // Original text: "Core (socket)" + hostCpusNumber: 'Núcleo (soquete)', + + // Original text: "Manufacturer info" + hostManufacturerinfo: 'Informações do Fabricante', + + // Original text: "BIOS info" + hostBiosinfo: 'Informações BIOS', + + // Original text: "Licence" + licenseHostSettingsLabel: 'Licença', + + // Original text: "Type" + hostLicenseType: 'Tipo', + + // Original text: "Socket" + hostLicenseSocket: 'Soquete', + + // Original text: "Expiry" + hostLicenseExpiry: 'Expiração', + + // Original text: "Add a network" + networkCreateButton: 'Adicionar a Rede', + + // Original text: 'Add a bonded network' + networkCreateBondedButton: undefined, + + // Original text: "Device" + pifDeviceLabel: 'Dispositivo', + + // Original text: "Network" + pifNetworkLabel: 'Rede', + + // Original text: "VLAN" + pifVlanLabel: 'VLAN', + + // Original text: "Address" + pifAddressLabel: 'Endereço', + + // Original text: 'Mode' + pifModeLabel: undefined, + + // Original text: "MAC" + pifMacLabel: 'MAC', + + // Original text: "MTU" + pifMtuLabel: 'MTU', + + // Original text: "Status" + pifStatusLabel: 'Status', + + // Original text: "Connected" + pifStatusConnected: 'Conectado', + + // Original text: "Disconnected" + pifStatusDisconnected: 'Desconectado', + + // Original text: "No physical interface detected" + pifNoInterface: 'Nenhuma interface física foi detectada', + + // Original text: 'This interface is currently in use' + pifInUse: undefined, + + // Original text: 'Default locking mode' + defaultLockingMode: undefined, + + // Original text: 'Configure IP address' + pifConfigureIp: undefined, + + // Original text: 'Invalid parameters' + configIpErrorTitle: undefined, + + // Original text: 'IP address and netmask required' + configIpErrorMessage: undefined, + + // Original text: 'Static IP address' + staticIp: undefined, + + // Original text: 'Netmask' + netmask: undefined, + + // Original text: 'DNS' + dns: undefined, + + // Original text: 'Gateway' + gateway: undefined, + + // Original text: "Add a storage" + addSrDeviceButton: 'Adicionar um armazenamento', + + // Original text: "Name" + srNameLabel: 'Nome', + + // Original text: "Type" + srType: 'Tipo', + + // Original text: "Status" + pbdStatus: 'Status', + + // Original text: "Connected" + pbdStatusConnected: 'Conectado', + + // Original text: "Disconnected" + pbdStatusDisconnected: 'Desconectado', + + // Original text: 'Connect' + pbdConnect: undefined, + + // Original text: 'Disconnect' + pbdDisconnect: undefined, + + // Original text: 'Forget' + pbdForget: undefined, + + // Original text: "Shared" + srShared: 'Compartilhado', + + // Original text: "Not shared" + srNotShared: 'Não compartilhado', + + // Original text: "No storage detected" + pbdNoSr: 'Nenhum armazenamento detectado', + + // Original text: "Name" + patchNameLabel: 'Nome', + + // Original text: "Install all patches" + patchUpdateButton: 'Instalar todas as correções', + + // Original text: "Description" + patchDescription: 'Descrição', + + // Original text: "Applied date" + patchApplied: 'Data de lançamento', + + // Original text: "Size" + patchSize: 'Tamanho', + + // Original text: "Status" + patchStatus: 'Status', + + // Original text: "Applied" + patchStatusApplied: 'Aplicado', + + // Original text: "Missing patches" + patchStatusNotApplied: 'Correções faltando', + + // Original text: "No patch detected" + patchNothing: 'Nenhuma correção foi detectada', + + // Original text: "Release date" + patchReleaseDate: 'Data de lançamento', + + // Original text: "Guidance" + patchGuidance: 'Direção', + + // Original text: "Action" + patchAction: 'Ação', + + // Original text: 'Applied patches' + hostAppliedPatches: undefined, + + // Original text: "Missing patches" + hostMissingPatches: 'Correções faltando', + + // Original text: "Host up-to-date!" + hostUpToDate: 'Host pronto para atualizar!', + + // Original text: 'Refresh patches' + refreshPatches: undefined, + + // Original text: 'Install pool patches' + installPoolPatches: undefined, + + // Original text: 'Default SR' + defaultSr: undefined, + + // Original text: 'Set as default SR' + setAsDefaultSr: undefined, + + // Original text: "General" + generalTabName: 'Geral', + + // Original text: "Stats" + statsTabName: 'Estatísticas', + + // Original text: "Console" + consoleTabName: 'Console', + + // Original text: 'Container' + containersTabName: undefined, + + // Original text: "Snapshots" + snapshotsTabName: 'Snapshots', + + // Original text: "Logs" + logsTabName: 'Logs', + + // Original text: "Advanced" + advancedTabName: 'Avançado', + + // Original text: "Network" + networkTabName: 'Rede', + + // Original text: "Disk{disks, plural, one {} other {s}}" + disksTabName: 'Disco{disks, plural, one {} other {s}}', + + // Original text: "halted" + powerStateHalted: 'Interrompido', + + // Original text: "running" + powerStateRunning: 'Executando', + + // Original text: "suspended" + powerStateSuspended: 'Suspendido', + + // Original text: "No Xen tools detected" + vmStatus: 'Nenhum Xen tools foi detectado', + + // Original text: "No IPv4 record" + vmName: 'Nenhum registro IPv4', + + // Original text: "No IP record" + vmDescription: 'Nenhum registro IP', + + // Original text: "Started {ago}" + vmSettings: 'Iniciado {ago}', + + // Original text: "Current status:" + vmCurrentStatus: 'Status atual', + + // Original text: "Not running" + vmNotRunning: 'Parado', + + // Original text: "No Xen tools detected" + noToolsDetected: 'Nenhum Xen tools foi detectado', + + // Original text: "No IPv4 record" + noIpv4Record: 'Nenhum registro IPv4', + + // Original text: "No IP record" + noIpRecord: 'Nenhum registro IP', + + // Original text: "Started {ago}" + started: 'Iniciado {ago}', + + // Original text: "Paravirtualization (PV)" + paraVirtualizedMode: 'Paravirtualização', + + // Original text: "Hardware virtualization (HVM)" + hardwareVirtualizedMode: 'Virtualização de Hadware (HVM)', + + // Original text: "CPU usage" + statsCpu: 'Uso de CPU', + + // Original text: "Memory usage" + statsMemory: 'Uso de Memória', + + // Original text: "Network throughput" + statsNetwork: 'Taxa de transferência de Rede', + + // Original text: 'Stacked values' + useStackedValuesOnStats: undefined, + + // Original text: "Disk throughput" + statDisk: 'Taxa de transferência de Disco', + + // Original text: "Last 10 minutes" + statLastTenMinutes: 'Últimos 10 minutos', + + // Original text: "Last 2 hours" + statLastTwoHours: 'Últimas 2 horas', + + // Original text: "Last week" + statLastWeek: 'Semana passada', + + // Original text: "Last year" + statLastYear: 'Ano passado', + + // Original text: "Copy" + copyToClipboardLabel: 'Copiar', + + // Original text: "Ctrl+Alt+Del" + ctrlAltDelButtonLabel: 'Ctrl+Alt+Del', + + // Original text: "Tip:" + tipLabel: 'Dica', + + // Original text: "non-US keyboard could have issues with console: switch your own layout to US." + tipConsoleLabel: + 'Teclados fora do padrão US-Keyboard podem apresentar problemas com o console: Altere seu teclado e verifique!', + + // Original text: 'Hide infos' + hideHeaderTooltip: undefined, + + // Original text: 'Show infos' + showHeaderTooltip: undefined, + + // Original text: 'Name' + containerName: undefined, + + // Original text: 'Command' + containerCommand: undefined, + + // Original text: 'Creation date' + containerCreated: undefined, + + // Original text: 'Status' + containerStatus: undefined, + + // Original text: 'Action' + containerAction: undefined, + + // Original text: 'No existing containers' + noContainers: undefined, + + // Original text: 'Stop this container' + containerStop: undefined, + + // Original text: 'Start this container' + containerStart: undefined, + + // Original text: 'Pause this container' + containerPause: undefined, + + // Original text: 'Resume this container' + containerResume: undefined, + + // Original text: 'Restart this container' + containerRestart: undefined, + + // Original text: "Action" + vdiAction: 'Ação', + + // Original text: "Attach disk" + vdiAttachDeviceButton: 'Anexar disco', + + // Original text: "New disk" + vbdCreateDeviceButton: 'Novo disco', + + // Original text: "Boot order" + vdiBootOrder: 'Ordem de boot', + + // Original text: "Name" + vdiNameLabel: 'Nome', + + // Original text: "Description" + vdiNameDescription: 'Descrição', + + // Original text: "Tags" + vdiTags: 'Etiquetas', + + // Original text: "Size" + vdiSize: 'Tamanho', + + // Original text: "SR" + vdiSr: 'SR', + + // Original text: 'VM' + vdiVm: undefined, + + // Original text: 'Migrate VDI' + vdiMigrate: undefined, + + // Original text: 'Destination SR:' + vdiMigrateSelectSr: undefined, + + // Original text: 'Migrate all VDIs' + vdiMigrateAll: undefined, + + // Original text: 'No SR' + vdiMigrateNoSr: undefined, + + // Original text: 'A target SR is required to migrate a VDI' + vdiMigrateNoSrMessage: undefined, + + // Original text: 'Forget' + vdiForget: undefined, + + // Original text: 'Remove VDI' + vdiRemove: undefined, + + // Original text: "Boot flag" + vbdBootableStatus: 'Indicador de inicialização', + + // Original text: "Status" + vbdStatus: 'Status', + + // Original text: "Connected" + vbdStatusConnected: 'Conectado', + + // Original text: "Disconnected" + vbdStatusDisconnected: 'Desconectado', + + // Original text: "No disks" + vbdNoVbd: 'Nenhum disco encontrado', + + // Original text: 'Connect VBD' + vbdConnect: undefined, + + // Original text: 'Disconnect VBD' + vbdDisconnect: undefined, + + // Original text: 'Bootable' + vbdBootable: undefined, + + // Original text: 'Readonly' + vbdReadonly: undefined, + + // Original text: 'Create' + vbdCreate: undefined, + + // Original text: 'Disk name' + vbdNamePlaceHolder: undefined, + + // Original text: 'Size' + vbdSizePlaceHolder: undefined, + + // Original text: 'Save' + saveBootOption: undefined, + + // Original text: 'Reset' + resetBootOption: undefined, + + // Original text: "New device" + vifCreateDeviceButton: 'Novo dispositivo', + + // Original text: "No interface" + vifNoInterface: 'Nenhuma interface encontrada', + + // Original text: "Device" + vifDeviceLabel: 'Dispositivo', + + // Original text: "MAC address" + vifMacLabel: 'Endereço MAC', + + // Original text: "MTU" + vifMtuLabel: 'MTU', + + // Original text: "Network" + vifNetworkLabel: 'Rede', + + // Original text: "Status" + vifStatusLabel: 'Status', + + // Original text: "Connected" + vifStatusConnected: 'Conectado', + + // Original text: "Disconnected" + vifStatusDisconnected: 'Desconectado', + + // Original text: 'Connect' + vifConnect: undefined, + + // Original text: 'Disconnect' + vifDisconnect: undefined, + + // Original text: 'Remove' + vifRemove: undefined, + + // Original text: "IP addresses" + vifIpAddresses: 'Endereço IP', + + // Original text: 'Auto-generated if empty' + vifMacAutoGenerate: undefined, + + // Original text: 'Allowed IPs' + vifAllowedIps: undefined, + + // Original text: 'No IPs' + vifNoIps: undefined, + + // Original text: 'Network locked' + vifLockedNetwork: undefined, + + // Original text: 'Network locked and no IPs are allowed for this interface' + vifLockedNetworkNoIps: undefined, + + // Original text: 'Network not locked' + vifUnLockedNetwork: undefined, + + // Original text: 'Unknown network' + vifUnknownNetwork: undefined, + + // Original text: 'Create' + vifCreate: undefined, + + // Original text: "No snapshots" + noSnapshots: 'Nenhum snapshot encontrado', + + // Original text: "New snapshot" + snapshotCreateButton: 'Novo snapshot', + + // Original text: "Just click on the snapshot button to create one!" + tipCreateSnapshotLabel: 'Clique sobre o botão snapshop para criar!', + + // Original text: 'Revert VM to this snapshot' + revertSnapshot: undefined, + + // Original text: 'Remove this snapshot' + deleteSnapshot: undefined, + + // Original text: 'Create a VM from this snapshot' + copySnapshot: undefined, + + // Original text: 'Export this snapshot' + exportSnapshot: undefined, + + // Original text: "Creation date" + snapshotDate: 'Data de criação', + + // Original text: "Name" + snapshotName: 'Nome', + + // Original text: "Action" + snapshotAction: 'Ação', + + // Original text: "Remove all logs" + logRemoveAll: 'Remover todos os logs', + + // Original text: "No logs so far" + noLogs: 'Sem registros até o momento', + + // Original text: "Creation date" + logDate: 'Data de criação', + + // Original text: "Name" + logName: 'Nome', + + // Original text: "Content" + logContent: 'Conteúdo', + + // Original text: "Action" + logAction: 'Ação', + + // Original text: "Remove" + vmRemoveButton: 'Remover', + + // Original text: "Convert" + vmConvertButton: 'Converter', + + // Original text: "Xen settings" + xenSettingsLabel: 'Configurações Xen', + + // Original text: "Guest OS" + guestOsLabel: 'Convidado OS', + + // Original text: "Misc" + miscLabel: 'Misc', + + // Original text: "UUID" + uuid: 'UUID', + + // Original text: "Virtualization mode" + virtualizationMode: 'Modo de virtualização', + + // Original text: "CPU weight" + cpuWeightLabel: 'Carga da CPU', + + // Original text: "Default ({value, number})" + defaultCpuWeight: 'Padrão', + + // Original text: 'CPU cap' + cpuCapLabel: undefined, + + // Original text: 'Default ({value, number})' + defaultCpuCap: undefined, + + // Original text: "PV args" + pvArgsLabel: 'PV argos', + + // Original text: "Xen tools status" + xenToolsStatus: 'Status de ferramentas Xen', + + // Original text: "{status}" + xenToolsStatusValue: '{status}', + + // Original text: "OS name" + osName: 'Nome OS', + + // Original text: "OS kernel" + osKernel: 'OS kernel (núcleo)', + + // Original text: "Auto power on" + autoPowerOn: 'Ligar automaticamente', + + // Original text: "HA" + ha: 'HA', + + // Original text: "Original template" + originalTemplate: 'Modelo original (template)', + + // Original text: "Unknown" + unknownOsName: 'Desconhecido', + + // Original text: "Unknown" + unknownOsKernel: 'Desconhecido', + + // Original text: "Unknown" + unknownOriginalTemplate: 'Desconhecido', + + // Original text: "VM limits" + vmLimitsLabel: 'Limites de VM', + + // Original text: "CPU limits" + vmCpuLimitsLabel: 'Limites de CPU', + + // Original text: "Memory limits (min/max)" + vmMemoryLimitsLabel: 'Limites de memória (min/max)', + + // Original text: "vCPUs max:" + vmMaxVcpus: 'máximo', + + // Original text: "Memory max:" + vmMaxRam: 'Limite máximo de memória', + + // Original text: "Long click to add a name" + vmHomeNamePlaceholder: 'Faça um longo clique para adicionar um nome', + + // Original text: "Long click to add a description" + vmHomeDescriptionPlaceholder: + 'Faça um longo clique para adicionar uma descrição', + + // Original text: "Click to add a name" + vmViewNamePlaceholder: 'Clique para adicionar um nome', + + // Original text: "Click to add a description" + vmViewDescriptionPlaceholder: 'Clique para adicionar uma descrição', + + // Original text: 'Click to add a name' + templateHomeNamePlaceholder: undefined, + + // Original text: 'Click to add a description' + templateHomeDescriptionPlaceholder: undefined, + + // Original text: 'Delete template' + templateDelete: undefined, + + // Original text: 'Delete VM template{templates, plural, one {} other {s}}' + templateDeleteModalTitle: undefined, + + // Original text: 'Are you sure you want to delete {templates, plural, one {this} other {these}} template{templates, plural, one {} other {s}}?' + templateDeleteModalBody: undefined, + + // Original text: "Pool{pools, plural, one {} other {s}}" + poolPanel: 'Pool{pools, plural, one {} other {s}}', + + // Original text: "Host{hosts, plural, one {} other {s}}" + hostPanel: 'Host{hosts, plural, one {} other {s}}', + + // Original text: "VM{vms, plural, one {} other {s}}" + vmPanel: 'VM{vms, plural, one {} other {s}}', + + // Original text: "RAM Usage" + memoryStatePanel: 'Utilização RAM', + + // Original text: "CPUs Usage" + cpuStatePanel: 'Utilização de CPU', + + // Original text: "VMs Power state" + vmStatePanel: 'Estado de energia das VMs', + + // Original text: "Pending tasks" + taskStatePanel: 'Tarefas pendentes', + + // Original text: "Users" + usersStatePanel: 'Usuários', + + // Original text: "Storage state" + srStatePanel: 'Data do armazenamento (storage)', + + // Original text: "{usage} (of {total})" + ofUsage: 'de', + + // Original text: "No storage" + noSrs: 'Nenhum armazenamento (storage)', + + // Original text: "Name" + srName: 'Nome', + + // Original text: "Pool" + srPool: 'Pool', + + // Original text: "Host" + srHost: 'Host', + + // Original text: "Type" + srFormat: 'Tipo', + + // Original text: "Size" + srSize: 'Tamanho', + + // Original text: "Usage" + srUsage: 'Utilização', + + // Original text: "used" + srUsed: 'Usado', + + // Original text: "free" + srFree: 'Livre', + + // Original text: "Storage Usage" + srUsageStatePanel: 'Utilização atual de armazenamento', + + // Original text: "Top 5 SR Usage (in %)" + srTopUsageStatePanel: 'Top 5 de Utilização SR (em %)', + + // Original text: '{running} running ({halted} halted)' + vmsStates: undefined, + + // Original text: 'Clear selection' + dashboardStatsButtonRemoveAll: undefined, + + // Original text: 'Add all hosts' + dashboardStatsButtonAddAllHost: undefined, + + // Original text: 'Add all VMs' + dashboardStatsButtonAddAllVM: undefined, + + // Original text: "{value} {date, date, medium}" + weekHeatmapData: '{value} {date, date, medium}', + + // Original text: "No data." + weekHeatmapNoData: 'Nenhum dado encontrado', + + // Original text: 'Weekly Heatmap' + weeklyHeatmap: undefined, + + // Original text: 'Weekly Charts' + weeklyCharts: undefined, + + // Original text: 'Synchronize scale:' + weeklyChartsScaleInfo: undefined, + + // Original text: "Stats error" + statsDashboardGenericErrorTitle: 'Erro de estatísticas', + + // Original text: "There is no stats available for:" + statsDashboardGenericErrorMessage: 'Não há estatísticas disponíveis para:', + + // Original text: "No selected metric" + noSelectedMetric: 'Nenhuma métrica selecionada', + + // Original text: "Select" + statsDashboardSelectObjects: 'Selecionar', + + // Original text: "Loading…" + metricsLoading: 'Carregando…', + + // Original text: "Coming soon!" + comingSoon: 'Em breve!', + + // Original text: "Orphaned snapshot VDIs" + orphanedVdis: 'VDI órfãs', + + // Original text: "Orphaned VMs snapshot" + orphanedVms: 'VMs órfãs', + + // Original text: "No orphans" + noOrphanedObject: 'Sem órfãs', + + // Original text: "Remove all orphaned snapshot VDIs" + removeAllOrphanedObject: 'Remover todos as VDIs órfãs', + + // Original text: "Name" + vmNameLabel: 'Nome', + + // Original text: "Description" + vmNameDescription: 'Descrição', + + // Original text: "Resident on" + vmContainer: 'Residente em', + + // Original text: "Alarms" + alarmMessage: 'Alarmes', + + // Original text: "No alarms" + noAlarms: 'Sem alarmes', + + // Original text: "Date" + alarmDate: 'Data', + + // Original text: "Content" + alarmContent: 'Conteúdo', + + // Original text: "Issue on" + alarmObject: 'Tipo de alarme', + + // Original text: "Pool" + alarmPool: 'Pool', + + // Original text: "Remove all alarms" + alarmRemoveAll: 'Remover todos os alarmes', + + // Original text: '{used}% used ({free} left)' + spaceLeftTooltip: undefined, + + // Original text: "Create a new VM on {select}" + newVmCreateNewVmOn: 'Criar uma nova VM em {pool}', + + // Original text: 'Create a new VM on {select1} or {select2}' + newVmCreateNewVmOn2: undefined, + + // Original text: 'You have no permission to create a VM' + newVmCreateNewVmNoPermission: undefined, + + // Original text: "Infos" + newVmInfoPanel: 'Informações', + + // Original text: "Name" + newVmNameLabel: 'Nome', + + // Original text: "Template" + newVmTemplateLabel: 'Modelo (Template)', + + // Original text: "Description" + newVmDescriptionLabel: 'Descrição', + + // Original text: "Performances" + newVmPerfPanel: 'Desempenho', + + // Original text: "vCPUs" + newVmVcpusLabel: 'vCPUs', + + // Original text: "RAM" + newVmRamLabel: 'RAM', + + // Original text: 'Static memory max' + newVmStaticMaxLabel: undefined, + + // Original text: 'Dynamic memory min' + newVmDynamicMinLabel: undefined, + + // Original text: 'Dynamic memory max' + newVmDynamicMaxLabel: undefined, + + // Original text: "Install settings" + newVmInstallSettingsPanel: 'Definições de instalação', + + // Original text: "ISO/DVD" + newVmIsoDvdLabel: 'ISO/DVD', + + // Original text: "Network" + newVmNetworkLabel: 'Rede', + + // Original text: 'e.g: http://httpredir.debian.org/debian' + newVmInstallNetworkPlaceHolder: undefined, + + // Original text: "PV Args" + newVmPvArgsLabel: 'PV argos', + + // Original text: "PXE" + newVmPxeLabel: 'PXE', + + // Original text: "Interfaces" + newVmInterfacesPanel: 'Interfaces', + + // Original text: "MAC" + newVmMacLabel: 'MAC', + + // Original text: "Add interface" + newVmAddInterface: 'Adicionar uma interface', + + // Original text: "Disks" + newVmDisksPanel: 'Discos', + + // Original text: "SR" + newVmSrLabel: 'SR', + + // Original text: "Bootable" + newVmBootableLabel: 'Inicializável', + + // Original text: "Size" + newVmSizeLabel: 'Tamanho', + + // Original text: "Add disk" + newVmAddDisk: 'Adicionar disco', + + // Original text: "Summary" + newVmSummaryPanel: 'Sumário', + + // Original text: "Create" + newVmCreate: 'Criar', + + // Original text: "Reset" + newVmReset: 'Reiniciar', + + // Original text: "Select template" + newVmSelectTemplate: 'Selecionar modelo (template)', + + // Original text: "SSH key" + newVmSshKey: 'Chave SSH', + + // Original text: "Config drive" + newVmConfigDrive: 'Configuração do drive', + + // Original text: "Custom config" + newVmCustomConfig: 'Configuração personalizada', + + // Original text: "Boot VM after creation" + newVmBootAfterCreate: 'Inicializar VM após sua criação', + + // Original text: "Auto-generated if empty" + newVmMacPlaceholder: 'Auto-gerada se vazio', + + // Original text: "CPU weight" + newVmCpuWeightLabel: 'Carga da CPU', + + // Original text: 'Default: {value, number}' + newVmDefaultCpuWeight: undefined, + + // Original text: 'CPU cap' + newVmCpuCapLabel: undefined, + + // Original text: 'Default: {value, number}' + newVmDefaultCpuCap: undefined, + + // Original text: "Cloud config" + newVmCloudConfig: 'Configuração do Cloud', + + // Original text: "Create VMs" + newVmCreateVms: 'Criar VMs', + + // Original text: "Are you sure you want to create {nbVms} VMs?" + newVmCreateVmsConfirm: 'Você tem certeza que deseja criar {nbVms} VMs?', + + // Original text: "Multiple VMs:" + newVmMultipleVms: 'Multiplas VMs', + + // Original text: 'Select a resource set:' + newVmSelectResourceSet: undefined, + + // Original text: 'Name pattern:' + newVmMultipleVmsPattern: undefined, + + // Original text: 'e.g.: \\{name\\}_%' + newVmMultipleVmsPatternPlaceholder: undefined, + + // Original text: 'First index:' + newVmFirstIndex: undefined, + + // Original text: 'Recalculate VMs number' + newVmNumberRecalculate: undefined, + + // Original text: 'Refresh VMs name' + newVmNameRefresh: undefined, + + // Original text: 'Advanced' + newVmAdvancedPanel: undefined, + + // Original text: 'Show advanced settings' + newVmShowAdvanced: undefined, + + // Original text: 'Hide advanced settings' + newVmHideAdvanced: undefined, + + // Original text: "Resource sets" + resourceSets: 'Ajustes de recursos', + + // Original text: 'No resource sets.' + noResourceSets: undefined, + + // Original text: 'Loading resource sets' + loadingResourceSets: undefined, + + // Original text: "Resource set name" + resourceSetName: 'Ajuste de nome do recurso', + + // Original text: 'Recompute all limits' + recomputeResourceSets: undefined, + + // Original text: "Save" + saveResourceSet: 'Salvar', + + // Original text: "Reset" + resetResourceSet: 'Redefinir', + + // Original text: "Edit" + editResourceSet: 'Editar', + + // Original text: "Delete" + deleteResourceSet: 'Deletar', + + // Original text: "Delete resource set" + deleteResourceSetWarning: 'Deletar grupo de recurso', + + // Original text: "Are you sure you want to delete this resource set?" + deleteResourceSetQuestion: 'Você tem certeza que deseja deletar este ajuste?', + + // Original text: "Missing objects:" + resourceSetMissingObjects: 'Objetos faltando', + + // Original text: "vCPUs" + resourceSetVcpus: 'vCPUs', + + // Original text: "Memory" + resourceSetMemory: 'Memória', + + // Original text: "Storage" + resourceSetStorage: 'Armazenamento (Storage)', + + // Original text: "Unknown" + unknownResourceSetValue: 'Desconhecido', + + // Original text: "Available hosts" + availableHosts: 'Hosts disponiveis', + + // Original text: "Excluded hosts" + excludedHosts: 'Hosts excluídos', + + // Original text: "No hosts available." + noHostsAvailable: 'Sem hosts disponiveis', + + // Original text: "VMs created from this resource set shall run on the following hosts." + availableHostsDescription: + 'VMs criadas a partir desse conjunto de recursos deve ser executado nos hosts indicados.', + + // Original text: "Maximum CPUs" + maxCpus: 'Limite de CPUs', + + // Original text: "Maximum RAM (GiB)" + maxRam: 'Limite de RAM (GiB)', + + // Original text: "Maximum disk space" + maxDiskSpace: 'Limite de espaço de disco', + + // Original text: 'IP pool' + ipPool: undefined, + + // Original text: 'Quantity' + quantity: undefined, + + // Original text: "No limits." + noResourceSetLimits: 'Sem limites', + + // Original text: "Total:" + totalResource: 'Total', + + // Original text: "Remaining:" + remainingResource: 'Restando;', + + // Original text: "Used:" + usedResource: 'Usado:', + + // Original text: 'New' + resourceSetNew: undefined, + + // Original text: "Try dropping some VMs files here, or click to select VMs to upload. Accept only .xva/.ova files." + importVmsList: + 'Tente soltar alguns backups aqui, ou clique para selecionar os backups para que seja feito o upload. Apenas arquivos .xva são aceitos.', + + // Original text: "No selected VMs." + noSelectedVms: 'Nenhuma VM selecionada', + + // Original text: "To Pool:" + vmImportToPool: 'Enviar para Pool:', + + // Original text: "To SR:" + vmImportToSr: 'Enviar para SR:', + + // Original text: "VMs to import" + vmsToImport: 'Importar VMs', + + // Original text: "Reset" + importVmsCleanList: 'Reiniciar', + + // Original text: "VM import success" + vmImportSuccess: 'Importação feita com sucesso', + + // Original text: "VM import failed" + vmImportFailed: 'Falha na importação', + + // Original text: "Import starting…" + startVmImport: 'Iniciando importação…', + + // Original text: "Export starting…" + startVmExport: 'Iniciando exportação…', + + // Original text: 'N CPUs' + nCpus: undefined, + + // Original text: 'Memory' + vmMemory: undefined, + + // Original text: 'Disk {position} ({capacity})' + diskInfo: undefined, + + // Original text: 'Disk description' + diskDescription: undefined, + + // Original text: 'No disks.' + noDisks: undefined, + + // Original text: 'No networks.' + noNetworks: undefined, + + // Original text: 'Network {name}' + networkInfo: undefined, + + // Original text: 'No description available' + noVmImportErrorDescription: undefined, + + // Original text: 'Error:' + vmImportError: undefined, + + // Original text: '{type} file:' + vmImportFileType: undefined, + + // Original text: 'Please to check and/or modify the VM configuration.' + vmImportConfigAlert: undefined, + + // Original text: "No pending tasks" + noTasks: 'Nenhuma tarefa pendente', + + // Original text: "Currently, there are not any pending XenServer tasks" + xsTasks: 'Atualmente nenhuma tarefa esta pendente no XenServer', + + // Original text: 'Schedules' + backupSchedules: undefined, + + // Original text: 'Get remote' + getRemote: undefined, + + // Original text: 'List Remote' + listRemote: undefined, + + // Original text: 'simple' + simpleBackup: undefined, + + // Original text: "delta" + delta: 'delta', + + // Original text: "Restore Backups" + restoreBackups: 'Recuperação de Backups', + + // Original text: 'Click on a VM to display restore options' + restoreBackupsInfo: undefined, + + // Original text: "Enabled" + remoteEnabled: 'Habilitado', + + // Original text: "Error" + remoteError: 'erro', + + // Original text: "No backup available" + noBackup: 'Nenhum backup disponível', + + // Original text: 'VM Name' + backupVmNameColumn: undefined, + + // Original text: 'Tags' + backupTags: undefined, + + // Original text: 'Last Backup' + lastBackupColumn: undefined, + + // Original text: 'Available Backups' + availableBackupsColumn: undefined, + + // Original text: 'Missing parameters' + backupRestoreErrorTitle: undefined, + + // Original text: 'Choose a SR and a backup' + backupRestoreErrorMessage: undefined, + + // Original text: 'Display backups' + displayBackup: undefined, + + // Original text: 'Import VM' + importBackupTitle: undefined, + + // Original text: 'Starting your backup import' + importBackupMessage: undefined, + + // Original text: 'VMs to backup' + vmsToBackup: undefined, + + // Original text: 'Emergency shutdown Host{nHosts, plural, one {} other {s}}' + emergencyShutdownHostsModalTitle: undefined, + + // Original text: 'Are you sure you want to shutdown {nHosts} Host{nHosts, plural, one {} other {s}}?' + emergencyShutdownHostsModalMessage: undefined, + + // Original text: "Shutdown host" + stopHostModalTitle: 'Desligar host', + + // Original text: "This will shutdown your host. Do you want to continue? If it's the pool master, your connection to the pool will be lost" + stopHostModalMessage: + 'O host será desligado. Você tem certeza que deseja continuar?', + + // Original text: 'Add host' + addHostModalTitle: undefined, + + // Original text: 'Are you sure you want to add {host} to {pool}?' + addHostModalMessage: undefined, + + // Original text: "Restart host" + restartHostModalTitle: 'Reiniciar host', + + // Original text: "This will restart your host. Do you want to continue?" + restartHostModalMessage: + 'O host será reiniciado. Você tem certeza que deseja continuar?', + + // Original text: 'Restart Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}' + restartHostsAgentsModalTitle: undefined, + + // Original text: 'Are you sure you want to restart {nHosts} Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}?' + restartHostsAgentsModalMessage: undefined, + + // Original text: 'Restart Host{nHosts, plural, one {} other {s}}' + restartHostsModalTitle: undefined, + + // Original text: 'Are you sure you want to restart {nHosts} Host{nHosts, plural, one {} other {s}}?' + restartHostsModalMessage: undefined, + + // Original text: "Start VM{vms, plural, one {} other {s}}" + startVmsModalTitle: 'Iniciar VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to start {vms} VM{vms, plural, one {} other {s}}?" + startVmsModalMessage: + 'Você tem certeza que deseja iniciar {vms} VM{vms, plural, one {} other {s}}?', + + // Original text: 'Stop Host{nHosts, plural, one {} other {s}}' + stopHostsModalTitle: undefined, + + // Original text: 'Are you sure you want to stop {nHosts} Host{nHosts, plural, one {} other {s}}?' + stopHostsModalMessage: undefined, + + // Original text: "Stop VM{vms, plural, one {} other {s}}" + stopVmsModalTitle: 'Parar VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to stop {vms} VM{vms, plural, one {} other {s}}?" + stopVmsModalMessage: + 'Você tem certeza que deseja parar {vms} VM{vms, plural, one {} other {s}}?', + + // Original text: "Restart VM" + restartVmModalTitle: 'Reiniciar VM', + + // Original text: "Are you sure you want to restart {name}?" + restartVmModalMessage: 'Você tem certeza que deseja reiniciar {name}?', + + // Original text: "Stop VM" + stopVmModalTitle: 'Parar VM', + + // Original text: "Are you sure you want to stop {name}?" + stopVmModalMessage: 'Você tem certeza que deseja parar {name}?', + + // Original text: "Restart VM{vms, plural, one {} other {s}}" + restartVmsModalTitle: 'Reiniciar VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to restart {vms} VM{vms, plural, one {} other {s}}?" + restartVmsModalMessage: + 'Você tem certeza que deseja reiniciar {vms} VM{vms, plural, one {} other {s}}?', + + // Original text: "Snapshot VM{vms, plural, one {} other {s}}" + snapshotVmsModalTitle: 'Snapshot VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to snapshot {vms} VM{vms, plural, one {} other {s}}?" + snapshotVmsModalMessage: + 'Você tem certeza que deseja executar snapshop para {vms} VM{vms, plural, one {} other {s}}?', + + // Original text: "Delete VM{vms, plural, one {} other {s}}" + deleteVmsModalTitle: 'Deletar VM{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to delete {vms} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED" + deleteVmsModalMessage: + 'Você tem certeza que deseja deletar {vms} VM{vms, plural, one {} other {s}}? Todos os discos de VM serão removidos', + + // Original text: "Delete VM" + deleteVmModalTitle: 'Deletar VM', + + // Original text: "Are you sure you want to delete this VM? ALL VM DISKS WILL BE REMOVED" + deleteVmModalMessage: + 'Você tem certeza que deseja deletar esta VM? Todos os discos de VM serão removidos', + + // Original text: "Migrate VM" + migrateVmModalTitle: 'Migrar VM', + + // Original text: 'Select a destination host:' + migrateVmSelectHost: undefined, + + // Original text: 'Select a migration network:' + migrateVmSelectMigrationNetwork: undefined, + + // Original text: 'For each VDI, select an SR:' + migrateVmSelectSrs: undefined, + + // Original text: 'For each VIF, select a network:' + migrateVmSelectNetworks: undefined, + + // Original text: 'Select a destination SR:' + migrateVmsSelectSr: undefined, + + // Original text: 'Select a destination SR for local disks:' + migrateVmsSelectSrIntraPool: undefined, + + // Original text: 'Select a network on which to connect each VIF:' + migrateVmsSelectNetwork: undefined, + + // Original text: 'Smart mapping' + migrateVmsSmartMapping: undefined, + + // Original text: 'Name' + migrateVmName: undefined, + + // Original text: 'SR' + migrateVmSr: undefined, + + // Original text: 'VIF' + migrateVmVif: undefined, + + // Original text: 'Network' + migrateVmNetwork: undefined, + + // Original text: 'No target host' + migrateVmNoTargetHost: undefined, + + // Original text: 'A target host is required to migrate a VM' + migrateVmNoTargetHostMessage: undefined, + + // Original text: 'Delete VDI' + deleteVdiModalTitle: undefined, + + // Original text: 'Are you sure you want to delete this disk? ALL DATA ON THIS DISK WILL BE LOST' + deleteVdiModalMessage: undefined, + + // Original text: 'Revert your VM' + revertVmModalTitle: undefined, + + // Original text: 'Delete snapshot' + deleteSnapshotModalTitle: undefined, + + // Original text: 'Are you sure you want to delete this snapshot?' + deleteSnapshotModalMessage: undefined, + + // Original text: 'Are you sure you want to revert this VM to the snapshot state? This operation is irreversible.' + revertVmModalMessage: undefined, + + // Original text: 'Snapshot before' + revertVmModalSnapshotBefore: undefined, + + // Original text: "Import a {name} Backup" + importBackupModalTitle: 'Importar este Backup: {name}', + + // Original text: "Start VM after restore" + importBackupModalStart: 'Iniciar VM após restauração', + + // Original text: "Select your backup…" + importBackupModalSelectBackup: 'Selecionar backup…', + + // Original text: "Are you sure you want to remove all orphaned snapshot VDIs?" + removeAllOrphanedModalWarning: + 'Você tem certeza que deseja remover todos as VDIs orfãs?', + + // Original text: "Remove all logs" + removeAllLogsModalTitle: 'Remover todos os logs', + + // Original text: "Are you sure you want to remove all logs?" + removeAllLogsModalWarning: + 'Você tem certeza que deseja remover todos os logs?', + + // Original text: "This operation is definitive." + definitiveMessageModal: 'Esta operação é definitiva.', + + // Original text: "Previous SR Usage" + existingSrModalTitle: 'Uso anterior SR', + + // Original text: "This path has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingSrModalText: + 'Este caminho foi previamente utilizado como um dispositivo de armazenamento por um host XenServer. Todos os dados serão perdidos se você optar por continuar a criação do SR.', + + // Original text: "Previous LUN Usage" + existingLunModalTitle: 'Uso anterior LUN', + + // Original text: "This LUN has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingLunModalText: + 'Este LUN foi previamente utilizado como um dispositivo de armazenamento por um host XenServer. Todos os dados serão perdidos se você optar por continuar a criação do SR.', + + // Original text: "Replace current registration?" + alreadyRegisteredModal: 'Deseja substituir o registro atual?', + + // Original text: "Your XO appliance is already registered to {email}, do you want to forget and replace this registration ?" + alreadyRegisteredModalText: + 'O seu XO appliance já foi registrado com o e-mail {email}, você tem certeza que gostaria de substituir este registro?', + + // Original text: "Ready for trial?" + trialReadyModal: 'Pronto para iniciar o teste (trial)?', + + // Original text: "During the trial period, XOA need to have a working internet connection. This limitation does not apply for our paid plans!" + trialReadyModalText: + 'Durante o período experimental, XOA precisa de uma conexão internet. Esta limitação não se aplica em nossos planos pagos!', + + // Original text: "Host" + serverHost: 'Host', + + // Original text: "Username" + serverUsername: 'Nome de Usuário', + + // Original text: "Password" + serverPassword: 'Senha', + + // Original text: "Action" + serverAction: 'Ação', + + // Original text: "Read Only" + serverReadOnly: 'Modo Leitura', + + // Original text: 'Disconnect server' + serverDisconnect: undefined, + + // Original text: 'username' + serverPlaceHolderUser: undefined, + + // Original text: 'password' + serverPlaceHolderPassword: undefined, + + // Original text: 'address[:port]' + serverPlaceHolderAddress: undefined, + + // Original text: 'Connect' + serverConnect: undefined, + + // Original text: "Copy VM" + copyVm: 'Copiar VM', + + // Original text: "Are you sure you want to copy this VM to {SR}?" + copyVmConfirm: 'Você tem certeza que deseja copiar esta VM para {SR}?', + + // Original text: "Name" + copyVmName: 'Nome', + + // Original text: 'Name pattern' + copyVmNamePattern: undefined, + + // Original text: "If empty: name of the copied VM" + copyVmNamePlaceholder: 'Se vazio: Nome da VM copiada', + + // Original text: 'e.g.: "\\{name\\}_COPY"' + copyVmNamePatternPlaceholder: undefined, + + // Original text: "Select SR" + copyVmSelectSr: 'Selecionar SR', + + // Original text: "Use compression" + copyVmCompress: 'Compressão', + + // Original text: 'No target SR' + copyVmsNoTargetSr: undefined, + + // Original text: 'A target SR is required to copy a VM' + copyVmsNoTargetSrMessage: undefined, + + // Original text: 'Detach host' + detachHostModalTitle: undefined, + + // Original text: 'Are you sure you want to detach {host} from its pool? THIS WILL REMOVE ALL VMs ON ITS LOCAL STORAGE AND REBOOT THE HOST.' + detachHostModalMessage: undefined, + + // Original text: 'Detach' + detachHost: undefined, + + // Original text: "Create network" + newNetworkCreate: 'Criar rede', + + // Original text: 'Create bonded network' + newBondedNetworkCreate: undefined, + + // Original text: "Interface" + newNetworkInterface: 'Inerface', + + // Original text: "Name" + newNetworkName: 'Nome', + + // Original text: "Description" + newNetworkDescription: 'Descrição', + + // Original text: "VLAN" + newNetworkVlan: 'VLAN', + + // Original text: "No VLAN if empty" + newNetworkDefaultVlan: 'Sem VLAN, caso esteja vazia', + + // Original text: "MTU" + newNetworkMtu: 'MTU', + + // Original text: "Default: 1500" + newNetworkDefaultMtu: 'Padrão: 1500', + + // Original text: 'Name required' + newNetworkNoNameErrorTitle: undefined, + + // Original text: 'A name is required to create a network' + newNetworkNoNameErrorMessage: undefined, + + // Original text: 'Bond mode' + newNetworkBondMode: undefined, + + // Original text: "Delete network" + deleteNetwork: 'Deletar rede', + + // Original text: "Are you sure you want to delete this network?" + deleteNetworkConfirm: 'Você tem certeza que deseja deletar esta rede?', + + // Original text: 'This network is currently in use' + networkInUse: undefined, + + // Original text: 'Bonded' + pillBonded: undefined, + + // Original text: 'Host' + addHostSelectHost: undefined, + + // Original text: 'No host' + addHostNoHost: undefined, + + // Original text: 'No host selected to be added' + addHostNoHostMessage: undefined, + + // Original text: "Xen Orchestra" + xenOrchestra: 'Xen Orchestra', + + // Original text: "server" + xenOrchestraServer: 'servidor', + + // Original text: "web client" + xenOrchestraWeb: 'cliente web', + + // Original text: "No pro support provided!" + noProSupport: 'Nenhum suporte pro fornecido!', + + // Original text: "Use in production at your own risks" + noProductionUse: 'O uso deste em produção é por sua conta e risco', + + // Original text: "You can download our turnkey appliance at" + downloadXoa: 'Você pode baixar nosso turnkey appliance em', + + // Original text: "Bug Tracker" + bugTracker: 'Rastreador de bug', + + // Original text: "Issues? Report it!" + bugTrackerText: 'Problemas? Envie agora!', + + // Original text: "Community" + community: 'Comunidade', + + // Original text: "Join our community forum!" + communityText: 'Participe do nosso forum e de nossa comunidade!', + + // Original text: "Free Trial for Premium Edition!" + freeTrial: 'Versão Premium Edition disponível para período de teste (Trial)', + + // Original text: "Request your trial now!" + freeTrialNow: 'Peça já seu período de teste (Trial)', + + // Original text: "Any issue?" + issues: 'Algum problema encontrado?', + + // Original text: "Problem? Contact us!" + issuesText: 'Problemas? Entre em contato conosco', + + // Original text: "Documentation" + documentation: 'Documentação', + + // Original text: "Read our official doc" + documentationText: 'Leia nossa documentação oficial (Em inglês)', + + // Original text: "Pro support included" + proSupportIncluded: 'Suporte Pro incluído', + + // Original text: "Acces your XO Account" + xoAccount: 'Acesse sua conta XO', + + // Original text: "Report a problem" + openTicket: 'Enviar um problema', + + // Original text: "Problem? Open a ticket!" + openTicketText: 'Algum problema? Abra um ticket agora!', + + // Original text: "Upgrade needed" + upgradeNeeded: 'Atualização necessária', + + // Original text: "Upgrade now!" + upgradeNow: 'Atualize agora!', + + // Original text: "Or" + or: 'Ou', + + // Original text: "Try it for free!" + tryIt: 'Teste agora, é grátis!', + + // Original text: "This feature is available starting from {plan} Edition" + availableIn: 'Este recurso é disponível a partir da versão {plan}', + + // Original text: 'This feature is not available in your version, contact your administrator to know more.' + notAvailable: undefined, + + // Original text: 'Updates' + updateTitle: undefined, + + // Original text: "Registration" + registration: 'Inscrição', + + // Original text: "Trial" + trial: 'Teste (Trial)', + + // Original text: "Settings" + settings: 'Configurações', + + // Original text: 'Proxy settings' + proxySettings: undefined, + + // Original text: 'Host (myproxy.example.org)' + proxySettingsHostPlaceHolder: undefined, + + // Original text: 'Port (eg: 3128)' + proxySettingsPortPlaceHolder: undefined, + + // Original text: 'Username' + proxySettingsUsernamePlaceHolder: undefined, + + // Original text: 'Password' + proxySettingsPasswordPlaceHolder: undefined, + + // Original text: 'Your email account' + updateRegistrationEmailPlaceHolder: undefined, + + // Original text: 'Your password' + updateRegistrationPasswordPlaceHolder: undefined, + + // Original text: "Update" + update: 'Atualizar (Update)', + + // Original text: 'Refresh' + refresh: undefined, + + // Original text: "Upgrade" + upgrade: 'Atualização (Upgrade)', + + // Original text: "No updater available for Community Edition" + noUpdaterCommunity: + 'Nenhuma atualização disponível para a versão Community Edition', + + // Original text: "Please consider subscribe and try it with all features for free during 15 days on" + noUpdaterSubscribe: + 'Oi, inscreva-se e venha testar todos nossos recursos e serviços gratuitamente por 15 dias!', + + // Original text: "Manual update could break your current installation due to dependencies issues, do it with caution" + noUpdaterWarning: + 'Atualização feita de forma manual pode corromper sua instalação atual devido a problema de dependências, tenha cuidado!', + + // Original text: "Current version:" + currentVersion: 'Versão atual:', + + // Original text: "Register" + register: 'Registrar', + + // Original text: 'Edit registration' + editRegistration: undefined, + + // Original text: "Please, take time to register in order to enjoy your trial." + trialRegistration: + 'Por favor, tome seu tempo para se registrar a fim de desfrutar do seu período de teste (trial)', + + // Original text: "Start trial" + trialStartButton: 'Iniciar teste (trial)', + + // Original text: "You can use a trial version until {date, date, medium}. Upgrade your appliance to get it." + trialAvailableUntil: + 'Sua versao de teste é válida até {date, date, medium}. Após esta data escolha um de nossos planos e continue a desfrutar de nosso software e serviços!', + + // Original text: "Your trial has been ended. Contact us or downgrade to Free version" + trialConsumed: + 'Seu período de teste chegou ao fim. Entre em contato conosco ou faça o downgrade para a versão grátis', + + // Original text: "Your xoa-updater service appears to be down. Your XOA cannot run fully without reaching this service." + trialLocked: + 'Seu serviço de atualização XOA parece não funcionar. Seu XOA não pode funcionar corretamente sem este serviço.', + + // Original text: 'No update information available' + noUpdateInfo: undefined, + + // Original text: 'Update information may be available' + waitingUpdateInfo: undefined, + + // Original text: 'Your XOA is up-to-date' + upToDate: undefined, + + // Original text: 'You need to update your XOA (new version is available)' + mustUpgrade: undefined, + + // Original text: 'Your XOA is not registered for updates' + registerNeeded: undefined, + + // Original text: "Can't fetch update information" + updaterError: undefined, + + // Original text: 'Upgrade successful' + promptUpgradeReloadTitle: undefined, + + // Original text: 'Your XOA has successfully upgraded, and your browser must reload the application. Do you want to reload now ?' + promptUpgradeReloadMessage: undefined, + + // Original text: "Xen Orchestra from the sources" + disclaimerTitle: 'Xen Orchestra versão Open-Source', + + // Original text: "You are using XO from the sources! That's great for a personal/non-profit usage." + disclaimerText1: + 'Você está usando XO Open-Source! Isso é ótimo para um uso pessoal / sem fins lucrativos.', + + // Original text: "If you are a company, it's better to use it with our appliance + pro support included:" + disclaimerText2: + 'Se você é uma empresa, é melhor usá-lo com o nosso sistema appliance + suporte pro inclusos:', + + // Original text: "This version is not bundled with any support nor updates. Use it with caution for critical tasks." + disclaimerText3: + 'Esta versão não está vinculada a qualquer tipo de suporte nem atualizações. Use-a com cuidado em se tratando de tarefas críticas.', + + // Original text: "Connect PIF" + connectPif: 'Conectar PIF', + + // Original text: "Are you sure you want to connect this PIF?" + connectPifConfirm: 'Você tem certeza que deseja conectar este PIF?', + + // Original text: "Disconnect PIF" + disconnectPif: 'Desconectar PIF', + + // Original text: "Are you sure you want to disconnect this PIF?" + disconnectPifConfirm: 'Você tem certeza que deseja desconectar este PIF?', + + // Original text: "Delete PIF" + deletePif: 'Deletar PIF', + + // Original text: "Are you sure you want to delete this PIF?" + deletePifConfirm: 'Você tem certeza que deseja conectar este PIF?', + + // Original text: 'Username' + username: undefined, + + // Original text: 'Password' + password: undefined, + + // Original text: 'Language' + language: undefined, + + // Original text: 'Old password' + oldPasswordPlaceholder: undefined, + + // Original text: 'New password' + newPasswordPlaceholder: undefined, + + // Original text: 'Confirm new password' + confirmPasswordPlaceholder: undefined, + + // Original text: 'Confirmation password incorrect' + confirmationPasswordError: undefined, + + // Original text: 'Password does not match the confirm password.' + confirmationPasswordErrorBody: undefined, + + // Original text: 'Password changed' + pwdChangeSuccess: undefined, + + // Original text: 'Your password has been successfully changed.' + pwdChangeSuccessBody: undefined, + + // Original text: 'Incorrect password' + pwdChangeError: undefined, + + // Original text: 'The old password provided is incorrect. Your password has not been changed.' + pwdChangeErrorBody: undefined, + + // Original text: 'OK' + changePasswordOk: undefined, + + // Original text: 'SSH keys' + sshKeys: undefined, + + // Original text: 'New SSH key' + newSshKey: undefined, + + // Original text: 'Delete' + deleteSshKey: undefined, + + // Original text: 'No SSH keys' + noSshKeys: undefined, + + // Original text: 'New SSH key' + newSshKeyModalTitle: undefined, + + // Original text: 'Invalid key' + sshKeyErrorTitle: undefined, + + // Original text: 'An SSH key requires both a title and a key.' + sshKeyErrorMessage: undefined, + + // Original text: 'Title' + title: undefined, + + // Original text: 'Key' + key: undefined, + + // Original text: 'Delete SSH key' + deleteSshKeyConfirm: undefined, + + // Original text: 'Are you sure you want to delete the SSH key {title}?' + deleteSshKeyConfirmMessage: undefined, + + // Original text: 'Others' + others: undefined, + + // Original text: 'Loading logs…' + loadingLogs: undefined, + + // Original text: 'User' + logUser: undefined, + + // Original text: 'Method' + logMethod: undefined, + + // Original text: 'Params' + logParams: undefined, + + // Original text: 'Message' + logMessage: undefined, + + // Original text: 'Error' + logError: undefined, + + // Original text: 'Display details' + logDisplayDetails: undefined, + + // Original text: 'Date' + logTime: undefined, + + // Original text: 'No stack trace' + logNoStackTrace: undefined, + + // Original text: 'No params' + logNoParams: undefined, + + // Original text: 'Delete log' + logDelete: undefined, + + // Original text: 'Delete all logs' + logDeleteAll: undefined, + + // Original text: 'Delete all logs' + logDeleteAllTitle: undefined, + + // Original text: 'Are you sure you want to delete all the logs?' + logDeleteAllMessage: undefined, + + // Original text: 'Name' + ipPoolName: undefined, + + // Original text: 'IPs' + ipPoolIps: undefined, + + // Original text: 'IPs (e.g.: 1.0.0.12-1.0.0.17;1.0.0.23)' + ipPoolIpsPlaceholder: undefined, + + // Original text: 'Networks' + ipPoolNetworks: undefined, + + // Original text: 'No IP pools' + ipsNoIpPool: undefined, + + // Original text: 'Create' + ipsCreate: undefined, + + // Original text: 'Delete all IP pools' + ipsDeleteAllTitle: undefined, + + // Original text: 'Are you sure you want to delete all the IP pools?' + ipsDeleteAllMessage: undefined, + + // Original text: 'VIFs' + ipsVifs: undefined, + + // Original text: 'Not used' + ipsNotUsed: undefined, + + // Original text: 'Keyboard shortcuts' + shortcutModalTitle: undefined, + + // Original text: 'Global' + shortcut_XoApp: undefined, + + // Original text: 'Go to hosts list' + shortcut_GO_TO_HOSTS: undefined, + + // Original text: 'Go to pools list' + shortcut_GO_TO_POOLS: undefined, + + // Original text: 'Go to VMs list' + shortcut_GO_TO_VMS: undefined, + + // Original text: 'Create a new VM' + shortcut_CREATE_VM: undefined, + + // Original text: 'Unfocus field' + shortcut_UNFOCUS: undefined, + + // Original text: 'Show shortcuts key bindings' + shortcut_HELP: undefined, + + // Original text: 'Home' + shortcut_Home: undefined, + + // Original text: 'Focus search bar' + shortcut_SEARCH: undefined, + + // Original text: 'Next item' + shortcut_NAV_DOWN: undefined, + + // Original text: 'Previous item' + shortcut_NAV_UP: undefined, + + // Original text: 'Select item' + shortcut_SELECT: undefined, + + // Original text: 'Open' + shortcut_JUMP_INTO: undefined, + + // Original text: 'VM' + settingsAclsButtonTooltipVM: undefined, + + // Original text: 'Hosts' + settingsAclsButtonTooltiphost: undefined, + + // Original text: 'Pool' + settingsAclsButtonTooltippool: undefined, + + // Original text: 'SR' + settingsAclsButtonTooltipSR: undefined, + + // Original text: 'Network' + settingsAclsButtonTooltipnetwork: undefined, +} diff --git a/packages/xo-web/src/common/intl/locales/zh.js b/packages/xo-web/src/common/intl/locales/zh.js new file mode 100644 index 000000000..2d6badb0a --- /dev/null +++ b/packages/xo-web/src/common/intl/locales/zh.js @@ -0,0 +1,2300 @@ +// See http://momentjs.com/docs/#/use-it/browserify/ +import 'moment/locale/zh-cn' + +import reactIntlData from 'react-intl/locale-data/zh' +import { addLocaleData } from 'react-intl' +addLocaleData(reactIntlData) + +// =================================================================== + +export default { + // Original text: "Long click to edit" + editableLongClickPlaceholder: '长按编辑', + + // Original text: "Click to edit" + editableClickPlaceholder: '点击编辑', + + // Original text: "OK" + alertOk: '确认', + + // Original text: "OK" + confirmOk: '确认', + + // Original text: "Cancel" + confirmCancel: '取消', + + // Original text: "On error" + onError: '出现错误', + + // Original text: "Successful" + successful: '成功', + + // Original text: "Home" + homePage: '主页', + + // Original text: "Dashboard" + dashboardPage: '仪表盘', + + // Original text: "Overview" + overviewDashboardPage: '概览', + + // Original text: "Visualizations" + overviewVisualizationDashboardPage: '虚拟化', + + // Original text: "Statistics" + overviewStatsDashboardPage: '状态统计', + + // Original text: "Health" + overviewHealthDashboardPage: '健康状态', + + // Original text: "Self service" + selfServicePage: '自助服务', + + // Original text: "Dashboard" + selfServiceDashboardPage: '仪表盘', + + // Original text: "Administration" + selfServiceAdminPage: '管理', + + // Original text: "Backup" + backupPage: '备份', + + // Original text: "Jobs" + jobsPage: '任务', + + // Original text: "Updates" + updatePage: '更新', + + // Original text: "Settings" + settingsPage: '设置', + + // Original text: "Servers" + settingsServersPage: '服务器', + + // Original text: "Users" + settingsUsersPage: '用户', + + // Original text: "Groups" + settingsGroupsPage: '组', + + // Original text: "ACLs" + settingsAclsPage: '访问控制', + + // Original text: "Plugins" + settingsPluginsPage: '插件', + + // Original text: "About" + aboutPage: '关于', + + // Original text: "New" + newMenu: '新建', + + // Original text: "Tasks" + taskMenu: '任务', + + // Original text: "Tasks" + taskPage: '任务', + + // Original text: "VM" + newVmPage: '虚拟机', + + // Original text: "Storage" + newSrPage: '存储', + + // Original text: "Server" + newServerPage: '服务器', + + // Original text: "Import" + newImport: '导入', + + // Original text: "Overview" + backupOverviewPage: '概览', + + // Original text: "New" + backupNewPage: '新建', + + // Original text: "Remotes" + backupRemotesPage: '远程', + + // Original text: "Restore" + backupRestorePage: '恢复', + + // Original text: "Schedule" + schedule: '计划', + + // Original text: "New VM backup" + newVmBackup: '新建虚拟机备份', + + // Original text: "Edit VM backup" + editVmBackup: '编辑虚拟机备份', + + // Original text: "Backup" + backup: '备份', + + // Original text: "Rolling Snapshot" + rollingSnapshot: '滚动快照', + + // Original text: "Delta Backup" + deltaBackup: '差异备份', + + // Original text: "Disaster Recovery" + disasterRecovery: '灾难恢复', + + // Original text: "Continuous Replication" + continuousReplication: '持续复制', + + // Original text: "Overview" + jobsOverviewPage: '概览', + + // Original text: "New" + jobsNewPage: '新建', + + // Original text: "Scheduling" + jobsSchedulingPage: '计划', + + // Original text: "Custom Job" + customJob: '自定义任务', + + // Original text: "User" + userPage: '用户', + + // Original text: "Sign out" + signOut: '注销', + + // Original text: "Fetching data…" + homeFetchingData: '获取数据', + + // Original text: "Welcome on Xen Orchestra!" + homeWelcome: '欢迎使用Xen Orchestra', + + // Original text: "Add your XenServer hosts or pools" + homeWelcomeText: '添加您的XenServer主机或资源池', + + // Original text: "Want some help?" + homeHelp: '需要帮助?', + + // Original text: "Add server" + homeAddServer: '添加服务器', + + // Original text: "Online Doc" + homeOnlineDoc: '在线文档', + + // Original text: "Pro Support" + homeProSupport: '专业支持', + + // Original text: "There are no VMs!" + homeNoVms: '没有可用的虚拟机', + + // Original text: "Or…" + homeNoVmsOr: '或', + + // Original text: "Import VM" + homeImportVm: '导入虚拟机', + + // Original text: "Import an existing VM in xva format" + homeImportVmMessage: '导入一个XVA格式的虚拟机', + + // Original text: "Restore a backup" + homeRestoreBackup: '恢复到备份', + + // Original text: "Restore a backup from a remote store" + homeRestoreBackupMessage: '恢复到远程存储上的备份', + + // Original text: "This will create a new VM" + homeNewVmMessage: '将创建一个新的虚拟机', + + // Original text: "Filters" + homeFilters: '过滤器', + + // Original text: "Pool" + homeTypePool: '资源池', + + // Original text: "Host" + homeTypeHost: '主机', + + // Original text: "VM" + homeTypeVm: '虚拟机', + + // Original text: "SR" + homeTypeSr: '数据存储', + + // Original text: "VDI" + homeTypeVdi: '虚拟硬盘', + + // Original text: "Sort" + homeSort: '排序', + + // Original text: "Pools" + homeAllPools: '资源池', + + // Original text: "Hosts" + homeAllHosts: '主机', + + // Original text: "Tags" + homeAllTags: '标签', + + // Original text: "New VM" + homeNewVm: '新建虚拟机', + + // Original text: "Running hosts" + homeFilterRunningHosts: '正在运行的主机', + + // Original text: "Disabled hosts" + homeFilterDisabledHosts: '不可用的主机', + + // Original text: "Running VMs" + homeFilterRunningVms: '正在运行的虚拟机', + + // Original text: "Non running VMs" + homeFilterNonRunningVms: '未运行的虚拟机', + + // Original text: "Pending VMs" + homeFilterPendingVms: '正在创建的虚拟机', + + // Original text: "HVM guests" + homeFilterHvmGuests: 'HVM客户机', + + // Original text: "Tags" + homeFilterTags: '标签', + + // Original text: "Sort by" + homeSortBy: '排序方式', + + // Original text: "Name" + homeSortByName: '名称', + + // Original text: "Power state" + homeSortByPowerstate: '电源状态', + + // Original text: "RAM" + homeSortByRAM: '内存', + + // Original text: "vCPUs" + homeSortByvCPUs: '虚拟机CPU', + + // Original text: "CPUs" + homeSortByCpus: 'CPU', + + // Original text: "{displayed, number}x {icon} (on {total, number})" + homeDisplayedItems: undefined, + + // Original text: "{selected, number}x {icon} selected (on {total, number})" + homeSelectedItems: undefined, + + // Original text: "More" + homeMore: '更多', + + // Original text: "Migrate to…" + homeMigrateTo: '迁移至…', + + // Original text: "Missing patches" + homeMissingPaths: '缺少补丁', + + // Original text: "High Availability" + highAvailability: '高可用', + + // Original text: "Add" + add: '添加', + + // Original text: "Remove" + remove: '删除', + + // Original text: "Preview" + preview: '预览', + + // Original text: "Item" + item: '项', + + // Original text: "No selected value" + noSelectedValue: '没有选择的值', + + // Original text: "Choose user(s) and/or group(s)" + selectSubjects: '选择用户和/或用户组', + + // Original text: "Select Object(s)…" + selectObjects: '选择对象', + + // Original text: "Choose a role" + selectRole: '选择一个角色', + + // Original text: "Select Host(s)…" + selectHosts: '选择主机', + + // Original text: "Select object(s)…" + selectHostsVms: '选择虚拟机', + + // Original text: "Select Network(s)…" + selectNetworks: '选择网络', + + // Original text: "Select PIF(s)…" + selectPifs: '选择网卡', + + // Original text: "Select Pool(s)…" + selectPools: '选择资源池', + + // Original text: "Select Remote(s)…" + selectRemotes: '选择远程', + + // Original text: "Select resource set(s)…" + selectResourceSets: '选择资源集', + + // Original text: "Select template(s)…" + selectResourceSetsVmTemplate: '选择模板', + + // Original text: "Select SR(s)…" + selectResourceSetsSr: '选择数据存储', + + // Original text: "Select network(s)…" + selectResourceSetsNetwork: '选择网络', + + // Original text: "Select disk(s)…" + selectResourceSetsVdi: '选择硬盘', + + // Original text: "Select SR(s)…" + selectSrs: '选择数据存储', + + // Original text: "Select VM(s)…" + selectVms: '选择虚拟机', + + // Original text: "Select VM template(s)…" + selectVmTemplates: '选择虚拟机模板', + + // Original text: "Select tag(s)…" + selectTags: '选择标签', + + // Original text: "Select disk(s)…" + selectVdis: '选择硬盘', + + // Original text: "Fill required informations." + fillRequiredInformations: '填写需要的信息', + + // Original text: "Fill informations (optional)" + fillOptionalInformations: '填写信息', + + // Original text: "Reset" + selectTableReset: '重置', + + // Original text: "Month" + schedulingMonth: '月', + + // Original text: "Every month" + schedulingEveryMonth: '每月', + + // Original text: "Each selected month" + schedulingEachSelectedMonth: '每个选定月份', + + // Original text: "Day of the month" + schedulingMonthDay: '本月的一天', + + // Original text: "Every day" + schedulingEveryMonthDay: '每天', + + // Original text: "Each selected day" + schedulingEachSelectedMonthDay: '每个选定天', + + // Original text: "Day of the week" + schedulingWeekDay: '本周的一天', + + // Original text: "Every day" + schedulingEveryWeekDay: '每天', + + // Original text: "Each selected day" + schedulingEachSelectedWeekDay: '每个选定天', + + // Original text: "Hour" + schedulingHour: '小时', + + // Original text: "Every hour" + schedulingEveryHour: '每小时', + + // Original text: "Every N hour" + schedulingEveryNHour: '每N小时', + + // Original text: "Each selected hour" + schedulingEachSelectedHour: '每个选定小时', + + // Original text: "Minute" + schedulingMinute: '分钟', + + // Original text: "Every minute" + schedulingEveryMinute: '每分钟', + + // Original text: "Every N minute" + schedulingEveryNMinute: '每N分钟', + + // Original text: "Each selected minute" + schedulingEachSelectedMinute: '每个选定分钟', + + // Original text: "Reset" + schedulingReset: '重置', + + // Original text: "Unknown" + unknownSchedule: '未知', + + // Original text: "Cannot edit backup" + backupEditNotFoundTitle: '不能编辑备份', + + // Original text: "Missing required info for edition" + backupEditNotFoundMessage: '缺少版本所需要的信息', + + // Original text: "Job" + job: '任务', + + // Original text: "Job ID" + jobId: '任务ID', + + // Original text: "Name" + jobName: '名称', + + // Original text: "Start" + jobStart: '开始', + + // Original text: "End" + jobEnd: '结束', + + // Original text: "Duration" + jobDuration: '周期', + + // Original text: "Status" + jobStatus: '状态', + + // Original text: "Action" + jobAction: '行为', + + // Original text: "Tag" + jobTag: '标签', + + // Original text: "Scheduling" + jobScheduling: '计划', + + // Original text: "State" + jobState: '状态', + + // Original text: "Run job" + runJob: '运行任务', + + // Original text: "One shot running started. See overview for logs." + runJobVerbose: '开始一次运行,可查看概要日志', + + // Original text: "Started" + jobStarted: '已经开始', + + // Original text: "Finished" + jobFinished: '已完成', + + // Original text: "Save" + saveBackupJob: '保存', + + // Original text: "Remove backup job" + deleteBackupSchedule: '删除备份任务', + + // Original text: "Are you sure you want to delete this backup job?" + deleteBackupScheduleQuestion: '你确认你要删除这个备份任务吗?', + + // Original text: "Enable immediately after creation" + scheduleEnableAfterCreation: '创建后立即启用', + + // Original text: "You are editing Schedule {name} ({id}). Saving will override previous schedule state." + scheduleEditMessage: '你正在编辑计划{name} ({id}).保存将覆盖前一个计划状态.', + + // Original text: "You are editing job {name} ({id}). Saving will override previous job state." + jobEditMessage: '你正在编辑任务{name} ({id}).保存将覆盖前一个任务状态', + + // Original text: "No scheduled jobs." + noScheduledJobs: '没有计划任务', + + // Original text: "No jobs found." + noJobs: '未找到任务', + + // Original text: "No schedules found" + noSchedules: '未找到计划', + + // Original text: "Select a xo-server API command" + jobActionPlaceHolder: '选择一个xo-server API 命令', + + // Original text: "Select your backup type:" + newBackupSelection: '选择你的备份类型', + + // Original text: "Remote stores for backup" + remoteList: '远程备份存储', + + // Original text: "New File System Remote" + newRemote: '新建远程文件系统', + + // Original text: "Local" + remoteTypeLocal: '本地', + + // Original text: "NFS" + remoteTypeNfs: 'NFS', + + // Original text: "SMB" + remoteTypeSmb: 'SMB', + + // Original text: "Type" + remoteType: '类型', + + // Original text: "Test your remote" + remoteTestTip: '测试你的远程配置', + + // Original text: "Test Remote" + testRemote: '测试远程配置', + + // Original text: "Test failed for {name}" + remoteTestFailure: '失败的测试项 {name}', + + // Original text: "Test passed for {name}" + remoteTestSuccess: '通过的测试项{name}', + + // Original text: "Error" + remoteTestError: '错误', + + // Original text: "Test Step" + remoteTestStep: '测试步骤', + + // Original text: "Test file" + remoteTestFile: '测试文件', + + // Original text: "The remote appears to work correctly" + remoteTestSuccessMessage: '远程配置运行正常', + + // Original text: "Create a new SR" + newSrTitle: '创建一个新的数据存储', + + // Original text: "General" + newSrGeneral: '常规', + + // Original text: "Select Strorage Type:" + newSrTypeSelection: '选择存储类型', + + // Original text: "Settings" + newSrSettings: '设置', + + // Original text: "Storage Usage" + newSrUsage: '存储利用率', + + // Original text: "Summary" + newSrSummary: '综述', + + // Original text: "Host" + newSrHost: '主机', + + // Original text: "Type" + newSrType: '类型', + + // Original text: "Name" + newSrName: '名称', + + // Original text: "Description" + newSrDescription: '描述', + + // Original text: "Server" + newSrServer: '服务器', + + // Original text: "Path" + newSrPath: '路径', + + // Original text: "IQN" + newSrIqn: 'IQN', + + // Original text: "LUN" + newSrLun: 'LUN', + + // Original text: "with auth." + newSrAuth: '启用认证', + + // Original text: "User Name" + newSrUsername: '用户名', + + // Original text: "Password" + newSrPassword: '密码', + + // Original text: "Device" + newSrDevice: '设备', + + // Original text: "in use" + newSrInUse: '使用中', + + // Original text: "Size" + newSrSize: '大小', + + // Original text: "Create" + newSrCreate: '创建', + + // Original text: "Users/Groups" + subjectName: '用户/组', + + // Original text: "Object" + objectName: '对象', + + // Original text: "Role" + roleName: '角色', + + // Original text: "New Group Name" + newGroupName: '新建组名', + + // Original text: "Create Group" + createGroup: '创建组', + + // Original text: "Create" + createGroupButton: '创建', + + // Original text: "Delete Group" + deleteGroup: '删除组', + + // Original text: "Are you sure you want to delete this group?" + deleteGroupConfirm: '你确定要删除该组?', + + // Original text: "Remove user from Group" + removeUserFromGroup: '从组中删除用户', + + // Original text: "Are you sure you want to delete this user?" + deleteUserConfirm: '你确定要删除该用户?', + + // Original text: "Delete User" + deleteUser: '删除用户', + + // Original text: "unknown user" + unknownUser: '未知用户', + + // Original text: "No group found" + noGroupFound: '没有找到组', + + // Original text: "Name" + groupNameColumn: '名称', + + // Original text: "Users" + groupUsersColumn: '用户', + + // Original text: "Add User" + addUserToGroupColumn: '增加用户', + + // Original text: "Email" + userNameColumn: '邮件', + + // Original text: "Permissions" + userPermissionColumn: '权限', + + // Original text: "Password" + userPasswordColumn: '密码', + + // Original text: "Email" + userName: '邮件', + + // Original text: "Password" + userPassword: '密码', + + // Original text: "Create" + createUserButton: '创建', + + // Original text: "No user found" + noUserFound: '没有找到用户', + + // Original text: "User" + userLabel: '用户', + + // Original text: "Admin" + adminLabel: '管理', + + // Original text: "No user in group" + noUserInGroup: '组中没有用户', + + // Original text: "{users} user{users, plural, one {} other {s}}" + countUsers: '{users} 用户{users, plural, one {} 其他 {s}}', + + // Original text: "Select Permission" + selectPermission: '选择权限', + + // Original text: "Auto-load at server start" + autoloadPlugin: '服务器启动时自动加载', + + // Original text: "Save configuration" + savePluginConfiguration: '保存配置', + + // Original text: "Delete configuration" + deletePluginConfiguration: '删除配置', + + // Original text: "Plugin error" + pluginError: '插件错误', + + // Original text: "Unknown error" + unknownPluginError: '未知错误', + + // Original text: "Purge plugin configuration" + purgePluginConfiguration: '清除插件配置', + + // Original text: "Are you sure you want to purge this configuration ?" + purgePluginConfigurationQuestion: '你确定要清除此配置?', + + // Original text: "Edit" + editPluginConfiguration: '编辑', + + // Original text: "Cancel" + cancelPluginEdition: '取消', + + // Original text: "Plugin configuration" + pluginConfigurationSuccess: '插件配置', + + // Original text: "Plugin configuration successfully saved!" + pluginConfigurationChanges: '插件配置保存成功', + + // Original text: "Start" + startVmLabel: '启动', + + // Original text: "Recovery start" + recoveryModeLabel: '恢复启动', + + // Original text: "Suspend" + suspendVmLabel: '暂停', + + // Original text: "Stop" + stopVmLabel: '关机', + + // Original text: "Force shutdown" + forceShutdownVmLabel: '强制关机', + + // Original text: "Reboot" + rebootVmLabel: '重启', + + // Original text: "Force reboot" + forceRebootVmLabel: '强制重启', + + // Original text: "Delete" + deleteVmLabel: '删除', + + // Original text: "Migrate" + migrateVmLabel: '迁移', + + // Original text: "Snapshot" + snapshotVmLabel: '快照', + + // Original text: "Export" + exportVmLabel: '导出', + + // Original text: "Resume" + resumeVmLabel: '恢复', + + // Original text: "Copy" + copyVmLabel: '复制', + + // Original text: "Clone" + cloneVmLabel: '克隆', + + // Original text: "Fast clone" + fastCloneVmLabel: '快速克隆', + + // Original text: "Convert to template" + convertVmToTemplateLabel: '转换成模板', + + // Original text: "Console" + vmConsoleLabel: '控制台', + + // Original text: "Rescan all disks" + srRescan: '重新扫描所有磁盘', + + // Original text: "Connect to all hosts" + srReconnectAll: '连接所有主机', + + // Original text: "Disconnect to all hosts" + srDisconnectAll: '断开所有主机', + + // Original text: "Forget this SR" + srForget: '移除此数据存储', + + // Original text: "Remove this SR" + srRemoveButton: '删除此数据存储', + + // Original text: "No VDIs in this storage" + srNoVdis: '此存储中没有VDI', + + // Original text: "Hosts" + hostsTabName: '主机', + + // Original text: "High Availability" + poolHaStatus: '高可用', + + // Original text: "Enabled" + poolHaEnabled: '启用', + + // Original text: "Disabled" + poolHaDisabled: '禁用', + + // Original text: "Name" + hostNameLabel: '名称', + + // Original text: "Description" + hostDescription: '描述', + + // Original text: "Memory" + hostMemory: '内存', + + // Original text: "No hosts" + noHost: '没有主机', + + // Original text: "Name" + poolNetworkNameLabel: '名称', + + // Original text: "Description" + poolNetworkDescription: '描述', + + // Original text: "PIFs" + poolNetworkPif: 'PIFs', + + // Original text: "No networks" + poolNoNetwork: '没有网络', + + // Original text: "MTU" + poolNetworkMTU: 'MTU', + + // Original text: "Connected" + poolNetworkPifAttached: '已连接', + + // Original text: "Disconnected" + poolNetworkPifDetached: '未连接', + + // Original text: "Add SR" + addSrLabel: '添加数据存储', + + // Original text: "Add VM" + addVmLabel: '添加虚拟机', + + // Original text: "Add Host" + addHostLabel: '添加主机', + + // Original text: "Disconnect" + disconnectServer: '断开', + + // Original text: "Start" + startHostLabel: '启动', + + // Original text: "Stop" + stopHostLabel: '关机', + + // Original text: "Enable" + enableHostLabel: '启用', + + // Original text: "Disable" + disableHostLabel: '禁用', + + // Original text: "Restart toolstack" + restartHostAgent: '重启toolstack', + + // Original text: "Force reboot" + forceRebootHostLabel: '强制重启', + + // Original text: "Reboot" + rebootHostLabel: '重启', + + // Original text: "Emergency mode" + emergencyModeLabel: '紧急模式', + + // Original text: "Storage" + storageTabName: '存储', + + // Original text: "Patches" + patchesTabName: '补丁', + + // Original text: "Load average" + statLoad: '负载平衡', + + // Original text: "Hardware" + hardwareHostSettingsLabel: '硬件', + + // Original text: "Address" + hostAddress: '地址', + + // Original text: "Status" + hostStatus: '状态', + + // Original text: "Build number" + hostBuildNumber: '版本号', + + // Original text: "iSCSI name" + hostIscsiName: 'iSCSI名称', + + // Original text: "Version" + hostXenServerVersion: '版本', + + // Original text: "Enabled" + hostStatusEnabled: '启用', + + // Original text: "Disabled" + hostStatusDisabled: '禁用', + + // Original text: "Power on mode" + hostPowerOnMode: '开机模式', + + // Original text: "Host uptime" + hostStartedSince: '系统启动时间', + + // Original text: "Toolstack uptime" + hostStackStartedSince: 'Toolstack启动时间', + + // Original text: "CPU model" + hostCpusModel: 'CPU型号', + + // Original text: "Core (socket)" + hostCpusNumber: '核 (socket)', + + // Original text: "Manufacturer info" + hostManufacturerinfo: '制造商信息', + + // Original text: "BIOS info" + hostBiosinfo: 'BIOS 信息', + + // Original text: "Licence" + licenseHostSettingsLabel: '授权', + + // Original text: "Type" + hostLicenseType: '类型', + + // Original text: "Socket" + hostLicenseSocket: '插槽', + + // Original text: "Expiry" + hostLicenseExpiry: '过期', + + // Original text: "Add a network" + networkCreateButton: '新建一个网络', + + // Original text: "Device" + pifDeviceLabel: '设备', + + // Original text: "Network" + pifNetworkLabel: '网络', + + // Original text: "VLAN" + pifVlanLabel: 'VLAN', + + // Original text: "Address" + pifAddressLabel: '地址', + + // Original text: "MAC" + pifMacLabel: 'MAC', + + // Original text: "MTU" + pifMtuLabel: 'MTU', + + // Original text: "Status" + pifStatusLabel: '状态', + + // Original text: "Connected" + pifStatusConnected: '已连接', + + // Original text: "Disconnected" + pifStatusDisconnected: '未连接', + + // Original text: "No physical interface detected" + pifNoInterface: '没有检测到物理接口', + + // Original text: "Add a storage" + addSrDeviceButton: '新建存储', + + // Original text: "Name" + srNameLabel: '名称', + + // Original text: "Type" + srType: '类型', + + // Original text: "Status" + pdbStatus: '状态', + + // Original text: "Connected" + pbdStatusConnected: '已连接', + + // Original text: "Disconnected" + pbdStatusDisconnected: '未连接', + + // Original text: "Shared" + srShared: '已共享', + + // Original text: "Not shared" + srNotShared: '未共享', + + // Original text: "No storage detected" + pbdNoSr: '未检测到存储', + + // Original text: "Name" + patchNameLabel: '名称', + + // Original text: "Install all patches" + patchUpdateButton: '安装所有补丁', + + // Original text: "Description" + patchDescription: '描述', + + // Original text: "Applied date" + patchApplied: '应用日期', + + // Original text: "Size" + patchSize: '大小', + + // Original text: "Status" + patchStatus: '状态', + + // Original text: "Applied" + patchStatusApplied: '已应用', + + // Original text: "Missing patches" + patchStatusNotApplied: '缺少补丁', + + // Original text: "No patch detected" + patchNothing: '未检测到补丁', + + // Original text: "Release date" + patchReleaseDate: '发布日期', + + // Original text: "Guidance" + patchGuidance: '导航', + + // Original text: "Action" + patchAction: '操作', + + // Original text: "Applied patches" + hostAppliedPatches: '已应用补丁', + + // Original text: "Missing patches" + hostMissingPatches: '缺少补丁', + + // Original text: "Host up-to-date!" + hostUpToDate: '主机补丁为最新', + + // Original text: "Refresh patches" + refreshPatches: '刷新补丁包', + + // Original text: "Install pool patches" + installPoolPatches: '安装池补丁', + + // Original text: "General" + generalTabName: '常规', + + // Original text: "Stats" + statsTabName: '状态', + + // Original text: "Console" + consoleTabName: '控制台', + + // Original text: "Snapshots" + snapshotsTabName: '快照', + + // Original text: "Logs" + logsTabName: '日志', + + // Original text: "Advanced" + advancedTabName: '高级', + + // Original text: "Network" + networkTabName: '网络', + + // Original text: "Disk{disks, plural, one {} other {s}}" + disksTabName: '磁盘{disks, plural, one {} 其他 {s}}', + + // Original text: "halted" + powerStateHalted: '已停止', + + // Original text: "running" + powerStateRunning: '正在运行', + + // Original text: "suspended" + powerStateSuspended: '已暂停', + + // Original text: "No Xen tools detected" + vmStatus: '没有检测到Xen Tools', + + // Original text: "No IPv4 record" + vmName: '没有IPv4记录', + + // Original text: "No IP record" + vmDescription: '没有IP记录', + + // Original text: "Started {ago}" + vmSettings: '已启动 {ago}', + + // Original text: "Current status:" + vmCurrentStatus: '当前状态', + + // Original text: "Not running" + vmNotRunning: '没有运行', + + // Original text: "No Xen tools detected" + noToolsDetected: '没有检测到Xen Tools', + + // Original text: "No IPv4 record" + noIpv4Record: '没有IPv4记录', + + // Original text: "No IP record" + noIpRecord: '没有IP记录', + + // Original text: "Started {ago}" + started: '已启动 {ago}', + + // Original text: "Paravirtualization (PV)" + paraVirtualizedMode: '半虚拟化 (PV)', + + // Original text: "Hardware virtualization (HVM)" + hardwareVirtualizedMode: '硬件虚拟化 (HVM)', + + // Original text: "CPU usage" + statsCpu: 'CPU利用率', + + // Original text: "Memory usage" + statsMemory: '内存利用率', + + // Original text: "Network throughput" + statsNetwork: '网络流量', + + // Original text: "Stacked values" + useStackedValuesOnStats: 'Stacked 值', + + // Original text: "Disk throughput" + statDisk: '磁盘吞吐', + + // Original text: "Last 10 minutes" + statLastTenMinutes: '最近10分钟', + + // Original text: "Last 2 hours" + statLastTwoHours: '最近两小时', + + // Original text: "Last week" + statLastWeek: '最近一周', + + // Original text: "Last year" + statLastYear: '最近一年', + + // Original text: "Copy" + copyToClipboardLabel: '复制', + + // Original text: "Ctrl+Alt+Del" + ctrlAltDelButtonLabel: '发送Ctrl+Alt+Del', + + // Original text: "Tip:" + tipLabel: '提示', + + // Original text: "non-US keyboard could have issues with console: switch your own layout to US." + tipConsoleLabel: '非美式键盘操作控制台可能出现问题:请切换至美式键盘模式', + + // Original text: "Action" + vdiAction: '操作', + + // Original text: "Attach disk" + vdiAttachDeviceButton: '附加磁盘', + + // Original text: "New disk" + vbdCreateDeviceButton: '新建磁盘', + + // Original text: "Boot order" + vdiBootOrder: '启动顺序', + + // Original text: "Name" + vdiNameLabel: '名称', + + // Original text: "Description" + vdiNameDescription: '描述', + + // Original text: "Tags" + vdiTags: '标签', + + // Original text: "Size" + vdiSize: '磁盘大小', + + // Original text: "SR" + vdiSr: '数据存储', + + // Original text: "VM" + vdiVm: '虚拟机', + + // Original text: "Boot flag" + vbdBootableStatus: '启动标识', + + // Original text: "Status" + vbdStatus: '状态', + + // Original text: "Connected" + vbdStatusConnected: '已连接', + + // Original text: "Disconnected" + vbdStatusDisconnected: '未连接', + + // Original text: "No disks" + vbdNoVbd: '没有磁盘', + + // Original text: "New device" + vifCreateDeviceButton: '新建设备', + + // Original text: "No interface" + vifNoInterface: '没有网卡', + + // Original text: "Device" + vifDeviceLabel: '设备', + + // Original text: "MAC address" + vifMacLabel: 'MAC地址', + + // Original text: "MTU" + vifMtuLabel: 'MTU', + + // Original text: "Network" + vifNetworkLabel: '网络', + + // Original text: "Status" + vifStatusLabel: '状态', + + // Original text: "Connected" + vifStatusConnected: '已连接', + + // Original text: "Disconnected" + vifStatusDisconnected: '未连接', + + // Original text: "IP addresses" + vifIpAddresses: 'IP地址', + + // Original text: "Auto-generated if empty" + vifMacAutoGenerate: '如果没有自动创建', + + // Original text: "No snapshots" + noSnapshots: '没有快照', + + // Original text: "New snapshot" + snapshotCreateButton: '新建快照', + + // Original text: "Just click on the snapshot button to create one!" + tipCreateSnapshotLabel: '点击快照按钮来创建快照', + + // Original text: "Creation date" + snapshotDate: '创建日期', + + // Original text: "Name" + snapshotName: '名称', + + // Original text: "Action" + snapshotAction: '操作', + + // Original text: "Remove all logs" + logRemoveAll: '删除所有日志', + + // Original text: "No logs so far" + noLogs: '目前没有日志', + + // Original text: "Creation date" + logDate: '创建日期', + + // Original text: "Name" + logName: '名称', + + // Original text: "Content" + logContent: '目录', + + // Original text: "Action" + logAction: '操作', + + // Original text: "Remove" + vmRemoveButton: '删除', + + // Original text: "Convert" + vmConvertButton: '转换', + + // Original text: "Xen settings" + xenSettingsLabel: 'Xen 设置', + + // Original text: "Guest OS" + guestOsLabel: '客户操作系统', + + // Original text: "Misc" + miscLabel: 'Misc', + + // Original text: "UUID" + uuid: 'UUID', + + // Original text: "Virtualization mode" + virtualizationMode: '虚拟化模式', + + // Original text: "CPU weight" + cpuWeightLabel: 'CPU权重', + + // Original text: "Default" + defaultCpuWeight: '默认', + + // Original text: "PV args" + pvArgsLabel: 'PV参数', + + // Original text: "Xen tools status" + xenToolsStatus: 'Xen tools状态', + + // Original text: "{status}" + xenToolsStatusValue: '{status}', + + // Original text: "OS name" + osName: '操作系统名称', + + // Original text: "OS kernel" + osKernel: '操作系统内核', + + // Original text: "Auto power on" + autoPowerOn: '自动卡机', + + // Original text: "HA" + ha: '高可用', + + // Original text: "Original template" + originalTemplate: '来源模板', + + // Original text: "Unknown" + unknownOsName: '未知', + + // Original text: "Unknown" + unknownOsKernel: '未知', + + // Original text: "Unknown" + unknownOriginalTemplate: '未知', + + // Original text: "VM limits" + vmLimitsLabel: '虚拟机限制', + + // Original text: "CPU limits" + vmCpuLimitsLabel: 'CPU限制', + + // Original text: "Memory limits (min/max)" + vmMemoryLimitsLabel: '内存限制(min/max)', + + // Original text: "vCPUs max:" + vmMaxVcpus: '最大虚拟CPU数', + + // Original text: "Memory max:" + vmMaxRam: '最大内存', + + // Original text: "Long click to add a name" + vmHomeNamePlaceholder: '长按来添加名称', + + // Original text: "Long click to add a description" + vmHomeDescriptionPlaceholder: '长按来添加描述', + + // Original text: "Click to add a name" + vmViewNamePlaceholder: '点击添加名称', + + // Original text: "Click to add a description" + vmViewDescriptionPlaceholder: '点击添加描述', + + // Original text: "Pool{pools, plural, one {} other {s}}" + poolPanel: '池{pools, plural, one {} 其他 {s}}', + + // Original text: "Host{hosts, plural, one {} other {s}}" + hostPanel: '主机{hosts, plural, one {} 其他 {s}}', + + // Original text: "VM{vms, plural, one {} other {s}}" + vmPanel: '虚拟机{vms, plural, one {} 其他 {s}}', + + // Original text: "RAM Usage" + memoryStatePanel: '内容使用率', + + // Original text: "CPUs Usage" + cpuStatePanel: 'CPU使用率', + + // Original text: "VMs Power state" + vmStatePanel: '虚拟机电源状态', + + // Original text: "Pending tasks" + taskStatePanel: '正在运行的任务', + + // Original text: "Users" + usersStatePanel: '用户', + + // Original text: "Storage state" + srStatePanel: '存储状态', + + // Original text: "{usage} (of {total})" + ofUsage: '{usage} (of {total})', + + // Original text: "No storage" + noSrs: '没有存储', + + // Original text: "Name" + srName: '名称', + + // Original text: "Pool" + srPool: '资源池', + + // Original text: "Host" + srHost: '主机', + + // Original text: "Type" + srFormat: '类型', + + // Original text: "Size" + srSize: '大小', + + // Original text: "Usage" + srUsage: '利用率', + + // Original text: "used" + srUsed: '已使用', + + // Original text: "free" + srFree: '剩余空间', + + // Original text: "Storage Usage" + srUsageStatePanel: '存储利用率', + + // Original text: "Top 5 SR Usage (in %)" + srTopUsageStatePanel: '数据存储使用率前5名(in %)', + + // Original text: "{running} running ({halted} halted)" + vmsStates: '{running} 正在运行 ({halted} 已停止)', + + // Original text: "{value} {date, date, medium}" + weekHeatmapData: '{value} {date, date, medium}', + + // Original text: "No data." + weekHeatmapNoData: '没有数据', + + // Original text: "Weekly Heatmap" + weeklyHeatmap: '每周热图', + + // Original text: "Weekly Charts" + weeklyCharts: '每周图表', + + // Original text: "Synchronize scale:" + weeklyChartsScaleInfo: '同步范围', + + // Original text: "Stats error" + statsDashboardGenericErrorTitle: '状态错误', + + // Original text: "There is no stats available for:" + statsDashboardGenericErrorMessage: '没有可用的状态:', + + // Original text: "No selected metric" + noSelectedMetric: '没有选择度量标准', + + // Original text: "Select" + statsDashboardSelectObjects: '选择', + + // Original text: "Loading…" + metricsLoading: '加载中….', + + // Original text: "Coming soon!" + comingSoon: '即将呈现', + + // Original text: "Orphaned VDIs" + orphanedVdis: '孤立的VDI', + + // Original text: "Orphaned VMs" + orphanedVms: '孤立的虚拟机', + + // Original text: "No orphans" + noOrphanedObject: '没有孤立的内容', + + // Original text: "Remove all orphaned VDIs" + removeAllOrphanedObject: '删除所有孤立的VDI', + + // Original text: "Name" + vmNameLabel: '名称', + + // Original text: "Description" + vmNameDescription: '描述', + + // Original text: "Resident on" + vmContainer: '位于', + + // Original text: "Alarms" + alarmMessage: '警告', + + // Original text: "No alarms" + noAlarms: '没有警告', + + // Original text: "Date" + alarmDate: '日期', + + // Original text: "Content" + alarmContent: '内容', + + // Original text: "Issue on" + alarmObject: '问题', + + // Original text: "Pool" + alarmPool: '资源池', + + // Original text: "Remove all alarms" + alarmRemoveAll: '删除所有警告', + + // Original text: "Create a new VM on {select}" + newVmCreateNewVmOn: '创建一个新的位于{select}的虚拟机', + + // Original text: "Create a new VM on {select1} or {select2}" + newVmCreateNewVmOn2: '创建一个新的位于{select1} 或 {select2}的虚拟机', + + // Original text: "You have no permission to create a VM" + newVmCreateNewVmNoPermission: '你没有权限创建虚拟机', + + // Original text: "Infos" + newVmInfoPanel: '信息', + + // Original text: "Name" + newVmNameLabel: '名称', + + // Original text: "Template" + newVmTemplateLabel: '模板', + + // Original text: "Description" + newVmDescriptionLabel: '描述', + + // Original text: "Performances" + newVmPerfPanel: '性能', + + // Original text: "vCPUs" + newVmVcpusLabel: '虚拟CPU', + + // Original text: "RAM" + newVmRamLabel: '内存', + + // Original text: "Install settings" + newVmInstallSettingsPanel: '安装设置', + + // Original text: "ISO/DVD" + newVmIsoDvdLabel: 'ISO/DVD', + + // Original text: "Network" + newVmNetworkLabel: '网络', + + // Original text: "PV Args" + newVmPvArgsLabel: 'PV参数', + + // Original text: "PXE" + newVmPxeLabel: 'PXE', + + // Original text: "Interfaces" + newVmInterfacesPanel: '网络接口', + + // Original text: "MAC" + newVmMacLabel: 'MAC', + + // Original text: "Add interface" + newVmAddInterface: '添加网络接口', + + // Original text: "Disks" + newVmDisksPanel: '磁盘', + + // Original text: "SR" + newVmSrLabel: '数据存储', + + // Original text: "Bootable" + newVmBootableLabel: '启动项', + + // Original text: "Size" + newVmSizeLabel: '大小', + + // Original text: "Add disk" + newVmAddDisk: '添加磁盘', + + // Original text: "Summary" + newVmSummaryPanel: '概述', + + // Original text: "Create" + newVmCreate: '创建', + + // Original text: "Reset" + newVmReset: '重置', + + // Original text: "Select template" + newVmSelectTemplate: '选择模板', + + // Original text: "SSH key" + newVmSshKey: 'SSH Key', + + // Original text: "Config drive" + newVmConfigDrive: '配置驱动器', + + // Original text: "Custom config" + newVmCustomConfig: '自定义配置', + + // Original text: "Boot VM after creation" + newVmBootAfterCreate: '创建后启动', + + // Original text: "Auto-generated if empty" + newVmMacPlaceholder: '如果为空自动创建', + + // Original text: "CPU weight" + newVmCpuWeightLabel: 'CPU权重', + + // Original text: "Quarter (1/4)" + newVmCpuWeightQuarter: '四分之一 (1/4)', + + // Original text: "Half (1/2)" + newVmCpuWeightHalf: '二分之一 (1/2)', + + // Original text: "Normal" + newVmCpuWeightNormal: '普通', + + // Original text: "Double (x2)" + newVmCpuWeightDouble: '双倍(x2)', + + // Original text: "Cloud config" + newVmCloudConfig: '云配置', + + // Original text: "Create VMs" + newVmCreateVms: '创建虚拟机', + + // Original text: "Are you sure you want to create {nbVms} VMs?" + newVmCreateVmsConfirm: '你确定要创建 {nbVms} 虚拟机?', + + // Original text: "Multiple VMs:" + newVmMultipleVms: '多个虚拟机', + + // Original text: "Select a resource set:" + newVmSelectResourceSet: '选择资源集', + + // Original text: "Name pattern:" + newVmMultipleVmsPattern: '命名模式', + + // Original text: "e.g.: \\{name\\}_%" + newVmMultipleVmsPatternPlaceholder: '例如: \\{name\\}_%', + + // Original text: "First index:" + newVmFirstIndex: '首要标识', + + // Original text: "Resource sets" + resourceSets: '资源集', + + // Original text: "No resource sets." + noResourceSets: '没有资源集', + + // Original text: "Resource set name" + resourceSetName: '资源集名称', + + // Original text: "Creation and edition" + resourceSetCreation: '创建并编辑', + + // Original text: "Save" + saveResourceSet: '保存', + + // Original text: "Reset" + resetResourceSet: '重置', + + // Original text: "Edit" + editResourceSet: '编辑', + + // Original text: "Delete" + deleteResourceSet: '删除', + + // Original text: "Delete resource set" + deleteResourceSetWarning: '删除资源集', + + // Original text: "Are you sure you want to delete this resource set?" + deleteResourceSetQuestion: '你确定要删除此资源集', + + // Original text: "Missing objects:" + resourceSetMissingObjects: '缺少对象', + + // Original text: "vCPUs" + resourceSetVcpus: '虚拟CPU', + + // Original text: "Memory" + resourceSetMemory: '内存', + + // Original text: "Storage" + resourceSetStorage: '存储', + + // Original text: "Unknown" + unknownResourceSetValue: '未知', + + // Original text: "Available hosts" + availableHosts: '可用主机', + + // Original text: "Excluded hosts" + excludedHosts: '被排除的主机', + + // Original text: "No hosts available." + noHostsAvailable: '没有可用主机', + + // Original text: "VMs created from this resource set shall run on the following hosts." + availableHostsDescription: '从这些资源中创建的虚拟机将运行在以下主机上', + + // Original text: "Maximum CPUs" + maxCpus: '最大CPU', + + // Original text: "Maximum RAM (GiB)" + maxRam: '最大内存', + + // Original text: "Maximum disk space" + maxDiskSpace: '最大磁盘空间', + + // Original text: "No limits." + noResourceSetLimits: '没有限制', + + // Original text: "Total:" + totalResource: '合计', + + // Original text: "Remaining:" + remainingResource: '剩余', + + // Original text: "Used:" + usedResource: '已使用', + + // Original text: "Try dropping some backups here, or click to select backups to upload. Accept only .xva files." + importVmsList: + '尝试将备份文件拖拽到这里,或点击选择备份文件上传,仅支持.xva格式的文件', + + // Original text: "No selected VMs." + noSelectedVms: '没有选择虚拟机', + + // Original text: "To Pool:" + vmImportToPool: '到资源池', + + // Original text: "To SR:" + vmImportToSr: '到存储库', + + // Original text: "VMs to import" + vmsToImport: '导入虚拟机', + + // Original text: "Reset" + importVmsCleanList: '重置', + + // Original text: "VM import success" + vmImportSuccess: '虚拟机导入成功', + + // Original text: "VM import failed" + vmImportFailed: '虚拟机导入失败', + + // Original text: "Import starting…" + startVmImport: '开始导入', + + // Original text: "Export starting…" + startVmExport: '开始导出', + + // Original text: "No pending tasks" + noTasks: '没有等待中的任务', + + // Original text: "Currently, there are not any pending XenServer tasks" + xsTasks: '当前,没有任何等待中的XenServer任务', + + // Original text: "List Remote" + listRemote: '列出远程', + + // Original text: "simple" + simpleBackup: '简单', + + // Original text: "delta" + delta: '增量', + + // Original text: "Restore Backups" + restoreBackups: '恢复备份', + + // Original text: "No remotes" + noRemotes: '没有远程', + + // Original text: "enabled" + remoteEnabled: '启用', + + // Original text: "error" + remoteError: '错误', + + // Original text: "No backup available" + noBackup: '没有可用的备份', + + // Original text: "VM Name" + backupVmNameColumn: '虚拟机名称', + + // Original text: "Backup Tag" + backupTagColumn: '备份标识', + + // Original text: "Last Backup" + lastBackupColumn: '最后备份', + + // Original text: "Available Backups" + availableBackupsColumn: '可用的备份', + + // Original text: "Restore" + restoreColumn: '恢复', + + // Original text: "Restore VM" + restoreTip: '恢复虚拟机', + + // Original text: "Import VM" + importBackupTitle: '导入虚拟机', + + // Original text: "Starting your backup import" + importBackupMessage: '开始你的备份导入', + + // Original text: "Emergency shutdown Host{nHosts, plural, one {} other {s}}" + emergencyShutdownHostsModalTitle: + '紧急关闭主机{nHosts, plural, one {} other {s}}', + + // Original text: "Are you sure you want to shutdown {nHosts} Host{nHosts, plural, one {} other {s}}?" + emergencyShutdownHostsModalMessage: + '你确定要关闭 {nHosts} 主机{nHosts, plural, one {} other {s}}?', + + // Original text: "Shutdown host" + stopHostModalTitle: '关闭主机', + + // Original text: "This will shutdown your host. Do you want to continue?" + stopHostModalMessage: '此操作将关闭你的主机,你确定要继续吗?', + + // Original text: "Restart host" + restartHostModalTitle: '重启主机', + + // Original text: "This will restart your host. Do you want to continue?" + restartHostModalMessage: '此操作将重启你的主机,你确定要继续吗?', + + // Original text: "Restart Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}" + restartHostsAgentsModalTitle: + '重启主机{nHosts, plural, one {} other {s}} 代理{nHosts, plural, one {} other {s}}', + + // Original text: "Are you sure you want to restart {nHosts} Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}?" + restartHostsAgentsModalMessage: + '你确定要重启{nHosts}主机{nHosts, plural, one {} other {s}} 代理{nHosts, plural, one {} other {s}}?', + + // Original text: "Restart Host{nHosts, plural, one {} other {s}}" + restartHostsModalTitle: '重启主机{nHosts, plural, one {} other {s}}', + + // Original text: "Are you sure you want to restart {nHosts} Host{nHosts, plural, one {} other {s}}?" + restartHostsModalMessage: + '你确定要重启{nHosts}主机{nHosts, plural, one {} other {s}}?', + + // Original text: "Start VM{vms, plural, one {} other {s}}" + startVmsModalTitle: '启动虚拟机{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to start {vms} VM{vms, plural, one {} other {s}}?" + startVmsModalMessage: + '你确定要启动 {vms} 虚拟机{vms, plural, one {} other {s}}?', + + // Original text: "Stop Host{nHosts, plural, one {} other {s}}" + stopHostsModalTitle: '停止主机{nHosts, plural, one {} other {s}}', + + // Original text: "Are you sure you want to stop {nHosts} Host{nHosts, plural, one {} other {s}}?" + stopHostsModalMessage: + '你确定要停止{nHosts}主机{nHosts, plural, one {} other {s}}?', + + // Original text: "Stop VM{vms, plural, one {} other {s}}" + stopVmsModalTitle: '停止虚拟机{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to stop {vms} VM{vms, plural, one {} other {s}}?" + stopVmsModalMessage: + '你确定要停止{vms}虚拟机{vms, plural, one {} other {s}}?', + + // Original text: "Restart VM" + restartVmModalTitle: '重新启动虚拟机', + + // Original text: "Are you sure you want to restart {name}?" + restartVmModalMessage: '你确定要重新启动{name}?', + + // Original text: "Stop VM" + stopVmModalTitle: '停止虚拟机', + + // Original text: "Are you sure you want to stop {name}?" + stopVmModalMessage: '你确定要停止 {name}?', + + // Original text: "Restart VM{vms, plural, one {} other {s}}" + restartVmsModalTitle: '重新启动虚拟机{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to restart {vms} VM{vms, plural, one {} other {s}}?" + restartVmsModalMessage: + '你确定要重新启动{vms}虚拟机{vms, plural, one {} other {s}}?', + + // Original text: "Snapshot VM{vms, plural, one {} other {s}}" + snapshotVmsModalTitle: '执行虚拟机快照{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to snapshot {vms} VM{vms, plural, one {} other {s}}?" + snapshotVmsModalMessage: + '你确定要执行虚拟机{vms}快照{vms, plural, one {} other {s}}?', + + // Original text: "Delete VM" + deleteVmModalTitle: '删除虚拟机', + + // Original text: "Delete VM{vms, plural, one {} other {s}}" + deleteVmsModalTitle: '删除虚拟机{vms, plural, one {} other {s}}', + + // Original text: "Are you sure you want to delete this VM? ALL VM DISKS WILL BE REMOVED" + deleteVmModalMessage: '你确定要删除此虚拟机?所有的虚拟机磁盘将被删除', + + // Original text: "Are you sure you want to delete {vms} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED" + deleteVmsModalMessage: + '你确定要删除 {vms}虚拟机{vms, plural, one {} other {s}}?所有的虚拟机磁盘将被删除', + + // Original text: "Migrate VM" + migrateVmModalTitle: '迁移虚拟机', + + // Original text: "Select a destination host:" + migrateVmSelectHost: '选择一个目标主机', + + // Original text: "Select a migration network:" + migrateVmSelectMigrationNetwork: '选择一个迁移网络', + + // Original text: "For each VDI, select an SR:" + migrateVmSelectSrs: '为每个虚拟磁盘,选择存储库', + + // Original text: "For each VIF, select a network:" + migrateVmSelectNetworks: '为每个虚拟网卡,选择一个网络', + + // Original text: "Select a destination SR:" + migrateVmsSelectSr: '选择一个目标存储库', + + // Original text: "Select a destination SR for local disks:" + migrateVmsSelectSrIntraPool: '为本地磁盘选择一个目标存储库', + + // Original text: "Select a network on which to connect each VIF:" + migrateVmsSelectNetwork: '选择一个网络来连接每个虚拟网卡', + + // Original text: "Smart mapping" + migrateVmsSmartMapping: '智能映射', + + // Original text: "Name" + migrateVmName: '名称', + + // Original text: "SR" + migrateVmSr: '存储库', + + // Original text: "VIF" + migrateVmVif: '虚拟网卡', + + // Original text: "Network" + migrateVmNetwork: '网络', + + // Original text: "No target host" + migrateVmNoTargetHost: '没有目标主机', + + // Original text: "A target host is required to migrate a VM" + migrateVmNoTargetHostMessage: '需要一个目标主机来迁移一个虚拟机', + + // Original text: "Import a {name} Backup" + importBackupModalTitle: '导入一个{name}备份', + + // Original text: "Start VM after restore" + importBackupModalStart: '恢复后启动虚拟机', + + // Original text: "Select your backup…" + importBackupModalSelectBackup: '选择你的备份…', + + // Original text: "Are you sure you want to remove all orphaned VDIs?" + removeAllOrphanedModalWarning: '你确定要删除所有孤立的虚拟磁盘?', + + // Original text: "Remove all logs" + removeAllLogsModalTitle: '删除所有日志', + + // Original text: "Are you sure you want to remove all logs?" + removeAllLogsModalWarning: '你确定要删除所有日志?', + + // Original text: "This operation is definitive." + definitiveMessageModal: '这个操作是不可更改的', + + // Original text: "Previous SR Usage" + existingSrModalTitle: '之前存储库的使用情况', + + // Original text: "This path has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingSrModalText: + '这条路径之前已经被一台XenServer主机用来连接存储。如果你选择继续创建存储库,所有的数据将丢失。', + + // Original text: "Previous LUN Usage" + existingLunModalTitle: '之前LUN使用情况', + + // Original text: "This LUN has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation." + existingLunModalText: + '这个LUN之前已经被一台XenServer主机使用。如果你选择继续创建存储库,所有的数据将丢失。', + + // Original text: "Replace current registration?" + alreadyRegisteredModal: '替换当前的注册?', + + // Original text: "Your XO appliance is already registered to {email}, do you want to forget and replace this registration ?" + alreadyRegisteredModalText: + '你的XO设备已经注册给{email},你确定要删除并替换这个注册信息?', + + // Original text: "Ready for trial?" + trialReadyModal: '准备试用?', + + // Original text: "During the trial period, XOA need to have a working internet connection. This limitation does not apply for our paid plans!" + trialReadyModalText: + '在试用期内,XOA需要Internet连接才能正常使用,如果您正式付费将不受此限制', + + // Original text: "Host" + serverHost: '主机', + + // Original text: "Username" + serverUsername: '用户名', + + // Original text: "Password" + serverPassword: '密码', + + // Original text: "Action" + serverAction: '操作', + + // Original text: "Read Only" + serverReadOnly: '只读', + + // Original text: "Copy VM" + copyVm: '复制虚拟机', + + // Original text: "Are you sure you want to copy this VM to {SR}?" + copyVmConfirm: '你确定要复制此虚拟机到{SR}', + + // Original text: "Name" + copyVmName: '名称', + + // Original text: "Name pattern" + copyVmNamePattern: '命名规范', + + // Original text: "If empty: name of the copied VM" + copyVmNamePlaceholder: '如果复制虚拟机名称为空', + + // Original text: "e.g.: \"\\{name\\}_COPY\"" + copyVmNamePatternPlaceholder: 'e.g.: "\\{name\\}_COPY"', + + // Original text: "Select SR" + copyVmSelectSr: '选择存储库', + + // Original text: "Use compression" + copyVmCompress: '使用压缩', + + // Original text: "No target SR" + copyVmsNoTargetSr: '没有目标存储库', + + // Original text: "A target SR is required to copy a VM" + copyVmsNoTargetSrMessage: '复制虚拟机需要选择一个目标存储库', + + // Original text: "Create network" + newNetworkCreate: '创建网络', + + // Original text: "Interface" + newNetworkInterface: '接口', + + // Original text: "Name" + newNetworkName: '名称', + + // Original text: "Description" + newNetworkDescription: '描述', + + // Original text: "VLAN" + newNetworkVlan: 'VLAN', + + // Original text: "No VLAN if empty" + newNetworkDefaultVlan: '如果为空则没有VLAN', + + // Original text: "MTU" + newNetworkMtu: 'MTU', + + // Original text: "Default: 1500" + newNetworkDefaultMtu: '默认1500', + + // Original text: "Delete network" + deleteNetwork: '删除网络', + + // Original text: "Are you sure you want to delete this network?" + deleteNetworkConfirm: '你确定要删除此网络', + + // Original text: "Xen Orchestra" + xenOrchestra: 'Xen Orchestra', + + // Original text: "server" + xenOrchestraServer: '服务器', + + // Original text: "web client" + xenOrchestraWeb: 'Web客户端', + + // Original text: "No pro support provided!" + noProSupport: '不提供专业支持!', + + // Original text: "Use in production at your own risks" + noProductionUse: '在生产环境中使用将存在风险', + + // Original text: "You can download our turnkey appliance at" + downloadXoa: '您可以在这里下载我们的整套设备', + + // Original text: "Bug Tracker" + bugTracker: '问题跟踪器', + + // Original text: "Issues? Report it!" + bugTrackerText: '出现问题?报告!', + + // Original text: "Community" + community: '社区', + + // Original text: "Join our community forum!" + communityText: '加入我们的社区论坛', + + // Original text: "Free Trial for Premium Edition!" + freeTrial: '铂金版免费试用', + + // Original text: "Request your trial now!" + freeTrialNow: '立即请求试用', + + // Original text: "Any issue?" + issues: '出现任何问题?', + + // Original text: "Problem? Contact us!" + issuesText: '有问题?联系我们!', + + // Original text: "Documentation" + documentation: '文档', + + // Original text: "Read our official doc" + documentationText: '阅读我们官方文档', + + // Original text: "Pro support included" + proSupportIncluded: '包含专业支持', + + // Original text: "Acces your XO Account" + xoAccount: '进入你的XO账户', + + // Original text: "Report a problem" + openTicket: '报告一个问题', + + // Original text: "Problem? Open a ticket !" + openTicketText: '存在问题?开个Case!', + + // Original text: "Upgrade needed" + upgradeNeeded: '需要升级', + + // Original text: "Upgrade now!" + upgradeNow: '立即升级', + + // Original text: "Or" + or: '或', + + // Original text: "Try it for free!" + tryIt: '免费试用', + + // Original text: "This feature is available starting from {plan} Edition" + availableIn: '这个功能将在{plan}版本中可用', + + // Original text: "Updates" + updateTitle: '更新', + + // Original text: "Registration" + registration: '注册', + + // Original text: "Trial" + trial: '试用', + + // Original text: "Settings" + settings: '设置', + + // Original text: "Update" + update: '更新', + + // Original text: "Upgrade" + upgrade: '升级', + + // Original text: "No updater available for Community Edition" + noUpdaterCommunity: '社区版本没有可用的升级', + + // Original text: "Please consider subscribe and try it with all features for free during 15 days on" + noUpdaterSubscribe: '请考虑订购或在15天内免费试用所有功能', + + // Original text: "Manual update could break your current installation due to dependencies issues, do it with caution" + noUpdaterWarning: + '由于相关依赖关系的问题,手动更新将跑坏你当前的安全,请小心使用', + + // Original text: "Current version:" + currentVersion: '当前版本', + + // Original text: "Register" + register: '注册', + + // Original text: "Please, take time to register in order to enjoy your trial." + trialRegistration: '为了您的正常使用,请考虑花时间注册', + + // Original text: "Start trial" + trialStartButton: '开始试用', + + // Original text: "You can use a trial version until {date, date, medium}. Upgrade your appliance to get it." + trialAvailableUntil: + '你可以使用试用版本直到{date, date, medium}。更新你的设备来获取', + + // Original text: "Your trial has been ended. Contact us or downgrade to Free version" + trialConsumed: '你的使用已经结束,联系我们或下载免费版本', + + // Original text: "Your xoa-updater service appears to be down. Your XOA cannot run fully without reaching this service." + trialLocked: '你的xoa-更新服务已停止。没有此服务你的XOA不能完全正常运行', + + // Original text: "No update information available" + noUpdateInfo: '没有更新信息可用', + + // Original text: "Update information may be available" + waitingUpdateInfo: '更新信息可能可用', + + // Original text: "Your XOA is up-to-date" + upToDate: '你的XOA是最新的', + + // Original text: "You need to update your XOA (new version is available)" + mustUpgrade: '你需要更新你的XOA(有新版本可用)', + + // Original text: "Your XOA is not registered for updates" + registerNeeded: '你的XOA没有注册更新', + + // Original text: "Can't fetch update information" + updaterError: '不能获取更新信息', + + // Original text: "Upgrade successful" + promptUpgradeReloadTitle: '更新成功', + + // Original text: "Your XOA has successfully upgraded, and your browser must reload the application. Do you want to reload now ?" + promptUpgradeReloadMessage: + '你的XOA已经成功更新,你的浏览器必须重新加载,你要现在重新加载吗?', + + // Original text: "Xen Orchestra from the sources" + disclaimerTitle: 'Xen Orchestra 源码版', + + // Original text: "You are using XO from the sources! That's great for a personal/non-profit usage." + disclaimerText1: '你在使用XO的源码版!这非常适合个人/非商业用途', + + // Original text: "If you are a company, it's better to use it with our appliance + pro support included:" + disclaimerText2: '如果你是一个公司,建议使用我们的设备结合专业的支持', + + // Original text: "This version is not bundled with any support nor updates. Use it with caution for critical tasks." + disclaimerText3: '这个版本没有绑定任何支持或更新,在紧急任务下,请谨慎使用', + + // Original text: "Connect PIF" + connectPif: '连接物理网卡', + + // Original text: "Are you sure you want to connect this PIF?" + connectPifConfirm: '你确定要连接这个物理网卡?', + + // Original text: "Disconnect PIF" + disconnectPif: '断开物理网卡', + + // Original text: "Are you sure you want to disconnect this PIF?" + disconnectPifConfirm: '你确定要断开这个网卡网卡?', + + // Original text: "Delete PIF" + deletePif: '删除物理网卡', + + // Original text: "Are you sure you want to delete this PIF?" + deletePifConfirm: '你确定要删除这个物理网卡?', + + // Original text: "Username" + username: '用户名', + + // Original text: "Password" + password: '密码', + + // Original text: "Language" + language: '语言', + + // Original text: "Old password" + oldPasswordPlaceholder: '原密码', + + // Original text: "New password" + newPasswordPlaceholder: '新密码', + + // Original text: "Confirm new password" + confirmPasswordPlaceholder: '确认新密码', + + // Original text: "Confirmation password incorrect" + confirmationPasswordError: '确认密码不正确', + + // Original text: "Password does not match the confirm password." + confirmationPasswordErrorBody: '确认密码不匹配', + + // Original text: "Password changed" + pwdChangeSuccess: '密码已修改', + + // Original text: "Your password has been successfully changed." + pwdChangeSuccessBody: '你的密码已成功修改', + + // Original text: "Incorrect password" + pwdChangeError: '密码错误', + + // Original text: "The old password provided is incorrect. Your password has not been changed." + pwdChangeErrorBody: '原密码错误,你的密码未更改', + + // Original text: "OK" + changePasswordOk: '确认', + + // Original text: "Others" + others: '其他', +} diff --git a/packages/xo-web/src/common/intl/messages.js b/packages/xo-web/src/common/intl/messages.js new file mode 100644 index 000000000..724ed6a15 --- /dev/null +++ b/packages/xo-web/src/common/intl/messages.js @@ -0,0 +1,1775 @@ +// This file is coded in ES5 and CommonJS to be compatible with +// `create-locale`. + +const forEach = require('lodash/forEach') +const isString = require('lodash/isString') + +const messages = { + keyValue: '{key}: {value}', + + statusConnecting: 'Connecting', + statusDisconnected: 'Disconnected', + statusLoading: 'Loading…', + errorPageNotFound: 'Page not found', + errorNoSuchItem: 'no such item', + + editableLongClickPlaceholder: 'Long click to edit', + editableClickPlaceholder: 'Click to edit', + browseFiles: 'Browse files', + showLogs: 'Show logs', + + // ----- Modals ----- + alertOk: 'OK', + confirmOk: 'OK', + genericCancel: 'Cancel', + enterConfirmText: 'Enter the following text to confirm:', + + // ----- Filters ----- + onError: 'On error', + successful: 'Successful', + filterOnlyManaged: 'Managed disks', + filterOnlyOrphaned: 'Orphaned disks', + filterOnlyRegular: 'Normal disks', + filterOnlySnapshots: 'Snapshot disks', + filterOnlyUnmanaged: 'Unmanaged disks', + filterSaveAs: 'Save…', + filterSyntaxLinkTooltip: 'Explore the search syntax in the documentation', + filterVifsOnlyConnected: 'Connected VIFs', + filterVifsOnlyDisconnected: 'Disconnected VIFs', + filterRemotesOnlyConnected: 'Connected remotes', + filterRemotesOnlyDisconnected: 'Disconnected remotes', + + // ----- Copiable component ----- + copyToClipboard: 'Copy to clipboard', + + // ----- Pills ----- + pillMaster: 'Master', + + // ----- Titles ----- + homePage: 'Home', + homeVmPage: 'VMs', + homeHostPage: 'Hosts', + homePoolPage: 'Pools', + homeTemplatePage: 'Templates', + homeSrPage: 'Storages', + dashboardPage: 'Dashboard', + overviewDashboardPage: 'Overview', + overviewVisualizationDashboardPage: 'Visualizations', + overviewStatsDashboardPage: 'Statistics', + overviewHealthDashboardPage: 'Health', + selfServicePage: 'Self service', + backupPage: 'Backup', + jobsPage: 'Jobs', + xoaPage: 'XOA', + updatePage: 'Updates', + licensesPage: 'Licenses', + settingsPage: 'Settings', + settingsServersPage: 'Servers', + settingsUsersPage: 'Users', + settingsGroupsPage: 'Groups', + settingsAclsPage: 'ACLs', + settingsPluginsPage: 'Plugins', + settingsLogsPage: 'Logs', + settingsIpsPage: 'IPs', + settingsConfigPage: 'Config', + aboutPage: 'About', + aboutXoaPlan: 'About XO {xoaPlan}', + newMenu: 'New', + taskMenu: 'Tasks', + taskPage: 'Tasks', + newVmPage: 'VM', + newSrPage: 'Storage', + newServerPage: 'Server', + newImport: 'Import', + xosan: 'XOSAN', + backupOverviewPage: 'Overview', + backupNewPage: 'New', + backupRemotesPage: 'Remotes', + backupRestorePage: 'Restore', + backupFileRestorePage: 'File restore', + schedule: 'Schedule', + newVmBackup: 'New VM backup', + editVmBackup: 'Edit VM backup', + backup: 'Backup', + rollingSnapshot: 'Rolling Snapshot', + deltaBackup: 'Delta Backup', + disasterRecovery: 'Disaster Recovery', + continuousReplication: 'Continuous Replication', + jobsOverviewPage: 'Overview', + jobsNewPage: 'New', + jobsSchedulingPage: 'Scheduling', + customJob: 'Custom Job', + userPage: 'User', + xoa: 'XOA', + + // ----- Support ----- + noSupport: 'No support', + freeUpgrade: 'Free upgrade!', + + // ----- Sign out ----- + signOut: 'Sign out', + + // ----- User Profile ----- + editUserProfile: 'Edit my settings {username}', + + // ----- Home view ------ + homeFetchingData: 'Fetching data…', + homeWelcome: 'Welcome to Xen Orchestra!', + homeWelcomeText: 'Add your XenServer hosts or pools', + homeConnectServerText: + 'Some XenServers have been registered but are not connected', + homeHelp: 'Want some help?', + homeAddServer: 'Add server', + homeConnectServer: 'Connect servers', + homeOnlineDoc: 'Online Doc', + homeProSupport: 'Pro Support', + homeNoVms: 'There are no VMs!', + homeNoVmsOr: 'Or…', + homeImportVm: 'Import VM', + homeImportVmMessage: 'Import an existing VM in xva format', + homeRestoreBackup: 'Restore a backup', + homeRestoreBackupMessage: 'Restore a backup from a remote store', + homeNewVmMessage: 'This will create a new VM', + homeFilters: 'Filters', + homeNoMatches: 'No results! Click here to reset your filters', + homeTypePool: 'Pool', + homeTypeHost: 'Host', + homeTypeVm: 'VM', + homeTypeSr: 'SR', + homeTypeVmTemplate: 'Template', + homeSort: 'Sort', + homeAllPools: 'Pools', + homeAllHosts: 'Hosts', + homeAllTags: 'Tags', + homeAllResourceSets: 'Resource sets', + homeNewVm: 'New VM', + homeFilterNone: 'None', + homeFilterRunningHosts: 'Running hosts', + homeFilterDisabledHosts: 'Disabled hosts', + homeFilterRunningVms: 'Running VMs', + homeFilterNonRunningVms: 'Non running VMs', + homeFilterPendingVms: 'Pending VMs', + homeFilterHvmGuests: 'HVM guests', + homeFilterTags: 'Tags', + homeSortBy: 'Sort by', + homeSortByCpus: 'CPUs', + homeSortByName: 'Name', + homeSortByPowerstate: 'Power state', + homeSortByRAM: 'RAM', + homeSortByShared: 'Shared/Not shared', + homeSortBySize: 'Size', + homeSortByType: 'Type', + homeSortByUsage: 'Usage', + homeSortByvCPUs: 'vCPUs', + homeSortVmsBySnapshots: 'Snapshots', + homeDisplayedItems: '{displayed, number}x {icon} (on {total, number})', + homeSelectedItems: '{selected, number}x {icon} selected (on {total, number})', + homeMore: 'More', + homeMigrateTo: 'Migrate to…', + homeMissingPaths: 'Missing patches', + homePoolMaster: 'Master:', + homeResourceSet: 'Resource set: {resourceSet}', + highAvailability: 'High Availability', + srSharedType: 'Shared {type}', + srNotSharedType: 'Not shared {type}', + + // ----- Common components ----- + sortedTableAllItemsSelected: 'All of them are selected', + sortedTableNoItems: 'No items found', + sortedTableNumberOfFilteredItems: + '{nFiltered, number} of {nTotal, number} items', + sortedTableNumberOfItems: '{nTotal, number} items', + sortedTableNumberOfSelectedItems: '{nSelected, number} selected', + sortedTableSelectAllItems: 'Click here to select all items', + + // ----- Forms ----- + add: 'Add', + selectAll: 'Select all', + remove: 'Remove', + preview: 'Preview', + action: 'Action', + item: 'Item', + noSelectedValue: 'No selected value', + selectSubjects: 'Choose user(s) and/or group(s)', + selectObjects: 'Select Object(s)…', + selectRole: 'Choose a role', + selectHosts: 'Select Host(s)…', + selectHostsVms: 'Select object(s)…', + selectNetworks: 'Select Network(s)…', + selectPifs: 'Select PIF(s)…', + selectPools: 'Select Pool(s)…', + selectRemotes: 'Select Remote(s)…', + selectResourceSets: 'Select resource set(s)…', + selectResourceSetsVmTemplate: 'Select template(s)…', + selectResourceSetsSr: 'Select SR(s)…', + selectResourceSetsNetwork: 'Select network(s)…', + selectResourceSetsVdi: 'Select disk(s)…', + selectSshKey: 'Select SSH key(s)…', + selectSrs: 'Select SR(s)…', + selectVms: 'Select VM(s)…', + selectVmTemplates: 'Select VM template(s)…', + selectTags: 'Select tag(s)…', + selectVdis: 'Select disk(s)…', + selectTimezone: 'Select timezone…', + selectIp: 'Select IP(s)…', + selectIpPool: 'Select IP pool(s)…', + selectVgpuType: 'Select VGPU type(s)…', + fillRequiredInformations: 'Fill required informations.', + fillOptionalInformations: 'Fill informations (optional)', + selectTableReset: 'Reset', + + // --- Dates/Scheduler --- + + schedulingMonth: 'Month', + schedulingEveryNMonth: 'Every N month', + schedulingEachSelectedMonth: 'Each selected month', + schedulingDay: 'Day', + schedulingEveryNDay: 'Every N day', + schedulingEachSelectedDay: 'Each selected day', + schedulingSetWeekDayMode: 'Switch to week days', + schedulingSetMonthDayMode: 'Switch to month days', + schedulingHour: 'Hour', + schedulingEachSelectedHour: 'Each selected hour', + schedulingEveryNHour: 'Every N hour', + schedulingMinute: 'Minute', + schedulingEachSelectedMinute: 'Each selected minute', + schedulingEveryNMinute: 'Every N minute', + selectTableAllMonth: 'Every month', + selectTableAllDay: 'Every day', + selectTableAllHour: 'Every hour', + selectTableAllMinute: 'Every minute', + schedulingReset: 'Reset', + unknownSchedule: 'Unknown', + timezonePickerUseLocalTime: 'Web browser timezone', + serverTimezoneOption: 'Server timezone ({value})', + cronPattern: 'Cron Pattern:', + backupEditNotFoundTitle: 'Cannot edit backup', + backupEditNotFoundMessage: 'Missing required info for edition', + successfulJobCall: 'Successful', + failedJobCall: 'Failed', + jobCallInProgess: 'In progress', + jobTransferredDataSize: 'Transfer size:', + jobTransferredDataSpeed: 'Transfer speed:', + jobMergedDataSize: 'Merge size:', + jobMergedDataSpeed: 'Merge speed:', + allJobCalls: 'All', + job: 'Job', + jobModalTitle: 'Job {job}', + jobId: 'ID', + jobType: 'Type', + jobName: 'Name', + jobNamePlaceholder: 'Name of your job (forbidden: "_")', + jobStart: 'Start', + jobEnd: 'End', + jobDuration: 'Duration', + jobStatus: 'Status', + jobAction: 'Action', + jobTag: 'Tag', + jobScheduling: 'Scheduling', + jobState: 'State', + jobStateEnabled: 'Enabled', + jobStateDisabled: 'Disabled', + jobTimezone: 'Timezone', + jobServerTimezone: 'Server', + runJob: 'Run job', + runJobVerbose: 'One shot running started. See overview for logs.', + jobStarted: 'Started', + jobFinished: 'Finished', + saveBackupJob: 'Save', + deleteBackupSchedule: 'Remove backup job', + deleteBackupScheduleQuestion: + 'Are you sure you want to delete this backup job?', + scheduleEnableAfterCreation: 'Enable immediately after creation', + scheduleEditMessage: + 'You are editing Schedule {name} ({id}). Saving will override previous schedule state.', + jobEditMessage: + 'You are editing job {name} ({id}). Saving will override previous job state.', + scheduleEdit: 'Edit', + scheduleDelete: 'Delete', + deleteSelectedSchedules: 'Delete selected schedules', + noScheduledJobs: 'No scheduled jobs.', + newSchedule: 'New schedule', + noJobs: 'No jobs found.', + noSchedules: 'No schedules found', + jobActionPlaceHolder: 'Select a xo-server API command', + jobTimeoutPlaceHolder: + 'Timeout (number of seconds after which a VM is considered failed)', + jobSchedules: 'Schedules', + jobScheduleNamePlaceHolder: 'Name of your schedule', + jobScheduleJobPlaceHolder: 'Select a Job', + jobOwnerPlaceholder: 'Job owner', + jobUserNotFound: "This job's creator no longer exists", + backupUserNotFound: "This backup's creator no longer exists", + redirectToMatchingVms: 'Click here to see the matching VMs', + noMatchingVms: 'There are no matching VMs!', + allMatchingVms: '{icon} See the matching VMs ({nMatchingVms, number})', + backupOwner: 'Backup owner', + + // ------ New backup ----- + newBackupSelection: 'Select your backup type:', + smartBackupModeSelection: 'Select backup mode:', + normalBackup: 'Normal backup', + smartBackup: 'Smart backup', + localRemoteWarningTitle: 'Local remote selected', + localRemoteWarningMessage: + 'Warning: local remotes will use limited XOA disk space. Only for advanced users.', + backupVersionWarning: + 'Warning: this feature works only with XenServer 6.5 or newer.', + editBackupVmsTitle: 'VMs', + editBackupSmartStatusTitle: 'VMs statuses', + editBackupSmartResidentOn: 'Resident on', + editBackupSmartPools: 'Pools', + editBackupSmartTags: 'Tags', + sampleOfMatchingVms: 'Sample of matching Vms', + editBackupSmartTagsTitle: 'VMs Tags', + editBackupNot: 'Reverse', + editBackupTagTitle: 'Tag', + editBackupReportTitle: 'Report', + editBackupScheduleEnabled: 'Automatically run as scheduled', + editBackupRetentionTitle: 'Retention', + editBackupRemoteTitle: 'Remote', + deleteOldBackupsFirst: 'Delete the old backups first', + + // ------ New Remote ----- + remoteList: 'Remote stores for backup', + newRemote: 'New File System Remote', + remoteTypeLocal: 'Local', + remoteTypeNfs: 'NFS', + remoteTypeSmb: 'SMB', + remoteType: 'Type', + remoteSmbWarningMessage: + 'SMB remotes are meant to work on Windows Server. For other systems (Linux Samba, which means almost all NAS), please use NFS.', + remoteTestTip: 'Test your remote', + testRemote: 'Test Remote', + remoteTestFailure: 'Test failed for {name}', + remoteTestSuccess: 'Test passed for {name}', + remoteTestError: 'Error', + remoteTestStep: 'Test Step', + remoteTestFile: 'Test file', + remoteTestName: 'Test name', + remoteTestNameFailure: 'Remote name already exists!', + remoteTestSuccessMessage: 'The remote appears to work correctly', + remoteConnectionFailed: 'Connection failed', + + // ------ Remote ----- + remoteName: 'Name', + remotePath: 'Path', + remoteState: 'State', + remoteDevice: 'Device', + remoteShare: 'Share', + remoteAction: 'Action', + remoteAuth: 'Auth', + remoteMounted: 'Mounted', + remoteUnmounted: 'Unmounted', + remoteConnectTip: 'Connect', + remoteDisconnectTip: 'Disconnect', + remoteConnected: 'Connected', + remoteDisconnected: 'Disconnected', + remoteDeleteTip: 'Delete', + remoteDeleteSelected: 'Delete selected remotes', + remoteNamePlaceHolder: 'remote name *', + remoteMyNamePlaceHolder: 'Name *', + remoteLocalPlaceHolderPath: '/path/to/backup', + remoteNfsPlaceHolderHost: 'host *', + remoteNfsPlaceHolderPath: 'path/to/backup', + remoteSmbPlaceHolderRemotePath: 'subfolder [path\\\\to\\\\backup]', + remoteSmbPlaceHolderUsername: 'Username', + remoteSmbPlaceHolderPassword: 'Password', + remoteSmbPlaceHolderDomain: 'Domain', + remoteSmbPlaceHolderAddressShare: '
\\\\ *', + remotePlaceHolderPassword: 'password(fill to edit)', + + // ------ New Storage ----- + newSrTitle: 'Create a new SR', + newSrGeneral: 'General', + newSrTypeSelection: 'Select Storage Type:', + newSrSettings: 'Settings', + newSrUsage: 'Storage Usage', + newSrSummary: 'Summary', + newSrHost: 'Host', + newSrType: 'Type', + newSrName: 'Name', + newSrDescription: 'Description', + newSrServer: 'Server', + newSrPath: 'Path', + newSrIqn: 'IQN', + newSrLun: 'LUN', + newSrAuth: 'with auth.', + newSrUsername: 'User Name', + newSrPassword: 'Password', + newSrDevice: 'Device', + newSrInUse: 'in use', + newSrSize: 'Size', + newSrCreate: 'Create', + newSrNamePlaceHolder: 'Storage name', + newSrDescPlaceHolder: 'Storage description', + newSrAddressPlaceHolder: 'Address', + newSrPortPlaceHolder: '[port]', + newSrUsernamePlaceHolder: 'Username', + newSrPasswordPlaceHolder: 'Password', + newSrLvmDevicePlaceHolder: 'Device, e.g /dev/sda…', + newSrLocalPathPlaceHolder: '/path/to/directory', + + // ----- Acls, Users, Groups ------ + subjectName: 'Users/Groups', + objectName: 'Object', + aclNoneFound: 'No acls found', + roleName: 'Role', + aclCreate: 'Create', + newGroupName: 'New Group Name', + createGroup: 'Create Group', + createGroupButton: 'Create', + deleteGroup: 'Delete Group', + deleteGroupConfirm: 'Are you sure you want to delete this group?', + removeUserFromGroup: 'Remove user from Group', + deleteUserConfirm: 'Are you sure you want to delete this user?', + deleteUser: 'Delete User', + noUser: 'no user', + unknownUser: 'unknown user', + noGroupFound: 'No group found', + groupNameColumn: 'Name', + groupUsersColumn: 'Users', + addUserToGroupColumn: 'Add User', + userNameColumn: 'Email', + userPermissionColumn: 'Permissions', + userPasswordColumn: 'Password', + userName: 'Email', + userPassword: 'Password', + createUserButton: 'Create', + noUserFound: 'No user found', + userLabel: 'User', + adminLabel: 'Admin', + noUserInGroup: 'No user in group', + countUsers: '{users, number} user{users, plural, one {} other {s}}', + selectPermission: 'Select Permission', + + // ----- Plugins ------ + noPlugins: 'No plugins found', + autoloadPlugin: 'Auto-load at server start', + savePluginConfiguration: 'Save configuration', + deletePluginConfiguration: 'Delete configuration', + pluginError: 'Plugin error', + unknownPluginError: 'Unknown error', + purgePluginConfiguration: 'Purge plugin configuration', + purgePluginConfigurationQuestion: + 'Are you sure you want to purge this configuration ?', + editPluginConfiguration: 'Edit', + cancelPluginEdition: 'Cancel', + pluginConfigurationSuccess: 'Plugin configuration', + pluginConfigurationChanges: 'Plugin configuration successfully saved!', + pluginConfigurationPresetTitle: 'Predefined configuration', + pluginConfigurationChoosePreset: 'Choose a predefined configuration.', + applyPluginPreset: 'Apply', + + // ----- User preferences ----- + saveNewUserFilterErrorTitle: 'Save filter error', + saveNewUserFilterErrorBody: 'Bad parameter: name must be given.', + filterName: 'Name:', + filterValue: 'Value:', + saveNewFilterTitle: 'Save new filter', + setUserFiltersTitle: 'Set custom filters', + setUserFiltersBody: 'Are you sure you want to set custom filters?', + removeUserFilterTitle: 'Remove custom filter', + removeUserFilterBody: 'Are you sure you want to remove custom filter?', + defaultFilter: 'Default filter', + defaultFilters: 'Default filters', + customFilters: 'Custom filters', + customizeFilters: 'Customize filters', + saveCustomFilters: 'Save custom filters', + + // ----- VM actions ------ + startVmLabel: 'Start', + recoveryModeLabel: 'Recovery start', + suspendVmLabel: 'Suspend', + stopVmLabel: 'Stop', + forceShutdownVmLabel: 'Force shutdown', + rebootVmLabel: 'Reboot', + forceRebootVmLabel: 'Force reboot', + deleteVmLabel: 'Delete', + migrateVmLabel: 'Migrate', + snapshotVmLabel: 'Snapshot', + exportVmLabel: 'Export', + resumeVmLabel: 'Resume', + copyVmLabel: 'Copy', + cloneVmLabel: 'Clone', + fastCloneVmLabel: 'Fast clone', + convertVmToTemplateLabel: 'Convert to template', + vmConsoleLabel: 'Console', + + // ----- SR advanced tab ----- + + srUnhealthyVdiNameLabel: 'Name', + srUnhealthyVdiSize: 'Size', + srUnhealthyVdiDepth: 'Depth', + srUnhealthyVdiTitle: 'VDI to coalesce ({total, number})', + + // ----- SR actions ----- + srRescan: 'Rescan all disks', + srReconnectAll: 'Connect to all hosts', + srDisconnectAll: 'Disconnect from all hosts', + srForget: 'Forget this SR', + srsForget: 'Forget SRs', + srRemoveButton: 'Remove this SR', + srNoVdis: 'No VDIs in this storage', + // ----- Pool general ----- + poolTitleRamUsage: 'Pool RAM usage:', + poolRamUsage: '{used} used on {total}', + poolMaster: 'Master:', + displayAllHosts: 'Display all hosts of this pool', + displayAllStorages: 'Display all storages of this pool', + displayAllVMs: 'Display all VMs of this pool', + // ----- Pool tabs ----- + hostsTabName: 'Hosts', + vmsTabName: 'Vms', + srsTabName: 'Srs', + // ----- Pool advanced tab ----- + poolHaStatus: 'High Availability', + poolHaEnabled: 'Enabled', + poolHaDisabled: 'Disabled', + setpoolMaster: 'Master', + poolGpuGroups: 'GPU groups', + // ----- Pool host tab ----- + hostNameLabel: 'Name', + hostDescription: 'Description', + hostMemory: 'Memory', + noHost: 'No hosts', + memoryLeftTooltip: '{used}% used ({free} free)', + // ----- Pool network tab ----- + pif: 'PIF', + poolNetworkNameLabel: 'Name', + poolNetworkDescription: 'Description', + poolNetworkPif: 'PIFs', + poolNoNetwork: 'No networks', + poolNetworkMTU: 'MTU', + poolNetworkPifAttached: 'Connected', + poolNetworkPifDetached: 'Disconnected', + showPifs: 'Show PIFs', + hidePifs: 'Hide PIFs', + showDetails: 'Show details', + hideDetails: 'Hide details', + // ----- Pool stats tab ----- + poolNoStats: 'No stats', + poolAllHosts: 'All hosts', + // ----- Pool actions ------ + addSrLabel: 'Add SR', + addVmLabel: 'Add VM', + addHostLabel: 'Add Host', + hostNeedsPatchUpdate: + 'This host needs to install {patches, number} patch{patches, plural, one {} other {es}} before it can be added to the pool. This operation may be long.', + hostNeedsPatchUpdateNoInstall: + "This host cannot be added to the pool because it's missing some patches.", + addHostErrorTitle: 'Adding host failed', + addHostNotHomogeneousErrorMessage: 'Host patches could not be homogenized.', + disconnectServer: 'Disconnect', + + // ----- Host actions ------ + startHostLabel: 'Start', + stopHostLabel: 'Stop', + enableHostLabel: 'Enable', + disableHostLabel: 'Disable', + restartHostAgent: 'Restart toolstack', + forceRebootHostLabel: 'Force reboot', + rebootHostLabel: 'Reboot', + noHostsAvailableErrorTitle: 'Error while restarting host', + noHostsAvailableErrorMessage: + 'Some VMs cannot be migrated before restarting this host. Please try force reboot.', + failHostBulkRestartTitle: 'Error while restarting hosts', + failHostBulkRestartMessage: + '{failedHosts, number}/{totalHosts, number} host{failedHosts, plural, one {} other {s}} could not be restarted.', + rebootUpdateHostLabel: 'Reboot to apply updates', + emergencyModeLabel: 'Emergency mode', + // ----- Host tabs ----- + storageTabName: 'Storage', + patchesTabName: 'Patches', + // ----- host stat tab ----- + statLoad: 'Load average', + // ----- host advanced tab ----- + memoryHostState: 'RAM Usage: {memoryUsed}', + hardwareHostSettingsLabel: 'Hardware', + hostAddress: 'Address', + hostStatus: 'Status', + hostBuildNumber: 'Build number', + hostIscsiName: 'iSCSI name', + hostXenServerVersion: 'Version', + hostStatusEnabled: 'Enabled', + hostStatusDisabled: 'Disabled', + hostPowerOnMode: 'Power on mode', + hostStartedSince: 'Host uptime', + hostStackStartedSince: 'Toolstack uptime', + hostCpusModel: 'CPU model', + hostGpus: 'GPUs', + hostCpusNumber: 'Core (socket)', + hostManufacturerinfo: 'Manufacturer info', + hostBiosinfo: 'BIOS info', + licenseHostSettingsLabel: 'License', + hostLicenseType: 'Type', + hostLicenseSocket: 'Socket', + hostLicenseExpiry: 'Expiry', + supplementalPacks: 'Installed supplemental packs', + supplementalPackNew: 'Install new supplemental pack', + supplementalPackPoolNew: 'Install supplemental pack on every host', + supplementalPackTitle: '{name} (by {author})', + supplementalPackInstallStartedTitle: 'Installation started', + supplementalPackInstallStartedMessage: 'Installing new supplemental pack…', + supplementalPackInstallErrorTitle: 'Installation error', + supplementalPackInstallErrorMessage: + 'The installation of the supplemental pack failed.', + supplementalPackInstallSuccessTitle: 'Installation success', + supplementalPackInstallSuccessMessage: + 'Supplemental pack successfully installed.', + // ----- Host net tabs ----- + networkCreateButton: 'Add a network', + networkCreateBondedButton: 'Add a bonded network', + pifDeviceLabel: 'Device', + pifNetworkLabel: 'Network', + pifVlanLabel: 'VLAN', + pifAddressLabel: 'Address', + pifModeLabel: 'Mode', + pifMacLabel: 'MAC', + pifMtuLabel: 'MTU', + pifStatusLabel: 'Status', + pifStatusConnected: 'Connected', + pifStatusDisconnected: 'Disconnected', + pifNoInterface: 'No physical interface detected', + pifInUse: 'This interface is currently in use', + pifAction: 'Action', + defaultLockingMode: 'Default locking mode', + pifConfigureIp: 'Configure IP address', + configIpErrorTitle: 'Invalid parameters', + configIpErrorMessage: 'IP address and netmask required', + staticIp: 'Static IP address', + netmask: 'Netmask', + dns: 'DNS', + gateway: 'Gateway', + // ----- Host storage tabs ----- + addSrDeviceButton: 'Add a storage', + srNameLabel: 'Name', + srType: 'Type', + pbdAction: 'Action', + pbdStatus: 'Status', + pbdStatusConnected: 'Connected', + pbdStatusDisconnected: 'Disconnected', + pbdConnect: 'Connect', + pbdDisconnect: 'Disconnect', + pbdForget: 'Forget', + srShared: 'Shared', + srNotShared: 'Not shared', + pbdNoSr: 'No storage detected', + // ----- Host patch tabs ----- + patchNameLabel: 'Name', + patchUpdateButton: 'Install all patches', + patchDescription: 'Description', + patchApplied: 'Applied date', + patchSize: 'Size', + patchStatus: 'Status', + patchStatusApplied: 'Applied', + patchStatusNotApplied: 'Missing patches', + patchNothing: 'No patches detected', + patchReleaseDate: 'Release date', + patchGuidance: 'Guidance', + patchAction: 'Action', + hostAppliedPatches: 'Applied patches', + hostMissingPatches: 'Missing patches', + hostUpToDate: 'Host up-to-date!', + installPatchWarningTitle: 'Non-recommended patch install', + installPatchWarningContent: + 'This will install a patch only on this host. This is NOT the recommended way: please go into the Pool patch view and follow instructions there. If you are sure about this, you can continue anyway', + installPatchWarningReject: 'Go to pool', + installPatchWarningResolve: 'Install', + // ----- Pool patch tabs ----- + refreshPatches: 'Refresh patches', + installPoolPatches: 'Install pool patches', + // ----- Pool storage tabs ----- + defaultSr: 'Default SR', + setAsDefaultSr: 'Set as default SR', + + // ----- VM tabs ----- + generalTabName: 'General', + statsTabName: 'Stats', + consoleTabName: 'Console', + containersTabName: 'Container', + snapshotsTabName: 'Snapshots', + logsTabName: 'Logs', + advancedTabName: 'Advanced', + networkTabName: 'Network', + disksTabName: 'Disk{disks, plural, one {} other {s}}', + + powerStateHalted: 'halted', + powerStateRunning: 'running', + powerStateSuspended: 'suspended', + + // ----- VM home ----- + vmStatus: 'No Xen tools detected', + vmName: 'No IPv4 record', + vmDescription: 'No IP record', + vmSettings: 'Started {ago}', + vmCurrentStatus: 'Current status:', + vmNotRunning: 'Not running', + vmHaltedSince: 'Halted {ago}', + + // ----- VM general tab ----- + noToolsDetected: 'No Xen tools detected', + noIpv4Record: 'No IPv4 record', + noIpRecord: 'No IP record', + started: 'Started {ago}', + paraVirtualizedMode: 'Paravirtualization (PV)', + hardwareVirtualizedMode: 'Hardware virtualization (HVM)', + + // ----- VM stat tab ----- + statsCpu: 'CPU usage', + statsMemory: 'Memory usage', + statsNetwork: 'Network throughput', + useStackedValuesOnStats: 'Stacked values', + statDisk: 'Disk throughput', + statLastTenMinutes: 'Last 10 minutes', + statLastTwoHours: 'Last 2 hours', + statLastWeek: 'Last week', + statLastYear: 'Last year', + + // ----- VM console tab ----- + copyToClipboardLabel: 'Copy', + ctrlAltDelButtonLabel: 'Ctrl+Alt+Del', + tipLabel: 'Tip:', + hideHeaderTooltip: 'Hide infos', + showHeaderTooltip: 'Show infos', + + // ----- VM container tab ----- + containerName: 'Name', + containerCommand: 'Command', + containerCreated: 'Creation date', + containerStatus: 'Status', + containerAction: 'Action', + noContainers: 'No existing containers', + containerStop: 'Stop this container', + containerStart: 'Start this container', + containerPause: 'Pause this container', + containerResume: 'Resume this container', + containerRestart: 'Restart this container', + + // ----- VM disk tab ----- + vdiAction: 'Action', + vdiAttachDeviceButton: 'Attach disk', + vbdCreateDeviceButton: 'New disk', + vdiBootOrder: 'Boot order', + vdiNameLabel: 'Name', + vdiNameDescription: 'Description', + vdiPool: 'Pool', + vdiDisconnect: 'Disconnect', + vdiTags: 'Tags', + vdiSize: 'Size', + vdiSr: 'SR', + vdiVm: 'VM', + vdiMigrate: 'Migrate VDI', + vdiMigrateSelectSr: 'Destination SR:', + vdiMigrateAll: 'Migrate all VDIs', + vdiMigrateNoSr: 'No SR', + vdiMigrateNoSrMessage: 'A target SR is required to migrate a VDI', + vdiForget: 'Forget', + vdiRemove: 'Remove VDI', + noControlDomainVdis: 'No VDIs attached to Control Domain', + vbdBootableStatus: 'Boot flag', + vbdStatus: 'Status', + vbdStatusConnected: 'Connected', + vbdStatusDisconnected: 'Disconnected', + vbdNoVbd: 'No disks', + vbdConnect: 'Connect VBD', + vbdDisconnect: 'Disconnect VBD', + vbdBootable: 'Bootable', + vbdReadonly: 'Readonly', + vbdAction: 'Action', + vbdCreate: 'Create', + vbdAttach: 'Attach', + vbdNamePlaceHolder: 'Disk name', + vbdSizePlaceHolder: 'Size', + cdDriveNotInstalled: 'CD drive not completely installed', + cdDriveInstallation: 'Stop and start the VM to install the CD drive', + saveBootOption: 'Save', + resetBootOption: 'Reset', + deleteSelectedVdis: 'Delete selected VDIs', + deleteSelectedVdi: 'Delete selected VDI', + useQuotaWarning: + 'Creating this disk will use the disk space quota from the resource set {resourceSet} ({spaceLeft} left)', + notEnoughSpaceInResourceSet: + 'Not enough space in resource set {resourceSet} ({spaceLeft} left)', + + // ----- VM network tab ----- + vifCreateDeviceButton: 'New device', + vifNoInterface: 'No interface', + vifDeviceLabel: 'Device', + vifMacLabel: 'MAC address', + vifMtuLabel: 'MTU', + vifNetworkLabel: 'Network', + vifStatusLabel: 'Status', + vifStatusConnected: 'Connected', + vifStatusDisconnected: 'Disconnected', + vifConnect: 'Connect', + vifDisconnect: 'Disconnect', + vifRemove: 'Remove', + vifsRemove: 'Remove selected VIFs', + vifIpAddresses: 'IP addresses', + vifMacAutoGenerate: 'Auto-generated if empty', + vifAllowedIps: 'Allowed IPs', + vifNoIps: 'No IPs', + vifLockedNetwork: 'Network locked', + vifLockedNetworkNoIps: + 'Network locked and no IPs are allowed for this interface', + vifUnLockedNetwork: 'Network not locked', + vifUnknownNetwork: 'Unknown network', + vifAction: 'Action', + vifCreate: 'Create', + + // ----- VM snapshot tab ----- + noSnapshots: 'No snapshots', + snapshotCreateButton: 'New snapshot', + tipCreateSnapshotLabel: 'Just click on the snapshot button to create one!', + revertSnapshot: 'Revert VM to this snapshot', + deleteSnapshot: 'Remove this snapshot', + deleteSnapshots: 'Remove selected snapshots', + copySnapshot: 'Create a VM from this snapshot', + exportSnapshot: 'Export this snapshot', + snapshotDate: 'Creation date', + snapshotName: 'Name', + snapshotDescription: 'Description', + snapshotAction: 'Action', + snapshotQuiesce: 'Quiesced snapshot', + + // ----- VM log tab ----- + logRemoveAll: 'Remove all logs', + noLogs: 'No logs so far', + logDate: 'Creation date', + logName: 'Name', + logContent: 'Content', + logAction: 'Action', + + // ----- VM advanced tab ----- + vmRemoveButton: 'Remove', + vmConvertButton: 'Convert', + xenSettingsLabel: 'Xen settings', + guestOsLabel: 'Guest OS', + miscLabel: 'Misc', + uuid: 'UUID', + virtualizationMode: 'Virtualization mode', + cpuWeightLabel: 'CPU weight', + defaultCpuWeight: 'Default ({value, number})', + cpuCapLabel: 'CPU cap', + defaultCpuCap: 'Default ({value, number})', + pvArgsLabel: 'PV args', + xenToolsStatus: 'Xen tools status', + xenToolsStatusValue: { + defaultMessage: '{status}', + description: + 'status can be `not-installed`, `unknown`, `out-of-date` & `up-to-date`', + }, + osName: 'OS name', + osKernel: 'OS kernel', + autoPowerOn: 'Auto power on', + ha: 'HA', + vmAffinityHost: 'Affinity host', + vmVga: 'VGA', + vmVideoram: 'Video RAM', + noAffinityHost: 'None', + originalTemplate: 'Original template', + unknownOsName: 'Unknown', + unknownOsKernel: 'Unknown', + unknownOriginalTemplate: 'Unknown', + vmLimitsLabel: 'VM limits', + resourceSet: 'Resource set', + resourceSetNone: 'None', + vmCpuLimitsLabel: 'CPU limits', + vmCpuTopology: 'Topology', + vmChooseCoresPerSocket: 'Default behavior', + vmCoresPerSocket: + '{nSockets, number} socket{nSockets, plural, one {} other {s}} with {nCores, number} core{nCores, plural, one {} other {s}} per socket', + vmCoresPerSocketNone: 'None', + vmCoresPerSocketIncorrectValue: 'Incorrect cores per socket value', + vmCoresPerSocketIncorrectValueSolution: + 'Please change the selected value to fix it.', + vmMemoryLimitsLabel: 'Memory limits (min/max)', + vmMaxVcpus: 'vCPUs max:', + vmMaxRam: 'Memory max:', + vmVgpu: 'vGPU', + vmVgpus: 'GPUs', + vmVgpuNone: 'None', + vmAddVgpu: 'Add vGPU', + vmSelectVgpuType: 'Select vGPU type', + + // ----- VM placeholders ----- + + vmHomeNamePlaceholder: 'Long click to add a name', + vmHomeDescriptionPlaceholder: 'Long click to add a description', + vmViewNamePlaceholder: 'Click to add a name', + vmViewDescriptionPlaceholder: 'Click to add a description', + + // ----- Templates ----- + + templateHomeNamePlaceholder: 'Click to add a name', + templateHomeDescriptionPlaceholder: 'Click to add a description', + templateDelete: 'Delete template', + templateDeleteModalTitle: + 'Delete VM template{templates, plural, one {} other {s}}', + templateDeleteModalBody: + 'Are you sure you want to delete {templates, plural, one {this} other {these}} template{templates, plural, one {} other {s}}?', + + // ----- Dashboard ----- + poolPanel: 'Pool{pools, plural, one {} other {s}}', + hostPanel: 'Host{hosts, plural, one {} other {s}}', + vmPanel: 'VM{vms, plural, one {} other {s}}', + memoryStatePanel: 'RAM Usage:', + usedMemory: 'Used Memory', + totalMemory: 'Total Memory', + totalCpus: 'CPUs Total', + usedVCpus: 'Used vCPUs', + usedSpace: 'Used Space', + totalSpace: 'Total Space', + cpuStatePanel: 'CPUs Usage', + vmStatePanel: 'VMs Power state', + vmStateHalted: 'Halted', + vmStateOther: 'Other', + vmStateRunning: 'Running', + taskStatePanel: 'Pending tasks', + usersStatePanel: 'Users', + srStatePanel: 'Storage state', + ofUsage: '{usage} (of {total})', + ofCpusUsage: + '{nVcpus, number} vCPU{nVcpus, plural, one {} other {s}} (of {nCpus, number} CPU{nCpus, plural, one {} other {s}})', + noSrs: 'No storage', + srName: 'Name', + srPool: 'Pool', + srHost: 'Host', + srFormat: 'Type', + srSize: 'Size', + srUsage: 'Usage', + srUsed: 'used', + srFree: 'free', + srUsageStatePanel: 'Storage Usage', + srTopUsageStatePanel: 'Top 5 SR Usage (in %)', + notEnoughPermissionsError: 'Not enough permissions!', + vmsStates: '{running, number} running ({halted, number} halted)', + dashboardStatsButtonRemoveAll: 'Clear selection', + dashboardStatsButtonAddAllHost: 'Add all hosts', + dashboardStatsButtonAddAllVM: 'Add all VMs', + + // --- Stats board -- + weekHeatmapData: '{value} {date, date, medium}', + weekHeatmapNoData: 'No data.', + weeklyHeatmap: 'Weekly Heatmap', + weeklyCharts: 'Weekly Charts', + weeklyChartsScaleInfo: 'Synchronize scale:', + statsDashboardGenericErrorTitle: 'Stats error', + statsDashboardGenericErrorMessage: 'There is no stats available for:', + noSelectedMetric: 'No selected metric', + statsDashboardSelectObjects: 'Select', + metricsLoading: 'Loading…', + + // ----- Visualizations ----- + comingSoon: 'Coming soon!', + + // ----- Health ----- + orphanedVdis: 'Orphaned snapshot VDIs', + orphanedVms: 'Orphaned VMs snapshot', + noOrphanedObject: 'No orphans', + removeAllOrphanedObject: 'Remove all orphaned snapshot VDIs', + vdisOnControlDomain: 'VDIs attached to Control Domain', + vmNameLabel: 'Name', + vmNameDescription: 'Description', + vmContainer: 'Resident on', + alarmMessage: 'Alarms', + noAlarms: 'No alarms', + alarmDate: 'Date', + alarmContent: 'Content', + alarmObject: 'Issue on', + alarmPool: 'Pool', + alarmRemoveAll: 'Remove all alarms', + spaceLeftTooltip: '{used}% used ({free} left)', + + // ----- New VM ----- + newVmCreateNewVmOn: 'Create a new VM on {select}', + newVmCreateNewVmNoPermission: 'You have no permission to create a VM', + newVmInfoPanel: 'Infos', + newVmNameLabel: 'Name', + newVmTemplateLabel: 'Template', + newVmDescriptionLabel: 'Description', + newVmPerfPanel: 'Performances', + newVmVcpusLabel: 'vCPUs', + newVmRamLabel: 'RAM', + newVmStaticMaxLabel: 'Static memory max', + newVmDynamicMinLabel: 'Dynamic memory min', + newVmDynamicMaxLabel: 'Dynamic memory max', + newVmInstallSettingsPanel: 'Install settings', + newVmIsoDvdLabel: 'ISO/DVD', + newVmNetworkLabel: 'Network', + newVmInstallNetworkPlaceHolder: 'e.g: http://httpredir.debian.org/debian', + newVmPvArgsLabel: 'PV Args', + newVmPxeLabel: 'PXE', + newVmInterfacesPanel: 'Interfaces', + newVmMacLabel: 'MAC', + newVmAddInterface: 'Add interface', + newVmDisksPanel: 'Disks', + newVmSrLabel: 'SR', + newVmSizeLabel: 'Size', + newVmAddDisk: 'Add disk', + newVmSummaryPanel: 'Summary', + newVmCreate: 'Create', + newVmReset: 'Reset', + newVmSelectTemplate: 'Select template', + newVmSshKey: 'SSH key', + newVmConfigDrive: 'Config drive', + newVmCustomConfig: 'Custom config', + newVmBootAfterCreate: 'Boot VM after creation', + newVmMacPlaceholder: 'Auto-generated if empty', + newVmCpuWeightLabel: 'CPU weight', + newVmDefaultCpuWeight: 'Default: {value, number}', + newVmCpuCapLabel: 'CPU cap', + newVmDefaultCpuCap: 'Default: {value, number}', + newVmCloudConfig: 'Cloud config', + newVmCreateVms: 'Create VMs', + newVmCreateVmsConfirm: 'Are you sure you want to create {nbVms, number} VMs?', + newVmMultipleVms: 'Multiple VMs:', + newVmSelectResourceSet: 'Select a resource set:', + newVmMultipleVmsPattern: 'Name pattern:', + newVmMultipleVmsPatternPlaceholder: 'e.g.: \\{name\\}_%', + newVmFirstIndex: 'First index:', + newVmNumberRecalculate: 'Recalculate VMs number', + newVmNameRefresh: 'Refresh VMs name', + newVmAffinityHost: 'Affinity host', + newVmAdvancedPanel: 'Advanced', + newVmShowAdvanced: 'Show advanced settings', + newVmHideAdvanced: 'Hide advanced settings', + newVmShare: 'Share this VM', + + // ----- Self ----- + resourceSets: 'Resource sets', + noResourceSets: 'No resource sets.', + loadingResourceSets: 'Loading resource sets', + resourceSetName: 'Resource set name', + resourceSetUsers: 'Users', + resourceSetPools: 'Pools', + resourceSetTemplates: 'Templates', + resourceSetSrs: 'SRs', + resourceSetNetworks: 'Networks', + recomputeResourceSets: 'Recompute all limits', + saveResourceSet: 'Save', + resetResourceSet: 'Reset', + editResourceSet: 'Edit', + deleteResourceSet: 'Delete', + deleteResourceSetWarning: 'Delete resource set', + deleteResourceSetQuestion: + 'Are you sure you want to delete this resource set?', + resourceSetMissingObjects: 'Missing objects:', + resourceSetVcpus: 'vCPUs', + resourceSetMemory: 'Memory', + resourceSetStorage: 'Storage', + unknownResourceSetValue: 'Unknown', + availableHosts: 'Available hosts', + excludedHosts: 'Excluded hosts', + noHostsAvailable: 'No hosts available.', + availableHostsDescription: + 'VMs created from this resource set shall run on the following hosts.', + maxCpus: 'Maximum CPUs', + maxRam: 'Maximum RAM', + maxDiskSpace: 'Maximum disk space', + ipPool: 'IP pool', + quantity: 'Quantity', + noResourceSetLimits: 'No limits.', + remainingResource: 'Remaining:', + usedResourceLabel: 'Used', + availableResourceLabel: 'Available', + resourceSetQuota: 'Used: {usage} (Total: {total})', + resourceSetNew: 'New', + + // ---- VM import --- + importVmsList: + 'Try dropping some VMs files here, or click to select VMs to upload. Accept only .xva/.ova files.', + noSelectedVms: 'No selected VMs.', + vmImportToPool: 'To Pool:', + vmImportToSr: 'To SR:', + vmsToImport: 'VMs to import', + importVmsCleanList: 'Reset', + vmImportSuccess: 'VM import success', + vmImportFailed: 'VM import failed', + startVmImport: 'Import starting…', + startVmExport: 'Export starting…', + nCpus: 'N CPUs', + vmMemory: 'Memory', + diskInfo: 'Disk {position} ({capacity})', + diskDescription: 'Disk description', + noDisks: 'No disks.', + noNetworks: 'No networks.', + networkInfo: 'Network {name}', + noVmImportErrorDescription: 'No description available', + vmImportError: 'Error:', + vmImportFileType: '{type} file:', + vmImportConfigAlert: 'Please to check and/or modify the VM configuration.', + + // ---- Tasks --- + noTasks: 'No pending tasks', + xsTasks: 'Currently, there are not any pending XenServer tasks', + cancelTask: 'Cancel', + destroyTask: 'Destroy', + cancelTasks: 'Cancel selected tasks', + destroyTasks: 'Destroy selected tasks', + pool: 'Pool', + task: 'Task', + progress: 'Progress', + + // ---- Backup views --- + backupSchedules: 'Schedules', + getRemote: 'Get remote', + listRemote: 'List Remote', + simpleBackup: 'simple', + delta: 'delta', + restoreBackups: 'Restore Backups', + restoreBackupsInfo: 'Click on a VM to display restore options', + restoreDeltaBackupsInfo: + 'Only the files of Delta Backup which are not on a SMB remote can be restored', + remoteEnabled: 'Enabled', + remoteError: 'Error', + noBackup: 'No backup available', + backupVmNameColumn: 'VM Name', + backupTags: 'Tags', + lastBackupColumn: 'Last Backup', + availableBackupsColumn: 'Available Backups', + backupRestoreErrorTitle: 'Missing parameters', + backupRestoreErrorMessage: 'Choose a SR and a backup', + backupRestoreSelectDefaultSr: 'Select default SR…', + backupRestoreChooseSrForEachVdis: 'Choose a SR for each VDI', + backupRestoreVdiLabel: 'VDI', + backupRestoreSrLabel: 'SR', + displayBackup: 'Display backups', + importBackupTitle: 'Import VM', + importBackupMessage: 'Starting your backup import', + vmsToBackup: 'VMs to backup', + + // ----- Restore files view ----- + listRemoteBackups: 'List remote backups', + restoreFiles: 'Restore backup files', + restoreFilesError: 'Invalid options', + restoreFilesFromBackup: 'Restore file from {name}', + restoreFilesSelectBackup: 'Select a backup…', + restoreFilesSelectDisk: 'Select a disk…', + restoreFilesSelectPartition: 'Select a partition…', + restoreFilesSelectFolderPath: 'Folder path', + restoreFilesSelectFiles: 'Select a file…', + restoreFileContentNotFound: 'Content not found', + restoreFilesNoFilesSelected: 'No files selected', + restoreFilesSelectedFiles: 'Selected files ({files}):', + restoreFilesDiskError: 'Error while scanning disk', + restoreFilesSelectAllFiles: "Select all this folder's files", + restoreFilesUnselectAll: 'Unselect all files', + + // ----- Modals ----- + emergencyShutdownHostsModalTitle: + 'Emergency shutdown Host{nHosts, plural, one {} other {s}}', + emergencyShutdownHostsModalMessage: + 'Are you sure you want to shutdown {nHosts, number} Host{nHosts, plural, one {} other {s}}?', + stopHostModalTitle: 'Shutdown host', + stopHostModalMessage: + "This will shutdown your host. Do you want to continue? If it's the pool master, your connection to the pool will be lost", + addHostModalTitle: 'Add host', + addHostModalMessage: 'Are you sure you want to add {host} to {pool}?', + restartHostModalTitle: 'Restart host', + restartHostModalMessage: + 'This will restart your host. Do you want to continue?', + restartHostsAgentsModalTitle: + 'Restart Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}', + restartHostsAgentsModalMessage: + 'Are you sure you want to restart {nHosts, number} Host{nHosts, plural, one {} other {s}} agent{nHosts, plural, one {} other {s}}?', + restartHostsModalTitle: 'Restart Host{nHosts, plural, one {} other {s}}', + restartHostsModalMessage: + 'Are you sure you want to restart {nHosts, number} Host{nHosts, plural, one {} other {s}}?', + startVmsModalTitle: 'Start VM{vms, plural, one {} other {s}}', + cloneAndStartVM: 'Start a copy', + forceStartVm: 'Force start', + forceStartVmModalTitle: 'Forbidden operation', + blockedStartVmModalMessage: 'Start operation for this vm is blocked.', + blockedStartVmsModalMessage: + 'Forbidden operation start for {nVms, number} vm{nVms, plural, one {} other {s}}.', + startVmsModalMessage: + 'Are you sure you want to start {vms, number} VM{vms, plural, one {} other {s}}?', + failedVmsErrorMessage: + '{nVms, number} vm{nVms, plural, one {} other {s}} are failed. Please see your logs to get more information', + failedVmsErrorTitle: 'Start failed', + stopHostsModalTitle: 'Stop Host{nHosts, plural, one {} other {s}}', + stopHostsModalMessage: + 'Are you sure you want to stop {nHosts, number} Host{nHosts, plural, one {} other {s}}?', + stopVmsModalTitle: 'Stop VM{vms, plural, one {} other {s}}', + stopVmsModalMessage: + 'Are you sure you want to stop {vms, number} VM{vms, plural, one {} other {s}}?', + restartVmModalTitle: 'Restart VM', + restartVmModalMessage: 'Are you sure you want to restart {name}?', + stopVmModalTitle: 'Stop VM', + stopVmModalMessage: 'Are you sure you want to stop {name}?', + suspendVmsModalTitle: 'Suspend VM{vms, plural, one {} other {s}}', + suspendVmsModalMessage: + 'Are you sure you want to suspend {vms, number} VM{vms, plural, one {} other {s}}?', + restartVmsModalTitle: 'Restart VM{vms, plural, one {} other {s}}', + restartVmsModalMessage: + 'Are you sure you want to restart {vms, number} VM{vms, plural, one {} other {s}}?', + snapshotVmsModalTitle: 'Snapshot VM{vms, plural, one {} other {s}}', + snapshotVmsModalMessage: + 'Are you sure you want to snapshot {vms, number} VM{vms, plural, one {} other {s}}?', + deleteVmsModalTitle: 'Delete VM{vms, plural, one {} other {s}}', + deleteVmsModalMessage: + 'Are you sure you want to delete {vms, number} VM{vms, plural, one {} other {s}}? ALL VM DISKS WILL BE REMOVED', + deleteVmsConfirmText: + 'delete {nVms, number} vm{nVms, plural, one {} other {s}}', + deleteVmModalTitle: 'Delete VM', + deleteVmModalMessage: + 'Are you sure you want to delete this VM? ALL VM DISKS WILL BE REMOVED', + deleteVmBlockedModalTitle: 'Blocked operation', + deleteVmBlockedModalMessage: + 'Removing the VM is a blocked operation. Would you like to remove it anyway?', + migrateVmModalTitle: 'Migrate VM', + migrateVmSelectHost: 'Select a destination host:', + migrateVmSelectMigrationNetwork: 'Select a migration network:', + migrateVmSelectNetworks: 'For each VIF, select a network:', + migrateVmsSelectSr: 'Select a destination SR:', + migrateVmsSelectSrIntraPool: 'Select a destination SR for local disks:', + migrateVmsSelectNetwork: 'Select a network on which to connect each VIF:', + migrateVmsSmartMapping: 'Smart mapping', + migrateVmVif: 'VIF', + migrateVmNetwork: 'Network', + migrateVmNoTargetHost: 'No target host', + migrateVmNoTargetHostMessage: 'A target host is required to migrate a VM', + migrateVmNoDefaultSrError: 'No default SR', + migrateVmNotConnectedDefaultSrError: 'Default SR not connected to host', + chooseSrForEachVdisModalSelectSr: 'For each VDI, select an SR:', + chooseSrForEachVdisModalMainSr: 'Select main SR…', + chooseSrForEachVdisModalVdiLabel: 'VDI', + chooseSrForEachVdisModalSrLabel: 'SR*', + chooseSrForEachVdisModalOptionalEntry: '* optional', + deleteVdiModalTitle: 'Delete VDI', + deleteVdiModalMessage: + 'Are you sure you want to delete this disk? ALL DATA ON THIS DISK WILL BE LOST', + deleteVdisModalTitle: 'Delete VDI{nVdis, plural, one {} other {s}}', + deleteVdisModalMessage: + 'Are you sure you want to delete {nVdis, number} disk{nVdis, plural, one {} other {s}}? ALL DATA ON THESE DISKS WILL BE LOST', + deleteSchedulesModalTitle: + 'Delete schedule{nSchedules, plural, one {} other {s}}', + deleteSchedulesModalMessage: + 'Are you sure you want to delete {nSchedules, number} schedule{nSchedules, plural, one {} other {s}}?', + deleteRemotesModalTitle: 'Delete remote{nRemotes, plural, one {} other {s}}', + deleteRemotesModalMessage: + 'Are you sure you want to delete {nRemotes, number} remote{nRemotes, plural, one {} other {s}}?', + revertVmModalTitle: 'Revert your VM', + deleteVifsModalTitle: 'Delete VIF{nVifs, plural, one {} other {s}}', + deleteVifsModalMessage: + 'Are you sure you want to delete {nVifs, number} VIF{nVifs, plural, one {} other {s}}?', + deleteSnapshotModalTitle: 'Delete snapshot', + deleteSnapshotModalMessage: 'Are you sure you want to delete this snapshot?', + deleteSnapshotsModalTitle: 'Delete snapshot{nVms, plural, one {} other {s}}', + deleteSnapshotsModalMessage: + 'Are you sure you want to delete {nVms, number} snapshot{nVms, plural, one {} other {s}}?', + revertVmModalMessage: + 'Are you sure you want to revert this VM to the snapshot state? This operation is irreversible.', + revertVmModalSnapshotBefore: 'Snapshot before', + importBackupModalTitle: 'Import a {name} Backup', + importBackupModalStart: 'Start VM after restore', + importBackupModalSelectBackup: 'Select your backup…', + removeAllOrphanedModalWarning: + 'Are you sure you want to remove all orphaned snapshot VDIs?', + removeAllLogsModalTitle: 'Remove all logs', + removeAllLogsModalWarning: 'Are you sure you want to remove all logs?', + definitiveMessageModal: 'This operation is definitive.', + existingSrModalTitle: 'Previous SR Usage', + existingSrModalText: + 'This path has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation.', + existingLunModalTitle: 'Previous LUN Usage', + existingLunModalText: + 'This LUN has been previously used as a Storage by a XenServer host. All data will be lost if you choose to continue the SR creation.', + alreadyRegisteredModal: 'Replace current registration?', + alreadyRegisteredModalText: + 'Your XO appliance is already registered to {email}, do you want to forget and replace this registration ?', + trialReadyModal: 'Ready for trial?', + trialReadyModalText: + 'During the trial period, XOA need to have a working internet connection. This limitation does not apply for our paid plans!', + cancelTasksModalTitle: 'Cancel task{nTasks, plural, one {} other {s}}', + cancelTasksModalMessage: + 'Are you sure you want to cancel {nTasks, number} task{nTasks, plural, one {} other {s}}?', + destroyTasksModalTitle: 'Destroy task{nTasks, plural, one {} other {s}}', + destroyTasksModalMessage: + 'Are you sure you want to destroy {nTasks, number} task{nTasks, plural, one {} other {s}}?', + + // ----- Servers ----- + serverLabel: 'Label', + serverHost: 'Host', + serverUsername: 'Username', + serverPassword: 'Password', + serverAction: 'Action', + serverReadOnly: 'Read Only', + serverUnauthorizedCertificates: 'Unauthorized Certificates', + serverAllowUnauthorizedCertificates: 'Allow Unauthorized Certificates', + serverUnauthorizedCertificatesInfo: + "Enable it if your certificate is rejected, but it's not recommended because your connection will not be secured.", + serverDisconnect: 'Disconnect server', + serverPlaceHolderUser: 'username', + serverPlaceHolderPassword: 'password', + serverPlaceHolderAddress: 'address[:port]', + serverPlaceHolderLabel: 'label', + serverConnect: 'Connect', + serverError: 'Error', + serverAddFailed: 'Adding server failed', + serverStatus: 'Status', + serverConnectionFailed: 'Connection failed. Click for more information.', + serverConnecting: 'Connecting…', + serverConnected: 'Connected', + serverDisconnected: 'Disconnected', + serverAuthFailed: 'Authentication error', + serverUnknownError: 'Unknown error', + serverSelfSignedCertError: 'Invalid self-signed certificate', + serverSelfSignedCertQuestion: + 'Do you want to accept self-signed certificate for this server even though it would decrease security?', + + // ----- Copy VM ----- + copyVm: 'Copy VM', + copyVmConfirm: 'Are you sure you want to copy this VM to {SR}?', + copyVmName: 'Name', + copyVmNamePattern: 'Name pattern', + copyVmNamePlaceholder: 'If empty: name of the copied VM', + copyVmNamePatternPlaceholder: 'e.g.: "\\{name\\}_COPY"', + copyVmSelectSr: 'Select SR', + copyVmCompress: 'Use compression', + copyVmsNoTargetSr: 'No target SR', + copyVmsNoTargetSrMessage: 'A target SR is required to copy a VM', + + // ----- Detach host ----- + detachHostModalTitle: 'Detach host', + detachHostModalMessage: + 'Are you sure you want to detach {host} from its pool? THIS WILL REMOVE ALL VMs ON ITS LOCAL STORAGE AND REBOOT THE HOST.', + detachHost: 'Detach', + + // ----- Forget host ----- + forgetHostModalTitle: 'Forget host', + forgetHostModalMessage: + "Are you sure you want to forget {host} from its pool? Be sure this host can't be back online, or use detach instead.", + forgetHost: 'Forget', + + // ----- Set pool master ----- + + setPoolMasterModalTitle: 'Designate a new master', + setPoolMasterModalMessage: + 'This operation may take several minutes. Do you want to continue?', + + // ----- Network ----- + newNetworkCreate: 'Create network', + newBondedNetworkCreate: 'Create bonded network', + newNetworkInterface: 'Interface', + newNetworkName: 'Name', + newNetworkDescription: 'Description', + newNetworkVlan: 'VLAN', + newNetworkDefaultVlan: 'No VLAN if empty', + newNetworkMtu: 'MTU', + newNetworkDefaultMtu: 'Default: 1500', + newNetworkNoNameErrorTitle: 'Name required', + newNetworkNoNameErrorMessage: 'A name is required to create a network', + newNetworkBondMode: 'Bond mode', + deleteNetwork: 'Delete network', + deleteNetworkConfirm: 'Are you sure you want to delete this network?', + networkInUse: 'This network is currently in use', + pillBonded: 'Bonded', + + // ----- Add host ----- + addHostSelectHost: 'Host', + addHostNoHost: 'No host', + addHostNoHostMessage: 'No host selected to be added', + + // ----- About View ----- + xenOrchestra: 'Xen Orchestra', + xenOrchestraServer: 'Xen Orchestra server', + xenOrchestraWeb: 'Xen Orchestra web client', + noProSupport: 'No pro support provided!', + noProductionUse: 'Use in production at your own risks', + downloadXoaFromWebsite: 'You can download our turnkey appliance at {website}', + bugTracker: 'Bug Tracker', + bugTrackerText: 'Issues? Report it!', + community: 'Community', + communityText: 'Join our community forum!', + freeTrial: 'Free Trial for Premium Edition!', + freeTrialNow: 'Request your trial now!', + issues: 'Any issue?', + issuesText: 'Problem? Contact us!', + documentation: 'Documentation', + documentationText: 'Read our official doc', + proSupportIncluded: 'Pro support included', + xoAccount: 'Access your XO Account', + openTicket: 'Report a problem', + openTicketText: 'Problem? Open a ticket!', + + // ----- Upgrade Panel ----- + upgradeNeeded: 'Upgrade needed', + upgradeNow: 'Upgrade now!', + or: 'Or', + tryIt: 'Try it for free!', + availableIn: 'This feature is available starting from {plan} Edition', + notAvailable: + 'This feature is not available in your version, contact your administrator to know more.', + + // ----- Updates View ----- + updateTitle: 'Updates', + registration: 'Registration', + trial: 'Trial', + settings: 'Settings', + proxySettings: 'Proxy settings', + proxySettingsHostPlaceHolder: 'Host (myproxy.example.org)', + proxySettingsPortPlaceHolder: 'Port (eg: 3128)', + proxySettingsUsernamePlaceHolder: 'Username', + proxySettingsPasswordPlaceHolder: 'Password', + updateRegistrationEmailPlaceHolder: 'Your email account', + updateRegistrationPasswordPlaceHolder: 'Your password', + updaterTroubleshootingLink: 'Troubleshooting documentation', + update: 'Update', + refresh: 'Refresh', + upgrade: 'Upgrade', + noUpdaterCommunity: 'No updater available for Community Edition', + considerSubscribe: + 'Please consider subscribing and trying it with all the features for free during 15 days on {link}.', + noUpdaterWarning: + 'Manual update could break your current installation due to dependencies issues, do it with caution', + currentVersion: 'Current version:', + register: 'Register', + editRegistration: 'Edit registration', + trialRegistration: + 'Please, take time to register in order to enjoy your trial.', + trialStartButton: 'Start trial', + trialAvailableUntil: + 'You can use a trial version until {date, date, medium}. Upgrade your appliance to get it.', + trialConsumed: + 'Your trial has been ended. Contact us or downgrade to Free version', + trialLocked: + 'Your xoa-updater service appears to be down. Your XOA cannot run fully without reaching this service.', + noUpdateInfo: 'No update information available', + waitingUpdateInfo: 'Update information may be available', + upToDate: 'Your XOA is up-to-date', + mustUpgrade: 'You need to update your XOA (new version is available)', + registerNeeded: 'Your XOA is not registered for updates', + updaterError: "Can't fetch update information", + promptUpgradeReloadTitle: 'Upgrade successful', + promptUpgradeReloadMessage: + 'Your XOA has successfully upgraded, and your browser must reload the application. Do you want to reload now ?', + + // ----- OS Disclaimer ----- + disclaimerTitle: 'Xen Orchestra from the sources', + disclaimerText1: + "You are using XO from the sources! That's great for a personal/non-profit usage.", + disclaimerText2: + "If you are a company, it's better to use it with our appliance + pro support included:", + disclaimerText3: + 'This version is not bundled with any support nor updates. Use it with caution for critical tasks.', + + // ----- PIF ----- + connectPif: 'Connect PIF', + connectPifConfirm: 'Are you sure you want to connect this PIF?', + disconnectPif: 'Disconnect PIF', + disconnectPifConfirm: 'Are you sure you want to disconnect this PIF?', + deletePif: 'Delete PIF', + deletePifConfirm: 'Are you sure you want to delete this PIF?', + deletePifs: 'Delete PIFs', + deletePifsConfirm: + 'Are you sure you want to delete {nPifs, number} PIF{nPifs, plural, one {} other {s}}?', + pifConnected: 'Connected', + pifDisconnected: 'Disconnected', + pifPhysicallyConnected: 'Physically connected', + pifPhysicallyDisconnected: 'Physically disconnected', + + // ----- User ----- + username: 'Username', + password: 'Password', + language: 'Language', + oldPasswordPlaceholder: 'Old password', + newPasswordPlaceholder: 'New password', + confirmPasswordPlaceholder: 'Confirm new password', + confirmationPasswordError: 'Confirmation password incorrect', + confirmationPasswordErrorBody: + 'Password does not match the confirm password.', + pwdChangeSuccess: 'Password changed', + pwdChangeSuccessBody: 'Your password has been successfully changed.', + pwdChangeError: 'Incorrect password', + pwdChangeErrorBody: + 'The old password provided is incorrect. Your password has not been changed.', + changePasswordOk: 'OK', + sshKeys: 'SSH keys', + newSshKey: 'New SSH key', + deleteSshKey: 'Delete', + deleteSshKeys: 'Delete selected SSH keys', + noSshKeys: 'No SSH keys', + newSshKeyModalTitle: 'New SSH key', + sshKeyErrorTitle: 'Invalid key', + sshKeyErrorMessage: 'An SSH key requires both a title and a key.', + title: 'Title', + key: 'Key', + deleteSshKeyConfirm: 'Delete SSH key', + deleteSshKeyConfirmMessage: + 'Are you sure you want to delete the SSH key {title}?', + deleteSshKeysConfirm: 'Delete SSH key{nKeys, plural, one {} other {s}}', + deleteSshKeysConfirmMessage: + 'Are you sure you want to delete {nKeys, number} SSH key{nKeys, plural, one {} other {s}}?', + + // ----- Usage ----- + others: 'Others', + + // ----- Logs ----- + loadingLogs: 'Loading logs…', + logUser: 'User', + logMethod: 'Method', + logParams: 'Params', + logMessage: 'Message', + logError: 'Error', + logDisplayDetails: 'Display details', + logTime: 'Date', + logNoStackTrace: 'No stack trace', + logNoParams: 'No params', + logDelete: 'Delete log', + logsDelete: 'Delete logs', + logDeleteMultiple: 'Delete log{nLogs, plural, one {} other {s}}', + logDeleteMultipleMessage: + 'Are you sure you want to delete {nLogs, number} log{nLogs, plural, one {} other {s}}?', + logDeleteAll: 'Delete all logs', + logDeleteAllTitle: 'Delete all logs', + logDeleteAllMessage: 'Are you sure you want to delete all the logs?', + logIndicationToEnable: 'Click to enable', + logIndicationToDisable: 'Click to disable', + reportBug: 'Report a bug', + unhealthyVdiChainError: 'Job canceled to protect the VDI chain', + clickForMoreInformation: 'Click for more information', + + // ----- IPs ------ + ipPoolName: 'Name', + ipPoolIps: 'IPs', + ipPoolIpsPlaceholder: 'IPs (e.g.: 1.0.0.12-1.0.0.17;1.0.0.23)', + ipPoolNetworks: 'Networks', + ipsNoIpPool: 'No IP pools', + ipsCreate: 'Create', + ipsDeleteAllTitle: 'Delete all IP pools', + ipsDeleteAllMessage: 'Are you sure you want to delete all the IP pools?', + ipsVifs: 'VIFs', + ipsNotUsed: 'Not used', + ipPoolUnknownVif: 'unknown VIF', + ipPoolNameAlreadyExists: 'Name already exists', + + // ----- Shortcuts ----- + shortcutModalTitle: 'Keyboard shortcuts', + shortcut_XoApp: 'Global', + shortcut_XoApp_GO_TO_HOSTS: 'Go to hosts list', + shortcut_XoApp_GO_TO_POOLS: 'Go to pools list', + shortcut_XoApp_GO_TO_VMS: 'Go to VMs list', + shortcut_XoApp_GO_TO_SRS: 'Go to SRs list', + shortcut_XoApp_CREATE_VM: 'Create a new VM', + shortcut_XoApp_UNFOCUS: 'Unfocus field', + shortcut_XoApp_HELP: 'Show shortcuts key bindings', + shortcut_Home: 'Home', + shortcut_Home_SEARCH: 'Focus search bar', + shortcut_Home_NAV_DOWN: 'Next item', + shortcut_Home_NAV_UP: 'Previous item', + shortcut_Home_SELECT: 'Select item', + shortcut_Home_JUMP_INTO: 'Open', + shortcut_SortedTable: 'Supported tables', + shortcut_SortedTable_SEARCH: 'Focus the table search bar', + shortcut_SortedTable_NAV_DOWN: 'Next item', + shortcut_SortedTable_NAV_UP: 'Previous item', + shortcut_SortedTable_SELECT: 'Select item', + shortcut_SortedTable_ROW_ACTION: 'Action', + + // ----- Settings/ACLs ----- + settingsAclsButtonTooltipVM: 'VM', + settingsAclsButtonTooltiphost: 'Hosts', + settingsAclsButtonTooltippool: 'Pool', + settingsAclsButtonTooltipSR: 'SR', + settingsAclsButtonTooltipnetwork: 'Network', + + // ----- Config ----- + noConfigFile: 'No config file selected', + importTip: + 'Try dropping a config file here, or click to select a config file to upload.', + config: 'Config', + importConfig: 'Import', + importConfigSuccess: 'Config file successfully imported', + importConfigError: 'Error while importing config file', + exportConfig: 'Export', + downloadConfig: 'Download current config', + noConfigImportCommunity: 'No config import available for Community Edition', + + // ----- SR ----- + srReconnectAllModalTitle: 'Reconnect all hosts', + srReconnectAllModalMessage: 'This will reconnect this SR to all its hosts.', + srsReconnectAllModalMessage: + 'This will reconnect each selected SR to its host (local SR) or to every hosts of its pool (shared SR).', + srDisconnectAllModalTitle: 'Disconnect all hosts', + srDisconnectAllModalMessage: + 'This will disconnect this SR from all its hosts.', + srsDisconnectAllModalMessage: + 'This will disconnect each selected SR from its host (local SR) or from every hosts of its pool (shared SR).', + srForgetModalTitle: 'Forget SR', + srsForgetModalTitle: 'Forget selected SRs', + srForgetModalMessage: + "Are you sure you want to forget this SR? VDIs on this storage won't be removed.", + srsForgetModalMessage: + "Are you sure you want to forget all the selected SRs? VDIs on these storages won't be removed.", + srAllDisconnected: 'Disconnected', + srSomeConnected: 'Partially connected', + srAllConnected: 'Connected', + + // ----- XOSAN ----- + xosanTitle: 'XOSAN', + xosanSrTitle: 'Xen Orchestra SAN SR', + xosanAvailableSrsTitle: 'Select local SRs (lvm)', + xosanSuggestions: 'Suggestions', + xosanDisperseWarning: + 'Warning: using disperse layout is not recommended right now. Please read {link}.', + xosanName: 'Name', + xosanHost: 'Host', + xosanHosts: 'Connected Hosts', + xosanPool: 'Pool', + xosanVolumeId: 'Volume ID', + xosanSize: 'Size', + xosanUsedSpace: 'Used space', + xosanLicense: 'License', + xosanMultipleLicenses: 'This XOSAN has more than 1 license!', + xosanNeedPack: 'XOSAN pack needs to be installed on each host of the pool.', + xosanInstallIt: 'Install it now!', + xosanNeedRestart: + 'Some hosts need their toolstack to be restarted before you can create an XOSAN', + xosanRestartAgents: 'Restart toolstacks', + xosanMasterOffline: 'Pool master is not running', + xosanInstallPackTitle: 'Install XOSAN pack on {pool}', + xosanSelect2Srs: 'Select at least 2 SRs', + xosanLayout: 'Layout', + xosanRedundancy: 'Redundancy', + xosanCapacity: 'Capacity', + xosanAvailableSpace: 'Available space', + xosanDiskLossLegend: '* Can fail without data loss', + xosanCreate: 'Create', + xosanAdd: 'Add', + xosanInstalling: 'Installing XOSAN. Please wait…', + xosanCommunity: 'No XOSAN available for Community Edition', + xosanNew: 'New', + xosanAdvanced: 'Advanced', + xosanRemoveSubvolumes: 'Remove subvolumes', + xosanAddSubvolume: 'Add subvolume…', + xosanWarning: + "This version of XOSAN SR is from the first beta phase. You can keep using it, but to modify it you'll have to save your disks and re-create it.", + xosanVlan: 'VLAN', + xosanNoSrs: 'No XOSAN found', + xosanPbdsDetached: 'Some SRs are detached from the XOSAN', + xosanBadStatus: 'Something is wrong with: {badStatuses}', + xosanRunning: 'Running', + xosanDelete: 'Delete XOSAN', + xosanFixIssue: 'Fix', + xosanCreatingOn: 'Creating XOSAN on {pool}', + xosanState_configuringNetwork: 'Configuring network…', + xosanState_importingVm: 'Importing VM…', + xosanState_copyingVms: 'Copying VMs…', + xosanState_configuringVms: 'Configuring VMs…', + xosanState_configuringGluster: 'Configuring gluster…', + xosanState_creatingSr: 'Creating SR…', + xosanState_scanningSr: 'Scanning SR…', + // Pack download modal + xosanInstallCloudPlugin: 'Install cloud plugin first', + xosanLoadCloudPlugin: 'Load cloud plugin first', + xosanRegister: 'Register your appliance first', + xosanLoading: 'Loading…', + xosanNotAvailable: 'XOSAN is not available at the moment', + xosanInstallPackOnHosts: 'Install XOSAN pack on these hosts:', + xosanInstallPack: 'Install {pack} v{version}?', + xosanNoPackFound: + 'No compatible XOSAN pack found for your XenServer versions.', + xosanPackRequirements: + 'At least one of these version requirements must be satisfied by all the hosts in this pool:', + // SR tab XOSAN + xosanVmsNotRunning: 'Some XOSAN Virtual Machines are not running', + xosanVmsNotFound: 'Some XOSAN Virtual Machines could not be found', + xosanFilesNeedingHealing: 'Files needing healing', + xosanFilesNeedHealing: + 'Some XOSAN Virtual Machines have files needing healing', + xosanHostNotInNetwork: 'Host {hostName} is not in XOSAN network', + xosanVm: 'VM controller', + xosanUnderlyingStorage: 'SR', + xosanReplace: 'Replace…', + xosanOnSameVm: 'On same VM', + xosanBrickName: 'Brick name', + xosanBrickUuid: 'Brick UUID', + xosanBrickSize: 'Brick size', + xosanMemorySize: 'Memory size', + xosanStatus: 'Status', + xosanArbiter: 'Arbiter', + xosanUsedInodes: 'Used Inodes', + xosanBlockSize: 'Block size', + xosanDevice: 'Device', + xosanFsName: 'FS name', + xosanMountOptions: 'Mount options', + xosanPath: 'Path', + xosanJob: 'Job', + xosanPid: 'PID', + xosanPort: 'Port', + xosanReplaceBrickErrorTitle: 'Missing values', + xosanReplaceBrickErrorMessage: 'You need to select a SR and a size', + xosanAddSubvolumeErrorTitle: 'Bad values', + xosanAddSubvolumeErrorMessage: 'You need to select {nSrs, number} and a size', + xosanSelectNSrs: 'Select {nSrs, number} SRs', + xosanRun: 'Run', + xosanRemove: 'Remove', + xosanVolume: 'Volume', + xosanVolumeOptions: 'Volume options', + xosanCouldNotFindVm: 'Could not find VM', + xosanUnderlyingStorageUsage: 'Using {usage}', + xosanCustomIpNetwork: 'Custom IP network (/24)', + xosanIssueHostNotInNetwork: + 'Will configure the host xosan network device with a static IP address and plug it in.', + + // Licenses + licensesTitle: 'Licenses', + xosanUnregisteredDisclaimer: + 'You are not registered and therefore will not be able to create or manage your XOSAN SRs. {link}', + xosanSourcesDisclaimer: + 'In order to create a XOSAN SR, you need to use the Xen Orchestra Appliance and buy a XOSAN license on {link}.', + registerNow: 'Register now!', + licensesUnregisteredDisclaimer: + 'You need to register your appliance to manage your licenses.', + licenseProduct: 'Product', + licenseBoundObject: 'Attached to', + licensePurchaser: 'Purchaser', + licenseExpires: 'Expires', + licensePurchaserYou: 'You', + productSupport: 'Support', + licenseNotBoundXosan: 'No XOSAN attached', + licenseBoundUnknownXosan: 'License attached to an unknown XOSAN', + licensesManage: 'Manage the licenses', + newLicense: 'New license', + refreshLicenses: 'Refresh', + xosanLicenseRestricted: 'Limited size because XOSAN is in trial', + xosanAdminNoLicenseDisclaimer: + 'You need a license on this SR to manage the XOSAN.', + xosanAdminExpiredLicenseDisclaimer: + 'Your XOSAN license has expired. You can still use the SR but cannot administrate it anymore.', + xosanCheckLicenseError: 'Could not check the license on this XOSAN SR', + xosanGetLicensesError: 'Could not fetch licenses', + xosanLicenseHasExpired: 'License has expired.', + xosanLicenseExpiresDate: 'License expires on {date}.', + xosanUpdateLicenseMessage: 'Update the license now!', + xosanUnknownSr: 'Unknown XOSAN SR.', + contactUs: 'Contact us!', + xosanNoLicense: 'No license.', + xosanUnlockNow: 'Unlock now!', + xosanBetaOverMessage: + 'XOSAN Beta is over. You may now delete and recreate previous existing XOSAN SRs.', + selectLicense: 'Select a license', + bindLicense: 'Bind license', + expiresOn: 'expires on {date}', + xosanInstallXoaPlugin: 'Install XOA plugin first', + xosanLoadXoaPlugin: 'Load XOA plugin first', + + // ----- Utils ----- + durationFormat: + '{days, plural, =0 {} one {# day } other {# days }}{hours, plural, =0 {} one {# hour } other {# hours }}{minutes, plural, =0 {} one {# minute } other {# minutes }}{seconds, plural, =0 {} one {# second} other {# seconds}}', +} +forEach(messages, function (message, id) { + if (isString(message)) { + messages[id] = { + id, + defaultMessage: message, + } + } else if (!message.id) { + message.id = id + } +}) + +module.exports = messages diff --git a/packages/xo-web/src/common/invoke.js b/packages/xo-web/src/common/invoke.js new file mode 100644 index 000000000..af9e6f35e --- /dev/null +++ b/packages/xo-web/src/common/invoke.js @@ -0,0 +1,33 @@ +// Invoke a function and returns it result. +// All parameters are forwarded. +// +// Why using `invoke()`? +// - avoid tedious IIFE syntax +// - avoid declaring variables in the common scope +// - monkey-patching +// +// ```js +// const sum = invoke(1, 2, (a, b) => a + b) +// +// eventEmitter.emit = invoke(eventEmitter.emit, emit => function (event) { +// if (event === 'foo') { +// throw new Error('event foo is disabled') +// } +// +// return emit.apply(this, arguments) +// }) +// ``` +export default function invoke (fn) { + const n = arguments.length - 1 + if (!n) { + return fn() + } + + fn = arguments[n] + const args = new Array(n) + for (let i = 0; i < n; ++i) { + args[i] = arguments[i] + } + + return fn.apply(undefined, args) +} diff --git a/packages/xo-web/src/common/ip.js b/packages/xo-web/src/common/ip.js new file mode 100644 index 000000000..6278287d9 --- /dev/null +++ b/packages/xo-web/src/common/ip.js @@ -0,0 +1,136 @@ +import forEachRight from 'lodash/forEachRight' +import forEach from 'lodash/forEach' +import isArray from 'lodash/isArray' +import isIp from 'is-ip' +import some from 'lodash/some' + +export { isIp } +export const isIpV4 = isIp.v4 +export const isIpV6 = isIp.v6 + +// Source: https://github.com/ezpaarse-project/ip-range-generator/blob/master/index.js + +const ipv4 = /^(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(?:\.(?!$)|$)){4}$/ + +function ip2hex (ip) { + const parts = ip.split('.').map(str => parseInt(str, 10)) + let n = 0 + + n += parts[3] + n += parts[2] * 256 // 2^8 + n += parts[1] * 65536 // 2^16 + n += parts[0] * 16777216 // 2^24 + + return n +} + +function assertIpv4 (str, msg) { + if (!ipv4.test(str)) { + throw new Error(msg) + } +} + +function * range (ip1, ip2) { + assertIpv4(ip1, 'argument "ip1" must be a valid IPv4 address') + assertIpv4(ip2, 'argument "ip2" must be a valid IPv4 address') + + let hex = ip2hex(ip1) + let hex2 = ip2hex(ip2) + + if (hex > hex2) { + const tmp = hex + hex = hex2 + hex2 = tmp + } + + for (let i = hex; i <= hex2; i++) { + yield `${(i >> 24) & 0xff}.${(i >> 16) & 0xff}.${(i >> 8) & 0xff}.${i & + 0xff}` + } +} + +// ----------------------------------------------------------------------------- + +export const getNextIpV4 = ip => { + const splitIp = ip.split('.') + if ( + splitIp.length !== 4 || + some(splitIp, value => value < 0 || value > 255) + ) { + return + } + let index + forEachRight(splitIp, (value, i) => { + if (value < 255) { + index = i + return false + } + splitIp[i] = 1 + }) + if (index === 0 && +splitIp[0] === 255) { + return 0 + } + splitIp[index]++ + + return splitIp.join('.') +} + +export const formatIps = ips => { + if (!isArray(ips)) { + throw new Error('ips must be an array') + } + if (ips.length === 0) { + return [] + } + const sortedIps = ips.sort((ip1, ip2) => { + const splitIp1 = ip1.split('.') + const splitIp2 = ip2.split('.') + if (splitIp1.length !== 4) { + return 1 + } + if (splitIp2.length !== 4) { + return -1 + } + return ( + splitIp1[3] - + splitIp2[3] + + (splitIp1[2] - splitIp2[2]) * 256 + + (splitIp1[1] - splitIp2[1]) * 256 * 256 + + (splitIp1[0] - splitIp2[0]) * 256 * 256 * 256 + ) + }) + const range = { first: '', last: '' } + const formattedIps = [] + let index = 0 + forEach(sortedIps, ip => { + if (ip !== getNextIpV4(range.last)) { + if (range.first) { + formattedIps[index] = + range.first === range.last ? range.first : { ...range } + index++ + } + range.first = range.last = ip + } else { + range.last = ip + } + }) + formattedIps[index] = range.first === range.last ? range.first : range + + return formattedIps +} + +export const parseIpPattern = pattern => { + const ips = [] + forEach(pattern.split(';'), rawIpRange => { + const ipRange = rawIpRange.split('-') + if (ipRange.length < 2) { + ips.push(ipRange[0]) + } else if (!isIpV4(ipRange[0]) || !isIpV4(ipRange[1])) { + ips.push(rawIpRange) + } else { + ips.push(...range(ipRange[0], ipRange[1])) + } + }) + + return ips +} diff --git a/packages/xo-web/src/common/iso-device.js b/packages/xo-web/src/common/iso-device.js new file mode 100644 index 000000000..0966434dc --- /dev/null +++ b/packages/xo-web/src/common/iso-device.js @@ -0,0 +1,103 @@ +import React from 'react' + +import _ from 'intl' +import ActionButton from './action-button' +import Component from './base-component' +import Icon from 'icon' +import propTypes from './prop-types-decorator' +import Tooltip from 'tooltip' +import { alert } from 'modal' +import { connectStore } from './utils' +import { SelectVdi } from './select-objects' +import { + createGetObjectsOfType, + createFinder, + createGetObject, + createSelector, +} from './selectors' +import { ejectCd, insertCd } from './xo' + +@propTypes({ + vm: propTypes.object.isRequired, +}) +@connectStore(() => { + const getCdDrive = createFinder( + createGetObjectsOfType('VBD').pick((_, { vm }) => vm.$VBDs), + [vbd => vbd.is_cd_drive] + ) + + const getMountedIso = createGetObject((state, props) => { + const cdDrive = getCdDrive(state, props) + if (cdDrive) { + return cdDrive.VDI + } + }) + + return { + cdDrive: getCdDrive, + mountedIso: getMountedIso, + } +}) +export default class IsoDevice extends Component { + _getPredicate = createSelector( + () => this.props.vm.$pool, + () => this.props.vm.$container, + (vmPool, vmContainer) => sr => { + const vmRunning = vmContainer !== vmPool + const sameHost = vmContainer === sr.$container + const samePool = vmPool === sr.$pool + + return ( + samePool && + (vmRunning ? sr.shared || sameHost : true) && + (sr.SR_type === 'iso' || (sr.SR_type === 'udev' && sr.size)) + ) + } + ) + + _handleInsert = iso => { + const { vm } = this.props + + if (iso) { + insertCd(vm, iso.id, true) + } else { + ejectCd(vm) + } + } + + _handleEject = () => ejectCd(this.props.vm) + + _showWarning = () => alert(_('cdDriveNotInstalled'), _('cdDriveInstallation')) + + render () { + const { cdDrive, mountedIso } = this.props + + return ( +
+ + + + + {mountedIso && + !cdDrive.device && ( + + + + + + )} +
+ ) + } +} diff --git a/packages/xo-web/src/common/json-schema-input/array-input.js b/packages/xo-web/src/common/json-schema-input/array-input.js new file mode 100644 index 000000000..30f5c9577 --- /dev/null +++ b/packages/xo-web/src/common/json-schema-input/array-input.js @@ -0,0 +1,126 @@ +import React from 'react' +import uncontrollableInput from 'uncontrollable-input' +import { filter, map } from 'lodash' + +import _ from '../intl' +import Button from '../button' +import Component from '../base-component' +import propTypes from '../prop-types-decorator' +import { EMPTY_ARRAY } from '../utils' + +import GenericInput from './generic-input' +import { descriptionRender, forceDisplayOptionalAttr } from './helpers' + +@propTypes({ + depth: propTypes.number, + disabled: propTypes.bool, + label: propTypes.any.isRequired, + required: propTypes.bool, + schema: propTypes.object.isRequired, + uiSchema: propTypes.object, +}) +@uncontrollableInput() +export default class ObjectInput extends Component { + state = { + use: this.props.required || forceDisplayOptionalAttr(this.props), + } + + _onAddItem = () => { + const { props } = this + props.onChange((props.value || EMPTY_ARRAY).concat(undefined)) + } + + _onChangeItem = (value, name) => { + const key = Number(name) + + const { props } = this + const newValue = (props.value || EMPTY_ARRAY).slice() + newValue[key] = value + props.onChange(newValue) + } + + _onRemoveItem = key => { + const { props } = this + props.onChange(filter(props.value, (_, i) => i !== key)) + } + + render () { + const { + props: { + depth = 0, + disabled, + label, + required, + schema, + uiSchema, + value = EMPTY_ARRAY, + }, + state: { use }, + } = this + + const childDepth = depth + 2 + const itemSchema = schema.items + const itemUiSchema = uiSchema && uiSchema.items + + const itemLabel = itemSchema.title || _('item') + + return ( +
+ {label} + {descriptionRender(schema.description)} +
+ {!required && ( +
+ +
+ )} + {use && ( +
+
    + {map(value, (value, key) => ( +
  • + + +
  • + ))} +
+ +
+ )} +
+ ) + } +} diff --git a/packages/xo-web/src/common/json-schema-input/boolean-input.js b/packages/xo-web/src/common/json-schema-input/boolean-input.js new file mode 100644 index 000000000..701d0d67b --- /dev/null +++ b/packages/xo-web/src/common/json-schema-input/boolean-input.js @@ -0,0 +1,24 @@ +import React from 'react' + +import uncontrollableInput from 'uncontrollable-input' +import Component from '../base-component' +import { Toggle } from '../form' + +import { PrimitiveInputWrapper } from './helpers' + +// =================================================================== + +@uncontrollableInput() +export default class BooleanInput extends Component { + render () { + const { disabled, onChange, value, ...props } = this.props + + return ( + +
+ +
+
+ ) + } +} diff --git a/packages/xo-web/src/common/json-schema-input/enum-input.js b/packages/xo-web/src/common/json-schema-input/enum-input.js new file mode 100644 index 000000000..db900b185 --- /dev/null +++ b/packages/xo-web/src/common/json-schema-input/enum-input.js @@ -0,0 +1,56 @@ +import _ from 'intl' +import uncontrollableInput from 'uncontrollable-input' +import Component from 'base-component' +import React from 'react' +import { createSelector } from 'reselect' +import { findIndex, map } from 'lodash' + +import { PrimitiveInputWrapper } from './helpers' + +// =================================================================== + +@uncontrollableInput() +export default class EnumInput extends Component { + _getSelectedIndex = createSelector( + () => this.props.schema.enum, + () => { + const { schema, value = schema.default } = this.props + return value + }, + (enumValues, value) => { + const index = findIndex(enumValues, current => current === value) + return index === -1 ? '' : index + } + ) + + _onChange = event => { + this.props.onChange(this.props.schema.enum[event.target.value]) + } + + render () { + const { + disabled, + schema: { enum: enumValues, enumNames = enumValues }, + required, + } = this.props + + return ( + + + + ) + } +} diff --git a/packages/xo-web/src/common/json-schema-input/generic-input.js b/packages/xo-web/src/common/json-schema-input/generic-input.js new file mode 100644 index 000000000..b3c3ccb11 --- /dev/null +++ b/packages/xo-web/src/common/json-schema-input/generic-input.js @@ -0,0 +1,76 @@ +import React, { Component } from 'react' + +import getEventValue from '../get-event-value' +import propTypes from '../prop-types-decorator' +import uncontrollableInput from 'uncontrollable-input' +import { EMPTY_OBJECT } from '../utils' + +import ArrayInput from './array-input' +import BooleanInput from './boolean-input' +import EnumInput from './enum-input' +import IntegerInput from './integer-input' +import NumberInput from './number-input' +import ObjectInput from './object-input' +import StringInput from './string-input' + +import { getType } from './helpers' + +// =================================================================== + +const InputByType = { + array: ArrayInput, + boolean: BooleanInput, + integer: IntegerInput, + number: NumberInput, + object: ObjectInput, + string: StringInput, +} + +// =================================================================== + +@propTypes({ + depth: propTypes.number, + disabled: propTypes.bool, + label: propTypes.any.isRequired, + required: propTypes.bool, + schema: propTypes.object.isRequired, + uiSchema: propTypes.object, +}) +@uncontrollableInput() +export default class GenericInput extends Component { + _onChange = event => { + const { name, onChange } = this.props + onChange && onChange(getEventValue(event), name) + } + + render () { + const { + schema, + value = schema.default, + uiSchema = EMPTY_OBJECT, + ...opts + } = this.props + + const props = { + ...opts, + onChange: this._onChange, + schema, + uiSchema, + value, + } + + // Enum, special case. + if (schema.enum) { + return + } + + const type = getType(schema) + const Input = uiSchema.widget || InputByType[type.toLowerCase()] + + if (!Input) { + throw new Error(`Unsupported type: ${type}.`) + } + + return + } +} diff --git a/packages/xo-web/src/common/json-schema-input/helpers.js b/packages/xo-web/src/common/json-schema-input/helpers.js new file mode 100644 index 000000000..ca9bb91a8 --- /dev/null +++ b/packages/xo-web/src/common/json-schema-input/helpers.js @@ -0,0 +1,90 @@ +import React from 'react' +import includes from 'lodash/includes' +import isArray from 'lodash/isArray' +import marked from 'marked' + +import { Col, Row } from 'grid' + +// =================================================================== + +export const getType = schema => { + if (!schema) { + return + } + + const type = schema.type + + if (isArray(type)) { + if (includes(type, 'integer')) { + return 'integer' + } + if (includes(type, 'number')) { + return 'number' + } + + return 'string' + } + + return type +} + +export const getXoType = schema => { + const type = schema && (schema['xo:type'] || schema.$type) + + if (type) { + return type.toLowerCase() + } +} + +// =================================================================== + +export const descriptionRender = description => ( + +) + +// =================================================================== + +export const PrimitiveInputWrapper = ({ + label, + required = false, + schema, + children, +}) => ( + + +
+ + {label} + {required && *} + + {children} +
+ + {descriptionRender(schema.description)} +
+) + +// =================================================================== + +export const forceDisplayOptionalAttr = ({ schema, value }) => { + if (!schema || !value) { + return false + } + + // Array + if (schema.items && Array.isArray(value)) { + return true + } + + // Object + for (const key in schema.properties) { + if (value[key]) { + return true + } + } + + return false +} diff --git a/packages/xo-web/src/common/json-schema-input/index.js b/packages/xo-web/src/common/json-schema-input/index.js new file mode 100644 index 000000000..9007a6379 --- /dev/null +++ b/packages/xo-web/src/common/json-schema-input/index.js @@ -0,0 +1 @@ +export default from './generic-input' diff --git a/packages/xo-web/src/common/json-schema-input/integer-input.js b/packages/xo-web/src/common/json-schema-input/integer-input.js new file mode 100644 index 000000000..dbe566ba7 --- /dev/null +++ b/packages/xo-web/src/common/json-schema-input/integer-input.js @@ -0,0 +1,46 @@ +import React from 'react' + +import uncontrollableInput from 'uncontrollable-input' +import Combobox from '../combobox' +import Component from '../base-component' +import getEventValue from '../get-event-value' + +import { PrimitiveInputWrapper } from './helpers' + +// =================================================================== + +@uncontrollableInput() +export default class IntegerInput extends Component { + _onChange = event => { + const value = getEventValue(event) + this.props.onChange(value ? +value : undefined) + } + + render () { + const { required, schema } = this.props + const { + disabled, + onChange, // eslint-disable-line no-unused-vars + placeholder = schema.default, + value, + ...props + } = this.props + + return ( + + + + ) + } +} diff --git a/packages/xo-web/src/common/json-schema-input/number-input.js b/packages/xo-web/src/common/json-schema-input/number-input.js new file mode 100644 index 000000000..7b2d52ff2 --- /dev/null +++ b/packages/xo-web/src/common/json-schema-input/number-input.js @@ -0,0 +1,46 @@ +import React from 'react' + +import uncontrollableInput from 'uncontrollable-input' +import Combobox from '../combobox' +import Component from '../base-component' +import getEventValue from '../get-event-value' + +import { PrimitiveInputWrapper } from './helpers' + +// =================================================================== + +@uncontrollableInput() +export default class NumberInput extends Component { + _onChange = event => { + const value = getEventValue(event) + this.props.onChange(value ? +value : undefined) + } + + render () { + const { required, schema } = this.props + const { + disabled, + onChange, // eslint-disable-line no-unused-vars + placeholder = schema.default, + value, + ...props + } = this.props + + return ( + + + + ) + } +} diff --git a/packages/xo-web/src/common/json-schema-input/object-input.js b/packages/xo-web/src/common/json-schema-input/object-input.js new file mode 100644 index 000000000..4aafa9a5f --- /dev/null +++ b/packages/xo-web/src/common/json-schema-input/object-input.js @@ -0,0 +1,98 @@ +import React from 'react' +import uncontrollableInput from 'uncontrollable-input' +import { createSelector } from 'reselect' +import { keyBy, map } from 'lodash' + +import _ from '../intl' +import Component from '../base-component' +import propTypes from '../prop-types-decorator' +import { EMPTY_OBJECT } from '../utils' + +import GenericInput from './generic-input' +import { descriptionRender, forceDisplayOptionalAttr } from './helpers' + +@propTypes({ + depth: propTypes.number, + disabled: propTypes.bool, + label: propTypes.any.isRequired, + required: propTypes.bool, + schema: propTypes.object.isRequired, + uiSchema: propTypes.object, +}) +@uncontrollableInput() +export default class ObjectInput extends Component { + state = { + use: this.props.required || forceDisplayOptionalAttr(this.props), + } + + _onChildChange = (value, key) => { + this.props.onChange({ + ...this.props.value, + [key]: value, + }) + } + + _getRequiredProps = createSelector( + () => this.props.schema.required, + required => (required ? keyBy(required) : EMPTY_OBJECT) + ) + + render () { + const { + props: { + depth = 0, + disabled, + label, + required, + schema, + uiSchema, + value = EMPTY_OBJECT, + }, + state: { use }, + } = this + + const childDepth = depth + 2 + const properties = (uiSchema != null && uiSchema.properties) || EMPTY_OBJECT + const requiredProps = this._getRequiredProps() + + return ( +
+ {label} + {descriptionRender(schema.description)} +
+ {!required && ( +
+ +
+ )} + {use && ( +
+ {map(schema.properties, (childSchema, key) => ( +
+ +
+ ))} +
+ )} +
+ ) + } +} diff --git a/packages/xo-web/src/common/json-schema-input/string-input.js b/packages/xo-web/src/common/json-schema-input/string-input.js new file mode 100644 index 000000000..5aa441093 --- /dev/null +++ b/packages/xo-web/src/common/json-schema-input/string-input.js @@ -0,0 +1,50 @@ +import React from 'react' +import uncontrollableInput from 'uncontrollable-input' + +import Combobox from '../combobox' +import Component from '../base-component' +import getEventValue from '../get-event-value' +import propTypes from '../prop-types-decorator' + +import { PrimitiveInputWrapper } from './helpers' + +// =================================================================== + +@propTypes({ + password: propTypes.bool, +}) +@uncontrollableInput() +export default class StringInput extends Component { + // the value of this input is undefined not '' when empty to make + // it homogenous with when the user has never touched this input + _onChange = event => { + const value = getEventValue(event) + this.props.onChange(value !== '' ? value : undefined) + } + + render () { + const { required, schema } = this.props + const { + disabled, + password, + placeholder = schema.default, + value, + ...props + } = this.props + delete props.onChange + + return ( + + + + ) + } +} diff --git a/packages/xo-web/src/common/link.js b/packages/xo-web/src/common/link.js new file mode 100644 index 000000000..1df3612d9 --- /dev/null +++ b/packages/xo-web/src/common/link.js @@ -0,0 +1,71 @@ +import Link from 'react-router/lib/Link' +import React from 'react' +import { routerShape } from 'react-router/lib/PropTypes' + +import Component from './base-component' +import propTypes from './prop-types-decorator' + +// =================================================================== + +export { Link as default } + +// ------------------------------------------------------------------- + +const _IGNORED_TAGNAMES = { + A: true, + BUTTON: true, + INPUT: true, + SELECT: true, +} + +@propTypes({ + className: propTypes.string, + tagName: propTypes.string, +}) +export class BlockLink extends Component { + static contextTypes = { + router: routerShape, + } + + _style = { cursor: 'pointer' } + _onClickCapture = event => { + const { currentTarget } = event + let element = event.target + while (element !== currentTarget) { + if (_IGNORED_TAGNAMES[element.tagName]) { + return + } + element = element.parentNode + } + event.stopPropagation() + if (event.ctrlKey || event.button === 1) { + window.open(this.context.router.createHref(this.props.to)) + } else { + this.context.router.push(this.props.to) + } + } + + _addAuxClickListener = ref => { + // FIXME: when https://github.com/facebook/react/issues/8529 is fixed, + // remove and use onAuxClickCapture. + // In Chrome ^55, middle-clicking triggers auxclick event instead of click + if (ref !== null) { + ref.addEventListener('auxclick', this._onClickCapture) + } + } + + render () { + const { children, tagName = 'div', className } = this.props + const Component = tagName + return ( + + {children} + + ) + } +} diff --git a/packages/xo-web/src/common/log-error.js b/packages/xo-web/src/common/log-error.js new file mode 100644 index 000000000..11121c0e7 --- /dev/null +++ b/packages/xo-web/src/common/log-error.js @@ -0,0 +1,13 @@ +// Logs an error properly, correctly use the source map for the stack. +// +// This is achieved by throwing the error asynchronously. +const logError = (error, ...args) => { + setTimeout(() => { + if (args.length) { + console.error(...args) + } + + throw error + }, 0) +} +export { logError as default } diff --git a/packages/xo-web/src/common/modal.js b/packages/xo-web/src/common/modal.js new file mode 100644 index 000000000..6b9574811 --- /dev/null +++ b/packages/xo-web/src/common/modal.js @@ -0,0 +1,286 @@ +import isArray from 'lodash/isArray' +import isString from 'lodash/isString' +import map from 'lodash/map' +import React, { Component, cloneElement } from 'react' +import { createSelector } from 'selectors' +import { injectIntl } from 'react-intl' +import { Modal as ReactModal } from 'react-bootstrap-4/lib' + +import _, { messages } from './intl' +import Button from './button' +import Icon from './icon' +import propTypes from './prop-types-decorator' +import Tooltip from './tooltip' +import { + disable as disableShortcuts, + enable as enableShortcuts, +} from './shortcuts' + +// ----------------------------------------------------------------------------- + +let instance +const modal = (content, onClose) => { + if (!instance) { + throw new Error('No modal instance.') + } else if (instance.state.showModal) { + throw new Error('Other modal still open.') + } + instance.setState({ content, onClose, showModal: true }, disableShortcuts) +} + +const _addRef = (component, ref) => { + if (isString(component) || isArray(component)) { + return component + } + + try { + return cloneElement(component, { ref }) + } catch (_) {} // Stateless component. + return component +} + +// ----------------------------------------------------------------------------- + +@propTypes({ + buttons: propTypes.arrayOf( + propTypes.shape({ + btnStyle: propTypes.string, + icon: propTypes.string, + label: propTypes.node.isRequired, + tooltip: propTypes.node, + value: propTypes.any, + }) + ).isRequired, + children: propTypes.node.isRequired, + icon: propTypes.string, + title: propTypes.node.isRequired, +}) +class GenericModal extends Component { + _getBodyValue = () => { + const { body } = this.refs + if (body !== undefined) { + return body.getWrappedInstance === undefined + ? body.value + : body.getWrappedInstance().value + } + } + + _resolve = (value = this._getBodyValue()) => { + this.props.resolve(value) + instance.close() + } + + _reject = () => { + this.props.reject() + instance.close() + } + + render () { + const { buttons, icon, title } = this.props + + const body = _addRef(this.props.children, 'body') + + return ( +
+ + + {icon ? ( + + {title} + + ) : ( + title + )} + + + {body} + + {map(buttons, ({ label, tooltip, value, icon, ...props }, key) => { + const button = ( + + ) + return ( + + {tooltip !== undefined ? ( + {button} + ) : ( + button + )}{' '} + + ) + })} + {this.props.reject !== undefined && ( + + )} + +
+ ) + } +} + +export const chooseAction = ({ body, buttons, icon, title }) => { + return new Promise((resolve, reject) => { + modal( + + {body} + , + reject + ) + }) +} + +@propTypes({ + body: propTypes.node, + strongConfirm: propTypes.object.isRequired, + icon: propTypes.string, + reject: propTypes.func, + resolve: propTypes.func, + title: propTypes.node.isRequired, +}) +@injectIntl +class StrongConfirm extends Component { + state = { + buttons: [{ btnStyle: 'danger', label: _('confirmOk'), disabled: true }], + } + + _getStrongConfirmString = createSelector( + () => this.props.intl.formatMessage, + () => this.props.strongConfirm, + (format, { messageId, values }) => format(messages[messageId], values) + ) + + _onInputChange = event => { + const userInput = event.target.value + const strongConfirmString = this._getStrongConfirmString() + const confirmButton = this.state.buttons[0] + + let disabled + if ( + (userInput.toLowerCase() === strongConfirmString.toLowerCase()) ^ + (disabled = !confirmButton.disabled) + ) { + this.setState({ + buttons: [{ ...confirmButton, disabled }], + }) + } + } + + render () { + const { + body, + strongConfirm: { messageId, values }, + icon, + reject, + resolve, + title, + } = this.props + + return ( + + {body} +
+
+ {_('enterConfirmText')}{' '} + {_(messageId, values)} +
+
+ +
+
+ ) + } +} + +// ----------------------------------------------------------------------------- + +const ALERT_BUTTONS = [{ label: _('alertOk'), value: 'ok' }] + +export const alert = (title, body) => + new Promise(resolve => { + modal( + + {body} + , + resolve + ) + }) + +// ----------------------------------------------------------------------------- + +const CONFIRM_BUTTONS = [{ btnStyle: 'primary', label: _('confirmOk') }] + +export const confirm = ({ body, icon = 'alarm', title, strongConfirm }) => + strongConfirm + ? new Promise((resolve, reject) => { + modal( + + ) + }) + : chooseAction({ + body, + buttons: CONFIRM_BUTTONS, + icon, + title, + }) + +// ----------------------------------------------------------------------------- + +export default class Modal extends Component { + constructor () { + super() + + this.state = { showModal: false } + } + + componentDidMount () { + if (instance) { + throw new Error('Modal is a singleton!') + } + instance = this + } + + componentWillUnmount () { + instance = undefined + } + + close () { + this.setState({ showModal: false }, enableShortcuts) + } + + _onHide = () => { + this.close() + + const { onClose } = this.state + onClose && onClose() + } + + render () { + return ( + + {this.state.content} + + ) + } +} diff --git a/packages/xo-web/src/common/nav.js b/packages/xo-web/src/common/nav.js new file mode 100644 index 000000000..66b2f3eba --- /dev/null +++ b/packages/xo-web/src/common/nav.js @@ -0,0 +1,18 @@ +import classNames from 'classnames' +import React from 'react' + +import Link from './link' + +export const NavLink = ({ children, to }) => ( +
  • + + {children} + +
  • +) + +export const NavTabs = ({ children, className }) => ( +
      + {children} +
    +) diff --git a/packages/xo-web/src/common/no-objects.js b/packages/xo-web/src/common/no-objects.js new file mode 100644 index 000000000..b3560c3ea --- /dev/null +++ b/packages/xo-web/src/common/no-objects.js @@ -0,0 +1,41 @@ +import React from 'react' +import { isEmpty } from 'lodash' + +import propTypes from './prop-types-decorator' + +// This component returns : +// - A loading icon when the objects are not fetched +// - A default message if the objects are fetched and the collection is empty +// - The children if the objects are fetched and the collection is not empty +// +// ```js +// +// {children} +// +// ```` +const NoObjects = props => { + const { collection } = props + + if (collection == null) { + return loading + } + + if (isEmpty(collection)) { + return

    {props.emptyMessage}

    + } + + const { children, component: Component, ...otherProps } = props + return children !== undefined ? ( + children(otherProps) + ) : ( + + ) +} + +propTypes(NoObjects)({ + children: propTypes.func, + collection: propTypes.oneOfType([propTypes.array, propTypes.object]), + component: propTypes.func, + emptyMessage: propTypes.node.isRequired, +}) +export default NoObjects diff --git a/packages/xo-web/src/common/notification.js b/packages/xo-web/src/common/notification.js new file mode 100644 index 000000000..985d655f3 --- /dev/null +++ b/packages/xo-web/src/common/notification.js @@ -0,0 +1,88 @@ +import _ from 'intl' +import ButtonLink from 'button-link' +import Icon from 'icon' +import React, { Component } from 'react' +import ReactNotify from 'react-notify' +import { connectStore } from 'utils' +import { isAdmin } from 'selectors' + +let instance + +export let error +export let info +export let success + +@connectStore({ + isAdmin, +}) +export class Notification extends Component { + componentDidMount () { + if (instance) { + throw new Error('Notification is a singleton!') + } + instance = this + } + + componentWillUnmount () { + instance = undefined + } + + // This special component never have to rerender! + shouldComponentUpdate () { + return false + } + + render () { + return ( + { + if (!notification) { + return + } + + error = (title, body) => + notification.error( + title, + this.props.isAdmin ? ( +
    +
    {body}
    + + {_('showLogs')} + +
    + ) : ( + body + ), + 6e3 + ) + info = (title, body) => notification.info(title, body, 3e3) + success = (title, body) => notification.success(title, body, 3e3) + }} + /> + ) + } +} + +export { info as default } + +/* Example: + +import info, { success, error } from 'notification' + + + + + + +*/ diff --git a/packages/xo-web/src/common/object-name.js b/packages/xo-web/src/common/object-name.js new file mode 100644 index 000000000..eca61df28 --- /dev/null +++ b/packages/xo-web/src/common/object-name.js @@ -0,0 +1,15 @@ +/** EXPERIMENT: this is here to avoid a littel code dupplication, but is not admitted as a highly recommendable component */ +import { connectStore } from 'utils' +import { createGetObject } from 'selectors' +import React, { Component } from 'react' + +@connectStore(() => { + const object = createGetObject() + return (state, props) => ({ object: object(state, props) }) +}) +export default class ObjectName extends Component { + render () { + const { object } = this.props + return {object && object.name_label} + } +} diff --git a/packages/xo-web/src/common/pagination.js b/packages/xo-web/src/common/pagination.js new file mode 100644 index 000000000..22ded2c66 --- /dev/null +++ b/packages/xo-web/src/common/pagination.js @@ -0,0 +1,125 @@ +import React from 'react' +import PropTypes from 'prop-types' + +const PageItem = ({ active, children, disabled, onClick, value }) => + active ? ( +
  • + {children} +
  • + ) : disabled ? ( +
  • + {children} +
  • + ) : ( +
  • + + {children} + +
  • + ) + +export default class Pagination extends React.PureComponent { + static defaultProps = { + ellipsis: true, + maxButtons: 7, + next: true, + prev: true, + } + + static propTypes = { + ariaLabel: PropTypes.string, + ellipsis: PropTypes.bool, + maxButtons: PropTypes.number, + next: PropTypes.bool, + onChange: PropTypes.func.isRequired, + pages: PropTypes.number.isRequired, + prev: PropTypes.bool, + value: PropTypes.number.isRequired, + } + + _onClick (event) { + event.preventDefault() + this.props.onChange(+event.currentTarget.dataset.value) + } + _onClick = this._onClick.bind(this) + + render () { + const { + ariaLabel, + ellipsis, + maxButtons, + next, + pages, + prev, + value, + } = this.props + const onClick = this._onClick + + let min, max + if (pages <= maxButtons) { + min = 1 + max = pages + } else { + min = Math.max( + 1, + Math.min(value - Math.floor(maxButtons / 2), pages - maxButtons + 1) + ) + max = min + maxButtons - 1 + } + + const pageButtons = [] + if (ellipsis && min !== 1) { + pageButtons.push( + + … + + ) + } + for (let page = min; page <= max; ++page) { + pageButtons.push( + + {page} + + ) + } + if (ellipsis && max !== pages) { + pageButtons.push( + + … + + ) + } + return ( + + ) + } +} diff --git a/packages/xo-web/src/common/prop-types-decorator.js b/packages/xo-web/src/common/prop-types-decorator.js new file mode 100644 index 000000000..b2981deaf --- /dev/null +++ b/packages/xo-web/src/common/prop-types-decorator.js @@ -0,0 +1,45 @@ +import assign from 'lodash/assign' +import PropTypes from 'prop-types' + +// Deprecated because : +// - unnecessary +// - not standard in the React ecosystem +if (__DEV__) { + console.warn(`DEPRECATED: use prop-types directly: +class MyComponent extends React.Component { + static propTypes = { + foo: PropTypes.string.isRequired + } +}`) +} + +// Decorators to help declaring properties and context types on React +// components without using the tedious static properties syntax. +// +// ```js +// @propTypes({ +// children: propTypes.node.isRequired +// }, { +// store: propTypes.object.isRequired +// }) +// class MyComponent extends React.Component {} +// ``` +const propTypes = (propTypes, contextTypes) => target => { + if (propTypes !== undefined) { + target.propTypes = { + ...target.propTypes, + ...propTypes, + } + } + if (contextTypes !== undefined) { + target.contextTypes = { + ...target.contextTypes, + ...contextTypes, + } + } + + return target +} +assign(propTypes, PropTypes) + +export { propTypes as default } diff --git a/packages/xo-web/src/common/react-novnc.js b/packages/xo-web/src/common/react-novnc.js new file mode 100644 index 000000000..c83d7ffcc --- /dev/null +++ b/packages/xo-web/src/common/react-novnc.js @@ -0,0 +1,169 @@ +import React, { Component } from 'react' +import RFB from '@nraynaud/novnc/lib/rfb' +import URL from 'url-parse' +import { createBackoff } from 'jsonrpc-websocket-client' +import { + enable as enableShortcuts, + disable as disableShortcuts, +} from 'shortcuts' + +import propTypes from './prop-types-decorator' + +const PROTOCOL_ALIASES = { + 'http:': 'ws:', + 'https:': 'wss:', +} +const fixProtocol = url => { + const protocol = PROTOCOL_ALIASES[url.protocol] + if (protocol) { + url.protocol = protocol + } +} + +@propTypes({ + onClipboardChange: propTypes.func, + url: propTypes.string.isRequired, +}) +export default class NoVnc extends Component { + constructor (props) { + super(props) + this._rfb = null + this._retryGen = createBackoff(Infinity) + + this._onUpdateState = (rfb, state) => { + if (state === 'normal') { + if (this._retryTimeout) { + clearTimeout(this._retryTimeout) + this._retryTimeout = undefined + this._retryGen = createBackoff(Infinity) + } + } + + if (state !== 'disconnected' || this.refs.canvas == null) { + return + } + + clearTimeout(this._retryTimeout) + this._retryTimeout = setTimeout( + this._connect, + this._retryGen.next().value + ) + } + } + + sendCtrlAltDel () { + const rfb = this._rfb + if (rfb) { + rfb.sendCtrlAltDel() + } + } + + setClipboard (text) { + const rfb = this._rfb + if (rfb) { + rfb.clipboardPasteFrom(text) + } + } + + _clean () { + const rfb = this._rfb + if (rfb) { + this._rfb = null + rfb.disconnect() + } + enableShortcuts() + } + + _connect = () => { + this._clean() + + const { canvas } = this.refs + if (!canvas) { + return + } + + const url = new URL(this.props.url) + fixProtocol(url) + + const isSecure = url.protocol === 'wss:' + + const { onClipboardChange } = this.props + const rfb = (this._rfb = new RFB({ + encrypt: isSecure, + target: this.refs.canvas, + onClipboard: + onClipboardChange && + ((_, text) => { + onClipboardChange(text) + }), + onUpdateState: this._onUpdateState, + })) + + // remove leading slashes from the path + // + // a leading slassh will be added by noVNC + const clippedPath = url.pathname.replace(/^\/+/, '') + + // a port is required + // + // if not available from the URL, use the default ones + const port = url.port || (isSecure ? 443 : 80) + + rfb.connect(url.hostname, port, null, clippedPath) + disableShortcuts() + } + + componentDidMount () { + this._connect() + } + + componentWillUnmount () { + this._clean() + } + + componentWillReceiveProps (props) { + const rfb = this._rfb + if (rfb && this.props.scale !== props.scale) { + rfb.get_display().set_scale(props.scale || 1) + rfb.get_mouse().set_scale(props.scale || 1) + } + } + + _focus = () => { + const rfb = this._rfb + if (rfb) { + const { activeElement } = document + if (activeElement) { + activeElement.blur() + } + + rfb.get_keyboard().grab() + rfb.get_mouse().grab() + + disableShortcuts() + } + } + + _unfocus = () => { + const rfb = this._rfb + if (rfb) { + rfb.get_keyboard().ungrab() + rfb.get_mouse().ungrab() + + enableShortcuts() + } + } + + render () { + return ( + + ) + } +} diff --git a/packages/xo-web/src/common/render-xo-item.js b/packages/xo-web/src/common/render-xo-item.js new file mode 100644 index 000000000..4ade1211d --- /dev/null +++ b/packages/xo-web/src/common/render-xo-item.js @@ -0,0 +1,265 @@ +import _ from 'intl' +import React from 'react' +import { startsWith } from 'lodash' + +import Icon from './icon' +import propTypes from './prop-types-decorator' +import { createGetObject } from './selectors' +import { isSrWritable } from './xo' +import { connectStore, formatSize } from './utils' + +// =================================================================== + +const OBJECT_TYPE_TO_ICON = { + 'VM-template': 'vm', + host: 'host', + network: 'network', +} + +// Host, Network, VM-template. +const PoolObjectItem = propTypes({ + object: propTypes.object.isRequired, +})( + connectStore(() => { + const getPool = createGetObject((_, props) => props.object.$pool) + + return (state, props) => ({ + pool: getPool(state, props), + }) + })(({ object, pool }) => { + const icon = OBJECT_TYPE_TO_ICON[object.type] + const { id } = object + + return ( + + {`${object.name_label || id} `} + {pool && `(${pool.name_label || pool.id})`} + + ) + }) +) + +// SR. +const SrItem = propTypes({ + sr: propTypes.object.isRequired, +})( + connectStore(() => { + const getContainer = createGetObject((_, props) => props.sr.$container) + + return (state, props) => ({ + container: getContainer(state, props), + }) + })(({ sr, container }) => { + let label = `${sr.name_label || sr.id}` + + if (isSrWritable(sr)) { + label += ` (${formatSize(sr.size - sr.physical_usage)} free)` + } + + return ( + + {label} + + ) + }) +) + +// VM. +const VmItem = propTypes({ + vm: propTypes.object.isRequired, +})( + connectStore(() => { + const getContainer = createGetObject((_, props) => props.vm.$container) + + return (state, props) => ({ + container: getContainer(state, props), + }) + })(({ vm, container }) => ( + + {' '} + {vm.name_label || vm.id} + {container && ` (${container.name_label || container.id})`} + + )) +) + +const VgpuItem = connectStore(() => ({ + vgpuType: createGetObject((_, props) => props.vgpu.vgpuType), +}))(({ vgpu, vgpuType }) => ( + + {vgpuType.modelName} + +)) + +// =================================================================== + +const xoItemToRender = { + // Subscription objects. + group: group => ( + + {group.name} + + ), + remote: remote => ( + + {remote.value.name} + + ), + role: role => {role.name}, + user: user => ( + + {user.email} + + ), + resourceSet: resourceSet => ( + + + {resourceSet.name} + {' '} + ({resourceSet.id}) + + ), + sshKey: key => ( + + {key.label} + + ), + ipPool: ipPool => ( + + {ipPool.name} + + ), + ipAddress: ({ label, used }) => { + if (used) { + return {label} + } + return {label} + }, + + // XO objects. + pool: pool => ( + + {pool.name_label || pool.id} + + ), + + VDI: vdi => ( + + {vdi.name_label}{' '} + {vdi.name_description && ({vdi.name_description})} + + ), + + // Pool objects. + 'VM-template': vmTemplate => , + host: host => , + network: network => , + + // SR. + SR: sr => , + + // VM. + VM: vm => , + 'VM-snapshot': vm => , + 'VM-controller': vm => ( + + + + ), + + // PIF. + PIF: pif => ( + + {' '} + {pif.device} ({pif.deviceName}) + + ), + + // Tags. + tag: tag => ( + + {tag.value} + + ), + + // GPUs + + vgpu: vgpu => , + + vgpuType: type => ( + + {type.modelName} ({type.vendorName}){' '} + {type.maxResolutionX}x{type.maxResolutionY} + + ), + + gpuGroup: group => ( + + {startsWith(group.name_label, 'Group of ') + ? group.name_label.slice(9) + : group.name_label} + + ), +} + +const renderXoItem = (item, { className } = {}) => { + const { id, type, label } = item + + if (item.removed) { + return ( + + {' '} + {id} + + ) + } + + if (!type) { + if (process.env.NODE_ENV !== 'production' && !label) { + throw new Error(`an item must have at least either a type or a label`) + } + return ( + + {label} + + ) + } + + const Component = xoItemToRender[type] + + if (process.env.NODE_ENV !== 'production' && !Component) { + throw new Error(`no available component for type ${type}`) + } + + if (Component) { + return ( + + + + ) + } +} + +export { renderXoItem as default } + +const GenericXoItem = connectStore(() => { + const getObject = createGetObject() + + return (state, props) => ({ + xoItem: getObject(state, props), + }) +})( + ({ xoItem, ...props }) => + xoItem ? renderXoItem(xoItem, props) : renderXoUnknownItem() +) + +export const renderXoItemFromId = (id, props) => ( + +) + +export const renderXoUnknownItem = () => ( + {_('errorNoSuchItem')} +) diff --git a/packages/xo-web/src/common/resource-set-quotas.js b/packages/xo-web/src/common/resource-set-quotas.js new file mode 100644 index 000000000..b19da2c96 --- /dev/null +++ b/packages/xo-web/src/common/resource-set-quotas.js @@ -0,0 +1,127 @@ +import _, { messages } from 'intl' +import ChartistGraph from 'react-chartist' +import PropTypes from 'prop-types' +import React from 'react' +import { Card, CardBlock, CardHeader } from 'card' +import { Container, Row, Col } from 'grid' +import { forEach, map } from 'lodash' +import { injectIntl } from 'react-intl' + +import Component from './base-component' +import Icon from './icon' +import { createSelector } from './selectors' +import { formatSize } from './utils' + +// =================================================================== + +const RESOURCES = ['disk', 'memory', 'cpus'] + +// =================================================================== + +@injectIntl +export default class ResourceSetQuotas extends Component { + static propTypes = { + limits: PropTypes.object.isRequired, + } + + _getQuotas = createSelector( + () => this.props.limits, + limits => { + const quotas = {} + + forEach(RESOURCES, resource => { + if (limits[resource] != null) { + const { available, total } = limits[resource] + quotas[resource] = { + available, + total, + usage: total - available, + } + } + }) + + return quotas + } + ) + + render () { + const { intl: { formatMessage } } = this.props + const labels = [ + formatMessage(messages.availableResourceLabel), + formatMessage(messages.usedResourceLabel), + ] + const { cpus, disk, memory } = this._getQuotas() + const quotas = [ + { + header: ( + + {_('cpuStatePanel')} + + ), + validFormat: true, + quota: cpus, + }, + { + header: ( + + {_('memoryStatePanel')} + + ), + validFormat: false, + quota: memory, + }, + { + header: ( + + {_('srUsageStatePanel')} + + ), + validFormat: false, + quota: disk, + }, + ] + return ( + + + {map(quotas, ({ header, validFormat, quota }, key) => ( + + + {header} + + {quota !== undefined ? ( +
    + +

    + {_('resourceSetQuota', { + total: validFormat + ? quota.total.toString() + : formatSize(quota.total), + usage: validFormat + ? quota.usage.toString() + : formatSize(quota.usage), + })} +

    +
    + ) : ( +

    + )} +
    +
    + + ))} +
    +
    + ) + } +} diff --git a/packages/xo-web/src/common/scheduling.js b/packages/xo-web/src/common/scheduling.js new file mode 100644 index 000000000..a98bedec5 --- /dev/null +++ b/packages/xo-web/src/common/scheduling.js @@ -0,0 +1,582 @@ +import classNames from 'classnames' +import later from 'later' +import React from 'react' +import { FormattedDate, FormattedTime } from 'react-intl' +import { forEach, includes, isArray, map, sortedIndex } from 'lodash' + +import _ from './intl' +import Button from './button' +import Component from './base-component' +import propTypes from './prop-types-decorator' +import TimezonePicker from './timezone-picker' +import Icon from './icon' +import Tooltip from './tooltip' +import { Card, CardHeader, CardBlock } from './card' +import { Col, Row } from './grid' +import { Range, Toggle } from './form' + +// =================================================================== + +// By default, later uses UTC but we use this line for future versions. +later.date.UTC() + +// =================================================================== + +const CLICKABLE = { cursor: 'pointer' } +const PREVIEW_SLIDER_STYLE = { width: '400px' } + +// =================================================================== + +const UNITS = ['minute', 'hour', 'monthDay', 'month', 'weekDay'] + +const MINUTES_RANGE = [2, 30] +const HOURS_RANGE = [2, 12] +const MONTH_DAYS_RANGE = [2, 15] +const MONTHS_RANGE = [2, 6] + +const MIN_PREVIEWS = 5 +const MAX_PREVIEWS = 20 + +const MONTHS = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]] + +const DAYS = (() => { + const days = [] + + for (let i = 0; i < 4; i++) { + days[i] = [] + + for (let j = 1; j < 8; j++) { + days[i].push(7 * i + j) + } + } + + days.push([29, 30, 31]) + + return days +})() + +const WEEK_DAYS = [[0, 1, 2], [3, 4, 5], [6]] + +const HOURS = (() => { + const hours = [] + + for (let i = 0; i < 4; i++) { + hours[i] = [] + + for (let j = 0; j < 6; j++) { + hours[i].push(6 * i + j) + } + } + + return hours +})() + +const MINS = (() => { + const minutes = [] + + for (let i = 0; i < 6; i++) { + minutes[i] = [] + + for (let j = 0; j < 10; j++) { + minutes[i].push(10 * i + j) + } + } + + return minutes +})() + +const PICKTIME_TO_ID = { + minute: 0, + hour: 1, + monthDay: 2, + month: 3, + weekDay: 4, +} + +const TIME_FORMAT = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + + // The timezone is not significant for displaying the date previews + // as long as it is the same used to generate the next occurrences + // from the cron patterns. + + // Therefore we can use UTC everywhere and say to the user that the + // previews are in the configured timezone. + timeZone: 'UTC', +} + +// =================================================================== + +// monthNum: [ 0 : 11 ] +const getMonthName = monthNum => ( + +) + +// dayNum: [ 0 : 6 ] +const getDayName = dayNum => ( + // January, 1970, 5th => Monday + +) + +// =================================================================== + +@propTypes({ + cronPattern: propTypes.string.isRequired, +}) +export class SchedulePreview extends Component { + render () { + const { cronPattern } = this.props + const { value } = this.state + + const cronSched = later.parse.cron(cronPattern) + + // Due to implementation, the range used for months is 0-11 + // instead of 1-12 + forEach(cronSched.schedules[0].M, (v, i, a) => { + a[i] = v + 1 + }) + + const dates = later.schedule(cronSched).next(value) + + return ( +
    +
    + {_('cronPattern')} {cronPattern} +
    +
    + +
    +
      + {map(dates, (date, id) => ( +
    • + +
    • + ))} +
    • ...
    • +
    +
    + ) + } +} + +// =================================================================== + +@propTypes({ + children: propTypes.any.isRequired, + onChange: propTypes.func.isRequired, + tdId: propTypes.number.isRequired, + value: propTypes.bool.isRequired, +}) +class ToggleTd extends Component { + _onClick = () => { + const { props } = this + props.onChange(props.tdId, !props.value) + } + + render () { + const { props } = this + return ( + + {props.children} + + ) + } +} + +// =================================================================== + +@propTypes({ + labelId: propTypes.string.isRequired, + options: propTypes.array.isRequired, + optionRenderer: propTypes.func, + onChange: propTypes.func.isRequired, + value: propTypes.array.isRequired, +}) +class TableSelect extends Component { + static defaultProps = { + optionRenderer: value => value, + } + + _reset = () => { + this.props.onChange([]) + } + + _handleChange = (tdId, tdValue) => { + const { props } = this + + const newValue = props.value.slice() + const index = sortedIndex(newValue, tdId) + + if (tdValue) { + // Add + if (newValue[index] !== tdId) { + newValue.splice(index, 0, tdId) + } + } else { + // Remove + if (newValue[index] === tdId) { + newValue.splice(index, 1) + } + } + + props.onChange(newValue) + } + + render () { + const { labelId, options, optionRenderer, value } = this.props + + return ( +
    + + + {map(options, (line, i) => ( + + {map(line, tdOption => ( + + ))} + + ))} + +
    + +
    + ) + } +} + +// =================================================================== + +// "2,7" => [2,7] "*/2" => 2 "*" => [] +const cronToValue = (cron, range) => { + if (cron.indexOf('/') === 1) { + return +cron.split('/')[1] + } + + if (cron === '*') { + return [] + } + + return map(cron.split(','), Number) +} + +// [2,7] => "2,7" 2 => "*/2" [] => "*" +const valueToCron = value => { + if (!isArray(value)) { + return `*/${value}` + } + + if (!value.length) { + return '*' + } + + return value.join(',') +} + +@propTypes({ + headerAddon: propTypes.node, + optionRenderer: propTypes.func, + onChange: propTypes.func.isRequired, + range: propTypes.array, + labelId: propTypes.string.isRequired, + value: propTypes.any.isRequired, +}) +class TimePicker extends Component { + _update = cron => { + const { tableValue, rangeValue } = this.state + + const newValue = cronToValue(cron) + const periodic = !isArray(newValue) + + this.setState({ + periodic, + tableValue: periodic ? tableValue : newValue, + rangeValue: periodic ? newValue : rangeValue, + }) + } + + componentWillReceiveProps (props) { + if (props.value !== this.props.value) { + this._update(props.value) + } + } + + componentDidMount () { + this._update(this.props.value) + } + + _onChange = value => { + this.props.onChange(valueToCron(value)) + } + + _tableTab = () => this._onChange(this.state.tableValue || []) + _periodicTab = () => + this._onChange(this.state.rangeValue || this.props.range[0]) + + render () { + const { headerAddon, labelId, options, optionRenderer, range } = this.props + + const { periodic, tableValue, rangeValue } = this.state + + return ( + + + {_(`scheduling${labelId}`)} + {headerAddon} + + + {range && ( + + )} + {periodic ? ( + + ) : ( + + )} + + + ) + } +} + +const isWeekDayMode = ({ monthDayPattern, weekDayPattern }) => { + if (monthDayPattern === '*' && weekDayPattern === '*') { + return + } + + return weekDayPattern !== '*' +} + +@propTypes({ + monthDayPattern: propTypes.string.isRequired, + weekDayPattern: propTypes.string.isRequired, +}) +class DayPicker extends Component { + state = { + weekDayMode: isWeekDayMode(this.props), + } + + componentWillReceiveProps (props) { + const weekDayMode = isWeekDayMode(props) + + if (weekDayMode !== undefined) { + this.setState({ weekDayMode }) + } + } + + _setWeekDayMode = weekDayMode => { + this.props.onChange(['*', '*']) + this.setState({ weekDayMode }) + } + + _onChange = cron => { + const isMonthDayPattern = !this.state.weekDayMode || includes(cron, '/') + + this.props.onChange([ + isMonthDayPattern ? cron : '*', + isMonthDayPattern ? '*' : cron, + ]) + } + + render () { + const { monthDayPattern, weekDayPattern } = this.props + const { weekDayMode } = this.state + + const dayModeToggle = ( + + + + + + ) + + return ( + + ) + } +} + +// =================================================================== + +@propTypes({ + cronPattern: propTypes.string, + onChange: propTypes.func, + timezone: propTypes.string, + value: propTypes.shape({ + cronPattern: propTypes.string.isRequired, + timezone: propTypes.string, + }), +}) +export default class Scheduler extends Component { + constructor (props) { + super(props) + + this._onCronChange = newCrons => { + const cronPattern = this._getCronPattern().split(' ') + forEach(newCrons, (cron, unit) => { + cronPattern[PICKTIME_TO_ID[unit]] = cron + }) + + this.props.onChange({ + cronPattern: cronPattern.join(' '), + timezone: this._getTimezone(), + }) + } + + forEach(UNITS, unit => { + this[`_${unit}Change`] = cron => this._onCronChange({ [unit]: cron }) + }) + this._dayChange = ([monthDay, weekDay]) => + this._onCronChange({ monthDay, weekDay }) + } + + _onTimezoneChange = timezone => { + this.props.onChange({ + cronPattern: this._getCronPattern(), + timezone, + }) + } + + _getCronPattern = () => { + const { value, cronPattern = value.cronPattern } = this.props + return cronPattern + } + + _getTimezone = () => { + const { value, timezone = value && value.timezone } = this.props + return timezone + } + + render () { + const cronPatternArr = this._getCronPattern().split(' ') + const timezone = this._getTimezone() + + return ( +
    + + + + + + + + + + + + + + + + + + +
    + + +
    +
    + ) + } +} diff --git a/packages/xo-web/src/common/select-files.js b/packages/xo-web/src/common/select-files.js new file mode 100644 index 000000000..8a8846626 --- /dev/null +++ b/packages/xo-web/src/common/select-files.js @@ -0,0 +1,34 @@ +import _ from 'intl' +import Component from 'base-component' +import Icon from 'icon' +import propTypes from 'prop-types-decorator' +import React from 'react' +import { omit } from 'lodash' + +@propTypes({ + multi: propTypes.bool, + label: propTypes.node, + onChange: propTypes.func.isRequired, +}) +export default class SelectFiles extends Component { + _onChange = e => { + const { multi, onChange } = this.props + const { files } = e.target + + onChange(multi ? files : files[0]) + } + + render () { + return ( + + ) + } +} diff --git a/packages/xo-web/src/common/select-objects.js b/packages/xo-web/src/common/select-objects.js new file mode 100644 index 000000000..32be7d416 --- /dev/null +++ b/packages/xo-web/src/common/select-objects.js @@ -0,0 +1,1017 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { parse as parseRemote } from 'xo-remote-parser' +import { + assign, + filter, + flatten, + forEach, + groupBy, + includes, + isArray, + isEmpty, + isInteger, + isString, + keyBy, + keys, + map, + mapValues, + pick, + sortBy, + toArray, +} from 'lodash' + +import _ from './intl' +import Button from './button' +import Icon from './icon' +import renderXoItem from './render-xo-item' +import Select from './form/select' +import store from './store' +import Tooltip from './tooltip' +import uncontrollableInput from 'uncontrollable-input' +import { + createCollectionWrapper, + createFilter, + createGetObjectsOfType, + createGetTags, + createSelector, + getObject, +} from './selectors' +import { addSubscriptions, connectStore, resolveResourceSets } from './utils' +import { + isSrWritable, + subscribeCurrentUser, + subscribeGroups, + subscribeIpPools, + subscribeRemotes, + subscribeResourceSets, + subscribeRoles, + subscribeUsers, +} from './xo' + +// =================================================================== + +// react-select's line-height is 1.4 +// https://github.com/JedWatson/react-select/blob/916ab0e62fc7394be8e24f22251c399a68de8b1c/less/multi.less#L33 +// while bootstrap button's line-height is 1.25 +// https://github.com/twbs/bootstrap/blob/959c4e527c6ef69623928db638267ba1c370479d/scss/_variables.scss#L342 +const ADDON_BUTTON_STYLE = { lineHeight: '1.4' } + +const getIds = value => + value == null || isString(value) || isInteger(value) + ? value + : isArray(value) ? map(value, getIds) : value.id + +const getOption = (object, container) => ({ + label: container + ? `${getLabel(object)} ${getLabel(container)}` + : getLabel(object), + value: object.id, + xoItem: object, +}) + +const getLabel = object => + object.name_label || + object.name || + object.email || + (object.value && object.value.name) || + object.value || + object.label + +const options = props => ({ + defaultValue: props.multi ? [] : undefined, +}) + +// =================================================================== + +/* + * WITHOUT xoContainers : + * + * xoObjects: [ + * { type: 'myType', id: 'abc', label: 'First object' }, + * { type: 'myType', id: 'def', label: 'Second object' } + * ] + * + * + * WITH xoContainers : + * + * xoContainers: [ + * { type: 'containerType', id: 'ghi', label: 'First container' }, + * { type: 'containerType', id: 'jkl', label: 'Second container' } + * ] + * + * xoObjects: { + * ghi: [ + * { type: 'objectType', id: 'mno', label: 'First object' } + * { type: 'objectType', id: 'pqr', label: 'Second object' } + * ], + * jkl: [ + * { type: 'objectType', id: 'stu', label: 'Third object' } + * ] + * } + */ +class GenericSelect extends React.Component { + static propTypes = { + hasSelectAll: PropTypes.bool, + multi: PropTypes.bool, + onChange: PropTypes.func.isRequired, + xoContainers: PropTypes.array, + xoObjects: PropTypes.oneOfType([ + PropTypes.array, + PropTypes.objectOf(PropTypes.array), + ]).isRequired, + } + + _getObjectsById = createSelector( + () => this.props.xoObjects, + objects => + keyBy(isArray(objects) ? objects : flatten(toArray(objects)), 'id') + ) + + _getOptions = createSelector( + () => this.props.xoContainers, + () => this.props.xoObjects, + (containers, objects) => { + // createCollectionWrapper with a depth? + const { name } = this.constructor + + let options + if (containers === undefined) { + if (__DEV__ && !isArray(objects)) { + throw new Error( + `${name}: without xoContainers, xoObjects must be an array` + ) + } + + options = map(objects, getOption) + } else { + if (__DEV__ && isArray(objects)) { + throw new Error( + `${name}: with xoContainers, xoObjects must be an object` + ) + } + + options = [] + forEach(containers, container => { + options.push({ + disabled: true, + xoItem: container, + }) + + forEach(objects[container.id], object => { + options.push(getOption(object, container)) + }) + }) + } + + const objectsById = this._getObjectsById() + const addIfMissing = val => { + if (val != null && !(val in objectsById)) { + options.push({ + disabled: true, + id: val, + label: val, + value: val, + xoItem: { + id: val, + removed: true, + }, + }) + } + } + + const values = this._getSelectedIds() + if (isArray(values)) { + forEach(values, addIfMissing) + } else { + addIfMissing(values) + } + + return options + } + ) + + _getSelectedIds = createSelector( + () => this.props.value, + createCollectionWrapper(getIds) + ) + + _getSelectedObjects = (() => { + const helper = createSelector( + this._getObjectsById, + value => value, + (objectsById, value) => + isArray(value) + ? map(value, value => objectsById[value.value]) + : objectsById[value.value] + ) + return value => (value == null ? value : helper(value)) + })() + + _onChange = value => { + this.props.onChange(this._getSelectedObjects(value)) + } + + _selectAll = () => { + this._onChange(filter(this._getOptions(), ({ disabled }) => !disabled)) + } + + // GroupBy: Display option with margin if not disabled and containers exists. + _renderOption = option => ( + + {renderXoItem(option.xoItem)} + + ) + + render () { + const { hasSelectAll, xoContainers, xoObjects, ...props } = this.props + + const select = ( + + } +} + +// =================================================================== + +const actionsShape = propTypes.arrayOf( + propTypes.shape({ + // groupedActions: the function will be called with an array of the selected items in parameters + // individualActions: the function will be called with the related item in parameters + disabled: propTypes.oneOfType([propTypes.bool, propTypes.func]), + handler: propTypes.func.isRequired, + icon: propTypes.string.isRequired, + label: propTypes.node.isRequired, + level: propTypes.oneOf(['primary', 'warning', 'danger']), + }) +) + +class IndividualAction extends Component { + _getIsDisabled = createSelector( + () => this.props.disabled, + () => this.props.item, + () => this.props.userData, + (disabled, item, userData) => + isFunction(disabled) ? disabled(item, userData) : disabled + ) + + render () { + const { icon, label, level, handler, item } = this.props + + return ( + + ) + } +} + +class GroupedAction extends Component { + _getIsDisabled = createSelector( + () => this.props.disabled, + () => this.props.selectedItems, + () => this.props.userData, + (disabled, selectedItems, userData) => + isFunction(disabled) ? disabled(selectedItems, userData) : disabled + ) + + render () { + const { icon, label, level, handler, selectedItems } = this.props + + return ( + + ) + } +} + +// page number and sort info are optional for backward compatibility +const URL_STATE_RE = /^(?:(\d+)(?:_(\d+)(_desc)?)?-)?(.*)$/ + +@propTypes( + { + defaultColumn: propTypes.number, + defaultFilter: propTypes.string, + collection: propTypes.oneOfType([propTypes.array, propTypes.object]) + .isRequired, + columns: propTypes.arrayOf( + propTypes.shape({ + component: propTypes.func, + default: propTypes.bool, + name: propTypes.node, + itemRenderer: propTypes.func, + sortCriteria: propTypes.oneOfType([propTypes.func, propTypes.string]), + sortOrder: propTypes.string, + textAlign: propTypes.string, + }) + ).isRequired, + filterContainer: propTypes.func, + filters: propTypes.object, + actions: propTypes.arrayOf( + propTypes.shape({ + // regroup individual actions and grouped actions + disabled: propTypes.oneOfType([propTypes.bool, propTypes.func]), + handler: propTypes.func.isRequired, + icon: propTypes.string.isRequired, + individualHandler: propTypes.func, + label: propTypes.node.isRequired, + level: propTypes.oneOf(['primary', 'warning', 'danger']), + }) + ), + groupedActions: actionsShape, + individualActions: actionsShape, + itemsPerPage: propTypes.number, + paginationContainer: propTypes.func, + rowAction: propTypes.func, + rowLink: propTypes.oneOfType([propTypes.func, propTypes.string]), + // DOM node selector like body or .my-class + // The shortcuts will be enabled when the node is focused + shortcutsTarget: propTypes.string, + stateUrlParam: propTypes.string, + userData: propTypes.any, + }, + { + router: routerShape, + } +) +export default class SortedTable extends Component { + static defaultProps = { + itemsPerPage: 10, + } + + constructor (props, context) { + super(props, context) + + let selectedColumn = props.defaultColumn + if (selectedColumn == null) { + selectedColumn = findIndex(props.columns, 'default') + + if (selectedColumn === -1) { + selectedColumn = 0 + } + } + + const state = (this.state = { + all: false, // whether all items are selected (accross pages) + filter: defined(() => props.filters[props.defaultFilter], ''), + page: 1, + selectedColumn, + sortOrder: + props.columns[selectedColumn].sortOrder === 'desc' ? 'desc' : 'asc', + }) + + const urlState = get( + () => context.router.location.query[props.stateUrlParam] + ) + + let matches + if ( + urlState !== undefined && + (matches = URL_STATE_RE.exec(urlState)) !== null + ) { + state.filter = matches[4] + const page = matches[1] + if (page !== undefined) { + state.page = +page + } + let selectedColumn = matches[2] + if ( + selectedColumn !== undefined && + (selectedColumn = +selectedColumn) < props.columns.length + ) { + state.selectedColumn = selectedColumn + state.sortOrder = matches[3] !== undefined ? 'desc' : 'asc' + } + } + + this._getSelectedColumn = () => + this.props.columns[this.state.selectedColumn] + + this._getTotalNumberOfItems = createCounter(() => this.props.collection) + + const createMatcher = str => CM.parse(str).createPredicate() + this._getItems = createSort( + createFilter( + () => this.props.collection, + createSelector(() => this.state.filter, createMatcher) + ), + createSelector( + () => this._getSelectedColumn().sortCriteria, + () => this.props.userData, + (sortCriteria, userData) => + typeof sortCriteria === 'function' + ? object => sortCriteria(object, userData) + : sortCriteria + ), + () => this.state.sortOrder + ) + + this._getVisibleItems = createPager( + this._getItems, + () => this.state.page, + () => this.props.itemsPerPage + ) + + state.selectedItemsIds = new Set() + + this._getSelectedItems = createSelector( + () => this.state.all, + () => this.state.selectedItemsIds, + this._getItems, + (all, selectedItemsIds, items) => + all ? items : filter(items, item => selectedItemsIds.has(item.id)) + ) + + this._hasGroupedActions = createSelector( + this._getGroupedActions, + actions => !isEmpty(actions) + ) + + this._getShortcutsHandler = createSelector( + this._getVisibleItems, + this._hasGroupedActions, + () => this.state.highlighted, + () => this.props.rowLink, + () => this.props.rowAction, + () => this.props.userData, + ( + visibleItems, + hasGroupedActions, + itemIndex, + rowLink, + rowAction, + userData + ) => (command, event) => { + event.preventDefault() + const item = + itemIndex !== undefined ? visibleItems[itemIndex] : undefined + + switch (command) { + case 'SEARCH': + this.refs.filterInput.focus() + break + case 'NAV_DOWN': + if ( + hasGroupedActions || + rowAction !== undefined || + rowLink !== undefined + ) { + this.setState({ + highlighted: + (itemIndex + visibleItems.length + 1) % visibleItems.length || + 0, + }) + } + break + case 'NAV_UP': + if ( + hasGroupedActions || + rowAction !== undefined || + rowLink !== undefined + ) { + this.setState({ + highlighted: + (itemIndex + visibleItems.length - 1) % visibleItems.length || + 0, + }) + } + break + case 'SELECT': + if (itemIndex !== undefined && hasGroupedActions) { + this._selectItem(itemIndex) + } + break + case 'ROW_ACTION': + if (item !== undefined) { + if (rowLink !== undefined) { + this.context.router.push( + isFunction(rowLink) ? rowLink(item, userData) : rowLink + ) + } else if (rowAction !== undefined) { + rowAction(item, userData) + } + } + break + } + } + ) + } + + componentDidMount () { + this._checkUpdatePage() + + // Force one Portal refresh. + // Because Portal cannot see the container reference at first rendering. + if (this.props.paginationContainer) { + this.forceUpdate() + } + } + + _sort = columnId => { + const { state } = this + let sortOrder + + if (state.selectedColumn === columnId) { + sortOrder = state.sortOrder === 'desc' ? 'asc' : 'desc' + } else { + sortOrder = + this.props.columns[columnId].sortOrder === 'desc' ? 'desc' : 'asc' + } + + this._setVisibleState({ + selectedColumn: columnId, + sortOrder, + }) + } + + componentDidUpdate () { + const { selectedItemsIds } = this.state + + // Unselect items that are no longer visible + if ( + (this._visibleItemsRecomputations || 0) < + (this._visibleItemsRecomputations = this._getVisibleItems.recomputations()) + ) { + const newSelectedItems = selectedItemsIds.intersect( + map(this._getVisibleItems(), 'id') + ) + if (newSelectedItems.size < selectedItemsIds.size) { + this.setState({ selectedItemsIds: newSelectedItems }) + } + } + + this._checkUpdatePage() + } + + _saveUrlState = () => { + const { filter, page, selectedColumn, sortOrder } = this.state + const { router } = this.context + const { location } = router + router.replace({ + ...location, + query: { + ...location.query, + [this.props.stateUrlParam]: `${page}_${selectedColumn}${ + sortOrder === 'desc' ? '_desc' : '' + }-${filter}`, + }, + }) + } + + // update state in the state and update the URL param + _setVisibleState (state) { + this.setState(state, this.props.stateUrlParam && this._saveUrlState) + } + + _setFilter = filter => { + this._setVisibleState({ + filter, + page: 1, + highlighted: undefined, + }) + } + + _checkUpdatePage () { + const { page } = this.state + if (page === 1) { + return + } + + const n = this._getItems().length + const { itemsPerPage } = this.props + if (n < itemsPerPage) { + return this._setPage(1) + } + + const last = ceil(n / itemsPerPage) + if (page > last) { + return this._setPage(last) + } + } + + _setPage (page) { + this._setVisibleState({ page }) + } + _setPage = this._setPage.bind(this) + + _selectAllVisibleItems = event => { + this.setState({ + all: false, + selectedItemsIds: event.target.checked + ? this.state.selectedItemsIds.union(map(this._getVisibleItems(), 'id')) + : this.state.selectedItemsIds.clear(), + }) + } + + // TODO: figure out why it's necessary + _toggleNestedCheckboxGuard = false + + _toggleNestedCheckbox = event => { + const child = event.target.firstElementChild + if (child != null && child.tagName === 'INPUT') { + if (this._toggleNestedCheckboxGuard) { + return + } + this._toggleNestedCheckboxGuard = true + child.dispatchEvent(new window.MouseEvent('click', event.nativeEvent)) + this._toggleNestedCheckboxGuard = false + } + } + + _selectAll = () => this.setState({ all: true }) + + _selectItem (current, selected, range = false) { + const { all, selectedItemsIds } = this.state + const visibleItems = this._getVisibleItems() + const item = visibleItems[current] + + if (all) { + return this.setState({ + all: false, + selectedItemsIds: new Set().withMutations(selectedItemsIds => { + forEach(visibleItems, item => { + selectedItemsIds.add(item.id) + }) + selectedItemsIds.delete(item.id) + }), + }) + } + + const method = (selected === undefined + ? !selectedItemsIds.has(item.id) + : selected) + ? 'add' + : 'delete' + + let previous + this.setState({ + selectedItemsIds: + range && (previous = this._previous) !== undefined + ? selectedItemsIds.withMutations(selectedItemsIds => { + let i = previous + let end = current + if (previous > current) { + i = current + end = previous + } + for (; i <= end; ++i) { + selectedItemsIds[method](visibleItems[i].id) + } + }) + : selectedItemsIds[method](item.id), + }) + + this._previous = current + } + + _onSelectItemCheckbox = event => { + const { target } = event + this._selectItem(+target.name, target.checked, event.nativeEvent.shiftKey) + } + + _getGroupedActions = createSelector( + () => this.props.groupedActions, + () => this.props.actions, + (groupedActions, actions) => + groupedActions !== undefined && actions !== undefined + ? groupedActions.concat(actions) + : groupedActions || actions + ) + + _renderItem = (item, i) => { + const { props, state } = this + const { actions, individualActions, rowAction, rowLink, userData } = props + + const hasGroupedActions = this._hasGroupedActions() + const hasIndividualActions = + !isEmpty(individualActions) || !isEmpty(actions) + + const columns = map( + props.columns, + ({ component: Component, itemRenderer, textAlign }, key) => ( + + {Component !== undefined ? ( + + ) : ( + itemRenderer(item, userData) + )} + + ) + ) + + const { id = i } = item + + const selectionColumn = hasGroupedActions && ( + + + + ) + const actionsColumn = hasIndividualActions && ( + +
    + + {map(individualActions, (props, key) => ( + + ))} + {map(actions, (props, key) => ( + + ))} + +
    + + ) + + return rowLink != null ? ( + + {selectionColumn} + {columns} + {actionsColumn} + + ) : ( + rowAction(item, userData))} + > + {selectionColumn} + {columns} + {actionsColumn} + + ) + } + + render () { + const { props, state } = this + const { + actions, + filterContainer, + individualActions, + itemsPerPage, + paginationContainer, + shortcutsTarget, + userData, + } = props + const { all } = state + const groupedActions = this._getGroupedActions() + + const nAllItems = this._getTotalNumberOfItems() + const nItems = this._getItems().length + const nSelectedItems = state.selectedItemsIds.size + const nVisibleItems = this._getVisibleItems().length + + const hasGroupedActions = this._hasGroupedActions() + const hasIndividualActions = + !isEmpty(individualActions) || !isEmpty(actions) + + const nColumns = props.columns.length + (hasIndividualActions ? 2 : 1) + + const displayPagination = + paginationContainer === undefined && itemsPerPage < nAllItems + const displayFilter = nAllItems !== 0 + + const paginationInstance = displayPagination && ( + + ) + + const filterInstance = displayFilter && ( + + ) + + return ( +
    + {shortcutsTarget !== undefined && ( + + )} + + + + + + + {hasGroupedActions && ( + + )} + {map(props.columns, (column, key) => ( + + ))} + {hasIndividualActions && + + + {nVisibleItems !== 0 ? ( + map(this._getVisibleItems(), this._renderItem) + ) : ( + + + + )} + +
    + {nItems === nAllItems + ? _('sortedTableNumberOfItems', { nTotal: nItems }) + : _('sortedTableNumberOfFilteredItems', { + nFiltered: nItems, + nTotal: nAllItems, + })} + {all ? ( + + {' '} + -{' '} + + {_('sortedTableAllItemsSelected')} + + + ) : ( + nSelectedItems !== 0 && ( + + {' '} + -{' '} + {_('sortedTableNumberOfSelectedItems', { + nSelected: nSelectedItems, + })} + {nSelectedItems === nVisibleItems && + nSelectedItems < nItems && ( + + )} + + ) + )} + {nSelectedItems !== 0 && ( +
    + + {map(groupedActions, (props, key) => ( + + ))} + +
    + )} +
    + + } +
    + {_('sortedTableNoItems')} +
    + {(displayFilter || displayPagination) && ( + + + + {displayPagination && + (paginationContainer !== undefined ? ( + // Rebuild container function to refresh Portal component. + paginationContainer()}> + {paginationInstance} + + ) : ( + paginationInstance + ))} + + + {displayFilter && + (filterContainer ? ( + filterContainer()}> + {filterInstance} + + ) : ( + filterInstance + ))} + + + + )} +
    + ) + } +} diff --git a/packages/xo-web/src/common/state-button.js b/packages/xo-web/src/common/state-button.js new file mode 100644 index 000000000..2d9d7f21f --- /dev/null +++ b/packages/xo-web/src/common/state-button.js @@ -0,0 +1,46 @@ +import React from 'react' +import styled from 'styled-components' +import { omit } from 'lodash' + +import ActionButton from './action-button' +import propTypes from './prop-types-decorator' + +// do not forward `state` to ActionButton +const Button = styled(p => )` + background-color: ${p => + p.theme[`${p.state ? 'enabled' : 'disabled'}StateBg`]}; + border: 2px solid + ${p => p.theme[`${p.state ? 'enabled' : 'disabled'}StateColor`]}; + color: ${p => p.theme[`${p.state ? 'enabled' : 'disabled'}StateColor`]}; +` + +const StateButton = ({ + disabledHandler, + disabledHandlerParam, + disabledLabel, + disabledTooltip, + + enabledLabel, + enabledTooltip, + enabledHandler, + enabledHandlerParam, + + state, + ...props +}) => ( + +) + +export default propTypes({ + state: propTypes.bool.isRequired, +})(StateButton) diff --git a/packages/xo-web/src/common/store/actions.js b/packages/xo-web/src/common/store/actions.js new file mode 100644 index 000000000..62e16a75a --- /dev/null +++ b/packages/xo-web/src/common/store/actions.js @@ -0,0 +1,52 @@ +const createAction = (() => { + const { defineProperty } = Object + + return (type, payloadCreator) => + defineProperty( + payloadCreator + ? (...args) => ({ + type, + payload: payloadCreator(...args), + }) + : (action => + function () { + if (arguments.length) { + throw new Error('this action expects no payload!') + } + + return action + })({ type }), + 'toString', + { value: () => type } + ) +})() + +// =================================================================== + +export const selectLang = createAction('SELECT_LANG', lang => lang) + +// =================================================================== + +export const connected = createAction('CONNECTED') +export const disconnected = createAction('DISCONNECTED') + +export const updateObjects = createAction('UPDATE_OBJECTS', updates => updates) +export const updatePermissions = createAction( + 'UPDATE_PERMISSIONS', + permissions => permissions +) + +export const signedIn = createAction('SIGNED_IN', user => user) +export const signedOut = createAction('SIGNED_OUT') + +export const xoaUpdaterState = createAction('XOA_UPDATER_STATE', state => state) +export const xoaTrialState = createAction('XOA_TRIAL_STATE', state => state) +export const xoaUpdaterLog = createAction('XOA_UPDATER_LOG', log => log) +export const xoaRegisterState = createAction( + 'XOA_REGISTER_STATE', + registration => registration +) +export const xoaConfiguration = createAction( + 'XOA_CONFIGURATION', + configuration => configuration +) diff --git a/packages/xo-web/src/common/store/index.js b/packages/xo-web/src/common/store/index.js new file mode 100644 index 000000000..e606cb1c8 --- /dev/null +++ b/packages/xo-web/src/common/store/index.js @@ -0,0 +1,18 @@ +import reduxThunk from 'redux-thunk' +import { applyMiddleware, combineReducers, createStore } from 'redux' + +import { connectStore as connectXo } from '../xo' + +import reducer from './reducer' + +// =================================================================== + +const store = createStore(combineReducers(reducer), applyMiddleware(reduxThunk)) + +connectXo(store) + +if (process.env.XOA_PLAN < 5) { + require('xoa-updater').connectStore(store) +} + +export default store diff --git a/packages/xo-web/src/common/store/reducer.js b/packages/xo-web/src/common/store/reducer.js new file mode 100644 index 000000000..0cc75dd19 --- /dev/null +++ b/packages/xo-web/src/common/store/reducer.js @@ -0,0 +1,166 @@ +import cookies from 'cookies-js' + +import invoke from '../invoke' + +import * as actions from './actions' + +// =================================================================== + +const createAsyncHandler = ({ error, next }) => (state, payload, action) => { + if (action.error) { + if (error) { + return error(state, payload, action) + } + } else { + if (next) { + return next(state, payload, action) + } + } + + return state +} + +// Action handlers are reducers but bound to a specific action. +const combineActionHandlers = invoke( + Object.hasOwnProperty, + obj => { + for (const prop in obj) { + return prop + } + }, + (has, firstProp) => (initialState, handlers) => { + let n = 0 + for (const actionType in handlers) { + if (has.call(handlers, actionType)) { + if (actionType === 'undefined') { + throw new Error('invalid action type: undefined') + } + + ++n + + const handler = handlers[actionType] + if (typeof handler === 'object') { + handlers[actionType] = createAsyncHandler(handler) + } + } + } + + if (!n) { + throw new Error('no action handlers declared') + } + + // Optimization for this special case. + if (n === 1) { + const actionType = firstProp(handlers) + const handler = handlers[actionType] + + return (state = initialState, action) => + action.type === actionType + ? handler(state, action.payload, action) + : state + } + + return (state = initialState, action) => { + const handler = handlers[action.type] + + return handler ? handler(state, action.payload, action) : state + } + } +) + +// =================================================================== + +export default { + lang: combineActionHandlers(cookies.get('lang') || 'en', { + [actions.selectLang]: (_, lang) => { + cookies.set('lang', lang) + + return lang + }, + }), + + permissions: combineActionHandlers( + {}, + { + [actions.updatePermissions]: (_, permissions) => permissions, + } + ), + + objects: combineActionHandlers( + { + all: {}, // Mutable for performance! + byType: {}, + }, + { + [actions.updateObjects]: ({ all, byType: prevByType }, updates) => { + const byType = { ...prevByType } + const get = type => { + const curr = byType[type] + const prev = prevByType[type] + return curr === prev ? (byType[type] = { ...prev }) : curr + } + + for (const id in updates) { + const object = updates[id] + const previous = all[id] + + if (object) { + const { type } = object + + all[id] = object + get(type)[id] = object + + if (previous && previous.type !== type) { + delete get(previous.type)[id] + } + } else if (previous) { + delete all[id] + delete get(previous.type)[id] + } + } + + return { all, byType, fetched: true } + }, + } + ), + + user: combineActionHandlers(null, { + [actions.signedIn]: { + next: (_, user) => user, + }, + }), + + status: combineActionHandlers('disconnected', { + [actions.connected]: () => 'connected', + [actions.disconnected]: () => 'disconnected', + }), + + xoaUpdaterState: combineActionHandlers('disconnected', { + [actions.xoaUpdaterState]: (_, state) => state, + }), + xoaTrialState: combineActionHandlers( + {}, + { + [actions.xoaTrialState]: (_, state) => state, + } + ), + xoaUpdaterLog: combineActionHandlers([], { + [actions.xoaUpdaterLog]: (_, log) => log, + }), + xoaRegisterState: combineActionHandlers( + { state: '?' }, + { + [actions.xoaRegisterState]: (_, registration) => registration, + } + ), + xoaConfiguration: combineActionHandlers( + { proxyHost: '', proxyPort: '', proxyUser: '' }, + { + // defined values for controlled inputs + [actions.xoaConfiguration]: (_, configuration) => { + delete configuration.password + return configuration + }, + } + ), +} diff --git a/packages/xo-web/src/common/tab-button.js b/packages/xo-web/src/common/tab-button.js new file mode 100644 index 000000000..973091fb4 --- /dev/null +++ b/packages/xo-web/src/common/tab-button.js @@ -0,0 +1,33 @@ +import React from 'react' + +import _ from './intl' +import ActionButton from './action-button' +import Icon from './icon' +import Link from './link' + +const STYLE = { + marginBottom: '1em', + marginLeft: '1em', +} + +const TabButton = ({ labelId, ...props }) => ( + + {labelId !== undefined && ( + {_(labelId)} + )} + +) +export { TabButton as default } + +export const TabButtonLink = ({ labelId, icon, ...props }) => ( + + + {icon && ( + + {' '} + + )} + {_(labelId)} + + +) diff --git a/packages/xo-web/src/common/tags.js b/packages/xo-web/src/common/tags.js new file mode 100644 index 000000000..1a0fdb21e --- /dev/null +++ b/packages/xo-web/src/common/tags.js @@ -0,0 +1,147 @@ +import filter from 'lodash/filter' +import includes from 'lodash/includes' +import map from 'lodash/map' +import React from 'react' + +import Component from './base-component' +import Icon from './icon' +import propTypes from './prop-types-decorator' + +const INPUT_STYLE = { + margin: '2px', + maxWidth: '4em', +} +const TAG_STYLE = { + backgroundColor: '#2598d9', + borderRadius: '0.5em', + color: 'white', + fontSize: '0.6em', + margin: '0.2em', + marginTop: '-0.1em', + padding: '0.3em', + verticalAlign: 'middle', +} +const LINK_STYLE = { + cursor: 'pointer', +} +const ADD_TAG_STYLE = { + cursor: 'pointer', + fontSize: '0.8em', + marginLeft: '0.2em', +} +const REMOVE_TAG_STYLE = { + cursor: 'pointer', +} + +@propTypes({ + labels: propTypes.arrayOf(React.PropTypes.string).isRequired, + onAdd: propTypes.func, + onChange: propTypes.func, + onClick: propTypes.func, + onDelete: propTypes.func, +}) +export default class Tags extends Component { + componentWillMount () { + this.setState({ editing: false }) + } + + _startEdit = () => { + this.setState({ editing: true }) + } + _stopEdit = () => { + this.setState({ editing: false }) + } + + _addTag = newTag => { + const { labels, onAdd, onChange } = this.props + + if (!includes(labels, newTag)) { + onAdd && onAdd(newTag) + onChange && onChange([...labels, newTag]) + } + } + _deleteTag = tag => { + const { onChange, onDelete } = this.props + + onDelete && onDelete(tag) + onChange && onChange(filter(this.props.labels, t => t !== tag)) + } + + _onKeyDown = event => { + const { keyCode, target } = event + + if (keyCode === 13) { + if (target.value) { + this._addTag(target.value) + target.value = '' + } + } else if (keyCode === 27) { + this._stopEdit() + } else { + return + } + + event.preventDefault() + } + + render () { + const { labels, onAdd, onChange, onClick, onDelete } = this.props + + const deleteTag = (onDelete || onChange) && this._deleteTag + + return ( + + {' '} + + {map(labels.sort(), (label, index) => ( + + ))} + + {(onAdd || onChange) && !this.state.editing ? ( + + + + ) : ( + + + + )} + + ) + } +} + +export const Tag = ({ type, label, onDelete, onClick }) => ( + + onClick(label))} + style={onClick && LINK_STYLE} + > + {label} + {' '} + {onDelete ? ( + onDelete(label))} + style={REMOVE_TAG_STYLE} + > + + + ) : ( + [] + )} + +) +Tag.propTypes = { + label: React.PropTypes.string.isRequired, +} diff --git a/packages/xo-web/src/common/themes/.index-modules b/packages/xo-web/src/common/themes/.index-modules new file mode 100644 index 000000000..e69de29bb diff --git a/packages/xo-web/src/common/themes/base.js b/packages/xo-web/src/common/themes/base.js new file mode 100644 index 000000000..34f7bcb81 --- /dev/null +++ b/packages/xo-web/src/common/themes/base.js @@ -0,0 +1,6 @@ +export default { + disabledStateBg: '#fff', + disabledStateColor: '#c0392b', + enabledStateBg: '#fff', + enabledStateColor: '#27ae60', +} diff --git a/packages/xo-web/src/common/timezone-picker.js b/packages/xo-web/src/common/timezone-picker.js new file mode 100644 index 000000000..4a2e213da --- /dev/null +++ b/packages/xo-web/src/common/timezone-picker.js @@ -0,0 +1,99 @@ +import ActionButton from 'action-button' +import map from 'lodash/map' +import moment from 'moment-timezone' +import React from 'react' + +import _ from './intl' +import Component from './base-component' +import propTypes from './prop-types-decorator' +import { getXoServerTimezone } from './xo' +import { Select } from './form' + +const SERVER_TIMEZONE_TAG = 'server' +const LOCAL_TIMEZONE = moment.tz.guess() + +@propTypes({ + defaultValue: propTypes.string, + onChange: propTypes.func.isRequired, + required: propTypes.bool, + value: propTypes.string, +}) +export default class TimezonePicker extends Component { + componentDidMount () { + getXoServerTimezone.then(serverTimezone => { + this.setState({ + timezone: + this.props.value || this.props.defaultValue || SERVER_TIMEZONE_TAG, + options: [ + ...map(moment.tz.names(), value => ({ label: value, value })), + { + label: _('serverTimezoneOption', { + value: serverTimezone, + }), + value: SERVER_TIMEZONE_TAG, + }, + ], + }) + }) + } + + componentWillReceiveProps (props) { + if (props.value !== this.props.value) { + this.setState({ timezone: props.value || SERVER_TIMEZONE_TAG }) + } + } + + get value () { + return this.state.timezone === SERVER_TIMEZONE_TAG + ? null + : this.state.timezone + } + + set value (value) { + this.setState({ timezone: value || SERVER_TIMEZONE_TAG }) + } + + _onChange = option => { + if (option && option.value === this.state.timezone) { + return + } + + this.setState( + { + timezone: (option != null && option.value) || SERVER_TIMEZONE_TAG, + }, + () => + this.props.onChange( + this.state.timezone === SERVER_TIMEZONE_TAG + ? null + : this.state.timezone + ) + ) + } + + _useLocalTime = () => { + this._onChange({ value: LOCAL_TIMEZONE }) + } + + render () { + const { timezone, options } = this.state + + return ( +
    + + + +
    + ) + } +} diff --git a/packages/xo-web/src/common/xo/choose-sr-for-each-vdis-modal/index.js b/packages/xo-web/src/common/xo/choose-sr-for-each-vdis-modal/index.js new file mode 100644 index 000000000..5537771e0 --- /dev/null +++ b/packages/xo-web/src/common/xo/choose-sr-for-each-vdis-modal/index.js @@ -0,0 +1,107 @@ +import Collapse from 'collapse' +import Component from 'base-component' +import React from 'react' +import { map } from 'lodash' + +import _ from '../../intl' +import propTypes from '../../prop-types-decorator' +import SingleLineRow from '../../single-line-row' +import { Container, Col } from 'grid' +import { isSrWritable } from 'xo' +import { SelectSr } from '../../select-objects' + +const Collapsible = ({ collapsible, children, ...props }) => + collapsible ? ( + {children} + ) : ( +
    + {props.buttonText} +
    + {children} +
    + ) + +Collapsible.propTypes = { + collapsible: propTypes.bool.isRequired, + children: propTypes.node.isRequired, +} + +@propTypes({ + mainSrPredicate: propTypes.func, + onChange: propTypes.func.isRequired, + srPredicate: propTypes.func, + value: propTypes.objectOf( + propTypes.shape({ + mainSr: propTypes.object, + mapVdisSrs: propTypes.object, + }) + ).isRequired, + vdis: propTypes.object.isRequired, +}) +export default class ChooseSrForEachVdisModal extends Component { + _onChange = newValues => { + this.props.onChange({ + ...this.props.value, + ...newValues, + }) + } + + _onChangeMainSr = mainSr => this._onChange({ mainSr }) + + render () { + const { props } = this + const { + mainSrPredicate = isSrWritable, + srPredicate = mainSrPredicate, + value: { mainSr, mapVdisSrs }, + } = props + + return ( +
    + +
    + {props.vdis != null && + mainSr != null && ( + = 3} + > +
    + + + + {_('chooseSrForEachVdisModalVdiLabel')} + + + {_('chooseSrForEachVdisModalSrLabel')} + + + {map(props.vdis, vdi => ( + + {vdi.name_label || vdi.name} + + + this._onChange({ + mapVdisSrs: { ...mapVdisSrs, [vdi.uuid]: sr }, + }) + } + predicate={srPredicate} + value={mapVdisSrs !== undefined && mapVdisSrs[vdi.uuid]} + /> + + + ))} + {_('chooseSrForEachVdisModalOptionalEntry')} + +
    + )} +
    + ) + } +} diff --git a/packages/xo-web/src/common/xo/copy-vm-modal/index.js b/packages/xo-web/src/common/xo/copy-vm-modal/index.js new file mode 100644 index 000000000..5e27e1272 --- /dev/null +++ b/packages/xo-web/src/common/xo/copy-vm-modal/index.js @@ -0,0 +1,64 @@ +import React, { Component } from 'react' + +import _, { messages } from '../../intl' +import SingleLineRow from '../../single-line-row' +import Upgrade from 'xoa-upgrade' +import { Col } from '../../grid' +import { SelectSr } from '../../select-objects' +import { Toggle } from '../../form' +import { injectIntl } from 'react-intl' + +class CopyVmModalBody extends Component { + state = { compress: false } + + get value () { + const { state } = this + return { + compress: state.compress, + name: this.state.name || this.props.vm.name_label, + sr: state.sr.id, + } + } + + _onChangeSr = sr => this.setState({ sr }) + _onChangeName = event => this.setState({ name: event.target.value }) + _onChangeCompress = compress => this.setState({ compress }) + + render () { + const { formatMessage } = this.props.intl + return process.env.XOA_PLAN > 2 ? ( +
    + + {_('copyVmSelectSr')} + + + + +   + + {_('copyVmName')} + + + + +   + + {_('copyVmCompress')} + + + + +
    + ) : ( +
    + +
    + ) + } +} +export default injectIntl(CopyVmModalBody, { withRef: true }) diff --git a/packages/xo-web/src/common/xo/copy-vms-modal/index.js b/packages/xo-web/src/common/xo/copy-vms-modal/index.js new file mode 100644 index 000000000..286bbcfee --- /dev/null +++ b/packages/xo-web/src/common/xo/copy-vms-modal/index.js @@ -0,0 +1,100 @@ +import _, { messages } from 'intl' +import map from 'lodash/map' +import React from 'react' +import { injectIntl } from 'react-intl' + +import BaseComponent from 'base-component' +import SingleLineRow from 'single-line-row' +import Upgrade from 'xoa-upgrade' +import { Col } from 'grid' +import { createGetObjectsOfType } from 'selectors' +import { SelectSr } from 'select-objects' +import { Toggle } from 'form' +import { buildTemplate, connectStore } from 'utils' + +@connectStore( + () => { + const getVms = createGetObjectsOfType('VM').pick((_, props) => props.vms) + return { + vms: getVms, + } + }, + { withRef: true } +) +class CopyVmsModalBody extends BaseComponent { + get value () { + const { state } = this + if (!state || !state.sr) { + return {} + } + const { vms } = this.props + const { namePattern } = state + + const names = namePattern + ? map( + vms, + buildTemplate(namePattern, { + '{name}': vm => vm.name_label, + '{id}': vm => vm.id, + }) + ) + : map(vms, vm => vm.name_label) + return { + compress: state.compress, + names, + sr: state.sr.id, + } + } + + componentWillMount () { + this.setState({ + compress: false, + namePattern: '{name}_COPY', + }) + } + + _onChangeSr = sr => this.setState({ sr }) + _onChangeNamePattern = event => + this.setState({ namePattern: event.target.value }) + _onChangeCompress = compress => this.setState({ compress }) + + render () { + const { formatMessage } = this.props.intl + const { compress, namePattern, sr } = this.state + return process.env.XOA_PLAN > 2 ? ( +
    + + {_('copyVmSelectSr')} + + + + +   + + {_('copyVmName')} + + + + +   + + {_('copyVmCompress')} + + + + +
    + ) : ( +
    + +
    + ) + } +} +export default injectIntl(CopyVmsModalBody, { withRef: true }) diff --git a/packages/xo-web/src/common/xo/create-bonded-network-modal/index.js b/packages/xo-web/src/common/xo/create-bonded-network-modal/index.js new file mode 100644 index 000000000..61fe462ef --- /dev/null +++ b/packages/xo-web/src/common/xo/create-bonded-network-modal/index.js @@ -0,0 +1,113 @@ +import Component from 'base-component' +import map from 'lodash/map' +import React from 'react' +import { createGetObject, createSelector } from 'selectors' +import { getBondModes } from 'xo' +import { injectIntl } from 'react-intl' + +import _, { messages } from '../../intl' +import { Col } from '../../grid' +import { connectStore } from '../../utils' +import { SelectPif } from '../../select-objects' +import SingleLineRow from '../../single-line-row' + +@connectStore( + () => ({ + poolMaster: createSelector( + createGetObject((_, props) => props.pool), + pool => pool.master + ), + }), + { withRef: true } +) +class CreateBondedNetworkModalBody extends Component { + componentWillMount () { + getBondModes().then(bondModes => + this.setState({ bondModes, bondMode: bondModes[0] }) + ) + } + + _getPifPredicate = createSelector( + () => this.props.poolMaster, + hostId => pif => pif.$host === hostId && pif.vlan === -1 + ) + + get value () { + const { name, description, pifs, mtu, bondMode } = this.state + return { + pool: this.props.pool, + name, + description, + pifs: map(pifs, pif => pif.id), + mtu, + bondMode, + } + } + + render () { + const { formatMessage } = this.props.intl + return ( +
    + + {_('newNetworkInterface')} + + + + +   + + {_('newNetworkName')} + + + + +   + + {_('newNetworkDescription')} + + + + +   + + {_('newNetworkMtu')} + + + + +   + + {_('newNetworkBondMode')} + + + + +
    + ) + } +} +export default injectIntl(CreateBondedNetworkModalBody, { withRef: true }) diff --git a/packages/xo-web/src/common/xo/create-network-modal/index.js b/packages/xo-web/src/common/xo/create-network-modal/index.js new file mode 100644 index 000000000..f8e0a27f6 --- /dev/null +++ b/packages/xo-web/src/common/xo/create-network-modal/index.js @@ -0,0 +1,84 @@ +import React, { Component } from 'react' +import { injectIntl } from 'react-intl' +import { createSelector } from 'selectors' + +import SingleLineRow from '../../single-line-row' +import _, { messages } from '../../intl' +import { SelectPif } from '../../select-objects' +import { Col } from '../../grid' + +class CreateNetworkModalBody extends Component { + _getPifPredicate = createSelector( + () => { + const { container } = this.props + return container.type === 'pool' ? container.master : container.id + }, + hostId => pif => pif.$host === hostId && pif.vlan === -1 + ) + + get value () { + const { refs } = this + const { container } = this.props + return { + pool: container.$pool, + name: refs.name.value, + description: refs.description.value, + pif: refs.pif.value.id, + mtu: refs.mtu.value, + vlan: refs.vlan.value, + } + } + + render () { + const { formatMessage } = this.props.intl + return ( +
    + + {_('newNetworkInterface')} + + + + +   + + {_('newNetworkName')} + + + + +   + + {_('newNetworkDescription')} + + + + +   + + {_('newNetworkVlan')} + + + + +   + + {_('newNetworkMtu')} + + + + +
    + ) + } +} +export default injectIntl(CreateNetworkModalBody, { withRef: true }) diff --git a/packages/xo-web/src/common/xo/index.js b/packages/xo-web/src/common/xo/index.js new file mode 100644 index 000000000..4cd9b37ac --- /dev/null +++ b/packages/xo-web/src/common/xo/index.js @@ -0,0 +1,2193 @@ +import asap from 'asap' +import cookies from 'cookies-js' +import fpSortBy from 'lodash/fp/sortBy' +import React from 'react' +import URL from 'url-parse' +import Xo from 'xo-lib' +import { createBackoff } from 'jsonrpc-websocket-client' +import { + assign, + filter, + forEach, + includes, + isEmpty, + isEqual, + map, + once, + size, + sortBy, + throttle, +} from 'lodash' +import { lastly, reflect, tap } from 'promise-toolbox' +import { forbiddenOperation, noHostsAvailable } from 'xo-common/api-errors' + +import _ from '../intl' +import invoke from '../invoke' +import logError from '../log-error' +import store from 'store' +import { alert, chooseAction, confirm } from '../modal' +import { error, info, success } from '../notification' +import { getObject } from 'selectors' +import { post } from '../fetch' +import { noop, resolveId, resolveIds } from '../utils' +import { + connected, + disconnected, + signedIn, + signedOut, + updateObjects, + updatePermissions, +} from '../store/actions' + +// =================================================================== + +export const XEN_DEFAULT_CPU_WEIGHT = 256 +export const XEN_DEFAULT_CPU_CAP = 0 + +// =================================================================== + +export const XEN_VIDEORAM_VALUES = [1, 2, 4, 8, 16] + +// =================================================================== + +export const isSrWritable = sr => sr && sr.content_type !== 'iso' && sr.size > 0 +export const isSrShared = sr => sr && sr.shared +export const isVmRunning = vm => vm && vm.power_state === 'Running' + +// =================================================================== + +export const signOut = () => { + cookies.expire('token') + window.location.reload(true) +} + +export const connect = () => { + xo.open(createBackoff()).catch(error => { + logError(error, 'failed to connect to xo-server') + }) +} + +const xo = invoke(() => { + const token = cookies.get('token') + if (!token) { + signOut() + throw new Error('no valid token') + } + + const xo = new Xo({ + credentials: { token }, + }) + + xo.on('authenticationFailure', signOut) + xo.on('scheduledAttempt', ({ delay }) => { + console.warn('next attempt in %s ms', delay) + }) + + xo.on('closed', connect) + + return xo +}) +connect() + +const _signIn = new Promise(resolve => xo.once('authenticated', resolve)) + +const _call = (method, params) => { + let promise = _signIn.then(() => xo.call(method, params)) + + if (process.env.NODE_ENV !== 'production') { + promise = promise::tap(null, error => { + console.error('XO error', { + method, + params, + code: error.code, + message: error.message, + data: error.data, + }) + }) + } + + return promise +} + +// =================================================================== + +export const connectStore = store => { + let updates = {} + const sendUpdates = throttle(() => { + store.dispatch(updateObjects(updates)) + updates = {} + }, 5e2) + + xo.on('open', () => store.dispatch(connected())) + xo.on('closed', () => { + store.dispatch(signedOut()) + store.dispatch(disconnected()) + }) + xo.on('authenticated', () => { + store.dispatch(signedIn(xo.user)) + + _call('xo.getAllObjects').then(objects => + store.dispatch(updateObjects(objects)) + ) + }) + xo.on('notification', notification => { + if (notification.method !== 'all') { + return + } + + assign(updates, notification.params.items) + sendUpdates() + }) + subscribePermissions(permissions => + store.dispatch(updatePermissions(permissions)) + ) + + // work around to keep the user in Redux store up to date + // + // FIXME: store subscriptions data directly in Redux + subscribeUsers(user => { + store.dispatch(signedIn(xo.user)) + }) +} + +// ------------------------------------------------------------------- + +export const resolveUrl = invoke( + xo._url, // FIXME: accessing private prop + baseUrl => to => new URL(to, baseUrl).toString() +) + +// ------------------------------------------------------------------- + +const createSubscription = cb => { + const delay = 5e3 + + const subscribers = Object.create(null) + let cache + let n = 0 + let nextId = 0 + let timeout + + let running = false + + const uninstall = () => { + clearTimeout(timeout) + cache = undefined + } + + const loop = () => { + if (running) { + return + } + + running = true + _signIn.then(() => cb()).then( + result => { + running = false + + if (n === 0) { + return uninstall() + } + + timeout = setTimeout(loop, delay) + + if (!isEqual(result, cache)) { + cache = result + + forEach(subscribers, subscriber => { + // A subscriber might have disappeared during iteration. + // + // E.g.: if a subscriber triggers the subscription of another. + if (subscriber) { + subscriber(result) + } + }) + } + }, + error => { + running = false + + if (n === 0) { + return uninstall() + } + + console.error(error) + } + ) + } + + const subscribe = cb => { + const id = nextId++ + subscribers[id] = cb + + if (n++ !== 0) { + if (cache !== undefined) { + asap(() => cb(cache)) + } + } else { + loop() + } + + return once(() => { + delete subscribers[id] + + if (--n === 0) { + uninstall() + } + }) + } + + subscribe.forceRefresh = () => { + if (n) { + clearTimeout(timeout) + loop() + } + } + + return subscribe +} + +// Subscriptions ----------------------------------------------------- + +export const subscribeCurrentUser = createSubscription(() => xo.refreshUser()) + +export const subscribeAcls = createSubscription(() => _call('acl.get')) + +export const subscribeJobs = createSubscription(() => _call('job.getAll')) + +export const subscribeJobsLogs = createSubscription(() => + _call('log.get', { namespace: 'jobs' }) +) + +export const subscribeApiLogs = createSubscription(() => + _call('log.get', { namespace: 'api' }) +) + +export const subscribePermissions = createSubscription(() => + _call('acl.getCurrentPermissions') +) + +export const subscribePlugins = createSubscription(() => _call('plugin.get')) + +export const subscribeRemotes = createSubscription(() => _call('remote.getAll')) + +export const subscribeResourceSets = createSubscription(() => + _call('resourceSet.getAll') +) + +export const subscribeScheduleTable = createSubscription(() => + _call('scheduler.getScheduleTable') +) + +export const subscribeSchedules = createSubscription(() => + _call('schedule.getAll') +) + +export const subscribeServers = createSubscription( + invoke(fpSortBy('host'), sort => () => _call('server.getAll').then(sort)) +) + +export const subscribeUsers = createSubscription(() => + _call('user.getAll').then(users => { + forEach(users, user => { + user.type = 'user' + }) + + return sortBy(users, 'email') + }) +) + +export const subscribeGroups = createSubscription(() => + _call('group.getAll').then(groups => { + forEach(groups, group => { + group.type = 'group' + }) + + return sortBy(groups, 'name') + }) +) + +export const subscribeRoles = createSubscription( + invoke(sortBy('name'), sort => () => _call('role.getAll').then(sort)) +) + +export const subscribeIpPools = createSubscription(() => _call('ipPool.getAll')) + +export const subscribeResourceCatalog = createSubscription(() => + _call('cloud.getResourceCatalog') +) + +const checkSrCurrentStateSubscriptions = {} +export const subscribeCheckSrCurrentState = (pool, cb) => { + const poolId = resolveId(pool) + + if (!checkSrCurrentStateSubscriptions[poolId]) { + checkSrCurrentStateSubscriptions[poolId] = createSubscription(() => + _call('xosan.checkSrCurrentState', { poolId }) + ) + } + + return checkSrCurrentStateSubscriptions[poolId](cb) +} +subscribeCheckSrCurrentState.forceRefresh = pool => { + if (pool === undefined) { + forEach(checkSrCurrentStateSubscriptions, subscription => + subscription.forceRefresh() + ) + return + } + + const subscription = checkSrCurrentStateSubscriptions[resolveId(pool)] + if (subscription !== undefined) { + subscription.forceRefresh() + } +} + +const missingPatchesByHost = {} +export const subscribeHostMissingPatches = (host, cb) => { + const hostId = resolveId(host) + + if (missingPatchesByHost[hostId] == null) { + missingPatchesByHost[hostId] = createSubscription(() => + getHostMissingPatches(host) + ) + } + + return missingPatchesByHost[hostId](cb) +} +subscribeHostMissingPatches.forceRefresh = host => { + if (host === undefined) { + forEach(missingPatchesByHost, subscription => subscription.forceRefresh()) + return + } + + const subscription = missingPatchesByHost[resolveId(host)] + if (subscription !== undefined) { + subscription.forceRefresh() + } +} + +const volumeInfoBySr = {} +export const subscribeVolumeInfo = ({ sr, infoType }, cb) => { + sr = resolveId(sr) + + if (volumeInfoBySr[sr] == null) { + volumeInfoBySr[sr] = {} + } + + if (volumeInfoBySr[sr][infoType] == null) { + volumeInfoBySr[sr][infoType] = createSubscription(() => + _call('xosan.getVolumeInfo', { sr, infoType }) + ) + } + + return volumeInfoBySr[sr][infoType](cb) +} +subscribeVolumeInfo.forceRefresh = (() => { + const refreshSrVolumeInfo = volumeInfo => { + forEach(volumeInfo, subscription => subscription.forceRefresh()) + } + + return sr => { + if (sr === undefined) { + forEach(volumeInfoBySr, refreshSrVolumeInfo) + } else { + refreshSrVolumeInfo(volumeInfoBySr[sr]) + } + } +})() + +const unhealthyVdiChainsLengthSubscriptionsBySr = {} +export const createSrUnhealthyVdiChainsLengthSubscription = sr => { + sr = resolveId(sr) + let subscription = unhealthyVdiChainsLengthSubscriptionsBySr[sr] + if (subscription === undefined) { + subscription = createSubscription(() => + _call('sr.getUnhealthyVdiChainsLength', { sr }) + ) + unhealthyVdiChainsLengthSubscriptionsBySr[sr] = subscription + } + return subscription +} + +// System ============================================================ + +export const apiMethods = _call('system.getMethodsInfo') + +export const serverVersion = _call('system.getServerVersion') + +export const getXoServerTimezone = _call('system.getServerTimezone') + +// XO -------------------------------------------------------------------------- + +export const importConfig = config => + _call('xo.importConfig').then(({ $sendTo }) => + post($sendTo, config).then(response => { + if (response.status !== 200) { + throw new Error('config import failed') + } + }) + ) + +export const exportConfig = () => + _call('xo.exportConfig').then(({ $getFrom: url }) => { + window.location = `.${url}` + }) + +// Server ------------------------------------------------------------ + +export const addServer = (host, username, password, label) => + _call('server.add', { host, label, password, username })::tap( + subscribeServers.forceRefresh, + () => error(_('serverError'), _('serverAddFailed')) + ) + +export const editServer = (server, props) => + _call('server.set', { ...props, id: resolveId(server) })::tap( + subscribeServers.forceRefresh + ) + +export const connectServer = server => + _call('server.connect', { id: resolveId(server) })::lastly( + subscribeServers.forceRefresh + ) + +export const disconnectServer = server => + _call('server.disconnect', { id: resolveId(server) })::tap( + subscribeServers.forceRefresh + ) + +export const removeServer = server => + _call('server.remove', { id: resolveId(server) })::tap( + subscribeServers.forceRefresh + ) + +// Pool -------------------------------------------------------------- + +export const editPool = (pool, props) => + _call('pool.set', { id: resolveId(pool), ...props }) + +import AddHostModalBody from './add-host-modal' // eslint-disable-line import/first +export const addHostToPool = (pool, host) => { + if (host) { + return confirm({ + icon: 'add', + title: _('addHostModalTitle'), + body: _('addHostModalMessage', { + pool: pool.name_label, + host: host.name_label, + }), + }).then(() => + _call('pool.mergeInto', { + source: host.$pool, + target: pool.id, + force: true, + }) + ) + } + + return confirm({ + icon: 'add', + title: _('addHostModalTitle'), + body: , + }).then(params => { + if (!params.host) { + error(_('addHostNoHost'), _('addHostNoHostMessage')) + return + } + return _call('pool.mergeInto', { + source: params.host.$pool, + target: pool.id, + force: true, + }).catch(error => { + if (error.code !== 'HOSTS_NOT_HOMOGENEOUS') { + throw error + } + + error(_('addHostErrorTitle'), _('addHostNotHomogeneousErrorMessage')) + }) + }, noop) +} + +export const detachHost = host => + confirm({ + icon: 'host-eject', + title: _('detachHostModalTitle'), + body: _('detachHostModalMessage', { + host: {host.name_label}, + }), + }).then(() => _call('host.detach', { host: host.id })) + +export const forgetHost = host => + confirm({ + icon: 'host-forget', + title: _('forgetHostModalTitle'), + body: _('forgetHostModalMessage', { + host: {host.name_label}, + }), + }).then(() => _call('host.forget', { host: resolveId(host) })) + +export const setDefaultSr = sr => + _call('pool.setDefaultSr', { sr: resolveId(sr) }) + +export const setPoolMaster = host => + confirm({ + title: _('setPoolMasterModalTitle'), + body: _('setPoolMasterModalMessage', { + host: {host.name_label}, + }), + }).then(() => _call('pool.setPoolMaster', { host: resolveId(host) }), noop) + +// Host -------------------------------------------------------------- + +export const editHost = (host, props) => + _call('host.set', { ...props, id: resolveId(host) }) + +export const fetchHostStats = (host, granularity) => + _call('host.stats', { host: resolveId(host), granularity }) + +export const restartHost = (host, force = false) => + confirm({ + title: _('restartHostModalTitle'), + body: _('restartHostModalMessage'), + }).then( + () => + _call('host.restart', { id: resolveId(host), force }).catch(error => { + if (noHostsAvailable.is(error)) { + alert( + _('noHostsAvailableErrorTitle'), + _('noHostsAvailableErrorMessage') + ) + } + }), + noop + ) + +export const restartHosts = (hosts, force = false) => { + const nHosts = size(hosts) + return confirm({ + title: _('restartHostsModalTitle', { nHosts }), + body: _('restartHostsModalMessage', { nHosts }), + }).then( + () => + Promise.all( + map(hosts, host => + _call('host.restart', { id: resolveId(host), force })::reflect() + ) + ).then(results => { + const nbErrors = filter(results, result => !result.isFulfilled()).length + if (nbErrors) { + return alert( + _('failHostBulkRestartTitle'), + _('failHostBulkRestartMessage', { + failedHosts: nbErrors, + totalHosts: results.length, + }) + ) + } + }), + noop + ) +} + +export const restartHostAgent = host => + _call('host.restart_agent', { id: resolveId(host) }) + +export const restartHostsAgents = hosts => { + const nHosts = size(hosts) + return confirm({ + title: _('restartHostsAgentsModalTitle', { nHosts }), + body: _('restartHostsAgentsModalMessage', { nHosts }), + }).then(() => Promise.all(map(hosts, restartHostAgent)), noop) +} + +export const startHost = host => _call('host.start', { id: resolveId(host) }) + +export const stopHost = host => + confirm({ + title: _('stopHostModalTitle'), + body: _('stopHostModalMessage'), + }).then(() => _call('host.stop', { id: resolveId(host) }), noop) + +export const stopHosts = hosts => { + const nHosts = size(hosts) + return confirm({ + title: _('stopHostsModalTitle', { nHosts }), + body: _('stopHostsModalMessage', { nHosts }), + }).then( + () => map(hosts, host => _call('host.stop', { id: resolveId(host) })), + noop + ) +} + +export const enableHost = host => _call('host.enable', { id: resolveId(host) }) + +export const disableHost = host => + _call('host.disable', { id: resolveId(host) }) + +export const getHostMissingPatches = host => + _call('host.listMissingPatches', { host: resolveId(host) }).then( + patches => + // Hide paid patches to XS-free users + host.license_params.sku_type !== 'free' + ? patches + : filter(patches, ['paid', false]) + ) + +export const emergencyShutdownHost = host => + _call('host.emergencyShutdownHost', { host: resolveId(host) }) + +export const emergencyShutdownHosts = hosts => { + const nHosts = size(hosts) + return confirm({ + title: _('emergencyShutdownHostsModalTitle', { nHosts }), + body: _('emergencyShutdownHostsModalMessage', { nHosts }), + }).then(() => map(hosts, host => emergencyShutdownHost(host)), noop) +} + +export const installHostPatch = (host, { uuid }) => + _call('host.installPatch', { host: resolveId(host), patch: uuid })::tap(() => + subscribeHostMissingPatches.forceRefresh(host) + ) + +export const installAllHostPatches = host => + _call('host.installAllPatches', { host: resolveId(host) })::tap(() => + subscribeHostMissingPatches.forceRefresh(host) + ) + +export const installAllPatchesOnPool = pool => + _call('pool.installAllPatches', { pool: resolveId(pool) })::tap(() => + subscribeHostMissingPatches.forceRefresh() + ) + +export const installSupplementalPack = (host, file) => { + info( + _('supplementalPackInstallStartedTitle'), + _('supplementalPackInstallStartedMessage') + ) + + return _call('host.installSupplementalPack', { host: resolveId(host) }).then( + ({ $sendTo }) => + post($sendTo, file) + .then(res => { + if (res.status !== 200) { + throw new Error('installing supplemental pack failed') + } + + success( + _('supplementalPackInstallSuccessTitle'), + _('supplementalPackInstallSuccessMessage') + ) + }) + .catch(err => { + error( + _('supplementalPackInstallErrorTitle'), + _('supplementalPackInstallErrorMessage') + ) + throw err + }) + ) +} + +export const installSupplementalPackOnAllHosts = (pool, file) => { + info( + _('supplementalPackInstallStartedTitle'), + _('supplementalPackInstallStartedMessage') + ) + + return _call('pool.installSupplementalPack', { pool: resolveId(pool) }).then( + ({ $sendTo }) => + post($sendTo, file) + .then(res => { + if (res.status !== 200) { + throw new Error('installing supplemental pack failed') + } + + success( + _('supplementalPackInstallSuccessTitle'), + _('supplementalPackInstallSuccessMessage') + ) + }) + .catch(err => { + error( + _('supplementalPackInstallErrorTitle'), + _('supplementalPackInstallErrorMessage') + ) + throw err + }) + ) +} + +// Containers -------------------------------------------------------- + +export const pauseContainer = (vm, container) => + _call('docker.pause', { vm: resolveId(vm), container }) + +export const restartContainer = (vm, container) => + _call('docker.restart', { vm: resolveId(vm), container }) + +export const startContainer = (vm, container) => + _call('docker.start', { vm: resolveId(vm), container }) + +export const stopContainer = (vm, container) => + _call('docker.stop', { vm: resolveId(vm), container }) + +export const unpauseContainer = (vm, container) => + _call('docker.unpause', { vm: resolveId(vm), container }) + +// VM ---------------------------------------------------------------- + +const chooseActionToUnblockForbiddenStartVm = props => + chooseAction({ + icon: 'alarm', + buttons: [ + { label: _('cloneAndStartVM'), value: 'clone', btnStyle: 'success' }, + { label: _('forceStartVm'), value: 'force', btnStyle: 'danger' }, + ], + ...props, + }) + +const cloneAndStartVM = async vm => _call('vm.start', { id: await cloneVm(vm) }) + +export const startVm = vm => + _call('vm.start', { id: resolveId(vm) }).catch(async reason => { + if (!forbiddenOperation.is(reason)) { + throw reason + } + + const choice = await chooseActionToUnblockForbiddenStartVm({ + body: _('blockedStartVmModalMessage'), + title: _('forceStartVmModalTitle'), + }) + + if (choice === 'clone') { + return cloneAndStartVM(vm) + } + + return _call('vm.start', { id: resolveId(vm), force: true }) + }) + +export const startVms = vms => + confirm({ + title: _('startVmsModalTitle', { vms: vms.length }), + body: _('startVmsModalMessage', { vms: vms.length }), + }).then(async () => { + const forbiddenStart = [] + let nErrors = 0 + + await Promise.all( + map(vms, id => + _call('vm.start', { id }).catch(reason => { + if (forbiddenOperation.is(reason)) { + forbiddenStart.push(id) + } else { + nErrors++ + } + }) + ) + ) + + if (forbiddenStart.length === 0) { + if (nErrors === 0) { + return + } + + return error( + _('failedVmsErrorTitle'), + _('failedVmsErrorMessage', { nVms: nErrors }) + ) + } + + const choice = await chooseActionToUnblockForbiddenStartVm({ + body: _('blockedStartVmsModalMessage', { nVms: forbiddenStart.length }), + title: _('forceStartVmModalTitle'), + }).catch(noop) + + if (nErrors !== 0) { + error( + _('failedVmsErrorTitle'), + _('failedVmsErrorMessage', { nVms: nErrors }) + ) + } + + if (choice === 'clone') { + return Promise.all( + map(forbiddenStart, async id => + cloneAndStartVM(getObject(store.getState(), id)) + ) + ) + } + + if (choice === 'force') { + return Promise.all( + map(forbiddenStart, id => _call('vm.start', { id, force: true })) + ) + } + }, noop) + +export const stopVm = (vm, force = false) => + confirm({ + title: _('stopVmModalTitle'), + body: _('stopVmModalMessage', { name: vm.name_label }), + }).then(() => _call('vm.stop', { id: resolveId(vm), force }), noop) + +export const stopVms = (vms, force = false) => + confirm({ + title: _('stopVmsModalTitle', { vms: vms.length }), + body: _('stopVmsModalMessage', { vms: vms.length }), + }).then( + () => map(vms, vm => _call('vm.stop', { id: resolveId(vm), force })), + noop + ) + +export const suspendVm = vm => _call('vm.suspend', { id: resolveId(vm) }) + +export const suspendVms = vms => + confirm({ + title: _('suspendVmsModalTitle', { nVms: vms.length }), + body: _('suspendVmsModalMessage', { nVms: vms.length }), + }).then( + () => + Promise.all(map(vms, vm => _call('vm.suspend', { id: resolveId(vm) }))), + noop + ) + +export const resumeVm = vm => _call('vm.resume', { id: resolveId(vm) }) + +export const recoveryStartVm = vm => + _call('vm.recoveryStart', { id: resolveId(vm) }) + +export const restartVm = (vm, force = false) => + confirm({ + title: _('restartVmModalTitle'), + body: _('restartVmModalMessage', { name: vm.name_label }), + }).then(() => _call('vm.restart', { id: resolveId(vm), force }), noop) + +export const restartVms = (vms, force = false) => + confirm({ + title: _('restartVmsModalTitle', { vms: vms.length }), + body: _('restartVmsModalMessage', { vms: vms.length }), + }).then( + () => + Promise.all( + map(vms, vmId => _call('vm.restart', { id: resolveId(vmId), force })) + ), + noop + ) + +export const cloneVm = ({ id, name_label: nameLabel }, fullCopy = false) => + _call('vm.clone', { + id, + name: `${nameLabel}_clone`, + full_copy: fullCopy, + }) + +import CopyVmModalBody from './copy-vm-modal' // eslint-disable-line import/first +export const copyVm = (vm, sr, name, compress) => { + const vmId = resolveId(vm) + return sr !== undefined + ? confirm({ + title: _('copyVm'), + body: _('copyVmConfirm', { SR: sr.name_label }), + }).then(() => + _call('vm.copy', { + vm: vmId, + sr: sr.id, + name: name || vm.name_label + '_COPY', + compress, + }) + ) + : confirm({ + title: _('copyVm'), + body: , + }).then(params => { + if (!params.sr) { + error('copyVmsNoTargetSr', 'copyVmsNoTargetSrMessage') + return + } + return _call('vm.copy', { vm: vmId, ...params }) + }, noop) +} + +import CopyVmsModalBody from './copy-vms-modal' // eslint-disable-line import/first +export const copyVms = vms => { + const _vms = resolveIds(vms) + return confirm({ + title: _('copyVm'), + body: , + }).then(({ compress, names, sr }) => { + if (sr !== undefined) { + return Promise.all( + map(_vms, (vm, index) => + _call('vm.copy', { vm, sr, compress, name: names[index] }) + ) + ) + } + error(_('copyVmsNoTargetSr'), _('copyVmsNoTargetSrMessage')) + }, noop) +} + +export const convertVmToTemplate = vm => + confirm({ + title: 'Convert to template', + body: ( +
    +

    Are you sure you want to convert this VM into a template?

    +

    This operation is definitive.

    +
    + ), + }).then(() => _call('vm.convert', { id: resolveId(vm) }), noop) + +export const deleteTemplates = templates => + confirm({ + title: _('templateDeleteModalTitle', { templates: templates.length }), + body: _('templateDeleteModalBody', { templates: templates.length }), + }).then( + () => + Promise.all(map(resolveIds(templates), id => _call('vm.delete', { id }))), + noop + ) + +export const snapshotVm = vm => _call('vm.snapshot', { id: resolveId(vm) }) + +export const snapshotVms = vms => + confirm({ + title: _('snapshotVmsModalTitle', { vms: vms.length }), + body: _('snapshotVmsModalMessage', { vms: vms.length }), + }).then(() => map(vms, vmId => snapshotVm({ id: vmId })), noop) + +export const deleteSnapshot = vm => + confirm({ + title: _('deleteSnapshotModalTitle'), + body: _('deleteSnapshotModalMessage'), + }).then(() => _call('vm.delete', { id: resolveId(vm) }), noop) + +export const deleteSnapshots = vms => + confirm({ + title: _('deleteSnapshotsModalTitle', { nVms: vms.length }), + body: _('deleteSnapshotsModalMessage', { nVms: vms.length }), + }).then( + () => + Promise.all(map(vms, vm => _call('vm.delete', { id: resolveId(vm) }))), + noop + ) + +import MigrateVmModalBody from './migrate-vm-modal' // eslint-disable-line import/first +export const migrateVm = (vm, host) => + confirm({ + title: _('migrateVmModalTitle'), + body: , + }).then(params => { + if (!params.targetHost) { + return error( + _('migrateVmNoTargetHost'), + _('migrateVmNoTargetHostMessage') + ) + } + _call('vm.migrate', { vm: vm.id, ...params }) + }, noop) + +import MigrateVmsModalBody from './migrate-vms-modal' // eslint-disable-line import/first +export const migrateVms = vms => + confirm({ + title: _('migrateVmModalTitle'), + body: , + }).then(params => { + if (isEmpty(params.vms)) { + return + } + if (!params.targetHost) { + return error( + _('migrateVmNoTargetHost'), + _('migrateVmNoTargetHostMessage') + ) + } + + const { + mapVmsMapVdisSrs, + mapVmsMapVifsNetworks, + mapVmsMigrationNetwork, + targetHost, + vms, + } = params + Promise.all( + map(vms, ({ id }) => + _call('vm.migrate', { + mapVdisSrs: mapVmsMapVdisSrs[id], + mapVifsNetworks: mapVmsMapVifsNetworks[id], + migrationNetwork: mapVmsMigrationNetwork[id], + targetHost, + vm: id, + }) + ) + ) + }, noop) + +export const createVm = args => _call('vm.create', args) + +export const createVms = (args, nameLabels) => + confirm({ + title: _('newVmCreateVms'), + body: _('newVmCreateVmsConfirm', { nbVms: nameLabels.length }), + }).then( + () => + Promise.all( + map(nameLabels, ( + name_label // eslint-disable-line camelcase + ) => _call('vm.create', { ...args, name_label })) + ), + noop + ) + +export const getCloudInitConfig = template => + _call('vm.getCloudInitConfig', { template }) + +export const deleteVm = (vm, retryWithForce = true) => + confirm({ + title: _('deleteVmModalTitle'), + body: _('deleteVmModalMessage'), + }) + .then(() => _call('vm.delete', { id: resolveId(vm) }), noop) + .catch(error => { + if (forbiddenOperation.is(error) || !retryWithForce) { + throw error + } + + return confirm({ + title: _('deleteVmBlockedModalTitle'), + body: _('deleteVmBlockedModalMessage'), + }).then( + () => _call('vm.delete', { id: resolveId(vm), force: true }), + noop + ) + }) + +export const deleteVms = vms => + confirm({ + title: _('deleteVmsModalTitle', { vms: vms.length }), + body: _('deleteVmsModalMessage', { vms: vms.length }), + strongConfirm: vms.length > 1 && { + messageId: 'deleteVmsConfirmText', + values: { nVms: vms.length }, + }, + }).then( + () => + Promise.all( + map(vms, vmId => _call('vm.delete', { id: resolveId(vmId) })) + ), + noop + ) + +export const importBackup = ({ remote, file, sr }) => + _call('vm.importBackup', resolveIds({ remote, file, sr })) + +export const importDeltaBackup = ({ remote, file, sr, mapVdisSrs }) => + _call( + 'vm.importDeltaBackup', + resolveIds({ + remote, + filePath: file, + sr, + mapVdisSrs: resolveIds(mapVdisSrs), + }) + ) + +import RevertSnapshotModalBody from './revert-snapshot-modal' // eslint-disable-line import/first +export const revertSnapshot = vm => + confirm({ + title: _('revertVmModalTitle'), + body: , + }).then( + snapshotBefore => _call('vm.revert', { id: resolveId(vm), snapshotBefore }), + noop + ) + +export const editVm = (vm, props) => + _call('vm.set', { ...props, id: resolveId(vm) }) + +export const fetchVmStats = (vm, granularity) => + _call('vm.stats', { id: resolveId(vm), granularity }) + +export const importVm = (file, type = 'xva', data = undefined, sr) => { + const { name } = file + + info(_('startVmImport'), name) + + return _call('vm.import', { type, data, sr: resolveId(sr) }).then( + ({ $sendTo }) => + post($sendTo, file) + .then(res => { + if (res.status !== 200) { + throw res.status + } + success(_('vmImportSuccess'), name) + }) + .catch(() => { + error(_('vmImportFailed'), name) + }) + ) +} + +export const importVms = (vms, sr) => + Promise.all( + map(vms, ({ file, type, data }) => + importVm(file, type, data, sr).catch(noop) + ) + ) + +export const exportVm = vm => { + info(_('startVmExport'), vm.id) + return _call('vm.export', { vm: resolveId(vm) }).then(({ $getFrom: url }) => { + window.location = `.${url}` + }) +} + +export const insertCd = (vm, cd, force = false) => + _call('vm.insertCd', { + id: resolveId(vm), + cd_id: resolveId(cd), + force, + }) + +export const ejectCd = vm => _call('vm.ejectCd', { id: resolveId(vm) }) + +export const setVmBootOrder = (vm, order) => + _call('vm.setBootOrder', { + vm: resolveId(vm), + order, + }) + +export const attachDiskToVm = (vdi, vm, { bootable, mode, position }) => + _call('vm.attachDisk', { + bootable, + mode, + position: (position && String(position)) || undefined, + vdi: resolveId(vdi), + vm: resolveId(vm), + }) + +export const createVgpu = (vm, { gpuGroup, vgpuType }) => + _call('vm.createVgpu', resolveIds({ vm, gpuGroup, vgpuType })) + +export const deleteVgpu = vgpu => _call('vm.deleteVgpu', resolveIds({ vgpu })) + +// DISK --------------------------------------------------------------- + +export const createDisk = (name, size, sr, { vm, bootable, mode, position }) => + _call('disk.create', { + bootable, + mode, + name, + position, + size, + sr: resolveId(sr), + vm: resolveId(vm), + }) + +// VDI --------------------------------------------------------------- + +export const editVdi = (vdi, props) => + _call('vdi.set', { ...props, id: resolveId(vdi) }) + +export const deleteVdi = vdi => + confirm({ + title: _('deleteVdiModalTitle'), + body: _('deleteVdiModalMessage'), + }).then(() => _call('vdi.delete', { id: resolveId(vdi) }), noop) + +export const deleteVdis = vdis => + confirm({ + title: _('deleteVdisModalTitle', { nVdis: vdis.length }), + body: _('deleteVdisModalMessage', { nVdis: vdis.length }), + }).then( + () => + Promise.all( + map(vdis, vdi => _call('vdi.delete', { id: resolveId(vdi) })) + ), + noop + ) + +export const deleteOrphanedVdis = vdis => + confirm({ + title: _('removeAllOrphanedObject'), + body: ( +
    +

    {_('removeAllOrphanedModalWarning')}

    +

    {_('definitiveMessageModal')}

    +
    + ), + }).then( + () => Promise.all(map(resolveIds(vdis), id => _call('vdi.delete', { id }))), + noop + ) + +export const migrateVdi = (vdi, sr) => + _call('vdi.migrate', { id: resolveId(vdi), sr_id: resolveId(sr) }) + +// VBD --------------------------------------------------------------- + +export const connectVbd = vbd => _call('vbd.connect', { id: resolveId(vbd) }) + +export const disconnectVbd = vbd => + _call('vbd.disconnect', { id: resolveId(vbd) }) + +export const deleteVbd = vbd => _call('vbd.delete', { id: resolveId(vbd) }) + +export const editVbd = (vbd, props) => + _call('vbd.set', { ...props, id: resolveId(vbd) }) + +export const setBootableVbd = (vbd, bootable) => + _call('vbd.setBootable', { vbd: resolveId(vbd), bootable }) + +// VIF --------------------------------------------------------------- + +export const createVmInterface = (vm, network, mac) => + _call('vm.createInterface', resolveIds({ vm, network, mac })) + +export const connectVif = vif => _call('vif.connect', { id: resolveId(vif) }) + +export const disconnectVif = vif => + _call('vif.disconnect', { id: resolveId(vif) }) + +export const deleteVif = vif => _call('vif.delete', { id: resolveId(vif) }) + +export const deleteVifs = vifs => + confirm({ + title: _('deleteVifsModalTitle', { nVifs: vifs.length }), + body: _('deleteVifsModalMessage', { nVifs: vifs.length }), + }).then( + () => map(vifs, vif => _call('vif.delete', { id: resolveId(vif) })), + noop + ) + +export const setVif = ( + vif, + { network, mac, allowedIpv4Addresses, allowedIpv6Addresses } +) => + _call('vif.set', { + id: resolveId(vif), + network: resolveId(network), + mac, + allowedIpv4Addresses, + allowedIpv6Addresses, + }) + +// Network ----------------------------------------------------------- + +export const editNetwork = (network, props) => + _call('network.set', { ...props, id: resolveId(network) }) + +import CreateNetworkModalBody from './create-network-modal' // eslint-disable-line import/first +export const createNetwork = container => + confirm({ + icon: 'network', + title: _('newNetworkCreate'), + body: , + }).then(params => { + if (!params.name) { + return error( + _('newNetworkNoNameErrorTitle'), + _('newNetworkNoNameErrorMessage') + ) + } + return _call('network.create', params) + }, noop) + +export const getBondModes = () => _call('network.getBondModes') + +import CreateBondedNetworkModalBody from './create-bonded-network-modal' // eslint-disable-line import/first +export const createBondedNetwork = container => + confirm({ + icon: 'network', + title: _('newBondedNetworkCreate'), + body: , + }).then(params => { + if (!params.name) { + return error( + _('newNetworkNoNameErrorTitle'), + _('newNetworkNoNameErrorMessage') + ) + } + return _call('network.createBonded', params) + }, noop) + +export const deleteNetwork = network => + confirm({ + title: _('deleteNetwork'), + body: _('deleteNetworkConfirm'), + }).then(() => _call('network.delete', { network: resolveId(network) }), noop) + +// PIF --------------------------------------------------------------- + +export const connectPif = pif => + confirm({ + title: _('connectPif'), + body: _('connectPifConfirm'), + }).then(() => _call('pif.connect', { pif: resolveId(pif) }), noop) + +export const disconnectPif = pif => + confirm({ + title: _('disconnectPif'), + body: _('disconnectPifConfirm'), + }).then(() => _call('pif.disconnect', { pif: resolveId(pif) }), noop) + +export const deletePif = pif => + confirm({ + title: _('deletePif'), + body: _('deletePifConfirm'), + }).then(() => _call('pif.delete', { pif: resolveId(pif) }), noop) + +export const deletePifs = pifs => + confirm({ + title: _('deletePifs'), + body: _('deletePifsConfirm', { nPifs: pifs.length }), + }).then( + () => + Promise.all( + map(pifs, pif => _call('pif.delete', { pif: resolveId(pif) })) + ), + noop + ) + +export const reconfigurePifIp = (pif, { mode, ip, netmask, gateway, dns }) => + _call('pif.reconfigureIp', { + pif: resolveId(pif), + mode, + ip, + netmask, + gateway, + dns, + }) + +export const getIpv4ConfigModes = () => _call('pif.getIpv4ConfigurationModes') + +export const editPif = (pif, { vlan }) => + _call('pif.editPif', { pif: resolveId(pif), vlan }) + +// SR ---------------------------------------------------------------- + +export const deleteSr = sr => + confirm({ + title: 'Delete SR', + body: ( +
    +

    Are you sure you want to remove this SR?

    +

    This operation is definitive, and ALL DISKS WILL BE LOST FOREVER.

    +
    + ), + }).then(() => _call('sr.destroy', { id: resolveId(sr) }), noop) + +export const forgetSr = sr => + confirm({ + title: _('srForgetModalTitle'), + body: _('srForgetModalMessage'), + }).then(() => _call('sr.forget', { id: resolveId(sr) }), noop) +export const forgetSrs = srs => + confirm({ + title: _('srsForgetModalTitle'), + body: _('srsForgetModalMessage'), + }).then( + () => Promise.all(map(resolveIds(srs), id => _call('sr.forget', { id }))), + noop + ) + +export const reconnectAllHostsSr = sr => + confirm({ + title: _('srReconnectAllModalTitle'), + body: _('srReconnectAllModalMessage'), + }).then(() => _call('sr.connectAllPbds', { id: resolveId(sr) }), noop) +export const reconnectAllHostsSrs = srs => + confirm({ + title: _('srReconnectAllModalTitle'), + body: _('srReconnectAllModalMessage'), + }).then( + () => + Promise.all(resolveIds(srs), id => _call('sr.connectAllPbds', { id })), + noop + ) + +export const disconnectAllHostsSr = sr => + confirm({ + title: _('srDisconnectAllModalTitle'), + body: _('srDisconnectAllModalMessage'), + }).then(() => _call('sr.disconnectAllPbds', { id: resolveId(sr) }), noop) +export const disconnectAllHostsSrs = srs => + confirm({ + title: _('srDisconnectAllModalTitle'), + body: _('srsDisconnectAllModalMessage'), + }).then( + () => + Promise.all(resolveIds(srs), id => _call('sr.disconnectAllPbds', { id })), + noop + ) + +export const editSr = (sr, { nameDescription, nameLabel }) => + _call('sr.set', { + id: resolveId(sr), + name_description: nameDescription, + name_label: nameLabel, + }) + +export const rescanSr = sr => _call('sr.scan', { id: resolveId(sr) }) +export const rescanSrs = srs => + Promise.all(map(resolveIds(srs), id => _call('sr.scan', { id }))) + +// PBDs -------------------------------------------------------------- + +export const connectPbd = pbd => _call('pbd.connect', { id: resolveId(pbd) }) + +export const disconnectPbd = pbd => + _call('pbd.disconnect', { id: resolveId(pbd) }) + +export const deletePbd = pbd => _call('pbd.delete', { id: resolveId(pbd) }) + +// Messages ---------------------------------------------------------- + +export const deleteMessage = message => + _call('message.delete', { id: resolveId(message) }) + +export const deleteMessages = logs => + confirm({ + title: _('logDeleteMultiple', { nLogs: logs.length }), + body: _('logDeleteMultipleMessage', { nLogs: logs.length }), + }).then(() => Promise.all(map(logs, deleteMessage)), noop) + +// Tags -------------------------------------------------------------- + +export const addTag = (object, tag) => + _call('tag.add', { id: resolveId(object), tag }) + +export const removeTag = (object, tag) => + _call('tag.remove', { id: resolveId(object), tag }) + +// Tasks -------------------------------------------------------------- + +export const cancelTask = task => _call('task.cancel', { id: resolveId(task) }) + +export const cancelTasks = tasks => + confirm({ + title: _('cancelTasksModalTitle', { nTasks: tasks.length }), + body: _('cancelTasksModalMessage', { nTasks: tasks.length }), + }).then( + () => + Promise.all( + map(tasks, task => _call('task.cancel', { id: resolveId(task) })) + ), + noop + ) + +export const destroyTask = task => + _call('task.destroy', { id: resolveId(task) }) + +export const destroyTasks = tasks => + confirm({ + title: _('destroyTasksModalTitle', { nTasks: tasks.length }), + body: _('destroyTasksModalMessage', { nTasks: tasks.length }), + }).then( + () => + Promise.all( + map(tasks, task => _call('task.destroy', { id: resolveId(task) })) + ), + noop + ) + +// Jobs ------------------------------------------------------------- + +export const createJob = job => + _call('job.create', { job })::tap(subscribeJobs.forceRefresh) + +export const deleteJob = job => + _call('job.delete', { id: resolveId(job) })::tap(subscribeJobs.forceRefresh) + +export const editJob = job => + _call('job.set', { job })::tap(subscribeJobs.forceRefresh) + +export const getJob = id => _call('job.get', { id }) + +export const runJob = id => { + info(_('runJob'), _('runJobVerbose')) + return _call('job.runSequence', { idSequence: [id] }) +} + +// Backup/Schedule --------------------------------------------------------- + +export const createSchedule = ( + jobId, + { cron, enabled, name = undefined, timezone = undefined } +) => + _call('schedule.create', { jobId, cron, enabled, name, timezone })::tap( + subscribeSchedules.forceRefresh + ) + +export const deleteBackupSchedule = async schedule => { + await confirm({ + title: _('deleteBackupSchedule'), + body: _('deleteBackupScheduleQuestion'), + }) + await _call('schedule.delete', { id: schedule.id }) + await _call('job.delete', { id: schedule.job }) + + subscribeSchedules.forceRefresh() + subscribeJobs.forceRefresh() +} + +export const deleteSchedule = schedule => + _call('schedule.delete', { id: resolveId(schedule) })::tap( + subscribeSchedules.forceRefresh + ) + +export const deleteSchedules = schedules => + confirm({ + title: _('deleteSchedulesModalTitle', { nSchedules: schedules.length }), + body: _('deleteSchedulesModalMessage', { nSchedules: schedules.length }), + }).then(() => + map(schedules, schedule => + _call('schedule.delete', { id: resolveId(schedule) })::tap( + subscribeSchedules.forceRefresh + ) + ) + ) + +export const disableSchedule = id => + _call('scheduler.disable', { id })::tap(subscribeScheduleTable.forceRefresh) + +export const editSchedule = ({ + id, + job: jobId, + cron, + enabled, + name, + timezone, +}) => + _call('schedule.set', { id, jobId, cron, enabled, name, timezone })::tap( + subscribeSchedules.forceRefresh + ) + +export const enableSchedule = id => + _call('scheduler.enable', { id })::tap(subscribeScheduleTable.forceRefresh) + +export const getSchedule = id => _call('schedule.get', { id }) + +// Plugins ----------------------------------------------------------- + +export const loadPlugin = async id => + _call('plugin.load', { id })::tap(subscribePlugins.forceRefresh, err => + error(_('pluginError'), (err && err.message) || _('unknownPluginError')) + ) + +export const unloadPlugin = id => + _call('plugin.unload', { id })::tap(subscribePlugins.forceRefresh, err => + error(_('pluginError'), (err && err.message) || _('unknownPluginError')) + ) + +export const enablePluginAutoload = id => + _call('plugin.enableAutoload', { id })::tap(subscribePlugins.forceRefresh) + +export const disablePluginAutoload = id => + _call('plugin.disableAutoload', { id })::tap(subscribePlugins.forceRefresh) + +export const configurePlugin = (id, configuration) => + _call('plugin.configure', { id, configuration })::tap( + () => { + info(_('pluginConfigurationSuccess'), _('pluginConfigurationChanges')) + subscribePlugins.forceRefresh() + }, + err => + error( + _('pluginError'), + JSON.stringify(err.data) || _('unknownPluginError') + ) + ) + +export const purgePluginConfiguration = async id => { + await confirm({ + title: _('purgePluginConfiguration'), + body: _('purgePluginConfigurationQuestion'), + }) + await _call('plugin.purgeConfiguration', { id }) + + subscribePlugins.forceRefresh() +} + +export const testPlugin = async (id, data) => _call('plugin.test', { id, data }) + +// Resource set ------------------------------------------------------ + +export const createResourceSet = (name, { subjects, objects, limits } = {}) => + _call('resourceSet.create', { name, subjects, objects, limits })::tap( + subscribeResourceSets.forceRefresh + ) + +export const editResourceSet = ( + id, + { name, subjects, objects, limits, ipPools } = {} +) => + _call('resourceSet.set', { + id, + name, + subjects, + objects, + limits, + ipPools, + })::tap(subscribeResourceSets.forceRefresh) + +export const deleteResourceSet = async id => { + await confirm({ + title: _('deleteResourceSetWarning'), + body: _('deleteResourceSetQuestion'), + }) + await _call('resourceSet.delete', { id: resolveId(id) }) + + subscribeResourceSets.forceRefresh() +} + +export const recomputeResourceSetsLimits = () => + _call('resourceSet.recomputeAllLimits') + +// Remote ------------------------------------------------------------ + +export const getRemote = remote => + _call('remote.get', resolveIds({ id: remote }))::tap(null, err => + error(_('getRemote'), err.message || String(err)) + ) + +export const createRemote = (name, url) => + _call('remote.create', { name, url })::tap(subscribeRemotes.forceRefresh) + +export const deleteRemote = remote => + _call('remote.delete', { id: resolveId(remote) })::tap( + subscribeRemotes.forceRefresh + ) + +export const deleteRemotes = remotes => + confirm({ + title: _('deleteRemotesModalTitle', { nRemotes: remotes.length }), + body: _('deleteRemotesModalMessage', { nRemotes: remotes.length }), + }).then( + () => + Promise.all( + map(remotes, remote => + _call('remote.delete', { id: resolveId(remote) }) + ) + )::tap(subscribeRemotes.forceRefresh), + noop + ) + +export const enableRemote = remote => + _call('remote.set', { id: resolveId(remote), enabled: true })::tap( + subscribeRemotes.forceRefresh + ) + +export const disableRemote = remote => + _call('remote.set', { id: resolveId(remote), enabled: false })::tap( + subscribeRemotes.forceRefresh + ) + +export const editRemote = (remote, { name, url }) => + _call('remote.set', resolveIds({ remote, name, url }))::tap( + subscribeRemotes.forceRefresh + ) + +export const listRemote = remote => + _call('remote.list', resolveIds({ id: remote }))::tap( + subscribeRemotes.forceRefresh, + err => error(_('listRemote'), err.message || String(err)) + ) + +export const listRemoteBackups = remote => + _call('backup.list', resolveIds({ remote }))::tap(null, err => + error(_('listRemote'), err.message || String(err)) + ) + +export const testRemote = remote => + _call('remote.test', resolveIds({ id: remote }))::tap(null, err => + error(_('testRemote'), err.message || String(err)) + ) + +// File restore ---------------------------------------------------- + +export const scanDisk = (remote, disk) => + _call('backup.scanDisk', resolveIds({ remote, disk })) + +export const scanFiles = (remote, disk, path, partition) => + _call('backup.scanFiles', resolveIds({ remote, disk, path, partition })) + +export const fetchFiles = (remote, disk, partition, paths, format) => + _call( + 'backup.fetchFiles', + resolveIds({ remote, disk, partition, paths, format }) + ).then(({ $getFrom: url }) => { + window.location = `.${url}` + }) + +// ------------------------------------------------------------------- + +export const probeSrNfs = (host, server) => + _call('sr.probeNfs', { host, server }) + +export const probeSrNfsExists = (host, server, serverPath) => + _call('sr.probeNfsExists', { host, server, serverPath }) + +export const probeSrIscsiIqns = ( + host, + target, + port = undefined, + chapUser = undefined, + chapPassword +) => { + const params = { host, target } + port && (params.port = port) + chapUser && (params.chapUser = chapUser) + chapPassword && (params.chapPassword = chapPassword) + return _call('sr.probeIscsiIqns', params) +} + +export const probeSrIscsiLuns = ( + host, + target, + targetIqn, + chapUser = undefined, + chapPassword +) => { + const params = { host, target, targetIqn } + chapUser && (params.chapUser = chapUser) + chapPassword && (params.chapPassword = chapPassword) + return _call('sr.probeIscsiLuns', params) +} + +export const probeSrIscsiExists = ( + host, + target, + targetIqn, + scsiId, + port = undefined, + chapUser = undefined, + chapPassword +) => { + const params = { host, target, targetIqn, scsiId } + port && (params.port = port) + chapUser && (params.chapUser = chapUser) + chapPassword && (params.chapPassword = chapPassword) + return _call('sr.probeIscsiExists', params) +} + +export const reattachSr = (host, uuid, nameLabel, nameDescription, type) => + _call('sr.reattach', { host, uuid, nameLabel, nameDescription, type }) + +export const reattachSrIso = (host, uuid, nameLabel, nameDescription, type) => + _call('sr.reattachIso', { host, uuid, nameLabel, nameDescription, type }) + +export const createSrNfs = ( + host, + nameLabel, + nameDescription, + server, + serverPath, + nfsVersion = undefined +) => { + const params = { host, nameLabel, nameDescription, server, serverPath } + nfsVersion && (params.nfsVersion = nfsVersion) + return _call('sr.createNfs', params) +} + +export const createSrIscsi = ( + host, + nameLabel, + nameDescription, + target, + targetIqn, + scsiId, + port = undefined, + chapUser = undefined, + chapPassword = undefined +) => { + const params = { host, nameLabel, nameDescription, target, targetIqn, scsiId } + port && (params.port = port) + chapUser && (params.chapUser = chapUser) + chapPassword && (params.chapPassword = chapPassword) + return _call('sr.createIscsi', params) +} + +export const createSrIso = ( + host, + nameLabel, + nameDescription, + path, + type, + user = undefined, + password = undefined +) => { + const params = { host, nameLabel, nameDescription, path, type } + user && (params.user = user) + password && (params.password = password) + return _call('sr.createIso', params) +} + +export const createSrLvm = (host, nameLabel, nameDescription, device) => + _call('sr.createLvm', { host, nameLabel, nameDescription, device }) + +// Job logs ---------------------------------------------------------- + +export const deleteJobsLog = id => + _call('log.delete', { namespace: 'jobs', id })::tap( + subscribeJobsLogs.forceRefresh + ) + +// Logs + +export const deleteApiLog = id => + _call('log.delete', { namespace: 'api', id })::tap( + subscribeApiLogs.forceRefresh + ) + +// Acls, users, groups ---------------------------------------------------------- + +export const addAcl = ({ subject, object, action }) => + _call('acl.add', resolveIds({ subject, object, action }))::tap( + subscribeAcls.forceRefresh, + err => error('Add ACL', err.message || String(err)) + ) + +export const removeAcl = ({ subject, object, action }) => + _call('acl.remove', resolveIds({ subject, object, action }))::tap( + subscribeAcls.forceRefresh, + err => error('Remove ACL', err.message || String(err)) + ) + +export const editAcl = ( + { subject, object, action }, + { + subject: newSubject = subject, + object: newObject = object, + action: newAction = action, + } +) => + _call('acl.remove', resolveIds({ subject, object, action })) + .then(() => + _call( + 'acl.add', + resolveIds({ + subject: newSubject, + object: newObject, + action: newAction, + }) + ) + ) + ::tap(subscribeAcls.forceRefresh, err => + error('Edit ACL', err.message || String(err)) + ) + +export const createGroup = name => + _call('group.create', { name })::tap(subscribeGroups.forceRefresh, err => + error(_('createGroup'), err.message || String(err)) + ) + +export const setGroupName = (group, name) => + _call('group.set', resolveIds({ group, name }))::tap( + subscribeGroups.forceRefresh + ) + +export const deleteGroup = group => + confirm({ + title: _('deleteGroup'), + body:

    {_('deleteGroupConfirm')}

    , + }).then( + () => + _call('group.delete', resolveIds({ id: group }))::tap( + subscribeGroups.forceRefresh, + err => error(_('deleteGroup'), err.message || String(err)) + ), + noop + ) + +export const removeUserFromGroup = (user, group) => + _call('group.removeUser', resolveIds({ id: group, userId: user }))::tap( + subscribeGroups.forceRefresh, + err => error(_('removeUserFromGroup'), err.message || String(err)) + ) + +export const addUserToGroup = (user, group) => + _call('group.addUser', resolveIds({ id: group, userId: user }))::tap( + subscribeGroups.forceRefresh, + err => error('Add User', err.message || String(err)) + ) + +export const createUser = (email, password, permission) => + _call('user.create', { email, password, permission })::tap( + subscribeUsers.forceRefresh, + err => error('Create user', err.message || String(err)) + ) + +export const deleteUser = user => + confirm({ + title: _('deleteUser'), + body:

    {_('deleteUserConfirm')}

    , + }).then(() => + _call('user.delete', { id: resolveId(user) })::tap( + subscribeUsers.forceRefresh, + err => error(_('deleteUser'), err.message || String(err)) + ) + ) + +export const editUser = (user, { email, password, permission }) => + _call('user.set', { id: resolveId(user), email, password, permission })::tap( + subscribeUsers.forceRefresh + ) + +export const changePassword = (oldPassword, newPassword) => + _call('user.changePassword', { + oldPassword, + newPassword, + }).then( + () => success(_('pwdChangeSuccess'), _('pwdChangeSuccessBody')), + () => error(_('pwdChangeError'), _('pwdChangeErrorBody')) + ) + +const _setUserPreferences = preferences => + _call('user.set', { + id: xo.user.id, + preferences, + })::tap(subscribeCurrentUser.forceRefresh) + +import NewSshKeyModalBody from './new-ssh-key-modal' // eslint-disable-line import/first +export const addSshKey = key => { + const { preferences } = xo.user + const otherKeys = (preferences && preferences.sshKeys) || [] + if (key) { + return _setUserPreferences({ + sshKeys: [...otherKeys, key], + }) + } + return confirm({ + icon: 'ssh-key', + title: _('newSshKeyModalTitle'), + body: , + }).then(newKey => { + if (!newKey.title || !newKey.key) { + error(_('sshKeyErrorTitle'), _('sshKeyErrorMessage')) + return + } + return _setUserPreferences({ + sshKeys: [...otherKeys, newKey], + }) + }, noop) +} + +export const deleteSshKey = key => + confirm({ + title: _('deleteSshKeyConfirm'), + body: _('deleteSshKeyConfirmMessage', { + title: {key.title}, + }), + }).then(() => { + const { preferences } = xo.user + return _setUserPreferences({ + sshKeys: filter( + preferences && preferences.sshKeys, + k => k.key !== resolveId(key) + ), + }) + }, noop) + +export const deleteSshKeys = keys => + confirm({ + title: _('deleteSshKeysConfirm', { nKeys: keys.length }), + body: _('deleteSshKeysConfirmMessage', { + nKeys: keys.length, + }), + }).then(() => { + const { preferences } = xo.user + const keyIds = resolveIds(keys) + return _setUserPreferences({ + sshKeys: filter( + preferences && preferences.sshKeys, + sshKey => !includes(keyIds, sshKey.key) + ), + }) + }, noop) + +// User filters -------------------------------------------------- + +import AddUserFilterModalBody from './add-user-filter-modal' // eslint-disable-line import/first +export const addCustomFilter = (type, value) => { + const { user } = xo + return confirm({ + title: _('saveNewFilterTitle'), + body: , + }).then(name => { + if (name.length === 0) { + return error( + _('saveNewUserFilterErrorTitle'), + _('saveNewUserFilterErrorBody') + ) + } + + const { preferences } = user + const filters = (preferences && preferences.filters) || {} + + return _setUserPreferences({ + filters: { + ...filters, + [type]: { + ...filters[type], + [name]: value, + }, + }, + }) + }) +} + +export const removeCustomFilter = (type, name) => + confirm({ + title: _('removeUserFilterTitle'), + body:

    {_('removeUserFilterBody')}

    , + }).then(() => { + const { user } = xo + const { filters } = user.preferences + + return _setUserPreferences({ + filters: { + ...filters, + [type]: { + ...filters[type], + [name]: undefined, + }, + }, + }) + }) + +export const editCustomFilter = (type, name, { newName = name, newValue }) => { + const { filters } = xo.user.preferences + return _setUserPreferences({ + filters: { + ...filters, + [type]: { + ...filters[type], + [name]: undefined, + [newName]: newValue || filters[type][name], + }, + }, + }) +} + +export const setDefaultHomeFilter = (type, name) => { + const { user } = xo + const { preferences } = user + const defaultFilters = (preferences && preferences.defaultHomeFilters) || {} + + return _setUserPreferences({ + defaultHomeFilters: { + ...defaultFilters, + [type]: name, + }, + }) +} + +// IP pools -------------------------------------------------------------------- + +export const createIpPool = ({ name, ips, networks }) => { + const addresses = {} + forEach(ips, ip => { + addresses[ip] = {} + }) + return _call('ipPool.create', { + name, + addresses, + networks: resolveIds(networks), + })::tap(subscribeIpPools.forceRefresh) +} + +export const deleteIpPool = ipPool => + _call('ipPool.delete', { id: resolveId(ipPool) })::tap( + subscribeIpPools.forceRefresh + ) + +export const setIpPool = (ipPool, { name, addresses, networks }) => + _call('ipPool.set', { + id: resolveId(ipPool), + name, + addresses, + networks: resolveIds(networks), + })::tap(subscribeIpPools.forceRefresh) + +// XO SAN ---------------------------------------------------------------------- + +export const getVolumeInfo = (xosanSr, infoType) => + _call('xosan.getVolumeInfo', { sr: xosanSr, infoType }) + +export const createXosanSR = ({ + template, + pif, + vlan, + srs, + glusterType, + redundancy, + brickSize, + memorySize, + ipRange, +}) => { + const promise = _call('xosan.createSR', { + template, + pif: pif.id, + vlan: String(vlan), + srs: resolveIds(srs), + glusterType, + redundancy: Number.parseInt(redundancy), + brickSize, + memorySize, + ipRange, + }) + + // Force refresh in parallel to get the creation progress sooner + subscribeCheckSrCurrentState.forceRefresh() + + return promise +} + +export const addXosanBricks = (xosansr, lvmsrs, brickSize) => + _call('xosan.addBricks', { xosansr, lvmsrs, brickSize }) + +export const replaceXosanBrick = ( + xosansr, + previousBrick, + newLvmSr, + brickSize, + onSameVM = false +) => + _call( + 'xosan.replaceBrick', + resolveIds({ xosansr, previousBrick, newLvmSr, brickSize, onSameVM }) + ) + +export const removeXosanBricks = (xosansr, bricks) => + _call('xosan.removeBricks', { xosansr, bricks }) + +export const computeXosanPossibleOptions = (lvmSrs, brickSize) => + _call('xosan.computeXosanPossibleOptions', { lvmSrs, brickSize }) + +import InstallXosanPackModal from './install-xosan-pack-modal' // eslint-disable-line import/first +export const downloadAndInstallXosanPack = pool => + confirm({ + title: _('xosanInstallPackTitle', { pool: pool.name_label }), + icon: 'export', + body: , + }).then(pack => + _call('xosan.downloadAndInstallXosanPack', { + id: pack.id, + version: pack.version, + pool: resolveId(pool), + }) + ) + +export const fixHostNotInXosanNetwork = (xosanSr, host) => + _call('xosan.fixHostNotInNetwork', { xosanSr, host }) + +// Licenses -------------------------------------------------------------------- + +export const getLicenses = productId => _call('xoa.getLicenses', { productId }) + +export const getLicense = (productId, boundObjectId) => + _call('xoa.getLicense', { productId, boundObjectId }) + +export const unlockXosan = (licenseId, srId) => + _call('xosan.unlock', { licenseId, sr: srId }) diff --git a/packages/xo-web/src/common/xo/install-xosan-pack-modal/index.js b/packages/xo-web/src/common/xo/install-xosan-pack-modal/index.js new file mode 100644 index 000000000..c5c1c7202 --- /dev/null +++ b/packages/xo-web/src/common/xo/install-xosan-pack-modal/index.js @@ -0,0 +1,130 @@ +import _ from 'intl' +import Component from 'base-component' +import React from 'react' +import { connectStore, compareVersions, isXosanPack } from 'utils' +import { subscribeResourceCatalog, subscribePlugins } from 'xo' +import { + createGetObjectsOfType, + createSelector, + createCollectionWrapper, +} from 'selectors' +import { satisfies as versionSatisfies } from 'semver' +import { every, filter, forEach, map, some } from 'lodash' + +const findLatestPack = (packs, hostsVersions) => { + const checkVersion = version => + every(hostsVersions, hostVersion => versionSatisfies(hostVersion, version)) + + let latestPack = { version: '0' } + forEach(packs, pack => { + const xsVersionRequirement = + pack.requirements && pack.requirements.xenserver + + if ( + pack.type === 'iso' && + compareVersions(pack.version, latestPack.version) > 0 && + (!xsVersionRequirement || checkVersion(xsVersionRequirement)) + ) { + latestPack = pack + } + }) + + if (latestPack.version === '0') { + // No compatible pack was found + return + } + + return latestPack +} + +@connectStore( + () => ({ + hosts: createGetObjectsOfType('host').filter( + createSelector( + (_, { pool }) => pool != null && pool.id, + poolId => + poolId + ? host => + host.$pool === poolId && + !some(host.supplementalPacks, isXosanPack) + : false + ) + ), + }), + { withRef: true } +) +export default class InstallXosanPackModal extends Component { + componentDidMount () { + this._unsubscribePlugins = subscribePlugins(plugins => + this.setState({ plugins }) + ) + this._unsubscribeResourceCatalog = subscribeResourceCatalog(catalog => + this.setState({ catalog }) + ) + } + + componentWillUnmount () { + this._unsubscribePlugins() + this._unsubscribeResourceCatalog() + } + + _getXosanLatestPack = createSelector( + () => this.state.catalog && this.state.catalog.xosan, + createSelector( + () => this.props.hosts, + createCollectionWrapper(hosts => map(hosts, 'version')) + ), + findLatestPack + ) + + _getXosanPacks = createSelector( + () => this.state.catalog && this.state.catalog.xosan, + packs => filter(packs, ({ type }) => type === 'iso') + ) + + get value () { + return this._getXosanLatestPack() + } + + render () { + const { hosts } = this.props + const latestPack = this._getXosanLatestPack() + + return ( +
    + {latestPack ? ( +
    + {_('xosanInstallPackOnHosts')} +
      + {map(hosts, host =>
    • {host.name_label}
    • )} +
    +
    + {_('xosanInstallPack', { + pack: latestPack.name, + version: latestPack.version, + })} +
    +
    + ) : ( +
    + {_('xosanNoPackFound')} +
    + {_('xosanPackRequirements')} +
      + {map(this._getXosanPacks(), ({ name, requirements }, key) => ( +
    • + {_.keyValue( + name, + requirements && requirements.xenserver + ? requirements.xenserver + : '/' + )} +
    • + ))} +
    +
    + )} +
    + ) + } +} diff --git a/packages/xo-web/src/common/xo/migrate-vm-modal/index.css b/packages/xo-web/src/common/xo/migrate-vm-modal/index.css new file mode 100644 index 000000000..013f70502 --- /dev/null +++ b/packages/xo-web/src/common/xo/migrate-vm-modal/index.css @@ -0,0 +1,18 @@ +.listTitle { + font-weight: bold; + font-size: 1.1em +} + +.listItem { + margin-top: 2px; +} + +.block { + padding-bottom: 1em; +} + +.groupBlock { + padding-bottom: 1em; + padding-top: 1em; + border-top: 1px solid #e5e5e5; +} diff --git a/packages/xo-web/src/common/xo/migrate-vm-modal/index.js b/packages/xo-web/src/common/xo/migrate-vm-modal/index.js new file mode 100644 index 000000000..0ae9d17c0 --- /dev/null +++ b/packages/xo-web/src/common/xo/migrate-vm-modal/index.js @@ -0,0 +1,315 @@ +import BaseComponent from 'base-component' +import every from 'lodash/every' +import find from 'lodash/find' +import forEach from 'lodash/forEach' +import map from 'lodash/map' +import React from 'react' +import store from 'store' + +import _ from '../../intl' +import ChooseSrForEachVdisModal from '../choose-sr-for-each-vdis-modal' +import invoke from '../../invoke' +import SingleLineRow from '../../single-line-row' +import { Col } from '../../grid' +import { connectStore, mapPlus, resolveId, resolveIds } from '../../utils' +import { getDefaultNetworkForVif } from '../utils' +import { SelectHost, SelectNetwork } from '../../select-objects' +import { + createGetObjectsOfType, + createPicker, + createSelector, + getObject, +} from '../../selectors' + +import { isSrShared, isSrWritable } from '../' + +import styles from './index.css' + +@connectStore( + () => { + const getVm = (_, props) => props.vm + + const getVbds = createGetObjectsOfType('VBD') + .pick((state, props) => getVm(state, props).$VBDs) + .sort() + + const getVdis = createGetObjectsOfType('VDI').pick( + createSelector(getVbds, vbds => + mapPlus(vbds, (vbd, push) => { + if (!vbd.is_cd_drive && vbd.VDI) { + push(vbd.VDI) + } + }) + ) + ) + + const getVifs = createGetObjectsOfType('VIF') + .pick(createSelector(getVm, vm => vm.VIFs)) + .sort() + + const getPifs = createGetObjectsOfType('PIF') + const getNetworks = createGetObjectsOfType('network') + const getPools = createGetObjectsOfType('pool') + + return { + networks: getNetworks, + pifs: getPifs, + pools: getPools, + vbds: getVbds, + vdis: getVdis, + vifs: getVifs, + } + }, + { withRef: true } +) +export default class MigrateVmModalBody extends BaseComponent { + constructor (props) { + super(props) + + this.state = { + mapVifsNetworks: {}, + targetSrs: {}, + } + + this._getHostPredicate = createSelector( + () => this.props.vm, + ({ $container }) => host => host.id !== $container + ) + + this._getSrPredicate = createSelector( + () => this.state.host, + host => + host + ? sr => + isSrWritable(sr) && + (sr.$container === host.id || sr.$container === host.$pool) + : false + ) + + this._getTargetNetworkPredicate = createSelector( + createPicker(() => this.props.pifs, () => this.state.host.$PIFs), + pifs => { + if (!pifs) { + return false + } + + const networks = {} + forEach(pifs, pif => { + networks[pif.$network] = true + }) + + return network => networks[network.id] + } + ) + + this._getMigrationNetworkPredicate = createSelector( + createPicker(() => this.props.pifs, () => this.state.host.$PIFs), + pifs => { + if (!pifs) { + return false + } + + const networks = {} + forEach(pifs, pif => { + pif.ip && (networks[pif.$network] = true) + }) + + return network => networks[network.id] + } + ) + } + + componentDidMount () { + this._selectHost(this.props.host) + } + + get value () { + return { + mapVdisSrs: resolveIds(this.state.targetSrs.mapVdisSrs), + mapVifsNetworks: this.state.mapVifsNetworks, + migrationNetwork: this.state.migrationNetworkId, + sr: resolveId(this.state.targetSrs.mainSr), + targetHost: this.state.host && this.state.host.id, + } + } + + _getObject (id) { + return getObject(store.getState(), id) + } + + _selectHost = host => { + // No host selected + if (!host) { + this.setState({ + host: undefined, + intraPool: undefined, + }) + return + } + + const { vbds, vm } = this.props + const intraPool = vm.$pool === host.$pool + + // Intra-pool + if (intraPool) { + let doNotMigrateVdis + if (vm.$container === host.id) { + doNotMigrateVdis = true + } else { + const _doNotMigrateVdi = {} + forEach(vbds, vbd => { + if (vbd.VDI != null) { + _doNotMigrateVdi[vbd.VDI] = isSrShared( + this._getObject(this._getObject(vbd.VDI).$SR) + ) + } + }) + doNotMigrateVdis = every(_doNotMigrateVdi) + } + + this.setState({ + doNotMigrateVdis, + host, + intraPool, + mapVifsNetworks: undefined, + migrationNetwork: undefined, + targetSrs: {}, + }) + return + } + + // Inter-pool + const { networks, pifs, vifs } = this.props + const defaultMigrationNetworkId = find( + pifs, + pif => pif.$host === host.id && pif.management + ).$network + + const defaultNetwork = invoke(() => { + // First PIF with an IP. + const pifId = find(host.$PIFs, pif => pifs[pif].ip) + const pif = pifId && pifs[pifId] + + return pif && pif.$network + }) + + const defaultNetworksForVif = {} + forEach(vifs, vif => { + defaultNetworksForVif[vif.id] = + getDefaultNetworkForVif(vif, host, pifs, networks) || defaultNetwork + }) + + this.setState({ + doNotMigrateVdis: false, + host, + intraPool, + mapVifsNetworks: defaultNetworksForVif, + migrationNetworkId: defaultMigrationNetworkId, + targetSrs: {}, + }) + } + + _selectMigrationNetwork = migrationNetwork => + this.setState({ migrationNetworkId: migrationNetwork.id }) + + render () { + const { vdis, vifs, networks } = this.props + const { + doNotMigrateVdis, + host, + intraPool, + mapVifsNetworks, + migrationNetworkId, + targetSrs, + } = this.state + return ( +
    +
    + + {_('migrateVmSelectHost')} + + + + +
    + {host && + !doNotMigrateVdis && ( +
    + + + + + +
    + )} + {intraPool !== undefined && + (!intraPool && ( +
    +
    + + {_('migrateVmSelectMigrationNetwork')} + + + + +
    +
    + + {_('migrateVmSelectNetworks')} + +
    + + + + {_('migrateVmVif')} + + + + + {_('migrateVmNetwork')} + + + + {map(vifs, vif => ( +
    + + + {vif.MAC} ({networks[vif.$network].name_label}) + + + + this.setState({ + mapVifsNetworks: { + ...mapVifsNetworks, + [vif.id]: network.id, + }, + }) + } + predicate={this._getTargetNetworkPredicate()} + value={mapVifsNetworks[vif.id]} + /> + + +
    + ))} +
    +
    + ))} +
    + ) + } +} diff --git a/packages/xo-web/src/common/xo/migrate-vms-modal/index.js b/packages/xo-web/src/common/xo/migrate-vms-modal/index.js new file mode 100644 index 000000000..7f5117265 --- /dev/null +++ b/packages/xo-web/src/common/xo/migrate-vms-modal/index.js @@ -0,0 +1,357 @@ +import BaseComponent from 'base-component' +import every from 'lodash/every' +import flatten from 'lodash/flatten' +import forEach from 'lodash/forEach' +import filter from 'lodash/filter' +import find from 'lodash/find' +import isEmpty from 'lodash/isEmpty' +import map from 'lodash/map' +import mapValues from 'lodash/mapValues' +import React from 'react' +import some from 'lodash/some' +import store from 'store' + +import _ from '../../intl' +import Icon from 'icon' +import invoke from '../../invoke' +import SingleLineRow from '../../single-line-row' +import Tooltip from '../../tooltip' +import { Col } from '../../grid' +import { getDefaultNetworkForVif } from '../utils' +import { SelectHost, SelectNetwork, SelectSr } from '../../select-objects' +import { connectStore } from '../../utils' +import { + createGetObjectsOfType, + createPicker, + createSelector, + getObject, +} from '../../selectors' +import { isSrShared } from 'xo' + +import { isSrWritable } from '../' + +const LINE_STYLE = { paddingBottom: '1em' } + +@connectStore( + () => { + const getNetworks = createGetObjectsOfType('network') + const getPifs = createGetObjectsOfType('PIF') + const getPools = createGetObjectsOfType('pool') + + const getVms = createGetObjectsOfType('VM').pick((_, props) => props.vms) + + const getVbdsByVm = createGetObjectsOfType('VBD') + .pick(createSelector(getVms, vms => flatten(map(vms, vm => vm.$VBDs)))) + .groupBy('VM') + + const getVifsByVM = createGetObjectsOfType('VIF') + .pick(createSelector(getVms, vms => flatten(map(vms, vm => vm.VIFs)))) + .groupBy('$VM') + + return { + networks: getNetworks, + pifs: getPifs, + pools: getPools, + vbdsByVm: getVbdsByVm, + vifsByVm: getVifsByVM, + vms: getVms, + } + }, + { withRef: true } +) +export default class MigrateVmsModalBody extends BaseComponent { + constructor (props) { + super(props) + + this._getHostPredicate = createSelector( + () => this.props.vms, + vms => host => some(vms, vm => host.id !== vm.$container) + ) + + this._getSrPredicate = createSelector( + () => this.state.host, + host => + host + ? sr => + isSrWritable(sr) && + (sr.$container === host.id || sr.$container === host.$pool) + : false + ) + + this._getTargetNetworkPredicate = createSelector( + createPicker(() => this.props.pifs, () => this.state.host.$PIFs), + pifs => { + if (!pifs) { + return false + } + + const networks = {} + forEach(pifs, pif => { + networks[pif.$network] = true + }) + + return network => networks[network.id] + } + ) + + this._getMigrationNetworkPredicate = createSelector( + createPicker(() => this.props.pifs, () => this.state.host.$PIFs), + pifs => { + if (!pifs) { + return false + } + + const networks = {} + forEach(pifs, pif => { + pif.ip && (networks[pif.$network] = true) + }) + + return network => networks[network.id] + } + ) + } + + componentDidMount () { + this._selectHost(this.props.host) + } + + get value () { + const { host } = this.state + const vms = filter(this.props.vms, vm => vm.$container !== host.id) + if (!host || isEmpty(vms)) { + return { vms } + } + const { networks, pifs, vbdsByVm, vifsByVm } = this.props + const { + intraPool, + doNotMigrateVdi, + doNotMigrateVmVdis, + migrationNetworkId, + networkId, + smartVifMapping, + srId, + } = this.state + + // Map VM --> ( Map VDI --> SR ) + const mapVmsMapVdisSrs = {} + forEach(vbdsByVm, (vbds, vm) => { + if (doNotMigrateVmVdis[vm]) { + return + } + const mapVdisSrs = {} + forEach(vbds, vbd => { + const vdi = vbd.VDI + if (!vbd.is_cd_drive && vdi) { + mapVdisSrs[vdi] = + intraPool && doNotMigrateVdi[vdi] ? this._getObject(vdi).SR : srId + } + }) + mapVmsMapVdisSrs[vm] = mapVdisSrs + }) + + const defaultNetwork = + smartVifMapping && + invoke(() => { + // First PIF with an IP. + const pifId = find(host.$PIFs, pif => pifs[pif].ip) + const pif = pifId && pifs[pifId] + + return pif && pif.$network + }) + + // Map VM --> ( Map VIF --> network ) + const mapVmsMapVifsNetworks = {} + forEach(vms, vm => { + if (vm.$pool === host.$pool) { + return + } + const mapVifsNetworks = {} + forEach(vifsByVm[vm.id], vif => { + mapVifsNetworks[vif.id] = smartVifMapping + ? getDefaultNetworkForVif(vif, host, pifs, networks) || defaultNetwork + : networkId + }) + mapVmsMapVifsNetworks[vm.id] = mapVifsNetworks + }) + + // Map VM --> migration network + const mapVmsMigrationNetwork = mapValues( + doNotMigrateVmVdis, + doNotMigrateVdis => (doNotMigrateVdis ? undefined : migrationNetworkId) + ) + + return { + mapVmsMapVdisSrs, + mapVmsMapVifsNetworks, + mapVmsMigrationNetwork, + targetHost: host.id, + vms, + } + } + + _getObject (id) { + return getObject(store.getState(), id) + } + + _selectHost = host => { + if (!host) { + this.setState({ targetHost: undefined }) + return + } + const { pools, pifs } = this.props + const defaultMigrationNetworkId = find( + pifs, + pif => pif.$host === host.id && pif.management + ).$network + const defaultSrId = pools[host.$pool].default_SR + const defaultSrConnectedToHost = some( + host.$PBDs, + pbd => this._getObject(pbd).SR === defaultSrId + ) + const doNotMigrateVmVdis = {} + const doNotMigrateVdi = {} + forEach(this.props.vbdsByVm, (vbds, vm) => { + if (this._getObject(vm).$container === host.id) { + doNotMigrateVmVdis[vm] = true + return + } + const _doNotMigrateVdi = {} + forEach(vbds, vbd => { + if (vbd.VDI != null) { + doNotMigrateVdi[vbd.VDI] = _doNotMigrateVdi[vbd.VDI] = isSrShared( + this._getObject(this._getObject(vbd.VDI).$SR) + ) + } + }) + doNotMigrateVmVdis[vm] = every(_doNotMigrateVdi) + }) + const noVdisMigration = every(doNotMigrateVmVdis) + this.setState({ + defaultSrConnectedToHost, + defaultSrId, + host, + intraPool: every(this.props.vms, vm => vm.$pool === host.$pool), + doNotMigrateVdi, + doNotMigrateVmVdis, + migrationNetworkId: defaultMigrationNetworkId, + networkId: defaultMigrationNetworkId, + noVdisMigration, + smartVifMapping: true, + srId: defaultSrConnectedToHost ? defaultSrId : undefined, + }) + } + _selectMigrationNetwork = migrationNetwork => + this.setState({ migrationNetworkId: migrationNetwork.id }) + _selectNetwork = network => this.setState({ networkId: network.id }) + _selectSr = sr => this.setState({ srId: sr.id }) + _toggleSmartVifMapping = () => + this.setState({ smartVifMapping: !this.state.smartVifMapping }) + + render () { + const { + defaultSrConnectedToHost, + defaultSrId, + host, + intraPool, + migrationNetworkId, + networkId, + noVdisMigration, + smartVifMapping, + srId, + } = this.state + return ( +
    +
    + + {_('migrateVmSelectHost')} + + + + +
    + {intraPool === false && ( +
    + + {_('migrateVmSelectMigrationNetwork')} + + + + +
    + )} + {host && + (!intraPool || !noVdisMigration) && ( +
    + + + {!intraPool + ? _('migrateVmsSelectSr') + : _('migrateVmsSelectSrIntraPool')}{' '} + {(defaultSrId === undefined || !defaultSrConnectedToHost) && ( + + + + )} + + + + + +
    + )} + {host && + !intraPool && ( +
    + + {_('migrateVmsSelectNetwork')} + + + + + + + {' '} + {_('migrateVmsSmartMapping')} + + +
    + )} +
    + ) + } +} diff --git a/packages/xo-web/src/common/xo/new-ssh-key-modal/index.js b/packages/xo-web/src/common/xo/new-ssh-key-modal/index.js new file mode 100644 index 000000000..2ee728a78 --- /dev/null +++ b/packages/xo-web/src/common/xo/new-ssh-key-modal/index.js @@ -0,0 +1,57 @@ +import BaseComponent from 'base-component' +import React from 'react' + +import _ from '../../intl' +import SingleLineRow from '../../single-line-row' +import { Col } from '../../grid' +import getEventValue from '../../get-event-value' + +export default class NewSshKeyModalBody extends BaseComponent { + get value () { + return this.state + } + + _onKeyChange = event => { + const key = getEventValue(event) + const splitKey = key.split(' ') + if (!this.state.title && splitKey.length === 3) { + this.setState({ title: splitKey[2].split('\n')[0] }) + } + this.setState({ key }) + } + + render () { + const { key, title } = this.state + + return ( +
    +
    + + {_('title')} + + + + +
    +
    + + {_('key')} + +