mirror of
https://github.com/Microsoft/vscode
synced 2024-06-30 23:04:56 +00:00
Implement NLS without AMD loader (#214588)
This commit is contained in:
parent
cb1514f9a6
commit
f6f90e0163
|
@ -1015,10 +1015,6 @@
|
|||
"vs/base/common/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "src/vs/workbench/{workbench.desktop.main.nls.js,workbench.web.main.nls.js}",
|
||||
"restrictions": []
|
||||
},
|
||||
{
|
||||
"target": "src/vs/{loader.d.ts,css.ts,css.build.ts,monaco.d.ts,nls.ts,nls.build.ts,nls.mock.ts}",
|
||||
"restrictions": []
|
||||
|
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -40,8 +40,6 @@
|
|||
"**/Cargo.lock": true,
|
||||
"src/vs/workbench/workbench.web.main.css": true,
|
||||
"src/vs/workbench/workbench.desktop.main.css": true,
|
||||
"src/vs/workbench/workbench.desktop.main.nls.js": true,
|
||||
"src/vs/workbench/workbench.web.main.nls.js": true,
|
||||
"build/**/*.js": true,
|
||||
"out/**": true,
|
||||
"out-build/**": true,
|
||||
|
|
|
@ -216,104 +216,105 @@ extends:
|
|||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
|
||||
- stage: CompileCLI
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}:
|
||||
- job: CLILinuxX64
|
||||
pool:
|
||||
name: 1es-ubuntu-20.04-x64
|
||||
os: linux
|
||||
steps:
|
||||
- template: build/azure-pipelines/linux/cli-build-linux.yml@self
|
||||
parameters:
|
||||
VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }}
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_LINUX: ${{ parameters.VSCODE_BUILD_LINUX }}
|
||||
- ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
|
||||
- stage: CompileCLI
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}:
|
||||
- job: CLILinuxX64
|
||||
pool:
|
||||
name: 1es-ubuntu-20.04-x64
|
||||
os: linux
|
||||
steps:
|
||||
- template: build/azure-pipelines/linux/cli-build-linux.yml@self
|
||||
parameters:
|
||||
VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }}
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_LINUX: ${{ parameters.VSCODE_BUILD_LINUX }}
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), or(eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true))) }}:
|
||||
- job: CLILinuxGnuARM
|
||||
pool:
|
||||
name: 1es-ubuntu-20.04-x64
|
||||
os: linux
|
||||
steps:
|
||||
- template: build/azure-pipelines/linux/cli-build-linux.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_LINUX_ARMHF: ${{ parameters.VSCODE_BUILD_LINUX_ARMHF }}
|
||||
VSCODE_BUILD_LINUX_ARM64: ${{ parameters.VSCODE_BUILD_LINUX_ARM64 }}
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), or(eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true))) }}:
|
||||
- job: CLILinuxGnuARM
|
||||
pool:
|
||||
name: 1es-ubuntu-20.04-x64
|
||||
os: linux
|
||||
steps:
|
||||
- template: build/azure-pipelines/linux/cli-build-linux.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_LINUX_ARMHF: ${{ parameters.VSCODE_BUILD_LINUX_ARMHF }}
|
||||
VSCODE_BUILD_LINUX_ARM64: ${{ parameters.VSCODE_BUILD_LINUX_ARM64 }}
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE, true)) }}:
|
||||
- job: CLIAlpineX64
|
||||
pool:
|
||||
name: 1es-ubuntu-20.04-x64
|
||||
os: linux
|
||||
steps:
|
||||
- template: build/azure-pipelines/alpine/cli-build-alpine.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_ALPINE: ${{ parameters.VSCODE_BUILD_ALPINE }}
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE, true)) }}:
|
||||
- job: CLIAlpineX64
|
||||
pool:
|
||||
name: 1es-ubuntu-20.04-x64
|
||||
os: linux
|
||||
steps:
|
||||
- template: build/azure-pipelines/alpine/cli-build-alpine.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_ALPINE: ${{ parameters.VSCODE_BUILD_ALPINE }}
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true)) }}:
|
||||
- job: CLIAlpineARM64
|
||||
pool:
|
||||
name: 1es-mariner-2.0-arm64
|
||||
os: linux
|
||||
hostArchitecture: arm64
|
||||
container: ubuntu-2004-arm64
|
||||
steps:
|
||||
- template: build/azure-pipelines/alpine/cli-build-alpine.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_ALPINE_ARM64: ${{ parameters.VSCODE_BUILD_ALPINE_ARM64 }}
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true)) }}:
|
||||
- job: CLIAlpineARM64
|
||||
pool:
|
||||
name: 1es-mariner-2.0-arm64
|
||||
os: linux
|
||||
hostArchitecture: arm64
|
||||
container: ubuntu-2004-arm64
|
||||
steps:
|
||||
- template: build/azure-pipelines/alpine/cli-build-alpine.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_ALPINE_ARM64: ${{ parameters.VSCODE_BUILD_ALPINE_ARM64 }}
|
||||
|
||||
- ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}:
|
||||
- job: CLIMacOSX64
|
||||
pool:
|
||||
name: Azure Pipelines
|
||||
image: macOS-13
|
||||
os: macOS
|
||||
steps:
|
||||
- template: build/azure-pipelines/darwin/cli-build-darwin.yml@self
|
||||
parameters:
|
||||
VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }}
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_MACOS: ${{ parameters.VSCODE_BUILD_MACOS }}
|
||||
- ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}:
|
||||
- job: CLIMacOSX64
|
||||
pool:
|
||||
name: Azure Pipelines
|
||||
image: macOS-13
|
||||
os: macOS
|
||||
steps:
|
||||
- template: build/azure-pipelines/darwin/cli-build-darwin.yml@self
|
||||
parameters:
|
||||
VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }}
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_MACOS: ${{ parameters.VSCODE_BUILD_MACOS }}
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}:
|
||||
- job: CLIMacOSARM64
|
||||
pool:
|
||||
name: Azure Pipelines
|
||||
image: macOS-13
|
||||
os: macOS
|
||||
steps:
|
||||
- template: build/azure-pipelines/darwin/cli-build-darwin.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_MACOS_ARM64: ${{ parameters.VSCODE_BUILD_MACOS_ARM64 }}
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}:
|
||||
- job: CLIMacOSARM64
|
||||
pool:
|
||||
name: Azure Pipelines
|
||||
image: macOS-13
|
||||
os: macOS
|
||||
steps:
|
||||
- template: build/azure-pipelines/darwin/cli-build-darwin.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_MACOS_ARM64: ${{ parameters.VSCODE_BUILD_MACOS_ARM64 }}
|
||||
|
||||
- ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}:
|
||||
- job: CLIWindowsX64
|
||||
pool:
|
||||
name: 1es-windows-2019-x64
|
||||
os: windows
|
||||
steps:
|
||||
- template: build/azure-pipelines/win32/cli-build-win32.yml@self
|
||||
parameters:
|
||||
VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }}
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_WIN32: ${{ parameters.VSCODE_BUILD_WIN32 }}
|
||||
- ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}:
|
||||
- job: CLIWindowsX64
|
||||
pool:
|
||||
name: 1es-windows-2019-x64
|
||||
os: windows
|
||||
steps:
|
||||
- template: build/azure-pipelines/win32/cli-build-win32.yml@self
|
||||
parameters:
|
||||
VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }}
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_WIN32: ${{ parameters.VSCODE_BUILD_WIN32 }}
|
||||
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
|
||||
- job: CLIWindowsARM64
|
||||
pool:
|
||||
name: 1es-windows-2019-x64
|
||||
os: windows
|
||||
steps:
|
||||
- template: build/azure-pipelines/win32/cli-build-win32.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_WIN32_ARM64: ${{ parameters.VSCODE_BUILD_WIN32_ARM64 }}
|
||||
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
|
||||
- job: CLIWindowsARM64
|
||||
pool:
|
||||
name: 1es-windows-2019-x64
|
||||
os: windows
|
||||
steps:
|
||||
- template: build/azure-pipelines/win32/cli-build-win32.yml@self
|
||||
parameters:
|
||||
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
|
||||
VSCODE_BUILD_WIN32_ARM64: ${{ parameters.VSCODE_BUILD_WIN32_ARM64 }}
|
||||
|
||||
- stage: CustomSDL
|
||||
dependsOn: []
|
||||
|
@ -331,7 +332,8 @@ extends:
|
|||
- stage: Windows
|
||||
dependsOn:
|
||||
- Compile
|
||||
- CompileCLI
|
||||
- ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
|
||||
- CompileCLI
|
||||
pool:
|
||||
name: 1es-windows-2019-x64
|
||||
os: windows
|
||||
|
@ -422,7 +424,8 @@ extends:
|
|||
- stage: Linux
|
||||
dependsOn:
|
||||
- Compile
|
||||
- CompileCLI
|
||||
- ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
|
||||
- CompileCLI
|
||||
pool:
|
||||
name: 1es-ubuntu-20.04-x64
|
||||
os: linux
|
||||
|
@ -580,7 +583,8 @@ extends:
|
|||
- stage: Alpine
|
||||
dependsOn:
|
||||
- Compile
|
||||
- CompileCLI
|
||||
- ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
|
||||
- CompileCLI
|
||||
pool:
|
||||
name: 1es-ubuntu-20.04-x64
|
||||
os: linux
|
||||
|
@ -606,7 +610,8 @@ extends:
|
|||
- stage: macOS
|
||||
dependsOn:
|
||||
- Compile
|
||||
- CompileCLI
|
||||
- ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
|
||||
- CompileCLI
|
||||
pool:
|
||||
name: Azure Pipelines
|
||||
image: macOS-13
|
||||
|
|
|
@ -16,13 +16,33 @@ const commit = process.env['BUILD_SOURCEVERSION'];
|
|||
const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']);
|
||||
function main() {
|
||||
return new Promise((c, e) => {
|
||||
es.merge(vfs.src('out-vscode-web-min/nls.metadata.json', { base: 'out-vscode-web-min' }), vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' }))
|
||||
const combinedMetadataJson = es.merge(
|
||||
// vscode: we are not using `out-build/nls.metadata.json` here because
|
||||
// it includes metadata for translators for `keys`. but for our purpose
|
||||
// we want only the `keys` and `messages` as `string`.
|
||||
es.merge(vfs.src('out-build/nls.keys.json', { base: 'out-build' }), vfs.src('out-build/nls.messages.json', { base: 'out-build' }))
|
||||
.pipe(merge({
|
||||
fileName: 'vscode.json',
|
||||
jsonSpace: '',
|
||||
concatArrays: true,
|
||||
edit: (parsedJson, file) => {
|
||||
if (file.base === 'out-build') {
|
||||
if (file.basename === 'nls.keys.json') {
|
||||
return { keys: parsedJson };
|
||||
}
|
||||
else {
|
||||
return { messages: parsedJson };
|
||||
}
|
||||
}
|
||||
}
|
||||
})),
|
||||
// extensions
|
||||
vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })).pipe(merge({
|
||||
fileName: 'combined.nls.metadata.json',
|
||||
jsonSpace: '',
|
||||
concatArrays: true,
|
||||
edit: (parsedJson, file) => {
|
||||
if (file.base === 'out-vscode-web-min') {
|
||||
if (file.basename === 'vscode.json') {
|
||||
return { vscode: parsedJson };
|
||||
}
|
||||
// Handle extensions and follow the same structure as the Core nls file.
|
||||
|
@ -72,13 +92,15 @@ function main() {
|
|||
const key = manifestJson.publisher + '.' + manifestJson.name;
|
||||
return { [key]: parsedJson };
|
||||
},
|
||||
}))
|
||||
}));
|
||||
const nlsMessagesJs = vfs.src('out-build/nls.messages.js', { base: 'out-build' });
|
||||
es.merge(combinedMetadataJson, nlsMessagesJs)
|
||||
.pipe(gzip({ append: false }))
|
||||
.pipe(vfs.dest('./nlsMetadata'))
|
||||
.pipe(es.through(function (data) {
|
||||
console.log(`Uploading ${data.path}`);
|
||||
// trigger artifact upload
|
||||
console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=combined.nls.metadata.json]${data.path}`);
|
||||
console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=${data.basename}]${data.path}`);
|
||||
this.emit('data', data);
|
||||
}))
|
||||
.pipe(azure.upload({
|
||||
|
|
|
@ -24,79 +24,103 @@ interface NlsMetadata {
|
|||
|
||||
function main(): Promise<void> {
|
||||
return new Promise((c, e) => {
|
||||
|
||||
es.merge(
|
||||
vfs.src('out-vscode-web-min/nls.metadata.json', { base: 'out-vscode-web-min' }),
|
||||
vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }),
|
||||
vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }),
|
||||
vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' }))
|
||||
.pipe(merge({
|
||||
fileName: 'combined.nls.metadata.json',
|
||||
jsonSpace: '',
|
||||
concatArrays: true,
|
||||
edit: (parsedJson, file) => {
|
||||
if (file.base === 'out-vscode-web-min') {
|
||||
return { vscode: parsedJson };
|
||||
}
|
||||
|
||||
// Handle extensions and follow the same structure as the Core nls file.
|
||||
switch (file.basename) {
|
||||
case 'package.nls.json':
|
||||
// put package.nls.json content in Core NlsMetadata format
|
||||
// language packs use the key "package" to specify that
|
||||
// translations are for the package.json file
|
||||
parsedJson = {
|
||||
messages: {
|
||||
package: Object.values(parsedJson)
|
||||
},
|
||||
keys: {
|
||||
package: Object.keys(parsedJson)
|
||||
},
|
||||
bundles: {
|
||||
main: ['package']
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case 'nls.metadata.header.json':
|
||||
parsedJson = { header: parsedJson };
|
||||
break;
|
||||
|
||||
case 'nls.metadata.json': {
|
||||
// put nls.metadata.json content in Core NlsMetadata format
|
||||
const modules = Object.keys(parsedJson);
|
||||
|
||||
const json: NlsMetadata = {
|
||||
keys: {},
|
||||
messages: {},
|
||||
bundles: {
|
||||
main: []
|
||||
}
|
||||
};
|
||||
for (const module of modules) {
|
||||
json.messages[module] = parsedJson[module].messages;
|
||||
json.keys[module] = parsedJson[module].keys;
|
||||
json.bundles.main.push(module);
|
||||
const combinedMetadataJson = es.merge(
|
||||
// vscode: we are not using `out-build/nls.metadata.json` here because
|
||||
// it includes metadata for translators for `keys`. but for our purpose
|
||||
// we want only the `keys` and `messages` as `string`.
|
||||
es.merge(
|
||||
vfs.src('out-build/nls.keys.json', { base: 'out-build' }),
|
||||
vfs.src('out-build/nls.messages.json', { base: 'out-build' }))
|
||||
.pipe(merge({
|
||||
fileName: 'vscode.json',
|
||||
jsonSpace: '',
|
||||
concatArrays: true,
|
||||
edit: (parsedJson, file) => {
|
||||
if (file.base === 'out-build') {
|
||||
if (file.basename === 'nls.keys.json') {
|
||||
return { keys: parsedJson };
|
||||
} else {
|
||||
return { messages: parsedJson };
|
||||
}
|
||||
parsedJson = json;
|
||||
break;
|
||||
}
|
||||
}
|
||||
})),
|
||||
|
||||
// Get extension id and use that as the key
|
||||
const folderPath = path.join(file.base, file.relative.split('/')[0]);
|
||||
const manifest = readFileSync(path.join(folderPath, 'package.json'), 'utf-8');
|
||||
const manifestJson = JSON.parse(manifest);
|
||||
const key = manifestJson.publisher + '.' + manifestJson.name;
|
||||
return { [key]: parsedJson };
|
||||
},
|
||||
}))
|
||||
// extensions
|
||||
vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }),
|
||||
vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }),
|
||||
vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })
|
||||
).pipe(merge({
|
||||
fileName: 'combined.nls.metadata.json',
|
||||
jsonSpace: '',
|
||||
concatArrays: true,
|
||||
edit: (parsedJson, file) => {
|
||||
if (file.basename === 'vscode.json') {
|
||||
return { vscode: parsedJson };
|
||||
}
|
||||
|
||||
// Handle extensions and follow the same structure as the Core nls file.
|
||||
switch (file.basename) {
|
||||
case 'package.nls.json':
|
||||
// put package.nls.json content in Core NlsMetadata format
|
||||
// language packs use the key "package" to specify that
|
||||
// translations are for the package.json file
|
||||
parsedJson = {
|
||||
messages: {
|
||||
package: Object.values(parsedJson)
|
||||
},
|
||||
keys: {
|
||||
package: Object.keys(parsedJson)
|
||||
},
|
||||
bundles: {
|
||||
main: ['package']
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case 'nls.metadata.header.json':
|
||||
parsedJson = { header: parsedJson };
|
||||
break;
|
||||
|
||||
case 'nls.metadata.json': {
|
||||
// put nls.metadata.json content in Core NlsMetadata format
|
||||
const modules = Object.keys(parsedJson);
|
||||
|
||||
const json: NlsMetadata = {
|
||||
keys: {},
|
||||
messages: {},
|
||||
bundles: {
|
||||
main: []
|
||||
}
|
||||
};
|
||||
for (const module of modules) {
|
||||
json.messages[module] = parsedJson[module].messages;
|
||||
json.keys[module] = parsedJson[module].keys;
|
||||
json.bundles.main.push(module);
|
||||
}
|
||||
parsedJson = json;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get extension id and use that as the key
|
||||
const folderPath = path.join(file.base, file.relative.split('/')[0]);
|
||||
const manifest = readFileSync(path.join(folderPath, 'package.json'), 'utf-8');
|
||||
const manifestJson = JSON.parse(manifest);
|
||||
const key = manifestJson.publisher + '.' + manifestJson.name;
|
||||
return { [key]: parsedJson };
|
||||
},
|
||||
}));
|
||||
|
||||
const nlsMessagesJs = vfs.src('out-build/nls.messages.js', { base: 'out-build' });
|
||||
|
||||
es.merge(combinedMetadataJson, nlsMessagesJs)
|
||||
.pipe(gzip({ append: false }))
|
||||
.pipe(vfs.dest('./nlsMetadata'))
|
||||
.pipe(es.through(function (data: Vinyl) {
|
||||
console.log(`Uploading ${data.path}`);
|
||||
// trigger artifact upload
|
||||
console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=combined.nls.metadata.json]${data.path}`);
|
||||
console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=${data.basename}]${data.path}`);
|
||||
this.emit('data', data);
|
||||
}))
|
||||
.pipe(azure.upload({
|
||||
|
|
|
@ -152,7 +152,7 @@ steps:
|
|||
|
||||
- script: |
|
||||
set -e
|
||||
AZURE_STORAGE_ACCOUNT="ticino" \
|
||||
AZURE_STORAGE_ACCOUNT="vscodeweb" \
|
||||
AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \
|
||||
AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \
|
||||
AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \
|
||||
|
|
|
@ -86,7 +86,7 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => {
|
|||
});
|
||||
|
||||
// Disable mangling for the editor, as it complicates debugging & quite a few users rely on private/protected fields.
|
||||
const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true, { disableMangle: true }));
|
||||
const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true, { disableMangle: true, preserveEnglish: true }));
|
||||
|
||||
const optimizeEditorAMDTask = task.define('optimize-editor-amd', optimize.optimizeTask(
|
||||
{
|
||||
|
|
|
@ -58,6 +58,9 @@ const serverResources = [
|
|||
'out-build/bootstrap-amd.js',
|
||||
'out-build/bootstrap-node.js',
|
||||
|
||||
// NLS
|
||||
'out-build/nls.messages.json',
|
||||
|
||||
// Performance
|
||||
'out-build/vs/base/common/performance.js',
|
||||
|
||||
|
@ -82,6 +85,9 @@ const serverWithWebResources = [
|
|||
// Include all of server...
|
||||
...serverResources,
|
||||
|
||||
// NLS
|
||||
'out-build/nls.messages.js',
|
||||
|
||||
// ...and all of web
|
||||
...vscodeWebResourceIncludes
|
||||
];
|
||||
|
|
|
@ -56,6 +56,8 @@ const vscodeResources = [
|
|||
'out-build/bootstrap-amd.js',
|
||||
'out-build/bootstrap-node.js',
|
||||
'out-build/bootstrap-window.js',
|
||||
'out-build/nls.messages.json',
|
||||
'out-build/nls.keys.json',
|
||||
'out-build/vs/**/*.{svg,png,html,jpg,mp3}',
|
||||
'!out-build/vs/code/browser/**/*.html',
|
||||
'!out-build/vs/code/**/*-dev.html',
|
||||
|
@ -496,17 +498,12 @@ gulp.task(task.define(
|
|||
core,
|
||||
compileExtensionsBuildTask,
|
||||
function () {
|
||||
const pathToMetadata = './out-vscode/nls.metadata.json';
|
||||
const pathToRehWebMetadata = './out-vscode-reh-web/nls.metadata.json';
|
||||
const pathToMetadata = './out-build/nls.metadata.json';
|
||||
const pathToExtensions = '.build/extensions/*';
|
||||
const pathToSetup = 'build/win32/i18n/messages.en.isl';
|
||||
|
||||
return es.merge(
|
||||
gulp.src([pathToMetadata, pathToRehWebMetadata]).pipe(merge({
|
||||
fileName: 'nls.metadata.json',
|
||||
jsonSpace: '',
|
||||
concatArrays: true
|
||||
})).pipe(i18n.createXlfFilesForCoreBundle()),
|
||||
gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()),
|
||||
gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
|
||||
gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
|
||||
).pipe(vfs.dest('../vscode-translations-export'));
|
||||
|
|
|
@ -41,7 +41,7 @@ function getTypeScriptCompilerOptions(src) {
|
|||
options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 0 : 1;
|
||||
return options;
|
||||
}
|
||||
function createCompile(src, build, emitError, transpileOnly) {
|
||||
function createCompile(src, { build, emitError, transpileOnly, preserveEnglish }) {
|
||||
const tsb = require('./tsb');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json');
|
||||
|
@ -71,7 +71,7 @@ function createCompile(src, build, emitError, transpileOnly) {
|
|||
.pipe(util.loadSourcemaps())
|
||||
.pipe(compilation(token))
|
||||
.pipe(noDeclarationsFilter)
|
||||
.pipe(util.$if(build, nls.nls()))
|
||||
.pipe(util.$if(build, nls.nls({ preserveEnglish })))
|
||||
.pipe(noDeclarationsFilter.restore)
|
||||
.pipe(util.$if(!transpileOnly, sourcemaps.write('.', {
|
||||
addComment: false,
|
||||
|
@ -90,7 +90,7 @@ function createCompile(src, build, emitError, transpileOnly) {
|
|||
}
|
||||
function transpileTask(src, out, swc) {
|
||||
const task = () => {
|
||||
const transpile = createCompile(src, false, true, { swc });
|
||||
const transpile = createCompile(src, { build: false, emitError: true, transpileOnly: { swc }, preserveEnglish: false });
|
||||
const srcPipe = gulp.src(`${src}/**`, { base: `${src}` });
|
||||
return srcPipe
|
||||
.pipe(transpile())
|
||||
|
@ -104,7 +104,7 @@ function compileTask(src, out, build, options = {}) {
|
|||
if (os.totalmem() < 4_000_000_000) {
|
||||
throw new Error('compilation requires 4GB of RAM');
|
||||
}
|
||||
const compile = createCompile(src, build, true, false);
|
||||
const compile = createCompile(src, { build, emitError: true, transpileOnly: false, preserveEnglish: !!options.preserveEnglish });
|
||||
const srcPipe = gulp.src(`${src}/**`, { base: `${src}` });
|
||||
const generator = new MonacoGenerator(false);
|
||||
if (src === 'src') {
|
||||
|
@ -141,7 +141,7 @@ function compileTask(src, out, build, options = {}) {
|
|||
}
|
||||
function watchTask(out, build) {
|
||||
const task = () => {
|
||||
const compile = createCompile('src', build, false, false);
|
||||
const compile = createCompile('src', { build, emitError: false, transpileOnly: false, preserveEnglish: false });
|
||||
const src = gulp.src('src/**', { base: 'src' });
|
||||
const watchSrc = watch('src/**', { base: 'src', readDelay: 200 });
|
||||
const generator = new MonacoGenerator(true);
|
||||
|
|
|
@ -42,7 +42,14 @@ function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions {
|
|||
return options;
|
||||
}
|
||||
|
||||
function createCompile(src: string, build: boolean, emitError: boolean, transpileOnly: boolean | { swc: boolean }) {
|
||||
interface ICompileTaskOptions {
|
||||
readonly build: boolean;
|
||||
readonly emitError: boolean;
|
||||
readonly transpileOnly: boolean | { swc: boolean };
|
||||
readonly preserveEnglish: boolean;
|
||||
}
|
||||
|
||||
function createCompile(src: string, { build, emitError, transpileOnly, preserveEnglish }: ICompileTaskOptions) {
|
||||
const tsb = require('./tsb') as typeof import('./tsb');
|
||||
const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps');
|
||||
|
||||
|
@ -79,7 +86,7 @@ function createCompile(src: string, build: boolean, emitError: boolean, transpil
|
|||
.pipe(util.loadSourcemaps())
|
||||
.pipe(compilation(token))
|
||||
.pipe(noDeclarationsFilter)
|
||||
.pipe(util.$if(build, nls.nls()))
|
||||
.pipe(util.$if(build, nls.nls({ preserveEnglish })))
|
||||
.pipe(noDeclarationsFilter.restore)
|
||||
.pipe(util.$if(!transpileOnly, sourcemaps.write('.', {
|
||||
addComment: false,
|
||||
|
@ -102,7 +109,7 @@ export function transpileTask(src: string, out: string, swc: boolean): task.Stre
|
|||
|
||||
const task = () => {
|
||||
|
||||
const transpile = createCompile(src, false, true, { swc });
|
||||
const transpile = createCompile(src, { build: false, emitError: true, transpileOnly: { swc }, preserveEnglish: false });
|
||||
const srcPipe = gulp.src(`${src}/**`, { base: `${src}` });
|
||||
|
||||
return srcPipe
|
||||
|
@ -114,7 +121,7 @@ export function transpileTask(src: string, out: string, swc: boolean): task.Stre
|
|||
return task;
|
||||
}
|
||||
|
||||
export function compileTask(src: string, out: string, build: boolean, options: { disableMangle?: boolean } = {}): task.StreamTask {
|
||||
export function compileTask(src: string, out: string, build: boolean, options: { disableMangle?: boolean; preserveEnglish?: boolean } = {}): task.StreamTask {
|
||||
|
||||
const task = () => {
|
||||
|
||||
|
@ -122,7 +129,7 @@ export function compileTask(src: string, out: string, build: boolean, options: {
|
|||
throw new Error('compilation requires 4GB of RAM');
|
||||
}
|
||||
|
||||
const compile = createCompile(src, build, true, false);
|
||||
const compile = createCompile(src, { build, emitError: true, transpileOnly: false, preserveEnglish: !!options.preserveEnglish });
|
||||
const srcPipe = gulp.src(`${src}/**`, { base: `${src}` });
|
||||
const generator = new MonacoGenerator(false);
|
||||
if (src === 'src') {
|
||||
|
@ -166,7 +173,7 @@ export function compileTask(src: string, out: string, build: boolean, options: {
|
|||
export function watchTask(out: string, build: boolean): task.StreamTask {
|
||||
|
||||
const task = () => {
|
||||
const compile = createCompile('src', build, false, false);
|
||||
const compile = createCompile('src', { build, emitError: false, transpileOnly: false, preserveEnglish: false });
|
||||
|
||||
const src = gulp.src('src/**', { base: 'src' });
|
||||
const watchSrc = watch('src/**', { base: 'src', readDelay: 200 });
|
||||
|
|
|
@ -23,6 +23,7 @@ const fancyLog = require("fancy-log");
|
|||
const ansiColors = require("ansi-colors");
|
||||
const iconv = require("@vscode/iconv-lite-umd");
|
||||
const l10n_dev_1 = require("@vscode/l10n-dev");
|
||||
const REPO_ROOT_PATH = path.join(__dirname, '../..');
|
||||
function log(message, ...rest) {
|
||||
fancyLog(ansiColors.green('[i18n]'), message, ...rest);
|
||||
}
|
||||
|
@ -63,6 +64,17 @@ var BundledFormat;
|
|||
}
|
||||
BundledFormat.is = is;
|
||||
})(BundledFormat || (BundledFormat = {}));
|
||||
var NLSKeysFormat;
|
||||
(function (NLSKeysFormat) {
|
||||
function is(value) {
|
||||
if (value === undefined) {
|
||||
return false;
|
||||
}
|
||||
const candidate = value;
|
||||
return Array.isArray(candidate) && Array.isArray(candidate[1]);
|
||||
}
|
||||
NLSKeysFormat.is = is;
|
||||
})(NLSKeysFormat || (NLSKeysFormat = {}));
|
||||
class Line {
|
||||
buffer = [];
|
||||
constructor(indent = 0) {
|
||||
|
@ -265,67 +277,8 @@ function stripComments(content) {
|
|||
});
|
||||
return result;
|
||||
}
|
||||
function escapeCharacters(value) {
|
||||
const result = [];
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const ch = value.charAt(i);
|
||||
switch (ch) {
|
||||
case '\'':
|
||||
result.push('\\\'');
|
||||
break;
|
||||
case '"':
|
||||
result.push('\\"');
|
||||
break;
|
||||
case '\\':
|
||||
result.push('\\\\');
|
||||
break;
|
||||
case '\n':
|
||||
result.push('\\n');
|
||||
break;
|
||||
case '\r':
|
||||
result.push('\\r');
|
||||
break;
|
||||
case '\t':
|
||||
result.push('\\t');
|
||||
break;
|
||||
case '\b':
|
||||
result.push('\\b');
|
||||
break;
|
||||
case '\f':
|
||||
result.push('\\f');
|
||||
break;
|
||||
default:
|
||||
result.push(ch);
|
||||
}
|
||||
}
|
||||
return result.join('');
|
||||
}
|
||||
function processCoreBundleFormat(fileHeader, languages, json, emitter) {
|
||||
const keysSection = json.keys;
|
||||
const messageSection = json.messages;
|
||||
const bundleSection = json.bundles;
|
||||
const statistics = Object.create(null);
|
||||
const defaultMessages = Object.create(null);
|
||||
const modules = Object.keys(keysSection);
|
||||
modules.forEach((module) => {
|
||||
const keys = keysSection[module];
|
||||
const messages = messageSection[module];
|
||||
if (!messages || keys.length !== messages.length) {
|
||||
emitter.emit('error', `Message for module ${module} corrupted. Mismatch in number of keys and messages.`);
|
||||
return;
|
||||
}
|
||||
const messageMap = Object.create(null);
|
||||
defaultMessages[module] = messageMap;
|
||||
keys.map((key, i) => {
|
||||
if (typeof key === 'string') {
|
||||
messageMap[key] = messages[i];
|
||||
}
|
||||
else {
|
||||
messageMap[key.key] = messages[i];
|
||||
}
|
||||
});
|
||||
});
|
||||
const languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n');
|
||||
function processCoreBundleFormat(base, fileHeader, languages, json, emitter) {
|
||||
const languageDirectory = path.join(REPO_ROOT_PATH, '..', 'vscode-loc', 'i18n');
|
||||
if (!fs.existsSync(languageDirectory)) {
|
||||
log(`No VS Code localization repository found. Looking at ${languageDirectory}`);
|
||||
log(`To bundle translations please check out the vscode-loc repository as a sibling of the vscode repository.`);
|
||||
|
@ -335,8 +288,6 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) {
|
|||
if (process.env['VSCODE_BUILD_VERBOSE']) {
|
||||
log(`Generating nls bundles for: ${language.id}`);
|
||||
}
|
||||
statistics[language.id] = 0;
|
||||
const localizedModules = Object.create(null);
|
||||
const languageFolderName = language.translationId || language.id;
|
||||
const i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json');
|
||||
let allMessages;
|
||||
|
@ -344,87 +295,36 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) {
|
|||
const content = stripComments(fs.readFileSync(i18nFile, 'utf8'));
|
||||
allMessages = JSON.parse(content);
|
||||
}
|
||||
modules.forEach((module) => {
|
||||
const order = keysSection[module];
|
||||
let moduleMessage;
|
||||
if (allMessages) {
|
||||
moduleMessage = allMessages.contents[module];
|
||||
let nlsIndex = 0;
|
||||
const nlsResult = [];
|
||||
for (const [moduleId, nlsKeys] of json) {
|
||||
const moduleTranslations = allMessages?.contents[moduleId];
|
||||
for (const nlsKey of nlsKeys) {
|
||||
nlsResult.push(moduleTranslations?.[nlsKey]); // pushing `undefined` is fine, as we keep english strings as fallback for monaco editor in the build
|
||||
nlsIndex++;
|
||||
}
|
||||
if (!moduleMessage) {
|
||||
if (process.env['VSCODE_BUILD_VERBOSE']) {
|
||||
log(`No localized messages found for module ${module}. Using default messages.`);
|
||||
}
|
||||
moduleMessage = defaultMessages[module];
|
||||
statistics[language.id] = statistics[language.id] + Object.keys(moduleMessage).length;
|
||||
}
|
||||
const localizedMessages = [];
|
||||
order.forEach((keyInfo) => {
|
||||
let key = null;
|
||||
if (typeof keyInfo === 'string') {
|
||||
key = keyInfo;
|
||||
}
|
||||
else {
|
||||
key = keyInfo.key;
|
||||
}
|
||||
let message = moduleMessage[key];
|
||||
if (!message) {
|
||||
if (process.env['VSCODE_BUILD_VERBOSE']) {
|
||||
log(`No localized message found for key ${key} in module ${module}. Using default message.`);
|
||||
}
|
||||
message = defaultMessages[module][key];
|
||||
statistics[language.id] = statistics[language.id] + 1;
|
||||
}
|
||||
localizedMessages.push(message);
|
||||
});
|
||||
localizedModules[module] = localizedMessages;
|
||||
});
|
||||
Object.keys(bundleSection).forEach((bundle) => {
|
||||
const modules = bundleSection[bundle];
|
||||
const contents = [
|
||||
fileHeader,
|
||||
`define("${bundle}.nls.${language.id}", {`
|
||||
];
|
||||
modules.forEach((module, index) => {
|
||||
contents.push(`\t"${module}": [`);
|
||||
const messages = localizedModules[module];
|
||||
if (!messages) {
|
||||
emitter.emit('error', `Didn't find messages for module ${module}.`);
|
||||
return;
|
||||
}
|
||||
messages.forEach((message, index) => {
|
||||
contents.push(`\t\t"${escapeCharacters(message)}${index < messages.length ? '",' : '"'}`);
|
||||
});
|
||||
contents.push(index < modules.length - 1 ? '\t],' : '\t]');
|
||||
});
|
||||
contents.push('});');
|
||||
emitter.queue(new File({ path: bundle + '.nls.' + language.id + '.js', contents: Buffer.from(contents.join('\n'), 'utf-8') }));
|
||||
});
|
||||
});
|
||||
Object.keys(statistics).forEach(key => {
|
||||
const value = statistics[key];
|
||||
log(`${key} has ${value} untranslated strings.`);
|
||||
});
|
||||
sortedLanguages.forEach(language => {
|
||||
const stats = statistics[language.id];
|
||||
if (!stats) {
|
||||
log(`\tNo translations found for language ${language.id}. Using default language instead.`);
|
||||
}
|
||||
emitter.queue(new File({
|
||||
contents: Buffer.from(`${fileHeader}
|
||||
globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(nlsResult)};
|
||||
globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(language.id)};`),
|
||||
base,
|
||||
path: `${base}/nls.messages.${language.id}.js`
|
||||
}));
|
||||
});
|
||||
}
|
||||
function processNlsFiles(opts) {
|
||||
return (0, event_stream_1.through)(function (file) {
|
||||
const fileName = path.basename(file.path);
|
||||
if (fileName === 'nls.metadata.json') {
|
||||
let json = null;
|
||||
if (file.isBuffer()) {
|
||||
json = JSON.parse(file.contents.toString('utf8'));
|
||||
if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles
|
||||
try {
|
||||
const json = JSON.parse(fs.readFileSync(path.join(REPO_ROOT_PATH, opts.out, 'nls.keys.json')).toString());
|
||||
if (NLSKeysFormat.is(json)) {
|
||||
processCoreBundleFormat(file.base, opts.fileHeader, opts.languages, json, this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.emit('error', `Failed to read component file: ${file.relative}`);
|
||||
return;
|
||||
}
|
||||
if (BundledFormat.is(json)) {
|
||||
processCoreBundleFormat(opts.fileHeader, opts.languages, json, this);
|
||||
catch (error) {
|
||||
this.emit('error', `Failed to read component file: ${error}`);
|
||||
}
|
||||
}
|
||||
this.queue(file);
|
||||
|
|
|
@ -16,6 +16,8 @@ import * as ansiColors from 'ansi-colors';
|
|||
import * as iconv from '@vscode/iconv-lite-umd';
|
||||
import { l10nJsonFormat, getL10nXlf, l10nJsonDetails, getL10nFilesFromXlf, getL10nJson } from '@vscode/l10n-dev';
|
||||
|
||||
const REPO_ROOT_PATH = path.join(__dirname, '../..');
|
||||
|
||||
function log(message: any, ...rest: any[]): void {
|
||||
fancyLog(ansiColors.green('[i18n]'), message, ...rest);
|
||||
}
|
||||
|
@ -91,6 +93,19 @@ module BundledFormat {
|
|||
}
|
||||
}
|
||||
|
||||
type NLSKeysFormat = [string /* module ID */, string[] /* keys */];
|
||||
|
||||
module NLSKeysFormat {
|
||||
export function is(value: any): value is NLSKeysFormat {
|
||||
if (value === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const candidate = value as NLSKeysFormat;
|
||||
return Array.isArray(candidate) && Array.isArray(candidate[1]);
|
||||
}
|
||||
}
|
||||
|
||||
interface BundledExtensionFormat {
|
||||
[key: string]: {
|
||||
messages: string[];
|
||||
|
@ -329,70 +344,8 @@ function stripComments(content: string): string {
|
|||
return result;
|
||||
}
|
||||
|
||||
function escapeCharacters(value: string): string {
|
||||
const result: string[] = [];
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const ch = value.charAt(i);
|
||||
switch (ch) {
|
||||
case '\'':
|
||||
result.push('\\\'');
|
||||
break;
|
||||
case '"':
|
||||
result.push('\\"');
|
||||
break;
|
||||
case '\\':
|
||||
result.push('\\\\');
|
||||
break;
|
||||
case '\n':
|
||||
result.push('\\n');
|
||||
break;
|
||||
case '\r':
|
||||
result.push('\\r');
|
||||
break;
|
||||
case '\t':
|
||||
result.push('\\t');
|
||||
break;
|
||||
case '\b':
|
||||
result.push('\\b');
|
||||
break;
|
||||
case '\f':
|
||||
result.push('\\f');
|
||||
break;
|
||||
default:
|
||||
result.push(ch);
|
||||
}
|
||||
}
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
function processCoreBundleFormat(fileHeader: string, languages: Language[], json: BundledFormat, emitter: ThroughStream) {
|
||||
const keysSection = json.keys;
|
||||
const messageSection = json.messages;
|
||||
const bundleSection = json.bundles;
|
||||
|
||||
const statistics: Record<string, number> = Object.create(null);
|
||||
|
||||
const defaultMessages: Record<string, Record<string, string>> = Object.create(null);
|
||||
const modules = Object.keys(keysSection);
|
||||
modules.forEach((module) => {
|
||||
const keys = keysSection[module];
|
||||
const messages = messageSection[module];
|
||||
if (!messages || keys.length !== messages.length) {
|
||||
emitter.emit('error', `Message for module ${module} corrupted. Mismatch in number of keys and messages.`);
|
||||
return;
|
||||
}
|
||||
const messageMap: Record<string, string> = Object.create(null);
|
||||
defaultMessages[module] = messageMap;
|
||||
keys.map((key, i) => {
|
||||
if (typeof key === 'string') {
|
||||
messageMap[key] = messages[i];
|
||||
} else {
|
||||
messageMap[key.key] = messages[i];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n');
|
||||
function processCoreBundleFormat(base: string, fileHeader: string, languages: Language[], json: NLSKeysFormat, emitter: ThroughStream) {
|
||||
const languageDirectory = path.join(REPO_ROOT_PATH, '..', 'vscode-loc', 'i18n');
|
||||
if (!fs.existsSync(languageDirectory)) {
|
||||
log(`No VS Code localization repository found. Looking at ${languageDirectory}`);
|
||||
log(`To bundle translations please check out the vscode-loc repository as a sibling of the vscode repository.`);
|
||||
|
@ -403,8 +356,6 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json
|
|||
log(`Generating nls bundles for: ${language.id}`);
|
||||
}
|
||||
|
||||
statistics[language.id] = 0;
|
||||
const localizedModules: Record<string, string[]> = Object.create(null);
|
||||
const languageFolderName = language.translationId || language.id;
|
||||
const i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json');
|
||||
let allMessages: I18nFormat | undefined;
|
||||
|
@ -412,86 +363,38 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json
|
|||
const content = stripComments(fs.readFileSync(i18nFile, 'utf8'));
|
||||
allMessages = JSON.parse(content);
|
||||
}
|
||||
modules.forEach((module) => {
|
||||
const order = keysSection[module];
|
||||
let moduleMessage: { [messageKey: string]: string } | undefined;
|
||||
if (allMessages) {
|
||||
moduleMessage = allMessages.contents[module];
|
||||
|
||||
let nlsIndex = 0;
|
||||
const nlsResult: Array<string | undefined> = [];
|
||||
for (const [moduleId, nlsKeys] of json) {
|
||||
const moduleTranslations = allMessages?.contents[moduleId];
|
||||
for (const nlsKey of nlsKeys) {
|
||||
nlsResult.push(moduleTranslations?.[nlsKey]); // pushing `undefined` is fine, as we keep english strings as fallback for monaco editor in the build
|
||||
nlsIndex++;
|
||||
}
|
||||
if (!moduleMessage) {
|
||||
if (process.env['VSCODE_BUILD_VERBOSE']) {
|
||||
log(`No localized messages found for module ${module}. Using default messages.`);
|
||||
}
|
||||
moduleMessage = defaultMessages[module];
|
||||
statistics[language.id] = statistics[language.id] + Object.keys(moduleMessage).length;
|
||||
}
|
||||
const localizedMessages: string[] = [];
|
||||
order.forEach((keyInfo) => {
|
||||
let key: string | null = null;
|
||||
if (typeof keyInfo === 'string') {
|
||||
key = keyInfo;
|
||||
} else {
|
||||
key = keyInfo.key;
|
||||
}
|
||||
let message: string = moduleMessage![key];
|
||||
if (!message) {
|
||||
if (process.env['VSCODE_BUILD_VERBOSE']) {
|
||||
log(`No localized message found for key ${key} in module ${module}. Using default message.`);
|
||||
}
|
||||
message = defaultMessages[module][key];
|
||||
statistics[language.id] = statistics[language.id] + 1;
|
||||
}
|
||||
localizedMessages.push(message);
|
||||
});
|
||||
localizedModules[module] = localizedMessages;
|
||||
});
|
||||
Object.keys(bundleSection).forEach((bundle) => {
|
||||
const modules = bundleSection[bundle];
|
||||
const contents: string[] = [
|
||||
fileHeader,
|
||||
`define("${bundle}.nls.${language.id}", {`
|
||||
];
|
||||
modules.forEach((module, index) => {
|
||||
contents.push(`\t"${module}": [`);
|
||||
const messages = localizedModules[module];
|
||||
if (!messages) {
|
||||
emitter.emit('error', `Didn't find messages for module ${module}.`);
|
||||
return;
|
||||
}
|
||||
messages.forEach((message, index) => {
|
||||
contents.push(`\t\t"${escapeCharacters(message)}${index < messages.length ? '",' : '"'}`);
|
||||
});
|
||||
contents.push(index < modules.length - 1 ? '\t],' : '\t]');
|
||||
});
|
||||
contents.push('});');
|
||||
emitter.queue(new File({ path: bundle + '.nls.' + language.id + '.js', contents: Buffer.from(contents.join('\n'), 'utf-8') }));
|
||||
});
|
||||
});
|
||||
Object.keys(statistics).forEach(key => {
|
||||
const value = statistics[key];
|
||||
log(`${key} has ${value} untranslated strings.`);
|
||||
});
|
||||
sortedLanguages.forEach(language => {
|
||||
const stats = statistics[language.id];
|
||||
if (!stats) {
|
||||
log(`\tNo translations found for language ${language.id}. Using default language instead.`);
|
||||
}
|
||||
|
||||
emitter.queue(new File({
|
||||
contents: Buffer.from(`${fileHeader}
|
||||
globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(nlsResult)};
|
||||
globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(language.id)};`),
|
||||
base,
|
||||
path: `${base}/nls.messages.${language.id}.js`
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
export function processNlsFiles(opts: { fileHeader: string; languages: Language[] }): ThroughStream {
|
||||
export function processNlsFiles(opts: { out: string; fileHeader: string; languages: Language[] }): ThroughStream {
|
||||
return through(function (this: ThroughStream, file: File) {
|
||||
const fileName = path.basename(file.path);
|
||||
if (fileName === 'nls.metadata.json') {
|
||||
let json = null;
|
||||
if (file.isBuffer()) {
|
||||
json = JSON.parse((<Buffer>file.contents).toString('utf8'));
|
||||
} else {
|
||||
this.emit('error', `Failed to read component file: ${file.relative}`);
|
||||
return;
|
||||
}
|
||||
if (BundledFormat.is(json)) {
|
||||
processCoreBundleFormat(opts.fileHeader, opts.languages, json, this);
|
||||
if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles
|
||||
try {
|
||||
const json = JSON.parse(fs.readFileSync(path.join(REPO_ROOT_PATH, opts.out, 'nls.keys.json')).toString());
|
||||
if (NLSKeysFormat.is(json)) {
|
||||
processCoreBundleFormat(file.base, opts.fileHeader, opts.languages, json, this);
|
||||
}
|
||||
} catch (error) {
|
||||
this.emit('error', `Failed to read component file: ${error}`);
|
||||
}
|
||||
}
|
||||
this.queue(file);
|
||||
|
|
126
build/lib/nls.js
126
build/lib/nls.js
|
@ -38,21 +38,11 @@ function clone(object) {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
function template(lines) {
|
||||
let indent = '', wrap = '';
|
||||
if (lines.length > 1) {
|
||||
indent = '\t';
|
||||
wrap = '\n';
|
||||
}
|
||||
return `/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
define([], [${wrap + lines.map(l => indent + l).join(',\n') + wrap}]);`;
|
||||
}
|
||||
/**
|
||||
* Returns a stream containing the patched JavaScript and source maps.
|
||||
*/
|
||||
function nls() {
|
||||
function nls(options) {
|
||||
let base;
|
||||
const input = (0, event_stream_1.through)();
|
||||
const output = input.pipe((0, event_stream_1.through)(function (f) {
|
||||
if (!f.sourceMap) {
|
||||
|
@ -70,7 +60,40 @@ function nls() {
|
|||
if (!typescript) {
|
||||
return this.emit('error', new Error(`File ${f.relative} does not have the original content in the source map.`));
|
||||
}
|
||||
_nls.patchFiles(f, typescript).forEach(f => this.emit('data', f));
|
||||
base = f.base;
|
||||
this.emit('data', _nls.patchFile(f, typescript, options));
|
||||
}, function () {
|
||||
for (const file of [
|
||||
new File({
|
||||
contents: Buffer.from(JSON.stringify({
|
||||
keys: _nls.moduleToNLSKeys,
|
||||
messages: _nls.moduleToNLSMessages,
|
||||
}, null, '\t')),
|
||||
base,
|
||||
path: `${base}/nls.metadata.json`
|
||||
}),
|
||||
new File({
|
||||
contents: Buffer.from(JSON.stringify(_nls.allNLSMessages)),
|
||||
base,
|
||||
path: `${base}/nls.messages.json`
|
||||
}),
|
||||
new File({
|
||||
contents: Buffer.from(JSON.stringify(_nls.allNLSModulesAndKeys)),
|
||||
base,
|
||||
path: `${base}/nls.keys.json`
|
||||
}),
|
||||
new File({
|
||||
contents: Buffer.from(`/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(_nls.allNLSMessages)};`),
|
||||
base,
|
||||
path: `${base}/nls.messages.js`
|
||||
})
|
||||
]) {
|
||||
this.emit('data', file);
|
||||
}
|
||||
this.emit('end');
|
||||
}));
|
||||
return (0, event_stream_1.duplex)(input, output);
|
||||
}
|
||||
|
@ -79,6 +102,11 @@ function isImportNode(ts, node) {
|
|||
}
|
||||
var _nls;
|
||||
(function (_nls) {
|
||||
_nls.moduleToNLSKeys = {};
|
||||
_nls.moduleToNLSMessages = {};
|
||||
_nls.allNLSMessages = [];
|
||||
_nls.allNLSModulesAndKeys = [];
|
||||
let allNLSMessagesIndex = 0;
|
||||
function fileFrom(file, contents, path = file.path) {
|
||||
return new File({
|
||||
contents: Buffer.from(contents),
|
||||
|
@ -146,13 +174,6 @@ var _nls;
|
|||
.filter(d => d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral)
|
||||
.filter(d => d.moduleSpecifier.getText() === '\'vs/nls\'')
|
||||
.filter(d => !!d.importClause && !!d.importClause.namedBindings);
|
||||
const nlsExpressions = importEqualsDeclarations
|
||||
.map(d => d.moduleReference.expression)
|
||||
.concat(importDeclarations.map(d => d.moduleSpecifier))
|
||||
.map(d => ({
|
||||
start: ts.getLineAndCharacterOfPosition(sourceFile, d.getStart()),
|
||||
end: ts.getLineAndCharacterOfPosition(sourceFile, d.getEnd())
|
||||
}));
|
||||
// `nls.localize(...)` calls
|
||||
const nlsLocalizeCallExpressions = importDeclarations
|
||||
.filter(d => !!(d.importClause && d.importClause.namedBindings && d.importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport))
|
||||
|
@ -206,8 +227,7 @@ var _nls;
|
|||
value: a[1].getText()
|
||||
}));
|
||||
return {
|
||||
localizeCalls: localizeCalls.toArray(),
|
||||
nlsExpressions: nlsExpressions.toArray()
|
||||
localizeCalls: localizeCalls.toArray()
|
||||
};
|
||||
}
|
||||
class TextModel {
|
||||
|
@ -262,14 +282,10 @@ var _nls;
|
|||
.flatten().toArray().join('');
|
||||
}
|
||||
}
|
||||
function patchJavascript(patches, contents, moduleId) {
|
||||
function patchJavascript(patches, contents) {
|
||||
const model = new TextModel(contents);
|
||||
// patch the localize calls
|
||||
lazy(patches).reverse().each(p => model.apply(p));
|
||||
// patch the 'vs/nls' imports
|
||||
const firstLine = model.get(0);
|
||||
const patchedFirstLine = firstLine.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`);
|
||||
model.set(0, patchedFirstLine);
|
||||
return model.toString();
|
||||
}
|
||||
function patchSourcemap(patches, rsm, smc) {
|
||||
|
@ -307,14 +323,21 @@ var _nls;
|
|||
}
|
||||
return JSON.parse(smg.toString());
|
||||
}
|
||||
function patch(ts, moduleId, typescript, javascript, sourcemap) {
|
||||
const { localizeCalls, nlsExpressions } = analyze(ts, typescript, 'localize');
|
||||
const { localizeCalls: localize2Calls, nlsExpressions: nls2Expressions } = analyze(ts, typescript, 'localize2');
|
||||
function parseLocalizeKeyOrValue(sourceExpression) {
|
||||
// sourceValue can be "foo", 'foo', `foo` or { .... }
|
||||
// in its evalulated form
|
||||
// we want to return either the string or the object
|
||||
// eslint-disable-next-line no-eval
|
||||
return eval(`(${sourceExpression})`);
|
||||
}
|
||||
function patch(ts, typescript, javascript, sourcemap, options) {
|
||||
const { localizeCalls } = analyze(ts, typescript, 'localize');
|
||||
const { localizeCalls: localize2Calls } = analyze(ts, typescript, 'localize2');
|
||||
if (localizeCalls.length === 0 && localize2Calls.length === 0) {
|
||||
return { javascript, sourcemap };
|
||||
}
|
||||
const nlsKeys = template(localizeCalls.map(lc => lc.key).concat(localize2Calls.map(lc => lc.key)));
|
||||
const nls = template(localizeCalls.map(lc => lc.value).concat(localize2Calls.map(lc => lc.value)));
|
||||
const nlsKeys = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.key)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.key)));
|
||||
const nlsMessages = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.value)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.value)));
|
||||
const smc = new sm.SourceMapConsumer(sourcemap);
|
||||
const positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]);
|
||||
// build patches
|
||||
|
@ -323,16 +346,18 @@ var _nls;
|
|||
const end = lcFrom(smc.generatedPositionFor(positionFrom(c.range.end)));
|
||||
return { span: { start, end }, content: c.content };
|
||||
};
|
||||
let i = 0;
|
||||
const localizePatches = lazy(localizeCalls)
|
||||
.map(lc => ([
|
||||
{ range: lc.keySpan, content: '' + (i++) },
|
||||
.map(lc => (options.preserveEnglish ? [
|
||||
{ range: lc.keySpan, content: `${allNLSMessagesIndex++}` } // localize('key', "message") => localize(<index>, "message")
|
||||
] : [
|
||||
{ range: lc.keySpan, content: `${allNLSMessagesIndex++}` }, // localize('key', "message") => localize(<index>, null)
|
||||
{ range: lc.valueSpan, content: 'null' }
|
||||
]))
|
||||
.flatten()
|
||||
.map(toPatch);
|
||||
const localize2Patches = lazy(localize2Calls)
|
||||
.map(lc => ({ range: lc.keySpan, content: '' + (i++) }))
|
||||
.map(lc => ({ range: lc.keySpan, content: `${allNLSMessagesIndex++}` } // localize2('key', "message") => localize(<index>, "message")
|
||||
))
|
||||
.map(toPatch);
|
||||
// Sort patches by their start position
|
||||
const patches = localizePatches.concat(localize2Patches).toArray().sort((a, b) => {
|
||||
|
@ -352,34 +377,29 @@ var _nls;
|
|||
return 0;
|
||||
}
|
||||
});
|
||||
javascript = patchJavascript(patches, javascript, moduleId);
|
||||
// since imports are not within the sourcemap information,
|
||||
// we must do this MacGyver style
|
||||
if (nlsExpressions.length || nls2Expressions.length) {
|
||||
javascript = javascript.replace(/^define\(.*$/m, line => {
|
||||
return line.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`);
|
||||
});
|
||||
}
|
||||
javascript = patchJavascript(patches, javascript);
|
||||
sourcemap = patchSourcemap(patches, sourcemap, smc);
|
||||
return { javascript, sourcemap, nlsKeys, nls };
|
||||
return { javascript, sourcemap, nlsKeys, nlsMessages };
|
||||
}
|
||||
function patchFiles(javascriptFile, typescript) {
|
||||
function patchFile(javascriptFile, typescript, options) {
|
||||
const ts = require('typescript');
|
||||
// hack?
|
||||
const moduleId = javascriptFile.relative
|
||||
.replace(/\.js$/, '')
|
||||
.replace(/\\/g, '/');
|
||||
const { javascript, sourcemap, nlsKeys, nls } = patch(ts, moduleId, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap);
|
||||
const result = [fileFrom(javascriptFile, javascript)];
|
||||
result[0].sourceMap = sourcemap;
|
||||
const { javascript, sourcemap, nlsKeys, nlsMessages } = patch(ts, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap, options);
|
||||
const result = fileFrom(javascriptFile, javascript);
|
||||
result.sourceMap = sourcemap;
|
||||
if (nlsKeys) {
|
||||
result.push(fileFrom(javascriptFile, nlsKeys, javascriptFile.path.replace(/\.js$/, '.nls.keys.js')));
|
||||
_nls.moduleToNLSKeys[moduleId] = nlsKeys;
|
||||
_nls.allNLSModulesAndKeys.push([moduleId, nlsKeys.map(nlsKey => typeof nlsKey === 'string' ? nlsKey : nlsKey.key)]);
|
||||
}
|
||||
if (nls) {
|
||||
result.push(fileFrom(javascriptFile, nls, javascriptFile.path.replace(/\.js$/, '.nls.js')));
|
||||
if (nlsMessages) {
|
||||
_nls.moduleToNLSMessages[moduleId] = nlsMessages;
|
||||
_nls.allNLSMessages.push(...nlsMessages);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
_nls.patchFiles = patchFiles;
|
||||
_nls.patchFile = patchFile;
|
||||
})(_nls || (_nls = {}));
|
||||
//# sourceMappingURL=nls.js.map
|
150
build/lib/nls.ts
150
build/lib/nls.ts
|
@ -48,24 +48,11 @@ function clone<T extends object>(object: T): T {
|
|||
return result;
|
||||
}
|
||||
|
||||
function template(lines: string[]): string {
|
||||
let indent = '', wrap = '';
|
||||
|
||||
if (lines.length > 1) {
|
||||
indent = '\t';
|
||||
wrap = '\n';
|
||||
}
|
||||
|
||||
return `/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
define([], [${wrap + lines.map(l => indent + l).join(',\n') + wrap}]);`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream containing the patched JavaScript and source maps.
|
||||
*/
|
||||
export function nls(): NodeJS.ReadWriteStream {
|
||||
export function nls(options: { preserveEnglish: boolean }): NodeJS.ReadWriteStream {
|
||||
let base: string;
|
||||
const input = through();
|
||||
const output = input.pipe(through(function (f: FileSourceMap) {
|
||||
if (!f.sourceMap) {
|
||||
|
@ -87,7 +74,41 @@ export function nls(): NodeJS.ReadWriteStream {
|
|||
return this.emit('error', new Error(`File ${f.relative} does not have the original content in the source map.`));
|
||||
}
|
||||
|
||||
_nls.patchFiles(f, typescript).forEach(f => this.emit('data', f));
|
||||
base = f.base;
|
||||
this.emit('data', _nls.patchFile(f, typescript, options));
|
||||
}, function () {
|
||||
for (const file of [
|
||||
new File({
|
||||
contents: Buffer.from(JSON.stringify({
|
||||
keys: _nls.moduleToNLSKeys,
|
||||
messages: _nls.moduleToNLSMessages,
|
||||
}, null, '\t')),
|
||||
base,
|
||||
path: `${base}/nls.metadata.json`
|
||||
}),
|
||||
new File({
|
||||
contents: Buffer.from(JSON.stringify(_nls.allNLSMessages)),
|
||||
base,
|
||||
path: `${base}/nls.messages.json`
|
||||
}),
|
||||
new File({
|
||||
contents: Buffer.from(JSON.stringify(_nls.allNLSModulesAndKeys)),
|
||||
base,
|
||||
path: `${base}/nls.keys.json`
|
||||
}),
|
||||
new File({
|
||||
contents: Buffer.from(`/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(_nls.allNLSMessages)};`),
|
||||
base,
|
||||
path: `${base}/nls.messages.js`
|
||||
})
|
||||
]) {
|
||||
this.emit('data', file);
|
||||
}
|
||||
|
||||
this.emit('end');
|
||||
}));
|
||||
|
||||
return duplex(input, output);
|
||||
|
@ -99,11 +120,19 @@ function isImportNode(ts: typeof import('typescript'), node: ts.Node): boolean {
|
|||
|
||||
module _nls {
|
||||
|
||||
interface INlsStringResult {
|
||||
export const moduleToNLSKeys: { [name: string /* module ID */]: ILocalizeKey[] /* keys */ } = {};
|
||||
export const moduleToNLSMessages: { [name: string /* module ID */]: string[] /* messages */ } = {};
|
||||
export const allNLSMessages: string[] = [];
|
||||
export const allNLSModulesAndKeys: Array<[string /* module ID */, string[] /* keys */]> = [];
|
||||
let allNLSMessagesIndex = 0;
|
||||
|
||||
type ILocalizeKey = string | { key: string }; // key might contain metadata for translators and then is not just a string
|
||||
|
||||
interface INlsPatchResult {
|
||||
javascript: string;
|
||||
sourcemap: sm.RawSourceMap;
|
||||
nls?: string;
|
||||
nlsKeys?: string;
|
||||
nlsMessages?: string[];
|
||||
nlsKeys?: ILocalizeKey[];
|
||||
}
|
||||
|
||||
interface ISpan {
|
||||
|
@ -120,7 +149,6 @@ module _nls {
|
|||
|
||||
interface ILocalizeAnalysisResult {
|
||||
localizeCalls: ILocalizeCall[];
|
||||
nlsExpressions: ISpan[];
|
||||
}
|
||||
|
||||
interface IPatch {
|
||||
|
@ -210,14 +238,6 @@ module _nls {
|
|||
.filter(d => d.moduleSpecifier.getText() === '\'vs/nls\'')
|
||||
.filter(d => !!d.importClause && !!d.importClause.namedBindings);
|
||||
|
||||
const nlsExpressions = importEqualsDeclarations
|
||||
.map(d => (<ts.ExternalModuleReference>d.moduleReference).expression)
|
||||
.concat(importDeclarations.map(d => d.moduleSpecifier))
|
||||
.map<ISpan>(d => ({
|
||||
start: ts.getLineAndCharacterOfPosition(sourceFile, d.getStart()),
|
||||
end: ts.getLineAndCharacterOfPosition(sourceFile, d.getEnd())
|
||||
}));
|
||||
|
||||
// `nls.localize(...)` calls
|
||||
const nlsLocalizeCallExpressions = importDeclarations
|
||||
.filter(d => !!(d.importClause && d.importClause.namedBindings && d.importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport))
|
||||
|
@ -280,8 +300,7 @@ module _nls {
|
|||
}));
|
||||
|
||||
return {
|
||||
localizeCalls: localizeCalls.toArray(),
|
||||
nlsExpressions: nlsExpressions.toArray()
|
||||
localizeCalls: localizeCalls.toArray()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -351,17 +370,12 @@ module _nls {
|
|||
}
|
||||
}
|
||||
|
||||
function patchJavascript(patches: IPatch[], contents: string, moduleId: string): string {
|
||||
function patchJavascript(patches: IPatch[], contents: string): string {
|
||||
const model = new TextModel(contents);
|
||||
|
||||
// patch the localize calls
|
||||
lazy(patches).reverse().each(p => model.apply(p));
|
||||
|
||||
// patch the 'vs/nls' imports
|
||||
const firstLine = model.get(0);
|
||||
const patchedFirstLine = firstLine.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`);
|
||||
model.set(0, patchedFirstLine);
|
||||
|
||||
return model.toString();
|
||||
}
|
||||
|
||||
|
@ -410,16 +424,24 @@ module _nls {
|
|||
return JSON.parse(smg.toString());
|
||||
}
|
||||
|
||||
function patch(ts: typeof import('typescript'), moduleId: string, typescript: string, javascript: string, sourcemap: sm.RawSourceMap): INlsStringResult {
|
||||
const { localizeCalls, nlsExpressions } = analyze(ts, typescript, 'localize');
|
||||
const { localizeCalls: localize2Calls, nlsExpressions: nls2Expressions } = analyze(ts, typescript, 'localize2');
|
||||
function parseLocalizeKeyOrValue(sourceExpression: string) {
|
||||
// sourceValue can be "foo", 'foo', `foo` or { .... }
|
||||
// in its evalulated form
|
||||
// we want to return either the string or the object
|
||||
// eslint-disable-next-line no-eval
|
||||
return eval(`(${sourceExpression})`);
|
||||
}
|
||||
|
||||
function patch(ts: typeof import('typescript'), typescript: string, javascript: string, sourcemap: sm.RawSourceMap, options: { preserveEnglish: boolean }): INlsPatchResult {
|
||||
const { localizeCalls } = analyze(ts, typescript, 'localize');
|
||||
const { localizeCalls: localize2Calls } = analyze(ts, typescript, 'localize2');
|
||||
|
||||
if (localizeCalls.length === 0 && localize2Calls.length === 0) {
|
||||
return { javascript, sourcemap };
|
||||
}
|
||||
|
||||
const nlsKeys = template(localizeCalls.map(lc => lc.key).concat(localize2Calls.map(lc => lc.key)));
|
||||
const nls = template(localizeCalls.map(lc => lc.value).concat(localize2Calls.map(lc => lc.value)));
|
||||
const nlsKeys = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.key)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.key)));
|
||||
const nlsMessages = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.value)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.value)));
|
||||
const smc = new sm.SourceMapConsumer(sourcemap);
|
||||
const positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]);
|
||||
|
||||
|
@ -430,18 +452,20 @@ module _nls {
|
|||
return { span: { start, end }, content: c.content };
|
||||
};
|
||||
|
||||
let i = 0;
|
||||
const localizePatches = lazy(localizeCalls)
|
||||
.map(lc => ([
|
||||
{ range: lc.keySpan, content: '' + (i++) },
|
||||
{ range: lc.valueSpan, content: 'null' }
|
||||
]))
|
||||
.map(lc => (
|
||||
options.preserveEnglish ? [
|
||||
{ range: lc.keySpan, content: `${allNLSMessagesIndex++}` } // localize('key', "message") => localize(<index>, "message")
|
||||
] : [
|
||||
{ range: lc.keySpan, content: `${allNLSMessagesIndex++}` }, // localize('key', "message") => localize(<index>, null)
|
||||
{ range: lc.valueSpan, content: 'null' }
|
||||
]))
|
||||
.flatten()
|
||||
.map(toPatch);
|
||||
|
||||
const localize2Patches = lazy(localize2Calls)
|
||||
.map(lc => (
|
||||
{ range: lc.keySpan, content: '' + (i++) }
|
||||
{ range: lc.keySpan, content: `${allNLSMessagesIndex++}` } // localize2('key', "message") => localize(<index>, "message")
|
||||
))
|
||||
.map(toPatch);
|
||||
|
||||
|
@ -460,45 +484,39 @@ module _nls {
|
|||
}
|
||||
});
|
||||
|
||||
javascript = patchJavascript(patches, javascript, moduleId);
|
||||
|
||||
// since imports are not within the sourcemap information,
|
||||
// we must do this MacGyver style
|
||||
if (nlsExpressions.length || nls2Expressions.length) {
|
||||
javascript = javascript.replace(/^define\(.*$/m, line => {
|
||||
return line.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`);
|
||||
});
|
||||
}
|
||||
javascript = patchJavascript(patches, javascript);
|
||||
|
||||
sourcemap = patchSourcemap(patches, sourcemap, smc);
|
||||
|
||||
return { javascript, sourcemap, nlsKeys, nls };
|
||||
return { javascript, sourcemap, nlsKeys, nlsMessages };
|
||||
}
|
||||
|
||||
export function patchFiles(javascriptFile: File, typescript: string): File[] {
|
||||
export function patchFile(javascriptFile: File, typescript: string, options: { preserveEnglish: boolean }): File {
|
||||
const ts = require('typescript') as typeof import('typescript');
|
||||
// hack?
|
||||
const moduleId = javascriptFile.relative
|
||||
.replace(/\.js$/, '')
|
||||
.replace(/\\/g, '/');
|
||||
|
||||
const { javascript, sourcemap, nlsKeys, nls } = patch(
|
||||
const { javascript, sourcemap, nlsKeys, nlsMessages } = patch(
|
||||
ts,
|
||||
moduleId,
|
||||
typescript,
|
||||
javascriptFile.contents.toString(),
|
||||
(<any>javascriptFile).sourceMap
|
||||
(<any>javascriptFile).sourceMap,
|
||||
options
|
||||
);
|
||||
|
||||
const result: File[] = [fileFrom(javascriptFile, javascript)];
|
||||
(<any>result[0]).sourceMap = sourcemap;
|
||||
const result = fileFrom(javascriptFile, javascript);
|
||||
(<any>result).sourceMap = sourcemap;
|
||||
|
||||
if (nlsKeys) {
|
||||
result.push(fileFrom(javascriptFile, nlsKeys, javascriptFile.path.replace(/\.js$/, '.nls.keys.js')));
|
||||
moduleToNLSKeys[moduleId] = nlsKeys;
|
||||
allNLSModulesAndKeys.push([moduleId, nlsKeys.map(nlsKey => typeof nlsKey === 'string' ? nlsKey : nlsKey.key)]);
|
||||
}
|
||||
|
||||
if (nls) {
|
||||
result.push(fileFrom(javascriptFile, nls, javascriptFile.path.replace(/\.js$/, '.nls.js')));
|
||||
if (nlsMessages) {
|
||||
moduleToNLSMessages[moduleId] = nlsMessages;
|
||||
allNLSMessages.push(...nlsMessages);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -192,6 +192,7 @@ function optimizeAMDTask(opts) {
|
|||
includeContent: true
|
||||
}))
|
||||
.pipe(opts.languages && opts.languages.length ? (0, i18n_1.processNlsFiles)({
|
||||
out: opts.src,
|
||||
fileHeader: bundledFileHeader,
|
||||
languages: opts.languages
|
||||
}) : es.through());
|
||||
|
|
|
@ -269,6 +269,7 @@ function optimizeAMDTask(opts: IOptimizeAMDTaskOpts): NodeJS.ReadWriteStream {
|
|||
includeContent: true
|
||||
}))
|
||||
.pipe(opts.languages && opts.languages.length ? processNlsFiles({
|
||||
out: opts.src,
|
||||
fileHeader: bundledFileHeader,
|
||||
languages: opts.languages
|
||||
}) : es.through());
|
||||
|
|
102
src/bootstrap-amd.js
vendored
102
src/bootstrap-amd.js
vendored
|
@ -6,6 +6,10 @@
|
|||
//@ts-check
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @typedef {import('./vs/nls').INLSConfiguration} INLSConfiguration
|
||||
*/
|
||||
|
||||
// Store the node.js require function in a variable
|
||||
// before loading our AMD loader to avoid issues
|
||||
// when this file is bundled with other files.
|
||||
|
@ -31,16 +35,13 @@ globalThis._VSCODE_PACKAGE_JSON = require('../package.json');
|
|||
const loader = require('./vs/loader');
|
||||
const bootstrap = require('./bootstrap');
|
||||
const performance = require('./vs/base/common/performance');
|
||||
|
||||
// Bootstrap: NLS
|
||||
const nlsConfig = bootstrap.setupNLS();
|
||||
const fs = require('fs');
|
||||
|
||||
// Bootstrap: Loader
|
||||
loader.config({
|
||||
baseUrl: bootstrap.fileUriFromPath(__dirname, { isWindows: process.platform === 'win32' }),
|
||||
catchError: true,
|
||||
nodeRequire,
|
||||
'vs/nls': nlsConfig,
|
||||
amdModulesPattern: /^vs\//,
|
||||
recordStats: true
|
||||
});
|
||||
|
@ -52,13 +53,90 @@ if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']) {
|
|||
});
|
||||
}
|
||||
|
||||
// Pseudo NLS support
|
||||
if (nlsConfig && nlsConfig.pseudo) {
|
||||
loader(['vs/nls'], function (/** @type {import('vs/nls')} */nlsPlugin) {
|
||||
nlsPlugin.setPseudoTranslation(!!nlsConfig.pseudo);
|
||||
});
|
||||
//#region NLS helpers
|
||||
|
||||
/** @type {Promise<INLSConfiguration | undefined> | undefined} */
|
||||
let setupNLSResult = undefined;
|
||||
|
||||
/**
|
||||
* @returns {Promise<INLSConfiguration | undefined>}
|
||||
*/
|
||||
function setupNLS() {
|
||||
if (!setupNLSResult) {
|
||||
setupNLSResult = doSetupNLS();
|
||||
}
|
||||
|
||||
return setupNLSResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<INLSConfiguration | undefined>}
|
||||
*/
|
||||
async function doSetupNLS() {
|
||||
performance.mark('code/fork/willLoadNls');
|
||||
|
||||
/** @type {INLSConfiguration | undefined} */
|
||||
let nlsConfig = undefined;
|
||||
|
||||
/** @type {string | undefined} */
|
||||
let messagesFile;
|
||||
if (process.env['VSCODE_NLS_CONFIG']) {
|
||||
try {
|
||||
/** @type {INLSConfiguration} */
|
||||
nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']);
|
||||
if (nlsConfig?.languagePack?.messagesFile) {
|
||||
messagesFile = nlsConfig.languagePack.messagesFile;
|
||||
} else if (nlsConfig?.defaultMessagesFile) {
|
||||
messagesFile = nlsConfig.defaultMessagesFile;
|
||||
}
|
||||
|
||||
// VSCODE_GLOBALS: NLS
|
||||
globalThis._VSCODE_NLS_LANGUAGE = nlsConfig?.resolvedLanguage;
|
||||
} catch (e) {
|
||||
console.error(`Error reading VSCODE_NLS_CONFIG from environment: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
process.env['VSCODE_DEV'] || // no NLS support in dev mode
|
||||
!messagesFile // no NLS messages file
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
// VSCODE_GLOBALS: NLS
|
||||
globalThis._VSCODE_NLS_MESSAGES = JSON.parse((await fs.promises.readFile(messagesFile)).toString());
|
||||
} catch (error) {
|
||||
console.error(`Error reading NLS messages file ${messagesFile}: ${error}`);
|
||||
|
||||
// Mark as corrupt: this will re-create the language pack cache next startup
|
||||
if (nlsConfig?.languagePack?.corruptMarkerFile) {
|
||||
try {
|
||||
await fs.promises.writeFile(nlsConfig.languagePack.corruptMarkerFile, 'corrupted');
|
||||
} catch (error) {
|
||||
console.error(`Error writing corrupted NLS marker file: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to the default message file to ensure english translation at least
|
||||
if (nlsConfig?.defaultMessagesFile && nlsConfig.defaultMessagesFile !== messagesFile) {
|
||||
try {
|
||||
// VSCODE_GLOBALS: NLS
|
||||
globalThis._VSCODE_NLS_MESSAGES = JSON.parse((await fs.promises.readFile(nlsConfig.defaultMessagesFile)).toString());
|
||||
} catch (error) {
|
||||
console.error(`Error reading default NLS messages file ${nlsConfig.defaultMessagesFile}: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
performance.mark('code/fork/didLoadNls');
|
||||
|
||||
return nlsConfig;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
/**
|
||||
* @param {string=} entrypoint
|
||||
* @param {(value: any) => void=} onLoad
|
||||
|
@ -82,6 +160,8 @@ exports.load = function (entrypoint, onLoad, onError) {
|
|||
onLoad = onLoad || function () { };
|
||||
onError = onError || function (err) { console.error(err); };
|
||||
|
||||
performance.mark('code/fork/willLoadCode');
|
||||
loader([entrypoint], onLoad, onError);
|
||||
setupNLS().then(() => {
|
||||
performance.mark('code/fork/willLoadCode');
|
||||
loader([entrypoint], onLoad, onError);
|
||||
});
|
||||
};
|
||||
|
|
32
src/bootstrap-window.js
vendored
32
src/bootstrap-window.js
vendored
|
@ -82,28 +82,23 @@
|
|||
developerDeveloperKeybindingsDisposable = registerDeveloperKeybindings(disallowReloadKeybinding);
|
||||
}
|
||||
|
||||
// Get the nls configuration into the process.env as early as possible
|
||||
// @ts-ignore
|
||||
const nlsConfig = globalThis.MonacoBootstrap.setupNLS();
|
||||
|
||||
let locale = nlsConfig.availableLanguages['*'] || 'en';
|
||||
if (locale === 'zh-tw') {
|
||||
locale = 'zh-Hant';
|
||||
} else if (locale === 'zh-cn') {
|
||||
locale = 'zh-Hans';
|
||||
// VSCODE_GLOBALS: NLS
|
||||
globalThis._VSCODE_NLS_MESSAGES = configuration.nls.messages;
|
||||
globalThis._VSCODE_NLS_LANGUAGE = configuration.nls.language;
|
||||
let language = configuration.nls.language || 'en';
|
||||
if (language === 'zh-tw') {
|
||||
language = 'zh-Hant';
|
||||
} else if (language === 'zh-cn') {
|
||||
language = 'zh-Hans';
|
||||
}
|
||||
|
||||
window.document.documentElement.setAttribute('lang', locale);
|
||||
window.document.documentElement.setAttribute('lang', language);
|
||||
|
||||
window['MonacoEnvironment'] = {};
|
||||
|
||||
/**
|
||||
* @typedef {any} LoaderConfig
|
||||
*/
|
||||
/** @type {LoaderConfig} */
|
||||
/** @type {any} */
|
||||
const loaderConfig = {
|
||||
baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out`,
|
||||
'vs/nls': nlsConfig,
|
||||
preferScriptTags: true
|
||||
};
|
||||
|
||||
|
@ -147,13 +142,6 @@
|
|||
// Configure loader
|
||||
require.config(loaderConfig);
|
||||
|
||||
// Handle pseudo NLS
|
||||
if (nlsConfig.pseudo) {
|
||||
require(['vs/nls'], function (nlsPlugin) {
|
||||
nlsPlugin.setPseudoTranslation(nlsConfig.pseudo);
|
||||
});
|
||||
}
|
||||
|
||||
// Signal before require()
|
||||
if (typeof options?.beforeRequire === 'function') {
|
||||
options.beforeRequire(configuration);
|
||||
|
|
135
src/bootstrap.js
vendored
135
src/bootstrap.js
vendored
|
@ -22,8 +22,6 @@
|
|||
}(this, function () {
|
||||
const Module = typeof require === 'function' ? require('module') : undefined;
|
||||
const path = typeof require === 'function' ? require('path') : undefined;
|
||||
const fs = typeof require === 'function' ? require('fs') : undefined;
|
||||
const util = typeof require === 'function' ? require('util') : undefined;
|
||||
|
||||
//#region global bootstrapping
|
||||
|
||||
|
@ -118,141 +116,8 @@
|
|||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region NLS helpers
|
||||
|
||||
/**
|
||||
* @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean } | undefined}
|
||||
*/
|
||||
function setupNLS() {
|
||||
|
||||
// Get the nls configuration as early as possible.
|
||||
const process = safeProcess();
|
||||
/** @type {{ availableLanguages: {}; loadBundle?: (bundle: string, language: string, cb: (err: Error | undefined, result: string | undefined) => void) => void; _resolvedLanguagePackCoreLocation?: string; _corruptedFile?: string }} */
|
||||
let nlsConfig = { availableLanguages: {} };
|
||||
if (process && process.env['VSCODE_NLS_CONFIG']) {
|
||||
try {
|
||||
nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (nlsConfig._resolvedLanguagePackCoreLocation) {
|
||||
const bundles = Object.create(null);
|
||||
|
||||
/**
|
||||
* @param {string} bundle
|
||||
* @param {string} language
|
||||
* @param {(err: Error | undefined, result: string | undefined) => void} cb
|
||||
*/
|
||||
nlsConfig.loadBundle = function (bundle, language, cb) {
|
||||
const result = bundles[bundle];
|
||||
if (result) {
|
||||
cb(undefined, result);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
safeReadNlsFile(nlsConfig._resolvedLanguagePackCoreLocation, `${bundle.replace(/\//g, '!')}.nls.json`).then(function (content) {
|
||||
const json = JSON.parse(content);
|
||||
bundles[bundle] = json;
|
||||
|
||||
cb(undefined, json);
|
||||
}).catch((error) => {
|
||||
try {
|
||||
if (nlsConfig._corruptedFile) {
|
||||
safeWriteNlsFile(nlsConfig._corruptedFile, 'corrupted').catch(function (error) { console.error(error); });
|
||||
}
|
||||
} finally {
|
||||
cb(error, undefined);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return nlsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {typeof import('./vs/base/parts/sandbox/electron-sandbox/globals') | undefined}
|
||||
*/
|
||||
function safeSandboxGlobals() {
|
||||
const globals = (typeof self === 'object' ? self : typeof global === 'object' ? global : {});
|
||||
|
||||
// @ts-ignore
|
||||
return globals.vscode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {import('./vs/base/parts/sandbox/electron-sandbox/globals').ISandboxNodeProcess | NodeJS.Process | undefined}
|
||||
*/
|
||||
function safeProcess() {
|
||||
const sandboxGlobals = safeSandboxGlobals();
|
||||
if (sandboxGlobals) {
|
||||
return sandboxGlobals.process; // Native environment (sandboxed)
|
||||
}
|
||||
|
||||
if (typeof process !== 'undefined') {
|
||||
return process; // Native environment (non-sandboxed)
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {import('./vs/base/parts/sandbox/electron-sandbox/electronTypes').IpcRenderer | undefined}
|
||||
*/
|
||||
function safeIpcRenderer() {
|
||||
const sandboxGlobals = safeSandboxGlobals();
|
||||
if (sandboxGlobals) {
|
||||
return sandboxGlobals.ipcRenderer;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} pathSegments
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function safeReadNlsFile(...pathSegments) {
|
||||
const ipcRenderer = safeIpcRenderer();
|
||||
if (ipcRenderer) {
|
||||
return ipcRenderer.invoke('vscode:readNlsFile', ...pathSegments);
|
||||
}
|
||||
|
||||
if (fs && path && util) {
|
||||
return (await util.promisify(fs.readFile)(path.join(...pathSegments))).toString();
|
||||
}
|
||||
|
||||
throw new Error('Unsupported operation (read NLS files)');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {string} content
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function safeWriteNlsFile(path, content) {
|
||||
const ipcRenderer = safeIpcRenderer();
|
||||
if (ipcRenderer) {
|
||||
return ipcRenderer.invoke('vscode:writeNlsFile', path, content);
|
||||
}
|
||||
|
||||
if (fs && util) {
|
||||
return util.promisify(fs.writeFile)(path, content);
|
||||
}
|
||||
|
||||
throw new Error('Unsupported operation (write NLS files)');
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
return {
|
||||
enableASARSupport,
|
||||
setupNLS,
|
||||
fileUriFromPath
|
||||
};
|
||||
}));
|
||||
|
|
36
src/cli.js
36
src/cli.js
|
@ -6,6 +6,10 @@
|
|||
//@ts-check
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @import { IProductConfiguration } from './vs/base/common/product'
|
||||
*/
|
||||
|
||||
// Delete `VSCODE_CWD` very early even before
|
||||
// importing bootstrap files. We have seen
|
||||
// reports where `code .` would use the wrong
|
||||
|
@ -16,17 +20,29 @@ delete process.env['VSCODE_CWD'];
|
|||
|
||||
const bootstrap = require('./bootstrap');
|
||||
const bootstrapNode = require('./bootstrap-node');
|
||||
const product = require('../product.json');
|
||||
|
||||
// Enable portable support
|
||||
/** @type {Partial<IProductConfiguration>} */
|
||||
// @ts-ignore
|
||||
bootstrapNode.configurePortable(product);
|
||||
const product = require('../product.json');
|
||||
const { resolveNLSConfiguration } = require('./vs/base/node/nls');
|
||||
|
||||
// Enable ASAR support
|
||||
bootstrap.enableASARSupport();
|
||||
async function start() {
|
||||
|
||||
// Signal processes that we got launched as CLI
|
||||
process.env['VSCODE_CLI'] = '1';
|
||||
// NLS
|
||||
const nlsConfiguration = await resolveNLSConfiguration({ userLocale: 'en', osLocale: 'en', commit: product.commit, userDataPath: '', nlsMetadataPath: __dirname });
|
||||
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfiguration); // required for `bootstrap-amd` to pick up NLS messages
|
||||
|
||||
// Load CLI through AMD loader
|
||||
require('./bootstrap-amd').load('vs/code/node/cli');
|
||||
// Enable portable support
|
||||
// @ts-ignore
|
||||
bootstrapNode.configurePortable(product);
|
||||
|
||||
// Enable ASAR support
|
||||
bootstrap.enableASARSupport();
|
||||
|
||||
// Signal processes that we got launched as CLI
|
||||
process.env['VSCODE_CLI'] = '1';
|
||||
|
||||
// Load CLI through AMD loader
|
||||
require('./bootstrap-amd').load('vs/code/node/cli');
|
||||
}
|
||||
|
||||
start();
|
||||
|
|
109
src/main.js
109
src/main.js
|
@ -8,7 +8,7 @@
|
|||
|
||||
/**
|
||||
* @import { IProductConfiguration } from './vs/base/common/product'
|
||||
* @import { NLSConfiguration } from './vs/base/node/languagePacks'
|
||||
* @import { INLSConfiguration } from './vs/nls'
|
||||
* @import { NativeParsedArgs } from './vs/platform/environment/common/argv'
|
||||
*/
|
||||
|
||||
|
@ -109,27 +109,29 @@ protocol.registerSchemesAsPrivileged([
|
|||
registerListeners();
|
||||
|
||||
/**
|
||||
* Support user defined locale: load it early before app('ready')
|
||||
* to have more things running in parallel.
|
||||
* We can resolve the NLS configuration early if it is defined
|
||||
* in argv.json before `app.ready` event. Otherwise we can only
|
||||
* resolve NLS after `app.ready` event to resolve the OS locale.
|
||||
*
|
||||
* @type {Promise<NLSConfiguration> | undefined}
|
||||
* @type {Promise<INLSConfiguration> | undefined}
|
||||
*/
|
||||
let nlsConfigurationPromise = undefined;
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
**/
|
||||
// Use the most preferred OS language for language recommendation.
|
||||
// The API might return an empty array on Linux, such as when
|
||||
// the 'C' locale is the user's only configured locale.
|
||||
// No matter the OS, if the array is empty, default back to 'en'.
|
||||
const resolved = app.getPreferredSystemLanguages()?.[0] ?? 'en';
|
||||
const osLocale = processZhLocale(resolved.toLowerCase());
|
||||
const metaDataFile = path.join(__dirname, 'nls.metadata.json');
|
||||
const locale = getUserDefinedLocale(argvConfig);
|
||||
if (locale) {
|
||||
const { getNLSConfiguration } = require('./vs/base/node/languagePacks');
|
||||
nlsConfigurationPromise = getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale, osLocale);
|
||||
const osLocale = processZhLocale((app.getPreferredSystemLanguages()?.[0] ?? 'en').toLowerCase());
|
||||
const userLocale = getUserDefinedLocale(argvConfig);
|
||||
if (userLocale) {
|
||||
const { resolveNLSConfiguration } = require('./vs/base/node/nls');
|
||||
nlsConfigurationPromise = resolveNLSConfiguration({
|
||||
userLocale,
|
||||
osLocale,
|
||||
commit: product.commit,
|
||||
userDataPath,
|
||||
nlsMetadataPath: __dirname
|
||||
});
|
||||
}
|
||||
|
||||
// Pass in the locale to Electron so that the
|
||||
|
@ -141,7 +143,7 @@ if (locale) {
|
|||
// In that case, use `en` as the Electron locale.
|
||||
|
||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||
const electronLocale = (!locale || locale === 'qps-ploc') ? 'en' : locale;
|
||||
const electronLocale = (!userLocale || userLocale === 'qps-ploc') ? 'en' : userLocale;
|
||||
app.commandLine.appendSwitch('lang', electronLocale);
|
||||
}
|
||||
|
||||
|
@ -161,15 +163,28 @@ app.once('ready', function () {
|
|||
}
|
||||
});
|
||||
|
||||
async function onReady() {
|
||||
perf.mark('code/mainAppReady');
|
||||
|
||||
try {
|
||||
const [, nlsConfig] = await Promise.all([
|
||||
mkdirpIgnoreError(codeCachePath),
|
||||
resolveNlsConfiguration()
|
||||
]);
|
||||
|
||||
startup(codeCachePath, nlsConfig);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main startup routine
|
||||
*
|
||||
* @param {string | undefined} codeCachePath
|
||||
* @param {NLSConfiguration} nlsConfig
|
||||
* @param {INLSConfiguration} nlsConfig
|
||||
*/
|
||||
function startup(codeCachePath, nlsConfig) {
|
||||
nlsConfig._languagePackSupport = true;
|
||||
|
||||
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig);
|
||||
process.env['VSCODE_CODE_CACHE_PATH'] = codeCachePath || '';
|
||||
|
||||
|
@ -180,18 +195,6 @@ function startup(codeCachePath, nlsConfig) {
|
|||
});
|
||||
}
|
||||
|
||||
async function onReady() {
|
||||
perf.mark('code/mainAppReady');
|
||||
|
||||
try {
|
||||
const [, nlsConfig] = await Promise.all([mkdirpIgnoreError(codeCachePath), resolveNlsConfiguration()]);
|
||||
|
||||
startup(codeCachePath, nlsConfig);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NativeParsedArgs} cliArgs
|
||||
*/
|
||||
|
@ -643,35 +646,47 @@ function processZhLocale(appLocale) {
|
|||
/**
|
||||
* Resolve the NLS configuration
|
||||
*
|
||||
* @return {Promise<NLSConfiguration>}
|
||||
* @return {Promise<INLSConfiguration>}
|
||||
*/
|
||||
async function resolveNlsConfiguration() {
|
||||
|
||||
// First, we need to test a user defined locale. If it fails we try the app locale.
|
||||
// First, we need to test a user defined locale.
|
||||
// If it fails we try the app locale.
|
||||
// If that fails we fall back to English.
|
||||
let nlsConfiguration = nlsConfigurationPromise ? await nlsConfigurationPromise : undefined;
|
||||
|
||||
const nlsConfiguration = nlsConfigurationPromise ? await nlsConfigurationPromise : undefined;
|
||||
if (nlsConfiguration) {
|
||||
return nlsConfiguration;
|
||||
}
|
||||
|
||||
// Try to use the app locale. Please note that the app locale is only
|
||||
// valid after we have received the app ready event. This is why the
|
||||
// code is here.
|
||||
// Try to use the app locale which is only valid
|
||||
// after the app ready event has been fired.
|
||||
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
let appLocale = app.getLocale();
|
||||
if (!appLocale) {
|
||||
return { locale: 'en', osLocale, availableLanguages: {} };
|
||||
let userLocale = app.getLocale();
|
||||
if (!userLocale) {
|
||||
return {
|
||||
userLocale: 'en',
|
||||
osLocale,
|
||||
resolvedLanguage: 'en',
|
||||
defaultMessagesFile: path.join(__dirname, 'nls.messages.json'),
|
||||
|
||||
// NLS: below 2 are a relic from old times only used by vscode-nls and deprecated
|
||||
locale: 'en',
|
||||
availableLanguages: {}
|
||||
};
|
||||
}
|
||||
|
||||
// See above the comment about the loader and case sensitiveness
|
||||
appLocale = processZhLocale(appLocale.toLowerCase());
|
||||
userLocale = processZhLocale(userLocale.toLowerCase());
|
||||
|
||||
const { getNLSConfiguration } = require('./vs/base/node/languagePacks');
|
||||
nlsConfiguration = await getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale, osLocale);
|
||||
return nlsConfiguration ?? { locale: 'en', osLocale, availableLanguages: {} };
|
||||
const { resolveNLSConfiguration } = require('./vs/base/node/nls');
|
||||
return resolveNLSConfiguration({
|
||||
userLocale,
|
||||
osLocale,
|
||||
commit: product.commit,
|
||||
userDataPath,
|
||||
nlsMetadataPath: __dirname
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -689,7 +704,7 @@ function getUserDefinedLocale(argvConfig) {
|
|||
return locale.toLowerCase(); // a directly provided --locale always wins
|
||||
}
|
||||
|
||||
return argvConfig.locale && typeof argvConfig.locale === 'string' ? argvConfig.locale.toLowerCase() : undefined;
|
||||
return typeof argvConfig?.locale === 'string' ? argvConfig.locale.toLowerCase() : undefined;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -4,18 +4,36 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// @ts-check
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @import { IProductConfiguration } from './vs/base/common/product'
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
/** @type {Partial<IProductConfiguration>} */
|
||||
// @ts-ignore
|
||||
const product = require('../product.json');
|
||||
const { resolveNLSConfiguration } = require('./vs/base/node/nls');
|
||||
|
||||
// Keep bootstrap-amd.js from redefining 'fs'.
|
||||
delete process.env['ELECTRON_RUN_AS_NODE'];
|
||||
async function start() {
|
||||
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
// When running out of sources, we need to load node modules from remote/node_modules,
|
||||
// which are compiled against nodejs, not electron
|
||||
process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] = process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] || path.join(__dirname, '..', 'remote', 'node_modules');
|
||||
require('./bootstrap-node').injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']);
|
||||
} else {
|
||||
delete process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'];
|
||||
// Keep bootstrap-amd.js from redefining 'fs'.
|
||||
delete process.env['ELECTRON_RUN_AS_NODE'];
|
||||
|
||||
// NLS
|
||||
const nlsConfiguration = await resolveNLSConfiguration({ userLocale: 'en', osLocale: 'en', commit: product.commit, userDataPath: '', nlsMetadataPath: __dirname });
|
||||
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfiguration); // required for `bootstrap-amd` to pick up NLS messages
|
||||
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
// When running out of sources, we need to load node modules from remote/node_modules,
|
||||
// which are compiled against nodejs, not electron
|
||||
process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] = process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] || path.join(__dirname, '..', 'remote', 'node_modules');
|
||||
require('./bootstrap-node').injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']);
|
||||
} else {
|
||||
delete process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'];
|
||||
}
|
||||
require('./bootstrap-amd').load('vs/server/node/server.cli');
|
||||
}
|
||||
require('./bootstrap-amd').load('vs/server/node/server.cli');
|
||||
|
||||
start();
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// @ts-check
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @import { IProductConfiguration } from './vs/base/common/product'
|
||||
* @import { INLSConfiguration } from './vs/nls'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @import { IServerAPI } from './vs/server/node/remoteExtensionHostAgentServer'
|
||||
|
@ -11,9 +17,12 @@
|
|||
|
||||
const perf = require('./vs/base/common/performance');
|
||||
const performance = require('perf_hooks').performance;
|
||||
/** @type {Partial<IProductConfiguration>} */
|
||||
// @ts-ignore
|
||||
const product = require('../product.json');
|
||||
const readline = require('readline');
|
||||
const http = require('http');
|
||||
const { resolveNLSConfiguration } = require('./vs/base/node/nls');
|
||||
|
||||
perf.mark('code/server/start');
|
||||
// @ts-ignore
|
||||
|
@ -42,8 +51,10 @@ async function start() {
|
|||
|
||||
const shouldSpawnCli = parsedArgs.help || parsedArgs.version || extensionLookupArgs.some(a => !!parsedArgs[a]) || (extensionInstallArgs.some(a => !!parsedArgs[a]) && !parsedArgs['start-server']);
|
||||
|
||||
const nlsConfiguration = await resolveNLSConfiguration({ userLocale: 'en', osLocale: 'en', commit: product.commit, userDataPath: '', nlsMetadataPath: __dirname });
|
||||
|
||||
if (shouldSpawnCli) {
|
||||
loadCode().then((mod) => {
|
||||
loadCode(nlsConfiguration).then((mod) => {
|
||||
mod.spawnCli();
|
||||
});
|
||||
return;
|
||||
|
@ -56,7 +67,7 @@ async function start() {
|
|||
/** @returns {Promise<IServerAPI>} */
|
||||
const getRemoteExtensionHostAgentServer = () => {
|
||||
if (!_remoteExtensionHostAgentServerPromise) {
|
||||
_remoteExtensionHostAgentServerPromise = loadCode().then(async (mod) => {
|
||||
_remoteExtensionHostAgentServerPromise = loadCode(nlsConfiguration).then(async (mod) => {
|
||||
const server = await mod.createServer(address);
|
||||
_remoteExtensionHostAgentServer = server;
|
||||
return server;
|
||||
|
@ -249,13 +260,19 @@ async function findFreePort(host, start, end) {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
/** @returns { Promise<typeof import('./vs/server/node/server.main')> } */
|
||||
function loadCode() {
|
||||
/**
|
||||
* @param {INLSConfiguration} nlsConfiguration
|
||||
* @returns { Promise<typeof import('./vs/server/node/server.main')> }
|
||||
*/
|
||||
function loadCode(nlsConfiguration) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const path = require('path');
|
||||
|
||||
delete process.env['ELECTRON_RUN_AS_NODE']; // Keep bootstrap-amd.js from redefining 'fs'.
|
||||
|
||||
/** @type {INLSConfiguration} */
|
||||
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfiguration); // required for `bootstrap-amd` to pick up NLS messages
|
||||
|
||||
// See https://github.com/microsoft/vscode-remote-release/issues/6543
|
||||
// We would normally install a SIGPIPE listener in bootstrap.js
|
||||
// But in certain situations, the console itself can be in a broken pipe state
|
||||
|
@ -308,5 +325,4 @@ function prompt(question) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
start();
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"typings/require.d.ts",
|
||||
"typings/thenable.d.ts",
|
||||
"typings/vscode-globals-product.d.ts",
|
||||
"typings/vscode-globals-nls.d.ts",
|
||||
"vs/loader.d.ts",
|
||||
"vs/monaco.d.ts",
|
||||
"vs/editor/*",
|
||||
|
|
36
src/typings/vscode-globals-nls.d.ts
vendored
Normal file
36
src/typings/vscode-globals-nls.d.ts
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// AMD2ESM mirgation relevant
|
||||
|
||||
/**
|
||||
* NLS Globals: these need to be defined in all contexts that make
|
||||
* use of our `nls.localize` and `nls.localize2` functions. This includes:
|
||||
* - Electron main process
|
||||
* - Electron window (renderer) process
|
||||
* - Utility Process
|
||||
* - Node.js
|
||||
* - Browser
|
||||
* - Web worker
|
||||
*
|
||||
* That is because during build time we strip out all english strings from
|
||||
* the resulting JS code and replace it with a <number> that is then looked
|
||||
* up from the `_VSCODE_NLS_MESSAGES` array.
|
||||
*/
|
||||
declare global {
|
||||
/**
|
||||
* All NLS messages produced by `localize` and `localize2` calls
|
||||
* under `src/vs` translated to the language as indicated by
|
||||
* `_VSCODE_NLS_LANGUAGE`.
|
||||
*/
|
||||
var _VSCODE_NLS_MESSAGES: string[];
|
||||
/**
|
||||
* The actual language of the NLS messages (e.g. 'en', de' or 'pt-br').
|
||||
*/
|
||||
var _VSCODE_NLS_LANGUAGE: string | undefined;
|
||||
}
|
||||
|
||||
// fake export to make global work
|
||||
export { }
|
|
@ -50,27 +50,35 @@ export function getWorkerBootstrapUrl(scriptPath: string, label: string): string
|
|||
if (/^((http:)|(https:)|(file:))/.test(scriptPath) && scriptPath.substring(0, globalThis.origin.length) !== globalThis.origin) {
|
||||
// this is the cross-origin case
|
||||
// i.e. the webpage is running at a different origin than where the scripts are loaded from
|
||||
const myPath = 'vs/base/worker/defaultWorkerFactory.js';
|
||||
const workerBaseUrl = require.toUrl(myPath).slice(0, -myPath.length); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321
|
||||
const js = `/*${label}*/globalThis.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};const ttPolicy = globalThis.trustedTypes?.createPolicy('defaultWorkerFactory', { createScriptURL: value => value });importScripts(ttPolicy?.createScriptURL('${scriptPath}') ?? '${scriptPath}');/*${label}*/`;
|
||||
const blob = new Blob([js], { type: 'application/javascript' });
|
||||
return URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
const start = scriptPath.lastIndexOf('?');
|
||||
const end = scriptPath.lastIndexOf('#', start);
|
||||
const params = start > 0
|
||||
? new URLSearchParams(scriptPath.substring(start + 1, ~end ? end : undefined))
|
||||
: new URLSearchParams();
|
||||
|
||||
COI.addSearchParam(params, true, true);
|
||||
const search = params.toString();
|
||||
|
||||
if (!search) {
|
||||
return `${scriptPath}#${label}`;
|
||||
} else {
|
||||
return `${scriptPath}?${params.toString()}#${label}`;
|
||||
const start = scriptPath.lastIndexOf('?');
|
||||
const end = scriptPath.lastIndexOf('#', start);
|
||||
const params = start > 0
|
||||
? new URLSearchParams(scriptPath.substring(start + 1, ~end ? end : undefined))
|
||||
: new URLSearchParams();
|
||||
|
||||
COI.addSearchParam(params, true, true);
|
||||
const search = params.toString();
|
||||
if (!search) {
|
||||
scriptPath = `${scriptPath}#${label}`;
|
||||
} else {
|
||||
scriptPath = `${scriptPath}?${params.toString()}#${label}`;
|
||||
}
|
||||
}
|
||||
|
||||
const factoryModuleId = 'vs/base/worker/defaultWorkerFactory.js';
|
||||
const workerBaseUrl = require.toUrl(factoryModuleId).slice(0, -factoryModuleId.length); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321
|
||||
const blob = new Blob([[
|
||||
`/*${label}*/`,
|
||||
`globalThis.MonacoEnvironment = { baseUrl: '${workerBaseUrl}' };`,
|
||||
// VSCODE_GLOBALS: NLS
|
||||
`globalThis._VSCODE_NLS_MESSAGES = ${JSON.stringify(globalThis._VSCODE_NLS_MESSAGES)};`,
|
||||
`globalThis._VSCODE_NLS_LANGUAGE = ${JSON.stringify(globalThis._VSCODE_NLS_LANGUAGE)};`,
|
||||
`const ttPolicy = globalThis.trustedTypes?.createPolicy('defaultWorkerFactory', { createScriptURL: value => value });`,
|
||||
`importScripts(ttPolicy?.createScriptURL('${scriptPath}') ?? '${scriptPath}');`,
|
||||
`/*${label}*/`
|
||||
].join('')], { type: 'application/javascript' });
|
||||
return URL.createObjectURL(blob);
|
||||
}
|
||||
// ESM-comment-end
|
||||
|
||||
|
@ -136,8 +144,6 @@ class WebWorker extends Disposable implements IWorker {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class DefaultWorkerFactory implements IWorkerFactory {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* 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';
|
||||
|
@ -22,13 +23,6 @@ let _platformLocale: string = LANGUAGE_DEFAULT;
|
|||
let _translationsConfigFile: string | undefined = undefined;
|
||||
let _userAgent: string | undefined = undefined;
|
||||
|
||||
interface NLSConfig {
|
||||
locale: string;
|
||||
osLocale: string;
|
||||
availableLanguages: { [key: string]: string };
|
||||
_translationsConfigFile: string;
|
||||
}
|
||||
|
||||
export interface IProcessEnvironment {
|
||||
[key: string]: string | undefined;
|
||||
}
|
||||
|
@ -89,13 +83,11 @@ if (typeof nodeProcess === 'object') {
|
|||
const rawNlsConfig = nodeProcess.env['VSCODE_NLS_CONFIG'];
|
||||
if (rawNlsConfig) {
|
||||
try {
|
||||
const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
|
||||
const resolved = nlsConfig.availableLanguages['*'];
|
||||
_locale = nlsConfig.locale;
|
||||
const nlsConfig: nls.INLSConfiguration = JSON.parse(rawNlsConfig);
|
||||
_locale = nlsConfig.userLocale;
|
||||
_platformLocale = nlsConfig.osLocale;
|
||||
// VSCode's default language is 'en'
|
||||
_language = resolved ? resolved : LANGUAGE_DEFAULT;
|
||||
_translationsConfigFile = nlsConfig._translationsConfigFile;
|
||||
_language = nlsConfig.resolvedLanguage || LANGUAGE_DEFAULT;
|
||||
_translationsConfigFile = nlsConfig.languagePack?.translationsConfigFile;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
@ -111,18 +103,10 @@ else if (typeof navigator === 'object' && !isElectronRenderer) {
|
|||
_isLinux = _userAgent.indexOf('Linux') >= 0;
|
||||
_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;
|
||||
_language = _locale;
|
||||
_platformLocale = navigator.language;
|
||||
// VSCODE_GLOBALS: NLS
|
||||
_language = globalThis._VSCODE_NLS_LANGUAGE || LANGUAGE_DEFAULT;
|
||||
_locale = navigator.language.toLowerCase();
|
||||
_platformLocale = _locale;
|
||||
}
|
||||
|
||||
// Unknown environment
|
||||
|
@ -178,7 +162,7 @@ export const userAgent = _userAgent;
|
|||
/**
|
||||
* The language used for the user interface. The format of
|
||||
* the string is all lower case (e.g. zh-tw for Traditional
|
||||
* Chinese)
|
||||
* Chinese or de for German)
|
||||
*/
|
||||
export const language = _language;
|
||||
|
||||
|
@ -204,15 +188,16 @@ export namespace Language {
|
|||
}
|
||||
|
||||
/**
|
||||
* The OS locale or the locale specified by --locale. The format of
|
||||
* the string is all lower case (e.g. zh-tw for Traditional
|
||||
* Chinese). The UI is not necessarily shown in the provided locale.
|
||||
* Desktop: The OS locale or the locale specified by --locale or `argv.json`.
|
||||
* Web: matches `platformLocale`.
|
||||
*
|
||||
* The UI is not necessarily shown in the provided locale.
|
||||
*/
|
||||
export const locale = _locale;
|
||||
|
||||
/**
|
||||
* This will always be set to the OS/browser's locale regardless of
|
||||
* what was specified by --locale. The format of the string is all
|
||||
* what was specified otherwise. The format of the string is all
|
||||
* lower case (e.g. zh-tw for Traditional Chinese). The UI is not
|
||||
* necessarily shown in the provided locale.
|
||||
*/
|
||||
|
|
25
src/vs/base/node/languagePacks.d.ts
vendored
25
src/vs/base/node/languagePacks.d.ts
vendored
|
@ -1,25 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export interface NLSConfiguration {
|
||||
locale: string;
|
||||
osLocale: string;
|
||||
availableLanguages: {
|
||||
[key: string]: string;
|
||||
};
|
||||
pseudo?: boolean;
|
||||
_languagePackSupport?: boolean;
|
||||
}
|
||||
|
||||
export interface InternalNLSConfiguration extends NLSConfiguration {
|
||||
_languagePackId: string;
|
||||
_translationsConfigFile: string;
|
||||
_cacheRoot: string;
|
||||
_resolvedLanguagePackCoreLocation: string;
|
||||
_corruptedFile: string;
|
||||
_languagePackSupport?: boolean;
|
||||
}
|
||||
|
||||
export function getNLSConfiguration(commit: string | undefined, userDataPath: string, metaDataFile: string, locale: string, osLocale: string): Promise<NLSConfiguration>;
|
|
@ -1,264 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path="../../../typings/require.d.ts" />
|
||||
|
||||
//@ts-check
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @param {typeof import('path')} path
|
||||
* @param {typeof import('fs')} fs
|
||||
* @param {typeof import('../common/performance')} perf
|
||||
*/
|
||||
function factory(path, fs, perf) {
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
function exists(file) {
|
||||
return new Promise(c => fs.exists(file, c));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function touch(file) {
|
||||
return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function mkdirp(dir) {
|
||||
return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} location
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function rimraf(location) {
|
||||
return new Promise((c, e) => fs.rm(location, { recursive: true, force: true, maxRetries: 3 }, err => err ? e(err) : c()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function readFile(file) {
|
||||
return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file
|
||||
* @param {string} content
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
function writeFile(file, content) {
|
||||
return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} userDataPath
|
||||
* @returns {Promise<object | undefined>}
|
||||
*/
|
||||
async function getLanguagePackConfigurations(userDataPath) {
|
||||
const configFile = path.join(userDataPath, 'languagepacks.json');
|
||||
try {
|
||||
return JSON.parse(await readFile(configFile));
|
||||
} catch (err) {
|
||||
// Do nothing. If we can't read the file we have no
|
||||
// language pack config.
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} config
|
||||
* @param {string | undefined} locale
|
||||
*/
|
||||
function resolveLanguagePackLocale(config, locale) {
|
||||
try {
|
||||
while (locale) {
|
||||
if (config[locale]) {
|
||||
return locale;
|
||||
} else {
|
||||
const index = locale.lastIndexOf('-');
|
||||
if (index > 0) {
|
||||
locale = locale.substring(0, index);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Resolving language pack configuration failed.', err);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | undefined} commit
|
||||
* @param {string} userDataPath
|
||||
* @param {string} metaDataFile
|
||||
* @param {string} locale
|
||||
* @param {string} osLocale
|
||||
* @returns {Promise<import('./languagePacks').NLSConfiguration>}
|
||||
*/
|
||||
function getNLSConfiguration(commit, userDataPath, metaDataFile, locale, osLocale) {
|
||||
const defaultResult = function (locale) {
|
||||
perf.mark('code/didGenerateNls');
|
||||
return Promise.resolve({ locale, osLocale, availableLanguages: {} });
|
||||
};
|
||||
|
||||
perf.mark('code/willGenerateNls');
|
||||
|
||||
if (locale === 'pseudo') {
|
||||
return Promise.resolve({ locale, osLocale, availableLanguages: {}, pseudo: true });
|
||||
}
|
||||
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
return Promise.resolve({ locale, osLocale, availableLanguages: {} });
|
||||
}
|
||||
|
||||
// We have a built version so we have extracted nls file. Try to find
|
||||
// the right file to use.
|
||||
|
||||
// Check if we have an English or English US locale. If so fall to default since that is our
|
||||
// English translation (we don't ship *.nls.en.json files)
|
||||
if (locale && (locale === 'en' || locale === 'en-us')) {
|
||||
return Promise.resolve({ locale, osLocale, availableLanguages: {} });
|
||||
}
|
||||
|
||||
const initialLocale = locale;
|
||||
|
||||
try {
|
||||
if (!commit) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
return getLanguagePackConfigurations(userDataPath).then(configs => {
|
||||
if (!configs) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
const resolvedLocale = resolveLanguagePackLocale(configs, locale);
|
||||
if (!resolvedLocale) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
locale = resolvedLocale;
|
||||
const packConfig = configs[locale];
|
||||
let mainPack;
|
||||
if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
return exists(mainPack).then(fileExists => {
|
||||
if (!fileExists) {
|
||||
return defaultResult(initialLocale);
|
||||
}
|
||||
const packId = packConfig.hash + '.' + locale;
|
||||
const cacheRoot = path.join(userDataPath, 'clp', packId);
|
||||
const coreLocation = path.join(cacheRoot, commit);
|
||||
const translationsConfigFile = path.join(cacheRoot, 'tcf.json');
|
||||
const corruptedFile = path.join(cacheRoot, 'corrupted.info');
|
||||
const result = {
|
||||
locale: initialLocale,
|
||||
osLocale,
|
||||
availableLanguages: { '*': locale },
|
||||
_languagePackId: packId,
|
||||
_translationsConfigFile: translationsConfigFile,
|
||||
_cacheRoot: cacheRoot,
|
||||
_resolvedLanguagePackCoreLocation: coreLocation,
|
||||
_corruptedFile: corruptedFile
|
||||
};
|
||||
return exists(corruptedFile).then(corrupted => {
|
||||
// The nls cache directory is corrupted.
|
||||
let toDelete;
|
||||
if (corrupted) {
|
||||
toDelete = rimraf(cacheRoot);
|
||||
} else {
|
||||
toDelete = Promise.resolve(undefined);
|
||||
}
|
||||
return toDelete.then(() => {
|
||||
return exists(coreLocation).then(fileExists => {
|
||||
if (fileExists) {
|
||||
// We don't wait for this. No big harm if we can't touch
|
||||
touch(coreLocation).catch(() => { });
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}
|
||||
return mkdirp(coreLocation).then(() => {
|
||||
return Promise.all([readFile(metaDataFile), readFile(mainPack)]);
|
||||
}).then(values => {
|
||||
const metadata = JSON.parse(values[0]);
|
||||
const packData = JSON.parse(values[1]).contents;
|
||||
const bundles = Object.keys(metadata.bundles);
|
||||
const writes = [];
|
||||
for (const bundle of bundles) {
|
||||
const modules = metadata.bundles[bundle];
|
||||
const target = Object.create(null);
|
||||
for (const module of modules) {
|
||||
const keys = metadata.keys[module];
|
||||
const defaultMessages = metadata.messages[module];
|
||||
const translations = packData[module];
|
||||
let targetStrings;
|
||||
if (translations) {
|
||||
targetStrings = [];
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const elem = keys[i];
|
||||
const key = typeof elem === 'string' ? elem : elem.key;
|
||||
let translatedMessage = translations[key];
|
||||
if (translatedMessage === undefined) {
|
||||
translatedMessage = defaultMessages[i];
|
||||
}
|
||||
targetStrings.push(translatedMessage);
|
||||
}
|
||||
} else {
|
||||
targetStrings = defaultMessages;
|
||||
}
|
||||
target[module] = targetStrings;
|
||||
}
|
||||
writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target)));
|
||||
}
|
||||
writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations)));
|
||||
return Promise.all(writes);
|
||||
}).then(() => {
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}).catch(err => {
|
||||
console.error('Generating translation files failed.', err);
|
||||
return defaultResult(locale);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Generating translation files failed.', err);
|
||||
return defaultResult(locale);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getNLSConfiguration
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof define === 'function') {
|
||||
// amd
|
||||
define(['path', 'fs', 'vs/base/common/performance'], function (/** @type {typeof import('path')} */ path, /** @type {typeof import('fs')} */ fs, /** @type {typeof import('../common/performance')} */ perf) { return factory(path, fs, perf); });
|
||||
} else if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const perf = require('../common/performance');
|
||||
module.exports = factory(path, fs, perf);
|
||||
} else {
|
||||
throw new Error('Unknown context');
|
||||
}
|
||||
}());
|
38
src/vs/base/node/nls.d.ts
vendored
Normal file
38
src/vs/base/node/nls.d.ts
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { INLSConfiguration } from 'vs/nls';
|
||||
|
||||
export interface IResolveNLSConfigurationContext {
|
||||
|
||||
/**
|
||||
* Location where `nls.messages.json` and `nls.keys.json` are stored.
|
||||
*/
|
||||
readonly nlsMetadataPath: string;
|
||||
|
||||
/**
|
||||
* Path to the user data directory. Used as a cache for
|
||||
* language packs converted to the format we need.
|
||||
*/
|
||||
readonly userDataPath: string;
|
||||
|
||||
/**
|
||||
* Commit of the running application. Can be `undefined`
|
||||
* when not built.
|
||||
*/
|
||||
readonly commit: string | undefined;
|
||||
|
||||
/**
|
||||
* Locale as defined in `argv.json` or `app.getLocale()`.
|
||||
*/
|
||||
readonly userLocale: string;
|
||||
|
||||
/**
|
||||
* Locale as defined by the OS (e.g. `app.getPreferredSystemLanguages()`).
|
||||
*/
|
||||
readonly osLocale: string;
|
||||
}
|
||||
|
||||
export function resolveNLSConfiguration(context: IResolveNLSConfigurationContext): Promise<INLSConfiguration>;
|
288
src/vs/base/node/nls.js
Normal file
288
src/vs/base/node/nls.js
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path="../../../typings/require.d.ts" />
|
||||
|
||||
//@ts-check
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @import { INLSConfiguration, ILanguagePacks } from '../../nls'
|
||||
* @import { IResolveNLSConfigurationContext } from './nls'
|
||||
*/
|
||||
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* @param {typeof import('path')} path
|
||||
* @param {typeof import('fs')} fs
|
||||
* @param {typeof import('../common/performance')} perf
|
||||
*/
|
||||
function factory(path, fs, perf) {
|
||||
|
||||
//#region fs helpers
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
*/
|
||||
async function exists(path) {
|
||||
try {
|
||||
await fs.promises.access(path);
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
*/
|
||||
function touch(path) {
|
||||
const date = new Date();
|
||||
return fs.promises.utimes(path, date, date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
*/
|
||||
function mkdirp(path) {
|
||||
return fs.promises.mkdir(path, { recursive: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
*/
|
||||
function rimraf(path) {
|
||||
return fs.promises.rm(path, { recursive: true, force: true, maxRetries: 3 });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
*/
|
||||
function readFile(path) {
|
||||
return fs.promises.readFile(path, 'utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {string} content
|
||||
*/
|
||||
function writeFile(path, content) {
|
||||
return fs.promises.writeFile(path, content, 'utf-8');
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
/**
|
||||
* The `languagepacks.json` file is a JSON file that contains all metadata
|
||||
* about installed language extensions per language. Specifically, for
|
||||
* core (`vscode`) and all extensions it supports, it points to the related
|
||||
* translation files.
|
||||
*
|
||||
* The file is updated whenever a new language pack is installed or removed.
|
||||
*
|
||||
* @param {string} userDataPath
|
||||
* @returns {Promise<ILanguagePacks | undefined>}
|
||||
*/
|
||||
async function getLanguagePackConfigurations(userDataPath) {
|
||||
const configFile = path.join(userDataPath, 'languagepacks.json');
|
||||
try {
|
||||
return JSON.parse(await readFile(configFile));
|
||||
} catch (err) {
|
||||
return undefined; // Do nothing. If we can't read the file we have no language pack config.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ILanguagePacks} languagePacks
|
||||
* @param {string | undefined} locale
|
||||
*/
|
||||
function resolveLanguagePackLanguage(languagePacks, locale) {
|
||||
try {
|
||||
while (locale) {
|
||||
if (languagePacks[locale]) {
|
||||
return locale;
|
||||
}
|
||||
|
||||
const index = locale.lastIndexOf('-');
|
||||
if (index > 0) {
|
||||
locale = locale.substring(0, index);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Resolving language pack configuration failed.', error);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} userLocale
|
||||
* @param {string} osLocale
|
||||
* @param {string} nlsMetadataPath
|
||||
* @returns {INLSConfiguration}
|
||||
*/
|
||||
function defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath) {
|
||||
perf.mark('code/didGenerateNls');
|
||||
|
||||
return {
|
||||
userLocale,
|
||||
osLocale,
|
||||
resolvedLanguage: 'en',
|
||||
defaultMessagesFile: path.join(nlsMetadataPath, 'nls.messages.json'),
|
||||
|
||||
// NLS: below 2 are a relic from old times only used by vscode-nls and deprecated
|
||||
locale: 'en',
|
||||
availableLanguages: {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {IResolveNLSConfigurationContext} context
|
||||
* @returns {Promise<INLSConfiguration>}
|
||||
*/
|
||||
async function resolveNLSConfiguration({ userLocale, osLocale, userDataPath, commit, nlsMetadataPath }) {
|
||||
perf.mark('code/willGenerateNls');
|
||||
|
||||
if (
|
||||
process.env['VSCODE_DEV'] ||
|
||||
userLocale === 'pseudo' ||
|
||||
userLocale === 'en' || userLocale === 'en-us' ||
|
||||
!commit ||
|
||||
!userDataPath
|
||||
) {
|
||||
return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath);
|
||||
}
|
||||
|
||||
try {
|
||||
const languagePacks = await getLanguagePackConfigurations(userDataPath);
|
||||
if (!languagePacks) {
|
||||
return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath);
|
||||
}
|
||||
|
||||
const resolvedLanguage = resolveLanguagePackLanguage(languagePacks, userLocale);
|
||||
if (!resolvedLanguage) {
|
||||
return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath);
|
||||
}
|
||||
|
||||
const languagePack = languagePacks[resolvedLanguage];
|
||||
const mainLanguagePackPath = languagePack?.translations?.['vscode'];
|
||||
if (
|
||||
!languagePack ||
|
||||
typeof languagePack.hash !== 'string' ||
|
||||
!languagePack.translations ||
|
||||
typeof mainLanguagePackPath !== 'string' ||
|
||||
!(await exists(mainLanguagePackPath))
|
||||
) {
|
||||
return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath);
|
||||
}
|
||||
|
||||
const languagePackId = `${languagePack.hash}.${resolvedLanguage}`;
|
||||
const globalLanguagePackCachePath = path.join(userDataPath, 'clp', languagePackId);
|
||||
const commitLanguagePackCachePath = path.join(globalLanguagePackCachePath, commit);
|
||||
const languagePackMessagesFile = path.join(commitLanguagePackCachePath, 'nls.messages.json');
|
||||
const translationsConfigFile = path.join(globalLanguagePackCachePath, 'tcf.json');
|
||||
const languagePackCorruptMarkerFile = path.join(globalLanguagePackCachePath, 'corrupted.info');
|
||||
|
||||
if (await exists(languagePackCorruptMarkerFile)) {
|
||||
await rimraf(globalLanguagePackCachePath); // delete corrupted cache folder
|
||||
}
|
||||
|
||||
/** @type {INLSConfiguration} */
|
||||
const result = {
|
||||
userLocale,
|
||||
osLocale,
|
||||
resolvedLanguage,
|
||||
defaultMessagesFile: path.join(nlsMetadataPath, 'nls.messages.json'),
|
||||
languagePack: {
|
||||
translationsConfigFile,
|
||||
messagesFile: languagePackMessagesFile,
|
||||
corruptMarkerFile: languagePackCorruptMarkerFile
|
||||
},
|
||||
|
||||
// NLS: below properties are a relic from old times only used by vscode-nls and deprecated
|
||||
locale: userLocale,
|
||||
availableLanguages: { '*': resolvedLanguage },
|
||||
_languagePackId: languagePackId,
|
||||
_languagePackSupport: true,
|
||||
_translationsConfigFile: translationsConfigFile,
|
||||
_cacheRoot: globalLanguagePackCachePath,
|
||||
_resolvedLanguagePackCoreLocation: commitLanguagePackCachePath,
|
||||
_corruptedFile: languagePackCorruptMarkerFile
|
||||
};
|
||||
|
||||
if (await exists(commitLanguagePackCachePath)) {
|
||||
touch(commitLanguagePackCachePath).catch(() => { }); // We don't wait for this. No big harm if we can't touch
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @type {[unknown, Array<[string, string[]]>, string[], { contents: Record<string, Record<string, string>> }]} */
|
||||
// ^moduleId ^nlsKeys ^moduleId ^nlsKey ^nlsValue
|
||||
const [
|
||||
,
|
||||
nlsDefaultKeys,
|
||||
nlsDefaultMessages,
|
||||
nlsPackdata
|
||||
] = await Promise.all([
|
||||
mkdirp(commitLanguagePackCachePath),
|
||||
JSON.parse(await readFile(path.join(nlsMetadataPath, 'nls.keys.json'))),
|
||||
JSON.parse(await readFile(path.join(nlsMetadataPath, 'nls.messages.json'))),
|
||||
JSON.parse(await readFile(mainLanguagePackPath))
|
||||
]);
|
||||
|
||||
/** @type {string[]} */
|
||||
const nlsResult = [];
|
||||
|
||||
// We expect NLS messages to be in a flat array in sorted order as they
|
||||
// where produced during build time. We use `nls.keys.json` to know the
|
||||
// right order and then lookup the related message from the translation.
|
||||
// If a translation does not exist, we fallback to the default message.
|
||||
|
||||
let nlsIndex = 0;
|
||||
for (const [moduleId, nlsKeys] of nlsDefaultKeys) {
|
||||
const moduleTranslations = nlsPackdata.contents[moduleId];
|
||||
for (const nlsKey of nlsKeys) {
|
||||
nlsResult.push(moduleTranslations?.[nlsKey] || nlsDefaultMessages[nlsIndex]);
|
||||
nlsIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
writeFile(languagePackMessagesFile, JSON.stringify(nlsResult)),
|
||||
writeFile(translationsConfigFile, JSON.stringify(languagePack.translations))
|
||||
]);
|
||||
|
||||
perf.mark('code/didGenerateNls');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Generating translation files failed.', error);
|
||||
}
|
||||
|
||||
return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath);
|
||||
}
|
||||
|
||||
return {
|
||||
resolveNLSConfiguration
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof define === 'function') {
|
||||
// amd
|
||||
define(['path', 'fs', 'vs/base/common/performance'], function (/** @type {typeof import('path')} */ path, /** @type {typeof import('fs')} */ fs, /** @type {typeof import('../common/performance')} */ perf) { return factory(path, fs, perf); });
|
||||
} else if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
// commonjs
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const perf = require('../common/performance');
|
||||
module.exports = factory(path, fs, perf);
|
||||
} else {
|
||||
throw new Error('vs/base/node/nls defined in UNKNOWN context (neither requirejs or commonjs)');
|
||||
}
|
||||
})();
|
|
@ -53,4 +53,21 @@ export interface ISandboxConfiguration {
|
|||
* Location of V8 code cache.
|
||||
*/
|
||||
codeCachePath?: string;
|
||||
|
||||
/**
|
||||
* NLS support
|
||||
*/
|
||||
nls: {
|
||||
|
||||
/**
|
||||
* All NLS messages produced by `localize` and `localize2` calls
|
||||
* under `src/vs`.
|
||||
*/
|
||||
messages: string[];
|
||||
|
||||
/**
|
||||
* The actual language of the NLS messages (e.g. 'en', de' or 'pt-br').
|
||||
*/
|
||||
language: string | undefined;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -36,25 +36,14 @@
|
|||
<script src="{{WORKBENCH_WEB_BASE_URL}}/out/vs/loader.js"></script>
|
||||
<script src="{{WORKBENCH_WEB_BASE_URL}}/out/vs/webPackagePaths.js"></script>
|
||||
<script>
|
||||
|
||||
// Packages
|
||||
const baseUrl = new URL('{{WORKBENCH_WEB_BASE_URL}}', window.location.origin).toString();
|
||||
Object.keys(self.webPackagePaths).map(function (key, index) {
|
||||
self.webPackagePaths[key] = `${baseUrl}/node_modules/${key}/${self.webPackagePaths[key]}`;
|
||||
});
|
||||
|
||||
// Set up nls if the user is not using the default language (English)
|
||||
const nlsConfig = {};
|
||||
// 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}}'
|
||||
};
|
||||
}
|
||||
|
||||
// AMD Loader
|
||||
require.config({
|
||||
baseUrl: `${baseUrl}/out`,
|
||||
recordStats: true,
|
||||
|
@ -66,14 +55,16 @@
|
|||
throw new Error(`Invalid script url: ${value}`)
|
||||
}
|
||||
}),
|
||||
paths: self.webPackagePaths,
|
||||
...nlsConfig
|
||||
paths: self.webPackagePaths
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
performance.mark('code/willLoadWorkbenchMain');
|
||||
</script>
|
||||
<script src="{{WORKBENCH_WEB_BASE_URL}}/out/vs/workbench/workbench.web.main.nls.js"></script>
|
||||
<!-- always ensure built in english NLS messages -->
|
||||
<script src="{{WORKBENCH_NLS_FALLBACK_URL}}"></script>
|
||||
<!-- attempt to load NLS messages in case non-english -->
|
||||
<script src="{{WORKBENCH_NLS_URL}}"></script>
|
||||
<script src="{{WORKBENCH_WEB_BASE_URL}}/out/vs/workbench/workbench.web.main.js"></script>
|
||||
<script src="{{WORKBENCH_WEB_BASE_URL}}/out/vs/code/browser/workbench/workbench.js"></script>
|
||||
</html>
|
||||
|
|
|
@ -10,13 +10,12 @@ import { hostname, release } from 'os';
|
|||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { isSigPipeError, onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors';
|
||||
import { isEqualOrParent } from 'vs/base/common/extpath';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { parse } from 'vs/base/common/jsonc';
|
||||
import { getPathLabel } from 'vs/base/common/labels';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas, VSCODE_AUTHORITY } from 'vs/base/common/network';
|
||||
import { isAbsolute, join, posix } from 'vs/base/common/path';
|
||||
import { join, posix } from 'vs/base/common/path';
|
||||
import { IProcessEnvironment, isLinux, isLinuxSnap, isMacintosh, isWindows, OS } from 'vs/base/common/platform';
|
||||
import { assertType } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
@ -496,24 +495,6 @@ export class CodeApplication extends Disposable {
|
|||
return this.resolveShellEnvironment(args, env, false);
|
||||
});
|
||||
|
||||
validatedIpcMain.handle('vscode:writeNlsFile', (event, path: unknown, data: unknown) => {
|
||||
const uri = this.validateNlsPath([path]);
|
||||
if (!uri || typeof data !== 'string') {
|
||||
throw new Error('Invalid operation (vscode:writeNlsFile)');
|
||||
}
|
||||
|
||||
return this.fileService.writeFile(uri, VSBuffer.fromString(data));
|
||||
});
|
||||
|
||||
validatedIpcMain.handle('vscode:readNlsFile', async (event, ...paths: unknown[]) => {
|
||||
const uri = this.validateNlsPath(paths);
|
||||
if (!uri) {
|
||||
throw new Error('Invalid operation (vscode:readNlsFile)');
|
||||
}
|
||||
|
||||
return (await this.fileService.readFile(uri)).value.toString();
|
||||
});
|
||||
|
||||
validatedIpcMain.on('vscode:toggleDevTools', event => event.sender.toggleDevTools());
|
||||
validatedIpcMain.on('vscode:openDevTools', event => event.sender.openDevTools());
|
||||
|
||||
|
@ -529,26 +510,6 @@ export class CodeApplication extends Disposable {
|
|||
//#endregion
|
||||
}
|
||||
|
||||
private validateNlsPath(pathSegments: unknown[]): URI | undefined {
|
||||
let path: string | undefined = undefined;
|
||||
|
||||
for (const pathSegment of pathSegments) {
|
||||
if (typeof pathSegment === 'string') {
|
||||
if (typeof path !== 'string') {
|
||||
path = pathSegment;
|
||||
} else {
|
||||
path = join(path, pathSegment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentMainService.cachedLanguagesPath, !isLinux)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return URI.file(path);
|
||||
}
|
||||
|
||||
private onUnexpectedError(error: Error): void {
|
||||
if (error) {
|
||||
|
||||
|
|
|
@ -20,13 +20,12 @@
|
|||
// Add a perf entry right from the top
|
||||
performance.mark('code/didStartRenderer');
|
||||
|
||||
// Load workbench main JS, CSS and NLS all in parallel. This is an
|
||||
// Load workbench main JS and CSS all in parallel. This is an
|
||||
// optimization to prevent a waterfall of loading to happen, because
|
||||
// we know for a fact that workbench.desktop.main will depend on
|
||||
// the related CSS and NLS counterparts.
|
||||
// the related CSS counterpart.
|
||||
bootstrapWindow.load([
|
||||
'vs/workbench/workbench.desktop.main',
|
||||
'vs/nls!vs/workbench/workbench.desktop.main',
|
||||
'vs/css!vs/workbench/workbench.desktop.main'
|
||||
],
|
||||
function (desktopMain, configuration) {
|
||||
|
|
330
src/vs/nls.ts
330
src/vs/nls.ts
|
@ -3,27 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
let isPseudo = (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0);
|
||||
const DEFAULT_TAG = 'i-default';
|
||||
|
||||
interface INLSPluginConfig {
|
||||
availableLanguages?: INLSPluginConfigAvailableLanguages;
|
||||
loadBundle?: BundleLoader;
|
||||
translationServiceUrl?: string;
|
||||
}
|
||||
|
||||
export interface INLSPluginConfigAvailableLanguages {
|
||||
'*'?: string;
|
||||
[module: string]: string | undefined;
|
||||
}
|
||||
|
||||
interface BundleLoader {
|
||||
(bundle: string, locale: string | null, cb: (err: Error, messages: string[] | IBundledStrings) => void): void;
|
||||
}
|
||||
|
||||
interface IBundledStrings {
|
||||
[moduleId: string]: string[];
|
||||
}
|
||||
// VSCODE_GLOBALS: NLS
|
||||
const isPseudo = globalThis._VSCODE_NLS_LANGUAGE === 'pseudo' || (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0);
|
||||
|
||||
export interface ILocalizeInfo {
|
||||
key: string;
|
||||
|
@ -35,30 +16,6 @@ export interface ILocalizedString {
|
|||
value: string;
|
||||
}
|
||||
|
||||
interface ILocalizeFunc {
|
||||
(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
|
||||
(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
|
||||
}
|
||||
|
||||
interface IBoundLocalizeFunc {
|
||||
(idx: number, defaultValue: null): string;
|
||||
}
|
||||
|
||||
interface ILocalize2Func {
|
||||
(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
|
||||
(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
|
||||
}
|
||||
|
||||
interface IBoundLocalize2Func {
|
||||
(idx: number, defaultValue: string): ILocalizedString;
|
||||
}
|
||||
|
||||
interface IConsumerAPI {
|
||||
localize: ILocalizeFunc | IBoundLocalizeFunc;
|
||||
localize2: ILocalize2Func | IBoundLocalize2Func;
|
||||
getConfiguredDefaultLocale(stringFromLocalizeCall: string): string | undefined;
|
||||
}
|
||||
|
||||
function _format(message: string, args: (string | number | boolean | undefined | null)[]): string {
|
||||
let result: string;
|
||||
|
||||
|
@ -86,49 +43,6 @@ function _format(message: string, args: (string | number | boolean | undefined |
|
|||
return result;
|
||||
}
|
||||
|
||||
function findLanguageForModule(config: INLSPluginConfigAvailableLanguages, name: string) {
|
||||
let result = config[name];
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = config['*'];
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function endWithSlash(path: string): string {
|
||||
if (path.charAt(path.length - 1) === '/') {
|
||||
return path;
|
||||
}
|
||||
return path + '/';
|
||||
}
|
||||
|
||||
async function getMessagesFromTranslationsService(translationServiceUrl: string, language: string, name: string): Promise<string[] | IBundledStrings> {
|
||||
const url = endWithSlash(translationServiceUrl) + endWithSlash(language) + 'vscode/' + endWithSlash(name);
|
||||
const res = await fetch(url);
|
||||
if (res.ok) {
|
||||
const messages = await res.json() as string[] | IBundledStrings;
|
||||
return messages;
|
||||
}
|
||||
throw new Error(`${res.status} - ${res.statusText}`);
|
||||
}
|
||||
|
||||
function createScopedLocalize(scope: string[]): IBoundLocalizeFunc {
|
||||
return function (idx: number, defaultValue: null) {
|
||||
const restArgs = Array.prototype.slice.call(arguments, 2);
|
||||
return _format(scope[idx], restArgs);
|
||||
};
|
||||
}
|
||||
|
||||
function createScopedLocalize2(scope: string[]): IBoundLocalize2Func {
|
||||
return (idx: number, defaultValue: string, ...args) => ({
|
||||
value: _format(scope[idx], args),
|
||||
original: _format(defaultValue, args)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a string to be localized. Returns the localized string.
|
||||
*
|
||||
|
@ -160,10 +74,30 @@ export function localize(key: string, message: string, ...args: (string | number
|
|||
/**
|
||||
* @skipMangle
|
||||
*/
|
||||
export function localize(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): string {
|
||||
export function localize(data: ILocalizeInfo | string /* | number when built */, message: string /* | null when built */, ...args: (string | number | boolean | undefined | null)[]): string {
|
||||
if (typeof data === 'number') {
|
||||
return _format(lookupMessage(data, message), args);
|
||||
}
|
||||
return _format(message, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only used when built: Looks up the message in the global NLS table.
|
||||
* This table is being made available as a global through bootstrapping
|
||||
* depending on the target context.
|
||||
*/
|
||||
function lookupMessage(index: number, fallback: string | null): string {
|
||||
// VSCODE_GLOBALS: NLS
|
||||
const message = globalThis._VSCODE_NLS_MESSAGES?.[index];
|
||||
if (typeof message !== 'string') {
|
||||
if (typeof fallback === 'string') {
|
||||
return fallback;
|
||||
}
|
||||
throw new Error(`!!! NLS MISSING: ${index} !!!`);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a string to be localized. Returns an {@linkcode ILocalizedString}
|
||||
* which contains the localized string and the original string.
|
||||
|
@ -197,123 +131,107 @@ export function localize2(key: string, message: string, ...args: (string | numbe
|
|||
/**
|
||||
* @skipMangle
|
||||
*/
|
||||
export function localize2(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString {
|
||||
const original = _format(message, args);
|
||||
return {
|
||||
value: original,
|
||||
original
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param stringFromLocalizeCall You must pass in a string that was returned from a `nls.localize()` call
|
||||
* in order to ensure the loader plugin has been initialized before this function is called.
|
||||
*/
|
||||
export function getConfiguredDefaultLocale(stringFromLocalizeCall: string): string | undefined;
|
||||
/**
|
||||
* @skipMangle
|
||||
*/
|
||||
export function getConfiguredDefaultLocale(_: string): string | undefined {
|
||||
// This returns undefined because this implementation isn't used and is overwritten by the loader
|
||||
// when loaded.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @skipMangle
|
||||
*/
|
||||
export function setPseudoTranslation(value: boolean) {
|
||||
isPseudo = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked in a built product at run-time
|
||||
* @skipMangle
|
||||
*/
|
||||
export function create(key: string, data: IBundledStrings & IConsumerAPI): IConsumerAPI {
|
||||
return {
|
||||
localize: createScopedLocalize(data[key]),
|
||||
localize2: createScopedLocalize2(data[key]),
|
||||
getConfiguredDefaultLocale: data.getConfiguredDefaultLocale ?? ((_: string) => undefined)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the loader at run-time
|
||||
* @skipMangle
|
||||
*/
|
||||
export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void {
|
||||
const pluginConfig: INLSPluginConfig = config['vs/nls'] ?? {};
|
||||
if (!name || name.length === 0) {
|
||||
// TODO: We need to give back the mangled names here
|
||||
return load({
|
||||
localize: localize,
|
||||
localize2: localize2,
|
||||
getConfiguredDefaultLocale: () => pluginConfig.availableLanguages?.['*']
|
||||
} as IConsumerAPI);
|
||||
}
|
||||
const language = pluginConfig.availableLanguages ? findLanguageForModule(pluginConfig.availableLanguages, name) : null;
|
||||
const useDefaultLanguage = language === null || language === DEFAULT_TAG;
|
||||
let suffix = '.nls';
|
||||
if (!useDefaultLanguage) {
|
||||
suffix = suffix + '.' + language;
|
||||
}
|
||||
const messagesLoaded = (messages: string[] | IBundledStrings) => {
|
||||
if (Array.isArray(messages)) {
|
||||
(messages as any as IConsumerAPI).localize = createScopedLocalize(messages);
|
||||
(messages as any as IConsumerAPI).localize2 = createScopedLocalize2(messages);
|
||||
} else {
|
||||
(messages as any as IConsumerAPI).localize = createScopedLocalize(messages[name]);
|
||||
(messages as any as IConsumerAPI).localize2 = createScopedLocalize2(messages[name]);
|
||||
}
|
||||
(messages as any as IConsumerAPI).getConfiguredDefaultLocale = () => pluginConfig.availableLanguages?.['*'];
|
||||
load(messages);
|
||||
};
|
||||
if (typeof pluginConfig.loadBundle === 'function') {
|
||||
(pluginConfig.loadBundle as BundleLoader)(name, language, (err: Error, messages) => {
|
||||
// We have an error. Load the English default strings to not fail
|
||||
if (err) {
|
||||
req([name + '.nls'], messagesLoaded);
|
||||
} else {
|
||||
messagesLoaded(messages);
|
||||
}
|
||||
});
|
||||
} else if (pluginConfig.translationServiceUrl && !useDefaultLanguage) {
|
||||
(async () => {
|
||||
try {
|
||||
const messages = await getMessagesFromTranslationsService(pluginConfig.translationServiceUrl!, language, name);
|
||||
return messagesLoaded(messages);
|
||||
} catch (err) {
|
||||
// Language is already as generic as it gets, so require default messages
|
||||
if (!language.includes('-')) {
|
||||
console.error(err);
|
||||
return req([name + '.nls'], messagesLoaded);
|
||||
}
|
||||
try {
|
||||
// Since there is a dash, the language configured is a specific sub-language of the same generic language.
|
||||
// Since we were unable to load the specific language, try to load the generic language. Ex. we failed to find a
|
||||
// Swiss German (de-CH), so try to load the generic German (de) messages instead.
|
||||
const genericLanguage = language.split('-')[0];
|
||||
const messages = await getMessagesFromTranslationsService(pluginConfig.translationServiceUrl!, genericLanguage, name);
|
||||
// We got some messages, so we configure the configuration to use the generic language for this session.
|
||||
pluginConfig.availableLanguages ??= {};
|
||||
pluginConfig.availableLanguages['*'] = genericLanguage;
|
||||
return messagesLoaded(messages);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return req([name + '.nls'], messagesLoaded);
|
||||
}
|
||||
}
|
||||
})();
|
||||
export function localize2(data: ILocalizeInfo | string /* | number when built */, originalMessage: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString {
|
||||
let message: string;
|
||||
if (typeof data === 'number') {
|
||||
message = lookupMessage(data, originalMessage);
|
||||
} else {
|
||||
req([name + suffix], messagesLoaded, (err: Error) => {
|
||||
if (suffix === '.nls') {
|
||||
console.error('Failed trying to load default language strings', err);
|
||||
return;
|
||||
}
|
||||
console.error(`Failed to load message bundle for language ${language}. Falling back to the default language:`, err);
|
||||
req([name + '.nls'], messagesLoaded);
|
||||
});
|
||||
message = originalMessage;
|
||||
}
|
||||
|
||||
const value = _format(message, args);
|
||||
|
||||
return {
|
||||
value,
|
||||
original: originalMessage === message ? value : _format(originalMessage, args)
|
||||
};
|
||||
}
|
||||
|
||||
export interface INLSLanguagePackConfiguration {
|
||||
|
||||
/**
|
||||
* The path to the translations config file that contains pointers to
|
||||
* all message bundles for `main` and extensions.
|
||||
*/
|
||||
readonly translationsConfigFile: string;
|
||||
|
||||
/**
|
||||
* The path to the file containing the translations for this language
|
||||
* pack as flat string array.
|
||||
*/
|
||||
readonly messagesFile: string;
|
||||
|
||||
/**
|
||||
* The path to the file that can be used to signal a corrupt language
|
||||
* pack, for example when reading the `messagesFile` fails. This will
|
||||
* instruct the application to re-create the cache on next startup.
|
||||
*/
|
||||
readonly corruptMarkerFile: string;
|
||||
}
|
||||
|
||||
export interface INLSConfiguration {
|
||||
|
||||
/**
|
||||
* Locale as defined in `argv.json` or `app.getLocale()`.
|
||||
*/
|
||||
readonly userLocale: string;
|
||||
|
||||
/**
|
||||
* Locale as defined by the OS (e.g. `app.getPreferredSystemLanguages()`).
|
||||
*/
|
||||
readonly osLocale: string;
|
||||
|
||||
/**
|
||||
* The actual language of the UI that ends up being used considering `userLocale`
|
||||
* and `osLocale`.
|
||||
*/
|
||||
readonly resolvedLanguage: string;
|
||||
|
||||
/**
|
||||
* Defined if a language pack is used that is not the
|
||||
* default english language pack. This requires a language
|
||||
* pack to be installed as extension.
|
||||
*/
|
||||
readonly languagePack?: INLSLanguagePackConfiguration;
|
||||
|
||||
/**
|
||||
* The path to the file containing the default english messages
|
||||
* as flat string array. The file is only present in built
|
||||
* versions of the application.
|
||||
*/
|
||||
readonly defaultMessagesFile: string;
|
||||
|
||||
/**
|
||||
* Below properties are deprecated and only there to continue support
|
||||
* for `vscode-nls` module that depends on them.
|
||||
* Refs https://github.com/microsoft/vscode-nls/blob/main/src/node/main.ts#L36-L46
|
||||
*/
|
||||
/** @deprecated */
|
||||
readonly locale: string;
|
||||
/** @deprecated */
|
||||
readonly availableLanguages: Record<string, string>;
|
||||
/** @deprecated */
|
||||
readonly _languagePackSupport?: boolean;
|
||||
/** @deprecated */
|
||||
readonly _languagePackId?: string;
|
||||
/** @deprecated */
|
||||
readonly _translationsConfigFile?: string;
|
||||
/** @deprecated */
|
||||
readonly _cacheRoot?: string;
|
||||
/** @deprecated */
|
||||
readonly _resolvedLanguagePackCoreLocation?: string;
|
||||
/** @deprecated */
|
||||
readonly _corruptedFile?: string;
|
||||
}
|
||||
|
||||
export interface ILanguagePack {
|
||||
readonly hash: string;
|
||||
readonly label: string | undefined;
|
||||
readonly extensions: {
|
||||
readonly extensionIdentifier: { readonly id: string; readonly uuid?: string };
|
||||
readonly version: string;
|
||||
}[];
|
||||
readonly translations: Record<string, string | undefined>;
|
||||
}
|
||||
|
||||
export type ILanguagePacks = Record<string, ILanguagePack | undefined>;
|
||||
|
|
|
@ -19,9 +19,6 @@ export const IEnvironmentMainService = refineServiceDecorator<IEnvironmentServic
|
|||
*/
|
||||
export interface IEnvironmentMainService extends INativeEnvironmentService {
|
||||
|
||||
// --- NLS cache path
|
||||
readonly cachedLanguagesPath: string;
|
||||
|
||||
// --- backup paths
|
||||
readonly backupHome: string;
|
||||
|
||||
|
@ -44,9 +41,6 @@ export class EnvironmentMainService extends NativeEnvironmentService implements
|
|||
|
||||
private _snapEnv: Record<string, string> = {};
|
||||
|
||||
@memoize
|
||||
get cachedLanguagesPath(): string { return join(this.userDataPath, 'clp'); }
|
||||
|
||||
@memoize
|
||||
get backupHome(): string { return join(this.userDataPath, 'Backups'); }
|
||||
|
||||
|
|
|
@ -81,7 +81,12 @@ export class IssueMainService implements IIssueMainService {
|
|||
arch: arch(),
|
||||
release: release(),
|
||||
},
|
||||
product
|
||||
product,
|
||||
nls: {
|
||||
// VSCODE_GLOBALS: NLS
|
||||
messages: globalThis._VSCODE_NLS_MESSAGES,
|
||||
language: globalThis._VSCODE_NLS_LANGUAGE
|
||||
}
|
||||
});
|
||||
|
||||
this.issueReporterWindow.loadURL(
|
||||
|
|
|
@ -153,7 +153,12 @@ export class ProcessMainService implements IProcessMainService {
|
|||
windowId: this.processExplorerWindow.id,
|
||||
userEnv: this.userEnv,
|
||||
data,
|
||||
product
|
||||
product,
|
||||
nls: {
|
||||
// VSCODE_GLOBALS: NLS
|
||||
messages: globalThis._VSCODE_NLS_MESSAGES,
|
||||
language: globalThis._VSCODE_NLS_LANGUAGE
|
||||
}
|
||||
});
|
||||
|
||||
this.processExplorerWindow.loadURL(
|
||||
|
|
|
@ -1444,6 +1444,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
|||
workspace: options.workspace,
|
||||
userEnv: { ...this.initialUserEnv, ...options.userEnv },
|
||||
|
||||
nls: {
|
||||
// VSCODE_GLOBALS: NLS
|
||||
messages: globalThis._VSCODE_NLS_MESSAGES,
|
||||
language: globalThis._VSCODE_NLS_LANGUAGE
|
||||
},
|
||||
|
||||
filesToOpenOrCreate: options.filesToOpen?.filesToOpenOrCreate,
|
||||
filesToDiff: options.filesToOpen?.filesToDiff,
|
||||
filesToMerge: options.filesToOpen?.filesToMerge,
|
||||
|
|
|
@ -43,7 +43,7 @@ export async function buildUserEnvironment(startParamsEnv: { [key: string]: stri
|
|||
...{
|
||||
VSCODE_AMD_ENTRYPOINT: 'vs/workbench/api/node/extensionHostProcess',
|
||||
VSCODE_HANDLES_UNCAUGHT_ERRORS: 'true',
|
||||
VSCODE_NLS_CONFIG: JSON.stringify(nlsConfig, undefined, 0)
|
||||
VSCODE_NLS_CONFIG: JSON.stringify(nlsConfig)
|
||||
},
|
||||
...startParamsEnv
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
|||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
|
||||
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { getNLSConfiguration, InternalNLSConfiguration } from 'vs/server/node/remoteLanguagePacks';
|
||||
import { getNLSConfiguration } from 'vs/server/node/remoteLanguagePacks';
|
||||
|
||||
export class ExtensionsScannerService extends AbstractExtensionsScannerService implements IExtensionsScannerService {
|
||||
|
||||
|
@ -38,9 +38,9 @@ export class ExtensionsScannerService extends AbstractExtensionsScannerService i
|
|||
|
||||
protected async getTranslations(language: string): Promise<Translations> {
|
||||
const config = await getNLSConfiguration(language, this.nativeEnvironmentService.userDataPath);
|
||||
if (InternalNLSConfiguration.is(config)) {
|
||||
if (config.languagePack) {
|
||||
try {
|
||||
const content = await this.fileService.readFile(URI.file(config._translationsConfigFile));
|
||||
const content = await this.fileService.readFile(URI.file(config.languagePack.translationsConfigFile));
|
||||
return JSON.parse(content.value.toString());
|
||||
} catch (err) { /* Ignore error */ }
|
||||
}
|
||||
|
|
|
@ -3,46 +3,37 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
import * as path from 'vs/base/common/path';
|
||||
|
||||
import * as lp from 'vs/base/node/languagePacks';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import type { INLSConfiguration } from 'vs/nls';
|
||||
import { resolveNLSConfiguration } from 'vs/base/node/nls';
|
||||
import { Promises } from 'vs/base/node/pfs';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
|
||||
const metaData = path.join(FileAccess.asFileUri('').fsPath, 'nls.metadata.json');
|
||||
const _cache: Map<string, Promise<lp.NLSConfiguration>> = new Map();
|
||||
const nlsMetadataPath = join(FileAccess.asFileUri('').fsPath);
|
||||
const defaultMessagesFile = join(nlsMetadataPath, 'nls.messages.json');
|
||||
const nlsConfigurationCache = new Map<string, Promise<INLSConfiguration>>();
|
||||
|
||||
function exists(file: string) {
|
||||
return new Promise(c => fs.exists(file, c));
|
||||
}
|
||||
export async function getNLSConfiguration(language: string, userDataPath: string): Promise<INLSConfiguration> {
|
||||
if (!product.commit || !(await Promises.exists(defaultMessagesFile))) {
|
||||
return {
|
||||
userLocale: 'en',
|
||||
osLocale: 'en',
|
||||
resolvedLanguage: 'en',
|
||||
defaultMessagesFile,
|
||||
|
||||
export function getNLSConfiguration(language: string, userDataPath: string): Promise<lp.NLSConfiguration> {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
export namespace InternalNLSConfiguration {
|
||||
export function is(value: lp.NLSConfiguration): value is lp.InternalNLSConfiguration {
|
||||
const candidate: lp.InternalNLSConfiguration = value as lp.InternalNLSConfiguration;
|
||||
return candidate && typeof candidate._languagePackId === 'string';
|
||||
// NLS: below 2 are a relic from old times only used by vscode-nls and deprecated
|
||||
locale: 'en',
|
||||
availableLanguages: {}
|
||||
};
|
||||
}
|
||||
|
||||
const cacheKey = `${language}||${userDataPath}`;
|
||||
let result = nlsConfigurationCache.get(cacheKey);
|
||||
if (!result) {
|
||||
result = resolveNLSConfiguration({ userLocale: language, osLocale: language, commit: product.commit, userDataPath, nlsMetadataPath });
|
||||
nlsConfigurationCache.set(cacheKey, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -338,12 +338,23 @@ export class WebClientServer {
|
|||
callbackRoute: this._callbackRoute
|
||||
};
|
||||
|
||||
const nlsBaseUrl = this._productService.extensionsGallery?.nlsBaseUrl;
|
||||
const cookies = cookie.parse(req.headers.cookie || '');
|
||||
const locale = 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')) {
|
||||
WORKBENCH_NLS_BASE_URL = `https://www.vscode-unpkg.net/nls/`;
|
||||
WORKBENCH_NLS_URL = `${WORKBENCH_NLS_BASE_URL}${this._productService.commit}/${this._productService.version}/${locale}/nls.messages.js`; // TODO@bpasero make it a product.json thing
|
||||
} else {
|
||||
WORKBENCH_NLS_URL = ''; // fallback will apply
|
||||
}
|
||||
|
||||
const values: { [key: string]: string } = {
|
||||
WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration),
|
||||
WORKBENCH_AUTH_SESSION: authSessionInfo ? asJSON(authSessionInfo) : '',
|
||||
WORKBENCH_WEB_BASE_URL: this._staticRoute,
|
||||
WORKBENCH_NLS_BASE_URL: nlsBaseUrl ? `${nlsBaseUrl}${!nlsBaseUrl.endsWith('/') ? '/' : ''}${this._productService.commit}/${this._productService.version}/` : '',
|
||||
WORKBENCH_NLS_URL,
|
||||
WORKBENCH_NLS_FALLBACK_URL: `${this._staticRoute}/out/nls.messages.js`
|
||||
};
|
||||
|
||||
if (useTestResolver) {
|
||||
|
@ -364,13 +375,13 @@ export class WebClientServer {
|
|||
return void res.end('Not found');
|
||||
}
|
||||
|
||||
const webWorkerExtensionHostIframeScriptSHA = 'sha256-75NYUUvf+5++1WbfCZOV3PSWxBhONpaxwx+mkOFRv/Y=';
|
||||
const webWorkerExtensionHostIframeScriptSHA = 'sha256-V28GQnL3aYxbwgpV3yW1oJ+VKKe/PBSzWntNyH8zVXA=';
|
||||
|
||||
const cspDirectives = [
|
||||
'default-src \'self\';',
|
||||
'img-src \'self\' https: data: blob:;',
|
||||
'media-src \'self\';',
|
||||
`script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} '${webWorkerExtensionHostIframeScriptSHA}' ${useTestResolver ? '' : `http://${remoteAuthority}`};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html
|
||||
`script-src 'self' 'unsafe-eval' ${WORKBENCH_NLS_BASE_URL ?? ''} ${this._getScriptCspHashes(data).join(' ')} '${webWorkerExtensionHostIframeScriptSHA}' ${useTestResolver ? '' : `http://${remoteAuthority}`};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html
|
||||
'child-src \'self\';',
|
||||
`frame-src 'self' https://*.vscode-cdn.net data:;`,
|
||||
'worker-src \'self\' data: blob:;',
|
||||
|
|
|
@ -181,6 +181,23 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
err.stack = stack;
|
||||
return rejectBarrier(ExtensionHostExitCode.UnexpectedError, err);
|
||||
}
|
||||
if (event.data.type === 'vscode.bootstrap.nls') {
|
||||
const factoryModuleId = 'vs/base/worker/workerMain.js';
|
||||
const baseUrl = require.toUrl(factoryModuleId).slice(0, -factoryModuleId.length);
|
||||
iframe.contentWindow!.postMessage({
|
||||
type: event.data.type,
|
||||
data: {
|
||||
baseUrl,
|
||||
workerUrl: require.toUrl(factoryModuleId),
|
||||
nls: {
|
||||
// VSCODE_GLOBALS: NLS
|
||||
messages: globalThis._VSCODE_NLS_MESSAGES,
|
||||
language: globalThis._VSCODE_NLS_LANGUAGE
|
||||
}
|
||||
}
|
||||
}, '*');
|
||||
return;
|
||||
}
|
||||
const { data } = event.data;
|
||||
if (barrier.isOpen() || !(data instanceof MessagePort)) {
|
||||
console.warn('UNEXPECTED message', event);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<meta http-equiv="Content-Security-Policy" content="
|
||||
default-src 'none';
|
||||
child-src 'self' data: blob:;
|
||||
script-src 'self' 'unsafe-eval' 'sha256-75NYUUvf+5++1WbfCZOV3PSWxBhONpaxwx+mkOFRv/Y=' https:;
|
||||
script-src 'self' 'unsafe-eval' 'sha256-V28GQnL3aYxbwgpV3yW1oJ+VKKe/PBSzWntNyH8zVXA=' https:;
|
||||
connect-src 'self' https: wss: http://localhost:* http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:*;"/>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -66,13 +66,44 @@
|
|||
}
|
||||
|
||||
function start() {
|
||||
|
||||
// Before we can load the worker, we need to get the current set of NLS
|
||||
// configuration into this iframe. We ask the parent window to send it
|
||||
// together with the necessary information to load the worker via Blob.
|
||||
|
||||
const bootstrapNlsType = 'vscode.bootstrap.nls';
|
||||
|
||||
self.onmessage = (event) => {
|
||||
if (event.origin !== parentOrigin || event.data.type !== bootstrapNlsType) {
|
||||
return;
|
||||
}
|
||||
const { data } = event.data;
|
||||
createWorker(data.baseUrl, data.workerUrl, data.nls.messages, data.nls.language);
|
||||
};
|
||||
|
||||
window.parent.postMessage({
|
||||
vscodeWebWorkerExtHostId,
|
||||
type: bootstrapNlsType
|
||||
}, '*');
|
||||
}
|
||||
|
||||
function createWorker(baseUrl, workerUrl, nlsMessages, nlsLanguage) {
|
||||
try {
|
||||
let workerUrl = '../../../../base/worker/workerMain.js';
|
||||
if (globalThis.crossOriginIsolated) {
|
||||
workerUrl += '?vscode-coi=2'; // COEP
|
||||
}
|
||||
|
||||
const worker = new Worker(workerUrl, { name });
|
||||
const blob = new Blob([[
|
||||
`/*extensionHostWorker*/`,
|
||||
`globalThis.MonacoEnvironment = { baseUrl: '${baseUrl}' };`,
|
||||
// VSCODE_GLOBALS: NLS
|
||||
`globalThis._VSCODE_NLS_MESSAGES = ${JSON.stringify(nlsMessages)};`,
|
||||
`globalThis._VSCODE_NLS_LANGUAGE = ${JSON.stringify(nlsLanguage)};`,
|
||||
`importScripts('${workerUrl}');`,
|
||||
`/*extensionHostWorker*/`
|
||||
].join('')], { type: 'application/javascript' });
|
||||
|
||||
const worker = new Worker(URL.createObjectURL(blob), { name });
|
||||
worker.postMessage('vs/workbench/api/worker/extensionHostWorker');
|
||||
const nestedWorkers = new Map();
|
||||
|
||||
|
|
|
@ -14,11 +14,69 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/
|
|||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { getCookieValue } from 'vs/base/browser/dom';
|
||||
|
||||
const localeStorage = new class LocaleStorage {
|
||||
|
||||
private static readonly LOCAL_STORAGE_LOCALE_KEY = 'vscode.nls.locale';
|
||||
private static readonly LOCAL_STORAGE_EXTENSION_ID_KEY = 'vscode.nls.languagePackExtensionId';
|
||||
|
||||
constructor() {
|
||||
this.migrateCookie(); // TODO@bpasero remove me eventually
|
||||
}
|
||||
|
||||
private migrateCookie(): void {
|
||||
const localeCookieValue = getCookieValue(LocaleStorage.LOCAL_STORAGE_LOCALE_KEY);
|
||||
const localeStorageValue = localStorage.getItem(LocaleStorage.LOCAL_STORAGE_LOCALE_KEY);
|
||||
|
||||
if (
|
||||
(typeof localeCookieValue !== 'string' && typeof localeStorageValue !== 'string') ||
|
||||
(localeCookieValue === localeStorageValue)
|
||||
) {
|
||||
return; // already matching
|
||||
}
|
||||
|
||||
if (typeof localeStorageValue === 'string') {
|
||||
this.doSetLocaleToCookie(localeStorageValue);
|
||||
} else {
|
||||
this.doClearLocaleToCookie();
|
||||
}
|
||||
}
|
||||
|
||||
setLocale(locale: string): void {
|
||||
localStorage.setItem(LocaleStorage.LOCAL_STORAGE_LOCALE_KEY, locale);
|
||||
this.doSetLocaleToCookie(locale);
|
||||
}
|
||||
|
||||
private doSetLocaleToCookie(locale: string): void {
|
||||
document.cookie = `${LocaleStorage.LOCAL_STORAGE_LOCALE_KEY}=${locale};path=/;max-age=3153600000`;
|
||||
}
|
||||
|
||||
clearLocale(): void {
|
||||
localStorage.removeItem(LocaleStorage.LOCAL_STORAGE_LOCALE_KEY);
|
||||
this.doClearLocaleToCookie();
|
||||
}
|
||||
|
||||
private doClearLocaleToCookie(): void {
|
||||
document.cookie = `${LocaleStorage.LOCAL_STORAGE_LOCALE_KEY}=;path=/;max-age=0`;
|
||||
}
|
||||
|
||||
setExtensionId(extensionId: string): void {
|
||||
localStorage.setItem(LocaleStorage.LOCAL_STORAGE_EXTENSION_ID_KEY, extensionId);
|
||||
}
|
||||
|
||||
getExtensionId(): string | null {
|
||||
return localStorage.getItem(LocaleStorage.LOCAL_STORAGE_EXTENSION_ID_KEY);
|
||||
}
|
||||
|
||||
clearExtensionId(): void {
|
||||
localStorage.removeItem(LocaleStorage.LOCAL_STORAGE_EXTENSION_ID_KEY);
|
||||
}
|
||||
};
|
||||
|
||||
export class WebLocaleService implements ILocaleService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
static readonly _LOCAL_STORAGE_EXTENSION_ID_KEY = 'vscode.nls.languagePackExtensionId';
|
||||
static readonly _LOCAL_STORAGE_LOCALE_KEY = 'vscode.nls.locale';
|
||||
|
||||
constructor(
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
|
@ -32,13 +90,13 @@ export class WebLocaleService implements ILocaleService {
|
|||
return;
|
||||
}
|
||||
if (locale) {
|
||||
localStorage.setItem(WebLocaleService._LOCAL_STORAGE_LOCALE_KEY, locale);
|
||||
localeStorage.setLocale(locale);
|
||||
if (languagePackItem.extensionId) {
|
||||
localStorage.setItem(WebLocaleService._LOCAL_STORAGE_EXTENSION_ID_KEY, languagePackItem.extensionId);
|
||||
localeStorage.setExtensionId(languagePackItem.extensionId);
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem(WebLocaleService._LOCAL_STORAGE_LOCALE_KEY);
|
||||
localStorage.removeItem(WebLocaleService._LOCAL_STORAGE_EXTENSION_ID_KEY);
|
||||
localeStorage.clearLocale();
|
||||
localeStorage.clearExtensionId();
|
||||
}
|
||||
|
||||
const restartDialog = await this.dialogService.confirm({
|
||||
|
@ -54,8 +112,8 @@ export class WebLocaleService implements ILocaleService {
|
|||
}
|
||||
|
||||
async clearLocalePreference(): Promise<void> {
|
||||
localStorage.removeItem(WebLocaleService._LOCAL_STORAGE_LOCALE_KEY);
|
||||
localStorage.removeItem(WebLocaleService._LOCAL_STORAGE_EXTENSION_ID_KEY);
|
||||
localeStorage.clearLocale();
|
||||
localeStorage.clearExtensionId();
|
||||
|
||||
if (Language.value() === navigator.language.toLowerCase()) {
|
||||
return;
|
||||
|
@ -87,7 +145,7 @@ class WebActiveLanguagePackService implements IActiveLanguagePackService {
|
|||
if (language === LANGUAGE_DEFAULT) {
|
||||
return undefined;
|
||||
}
|
||||
const extensionId = localStorage.getItem(WebLocaleService._LOCAL_STORAGE_EXTENSION_ID_KEY);
|
||||
const extensionId = localeStorage.getExtensionId();
|
||||
if (extensionId) {
|
||||
return extensionId;
|
||||
}
|
||||
|
@ -102,7 +160,7 @@ class WebActiveLanguagePackService implements IActiveLanguagePackService {
|
|||
// Only install extensions that are published by Microsoft and start with vscode-language-pack for extra certainty
|
||||
const extensionToInstall = tagResult.firstPage.find(e => e.publisher === 'MS-CEINTL' && e.name.startsWith('vscode-language-pack'));
|
||||
if (extensionToInstall) {
|
||||
localStorage.setItem(WebLocaleService._LOCAL_STORAGE_EXTENSION_ID_KEY, extensionToInstall.identifier.id);
|
||||
localeStorage.setExtensionId(extensionToInstall.identifier.id);
|
||||
return extensionToInstall.identifier.id;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,10 @@ const TestNativeWindowConfiguration: INativeWindowConfiguration = {
|
|||
tmpDir: tmpDir.fsPath,
|
||||
userDataDir: joinPath(homeDir, product.nameShort).fsPath,
|
||||
profiles: { profile: NULL_PROFILE, all: [NULL_PROFILE], home: homeDir },
|
||||
nls: {
|
||||
messages: [],
|
||||
language: 'en'
|
||||
},
|
||||
_: []
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// NOTE: THIS FILE WILL BE OVERWRITTEN DURING BUILD TIME, DO NOT EDIT
|
||||
|
||||
define([], {});
|
|
@ -156,7 +156,6 @@ import 'vs/workbench/contrib/tags/browser/workspaceTagsService';
|
|||
// Issues
|
||||
import 'vs/workbench/contrib/issue/browser/issue.contribution';
|
||||
|
||||
|
||||
// Splash
|
||||
import 'vs/workbench/contrib/splash/browser/splash.contribution';
|
||||
|
||||
|
|
|
@ -246,6 +246,18 @@ async function runTestsInBrowser(testModules, browserType) {
|
|||
|
||||
await page.goto(target.href);
|
||||
|
||||
if (args.build) {
|
||||
const nlsMessages = await fs.promises.readFile(path.join(out, 'nls.messages.json'), 'utf8');
|
||||
await page.evaluate(value => {
|
||||
// when running from `out-build`, ensure to load the default
|
||||
// messages file, because all `nls.localize` calls have their
|
||||
// english values removed and replaced by an index.
|
||||
// VSCODE_GLOBALS: NLS
|
||||
// @ts-ignore
|
||||
globalThis._VSCODE_NLS_MESSAGES = JSON.parse(value);
|
||||
}, nlsMessages);
|
||||
}
|
||||
|
||||
page.on('console', async msg => {
|
||||
consoleLogFn(msg)(msg.text(), await Promise.all(msg.args().map(async arg => await arg.jsonValue())));
|
||||
});
|
||||
|
|
|
@ -97,6 +97,16 @@ const _tests_glob = '**/test/**/*.test.js';
|
|||
let loader;
|
||||
let _out;
|
||||
|
||||
function initNls(opts) {
|
||||
if (opts.build) {
|
||||
// when running from `out-build`, ensure to load the default
|
||||
// messages file, because all `nls.localize` calls have their
|
||||
// english values removed and replaced by an index.
|
||||
// VSCODE_GLOBALS: NLS
|
||||
globalThis._VSCODE_NLS_MESSAGES = (require.__$__nodeRequire ?? require)(`../../../out-build/nls.messages.json`);
|
||||
}
|
||||
}
|
||||
|
||||
function initLoader(opts) {
|
||||
const outdir = opts.build ? 'out-build' : 'out';
|
||||
_out = path.join(__dirname, `../../../${outdir}`);
|
||||
|
@ -438,6 +448,7 @@ function runTests(opts) {
|
|||
}
|
||||
|
||||
ipcRenderer.on('run', (e, opts) => {
|
||||
initNls(opts);
|
||||
initLoader(opts);
|
||||
runTests(opts).catch(err => {
|
||||
if (typeof err !== 'string') {
|
||||
|
|
|
@ -84,6 +84,14 @@ function main() {
|
|||
globalThis._VSCODE_PRODUCT_JSON = require(`${REPO_ROOT}/product.json`);
|
||||
globalThis._VSCODE_PACKAGE_JSON = require(`${REPO_ROOT}/package.json`);
|
||||
|
||||
if (args.build) {
|
||||
// when running from `out-build`, ensure to load the default
|
||||
// messages file, because all `nls.localize` calls have their
|
||||
// english values removed and replaced by an index.
|
||||
// VSCODE_GLOBALS: NLS
|
||||
globalThis._VSCODE_NLS_MESSAGES = require(`../../../${out}/nls.messages.json`);
|
||||
}
|
||||
|
||||
// Test file operations that are common across platforms. Used for test infra, namely snapshot tests
|
||||
Object.assign(globalThis, {
|
||||
__analyzeSnapshotInTests: takeSnapshotAndCountClasses,
|
||||
|
|
Loading…
Reference in New Issue
Block a user