ilib-lint 2.0.1 → 2.1.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.
@@ -1,5 +1,13 @@
1
1
  Release Notes
2
- ============================
2
+ ### v2.1.0
3
+ - fixed a bug where the quote style checker was not converting the highlight quotes properly
4
+ - added an option `output` to write the output to a file.
5
+ - added an option `name` to give the project name. It is useful when the config file is shared in multiple projects.
6
+ - updated to use `formatOutput()` if it is defined in the plugin formatter.
7
+ - fixed quote checker rule to make a special case for Japanese. In Japanese, square brackets
8
+ are now allowed as a quote style, and only the main hockey-stick-style of quotes are
9
+ accepted even if the source has an alternate quote style. The double-hockey sticks are no
10
+ longer accepted.
3
11
 
4
12
  ### v2.0.1
5
13
  - fixed loading of plugins
@@ -1,8 +1,8 @@
1
- # resource-no-fullwidth-digits
1
+ # resource-no-fullwidth-punctuation-subset
2
2
 
3
3
  Ensure that translations do not contain fullwidth punctuation characters. These should be represented as ASCII symbols instead.
4
4
 
5
5
  Examples:
6
6
 
7
7
  Good translation: "本当? はい! 100%"<br>
8
- Bad translation: "本当? はい! 100%"
8
+ Bad translation: "本当? はい! 100%"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilib-lint",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "module": "./src/index.js",
5
5
  "type": "module",
6
6
  "bin": "./src/index.js",
