Skip to content

Commit cf141c3

Browse files
committed
Fix display language
1 parent b82fe47 commit cf141c3

File tree

2 files changed

+132
-154
lines changed

2 files changed

+132
-154
lines changed

patches/display-language.diff

+131-154
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,15 @@
11
Add display language support
22

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.
197

208
Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
219
===================================================================
2210
--- code-server.orig/lib/vscode/src/vs/server/node/serverServices.ts
2311
+++ 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
2513
import { IURITransformer } from 'vs/base/common/uriIpc';
2614
import { getMachineId, getSqmMachineId, getdevDeviceId } from 'vs/base/node/id';
2715
import { Promises } from 'vs/base/node/pfs';
@@ -30,7 +18,7 @@ Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
3018
import { ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net';
3119
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
3220
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
3422
const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
3523
socketServer.registerChannel('extensions', channel);
3624

@@ -40,100 +28,6 @@ Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
4028
// clean up extensions folder
4129
remoteExtensionsScanner.whenExtensionsReady().then(() => extensionManagementService.cleanUp());
4230

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,
13731
Index: code-server/lib/vscode/src/vs/platform/environment/common/environmentService.ts
13832
===================================================================
13933
--- 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
15145
===================================================================
15246
--- code-server.orig/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
15347
+++ 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+
+ })
16968
}
69+
70+
return result;
17071
}
17172
+
17273
+/**
173-
+ * The code below is copied from from src/main.js.
74+
+ * Copied from from src/main.js.
17475
+ */
175-
+
17676
+export const getLocaleFromConfig = async (argvResource: string): Promise<string> => {
17777
+ try {
178-
+ const content = stripComments(await fs.promises.readFile(argvResource, 'utf8'));
78+
+ const content = stripComments(await fs.readFile(argvResource, 'utf8'));
17979
+ return JSON.parse(content).locale;
18080
+ } catch (error) {
18181
+ if (error.code !== "ENOENT") {
@@ -185,6 +85,9 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
18585
+ }
18686
+};
18787
+
88+
+/**
89+
+ * Copied from from src/main.js.
90+
+ */
18891
+const stripComments = (content: string): string => {
18992
+ const regexp = /('(?:[^\\']*(?:\\.)?)*')|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
19093
+
@@ -208,51 +111,113 @@ Index: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
208111
+ }
209112
+ });
210113
+};
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+
+}
211152
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
212153
===================================================================
213154
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
214155
+++ 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'
216157
import { streamToBuffer } from 'vs/base/common/buffer';
217158
import { IProductConfiguration } from 'vs/base/common/product';
218159
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';
220161
import { CharCode } from 'vs/base/common/charCode';
221162
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
222163

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;
226166

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 {
237181
};
238182

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 } = {
240205
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
241206
===================================================================
242207
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
243208
+++ 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
246210
'disable-file-downloads': { type: 'boolean' },
247211
'disable-file-uploads': { type: 'boolean' },
212+
'disable-getting-started-override': { type: 'boolean' },
248213
+ 'locale': { type: 'string' },
249214

250215
/* ----- server setup ----- */
251216

252-
@@ -103,6 +104,7 @@ export interface ServerParsedArgs {
253-
'auth'?: string;
217+
@@ -105,6 +106,7 @@ export interface ServerParsedArgs {
254218
'disable-file-downloads'?: boolean;
255219
'disable-file-uploads'?: boolean;
220+
'disable-getting-started-override'?: boolean,
256221
+ 'locale'?: string
257222

258223
/* ----- server setup ----- */
@@ -367,7 +332,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
367332
}
368333

369334
// 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
371336
update(): void {
372337
this.enabled = false;
373338
this.class = SetLanguageAction.DisabledClass;
@@ -385,15 +350,15 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
385350
}
386351

387352
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`;
390355

391356
constructor(
392357
- @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
393358
@ILocaleService private readonly localeService: ILocaleService,
394359
) {
395360
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
397362
update(): void {
398363
this.enabled = false;
399364
this.class = ClearLanguageAction.DisabledClass;
@@ -411,3 +376,15 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extens
411376
}
412377

413378
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',

patches/series

+1
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ cli-window-open.diff
1919
getting-started.diff
2020
keepalive.diff
2121
clipboard.diff
22+
display-language.diff

0 commit comments

Comments
 (0)