localewarden 0.0.0-stage → 0.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.
- package/LICENSE +21 -0
- package/README.md +268 -2
- package/dist/checks.d.ts +75 -0
- package/dist/checks.js +434 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +262 -0
- package/dist/config.d.ts +55 -0
- package/dist/config.js +106 -0
- package/dist/files.d.ts +44 -0
- package/dist/files.js +172 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/llm.d.ts +53 -0
- package/dist/llm.js +135 -0
- package/dist/placeholders.d.ts +22 -0
- package/dist/placeholders.js +129 -0
- package/dist/project.d.ts +18 -0
- package/dist/project.js +66 -0
- package/dist/prompt.d.ts +13 -0
- package/dist/prompt.js +62 -0
- package/dist/review.d.ts +19 -0
- package/dist/review.js +64 -0
- package/dist/state.d.ts +56 -0
- package/dist/state.js +86 -0
- package/dist/style.d.ts +39 -0
- package/dist/style.js +327 -0
- package/dist/translate.d.ts +50 -0
- package/dist/translate.js +378 -0
- package/dist/util.d.ts +14 -0
- package/dist/util.js +34 -0
- package/package.json +56 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Martin Brezina
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,269 @@
|
|
|
1
|
-
#
|
|
1
|
+
# localewarden
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Incremental AI translation for JSON locale files.** It translates only what changed, never overwrites a translation a person fixed, and checks every result before it is written.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx localewarden init # create a config
|
|
7
|
+
npx localewarden --dry-run # see what would be translated and roughly what it costs
|
|
8
|
+
npx localewarden # translate new and changed strings
|
|
9
|
+
npx localewarden check # quality check, no API calls (use it in CI)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Works with i18next, react-intl / FormatJS, vue-i18n, next-intl, ngx-translate and any other setup that keeps strings in JSON files. Uses any OpenAI-compatible API (OpenAI, OpenRouter, a local Ollama, ...).
|
|
13
|
+
|
|
14
|
+
## Why
|
|
15
|
+
|
|
16
|
+
Translating locale files with a language model is easy once. Keeping 20 languages correct while the source keeps changing is not:
|
|
17
|
+
|
|
18
|
+
- **Re-translating everything** on every change costs money and rewrites wording that was already fine.
|
|
19
|
+
- **Hand fixes get overwritten** by the next run.
|
|
20
|
+
- **Models are inconsistent across batches.** French screens mix "tu" and "vous", Spanish copies English Title Case ("Configure Su Cuenta"), and Polish or Russian address every user as a man.
|
|
21
|
+
- **Broken output ships silently.** A translated placeholder (`{heures}` instead of `{hours}`) shows raw braces in your app. A dropped `</strong>` breaks the layout. Stray Cyrillic letters end up in a Danish sentence.
|
|
22
|
+
|
|
23
|
+
localewarden grew out of the translation pipeline of a production app that ships in 38 languages. Every rule and check in it exists because one of these failures happened in real output.
|
|
24
|
+
|
|
25
|
+
## What it does
|
|
26
|
+
|
|
27
|
+
- **Translates only what changed.** It remembers a hash of each source string per language. New strings are translated. Changed strings are *revised*: the model gets the existing translation and changes only what the source change requires. Removed strings are deleted from every language.
|
|
28
|
+
- **Protects hand edits.** If someone edited a translation, localewarden detects it, keeps it, and lists it for review. If the source of a hand-edited string changes later, the string is flagged instead of overwritten.
|
|
29
|
+
- **Checks every result before writing it.** Broken placeholders, foreign alphabets, changed links, broken HTML and echoed source text are rejected (retried once, then left for the next run). Softer problems are retried and reported.
|
|
30
|
+
- **Consistent style per language.** It enforces formal or informal address per language (`du`/`Sie`, `tu`/`vous`, `ты`/`вы` and 16 more), uses sentence case where the language does, avoids gendered forms for "you", and applies local typography (French spacing, `92 %` in German, CJK quotation marks).
|
|
31
|
+
- **Glossary and protected names.** You choose fixed renderings ("Privacy Policy" -> "Politique de confidentialité") and names that must never be translated. The check accepts grammatical case endings.
|
|
32
|
+
- **Quality check for CI.** `localewarden check` runs all checks without any API calls and exits non-zero on errors.
|
|
33
|
+
- **Targeted repair.** `--fix-flagged` asks the model to fix only what the check flagged. The fix is accepted only if the problem is gone and little else changed.
|
|
34
|
+
- **Budget control.** A token budget per run, a dry run with a cost estimate, and graceful stop and resume.
|
|
35
|
+
- **No runtime dependencies.** Node.js 20+.
|
|
36
|
+
|
|
37
|
+
## Example
|
|
38
|
+
|
|
39
|
+
Source (`locales/en.json`) and config for a small plant-watering app:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"app": { "tagline": "Keep Your Plants Alive Without Thinking About It" },
|
|
44
|
+
"onboarding": {
|
|
45
|
+
"welcome": "Welcome to Plantly, {name}!",
|
|
46
|
+
"tip": "Most houseplants tend to need less water in winter."
|
|
47
|
+
},
|
|
48
|
+
"reminders": { "due_other": "{{count}} plants need water today", "snooze": "Remind Me Tomorrow" },
|
|
49
|
+
"settings": { "privacy": "Read our <a href=\"/privacy\">Privacy Policy</a> to see what we store." }
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"targetLanguages": ["de", "fr", "es", "ja"],
|
|
56
|
+
"files": "locales/{lang}.json",
|
|
57
|
+
"context": "Plantly is a mobile app that reminds people when to water their houseplants.",
|
|
58
|
+
"doNotTranslate": ["Plantly"],
|
|
59
|
+
"formality": { "de": "informal", "fr": "formal", "es": "informal" },
|
|
60
|
+
"glossary": { "fr": { "Privacy Policy": "Politique de confidentialité" } },
|
|
61
|
+
"termNotes": { "snooze": "postpone a reminder, not sleep" }
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Real output with the default model (`gpt-5.4-mini`), 4 requests, about 6,000 tokens:
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
de Halte deine Pflanzen am Leben, ohne daran denken zu müssen
|
|
69
|
+
Die meisten Zimmerpflanzen brauchen im Winter tendenziell weniger Wasser.
|
|
70
|
+
Erinnere mich morgen
|
|
71
|
+
fr Gardez vos plantes en vie sans y penser
|
|
72
|
+
Bienvenue sur Plantly, {name} !
|
|
73
|
+
Lisez notre <a href="/privacy">Politique de confidentialité</a> pour voir ce que nous stockons.
|
|
74
|
+
es Mantén vivas tus plantas sin tener que pensar en ello
|
|
75
|
+
Hoy necesitan agua {{count}} plantas
|
|
76
|
+
ja 何も考えなくても、植物を元気に保てます
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
German uses "du" and French "vous", as configured. Spanish and French use sentence case, not the English Title Case. French has its space before "!". The hedge "tend to" survived, and so did the placeholders, the link and the brand name. The full example is in [`examples/basic`](examples/basic).
|
|
80
|
+
|
|
81
|
+
## Quick start
|
|
82
|
+
|
|
83
|
+
1. Install, or use `npx`:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install --save-dev localewarden
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
2. Create a config. `init` looks for common locale folder layouts:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx localewarden init
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
3. Edit `localewarden.config.json`: set `targetLanguages`, `files` and `context`.
|
|
96
|
+
|
|
97
|
+
4. Set your API key and do a dry run:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
export OPENAI_API_KEY=sk-...
|
|
101
|
+
npx localewarden --dry-run
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
5. Translate, then commit the locale files **and** the `.localewarden/` folder:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npx localewarden
|
|
108
|
+
git add locales .localewarden
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`.localewarden/` holds the hashes that tell localewarden what changed and what was edited by hand. Commit it so your team and your CI share the same state.
|
|
112
|
+
|
|
113
|
+
## How it decides what to translate
|
|
114
|
+
|
|
115
|
+
For every string and language:
|
|
116
|
+
|
|
117
|
+
| Situation | What happens |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| No translation yet | Translated |
|
|
120
|
+
| Translation exists, localewarden has never seen it (first run) | Kept as is. Your existing translations are adopted, not re-translated. Exception: a copy of the source text is translated. |
|
|
121
|
+
| Source changed since the last translation | Revised: the model gets the old translation and changes only what is needed |
|
|
122
|
+
| Translation differs from what localewarden wrote | Hand edit: kept, protected, listed in `localewarden review` |
|
|
123
|
+
| Hand-edited, and then the source changed | Kept and flagged for review (`source-changed`) |
|
|
124
|
+
| String removed from the source | Removed from the translation |
|
|
125
|
+
| Result fails a hard check | Not written. The string is retried on the next run. |
|
|
126
|
+
|
|
127
|
+
## Quality checks
|
|
128
|
+
|
|
129
|
+
The same checks run in two places. Right after each model answer, a failed hard check means the translation is never written. `localewarden check` runs all of them over your files without any API calls.
|
|
130
|
+
|
|
131
|
+
| Check | Finds | Severity |
|
|
132
|
+
| --- | --- | --- |
|
|
133
|
+
| `placeholder` | `{name}`, `{{count}}`, `%s`, `%1$d`, `%{x}`, `${x}`, `<0></0>` renamed, translated, added or dropped. ICU `plural`/`select` arguments are compared, while plural categories may differ per language. | error |
|
|
134
|
+
| `script` | Letters from an alphabet the language does not use ("刺激" in German), or a word that mixes Latin with Cyrillic/Greek lookalikes ("Вarda") | error |
|
|
135
|
+
| `markup` | Changed link targets, different number of tags, unclosed or misnested tags, dropped list items | warning (broken tags and changed links: never written) |
|
|
136
|
+
| `years` | A year from the source missing or changed (citations, dates) | warning |
|
|
137
|
+
| `formality` | The other form of address than configured, both forms in one string, or masculine-only forms for "you" | warning |
|
|
138
|
+
| `titlecase` | English Title Case copied into a language that uses sentence case | warning |
|
|
139
|
+
| `ampersand` | "&" in languages that write the word | warning |
|
|
140
|
+
| `glossary` | A glossary rendering missing (case endings allowed), or a `doNotTranslate` name translated | warning |
|
|
141
|
+
| `untranslated` | Identical to the source (prose of 3+ words; "OK" and names are fine) | warning |
|
|
142
|
+
| `partial` | Source-language words left inside the translation, an untranslated bold lead-in, or a hedge that became certainty ("tend to" stated as fact) | warning |
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npx localewarden check # counts per language and check
|
|
146
|
+
npx localewarden check -v # with examples
|
|
147
|
+
npx localewarden check --strict # exit 1 on warnings too
|
|
148
|
+
npx localewarden check --json # for scripts
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Approved hand edits are skipped, except for placeholder and script errors, which break the app either way.
|
|
152
|
+
|
|
153
|
+
### In CI
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
# .github/workflows/i18n.yml
|
|
157
|
+
name: i18n
|
|
158
|
+
on: [pull_request]
|
|
159
|
+
jobs:
|
|
160
|
+
check:
|
|
161
|
+
runs-on: ubuntu-latest
|
|
162
|
+
steps:
|
|
163
|
+
- uses: actions/checkout@v4
|
|
164
|
+
- uses: actions/setup-node@v4
|
|
165
|
+
with: { node-version: 22 }
|
|
166
|
+
- run: npx localewarden check
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Fixing what the check finds
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npx localewarden --fix-flagged
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
For each flagged string, the model gets the source, the current translation and the exact findings, with the instruction to change only what is needed. The fix is written only if the same check passes afterwards, nothing else breaks, and few words changed. Rejected fixes are recorded in `.localewarden/repair-failures.json` and not retried until the translation changes.
|
|
176
|
+
|
|
177
|
+
## Hand edits and review
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
npx localewarden review # list pending hand edits
|
|
181
|
+
npx localewarden review --approve de:home.title # correct: keep it protected
|
|
182
|
+
npx localewarden review --approve de:* # all German entries
|
|
183
|
+
npx localewarden review --release de:home.title # hand it back: next run revises it
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`--overwrite-manual` makes a run replace hand edits on purpose.
|
|
187
|
+
|
|
188
|
+
## Configuration
|
|
189
|
+
|
|
190
|
+
`localewarden.config.json`:
|
|
191
|
+
|
|
192
|
+
| Option | Default | Description |
|
|
193
|
+
| --- | --- | --- |
|
|
194
|
+
| `targetLanguages` | (required) | Language codes, e.g. `["de", "fr", "pt-BR", "zh-TW"]` |
|
|
195
|
+
| `files` | (required) | Path pattern with `{lang}`. `*` matches within one folder, `**/` any depth. Examples: `locales/{lang}.json`, `public/locales/{lang}/*.json`, `src/**/i18n/{lang}.json`, `messages.{lang}.json` |
|
|
196
|
+
| `sourceLanguage` | `"en"` | Language of the source files. Some checks (title case, source words left in, hedges) assume English. |
|
|
197
|
+
| `context` | | One or two sentences about your product. This is the most effective way to get the right meaning. |
|
|
198
|
+
| `tone` | | e.g. `"friendly and plain, no marketing hype"` |
|
|
199
|
+
| `doNotTranslate` | `[]` | Brand, product and feature names that must stay as written |
|
|
200
|
+
| `formality` | `{}` | `{"de": "informal", "fr": "formal"}`. Built-in rules for fr, de, es, it, pt, nl, ru, uk, cs, sk, pl, ro, ca, id, ms, hr, sl, tr, el. Other languages get a general instruction. |
|
|
201
|
+
| `genderNeutral` | `true` | Avoid gendered forms when addressing the user |
|
|
202
|
+
| `sentenceCase` | `true` | Use sentence case in languages that do not capitalise titles |
|
|
203
|
+
| `glossary` | `{}` | `{"fr": {"Terms of Service": "Conditions d'utilisation"}}` |
|
|
204
|
+
| `termNotes` | `{}` | Meanings of ambiguous terms, sent only with strings that contain them: `{"snooze": "postpone a reminder"}` |
|
|
205
|
+
| `instructions` | `{}` | Extra instructions per language, `"*"` for all: `{"es": "Use neutral Latin American Spanish."}` |
|
|
206
|
+
| `placeholders` | built-in | Regular expressions (strings) that match your placeholders. Replaces the built-in list. |
|
|
207
|
+
| `model` | `"gpt-5.4-mini"` | Any chat model your endpoint offers |
|
|
208
|
+
| `baseUrl` | `"https://api.openai.com/v1"` | Any OpenAI-compatible endpoint |
|
|
209
|
+
| `apiKeyEnv` | `"OPENAI_API_KEY"` | Environment variable that holds the key |
|
|
210
|
+
| `reasoningEffort` | `"medium"` for reasoning models | `"low"` is cheaper. `"medium"` gave noticeably more natural wording in our tests. |
|
|
211
|
+
| `temperature` | `0.3` for other models | |
|
|
212
|
+
| `maxTokensPerRun` | `500000` | The run stops when the API-reported usage reaches this |
|
|
213
|
+
| `concurrency` | `4` | Languages translated in parallel |
|
|
214
|
+
| `batchSize` | `20` | Strings per request (smaller for scripts that need many tokens) |
|
|
215
|
+
| `stateDir` | `".localewarden"` | Where state and the review list live |
|
|
216
|
+
|
|
217
|
+
### Other providers
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
{ "baseUrl": "https://openrouter.ai/api/v1", "apiKeyEnv": "OPENROUTER_API_KEY", "model": "anthropic/claude-sonnet-4.5" }
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
{ "baseUrl": "http://localhost:11434/v1", "model": "qwen3:14b" }
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Small local models make noticeably more mistakes. The checks catch the mechanical ones, not wrong meaning.
|
|
228
|
+
|
|
229
|
+
## Commands
|
|
230
|
+
|
|
231
|
+
```text
|
|
232
|
+
localewarden [translate] --dry-run --lang de,fr --fix-flagged --retranslate-all
|
|
233
|
+
--overwrite-manual --max-tokens <n> --verbose
|
|
234
|
+
localewarden check --lang de,fr --verbose --limit <n> --strict --json
|
|
235
|
+
localewarden review --all --approve <sel>... --release <sel>...
|
|
236
|
+
localewarden init
|
|
237
|
+
Global: --config <path> --help --version
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Exit codes: `0` ok, `1` errors (or check findings), `2` configuration problem.
|
|
241
|
+
|
|
242
|
+
## Programmatic use
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
import { loadConfig, run, checkProject } from 'localewarden';
|
|
246
|
+
|
|
247
|
+
const config = loadConfig('localewarden.config.json');
|
|
248
|
+
const summary = await run(config, { languages: ['de'] });
|
|
249
|
+
const findings = checkProject(config);
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
`run` also accepts a `model` with a `complete(system, user)` method, so you can plug in any SDK.
|
|
253
|
+
|
|
254
|
+
## Costs and privacy
|
|
255
|
+
|
|
256
|
+
- You pay your API provider. Run `--dry-run` first: it prints the number of strings and a rough token estimate. Reasoning models also bill reasoning tokens, which can triple the cost.
|
|
257
|
+
- Unchanged strings cost nothing. In the example above, changing two English strings and updating four languages took 4 requests and about 4,000 tokens.
|
|
258
|
+
- Strings, keys, your `context` and glossary are sent to the API you configure. Nothing else is sent anywhere. There is no telemetry.
|
|
259
|
+
|
|
260
|
+
## Limitations
|
|
261
|
+
|
|
262
|
+
- JSON only (nested objects, arrays, flat keys). YAML, PO, XLIFF and ARB are not supported yet.
|
|
263
|
+
- The checks catch mechanical problems, not every wrong meaning. Have a native speaker look at important screens, then approve their edits with `review`.
|
|
264
|
+
- Rules for form of address, gender and typography exist for the languages listed above. Other languages are translated with the general rules.
|
|
265
|
+
- A run that is interrupted keeps everything written so far. Unwritten strings are picked up on the next run.
|
|
266
|
+
|
|
267
|
+
## License
|
|
268
|
+
|
|
269
|
+
[MIT](LICENSE)
|
package/dist/checks.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Config } from './config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic quality checks. No API calls, so they can run in CI on every commit.
|
|
4
|
+
*
|
|
5
|
+
* placeholder placeholder set differs from the source error
|
|
6
|
+
* script letters from a script the language does not use, or a word
|
|
7
|
+
* mixing Latin with Cyrillic/Greek lookalikes error
|
|
8
|
+
* markup links, tags or list items differ from the source; broken tags
|
|
9
|
+
* years a year from the source is missing or changed (citations, dates)
|
|
10
|
+
* formality the other form of address than configured, or both mixed;
|
|
11
|
+
* masculine-only forms for "you" when genderNeutral is on
|
|
12
|
+
* titlecase English Title Case copied into a sentence-case language
|
|
13
|
+
* ampersand "&" in a language that writes the word
|
|
14
|
+
* glossary a glossary rendering or a doNotTranslate name is missing
|
|
15
|
+
* untranslated identical to the source (prose of 3+ words)
|
|
16
|
+
* partial source-language words left inside an otherwise translated string,
|
|
17
|
+
* or a dropped hedge ("tend to" stated as certain)
|
|
18
|
+
*/
|
|
19
|
+
export type CheckName = 'placeholder' | 'script' | 'markup' | 'years' | 'formality' | 'titlecase' | 'ampersand' | 'glossary' | 'untranslated' | 'partial';
|
|
20
|
+
export declare const CHECKS: CheckName[];
|
|
21
|
+
export declare const ERROR_CHECKS: Set<CheckName>;
|
|
22
|
+
/** Checks a targeted repair (--fix-flagged) may try to fix. */
|
|
23
|
+
export declare const FIXABLE_CHECKS: Set<CheckName>;
|
|
24
|
+
export interface Issue {
|
|
25
|
+
check: CheckName;
|
|
26
|
+
note?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare class Checker {
|
|
29
|
+
readonly config: Config;
|
|
30
|
+
readonly placeholderRe: RegExp;
|
|
31
|
+
constructor(config: Config);
|
|
32
|
+
get englishSource(): boolean;
|
|
33
|
+
placeholdersMatch(key: string, source: string, text: string): boolean;
|
|
34
|
+
placeholderNote(source: string, text: string): string;
|
|
35
|
+
/** All issues of one translated string. */
|
|
36
|
+
checkString(lang: string, key: string, source: string, text: string): Issue[];
|
|
37
|
+
/**
|
|
38
|
+
* Output check right after the API call.
|
|
39
|
+
* hard: certain corruption (placeholders, foreign alphabet, changed link targets, broken
|
|
40
|
+
* markup, source echoed back). Retried once; if still present, never written.
|
|
41
|
+
* soft: likely loss (tag count, missing year, source words left in). Retried once, then
|
|
42
|
+
* accepted; `localewarden check` keeps reporting it.
|
|
43
|
+
*/
|
|
44
|
+
defect(lang: string, key: string, source: string, text: string): {
|
|
45
|
+
hard: string | null;
|
|
46
|
+
soft: string | null;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Non-Latin scripts each language is written in. Latin is always allowed (names, codes). */
|
|
50
|
+
export declare const NATIVE_SCRIPTS: Record<string, string[]>;
|
|
51
|
+
/** Why `text` contains letters that cannot belong to `lang`, or null. */
|
|
52
|
+
export declare function foreignScript(lang: string, text: string): string | null;
|
|
53
|
+
export declare const hrefSignature: (text: string) => string;
|
|
54
|
+
/** A malformed, unclosed or misnested tag, or null. */
|
|
55
|
+
export declare function brokenMarkup(text: string): string | null;
|
|
56
|
+
/** Fewer "•" list items than the source: items dropped at the end of a long text. */
|
|
57
|
+
export declare function droppedBullets(source: string, text: string): string | null;
|
|
58
|
+
export declare function yearDifference(source: string, text: string): string | null;
|
|
59
|
+
/**
|
|
60
|
+
* Whether `translation` contains `target`, allowing case endings: every target word of 3+
|
|
61
|
+
* letters must appear in order as the start of a translation word, minus up to two final
|
|
62
|
+
* letters ("Polityka prywatności" matches "Polityką prywatności").
|
|
63
|
+
*/
|
|
64
|
+
export declare function containsInflected(lang: string, translation: string, target: string): boolean;
|
|
65
|
+
/** Glossary terms in `source` whose required rendering is missing from `translation`. */
|
|
66
|
+
export declare function missingGlossaryTerms(config: Config, lang: string, source: string, translation: string): string[];
|
|
67
|
+
/** A doNotTranslate name used in the source but missing (or translated) in the translation. */
|
|
68
|
+
export declare function keptNameMissing(name: string, source: string, text: string): boolean;
|
|
69
|
+
/** `text` equals `source` and the source is prose of 3+ words (not a name, code or "OK"). */
|
|
70
|
+
export declare function isUnchangedProse(source: string, text: string, placeholderRe: RegExp): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* First run of six ordinary source words (at most one capitalised, so titles of works stay
|
|
73
|
+
* allowed) copied into the translation, or null.
|
|
74
|
+
*/
|
|
75
|
+
export declare function sourceRun(source: string, text: string): string | null;
|