@l10nmonster/helpers-java 1.0.0

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.
Files changed (4) hide show
  1. package/README.md +18 -0
  2. package/filter.js +56 -0
  3. package/index.js +51 -0
  4. package/package.json +17 -0
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # L10n Monster Java Helpers
2
+
3
+ |Module|Export|Description|
4
+ |---|---|---|
5
+ |`helpers-java`|`escapesDecoder`|Decoder for escaped chars like `\n` and `\u00a0`.|
6
+ |`helpers-java`|`MFQuotesDecoder`|Decoder for dealing with quotes in MessageFormat strings.|
7
+ |`helpers-java`|`escapesEncoder`|Encoder for escaped chars like `\n`.|
8
+ |`helpers-java`|`MFQuotesEncoder`|Encoder for dealing with quotes in MessageFormat strings.|
9
+
10
+ ### Java Properties Filter
11
+
12
+ ```js
13
+ this.resourceFilter = new filters.JavaPropertiesFilter();
14
+ ```
15
+
16
+ A filter for properties files used as defined by the Java resource bundle specification.
17
+
18
+ * [TODO] it needs an option to make it stricter to deal with technically invalid files.
package/filter.js ADDED
@@ -0,0 +1,56 @@
1
+ const { parseToEntries, stringifyFromEntries } = require('@js.properties/properties');
2
+
3
+ module.exports = class JavaPropertiesFilter {
4
+ async parseResource({ resource }) {
5
+ const parsedResource = parseToEntries(resource, { sep: true, eol: true, all: true, original: true, location: true });
6
+ const segments = [];
7
+ let previousComments = [];
8
+ for (const e of parsedResource) {
9
+ if (e.key && e.sep.trim() === '=') {
10
+ const location = {startLine: e.location.start.line, endLine: e.location.end.line}
11
+ const seg = {
12
+ sid: e.key,
13
+ str: e.element,
14
+ location
15
+ };
16
+
17
+ if (previousComments.length > 0) {
18
+ const notes = previousComments.join('\n');
19
+ if (notes.indexOf('DO_NOT_TRANSLATE') === -1) {
20
+ segments.push({
21
+ ...seg,
22
+ notes,
23
+ });
24
+ }
25
+ previousComments = [];
26
+ } else {
27
+ segments.push(seg);
28
+ }
29
+ } else {
30
+ e.original.trim().length > 0 && previousComments.push(e.original);
31
+ }
32
+ }
33
+ return {
34
+ segments,
35
+ };
36
+ }
37
+
38
+ async translateResource({ resource, translator }) {
39
+ const parsedResource = parseToEntries(resource, { sep: true, eol: true, all: true, original: true });
40
+ const translatedEntries = [];
41
+ for (const entry of parsedResource) {
42
+ if (entry.key) {
43
+ const translation = await translator(entry.key, entry.element);
44
+ if (translation !== undefined) {
45
+ // eslint-disable-next-line no-unused-vars
46
+ const { original, element, ...rest } = entry;
47
+ translatedEntries.push({
48
+ ...rest,
49
+ element: translation,
50
+ })
51
+ }
52
+ }
53
+ }
54
+ return stringifyFromEntries(translatedEntries);
55
+ }
56
+ }
package/index.js ADDED
@@ -0,0 +1,51 @@
1
+ const { regex } = require('@l10nmonster/helpers');
2
+
3
+ exports.PropertiesFilter = require('./filter');
4
+
5
+ const javaControlCharsToDecode = {
6
+ t: '\t',
7
+ b: '\b',
8
+ n: '\n',
9
+ r: '\r',
10
+ f: '\f',
11
+ };
12
+ exports.escapesDecoder = regex.decoderMaker(
13
+ 'javaEscapesDecoder',
14
+ /(?<node>\\(?<escapedChar>['"\\])|\\(?<escapedControl>[tbnrf])|\\u(?<codePoint>[0-9A-Za-z]{4}))/g,
15
+ (groups) => (groups.escapedChar ??
16
+ (groups.escapedControl ?
17
+ (javaControlCharsToDecode[groups.escapedControl] ?? `\\${groups.escapedControl}`) :
18
+ String.fromCharCode(parseInt(groups.codePoint, 16))
19
+ )
20
+ )
21
+ );
22
+
23
+ // TODO: do we need to escape also those escapedChar that we decoded?
24
+ exports.escapesEncoder = regex.encoderMaker(
25
+ 'javaEscapesEncoder',
26
+ // eslint-disable-next-line prefer-named-capture-group
27
+ /(\t)|(\n)|(\r)|(\f)|(\u00a0)/g,
28
+ {
29
+ '\t': '\\t',
30
+ '\n': '\\n',
31
+ '\r': '\\r',
32
+ '\f': '\\f',
33
+ '\u00a0': '\\u00a0',
34
+ }
35
+ );
36
+
37
+ exports.MFQuotesDecoder = regex.decoderMaker(
38
+ 'javaMFQuotesDecoder',
39
+ /(?:(?<quote>')'|(?:'(?<quoted>[^']+)'))/g,
40
+ groups => groups.quote ?? groups.quoted
41
+ );
42
+
43
+ // need to be smart about detecting whether MessageFormat was used or not based on presence of {vars}
44
+ exports.MFQuotesEncoder = regex.encoderMaker(
45
+ 'javaMFQuotesEncoder',
46
+ // eslint-disable-next-line prefer-named-capture-group
47
+ /(')/g,
48
+ {
49
+ "'": "''",
50
+ }
51
+ );
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@l10nmonster/helpers-java",
3
+ "version": "1.0.0",
4
+ "description": "Helpers to deal with Java file formats",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "author": "Diego Lagunas",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "@js.properties/properties": "^0.5.4"
13
+ },
14
+ "peerDependencies": {
15
+ "@l10nmonster/helpers": "^1"
16
+ }
17
+ }