fix: prevent asset conflicts between React and Grid.js versions

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>
This commit is contained in:
dwindown
2026-04-18 17:02:14 +07:00
parent bd9cdac02e
commit e8fbfb14c1
74973 changed files with 6658406 additions and 71 deletions

506
node_modules/@wordpress/i18n/src/create-i18n.js generated vendored Normal file
View File

@@ -0,0 +1,506 @@
/**
* 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 ?? '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,
};
};

147
node_modules/@wordpress/i18n/src/default-i18n.js generated vendored Normal file
View File

@@ -0,0 +1,147 @@
/**
* Internal dependencies
*/
import { createI18n } from './create-i18n';
/**
* WordPress dependencies
*/
import { defaultHooks } from '@wordpress/hooks';
const i18n = createI18n( undefined, undefined, defaultHooks );
/**
* Default, singleton instance of `I18n`.
*/
export default i18n;
/*
* Comments in this file are duplicated from ./i18n due to
* https://github.com/WordPress/gutenberg/pull/20318#issuecomment-590837722
*/
/**
* @typedef {import('./create-i18n').LocaleData} LocaleData
* @typedef {import('./create-i18n').SubscribeCallback} SubscribeCallback
* @typedef {import('./create-i18n').UnsubscribeCallback} UnsubscribeCallback
*/
/**
* Returns locale data by domain in a Jed-formatted JSON object shape.
*
* @see http://messageformat.github.io/Jed/
*
* @param {string} [domain] Domain for which to get the data.
* @return {LocaleData} Locale data.
*/
export const getLocaleData = i18n.getLocaleData.bind( i18n );
/**
* 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 );
/**
* 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/
*
* @param {LocaleData} [data] Locale data configuration.
* @param {string} [domain] Domain for which configuration applies.
*/
export const resetLocaleData = i18n.resetLocaleData.bind( i18n );
/**
* Subscribes to changes of locale data
*
* @param {SubscribeCallback} callback Subscription callback
* @return {UnsubscribeCallback} Unsubscribe callback
*/
export const subscribe = i18n.subscribe.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 );
/**
* Check if there is a translation for a given string (in singular form).
*
* @param {string} single Singular form of the string to look up.
* @param {string} [context] Context information for the translators.
* @param {string} [domain] Domain to retrieve the translated text.
* @return {boolean} Whether the translation exists or not.
*/
export const hasTranslation = i18n.hasTranslation.bind( i18n );

15
node_modules/@wordpress/i18n/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
export { sprintf } from './sprintf';
export * from './create-i18n';
export {
default as defaultI18n,
setLocaleData,
resetLocaleData,
getLocaleData,
subscribe,
__,
_x,
_n,
_nx,
isRTL,
hasTranslation,
} from './default-i18n';

36
node_modules/@wordpress/i18n/src/sprintf.js generated vendored Normal file
View File

@@ -0,0 +1,36 @@
/**
* 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 {...*} args Arguments to apply to the format.
*
* @see https://www.npmjs.com/package/sprintf-js
*
* @return {string} The formatted string.
*/
export function sprintf( format, ...args ) {
try {
return sprintfjs.sprintf( format, ...args );
} catch ( error ) {
if ( error instanceof Error ) {
logErrorOnce( 'sprintf error: \n\n' + error.toString() );
}
return format;
}
}

549
node_modules/@wordpress/i18n/src/test/create-i18n.js generated vendored Normal file
View File

