Plumbing for desktop to pick up strings from language packs (#163494)

Web will come in the next PR (hence the TODO)

Also this includes the smallest translation change which will be the ultimate test that this is all working.
This commit is contained in:
Tyler James Leonhardt 2022-10-12 17:08:27 -07:00 committed by GitHub
parent 5b7233c5e3
commit 2457d9eb46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 57 additions and 12 deletions

View file

@ -156,7 +156,7 @@ export class GitHubServer implements IGitHubServer {
// Used for showing a friendlier message to the user when the explicitly cancel a flow.
let userCancelled: boolean | undefined;
const yes = localize('yes', "Yes");
const yes = vscode.l10n.t('Yes');
const no = localize('no', "No");
const promptToContinue = async () => {
if (userCancelled === undefined) {

View file

@ -3,9 +3,23 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
export class WebLanguagePacksService extends LanguagePackBaseService {
// TODO: support builtin extensions using unpkg service
// constructor(
// @IExtensionResourceLoaderService extensionResourceLoaderService: IExtensionResourceLoaderService,
// @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
// @ILogService private readonly logService: ILogService
// ) {
// super(extensionGalleryService);
// }
getTranslationsUri(id: string): Promise<URI | undefined> {
return Promise.resolve(undefined);
}
// Web doesn't have a concept of language packs, so we just return an empty array
getInstalledLanguages(): Promise<ILanguagePackItem[]> {
return Promise.resolve([]);

View file

@ -6,6 +6,7 @@
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Disposable } from 'vs/base/common/lifecycle';
import { language } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { IQuickPickItem } from 'vs/base/parts/quickinput/common/quickInput';
import { localize } from 'vs/nls';
import { IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
@ -22,6 +23,7 @@ export interface ILanguagePackService {
readonly _serviceBrand: undefined;
getAvailableLanguages(): Promise<Array<ILanguagePackItem>>;
getInstalledLanguages(): Promise<Array<ILanguagePackItem>>;
getTranslationsUri(id: string): Promise<URI | undefined>;
getLocale(extension: IGalleryExtension): string | undefined;
}
@ -32,6 +34,8 @@ export abstract class LanguagePackBaseService extends Disposable implements ILan
super();
}
abstract getTranslationsUri(id: string): Promise<URI | undefined>;
abstract getInstalledLanguages(): Promise<Array<ILanguagePackItem>>;
async getAvailableLanguages(): Promise<ILanguagePackItem[]> {

View file

@ -16,6 +16,8 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens
import { ILogService } from 'vs/platform/log/common/log';
import { ILocalizationContribution } from 'vs/platform/extensions/common/extensions';
import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
import { locale } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
interface ILanguagePack {
hash: string;
@ -48,6 +50,18 @@ export class NativeLanguagePackService extends LanguagePackBaseService {
});
}
async getTranslationsUri(id: string): Promise<URI | undefined> {
const packs = await this.cache.getLanguagePacks();
const pack = packs[locale!];
if (!pack) {
this.logService.warn(`No language pack found for ${locale}`);
return undefined;
}
const translation = pack.translations[id];
return translation ? URI.file(translation) : undefined;
}
async getInstalledLanguages(): Promise<Array<ILanguagePackItem>> {
const languagePacks = await this.cache.getLanguagePacks();
const languages = Object.keys(languagePacks).map(locale => {

View file

@ -8,6 +8,7 @@ import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/ext
import { URI, UriComponents } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks';
@extHostNamedCustomer(MainContext.MainThreadLocalization)
export class MainThreadLocalization extends Disposable implements MainThreadLocalizationShape {
@ -15,10 +16,20 @@ export class MainThreadLocalization extends Disposable implements MainThreadLoca
constructor(
extHostContext: IExtHostContext,
@IFileService private readonly fileService: IFileService,
@ILanguagePackService private readonly languagePackService: ILanguagePackService
) {
super();
}
async $fetchBuiltInBundleUri(id: string): Promise<URI | undefined> {
try {
const uri = await this.languagePackService.getTranslationsUri(id);
return uri;
} catch (e) {
return undefined;
}
}
async $fetchBundleContents(uriComponents: UriComponents): Promise<string> {
const contents = await this.fileService.readFile(URI.revive(uriComponents));
return contents.value.toString();

View file

@ -2144,6 +2144,7 @@ export interface MainThreadThemingShape extends IDisposable {
}
export interface MainThreadLocalizationShape extends IDisposable {
$fetchBuiltInBundleUri(id: string): Promise<UriComponents | undefined>;
$fetchBundleContents(uriComponents: UriComponents): Promise<string>;
}

View file

@ -60,7 +60,7 @@ export class ExtHostLocalizationService implements ExtHostLocalizationShape {
async initializeLocalizedMessages(extension: IExtensionDescription): Promise<void> {
if (this.isDefaultLanguage
// TODO: support builtin extensions
|| !extension.l10n
|| (!extension.l10n && !extension.isBuiltin)
) {
return;
}
@ -70,16 +70,17 @@ export class ExtHostLocalizationService implements ExtHostLocalizationShape {
}
let contents: { [key: string]: string } | undefined;
const bundleLocation = this.getBundleLocation(extension);
if (!bundleLocation) {
const bundleUri = await this.getBundleLocation(extension);
if (!bundleUri) {
this.logService.error(`No bundle location found for extension ${extension.identifier.value}`);
return;
}
const bundleUri = URI.joinPath(bundleLocation, `bundle.l10n.${this.currentLanguage}.json`);
try {
const response = await this._proxy.$fetchBundleContents(bundleUri);
contents = JSON.parse(response);
const result = JSON.parse(response);
// 'contents.bundle' is a well-known key in the language pack json file that contains the _code_ translations for the extension
contents = extension.isBuiltin ? result.contents?.bundle : result;
} catch (e) {
this.logService.error(`Failed to load translations for ${extension.identifier.value} from ${bundleUri}: ${e.message}`);
return;
@ -93,14 +94,14 @@ export class ExtHostLocalizationService implements ExtHostLocalizationShape {
}
}
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');
// }
private async getBundleLocation(extension: IExtensionDescription): Promise<URI | undefined> {
if (extension.isBuiltin) {
const uri = await this._proxy.$fetchBuiltInBundleUri(extension.identifier.value);
return URI.revive(uri);
}
return extension.l10n
? URI.joinPath(extension.extensionLocation, extension.l10n)
? URI.joinPath(extension.extensionLocation, extension.l10n, `bundle.l10n.${this.currentLanguage}.json`)
: undefined;
}
}