From 6d6602f2027f9dc37041b1d2a5ac80d025da9df9 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 3 Jan 2022 18:41:52 +0530 Subject: [PATCH] feat: translations (#145) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Co-authored-by: Faris Ansari --- index.html | 11 ++++ src/cellmanager.js | 5 +- src/datatable.js | 28 +++++++-- src/defaults.js | 128 +++++++++++++++++++------------------- src/rowmanager.js | 5 +- src/translationmanager.js | 30 +++++++++ src/translations/de.json | 15 +++++ src/translations/en.json | 15 +++++ src/translations/fr.json | 15 +++++ src/translations/index.js | 13 ++++ src/translations/it.json | 15 +++++ src/utils.js | 11 ++++ 12 files changed, 222 insertions(+), 69 deletions(-) create mode 100644 src/translationmanager.js create mode 100644 src/translations/de.json create mode 100644 src/translations/en.json create mode 100644 src/translations/fr.json create mode 100644 src/translations/index.js create mode 100644 src/translations/it.json diff --git a/index.html b/index.html index fba0a64..6317566 100644 --- a/index.html +++ b/index.html @@ -189,6 +189,17 @@ dynamicRowHeight: true, treeView: treeView, showTotalRow: true, + // language: 'myLang', + // translations: { + // myLang: { + // "Sort Ascending": "Sort low to high", + // "{count} cells copied": { + // "1": "1 cell was copied", + // "2": "2 cells were copied", + // "default": "Many cells were copied" + // } + // } + // }, // filterRows(keyword, cells, colIndex) { // return cells // .filter(cell => cell.content.includes(keyword)) diff --git a/src/cellmanager.js b/src/cellmanager.js index a796297..b8b9b83 100644 --- a/src/cellmanager.js +++ b/src/cellmanager.js @@ -133,7 +133,10 @@ export default class CellManager { bindCopyCellContents() { this.keyboard.on('ctrl+c', () => { const noOfCellsCopied = this.copyCellContents(this.$focusedCell, this.$selectionCursor); - const message = `${noOfCellsCopied} cell${noOfCellsCopied > 1 ? 's' : ''} copied`; + const message = this.instance.translate('{count} cells copied', { + count: noOfCellsCopied + }); + if (noOfCellsCopied) { this.instance.showToastMessage(message, 2); } diff --git a/src/datatable.js b/src/datatable.js index 0d7991b..1feca4d 100644 --- a/src/datatable.js +++ b/src/datatable.js @@ -6,7 +6,8 @@ import RowManager from './rowmanager'; import BodyRenderer from './body-renderer'; import Style from './style'; import Keyboard from './keyboard'; -import DEFAULT_OPTIONS from './defaults'; +import TranslationManager from './translationmanager'; +import getDefaultOptions from './defaults'; let defaultComponents = { DataManager, @@ -31,6 +32,8 @@ class DataTable { throw new Error('Invalid argument given for `wrapper`'); } + this.initializeTranslations(options); + this.setDefaultOptions(); this.buildOptions(options); this.prepare(); this.initializeComponents(); @@ -41,23 +44,36 @@ class DataTable { } } + initializeTranslations(options) { + this.language = options.language || 'en'; + this.translationManager = new TranslationManager(this.language); + + if (options.translations) { + this.translationManager.addTranslations(options.translations); + } + } + + setDefaultOptions() { + this.DEFAULT_OPTIONS = getDefaultOptions(this); + } + buildOptions(options) { this.options = this.options || {}; this.options = Object.assign( - {}, DEFAULT_OPTIONS, + {}, this.DEFAULT_OPTIONS, this.options || {}, options ); options.headerDropdown = options.headerDropdown || []; this.options.headerDropdown = [ - ...DEFAULT_OPTIONS.headerDropdown, + ...this.DEFAULT_OPTIONS.headerDropdown, ...options.headerDropdown ]; // custom user events this.events = Object.assign( - {}, DEFAULT_OPTIONS.events, + {}, this.DEFAULT_OPTIONS.events, this.options.events || {}, options.events || {} ); @@ -243,6 +259,10 @@ class DataTable { console.log.apply(console, arguments); } } + + translate(str, args) { + return this.translationManager.translate(str, args); + } } DataTable.instances = 0; diff --git a/src/defaults.js b/src/defaults.js index a0ac86f..359f8e8 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -1,71 +1,73 @@ import filterRows from './filterRows'; import icons from './icons'; -export default { - columns: [], - data: [], - dropdownButton: icons.chevronDown, - headerDropdown: [ - { - label: 'Sort Ascending', - action: function (column) { - this.sortColumn(column.colIndex, 'asc'); +export default function getDefaultOptions(instance) { + return { + columns: [], + data: [], + dropdownButton: icons.chevronDown, + headerDropdown: [ + { + label: instance.translate('Sort Ascending'), + action: function (column) { + this.sortColumn(column.colIndex, 'asc'); + } + }, + { + label: instance.translate('Sort Descending'), + action: function (column) { + this.sortColumn(column.colIndex, 'desc'); + } + }, + { + label: instance.translate('Reset sorting'), + action: function (column) { + this.sortColumn(column.colIndex, 'none'); + } + }, + { + label: instance.translate('Remove column'), + action: function (column) { + this.removeColumn(column.colIndex); + } } + ], + events: { + onRemoveColumn(column) {}, + onSwitchColumn(column1, column2) {}, + onSortColumn(column) {}, + onCheckRow(row) {}, + onDestroy() {} }, - { - label: 'Sort Descending', - action: function (column) { - this.sortColumn(column.colIndex, 'desc'); - } + hooks: { + columnTotal: null }, - { - label: 'Reset sorting', - action: function (column) { - this.sortColumn(column.colIndex, 'none'); - } + sortIndicator: { + asc: '↑', + desc: '↓', + none: '' }, - { - label: 'Remove column', - action: function (column) { - this.removeColumn(column.colIndex); - } - } - ], - events: { - onRemoveColumn(column) {}, - onSwitchColumn(column1, column2) {}, - onSortColumn(column) {}, - onCheckRow(row) {}, - onDestroy() {} - }, - hooks: { - columnTotal: null - }, - sortIndicator: { - asc: '↑', - desc: '↓', - none: '' - }, - overrideComponents: { - // ColumnManager: CustomColumnManager - }, - filterRows: filterRows, - freezeMessage: '', - getEditor: null, - serialNoColumn: true, - checkboxColumn: false, - clusterize: true, - logs: false, - layout: 'fixed', // fixed, fluid, ratio - noDataMessage: 'No Data', - cellHeight: 40, - minimumColumnWidth: 30, - inlineFilters: false, - treeView: false, - checkedRowStatus: true, - dynamicRowHeight: false, - pasteFromClipboard: false, - showTotalRow: false, - direction: 'ltr', - disableReorderColumn: false + overrideComponents: { + // ColumnManager: CustomColumnManager + }, + filterRows: filterRows, + freezeMessage: '', + getEditor: null, + serialNoColumn: true, + checkboxColumn: false, + clusterize: true, + logs: false, + layout: 'fixed', // fixed, fluid, ratio + noDataMessage: instance.translate('No Data'), + cellHeight: 40, + minimumColumnWidth: 30, + inlineFilters: false, + treeView: false, + checkedRowStatus: true, + dynamicRowHeight: false, + pasteFromClipboard: false, + showTotalRow: false, + direction: 'ltr', + disableReorderColumn: false + }; }; diff --git a/src/rowmanager.js b/src/rowmanager.js index cf9934f..1d30c92 100644 --- a/src/rowmanager.js +++ b/src/rowmanager.js @@ -133,7 +133,10 @@ export default class RowManager { const checkedRows = this.getCheckedRows(); const count = checkedRows.length; if (count > 0) { - this.bodyRenderer.showToastMessage(`${count} row${count > 1 ? 's' : ''} selected`); + let message = this.instance.translate('{count} rows selected', { + count: count + }); + this.bodyRenderer.showToastMessage(message); } else { this.bodyRenderer.clearToastMessage(); } diff --git a/src/translationmanager.js b/src/translationmanager.js new file mode 100644 index 0000000..77467cd --- /dev/null +++ b/src/translationmanager.js @@ -0,0 +1,30 @@ +import { format } from './utils'; +import getTranslations from './translations'; + +export default class TranslationManager { + constructor(language) { + this.language = language; + this.translations = getTranslations(); + } + + addTranslations(translations) { + this.translations = Object.assign(this.translations, translations); + } + + translate(sourceText, args) { + let translation = (this.translations[this.language] && + this.translations[this.language][sourceText]) || sourceText; + + if (typeof translation === 'object') { + translation = args && args.count ? + this.getPluralizedTranslation(translation, args.count) : + sourceText; + } + + return format(translation, args || {}); + } + + getPluralizedTranslation(translations, count) { + return translations[count] || translations['default']; + } +}; diff --git a/src/translations/de.json b/src/translations/de.json new file mode 100644 index 0000000..0e667df --- /dev/null +++ b/src/translations/de.json @@ -0,0 +1,15 @@ +{ + "Sort Ascending": "Aufsteigend sortieren", + "Sort Descending": "Absteigend sortieren", + "Reset sorting": "Sortierung zurücksetzen", + "Remove column": "Spalte entfernen", + "No Data": "Keine Daten", + "{count} cells copied": { + "1": "{count} Zelle kopiert", + "default": "{count} Zellen kopiert" + }, + "{count} rows selected": { + "1": "{count} Zeile ausgewählt", + "default": "{count} Zeilen ausgewählt" + } +} diff --git a/src/translations/en.json b/src/translations/en.json new file mode 100644 index 0000000..8029868 --- /dev/null +++ b/src/translations/en.json @@ -0,0 +1,15 @@ +{ + "Sort Ascending": "Sort Ascending", + "Sort Descending": "Sort Descending", + "Reset sorting": "Reset sorting", + "Remove column": "Remove column", + "No Data": "No Data", + "{count} cells copied": { + "1": "{count} cell copied", + "default": "{count} cells copied" + }, + "{count} rows selected": { + "1": "{count} row selected", + "default": "{count} rows selected" + } +} diff --git a/src/translations/fr.json b/src/translations/fr.json new file mode 100644 index 0000000..194ec10 --- /dev/null +++ b/src/translations/fr.json @@ -0,0 +1,15 @@ +{ + "Sort Ascending": "Trier par ordre croissant", + "Sort Descending": "Trier par ordre décroissant", + "Reset sorting": "Réinitialiser le tri", + "Remove column": "Supprimer colonne", + "No Data": "Pas de données", + "{count} cells copied": { + "1": "{count} cellule copiée", + "default": "{count} cellules copiées" + }, + "{count} rows selected": { + "1": "{count} ligne sélectionnée", + "default": "{count} lignes sélectionnées" + } +} diff --git a/src/translations/index.js b/src/translations/index.js new file mode 100644 index 0000000..55d7a32 --- /dev/null +++ b/src/translations/index.js @@ -0,0 +1,13 @@ +import en from './en.json'; +import de from './de.json'; +import fr from './fr.json'; +import it from './it.json'; + +export default function getTranslations() { + return { + en, + de, + fr, + it, + }; +}; diff --git a/src/translations/it.json b/src/translations/it.json new file mode 100644 index 0000000..a7308c1 --- /dev/null +++ b/src/translations/it.json @@ -0,0 +1,15 @@ +{ + "Sort Ascending": "Ordinamento ascendente", + "Sort Descending": "Ordinamento decrescente", + "Reset sorting": "Azzeramento ordinamento", + "Remove column": "Rimuovi colonna", + "No Data": "Nessun dato", + "{count} cells copied": { + "1": "Copiato {count} cella", + "default": "{count} celle copiate" + }, + "{count} rows selected": { + "1": "{count} linea selezionata", + "default": "{count} linee selezionate" + } +} diff --git a/src/utils.js b/src/utils.js index 4666036..0899b1f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -138,3 +138,14 @@ export function numberSortAsc(a, b) { export function stripHTML(html) { return html.replace(/<[^>]*>/g, ''); }; + +export function format(str, args) { + if (!str) return str; + + Object.keys(args).forEach(arg => { + let regex = new RegExp(`{(${arg})}`, 'g'); + str = str.replace(regex, args[arg]); + }); + + return str; +};