8
0
Просмотр исходного кода

Implemented Locale and used Locale#t in the existing code.

I also implemented the mock for testing against Locale. It will be useful in the future.

For now the Locale is passed to the views manually. We were considering adding some
views factory, but calling editor.view( ViewClass, model, ...other args ) would not be that
simpler and there's a problem with arguments order, because a view may accept more than
a model and then the locale must be injected in the middle of (model, ..., other args).
Piotrek Koszuliński 10 лет назад
Родитель
Сommit
b02519fec2

+ 20 - 5
packages/ckeditor5-engine/src/editor.js

@@ -9,6 +9,7 @@ import ObservableMixin from './observablemixin.js';
 import EditorConfig from './editorconfig.js';
 import PluginCollection from './plugincollection.js';
 import CKEditorError from './ckeditorerror.js';
+import Locale from './locale.js';
 import isArray from './lib/lodash/isArray.js';
 import utils from './utils.js';
 
@@ -33,7 +34,7 @@ export default class Editor {
 		 * editor creation and is not subject to be modified.
 		 *
 		 * @readonly
-		 * @type {HTMLElement}
+		 * @member {HTMLElement}
 		 */
 		this.element = element;
 
@@ -45,23 +46,37 @@ export default class Editor {
 		 * instance itself.
 		 *
 		 * @readonly
-		 * @type {Config}
+		 * @member {Config}
 		 */
-		this.config = new EditorConfig( config );
+		this.config = config = new EditorConfig( config );
 
 		/**
 		 * The plugins loaded and in use by this editor instance.
 		 *
 		 * @readonly
-		 * @type {PluginCollection}
+		 * @member {PluginCollection}
 		 */
 		this.plugins = new PluginCollection( this );
 
+		/**
+		 * @readonly
+		 * @member {core.Locale} core.Editor#locale
+		 */
+		this.locale = new Locale( config.lang );
+
+		/**
+		 * Shorthand for {@link core.Locale#t}.
+		 *
+		 * @see core.Locale#t
+		 * @method t
+		 */
+		this.t = this.locale.t;
+
 		/**
 		 * The chosen creator.
 		 *
 		 * @protected
-		 * @type {Creator}
+		 * @member {core.Creator} core.Editor#_creator
 		 */
 	}
 

+ 2 - 2
packages/ckeditor5-engine/src/editorui/editoruiview.js

@@ -15,8 +15,8 @@ import View from '../ui/view.js';
  */
 
 export default class EditorUIView extends View {
-	constructor( model ) {
-		super( model );
+	constructor( model, locale ) {
+		super( model, locale );
 
 		this._createBodyRegion();
 

+ 62 - 0
packages/ckeditor5-engine/src/locale.js

@@ -0,0 +1,62 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+/**
+ * Represents the localization services.
+ *
+ * @memberOf core
+ */
+export default class Locale {
+	/**
+	 * Creates a new instance of the Locale class. {@link Foo#bar}
+	 *
+	 * @param {String} [lang='en'] The language code in [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
+	 */
+	constructor( lang ) {
+		/**
+		 * The language code in [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
+		 *
+		 * @readonly
+		 * @member {String} core.Locale#lang
+		 */
+		this.lang = lang || 'en';
+
+		/**
+		 * Translates the given string to the {@link #lang}. This method is also availble in {@link core.Editor#t} and
+		 * {@link core.ui.View#t}.
+		 *
+		 * The strings may contain placeholders (`%<index>`) for values which are passed as the second argument.
+		 * `<index>` is the index in the `values` array.
+		 *
+		 *		editor.t( 'Created file "%0" in %1ms.', [ fileName, timeTaken ] );
+		 *
+		 * This method's context is statically bound to Locale instance,
+		 * so it can be called as a function:
+		 *
+		 *		const t = this.t;
+		 *		t( 'Label' );
+		 *
+		 * @method t
+		 * @param {String} str The string to translate.
+		 * @param {String[]} values Values that should be used to interpolate the string.
+		 */
+		this.t = ( ...args ) => this._t( ...args );
+	}
+
+	/**
+	 * Base for the {@link #t} method.
+	 *
+	 * @private
+	 */
+	_t( str, values ) {
+		if ( values ) {
+			str = str.replace( /\%(\d+)/g, ( match, index ) => ( index < values.length ) ? values[ index ] : match );
+		}
+
+		return str;
+	}
+}

+ 1 - 1
packages/ckeditor5-engine/src/ui/componentfactory.js

@@ -74,7 +74,7 @@ export default class ComponentFactory {
 		const component = this._components.get( name );
 
 		const model = component.model;
-		const view = new component.ViewClass( model );
+		const view = new component.ViewClass( model, this.editor.locale );
 		const controller = new component.ControllerClass( model, view, this.editor );
 
 		return controller;

+ 38 - 17
packages/ckeditor5-engine/src/ui/view.js

@@ -27,19 +27,36 @@ export default class View {
 	 * Creates an instance of the {@link View} class.
 	 *
 	 * @param {core.ui.Model} model (View)Model of this View.
+	 * @param {core.Locale} [locale] The {@link core.Editor#locale editor's locale} instance.
 	 */
-	constructor( model ) {
+	constructor( model, locale ) {
 		/**
 		 * Model of this view.
 		 *
-		 * @type {core.ui.Model}
+		 * @member {core.ui.Model} core.ui.View#model
 		 */
-		this.model = model || null;
+		this.model = model;
+
+		/**
+		 * @readonly
+		 * @member {core.Locale} core.ui.View#locale
+		 */
+		this.locale = locale;
+
+		/**
+		 * Shorthand for {@link core.Locale#t}.
+		 *
+		 * Note: If locale instance hasn't been passed to the view this method may not be available.
+		 *
+		 * @see core.Locale#t
+		 * @method t
+		 */
+		this.t = locale && locale.t;
 
 		/**
 		 * Regions of this view. See {@link core.ui.View#register}.
 		 *
-		 * @type {Collection}
+		 * @member {core.Collection} core.ui.View#regions
 		 */
 		this.regions = new Collection( {
 			idProperty: 'name'
@@ -48,38 +65,37 @@ export default class View {
 		/**
 		 * Template of this view.
 		 *
-		 * @type {Object}
+		 * @member {Object} core.ui.View#template
 		 */
-		this.template = null;
 
 		/**
 		 * Region selectors of this view. See {@link core.ui.View#register}.
 		 *
 		 * @private
-		 * @type {Object}
+		 * @member {Object} core.ui.View#_regionSelectors
 		 */
-		this._regionsSelectors = {};
+		this._regionSelectors = {};
 
 		/**
 		 * Element of this view.
 		 *
 		 * @private
-		 * @type {HTMLElement}
+		 * @member {HTMLElement} core.ui.View.#_element
 		 */
-		this._element = null;
 
 		/**
 		 * An instance of Template to generate {@link core.ui.View#_el}.
 		 *
 		 * @private
-		 * @type {Template}
+		 * @member {Template} core.ui.View#_template
 		 */
-		this._template = null;
 	}
 
 	/**
 	 * Element of this view. The element is rendered on first reference
-	 * using {@link core.ui.View#template} definition and {@link core.ui.View#_template} object.
+	 * using {@link core.ui.View#template} definition.
+	 *
+	 * @type {HTMLElement}
 	 */
 	get element() {
 		if ( this._element ) {
@@ -112,6 +128,7 @@ export default class View {
 	 * is synchronized with {@link View#model}.
 	 *
 	 * @readonly
+	 * @type core.ui.ViewModelBinding
 	 */
 	get attributeBinder() {
 		if ( this._attributeBinder ) {
@@ -194,6 +211,9 @@ export default class View {
 
 	/**
 	 * Initializes the view.
+	 *
+	 * Note: {@link core.Controller} supports if a promise is returned by this method,
+	 * what means that view initialization may be asynchronous.
 	 */
 	init() {
 		this._initRegions();
@@ -271,7 +291,7 @@ export default class View {
 			}
 		}
 
-		this._regionsSelectors[ regionName ] = regionSelector;
+		this._regionSelectors[ regionName ] = regionSelector;
 	}
 
 	/**
@@ -334,12 +354,13 @@ export default class View {
 			this.element.remove();
 		}
 
-		this.model = this.regions = this.template = this._regionsSelectors = this._element = this._template = null;
+		this.model = this.regions = this.template = this.locale = this.t = null;
+		this._regionSelectors = this._element = this._template = null;
 	}
 
 	/**
 	 * Initializes {@link core.ui.View#regions} of this view by passing a DOM element
-	 * generated from {@link core.ui.View#_regionsSelectors} into {@link Region#init}.
+	 * generated from {@link core.ui.View#_regionSelectors} into {@link Region#init}.
 	 *
 	 * @protected
 	 */
@@ -347,7 +368,7 @@ export default class View {
 		let region, regionEl, regionSelector;
 
 		for ( region of this.regions ) {
-			regionSelector = this._regionsSelectors[ region.name ];
+			regionSelector = this._regionSelectors[ region.name ];
 
 			if ( typeof regionSelector == 'string' ) {
 				regionEl = this.element.querySelector( regionSelector );

+ 27 - 0
packages/ckeditor5-engine/tests/_utils/locale-mock.js

@@ -0,0 +1,27 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+/**
+ * A replacement for the {@link core.Locale} class.
+ *
+ * @memberOf tests.core._utils
+ */
+export default class Locale {
+	constructor() {
+		this.t = ( str ) => `t( ${ str } )`;
+	}
+
+	/**
+	 * Injects instance of this class to the editor.
+	 *
+	 * @param {core.Editor} editor
+	 */
+	static inject( editor ) {
+		editor.locale = new Locale();
+		editor.t = editor.locale.t;
+	}
+}

+ 2 - 2
packages/ckeditor5-engine/tests/_utils/ui/boxededitorui/boxededitoruiview.js

@@ -8,8 +8,8 @@
 import EditorUIView from '/ckeditor5/core/editorui/editoruiview.js';
 
 export default class BoxedEditorUIView extends EditorUIView {
-	constructor( model ) {
-		super( model );
+	constructor( model, locale ) {
+		super( model, locale );
 
 		this.template = {
 			tag: 'div',

+ 2 - 2
packages/ckeditor5-engine/tests/_utils/ui/editable/framed/framededitableview.js

@@ -8,8 +8,8 @@
 import EditableView from '/ckeditor5/core/editable/editableview.js';
 
 export default class FramedEditableView extends EditableView {
-	constructor( model ) {
-		super( model );
+	constructor( model, locale ) {
+		super( model, locale );
 
 		const bind = this.attributeBinder;
 

+ 2 - 2
packages/ckeditor5-engine/tests/_utils/ui/editable/inline/inlineeditableview.js

@@ -8,8 +8,8 @@
 import EditableView from '/ckeditor5/core/editable/editableview.js';
 
 export default class InlineEditableView extends EditableView {
-	constructor( model, editableElement ) {
-		super( model );
+	constructor( model, locale, editableElement ) {
+		super( model, locale );
 
 		this.element = editableElement;
 	}

+ 2 - 2
packages/ckeditor5-engine/tests/_utils/ui/floatingtoolbar/floatingtoolbarview.js

@@ -8,8 +8,8 @@
 import ToolbarView from '/ckeditor5/ui/toolbar/toolbarview.js';
 
 export default class FloatingToolbarView extends ToolbarView {
-	constructor( model ) {
-		super( model );
+	constructor( model, locale ) {
+		super( model, locale );
 
 		const bind = this.attributeBinder;
 

+ 4 - 3
packages/ckeditor5-engine/tests/creator/manual/_utils/creator/classiccreator.js

@@ -49,7 +49,8 @@ export default class ClassicCreator extends Creator {
 	}
 
 	_setupToolbar() {
-		const toolbar = new Toolbar( new Model(), new ToolbarView(), this.editor );
+		const toolbarModel = new Model();
+		const toolbar = new Toolbar( toolbarModel, new ToolbarView( toolbarModel, this.editor.locale ), this.editor );
 
 		toolbar.addButtons( this.editor.config.toolbar );
 
@@ -58,7 +59,7 @@ export default class ClassicCreator extends Creator {
 
 	_createEditable() {
 		const editable = new FramedEditable( this.editor );
-		const editableView = new FramedEditableView( editable.viewModel );
+		const editableView = new FramedEditableView( editable.viewModel, this.editor.locale );
 
 		editable.view = editableView;
 
@@ -67,7 +68,7 @@ export default class ClassicCreator extends Creator {
 
 	_createEditorUI() {
 		const editorUI = new BoxedEditorUI( this.editor );
-		const editorUIView = new BoxedEditorUIView( editorUI.viewModel );
+		const editorUIView = new BoxedEditorUIView( editorUI.viewModel, this.editor.locale );
 
 		editorUI.view = editorUIView;
 

+ 6 - 4
packages/ckeditor5-engine/tests/creator/manual/_utils/creator/inlinecreator.js

@@ -47,11 +47,13 @@ export default class InlineCreator extends Creator {
 	}
 
 	_setupToolbar() {
+		const locale = this.editor.locale;
+
 		const toolbar1Model = new Model();
 		const toolbar2Model = new Model();
 
-		const toolbar1 = new FloatingToolbar( toolbar1Model, new FloatingToolbarView( toolbar1Model ), this.editor );
-		const toolbar2 = new FloatingToolbar( toolbar2Model, new FloatingToolbarView( toolbar2Model ), this.editor );
+		const toolbar1 = new FloatingToolbar( toolbar1Model, new FloatingToolbarView( toolbar1Model, locale ), this.editor );
+		const toolbar2 = new FloatingToolbar( toolbar2Model, new FloatingToolbarView( toolbar2Model, locale ), this.editor );
 
 		toolbar1.addButtons( this.editor.config.toolbar );
 		toolbar2.addButtons( this.editor.config.toolbar.reverse() );
@@ -62,7 +64,7 @@ export default class InlineCreator extends Creator {
 
 	_createEditable() {
 		const editable = new InlineEditable( this.editor );
-		const editableView = new InlineEditableView( editable.viewModel, this.editor.element );
+		const editableView = new InlineEditableView( editable.viewModel, this.editor.locale, this.editor.element );
 
 		editable.view = editableView;
 
@@ -72,7 +74,7 @@ export default class InlineCreator extends Creator {
 	_createEditorUI() {
 		const editorUI = new BoxlessEditorUI( this.editor );
 
-		editorUI.view = new EditorUIView( editorUI.viewModel );
+		editorUI.view = new EditorUIView( editorUI.viewModel, this.editor.locale );
 
 		return editorUI;
 	}

+ 4 - 2
packages/ckeditor5-engine/tests/creator/manual/_utils/imitatefeatures.js

@@ -15,10 +15,12 @@ import ButtonView from '/ckeditor5/ui/button/buttonview.js';
  * @param {core.Editor} editor
  */
 export function imitateFeatures( editor ) {
+	const t = editor.t;
+
 	const boldModel = new Model( {
 		isEnabled: true,
 		isOn: false,
-		label: 'bold'
+		label: t( 'Bold' )
 	} );
 
 	boldModel.on( 'execute', () => {
@@ -33,7 +35,7 @@ export function imitateFeatures( editor ) {
 	const italicModel = new Model( {
 		isEnabled: true,
 		isOn: false,
-		label: 'italic'
+		label: t( 'Italic' )
 	} );
 
 	italicModel.on( 'execute', () => {

+ 1 - 1
packages/ckeditor5-engine/tests/creator/manual/creator-classic.md

@@ -5,7 +5,7 @@
   * Framed editor should be created.
   * It should be rectangular (400x400).
   * Original element should disappear.
-  * There should be a toolbar with "bold" and "italic" buttons.
+  * There should be a toolbar with "Bold" and "Italic" buttons.
 3. Click "Destroy editor".
 4. Expected:
   * Editor should be destroyed.

+ 2 - 2
packages/ckeditor5-engine/tests/creator/manual/creator-inline.md

@@ -4,8 +4,8 @@
 2. Expected:
   * Inline editor should be created.
   * There should be **two** toolbars:
-    * one with "bold" and "italic" buttons,
-    * second with "italic" and "bold" buttons.
+    * one with "Bold" and "Italic" buttons,
+    * second with "Italic" and "Bold" buttons.
 3. Click "Destroy editor".
 4. Expected:
   * Editor should be destroyed (the element should not be editable).

+ 16 - 0
packages/ckeditor5-engine/tests/editor/editor.js

@@ -12,6 +12,7 @@ import coreTestUtils from '/tests/core/_utils/utils.js';
 import Editor from '/ckeditor5/core/editor.js';
 import EditorConfig from '/ckeditor5/core/editorconfig.js';
 import Plugin from '/ckeditor5/core/plugin.js';
+import Locale from '/ckeditor5/core/locale.js';
 
 const pluginClasses = {};
 let element;
@@ -51,6 +52,21 @@ describe( 'config', () => {
 	} );
 } );
 
+describe( 'locale', () => {
+	it( 'is instantiated and t() is exposed', () => {
+		const editor = new Editor( element );
+
+		expect( editor.locale ).to.be.instanceof( Locale );
+		expect( editor.t ).to.equal( editor.locale.t );
+	} );
+
+	it( 'is configured with the config.lang', () => {
+		const editor = new Editor( element, { lang: 'pl' } );
+
+		expect( editor.locale.lang ).to.equal( 'pl' );
+	} );
+} );
+
 describe( 'init', () => {
 	it( 'should return a promise that resolves properly', () => {
 		const editor = new Editor( element, {

+ 64 - 0
packages/ckeditor5-engine/tests/locale.js

@@ -0,0 +1,64 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import Locale from '/ckeditor5/core/locale.js';
+
+describe( 'Locale', () => {
+	let locale;
+
+	beforeEach( () => {
+		locale = new Locale();
+	} );
+
+	describe( 'constructor', () => {
+		it( 'sets the lang', () => {
+			const locale = new Locale( 'pl' );
+
+			expect( locale ).to.have.property( 'lang', 'pl' );
+		} );
+
+		it( 'defaults lang to en', () => {
+			const locale = new Locale();
+
+			expect( locale ).to.have.property( 'lang', 'en' );
+		} );
+	} );
+
+	describe( 't', () => {
+		it( 'has the context bound', () => {
+			const t = locale.t;
+
+			expect( t( 'Foo' ) ).to.equal( 'Foo' );
+		} );
+
+		it( 'interpolates 1 value', () => {
+			const t = locale.t;
+
+			expect( t( '%0 - %0', [ 'foo' ] ) ).to.equal( 'foo - foo' );
+		} );
+
+		it( 'interpolates 3 values', () => {
+			const t = locale.t;
+
+			expect( t( '%1 - %0 - %2', [ 'a', 'b', 'c' ] ) ).to.equal( 'b - a - c' );
+		} );
+
+		// Those test make sure that if %0 is really to be used, then it's going to work.
+		// It'd be a super rare case if one would need to use %0 and at the same time interpolate something.
+		it( 'does not interpolate placeholders if values not passed', () => {
+			const t = locale.t;
+
+			expect( t( '%1 - %0 - %2' ) ).to.equal( '%1 - %0 - %2' );
+		} );
+
+		it( 'does not interpolate those placeholders for which values has not been passed', () => {
+			const t = locale.t;
+
+			expect( t( '%1 - %0 - %2', [ 'a' ] ) ).to.equal( '%1 - a - %2' );
+		} );
+	} );
+} );

+ 94 - 78
packages/ckeditor5-engine/tests/ui/view.js

@@ -34,15 +34,23 @@ describe( 'View', () => {
 		} );
 
 		it( 'defines basic view properties', () => {
-			view = new View();
+			const model = new Model();
 
-			expect( view.model ).to.be.null;
-			expect( view.regions.length ).to.be.equal( 0 );
-			expect( view.template ).to.be.null;
-			expect( view._regionsSelectors ).to.be.empty;
-			expect( view._element ).to.be.null;
-			expect( view._template ).to.be.null;
-			expect( view.element ).to.be.null;
+			view = new View( model );
+
+			expect( view.model ).to.equal( model );
+			expect( view.t ).to.be.undefined;
+			expect( view.regions.length ).to.equal( 0 );
+		} );
+
+		it( 'defines the locale property and the t function', () => {
+			const model = new Model();
+			const locale = { t() {} };
+
+			view = new View( model, locale );
+
+			expect( view.locale ).to.equal( locale );
+			expect( view.t ).to.equal( locale.t );
 		} );
 	} );
 
@@ -86,8 +94,8 @@ describe( 'View', () => {
 
 			view.init();
 
-			expect( region1.element ).to.be.equal( view.element.firstChild );
-			expect( region2.element ).to.be.equal( view.element.lastChild );
+			expect( region1.element ).to.equal( view.element.firstChild );
+			expect( region2.element ).to.equal( view.element.lastChild );
 		} );
 
 		it( 'initializes view regions with function selector', () => {
@@ -101,8 +109,8 @@ describe( 'View', () => {
 
 			view.init();
 
-			expect( region1.element ).to.be.equal( view.element.firstChild );
-			expect( region2.element ).to.be.equal( view.element.lastChild );
+			expect( region1.element ).to.equal( view.element.firstChild );
+			expect( region2.element ).to.equal( view.element.lastChild );
 		} );
 
 		it( 'initializes view regions with boolean selector', () => {
@@ -169,6 +177,13 @@ describe( 'View', () => {
 		} );
 
 		it( 'should override an existing region with override flag', () => {
+			view.template = {
+				tag: 'div',
+				children: [
+					{ tag: 'span' }
+				]
+			};
+
 			const region1 = new Region( 'x' );
 			const region2 = new Region( 'x' );
 
@@ -176,8 +191,10 @@ describe( 'View', () => {
 			view.register( region2, true, true );
 			view.register( 'x', 'span', true );
 
-			expect( view.regions.get( 'x' ) ).to.be.equal( region2 );
-			expect( view._regionsSelectors.x ).to.be.equal( 'span' );
+			view.init();
+
+			expect( view.regions.get( 'x' ) ).to.equal( region2 );
+			expect( view.regions.get( 'x' ).element ).to.equal( view.element.firstChild );
 		} );
 
 		it( 'should not override an existing region with the same region with override flag', () => {
@@ -198,7 +215,7 @@ describe( 'View', () => {
 			setTestViewInstance( { a: 1 } );
 
 			expect( view.element ).to.be.an.instanceof( HTMLElement );
-			expect( view.element.nodeName ).to.be.equal( 'A' );
+			expect( view.element.nodeName ).to.equal( 'A' );
 		} );
 
 		it( 'can be explicitly declared', () => {
@@ -221,8 +238,8 @@ describe( 'View', () => {
 			const bind = view.attributeBinder;
 
 			expect( bind ).to.have.keys( 'to', 'if' );
-			expect( typeof bind.to ).to.be.equal( 'function' );
-			expect( typeof bind.if ).to.be.equal( 'function' );
+			expect( typeof bind.to ).to.equal( 'function' );
+			expect( typeof bind.if ).to.equal( 'function' );
 		} );
 
 		describe( 'to', () => {
@@ -254,10 +271,10 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p class="bar">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="bar">abc</p>' );
 
 				view.model.foo = 'baz';
-				expect( view.element.outerHTML ).to.be.equal( '<p class="baz">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="baz">abc</p>' );
 			} );
 
 			it( 'allows binding attribute to the model – simple (Text Node)', () => {
@@ -275,10 +292,10 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p>bar</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>bar</p>' );
 
 				view.model.foo = 'baz';
-				expect( view.element.outerHTML ).to.be.equal( '<p>baz</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>baz</p>' );
 			} );
 
 			it( 'allows binding attribute to the model – value processing', () => {
@@ -300,10 +317,10 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 3 } );
-				expect( view.element.outerHTML ).to.be.equal( '<p class="positive">positive</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="positive">positive</p>' );
 
 				view.model.foo = -7;
-				expect( view.element.outerHTML ).to.be.equal( '<p class="negative">negative</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="negative">negative</p>' );
 			} );
 
 			it( 'allows binding attribute to the model – value processing (use Node)', () => {
@@ -327,10 +344,10 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 3 } );
-				expect( view.element.outerHTML ).to.be.equal( '<p class="HTMLElement positive"></p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="HTMLElement positive"></p>' );
 
 				view.model.foo = -7;
-				expect( view.element.outerHTML ).to.be.equal( '<p></p>' );
+				expect( view.element.outerHTML ).to.equal( '<p></p>' );
 			} );
 
 			it( 'allows binding attribute to the model – custom callback', () => {
@@ -352,10 +369,10 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'moo' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p class="undefined">moo</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="undefined">moo</p>' );
 
 				view.model.foo = 'changed';
-				expect( view.element.outerHTML ).to.be.equal( '<p class="changed">changed</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="changed">changed</p>' );
 			} );
 
 			it( 'allows binding attribute to the model – array of bindings (HTMLElement attribute)', () => {
@@ -378,11 +395,11 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'a', bar: 'b' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p class="ck-class a b foo-is-a ck-end">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="ck-class a b foo-is-a ck-end">abc</p>' );
 
 				view.model.foo = 'c';
 				view.model.bar = 'd';
-				expect( view.element.outerHTML ).to.be.equal( '<p class="ck-class c d foo-is-c ck-end">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="ck-class c d foo-is-c ck-end">abc</p>' );
 			} );
 
 			it( 'allows binding attribute to the model – array of bindings (Text Node)', () => {
@@ -408,11 +425,11 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'a', bar: 'b' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p>ck-class a b foo-is-a ck-end</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>ck-class a b foo-is-a ck-end</p>' );
 
 				view.model.foo = 'c';
 				view.model.bar = 'd';
-				expect( view.element.outerHTML ).to.be.equal( '<p>ck-class c d foo-is-c ck-end</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>ck-class c d foo-is-c ck-end</p>' );
 			} );
 
 			it( 'allows binding attribute to the model – falsy values', () => {
@@ -429,22 +446,22 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p class="bar">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="bar">abc</p>' );
 
 				view.model.foo = false;
-				expect( view.element.outerHTML ).to.be.equal( '<p class="false">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="false">abc</p>' );
 
 				view.model.foo = null;
-				expect( view.element.outerHTML ).to.be.equal( '<p class="null">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="null">abc</p>' );
 
 				view.model.foo = undefined;
-				expect( view.element.outerHTML ).to.be.equal( '<p class="undefined">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="undefined">abc</p>' );
 
 				view.model.foo = 0;
-				expect( view.element.outerHTML ).to.be.equal( '<p class="0">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="0">abc</p>' );
 
 				view.model.foo = '';
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 			} );
 		} );
 
@@ -478,13 +495,13 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: true } );
-				expect( view.element.outerHTML ).to.be.equal( '<p class="">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="">abc</p>' );
 
 				view.model.foo = false;
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 
 				view.model.foo = 'bar';
-				expect( view.element.outerHTML ).to.be.equal( '<p class="">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="">abc</p>' );
 			} );
 
 			// TODO: Is this alright? It makes sense but it's pretty useless. Text Node cannot be
@@ -504,13 +521,13 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: true } );
-				expect( view.element.outerHTML ).to.be.equal( '<p></p>' );
+				expect( view.element.outerHTML ).to.equal( '<p></p>' );
 
 				view.model.foo = false;
-				expect( view.element.outerHTML ).to.be.equal( '<p></p>' );
+				expect( view.element.outerHTML ).to.equal( '<p></p>' );
 
 				view.model.foo = 'bar';
-				expect( view.element.outerHTML ).to.be.equal( '<p></p>' );
+				expect( view.element.outerHTML ).to.equal( '<p></p>' );
 			} );
 
 			it( 'allows binding attribute to the model – value of an attribute (HTMLElement attribute)', () => {
@@ -527,13 +544,13 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p class="bar">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="bar">abc</p>' );
 
 				view.model.foo = false;
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 
 				view.model.foo = 64;
-				expect( view.element.outerHTML ).to.be.equal( '<p class="bar">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="bar">abc</p>' );
 			} );
 
 			it( 'allows binding attribute to the model – value of an attribute (Text Node)', () => {
@@ -551,13 +568,13 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p>bar</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>bar</p>' );
 
 				view.model.foo = false;
-				expect( view.element.outerHTML ).to.be.equal( '<p></p>' );
+				expect( view.element.outerHTML ).to.equal( '<p></p>' );
 
 				view.model.foo = 64;
-				expect( view.element.outerHTML ).to.be.equal( '<p>bar</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>bar</p>' );
 			} );
 
 			it( 'allows binding attribute to the model – value of an attribute processed by a callback', () => {
@@ -574,13 +591,13 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 
 				view.model.foo = false;
-				expect( view.element.outerHTML ).to.be.equal( '<p class="there–is–no–foo">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="there–is–no–foo">abc</p>' );
 
 				view.model.foo = 64;
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 			} );
 
 			it( 'allows binding attribute to the model – value of an attribute processed by a callback (use Node)', () => {
@@ -597,13 +614,13 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 
 				view.model.foo = 'P';
-				expect( view.element.outerHTML ).to.be.equal( '<p class="eqls-tag-name">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="eqls-tag-name">abc</p>' );
 
 				view.model.foo = 64;
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 			} );
 
 			it( 'allows binding attribute to the model – falsy values', () => {
@@ -620,22 +637,22 @@ describe( 'View', () => {
 				} );
 
 				setTestViewInstance( { foo: 'bar' } );
-				expect( view.element.outerHTML ).to.be.equal( '<p class="foo-is-set">abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p class="foo-is-set">abc</p>' );
 
 				view.model.foo = false;
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 
 				view.model.foo = null;
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 
 				view.model.foo = undefined;
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 
 				view.model.foo = '';
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 
 				view.model.foo = 0;
-				expect( view.element.outerHTML ).to.be.equal( '<p>abc</p>' );
+				expect( view.element.outerHTML ).to.equal( '<p>abc</p>' );
 			} );
 		} );
 	} );
@@ -872,9 +889,8 @@ describe( 'View', () => {
 			expect( view.model ).to.be.null;
 			expect( view.regions ).to.be.null;
 			expect( view.template ).to.be.null;
-			expect( view._regionsSelectors ).to.be.null;
-			expect( view._element ).to.be.null;
-			expect( view._template ).to.be.null;
+			expect( view.locale ).to.be.null;
+			expect( view.t ).to.be.null;
 		} );
 
 		it( 'detaches the element from DOM', () => {
@@ -897,8 +913,8 @@ describe( 'View', () => {
 			view.regions.get( 'x' ).views.add( new View() );
 			view.destroy();
 
-			expect( regionsRef.length ).to.be.equal( 0 );
-			expect( regionViewsRef.length ).to.be.equal( 0 );
+			expect( regionsRef.length ).to.equal( 0 );
+			expect( regionViewsRef.length ).to.equal( 0 );
 			expect( spy.calledOnce ).to.be.true;
 		} );
 
@@ -919,15 +935,15 @@ describe( 'View', () => {
 			const modelRef = view.model;
 			const elRef = view.element;
 
-			expect( view.element.outerHTML ).to.be.equal( '<p>bar</p>' );
+			expect( view.element.outerHTML ).to.equal( '<p>bar</p>' );
 
 			modelRef.foo = 'baz';
-			expect( view.element.outerHTML ).to.be.equal( '<p>baz</p>' );
+			expect( view.element.outerHTML ).to.equal( '<p>baz</p>' );
 
 			view.destroy();
 
 			modelRef.foo = 'abc';
-			expect( elRef.outerHTML ).to.be.equal( '<p>baz</p>' );
+			expect( elRef.outerHTML ).to.equal( '<p>baz</p>' );
 		} );
 
 		it( 'destroy a template–less view', () => {
@@ -958,11 +974,11 @@ describe( 'View', () => {
 				}
 			} );
 
-			expect( el.outerHTML ).to.be.equal( '<div class="42" id="foo" checked="checked"></div>' );
+			expect( el.outerHTML ).to.equal( '<div class="42" id="foo" checked="checked"></div>' );
 
 			view.model.b = 64;
 
-			expect( el.outerHTML ).to.be.equal( '<div class="64" id="foo" checked="checked"></div>' );
+			expect( el.outerHTML ).to.equal( '<div class="64" id="foo" checked="checked"></div>' );
 		} );
 
 		it( 'should initialize DOM listeners', () => {
@@ -999,7 +1015,7 @@ describe( 'View', () => {
 			el.appendChild( childA );
 			el.appendChild( childB );
 
-			expect( el.outerHTML ).to.be.equal( '<div><a>anchor</a><b>bold</b></div>' );
+			expect( el.outerHTML ).to.equal( '<div><a>anchor</a><b>bold</b></div>' );
 
 			const spy1 = testUtils.sinon.spy();
 			const spy2 = testUtils.sinon.spy();
@@ -1042,14 +1058,14 @@ describe( 'View', () => {
 				}
 			} );
 
-			expect( el.outerHTML ).to.be.equal( '<div id="FOO" class="applied-parent-42">' +
+			expect( el.outerHTML ).to.equal( '<div id="FOO" class="applied-parent-42">' +
 				'<a class="applied-A-42" id="applied-A">Text applied to childA.</a>' +
 				'<b class="applied-B-42" id="applied-B">Text applied to childB.</b>' +
 			'</div>' );
 
 			view.model.b = 16;
 
-			expect( el.outerHTML ).to.be.equal( '<div id="FOO" class="applied-parent-16">' +
+			expect( el.outerHTML ).to.equal( '<div id="FOO" class="applied-parent-16">' +
 				'<a class="applied-A-16" id="applied-A">Text applied to childA.</a>' +
 				'<b class="applied-B-16" id="applied-B">Text applied to childB.</b>' +
 			'</div>' );
@@ -1087,8 +1103,8 @@ describe( 'View', () => {
 			view.applyTemplateToElement( el2, def );
 
 			// Attacher function didn't change because it's still the same View instance.
-			expect( attacherFn ).to.be.equal( def.on.click );
-			expect( def.on._listenerView ).to.be.equal( view );
+			expect( attacherFn ).to.equal( def.on.click );
+			expect( def.on._listenerView ).to.equal( view );
 
 			document.body.appendChild( el1 );
 			document.body.appendChild( el2 );
@@ -1115,14 +1131,14 @@ describe( 'View', () => {
 			document.body.appendChild( el );
 
 			view1.applyTemplateToElement( el, def );
-			expect( def.on._listenerView ).to.be.equal( view1 );
+			expect( def.on._listenerView ).to.equal( view1 );
 
 			dispatchEvent( el, 'click' );
 			sinon.assert.calledOnce( spy );
 
 			// Use another view1 to see if attachers are re–defined.
 			view2.applyTemplateToElement( el, def );
-			expect( def.on._listenerView ).to.be.equal( view2 );
+			expect( def.on._listenerView ).to.equal( view2 );
 
 			dispatchEvent( el, 'click' );
 			// Spy was called for view1 (1st dispatch), then for view1 and view2 (2nd dispatch).