Skip to content

Commit e94f507

Browse files
authored
Translate the Review & submit dialog (#635)
## Why The Review & submit dialog was still plain English in a translated app. This is the #624 batch of #622. ## What changes - **The diff pane stops reading English to decide what it holds.** Main returned the literal `'No changes.'` and failures were prefixed `'Error'`, and the dialog compared against both. Main now returns an empty patch, a failed load sets `patchLoadFailed` with the raw error, and the pane says "No changes." or "Error: %s" in the contributor's language. - **Wrapped:** the dialog, its diff pane, the Trac and mentor destinations, and the sentences they take from `changes-note.cjs` (`patchReviewContext`, `discardDisabledReason`), `pr-checkout.cjs` (`prSubmissionRefusal`, `prSubmissionBlocked`), `wporg-handle.cjs` and `patch-provenance.cjs`. - **Rewritten, not just wrapped:** "N days old" uses `_n`; "Save, then open #N" and "Save patch as %s" use `sprintf`; the heading is one sentence per kind (ticket, issue); `<strong>Save</strong>`, the event name and the applied patch's name go through `createInterpolateElement`. User text (an event name, a patch's file name) goes in as an element, never into the string, so a `<` in it stays text. - **An English fix on the way:** with no name on record, the refusal used to read "Revert The patch you applied before…". Each sentence now has a version without a name. - `@wordpress/element` is now a direct dependency (a one-line lockfile change; it was already installed through `@wordpress/components`). - Deliberately not here: the pull request card (#625), the Copy button's labels (#630), and the changes note and pull request checkout sentences (#628, #629). ## How to test this Covered by journeys in `tests/e2e/journeys/i18n.spec.js`, all red without this change. From the repository root: `npx playwright test --project=journeys tests/e2e/journeys/i18n.spec.js -g "Review & submit|nothing to send|mentor card"`. They open the dialog in the en-XA pseudo-locale with an old trunk, with no ticket and with one, with nothing to send, with a remembered username and event, over someone else's pull request, and over an applied patch with and without a name, and fail on any visible English. To look at it by hand, on either platform, on the current head: run `npx electron . --lang=en-XA` from the repository root, open a site with an edited file and click the pseudo-localised **Review & submit changes**. **Expected:** every string in the dialog is accented and in brackets except the pull request card, the Copy button, file paths and the diff itself. **What must not have happened:** in English, the dialog reads exactly as before, apart from "Generating patch..." becoming "Generating patch…". A tree with nothing to send still shows "No changes." and offers no destinations, and a diff that fails to load still says so instead of claiming there are no changes (`pr-checkout.spec.js` covers that one). Not covered: the invalid-username error. It is built in main, which has no catalog until #634 lands, so its journey step is left for after that. ## Risks and limitations - Review: 1 [fix here] · 3 [follow-up], all low. The fix is in; the three follow-ups are below. - Saving or copying with nothing to send now writes a 0-byte file and empties the clipboard; before, both carried the text "No changes.". The buttons were already enabled in that state. Follow-up: disable them when there is no diff. ## Related Fixes #624. Part of #622. Pairs with #634 for the two messages that come from main. --- <details> <summary>Design decisions and alternatives considered</summary> The journey's scanner (`unwrapped`) now treats a text node as translated when the element around it, past bold, italic or code, holds one bracketed string from first character to last. A sentence with a link or a bold word in it is several text nodes, and only the whole is bracketed. "[Foo] English [Bar]" still fails, so English spliced between two wrapped strings is still caught. Sentences that sit side by side in one notice each got their own `<span>` so the rule can see where one ends. `'(<discard />)'` is a translatable string of punctuation: some languages write brackets differently. </details> <details> <summary>Review outcome (required, see AGENTS.md)</summary> - **Review:** completed. Reviewer: a separate agent context (Claude Code, read-only) against `.github/instructions/code-review.instructions.md`; head `90ae783` / base `8dbed36`. `npm run lint` clean, `npm test` 1954 pass / 0 fail, journeys 115 pass. Outcome: 1 [fix here] · 3 [follow-up], all 🔵. - Tests [fix here]: the journeys missed the remembered mentor card, the empty diff and the applied-patch warning. **Fixed** in `529b7ae`; reaching them also showed the applied patch's headline shared an element with the next sentence, now its own `<span>`. - Architecture [follow-up]: `loadError` in `patch-diff-pane.jsx` and the applied-patch headline in `review-dialog.jsx` pick a string inside `.jsx`, where the unit suite cannot reach them. **Deferred:** both are two-way choices now covered by journeys. - Behaviour [follow-up]: Save and Copy with nothing to send (above). **Deferred:** the buttons were enabled in that state before this PR. - Tests [follow-up]: the invalid-username step of the issue's journey. **Deferred** until #634 lands. - **Since review:** `90ae783 → 529b7ae` checked; the change is the journeys and the one `<span>`. Lint clean, `npm test` 1954 pass, journeys 119 pass on the new head. </details> <details> <summary>Implementation notes</summary> - `tests/e2e/helpers/git-site.cjs` gains a `trunkDate` option on `makeSite`: the app reads a site's age from its trunk commit, not from its record. - `NOT_A_HANDLE` in `wporg-handle.cjs` became `notAHandle()`, so it is translated when said. </details> <details> <summary>Screenshots or recording</summary> Nothing changes in English. In the pseudo-locale the dialog is bracketed throughout, which the journeys assert. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 9fd167e commit e94f507

16 files changed

Lines changed: 371 additions & 100 deletions

‎package-lock.json‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
"@wordpress/admin-ui": "^2.11.0",
140140
"@wordpress/components": "^41.0.0",
141141
"@wordpress/dataviews": "^19.1.0",
142+
"@wordpress/element": "^8.8.0",
142143
"@wordpress/hooks": "^4.56.0",
143144
"@wordpress/i18n": "^6.29.0",
144145
"@wordpress/icons": "^17.0.0",

‎src/main.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,7 @@ async function createMinimalPatchForDir(dir, baseOid = null) {
10041004
gone && a.endsWith('\n')
10051005
);
10061006
}
1007-
return skippedNotice(binaries, unreadable) + (patch || 'No changes.');
1007+
return skippedNotice(binaries, unreadable) + patch;
10081008
}
10091009