@@ -0,0 +1,549 @@
/* eslint-disable @wordpress/i18n-text-domain, @wordpress/i18n-translator-comments */
/**
* WordPress dependencies
*/
import { createHooks } from '@wordpress/hooks';
/**
* Internal dependencies
*/
import { createI18n } from '..';
const strayaLocale = {
hello: [ 'gday' ],
};
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' );
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();
straya.setLocaleData( strayaLocale );
french.setLocaleData( frenchLocale );
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', () => {
const createTestLocaleWithAdditionalData = () => {
const locale = createI18n( localeData, 'test_domain' );
locale.setLocaleData( additionalLocaleData, 'test_domain' );
return locale;
};
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' );
} );
it( 'overwrites domain configuration', () => {
const locale = createTestLocaleWithAdditionalData();
const domain = 'test_domain';
const domainConfiguration = {
additionalData: 'This is setLocaleData',
};
locale.setLocaleData(
{
'': domainConfiguration,
},
domain
);
expect(
locale.getLocaleData( domain )[ '' ].domain
).toBeUndefined();
expect( locale.getLocaleData( domain )[ '' ].lang ).toBeUndefined();
expect( locale.getLocaleData( domain )[ '' ].additionalData ).toBe(
domainConfiguration.additionalData
);
} );
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' );
} );
} );
} );
describe( 'addLocaleData', () => {
const createTestLocaleWithAdditionalData = () => {
const locale = createI18n( localeData, 'test_domain' );
locale.addLocaleData( additionalLocaleData, 'test_domain' );
return locale;
};
it( 'supports omitted plural forms expression', () => {
const locale = createTestLocaleWithAdditionalData();
locale.addLocaleData(
{
'': {
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' );
} );
it( 'merges domain configuration', () => {
const locale = createTestLocaleWithAdditionalData();
const domain = 'test_domain';
const domainConfiguration = {
additionalData: 'This is addLocaleData',
};
locale.addLocaleData(
{
'': domainConfiguration,
},
domain
);
expect( locale.getLocaleData( domain )[ '' ].domain ).toBe(
domain
);
expect( locale.getLocaleData( domain )[ '' ].lang ).toBe( 'fr' );
expect( locale.getLocaleData( domain )[ '' ].additionalData ).toBe(
domainConfiguration.additionalData
);
} );
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' );
} );
} );
} );
describe( 'resetLocaleData', () => {
it( 'reset the locale data', () => {
const locale = createTestLocale();
expect( locale.__( 'hello', 'test_domain' ) ).toBe( 'bonjour' );
locale.resetLocaleData();
expect( locale.__( 'hello', 'test_domain' ) ).toBe( 'hello' );
} );
it( 'reset the current locale data and set new locale data for the specified domain', () => {
const locale = createTestLocale();
expect( locale.__( 'hello', 'test_domain' ) ).toBe( 'bonjour' );
locale.resetLocaleData( additionalLocaleData );
expect( locale.__( 'cheeseburger' ) ).toBe(
'hamburger au fromage'
);
locale.resetLocaleData( additionalLocaleData, 'test_domain2' );
expect( locale.__( '%d cat', 'test_domain2' ) ).toBe( '%d chat' );
} );
it( 'reset the plural forms function cache', () => {
const locale = createI18n( {}, 'test_domain' );
// Call `_n` to get the plural forms function cached.
locale._n( 'singular', 'plural', 1, 'test_domain' );
// Reset the locale data and provide custom plural forms function.
locale.resetLocaleData(
{
'': {
domain: 'test_domain',
lang: 'aa',
plural_forms:
'nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;',
},
singular: [
'translated',
'translated_plural_1',
'translated_plural_2',
],
},
'test_domain'
);
expect( locale._n( 'singular', 'plural', 1, 'test_domain' ) ).toBe(
'translated'
);
expect( locale._n( 'singular', 'plural', 2, 'test_domain' ) ).toBe(
'translated_plural_1'
);
expect( locale._n( 'singular', 'plural', 3, 'test_domain' ) ).toBe(
'translated_plural_2'
);
// Reset the locale data and fallback to the defualt plural forms function.
locale.resetLocaleData(
{
singular: [
'translated',
'translated_plural_1',
'translated_plural_2',
],
},
'test_domain'
);
expect( locale._n( 'singular', 'plural', 1, 'test_domain' ) ).toBe(
'translated'
);
expect( locale._n( 'singular', 'plural', 2, 'test_domain' ) ).toBe(
'translated_plural_1'
);
expect( locale._n( 'singular', 'plural', 3, 'test_domain' ) ).toBe(
'translated_plural_1'
);
} );
} );
} );
describe( 'i18n filters', () => {
function createHooksWithI18nFilters() {
const hooks = createHooks();
hooks.addFilter(
'i18n.gettext',
'test',
( translation ) => translation + '/i18n.gettext'
);
hooks.addFilter(
'i18n.gettext_default',
'test',
( translation ) => translation + '/i18n.gettext_default'
);
hooks.addFilter(
'i18n.gettext_domain',
'test',
( translation ) => translation + '/i18n.gettext_domain'
);
hooks.addFilter(
'i18n.ngettext',
'test',
( translation ) => translation + '/i18n.ngettext'
);
hooks.addFilter(
'i18n.ngettext_default',
'test',
( translation ) => translation + '/i18n.ngettext_default'
);
hooks.addFilter(
'i18n.ngettext_domain',
'test',
( translation ) => translation + '/i18n.ngettext_domain'
);
hooks.addFilter(
'i18n.gettext_with_context',
'test',
( translation, text, context ) =>
translation + `/i18n.gettext_with_${ context }`
);
hooks.addFilter(
'i18n.gettext_with_context_default',
'test',
( translation, text, context ) =>
translation + `/i18n.gettext_with_${ context }_default`
);
hooks.addFilter(
'i18n.gettext_with_context_domain',
'test',
( translation, text, context ) =>
translation + `/i18n.gettext_with_${ context }_domain`
);
hooks.addFilter(
'i18n.ngettext_with_context',
'test',
( translation, single, plural, number, context ) =>
translation + `/i18n.ngettext_with_${ context }`
);
hooks.addFilter(
'i18n.ngettext_with_context_default',
'test',
( translation, single, plural, number, context ) =>
translation + `/i18n.ngettext_with_${ context }_default`
);
hooks.addFilter(
'i18n.ngettext_with_context_domain',
'test',
( translation, single, plural, number, context ) =>
translation + `/i18n.ngettext_with_${ context }_domain`
);
hooks.addFilter(
'i18n.has_translation',
'test',
( hasTranslation, single, context, domain ) => {
if (
single === 'Always' &&
! context &&
( domain ?? 'default' ) === 'default'
) {
return true;
}
return hasTranslation;
}
);
return hooks;
}
test( '__() calls filters', () => {
const hooks = createHooksWithI18nFilters();
const i18n = createI18n( undefined, undefined, hooks );
expect( i18n.__( 'hello' ) ).toEqual(
'hello/i18n.gettext/i18n.gettext_default'
);
expect( i18n.__( 'hello', 'domain' ) ).toEqual(
'hello/i18n.gettext/i18n.gettext_domain'
);
} );
test( '_x() calls filters', () => {
const hooks = createHooksWithI18nFilters();
const i18n = createI18n( undefined, undefined, hooks );
expect( i18n._x( 'hello', 'ctx' ) ).toEqual(
'hello/i18n.gettext_with_ctx/i18n.gettext_with_ctx_default'
);
expect( i18n._x( 'hello', 'ctx', 'domain' ) ).toEqual(
'hello/i18n.gettext_with_ctx/i18n.gettext_with_ctx_domain'
);
} );
test( '_n() calls filters', () => {
const hooks = createHooksWithI18nFilters();
const i18n = createI18n( undefined, undefined, hooks );
expect( i18n._n( 'hello', 'hellos', 1 ) ).toEqual(
'hello/i18n.ngettext/i18n.ngettext_default'
);
expect( i18n._n( 'hello', 'hellos', 1, 'domain' ) ).toEqual(
'hello/i18n.ngettext/i18n.ngettext_domain'
);
expect( i18n._n( 'hello', 'hellos', 2 ) ).toEqual(
'hellos/i18n.ngettext/i18n.ngettext_default'
);
expect( i18n._n( 'hello', 'hellos', 2, 'domain' ) ).toEqual(
'hellos/i18n.ngettext/i18n.ngettext_domain'
);
} );
test( '_nx() calls filters', () => {
const hooks = createHooksWithI18nFilters();
const i18n = createI18n( undefined, undefined, hooks );
expect( i18n._nx( 'hello', 'hellos', 1, 'ctx' ) ).toEqual(
'hello/i18n.ngettext_with_ctx/i18n.ngettext_with_ctx_default'
);
expect( i18n._nx( 'hello', 'hellos', 1, 'ctx', 'domain' ) ).toEqual(
'hello/i18n.ngettext_with_ctx/i18n.ngettext_with_ctx_domain'
);
expect( i18n._nx( 'hello', 'hellos', 2, 'ctx' ) ).toEqual(
'hellos/i18n.ngettext_with_ctx/i18n.ngettext_with_ctx_default'
);
expect( i18n._nx( 'hello', 'hellos', 2, 'ctx', 'domain' ) ).toEqual(
'hellos/i18n.ngettext_with_ctx/i18n.ngettext_with_ctx_domain'
);
} );
test( 'hasTranslation() calls filters', () => {
const hooks = createHooksWithI18nFilters();
const { hasTranslation } = createI18n( frenchLocale, undefined, hooks );
expect( hasTranslation( 'hello' ) ).toBe( true );
expect( hasTranslation( 'hello', 'not a greeting' ) ).toBe( false );
expect( hasTranslation( 'Always' ) ).toBe( true );
expect( hasTranslation( 'Always', 'other context' ) ).toBe( false );
expect( hasTranslation( 'Always', undefined, 'domain' ) ).toBe( false );
} );
} );
/* eslint-enable @wordpress/i18n-text-domain, @wordpress/i18n-translator-comments */

