@_linked/translation 0.2.2
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 +102 -0
- package/lib/cjs/data/translation.json +8 -0
- package/lib/esm/archive.d.ts +133 -0
- package/lib/esm/archive.js +567 -0
- package/lib/esm/archive.js.map +1 -0
- package/lib/esm/authorization.d.ts +12 -0
- package/lib/esm/authorization.js +10 -0
- package/lib/esm/authorization.js.map +1 -0
- package/lib/esm/backend.d.ts +1 -0
- package/lib/esm/backend.js +3 -0
- package/lib/esm/backend.js.map +1 -0
- package/lib/esm/cdn-loader.d.ts +40 -0
- package/lib/esm/cdn-loader.js +200 -0
- package/lib/esm/cdn-loader.js.map +1 -0
- package/lib/esm/checks.d.ts +56 -0
- package/lib/esm/checks.js +288 -0
- package/lib/esm/checks.js.map +1 -0
- package/lib/esm/compile.d.ts +64 -0
- package/lib/esm/compile.js +99 -0
- package/lib/esm/compile.js.map +1 -0
- package/lib/esm/core/messages.d.ts +25 -0
- package/lib/esm/core/messages.js +76 -0
- package/lib/esm/core/messages.js.map +1 -0
- package/lib/esm/data/translation.json +8 -0
- package/lib/esm/discovery.d.ts +97 -0
- package/lib/esm/discovery.js +365 -0
- package/lib/esm/discovery.js.map +1 -0
- package/lib/esm/exchange.d.ts +100 -0
- package/lib/esm/exchange.js +223 -0
- package/lib/esm/exchange.js.map +1 -0
- package/lib/esm/export.d.ts +28 -0
- package/lib/esm/export.js +92 -0
- package/lib/esm/export.js.map +1 -0
- package/lib/esm/formats/csv.d.ts +8 -0
- package/lib/esm/formats/csv.js +397 -0
- package/lib/esm/formats/csv.js.map +1 -0
- package/lib/esm/formats/json.d.ts +30 -0
- package/lib/esm/formats/json.js +459 -0
- package/lib/esm/formats/json.js.map +1 -0
- package/lib/esm/formats/xliff.d.ts +12 -0
- package/lib/esm/formats/xliff.js +577 -0
- package/lib/esm/formats/xliff.js.map +1 -0
- package/lib/esm/import.d.ts +197 -0
- package/lib/esm/import.js +391 -0
- package/lib/esm/import.js.map +1 -0
- package/lib/esm/index.d.ts +42 -0
- package/lib/esm/index.js +34 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/esm/key-sync-node.d.ts +29 -0
- package/lib/esm/key-sync-node.js +174 -0
- package/lib/esm/key-sync-node.js.map +1 -0
- package/lib/esm/key-sync.d.ts +46 -0
- package/lib/esm/key-sync.js +114 -0
- package/lib/esm/key-sync.js.map +1 -0
- package/lib/esm/key-version.d.ts +50 -0
- package/lib/esm/key-version.js +134 -0
- package/lib/esm/key-version.js.map +1 -0
- package/lib/esm/languages.d.ts +18 -0
- package/lib/esm/languages.js +74 -0
- package/lib/esm/languages.js.map +1 -0
- package/lib/esm/linked-discovery.d.ts +44 -0
- package/lib/esm/linked-discovery.js +267 -0
- package/lib/esm/linked-discovery.js.map +1 -0
- package/lib/esm/message-format.d.ts +41 -0
- package/lib/esm/message-format.js +133 -0
- package/lib/esm/message-format.js.map +1 -0
- package/lib/esm/mt.d.ts +111 -0
- package/lib/esm/mt.js +142 -0
- package/lib/esm/mt.js.map +1 -0
- package/lib/esm/ontologies/translation.d.ts +203 -0
- package/lib/esm/ontologies/translation.js +219 -0
- package/lib/esm/ontologies/translation.js.map +1 -0
- package/lib/esm/package.d.ts +4 -0
- package/lib/esm/package.js +11 -0
- package/lib/esm/package.js.map +1 -0
- package/lib/esm/react.d.ts +55 -0
- package/lib/esm/react.js +127 -0
- package/lib/esm/react.js.map +1 -0
- package/lib/esm/records.d.ts +151 -0
- package/lib/esm/records.js +2 -0
- package/lib/esm/records.js.map +1 -0
- package/lib/esm/release.d.ts +198 -0
- package/lib/esm/release.js +435 -0
- package/lib/esm/release.js.map +1 -0
- package/lib/esm/shape-catalog.d.ts +79 -0
- package/lib/esm/shape-catalog.js +118 -0
- package/lib/esm/shape-catalog.js.map +1 -0
- package/lib/esm/shapes/GlossaryTerm.d.ts +28 -0
- package/lib/esm/shapes/GlossaryTerm.js +94 -0
- package/lib/esm/shapes/GlossaryTerm.js.map +1 -0
- package/lib/esm/shapes/TranslationInventoryOrigin.d.ts +33 -0
- package/lib/esm/shapes/TranslationInventoryOrigin.js +209 -0
- package/lib/esm/shapes/TranslationInventoryOrigin.js.map +1 -0
- package/lib/esm/shapes/TranslationKey.d.ts +92 -0
- package/lib/esm/shapes/TranslationKey.js +194 -0
- package/lib/esm/shapes/TranslationKey.js.map +1 -0
- package/lib/esm/shapes/TranslationKeyVersion.d.ts +19 -0
- package/lib/esm/shapes/TranslationKeyVersion.js +125 -0
- package/lib/esm/shapes/TranslationKeyVersion.js.map +1 -0
- package/lib/esm/shapes/TranslationLanguage.d.ts +15 -0
- package/lib/esm/shapes/TranslationLanguage.js +102 -0
- package/lib/esm/shapes/TranslationLanguage.js.map +1 -0
- package/lib/esm/shapes/TranslationProvider.d.ts +409 -0
- package/lib/esm/shapes/TranslationProvider.js +1376 -0
- package/lib/esm/shapes/TranslationProvider.js.map +1 -0
- package/lib/esm/shapes/TranslationRelease.d.ts +36 -0
- package/lib/esm/shapes/TranslationRelease.js +149 -0
- package/lib/esm/shapes/TranslationRelease.js.map +1 -0
- package/lib/esm/shapes/TranslationRevision.d.ts +39 -0
- package/lib/esm/shapes/TranslationRevision.js +159 -0
- package/lib/esm/shapes/TranslationRevision.js.map +1 -0
- package/lib/esm/shapes/TranslationUnit.d.ts +26 -0
- package/lib/esm/shapes/TranslationUnit.js +111 -0
- package/lib/esm/shapes/TranslationUnit.js.map +1 -0
- package/package.json +134 -0
- package/src/archive.ts +894 -0
- package/src/authorization.ts +33 -0
- package/src/backend.ts +2 -0
- package/src/cdn-loader.ts +264 -0
- package/src/checks.ts +413 -0
- package/src/compile.ts +168 -0
- package/src/core/messages.ts +106 -0
- package/src/data/translation.json +8 -0
- package/src/discovery.ts +594 -0
- package/src/exchange.ts +428 -0
- package/src/export.ts +135 -0
- package/src/formats/csv.ts +459 -0
- package/src/formats/json.ts +587 -0
- package/src/formats/xliff.ts +708 -0
- package/src/import.ts +648 -0
- package/src/index.ts +207 -0
- package/src/key-sync-node.ts +269 -0
- package/src/key-sync.ts +168 -0
- package/src/key-version.ts +206 -0
- package/src/languages.ts +102 -0
- package/src/linked-discovery.ts +385 -0
- package/src/message-format.ts +214 -0
- package/src/mt.ts +248 -0
- package/src/ontologies/translation.ts +234 -0
- package/src/package.ts +19 -0
- package/src/react.tsx +250 -0
- package/src/records.ts +180 -0
- package/src/release.ts +726 -0
- package/src/shape-catalog.ts +198 -0
- package/src/shapes/GlossaryTerm.ts +61 -0
- package/src/shapes/TranslationInventoryOrigin.ts +131 -0
- package/src/shapes/TranslationKey.ts +174 -0
- package/src/shapes/TranslationKeyVersion.ts +77 -0
- package/src/shapes/TranslationLanguage.ts +51 -0
- package/src/shapes/TranslationProvider.ts +1755 -0
- package/src/shapes/TranslationRelease.ts +114 -0
- package/src/shapes/TranslationRevision.ts +105 -0
- package/src/shapes/TranslationUnit.ts +72 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type TranslationLanguageDefinition } from './languages.js';
|
|
2
|
+
/**
|
|
3
|
+
* `loadMessages` factory for the CDN delivery cascade (Plan 016 P2.4, AD-J/AD-K).
|
|
4
|
+
* SIMPLE-POINTER mode (the ratified default): fetch `${base}/{lang}.json` from
|
|
5
|
+
* the published CDN — changing `base` (an app's `tr:cdnTarget`) repoints at
|
|
6
|
+
* different cloud files with no call-site change, exactly the Tolgee model.
|
|
7
|
+
* `bundledPath` (same-origin bundled copy) and `devApi` (e.g. the `getMessages`
|
|
8
|
+
* RPC for live dev editing) are optional fallbacks, tried in the order given.
|
|
9
|
+
* Any source that yields a non-empty map wins; all-empty ⇒ `{}` and the client
|
|
10
|
+
* keeps its inline English defaults. Never throws.
|
|
11
|
+
*
|
|
12
|
+
* Portable (no CN import): the caller wires `base`/`devApi` for its environment.
|
|
13
|
+
*/
|
|
14
|
+
import type { TranslationMessages } from './core/messages.js';
|
|
15
|
+
import { type TranslationBuildDescriptor } from './release.js';
|
|
16
|
+
export interface CdnLoaderConfig {
|
|
17
|
+
/** Published CDN base — `${base}/{lang}.json`. The repoint knob. Omit to skip. */
|
|
18
|
+
base?: string;
|
|
19
|
+
/** Same-origin bundled base — `${bundledPath}/{lang}.json`. Omit to skip. */
|
|
20
|
+
bundledPath?: string;
|
|
21
|
+
/** Last-resort loader (e.g. a live backend RPC). Omit to skip. */
|
|
22
|
+
devApi?: (language: string) => Promise<TranslationMessages>;
|
|
23
|
+
/** Injectable for tests / non-DOM runtimes. */
|
|
24
|
+
fetchImpl?: typeof fetch;
|
|
25
|
+
}
|
|
26
|
+
export declare function createCdnLoader(config: CdnLoaderConfig): (language: string) => Promise<TranslationMessages>;
|
|
27
|
+
export interface ReleaseLoaderConfig {
|
|
28
|
+
bundledPath: string;
|
|
29
|
+
descriptor: TranslationBuildDescriptor | (() => Promise<TranslationBuildDescriptor | null>);
|
|
30
|
+
fetchImpl?: typeof fetch;
|
|
31
|
+
devApi?: (language: string) => Promise<TranslationMessages>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Schema-v2 release loader. Bundled messages always win the first render;
|
|
35
|
+
* compatible immutable CDN objects fetched in the background become visible
|
|
36
|
+
* only on the next call for that language.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createReleaseLoader(config: ReleaseLoaderConfig): (language: string) => Promise<TranslationMessages>;
|
|
39
|
+
/** Fetch public language resources independently of a compiled application. */
|
|
40
|
+
export declare function createCdnLanguageLoader(config: Pick<CdnLoaderConfig, 'base' | 'fetchImpl'>): () => Promise<TranslationLanguageDefinition[]>;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { defineLanguage, validateLanguageDefinitions, } from './languages.js';
|
|
2
|
+
import { sha256Hex } from './key-version.js';
|
|
3
|
+
import { applyCatalogPatches, } from './release.js';
|
|
4
|
+
function isNonEmptyMap(v) {
|
|
5
|
+
return !!v && typeof v === 'object' && Object.keys(v).length > 0;
|
|
6
|
+
}
|
|
7
|
+
export function createCdnLoader(config) {
|
|
8
|
+
var _a, _b, _c;
|
|
9
|
+
const doFetch = (_a = config.fetchImpl) !== null && _a !== void 0 ? _a : (typeof fetch !== 'undefined' ? fetch : undefined);
|
|
10
|
+
const base = (_b = config.base) === null || _b === void 0 ? void 0 : _b.replace(/\/+$/, '');
|
|
11
|
+
const bundled = (_c = config.bundledPath) === null || _c === void 0 ? void 0 : _c.replace(/\/+$/, '');
|
|
12
|
+
const fetchJson = async (url) => {
|
|
13
|
+
if (!doFetch)
|
|
14
|
+
return null;
|
|
15
|
+
try {
|
|
16
|
+
const res = await doFetch(url);
|
|
17
|
+
if (!res.ok)
|
|
18
|
+
return null;
|
|
19
|
+
return (await res.json());
|
|
20
|
+
}
|
|
21
|
+
catch (_a) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
return async (language) => {
|
|
26
|
+
const sources = [];
|
|
27
|
+
if (base)
|
|
28
|
+
sources.push(() => fetchJson(`${base}/${language}.json`));
|
|
29
|
+
if (bundled)
|
|
30
|
+
sources.push(() => fetchJson(`${bundled}/${language}.json`));
|
|
31
|
+
if (config.devApi)
|
|
32
|
+
sources.push(async () => {
|
|
33
|
+
try {
|
|
34
|
+
return await config.devApi(language);
|
|
35
|
+
}
|
|
36
|
+
catch (_a) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
for (const source of sources) {
|
|
41
|
+
const result = await source();
|
|
42
|
+
if (isNonEmptyMap(result))
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
return {};
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function resolveReleaseUrl(url, manifestUrl) {
|
|
49
|
+
try {
|
|
50
|
+
return new URL(url, manifestUrl).toString();
|
|
51
|
+
}
|
|
52
|
+
catch (_a) {
|
|
53
|
+
return url;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function isReleaseManifest(value) {
|
|
57
|
+
const manifest = value;
|
|
58
|
+
return ((manifest === null || manifest === void 0 ? void 0 : manifest.schemaVersion) === 2 &&
|
|
59
|
+
typeof manifest.releaseId === 'string' &&
|
|
60
|
+
typeof manifest.contractSetHash === 'string' &&
|
|
61
|
+
!!manifest.keyVersions &&
|
|
62
|
+
typeof manifest.keyVersions === 'object' &&
|
|
63
|
+
!!manifest.languages &&
|
|
64
|
+
typeof manifest.languages === 'object');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Schema-v2 release loader. Bundled messages always win the first render;
|
|
68
|
+
* compatible immutable CDN objects fetched in the background become visible
|
|
69
|
+
* only on the next call for that language.
|
|
70
|
+
*/
|
|
71
|
+
export function createReleaseLoader(config) {
|
|
72
|
+
var _a;
|
|
73
|
+
const doFetch = (_a = config.fetchImpl) !== null && _a !== void 0 ? _a : (typeof fetch !== 'undefined' ? fetch : undefined);
|
|
74
|
+
const bundled = config.bundledPath.replace(/\/+$/, '');
|
|
75
|
+
const objectCache = new Map();
|
|
76
|
+
const pending = new Map();
|
|
77
|
+
const requested = new Set();
|
|
78
|
+
let descriptorPromise;
|
|
79
|
+
const getDescriptor = () => (descriptorPromise !== null && descriptorPromise !== void 0 ? descriptorPromise : (descriptorPromise = typeof config.descriptor === 'function'
|
|
80
|
+
? config.descriptor()
|
|
81
|
+
: Promise.resolve(config.descriptor)));
|
|
82
|
+
const fetchText = async (url) => {
|
|
83
|
+
if (!doFetch)
|
|
84
|
+
return null;
|
|
85
|
+
try {
|
|
86
|
+
const response = await doFetch(url);
|
|
87
|
+
return response.ok ? await response.text() : null;
|
|
88
|
+
}
|
|
89
|
+
catch (_a) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const fetchMessages = async (url) => {
|
|
94
|
+
const text = await fetchText(url);
|
|
95
|
+
if (text === null)
|
|
96
|
+
return null;
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(text);
|
|
99
|
+
return isNonEmptyMap(parsed) ? parsed : null;
|
|
100
|
+
}
|
|
101
|
+
catch (_a) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const fetchHashed = async (url, hash) => {
|
|
106
|
+
if (objectCache.has(hash))
|
|
107
|
+
return objectCache.get(hash);
|
|
108
|
+
const text = await fetchText(url);
|
|
109
|
+
if (text === null || (await sha256Hex(text)) !== hash)
|
|
110
|
+
return null;
|
|
111
|
+
try {
|
|
112
|
+
const parsed = JSON.parse(text);
|
|
113
|
+
objectCache.set(hash, parsed);
|
|
114
|
+
return parsed;
|
|
115
|
+
}
|
|
116
|
+
catch (_a) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const fetchReleaseLanguage = async (language) => {
|
|
121
|
+
const descriptor = await getDescriptor();
|
|
122
|
+
if (!descriptor)
|
|
123
|
+
return null;
|
|
124
|
+
const manifestText = await fetchText(descriptor.manifestUrl);
|
|
125
|
+
if (manifestText === null)
|
|
126
|
+
return null;
|
|
127
|
+
let manifest;
|
|
128
|
+
try {
|
|
129
|
+
manifest = JSON.parse(manifestText);
|
|
130
|
+
}
|
|
131
|
+
catch (_a) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
if (!isReleaseManifest(manifest) ||
|
|
135
|
+
manifest.releaseId !== descriptor.releaseId ||
|
|
136
|
+
manifest.contractSetHash !== descriptor.contractSetHash) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const lane = manifest.languages[language];
|
|
140
|
+
if (!lane)
|
|
141
|
+
return null;
|
|
142
|
+
const base = await fetchHashed(resolveReleaseUrl(lane.base.url, descriptor.manifestUrl), lane.base.hash);
|
|
143
|
+
if (!base)
|
|
144
|
+
return null;
|
|
145
|
+
const patches = [];
|
|
146
|
+
for (const patch of [...lane.patches].sort((left, right) => left.sequence - right.sequence)) {
|
|
147
|
+
const body = await fetchHashed(resolveReleaseUrl(patch.url, descriptor.manifestUrl), patch.hash);
|
|
148
|
+
if (!body)
|
|
149
|
+
return null;
|
|
150
|
+
patches.push(body);
|
|
151
|
+
}
|
|
152
|
+
return applyCatalogPatches(base, patches);
|
|
153
|
+
};
|
|
154
|
+
return async (language) => {
|
|
155
|
+
var _a;
|
|
156
|
+
const bundledMessages = await fetchMessages(`${bundled}/${language}.json`);
|
|
157
|
+
const existing = pending.get(language);
|
|
158
|
+
if (requested.has(language) && existing) {
|
|
159
|
+
const released = await existing;
|
|
160
|
+
return (_a = released !== null && released !== void 0 ? released : bundledMessages) !== null && _a !== void 0 ? _a : {};
|
|
161
|
+
}
|
|
162
|
+
requested.add(language);
|
|
163
|
+
const background = fetchReleaseLanguage(language);
|
|
164
|
+
pending.set(language, background);
|
|
165
|
+
if (bundledMessages) {
|
|
166
|
+
void background.catch(() => null);
|
|
167
|
+
return bundledMessages;
|
|
168
|
+
}
|
|
169
|
+
const released = await background;
|
|
170
|
+
if (released)
|
|
171
|
+
return released;
|
|
172
|
+
if (config.devApi) {
|
|
173
|
+
try {
|
|
174
|
+
return await config.devApi(language);
|
|
175
|
+
}
|
|
176
|
+
catch (_b) {
|
|
177
|
+
// Preserve the empty fallback contract.
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return {};
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/** Fetch public language resources independently of a compiled application. */
|
|
184
|
+
export function createCdnLanguageLoader(config) {
|
|
185
|
+
return async () => {
|
|
186
|
+
var _a;
|
|
187
|
+
if (!config.base)
|
|
188
|
+
throw new Error('A translation CDN base is required.');
|
|
189
|
+
const response = await ((_a = config.fetchImpl) !== null && _a !== void 0 ? _a : fetch)(`${config.base.replace(/\/+$/, '')}/languages.json`);
|
|
190
|
+
if (!response.ok)
|
|
191
|
+
throw new Error(`Language catalog HTTP ${response.status}`);
|
|
192
|
+
const catalog = await response.json();
|
|
193
|
+
if (catalog.schemaVersion !== 1 || !Array.isArray(catalog.languages))
|
|
194
|
+
throw new Error('Invalid language catalog.');
|
|
195
|
+
const definitions = catalog.languages.map((item) => defineLanguage(item.code, item));
|
|
196
|
+
validateLanguageDefinitions(definitions);
|
|
197
|
+
return definitions;
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=cdn-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdn-loader.js","sourceRoot":"","sources":["../../src/cdn-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translation quality CHECKS engine (Plan 017 B2-1). One pure function turns
|
|
3
|
+
* entries + the termbase into findings; the same engine runs live in the
|
|
4
|
+
* Studio (inline badges + Checks tab), post-MT (reject bad drafts before they
|
|
5
|
+
* are saved), and at import. Enforcement philosophy (ratified 2026-07-23):
|
|
6
|
+
* warn everywhere, gate only at publish — findings never block a save.
|
|
7
|
+
*/
|
|
8
|
+
import type { GlossaryTermRecord, TranslationEntryRecord } from './records.js';
|
|
9
|
+
import { type ConsistencyEntry } from './mt.js';
|
|
10
|
+
export type CheckSeverity = 'error' | 'warning';
|
|
11
|
+
export interface CheckFinding {
|
|
12
|
+
key: string;
|
|
13
|
+
language: string;
|
|
14
|
+
/** placeholder-integrity | term-keep | term-prefer | term-required | term-forbid | source-divergence */
|
|
15
|
+
rule: string;
|
|
16
|
+
severity: CheckSeverity;
|
|
17
|
+
message: string;
|
|
18
|
+
/** A concrete replacement text or wording hint, when one exists. */
|
|
19
|
+
suggestion?: string;
|
|
20
|
+
}
|
|
21
|
+
export type TermHarvestReason = 'repeated' | 'capitalized' | 'untranslated';
|
|
22
|
+
export interface TermHarvestCandidate {
|
|
23
|
+
term: string;
|
|
24
|
+
occurrences: number;
|
|
25
|
+
keys: string[];
|
|
26
|
+
languages: string[];
|
|
27
|
+
reasons: TermHarvestReason[];
|
|
28
|
+
suggestedType: 'prefer' | 'keep';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Mine review candidates from source catalogs. This is intentionally
|
|
32
|
+
* conservative and pure: it suggests repeated terms, likely proper names,
|
|
33
|
+
* and source words that survive unchanged in translations. A human still
|
|
34
|
+
* chooses the rule and saves the term.
|
|
35
|
+
*/
|
|
36
|
+
export declare function harvestTermCandidates(entries: TranslationEntryRecord[], languages: string[], termbase?: GlossaryTermRecord[]): TermHarvestCandidate[];
|
|
37
|
+
/**
|
|
38
|
+
* Whole-word-ish containment test. Word boundaries only apply when the term
|
|
39
|
+
* starts/ends with word characters (so "e.V." or "#tag" still match), and
|
|
40
|
+
* matching is Unicode-aware.
|
|
41
|
+
*/
|
|
42
|
+
export declare function containsTerm(text: string, term: string, caseSensitive?: boolean, locale?: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Termbase violations for ONE translated text. Exported separately so the
|
|
45
|
+
* post-MT guard can reject a draft with the same rules the Studio shows.
|
|
46
|
+
*/
|
|
47
|
+
export declare function termbaseViolations(sourceText: string, translatedText: string, termbase: GlossaryTermRecord[], language: string): Array<Pick<CheckFinding, 'rule' | 'severity' | 'message' | 'suggestion'>>;
|
|
48
|
+
/** True when a draft breaks an ERROR-severity termbase rule (post-MT gate). */
|
|
49
|
+
export declare function violatesTermbase(sourceText: string, translatedText: string, termbase: GlossaryTermRecord[], language: string): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Run every check for one language across the app's entries. Pure and
|
|
52
|
+
* client-safe — the Studio computes findings locally from data it already has.
|
|
53
|
+
*/
|
|
54
|
+
export declare function runChecks(entries: ConsistencyEntry[], language: string, termbase?: GlossaryTermRecord[]): CheckFinding[];
|
|
55
|
+
/** Findings grouped per key for inline cell badges. */
|
|
56
|
+
export declare function findingsByKey(findings: CheckFinding[]): Map<string, CheckFinding[]>;
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { findInconsistentTranslations } from './mt.js';
|
|
2
|
+
import { compareMessageArguments, detectLegacyPluralSuffixes, } from './message-format.js';
|
|
3
|
+
const HARVEST_STOP_WORDS = new Set([
|
|
4
|
+
'and',
|
|
5
|
+
'are',
|
|
6
|
+
'but',
|
|
7
|
+
'for',
|
|
8
|
+
'from',
|
|
9
|
+
'has',
|
|
10
|
+
'have',
|
|
11
|
+
'into',
|
|
12
|
+
'not',
|
|
13
|
+
'that',
|
|
14
|
+
'the',
|
|
15
|
+
'their',
|
|
16
|
+
'this',
|
|
17
|
+
'was',
|
|
18
|
+
'were',
|
|
19
|
+
'will',
|
|
20
|
+
'with',
|
|
21
|
+
'you',
|
|
22
|
+
'your',
|
|
23
|
+
]);
|
|
24
|
+
function harvestTokens(text) {
|
|
25
|
+
const matches = [...text.matchAll(/[\p{L}\p{N}][\p{L}\p{M}\p{N}'’.-]*/gu)];
|
|
26
|
+
return matches.flatMap((match, index) => {
|
|
27
|
+
const value = match[0].replace(/[.'’-]+$/u, '');
|
|
28
|
+
const normalized = value.toLocaleLowerCase();
|
|
29
|
+
if (value.length < 3 ||
|
|
30
|
+
HARVEST_STOP_WORDS.has(normalized) ||
|
|
31
|
+
/^\d+$/u.test(value)) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const first = value[0];
|
|
35
|
+
const capitalized = (index > 0 &&
|
|
36
|
+
first === first.toLocaleUpperCase() &&
|
|
37
|
+
first !== first.toLocaleLowerCase()) ||
|
|
38
|
+
/^\p{Lu}{2,}[\p{Lu}\p{N}-]*$/u.test(value);
|
|
39
|
+
return [{ value, normalized, capitalized }];
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Mine review candidates from source catalogs. This is intentionally
|
|
44
|
+
* conservative and pure: it suggests repeated terms, likely proper names,
|
|
45
|
+
* and source words that survive unchanged in translations. A human still
|
|
46
|
+
* chooses the rule and saves the term.
|
|
47
|
+
*/
|
|
48
|
+
export function harvestTermCandidates(entries, languages, termbase = []) {
|
|
49
|
+
var _a, _b;
|
|
50
|
+
const existing = new Set(termbase.map((entry) => entry.term.toLocaleLowerCase()));
|
|
51
|
+
const aggregate = new Map();
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
const tokens = harvestTokens(entry.sourceText);
|
|
54
|
+
for (const token of tokens) {
|
|
55
|
+
const value = (_a = aggregate.get(token.normalized)) !== null && _a !== void 0 ? _a : {
|
|
56
|
+
display: token.value,
|
|
57
|
+
occurrences: 0,
|
|
58
|
+
keys: new Set(),
|
|
59
|
+
capitalizedKeys: new Set(),
|
|
60
|
+
untranslatedLanguages: new Set(),
|
|
61
|
+
};
|
|
62
|
+
value.occurrences++;
|
|
63
|
+
value.keys.add(entry.key);
|
|
64
|
+
if (token.capitalized) {
|
|
65
|
+
value.display = token.value;
|
|
66
|
+
value.capitalizedKeys.add(entry.key);
|
|
67
|
+
}
|
|
68
|
+
for (const language of languages) {
|
|
69
|
+
const translated = (_b = entry.units[language]) === null || _b === void 0 ? void 0 : _b.text;
|
|
70
|
+
if (translated && containsTerm(translated, token.value, true)) {
|
|
71
|
+
value.untranslatedLanguages.add(language);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
aggregate.set(token.normalized, value);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return [...aggregate.entries()]
|
|
78
|
+
.flatMap(([normalized, value]) => {
|
|
79
|
+
if (existing.has(normalized))
|
|
80
|
+
return [];
|
|
81
|
+
const repeated = value.keys.size >= 3;
|
|
82
|
+
const capitalized = value.capitalizedKeys.size >= 2;
|
|
83
|
+
const untranslated = value.untranslatedLanguages.size > 0;
|
|
84
|
+
if (!repeated && !capitalized && !untranslated)
|
|
85
|
+
return [];
|
|
86
|
+
const reasons = [];
|
|
87
|
+
if (repeated)
|
|
88
|
+
reasons.push('repeated');
|
|
89
|
+
if (capitalized)
|
|
90
|
+
reasons.push('capitalized');
|
|
91
|
+
if (untranslated)
|
|
92
|
+
reasons.push('untranslated');
|
|
93
|
+
return [
|
|
94
|
+
{
|
|
95
|
+
term: value.display,
|
|
96
|
+
occurrences: value.occurrences,
|
|
97
|
+
keys: [...value.keys].sort(),
|
|
98
|
+
languages: [...value.untranslatedLanguages].sort(),
|
|
99
|
+
reasons,
|
|
100
|
+
suggestedType: capitalized || untranslated ? 'keep' : 'prefer',
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
})
|
|
104
|
+
.sort((left, right) => right.keys.length - left.keys.length ||
|
|
105
|
+
right.reasons.length - left.reasons.length ||
|
|
106
|
+
left.term.localeCompare(right.term));
|
|
107
|
+
}
|
|
108
|
+
/** Escape a literal for embedding in a RegExp. */
|
|
109
|
+
const escapeRe = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
110
|
+
/**
|
|
111
|
+
* Whole-word-ish containment test. Word boundaries only apply when the term
|
|
112
|
+
* starts/ends with word characters (so "e.V." or "#tag" still match), and
|
|
113
|
+
* matching is Unicode-aware.
|
|
114
|
+
*/
|
|
115
|
+
export function containsTerm(text, term, caseSensitive = false, locale) {
|
|
116
|
+
// Unicode regex folding alone does not handle Turkish dotted/dotless I.
|
|
117
|
+
// Only target text uses its locale; an English source still uses English
|
|
118
|
+
// matching even when the translation is Turkish.
|
|
119
|
+
if (!caseSensitive && locale) {
|
|
120
|
+
try {
|
|
121
|
+
text = text.toLocaleLowerCase(locale);
|
|
122
|
+
term = term.toLocaleLowerCase(locale);
|
|
123
|
+
}
|
|
124
|
+
catch (_a) {
|
|
125
|
+
// Private/custom language tags can be valid application identifiers
|
|
126
|
+
// without being supported by the host's locale implementation.
|
|
127
|
+
text = text.toLowerCase();
|
|
128
|
+
term = term.toLowerCase();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Chinese and Japanese phrases routinely touch other letters without spaces.
|
|
132
|
+
// Requiring whitespace-like boundaries would reject valid terms such as 合一.
|
|
133
|
+
const unspaced = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u.test(term);
|
|
134
|
+
const boundedStart = !unspaced && /^[\p{L}\p{N}]/u.test(term) ? '(?<![\\p{L}\\p{N}])' : '';
|
|
135
|
+
const boundedEnd = !unspaced && /[\p{L}\p{N}]$/u.test(term) ? '(?![\\p{L}\\p{N}])' : '';
|
|
136
|
+
const re = new RegExp(`${boundedStart}${escapeRe(term)}${boundedEnd}`, caseSensitive ? 'u' : 'iu');
|
|
137
|
+
return re.test(text);
|
|
138
|
+
}
|
|
139
|
+
const forLanguage = (termbase, language) => termbase.filter((t) => t.term && (!t.language || t.language === language));
|
|
140
|
+
/**
|
|
141
|
+
* Termbase violations for ONE translated text. Exported separately so the
|
|
142
|
+
* post-MT guard can reject a draft with the same rules the Studio shows.
|
|
143
|
+
*/
|
|
144
|
+
export function termbaseViolations(sourceText, translatedText, termbase, language) {
|
|
145
|
+
var _a, _b, _c, _d;
|
|
146
|
+
const findings = [];
|
|
147
|
+
for (const entry of forLanguage(termbase, language)) {
|
|
148
|
+
if (entry.termType === 'keep') {
|
|
149
|
+
// A keep-term appearing in the source must survive VERBATIM (exact
|
|
150
|
+
// casing — that's the point of keep: proper nouns, brand styling,
|
|
151
|
+
// stay-one-language words).
|
|
152
|
+
if (containsTerm(sourceText, entry.term, (_a = entry.caseSensitive) !== null && _a !== void 0 ? _a : false) &&
|
|
153
|
+
!containsTerm(translatedText, entry.term, true)) {
|
|
154
|
+
findings.push({
|
|
155
|
+
rule: 'term-keep',
|
|
156
|
+
severity: 'error',
|
|
157
|
+
message: `“${entry.term}” must stay exactly as written.`,
|
|
158
|
+
suggestion: entry.term,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else if (entry.termType === 'forbid') {
|
|
163
|
+
if (containsTerm(translatedText, entry.term, (_b = entry.caseSensitive) !== null && _b !== void 0 ? _b : false, language)) {
|
|
164
|
+
findings.push({
|
|
165
|
+
rule: 'term-forbid',
|
|
166
|
+
severity: 'error',
|
|
167
|
+
message: entry.useInstead
|
|
168
|
+
? `“${entry.term}” is not allowed — use “${entry.useInstead}”.`
|
|
169
|
+
: `“${entry.term}” is not allowed here.`,
|
|
170
|
+
suggestion: entry.useInstead,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Prefer remains advisory. Require is a source-scoped contract: it
|
|
176
|
+
// prevents two distinct concepts from collapsing into the same label.
|
|
177
|
+
if (entry.translation &&
|
|
178
|
+
containsTerm(sourceText, entry.term, (_c = entry.caseSensitive) !== null && _c !== void 0 ? _c : false) &&
|
|
179
|
+
!containsTerm(translatedText, entry.translation, (_d = entry.caseSensitive) !== null && _d !== void 0 ? _d : false, language)) {
|
|
180
|
+
findings.push({
|
|
181
|
+
rule: entry.termType === 'require' ? 'term-required' : 'term-prefer',
|
|
182
|
+
severity: entry.termType === 'require' ? 'error' : 'warning',
|
|
183
|
+
message: entry.termType === 'require'
|
|
184
|
+
? `“${entry.term}” must be translated as “${entry.translation}”.`
|
|
185
|
+
: `“${entry.term}” is preferably translated as “${entry.translation}”.`,
|
|
186
|
+
suggestion: entry.translation,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return findings;
|
|
192
|
+
}
|
|
193
|
+
/** True when a draft breaks an ERROR-severity termbase rule (post-MT gate). */
|
|
194
|
+
export function violatesTermbase(sourceText, translatedText, termbase, language) {
|
|
195
|
+
return termbaseViolations(sourceText, translatedText, termbase, language).some((f) => f.severity === 'error');
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Run every check for one language across the app's entries. Pure and
|
|
199
|
+
* client-safe — the Studio computes findings locally from data it already has.
|
|
200
|
+
*/
|
|
201
|
+
export function runChecks(entries, language, termbase = []) {
|
|
202
|
+
var _a, _b;
|
|
203
|
+
const findings = [];
|
|
204
|
+
for (const entry of entries) {
|
|
205
|
+
const text = (_b = (_a = entry.units[language]) === null || _a === void 0 ? void 0 : _a.text) === null || _b === void 0 ? void 0 : _b.trim();
|
|
206
|
+
if (!text || !entry.sourceText.trim())
|
|
207
|
+
continue;
|
|
208
|
+
const format = entry.format === 'icu' ? 'icu' : 'simple';
|
|
209
|
+
const comparison = compareMessageArguments(entry.sourceText, text, format);
|
|
210
|
+
if (comparison.sourceError || comparison.targetError) {
|
|
211
|
+
findings.push({
|
|
212
|
+
key: entry.key,
|
|
213
|
+
language,
|
|
214
|
+
rule: 'message-syntax',
|
|
215
|
+
severity: 'error',
|
|
216
|
+
message: comparison.sourceError
|
|
217
|
+
? `The source ${format.toUpperCase()} message is invalid: ${comparison.sourceError}`
|
|
218
|
+
: `The translation ${format.toUpperCase()} message is invalid: ${comparison.targetError}`,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
else if (!comparison.valid) {
|
|
222
|
+
const details = [
|
|
223
|
+
comparison.missing.length
|
|
224
|
+
? `Missing ${comparison.missing
|
|
225
|
+
.map((name) => `{${name}}`)
|
|
226
|
+
.join(', ')}.`
|
|
227
|
+
: '',
|
|
228
|
+
comparison.extra.length
|
|
229
|
+
? `Unexpected ${comparison.extra
|
|
230
|
+
.map((name) => `{${name}}`)
|
|
231
|
+
.join(', ')}.`
|
|
232
|
+
: '',
|
|
233
|
+
...comparison.typeMismatches.map(({ name, source, target }) => `{${name}} changes from ${source.join('/')} to ${target.join('/')}.`),
|
|
234
|
+
].filter(Boolean);
|
|
235
|
+
findings.push({
|
|
236
|
+
key: entry.key,
|
|
237
|
+
language,
|
|
238
|
+
rule: 'placeholder-integrity',
|
|
239
|
+
severity: 'error',
|
|
240
|
+
message: details.join(' '),
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
const legacyPlural = detectLegacyPluralSuffixes(entry.sourceText);
|
|
244
|
+
if (format === 'simple' && legacyPlural.length > 0) {
|
|
245
|
+
findings.push({
|
|
246
|
+
key: entry.key,
|
|
247
|
+
language,
|
|
248
|
+
rule: 'legacy-plural-suffix',
|
|
249
|
+
severity: 'warning',
|
|
250
|
+
message: `${legacyPlural
|
|
251
|
+
.map(({ suffixArgument }) => `{${suffixArgument}}`)
|
|
252
|
+
.join(', ')} is an English suffix variable, not pluralization. It may be omitted in this translation; migrate this key to an ICU plural for locale-aware grammar.`,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
for (const violation of termbaseViolations(entry.sourceText, text, termbase, language)) {
|
|
256
|
+
findings.push({ key: entry.key, language, ...violation });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Identical source ⇒ divergent target. Suggest the majority variant.
|
|
260
|
+
for (const conflict of findInconsistentTranslations(entries, language)) {
|
|
261
|
+
const majority = [...conflict.variants].sort((a, b) => b.keys.length - a.keys.length || a.text.localeCompare(b.text))[0];
|
|
262
|
+
for (const variant of conflict.variants) {
|
|
263
|
+
if (variant === majority)
|
|
264
|
+
continue;
|
|
265
|
+
for (const key of variant.keys) {
|
|
266
|
+
findings.push({
|
|
267
|
+
key,
|
|
268
|
+
language,
|
|
269
|
+
rule: 'source-divergence',
|
|
270
|
+
severity: 'warning',
|
|
271
|
+
message: `“${conflict.sourceText}” is translated “${variant.text}” here but “${majority.text}” on ${majority.keys.length} other key${majority.keys.length === 1 ? '' : 's'}.`,
|
|
272
|
+
suggestion: majority.text,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return findings;
|
|
278
|
+
}
|
|
279
|
+
/** Findings grouped per key for inline cell badges. */
|
|
280
|
+
export function findingsByKey(findings) {
|
|
281
|
+
var _a;
|
|
282
|
+
const map = new Map();
|
|
283
|
+
for (const finding of findings) {
|
|
284
|
+
map.set(finding.key, [...((_a = map.get(finding.key)) !== null && _a !== void 0 ? _a : []), finding]);
|
|
285
|
+
}
|
|
286
|
+
return map;
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/checks.ts"],"names":[],"mappings":"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"}
|