@_linked/translation 0.8.1 → 0.9.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.
@@ -1,4 +1,9 @@
1
- let resolver;
1
+ import { sharedState } from '@_linked/core/utils/sharedState';
2
+ // On globalThis so a second copy of this module sees the resolver the host configured on the
3
+ // first; a copy that cannot see it would deny every author.
4
+ const state = sharedState('@_linked/translation', 'authorization', () => ({
5
+ resolver: undefined,
6
+ }));
2
7
  /**
3
8
  * Host hook: the host decides who may read and author an app's translations
4
9
  * (for example by evaluating its own translator assignments and project roles).
@@ -8,13 +13,14 @@ let resolver;
8
13
  * public runtime fetch (`getMessages`) keeps working.
9
14
  */
10
15
  export function configureTranslationAuthorization(next) {
11
- resolver = next;
16
+ state.resolver = next;
12
17
  }
13
18
  /**
14
19
  * Whether the host's resolver grants `request`. Denies when no resolver is
15
20
  * configured, and when the resolver throws or answers anything but `true`.
16
21
  */
17
22
  export async function canAuthorTranslation(request) {
23
+ const resolver = state.resolver;
18
24
  if (!resolver)
19
25
  return false;
20
26
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"authorization.js","sourceRoot":"","sources":["../../src/authorization.ts"],"names":[],"mappings":"AAkBA,IAAI,QAAsD,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC,CAC/C,IAAuC;IAEvC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAwC;IAExC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,uDAAuD,EAAE,KAAK,CAAC,CAAC;QAC7E,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["export type TranslationAuthoringAction =\n | 'manage'\n | 'review'\n | 'propose'\n | 'run-mt'\n | 'read';\n\nexport interface TranslationAuthorizationRequest {\n appId: string;\n actorWebId: string;\n action: TranslationAuthoringAction;\n language?: string;\n}\n\nexport type TranslationAuthorizationResolver = (\n request: TranslationAuthorizationRequest,\n) => Promise<boolean>;\n\nlet resolver: TranslationAuthorizationResolver | undefined;\n\n/**\n * Host hook: the host decides who may read and author an app's translations\n * (for example by evaluating its own translator assignments and project roles).\n *\n * The resolver is the only source of a \"yes\". A host that does not configure\n * one gets no authoring or read access over the translation provider; only the\n * public runtime fetch (`getMessages`) keeps working.\n */\nexport function configureTranslationAuthorization(\n next?: TranslationAuthorizationResolver,\n): void {\n resolver = next;\n}\n\n/**\n * Whether the host's resolver grants `request`. Denies when no resolver is\n * configured, and when the resolver throws or answers anything but `true`.\n */\nexport async function canAuthorTranslation(\n request: TranslationAuthorizationRequest,\n): Promise<boolean> {\n if (!resolver) return false;\n try {\n return (await resolver(request)) === true;\n } catch (error) {\n console.warn('[translation] authorization resolver failed; denying.', error);\n return false;\n }\n}\n"]}
1
+ {"version":3,"file":"authorization.js","sourceRoot":"","sources":["../../src/authorization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAoB9D,6FAA6F;AAC7F,4DAA4D;AAC5D,MAAM,KAAK,GAAG,WAAW,CAAC,sBAAsB,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC,CAAC;IACxE,QAAQ,EAAE,SAAyD;CACpE,CAAC,CAAC,CAAC;AAEJ;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC,CAC/C,IAAuC;IAEvC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAwC;IAExC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAChC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,uDAAuD,EAAE,KAAK,CAAC,CAAC;QAC7E,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["import { sharedState } from '@_linked/core/utils/sharedState';\n\nexport type TranslationAuthoringAction =\n | 'manage'\n | 'review'\n | 'propose'\n | 'run-mt'\n | 'read';\n\nexport interface TranslationAuthorizationRequest {\n appId: string;\n actorWebId: string;\n action: TranslationAuthoringAction;\n language?: string;\n}\n\nexport type TranslationAuthorizationResolver = (\n request: TranslationAuthorizationRequest,\n) => Promise<boolean>;\n\n// On globalThis so a second copy of this module sees the resolver the host configured on the\n// first; a copy that cannot see it would deny every author.\nconst state = sharedState('@_linked/translation', 'authorization', () => ({\n resolver: undefined as TranslationAuthorizationResolver | undefined,\n}));\n\n/**\n * Host hook: the host decides who may read and author an app's translations\n * (for example by evaluating its own translator assignments and project roles).\n *\n * The resolver is the only source of a \"yes\". A host that does not configure\n * one gets no authoring or read access over the translation provider; only the\n * public runtime fetch (`getMessages`) keeps working.\n */\nexport function configureTranslationAuthorization(\n next?: TranslationAuthorizationResolver,\n): void {\n state.resolver = next;\n}\n\n/**\n * Whether the host's resolver grants `request`. Denies when no resolver is\n * configured, and when the resolver throws or answers anything but `true`.\n */\nexport async function canAuthorTranslation(\n request: TranslationAuthorizationRequest,\n): Promise<boolean> {\n const resolver = state.resolver;\n if (!resolver) return false;\n try {\n return (await resolver(request)) === true;\n } catch (error) {\n console.warn('[translation] authorization resolver failed; denying.', error);\n return false;\n }\n}\n"]}
@@ -0,0 +1,489 @@
1
+ @prefix translation: <https://linked.cm/ont/translation/> .
2
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
3
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
4
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
5
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
6
+ @prefix dcterms: <http://purl.org/dc/terms/> .
7
+ @prefix skos: <http://www.w3.org/2004/02/skos/core#> .
8
+ @prefix schema: <http://schema.org/> .
9
+
10
+ translation:
11
+ a owl:Ontology ;
12
+ dcterms:description "Terms of translation keys, their versions, per-language units, revisions, releases, glossary and inventory."@en ;
13
+ dcterms:title "Linked translation"@en ;
14
+ owl:versionInfo "0.9.0" .
15
+
16
+ translation:GlossaryTerm
17
+ a owl:Class ;
18
+ rdfs:comment "A glossary entry that steers machine translation, such as a brand name to keep or a spelling to forbid."@en ;
19
+ rdfs:label "glossary term"@en .
20
+
21
+ translation:TranslationInventoryOrigin
22
+ a owl:Class ;
23
+ rdfs:comment "One place a branch's translation inventory observed a key, such as a source file line or a shape property."@en ;
24
+ rdfs:label "translation inventory origin"@en .
25
+
26
+ translation:TranslationKey
27
+ a owl:Class ;
28
+ rdfs:comment "A translatable key, either a UI string (kind ui) or a field of a graph node (kind content), with its canonical English source text."@en ;
29
+ rdfs:label "translation key"@en .
30
+
31
+ translation:TranslationKeyVersion
32
+ a owl:Class ;
33
+ rdfs:comment "An immutable version of a key's source text and runtime argument contract."@en ;
34
+ rdfs:label "translation key version"@en .
35
+
36
+ translation:TranslationLanguage
37
+ a owl:Class ;
38
+ rdfs:comment "A language an app is configured for, identified by its BCP-47 tag."@en ;
39
+ rdfs:label "translation language"@en .
40
+
41
+ translation:TranslationRelease
42
+ a owl:Class ;
43
+ rdfs:comment "Graph metadata of an immutable translation release, whose key and language selection lives in a content-addressed manifest."@en ;
44
+ rdfs:label "translation release"@en .
45
+
46
+ translation:TranslationRevision
47
+ a owl:Class ;
48
+ rdfs:comment "One append-only authored value for a key and language: a change in its history, a translator's proposal or a machine suggestion."@en ;
49
+ rdfs:label "translation revision"@en .
50
+
51
+ translation:TranslationUnit
52
+ a owl:Class ;
53
+ rdfs:comment "One language's current value for a translation key, with its review state and provenance."@en ;
54
+ rdfs:label "translation unit"@en .
55
+
56
+ translation:argumentSignature
57
+ a owl:DatatypeProperty ;
58
+ rdfs:comment "The runtime arguments a key version's message takes."@en ;
59
+ rdfs:label "argument signature"@en .
60
+
61
+ translation:author
62
+ a owl:ObjectProperty ;
63
+ rdfs:comment "The WebID or machine translation provider that wrote a revision."@en ;
64
+ rdfs:label "author"@en .
65
+
66
+ translation:authorKind
67
+ a owl:DatatypeProperty ;
68
+ rdfs:comment "Whether a revision was written by a human or a machine."@en ;
69
+ rdfs:label "author kind"@en .
70
+
71
+ translation:autoPublish
72
+ a owl:DatatypeProperty ;
73
+ rdfs:comment "Whether an edit publishes translations automatically (off by default)."@en ;
74
+ rdfs:label "auto publish"@en .
75
+
76
+ translation:basedOnText
77
+ a owl:DatatypeProperty ;
78
+ rdfs:comment "The unit's text when a revision was written, to detect conflicts."@en ;
79
+ rdfs:label "based on text"@en .
80
+
81
+ translation:branch
82
+ a owl:ObjectProperty ;
83
+ rdfs:comment "The branch a translation release was built from."@en ;
84
+ rdfs:label "branch"@en .
85
+
86
+ translation:buildId
87
+ a owl:DatatypeProperty ;
88
+ rdfs:comment "The identifier of the build a translation release was cut for."@en ;
89
+ rdfs:label "build id"@en .
90
+
91
+ translation:caseSensitive
92
+ a owl:DatatypeProperty ;
93
+ rdfs:comment "Whether a glossary term matches only with its exact casing."@en ;
94
+ rdfs:label "case sensitive"@en .
95
+
96
+ translation:cdnTarget
97
+ a owl:DatatypeProperty ;
98
+ rdfs:comment "The base URL or bucket prefix translations are delivered to."@en ;
99
+ rdfs:label "CDN target"@en .
100
+
101
+ translation:channel
102
+ a owl:DatatypeProperty ;
103
+ rdfs:comment "The delivery channel of a translation release, preview or production."@en ;
104
+ rdfs:label "channel"@en .
105
+
106
+ translation:contractHash
107
+ a owl:DatatypeProperty ;
108
+ rdfs:comment "The hash of a key version's source text and argument contract."@en ;
109
+ rdfs:label "contract hash"@en .
110
+
111
+ translation:contractSetHash
112
+ a owl:DatatypeProperty ;
113
+ rdfs:comment "The hash of every key contract a release covers."@en ;
114
+ rdfs:label "contract set hash"@en .
115
+
116
+ translation:createdAt
117
+ a owl:DatatypeProperty ;
118
+ rdfs:comment "When a key version, release or revision was created."@en ;
119
+ rdfs:label "created at"@en .
120
+
121
+ translation:createdBy
122
+ a owl:ObjectProperty ;
123
+ rdfs:comment "The WebID that created a key version."@en ;
124
+ rdfs:label "created by"@en .
125
+
126
+ translation:currentVersion
127
+ a owl:ObjectProperty ;
128
+ rdfs:comment "The key version that is a translation key's current source."@en ;
129
+ rdfs:label "current version"@en .
130
+
131
+ translation:decidedAt
132
+ a owl:DatatypeProperty ;
133
+ rdfs:comment "When a proposed or suggested revision was accepted or rejected."@en ;
134
+ rdfs:label "decided at"@en .
135
+
136
+ translation:decidedBy
137
+ a owl:ObjectProperty ;
138
+ rdfs:comment "The WebID of the reviewer who decided a revision."@en ;
139
+ rdfs:label "decided by"@en .
140
+
141
+ translation:defaultLanguage
142
+ a owl:DatatypeProperty ;
143
+ rdfs:comment "The BCP-47 tag of the app's authored source language."@en ;
144
+ rdfs:label "default language"@en .
145
+
146
+ translation:description
147
+ a owl:DatatypeProperty ;
148
+ rdfs:comment "A note that tells translators what a key or glossary term means and where it appears."@en ;
149
+ rdfs:label "description"@en .
150
+
151
+ translation:englishName
152
+ a owl:DatatypeProperty ;
153
+ rdfs:comment "A language's name in English."@en ;
154
+ rdfs:label "English name"@en .
155
+
156
+ translation:fallbackLanguage
157
+ a owl:ObjectProperty ;
158
+ rdfs:comment "The language whose text is shown when a value is missing in this one."@en ;
159
+ rdfs:label "fallback language"@en .
160
+
161
+ translation:format
162
+ a owl:DatatypeProperty ;
163
+ rdfs:comment "The message syntax of a key's text, simple or icu."@en ;
164
+ rdfs:label "format"@en .
165
+
166
+ translation:fromPackage
167
+ a owl:DatatypeProperty ;
168
+ rdfs:comment "The package whose seed catalog supplied a key."@en ;
169
+ rdfs:label "from package"@en .
170
+
171
+ translation:glossaryLanguage
172
+ a owl:DatatypeProperty ;
173
+ rdfs:comment "The BCP-47 tag of the language a glossary translation is for."@en ;
174
+ rdfs:label "glossary language"@en .
175
+
176
+ translation:glossaryTranslation
177
+ a owl:DatatypeProperty ;
178
+ rdfs:comment "The required translation of a glossary term."@en ;
179
+ rdfs:label "glossary translation"@en .
180
+
181
+ translation:hotfixSequence
182
+ a owl:DatatypeProperty ;
183
+ rdfs:comment "The hotfix number of a release on top of its base release."@en ;
184
+ rdfs:label "hotfix sequence"@en .
185
+
186
+ translation:inventoryAuthoritative
187
+ a owl:DatatypeProperty ;
188
+ rdfs:comment "Whether an inventory origin is the authoritative one for its key."@en ;
189
+ rdfs:label "inventory authoritative"@en .
190
+
191
+ translation:inventoryBranch
192
+ a owl:ObjectProperty ;
193
+ rdfs:comment "The branch an inventory origin was observed on."@en ;
194
+ rdfs:label "inventory branch"@en .
195
+
196
+ translation:inventoryFile
197
+ a owl:DatatypeProperty ;
198
+ rdfs:comment "The source file an inventory origin was found in."@en ;
199
+ rdfs:label "inventory file"@en .
200
+
201
+ translation:inventoryIdentity
202
+ a owl:DatatypeProperty ;
203
+ rdfs:comment "The stable identity of an inventory item across observations."@en ;
204
+ rdfs:label "inventory identity"@en .
205
+
206
+ translation:inventoryLine
207
+ a owl:DatatypeProperty ;
208
+ rdfs:comment "The line of the source file an inventory origin was found on."@en ;
209
+ rdfs:label "inventory line"@en .
210
+
211
+ translation:inventoryNode
212
+ a owl:ObjectProperty ;
213
+ rdfs:comment "The IRI of the graph node an inventory origin was found on."@en ;
214
+ rdfs:label "inventory node"@en .
215
+
216
+ translation:inventoryObservedAt
217
+ a owl:DatatypeProperty ;
218
+ rdfs:comment "When an inventory origin was last observed."@en ;
219
+ rdfs:label "inventory observed at"@en .
220
+
221
+ translation:inventoryOwner
222
+ a owl:ObjectProperty ;
223
+ rdfs:comment "The IRI of the node that owns an inventory origin."@en ;
224
+ rdfs:label "inventory owner"@en .
225
+
226
+ translation:inventoryProperty
227
+ a owl:ObjectProperty ;
228
+ rdfs:comment "The IRI of the property an inventory origin was found on."@en ;
229
+ rdfs:label "inventory property"@en .
230
+
231
+ translation:inventoryShape
232
+ a owl:ObjectProperty ;
233
+ rdfs:comment "The IRI of the shape an inventory origin was found on."@en ;
234
+ rdfs:label "inventory shape"@en .
235
+
236
+ translation:inventorySource
237
+ a owl:DatatypeProperty ;
238
+ rdfs:comment "The kind of source an inventory origin was found in."@en ;
239
+ rdfs:label "inventory source"@en .
240
+
241
+ translation:inventorySourceId
242
+ a owl:DatatypeProperty ;
243
+ rdfs:comment "The identifier of the source an inventory origin was found in."@en ;
244
+ rdfs:label "inventory source id"@en .
245
+
246
+ translation:inventoryStatus
247
+ a owl:DatatypeProperty ;
248
+ rdfs:comment "The status of an inventory origin: confirmed, pending, conflict or orphaned."@en ;
249
+ rdfs:label "inventory status"@en .
250
+
251
+ translation:inventoryTargetClass
252
+ a owl:DatatypeProperty ;
253
+ rdfs:comment "The target class of the shape an inventory origin was found on, as a literal."@en ;
254
+ rdfs:label "inventory target class"@en .
255
+
256
+ translation:key
257
+ a owl:DatatypeProperty ;
258
+ rdfs:comment "A translation key's name within its namespace."@en ;
259
+ rdfs:label "key"@en .
260
+
261
+ translation:kind
262
+ a owl:DatatypeProperty ;
263
+ rdfs:comment "Whether a translation key is a UI string (ui) or a field of a graph node (content)."@en ;
264
+ rdfs:label "kind"@en .
265
+
266
+ translation:language
267
+ a owl:DatatypeProperty ;
268
+ rdfs:comment "The BCP-47 tag of a unit's or revision's language."@en ;
269
+ rdfs:label "language"@en .
270
+
271
+ translation:languageCode
272
+ a owl:DatatypeProperty ;
273
+ rdfs:comment "The BCP-47 tag of a configured language."@en ;
274
+ rdfs:label "language code"@en .
275
+
276
+ translation:languageEnabled
277
+ a owl:DatatypeProperty ;
278
+ rdfs:comment "Whether a configured language is switched on for the app."@en ;
279
+ rdfs:label "language enabled"@en .
280
+
281
+ translation:languageIdentifier
282
+ a owl:ObjectProperty ;
283
+ rdfs:comment "The IRI that identifies a configured language."@en ;
284
+ rdfs:label "language identifier"@en .
285
+
286
+ translation:languageSupported
287
+ a owl:DatatypeProperty ;
288
+ rdfs:comment "Whether a language is supported by the app."@en ;
289
+ rdfs:label "language supported"@en .
290
+
291
+ translation:languages
292
+ a owl:DatatypeProperty ;
293
+ rdfs:comment "A BCP-47 tag the app's translation is active for, the source language included; multi-valued."@en ;
294
+ rdfs:label "languages"@en .
295
+
296
+ translation:manifestHash
297
+ a owl:DatatypeProperty ;
298
+ rdfs:comment "The hash of a release's content-addressed manifest."@en ;
299
+ rdfs:label "manifest hash"@en .
300
+
301
+ translation:mtDefault
302
+ a owl:DatatypeProperty ;
303
+ rdfs:comment "The key of the app's default machine translation provider."@en ;
304
+ rdfs:label "default MT provider"@en .
305
+
306
+ translation:mtFormality
307
+ a owl:DatatypeProperty ;
308
+ rdfs:comment "The formality machine translation uses, for the app (formal) or per language (de:informal); multi-valued."@en ;
309
+ rdfs:label "MT formality"@en .
310
+
311
+ translation:mtProvider
312
+ a owl:DatatypeProperty ;
313
+ rdfs:comment "The key of the machine translation provider that wrote a revision."@en ;
314
+ rdfs:label "MT provider"@en .
315
+
316
+ translation:mtStyleNote
317
+ a owl:DatatypeProperty ;
318
+ rdfs:comment "Free-text tone guidance given to LLM machine translation providers."@en ;
319
+ rdfs:label "MT style note"@en .
320
+
321
+ translation:namespace
322
+ a owl:DatatypeProperty ;
323
+ rdfs:comment "The namespace a translation key belongs to."@en ;
324
+ rdfs:label "namespace"@en .
325
+
326
+ translation:nativeName
327
+ a owl:DatatypeProperty ;
328
+ rdfs:comment "A language's name in that language."@en ;
329
+ rdfs:label "native name"@en .
330
+
331
+ translation:note
332
+ a owl:DatatypeProperty ;
333
+ rdfs:comment "A translator's or reviewer's comment on a revision."@en ;
334
+ rdfs:label "note"@en .
335
+
336
+ translation:ofApplication
337
+ a owl:ObjectProperty ;
338
+ rdfs:comment "The app a translation key, unit, revision, release or inventory record belongs to."@en ;
339
+ rdfs:label "of application"@en .
340
+
341
+ translation:ofField
342
+ a owl:DatatypeProperty ;
343
+ rdfs:comment "The field (property) of the node a content translation key translates."@en ;
344
+ rdfs:label "of field"@en .
345
+
346
+ translation:ofKey
347
+ a owl:ObjectProperty ;
348
+ rdfs:comment "The translation key a unit, revision or key version belongs to."@en ;
349
+ rdfs:label "of key"@en .
350
+
351
+ translation:ofKeyVersion
352
+ a owl:ObjectProperty ;
353
+ rdfs:comment "The key version a unit or revision was written against."@en ;
354
+ rdfs:label "of key version"@en .
355
+
356
+ translation:ofNode
357
+ a owl:ObjectProperty ;
358
+ rdfs:comment "The graph node whose field a content translation key translates."@en ;
359
+ rdfs:label "of node"@en .
360
+
361
+ translation:ofProperty
362
+ a owl:ObjectProperty ;
363
+ rdfs:comment "The predicate a package-supplied key is about."@en ;
364
+ rdfs:label "of property"@en .
365
+
366
+ translation:ofResource
367
+ a owl:ObjectProperty ;
368
+ rdfs:comment "Any other semantic resource a key is about, such as an action, group, condition or port."@en ;
369
+ rdfs:label "of resource"@en .
370
+
371
+ translation:ofShape
372
+ a owl:ObjectProperty ;
373
+ rdfs:comment "The SHACL shape a package-supplied key is about."@en ;
374
+ rdfs:label "of shape"@en .
375
+
376
+ translation:origin
377
+ a owl:DatatypeProperty ;
378
+ rdfs:comment "Where a unit's current value came from, such as translation memory (tm)."@en ;
379
+ rdfs:label "origin"@en .
380
+
381
+ translation:overridden
382
+ a owl:DatatypeProperty ;
383
+ rdfs:comment "Marks a shape key the app changed from its canonical text, so shape propagation leaves it alone."@en ;
384
+ rdfs:label "overridden"@en .
385
+
386
+ translation:parentLanguage
387
+ a owl:ObjectProperty ;
388
+ rdfs:comment "The broader language a regional language belongs to (de for de-CH)."@en ;
389
+ rdfs:label "parent language"@en .
390
+
391
+ translation:publishedAt
392
+ a owl:DatatypeProperty ;
393
+ rdfs:comment "When a translation release was published."@en ;
394
+ rdfs:label "published at"@en .
395
+
396
+ translation:qualitySummary
397
+ a owl:DatatypeProperty ;
398
+ rdfs:comment "A summary of the review state of the values in a release."@en ;
399
+ rdfs:label "quality summary"@en .
400
+
401
+ translation:releaseId
402
+ a owl:DatatypeProperty ;
403
+ rdfs:comment "The identifier of a translation release."@en ;
404
+ rdfs:label "release id"@en .
405
+
406
+ translation:reviewedOnlyLanguage
407
+ a owl:DatatypeProperty ;
408
+ rdfs:comment "A BCP-47 tag whose translations are published only once reviewed; multi-valued."@en ;
409
+ rdfs:label "reviewed-only language"@en .
410
+
411
+ translation:shapeSource
412
+ a owl:DatatypeProperty ;
413
+ rdfs:comment "The canonical source text at the moment a shape key was overridden, which clearing the override restores."@en ;
414
+ rdfs:label "shape source"@en .
415
+
416
+ translation:sourceHash
417
+ a owl:DatatypeProperty ;
418
+ rdfs:comment "The hash of a key version's source text."@en ;
419
+ rdfs:label "source hash"@en .
420
+
421
+ translation:sourceLanguage
422
+ a owl:DatatypeProperty ;
423
+ rdfs:comment "The BCP-47 tag of the language a key version's source text is in."@en ;
424
+ rdfs:label "source language"@en .
425
+
426
+ translation:sourceText
427
+ a owl:DatatypeProperty ;
428
+ rdfs:comment "The canonical English text of a translation key or key version."@en ;
429
+ rdfs:label "source text"@en .
430
+
431
+ translation:state
432
+ a owl:DatatypeProperty ;
433
+ rdfs:comment "The review state of a translation unit: untranslated, machine, reviewed or stale."@en ;
434
+ rdfs:label "state"@en .
435
+
436
+ translation:status
437
+ a owl:DatatypeProperty ;
438
+ rdfs:comment "The state of a release (draft, published, superseded, retired) or of a revision (applied, proposed, suggested, accepted, rejected, superseded)."@en ;
439
+ rdfs:label "status"@en .
440
+
441
+ translation:supersedes
442
+ a owl:ObjectProperty ;
443
+ rdfs:comment "The key version or release this one replaces."@en ;
444
+ rdfs:label "supersedes"@en .
445
+
446
+ translation:supportedUntil
447
+ a owl:DatatypeProperty ;
448
+ rdfs:comment "Until when a translation release is supported."@en ;
449
+ rdfs:label "supported until"@en .
450
+
451
+ translation:term
452
+ a owl:DatatypeProperty ;
453
+ rdfs:comment "The source-language term a glossary entry is about."@en ;
454
+ rdfs:label "term"@en .
455
+
456
+ translation:termType
457
+ a owl:DatatypeProperty ;
458
+ rdfs:comment "How machine translation treats a glossary term: prefer, require, keep or forbid."@en ;
459
+ rdfs:label "term type"@en .
460
+
461
+ translation:text
462
+ a owl:DatatypeProperty ;
463
+ rdfs:comment "The translated text of a unit or revision."@en ;
464
+ rdfs:label "text"@en .
465
+
466
+ translation:textDirection
467
+ a owl:DatatypeProperty ;
468
+ rdfs:comment "Whether a language is written left-to-right or right-to-left."@en ;
469
+ rdfs:label "text direction"@en .
470
+
471
+ translation:updatedAt
472
+ a owl:DatatypeProperty ;
473
+ rdfs:comment "When a translation unit last changed."@en ;
474
+ rdfs:label "updated at"@en .
475
+
476
+ translation:updatedBy
477
+ a owl:ObjectProperty ;
478
+ rdfs:comment "The WebID that last changed a translation unit."@en ;
479
+ rdfs:label "updated by"@en .
480
+
481
+ translation:useInstead
482
+ a owl:DatatypeProperty ;
483
+ rdfs:comment "The replacement for a forbidden glossary term or spelling."@en ;
484
+ rdfs:label "use instead"@en .
485
+
486
+ translation:versionId
487
+ a owl:DatatypeProperty ;
488
+ rdfs:comment "The identifier of a translation key version."@en ;
489
+ rdfs:label "version id"@en .
@@ -1,3 +1,4 @@
1
+ import { sharedState } from '@_linked/core/utils/sharedState';
1
2
  import { canonicalLanguageTag } from './key-version.js';
2
3
  const EXCHANGE_STATES = new Set([
3
4
  'untranslated',
@@ -5,7 +6,11 @@ const EXCHANGE_STATES = new Set([
5
6
  'reviewed',
6
7
  'stale',
7
8
  ]);
8
- const adapters = new Map();
9
+ // On globalThis so an adapter the host registers on one copy of this module is found by
10
+ // another copy.
11
+ const { adapters } = sharedState('@_linked/translation', 'exchange', () => ({
12
+ adapters: new Map(),
13
+ }));
9
14
  function record(value, label) {
10
15
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
11
16
  throw new Error(`${label} must be an object.`);
@@ -205,7 +210,12 @@ export function isConfirmedBuildDeclaration(declaration) {
205
210
  declaration.authoritative !== false);
206
211
  }
207
212
  export function registerTranslationFormatAdapter(adapter) {
208
- if (adapters.has(adapter.format)) {
213
+ const registered = adapters.get(adapter.format);
214
+ // The registry is process-wide, so the same adapter registered again (by a second copy of
215
+ // the host module that registers it) is not a conflict; a different adapter for the format is.
216
+ if (registered === adapter)
217
+ return;
218
+ if (registered) {
209
219
  throw new Error(`Translation format adapter "${adapter.format}" is already registered.`);
210
220
  }
211
221
  adapters.set(adapter.format, adapter);
@@ -1 +1 @@
1
- {"version":3,"file":"exchange.js","sourceRoot":"","sources":["../../src/exchange.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MessageFormat } from './core/messages.js';\nimport { canonicalLanguageTag } from './key-version.js';\nimport type { TranslationState } from './records.js';\n\nexport type TranslationExchangeKind = 'ui' | 'semantic';\nexport type TranslationDeclarationKind =\n | TranslationExchangeKind\n | 'content';\nexport type TranslationDeclarationStatus = 'confirmed' | 'pending';\n\n/**\n * The build-facing subset of the later multi-source discovery contract.\n * Static extraction omits the optional fields and retains its existing\n * confirmed/simple behavior.\n */\nexport interface TranslationBuildDeclaration {\n key: string;\n sourceText: string;\n format?: MessageFormat;\n kind?: TranslationDeclarationKind;\n status?: TranslationDeclarationStatus;\n authoritative?: boolean;\n namespace?: string;\n file?: string;\n line?: number;\n}\n\nexport interface TranslationExchangeValue {\n text: string;\n state?: TranslationState;\n note?: string;\n}\n\nexport interface TranslationExchangeEntry {\n key: string;\n namespace?: string;\n kind: TranslationExchangeKind;\n sourceText: string;\n keyVersionId: string;\n sourceHash: string;\n contractHash: string;\n argumentSignature: string;\n description?: string;\n format: MessageFormat;\n translations: Record<string, TranslationExchangeValue>;\n}\n\nexport interface TranslationExchangeDocument {\n schemaVersion: 1;\n appId?: string;\n branchId?: string;\n sourceLanguage: string;\n targetLanguages: string[];\n exportedAt?: string;\n revisionWatermark?: string;\n contractSetHash?: string;\n entries: TranslationExchangeEntry[];\n}\n\nexport type ParsedTranslationExchangeEntry = Omit<\n TranslationExchangeEntry,\n | 'sourceText'\n | 'keyVersionId'\n | 'sourceHash'\n | 'contractHash'\n | 'argumentSignature'\n> & {\n sourceText?: string;\n keyVersionId?: string;\n sourceHash?: string;\n contractHash?: string;\n argumentSignature?: string;\n};\n\nexport interface ParsedTranslationExchangeDocument\n extends Omit<TranslationExchangeDocument, 'entries'> {\n entries: ParsedTranslationExchangeEntry[];\n}\n\nexport type TranslationFormat =\n | 'xliff-1.2'\n | 'xliff-2.0'\n | 'csv'\n | 'json';\n\nexport interface SerializedTranslationDocument {\n fileName: string;\n mediaType: string;\n body: Uint8Array;\n}\n\nexport interface TranslationFormatAdapter {\n readonly format: TranslationFormat;\n sniff(input: Uint8Array, fileName?: string): boolean;\n parse(\n input: Uint8Array,\n options?: unknown,\n ): Promise<ParsedTranslationExchangeDocument>;\n serialize(\n document: TranslationExchangeDocument,\n options?: unknown,\n ): Promise<SerializedTranslationDocument>;\n}\n\nexport type TranslationImportDisposition =\n | 'import'\n | 'carry-forward'\n | 'unverified-source'\n | 'conflict'\n | 'skip';\n\nexport interface TranslationImportDryRunEntry {\n key: string;\n language: string;\n disposition: TranslationImportDisposition;\n expectedKeyVersionId?: string;\n expectedUnitContentHash?: string;\n}\n\nexport interface TranslationImportDryRun {\n sessionId: string;\n documentHash: string;\n revisionWatermark: string;\n expiresAt: string;\n entries: TranslationImportDryRunEntry[];\n}\n\nconst EXCHANGE_STATES = new Set<TranslationState>([\n 'untranslated',\n 'machine',\n 'reviewed',\n 'stale',\n]);\nconst adapters = new Map<TranslationFormat, TranslationFormatAdapter>();\n\nfunction record(value: unknown, label: string): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`${label} must be an object.`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction requiredString(value: unknown, label: string): string {\n if (typeof value !== 'string' || !value.trim()) {\n throw new Error(`${label} is required.`);\n }\n return value.trim();\n}\n\nfunction optionalString(value: unknown, label: string): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'string' || !value.trim()) {\n throw new Error(`${label} must be a non-empty string when supplied.`);\n }\n return value.trim();\n}\n\nfunction messageFormat(value: unknown, label: string): MessageFormat {\n if (value !== 'simple' && value !== 'icu') {\n throw new Error(`${label} must be \"simple\" or \"icu\".`);\n }\n return value;\n}\n\nfunction exchangeKind(value: unknown, label: string): TranslationExchangeKind {\n if (value !== 'ui' && value !== 'semantic') {\n throw new Error(`${label} must be \"ui\" or \"semantic\".`);\n }\n return value;\n}\n\nfunction canonicalLanguages(value: unknown, label: string): string[] {\n if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);\n const canonical: string[] = [];\n const seen = new Set<string>();\n for (const item of value) {\n if (typeof item !== 'string') {\n throw new Error(`${label} must contain only BCP-47 strings.`);\n }\n const language = canonicalLanguageTag(item);\n if (!seen.has(language)) {\n seen.add(language);\n canonical.push(language);\n }\n }\n return canonical;\n}\n\nfunction normalizeTranslations(\n value: unknown,\n label: string,\n): Record<string, TranslationExchangeValue> {\n const input = record(value, label);\n const translations: Record<string, TranslationExchangeValue> = {};\n for (const [rawLanguage, rawValue] of Object.entries(input)) {\n const language = canonicalLanguageTag(rawLanguage);\n if (translations[language]) {\n throw new Error(\n `${label} contains duplicate canonical language \"${language}\".`,\n );\n }\n const item = record(rawValue, `${label}.${rawLanguage}`);\n if (typeof item.text !== 'string') {\n throw new Error(`${label}.${rawLanguage}.text must be a string.`);\n }\n const state = item.state;\n if (state !== undefined && !EXCHANGE_STATES.has(state as TranslationState)) {\n throw new Error(`${label}.${rawLanguage}.state is invalid.`);\n }\n translations[language] = {\n text: item.text,\n ...(state ? { state: state as TranslationState } : {}),\n ...(item.note === undefined\n ? {}\n : {\n note: requiredString(\n item.note,\n `${label}.${rawLanguage}.note`,\n ),\n }),\n };\n }\n return translations;\n}\n\nfunction normalizeEntry(\n value: unknown,\n index: number,\n): ParsedTranslationExchangeEntry {\n const item = record(value, `entries[${index}]`);\n const key = requiredString(item.key, `entries[${index}].key`);\n return {\n key,\n ...(item.namespace === undefined\n ? {}\n : {\n namespace: requiredString(\n item.namespace,\n `entries[${index}].namespace`,\n ),\n }),\n kind: exchangeKind(item.kind, `entries[${index}].kind`),\n format: messageFormat(item.format, `entries[${index}].format`),\n ...(item.sourceText === undefined\n ? {}\n : {\n sourceText:\n typeof item.sourceText === 'string'\n ? item.sourceText\n : requiredString(item.sourceText, `entries[${index}].sourceText`),\n }),\n ...(item.keyVersionId === undefined\n ? {}\n : {\n keyVersionId: requiredString(\n item.keyVersionId,\n `entries[${index}].keyVersionId`,\n ),\n }),\n ...(item.sourceHash === undefined\n ? {}\n : {\n sourceHash: requiredString(\n item.sourceHash,\n `entries[${index}].sourceHash`,\n ),\n }),\n ...(item.contractHash === undefined\n ? {}\n : {\n contractHash: requiredString(\n item.contractHash,\n `entries[${index}].contractHash`,\n ),\n }),\n ...(item.argumentSignature === undefined\n ? {}\n : {\n argumentSignature: requiredString(\n item.argumentSignature,\n `entries[${index}].argumentSignature`,\n ),\n }),\n ...(item.description === undefined\n ? {}\n : {\n description: requiredString(\n item.description,\n `entries[${index}].description`,\n ),\n }),\n translations: normalizeTranslations(\n item.translations,\n `entries[${index}].translations`,\n ),\n };\n}\n\n/**\n * Normalize adapter output without inventing evidence missing from a foreign\n * file. Import disposition handles those entries as unverified later.\n */\nexport function normalizeParsedTranslationExchangeDocument(\n value: unknown,\n): ParsedTranslationExchangeDocument {\n const input = record(value, 'Translation exchange document');\n if (input.schemaVersion !== 1) {\n throw new Error('Translation exchange schemaVersion must be 1.');\n }\n const sourceLanguage = canonicalLanguageTag(\n requiredString(input.sourceLanguage, 'sourceLanguage'),\n );\n const targetLanguages = canonicalLanguages(\n input.targetLanguages,\n 'targetLanguages',\n );\n if (!Array.isArray(input.entries)) {\n throw new Error('entries must be an array.');\n }\n const entries = input.entries.map(normalizeEntry);\n const identities = new Set<string>();\n for (const entry of entries) {\n for (const language of Object.keys(entry.translations)) {\n const identity = `${entry.namespace ?? ''}\\u0000${entry.key}\\u0000${language}`;\n if (identities.has(identity)) {\n throw new Error(\n `Duplicate translation entry for \"${entry.key}\" in ${language}.`,\n );\n }\n identities.add(identity);\n }\n }\n return {\n schemaVersion: 1,\n ...(input.appId === undefined\n ? {}\n : { appId: requiredString(input.appId, 'appId') }),\n ...(input.branchId === undefined\n ? {}\n : { branchId: requiredString(input.branchId, 'branchId') }),\n sourceLanguage,\n targetLanguages,\n ...(input.exportedAt === undefined\n ? {}\n : { exportedAt: requiredString(input.exportedAt, 'exportedAt') }),\n ...(input.revisionWatermark === undefined\n ? {}\n : {\n revisionWatermark: requiredString(\n input.revisionWatermark,\n 'revisionWatermark',\n ),\n }),\n ...(input.contractSetHash === undefined\n ? {}\n : {\n contractSetHash: requiredString(\n input.contractSetHash,\n 'contractSetHash',\n ),\n }),\n entries,\n };\n}\n\n/** Validate the stronger, version-evidenced document this package's own exports carry. */\nexport function validateTranslationExchangeDocument(\n value: unknown,\n): TranslationExchangeDocument {\n const parsed = normalizeParsedTranslationExchangeDocument(value);\n const entries = parsed.entries.map((entry, index): TranslationExchangeEntry => ({\n ...entry,\n sourceText:\n entry.sourceText ??\n requiredString(undefined, `entries[${index}].sourceText`),\n keyVersionId:\n entry.keyVersionId ??\n requiredString(undefined, `entries[${index}].keyVersionId`),\n sourceHash:\n entry.sourceHash ??\n requiredString(undefined, `entries[${index}].sourceHash`),\n contractHash:\n entry.contractHash ??\n requiredString(undefined, `entries[${index}].contractHash`),\n argumentSignature:\n entry.argumentSignature ??\n requiredString(undefined, `entries[${index}].argumentSignature`),\n }));\n return { ...parsed, entries };\n}\n\nexport function isConfirmedBuildDeclaration(\n declaration: TranslationBuildDeclaration,\n): boolean {\n return (\n (declaration.kind ?? 'ui') !== 'content' &&\n (declaration.status ?? 'confirmed') === 'confirmed' &&\n declaration.authoritative !== false\n );\n}\n\nexport function registerTranslationFormatAdapter(\n adapter: TranslationFormatAdapter,\n): void {\n if (adapters.has(adapter.format)) {\n throw new Error(\n `Translation format adapter \"${adapter.format}\" is already registered.`,\n );\n }\n adapters.set(adapter.format, adapter);\n}\n\nexport function getTranslationFormatAdapter(\n format: TranslationFormat,\n): TranslationFormatAdapter | undefined {\n return adapters.get(format);\n}\n\nexport function listTranslationFormatAdapters(): TranslationFormatAdapter[] {\n return [...adapters.values()].sort((left, right) =>\n left.format.localeCompare(right.format),\n );\n}\n\n/** Test/host reset hook; adapters are registered explicitly by the host. */\nexport function clearTranslationFormatAdapters(): void {\n adapters.clear();\n}\n"]}
1
+ {"version":3,"file":"exchange.js","sourceRoot":"","sources":["../../src/exchange.ts"],"names":[],"mappings":"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","sourcesContent":["import { sharedState } from '@_linked/core/utils/sharedState';\nimport type { MessageFormat } from './core/messages.js';\nimport { canonicalLanguageTag } from './key-version.js';\nimport type { TranslationState } from './records.js';\n\nexport type TranslationExchangeKind = 'ui' | 'semantic';\nexport type TranslationDeclarationKind =\n | TranslationExchangeKind\n | 'content';\nexport type TranslationDeclarationStatus = 'confirmed' | 'pending';\n\n/**\n * The build-facing subset of the later multi-source discovery contract.\n * Static extraction omits the optional fields and retains its existing\n * confirmed/simple behavior.\n */\nexport interface TranslationBuildDeclaration {\n key: string;\n sourceText: string;\n format?: MessageFormat;\n kind?: TranslationDeclarationKind;\n status?: TranslationDeclarationStatus;\n authoritative?: boolean;\n namespace?: string;\n file?: string;\n line?: number;\n}\n\nexport interface TranslationExchangeValue {\n text: string;\n state?: TranslationState;\n note?: string;\n}\n\nexport interface TranslationExchangeEntry {\n key: string;\n namespace?: string;\n kind: TranslationExchangeKind;\n sourceText: string;\n keyVersionId: string;\n sourceHash: string;\n contractHash: string;\n argumentSignature: string;\n description?: string;\n format: MessageFormat;\n translations: Record<string, TranslationExchangeValue>;\n}\n\nexport interface TranslationExchangeDocument {\n schemaVersion: 1;\n appId?: string;\n branchId?: string;\n sourceLanguage: string;\n targetLanguages: string[];\n exportedAt?: string;\n revisionWatermark?: string;\n contractSetHash?: string;\n entries: TranslationExchangeEntry[];\n}\n\nexport type ParsedTranslationExchangeEntry = Omit<\n TranslationExchangeEntry,\n | 'sourceText'\n | 'keyVersionId'\n | 'sourceHash'\n | 'contractHash'\n | 'argumentSignature'\n> & {\n sourceText?: string;\n keyVersionId?: string;\n sourceHash?: string;\n contractHash?: string;\n argumentSignature?: string;\n};\n\nexport interface ParsedTranslationExchangeDocument\n extends Omit<TranslationExchangeDocument, 'entries'> {\n entries: ParsedTranslationExchangeEntry[];\n}\n\nexport type TranslationFormat =\n | 'xliff-1.2'\n | 'xliff-2.0'\n | 'csv'\n | 'json';\n\nexport interface SerializedTranslationDocument {\n fileName: string;\n mediaType: string;\n body: Uint8Array;\n}\n\nexport interface TranslationFormatAdapter {\n readonly format: TranslationFormat;\n sniff(input: Uint8Array, fileName?: string): boolean;\n parse(\n input: Uint8Array,\n options?: unknown,\n ): Promise<ParsedTranslationExchangeDocument>;\n serialize(\n document: TranslationExchangeDocument,\n options?: unknown,\n ): Promise<SerializedTranslationDocument>;\n}\n\nexport type TranslationImportDisposition =\n | 'import'\n | 'carry-forward'\n | 'unverified-source'\n | 'conflict'\n | 'skip';\n\nexport interface TranslationImportDryRunEntry {\n key: string;\n language: string;\n disposition: TranslationImportDisposition;\n expectedKeyVersionId?: string;\n expectedUnitContentHash?: string;\n}\n\nexport interface TranslationImportDryRun {\n sessionId: string;\n documentHash: string;\n revisionWatermark: string;\n expiresAt: string;\n entries: TranslationImportDryRunEntry[];\n}\n\nconst EXCHANGE_STATES = new Set<TranslationState>([\n 'untranslated',\n 'machine',\n 'reviewed',\n 'stale',\n]);\n// On globalThis so an adapter the host registers on one copy of this module is found by\n// another copy.\nconst { adapters } = sharedState('@_linked/translation', 'exchange', () => ({\n adapters: new Map<TranslationFormat, TranslationFormatAdapter>(),\n}));\n\nfunction record(value: unknown, label: string): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`${label} must be an object.`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction requiredString(value: unknown, label: string): string {\n if (typeof value !== 'string' || !value.trim()) {\n throw new Error(`${label} is required.`);\n }\n return value.trim();\n}\n\nfunction optionalString(value: unknown, label: string): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'string' || !value.trim()) {\n throw new Error(`${label} must be a non-empty string when supplied.`);\n }\n return value.trim();\n}\n\nfunction messageFormat(value: unknown, label: string): MessageFormat {\n if (value !== 'simple' && value !== 'icu') {\n throw new Error(`${label} must be \"simple\" or \"icu\".`);\n }\n return value;\n}\n\nfunction exchangeKind(value: unknown, label: string): TranslationExchangeKind {\n if (value !== 'ui' && value !== 'semantic') {\n throw new Error(`${label} must be \"ui\" or \"semantic\".`);\n }\n return value;\n}\n\nfunction canonicalLanguages(value: unknown, label: string): string[] {\n if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);\n const canonical: string[] = [];\n const seen = new Set<string>();\n for (const item of value) {\n if (typeof item !== 'string') {\n throw new Error(`${label} must contain only BCP-47 strings.`);\n }\n const language = canonicalLanguageTag(item);\n if (!seen.has(language)) {\n seen.add(language);\n canonical.push(language);\n }\n }\n return canonical;\n}\n\nfunction normalizeTranslations(\n value: unknown,\n label: string,\n): Record<string, TranslationExchangeValue> {\n const input = record(value, label);\n const translations: Record<string, TranslationExchangeValue> = {};\n for (const [rawLanguage, rawValue] of Object.entries(input)) {\n const language = canonicalLanguageTag(rawLanguage);\n if (translations[language]) {\n throw new Error(\n `${label} contains duplicate canonical language \"${language}\".`,\n );\n }\n const item = record(rawValue, `${label}.${rawLanguage}`);\n if (typeof item.text !== 'string') {\n throw new Error(`${label}.${rawLanguage}.text must be a string.`);\n }\n const state = item.state;\n if (state !== undefined && !EXCHANGE_STATES.has(state as TranslationState)) {\n throw new Error(`${label}.${rawLanguage}.state is invalid.`);\n }\n translations[language] = {\n text: item.text,\n ...(state ? { state: state as TranslationState } : {}),\n ...(item.note === undefined\n ? {}\n : {\n note: requiredString(\n item.note,\n `${label}.${rawLanguage}.note`,\n ),\n }),\n };\n }\n return translations;\n}\n\nfunction normalizeEntry(\n value: unknown,\n index: number,\n): ParsedTranslationExchangeEntry {\n const item = record(value, `entries[${index}]`);\n const key = requiredString(item.key, `entries[${index}].key`);\n return {\n key,\n ...(item.namespace === undefined\n ? {}\n : {\n namespace: requiredString(\n item.namespace,\n `entries[${index}].namespace`,\n ),\n }),\n kind: exchangeKind(item.kind, `entries[${index}].kind`),\n format: messageFormat(item.format, `entries[${index}].format`),\n ...(item.sourceText === undefined\n ? {}\n : {\n sourceText:\n typeof item.sourceText === 'string'\n ? item.sourceText\n : requiredString(item.sourceText, `entries[${index}].sourceText`),\n }),\n ...(item.keyVersionId === undefined\n ? {}\n : {\n keyVersionId: requiredString(\n item.keyVersionId,\n `entries[${index}].keyVersionId`,\n ),\n }),\n ...(item.sourceHash === undefined\n ? {}\n : {\n sourceHash: requiredString(\n item.sourceHash,\n `entries[${index}].sourceHash`,\n ),\n }),\n ...(item.contractHash === undefined\n ? {}\n : {\n contractHash: requiredString(\n item.contractHash,\n `entries[${index}].contractHash`,\n ),\n }),\n ...(item.argumentSignature === undefined\n ? {}\n : {\n argumentSignature: requiredString(\n item.argumentSignature,\n `entries[${index}].argumentSignature`,\n ),\n }),\n ...(item.description === undefined\n ? {}\n : {\n description: requiredString(\n item.description,\n `entries[${index}].description`,\n ),\n }),\n translations: normalizeTranslations(\n item.translations,\n `entries[${index}].translations`,\n ),\n };\n}\n\n/**\n * Normalize adapter output without inventing evidence missing from a foreign\n * file. Import disposition handles those entries as unverified later.\n */\nexport function normalizeParsedTranslationExchangeDocument(\n value: unknown,\n): ParsedTranslationExchangeDocument {\n const input = record(value, 'Translation exchange document');\n if (input.schemaVersion !== 1) {\n throw new Error('Translation exchange schemaVersion must be 1.');\n }\n const sourceLanguage = canonicalLanguageTag(\n requiredString(input.sourceLanguage, 'sourceLanguage'),\n );\n const targetLanguages = canonicalLanguages(\n input.targetLanguages,\n 'targetLanguages',\n );\n if (!Array.isArray(input.entries)) {\n throw new Error('entries must be an array.');\n }\n const entries = input.entries.map(normalizeEntry);\n const identities = new Set<string>();\n for (const entry of entries) {\n for (const language of Object.keys(entry.translations)) {\n const identity = `${entry.namespace ?? ''}\\u0000${entry.key}\\u0000${language}`;\n if (identities.has(identity)) {\n throw new Error(\n `Duplicate translation entry for \"${entry.key}\" in ${language}.`,\n );\n }\n identities.add(identity);\n }\n }\n return {\n schemaVersion: 1,\n ...(input.appId === undefined\n ? {}\n : { appId: requiredString(input.appId, 'appId') }),\n ...(input.branchId === undefined\n ? {}\n : { branchId: requiredString(input.branchId, 'branchId') }),\n sourceLanguage,\n targetLanguages,\n ...(input.exportedAt === undefined\n ? {}\n : { exportedAt: requiredString(input.exportedAt, 'exportedAt') }),\n ...(input.revisionWatermark === undefined\n ? {}\n : {\n revisionWatermark: requiredString(\n input.revisionWatermark,\n 'revisionWatermark',\n ),\n }),\n ...(input.contractSetHash === undefined\n ? {}\n : {\n contractSetHash: requiredString(\n input.contractSetHash,\n 'contractSetHash',\n ),\n }),\n entries,\n };\n}\n\n/** Validate the stronger, version-evidenced document this package's own exports carry. */\nexport function validateTranslationExchangeDocument(\n value: unknown,\n): TranslationExchangeDocument {\n const parsed = normalizeParsedTranslationExchangeDocument(value);\n const entries = parsed.entries.map((entry, index): TranslationExchangeEntry => ({\n ...entry,\n sourceText:\n entry.sourceText ??\n requiredString(undefined, `entries[${index}].sourceText`),\n keyVersionId:\n entry.keyVersionId ??\n requiredString(undefined, `entries[${index}].keyVersionId`),\n sourceHash:\n entry.sourceHash ??\n requiredString(undefined, `entries[${index}].sourceHash`),\n contractHash:\n entry.contractHash ??\n requiredString(undefined, `entries[${index}].contractHash`),\n argumentSignature:\n entry.argumentSignature ??\n requiredString(undefined, `entries[${index}].argumentSignature`),\n }));\n return { ...parsed, entries };\n}\n\nexport function isConfirmedBuildDeclaration(\n declaration: TranslationBuildDeclaration,\n): boolean {\n return (\n (declaration.kind ?? 'ui') !== 'content' &&\n (declaration.status ?? 'confirmed') === 'confirmed' &&\n declaration.authoritative !== false\n );\n}\n\nexport function registerTranslationFormatAdapter(\n adapter: TranslationFormatAdapter,\n): void {\n const registered = adapters.get(adapter.format);\n // The registry is process-wide, so the same adapter registered again (by a second copy of\n // the host module that registers it) is not a conflict; a different adapter for the format is.\n if (registered === adapter) return;\n if (registered) {\n throw new Error(\n `Translation format adapter \"${adapter.format}\" is already registered.`,\n );\n }\n adapters.set(adapter.format, adapter);\n}\n\nexport function getTranslationFormatAdapter(\n format: TranslationFormat,\n): TranslationFormatAdapter | undefined {\n return adapters.get(format);\n}\n\nexport function listTranslationFormatAdapters(): TranslationFormatAdapter[] {\n return [...adapters.values()].sort((left, right) =>\n left.format.localeCompare(right.format),\n );\n}\n\n/** Test/host reset hook; adapters are registered explicitly by the host. */\nexport function clearTranslationFormatAdapters(): void {\n adapters.clear();\n}\n"]}