mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-03 21:37:49 -05:00
file upload in UserFileEditor and RecipeEditor
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<model-editor-base
|
||||
:loading="loading"
|
||||
:loading="loading || fileApiLoading"
|
||||
:dialog="dialog"
|
||||
@save="saveObject"
|
||||
@save="saveRecipe"
|
||||
@delete="deleteObject"
|
||||
@close="emit('close')"
|
||||
:is-update="isUpdate()"
|
||||
:model-class="modelClass"
|
||||
: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="steps">{{ $t('Steps') }}</v-tab>
|
||||
<v-tab value="properties">{{ $t('Properties') }}</v-tab>
|
||||
@@ -20,18 +20,23 @@
|
||||
<v-tabs-window v-model="tab">
|
||||
<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-textarea :label="$t('Description')" v-model="editingObj.description" clearable counter="512" rows="2"></v-textarea>
|
||||
|
||||
<v-row>
|
||||
<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 cols="12" md="6">
|
||||
<v-label>{{ $t('Image')}}</v-label>
|
||||
<v-img style="max-height: 150px" :src="editingObj.image"></v-img>
|
||||
<v-file-input></v-file-input>
|
||||
<v-btn color="delete" prepend-icon="$delete" v-if="editingObj.image">{{$t('Delete')}}</v-btn>
|
||||
<v-btn color="success" prepend-icon="$upload">{{$t('Select')}}</v-btn>
|
||||
<v-label>{{ $t('Image') }}</v-label>
|
||||
<v-img style="max-height: 150px" class="mb-2" :src="editingObj.image">
|
||||
<v-btn color="delete" class="float-right" prepend-icon="$delete" v-if="editingObj.image" @click="deleteImage()">{{ $t('Delete') }}</v-btn>
|
||||
</v-img>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -56,7 +61,7 @@
|
||||
|
||||
</v-tabs-window-item>
|
||||
<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-col>
|
||||
<step-editor v-model="editingObj.steps[i]" :step-index="i"></step-editor>
|
||||
@@ -76,13 +81,13 @@
|
||||
</v-form>
|
||||
</v-tabs-window-item>
|
||||
<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>
|
||||
<properties-editor v-model="editingObj.properties" :amount-for="$t('Serving')"></properties-editor>
|
||||
</v-form>
|
||||
</v-tabs-window-item>
|
||||
<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-model="editingObj.showIngredientOverview"></v-checkbox>
|
||||
|
||||
@@ -118,7 +123,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, PropType, ref} from "vue";
|
||||
import {onMounted, PropType, ref, shallowRef} from "vue";
|
||||
import {Recipe, Step} from "@/openapi";
|
||||
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
|
||||
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
|
||||
@@ -127,8 +132,9 @@ import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
import StepEditor from "@/components/inputs/StepEditor.vue";
|
||||
import {VueDraggable} from "vue-draggable-plus";
|
||||
import PropertiesEditor from "@/components/inputs/PropertiesEditor.vue";
|
||||
import {useFileApi} from "@/composables/useFileApi";
|
||||
import {VFileUpload} from 'vuetify/labs/VFileUpload'
|
||||
|
||||
const {t} = useI18n()
|
||||
|
||||
const props = defineProps({
|
||||
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 dialogStepManager = ref(false)
|
||||
|
||||
const {fileApiLoading, updateRecipeImage} = useFileApi()
|
||||
const file = shallowRef<File | null>(null)
|
||||
|
||||
onMounted(() => {
|
||||
setupState(props.item, props.itemId, {
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -16,19 +16,26 @@
|
||||
|
||||
<v-row>
|
||||
<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 cols="12" md="6">
|
||||
<v-label> {{ $t('Preview') }}</v-label>
|
||||
<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-row>
|
||||
|
||||
<v-alert class="mt-2" v-if="!loading && !fileApiLoading && Object.keys(editingObj).length > 0">
|
||||
{{ $n(editingObj.fileSizeKb / 1000) }} MB <br/>
|
||||
{{ editingObj.createdBy.displayName }} <br/>
|
||||
{{ DateTime.fromJSDate(editingObj.createdAt).toLocaleString(DateTime.DATETIME_SHORT) }}
|
||||
<v-alert class="mt-2" v-if="!loading && !fileApiLoading && Object.keys(editingObj).length > 0 && Number(editingObj.fileSizeKb)">
|
||||
<p v-if="Number(editingObj.fileSizeKb)">{{ $n(editingObj.fileSizeKb / 1000) }} MB <br/></p>
|
||||
<p v-if="editingObj.createdBy"> {{ editingObj.createdBy.displayName }}</p>
|
||||
<p v-if="editingObj.createdAt"> {{ DateTime.fromJSDate(editingObj.createdAt).toLocaleString(DateTime.DATETIME_SHORT) }}</p>
|
||||
</v-alert>
|
||||
|
||||
</v-form>
|
||||
@@ -56,26 +63,35 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['create', 'save', 'delete', 'close'])
|
||||
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, modelClass} = useModelEditorFunctions<UserFile>('UserFile', emit)
|
||||
const {fileApiLoading, createOrUpdateUserFile} = useFileApi()
|
||||
|
||||
// object specific data (for selects/display)
|
||||
|
||||
const {fileApiLoading, createOrUpdateUserFile} = useFileApi()
|
||||
const file = shallowRef<File | null>(null)
|
||||
|
||||
onMounted(() => {
|
||||
setupState(props.item, props.itemId)
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* save file to database via fileApi composable
|
||||
*/
|
||||
function saveFile() {
|
||||
|
||||
createOrUpdateUserFile(editingObj.value.name, file.value, editingObj.value.id).then(r => {
|
||||
editingObj.value = r
|
||||
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
|
||||
}).catch(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>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useDjangoUrls} from "@/composables/useDjangoUrls";
|
||||
import {ref} from "vue";
|
||||
import {getCookie} from "@/utils/cookie";
|
||||
import {UserFile, UserFileFromJSON} from "@/openapi";
|
||||
import {RecipeImageFromJSON, UserFile, UserFileFromJSON} from "@/openapi";
|
||||
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
|
||||
|
||||
/**
|
||||
@@ -12,6 +12,12 @@ export function useFileApi() {
|
||||
|
||||
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> {
|
||||
let formData = new FormData()
|
||||
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}
|
||||
}
|
||||
@@ -140,10 +140,11 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
|
||||
function saveObject() {
|
||||
loading.value = true
|
||||
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)
|
||||
editingObj.value = r
|
||||
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
|
||||
return r
|
||||
}).catch((err: any) => {
|
||||
console.error(err)
|
||||
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
|
||||
@@ -151,10 +152,11 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
modelClass.value.create(editingObj.value).then((r: T) => {
|
||||
return modelClass.value.create(editingObj.value).then((r: T) => {
|
||||
emit('create', r)
|
||||
editingObj.value = r
|
||||
useMessageStore().addPreparedMessage(PreparedMessage.CREATE_SUCCESS)
|
||||
return r
|
||||
}).catch((err: any) => {
|
||||
console.error(err)
|
||||
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
"Disable_Amount": "",
|
||||
"Documentation": "",
|
||||
"Download": "",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
"Disable_Amount": "Деактивиране на сумата",
|
||||
"Documentation": "Документация",
|
||||
"Download": "Изтегляне",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Плъзнете тук, за да изтриете",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
"Disabled": "",
|
||||
"Documentation": "",
|
||||
"Download": "",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
"Disabled": "Deaktivované",
|
||||
"Documentation": "Dokumentace",
|
||||
"Download": "Stáhnout",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Přesunutím sem smazat",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
"Disabled": "Slået fra",
|
||||
"Documentation": "Dokumentation",
|
||||
"Download": "Download",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Træk herhen for at slette",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
"Disabled": "Deaktiviert",
|
||||
"Documentation": "Dokumentation",
|
||||
"Download": "Herunterladen",
|
||||
"DragToUpload": "Datei per Drag & Drop hinzufügen",
|
||||
"Drag_Here_To_Delete": "Hierher ziehen zum Löschen",
|
||||
"Duplicate": "Duplikat",
|
||||
"DuplicateFoundInfo": "Ein Rezept mit dieser URL wurde bereits in deinem Space gefunden. Trotzdem fortfahren?",
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
"Disabled": "Απενεροποιημένο",
|
||||
"Documentation": "Τεκμηρίωση",
|
||||
"Download": "Λήψη",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Σύρετε εδώ για διαγραφή",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -115,6 +115,7 @@
|
||||
"Disabled": "Disabled",
|
||||
"Documentation": "Documentation",
|
||||
"Download": "Download",
|
||||
"DragToUpload": "Drag and Drop files here",
|
||||
"Drag_Here_To_Delete": "Drag here to delete",
|
||||
"Duplicate": "Duplicate",
|
||||
"DuplicateFoundInfo": "A recipe with this URL was already found in your space. Continue anyway?",
|
||||
|
||||
@@ -115,6 +115,7 @@
|
||||
"Disabled": "Desactivado",
|
||||
"Documentation": "Documentación",
|
||||
"Download": "Descarga",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Arrastrar aquí para eliminar",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
"DeviceSettingsHelp": "",
|
||||
"Disable_Amount": "Poista Määrä käytöstä",
|
||||
"Download": "Lataa",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Vedä tänne poistaaksesi",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
"Disabled": "Désactivé",
|
||||
"Documentation": "Documentation",
|
||||
"Download": "Télécharger",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Glissez ici pour supprimer",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -115,6 +115,7 @@
|
||||
"Disabled": "מושבת",
|
||||
"Documentation": "תיעוד",
|
||||
"Download": "הורדה",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "משוך לכאן למחיקה",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
"Disabled": "Kikapcsolva",
|
||||
"Documentation": "Dokumentáció",
|
||||
"Download": "Letöltés",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Törléshez húzza ide",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"DeviceSettings": "",
|
||||
"DeviceSettingsHelp": "",
|
||||
"Download": "Ներբեռնել",
|
||||
"DragToUpload": "",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
"Edit": "",
|
||||
|
||||
@@ -93,6 +93,7 @@
|
||||
"Disabled": "",
|
||||
"Documentation": "",
|
||||
"Download": "Unduh",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
"Disabled": "",
|
||||
"Documentation": "",
|
||||
"Download": "",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
"Disabled": "Disabilitato",
|
||||
"Documentation": "Documentazione",
|
||||
"Download": "Scarica",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Sposta qui per eliminare",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
"Disabled": "",
|
||||
"Documentation": "",
|
||||
"Download": "",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"Disabled": "",
|
||||
"Documentation": "",
|
||||
"Download": "Last ned",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Dra her for å slette",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
"Disabled": "Gedeactiveerd",
|
||||
"Documentation": "Documentatie",
|
||||
"Download": "Download",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Sleep hierheen om te verwijderen",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
"Disabled": "Wyłączone",
|
||||
"Documentation": "Dokumentacja",
|
||||
"Download": "Pobieranie",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Przeciągnij tutaj, aby usunąć",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
"DeviceSettingsHelp": "",
|
||||
"Disable_Amount": "Desativar quantidade",
|
||||
"Download": "Transferência",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Arraste para aqui para eliminar",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
"Disabled": "Desabilitado",
|
||||
"Documentation": "Documentação",
|
||||
"Download": "Baixar",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Arraste aqui para deletar",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
"Disabled": "Dezactivat",
|
||||
"Documentation": "Documentație",
|
||||
"Download": "Descarcă",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Mută aici pentru a șterge",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
"Disable_Amount": "Деактивировать количество",
|
||||
"Documentation": "Документация",
|
||||
"Download": "Загрузить",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Переместить для удаления",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
"DeviceSettingsHelp": "",
|
||||
"Disable_Amount": "Onemogoči količino",
|
||||
"Download": "Prenesi",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Povleci sem za izbris",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
"Disabled": "Inaktiverad",
|
||||
"Documentation": "Dokumentation",
|
||||
"Download": "Ladda ned",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Dra hit för att radera",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -115,6 +115,7 @@
|
||||
"Disabled": "Devre Dışı",
|
||||
"Documentation": "Dokümantasyon",
|
||||
"Download": "İndir",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Silmek için buraya sürükleyin",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
"Disable_Amount": "Виключити Кількість",
|
||||
"Documentation": "",
|
||||
"Download": "Скачати",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "Перемістіть сюди, щоб видалити",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
"Disabled": "禁用",
|
||||
"Documentation": "文档",
|
||||
"Download": "下载",
|
||||
"DragToUpload": "",
|
||||
"Drag_Here_To_Delete": "拖动此处可删除",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"DeviceSettings": "",
|
||||
"DeviceSettingsHelp": "",
|
||||
"Download": "",
|
||||
"DragToUpload": "",
|
||||
"Duplicate": "",
|
||||
"DuplicateFoundInfo": "",
|
||||
"Edit": "",
|
||||
|
||||
Reference in New Issue
Block a user