diff --git a/react/data/schema.graphql b/react/data/schema.graphql index 7d9e413b00..2a2c5f4f9d 100644 --- a/react/data/schema.graphql +++ b/react/data/schema.graphql @@ -41,9 +41,20 @@ type Queries { """Added in 24.03.1""" id: String reference: String - architecture: String = "x86_64" + architecture: String = "aarch64" ): Image - images(is_installed: Boolean, is_operation: Boolean): [Image] + images( + """ + Added in 19.09.0. If it is specified, fetch images installed on at least one agent. + """ + is_installed: Boolean + is_operation: Boolean @deprecated(reason: "Deprecated since 24.03.4. This field is ignored if `image_filters` is specified and is not null.") + + """ + Added in 24.03.4. Allowed values are: [operational, customized]. When superuser queries with `customized` option set the resolver will return every customized images (including those not owned by callee). To resolve images owned by user only call `customized_images`. + """ + image_filters: [String] = null + ): [Image] """Added in 24.03.1""" customized_images: [ImageNode] @@ -96,7 +107,7 @@ type Queries { legacy_compute_session(sess_id: String!, domain_name: String, access_key: String): LegacyComputeSession vfolder_host_permissions: PredefinedAtomicPermission endpoint(endpoint_id: UUID!): Endpoint - endpoint_list(limit: Int!, offset: Int!, filter: String, order: String, domain_name: String, group_id: String, access_key: String, project: UUID): EndpointList + endpoint_list(limit: Int!, offset: Int!, filter: String, order: String, domain_name: String, group_id: String, user_uuid: String, project: UUID): EndpointList routing(routing_id: UUID!): Routing routing_list(limit: Int!, offset: Int!, filter: String, order: String, endpoint_id: UUID): RoutingList endpoint_token(token: String!): EndpointToken @@ -222,6 +233,9 @@ scalar UUID type ImageNode implements Node { """The ID of the object""" id: ID! + + """Added in 24.03.4. The undecoded id value stored in DB.""" + row_id: UUID name: String humanized_name: String tag: String @@ -233,6 +247,9 @@ type ImageNode implements Node { size_bytes: BigInt resource_limits: [ResourceLimit] supported_accelerators: [String] + + """Added in 24.03.4. The array of image aliases.""" + aliases: [String] } type KVPair { @@ -888,6 +905,9 @@ type Endpoint implements Item { cluster_mode: String cluster_size: Int open_to_public: Boolean + + """Added in 24.03.5.""" + runtime_variant: RuntimeVariantInfo created_at: DateTime! destroyed_at: DateTime routings: [Routing] @@ -897,6 +917,12 @@ type Endpoint implements Item { errors: [InferenceSessionError!]! } +"""Added in 24.03.5.""" +type RuntimeVariantInfo { + name: String + human_readable_name: String +} + type Routing implements Item { id: ID routing_id: UUID @@ -979,6 +1005,9 @@ type ModelCard implements Node { id: ID! name: String vfolder: VirtualFolder + + """Added in 24.09.0.""" + vfolder_node: VirtualFolderNode author: String """Human readable name of the model.""" @@ -1087,15 +1116,15 @@ type Mutations { rescan_images(registry: String): RescanImages preload_image(references: [String]!, target_agents: [String]!): PreloadImage unload_image(references: [String]!, target_agents: [String]!): UnloadImage - modify_image(architecture: String = "x86_64", props: ModifyImageInput!, target: String!): ModifyImage + modify_image(architecture: String = "aarch64", props: ModifyImageInput!, target: String!): ModifyImage """Added in 24.03.0""" forget_image_by_id(image_id: String!): ForgetImageById - forget_image(architecture: String = "x86_64", reference: String!): ForgetImage + forget_image(architecture: String = "aarch64", reference: String!): ForgetImage """Added in 24.03.1""" untag_image_from_registry(image_id: String!): UntagImageFromRegistry - alias_image(alias: String!, architecture: String = "x86_64", target: String!): AliasImage + alias_image(alias: String!, architecture: String = "aarch64", target: String!): AliasImage dealias_image(alias: String!): DealiasImage clear_images(registry: String): ClearImages create_keypair_resource_policy(name: String!, props: CreateKeyPairResourcePolicyInput!): CreateKeyPairResourcePolicy @@ -1777,12 +1806,22 @@ input ModifyEndpointInput { name: String resource_group: String - """Added in 24.03.4.""" + """ + Added in 24.03.4. Must be set to `/models` when choosing `runtime_variant` other than `CUSTOM` or `CMD`. + """ model_definition_path: String open_to_public: Boolean - """Added in 24.03.4.""" + """ + Added in 24.03.4. MODEL type VFolders are not allowed to be attached to model service session with this option. + """ extra_mounts: [ExtraMountInput] + + """Added in 24.03.5.""" + environ: JSONString + + """Added in 24.03.5.""" + runtime_variant: String } input ImageRefType { @@ -1795,4 +1834,14 @@ input ImageRefType { input ExtraMountInput { vfolder_id: String mount_destination: String + + """ + Added in 24.03.4. Set bind type of this mount. Shoud be one of (volume,bind,tmpfs,k8s-generic,k8s-hostpath). Default is 'bind'. + """ + type: String + + """ + Added in 24.03.4. Set permission of this mount. Should be one of (ro,rw,wd). Default is null + """ + permission: String } diff --git a/react/src/components/ModelCardModal.tsx b/react/src/components/ModelCardModal.tsx index e1d2849cd6..16e1d64031 100644 --- a/react/src/components/ModelCardModal.tsx +++ b/react/src/components/ModelCardModal.tsx @@ -4,12 +4,7 @@ import Flex from './Flex'; import ModelCloneModal from './ModelCloneModal'; import ResourceNumber from './ResourceNumber'; import { ModelCardModalFragment$key } from './__generated__/ModelCardModalFragment.graphql'; -import { - BankOutlined, - CopyOutlined, - DownloadOutlined, - FileOutlined, -} from '@ant-design/icons'; +import { BankOutlined, CopyOutlined, FileOutlined } from '@ant-design/icons'; import { Button, Card, @@ -69,9 +64,10 @@ const ModelCardModal: React.FC = ({ architecture framework vfolder { - name cloneable - host + } + vfolder_node { + ...ModelCloneModalVFolderFragment } } `, @@ -142,7 +138,7 @@ const ModelCardModal: React.FC = ({ )} - + */} @@ -307,8 +303,7 @@ const ModelCardModal: React.FC = ({ { diff --git a/react/src/components/ModelCloneModal.tsx b/react/src/components/ModelCloneModal.tsx index 1c75627704..4e67753ac4 100644 --- a/react/src/components/ModelCloneModal.tsx +++ b/react/src/components/ModelCloneModal.tsx @@ -1,34 +1,59 @@ import { useSuspendedBackendaiClient } from '../hooks'; import { useTanMutation } from '../hooks/reactQueryAlias'; +import { useSetBAINotification } from '../hooks/useBAINotification'; import { usePainKiller } from '../hooks/usePainKiller'; import BAIModal, { BAIModalProps } from './BAIModal'; import Flex from './Flex'; import StorageSelect from './StorageSelect'; +import { ModelCloneModalVFolderFragment$key } from './__generated__/ModelCloneModalVFolderFragment.graphql'; import { Alert, Form, FormInstance, + FormItemProps, Input, Select, Switch, message, } from 'antd'; -import { useRef } from 'react'; +import graphql from 'babel-plugin-relay/macro'; +import { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useFragment } from 'react-relay'; interface ModelCloneModalProps extends BAIModalProps { - sourceFolderName: string; - sourceFolderHost: string; + vfolderNode: ModelCloneModalVFolderFragment$key | null; } const ModelCloneModal: React.FC = ({ - sourceFolderName, - sourceFolderHost, + // sourceFolderName, + // sourceFolderHost, + vfolderNode, ...props }) => { const { t } = useTranslation(); const baiClient = useSuspendedBackendaiClient(); - const formRef = useRef(null); + const vfolder = useFragment( + graphql` + fragment ModelCloneModalVFolderFragment on VirtualFolderNode { + id + name + host + } + `, + vfolderNode, + ); + const formRef = useRef< + FormInstance<{ + target_name: string; + target_host: string; + permission: string; + // type: string; + // project: string; + usage_mode: string; + }> + >(null); const painKiller = usePainKiller(); + const { upsertNotification } = useSetBAINotification(); // const { data: allowed_vfolder_types } = useTanQuery({ // queryKey: ['modelCloneModal', 'vfolder_allowed_types'], @@ -37,7 +62,20 @@ const ModelCloneModal: React.FC = ({ // }, // }); - const mutationToClone = useTanMutation({ + const [extraNameError, setExtraNameError] = useState< + Pick + >({}); + + const mutationToClone = useTanMutation< + { + bgtask_id: string; + }, + { type?: string; title?: string; message?: string }, + { + input: any; + name: string; + } + >({ // @ts-ignore mutationFn: ({ input, name }: { input: any; name: string }) => { return baiClient.vfolder.clone(input, name); @@ -50,31 +88,56 @@ const ModelCloneModal: React.FC = ({ {...props} okText={t('button.Clone')} confirmLoading={mutationToClone.isLoading} - onOk={() => { + onOk={(e) => { formRef.current ?.validateFields() .then((values) => { - mutationToClone.mutate( - { - input: values, - name: sourceFolderName, - }, - { - onSuccess(data) { - message.info({ - content: t('modelStore.CloneSuccess'), - }); - props.onOk?.(data); + if (vfolder?.name && vfolder.host) { + mutationToClone.mutate( + { + input: values, + name: vfolder.name, }, - onError(error: any) { - // const title = painKiller.relieve(error?.title); - const messageStr = painKiller.relieve(error?.message); - message.error({ - content: messageStr, - }); + { + onSuccess(data) { + upsertNotification({ + key: 'modelStore.clone.' + vfolder.id, + open: true, + backgroundTask: { + status: 'pending', + percent: 0, + taskId: data.bgtask_id, + statusDescriptions: { + pending: t('data.folders.FolderClonePending'), + resolved: t('data.folders.FolderCloned'), + rejected: t('data.folders.FolderCloneFailed'), + }, + }, + }); + console.log(data); + props.onOk?.(e); + }, + onError(error) { + if ( + error.type === 'https://api.backend.ai/probs/server-error' + ) { + setExtraNameError({ + validateStatus: 'error', + help: t('modelStore.FolderAlreadyExists'), + }); + } else { + const messageStr = painKiller.relieve( + error?.message || '', + ); + message.error({ + content: messageStr, + }); + } + }, }, - }, - ); + ); + } else { + } }) .catch(() => {}); }} @@ -90,16 +153,18 @@ const ModelCloneModal: React.FC = ({ // project: currentProject.id, // type: 'user', usage_mode: 'model', - target_name: sourceFolderName + '_1', - target_host: sourceFolderHost, + target_name: vfolder?.name + '_1', + target_host: vfolder?.host, }} + scrollToFirstError > + {/* */} - + = ({ message: t('data.Allowslettersnumbersand-_dot'), }, ]} + {...extraNameError} > - + { + setExtraNameError({}); + }} + /> void; +} + +const VFolderNameFormItem: React.FC = ({ ...props }) => { + const { t } = useTranslation(); + return ( + + + + ); +}; + +export default VFolderNameFormItem; diff --git a/react/src/pages/ModelStoreListPage.tsx b/react/src/pages/ModelStoreListPage.tsx index 69ebf35814..a97a472011 100644 --- a/react/src/pages/ModelStoreListPage.tsx +++ b/react/src/pages/ModelStoreListPage.tsx @@ -4,7 +4,7 @@ import TextHighlighter from '../components/TextHighlighter'; import { ModelCardModalFragment$key } from '../components/__generated__/ModelCardModalFragment.graphql'; import { useUpdatableState } from '../hooks'; import { ModelStoreListPageQuery } from './__generated__/ModelStoreListPageQuery.graphql'; -import { ReloadOutlined } from '@ant-design/icons'; +import { ReloadOutlined, SearchOutlined } from '@ant-design/icons'; import { Button, Card, Input, List, Select, Tag, theme } from 'antd'; import graphql from 'babel-plugin-relay/macro'; import _ from 'lodash'; @@ -30,7 +30,8 @@ const ModelStoreListPage: React.FC = () => { const { model_cards } = useLazyLoadQuery( graphql` query ModelStoreListPageQuery($filter: String) { - model_cards(filter: $filter) { + # TODO: Implement pagination for model_cards + model_cards(filter: $filter, first: 200) { edges { cursor node { @@ -106,7 +107,8 @@ const ModelStoreListPage: React.FC = () => { gap={'xs'} > - } placeholder={t('modelStore.SearchModels')} allowClear onChange={(e) => { diff --git a/resources/i18n/de.json b/resources/i18n/de.json index 8760552f97..e154147d85 100644 --- a/resources/i18n/de.json +++ b/resources/i18n/de.json @@ -658,7 +658,9 @@ "FolderRestored": "Der Ordner {{ folderName }} wurde wiederhergestellt.", "FolderDeletedForever": "Der Ordner {{ folderName }} wurde endgültig gelöscht.", "MoveToTrashBin": "In den Papierkorb verschieben", - "MovedToTrashBin": "Der Ordner {{ folderName }} wurde in den Papierkorb verschoben." + "MovedToTrashBin": "Der Ordner {{ folderName }} wurde in den Papierkorb verschoben.", + "FolderCloneFailed": "Der Ordner konnte nicht geklont werden.", + "FolderClonePending": "Das Klonen des Ordners ist im Gange." }, "explorer": { "Delete": "Löschen...", @@ -750,7 +752,8 @@ "New": "Neu", "Add": "hinzufügen", "CloningIsOnlyPossibleSameHost": "Derzeit ist das Klonen nur auf demselben Host möglich.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Neuer Ordnername" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Als Ordner klonen", "CloneSuccess": "Die Klonanfrage wurde erfolgreich gesendet.", "CloneInfo": "Er wird als Ihr Benutzertyp-Ordner geklont.", - "category": "Kategorie" + "category": "Kategorie", + "CloneToFolder": "In einen Ordner klonen", + "FolderAlreadyExists": "Ein Ordner mit diesem Namen existiert bereits." }, "table": { "SettingTable": "Sitzordnung bei Tisch", diff --git a/resources/i18n/el.json b/resources/i18n/el.json index 9a1176e578..54e1547f53 100644 --- a/resources/i18n/el.json +++ b/resources/i18n/el.json @@ -658,7 +658,9 @@ "FolderRestored": "Ο φάκελος {{ folderName }} έχει αποκατασταθεί.", "FolderDeletedForever": "Ο φάκελος {{ folderName }} έχει διαγραφεί οριστικά.", "MovedToTrashBin": "Ο φάκελος {{ folderName }} έχει μετακινηθεί στον κάδο απορριμμάτων.", - "MoveToTrashBin": "Μετακίνηση στον κάδο απορριμμάτων" + "MoveToTrashBin": "Μετακίνηση στον κάδο απορριμμάτων", + "FolderCloneFailed": "Απέτυχε η κλωνοποίηση του φακέλου.", + "FolderClonePending": "Η κλωνοποίηση του φακέλου βρίσκεται σε εξέλιξη." }, "explorer": { "Delete": "Διαγράφω...", @@ -750,7 +752,8 @@ "New": "Νέος", "Add": "Προσθέστε", "CloningIsOnlyPossibleSameHost": "Προς το παρόν, η κλωνοποίηση είναι δυνατή μόνο στον ίδιο κεντρικό υπολογιστή.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Νέο όνομα φακέλου" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Clone as Folder", "CloneSuccess": "Η αίτηση κλωνοποίησης έχει σταλεί με επιτυχία.", "CloneInfo": "Θα κλωνοποιηθεί ως φάκελος τύπου χρήστη.", - "category": "Κατηγορία" + "category": "Κατηγορία", + "CloneToFolder": "Κλωνοποίηση σε φάκελο", + "FolderAlreadyExists": "Ένας φάκελος με αυτό το όνομα υπάρχει ήδη." }, "table": { "SettingTable": "Ρύθμιση πίνακα", diff --git a/resources/i18n/en.json b/resources/i18n/en.json index d7ac6323c0..bbbccd2339 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -787,7 +787,9 @@ "FolderRestored": "The {{ folderName }} folder has been restored.", "FolderDeletedForever": "The {{ folderName }} folder has been deleted forever.", "MovedToTrashBin": "The {{ folderName }} folder has been moved to the trash bin.", - "MoveToTrashBin": "Move to trash bin" + "MoveToTrashBin": "Move to trash bin", + "FolderCloneFailed": "Failed to clone the folder.", + "FolderClonePending": "Cloning the folder is in progress." }, "explorer": { "Delete": "Delete", @@ -871,7 +873,8 @@ "New": "New", "Add": "Add", "CloningIsOnlyPossibleSameHost": "Currently, cloning is only possible on the same host.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "New folder name" }, "dialog": { "warning": { @@ -1607,7 +1610,9 @@ "CloneAsFolder": "Clone as Folder", "CloneSuccess": "The clone request has been sent successfully.", "CloneInfo": "It will be cloned as your user type folder.", - "category": "Category" + "category": "Category", + "CloneToFolder": "Clone to a folder", + "FolderAlreadyExists": "A folder with this name already exists." }, "tourguide": { "NeoSessionLauncher": { diff --git a/resources/i18n/es.json b/resources/i18n/es.json index 1869de7e30..65d0e36c02 100644 --- a/resources/i18n/es.json +++ b/resources/i18n/es.json @@ -366,7 +366,9 @@ "DeleteForever": "Borrar para siempre", "Restore": "Restaurar", "FolderRestored": "La carpeta {{ folderName }} ha sido restaurada.", - "FolderDeletedForever": "La carpeta {{ folderName }} se ha eliminado para siempre." + "FolderDeletedForever": "La carpeta {{ folderName }} se ha eliminado para siempre.", + "FolderCloneFailed": "Error al clonar la carpeta.", + "FolderClonePending": "La clonación de la carpeta está en curso." }, "invitation": { "FolderSharingNotAvailableToUser": "El uso compartido de carpetas no está disponible para los usuarios solicitados:", @@ -395,7 +397,8 @@ "New": "Nuevo", "Add": "Añadir", "CloningIsOnlyPossibleSameHost": "Actualmente, la clonación sólo es posible en el mismo host.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Nuevo nombre de carpeta" }, "dialog": { "warning": { @@ -1602,7 +1605,9 @@ "CloneAsFolder": "Clonar como carpeta", "CloneSuccess": "La solicitud de clonación se ha enviado correctamente.", "CloneInfo": "Se clonará como su carpeta de tipo de usuario.", - "category": "Categoría" + "category": "Categoría", + "CloneToFolder": "Clonar en una carpeta", + "FolderAlreadyExists": "Ya existe una carpeta con este nombre." }, "table": { "SettingTable": "Ajuste de la tabla", diff --git a/resources/i18n/fi.json b/resources/i18n/fi.json index 4105812cc5..136878f9ac 100644 --- a/resources/i18n/fi.json +++ b/resources/i18n/fi.json @@ -366,7 +366,9 @@ "DeleteForever": "Poista ikuisesti", "Restore": "Palauttaa", "FolderRestored": "Kansio {{ folderName }} on palautettu.", - "FolderDeletedForever": "Kansio {{ folderName }} on poistettu pysyvästi." + "FolderDeletedForever": "Kansio {{ folderName }} on poistettu pysyvästi.", + "FolderCloneFailed": "Kansiota ei onnistuttu kloonaamaan.", + "FolderClonePending": "Kansion kloonaus on käynnissä." }, "invitation": { "FolderSharingNotAvailableToUser": "Kansioiden jakaminen ei ole käytettävissä pyydetyille käyttäjille:", @@ -395,7 +397,8 @@ "Add": "Lisätä", "CloningIsOnlyPossibleSameHost": "Tällä hetkellä kloonaus on mahdollista vain samassa isännässä.", "New": "Uusi", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Uusi kansion nimi" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "ServiceDetails": "Palvelun tiedot", "StartCommand": "Käynnistä komento", "Task": "Tehtävä", - "Version": "Versio" + "Version": "Versio", + "CloneToFolder": "Kloonaa kansioon", + "FolderAlreadyExists": "Tämän niminen kansio on jo olemassa." }, "table": { "SettingTable": "Kattaus", diff --git a/resources/i18n/fr.json b/resources/i18n/fr.json index 33180b379b..58df9652bf 100644 --- a/resources/i18n/fr.json +++ b/resources/i18n/fr.json @@ -658,7 +658,9 @@ "FolderRestored": "Le dossier {{ folderName }} a été restauré.", "FolderDeletedForever": "Le dossier {{ folderName }} a été supprimé définitivement.", "MoveToTrashBin": "Déplacer vers la poubelle", - "MovedToTrashBin": "Le dossier {{ folderName }} a été déplacé vers la corbeille." + "MovedToTrashBin": "Le dossier {{ folderName }} a été déplacé vers la corbeille.", + "FolderCloneFailed": "Échec du clonage du dossier.", + "FolderClonePending": "Le clonage du dossier est en cours." }, "explorer": { "Delete": "Effacer...", @@ -750,7 +752,8 @@ "New": "Nouveau", "Add": "Ajouter", "CloningIsOnlyPossibleSameHost": "Actuellement, le clonage n'est possible que sur le même hôte.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Nouveau nom de dossier" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Cloner en tant que dossier", "CloneSuccess": "La demande de clone a été envoyée avec succès.", "CloneInfo": "Il sera cloné en tant que dossier de votre type d'utilisateur.", - "category": "Catégorie" + "category": "Catégorie", + "CloneToFolder": "Cloner dans un dossier", + "FolderAlreadyExists": "Un dossier portant ce nom existe déjà." }, "table": { "SettingTable": "Paramètre de table", diff --git a/resources/i18n/id.json b/resources/i18n/id.json index 4b74eddd01..cd383fb937 100644 --- a/resources/i18n/id.json +++ b/resources/i18n/id.json @@ -659,7 +659,9 @@ "FolderRestored": "Folder {{ folderName }} telah dipulihkan.", "FolderDeletedForever": "Folder {{ folderName }} telah dihapus selamanya.", "MovedToTrashBin": "Folder {{ folderName }} telah dipindahkan ke tempat sampah.", - "MoveToTrashBin": "Pindah ke tempat sampah" + "MoveToTrashBin": "Pindah ke tempat sampah", + "FolderCloneFailed": "Gagal mengkloning folder.", + "FolderClonePending": "Kloning folder sedang berlangsung." }, "explorer": { "Delete": "Menghapus...", @@ -751,7 +753,8 @@ "New": "Baru", "Add": "Menambahkan", "CloningIsOnlyPossibleSameHost": "Saat ini, kloning hanya dapat dilakukan pada host yang sama.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Nama folder baru" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Mengkloning sebagai Folder", "CloneSuccess": "Permintaan kloning telah berhasil dikirim.", "CloneInfo": "Ini akan dikloning sebagai folder tipe pengguna Anda.", - "category": "Kategori" + "category": "Kategori", + "CloneToFolder": "Mengkloning ke folder", + "FolderAlreadyExists": "Folder dengan nama ini sudah ada." }, "table": { "SettingTable": "Penataan meja", diff --git a/resources/i18n/it.json b/resources/i18n/it.json index de99ccf7cb..587223837e 100644 --- a/resources/i18n/it.json +++ b/resources/i18n/it.json @@ -659,7 +659,9 @@ "FolderRestored": "La cartella {{ folderName }} è stata ripristinata.", "FolderDeletedForever": "La cartella {{ folderName }} è stata eliminata per sempre.", "MoveToTrashBin": "Sposta nel cestino", - "MovedToTrashBin": "La cartella {{ folderName }} è stata spostata nel cestino." + "MovedToTrashBin": "La cartella {{ folderName }} è stata spostata nel cestino.", + "FolderCloneFailed": "Non è stato possibile clonare la cartella.", + "FolderClonePending": "La clonazione della cartella è in corso." }, "explorer": { "Delete": "Elimina...", @@ -751,7 +753,8 @@ "New": "Nuovo", "Add": "Aggiungi", "CloningIsOnlyPossibleSameHost": "Attualmente la clonazione è possibile solo sullo stesso host.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Nuovo nome della cartella" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Clonare come cartella", "CloneSuccess": "La richiesta di clonazione è stata inviata con successo.", "CloneInfo": "Verrà clonata come cartella del tipo di utente.", - "category": "Categoria" + "category": "Categoria", + "CloneToFolder": "Clonare in una cartella", + "FolderAlreadyExists": "Una cartella con questo nome esiste già." }, "table": { "SettingTable": "Impostazione della tabella", diff --git a/resources/i18n/ja.json b/resources/i18n/ja.json index c8f98ff540..ba4ab05bc7 100644 --- a/resources/i18n/ja.json +++ b/resources/i18n/ja.json @@ -658,7 +658,9 @@ "FolderRestored": "{{ folderName }} フォルダが復元されました。", "FolderDeletedForever": "{{ folderName }} フォルダは完全に削除されました。", "MoveToTrashBin": "ゴミ箱に移動", - "MovedToTrashBin": "{{ folderName }} フォルダーはゴミ箱に移動されました。" + "MovedToTrashBin": "{{ folderName }} フォルダーはゴミ箱に移動されました。", + "FolderCloneFailed": "フォルダのクローンに失敗しました。", + "FolderClonePending": "フォルダのクローンを作成中です。" }, "explorer": { "Delete": "削除...", @@ -750,7 +752,8 @@ "New": "新しい", "Add": "追加", "CloningIsOnlyPossibleSameHost": "現在、クローン作成は同じホスト上でのみ可能です。", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "新しいフォルダ名" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "フォルダとしてクローン", "CloneSuccess": "クローン要求は正常に送信された。", "CloneInfo": "ユーザータイプのフォルダとしてクローンされます。", - "category": "カテゴリー" + "category": "カテゴリー", + "CloneToFolder": "フォルダへのクローン", + "FolderAlreadyExists": "この名前のフォルダはすでに存在する。" }, "table": { "SettingTable": "テーブルセッティング", diff --git a/resources/i18n/ko.json b/resources/i18n/ko.json index 411b34172d..dc27f09a4e 100644 --- a/resources/i18n/ko.json +++ b/resources/i18n/ko.json @@ -511,7 +511,7 @@ "Unset": "초기화", "OK": "확인", "Download": "다운로드", - "Clone": "복사", + "Clone": "복제", "Config": "설정", "Next": "다음", "Previous": "이전", @@ -775,7 +775,9 @@ "FolderRestored": "{{ folderName }} 폴더가 복원되었습니다.", "FolderDeletedForever": "{{ folderName }} 폴더가 완전히 삭제되었습니다.", "MoveToTrashBin": "휴지통으로 이동", - "MovedToTrashBin": "{{ folderName }} 폴더가 휴지통으로 이동되었습니다." + "MovedToTrashBin": "{{ folderName }} 폴더가 휴지통으로 이동되었습니다.", + "FolderCloneFailed": "폴더 복제에 실패하였습니다.", + "FolderClonePending": "폴더 복제가 진행중입니다." }, "explorer": { "Delete": "삭제", @@ -858,7 +860,8 @@ "New": "새로운", "Add": "추가", "CloningIsOnlyPossibleSameHost": "현재 폴더 복사는 동일한 호스트간에만 가능합니다.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "새 폴더 이름" }, "dialog": { "warning": { @@ -1599,9 +1602,11 @@ "Category": "카테고리", "Label": "레이블", "Task": "작업", - "CloneAsFolder": "새 폴더로 복사", + "CloneAsFolder": "새 폴더로 복제", "CloneSuccess": "복사 요청이 성공적으로 보내졌습니다.", - "CloneInfo": "새로운 사용자 폴더로 복사됩니다." + "CloneInfo": "새로운 사용자 폴더로 복제됩니다.", + "CloneToFolder": "폴더로 복제", + "FolderAlreadyExists": "같은 이름의 폴더가 존재합니다." }, "table": { "SettingTable": "테이블 설정", diff --git a/resources/i18n/mn.json b/resources/i18n/mn.json index b2b980f3ee..97ed30c016 100644 --- a/resources/i18n/mn.json +++ b/resources/i18n/mn.json @@ -660,7 +660,9 @@ "FolderRestored": "{{ folderName }} хавтас сэргээгдсэн.", "FolderDeletedForever": "{{ folderName }} хавтас бүрмөсөн устгагдсан.", "MovedToTrashBin": "{{ folderName }} фолдерыг хогийн сав руу зөөв.", - "MoveToTrashBin": "Хогийн сав руу шилжүүлнэ үү" + "MoveToTrashBin": "Хогийн сав руу шилжүүлнэ үү", + "FolderCloneFailed": "Фолдерыг хуулбарлаж чадсангүй.", + "FolderClonePending": "Фолдерыг хуулбарлаж байна." }, "explorer": { "Delete": "Устгах ...", @@ -752,7 +754,8 @@ "New": "Шинэ", "Add": "Нэмэх", "CloningIsOnlyPossibleSameHost": "Одоогоор зөвхөн нэг хост дээр клон хийх боломжтой.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Шинэ фолдерын нэр" }, "dialog": { "warning": { @@ -1601,7 +1604,9 @@ "CloneAsFolder": "Хавтас болгон хуваах", "CloneSuccess": "Хувилах хүсэлтийг амжилттай илгээлээ.", "CloneInfo": "Үүнийг таны хэрэглэгчийн төрлийн хавтас болгон хувилах болно.", - "category": "Ангилал" + "category": "Ангилал", + "CloneToFolder": "Хавтас руу клон хийх", + "FolderAlreadyExists": "Ийм нэртэй хавтас аль хэдийн байна." }, "table": { "SettingTable": "Хүснэгтийн тохиргоо", diff --git a/resources/i18n/ms.json b/resources/i18n/ms.json index 7d57f4f9b3..616fafb89b 100644 --- a/resources/i18n/ms.json +++ b/resources/i18n/ms.json @@ -658,7 +658,9 @@ "DeleteForever": "Padam selama-lamanya", "Restore": "Pulihkan", "FolderRestored": "Folder {{ folderName }} telah dipulihkan.", - "FolderDeletedForever": "Folder {{ folderName }} telah dipadamkan selama-lamanya." + "FolderDeletedForever": "Folder {{ folderName }} telah dipadamkan selama-lamanya.", + "FolderCloneFailed": "Gagal mengklon folder.", + "FolderClonePending": "Pengklonan folder sedang dijalankan." }, "explorer": { "Delete": "Padamkan ...", @@ -750,7 +752,8 @@ "New": "Baru", "Add": "Tambah", "CloningIsOnlyPossibleSameHost": "Pada masa ini, pengklonan hanya boleh dilakukan pada hos yang sama.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Nama folder baharu" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Klon sebagai Folder", "CloneSuccess": "Permintaan klon telah berjaya dihantar.", "CloneInfo": "Ia akan diklonkan sebagai folder jenis pengguna anda.", - "category": "kategori" + "category": "kategori", + "CloneToFolder": "Klon ke folder", + "FolderAlreadyExists": "Folder dengan nama ini sudah wujud." }, "table": { "SelectColumnToDisplay": "Pilih untuk lajur untuk dipaparkan", diff --git a/resources/i18n/pl.json b/resources/i18n/pl.json index c288e77a9b..ca8204f3d1 100644 --- a/resources/i18n/pl.json +++ b/resources/i18n/pl.json @@ -658,7 +658,9 @@ "FolderRestored": "Folder {{ folderName }} został przywrócony.", "FolderDeletedForever": "Folder {{ folderName }} został usunięty na zawsze.", "MoveToTrashBin": "Przenieś do kosza", - "MovedToTrashBin": "Folder {{ folderName }} został przeniesiony do kosza." + "MovedToTrashBin": "Folder {{ folderName }} został przeniesiony do kosza.", + "FolderCloneFailed": "Nie udało się sklonować folderu.", + "FolderClonePending": "Klonowanie folderu jest w toku." }, "explorer": { "Delete": "Kasować...", @@ -750,7 +752,8 @@ "New": "Nowy", "Add": "Dodaj", "CloningIsOnlyPossibleSameHost": "Obecnie klonowanie jest możliwe tylko na tym samym hoście.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Nowa nazwa folderu" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Sklonuj jako Folder", "CloneSuccess": "Żądanie klonowania zostało wysłane pomyślnie.", "CloneInfo": "Zostanie on sklonowany jako folder typu użytkownika.", - "category": "Kategoria" + "category": "Kategoria", + "CloneToFolder": "Klonowanie do folderu", + "FolderAlreadyExists": "Folder o tej nazwie już istnieje." }, "table": { "SettingTable": "Nakrycie stołu", diff --git a/resources/i18n/pt-BR.json b/resources/i18n/pt-BR.json index 1bdce1a09c..4256c7011b 100644 --- a/resources/i18n/pt-BR.json +++ b/resources/i18n/pt-BR.json @@ -658,7 +658,9 @@ "FolderRestored": "A pasta {{ folderName }} foi restaurada.", "FolderDeletedForever": "A pasta {{ folderName }} foi excluída definitivamente.", "MoveToTrashBin": "Mover para a lixeira", - "MovedToTrashBin": "A pasta {{ folderName }} foi movida para a lixeira." + "MovedToTrashBin": "A pasta {{ folderName }} foi movida para a lixeira.", + "FolderCloneFailed": "Falha ao clonar a pasta.", + "FolderClonePending": "A clonagem da pasta está a decorrer." }, "explorer": { "Delete": "Excluir...", @@ -750,7 +752,8 @@ "New": "Novo", "Add": "Adicionar", "CloningIsOnlyPossibleSameHost": "Atualmente, a clonagem só é possível no mesmo host.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Novo nome da pasta" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Clonar como Pasta", "CloneSuccess": "O pedido de clone foi enviado com sucesso.", "CloneInfo": "Será clonada como a pasta do seu tipo de utilizador.", - "category": "Categoria" + "category": "Categoria", + "CloneToFolder": "Clonar para uma pasta", + "FolderAlreadyExists": "Já existe uma pasta com este nome." }, "table": { "SettingTable": "Configuração de mesa", diff --git a/resources/i18n/pt.json b/resources/i18n/pt.json index 4f3ebe8fef..d5812acd1f 100644 --- a/resources/i18n/pt.json +++ b/resources/i18n/pt.json @@ -658,7 +658,9 @@ "FolderRestored": "A pasta {{ folderName }} foi restaurada.", "FolderDeletedForever": "A pasta {{ folderName }} foi excluída definitivamente.", "MoveToTrashBin": "Mover para a lixeira", - "MovedToTrashBin": "A pasta {{ folderName }} foi movida para a lixeira." + "MovedToTrashBin": "A pasta {{ folderName }} foi movida para a lixeira.", + "FolderCloneFailed": "Falha ao clonar a pasta.", + "FolderClonePending": "A clonagem da pasta está a decorrer." }, "explorer": { "Delete": "Excluir...", @@ -750,7 +752,8 @@ "New": "Novo", "Add": "Adicionar", "CloningIsOnlyPossibleSameHost": "Atualmente, a clonagem só é possível no mesmo host.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Novo nome da pasta" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Clonar como Pasta", "CloneSuccess": "O pedido de clone foi enviado com sucesso.", "category": "Categoria", - "CloneInfo": "Ele será clonado como sua pasta de tipo de usuário." + "CloneInfo": "Ele será clonado como sua pasta de tipo de usuário.", + "CloneToFolder": "Clonar para uma pasta", + "FolderAlreadyExists": "Já existe uma pasta com este nome." }, "table": { "SettingTable": "Configuração de mesa", diff --git a/resources/i18n/ru.json b/resources/i18n/ru.json index 411cd34e87..dbf78d96cf 100644 --- a/resources/i18n/ru.json +++ b/resources/i18n/ru.json @@ -658,7 +658,9 @@ "FolderRestored": "Папка {{ folderName }} восстановлена.", "FolderDeletedForever": "Папка {{ folderName}} удалена навсегда.", "MoveToTrashBin": "Переместить в мусорное ведро", - "MovedToTrashBin": "Папка {{ folderName }} была перемещена в корзину." + "MovedToTrashBin": "Папка {{ folderName }} была перемещена в корзину.", + "FolderCloneFailed": "Не удалось клонировать папку.", + "FolderClonePending": "Выполняется клонирование папки." }, "explorer": { "Delete": "Удалить...", @@ -750,7 +752,8 @@ "New": "Новый", "Add": "Добавить", "CloningIsOnlyPossibleSameHost": "В настоящее время клонирование возможно только на том же хосте.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Новое имя папки" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "клонировать как папку", "CloneSuccess": "Запрос на клонирование успешно отправлен.", "CloneInfo": "Она будет клонирована как папка вашего типа пользователя.", - "category": "Категория" + "category": "Категория", + "CloneToFolder": "Клонирование в папку", + "FolderAlreadyExists": "Папка с таким именем уже существует." }, "table": { "SettingTable": "Сервировка стола", diff --git a/resources/i18n/tr.json b/resources/i18n/tr.json index 89ef6c935d..ea84a1c963 100644 --- a/resources/i18n/tr.json +++ b/resources/i18n/tr.json @@ -658,7 +658,9 @@ "FolderRestored": "{{ klasörAdı }} klasörü geri yüklendi.", "FolderDeletedForever": "{{ folderName }} klasörü kalıcı olarak silindi.", "MoveToTrashBin": "Çöp kutusuna taşı", - "MovedToTrashBin": "{{ folderName }} klasörü çöp kutusuna taşındı." + "MovedToTrashBin": "{{ folderName }} klasörü çöp kutusuna taşındı.", + "FolderCloneFailed": "Klasör klonlanamadı.", + "FolderClonePending": "Klasörün klonlanması devam ediyor." }, "explorer": { "Delete": "Sil...", @@ -750,7 +752,8 @@ "New": "Yeni", "Add": "Ekle", "CloningIsOnlyPossibleSameHost": "Şu anda klonlama yalnızca aynı ana bilgisayarda mümkündür.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Yeni klasör adı" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Klasör olarak klonla", "CloneSuccess": "Klonlama isteği başarıyla gönderildi.", "CloneInfo": "Kullanıcı tipi klasörünüz olarak klonlanacaktır.", - "category": "Kategori" + "category": "Kategori", + "CloneToFolder": "Bir klasöre klonlama", + "FolderAlreadyExists": "Bu isimde bir klasör zaten mevcut." }, "table": { "SettingTable": "Tablo Ayarı", diff --git a/resources/i18n/vi.json b/resources/i18n/vi.json index bda6b26633..d57868b095 100644 --- a/resources/i18n/vi.json +++ b/resources/i18n/vi.json @@ -658,7 +658,9 @@ "FolderRestored": "Thư mục {{ folderName }} đã được khôi phục.", "FolderDeletedForever": "Thư mục {{ folderName }} đã bị xóa vĩnh viễn.", "MoveToTrashBin": "Di chuyển vào thùng rác", - "MovedToTrashBin": "Thư mục {{ folderName }} đã được chuyển vào thùng rác." + "MovedToTrashBin": "Thư mục {{ folderName }} đã được chuyển vào thùng rác.", + "FolderCloneFailed": "Không sao chép được thư mục.", + "FolderClonePending": "Đang nhân bản thư mục." }, "explorer": { "Delete": "Xóa bỏ...", @@ -750,7 +752,8 @@ "New": "Mới", "Add": "Thêm vào", "CloningIsOnlyPossibleSameHost": "Hiện tại, việc nhân bản chỉ có thể thực hiện được trên cùng một máy chủ.", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "Tên thư mục mới" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "Sao chép dưới dạng Thư mục", "CloneSuccess": "Yêu cầu nhân bản đã được gửi thành công.", "CloneInfo": "Nó sẽ được sao chép thành thư mục loại người dùng của bạn.", - "category": "Loại" + "category": "Loại", + "CloneToFolder": "Sao chép vào một thư mục", + "FolderAlreadyExists": "Một thư mục có tên này đã tồn tại." }, "table": { "SettingTable": "Cài đặt bảng", diff --git a/resources/i18n/zh-CN.json b/resources/i18n/zh-CN.json index a3af896a6a..b72337e5c8 100644 --- a/resources/i18n/zh-CN.json +++ b/resources/i18n/zh-CN.json @@ -658,7 +658,9 @@ "FolderRestored": "{{ folderName }} 文件夹已恢复。", "FolderDeletedForever": "{{ folderName }} 文件夹已被永久删除。", "MoveToTrashBin": "移至垃圾箱", - "MovedToTrashBin": "{{ folderName }} 文件夹已移至垃圾箱。" + "MovedToTrashBin": "{{ folderName }} 文件夹已移至垃圾箱。", + "FolderCloneFailed": "克隆文件夹失败。", + "FolderClonePending": "正在克隆文件夹。" }, "explorer": { "Delete": "删除...", @@ -750,7 +752,8 @@ "New": "新的", "Add": "添加", "CloningIsOnlyPossibleSameHost": "目前,克隆只能在同一主机上进行。", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "新文件夹名称" }, "dialog": { "warning": { @@ -1601,7 +1604,9 @@ "CloneAsFolder": "克隆为文件夹", "CloneSuccess": "克隆请求已成功发送。", "CloneInfo": "它将被克隆为用户类型文件夹。", - "category": "类别" + "category": "类别", + "CloneToFolder": "克隆到文件夹", + "FolderAlreadyExists": "该名称的文件夹已经存在。" }, "table": { "SettingTable": "桌面设置", diff --git a/resources/i18n/zh-TW.json b/resources/i18n/zh-TW.json index d55c426d5c..3d205f0db6 100644 --- a/resources/i18n/zh-TW.json +++ b/resources/i18n/zh-TW.json @@ -658,7 +658,9 @@ "FolderRestored": "{{ folderName }} 資料夾已恢復。", "FolderDeletedForever": "{{ folderName }} 資料夾已永久刪除。", "MoveToTrashBin": "移至垃圾箱", - "MovedToTrashBin": "{{ folderName }} 資料夾已移至垃圾箱。" + "MovedToTrashBin": "{{ folderName }} 資料夾已移至垃圾箱。", + "FolderCloneFailed": "克隆文件夹失败。", + "FolderClonePending": "正在克隆文件夹。" }, "explorer": { "Delete": "刪除...", @@ -750,7 +752,8 @@ "New": "新的", "Add": "添加", "CloningIsOnlyPossibleSameHost": "目前,克隆只能在同一主機上進行。", - "userQuotaScopeId": "Quota Scope ID" + "userQuotaScopeId": "Quota Scope ID", + "NewFolderName": "新文件夹名称" }, "dialog": { "warning": { @@ -1600,7 +1603,9 @@ "CloneAsFolder": "克隆为文件夹", "CloneSuccess": "克隆请求已成功发送。", "CloneInfo": "它将被克隆为用户类型文件夹。", - "category": "類別" + "category": "類別", + "CloneToFolder": "克隆到文件夹", + "FolderAlreadyExists": "该名称的文件夹已经存在。" }, "table": { "SettingTable": "桌面設定",