diff --git a/configs/errors.eslintrc.json b/configs/errors.eslintrc.json index ede72d62b6dec..72706b3b22bc2 100644 --- a/configs/errors.eslintrc.json +++ b/configs/errors.eslintrc.json @@ -131,6 +131,7 @@ } } ], + "@theia/localization-check": "error", "@theia/no-src-import": "error", "@theia/runtime-import-check": "error", "@theia/shared-dependencies": "error", diff --git a/dev-packages/private-eslint-plugin/README.md b/dev-packages/private-eslint-plugin/README.md index 09232928c5fbe..cca75688e1af9 100644 --- a/dev-packages/private-eslint-plugin/README.md +++ b/dev-packages/private-eslint-plugin/README.md @@ -17,6 +17,12 @@ The plugin helps identify problems during development through static analysis in ## Rules +### `localization-check`: + +The rule prevents the following localization related issues: +- incorrect usage of the `nls.localizeByDefault` function by using an incorrect default value. +- unnecessary call to `nls.localize` which could be replaced by `nls.localizeByDefault`. + ### `no-src-import`: The rule prevents imports using `/src/` rather than `/lib/` as it causes build failures. diff --git a/dev-packages/private-eslint-plugin/index.js b/dev-packages/private-eslint-plugin/index.js index 487bebf5930d0..0b1692a6777f0 100644 --- a/dev-packages/private-eslint-plugin/index.js +++ b/dev-packages/private-eslint-plugin/index.js @@ -17,6 +17,7 @@ /** @type {{[ruleId: string]: import('eslint').Rule.RuleModule}} */ exports.rules = { + "localization-check": require('./rules/localization-check'), "no-src-import": require('./rules/no-src-import'), "runtime-import-check": require('./rules/runtime-import-check'), "shared-dependencies": require('./rules/shared-dependencies') diff --git a/dev-packages/private-eslint-plugin/package.json b/dev-packages/private-eslint-plugin/package.json index 66ddf05cc1c48..ac2a077bac027 100644 --- a/dev-packages/private-eslint-plugin/package.json +++ b/dev-packages/private-eslint-plugin/package.json @@ -10,6 +10,7 @@ "dependencies": { "@theia/core": "1.22.1", "@theia/ext-scripts": "1.22.1", - "@theia/re-exports": "1.22.1" + "@theia/re-exports": "1.22.1", + "js-levenshtein": "^1.1.6" } } diff --git a/dev-packages/private-eslint-plugin/rules/localization-check.js b/dev-packages/private-eslint-plugin/rules/localization-check.js new file mode 100644 index 0000000000000..6decf1cc95276 --- /dev/null +++ b/dev-packages/private-eslint-plugin/rules/localization-check.js @@ -0,0 +1,109 @@ +// @ts-check +/******************************************************************************** + * Copyright (C) 2021 TypeFox and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +const levenshtein = require('js-levenshtein'); + +const metadata = require('@theia/core/src/common/i18n/nls.metadata.json'); +const messages = new Set(Object.values(metadata.messages) + .reduceRight((prev, curr) => prev.concat(curr), []) + .map(e => e.replace(/&&/g, ''))); + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'problem', + fixable: 'code', + docs: { + description: 'prevent incorrect use of \'nls.localize\'.', + }, + }, + create(context) { + return { + CallExpression(node) { + const callee = node.callee; + if (callee.type === 'Super') { + return; + } + const { value, byDefault, node: localizeNode } = evaluateLocalize(node); + if (value !== undefined) { + if (byDefault && !messages.has(value)) { + let lowestDistance = Number.MAX_VALUE; + let lowestMessage = ''; + for (const message of messages) { + const distance = levenshtein(value, message); + if (distance < lowestDistance) { + lowestDistance = distance; + lowestMessage = message; + } + } + if (lowestMessage) { + context.report({ + node: localizeNode, + message: `'${value}' is not a valid default value. Did you mean '${lowestMessage}'?`, + fix: function (fixer) { + const updatedCall = `'${lowestMessage.replace(/'/g, "\\'")}'`; + return fixer.replaceText(localizeNode, updatedCall); + } + }); + } else { + context.report({ + node: localizeNode, + message: `'${value}' is not a valid default value.` + }); + } + } else if (!byDefault && messages.has(value)) { + context.report({ + node, + message: `'${value}' can be translated using the 'nls.localizeByDefault' function.`, + fix: function (fixer) { + const code = context.getSourceCode(); + const args = node.arguments.slice(1); + const argsCode = args.map(e => code.getText(e)).join(', '); + const updatedCall = `nls.localizeByDefault(${argsCode})`; + return fixer.replaceText(node, updatedCall); + } + }); + } + } + } + }; + function evaluateLocalize(/** @type {import('estree').CallExpression} */ node) { + const callee = node.callee; + if ('object' in callee && 'name' in callee.object && 'property' in callee && 'name' in callee.property && callee.object.name === 'nls') { + if (callee.property.name === 'localize') { + const defaultTextNode = node.arguments[1]; // The default text node is the second argument for `nls.localize` + if (defaultTextNode && defaultTextNode.type === 'Literal' && typeof defaultTextNode.value === 'string') { + return { + value: defaultTextNode.value, + byDefault: false + }; + } + } else if (callee.property.name === 'localizeByDefault') { + const defaultTextNode = node.arguments[0]; // The default text node is the first argument for ``nls.localizeByDefault` + if (defaultTextNode && defaultTextNode.type === 'Literal' && typeof defaultTextNode.value === 'string') { + return { + node: defaultTextNode, + value: defaultTextNode.value, + byDefault: true + }; + } + } + } + return {}; + } + } +}; diff --git a/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree-widget.tsx b/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree-widget.tsx index 93a4f53434039..57cae3cc37a5a 100644 --- a/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree-widget.tsx +++ b/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree-widget.tsx @@ -95,7 +95,7 @@ export class BulkEditTreeWidget extends TreeWidget { if (CompositeTreeNode.is(model.root) && model.root.children.length > 0) { return super.renderTree(model); } - return
{nls.localizeByDefault('No edits have been detected in the workspace so far.')}
; + return
{nls.localizeByDefault('Made no edits')}
; } protected renderCaption(node: TreeNode, props: NodeProps): React.ReactNode { diff --git a/packages/core/src/browser/core-preferences.ts b/packages/core/src/browser/core-preferences.ts index edb18d70405bf..ed3d1760f5e05 100644 --- a/packages/core/src/browser/core-preferences.ts +++ b/packages/core/src/browser/core-preferences.ts @@ -140,14 +140,19 @@ export const corePreferenceSchema: PreferenceSchema = { default: 300, minimum: 0, maximum: 2000, - description: nls.localizeByDefault('Controls the hover feedback delay in milliseconds of the dragging area in between views/editors.') + // nls-todo: Will be available with VSCode API 1.55 + description: nls.localize('theia/core/sashDelay', 'Controls the hover feedback delay in milliseconds of the dragging area in between views/editors.') }, 'workbench.sash.size': { type: 'number', default: 4, minimum: 1, maximum: 20, - description: nls.localizeByDefault('Controls the feedback area size in pixels of the dragging area in between views/editors. Set it to a larger value if needed.') + // nls-todo: Will be available with VSCode API 1.55 + description: nls.localize( + 'theia/core/sashSize', + 'Controls the feedback area size in pixels of the dragging area in between views/editors. Set it to a larger value if needed.' + ) }, } }; diff --git a/packages/core/src/browser/keyboard/browser-keyboard-frontend-contribution.ts b/packages/core/src/browser/keyboard/browser-keyboard-frontend-contribution.ts index ccfd14bf67d1d..07ba75d004a8f 100644 --- a/packages/core/src/browser/keyboard/browser-keyboard-frontend-contribution.ts +++ b/packages/core/src/browser/keyboard/browser-keyboard-frontend-contribution.ts @@ -52,7 +52,7 @@ export class BrowserKeyboardFrontendContribution implements CommandContribution protected async chooseLayout(): Promise { const current = this.layoutProvider.currentLayoutData; const autodetect: QuickPickValue<'autodetect'> = { - label: nls.localizeByDefault('Auto-detect'), + label: nls.localizeByDefault('Auto Detect'), description: this.layoutProvider.currentLayoutSource !== 'user-choice' ? nls.localize('theia/core/keyboard/current', '(current: {0})', current.name) : undefined, detail: nls.localize('theia/core/keyboard/tryDetect', 'Try to detect the keyboard layout from browser information and pressed keys.'), value: 'autodetect' diff --git a/packages/core/src/electron-browser/menu/electron-menu-contribution.ts b/packages/core/src/electron-browser/menu/electron-menu-contribution.ts index b7049fd2275b0..2a4539f305ec2 100644 --- a/packages/core/src/electron-browser/menu/electron-menu-contribution.ts +++ b/packages/core/src/electron-browser/menu/electron-menu-contribution.ts @@ -233,7 +233,7 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme protected async handleRequiredRestart(): Promise { const msgNode = document.createElement('div'); const message = document.createElement('p'); - message.textContent = nls.localizeByDefault('A setting has changed that requires a restart to take effect'); + message.textContent = nls.localizeByDefault('A setting has changed that requires a restart to take effect.'); const detail = document.createElement('p'); detail.textContent = nls.localizeByDefault( 'Press the restart button to restart {0} and enable the setting.', FrontendApplicationConfigProvider.get().applicationName); diff --git a/packages/core/src/electron-browser/window/electron-window-preferences.ts b/packages/core/src/electron-browser/window/electron-window-preferences.ts index ddb742fd3b6df..a975898e1860d 100644 --- a/packages/core/src/electron-browser/window/electron-window-preferences.ts +++ b/packages/core/src/electron-browser/window/electron-window-preferences.ts @@ -38,7 +38,7 @@ export const electronWindowPreferencesSchema: PreferenceSchema = { 'maximum': ZoomLevel.MAX, 'scope': 'application', // eslint-disable-next-line max-len - 'description': nls.localizeByDefault('Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1.0) or below (e.g. -1.0) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.') + 'description': nls.localizeByDefault('Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.') }, 'window.titleBarStyle': { type: 'string', diff --git a/packages/debug/src/node/vscode/vscode-debug-adapter-contribution.ts b/packages/debug/src/node/vscode/vscode-debug-adapter-contribution.ts index 19a08b26558ee..adb7435fd19b6 100644 --- a/packages/debug/src/node/vscode/vscode-debug-adapter-contribution.ts +++ b/packages/debug/src/node/vscode/vscode-debug-adapter-contribution.ts @@ -14,6 +14,8 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +/* eslint-disable @theia/localization-check */ + import * as fs from '@theia/core/shared/fs-extra'; import * as path from 'path'; import { DebugAdapterExecutable, DebugAdapterContribution } from '../debug-model'; diff --git a/packages/editor/src/browser/editor-command.ts b/packages/editor/src/browser/editor-command.ts index faabea14588b0..01497f9e60365 100644 --- a/packages/editor/src/browser/editor-command.ts +++ b/packages/editor/src/browser/editor-command.ts @@ -364,7 +364,7 @@ export class EditorCommandContribution implements CommandContribution { return; } if (editor.document.dirty && isReopenWithEncoding) { - this.messageService.info(nls.localize('theia/editor/reopenDirty', 'The file is dirty. Please save it first before reopening it with another encoding.')); + this.messageService.info(nls.localizeByDefault('The file is dirty. Please save it first before reopening it with another encoding.')); return; } else if (selectedFileEncoding.value) { editor.setEncoding(selectedFileEncoding.value.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); diff --git a/packages/editor/src/browser/editor-preferences.ts b/packages/editor/src/browser/editor-preferences.ts index f80433f0b4f12..2bb9072534be5 100644 --- a/packages/editor/src/browser/editor-preferences.ts +++ b/packages/editor/src/browser/editor-preferences.ts @@ -685,7 +685,7 @@ const codeEditorPreferenceProperties = { 'default': false }, 'editor.highlightActiveIndentGuide': { - 'description': nls.localize('theia/editor/highlightActiveIndentGuide', 'Controls whether the editor should highlight the active indent guide.'), + 'description': nls.localizeByDefault('Controls whether the editor should highlight the active indent guide.'), 'type': 'boolean', 'default': true }, @@ -743,7 +743,7 @@ const codeEditorPreferenceProperties = { 'description': nls.localizeByDefault('Controls the display of line numbers.') }, 'editor.lineNumbersMinChars': { - 'description': nls.localize('theia/editor/lineNumbersMinChars', 'Controls the line height. Use 0 to compute the line height from the font size.'), + 'description': nls.localizeByDefault('Controls the line height. Use 0 to compute the line height from the font size.'), 'type': 'integer', 'default': 5, 'minimum': 1, @@ -900,15 +900,13 @@ const codeEditorPreferenceProperties = { 'editor.peekWidgetDefaultFocus': { 'enumDescriptions': [ nls.localizeByDefault('Focus the tree when opening peek'), - nls.localizeByDefault('Focus the editor when opening peek'), - nls.localizeByDefault('Focus the webview when opening peek') + nls.localizeByDefault('Focus the editor when opening peek') ], 'description': nls.localizeByDefault('Controls whether to focus the inline editor or the tree in the peek widget.'), 'type': 'string', 'enum': [ 'tree', - 'editor', - 'webview' + 'editor' ], 'default': 'tree' }, @@ -1399,7 +1397,7 @@ const codeEditorPreferenceProperties = { 'default': 'off' }, 'editor.tabIndex': { - 'markdownDescription': nls.localize('theia/editor/tabIndex', 'Controls the wrapping column of the editor when `#editor.wordWrap#` is `wordWrapColumn` or `bounded`.'), + 'markdownDescription': nls.localizeByDefault('Controls the wrapping column of the editor when `#editor.wordWrap#` is `wordWrapColumn` or `bounded`.'), 'type': 'integer', 'default': 0, 'minimum': -1, @@ -1407,9 +1405,9 @@ const codeEditorPreferenceProperties = { }, 'editor.unusualLineTerminators': { 'markdownEnumDescriptions': [ - nls.localize('unusualLineTerminators.auto', 'Unusual line terminators are automatically removed.'), - nls.localize('unusualLineTerminators.off', 'Unusual line terminators are ignored.'), - nls.localize('unusualLineTerminators.prompt', 'Unusual line terminators prompt to be removed.') + nls.localizeByDefault('Unusual line terminators are automatically removed.'), + nls.localizeByDefault('Unusual line terminators are ignored.'), + nls.localizeByDefault('Unusual line terminators prompt to be removed.') ], 'description': nls.localizeByDefault('Remove unusual line terminators that might cause problems.'), 'type': 'string', diff --git a/packages/external-terminal/src/electron-browser/external-terminal-preference.ts b/packages/external-terminal/src/electron-browser/external-terminal-preference.ts index ef94da7292974..2937f035d02d1 100644 --- a/packages/external-terminal/src/electron-browser/external-terminal-preference.ts +++ b/packages/external-terminal/src/electron-browser/external-terminal-preference.ts @@ -92,7 +92,7 @@ export async function getExternalTerminalSchema(externalTerminalService: Externa }, 'terminal.external.osxExec': { type: 'string', - description: nls.localizeByDefault('Customizes which terminal to run on macOS.'), + description: nls.localizeByDefault('Customizes which terminal application to run on macOS.'), default: `${isOSX ? hostExec : 'Terminal.app'}` }, 'terminal.external.linuxExec': { diff --git a/packages/file-search/src/browser/quick-file-open.ts b/packages/file-search/src/browser/quick-file-open.ts index 6d934a579dd27..418fa4281ff51 100644 --- a/packages/file-search/src/browser/quick-file-open.ts +++ b/packages/file-search/src/browser/quick-file-open.ts @@ -19,7 +19,7 @@ import { OpenerService, KeybindingRegistry, QuickAccessRegistry, QuickAccessProv import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import URI from '@theia/core/lib/common/uri'; import { FileSearchService, WHITESPACE_QUERY_SEPARATOR } from '../common/file-search-service'; -import { CancellationToken, Command, MAX_SAFE_INTEGER } from '@theia/core/lib/common'; +import { CancellationToken, Command, nls, MAX_SAFE_INTEGER } from '@theia/core/lib/common'; import { LabelProvider } from '@theia/core/lib/browser/label-provider'; import { NavigationLocationService } from '@theia/editor/lib/browser/navigation/navigation-location-service'; import * as fuzzy from '@theia/core/shared/fuzzy'; @@ -328,10 +328,10 @@ export class QuickFileOpenService implements QuickAccessProvider { } private getPlaceHolder(): string { - let placeholder = 'File name to search (append : to go to line).'; + let placeholder = nls.localizeByDefault('Search files by name (append {0} to go to line or {1} to go to symbol)', ':', '@'); const keybinding = this.getKeyCommand(); if (keybinding) { - placeholder += ` (Press ${keybinding} to show/hide ignored files)`; + placeholder += nls.localize('theia/file-search/toggleIgnoredFiles', ' (Press {0} to show/hide ignored files)', keybinding); } return placeholder; } diff --git a/packages/filesystem/src/browser/file-upload-service.ts b/packages/filesystem/src/browser/file-upload-service.ts index 5ec856dc2f42f..bcf0576f72b49 100644 --- a/packages/filesystem/src/browser/file-upload-service.ts +++ b/packages/filesystem/src/browser/file-upload-service.ts @@ -245,9 +245,9 @@ export class FileUploadService { protected async confirmOverwrite(fileUri: URI): Promise { const dialog = new ConfirmDialog({ - title: nls.localizeByDefault('Replace file'), - msg: nls.localizeByDefault('File "{0}" already exists in the destination folder. Do you want to replace it?', fileUri.path.base), - ok: nls.localizeByDefault('Replace file'), + title: nls.localizeByDefault('Replace'), + msg: nls.localizeByDefault("A file or folder with the name '{0}' already exists in the destination folder. Do you want to replace it?", fileUri.path.base), + ok: nls.localizeByDefault('Replace'), cancel: Dialog.CANCEL }); return !!await dialog.open(); diff --git a/packages/getting-started/src/browser/getting-started-widget.tsx b/packages/getting-started/src/browser/getting-started-widget.tsx index b331ee005a9c1..2f3a9c221d447 100644 --- a/packages/getting-started/src/browser/getting-started-widget.tsx +++ b/packages/getting-started/src/browser/getting-started-widget.tsx @@ -245,7 +245,7 @@ export class GettingStartedWidget extends ReactWidget {

{nls.localizeByDefault('Recent')}

- {items.length > 0 ? content :

{nls.localizeByDefault('No Recent Workspaces')}

} + {items.length > 0 ? content :

{nls.localizeByDefault('No recent folders')}

} {more} ; } diff --git a/packages/git/src/browser/git-contribution.ts b/packages/git/src/browser/git-contribution.ts index b508290d14b30..13cb7f5970bd6 100644 --- a/packages/git/src/browser/git-contribution.ts +++ b/packages/git/src/browser/git-contribution.ts @@ -897,7 +897,7 @@ export class GitContribution implements CommandContribution, MenuContribution, T } catch (e) { scmRepository.input.issue = { type: ScmInputIssueType.Warning, - message: nls.localizeByDefault('Make sure you configure your \'user.name\' and \'user.email\' in git.') + message: nls.localize('theia/git/missingUserInfo', 'Make sure you configure your \'user.name\' and \'user.email\' in git.') }; } diff --git a/packages/git/src/browser/git-quick-open-service.ts b/packages/git/src/browser/git-quick-open-service.ts index 11ef547f80e1b..7d5a848ab1366 100644 --- a/packages/git/src/browser/git-quick-open-service.ts +++ b/packages/git/src/browser/git-quick-open-service.ts @@ -22,6 +22,7 @@ import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service import { GitErrorHandler } from './git-error-handler'; import { ProgressService } from '@theia/core/lib/common/progress-service'; import URI from '@theia/core/lib/common/uri'; +import { nls } from '@theia/core/lib/common/nls'; import { LabelProvider, QuickInputService, QuickPick, QuickPickItem } from '@theia/core/lib/browser'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileStat } from '@theia/filesystem/lib/common/files'; @@ -67,9 +68,14 @@ export class GitQuickOpenService { return repo.localUri; } - this.quickInputService?.showQuickPick([new GitQuickPickItem('Please provide a Git repository location. Press \'Enter\' to confirm or \'Escape\' to cancel.')], + this.quickInputService?.showQuickPick( + [ + new GitQuickPickItem( + nls.localize('theia/git/cloneQuickInputLabel', 'Please provide a Git repository location. Press \'Enter\' to confirm or \'Escape\' to cancel.') + ) + ], { - placeholder: 'Git repository location:', + placeholder: nls.localize('vscode.git/dist/commands/selectFolder', 'Select Repository Location'), onDidChangeValue: (quickPick: QuickPick, filter: string) => this.query(quickPick, filter, folder) }); }); @@ -81,22 +87,31 @@ export class GitQuickOpenService { const { git, buildDefaultProjectPath, gitErrorHandler, wrapWithProgress } = this; try { - const suffix = "Press 'Enter' to confirm or 'Escape' to cancel."; - if (filter === undefined || filter.length === 0) { - quickPick.items = [new GitQuickPickItem(`Please provide a Git repository location. ${suffix}`)]; + quickPick.items = [ + new GitQuickPickItem( + nls.localize('theia/git/cloneQuickInputLabel', 'Please provide a Git repository location. Press \'Enter\' to confirm or \'Escape\' to cancel.') + ) + ]; } else { - quickPick.items = [new GitQuickPickItem(`Clone the Git repository: ${filter}. ${suffix}`, - wrapWithProgress(async () => { - try { - await git.clone(filter, { localUri: await buildDefaultProjectPath(folder, filter) }); - } catch (error) { - gitErrorHandler.handleError(error); - } - }))]; + quickPick.items = [ + new GitQuickPickItem( + nls.localize( + 'theia/git/cloneRepository', + 'Clone the Git repository: {0}. Press \'Enter\' to confirm or \'Escape\' to cancel.', + filter + ), + wrapWithProgress(async () => { + try { + await git.clone(filter, { localUri: await buildDefaultProjectPath(folder, filter) }); + } catch (error) { + gitErrorHandler.handleError(error); + } + })) + ]; } } catch (err) { - quickPick.items = [new GitQuickPickItem(`$(error) Error: ${err.message}`)]; + quickPick.items = [new GitQuickPickItem('$(error) ' + nls.localizeByDefault('Error: {0}', err.message))]; console.error(err); } finally { quickPick.busy = false; @@ -132,7 +147,7 @@ export class GitQuickOpenService { } }; const items = remotes.map(remote => new GitQuickPickItem(remote.name, execute, remote, remote.fetch)); - this.quickInputService?.showQuickPick(items, { placeholder: 'Pick a remote to fetch from:' }); + this.quickInputService?.showQuickPick(items, { placeholder: nls.localize('theia/git/fetchPickRemote', 'Pick a remote to fetch from:') }); }); } @@ -174,7 +189,9 @@ export class GitQuickOpenService { }; const items = remotes.map(remote => new GitQuickPickItem(remote.name, execute, remote, remote.push)); const branchName = currentBranch ? `'${currentBranch.name}' ` : ''; - this.quickInputService?.showQuickPick(items, { placeholder: `Pick a remote to push the currently active branch ${branchName}to:` }); + this.quickInputService?.showQuickPick(items, { + placeholder: nls.localize('vscode.git/dist/commands/pick remote', "Pick a remote to publish the branch '{0}' to:", branchName) + }); }); } @@ -209,11 +226,15 @@ export class GitQuickOpenService { .filter(branch => (branch.name || '').startsWith(`${remoteItem.label}/`)) .map(branch => new GitQuickPickItem(branch.name, executeBranch, branch)); - this.quickInputService?.showQuickPick(branchItems, { placeholder: 'Select the branch to pull the changes from:' }); + this.quickInputService?.showQuickPick(branchItems, { + placeholder: nls.localize('vscode.git/dist/commands/pick branch pull', 'Pick a branch to pull from') + }); } }; const remoteItems = remotes.map(remote => new GitQuickPickItem(remote.name, executeRemote, remote, remote.fetch)); - this.quickInputService?.showQuickPick(remoteItems, { placeholder: 'Pick a remote to pull the branch from:' }); + this.quickInputService?.showQuickPick(remoteItems, { + placeholder: nls.localize('vscode.git/dist/commands/pick remote pull repo', 'Pick a remote to pull the branch from') + }); }); } @@ -233,7 +254,12 @@ export class GitQuickOpenService { }; const items = branches.map(branch => new GitQuickPickItem(branch.name, execute, branch)); const branchName = currentBranch ? `'${currentBranch.name}' ` : ''; - this.quickInputService?.showQuickPick(items, { placeholder: `Pick a branch to merge into the currently active ${branchName}branch:` }); + this.quickInputService?.showQuickPick( + items, + { + placeholder: nls.localize('theia/git/mergeQuickPickPlaceholder', 'Pick a branch to merge into the currently active {0} branch:', branchName) + } + ); }); } @@ -260,18 +286,26 @@ export class GitQuickOpenService { const items = branches.map(branch => new GitQuickPickItem( branch.type === BranchType.Remote ? branch.name : branch.nameWithoutRemote, switchBranch, branch, - branch.type === BranchType.Remote ? 'Remote branch at' : '' + `${(branch.tip.sha.length > 8 ? ` ${branch.tip.sha.slice(0, 7)}` : '')}`)); + branch.type === BranchType.Remote + ? nls.localize('vscode.git/dist/commands/remote branch at', 'Remote branch at {0}', (branch.tip.sha.length > 8 ? ` ${branch.tip.sha.slice(0, 7)}` : '')) + : (branch.tip.sha.length > 8 ? ` ${branch.tip.sha.slice(0, 7)}` : ''))); const createBranchItem = async () => { const { git, gitErrorHandler, wrapWithProgress } = this; const getItems = (lookFor?: string) => { - const suffix = "Press 'Enter' to confirm or 'Escape' to cancel."; const dynamicItems: GitQuickPickItem[] = []; if (lookFor === undefined || lookFor.length === 0) { - dynamicItems.push(new GitQuickPickItem(`Please provide a branch name. ${suffix}`, () => { })); + dynamicItems.push(new GitQuickPickItem( + nls.localize('theia/git/checkoutProvideBranchName', 'Please provide a branch name. '), + () => { }) + ); } else { dynamicItems.push(new GitQuickPickItem( - `Create a new local branch with name: ${lookFor}. ${suffix}`, + nls.localize( + 'theia/git/checkoutCreateLocalBranchWithName', + "Create a new local branch with name: {0}. Press 'Enter' to confirm or 'Escape' to cancel.", + lookFor + ), wrapWithProgress(async () => { try { await git.branch(repository, { toCreate: lookFor }); @@ -285,15 +319,15 @@ export class GitQuickOpenService { return dynamicItems; }; this.quickInputService?.showQuickPick(getItems(), { - placeholder: 'The name of the branch:', + placeholder: nls.localize('vscode.git/dist/commands/branch name', 'Branch name'), onDidChangeValue: (quickPick: QuickPick, filter: string) => { quickPick.items = getItems(filter); } }); }; - items.unshift(new GitQuickPickItem('Create new branch...', createBranchItem)); - this.quickInputService?.showQuickPick(items, { placeholder: 'Select a ref to checkout or create a new local branch:' }); + items.unshift(new GitQuickPickItem(nls.localize('vscode.git/dist/commands/create branch', 'Create new branch...'), createBranchItem)); + this.quickInputService?.showQuickPick(items, { placeholder: nls.localize('theia/git/checkoutSelectRef', 'Select a ref to checkout or create a new local branch:') }); }); } @@ -311,29 +345,32 @@ export class GitQuickOpenService { const tagItems = tags.map(tag => new GitQuickPickItem(tag.name, execute, tag)); this.quickInputService?.showQuickPick([...branchItems, ...tagItems], - { placeholder: `Pick a branch or tag to compare with the currently active ${branchName} branch:` }); + { placeholder: nls.localize('theia/git/compareWithBranchOrTag', 'Pick a branch or tag to compare with the currently active {0} branch:', branchName) }); }); } async commitMessageForAmend(): Promise { const repository = this.getRepository(); if (!repository) { - throw new Error('No repositories were selected.'); + throw new Error(nls.localize('theia/git/noRepositoriesSelected', 'No repositories were selected.')); } return this.withProgress(async () => { const lastMessage = (await this.git.exec(repository, ['log', '--format=%B', '-n', '1'])).stdout.trim(); if (lastMessage.length === 0) { - throw new Error(`Repository ${repository.localUri} is not yet initialized.`); + throw new Error(nls.localize('theia/git/repositoryNotInitialized', 'Repository {0} is not yet initialized.', repository.localUri)); } const message = lastMessage.replace(/[\r\n]+/g, ' '); const result = await new Promise(async (resolve, reject) => { const getItems = (lookFor?: string) => { const items = []; if (!lookFor) { - const label = "To reuse the last commit message, press 'Enter' or 'Escape' to cancel."; + const label = nls.localize('theia/git/amendReuseMessag', "To reuse the last commit message, press 'Enter' or 'Escape' to cancel."); items.push(new GitQuickPickItem(label, () => resolve(lastMessage), label)); } else { - items.push(new GitQuickPickItem("Rewrite previous commit message. Press 'Enter' to confirm or 'Escape' to cancel.", () => resolve(lookFor))); + items.push(new GitQuickPickItem( + nls.localize('theia/git/amendRewrite', "Rewrite previous commit message. Press 'Enter' to confirm or 'Escape' to cancel."), + () => resolve(lookFor)) + ); } return items; }; @@ -357,18 +394,22 @@ export class GitQuickOpenService { }); const getItems = (lookFor?: string) => { const items = []; - const suffix = "Press 'Enter' to confirm or 'Escape' to cancel."; if (lookFor === undefined || lookFor.length === 0) { - items.push(new GitQuickPickItem(`Stash changes. ${suffix}`, () => doStash(''))); + items.push(new GitQuickPickItem(nls.localize('theia/git/stashChanges', "Stash changes. Press 'Enter' to confirm or 'Escape' to cancel."), () => doStash(''))); } else { - items.push(new GitQuickPickItem(`Stash changes with message: ${lookFor}. ${suffix}`, () => doStash(lookFor))); + items.push(new GitQuickPickItem( + nls.localize('theia/git/stashChangesWithMessage', "Stash changes with message: {0}. Press 'Enter' to confirm or 'Escape' to cancel.", lookFor), + () => doStash(lookFor)) + ); } return items; }; const updateItems = (quickPick: QuickPick, filter: string) => { quickPick.items = getItems(filter); }; - this.quickInputService?.showQuickPick(getItems(), { placeholder: 'Stash message', onDidChangeValue: updateItems }); + this.quickInputService?.showQuickPick(getItems(), { + placeholder: nls.localize('vscode.git/dist/commands/stash message', 'Stash message'), onDidChangeValue: updateItems + }); }); } @@ -397,11 +438,11 @@ export class GitQuickOpenService { } async applyStash(): Promise { - this.doStashAction('apply', 'Select a stash to \'apply\'.'); + this.doStashAction('apply', nls.localize('vscode.git/dist/commands/pick stash to apply', 'Pick a stash to apply')); } async popStash(): Promise { - this.doStashAction('pop', 'Select a stash to \'pop\'.'); + this.doStashAction('pop', nls.localize('pick stash to pop', 'Pick a stash to pop')); } async dropStash(): Promise { @@ -409,17 +450,11 @@ export class GitQuickOpenService { if (!repository) { return; } - this.doStashAction('drop', 'Select a stash entry to remove it from the list of stash entries.', - async () => { - const list = await this.git.stash(repository, { action: 'list' }); - let listString = ''; - list.forEach(stashEntry => { - listString += stashEntry.message + '\n'; - }); - return `Stash successfully removed. - There ${list.length === 1 ? 'is' : 'are'} ${list.length || 'no'} more entry in stash list. - \n${listString}`; - }); + this.doStashAction( + 'drop', + nls.localize('vscode.git/dist/commands/pick stash to drop', 'Pick a stash to drop'), + async () => nls.localize('theia/git/dropStashMessage', 'Stash successfully removed.') + ); } async applyLatestStash(): Promise { @@ -458,7 +493,7 @@ export class GitQuickOpenService { const wsRoots = await this.workspaceService.roots; if (wsRoots && wsRoots.length > 1) { const items = wsRoots.map>(root => this.toRepositoryPathQuickOpenItem(root)); - this.quickInputService?.showQuickPick(items, { placeholder: 'Choose workspace root to initialize git repo in' }); + this.quickInputService?.showQuickPick(items, { placeholder: nls.localize('vscode.git/dist/commands/init', 'Pick workspace folder to initialize git repo in') }); } else { const rootUri = wsRoots[0].resource; this.doInitRepository(rootUri.toString()); diff --git a/packages/git/src/common/git-model.ts b/packages/git/src/common/git-model.ts index 9cdbb96edf808..d550c79fa4fff 100644 --- a/packages/git/src/common/git-model.ts +++ b/packages/git/src/common/git-model.ts @@ -108,6 +108,7 @@ export namespace GitFileStatus { case GitFileStatus.New: return !!staged ? nls.localize('theia/git/added', 'Added') : nls.localize('theia/git/unstaged', 'Unstaged'); case GitFileStatus.Renamed: return nls.localize('theia/git/renamed', 'Renamed'); case GitFileStatus.Copied: return nls.localize('theia/git/copied', 'Copied'); + // eslint-disable-next-line @theia/localization-check case GitFileStatus.Modified: return nls.localize('vscode.git/repository/modified', 'Modified'); case GitFileStatus.Deleted: return nls.localize('vscode.git/repository/deleted', 'Deleted'); case GitFileStatus.Conflicted: return nls.localize('theia/git/conflicted', 'Conflicted'); diff --git a/packages/outline-view/src/browser/outline-view-widget.tsx b/packages/outline-view/src/browser/outline-view-widget.tsx index b0d7fe09733d7..6fdbf649ad111 100644 --- a/packages/outline-view/src/browser/outline-view-widget.tsx +++ b/packages/outline-view/src/browser/outline-view-widget.tsx @@ -180,7 +180,7 @@ export class OutlineViewWidget extends TreeWidget { protected renderTree(model: TreeModel): React.ReactNode { if (CompositeTreeNode.is(this.model.root) && !this.model.root.children.length) { - return
{nls.localizeByDefault('No outline information available.')}
; + return
{nls.localizeByDefault('The active editor cannot provide outline information.')}
; } return super.renderTree(model); } diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts index 5fb024c96aaa9..03f776ef19a70 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts @@ -14,6 +14,8 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +/* eslint-disable @theia/localization-check */ + import { inject, injectable } from '@theia/core/shared/inversify'; import { AutoClosingPair, diff --git a/packages/plugin-ext/src/main/browser/authentication-main.ts b/packages/plugin-ext/src/main/browser/authentication-main.ts index 3b34515e8a1a8..e4646b70c6a1b 100644 --- a/packages/plugin-ext/src/main/browser/authentication-main.ts +++ b/packages/plugin-ext/src/main/browser/authentication-main.ts @@ -210,7 +210,7 @@ export class AuthenticationMainImpl implements AuthenticationMain { protected async loginPrompt(providerName: string, extensionName: string, recreatingSession: boolean, _detail?: string): Promise { const message = recreatingSession - ? nls.localizeByDefault("The extension '{0}' wants you to sign in again using {1}.", extensionName, providerName) + ? nls.localize('theia/plugin-ext/signInAgain', "The extension '{0}' wants you to sign in again using {1}.", extensionName, providerName) : nls.localizeByDefault("The extension '{0}' wants to sign in using {1}.", extensionName, providerName); const choice = await this.messageService.info(message, 'Allow', 'Cancel'); return choice === 'Allow'; @@ -306,9 +306,9 @@ export class AuthenticationProviderImpl implements AuthenticationProvider { const accountUsages = await readAccountUsages(this.storageService, this.id, accountName); const sessionsForAccount = this.accounts.get(accountName); const result = await this.messageService.info(accountUsages.length - ? nls.localizeByDefault("The account '{0}' has been used by: \n\n{1}\n\n Sign out from these extensions?", accountName, + ? nls.localizeByDefault('The account {0} has been used by: \n\n{1}\n\n Sign out of these features?', accountName, accountUsages.map(usage => usage.extensionName).join(', ')) - : nls.localizeByDefault("Sign out of '{0}'?", accountName), + : nls.localizeByDefault('Sign out of {0}?', accountName), nls.localizeByDefault('Sign Out'), Dialog.CANCEL); diff --git a/packages/preferences/src/browser/preference-transaction-manager.ts b/packages/preferences/src/browser/preference-transaction-manager.ts index 5ca91e802612a..9bf8021bf2c14 100644 --- a/packages/preferences/src/browser/preference-transaction-manager.ts +++ b/packages/preferences/src/browser/preference-transaction-manager.ts @@ -190,6 +190,7 @@ export class PreferenceTransaction extends Transaction<[string, string[], unknow const saveAndRetry = nls.localizeByDefault('Save and Retry'); const open = nls.localizeByDefault('Open File'); const msg = await this.messageService.error( + // eslint-disable-next-line @theia/localization-check nls.localizeByDefault('Unable to write into {0} settings because the file has unsaved changes. Please save the {0} settings file first and then try again.', nls.localizeByDefault(PreferenceScope[this.context.getScope()].toLocaleLowerCase()) ), diff --git a/packages/property-view/src/browser/resource-property-view/resource-property-view-tree-widget.tsx b/packages/property-view/src/browser/resource-property-view/resource-property-view-tree-widget.tsx index 6aa1774e71be4..61fdacf1c6561 100644 --- a/packages/property-view/src/browser/resource-property-view/resource-property-view-tree-widget.tsx +++ b/packages/property-view/src/browser/resource-property-view/resource-property-view-tree-widget.tsx @@ -97,12 +97,12 @@ export class ResourcePropertyViewTreeWidget extends TreeWidget implements Proper this.propertiesTree.set('info', infoNode); infoNode.children.push(this.createResultLineNode('isDirectory', nls.localize('theia/property-view/directory', 'Directory'), fileStatObject.isDirectory, infoNode)); - infoNode.children.push(this.createResultLineNode('isFile', nls.localize('theia/property-view/file', 'File'), fileStatObject.isFile, infoNode)); + infoNode.children.push(this.createResultLineNode('isFile', nls.localizeByDefault('File'), fileStatObject.isFile, infoNode)); infoNode.children.push(this.createResultLineNode('isSymbolicLink', nls.localize('theia/property-view/symbolicLink', 'Symbolic link'), fileStatObject.isSymbolicLink, infoNode)); infoNode.children.push(this.createResultLineNode('location', nls.localize('theia/property-view/location', 'Location'), this.getLocationString(fileStatObject), infoNode)); - infoNode.children.push(this.createResultLineNode('name', nls.localize('theia/property-view/name', 'Name'), this.getFileName(fileStatObject), infoNode)); + infoNode.children.push(this.createResultLineNode('name', nls.localizeByDefault('Name'), this.getFileName(fileStatObject), infoNode)); infoNode.children.push(this.createResultLineNode('path', nls.localize('theia/property-view/path', 'Path'), this.getFilePath(fileStatObject), infoNode)); infoNode.children.push(this.createResultLineNode('lastModification', nls.localize('theia/property-view/lastModified', 'Last modified'), this.getLastModificationString(fileStatObject), infoNode)); @@ -134,7 +134,7 @@ export class ResourcePropertyViewTreeWidget extends TreeWidget implements Proper } protected getSizeString(fileStat: FileStat): string { - return fileStat.size ? nls.localizeByDefault('{0} B', fileStat.size.toString()) : ''; + return fileStat.size ? nls.localizeByDefault('{0}B', fileStat.size.toString()) : ''; } /* diff --git a/packages/scm-extra/src/browser/history/scm-history-widget.tsx b/packages/scm-extra/src/browser/history/scm-history-widget.tsx index 0625cfb89df6c..ead01a3eeb184 100644 --- a/packages/scm-extra/src/browser/history/scm-history-widget.tsx +++ b/packages/scm-extra/src/browser/history/scm-history-widget.tsx @@ -264,7 +264,7 @@ export class ScmHistoryWidget extends ScmNavigableListWidget } else { this.status = { state: 'error', - errorMessage: {nls.localizeByDefault('There is no repository selected in this workspace.')} + errorMessage: {nls.localizeByDefault('No source control providers registered.')} }; } } diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-factory.ts b/packages/search-in-workspace/src/browser/search-in-workspace-factory.ts index cb15895a45be1..2a79cc9f1bc8f 100644 --- a/packages/search-in-workspace/src/browser/search-in-workspace-factory.ts +++ b/packages/search-in-workspace/src/browser/search-in-workspace-factory.ts @@ -23,10 +23,11 @@ import { WidgetManager } from '@theia/core/lib/browser'; import { SearchInWorkspaceWidget } from './search-in-workspace-widget'; +import { nls } from '@theia/core/lib/common/nls'; export const SEARCH_VIEW_CONTAINER_ID = 'search-view-container'; export const SEARCH_VIEW_CONTAINER_TITLE_OPTIONS: ViewContainerTitleOptions = { - label: 'Search', + label: nls.localizeByDefault('Search'), iconClass: codicon('search'), closeable: true }; diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts b/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts index f85abf363b78c..7ff2956f9cfbc 100644 --- a/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts +++ b/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts @@ -50,10 +50,10 @@ export namespace SearchInWorkspaceCommands { category: SEARCH_CATEGORY, label: 'Replace in Files' }); - export const FIND_IN_FOLDER = Command.toLocalizedCommand({ + export const FIND_IN_FOLDER = Command.toDefaultLocalizedCommand({ id: 'search-in-workspace.in-folder', category: SEARCH_CATEGORY, - label: 'Find in Folder' + label: 'Find in Folder...' }); export const REFRESH_RESULTS = Command.toDefaultLocalizedCommand({ id: 'search-in-workspace.refresh', diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx b/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx index 1894c6ac33e74..0686bcbeb6751 100644 --- a/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx +++ b/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx @@ -125,7 +125,7 @@ export class SearchInWorkspaceResultTreeWidget extends TreeWidget { protected readonly toDisposeOnActiveEditorChanged = new DisposableCollection(); // The default root name to add external search results in the case that a workspace is opened. - protected readonly defaultRootName = nls.localize('theia/searchResultsView/searchFolderMatch.other.label', 'Other files'); + protected readonly defaultRootName = nls.localizeByDefault('Other files'); protected forceVisibleRootNode = false; protected appliedDecorations = new Map(); diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx b/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx index 12cc652b40976..07d44c802ae3a 100644 --- a/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx +++ b/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx @@ -686,7 +686,7 @@ export class SearchInWorkspaceWidget extends BaseWidget implements StatefulWidge message = nls.localizeByDefault("No results found excluding '{0}' - ", this.searchInWorkspaceOptions.exclude!.toString()); } else { - message = nls.localizeByDefault('No results found. - '); + message = nls.localizeByDefault('No results found') + ' - '; } // We have to trim here as vscode will always add a trailing " - " string return message.substring(0, message.length - 2).trim(); diff --git a/packages/terminal/src/browser/terminal-preferences.ts b/packages/terminal/src/browser/terminal-preferences.ts index e21438909d029..acfe89c46ad6e 100644 --- a/packages/terminal/src/browser/terminal-preferences.ts +++ b/packages/terminal/src/browser/terminal-preferences.ts @@ -143,12 +143,13 @@ export const TerminalConfigSchema: PreferenceSchema = { }, 'terminal.integrated.confirmOnExit': { type: 'string', - description: nls.localizeByDefault('Controls whether to confirm when the window closes if there are active terminal sessions'), + // nls-todo: Will be included by default in VSCode version 1.58.0 + description: nls.localize('theia/terminal/confirmClose', 'Controls whether to confirm when the window closes if there are active terminal sessions.'), enum: ['never', 'always', 'hasChildProcesses'], enumDescriptions: [ - nls.localizeByDefault('Never confirm.'), - nls.localizeByDefault('Always confirm if there are terminals.'), - nls.localizeByDefault('Confirm if there are any terminals that have child processes.'), + nls.localize('theia/terminal/confirmCloseNever', 'Never confirm.'), + nls.localize('theia/terminal/confirmCloseAlways', 'Always confirm if there are terminals.'), + nls.localize('theia/terminal/confirmCloseChildren', 'Confirm if there are any terminals that have child processes.'), ], default: 'never' } diff --git a/packages/vsx-registry/src/browser/vsx-extension-commands.ts b/packages/vsx-registry/src/browser/vsx-extension-commands.ts index 02f7e6dba60ae..e4f345a8d5f03 100644 --- a/packages/vsx-registry/src/browser/vsx-extension-commands.ts +++ b/packages/vsx-registry/src/browser/vsx-extension-commands.ts @@ -33,8 +33,8 @@ export namespace VSXExtensionsCommands { category: nls.localizeByDefault(EXTENSIONS_CATEGORY), originalCategory: EXTENSIONS_CATEGORY, originalLabel: 'Install from VSIX...', - label: nls.localize('theia/vsx-registry/installFromVSIX', 'Install from VSIX') + '...', - dialogLabel: nls.localize('theia/vsx-registry/installFromVSIX', 'Install from VSIX') + label: nls.localizeByDefault('Install from VSIX') + '...', + dialogLabel: nls.localizeByDefault('Install from VSIX') }; export const COPY: Command = { id: 'vsxExtensions.copy' diff --git a/packages/workspace/src/browser/workspace-commands.ts b/packages/workspace/src/browser/workspace-commands.ts index c318c00197b0c..4c5ac88107f14 100644 --- a/packages/workspace/src/browser/workspace-commands.ts +++ b/packages/workspace/src/browser/workspace-commands.ts @@ -421,21 +421,21 @@ export class WorkspaceCommandContribution implements CommandContribution { } // do not allow recursive rename if (!allowNested && !validFilename(name)) { - return nls.localizeByDefault('Invalid file or folder name'); + return nls.localizeByDefault('The name **{0}** is not valid as a file or folder name. Please choose a different name.'); } if (name.startsWith('/')) { - return nls.localizeByDefault('Absolute paths or names that starts with / are not allowed'); + return nls.localizeByDefault('A file or folder name cannot start with a slash.'); } else if (name.startsWith(' ') || name.endsWith(' ')) { - return nls.localizeByDefault('Names with leading or trailing whitespaces are not allowed'); + return nls.localizeByDefault('Leading or trailing whitespace detected in file or folder name.'); } // check and validate each sub-paths if (name.split(/[\\/]/).some(file => !file || !validFilename(file) || /^\s+$/.test(file))) { - return nls.localizeByDefault('The name "{0}" is not a valid file or folder name.', this.trimFileName(name)); + return nls.localizeByDefault('\'{0}\' is not a valid file name', this.trimFileName(name)); } const childUri = parent.resource.resolve(name); const exists = await this.fileService.exists(childUri); if (exists) { - return nls.localizeByDefault('A file or folder "{0}" already exists at this location.', this.trimFileName(name)); + return nls.localizeByDefault('A file or folder **{0}** already exists at this location. Please choose a different name.', this.trimFileName(name)); } return ''; } diff --git a/packages/workspace/src/browser/workspace-frontend-contribution.ts b/packages/workspace/src/browser/workspace-frontend-contribution.ts index 3ac0c30a52220..88a47ce95edc4 100644 --- a/packages/workspace/src/browser/workspace-frontend-contribution.ts +++ b/packages/workspace/src/browser/workspace-frontend-contribution.ts @@ -568,7 +568,7 @@ export class WorkspaceFrontendContribution implements CommandContribution, Keybi // Prompt users for confirmation before overwriting. const confirmed = await new ConfirmDialog({ title: nls.localizeByDefault('Overwrite'), - msg: nls.localizeByDefault('Do you really want to overwrite "{0}"?', uri.toString()) + msg: nls.localizeByDefault('{0} already exists. Are you sure you want to overwrite it?', uri.toString()) }).open(); return !!confirmed; } diff --git a/packages/workspace/src/browser/workspace-trust-preferences.ts b/packages/workspace/src/browser/workspace-trust-preferences.ts index cd1708a65f941..d7a4f2c6e1e96 100644 --- a/packages/workspace/src/browser/workspace-trust-preferences.ts +++ b/packages/workspace/src/browser/workspace-trust-preferences.ts @@ -39,7 +39,8 @@ export const workspaceTrustPreferenceSchema: PreferenceSchema = { defaultValue: true }, [WORKSPACE_TRUST_STARTUP_PROMPT]: { - description: nls.localizeByDefault('Controls when the startup prompt to trust a workspace is shown.'), + // nls-todo: This string will be available in vscode starting from API version 1.57.0 + description: nls.localize('theia/workspace/trustPrompt', 'Controls when the startup prompt to trust a workspace is shown.'), enum: Object.values(WorkspaceTrustPrompt), defaultValue: WorkspaceTrustPrompt.ALWAYS }, diff --git a/yarn.lock b/yarn.lock index fd56401ec0061..e4907d6906959 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6995,6 +6995,11 @@ jpeg-js@^0.4.2: resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b" integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q== +js-levenshtein@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"