ilib-lint 1.0.0 → 1.2.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 (85) hide show
  1. package/README.md +442 -66
  2. package/docs/AnsiConsoleFormatter.html +25 -131
  3. package/docs/ConfigBasedFormatter.html +368 -0
  4. package/docs/DirItem.html +718 -0
  5. package/docs/DirItem.js.html +162 -0
  6. package/docs/FileType.html +493 -0
  7. package/docs/FileType.js.html +215 -0
  8. package/docs/FormatterManager.html +744 -0
  9. package/docs/FormatterManager.js.html +195 -0
  10. package/docs/ParserManager.html +607 -0
  11. package/docs/ParserManager.js.html +168 -0
  12. package/docs/PluginManager.html +451 -49
  13. package/docs/PluginManager.js.html +208 -16
  14. package/docs/Project.html +2069 -0
  15. package/docs/Project.js.html +400 -0
  16. package/docs/ResourceICUPlurals.html +15 -9
  17. package/docs/ResourceMatcher.html +2192 -0
  18. package/docs/ResourceQuoteStyle.html +15 -9
  19. package/docs/ResourceRegExpChecker.html +4 -4
  20. package/docs/ResourceUniqueKeys.html +64 -8
  21. package/docs/RuleManager.html +1592 -0
  22. package/docs/RuleManager.js.html +352 -0
  23. package/docs/RuleSet.html +247 -51
  24. package/docs/RuleSet.js.html +49 -32
  25. package/docs/SourceFile.html +164 -258
  26. package/docs/SourceFile.js.html +67 -75
  27. package/docs/XliffParser.html +28 -28
  28. package/docs/XliffPlugin.html +21 -181
  29. package/docs/formatters_AnsiConsoleFormatter.js.html +14 -17
  30. package/docs/formatters_ConfigBasedFormatter.js.html +166 -0
  31. package/docs/global.html +122 -46
  32. package/docs/ilibLint.md +1497 -493
  33. package/docs/index.html +5 -2
  34. package/docs/plugins_XliffParser.js.html +18 -8
  35. package/docs/plugins_XliffPlugin.js.html +10 -20
  36. package/docs/resource-icu-plurals.md +21 -0
  37. package/docs/resource-named-params.md +8 -0
  38. package/docs/resource-quote-style.md +21 -0
  39. package/docs/resource-unique-keys.md +6 -0
  40. package/docs/resource-url-match.md +7 -0
  41. package/docs/rules_ResourceICUPlurals.js.html +12 -6
  42. package/docs/rules_ResourceMatcher.js.html +253 -0
  43. package/docs/rules_ResourceQuoteStyle.js.html +20 -8
  44. package/docs/rules_ResourceRegExpChecker.js.html +4 -5
  45. package/docs/rules_ResourceSourceChecker.js.html +252 -0
  46. package/docs/rules_ResourceTargetChecker.js.html +252 -0
  47. package/docs/rules_ResourceUniqueKeys.js.html +13 -6
  48. package/docs/rules_SourceFileChecker.js.html +252 -0
  49. package/docs/scripts/collapse.js +19 -0
  50. package/docs/scripts/commonNav.js +28 -0
  51. package/docs/scripts/search.js +16 -0
  52. package/docs/styles/jsdoc.css +13 -2
  53. package/docs/styles/prettify.css +1 -0
  54. package/docs/walk.js.html +49 -37
  55. package/package.json +15 -13
  56. package/src/DirItem.js +90 -0
  57. package/src/FileType.js +144 -0
  58. package/src/FormatterManager.js +124 -0
  59. package/src/ParserManager.js +97 -0
  60. package/src/PluginManager.js +203 -14
  61. package/src/Project.js +328 -0
  62. package/src/RuleManager.js +281 -0
  63. package/src/RuleSet.js +44 -30
  64. package/src/SourceFile.js +62 -73
  65. package/src/formatters/AnsiConsoleFormatter.js +9 -15
  66. package/src/formatters/ConfigBasedFormatter.js +94 -0
  67. package/src/index.js +128 -76
  68. package/src/plugins/XliffParser.js +13 -6
  69. package/src/plugins/XliffPlugin.js +5 -18
  70. package/src/rules/ResourceICUPlurals.js +7 -4
  71. package/src/rules/ResourceMatcher.js +181 -0
  72. package/src/rules/ResourceQuoteStyle.js +15 -6
  73. package/src/rules/{ResourceRegExpChecker.js → ResourceSourceChecker.js} +10 -9
  74. package/src/rules/ResourceTargetChecker.js +180 -0
  75. package/src/rules/ResourceUniqueKeys.js +8 -4
  76. package/src/rules/SourceFileChecker.js +180 -0
  77. package/src/rules/utils.js +31 -1
  78. package/src/walk.js +44 -35
  79. package/src/Formatter.js +0 -66
  80. package/src/FormatterFactory.js +0 -41
  81. package/src/Parser.js +0 -53
  82. package/src/ParserFactory.js +0 -41
  83. package/src/Plugin.js +0 -99
  84. package/src/Result.js +0 -62
  85. package/src/Rule.js +0 -162
package/README.md CHANGED
@@ -4,15 +4,25 @@ A static analysis linter for many types of source files that looks for i18n prob
4
4
 
5
5
  This i18n linter differs from other static linters in the following ways:
6
6
 
7
- * it can recognize the locale of files from the path name of files or from within
8
- the file itself, and applies locale-sensitive rules. For example, you can apply
9
- a rule that checks that the translations in a resource file of a plural resource
10
- contain the correct set of plural categories for the target language.
11
- * it can apply different rulesets to different sets of files. This is useful for
7
+ * It can apply different rulesets to different sets of files. This is useful for
12
8
  a number of reasons:
13
9
  * when linting a mono-repo that has different subprojects inside of it
14
10
  and each subproject needs different rules applied to its files
15
- * when different file types need different rulesets
11
+ * when different sets of files need different rulesets, even within the same file type
12
+ * It can handle any file type
13
+ * most linters are specific to a programming language and its related files. This linter
14
+ can read any type of file and apply the appropriate set of rules.
15
+ * Rules can be locale-sensitive
16
+ * most linters apply the same rules blindly to all files, regardless of the locale
17
+ * for resource files, it can apply the appropriate locale for each resource individually
18
+ * It can recognize the locale of files from the path name of files
19
+ * this allows it to apply the locale-sensitive rules automatically. For example, you can apply
20
+ a rule that checks that the translations in a resource file of a plural resource
21
+ contain the correct set of plural categories for the target language.
22
+ * It can load plugins
23
+ * Parsers - you can add parsers for new programming languages or resource file types
24
+ * Formatters - you can make the output look exactly the way you want
25
+ * Rules - you can add new rules declaratively or programmatically
16
26
 
17
27
  ## Installation
18
28
 
@@ -38,31 +48,14 @@ is written with ESM modules.
38
48
  ## Quick Start
39
49
 
40
50
  Running ilib-lint is easy. Just change your directory to the top level directory
41
- of your project and run it with no parameters and no configuration file. When
42
- there are no parameters and no configuration file, it will do all default
43
- behaviours, which for some projects is sufficient.
51
+ of your project and run it with no parameters and no configuration file. It will
52
+ do all default behaviours and apply the default rules, which for some projects
53
+ is sufficient:
44
54
 
45
- The default behaviours are:
46
-
47
- * Start in the current directory and recursively find all files
48
- and subdirectories underneath there
49
- * All built-in rules will be added to the current rule set
50
- * There are a few built-in
51
- types of files handled and default rulesets that apply to them.
52
- * For each rule in a ruleset, it will use the default settings for that rule
53
- * It will use the default set of locales (the top 20 locales on the internet
54
- by traffic) with "en-US" being the source locale
55
- * It will apply each rule in the current ruleset that applies to
56
- each type of file. If a file type does not have any rulesets that apply to it,
57
- it will be skipped.
58
- * the locale of a file will be gleaned from its path name if possible
59
- * for some types of resource files, the locale is documented in
60
- the file itself. (eg. xliff files)
61
- * Output will be printed on the standard output in human readable form
62
55
  ```
63
56
  $ cd myproject
64
57
  $ ilib-lint
65
- ilib-lint - Copyright (c) 2022 JEDLsoft, All rights reserved.
58
+ ilib-lint - Copyright (c) 2022-2023 JEDLsoft, All rights reserved.
66
59
  WARN: i18n/ru_RU.properties(45): translation should use the appropriate
67
60
  quote style
68
61
  myproject.dialog1.body.text = Нажмите кнопку "Справка", чтобы получить
@@ -72,6 +65,29 @@ be «text»
72
65
  $
73
66
  ```
