Add coexistence checks to all enqueue methods to prevent loading both React and Grid.js assets simultaneously. Changes: - ReactAdmin.php: Only enqueue React assets when ?react=1 - Init.php: Skip Grid.js when React active on admin pages - Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0 - Customer.php, Product.php, License.php: Add coexistence checks Now the toggle between Classic and React versions works correctly. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
408 lines
15 KiB
JavaScript
408 lines
15 KiB
JavaScript
/**
|
|
* External dependencies
|
|
*/
|
|
import Tannin from 'tannin';
|
|
|
|
/**
|
|
* @typedef {Record<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 = {
|
|
'': {
|
|
/** @param {number} n */
|
|
plural_forms(n) {
|
|
return n === 1 ? 0 : 1;
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Regular expression that matches i18n hooks like `i18n.gettext`, `i18n.ngettext`,
|
|
* `i18n.gettext_domain` or `i18n.ngettext_with_context` or `i18n.has_translation`.
|
|
*/
|
|
const I18N_HOOK_REGEXP = /^i18n\.(n?gettext|has_translation)(_|$)/;
|
|
|
|
/**
|
|
* @typedef {(domain?: string) => LocaleData} GetLocaleData
|
|
*
|
|
* Returns locale data by domain in a
|
|
* Jed-formatted JSON object shape.
|
|
*
|
|
* @see http://messageformat.github.io/Jed/
|
|
*/
|
|
/**
|
|
* @typedef {(data?: LocaleData, domain?: string) => void} SetLocaleData
|
|
*
|
|
* Merges locale data into the Tannin instance by domain. Note that this
|
|
* function will overwrite the domain configuration. Accepts data in a
|
|
* Jed-formatted JSON object shape.
|
|
*
|
|
* @see http://messageformat.github.io/Jed/
|
|
*/
|
|
/**
|
|
* @typedef {(data?: LocaleData, domain?: string) => void} AddLocaleData
|
|
*
|
|
* Merges locale data into the Tannin instance by domain. Note that this
|
|
* function will also merge the domain configuration. Accepts data in a
|
|
* Jed-formatted JSON object shape.
|
|
*
|
|
* @see http://messageformat.github.io/Jed/
|
|
*/
|
|
/**
|
|
* @typedef {(data?: LocaleData, domain?: string) => void} ResetLocaleData
|
|
*
|
|
* Resets all current Tannin instance locale data and sets the specified
|
|
* locale data for the domain. Accepts data in a Jed-formatted JSON object shape.
|
|
*
|
|
* @see http://messageformat.github.io/Jed/
|
|
*/
|
|
/** @typedef {() => void} SubscribeCallback */
|
|
/** @typedef {() => void} UnsubscribeCallback */
|
|
/**
|
|
* @typedef {(callback: SubscribeCallback) => UnsubscribeCallback} Subscribe
|
|
*
|
|
* Subscribes to changes of locale data
|
|
*/
|
|
/**
|
|
* @typedef {(domain?: string) => string} GetFilterDomain
|
|
* Retrieve the domain to use when calling domain-specific filters.
|
|
*/
|
|
/**
|
|
* @typedef {(text: string, domain?: string) => string} __
|
|
*
|
|
* Retrieve the translation of text.
|
|
*
|
|
* @see https://developer.wordpress.org/reference/functions/__/
|
|
*/
|
|
/**
|
|
* @typedef {(text: string, context: string, domain?: string) => string} _x
|
|
*
|
|
* Retrieve translated string with gettext context.
|
|
*
|
|
* @see https://developer.wordpress.org/reference/functions/_x/
|
|
*/
|
|
/**
|
|
* @typedef {(single: string, plural: string, number: number, domain?: string) => string} _n
|
|
*
|
|
* Translates and retrieves the singular or plural form based on the supplied
|
|
* number.
|
|
*
|
|
* @see https://developer.wordpress.org/reference/functions/_n/
|
|
*/
|
|
/**
|
|
* @typedef {(single: string, plural: string, number: number, context: string, domain?: string) => string} _nx
|
|
*
|
|
* Translates and retrieves the singular or plural form based on the supplied
|
|
* number, with gettext context.
|
|
*
|
|
* @see https://developer.wordpress.org/reference/functions/_nx/
|
|
*/
|
|
/**
|
|
* @typedef {() => boolean} 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`).
|
|
*/
|
|
/**
|
|
* @typedef {(single: string, context?: string, domain?: string) => boolean} HasTranslation
|
|
*
|
|
* Check if there is a translation for a given string in singular form.
|
|
*/
|
|
/** @typedef {import('@wordpress/hooks').Hooks} Hooks */
|
|
|
|
/**
|
|
* An i18n instance
|
|
*
|
|
* @typedef I18n
|
|
* @property {GetLocaleData} getLocaleData Returns locale data by domain in a Jed-formatted JSON object shape.
|
|
* @property {SetLocaleData} setLocaleData Merges locale data into the Tannin instance by domain. Note that this
|
|
* function will overwrite the domain configuration. Accepts data in a
|
|
* Jed-formatted JSON object shape.
|
|
* @property {AddLocaleData} addLocaleData Merges locale data into the Tannin instance by domain. Note that this
|
|
* function will also merge the domain configuration. Accepts data in a
|
|
* Jed-formatted JSON object shape.
|
|
* @property {ResetLocaleData} resetLocaleData Resets all current Tannin instance locale data and sets the specified
|
|
* locale data for the domain. Accepts data in a Jed-formatted JSON object shape.
|
|
* @property {Subscribe} subscribe Subscribes to changes of Tannin locale data.
|
|
* @property {__} __ Retrieve the translation of text.
|
|
* @property {_x} _x Retrieve translated string with gettext context.
|
|
* @property {_n} _n Translates and retrieves the singular or plural form based on the supplied
|
|
* number.
|
|
* @property {_nx} _nx Translates and retrieves the singular or plural form based on the supplied
|
|
* number, with gettext context.
|
|
* @property {IsRtl} isRTL Check if current locale is RTL.
|
|
* @property {HasTranslation} hasTranslation Check if there is a translation for a given string.
|
|
*/
|
|
|
|
/**
|
|
* Create an i18n instance
|
|
*
|
|
* @param {LocaleData} [initialData] Locale data configuration.
|
|
* @param {string} [initialDomain] Domain for which configuration applies.
|
|
* @param {Hooks} [hooks] Hooks implementation.
|
|
*
|
|
* @return {I18n} I18n instance.
|
|
*/
|
|
export const createI18n = (initialData, initialDomain, hooks) => {
|
|
/**
|
|
* The underlying instance of Tannin to which exported functions interface.
|
|
*
|
|
* @type {Tannin}
|
|
*/
|
|
const tannin = new Tannin({});
|
|
const listeners = new Set();
|
|
const notifyListeners = () => {
|
|
listeners.forEach(listener => listener());
|
|
};
|
|
|
|
/**
|
|
* Subscribe to changes of locale data.
|
|
*
|
|
* @param {SubscribeCallback} callback Subscription callback.
|
|
* @return {UnsubscribeCallback} Unsubscribe callback.
|
|
*/
|
|
const subscribe = callback => {
|
|
listeners.add(callback);
|
|
return () => listeners.delete(callback);
|
|
};
|
|
|
|
/** @type {GetLocaleData} */
|
|
const getLocaleData = (domain = 'default') => tannin.data[domain];
|
|
|
|
/**
|
|
* @param {LocaleData} [data]
|
|
* @param {string} [domain]
|
|
*/
|
|
const doSetLocaleData = (data, domain = 'default') => {
|
|
tannin.data[domain] = {
|
|
...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]?.['']
|
|
};
|
|
|
|
// Clean up cached plural forms functions cache as it might be updated.
|
|
delete tannin.pluralForms[domain];
|
|
};
|
|
|
|
/** @type {SetLocaleData} */
|
|
const setLocaleData = (data, domain) => {
|
|
doSetLocaleData(data, domain);
|
|
notifyListeners();
|
|
};
|
|
|
|
/** @type {AddLocaleData} */
|
|
const addLocaleData = (data, domain = 'default') => {
|
|
tannin.data[domain] = {
|
|
...tannin.data[domain],
|
|
...data,
|
|
// Populate default domain configuration (supported locale date which omits
|
|
// a plural forms expression).
|
|
'': {
|
|
...DEFAULT_LOCALE_DATA[''],
|
|
...tannin.data[domain]?.[''],
|
|
...data?.['']
|
|
}
|
|
};
|
|
|
|
// Clean up cached plural forms functions cache as it might be updated.
|
|
delete tannin.pluralForms[domain];
|
|
notifyListeners();
|
|
};
|
|
|
|
/** @type {ResetLocaleData} */
|
|
const resetLocaleData = (data, domain) => {
|
|
// Reset all current Tannin locale data.
|
|
tannin.data = {};
|
|
|
|
// Reset cached plural forms functions cache.
|
|
tannin.pluralForms = {};
|
|
setLocaleData(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]) {
|
|
// Use `doSetLocaleData` to set silently, without notifying listeners.
|
|
doSetLocaleData(undefined, domain);
|
|
}
|
|
return tannin.dcnpgettext(domain, context, single, plural, number);
|
|
};
|
|
|
|
/** @type {GetFilterDomain} */
|
|
const getFilterDomain = (domain = 'default') => domain;
|
|
|
|
/** @type {__} */
|
|
const __ = (text, domain) => {
|
|
let translation = dcnpgettext(domain, undefined, text);
|
|
if (!hooks) {
|
|
return translation;
|
|
}
|
|
|
|
/**
|
|
* Filters text with its translation.
|
|
*
|
|
* @param {string} translation Translated text.
|
|
* @param {string} text Text to translate.
|
|
* @param {string} domain Text domain. Unique identifier for retrieving translated strings.
|
|
*/
|
|
translation = /** @type {string} */
|
|
/** @type {*} */hooks.applyFilters('i18n.gettext', translation, text, domain);
|
|
return /** @type {string} */(
|
|
/** @type {*} */hooks.applyFilters('i18n.gettext_' + getFilterDomain(domain), translation, text, domain)
|
|
);
|
|
};
|
|
|
|
/** @type {_x} */
|
|
const _x = (text, context, domain) => {
|
|
let translation = dcnpgettext(domain, context, text);
|
|
if (!hooks) {
|
|
return translation;
|
|
}
|
|
|
|
/**
|
|
* Filters text with its translation based on context information.
|
|
*
|
|
* @param {string} translation Translated text.
|
|
* @param {string} text Text to translate.
|
|
* @param {string} context Context information for the translators.
|
|
* @param {string} domain Text domain. Unique identifier for retrieving translated strings.
|
|
*/
|
|
translation = /** @type {string} */
|
|
/** @type {*} */hooks.applyFilters('i18n.gettext_with_context', translation, text, context, domain);
|
|
return /** @type {string} */(
|
|
/** @type {*} */hooks.applyFilters('i18n.gettext_with_context_' + getFilterDomain(domain), translation, text, context, domain)
|
|
);
|
|
};
|
|
|
|
/** @type {_n} */
|
|
const _n = (single, plural, number, domain) => {
|
|
let translation = dcnpgettext(domain, undefined, single, plural, number);
|
|
if (!hooks) {
|
|
return translation;
|
|
}
|
|
|
|
/**
|
|
* Filters the singular or plural form of a string.
|
|
*
|
|
* @param {string} translation Translated text.
|
|
* @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 {string} number The number to compare against to use either the singular or plural form.
|
|
* @param {string} domain Text domain. Unique identifier for retrieving translated strings.
|
|
*/
|
|
translation = /** @type {string} */
|
|
/** @type {*} */hooks.applyFilters('i18n.ngettext', translation, single, plural, number, domain);
|
|
return /** @type {string} */(
|
|
/** @type {*} */hooks.applyFilters('i18n.ngettext_' + getFilterDomain(domain), translation, single, plural, number, domain)
|
|
);
|
|
};
|
|
|
|
/** @type {_nx} */
|
|
const _nx = (single, plural, number, context, domain) => {
|
|
let translation = dcnpgettext(domain, context, single, plural, number);
|
|
if (!hooks) {
|
|
return translation;
|
|
}
|
|
|
|
/**
|
|
* Filters the singular or plural form of a string with gettext context.
|
|
*
|
|
* @param {string} translation Translated text.
|
|
* @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 {string} 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 Text domain. Unique identifier for retrieving translated strings.
|
|
*/
|
|
translation = /** @type {string} */
|
|
/** @type {*} */hooks.applyFilters('i18n.ngettext_with_context', translation, single, plural, number, context, domain);
|
|
return /** @type {string} */(
|
|
/** @type {*} */hooks.applyFilters('i18n.ngettext_with_context_' + getFilterDomain(domain), translation, single, plural, number, context, domain)
|
|
);
|
|
};
|
|
|
|
/** @type {IsRtl} */
|
|
const isRTL = () => {
|
|
return 'rtl' === _x('ltr', 'text direction');
|
|
};
|
|
|
|
/** @type {HasTranslation} */
|
|
const hasTranslation = (single, context, domain) => {
|
|
const key = context ? context + '\u0004' + single : single;
|
|
let result = !!tannin.data?.[domain !== null && domain !== void 0 ? domain : 'default']?.[key];
|
|
if (hooks) {
|
|
/**
|
|
* Filters the presence of a translation in the locale data.
|
|
*
|
|
* @param {boolean} hasTranslation Whether the translation is present or not..
|
|
* @param {string} single The singular form of the translated text (used as key in locale data)
|
|
* @param {string} context Context information for the translators.
|
|
* @param {string} domain Text domain. Unique identifier for retrieving translated strings.
|
|
*/
|
|
result = /** @type { boolean } */
|
|
/** @type {*} */hooks.applyFilters('i18n.has_translation', result, single, context, domain);
|
|
result = /** @type { boolean } */
|
|
/** @type {*} */hooks.applyFilters('i18n.has_translation_' + getFilterDomain(domain), result, single, context, domain);
|
|
}
|
|
return result;
|
|
};
|
|
if (initialData) {
|
|
setLocaleData(initialData, initialDomain);
|
|
}
|
|
if (hooks) {
|
|
/**
|
|
* @param {string} hookName
|
|
*/
|
|
const onHookAddedOrRemoved = hookName => {
|
|
if (I18N_HOOK_REGEXP.test(hookName)) {
|
|
notifyListeners();
|
|
}
|
|
};
|
|
hooks.addAction('hookAdded', 'core/i18n', onHookAddedOrRemoved);
|
|
hooks.addAction('hookRemoved', 'core/i18n', onHookAddedOrRemoved);
|
|
}
|
|
return {
|
|
getLocaleData,
|
|
setLocaleData,
|
|
addLocaleData,
|
|
resetLocaleData,
|
|
subscribe,
|
|
__,
|
|
_x,
|
|
_n,
|
|
_nx,
|
|
isRTL,
|
|
hasTranslation
|
|
};
|
|
};
|
|
//# sourceMappingURL=create-i18n.js.map
|