From bf8fc071ea0ec0dabd1675c0bcd7633a8e6117b3 Mon Sep 17 00:00:00 2001 From: Dmitry Mazurov Date: Tue, 24 Mar 2020 21:31:25 +0300 Subject: [PATCH] Added multilanguage translation Signed-off-by: Dmitry Mazurov --- README.md | 103 ++++++++++++++++++++++------ src/css/easymde.css | 8 +-- src/js/easymde.js | 156 +++++++++++++++++------------------------- src/js/languages.json | 66 ++++++++++++++++++ 4 files changed, 215 insertions(+), 118 deletions(-) create mode 100644 src/js/languages.json diff --git a/README.md b/README.md index ba8a756..7f31c9c 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ The editor is entirely customizable, from theming to toolbar buttons and javascr - [Configuration](#configuration) - [Options list](#options-list) - [Options example](#options-example) + - [Localization example](#localization-example) - [Toolbar icons](#toolbar-icons) - [Toolbar customization](#toolbar-customization) - [Keyboard shortcuts](#keyboard-shortcuts) @@ -115,6 +116,20 @@ easyMDE.value('New input for **EasyMDE**'); ### Options list +- **locale**: Set locale. Default `en`. +- **localization**: Set custom translate. + - **imageTexts**: Texts displayed to the user (mainly on the status bar) for the import image feature, where `#image_name#`, `#image_size#` and `#image_max_size#` will replaced by their respective values, that can be used for customization or internationalization: + - **sbInit**: Status message displayed initially if `uploadImage` is set to `true`. Defaults to `Attach files by drag and dropping or pasting from clipboard.`. + - **sbOnDragEnter**: Status message displayed when the user drags a file to the text area. Defaults to `Drop image to upload it.`. + - **sbOnDrop**: Status message displayed when the user drops a file in the text area. Defaults to `Uploading images #images_names#`. + - **sbProgress**: Status message displayed to show uploading progress. Defaults to `Uploading #file_name#: #progress#%`. + - **sbOnUploaded**: Status message displayed when the image has been uploaded. Defaults to `Uploaded #image_name#`. + - **sizeUnits**: A comma-separated list of units used to display messages with human-readable file sizes. Defaults to `b,Kb,Mb`. + - **noFileGiven**: The server did not receive any file from the user. Defaults to `You must select a file.`. + - **typeNotAllowed**: The user send a file type which doesn't match the `imageAccept` list, or the server returned this error code. Defaults to `This image type is not allowed.`. + - **fileTooLarge**: The size of the image being imported is bigger than the `imageMaxSize`, or if the server returned this error code. Defaults to `Image #image_name# is too big (#image_size#).\nMaximum file size is #image_max_size#.`. + - **importError**: An unexpected error occurred when uploading the image. Defaults to `Something went wrong when uploading the image #image_name#.`. + - **errorMessages**: Errors displayed to the user, using the `errorCallback` option, where `#image_name#`, `#image_size#` and `#image_max_size#` will replaced by their respective values, that can be used for customization or internationalization: - **autoDownloadFontAwesome**: If set to `true`, force downloads Font Awesome (used for icons). If set to `false`, prevents downloading. Defaults to `undefined`, which will intelligently check whether Font Awesome has already been included, then download accordingly. - **autofocus**: If set to `true`, focuses the editor automatically. Defaults to `false`. - **autosave**: *Saves the text that's being written and will load it back in the future. It will forget the text when the form it's contained in is submitted.* @@ -123,7 +138,6 @@ easyMDE.value('New input for **EasyMDE**'); - **submit_delay**: Delay before assuming that submit of the form failed and saving the text, in milliseconds. Defaults to `autosave.delay` or `10000` (10s). - **uniqueId**: You must set a unique string identifier so that EasyMDE can autosave. Something that separates this from other instances of EasyMDE elsewhere on your website. - **timeFormat**: Set DateTimeFormat. More information see [DateTimeFormat instances](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat). Default `locale: en-US, format: hour:minute`. - - **text**: Set text for autosave. - **blockStyles**: Customize how certain buttons that style blocks of text behave. - **bold**: Can be set to `**` or `__`. Defaults to `**`. - **code**: Can be set to ```` ``` ```` or `~~~`. Defaults to ```` ``` ````. @@ -149,9 +163,6 @@ easyMDE.value('New input for **EasyMDE**'); - **previewClass**: A string or array of strings that will be applied to the preview screen when activated. Defaults to `"editor-preview"`. - **previewRender**: Custom function for parsing the plaintext Markdown and returning HTML. Used when user previews. - **promptURLs**: If set to `true`, a JS alert window appears asking for the link or image URL. Defaults to `false`. -- **promptTexts**: Customize the text used to prompt for URLs. - - **image**: The text to use when prompting for an image's URL. Defaults to `URL of the image:`. - - **link**: The text to use when prompting for a link's URL. Defaults to `URL for the link:`. - **uploadImage**: If set to `true`, enables the image upload functionality, which can be triggered by drag&drop, copy-paste and through the browse-file window (opened when the user click on the *upload-image* icon). Defaults to `false`. - **imageMaxSize**: Maximum image size in bytes, checked before upload (note: never trust client, always check image size at server-side). Defaults to `1024*1024*2` (2Mb). - **imageAccept**: A comma-separated list of mime-types used to check image type before upload (note: never trust client, always check file types at server-side). Defaults to `image/png, image/jpeg`. @@ -162,18 +173,6 @@ easyMDE.value('New input for **EasyMDE**'); - otherwise: `{"error": ""}`, where *errorCode* can be `noFileGiven` (HTTP 400), `typeNotAllowed` (HTTP 415), `fileTooLarge` (HTTP 413) or `importError` (see *errorMessages* below). If *errorCode* is not one of the *errorMessages*, it is alerted unchanged to the user. This allows for server side error messages. No default value. - **imageCSRFToken**: CSRF token to include with AJAX call to upload image. For instance used with Django backend. -- **imageTexts**: Texts displayed to the user (mainly on the status bar) for the import image feature, where `#image_name#`, `#image_size#` and `#image_max_size#` will replaced by their respective values, that can be used for customization or internationalization: - - **sbInit**: Status message displayed initially if `uploadImage` is set to `true`. Defaults to `Attach files by drag and dropping or pasting from clipboard.`. - - **sbOnDragEnter**: Status message displayed when the user drags a file to the text area. Defaults to `Drop image to upload it.`. - - **sbOnDrop**: Status message displayed when the user drops a file in the text area. Defaults to `Uploading images #images_names#`. - - **sbProgress**: Status message displayed to show uploading progress. Defaults to `Uploading #file_name#: #progress#%`. - - **sbOnUploaded**: Status message displayed when the image has been uploaded. Defaults to `Uploaded #image_name#`. - - **sizeUnits**: A comma-separated list of units used to display messages with human-readable file sizes. Defaults to `b,Kb,Mb`. -- **errorMessages**: Errors displayed to the user, using the `errorCallback` option, where `#image_name#`, `#image_size#` and `#image_max_size#` will replaced by their respective values, that can be used for customization or internationalization: - - **noFileGiven**: The server did not receive any file from the user. Defaults to `You must select a file.`. - - **typeNotAllowed**: The user send a file type which doesn't match the `imageAccept` list, or the server returned this error code. Defaults to `This image type is not allowed.`. - - **fileTooLarge**: The size of the image being imported is bigger than the `imageMaxSize`, or if the server returned this error code. Defaults to `Image #image_name# is too big (#image_size#).\nMaximum file size is #image_max_size#.`. - - **importError**: An unexpected error occurred when uploading the image. Defaults to `Something went wrong when uploading the image #image_name#.`. - **errorCallback**: A callback function used to define how to display an error message. Defaults to `function(errorMessage) {alert(errorMessage);};`. - **renderingConfig**: Adjust settings for parsing the Markdown during previewing (not editing). - **codeSyntaxHighlighting**: If set to `true`, will highlight using [highlight.js](https://github.com/isagalaev/highlight.js). Defaults to `false`. To use this feature you must include highlight.js on your page or pass in using the `hljs` option. For example, include the script and the CSS files like:
``
`` @@ -202,6 +201,14 @@ Most options demonstrate the non-default behavior: ```JavaScript var editor = new EasyMDE({ + locale: 'en', + localization: { + "status": { + "lines": "lines", + "words": "words", + "autosave": "Autosaved: ", + }, + }, autofocus: true, autosave: { enabled: true, @@ -218,7 +225,6 @@ var editor = new EasyMDE({ minute: '2-digit', }, }, - text: "Autosaved: " }, blockStyles: { bold: "__", @@ -258,10 +264,6 @@ var editor = new EasyMDE({ return "Loading..."; }, promptURLs: true, - promptTexts: { - image: "Custom prompt for URL:", - link: "Custom prompt for URL:", - }, renderingConfig: { singleLineBreaks: false, codeSyntaxHighlighting: true, @@ -294,7 +296,66 @@ var editor = new EasyMDE({ toolbarTips: false, }); ``` +### Localization example +```JavaScript +var editor = new EasyMDE({ + locale: 'en', + localization: { + "promptTexts": { + "link": "URL for the link:", + "image": "URL of the image:" + }, + "status": { + "lines": "lines", + "words": "words", + "autosave": "Autosaved: " + }, + "errorMessages": { + "noFileGiven": "You must select a file.", + "typeNotAllowed": "This image type is not allowed.", + "fileTooLarge": "Image #image_name# is too big (#image_size#).\nMaximum file size is #image_max_size#.", + "importError": "Something went wrong when uploading the image #image_name#." + }, + "imageTexts": { + "sbInit": "Attach files by drag and dropping or pasting from clipboard.", + "sbOnDragEnter": "Drop image to upload it.", + "sbOnDrop": "Uploading image #images_names#...", + "sbProgress": "Uploading #file_name#: #progress#%", + "sbOnUploaded": "Uploaded #image_name#", + "sizeUnits": "b,Kb,Mb" + }, + "toolbar": { + "bold": {"title": "Bold"}, + "italic": {"title": "Italic"}, + "strikethrough": {"title": "Strikethrough"}, + "heading": {"title": "Heading"}, + "heading-smaller": {"title": "Smaller Heading"}, + "heading-bigger": {"title": "Bigger Heading"}, + "heading-1": {"title": "Big Heading"}, + "heading-2": {"title": "Medium Heading"}, + "heading-3": {"title": "Small Heading"}, + "code": {"title": "Code"}, + "quote": {"title": "Quote"}, + "unordered-list": {"title": "Generic List"}, + "ordered-list": {"title": "Numbered List"}, + "clean-block": {"title": "Clean block"}, + "link": {"title": "Create Link"}, + "image": {"title": "Insert Image"}, + "upload-image": {"title": "Import an image"}, + "table": {"title": "Insert Table"}, + "horizontal-rule": {"title": "Insert Horizontal Line"}, + "preview": {"title": "Toggle Preview"}, + "side-by-side": {"title": "Toggle Side by Side"}, + "fullscreen": {"title": "Toggle Fullscreen"}, + "guide": {"title": "Markdown Guide"}, + "undo": {"title": "Undo"}, + "redo": {"title": "Redo"} + } + }, + ... +}); +``` ### Toolbar icons diff --git a/src/css/easymde.css b/src/css/easymde.css index 92cf821..5dafac2 100644 --- a/src/css/easymde.css +++ b/src/css/easymde.css @@ -205,12 +205,12 @@ content: 'lines: ' } -.editor-statusbar .words:before { - content: 'words: ' +.editor-statusbar .lines:before { + content: attr(data-status-bar-before) ': '; } -.editor-statusbar .characters:before { - content: 'characters: ' +.editor-statusbar .words:before { + content: attr(data-status-bar-before) ': '; } .editor-preview-full { diff --git a/src/js/easymde.js b/src/js/easymde.js index d64a52f..9d38c52 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -11,7 +11,8 @@ require('codemirror/addon/search/searchcursor.js'); require('codemirror/mode/gfm/gfm.js'); require('codemirror/mode/xml/xml.js'); var CodeMirrorSpellChecker = require('codemirror-spell-checker'); -var marked = require('marked/lib/marked'); +var marked = require('marked'); +var localization = require('./languages.json'); // Some variables @@ -730,7 +731,7 @@ function drawLink(editor) { var options = editor.options; var url = 'https://'; if (options.promptURLs) { - url = prompt(options.promptTexts.link, 'https://'); + url = prompt(options.locale.promptTexts.link, 'https://'); if (!url) { return false; } @@ -747,7 +748,7 @@ function drawImage(editor) { var options = editor.options; var url = 'https://'; if (options.promptURLs) { - url = prompt(options.promptTexts.image, 'https://'); + url = prompt(options.locale.promptTexts.image, 'https://'); if (!url) { return false; } @@ -776,9 +777,9 @@ function afterImageUploaded(editor, url) { var imageName = url.substr(url.lastIndexOf('/') + 1); _replaceSelection(cm, stat.image, options.insertTexts.uploadedImage, url); // show uploaded image filename for 1000ms - editor.updateStatusBar('upload-image', editor.options.imageTexts.sbOnUploaded.replace('#image_name#', imageName)); + editor.updateStatusBar('upload-image', editor.options.locale.imageTexts.sbOnUploaded.replace('#image_name#', imageName)); setTimeout(function () { - editor.updateStatusBar('upload-image', editor.options.imageTexts.sbInit); + editor.updateStatusBar('upload-image', editor.options.locale.imageTexts.sbInit); }, 1000); } @@ -1292,58 +1293,49 @@ var toolbarBuiltInButtons = { name: 'bold', action: toggleBold, className: 'fa fa-bold', - title: 'Bold', default: true, }, 'italic': { name: 'italic', action: toggleItalic, className: 'fa fa-italic', - title: 'Italic', default: true, }, 'strikethrough': { name: 'strikethrough', action: toggleStrikethrough, className: 'fa fa-strikethrough', - title: 'Strikethrough', }, 'heading': { name: 'heading', action: toggleHeadingSmaller, className: 'fa fa-header fa-heading', - title: 'Heading', default: true, }, 'heading-smaller': { name: 'heading-smaller', action: toggleHeadingSmaller, className: 'fa fa-header fa-heading header-smaller', - title: 'Smaller Heading', }, 'heading-bigger': { name: 'heading-bigger', action: toggleHeadingBigger, className: 'fa fa-header fa-heading header-bigger', - title: 'Bigger Heading', }, 'heading-1': { name: 'heading-1', action: toggleHeading1, className: 'fa fa-header fa-heading header-1', - title: 'Big Heading', }, 'heading-2': { name: 'heading-2', action: toggleHeading2, className: 'fa fa-header fa-heading header-2', - title: 'Medium Heading', }, 'heading-3': { name: 'heading-3', action: toggleHeading3, className: 'fa fa-header fa-heading header-3', - title: 'Small Heading', }, 'separator-1': { name: 'separator-1', @@ -1352,34 +1344,29 @@ var toolbarBuiltInButtons = { name: 'code', action: toggleCodeBlock, className: 'fa fa-code', - title: 'Code', }, 'quote': { name: 'quote', action: toggleBlockquote, className: 'fa fa-quote-left', - title: 'Quote', default: true, }, 'unordered-list': { name: 'unordered-list', action: toggleUnorderedList, className: 'fa fa-list-ul', - title: 'Generic List', default: true, }, 'ordered-list': { name: 'ordered-list', action: toggleOrderedList, className: 'fa fa-list-ol', - title: 'Numbered List', default: true, }, 'clean-block': { name: 'clean-block', action: cleanBlock, className: 'fa fa-eraser', - title: 'Clean block', }, 'separator-2': { name: 'separator-2', @@ -1388,33 +1375,28 @@ var toolbarBuiltInButtons = { name: 'link', action: drawLink, className: 'fa fa-link', - title: 'Create Link', default: true, }, 'image': { name: 'image', action: drawImage, className: 'fa fa-image', - title: 'Insert Image', default: true, }, 'upload-image': { name: 'upload-image', action: drawUploadedImage, className: 'fa fa-image', - title: 'Import an image', }, 'table': { name: 'table', action: drawTable, className: 'fa fa-table', - title: 'Insert Table', }, 'horizontal-rule': { name: 'horizontal-rule', action: drawHorizontalRule, className: 'fa fa-minus', - title: 'Insert Horizontal Line', }, 'separator-3': { name: 'separator-3', @@ -1424,7 +1406,6 @@ var toolbarBuiltInButtons = { action: togglePreview, className: 'fa fa-eye', noDisable: true, - title: 'Toggle Preview', default: true, }, 'side-by-side': { @@ -1433,7 +1414,6 @@ var toolbarBuiltInButtons = { className: 'fa fa-columns', noDisable: true, noMobile: true, - title: 'Toggle Side by Side', default: true, }, 'fullscreen': { @@ -1442,7 +1422,6 @@ var toolbarBuiltInButtons = { className: 'fa fa-arrows-alt', noDisable: true, noMobile: true, - title: 'Toggle Fullscreen', default: true, }, 'separator-4': { @@ -1453,7 +1432,6 @@ var toolbarBuiltInButtons = { action: 'https://www.markdownguide.org/basic-syntax/', className: 'fa fa-question-circle', noDisable: true, - title: 'Markdown Guide', default: true, }, 'separator-5': { @@ -1464,14 +1442,12 @@ var toolbarBuiltInButtons = { action: undo, className: 'fa fa-undo', noDisable: true, - title: 'Undo', }, 'redo': { name: 'redo', action: redo, className: 'fa fa-repeat fa-redo', noDisable: true, - title: 'Redo', }, }; @@ -1484,11 +1460,6 @@ var insertTexts = { horizontalRule: ['', '\n\n-----\n\n'], }; -var promptTexts = { - link: 'URL for the link:', - image: 'URL of the image:', -}; - var timeFormat = { locale: 'en-US', format: { @@ -1503,31 +1474,6 @@ var blockStyles = { 'italic': '*', }; -/** - * Texts displayed to the user (mainly on the status bar) for the import image - * feature. Can be used for customization or internationalization. - */ -var imageTexts = { - sbInit: 'Attach files by drag and dropping or pasting from clipboard.', - sbOnDragEnter: 'Drop image to upload it.', - sbOnDrop: 'Uploading image #images_names#...', - sbProgress: 'Uploading #file_name#: #progress#%', - sbOnUploaded: 'Uploaded #image_name#', - sizeUnits: 'b,Kb,Mb', -}; - -/** - * Errors displayed to the user, using the `errorCallback` option. Can be used for - * customization or internationalization. - */ -var errorMessages = { - noFileGiven: 'You must select a file.', - typeNotAllowed: 'This image type is not allowed.', - fileTooLarge: 'Image #image_name# is too big (#image_size#).\n' + - 'Maximum file size is #image_max_size#.', - importError: 'Something went wrong when uploading the image #image_name#.', -}; - /** * Interface of EasyMDE. */ @@ -1625,24 +1571,22 @@ function EasyMDE(options) { }, options.parsingConfig || {}); - // Merging the insertTexts, with the given options + // Merging localizations + options.locale = extend(localization['en'], localization[options.locale], options.localization || {}); + options.insertTexts = extend({}, insertTexts, options.insertTexts || {}); - // Merging the promptTexts, with the given options - options.promptTexts = extend({}, promptTexts, options.promptTexts || {}); - - - // Merging the blockStyles, with the given options - options.blockStyles = extend({}, blockStyles, options.blockStyles || {}); - - if (options.autosave != undefined) { // Merging the Autosave timeFormat, with the given options options.autosave.timeFormat = extend({}, timeFormat, options.autosave.timeFormat || {}); } + // Merging the blockStyles, with the given options + options.blockStyles = extend({}, blockStyles, options.blockStyles || {}); + + // Merging the shortcuts, with the given options options.shortcuts = extend({}, shortcuts, options.shortcuts || {}); @@ -1656,8 +1600,6 @@ function EasyMDE(options) { options.uploadImage = options.uploadImage || false; options.imageMaxSize = options.imageMaxSize || 2097152; // 1024 * 1024 * 2 options.imageAccept = options.imageAccept || 'image/png, image/jpeg'; - options.imageTexts = extend({}, imageTexts, options.imageTexts || {}); - options.errorMessages = extend({}, errorMessages, options.errorMessages || {}); // Change unique_id to uniqueId for backwards compatibility @@ -1684,23 +1626,23 @@ function EasyMDE(options) { var self = this; this.codemirror.on('dragenter', function (cm, event) { - self.updateStatusBar('upload-image', self.options.imageTexts.sbOnDragEnter); + self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbOnDragEnter); event.stopPropagation(); event.preventDefault(); }); this.codemirror.on('dragend', function (cm, event) { - self.updateStatusBar('upload-image', self.options.imageTexts.sbInit); + self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbInit); event.stopPropagation(); event.preventDefault(); }); this.codemirror.on('dragleave', function (cm, event) { - self.updateStatusBar('upload-image', self.options.imageTexts.sbInit); + self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbInit); event.stopPropagation(); event.preventDefault(); }); this.codemirror.on('dragover', function (cm, event) { - self.updateStatusBar('upload-image', self.options.imageTexts.sbOnDragEnter); + self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbOnDragEnter); event.stopPropagation(); event.preventDefault(); }); @@ -1745,7 +1687,7 @@ EasyMDE.prototype.uploadImages = function (files, onSuccess, onError) { names.push(files[i].name); this.uploadImage(files[i], onSuccess, onError); } - this.updateStatusBar('upload-image', this.options.imageTexts.sbOnDrop.replace('#images_names#', names.join(', '))); + this.updateStatusBar('upload-image', this.options.locale.imageTexts.sbOnDrop.replace('#images_names#', names.join(', '))); }; /** @@ -1767,7 +1709,7 @@ EasyMDE.prototype.uploadImagesUsingCustomFunction = function (imageUploadFunctio names.push(files[i].name); this.uploadImageUsingCustomFunction(imageUploadFunction, files[i]); } - this.updateStatusBar('upload-image', this.options.imageTexts.sbOnDrop.replace('#images_names#', names.join(', '))); + this.updateStatusBar('upload-image', this.options.locale.imageTexts.sbOnDrop.replace('#images_names#', names.join(', '))); }; /** @@ -2025,7 +1967,7 @@ EasyMDE.prototype.autosave = function () { if (el != null && el != undefined && el != '') { var d = new Date(); var dd = new Intl.DateTimeFormat([this.options.autosave.timeFormat.locale, 'en-US'], this.options.autosave.timeFormat.format).format(d); - var save = this.options.autosave.text == undefined ? 'Autosaved: ' : this.options.autosave.text; + var save = this.options.locale.status.autosave; el.innerHTML = save + dd; } @@ -2092,7 +2034,7 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) { self.updateStatusBar('upload-image', errorMessage); setTimeout(function () { - self.updateStatusBar('upload-image', self.options.imageTexts.sbInit); + self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbInit); }, 10000); // run custom error handler @@ -2104,7 +2046,7 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) { } function fillErrorMessage(errorMessage) { - var units = self.options.imageTexts.sizeUnits.split(','); + var units = self.options.locale.imageTexts.sizeUnits.split(','); return errorMessage .replace('#image_name#', file.name) .replace('#image_size#', humanFileSize(file.size, units)) @@ -2112,7 +2054,7 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) { } if (file.size > this.options.imageMaxSize) { - onErrorSup(fillErrorMessage(this.options.errorMessages.fileTooLarge)); + onErrorSup(fillErrorMessage(this.options.locale.errorMessages.fileTooLarge)); return; } @@ -2127,7 +2069,7 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) { request.upload.onprogress = function (event) { if (event.lengthComputable) { var progress = '' + Math.round((event.loaded * 100) / event.total); - self.updateStatusBar('upload-image', self.options.imageTexts.sbProgress.replace('#file_name#', file.name).replace('#progress#', progress)); + self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbProgress.replace('#file_name#', file.name).replace('#progress#', progress)); } }; request.open('POST', this.options.imageUploadEndpoint); @@ -2137,20 +2079,20 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) { var response = JSON.parse(this.responseText); } catch (error) { console.error('EasyMDE: The server did not return a valid json.'); - onErrorSup(fillErrorMessage(self.options.errorMessages.importError)); + onErrorSup(fillErrorMessage(self.options.locale.errorMessages.importError)); return; } if (this.status === 200 && response && !response.error && response.data && response.data.filePath) { onSuccess(window.location.origin + '/' + response.data.filePath); } else { - if (response.error && response.error in self.options.errorMessages) { // preformatted error message - onErrorSup(fillErrorMessage(self.options.errorMessages[response.error])); + if (response.error && response.error in self.options.locale.errorMessages) { // preformatted error message + onErrorSup(fillErrorMessage(self.options.locale.errorMessages[response.error])); } else if (response.error) { // server side generated error message onErrorSup(fillErrorMessage(response.error)); } else { //unknown error console.error('EasyMDE: Received an unexpected response after uploading the image.' + this.status + ' (' + this.statusText + ')'); - onErrorSup(fillErrorMessage(self.options.errorMessages.importError)); + onErrorSup(fillErrorMessage(self.options.locale.errorMessages.importError)); } } }; @@ -2158,7 +2100,7 @@ EasyMDE.prototype.uploadImage = function (file, onSuccess, onError) { request.onerror = function (event) { console.error('EasyMDE: An unexpected error occurred when trying to upload the image.' + event.target.status + ' (' + event.target.statusText + ')'); - onErrorSup(self.options.errorMessages.importError); + onErrorSup(self.options.locale.errorMessages.importError); }; request.send(formData); @@ -2183,7 +2125,7 @@ EasyMDE.prototype.uploadImageUsingCustomFunction = function(imageUploadFunction, self.updateStatusBar('upload-image', filledErrorMessage); setTimeout(function () { - self.updateStatusBar('upload-image', self.options.imageTexts.sbInit); + self.updateStatusBar('upload-image', self.options.locale.imageTexts.sbInit); }, 10000); // run error handler from options, this alerts the message. @@ -2191,7 +2133,7 @@ EasyMDE.prototype.uploadImageUsingCustomFunction = function(imageUploadFunction, } function fillErrorMessage(errorMessage) { - var units = self.options.imageTexts.sizeUnits.split(','); + var units = self.options.locale.imageTexts.sizeUnits.split(','); return errorMessage .replace('#image_name#', file.name) .replace('#image_size#', humanFileSize(file.size, units)) @@ -2265,7 +2207,7 @@ EasyMDE.prototype.createToolbar = function (items) { var i; for (i = 0; i < items.length; i++) { if (toolbarBuiltInButtons[items[i]] != undefined) { - items[i] = toolbarBuiltInButtons[items[i]]; + items[i] = extend({}, toolbarBuiltInButtons[items[i]], this.options.locale.toolbar[items[i]] || {}); } } @@ -2372,25 +2314,30 @@ EasyMDE.prototype.createStatusbar = function (status) { // Set up the built-in items var items = []; - var i, onUpdate, defaultValue; + var i, onUpdate, onCursorActivity, defaultValue, dataSet; for (i = 0; i < status.length; i++) { // Reset some values onUpdate = undefined; + onCursorActivity = undefined; defaultValue = undefined; - + dataSet = undefined; // Handle if custom or not if (typeof status[i] === 'object') { items.push({ className: status[i].className, + dataSet: status[i].dataSet, defaultValue: status[i].defaultValue, onUpdate: status[i].onUpdate, + onCursorActivity: status[i].onCursorActivity, }); } else { var name = status[i]; if (name === 'words') { + dataSet = options.locale.status[name]; + defaultValue = function (el) { el.innerHTML = wordCount(cm.getValue()); }; @@ -2398,6 +2345,8 @@ EasyMDE.prototype.createStatusbar = function (status) { el.innerHTML = wordCount(cm.getValue()); }; } else if (name === 'lines') { + dataSet = options.locale.status[name]; + defaultValue = function (el) { el.innerHTML = cm.lineCount(); }; @@ -2410,6 +2359,12 @@ EasyMDE.prototype.createStatusbar = function (status) { }; onUpdate = function (el) { var pos = cm.getCursor(); + + el.innerHTML = pos.line + ':' + pos.ch; + }; + onCursorActivity = function (el) { + var pos = cm.getCursor(); + el.innerHTML = pos.line + ':' + pos.ch; }; } else if (name === 'autosave') { @@ -2420,14 +2375,16 @@ EasyMDE.prototype.createStatusbar = function (status) { }; } else if (name === 'upload-image') { defaultValue = function (el) { - el.innerHTML = options.imageTexts.sbInit; + el.innerHTML = options.locale.imageTexts.sbInit; }; } items.push({ className: name, + dataSet: dataSet, defaultValue: defaultValue, onUpdate: onUpdate, + onCursorActivity: onCursorActivity, }); } } @@ -2448,6 +2405,10 @@ EasyMDE.prototype.createStatusbar = function (status) { var el = document.createElement('span'); el.className = item.className; + if (item.dataSet != undefined) { + el.dataset.statusBarBefore = item.dataSet; + } + // Ensure the defaultValue is a function if (typeof item.defaultValue === 'function') { @@ -2465,6 +2426,15 @@ EasyMDE.prototype.createStatusbar = function (status) { }(el, item))); } + // Ensure the onCursorActivity is a function + if (typeof item.onCursorActivity === 'function') { + // Create a closure around the span of the current action, then execute the onCursorActivity handler + this.codemirror.on('cursorActivity', (function (el, item) { + return function () { + item.onCursorActivity(el); + }; + }(el, item))); + } // Append the item to the status bar bar.appendChild(el); diff --git a/src/js/languages.json b/src/js/languages.json new file mode 100644 index 0000000..2aa3932 --- /dev/null +++ b/src/js/languages.json @@ -0,0 +1,66 @@ + +{ + "en": { + "promptTexts": { + "link": "URL for the link:", + "image": "URL of the image:" + }, + "status": { + "lines": "lines", + "words": "words", + "autosave": "Autosaved: " + }, + "errorMessages": { + "noFileGiven": "You must select a file.", + "typeNotAllowed": "This image type is not allowed.", + "fileTooLarge": "Image #image_name# is too big (#image_size#).\nMaximum file size is #image_max_size#.", + "importError": "Something went wrong when uploading the image #image_name#." + }, + "imageTexts": { + "sbInit": "Attach files by drag and dropping or pasting from clipboard.", + "sbOnDragEnter": "Drop image to upload it.", + "sbOnDrop": "Uploading image #images_names#...", + "sbProgress": "Uploading #file_name#: #progress#%", + "sbOnUploaded": "Uploaded #image_name#", + "sizeUnits": "b,Kb,Mb" + }, + "toolbar": { + "bold": {"title": "Bold"}, + "italic": {"title": "Italic"}, + "strikethrough": {"title": "Strikethrough"}, + "heading": {"title": "Heading"}, + "heading-smaller": {"title": "Smaller Heading"}, + "heading-bigger": {"title": "Bigger Heading"}, + "heading-1": {"title": "Big Heading"}, + "heading-2": {"title": "Medium Heading"}, + "heading-3": {"title": "Small Heading"}, + "code": {"title": "Code"}, + "quote": {"title": "Quote"}, + "unordered-list": {"title": "Generic List"}, + "ordered-list": {"title": "Numbered List"}, + "clean-block": {"title": "Clean block"}, + "link": {"title": "Create Link"}, + "image": {"title": "Insert Image"}, + "upload-image": {"title": "Import an image"}, + "table": {"title": "Insert Table"}, + "horizontal-rule": {"title": "Insert Horizontal Line"}, + "preview": {"title": "Toggle Preview"}, + "side-by-side": {"title": "Toggle Side by Side"}, + "fullscreen": {"title": "Toggle Fullscreen"}, + "guide": {"title": "Markdown Guide"}, + "undo": {"title": "Undo"}, + "redo": {"title": "Redo"} + } + }, + "ru": { + "promptTexts": { + "link": "Введите гиперссылку:", + "image": "Вставьте ссылку на изображение:" + }, + "status": { + "lines": "строк", + "words": "слов", + "autosave": "Автосохранение: " + } + } +} \ No newline at end of file