package/src/Project.js CHANGED
@@ -578,46 +578,79 @@ class Project extends DirItem {
578
578
  // source file in order to make it easier for the engineer to fix all the
579
579
  // problems in the source file sequentially.
580
580
  results.sort(ResultComparator);
581
-
581
+ let resultAll;
582
582
  if (results) {
583
+ results.forEach(result => {
584
+ if (result.severity === "error") {
585
+ this.resultStats.errors++;
586
+ } else if (result.severity === "warning") {
587
+ this.resultStats.warnings++;
588
+ } else {
589
+ this.resultStats.suggestions++;
590
+ }
591
+ });
592
+ }
593
+ const fmt = new Intl.NumberFormat("en-US", {
594
+ maxFractionDigits: 2
595
+ });
596
+ const score = this.getScore();
597
+ if (typeof (this.formatter.formatOutput) === "function") {
598
+ resultAll = this.formatter.formatOutput({
599
+ name: this.options.opt.name || this.project.name,
600
+ fileStats: this.fileStats,
601
+ resultStats: this.resultStats,
602
+ results: results,
603
+ score: score,
604
+ time: totalTime,
605
+ errorsOnly : this.options.opt.errorsOnly || false
606
+ });
607
+ } else {
583
608
  results.forEach(result => {
584
609
  const str = this.formatter.format(result);
585
610
  if (str) {
586
611
  if (result.severity === "error") {
587
612
  logger.error(str);
588
- this.resultStats.errors++;
589
613
  } else if (result.severity === "warning") {
590
- this.resultStats.warnings++;
591
614
  if (!this.options.errorsOnly) {
592
615
  logger.warn(str);
593
616
  }
594
617
  } else {
595
- this.resultStats.suggestions++;
596
618
  if (!this.options.errorsOnly) {
597
619
  logger.info(str);
598
620
  }
599
621
  }
600
622
  }
601
623
  });
602
- }
603
624
 
604
- const fmt = new Intl.NumberFormat("en-US", {
605
- maxFractionDigits: 2
606
- });
607
- logger.info(`Total Elapse Time: ${String(totalTime)} seconds`);
608
- logger.info(` ${`Average over`.padEnd(15, ' ')}${`Average over`.padEnd(15, ' ')}${`Average over`.padEnd(15, ' ')}`);
609
- logger.info(` Total ${`${String(this.fileStats.files)} Files`.padEnd(15, ' ')}${`${String(this.fileStats.modules)} Modules`.padEnd(15, ' ')}${`${String(this.fileStats.lines)} Lines`.padEnd(15, ' ')}`);
610
- if (results.length) {
611
- logger.info(
612
- `Errors: ${String(this.resultStats.errors).padEnd(10, ' ')}${fmt.format(this.resultStats.errors/this.fileStats.files).padEnd(15, ' ')}${fmt.format(this.resultStats.errors/this.fileStats.modules).padEnd(15, ' ')}${fmt.format(this.resultStats.errors/this.fileStats.lines).padEnd(15, ' ')}`);
613
- if (!this.options.errorsOnly) {
625
+ logger.info(`Total Elapse Time: ${String(totalTime)} seconds`);
626
+ logger.info(` ${`Average over`.padEnd(15, ' ')}${`Average over`.padEnd(15, ' ')}${`Average over`.padEnd(15, ' ')}`);
627
+ logger.info(` Total ${`${String(this.fileStats.files)} Files`.padEnd(15, ' ')}${`${String(this.fileStats.modules)} Modules`.padEnd(15, ' ')}${`${String(this.fileStats.lines)} Lines`.padEnd(15, ' ')}`);
628
+ if (results.length) {
614
629
  logger.info(
615
- `Warnings: ${String(this.resultStats.warnings).padEnd(10, ' ')}${fmt.format(this.resultStats.warnings/this.fileStats.files).padEnd(15, ' ')}${fmt.format(this.resultStats.warnings/this.fileStats.modules).padEnd(15, ' ')}${fmt.format(this.resultStats.warnings/this.fileStats.lines).padEnd(15, ' ')}`);
616
- logger.info(
617
- `Suggestions: ${String(this.resultStats.suggestions).padEnd(10, ' ')}${fmt.format(this.resultStats.suggestions/this.fileStats.files).padEnd(15, ' ')}${fmt.format(this.resultStats.suggestions/this.fileStats.modules).padEnd(15, ' ')}${fmt.format(this.resultStats.suggestions/this.fileStats.lines).padEnd(15, ' ')}`);
630
+ `Errors: ${String(this.resultStats.errors).padEnd(10, ' ')}${fmt.format(this.resultStats.errors/this.fileStats.files).padEnd(15, ' ')}${fmt.format(this.resultStats.errors/this.fileStats.modules).padEnd(15, ' ')}${fmt.format(this.resultStats.errors/this.fileStats.lines).padEnd(15, ' ')}`);
631
+ if (!this.options.errorsOnly) {
632
+ logger.info(
633
+ `Warnings: ${String(this.resultStats.warnings).padEnd(10, ' ')}${fmt.format(this.resultStats.warnings/this.fileStats.files).padEnd(15, ' ')}${fmt.format(this.resultStats.warnings/this.fileStats.modules).padEnd(15, ' ')}${fmt.format(this.resultStats.warnings/this.fileStats.lines).padEnd(15, ' ')}`);
634
+ logger.info(
635
+ `Suggestions: ${String(this.resultStats.suggestions).padEnd(10, ' ')}${fmt.format(this.resultStats.suggestions/this.fileStats.files).padEnd(15, ' ')}${fmt.format(this.resultStats.suggestions/this.fileStats.modules).padEnd(15, ' ')}${fmt.format(this.resultStats.suggestions/this.fileStats.lines).padEnd(15, ' ')}`);
636
+ }
618
637
  }
619
638
  }
620
- const score = this.getScore();
639
+
640
+ if (this.options.opt.output) {
641
+ let file = this.options.opt.output;
642
+ let fileDir = path.dirname(file);
643
+ if (!fs.existsSync(fileDir)) {
644
+ fs.mkdirSync(fileDir);
645
+ }
646
+ fs.writeFileSync(file, resultAll, "utf8");
647
+ if (!this.options.opt.quiet && this.options.opt.progressInfo) {
648
+ logger.info("Wrote the results to file " + file);
649
+ }
650
+ } else {
651
+ logger.info(resultAll);
652
+ }
653
+
621
654
  logger.info(`I18N Score (0-100) ${fmt.format(score)}`);
622
655
 
623
656
  if (this.options.opt["max-errors"]) {
@@ -633,6 +666,7 @@ class Project extends DirItem {
633
666
  } else {
634
667
  exitValue = this.resultStats.errors > 0 ? 2 : ((this.resultStats.warnings > 0) ? 1 : 0);
635
668
  }
669
+
636
670
  return exitValue;
637
671
  }
638
672
 
package/src/index.js CHANGED
@@ -130,6 +130,16 @@ const optionConfig = {
130
130
  varName: "NUMBER",
131
131
  help: "Give the minimum acceptable I18N score allowed in this run. Valid values are 0-100. Default: no minimum",
132
132
  type: validateInt.bind(null, "min-score")
133
+ },
134
+ output: {
135
+ short: "o",
136
+ varName: "fileName",
137
+ help: "Allows you to write the output into a file."
138
+ },
139
+ name: {
140
+ short: "n",
141
+ varName: "projectName",
142
+ help: "Give the project name. It is useful when the config file is shared in multiple projects."
133
143
  }
134
144
  };
135
145
 