10101010
/**

‎src/renderer/changes-note.cjs‎

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// to nothing is news for the buttons that would give it somewhere to go.
1313
'use strict';
1414

15+
const { __, sprintf } = require('@wordpress/i18n');
16+
1517
// Byte-identical to the confirm the dirty-update modal has always used, so
1618
// the same action reads the same everywhere it can be triggered.
1719
const DISCARD_CONFIRM_MESSAGE = 'Discard all local changes? This cannot be undone.';
@@ -186,29 +188,39 @@ function discardBlocked({ isUpdating, installing, building, devServerActive, dis
186188
* @return {string|null}
187189
*/
188190
function discardDisabledReason({ patchLoading, patchLoadFailed, patchHasChanges, isUpdating, installing, building, devServerActive, discarding } = {}) {
189-
if (discarding) return 'Changes are already being discarded.';
190-
if (patchLoading) return 'Wait for your changes to finish loading.';
191-
if (patchLoadFailed) return 'Changes could not be loaded.';
192-
if (!patchHasChanges) return 'There are no changes to discard.';
193-
if (isUpdating) return 'Wait for the trunk update to finish before discarding changes.';
194-
if (installing) return 'Wait for the installation to finish before discarding changes.';
195-
if (building) return 'Wait for the build to finish before discarding changes.';
196-
if (devServerActive) return 'Stop the dev server before discarding changes.';
191+
if (discarding) return __('Changes are already being discarded.');
192+
if (patchLoading) return __('Wait for your changes to finish loading.');
193+
if (patchLoadFailed) return __('Changes could not be loaded.');
194+
if (!patchHasChanges) return __('There are no changes to discard.');
195+
if (isUpdating) return __('Wait for the trunk update to finish before discarding changes.');
196+
if (installing) return __('Wait for the installation to finish before discarding changes.');
197+
if (building) return __('Wait for the build to finish before discarding changes.');
198+
if (devServerActive) return __('Stop the dev server before discarding changes.');
197199
return null;
198200
}
199201

