diff --git a/react/src/App.tsx b/react/src/App.tsx index b9a9e006c..7b445f9b4 100644 --- a/react/src/App.tsx +++ b/react/src/App.tsx @@ -303,7 +303,7 @@ const router = createBrowserRouter([ }, { path: '/session/start', - handle: { labelKey: 'session.launcher.StartNewSession' }, + // handle: { labelKey: 'session.launcher.StartNewSession' }, Component: () => { const { token } = theme.useToken(); return ( diff --git a/react/src/components/DynamicStepInputNumber.tsx b/react/src/components/DynamicStepInputNumber.tsx index c62acf273..fd079680e 100644 --- a/react/src/components/DynamicStepInputNumber.tsx +++ b/react/src/components/DynamicStepInputNumber.tsx @@ -29,7 +29,7 @@ const DynamicInputNumber: React.FC = ({ const [key, updateKey] = useUpdatableState('first'); useEffect(() => { setTimeout(() => { - updateKey(value); + updateKey(value.toString()); }, 0); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/react/src/components/DynamicUnitInputNumberWithSlider.tsx b/react/src/components/DynamicUnitInputNumberWithSlider.tsx index 830a0b1d6..896e148ff 100644 --- a/react/src/components/DynamicUnitInputNumberWithSlider.tsx +++ b/react/src/components/DynamicUnitInputNumberWithSlider.tsx @@ -51,7 +51,7 @@ const DynamicUnitInputNumberWithSlider: React.FC< const [key, updateKey] = useUpdatableState('first'); useEffect(() => { setTimeout(() => { - updateKey(value); + updateKey(value?.toString()); }, 0); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/react/src/components/SessionOwnerSetterCard.tsx b/react/src/components/SessionOwnerSetterCard.tsx index 58ee03994..a47321454 100644 --- a/react/src/components/SessionOwnerSetterCard.tsx +++ b/react/src/components/SessionOwnerSetterCard.tsx @@ -27,14 +27,23 @@ import { useTranslation } from 'react-i18next'; import { fetchQuery, useRelayEnvironment } from 'react-relay'; export interface SessionOwnerSetterFormValues { - owner?: { - email: string; - accesskey: string; - project: string; - resourceGroup: string; - enabled: boolean; - domainName: string; - }; + owner?: + | { + email: string; + accesskey: string; + project: string; + resourceGroup: string; + enabled: true; + domainName: string; + } + | { + email?: string; + accesskey?: string; + project?: string; + resourceGroup?: string; + enabled: false; + domainName?: string; + }; } const SessionOwnerSetterCard: React.FC = (props) => { @@ -74,8 +83,10 @@ const SessionOwnerSetterCard: React.FC = (props) => { enabled: !!fetchingEmail, }); - const ownerKeypairs = data?.keypairs; - const owner = data?.user; + const ownerKeypairs = form.getFieldValue(['owner', 'email']) + ? data?.keypairs + : undefined; + const owner = form.getFieldValue(['owner', 'email']) ? data?.user : undefined; const nonExistentOwner = !isFetching && fetchingEmail && !owner; return ( diff --git a/react/src/components/SessionTemplateModal.tsx b/react/src/components/SessionTemplateModal.tsx index a192695fb..bacbe9b0d 100644 --- a/react/src/components/SessionTemplateModal.tsx +++ b/react/src/components/SessionTemplateModal.tsx @@ -1,11 +1,167 @@ +import { useBackendAIImageMetaData } from '../hooks'; +import { useRecentSessionHistory } from '../hooks/backendai'; +import { + ResourceNumbersOfSession, + SessionLauncherFormValue, +} from '../pages/SessionLauncherPage'; import BAIModal, { BAIModalProps } from './BAIModal'; -import React from 'react'; +import Flex from './Flex'; +import ImageMetaIcon from './ImageMetaIcon'; +import { Divider, Table, Typography } from 'antd'; +import dayjs from 'dayjs'; +import _ from 'lodash'; +import React, { useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; -interface SessionTemplateModalProps extends BAIModalProps {} +interface SessionTemplateModalProps + extends Omit { + onRequestClose: (formValue?: SessionLauncherFormValue) => void; +} const SessionTemplateModal: React.FC = ({ ...modalProps }) => { - return ; + const { t } = useTranslation(); + const [sessionHistory] = useRecentSessionHistory(); + + const [, { getImageAliasName, getBaseVersion }] = useBackendAIImageMetaData(); + + const [selectedHistoryId, setSelectedHistoryId] = useState(); + + const parsedSessionHistory = useMemo(() => { + return _.map(sessionHistory, (history) => { + const params = new URLSearchParams(history.params); + const formValues: SessionLauncherFormValue = JSON.parse( + params.get('formValues') || '{}', + ); + return { + ...history, + ...formValues, + // resourceAllocation: `${history.cpu}CPU ${history.memory}GB`, + }; + }); + }, [sessionHistory]); + + return ( + { + const params = _.find(sessionHistory, { + id: selectedHistoryId, + })?.params; + modalProps.onRequestClose?.( + JSON.parse(new URLSearchParams(params).get('formValues') || '{}'), + ); + }} + onCancel={() => { + // reset + setSelectedHistoryId(undefined); + modalProps.onRequestClose(); + }} + > + + { + setSelectedHistoryId(record.id); + }, + }} + onRow={(record) => ({ + onClick: () => { + setSelectedHistoryId(record.id); + }, + })} + rowKey={(record) => record.id} + columns={[ + // { + // title: t('session.launcher.SessionName'), + // dataIndex: 'sessionName', + // render: (sessionName, record) => { + // return sessionName ?? '-'; + // }, + // }, + { + title: t('general.Image'), + dataIndex: ['environments', 'version'], + render: (version, record) => { + const imageStr = + record.environments.version || record.environments.manual; + return ( + !!imageStr && ( + + + + {getImageAliasName(imageStr)} + + + {getBaseVersion(imageStr)} + + + {record.sessionName ? `(${record.sessionName})` : null} + + + ) + ); + }, + // onCell: () => ({ + // style: { maxWidth: 250, textOverflow: 'ellipsis' }, + // }), + }, + { + title: t('session.launcher.ResourceAllocation'), + dataIndex: 'resource', + render: (resource) => { + // return JSON.stringify(resource) + return ( + + + + ); + }, + }, + // { + // dataIndex: 'mounts', + // render: (value, record) => { + // record.mou + // } + // }, + { + title: t('session.launcher.CreatedAt'), + dataIndex: 'createdAt', + render: (createdAt: string) => { + return dayjs(createdAt).fromNow(); + }, + }, + ]} + /> + {/* Template, + }, + { + key: 'history', + label: t('session.launcher.RecentHistory'), + children: ( + + ), + }, + ]} + /> */} + + ); }; export default SessionTemplateModal; diff --git a/react/src/components/VFolderTable.tsx b/react/src/components/VFolderTable.tsx index 6658a2ad4..81504ce51 100644 --- a/react/src/components/VFolderTable.tsx +++ b/react/src/components/VFolderTable.tsx @@ -31,7 +31,13 @@ import { ColumnsType } from 'antd/lib/table'; import graphql from 'babel-plugin-relay/macro'; import dayjs from 'dayjs'; import _ from 'lodash'; -import React, { useEffect, useMemo, useState, useTransition } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useState, + useTransition, +} from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useLazyLoadQuery } from 'react-relay'; @@ -244,22 +250,23 @@ const VFolderTable: React.FC = ({ * @param input - The input path to be converted. * @returns The aliased path based on the name and input. */ - const inputToAliasPath = useEventNotStable( + const inputToAliasPath = useCallback( (name: VFolderKey, input?: string) => { - if (_.isEmpty(input)) { + if (input === undefined || input === '') { return `${aliasBasePath}${name}`; - } else if (input?.startsWith('/')) { + } else if (input.startsWith('/')) { return input; } else { return `${aliasBasePath}${input}`; } }, + [aliasBasePath], ); const handleAliasUpdate = useEventNotStable(() => { setAliasMap( _.mapValues( - _.pickBy(internalForm.getFieldsValue(), (v) => !!v), //remove empty + _.pickBy(internalForm.getFieldsValue({ strict: false }), (v) => !!v), //remove empty (v, k) => inputToAliasPath(k, v), // add alias base path ), ); @@ -526,7 +533,7 @@ const VFolderTable: React.FC = ({ }} /> -
+
= ({ selectedRowKeys, onChange: (selectedRowKeys) => { setSelectedRowKeys(selectedRowKeys as VFolderKey[]); + handleAliasUpdate(); }, }} showSorterTooltip={false} diff --git a/react/src/components/VFolderTableFormItem.tsx b/react/src/components/VFolderTableFormItem.tsx index 56cb3060f..b8192c93c 100644 --- a/react/src/components/VFolderTableFormItem.tsx +++ b/react/src/components/VFolderTableFormItem.tsx @@ -30,7 +30,6 @@ const VFolderTableFormItem: React.FC = ({ }) => { const form = Form.useFormInstance(); const { t } = useTranslation(); - Form.useWatch('vfoldersAliasMap', form); return ( <> { }); return allowedHosts?.allowed; }; + +export const useRecentSessionHistory = () => { + const [recentSessionHistory, setRecentSessionHistory] = + useBAISettingUserState('recentSessionHistory'); + + const push = useEventNotStable( + ({ + id, + params, + createdAt, + }: SelectivePartial) => { + const newHistory: SessionHistory = { + id: id ?? generateRandomString(8), + params, + createdAt: createdAt ?? new Date().toISOString(), + }; + // push new history to the top of recentSessionHistory and keep it up to 5 + const newRecentSessionHistory = [ + newHistory, + ...(recentSessionHistory || []), + ].slice(0, 5); + setRecentSessionHistory(newRecentSessionHistory); + }, + ); + const clear = useEventNotStable(() => setRecentSessionHistory([])); + const remove = useEventNotStable((id: string) => { + const newRecentSessionHistory = (recentSessionHistory || []).filter( + (item) => item.id !== id, + ); + setRecentSessionHistory(newRecentSessionHistory); + }); + return [ + recentSessionHistory, + { + push, + clear, + remove, + }, + ] as const; +}; diff --git a/react/src/hooks/useBAISetting.tsx b/react/src/hooks/useBAISetting.tsx index c95ef48b9..f0641f3d8 100644 --- a/react/src/hooks/useBAISetting.tsx +++ b/react/src/hooks/useBAISetting.tsx @@ -17,8 +17,16 @@ interface UserSettings { summary_items?: Array>; selected_language?: string; classic_session_launcher?: boolean; + recentSessionHistory?: Array; } +export type SessionHistory = { + id: string; + name?: string; + params: string; + createdAt: string; +}; + interface GeneralSettings { last_login?: number; login_attempt?: number; @@ -28,8 +36,9 @@ interface GeneralSettings { export const useBAISettingUserState = ( name: K, ): [UserSettings[K], (newValue: UserSettings[K]) => void] => { - return useAtom(SettingAtomFamily('user.' + name)); + return useAtom(SettingAtomFamily('user.' + name)); }; + export const useBAISettingGeneralState = ( name: K, ): [GeneralSettings[K], (newValue: GeneralSettings[K]) => void] => { diff --git a/react/src/hooks/useEventNotStable.tsx b/react/src/hooks/useEventNotStable.tsx index 4eedcf182..dbc424c38 100644 --- a/react/src/hooks/useEventNotStable.tsx +++ b/react/src/hooks/useEventNotStable.tsx @@ -1,8 +1,10 @@ import { useCallback, useLayoutEffect, useRef } from 'react'; -export function useEventNotStable(handler: (props: any) => void) { +export function useEventNotStable( + handler: (...args: Args) => Return, +) { // eslint-disable-next-line - const handlerRef = useRef(null); + const handlerRef = useRef(undefined); // In a real implementation, this would run before layout effects useLayoutEffect(() => { @@ -11,9 +13,9 @@ export function useEventNotStable(handler: (props: any) => void) { // eslint-disable-next-line // @ts-ignore - return useCallback((...args) => { + return useCallback((...args: Args) => { // In a real implementation, this would throw if called during render const fn = handlerRef.current; - return fn ? fn(...args) : null; + return fn ? fn(...args) : undefined; }, []); } diff --git a/react/src/pages/SessionLauncherPage.tsx b/react/src/pages/SessionLauncherPage.tsx index f6e1385be..b79dd1d8d 100644 --- a/react/src/pages/SessionLauncherPage.tsx +++ b/react/src/pages/SessionLauncherPage.tsx @@ -31,6 +31,7 @@ import SessionOwnerSetterCard, { SessionOwnerSetterFormValues, } from '../components/SessionOwnerSetterCard'; import { SessionOwnerSetterPreviewCard } from '../components/SessionOwnerSetterCard'; +import SessionTemplateModal from '../components/SessionTemplateModal'; import SourceCodeViewer from '../components/SourceCodeViewer'; import VFolderTableFormItem, { VFolderTableFormValues, @@ -45,7 +46,10 @@ import { useUpdatableState, useWebUINavigate, } from '../hooks'; -import { useCurrentUserRole } from '../hooks/backendai'; +import { + useCurrentUserRole, + useRecentSessionHistory, +} from '../hooks/backendai'; import { useSetBAINotification } from '../hooks/useBAINotification'; import { useCurrentProjectValue } from '../hooks/useCurrentProject'; import { useThemeMode } from '../hooks/useThemeMode'; @@ -59,7 +63,7 @@ import { QuestionCircleOutlined, RightOutlined, } from '@ant-design/icons'; -import { useDebounceFn } from 'ahooks'; +import { useDebounceFn, useToggle } from 'ahooks'; import { Alert, App, @@ -69,7 +73,6 @@ import { Col, Descriptions, Form, - FormInstance, Grid, Input, InputNumber, @@ -92,7 +95,7 @@ import _ from 'lodash'; import React, { useEffect, useMemo, useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { Trans, useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import SyntaxHighlighter from 'react-syntax-highlighter'; import { dark } from 'react-syntax-highlighter/dist/esm/styles/hljs'; import { @@ -223,14 +226,17 @@ const SessionLauncherPage = () => { redirectTo: StringParam, appOption: AppOptionParam, }); + const { search } = useLocation(); const { isDarkMode } = useThemeMode(); - const navigate = useNavigate(); // const { moveTo } = useWebComponentInfo(); const webuiNavigate = useWebUINavigate(); const currentProject = useCurrentProjectValue(); + const [isOpenTemplateModal, { toggle: toggleIsOpenTemplateModal }] = + useToggle(); const { upsertNotification } = useSetBAINotification(); + const [, { push: pushSessionHistory }] = useRecentSessionHistory(); const { run: syncFormToURLWithDebounce } = useDebounceFn( () => { @@ -290,7 +296,13 @@ const SessionLauncherPage = () => { }, []); const mergedInitialValues = useMemo(() => { - return _.merge({}, INITIAL_FORM_VALUES, formValuesFromQueryParams); + return _.merge( + {}, + // bec + // { sessionName: '' } as Partial, + INITIAL_FORM_VALUES, + formValuesFromQueryParams, + ); }, [INITIAL_FORM_VALUES, formValuesFromQueryParams]); // ScrollTo top when step is changed @@ -375,6 +387,7 @@ const SessionLauncherPage = () => { const startSession = () => { // TODO: support inference mode, support import mode setIsStartingSession(true); + const usedSearchParams = search; form .validateFields() .then(async (values) => { @@ -539,58 +552,72 @@ const SessionLauncherPage = () => { open: true, }); await Promise.all(sessionPromises) - .then(([firstSession]) => { - // console.log('##sessionPromises', firstSession); - if ( - values.num_of_sessions === 1 && - values.sessionType !== 'batch' - ) { - const res = firstSession; - let appOptions: AppOption = _.cloneDeep(appOptionFromQueryParams); - if ('kernelId' in res) { - // API v4 - appOptions = _.extend(appOptions, { - 'session-name': res.kernelId, - 'access-key': '', - mode: sessionMode, - // mode: this.mode, - }); - } else { - // API >= v5 - appOptions = _.extend(appOptions, { - 'session-uuid': res.sessionId, - 'session-name': res.sessionName, - 'access-key': '', - mode: sessionMode, - // mode: this.mode, - }); - } - const service_info = res.servicePorts; - if (Array.isArray(service_info) === true) { - appOptions['app-services'] = service_info.map( - (a: { name: string }) => a.name, + .then( + ([firstSession]: Array<{ + kernelId?: string; + sessionId: string; + sessionName: string; + servicePorts: Array<{ name: string }>; + }>) => { + pushSessionHistory({ + id: firstSession.sessionId, + params: usedSearchParams, + name: firstSession.sessionName, + }); + // console.log('##sessionPromises', firstSession); + if ( + values.num_of_sessions === 1 && + values.sessionType !== 'batch' + ) { + const res = firstSession; + let appOptions: AppOption = _.cloneDeep( + appOptionFromQueryParams, ); - } else { - appOptions['app-services'] = []; - } - // TODO: support import and inference - // if (sessionMode === 'import') { - // appOptions['runtime'] = 'jupyter'; - // appOptions['filename'] = this.importFilename; - // } - // if (sessionMode === 'inference') { - // appOptions['runtime'] = appOptions['app-services'].find( - // (element: any) => !['ttyd', 'sshd'].includes(element), - // ); - // } + if ('kernelId' in res) { + // API v4 + appOptions = _.extend(appOptions, { + 'session-name': res.kernelId, + 'access-key': '', + mode: sessionMode, + // mode: this.mode, + }); + } else { + // API >= v5 + appOptions = _.extend(appOptions, { + 'session-uuid': res.sessionId, + 'session-name': res.sessionName, + 'access-key': '', + mode: sessionMode, + // mode: this.mode, + }); + } + const service_info = res.servicePorts; + if (Array.isArray(service_info) === true) { + appOptions['app-services'] = service_info.map( + (a: { name: string }) => a.name, + ); + } else { + appOptions['app-services'] = []; + } + // TODO: support import and inference + // if (sessionMode === 'import') { + // appOptions['runtime'] = 'jupyter'; + // appOptions['filename'] = this.importFilename; + // } + // if (sessionMode === 'inference') { + // appOptions['runtime'] = appOptions['app-services'].find( + // (element: any) => !['ttyd', 'sshd'].includes(element), + // ); + // } - // only launch app when it has valid service ports - if (service_info.length > 0) { - // @ts-ignore - globalThis.appLauncher.showLauncher(appOptions); + // only launch app when it has valid service ports + if (service_info.length > 0) { + // @ts-ignore + globalThis.appLauncher.showLauncher(appOptions); + } } - } - }) + }, + ) .catch(() => { upsertNotification({ key: 'session-launcher:' + sessionName, @@ -649,21 +676,22 @@ const SessionLauncherPage = () => { align="stretch" style={{ flex: 1, maxWidth: 700 }} > - {/* - + + {t('session.launcher.StartNewSession')} - */} + {/* }> */} { @@ -1447,7 +1475,9 @@ const SessionLauncherPage = () => { )} - + {/* {_.chain( form.getFieldValue('allocationPreset') === 'custom' @@ -1502,8 +1532,8 @@ const SessionLauncherPage = () => { title={t('session.launcher.TotalAllocation')} > - { title={t('button.Reset')} description={t('session.launcher.ResetFormConfirm')} onConfirm={() => { + webuiNavigate('/session/start'); form.resetFields(); - - navigate('/session/start'); }} icon={ { )} - {/* { - setSelectedFolderName(undefined); + { + if (formValue) { + const fieldsValue = _.merge( + { + // reset fields related to optional and nested fields + sessionName: '', + ports: [], + mounts: [], + vfoldersAliasMap: {}, + bootstrap_script: '', + num_of_sessions: 1, + owner: { + enabled: false, + accesskey: '', + domainName: '', + email: undefined, + project: '', + resourceGroup: '', + }, + environments: { + manual: '', + }, + batch: { + enabled: false, + command: undefined, + scheduleDate: undefined, + }, + } as Omit< + Required>, + 'autoMountedFolderNames' + >, + formValue, + ); + + if (!_.isEmpty(fieldsValue.sessionName)) { + fieldsValue.sessionName = + fieldsValue.sessionName + '-' + generateRandomString(4); + } + form.setFieldsValue(fieldsValue); + setCurrentStep(steps.length - 1); + form.validateFields().catch(() => {}); + } + toggleIsOpenTemplateModal(); }} - /> */} + open={isOpenTemplateModal} + /> {currentStep === steps.length - 1 ? ( { ); }; -const FormResourceNumbers: React.FC<{ - form: FormInstance; +type FormOrResourceRequired = { + resource: ResourceAllocationFormValue['resource']; containerCount?: number; -}> = ({ form, containerCount = 1 }) => { +}; + +export const ResourceNumbersOfSession: React.FC = ({ + resource, + containerCount = 1, +}) => { return ( <> {_.map( - _.omit( - form.getFieldsValue().resource, - 'shmem', - 'accelerator', - 'acceleratorType', - ), + _.omit(resource, 'shmem', 'accelerator', 'acceleratorType'), (value, type) => { return ( ); }, )} - {_.isNumber(form.getFieldValue(['resource', 'accelerator'])) && - form.getFieldValue(['resource', 'acceleratorType']) && ( - - )} + {resource && + resource.accelerator && + resource.acceleratorType && + _.isNumber(resource.accelerator) ? ( + + ) : null} ); }; diff --git a/react/src/react-app-env.d.ts b/react/src/react-app-env.d.ts index 44a262365..93c3f6538 100644 --- a/react/src/react-app-env.d.ts +++ b/react/src/react-app-env.d.ts @@ -26,6 +26,12 @@ type DeepPartial = { : T[P]; }; +type SelectivePartial = Partial> & Omit; + +type OptionalFieldsOnly = { + [K in keyof T as {} extends Pick ? K : never]?: T[K]; +}; + type NonNullableItem = NonNullable['items']>>[0]; type NonNullableNodeOnEdges = NonNullable< diff --git a/resources/i18n/de.json b/resources/i18n/de.json index af25679c4..fe2c67e98 100644 --- a/resources/i18n/de.json +++ b/resources/i18n/de.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "Unzureichende Ressourcen. \nDie Sitzung steht möglicherweise noch aus.", "SessionName": "Sitzungsname", "ResetFormConfirm": "Möchten Sie alle Ihre Einträge zurücksetzen?", - "TemplateAndHistory": "Vorlagen", + "TemplateAndHistory": "Vorlagen & Geschichte", "ScheduleTimeSimple": "Startzeit einstellen", "ResourceAllocationPerContainer": "Ressourcenzuweisung pro Container", "NumberOfContainer": "Nummer des Behälters", @@ -279,7 +279,8 @@ "InvalidPortFormat": "Das Format der Anschlussnummer ist falsch.", "DuplicatedPort": "Es gibt doppelte Anschlüsse.", "FolderAliasOverlappingToAutoMount": "Es existiert ein Auto-Mount-Ordner mit demselben Namen.", - "InsufficientAllocationOfResourcesWarning": "Die zuweisbaren Ressourcen liegen unter der im ausgewählten Bild erforderlichen Mindestressource." + "InsufficientAllocationOfResourcesWarning": "Die zuweisbaren Ressourcen liegen unter der im ausgewählten Bild erforderlichen Mindestressource.", + "RecentHistory": "Jüngere Geschichte" }, "Preparing": "Vorbereitung...", "PreparingSession": "Sitzung vorbereiten...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "Speichern ohne Schließen", "Collapse": "Zusammenbruch", "Expand": "Expandieren", - "Clear": "Löschen" + "Clear": "Löschen", + "Apply": "Bewerbung" }, "agent": { "Endpoint": "Endpunkt", diff --git a/resources/i18n/el.json b/resources/i18n/el.json index cee1365cc..2baf5c9c8 100644 --- a/resources/i18n/el.json +++ b/resources/i18n/el.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "Ανεπαρκείς πόροι. \nΗ συνεδρία ενδέχεται να εκκρεμεί.", "SessionName": "Όνομα συνεδρίας", "ResetFormConfirm": "Θέλετε να επαναφέρετε όλες τις καταχωρήσεις σας;", - "TemplateAndHistory": "Πρότυπα", + "TemplateAndHistory": "Πρότυπα & ιστορία", "ScheduleTimeSimple": "Ρύθμιση ώρας έναρξης", "ResourceAllocationPerContainer": "Κατανομή πόρων ανά εμπορευματοκιβώτιο", "NumberOfContainer": "Αριθμός εμπορευματοκιβωτίων", @@ -279,7 +279,8 @@ "InvalidPortFormat": "Η μορφή του αριθμού θύρας είναι λανθασμένη.", "DuplicatedPort": "Υπάρχουν διπλές θύρες.", "FolderAliasOverlappingToAutoMount": "Υπάρχει ένας φάκελος αυτόματης προσάρτησης με το ίδιο όνομα.", - "InsufficientAllocationOfResourcesWarning": "Οι κατανεμητέοι πόροι πέφτουν κάτω από τον ελάχιστο απαιτούμενο πόρο στην επιλεγμένη εικόνα." + "InsufficientAllocationOfResourcesWarning": "Οι κατανεμητέοι πόροι πέφτουν κάτω από τον ελάχιστο απαιτούμενο πόρο στην επιλεγμένη εικόνα.", + "RecentHistory": "Πρόσφατη ιστορία" }, "Preparing": "Προετοιμασία ...", "PreparingSession": "Προετοιμασία συνεδρίας ...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "Αποθήκευση χωρίς κλείσιμο", "Collapse": "Κατάρρευση", "Expand": "Επεκτείνουν", - "Clear": "Διαγράφω" + "Clear": "Διαγράφω", + "Apply": "Εφαρμογή" }, "agent": { "Endpoint": "Τελικό σημείο", diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 4a3cbc373..2f72406fa 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -293,7 +293,8 @@ "InvalidPortFormat": "The port number format is incorrect.", "DuplicatedPort": "There are duplicate ports.", "FolderAliasOverlappingToAutoMount": "An auto-mount folder with the same name exists.", - "InsufficientAllocationOfResourcesWarning": "Allocatable resources falls below the minimum resource required in the selected image." + "InsufficientAllocationOfResourcesWarning": "Allocatable resources falls below the minimum resource required in the selected image.", + "RecentHistory": "Recent History" }, "Preparing": "Preparing...", "PreparingSession": "Preparing session...", @@ -547,7 +548,8 @@ "SaveWithoutClose": "Save Without Close", "Collapse": "Collapse", "Expand": "Expand", - "Clear": "Clear" + "Clear": "Clear", + "Apply": "Apply" }, "agent": { "Endpoint": "Endpoint", diff --git a/resources/i18n/es.json b/resources/i18n/es.json index 489a07c79..b292de8cd 100644 --- a/resources/i18n/es.json +++ b/resources/i18n/es.json @@ -143,7 +143,8 @@ "SaveWithoutClose": "Guardar sin cerrar", "Collapse": "Colapsar", "Expand": "Expandir", - "Clear": "Borrar" + "Clear": "Borrar", + "Apply": "Solicitar" }, "credential": { "AccessKeyOptional": "Clave de acceso (opcional)", @@ -1271,7 +1272,7 @@ "EnqueueComputeSessionWarning": "Recursos insuficientes. \nLa sesión puede estar pendiente.", "SessionName": "Nombre de la sesión", "ResetFormConfirm": "¿Quieres restablecer todas tus entradas?", - "TemplateAndHistory": "Plantillas", + "TemplateAndHistory": "Plantillas e historia", "ScheduleTimeSimple": "Establecer hora de inicio", "ResourceAllocationPerContainer": "Asignación de recursos por contenedor", "NumberOfContainer": "Número de contenedor", @@ -1300,7 +1301,8 @@ "InvalidPortFormat": "El formato del número de puerto es incorrecto.", "DuplicatedPort": "Hay puertos duplicados.", "FolderAliasOverlappingToAutoMount": "Existe una carpeta de montaje automático con el mismo nombre.", - "InsufficientAllocationOfResourcesWarning": "Los recursos asignables están por debajo del recurso mínimo requerido en la imagen seleccionada." + "InsufficientAllocationOfResourcesWarning": "Los recursos asignables están por debajo del recurso mínimo requerido en la imagen seleccionada.", + "RecentHistory": "Historia reciente" }, "ExpiresAfter": "Tiempo restante", "CPU": "CPU", diff --git a/resources/i18n/fi.json b/resources/i18n/fi.json index 8fff1da6d..c9b82b325 100644 --- a/resources/i18n/fi.json +++ b/resources/i18n/fi.json @@ -143,7 +143,8 @@ "SaveWithoutClose": "Tallenna ilman sulkemista", "Collapse": "Romahdus", "Expand": "Laajentaa", - "Clear": "Poistaa" + "Clear": "Poistaa", + "Apply": "Hae" }, "credential": { "AccessKeyOptional": "Pääsyavain (valinnainen)", @@ -1268,7 +1269,7 @@ "EnqueueComputeSessionWarning": "Jos asetat sen suuremmaksi kuin tällä hetkellä käytettävissä olevat resurssit, istunnon luominen viivästyy jonossa, kunnes se tulee saataville.", "SessionName": "Istunnon nimi", "ResetFormConfirm": "Haluatko nollata kaikki tietosi?", - "TemplateAndHistory": "Mallit", + "TemplateAndHistory": "Mallit & historia", "ScheduleTimeSimple": "Aseta aloitusaika", "ResourceAllocationPerContainer": "Resurssien jakaminen konttia kohti", "NumberOfContainer": "Säiliön numero", @@ -1297,7 +1298,8 @@ "InvalidPortFormat": "Porttinumeron muoto on virheellinen.", "DuplicatedPort": "Portteja on päällekkäin.", "FolderAliasOverlappingToAutoMount": "Samanniminen automaattinen kiinnityskansio on olemassa.", - "InsufficientAllocationOfResourcesWarning": "Allokoitavat resurssit jäävät alle valitussa kuvassa vaaditun vähimmäisresurssin." + "InsufficientAllocationOfResourcesWarning": "Allokoitavat resurssit jäävät alle valitussa kuvassa vaaditun vähimmäisresurssin.", + "RecentHistory": "Lähihistoria" }, "ExpiresAfter": "Jäljellä oleva aika", "CPU": "CPU", diff --git a/resources/i18n/fr.json b/resources/i18n/fr.json index bef9c6127..f111ceeab 100644 --- a/resources/i18n/fr.json +++ b/resources/i18n/fr.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "Ressources insuffisantes. \nLa session est peut-être en attente.", "SessionName": "Nom de la séance", "ResetFormConfirm": "Voulez-vous réinitialiser toutes vos entrées ?", - "TemplateAndHistory": "Modèles", + "TemplateAndHistory": "Modèles et histoire", "ScheduleTimeSimple": "Définir l'heure de début", "ResourceAllocationPerContainer": "Allocation de ressources par conteneur", "NumberOfContainer": "Nombre de conteneurs", @@ -279,7 +279,8 @@ "InvalidPortFormat": "Le format du numéro de port est incorrect.", "DuplicatedPort": "Il y a des ports en double.", "FolderAliasOverlappingToAutoMount": "Il existe un dossier de montage automatique portant le même nom.", - "InsufficientAllocationOfResourcesWarning": "Les ressources allouables sont inférieures à la ressource minimale requise dans l'image sélectionnée." + "InsufficientAllocationOfResourcesWarning": "Les ressources allouables sont inférieures à la ressource minimale requise dans l'image sélectionnée.", + "RecentHistory": "Histoire récente" }, "Preparing": "En train de préparer...", "PreparingSession": "Séance de préparation...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "Enregistrer sans fermer", "Collapse": "Effondrement", "Expand": "Développer", - "Clear": "Supprimer" + "Clear": "Supprimer", + "Apply": "Appliquer" }, "agent": { "Endpoint": "Point de terminaison", diff --git a/resources/i18n/id.json b/resources/i18n/id.json index 2e7d25cb2..86ac990f8 100644 --- a/resources/i18n/id.json +++ b/resources/i18n/id.json @@ -256,7 +256,7 @@ "EnqueueComputeSessionWarning": "Sumber daya tidak mencukupi. \nSesi mungkin tertunda.", "SessionName": "Nama sesi", "ResetFormConfirm": "Apakah Anda ingin mengatur ulang semua entri Anda?", - "TemplateAndHistory": "Templat", + "TemplateAndHistory": "Templat & riwayat", "ScheduleTimeSimple": "Tetapkan waktu mulai", "ResourceAllocationPerContainer": "Alokasi Sumber Daya Per Kontainer", "NumberOfContainer": "Jumlah kontainer", @@ -280,7 +280,8 @@ "InvalidPortFormat": "Format nomor port salah.", "DuplicatedPort": "Terdapat port ganda.", "FolderAliasOverlappingToAutoMount": "Folder pemasangan otomatis dengan nama yang sama tersedia.", - "InsufficientAllocationOfResourcesWarning": "Sumber daya yang dapat dialokasikan berada di bawah sumber daya minimum yang diperlukan pada gambar yang dipilih." + "InsufficientAllocationOfResourcesWarning": "Sumber daya yang dapat dialokasikan berada di bawah sumber daya minimum yang diperlukan pada gambar yang dipilih.", + "RecentHistory": "Riwayat Terbaru" }, "Preparing": "Mempersiapkan...", "PreparingSession": "Mempersiapkan sesi...", @@ -458,7 +459,8 @@ "SaveWithoutClose": "Simpan Tanpa Tutup", "Collapse": "Runtuh", "Expand": "Memperluas", - "Clear": "Menghapus" + "Clear": "Menghapus", + "Apply": "Menerapkan" }, "agent": { "Endpoint": "Titik akhir", diff --git a/resources/i18n/it.json b/resources/i18n/it.json index c025df134..cb0941640 100644 --- a/resources/i18n/it.json +++ b/resources/i18n/it.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "Risorse insufficienti. \nLa sessione potrebbe essere in sospeso.", "SessionName": "Nome della sessione", "ResetFormConfirm": "Vuoi reimpostare tutte le tue voci?", - "TemplateAndHistory": "Modelli", + "TemplateAndHistory": "Modelli e storia", "ScheduleTimeSimple": "Imposta l'ora di inizio", "ResourceAllocationPerContainer": "Allocazione delle risorse per contenitore", "NumberOfContainer": "Numero di contenitori", @@ -279,7 +279,8 @@ "InvalidPortFormat": "Il formato del numero di porta non è corretto.", "DuplicatedPort": "Ci sono porte duplicate.", "FolderAliasOverlappingToAutoMount": "Esiste una cartella di montaggio automatico con lo stesso nome.", - "InsufficientAllocationOfResourcesWarning": "Le risorse assegnabili sono inferiori alla risorsa minima richiesta nell'immagine selezionata." + "InsufficientAllocationOfResourcesWarning": "Le risorse assegnabili sono inferiori alla risorsa minima richiesta nell'immagine selezionata.", + "RecentHistory": "Storia recente" }, "Preparing": "Preparazione...", "PreparingSession": "Preparazione della sessione...", @@ -458,7 +459,8 @@ "SaveWithoutClose": "Salva senza chiudere", "Collapse": "Crollo", "Expand": "Espandere", - "Clear": "Eliminare" + "Clear": "Eliminare", + "Apply": "Applicare" }, "agent": { "Endpoint": "Endpoint", diff --git a/resources/i18n/ja.json b/resources/i18n/ja.json index f5969d863..5e2647f67 100644 --- a/resources/i18n/ja.json +++ b/resources/i18n/ja.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "リソースが不十分です。\nセッションが保留中の可能性があります。", "SessionName": "セッション名", "ResetFormConfirm": "入力した内容をすべて初期化しますか?", - "TemplateAndHistory": "テンプレート", + "TemplateAndHistory": "テンプレートと歴史", "ScheduleTimeSimple": "開始時間の設定", "ResourceAllocationPerContainer": "コンテナあたりのリソース割り当て", "NumberOfContainer": "コンテナ数", @@ -279,7 +279,8 @@ "InvalidPortFormat": "ポート番号の形式が正しくない。", "DuplicatedPort": "ポートが重複している。", "FolderAliasOverlappingToAutoMount": "同名の自動マウントフォルダが存在する。", - "InsufficientAllocationOfResourcesWarning": "割り当て可能なリソースが、選択したイメージに必要な最小リソースを下回ります。" + "InsufficientAllocationOfResourcesWarning": "割り当て可能なリソースが、選択したイメージに必要な最小リソースを下回ります。", + "RecentHistory": "最近の歴史" }, "Preparing": "準備...", "PreparingSession": "セッションの準備...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "閉じずに保存", "Collapse": "崩壊", "Expand": "拡大する", - "Clear": "消去" + "Clear": "消去", + "Apply": "応募する" }, "agent": { "Endpoint": "終点", diff --git a/resources/i18n/ko.json b/resources/i18n/ko.json index 4da982ba1..50ba0b011 100644 --- a/resources/i18n/ko.json +++ b/resources/i18n/ko.json @@ -280,7 +280,9 @@ "InvalidPortFormat": "포트 형식이 올바르지 않습니다.", "DuplicatedPort": "중복된 포트가 있습니다.", "FolderAliasOverlappingToAutoMount": "동일한 이름의 자동 마운트 폴더가 존재합니다.", - "InsufficientAllocationOfResourcesWarning": "할당 가능한 자원이 선택된 이미지에서 요구되는 최소 자원보다 부족합니다." + "InsufficientAllocationOfResourcesWarning": "할당 가능한 자원이 선택된 이미지에서 요구되는 최소 자원보다 부족합니다.", + "RecentHistory": "최근 기록", + "CreatedAt": "생성 시간" }, "Preparing": "준비중...", "PreparingSession": "세션 준비중...", @@ -534,7 +536,8 @@ "SaveWithoutClose": "저장 후 유지", "Collapse": "접기", "Expand": "펼치기", - "Clear": "삭제" + "Clear": "삭제", + "Apply": "적용" }, "agent": { "Endpoint": "엔드포인트", diff --git a/resources/i18n/mn.json b/resources/i18n/mn.json index 5790e293e..f9b8542cf 100644 --- a/resources/i18n/mn.json +++ b/resources/i18n/mn.json @@ -280,7 +280,8 @@ "InvalidPortFormat": "Портын дугаарын формат буруу байна.", "DuplicatedPort": "Давхардсан портууд байна.", "FolderAliasOverlappingToAutoMount": "Ижил нэртэй автоматаар холбох хавтас байна.", - "InsufficientAllocationOfResourcesWarning": "Хуваарилах нөөц нь сонгосон зурагт шаардагдах хамгийн бага нөөцөөс доогуур байна." + "InsufficientAllocationOfResourcesWarning": "Хуваарилах нөөц нь сонгосон зурагт шаардагдах хамгийн бага нөөцөөс доогуур байна.", + "RecentHistory": "Сүүлийн үеийн түүх" }, "Preparing": "Бэлтгэж байна ...", "PreparingSession": "Session бэлтгэж байна ...", @@ -459,7 +460,8 @@ "SaveWithoutClose": "Хаахгүйгээр хадгалах", "Collapse": "Нурах", "Expand": "Өргөтгөх", - "Clear": "Устгах" + "Clear": "Устгах", + "Apply": "Өргөдөл гаргах" }, "agent": { "Endpoint": "Төгсгөлийн цэг", diff --git a/resources/i18n/ms.json b/resources/i18n/ms.json index 5f8ddfbaa..1d32c2674 100644 --- a/resources/i18n/ms.json +++ b/resources/i18n/ms.json @@ -279,7 +279,8 @@ "InvalidPortFormat": "Format nombor port tidak betul.", "DuplicatedPort": "Terdapat port pendua.", "FolderAliasOverlappingToAutoMount": "Folder auto-lekap dengan nama yang sama wujud.", - "InsufficientAllocationOfResourcesWarning": "Sumber yang boleh diperuntukkan berada di bawah sumber minimum yang diperlukan dalam imej yang dipilih." + "InsufficientAllocationOfResourcesWarning": "Sumber yang boleh diperuntukkan berada di bawah sumber minimum yang diperlukan dalam imej yang dipilih.", + "RecentHistory": "Sejarah Terkini" }, "Preparing": "Menyiapkan ...", "PreparingSession": "Menyiapkan sesi ...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "Simpan Tanpa Tutup", "Collapse": "Runtuh", "Expand": "Kembangkan", - "Clear": "Padam" + "Clear": "Padam", + "Apply": "Mohon" }, "agent": { "Endpoint": "Titik Akhir", diff --git a/resources/i18n/pl.json b/resources/i18n/pl.json index 21dd039eb..3a1e5c5e5 100644 --- a/resources/i18n/pl.json +++ b/resources/i18n/pl.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "Niewystarczające zasoby. \nSesja może być w toku.", "SessionName": "Nazwa sesji", "ResetFormConfirm": "Czy chcesz zresetować wszystkie swoje wpisy?", - "TemplateAndHistory": "Szablony", + "TemplateAndHistory": "Szablony i historia", "ScheduleTimeSimple": "Ustaw godzinę rozpoczęcia", "ResourceAllocationPerContainer": "Alokacja zasobów na kontener", "NumberOfContainer": "Liczba pojemników", @@ -279,7 +279,8 @@ "InvalidPortFormat": "Format numeru portu jest nieprawidłowy.", "DuplicatedPort": "Istnieją zduplikowane porty.", "FolderAliasOverlappingToAutoMount": "Istnieje folder automatycznego montażu o tej samej nazwie.", - "InsufficientAllocationOfResourcesWarning": "Zasoby, które można przydzielić, są mniejsze niż minimalne zasoby wymagane w wybranym obrazie." + "InsufficientAllocationOfResourcesWarning": "Zasoby, które można przydzielić, są mniejsze niż minimalne zasoby wymagane w wybranym obrazie.", + "RecentHistory": "Najnowsza historia" }, "Preparing": "Przygotowuję...", "PreparingSession": "Przygotowuję sesję...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "Zapisz bez zamykania", "Collapse": "Zawalić się", "Expand": "Zwiększać", - "Clear": "Usuwać" + "Clear": "Usuwać", + "Apply": "Zastosuj" }, "agent": { "Endpoint": "Punkt końcowy", diff --git a/resources/i18n/pt-BR.json b/resources/i18n/pt-BR.json index 031e7955f..b6f344cec 100644 --- a/resources/i18n/pt-BR.json +++ b/resources/i18n/pt-BR.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "Recursos insuficientes. \nA sessão pode estar pendente.", "SessionName": "Nome da sessão", "ResetFormConfirm": "Deseja redefinir todas as suas entradas?", - "TemplateAndHistory": "Modelos", + "TemplateAndHistory": "Modelos e história", "ScheduleTimeSimple": "Definir horário de início", "ResourceAllocationPerContainer": "Atribuição de recursos por contentor", "NumberOfContainer": "Número de contentores", @@ -279,7 +279,8 @@ "InvalidPortFormat": "O formato do número da porta está incorreto.", "DuplicatedPort": "Existem portas duplicadas.", "FolderAliasOverlappingToAutoMount": "Existe uma pasta de montagem automática com o mesmo nome.", - "InsufficientAllocationOfResourcesWarning": "Os recursos alocáveis ​​ficam abaixo do recurso mínimo exigido na imagem selecionada." + "InsufficientAllocationOfResourcesWarning": "Os recursos alocáveis ​​ficam abaixo do recurso mínimo exigido na imagem selecionada.", + "RecentHistory": "História recente" }, "Preparing": "Preparando...", "PreparingSession": "Preparando sessão ...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "Salvar sem fechar", "Collapse": "Colapso", "Expand": "Expandir", - "Clear": "Excluir" + "Clear": "Excluir", + "Apply": "Aplicar" }, "agent": { "Endpoint": "Endpoint", diff --git a/resources/i18n/pt.json b/resources/i18n/pt.json index b513e5370..70af6436f 100644 --- a/resources/i18n/pt.json +++ b/resources/i18n/pt.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "Recursos insuficientes. \nA sessão pode estar pendente.", "SessionName": "Nome da sessão", "ResetFormConfirm": "Deseja redefinir todas as suas entradas?", - "TemplateAndHistory": "Modelos", + "TemplateAndHistory": "Modelos e história", "ScheduleTimeSimple": "Definir horário de início", "ResourceAllocationPerContainer": "Atribuição de recursos por contentor", "NumberOfContainer": "Número de contentores", @@ -279,7 +279,8 @@ "InvalidPortFormat": "O formato do número da porta está incorreto.", "DuplicatedPort": "Existem portas duplicadas.", "FolderAliasOverlappingToAutoMount": "Existe uma pasta de montagem automática com o mesmo nome.", - "InsufficientAllocationOfResourcesWarning": "Os recursos alocáveis ​​ficam abaixo do recurso mínimo exigido na imagem selecionada." + "InsufficientAllocationOfResourcesWarning": "Os recursos alocáveis ​​ficam abaixo do recurso mínimo exigido na imagem selecionada.", + "RecentHistory": "História recente" }, "Preparing": "Preparando...", "PreparingSession": "Preparando sessão ...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "Salvar sem fechar", "Collapse": "Colapso", "Expand": "Expandir", - "Clear": "Excluir" + "Clear": "Excluir", + "Apply": "Aplicar" }, "agent": { "Endpoint": "Endpoint", diff --git a/resources/i18n/ru.json b/resources/i18n/ru.json index b14055dae..ab980b427 100644 --- a/resources/i18n/ru.json +++ b/resources/i18n/ru.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "Недостаточно ресурсов. \nВозможно, сессия еще не завершена.", "SessionName": "Имя сеанса", "ResetFormConfirm": "Вы хотите сбросить все ваши записи?", - "TemplateAndHistory": "Шаблоны", + "TemplateAndHistory": "Шаблоны и история", "ScheduleTimeSimple": "Установить время начала", "ResourceAllocationPerContainer": "Распределение ресурсов на контейнер", "NumberOfContainer": "Количество контейнеров", @@ -279,7 +279,8 @@ "InvalidPortFormat": "Неверный формат номера порта.", "DuplicatedPort": "Имеются дублирующиеся порты.", "FolderAliasOverlappingToAutoMount": "Существует папка автомонтирования с таким же именем.", - "InsufficientAllocationOfResourcesWarning": "Выделяемые ресурсы ниже минимального ресурса, необходимого для выбранного изображения." + "InsufficientAllocationOfResourcesWarning": "Выделяемые ресурсы ниже минимального ресурса, необходимого для выбранного изображения.", + "RecentHistory": "Новейшая история" }, "Preparing": "Подготовка ...", "PreparingSession": "Подготовка сеанса ...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "Сохранить без закрытия", "Collapse": "Крах", "Expand": "Расширять", - "Clear": "Удалить" + "Clear": "Удалить", + "Apply": "Применить" }, "agent": { "Endpoint": "Конечная точка", diff --git a/resources/i18n/th.json b/resources/i18n/th.json index 2943f970a..99df4601a 100644 --- a/resources/i18n/th.json +++ b/resources/i18n/th.json @@ -261,7 +261,7 @@ "PreOpenPortMaxCountLimit": "คุณสามารถตั้งค่าพอร์ตที่เปิดล่วงหน้าได้สูงสุด {{count}} พอร์ต", "PreOpenPortMaxCountLimit_plural": "คุณสามารถตั้งค่าพอร์ตที่เปิดล่วงหน้าได้สูงสุด {{count}} พอร์ต", "EnqueueComputeSessionWarning": "ทรัพยากรไม่เพียงพอ เซสชันอาจอยู่ในสถานะรอ", - "TemplateAndHistory": "เทมเพลตและประวัติ", + "TemplateAndHistory": "เทมเพลต", "StartTimeMustBeInTheFuture": "เวลาเริ่มต้นต้องเป็นเวลาหลังจากปัจจุบัน", "SessionName": "ชื่อเซสชัน", "ResetFormConfirm": "คุณต้องการรีเซ็ตรายการที่คุณป้อนทั้งหมดหรือไม่?", @@ -293,7 +293,8 @@ "InvalidPortFormat": "รูปแบบหมายเลขพอร์ตไม่ถูกต้อง", "DuplicatedPort": "มีพอร์ตที่ซ้ำกัน", "FolderAliasOverlappingToAutoMount": "มีโฟลเดอร์ที่เมาท์อัตโนมัติที่มีชื่อเดียวกันอยู่แล้ว", - "InsufficientAllocationOfResourcesWarning": "ทรัพยากรที่จัดสรรได้อยู่ต่ำกว่าทรัพยากรขั้นต่ำที่จำเป็นในรูปภาพที่เลือก" + "InsufficientAllocationOfResourcesWarning": "ทรัพยากรที่จัดสรรได้อยู่ต่ำกว่าทรัพยากรขั้นต่ำที่จำเป็นในรูปภาพที่เลือก", + "RecentHistory": "ประวัติศาสตร์ล่าสุด" }, "Preparing": "กำลังเตรียมการ...", "PreparingSession": "กำลังเตรียมเซสชัน...", @@ -546,7 +547,8 @@ "SaveWithoutClose": "บันทึกโดยไม่ปิด", "Collapse": "ย่อ", "Expand": "ขยาย", - "Clear": "ล้าง" + "Clear": "ล้าง", + "Apply": "นำมาใช้" }, "agent": { "Endpoint": "จุดสิ้นสุด", diff --git a/resources/i18n/tr.json b/resources/i18n/tr.json index 9e770decc..d049d0859 100644 --- a/resources/i18n/tr.json +++ b/resources/i18n/tr.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "Mevcut mevcut kaynaklardan daha büyük bir değere ayarlarsanız, oturumun oluşturulması, kullanılabilir hale gelene kadar kuyrukta ertelenecektir.", "SessionName": "Oturum adı", "ResetFormConfirm": "Tüm girişlerinizi sıfırlamak istiyor musunuz?", - "TemplateAndHistory": "Şablonlar", + "TemplateAndHistory": "Şablonlar ve tarihçe", "ScheduleTimeSimple": "Başlangıç ​​zamanını ayarla", "ResourceAllocationPerContainer": "Konteyner Başına Kaynak Tahsisi", "NumberOfContainer": "Konteyner sayısı", @@ -279,7 +279,8 @@ "InvalidPortFormat": "Bağlantı noktası numarası biçimi yanlış.", "DuplicatedPort": "Çift bağlantı noktaları var.", "FolderAliasOverlappingToAutoMount": "Aynı ada sahip bir otomatik montaj klasörü mevcut.", - "InsufficientAllocationOfResourcesWarning": "Tahsis edilebilir kaynaklar, seçilen görüntüde gereken minimum kaynağın altına düşüyor." + "InsufficientAllocationOfResourcesWarning": "Tahsis edilebilir kaynaklar, seçilen görüntüde gereken minimum kaynağın altına düşüyor.", + "RecentHistory": "Yakın Tarih" }, "Preparing": "hazırlanıyor...", "PreparingSession": "Oturum hazırlanıyor...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "Kapatmadan Kaydet", "Collapse": "Yıkılmak", "Expand": "Genişletmek", - "Clear": "Silmek" + "Clear": "Silmek", + "Apply": "Başvurmak" }, "agent": { "Endpoint": "uç nokta", diff --git a/resources/i18n/vi.json b/resources/i18n/vi.json index 4fb218054..807dec706 100644 --- a/resources/i18n/vi.json +++ b/resources/i18n/vi.json @@ -279,7 +279,8 @@ "InvalidPortFormat": "Định dạng số cổng không chính xác.", "DuplicatedPort": "Có cổng trùng lặp.", "FolderAliasOverlappingToAutoMount": "Đã tồn tại một thư mục tự động gắn kết có cùng tên.", - "InsufficientAllocationOfResourcesWarning": "Tài nguyên có thể phân bổ giảm xuống dưới mức tài nguyên tối thiểu được yêu cầu trong hình ảnh đã chọn." + "InsufficientAllocationOfResourcesWarning": "Tài nguyên có thể phân bổ giảm xuống dưới mức tài nguyên tối thiểu được yêu cầu trong hình ảnh đã chọn.", + "RecentHistory": "Lịch sử gần đây" }, "Preparing": "Đang chuẩn bị ...", "PreparingSession": "Đang chuẩn bị phiên ...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "Lưu mà không đóng", "Collapse": "Sụp đổ", "Expand": "Mở rộng", - "Clear": "Xóa bỏ" + "Clear": "Xóa bỏ", + "Apply": "Áp dụng" }, "agent": { "Endpoint": "Điểm cuối", diff --git a/resources/i18n/zh-CN.json b/resources/i18n/zh-CN.json index 702f3c63f..8a497b652 100644 --- a/resources/i18n/zh-CN.json +++ b/resources/i18n/zh-CN.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "资源不足。\n会话可能处于待处理状态。", "SessionName": "会话名称", "ResetFormConfirm": "您想重置所有条目吗?", - "TemplateAndHistory": "模板", + "TemplateAndHistory": "模板和历史", "ScheduleTimeSimple": "设置开始时间", "ResourceAllocationPerContainer": "每个集装箱的资源分配", "NumberOfContainer": "集装箱数量", @@ -279,7 +279,8 @@ "InvalidPortFormat": "端口号格式不正确。", "DuplicatedPort": "有重复的端口。", "FolderAliasOverlappingToAutoMount": "存在同名的自动挂载文件夹。", - "InsufficientAllocationOfResourcesWarning": "可分配资源低于所选映像所需的最低资源。" + "InsufficientAllocationOfResourcesWarning": "可分配资源低于所选映像所需的最低资源。", + "RecentHistory": "近代历史" }, "Preparing": "正在准备...", "PreparingSession": "正在准备会议...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "保存而不关闭", "Collapse": "坍塌", "Expand": "扩张", - "Clear": "删除" + "Clear": "删除", + "Apply": "申请" }, "agent": { "Endpoint": "端点", diff --git a/resources/i18n/zh-TW.json b/resources/i18n/zh-TW.json index 192dc173b..40a4a3b97 100644 --- a/resources/i18n/zh-TW.json +++ b/resources/i18n/zh-TW.json @@ -250,7 +250,7 @@ "EnqueueComputeSessionWarning": "資源不足。\n會話可能處於待處理狀態。", "SessionName": "會話名稱", "ResetFormConfirm": "您想重置所有條目嗎?", - "TemplateAndHistory": "範本", + "TemplateAndHistory": "模板和历史", "ScheduleTimeSimple": "設定開始時間", "ResourceAllocationPerContainer": "每个集装箱的资源分配", "NumberOfContainer": "集装箱数量", @@ -279,7 +279,8 @@ "InvalidPortFormat": "端口号格式不正确。", "DuplicatedPort": "有重复的端口。", "FolderAliasOverlappingToAutoMount": "存在同名的自动挂载文件夹。", - "InsufficientAllocationOfResourcesWarning": "可分配資源低於所選映像所需的最低資源。" + "InsufficientAllocationOfResourcesWarning": "可分配資源低於所選映像所需的最低資源。", + "RecentHistory": "近代历史" }, "Preparing": "正在準備...", "PreparingSession": "正在準備會議...", @@ -457,7 +458,8 @@ "SaveWithoutClose": "儲存而不關閉", "Collapse": "坍塌", "Expand": "擴張", - "Clear": "刪除" + "Clear": "刪除", + "Apply": "申请" }, "agent": { "Endpoint": "端點",