1
1
Add display language support
2
2
3
- We can remove this once upstream supports all language packs.
4
-
5
- 1. Proxies language packs to the service on the backend.
6
- 2. NLS configuration is embedded into the HTML for the browser to pick up. This
7
- code to generate this configuration is copied from the native portion.
8
- 3. Remove configuredLocale since we have our own thing.
9
- 4. Move the argv.json file to the server instead of in-browser storage. This is
10
- where the current locale is stored and currently the server needs to be able
11
- to read it.
12
- 5. Add the locale flag.
13
- 6. Remove the redundant locale verification. It does the same as the existing
14
- one but is worse because it does not handle non-existent or empty files.
15
- 7. Replace some caching and Node requires because code-server does not restart
16
- when changing the language unlike native Code.
17
- 8. Make language extensions installable like normal rather than using the
18
- special set/clear language actions.
3
+ VS Code web appears to implement language support by setting a cookie and
4
+ downloading language packs from a URL configured in the product.json. This patch
5
+ supports language pack extensions and uses files on the remote to set the
6
+ language instead, so it works like the desktop version.
19
7
20
8
Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
21
9
===================================================================
22
10
--- code-server.orig/lib/vscode/src/vs/server/node/serverServices.ts
23
11
+++ code-server/lib/vscode/src/vs/server/node/serverServices.ts
24
- @@ -11 ,7 +11 ,7 @@ import * as path from 'vs/base/common/pa
12
+ @@ -12 ,7 +12 ,7 @@ import * as path from 'vs/base/common/pa
25
13
import { IURITransformer } from 'vs/base/common/uriIpc';
26
14
import { getMachineId, getSqmMachineId, getdevDeviceId } from 'vs/base/node/id';
27
15
import { Promises } from 'vs/base/node/pfs';
@@ -30,7 +18,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
30
18
import { ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net';
31
19
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
32
20
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
33
- @@ -238 ,6 +238 ,9 @@ export async function setupServerService
21
+ @@ -239 ,6 +239 ,9 @@ export async function setupServerService
34
22
const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
35
23
socketServer.registerChannel('extensions', channel);
36
24
@@ -40,100 +28,6 @@ Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
40
28
// clean up extensions folder
41
29
remoteExtensionsScanner.whenExtensionsReady().then(() => extensionManagementService.cleanUp());
42
30
43
- Index: code-server/lib/vscode/src/vs/base/common/platform.ts
44
- ===================================================================
45
- --- code-server.orig/lib/vscode/src/vs/base/common/platform.ts
46
- +++ code-server/lib/vscode/src/vs/base/common/platform.ts
47
- @@ -2,8 +2,6 @@
48
- * Copyright (c) Microsoft Corporation. All rights reserved.
49
- * Licensed under the MIT License. See License.txt in the project root for license information.
50
- *--------------------------------------------------------------------------------------------*/
51
- - import * as nls from 'vs/nls';
52
- -
53
- export const LANGUAGE_DEFAULT = 'en';
54
-
55
- let _isWindows = false;
56
- @@ -112,17 +110,21 @@ else if (typeof navigator === 'object' &
57
- _isMobile = _userAgent?.indexOf('Mobi') >= 0;
58
- _isWeb = true;
59
-
60
- - const configuredLocale = nls.getConfiguredDefaultLocale(
61
- - // This call _must_ be done in the file that calls `nls.getConfiguredDefaultLocale`
62
- - // to ensure that the NLS AMD Loader plugin has been loaded and configured.
63
- - // This is because the loader plugin decides what the default locale is based on
64
- - // how it's able to resolve the strings.
65
- - nls.localize({ key: 'ensureLoaderPluginIsLoaded', comment: ['{Locked}'] }, '_')
66
- - );
67
- -
68
- - _locale = configuredLocale || LANGUAGE_DEFAULT;
69
- + _locale = LANGUAGE_DEFAULT;
70
- _language = _locale;
71
- _platformLocale = navigator.language;
72
- + const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration');
73
- + const rawNlsConfig = el && el.getAttribute('data-settings');
74
- + if (rawNlsConfig) {
75
- + try {
76
- + const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
77
- + const resolved = nlsConfig.availableLanguages['*'];
78
- + _locale = nlsConfig.locale;
79
- + _platformLocale = nlsConfig.osLocale;
80
- + _language = resolved ? resolved : LANGUAGE_DEFAULT;
81
- + _translationsConfigFile = nlsConfig._translationsConfigFile;
82
- + } catch (error) { /* Oh well. */ }
83
- + }
84
- }
85
-
86
- // Unknown environment
87
- Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html
88
- ===================================================================
89
- --- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.html
90
- +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html
91
- @@ -23,6 +23,9 @@
92
- <!-- Workbench Auth Session -->
93
- <meta id="vscode-workbench-auth-session" data-settings="{{WORKBENCH_AUTH_SESSION}}">
94
-
95
- + <!-- NLS Configuration -->
96
- + <meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
97
- +
98
- <!-- Workbench Icon/Manifest/CSS -->
99
- <link rel="icon" href="{{BASE}}/_static/src/browser/media/favicon-dark-support.svg" />
100
- <link rel="alternate icon" href="{{BASE}}/_static/src/browser/media/favicon.ico" type="image/x-icon" />
101
- @@ -48,15 +51,27 @@
102
- // Normalize locale to lowercase because translationServiceUrl is case-sensitive.
103
- // ref: https://github.com/microsoft/vscode/issues/187795
104
- const locale = localStorage.getItem('vscode.nls.locale') || navigator.language.toLowerCase();
105
- - if (!locale.startsWith('en')) {
106
- - nlsConfig['vs/nls'] = {
107
- - availableLanguages: {
108
- - '*': locale
109
- - },
110
- - translationServiceUrl: '{{WORKBENCH_NLS_BASE_URL}}'
111
- - };
112
- - }
113
-
114
- + try {
115
- + nlsConfig['vs/nls'] = JSON.parse(document.getElementById("vscode-remote-nls-configuration").getAttribute("data-settings"))
116
- + if (nlsConfig['vs/nls']._resolvedLanguagePackCoreLocation) {
117
- + const bundles = Object.create(null)
118
- + nlsConfig['vs/nls'].loadBundle = (bundle, _language, cb) => {
119
- + const result = bundles[bundle]
120
- + if (result) {
121
- + return cb(undefined, result)
122
- + }
123
- + const path = nlsConfig['vs/nls']._resolvedLanguagePackCoreLocation + "/" + bundle.replace(/\//g, "!") + ".nls.json"
124
- + fetch(`{{WORKBENCH_WEB_BASE_URL}}/../vscode-remote-resource?path=${encodeURIComponent(path)}`)
125
- + .then((response) => response.json())
126
- + .then((json) => {
127
- + bundles[bundle] = json
128
- + cb(undefined, json)
129
- + })
130
- + .catch(cb)
131
- + }
132
- + }
133
- + } catch (error) { /* Probably fine. */ }
134
- require.config({
135
- baseUrl: `${baseUrl}/out`,
136
- recordStats: true,
137
31
Index: code-server/lib/vscode/src/vs/platform/environment/common/environmentService.ts
138
32
===================================================================
139
33
--- code-server.orig/lib/vscode/src/vs/platform/environment/common/environmentService.ts
@@ -151,31 +45,37 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
151
45
===================================================================
152
46
--- code-server.orig/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
153
47
+++ code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
154
- @@ -32,6 +32,12 @@ export function getNLSConfiguration(lang
155
- if (InternalNLSConfiguration.is(value)) {
156
- value._languagePackSupport = true;
157
- }
158
- + // If the configuration has no results keep trying since code-server
159
- + // doesn't restart when a language is installed so this result would
160
- + // persist (the plugin might not be installed yet for example).
161
- + if (value.locale !== 'en' && value.locale !== 'en-us' && Object.keys(value.availableLanguages).length === 0) {
162
- + _cache.delete(key);
163
- + }
164
- return value;
165
- });
166
- _cache.set(key, result);
167
- @@ -46,3 +52,43 @@ export namespace InternalNLSConfiguratio
168
- return candidate && typeof candidate._languagePackId === 'string';
48
+ @@ -3,6 +3,8 @@
49
+ * Licensed under the MIT License. See License.txt in the project root for license information.
50
+ *--------------------------------------------------------------------------------------------*/
51
+
52
+ + import { promises as fs } from 'fs';
53
+ + import * as path from 'path';
54
+ import { FileAccess } from 'vs/base/common/network';
55
+ import { join } from 'vs/base/common/path';
56
+ import type { INLSConfiguration } from 'vs/nls';
57
+ @@ -33,7 +35,94 @@ export async function getNLSConfiguratio
58
+ if (!result) {
59
+ result = resolveNLSConfiguration({ userLocale: language, osLocale: language, commit: product.commit, userDataPath, nlsMetadataPath });
60
+ nlsConfigurationCache.set(cacheKey, result);
61
+ + // If the language pack does not yet exist, it defaults to English, which is
62
+ + // then cached and you have to restart even if you then install the pack.
63
+ + result.then((r) => {
64
+ + if (!language.startsWith('en') && r.resolvedLanguage.startsWith('en')) {
65
+ + nlsConfigurationCache.delete(cacheKey);
66
+ + }
67
+ + })
169
68
}
69
+
70
+ return result;
170
71
}
171
72
+
172
73
+ /**
173
- + * The code below is copied from from src/main.js.
74
+ + * Copied from from src/main.js.
174
75
+ */
175
- +
176
76
+ export const getLocaleFromConfig = async (argvResource: string): Promise<string> => {
177
77
+ try {
178
- + const content = stripComments(await fs.promises. readFile(argvResource, 'utf8'));
78
+ + const content = stripComments(await fs.readFile(argvResource, 'utf8'));
179
79
+ return JSON.parse(content).locale;
180
80
+ } catch (error) {
181
81
+ if (error.code !== "ENOENT") {
@@ -185,6 +85,9 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
185
85
+ }
186
86
+ };
187
87
+
88
+ + /**
89
+ + * Copied from from src/main.js.
90
+ + */
188
91
+ const stripComments = (content: string): string => {
189
92
+ const regexp = /('(?:[^\\']*(?:\\.)?)*')|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
190
93
+
@@ -208,51 +111,113 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
208
111
+ }
209
112
+ });
210
113
+ };
114
+ +
115
+ + /**
116
+ + * Generate translations then return a path to a JavaScript file that sets the
117
+ + * translations into global variables. This file is loaded by the browser to
118
+ + * set global variables that the loader uses when looking for translations.
119
+ + *
120
+ + * Normally, VS Code pulls these files from a CDN but we want them to be local.
121
+ + */
122
+ + export async function getBrowserNLSConfiguration(locale: string, userDataPath: string): Promise<string> {
123
+ + if (locale.startsWith('en')) {
124
+ + return ''; // Use fallback translations.
125
+ + }
126
+ +
127
+ + const nlsConfig = await getNLSConfiguration(locale, userDataPath);
128
+ + const messagesFile = nlsConfig?.languagePack?.messagesFile;
129
+ + const resolvedLanguage = nlsConfig?.resolvedLanguage;
130
+ + if (!messagesFile || !resolvedLanguage) {
131
+ + return ''; // Use fallback translations.
132
+ + }
133
+ +
134
+ + const nlsFile = path.join(path.dirname(messagesFile), "nls.messages.js");
135
+ + try {
136
+ + await fs.stat(nlsFile);
137
+ + return nlsFile; // We already generated the file.
138
+ + } catch (error) {
139
+ + // ENOENT is fine, that just means we need to generate the file.
140
+ + if (error.code !== 'ENOENT') {
141
+ + throw error;
142
+ + }
143
+ + }
144
+ +
145
+ + const messages = (await fs.readFile(messagesFile)).toString();
146
+ + const content = `globalThis._VSCODE_NLS_MESSAGES=${messages};
147
+ + globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(resolvedLanguage)};`
148
+ + await fs.writeFile(nlsFile, content, "utf-8");
149
+ +
150
+ + return nlsFile;
151
+ + }
211
152
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
212
153
===================================================================
213
154
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
214
155
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
215
- @@ -27 ,6 +27 ,7 @@ import { URI } from 'vs/base/common/uri'
156
+ @@ -26 ,6 +26 ,7 @@ import { URI } from 'vs/base/common/uri'
216
157
import { streamToBuffer } from 'vs/base/common/buffer';
217
158
import { IProductConfiguration } from 'vs/base/common/product';
218
159
import { isString } from 'vs/base/common/types';
219
- + import { getLocaleFromConfig, getNLSConfiguration } from 'vs/server/node/remoteLanguagePacks';
160
+ + import { getLocaleFromConfig, getBrowserNLSConfiguration } from 'vs/server/node/remoteLanguagePacks';
220
161
import { CharCode } from 'vs/base/common/charCode';
221
162
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
222
163
223
- @@ -348,6 +349,8 @@ export class WebClientServer {
224
- callbackRoute: this._callbackRoute
225
- };
164
+ @@ -97,6 +98,7 @@ export class WebClientServer {
165
+ private readonly _webExtensionResourceUrlTemplate: URI | undefined;
226
166
227
- + const locale = this._environmentService.args.locale || await getLocaleFromConfig(this._environmentService.argvResource.fsPath);
228
- + const nlsConfiguration = await getNLSConfiguration(locale, this._environmentService.userDataPath)
229
- const nlsBaseUrl = this._productService.extensionsGallery?.nlsBaseUrl;
230
- const values: { [key: string]: string } = {
231
- WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
232
- @@ -356,6 +359,7 @@ export class WebClientServer {
233
- WORKBENCH_NLS_BASE_URL: vscodeBase + (nlsBaseUrl ? `${nlsBaseUrl}${!nlsBaseUrl.endsWith('/') ? '/' : ''}${this._productService.commit}/${this._productService.version}/` : ''),
234
- BASE: base,
235
- VS_BASE: vscodeBase,
236
- + NLS_CONFIGURATION: asJSON(nlsConfiguration),
167
+ private readonly _staticRoute: string;
168
+ + private readonly _serverRoot: string;
169
+ private readonly _callbackRoute: string;
170
+ private readonly _webExtensionRoute: string;
171
+
172
+ @@ -111,6 +113,7 @@ export class WebClientServer {
173
+ ) {
174
+ this._webExtensionResourceUrlTemplate = this._productService.extensionsGallery?.resourceUrlTemplate ? URI.parse(this._productService.extensionsGallery.resourceUrlTemplate) : undefined;
175
+
176
+ + this._serverRoot = serverRootPath;
177
+ this._staticRoute = `${serverRootPath}/static`;
178
+ this._callbackRoute = `${serverRootPath}/callback`;
179
+ this._webExtensionRoute = `/web-extension-resource`;
180
+ @@ -349,14 +352,22 @@ export class WebClientServer {
237
181
};
238
182
239
- if (useTestResolver) {
183
+ const cookies = cookie.parse(req.headers.cookie || '');
184
+ - const locale = cookies['vscode.nls.locale'] || req.headers['accept-language']?.split(',')[0]?.toLowerCase() || 'en';
185
+ + const locale = this._environmentService.args.locale || await getLocaleFromConfig(this._environmentService.argvResource.fsPath) || cookies['vscode.nls.locale'] || req.headers['accept-language']?.split(',')[0]?.toLowerCase() || 'en';
186
+ let WORKBENCH_NLS_BASE_URL: string | undefined;
187
+ let WORKBENCH_NLS_URL: string;
188
+ if (!locale.startsWith('en') && this._productService.nlsCoreBaseUrl) {
189
+ WORKBENCH_NLS_BASE_URL = this._productService.nlsCoreBaseUrl;
190
+ WORKBENCH_NLS_URL = `${WORKBENCH_NLS_BASE_URL}${this._productService.commit}/${this._productService.version}/${locale}/nls.messages.js`;
191
+ } else {
192
+ - WORKBENCH_NLS_URL = ''; // fallback will apply
193
+ + try {
194
+ + const nlsFile = await getBrowserNLSConfiguration(locale, this._environmentService.userDataPath);
195
+ + WORKBENCH_NLS_URL = nlsFile
196
+ + ? `${vscodeBase}${this._serverRoot}/vscode-remote-resource?path=${encodeURIComponent(nlsFile)}`
197
+ + : '';
198
+ + } catch (error) {
199
+ + console.error("Failed to generate translations", error);
200
+ + WORKBENCH_NLS_URL = '';
201
+ + }
202
+ }
203
+
204
+ const values: { [key: string]: string } = {
240
205
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
241
206
===================================================================
242
207
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
243
208
+++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
244
- @@ -18,6 +18,7 @@ export const serverOptions: OptionDescri
245
- 'auth': { type: 'string' },
209
+ @@ -19,6 +19,7 @@ export const serverOptions: OptionDescri
246
210
'disable-file-downloads': { type: 'boolean' },
247
211
'disable-file-uploads': { type: 'boolean' },
212
+ 'disable-getting-started-override': { type: 'boolean' },
248
213
+ 'locale': { type: 'string' },
249
214
250
215
/* ----- server setup ----- */
251
216
252
- @@ -103,6 +104,7 @@ export interface ServerParsedArgs {
253
- 'auth'?: string;
217
+ @@ -105,6 +106,7 @@ export interface ServerParsedArgs {
254
218
'disable-file-downloads'?: boolean;
255
219
'disable-file-uploads'?: boolean;
220
+ 'disable-getting-started-override'?: boolean,
256
221
+ 'locale'?: string
257
222
258
223
/* ----- server setup ----- */
@@ -367,7 +332,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
367
332
}
368
333
369
334
// Prefers to run on UI
370
- @@ -1928 ,17 +1925 ,6 @@ export class SetLanguageAction extends E
335
+ @@ -1951 ,17 +1948 ,6 @@ export class SetLanguageAction extends E
371
336
update(): void {
372
337
this.enabled = false;
373
338
this.class = SetLanguageAction.DisabledClass;
@@ -385,15 +350,15 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
385
350
}
386
351
387
352
override async run(): Promise<any> {
388
- @@ -1955 ,7 +1941 ,6 @@ export class ClearLanguageAction extends
389
- private static readonly DisabledClass = `${ClearLanguageAction .EnabledClass} disabled`;
353
+ @@ -1978 ,7 +1964 ,6 @@ export class ClearLanguageAction extends
354
+ private static readonly DisabledClass = `${this .EnabledClass} disabled`;
390
355
391
356
constructor(
392
357
- @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
393
358
@ILocaleService private readonly localeService: ILocaleService,
394
359
) {
395
360
super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false);
396
- @@ -1965 ,17 +1950 ,6 @@ export class ClearLanguageAction extends
361
+ @@ -1988 ,17 +1973 ,6 @@ export class ClearLanguageAction extends
397
362
update(): void {
398
363
this.enabled = false;
399
364
this.class = ClearLanguageAction.DisabledClass;
@@ -411,3 +376,15 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
411
376
}
412
377
413
378
override async run(): Promise<any> {
379
+ Index: code-server/lib/vscode/build/gulpfile.reh.js
380
+ ===================================================================
381
+ --- code-server.orig/lib/vscode/build/gulpfile.reh.js
382
+ +++ code-server/lib/vscode/build/gulpfile.reh.js
383
+ @@ -56,6 +56,7 @@ const serverResources = [
384
+
385
+ // NLS
386
+ 'out-build/nls.messages.json',
387
+ + 'out-build/nls.keys.json', // Required to generate translations.
388
+
389
+ // Process monitor
390
+ 'out-build/vs/base/node/cpuUsage.sh',
0 commit comments