46
node_modules/@wordpress/i18n/src/test/default-i18n.js generated vendored Normal file
View File

@@ -0,0 +1,46 @@
/* eslint-disable @wordpress/i18n-text-domain, @wordpress/i18n-translator-comments */
/**
* WordPress dependencies
*/
import { __, _x, _n, _nx } from '@wordpress/i18n';
import { addFilter } from '@wordpress/hooks';
describe( 'i18n filters', () => {
test( 'Default i18n functions call filters', () => {
addFilter( 'i18n.gettext', 'tests', () => {
return 'goodbye';
} );
expect( __( 'hello' ) ).toBe( 'goodbye' );
addFilter( 'i18n.gettext_with_context', 'tests', () => {
return 'goodbye';
} );
expect( _x( 'hello', 'context' ) ).toBe( 'goodbye' );
addFilter(
'i18n.ngettext',
'tests',
( translation, singular, plural, count ) => {
if ( count === 1 ) {
return 'goodbye';
}
return 'goodbyes';
}
);
expect( _n( 'hello', 'hellos', 1 ) ).toBe( 'goodbye' );
expect( _n( 'hello', 'hellos', 2 ) ).toBe( 'goodbyes' );
addFilter(
'i18n.ngettext_with_context',
'tests',
( translation, singular, plural, count ) => {
if ( count === 1 ) {
return 'goodbye';
}
return 'goodbyes';
}
);
expect( _nx( 'hello', 'hellos', 1, 'context' ) ).toBe( 'goodbye' );
expect( _nx( 'hello', 'hellos', 2, 'context' ) ).toBe( 'goodbyes' );
} );
} );
/* eslint-enable @wordpress/i18n-text-domain, @wordpress/i18n-translator-comments */

