diff --git a/__tests__/client/prerender.spec.js b/__tests__/client/prerender.spec.js
index c4ff07abc..52462df39 100644
--- a/__tests__/client/prerender.spec.js
+++ b/__tests__/client/prerender.spec.js
@@ -128,6 +128,17 @@ describe('loadPrerenderScripts', () => {
expect(getLocalePack).not.toHaveBeenCalled();
})
);
+
+ it('should still resolve if useNativeIntl is set to true but not call getLocalePack', async () => {
+ window.useNativeIntl = true;
+ const { getLocalePack } = require('@americanexpress/one-app-ducks');
+ const initialState = fromJS({ intl: { activeLocale: 'es-ES' } });
+ getLocalePack.mockClear();
+
+ await loadPrerenderScripts(initialState);
+
+ expect(getLocalePack).not.toHaveBeenCalled();
+ });
});
describe('moveHelmetScripts', () => {
diff --git a/__tests__/integration/helpers/testRunner.js b/__tests__/integration/helpers/testRunner.js
index 9e724608b..30ad916ae 100644
--- a/__tests__/integration/helpers/testRunner.js
+++ b/__tests__/integration/helpers/testRunner.js
@@ -95,7 +95,7 @@ const setUpTestRunner = async ({
}));
// uncomment this line in order to view full logs for debugging
- // logWatcherDuplex.pipe(process.stdout);
+ logWatcherDuplex.pipe(process.stdout);
try {
await Promise.all([
diff --git a/__tests__/server/config/env/runTime.spec.js b/__tests__/server/config/env/runTime.spec.js
index 1ae7f49ee..31f012a4d 100644
--- a/__tests__/server/config/env/runTime.spec.js
+++ b/__tests__/server/config/env/runTime.spec.js
@@ -525,4 +525,20 @@ describe('runTime', () => {
expect(() => otelTraceCollectorUrl.validate()).not.toThrow();
});
});
+
+ describe('ONE_CONFIG_USE_NATIVE_INTL', () => {
+ const useNativeIntl = getEnvVarConfig('ONE_CONFIG_USE_NATIVE_INTL');
+
+ it('should have a default value of false', () => {
+ expect(useNativeIntl.defaultValue).toBe('false');
+ });
+
+ it('should normalise the value to false when not explicitly true', () => {
+ expect(useNativeIntl.normalize('Value')).toBe('false');
+ expect(useNativeIntl.normalize('VALUE')).toBe('false');
+ expect(useNativeIntl.normalize('true')).toBe('true');
+ expect(useNativeIntl.normalize('TRUE')).toBe('true');
+ expect(useNativeIntl.normalize('FALSE')).toBe('false');
+ });
+ });
});
diff --git a/__tests__/server/plugins/reactHtml/index.spec.jsx b/__tests__/server/plugins/reactHtml/index.spec.jsx
index b21b7b4b4..b47928a31 100644
--- a/__tests__/server/plugins/reactHtml/index.spec.jsx
+++ b/__tests__/server/plugins/reactHtml/index.spec.jsx
@@ -23,6 +23,7 @@ import reactHtml, {
renderModuleScripts,
renderExternalFallbacks,
checkStateForRedirectAndStatusCode,
+ renderEnvironmentVariables,
} from '../../../../src/server/plugins/reactHtml';
import { getClientStateConfig } from '../../../../src/server/utils/stateConfig';
import transit from '../../../../src/universal/utils/transit';
@@ -301,6 +302,8 @@ describe('reactHtml', () => {
cdnUrl: '/cdnUrl/',
rootModuleName: 'test-root',
}));
+
+ process.env.ONE_CONFIG_USE_NATIVE_INTL = 'false';
});
function removeInitialState(body) {
@@ -448,6 +451,15 @@ describe('reactHtml', () => {
expect(reply.send.mock.calls[0][0]).not.toContain('src="/cdnUrl/app/1.2.3-rc.4-abc123/i18n/');
});
+ it('sends a rendered page without the locale data script tag when te ONE_CONFIG_USE_NATIVE_INTL environment variable is true', () => {
+ process.env.ONE_CONFIG_USE_NATIVE_INTL = 'true';
+ setFullMap();
+ sendHtml(request, reply);
+
+ expect(reply.send).toHaveBeenCalledTimes(1);
+ expect(reply.send.mock.calls[0][0]).not.toContain('src="/cdnUrl/app/1.2.3-rc.4-abc123/i18n/');
+ });
+
it('sends a rendered page with the module styles and scripts', () => {
sendHtml(request, reply);
expect(reply.send).toHaveBeenCalledTimes(1);
@@ -1349,4 +1361,23 @@ describe('reactHtml', () => {
expect(reply.send.mock.calls[0][0]).toContain('
One App');
});
});
+
+ describe('renderEnvironmentVariables', () => {
+ it('should set useNativeIntl to false if the environment variable is not true', () => {
+ expect(renderEnvironmentVariables('')).toMatchInlineSnapshot(`
+ ""
+ `);
+ });
+
+ it('should set useNativeIntl to true is the environment variable is true', () => {
+ process.env.ONE_CONFIG_USE_NATIVE_INTL = 'true';
+ expect(renderEnvironmentVariables('')).toMatchInlineSnapshot(`
+ ""
+ `);
+ });
+ });
});
diff --git a/docs/api/server/Environment-Variables.md b/docs/api/server/Environment-Variables.md
index ea0823053..4387e678f 100644
--- a/docs/api/server/Environment-Variables.md
+++ b/docs/api/server/Environment-Variables.md
@@ -31,6 +31,7 @@ One App can be configured via Environment Variables:
* [`ONE_CLIENT_ROOT_MODULE_NAME`](#one_client_root_module_name) ⚠️
* [`ONE_CLIENT_CDN_URL`](#one_client_cdn_url) ⚠️
* [`ONE_CONFIG_ENV`](#one_config_env) ⚠️
+ * [`ONE_CONFIG_USE_NATIVE_INTL`](#one_config_use_native_intl)
* Running in Development
* [`NODE_ENV`](#node_env) ⚠️
* [`ONE_CLIENT_ROOT_MODULE_NAME`](#one_client_root_module_name) ⚠️
@@ -38,6 +39,7 @@ One App can be configured via Environment Variables:
* [`ONE_DANGEROUSLY_ACCEPT_BREAKING_EXTERNALS`](#ONE_DANGEROUSLY_ACCEPT_BREAKING_EXTERNALS)
* [`ONE_CSP_ALLOW_INLINE_SCRIPTS`](#ONE_CSP_ALLOW_INLINE_SCRIPTS)
* [`ONE_DANGEROUSLY_DISABLE_CSP`](#ONE_DANGEROUSLY_DISABLE_CSP)
+ * [`ONE_CONFIG_USE_NATIVE_INTL`](#one_config_use_native_intl)
* Server Settings
* [`HOLOCRON_SERVER_MAX_MODULES_RETRY`](#holocron_server_max_modules_retry)
* [`HOLOCRON_SERVER_MAX_SIM_MODULES_FETCH`](#holocron_server_max_sim_modules_fetch)
@@ -79,6 +81,7 @@ One App can be configured via Environment Variables:
* [`ONE_CLIENT_REPORTING_URL`](#one_client_reporting_url) ⚠️
* [`ONE_CLIENT_ROOT_MODULE_NAME`](#one_client_root_module_name) ⚠️
* [`ONE_CONFIG_ENV`](#one_config_env) ⚠️
+ * [`ONE_CONFIG_USE_NATIVE_INTL`](#one_config_use_native_intl)
* [`ONE_ENABLE_POST_TO_MODULE_ROUTES`](#one_enable_post_to_module_routes)
* [`ONE_ENABLE_REQUEST_LOGGING_WHILE_TRACING`](#one_enable_request_logging_while_tracing)
* [`ONE_MAP_POLLING_MAX`](#one_map_polling_max)
@@ -550,6 +553,27 @@ ONE_CONFIG_ENV=staging
ONE_CONFIG_ENV=undefined
```
+## `ONE_CONFIG_USE_NATIVE_INTL`
+**Runs In**
+* ✅ Production
+* ✅ Development
+
+Feature flag to disable lean-intl polyfill.
+This allows you to use modern intl features such as timezones, but will result in your application supporting fewer older browsers.
+
+**Shape**
+```bash
+ONE_CONFIG_USE_NATIVE_INTL=Boolean
+```
+**Example**
+```bash
+ONE_CONFIG_USE_NATIVE_INTL=true
+```
+**Default Value**
+```bash
+ONE_CONFIG_USE_NATIVE_INTL=false
+```
+
## `ONE_DANGEROUSLY_ACCEPT_BREAKING_EXTERNALS`
**Runs In**
@@ -903,4 +927,4 @@ ONE_ENABLE_REQUEST_LOGGING_WHILE_TRACING=true
[`HTTPS_PRIVATE_KEY_PASS_FILE_PATH`]: #https_private_key_pass_file_path
[`HTTPS_PORT`]: #https_port
[OTel Environment Variable Specification]: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
-[OTel Resource SDK documentation]: https://opentelemetry.io/docs/specs/otel/resource/sdk/#specifying-resource-information-via-an-environment-variable
\ No newline at end of file
+[OTel Resource SDK documentation]: https://opentelemetry.io/docs/specs/otel/resource/sdk/#specifying-resource-information-via-an-environment-variable
diff --git a/package-lock.json b/package-lock.json
index 527885733..1c60db0ff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,7 @@
"dependencies": {
"@americanexpress/env-config-utils": "^2.0.4",
"@americanexpress/fetch-enhancers": "^1.1.5",
- "@americanexpress/one-app-ducks": "^4.4.4",
+ "@americanexpress/one-app-ducks": "^4.5.1",
"@americanexpress/one-app-router": "^1.2.1",
"@americanexpress/one-app-server-bundler": "^1.0.2",
"@americanexpress/vitruvius": "^3.0.1",
@@ -314,9 +314,9 @@
}
},
"node_modules/@americanexpress/one-app-ducks": {
- "version": "4.4.4",
- "resolved": "https://registry.npmjs.org/@americanexpress/one-app-ducks/-/one-app-ducks-4.4.4.tgz",
- "integrity": "sha512-KlIcfAnMdThI3r+xBihJrDAjfgA51GR7xO5cuMbL965JEdlWNFhw/f2vsWiSID7RKMcMbkhZ3UsbwrVNBnLVow==",
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@americanexpress/one-app-ducks/-/one-app-ducks-4.5.1.tgz",
+ "integrity": "sha512-yAMNxEduc5+WLImNkolBvhaeO3WS5ZMBifP8W6VoJxunba14rFDRwI4K31jndQ2ktapA/0lLmK0DRcsmGI/NPg==",
"dependencies": {
"holocron": "^1.0.0",
"immutable": "^4.0.0-rc.12",
diff --git a/package.json b/package.json
index 61f427dc5..ae582577b 100644
--- a/package.json
+++ b/package.json
@@ -82,7 +82,7 @@
"dependencies": {
"@americanexpress/env-config-utils": "^2.0.4",
"@americanexpress/fetch-enhancers": "^1.1.5",
- "@americanexpress/one-app-ducks": "^4.4.4",
+ "@americanexpress/one-app-ducks": "^4.5.1",
"@americanexpress/one-app-router": "^1.2.1",
"@americanexpress/one-app-server-bundler": "^1.0.2",
"@americanexpress/vitruvius": "^3.0.1",
diff --git a/src/client/polyfill/Intl.js b/src/client/polyfill/Intl.js
index 8cbb70143..234499492 100644
--- a/src/client/polyfill/Intl.js
+++ b/src/client/polyfill/Intl.js
@@ -19,5 +19,7 @@
import Intl from 'lean-intl';
-global.Intl = Intl;
-global.IntlPolyfill = Intl;
+if (!(window && window.useNativeIntl)) {
+ global.Intl = Intl;
+ global.IntlPolyfill = Intl;
+}
diff --git a/src/client/prerender.js b/src/client/prerender.js
index a68ee5984..9a2fb12e0 100644
--- a/src/client/prerender.js
+++ b/src/client/prerender.js
@@ -42,7 +42,7 @@ export function initializeClientStore() {
export function loadPrerenderScripts(initialState) {
const locale = initialState && initialState.getIn(['intl', 'activeLocale']);
- return locale ? getLocalePack(locale) : Promise.resolve();
+ return locale && !window?.useNativeIntl ? getLocalePack(locale) : Promise.resolve();
}
export function moveHelmetScripts() {
diff --git a/src/server/config/env/runTime.js b/src/server/config/env/runTime.js
index c3dc079f4..18e4bde0f 100644
--- a/src/server/config/env/runTime.js
+++ b/src/server/config/env/runTime.js
@@ -186,6 +186,16 @@ const runTime = [
validate: (value) => { if (!value) { throw new Error('The `ONE_CLIENT_ROOT_MODULE_NAME` environment variable must be defined.'); } },
defaultValue: () => (process.env.NODE_ENV === 'development' ? argv.rootModuleName : undefined),
},
+ {
+ name: 'ONE_CONFIG_USE_NATIVE_INTL',
+ defaultValue: 'false',
+ normalize: (input) => {
+ if (input?.toLowerCase() === 'true') {
+ return 'true';
+ }
+ return 'false';
+ },
+ },
{
name: 'ONE_REFERRER_POLICY_OVERRIDE',
defaultValue: () => '',
diff --git a/src/server/plugins/reactHtml/index.jsx b/src/server/plugins/reactHtml/index.jsx
index 7206885f7..72b95fcf0 100644
--- a/src/server/plugins/reactHtml/index.jsx
+++ b/src/server/plugins/reactHtml/index.jsx
@@ -65,6 +65,10 @@ const legacyBrowserChunkAssets = getChunkAssets(readJsonFile('../../../.build-me
.map((chunkAsset) => `legacy/${chunkAsset}`);
function renderI18nScript(clientInitialState, appBundlesURLPrefix) {
+ if (process.env.ONE_CONFIG_USE_NATIVE_INTL === 'true') {
+ return '';
+ }
+
const i18nFile = getI18nFileFromState(clientInitialState);
if (!i18nFile) {
return '';
@@ -236,6 +240,12 @@ export function getHead({
`;
}
+export function renderEnvironmentVariables(nonce) {
+ return ``;
+}
+
export function getBody({
isLegacy,
helmetInfo,
@@ -253,13 +263,14 @@ export function getBody({
const bundle = isLegacy ? 'legacyBrowser' : 'browser';
const { bodyAttributes, script } = helmetInfo;
const bundlePrefixForBrowser = isLegacy ? `${appBundlesURLPrefix}/legacy` : appBundlesURLPrefix;
+ const nonce = scriptNonce ? `nonce="${scriptNonce}"` : '';
return `
${appHtml || ''}
${disableScripts
? ''
: `
-
+ ${renderEnvironmentVariables(nonce)}
${assets}
${renderI18nScript(clientInitialState, bundlePrefixForBrowser)}
${renderExternalFallbacks({
diff --git a/src/server/polyfill/intl.js b/src/server/polyfill/intl.js
index 4d78166a8..2c5c0ca9b 100644
--- a/src/server/polyfill/intl.js
+++ b/src/server/polyfill/intl.js
@@ -14,4 +14,8 @@
* permissions and limitations under the License.
*/
-global.Intl = require('lean-intl');
+import Intl from 'lean-intl';
+
+if (process.env.ONE_CONFIG_USE_NATIVE_INTL !== 'true') {
+ global.Intl = Intl;
+}