file upload in UserFileEditor and RecipeEditor

This commit is contained in:
vabene1111
2024-12-22 13:11:48 +01:00
parent de6627ab32
commit 55ee575c9c
35 changed files with 134 additions and 28 deletions

View File

@@ -1,15 +1,15 @@
<template> <template>
<model-editor-base <model-editor-base
:loading="loading" :loading="loading || fileApiLoading"
:dialog="dialog" :dialog="dialog"
@save="saveObject" @save="saveRecipe"
@delete="deleteObject" @delete="deleteObject"
@close="emit('close')" @close="emit('close')"
:is-update="isUpdate()" :is-update="isUpdate()"
:model-class="modelClass" :model-class="modelClass"
:object-name="editingObjName()"> :object-name="editingObjName()">
<v-tabs v-model="tab" :disabled="loading" grow> <v-tabs v-model="tab" :disabled="loading || fileApiLoading" grow>
<v-tab value="recipe">{{ $t('Recipe') }}</v-tab> <v-tab value="recipe">{{ $t('Recipe') }}</v-tab>
<v-tab value="steps">{{ $t('Steps') }}</v-tab> <v-tab value="steps">{{ $t('Steps') }}</v-tab>
<v-tab value="properties">{{ $t('Properties') }}</v-tab> <v-tab value="properties">{{ $t('Properties') }}</v-tab>
@@ -20,18 +20,23 @@
<v-tabs-window v-model="tab"> <v-tabs-window v-model="tab">
<v-tabs-window-item value="recipe"> <v-tabs-window-item value="recipe">
<v-form :disabled="loading"> <v-form :disabled="loading || fileApiLoading">
<v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field> <v-text-field :label="$t('Name')" v-model="editingObj.name"></v-text-field>
<v-textarea :label="$t('Description')" v-model="editingObj.description" clearable counter="512" rows="2"></v-textarea>
<v-row> <v-row>
<v-col cols="12" md="6"> <v-col cols="12" md="6">
<v-textarea :label="$t('Description')" v-model="editingObj.description" clearable counter="512"></v-textarea> <v-file-upload v-model="file" @update:modelValue="updateUserFileName"
:title="$t('DragToUpload')"
:browse-text="$t('Select_File')"
:divider-text="$t('or')"
></v-file-upload>
</v-col> </v-col>
<v-col cols="12" md="6"> <v-col cols="12" md="6">
<v-label>{{ $t('Image')}}</v-label> <v-label>{{ $t('Image') }}</v-label>
<v-img style="max-height: 150px" :src="editingObj.image"></v-img> <v-img style="max-height: 150px" class="mb-2" :src="editingObj.image">
<v-file-input></v-file-input> <v-btn color="delete" class="float-right" prepend-icon="$delete" v-if="editingObj.image" @click="deleteImage()">{{ $t('Delete') }}</v-btn>
<v-btn color="delete" prepend-icon="$delete" v-if="editingObj.image">{{$t('Delete')}}</v-btn> </v-img>
<v-btn color="success" prepend-icon="$upload">{{$t('Select')}}</v-btn>
</v-col> </v-col>
</v-row> </v-row>
@@ -56,7 +61,7 @@
</v-tabs-window-item> </v-tabs-window-item>
<v-tabs-window-item value="steps"> <v-tabs-window-item value="steps">
<v-form :disabled="loading"> <v-form :disabled="loading || fileApiLoading">
<v-row v-for="(s,i ) in editingObj.steps" :key="s.id"> <v-row v-for="(s,i ) in editingObj.steps" :key="s.id">
<v-col> <v-col>
<step-editor v-model="editingObj.steps[i]" :step-index="i"></step-editor> <step-editor v-model="editingObj.steps[i]" :step-index="i"></step-editor>
@@ -76,13 +81,13 @@
</v-form> </v-form>
</v-tabs-window-item> </v-tabs-window-item>
<v-tabs-window-item value="properties"> <v-tabs-window-item value="properties">
<v-form :disabled="loading"> <v-form :disabled="loading || fileApiLoading">
<v-alert class="mb-2" icon="$help">{{ $t('PropertiesFoodHelp') }}</v-alert> <v-alert class="mb-2" icon="$help">{{ $t('PropertiesFoodHelp') }}</v-alert>
<properties-editor v-model="editingObj.properties" :amount-for="$t('Serving')"></properties-editor> <properties-editor v-model="editingObj.properties" :amount-for="$t('Serving')"></properties-editor>
</v-form> </v-form>
</v-tabs-window-item> </v-tabs-window-item>
<v-tabs-window-item value="settings"> <v-tabs-window-item value="settings">
<v-form :disabled="loading"> <v-form :disabled="loading || fileApiLoading">
<v-checkbox :label="$t('Ingredient Overview')" :hint="$t('show_ingredient_overview')" persistent-hint <v-checkbox :label="$t('Ingredient Overview')" :hint="$t('show_ingredient_overview')" persistent-hint
v-model="editingObj.showIngredientOverview"></v-checkbox> v-model="editingObj.showIngredientOverview"></v-checkbox>
@@ -118,7 +123,7 @@
<script setup lang="ts"> <script setup lang="ts">
import {onMounted, PropType, ref} from "vue"; import {onMounted, PropType, ref, shallowRef} from "vue";
import {Recipe, Step} from "@/openapi"; import {Recipe, Step} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue"; import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions"; import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
@@ -127,8 +132,9 @@ import ModelSelect from "@/components/inputs/ModelSelect.vue";
import StepEditor from "@/components/inputs/StepEditor.vue"; import StepEditor from "@/components/inputs/StepEditor.vue";
import {VueDraggable} from "vue-draggable-plus"; import {VueDraggable} from "vue-draggable-plus";
import PropertiesEditor from "@/components/inputs/PropertiesEditor.vue"; import PropertiesEditor from "@/components/inputs/PropertiesEditor.vue";
import {useFileApi} from "@/composables/useFileApi";
import {VFileUpload} from 'vuetify/labs/VFileUpload'
const {t} = useI18n()
const props = defineProps({ const props = defineProps({
item: {type: {} as PropType<Recipe>, required: false, default: null}, item: {type: {} as PropType<Recipe>, required: false, default: null},
@@ -143,6 +149,9 @@ const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading,
const tab = ref("recipe") const tab = ref("recipe")
const dialogStepManager = ref(false) const dialogStepManager = ref(false)
const {fileApiLoading, updateRecipeImage} = useFileApi()
const file = shallowRef<File | null>(null)
onMounted(() => { onMounted(() => {
setupState(props.item, props.itemId, { setupState(props.item, props.itemId, {
newItemFunction: () => { newItemFunction: () => {
@@ -151,6 +160,29 @@ onMounted(() => {
}) })
}) })
/**
* save recipe via normal saveMethod and update image afterward if it was changed
*/
function saveRecipe() {
saveObject().then(() => {
if (file.value != null && editingObj.value.id) {
updateRecipeImage(editingObj.value.id, file.value).then(r => {
file.value = null
setupState(props.item, props.itemId)
})
}
})
}
/**
* remove image if delete was manually triggered
*/
function deleteImage() {
updateRecipeImage(editingObj.value.id!, null).then(r => {
setupState(props.item, props.itemId)
})
}
/** /**
* called by draggable in step manager dialog when steps are sorted * called by draggable in step manager dialog when steps are sorted
*/ */

View File

@@ -16,19 +16,26 @@
<v-row> <v-row>
<v-col cols="12" md="6"> <v-col cols="12" md="6">
<v-file-upload v-model="file"></v-file-upload> <v-file-upload v-model="file" @update:modelValue="updateUserFileName"
:title="$t('DragToUpload')"
:browse-text="$t('Select_File')"
:divider-text="$t('or')"
></v-file-upload>
</v-col> </v-col>
<v-col cols="12" md="6"> <v-col cols="12" md="6">
<v-label> {{ $t('Preview') }}</v-label> <v-label> {{ $t('Preview') }}</v-label>
<v-img max-height="25vh" rounded :src="editingObj.preview"></v-img> <v-img max-height="25vh" rounded :src="editingObj.preview"></v-img>
<v-btn :href="editingObj.fileDownload" target="_blank" color="success" class="float-right" prepend-icon="fa-solid fa-file-arrow-down">{{ $t('Download') }}</v-btn> <v-btn :href="editingObj.fileDownload" target="_blank" color="success" class="float-right" prepend-icon="fa-solid fa-file-arrow-down"
v-if="editingObj.fileDownload != undefined">
{{ $t('Download') }}
</v-btn>
</v-col> </v-col>
</v-row> </v-row>
<v-alert class="mt-2" v-if="!loading && !fileApiLoading && Object.keys(editingObj).length > 0"> <v-alert class="mt-2" v-if="!loading && !fileApiLoading && Object.keys(editingObj).length > 0 && Number(editingObj.fileSizeKb)">
{{ $n(editingObj.fileSizeKb / 1000) }} MB <br/> <p v-if="Number(editingObj.fileSizeKb)">{{ $n(editingObj.fileSizeKb / 1000) }} MB <br/></p>
{{ editingObj.createdBy.displayName }} <br/> <p v-if="editingObj.createdBy"> {{ editingObj.createdBy.displayName }}</p>
{{ DateTime.fromJSDate(editingObj.createdAt).toLocaleString(DateTime.DATETIME_SHORT) }} <p v-if="editingObj.createdAt"> {{ DateTime.fromJSDate(editingObj.createdAt).toLocaleString(DateTime.DATETIME_SHORT) }}</p>
</v-alert> </v-alert>
</v-form> </v-form>
@@ -56,26 +63,35 @@ const props = defineProps({
const emit = defineEmits(['create', 'save', 'delete', 'close']) const emit = defineEmits(['create', 'save', 'delete', 'close'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, modelClass} = useModelEditorFunctions<UserFile>('UserFile', emit) const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, modelClass} = useModelEditorFunctions<UserFile>('UserFile', emit)
const {fileApiLoading, createOrUpdateUserFile} = useFileApi()
// object specific data (for selects/display) // object specific data (for selects/display)
const {fileApiLoading, createOrUpdateUserFile} = useFileApi()
const file = shallowRef<File | null>(null) const file = shallowRef<File | null>(null)
onMounted(() => { onMounted(() => {
setupState(props.item, props.itemId) setupState(props.item, props.itemId)
}) })
/**
* save file to database via fileApi composable
*/
function saveFile() { function saveFile() {
createOrUpdateUserFile(editingObj.value.name, file.value, editingObj.value.id).then(r => { createOrUpdateUserFile(editingObj.value.name, file.value, editingObj.value.id).then(r => {
editingObj.value = r editingObj.value = r
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS) useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
}).catch(err => { }).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err) useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}) })
}
/**
* set name based on file name if name is empty
*/
function updateUserFileName() {
if (file.value != null && (editingObj.value.name == '' || editingObj.value.name == undefined)) {
editingObj.value.name = file.value.name
}
} }
</script> </script>

View File

@@ -1,7 +1,7 @@
import {useDjangoUrls} from "@/composables/useDjangoUrls"; import {useDjangoUrls} from "@/composables/useDjangoUrls";
import {ref} from "vue"; import {ref} from "vue";
import {getCookie} from "@/utils/cookie"; import {getCookie} from "@/utils/cookie";
import {UserFile, UserFileFromJSON} from "@/openapi"; import {RecipeImageFromJSON, UserFile, UserFileFromJSON} from "@/openapi";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore"; import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
/** /**
@@ -12,6 +12,12 @@ export function useFileApi() {
const fileApiLoading = ref(false) const fileApiLoading = ref(false)
/**
* creates or updates an existing UserFile if an id is given
* @param name name to set for user file
* @param file file object to upload
* @param id optional id to update existing user file
*/
function createOrUpdateUserFile(name: string, file: File | null, id?: number): Promise<UserFile> { function createOrUpdateUserFile(name: string, file: File | null, id?: number): Promise<UserFile> {
let formData = new FormData() let formData = new FormData()
formData.append('name', name) formData.append('name', name)
@@ -42,5 +48,24 @@ export function useFileApi() {
}) })
} }
return {fileApiLoading, createOrUpdateUserFile} function updateRecipeImage(recipeId: number, file: File|null){
let formData = new FormData()
if (file != null) {
formData.append('image', file)
}
return fetch(getDjangoUrl(`api/recipe/${recipeId}/image/`), {
method: 'PUT',
headers: {'X-CSRFToken': getCookie('csrftoken')},
body: formData
}).then(r => {
return r.json().then(r => {
return RecipeImageFromJSON(r)
})
}).finally(() => {
fileApiLoading.value = false
})
}
return {fileApiLoading, createOrUpdateUserFile, updateRecipeImage}
} }

View File

@@ -140,10 +140,11 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
function saveObject() { function saveObject() {
loading.value = true loading.value = true
if (isUpdate()) { if (isUpdate()) {
modelClass.value.update(editingObj.value.id, editingObj.value).then((r: T) => { return modelClass.value.update(editingObj.value.id, editingObj.value).then((r: T) => {
emit('save', r) emit('save', r)
editingObj.value = r editingObj.value = r
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS) useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
return r
}).catch((err: any) => { }).catch((err: any) => {
console.error(err) console.error(err)
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err) useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
@@ -151,10 +152,11 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
loading.value = false loading.value = false
}) })
} else { } else {
modelClass.value.create(editingObj.value).then((r: T) => { return modelClass.value.create(editingObj.value).then((r: T) => {
emit('create', r) emit('create', r)
editingObj.value = r editingObj.value = r
useMessageStore().addPreparedMessage(PreparedMessage.CREATE_SUCCESS) useMessageStore().addPreparedMessage(PreparedMessage.CREATE_SUCCESS)
return r
}).catch((err: any) => { }).catch((err: any) => {
console.error(err) console.error(err)
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err) useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)

View File

@@ -82,6 +82,7 @@
"Disable_Amount": "", "Disable_Amount": "",
"Documentation": "", "Documentation": "",
"Download": "", "Download": "",
"DragToUpload": "",
"Drag_Here_To_Delete": "", "Drag_Here_To_Delete": "",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -79,6 +79,7 @@
"Disable_Amount": "Деактивиране на сумата", "Disable_Amount": "Деактивиране на сумата",
"Documentation": "Документация", "Documentation": "Документация",
"Download": "Изтегляне", "Download": "Изтегляне",
"DragToUpload": "",
"Drag_Here_To_Delete": "Плъзнете тук, за да изтриете", "Drag_Here_To_Delete": "Плъзнете тук, за да изтриете",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -114,6 +114,7 @@
"Disabled": "", "Disabled": "",
"Documentation": "", "Documentation": "",
"Download": "", "Download": "",
"DragToUpload": "",
"Drag_Here_To_Delete": "", "Drag_Here_To_Delete": "",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -114,6 +114,7 @@
"Disabled": "Deaktivované", "Disabled": "Deaktivované",
"Documentation": "Dokumentace", "Documentation": "Dokumentace",
"Download": "Stáhnout", "Download": "Stáhnout",
"DragToUpload": "",
"Drag_Here_To_Delete": "Přesunutím sem smazat", "Drag_Here_To_Delete": "Přesunutím sem smazat",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -105,6 +105,7 @@
"Disabled": "Slået fra", "Disabled": "Slået fra",
"Documentation": "Dokumentation", "Documentation": "Dokumentation",
"Download": "Download", "Download": "Download",
"DragToUpload": "",
"Drag_Here_To_Delete": "Træk herhen for at slette", "Drag_Here_To_Delete": "Træk herhen for at slette",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -116,6 +116,7 @@
"Disabled": "Deaktiviert", "Disabled": "Deaktiviert",
"Documentation": "Dokumentation", "Documentation": "Dokumentation",
"Download": "Herunterladen", "Download": "Herunterladen",
"DragToUpload": "Datei per Drag & Drop hinzufügen",
"Drag_Here_To_Delete": "Hierher ziehen zum Löschen", "Drag_Here_To_Delete": "Hierher ziehen zum Löschen",
"Duplicate": "Duplikat", "Duplicate": "Duplikat",
"DuplicateFoundInfo": "Ein Rezept mit dieser URL wurde bereits in deinem Space gefunden. Trotzdem fortfahren?", "DuplicateFoundInfo": "Ein Rezept mit dieser URL wurde bereits in deinem Space gefunden. Trotzdem fortfahren?",

View File

@@ -104,6 +104,7 @@
"Disabled": "Απενεροποιημένο", "Disabled": "Απενεροποιημένο",
"Documentation": "Τεκμηρίωση", "Documentation": "Τεκμηρίωση",
"Download": "Λήψη", "Download": "Λήψη",
"DragToUpload": "",
"Drag_Here_To_Delete": "Σύρετε εδώ για διαγραφή", "Drag_Here_To_Delete": "Σύρετε εδώ για διαγραφή",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -115,6 +115,7 @@
"Disabled": "Disabled", "Disabled": "Disabled",
"Documentation": "Documentation", "Documentation": "Documentation",
"Download": "Download", "Download": "Download",
"DragToUpload": "Drag and Drop files here",
"Drag_Here_To_Delete": "Drag here to delete", "Drag_Here_To_Delete": "Drag here to delete",
"Duplicate": "Duplicate", "Duplicate": "Duplicate",
"DuplicateFoundInfo": "A recipe with this URL was already found in your space. Continue anyway?", "DuplicateFoundInfo": "A recipe with this URL was already found in your space. Continue anyway?",

View File

@@ -115,6 +115,7 @@
"Disabled": "Desactivado", "Disabled": "Desactivado",
"Documentation": "Documentación", "Documentation": "Documentación",
"Download": "Descarga", "Download": "Descarga",
"DragToUpload": "",
"Drag_Here_To_Delete": "Arrastrar aquí para eliminar", "Drag_Here_To_Delete": "Arrastrar aquí para eliminar",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -58,6 +58,7 @@
"DeviceSettingsHelp": "", "DeviceSettingsHelp": "",
"Disable_Amount": "Poista Määrä käytöstä", "Disable_Amount": "Poista Määrä käytöstä",
"Download": "Lataa", "Download": "Lataa",
"DragToUpload": "",
"Drag_Here_To_Delete": "Vedä tänne poistaaksesi", "Drag_Here_To_Delete": "Vedä tänne poistaaksesi",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -114,6 +114,7 @@
"Disabled": "Désactivé", "Disabled": "Désactivé",
"Documentation": "Documentation", "Documentation": "Documentation",
"Download": "Télécharger", "Download": "Télécharger",
"DragToUpload": "",
"Drag_Here_To_Delete": "Glissez ici pour supprimer", "Drag_Here_To_Delete": "Glissez ici pour supprimer",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -115,6 +115,7 @@
"Disabled": "מושבת", "Disabled": "מושבת",
"Documentation": "תיעוד", "Documentation": "תיעוד",
"Download": "הורדה", "Download": "הורדה",
"DragToUpload": "",
"Drag_Here_To_Delete": "משוך לכאן למחיקה", "Drag_Here_To_Delete": "משוך לכאן למחיקה",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -103,6 +103,7 @@
"Disabled": "Kikapcsolva", "Disabled": "Kikapcsolva",
"Documentation": "Dokumentáció", "Documentation": "Dokumentáció",
"Download": "Letöltés", "Download": "Letöltés",
"DragToUpload": "",
"Drag_Here_To_Delete": "Törléshez húzza ide", "Drag_Here_To_Delete": "Törléshez húzza ide",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -44,6 +44,7 @@
"DeviceSettings": "", "DeviceSettings": "",
"DeviceSettingsHelp": "", "DeviceSettingsHelp": "",
"Download": "Ներբեռնել", "Download": "Ներբեռնել",
"DragToUpload": "",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",
"Edit": "", "Edit": "",

View File

@@ -93,6 +93,7 @@
"Disabled": "", "Disabled": "",
"Documentation": "", "Documentation": "",
"Download": "Unduh", "Download": "Unduh",
"DragToUpload": "",
"Drag_Here_To_Delete": "", "Drag_Here_To_Delete": "",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -114,6 +114,7 @@
"Disabled": "", "Disabled": "",
"Documentation": "", "Documentation": "",
"Download": "", "Download": "",
"DragToUpload": "",
"Drag_Here_To_Delete": "", "Drag_Here_To_Delete": "",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -98,6 +98,7 @@
"Disabled": "Disabilitato", "Disabled": "Disabilitato",
"Documentation": "Documentazione", "Documentation": "Documentazione",
"Download": "Scarica", "Download": "Scarica",
"DragToUpload": "",
"Drag_Here_To_Delete": "Sposta qui per eliminare", "Drag_Here_To_Delete": "Sposta qui per eliminare",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -105,6 +105,7 @@
"Disabled": "", "Disabled": "",
"Documentation": "", "Documentation": "",
"Download": "", "Download": "",
"DragToUpload": "",
"Drag_Here_To_Delete": "", "Drag_Here_To_Delete": "",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -102,6 +102,7 @@
"Disabled": "", "Disabled": "",
"Documentation": "", "Documentation": "",
"Download": "Last ned", "Download": "Last ned",
"DragToUpload": "",
"Drag_Here_To_Delete": "Dra her for å slette", "Drag_Here_To_Delete": "Dra her for å slette",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -106,6 +106,7 @@
"Disabled": "Gedeactiveerd", "Disabled": "Gedeactiveerd",
"Documentation": "Documentatie", "Documentation": "Documentatie",
"Download": "Download", "Download": "Download",
"DragToUpload": "",
"Drag_Here_To_Delete": "Sleep hierheen om te verwijderen", "Drag_Here_To_Delete": "Sleep hierheen om te verwijderen",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -116,6 +116,7 @@
"Disabled": "Wyłączone", "Disabled": "Wyłączone",
"Documentation": "Dokumentacja", "Documentation": "Dokumentacja",
"Download": "Pobieranie", "Download": "Pobieranie",
"DragToUpload": "",
"Drag_Here_To_Delete": "Przeciągnij tutaj, aby usunąć", "Drag_Here_To_Delete": "Przeciągnij tutaj, aby usunąć",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -83,6 +83,7 @@
"DeviceSettingsHelp": "", "DeviceSettingsHelp": "",
"Disable_Amount": "Desativar quantidade", "Disable_Amount": "Desativar quantidade",
"Download": "Transferência", "Download": "Transferência",
"DragToUpload": "",
"Drag_Here_To_Delete": "Arraste para aqui para eliminar", "Drag_Here_To_Delete": "Arraste para aqui para eliminar",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -112,6 +112,7 @@
"Disabled": "Desabilitado", "Disabled": "Desabilitado",
"Documentation": "Documentação", "Documentation": "Documentação",
"Download": "Baixar", "Download": "Baixar",
"DragToUpload": "",
"Drag_Here_To_Delete": "Arraste aqui para deletar", "Drag_Here_To_Delete": "Arraste aqui para deletar",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -100,6 +100,7 @@
"Disabled": "Dezactivat", "Disabled": "Dezactivat",
"Documentation": "Documentație", "Documentation": "Documentație",
"Download": "Descarcă", "Download": "Descarcă",
"DragToUpload": "",
"Drag_Here_To_Delete": "Mută aici pentru a șterge", "Drag_Here_To_Delete": "Mută aici pentru a șterge",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -73,6 +73,7 @@
"Disable_Amount": "Деактивировать количество", "Disable_Amount": "Деактивировать количество",
"Documentation": "Документация", "Documentation": "Документация",
"Download": "Загрузить", "Download": "Загрузить",
"DragToUpload": "",
"Drag_Here_To_Delete": "Переместить для удаления", "Drag_Here_To_Delete": "Переместить для удаления",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -73,6 +73,7 @@
"DeviceSettingsHelp": "", "DeviceSettingsHelp": "",
"Disable_Amount": "Onemogoči količino", "Disable_Amount": "Onemogoči količino",
"Download": "Prenesi", "Download": "Prenesi",
"DragToUpload": "",
"Drag_Here_To_Delete": "Povleci sem za izbris", "Drag_Here_To_Delete": "Povleci sem za izbris",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -116,6 +116,7 @@
"Disabled": "Inaktiverad", "Disabled": "Inaktiverad",
"Documentation": "Dokumentation", "Documentation": "Dokumentation",
"Download": "Ladda ned", "Download": "Ladda ned",
"DragToUpload": "",
"Drag_Here_To_Delete": "Dra hit för att radera", "Drag_Here_To_Delete": "Dra hit för att radera",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -115,6 +115,7 @@
"Disabled": "Devre Dışı", "Disabled": "Devre Dışı",
"Documentation": "Dokümantasyon", "Documentation": "Dokümantasyon",
"Download": "İndir", "Download": "İndir",
"DragToUpload": "",
"Drag_Here_To_Delete": "Silmek için buraya sürükleyin", "Drag_Here_To_Delete": "Silmek için buraya sürükleyin",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -88,6 +88,7 @@
"Disable_Amount": "Виключити Кількість", "Disable_Amount": "Виключити Кількість",
"Documentation": "", "Documentation": "",
"Download": "Скачати", "Download": "Скачати",
"DragToUpload": "",
"Drag_Here_To_Delete": "Перемістіть сюди, щоб видалити", "Drag_Here_To_Delete": "Перемістіть сюди, щоб видалити",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -112,6 +112,7 @@
"Disabled": "禁用", "Disabled": "禁用",
"Documentation": "文档", "Documentation": "文档",
"Download": "下载", "Download": "下载",
"DragToUpload": "",
"Drag_Here_To_Delete": "拖动此处可删除", "Drag_Here_To_Delete": "拖动此处可删除",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",

View File

@@ -36,6 +36,7 @@
"DeviceSettings": "", "DeviceSettings": "",
"DeviceSettingsHelp": "", "DeviceSettingsHelp": "",
"Download": "", "Download": "",
"DragToUpload": "",
"Duplicate": "", "Duplicate": "",
"DuplicateFoundInfo": "", "DuplicateFoundInfo": "",
"Edit": "", "Edit": "",