Skip to content

Commit

Permalink
feat(xliff): initial implementation of xliff format
Browse files Browse the repository at this point in the history
  • Loading branch information
timofei-iatsenko committed May 26, 2023
1 parent 3303228 commit 68e5fb6
Show file tree
Hide file tree
Showing 7 changed files with 497 additions and 0 deletions.
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ module.exports = {
"<rootDir>/packages/format-csv",
"<rootDir>/packages/message-utils",
"<rootDir>/packages/extractor-vue",
"<rootDir>/packages/format-xliff",
],
},
],
Expand Down
77 changes: 77 additions & 0 deletions packages/format-xliff/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
[![License][badge-license]][license]
[![Version][badge-version]][package]
[![Downloads][badge-downloads]][package]

# @lingui/format-po

> Read and write message catalogs in Gettext PO format with ICU plurals
`@lingui/format-po` is part of [LinguiJS][linguijs]. See the
[documentation][documentation] for all information, tutorials and examples.

## Catalog example

```po
#, Comment for translators
#: src/App.js:4, src/Component.js:2
msgid "MessageID"
msgstr "Translated Message"
```

## Installation

```sh
npm install --save-dev @lingui/format-po
# yarn add --dev @lingui/format-po
```

## Usage

```js
// lingui.config.{js,ts}
import {formatter} from "@lingui/format-po"

export default {
[...]
format: formatter({lineNumbers: false}),
}
```

Possible options:

```ts
export type PoFormatterOptions = {
/**
* Print places where message is used
*
* @default true
*/
origins?: boolean

/**
* Print line numbers in origins
*
* @default true
*/
lineNumbers?: boolean

/**
* Print `js-lingui-id: Xs4as` statement in extracted comments section
*
* @default false
*/
printLinguiId?: boolean
}
```
## License
This package is licensed under [MIT][license] license.
[license]: https://github.com/lingui/js-lingui/blob/main/LICENSE
[linguijs]: https://github.com/lingui/js-lingui
[documentation]: https://lingui.dev
[package]: https://www.npmjs.com/package/@lingui/format-po
[badge-downloads]: https://img.shields.io/npm/dw/@lingui/format-po.svg
[badge-version]: https://img.shields.io/npm/v/@lingui/format-po.svg
[badge-license]: https://img.shields.io/npm/l/@lingui/format-po.svg
48 changes: 48 additions & 0 deletions packages/format-xliff/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@lingui/format-xliff",
"version": "4.0.0",
"description": "Gettext PO format for Lingui Catalogs",
"main": "./dist/xliff.cjs",
"module": "./dist/xliff.mjs",
"types": "./dist/xliff.d.ts",
"license": "MIT",
"keywords": [
"i18n",
"lingui-format",
"lingui-formatter",
"xliff",
"internationalization",
"i10n",
"localization",
"i9n",
"translation"
],
"scripts": {
"build": "rimraf ./dist && unbuild",
"stub": "unbuild --stub"
},
"repository": {
"type": "git",
"url": "https://github.com/lingui/js-lingui.git"
},
"bugs": {
"url": "https://github.com/lingui/js-lingui/issues"
},
"engines": {
"node": ">=16.0.0"
},
"files": [
"LICENSE",
"README.md",
"dist/"
],
"dependencies": {
"@lingui/cli": "4.1.2",
"@lingui/conf": "4.1.2",
"xml-js": "^1.6.11"
},
"devDependencies": {
"@lingui/jest-mocks": "workspace:^",
"unbuild": "^1.1.2"
}
}
53 changes: 53 additions & 0 deletions packages/format-xliff/src/__snapshots__/xliff.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`xliff format should write catalog in pofile format 1`] = `
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="js-lingui" target-language="en">
<body>
<trans-unit id="static" datatype="html">
<target><![CDATA[Static message]]></target>
</trans-unit>
<trans-unit id="withOrigin" datatype="html">
<target><![CDATA[Message with origin]]></target>
<context-group purpose="location">
<context context-type="sourcefile">src/App.js</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="withContext" datatype="html">
<target><![CDATA[Message with context]]></target>
<note priority="1" from="context">my context</note>
</trans-unit>
<trans-unit id="Dgzql1" datatype="html">
<source><![CDATA[with generated id]]></source>
<note priority="1" from="context">my context</note>
</trans-unit>
<trans-unit id="withMultipleOrigins" datatype="html">
<target><![CDATA[Message with multiple origin]]></target>
<context-group purpose="location">
<context context-type="sourcefile">src/App.js</context>
<context context-type="linenumber">4</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/Component.js</context>
<context context-type="linenumber">2</context>
</context-group>
</trans-unit>
<trans-unit id="withDescription" datatype="html">
<target><![CDATA[Message with description]]></target>
<note priority="1" from="comment">Description is comment from developers to translators</note>
</trans-unit>
<trans-unit id="veryLongString" datatype="html">
<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>
</trans-unit>
<trans-unit id="stringWithPlaceholders" datatype="html">
<source><![CDATA[Hello {world}]]></source>
</trans-unit>
<trans-unit id="stringWithJsxPlaceholders" datatype="html">
<source><![CDATA[Hello <0>String</0>]]></source>
</trans-unit>
</body>
</file>
</xliff>
`;
93 changes: 93 additions & 0 deletions packages/format-xliff/src/xliff.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { formatter as createFormatter } from "./xliff"
import { CatalogType } from "@lingui/conf"

describe("xliff format", () => {
it("should write catalog in pofile format", async () => {
const format = createFormatter({ origins: true })

const catalog: CatalogType = {
static: {
translation: "Static message",
},
withOrigin: {
translation: "Message with origin",
origin: [["src/App.js", 4]],
},
withContext: {
translation: "Message with context",
context: "my context",
},
Dgzql1: {
message: "with generated id",
translation: "",
context: "my context",
},
withMultipleOrigins: {
translation: "Message with multiple origin",
origin: [
["src/App.js", 4],
["src/Component.js", 2],
],
},
withDescription: {
translation: "Message with description",
comments: ["Description is comment from developers to translators"],
},
// obsolete: {
// translation: "Obsolete message",
// obsolete: true,
// },
// withFlags: {
// flags: ["fuzzy", "otherFlag"],
// translation: "Keeps any flags that are defined",
// },
veryLongString: {
translation:
"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",
},

stringWithPlaceholders: {
message: "Hello {world}",
translation: null,
},

stringWithJsxPlaceholders: {
message: "Hello <0>String</0>",
translation: null,
},
}

const actual = format.serialize(catalog, {
locale: "en",
existing: null,
})
expect(actual).toMatchSnapshot()
})

it.todo(
"should read catalog in xliff format" /*, () => {
const format = createFormatter()
const pofile = fs
.readFileSync(path.join(__dirname, "fixtures/messages.po"))
.toString()
const actual = format.parse(pofile)
expect(actual).toMatchSnapshot()
}*/
)

it.todo("should preserve attributes stored on trans unit")
it.todo("should preserve children in trans unit")

it.todo("should not include origins if origins option is false")

it.todo("should not include lineNumbers if lineNumbers option is false")
})
Loading

0 comments on commit 68e5fb6

Please sign in to comment.