@lingui/format-po 4.0.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -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
package/dist/po.cjs ADDED
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+
3
+ const dateFns = require('date-fns');
4
+ const PO = require('pofile');
5
+ const api = require('@lingui/cli/api');
6
+
7
+ const splitOrigin = (origin) => {
8
+ const [file, line] = origin.split(":");
9
+ return [file, line ? Number(line) : null];
10
+ };
11
+ const joinOrigin = (origin) => origin.join(":");
12
+ function isGeneratedId(id, message) {
13
+ return id === api.generateMessageId(message.message, message.context);
14
+ }
15
+ function getCreateHeaders(language) {
16
+ return {
17
+ "POT-Creation-Date": dateFns.format(/* @__PURE__ */ new Date(), "yyyy-MM-dd HH:mmxxxx"),
18
+ "MIME-Version": "1.0",
19
+ "Content-Type": "text/plain; charset=utf-8",
20
+ "Content-Transfer-Encoding": "8bit",
21
+ "X-Generator": "@lingui/cli",
22
+ ...language ? { Language: language } : {}
23
+ };
24
+ }
25
+ const EXPLICIT_ID_FLAG = "explicit-id";
26
+ const serialize = (catalog, options) => {
27
+ return Object.keys(catalog).map((id) => {
28
+ const message = catalog[id];
29
+ const item = new PO.Item();
30
+ item.extractedComments = [...message.extractedComments || []];
31
+ item.flags = (message.flags || []).reduce((acc, flag) => {
32
+ acc[flag] = true;
33
+ return acc;
34
+ }, {});
35
+ const _isGeneratedId = isGeneratedId(id, message);
36
+ if (_isGeneratedId) {
37
+ item.msgid = message.message;
38
+ if (options.printLinguiId) {
39
+ if (!item.extractedComments.find((c) => c.includes("js-lingui-id"))) {
40
+ item.extractedComments.push(`js-lingui-id: ${id}`);
41
+ }
42
+ }
43
+ } else {
44
+ item.flags[EXPLICIT_ID_FLAG] = true;
45
+ item.msgid = id;
46
+ }
47
+ if (message.context) {
48
+ item.msgctxt = message.context;
49
+ }
50
+ item.msgstr = [message.translation];
51
+ item.comments = message.comments || [];
52
+ if (options.origins !== false) {
53
+ if (message.origin && options.lineNumbers === false) {
54
+ item.references = message.origin.map(([path]) => path);
55
+ } else {
56
+ item.references = message.origin ? message.origin.map(joinOrigin) : [];
57
+ }
58
+ }
59
+ item.obsolete = message.obsolete;
60
+ return item;
61
+ });
62
+ };
63
+ function deserialize(items) {
64
+ return items.reduce((catalog, item) => {
65
+ const message = {
66
+ translation: item.msgstr[0],
67
+ extractedComments: item.extractedComments || [],
68
+ comments: item.comments || [],
69
+ context: item.msgctxt ?? null,
70
+ obsolete: item.flags.obsolete || item.obsolete,
71
+ origin: (item.references || []).map((ref) => splitOrigin(ref)),
72
+ flags: Object.keys(item.flags).map((flag) => flag.trim())
73
+ };
74
+ let id = item.msgid;
75
+ if (!item.flags[EXPLICIT_ID_FLAG]) {
76
+ id = api.generateMessageId(item.msgid, item.msgctxt);
77
+ message.message = item.msgid;
78
+ }
79
+ catalog[id] = message;
80
+ return catalog;
81
+ }, {});
82
+ }
83
+ function formatter(options = {}) {
84
+ options = {
85
+ origins: true,
86
+ lineNumbers: true,
87
+ ...options
88
+ };
89
+ return {
90
+ catalogExtension: ".po",
91
+ templateExtension: ".pot",
92
+ parse(content) {
93
+ const po = PO.parse(content);
94
+ return deserialize(po.items);
95
+ },
96
+ serialize(catalog, ctx) {
97
+ let po;
98
+ if (ctx.existing) {
99
+ po = PO.parse(ctx.existing);
100
+ } else {
101
+ po = new PO();
102
+ po.headers = getCreateHeaders(ctx.locale);
103
+ po.headerOrder = Object.keys(po.headers);
104
+ }
105
+ po.items = serialize(catalog, options);
106
+ return po.toString();
107
+ }
108
+ };
109
+ }
110
+
111
+ exports.formatter = formatter;
package/dist/po.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { CatalogType } from '@lingui/conf';
2
+
3
+ type PoFormatterOptions = {
4
+ /**
5
+ * Print places where message is used
6
+ *
7
+ * @default true
8
+ */
9
+ origins?: boolean;
10
+ /**
11
+ * Print line numbers in origins
12
+ *
13
+ * @default true
14
+ */
15
+ lineNumbers?: boolean;
16
+ /**
17
+ * Print `js-lingui-id: Xs4as` statement in extracted comments section
18
+ *
19
+ * @default false
20
+ */
21
+ printLinguiId?: boolean;
22
+ };
23
+ declare function formatter(options?: PoFormatterOptions): {
24
+ catalogExtension: string;
25
+ templateExtension: string;
26
+ parse(content: string): CatalogType;
27
+ serialize(catalog: CatalogType, ctx: {
28
+ locale: string;
29
+ existing: string;
30
+ }): string;
31
+ };
32
+
33
+ export { PoFormatterOptions, formatter };
package/dist/po.mjs ADDED
@@ -0,0 +1,109 @@
1
+ import { format } from 'date-fns';
2
+ import PO from 'pofile';
3
+ import { generateMessageId } from '@lingui/cli/api';
4
+
5
+ const splitOrigin = (origin) => {
6
+ const [file, line] = origin.split(":");
7
+ return [file, line ? Number(line) : null];
8
+ };
9
+ const joinOrigin = (origin) => origin.join(":");
10
+ function isGeneratedId(id, message) {
11
+ return id === generateMessageId(message.message, message.context);
12
+ }
13
+ function getCreateHeaders(language) {
14
+ return {
15
+ "POT-Creation-Date": format(/* @__PURE__ */ new Date(), "yyyy-MM-dd HH:mmxxxx"),
16
+ "MIME-Version": "1.0",
17
+ "Content-Type": "text/plain; charset=utf-8",
18
+ "Content-Transfer-Encoding": "8bit",
19
+ "X-Generator": "@lingui/cli",
20
+ ...language ? { Language: language } : {}
21
+ };
22
+ }
23
+ const EXPLICIT_ID_FLAG = "explicit-id";
24
+ const serialize = (catalog, options) => {
25
+ return Object.keys(catalog).map((id) => {
26
+ const message = catalog[id];
27
+ const item = new PO.Item();
28
+ item.extractedComments = [...message.extractedComments || []];
29
+ item.flags = (message.flags || []).reduce((acc, flag) => {
30
+ acc[flag] = true;
31
+ return acc;
32
+ }, {});
33
+ const _isGeneratedId = isGeneratedId(id, message);
34
+ if (_isGeneratedId) {
35
+ item.msgid = message.message;
36
+ if (options.printLinguiId) {
37
+ if (!item.extractedComments.find((c) => c.includes("js-lingui-id"))) {
38
+ item.extractedComments.push(`js-lingui-id: ${id}`);
39
+ }
40
+ }
41
+ } else {
42
+ item.flags[EXPLICIT_ID_FLAG] = true;
43
+ item.msgid = id;
44
+ }
45
+ if (message.context) {
46
+ item.msgctxt = message.context;
47
+ }
48
+ item.msgstr = [message.translation];
49
+ item.comments = message.comments || [];
50
+ if (options.origins !== false) {
51
+ if (message.origin && options.lineNumbers === false) {
52
+ item.references = message.origin.map(([path]) => path);
53
+ } else {
54
+ item.references = message.origin ? message.origin.map(joinOrigin) : [];
55
+ }
56
+ }
57
+ item.obsolete = message.obsolete;
58
+ return item;
59
+ });
60
+ };
61
+ function deserialize(items) {
62
+ return items.reduce((catalog, item) => {
63
+ const message = {
64
+ translation: item.msgstr[0],
65
+ extractedComments: item.extractedComments || [],
66
+ comments: item.comments || [],
67
+ context: item.msgctxt ?? null,
68
+ obsolete: item.flags.obsolete || item.obsolete,
69
+ origin: (item.references || []).map((ref) => splitOrigin(ref)),
70
+ flags: Object.keys(item.flags).map((flag) => flag.trim())
71
+ };
72
+ let id = item.msgid;
73
+ if (!item.flags[EXPLICIT_ID_FLAG]) {
74
+ id = generateMessageId(item.msgid, item.msgctxt);
75
+ message.message = item.msgid;
76
+ }
77
+ catalog[id] = message;
78
+ return catalog;
79
+ }, {});
80
+ }
81
+ function formatter(options = {}) {
82
+ options = {
83
+ origins: true,
84
+ lineNumbers: true,
85
+ ...options
86
+ };
87
+ return {
88
+ catalogExtension: ".po",
89
+ templateExtension: ".pot",
90
+ parse(content) {
91
+ const po = PO.parse(content);
92
+ return deserialize(po.items);
93
+ },
94
+ serialize(catalog, ctx) {
95
+ let po;
96
+ if (ctx.existing) {
97
+ po = PO.parse(ctx.existing);
98
+ } else {
99
+ po = new PO();
100
+ po.headers = getCreateHeaders(ctx.locale);
101
+ po.headerOrder = Object.keys(po.headers);
102
+ }
103
+ po.items = serialize(catalog, options);
104
+ return po.toString();
105
+ }
106
+ };
107
+ }
108
+
109
+ export { formatter };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@lingui/format-po",
3
+ "version": "4.0.0-next.2",
4
+ "description": "Gettext PO format for Lingui Catalogs",
5
+ "main": "./dist/po.cjs",
6
+ "module": "./dist/po.mjs",
7
+ "types": "./dist/po.d.ts",
8
+ "license": "MIT",
9
+ "keywords": [
10
+ "i18n",
11
+ "lingui-format",
12
+ "lingui-formatter",
13
+ "gettext",
14
+ "po",
15
+ "pot",
16
+ "internationalization",
17
+ "i10n",
18
+ "localization",
19
+ "i9n",
20
+ "translation"
21
+ ],
22
+ "scripts": {
23
+ "build": "rimraf ./dist && unbuild",
24
+ "stub": "unbuild --stub"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/lingui/js-lingui.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/lingui/js-lingui/issues"
32
+ },
33
+ "engines": {
34
+ "node": ">=16.0.0"
35
+ },
36
+ "files": [
37
+ "LICENSE",
38
+ "README.md",
39
+ "dist/"
40
+ ],
41
+ "dependencies": {
42
+ "@lingui/cli": "4.0.0-next.2",
43
+ "@lingui/conf": "4.0.0-next.2",
44
+ "date-fns": "^2.29.3",
45
+ "pofile": "^1.1.4"
46
+ },
47
+ "devDependencies": {
48
+ "@lingui/jest-mocks": "workspace:^",
49
+ "unbuild": "^1.1.2"
50
+ }
51
+ }