200-
// The review always names the base used to measure the displayed changes;
201-
// `workItemNoun` as in changesNoteParts.
202+
// The heading over a linked work item's changes, one sentence per kind
203+
// (`workItemNoun` as in changesNoteParts).
204+
function workItemChangesHeading(workItemNoun, number) {
205+
// translators: %d: a GitHub issue number.
206+
if (workItemNoun === 'issue') return sprintf(__('Your changes for issue #%d'), number);
207+
// translators: %d: a Trac ticket number.
208+
return sprintf(__('Your changes for ticket #%d'), number);
209+
}
210+
211+
// The review always names the base used to measure the displayed changes.
202212
function patchReviewContext({ pullRequest, tracTicket, workItemNoun = 'ticket' } = {}) {
203213
if (pullRequest) return {
204-
heading: `Your changes on top of PR #${pullRequest.number}`,
205-
description: 'Edits to this local copy, compared with the original PR commits.',
206-
empty: `There are no changes on top of PR #${pullRequest.number}.`
214+
// translators: %d: a pull request number.
215+
heading: sprintf(__('Your changes on top of PR #%d'), pullRequest.number),
216+
description: __('Edits to this local copy, compared with the original PR commits.'),
217+
// translators: %d: a pull request number.
218+
empty: sprintf(__('There are no changes on top of PR #%d.'), pullRequest.number)
207219
};
208220
return {
209-
heading: tracTicket ? `Your changes for ${workItemNoun} #${tracTicket}` : 'Your changes',
210-
description: 'Everything this site has that its copy of trunk does not.',
211-
empty: 'There is nothing to send yet — this site has no changes against its copy of trunk.'
221+
heading: tracTicket ? workItemChangesHeading(workItemNoun, tracTicket) : __('Your changes'),
222+
description: __('Everything this site has that its copy of trunk does not.'),
223+
empty: __('There is nothing to send yet — this site has no changes against its copy of trunk.')
212224
};
213225
}
214226

‎src/renderer/components/mentor-handoff.jsx‎

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { useState } from 'react';
22
import { Button, TextControl } from '@wordpress/components';
3+
import { createInterpolateElement } from '@wordpress/element';
4+
import { __, sprintf } from '@wordpress/i18n';
35
import { Text } from '@wordpress/ui';
46
import { Destination } from './destination.jsx';
57