33
node_modules/@wordpress/i18n/src/test/sprintf.js generated vendored Normal file
View File

@@ -0,0 +1,33 @@
// 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' );
} );
it( 'replaces named placeholders', () => {
const result = sprintf( 'bonjour %(name)s', { name: 'Riad' } );
expect( result ).toBe( 'bonjour Riad' );
} );
} );
} );

View File

@@ -0,0 +1,44 @@
/**
* Internal dependencies
*/
import { createI18n } from '..';
/**
* WordPress dependencies
*/
import { createHooks } from '@wordpress/hooks';
describe( 'i18n updates', () => {
it( 'updates on setLocaleData', () => {
const hooks = createHooks();
const i18n = createI18n( undefined, undefined, hooks );
const doneTranslations = [];
function doTranslation() {
doneTranslations.push( i18n.__( 'original' ) );
}
i18n.subscribe( doTranslation );
// Do translation on empty instance with no translation data.
doTranslation();
// Set translation data.
i18n.setLocaleData( {
original: [ 'translated' ],
} );
// Add a filter and then remove it.
const filter = ( text ) => '[' + text + ']';
hooks.addFilter( 'i18n.gettext', 'test', filter );
hooks.removeFilter( 'i18n.gettext', 'test', filter );
expect( doneTranslations ).toEqual( [
'original', // No translations before setLocaleData.
'translated', // After setLocaleData.
'[translated]', // After addFilter.
'translated', // After removeFilter.
] );
} );
} );