diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index c4dd03bdfc7..ebe29bc3836 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -44,7 +44,8 @@ "treeItemCheckbox", "treeViewReveal", "workspaceTrust", - "telemetry" + "telemetry", + "localization" ], "private": true, "activationEvents": [], diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 9296a375d9c..011d3fbee14 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -268,6 +268,7 @@ export interface IRelaxedExtensionManifest { description?: string; main?: string; browser?: string; + l10nBundleLocation?: string; icon?: string; categories?: string[]; keywords?: string[]; diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 2832cd5c9f2..203f680485c 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -16,6 +16,7 @@ import { TokenClassificationExtensionPoints } from 'vs/workbench/services/themes import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint'; // --- mainThread participants +import './mainThreadLocalization'; import './mainThreadBulkEdits'; import './mainThreadCodeInsets'; import './mainThreadCLICommands'; diff --git a/src/vs/workbench/api/browser/mainThreadLocalization.ts b/src/vs/workbench/api/browser/mainThreadLocalization.ts new file mode 100644 index 00000000000..988fb8ba6c3 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadLocalization.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MainContext, MainThreadLocalizationShape } from 'vs/workbench/api/common/extHost.protocol'; +import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { Disposable } from 'vs/base/common/lifecycle'; + +@extHostNamedCustomer(MainContext.MainThreadLocalization) +export class MainThreadLocalization extends Disposable implements MainThreadLocalizationShape { + + constructor( + extHostContext: IExtHostContext, + @IFileService private readonly fileService: IFileService, + ) { + super(); + } + + async $fetchBundleContents(uriComponents: UriComponents): Promise { + const contents = await this.fileService.readFile(URI.revive(uriComponents)); + return contents.value.toString(); + } +} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 8fadafd3118..3b49d968ab8 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -93,6 +93,7 @@ import { combinedDisposable } from 'vs/base/common/lifecycle'; import { checkProposedApiEnabled, ExtensionIdentifierSet, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; import { IExtHostTelemetryLogService } from 'vs/workbench/api/common/extHostTelemetryLogService'; +import { IExtHostLocalizationService } from 'vs/workbench/api/common/extHostLocalizationService'; export interface IExtensionRegistries { mine: ExtensionDescriptionRegistry; @@ -151,6 +152,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, accessor.get(IExtHostSearch)); const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, accessor.get(IExtHostTask)); const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, accessor.get(IExtHostOutputService)); + const extHostLocalization = rpcProtocol.set(ExtHostContext.ExtHostLocalization, accessor.get(IExtHostLocalizationService)); // manually create and register addressable instances const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol)); @@ -1167,6 +1169,28 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } }; + // namespace: l10n + const l10n: typeof vscode.l10n = { + t(...params: [message: string, ...args: string[]] | [{ message: string; args: string[]; comment: string[] }]): string { + checkProposedApiEnabled(extension, 'localization'); + + if (typeof params[0] === 'string') { + const key = params.shift() as string; + return extHostLocalization.getMessage(extension.identifier.value, { message: key, args: params as string[] }); + } + + return extHostLocalization.getMessage(extension.identifier.value, params[0]); + }, + get bundle() { + checkProposedApiEnabled(extension, 'localization'); + return extHostLocalization.getBundle(extension.identifier.value); + }, + get uri() { + checkProposedApiEnabled(extension, 'localization'); + return extHostLocalization.getBundleUri(extension.identifier.value); + } + }; + return { version: initData.version, // namespaces @@ -1176,6 +1200,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I debug, env, extensions, + l10n, languages, notebooks, scm, diff --git a/src/vs/workbench/api/common/extHost.common.services.ts b/src/vs/workbench/api/common/extHost.common.services.ts index 45ccc2baf3b..4658a622298 100644 --- a/src/vs/workbench/api/common/extHost.common.services.ts +++ b/src/vs/workbench/api/common/extHost.common.services.ts @@ -28,7 +28,9 @@ import { ILoggerService, ILogService } from 'vs/platform/log/common/log'; import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; import { ExtHostVariableResolverProviderService, IExtHostVariableResolverProvider } from 'vs/workbench/api/common/extHostVariableResolverService'; import { ExtHostTelemetryLogService, IExtHostTelemetryLogService } from 'vs/workbench/api/common/extHostTelemetryLogService'; +import { ExtHostLocalizationService, IExtHostLocalizationService } from 'vs/workbench/api/common/extHostLocalizationService'; +registerSingleton(IExtHostLocalizationService, ExtHostLocalizationService, true); registerSingleton(ILoggerService, ExtHostLoggerService, true); registerSingleton(ILogService, ExtHostLogService, true); registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService, false); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d355265276e..f4532aa46a7 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -2144,6 +2144,10 @@ export interface ExtHostThemingShape { export interface MainThreadThemingShape extends IDisposable { } +export interface MainThreadLocalizationShape extends IDisposable { + $fetchBundleContents(uriComponents: UriComponents): Promise; +} + export interface TunnelDto { remoteAddress: { port: number; host: string }; localAddress: { port: number; host: string } | string; @@ -2193,6 +2197,19 @@ export interface ExtHostTestingShape { $refreshTests(controllerId: string, token: CancellationToken): Promise; } +export interface ExtHostLocalizationShape { + getMessage(extensionId: string, details: IStringDetails): string; + getBundle(extensionId: string): { [key: string]: string }; + getBundleUri(extensionId: string): URI | undefined; + initializeLocalizedMessages(extension: IExtensionDescription): Promise; +} + +export interface IStringDetails { + message: string; + args?: string[]; + comment?: string[]; +} + export interface ITestControllerPatch { label?: string; canRefresh?: boolean; @@ -2310,6 +2327,7 @@ export const MainContext = { MainThreadTunnelService: createProxyIdentifier('MainThreadTunnelService'), MainThreadTimeline: createProxyIdentifier('MainThreadTimeline'), MainThreadTesting: createProxyIdentifier('MainThreadTesting'), + MainThreadLocalization: createProxyIdentifier('MainThreadLocalizationShape') }; export const ExtHostContext = { @@ -2364,4 +2382,5 @@ export const ExtHostContext = { ExtHostTimeline: createProxyIdentifier('ExtHostTimeline'), ExtHostTesting: createProxyIdentifier('ExtHostTesting'), ExtHostTelemetry: createProxyIdentifier('ExtHostTelemetry'), + ExtHostLocalization: createProxyIdentifier('ExtHostLocalization'), }; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 2f1bb393282..63f37f89625 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -40,6 +40,7 @@ import { ExtHostSecretState, IExtHostSecretState } from 'vs/workbench/api/common import { ExtensionSecrets } from 'vs/workbench/api/common/extHostSecrets'; import { Schemas } from 'vs/base/common/network'; import { IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; +import { IExtHostLocalizationService } from 'vs/workbench/api/common/extHostLocalizationService'; interface ITestRunner { /** Old test runner API, as exported from `vscode/lib/testrunner` */ @@ -90,6 +91,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme protected readonly _logService: ILogService; protected readonly _extHostTunnelService: IExtHostTunnelService; protected readonly _extHostTerminalService: IExtHostTerminalService; + protected readonly _extHostLocalizationService: IExtHostLocalizationService; protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape; protected readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape; @@ -125,6 +127,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme @IExtensionStoragePaths storagePath: IExtensionStoragePaths, @IExtHostTunnelService extHostTunnelService: IExtHostTunnelService, @IExtHostTerminalService extHostTerminalService: IExtHostTerminalService, + @IExtHostLocalizationService extHostLocalizationService: IExtHostLocalizationService ) { super(); this._hostUtils = hostUtils; @@ -136,6 +139,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme this._logService = logService; this._extHostTunnelService = extHostTunnelService; this._extHostTerminalService = extHostTerminalService; + this._extHostLocalizationService = extHostLocalizationService; this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace); this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry); diff --git a/src/vs/workbench/api/common/extHostLocalizationService.ts b/src/vs/workbench/api/common/extHostLocalizationService.ts new file mode 100644 index 00000000000..c09eccb0077 --- /dev/null +++ b/src/vs/workbench/api/common/extHostLocalizationService.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Language } from 'vs/base/common/platform'; +import { format } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ExtHostLocalizationShape, IStringDetails, MainContext, MainThreadLocalizationShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; + +export class ExtHostLocalizationService implements ExtHostLocalizationShape { + readonly _serviceBrand: undefined; + + private readonly _proxy: MainThreadLocalizationShape; + + private readonly bundleCache: Map = new Map(); + + constructor( + @IExtHostRpcService rpc: IExtHostRpcService, + @ILogService private readonly logService: ILogService + ) { + this._proxy = rpc.getProxy(MainContext.MainThreadLocalization); + } + + getMessage(extensionId: string, details: IStringDetails): string { + const { message, args, comment } = details; + if (Language.isDefault()) { + return format(message, args); + } + + let key = message; + if (comment && comment.length > 0) { + key += `/${comment.join()}`; + } + const str = this.bundleCache.get(extensionId)?.contents[key]; + if (!str) { + this.logService.warn(`Using default string since no string found in i18n bundle that has the key: ${key}`); + } + return format(str ?? key, args); + } + + getBundle(extensionId: string): { [key: string]: string } { + return this.bundleCache.get(extensionId)?.contents ?? {}; + } + + getBundleUri(extensionId: string): URI | undefined { + return this.bundleCache.get(extensionId)?.uri; + } + + async initializeLocalizedMessages(extension: IExtensionDescription): Promise { + if (Language.isDefault() + // TODO: support builtin extensions + || !extension.l10nBundleLocation + ) { + return; + } + + if (this.bundleCache.has(extension.identifier.value)) { + return; + } + + let contents: { [key: string]: string } | undefined; + const bundleLocation = this.getBundleLocation(extension); + if (!bundleLocation) { + this.logService.error(`No bundle location found for extension ${extension.identifier.value}`); + return; + } + const bundleUri = URI.joinPath(bundleLocation, `bundle.l10n.${Language.value()}.json`); + + try { + const response = await this._proxy.$fetchBundleContents(bundleUri); + contents = JSON.parse(response); + } catch (e) { + this.logService.error(`Failed to load translations for ${extension.identifier.value} from ${bundleUri}: ${e.message}`); + return; + } + + if (contents) { + this.bundleCache.set(extension.identifier.value, { + contents, + uri: bundleUri + }); + } + } + + private getBundleLocation(extension: IExtensionDescription): URI | undefined { + // TODO: support builtin extensions using IExtHostInitDataService + // if (extension.isBuiltin && this.initData.nlsBaseUrl) { + // return URI.joinPath(this.initData.nlsBaseUrl, extension.identifier.value, 'main'); + // } + + if (extension.l10nBundleLocation) { + return URI.joinPath(extension.extensionLocation, extension.l10nBundleLocation); + } + return undefined; + } +} + +export const IExtHostLocalizationService = createDecorator('IExtHostLocalizationService'); +export interface IExtHostLocalizationService extends ExtHostLocalizationService { } diff --git a/src/vs/workbench/api/common/extensionHostMain.ts b/src/vs/workbench/api/common/extensionHostMain.ts index 6328360e0b3..a204a569f0e 100644 --- a/src/vs/workbench/api/common/extensionHostMain.ts +++ b/src/vs/workbench/api/common/extensionHostMain.ts @@ -136,6 +136,7 @@ export class ExtensionHostMain { initData.environment.extensionTestsLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionTestsLocationURI)); initData.environment.globalStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.globalStorageHome)); initData.environment.workspaceStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.workspaceStorageHome)); + initData.nlsBaseUrl = URI.revive(rpcProtocol.transformIncomingURIs(initData.nlsBaseUrl)); initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation)); initData.logFile = URI.revive(rpcProtocol.transformIncomingURIs(initData.logFile)); initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace); diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index c15b76ff78d..32e4f223aad 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -89,7 +89,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { return extensionDescription.main; } - protected _loadCommonJSModule(extension: IExtensionDescription | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + protected async _loadCommonJSModule(extension: IExtensionDescription | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { if (module.scheme !== Schemas.file) { throw new Error(`Cannot load URI: '${module}', must be of file-scheme`); } @@ -98,20 +98,21 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { this._logService.trace(`ExtensionService#loadCommonJSModule ${module.toString(true)}`); this._logService.flush(); const extensionId = extension?.identifier.value; + if (extension) { + await this._extHostLocalizationService.initializeLocalizedMessages(extension); + } try { if (extensionId) { performance.mark(`code/extHost/willLoadExtensionCode/${extensionId}`); } r = require.__$__nodeRequire(module.fsPath); - } catch (e) { - return Promise.reject(e); } finally { if (extensionId) { performance.mark(`code/extHost/didLoadExtensionCode/${extensionId}`); } activationTimesBuilder.codeLoadingStop(); } - return Promise.resolve(r); + return r; } public async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index aceb7ba278a..28b24d42950 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -12,7 +12,6 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes'; import { timeout } from 'vs/base/common/async'; import { ExtHostConsoleForwarder } from 'vs/workbench/api/worker/extHostConsoleForwarder'; -import { Language } from 'vs/base/common/platform'; class WorkerRequireInterceptor extends RequireInterceptor { @@ -96,17 +95,14 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { throw err; } - const strings: { [key: string]: string[] } = await this.fetchTranslatedStrings(extension); + if (extension) { + await this._extHostLocalizationService.initializeLocalizedMessages(extension); + } // define commonjs globals: `module`, `exports`, and `require` const _exports = {}; const _module = { exports: _exports }; const _require = (request: string) => { - // In order to keep vscode-nls synchronous, we prefetched the translations above - // and then return them here when the extension is loaded. - if (request === 'vscode-nls-web-data') { - return strings; - } const result = this._fakeModules!.getModule(request, module); if (result === undefined) { throw new Error(`Cannot load module '${request}'`); @@ -144,44 +140,6 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { await timeout(10); } } - - private async fetchTranslatedStrings(extension: IExtensionDescription | null): Promise<{ [key: string]: string[] }> { - let strings: { [key: string]: string[] } = {}; - if (!extension) { - return {}; - } - const translationsUri = Language.isDefaultVariant() - // If we are in the default variant, load the translations for en only. - ? extension.browserNlsBundleUris?.en - // Otherwise load the translations for the current locale with English as a fallback. - : extension.browserNlsBundleUris?.[Language.value()] ?? extension.browserNlsBundleUris?.en; - if (extension && translationsUri) { - try { - const response = await fetch(translationsUri.toString(true)); - if (!response.ok) { - throw new Error(await response.text()); - } - strings = await response.json(); - } catch (e) { - try { - console.error(`Failed to load translations for ${extension.identifier.value} from ${translationsUri}: ${e.message}`); - const englishStrings = extension.browserNlsBundleUris?.en; - if (englishStrings) { - const response = await fetch(englishStrings.toString(true)); - if (!response.ok) { - throw new Error(await response.text()); - } - strings = await response.json(); - } - throw new Error('No English strings found'); - } catch (e) { - // TODO what should this do? We really shouldn't ever be here... - console.error(e); - } - } - } - return strings; - } } function ensureSuffix(path: string, suffix: string): string { diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index eb70bb9ff17..2c35ebcfaa6 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -277,6 +277,12 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]); const workspace = this._contextService.getWorkspace(); const deltaExtensions = this.extensions.set(initData.allExtensions, initData.myExtensions); + const nlsBaseUrl = this._productService.extensionsGallery?.nlsBaseUrl; + let nlsUrlWithDetails: URI | undefined = undefined; + // Only use the nlsBaseUrl if we are using a language other than the default, English. + if (nlsBaseUrl && this._productService.commit && !platform.Language.isDefaultVariant()) { + nlsUrlWithDetails = URI.joinPath(URI.parse(nlsBaseUrl), this._productService.commit, this._productService.version, platform.Language.value()); + } return { commit: this._productService.commit, version: this._productService.version, @@ -304,6 +310,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost }, allExtensions: deltaExtensions.toAdd, myExtensions: deltaExtensions.myToAdd, + nlsBaseUrl: nlsUrlWithDetails, telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._extensionHostLogsLocation, diff --git a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts index 285ab4355c2..0f5865d1100 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts @@ -28,6 +28,7 @@ export interface IExtensionHostInitData { workspace?: IStaticWorkspaceData | null; allExtensions: IExtensionDescription[]; myExtensions: ExtensionIdentifier[]; + nlsBaseUrl?: URI; telemetryInfo: ITelemetryInfo; logLevel: LogLevel; logsLocation: URI; diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 6d0b33157b9..cd7b1c8b8f2 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -37,6 +37,7 @@ export const allApiProposals = Object.freeze({ inlineCompletionsNew: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts', interactiveWindow: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.interactiveWindow.d.ts', ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts', + localization: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.localization.d.ts', notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts', notebookContentProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts', notebookControllerKind: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts', diff --git a/src/vscode-dts/vscode.proposed.localization.d.ts b/src/vscode-dts/vscode.proposed.localization.d.ts new file mode 100644 index 00000000000..538dc68d108 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.localization.d.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + export namespace l10n { + /** + * A string that can be pulled out of a localization bundle if it exists. + */ + export function t(message: string, ...args: string[]): string; + /** + * A string that can be pulled out of a localization bundle if it exists. + */ + export function t(options: { message: string; args: string[]; comment: string[] }): string; + /** + * The bundle of localized strings that have been loaded for the extension. + */ + export const bundle: { [key: string]: string }; + /** + * The URI of the localization bundle that has been loaded for the extension. + */ + export const uri: Uri | undefined; + } +}