Просмотр исходного кода

Disable transSupportBasicHtmlNodes in react-i18next config (#15430)

* Set transSupportBasicHtmlNodes to false
* Update ESLint rule
* Convert Trans to t
* Convert shouldUnescape={true}
* Convert some arrays to objects
* Update translations

GitOrigin-RevId: 64a50318388abcada408f72d949de148129a9f63
Alf Eaton 2 лет назад
Родитель
Сommit
221d16f4e4
21 измененных файлов с 97 добавлено и 176 удалено
  1. 1 1
      services/web/.eslintrc
  2. 4 9
      services/web/frontend/js/features/file-tree/components/file-tree-create/error-message.jsx
  3. 5 9
      services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.jsx
  4. 5 9
      services/web/frontend/js/features/file-tree/components/file-tree-create/redirect-to-login.jsx
  5. 3 2
      services/web/frontend/js/features/pdf-preview/components/compile-time-warning.tsx
  6. 2 2
      services/web/frontend/js/features/pdf-preview/components/compile-timeout-changing-soon.tsx
  7. 2 9
      services/web/frontend/js/features/project-list/components/notifications/groups/group-invitation/group-invitation-cancel-subscription.tsx
  8. 2 9
      services/web/frontend/js/features/project-list/components/notifications/groups/group-invitation/group-invitation-join.tsx
  9. 7 13
      services/web/frontend/js/features/project-list/components/table/cells/last-updated-cell.tsx
  10. 2 4
      services/web/frontend/js/features/settings/components/emails/add-email/sso-linking-info.tsx
  11. 1 1
      services/web/frontend/js/features/settings/components/leave/modal-content.tsx
  12. 5 9
      services/web/frontend/js/features/settings/components/managed-account-alert.tsx
  13. 4 7
      services/web/frontend/js/features/share-project-modal/components/add-collaborators-upgrade-content-default.jsx
  14. 4 7
      services/web/frontend/js/features/share-project-modal/components/add-collaborators-upgrade-content-variant.jsx
  15. 1 2
      services/web/frontend/js/features/share-project-modal/components/transfer-ownership-modal.jsx
  16. 4 2
      services/web/frontend/js/features/source-editor/components/review-panel/editor-widgets/toggle-widget.tsx
  17. 8 4
      services/web/frontend/js/features/source-editor/components/review-panel/toolbar/toggle-menu.tsx
  18. 1 4
      services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/cancel-subscription.tsx
  19. 32 72
      services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/modals/change-to-group-modal.tsx
  20. 3 0
      services/web/frontend/js/i18n.js
  21. 1 1
      services/web/locales/en.json

+ 1 - 1
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/

+ 4 - 9
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 (
           <DangerMessage>
-            <Trans
-              i18nKey="invalid_filename"
-              values={{
-                nameLimit: fileNameLimit,
-              }}
-              shouldUnescape
-              tOptions={{ interpolation: { escapeValue: true } }}
-            />
+            {t('invalid_filename', {
+              nameLimit: fileNameLimit,
+            })}
           </DangerMessage>
         )
 

+ 5 - 9
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 (
-        <Trans
-          i18nKey="maximum_files_uploaded_together"
-          values={{ max: maxNumberOfFiles }}
-          shouldUnescape
-          tOptions={{ interpolation: { escapeValue: true } }}
-        />
-      )
+      return t('maximum_files_uploaded_together', {
+        max: maxNumberOfFiles,
+      })
 
     default:
       return <ErrorMessage error={error} />

+ 5 - 9
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 (
-    <Trans
-      i18nKey="session_expired_redirecting_to_login"
-      values={{ seconds: secondsToRedirect }}
-      shouldUnescape
-      tOptions={{ interpolation: { escapeValue: true } }}
-    />
-  )
+  return t('session_expired_redirecting_to_login', {
+    seconds: secondsToRedirect,
+  })
 }
 
 const projectContextPropTypes = {

+ 3 - 2
services/web/frontend/js/features/pdf-preview/components/compile-time-warning.tsx

@@ -90,8 +90,9 @@ function CompileTimeWarning() {
         <div className="warning-text">
           <Trans
             i18nKey="approaching_compile_timeout_limit_upgrade_for_more_compile_time"
-            // eslint-disable-next-line react/jsx-key
-            components={[<strong style={{ display: 'inline-block' }} />]}
+            components={{
+              strong: <strong style={{ display: 'inline-block' }} />,
+            }}
           />
         </div>
         <div className="upgrade-prompt">

+ 2 - 2
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 */
     <a
       aria-label={t('read_more_about_free_compile_timeouts_servers')}
       href="/blog/changes-to-free-compile-timeouts-and-servers"
@@ -27,7 +27,7 @@ export const CompileTimeoutChangingSoon: FC<{
       target="_blank"
       onClick={sendInfoClickEvent}
     />,
-    /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
+    /* eslint-disable-next-line jsx-a11y/anchor-has-content */
     <a
       aria-label={t('read_more_about_fix_prevent_timeout')}
       href="/learn/how-to/Fixing_and_preventing_compile_timeouts"

+ 2 - 9
services/web/frontend/js/features/project-list/components/notifications/groups/group-invitation/group-invitation-cancel-subscription.tsx

@@ -1,5 +1,5 @@
 import { Button } from 'react-bootstrap'
-import { Trans, useTranslation } from 'react-i18next'
+import { useTranslation } from 'react-i18next'
 import type { Dispatch, SetStateAction } from 'react'
 import Notification from '../../notification'
 import { GroupInvitationStatus } from './hooks/use-group-invitation-notification'
@@ -26,14 +26,7 @@ export default function GroupInvitationCancelIndividualSubscriptionNotification(
   return (
     <Notification bsStyle="info" onDismiss={dismissGroupInviteNotification}>
       <Notification.Body>
-        <Trans
-          i18nKey="invited_to_group_have_individual_subcription"
-          values={{
-            inviterName,
-          }}
-          shouldUnescape
-          tOptions={{ interpolation: { escapeValue: true } }}
-        />
+        {t('invited_to_group_have_individual_subcription', { inviterName })}
       </Notification.Body>
       <Notification.Action className="group-invitation-cancel-subscription-notification-buttons">
         <Button

+ 2 - 9
services/web/frontend/js/features/project-list/components/notifications/groups/group-invitation/group-invitation-join.tsx

@@ -1,5 +1,5 @@
 import { Button } from 'react-bootstrap'
-import { Trans, useTranslation } from 'react-i18next'
+import { useTranslation } from 'react-i18next'
 import Notification from '../../notification'
 import type { NotificationGroupInvitation } from '../../../../../../../../types/project/dashboard/notification'
 
@@ -24,14 +24,7 @@ export default function GroupInvitationNotificationJoin({
   return (
     <Notification bsStyle="info" onDismiss={dismissGroupInviteNotification}>
       <Notification.Body>
-        <Trans
-          i18nKey="invited_to_group"
-          values={{
-            inviterName,
-          }}
-          shouldUnescape
-          tOptions={{ interpolation: { escapeValue: true } }}
-        />
+        {t('invited_to_group', { inviterName })}
       </Notification.Body>
       <Notification.Action>
         <Button

+ 7 - 13
services/web/frontend/js/features/project-list/components/table/cells/last-updated-cell.tsx

@@ -1,4 +1,4 @@
-import { Trans } from 'react-i18next'
+import { useTranslation } from 'react-i18next'
 import { formatDate, fromNowDate } from '../../../../../utils/dates'
 import { Project } from '../../../../../../../types/project/dashboard/api'
 import Tooltip from '../../../../../shared/components/tooltip'
@@ -9,20 +9,14 @@ type LastUpdatedCellProps = {
 }
 
 export default function LastUpdatedCell({ project }: LastUpdatedCellProps) {
-  const displayText = project.lastUpdatedBy ? (
-    <Trans
-      i18nKey="last_updated_date_by_x"
-      values={{
+  const { t } = useTranslation()
+
+  const displayText = project.lastUpdatedBy
+    ? t('last_updated_date_by_x', {
         lastUpdatedDate: fromNowDate(project.lastUpdated),
         person: getUserName(project.lastUpdatedBy),
-      }}
-      // eslint-disable-next-line react/jsx-boolean-value
-      shouldUnescape={true}
-      tOptions={{ interpolation: { escapeValue: true } }}
-    />
-  ) : (
-    fromNowDate(project.lastUpdated)
-  )
+      })
+    : fromNowDate(project.lastUpdated)
 
   const tooltipText = formatDate(project.lastUpdated)
   return (

+ 2 - 4
services/web/frontend/js/features/settings/components/emails/add-email/sso-linking-info.tsx

@@ -34,8 +34,7 @@ function SsoLinkingInfo({ domainInfo, email }: SSOLinkingInfoProps) {
           i18nKey="to_add_email_accounts_need_to_be_linked_2"
           components={[<strong />]} // eslint-disable-line react/jsx-key
           values={{ institutionName: domainInfo.university.name }}
-          // eslint-disable-next-line react/jsx-boolean-value
-          shouldUnescape={true}
+          shouldUnescape
           tOptions={{ interpolation: { escapeValue: true } }}
         />
       </p>
@@ -44,8 +43,7 @@ function SsoLinkingInfo({ domainInfo, email }: SSOLinkingInfoProps) {
           i18nKey="doing_this_will_verify_affiliation_and_allow_log_in_2"
           components={[<strong />]} // eslint-disable-line react/jsx-key
           values={{ institutionName: domainInfo.university.name }}
-          // eslint-disable-next-line react/jsx-boolean-value
-          shouldUnescape={true}
+          shouldUnescape
           tOptions={{ interpolation: { escapeValue: true } }}
         />{' '}
         <a

+ 1 - 1
services/web/frontend/js/features/settings/components/leave/modal-content.tsx

@@ -57,7 +57,7 @@ function LeaveModalContent({
         <p>
           <Trans
             i18nKey="delete_account_warning_message_3"
-            components={[<strong />]} // eslint-disable-line react/jsx-key
+            components={{ strong: <strong /> }}
           />
         </p>
         <LeaveModalContentBlock

+ 5 - 9
services/web/frontend/js/features/settings/components/managed-account-alert.tsx

@@ -1,7 +1,8 @@
-import { Trans } from 'react-i18next'
+import { Trans, useTranslation } from 'react-i18next'
 import getMeta from '../../../utils/meta'
 
 export default function ManagedAccountAlert() {
+  const { t } = useTranslation()
   const isManaged = getMeta('ol-isManagedAccount', false)
   const currentManagedUserAdminEmail: string = getMeta(
     'ol-currentManagedUserAdminEmail',
@@ -20,14 +21,9 @@ export default function ManagedAccountAlert() {
       <div>
         <div>
           <strong>
-            <Trans
-              i18nKey="account_managed_by_group_administrator"
-              values={{
-                admin: currentManagedUserAdminEmail,
-              }}
-              shouldUnescape
-              tOptions={{ interpolation: { escapeValue: true } }}
-            />
+            {t('account_managed_by_group_administrator', {
+              admin: currentManagedUserAdminEmail,
+            })}
           </strong>
         </div>
         <div>

+ 4 - 7
services/web/frontend/js/features/share-project-modal/components/add-collaborators-upgrade-content-default.jsx

@@ -1,5 +1,5 @@
 import Icon from '../../../shared/components/icon'
-import { Trans, useTranslation } from 'react-i18next'
+import { useTranslation } from 'react-i18next'
 
 export default function AddCollaboratorsUpgradeContentDefault() {
   const { t } = useTranslation()
@@ -18,12 +18,9 @@ export default function AddCollaboratorsUpgradeContentDefault() {
         <li>
           <Icon type="check" />
           &nbsp;
-          <Trans
-            i18nKey="collabs_per_proj"
-            values={{ collabcount: 'Multiple' }}
-            shouldUnescape
-            tOptions={{ interpolation: { escapeValue: true } }}
-          />
+          {t('collabs_per_proj', {
+            collabcount: 'Multiple',
+          })}
         </li>
         <li>
           <Icon type="check" />

+ 4 - 7
services/web/frontend/js/features/share-project-modal/components/add-collaborators-upgrade-content-variant.jsx

@@ -1,5 +1,5 @@
 import Icon from '../../../shared/components/icon'
-import { Trans, useTranslation } from 'react-i18next'
+import { useTranslation } from 'react-i18next'
 
 export default function AddCollaboratorsUpgradeContentVariant() {
   const { t } = useTranslation()
@@ -23,12 +23,9 @@ export default function AddCollaboratorsUpgradeContentVariant() {
           <li>
             <Icon type="check" />
             &nbsp;
-            <Trans
-              i18nKey="collabs_per_proj"
-              values={{ collabcount: 'Multiple' }}
-              shouldUnescape
-              tOptions={{ interpolation: { escapeValue: true } }}
-            />
+            {t('collabs_per_proj', {
+              collabcount: 'Multiple',
+            })}
           </li>
           <li>
             <Icon type="check" />

+ 1 - 2
services/web/frontend/js/features/share-project-modal/components/transfer-ownership-modal.jsx

@@ -42,8 +42,7 @@ export default function TransferOwnershipModal({ member, cancel }) {
             i18nKey="project_ownership_transfer_confirmation_1"
             values={{ user: member.email, project: projectName }}
             components={[<strong key="strong-1" />, <strong key="strong-2" />]}
-            // eslint-disable-next-line react/jsx-boolean-value
-            shouldUnescape={true}
+            shouldUnescape
             tOptions={{ interpolation: { escapeValue: true } }}
           />
         </p>

+ 4 - 2
services/web/frontend/js/features/source-editor/components/review-panel/editor-widgets/toggle-widget.tsx

@@ -16,8 +16,10 @@ function ToggleWidget() {
       })}
       onClick={toggleReviewPanel}
     >
-      {/* eslint-disable-next-line react/jsx-key */}
-      <Trans i18nKey="track_changes_is_on" components={[<strong />]} />
+      <Trans
+        i18nKey="track_changes_is_on"
+        components={{ strong: <strong /> }}
+      />
     </button>
   )
 }

+ 8 - 4
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
-            <Trans i18nKey="track_changes_is_on" components={[<strong />]} />
+            <Trans
+              i18nKey="track_changes_is_on"
+              components={{ strong: <strong /> }}
+            />
           ) : (
-            // eslint-disable-next-line react/jsx-key
-            <Trans i18nKey="track_changes_is_off" components={[<strong />]} />
+            <Trans
+              i18nKey="track_changes_is_off"
+              components={{ strong: <strong /> }}
+            />
           )}
           <span
             className={classnames('rp-tc-state-collapse', {

+ 1 - 4
services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/cancel-subscription.tsx

@@ -80,10 +80,7 @@ function NotCancelOption({
             }}
             shouldUnescape
             tOptions={{ interpolation: { escapeValue: true } }}
-            components={[
-              // eslint-disable-next-line react/jsx-key
-              <strong />,
-            ]}
+            components={{ strong: <strong /> }}
           />
         </p>
         <p>

+ 32 - 72
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 (
       <>
-        <Trans
-          i18nKey="collabs_per_proj"
-          values={{
-            collabcount: 10,
-          }}
-          shouldUnescape
-          tOptions={{ interpolation: { escapeValue: true } }}
-        />
+        {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 (
       <p className="applied">
-        <Trans
-          i18nKey="educational_percent_discount_applied"
-          values={{ percent: educationalPercentDiscount }}
-          shouldUnescape
-          tOptions={{ interpolation: { escapeValue: true } }}
-        />
+        {t('educational_percent_discount_applied', {
+          percent: educationalPercentDiscount,
+        })}
       </p>
     )
   }
 
   return (
     <p className="ineligible">
-      <Trans
-        i18nKey="educational_discount_for_groups_of_x_or_more"
-        values={{ size: groupSizeForEducationalDiscount }}
-        shouldUnescape
-        tOptions={{ interpolation: { escapeValue: true } }}
-      />
+      {t('educational_discount_for_groups_of_x_or_more', {
+        size: groupSizeForEducationalDiscount,
+      })}
     </p>
   )
 }
@@ -92,44 +82,27 @@ function GroupPrice({
         {totalPrice} <span className="small">/ {t('year')}</span>
       </span>
       <span className="sr-only">
-        {queryingGroupPlanToChangeToPrice ? (
-          t('loading_prices')
-        ) : (
-          <Trans
-            i18nKey="x_price_per_year"
-            values={{ price: groupPlanToChangeToPrice?.totalForDisplay }}
-            shouldUnescape
-            tOptions={{ interpolation: { escapeValue: true } }}
-          />
-        )}
+        {queryingGroupPlanToChangeToPrice
+          ? t('loading_prices')
+          : t('x_price_per_year', {
+              price: groupPlanToChangeToPrice?.totalForDisplay,
+            })}
       </span>
 
       <br />
 
       <span className="circle-subtext">
         <span aria-hidden>
-          <Trans
-            i18nKey="x_price_per_user"
-            values={{
-              price: perUserPrice,
-            }}
-            shouldUnescape
-            tOptions={{ interpolation: { escapeValue: true } }}
-          />
+          {t('x_price_per_user', {
+            price: perUserPrice,
+          })}
         </span>
         <span className="sr-only">
-          {queryingGroupPlanToChangeToPrice ? (
-            t('loading_prices')
-          ) : (
-            <Trans
-              i18nKey="x_price_per_user"
-              values={{
+          {queryingGroupPlanToChangeToPrice
+            ? t('loading_prices')
+            : t('x_price_per_user', {
                 price: perUserPrice,
-              }}
-              shouldUnescape
-              tOptions={{ interpolation: { escapeValue: true } }}
-            />
-          )}
+              })}
         </span>
       </span>
     </>
@@ -215,14 +188,9 @@ export function ChangeToGroupModal() {
         <div className="modal-title">
           <h2>{t('customize_your_group_subscription')}</h2>
           <h3>
-            <Trans
-              i18nKey="save_x_percent_or_more"
-              values={{
-                percent: '30',
-              }}
-              shouldUnescape
-              tOptions={{ interpolation: { escapeValue: true } }}
-            />
+            {t('save_x_percent_or_more', {
+              percent: '30',
+            })}
           </h3>
         </div>
       </Modal.Header>
@@ -304,15 +272,10 @@ export function ChangeToGroupModal() {
 
                 <div className="form-group">
                   <strong>
-                    <Trans
-                      i18nKey="percent_discount_for_groups"
-                      values={{
-                        percent: educationalPercentDiscount,
-                        size: groupSizeForEducationalDiscount,
-                      }}
-                      shouldUnescape
-                      tOptions={{ interpolation: { escapeValue: true } }}
-                    />
+                    {t('percent_discount_for_groups', {
+                      percent: educationalPercentDiscount,
+                      size: groupSizeForEducationalDiscount,
+                    })}
                   </strong>
                 </div>
 
@@ -393,12 +356,9 @@ export function ChangeToGroupModal() {
           </button>
           <hr className="thin" />
           <button className="btn-inline-link" onClick={handleGetInTouchButton}>
-            <Trans
-              i18nKey="need_more_than_x_licenses"
-              values={{ x: 50 }}
-              shouldUnescape
-              tOptions={{ interpolation: { escapeValue: true } }}
-            />{' '}
+            {t('need_more_than_x_licenses', {
+              x: 50,
+            })}{' '}
             {t('please_get_in_touch')}
           </button>
         </div>

+ 3 - 0
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: {

+ 1 - 1
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__</0>",
-  "delete_account_warning_message_3": "You are about to permanently <0>delete all of your account data</0>, 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 <strong>delete all of your account data</strong>, 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",