Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 47 additions & 8 deletions src/vs/platform/languagePacks/browser/languagePacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import { URI } from '../../../base/common/uri.js';
import { ILanguagePackItem, ILanguagePackService } from '../common/languagePacks.js';
import { IExtensionGalleryService } from '../../extensionManagement/common/extensionManagement.js';
import { CancellationToken } from '../../../base/common/cancellation.js';
import { IExtensionService } from '../../../workbench/services/extensions/common/extensions.js';

export class WebLanguagePacksService implements ILanguagePackService {
declare readonly _serviceBrand: undefined;

constructor(@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService) {}
constructor(
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
@IExtensionService private readonly extensionService: IExtensionService
) {}

async getBuiltInExtensionTranslationsUri(_id: string, _language: string): Promise<URI | undefined> {
return undefined;
Expand Down Expand Up @@ -45,20 +49,43 @@ export class WebLanguagePacksService implements ILanguagePackService {
}

async getInstalledLanguages(): Promise<ILanguagePackItem[]> {
const items: ILanguagePackItem[] = [];
const seen = new Set<string>();

// Check localStorage for explicitly set locale
const extensionId = localStorage.getItem('vscode.nls.languagePackExtensionId');
const locale = localStorage.getItem('vscode.nls.locale');

if (!extensionId || !locale) {
return [];
}

return [
{
if (extensionId && locale) {
items.push({
id: locale,
label: this.getLanguageLabel(locale),
extensionId
});
seen.add(locale.toLowerCase());
}

// Also scan installed extensions for language packs
try {
const extensions = this.extensionService.extensions;
for (const ext of extensions) {
if (ext.identifier && ext.name.startsWith('vscode-language-pack-')) {
const extLocale = this.getLocaleFromExtensionName(ext.name);
if (extLocale && !seen.has(extLocale.toLowerCase())) {
items.push({
id: extLocale,
label: this.getLanguageLabel(extLocale),
extensionId: ext.identifier.value
});
seen.add(extLocale.toLowerCase());
}
}
}
];
} catch {
// ignore errors scanning extensions
}

return items;
}

private getLanguageLabel(locale: string): string {
Expand All @@ -80,4 +107,16 @@ export class WebLanguagePacksService implements ILanguagePackService {
};
return labels[locale.toLowerCase()] ?? locale;
}

// Map extension name suffixes to locale codes
// e.g. vscode-language-pack-zh-hans -> zh-cn
private getLocaleFromExtensionName(name: string): string | undefined {
const nameToLocale: Record<string, string> = {
'zh-hans': 'zh-cn',
'zh-hant': 'zh-tw',
'pt-br': 'pt-br'
};
const suffix = name.replace('vscode-language-pack-', '');
return nameToLocale[suffix] ?? suffix;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -877,16 +877,37 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
galleryExtension: IGalleryExtension,
metadata?: Metadata
): Promise<IWebExtension> {
const extensionLocation = await this.extensionResourceLoaderService.getExtensionGalleryResourceURL(
{
publisher: galleryExtension.publisher,
name: galleryExtension.name,
version: galleryExtension.version,
targetPlatform:
galleryExtension.properties.targetPlatform === TargetPlatform.WEB ? TargetPlatform.WEB : undefined
},
'extension'
);
// Use the gallery extension's manifest asset URL directly (already proxied
// correctly by the marketplace proxy for both Microsoft and Open VSX sources)
// instead of constructing a URL from resourceUrlTemplate which may point to
// a version that doesn't exist on Open VSX.
let manifest: IExtensionManifest | undefined;
let extensionLocation: URI | undefined;

if (galleryExtension.assets.manifest) {
try {
const manifestUri = URI.parse(galleryExtension.assets.manifest.uri);
const content = await this.extensionResourceLoaderService.readExtensionResource(manifestUri);
manifest = JSON.parse(content);
// Use the manifest URI's directory as the extension location
extensionLocation = joinPath(manifestUri, '..');
} catch (error) {
this.logService.warn(`Failed to fetch manifest from gallery asset, falling back to resourceUrlTemplate`, getErrorMessage(error));
}
}

if (!extensionLocation) {
extensionLocation = await this.extensionResourceLoaderService.getExtensionGalleryResourceURL(
{
publisher: galleryExtension.publisher,
name: galleryExtension.name,
version: galleryExtension.version,
targetPlatform:
galleryExtension.properties.targetPlatform === TargetPlatform.WEB ? TargetPlatform.WEB : undefined
},
'extension'
);
}

if (!extensionLocation) {
throw new Error('No extension gallery service configured.');
Expand All @@ -897,7 +918,8 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
galleryExtension.identifier,
galleryExtension.assets.readme ? URI.parse(galleryExtension.assets.readme.uri) : undefined,
galleryExtension.assets.changelog ? URI.parse(galleryExtension.assets.changelog.uri) : undefined,
metadata
metadata,
manifest
);
}

Expand All @@ -906,7 +928,8 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
identifier?: IExtensionIdentifier,
readmeUri?: URI,
changelogUri?: URI,
metadata?: Metadata
metadata?: Metadata,
manifest?: IExtensionManifest
): Promise<IWebExtension> {
const extensionResources = await this.listExtensionResources(extensionLocation);
const packageNLSResources = this.getPackageNLSResourceMapFromResources(extensionResources);
Expand All @@ -916,7 +939,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
return this.toWebExtension(
extensionLocation,
identifier,
undefined,
manifest,
packageNLSResources,
fallbackPackageNLSResource ? URI.parse(fallbackPackageNLSResource) : null,
readmeUri,
Expand Down