Merge pull request #26244 from overleaf/td-limit-browser-translate-ide

Prevent browser translation of stuff that shouldn't be translated in IDE page

GitOrigin-RevId: 96a75b51c3c8efc4cbcec7eb17d9e331a03e2c96
This commit is contained in:
Tim Down
2025-06-23 12:25:47 +01:00
committed by Copybot
parent 6780f4cc61
commit fa0928059e
41 changed files with 94 additions and 29 deletions

View File

@@ -1,10 +1,10 @@
//- Title
if metadata && metadata.title
title= metadata.title + ' - ' + settings.appName + ', ' + translate('online_latex_editor')
title(translate='no')= metadata.title + ' - ' + settings.appName + ', ' + translate('online_latex_editor')
meta(name='twitter:title' content=metadata.title)
meta(name='og:title' content=metadata.title)
else if typeof title == 'undefined'
title= settings.appName + ', ' + translate('online_latex_editor')
title(translate='no')= settings.appName + ', ' + translate('online_latex_editor')
meta(
name='twitter:title'
content=settings.appName + ', ' + translate('online_latex_editor')
@@ -14,7 +14,7 @@ else if typeof title == 'undefined'
content=settings.appName + ', ' + translate('online_latex_editor')
)
else
title= translate(title) + ' - ' + settings.appName + ', ' + translate('online_latex_editor')
title(translate='no')= translate(title) + ' - ' + settings.appName + ', ' + translate('online_latex_editor')
//- to do - not translate?
meta(name='twitter:title' content=translate(title))
meta(name='og:title' content=translate(title))

View File

@@ -34,7 +34,7 @@ const MessageContent: FC<{ content: string }> = ({ content }) => {
}, [content, mounted])
return (
<p ref={root}>
<p ref={root} translate="no">
<Linkify>{content}</Linkify>
</p>
)

View File

