@l10nmonster/helpers-java 3.0.0-alpha.9 → 3.0.1
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/.releaserc.json +1 -1
- package/CHANGELOG.md +9 -0
- package/filter.js +190 -12
- package/package.json +2 -2
package/.releaserc.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## @l10nmonster/helpers-java [3.0.1](https://public-github/l10nmonster/l10nmonster/compare/@l10nmonster/helpers-java@3.0.0...@l10nmonster/helpers-java@3.0.1) (2025-12-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* Add proper pluralization expansion support ([3d062bb](https://public-github/l10nmonster/l10nmonster/commit/3d062bbb3272c61e969b419a56a7a5e347ab96c6))
|
|
7
|
+
* Pluralization improvements ([5964250](https://public-github/l10nmonster/l10nmonster/commit/596425092c425cc8d6c312ef58509c4c3c537431))
|
|
8
|
+
* **server:** Fix cart cleanup ([9bbcab9](https://public-github/l10nmonster/l10nmonster/commit/9bbcab93e1fd20aeb09f59c828665159f091f37c))
|
|
9
|
+
|
|
1
10
|
# Changelog
|
|
2
11
|
|
|
3
12
|
All notable changes to this project will be documented in this file.
|
package/filter.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { parseToEntries, stringifyFromEntries } from '@js.properties/properties';
|
|
2
2
|
|
|
3
3
|
export default class JavaPropertiesFilter {
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* @param {Object} [params] - Configuration options
|
|
6
|
+
* @param {boolean} [params.enablePluralizationSuffixes=false] - Enable detection of plural forms via key suffixes (_one, _other, etc.)
|
|
7
|
+
*/
|
|
8
|
+
constructor(params) {
|
|
9
|
+
this.enablePluralizationSuffixes = params?.enablePluralizationSuffixes || false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async parseResource({ resource, sourcePluralForms, targetPluralForms }) {
|
|
5
13
|
const parsedResource = parseToEntries(resource, { sep: true, eol: true, all: true, original: true, location: true });
|
|
6
14
|
const segments = [];
|
|
7
15
|
let previousComments = [];
|
|
16
|
+
|
|
17
|
+
// First pass: collect all segments
|
|
8
18
|
for (const e of parsedResource) {
|
|
9
19
|
if (e.key && e.sep.trim() === '=') {
|
|
10
20
|
const location = {startLine: e.location.start.line, endLine: e.location.end.line}
|
|
@@ -30,24 +40,192 @@ export default class JavaPropertiesFilter {
|
|
|
30
40
|
e.original.trim().length > 0 && previousComments.push(e.original);
|
|
31
41
|
}
|
|
32
42
|
}
|
|
43
|
+
|
|
44
|
+
// Second pass: detect and mark plural forms (only if enabled)
|
|
45
|
+
if (this.enablePluralizationSuffixes) {
|
|
46
|
+
const targetFormsSet = new Set(targetPluralForms);
|
|
47
|
+
const potentialPluralGroups = new Map(); // baseKey -> Map(suffix -> segment)
|
|
48
|
+
|
|
49
|
+
for (const seg of segments) {
|
|
50
|
+
const underscoreIdx = seg.sid.lastIndexOf('_');
|
|
51
|
+
if (underscoreIdx !== -1) {
|
|
52
|
+
const suffix = seg.sid.slice(underscoreIdx + 1);
|
|
53
|
+
if (targetFormsSet.has(suffix)) {
|
|
54
|
+
const baseKey = seg.sid.slice(0, underscoreIdx);
|
|
55
|
+
if (!potentialPluralGroups.has(baseKey)) {
|
|
56
|
+
potentialPluralGroups.set(baseKey, new Map());
|
|
57
|
+
}
|
|
58
|
+
potentialPluralGroups.get(baseKey).set(suffix, seg);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// For groups with all required source forms: set pluralForm and generate missing forms
|
|
64
|
+
// We need to reorder segments so all forms of a plural group are together in CLDR order
|
|
65
|
+
const pluralGroupsToReorder = new Map(); // baseKey -> forms Map
|
|
66
|
+
|
|
67
|
+
for (const [baseKey, forms] of potentialPluralGroups) {
|
|
68
|
+
const hasAllForms = sourcePluralForms.every(form => forms.has(form));
|
|
69
|
+
if (!hasAllForms) continue;
|
|
70
|
+
|
|
71
|
+
// Set pluralForm on existing segments
|
|
72
|
+
for (const [suffix, seg] of forms) {
|
|
73
|
+
seg.pluralForm = suffix;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Generate missing forms from _other
|
|
77
|
+
const other = forms.get('other');
|
|
78
|
+
for (const suffix of targetPluralForms) {
|
|
79
|
+
if (!forms.has(suffix)) {
|
|
80
|
+
forms.set(suffix, {
|
|
81
|
+
sid: `${baseKey}_${suffix}`,
|
|
82
|
+
str: other.str,
|
|
83
|
+
pluralForm: suffix,
|
|
84
|
+
...(other.notes && { notes: other.notes }),
|
|
85
|
+
...(other.location && { location: other.location })
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
pluralGroupsToReorder.set(baseKey, forms);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Rebuild segments with plural groups in correct order
|
|
94
|
+
if (pluralGroupsToReorder.size > 0) {
|
|
95
|
+
const newSegments = [];
|
|
96
|
+
const processedPluralKeys = new Set();
|
|
97
|
+
|
|
98
|
+
for (const seg of segments) {
|
|
99
|
+
const underscoreIdx = seg.sid.lastIndexOf('_');
|
|
100
|
+
const suffix = underscoreIdx !== -1 ? seg.sid.slice(underscoreIdx + 1) : null;
|
|
101
|
+
const baseKey = underscoreIdx !== -1 ? seg.sid.slice(0, underscoreIdx) : null;
|
|
102
|
+
|
|
103
|
+
if (baseKey && pluralGroupsToReorder.has(baseKey) && !processedPluralKeys.has(baseKey)) {
|
|
104
|
+
// Insert all forms of this plural group in CLDR order
|
|
105
|
+
processedPluralKeys.add(baseKey);
|
|
106
|
+
const forms = pluralGroupsToReorder.get(baseKey);
|
|
107
|
+
for (const form of targetPluralForms) {
|
|
108
|
+
if (forms.has(form)) {
|
|
109
|
+
newSegments.push(forms.get(form));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} else if (!baseKey || !pluralGroupsToReorder.has(baseKey)) {
|
|
113
|
+
// Non-plural segment
|
|
114
|
+
newSegments.push(seg);
|
|
115
|
+
}
|
|
116
|
+
// Skip plural segments that were already added via the group
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { segments: newSegments };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
33
123
|
return {
|
|
34
124
|
segments,
|
|
35
125
|
};
|
|
36
126
|
}
|
|
37
127
|
|
|
38
|
-
async translateResource({ resource, translator }) {
|
|
128
|
+
async translateResource({ resource, translator, sourcePluralForms, targetPluralForms }) {
|
|
39
129
|
const parsedResource = parseToEntries(resource, { sep: true, eol: true, all: true, original: true });
|
|
40
130
|
const translatedEntries = [];
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
131
|
+
|
|
132
|
+
if (this.enablePluralizationSuffixes) {
|
|
133
|
+
const targetFormsSet = new Set(targetPluralForms);
|
|
134
|
+
|
|
135
|
+
// First pass: identify plural groups and collect entries
|
|
136
|
+
const potentialPluralGroups = new Map(); // baseKey -> Map(suffix -> entry)
|
|
137
|
+
|
|
138
|
+
for (const entry of parsedResource) {
|
|
139
|
+
if (entry.key) {
|
|
140
|
+
const underscoreIdx = entry.key.lastIndexOf('_');
|
|
141
|
+
if (underscoreIdx !== -1) {
|
|
142
|
+
const suffix = entry.key.slice(underscoreIdx + 1);
|
|
143
|
+
if (targetFormsSet.has(suffix)) {
|
|
144
|
+
const baseKey = entry.key.slice(0, underscoreIdx);
|
|
145
|
+
if (!potentialPluralGroups.has(baseKey)) {
|
|
146
|
+
potentialPluralGroups.set(baseKey, new Map());
|
|
147
|
+
}
|
|
148
|
+
potentialPluralGroups.get(baseKey).set(suffix, entry);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Identify valid plural groups (those with all required source forms)
|
|
155
|
+
const validPluralBaseKeys = new Set();
|
|
156
|
+
for (const [baseKey, forms] of potentialPluralGroups) {
|
|
157
|
+
const hasAllForms = sourcePluralForms.every(form => forms.has(form));
|
|
158
|
+
if (hasAllForms) {
|
|
159
|
+
validPluralBaseKeys.add(baseKey);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Second pass: translate entries and handle plurals
|
|
164
|
+
const processedPluralGroups = new Set();
|
|
165
|
+
|
|
166
|
+
for (const entry of parsedResource) {
|
|
167
|
+
if (entry.key) {
|
|
168
|
+
const underscoreIdx = entry.key.lastIndexOf('_');
|
|
169
|
+
const suffix = underscoreIdx !== -1 ? entry.key.slice(underscoreIdx + 1) : null;
|
|
170
|
+
const baseKey = underscoreIdx !== -1 ? entry.key.slice(0, underscoreIdx) : null;
|
|
171
|
+
|
|
172
|
+
// Check if this is part of a valid plural group
|
|
173
|
+
if (baseKey && validPluralBaseKeys.has(baseKey) && targetFormsSet.has(suffix)) {
|
|
174
|
+
// Skip if we already processed this plural group
|
|
175
|
+
if (processedPluralGroups.has(baseKey)) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
processedPluralGroups.add(baseKey);
|
|
179
|
+
|
|
180
|
+
// Get the _other form as template for generating missing forms
|
|
181
|
+
const otherEntry = potentialPluralGroups.get(baseKey).get('other');
|
|
182
|
+
const templateEntry = otherEntry || entry;
|
|
183
|
+
|
|
184
|
+
// Generate all required target forms
|
|
185
|
+
for (const targetSuffix of targetPluralForms) {
|
|
186
|
+
const targetKey = `${baseKey}_${targetSuffix}`;
|
|
187
|
+
const sourceEntry = potentialPluralGroups.get(baseKey).get(targetSuffix) || otherEntry;
|
|
188
|
+
|
|
189
|
+
if (sourceEntry) {
|
|
190
|
+
const translation = await translator(targetKey, sourceEntry.element);
|
|
191
|
+
if (translation !== undefined) {
|
|
192
|
+
// eslint-disable-next-line no-unused-vars
|
|
193
|
+
const { original, element, key, ...rest } = templateEntry;
|
|
194
|
+
translatedEntries.push({
|
|
195
|
+
...rest,
|
|
196
|
+
key: targetKey,
|
|
197
|
+
element: translation,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
// Regular (non-plural) entry
|
|
204
|
+
const translation = await translator(entry.key, entry.element);
|
|
205
|
+
if (translation !== undefined) {
|
|
206
|
+
// eslint-disable-next-line no-unused-vars
|
|
207
|
+
const { original, element, ...rest } = entry;
|
|
208
|
+
translatedEntries.push({
|
|
209
|
+
...rest,
|
|
210
|
+
element: translation,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
// Simple translation without plural handling
|
|
218
|
+
for (const entry of parsedResource) {
|
|
219
|
+
if (entry.key) {
|
|
220
|
+
const translation = await translator(entry.key, entry.element);
|
|
221
|
+
if (translation !== undefined) {
|
|
222
|
+
// eslint-disable-next-line no-unused-vars
|
|
223
|
+
const { original, element, ...rest } = entry;
|
|
224
|
+
translatedEntries.push({
|
|
225
|
+
...rest,
|
|
226
|
+
element: translation,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
51
229
|
}
|
|
52
230
|
}
|
|
53
231
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@l10nmonster/helpers-java",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Helpers to deal with Java file formats",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -13,6 +13,6 @@
|
|
|
13
13
|
"@js.properties/properties": "^0.5.4"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"@l10nmonster/core": "
|
|
16
|
+
"@l10nmonster/core": "3.1.0"
|
|
17
17
|
}
|
|
18
18
|
}
|