main.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. "use strict";
  2. /* --------------------------------------------------------------------------------------------
  3. * Copyright (c) Microsoft Corporation. All rights reserved.
  4. * Licensed under the MIT License. See License.txt in the project root for license information.
  5. * ------------------------------------------------------------------------------------------ */
  6. Object.defineProperty(exports, "__esModule", { value: true });
  7. exports.config = exports.loadMessageBundle = void 0;
  8. var path = require("path");
  9. var fs = require("fs");
  10. var ral_1 = require("../common/ral");
  11. var common_1 = require("../common/common");
  12. var common_2 = require("../common/common");
  13. Object.defineProperty(exports, "MessageFormat", { enumerable: true, get: function () { return common_2.MessageFormat; } });
  14. Object.defineProperty(exports, "BundleFormat", { enumerable: true, get: function () { return common_2.BundleFormat; } });
  15. var toString = Object.prototype.toString;
  16. function isNumber(value) {
  17. return toString.call(value) === '[object Number]';
  18. }
  19. function isString(value) {
  20. return toString.call(value) === '[object String]';
  21. }
  22. function isBoolean(value) {
  23. return value === true || value === false;
  24. }
  25. function readJsonFileSync(filename) {
  26. return JSON.parse(fs.readFileSync(filename, 'utf8'));
  27. }
  28. var resolvedBundles;
  29. var options;
  30. function initializeSettings() {
  31. options = { locale: undefined, language: undefined, languagePackSupport: false, cacheLanguageResolution: true, messageFormat: common_1.MessageFormat.bundle };
  32. if (isString(process.env.VSCODE_NLS_CONFIG)) {
  33. try {
  34. var vscodeOptions_1 = JSON.parse(process.env.VSCODE_NLS_CONFIG);
  35. var language = void 0;
  36. if (vscodeOptions_1.availableLanguages) {
  37. var value = vscodeOptions_1.availableLanguages['*'];
  38. if (isString(value)) {
  39. language = value;
  40. }
  41. }
  42. if (isString(vscodeOptions_1.locale)) {
  43. options.locale = vscodeOptions_1.locale.toLowerCase();
  44. }
  45. if (language === undefined) {
  46. options.language = options.locale;
  47. }
  48. else if (language !== 'en') {
  49. options.language = language;
  50. }
  51. if (isBoolean(vscodeOptions_1._languagePackSupport)) {
  52. options.languagePackSupport = vscodeOptions_1._languagePackSupport;
  53. }
  54. if (isString(vscodeOptions_1._cacheRoot)) {
  55. options.cacheRoot = vscodeOptions_1._cacheRoot;
  56. }
  57. if (isString(vscodeOptions_1._languagePackId)) {
  58. options.languagePackId = vscodeOptions_1._languagePackId;
  59. }
  60. if (isString(vscodeOptions_1._translationsConfigFile)) {
  61. options.translationsConfigFile = vscodeOptions_1._translationsConfigFile;
  62. try {
  63. options.translationsConfig = readJsonFileSync(options.translationsConfigFile);
  64. }
  65. catch (error) {
  66. // We can't read the translation config file. Mark the cache as corrupted.
  67. if (vscodeOptions_1._corruptedFile) {
  68. var dirname = path.dirname(vscodeOptions_1._corruptedFile);
  69. fs.exists(dirname, function (exists) {
  70. if (exists) {
  71. fs.writeFile(vscodeOptions_1._corruptedFile, 'corrupted', 'utf8', function (err) {
  72. console.error(err);
  73. });
  74. }
  75. });
  76. }
  77. }
  78. }
  79. }
  80. catch (_a) {
  81. // Do nothing.
  82. }
  83. }
  84. common_1.setPseudo(options.locale === 'pseudo');
  85. resolvedBundles = Object.create(null);
  86. }
  87. initializeSettings();
  88. function supportsLanguagePack() {
  89. return options.languagePackSupport === true && options.cacheRoot !== undefined && options.languagePackId !== undefined && options.translationsConfigFile !== undefined
  90. && options.translationsConfig !== undefined;
  91. }
  92. function createScopedLocalizeFunction(messages) {
  93. return function (key, message) {
  94. var args = [];
  95. for (var _i = 2; _i < arguments.length; _i++) {
  96. args[_i - 2] = arguments[_i];
  97. }
  98. if (isNumber(key)) {
  99. if (key >= messages.length) {
  100. console.error("Broken localize call found. Index out of bounds. Stacktrace is\n: " + new Error('').stack);
  101. return;
  102. }
  103. return common_1.format(messages[key], args);
  104. }
  105. else {
  106. if (isString(message)) {
  107. console.warn("Message " + message + " didn't get externalized correctly.");
  108. return common_1.format(message, args);
  109. }
  110. else {
  111. console.error("Broken localize call found. Stacktrace is\n: " + new Error('').stack);
  112. }
  113. }
  114. };
  115. }
  116. function resolveLanguage(file) {
  117. var resolvedLanguage;
  118. if (options.cacheLanguageResolution && resolvedLanguage) {
  119. resolvedLanguage = resolvedLanguage;
  120. }
  121. else {
  122. if (common_1.isPseudo || !options.language) {
  123. resolvedLanguage = '.nls.json';
  124. }
  125. else {
  126. var locale = options.language;
  127. while (locale) {
  128. var candidate = '.nls.' + locale + '.json';
  129. if (fs.existsSync(file + candidate)) {
  130. resolvedLanguage = candidate;
  131. break;
  132. }
  133. else {
  134. var index = locale.lastIndexOf('-');
  135. if (index > 0) {
  136. locale = locale.substring(0, index);
  137. }
  138. else {
  139. resolvedLanguage = '.nls.json';
  140. locale = null;
  141. }
  142. }
  143. }
  144. }
  145. if (options.cacheLanguageResolution) {
  146. resolvedLanguage = resolvedLanguage;
  147. }
  148. }
  149. return file + resolvedLanguage;
  150. }
  151. function findInTheBoxBundle(root) {
  152. var language = options.language;
  153. while (language) {
  154. var candidate = path.join(root, "nls.bundle." + language + ".json");
  155. if (fs.existsSync(candidate)) {
  156. return candidate;
  157. }
  158. else {
  159. var index = language.lastIndexOf('-');
  160. if (index > 0) {
  161. language = language.substring(0, index);
  162. }
  163. else {
  164. language = undefined;
  165. }
  166. }
  167. }
  168. // Test if we can reslove the default bundle.
  169. if (language === undefined) {
  170. var candidate = path.join(root, 'nls.bundle.json');
  171. if (fs.existsSync(candidate)) {
  172. return candidate;
  173. }
  174. }
  175. return undefined;
  176. }
  177. function mkdir(directory) {
  178. try {
  179. fs.mkdirSync(directory);
  180. }
  181. catch (err) {
  182. if (err.code === 'EEXIST') {
  183. return;
  184. }
  185. else if (err.code === 'ENOENT') {
  186. var parent = path.dirname(directory);
  187. if (parent !== directory) {
  188. mkdir(parent);
  189. fs.mkdirSync(directory);
  190. }
  191. }
  192. else {
  193. throw err;
  194. }
  195. }
  196. }
  197. function createDefaultNlsBundle(folder) {
  198. var metaData = readJsonFileSync(path.join(folder, 'nls.metadata.json'));
  199. var result = Object.create(null);
  200. for (var module_1 in metaData) {
  201. var entry = metaData[module_1];
  202. result[module_1] = entry.messages;
  203. }
  204. return result;
  205. }
  206. function createNLSBundle(header, metaDataPath) {
  207. var languagePackLocation = options.translationsConfig[header.id];
  208. if (!languagePackLocation) {
  209. return undefined;
  210. }
  211. var languagePack = readJsonFileSync(languagePackLocation).contents;
  212. var metaData = readJsonFileSync(path.join(metaDataPath, 'nls.metadata.json'));
  213. var result = Object.create(null);
  214. for (var module_2 in metaData) {
  215. var entry = metaData[module_2];
  216. var translations = languagePack[header.outDir + "/" + module_2];
  217. if (translations) {
  218. var resultMessages = [];
  219. for (var i = 0; i < entry.keys.length; i++) {
  220. var messageKey = entry.keys[i];
  221. var key = isString(messageKey) ? messageKey : messageKey.key;
  222. var translatedMessage = translations[key];
  223. if (translatedMessage === undefined) {
  224. translatedMessage = entry.messages[i];
  225. }
  226. resultMessages.push(translatedMessage);
  227. }
  228. result[module_2] = resultMessages;
  229. }
  230. else {
  231. result[module_2] = entry.messages;
  232. }
  233. }
  234. return result;
  235. }
  236. function touch(file) {
  237. var d = new Date();
  238. fs.utimes(file, d, d, function () {
  239. // Do nothing. Ignore
  240. });
  241. }
  242. function cacheBundle(key, bundle) {
  243. resolvedBundles[key] = bundle;
  244. return bundle;
  245. }
  246. function loadNlsBundleOrCreateFromI18n(header, bundlePath) {
  247. var result;
  248. var bundle = path.join(options.cacheRoot, header.id + "-" + header.hash + ".json");
  249. var useMemoryOnly = false;
  250. var writeBundle = false;
  251. try {
  252. result = JSON.parse(fs.readFileSync(bundle, { encoding: 'utf8', flag: 'r' }));
  253. touch(bundle);
  254. return result;
  255. }
  256. catch (err) {
  257. if (err.code === 'ENOENT') {
  258. writeBundle = true;
  259. }
  260. else if (err instanceof SyntaxError) {
  261. // We have a syntax error. So no valid JSON. Use
  262. console.log("Syntax error parsing message bundle: " + err.message + ".");
  263. fs.unlink(bundle, function (err) {
  264. if (err) {
  265. console.error("Deleting corrupted bundle " + bundle + " failed.");
  266. }
  267. });
  268. useMemoryOnly = true;
  269. }
  270. else {
  271. throw err;
  272. }
  273. }
  274. result = createNLSBundle(header, bundlePath);
  275. if (!result || useMemoryOnly) {
  276. return result;
  277. }
  278. if (writeBundle) {
  279. try {
  280. fs.writeFileSync(bundle, JSON.stringify(result), { encoding: 'utf8', flag: 'wx' });
  281. }
  282. catch (err) {
  283. if (err.code === 'EEXIST') {
  284. return result;
  285. }
  286. throw err;
  287. }
  288. }
  289. return result;
  290. }
  291. function loadDefaultNlsBundle(bundlePath) {
  292. try {
  293. return createDefaultNlsBundle(bundlePath);
  294. }
  295. catch (err) {
  296. console.log("Generating default bundle from meta data failed.", err);
  297. return undefined;
  298. }
  299. }
  300. function loadNlsBundle(header, bundlePath) {
  301. var result;
  302. // Core decided to use a language pack. Do the same in the extension
  303. if (supportsLanguagePack()) {
  304. try {
  305. result = loadNlsBundleOrCreateFromI18n(header, bundlePath);
  306. }
  307. catch (err) {
  308. console.log("Load or create bundle failed ", err);
  309. }
  310. }
  311. if (!result) {
  312. // No language pack found, but core is running in language pack mode
  313. // Don't try to use old in the box bundles since the might be stale
  314. // Fall right back to the default bundle.
  315. if (options.languagePackSupport) {
  316. return loadDefaultNlsBundle(bundlePath);
  317. }
  318. var candidate = findInTheBoxBundle(bundlePath);
  319. if (candidate) {
  320. try {
  321. return readJsonFileSync(candidate);
  322. }
  323. catch (err) {
  324. console.log("Loading in the box message bundle failed.", err);
  325. }
  326. }
  327. result = loadDefaultNlsBundle(bundlePath);
  328. }
  329. return result;
  330. }
  331. function tryFindMetaDataHeaderFile(file) {
  332. var result;
  333. var dirname = path.dirname(file);
  334. while (true) {
  335. result = path.join(dirname, 'nls.metadata.header.json');
  336. if (fs.existsSync(result)) {
  337. break;
  338. }
  339. var parent = path.dirname(dirname);
  340. if (parent === dirname) {
  341. result = undefined;
  342. break;
  343. }
  344. else {
  345. dirname = parent;
  346. }
  347. }
  348. return result;
  349. }
  350. function loadMessageBundle(file) {
  351. if (!file) {
  352. // No file. We are in dev mode. Return the default
  353. // localize function.
  354. return common_1.localize;
  355. }
  356. // Remove extension since we load json files.
  357. var ext = path.extname(file);
  358. if (ext) {
  359. file = file.substr(0, file.length - ext.length);
  360. }
  361. if (options.messageFormat === common_1.MessageFormat.both || options.messageFormat === common_1.MessageFormat.bundle) {
  362. var headerFile = tryFindMetaDataHeaderFile(file);
  363. if (headerFile) {
  364. var bundlePath = path.dirname(headerFile);
  365. var bundle = resolvedBundles[bundlePath];
  366. if (bundle === undefined) {
  367. try {
  368. var header = JSON.parse(fs.readFileSync(headerFile, 'utf8'));
  369. try {
  370. var nlsBundle = loadNlsBundle(header, bundlePath);
  371. bundle = cacheBundle(bundlePath, nlsBundle ? { header: header, nlsBundle: nlsBundle } : null);
  372. }
  373. catch (err) {
  374. console.error('Failed to load nls bundle', err);
  375. bundle = cacheBundle(bundlePath, null);
  376. }
  377. }
  378. catch (err) {
  379. console.error('Failed to read header file', err);
  380. bundle = cacheBundle(bundlePath, null);
  381. }
  382. }
  383. if (bundle) {
  384. var module_3 = file.substr(bundlePath.length + 1).replace(/\\/g, '/');
  385. var messages = bundle.nlsBundle[module_3];
  386. if (messages === undefined) {
  387. console.error("Messages for file " + file + " not found. See console for details.");
  388. return function () {
  389. return 'Messages not found.';
  390. };
  391. }
  392. return createScopedLocalizeFunction(messages);
  393. }
  394. }
  395. }
  396. if (options.messageFormat === common_1.MessageFormat.both || options.messageFormat === common_1.MessageFormat.file) {
  397. // Try to load a single file bundle
  398. try {
  399. var json = readJsonFileSync(resolveLanguage(file));
  400. if (Array.isArray(json)) {
  401. return createScopedLocalizeFunction(json);
  402. }
  403. else {
  404. if (common_1.isDefined(json.messages) && common_1.isDefined(json.keys)) {
  405. return createScopedLocalizeFunction(json.messages);
  406. }
  407. else {
  408. console.error("String bundle '" + file + "' uses an unsupported format.");
  409. return function () {
  410. return 'File bundle has unsupported format. See console for details';
  411. };
  412. }
  413. }
  414. }
  415. catch (err) {
  416. if (err.code !== 'ENOENT') {
  417. console.error('Failed to load single file bundle', err);
  418. }
  419. }
  420. }
  421. console.error("Failed to load message bundle for file " + file);
  422. return function () {
  423. return 'Failed to load message bundle. See console for details.';
  424. };
  425. }
  426. exports.loadMessageBundle = loadMessageBundle;
  427. function config(opts) {
  428. if (opts) {
  429. if (isString(opts.locale)) {
  430. options.locale = opts.locale.toLowerCase();
  431. options.language = options.locale;
  432. resolvedBundles = Object.create(null);
  433. }
  434. if (opts.messageFormat !== undefined) {
  435. options.messageFormat = opts.messageFormat;
  436. }
  437. if (opts.bundleFormat === common_1.BundleFormat.standalone && options.languagePackSupport === true) {
  438. options.languagePackSupport = false;
  439. }
  440. }
  441. common_1.setPseudo(options.locale === 'pseudo');
  442. return loadMessageBundle;
  443. }
  444. exports.config = config;
  445. ral_1.default.install(Object.freeze({
  446. loadMessageBundle: loadMessageBundle,
  447. config: config
  448. }));
  449. //# sourceMappingURL=main.js.map