@l10nmonster/helpers-java 3.0.0-alpha.15 → 3.0.0-alpha.17
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/filter.js +164 -12
- package/package.json +1 -1
package/filter.js
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import { parseToEntries, stringifyFromEntries } from '@js.properties/properties';
|
|
2
2
|
|
|
3
|
+
const ALL_PLURAL_FORMS = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
|
4
|
+
const DEFAULT_SOURCE_PLURAL_FORMS = ['one', 'other'];
|
|
5
|
+
|
|
3
6
|
export default class JavaPropertiesFilter {
|
|
4
|
-
|
|
7
|
+
/**
|
|
8
|
+
* @param {Object} [params] - Configuration options
|
|
9
|
+
* @param {boolean} [params.enablePluralizationSuffixes=false] - Enable detection of plural forms via key suffixes (_one, _other, etc.)
|
|
10
|
+
*/
|
|
11
|
+
constructor(params) {
|
|
12
|
+
this.enablePluralizationSuffixes = params?.enablePluralizationSuffixes || false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async parseResource({ resource, sourcePluralForms, targetPluralForms }) {
|
|
5
16
|
const parsedResource = parseToEntries(resource, { sep: true, eol: true, all: true, original: true, location: true });
|
|
6
17
|
const segments = [];
|
|
7
18
|
let previousComments = [];
|
|
19
|
+
|
|
20
|
+
// First pass: collect all segments
|
|
8
21
|
for (const e of parsedResource) {
|
|
9
22
|
if (e.key && e.sep.trim() === '=') {
|
|
10
23
|
const location = {startLine: e.location.start.line, endLine: e.location.end.line}
|
|
@@ -30,24 +43,163 @@ export default class JavaPropertiesFilter {
|
|
|
30
43
|
e.original.trim().length > 0 && previousComments.push(e.original);
|
|
31
44
|
}
|
|
32
45
|
}
|
|
46
|
+
|
|
47
|
+
// Second pass: detect and mark plural forms (only if enabled)
|
|
48
|
+
if (this.enablePluralizationSuffixes) {
|
|
49
|
+
const requiredSourceForms = sourcePluralForms ?? DEFAULT_SOURCE_PLURAL_FORMS;
|
|
50
|
+
const requiredTargetForms = targetPluralForms ?? ALL_PLURAL_FORMS;
|
|
51
|
+
const targetFormsSet = new Set(requiredTargetForms);
|
|
52
|
+
const potentialPluralGroups = new Map(); // baseKey -> Map(suffix -> segment)
|
|
53
|
+
|
|
54
|
+
for (const seg of segments) {
|
|
55
|
+
const underscoreIdx = seg.sid.lastIndexOf('_');
|
|
56
|
+
if (underscoreIdx !== -1) {
|
|
57
|
+
const suffix = seg.sid.slice(underscoreIdx + 1);
|
|
58
|
+
if (targetFormsSet.has(suffix)) {
|
|
59
|
+
const baseKey = seg.sid.slice(0, underscoreIdx);
|
|
60
|
+
if (!potentialPluralGroups.has(baseKey)) {
|
|
61
|
+
potentialPluralGroups.set(baseKey, new Map());
|
|
62
|
+
}
|
|
63
|
+
potentialPluralGroups.get(baseKey).set(suffix, seg);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// For groups with all required source forms: set pluralForm and generate missing forms
|
|
69
|
+
for (const [baseKey, forms] of potentialPluralGroups) {
|
|
70
|
+
const hasAllForms = requiredSourceForms.every(form => forms.has(form));
|
|
71
|
+
if (!hasAllForms) continue;
|
|
72
|
+
|
|
73
|
+
// Set pluralForm on existing segments
|
|
74
|
+
for (const [suffix, seg] of forms) {
|
|
75
|
+
seg.pluralForm = suffix;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Generate missing forms from _other
|
|
79
|
+
const other = forms.get('other');
|
|
80
|
+
for (const suffix of requiredTargetForms) {
|
|
81
|
+
if (!forms.has(suffix)) {
|
|
82
|
+
segments.push({
|
|
83
|
+
sid: `${baseKey}_${suffix}`,
|
|
84
|
+
str: other.str,
|
|
85
|
+
pluralForm: suffix,
|
|
86
|
+
...(other.notes && { notes: other.notes }),
|
|
87
|
+
...(other.location && { location: other.location })
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
33
94
|
return {
|
|
34
95
|
segments,
|
|
35
96
|
};
|
|
36
97
|
}
|
|
37
98
|
|
|
38
|
-
async translateResource({ resource, translator }) {
|
|
99
|
+
async translateResource({ resource, translator, sourcePluralForms, targetPluralForms }) {
|
|
39
100
|
const parsedResource = parseToEntries(resource, { sep: true, eol: true, all: true, original: true });
|
|
40
101
|
const translatedEntries = [];
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
102
|
+
|
|
103
|
+
if (this.enablePluralizationSuffixes) {
|
|
104
|
+
// Use provided plural forms or defaults
|
|
105
|
+
const requiredSourceForms = sourcePluralForms ?? DEFAULT_SOURCE_PLURAL_FORMS;
|
|
106
|
+
const requiredTargetForms = targetPluralForms ?? ALL_PLURAL_FORMS;
|
|
107
|
+
const targetFormsSet = new Set(requiredTargetForms);
|
|
108
|
+
|
|
109
|
+
// First pass: identify plural groups and collect entries
|
|
110
|
+
const potentialPluralGroups = new Map(); // baseKey -> Map(suffix -> entry)
|
|
111
|
+
|
|
112
|
+
for (const entry of parsedResource) {
|
|
113
|
+
if (entry.key) {
|
|
114
|
+
const underscoreIdx = entry.key.lastIndexOf('_');
|
|
115
|
+
if (underscoreIdx !== -1) {
|
|
116
|
+
const suffix = entry.key.slice(underscoreIdx + 1);
|
|
117
|
+
if (targetFormsSet.has(suffix)) {
|
|
118
|
+
const baseKey = entry.key.slice(0, underscoreIdx);
|
|
119
|
+
if (!potentialPluralGroups.has(baseKey)) {
|
|
120
|
+
potentialPluralGroups.set(baseKey, new Map());
|
|
121
|
+
}
|
|
122
|
+
potentialPluralGroups.get(baseKey).set(suffix, entry);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Identify valid plural groups (those with all required source forms)
|
|
129
|
+
const validPluralBaseKeys = new Set();
|
|
130
|
+
for (const [baseKey, forms] of potentialPluralGroups) {
|
|
131
|
+
const hasAllForms = requiredSourceForms.every(form => forms.has(form));
|
|
132
|
+
if (hasAllForms) {
|
|
133
|
+
validPluralBaseKeys.add(baseKey);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Second pass: translate entries and handle plurals
|
|
138
|
+
const processedPluralGroups = new Set();
|
|
139
|
+
|
|
140
|
+
for (const entry of parsedResource) {
|
|
141
|
+
if (entry.key) {
|
|
142
|
+
const underscoreIdx = entry.key.lastIndexOf('_');
|
|
143
|
+
const suffix = underscoreIdx !== -1 ? entry.key.slice(underscoreIdx + 1) : null;
|
|
144
|
+
const baseKey = underscoreIdx !== -1 ? entry.key.slice(0, underscoreIdx) : null;
|
|
145
|
+
|
|
146
|
+
// Check if this is part of a valid plural group
|
|
147
|
+
if (baseKey && validPluralBaseKeys.has(baseKey) && targetFormsSet.has(suffix)) {
|
|
148
|
+
// Skip if we already processed this plural group
|
|
149
|
+
if (processedPluralGroups.has(baseKey)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
processedPluralGroups.add(baseKey);
|
|
153
|
+
|
|
154
|
+
// Get the _other form as template for generating missing forms
|
|
155
|
+
const otherEntry = potentialPluralGroups.get(baseKey).get('other');
|
|
156
|
+
const templateEntry = otherEntry || entry;
|
|
157
|
+
|
|
158
|
+
// Generate all required target forms
|
|
159
|
+
for (const targetSuffix of requiredTargetForms) {
|
|
160
|
+
const targetKey = `${baseKey}_${targetSuffix}`;
|
|
161
|
+
const sourceEntry = potentialPluralGroups.get(baseKey).get(targetSuffix) || otherEntry;
|
|
162
|
+
|
|
163
|
+
if (sourceEntry) {
|
|
164
|
+
const translation = await translator(targetKey, sourceEntry.element);
|
|
165
|
+
if (translation !== undefined) {
|
|
166
|
+
// eslint-disable-next-line no-unused-vars
|
|
167
|
+
const { original, element, key, ...rest } = templateEntry;
|
|
168
|
+
translatedEntries.push({
|
|
169
|
+
...rest,
|
|
170
|
+
key: targetKey,
|
|
171
|
+
element: translation,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
// Regular (non-plural) entry
|
|
178
|
+
const translation = await translator(entry.key, entry.element);
|
|
179
|
+
if (translation !== undefined) {
|
|
180
|
+
// eslint-disable-next-line no-unused-vars
|
|
181
|
+
const { original, element, ...rest } = entry;
|
|
182
|
+
translatedEntries.push({
|
|
183
|
+
...rest,
|
|
184
|
+
element: translation,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
// Simple translation without plural handling
|
|
192
|
+
for (const entry of parsedResource) {
|
|
193
|
+
if (entry.key) {
|
|
194
|
+
const translation = await translator(entry.key, entry.element);
|
|
195
|
+
if (translation !== undefined) {
|
|
196
|
+
// eslint-disable-next-line no-unused-vars
|
|
197
|
+
const { original, element, ...rest } = entry;
|
|
198
|
+
translatedEntries.push({
|
|
199
|
+
...rest,
|
|
200
|
+
element: translation,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
51
203
|
}
|
|
52
204
|
}
|
|
53
205
|
}
|