Enable ESLint rule that enforces t when preferable to Trans (#15253)

GitOrigin-RevId: d11b3f587b462d400a8d68128dc8be342415bf7d
This commit is contained in:
Alf Eaton 2023-10-19 09:33:26 +01:00 committed by Copybot
parent 0c81bccfca
commit 7755203ff7
29 changed files with 170 additions and 172 deletions

View file

@ -114,7 +114,7 @@
// //
"files": ["**/frontend/js/**/components/**/*.{js,jsx,ts,tsx}", "**/frontend/js/**/hooks/**/*.{js,jsx,ts,tsx}"], "files": ["**/frontend/js/**/components/**/*.{js,jsx,ts,tsx}", "**/frontend/js/**/hooks/**/*.{js,jsx,ts,tsx}"],
"rules": { "rules": {
"@overleaf/no-empty-trans": "off", "@overleaf/no-empty-trans": "error",
"@overleaf/should-unescape-trans": "error", "@overleaf/should-unescape-trans": "error",
// https://astexplorer.net/ // https://astexplorer.net/

View file

@ -92,7 +92,7 @@
"back_to_subscription": "", "back_to_subscription": "",
"back_to_your_projects": "", "back_to_your_projects": "",
"beta_program_already_participating": "", "beta_program_already_participating": "",
"beta_program_benefits": "<0></0>", "beta_program_benefits": "",
"beta_program_not_participating": "", "beta_program_not_participating": "",
"binary_history_error": "", "binary_history_error": "",
"blank_project": "", "blank_project": "",
@ -288,7 +288,7 @@
"dropbox_sync_now_rate_limited": "", "dropbox_sync_now_rate_limited": "",
"dropbox_sync_now_running": "", "dropbox_sync_now_running": "",
"dropbox_sync_out": "", "dropbox_sync_out": "",
"dropbox_sync_troubleshoot": "<0></0>", "dropbox_sync_troubleshoot": "",
"dropbox_synced": "", "dropbox_synced": "",
"dropbox_unlinked_premium_feature": "", "dropbox_unlinked_premium_feature": "",
"duplicate_file": "", "duplicate_file": "",
@ -305,7 +305,7 @@
"edit_tag": "", "edit_tag": "",
"editing": "", "editing": "",
"editing_captions": "", "editing_captions": "",
"editor_and_pdf": "&", "editor_and_pdf": "",
"editor_disconected_click_to_reconnect": "", "editor_disconected_click_to_reconnect": "",
"editor_only_hide_pdf": "", "editor_only_hide_pdf": "",
"editor_theme": "", "editor_theme": "",
@ -567,7 +567,7 @@
"labels_help_you_to_easily_reference_your_figures": "", "labels_help_you_to_easily_reference_your_figures": "",
"labels_help_you_to_reference_your_tables": "", "labels_help_you_to_reference_your_tables": "",
"labs_program_already_participating": "", "labs_program_already_participating": "",
"labs_program_benefits": "<0></0>", "labs_program_benefits": "",
"labs_program_not_participating": "", "labs_program_not_participating": "",
"large_or_high-resolution_images_taking_too_long": "", "large_or_high-resolution_images_taking_too_long": "",
"last_active": "", "last_active": "",
@ -1050,7 +1050,7 @@
"something_went_wrong_loading_pdf_viewer": "", "something_went_wrong_loading_pdf_viewer": "",
"something_went_wrong_processing_the_request": "", "something_went_wrong_processing_the_request": "",
"something_went_wrong_rendering_pdf": "", "something_went_wrong_rendering_pdf": "",
"something_went_wrong_rendering_pdf_expected": "<0></0>", "something_went_wrong_rendering_pdf_expected": "",
"something_went_wrong_server": "", "something_went_wrong_server": "",
"somthing_went_wrong_compiling": "", "somthing_went_wrong_compiling": "",
"sorry_your_table_cant_be_displayed_at_the_moment": "", "sorry_your_table_cant_be_displayed_at_the_moment": "",

View file

@ -171,7 +171,7 @@ function LayoutDropdownButton() {
/> />
} }
icon={<IconSplit />} icon={<IconSplit />}
text={<Trans i18nKey="editor_and_pdf">&</Trans>} text={t('editor_and_pdf')}
/> />
<LayoutMenuItem <LayoutMenuItem

View file

@ -2,7 +2,7 @@ import ControlLabel from 'react-bootstrap/lib/ControlLabel'
import { Alert, FormControl } from 'react-bootstrap' import { Alert, FormControl } from 'react-bootstrap'
import FormGroup from 'react-bootstrap/lib/FormGroup' import FormGroup from 'react-bootstrap/lib/FormGroup'
import { useCallback } from 'react' import { useCallback } from 'react'
import { Trans, useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useFileTreeCreateName } from '../../contexts/file-tree-create-name' import { useFileTreeCreateName } from '../../contexts/file-tree-create-name'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { import {
@ -57,7 +57,7 @@ export default function FileTreeCreateNameInput({
{touchedName && !validName && ( {touchedName && !validName && (
<Alert bsStyle="danger" className="row-spaced-small"> <Alert bsStyle="danger" className="row-spaced-small">
<Trans i18nKey="files_cannot_include_invalid_characters" /> {t('files_cannot_include_invalid_characters')}
</Alert> </Alert>
)} )}
@ -77,6 +77,8 @@ FileTreeCreateNameInput.propTypes = {
} }
function ErrorMessage({ error }) { function ErrorMessage({ error }) {
const { t } = useTranslation()
// if (typeof error === 'string') { // if (typeof error === 'string') {
// return error // return error
// } // }
@ -85,21 +87,21 @@ function ErrorMessage({ error }) {
case DuplicateFilenameError: case DuplicateFilenameError:
return ( return (
<Alert bsStyle="danger" className="row-spaced-small"> <Alert bsStyle="danger" className="row-spaced-small">
<Trans i18nKey="file_already_exists" /> {t('file_already_exists')}
</Alert> </Alert>
) )
case InvalidFilenameError: case InvalidFilenameError:
return ( return (
<Alert bsStyle="danger" className="row-spaced-small"> <Alert bsStyle="danger" className="row-spaced-small">
<Trans i18nKey="files_cannot_include_invalid_characters" /> {t('files_cannot_include_invalid_characters')}
</Alert> </Alert>
) )
case BlockedFilenameError: case BlockedFilenameError:
return ( return (
<Alert bsStyle="danger" className="row-spaced-small"> <Alert bsStyle="danger" className="row-spaced-small">
<Trans i18nKey="blocked_filename" /> {t('blocked_filename')}
</Alert> </Alert>
) )

View file

@ -154,21 +154,15 @@ function CompileTimeoutMessages() {
content={ content={
<div> <div>
<div> <div>
<span> <span>{t('your_project_near_compile_timeout_limit')}</span>
<Trans i18nKey="your_project_near_compile_timeout_limit" />
</span>
</div> </div>
{showNewCompileTimeoutUI === 'active' ? ( {showNewCompileTimeoutUI === 'active' ? (
<> <>
<strong> <strong>{t('upgrade_for_12x_more_compile_time')}</strong>
<Trans i18nKey="upgrade_for_12x_more_compile_time" />
</strong>
{'. '} {'. '}
</> </>
) : ( ) : (
<strong> <strong>{t('upgrade_for_plenty_more_compile_time')}</strong>
<Trans i18nKey="upgrade_for_plenty_more_compile_time" />
</strong>
)} )}
</div> </div>
} }
@ -204,7 +198,7 @@ function CompileTimeoutMessages() {
shouldUnescape shouldUnescape
tOptions={{ interpolation: { escapeValue: true } }} tOptions={{ interpolation: { escapeValue: true } }}
/>{' '} />{' '}
<Trans i18nKey="and_you_can_upgrade_for_plenty_more_compile_time" /> {t('and_you_can_upgrade_for_plenty_more_compile_time')}
</p> </p>
</div> </div>
} }
@ -229,7 +223,9 @@ function CompileTimeoutMessages() {
/> />
</p> </p>
<p className="row-spaced"> <p className="row-spaced">
<Trans i18nKey="tell_the_project_owner_to_upgrade_plan_for_more_compile_time" /> {t(
'tell_the_project_owner_to_upgrade_plan_for_more_compile_time'
)}
</p> </p>
</div> </div>
} }

View file

@ -19,13 +19,17 @@ function PdfPreviewError({ error }) {
headerTitle={t('pdf_rendering_error')} headerTitle={t('pdf_rendering_error')}
formattedContent={ formattedContent={
<> <>
<Trans i18nKey="something_went_wrong_rendering_pdf_expected"> <Trans
i18nKey="something_went_wrong_rendering_pdf_expected"
components={[
// eslint-disable-next-line react/jsx-key
<Button <Button
bsSize="xs" bsSize="xs"
bsStyle="info" bsStyle="info"
onClick={() => startCompile()} onClick={() => startCompile()}
/>,
]}
/> />
</Trans>
<br /> <br />
<br /> <br />
<Trans <Trans

View file

@ -78,10 +78,10 @@ const CompileTimeout = memo(function CompileTimeout({
</> </>
) : ( ) : (
<> <>
<strong> <strong>{t('upgrade_for_12x_more_compile_time')}</strong>{' '}
<Trans i18nKey="upgrade_for_12x_more_compile_time" /> {t(
</strong>{' '} 'plus_additional_collaborators_document_history_track_changes_and_more'
<Trans i18nKey="plus_additional_collaborators_document_history_track_changes_and_more" /> )}
</> </>
)} )}
</p> </p>

View file

@ -154,9 +154,7 @@ export default function INRBanner({ variant, splitTestName }: INRBannerProps) {
}} }}
/> />
</p> </p>
<p> <p>{t('inr_discount_modal_info')}</p>
<Trans i18nKey="inr_discount_modal_info" />
</p>
</Modal.Body> </Modal.Body>
<Modal.Footer> <Modal.Footer>
<Button bsStyle="default" onClick={handleMaybeLater}> <Button bsStyle="default" onClick={handleMaybeLater}>

View file

@ -4,7 +4,7 @@ import * as eventTracking from '../../../../infrastructure/event-tracking'
import getMeta from '../../../../utils/meta' import getMeta from '../../../../utils/meta'
import customLocalStorage from '../../../../infrastructure/local-storage' import customLocalStorage from '../../../../infrastructure/local-storage'
import { useProjectListContext } from '../../context/project-list-context' import { useProjectListContext } from '../../context/project-list-context'
import { Trans, useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
const variants = ['did-you-know', 'on-premise', 'people', 'FOMO'] as const const variants = ['did-you-know', 'on-premise', 'people', 'FOMO'] as const
type GroupsAndEnterpriseBannerVariant = typeof variants[number] type GroupsAndEnterpriseBannerVariant = typeof variants[number]
@ -64,7 +64,7 @@ export default function GroupsAndEnterpriseBanner() {
return ( return (
<Notification bsStyle="info" onDismiss={handleClose}> <Notification bsStyle="info" onDismiss={handleClose}>
<Notification.Body> <Notification.Body>
<span>{getText(groupsAndEnterpriseBannerVariant)}</span> <BannerContent variant={groupsAndEnterpriseBannerVariant} />
</Notification.Body> </Notification.Body>
<Notification.Action> <Notification.Action>
<a <a
@ -85,16 +85,40 @@ function isVariantValid(variant: GroupsAndEnterpriseBannerVariant) {
return variants.includes(variant) return variants.includes(variant)
} }
function getText(variant: GroupsAndEnterpriseBannerVariant) { function BannerContent({
variant,
}: {
variant: GroupsAndEnterpriseBannerVariant
}) {
const { t } = useTranslation()
switch (variant) { switch (variant) {
case 'did-you-know': case 'did-you-know':
return <Trans i18nKey="did_you_know_that_overleaf_offers" /> return <span>{t('did_you_know_that_overleaf_offers')}</span>
case 'on-premise': case 'on-premise':
return 'Overleaf On-Premises: Does your company want to keep its data within its firewall? Overleaf offers Server Pro, an on-premises solution for companies. Get in touch to learn more.' return (
<span>
Overleaf On-Premises: Does your company want to keep its data within
its firewall? Overleaf offers Server Pro, an on-premises solution for
companies. Get in touch to learn more.
</span>
)
case 'people': case 'people':
return 'Other people at your company may already be using Overleaf. Save money with Overleaf group and company-wide subscriptions. Request more information.' return (
<span>
Other people at your company may already be using Overleaf. Save money
with Overleaf group and company-wide subscriptions. Request more
information.
</span>
)
case 'FOMO': case 'FOMO':
return 'Why do Fortune 500 companies and top research institutions trust Overleaf to streamline their collaboration? Get in touch to learn more.' return (
<span>
Why do Fortune 500 companies and top research institutions trust
Overleaf to streamline their collaboration? Get in touch to learn
more.
</span>
)
} }
} }

View file

@ -1,4 +1,4 @@
import { useTranslation, Trans } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
import { useUserContext } from '../../../shared/context/user-context' import { useUserContext } from '../../../shared/context/user-context'
function BetaProgramSection() { function BetaProgramSection() {
@ -10,9 +10,8 @@ function BetaProgramSection() {
<h3>{t('sharelatex_beta_program')}</h3> <h3>{t('sharelatex_beta_program')}</h3>
{betaProgram ? null : ( {betaProgram ? null : (
<p className="small"> <p className="small">
<Trans i18nKey="beta_program_benefits"> {/* eslint-disable-next-line react/jsx-key */}
<span /> <Trans i18nKey="beta_program_benefits" components={[<span />]} />
</Trans>
</p> </p>
)} )}
<p className="small"> <p className="small">

View file

@ -1,4 +1,4 @@
import { useTranslation, Trans } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
import { useUserContext } from '../../../shared/context/user-context' import { useUserContext } from '../../../shared/context/user-context'
function LabsProgramSection() { function LabsProgramSection() {
@ -10,9 +10,8 @@ function LabsProgramSection() {
<h3>{t('overleaf_labs')}</h3> <h3>{t('overleaf_labs')}</h3>
{labsProgram ? null : ( {labsProgram ? null : (
<p className="small"> <p className="small">
<Trans i18nKey="labs_program_benefits"> {/* eslint-disable-next-line react/jsx-key */}
<span /> <Trans i18nKey="labs_program_benefits" components={[<span />]} />
</Trans>
</p> </p>
)} )}
<p className="small"> <p className="small">

View file

@ -7,13 +7,13 @@ export default function AddCollaboratorsUpgradeContentDefault() {
return ( return (
<> <>
<p className="text-center"> <p className="text-center">
<Trans i18nKey="need_to_upgrade_for_more_collabs" />. {t('also')}: {t('need_to_upgrade_for_more_collabs')}. {t('also')}:
</p> </p>
<ul className="list-unstyled"> <ul className="list-unstyled">
<li> <li>
<Icon type="check" /> <Icon type="check" />
&nbsp; &nbsp;
<Trans i18nKey="unlimited_projects" /> {t('unlimited_projects')}
</li> </li>
<li> <li>
<Icon type="check" /> <Icon type="check" />
@ -28,22 +28,22 @@ export default function AddCollaboratorsUpgradeContentDefault() {
<li> <li>
<Icon type="check" /> <Icon type="check" />
&nbsp; &nbsp;
<Trans i18nKey="full_doc_history" /> {t('full_doc_history')}
</li> </li>
<li> <li>
<Icon type="check" /> <Icon type="check" />
&nbsp; &nbsp;
<Trans i18nKey="sync_to_dropbox" /> {t('sync_to_dropbox')}
</li> </li>
<li> <li>
<Icon type="check" /> <Icon type="check" />
&nbsp; &nbsp;
<Trans i18nKey="sync_to_github" /> {t('sync_to_github')}
</li> </li>
<li> <li>
<Icon type="check" /> <Icon type="check" />
&nbsp; &nbsp;
<Trans i18nKey="compile_larger_projects" /> {t('compile_larger_projects')}
</li> </li>
</ul> </ul>
</> </>

View file

@ -1,13 +1,15 @@
import Icon from '../../../shared/components/icon' import Icon from '../../../shared/components/icon'
import { Trans } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
export default function AddCollaboratorsUpgradeContentVariant() { export default function AddCollaboratorsUpgradeContentVariant() {
const { t } = useTranslation()
return ( return (
<> <>
<div className="row"> <div className="row">
<div className="col-xs-10 col-xs-offset-1"> <div className="col-xs-10 col-xs-offset-1">
<p className="text-center"> <p className="text-center">
<Trans i18nKey="need_to_upgrade_for_more_collabs_variant" /> {t('need_to_upgrade_for_more_collabs_variant')}
</p> </p>
</div> </div>
</div> </div>
@ -16,7 +18,7 @@ export default function AddCollaboratorsUpgradeContentVariant() {
<li> <li>
<Icon type="check" /> <Icon type="check" />
&nbsp; &nbsp;
<Trans i18nKey="unlimited_projects" /> {t('unlimited_projects')}
</li> </li>
<li> <li>
<Icon type="check" /> <Icon type="check" />
@ -31,24 +33,24 @@ export default function AddCollaboratorsUpgradeContentVariant() {
<li> <li>
<Icon type="check" /> <Icon type="check" />
&nbsp; &nbsp;
<Trans i18nKey="full_doc_history" /> {t('full_doc_history')}
</li> </li>
</ul> </ul>
<ul className="list-unstyled col-xs-5"> <ul className="list-unstyled col-xs-5">
<li> <li>
<Icon type="check" /> <Icon type="check" />
&nbsp; &nbsp;
<Trans i18nKey="sync_to_dropbox" /> {t('sync_to_dropbox')}
</li> </li>
<li> <li>
<Icon type="check" /> <Icon type="check" />
&nbsp; &nbsp;
<Trans i18nKey="sync_to_github" /> {t('sync_to_github')}
</li> </li>
<li> <li>
<Icon type="check" /> <Icon type="check" />
&nbsp; &nbsp;
<Trans i18nKey="compile_larger_projects" /> {t('compile_larger_projects')}
</li> </li>
</ul> </ul>
</div> </div>

View file

@ -1,5 +1,5 @@
import { useState } from 'react' import { useState } from 'react'
import { Trans } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Button } from 'react-bootstrap' import { Button } from 'react-bootstrap'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
@ -12,6 +12,7 @@ import AddCollaboratorsUpgradeContentVariant from './add-collaborators-upgrade-c
import { useSplitTestContext } from '../../../shared/context/split-test-context' import { useSplitTestContext } from '../../../shared/context/split-test-context'
export default function AddCollaboratorsUpgrade() { export default function AddCollaboratorsUpgrade() {
const { t } = useTranslation()
const user = useUserContext({ const user = useUserContext({
allowedFreeTrial: PropTypes.bool, allowedFreeTrial: PropTypes.bool,
}) })
@ -45,14 +46,12 @@ export default function AddCollaboratorsUpgrade() {
setStartedFreeTrial(true) setStartedFreeTrial(true)
}} }}
> >
<Trans i18nKey="upgrade" /> {t('upgrade')}
</Button> </Button>
)} )}
</p> </p>
{startedFreeTrial && ( {startedFreeTrial && (
<p className="small"> <p className="small">{t('refresh_page_after_starting_free_trial')}</p>
<Trans i18nKey="refresh_page_after_starting_free_trial" />
</p>
)} )}
</div> </div>
) )

View file

@ -1,5 +1,5 @@
import { useState, useMemo } from 'react' import { useState, useMemo } from 'react'
import { useTranslation, Trans } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Form, FormGroup, FormControl, Button } from 'react-bootstrap' import { Form, FormGroup, FormControl, Button } from 'react-bootstrap'
import { useMultipleSelection } from 'downshift' import { useMultipleSelection } from 'downshift'
import { useShareProjectContext } from './share-project-modal' import { useShareProjectContext } from './share-project-modal'
@ -152,7 +152,7 @@ export default function AddCollaborators() {
</FormControl> </FormControl>
<span>&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;</span>
<Button type="submit" bsStyle="primary"> <Button type="submit" bsStyle="primary">
<Trans i18nKey="share" /> {t('share')}
</Button> </Button>
</div> </div>
</FormGroup> </FormGroup>

View file

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Trans, useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useShareProjectContext } from './share-project-modal' import { useShareProjectContext } from './share-project-modal'
import TransferOwnershipModal from './transfer-ownership-modal' import TransferOwnershipModal from './transfer-ownership-modal'
import { removeMemberFromProject, updateMember } from '../utils/api' import { removeMemberFromProject, updateMember } from '../utils/api'
@ -137,7 +137,7 @@ function RemoveMemberAction({ member }) {
<FormControl.Static className="text-center"> <FormControl.Static className="text-center">
<Tooltip <Tooltip
id="remove-collaborator" id="remove-collaborator"
description={<Trans i18nKey="remove_collaborator" />} description={t('remove_collaborator')}
overlayProps={{ placement: 'bottom' }} overlayProps={{ placement: 'bottom' }}
> >
<Button <Button
@ -163,16 +163,18 @@ RemoveMemberAction.propTypes = {
} }
function ChangePrivilegesActions({ handleReset }) { function ChangePrivilegesActions({ handleReset }) {
const { t } = useTranslation()
return ( return (
<div className="text-center"> <div className="text-center">
<Button type="submit" bsSize="sm" bsStyle="primary"> <Button type="submit" bsSize="sm" bsStyle="primary">
<Trans i18nKey="change_or_cancel-change" /> {t('change_or_cancel-change')}
</Button> </Button>
<div className="text-sm"> <div className="text-sm">
<Trans i18nKey="change_or_cancel-or" /> {t('change_or_cancel-or')}
&nbsp; &nbsp;
<Button type="button" className="btn-inline-link" onClick={handleReset}> <Button type="button" className="btn-inline-link" onClick={handleReset}>
<Trans i18nKey="change_or_cancel-cancel" /> {t('change_or_cancel-cancel')}
</Button> </Button>
</div> </div>
</div> </div>

View file

@ -4,20 +4,21 @@ import { useShareProjectContext } from './share-project-modal'
import Icon from '../../../shared/components/icon' import Icon from '../../../shared/components/icon'
import { Button, Col, Row } from 'react-bootstrap' import { Button, Col, Row } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip' import Tooltip from '../../../shared/components/tooltip'
import { Trans, useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import MemberPrivileges from './member-privileges' import MemberPrivileges from './member-privileges'
import { resendInvite, revokeInvite } from '../utils/api' import { resendInvite, revokeInvite } from '../utils/api'
import { useProjectContext } from '../../../shared/context/project-context' import { useProjectContext } from '../../../shared/context/project-context'
import { sendMB } from '../../../infrastructure/event-tracking' import { sendMB } from '../../../infrastructure/event-tracking'
export default function Invite({ invite, isProjectOwner }) { export default function Invite({ invite, isProjectOwner }) {
const { t } = useTranslation()
return ( return (
<Row className="project-invite"> <Row className="project-invite">
<Col xs={7}> <Col xs={7}>
<div>{invite.email}</div> <div>{invite.email}</div>
<div className="small"> <div className="small">
<Trans i18nKey="invite_not_accepted" /> {t('invite_not_accepted')}
.&nbsp; .&nbsp;
{isProjectOwner && <ResendInvite invite={invite} />} {isProjectOwner && <ResendInvite invite={invite} />}
</div> </div>
@ -42,6 +43,7 @@ Invite.propTypes = {
} }
function ResendInvite({ invite }) { function ResendInvite({ invite }) {
const { t } = useTranslation()
const { monitorRequest } = useShareProjectContext() const { monitorRequest } = useShareProjectContext()
const { _id: projectId } = useProjectContext() const { _id: projectId } = useProjectContext()
@ -66,7 +68,7 @@ function ResendInvite({ invite }) {
onClick={handleClick} onClick={handleClick}
// ref={buttonRef} // ref={buttonRef}
> >
<Trans i18nKey="resend" /> {t('resend')}
</Button> </Button>
) )
} }
@ -99,7 +101,7 @@ function RevokeInvite({ invite }) {
return ( return (
<Tooltip <Tooltip
id="revoke-invite" id="revoke-invite"
description={<Trans i18nKey="revoke_invite" />} description={t('revoke_invite')}
overlayProps={{ placement: 'bottom' }} overlayProps={{ placement: 'bottom' }}
> >
<Button <Button

View file

@ -1,7 +1,7 @@
import { useCallback, useState, useEffect } from 'react' import { useCallback, useState, useEffect } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Button, Col, Row } from 'react-bootstrap' import { Button, Col, Row } from 'react-bootstrap'
import { Trans } from 'react-i18next' import { useTranslation } from 'react-i18next'
import Tooltip from '../../../shared/components/tooltip' import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon' import Icon from '../../../shared/components/icon'
import { useShareProjectContext } from './share-project-modal' import { useShareProjectContext } from './share-project-modal'
@ -86,10 +86,11 @@ LinkSharing.propTypes = {
} }
function PrivateSharing({ setAccessLevel, inflight, projectId }) { function PrivateSharing({ setAccessLevel, inflight, projectId }) {
const { t } = useTranslation()
return ( return (
<Row className="public-access-level"> <Row className="public-access-level">
<Col xs={12} className="text-center"> <Col xs={12} className="text-center">
<Trans i18nKey="link_sharing_is_off" /> {t('link_sharing_is_off')}
<span>&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;</span>
<Button <Button
type="button" type="button"
@ -101,7 +102,7 @@ function PrivateSharing({ setAccessLevel, inflight, projectId }) {
}} }}
disabled={inflight} disabled={inflight}
> >
<Trans i18nKey="turn_on_link_sharing" /> {t('turn_on_link_sharing')}
</Button> </Button>
<span>&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;</span>
<LinkSharingInfo /> <LinkSharingInfo />
@ -117,6 +118,7 @@ PrivateSharing.propTypes = {
} }
function TokenBasedSharing({ setAccessLevel, inflight, canAddCollaborators }) { function TokenBasedSharing({ setAccessLevel, inflight, canAddCollaborators }) {
const { t } = useTranslation()
const { _id: projectId } = useProjectContext() const { _id: projectId } = useProjectContext()
const [tokens, setTokens] = useState(null) const [tokens, setTokens] = useState(null)
@ -132,9 +134,7 @@ function TokenBasedSharing({ setAccessLevel, inflight, canAddCollaborators }) {
return ( return (
<Row className="public-access-level"> <Row className="public-access-level">
<Col xs={12} className="text-center"> <Col xs={12} className="text-center">
<strong> <strong>{t('link_sharing_is_on')}</strong>
<Trans i18nKey="link_sharing_is_on" />
</strong>
<span>&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;</span>
<Button <Button
bsStyle="link" bsStyle="link"
@ -142,16 +142,14 @@ function TokenBasedSharing({ setAccessLevel, inflight, canAddCollaborators }) {
onClick={() => setAccessLevel('private')} onClick={() => setAccessLevel('private')}
disabled={inflight} disabled={inflight}
> >
<Trans i18nKey="turn_off_link_sharing" /> {t('turn_off_link_sharing')}
</Button> </Button>
<span>&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;</span>
<LinkSharingInfo /> <LinkSharingInfo />
</Col> </Col>
<Col xs={12} className="access-token-display-area"> <Col xs={12} className="access-token-display-area">
<div className="access-token-wrapper"> <div className="access-token-wrapper">
<strong> <strong>{t('anyone_with_link_can_edit')}</strong>
<Trans i18nKey="anyone_with_link_can_edit" />
</strong>
<AccessToken <AccessToken
token={tokens?.readAndWrite} token={tokens?.readAndWrite}
path="/" path="/"
@ -159,9 +157,7 @@ function TokenBasedSharing({ setAccessLevel, inflight, canAddCollaborators }) {
/> />
</div> </div>
<div className="access-token-wrapper"> <div className="access-token-wrapper">
<strong> <strong>{t('anyone_with_link_can_view')}</strong>
<Trans i18nKey="anyone_with_link_can_view" />
</strong>
<AccessToken <AccessToken
token={tokens?.readOnly} token={tokens?.readOnly}
path="/read/" path="/read/"
@ -180,16 +176,14 @@ TokenBasedSharing.propTypes = {
} }
function LegacySharing({ accessLevel, setAccessLevel, inflight }) { function LegacySharing({ accessLevel, setAccessLevel, inflight }) {
const { t } = useTranslation()
return ( return (
<Row className="public-access-level"> <Row className="public-access-level">
<Col xs={12} className="text-center"> <Col xs={12} className="text-center">
<strong> <strong>
{accessLevel === 'readAndWrite' && ( {accessLevel === 'readAndWrite' && t('this_project_is_public')}
<Trans i18nKey="this_project_is_public" /> {accessLevel === 'readOnly' && t('this_project_is_public_read_only')}
)}
{accessLevel === 'readOnly' && (
<Trans i18nKey="this_project_is_public_read_only" />
)}
</strong> </strong>
<span>&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;</span>
<Button <Button
@ -199,7 +193,7 @@ function LegacySharing({ accessLevel, setAccessLevel, inflight }) {
onClick={() => setAccessLevel('private')} onClick={() => setAccessLevel('private')}
disabled={inflight} disabled={inflight}
> >
<Trans i18nKey="make_private" /> {t('make_private')}
</Button> </Button>
<span>&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;</span>
<LinkSharingInfo /> <LinkSharingInfo />
@ -215,6 +209,7 @@ LegacySharing.propTypes = {
} }
export function ReadOnlyTokenLink() { export function ReadOnlyTokenLink() {
const { t } = useTranslation()
const { _id: projectId } = useProjectContext() const { _id: projectId } = useProjectContext()
const [tokens, setTokens] = useState(null) const [tokens, setTokens] = useState(null)
@ -231,9 +226,7 @@ export function ReadOnlyTokenLink() {
<Row className="public-access-level"> <Row className="public-access-level">
<Col xs={12} className="access-token-display-area"> <Col xs={12} className="access-token-display-area">
<div className="access-token-wrapper"> <div className="access-token-wrapper">
<strong> <strong>{t('anyone_with_link_can_view')}</strong>
<Trans i18nKey="anyone_with_link_can_view" />
</strong>
<AccessToken <AccessToken
token={tokens?.readOnly} token={tokens?.readOnly}
path="/read/" path="/read/"
@ -246,14 +239,13 @@ export function ReadOnlyTokenLink() {
} }
function AccessToken({ token, path, tooltipId }) { function AccessToken({ token, path, tooltipId }) {
const { t } = useTranslation()
const { isAdmin } = useUserContext() const { isAdmin } = useUserContext()
if (!token) { if (!token) {
return ( return (
<pre className="access-token"> <pre className="access-token">
<span> <span>{t('loading')}</span>
<Trans i18nKey="loading" />
</span>
</pre> </pre>
) )
} }
@ -279,10 +271,12 @@ AccessToken.propTypes = {
} }
function LinkSharingInfo() { function LinkSharingInfo() {
const { t } = useTranslation()
return ( return (
<Tooltip <Tooltip
id="link-sharing-info" id="link-sharing-info"
description={<Trans i18nKey="learn_more_about_link_sharing" />} description={t('learn_more_about_link_sharing')}
> >
<a <a
href="/learn/how-to/What_is_Link_Sharing%3F" href="/learn/how-to/What_is_Link_Sharing%3F"

View file

@ -1,13 +1,15 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Trans } from 'react-i18next' import { useTranslation } from 'react-i18next'
export default function MemberPrivileges({ privileges }) { export default function MemberPrivileges({ privileges }) {
const { t } = useTranslation()
switch (privileges) { switch (privileges) {
case 'readAndWrite': case 'readAndWrite':
return <Trans i18nKey="can_edit" /> return t('can_edit')
case 'readOnly': case 'readOnly':
return <Trans i18nKey="read_only" /> return t('read_only')
default: default:
return null return null

View file

@ -1,15 +1,16 @@
import { useProjectContext } from '../../../shared/context/project-context' import { useProjectContext } from '../../../shared/context/project-context'
import { Col, Row } from 'react-bootstrap' import { Col, Row } from 'react-bootstrap'
import { Trans } from 'react-i18next' import { useTranslation } from 'react-i18next'
export default function OwnerInfo() { export default function OwnerInfo() {
const { t } = useTranslation()
const { owner } = useProjectContext() const { owner } = useProjectContext()
return ( return (
<Row className="project-member"> <Row className="project-member">
<Col xs={7}>{owner?.email}</Col> <Col xs={7}>{owner?.email}</Col>
<Col xs={3} className="text-left"> <Col xs={3} className="text-left">
<Trans i18nKey="owner" /> {t('owner')}
</Col> </Col>
</Row> </Row>
) )

View file

@ -1,6 +1,6 @@
import { useEffect, useMemo, useState, useRef, useCallback } from 'react' import { useEffect, useMemo, useState, useRef, useCallback } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Trans, useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { matchSorter } from 'match-sorter' import { matchSorter } from 'match-sorter'
import { useCombobox } from 'downshift' import { useCombobox } from 'downshift'
import classnames from 'classnames' import classnames from 'classnames'
@ -19,6 +19,7 @@ export default function SelectCollaborators({
placeholder, placeholder,
multipleSelectionProps, multipleSelectionProps,
}) { }) {
const { t } = useTranslation()
const { const {
getSelectedItemProps, getSelectedItemProps,
getDropdownProps, getDropdownProps,
@ -141,7 +142,7 @@ export default function SelectCollaborators({
<div className="tags-input"> <div className="tags-input">
{/* eslint-disable-next-line jsx-a11y/label-has-for */} {/* eslint-disable-next-line jsx-a11y/label-has-for */}
<label className="small" {...getLabelProps()}> <label className="small" {...getLabelProps()}>
<Trans i18nKey="share_with_your_collabs" /> {t('share_with_your_collabs')}
&nbsp; &nbsp;
{loading && <Icon type="refresh" spin />} {loading && <Icon type="refresh" spin />}
</label> </label>

View file

@ -1,6 +1,6 @@
import { Col, Row } from 'react-bootstrap' import { Col, Row } from 'react-bootstrap'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Trans } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useProjectContext } from '../../../shared/context/project-context' import { useProjectContext } from '../../../shared/context/project-context'
export default function SendInvitesNotice() { export default function SendInvitesNotice() {
@ -16,12 +16,13 @@ export default function SendInvitesNotice() {
} }
function AccessLevel({ level }) { function AccessLevel({ level }) {
const { t } = useTranslation()
switch (level) { switch (level) {
case 'private': case 'private':
return <Trans i18nKey="to_add_more_collaborators" /> return t('to_add_more_collaborators')
case 'tokenBased': case 'tokenBased':
return <Trans i18nKey="to_change_access_permissions" /> return t('to_change_access_permissions')
default: default:
return null return null

View file

@ -1,5 +1,5 @@
import { Button, Modal, Grid } from 'react-bootstrap' import { Button, Modal, Grid } from 'react-bootstrap'
import { Trans } from 'react-i18next' import { useTranslation } from 'react-i18next'
import Icon from '../../../shared/components/icon' import Icon from '../../../shared/components/icon'
import AccessibleModal from '../../../shared/components/accessible-modal' import AccessibleModal from '../../../shared/components/accessible-modal'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
@ -23,6 +23,8 @@ export default function ShareProjectModalContent({
inFlight, inFlight,
error, error,
}) { }) {
const { t } = useTranslation()
const { isRestrictedTokenMember } = useEditorContext({ const { isRestrictedTokenMember } = useEditorContext({
isRestrictedTokenMember: PropTypes.bool, isRestrictedTokenMember: PropTypes.bool,
}) })
@ -30,9 +32,7 @@ export default function ShareProjectModalContent({
return ( return (
<AccessibleModal show={show} onHide={cancel} animation={animation}> <AccessibleModal show={show} onHide={cancel} animation={animation}>
<Modal.Header closeButton> <Modal.Header closeButton>
<Modal.Title> <Modal.Title>{t('share_project')}</Modal.Title>
<Trans i18nKey="share_project" />
</Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body className="modal-body-share"> <Modal.Body className="modal-body-share">
@ -65,7 +65,7 @@ export default function ShareProjectModalContent({
className="btn-secondary" className="btn-secondary"
disabled={inFlight} disabled={inFlight}
> >
<Trans i18nKey="close" /> {t('close')}
</Button> </Button>
</div> </div>
</Modal.Footer> </Modal.Footer>
@ -81,24 +81,26 @@ ShareProjectModalContent.propTypes = {
} }
function ErrorMessage({ error }) { function ErrorMessage({ error }) {
const { t } = useTranslation()
switch (error) { switch (error) {
case 'cannot_invite_non_user': case 'cannot_invite_non_user':
return <Trans i18nKey="cannot_invite_non_user" /> return t('cannot_invite_non_user')
case 'cannot_verify_user_not_robot': case 'cannot_verify_user_not_robot':
return <Trans i18nKey="cannot_verify_user_not_robot" /> return t('cannot_verify_user_not_robot')
case 'cannot_invite_self': case 'cannot_invite_self':
return <Trans i18nKey="cannot_invite_self" /> return t('cannot_invite_self')
case 'invalid_email': case 'invalid_email':
return <Trans i18nKey="invalid_email" /> return t('invalid_email')
case 'too_many_requests': case 'too_many_requests':
return <Trans i18nKey="too_many_requests" /> return t('too_many_requests')
default: default:
return <Trans i18nKey="generic_something_went_wrong" /> return t('generic_something_went_wrong')
} }
} }
ErrorMessage.propTypes = { ErrorMessage.propTypes = {

View file

@ -1,6 +1,6 @@
import { useState } from 'react' import { useState } from 'react'
import { Modal, Button } from 'react-bootstrap' import { Modal, Button } from 'react-bootstrap'
import { Trans } from 'react-i18next' import { Trans, useTranslation } from 'react-i18next'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Icon from '../../../shared/components/icon' import Icon from '../../../shared/components/icon'
import { transferProjectOwnership } from '../utils/api' import { transferProjectOwnership } from '../utils/api'
@ -9,6 +9,8 @@ import { useProjectContext } from '../../../shared/context/project-context'
import { useLocation } from '../../../shared/hooks/use-location' import { useLocation } from '../../../shared/hooks/use-location'
export default function TransferOwnershipModal({ member, cancel }) { export default function TransferOwnershipModal({ member, cancel }) {
const { t } = useTranslation()
const [inflight, setInflight] = useState(false) const [inflight, setInflight] = useState(false)
const [error, setError] = useState(false) const [error, setError] = useState(false)
const location = useLocation() const location = useLocation()
@ -32,9 +34,7 @@ export default function TransferOwnershipModal({ member, cancel }) {
return ( return (
<AccessibleModal show onHide={cancel}> <AccessibleModal show onHide={cancel}>
<Modal.Header closeButton> <Modal.Header closeButton>
<Modal.Title> <Modal.Title>{t('change_project_owner')}</Modal.Title>
<Trans i18nKey="change_project_owner" />
</Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body>
<p> <p>
@ -47,16 +47,14 @@ export default function TransferOwnershipModal({ member, cancel }) {
tOptions={{ interpolation: { escapeValue: true } }} tOptions={{ interpolation: { escapeValue: true } }}
/> />
</p> </p>
<p> <p>{t('project_ownership_transfer_confirmation_2')}</p>
<Trans i18nKey="project_ownership_transfer_confirmation_2" />
</p>
</Modal.Body> </Modal.Body>
<Modal.Footer> <Modal.Footer>
<div className="modal-footer-left"> <div className="modal-footer-left">
{inflight && <Icon type="refresh" spin />} {inflight && <Icon type="refresh" spin />}
{error && ( {error && (
<span className="text-danger"> <span className="text-danger">
<Trans i18nKey="generic_something_went_wrong" /> {t('generic_something_went_wrong')}
</span> </span>
)} )}
</div> </div>
@ -68,7 +66,7 @@ export default function TransferOwnershipModal({ member, cancel }) {
onClick={cancel} onClick={cancel}
disabled={inflight} disabled={inflight}
> >
<Trans i18nKey="cancel" /> {t('cancel')}
</Button> </Button>
<Button <Button
type="button" type="button"
@ -76,7 +74,7 @@ export default function TransferOwnershipModal({ member, cancel }) {
onClick={confirm} onClick={confirm}
disabled={inflight} disabled={inflight}
> >
<Trans i18nKey="change_owner" /> {t('change_owner')}
</Button> </Button>
</div> </div>
</Modal.Footer> </Modal.Footer>

View file

@ -9,13 +9,9 @@ export const FigureModalHelp = () => {
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<> <>
<p> <p>{t('this_tool_helps_you_insert_figures')}</p>
<Trans i18nKey="this_tool_helps_you_insert_figures" />
</p>
<b>{t('editing_captions')}</b> <b>{t('editing_captions')}</b>
<p> <p>{t('when_you_tick_the_include_caption_box')}</p>
<Trans i18nKey="when_you_tick_the_include_caption_box" />
</p>
<b>{t('understanding_labels')}</b> <b>{t('understanding_labels')}</b>
<p> <p>

View file

@ -410,7 +410,7 @@
"edit_tag": "Redigér tag", "edit_tag": "Redigér tag",
"editing": "Redigering", "editing": "Redigering",
"editing_captions": "Redigering af billedtekster", "editing_captions": "Redigering af billedtekster",
"editor_and_pdf": "Skrivevindue <0></0> PDF", "editor_and_pdf": "Skrivevindue & PDF",
"editor_disconected_click_to_reconnect": "Skriveprogrammets forbindelse afbrudt, klik hvor som helst for at forbinde igen.", "editor_disconected_click_to_reconnect": "Skriveprogrammets forbindelse afbrudt, klik hvor som helst for at forbinde igen.",
"editor_only_hide_pdf": "Kun skrivevindue <0>(gem PDF)</0>", "editor_only_hide_pdf": "Kun skrivevindue <0>(gem PDF)</0>",
"editor_resources": "Læringsmidler til skriveprogrammet", "editor_resources": "Læringsmidler til skriveprogrammet",

View file

@ -412,7 +412,7 @@
"edit_tag": "Schlagwort bearbeiten", "edit_tag": "Schlagwort bearbeiten",
"editing": "Bearbeitung", "editing": "Bearbeitung",
"editing_captions": "Beschriftungen bearbeiten", "editing_captions": "Beschriftungen bearbeiten",
"editor_and_pdf": "Editor <0></0> PDF", "editor_and_pdf": "Editor & PDF",
"editor_disconected_click_to_reconnect": "Editor wurde getrennt", "editor_disconected_click_to_reconnect": "Editor wurde getrennt",
"editor_only_hide_pdf": "Nur Editor <0>(PDF ausblenden)</0>", "editor_only_hide_pdf": "Nur Editor <0>(PDF ausblenden)</0>",
"editor_resources": "Editor-Literatur", "editor_resources": "Editor-Literatur",

View file

@ -460,7 +460,7 @@
"edit_tag": "Edit Tag", "edit_tag": "Edit Tag",
"editing": "Editing", "editing": "Editing",
"editing_captions": "Editing captions", "editing_captions": "Editing captions",
"editor_and_pdf": "Editor <0></0> PDF", "editor_and_pdf": "Editor & PDF",
"editor_disconected_click_to_reconnect": "Editor disconnected, click anywhere to reconnect.", "editor_disconected_click_to_reconnect": "Editor disconnected, click anywhere to reconnect.",
"editor_only_hide_pdf": "Editor only <0>(hide PDF)</0>", "editor_only_hide_pdf": "Editor only <0>(hide PDF)</0>",
"editor_resources": "Editor Resources", "editor_resources": "Editor Resources",

View file

@ -31,30 +31,6 @@ describe('i18n', function () {
}) })
describe('Trans', function () { describe('Trans', function () {
it('translates a plain string', function () {
const Test = () => {
return (
<div>
<Trans i18nKey="accept" />
</div>
)
}
cy.mount(<Test />)
cy.findByText('Accept')
})
it('uses defaultValues', function () {
const Test = () => {
return (
<div>
<Trans i18nKey="welcome_to_sl" />
</div>
)
}
cy.mount(<Test />)
cy.findByText('Welcome to Overleaf!')
})
it('uses values', function () { it('uses values', function () {
const Test = () => { const Test = () => {
return ( return (