diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index f73b5b23e..9923dc0f0 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -637,6 +637,14 @@ "gasPriceNoDenom": { "message": "Gas Price" }, + "gdprMessage": { + "message": "This data is aggregated and is therefore anonymous for the purposes of General Data Protection Regulation (EU) 2016/679. For more information in relation to our privacy practices, please see our $1.", + "description": "$1 refers to the gdprMessagePrivacyPolicy message, the translation of which is meant to be used exclusively in the context of gdprMessage" + }, + "gdprMessagePrivacyPolicy": { + "message": "Privacy Policy here", + "description": "this translation is intended to be exclusively used as the replacement for the $1 in the gdprMessage translation" + }, "general": { "message": "General" }, diff --git a/ui/app/helpers/utils/i18n-helper.js b/ui/app/helpers/utils/i18n-helper.js index 29e068648..66de8b558 100644 --- a/ui/app/helpers/utils/i18n-helper.js +++ b/ui/app/helpers/utils/i18n-helper.js @@ -1,4 +1,5 @@ // cross-browser connection to extension i18n API +import React from 'react' import log from 'loglevel' import * as Sentry from '@sentry/browser' @@ -39,13 +40,30 @@ export const getMessage = (localeCode, localeMessages, key, substitutions) => { } const entry = localeMessages[key] let phrase = entry.message + + const hasSubstitutions = Boolean(substitutions && substitutions.length) + const hasReactSubstitutions = hasSubstitutions && + substitutions.some((element) => typeof element === 'function' || typeof element === 'object') + // perform substitutions - if (substitutions && substitutions.length) { - substitutions.forEach((substitution, index) => { - const regex = new RegExp(`\\$${index + 1}`, 'g') - phrase = phrase.replace(regex, substitution) + if (hasSubstitutions) { + const parts = phrase.split(/(\$\d)/g) + const partsToReplace = phrase.match(/(\$\d)/g) + + if (partsToReplace.length > substitutions.length) { + throw new Error(`Insufficient number of substitutions for message: '${phrase}'`) + } + + const substitutedParts = parts.map((part) => { + const subMatch = part.match(/\$(\d)/) + return subMatch ? substitutions[Number(subMatch[1]) - 1] : part }) + + phrase = hasReactSubstitutions + ? { substitutedParts } + : substitutedParts.join('') } + return phrase } diff --git a/ui/app/helpers/utils/i18n-helper.test.js b/ui/app/helpers/utils/i18n-helper.test.js new file mode 100644 index 000000000..2832022e6 --- /dev/null +++ b/ui/app/helpers/utils/i18n-helper.test.js @@ -0,0 +1,160 @@ +import { getMessage } from './i18n-helper' +import React from 'react' +import { shallow } from 'enzyme' +import assert from 'assert' + +describe('i18n helper', function () { + const TEST_LOCALE_CODE = 'TEST_LOCALE_CODE' + + const TEST_KEY_1 = 'TEST_KEY_1' + const TEST_KEY_2 = 'TEST_KEY_2' + const TEST_KEY_3 = 'TEST_KEY_3' + const TEST_KEY_4 = 'TEST_KEY_4' + const TEST_KEY_5 = 'TEST_KEY_5' + const TEST_KEY_6 = 'TEST_KEY_6' + const TEST_KEY_6_HELPER = 'TEST_KEY_6_HELPER' + const TEST_KEY_7 = 'TEST_KEY_7' + const TEST_KEY_7_HELPER_1 = 'TEST_KEY_7_HELPER_1' + const TEST_KEY_7_HELPER_2 = 'TEST_KEY_7_HELPER_2' + const TEST_KEY_8 = 'TEST_KEY_8' + const TEST_KEY_8_HELPER_1 = 'TEST_KEY_8_HELPER_1' + const TEST_KEY_8_HELPER_2 = 'TEST_KEY_8_HELPER_2' + + const TEST_SUBSTITUTION_1 = 'TEST_SUBSTITUTION_1' + const TEST_SUBSTITUTION_2 = 'TEST_SUBSTITUTION_2' + const TEST_SUBSTITUTION_3 = 'TEST_SUBSTITUTION_3' + const TEST_SUBSTITUTION_4 = 'TEST_SUBSTITUTION_4' + const TEST_SUBSTITUTION_5 = 'TEST_SUBSTITUTION_5' + + const testLocaleMessages = { + [TEST_KEY_1]: { + message: 'This is a simple message.', + expectedResult: 'This is a simple message.', + }, + [TEST_KEY_2]: { + message: 'This is a message with a single non-react substitution $1.', + }, + [TEST_KEY_3]: { + message: 'This is a message with two non-react substitutions $1 and $2.', + }, + [TEST_KEY_4]: { + message: '$1 - $2 - $3 - $4 - $5', + }, + [TEST_KEY_5]: { + message: '$1 - $2 - $3', + }, + [TEST_KEY_6]: { + 'message': 'Testing a react substitution $1.', + }, + [TEST_KEY_6_HELPER]: { + 'message': TEST_SUBSTITUTION_1, + }, + [TEST_KEY_7]: { + 'message': 'Testing a react substitution $1 and another $2.', + }, + [TEST_KEY_7_HELPER_1]: { + 'message': TEST_SUBSTITUTION_1, + }, + [TEST_KEY_7_HELPER_2]: { + 'message': TEST_SUBSTITUTION_2, + }, + [TEST_KEY_8]: { + 'message': 'Testing a mix $1 of react substitutions $2 and string substitutions $3 + $4.', + }, + [TEST_KEY_8_HELPER_1]: { + 'message': TEST_SUBSTITUTION_3, + }, + [TEST_KEY_8_HELPER_2]: { + 'message': TEST_SUBSTITUTION_4, + }, + } + const t = getMessage.bind(null, TEST_LOCALE_CODE, testLocaleMessages) + + const TEST_SUBSTITUTION_6 = ( +