kg-ssot 4.17.2 → 4.18.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.
package/README.md CHANGED
@@ -1,85 +1,145 @@
1
1
  # kg-ssot
2
2
 
3
- **O leitor JavaScript do contrato do KG-SSOT, o grafo de conhecimento verificável: toda afirmação diz de onde veio,
4
- quando foi conferida e o que depende dela.**
3
+ [![npm](https://img.shields.io/npm/v/kg-ssot)](https://www.npmjs.com/package/kg-ssot)
4
+ [![license](https://img.shields.io/npm/l/kg-ssot)](https://github.com/marciocar/kg-ssot/blob/main/LICENSE)
5
+ [![node](https://img.shields.io/node/v/kg-ssot)](https://github.com/marciocar/kg-ssot)
5
6
 
6
- Este pacote julga um grafo `.kg.yaml` ou `.kg.json` contra o contrato vigente e devolve o veredito MUST e os avisos
7
- SHOULD, com os códigos estáveis da suíte de conformidade. Ele traz:
8
- - o **núcleo do leitor**, sem `fs` e sem processo;
9
- - os **validadores do contrato pré-compilados**, MUST e SHOULD, que não usam `new Function` nem `eval`. Por isso rodam
10
- em motores sem geração de código, como o Hermes do React Native e do Expo, e no Node;
11
- - os **schemas** do contrato e do veredito.
7
+ **The JavaScript reader for the KG-SSOT contract, the verifiable knowledge graph. Every claim says where it came
8
+ from, when someone verified it, and what depends on it.**
12
9
 
13
- O leitor de referência é o pacote Python [`kg-ssot`](https://pypi.org/project/kg-ssot/). Este leitor passa em todos
14
- os casos da suíte de conformidade (`latest/`), com o veredito, os códigos e os avisos esperados. O CI confere isso
15
- pelo pacote instalado a cada mudança no leitor.
10
+ `kg-ssot` checks a `.kg.yaml` or `.kg.json` graph against the current contract (v4). It returns the MUST verdict and
11
+ the SHOULD warnings, with the stable codes of the conformance suite. The package ships:
16
12
 
17
- ## Instalar
13
+ - **The reader core**, which uses neither `fs` nor `process`.
14
+ - **Precompiled contract validators**, MUST and SHOULD, that never call `new Function` or `eval`. They run on engines
15
+ without runtime code generation, like Hermes in React Native and Expo, and on Node.
16
+ - **The schemas** of the contract and of the verdict.
17
+
18
+ The reference reader is the Python package [`kg-ssot`](https://pypi.org/project/kg-ssot/). This reader passes every
19
+ case in the conformance suite (`latest/`) with the expected verdict, codes, and warnings. On every change to the
20
+ reader, CI runs the whole suite against the installed tarball.
21
+
22
+ ## Install
18
23
 
19
24
  ```bash
20
25
  npm install kg-ssot
21
26
  ```
22
27
 
23
- ## No código
28
+ On Node, you need version 22.12 or later. The package is ESM and ships its own TypeScript types.
29
+
30
+ ## Use it in code
24
31
 
25
32
  ```js
26
33
  import { verdict } from 'kg-ssot';
27
34
 
28
- verdict(texto); // .kg.yaml (o default)
29
- verdict(texto, { format: 'json' }); // .kg.json
35
+ verdict(text); // .kg.yaml (the default)
36
+ verdict(text, { format: 'json' }); // .kg.json
30
37
  // { verdict: 'accept', codes: [], warnings: [], detail: '' }
31
38
  // { verdict: 'accept', codes: [], warnings: ['yaml.unquoted-date'], detail: '' }
32
- // { verdict: 'reject', codes: ['form.range.node.impact'], warnings: [], detail: '' }
39
+ // { verdict: 'reject', codes: ['form.range.node.impact', 'form.type.node.impact'], warnings: [], detail: '' }
33
40
  ```
34
41
 
35
- - `verdict(texto, { format, corpus })` devolve o veredito no contrato de runner da suíte: `verdict` é `accept` ou
36
- `reject`, `codes` traz os códigos MUST e `warnings` os avisos SHOULD, os dois ordenados e sem repetição. Um aviso
37
- não reprova: o grafo com aviso continua `accept`.
38
- - `check(texto, { format, corpus })` devolve só os códigos MUST (vazio quando passa), e `checkWarnings(texto,
39
- { format })` só os avisos.
40
- - `kg-ssot/core` é o núcleo sem o validador, para quem compila o schema por conta própria. `kg-ssot/node` traz os
41
- auxiliares de Node, como `corpusIndex(dir)`, que indexa os `.kg.yaml` de um diretório para conferir as
42
- `external_edges`. Os schemas ficam em `kg-ssot/spec/*`.
42
+ - `verdict(text, { format, corpus })` returns the verdict in the suite's runner contract. `verdict` is `accept` or
43
+ `reject`, `codes` lists the MUST codes, and `warnings` lists the SHOULD codes. Both lists are sorted, with no
44
+ duplicates. A warning never fails a graph: a graph with warnings is still `accept`.
45
+ - `check(text, { format, corpus })` returns only the MUST codes (empty when the graph passes).
46
+ `checkWarnings(text, { format, corpus })` returns only the warnings.
47
+ - `corpus` lets the reader verify the far end of each `external_edges` entry. It is a
48
+ `Map<path, Set<nodeId>>`. Build it with `corpusIndex(dir)` from `kg-ssot/node`, or, without a file system, with
49
+ `indexTexts([[path, text], ...])` from the main entry.
43
50
 
44
- Desde a 4.10.0, este leitor também mede os avisos SHOULD, com os mesmos códigos do leitor de referência em todos os
45
- casos da suíte, e desde a 4.13.0 trata o `\r` como quebra de linha, como a referência. Desde a 4.11.0, as três construções raras do YAML em que ele divergia da referência (a tag `!` sem
46
- nome, o `!!timestamp` com texto que não é data e o tab fora de aspas) reprovam nos dois, com casos na suíte. A linha de cada ocorrência e as dicas de correção continuam só no leitor de referência em Python.
51
+ ### Entry points
47
52
 
48
- ## TypeScript e CommonJS
53
+ | Import | What you get |
54
+ |---|---|
55
+ | `kg-ssot` | `verdict`, `check`, `checkWarnings`, the precompiled `validateMust` and `validateShould`, and the core |
56
+ | `kg-ssot/core` | The core without the validators, for when you compile the schema yourself (`verdictOf`, `codes`, `warnings`, `parse`, `indexTexts`) |
57
+ | `kg-ssot/must`, `kg-ssot/should` | The precompiled MUST and SHOULD validators alone |
58
+ | `kg-ssot/node` | Node helpers: `corpusIndex(dir)` indexes the `.kg.yaml` and `.kg.json` graphs under a directory (the tracked ones, in a git repository); `compileMust()` compiles the schema with `ajv` at runtime |
59
+ | `kg-ssot/spec/*` | The contract and verdict schemas |
49
60
 
50
- O pacote traz os tipos (`index.d.mts`, `core.d.mts` e `node.d.mts`) e funciona com `moduleResolution` `bundler` ou
51
- `nodenext`, o que cobre o template TypeScript do Expo. É um pacote ESM. Num projeto CommonJS, o `require('kg-ssot')`
52
- funciona a partir do Node 22.12, que é o mínimo declarado em `engines`.
61
+ Since 4.10.0, this reader also measures the SHOULD warnings, with the same codes as the reference reader on every
62
+ suite case. Since 4.13.0, it treats `\r` as a line break, like the reference. Since 4.11.0, the three rare YAML
63
+ constructs where it used to diverge from the reference (the unnamed `!` tag, a `!!timestamp` whose text is not a
64
+ date, and a tab outside quotes) now fail in both, and the suite has cases for them. Only the Python reference reader
65
+ reports the line of each finding and the fix hints.
53
66
 
54
- ## Na linha de comando
67
+ ## TypeScript and CommonJS
68
+
69
+ The package ships types (`index.d.mts`, `core.d.mts`, `node.d.mts`, and `validator.d.mts`). It works with
70
+ `moduleResolution` set to `bundler` or `nodenext`, which covers the Expo TypeScript template. It is an ESM package.
71
+ In a CommonJS project, `require('kg-ssot')` works from Node 22.12, the minimum declared in `engines`.
72
+
73
+ ## Use it from the command line
55
74
 
56
75
  ```bash
57
- npx kg-ssot-validate grafo.kg.yaml outro.kg.json
76
+ npx kg-ssot-validate decisions.kg.yaml graph.kg.yaml other.kg.json
58
77
  ```
59
78
 
60
79
  ```text
61
- PASSA grafo.kg.yaml
80
+ PASS decisions.kg.yaml
81
+ PASS graph.kg.yaml
82
+ WARNING yaml.unquoted-date
83
+ FAIL other.kg.json
84
+ MUST form.range.node.impact · form.type.node.impact
85
+ ```
86
+
87
+ The exit code is 0 when every file passes, 1 when any file fails, and 2 on broken input: an unreadable file, invalid
88
+ UTF-8, an unknown option, or a `--corpus` that is not a directory. Warnings never change the exit code.
89
+
90
+ - `--json` prints one JSON line per file, in the suite's runner contract:
91
+
92
+ ```text
93
+ {"file":"graph.kg.yaml","verdict":"accept","codes":[],"warnings":["yaml.unquoted-date"],"detail":""}
94
+ {"file":"other.kg.json","verdict":"reject","codes":["form.range.node.impact","form.type.node.impact"],"warnings":[],"detail":""}
95
+ ```
96
+
97
+ - `--corpus DIR` verifies the far end of each `external_edges` entry against the `.kg.yaml` and `.kg.json` graphs
98
+ under `DIR`.
99
+
100
+ The human output is in English. The CLI speaks Brazilian Portuguese when you set `KG_SSOT_LANG=pt-BR` (or when your
101
+ locale is `pt_BR`):
102
+
103
+ ```bash
104
+ KG_SSOT_LANG=pt-BR npx kg-ssot-validate decisions.kg.yaml graph.kg.yaml other.kg.json
105
+ ```
106
+
107
+ ```text
108
+ PASSA decisions.kg.yaml
109
+ PASSA graph.kg.yaml
62
110
  AVISO yaml.unquoted-date
63
- REPROVA outro.kg.json
64
- MUST form.range.node.impact · integrity.orphan-node
111
+ REPROVA other.kg.json
112
+ MUST form.range.node.impact · form.type.node.impact
65
113
  ```
66
114
 
67
- O rc é 0 quando todos passam, 1 quando algum reprova e 2 com arquivo ilegível ou opção desconhecida. Os avisos não
68
- mudam o rc. Com `--json`, sai uma linha JSON por
69
- arquivo. Com `--corpus DIR`, a ponta externa das `external_edges` é conferida contra os `.kg.yaml` sob `DIR`.
115
+ The codes and the `--json` output are data, so they never change language.
70
116
 
71
- ## Num app React Native ou Expo
117
+ ## Use it in a React Native or Expo app
118
+
119
+ The package is ESM, and the main entry imports nothing from Node. Your bundler (Metro, esbuild) resolves the two
120
+ dependencies, `yaml` and the `ajv` runtime. The validators come precompiled, so nothing has to generate code on the
121
+ device. We ran the conformance suite on Hermes, and it returns the same verdicts as Node.
122
+
123
+ ```js
124
+ import { verdict, indexTexts } from 'kg-ssot';
125
+
126
+ const corpus = indexTexts([['other.kg.yaml', otherText]]); // optional, for external_edges
127
+ const result = verdict(graphText, { corpus });
128
+ if (result.verdict === 'reject') console.warn(result.codes);
129
+ ```
72
130
 
73
- O pacote é ESM e a entrada não importa nada do Node. Um bundler (Metro, esbuild) resolve as dependências, `yaml` e o
74
- runtime do `ajv`. O validador já vem compilado, então nada precisa gerar código no aparelho. O comportamento foi
75
- medido no Hermes: os mesmos vereditos do Node.
131
+ ## Documentation
76
132
 
77
- ## Documentação
133
+ - [Manual](https://github.com/marciocar/kg-ssot/tree/main/docs/manual): the format, the contract, the tools, and
134
+ common problems
135
+ - [Conformance suite](https://github.com/marciocar/kg-ssot/blob/main/spec/conformance/README.md)
136
+ - [Adoption guide](https://github.com/marciocar/kg-ssot/blob/main/ADOPTING.md)
137
+ - [Changelog](https://github.com/marciocar/kg-ssot/blob/main/CHANGELOG.md)
138
+ - [Issues](https://github.com/marciocar/kg-ssot/issues)
78
139
 
79
- - [Manual](https://github.com/marciocar/kg-ssot/tree/main/docs/manual): o formato, o contrato, as ferramentas e os problemas comuns
80
- - [Suíte de conformidade](https://github.com/marciocar/kg-ssot/blob/main/spec/conformance/README.md)
81
- - [Guia de adoção](https://github.com/marciocar/kg-ssot/blob/main/ADOPTING.md)
140
+ ## License
82
141
 
83
- ## Licença
142
+ Apache-2.0. See [`LICENSE`](https://github.com/marciocar/kg-ssot/blob/main/LICENSE) and
143
+ [`NOTICE`](https://github.com/marciocar/kg-ssot/blob/main/NOTICE).
84
144
 
85
- Apache-2.0. Veja `LICENSE` e `NOTICE`.
145
+ Documentação em português: [README.pt-BR](https://github.com/marciocar/kg-ssot/blob/main/README.pt-BR.md).
package/cli.mjs CHANGED
@@ -4,14 +4,60 @@
4
4
  // kg-ssot-validate <arquivo>... [--json] [--corpus DIR]
5
5
  //
6
6
  // rc 0 quando todos passam no MUST, 1 quando algum reprova, 2 com entrada quebrada (arquivo ilegível, UTF-8
7
- // inválido). Os avisos SHOULD saem numa linha AVISO e não mudam o rc. --json imprime uma linha JSON por arquivo, no contrato de runner da suíte. --corpus confere a ponta
7
+ // inválido). Os avisos SHOULD saem numa linha WARNING e não mudam o rc. --json imprime uma linha JSON por arquivo, no contrato de runner da suíte. --corpus confere a ponta
8
8
  // externa das external_edges contra os .kg.yaml e .kg.json sob DIR.
9
+ //
10
+ // A saída humana sai em inglês (a língua-fonte) ou na tradução pt-BR, negociada como no kg_i18n do Python:
11
+ // KG_SSOT_LANG, LANGUAGE, LC_ALL, LC_MESSAGES e LANG, parando na primeira definida; C, POSIX e vazio são o inglês.
12
+ // Como no gettext, o LANGUAGE só vale quando o locale efetivo (LC_ALL, LC_MESSAGES, LANG) não é o C.
13
+ // As frases ficam também no catálogo locale/pt_BR/LC_MESSAGES/kg-ssot.po (referência #: packaging/npm/cli.mjs), para o tradutor ter um lugar
14
+ // só. A saída --json é dado e não se traduz.
9
15
  import { readFileSync } from 'node:fs';
10
16
 
11
17
  import { verdict } from './index.mjs';
12
18
  import { corpusIndex } from './run.mjs';
13
19
 
14
- const USAGE = 'uso: kg-ssot-validate <arquivo.kg.yaml|arquivo.kg.json>... [--json] [--corpus DIR]\n';
20
+ // A tradução pt-BR (igual às entradas do cli.mjs no kg-ssot.po); a chave é o msgid em inglês.
21
+ const PT_BR = {
22
+ 'usage: kg-ssot-validate <file.kg.yaml|file.kg.json>... [--json] [--corpus DIR]':
23
+ 'uso: kg-ssot-validate <arquivo.kg.yaml|arquivo.kg.json>... [--json] [--corpus DIR]',
24
+ 'BROKEN INPUT': 'ENTRADA QUEBRADA',
25
+ '--corpus without a directory': '--corpus sem diretório',
26
+ 'unknown option: {option}': 'opção desconhecida: {option}',
27
+ 'corpus {root} is not a directory': 'corpus {root} não é um diretório',
28
+ PASS: 'PASSA',
29
+ FAIL: 'REPROVA',
30
+ ERROR: 'ERRO',
31
+ WARNING: 'AVISO',
32
+ };
33
+
34
+ // true quando a língua do ambiente é o português (pt, pt_BR e afins); o inglês e língua sem tradução dão false.
35
+ function wantsPortuguese(vars) {
36
+ const isC = (v) => { const t = v.trim().split('.')[0].split('@')[0].toLowerCase(); return !t || t === 'c' || t === 'posix'; };
37
+ const effective = ['LC_ALL', 'LC_MESSAGES', 'LANG'].map((n) => vars[n]).find((v) => v);
38
+ const localeIsC = !effective || isC(effective);
39
+ for (const name of ['KG_SSOT_LANG', 'LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG']) {
40
+ const value = vars[name];
41
+ if (!value || (name === 'LANGUAGE' && localeIsC)) continue;
42
+ for (const raw of name === 'LANGUAGE' ? value.split(':') : [value]) {
43
+ const tag = raw.trim().split('.')[0].split('@')[0].replace(/-/g, '_').toLowerCase();
44
+ if (!tag || tag === 'c' || tag === 'posix') return false;
45
+ const lang = tag.split('_')[0];
46
+ if (lang === 'pt') return true;
47
+ if (lang === 'en') return false;
48
+ }
49
+ return false; // a variável definida decide, mesmo sem tradução: o inglês
50
+ }
51
+ return false;
52
+ }
53
+
54
+ const TABLE = wantsPortuguese(process.env) ? PT_BR : {};
55
+ const _ = (msgid, vals = {}) =>
56
+ (TABLE[msgid] ?? msgid).replace(/\{(\w+)\}/g, (m, k) => (k in vals ? String(vals[k]) : m));
57
+ // o rótulo da linha do arquivo, com a coluna do nome alinhada
58
+ const label = (msgid) => `${_(msgid).padEnd(7)} `;
59
+
60
+ const USAGE = `${_('usage: kg-ssot-validate <file.kg.yaml|file.kg.json>... [--json] [--corpus DIR]')}\n`;
15
61
  const args = process.argv.slice(2);
16
62
  let json = false;
17
63
  let corpusDir = null;
@@ -25,12 +71,12 @@ for (let i = 0; i < args.length; i += 1) {
25
71
  else if (a === '--corpus') {
26
72
  corpusDir = args[++i];
27
73
  if (!corpusDir) {
28
- process.stdout.write(`ENTRADA QUEBRADA --corpus sem diretório\n${USAGE}`);
74
+ process.stdout.write(`${_('BROKEN INPUT')} ${_('--corpus without a directory')}\n${USAGE}`);
29
75
  process.exit(2);
30
76
  }
31
77
  } else if (a === '--') files.push(...args.slice(i + 1)), (i = args.length);
32
78
  else if (a.startsWith('-')) {
33
- process.stdout.write(`ENTRADA QUEBRADA opção desconhecida: ${a}\n${USAGE}`);
79
+ process.stdout.write(`${_('BROKEN INPUT')} ${_('unknown option: {option}', { option: a })}\n${USAGE}`);
34
80
  process.exit(2);
35
81
  } else files.push(a);
36
82
  }
@@ -43,7 +89,8 @@ let corpus = null;
43
89
  try {
44
90
  corpus = corpusDir ? corpusIndex(corpusDir) : null;
45
91
  } catch (exc) {
46
- process.stdout.write(`ENTRADA QUEBRADA ${exc.message}\n`);
92
+ const msg = exc?.kgMsgid ? _(exc.kgMsgid, exc.kgArgs) : exc?.message;
93
+ process.stdout.write(`${_('BROKEN INPUT')} ${msg}\n`);
47
94
  process.exit(2);
48
95
  }
49
96
  for (const file of files) {
@@ -57,9 +104,9 @@ for (const file of files) {
57
104
  if (out.verdict === 'error') rc = 2;
58
105
  else if (out.verdict === 'reject' && rc === 0) rc = 1;
59
106
  if (json) process.stdout.write(`${JSON.stringify({ file, ...out })}\n`);
60
- else if (out.verdict === 'accept') process.stdout.write(`PASSA ${file}\n`);
61
- else if (out.verdict === 'reject') process.stdout.write(`REPROVA ${file}\n MUST ${out.codes.join(' · ')}\n`);
62
- else process.stdout.write(`ERRO ${file}\n ${out.detail}\n`);
63
- if (!json && out.warnings?.length) process.stdout.write(` AVISO ${out.warnings.join(' · ')}\n`);
107
+ else if (out.verdict === 'accept') process.stdout.write(`${label('PASS')}${file}\n`);
108
+ else if (out.verdict === 'reject') process.stdout.write(`${label('FAIL')}${file}\n MUST ${out.codes.join(' · ')}\n`);
109
+ else process.stdout.write(`${label('ERROR')}${file}\n ${out.detail}\n`);
110
+ if (!json && out.warnings?.length) process.stdout.write(` ${_('WARNING')} ${out.warnings.join(' · ')}\n`);
64
111
  }
65
112
  process.exit(rc);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kg-ssot",
3
- "version": "4.17.2",
4
- "description": "Leitor JS do contrato do grafo de conhecimento verificável KG-SSOT (.kg.yaml e .kg.json): validador pré-compilado, sem eval, para Node e React Native/Expo.",
3
+ "version": "4.18.0",
4
+ "description": "JavaScript reader for the KG-SSOT verifiable knowledge graph contract (.kg.yaml and .kg.json): a precompiled validator, no eval, for Node and React Native/Expo.",
5
5
  "keywords": [
6
6
  "knowledge-graph",
7
7
  "provenance",
package/run.mjs CHANGED
@@ -56,7 +56,13 @@ function corpusFiles(root) {
56
56
  }
57
57
 
58
58
  export function corpusIndex(root) {
59
- if (!statSync(root, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`corpus ${root} não é um diretório`);
59
+ if (!statSync(root, { throwIfNoEntry: false })?.isDirectory()) {
60
+ // a mensagem sai em inglês (a língua-fonte); o msgid e os argumentos vão junto, para o cli.mjs traduzir
61
+ const exc = new Error(`corpus ${root} is not a directory`);
62
+ exc.kgMsgid = 'corpus {root} is not a directory';
63
+ exc.kgArgs = { root };
64
+ throw exc;
65
+ }
60
66
  const items = [];
61
67
  for (const name of corpusFiles(root)) {
62
68
  const p = join(root, name);
package/spec/release.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "4.17.2",
3
- "tag": "kg-ssot-v4.17.2",
2
+ "version": "4.18.0",
3
+ "tag": "kg-ssot-v4.18.0",
4
4
  "contract": {
5
5
  "must": "spec/kg-contract-v4.schema.json",
6
6
  "should": "spec/kg-contract-v4.should.schema.json"
@@ -9,9 +9,11 @@
9
9
  "NOTICE",
10
10
  "LICENSE",
11
11
  "README.md",
12
+ "README.pt-BR.md",
12
13
  "llms.txt",
13
14
  "llms-full.txt",
14
15
  "ADOPTING.md",
16
+ "ADOPTING.pt-BR.md",
15
17
  "CHANGELOG.md",
16
18
  "CHANGELOG.pt-BR.md",
17
19
  "docs/manual/",
@@ -23,9 +25,12 @@
23
25
  "spec/query.schema.json",
24
26
  "spec/diff.schema.json",
25
27
  "spec/conformance/README.md",
28
+ "spec/conformance/README.pt-BR.md",
26
29
  "spec/conformance/manifest.schema.json",
27
30
  "spec/conformance/fixtures/",
28
31
  "spec/conformance/metaschema-tests/",
32
+ "tools/kg_i18n.py",
33
+ "locale/",
29
34
  "tools/kg_validate.py",
30
35
  "tools/kg_conformance.py",
31
36
  "tools/kg_gate.py",
@@ -62,6 +67,8 @@
62
67
  "spec/release.json",
63
68
  "spec/kg-contract-v4.schema.json",
64
69
  "spec/kg-contract-v4.should.schema.json",
70
+ "tools/kg_i18n.py",
71
+ "locale/",
65
72
  "tools/kg_validate.py",
66
73
  "tools/kg_gate.py",
67
74
  "tools/kg_vendor.py",
@@ -57,8 +57,8 @@
57
57
  "place": {"type": "string", "description": "o lugar legível: 'nó X', 'aresta #3 (A -> B)', 'meta', 'topo' ou 'arquivo'"},
58
58
  "path": {"type": "string", "description": "o caminho no documento, como nodes[7].provenance.method; vazio quando é o arquivo inteiro"},
59
59
  "line": {"type": ["integer", "null"], "minimum": 1, "description": "a linha (contando de 1) do ponto mais fundo do caminho que existe: no objeto ou mapa, a linha da chave; na lista, o início do item. Vale para o .kg.yaml e, desde a 4.8.2, para o .kg.json; null quando o documento não varre, quando a ocorrência não tem caminho (a chave desconhecida no topo, por exemplo) e no erro de parse sem marca"},
60
- "message": {"type": "string", "description": "a frase em pt-BR para pessoas; pode mudar entre versões"},
61
- "hint": {"type": "string", "description": "a correção canônica, quando o código tem uma"}
60
+ "message": {"type": "string", "description": "a frase para pessoas, na língua do ambiente (inglês por padrão, pt-BR com KG_SSOT_LANG=pt-BR ou locale pt_BR; desde a 4.18.0); pode mudar entre versões e línguas: compare pelo code"},
61
+ "hint": {"type": "string", "description": "a correção canônica, quando o código tem uma, na língua do ambiente como o message"}
62
62
  }
63
63
  }
64
64
  },