Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/node/nls.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { join } from '../common/path.js';
7
import { promises } from 'fs';
8
import { mark } from '../common/performance.js';
9
import { ILanguagePacks, INLSConfiguration } from '../../nls.js';
10
import { Promises } from './pfs.js';
11
12
export interface IResolveNLSConfigurationContext {
13
14
/**
15
* Location where `nls.messages.json` and `nls.keys.json` are stored.
16
*/
17
readonly nlsMetadataPath: string;
18
19
/**
20
* Path to the user data directory. Used as a cache for
21
* language packs converted to the format we need.
22
*/
23
readonly userDataPath: string;
24
25
/**
26
* Commit of the running application. Can be `undefined`
27
* when not built.
28
*/
29
readonly commit: string | undefined;
30
31
/**
32
* Locale as defined in `argv.json` or `app.getLocale()`.
33
*/
34
readonly userLocale: string;
35
36
/**
37
* Locale as defined by the OS (e.g. `app.getPreferredSystemLanguages()`).
38
*/
39
readonly osLocale: string;
40
}
41
42
export async function resolveNLSConfiguration({ userLocale, osLocale, userDataPath, commit, nlsMetadataPath }: IResolveNLSConfigurationContext): Promise<INLSConfiguration> {
43
mark('code/willGenerateNls');
44
45
if (
46
process.env['VSCODE_DEV'] ||
47
userLocale === 'pseudo' ||
48
userLocale.startsWith('en') ||
49
!commit ||
50
!userDataPath
51
) {
52
return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath);
53
}
54
55
try {
56
const languagePacks = await getLanguagePackConfigurations(userDataPath);
57
if (!languagePacks) {
58
return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath);
59
}
60
61
const resolvedLanguage = resolveLanguagePackLanguage(languagePacks, userLocale);
62
if (!resolvedLanguage) {
63
return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath);
64
}
65
66
const languagePack = languagePacks[resolvedLanguage];
67
const mainLanguagePackPath = languagePack?.translations?.['vscode'];
68
if (
69
!languagePack ||
70
typeof languagePack.hash !== 'string' ||
71
!languagePack.translations ||
72
typeof mainLanguagePackPath !== 'string' ||
73
!(await Promises.exists(mainLanguagePackPath))
74
) {
75
return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath);
76
}
77
78
const languagePackId = `${languagePack.hash}.${resolvedLanguage}`;
79
const globalLanguagePackCachePath = join(userDataPath, 'clp', languagePackId);
80
const commitLanguagePackCachePath = join(globalLanguagePackCachePath, commit);
81
const languagePackMessagesFile = join(commitLanguagePackCachePath, 'nls.messages.json');
82
const translationsConfigFile = join(globalLanguagePackCachePath, 'tcf.json');
83
const languagePackCorruptMarkerFile = join(globalLanguagePackCachePath, 'corrupted.info');
84
85
if (await Promises.exists(languagePackCorruptMarkerFile)) {
86
await promises.rm(globalLanguagePackCachePath, { recursive: true, force: true, maxRetries: 3 }); // delete corrupted cache folder
87
}
88
89
const result: INLSConfiguration = {
90
userLocale,
91
osLocale,
92
resolvedLanguage,
93
defaultMessagesFile: join(nlsMetadataPath, 'nls.messages.json'),
94
languagePack: {
95
translationsConfigFile,
96
messagesFile: languagePackMessagesFile,
97
corruptMarkerFile: languagePackCorruptMarkerFile
98
},
99
100
// NLS: below properties are a relic from old times only used by vscode-nls and deprecated
101
locale: userLocale,
102
availableLanguages: { '*': resolvedLanguage },
103
_languagePackId: languagePackId,
104
_languagePackSupport: true,
105
_translationsConfigFile: translationsConfigFile,
106
_cacheRoot: globalLanguagePackCachePath,
107
_resolvedLanguagePackCoreLocation: commitLanguagePackCachePath,
108
_corruptedFile: languagePackCorruptMarkerFile
109
};
110
111
if (await Promises.exists(languagePackMessagesFile)) {
112
touch(commitLanguagePackCachePath).catch(() => { }); // We don't wait for this. No big harm if we can't touch
113
mark('code/didGenerateNls');
114
return result;
115
}
116
117
const [
118
nlsDefaultKeys,
119
nlsDefaultMessages,
120
nlsPackdata
121
]:
122
[Array<[string, string[]]>, string[], { contents: Record<string, Record<string, string>> }]
123
// ^moduleId ^nlsKeys ^moduleId ^nlsKey ^nlsValue
124
= await Promise.all([
125
promises.readFile(join(nlsMetadataPath, 'nls.keys.json'), 'utf-8').then(content => JSON.parse(content)),
126
promises.readFile(join(nlsMetadataPath, 'nls.messages.json'), 'utf-8').then(content => JSON.parse(content)),
127
promises.readFile(mainLanguagePackPath, 'utf-8').then(content => JSON.parse(content)),
128
]);
129
130
const nlsResult: string[] = [];
131
132
// We expect NLS messages to be in a flat array in sorted order as they
133
// where produced during build time. We use `nls.keys.json` to know the
134
// right order and then lookup the related message from the translation.
135
// If a translation does not exist, we fallback to the default message.
136
137
let nlsIndex = 0;
138
for (const [moduleId, nlsKeys] of nlsDefaultKeys) {
139
const moduleTranslations = nlsPackdata.contents[moduleId];
140
for (const nlsKey of nlsKeys) {
141
nlsResult.push(moduleTranslations?.[nlsKey] || nlsDefaultMessages[nlsIndex]);
142
nlsIndex++;
143
}
144
}
145
146
await promises.mkdir(commitLanguagePackCachePath, { recursive: true });
147
148
await Promise.all([
149
promises.writeFile(languagePackMessagesFile, JSON.stringify(nlsResult), 'utf-8'),
150
promises.writeFile(translationsConfigFile, JSON.stringify(languagePack.translations), 'utf-8')
151
]);
152
153
mark('code/didGenerateNls');
154
155
return result;
156
} catch (error) {
157
console.error('Generating translation files failed.', error);
158
}
159
160
return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath);
161
}
162
163
/**
164
* The `languagepacks.json` file is a JSON file that contains all metadata
165
* about installed language extensions per language. Specifically, for
166
* core (`vscode`) and all extensions it supports, it points to the related
167
* translation files.
168
*
169
* The file is updated whenever a new language pack is installed or removed.
170
*/
171
async function getLanguagePackConfigurations(userDataPath: string): Promise<ILanguagePacks | undefined> {
172
const configFile = join(userDataPath, 'languagepacks.json');
173
try {
174
return JSON.parse(await promises.readFile(configFile, 'utf-8'));
175
} catch (err) {
176
return undefined; // Do nothing. If we can't read the file we have no language pack config.
177
}
178
}
179
180
function resolveLanguagePackLanguage(languagePacks: ILanguagePacks, locale: string | undefined): string | undefined {
181
try {
182
while (locale) {
183
if (languagePacks[locale]) {
184
return locale;
185
}
186
187
const index = locale.lastIndexOf('-');
188
if (index > 0) {
189
locale = locale.substring(0, index);
190
} else {
191
return undefined;
192
}
193
}
194
} catch (error) {
195
console.error('Resolving language pack configuration failed.', error);
196
}
197
198
return undefined;
199
}
200
201
function defaultNLSConfiguration(userLocale: string, osLocale: string, nlsMetadataPath: string): INLSConfiguration {
202
mark('code/didGenerateNls');
203
204
return {
205
userLocale,
206
osLocale,
207
resolvedLanguage: 'en',
208
defaultMessagesFile: join(nlsMetadataPath, 'nls.messages.json'),
209
210
// NLS: below 2 are a relic from old times only used by vscode-nls and deprecated
211
locale: userLocale,
212
availableLanguages: {}
213
};
214
}
215
216
//#region fs helpers
217
218
function touch(path: string): Promise<void> {
219
const date = new Date();
220
221
return promises.utimes(path, date, date);
222
}
223
224
//#endregion
225
226