ilib-lint 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 (89) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +231 -0
  3. package/docs/AnsiConsoleFormatter.html +467 -0
  4. package/docs/Formatter.html +577 -0
  5. package/docs/Formatter.js.html +135 -0
  6. package/docs/FormatterFactory.html +191 -0
  7. package/docs/FormatterFactory.js.html +109 -0
  8. package/docs/Parser.html +483 -0
  9. package/docs/Parser.js.html +122 -0
  10. package/docs/ParserFactory.js.html +109 -0
  11. package/docs/Plugin.html +847 -0
  12. package/docs/Plugin.js.html +168 -0
  13. package/docs/PluginManager.html +541 -0
  14. package/docs/PluginManager.js.html +125 -0
  15. package/docs/ResourceICUPlurals.html +278 -0
  16. package/docs/ResourceQuoteStyle.html +278 -0
  17. package/docs/ResourceRegExpChecker.html +295 -0
  18. package/docs/ResourceUniqueKeys.html +278 -0
  19. package/docs/Result.html +263 -0
  20. package/docs/Result.js.html +130 -0
  21. package/docs/Rule.html +774 -0
  22. package/docs/Rule.js.html +230 -0
  23. package/docs/RuleSet.html +760 -0
  24. package/docs/RuleSet.js.html +153 -0
  25. package/docs/SourceFile.html +826 -0
  26. package/docs/SourceFile.js.html +232 -0
  27. package/docs/XliffParser.html +396 -0
  28. package/docs/XliffPlugin.html +472 -0
  29. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  30. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  31. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  32. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  33. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  34. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  35. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  36. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  37. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  38. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
  39. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  40. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  41. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  42. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  43. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
  44. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  45. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  46. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  47. package/docs/formatters_AnsiConsoleFormatter.js.html +147 -0
  48. package/docs/global.html +448 -0
  49. package/docs/ilibLint.md +1013 -0
  50. package/docs/index.html +81 -0
  51. package/docs/plugins_XliffParser.js.html +129 -0
  52. package/docs/plugins_XliffPlugin.js.html +129 -0
  53. package/docs/rules_ResourceICUPlurals.js.html +297 -0
  54. package/docs/rules_ResourceQuoteStyle.js.html +238 -0
  55. package/docs/rules_ResourceRegExpChecker.js.html +248 -0
  56. package/docs/rules_ResourceUniqueKeys.js.html +144 -0
  57. package/docs/scripts/collapse.js +20 -0
  58. package/docs/scripts/linenumber.js +25 -0
  59. package/docs/scripts/nav.js +12 -0
  60. package/docs/scripts/polyfill.js +4 -0
  61. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  62. package/docs/scripts/prettify/lang-css.js +2 -0
  63. package/docs/scripts/prettify/prettify.js +28 -0
  64. package/docs/scripts/search.js +83 -0
  65. package/docs/styles/jsdoc.css +765 -0
  66. package/docs/styles/prettify.css +79 -0
  67. package/docs/walk.js.html +214 -0
  68. package/log4js.json +21 -0
  69. package/package.json +83 -0
  70. package/src/Formatter.js +66 -0
  71. package/src/FormatterFactory.js +41 -0
  72. package/src/Parser.js +53 -0
  73. package/src/ParserFactory.js +41 -0
  74. package/src/Plugin.js +99 -0
  75. package/src/PluginManager.js +56 -0
  76. package/src/Result.js +62 -0
  77. package/src/Rule.js +162 -0
  78. package/src/RuleSet.js +84 -0
  79. package/src/SourceFile.js +163 -0
  80. package/src/formatters/AnsiConsoleFormatter.js +78 -0
  81. package/src/index.js +213 -0
  82. package/src/plugins/XliffParser.js +60 -0
  83. package/src/plugins/XliffPlugin.js +60 -0
  84. package/src/rules/ResourceICUPlurals.js +229 -0
  85. package/src/rules/ResourceQuoteStyle.js +170 -0
  86. package/src/rules/ResourceRegExpChecker.js +179 -0
  87. package/src/rules/ResourceUniqueKeys.js +76 -0
  88. package/src/rules/utils.js +78 -0
  89. package/src/walk.js +146 -0
