kylin-code/build/lib/i18n.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

882 lines
36 KiB
JavaScript
Raw Normal View History

2022-06-14 14:37:10 +08:00
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
2024-04-30 20:57:13 +08:00
exports.prepareIslFiles = exports.prepareI18nPackFiles = exports.createXlfFilesForIsl = exports.createXlfFilesForExtensions = exports.EXTERNAL_EXTENSIONS = exports.createXlfFilesForCoreBundle = exports.getResource = exports.processNlsFiles = exports.XLF = exports.Line = exports.extraLanguages = exports.defaultLanguages = void 0;
2022-06-14 14:37:10 +08:00
const path = require("path");
const fs = require("fs");
const event_stream_1 = require("event-stream");
2024-04-30 20:57:13 +08:00
const jsonMerge = require("gulp-merge-json");
2022-06-14 14:37:10 +08:00
const File = require("vinyl");
const xml2js = require("xml2js");
const gulp = require("gulp");
const fancyLog = require("fancy-log");
const ansiColors = require("ansi-colors");
const iconv = require("@vscode/iconv-lite-umd");
2024-04-30 20:57:13 +08:00
const l10n_dev_1 = require("@vscode/l10n-dev");
2022-06-14 14:37:10 +08:00
function log(message, ...rest) {
fancyLog(ansiColors.green('[i18n]'), message, ...rest);
}
exports.defaultLanguages = [
{ id: 'zh-tw', folderName: 'cht', translationId: 'zh-hant' },
{ id: 'zh-cn', folderName: 'chs', translationId: 'zh-hans' },
{ id: 'ja', folderName: 'jpn' },
{ id: 'ko', folderName: 'kor' },
{ id: 'de', folderName: 'deu' },
{ id: 'fr', folderName: 'fra' },
{ id: 'es', folderName: 'esn' },
{ id: 'ru', folderName: 'rus' },
{ id: 'it', folderName: 'ita' }
];
// languages requested by the community to non-stable builds
exports.extraLanguages = [
{ id: 'pt-br', folderName: 'ptb' },
{ id: 'hu', folderName: 'hun' },
{ id: 'tr', folderName: 'trk' }
];
var LocalizeInfo;
(function (LocalizeInfo) {
function is(value) {
2024-04-30 20:57:13 +08:00
const candidate = value;
return candidate && typeof candidate.key === 'string' && (candidate.comment === undefined || (Array.isArray(candidate.comment) && candidate.comment.every(element => typeof element === 'string')));
2022-06-14 14:37:10 +08:00
}
LocalizeInfo.is = is;
})(LocalizeInfo || (LocalizeInfo = {}));
var BundledFormat;
(function (BundledFormat) {
function is(value) {
2024-04-30 20:57:13 +08:00
if (value === undefined) {
2022-06-14 14:37:10 +08:00
return false;
}
2024-04-30 20:57:13 +08:00
const candidate = value;
const length = Object.keys(value).length;
return length === 3 && !!candidate.keys && !!candidate.messages && !!candidate.bundles;
2022-06-14 14:37:10 +08:00
}
BundledFormat.is = is;
})(BundledFormat || (BundledFormat = {}));
class Line {
2024-04-30 20:57:13 +08:00
buffer = [];
2022-06-14 14:37:10 +08:00
constructor(indent = 0) {
if (indent > 0) {
this.buffer.push(new Array(indent + 1).join(' '));
}
}
append(value) {
this.buffer.push(value);
return this;
}
toString() {
return this.buffer.join('');
}
}
exports.Line = Line;
class TextModel {
2024-04-30 20:57:13 +08:00
_lines;
2022-06-14 14:37:10 +08:00
constructor(contents) {
this._lines = contents.split(/\r\n|\r|\n/);
}
get lines() {
return this._lines;
}
}
class XLF {
2024-04-30 20:57:13 +08:00
project;
buffer;
files;
numberOfMessages;
2022-06-14 14:37:10 +08:00
constructor(project) {
this.project = project;
this.buffer = [];
this.files = Object.create(null);
this.numberOfMessages = 0;
}
toString() {
this.appendHeader();
const files = Object.keys(this.files).sort();
for (const file of files) {
this.appendNewLine(`<file original="${file}" source-language="en" datatype="plaintext"><body>`, 2);
const items = this.files[file].sort((a, b) => {
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
});
for (const item of items) {
this.addStringItem(file, item);
}
this.appendNewLine('</body></file>');
}
this.appendFooter();
return this.buffer.join('\r\n');
}
addFile(original, keys, messages) {
if (keys.length === 0) {
console.log('No keys in ' + original);
return;
}
if (keys.length !== messages.length) {
throw new Error(`Unmatching keys(${keys.length}) and messages(${messages.length}).`);
}
this.numberOfMessages += keys.length;
this.files[original] = [];
2024-04-30 20:57:13 +08:00
const existingKeys = new Set();
2022-06-14 14:37:10 +08:00
for (let i = 0; i < keys.length; i++) {
2024-04-30 20:57:13 +08:00
const key = keys[i];
2022-06-14 14:37:10 +08:00
let realKey;
let comment;
2024-04-30 20:57:13 +08:00
if (typeof key === 'string') {
2022-06-14 14:37:10 +08:00
realKey = key;
comment = undefined;
}
else if (LocalizeInfo.is(key)) {
realKey = key.key;
if (key.comment && key.comment.length > 0) {
comment = key.comment.map(comment => encodeEntities(comment)).join('\r\n');
}
}
if (!realKey || existingKeys.has(realKey)) {
continue;
}
existingKeys.add(realKey);
2024-04-30 20:57:13 +08:00
const message = encodeEntities(messages[i]);
2022-06-14 14:37:10 +08:00
this.files[original].push({ id: realKey, message: message, comment: comment });
}
}
addStringItem(file, item) {
if (!item.id || item.message === undefined || item.message === null) {
throw new Error(`No item ID or value specified: ${JSON.stringify(item)}. File: ${file}`);
}
if (item.message.length === 0) {
log(`Item with id ${item.id} in file ${file} has an empty message.`);
}
this.appendNewLine(`<trans-unit id="${item.id}">`, 4);
this.appendNewLine(`<source xml:lang="en">${item.message}</source>`, 6);
if (item.comment) {
this.appendNewLine(`<note>${item.comment}</note>`, 6);
}
this.appendNewLine('</trans-unit>', 4);
}
appendHeader() {
this.appendNewLine('<?xml version="1.0" encoding="utf-8"?>', 0);
this.appendNewLine('<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">', 0);
}
appendFooter() {
this.appendNewLine('</xliff>', 0);
}
appendNewLine(content, indent) {
2024-04-30 20:57:13 +08:00
const line = new Line(indent);
2022-06-14 14:37:10 +08:00
line.append(content);
this.buffer.push(line.toString());
}
2024-04-30 20:57:13 +08:00
static parse = function (xlfString) {
return new Promise((resolve, reject) => {
const parser = new xml2js.Parser();
const files = [];
parser.parseString(xlfString, function (err, result) {
if (err) {
reject(new Error(`XLF parsing error: Failed to parse XLIFF string. ${err}`));
2022-06-14 14:37:10 +08:00
}
2024-04-30 20:57:13 +08:00
const fileNodes = result['xliff']['file'];
if (!fileNodes) {
reject(new Error(`XLF parsing error: XLIFF file does not contain "xliff" or "file" node(s) required for parsing.`));
2022-06-14 14:37:10 +08:00
}
2024-04-30 20:57:13 +08:00
fileNodes.forEach((file) => {
const name = file.$.original;
if (!name) {
reject(new Error(`XLF parsing error: XLIFF file node does not contain original attribute to determine the original location of the resource file.`));
}
const language = file.$['target-language'];
if (!language) {
reject(new Error(`XLF parsing error: XLIFF file node does not contain target-language attribute to determine translated language.`));
}
const messages = {};
const transUnits = file.body[0]['trans-unit'];
if (transUnits) {
transUnits.forEach((unit) => {
const key = unit.$.id;
if (!unit.target) {
return; // No translation available
}
let val = unit.target[0];
if (typeof val !== 'string') {
// We allow empty source values so support them for translations as well.
val = val._ ? val._ : '';
}
if (!key) {
reject(new Error(`XLF parsing error: trans-unit ${JSON.stringify(unit, undefined, 0)} defined in file ${name} is missing the ID attribute.`));
return;
}
messages[key] = decodeEntities(val);
});
files.push({ messages, name, language: language.toLowerCase() });
}
});
resolve(files);
2022-06-14 14:37:10 +08:00
});
});
2024-04-30 20:57:13 +08:00
};
2022-06-14 14:37:10 +08:00
}
2024-04-30 20:57:13 +08:00
exports.XLF = XLF;
2022-06-14 14:37:10 +08:00
function sortLanguages(languages) {
return languages.sort((a, b) => {
return a.id < b.id ? -1 : (a.id > b.id ? 1 : 0);
});
}
function stripComments(content) {
// Copied from stripComments.js
//
// First group matches a double quoted string
// Second group matches a single quoted string
// Third group matches a multi line comment
// Forth group matches a single line comment
2024-04-30 20:57:13 +08:00
// Fifth group matches a trailing comma
const regexp = /("[^"\\]*(?:\\.[^"\\]*)*")|('[^'\\]*(?:\\.[^'\\]*)*')|(\/\*[^\/\*]*(?:(?:\*|\/)[^\/\*]*)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))|(,\s*[}\]])/g;
const result = content.replace(regexp, (match, _m1, _m2, m3, m4, m5) => {
// Only one of m1, m2, m3, m4, m5 matches
2022-06-14 14:37:10 +08:00
if (m3) {
// A block comment. Replace with nothing
return '';
}
else if (m4) {
// Since m4 is a single line comment is is at least of length 2 (e.g. //)
// If it ends in \r?\n then keep it.
const length = m4.length;
if (m4[length - 1] === '\n') {
return m4[length - 2] === '\r' ? '\r\n' : '\n';
}
else {
return '';
}
}
2024-04-30 20:57:13 +08:00
else if (m5) {
// Remove the trailing comma
return match.substring(1);
}
2022-06-14 14:37:10 +08:00
else {
// We match a string
return match;
}
});
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) {
2024-04-30 20:57:13 +08:00
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);
2022-06-14 14:37:10 +08:00
modules.forEach((module) => {
2024-04-30 20:57:13 +08:00
const keys = keysSection[module];
const messages = messageSection[module];
2022-06-14 14:37:10 +08:00
if (!messages || keys.length !== messages.length) {
emitter.emit('error', `Message for module ${module} corrupted. Mismatch in number of keys and messages.`);
return;
}
2024-04-30 20:57:13 +08:00
const messageMap = Object.create(null);
2022-06-14 14:37:10 +08:00
defaultMessages[module] = messageMap;
keys.map((key, i) => {
if (typeof key === 'string') {
messageMap[key] = messages[i];
}
else {
messageMap[key.key] = messages[i];
}
});
});
2024-04-30 20:57:13 +08:00
const languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n');
2022-06-14 14:37:10 +08:00
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.`);
}
2024-04-30 20:57:13 +08:00
const sortedLanguages = sortLanguages(languages);
2022-06-14 14:37:10 +08:00
sortedLanguages.forEach((language) => {
if (process.env['VSCODE_BUILD_VERBOSE']) {
log(`Generating nls bundles for: ${language.id}`);
}
statistics[language.id] = 0;
2024-04-30 20:57:13 +08:00
const localizedModules = Object.create(null);
const languageFolderName = language.translationId || language.id;
const i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json');
2022-06-14 14:37:10 +08:00
let allMessages;
if (fs.existsSync(i18nFile)) {
2024-04-30 20:57:13 +08:00
const content = stripComments(fs.readFileSync(i18nFile, 'utf8'));
2022-06-14 14:37:10 +08:00
allMessages = JSON.parse(content);
}
modules.forEach((module) => {
2024-04-30 20:57:13 +08:00
const order = keysSection[module];
2022-06-14 14:37:10 +08:00
let moduleMessage;
if (allMessages) {
moduleMessage = allMessages.contents[module];
}
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;
}
2024-04-30 20:57:13 +08:00
const localizedMessages = [];
2022-06-14 14:37:10 +08:00
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) => {
2024-04-30 20:57:13 +08:00
const modules = bundleSection[bundle];
const contents = [
2022-06-14 14:37:10 +08:00
fileHeader,
`define("${bundle}.nls.${language.id}", {`
];
modules.forEach((module, index) => {
contents.push(`\t"${module}": [`);
2024-04-30 20:57:13 +08:00
const messages = localizedModules[module];
2022-06-14 14:37:10 +08:00
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 => {
2024-04-30 20:57:13 +08:00
const value = statistics[key];
2022-06-14 14:37:10 +08:00
log(`${key} has ${value} untranslated strings.`);
});
sortedLanguages.forEach(language => {
2024-04-30 20:57:13 +08:00
const stats = statistics[language.id];
if (!stats) {
2022-06-14 14:37:10 +08:00
log(`\tNo translations found for language ${language.id}. Using default language instead.`);
}
});
}
function processNlsFiles(opts) {
return (0, event_stream_1.through)(function (file) {
2024-04-30 20:57:13 +08:00
const fileName = path.basename(file.path);
2022-06-14 14:37:10 +08:00
if (fileName === 'nls.metadata.json') {
let json = null;
if (file.isBuffer()) {
json = JSON.parse(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);
}
}
this.queue(file);
});
}
exports.processNlsFiles = processNlsFiles;
2024-04-30 20:57:13 +08:00
const editorProject = 'vscode-editor', workbenchProject = 'vscode-workbench', extensionsProject = 'vscode-extensions', setupProject = 'vscode-setup', serverProject = 'vscode-server';
2022-06-14 14:37:10 +08:00
function getResource(sourceFile) {
let resource;
if (/^vs\/platform/.test(sourceFile)) {
return { name: 'vs/platform', project: editorProject };
}
else if (/^vs\/editor\/contrib/.test(sourceFile)) {
return { name: 'vs/editor/contrib', project: editorProject };
}
else if (/^vs\/editor/.test(sourceFile)) {
return { name: 'vs/editor', project: editorProject };
}
else if (/^vs\/base/.test(sourceFile)) {
return { name: 'vs/base', project: editorProject };
}
else if (/^vs\/code/.test(sourceFile)) {
return { name: 'vs/code', project: workbenchProject };
}
2024-04-30 20:57:13 +08:00
else if (/^vs\/server/.test(sourceFile)) {
return { name: 'vs/server', project: serverProject };
}
2022-06-14 14:37:10 +08:00
else if (/^vs\/workbench\/contrib/.test(sourceFile)) {
resource = sourceFile.split('/', 4).join('/');
return { name: resource, project: workbenchProject };
}
else if (/^vs\/workbench\/services/.test(sourceFile)) {
resource = sourceFile.split('/', 4).join('/');
return { name: resource, project: workbenchProject };
}
else if (/^vs\/workbench/.test(sourceFile)) {
return { name: 'vs/workbench', project: workbenchProject };
}
throw new Error(`Could not identify the XLF bundle for ${sourceFile}`);
}
exports.getResource = getResource;
function createXlfFilesForCoreBundle() {
return (0, event_stream_1.through)(function (file) {
const basename = path.basename(file.path);
if (basename === 'nls.metadata.json') {
if (file.isBuffer()) {
const xlfs = Object.create(null);
const json = JSON.parse(file.contents.toString('utf8'));
2024-04-30 20:57:13 +08:00
for (const coreModule in json.keys) {
2022-06-14 14:37:10 +08:00
const projectResource = getResource(coreModule);
const resource = projectResource.name;
const project = projectResource.project;
const keys = json.keys[coreModule];
const messages = json.messages[coreModule];
if (keys.length !== messages.length) {
this.emit('error', `There is a mismatch between keys and messages in ${file.relative} for module ${coreModule}`);
return;
}
else {
let xlf = xlfs[resource];
if (!xlf) {
xlf = new XLF(project);
xlfs[resource] = xlf;
}
xlf.addFile(`src/${coreModule}`, keys, messages);
}
}
2024-04-30 20:57:13 +08:00
for (const resource in xlfs) {
2022-06-14 14:37:10 +08:00
const xlf = xlfs[resource];
const filePath = `${xlf.project}/${resource.replace(/\//g, '_')}.xlf`;
const xlfFile = new File({
path: filePath,
contents: Buffer.from(xlf.toString(), 'utf8')
});
this.queue(xlfFile);
}
}
else {
this.emit('error', new Error(`File ${file.relative} is not using a buffer content`));
return;
}
}
else {
this.emit('error', new Error(`File ${file.relative} is not a core meta data file.`));
return;
}
});
}
exports.createXlfFilesForCoreBundle = createXlfFilesForCoreBundle;
2024-04-30 20:57:13 +08:00
function createL10nBundleForExtension(extensionFolderName, prefixWithBuildFolder) {
const prefix = prefixWithBuildFolder ? '.build/' : '';
return gulp
.src([
// For source code of extensions
`${prefix}extensions/${extensionFolderName}/{src,client,server}/**/*.{ts,tsx}`,
// // For any dependencies pulled in (think vscode-css-languageservice or @vscode/emmet-helper)
`${prefix}extensions/${extensionFolderName}/**/node_modules/{@vscode,vscode-*}/**/*.{js,jsx}`,
// // For any dependencies pulled in that bundle @vscode/l10n. They needed to export the bundle
`${prefix}extensions/${extensionFolderName}/**/bundle.l10n.json`,
])
2024-04-30 20:57:13 +08:00
.pipe((0, event_stream_1.map)(function (data, callback) {
const file = data;
if (!file.isBuffer()) {
// Not a buffer so we drop it
callback();
return;
}
const extension = path.extname(file.relative);
if (extension !== '.json') {
const contents = file.contents.toString('utf8');
(0, l10n_dev_1.getL10nJson)([{ contents, extension }])
.then((json) => {
callback(undefined, new File({
path: `extensions/${extensionFolderName}/bundle.l10n.json`,
contents: Buffer.from(JSON.stringify(json), 'utf8')
}));
})
.catch((err) => {
callback(new Error(`File ${file.relative} threw an error when parsing: ${err}`));
});
// signal pause?
return false;
}
// for bundle.l10n.jsons
let bundleJson;
try {
bundleJson = JSON.parse(file.contents.toString('utf8'));
}
catch (err) {
2024-04-30 20:57:13 +08:00
callback(new Error(`File ${file.relative} threw an error when parsing: ${err}`));
return;
}
// some validation of the bundle.l10n.json format
for (const key in bundleJson) {
if (typeof bundleJson[key] !== 'string' &&
(typeof bundleJson[key].message !== 'string' || !Array.isArray(bundleJson[key].comment))) {
callback(new Error(`Invalid bundle.l10n.json file. The value for key ${key} is not in the expected format.`));
return;
}
}
callback(undefined, file);
}))
2024-04-30 20:57:13 +08:00
.pipe(jsonMerge({
fileName: `extensions/${extensionFolderName}/bundle.l10n.json`,
jsonSpace: '',
concatArrays: true
}));
2024-04-30 20:57:13 +08:00
}
exports.EXTERNAL_EXTENSIONS = [
'ms-vscode.js-debug',
'ms-vscode.js-debug-companion',
'ms-vscode.vscode-js-profile-table',
'KylinIDETeam.js-debug',
2024-04-30 20:57:13 +08:00
];
2022-06-14 14:37:10 +08:00
function createXlfFilesForExtensions() {
let counter = 0;
let folderStreamEnded = false;
let folderStreamEndEmitted = false;
return (0, event_stream_1.through)(function (extensionFolder) {
const folderStream = this;
const stat = fs.statSync(extensionFolder.path);
if (!stat.isDirectory()) {
return;
}
2024-04-30 20:57:13 +08:00
const extensionFolderName = path.basename(extensionFolder.path);
if (extensionFolderName === 'node_modules') {
2022-06-14 14:37:10 +08:00
return;
}
2024-04-30 20:57:13 +08:00
// Get extension id and use that as the id
const manifest = fs.readFileSync(path.join(extensionFolder.path, 'package.json'), 'utf-8');
const manifestJson = JSON.parse(manifest);
const extensionId = manifestJson.publisher + '.' + manifestJson.name;
2022-06-14 14:37:10 +08:00
counter++;
2024-04-30 20:57:13 +08:00
let _l10nMap;
function getL10nMap() {
if (!_l10nMap) {
_l10nMap = new Map();
2022-06-14 14:37:10 +08:00
}
2024-04-30 20:57:13 +08:00
return _l10nMap;
2022-06-14 14:37:10 +08:00
}
2024-04-30 20:57:13 +08:00
(0, event_stream_1.merge)(gulp.src([`.build/extensions/${extensionFolderName}/package.nls.json`, `.build/extensions/${extensionFolderName}/**/nls.metadata.json`], { allowEmpty: true }), createL10nBundleForExtension(extensionFolderName, exports.EXTERNAL_EXTENSIONS.includes(extensionId))).pipe((0, event_stream_1.through)(function (file) {
2022-06-14 14:37:10 +08:00
if (file.isBuffer()) {
const buffer = file.contents;
const basename = path.basename(file.path);
if (basename === 'package.nls.json') {
const json = JSON.parse(buffer.toString('utf8'));
2024-04-30 20:57:13 +08:00
getL10nMap().set(`extensions/${extensionId}/package`, json);
2022-06-14 14:37:10 +08:00
}
else if (basename === 'nls.metadata.json') {
const json = JSON.parse(buffer.toString('utf8'));
2024-04-30 20:57:13 +08:00
const relPath = path.relative(`.build/extensions/${extensionFolderName}`, path.dirname(file.path));
for (const file in json) {
2022-06-14 14:37:10 +08:00
const fileContent = json[file];
2024-04-30 20:57:13 +08:00
const info = Object.create(null);
for (let i = 0; i < fileContent.messages.length; i++) {
const message = fileContent.messages[i];
const { key, comment } = LocalizeInfo.is(fileContent.keys[i])
? fileContent.keys[i]
: { key: fileContent.keys[i], comment: undefined };
info[key] = comment ? { message, comment } : message;
}
getL10nMap().set(`extensions/${extensionId}/${relPath}/${file}`, info);
2022-06-14 14:37:10 +08:00
}
}
2024-04-30 20:57:13 +08:00
else if (basename === 'bundle.l10n.json') {
const json = JSON.parse(buffer.toString('utf8'));
getL10nMap().set(`extensions/${extensionId}/bundle`, json);
}
2022-06-14 14:37:10 +08:00
else {
this.emit('error', new Error(`${file.path} is not a valid extension nls file`));
return;
}
}
}, function () {
2024-04-30 20:57:13 +08:00
if (_l10nMap?.size > 0) {
const xlfFile = new File({
path: path.join(extensionsProject, extensionId + '.xlf'),
contents: Buffer.from((0, l10n_dev_1.getL10nXlf)(_l10nMap), 'utf8')
2022-06-14 14:37:10 +08:00
});
folderStream.queue(xlfFile);
}
this.queue(null);
counter--;
if (counter === 0 && folderStreamEnded && !folderStreamEndEmitted) {
folderStreamEndEmitted = true;
folderStream.queue(null);
}
}));
}, function () {
folderStreamEnded = true;
if (counter === 0) {
folderStreamEndEmitted = true;
this.queue(null);
}
});
}
exports.createXlfFilesForExtensions = createXlfFilesForExtensions;
function createXlfFilesForIsl() {
return (0, event_stream_1.through)(function (file) {
let projectName, resourceFile;
if (path.basename(file.path) === 'messages.en.isl') {
projectName = setupProject;
resourceFile = 'messages.xlf';
}
else {
throw new Error(`Unknown input file ${file.path}`);
}
2024-04-30 20:57:13 +08:00
const xlf = new XLF(projectName), keys = [], messages = [];
const model = new TextModel(file.contents.toString());
2022-06-14 14:37:10 +08:00
let inMessageSection = false;
model.lines.forEach(line => {
if (line.length === 0) {
return;
}
2024-04-30 20:57:13 +08:00
const firstChar = line.charAt(0);
2022-06-14 14:37:10 +08:00
switch (firstChar) {
case ';':
// Comment line;
return;
case '[':
inMessageSection = '[Messages]' === line || '[CustomMessages]' === line;
return;
}
if (!inMessageSection) {
return;
}
2024-04-30 20:57:13 +08:00
const sections = line.split('=');
2022-06-14 14:37:10 +08:00
if (sections.length !== 2) {
throw new Error(`Badly formatted message found: ${line}`);
}
else {
2024-04-30 20:57:13 +08:00
const key = sections[0];
const value = sections[1];
2022-06-14 14:37:10 +08:00
if (key.length > 0 && value.length > 0) {
keys.push(key);
messages.push(value);
}
}
});
const originalPath = file.path.substring(file.cwd.length + 1, file.path.split('.')[0].length).replace(/\\/g, '/');
xlf.addFile(originalPath, keys, messages);
// Emit only upon all ISL files combined into single XLF instance
const newFilePath = path.join(projectName, resourceFile);
const xlfFile = new File({ path: newFilePath, contents: Buffer.from(xlf.toString(), 'utf-8') });
this.queue(xlfFile);
});
}
exports.createXlfFilesForIsl = createXlfFilesForIsl;
2024-04-30 20:57:13 +08:00
function createI18nFile(name, messages) {
const result = Object.create(null);
2022-06-14 14:37:10 +08:00
result[''] = [
'--------------------------------------------------------------------------------------------',
'Copyright (c) Microsoft Corporation. All rights reserved.',
'Licensed under the MIT License. See License.txt in the project root for license information.',
'--------------------------------------------------------------------------------------------',
'Do not edit this file. It is machine generated.'
];
2024-04-30 20:57:13 +08:00
for (const key of Object.keys(messages)) {
2022-06-14 14:37:10 +08:00
result[key] = messages[key];
}
let content = JSON.stringify(result, null, '\t');
if (process.platform === 'win32') {
content = content.replace(/\n/g, '\r\n');
}
return new File({
2024-04-30 20:57:13 +08:00
path: path.join(name + '.i18n.json'),
2022-06-14 14:37:10 +08:00
contents: Buffer.from(content, 'utf8')
});
}
const i18nPackVersion = '1.0.0';
2024-04-30 20:57:13 +08:00
function getRecordFromL10nJsonFormat(l10nJsonFormat) {
const record = {};
for (const key of Object.keys(l10nJsonFormat).sort()) {
const value = l10nJsonFormat[key];
record[key] = typeof value === 'string' ? value : value.message;
}
return record;
}
function prepareI18nPackFiles(resultingTranslationPaths) {
const parsePromises = [];
const mainPack = { version: i18nPackVersion, contents: {} };
const extensionsPacks = {};
const errors = [];
2022-06-14 14:37:10 +08:00
return (0, event_stream_1.through)(function (xlf) {
let project = path.basename(path.dirname(path.dirname(xlf.relative)));
2024-04-30 20:57:13 +08:00
// strip `-new` since vscode-extensions-loc uses the `-new` suffix to indicate that it's from the new loc pipeline
const resource = path.basename(path.basename(xlf.relative, '.xlf'), '-new');
if (exports.EXTERNAL_EXTENSIONS.find(e => e === resource)) {
project = extensionsProject;
}
const contents = xlf.contents.toString();
2022-06-14 14:37:10 +08:00
log(`Found ${project}: ${resource}`);
2024-04-30 20:57:13 +08:00
const parsePromise = (0, l10n_dev_1.getL10nFilesFromXlf)(contents);
2022-06-14 14:37:10 +08:00
parsePromises.push(parsePromise);
parsePromise.then(resolvedFiles => {
resolvedFiles.forEach(file => {
2024-04-30 20:57:13 +08:00
const path = file.name;
2022-06-14 14:37:10 +08:00
const firstSlash = path.indexOf('/');
if (project === extensionsProject) {
2024-04-30 20:57:13 +08:00
// resource will be the extension id
2022-06-14 14:37:10 +08:00
let extPack = extensionsPacks[resource];
if (!extPack) {
extPack = extensionsPacks[resource] = { version: i18nPackVersion, contents: {} };
}
2024-04-30 20:57:13 +08:00
// remove 'extensions/extensionId/' segment
const secondSlash = path.indexOf('/', firstSlash + 1);
extPack.contents[path.substring(secondSlash + 1)] = getRecordFromL10nJsonFormat(file.messages);
2022-06-14 14:37:10 +08:00
}
else {
2024-04-30 20:57:13 +08:00
mainPack.contents[path.substring(firstSlash + 1)] = getRecordFromL10nJsonFormat(file.messages);
2022-06-14 14:37:10 +08:00
}
});
}).catch(reason => {
errors.push(reason);
});
}, function () {
Promise.all(parsePromises)
.then(() => {
if (errors.length > 0) {
throw errors;
}
const translatedMainFile = createI18nFile('./main', mainPack);
resultingTranslationPaths.push({ id: 'vscode', resourceName: 'main.i18n.json' });
this.queue(translatedMainFile);
for (const extensionId in extensionsPacks) {
const translatedExtFile = createI18nFile(`extensions/${extensionId}`, extensionsPacks[extensionId]);
this.queue(translatedExtFile);
resultingTranslationPaths.push({ id: extensionId, resourceName: `extensions/${extensionId}.i18n.json` });
}
this.queue(null);
})
2022-06-14 14:37:10 +08:00
.catch((reason) => {
this.emit('error', reason);
});
2022-06-14 14:37:10 +08:00
});
}
exports.prepareI18nPackFiles = prepareI18nPackFiles;
function prepareIslFiles(language, innoSetupConfig) {
2024-04-30 20:57:13 +08:00
const parsePromises = [];
2022-06-14 14:37:10 +08:00
return (0, event_stream_1.through)(function (xlf) {
2024-04-30 20:57:13 +08:00
const stream = this;
const parsePromise = XLF.parse(xlf.contents.toString());
2022-06-14 14:37:10 +08:00
parsePromises.push(parsePromise);
parsePromise.then(resolvedFiles => {
resolvedFiles.forEach(file => {
2024-04-30 20:57:13 +08:00
const translatedFile = createIslFile(file.name, file.messages, language, innoSetupConfig);
2022-06-14 14:37:10 +08:00
stream.queue(translatedFile);
});
}).catch(reason => {
this.emit('error', reason);
});
}, function () {
Promise.all(parsePromises)
.then(() => { this.queue(null); })
.catch(reason => {
this.emit('error', reason);
});
2022-06-14 14:37:10 +08:00
});
}
exports.prepareIslFiles = prepareIslFiles;
2024-04-30 20:57:13 +08:00
function createIslFile(name, messages, language, innoSetup) {
const content = [];
2022-06-14 14:37:10 +08:00
let originalContent;
2024-04-30 20:57:13 +08:00
if (path.basename(name) === 'Default') {
originalContent = new TextModel(fs.readFileSync(name + '.isl', 'utf8'));
2022-06-14 14:37:10 +08:00
}
else {
2024-04-30 20:57:13 +08:00
originalContent = new TextModel(fs.readFileSync(name + '.en.isl', 'utf8'));
2022-06-14 14:37:10 +08:00
}
originalContent.lines.forEach(line => {
if (line.length > 0) {
2024-04-30 20:57:13 +08:00
const firstChar = line.charAt(0);
2022-06-14 14:37:10 +08:00
if (firstChar === '[' || firstChar === ';') {
content.push(line);
}
else {
2024-04-30 20:57:13 +08:00
const sections = line.split('=');
const key = sections[0];
2022-06-14 14:37:10 +08:00
let translated = line;
if (key) {
2024-04-30 20:57:13 +08:00
const translatedMessage = messages[key];
2022-06-14 14:37:10 +08:00
if (translatedMessage) {
translated = `${key}=${translatedMessage}`;
}
}
content.push(translated);
}
}
});
2024-04-30 20:57:13 +08:00
const basename = path.basename(name);
2022-06-14 14:37:10 +08:00
const filePath = `${basename}.${language.id}.isl`;
const encoded = iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage);
return new File({
path: filePath,
contents: Buffer.from(encoded),
});
}
function encodeEntities(value) {
2024-04-30 20:57:13 +08:00
const result = [];
2022-06-14 14:37:10 +08:00
for (let i = 0; i < value.length; i++) {
2024-04-30 20:57:13 +08:00
const ch = value[i];
2022-06-14 14:37:10 +08:00
switch (ch) {
case '<':
result.push('&lt;');
break;
case '>':
result.push('&gt;');
break;
case '&':
result.push('&amp;');
break;
default:
result.push(ch);
}
}
return result.join('');
}
function decodeEntities(value) {
return value.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
}
//# sourceMappingURL=i18n.js.map