diff --git a/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap b/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap
index 64c50db1f..6bcf33e48 100644
--- a/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap
+++ b/src/components/i18n/__tests__/__snapshots__/i18n.test.js.snap
@@ -1,21 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`I18n Component should attempt to perform a component translate: translated component 1`] = `"
t(lorem.ipsum, hello world)
"`;
-
-exports[`I18n Component should attempt to perform a string replace: translate 1`] = `
-{
- "emptyContext": "t(lorem.ipsum, {"context":" "})",
- "emptyPartialContext": "t(lorem.ipsum, {"context":"hello_ "})",
- "localeKey": "t(lorem.ipsum)",
- "multiContext": "t(lorem.ipsum, {"context":"hello_world"})",
- "multiContextWithEmptyValue": "t(lorem.ipsum, {"context":"hello_world"})",
- "multiKey": "t([lorem.ipsum,lorem.fallback])",
- "placeholder": "t(lorem.ipsum, hello world)",
-}
-`;
-
-exports[`I18n Component should attempt to perform translate with a node: translated node 1`] = `"t(lorem.ipsum, {"hello":"world"}, [object Object])
"`;
-
exports[`I18n Component should generate a predictable locale key output snapshot: key output 1`] = `
[
{
diff --git a/src/components/i18n/__tests__/__snapshots__/i18nHelpers.test.js.snap b/src/components/i18n/__tests__/__snapshots__/i18nHelpers.test.js.snap
new file mode 100644
index 000000000..df3f252f3
--- /dev/null
+++ b/src/components/i18n/__tests__/__snapshots__/i18nHelpers.test.js.snap
@@ -0,0 +1,25 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`I18nHelpers should attempt to perform a component translate: translated component 1`] = `"t(lorem.ipsum, hello world)
"`;
+
+exports[`I18nHelpers should attempt to perform a string replace: translate 1`] = `
+{
+ "emptyContext": "t(lorem.ipsum, {"context":" "})",
+ "emptyPartialContext": "t(lorem.ipsum, {"context":"hello_ "})",
+ "localeKey": "t(lorem.ipsum)",
+ "multiContext": "t(lorem.ipsum, {"context":"hello_world"})",
+ "multiContextWithEmptyValue": "t(lorem.ipsum, {"context":"hello_world"})",
+ "multiKey": "t([lorem.ipsum,lorem.fallback])",
+ "placeholder": "t(lorem.ipsum, hello world)",
+}
+`;
+
+exports[`I18nHelpers should attempt to perform translate with a node: translated node 1`] = `"t(lorem.ipsum, {"hello":"world"}, [object Object])
"`;
+
+exports[`I18nHelpers should have specific functions: i18nHelpers 1`] = `
+{
+ "EMPTY_CONTEXT": "LOCALE_EMPTY_CONTEXT",
+ "translate": [Function],
+ "translateComponent": [Function],
+}
+`;
diff --git a/src/components/i18n/__tests__/i18n.test.js b/src/components/i18n/__tests__/i18n.test.js
index 104243ded..1378c1edc 100644
--- a/src/components/i18n/__tests__/i18n.test.js
+++ b/src/components/i18n/__tests__/i18n.test.js
@@ -1,11 +1,9 @@
import { readFileSync } from 'fs';
import glob from 'glob';
import React from 'react';
-import PropTypes from 'prop-types';
-import { shallow } from 'enzyme';
import _get from 'lodash/get';
import enLocales from '../../../../public/locales/en-US.json';
-import { EMPTY_CONTEXT, I18n, translate, translateComponent } from '../i18n';
+import { I18n } from '../i18n';
/**
* Get translation keys.
@@ -68,54 +66,6 @@ describe('I18n Component', () => {
expect(component.html()).toMatchSnapshot('children');
});
- it('should attempt to perform translate with a node', () => {
- const ExampleComponent = () => {translate('lorem.ipsum', { hello: 'world' }, [])}
;
-
- ExampleComponent.propTypes = {};
- ExampleComponent.defaultProps = {};
-
- const component = shallow();
-
- expect(component.html()).toMatchSnapshot('translated node');
- });
-
- it('should attempt to perform a component translate', () => {
- const ExampleComponent = ({ t }) => {t('lorem.ipsum', 'hello world')}
;
-
- ExampleComponent.propTypes = {
- t: PropTypes.func
- };
-
- ExampleComponent.defaultProps = {
- t: translate
- };
-
- const TranslatedComponent = translateComponent(ExampleComponent);
- const component = shallow();
-
- expect(component.html()).toMatchSnapshot('translated component');
- });
-
- it('should attempt to perform a string replace', () => {
- const emptyContext = translate('lorem.ipsum', { context: EMPTY_CONTEXT });
- const emptyPartialContext = translate('lorem.ipsum', { context: ['hello', EMPTY_CONTEXT] });
- const localeKey = translate('lorem.ipsum');
- const placeholder = translate('lorem.ipsum', 'hello world');
- const multiContext = translate('lorem.ipsum', { context: ['hello', 'world'] });
- const multiContextWithEmptyValue = translate('lorem.ipsum', { context: ['hello', undefined, null, '', 'world'] });
- const multiKey = translate(['lorem.ipsum', undefined, null, '', 'lorem.fallback']);
-
- expect({
- emptyContext,
- emptyPartialContext,
- localeKey,
- placeholder,
- multiContext,
- multiContextWithEmptyValue,
- multiKey
- }).toMatchSnapshot('translate');
- });
-
it('should generate a predictable locale key output snapshot', () => {
expect(getKeys).toMatchSnapshot('key output');
});
diff --git a/src/components/i18n/__tests__/i18nHelpers.test.js b/src/components/i18n/__tests__/i18nHelpers.test.js
new file mode 100644
index 000000000..2ecee4b37
--- /dev/null
+++ b/src/components/i18n/__tests__/i18nHelpers.test.js
@@ -0,0 +1,58 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import PropTypes from 'prop-types';
+import { i18nHelpers, EMPTY_CONTEXT, translate, translateComponent } from '../i18nHelpers';
+
+describe('I18nHelpers', () => {
+ it('should have specific functions', () => {
+ expect(i18nHelpers).toMatchSnapshot('i18nHelpers');
+ });
+
+ it('should attempt to perform translate with a node', () => {
+ const ExampleComponent = () => {translate('lorem.ipsum', { hello: 'world' }, [])}
;
+
+ ExampleComponent.propTypes = {};
+ ExampleComponent.defaultProps = {};
+
+ const component = shallow();
+
+ expect(component.html()).toMatchSnapshot('translated node');
+ });
+
+ it('should attempt to perform a component translate', () => {
+ const ExampleComponent = ({ t }) => {t('lorem.ipsum', 'hello world')}
;
+
+ ExampleComponent.propTypes = {
+ t: PropTypes.func
+ };
+
+ ExampleComponent.defaultProps = {
+ t: translate
+ };
+
+ const TranslatedComponent = translateComponent(ExampleComponent);
+ const component = shallow();
+
+ expect(component.html()).toMatchSnapshot('translated component');
+ });
+
+ it('should attempt to perform a string replace', () => {
+ const emptyContext = translate('lorem.ipsum', { context: EMPTY_CONTEXT });
+ const emptyPartialContext = translate('lorem.ipsum', { context: ['hello', EMPTY_CONTEXT] });
+ const localeKey = translate('lorem.ipsum');
+ const placeholder = translate('lorem.ipsum', 'hello world');
+ const multiContext = translate('lorem.ipsum', { context: ['hello', 'world'] });
+ const multiContextWithEmptyValue = translate('lorem.ipsum', { context: ['hello', undefined, null, '', 'world'] });
+ const multiKey = translate(['lorem.ipsum', undefined, null, '', 'lorem.fallback']);
+
+ expect({
+ emptyContext,
+ emptyPartialContext,
+ localeKey,
+ placeholder,
+ multiContext,
+ multiContextWithEmptyValue,
+ multiKey
+ }).toMatchSnapshot('translate');
+ });
+});
diff --git a/src/components/i18n/i18n.js b/src/components/i18n/i18n.js
index c4e1a2460..2bad0450d 100644
--- a/src/components/i18n/i18n.js
+++ b/src/components/i18n/i18n.js
@@ -2,78 +2,10 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import i18next from 'i18next';
import XHR from 'i18next-http-backend';
-import { initReactI18next, Trans } from 'react-i18next';
+import { initReactI18next } from 'react-i18next';
import { useMount } from 'react-use';
import { helpers } from '../../common/helpers';
-
-/**
- * Check to help provide an empty context.
- *
- * @type {string}
- */
-const EMPTY_CONTEXT = 'LOCALE_EMPTY_CONTEXT';
-
-/**
- * Apply a string towards a key. Optional replacement values and component/nodes.
- * See, https://react.i18next.com/
- *
- * @param {string|Array} translateKey A key reference, or an array of a primary key with fallback keys.
- * @param {string|object|Array} values A default string if the key can't be found. An object with i18next settings. Or an array of objects (key/value) pairs used to replace string tokes. i.e. "[{ hello: 'world' }]"
- * @param {Array} components An array of HTML/React nodes used to replace string tokens. i.e. "[, ]"
- * @param {object} options
- * @param {string} options.emptyContextValue Check to allow an empty context value.
- * @returns {string|React.ReactNode}
- */
-const translate = (translateKey, values = null, components, { emptyContextValue = EMPTY_CONTEXT } = {}) => {
- const updatedValues = values;
- let updatedTranslateKey = translateKey;
-
- if (Array.isArray(updatedTranslateKey)) {
- updatedTranslateKey = updatedTranslateKey.filter(value => typeof value === 'string' && value.length > 0);
- }
-
- if (Array.isArray(updatedValues?.context)) {
- updatedValues.context = updatedValues.context
- .map(value => (value === emptyContextValue && ' ') || value)
- .filter(value => typeof value === 'string' && value.length > 0)
- .join('_');
- } else if (updatedValues?.context === emptyContextValue) {
- updatedValues.context = ' ';
- }
-
- if (helpers.TEST_MODE) {
- return helpers.noopTranslate(updatedTranslateKey, updatedValues, components);
- }
-
- if (components) {
- return (
- (i18next.store && ) || (
- t({updatedTranslateKey})
- )
- );
- }
-
- return (i18next.store && i18next.t(updatedTranslateKey, updatedValues)) || `t([${updatedTranslateKey}])`;
-};
-
-/**
- * Apply string replacements against a component, HOC.
- *
- * @param {React.ReactNode} Component
- * @returns {React.ReactNode}
- */
-const translateComponent = Component => {
- const withTranslation = ({ ...props }) => (
-
- );
-
- withTranslation.displayName = 'withTranslation';
- return withTranslation;
-};
+import { EMPTY_CONTEXT, translate, translateComponent } from './i18nHelpers';
/**
* Load I18n.
diff --git a/src/components/i18n/i18nHelpers.js b/src/components/i18n/i18nHelpers.js
new file mode 100644
index 000000000..4dcfd8b9e
--- /dev/null
+++ b/src/components/i18n/i18nHelpers.js
@@ -0,0 +1,81 @@
+import React from 'react';
+import i18next from 'i18next';
+import { Trans } from 'react-i18next';
+import { helpers } from '../../common/helpers';
+
+/**
+ * Check to help provide an empty context.
+ *
+ * @type {string}
+ */
+const EMPTY_CONTEXT = 'LOCALE_EMPTY_CONTEXT';
+
+/**
+ * Apply a string towards a key. Optional replacement values and component/nodes.
+ * See, https://react.i18next.com/
+ *
+ * @param {string|Array} translateKey A key reference, or an array of a primary key with fallback keys.
+ * @param {string|object|Array} values A default string if the key can't be found. An object with i18next settings. Or an array of objects (key/value) pairs used to replace string tokes. i.e. "[{ hello: 'world' }]"
+ * @param {Array} components An array of HTML/React nodes used to replace string tokens. i.e. "[, ]"
+ * @param {object} options
+ * @param {string} options.emptyContextValue Check to allow an empty context value.
+ * @returns {string|React.ReactNode}
+ */
+const translate = (translateKey, values = null, components, { emptyContextValue = EMPTY_CONTEXT } = {}) => {
+ const updatedValues = values;
+ let updatedTranslateKey = translateKey;
+
+ if (Array.isArray(updatedTranslateKey)) {
+ updatedTranslateKey = updatedTranslateKey.filter(value => typeof value === 'string' && value.length > 0);
+ }
+
+ if (Array.isArray(updatedValues?.context)) {
+ updatedValues.context = updatedValues.context
+ .map(value => (value === emptyContextValue && ' ') || value)
+ .filter(value => typeof value === 'string' && value.length > 0)
+ .join('_');
+ } else if (updatedValues?.context === emptyContextValue) {
+ updatedValues.context = ' ';
+ }
+
+ if (helpers.TEST_MODE) {
+ return helpers.noopTranslate(updatedTranslateKey, updatedValues, components);
+ }
+
+ if (components) {
+ return (
+ (i18next.store && ) || (
+ t({updatedTranslateKey})
+ )
+ );
+ }
+
+ return (i18next.store && i18next.t(updatedTranslateKey, updatedValues)) || `t([${updatedTranslateKey}])`;
+};
+
+/**
+ * Apply string replacements against a component, HOC.
+ *
+ * @param {React.ReactNode} Component
+ * @returns {React.ReactNode}
+ */
+const translateComponent = Component => {
+ const withTranslation = ({ ...props }) => (
+
+ );
+
+ withTranslation.displayName = 'withTranslation';
+ return withTranslation;
+};
+
+const i18nHelpers = {
+ EMPTY_CONTEXT,
+ translate,
+ translateComponent
+};
+
+export { i18nHelpers as default, i18nHelpers, EMPTY_CONTEXT, translate, translateComponent };