-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactored @wordpress/i18n to be an instantiable #20318
Closed
jameslnewell
wants to merge
10
commits into
WordPress:master
from
jameslnewell:feat/instantiable-i18n
Closed
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
dd1869d
refactored i18n to be an instantiable class
3059db6
use explicit exports
324f4b3
update docs
3b29ce2
remove comment
663f14c
duplicate comments
80b30bc
switch from I18n class to createI18n fn
fda6948
don't export default instance
a760a64
refactor tests
f2e9742
improve docs
8ecee47
address feedback
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import Tannin from 'tannin'; | ||
|
||
/** | ||
* @typedef {{[key: string]: any}} 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 ), | ||
}, | ||
}; | ||
|
||
/** | ||
* 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 ) => { | ||
/** | ||
* The underlying instance of Tannin to which exported functions interface. | ||
* | ||
* @type {Tannin} | ||
*/ | ||
const tannin = 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. | ||
*/ | ||
const setLocaleData = ( data, domain = 'default' ) => { | ||
tannin.data[ domain ] = { | ||
...DEFAULT_LOCALE_DATA, | ||
...tannin.data[ domain ], | ||
...data, | ||
}; | ||
|
||
// Populate default domain configuration (supported locale date which omits | ||
// a plural forms expression). | ||
tannin.data[ domain ][ '' ] = { | ||
...DEFAULT_LOCALE_DATA[ '' ], | ||
...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. | ||
*/ | ||
const dcnpgettext = ( | ||
domain = 'default', | ||
context, | ||
single, | ||
plural, | ||
number | ||
) => { | ||
if ( ! tannin.data[ domain ] ) { | ||
setLocaleData( undefined, domain ); | ||
} | ||
|
||
return 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. | ||
*/ | ||
const __ = ( 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. | ||
*/ | ||
const _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. | ||
*/ | ||
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 | ||
* 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. | ||
*/ | ||
const _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. | ||
*/ | ||
const isRTL = () => { | ||
return 'rtl' === _x( 'ltr', 'text direction' ); | ||
}; | ||
|
||
setLocaleData( initialData, initialDomain ); | ||
|
||
return { | ||
setLocaleData, | ||
__, | ||
_x, | ||
_n, | ||
_nx, | ||
isRTL, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { createI18n } from './create-i18n'; | ||
|
||
const i18n = createI18n(); | ||
|
||
/* | ||
* 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 ); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we always be calling this, even if
initialData
and/orinitialDomain
are possiblyundefined
(per the documented types)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to this, are there specific use-cases in mind for setting the initial data? It seems like something which could be useful, but just wondering if we might be getting ahead of ourselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I requested this. It seems helpful to be able to populate with initial data, but there's not much lost if we require package consumers to first create then set the data. I don't have strong feelings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's still odd to me that we call
setLocaleData
if there is nothing to set. It works, but only by a combination of the fact thatdata
is an optional argument ofsetLocaleData
(pretty counter-intuitive), and that object spread is tolerant of undefined (i.e.{ ...undefined }
), when instead we could just add anif
here, and makesetLocaleData
'sdata
argument non-optional.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jameslnewell Do you mind addressing this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I addressed this in #21182. I believe that's the remaining feedback that needed to be addressed.