Skip to content

Commit 4b4b889

Browse files
committed
Updated for release 1.0.0
1 parent 6747cdd commit 4b4b889

10 files changed

Lines changed: 185 additions & 34 deletions

File tree

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ Load an XLIFF file and read the first source segment:
1111
import { XliffParser } from "typesxliff";
1212

1313
const parser = new XliffParser();
14-
await parser.parseFile("file.xlf");
14+
parser.parseFile("file.xlf");
1515

1616
const doc = parser.getXliffDocument();
17-
const segment = doc?.files[0].units[0].segments[0];
17+
const segment = doc?.getFiles()?.[0]?.getEntries()?.[0];
1818

19-
console.log(segment?.source.toString());
19+
if (segment && "getItems" in segment) {
20+
console.log(segment.getItems()[0]?.getSource()?.getContent().join(""));
21+
}
2022
```
2123

2224
## Why TypesXLIFF
@@ -48,7 +50,7 @@ console.log(segment?.source.toString());
4850
- [Parsing XLIFF Files](docs/parsing.md)
4951
- [Building XLIFF Documents](docs/building.md)
5052
- [JSON Conversion](docs/json.md)
51-
- [XLIFF Validation Example](https://github.com/maxprograms-com/xliff-validation) - Complete command-line tool demonstrating XML Schema and semantic validation of XLIFF 2.x files using TypesXLIFF.
53+
- [XLIFF Validation Example](https://github.com/maxprograms-com/xliff-validation) - Companion command-line tool for strict XML Schema validation and end-to-end validation workflows. TypesXLIFF provides the object model and semantic validation methods used by that project.
5254

5355
## Scope
5456

@@ -89,10 +91,10 @@ const file = new XliffFile("f1");
8991
const unit = new XliffUnit("u1");
9092
const segment = new XliffSegment("s1");
9193
const source = new XliffSource();
92-
source.addContent("Hello, world!");
94+
source.addText("Hello, world!");
9395
segment.setSource(source);
94-
unit.addItem(segment);
95-
file.addEntry(unit);
96+
unit.addSegment(segment);
97+
file.addUnit(unit);
9698
doc.addFile(file);
9799

98100
doc.writeDocument('/path/to/output.xlf', true);

docs/examples/01-parse-file.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,31 @@
1616
* npx ts-node docs/examples/01-parse-file.ts
1717
*/
1818

19-
import { join } from "node:path";
19+
import { dirname, join } from "node:path";
20+
import { fileURLToPath } from "node:url";
2021
import { XliffParser, XliffDocument, XliffUnit, XliffSegment } from "typesxliff";
2122

22-
const sampleFile = join(__dirname, "sample.xlf");
23+
const __filename: string = fileURLToPath(import.meta.url);
24+
const __dirname: string = dirname(__filename);
25+
const sampleFile: string = join(__dirname, "sample.xlf");
2326

24-
const parser = new XliffParser();
27+
const parser: XliffParser = new XliffParser();
2528
parser.parseFile(sampleFile);
2629

2730
const doc: XliffDocument | undefined = parser.getXliffDocument();
2831
if (!doc) {
2932
console.error("Failed to parse XLIFF file.");
3033
process.exit(1);
34+
throw new Error("Failed to parse XLIFF file.");
3135
}
36+
const parsedDoc: XliffDocument = doc;
3237

33-
console.log("Version :", doc.getVersion());
34-
console.log("Source lang :", doc.getSrcLang());
35-
console.log("Target lang :", doc.getTrgLang() ?? "(none)");
38+
console.log("Version :", parsedDoc.getVersion());
39+
console.log("Source lang :", parsedDoc.getSrcLang());
40+
console.log("Target lang :", parsedDoc.getTrgLang() ?? "(none)");
3641
console.log("");
3742

38-
for (const file of doc.getFiles()) {
43+
for (const file of parsedDoc.getFiles()) {
3944
console.log("File:", file.getId());
4045

4146
for (const entry of file.getEntries()) {
@@ -44,9 +49,9 @@ for (const file of doc.getFiles()) {
4449

4550
for (const item of entry.getItems()) {
4651
if (item instanceof XliffSegment) {
47-
const source = item.getSource();
48-
const target = item.getTarget();
49-
const state = item.getState() ?? "initial";
52+
const source: ReturnType<XliffSegment["getSource"]> = item.getSource();
53+
const target: ReturnType<XliffSegment["getTarget"]> = item.getTarget();
54+
const state: string = item.getState() ?? "initial";
5055

5156
console.log(" Segment:", item.getId() ?? "(no id)");
5257
console.log(" State :", state);

docs/examples/02-build-document.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,49 +16,52 @@
1616
* npx ts-node docs/examples/02-build-document.ts
1717
*/
1818

19-
import * as path from "path";
19+
import { dirname, join } from "node:path";
20+
import { fileURLToPath } from "node:url";
2021
import {
2122
XliffDocument, XliffFile, XliffUnit, XliffSegment,
2223
XliffSource, XliffTarget, XliffNote
2324
} from "typesxliff";
2425

25-
const outputFile = path.join(__dirname, "output.xlf");
26+
const __filename: string = fileURLToPath(import.meta.url);
27+
const __dirname: string = dirname(__filename);
28+
const outputFile: string = join(__dirname, "output.xlf");
2629

2730
// Create a document: XLIFF version, source language, target language
28-
const doc = new XliffDocument("2.1", "en", "es");
31+
const doc: XliffDocument = new XliffDocument("2.1", "en", "es");
2932

3033
// Create a file container
31-
const file = new XliffFile("f1");
34+
const file: XliffFile = new XliffFile("f1");
3235

3336
// --- Unit 1: fully translated segment ---
34-
const unit1 = new XliffUnit("u1");
37+
const unit1: XliffUnit = new XliffUnit("u1");
3538

36-
const segment1 = new XliffSegment();
39+
const segment1: XliffSegment = new XliffSegment();
3740
segment1.setState("translated");
3841

39-
const source1 = new XliffSource();
42+
const source1: XliffSource = new XliffSource();
4043
source1.addText("Hello, world!");
4144
segment1.setSource(source1);
4245

43-
const target1 = new XliffTarget();
46+
const target1: XliffTarget = new XliffTarget();
4447
target1.addText("¡Hola, mundo!");
4548
segment1.setTarget(target1);
4649

4750
unit1.addSegment(segment1);
4851
file.addUnit(unit1);
4952

5053
// --- Unit 2: untranslated segment with a note ---
51-
const unit2 = new XliffUnit("u2");
54+
const unit2: XliffUnit = new XliffUnit("u2");
5255

53-
const note = new XliffNote();
56+
const note: XliffNote = new XliffNote();
5457
note.setText("Needs review by native speaker.");
5558
note.setAppliesTo("target");
5659
unit2.addNote(note);
5760

58-
const segment2 = new XliffSegment();
61+
const segment2: XliffSegment = new XliffSegment();
5962
segment2.setState("initial");
6063

61-
const source2 = new XliffSource();
64+
const source2: XliffSource = new XliffSource();
6265
source2.addText("Welcome to TypesXLIFF.");
6366
segment2.setSource(source2);
6467

docs/examples/03-json-roundtrip.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Maxprograms.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the TypesXLIFF License Agreement, which accompanies this
6+
* distribution and is available at: LICENSE.md
7+
*
8+
* Contributors:
9+
* Maxprograms - initial API and implementation
10+
*******************************************************************************/
11+
12+
/**
13+
* Example: JSON round-trip and target/@order verification
14+
*
15+
* Run with:
16+
* npx ts-node docs/examples/03-json-roundtrip.ts
17+
*/
18+
19+
import { dirname, join } from "node:path";
20+
import { fileURLToPath } from "node:url";
21+
import {
22+
JsonToXliff,
23+
XliffDocument,
24+
XliffFile,
25+
XliffSegment,
26+
XliffSource,
27+
XliffTarget,
28+
XliffToJson,
29+
XliffUnit
30+
} from "typesxliff";
31+
32+
const __filename: string = fileURLToPath(import.meta.url);
33+
const __dirname: string = dirname(__filename);
34+
const outputFile: string = join(__dirname, "roundtrip-output.xlf");
35+
36+
function collectTargetOrders(doc: XliffDocument): Array<string> {
37+
const values: Array<string> = [];
38+
for (const file of doc.getFiles()) {
39+
for (const entry of file.getEntries()) {
40+
if (!(entry instanceof XliffUnit)) {
41+
continue;
42+
}
43+
for (const item of entry.getItems()) {
44+
if (!(item instanceof XliffSegment)) {
45+
continue;
46+
}
47+
const order: number | string | undefined = item.getTarget()?.getOrder();
48+
if (order !== undefined) {
49+
values.push(String(order));
50+
}
51+
}
52+
}
53+
}
54+
return values;
55+
}
56+
57+
const originalDoc: XliffDocument = new XliffDocument("2.1", "en", "es");
58+
const file: XliffFile = new XliffFile("f1");
59+
const unit: XliffUnit = new XliffUnit("u1");
60+
61+
const segment1: XliffSegment = new XliffSegment("s1");
62+
const source1: XliffSource = new XliffSource();
63+
source1.addText("Hello");
64+
const target1: XliffTarget = new XliffTarget();
65+
target1.addText("Hola");
66+
target1.setOrder("1");
67+
segment1.setSource(source1);
68+
segment1.setTarget(target1);
69+
unit.addSegment(segment1);
70+
71+
const segment2: XliffSegment = new XliffSegment("s2");
72+
const source2: XliffSource = new XliffSource();
73+
source2.addText("World");
74+
const target2: XliffTarget = new XliffTarget();
75+
target2.addText("Mundo");
76+
target2.setOrder("2");
77+
segment2.setSource(source2);
78+
segment2.setTarget(target2);
79+
unit.addSegment(segment2);
80+
81+
file.addUnit(unit);
82+
originalDoc.addFile(file);
83+
84+
const originalOrders: Array<string> = collectTargetOrders(originalDoc);
85+
86+
const jsonObject: ReturnType<typeof XliffToJson.toJsonObject> = XliffToJson.toJsonObject(originalDoc);
87+
const roundTripDoc: XliffDocument = JsonToXliff.fromJsonObject(jsonObject);
88+
const roundTripOrders: Array<string> = collectTargetOrders(roundTripDoc);
89+
90+
if (JSON.stringify(originalOrders) !== JSON.stringify(roundTripOrders)) {
91+
console.error("Round-trip changed target/@order values.");
92+
console.error("Original : " + originalOrders.join(", "));
93+
console.error("Roundtrip: " + roundTripOrders.join(", "));
94+
process.exit(1);
95+
}
96+
97+
roundTripDoc.writeDocument(outputFile, true);
98+
console.log("Round-trip preserved target/@order values.");
99+
console.log("Written to: " + outputFile);

docs/examples/tsconfig.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2022",
4+
"module": "NodeNext",
5+
"moduleResolution": "NodeNext",
6+
"types": ["node"],
7+
"strict": true,
8+
"skipLibCheck": true,
9+
"noEmit": true
10+
},
11+
"include": ["*.ts"]
12+
}

docs/json.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,18 @@ The conversion uses TypesXML's `"roundtrip"` mode, which preserves all XML const
5858
- CDATA sections
5959

6060
This ensures that an XLIFF document converted to JSON and back produces an equivalent XML document.
61+
62+
## target/@order Behavior
63+
64+
For `target/@order`, TypesXLIFF enforces positive integer values without leading zeros.
65+
66+
Examples:
67+
68+
- Valid: `1`, `2`, `10`
69+
- Invalid: `0`, `01`, `1abc`
70+
71+
When round-tripping (`XML -> JSON -> XLIFF -> XML`), valid `target/@order` values are preserved.
72+
73+
## Runnable Example
74+
75+
See [examples/03-json-roundtrip.ts](examples/03-json-roundtrip.ts) for a complete JSON round-trip flow that verifies `target/@order` values before and after reconstruction.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "typesxliff",
33
"productName": "TypesXLIFF",
4-
"version": "0.6.0",
4+
"version": "1.0.0",
55
"description": "TypeScript library for creating, loading, querying, and writing XLIFF 2.x documents",
66
"keywords": [
77
"XLIFF",

ts/models/structural/XliffTarget.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ export class XliffTarget implements XliffElement {
106106
return false;
107107
}
108108
if (this.order !== undefined) {
109+
if (typeof this.order === "string" && !/^[1-9][0-9]*$/.test(this.order)) {
110+
this.errorReason = 'The @order attribute value "' + this.order + '" is not valid';
111+
return false;
112+
}
109113
const order: number = typeof this.order === "number" ? this.order : Number(this.order);
110114
if (!Number.isInteger(order) || order <= 0) {
111115
this.errorReason = 'The @order attribute value "' + this.order + '" is not valid';

ts/parser/xliffContentHandler.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,12 +548,23 @@ export class XliffContentHandler implements ContentHandler {
548548
}
549549
let xmlLang: string | undefined = element.getAttribute('xml:lang')?.getValue();
550550
let parent = this.xliffStack[this.xliffStack.length - 1];
551+
let order: string | undefined = element.getAttribute('order')?.getValue();
552+
let orderValue: number | undefined;
553+
if (order !== undefined) {
554+
if (!/^[1-9][0-9]*$/.test(order)) {
555+
throw new Error('Invalid value for "order" attribute on <target> element: ' + order);
556+
}
557+
orderValue = Number(order);
558+
}
551559
if (parent instanceof XliffSegment || parent instanceof XliffIgnorable || parent instanceof XliffMatch) {
552560
let target = new XliffTarget();
553561
target.setXmlSpace(xmlSpace as XliffXmlSpace | undefined);
554562
if (xmlLang !== undefined) {
555563
target.setXmlLang(xmlLang);
556564
}
565+
if (orderValue !== undefined) {
566+
target.setOrder(orderValue);
567+
}
557568
parent.setTarget(target);
558569
this.xliffStack.push(target);
559570
} else {
@@ -576,7 +587,7 @@ export class XliffContentHandler implements ContentHandler {
576587
let id: string | undefined = element.getAttribute('id')?.getValue();
577588
let appliesTo: string | undefined = element.getAttribute('appliesTo')?.getValue();
578589
if (appliesTo !== undefined) {
579-
if (appliesTo !== 'source' && appliesTo !== 'target' && appliesTo !== 'all') {
590+
if (appliesTo !== 'source' && appliesTo !== 'target') {
580591
throw new Error('Invalid value for "appliesTo" attribute on <note> element: ' + appliesTo);
581592
}
582593
}
@@ -1361,7 +1372,7 @@ export class XliffContentHandler implements ContentHandler {
13611372
}
13621373

13631374
getCurrentText(): string {
1364-
if (this.stack.length > 0) {
1375+
if (this.stack.length > 0) {
13651376
return this.stack[this.stack.length - 1].pureText();
13661377
}
13671378
return '';

0 commit comments

Comments
 (0)