diff --git a/.eslintrc.js b/.eslintrc.js index 41922985c7f..5a6cb4dd63d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -23,6 +23,7 @@ module.exports = { 'src/worker/', 'src/script/components/Icon.tsx', '*.js', + 'src/types/i18n.d.ts', ], parserOptions: { project: ['./tsconfig.build.json', './server/tsconfig.json'], diff --git a/.prettierignore b/.prettierignore index 439678a4661..8e37a2d4c27 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,4 +11,4 @@ CHANGELOG.md node_modules npm-debug.log yarn-error.log - +src/types/i18n.d.ts diff --git a/bin/transalations_generate_types.js b/bin/transalations_generate_types.js new file mode 100644 index 00000000000..838cffd210f --- /dev/null +++ b/bin/transalations_generate_types.js @@ -0,0 +1,38 @@ +const fs = require('fs'); +const path = require('path'); + +const escapeString = string => { + return string + .replace(/\\/g, '\\\\') // Escape backslashes first + .replace(/'/g, "\\'") // Escape single quotes + .replace(/\n/g, '\\n') // Escape newlines + .replace(/\r/g, '\\r') // Escape carriage returns + .replace(/\t/g, '\\t'); // Escape tabs +}; + +const generateTypeDefinitions = (jsonPath, outputPath) => { + const json = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); + + const header = `// This file is autogenerated by the script. DO NOT EDIT BY HAND. +// To update this file, run: yarn run translate:generate-types + +`; + + const body = Object.entries(json) + .map(([key, value]) => ` '${key}': \`${escapeString(value)}\`;`) + .join('\n'); + + const content = `${header}declare module 'I18n/en-US.json' { + const translations: { +${body} + }; + export default translations; +} +`; + + fs.writeFileSync(outputPath, content); +}; + +const ROOT_PATH = path.resolve(__dirname, '..'); + +generateTypeDefinitions(path.join(ROOT_PATH, 'src/i18n/en-US.json'), path.join(ROOT_PATH, 'src/types/i18n.d.ts')); diff --git a/package.json b/package.json index fa2aaadf396..2cb788d8858 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "babel-loader": "9.2.1", "babel-plugin-transform-import-meta": "2.2.1", "cross-env": "7.0.3", + "cross-spawn": "^7.0.6", "css-loader": "7.1.2", "cssnano": "7.0.6", "dexie": "4.0.7", @@ -202,7 +203,9 @@ "test:server": "cd server && yarn test", "test:types": "tsc --project tsconfig.build.json --noEmit && cd server && tsc --noEmit", "translate:extract": "i18next-scanner 'src/{page,script}/**/*.{js,html,htm}'", - "translate:merge": "formatjs extract --format './bin/translations_extract_formatter.js' --out-file './src/i18n/en-US.json'" + "translate:merge": "formatjs extract --format './bin/translations_extract_formatter.js' --out-file './src/i18n/en-US.json'", + "translate:generate-types": "node ./bin/transalations_generate_types.js", + "typecheck": "tsc --noEmit" }, "resolutions": { "cross-spawn": "7.0.6", diff --git a/src/i18n/en-US.json b/src/i18n/en-US.json index 9e975821cd7..1f2c097c4b6 100644 --- a/src/i18n/en-US.json +++ b/src/i18n/en-US.json @@ -687,7 +687,7 @@ "featureConfigChangeModalAudioVideoDescriptionItemCameraEnabled": "Camera in calls is enabled", "featureConfigChangeModalAudioVideoHeadline": "There has been a change in {brandName}", "featureConfigChangeModalConferenceCallingEnabled": "Your team was upgraded to {brandName} Enterprise, which gives you access to features such as conference calls and more. [link]Learn more about {brandName} Enterprise[/link]", - "featureConfigChangeModalConferenceCallingTitle": "{brandName} Enterprise", + "featureConfigChangeModalConferenceCallingHeadline": "{brandName} Enterprise", "featureConfigChangeModalConversationGuestLinksDescriptionItemConversationGuestLinksDisabled": "Generating guest links is now disabled for all group admins.", "featureConfigChangeModalConversationGuestLinksDescriptionItemConversationGuestLinksEnabled": "Generating guest links is now enabled for all group admins.", "featureConfigChangeModalConversationGuestLinksHeadline": "Team settings changed", diff --git a/src/script/E2EIdentity/Modals/Modals.ts b/src/script/E2EIdentity/Modals/Modals.ts index b1332ced7e6..1e52df10256 100644 --- a/src/script/E2EIdentity/Modals/Modals.ts +++ b/src/script/E2EIdentity/Modals/Modals.ts @@ -80,14 +80,20 @@ export const getModalOptions = ({ `; + + const gracePeriodOverParagraph = t('acme.settingsChanged.gracePeriodOver.paragraph', undefined, { + br: '
', + ...replaceLearnMore, + }); + + const settingsChangedParagraph = t('acme.settingsChanged.paragraph', undefined, {br: '
', ...replaceLearnMore}); + switch (type) { case ModalType.ENROLL: options = { text: { closeBtnLabel: t('acme.settingsChanged.button.close'), - htmlMessage: extraParams?.isGracePeriodOver - ? t('acme.settingsChanged.gracePeriodOver.paragraph', {}, {br: '
', ...replaceLearnMore}) - : t('acme.settingsChanged.paragraph', {}, {br: '
', ...replaceLearnMore}), + htmlMessage: extraParams?.isGracePeriodOver ? gracePeriodOverParagraph : settingsChangedParagraph, title: t('acme.settingsChanged.headline.alt'), }, primaryAction: { @@ -109,8 +115,14 @@ export const getModalOptions = ({ text: { closeBtnLabel: t('acme.renewCertificate.button.close'), htmlMessage: extraParams?.isGracePeriodOver - ? t('acme.renewCertificate.gracePeriodOver.paragraph') - : t('acme.renewCertificate.paragraph'), + ? // @ts-expect-error + // the "url" should be provided + // TODO: check it when changing this code + t('acme.renewCertificate.gracePeriodOver.paragraph') + : // @ts-expect-error + // the "url" should be provided + // TODO: check it when changing this code + t('acme.renewCertificate.paragraph'), title: t('acme.renewCertificate.headline.alt'), }, primaryAction: { @@ -148,7 +160,10 @@ export const getModalOptions = ({ options = { text: { closeBtnLabel: t('acme.settingsChanged.button.close'), - htmlMessage: t('acme.remindLater.paragraph', extraParams?.delayTime), + // @ts-expect-error + // the "url" should be provided + // TODO: check it when changing this code + htmlMessage: t('acme.remindLater.paragraph', {delayTime: extraParams?.delayTime}), title: t('acme.settingsChanged.headline.alt'), }, primaryAction: { @@ -166,8 +181,8 @@ export const getModalOptions = ({ text: { closeBtnLabel: t('acme.error.button.close'), htmlMessage: extraParams?.isGracePeriodOver - ? t('acme.error.gracePeriod.paragraph', {}, {br: '
'}) - : t('acme.error.paragraph', {}, {br: '
'}), + ? t('acme.error.gracePeriod.paragraph', undefined, {br: '
'}) + : t('acme.error.paragraph', undefined, {br: '
'}), title: t('acme.error.headline'), }, primaryAction: { @@ -199,6 +214,9 @@ export const getModalOptions = ({ text: { closeBtnLabel: t('acme.done.button.close'), htmlMessage: `
${svgHtml}${ + // @ts-expect-error + // the "url" should be provided + // TODO: check it when changing this code extraParams?.isRenewal ? t('acme.renewal.done.paragraph') : t('acme.done.paragraph') }
`, title: extraParams?.isRenewal ? t('acme.renewal.done.headline') : t('acme.done.headline'), diff --git a/src/script/auth/page/Login.tsx b/src/script/auth/page/Login.tsx index 43859598f96..8ff3f3761f0 100644 --- a/src/script/auth/page/Login.tsx +++ b/src/script/auth/page/Login.tsx @@ -393,7 +393,7 @@ const LoginComponent = ({

{t('login.twoFactorLoginTitle')}

- {t('login.twoFactorLoginSubHead', {email: twoFactorLoginData.email})} + {t('login.twoFactorLoginSubHead', {email: twoFactorLoginData.email as string})}
)} diff --git a/src/script/page/MainContent/panels/preferences/avPreferences/CameraPreferences.tsx b/src/script/page/MainContent/panels/preferences/avPreferences/CameraPreferences.tsx index 3316ac0c075..24125682aaa 100644 --- a/src/script/page/MainContent/panels/preferences/avPreferences/CameraPreferences.tsx +++ b/src/script/page/MainContent/panels/preferences/avPreferences/CameraPreferences.tsx @@ -139,13 +139,17 @@ const CameraPreferences: React.FC = ({
', - br: '
', - faqLink: `
`, - }), + __html: t( + 'preferencesAVNoCamera', + {brandName}, + { + '/faqLink': '', + br: '
', + faqLink: ``, + }, + ), }} />

- {t('preferencesOptionsCallLogsDetail', brandName)} + {t('preferencesOptionsCallLogsDetail', {brandName})}

); diff --git a/src/script/page/RightSidebar/AddParticipants/AddParticipants.tsx b/src/script/page/RightSidebar/AddParticipants/AddParticipants.tsx index 124b4d7d2e6..92b739ce875 100644 --- a/src/script/page/RightSidebar/AddParticipants/AddParticipants.tsx +++ b/src/script/page/RightSidebar/AddParticipants/AddParticipants.tsx @@ -127,7 +127,7 @@ const AddParticipants: FC = ({ const enabledAddAction = selectedContacts.length > ENABLE_ADD_ACTIONS_LENGTH; const headerText = selectedContacts.length - ? t('addParticipantsHeaderWithCounter', selectedContacts.length) + ? t('addParticipantsHeaderWithCounter', {number: selectedContacts.length}) : t('addParticipantsHeader'); const showIntegrations = useMemo(() => { diff --git a/src/script/page/RightSidebar/ConversationDetails/ConversationDetails.tsx b/src/script/page/RightSidebar/ConversationDetails/ConversationDetails.tsx index 26d099f7080..9079a723d2f 100644 --- a/src/script/page/RightSidebar/ConversationDetails/ConversationDetails.tsx +++ b/src/script/page/RightSidebar/ConversationDetails/ConversationDetails.tsx @@ -290,10 +290,9 @@ const ConversationDetails = forwardRef