From 49173abdade56a598b4bdbb3921090ed98f8a5d3 Mon Sep 17 00:00:00 2001 From: davcs86 Date: Thu, 25 Feb 2016 10:40:56 -0600 Subject: [PATCH] feat(i18n): add localization * Palette, ContextPad, Errors... Closes #491 --- lib/Viewer.js | 3 +- .../context-pad/ContextPadProvider.js | 32 ++++++----- lib/features/modeling/BpmnUpdater.js | 16 ++++-- lib/features/modeling/ElementFactory.js | 10 ++-- .../modeling/behavior/ModelingFeedback.js | 6 +- .../behavior/UpdateFlowNodeRefsBehavior.js | 8 +-- lib/features/modeling/cmd/SplitLaneHandler.js | 10 ++-- .../modeling/cmd/UpdatePropertiesHandler.js | 10 ++-- lib/features/ordering/BpmnOrderingProvider.js | 9 ++- lib/features/ordering/index.js | 3 + lib/features/palette/PaletteProvider.js | 22 ++++--- lib/features/palette/index.js | 3 +- .../popup-menu/ReplaceMenuProvider.js | 19 ++++--- lib/import/BpmnImporter.js | 42 +++++++++----- lib/import/BpmnTreeWalker.js | 57 ++++++++++++++----- lib/import/Importer.js | 5 +- lib/import/index.js | 3 + test/spec/ModelerSpec.js | 44 +++++++++++--- .../i18n/custom-translate/custom-translate.js | 15 +++++ test/spec/i18n/custom-translate/index.js | 3 + test/spec/i18n/translateSpec.js | 53 +++++++++++++++++ 21 files changed, 274 insertions(+), 99 deletions(-) create mode 100644 test/spec/i18n/custom-translate/custom-translate.js create mode 100644 test/spec/i18n/custom-translate/index.js create mode 100644 test/spec/i18n/translateSpec.js diff --git a/lib/Viewer.js b/lib/Viewer.js index 6705e03d..f38761de 100644 --- a/lib/Viewer.js +++ b/lib/Viewer.js @@ -419,6 +419,7 @@ Viewer.prototype.off = function(event, callback) { // modules the viewer is composed of Viewer.prototype._modules = [ require('./core'), + require('diagram-js/lib/i18n/translate'), require('diagram-js/lib/features/selection'), require('diagram-js/lib/features/overlays') ]; @@ -462,4 +463,4 @@ function addProjectLogo(container) { }); } -/* */ +/* */ \ No newline at end of file diff --git a/lib/features/context-pad/ContextPadProvider.js b/lib/features/context-pad/ContextPadProvider.js index 1637c57a..b852594c 100644 --- a/lib/features/context-pad/ContextPadProvider.js +++ b/lib/features/context-pad/ContextPadProvider.js @@ -15,7 +15,7 @@ var assign = require('lodash/object/assign'), */ function ContextPadProvider(contextPad, modeling, elementFactory, connect, create, popupMenu, - canvas, rules) { + canvas, rules, translate) { contextPad.registerProvider(this); @@ -29,6 +29,7 @@ function ContextPadProvider(contextPad, modeling, elementFactory, this._popupMenu = popupMenu; this._canvas = canvas; this._rules = rules; + this._translate = translate; } ContextPadProvider.$inject = [ @@ -39,7 +40,8 @@ ContextPadProvider.$inject = [ 'create', 'popupMenu', 'canvas', - 'rules' + 'rules', + 'translate' ]; module.exports = ContextPadProvider; @@ -55,7 +57,9 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { create = this._create, popupMenu = this._popupMenu, canvas = this._canvas, - rules = this._rules; + rules = this._rules, + + translate = this._translate; var actions = {}; @@ -109,7 +113,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { if (typeof title !== 'string') { options = title; - title = 'Append ' + type.replace(/^bpmn\:/, ''); + title = translate('Append {type}', { type: type.replace(/^bpmn\:/, '') }); } function appendListener(event, element) { @@ -149,7 +153,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { 'lane-insert-above': { group: 'lane-insert-above', className: 'bpmn-icon-lane-insert-above', - title: 'Add Lane above', + title: translate('Add Lane above'), action: { click: function(event, element) { modeling.addLane(element, 'top'); @@ -165,7 +169,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { 'lane-divide-two': { group: 'lane-divide', className: 'bpmn-icon-lane-divide-two', - title: 'Divide into two Lanes', + title: translate('Divide into two Lanes'), action: { click: splitLaneHandler(2) } @@ -178,7 +182,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { 'lane-divide-three': { group: 'lane-divide', className: 'bpmn-icon-lane-divide-three', - title: 'Divide into three Lanes', + title: translate('Divide into three Lanes'), action: { click: splitLaneHandler(3) } @@ -191,7 +195,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { 'lane-insert-below': { group: 'lane-insert-below', className: 'bpmn-icon-lane-insert-below', - title: 'Add Lane below', + title: translate('Add Lane below'), action: { click: function(event, element) { modeling.addLane(element, 'bottom'); @@ -227,7 +231,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { assign(actions, { 'append.compensation-activity': - appendAction('bpmn:Task', 'bpmn-icon-task', 'Append compensation activity', { + appendAction('bpmn:Task', 'bpmn-icon-task', translate('Append compensation activity'), { isForCompensation: true }) }); @@ -261,7 +265,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { 'replace': { group: 'edit', className: 'bpmn-icon-screw-wrench', - title: 'Change type', + title: translate('Change type'), action: { click: function(event, element) { replaceMenu.open(assign(getReplaceMenuPosition(element), { @@ -281,9 +285,9 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { 'connect': { group: 'connect', className: 'bpmn-icon-connection-multi', - title: 'Connect using ' + + title: translate('Connect using ' + (businessObject.isForCompensation ? '' : 'Sequence/MessageFlow or ') + - 'Association', + 'Association'), action: { click: startConnect, dragstart: startConnect @@ -297,7 +301,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { 'connect': { group: 'connect', className: 'bpmn-icon-connection-multi', - title: 'Connect using DataInputAssociation', + title: translate('Connect using DataInputAssociation'), action: { click: startConnect, dragstart: startConnect @@ -319,7 +323,7 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) { 'delete': { group: 'edit', className: 'bpmn-icon-trash', - title: 'Remove', + title: translate('Remove'), action: { click: removeElement, dragstart: removeElement diff --git a/lib/features/modeling/BpmnUpdater.js b/lib/features/modeling/BpmnUpdater.js index 1664b3fa..974fd627 100644 --- a/lib/features/modeling/BpmnUpdater.js +++ b/lib/features/modeling/BpmnUpdater.js @@ -17,11 +17,12 @@ var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor'); * A handler responsible for updating the underlying BPMN 2.0 XML + DI * once changes on the diagram happen */ -function BpmnUpdater(eventBus, bpmnFactory, connectionDocking) { +function BpmnUpdater(eventBus, bpmnFactory, connectionDocking, translate) { CommandInterceptor.call(this, eventBus); this._bpmnFactory = bpmnFactory; + this._translate = translate; var self = this; @@ -247,7 +248,7 @@ inherits(BpmnUpdater, CommandInterceptor); module.exports = BpmnUpdater; -BpmnUpdater.$inject = [ 'eventBus', 'bpmnFactory', 'connectionDocking' ]; +BpmnUpdater.$inject = [ 'eventBus', 'bpmnFactory', 'connectionDocking', 'translate' ]; /////// implementation ////////////////////////////////// @@ -402,7 +403,8 @@ BpmnUpdater.prototype.getLaneSet = function(container) { BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent) { - var containment; + var containment, + translate = this._translate; if (businessObject.$parent === newParent) { return; @@ -494,7 +496,13 @@ BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent) } if (!containment) { - throw new Error('no parent for ', businessObject, newParent); + throw new Error(translate( + 'no parent for {element} in {parent}', + { + element: businessObject.id, + parent: newParent.id + } + )); } var children; diff --git a/lib/features/modeling/ElementFactory.js b/lib/features/modeling/ElementFactory.js index a1a5f83c..5e073578 100644 --- a/lib/features/modeling/ElementFactory.js +++ b/lib/features/modeling/ElementFactory.js @@ -12,17 +12,18 @@ var BaseElementFactory = require('diagram-js/lib/core/ElementFactory'), /** * A bpmn-aware factory for diagram-js shapes */ -function ElementFactory(bpmnFactory, moddle) { +function ElementFactory(bpmnFactory, moddle, translate) { BaseElementFactory.call(this); this._bpmnFactory = bpmnFactory; this._moddle = moddle; + this._translate = translate; } inherits(ElementFactory, BaseElementFactory); -ElementFactory.$inject = [ 'bpmnFactory', 'moddle' ]; +ElementFactory.$inject = [ 'bpmnFactory', 'moddle', 'translate' ]; module.exports = ElementFactory; @@ -40,7 +41,8 @@ ElementFactory.prototype.create = function(elementType, attrs) { }; ElementFactory.prototype.createBpmnElement = function(elementType, attrs) { - var size; + var size, + translate = this._translate; attrs = attrs || {}; @@ -48,7 +50,7 @@ ElementFactory.prototype.createBpmnElement = function(elementType, attrs) { if (!businessObject) { if (!attrs.type) { - throw new Error('no shape type specified'); + throw new Error(translate('no shape type specified')); } businessObject = this._bpmnFactory.create(attrs.type); diff --git a/lib/features/modeling/behavior/ModelingFeedback.js b/lib/features/modeling/behavior/ModelingFeedback.js index e4f20a9c..8d820e37 100644 --- a/lib/features/modeling/behavior/ModelingFeedback.js +++ b/lib/features/modeling/behavior/ModelingFeedback.js @@ -3,7 +3,7 @@ var is = require('../../../util/ModelUtil').is; -function ModelingFeedback(eventBus, tooltips) { +function ModelingFeedback(eventBus, tooltips, translate) { function showError(position, message) { tooltips.add({ @@ -24,13 +24,13 @@ function ModelingFeedback(eventBus, tooltips) { target = context.target; if (is(target, 'bpmn:Collaboration') && is(shape, 'bpmn:FlowNode')) { - showError(event, 'flow elements must be children of pools/participants'); + showError(event, translate('flow elements must be children of pools/participants')); } }); } -ModelingFeedback.$inject = [ 'eventBus', 'tooltips' ]; +ModelingFeedback.$inject = [ 'eventBus', 'tooltips', 'translate' ]; module.exports = ModelingFeedback; \ No newline at end of file diff --git a/lib/features/modeling/behavior/UpdateFlowNodeRefsBehavior.js b/lib/features/modeling/behavior/UpdateFlowNodeRefsBehavior.js index 03ab9258..ca652411 100644 --- a/lib/features/modeling/behavior/UpdateFlowNodeRefsBehavior.js +++ b/lib/features/modeling/behavior/UpdateFlowNodeRefsBehavior.js @@ -14,7 +14,7 @@ var LOW_PRIORITY = 500, /** * BPMN specific delete lane behavior */ -function UpdateFlowNodeRefsBehavior(eventBus, modeling) { +function UpdateFlowNodeRefsBehavior(eventBus, modeling, translate) { CommandInterceptor.call(this, eventBus); @@ -44,7 +44,7 @@ function UpdateFlowNodeRefsBehavior(eventBus, modeling) { function getContext() { if (!context) { - throw new Error('out of bounds release'); + throw new Error(translate('out of bounds release')); } return context; @@ -53,7 +53,7 @@ function UpdateFlowNodeRefsBehavior(eventBus, modeling) { function releaseContext() { if (!context) { - throw new Error('out of bounds release'); + throw new Error(translate('out of bounds release')); } var triggerUpdate = context.leave(); @@ -122,7 +122,7 @@ function UpdateFlowNodeRefsBehavior(eventBus, modeling) { }); } -UpdateFlowNodeRefsBehavior.$inject = [ 'eventBus', 'modeling' ]; +UpdateFlowNodeRefsBehavior.$inject = [ 'eventBus', 'modeling' , 'translate']; inherits(UpdateFlowNodeRefsBehavior, CommandInterceptor); diff --git a/lib/features/modeling/cmd/SplitLaneHandler.js b/lib/features/modeling/cmd/SplitLaneHandler.js index 19a328fb..fa35ce33 100644 --- a/lib/features/modeling/cmd/SplitLaneHandler.js +++ b/lib/features/modeling/cmd/SplitLaneHandler.js @@ -10,18 +10,20 @@ var LANE_INDENTATION = require('../util/LaneUtil').LANE_INDENTATION; * * @param {Modeling} modeling */ -function SplitLaneHandler(modeling) { +function SplitLaneHandler(modeling, translate) { this._modeling = modeling; + this._translate = translate; } -SplitLaneHandler.$inject = [ 'modeling' ]; +SplitLaneHandler.$inject = [ 'modeling', 'translate']; module.exports = SplitLaneHandler; SplitLaneHandler.prototype.preExecute = function(context) { - var modeling = this._modeling; + var modeling = this._modeling, + translate = this._translate; var shape = context.shape, newLanesCount = context.count; @@ -30,7 +32,7 @@ SplitLaneHandler.prototype.preExecute = function(context) { existingLanesCount = childLanes.length; if (existingLanesCount > newLanesCount) { - throw new Error('more than ' + newLanesCount + ' child lanes'); + throw new Error(translate('more than {count} child lanes', { count: newLanesCount })); } var newLanesHeight = Math.round(shape.height / newLanesCount); diff --git a/lib/features/modeling/cmd/UpdatePropertiesHandler.js b/lib/features/modeling/cmd/UpdatePropertiesHandler.js index a9c98505..649e1388 100644 --- a/lib/features/modeling/cmd/UpdatePropertiesHandler.js +++ b/lib/features/modeling/cmd/UpdatePropertiesHandler.js @@ -21,12 +21,13 @@ var DEFAULT_FLOW = 'default', * Use respective diagram-js provided handlers if you would * like to perform automated modeling. */ -function UpdatePropertiesHandler(elementRegistry, moddle) { +function UpdatePropertiesHandler(elementRegistry, moddle, translate) { this._elementRegistry = elementRegistry; this._moddle = moddle; + this._translate = translate; } -UpdatePropertiesHandler.$inject = [ 'elementRegistry', 'moddle' ]; +UpdatePropertiesHandler.$inject = [ 'elementRegistry', 'moddle', 'translate' ]; module.exports = UpdatePropertiesHandler; @@ -46,10 +47,11 @@ module.exports = UpdatePropertiesHandler; UpdatePropertiesHandler.prototype.execute = function(context) { var element = context.element, - changed = [ element ]; + changed = [ element], + translate = this._translate; if (!element) { - throw new Error('element required'); + throw new Error(translate('element required')); } var elementRegistry = this._elementRegistry, diff --git a/lib/features/ordering/BpmnOrderingProvider.js b/lib/features/ordering/BpmnOrderingProvider.js index 79d45489..a230f8a9 100644 --- a/lib/features/ordering/BpmnOrderingProvider.js +++ b/lib/features/ordering/BpmnOrderingProvider.js @@ -17,7 +17,7 @@ var find = require('lodash/collection/find'); * (1) elements are ordered by a {level} property * (2) elements with {alwaysOnTop} are always added to the root */ -function BpmnOrderingProvider(eventBus) { +function BpmnOrderingProvider(eventBus, translate) { OrderingProvider.call(this, eventBus); @@ -84,7 +84,10 @@ function BpmnOrderingProvider(eventBus) { } if (!actualParent) { - throw new Error('no parent for ' + element.id + ' in ' + newParent.id); + throw new Error(translate('no parent for {element} in {parent}', { + element: element.id, + parent: newParent.id + })); } return actualParent; @@ -132,7 +135,7 @@ function BpmnOrderingProvider(eventBus) { }; } -BpmnOrderingProvider.$inject = [ 'eventBus' ]; +BpmnOrderingProvider.$inject = [ 'eventBus', 'translate' ]; inherits(BpmnOrderingProvider, OrderingProvider); diff --git a/lib/features/ordering/index.js b/lib/features/ordering/index.js index 0ea7af9a..f1c7a739 100644 --- a/lib/features/ordering/index.js +++ b/lib/features/ordering/index.js @@ -1,4 +1,7 @@ module.exports = { __init__: [ 'bpmnOrderingProvider' ], + __depends__: [ + require('diagram-js/lib/i18n/translate') + ], bpmnOrderingProvider: [ 'type', require('./BpmnOrderingProvider') ] }; \ No newline at end of file diff --git a/lib/features/palette/PaletteProvider.js b/lib/features/palette/PaletteProvider.js index abeda60a..81423f9f 100644 --- a/lib/features/palette/PaletteProvider.js +++ b/lib/features/palette/PaletteProvider.js @@ -5,7 +5,7 @@ var assign = require('lodash/object/assign'); /** * A palette provider for BPMN 2.0 elements. */ -function PaletteProvider(palette, create, elementFactory, spaceTool, lassoTool, handTool) { +function PaletteProvider(palette, create, elementFactory, spaceTool, lassoTool, handTool, translate) { this._palette = palette; this._create = create; @@ -13,6 +13,7 @@ function PaletteProvider(palette, create, elementFactory, spaceTool, lassoTool, this._spaceTool = spaceTool; this._lassoTool = lassoTool; this._handTool = handTool; + this._translate = translate; palette.registerProvider(this); } @@ -25,7 +26,9 @@ PaletteProvider.$inject = [ 'elementFactory', 'spaceTool', 'lassoTool', - 'handTool' + 'handTool', + 'translate', + 'eventBus' ]; @@ -36,7 +39,8 @@ PaletteProvider.prototype.getPaletteEntries = function(element) { elementFactory = this._elementFactory, spaceTool = this._spaceTool, lassoTool = this._lassoTool, - handTool = this._handTool; + handTool = this._handTool, + translate = this._translate; function createAction(type, group, className, title, options) { @@ -55,7 +59,7 @@ PaletteProvider.prototype.getPaletteEntries = function(element) { return { group: group, className: className, - title: title || 'Create ' + shortType, + title: title || translate('Create {type}', { type: shortType }), action: { dragstart: createListener, click: createListener @@ -71,7 +75,7 @@ PaletteProvider.prototype.getPaletteEntries = function(element) { 'hand-tool': { group: 'tools', className: 'bpmn-icon-hand-tool', - title: 'Activate the hand tool', + title: translate('Activate the hand tool'), action: { click: function(event) { handTool.activateHand(event); @@ -81,7 +85,7 @@ PaletteProvider.prototype.getPaletteEntries = function(element) { 'lasso-tool': { group: 'tools', className: 'bpmn-icon-lasso-tool', - title: 'Activate the lasso tool', + title: translate('Activate the lasso tool'), action: { click: function(event) { lassoTool.activateSelection(event); @@ -91,7 +95,7 @@ PaletteProvider.prototype.getPaletteEntries = function(element) { 'space-tool': { group: 'tools', className: 'bpmn-icon-space-tool', - title: 'Activate the create/remove space tool', + title: translate('Activate the create/remove space tool'), action: { click: function(event) { spaceTool.activateSelection(event); @@ -124,13 +128,13 @@ PaletteProvider.prototype.getPaletteEntries = function(element) { 'bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store' ), 'create.subprocess-expanded': createAction( - 'bpmn:SubProcess', 'activity', 'bpmn-icon-subprocess-expanded', 'Create expanded SubProcess', + 'bpmn:SubProcess', 'activity', 'bpmn-icon-subprocess-expanded', translate('Create expanded SubProcess'), { isExpanded: true } ), 'create.participant-expanded': { group: 'collaboration', className: 'bpmn-icon-participant', - title: 'Create Pool/Participant', + title: translate('Create Pool/Participant'), action: { dragstart: createParticipant, click: createParticipant diff --git a/lib/features/palette/index.js b/lib/features/palette/index.js index 19a4d186..2af7fbef 100644 --- a/lib/features/palette/index.js +++ b/lib/features/palette/index.js @@ -4,7 +4,8 @@ module.exports = { require('diagram-js/lib/features/create'), require('diagram-js/lib/features/space-tool'), require('diagram-js/lib/features/lasso-tool'), - require('diagram-js/lib/features/hand-tool') + require('diagram-js/lib/features/hand-tool'), + require('diagram-js/lib/i18n/translate') ], __init__: [ 'paletteProvider' ], paletteProvider: [ 'type', require('./PaletteProvider') ] diff --git a/lib/features/popup-menu/ReplaceMenuProvider.js b/lib/features/popup-menu/ReplaceMenuProvider.js index a9a77a7a..cfd6a0ac 100644 --- a/lib/features/popup-menu/ReplaceMenuProvider.js +++ b/lib/features/popup-menu/ReplaceMenuProvider.js @@ -15,18 +15,19 @@ var replaceOptions = require ('../replace/ReplaceOptions'); /** * This module is an element agnostic replace menu provider for the popup menu. */ -function ReplaceMenuProvider(popupMenu, modeling, moddle, bpmnReplace, rules) { +function ReplaceMenuProvider(popupMenu, modeling, moddle, bpmnReplace, rules, translate) { this._popupMenu = popupMenu; this._modeling = modeling; this._moddle = moddle; this._bpmnReplace = bpmnReplace; this._rules = rules; + this._translate = translate; this.register(); } -ReplaceMenuProvider.$inject = [ 'popupMenu', 'modeling', 'moddle', 'bpmnReplace', 'rules' ]; +ReplaceMenuProvider.$inject = [ 'popupMenu', 'modeling', 'moddle', 'bpmnReplace', 'rules', 'translate' ]; /** @@ -331,7 +332,7 @@ ReplaceMenuProvider.prototype._createSequenceFlowEntries = function (element, re * @return {Object} menu entry item */ ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, action) { - + var translate = this._translate; var replaceElement = this._bpmnReplace.replaceElement; var replaceAction = function() { @@ -341,7 +342,7 @@ ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, a action = action || replaceAction; var menuEntry = { - label: definition.label, + label: translate(definition.label), className: definition.className, id: definition.actionName, action: action @@ -360,6 +361,7 @@ ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, a ReplaceMenuProvider.prototype._getLoopEntries = function(element) { var self = this; + var translate = this._translate; function toggleLoopEntry(event, entry) { var loopCharacteristics; @@ -394,7 +396,7 @@ ReplaceMenuProvider.prototype._getLoopEntries = function(element) { { id: 'toggle-parallel-mi', className: 'bpmn-icon-parallel-mi-marker', - title: 'Parallel Multi Instance', + title: translate('Parallel Multi Instance'), active: isParallel, action: toggleLoopEntry, options: { @@ -405,7 +407,7 @@ ReplaceMenuProvider.prototype._getLoopEntries = function(element) { { id: 'toggle-sequential-mi', className: 'bpmn-icon-sequential-mi-marker', - title: 'Sequential Multi Instance', + title: translate('Sequential Multi Instance'), active: isSequential, action: toggleLoopEntry, options: { @@ -416,7 +418,7 @@ ReplaceMenuProvider.prototype._getLoopEntries = function(element) { { id: 'toggle-loop', className: 'bpmn-icon-loop-marker', - title: 'Loop', + title: translate('Loop'), active: isLoop, action: toggleLoopEntry, options: { @@ -436,6 +438,7 @@ ReplaceMenuProvider.prototype._getLoopEntries = function(element) { * @return {Object} a menu item */ ReplaceMenuProvider.prototype._getAdHocEntry = function(element) { + var translate = this._translate; var businessObject = getBusinessObject(element); var isAdHoc = is(businessObject, 'bpmn:AdHocSubProcess'); @@ -445,7 +448,7 @@ ReplaceMenuProvider.prototype._getAdHocEntry = function(element) { var adHocEntry = { id: 'toggle-adhoc', className: 'bpmn-icon-ad-hoc-marker', - title: 'Ad-hoc', + title: translate('Ad-hoc'), active: isAdHoc, action: function(event, entry) { if (isAdHoc) { diff --git a/lib/import/BpmnImporter.js b/lib/import/BpmnImporter.js index 807f7bab..188ca372 100644 --- a/lib/import/BpmnImporter.js +++ b/lib/import/BpmnImporter.js @@ -27,10 +27,12 @@ function collectWaypoints(waypoints) { }); } -function notYetDrawn(semantic, refSemantic, property) { - return new Error( - 'element ' + elementToString(refSemantic) + ' referenced by ' + - elementToString(semantic) + '#' + property + ' not yet drawn'); +function notYetDrawn(translate, semantic, refSemantic, property) { + return new Error(translate('element {element} referenced by {referenced}#{property} not yet drawn', { + element: elementToString(refSemantic), + referenced: elementToString(semantic), + property: property + })); } /** @@ -41,15 +43,16 @@ function notYetDrawn(semantic, refSemantic, property) { * @param {ElementFactory} elementFactory * @param {ElementRegistry} elementRegistry */ -function BpmnImporter(eventBus, canvas, elementFactory, elementRegistry) { +function BpmnImporter(eventBus, canvas, elementFactory, elementRegistry, translate) { this._eventBus = eventBus; this._canvas = canvas; this._elementFactory = elementFactory; this._elementRegistry = elementRegistry; + this._translate = translate; } -BpmnImporter.$inject = [ 'eventBus', 'canvas', 'elementFactory', 'elementRegistry' ]; +BpmnImporter.$inject = [ 'eventBus', 'canvas', 'elementFactory', 'elementRegistry', 'translate' ]; module.exports = BpmnImporter; @@ -61,7 +64,8 @@ module.exports = BpmnImporter; BpmnImporter.prototype.add = function(semantic, parentElement) { var di = semantic.di, - element; + element, + translate = this._translate; // ROOT ELEMENT // handle the special case that we deal with a @@ -112,9 +116,11 @@ BpmnImporter.prototype.add = function(semantic, parentElement) { this._canvas.addConnection(element, parentElement); } else { - throw new Error('unknown di ' + elementToString(di) + ' for element ' + elementToString(semantic)); + throw new Error(translate('unknown di {di} for element {semantic}', { + di: elementToString(di), + semantic: elementToString(semantic) + })); } - // (optional) LABEL if (hasExternalLabel(semantic)) { this.addLabel(semantic, element); @@ -134,18 +140,20 @@ BpmnImporter.prototype.add = function(semantic, parentElement) { * @param {djs.model.Base} boundaryElement */ BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) { - + var translate = this._translate; var hostSemantic = boundarySemantic.attachedToRef; if (!hostSemantic) { - throw new Error('missing ' + elementToString(boundarySemantic) + '#attachedToRef'); + throw new Error(translate('missing {semantic}#attachedToRef', { + semantic: elementToString(boundarySemantic) + })); } var host = this._elementRegistry.get(hostSemantic.id), attachers = host && host.attachers; if (!host) { - throw notYetDrawn(boundarySemantic, hostSemantic, 'attachedToRef'); + throw notYetDrawn(translate, boundarySemantic, hostSemantic, 'attachedToRef'); } // wire element.host <> host.attachers @@ -190,7 +198,8 @@ BpmnImporter.prototype._getEnd = function(semantic, side) { var element, refSemantic, - type = semantic.$type; + type = semantic.$type, + translate = this._translate; refSemantic = semantic[side + 'Ref']; @@ -213,9 +222,12 @@ BpmnImporter.prototype._getEnd = function(semantic, side) { } if (refSemantic) { - throw notYetDrawn(semantic, refSemantic, side + 'Ref'); + throw notYetDrawn(translate, semantic, refSemantic, side + 'Ref'); } else { - throw new Error(elementToString(semantic) + '#' + side + 'Ref not specified'); + throw new Error(translate('{semantic}#{side} Ref not specified', { + semantic: elementToString(semantic), + side: side + })); } }; diff --git a/lib/import/BpmnTreeWalker.js b/lib/import/BpmnTreeWalker.js index ba78c921..9cd748b5 100644 --- a/lib/import/BpmnTreeWalker.js +++ b/lib/import/BpmnTreeWalker.js @@ -34,7 +34,7 @@ function findDisplayCandidate(definitions) { } -function BpmnTreeWalker(handler) { +function BpmnTreeWalker(handler, translate) { // list of containers already walked var handledElements = {}; @@ -65,7 +65,9 @@ function BpmnTreeWalker(handler) { // avoid multiple rendering of elements if (gfx) { - throw new Error('already rendered ' + elementToString(element)); + throw new Error( + translate('already rendered {element}', { element: elementToString(element) }) + ); } // call handler @@ -87,7 +89,7 @@ function BpmnTreeWalker(handler) { } catch (e) { logError(e.message, { element: element, error: e }); - console.error('failed to import ' + elementToString(element)); + console.error(translate('failed to import {element}', { element: elementToString(element) })); console.error(e); } } @@ -103,13 +105,23 @@ function BpmnTreeWalker(handler) { if (bpmnElement) { if (bpmnElement.di) { - logError('multiple DI elements defined for ' + elementToString(bpmnElement), { element: bpmnElement }); + logError( + translate('multiple DI elements defined for {element}', { + element: elementToString(bpmnElement) + }), + { element: bpmnElement } + ); } else { diRefs.bind(bpmnElement, 'di'); bpmnElement.di = di; } } else { - logError('no bpmnElement referenced in ' + elementToString(di), { element: di }); + logError( + translate('no bpmnElement referenced in {element}', { + element: elementToString(di) + }), + { element: di } + ); } } @@ -144,7 +156,7 @@ function BpmnTreeWalker(handler) { var diagrams = definitions.diagrams; if (diagram && diagrams.indexOf(diagram) === -1) { - throw new Error('diagram not part of bpmn:Definitions'); + throw new Error(translate('diagram not part of bpmn:Definitions')); } if (!diagram && diagrams && diagrams.length) { @@ -153,7 +165,7 @@ function BpmnTreeWalker(handler) { // no diagram -> nothing to import if (!diagram) { - throw new Error('no diagram to display'); + throw new Error(translate('no diagram to display')); } // load DI from selected diagram only @@ -163,10 +175,12 @@ function BpmnTreeWalker(handler) { var plane = diagram.plane; if (!plane) { - throw new Error('no plane for ' + elementToString(diagram)); + throw new Error(translate( + 'no plane for {element}', + { element: elementToString(diagram) } + )); } - var rootElement = plane.bpmnElement; // ensure we default to a suitable display candidate (process or collaboration), @@ -175,10 +189,15 @@ function BpmnTreeWalker(handler) { rootElement = findDisplayCandidate(definitions); if (!rootElement) { - throw new Error('no process or collaboration to display'); + throw new Error(translate('no process or collaboration to display')); } else { - logError('correcting missing bpmnElement on ' + elementToString(plane) + ' to ' + elementToString(rootElement)); + logError( + translate('correcting missing bpmnElement on {plane} to {rootElement}', { + plane: elementToString(plane), + rootElement: elementToString(rootElement) + }) + ); // correct DI on the fly plane.bpmnElement = rootElement; @@ -197,7 +216,12 @@ function BpmnTreeWalker(handler) { // force drawing of everything not yet drawn that is part of the target DI handleUnhandledProcesses(definitions.rootElements, ctx); } else { - throw new Error('unsupported bpmnElement for ' + elementToString(plane) + ' : ' + elementToString(rootElement)); + throw new Error( + translate('unsupported bpmnElement for {plane}: {rootElement}', { + plane: elementToString(plane), + rootElement: elementToString(rootElement) + }) + ); } // handle all deferred elements @@ -369,9 +393,12 @@ function BpmnTreeWalker(handler) { handleDataElement(e, context); } else { logError( - 'unrecognized flowElement ' + elementToString(e) + ' in context ' + - (context ? elementToString(context.businessObject) : null), - { element: e, context: context }); + translate('unrecognized flowElement {element} in context {context}', { + element: elementToString(e), + context: (context ? elementToString(context.businessObject) : 'null') + }), + { element: e, context: context } + ); } }); } diff --git a/lib/import/Importer.js b/lib/import/Importer.js index f946d09e..54915b48 100644 --- a/lib/import/Importer.js +++ b/lib/import/Importer.js @@ -15,7 +15,8 @@ var BpmnTreeWalker = require('./BpmnTreeWalker'); function importBpmnDiagram(diagram, definitions, done) { var importer = diagram.get('bpmnImporter'), - eventBus = diagram.get('eventBus'); + eventBus = diagram.get('eventBus'), + translate = diagram.get('translate'); var error, warnings = []; @@ -37,7 +38,7 @@ function importBpmnDiagram(diagram, definitions, done) { } }; - var walker = new BpmnTreeWalker(visitor); + var walker = new BpmnTreeWalker(visitor, translate); // import walker.handleDefinitions(definitions); diff --git a/lib/import/index.js b/lib/import/index.js index df77e2a3..bcb07d2b 100644 --- a/lib/import/index.js +++ b/lib/import/index.js @@ -1,3 +1,6 @@ module.exports = { + __depends__: [ + require('diagram-js/lib/i18n/translate') + ], bpmnImporter: [ 'type', require('./BpmnImporter') ] }; \ No newline at end of file diff --git a/test/spec/ModelerSpec.js b/test/spec/ModelerSpec.js index e12890d7..20cfeab7 100644 --- a/test/spec/ModelerSpec.js +++ b/test/spec/ModelerSpec.js @@ -107,17 +107,45 @@ describe('Modeler', function() { }); + describe('translate support', function() { + + var xml = require('../fixtures/bpmn/simple.bpmn'); + + it('should allow translation of multi-lingual strings', function(done) { + + createModeler(xml, function(err, warnings, modeler) { + + // given + var translate = modeler.get('translate'); + + // assume + expect(translate).to.exist; + + // when + var interpolatedString = translate('HELLO {you}!', { you: 'WALT' }); + + // then + expect(interpolatedString).to.eql('HELLO WALT!'); + + done(err); + }); + + }); + + }); + + describe('overlay support', function() { it('should allow to add overlays', function(done) { var xml = require('../fixtures/bpmn/simple.bpmn'); - createModeler(xml, function(err, warnings, viewer) { + createModeler(xml, function(err, warnings, modeler) { // given - var overlays = viewer.get('overlays'), - elementRegistry = viewer.get('elementRegistry'); + var overlays = modeler.get('overlays'), + elementRegistry = modeler.get('elementRegistry'); // assume expect(overlays).to.exist; @@ -162,13 +190,13 @@ describe('Modeler', function() { var xml = require('../fixtures/bpmn/simple.bpmn'); - createModeler(xml, function(err, warnings, viewer) { + createModeler(xml, function(err, warnings, modeler) { // given - var bendpointMove = viewer.get('bendpointMove'), - dragging = viewer.get('dragging'), - elementRegistry = viewer.get('elementRegistry'), - canvas = viewer.get('canvas'); + var bendpointMove = modeler.get('bendpointMove'), + dragging = modeler.get('dragging'), + elementRegistry = modeler.get('elementRegistry'), + canvas = modeler.get('canvas'); // assume expect(bendpointMove).to.exist; diff --git a/test/spec/i18n/custom-translate/custom-translate.js b/test/spec/i18n/custom-translate/custom-translate.js new file mode 100644 index 00000000..9fcb9171 --- /dev/null +++ b/test/spec/i18n/custom-translate/custom-translate.js @@ -0,0 +1,15 @@ +'use strict'; + +var translate = require('diagram-js/lib/i18n/translate/translate'); + +module.exports = function customTranslate(template, replacements) { + if (template === 'Remove') { + template = 'Eliminar'; + } + + if (template === 'Activate the hand tool') { + template = 'Activar herramienta mano'; + } + + return translate(template, replacements); +}; \ No newline at end of file diff --git a/test/spec/i18n/custom-translate/index.js b/test/spec/i18n/custom-translate/index.js new file mode 100644 index 00000000..da7cc3bd --- /dev/null +++ b/test/spec/i18n/custom-translate/index.js @@ -0,0 +1,3 @@ +module.exports = { + translate: [ 'value', require('./custom-translate') ] +}; \ No newline at end of file diff --git a/test/spec/i18n/translateSpec.js b/test/spec/i18n/translateSpec.js new file mode 100644 index 00000000..1911c3d6 --- /dev/null +++ b/test/spec/i18n/translateSpec.js @@ -0,0 +1,53 @@ +'use strict'; + +require('test/TestHelper'); + +/* global bootstrapModeler, inject */ + +var coreModule = require('lib/core'), + translateModule = require('diagram-js/lib/i18n/translate'), + customTranslateModule = require('./custom-translate'), + modelingModule = require('lib/features/modeling'), + paletteModule = require('lib/features/palette'), + contextPadModule = require('lib/features/context-pad'); + +var diagramXML = require('test/fixtures/bpmn/simple.bpmn'); + + +describe('i18n - translate', function() { + + beforeEach(bootstrapModeler(diagramXML, { + modules: [ + coreModule, + modelingModule, + paletteModule, + contextPadModule, + translateModule, + customTranslateModule + ] + })); + + + it('should translate palette', inject(function(palette) { + + // when + var handToolEntry = palette.getEntries()['hand-tool']; + + // then + expect(handToolEntry.title).to.equal('Activar herramienta mano'); + })); + + + it('should translate context pad', inject(function(contextPad) { + + // given + contextPad.open('Task_1'); + + // when + var deleteEntry = contextPad._current.entries.delete; + + // then + expect(deleteEntry.title).to.equal('Eliminar'); + })); + +}); \ No newline at end of file