74
67
 
68
+ ## Default Behaviours
69
+
70
+ The default behaviour is to recursively search the current directory for all
71
+ xliff files, and then apply all of the built-in resource rules to those files
72
+ and report human-readable results to the standard output.
73
+
74
+ The default behaviours are:
75
+
76
+ * Start in the current directory and recursively find all xliff files
77
+ underneath there. The xliff file type is built-in to the linter.
78
+ * All built-in rules will be added to the current rule set, and it will
79
+ instantiate each rule with its default settings.
80
+ * It will use the default set of locales (the top 20 locales on the internet
81
+ by traffic) with "en-US" being the source locale
82
+ * For each file found, it applies each rule in the ruleset.
83
+ If a file type does not have any rulesets that apply to it,
84
+ it will be skipped.
85
+ * the locale of a file can sometimes be gleaned from its path name
86
+ * for some types of resource files, the locale is documented in
87
+ the file itself. (eg. xliff or other resource files)
88
+ * Output will be printed on the standard output in human readable form
89
+
90
+
75
91
  ## Command-line Parameters
76
92
 
77
93
  ilib-lint accepts the following command-line parameters:
@@ -83,8 +99,8 @@ ilib-lint accepts the following command-line parameters:
83
99
  with status 2 if there are errors, and status 0 if there are warnings. This
84
100
  flag allows you to squelch the warnings and only fail a script if there are
85
101
  actual errors.
86
- * locales - Locales you want your app to support. Value is a comma-separated
87
- list of BCP-47 style locale tags.
102
+ * locales - Locales you want your app to support globally. Value is a comma-separated
103
+ list of BCP-47 style locale tags. File types can override this list.
88
104
  Default: the top 20 locales on the internet by traffic.
89
105
  * sourceLocale - locale of the source files or the source locale for resource
90
106
  files. Default: "en-US"
@@ -101,43 +117,104 @@ exit status:
101
117
  * 1 - warnings found
102
118
  * 2 - errors found
103
119
 
104
- When the `--errorsOnly` flag is given, the program will return 0 if only
105
- warnings can be found or if no issues are found.
120
+ When the `--errorsOnly` flag is given, the program will return 0 unless at least
121
+ one error was found.
106
122
 
107
123
  ## Configuration
108
124
 
109
- If the named paths contain a file called `ilib-lint-config.json`, it
110
- will be read and processed to configure the ilib-lint tool for that path. The
111
- `ilib-lint-config.json` file can have any of the following properties:
125
+ The paths to process are given on the command-line. If no path is specified
126
+ on the command-line, the tool will default to the current directory.
127
+ If any named path contains a file called `ilib-lint-config.json`, that
128
+ file will be read and processed to configure a project within the ilib-lint tool
129
+ with that path as the root directory for the project.
112
130
 
