@_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.
Files changed (153) hide show
  1. package/README.md +102 -0
  2. package/lib/cjs/data/translation.json +8 -0
  3. package/lib/esm/archive.d.ts +133 -0
  4. package/lib/esm/archive.js +567 -0
  5. package/lib/esm/archive.js.map +1 -0
  6. package/lib/esm/authorization.d.ts +12 -0
  7. package/lib/esm/authorization.js +10 -0
  8. package/lib/esm/authorization.js.map +1 -0
  9. package/lib/esm/backend.d.ts +1 -0
  10. package/lib/esm/backend.js +3 -0
  11. package/lib/esm/backend.js.map +1 -0
  12. package/lib/esm/cdn-loader.d.ts +40 -0
  13. package/lib/esm/cdn-loader.js +200 -0
  14. package/lib/esm/cdn-loader.js.map +1 -0
  15. package/lib/esm/checks.d.ts +56 -0
  16. package/lib/esm/checks.js +288 -0
  17. package/lib/esm/checks.js.map +1 -0
  18. package/lib/esm/compile.d.ts +64 -0
  19. package/lib/esm/compile.js +99 -0
  20. package/lib/esm/compile.js.map +1 -0
  21. package/lib/esm/core/messages.d.ts +25 -0
  22. package/lib/esm/core/messages.js +76 -0
  23. package/lib/esm/core/messages.js.map +1 -0
  24. package/lib/esm/data/translation.json +8 -0
  25. package/lib/esm/discovery.d.ts +97 -0
  26. package/lib/esm/discovery.js +365 -0
  27. package/lib/esm/discovery.js.map +1 -0
  28. package/lib/esm/exchange.d.ts +100 -0
  29. package/lib/esm/exchange.js +223 -0
  30. package/lib/esm/exchange.js.map +1 -0
  31. package/lib/esm/export.d.ts +28 -0
  32. package/lib/esm/export.js +92 -0
  33. package/lib/esm/export.js.map +1 -0
  34. package/lib/esm/formats/csv.d.ts +8 -0
  35. package/lib/esm/formats/csv.js +397 -0
  36. package/lib/esm/formats/csv.js.map +1 -0
  37. package/lib/esm/formats/json.d.ts +30 -0
  38. package/lib/esm/formats/json.js +459 -0
  39. package/lib/esm/formats/json.js.map +1 -0
  40. package/lib/esm/formats/xliff.d.ts +12 -0
  41. package/lib/esm/formats/xliff.js +577 -0
  42. package/lib/esm/formats/xliff.js.map +1 -0
  43. package/lib/esm/import.d.ts +197 -0
  44. package/lib/esm/import.js +391 -0
  45. package/lib/esm/import.js.map +1 -0
  46. package/lib/esm/index.d.ts +42 -0
  47. package/lib/esm/index.js +34 -0
  48. package/lib/esm/index.js.map +1 -0
  49. package/lib/esm/key-sync-node.d.ts +29 -0
  50. package/lib/esm/key-sync-node.js +174 -0
  51. package/lib/esm/key-sync-node.js.map +1 -0
  52. package/lib/esm/key-sync.d.ts +46 -0
  53. package/lib/esm/key-sync.js +114 -0
  54. package/lib/esm/key-sync.js.map +1 -0
  55. package/lib/esm/key-version.d.ts +50 -0
  56. package/lib/esm/key-version.js +134 -0
  57. package/lib/esm/key-version.js.map +1 -0
  58. package/lib/esm/languages.d.ts +18 -0
  59. package/lib/esm/languages.js +74 -0
  60. package/lib/esm/languages.js.map +1 -0
  61. package/lib/esm/linked-discovery.d.ts +44 -0
  62. package/lib/esm/linked-discovery.js +267 -0
  63. package/lib/esm/linked-discovery.js.map +1 -0
  64. package/lib/esm/message-format.d.ts +41 -0
  65. package/lib/esm/message-format.js +133 -0
  66. package/lib/esm/message-format.js.map +1 -0
  67. package/lib/esm/mt.d.ts +111 -0
  68. package/lib/esm/mt.js +142 -0
  69. package/lib/esm/mt.js.map +1 -0
  70. package/lib/esm/ontologies/translation.d.ts +203 -0
  71. package/lib/esm/ontologies/translation.js +219 -0
  72. package/lib/esm/ontologies/translation.js.map +1 -0
  73. package/lib/esm/package.d.ts +4 -0
  74. package/lib/esm/package.js +11 -0
  75. package/lib/esm/package.js.map +1 -0
  76. package/lib/esm/react.d.ts +55 -0
  77. package/lib/esm/react.js +127 -0
  78. package/lib/esm/react.js.map +1 -0
  79. package/lib/esm/records.d.ts +151 -0
  80. package/lib/esm/records.js +2 -0
  81. package/lib/esm/records.js.map +1 -0
  82. package/lib/esm/release.d.ts +198 -0
  83. package/lib/esm/release.js +435 -0
  84. package/lib/esm/release.js.map +1 -0
  85. package/lib/esm/shape-catalog.d.ts +79 -0
  86. package/lib/esm/shape-catalog.js +118 -0
  87. package/lib/esm/shape-catalog.js.map +1 -0
  88. package/lib/esm/shapes/GlossaryTerm.d.ts +28 -0
  89. package/lib/esm/shapes/GlossaryTerm.js +94 -0
  90. package/lib/esm/shapes/GlossaryTerm.js.map +1 -0
  91. package/lib/esm/shapes/TranslationInventoryOrigin.d.ts +33 -0
  92. package/lib/esm/shapes/TranslationInventoryOrigin.js +209 -0
  93. package/lib/esm/shapes/TranslationInventoryOrigin.js.map +1 -0
  94. package/lib/esm/shapes/TranslationKey.d.ts +92 -0
  95. package/lib/esm/shapes/TranslationKey.js +194 -0
  96. package/lib/esm/shapes/TranslationKey.js.map +1 -0
  97. package/lib/esm/shapes/TranslationKeyVersion.d.ts +19 -0
  98. package/lib/esm/shapes/TranslationKeyVersion.js +125 -0
  99. package/lib/esm/shapes/TranslationKeyVersion.js.map +1 -0
  100. package/lib/esm/shapes/TranslationLanguage.d.ts +15 -0
  101. package/lib/esm/shapes/TranslationLanguage.js +102 -0
  102. package/lib/esm/shapes/TranslationLanguage.js.map +1 -0
  103. package/lib/esm/shapes/TranslationProvider.d.ts +409 -0
  104. package/lib/esm/shapes/TranslationProvider.js +1376 -0
  105. package/lib/esm/shapes/TranslationProvider.js.map +1 -0
  106. package/lib/esm/shapes/TranslationRelease.d.ts +36 -0
  107. package/lib/esm/shapes/TranslationRelease.js +149 -0
  108. package/lib/esm/shapes/TranslationRelease.js.map +1 -0
  109. package/lib/esm/shapes/TranslationRevision.d.ts +39 -0
  110. package/lib/esm/shapes/TranslationRevision.js +159 -0
  111. package/lib/esm/shapes/TranslationRevision.js.map +1 -0
  112. package/lib/esm/shapes/TranslationUnit.d.ts +26 -0
  113. package/lib/esm/shapes/TranslationUnit.js +111 -0
  114. package/lib/esm/shapes/TranslationUnit.js.map +1 -0
  115. package/package.json +134 -0
  116. package/src/archive.ts +894 -0
  117. package/src/authorization.ts +33 -0
  118. package/src/backend.ts +2 -0
  119. package/src/cdn-loader.ts +264 -0
  120. package/src/checks.ts +413 -0
  121. package/src/compile.ts +168 -0
  122. package/src/core/messages.ts +106 -0
  123. package/src/data/translation.json +8 -0
  124. package/src/discovery.ts +594 -0
  125. package/src/exchange.ts +428 -0
  126. package/src/export.ts +135 -0
  127. package/src/formats/csv.ts +459 -0
  128. package/src/formats/json.ts +587 -0
  129. package/src/formats/xliff.ts +708 -0
  130. package/src/import.ts +648 -0
  131. package/src/index.ts +207 -0
  132. package/src/key-sync-node.ts +269 -0
  133. package/src/key-sync.ts +168 -0
  134. package/src/key-version.ts +206 -0
  135. package/src/languages.ts +102 -0
  136. package/src/linked-discovery.ts +385 -0
  137. package/src/message-format.ts +214 -0
  138. package/src/mt.ts +248 -0
  139. package/src/ontologies/translation.ts +234 -0
  140. package/src/package.ts +19 -0
  141. package/src/react.tsx +250 -0
  142. package/src/records.ts +180 -0
  143. package/src/release.ts +726 -0
  144. package/src/shape-catalog.ts +198 -0
  145. package/src/shapes/GlossaryTerm.ts +61 -0
  146. package/src/shapes/TranslationInventoryOrigin.ts +131 -0
  147. package/src/shapes/TranslationKey.ts +174 -0
  148. package/src/shapes/TranslationKeyVersion.ts +77 -0
  149. package/src/shapes/TranslationLanguage.ts +51 -0
  150. package/src/shapes/TranslationProvider.ts +1755 -0
  151. package/src/shapes/TranslationRelease.ts +114 -0
  152. package/src/shapes/TranslationRevision.ts +105 -0
  153. 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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"}