New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@lit/localize-tools

Package Overview
Dependencies
Maintainers
9
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lit/localize-tools - npm Package Compare versions

Comparing version 0.6.5 to 0.6.6

16

lib/formatters/xliff.d.ts

@@ -46,2 +46,18 @@ /**

private createPlaceholder;
private initNewTargetDocument;
private readTranslationFile;
private makeExistingTranslationsMap;
private getElementByTagName;
private getNoteElement;
/**
* xmldom does not implement replaceChildren.
*/
private clearElement;
/**
* Append childNode to parentNode, try to preserve some
* whitespace / indentation for common scenarios.
*/
private appendChild;
private removeChildAndPrecedingText;
private createIndentFunction;
}

226

lib/formatters/xliff.js

@@ -38,15 +38,6 @@ /**

for (const locale of this.config.targetLocales) {
const path = pathLib.join(this.config.resolve(this.xliffConfig.xliffDir), locale + '.xlf');
let xmlStr;
try {
xmlStr = fsExtra.readFileSync(path, 'utf8');
const xmlStr = this.readTranslationFile(locale);
if (!xmlStr) {
continue;
}
catch (err) {
if (err.code === 'ENOENT') {
// It's ok if the file doesn't exist, it's probably just the first
// time we're running for this locale.
continue;
}
throw err;
}
bundles.push(this.parseXliff(xmlStr));

@@ -125,3 +116,6 @@ }

for (const targetLocale of this.config.targetLocales) {
const xmlStr = this.encodeLocale(sourceMessages, targetLocale, translations.get(targetLocale) || []);
const existingTargetXmlStr = this.readTranslationFile(targetLocale);
const xmlStr = this.encodeLocale(sourceMessages, targetLocale, translations.get(targetLocale) || [], existingTargetXmlStr
? new xmldom.DOMParser().parseFromString(existingTargetXmlStr)
: undefined);
const path = pathLib.join(xliffDir, `${targetLocale}.xlf`);

@@ -139,65 +133,74 @@ writes.push(fsExtra.writeFile(path, xmlStr, 'utf8').catch((e) => {

*/
encodeLocale(sourceMessages, targetLocale, targetMessages) {
encodeLocale(sourceMessages, targetLocale, targetMessages, targetDocument) {
const translationsByName = makeMessageIdMap(targetMessages);
const doc = new xmldom.DOMImplementation().createDocument('', '', null);
const indent = (node, level = 0) => node.appendChild(doc.createTextNode('\n' + Array(level + 1).join(' ')));
doc.appendChild(doc.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"'));
indent(doc);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#xliff
const xliff = doc.createElement('xliff');
xliff.setAttribute('version', '1.2');
xliff.setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
doc.appendChild(xliff);
indent(xliff);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#file
const file = doc.createElement('file');
xliff.appendChild(file);
file.setAttribute('target-language', targetLocale);
file.setAttribute('source-language', this.config.sourceLocale);
// TODO The spec requires the source filename in the "original" attribute,
// but we don't currently track filenames.
file.setAttribute('original', 'lit-localize-inputs');
// Plaintext seems right, as opposed to HTML, since our translatable message
// text is just text, and all HTML markup is encoded into <x> or <ph>
// elements.
file.setAttribute('datatype', 'plaintext');
indent(file);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#body
const body = doc.createElement('body');
file.appendChild(body);
indent(body);
const doc = targetDocument || this.initNewTargetDocument(targetLocale);
const indent = this.createIndentFunction(doc);
const existingTranslationsByName = this.makeExistingTranslationsMap(doc);
const body = getOneElementByTagNameOrThrow(doc, 'body');
const names = new Set();
for (const { name, contents: sourceContents, desc } of sourceMessages) {
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#trans-unit
const transUnit = doc.createElement('trans-unit');
body.appendChild(transUnit);
indent(transUnit, 1);
transUnit.setAttribute('id', name);
let transUnit = existingTranslationsByName.get(name);
if (!transUnit) {
transUnit = doc.createElement('trans-unit');
transUnit.setAttribute('id', name);
this.appendChild(body, transUnit, 0, indent);
}
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#source
const source = doc.createElement('source');
let source = this.getElementByTagName(transUnit, 'source');
if (!source) {
source = doc.createElement('source');
this.appendChild(transUnit, source, 1, indent);
}
else {
this.clearElement(source);
}
for (const child of this.encodeContents(doc, sourceContents)) {
source.appendChild(child);
}
transUnit.appendChild(source);
const translation = translationsByName.get(name);
if (translation !== undefined) {
if (translation) {
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#target
const target = doc.createElement('target');
let target = this.getElementByTagName(transUnit, 'target');
if (!target) {
target = doc.createElement('target');
this.appendChild(transUnit, target, 1, indent);
}
else {
this.clearElement(target);
}
for (const child of this.encodeContents(doc, translation.contents)) {
target.appendChild(child);
}
indent(transUnit, 1);
transUnit.appendChild(target);
}
if (desc) {
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#note
const note = doc.createElement('note');
let note = this.getNoteElement(transUnit, desc);
if (!note) {
note = doc.createElement('note');
note.setAttribute('from', 'lit-localize');
this.appendChild(transUnit, note, 1, indent);
}
else {
// migrate existing notes generated by lit-localize
if (!note.hasAttribute('from')) {
note.setAttribute('from', 'lit-localize');
}
this.clearElement(note);
}
note.appendChild(doc.createTextNode(desc));
indent(transUnit, 1);
transUnit.appendChild(note);
}
indent(transUnit);
indent(body);
names.add(name);
}
indent(file);
indent(xliff);
// clean up obsolete translations
existingTranslationsByName.forEach((existingTransUnit, existingName) => {
if (!names.has(existingName)) {
this.removeChildAndPrecedingText(body, existingTransUnit);
}
});
if (!targetDocument) {
indent(getOneElementByTagNameOrThrow(doc, 'file'));
indent(getOneElementByTagNameOrThrow(doc, 'xliff'));
}
// for existing files this is a guess
indent(doc);

@@ -246,3 +249,110 @@ const serializer = new xmldom.XMLSerializer();

}
initNewTargetDocument(targetLocale) {
const doc = new xmldom.DOMImplementation().createDocument('', '', null);
const indent = this.createIndentFunction(doc);
doc.appendChild(doc.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"'));
indent(doc);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#xliff
const xliff = doc.createElement('xliff');
xliff.setAttribute('version', '1.2');
xliff.setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
doc.appendChild(xliff);
indent(xliff);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#file
const file = doc.createElement('file');
xliff.appendChild(file);
file.setAttribute('target-language', targetLocale);
file.setAttribute('source-language', this.config.sourceLocale);
// TODO The spec requires the source filename in the "original" attribute,
// but we don't currently track filenames.
file.setAttribute('original', 'lit-localize-inputs');
// Plaintext seems right, as opposed to HTML, since our translatable message
// text is just text, and all HTML markup is encoded into <x> or <ph>
// elements.
file.setAttribute('datatype', 'plaintext');
indent(file);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#body
const body = doc.createElement('body');
file.appendChild(body);
return doc;
}
readTranslationFile(locale) {
const path = pathLib.join(this.config.resolve(this.xliffConfig.xliffDir), locale + '.xlf');
try {
return fsExtra.readFileSync(path, 'utf8');
}
catch (err) {
if (err.code === 'ENOENT') {
// It's ok if the file doesn't exist, it's probably just the first
// time we're running for this locale.
return undefined;
}
throw err;
}
}
makeExistingTranslationsMap(document) {
const map = new Map();
const transUnits = document.getElementsByTagName('trans-unit');
for (let t = 0; t < transUnits.length; t++) {
const transUnit = transUnits[t];
map.set(getNonEmptyAttributeOrThrow(transUnit, 'id'), transUnit);
}
return map;
}
getElementByTagName(element, tagName) {
const matches = element.getElementsByTagName(tagName);
return matches.length === 1 ? matches[0] : undefined;
}
getNoteElement(element, note) {
const matches = element.getElementsByTagName('note');
if (matches.length < 1) {
return undefined;
}
for (let i = 0; i < matches.length; i++) {
const el = matches.item(i);
if (el?.getAttribute('from') === 'lit-localize') {
return el;
}
// generated by previous version of lit-localize
if (el?.textContent?.trim() === note) {
return el;
}
}
return undefined;
}
/**
* xmldom does not implement replaceChildren.
*/
clearElement(element) {
const children = element.childNodes;
for (let i = 0; i < children.length; i++) {
element.removeChild(children.item(i));
}
}
/**
* Append childNode to parentNode, try to preserve some
* whitespace / indentation for common scenarios.
*/
appendChild(parentNode, childNode, level, indent) {
const lastChild = parentNode.lastChild;
if (lastChild) {
parentNode.insertBefore(indent(parentNode, level), lastChild);
parentNode.insertBefore(childNode, lastChild);
}
else {
parentNode.appendChild(indent(parentNode, level));
parentNode.appendChild(childNode);
parentNode.appendChild(indent(parentNode, level - 1));
}
}
removeChildAndPrecedingText(parentNode, childNode) {
if (childNode.previousSibling?.nodeType === childNode.TEXT_NODE) {
parentNode.removeChild(childNode.previousSibling);
}
parentNode.removeChild(childNode);
}
createIndentFunction(doc) {
return (node, level = 0) => node.appendChild(doc.createTextNode('\n' + Array(level + 1).join(' ')));
}
}
//# sourceMappingURL=xliff.js.map
{
"name": "@lit/localize-tools",
"version": "0.6.5",
"version": "0.6.6",
"publishConfig": {

@@ -138,3 +138,3 @@ "access": "public"

"jsonschema": "^1.4.0",
"lit": "^2.2.0",
"lit": "^2.6.0",
"minimist": "^1.2.5",

@@ -146,3 +146,3 @@ "parse5": "^6.0.1",

"devDependencies": {
"@lit-labs/ssr": "^2.1.0",
"@lit-labs/ssr": "^3.0.0",
"@lit/ts-transformers": "^1.1.0",

@@ -149,0 +149,0 @@ "@types/fs-extra": "^9.0.1",

@@ -60,16 +60,5 @@ /**

for (const locale of this.config.targetLocales) {
const path = pathLib.join(
this.config.resolve(this.xliffConfig.xliffDir),
locale + '.xlf'
);
let xmlStr;
try {
xmlStr = fsExtra.readFileSync(path, 'utf8');
} catch (err) {
if ((err as Error & {code: string}).code === 'ENOENT') {
// It's ok if the file doesn't exist, it's probably just the first
// time we're running for this locale.
continue;
}
throw err;
const xmlStr = this.readTranslationFile(locale);
if (!xmlStr) {
continue;
}

@@ -174,6 +163,10 @@ bundles.push(this.parseXliff(xmlStr));

for (const targetLocale of this.config.targetLocales) {
const existingTargetXmlStr = this.readTranslationFile(targetLocale);
const xmlStr = this.encodeLocale(
sourceMessages,
targetLocale,
translations.get(targetLocale) || []
translations.get(targetLocale) || [],
existingTargetXmlStr
? new xmldom.DOMParser().parseFromString(existingTargetXmlStr)
: undefined
);

@@ -200,63 +193,45 @@ const path = pathLib.join(xliffDir, `${targetLocale}.xlf`);

targetLocale: Locale,
targetMessages: Message[]
targetMessages: Message[],
targetDocument?: XMLDocument
): string {
const translationsByName = makeMessageIdMap(targetMessages);
const doc = new xmldom.DOMImplementation().createDocument('', '', null);
const indent = (node: Element | Document, level = 0) =>
node.appendChild(doc.createTextNode('\n' + Array(level + 1).join(' ')));
doc.appendChild(
doc.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"')
);
indent(doc);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#xliff
const xliff = doc.createElement('xliff');
xliff.setAttribute('version', '1.2');
xliff.setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
doc.appendChild(xliff);
indent(xliff);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#file
const file = doc.createElement('file');
xliff.appendChild(file);
file.setAttribute('target-language', targetLocale);
file.setAttribute('source-language', this.config.sourceLocale);
// TODO The spec requires the source filename in the "original" attribute,
// but we don't currently track filenames.
file.setAttribute('original', 'lit-localize-inputs');
// Plaintext seems right, as opposed to HTML, since our translatable message
// text is just text, and all HTML markup is encoded into <x> or <ph>
// elements.
file.setAttribute('datatype', 'plaintext');
indent(file);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#body
const body = doc.createElement('body');
file.appendChild(body);
indent(body);
const doc = targetDocument || this.initNewTargetDocument(targetLocale);
const indent = this.createIndentFunction(doc);
const existingTranslationsByName = this.makeExistingTranslationsMap(doc);
const body = getOneElementByTagNameOrThrow(doc, 'body');
const names = new Set();
for (const {name, contents: sourceContents, desc} of sourceMessages) {
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#trans-unit
const transUnit = doc.createElement('trans-unit');
body.appendChild(transUnit);
indent(transUnit, 1);
transUnit.setAttribute('id', name);
let transUnit = existingTranslationsByName.get(name);
if (!transUnit) {
transUnit = doc.createElement('trans-unit');
transUnit.setAttribute('id', name);
this.appendChild(body, transUnit, 0, indent);
}
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#source
const source = doc.createElement('source');
let source = this.getElementByTagName(transUnit, 'source');
if (!source) {
source = doc.createElement('source');
this.appendChild(transUnit, source, 1, indent);
} else {
this.clearElement(source);
}
for (const child of this.encodeContents(doc, sourceContents)) {
source.appendChild(child);
}
transUnit.appendChild(source);
const translation = translationsByName.get(name);
if (translation !== undefined) {
if (translation) {
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#target
const target = doc.createElement('target');
let target = this.getElementByTagName(transUnit, 'target');
if (!target) {
target = doc.createElement('target');
this.appendChild(transUnit, target, 1, indent);
} else {
this.clearElement(target);
}
for (const child of this.encodeContents(doc, translation.contents)) {
target.appendChild(child);
}
indent(transUnit, 1);
transUnit.appendChild(target);
}

@@ -266,14 +241,35 @@

// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#note
const note = doc.createElement('note');
let note = this.getNoteElement(transUnit, desc);
if (!note) {
note = doc.createElement('note');
note.setAttribute('from', 'lit-localize');
this.appendChild(transUnit, note, 1, indent);
} else {
// migrate existing notes generated by lit-localize
if (!note.hasAttribute('from')) {
note.setAttribute('from', 'lit-localize');
}
this.clearElement(note);
}
note.appendChild(doc.createTextNode(desc));
indent(transUnit, 1);
transUnit.appendChild(note);
}
indent(transUnit);
indent(body);
names.add(name);
}
indent(file);
indent(xliff);
// clean up obsolete translations
existingTranslationsByName.forEach((existingTransUnit, existingName) => {
if (!names.has(existingName)) {
this.removeChildAndPrecedingText(body, existingTransUnit);
}
});
if (!targetDocument) {
indent(getOneElementByTagNameOrThrow(doc, 'file'));
indent(getOneElementByTagNameOrThrow(doc, 'xliff'));
}
// for existing files this is a guess
indent(doc);
const serializer = new xmldom.XMLSerializer();

@@ -326,2 +322,136 @@ const xmlStr = serializer.serializeToString(doc);

}
private initNewTargetDocument(targetLocale: Locale): XMLDocument {
const doc = new xmldom.DOMImplementation().createDocument('', '', null);
const indent = this.createIndentFunction(doc);
doc.appendChild(
doc.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"')
);
indent(doc);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#xliff
const xliff = doc.createElement('xliff');
xliff.setAttribute('version', '1.2');
xliff.setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
doc.appendChild(xliff);
indent(xliff);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#file
const file = doc.createElement('file');
xliff.appendChild(file);
file.setAttribute('target-language', targetLocale);
file.setAttribute('source-language', this.config.sourceLocale);
// TODO The spec requires the source filename in the "original" attribute,
// but we don't currently track filenames.
file.setAttribute('original', 'lit-localize-inputs');
// Plaintext seems right, as opposed to HTML, since our translatable message
// text is just text, and all HTML markup is encoded into <x> or <ph>
// elements.
file.setAttribute('datatype', 'plaintext');
indent(file);
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#body
const body = doc.createElement('body');
file.appendChild(body);
return doc;
}
private readTranslationFile(locale: Locale): string | undefined {
const path = pathLib.join(
this.config.resolve(this.xliffConfig.xliffDir),
locale + '.xlf'
);
try {
return fsExtra.readFileSync(path, 'utf8');
} catch (err) {
if ((err as Error & {code: string}).code === 'ENOENT') {
// It's ok if the file doesn't exist, it's probably just the first
// time we're running for this locale.
return undefined;
}
throw err;
}
}
private makeExistingTranslationsMap(
document: XMLDocument
): Map<string, Element> {
const map = new Map<string, Element>();
const transUnits = document.getElementsByTagName('trans-unit');
for (let t = 0; t < transUnits.length; t++) {
const transUnit = transUnits[t];
map.set(getNonEmptyAttributeOrThrow(transUnit, 'id'), transUnit);
}
return map;
}
private getElementByTagName(
element: Element,
tagName: string
): Element | undefined {
const matches = element.getElementsByTagName(tagName);
return matches.length === 1 ? matches[0] : undefined;
}
private getNoteElement(element: Element, note: string): Element | undefined {
const matches = element.getElementsByTagName('note');
if (matches.length < 1) {
return undefined;
}
for (let i = 0; i < matches.length; i++) {
const el = matches.item(i);
if (el?.getAttribute('from') === 'lit-localize') {
return el;
}
// generated by previous version of lit-localize
if (el?.textContent?.trim() === note) {
return el;
}
}
return undefined;
}
/**
* xmldom does not implement replaceChildren.
*/
private clearElement(element: Element) {
const children = element.childNodes;
for (let i = 0; i < children.length; i++) {
element.removeChild(children.item(i));
}
}
/**
* Append childNode to parentNode, try to preserve some
* whitespace / indentation for common scenarios.
*/
private appendChild(
parentNode: Node,
childNode: Node,
level: number,
indent: Function
) {
const lastChild = parentNode.lastChild;
if (lastChild) {
parentNode.insertBefore(indent(parentNode, level), lastChild);
parentNode.insertBefore(childNode, lastChild);
} else {
parentNode.appendChild(indent(parentNode, level));
parentNode.appendChild(childNode);
parentNode.appendChild(indent(parentNode, level - 1));
}
}
private removeChildAndPrecedingText(parentNode: Node, childNode: Node) {
if (childNode.previousSibling?.nodeType === childNode.TEXT_NODE) {
parentNode.removeChild(childNode.previousSibling);
}
parentNode.removeChild(childNode);
}
private createIndentFunction(doc: Document): Function {
return (node: Element | Document, level = 0) =>
node.appendChild(doc.createTextNode('\n' + Array(level + 1).join(' ')));
}
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc