mirror of
https://github.com/Microsoft/vscode
synced 2024-09-29 16:01:05 +00:00
Introduce localize2
function (#194750)
* Introduce `localize2` function This is syntax sugar around: ``` { value localize('id', "Hello"), original: 'Hello' } ``` That will now be returned when you do: ``` localize2('id', "Hello"); ``` * fix merge conflic * new source map due to updated deps
This commit is contained in:
parent
1fbca59042
commit
a27dc7725c
|
@ -142,6 +142,9 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule {
|
|||
// localize(...)
|
||||
['CallExpression[callee.type="MemberExpression"][callee.object.name="nls"][callee.property.name="localize"]:exit']: (node: any) => visitLocalizeCall(node),
|
||||
|
||||
// localize2(...)
|
||||
['CallExpression[callee.type="MemberExpression"][callee.object.name="nls"][callee.property.name="localize2"]:exit']: (node: any) => visitLocalizeCall(node),
|
||||
|
||||
// vscode.l10n.t(...)
|
||||
['CallExpression[callee.type="MemberExpression"][callee.object.property.name="l10n"][callee.property.name="t"]:exit']: (node: any) => visitL10NCall(node),
|
||||
|
||||
|
@ -149,6 +152,7 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule {
|
|||
['CallExpression[callee.object.name="l10n"][callee.property.name="t"]:exit']: (node: any) => visitL10NCall(node),
|
||||
|
||||
['CallExpression[callee.name="localize"][arguments.length>=2]:exit']: (node: any) => visitLocalizeCall(node),
|
||||
['CallExpression[callee.name="localize2"][arguments.length>=2]:exit']: (node: any) => visitLocalizeCall(node),
|
||||
['Program:exit']: reportBadStringsAndBadKeys,
|
||||
};
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -181,7 +181,12 @@ module _nls {
|
|||
return node.kind === ts.SyntaxKind.CallExpression ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse;
|
||||
}
|
||||
|
||||
function analyze(ts: typeof import('typescript'), contents: string, options: ts.CompilerOptions = {}): ILocalizeAnalysisResult {
|
||||
function analyze(
|
||||
ts: typeof import('typescript'),
|
||||
contents: string,
|
||||
functionName: 'localize' | 'localize2',
|
||||
options: ts.CompilerOptions = {}
|
||||
): ILocalizeAnalysisResult {
|
||||
const filename = 'file.ts';
|
||||
const serviceHost = new SingleFileServiceHost(ts, Object.assign(clone(options), { noResolve: true }), filename, contents);
|
||||
const service = ts.createLanguageService(serviceHost);
|
||||
|
@ -231,7 +236,7 @@ module _nls {
|
|||
.map(n => <ts.CallExpression>n)
|
||||
|
||||
// only `localize` calls
|
||||
.filter(n => n.expression.kind === ts.SyntaxKind.PropertyAccessExpression && (<ts.PropertyAccessExpression>n.expression).name.getText() === 'localize');
|
||||
.filter(n => n.expression.kind === ts.SyntaxKind.PropertyAccessExpression && (<ts.PropertyAccessExpression>n.expression).name.getText() === functionName);
|
||||
|
||||
// `localize` named imports
|
||||
const allLocalizeImportDeclarations = importDeclarations
|
||||
|
@ -241,14 +246,14 @@ module _nls {
|
|||
|
||||
// `localize` read-only references
|
||||
const localizeReferences = allLocalizeImportDeclarations
|
||||
.filter(d => d.name.getText() === 'localize')
|
||||
.filter(d => d.name.getText() === functionName)
|
||||
.map(n => service.getReferencesAtPosition(filename, n.pos + 1))
|
||||
.flatten()
|
||||
.filter(r => !r.isWriteAccess);
|
||||
|
||||
// custom named `localize` read-only references
|
||||
const namedLocalizeReferences = allLocalizeImportDeclarations
|
||||
.filter(d => d.propertyName && d.propertyName.getText() === 'localize')
|
||||
.filter(d => d.propertyName && d.propertyName.getText() === functionName)
|
||||
.map(n => service.getReferencesAtPosition(filename, n.name.pos + 1))
|
||||
.flatten()
|
||||
.filter(r => !r.isWriteAccess);
|
||||
|
@ -406,20 +411,21 @@ module _nls {
|
|||
}
|
||||
|
||||
function patch(ts: typeof import('typescript'), moduleId: string, typescript: string, javascript: string, sourcemap: sm.RawSourceMap): INlsStringResult {
|
||||
const { localizeCalls, nlsExpressions } = analyze(ts, typescript);
|
||||
const { localizeCalls, nlsExpressions } = analyze(ts, typescript, 'localize');
|
||||
const { localizeCalls: localize2Calls, nlsExpressions: nls2Expressions } = analyze(ts, typescript, 'localize2');
|
||||
|
||||
if (localizeCalls.length === 0) {
|
||||
return { javascript, sourcemap };
|
||||
}
|
||||
|
||||
const nlsKeys = template(localizeCalls.map(lc => lc.key));
|
||||
const nls = template(localizeCalls.map(lc => lc.value));
|
||||
const nlsKeys = template(localizeCalls.map(lc => lc.key).concat(localize2Calls.map(lc => lc.key)));
|
||||
const nls = template(localizeCalls.map(lc => lc.value).concat(localize2Calls.map(lc => lc.value)));
|
||||
const smc = new sm.SourceMapConsumer(sourcemap);
|
||||
const positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]);
|
||||
let i = 0;
|
||||
|
||||
// build patches
|
||||
const patches = lazy(localizeCalls)
|
||||
const localizePatches = lazy(localizeCalls)
|
||||
.map(lc => ([
|
||||
{ range: lc.keySpan, content: '' + (i++) },
|
||||
{ range: lc.valueSpan, content: 'null' }
|
||||
|
@ -429,14 +435,25 @@ module _nls {
|
|||
const start = lcFrom(smc.generatedPositionFor(positionFrom(c.range.start)));
|
||||
const end = lcFrom(smc.generatedPositionFor(positionFrom(c.range.end)));
|
||||
return { span: { start, end }, content: c.content };
|
||||
})
|
||||
.toArray();
|
||||
});
|
||||
|
||||
const localize2Patches = lazy(localize2Calls)
|
||||
.map(lc => ([
|
||||
{ range: lc.keySpan, content: '' + (i++) }
|
||||
])).flatten()
|
||||
.map<IPatch>(c => {
|
||||
const start = lcFrom(smc.generatedPositionFor(positionFrom(c.range.start)));
|
||||
const end = lcFrom(smc.generatedPositionFor(positionFrom(c.range.end)));
|
||||
return { span: { start, end }, content: c.content };
|
||||
});
|
||||
|
||||
const patches = localizePatches.concat(localize2Patches).toArray();
|
||||
|
||||
javascript = patchJavascript(patches, javascript, moduleId);
|
||||
|
||||
// since imports are not within the sourcemap information,
|
||||
// we must do this MacGyver style
|
||||
if (nlsExpressions.length) {
|
||||
if (nlsExpressions.length || nls2Expressions.length) {
|
||||
javascript = javascript.replace(/^define\(.*$/m, line => {
|
||||
return line.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`);
|
||||
});
|
||||
|
|
|
@ -16,6 +16,10 @@ export function localize(data: ILocalizeInfo | string, message: string, ...args:
|
|||
throw new Error(`Not supported at build time!`);
|
||||
}
|
||||
|
||||
export function localize2(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): never {
|
||||
throw new Error(`Not supported at build time!`);
|
||||
}
|
||||
|
||||
export function getConfiguredDefaultLocale(): string | undefined {
|
||||
throw new Error(`Not supported at build time!`);
|
||||
}
|
||||
|
@ -25,7 +29,7 @@ export function getConfiguredDefaultLocale(): string | undefined {
|
|||
*/
|
||||
export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void {
|
||||
if (!name || name.length === 0) {
|
||||
load({ localize, getConfiguredDefaultLocale });
|
||||
load({ localize, localize2, getConfiguredDefaultLocale });
|
||||
} else {
|
||||
req([name + '.nls', name + '.nls.keys'], function (messages: string[], keys: string[]) {
|
||||
buildMap[name] = messages;
|
||||
|
|
|
@ -8,6 +8,11 @@ export interface ILocalizeInfo {
|
|||
comment: string[];
|
||||
}
|
||||
|
||||
interface ILocalizedString {
|
||||
original: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function _format(message: string, args: any[]): string {
|
||||
let result: string;
|
||||
if (args.length === 0) {
|
||||
|
@ -25,6 +30,14 @@ export function localize(data: ILocalizeInfo | string, message: string, ...args:
|
|||
return _format(message, args);
|
||||
}
|
||||
|
||||
export function localize2(data: ILocalizeInfo | string, message: string, ...args: any[]): ILocalizedString {
|
||||
const res = _format(message, args);
|
||||
return {
|
||||
original: res,
|
||||
value: res
|
||||
};
|
||||
}
|
||||
|
||||
export function getConfiguredDefaultLocale(_: string) {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,11 @@ export interface ILocalizeInfo {
|
|||
comment: string[];
|
||||
}
|
||||
|
||||
interface ILocalizedString {
|
||||
original: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface ILocalizeFunc {
|
||||
(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
|
||||
(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
|
||||
|
@ -39,8 +44,18 @@ interface IBoundLocalizeFunc {
|
|||
(idx: number, defaultValue: null): string;
|
||||
}
|
||||
|
||||
interface ILocalize2Func {
|
||||
(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
|
||||
(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
|
||||
}
|
||||
|
||||
interface IBoundLocalize2Func {
|
||||
(idx: number, defaultValue: string): ILocalizedString;
|
||||
}
|
||||
|
||||
interface IConsumerAPI {
|
||||
localize: ILocalizeFunc | IBoundLocalizeFunc;
|
||||
localize2: ILocalize2Func | IBoundLocalize2Func;
|
||||
getConfiguredDefaultLocale(stringFromLocalizeCall: string): string | undefined;
|
||||
}
|
||||
|
||||
|
@ -107,19 +122,38 @@ function createScopedLocalize(scope: string[]): IBoundLocalizeFunc {
|
|||
};
|
||||
}
|
||||
|
||||
function createScopedLocalize2(scope: string[]): IBoundLocalize2Func {
|
||||
return (idx: number, defaultValue: string, ...args) => ({
|
||||
value: _format(scope[idx], args),
|
||||
original: _format(defaultValue, args)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Localize a message.
|
||||
* Marks a string to be localized. Returns the localized string.
|
||||
*
|
||||
* `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
|
||||
* For example, `localize({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)`
|
||||
* @param info The {@linkcode ILocalizeInfo} which describes the id and comments associated with the localized string.
|
||||
* @param message The string to localize
|
||||
* @param args The arguments to the string
|
||||
*
|
||||
* @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
|
||||
* @example `localize({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)`
|
||||
*
|
||||
* @returns string The localized string.
|
||||
*/
|
||||
export function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
|
||||
|
||||
/**
|
||||
* Localize a message.
|
||||
* Marks a string to be localized. Returns the localized string.
|
||||
*
|
||||
* `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
|
||||
* For example, `localize('sayHello', 'hello {0}', name)`
|
||||
* @param key The key to use for localizing the string
|
||||
* @param message The string to localize
|
||||
* @param args The arguments to the string
|
||||
*
|
||||
* @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
|
||||
* @example For example, `localize('sayHello', 'hello {0}', name)`
|
||||
*
|
||||
* @returns string The localized string.
|
||||
*/
|
||||
export function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
|
||||
|
||||
|
@ -130,6 +164,47 @@ export function localize(data: ILocalizeInfo | string, message: string, ...args:
|
|||
return _format(message, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a string to be localized. Returns an {@linkcode ILocalizedString}
|
||||
* which contains the localized string and the original string.
|
||||
*
|
||||
* @param info The {@linkcode ILocalizeInfo} which describes the id and comments associated with the localized string.
|
||||
* @param message The string to localize
|
||||
* @param args The arguments to the string
|
||||
*
|
||||
* @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
|
||||
* @example `localize2({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)`
|
||||
*
|
||||
* @returns ILocalizedString which contains the localized string and the original string.
|
||||
*/
|
||||
export function localize2(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
|
||||
|
||||
/**
|
||||
* Marks a string to be localized. Returns an {@linkcode ILocalizedString}
|
||||
* which contains the localized string and the original string.
|
||||
*
|
||||
* @param key The key to use for localizing the string
|
||||
* @param message The string to localize
|
||||
* @param args The arguments to the string
|
||||
*
|
||||
* @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
|
||||
* @example `localize('sayHello', 'hello {0}', name)`
|
||||
*
|
||||
* @returns ILocalizedString which contains the localized string and the original string.
|
||||
*/
|
||||
export function localize2(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
|
||||
|
||||
/**
|
||||
* @skipMangle
|
||||
*/
|
||||
export function localize2(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString {
|
||||
const original = _format(message, args);
|
||||
return {
|
||||
value: original,
|
||||
original
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param stringFromLocalizeCall You must pass in a string that was returned from a `nls.localize()` call
|
||||
|
@ -159,6 +234,7 @@ export function setPseudoTranslation(value: boolean) {
|
|||
export function create(key: string, data: IBundledStrings & IConsumerAPI): IConsumerAPI {
|
||||
return {
|
||||
localize: createScopedLocalize(data[key]),
|
||||
localize2: createScopedLocalize2(data[key]),
|
||||
getConfiguredDefaultLocale: data.getConfiguredDefaultLocale ?? ((_: string) => undefined)
|
||||
};
|
||||
}
|
||||
|
@ -173,8 +249,9 @@ export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoa
|
|||
// TODO: We need to give back the mangled names here
|
||||
return load({
|
||||
localize: localize,
|
||||
localize2: localize2,
|
||||
getConfiguredDefaultLocale: () => pluginConfig.availableLanguages?.['*']
|
||||
});
|
||||
} as IConsumerAPI);
|
||||
}
|
||||
const language = pluginConfig.availableLanguages ? findLanguageForModule(pluginConfig.availableLanguages, name) : null;
|
||||
const useDefaultLanguage = language === null || language === DEFAULT_TAG;
|
||||
|
@ -185,8 +262,10 @@ export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoa
|
|||
const messagesLoaded = (messages: string[] | IBundledStrings) => {
|
||||
if (Array.isArray(messages)) {
|
||||
(messages as any as IConsumerAPI).localize = createScopedLocalize(messages);
|
||||
(messages as any as IConsumerAPI).localize2 = createScopedLocalize2(messages);
|
||||
} else {
|
||||
(messages as any as IConsumerAPI).localize = createScopedLocalize(messages[name]);
|
||||
(messages as any as IConsumerAPI).localize2 = createScopedLocalize2(messages[name]);
|
||||
}
|
||||
(messages as any as IConsumerAPI).getConfiguredDefaultLocale = () => pluginConfig.availableLanguages?.['*'];
|
||||
load(messages);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { localize, localize2 } from 'vs/nls';
|
||||
import { IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
|
@ -15,12 +15,11 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/com
|
|||
|
||||
export class ConfigureDisplayLanguageAction extends Action2 {
|
||||
public static readonly ID = 'workbench.action.configureLocale';
|
||||
public static readonly LABEL = localize('configureLocale', "Configure Display Language");
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: ConfigureDisplayLanguageAction.ID,
|
||||
title: { original: 'Configure Display Language', value: ConfigureDisplayLanguageAction.LABEL },
|
||||
title: localize2('configureLocale', "Configure Display Language"),
|
||||
menu: {
|
||||
id: MenuId.CommandPalette
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue