Skip to content

Commit 6e28cf9

Browse files
feat(xliff): initial implementation of xliff format
1 parent 894c877 commit 6e28cf9

File tree

7 files changed

+497
-0
lines changed

7 files changed

+497
-0
lines changed

jest.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ module.exports = {
7070
"<rootDir>/packages/format-csv",
7171
"<rootDir>/packages/message-utils",
7272
"<rootDir>/packages/extractor-vue",
73+
"<rootDir>/packages/format-xliff",
7374
],
7475
},
7576
],

packages/format-xliff/README.md

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
[![License][badge-license]][license]
2+
[![Version][badge-version]][package]
3+
[![Downloads][badge-downloads]][package]
4+
5+
# @lingui/format-po
6+
7+
> Read and write message catalogs in Gettext PO format with ICU plurals
8+
9+
`@lingui/format-po` is part of [LinguiJS][linguijs]. See the
10+
[documentation][documentation] for all information, tutorials and examples.
11+
12+
## Catalog example
13+
14+
```po
15+
#, Comment for translators
16+
#: src/App.js:4, src/Component.js:2
17+
msgid "MessageID"
18+
msgstr "Translated Message"
19+
```
20+
21+
## Installation
22+
23+
```sh
24+
npm install --save-dev @lingui/format-po
25+
# yarn add --dev @lingui/format-po
26+
```
27+
28+
## Usage
29+
30+
```js
31+
// lingui.config.{js,ts}
32+
import {formatter} from "@lingui/format-po"
33+
34+
export default {
35+
[...]
36+
format: formatter({lineNumbers: false}),
37+
}
38+
```
39+
40+
Possible options:
41+
42+
```ts
43+
export type PoFormatterOptions = {
44+
/**
45+
* Print places where message is used
46+
*
47+
* @default true
48+
*/
49+
origins?: boolean
50+
51+
/**
52+
* Print line numbers in origins
53+
*
54+
* @default true
55+
*/
56+
lineNumbers?: boolean
57+
58+
/**
59+
* Print `js-lingui-id: Xs4as` statement in extracted comments section
60+
*
61+
* @default false
62+
*/
63+
printLinguiId?: boolean
64+
}
65+
```
66+
67+
## License
68+
69+
This package is licensed under [MIT][license] license.
70+
71+
[license]: https://github.com/lingui/js-lingui/blob/main/LICENSE
72+
[linguijs]: https://github.com/lingui/js-lingui
73+
[documentation]: https://lingui.dev
74+
[package]: https://www.npmjs.com/package/@lingui/format-po
75+
[badge-downloads]: https://img.shields.io/npm/dw/@lingui/format-po.svg
76+
[badge-version]: https://img.shields.io/npm/v/@lingui/format-po.svg
77+
[badge-license]: https://img.shields.io/npm/l/@lingui/format-po.svg

packages/format-xliff/package.json

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@lingui/format-xliff",
3+
"version": "4.0.0",
4+
"description": "Gettext PO format for Lingui Catalogs",
5+
"main": "./dist/xliff.cjs",
6+
"module": "./dist/xliff.mjs",
7+
"types": "./dist/xliff.d.ts",
8+
"license": "MIT",
9+
"keywords": [
10+
"i18n",
11+
"lingui-format",
12+
"lingui-formatter",
13+
"xliff",
14+
"internationalization",
15+
"i10n",
16+
"localization",
17+
"i9n",
18+
"translation"
19+
],
20+
"scripts": {
21+
"build": "rimraf ./dist && unbuild",
22+
"stub": "unbuild --stub"
23+
},
24+
"repository": {
25+
"type": "git",
26+
"url": "https://github.com/lingui/js-lingui.git"
27+
},
28+
"bugs": {
29+
"url": "https://github.com/lingui/js-lingui/issues"
30+
},
31+
"engines": {
32+
"node": ">=16.0.0"
33+
},
34+
"files": [
35+
"LICENSE",
36+
"README.md",
37+
"dist/"
38+
],
39+
"dependencies": {
40+
"@lingui/cli": "4.1.2",
41+
"@lingui/conf": "4.1.2",
42+
"xml-js": "^1.6.11"
43+
},
44+
"devDependencies": {
45+
"@lingui/jest-mocks": "workspace:^",
46+
"unbuild": "^1.1.2"
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`xliff format should write catalog in pofile format 1`] = `
4+
<?xml version="1.0" encoding="UTF-8"?>
5+
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
6+
<file source-language="en" datatype="plaintext" original="js-lingui" target-language="en">
7+
<body>
8+
<trans-unit id="static" datatype="html">
9+
<target><![CDATA[Static message]]></target>
10+
</trans-unit>
11+
<trans-unit id="withOrigin" datatype="html">
12+
<target><![CDATA[Message with origin]]></target>
13+
<context-group purpose="location">
14+
<context context-type="sourcefile">src/App.js</context>
15+
<context context-type="linenumber">4</context>
16+
</context-group>
17+
</trans-unit>
18+
<trans-unit id="withContext" datatype="html">
19+
<target><![CDATA[Message with context]]></target>
20+
<note priority="1" from="context">my context</note>
21+
</trans-unit>
22+
<trans-unit id="Dgzql1" datatype="html">
23+
<source><![CDATA[with generated id]]></source>
24+
<note priority="1" from="context">my context</note>
25+
</trans-unit>
26+
<trans-unit id="withMultipleOrigins" datatype="html">
27+
<target><![CDATA[Message with multiple origin]]></target>
28+
<context-group purpose="location">
29+
<context context-type="sourcefile">src/App.js</context>
30+
<context context-type="linenumber">4</context>
31+
</context-group>
32+
<context-group purpose="location">
33+
<context context-type="sourcefile">src/Component.js</context>
34+
<context context-type="linenumber">2</context>
35+
</context-group>
36+
</trans-unit>
37+
<trans-unit id="withDescription" datatype="html">
38+
<target><![CDATA[Message with description]]></target>
39+
<note priority="1" from="comment">Description is comment from developers to translators</note>
40+
</trans-unit>
41+
<trans-unit id="veryLongString" datatype="html">
42+
<target><![CDATA[One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, slightly domed and divided by arches into stiff sections. The bedding was hardly able to cover it and seemed ready to slide off any moment. His many legs, pitifully thin compared with the size of the rest of him, waved about helplessly as he looked. "What's happened to me?" he thought. It wasn't a dream. His room, a proper human]]></target>
43+
</trans-unit>
44+
<trans-unit id="stringWithPlaceholders" datatype="html">
45+
<source><![CDATA[Hello {world}]]></source>
46+
</trans-unit>
47+
<trans-unit id="stringWithJsxPlaceholders" datatype="html">
48+
<source><![CDATA[Hello <0>String</0>]]></source>
49+
</trans-unit>
50+
</body>
51+
</file>
52+
</xliff>
53+
`;
+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { formatter as createFormatter } from "./xliff"
2+
import { CatalogType } from "@lingui/conf"
3+
4+
describe("xliff format", () => {
5+
it("should write catalog in pofile format", async () => {
6+
const format = createFormatter({ origins: true })
7+
8+
const catalog: CatalogType = {
9+
static: {
10+
translation: "Static message",
11+
},
12+
withOrigin: {
13+
translation: "Message with origin",
14+
origin: [["src/App.js", 4]],
15+
},
16+
withContext: {
17+
translation: "Message with context",
18+
context: "my context",
19+
},
20+
Dgzql1: {
21+
message: "with generated id",
22+
translation: "",
23+
context: "my context",
24+
},
25+
withMultipleOrigins: {
26+
translation: "Message with multiple origin",
27+
origin: [
28+
["src/App.js", 4],
29+
["src/Component.js", 2],
30+
],
31+
},
32+
withDescription: {
33+
translation: "Message with description",
34+
comments: ["Description is comment from developers to translators"],
35+
},
36+
// obsolete: {
37+
// translation: "Obsolete message",
38+
// obsolete: true,
39+
// },
40+
// withFlags: {
41+
// flags: ["fuzzy", "otherFlag"],
42+
// translation: "Keeps any flags that are defined",
43+
// },
44+
veryLongString: {
45+
translation:
46+
"One morning, when Gregor Samsa woke from troubled dreams, he found himself" +
47+
" transformed in his bed into a horrible vermin. He lay on his armour-like" +
48+
" back, and if he lifted his head a little he could see his brown belly," +
49+
" slightly domed and divided by arches into stiff sections. The bedding was" +
50+
" hardly able to cover it and seemed ready to slide off any moment. His many" +
51+
" legs, pitifully thin compared with the size of the rest of him, waved about" +
52+
" helplessly as he looked. \"What's happened to me?\" he thought. It wasn't" +
53+
" a dream. His room, a proper human",
54+
},
55+
56+
stringWithPlaceholders: {
57+
message: "Hello {world}",
58+
translation: null,
59+
},
60+
61+
stringWithJsxPlaceholders: {
62+
message: "Hello <0>String</0>",
63+
translation: null,
64+
},
65+
}
66+
67+
const actual = format.serialize(catalog, {
68+
locale: "en",
69+
existing: null,
70+
})
71+
expect(actual).toMatchSnapshot()
72+
})
73+
74+
it.todo(
75+
"should read catalog in xliff format" /*, () => {
76+
const format = createFormatter()
77+
78+
const pofile = fs
79+
.readFileSync(path.join(__dirname, "fixtures/messages.po"))
80+
.toString()
81+
82+
const actual = format.parse(pofile)
83+
expect(actual).toMatchSnapshot()
84+
}*/
85+
)
86+
87+
it.todo("should preserve attributes stored on trans unit")
88+
it.todo("should preserve children in trans unit")
89+
90+
it.todo("should not include origins if origins option is false")
91+
92+
it.todo("should not include lineNumbers if lineNumbers option is false")
93+
})

0 commit comments

Comments
 (0)