Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions app/lang/i18n.de.json
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,17 @@
"show": "Öffnen",
"clone": "Duplizieren",
"export": "Exportieren",
"exportElementLabels": "Element-Beschriftungen exportieren",
"exportLabelsDescription": "Exportieren Sie alle Element-Beschriftungen aus Ihren Grids als einfache Textdatei. Dies ist nützlich, um einen Überblick über Ihr Vokabular zu erhalten oder für externe Verarbeitung.",
"selectLanguage": "Sprache auswählen",
"sortOrder": "Sortierreihenfolge",
"alphabetically": "Alphabetisch",
"byGrid": "Nach Grid",
"includeGridNames": "Grid-Namen einbeziehen",
"removeDuplicateLabels": "Doppelte Beschriftungen entfernen",
"preview": "Vorschau",
"labels": "Beschriftungen",
"exportToFile": "In Datei exportieren",
"saveAsPdf": "Als PDF speichern",
"gridList": "Grid-Liste",
"gridsToShow": "Anzuzeigende Grids",
Expand Down Expand Up @@ -832,6 +843,7 @@
"TAB_GENERAL": "Allgemein",
"TAB_IMAGE": "Bild",
"TAB_WORDFORMS": "Wortformen",
"TAB_TRANSLATION": "Übersetzung",
"TAB_ACTIONS": "Aktionen",
"TAB_LANGUAGE": "Sprache",
"TAB_APPEARANCE": "Darstellung",
Expand Down Expand Up @@ -1109,6 +1121,8 @@
"importexportDataTofromAllGrids": "Daten von/in alle Grids exportieren/importieren",
"pronunciation": "Aussprache",
"pronunciationOf": "Aussprache von \"{0}\"",
"testPronunciation": "Aussprache testen",
"translationTabInfo": "Übersetzungen werden automatisch gespeichert, wenn Sie das Element speichern. Sie können Übersetzungen für bereits verwendete Sprachen hinzufügen oder eine neue Sprache aus dem Dropdown auswählen.",
"deleteAll": "Alle löschen",
"toggleInCollectionElementIfAddedMultipleTimes": "Bei Mehrfachauswahl in Sammelelement hinzufügen und wieder entfernen",
"httpMethod": "HTTP Methode",
Expand Down
14 changes: 14 additions & 0 deletions app/lang/i18n.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,17 @@
"show": "Show",
"clone": "Clone",
"export": "Export",
"exportElementLabels": "Export element labels",
"exportLabelsDescription": "Export all element labels from your grids as a simple text file. This is useful for getting an overview of your vocabulary or for external processing.",
"selectLanguage": "Select language",
"sortOrder": "Sort order",
"alphabetically": "Alphabetically",
"byGrid": "By grid",
"includeGridNames": "Include grid names",
"removeDuplicateLabels": "Remove duplicate labels",
"preview": "Preview",
"labels": "labels",
"exportToFile": "Export to file",
"saveAsPdf": "Save as PDF",
"gridList": "Grid list",
"gridsToShow": "Grids to show",
Expand Down Expand Up @@ -832,6 +843,7 @@
"TAB_GENERAL": "General",
"TAB_IMAGE": "Image",
"TAB_WORDFORMS": "Word forms",
"TAB_TRANSLATION": "Translation",
"TAB_ACTIONS": "Actions",
"TAB_LANGUAGE": "Language",
"TAB_APPEARANCE": "Appearance",
Expand Down Expand Up @@ -1109,6 +1121,8 @@
"importexportDataTofromAllGrids": "Import/export data to/from all grids",
"pronunciation": "Pronunciation",
"pronunciationOf": "Pronunciation of \"{0}\"",
"testPronunciation": "Test pronunciation",
"translationTabInfo": "Translations are saved automatically when you save the element. You can add translations for languages already used in your grid set, or translate to a new language by selecting it from the dropdown.",
"deleteAll": "Delete all",
"toggleInCollectionElementIfAddedMultipleTimes": "Toggle in collection element if added multiple times",
"httpMethod": "HTTP method",
Expand Down
16 changes: 10 additions & 6 deletions src/vue-components/modals/editElement.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<edit-element-live v-if="currentTab === TABS.TAB_LIVE_DATA" :grid-element="gridElement"></edit-element-live>
<edit-element-image v-if="currentTab === TABS.TAB_IMAGE" :grid-element="gridElement" :grid-data="gridData" :image-search="imageSearch"></edit-element-image>
<edit-element-word-forms v-if="currentTab === TABS.TAB_WORDFORMS" :grid-element="gridElement" :grid-data="gridData" @reloadData="initInternal(true)"></edit-element-word-forms>
<edit-element-translation v-if="currentTab === TABS.TAB_TRANSLATION" :grid-element="gridElement" :grid-data="gridData"></edit-element-translation>
<edit-element-actions v-if="currentTab === TABS.TAB_ACTIONS" :grid-element="gridElement" :grid-data="gridData"></edit-element-actions>
</div>

