diff --git a/services/web/.eslintrc b/services/web/.eslintrc index 09898181af..4d7c76e5c0 100644 --- a/services/web/.eslintrc +++ b/services/web/.eslintrc @@ -114,7 +114,7 @@ // "files": ["**/frontend/js/**/components/**/*.{js,jsx,ts,tsx}", "**/frontend/js/**/hooks/**/*.{js,jsx,ts,tsx}"], "rules": { - "@overleaf/no-empty-trans": "error", + "@overleaf/no-unnecessary-trans": "error", "@overleaf/should-unescape-trans": "error", // https://astexplorer.net/ diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-create/error-message.jsx b/services/web/frontend/js/features/file-tree/components/file-tree-create/error-message.jsx index ac89755518..ef0c54429c 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-create/error-message.jsx +++ b/services/web/frontend/js/features/file-tree/components/file-tree-create/error-message.jsx @@ -1,5 +1,5 @@ import PropTypes from 'prop-types' -import { useTranslation, Trans } from 'react-i18next' +import { useTranslation } from 'react-i18next' import { FetchError } from '../../../../infrastructure/fetch-json' import RedirectToLogin from './redirect-to-login' import { @@ -29,14 +29,9 @@ export default function ErrorMessage({ error }) { case 'invalid_filename': return ( - + {t('invalid_filename', { + nameLimit: fileNameLimit, + })} ) diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.jsx b/services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.jsx index f8422bfbcf..36bc012ec4 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.jsx +++ b/services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.jsx @@ -1,4 +1,4 @@ -import { Trans, useTranslation } from 'react-i18next' +import { useTranslation } from 'react-i18next' import { Button } from 'react-bootstrap' import { useCallback, useEffect, useState } from 'react' import PropTypes from 'prop-types' @@ -209,16 +209,12 @@ const projectContextPropTypes = { } function UploadErrorMessage({ error, maxNumberOfFiles }) { + const { t } = useTranslation() switch (error) { case 'too-many-files': - return ( - - ) + return t('maximum_files_uploaded_together', { + max: maxNumberOfFiles, + }) default: return diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-create/redirect-to-login.jsx b/services/web/frontend/js/features/file-tree/components/file-tree-create/redirect-to-login.jsx index 4b0f1c9c41..fb91a864db 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-create/redirect-to-login.jsx +++ b/services/web/frontend/js/features/file-tree/components/file-tree-create/redirect-to-login.jsx @@ -1,11 +1,12 @@ import { useState, useEffect } from 'react' import PropTypes from 'prop-types' -import { Trans } from 'react-i18next' +import { useTranslation } from 'react-i18next' import { useProjectContext } from '../../../../shared/context/project-context' import { useLocation } from '../../../../shared/hooks/use-location' // handle "not-logged-in" errors by redirecting to the login page export default function RedirectToLogin() { + const { t } = useTranslation() const { _id: projectId } = useProjectContext(projectContextPropTypes) const [secondsToRedirect, setSecondsToRedirect] = useState(10) const location = useLocation() @@ -30,14 +31,9 @@ export default function RedirectToLogin() { } }, [projectId, location]) - return ( - - ) + return t('session_expired_redirecting_to_login', { + seconds: secondsToRedirect, + }) } const projectContextPropTypes = { diff --git a/services/web/frontend/js/features/pdf-preview/components/compile-time-warning.tsx b/services/web/frontend/js/features/pdf-preview/components/compile-time-warning.tsx index 158c130428..c28acdf3d9 100644 --- a/services/web/frontend/js/features/pdf-preview/components/compile-time-warning.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/compile-time-warning.tsx @@ -90,8 +90,9 @@ function CompileTimeWarning() {
]} + components={{ + strong: , + }} />
diff --git a/services/web/frontend/js/features/pdf-preview/components/compile-timeout-changing-soon.tsx b/services/web/frontend/js/features/pdf-preview/components/compile-timeout-changing-soon.tsx index ca8b7c0918..2f9f487b27 100644 --- a/services/web/frontend/js/features/pdf-preview/components/compile-timeout-changing-soon.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/compile-timeout-changing-soon.tsx @@ -18,7 +18,7 @@ export const CompileTimeoutChangingSoon: FC<{ const { t } = useTranslation() const compileTimeoutBlogLinks = [ - /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */ + /* eslint-disable-next-line jsx-a11y/anchor-has-content */ , - /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */ + /* eslint-disable-next-line jsx-a11y/anchor-has-content */ - + {t('invited_to_group_have_individual_subcription', { inviterName })} ) } diff --git a/services/web/frontend/js/features/source-editor/components/review-panel/toolbar/toggle-menu.tsx b/services/web/frontend/js/features/source-editor/components/review-panel/toolbar/toggle-menu.tsx index 050e5882d8..c3b89d5552 100644 --- a/services/web/frontend/js/features/source-editor/components/review-panel/toolbar/toggle-menu.tsx +++ b/services/web/frontend/js/features/source-editor/components/review-panel/toolbar/toggle-menu.tsx @@ -68,11 +68,15 @@ function ToggleMenu() { onClick={handleToggleFullTCStateCollapse} > {wantTrackChanges ? ( - // eslint-disable-next-line react/jsx-key - ]} /> + }} + /> ) : ( - // eslint-disable-next-line react/jsx-key - ]} /> + }} + /> )} , - ]} + components={{ strong: }} />

diff --git a/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx b/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx index e6e92d6b4c..50f925d07b 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx @@ -22,14 +22,9 @@ function GroupPlanCollaboratorCount({ planCode }: { planCode: string }) { if (planCode === 'collaborator') { return ( <> - + {t('collabs_per_proj', { + collabcount: 10, + })} ) } else if (planCode === 'professional') { @@ -39,28 +34,23 @@ function GroupPlanCollaboratorCount({ planCode }: { planCode: string }) { } function EducationDiscountAppliedOrNot({ groupSize }: { groupSize: string }) { + const { t } = useTranslation() const size = parseInt(groupSize) if (size >= groupSizeForEducationalDiscount) { return (

- + {t('educational_percent_discount_applied', { + percent: educationalPercentDiscount, + })}

) } return (

- + {t('educational_discount_for_groups_of_x_or_more', { + size: groupSizeForEducationalDiscount, + })}

) } @@ -92,44 +82,27 @@ function GroupPrice({ {totalPrice} / {t('year')}
- {queryingGroupPlanToChangeToPrice ? ( - t('loading_prices') - ) : ( - - )} + {queryingGroupPlanToChangeToPrice + ? t('loading_prices') + : t('x_price_per_year', { + price: groupPlanToChangeToPrice?.totalForDisplay, + })}
- + {t('x_price_per_user', { + price: perUserPrice, + })} - {queryingGroupPlanToChangeToPrice ? ( - t('loading_prices') - ) : ( - - )} + })} @@ -215,14 +188,9 @@ export function ChangeToGroupModal() {

{t('customize_your_group_subscription')}

- + {t('save_x_percent_or_more', { + percent: '30', + })}

@@ -304,15 +272,10 @@ export function ChangeToGroupModal() {
- + {t('percent_discount_for_groups', { + percent: educationalPercentDiscount, + size: groupSizeForEducationalDiscount, + })}
@@ -393,12 +356,9 @@ export function ChangeToGroupModal() {
diff --git a/services/web/frontend/js/i18n.js b/services/web/frontend/js/i18n.js index 9928d2879f..c63e9b73c4 100644 --- a/services/web/frontend/js/i18n.js +++ b/services/web/frontend/js/i18n.js @@ -23,6 +23,9 @@ i18n.use(initReactI18next).init({ // translation strings asynchronously, we need to trigger a re-render once // they've loaded bindI18nStore: 'added', + + // Disable automatic conversion of basic markup to React components + transSupportBasicHtmlNodes: false, }, interpolation: { diff --git a/services/web/locales/en.json b/services/web/locales/en.json index eec996e607..1f79333056 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -371,7 +371,7 @@ "delete": "Delete", "delete_account": "Delete Account", "delete_account_confirmation_label": "I understand this will delete all projects in my __appName__ account with email address <0>__userDefaultEmail__", - "delete_account_warning_message_3": "You are about to permanently <0>delete all of your account data, including your projects and settings. Please type your account email address and password in the boxes below to proceed.", + "delete_account_warning_message_3": "You are about to permanently delete all of your account data, including your projects and settings. Please type your account email address and password in the boxes below to proceed.", "delete_acct_no_existing_pw": "Please use the password reset form to set a password before deleting your account", "delete_and_leave": "Delete / Leave", "delete_and_leave_projects": "Delete and Leave Projects",