@@ -29,7 +29,7 @@ function Message({ message, fromSelf }: MessageProps) {
return (
<div className="message-wrapper">
{!fromSelf && (
<div className="name">
<div className="name" translate="no">
<span>{message.user.first_name || message.user.email}</span>
</div>
)}

View File

@@ -68,7 +68,9 @@ export default function DictionaryModalContent({
<ul className="list-unstyled dictionary-entries-list">
{[...learnedWords].sort(wordsSortFunction).map(learnedWord => (
<li key={learnedWord} className="dictionary-entry">
<span className="dictionary-entry-name">{learnedWord}</span>
<span className="dictionary-entry-name" translate="no">
{learnedWord}
</span>
<OLTooltip
id={`tooltip-remove-learned-word-${learnedWord}`}
description={t('edit_dictionary_remove')}

View File

@@ -9,6 +9,7 @@ type Props = {
disabledAccesibilityText?: string
type?: 'button' | 'link'
href?: string
translate?: React.HTMLAttributes<HTMLElement>['translate']
}
function LeftMenuButtonIcon({
@@ -34,12 +35,13 @@ export default function LeftMenuButton({
disabledAccesibilityText,
type = 'button',
href,
translate,
}: PropsWithChildren<Props>) {
if (disabled) {
return (
<div className="left-menu-button link-disabled">
<LeftMenuButtonIcon svgIcon={svgIcon} icon={icon} />
<span>{children}</span>
<span translate={translate}>{children}</span>
{disabledAccesibilityText ? (
<span className="sr-only">{disabledAccesibilityText}</span>
) : null}
@@ -51,7 +53,7 @@ export default function LeftMenuButton({
return (
<button onClick={onClick} className="left-menu-button">
<LeftMenuButtonIcon svgIcon={svgIcon} icon={icon} />
<span>{children}</span>
<span translate={translate}>{children}</span>
</button>
)
} else {
@@ -63,7 +65,7 @@ export default function LeftMenuButton({
className="left-menu-button"
>
<LeftMenuButtonIcon svgIcon={svgIcon} icon={icon} />
<span>{children}</span>
<span translate={translate}>{children}</span>
</a>
)
}

View File

@@ -34,6 +34,7 @@ export default function SettingsCompiler() {
]}
label={t('compiler')}
name="compiler"
translateOptions="no"
/>
)
}

View File

@@ -43,6 +43,7 @@ export default function SettingsDocument() {
options={validDocsOptions}
label={t('main_document')}
name="rootDocId"
translateOptions="no"
/>
)
}

View File

@@ -40,6 +40,7 @@ export default function SettingsEditorTheme() {
options={options}
label={t('editor_theme')}
name="editorTheme"
translateOptions="no"
/>
)
}

View File

@@ -29,6 +29,7 @@ export default function SettingsFontFamily() {
]}
label={t('font_family')}
name="fontFamily"
translateOptions="no"
/>
<BetaBadge
phase="release"

View File

@@ -37,6 +37,7 @@ export default function SettingsImageName() {
options={options}
label={t('tex_live_version')}
name="imageName"
translateOptions="no"
/>
)
}

View File

@@ -28,6 +28,7 @@ type SettingsMenuSelectProps<T extends PossibleValue = string> = {
onChange: (val: T) => void
value?: T
disabled?: boolean
translateOptions?: 'yes' | 'no'
}
export default function SettingsMenuSelect<T extends PossibleValue = string>({
@@ -39,6 +40,7 @@ export default function SettingsMenuSelect<T extends PossibleValue = string>({
onChange,
value,
disabled = false,
translateOptions,
}: SettingsMenuSelectProps<T>) {
const handleChange: ChangeEventHandler<HTMLSelectElement> = useCallback(
event => {
@@ -95,6 +97,7 @@ export default function SettingsMenuSelect<T extends PossibleValue = string>({
value={value?.toString()}
disabled={disabled}
ref={selectRef}
translate={translateOptions}
>
{options.map(option => (
<option

View File

@@ -23,6 +23,7 @@ export default function SettingsPdfViewer() {
]}
label={t('pdf_viewer')}
name="pdfViewer"
translateOptions="no"
/>
)
}

View File

@@ -60,6 +60,7 @@ function OnlineUsersWidget({
id="online-user"
description={user.name}
overlayProps={{ placement: 'bottom', trigger: ['hover', 'focus'] }}
tooltipProps={{ translate: 'no' }}
>
<span>
{/* OverlayTrigger won't fire unless UserIcon is wrapped in a span */}

View File

@@ -28,6 +28,7 @@ function FileTreeDoc({
{...selectableEntityProps}
aria-label={name}
tabIndex={0}
translate="no"
>
<FileTreeItemInner
id={id}

View File

@@ -67,6 +67,7 @@ function FileTreeFolder({
className={classNames(selectableEntityProps.className, {
'dnd-droppable-hover': isOverRoot || isOverList,
})}
translate="no"
>
<FileTreeItemInner
id={id}

View File

@@ -23,6 +23,7 @@ function Changes({ pathnames, projectOps }: ChangesProps) {
<div
className="history-version-change-doc"
data-testid="history-version-change-doc"
translate="no"
>
{pathname}
</div>
@@ -41,6 +42,7 @@ function Changes({ pathnames, projectOps }: ChangesProps) {
<div
className="history-version-change-doc"
data-testid="history-version-change-doc"
translate="no"
>
{getProjectOpDoc(op)}
</div>

View File

@@ -87,6 +87,7 @@ const ChangeTag = forwardRef<HTMLElement, TagProps>(
className="history-version-badge"
data-testid="history-version-badge"
{...props}
translate={isPseudoCurrentStateLabel ? 'yes' : 'no'}
>
{isPseudoCurrentStateLabel
? t('history_label_project_current_state')
@@ -147,24 +148,32 @@ function TagTooltip({ label, currentUserId, showTooltip }: LabelBadgesProps) {
const isPseudoCurrentStateLabel = isPseudoLabel(label)
const currentLabelData = allLabels?.find(({ id }) => id === label.id)
const labelOwnerName =
currentLabelData && !isPseudoLabel(currentLabelData)
? currentLabelData.user_display_name
: t('anonymous')
const isAnonymous = !currentLabelData || isPseudoLabel(currentLabelData)
const labelOwnerName = isAnonymous
? t('anonymous')
: currentLabelData.user_display_name
const labelOwnerNameComponent = isAnonymous ? (
labelOwnerName
) : (
<span translate="no">{labelOwnerName}</span>
)
return !isPseudoCurrentStateLabel ? (
<OLTooltip
description={
<div className="history-version-label-tooltip">
<div className="history-version-label-tooltip-row">
<b className="history-version-label-tooltip-row-comment">
<b
className="history-version-label-tooltip-row-comment"
translate="no"
>
<OLTagIcon />
&nbsp;
{label.comment}
</b>
</div>
<div className="history-version-label-tooltip-row">
{t('history_label_created_by')} {labelOwnerName}
{t('history_label_created_by')} {labelOwnerNameComponent}
</div>
<div className="history-version-label-tooltip-row">
<time>

View File

@@ -14,6 +14,7 @@ function UserNameWithColoredBadge({
currentUserId,
}: UserNameWithColoredBadgeProps) {
const { t } = useTranslation()
let allowBrowserTranslate = true
let userName: string
if (!user) {
@@ -22,8 +23,10 @@ function UserNameWithColoredBadge({
userName = t('you')
} else if ('displayName' in user) {
userName = user.displayName
allowBrowserTranslate = false
} else {
userName = formatUserName(user)
allowBrowserTranslate = false
}
return (
@@ -32,7 +35,12 @@ function UserNameWithColoredBadge({
className="history-version-user-badge-color"
style={{ backgroundColor: getBackgroundColorForUserId(user?.id) }}
/>
<span className="history-version-user-badge-text">{userName}</span>
<span
className="history-version-user-badge-text"
translate={allowBrowserTranslate ? 'yes' : 'no'}
>
{userName}
</span>
</>
)
}

View File

@@ -42,6 +42,7 @@ function HistoryFileTreeDoc({
aria-selected={selected}
aria-label={name}
tabIndex={0}
translate="no"
>
<HistoryFileTreeItem
name={name}

View File

@@ -79,6 +79,7 @@ function HistoryFileTreeFolder({
setExpanded(!expanded)
}
}}
translate="no"
>
<HistoryFileTreeItem name={name} icons={icons} />
</li>

View File

@@ -83,7 +83,7 @@ export default function Breadcrumbs() {
const numOutlineItems = outlineHierarchy.length
return (
<div className="ol-cm-breadcrumbs">
<div className="ol-cm-breadcrumbs" translate="no">
{folderHierarchy.map(folder => (
<Fragment key={folder._id}>
<div>{folder.name}</div>

View File

@@ -41,6 +41,7 @@ export default function EditorThemeSetting() {
options={options}
onChange={setEditorTheme}
value={editorTheme}
translateOptions="no"
/>
)
}

View File

@@ -27,6 +27,7 @@ export default function FontFamilySetting() {
onChange={setFontFamily}
value={fontFamily}
width="wide"
translateOptions="no"
/>
)
}

View File

@@ -38,6 +38,7 @@ export default function CompilerSetting() {
options={OPTIONS}
onChange={setCompiler}
value={compiler}
translateOptions="no"
/>
)
}

View File

@@ -38,6 +38,7 @@ export default function ImageNameSetting() {
options={options}
onChange={setImageName}
value={imageName}
translateOptions="no"
/>
)
}

View File

@@ -44,6 +44,7 @@ export default function RootDocumentSetting() {
options={validDocsOptions}
onChange={setRootDocId}
value={rootDocId}
translateOptions="no"
/>
)
}

View File

@@ -31,6 +31,7 @@ type SettingsMenuSelectProps<T extends PossibleValue = string> = {
disabled?: boolean
width?: 'default' | 'wide'
loading?: boolean
translateOptions?: 'yes' | 'no'
}
export default function DropdownSetting<T extends PossibleValue = string>({
@@ -44,6 +45,7 @@ export default function DropdownSetting<T extends PossibleValue = string>({
disabled = false,
width = 'default',
loading = false,
translateOptions,
}: SettingsMenuSelectProps<T>) {
const handleChange: ChangeEventHandler<HTMLSelectElement> = useCallback(
event => {
@@ -78,6 +80,7 @@ export default function DropdownSetting<T extends PossibleValue = string>({
onChange={handleChange}
value={value?.toString()}
disabled={disabled}
translate={translateOptions}
>
{options.map(option => (
<option

View File

@@ -22,6 +22,7 @@ export default function PDFViewerSetting() {
]}
onChange={setPdfViewer}
value={pdfViewer}
translateOptions="no"
/>
)
}

View File

@@ -58,7 +58,9 @@ export const ToolbarProjectTitle = () => {
id="project-title-options"
className="ide-redesign-toolbar-project-dropdown-toggle ide-redesign-toolbar-dropdown-toggle-subdued fw-bold ide-redesign-toolbar-button-subdued"
>
<span className="ide-redesign-toolbar-project-name">{name}</span>
<span className="ide-redesign-toolbar-project-name" translate="no">
{name}
</span>
<MaterialIcon
type="keyboard_arrow_down"
accessibilityLabel={t('project_title_options')}

View File

@@ -62,6 +62,7 @@ const OutlineItem = memo(function OutlineItem({
role="treeitem"
aria-current={isHighlighted}
aria-label={outlineItem.title}
translate="no"
>
<div className="outline-item-row">
{!!outlineItem.children && (

View File

@@ -39,7 +39,7 @@ export default function PdfLogEntryRawContent({
height: expanded || !needsExpander ? 'auto' : collapsedSize,
}}
>
<pre className="log-entry-content-raw" ref={elementRef}>
<pre className="log-entry-content-raw" ref={elementRef} translate="no">
{rawContent.trim()}
</pre>
</div>

View File

@@ -86,7 +86,11 @@ function PreviewLogEntryHeader({
onClick={onSourceLocationClick}
>
<MaterialIcon type="link" />
<span ref={logLocationSpanRef} className="log-entry-header-link-location">
<span
ref={logLocationSpanRef}
className="log-entry-header-link-location"
translate="no"
>
{`\u202A${locationLinkText}\u202C`}
</span>
</OLButton>
@@ -105,7 +109,7 @@ function PreviewLogEntryHeader({
id={locationLinkText}
description={locationLinkText}
overlayProps={{ placement: 'left' }}
tooltipProps={{ className: 'log-location-tooltip' }}
tooltipProps={{ className: 'log-location-tooltip', translate: 'no' }}
>
{locationLink}
</OLTooltip>

View File

@@ -12,7 +12,7 @@ const ReviewPanelEntryUser = ({
const userName = buildName(user)
return (
<div className="review-panel-entry-user">
<div className="review-panel-entry-user" translate="no">
<span
className="review-panel-entry-user-color-badge"
style={{

View File

@@ -11,6 +11,7 @@ export const ExpandableContent = memo<{
newLineCharsLimit?: number
checkNewLines?: boolean
inline?: boolean
translate?: 'yes' | 'no'
}>(function ExpandableContent({
content,
className,
@@ -18,6 +19,7 @@ export const ExpandableContent = memo<{
newLineCharsLimit = 3,
checkNewLines = true,
inline = false,
translate,
}) {
const { t } = useTranslation()
const contentRef = useRef<HTMLDivElement>(null)
@@ -50,6 +52,7 @@ export const ExpandableContent = memo<{
<div
ref={contentRef}
className={classNames('review-panel-expandable-content', className)}
translate={translate}
>
{isExpanded ? content : content.slice(0, limit)}
{isOverflowing && !isExpanded && '...'}

View File

@@ -135,6 +135,7 @@ export const ReviewPanelMessage: FC<{
contentLimit={100}
checkNewLines
content={message.content}
translate="no"
/>
)}

View File

@@ -73,7 +73,10 @@ export const ReviewPanelResolvedThread: FC<{
i18nKey="from_filename"
components={[
// eslint-disable-next-line react/jsx-key
<span className="review-panel-resolved-comment-filename" />,
<span
className="review-panel-resolved-comment-filename"
translate="no"
/>,
]}
values={{ filename: docName }}
shouldUnescape
@@ -121,6 +124,7 @@ export const ReviewPanelResolvedThread: FC<{
className="review-panel-resolved-comment-quoted-text-quote"
content={comment?.op.c}
checkNewLines
translate="no"
/>
</div>

View File

@@ -238,7 +238,7 @@ const toolbarTheme = EditorView.theme({
color: 'inherit',
},
'& .ol-cm-toolbar-menu': {
width: '120px',
minWidth: '120px',
display: 'flex',
flexDirection: 'column',
boxSizing: 'border-box',

View File

@@ -11,6 +11,7 @@ export const CollapsibleFileHeader: FC<{
type="button"
className="collapsible-file-header"
onClick={toggleCollapsed}
translate="no"
>
<MaterialIcon
type={collapsed ? 'keyboard_arrow_right' : 'keyboard_arrow_down'}

View File

@@ -200,7 +200,7 @@ const expectInvitePage = (user, link, callback) => {
tryFollowInviteLink(user, link, (err, response, body) => {
expect(err).not.to.exist
expect(response.statusCode).to.equal(200)
expect(body).to.match(/<title>Project Invite - .*<\/title>/)
expect(body).to.match(/<title[^>]*>Project Invite - .*<\/title>/)
callback()
})
}
@@ -210,7 +210,7 @@ const expectInvalidInvitePage = (user, link, callback) => {
tryFollowInviteLink(user, link, (err, response, body) => {
expect(err).not.to.exist
expect(response.statusCode).to.equal(404)
expect(body).to.match(/<title>Invalid Invite - .*<\/title>/)
expect(body).to.match(/<title[^>]*>Invalid Invite - .*<\/title>/)
callback()
})
}
@@ -237,7 +237,9 @@ const expectLoginPage = (user, callback) => {
tryFollowLoginLink(user, '/login', (err, response, body) => {
expect(err).not.to.exist
expect(response.statusCode).to.equal(200)
expect(body).to.match(/<title>(Login|Log in to Overleaf) - .*<\/title>/)
expect(body).to.match(
/<title[^>]*>(Login|Log in to Overleaf) - .*<\/title>/
)
callback()
})
}

View File

@@ -12,7 +12,7 @@ export default {
restricted: {
html(response, body) {
expect(response.statusCode).to.equal(403)
expect(body).to.match(/<head><title>Restricted/)
expect(body).to.match(/<head><title translate="no">Restricted/)
},
json(response, body) {
expect(response.statusCode).to.equal(403)

View File

@@ -1,4 +1,5 @@
const TITLE_REGEX = /<title>Your projects - .*, Online LaTeX Editor<\/title>/
const TITLE_REGEX =
/<title[^>]*>Your projects - .*, Online LaTeX Editor<\/title>/
async function run({ request, assertHasStatusCode }) {
const response = await request('/project')