mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-20 02:08:47 +00:00
Fix offending l10n.t call and add an eslint rule to prevent it from happening (#277577)
ref https://github.com/microsoft/vscode/issues/277576
This commit is contained in:
committed by
GitHub
parent
9f56e2671c
commit
8711dcb9da
@@ -0,0 +1,90 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
import { TSESTree } from '@typescript-eslint/utils';
|
||||
|
||||
/**
|
||||
* Prevents the use of template literals in localization function calls.
|
||||
*
|
||||
* vscode.l10n.t() and nls.localize() cannot handle string templating.
|
||||
* Use placeholders instead: vscode.l10n.t('Message {0}', value)
|
||||
*
|
||||
* Examples:
|
||||
* ❌ vscode.l10n.t(`Message ${value}`)
|
||||
* ✅ vscode.l10n.t('Message {0}', value)
|
||||
*
|
||||
* ❌ nls.localize('key', `Message ${value}`)
|
||||
* ✅ nls.localize('key', 'Message {0}', value)
|
||||
*/
|
||||
export default new class NoLocalizationTemplateLiterals implements eslint.Rule.RuleModule {
|
||||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
noTemplateLiteral: 'Template literals cannot be used in localization calls. Use placeholders like {0}, {1} instead.'
|
||||
},
|
||||
docs: {
|
||||
description: 'Prevents template literals in vscode.l10n.t() and nls.localize() calls',
|
||||
},
|
||||
schema: false,
|
||||
};
|
||||
|
||||
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
||||
|
||||
function checkCallExpression(node: TSESTree.CallExpression) {
|
||||
const callee = node.callee;
|
||||
let isLocalizationCall = false;
|
||||
let isNlsLocalize = false;
|
||||
|
||||
// Check for vscode.l10n.t()
|
||||
if (callee.type === 'MemberExpression') {
|
||||
const object = callee.object;
|
||||
const property = callee.property;
|
||||
|
||||
// vscode.l10n.t
|
||||
if (object.type === 'MemberExpression') {
|
||||
const outerObject = object.object;
|
||||
const outerProperty = object.property;
|
||||
if (outerObject.type === 'Identifier' && outerObject.name === 'vscode' &&
|
||||
outerProperty.type === 'Identifier' && outerProperty.name === 'l10n' &&
|
||||
property.type === 'Identifier' && property.name === 't') {
|
||||
isLocalizationCall = true;
|
||||
}
|
||||
}
|
||||
|
||||
// l10n.t or nls.localize or any *.localize
|
||||
if (object.type === 'Identifier' && property.type === 'Identifier') {
|
||||
if (object.name === 'l10n' && property.name === 't') {
|
||||
isLocalizationCall = true;
|
||||
} else if (property.name === 'localize') {
|
||||
isLocalizationCall = true;
|
||||
isNlsLocalize = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLocalizationCall) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For vscode.l10n.t(message, ...args) - check the first argument (message)
|
||||
// For nls.localize(key, message, ...args) - check first two arguments (key and message)
|
||||
const argsToCheck = isNlsLocalize ? 2 : 1;
|
||||
for (let i = 0; i < argsToCheck && i < node.arguments.length; i++) {
|
||||
const arg = node.arguments[i];
|
||||
if (arg && arg.type === 'TemplateLiteral' && arg.expressions.length > 0) {
|
||||
context.report({
|
||||
node: arg,
|
||||
messageId: 'noTemplateLiteral'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
CallExpression: (node: any) => checkCallExpression(node as TSESTree.CallExpression)
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -90,6 +90,7 @@ export default tseslint.config(
|
||||
'local/code-no-reader-after-await': 'warn',
|
||||
'local/code-no-observable-get-in-reactive-context': 'warn',
|
||||
'local/code-policy-localization-key-match': 'warn',
|
||||
'local/code-no-localization-template-literals': 'error',
|
||||
'local/code-no-deep-import-of-internal': ['error', { '.*Internal': true, 'searchExtTypesInternal': false }],
|
||||
'local/code-layering': [
|
||||
'warn',
|
||||
|
||||
@@ -367,7 +367,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider<VsCodeCode
|
||||
let title = action.description;
|
||||
if (action.fixName === fixNames.classIncorrectlyImplementsInterface) {
|
||||
title = vscode.l10n.t('{0} with AI', action.description);
|
||||
message = vscode.l10n.t(`Implement the stubbed-out class members for ${document.getText(diagnostic.range)} with a useful implementation.`);
|
||||
message = vscode.l10n.t('Implement the stubbed-out class members for {0} with a useful implementation.', document.getText(diagnostic.range));
|
||||
expand = { kind: 'code-action', action };
|
||||
} else if (action.fixName === fixNames.fixClassDoesntImplementInheritedAbstractMember) {
|
||||
title = vscode.l10n.t('{0} with AI', action.description);
|
||||
|
||||
Reference in New Issue
Block a user