@@ -0,0 +1,297 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>rules/ResourceICUPlurals.js - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
18
+ </head>
19
+ <body>
20
+
21
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
22
+ <label for="nav-trigger" class="navicon-button x">
23
+ <div class="navicon"></div>
24
+ </label>
25
+
26
+ <label for="nav-trigger" class="overlay"></label>
27
+
28
+ <nav >
29
+
30
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="AnsiConsoleFormatter.html">AnsiConsoleFormatter</a><ul class='methods'><li data-type='method'><a href="AnsiConsoleFormatter.html#format">format</a></li><li data-type='method'><a href="AnsiConsoleFormatter.html#getDescription">getDescription</a></li></ul></li><li><a href="Formatter.html">Formatter</a><ul class='methods'><li data-type='method'><a href="Formatter.html#format">format</a></li><li data-type='method'><a href="Formatter.html#getDescription">getDescription</a></li><li data-type='method'><a href="Formatter.html#getName">getName</a></li></ul></li><li><a href="FormatterFactory.html">FormatterFactory</a></li><li><a href="Parser.html">Parser</a><ul class='methods'><li data-type='method'><a href="Parser.html#getResources">getResources</a></li><li data-type='method'><a href="Parser.html#init">init</a></li><li data-type='method'><a href="Parser.html#parse">parse</a></li></ul></li><li><a href="Plugin.html">Plugin</a><ul class='methods'><li data-type='method'><a href="Plugin.html#getExtensions">getExtensions</a></li><li data-type='method'><a href="Plugin.html#getFormatters">getFormatters</a></li><li data-type='method'><a href="Plugin.html#getParsers">getParsers</a></li><li data-type='method'><a href="Plugin.html#getRules">getRules</a></li><li data-type='method'><a href="Plugin.html#getType">getType</a></li><li data-type='method'><a href="Plugin.html#init">init</a></li></ul></li><li><a href="PluginManager.html">PluginManager</a><ul class='methods'><li data-type='method'><a href="PluginManager.html#getHandlers">getHandlers</a></li><li data-type='method'><a href="PluginManager.html#load">load</a></li></ul></li><li><a href="ResourceICUPlurals.html">ResourceICUPlurals</a><ul class='methods'><li data-type='method'><a href="ResourceICUPlurals.html#match">match</a></li></ul></li><li><a href="ResourceQuoteStyle.html">ResourceQuoteStyle</a><ul class='methods'><li data-type='method'><a href="ResourceQuoteStyle.html#match">match</a></li></ul></li><li><a href="ResourceRegExpChecker.html">ResourceRegExpChecker</a><ul class='methods'><li data-type='method'><a href="ResourceRegExpChecker.html#match">match</a></li></ul></li><li><a href="ResourceUniqueKeys.html">ResourceUniqueKeys</a><ul class='methods'><li data-type='method'><a href="ResourceUniqueKeys.html#match">match</a></li></ul></li><li><a href="Result.html">Result</a></li><li><a href="Rule.html">Rule</a><ul class='methods'><li data-type='method'><a href="Rule.html#getDescription">getDescription</a></li><li data-type='method'><a href="Rule.html#getName">getName</a></li><li data-type='method'><a href="Rule.html#getRuleType">getRuleType</a></li><li data-type='method'><a href="Rule.html#match">match</a></li></ul></li><li><a href="RuleSet.html">RuleSet</a><ul class='methods'><li data-type='method'><a href="RuleSet.html#addRule">addRule</a></li><li data-type='method'><a href="RuleSet.html#getRule">getRule</a></li><li data-type='method'><a href="RuleSet.html#getRules">getRules</a></li><li data-type='method'><a href="RuleSet.html#getSize">getSize</a></li></ul></li><li><a href="SourceFile.html">SourceFile</a><ul class='methods'><li data-type='method'><a href="SourceFile.html#findIssues">findIssues</a></li><li data-type='method'><a href="SourceFile.html#getFilePath">getFilePath</a></li><li data-type='method'><a href="SourceFile.html#getLocaleFromPath">getLocaleFromPath</a></li><li data-type='method'><a href="SourceFile.html#getType">getType</a></li><li data-type='method'><a href="SourceFile.html#parse">parse</a></li></ul></li><li><a href="XliffParser.html">XliffParser</a><ul class='methods'><li data-type='method'><a href="XliffParser.html#getResources">getResources</a></li><li data-type='method'><a href="XliffParser.html#parse">parse</a></li></ul></li><li><a href="XliffPlugin.html">XliffPlugin</a><ul class='methods'><li data-type='method'><a href="XliffPlugin.html#getExtensions">getExtensions</a></li><li data-type='method'><a href="XliffPlugin.html#getParsers">getParsers</a></li><li data-type='method'><a href="XliffPlugin.html#getType">getType</a></li></ul></li></ul><h3>Global</h3><ul><li><a href="global.html#ParserFactory">ParserFactory</a></li><li><a href="global.html#walk">walk</a></li></ul>
31
+ </nav>
32
+
33
+ <div id="main">
34
+
35
+ <h1 class="page-title">rules/ResourceICUPlurals.js</h1>
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+ <section>
44
+ <article>
45
+ <pre class="prettyprint source linenums"><code>/*
46
+ * ResourceICUPlurals.js - rule to check formatjs/ICU style plurals in the target string
47
+ *
48
+ * Copyright © 2022 JEDLSoft
49
+ *
50
+ * Licensed under the Apache License, Version 2.0 (the "License");
51
+ * you may not use this file except in compliance with the License.
52
+ * You may obtain a copy of the License at
53
+ *
54
+ * http://www.apache.org/licenses/LICENSE-2.0
55
+ *
56
+ * Unless required by applicable law or agreed to in writing, software
57
+ * distributed under the License is distributed on an "AS IS" BASIS,
58
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
59
+ *
60
+ * See the License for the specific language governing permissions and
61
+ * limitations under the License.
62
+ */
63
+
64
+ import { IntlMessageFormat } from 'intl-messageformat';
65
+ import Locale from 'ilib-locale';
66
+
67
+ import Rule from '../Rule.js';
68
+ import Result from '../Result.js';
69
+
70
+ // all the plural categories from CLDR
71
+ const allCategories = ["zero", "one", "two", "few", "many", "other"];
72
+
73
+ // Map the language to the set of plural categories that the language
74
+ // uses. If the language is not listed below, it uses the default
75
+ // list of plurals: "one" and "other"
76
+ const categoriesForLang = {
77
+ "ja": [ "other" ],
78
+ "zh": [ "other" ],
79
+ "ko": [ "other" ],
80
+ "th": [ "other" ],
81
+ "lv": [ "zero", "one", "other" ],
82
+ "ga": [ "one", "two", "other" ],
83
+ "ro": [ "one", "few", "other" ],
84
+ "lt": [ "one", "few", "other" ],
85
+ "ru": [ "one", "few", "other" ],
86
+ "uk": [ "one", "few", "other" ],
87
+ "be": [ "one", "few", "other" ],
88
+ "sr": [ "one", "few", "other" ],
89
+ "hr": [ "one", "few", "other" ],
90
+ "cs": [ "one", "few", "other" ],
91
+ "sk": [ "one", "few", "other" ],
92
+ "pl": [ "one", "few", "other" ],
93
+ "sl": [ "one", "two", "few", "other" ],
94
+ "ar": [ "zero", "one", "two", "few", "many", "other" ]
95
+ }
96
+
97
+ /**
98
+ * @class Represent an ilib-lint rule.
99
+ */
100
+ class ResourceICUPlurals extends Rule {
101
+ constructor(options) {
102
+ super(options);
103
+ this.name = "resource-icu-plurals";
104
+ this.description = "Ensure that plurals in translated resources have the correct syntax";
105
+ this.sourceLocale = (options &amp;&amp; options.sourceLocale) || "en-US";
106
+ }
107
+
108
+ getRuleType() {
109
+ return "resource";
110
+ }
111
+
112
+ /**
113
+ * @private
114
+ */
115
+ checkPluralCategories(ast, neededCategories, isSource, stringToCheck, key, pathName, source) {
116
+ let value = [];
117
+ for (let i = 0; i &lt; ast.length; i++) {
118
+ const opts = ast[i].options;
119
+ if (opts) {
120
+ // check if any of the needed categories are missing
121
+ const missing = neededCategories.filter(category => {
122
+ return typeof(opts[category]) === 'undefined';
123
+ });
124
+ if ( missing &amp;&amp; missing.length ) {
125
+ let opts = {
126
+ severity: "error",
127
+ rule: this,
128
+ description: `Missing plural categories in ${isSource ? "source" : "target"} string: ${missing.join(", ")}. Expecting these: ${neededCategories.join(", ")}`,
129
+ id: key,
130
+ highlight: `${isSource ? "Source" : "Target"}: ${stringToCheck}&lt;e0>&lt;/e0>`,
131
+ pathName,
132
+ source
133
+ };
134
+ value.push(new Result(opts));
135
+ }
136
+ for (let category in opts) {
137
+ if ( opts[category] &amp;&amp; Array.isArray(opts[category].value) ) {
138
+ value = value.concat(this.checkPluralCategories(opts[category].value, neededCategories, isSource, stringToCheck, key, pathName, source));
139
+ }
140
+ }
141
+ // now check the other way around. That is, if the categories that exist are not needed.
142
+ if (!isSource) {
143
+ const extras = Object.keys(opts).filter(category => {
144
+ return neededCategories.indexOf(category) &lt; 0;
145
+ });
146
+ if (extras &amp;&amp; extras.length) {
147
+ let opts = {
148
+ severity: "warning",
149
+ rule: this,
150
+ description: `Extra plural categories in ${isSource ? "source" : "target"} string: ${extras.join(", ")}. Expecting only these: ${neededCategories.join(", ")}`,
151
+ id: key,
152
+ highlight: `${isSource ? "Source" : "Target"}: ${stringToCheck}&lt;e0>&lt;/e0>`,
153
+ pathName,
154
+ source
155
+ };
156
+ value.push(new Result(opts));
157
+ }
158
+ }
159
+ }
160
+ }
161
+ return value;
162
+ }
163
+
164
+ checkString(src, tar, file, resource, sourceLocale, targetLocale, lineNumber) {
165
+ const sLoc = new Locale(sourceLocale);
166
+ const tLoc = new Locale(targetLocale);
167
+ let results;
168
+ let problems = [];
169
+ let sourceCategories = [];
170
+ try {
171
+ const imf = new IntlMessageFormat(src, sourceLocale);
172
+ let categories = categoriesForLang[sLoc.getLanguage()] || [ "one", "other" ];
173
+ // look in the abstract syntax tree for the categories that were parsed out and make
174
+ // sure the required ones are there
175
+ const ast = imf.getAst();
176
+ problems = problems.concat(this.checkPluralCategories(ast, categories, true, src, resource.getKey(), file, resource.getSource()));
177
+ if ( ast[0] &amp;&amp; ast[0].options ) {
178
+ sourceCategories = Object.keys(ast[0].options).filter(category => {
179
+ // if it is not one of the standard categories, it is a special one, so search for it
180
+ // in the target too
181
+ return allCategories.indexOf(category) &lt; 0;
182
+ });
183
+ }
184
+ } catch (e) {
185
+ let value = {
186
+ pathName: file,
187
+ severity: "error",
188
+ rule: this,
189
+ description: `Incorrect plural or select syntax in source string: ${e}`,
190
+ id: resource.getKey(),
191
+ highlight: `Source: ${src.substring(0, e.location.end.offset)}&lt;e0>${src.substring(e.location.end.offset)}&lt;/e0>`,
192
+ pathName: file
193
+ };
194
+ if (typeof(lineNumber) !== 'undefined') {
195
+ value.lineNumber = lineNumber + e.location.end.line - 1;
196
+ }
197
+ problems.push(new Result(value));
198
+ }
199
+ try {
200
+ const imf = new IntlMessageFormat(tar, targetLocale);
201
+ let categories = categoriesForLang[tLoc.getLanguage()] || [ "one", "other" ];
202
+ if (sourceCategories.length) {
203
+ categories = categories.concat(sourceCategories);
204
+ }
205
+ // look in the abstract syntax tree for the categories that were parsed out and make
206
+ // sure the required ones are there
207
+ const ast = imf.getAst();
208
+ problems = problems.concat(this.checkPluralCategories(ast, categories, false, tar, resource.getKey(), file, resource.getSource()));
209
+ } catch (e) {
210
+ let value = {
211
+ severity: "error",
212
+ description: `Incorrect plural or select syntax in target string: ${e}`,
213
+ rule: this,
214
+ id: resource.getKey(),
215
+ source: src,
216
+ highlight: `Target: ${tar.substring(0, e.location.end.offset)}&lt;e0>${tar.substring(e.location.end.offset)}&lt;/e0>`,
217
+ pathName: file
218
+ };
219
+ if (typeof(lineNumber) !== 'undefined') {
220
+ value.lineNumber = lineNumber + e.location.end.line - 1;
221
+ }
222
+ problems.push(new Result(value));
223
+ }
224
+ return problems.length &lt; 2 ? problems[0] : problems;
225
+ }
226
+
227
+ /**
228
+ * @override
229
+ */
230
+ match(options) {
231
+ const { resource, file } = options;
232
+ const sourceLocale = this.sourceLocale;
233
+ let problems = [];
234
+
235
+ switch (resource.getType()) {
236
+ case 'string':
237
+ const tarString = resource.getTarget();
238
+ if (tarString) {
239
+ return this.checkString(resource.getSource(), tarString, file, resource, sourceLocale, options.locale, options.lineNumber);
240
+ }
241
+ break;
242
+
243
+ case 'array':
244
+ const srcArray = resource.getSource();
245
+ const tarArray = resource.getTarget();
246
+ if (tarArray) {
247
+ return srcArray.map((item, i) => {
248
+ if (i &lt; tarArray.length &amp;&amp; tarArray[i]) {
249
+ return this.checkString(srcArray[i], tarArray[i], file, resource, sourceLocale, options.locale, options.lineNumber);
250
+ }
251
+ }).filter(element => {
252
+ return element;
253
+ });
254
+ }
255
+ break;
256
+
257
+ case 'plural':
258
+ const srcPlural = resource.getSource();
259
+ const tarPlural = resource.getTarget();
260
+ if (tarPlural) {
261
+ return categories.map(category => {
262
+ return this.checkString(srcPlural.other, tarPlural[category], file, resource, sourceLocale, options.locale, options.lineNumber);
263
+ });
264
+ }
265
+ break;
266
+ }
267
+ }
268
+
269
+ // no match
270
+ return;
271
+ }
272
+
273
+ export default ResourceICUPlurals;</code></pre>
274
+ </article>
275
+ </section>
276
+
277
+
278
+
279
+
280
+
281
+
282
+ </div>
283
+
284
+ <br class="clear">
285
+
286
+ <footer>
287
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.11</a> on Mon Dec 19 2022 15:00:46 GMT-0800 (Pacific Standard Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
288
+ </footer>
289
+
290
+ <script>prettyPrint();</script>
291
+ <script src="scripts/polyfill.js"></script>
292
+ <script src="scripts/linenumber.js"></script>
293
+
294
+
295
+
296
+ </body>
297
+ </html>
@@ -0,0 +1,238 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>rules/ResourceQuoteStyle.js - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
18
+ </head>
19
+ <body>
20
+
21
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
22
+ <label for="nav-trigger" class="navicon-button x">
23
+ <div class="navicon"></div>
24
+ </label>
25
+
26
+ <label for="nav-trigger" class="overlay"></label>
27
+
28
+ <nav >
29
+
30
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="AnsiConsoleFormatter.html">AnsiConsoleFormatter</a><ul class='methods'><li data-type='method'><a href="AnsiConsoleFormatter.html#format">format</a></li><li data-type='method'><a href="AnsiConsoleFormatter.html#getDescription">getDescription</a></li></ul></li><li><a href="Formatter.html">Formatter</a><ul class='methods'><li data-type='method'><a href="Formatter.html#format">format</a></li><li data-type='method'><a href="Formatter.html#getDescription">getDescription</a></li><li data-type='method'><a href="Formatter.html#getName">getName</a></li></ul></li><li><a href="FormatterFactory.html">FormatterFactory</a></li><li><a href="Parser.html">Parser</a><ul class='methods'><li data-type='method'><a href="Parser.html#getResources">getResources</a></li><li data-type='method'><a href="Parser.html#init">init</a></li><li data-type='method'><a href="Parser.html#parse">parse</a></li></ul></li><li><a href="Plugin.html">Plugin</a><ul class='methods'><li data-type='method'><a href="Plugin.html#getExtensions">getExtensions</a></li><li data-type='method'><a href="Plugin.html#getFormatters">getFormatters</a></li><li data-type='method'><a href="Plugin.html#getParsers">getParsers</a></li><li data-type='method'><a href="Plugin.html#getRules">getRules</a></li><li data-type='method'><a href="Plugin.html#getType">getType</a></li><li data-type='method'><a href="Plugin.html#init">init</a></li></ul></li><li><a href="PluginManager.html">PluginManager</a><ul class='methods'><li data-type='method'><a href="PluginManager.html#getHandlers">getHandlers</a></li><li data-type='method'><a href="PluginManager.html#load">load</a></li></ul></li><li><a href="ResourceICUPlurals.html">ResourceICUPlurals</a><ul class='methods'><li data-type='method'><a href="ResourceICUPlurals.html#match">match</a></li></ul></li><li><a href="ResourceQuoteStyle.html">ResourceQuoteStyle</a><ul class='methods'><li data-type='method'><a href="ResourceQuoteStyle.html#match">match</a></li></ul></li><li><a href="ResourceRegExpChecker.html">ResourceRegExpChecker</a><ul class='methods'><li data-type='method'><a href="ResourceRegExpChecker.html#match">match</a></li></ul></li><li><a href="ResourceUniqueKeys.html">ResourceUniqueKeys</a><ul class='methods'><li data-type='method'><a href="ResourceUniqueKeys.html#match">match</a></li></ul></li><li><a href="Result.html">Result</a></li><li><a href="Rule.html">Rule</a><ul class='methods'><li data-type='method'><a href="Rule.html#getDescription">getDescription</a></li><li data-type='method'><a href="Rule.html#getName">getName</a></li><li data-type='method'><a href="Rule.html#getRuleType">getRuleType</a></li><li data-type='method'><a href="Rule.html#match">match</a></li></ul></li><li><a href="RuleSet.html">RuleSet</a><ul class='methods'><li data-type='method'><a href="RuleSet.html#addRule">addRule</a></li><li data-type='method'><a href="RuleSet.html#getRule">getRule</a></li><li data-type='method'><a href="RuleSet.html#getRules">getRules</a></li><li data-type='method'><a href="RuleSet.html#getSize">getSize</a></li></ul></li><li><a href="SourceFile.html">SourceFile</a><ul class='methods'><li data-type='method'><a href="SourceFile.html#findIssues">findIssues</a></li><li data-type='method'><a href="SourceFile.html#getFilePath">getFilePath</a></li><li data-type='method'><a href="SourceFile.html#getLocaleFromPath">getLocaleFromPath</a></li><li data-type='method'><a href="SourceFile.html#getType">getType</a></li><li data-type='method'><a href="SourceFile.html#parse">parse</a></li></ul></li><li><a href="XliffParser.html">XliffParser</a><ul class='methods'><li data-type='method'><a href="XliffParser.html#getResources">getResources</a></li><li data-type='method'><a href="XliffParser.html#parse">parse</a></li></ul></li><li><a href="XliffPlugin.html">XliffPlugin</a><ul class='methods'><li data-type='method'><a href="XliffPlugin.html#getExtensions">getExtensions</a></li><li data-type='method'><a href="XliffPlugin.html#getParsers">getParsers</a></li><li data-type='method'><a href="XliffPlugin.html#getType">getType</a></li></ul></li></ul><h3>Global</h3><ul><li><a href="global.html#ParserFactory">ParserFactory</a></li><li><a href="global.html#walk">walk</a></li></ul>
31
+ </nav>
32
+
33
+ <div id="main">
34
+
35
+ <h1 class="page-title">rules/ResourceQuoteStyle.js</h1>
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+ <section>
44
+ <article>
45
+ <pre class="prettyprint source linenums"><code>/*
46
+ * ResourceQuoteStyle.js - rule to check quotes in the target string
47
+ *
48
+ * Copyright © 2022 JEDLSoft
49
+ *
50
+ * Licensed under the Apache License, Version 2.0 (the "License");
51
+ * you may not use this file except in compliance with the License.
52
+ * You may obtain a copy of the License at
53
+ *
54
+ * http://www.apache.org/licenses/LICENSE-2.0
55
+ *
56
+ * Unless required by applicable law or agreed to in writing, software
57
+ * distributed under the License is distributed on an "AS IS" BASIS,
58
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
59
+ *
60
+ * See the License for the specific language governing permissions and
61
+ * limitations under the License.
62
+ */
63
+
64
+ import LocaleInfo from 'ilib-localeinfo';
65
+
66
+ import Rule from '../Rule.js';
67
+ import Result from '../Result.js';
68
+
69
+ let LICache = {};
70
+
71
+ // superset of all the start and end chars used in CLDR
72
+ const quoteChars = "«»‘“”„「」’‚‹›『』\"\'";
73
+
74
+ /**
75
+ * @class Represent an ilib-lint rule.
76
+ */
77
+ class ResourceQuoteStyle extends Rule {
78
+ constructor(options) {
79
+ super(options);
80
+ this.name = "resource-quote-style";
81
+ this.description = "Ensure that the proper quote characters are used in translated resources";
82
+ this.sourceLocale = (options &amp;&amp; options.sourceLocale) || "en-US";
83
+ }
84
+
85
+ getRuleType() {
86
+ return "resource";
87
+ }
88
+
89
+ /**
90
+ * @override
91
+ */
92
+ match(options) {
93
+ const { locale, resource, file } = options || {};
94
+ let li = LICache[locale];
95
+ const _this = this;
96
+
97
+ if (!li) {
98
+ li = new LocaleInfo(locale);
99
+ LICache[locale] = li;
100
+ }
101
+
102
+ let sourceLI = LICache[this.sourceLocale];
103
+ if (!sourceLI) {
104
+ sourceLI = new LocaleInfo(this.sourceLocale);
105
+ LICache[this.sourceLocale] = sourceLI;
106
+ }
107
+
108
+ const srcQuoteStart = sourceLI.getDelimiterQuotationStart();
109
+ const srcAltQuoteStart = sourceLI.info.delimiter.alternateQuotationStart;
110
+
111
+ const srcQuoteEnd = sourceLI.getDelimiterQuotationEnd();
112
+ const srcAltQuoteEnd = sourceLI.info.delimiter.alternateQuotationEnd;
113
+
114
+ const tarQuoteStart = li.getDelimiterQuotationStart();
115
+ const tarAltQuoteStart = li.info.delimiter.alternateQuotationStart;
116
+
117
+ const tarQuoteEnd = li.getDelimiterQuotationEnd();
118
+ const tarAltQuoteEnd = li.info.delimiter.alternateQuotationEnd;
119
+
120
+ // if the source uses ASCII quotes, then the target could have ASCII or native quotes
121
+ const srcQuotesAscii = new RegExp(`((^|\\W)['"]\\p{Letter}|\\p{Letter}['"](\\W|$))`, "gu");
122
+ const srcQuotesNative = new RegExp(`((^|\\W)[${srcQuoteStart}${srcAltQuoteStart}]\\p{Letter}|\\p{Letter}[${srcQuoteEnd}${srcAltQuoteEnd}](\\W|$))`, "gu");
123
+
124
+ // if the source contains native quotes, then the target should also have native quotes
125
+ const tarQuotesAll = new RegExp(`((^|\\W)[${tarQuoteStart}${tarAltQuoteStart}'"]\\p{Letter}|\\p{Letter}[${tarQuoteEnd}${tarAltQuoteEnd}'"](\\W|$))`, "gu");
126
+ const tarQuotesNative = new RegExp(`((^|\\W)[${tarQuoteStart}${tarAltQuoteStart}]\\p{Letter}|\\p{Letter}[${tarQuoteEnd}${tarAltQuoteEnd}](\\W|$))`, "gu");
127
+
128
+ const nonQuoteChars = `([${
129
+ quoteChars.
130
+ replace(srcQuoteStart, "").
131
+ replace(srcAltQuoteStart, "").
132
+ replace(tarQuoteStart, "").
133
+ replace(tarAltQuoteStart, "").
134
+ replace(srcQuoteEnd, "").
135
+ replace(srcAltQuoteEnd, "").
136
+ replace(tarQuoteEnd, "").
137
+ replace(tarAltQuoteEnd, "")}])`;
138
+ const re = new RegExp(nonQuoteChars, "g");
139
+
140
+ /**
141
+ * @private
142
+ */
143
+ function checkString(src, tar) {
144
+ srcQuotesAscii.lastIndex = 0;
145
+ tarQuotesAll.lastIndex = 0;
146
+ if ((src.match(srcQuotesAscii) &amp;&amp; !tar.match(tarQuotesAll)) ||
147
+ (src.match(srcQuotesNative) &amp;&amp; !tar.match(tarQuotesNative))) {
148
+ const matches = re.exec(tar);
149
+ let value = {
150
+ severity: "warning",
151
+ id: resource.getKey(),
152
+ source: src,
153
+ rule: _this,
154
+ pathName: file
155
+ };
156
+ if (matches) {
157
+ value.highlight = `Target: ${tar.replace(re, "&lt;e0>$1&lt;/e0>")}`;
158
+ value.description = `Quote style for the the locale ${locale} should be ${tarQuoteStart}text${tarQuoteEnd}`;
159
+ } else {
160
+ value.highlight = `Target: ${tar}&lt;e0>&lt;/e0>`;
161
+ value.description = `Quotes are missing in the target. Quote style for the the locale ${locale} should be ${tarQuoteStart}text${tarQuoteEnd}`;
162
+ }
163
+ if (typeof(options.lineNumber) !== 'undefined') {
164
+ value.lineNumber = options.lineNumber;
165
+ }
166
+ return new Result(value);
167
+ }
168
+ }
169
+
170
+ switch (resource.getType()) {
171
+ case 'string':
172
+ const tarString = resource.getTarget();
173
+ if (tarString) {
174
+ return checkString(resource.getSource(), tarString);
175
+ }
176
+ break;
177
+
178
+ case 'array':
179
+ const srcArray = resource.getSource();
180
+ const tarArray = resource.getTarget();
181
+ if (tarArray) {
182
+ return srcArray.map((item, i) => {
183
+ if (i &lt; tarArray.length &amp;&amp; tarArray[i]) {
184
+ return checkString(srcArray[i], tarArray[i]);
185
+ }
186
+ }).filter(element => {
187
+ return element;
188
+ });
189
+ }
190
+ break;
191
+
192
+ case 'plural':
193
+ const srcPlural = resource.getSource();
194
+ const tarPlural = resource.getTarget();
195
+ if (tarPlural) {
196
+ const hasQuotes = categories.find(category => {
197
+ return (srcPlural[category] &amp;&amp; srcPlural[category].contains(srcQuote));
198
+ });
199
+
200
+ if (hasQuotes) {
201
+ return categories.map(category => {
202
+ return checkString(srcPlural.other, tarPlural[category]);
203
+ });
204
+ }
205
+ }
206
+ break;
207
+ }
208
+ }
209
+
210
+ // no match
211
+ return;
212
+ }
213
+
214
+ export default ResourceQuoteStyle;</code></pre>
215
+ </article>
216
+ </section>
217
+
218
+
219
+
220
+
221
+
222
+
223
+ </div>
224
+
225
+ <br class="clear">
226
+
227
+ <footer>
228
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.11</a> on Mon Dec 19 2022 15:00:46 GMT-0800 (Pacific Standard Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
229
+ </footer>
230
+
231
+ <script>prettyPrint();</script>
232
+ <script src="scripts/polyfill.js"></script>
233
+ <script src="scripts/linenumber.js"></script>
234
+
235
+
236
+
237
+ </body>
238
+ </html>