@@ -254,4 +264,4 @@ try {
254
264
  process.exit(exitValue);
255
265
  } catch (e) {
256
266
  logger.error(e);
257
- }
267
+ }
@@ -61,7 +61,7 @@ export const regexRules = [
61
61
  description: "Ensure that the target does not contain any full-width Latin characters.",
62
62
  note: "The full-width characters '{matchString}' are not allowed in the target string. Use ASCII letters instead.",
63
63
  regexps: [ "[\\uFF21-\\uFF3A\\uFF41-\\uFF5A]+" ],
64
- link: "https://github.com/ilib-js/ilib-lint/blob/main/docs/resource-no-fullwidth.md"
64
+ link: "https://github.com/ilib-js/ilib-lint/blob/main/docs/resource-no-fullwidth-latin.md"
65
65
  },
66
66
  {
67
67
  type: "resource-target",
@@ -24,7 +24,7 @@ import ResourceRule from './ResourceRule.js';
24
24
 
25
25
  /** @ignore @typedef {import("ilib-tools-common").Resource} Resource */
26
26
 
27
- /**
27
+ /**
28
28
  * @typedef BaseRegExpCollection
29
29
  * @type {object}
30
30
  * @prop {string} quoteStart
@@ -33,7 +33,7 @@ import ResourceRule from './ResourceRule.js';
33
33
  * @prop {string} quoteEndAlt
34
34
  * @prop {RegExp} quotesNative
35
35
  * @prop {RegExp} quotesNativeAlt
36
- *
36
+ *
37
37
  * @typedef {BaseRegExpCollection} SourceRegExpCollection
38
38
  */
39
39
 
@@ -44,7 +44,7 @@ import ResourceRule from './ResourceRule.js';
44
44
  * @prop {RegExp} quotesAllAlt
45
45
  * @prop {string} nonQuoteChars
46
46
  * @prop {string} nonQuoteCharsAlt
47
- *
47
+ *
48
48
  * @typedef {BaseRegExpCollection & ExtendedRegExpCollection} TargetRegExpCollection
49
49
  */
50
50
 
@@ -77,7 +77,7 @@ let /** @type {{[locale: string]: LocaleInfo}} */ LICache = {};
77
77
  * One-param rule configuration.
78
78
  */
79
79
 
80
- /**
80
+ /**
81
81
  * @typedef Configuration
82
82
  * @type {Modes}
83
83
  * Parameters that can be set through rule configuration file.
@@ -89,8 +89,8 @@ let /** @type {{[locale: string]: LocaleInfo}} */ LICache = {};
89
89
  class ResourceQuoteStyle extends ResourceRule {
90
90
  /**
91
91
  * Make a new rule instance.
92
- *
93
- * @param {object} [options]
92
+ *
93
+ * @param {object} [options]
94
94
  * @param {string} [options.sourceLocale]
95
95
  * @param {Configuration} [options.param]
96
96
  */
@@ -100,20 +100,20 @@ class ResourceQuoteStyle extends ResourceRule {
100
100
  this.description = "Ensure that the proper quote characters are used in translated resources";
101
101
  this.sourceLocale = (options && options.sourceLocale) || "en-US";
102
102
  this.link = "https://github.com/ilib-js/ilib-lint/blob/main/docs/resource-quote-style.md";
103
-
103
+
104
104
  if (options?.param === "localeOnly") {
105
105
  // only localized quotes are allowed in the target string
106
106
  this.localeOnly = true;
107
107
  }
108
-
108
+
109
109
  if (!this.skipLocales) {
110
110
  this.skipLocales = new Set();
111
111
  }
112
-
112
+
113
113
  [
114
114
  "sv", // According to the MS Style guidelines, quotes are usually not required in Swedish when the source English text contains quotes
115
115
  "it", // Based on feedback from linguists quotes in Italian are not required to be the guillemets, even though CLDR says so
116
- ].forEach(locale => this.skipLocales.add(locale));
116
+ ].forEach(locale => this.skipLocales.add(locale));
117
117
  }
118
118
 
119
119
  /**
@@ -136,10 +136,10 @@ class ResourceQuoteStyle extends ResourceRule {
136
136
 
137
137
  // used in results to show what the expected quote style is
138
138
  const targetQuoteStyleExample = (sourceStyle.ascii || sourceStyle.native) ?
139
- `${regExps.target.quoteStart}text${regExps.target.quoteEnd}` :
140
- `${regExps.target.quoteStartAlt}text${regExps.target.quoteEndAlt}`;
141
-
142
- // verify that corresponding quote style is present in target
139
+ `${regExps.target.quoteStart[0]}text${regExps.target.quoteEnd[0]}` :
140
+ `${regExps.target.quoteStartAlt[0]}text${regExps.target.quoteEndAlt[0]}`;
141
+
142
+ // verify that corresponding quote style is present in target
143
143
  // otherwise, construct regexps to pinpoint violation positions
144
144
  // (for highlighting purposes)
145
145
  let startQuote, endQuote;
@@ -149,7 +149,7 @@ class ResourceQuoteStyle extends ResourceRule {
149
149
  endQuote = new RegExp(`([\\p{Letter}\\}])([${regExps.target.nonQuoteChars}'])(\\W|$)`, "gu");
150
150
  } else if (sourceStyle.asciiAlt) {
151
151
  if (regExps.target.quotesAllAlt.test(tar)) return;
152
- startQuote = new RegExp(`(^|\\W)[${regExps.target.nonQuoteCharsAlt}"]([\\p{Letter}\\{])`, "gu");
152
+ startQuote = new RegExp(`(^|\\W)([${regExps.target.nonQuoteCharsAlt}"])([\\p{Letter}\\{])`, "gu");
153
153
  endQuote = new RegExp(`([\\p{Letter}\\}])([${regExps.target.nonQuoteCharsAlt}"])(\\W|$)`, "gu");
154
154
  } else if (sourceStyle.native) {
155
155
  if (regExps.target.quotesNative.test(tar)) return;
@@ -202,12 +202,12 @@ class ResourceQuoteStyle extends ResourceRule {
202
202
  getRegExps(locale) {
203
203
  // superset of all the non-ASCII start and end chars used in CLDR
204
204
  const quoteChars = "«»‘“”„「」’‚‹›『』";
205
-
205
+
206
206
  // shared between all locales since there is nothing locale-specific in here
207
207
  const quotesAscii = new RegExp(`((^|\\W)"\\s?[\\p{Letter}\\{]|[\\p{Letter}\\}]\\s?"(\\W|$))`, "gu");
208
208
  // leave out the "s" before the final quote to take care of plural possessives (eg. my colleagues' files.)
209
209
  const quotesAsciiAlt = new RegExp(`((^|\\W)'\\s?[\\p{Letter}\\{]|[a-rt-zA-RT-Z\\}]\\s?'(\\W|$))`, "gu");
210
-
210
+
211
211
  // locale info object will tell us the quote chars for the locale
212
212
  let li = LICache[locale];
213
213
  if (!li) {
@@ -216,7 +216,7 @@ class ResourceQuoteStyle extends ResourceRule {
216
216
  li = new LocaleInfo(locale);
217
217
  LICache[locale] = li;
218
218
  }
219
-
219
+
220
220
  let sourceLI = LICache[this.sourceLocale];
221
221
  if (!sourceLI) {
222
222
  // @ts-ignore: An argument for 'options' was not provided
@@ -224,25 +224,35 @@ class ResourceQuoteStyle extends ResourceRule {
224
224
  sourceLI = new LocaleInfo(this.sourceLocale);
225
225
  LICache[this.sourceLocale] = sourceLI;
226
226
  }
227
-
227
+
228
228
  // what are all the quote chars that this locale uses?
229
229
  const sourceQuoteStart = sourceLI.getDelimiterQuotationStart();
230
230
  const sourceQuoteStartAlt = /** @type {string} */ (sourceLI.info.delimiter.alternateQuotationStart);
231
-
231
+
232
232
  const sourceQuoteEnd = sourceLI.getDelimiterQuotationEnd();
233
233
  const sourceQuoteEndAlt = /** @type {string} */ (sourceLI.info.delimiter.alternateQuotationEnd);
234
-
235
- const targetQuoteStart = li.getDelimiterQuotationStart();
236
- const targetQuoteStartAlt = /** @type {string} */ (li.info.delimiter.alternateQuotationStart);
237
-
238
- const targetQuoteEnd = li.getDelimiterQuotationEnd();
239
- const targetQuoteEndAlt = /** @type {string} */ (li.info.delimiter.alternateQuotationEnd);
240
-
234
+
235
+ /** special case for Japanese: accept the main quote style only and also square brackets */
236
+ const targetQuoteStartChars = (locale === "ja-JP") ? ["「", "\\["] : [li.getDelimiterQuotationStart()];
237
+ // used in regular expressions:
238
+ const targetQuoteStart = targetQuoteStartChars.join("");
239
+ const targetQuoteStartAltChars = (locale === "ja-JP") ? ["「", "\\["] : [li.info.delimiter.alternateQuotationStart];
240
+ // used in regular expressions:
241
+ const targetQuoteStartAlt = /** @type {string} */ targetQuoteStartAltChars.join("");
242
+
243
+ /** special case for Japanese: accept the main quote style only and also square brackets */
244
+ const targetQuoteEndChars = (locale === "ja-JP") ? ["」", "\\]"] : [li.getDelimiterQuotationEnd()];
245
+ // used in regular expressions:
246
+ const targetQuoteEnd = targetQuoteEndChars.join("");
247
+ const targetQuoteEndAltChars = (locale === "ja-JP") ? ["」", "\\]"] : [li.info.delimiter.alternateQuotationEnd];
248
+ // used in regular expressions:
249
+ const targetQuoteEndAlt = /** @type {string} */ targetQuoteEndAltChars.join("");
250
+
241
251
  // now calculate regular expressions for the source string that use those quotes
242
252
  // if the source uses ASCII quotes, then the target could have ASCII or native quotes
243
253
  const sourceQuotesNative = new RegExp(`((^|\\W)${sourceQuoteStart}\\s?[\\p{Letter}\\{]|[\\p{Letter}\\}]\\s?${sourceQuoteEnd}(\\W|$))`, "gu");
244
254
  const sourceQuotesNativeAlt = new RegExp(`((^|\\W)${sourceQuoteStartAlt}\\s?[\\p{Letter}\\{]|[\\p{Letter}\\}]\\s?${sourceQuoteEndAlt}(\\W|$))`, "gu");
245
-
255
+
246
256
  // now calculate the regular expressions for the target string that use quotes
247
257
  // if the source contains native quotes, then the target should also have native quotes
248
258
  const targetQuotesNative = new RegExp(`((^|\\W)${targetQuoteStart}\\s?[\\p{Letter}\\{]|[\\p{Letter}\\}]\\s?${targetQuoteEnd}(\\W|$))`, "gu");
@@ -253,18 +263,20 @@ class ResourceQuoteStyle extends ResourceRule {
253
263
  const targetQuotesAllAlt = this.localeOnly ?
254
264
  targetQuotesNativeAlt :
255
265
  new RegExp(`((^|\\W)[${targetQuoteStartAlt}']\\s?[\\p{Letter}\\{]|[\\p{Letter}\\}]\\s?[${targetQuoteEndAlt}'](\\W|$))`, "gu");
256
-
257
- // the non quote chars are used to highlight errors in the target string
266
+
267
+ // the non quote chars are used to highlight errors in the target string where they are using quotes, but
268
+ // they are the wrong type. Start with the superset of all quotes and then remove the valid ones so that
269
+ // you are left with the wrong ones for this locale.
258
270
  const targetNonQuoteChars = quoteChars.
259
271
  replace(sourceQuoteStart, "").
260
272
  replace(targetQuoteStart, "").
261
273
  replace(sourceQuoteEnd, "").
262
274
  replace(targetQuoteEnd, "");
263
275
  const targetNonQuoteCharsAlt = quoteChars.
264
- replace(sourceQuoteStartAlt, "").
265
- replace(targetQuoteStartAlt, "").
266
- replace(sourceQuoteEndAlt, "").
267
- replace(targetQuoteEndAlt, "");
276
+ replace(new RegExp(`[${sourceQuoteStartAlt}]`, "gu"), "").
277
+ replace(new RegExp(`[${targetQuoteStartAlt}]`, "gu"), "").
278
+ replace(new RegExp(`[${sourceQuoteEndAlt}]`, "gu"), "").
279
+ replace(new RegExp(`[${targetQuoteEndAlt}]`, "gu"), "");
268
280
 
269
281
  return {
270
282
  quotesAscii,
@@ -294,11 +306,11 @@ class ResourceQuoteStyle extends ResourceRule {
294
306
 
295
307
  /**
296
308
  * @override
297
- * @param {Object} params
298
- * @param {string | undefined} params.source
299
- * @param {string | undefined} params.target
300
- * @param {Resource} params.resource
301
- * @param {string} params.file
309
+ * @param {Object} params
310
+ * @param {string | undefined} params.source
311
+ * @param {string | undefined} params.target
312
+ * @param {Resource} params.resource
313
+ * @param {string} params.file
302
314
  */
303
315
  matchString({source, target, resource, file}) {
304
316
  if (!source || !target) return; // cannot match in strings that don't exist!
@@ -308,4 +320,4 @@ class ResourceQuoteStyle extends ResourceRule {
308
320
  }
309
321
  }
310
322
 
311
- export default ResourceQuoteStyle;
323
+ export default ResourceQuoteStyle;
package/docs/ilibLint.md DELETED
File without changes