From b6e1bb06f3339da13c5d9a349b2cf1f204826a85 Mon Sep 17 00:00:00 2001 From: adisposkofh Date: Wed, 6 May 2026 10:54:18 +0200 Subject: [PATCH] implemented tabulator localization --- .../controllers/api/frontend/v1/Phrasen.php | 27 ++ public/js/api/factory/phrasen.js | 12 +- .../Cis/Profil/MitarbeiterProfil.js | 33 +- .../Cis/Profil/MitarbeiterViewProfil.js | 27 +- public/js/components/Cis/Profil/Profil.js | 2 +- .../js/components/Cis/Profil/StudentProfil.js | 20 +- public/js/components/filter/Filter.js | 163 +++++++- system/phrasesupdate.php | 382 ++++++++++++++++++ 8 files changed, 603 insertions(+), 63 deletions(-) diff --git a/application/controllers/api/frontend/v1/Phrasen.php b/application/controllers/api/frontend/v1/Phrasen.php index 7cc652c71..9bf68a989 100644 --- a/application/controllers/api/frontend/v1/Phrasen.php +++ b/application/controllers/api/frontend/v1/Phrasen.php @@ -32,6 +32,7 @@ class Phrasen extends FHCAPI_Controller 'setLanguage' => self::PERM_ANONYMOUS, 'getLanguage' => self::PERM_ANONYMOUS, 'getAllLanguages' => self::PERM_ANONYMOUS, + 'getPhrases' => self::PERM_ANONYMOUS ]); $this->load->helper('hlp_language'); @@ -92,4 +93,30 @@ class Phrasen extends FHCAPI_Controller $this->terminateWithSuccess($langs); } + public function getPhrases() + { + $languages = json_decode($this->input->get('languages')); + if (!$languages || !count($languages)) { + $this->load->model('system/Sprache_model', 'sprachenModel'); + $activeLanguages = $this->sprachenModel->loadWhere(array('content' => true)); + $activeLanguagesData = $this->getDataOrTerminateWithError($activeLanguages); + $languages = array_map( + function ($languageData) { + return $languageData->sprache; + }, + $activeLanguagesData + ); + } + + $this->load->model('system/Phrase_model', 'phraseModel'); + $phrasesGroupedByCategory = json_decode($this->input->get('phrasesGroupedByCategory')); + $result = []; + foreach ($languages as $language) { + $phrases = $this->phraseModel->getPhrasesByCategoryAndPhrasesAndLanguage($phrasesGroupedByCategory, $language); + $result[$language] = $this->getDataOrTerminateWithError($phrases); + } + + $this->terminateWithSuccess($result); + } + } \ No newline at end of file diff --git a/public/js/api/factory/phrasen.js b/public/js/api/factory/phrasen.js index a87e4f4be..127e64826 100644 --- a/public/js/api/factory/phrasen.js +++ b/public/js/api/factory/phrasen.js @@ -41,5 +41,15 @@ export default { method: 'get', url: '/api/frontend/v1/phrasen/getAllLanguages' }; - } + }, + getPhrases(phrasesGroupedByCategory, languages = []) { + return { + method: 'get', + url: '/api/frontend/v1/phrasen/getPhrases', + params: { + phrasesGroupedByCategory: JSON.stringify(phrasesGroupedByCategory), + languages: JSON.stringify(languages), + }, + }; + }, }; \ No newline at end of file diff --git a/public/js/components/Cis/Profil/MitarbeiterProfil.js b/public/js/components/Cis/Profil/MitarbeiterProfil.js index 559006a1b..cd40d0741 100644 --- a/public/js/components/Cis/Profil/MitarbeiterProfil.js +++ b/public/js/components/Cis/Profil/MitarbeiterProfil.js @@ -34,7 +34,6 @@ export default { return { showModal: false, editDataFilter: null, - preloadedPhrasen:{}, // tabulator options funktionen_table_options: { persistenceID: "filterTableMaProfilFunktionen", @@ -46,6 +45,7 @@ export default { responsiveLayout: "collapse", responsiveLayoutCollapseUseFormatters: false, responsiveLayoutCollapseFormatter: Vue.$collapseFormatter, + locale: true, columns: [ { title: @@ -59,21 +59,21 @@ export default { visible: true }, { - title: Vue.computed(() => this.preloadedPhrasen.bezeichnungPhrase), + title: "ui/bezeichnung", field: "Bezeichnung", headerFilter: true, minWidth: 200, visible: true }, { - title: Vue.computed(() => this.preloadedPhrasen.organisationseinheitPhrase), + title: "lehre/organisationseinheit", field: "Organisationseinheit", headerFilter: true, minWidth: 200, visible: true }, { - title: Vue.computed(() => this.preloadedPhrasen.gueltigVonPhrase), + title: "global/gueltigVon", field: "Gültig_von", headerFilterFunc: 'dates', headerFilter: dateFilter, @@ -84,7 +84,7 @@ export default { formatterParams: this.datetimeFormatterParams() }, { - title: Vue.computed(() => this.preloadedPhrasen.gueltigBisPhrase), + title: "global/gueltigBis", field: "Gültig_bis", headerFilterFunc: 'dates', headerFilter: dateFilter, @@ -95,7 +95,7 @@ export default { formatterParams: this.datetimeFormatterParams() }, { - title: Vue.computed(() => this.preloadedPhrasen.wochenstundenPhrase), + title: "profil/wochenstunden", field: "Wochenstunden", headerFilter: true, minWidth: 200, @@ -115,6 +115,7 @@ export default { responsiveLayoutCollapseUseFormatters: false, responsiveLayoutCollapseFormatter: Vue.$collapseFormatter, data: [{betriebsmittel: "", Nummer: "", Ausgegeben_am: ""}], + locale: true, columns: [ { title: @@ -128,14 +129,14 @@ export default { visible: true }, { - title: Vue.computed(() => this.preloadedPhrasen.entlehnteBetriebsmittelPhrase), + title: "profil/entlehnteBetriebsmittel", field: "betriebsmittel", headerFilter: true, minWidth: 200, visible: true }, { - title: Vue.computed(() => this.preloadedPhrasen.inventarnummerPhrase), + title: "profil/inventarnummer", field: "Nummer", headerFilter: true, resizable: true, @@ -143,7 +144,7 @@ export default { visible: true }, { - title: Vue.computed(() => this.preloadedPhrasen.ausgabedatumPhrase), + title: "profil/ausgabedatum", field: "Ausgegeben_am", headerFilterFunc: 'dates', headerFilter: dateFilter, @@ -299,18 +300,6 @@ export default { }, created() { - // preload phrasen - this.$p.loadCategory(["ui","lehre","global","profil"]).then(() => { - this.preloadedPhrasen.bezeichnungPhrase = this.$p.t('ui/bezeichnung'); - this.preloadedPhrasen.organisationseinheitPhrase = this.$p.t('lehre/organisationseinheit'); - this.preloadedPhrasen.gueltigVonPhrase = this.$p.t('global/gueltigVon'); - this.preloadedPhrasen.gueltigBisPhrase = this.$p.t('global/gueltigBis'); - this.preloadedPhrasen.wochenstundenPhrase = this.$p.t('profil/wochenstunden'); - this.preloadedPhrasen.entlehnteBetriebsmittelPhrase = this.$p.t('profil/entlehnteBetriebsmittel'); - this.preloadedPhrasen.inventarnummerPhrase = this.$p.t('profil/inventarnummer'); - this.preloadedPhrasen.ausgabedatumPhrase = this.$p.t('profil/ausgabedatum'); - this.preloadedPhrasen.loaded=true; - }); //? sorts the profil Updates: pending -> accepted -> rejected this.data.profilUpdates?.sort(this.sortProfilUpdates); @@ -440,7 +429,6 @@ export default {
this.$p.t('ui/bezeichnung')), + title: "ui/bezeichnung", field: "Bezeichnung", headerFilter: true, minWidth: 200, visible: true }, { - title: Vue.computed(() => this.$p.t('lehre/organisationseinheit')), + title: "lehre/organisationseinheit", field: "Organisationseinheit", headerFilter: true, minWidth: 200, visible: true }, { - title: Vue.computed(() => this.$p.t('global/gueltigVon')), + title: "global/gueltigVon", field: "Gültig_von", headerFilterFunc: 'dates', headerFilter: dateFilter, @@ -71,7 +71,7 @@ export default { formatterParams: this.datetimeFormatterParams() }, { - title: Vue.computed(() => this.$p.t('global/gueltigBis')), + title: "global/gueltigBis", field: "Gültig_bis", headerFilterFunc: 'dates', headerFilter: dateFilter, @@ -82,7 +82,7 @@ export default { formatterParams: this.datetimeFormatterParams() }, { - title: Vue.computed(() => this.$p.t('profil/wochenstunden')), + title: "profil/wochenstunden", field: "Wochenstunden", headerFilter: true, minWidth: 200, @@ -113,9 +113,6 @@ export default { 'data.funktionen'(newVal) { if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setData(newVal); }, - 'language.value'(newVal) { // reevaluates computed phrasen - if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setColumns(this.funktionen_table_options.columns) - } }, computed: { getTelefonValue() { @@ -180,16 +177,6 @@ export default { }; }, }, - created(){ - this.$p.loadCategory(["ui", "lehre", "global", "profil"]).then(() => { - this.preloadedPhrasen.bezeichnungPhrase = this.$p.t('ui/bezeichnung'); - this.preloadedPhrasen.organisationseinheitPhrase = this.$p.t('lehre/organisationseinheit'); - this.preloadedPhrasen.gueltigVonPhrase = this.$p.t('global/gueltigVon'); - this.preloadedPhrasen.gueltigBisPhrase = this.$p.t('global/gueltigBis'); - this.preloadedPhrasen.wochenstundenPhrase = this.$p.t('profil/wochenstunden'); - this.preloadedPhrasen.loaded = true; - }); - }, template: /*html*/ ` @@ -247,7 +234,7 @@ export default {
- +
diff --git a/public/js/components/Cis/Profil/Profil.js b/public/js/components/Cis/Profil/Profil.js index da205816d..37f3a8edb 100644 --- a/public/js/components/Cis/Profil/Profil.js +++ b/public/js/components/Cis/Profil/Profil.js @@ -24,7 +24,7 @@ Vue.$collapseFormatter = function (data) { let item2 = document.createElement("div"); item2.classList.add("col-6"); - item.innerHTML = "" + col.title + ""; + item.innerHTML = "placeholder"; item2.innerHTML = col.value ? col.value : "-"; list.appendChild(item); diff --git a/public/js/components/Cis/Profil/StudentProfil.js b/public/js/components/Cis/Profil/StudentProfil.js index 3f8e8380d..b4debb3c3 100644 --- a/public/js/components/Cis/Profil/StudentProfil.js +++ b/public/js/components/Cis/Profil/StudentProfil.js @@ -33,7 +33,6 @@ export default { showModal: false, collapseIconBetriebsmittel: true, editDataFilter: null, - preloadedPhrasen:{}, // tabulator options zutrittsgruppen_table_options: { persistenceID: "filterTableStudentProfilZutrittsgruppen", @@ -43,7 +42,7 @@ export default { minHeight: 200, layout: "fitColumns", columns: [{ - title: Vue.computed(() => this.preloadedPhrasen.zutrittsGruppenPhrase), + title: "profil/zutrittsGruppen", field: "bezeichnung" }], }, @@ -57,6 +56,7 @@ export default { responsiveLayout: "collapse", responsiveLayoutCollapseUseFormatters: false, responsiveLayoutCollapseFormatter: Vue.$collapseFormatter, + locale: true, columns: [ { title: @@ -69,14 +69,14 @@ export default { headerClick: this.collapseFunction, }, { - title: Vue.computed(()=>this.preloadedPhrasen.entlehnteBetriebsmittelPhrase), + title: "profil/entlehnteBetriebsmittel", field: "betriebsmittel", headerFilter: true, minWidth: 200, visible: true }, { - title: Vue.computed(() =>this.preloadedPhrasen.inventarnummerPhrase) , + title: "profil/inventarnummer", field: "Nummer", headerFilter: true, resizable: true, @@ -84,7 +84,7 @@ export default { visible: true }, { - title: Vue.computed(() =>this.preloadedPhrasen.ausgabedatum) , + title: "profil/ausgabedatum", field: "Ausgegeben_am", headerFilterFunc: 'dates', headerFilter: dateFilter, @@ -242,14 +242,6 @@ export default { }, }, created() { - // preload phrasen - this.$p.loadCategory('profil').then(() => { - this.preloadedPhrasen.zutrittsGruppenPhrase = this.$p.t('profil/zutrittsGruppen'); - this.preloadedPhrasen.entlehnteBetriebsmittelPhrase = this.$p.t('profil/entlehnteBetriebsmittel'); - this.preloadedPhrasen.inventarnummerPhrase = this.$p.t('profil/inventarnummer'); - this.preloadedPhrasen.ausgabedatum = this.$p.t('profil/ausgabedatum'); - this.preloadedPhrasen.loaded = true; - }); //? sorts the profil Updates: pending -> accepted -> rejected this.data.profilUpdates?.sort(this.sortProfilUpdates); }, @@ -379,7 +371,6 @@ export default {
({...el, ...{title: filterTitles[el.name]}})); } }, + watch: { + "language.value": { + handler(newSelectedLanguage) { + if (!this.$props.tabulatorOptions.locale) return; + + this.tabulator.setLocale(newSelectedLanguage); + + if (this.$props.tabulatorOptions.responsiveLayoutCollapseFormatter) { + this.localizeCollapsedColumnHeadings(newSelectedLanguage); + } + }, + }, + }, methods: { reloadTable() { if (this.tableOnly) @@ -263,6 +278,11 @@ export const CoreFilterCmpt = { persistence: this.persistence, }, ...(this.tabulatorOptions || {})}; + // set up localizations if required + if (tabulatorOptions.locale) { + tabulatorOptions = await this.configureTabulatorLocalizations(tabulatorOptions); + } + // set default height if no height property is set if (tabulatorOptions.height === undefined && tabulatorOptions.minHeight === undefined && @@ -302,7 +322,13 @@ export const CoreFilterCmpt = { for (let evt of this.tabulatorEvents) this.tabulator.on(evt.event, evt.handler); } - this.tabulator.on('tableBuilt', () => {this.tableBuilt = true; this.$emit('tableBuilt');}); + this.tabulator.on('tableBuilt', () => { + this.tableBuilt = true; + this.$emit('tableBuilt'); + if (tabulatorOptions.locale && tabulatorOptions.responsiveLayoutCollapseFormatter) { + this.localizeCollapsedColumnHeadings(); + } + }); this.tabulator.on("rowSelectionChanged", data => { this.selectedData = data; }); @@ -340,6 +366,9 @@ export const CoreFilterCmpt = { this.selectedFields = cols.filter(col => col.isVisible()).map(col => col.getField()); if (this.tabulator.options.persistence.headerFilter) this._setHeaderFilter(); + if (tabulatorOptions.locale && tabulatorOptions.responsiveLayoutCollapseFormatter) { + this.localizeCollapsedColumnHeadings(); + } }); } @@ -349,6 +378,134 @@ export const CoreFilterCmpt = { this.$emit("headerFilterOn", this.filterActive); }); }, + async configureTabulatorLocalizations(tabulatorOptions) { + tabulatorOptions.locale = this.$p.user_language.value; + tabulatorOptions.langs = {}; + + let phrasesGroupedByCategoryRequestParam = { + tabulator: [ + "loading", + "error", + "item", + "items", + "page_size", + "page_title", + "first", + "first_title", + "last", + "last_title", + "prev", + "prev_title", + "next", + "next_title", + "all", + "showing", + "of", + "rows", + "pages", + ], + }; + tabulatorOptions.columns.forEach((column) => { + if (column.field === "collapse") return; + + let [category, phrase] = column.title.split("/"); + if (phrasesGroupedByCategoryRequestParam[category]) { + phrasesGroupedByCategoryRequestParam[category].push(phrase); + } else { + phrasesGroupedByCategoryRequestParam[category] = [phrase]; + } + }); + + const phrasesResponse = await this.$api.call( + ApiPhrases.getPhrases(phrasesGroupedByCategoryRequestParam), + ); + const phrasesData = phrasesResponse.data; + + Object.keys(phrasesData).forEach((language) => { + let genericTabulatorPhrases = phrasesData[language].filter( + (phrase) => phrase.category === "tabulator", + ); + + let tabulatorPhraseToTranslationMapper = {}; + genericTabulatorPhrases.forEach((phrase) => { + tabulatorPhraseToTranslationMapper[phrase.phrase] = + phrase.text; + }); + + tabulatorOptions.langs[language] = { + data: { + loading: tabulatorPhraseToTranslationMapper["loading"], + error: tabulatorPhraseToTranslationMapper["error"], + }, + groups: { + item: tabulatorPhraseToTranslationMapper["item"], + items: tabulatorPhraseToTranslationMapper["items"], + }, + pagination: { + page_size: + tabulatorPhraseToTranslationMapper["page_size"], + page_title: + tabulatorPhraseToTranslationMapper["page_title"], + first: tabulatorPhraseToTranslationMapper["first"], + first_title: + tabulatorPhraseToTranslationMapper["first_title"], + last: tabulatorPhraseToTranslationMapper["last"], + last_title: + tabulatorPhraseToTranslationMapper["last_title"], + prev: tabulatorPhraseToTranslationMapper["prev"], + prev_title: + tabulatorPhraseToTranslationMapper["prev_title"], + next: tabulatorPhraseToTranslationMapper["next"], + next_title: + tabulatorPhraseToTranslationMapper["next_title"], + all: tabulatorPhraseToTranslationMapper["all"], + counter: { + showing: + tabulatorPhraseToTranslationMapper["showing"], + of: tabulatorPhraseToTranslationMapper["of"], + rows: tabulatorPhraseToTranslationMapper["rows"], + pages: tabulatorPhraseToTranslationMapper["pages"], + }, + }, + }; + + let columnHeadingPhrases = phrasesData[language].filter( + (phrase) => phrase.category !== "tabulator", + ); + + let columnTitleToTranslationMapper = {}; + columnHeadingPhrases.forEach((phrase) => { + columnTitleToTranslationMapper[ + phrase.category + "/" + phrase.phrase + ] = phrase.text; + }); + + let columnFieldToTranslationMapper = {}; + tabulatorOptions.columns.forEach((column) => { + if (column.field === "collapse") return; + columnFieldToTranslationMapper[column.field] = + columnTitleToTranslationMapper[column.title]; + }); + + tabulatorOptions.langs[language].columns = + columnFieldToTranslationMapper; + }); + + return tabulatorOptions; + }, + async localizeCollapsedColumnHeadings() { + const columnHeadings = this.tabulator.getLang().columns; + + this.$refs.table + .querySelectorAll(".collapsedColumnHeading") + .forEach((collapsedColumnHeadingElement) => { + const field = collapsedColumnHeadingElement.getAttribute("field"); + if (!field?.length) return; + + collapsedColumnHeadingElement.innerHTML = + columnHeadings[field]; + }); + }, updateTabulator() { if (this.tabulator) { if (this.tableBuilt) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 0476db2c9..fd3de09b8 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -58177,6 +58177,388 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), // ### Phrases Dashboard Admin END + // generic tabulator phrases START + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'loading', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Loading', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'error', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Error', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'item', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'item', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'items', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'items', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'page_size', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Page Size', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'page_title', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Show Page', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'first', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'First', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'first_title', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'First Page', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'last', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Last', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'last_title', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Last Page', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'prev', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Prev', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'prev_title', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Prev Page', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'next', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Next', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'next_title', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Next Page', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'all', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'All', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'showing', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Showing', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'of', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'of', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'rows', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'rows', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tabulator', + 'phrase' => 'pages', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '???', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'pages', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + // generic tabulator phrases END );