diff --git a/patched-vscode/src/vs/base/common/platform.ts b/patched-vscode/src/vs/base/common/platform.ts
index 2251c7db5..aac70df57 100644
--- a/patched-vscode/src/vs/base/common/platform.ts
+++ b/patched-vscode/src/vs/base/common/platform.ts
@@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import * as nls from 'vs/nls';
-
export const LANGUAGE_DEFAULT = 'en';
let _isWindows = false;
@@ -112,17 +110,21 @@ else if (typeof navigator === 'object' && !isElectronRenderer) {
_isMobile = _userAgent?.indexOf('Mobi') >= 0;
_isWeb = true;
- const configuredLocale = nls.getConfiguredDefaultLocale(
- // This call _must_ be done in the file that calls `nls.getConfiguredDefaultLocale`
- // to ensure that the NLS AMD Loader plugin has been loaded and configured.
- // This is because the loader plugin decides what the default locale is based on
- // how it's able to resolve the strings.
- nls.localize({ key: 'ensureLoaderPluginIsLoaded', comment: ['{Locked}'] }, '_')
- );
-
- _locale = configuredLocale || LANGUAGE_DEFAULT;
+ _locale = LANGUAGE_DEFAULT;
_language = _locale;
_platformLocale = navigator.language;
+ const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration');
+ const rawNlsConfig = el && el.getAttribute('data-settings');
+ if (rawNlsConfig) {
+ try {
+ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
+ const resolved = nlsConfig.availableLanguages['*'];
+ _locale = nlsConfig.locale;
+ _platformLocale = nlsConfig.osLocale;
+ _language = resolved ? resolved : LANGUAGE_DEFAULT;
+ _translationsConfigFile = nlsConfig._translationsConfigFile;
+ } catch (error) { /* Oh well. */ }
+ }
}
// Unknown environment
diff --git a/patched-vscode/src/vs/code/browser/workbench/workbench.html b/patched-vscode/src/vs/code/browser/workbench/workbench.html
index cfee57cc1..96fe4a0b9 100644
--- a/patched-vscode/src/vs/code/browser/workbench/workbench.html
+++ b/patched-vscode/src/vs/code/browser/workbench/workbench.html
@@ -19,6 +19,9 @@
+
+
+
@@ -46,14 +49,26 @@
// Normalize locale to lowercase because translationServiceUrl is case-sensitive.
// ref: https://github.com/microsoft/vscode/issues/187795
const locale = localStorage.getItem('vscode.nls.locale') || navigator.language.toLowerCase();
- if (!locale.startsWith('en')) {
- nlsConfig['vs/nls'] = {
- availableLanguages: {
- '*': locale
- },
- translationServiceUrl: '{{WORKBENCH_NLS_BASE_URL}}'
- };
- }
+ try {
+ nlsConfig['vs/nls'] = JSON.parse(document.getElementById("vscode-remote-nls-configuration").getAttribute("data-settings"))
+ if (nlsConfig['vs/nls']._resolvedLanguagePackCoreLocation) {
+ const bundles = Object.create(null)
+ nlsConfig['vs/nls'].loadBundle = (bundle, _language, cb) => {
+ const result = bundles[bundle]
+ if (result) {
+ return cb(undefined, result)
+ }
+ const path = nlsConfig['vs/nls']._resolvedLanguagePackCoreLocation + "/" + bundle.replace(/\//g, "!") + ".nls.json"
+ fetch(`{{WORKBENCH_WEB_BASE_URL}}/../vscode-remote-resource?path=${encodeURIComponent(path)}`)
+ .then((response) => response.json())
+ .then((json) => {
+ bundles[bundle] = json
+ cb(undefined, json)
+ })
+ .catch(cb)
+ }
+ }
+ } catch (error) { /* Probably fine. */ }
require.config({
baseUrl: `${baseUrl}/out`,
diff --git a/patched-vscode/src/vs/platform/environment/common/environmentService.ts b/patched-vscode/src/vs/platform/environment/common/environmentService.ts
index cd55aa9b2..a09dde3f2 100644
--- a/patched-vscode/src/vs/platform/environment/common/environmentService.ts
+++ b/patched-vscode/src/vs/platform/environment/common/environmentService.ts
@@ -101,7 +101,7 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
return URI.file(join(vscodePortable, 'argv.json'));
}
- return joinPath(this.userHome, this.productService.dataFolderName, 'argv.json');
+ return joinPath(this.appSettingsHome, 'argv.json');
}
@memoize
diff --git a/patched-vscode/src/vs/platform/languagePacks/browser/languagePacks.ts b/patched-vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
index 62dedb224..c83c335bb 100644
--- a/patched-vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
+++ b/patched-vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
@@ -5,18 +5,24 @@
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { URI } from 'vs/base/common/uri';
+import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader';
-import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
+import { ILanguagePackItem, ILanguagePackService, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
import { ILogService } from 'vs/platform/log/common/log';
+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
export class WebLanguagePacksService extends LanguagePackBaseService {
+ private readonly languagePackService: ILanguagePackService;
+
constructor(
+ @IRemoteAgentService remoteAgentService: IRemoteAgentService,
@IExtensionResourceLoaderService private readonly extensionResourceLoaderService: IExtensionResourceLoaderService,
@IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
@ILogService private readonly logService: ILogService
) {
super(extensionGalleryService);
+ this.languagePackService = ProxyChannel.toService(remoteAgentService.getConnection()!.getChannel('languagePacks'))
}
async getBuiltInExtensionTranslationsUri(id: string, language: string): Promise {
@@ -72,6 +78,6 @@ export class WebLanguagePacksService extends LanguagePackBaseService {
// Web doesn't have a concept of language packs, so we just return an empty array
getInstalledLanguages(): Promise {
- return Promise.resolve([]);
+ return this.languagePackService.getInstalledLanguages()
}
}
diff --git a/patched-vscode/src/vs/server/node/remoteLanguagePacks.ts b/patched-vscode/src/vs/server/node/remoteLanguagePacks.ts
index 682b6f2b0..2b39c3f8d 100644
--- a/patched-vscode/src/vs/server/node/remoteLanguagePacks.ts
+++ b/patched-vscode/src/vs/server/node/remoteLanguagePacks.ts
@@ -8,36 +8,30 @@ import { FileAccess } from 'vs/base/common/network';
import * as path from 'vs/base/common/path';
import * as lp from 'vs/base/node/languagePacks';
-import product from 'vs/platform/product/common/product';
const metaData = path.join(FileAccess.asFileUri('').fsPath, 'nls.metadata.json');
const _cache: Map> = new Map();
-function exists(file: string) {
- return new Promise(c => fs.exists(file, c));
-}
-
export function getNLSConfiguration(language: string, userDataPath: string): Promise {
- return exists(metaData).then((fileExists) => {
- if (!fileExists || !product.commit) {
- // console.log(`==> MetaData or commit unknown. Using default language.`);
- // The OS Locale on the remote side really doesn't matter, so we return the default locale
- return Promise.resolve({ locale: 'en', osLocale: 'en', availableLanguages: {} });
- }
- const key = `${language}||${userDataPath}`;
- let result = _cache.get(key);
- if (!result) {
- // The OS Locale on the remote side really doesn't matter, so we pass in the same language
- result = lp.getNLSConfiguration(product.commit, userDataPath, metaData, language, language).then(value => {
- if (InternalNLSConfiguration.is(value)) {
- value._languagePackSupport = true;
- }
- return value;
- });
- _cache.set(key, result);
- }
- return result;
- });
+ const key = `${language}||${userDataPath}`;
+ let result = _cache.get(key);
+ if (!result) {
+ // The OS Locale on the remote side really doesn't matter, so we pass in the same language
+ result = lp.getNLSConfiguration("dummy_commmit", userDataPath, metaData, language, language).then(value => {
+ if (InternalNLSConfiguration.is(value)) {
+ value._languagePackSupport = true;
+ }
+ // If the configuration has no results keep trying since code-server
+ // doesn't restart when a language is installed so this result would
+ // persist (the plugin might not be installed yet for example).
+ if (value.locale !== 'en' && value.locale !== 'en-us' && Object.keys(value.availableLanguages).length === 0) {
+ _cache.delete(key);
+ }
+ return value;
+ });
+ _cache.set(key, result);
+ }
+ return result;
}
export namespace InternalNLSConfiguration {
@@ -46,3 +40,43 @@ export namespace InternalNLSConfiguration {
return candidate && typeof candidate._languagePackId === 'string';
}
}
+
+/**
+ * The code below is copied from from src/main.js.
+ */
+
+export const getLocaleFromConfig = async (argvResource: string): Promise => {
+ try {
+ const content = stripComments(await fs.promises.readFile(argvResource, 'utf8'));
+ return JSON.parse(content).locale;
+ } catch (error) {
+ if (error.code !== "ENOENT") {
+ console.warn(error)
+ }
+ return 'en';
+ }
+};
+
+const stripComments = (content: string): string => {
+ const regexp = /('(?:[^\\']*(?:\\.)?)*')|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
+
+ return content.replace(regexp, (match, _m1, _m2, m3, m4) => {
+ // Only one of m1, m2, m3, m4 matches
+ if (m3) {
+ // A block comment. Replace with nothing
+ return '';
+ } else if (m4) {
+ // A line comment. If it ends in \r?\n then keep it.
+ const length_1 = m4.length;
+ if (length_1 > 2 && m4[length_1 - 1] === '\n') {
+ return m4[length_1 - 2] === '\r' ? '\r\n' : '\n';
+ }
+ else {
+ return '';
+ }
+ } else {
+ // We match a string
+ return match;
+ }
+ });
+};
diff --git a/patched-vscode/src/vs/server/node/serverEnvironmentService.ts b/patched-vscode/src/vs/server/node/serverEnvironmentService.ts
index 83d4aac73..0bbddc948 100644
--- a/patched-vscode/src/vs/server/node/serverEnvironmentService.ts
+++ b/patched-vscode/src/vs/server/node/serverEnvironmentService.ts
@@ -14,6 +14,8 @@ import { URI } from 'vs/base/common/uri';
export const serverOptions: OptionDescriptions> = {
+ 'locale': { type: 'string' },
+
/* ----- server setup ----- */
'host': { type: 'string', cat: 'o', args: 'ip-address', description: nls.localize('host', "The host name or IP address the server should listen to. If not set, defaults to 'localhost'.") },
@@ -97,6 +99,8 @@ export const serverOptions: OptionDescriptions> = {
export interface ServerParsedArgs {
+ 'locale'?: string;
+
/* ----- server setup ----- */
host?: string;
diff --git a/patched-vscode/src/vs/server/node/serverServices.ts b/patched-vscode/src/vs/server/node/serverServices.ts
index 46f2f0046..e3609574d 100644
--- a/patched-vscode/src/vs/server/node/serverServices.ts
+++ b/patched-vscode/src/vs/server/node/serverServices.ts
@@ -11,7 +11,7 @@ import * as path from 'vs/base/common/path';
import { IURITransformer } from 'vs/base/common/uriIpc';
import { getMachineId, getSqmMachineId, getdevDeviceId } from 'vs/base/node/id';
import { Promises } from 'vs/base/node/pfs';
-import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
+import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, ProxyChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
import { ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
@@ -225,6 +225,9 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
socketServer.registerChannel('extensions', channel);
+ const languagePackChannel = ProxyChannel.fromService(accessor.get(ILanguagePackService), disposables);
+ socketServer.registerChannel('languagePacks', languagePackChannel);
+
// clean up extensions folder
remoteExtensionsScanner.whenExtensionsReady().then(() => extensionManagementService.cleanUp());
diff --git a/patched-vscode/src/vs/server/node/webClientServer.ts b/patched-vscode/src/vs/server/node/webClientServer.ts
index 330d587d4..5646dab94 100644
--- a/patched-vscode/src/vs/server/node/webClientServer.ts
+++ b/patched-vscode/src/vs/server/node/webClientServer.ts
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { createReadStream } from 'fs';
+import { createReadStream, existsSync, writeFileSync } from 'fs';
import {readFile } from 'fs/promises';
import { Promises } from 'vs/base/node/pfs';
import * as path from 'path';
@@ -28,6 +28,7 @@ import { URI } from 'vs/base/common/uri';
import { streamToBuffer } from 'vs/base/common/buffer';
import { IProductConfiguration } from 'vs/base/common/product';
import { isString } from 'vs/base/common/types';
+import { getLocaleFromConfig, getNLSConfiguration } from 'vs/server/node/remoteLanguagePacks';
import { CharCode } from 'vs/base/common/charCode';
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
@@ -351,6 +352,8 @@ export class WebClientServer {
callbackRoute: this._callbackRoute
};
+ const locale = this._environmentService.args.locale || await getLocaleFromConfig(this._environmentService.argvResource.fsPath);
+ const nlsConfiguration = await getNLSConfiguration(locale, this._environmentService.userDataPath)
const nlsBaseUrl = this._productService.extensionsGallery?.nlsBaseUrl;
const values: { [key: string]: string } = {
WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
@@ -359,6 +362,7 @@ export class WebClientServer {
WORKBENCH_NLS_BASE_URL: vscodeBase + (nlsBaseUrl ? `${nlsBaseUrl}${!nlsBaseUrl.endsWith('/') ? '/' : ''}${this._productService.commit}/${this._productService.version}/` : ''),
BASE: base,
VS_BASE: vscodeBase,
+ NLS_CONFIGURATION: asJSON(nlsConfiguration),
};
if (useTestResolver) {
@@ -390,7 +394,7 @@ export class WebClientServer {
`frame-src 'self' https://*.vscode-cdn.net data:;`,
'worker-src \'self\' data: blob:;',
'style-src \'self\' \'unsafe-inline\';',
- 'connect-src \'self\' ws: wss: https:;',
+ 'connect-src \'self\' ws: wss: https://main.vscode-cdn.net http://localhost:* https://localhost:* https://login.microsoftonline.com/ https://update.code.visualstudio.com https://*.vscode-unpkg.net/ https://default.exp-tas.com/vscode/ab https://vscode-sync.trafficmanager.net https://vscode-sync-insiders.trafficmanager.net https://*.gallerycdn.vsassets.io https://marketplace.visualstudio.com https://*.blob.core.windows.net https://az764295.vo.msecnd.net https://code.visualstudio.com https://*.gallery.vsassets.io https://*.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com https://*.servicebus.windows.net/ https://vscode.blob.core.windows.net https://vscode.search.windows.net https://vsmarketplacebadges.dev https://vscode.download.prss.microsoft.com https://download.visualstudio.microsoft.com https://*.vscode-unpkg.net https://open-vsx.org;',
'font-src \'self\' blob:;',
'manifest-src \'self\';'
].join(' ');
@@ -465,6 +469,14 @@ export class WebClientServer {
try {
const tmpDirectory = '/tmp/'
const idleFilePath = path.join(tmpDirectory, '.sagemaker-last-active-timestamp');
+
+ // If idle shutdown file does not exist, this indicates the app UI may never been opened
+ // Create the initial metadata file
+ if (!existsSync(idleFilePath)) {
+ const timestamp = new Date().toISOString();
+ writeFileSync(idleFilePath, timestamp);
+ }
+
const data = await readFile(idleFilePath, 'utf8');
res.statusCode = 200;
diff --git a/patched-vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/patched-vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
index fc174208e..e5e2bfd0b 100644
--- a/patched-vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
+++ b/patched-vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
@@ -341,9 +341,6 @@ export class InstallAction extends ExtensionAction {
if (this.extension.isBuiltin) {
return;
}
- if (this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
- return;
- }
if (this.extension.state === ExtensionState.Uninstalled && await this.extensionsWorkbenchService.canInstall(this.extension)) {
this.enabled = this.options.installPreReleaseVersion ? this.extension.hasPreReleaseVersion : this.extension.hasReleaseVersion;
this.updateLabel();
@@ -614,7 +611,7 @@ export abstract class InstallInOtherServerAction extends ExtensionAction {
}
if (isLanguagePackExtension(this.extension.local.manifest)) {
- return true;
+ return false;
}
// Prefers to run on UI
@@ -1848,17 +1845,6 @@ export class SetLanguageAction extends ExtensionAction {
update(): void {
this.enabled = false;
this.class = SetLanguageAction.DisabledClass;
- if (!this.extension) {
- return;
- }
- if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
- return;
- }
- if (this.extension.gallery && language === getLocale(this.extension.gallery)) {
- return;
- }
- this.enabled = true;
- this.class = SetLanguageAction.EnabledClass;
}
override async run(): Promise {
@@ -1875,7 +1861,6 @@ export class ClearLanguageAction extends ExtensionAction {
private static readonly DisabledClass = `${ClearLanguageAction.EnabledClass} disabled`;
constructor(
- @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@ILocaleService private readonly localeService: ILocaleService,
) {
super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false);
@@ -1885,17 +1870,6 @@ export class ClearLanguageAction extends ExtensionAction {
update(): void {
this.enabled = false;
this.class = ClearLanguageAction.DisabledClass;
- if (!this.extension) {
- return;
- }
- if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
- return;
- }
- if (this.extension.gallery && language !== getLocale(this.extension.gallery)) {
- return;
- }
- this.enabled = true;
- this.class = ClearLanguageAction.EnabledClass;
}
override async run(): Promise {
diff --git a/patched-vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts b/patched-vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
index d7124758e..4a7686dff 100644
--- a/patched-vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
+++ b/patched-vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
@@ -51,7 +51,8 @@ class NativeLocaleService implements ILocaleService {
@IProductService private readonly productService: IProductService
) { }
- private async validateLocaleFile(): Promise {
+ // Make public just so we do not have to patch all the unused code out.
+ public async validateLocaleFile(): Promise {
try {
const content = await this.textFileService.read(this.environmentService.argvResource, { encoding: 'utf8' });
@@ -78,9 +79,6 @@ class NativeLocaleService implements ILocaleService {
}
private async writeLocaleValue(locale: string | undefined): Promise {
- if (!(await this.validateLocaleFile())) {
- return false;
- }
await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: locale }], true);
return true;
}
diff --git a/patched-vscode/src/vs/workbench/workbench.web.main.ts b/patched-vscode/src/vs/workbench/workbench.web.main.ts
index e4a0a7e3e..bdbc65d66 100644
--- a/patched-vscode/src/vs/workbench/workbench.web.main.ts
+++ b/patched-vscode/src/vs/workbench/workbench.web.main.ts
@@ -52,7 +52,7 @@ import 'vs/workbench/services/dialogs/browser/fileDialogService';
import 'vs/workbench/services/host/browser/browserHostService';
import 'vs/workbench/services/lifecycle/browser/lifecycleService';
import 'vs/workbench/services/clipboard/browser/clipboardService';
-import 'vs/workbench/services/localization/browser/localeService';
+import 'vs/workbench/services/localization/electron-sandbox/localeService';
import 'vs/workbench/services/path/browser/pathService';
import 'vs/workbench/services/themes/browser/browserHostColorSchemeService';
import 'vs/workbench/services/encryption/browser/encryptionService';
@@ -118,8 +118,9 @@ registerSingleton(ILanguagePackService, WebLanguagePacksService, InstantiationTy
// Logs
import 'vs/workbench/contrib/logs/browser/logs.contribution';
-// Localization
-import 'vs/workbench/contrib/localization/browser/localization.contribution';
+// Localization. This does not actually import anything specific to Electron so
+// it should be safe.
+import 'vs/workbench/contrib/localization/electron-sandbox/localization.contribution';
// Performance
import 'vs/workbench/contrib/performance/browser/performance.web.contribution';
diff --git a/patches/display-language.diff b/patches/display-language.diff
new file mode 100644
index 000000000..d8d279db0
--- /dev/null
+++ b/patches/display-language.diff
@@ -0,0 +1,457 @@
+Add display language support
+
+We can remove this once upstream supports all language packs.
+
+1. Proxies language packs to the service on the backend.
+2. NLS configuration is embedded into the HTML for the browser to pick up. This
+ code to generate this configuration is copied from the native portion.
+3. Remove configuredLocale since we have our own thing.
+4. Move the argv.json file to the server instead of in-browser storage. This is
+ where the current locale is stored and currently the server needs to be able
+ to read it.
+5. Add the locale flag.
+6. Remove the redundant locale verification. It does the same as the existing
+ one but is worse because it does not handle non-existent or empty files.
+7. Replace some caching and Node requires because code-server does not restart
+ when changing the language unlike native Code.
+8. Make language extensions installable like normal rather than using the
+ special set/clear language actions.
+
+Index: sagemaker-code-editor/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
++++ sagemaker-code-editor/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
+@@ -5,18 +5,24 @@
+
+ import { CancellationTokenSource } from 'vs/base/common/cancellation';
+ import { URI } from 'vs/base/common/uri';
++import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
+ import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
+ import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader';
+-import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
++import { ILanguagePackItem, ILanguagePackService, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
+ import { ILogService } from 'vs/platform/log/common/log';
++import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
+
+ export class WebLanguagePacksService extends LanguagePackBaseService {
++ private readonly languagePackService: ILanguagePackService;
++
+ constructor(
++ @IRemoteAgentService remoteAgentService: IRemoteAgentService,
+ @IExtensionResourceLoaderService private readonly extensionResourceLoaderService: IExtensionResourceLoaderService,
+ @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
+ @ILogService private readonly logService: ILogService
+ ) {
+ super(extensionGalleryService);
++ this.languagePackService = ProxyChannel.toService(remoteAgentService.getConnection()!.getChannel('languagePacks'))
+ }
+
+ async getBuiltInExtensionTranslationsUri(id: string, language: string): Promise {
+@@ -72,6 +78,6 @@ export class WebLanguagePacksService ext
+
+ // Web doesn't have a concept of language packs, so we just return an empty array
+ getInstalledLanguages(): Promise {
+- return Promise.resolve([]);
++ return this.languagePackService.getInstalledLanguages()
+ }
+ }
+Index: sagemaker-code-editor/vscode/src/vs/server/node/serverServices.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/server/node/serverServices.ts
++++ sagemaker-code-editor/vscode/src/vs/server/node/serverServices.ts
+@@ -11,7 +11,7 @@ import * as path from 'vs/base/common/pa
+ import { IURITransformer } from 'vs/base/common/uriIpc';
+ import { getMachineId, getSqmMachineId, getdevDeviceId } from 'vs/base/node/id';
+ import { Promises } from 'vs/base/node/pfs';
+-import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
++import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, ProxyChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
+ import { ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net';
+ import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
+ import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
+@@ -225,6 +225,9 @@ export async function setupServerService
+ const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
+ socketServer.registerChannel('extensions', channel);
+
++ const languagePackChannel = ProxyChannel.fromService(accessor.get(ILanguagePackService), disposables);
++ socketServer.registerChannel('languagePacks', languagePackChannel);
++
+ // clean up extensions folder
+ remoteExtensionsScanner.whenExtensionsReady().then(() => extensionManagementService.cleanUp());
+
+Index: sagemaker-code-editor/vscode/src/vs/base/common/platform.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/base/common/platform.ts
++++ sagemaker-code-editor/vscode/src/vs/base/common/platform.ts
+@@ -2,8 +2,6 @@
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+-import * as nls from 'vs/nls';
+-
+ export const LANGUAGE_DEFAULT = 'en';
+
+ let _isWindows = false;
+@@ -112,17 +110,21 @@ else if (typeof navigator === 'object' &
+ _isMobile = _userAgent?.indexOf('Mobi') >= 0;
+ _isWeb = true;
+
+- const configuredLocale = nls.getConfiguredDefaultLocale(
+- // This call _must_ be done in the file that calls `nls.getConfiguredDefaultLocale`
+- // to ensure that the NLS AMD Loader plugin has been loaded and configured.
+- // This is because the loader plugin decides what the default locale is based on
+- // how it's able to resolve the strings.
+- nls.localize({ key: 'ensureLoaderPluginIsLoaded', comment: ['{Locked}'] }, '_')
+- );
+-
+- _locale = configuredLocale || LANGUAGE_DEFAULT;
++ _locale = LANGUAGE_DEFAULT;
+ _language = _locale;
+ _platformLocale = navigator.language;
++ const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration');
++ const rawNlsConfig = el && el.getAttribute('data-settings');
++ if (rawNlsConfig) {
++ try {
++ const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
++ const resolved = nlsConfig.availableLanguages['*'];
++ _locale = nlsConfig.locale;
++ _platformLocale = nlsConfig.osLocale;
++ _language = resolved ? resolved : LANGUAGE_DEFAULT;
++ _translationsConfigFile = nlsConfig._translationsConfigFile;
++ } catch (error) { /* Oh well. */ }
++ }
+ }
+
+ // Unknown environment
+Index: sagemaker-code-editor/vscode/src/vs/code/browser/workbench/workbench.html
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/code/browser/workbench/workbench.html
++++ sagemaker-code-editor/vscode/src/vs/code/browser/workbench/workbench.html
+@@ -19,6 +19,9 @@
+
+
+
++
++
++
+
+
+
+@@ -46,14 +49,26 @@
+ // Normalize locale to lowercase because translationServiceUrl is case-sensitive.
+ // ref: https://github.com/microsoft/vscode/issues/187795
+ const locale = localStorage.getItem('vscode.nls.locale') || navigator.language.toLowerCase();
+- if (!locale.startsWith('en')) {
+- nlsConfig['vs/nls'] = {
+- availableLanguages: {
+- '*': locale
+- },
+- translationServiceUrl: '{{WORKBENCH_NLS_BASE_URL}}'
+- };
+- }
++ try {
++ nlsConfig['vs/nls'] = JSON.parse(document.getElementById("vscode-remote-nls-configuration").getAttribute("data-settings"))
++ if (nlsConfig['vs/nls']._resolvedLanguagePackCoreLocation) {
++ const bundles = Object.create(null)
++ nlsConfig['vs/nls'].loadBundle = (bundle, _language, cb) => {
++ const result = bundles[bundle]
++ if (result) {
++ return cb(undefined, result)
++ }
++ const path = nlsConfig['vs/nls']._resolvedLanguagePackCoreLocation + "/" + bundle.replace(/\//g, "!") + ".nls.json"
++ fetch(`{{WORKBENCH_WEB_BASE_URL}}/../vscode-remote-resource?path=${encodeURIComponent(path)}`)
++ .then((response) => response.json())
++ .then((json) => {
++ bundles[bundle] = json
++ cb(undefined, json)
++ })
++ .catch(cb)
++ }
++ }
++ } catch (error) { /* Probably fine. */ }
+
+ require.config({
+ baseUrl: `${baseUrl}/out`,
+Index: sagemaker-code-editor/vscode/src/vs/platform/environment/common/environmentService.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/platform/environment/common/environmentService.ts
++++ sagemaker-code-editor/vscode/src/vs/platform/environment/common/environmentService.ts
+@@ -101,7 +101,7 @@ export abstract class AbstractNativeEnvi
+ return URI.file(join(vscodePortable, 'argv.json'));
+ }
+
+- return joinPath(this.userHome, this.productService.dataFolderName, 'argv.json');
++ return joinPath(this.appSettingsHome, 'argv.json');
+ }
+
+ @memoize
+Index: sagemaker-code-editor/vscode/src/vs/server/node/remoteLanguagePacks.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/server/node/remoteLanguagePacks.ts
++++ sagemaker-code-editor/vscode/src/vs/server/node/remoteLanguagePacks.ts
+@@ -8,36 +8,30 @@ import { FileAccess } from 'vs/base/comm
+ import * as path from 'vs/base/common/path';
+
+ import * as lp from 'vs/base/node/languagePacks';
+-import product from 'vs/platform/product/common/product';
+
+ const metaData = path.join(FileAccess.asFileUri('').fsPath, 'nls.metadata.json');
+ const _cache: Map> = new Map();
+
+-function exists(file: string) {
+- return new Promise(c => fs.exists(file, c));
+-}
+-
+ export function getNLSConfiguration(language: string, userDataPath: string): Promise {
+- return exists(metaData).then((fileExists) => {
+- if (!fileExists || !product.commit) {
+- // console.log(`==> MetaData or commit unknown. Using default language.`);
+- // The OS Locale on the remote side really doesn't matter, so we return the default locale
+- return Promise.resolve({ locale: 'en', osLocale: 'en', availableLanguages: {} });
+- }
+- const key = `${language}||${userDataPath}`;
+- let result = _cache.get(key);
+- if (!result) {
+- // The OS Locale on the remote side really doesn't matter, so we pass in the same language
+- result = lp.getNLSConfiguration(product.commit, userDataPath, metaData, language, language).then(value => {
+- if (InternalNLSConfiguration.is(value)) {
+- value._languagePackSupport = true;
+- }
+- return value;
+- });
+- _cache.set(key, result);
+- }
+- return result;
+- });
++ const key = `${language}||${userDataPath}`;
++ let result = _cache.get(key);
++ if (!result) {
++ // The OS Locale on the remote side really doesn't matter, so we pass in the same language
++ result = lp.getNLSConfiguration("dummy_commmit", userDataPath, metaData, language, language).then(value => {
++ if (InternalNLSConfiguration.is(value)) {
++ value._languagePackSupport = true;
++ }
++ // If the configuration has no results keep trying since code-server
++ // doesn't restart when a language is installed so this result would
++ // persist (the plugin might not be installed yet for example).
++ if (value.locale !== 'en' && value.locale !== 'en-us' && Object.keys(value.availableLanguages).length === 0) {
++ _cache.delete(key);
++ }
++ return value;
++ });
++ _cache.set(key, result);
++ }
++ return result;
+ }
+
+ export namespace InternalNLSConfiguration {
+@@ -46,3 +40,43 @@ export namespace InternalNLSConfiguratio
+ return candidate && typeof candidate._languagePackId === 'string';
+ }
+ }
++
++/**
++ * The code below is copied from from src/main.js.
++ */
++
++export const getLocaleFromConfig = async (argvResource: string): Promise => {
++ try {
++ const content = stripComments(await fs.promises.readFile(argvResource, 'utf8'));
++ return JSON.parse(content).locale;
++ } catch (error) {
++ if (error.code !== "ENOENT") {
++ console.warn(error)
++ }
++ return 'en';
++ }
++};
++
++const stripComments = (content: string): string => {
++ const regexp = /('(?:[^\\']*(?:\\.)?)*')|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
++
++ return content.replace(regexp, (match, _m1, _m2, m3, m4) => {
++ // Only one of m1, m2, m3, m4 matches
++ if (m3) {
++ // A block comment. Replace with nothing
++ return '';
++ } else if (m4) {
++ // A line comment. If it ends in \r?\n then keep it.
++ const length_1 = m4.length;
++ if (length_1 > 2 && m4[length_1 - 1] === '\n') {
++ return m4[length_1 - 2] === '\r' ? '\r\n' : '\n';
++ }
++ else {
++ return '';
++ }
++ } else {
++ // We match a string
++ return match;
++ }
++ });
++};
+Index: sagemaker-code-editor/vscode/src/vs/server/node/webClientServer.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/server/node/webClientServer.ts
++++ sagemaker-code-editor/vscode/src/vs/server/node/webClientServer.ts
+@@ -28,6 +28,7 @@ import { URI } from 'vs/base/common/uri'
+ import { streamToBuffer } from 'vs/base/common/buffer';
+ import { IProductConfiguration } from 'vs/base/common/product';
+ import { isString } from 'vs/base/common/types';
++import { getLocaleFromConfig, getNLSConfiguration } from 'vs/server/node/remoteLanguagePacks';
+ import { CharCode } from 'vs/base/common/charCode';
+ import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
+
+@@ -351,6 +352,8 @@ export class WebClientServer {
+ callbackRoute: this._callbackRoute
+ };
+
++ const locale = this._environmentService.args.locale || await getLocaleFromConfig(this._environmentService.argvResource.fsPath);
++ const nlsConfiguration = await getNLSConfiguration(locale, this._environmentService.userDataPath)
+ const nlsBaseUrl = this._productService.extensionsGallery?.nlsBaseUrl;
+ const values: { [key: string]: string } = {
+ WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
+@@ -359,6 +362,7 @@ export class WebClientServer {
+ WORKBENCH_NLS_BASE_URL: vscodeBase + (nlsBaseUrl ? `${nlsBaseUrl}${!nlsBaseUrl.endsWith('/') ? '/' : ''}${this._productService.commit}/${this._productService.version}/` : ''),
+ BASE: base,
+ VS_BASE: vscodeBase,
++ NLS_CONFIGURATION: asJSON(nlsConfiguration),
+ };
+
+ if (useTestResolver) {
+Index: sagemaker-code-editor/vscode/src/vs/server/node/serverEnvironmentService.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/server/node/serverEnvironmentService.ts
++++ sagemaker-code-editor/vscode/src/vs/server/node/serverEnvironmentService.ts
+@@ -14,6 +14,8 @@ import { URI } from 'vs/base/common/uri'
+
+ export const serverOptions: OptionDescriptions> = {
+
++ 'locale': { type: 'string' },
++
+ /* ----- server setup ----- */
+
+ 'host': { type: 'string', cat: 'o', args: 'ip-address', description: nls.localize('host', "The host name or IP address the server should listen to. If not set, defaults to 'localhost'.") },
+@@ -97,6 +99,8 @@ export const serverOptions: OptionDescri
+
+ export interface ServerParsedArgs {
+
++ 'locale'?: string;
++
+ /* ----- server setup ----- */
+
+ host?: string;
+Index: sagemaker-code-editor/vscode/src/vs/workbench/workbench.web.main.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/workbench/workbench.web.main.ts
++++ sagemaker-code-editor/vscode/src/vs/workbench/workbench.web.main.ts
+@@ -52,7 +52,7 @@ import 'vs/workbench/services/dialogs/br
+ import 'vs/workbench/services/host/browser/browserHostService';
+ import 'vs/workbench/services/lifecycle/browser/lifecycleService';
+ import 'vs/workbench/services/clipboard/browser/clipboardService';
+-import 'vs/workbench/services/localization/browser/localeService';
++import 'vs/workbench/services/localization/electron-sandbox/localeService';
+ import 'vs/workbench/services/path/browser/pathService';
+ import 'vs/workbench/services/themes/browser/browserHostColorSchemeService';
+ import 'vs/workbench/services/encryption/browser/encryptionService';
+@@ -118,8 +118,9 @@ registerSingleton(ILanguagePackService,
+ // Logs
+ import 'vs/workbench/contrib/logs/browser/logs.contribution';
+
+-// Localization
+-import 'vs/workbench/contrib/localization/browser/localization.contribution';
++// Localization. This does not actually import anything specific to Electron so
++// it should be safe.
++import 'vs/workbench/contrib/localization/electron-sandbox/localization.contribution';
+
+ // Performance
+ import 'vs/workbench/contrib/performance/browser/performance.web.contribution';
+Index: sagemaker-code-editor/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
++++ sagemaker-code-editor/vscode/src/vs/workbench/services/localization/electron-sandbox/localeService.ts
+@@ -51,7 +51,8 @@ class NativeLocaleService implements ILo
+ @IProductService private readonly productService: IProductService
+ ) { }
+
+- private async validateLocaleFile(): Promise {
++ // Make public just so we do not have to patch all the unused code out.
++ public async validateLocaleFile(): Promise {
+ try {
+ const content = await this.textFileService.read(this.environmentService.argvResource, { encoding: 'utf8' });
+
+@@ -78,9 +79,6 @@ class NativeLocaleService implements ILo
+ }
+
+ private async writeLocaleValue(locale: string | undefined): Promise {
+- if (!(await this.validateLocaleFile())) {
+- return false;
+- }
+ await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: locale }], true);
+ return true;
+ }
+Index: sagemaker-code-editor/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
+===================================================================
+--- sagemaker-code-editor.orig/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
++++ sagemaker-code-editor/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
+@@ -341,9 +341,6 @@ export class InstallAction extends Exten
+ if (this.extension.isBuiltin) {
+ return;
+ }
+- if (this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
+- return;
+- }
+ if (this.extension.state === ExtensionState.Uninstalled && await this.extensionsWorkbenchService.canInstall(this.extension)) {
+ this.enabled = this.options.installPreReleaseVersion ? this.extension.hasPreReleaseVersion : this.extension.hasReleaseVersion;
+ this.updateLabel();
+@@ -614,7 +611,7 @@ export abstract class InstallInOtherServ
+ }
+
+ if (isLanguagePackExtension(this.extension.local.manifest)) {
+- return true;
++ return false;
+ }
+
+ // Prefers to run on UI
+@@ -1848,17 +1845,6 @@ export class SetLanguageAction extends E
+ update(): void {
+ this.enabled = false;
+ this.class = SetLanguageAction.DisabledClass;
+- if (!this.extension) {
+- return;
+- }
+- if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
+- return;
+- }
+- if (this.extension.gallery && language === getLocale(this.extension.gallery)) {
+- return;
+- }
+- this.enabled = true;
+- this.class = SetLanguageAction.EnabledClass;
+ }
+
+ override async run(): Promise {
+@@ -1875,7 +1861,6 @@ export class ClearLanguageAction extends
+ private static readonly DisabledClass = `${ClearLanguageAction.EnabledClass} disabled`;
+
+ constructor(
+- @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
+ @ILocaleService private readonly localeService: ILocaleService,
+ ) {
+ super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false);
+@@ -1885,17 +1870,6 @@ export class ClearLanguageAction extends
+ update(): void {
+ this.enabled = false;
+ this.class = ClearLanguageAction.DisabledClass;
+- if (!this.extension) {
+- return;
+- }
+- if (!this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
+- return;
+- }
+- if (this.extension.gallery && language !== getLocale(this.extension.gallery)) {
+- return;
+- }
+- this.enabled = true;
+- this.class = ClearLanguageAction.EnabledClass;
+ }
+
+ override async run(): Promise {
diff --git a/patches/series b/patches/series
index 280c2e072..240a749b3 100644
--- a/patches/series
+++ b/patches/series
@@ -9,3 +9,4 @@ license.diff
sagemaker-idle-extension.patch
terminal-crash-mitigation.patch
sagemaker-open-notebook-extension.patch
+display-language.diff