@@ -35,12 +37,12 @@ export function MentorHandoff({ wporg, saveDisabled, onSave }) {
3537
try {
3638
const named = await wporg.rememberHandle(handleInput);
3739
if (!named?.ok) {
38-
setHandleError(named?.error || 'Could not save that username.');
40+
setHandleError(named?.error || __('Could not save that username.'));
3941
return;
4042
}
4143
const at = await wporg.rememberEvent(eventInput);
4244
if (!at?.ok) {
43-
setHandleError(at?.error || 'Could not save that event.');
45+
setHandleError(at?.error || __('Could not save that event.'));
4446
return;
4547
}
4648
setHandleInput('');
@@ -53,22 +55,33 @@ export function MentorHandoff({ wporg, saveDisabled, onSave }) {
5355

5456
return (
5557
<Destination
56-
title="Hand it to a mentor"
57-
cost="No accounts at all. The patch carries your WordPress.org username, and the event you are at."
58-
after="Someone else pushes it; the props still land on you."
58+
title={__('Hand it to a mentor')}
59+
cost={__('No accounts at all. The patch carries your WordPress.org username, and the event you are at.')}
60+
after={__('Someone else pushes it; the props still land on you.')}
5961
>
6062
{wporg?.handle && !editingHandle ? (
6163
<>
6264
<Button variant="primary" onClick={onSave} disabled={saveDisabled}>
63-
Save patch as {wporg.handle}
65+
{
66+
// translators: %s: the contributor's WordPress.org username.
67+
sprintf(__('Save patch as %s'), wporg.handle)
68+
}
6469
</Button>
6570
{/*
6671
The event is shown on every save rather than only when
6772
it is set: a remembered WordCamp from last year would
6873
otherwise keep stamping patches with nobody seeing it.
6974
*/}
7075
<Text variant="body-sm" className="muted-label">
71-
{wporg.event ? <>The patch will say it was written at <strong>{wporg.event}</strong>.</> : 'No event on the patch.'}
76+
{wporg.event
77+
? createInterpolateElement(
78+
// translators: <event /> is the name of the event the contributor is at, such as WordCamp Europe 2026.
79+
__('The patch will say it was written at <strong><event /></strong>.'),
80+
// The name goes in as an element, not into the string: it is
81+
// free text, and a `<` in it would be read as markup.
82+
{ strong: <strong />, event: <>{wporg.event}</> }
83+
)
84+
: __('No event on the patch.')}
7285
</Text>
7386
<Text variant="body-sm">
7487
<Button
@@ -79,29 +92,29 @@ export function MentorHandoff({ wporg, saveDisabled, onSave }) {
7992
setHandleError('');
8093
setEditingHandle(true);
8194
}}
82-
>Change these</Button>
95+
>{__('Change these')}</Button>
8396
</Text>
8497
</>
8598
) : (
8699
<>
87100
<Text variant="body-sm" className="muted-label">
88-
Asked once and remembered for every site — these are facts about you, not about this checkout.
101+
{__('Asked once and remembered for every site — these are facts about you, not about this checkout.')}
89102
</Text>
90103
<TextControl
91104
value={handleInput}
92105
onChange={(value) => { setHandleInput(value); setHandleError(''); }}
93106
onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault(); rememberContributor(); } }}
94107
disabled={handleSaving}
95-
placeholder="WordPress.org username, e.g. janedoe"
96-
aria-label="WordPress.org username"
108+
placeholder={__('WordPress.org username, e.g. janedoe')}
109+
aria-label={__('WordPress.org username')}
97110
/>
98111
<TextControl
99112
value={eventInput}
100113
onChange={(value) => { setEventInput(value); setHandleError(''); }}
101114
onKeyDown={(event) => { if (event.key === 'Enter') { event.preventDefault(); rememberContributor(); } }}
102115
disabled={handleSaving}
103-
placeholder="Event, e.g. WordCamp Europe 2026 (optional)"
104-
aria-label="Event this patch was written at"
116+
placeholder={__('Event, e.g. WordCamp Europe 2026 (optional)')}
117+
aria-label={__('Event this patch was written at')}
105118
/>
106119
<Button
107120
variant="secondary"
@@ -113,7 +126,7 @@ export function MentorHandoff({ wporg, saveDisabled, onSave }) {
113126
// Before the first answer it would just be a button
114127
// that does nothing.
115128
disabled={handleSaving || (!handleInput.trim() && !wporg?.handle)}
116-
>Remember this</Button>
129+
>{__('Remember this')}</Button>
117130
{handleError ? <Text variant="body-sm" className="problem-text" role="alert">{handleError}</Text> : null}
118131
</>
119132
)}

‎src/renderer/components/patch-diff-pane.jsx‎

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import { Button, Spinner } from '@wordpress/components';
2+
import { createInterpolateElement } from '@wordpress/element';
3+
import { __, sprintf } from '@wordpress/i18n';
24
import { Stack, Text } from '@wordpress/ui';
35
import { copy as copyIcon, check as checkIcon, download } from '@wordpress/icons';
46
import { DiscardChangesLink } from './discard-changes-link.jsx';
57
import { DiffText } from './diff-text.jsx';
8+
import { hasDiffLines } from '../diff-highlight.cjs';
9+
10+
// What the pane says when the diff could not be generated.
11+
function loadError(error) {
12+
// translators: %s: the error from generating the patch.
13+
return error ? sprintf(__('Error: %s'), error) : __('Failed to generate patch');
14+
}
615

716
// The left column of "Review & submit changes": the contributor's own diff,
817
// with what can be done to it as a whole. Save it to a file, copy it, or throw
@@ -11,7 +20,8 @@ import { DiffText } from './diff-text.jsx';
1120
// It holds no state. The diff, whether it is still being generated, and what
1221
// the last save, copy and discard came to are the caller's, because the same
1322
// patch feeds the destinations beside this pane and the same discard is
14-
// offered from the ticket's note. This is how they look here.
23+
// offered from the ticket's note. This is how they look here. When the diff
24+
// could not be generated, `patchText` is the error.
1525
export function PatchDiffPane({
1626
heading,
1727
description,
@@ -35,13 +45,19 @@ export function PatchDiffPane({
3545
<Stack direction="row" align="baseline" gap="xs" wrap="wrap">
3646
<Text variant="heading-md">{heading}</Text>
3747
<Text variant="body-sm">
38-
{'('}
39-
<DiscardChangesLink
40-
label="Discard all changes"
41-
onClick={onDiscard}
42-
reason={discardReason}
43-
/>
44-
{')'}
48+
{createInterpolateElement(
49+
// translators: <discard /> is the "Discard all changes" link, beside the heading.
50+
__('(<discard />)'),
51+
{
52+
discard: (
53+
<DiscardChangesLink
54+
label={__('Discard all changes')}
55+
onClick={onDiscard}
56+
reason={discardReason}
57+
/>
58+
)
59+
}
60+
)}
4561
</Text>
4662
</Stack>
4763
<Text variant="body-sm" className="muted-label">{description}</Text>
@@ -54,7 +70,7 @@ export function PatchDiffPane({
5470
pane is a column.
5571
*/}
5672
<Stack direction="row" gap="sm">
57-
<Button variant="secondary" icon={download} onClick={onSave} disabled={patchLoading || patchLoadFailed}>Save</Button>
73+
<Button variant="secondary" icon={download} onClick={onSave} disabled={patchLoading || patchLoadFailed}>{__('Save')}</Button>
5874
<Button
5975
variant="secondary"
6076
icon={copied ? checkIcon : copyIcon}
@@ -75,22 +91,34 @@ export function PatchDiffPane({
7591
show.
7692
*/}
7793
{patchSaved ? (
78-
<Text variant="body-md" className="success-text">Saved to {patchSaved}</Text>
94+
<Text variant="body-md" className="success-text">{
95+
// translators: %s: the path of the saved patch file.
96+
sprintf(__('Saved to %s'), patchSaved)
97+
}</Text>
7998
) : null}
8099
{patchSaveError ? (
81-
<Text variant="body-md" className="problem-text" role="alert">Could not save the patch: {patchSaveError}</Text>
100+
<Text variant="body-md" className="problem-text" role="alert">{
101+
// translators: %s: why the patch could not be saved.
102+
sprintf(__('Could not save the patch: %s'), patchSaveError)
103+
}</Text>
82104
) : null}
83105
<div className="patch-diff-body">
84106
{patchLoading ? (
85107
<Stack direction="column" align="center" justify="center" gap="lg" className="patch-diff-loading">
86108
<Spinner />
87-
<Text variant="body-lg" className="muted-label">Generating patch...</Text>
109+
<Text variant="body-lg" className="muted-label">{__('Generating patch…')}</Text>
88110
</Stack>
89111
) : (
90112
// Its height, and why its padding is counted in it, are in
91113
// shell.css under `.patch-diff-code`.
92114
<pre className="patch-diff-code">
93-
{patchText && patchText.trim().length ? <DiffText text={patchText} /> : 'No changes.'}
115+
{patchLoadFailed ? loadError(patchText) : (
116+
<>
117+
{patchText && patchText.trim().length ? <DiffText text={patchText} /> : null}
118+
{/* Under any `#` lines naming files the diff could not carry (#85). */}
119+
{hasDiffLines(patchText) ? null : __('No changes.')}
120+
</>
121+
)}
94122
</pre>
95123
)}
96124
</div>

‎src/renderer/components/review-dialog.jsx‎

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { Modal } from '@wordpress/components';
2+
import { createInterpolateElement } from '@wordpress/element';
3+
import { __, _n, sprintf } from '@wordpress/i18n';
24
import { Notice, Stack, Text } from '@wordpress/ui';
35
import { prSubmissionRefusal } from '../pr-checkout.cjs';
46

@@ -15,17 +17,40 @@ function OwnershipWarning({ pullRequest, appliedPatch, appliedPatchLabel }) {
1517
return (
1618
<Notice.Root intent="warning" role="alert" spokenMessage={SILENT}>
1719
<Notice.Description>
18-
{prSubmissionRefusal(pullRequest.number)} You can still use <strong>Save</strong> to keep an unattributed copy of your edits.
20+
{prSubmissionRefusal(pullRequest.number)}{' '}
21+
<span>
22+
{createInterpolateElement(
23+
// translators: <strong>Save</strong> is the name of the Save button.
24+
__('You can still use <strong>Save</strong> to keep an unattributed copy of your edits.'),
25+
{ strong: <strong /> }
26+
)}
27+
</span>
1928
</Notice.Description>
2029
</Notice.Root>
2130
);
2231
}
2332
if (appliedPatch) {
33+
// The label is a file's name or a pull request's, and goes in as an
34+
// element rather than into the string: a `<` in a file name would
35+
// otherwise be read as markup.
36+
const headline = appliedPatchLabel
37+
? createInterpolateElement(
38+
// translators: <label /> is the name of an applied patch, such as a file name.
39+
__('<strong><label /> is part of this checkout.</strong>'),
40+
{ strong: <strong />, label: <>{appliedPatchLabel}</> }
41+
)
42+
: createInterpolateElement(__('<strong>The patch you applied is part of this checkout.</strong>'), { strong: <strong /> });
2443
return (
2544
<Notice.Root intent="warning" role="alert" spokenMessage={SILENT}>
2645
<Notice.Description>
27-
<strong>{appliedPatchLabel} is part of this checkout.</strong>{' '}
28-
The app cannot safely separate its author’s changes from edits made afterward, so this combined patch cannot be submitted as your work. You can still use <strong>Save</strong> to keep an unattributed copy; revert the applied patch before submitting.
46+
<span>{headline}</span>{' '}
47+
<span>
48+
{createInterpolateElement(
49+
// translators: <strong>Save</strong> is the name of the Save button.
50+
__('The app cannot safely separate its author’s changes from edits made afterward, so this combined patch cannot be submitted as your work. You can still use <strong>Save</strong> to keep an unattributed copy; revert the applied patch before submitting.'),
51+
{ strong: <strong /> }
52+
)}
53+
</span>
2954
</Notice.Description>
3055
</Notice.Root>
3156
);
@@ -64,7 +89,7 @@ export function ReviewDialog({
6489
}) {
6590
return (
6691
<Modal
67-
title="Review & submit changes"
92+
title={__('Review & submit changes')}
6893
onRequestClose={onClose}
6994
shouldCloseOnClickOutside
7095
isFullScreen
@@ -73,14 +98,22 @@ export function ReviewDialog({
7398
{!loading && age.stale && (
7499
<Notice.Root intent="warning" spokenMessage={SILENT}>
75100
<Notice.Description>
76-
This site&apos;s WordPress code is {age.ageDays} days old — this patch may not apply on Trac. Consider updating to the latest trunk first.
101+
{sprintf(
102+
// translators: %d: how many days old the site's copy of WordPress is.
103+
_n(
104+
"This site's WordPress code is %d day old — this patch may not apply on Trac. Consider updating to the latest trunk first.",
105+
"This site's WordPress code is %d days old — this patch may not apply on Trac. Consider updating to the latest trunk first.",
106+
age.ageDays
107+
),
108+
age.ageDays
109+
)}
77110
</Notice.Description>
78111
</Notice.Root>
79112
)}
80113
{!loading && loadFailed ? (
81114
<Notice.Root intent="error" role="alert" spokenMessage={SILENT}>
82115
<Notice.Description>
83-
Could not load your changes. Close this panel and try again. The error is shown below.
116+
{__('Could not load your changes. Close this panel and try again. The error is shown below.')}
84117
</Notice.Description>
85118
</Notice.Root>
86119
) : null}
@@ -123,8 +156,8 @@ export function ReviewDialog({
123156
{!loading && hasChanges && (
124157
<div className="patch-destinations">
125158
<div>
126-
<Text variant="heading-md" render={<div />}>Where this patch goes</Text>
127-
<Text variant="body-sm" className="muted-label" render={<div />}>The pull request is the one the app sends for you. The others save a file for you to send.</Text>
159+
<Text variant="heading-md" render={<div />}>{__('Where this patch goes')}</Text>
160+
<Text variant="body-sm" className="muted-label" render={<div />}>{__('The pull request is the one the app sends for you. The others save a file for you to send.')}</Text>
128161
</div>
129162

130163
<OwnershipWarning pullRequest={pullRequest} appliedPatch={appliedPatch} appliedPatchLabel={appliedPatchLabel} />

0 commit comments

Comments
 (0)