Expand Down Expand Up @@ -68,13 +69,15 @@
import EditElementWordForms from "./editElementWordForms.vue";
import EditElementLive from './editElementLive.vue';
import EditElementMatrix from './editElementMatrix.vue';
import EditElementTranslation from './editElementTranslation.vue';

const TAB_GENERAL = 'TAB_GENERAL';
const TAB_IMAGE = 'TAB_IMAGE';
const TAB_WORDFORMS = 'TAB_WORDFORMS';
const TAB_TRANSLATION = 'TAB_TRANSLATION';
const TAB_ACTIONS = 'TAB_ACTIONS';
const TAB_LIVE_DATA = 'TAB_LIVE_DATA';
const TABS = {TAB_GENERAL, TAB_IMAGE, TAB_WORDFORMS, TAB_ACTIONS, TAB_LIVE_DATA};
const TABS = {TAB_GENERAL, TAB_IMAGE, TAB_WORDFORMS, TAB_TRANSLATION, TAB_ACTIONS, TAB_LIVE_DATA};

export default {
props: ['editElementIdParam', 'gridDataId', 'undoService', 'newPosition'],
Expand All @@ -84,6 +87,7 @@
EditElementWordForms,
EditElementHeader,
EditElementCollect,
EditElementTranslation,
NavTabs, EditElementGeneral, EditElementImage, EditElementActions, EditElementYoutube
},
data: function () {
Expand Down Expand Up @@ -177,17 +181,17 @@
thiz.gridData.gridElements.push(thiz.gridElement);
}
if (thiz.gridElement.type === GridElement.ELEMENT_TYPE_NORMAL) {
this.possibleTabs = { TAB_GENERAL, TAB_IMAGE, TAB_WORDFORMS, TAB_ACTIONS };
this.possibleTabs = { TAB_GENERAL, TAB_IMAGE, TAB_WORDFORMS, TAB_TRANSLATION, TAB_ACTIONS };
} else if (thiz.gridElement.type === GridElement.ELEMENT_TYPE_YT_PLAYER) {
this.possibleTabs = { TAB_GENERAL, TAB_ACTIONS };
this.possibleTabs = { TAB_GENERAL, TAB_TRANSLATION, TAB_ACTIONS };
} else if (thiz.gridElement.type === GridElement.ELEMENT_TYPE_COLLECT) {
this.possibleTabs = { TAB_GENERAL, TAB_ACTIONS };
this.possibleTabs = { TAB_GENERAL, TAB_TRANSLATION, TAB_ACTIONS };
} else if (thiz.gridElement.type === GridElement.ELEMENT_TYPE_PREDICTION) {
this.possibleTabs = { TAB_ACTIONS };
} else if (thiz.gridElement.type === GridElement.ELEMENT_TYPE_LIVE) {
this.possibleTabs = { TAB_GENERAL, TAB_LIVE_DATA, TAB_IMAGE, TAB_ACTIONS };
this.possibleTabs = { TAB_GENERAL, TAB_LIVE_DATA, TAB_IMAGE, TAB_TRANSLATION, TAB_ACTIONS };
} else if (thiz.gridElement.type === GridElement.ELEMENT_TYPE_MATRIX_CONVERSATION) {
this.possibleTabs = { TAB_GENERAL, TAB_ACTIONS };
this.possibleTabs = { TAB_GENERAL, TAB_TRANSLATION, TAB_ACTIONS };
}
thiz.originalGridElement = JSON.parse(JSON.stringify(thiz.gridElement));
});
Expand Down
242 changes: 242 additions & 0 deletions src/vue-components/modals/editElementTranslation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
<template>
<div class="container-fluid px-0">
<div class="srow" v-if="usedLocales.length > 0">
<label class="four columns">{{ $t('selectAlreadyUsedLanguages') }}</label>
<span class="eight columns">
<button
v-if="locale !== currentLocale"
@click="chosenLocale = locale"
v-for="locale in usedLocales"
style="margin-right: 0.5em; padding: 0; line-height: 1"
>
{{ getLocaleTranslation(locale) }}
</button>
</span>
</div>
<div class="srow" style="margin-top: 2em">
<div class="six columns">
<div class="srow" style="height: 2em">
<strong>{{ $t('textsIn') }}</strong>
<strong>{{ currentLangTranslated }} ({{ currentLocale }})</strong>
</div>
</div>
<div class="six columns">
<div class="srow">
<strong class="three columns">{{ $t('textsIn') }}</strong>
<select class="nine columns" v-model="chosenLocale">
<option
v-for="lang in allLanguages.filter((lang) => lang.code !== currentLocale)"
:value="lang.code"
>
{{ lang | extractTranslationAppLang }} ({{ lang.code }})
</option>
</select>
</div>
</div>
</div>

<div class="srow label-section">
<h3>{{ $t('label') }}</h3>
<div class="row">
<div class="six columns">
<input
type="text"
:placeholder="`${currentLangTranslated}`"
class="u-full-width"
:lang="currentLocale"
v-model="gridElement.label[currentLocale]"
/>
</div>
<div class="six columns">
<input
type="text"
:placeholder="`${chosenLangTranslated}`"
class="u-full-width"
:lang="chosenLocale"
v-model="gridElement.label[chosenLocale]"
/>
</div>
</div>
</div>

