Преглед изворни кода

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 година
родитељ
комит
d3d3da4b97

+ 62 - 0
packages/ckeditor5-utils/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;
+	}
+}

+ 27 - 0
packages/ckeditor5-utils/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;
+	}
+}

+ 64 - 0
packages/ckeditor5-utils/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' );
+		} );
+	} );
+} );