From 767736f4bb444693bcce8b14eb8b38189231e1de Mon Sep 17 00:00:00 2001 From: James Newell Date: Thu, 20 Feb 2020 09:06:46 +1100 Subject: [PATCH 01/18] refactored i18n to be an instantiable class export default instance updated the changelog instantiate locale in constructor refactor as per feedback rename I18N to I18n updated docs added isRTL --- packages/i18n/CHANGELOG.md | 2 +- packages/i18n/README.md | 111 ++--------------- packages/i18n/src/default-i18n.js | 12 ++ packages/i18n/src/i18n.js | 172 +++++++++++++++++++++++++++ packages/i18n/src/index.js | 190 +----------------------------- packages/i18n/src/sprintf.js | 35 ++++++ packages/i18n/src/test/i18n.js | 29 +++++ 7 files changed, 266 insertions(+), 285 deletions(-) create mode 100644 packages/i18n/src/default-i18n.js create mode 100644 packages/i18n/src/i18n.js create mode 100644 packages/i18n/src/sprintf.js create mode 100644 packages/i18n/src/test/i18n.js diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md index e9b5255dae532..3c31a57b1cd1d 100644 --- a/packages/i18n/CHANGELOG.md +++ b/packages/i18n/CHANGELOG.md @@ -4,7 +4,7 @@ - Add `isRTL` function ([#20298](https://github.com/WordPress/gutenberg/pull/20298)) - Include TypeScript type declarations ([#18942](https://github.com/WordPress/gutenberg/pull/18942)) - +- Add `I18n` class to allow creation of multiple i18n instances. (#19210) ## 3.1.0 (2018-11-15) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index 21847b5f3f017..fe484d7e0df66 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -27,126 +27,41 @@ For a complete example, see the [Internationalization section of the Block Edito -# **isRTL** - -Check if current locale is RTL. - -**RTL (Right To Left)** is a locale property indicating that text is written from right to left. -For example, the `he` locale (for Hebrew) specifies right-to-left. Arabic (ar) is another common -language written RTL. The opposite of RTL, LTR (Left To Right) is used in other languages, -including English (`en`, `en-US`, `en-GB`, etc.), Spanish (`es`), and French (`fr`). +# **I18n** -_Returns_ +Instantiable I18n methods -- `boolean`: Whether locale is RTL. - -# **setLocaleData** +# **i18n** -Merges locale data into the Tannin instance by domain. Accepts data in a -Jed-formatted JSON object shape. +Undocumented declaration. -_Related_ +# **isRTL** -- +Undocumented declaration. -_Parameters_ +# **setLocaleData** -- _data_ `[LocaleData]`: Locale data configuration. -- _domain_ `[string]`: Domain for which configuration applies. +Undocumented declaration. # **sprintf** -Returns a formatted string. If an error occurs in applying the format, the -original format string is returned. - -_Related_ - -- - -_Parameters_ - -- _format_ `string`: The format of the string to generate. -- _args_ `...string`: Arguments to apply to the format. - -_Returns_ - -- `string`: The formatted string. +Internal dependencies # **\_n** -Translates and retrieves the singular or plural form based on the supplied -number. - -_Related_ - -- - -_Parameters_ - -- _single_ `string`: The text to be used if the number is singular. -- _plural_ `string`: The text to be used if the number is plural. -- _number_ `number`: The number to compare against to use either the singular or plural form. -- _domain_ `[string]`: Domain to retrieve the translated text. - -_Returns_ - -- `string`: The translated singular or plural form. +Undocumented declaration. # **\_nx** -Translates and retrieves the singular or plural form based on the supplied -number, with gettext context. - -_Related_ - -- - -_Parameters_ - -- _single_ `string`: The text to be used if the number is singular. -- _plural_ `string`: The text to be used if the number is plural. -- _number_ `number`: The number to compare against to use either the singular or plural form. -- _context_ `string`: Context information for the translators. -- _domain_ `[string]`: Domain to retrieve the translated text. - -_Returns_ - -- `string`: The translated singular or plural form. +Undocumented declaration. # **\_x** -Retrieve translated string with gettext context. - -_Related_ - -- - -_Parameters_ - -- _text_ `string`: Text to translate. -- _context_ `string`: Context information for the translators. -- _domain_ `[string]`: Domain to retrieve the translated text. - -_Returns_ - -- `string`: Translated context string without pipe. +Undocumented declaration. # **\_\_** -Retrieve the translation of text. - -_Related_ - -- - -_Parameters_ - -- _text_ `string`: Text to translate. -- _domain_ `[string]`: Domain to retrieve the translated text. - -_Returns_ - -- `string`: Translated text. +Undocumented declaration. diff --git a/packages/i18n/src/default-i18n.js b/packages/i18n/src/default-i18n.js new file mode 100644 index 0000000000000..2d28c38d31264 --- /dev/null +++ b/packages/i18n/src/default-i18n.js @@ -0,0 +1,12 @@ +/** + * Internal dependencies + */ +import { I18n } from './i18n'; + +export const i18n = new I18n(); +export const setLocaleData = i18n.setLocaleData.bind( i18n ); +export const __ = i18n.__.bind( i18n ); +export const _x = i18n._x.bind( i18n ); +export const _n = i18n._n.bind( i18n ); +export const _nx = i18n._nx.bind( i18n ); +export const isRTL = i18n.isRTL.bind( i18n ); diff --git a/packages/i18n/src/i18n.js b/packages/i18n/src/i18n.js new file mode 100644 index 0000000000000..d5ef5a4428992 --- /dev/null +++ b/packages/i18n/src/i18n.js @@ -0,0 +1,172 @@ +/** + * External dependencies + */ +import Tannin from 'tannin'; + +/** + * @typedef {Record} LocaleData + */ + +/** + * Default locale data to use for Tannin domain when not otherwise provided. + * Assumes an English plural forms expression. + * + * @type {LocaleData} + */ +const DEFAULT_LOCALE_DATA = { + '': { + plural_forms: ( n ) => ( n === 1 ? 0 : 1 ), + }, +}; + +/** + * Instantiable I18n methods + */ +export class I18n { + /** + * @param {LocaleData} [data] Locale data configuration. + * @param {string} [domain] Domain for which configuration applies. + */ + constructor( data, domain ) { + /** + * The underlying instance of Tannin to which exported functions interface. + * + * @type {Tannin} + */ + this.tannin = new Tannin( {} ); + this.setLocaleData( data, domain ); + } + + /** + * Merges locale data into the Tannin instance by domain. Accepts data in a + * Jed-formatted JSON object shape. + * + * @see http://messageformat.github.io/Jed/ + * + * @param {LocaleData} [data] Locale data configuration. + * @param {string} [domain] Domain for which configuration applies. + */ + setLocaleData( data, domain = 'default' ) { + this.tannin.data[ domain ] = { + ...DEFAULT_LOCALE_DATA, + ...this.tannin.data[ domain ], + ...data, + }; + + // Populate default domain configuration (supported locale date which omits + // a plural forms expression). + this.tannin.data[ domain ][ '' ] = { + ...DEFAULT_LOCALE_DATA[ '' ], + ...this.tannin.data[ domain ][ '' ], + }; + } + + /** + * Wrapper for Tannin's `dcnpgettext`. Populates default locale data if not + * otherwise previously assigned. + * + * @param {string|undefined} domain Domain to retrieve the translated text. + * @param {string|undefined} context Context information for the translators. + * @param {string} single Text to translate if non-plural. Used as + * fallback return value on a caught error. + * @param {string} [plural] The text to be used if the number is + * plural. + * @param {number} [number] The number to compare against to use + * either the singular or plural form. + * + * @return {string} The translated string. + */ + dcnpgettext( domain = 'default', context, single, plural, number ) { + if ( ! this.tannin.data[ domain ] ) { + this.setLocaleData( undefined, domain ); + } + + return this.tannin.dcnpgettext( + domain, + context, + single, + plural, + number + ); + } + + /** + * Retrieve the translation of text. + * + * @see https://developer.wordpress.org/reference/functions/__/ + * + * @param {string} text Text to translate. + * @param {string} [domain] Domain to retrieve the translated text. + * + * @return {string} Translated text. + */ + __( text, domain ) { + return this.dcnpgettext( domain, undefined, text ); + } + + /** + * Retrieve translated string with gettext context. + * + * @see https://developer.wordpress.org/reference/functions/_x/ + * + * @param {string} text Text to translate. + * @param {string} context Context information for the translators. + * @param {string} [domain] Domain to retrieve the translated text. + * + * @return {string} Translated context string without pipe. + */ + _x( text, context, domain ) { + return this.dcnpgettext( domain, context, text ); + } + + /** + * Translates and retrieves the singular or plural form based on the supplied + * number. + * + * @see https://developer.wordpress.org/reference/functions/_n/ + * + * @param {string} single The text to be used if the number is singular. + * @param {string} plural The text to be used if the number is plural. + * @param {number} number The number to compare against to use either the + * singular or plural form. + * @param {string} [domain] Domain to retrieve the translated text. + * + * @return {string} The translated singular or plural form. + */ + _n( single, plural, number, domain ) { + return this.dcnpgettext( domain, undefined, single, plural, number ); + } + + /** + * Translates and retrieves the singular or plural form based on the supplied + * number, with gettext context. + * + * @see https://developer.wordpress.org/reference/functions/_nx/ + * + * @param {string} single The text to be used if the number is singular. + * @param {string} plural The text to be used if the number is plural. + * @param {number} number The number to compare against to use either the + * singular or plural form. + * @param {string} context Context information for the translators. + * @param {string} [domain] Domain to retrieve the translated text. + * + * @return {string} The translated singular or plural form. + */ + _nx( single, plural, number, context, domain ) { + return this.dcnpgettext( domain, context, single, plural, number ); + } + + /** + * Check if current locale is RTL. + * + * **RTL (Right To Left)** is a locale property indicating that text is written from right to left. + * For example, the `he` locale (for Hebrew) specifies right-to-left. Arabic (ar) is another common + * language written RTL. The opposite of RTL, LTR (Left To Right) is used in other languages, + * including English (`en`, `en-US`, `en-GB`, etc.), Spanish (`es`), and French (`fr`). + * + * @return {boolean} Whether locale is RTL. + */ + isRTL() { + return 'rtl' === this._x( 'ltr', 'text direction' ); + } +} diff --git a/packages/i18n/src/index.js b/packages/i18n/src/index.js index 111ea03e55571..1e840287e4636 100644 --- a/packages/i18n/src/index.js +++ b/packages/i18n/src/index.js @@ -1,189 +1,7 @@ /** - * External dependencies + * Internal dependencies */ -import Tannin from 'tannin'; -import memoize from 'memize'; -import sprintfjs from 'sprintf-js'; -/** - * @typedef {Record} LocaleData - */ - -/** - * Default locale data to use for Tannin domain when not otherwise provided. - * Assumes an English plural forms expression. - * - * @type {LocaleData} - */ -const DEFAULT_LOCALE_DATA = { - '': { - /** @param {number} n */ - plural_forms( n ) { - return n === 1 ? 0 : 1; - }, - }, -}; - -/** - * Log to console, once per message; or more precisely, per referentially equal - * argument set. Because Jed throws errors, we log these to the console instead - * to avoid crashing the application. - * - * @param {...*} args Arguments to pass to `console.error` - */ -const logErrorOnce = memoize( console.error ); // eslint-disable-line no-console - -/** - * The underlying instance of Tannin to which exported functions interface. - * - * @type {Tannin} - */ -const i18n = new Tannin( {} ); - -/** - * Merges locale data into the Tannin instance by domain. Accepts data in a - * Jed-formatted JSON object shape. - * - * @see http://messageformat.github.io/Jed/ - * - * @param {LocaleData} [data] Locale data configuration. - * @param {string} [domain] Domain for which configuration applies. - */ -export function setLocaleData( data, domain = 'default' ) { - i18n.data[ domain ] = { - ...DEFAULT_LOCALE_DATA, - ...i18n.data[ domain ], - ...data, - }; - - // Populate default domain configuration (supported locale date which omits - // a plural forms expression). - i18n.data[ domain ][ '' ] = { - ...DEFAULT_LOCALE_DATA[ '' ], - ...i18n.data[ domain ][ '' ], - }; -} - -/** - * Wrapper for Tannin's `dcnpgettext`. Populates default locale data if not - * otherwise previously assigned. - * - * @param {string|undefined} domain Domain to retrieve the translated text. - * @param {string|undefined} context Context information for the translators. - * @param {string} single Text to translate if non-plural. Used as - * fallback return value on a caught error. - * @param {string} [plural] The text to be used if the number is - * plural. - * @param {number} [number] The number to compare against to use - * either the singular or plural form. - * - * @return {string} The translated string. - */ -function dcnpgettext( domain = 'default', context, single, plural, number ) { - if ( ! i18n.data[ domain ] ) { - setLocaleData( undefined, domain ); - } - - return i18n.dcnpgettext( domain, context, single, plural, number ); -} - -/** - * Retrieve the translation of text. - * - * @see https://developer.wordpress.org/reference/functions/__/ - * - * @param {string} text Text to translate. - * @param {string} [domain] Domain to retrieve the translated text. - * - * @return {string} Translated text. - */ -export function __( text, domain ) { - return dcnpgettext( domain, undefined, text ); -} - -/** - * Retrieve translated string with gettext context. - * - * @see https://developer.wordpress.org/reference/functions/_x/ - * - * @param {string} text Text to translate. - * @param {string} context Context information for the translators. - * @param {string} [domain] Domain to retrieve the translated text. - * - * @return {string} Translated context string without pipe. - */ -export function _x( text, context, domain ) { - return dcnpgettext( domain, context, text ); -} - -/** - * Translates and retrieves the singular or plural form based on the supplied - * number. - * - * @see https://developer.wordpress.org/reference/functions/_n/ - * - * @param {string} single The text to be used if the number is singular. - * @param {string} plural The text to be used if the number is plural. - * @param {number} number The number to compare against to use either the - * singular or plural form. - * @param {string} [domain] Domain to retrieve the translated text. - * - * @return {string} The translated singular or plural form. - */ -export function _n( single, plural, number, domain ) { - return dcnpgettext( domain, undefined, single, plural, number ); -} - -/** - * Translates and retrieves the singular or plural form based on the supplied - * number, with gettext context. - * - * @see https://developer.wordpress.org/reference/functions/_nx/ - * - * @param {string} single The text to be used if the number is singular. - * @param {string} plural The text to be used if the number is plural. - * @param {number} number The number to compare against to use either the - * singular or plural form. - * @param {string} context Context information for the translators. - * @param {string} [domain] Domain to retrieve the translated text. - * - * @return {string} The translated singular or plural form. - */ -export function _nx( single, plural, number, context, domain ) { - return dcnpgettext( domain, context, single, plural, number ); -} - -/** - * Check if current locale is RTL. - * - * **RTL (Right To Left)** is a locale property indicating that text is written from right to left. - * For example, the `he` locale (for Hebrew) specifies right-to-left. Arabic (ar) is another common - * language written RTL. The opposite of RTL, LTR (Left To Right) is used in other languages, - * including English (`en`, `en-US`, `en-GB`, etc.), Spanish (`es`), and French (`fr`). - * - * @return {boolean} Whether locale is RTL. - */ -export function isRTL() { - return 'rtl' === _x( 'ltr', 'text direction' ); -} - -/** - * Returns a formatted string. If an error occurs in applying the format, the - * original format string is returned. - * - * @param {string} format The format of the string to generate. - * @param {...string} args Arguments to apply to the format. - * - * @see http://www.diveintojavascript.com/projects/javascript-sprintf - * - * @return {string} The formatted string. - */ -export function sprintf( format, ...args ) { - try { - return sprintfjs.sprintf( format, ...args ); - } catch ( error ) { - logErrorOnce( 'sprintf error: \n\n' + error.toString() ); - - return format; - } -} +export { sprintf } from './sprintf'; +export { I18n } from './i18n'; +export * from './default-i18n'; diff --git a/packages/i18n/src/sprintf.js b/packages/i18n/src/sprintf.js new file mode 100644 index 0000000000000..397fe7abe4e40 --- /dev/null +++ b/packages/i18n/src/sprintf.js @@ -0,0 +1,35 @@ +/** + * External dependencies + */ +import memoize from 'memize'; +import sprintfjs from 'sprintf-js'; + +/** + * Log to console, once per message; or more precisely, per referentially equal + * argument set. Because Jed throws errors, we log these to the console instead + * to avoid crashing the application. + * + * @param {...*} args Arguments to pass to `console.error` + */ +const logErrorOnce = memoize( console.error ); // eslint-disable-line no-console + +/** + * Returns a formatted string. If an error occurs in applying the format, the + * original format string is returned. + * + * @param {string} format The format of the string to generate. + * @param {...string} args Arguments to apply to the format. + * + * @see http://www.diveintojavascript.com/projects/javascript-sprintf + * + * @return {string} The formatted string. + */ +export function sprintf( format, ...args ) { + try { + return sprintfjs.sprintf( format, ...args ); + } catch ( error ) { + logErrorOnce( 'sprintf error: \n\n' + error.toString() ); + + return format; + } +} diff --git a/packages/i18n/src/test/i18n.js b/packages/i18n/src/test/i18n.js new file mode 100644 index 0000000000000..7f04586030a0d --- /dev/null +++ b/packages/i18n/src/test/i18n.js @@ -0,0 +1,29 @@ +/** + * Internal dependencies + */ +import { I18n } from '../'; + +const strayaLocale = { + hello: [ 'gday' ], +}; + +const frenchLocale = { + hello: [ 'bonjour' ], +}; + +describe( 'I18n', () => { + test( 'instantiated with locale data', () => { + const straya = new I18n( strayaLocale ); + expect( straya.__( 'hello' ) ).toEqual( 'gday' ); + } ); + test( 'multiple instances maintain their own distinct locale data', () => { + const straya = new I18n(); + const french = new I18n(); + + straya.setLocaleData( strayaLocale ); + french.setLocaleData( frenchLocale ); + + expect( straya.__( 'hello' ) ).toEqual( 'gday' ); + expect( french.__( 'hello' ) ).toEqual( 'bonjour' ); + } ); +} ); From 3375c7b177f8cdf66fc56beb28b694923d6aafea Mon Sep 17 00:00:00 2001 From: James Newell Date: Mon, 2 Mar 2020 10:49:21 +1100 Subject: [PATCH 02/18] use explicit exports --- packages/i18n/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18n/src/index.js b/packages/i18n/src/index.js index 1e840287e4636..fc92b753be47d 100644 --- a/packages/i18n/src/index.js +++ b/packages/i18n/src/index.js @@ -4,4 +4,4 @@ export { sprintf } from './sprintf'; export { I18n } from './i18n'; -export * from './default-i18n'; +export { i18n, setLocaleData, __, _x, _n, _nx, isRTL } from './default-i18n'; From 6e6f76bce0c85e4479c302e07f0efa6cf4f11db5 Mon Sep 17 00:00:00 2001 From: James Newell Date: Mon, 2 Mar 2020 11:02:51 +1100 Subject: [PATCH 03/18] update docs --- packages/i18n/README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index fe484d7e0df66..9e53ec28d1468 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -45,7 +45,21 @@ Undocumented declaration. # **sprintf** -Internal dependencies +Returns a formatted string. If an error occurs in applying the format, the +original format string is returned. + +_Related_ + +- + +_Parameters_ + +- _format_ `string`: The format of the string to generate. +- _args_ `...string`: Arguments to apply to the format. + +_Returns_ + +- `string`: The formatted string. # **\_n** From 680aca2dd004c20d86f51cff476cf91b03234387 Mon Sep 17 00:00:00 2001 From: James Newell Date: Mon, 2 Mar 2020 11:03:07 +1100 Subject: [PATCH 04/18] remove comment --- packages/i18n/src/index.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/i18n/src/index.js b/packages/i18n/src/index.js index fc92b753be47d..f9869bbcca8bb 100644 --- a/packages/i18n/src/index.js +++ b/packages/i18n/src/index.js @@ -1,7 +1,3 @@ -/** - * Internal dependencies - */ - export { sprintf } from './sprintf'; export { I18n } from './i18n'; export { i18n, setLocaleData, __, _x, _n, _nx, isRTL } from './default-i18n'; From 0a0e99444af7c9b5232826c16bea8873e9032085 Mon Sep 17 00:00:00 2001 From: James Newell Date: Mon, 2 Mar 2020 11:14:14 +1100 Subject: [PATCH 05/18] duplicate comments --- packages/i18n/README.md | 91 +++++++++++++++++++++++++++++-- packages/i18n/src/default-i18n.js | 84 ++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+), 6 deletions(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index 9e53ec28d1468..6ae807c296958 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -37,11 +37,30 @@ Undocumented declaration. # **isRTL** -Undocumented declaration. +Check if current locale is RTL. + +**RTL (Right To Left)** is a locale property indicating that text is written from right to left. +For example, the `he` locale (for Hebrew) specifies right-to-left. Arabic (ar) is another common +language written RTL. The opposite of RTL, LTR (Left To Right) is used in other languages, +including English (`en`, `en-US`, `en-GB`, etc.), Spanish (`es`), and French (`fr`). + +_Returns_ + +- `boolean`: Whether locale is RTL. # **setLocaleData** -Undocumented declaration. +Merges locale data into the Tannin instance by domain. Accepts data in a +Jed-formatted JSON object shape. + +_Related_ + +- + +_Parameters_ + +- _data_ `[LocaleData]`: Locale data configuration. +- _domain_ `[string]`: Domain for which configuration applies. # **sprintf** @@ -63,19 +82,79 @@ _Returns_ # **\_n** -Undocumented declaration. +Translates and retrieves the singular or plural form based on the supplied +number. + +_Related_ + +- + +_Parameters_ + +- _single_ `string`: The text to be used if the number is singular. +- _plural_ `string`: The text to be used if the number is plural. +- _number_ `number`: The number to compare against to use either the singular or plural form. +- _domain_ `[string]`: Domain to retrieve the translated text. + +_Returns_ + +- `string`: The translated singular or plural form. # **\_nx** -Undocumented declaration. +Translates and retrieves the singular or plural form based on the supplied +number, with gettext context. + +_Related_ + +- + +_Parameters_ + +- _single_ `string`: The text to be used if the number is singular. +- _plural_ `string`: The text to be used if the number is plural. +- _number_ `number`: The number to compare against to use either the singular or plural form. +- _context_ `string`: Context information for the translators. +- _domain_ `[string]`: Domain to retrieve the translated text. + +_Returns_ + +- `string`: The translated singular or plural form. # **\_x** -Undocumented declaration. +Retrieve translated string with gettext context. + +_Related_ + +- + +_Parameters_ + +- _text_ `string`: Text to translate. +- _context_ `string`: Context information for the translators. +- _domain_ `[string]`: Domain to retrieve the translated text. + +_Returns_ + +- `string`: Translated context string without pipe. # **\_\_** -Undocumented declaration. +Retrieve the translation of text. + +_Related_ + +- + +_Parameters_ + +- _text_ `string`: Text to translate. +- _domain_ `[string]`: Domain to retrieve the translated text. + +_Returns_ + +- `string`: Translated text. diff --git a/packages/i18n/src/default-i18n.js b/packages/i18n/src/default-i18n.js index 2d28c38d31264..9475caa10e1c3 100644 --- a/packages/i18n/src/default-i18n.js +++ b/packages/i18n/src/default-i18n.js @@ -4,9 +4,93 @@ import { I18n } from './i18n'; export const i18n = new I18n(); + +/* + * Comments in this file are duplicated from ./i18n due to + * https://github.com/WordPress/gutenberg/pull/20318#issuecomment-590837722 + */ + +/** + * @typedef {{[key: string]: any}} LocaleData + */ + +/** + * Merges locale data into the Tannin instance by domain. Accepts data in a + * Jed-formatted JSON object shape. + * + * @see http://messageformat.github.io/Jed/ + * + * @param {LocaleData} [data] Locale data configuration. + * @param {string} [domain] Domain for which configuration applies. + */ export const setLocaleData = i18n.setLocaleData.bind( i18n ); + +/** + * Retrieve the translation of text. + * + * @see https://developer.wordpress.org/reference/functions/__/ + * + * @param {string} text Text to translate. + * @param {string} [domain] Domain to retrieve the translated text. + * + * @return {string} Translated text. + */ export const __ = i18n.__.bind( i18n ); + +/** + * Retrieve translated string with gettext context. + * + * @see https://developer.wordpress.org/reference/functions/_x/ + * + * @param {string} text Text to translate. + * @param {string} context Context information for the translators. + * @param {string} [domain] Domain to retrieve the translated text. + * + * @return {string} Translated context string without pipe. + */ export const _x = i18n._x.bind( i18n ); + +/** + * Translates and retrieves the singular or plural form based on the supplied + * number. + * + * @see https://developer.wordpress.org/reference/functions/_n/ + * + * @param {string} single The text to be used if the number is singular. + * @param {string} plural The text to be used if the number is plural. + * @param {number} number The number to compare against to use either the + * singular or plural form. + * @param {string} [domain] Domain to retrieve the translated text. + * + * @return {string} The translated singular or plural form. + */ export const _n = i18n._n.bind( i18n ); + +/** + * Translates and retrieves the singular or plural form based on the supplied + * number, with gettext context. + * + * @see https://developer.wordpress.org/reference/functions/_nx/ + * + * @param {string} single The text to be used if the number is singular. + * @param {string} plural The text to be used if the number is plural. + * @param {number} number The number to compare against to use either the + * singular or plural form. + * @param {string} context Context information for the translators. + * @param {string} [domain] Domain to retrieve the translated text. + * + * @return {string} The translated singular or plural form. + */ export const _nx = i18n._nx.bind( i18n ); + +/** + * Check if current locale is RTL. + * + * **RTL (Right To Left)** is a locale property indicating that text is written from right to left. + * For example, the `he` locale (for Hebrew) specifies right-to-left. Arabic (ar) is another common + * language written RTL. The opposite of RTL, LTR (Left To Right) is used in other languages, + * including English (`en`, `en-US`, `en-GB`, etc.), Spanish (`es`), and French (`fr`). + * + * @return {boolean} Whether locale is RTL. + */ export const isRTL = i18n.isRTL.bind( i18n ); From 59264fe0e8c0c6ad9c4672c9c848278652211ae4 Mon Sep 17 00:00:00 2001 From: James Newell Date: Mon, 2 Mar 2020 12:11:47 +1100 Subject: [PATCH 06/18] switch from I18n class to createI18n fn --- packages/i18n/README.md | 7 +- packages/i18n/src/{i18n.js => create-i18n.js} | 97 ++++++++++--------- packages/i18n/src/default-i18n.js | 4 +- packages/i18n/src/index.js | 2 +- .../i18n/src/test/{i18n.js => create-i18n.js} | 10 +- 5 files changed, 64 insertions(+), 56 deletions(-) rename packages/i18n/src/{i18n.js => create-i18n.js} (74%) rename packages/i18n/src/test/{i18n.js => create-i18n.js} (74%) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index 6ae807c296958..b185366d49426 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -27,9 +27,12 @@ For a complete example, see the [Internationalization section of the Block Edito -# **I18n** +# **createI18n** -Instantiable I18n methods +_Parameters_ + +- _initialData_ `[LocaleData]`: Locale data configuration. +- _initialDomain_ `[string]`: Domain for which configuration applies. # **i18n** diff --git a/packages/i18n/src/i18n.js b/packages/i18n/src/create-i18n.js similarity index 74% rename from packages/i18n/src/i18n.js rename to packages/i18n/src/create-i18n.js index d5ef5a4428992..8d5bb9657cc8f 100644 --- a/packages/i18n/src/i18n.js +++ b/packages/i18n/src/create-i18n.js @@ -20,22 +20,16 @@ const DEFAULT_LOCALE_DATA = { }; /** - * Instantiable I18n methods + * @param {LocaleData} [initialData] Locale data configuration. + * @param {string} [initialDomain] Domain for which configuration applies. */ -export class I18n { +export const createI18n = ( initialData, initialDomain ) => { /** - * @param {LocaleData} [data] Locale data configuration. - * @param {string} [domain] Domain for which configuration applies. + * The underlying instance of Tannin to which exported functions interface. + * + * @type {Tannin} */ - constructor( data, domain ) { - /** - * The underlying instance of Tannin to which exported functions interface. - * - * @type {Tannin} - */ - this.tannin = new Tannin( {} ); - this.setLocaleData( data, domain ); - } + const tannin = new Tannin( {} ); /** * Merges locale data into the Tannin instance by domain. Accepts data in a @@ -46,20 +40,20 @@ export class I18n { * @param {LocaleData} [data] Locale data configuration. * @param {string} [domain] Domain for which configuration applies. */ - setLocaleData( data, domain = 'default' ) { - this.tannin.data[ domain ] = { + const setLocaleData = ( data, domain = 'default' ) => { + tannin.data[ domain ] = { ...DEFAULT_LOCALE_DATA, - ...this.tannin.data[ domain ], + ...tannin.data[ domain ], ...data, }; // Populate default domain configuration (supported locale date which omits // a plural forms expression). - this.tannin.data[ domain ][ '' ] = { + tannin.data[ domain ][ '' ] = { ...DEFAULT_LOCALE_DATA[ '' ], - ...this.tannin.data[ domain ][ '' ], + ...tannin.data[ domain ][ '' ], }; - } + }; /** * Wrapper for Tannin's `dcnpgettext`. Populates default locale data if not @@ -76,19 +70,19 @@ export class I18n { * * @return {string} The translated string. */ - dcnpgettext( domain = 'default', context, single, plural, number ) { - if ( ! this.tannin.data[ domain ] ) { - this.setLocaleData( undefined, domain ); + const dcnpgettext = ( + domain = 'default', + context, + single, + plural, + number + ) => { + if ( ! tannin.data[ domain ] ) { + setLocaleData( undefined, domain ); } - return this.tannin.dcnpgettext( - domain, - context, - single, - plural, - number - ); - } + return tannin.dcnpgettext( domain, context, single, plural, number ); + }; /** * Retrieve the translation of text. @@ -100,9 +94,9 @@ export class I18n { * * @return {string} Translated text. */ - __( text, domain ) { - return this.dcnpgettext( domain, undefined, text ); - } + const __ = ( text, domain ) => { + return dcnpgettext( domain, undefined, text ); + }; /** * Retrieve translated string with gettext context. @@ -115,9 +109,9 @@ export class I18n { * * @return {string} Translated context string without pipe. */ - _x( text, context, domain ) { - return this.dcnpgettext( domain, context, text ); - } + const _x = ( text, context, domain ) => { + return dcnpgettext( domain, context, text ); + }; /** * Translates and retrieves the singular or plural form based on the supplied @@ -133,9 +127,9 @@ export class I18n { * * @return {string} The translated singular or plural form. */ - _n( single, plural, number, domain ) { - return this.dcnpgettext( domain, undefined, single, plural, number ); - } + const _n = ( single, plural, number, domain ) => { + return dcnpgettext( domain, undefined, single, plural, number ); + }; /** * Translates and retrieves the singular or plural form based on the supplied @@ -152,9 +146,9 @@ export class I18n { * * @return {string} The translated singular or plural form. */ - _nx( single, plural, number, context, domain ) { - return this.dcnpgettext( domain, context, single, plural, number ); - } + const _nx = ( single, plural, number, context, domain ) => { + return dcnpgettext( domain, context, single, plural, number ); + }; /** * Check if current locale is RTL. @@ -166,7 +160,18 @@ export class I18n { * * @return {boolean} Whether locale is RTL. */ - isRTL() { - return 'rtl' === this._x( 'ltr', 'text direction' ); - } -} + const isRTL = () => { + return 'rtl' === _x( 'ltr', 'text direction' ); + }; + + setLocaleData( initialData, initialDomain ); + + return { + setLocaleData, + __, + _x, + _n, + _nx, + isRTL, + }; +}; diff --git a/packages/i18n/src/default-i18n.js b/packages/i18n/src/default-i18n.js index 9475caa10e1c3..12cbd25d2317f 100644 --- a/packages/i18n/src/default-i18n.js +++ b/packages/i18n/src/default-i18n.js @@ -1,9 +1,9 @@ /** * Internal dependencies */ -import { I18n } from './i18n'; +import { createI18n } from './create-i18n'; -export const i18n = new I18n(); +export const i18n = createI18n(); /* * Comments in this file are duplicated from ./i18n due to diff --git a/packages/i18n/src/index.js b/packages/i18n/src/index.js index f9869bbcca8bb..0e8cfd83decb9 100644 --- a/packages/i18n/src/index.js +++ b/packages/i18n/src/index.js @@ -1,3 +1,3 @@ export { sprintf } from './sprintf'; -export { I18n } from './i18n'; +export { createI18n } from './create-i18n'; export { i18n, setLocaleData, __, _x, _n, _nx, isRTL } from './default-i18n'; diff --git a/packages/i18n/src/test/i18n.js b/packages/i18n/src/test/create-i18n.js similarity index 74% rename from packages/i18n/src/test/i18n.js rename to packages/i18n/src/test/create-i18n.js index 7f04586030a0d..65baaddaf7324 100644 --- a/packages/i18n/src/test/i18n.js +++ b/packages/i18n/src/test/create-i18n.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { I18n } from '../'; +import { createI18n } from '..'; const strayaLocale = { hello: [ 'gday' ], @@ -11,14 +11,14 @@ const frenchLocale = { hello: [ 'bonjour' ], }; -describe( 'I18n', () => { +describe( 'createI18n', () => { test( 'instantiated with locale data', () => { - const straya = new I18n( strayaLocale ); + const straya = createI18n( strayaLocale ); expect( straya.__( 'hello' ) ).toEqual( 'gday' ); } ); test( 'multiple instances maintain their own distinct locale data', () => { - const straya = new I18n(); - const french = new I18n(); + const straya = createI18n(); + const french = createI18n(); straya.setLocaleData( strayaLocale ); french.setLocaleData( frenchLocale ); From 265a5bc137f5daf8c1558a5aeb43869ddd74a955 Mon Sep 17 00:00:00 2001 From: James Newell Date: Tue, 3 Mar 2020 15:58:40 +1100 Subject: [PATCH 07/18] don't export default instance --- packages/i18n/src/default-i18n.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18n/src/default-i18n.js b/packages/i18n/src/default-i18n.js index 12cbd25d2317f..760ad5240342a 100644 --- a/packages/i18n/src/default-i18n.js +++ b/packages/i18n/src/default-i18n.js @@ -3,7 +3,7 @@ */ import { createI18n } from './create-i18n'; -export const i18n = createI18n(); +const i18n = createI18n(); /* * Comments in this file are duplicated from ./i18n due to From a38540352c295b1949bba39ac6e789217e917df2 Mon Sep 17 00:00:00 2001 From: James Newell Date: Tue, 3 Mar 2020 16:21:23 +1100 Subject: [PATCH 08/18] refactor tests --- packages/i18n/src/test/create-i18n.js | 161 ++++++++++++++++++++++ packages/i18n/src/test/index.js | 187 -------------------------- packages/i18n/src/test/sprintf.js | 27 ++++ 3 files changed, 188 insertions(+), 187 deletions(-) delete mode 100644 packages/i18n/src/test/index.js create mode 100644 packages/i18n/src/test/sprintf.js diff --git a/packages/i18n/src/test/create-i18n.js b/packages/i18n/src/test/create-i18n.js index 65baaddaf7324..75e05c1f434a7 100644 --- a/packages/i18n/src/test/create-i18n.js +++ b/packages/i18n/src/test/create-i18n.js @@ -11,11 +11,44 @@ const frenchLocale = { hello: [ 'bonjour' ], }; +const localeData = { + '': { + // Domain name + domain: 'test_domain', + lang: 'fr', + // Plural form function for language + plural_forms: 'nplurals=2; plural=(n != 1);', + }, + + hello: [ 'bonjour' ], + + 'verb\u0004feed': [ 'nourrir' ], + + 'hello %s': [ 'bonjour %s' ], + + '%d banana': [ '%d banane', '%d bananes' ], + + 'fruit\u0004%d apple': [ '%d pomme', '%d pommes' ], +}; + +const additionalLocaleData = { + cheeseburger: [ 'hamburger au fromage' ], + '%d cat': [ '%d chat', '%d chats' ], +}; + +const createTestLocale = () => createI18n( localeData, 'test_domain' ); +const createTestLocaleWithAdditionalData = () => { + const locale = createI18n( localeData, 'test_domain' ); + locale.setLocaleData( additionalLocaleData, 'test_domain' ); + return locale; +}; + describe( 'createI18n', () => { test( 'instantiated with locale data', () => { const straya = createI18n( strayaLocale ); expect( straya.__( 'hello' ) ).toEqual( 'gday' ); } ); + test( 'multiple instances maintain their own distinct locale data', () => { const straya = createI18n(); const french = createI18n(); @@ -26,4 +59,132 @@ describe( 'createI18n', () => { expect( straya.__( 'hello' ) ).toEqual( 'gday' ); expect( french.__( 'hello' ) ).toEqual( 'bonjour' ); } ); + + describe( '__', () => { + it( 'use the translation', () => { + const locale = createTestLocale(); + expect( locale.__( 'hello', 'test_domain' ) ).toBe( 'bonjour' ); + } ); + } ); + + describe( '_x', () => { + it( 'use the translation with context', () => { + const locale = createTestLocale(); + expect( locale._x( 'feed', 'verb', 'test_domain' ) ).toBe( + 'nourrir' + ); + } ); + } ); + + describe( '_n', () => { + it( 'use the plural form', () => { + const locale = createTestLocale(); + expect( + locale._n( '%d banana', '%d bananas', 3, 'test_domain' ) + ).toBe( '%d bananes' ); + } ); + + it( 'use the singular form', () => { + const locale = createTestLocale(); + expect( + locale._n( '%d banana', '%d bananas', 1, 'test_domain' ) + ).toBe( '%d banane' ); + } ); + } ); + + describe( '_nx', () => { + it( 'use the plural form', () => { + const locale = createTestLocale(); + expect( + locale._nx( '%d apple', '%d apples', 3, 'fruit', 'test_domain' ) + ).toBe( '%d pommes' ); + } ); + + it( 'use the singular form', () => { + const locale = createTestLocale(); + expect( + locale._nx( '%d apple', '%d apples', 1, 'fruit', 'test_domain' ) + ).toBe( '%d pomme' ); + } ); + } ); + + describe( 'isRTL', () => { + const ARLocaleData = { + '': { + plural_forms: + 'nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;', + language: 'ar', + localeSlug: 'ar', + }, + 'text direction\u0004ltr': [ 'rtl' ], + Back: [ 'رجوع' ], + }; + + it( 'is false for non-rtl', () => { + const locale = createI18n(); + expect( locale.isRTL() ).toBe( false ); + } ); + + it( 'is true for rtl', () => { + const locale = createI18n( ARLocaleData ); + expect( locale.isRTL() ).toBe( true ); + } ); + } ); + + describe( 'setLocaleData', () => { + it( 'supports omitted plural forms expression', () => { + const locale = createTestLocaleWithAdditionalData(); + locale.setLocaleData( + { + '': { + domain: 'test_domain2', + lang: 'fr', + }, + + '%d banana': [ '%d banane', '%d bananes' ], + }, + 'test_domain2' + ); + expect( + locale._n( '%d banana', '%d bananes', 2, 'test_domain2' ) + ).toBe( '%d bananes' ); + } ); + + describe( '__', () => { + it( 'existing translation still available', () => { + const locale = createTestLocaleWithAdditionalData(); + expect( locale.__( 'hello', 'test_domain' ) ).toBe( 'bonjour' ); + } ); + + it( 'new translation available.', () => { + const locale = createTestLocaleWithAdditionalData(); + expect( locale.__( 'cheeseburger', 'test_domain' ) ).toBe( + 'hamburger au fromage' + ); + } ); + } ); + + describe( '_n', () => { + it( 'existing plural form still works', () => { + const locale = createTestLocaleWithAdditionalData(); + expect( + locale._n( '%d banana', '%d bananas', 3, 'test_domain' ) + ).toBe( '%d bananes' ); + } ); + + it( 'new singular form was added', () => { + const locale = createTestLocaleWithAdditionalData(); + expect( + locale._n( '%d cat', '%d cats', 1, 'test_domain' ) + ).toBe( '%d chat' ); + } ); + + it( 'new plural form was added', () => { + const locale = createTestLocaleWithAdditionalData(); + expect( + locale._n( '%d cat', '%d cats', 3, 'test_domain' ) + ).toBe( '%d chats' ); + } ); + } ); + } ); } ); diff --git a/packages/i18n/src/test/index.js b/packages/i18n/src/test/index.js deleted file mode 100644 index 5246bc8f3659b..0000000000000 --- a/packages/i18n/src/test/index.js +++ /dev/null @@ -1,187 +0,0 @@ -// Mock memoization as identity function. Inline since Jest errors on out-of- -// scope references in a mock callback. -jest.mock( 'memize', () => ( fn ) => fn ); - -const localeData = { - '': { - // Domain name - domain: 'test_domain', - lang: 'fr', - // Plural form function for language - plural_forms: 'nplurals=2; plural=(n != 1);', - }, - - hello: [ 'bonjour' ], - - 'verb\u0004feed': [ 'nourrir' ], - - 'hello %s': [ 'bonjour %s' ], - - '%d banana': [ '%d banane', '%d bananes' ], - - 'fruit\u0004%d apple': [ '%d pomme', '%d pommes' ], -}; -const additionalLocaleData = { - cheeseburger: [ 'hamburger au fromage' ], - '%d cat': [ '%d chat', '%d chats' ], -}; - -// Get clean locale data -let sprintf, __, _x, _n, _nx, isRTL, setLocaleData; -beforeEach( () => { - const module = require.resolve( '..' ); - delete require.cache[ module ]; - ( { sprintf, __, _x, _n, _nx, isRTL, setLocaleData } = require( '..' ) ); -} ); - -describe( 'i18n', () => { - describe( '__', () => { - beforeEach( setDefaultLocalData ); - - it( 'use the translation', () => { - expect( __( 'hello', 'test_domain' ) ).toBe( 'bonjour' ); - } ); - } ); - - describe( '_x', () => { - beforeEach( setDefaultLocalData ); - - it( 'use the translation with context', () => { - expect( _x( 'feed', 'verb', 'test_domain' ) ).toBe( 'nourrir' ); - } ); - } ); - - describe( '_n', () => { - beforeEach( setDefaultLocalData ); - - it( 'use the plural form', () => { - expect( _n( '%d banana', '%d bananas', 3, 'test_domain' ) ).toBe( - '%d bananes' - ); - } ); - - it( 'use the singular form', () => { - expect( _n( '%d banana', '%d bananas', 1, 'test_domain' ) ).toBe( - '%d banane' - ); - } ); - } ); - - describe( '_nx', () => { - beforeEach( setDefaultLocalData ); - - it( 'use the plural form', () => { - expect( - _nx( '%d apple', '%d apples', 3, 'fruit', 'test_domain' ) - ).toBe( '%d pommes' ); - } ); - - it( 'use the singular form', () => { - expect( - _nx( '%d apple', '%d apples', 1, 'fruit', 'test_domain' ) - ).toBe( '%d pomme' ); - } ); - } ); - - describe( 'isRTL', () => { - const ARLocaleData = { - '': { - plural_forms: - 'nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;', - language: 'ar', - localeSlug: 'ar', - }, - 'text direction\u0004ltr': [ 'rtl' ], - Back: [ 'رجوع' ], - }; - - it( 'is false for non-rtl', () => { - expect( isRTL() ).toBe( false ); - } ); - - it( 'is true for rtl', () => { - setLocaleData( ARLocaleData ); - expect( isRTL() ).toBe( true ); - } ); - } ); - - describe( 'sprintf', () => { - beforeEach( setDefaultLocalData ); - - it( 'absorbs errors', () => { - // Disable reason: Failing case is the purpose of the test. - // eslint-disable-next-line @wordpress/valid-sprintf - const result = sprintf( 'Hello %(placeholder-not-provided)s' ); - - expect( console ).toHaveErrored(); - expect( result ).toBe( 'Hello %(placeholder-not-provided)s' ); - } ); - - it( 'replaces placeholders', () => { - const result = sprintf( __( 'hello %s', 'test_domain' ), 'Riad' ); - - expect( result ).toBe( 'bonjour Riad' ); - } ); - } ); - - describe( 'setLocaleData', () => { - beforeAll( () => { - setDefaultLocalData(); - setLocaleData( additionalLocaleData, 'test_domain' ); - } ); - - it( 'supports omitted plural forms expression', () => { - setLocaleData( - { - '': { - domain: 'test_domain2', - lang: 'fr', - }, - - '%d banana': [ '%d banane', '%d bananes' ], - }, - 'test_domain2' - ); - - expect( _n( '%d banana', '%d bananes', 2, 'test_domain2' ) ).toBe( - '%d bananes' - ); - } ); - - describe( '__', () => { - it( 'existing translation still available', () => { - expect( __( 'hello', 'test_domain' ) ).toBe( 'bonjour' ); - } ); - - it( 'new translation available.', () => { - expect( __( 'cheeseburger', 'test_domain' ) ).toBe( - 'hamburger au fromage' - ); - } ); - } ); - - describe( '_n', () => { - it( 'existing plural form still works', () => { - expect( - _n( '%d banana', '%d bananas', 3, 'test_domain' ) - ).toBe( '%d bananes' ); - } ); - - it( 'new singular form was added', () => { - expect( _n( '%d cat', '%d cats', 1, 'test_domain' ) ).toBe( - '%d chat' - ); - } ); - - it( 'new plural form was added', () => { - expect( _n( '%d cat', '%d cats', 3, 'test_domain' ) ).toBe( - '%d chats' - ); - } ); - } ); - } ); -} ); - -function setDefaultLocalData() { - setLocaleData( localeData, 'test_domain' ); -} diff --git a/packages/i18n/src/test/sprintf.js b/packages/i18n/src/test/sprintf.js new file mode 100644 index 0000000000000..3e9088e21132a --- /dev/null +++ b/packages/i18n/src/test/sprintf.js @@ -0,0 +1,27 @@ +// Mock memoization as identity function. Inline since Jest errors on out-of- +// scope references in a mock callback. +jest.mock( 'memize', () => ( fn ) => fn ); + +/** + * Internal dependencies + */ +import { sprintf } from '../sprintf'; + +describe( 'i18n', () => { + describe( 'sprintf', () => { + it( 'absorbs errors', () => { + // Disable reason: Failing case is the purpose of the test. + // eslint-disable-next-line @wordpress/valid-sprintf + const result = sprintf( 'Hello %(placeholder-not-provided)s' ); + + expect( console ).toHaveErrored(); + expect( result ).toBe( 'Hello %(placeholder-not-provided)s' ); + } ); + + it( 'replaces placeholders', () => { + const result = sprintf( 'bonjour %s', 'Riad' ); + + expect( result ).toBe( 'bonjour Riad' ); + } ); + } ); +} ); From 31a35c26374687ed3f684fa881e0bf3fb5294719 Mon Sep 17 00:00:00 2001 From: James Newell Date: Tue, 3 Mar 2020 16:39:38 +1100 Subject: [PATCH 09/18] improve docs --- packages/i18n/README.md | 6 ++++-- packages/i18n/src/create-i18n.js | 21 +++++++++++++++++++-- packages/i18n/src/index.js | 2 +- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index b185366d49426..f57ce4124c191 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -29,14 +29,16 @@ For a complete example, see the [Internationalization section of the Block Edito # **createI18n** +Create an i18n instance + _Parameters_ - _initialData_ `[LocaleData]`: Locale data configuration. - _initialDomain_ `[string]`: Domain for which configuration applies. -# **i18n** +_Returns_ -Undocumented declaration. +- `I18n`: I18n instance # **isRTL** diff --git a/packages/i18n/src/create-i18n.js b/packages/i18n/src/create-i18n.js index 8d5bb9657cc8f..02795f15ead4f 100644 --- a/packages/i18n/src/create-i18n.js +++ b/packages/i18n/src/create-i18n.js @@ -20,8 +20,25 @@ const DEFAULT_LOCALE_DATA = { }; /** - * @param {LocaleData} [initialData] Locale data configuration. - * @param {string} [initialDomain] Domain for which configuration applies. + * An i18n instance + * @typedef {Object} I18n + * @property {Function} setLocaleData Merges locale data into the Tannin instance by domain. Accepts data in a + * Jed-formatted JSON object shape. + * @property {Function} __ Retrieve the translation of text. + * @property {Function} _x Retrieve translated string with gettext context. + * @property {Function} _n Translates and retrieves the singular or plural form based on the supplied + * number. + * @property {Function} _nx Translates and retrieves the singular or plural form based on the supplied + * number, with gettext context. + * @property {Function} isRTL Check if current locale is RTL. + */ + +/** + * Create an i18n instance + * + * @param {LocaleData} [initialData] Locale data configuration. + * @param {string} [initialDomain] Domain for which configuration applies. + * @return {I18n} I18n instance */ export const createI18n = ( initialData, initialDomain ) => { /** diff --git a/packages/i18n/src/index.js b/packages/i18n/src/index.js index 0e8cfd83decb9..9e83b23e77971 100644 --- a/packages/i18n/src/index.js +++ b/packages/i18n/src/index.js @@ -1,3 +1,3 @@ export { sprintf } from './sprintf'; export { createI18n } from './create-i18n'; -export { i18n, setLocaleData, __, _x, _n, _nx, isRTL } from './default-i18n'; +export { setLocaleData, __, _x, _n, _nx, isRTL } from './default-i18n'; From d129a67cd1c195ad83b95714f3d8d89967f497a9 Mon Sep 17 00:00:00 2001 From: James Newell Date: Wed, 4 Mar 2020 14:21:26 +1100 Subject: [PATCH 10/18] address feedback --- packages/i18n/CHANGELOG.md | 2 +- packages/i18n/src/create-i18n.js | 23 ++++++++++++----------- packages/i18n/src/test/sprintf.js | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md index 3c31a57b1cd1d..db6558df5f155 100644 --- a/packages/i18n/CHANGELOG.md +++ b/packages/i18n/CHANGELOG.md @@ -4,7 +4,7 @@ - Add `isRTL` function ([#20298](https://github.com/WordPress/gutenberg/pull/20298)) - Include TypeScript type declarations ([#18942](https://github.com/WordPress/gutenberg/pull/18942)) -- Add `I18n` class to allow creation of multiple i18n instances. (#19210) +- Add `createI18n` method to allow creation of multiple i18n instances ([#20318](https://github.com/WordPress/gutenberg/pull/20318)) ## 3.1.0 (2018-11-15) diff --git a/packages/i18n/src/create-i18n.js b/packages/i18n/src/create-i18n.js index 02795f15ead4f..4076951776c76 100644 --- a/packages/i18n/src/create-i18n.js +++ b/packages/i18n/src/create-i18n.js @@ -21,24 +21,25 @@ const DEFAULT_LOCALE_DATA = { /** * An i18n instance + * * @typedef {Object} I18n * @property {Function} setLocaleData Merges locale data into the Tannin instance by domain. Accepts data in a - * Jed-formatted JSON object shape. - * @property {Function} __ Retrieve the translation of text. - * @property {Function} _x Retrieve translated string with gettext context. - * @property {Function} _n Translates and retrieves the singular or plural form based on the supplied - * number. - * @property {Function} _nx Translates and retrieves the singular or plural form based on the supplied - * number, with gettext context. - * @property {Function} isRTL Check if current locale is RTL. + * Jed-formatted JSON object shape. + * @property {Function} __ Retrieve the translation of text. + * @property {Function} _x Retrieve translated string with gettext context. + * @property {Function} _n Translates and retrieves the singular or plural form based on the supplied + * number. + * @property {Function} _nx Translates and retrieves the singular or plural form based on the supplied + * number, with gettext context. + * @property {Function} isRTL Check if current locale is RTL. */ /** * Create an i18n instance * - * @param {LocaleData} [initialData] Locale data configuration. - * @param {string} [initialDomain] Domain for which configuration applies. - * @return {I18n} I18n instance + * @param {LocaleData} [initialData] Locale data configuration. + * @param {string} [initialDomain] Domain for which configuration applies. + * @return {I18n} I18n instance */ export const createI18n = ( initialData, initialDomain ) => { /** diff --git a/packages/i18n/src/test/sprintf.js b/packages/i18n/src/test/sprintf.js index 3e9088e21132a..035d3b3a4b3d4 100644 --- a/packages/i18n/src/test/sprintf.js +++ b/packages/i18n/src/test/sprintf.js @@ -1,5 +1,5 @@ -// Mock memoization as identity function. Inline since Jest errors on out-of- -// scope references in a mock callback. +// Mock memoization as identity function. Inline since Jest errors on +// out-of-scope references in a mock callback. jest.mock( 'memize', () => ( fn ) => fn ); /** From 13eb39a39b307d74cf1b8bcdb80c45faf815e264 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 26 Mar 2020 20:02:02 +0100 Subject: [PATCH 11/18] Skip setlocaledata if not provided --- packages/i18n/src/create-i18n.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/i18n/src/create-i18n.js b/packages/i18n/src/create-i18n.js index 4076951776c76..dcbbbf34c7d0b 100644 --- a/packages/i18n/src/create-i18n.js +++ b/packages/i18n/src/create-i18n.js @@ -182,7 +182,9 @@ export const createI18n = ( initialData, initialDomain ) => { return 'rtl' === _x( 'ltr', 'text direction' ); }; - setLocaleData( initialData, initialDomain ); + if ( initialData ) { + setLocaleData( initialData, initialDomain ); + } return { setLocaleData, From 72b58581b19ecacb943f0a675aef19b774161ad2 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 26 Mar 2020 20:21:25 +0100 Subject: [PATCH 12/18] Require `data` argument in `setLocalData` --- packages/i18n/src/create-i18n.js | 2 +- packages/i18n/src/default-i18n.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/i18n/src/create-i18n.js b/packages/i18n/src/create-i18n.js index dcbbbf34c7d0b..19dc042e6efff 100644 --- a/packages/i18n/src/create-i18n.js +++ b/packages/i18n/src/create-i18n.js @@ -55,7 +55,7 @@ export const createI18n = ( initialData, initialDomain ) => { * * @see http://messageformat.github.io/Jed/ * - * @param {LocaleData} [data] Locale data configuration. + * @param {LocaleData} data Locale data configuration. * @param {string} [domain] Domain for which configuration applies. */ const setLocaleData = ( data, domain = 'default' ) => { diff --git a/packages/i18n/src/default-i18n.js b/packages/i18n/src/default-i18n.js index 760ad5240342a..8fabfbbb2c489 100644 --- a/packages/i18n/src/default-i18n.js +++ b/packages/i18n/src/default-i18n.js @@ -20,7 +20,7 @@ const i18n = createI18n(); * * @see http://messageformat.github.io/Jed/ * - * @param {LocaleData} [data] Locale data configuration. + * @param {LocaleData} data Locale data configuration. * @param {string} [domain] Domain for which configuration applies. */ export const setLocaleData = i18n.setLocaleData.bind( i18n ); From 7b7654de4a693c14defafdabf53e1cbef11a5bb7 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 26 Mar 2020 20:24:09 +0100 Subject: [PATCH 13/18] Update docs --- packages/i18n/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index f57ce4124c191..7f72b71850924 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -64,7 +64,7 @@ _Related_ _Parameters_ -- _data_ `[LocaleData]`: Locale data configuration. +- _data_ `LocaleData`: Locale data configuration. - _domain_ `[string]`: Domain for which configuration applies. # **sprintf** From 1b6d2de3481be69ca8fcecdbebf720c8c075adf7 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 26 Mar 2020 21:40:23 +0100 Subject: [PATCH 14/18] Update PR number in changelog --- packages/i18n/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md index db6558df5f155..cf038c0a4fef9 100644 --- a/packages/i18n/CHANGELOG.md +++ b/packages/i18n/CHANGELOG.md @@ -4,7 +4,7 @@ - Add `isRTL` function ([#20298](https://github.com/WordPress/gutenberg/pull/20298)) - Include TypeScript type declarations ([#18942](https://github.com/WordPress/gutenberg/pull/18942)) -- Add `createI18n` method to allow creation of multiple i18n instances ([#20318](https://github.com/WordPress/gutenberg/pull/20318)) +- Add `createI18n` method to allow creation of multiple i18n instances ([#21182](https://github.com/WordPress/gutenberg/pull/21182)) ## 3.1.0 (2018-11-15) From c30ca0e1d51ef01de8816bcbd3f6d16ebaca0b19 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 26 Mar 2020 21:57:00 +0100 Subject: [PATCH 15/18] Update LocaleData type --- packages/i18n/src/default-i18n.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18n/src/default-i18n.js b/packages/i18n/src/default-i18n.js index 8fabfbbb2c489..0323144e05372 100644 --- a/packages/i18n/src/default-i18n.js +++ b/packages/i18n/src/default-i18n.js @@ -11,7 +11,7 @@ const i18n = createI18n(); */ /** - * @typedef {{[key: string]: any}} LocaleData + * @typedef {import('./create-i18n').LocaleData} LocaleData */ /** From aff0aefdb2bca3c70722f5c2b3fa532802ef0a70 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 27 Mar 2020 09:37:32 +0100 Subject: [PATCH 16/18] Make setLocaleData data optional --- packages/i18n/README.md | 2 +- packages/i18n/src/create-i18n.js | 2 +- packages/i18n/src/default-i18n.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/i18n/README.md b/packages/i18n/README.md index 7f72b71850924..f57ce4124c191 100644 --- a/packages/i18n/README.md +++ b/packages/i18n/README.md @@ -64,7 +64,7 @@ _Related_ _Parameters_ -- _data_ `LocaleData`: Locale data configuration. +- _data_ `[LocaleData]`: Locale data configuration. - _domain_ `[string]`: Domain for which configuration applies. # **sprintf** diff --git a/packages/i18n/src/create-i18n.js b/packages/i18n/src/create-i18n.js index 19dc042e6efff..dcbbbf34c7d0b 100644 --- a/packages/i18n/src/create-i18n.js +++ b/packages/i18n/src/create-i18n.js @@ -55,7 +55,7 @@ export const createI18n = ( initialData, initialDomain ) => { * * @see http://messageformat.github.io/Jed/ * - * @param {LocaleData} data Locale data configuration. + * @param {LocaleData} [data] Locale data configuration. * @param {string} [domain] Domain for which configuration applies. */ const setLocaleData = ( data, domain = 'default' ) => { diff --git a/packages/i18n/src/default-i18n.js b/packages/i18n/src/default-i18n.js index 0323144e05372..c5fdc8c06548b 100644 --- a/packages/i18n/src/default-i18n.js +++ b/packages/i18n/src/default-i18n.js @@ -20,7 +20,7 @@ const i18n = createI18n(); * * @see http://messageformat.github.io/Jed/ * - * @param {LocaleData} data Locale data configuration. + * @param {LocaleData} [data] Locale data configuration. * @param {string} [domain] Domain for which configuration applies. */ export const setLocaleData = i18n.setLocaleData.bind( i18n ); From 4b7b10715bc227e3c99db137434de9adfb17be39 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 27 Mar 2020 23:19:48 +0100 Subject: [PATCH 17/18] Include change from #18942 --- packages/i18n/src/create-i18n.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/i18n/src/create-i18n.js b/packages/i18n/src/create-i18n.js index dcbbbf34c7d0b..0b0b4cd1826d1 100644 --- a/packages/i18n/src/create-i18n.js +++ b/packages/i18n/src/create-i18n.js @@ -15,7 +15,10 @@ import Tannin from 'tannin'; */ const DEFAULT_LOCALE_DATA = { '': { - plural_forms: ( n ) => ( n === 1 ? 0 : 1 ), + /** @param {number} n */ + plural_forms( n ) { + return n === 1 ? 0 : 1; + }, }, }; From 87bb2f565994e29056b9ecb64a5431f5127f5fbb Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 27 Mar 2020 23:27:45 +0100 Subject: [PATCH 18/18] Export * from create-i18n to include types --- packages/i18n/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18n/src/index.js b/packages/i18n/src/index.js index 9e83b23e77971..e5e04852a3c22 100644 --- a/packages/i18n/src/index.js +++ b/packages/i18n/src/index.js @@ -1,3 +1,3 @@ export { sprintf } from './sprintf'; -export { createI18n } from './create-i18n'; +export * from './create-i18n'; export { setLocaleData, __, _x, _n, _nx, isRTL } from './default-i18n';