Skip to content

Commit 5f67407

Browse files
rzhao271bpasero
andauthored
Pass display language as a locale to Electron (microsoft#159958)
Co-authored-by: Benjamin Pasero <[email protected]>
1 parent fd7e27f commit 5f67407

File tree

2 files changed

+118
-85
lines changed

2 files changed

+118
-85
lines changed

src/main.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,20 @@ if (locale) {
9999
nlsConfigurationPromise = getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
100100
}
101101

102+
if (product.quality === 'insider' || product.quality === 'exploration') {
103+
104+
// Pass in the locale to Electron so that the
105+
// Windows Control Overlay is rendered correctly on Windows,
106+
// and so that the traffic lights are rendered properly
107+
// on macOS when using a custom titlebar.
108+
// If the locale is `qps-ploc`, the Microsoft
109+
// Pseudo Language Language Pack is being used.
110+
// In that case, use `en` as the Electron locale.
111+
112+
const electronLocale = (!locale || locale === 'qps-ploc') ? 'en' : locale;
113+
app.commandLine.appendSwitch('lang', electronLocale);
114+
}
115+
102116
// Load our code once ready
103117
app.once('ready', function () {
104118
if (args['trace']) {
@@ -551,7 +565,18 @@ async function resolveNlsConfiguration() {
551565
// Try to use the app locale. Please note that the app locale is only
552566
// valid after we have received the app ready event. This is why the
553567
// code is here.
554-
let appLocale = app.getLocale();
568+
569+
// The ternary and ts-ignore can both be removed once Electron
570+
// officially adopts the getPreferredSystemLanguages API.
571+
// Ref https://github.com/microsoft/vscode/issues/159813
572+
// and https://github.com/electron/electron/pull/36035
573+
/**
574+
* @type string
575+
*/
576+
// @ts-ignore API not yet available in the official Electron
577+
let appLocale = ((product.quality === 'insider' || product.quality === 'exploration') && app?.getPreferredSystemLanguages()?.length) ?
578+
// @ts-ignore API not yet available in the official Electron
579+
app.getPreferredSystemLanguages()[0] : app.getLocale();
555580
if (!appLocale) {
556581
nlsConfiguration = { locale: 'en', availableLanguages: {} };
557582
} else {

src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts

Lines changed: 92 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,12 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
8787

8888
private checkAndInstall(): void {
8989
const language = platform.language;
90-
const locale = platform.locale;
90+
let locale = platform.locale ?? '';
91+
if (locale.startsWith('zh-hans')) {
92+
locale = 'zh-cn';
93+
} else if (locale.startsWith('zh-hant')) {
94+
locale = 'zh-tw';
95+
}
9196
const languagePackSuggestionIgnoreList = <string[]>JSON.parse(this.storageService.get(LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY, StorageScope.APPLICATION, '[]'));
9297

9398
if (!this.galleryService.isEnabled()) {
@@ -96,118 +101,121 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
96101
if (!language || !locale || locale === 'en' || locale.indexOf('en-') === 0) {
97102
return;
98103
}
99-
if (language === locale || languagePackSuggestionIgnoreList.indexOf(locale) > -1) {
104+
if (locale.startsWith(language) || languagePackSuggestionIgnoreList.includes(locale)) {
100105
return;
101106
}
102107

103-
this.isLanguageInstalled(locale)
104-
.then(installed => {
108+
this.isLocaleInstalled(locale)
109+
.then(async (installed) => {
105110
if (installed) {
106111
return;
107112
}
108113

109-
this.galleryService.query({ text: `tag:lp-${locale}` }, CancellationToken.None).then(tagResult => {
114+
let searchLocale = locale;
115+
let tagResult = await this.galleryService.query({ text: `tag:lp-${searchLocale}` }, CancellationToken.None);
116+
if (tagResult.total === 0) {
117+
// Trim the locale and try again.
118+
searchLocale = locale.split('-')[0];
119+
tagResult = await this.galleryService.query({ text: `tag:lp-${searchLocale}` }, CancellationToken.None);
110120
if (tagResult.total === 0) {
111121
return;
112122
}
123+
}
113124

114-
const extensionToInstall = tagResult.total === 1 ? tagResult.firstPage[0] : tagResult.firstPage.filter(e => e.publisher === 'MS-CEINTL' && e.name.indexOf('vscode-language-pack') === 0)[0];
115-
const extensionToFetchTranslationsFrom = extensionToInstall || tagResult.firstPage[0];
125+
const extensionToInstall = tagResult.total === 1 ? tagResult.firstPage[0] : tagResult.firstPage.find(e => e.publisher === 'MS-CEINTL' && e.name.startsWith('vscode-language-pack'));
126+
const extensionToFetchTranslationsFrom = extensionToInstall ?? tagResult.firstPage[0];
116127

117-
if (!extensionToFetchTranslationsFrom.assets.manifest) {
118-
return;
119-
}
128+
if (!extensionToFetchTranslationsFrom.assets.manifest) {
129+
return;
130+
}
120131

121-
Promise.all([this.galleryService.getManifest(extensionToFetchTranslationsFrom, CancellationToken.None), this.galleryService.getCoreTranslation(extensionToFetchTranslationsFrom, locale)])
122-
.then(([manifest, translation]) => {
123-
const loc = manifest && manifest.contributes && manifest.contributes.localizations && manifest.contributes.localizations.filter(x => x.languageId.toLowerCase() === locale)[0];
124-
const languageName = loc ? (loc.languageName || locale) : locale;
125-
const languageDisplayName = loc ? (loc.localizedLanguageName || loc.languageName || locale) : locale;
126-
const translationsFromPack: { [key: string]: string } = translation?.contents?.['vs/workbench/contrib/localization/electron-sandbox/minimalTranslations'] ?? {};
127-
const promptMessageKey = extensionToInstall ? 'installAndRestartMessage' : 'showLanguagePackExtensions';
128-
const useEnglish = !translationsFromPack[promptMessageKey];
132+
Promise.all([this.galleryService.getManifest(extensionToFetchTranslationsFrom, CancellationToken.None), this.galleryService.getCoreTranslation(extensionToFetchTranslationsFrom, searchLocale)])
133+
.then(([manifest, translation]) => {
134+
const loc = manifest && manifest.contributes && manifest.contributes.localizations && manifest.contributes.localizations.find(x => locale.startsWith(x.languageId.toLowerCase()));
135+
const languageName = loc ? (loc.languageName || locale) : locale;
136+
const languageDisplayName = loc ? (loc.localizedLanguageName || loc.languageName || locale) : locale;
137+
const translationsFromPack: { [key: string]: string } = translation?.contents?.['vs/workbench/contrib/localization/electron-sandbox/minimalTranslations'] ?? {};
138+
const promptMessageKey = extensionToInstall ? 'installAndRestartMessage' : 'showLanguagePackExtensions';
139+
const useEnglish = !translationsFromPack[promptMessageKey];
129140

130-
const translations: { [key: string]: string } = {};
131-
Object.keys(minimumTranslatedStrings).forEach(key => {
132-
if (!translationsFromPack[key] || useEnglish) {
133-
translations[key] = minimumTranslatedStrings[key].replace('{0}', languageName);
134-
} else {
135-
translations[key] = `${translationsFromPack[key].replace('{0}', languageDisplayName)} (${minimumTranslatedStrings[key].replace('{0}', languageName)})`;
141+
const translations: { [key: string]: string } = {};
142+
Object.keys(minimumTranslatedStrings).forEach(key => {
143+
if (!translationsFromPack[key] || useEnglish) {
144+
translations[key] = minimumTranslatedStrings[key].replace('{0}', languageName);
145+
} else {
146+
translations[key] = `${translationsFromPack[key].replace('{0}', languageDisplayName)} (${minimumTranslatedStrings[key].replace('{0}', languageName)})`;
147+
}
148+
});
149+
150+
const logUserReaction = (userReaction: string) => {
151+
/* __GDPR__
152+
"languagePackSuggestion:popup" : {
153+
"owner": "TylerLeonhardt",
154+
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
155+
"language": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
136156
}
137-
});
157+
*/
158+
this.telemetryService.publicLog('languagePackSuggestion:popup', { userReaction, language: locale });
159+
};
160+
161+
const searchAction = {
162+
label: translations['searchMarketplace'],
163+
run: () => {
164+
logUserReaction('search');
165+
this.paneCompositeService.openPaneComposite(EXTENSIONS_VIEWLET_ID, ViewContainerLocation.Sidebar, true)
166+
.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
167+
.then(viewlet => {
168+
viewlet.search(`tag:lp-${searchLocale}`);
169+
viewlet.focus();
170+
});
171+
}
172+
};
138173

139-
const logUserReaction = (userReaction: string) => {
140-
/* __GDPR__
141-
"languagePackSuggestion:popup" : {
142-
"owner": "TylerLeonhardt",
143-
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
144-
"language": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
145-
}
146-
*/
147-
this.telemetryService.publicLog('languagePackSuggestion:popup', { userReaction, language: locale });
148-
};
174+
const installAndRestartAction = {
175+
label: translations['installAndRestart'],
176+
run: () => {
177+
logUserReaction('installAndRestart');
178+
this.installExtension(extensionToInstall!).then(() => this.hostService.restart());
179+
}
180+
};
149181

150-
const searchAction = {
151-
label: translations['searchMarketplace'],
152-
run: () => {
153-
logUserReaction('search');
154-
this.paneCompositeService.openPaneComposite(EXTENSIONS_VIEWLET_ID, ViewContainerLocation.Sidebar, true)
155-
.then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
156-
.then(viewlet => {
157-
viewlet.search(`tag:lp-${locale}`);
158-
viewlet.focus();
159-
});
160-
}
161-
};
182+
const promptMessage = translations[promptMessageKey];
162183

163-
const installAndRestartAction = {
164-
label: translations['installAndRestart'],
184+
this.notificationService.prompt(
185+
Severity.Info,
186+
promptMessage,
187+
[extensionToInstall ? installAndRestartAction : searchAction,
188+
{
189+
label: localize('neverAgain', "Don't Show Again"),
190+
isSecondary: true,
165191
run: () => {
166-
logUserReaction('installAndRestart');
167-
this.installExtension(extensionToInstall).then(() => this.hostService.restart());
192+
languagePackSuggestionIgnoreList.push(locale);
193+
this.storageService.store(
194+
LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY,
195+
JSON.stringify(languagePackSuggestionIgnoreList),
196+
StorageScope.APPLICATION,
197+
StorageTarget.USER
198+
);
199+
logUserReaction('neverShowAgain');
168200
}
169-
};
170-
171-
const promptMessage = translations[promptMessageKey];
172-
173-
this.notificationService.prompt(
174-
Severity.Info,
175-
promptMessage,
176-
[extensionToInstall ? installAndRestartAction : searchAction,
177-
{
178-
label: localize('neverAgain', "Don't Show Again"),
179-
isSecondary: true,
180-
run: () => {
181-
languagePackSuggestionIgnoreList.push(locale);
182-
this.storageService.store(
183-
LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY,
184-
JSON.stringify(languagePackSuggestionIgnoreList),
185-
StorageScope.APPLICATION,
186-
StorageTarget.USER
187-
);
188-
logUserReaction('neverShowAgain');
189-
}
190-
}],
191-
{
192-
onCancel: () => {
193-
logUserReaction('cancelled');
194-
}
201+
}],
202+
{
203+
onCancel: () => {
204+
logUserReaction('cancelled');
195205
}
196-
);
197-
198-
});
199-
});
206+
}
207+
);
208+
});
200209
});
201-
202210
}
203211

204-
private async isLanguageInstalled(language: string | undefined): Promise<boolean> {
212+
private async isLocaleInstalled(locale: string): Promise<boolean> {
205213
const installed = await this.extensionManagementService.getInstalled();
206214
return installed.some(i => !!(i.manifest
207215
&& i.manifest.contributes
208216
&& i.manifest.contributes.localizations
209217
&& i.manifest.contributes.localizations.length
210-
&& i.manifest.contributes.localizations.some(l => l.languageId.toLowerCase() === language)));
218+
&& i.manifest.contributes.localizations.some(l => locale.startsWith(l.languageId.toLowerCase()))));
211219
}
212220

213221
private installExtension(extension: IGalleryExtension): Promise<void> {

0 commit comments

Comments
 (0)