mirror of
https://github.com/cdr/code-server.git
synced 2026-03-31 10:33:09 +02:00
* Update Code to 1.113.0 * Use CI build targets The target we have been using has started throwing all sorts of errors during the build (even without any of our patches). After checking the VS Code repo I think these are the ones we should actually be using. They are way faster, too. --------- Co-authored-by: Asher <ash@coder.com>
350 lines
16 KiB
Diff
350 lines
16 KiB
Diff
Add display language support
|
|
|
|
VS Code web appears to implement language support by setting a cookie and
|
|
downloading language packs from a URL configured in the product.json. This patch
|
|
supports language pack extensions and uses files on the remote to set the
|
|
language instead, so it works like the desktop version.
|
|
|
|
Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/server/node/serverServices.ts
|
|
+++ code-server/lib/vscode/src/vs/server/node/serverServices.ts
|
|
@@ -12,7 +12,7 @@ import * as path from '../../base/common
|
|
import { IURITransformer } from '../../base/common/uriIpc.js';
|
|
import { getMachineId, getSqmMachineId, getDevDeviceId } from '../../base/node/id.js';
|
|
import { Promises } from '../../base/node/pfs.js';
|
|
-import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, StaticRouter } from '../../base/parts/ipc/common/ipc.js';
|
|
+import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, ProxyChannel, StaticRouter } from '../../base/parts/ipc/common/ipc.js';
|
|
import { ProtocolConstants } from '../../base/parts/ipc/common/ipc.net.js';
|
|
import { IConfigurationService } from '../../platform/configuration/common/configuration.js';
|
|
import { ConfigurationService } from '../../platform/configuration/common/configurationService.js';
|
|
@@ -297,6 +297,9 @@ export async function setupServerService
|
|
|
|
socketServer.registerChannel('mcpManagement', new McpManagementChannel(mcpManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority)));
|
|
|
|
+ const languagePackChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(ILanguagePackService), disposables);
|
|
+ socketServer.registerChannel('languagePacks', languagePackChannel);
|
|
+
|
|
// clean up extensions folder
|
|
remoteExtensionsScanner.whenExtensionsReady().then(() => extensionManagementService.cleanUp());
|
|
|
|
Index: code-server/lib/vscode/src/vs/platform/environment/common/environmentService.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/platform/environment/common/environmentService.ts
|
|
+++ code-server/lib/vscode/src/vs/platform/environment/common/environmentService.ts
|
|
@@ -98,7 +98,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: code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
|
|
+++ code-server/lib/vscode/src/vs/server/node/remoteLanguagePacks.ts
|
|
@@ -3,6 +3,8 @@
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
+import { promises as fs } from 'fs';
|
|
+import * as path from 'path';
|
|
import { FileAccess } from '../../base/common/network.js';
|
|
import { join } from '../../base/common/path.js';
|
|
import type { INLSConfiguration } from '../../nls.js';
|
|
@@ -33,7 +35,94 @@ export async function getNLSConfiguratio
|
|
if (!result) {
|
|
result = resolveNLSConfiguration({ userLocale: language, osLocale: language, commit: product.commit, userDataPath, nlsMetadataPath });
|
|
nlsConfigurationCache.set(cacheKey, result);
|
|
+ // If the language pack does not yet exist, it defaults to English, which is
|
|
+ // then cached and you have to restart even if you then install the pack.
|
|
+ result.then((r) => {
|
|
+ if (!language.startsWith('en') && r.resolvedLanguage.startsWith('en')) {
|
|
+ nlsConfigurationCache.delete(cacheKey);
|
|
+ }
|
|
+ })
|
|
}
|
|
|
|
return result;
|
|
}
|
|
+
|
|
+/**
|
|
+ * Copied from from src/main.js.
|
|
+ */
|
|
+export const getLocaleFromConfig = async (argvResource: string): Promise<string> => {
|
|
+ try {
|
|
+ const content = stripComments(await fs.readFile(argvResource, 'utf8'));
|
|
+ return JSON.parse(content).locale;
|
|
+ } catch (error) {
|
|
+ if (error.code !== "ENOENT") {
|
|
+ console.warn(error)
|
|
+ }
|
|
+ return 'en';
|
|
+ }
|
|
+};
|
|
+
|
|
+/**
|
|
+ * Copied from from src/main.js.
|
|
+ */
|
|
+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;
|
|
+ }
|
|
+ });
|
|
+};
|
|
+
|
|
+/**
|
|
+ * Generate translations then return a path to a JavaScript file that sets the
|
|
+ * translations into global variables. This file is loaded by the browser to
|
|
+ * set global variables that the loader uses when looking for translations.
|
|
+ *
|
|
+ * Normally, VS Code pulls these files from a CDN but we want them to be local.
|
|
+ */
|
|
+export async function getBrowserNLSConfiguration(locale: string, userDataPath: string): Promise<string> {
|
|
+ if (locale.startsWith('en')) {
|
|
+ return ''; // Use fallback translations.
|
|
+ }
|
|
+
|
|
+ const nlsConfig = await getNLSConfiguration(locale, userDataPath);
|
|
+ const messagesFile = nlsConfig?.languagePack?.messagesFile;
|
|
+ const resolvedLanguage = nlsConfig?.resolvedLanguage;
|
|
+ if (!messagesFile || !resolvedLanguage) {
|
|
+ return ''; // Use fallback translations.
|
|
+ }
|
|
+
|
|
+ const nlsFile = path.join(path.dirname(messagesFile), "nls.messages.js");
|
|
+ try {
|
|
+ await fs.stat(nlsFile);
|
|
+ return nlsFile; // We already generated the file.
|
|
+ } catch (error) {
|
|
+ // ENOENT is fine, that just means we need to generate the file.
|
|
+ if (error.code !== 'ENOENT') {
|
|
+ throw error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ const messages = (await fs.readFile(messagesFile)).toString();
|
|
+ const content = `globalThis._VSCODE_NLS_MESSAGES=${messages};
|
|
+globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(resolvedLanguage)};`
|
|
+ await fs.writeFile(nlsFile, content, "utf-8");
|
|
+
|
|
+ return nlsFile;
|
|
+}
|
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
|
@@ -25,6 +25,7 @@ import { URI } from '../../base/common/u
|
|
import { streamToBuffer } from '../../base/common/buffer.js';
|
|
import { IProductConfiguration } from '../../base/common/product.js';
|
|
import { isString, Mutable } from '../../base/common/types.js';
|
|
+import { getLocaleFromConfig, getBrowserNLSConfiguration } from './remoteLanguagePacks.js';
|
|
import { CharCode } from '../../base/common/charCode.js';
|
|
import { IExtensionManifest } from '../../platform/extensions/common/extensions.js';
|
|
import { ICSSDevelopmentService } from '../../platform/cssDev/node/cssDevService.js';
|
|
@@ -386,14 +387,22 @@ export class WebClientServer {
|
|
};
|
|
|
|
const cookies = cookie.parse(req.headers.cookie || '');
|
|
- const locale = cookies['vscode.nls.locale'] || req.headers['accept-language']?.split(',')[0]?.toLowerCase() || 'en';
|
|
+ const locale = this._environmentService.args.locale || await getLocaleFromConfig(this._environmentService.argvResource.fsPath) || cookies['vscode.nls.locale'] || req.headers['accept-language']?.split(',')[0]?.toLowerCase() || 'en';
|
|
let WORKBENCH_NLS_BASE_URL: string | undefined;
|
|
let WORKBENCH_NLS_URL: string;
|
|
if (!locale.startsWith('en') && this._productService.nlsCoreBaseUrl) {
|
|
WORKBENCH_NLS_BASE_URL = this._productService.nlsCoreBaseUrl;
|
|
WORKBENCH_NLS_URL = `${WORKBENCH_NLS_BASE_URL}${this._productService.commit}/${this._productService.version}/${locale}/nls.messages.js`;
|
|
} else {
|
|
- WORKBENCH_NLS_URL = ''; // fallback will apply
|
|
+ try {
|
|
+ const nlsFile = await getBrowserNLSConfiguration(locale, this._environmentService.userDataPath);
|
|
+ WORKBENCH_NLS_URL = nlsFile
|
|
+ ? `${vscodeBase}/vscode-remote-resource?path=${encodeURIComponent(nlsFile)}`
|
|
+ : '';
|
|
+ } catch (error) {
|
|
+ console.error("Failed to generate translations", error);
|
|
+ WORKBENCH_NLS_URL = '';
|
|
+ }
|
|
}
|
|
|
|
const values: { [key: string]: string } = {
|
|
Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
|
+++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts
|
|
@@ -22,6 +22,7 @@ export const serverOptions: OptionDescri
|
|
'disable-file-downloads': { type: 'boolean' },
|
|
'disable-file-uploads': { type: 'boolean' },
|
|
'disable-getting-started-override': { type: 'boolean' },
|
|
+ 'locale': { type: 'string' },
|
|
|
|
/* ----- server setup ----- */
|
|
|
|
@@ -116,6 +117,7 @@ export interface ServerParsedArgs {
|
|
'disable-file-downloads'?: boolean;
|
|
'disable-file-uploads'?: boolean;
|
|
'disable-getting-started-override'?: boolean,
|
|
+ 'locale'?: string
|
|
|
|
/* ----- server setup ----- */
|
|
|
|
Index: code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
|
|
+++ code-server/lib/vscode/src/vs/platform/languagePacks/browser/languagePacks.ts
|
|
@@ -5,18 +5,24 @@
|
|
|
|
import { CancellationTokenSource } from '../../../base/common/cancellation.js';
|
|
import { URI } from '../../../base/common/uri.js';
|
|
+import { ProxyChannel } from '../../../base/parts/ipc/common/ipc.js';
|
|
import { IExtensionGalleryService } from '../../extensionManagement/common/extensionManagement.js';
|
|
import { IExtensionResourceLoaderService } from '../../extensionResourceLoader/common/extensionResourceLoader.js';
|
|
-import { ILanguagePackItem, LanguagePackBaseService } from '../common/languagePacks.js';
|
|
+import { ILanguagePackItem, ILanguagePackService, LanguagePackBaseService } from '../common/languagePacks.js';
|
|
import { ILogService } from '../../log/common/log.js';
|
|
+import { IRemoteAgentService } from '../../../workbench/services/remote/common/remoteAgentService.js';
|
|
|
|
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<ILanguagePackService>(remoteAgentService.getConnection()!.getChannel('languagePacks'))
|
|
}
|
|
|
|
async getBuiltInExtensionTranslationsUri(id: string, language: string): Promise<URI | undefined> {
|
|
@@ -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<ILanguagePackItem[]> {
|
|
- return Promise.resolve([]);
|
|
+ return this.languagePackService.getInstalledLanguages()
|
|
}
|
|
}
|
|
Index: code-server/lib/vscode/src/vs/workbench/services/localization/electron-browser/localeService.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/workbench/services/localization/electron-browser/localeService.ts
|
|
+++ code-server/lib/vscode/src/vs/workbench/services/localization/electron-browser/localeService.ts
|
|
@@ -51,7 +51,8 @@ class NativeLocaleService implements ILo
|
|
@IProductService private readonly productService: IProductService
|
|
) { }
|
|
|
|
- private async validateLocaleFile(): Promise<boolean> {
|
|
+ // Make public just so we do not have to patch all the unused code out.
|
|
+ public async validateLocaleFile(): Promise<boolean> {
|
|
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<boolean> {
|
|
- if (!(await this.validateLocaleFile())) {
|
|
- return false;
|
|
- }
|
|
await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: locale }], true);
|
|
return true;
|
|
}
|
|
Index: code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
|
|
+++ code-server/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
|
|
@@ -474,9 +474,6 @@ export class InstallAction extends Exten
|
|
if (this.extension.isBuiltin) {
|
|
return;
|
|
}
|
|
- if (this.extensionsWorkbenchService.canSetLanguage(this.extension)) {
|
|
- return;
|
|
- }
|
|
if (this.extension.state !== ExtensionState.Uninstalled) {
|
|
return;
|
|
}
|
|
@@ -781,7 +778,7 @@ export abstract class InstallInOtherServ
|
|
}
|
|
|
|
if (isLanguagePackExtension(this.extension.local.manifest)) {
|
|
- return true;
|
|
+ return false;
|
|
}
|
|
|
|
// Prefers to run on UI
|
|
@@ -2072,17 +2069,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<any> {
|
|
@@ -2099,7 +2085,6 @@ export class ClearLanguageAction extends
|
|
private static readonly DisabledClass = `${this.EnabledClass} disabled`;
|
|
|
|
constructor(
|
|
- @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
|
|
@ILocaleService private readonly localeService: ILocaleService,
|
|
) {
|
|
super(ClearLanguageAction.ID, ClearLanguageAction.TITLE.value, ClearLanguageAction.DisabledClass, false);
|
|
@@ -2109,17 +2094,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<any> {
|
|
Index: code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/workbench/workbench.web.main.ts
|
|
+++ code-server/lib/vscode/src/vs/workbench/workbench.web.main.ts
|
|
@@ -55,7 +55,7 @@ import './services/host/browser/browserH
|
|
import '../platform/meteredConnection/browser/meteredConnectionService.js';
|
|
import './services/lifecycle/browser/lifecycleService.js';
|
|
import './services/clipboard/browser/clipboardService.js';
|
|
-import './services/localization/browser/localeService.js';
|
|
+import './services/localization/electron-browser/localeService.js';
|
|
import './services/path/browser/pathService.js';
|
|
import './services/themes/browser/browserHostColorSchemeService.js';
|
|
import './services/encryption/browser/encryptionService.js';
|