mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-12 17:45:49 +08:00
[eslint] Prohibit l10n phrases consisting only of a sinlge placeholder
R=petermarshall@chromium.org Bug: chromium:1180760 Change-Id: I11726e1d58835a3c1acbba85d137fb2fa1cfc522 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2734474 Reviewed-by: Peter Marshall <petermarshall@chromium.org> Commit-Queue: Simon Zünd <szuend@chromium.org>
This commit is contained in:
@@ -12,11 +12,6 @@ import {latestReleaseNote, releaseNoteViewId} from './HelpImpl.js';
|
||||
import type {ReleaseNote} from './HelpImpl.js';
|
||||
|
||||
const UIStrings = {
|
||||
/**
|
||||
*@description Text that only contain a placeholder
|
||||
*@example {100ms (at 200ms)} PH1
|
||||
*/
|
||||
s: '{PH1}',
|
||||
/**
|
||||
*@description Text that is usually a hyperlink to more documentation
|
||||
*/
|
||||
@@ -37,7 +32,7 @@ export class ReleaseNoteView extends UI.Widget.VBox {
|
||||
this.registerRequiredCSS('help/releaseNote.css', {enableLegacyPatching: true});
|
||||
this._releaseNoteElement = this._createReleaseNoteElement(latestReleaseNote());
|
||||
const topSection = this.contentElement.createChild('div', 'release-note-top-section');
|
||||
topSection.textContent = i18nString(UIStrings.s, {PH1: latestReleaseNote().header});
|
||||
topSection.textContent = latestReleaseNote().header;
|
||||
this.contentElement.appendChild(this._releaseNoteElement);
|
||||
}
|
||||
|
||||
@@ -58,7 +53,7 @@ export class ReleaseNoteView extends UI.Widget.VBox {
|
||||
hbox.classList.add('hbox');
|
||||
const container = hbox.createChild('div', 'release-note-container');
|
||||
const contentContainer = container.createChild('ul');
|
||||
UI.ARIAUtils.setAccessibleName(contentContainer, i18nString(UIStrings.s, {PH1: latestReleaseNote().header}));
|
||||
UI.ARIAUtils.setAccessibleName(contentContainer, latestReleaseNote().header);
|
||||
|
||||
let linkNumber = 1;
|
||||
for (const highlight of releaseNote.highlights) {
|
||||
@@ -94,7 +89,7 @@ export class ReleaseNoteView extends UI.Widget.VBox {
|
||||
|
||||
const imageLink = UI.XLink.XLink.create(releaseNote.link, ' ') as HTMLElement;
|
||||
imageLink.classList.add('release-note-image');
|
||||
UI.Tooltip.Tooltip.install(imageLink, i18nString(UIStrings.s, {PH1: latestReleaseNote().header}));
|
||||
UI.Tooltip.Tooltip.install(imageLink, latestReleaseNote().header);
|
||||
|
||||
hbox.appendChild(imageLink);
|
||||
const image = imageLink.createChild('img') as HTMLImageElement;
|
||||
|
||||
@@ -2801,9 +2801,6 @@
|
||||
"help/ReleaseNoteView.ts | learnMore": {
|
||||
"message": "Learn more"
|
||||
},
|
||||
"help/ReleaseNoteView.ts | s": {
|
||||
"message": "{PH1}"
|
||||
},
|
||||
"host/InspectorFrontendHost.ts | devtoolsS": {
|
||||
"message": "DevTools - {PH1}"
|
||||
},
|
||||
@@ -4937,9 +4934,6 @@
|
||||
"network/NetworkLogViewColumns.ts | responseTime": {
|
||||
"message": "Response Time"
|
||||
},
|
||||
"network/NetworkLogViewColumns.ts | s": {
|
||||
"message": "{PH1}"
|
||||
},
|
||||
"network/NetworkLogViewColumns.ts | scheme": {
|
||||
"message": "Scheme"
|
||||
},
|
||||
@@ -6092,9 +6086,6 @@
|
||||
"profiler/HeapSnapshotGridNodes.ts | detachedFromDomTree": {
|
||||
"message": "Detached from DOM tree"
|
||||
},
|
||||
"profiler/HeapSnapshotGridNodes.ts | emptyPlaceholder": {
|
||||
"message": "{PH1}"
|
||||
},
|
||||
"profiler/HeapSnapshotGridNodes.ts | genericStringsTwoPlaceholders": {
|
||||
"message": "{PH1}, {PH2}"
|
||||
},
|
||||
@@ -6443,9 +6434,6 @@
|
||||
"profiler/ProfileSidebarTreeElement.ts | saveWithEllipsis": {
|
||||
"message": "Save…"
|
||||
},
|
||||
"profiler/ProfileSidebarTreeElement.ts | singlePlaceholder": {
|
||||
"message": "{PH1}"
|
||||
},
|
||||
"profiler/ProfilesPanel.ts | cantLoadFileSupportedFile": {
|
||||
"message": "Can’t load file. Supported file extensions: '{PH1}.'"
|
||||
},
|
||||
|
||||
@@ -2801,9 +2801,6 @@
|
||||
"help/ReleaseNoteView.ts | learnMore": {
|
||||
"message": "L̂éâŕn̂ ḿôŕê"
|
||||
},
|
||||
"help/ReleaseNoteView.ts | s": {
|
||||
"message": "{PH1}"
|
||||
},
|
||||
"host/InspectorFrontendHost.ts | devtoolsS": {
|
||||
"message": "D̂év̂T́ôól̂ś - {PH1}"
|
||||
},
|
||||
@@ -4937,9 +4934,6 @@
|
||||
"network/NetworkLogViewColumns.ts | responseTime": {
|
||||
"message": "R̂éŝṕôńŝé T̂ím̂é"
|
||||
},
|
||||
"network/NetworkLogViewColumns.ts | s": {
|
||||
"message": "{PH1}"
|
||||
},
|
||||
"network/NetworkLogViewColumns.ts | scheme": {
|
||||
"message": "Ŝćĥém̂é"
|
||||
},
|
||||
@@ -6092,9 +6086,6 @@
|
||||
"profiler/HeapSnapshotGridNodes.ts | detachedFromDomTree": {
|
||||
"message": "D̂ét̂áĉh́êd́ f̂ŕôḿ D̂ÓM̂ t́r̂éê"
|
||||
},
|
||||
"profiler/HeapSnapshotGridNodes.ts | emptyPlaceholder": {
|
||||
"message": "{PH1}"
|
||||
},
|
||||
"profiler/HeapSnapshotGridNodes.ts | genericStringsTwoPlaceholders": {
|
||||
"message": "{PH1}, {PH2}"
|
||||
},
|
||||
@@ -6443,9 +6434,6 @@
|
||||
"profiler/ProfileSidebarTreeElement.ts | saveWithEllipsis": {
|
||||
"message": "Ŝáv̂é…"
|
||||
},
|
||||
"profiler/ProfileSidebarTreeElement.ts | singlePlaceholder": {
|
||||
"message": "{PH1}"
|
||||
},
|
||||
"profiler/ProfilesPanel.ts | cantLoadFileSupportedFile": {
|
||||
"message": "Ĉán̂’t́ l̂óâd́ f̂íl̂é. Ŝúp̂ṕôŕt̂éd̂ f́îĺê éx̂t́êńŝíôńŝ: '{PH1}.'"
|
||||
},
|
||||
|
||||
@@ -136,11 +136,6 @@ const UIStrings = {
|
||||
*/
|
||||
connectionId: 'Connection ID',
|
||||
/**
|
||||
*@description Text that only contain a placeholder
|
||||
*@example {100ms (at 200ms)} PH1
|
||||
*/
|
||||
s: '{PH1}',
|
||||
/**
|
||||
*@description Text in Network Log View Columns of the Network panel
|
||||
*/
|
||||
remoteAddressSpace: 'Remote Address Space',
|
||||
@@ -1011,9 +1006,7 @@ const _temporaryDefaultColumns = [
|
||||
{
|
||||
id: 'connection',
|
||||
isResponseHeader: true,
|
||||
// until IDs are supported for strings, the placeholder is used to workaround the limitation that
|
||||
// having multiple translations for a string is not supported
|
||||
title: i18nLazyString(UIStrings.s, {PH1: 'Connection'}),
|
||||
title: i18n.i18n.lockedLazyString('Connection'),
|
||||
sortingFunction: NetworkRequestNode.ResponseHeaderStringComparator.bind(null, 'connection'),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -45,11 +45,6 @@ import type {HeapSnapshotProviderProxy, HeapSnapshotProxy} from './HeapSnapshotP
|
||||
import type {DataDisplayDelegate} from './ProfileHeader.js';
|
||||
|
||||
const UIStrings = {
|
||||
/**
|
||||
*@description Text in Heap Snapshot Grid Nodes of a profiler tool
|
||||
*@example {2} PH1
|
||||
*/
|
||||
emptyPlaceholder: '{PH1}',
|
||||
/**
|
||||
*@description Generic text with two placeholders separated by a comma
|
||||
*@example {1 613 680} PH1
|
||||
@@ -211,8 +206,7 @@ export class HeapSnapshotGridNode extends DataGrid.DataGrid.DataGridNode<HeapSna
|
||||
|
||||
_toUIDistance(distance: number): string {
|
||||
const baseSystemDistance = HeapSnapshotModel.HeapSnapshotModel.baseSystemDistance;
|
||||
return distance >= 0 && distance < baseSystemDistance ? i18nString(UIStrings.emptyPlaceholder, {PH1: distance}) :
|
||||
'\u2212';
|
||||
return distance >= 0 && distance < baseSystemDistance ? distance.toString() : '\u2212';
|
||||
}
|
||||
|
||||
allChildren(): HeapSnapshotGridNode[] {
|
||||
|
||||
@@ -21,11 +21,6 @@ const UIStrings = {
|
||||
*/
|
||||
saveWithEllipsis: 'Save…',
|
||||
/**
|
||||
*@description Text that only contain a placeholder
|
||||
*@example {100ms (at 200ms)} PH1
|
||||
*/
|
||||
singlePlaceholder: '{PH1}',
|
||||
/**
|
||||
*@description A context menu item in the Profiles Panel of a profiler tool
|
||||
*/
|
||||
load: 'Load…',
|
||||
@@ -154,8 +149,7 @@ export class ProfileSidebarTreeElement extends UI.TreeOutline.TreeElement {
|
||||
this.listItemElement.append(this._iconElement, this._titlesElement);
|
||||
this.listItemElement.addEventListener('contextmenu', this._handleContextMenuEvent.bind(this), true);
|
||||
|
||||
UI.ARIAUtils.setDescription(
|
||||
this.listItemElement, i18nString(UIStrings.singlePlaceholder, {PH1: this.profile.profileType().name}));
|
||||
UI.ARIAUtils.setDescription(this.listItemElement, this.profile.profileType().name);
|
||||
}
|
||||
|
||||
_handleContextMenuEvent(event: Event): void {
|
||||
|
||||
@@ -1016,7 +1016,7 @@ const UIStrings = {
|
||||
*@description Text that only contain a placeholder
|
||||
*@example {100ms (at 200ms)} PH1
|
||||
*/
|
||||
emptyPlaceholder: '{PH1}',
|
||||
emptyPlaceholder: '{PH1}', // eslint-disable-line rulesdir/l10n_no_locked_only_phrase
|
||||
/**
|
||||
*@description Text in Timeline UIUtils of the Performance panel
|
||||
*/
|
||||
|
||||
@@ -9,12 +9,14 @@ function isModuleScope(context) {
|
||||
}
|
||||
|
||||
const FULLY_LOCKED_PHRASE_REGEX = /^`[^`]*`$/;
|
||||
const SINGLE_PLACEHOLDER_REGEX = /^\{\w+\}$/; // Matches the PH regex in `collect-strings.js`.
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'UIStrings object literals are not allowed to have phrases that are fully locked.',
|
||||
description:
|
||||
'UIStrings object literals are not allowed to have phrases that are fully locked, or consist only of a single placeholder.',
|
||||
category: 'Possible Errors',
|
||||
},
|
||||
schema: [] // no options
|
||||
@@ -44,6 +46,11 @@ module.exports = {
|
||||
node: property.value,
|
||||
message: 'Locking whole phrases is not allowed. Use i18n.i18n.lockedString instead.',
|
||||
});
|
||||
} else if (SINGLE_PLACEHOLDER_REGEX.test(property.value.value)) {
|
||||
context.report({
|
||||
node: property.value,
|
||||
message: 'Single placeholder-only phrases are not allowed. Use i18n.i18n.lockedString instead.',
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,11 +18,21 @@ ruleTester.run('l10n_no_locked_only_phrase', rule, {
|
||||
{
|
||||
code: 'const UIStrings = { foo: \'Some `locked` part\' };',
|
||||
},
|
||||
{
|
||||
code: 'const UIStrings = { foo: \'One {PH} placeholder\' };',
|
||||
},
|
||||
{
|
||||
code: 'const UIStrings = { foo: \'{PH} two {PH} placeholders\' };',
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: 'const UIStrings = { foo: \'`whole phrase is locked`\'};',
|
||||
errors: [{message: 'Locking whole phrases is not allowed. Use i18n.i18n.lockedString instead.'}],
|
||||
},
|
||||
{
|
||||
code: 'const UIStrings = { foo: \'{PH}\'};',
|
||||
errors: [{message: 'Single placeholder-only phrases are not allowed. Use i18n.i18n.lockedString instead.'}],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user