113
- * name (String) - name of this project
131
+ This json config file will be parsed as [JSON5](https://json5.org), which means
132
+ it can contain comments and other nice features that make it easier for humans
133
+ to read and write.
134
+
135
+ The `ilib-lint-config.json` file can have any of the following properties:
136
+
137
+ * name (String, required) - name of this project
114
138
  * locales (Array of strings) - that name the default set of locales for the
115
139
  whole project if they are not configured by each path
116
140
  * sourceLocale (String) - name the locale for source strings in this app.
117
141
  Default if not specified is "en-US".
118
- * paths (Object) - a set of configurations for various paths that are given
119
- by a [micromatch](https://github.com/micromatch/micromatch) glob expression.
120
- Each glob expression property should be an object that contains settings
121
- that apply to files that match the glob expression. The settings can be
122
- be any of:
123
- * locales (Array of strings) - a set of locales that override
124
- the global locales list
125
- * rules - (Object) a set of rules to use with this set of files.
126
- Each rule name maps either to a boolean (true means turn it
127
- on, and false means off) or to a string or object that gives
128
- options for the rule. (Each rule can be different)
129
- * rulesets (Array of strings) - list the names of rule sets to
130
- turn on. Rulesets are groups of rules that are typically
131
- bundled together for particular purpose, such as rules for a
132
- particular library in a particular programming languages. For
133
- example, you might have a "android-kotlin" ruleset that turns
134
- on a number of rules that are typical for checking Android
135
- apps written in Kotlin. If a ruleset is given, the
136
- individual rules in that rule set can still be overridden using
137
- the "rules" property above. Rulesets are just a short-hand
138
- to turn on many of them at once.
139
- * template (string) - a template that can be used to parse the
142
+ * excludes (Array of String) - an array of micromatch expressions for files
143
+ or folders in the project to exclude from the recursive search.
144
+ * rules (Array of Object) - an array of declarative regular-expression-based rules to use
145
+ with this project. Resource rules are applied to resources loaded from a
146
+ resource file. Source file rules are applied to regular programming source
147
+ files. Each item in the rules array should be an
148
+ object that contains the following properties, all of which are required:
149
+ * type (String) - the type of this rule. This may be any of the
150
+ following:
151
+ * resource-matcher - check resources in a resource file. The
152
+ regular expressions that match in the
153
+ source strings must also match in the target string
154
+ * resource-source - check resources in a resource file. If
155
+ the regular expressions match in the source string of a
156
+ resource, a result will be generated
157
+ * resource-target - check resources in a resource file. If
158
+ the regular expressions match in the target string of a
159
+ resource, a result will be generated
160
+ * sourcefile - Check the text in a source file, such as a
161
+ java file or a python file. Regular expressions that match
162
+ in the source file will generate results
163
+ * name (String) - a unique dash-separated name of this rule.
164
+ eg. "resource-url-match",
165
+ * description (String) - a description of what this rule is trying
166
+ to do. eg. "Ensure that URLs that appear in the source string are
167
+ also used in the translated string"
168
+ * note (String) - string to use when the regular expression check fails.
169
+ eg. "URL '{matchString}' from the source string does not appear in
170
+ the target string"
171
+ Note that you can use `{matchString}` to show the user the string
172
+ that the regular expression matched in the source but not in the target.
173
+ * regexps (Array.<String>) - an array of regular expressions to match
174
+ in the source and target strings. If any one of those expressions
175
+ matches in the source, but not the target, the rule will create
176
+ a Result that will be formatted for the user.
177
+ * formatters (Array of Object) - a set of declarative formatters. Each array element is
178
+ an object that contains the following properties:
179
+ * name - a unique name for this formatter
180
+ * description - a description of this formatter to show to users
181
+ * template - a template string that shows how the various fields of a Result instance should be
182
+ formatted, plus two extras that come from the rule: ruleName and ruleDescription
183
+ * highlightStart - string to use as the highlight starting marker in the highlight string
184
+ * highlightEnd - string to use as the highlight ending marker in the highlight string
185
+ * rulesets (Object) - configure named sets of rules. Some rules can be shared between
186
+ file types and others are more specific to the file type. As such, it is sometimes
187
+ convenient to to name a set of rules and refer to the whole set by its name instead
188
+ of listing them all out. The properties of the rulesets object are the names of the
189
+ sets, and the values is also a Object that configures each rule that should be
190
+ included in that set. The rules are turned on with a value "true" or with a
191
+ rule-specific option. They are turned off with a falsy value.
192
+ * filetypes (Object) - a set of configurations for various file types. The file types
193
+ are given dash-separated names such as "python-source-files" so that they can be referred
194
+ to in the
195
+ paths object below. Properties in the filetypes object are the name of the file type,
196
+ and the values are an object that gives the settings for that file type. The value
197
+ object can contain any of the following properties:
198
+ * template (String, required) - a template that can be used to parse the
140
199
  file name for the locale of that file.
200
+ * locales (Array of String) - a set of locales that override
201
+ the global locales list. If not specified, the file type uses the
202
+ global set of locales.
203
+ * ruleset (String, Array of String, or Object) - name the rule set or
204
+ list of rule sets to use with files of this type if the value is
205
+ a string or an array of strings. When the value is a list of strings,
206
+ the rules are a superset of all of the rules in the named rule sets.
207
+ If the value is an object, then it is considered to be an on-the-fly
208
+ unnamed rule set defined directly.
209
+ * paths (Object) - this maps sets of files to file types. The properties in this
210
+ object are [micromatch](https://github.com/micromatch/micromatch) glob expressions
211
+ that select a subset of files within the current project. The glob expressions
212
+ can only be relative to the root of the project.
213
+ The value of each glob expression property should be either a string that names
214
+ a file type for files that match the glob expression, or an on-the-fly unnamed
215
+ definition of the file type. If you specify the file type directly, it cannot be
216
+ shared with other mappings, so it is usually a good idea to define a named file type
217
+ in the "filetypes" property first.
141
218
 
142
219
  The `ilib-lint-config.json` file can be written in [JSON5](https://github.com/json5/json5)
143
220
  syntax, which means it can contain comments and other enhancements.
@@ -148,34 +225,87 @@ Here is an example of a configuration file:
148
225
 
149
226
  ```json
150
227
  {
228
+ // the name is required and should be unique amongst all your projects
151
229
  "name": "tester",
230
+ // this is the global set of locales that applies unless something else overrides it
152
231
  "locales": [
153
232
  "en-US",
154
233
  "de-DE",
155
234
  "ja-JP",
156
235
  "ko-KR"
157
236
  ],
158
- "excludes": {
237
+ // list of plugins to load
238
+ "plugins": [
239
+ "react"
240
+ ],
241
+ // default micromatch expressions to exclude from recursive dir searches
242
+ "excludes": [
159
243
  "node_modules/**",
160
244
  ".git/**",
161
245
  "test/**"
246
+ ],
247
+ // declarative definitions of new rules
248
+ "rules": [
249
+ // test that named parameters like {param} appear in both the source and target
250
+ {
251
+ "name": "resource-named-params",
252
+ "type": "resource-matcher",
253
+ "description": "Ensure that named parameters that appear in the source string are also used in the translated string",
254
+ "note": "The named parameter '{matchString}' from the source string does not appear in the target string",
255
+ "regexps": [ "\\{\\w+\\}" ],
256
+ "link": "https://github.com/ilib-js/i18nlint/blob/main/README.md"
257
+ }
258
+ ],
259
+ "formatters": [
260
+ {
261
+ "name": "minimal",
262
+ "description": "A minimalistic formatter that only outputs the path and the highlight",
263
+ "template": "{pathName}\n{highlight}\n",
264
+ "highlightStart": ">>",
265
+ "highlightEnd": "<<"
266
+ }
267
+ ],
268
+ // named rule sets to be used with the file types
269
+ "rulesets": {
270
+ "react-rules": {
271
+ // this is the declarative rule defined above
272
+ "resource-named-params": true,
273
+ // the "localeOnly" is an option that the quote matcher supports
274
+ // so this both includes the rule in the rule set and instantiates
275
+ // it with the "localeOnly" option
276
+ "resource-quote-matcher": "localeOnly"
277
+ }
162
278
  },
163
- "paths": {
164
- "src/**/*.json": {
279
+ // defines common settings for a particular types of file
280
+ "filetypes": {
281
+ "json": {
165
282
  // override the general locales
166
283
  "locales": [
167
284
  "en-US",
168
285
  "de-DE",
169
286
  "ja-JP"
170
- ],
287
+ ]
288
+ },
289
+ "javascript": {
171
290
  "rulesets": [
172
- "javascript-ilib"
291
+ "react-rules"
173
292
  ]
174
293
  },
175
- // check the translations from the translation vendor before
176
- // we use them in our project
294
+ "jsx": {
295
+ "rulesets": [
296
+ "react-rules"
297
+ ]
298
+ }
299
+ },
300
+ // this maps micromatch path expressions to a file type definition
301
+ "paths": {
302
+ // use the file type defined above
303
+ "src/**/*.json": "json",
304
+ "src/**/*.js": "javascript",
305
+ "src/**/*.jsx": "jsx",
306
+ // define a file type on the fly
177
307
  "**/*.xliff": {
178
- "rules": {
308
+ "ruleset": {
179
309
  "formatjs-plural-syntax": true,
180
310
  "formatjs-plural-categories": true,
181
311
  "formatjs-match-substitution-params": true,
@@ -204,9 +334,243 @@ The built-in rules are:
204
334
  - resource-unique-key - Ensure that the keys are unique within a locale across
205
335
  all resource files
206
336
 
337
+ ## Writing Plugins
338
+
339
+ The linter tool can support plugins that provide parsers, formatters, or rules,
340
+ or any of them at the same time.
341
+
342
+ ## Common Code
343
+
344
+ All plugins should import and use the classes in the
345
+ [i18nlint-common](https://github.com/ilib-js/i18nlint-common) package.
346
+ This defines the super classes for each plugin type, as well as a number
347
+ of utility classes and functions.
348
+
349
+ ### Linter Plugins
350
+
351
+ Linter plugins are simple wrappers around the parser, formatter, and rule
352
+ plugins, which allow the plugin to define multiple plugins. For example, many
353
+ plugins define multiple related rules at the same time which check for
354
+ different aspects of a string.
355
+
356
+ The linter plugin should override and implement these three methods:
357
+
358
+ - getParsers - return an array of classes that inherit from the Parser class
359
+ - getRules - return an array of classes that inherit from the Rule class
360
+ - getFormatters - return an array of classes that inherit from the Formatter class
361
+
362
+ For rules and formatters, each array entry can be either declarative or
363
+ programmatic. See the descriptions below about declarative and programmatic
364
+ plugins.
365
+
366
+ When returning programmatic plugins, make sure to return the actual class
367
+ itself instead of instances of the class. The linter will need to create
368
+ multiple instances of each class during the run of the program.
369
+
370
+ ### Parsers
371
+
372
+ The job of the parser is to convert a source file into an intermediate representation
373
+ that rules can easily digest. There are two standard representations that many
374
+ rules use, but your parser and rules can use their own representation, as
375
+ long as the parser and the rules agree on what that format is. Typically, the
376
+ parser will produce something like an abstract syntax tree (AST) that the rules
377
+ know how to traverse and interpret.
378
+
379
+ The two standard representations are:
380
+
381
+ - resources - the file is converted into a set of Resource instances
382
+ - lines - the file in converted into a simple array of lines
383
+
384
+ The resources representation is intended to represent entries in resource files
385
+ such as xliff files, gnu po files, or java properties files. Each entry in the
386
+ resource file is represented as an instance of one of the standard resource
387
+ classes all defined in the
388
+ [ilib-tools-common](https://github.com/ilib-js/ilib-tools-common)
389
+ project:
390
+
391
+ - ResourceString - the resource is a single string
392
+ - ResourceArray - the resource is an array of strings
393
+ - ResourcePlural - the resource is a plural string
394
+
395
+ The power of a resource file is that resources can contain both a source and a
396
+ target string, so the rules are able to check the source strings against the target
397
+ strings. Regularly, source files only have source strings in them (if any) so
398
+ the target translations cannot be checked.
399
+
400
+ Parsers should extend the `Parser` class from the `i18nlint-common` package. The constructor
401
+ for your class should define the following properties:
402
+
403
+ - `this.name` - a unique name for this parser
404
+ - `this.description` - a description of this type of parser to display to users
405
+
406
+ It should also override the
407
+ [parseData()](https://github.com/iLib-js/i18nlint-common/blob/main/src/Parser.js)
408
+ method which parses a string, and the
409
+ [parse()](https://github.com/iLib-js/i18nlint-common/blob/main/src/Parser.js)
410
+ method, which loads data from the file and then parses it.
411
+
412
+ You can see an example of a parser plugin by looking at the gnu PO file parser at
413
+ [ilib-lint-python-gnu](https://github.com/ilib-js/ilib-lint-python-gnu/blob/main/src/POParser.js).
414
+ That parser interprets a .po file as a resource file and returns a set of
415
+ Resource instances.
416
+
417
+ ### Rules
418
+
419
+ Rules interpret the intermediate representation of a file produced by a Parser
420
+ and produce a single
421
+ [Result](https://github.com/iLib-js/i18nlint-common/blob/main/src/Result.js)
422
+ instance, an array of Result instances, one for each problem found, or undefined
423
+ if there are no problems found.
424
+
425
+ There are two types of rules, declarative and programmatic.
426
+
427
+ Declarative rules are simply a list of regular expressions with metadata. The
428
+ linter searches for matches to those regular expressions and produces Result
429
+ instances when found. (Or when they are not found in some cases!)
430
+
431
+ These can be declared in the config file. (See the example config file above.)
432
+
433
+ Each declarative rule should have the following properties:
434
+
435
+ * type (String) - the type of this rule. This may be any of the
436
+ following:
437
+ * resource-matcher - check resources in a resource file. The
438
+ regular expressions that match in the
439
+ source strings must also match in the target string
440
+ * resource-source - check resources in a resource file. If
441
+ the regular expressions match in the source string of a
442
+ resource, a result will be generated
443
+ * resource-target - check resources in a resource file. If
444
+ the regular expressions match in the target string of a
445
+ resource, a result will be generated
446
+ * sourcefile - Check the text in a source file, such as a
447
+ java file or a python file. Regular expressions that match
448
+ in the source file will generate results
449
+ * name (String) - a unique dash-separated name of this rule.
450
+ eg. "resource-url-match",
451
+ * description (String) - a description of what this rule is trying
452
+ to do. eg. "Ensure that URLs that appear in the source string are
453
+ also used in the translated string"
454
+ * note (String) - string to use when the regular expression check fails.
455
+ eg. "URL '{matchString}' from the source string does not appear in
456
+ the target string"
457
+ Note that you can use `{matchString}` to show the user the string
458
+ that the regular expression matched in the source but not in the target.
459
+ * regexps (Array.<String>) - an array of regular expressions to match
460
+ in the source and target strings. If any one of those expressions
461
+ matches in the source, but not the target, the rule will create
462
+ a Result that will be formatted for the user.
463
+ * link (String) - an URL to a website with a more complete explanation
464
+ of the problem that was found and how the problem can be resolved
465
+ and avoided in the future. Often, this is a link to a markdown file
466
+ in the docs folder on the github repo for the plugin, but it can be
467
+ any link you like.
468
+
469
+ Programmatic rules are used when the requirements for the rules are more complicated
470
+ than a simple regular expression string can handle. For example, a rule that checks
471
+ if the target string of a resource has the correct quote style for the target
472
+ locale first needs to look up what the correct quote style even is in
473
+ order to apply the rule.
474
+
475
+ In order to create a rule instance, create a class that extends the
476
+ [Rule](https://github.com/ilib-js/i18nlint-common/blob/main/src/Rule.js)
477
+ class in the [i18nlint-common](https://github.com/ilib-js/i18nlint-common/] project.
478
+ The constructor of this class should define the following properties:
479
+
480
+ - `this.name` - a unique name for this rule
481
+ - `this.description` - a description of this type of rule to display to users
482
+
483
+ There are no rules for what to name your Rule, but the Rules written by the ilib-js
484
+ organization generally follow some conventions. Resource checkers start with
485
+ "resource-" and source file checkers start with "source-". For resource checkers,
486
+ the word "match" is used at the end when checking both the source and target,
487
+ "source" is used at the end when checking only the source string, and "target"
488
+ when checking only the target string. So, "resource-urls-match" is a Rule that
489
+ checks resource files for URLs in both the source and target. You are free to name
490
+ your rules anything you like or to follow the conventions above. The important
491
+ part is that the name should uniquely identify your Rule so that you can use it
492
+ in config files.
493
+
494
+ The rule should also override and implement the getRuleType() method and the
495
+ [match()](https://github.com/iLib-js/i18nlint-common/blob/main/src/Rule.js) method,
496
+ which takes an intermediate representation as a parameter and returns either
497
+ a single Result, an array of Result, or undefined.
498
+
499
+ If you would like to see an example rule plugin, see the definition of
500
+ the built-in ICU plural matcher rule:
501
+ [resource-icu-plurals](https://github.com/ilib-js/i18nlint/blob/main/src/rules/ResourceICUPlurals.js)
502
+ which checks resources to make sure that plurals in source and target strings
503
+ have the correct syntax for ICU and formatjs.
504
+
505
+ ### Formatters
506
+
507
+ Formatters transform a [Result object](https://github.com/iLib-js/i18nlint-common/blob/main/src/Result.js) into a format that the consumer can use. For the most part, the consumer
508
+ is a human, so the result should be formatted in text so that the user can read
509
+ it easily. Other times, the consumer is another program, so the result should be
510
+ formatted for easy parsing. Formatters can formats the results in any way necessary.
511
+
512
+ There are two types of formatters, declarative and programmatic.
513
+
514
+ Declarative formatters are simply a template string where properties of the Result
515
+ instances are formatted into it. These can be declared in the config file. (See the
516
+ example config file above.)
517
+
518
+ The template strings may have any of the following fields from the Result instance
519
+ in them:
520
+
521
+ - severity
522
+ - pathName
523
+ - lineNumber
524
+ - source
525
+ - highlight
526
+ - id
527
+
528
+ Additionally, they may have the following fields from the Rule instance in them:
529
+
530
+ - ruleDescription
531
+ - ruleName
532
+ - ruleLink
533
+
534
+ Programmatic formatters are used when the requirements for formatting are more complicated
535
+ than a simple template string can handle. For example, a CSV formatter would have to make
536
+ sure that fields in a CSV file are escaped properly to conform to CSV syntax, and would
537
+ include escaping code in it.
538
+
539
+ In order to create a formatter instance, create a class that extends the
540
+ [Formatter](https://github.com/ilib-js/i18nlint-common/blob/main/src/Formatter.js)
541
+ class in the [i18nlint-common](https://github.com/ilib-js/i18nlint-common/] project.
542
+ The constructor of this class should define the following properties:
543
+
544
+ - `this.name` - a unique name for this formatter
545
+ - `this.description` - a description of this type of formatter to display to users
546
+ - `this.link` - (optional) a link to a web page that gives a more complete explanation
547
+ the the rule and how to resolve the problem it found
548
+
549
+ The formatter should also override and implement the
550
+ [format()](https://github.com/iLib-js/i18nlint-common/blob/main/src/Formatter.js) method,
551
+ which takes a Result instance as a parameter and returns a formatted string.
552
+
553
+ If you would like to look at an example formatter plugin, see the definition of
554
+ the built-in default formatter
555
+ [ansi-console-formatter](https://github.com/ilib-js/i18nlint/blob/main/src/formatters/AnsiConsoleFormatter.js)
556
+ which formats a Result for colorful output on an ANSI console.
557
+
558
+ ## Example Plugin
559
+
560
+ You can take a look at the [ilib-lint-python](https://github.com/ilib-js/ilib-lint-python)
561
+ plugin as a working example of ilib-lint plugin. It implements some rules that
562
+ check the various types of substitution parameters that python/django and
563
+ gnu gettext support.
564
+
565
+ Additionally, there is a [sample python project](https://github.com/ilib-js/ilib-samples/lint)
566
+ that uses the ilib-lint-python plugin. It has purposeful errors built into it which
567
+ violate the rules implemented in the plugin so that the linter will produce some output.
568
+ Clone the project, cd to the lint directory, run `npm install`, and then `npm run lint`
569
+ to see the results.
570
+
207
571
  ## License
208
572
 
209
- Copyright © 2022, JEDLSoft
573
+ Copyright © 2022-2023, JEDLSoft
210
574
 
211
575
  Licensed under the Apache License, Version 2.0 (the "License");
212
576
  you may not use this file except in compliance with the License.
@@ -223,6 +587,18 @@ limitations under the License.
223
587
 
224
588
  ## Release Notes
225
589
 
590
+ ### v1.2.0
591
+
592
+ - added Rule links to give rule writers a way of giving a more complete explanation
593
+ of the rule and how to resolve the problem.
594
+
595
+ ### v1.1.0
596
+
597
+ - added support for plugins
598
+ - added count at the end of the output
599
+ - added the --list option to show what things are available to
600
+ put in the config file
601
+
226
602
  ### v1.0.0
227
603
 
228
604
  - initial version