<div class="srow">
<h3>{{ $t('pronunciation') }}</h3>
<div class="row">
<div class="six columns" style="position: relative">
<input
type="text"
:placeholder="getPronunciationPlaceholder(currentLocale)"
class="u-full-width"
:lang="currentLocale"
v-model="gridElement.pronunciation[currentLocale]"
/>
<button @click="speak(currentLocale)" class="input-button" :title="$t('testPronunciation')">
<i class="fas fa-play"></i>
</button>
</div>
<div class="six columns" style="position: relative">
<input
type="text"
:placeholder="getPronunciationPlaceholder(chosenLocale)"
class="u-full-width"
:lang="chosenLocale"
v-model="gridElement.pronunciation[chosenLocale]"
/>
<button @click="speak(chosenLocale)" class="input-button" :title="$t('testPronunciation')">
<i class="fas fa-play"></i>
</button>
</div>
</div>
</div>

<div class="srow" v-if="hasCustomSpeakActions">
<h3>{{ `${$t('actions')} "${$t('GridActionSpeakCustom')}"` }}</h3>
<div v-for="(action, index) in customSpeakActions" :key="index" class="row mb-2">
<div class="six columns">
<input
type="text"
:placeholder="`${currentLangTranslated}`"
class="u-full-width"
:lang="currentLocale"
v-model="action.speakText[currentLocale]"
/>
</div>
<div class="six columns">
<input
type="text"
:placeholder="`${chosenLangTranslated}`"
class="u-full-width"
:lang="chosenLocale"
v-model="action.speakText[chosenLocale]"
/>
</div>
</div>
</div>
</div>
</template>

<script>
import { i18nService } from '../../js/service/i18nService';
import { GridActionSpeakCustom } from '../../js/model/GridActionSpeakCustom';
import { speechService } from '../../js/service/speechService';
import { dataService } from '../../js/service/data/dataService';
import './../../css/modal.css';

export default {
props: ['gridElement', 'gridData'],
data: function () {
return {
currentLocale: i18nService.getContentLang(),
chosenLocale: i18nService.isCurrentContentLangEN() ? 'de' : 'en',
allLanguages: i18nService.getAllLanguages(),
usedLocales: []
};
},
computed: {
currentLangTranslated: function () {
return this.getLocaleTranslation(this.currentLocale);
},
chosenLangTranslated: function () {
return this.getLocaleTranslation(this.chosenLocale);
},
customSpeakActions: function () {
if (!this.gridElement || !this.gridElement.actions) {
return [];
}
return this.gridElement.actions.filter(
(action) => action.modelName === GridActionSpeakCustom.getModelName()
);
},
hasCustomSpeakActions: function () {
return this.customSpeakActions.length > 0;
}
},
methods: {
getPronunciationPlaceholder(locale) {
let label = this.gridElement.label[locale] || '';
return i18nService.t('pronunciationOf', label);
},
getLocaleTranslation(locale) {
return i18nService.getTranslationAppLang(this.allLanguages.filter((lang) => lang.code === locale)[0]);
},
speak(locale) {
let speakText = this.gridElement.pronunciation[locale] || this.gridElement.label[locale];
if (!speakText) {
return;
}
speechService.speak(speakText, {
lang: locale,
voiceLangIsTextLang: true
});
},
findUsedLocales() {
this.usedLocales = [];
dataService.getGrids(true).then((grids) => {
for (let grid of grids) {
for (let element of grid.gridElements) {
for (let lang of Object.keys(element.label)) {
if (!this.usedLocales.includes(lang) && !!element.label[lang]) {
this.usedLocales.push(lang);
}
}
}
}
this.usedLocales.sort((a, b) =>
this.getLocaleTranslation(a).localeCompare(this.getLocaleTranslation(b))
);
});
}
},
mounted() {
// Ensure gridElement has proper label and pronunciation structure
if (!this.gridElement.label || typeof this.gridElement.label === 'string') {
this.$set(this.gridElement, 'label', {});
}
if (!this.gridElement.pronunciation) {
this.$set(this.gridElement, 'pronunciation', {});
}

// Ensure speakText is initialized for custom speak actions
if (this.gridElement.actions) {
this.gridElement.actions.forEach((action) => {
if (action.modelName === GridActionSpeakCustom.getModelName() && !action.speakText) {
this.$set(action, 'speakText', {});
}
});
}

this.findUsedLocales();
}
};
</script>

<style scoped>
.srow {
margin-top: 1em;
}

.label-section {
margin-top: 2em;
}

h3 {
font-weight: normal;
font-size: 1.1em;
margin-top: 1em;
margin-bottom: 0.8em;
}

.input-button {
position: absolute;
right: 0;
height: 100%;
line-height: initial;
margin: 0;
padding: 0 1em;
box-shadow: none;
background-color: transparent;
cursor: pointer;
z-index: 1;
pointer-events: auto;
}
</style>
Loading