@_linked/translation 0.8.0 → 0.8.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/lib/esm/authorization.js +8 -2
- package/lib/esm/authorization.js.map +1 -1
- package/lib/esm/exchange.js +12 -2
- package/lib/esm/exchange.js.map +1 -1
- package/lib/esm/mt.js +6 -1
- package/lib/esm/mt.js.map +1 -1
- package/lib/esm/ontologies/translation.d.ts +192 -192
- package/lib/esm/package.d.ts +3 -3
- package/lib/esm/react.js +6 -1
- package/lib/esm/react.js.map +1 -1
- package/lib/esm/shapes/GlossaryTerm.d.ts +1 -1
- package/lib/esm/shapes/TranslationInventoryOrigin.d.ts +1 -1
- package/lib/esm/shapes/TranslationKey.d.ts +1 -1
- package/lib/esm/shapes/TranslationKeyVersion.d.ts +1 -1
- package/lib/esm/shapes/TranslationLanguage.d.ts +1 -1
- package/lib/esm/shapes/TranslationProvider.js +19 -19
- package/lib/esm/shapes/TranslationProvider.js.map +1 -1
- package/lib/esm/shapes/TranslationRelease.d.ts +1 -1
- package/lib/esm/shapes/TranslationRevision.d.ts +1 -1
- package/lib/esm/shapes/TranslationUnit.d.ts +1 -1
- package/package.json +7 -7
- package/src/authorization.ts +9 -2
- package/src/exchange.ts +11 -2
- package/src/mt.ts +6 -1
- package/src/react.tsx +6 -1
- package/src/shapes/TranslationProvider.ts +19 -19
|
@@ -11,7 +11,7 @@ import { TranslationKeyVersion } from './TranslationKeyVersion.js';
|
|
|
11
11
|
* (`status`, `decidedAt`, `decidedBy`).
|
|
12
12
|
*/
|
|
13
13
|
export declare class TranslationRevision extends Shape {
|
|
14
|
-
static targetClass: import("@_linked/core/utils/NodeReference").NodeReferenceValue;
|
|
14
|
+
static targetClass: import("@_linked/core/utils/NodeReference.js").NodeReferenceValue;
|
|
15
15
|
/** IRI of the owning Application (same app-scoping law as Key/Unit). */
|
|
16
16
|
get ofApplication(): string;
|
|
17
17
|
/** The key, NOT the unit — revisions can exist before any unit does. */
|
|
@@ -8,7 +8,7 @@ import { TranslationKeyVersion } from './TranslationKeyVersion.js';
|
|
|
8
8
|
* provenance — that a bare literal cannot hold.
|
|
9
9
|
*/
|
|
10
10
|
export declare class TranslationUnit extends Shape {
|
|
11
|
-
static targetClass: import("@_linked/core/utils/NodeReference").NodeReferenceValue;
|
|
11
|
+
static targetClass: import("@_linked/core/utils/NodeReference.js").NodeReferenceValue;
|
|
12
12
|
/** IRI of the owning Application (explicitly app-scoped per Plan 014 AD-I). */
|
|
13
13
|
get ofApplication(): string;
|
|
14
14
|
get ofKey(): TranslationKey;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@_linked/translation",
|
|
3
3
|
"description": "LINKED translation management — graph-native i18n keys + units, Tolgee-compatible client, CDN delivery.",
|
|
4
|
-
"version": "0.8.
|
|
5
|
-
"packageManager": "npm@12.
|
|
4
|
+
"version": "0.8.2",
|
|
5
|
+
"packageManager": "npm@12.2.0",
|
|
6
6
|
"linkedPackage": true,
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "lib/esm/index.js",
|
|
@@ -90,11 +90,11 @@
|
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
92
|
"dependencies": {
|
|
93
|
-
"@_linked/core": "^2.
|
|
94
|
-
"@_linked/server-utils": "^1.
|
|
95
|
-
"@formatjs/icu-messageformat-parser": "3.5.
|
|
93
|
+
"@_linked/core": "^2.36.1",
|
|
94
|
+
"@_linked/server-utils": "^1.14.0",
|
|
95
|
+
"@formatjs/icu-messageformat-parser": "3.5.21",
|
|
96
96
|
"fflate": "^0.8.2",
|
|
97
|
-
"intl-messageformat": "12.1.
|
|
97
|
+
"intl-messageformat": "12.1.3"
|
|
98
98
|
},
|
|
99
99
|
"peerDependencies": {
|
|
100
100
|
"react": "*",
|
|
@@ -121,6 +121,6 @@
|
|
|
121
121
|
"react": "^19.3.0",
|
|
122
122
|
"react-dom": "^19.3.0",
|
|
123
123
|
"typescript": "5.9.3",
|
|
124
|
-
"vitest": "^
|
|
124
|
+
"vitest": "^5.0.0"
|
|
125
125
|
}
|
|
126
126
|
}
|
package/src/authorization.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { sharedState } from '@_linked/core/utils/sharedState';
|
|
2
|
+
|
|
1
3
|
export type TranslationAuthoringAction =
|
|
2
4
|
| 'manage'
|
|
3
5
|
| 'review'
|
|
@@ -16,7 +18,11 @@ export type TranslationAuthorizationResolver = (
|
|
|
16
18
|
request: TranslationAuthorizationRequest,
|
|
17
19
|
) => Promise<boolean>;
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
// On globalThis so a second copy of this module sees the resolver the host configured on the
|
|
22
|
+
// first; a copy that cannot see it would deny every author.
|
|
23
|
+
const state = sharedState('@_linked/translation', 'authorization', () => ({
|
|
24
|
+
resolver: undefined as TranslationAuthorizationResolver | undefined,
|
|
25
|
+
}));
|
|
20
26
|
|
|
21
27
|
/**
|
|
22
28
|
* Host hook: the host decides who may read and author an app's translations
|
|
@@ -29,7 +35,7 @@ let resolver: TranslationAuthorizationResolver | undefined;
|
|
|
29
35
|
export function configureTranslationAuthorization(
|
|
30
36
|
next?: TranslationAuthorizationResolver,
|
|
31
37
|
): void {
|
|
32
|
-
resolver = next;
|
|
38
|
+
state.resolver = next;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
/**
|
|
@@ -39,6 +45,7 @@ export function configureTranslationAuthorization(
|
|
|
39
45
|
export async function canAuthorTranslation(
|
|
40
46
|
request: TranslationAuthorizationRequest,
|
|
41
47
|
): Promise<boolean> {
|
|
48
|
+
const resolver = state.resolver;
|
|
42
49
|
if (!resolver) return false;
|
|
43
50
|
try {
|
|
44
51
|
return (await resolver(request)) === true;
|
package/src/exchange.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sharedState } from '@_linked/core/utils/sharedState';
|
|
1
2
|
import type { MessageFormat } from './core/messages.js';
|
|
2
3
|
import { canonicalLanguageTag } from './key-version.js';
|
|
3
4
|
import type { TranslationState } from './records.js';
|
|
@@ -131,7 +132,11 @@ const EXCHANGE_STATES = new Set<TranslationState>([
|
|
|
131
132
|
'reviewed',
|
|
132
133
|
'stale',
|
|
133
134
|
]);
|
|
134
|
-
|
|
135
|
+
// On globalThis so an adapter the host registers on one copy of this module is found by
|
|
136
|
+
// another copy.
|
|
137
|
+
const { adapters } = sharedState('@_linked/translation', 'exchange', () => ({
|
|
138
|
+
adapters: new Map<TranslationFormat, TranslationFormatAdapter>(),
|
|
139
|
+
}));
|
|
135
140
|
|
|
136
141
|
function record(value: unknown, label: string): Record<string, unknown> {
|
|
137
142
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
@@ -402,7 +407,11 @@ export function isConfirmedBuildDeclaration(
|
|
|
402
407
|
export function registerTranslationFormatAdapter(
|
|
403
408
|
adapter: TranslationFormatAdapter,
|
|
404
409
|
): void {
|
|
405
|
-
|
|
410
|
+
const registered = adapters.get(adapter.format);
|
|
411
|
+
// The registry is process-wide, so the same adapter registered again (by a second copy of
|
|
412
|
+
// the host module that registers it) is not a conflict; a different adapter for the format is.
|
|
413
|
+
if (registered === adapter) return;
|
|
414
|
+
if (registered) {
|
|
406
415
|
throw new Error(
|
|
407
416
|
`Translation format adapter "${adapter.format}" is already registered.`,
|
|
408
417
|
);
|
package/src/mt.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* SDK imports — concrete providers live with the host application and are
|
|
9
9
|
* registered at boot.
|
|
10
10
|
*/
|
|
11
|
+
import { sharedState } from '@_linked/core/utils/sharedState';
|
|
11
12
|
import {
|
|
12
13
|
analyzeMessage,
|
|
13
14
|
compareMessageArguments,
|
|
@@ -72,7 +73,11 @@ export interface TranslationMtProvider {
|
|
|
72
73
|
translateBatch(request: MtBatchRequest): Promise<MtBatchResult[]>;
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
// On globalThis so a provider the host registers on one copy of this module is found by
|
|
77
|
+
// another copy.
|
|
78
|
+
const { providers } = sharedState('@_linked/translation', 'mt', () => ({
|
|
79
|
+
providers: new Map<string, TranslationMtProvider>(),
|
|
80
|
+
}));
|
|
76
81
|
|
|
77
82
|
export function registerMtProvider(provider: TranslationMtProvider): void {
|
|
78
83
|
providers.set(provider.key, provider);
|
package/src/react.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sharedState } from '@_linked/core/utils/sharedState';
|
|
1
2
|
import {
|
|
2
3
|
languageFallbacks,
|
|
3
4
|
type TranslationLanguageDefinition,
|
|
@@ -100,7 +101,11 @@ interface TranslationContextValue {
|
|
|
100
101
|
dir: 'ltr' | 'rtl';
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
|
|
104
|
+
// The context object is kept on globalThis: a context created per copy of this module would
|
|
105
|
+
// leave a consumer in one copy blind to a provider from the other (it gets the null default).
|
|
106
|
+
const { TranslationContext } = sharedState('@_linked/translation', 'react.contexts', () => ({
|
|
107
|
+
TranslationContext: createContext<TranslationContextValue | null>(null),
|
|
108
|
+
}));
|
|
104
109
|
|
|
105
110
|
export interface TranslationProviderProps {
|
|
106
111
|
children: React.ReactNode;
|
|
@@ -1602,21 +1602,21 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1602
1602
|
}
|
|
1603
1603
|
}
|
|
1604
1604
|
|
|
1605
|
-
@callable('
|
|
1605
|
+
@callable('signed-in')
|
|
1606
1606
|
async listKeys(data: { appId: string }) {
|
|
1607
1607
|
const actor = this.requireActor();
|
|
1608
1608
|
await this.requireAccess(actor, { appId: data?.appId }, 'read');
|
|
1609
1609
|
return listTranslationKeys(data);
|
|
1610
1610
|
}
|
|
1611
1611
|
|
|
1612
|
-
@callable('
|
|
1612
|
+
@callable('signed-in')
|
|
1613
1613
|
async listEntries(data: { appId: string }): Promise<TranslationEntryRecord[]> {
|
|
1614
1614
|
const actor = this.requireActor();
|
|
1615
1615
|
await this.requireAccess(actor, { appId: data?.appId }, 'read');
|
|
1616
1616
|
return listTranslationEntries(data);
|
|
1617
1617
|
}
|
|
1618
1618
|
|
|
1619
|
-
@callable('
|
|
1619
|
+
@callable('signed-in')
|
|
1620
1620
|
async listKeyVersions(data: {
|
|
1621
1621
|
appId: string;
|
|
1622
1622
|
key?: string;
|
|
@@ -1626,7 +1626,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1626
1626
|
return listTranslationKeyVersions(data);
|
|
1627
1627
|
}
|
|
1628
1628
|
|
|
1629
|
-
@callable('
|
|
1629
|
+
@callable('signed-in')
|
|
1630
1630
|
async listMemoryMatches(data: {
|
|
1631
1631
|
appId: string;
|
|
1632
1632
|
key: string;
|
|
@@ -1637,7 +1637,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1637
1637
|
return listTranslationMemoryMatches(data);
|
|
1638
1638
|
}
|
|
1639
1639
|
|
|
1640
|
-
@callable('
|
|
1640
|
+
@callable('signed-in')
|
|
1641
1641
|
async proposeRevision(data: {
|
|
1642
1642
|
appId: string;
|
|
1643
1643
|
key: string;
|
|
@@ -1661,7 +1661,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1661
1661
|
});
|
|
1662
1662
|
}
|
|
1663
1663
|
|
|
1664
|
-
@callable('
|
|
1664
|
+
@callable('signed-in')
|
|
1665
1665
|
async listProposals(data: {
|
|
1666
1666
|
appId: string;
|
|
1667
1667
|
language?: string;
|
|
@@ -1682,7 +1682,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1682
1682
|
return proposals.filter((_, index) => allowed[index]);
|
|
1683
1683
|
}
|
|
1684
1684
|
|
|
1685
|
-
@callable('
|
|
1685
|
+
@callable('signed-in')
|
|
1686
1686
|
async decideProposal(data: {
|
|
1687
1687
|
appId: string;
|
|
1688
1688
|
revisionId: string;
|
|
@@ -1706,7 +1706,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1706
1706
|
return decideTranslationProposal(data, actor);
|
|
1707
1707
|
}
|
|
1708
1708
|
|
|
1709
|
-
@callable('
|
|
1709
|
+
@callable('signed-in')
|
|
1710
1710
|
async listMemory(data: {
|
|
1711
1711
|
appId: string;
|
|
1712
1712
|
}): Promise<TranslationMemoryRecord[]> {
|
|
@@ -1715,7 +1715,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1715
1715
|
return listTranslationMemory(data);
|
|
1716
1716
|
}
|
|
1717
1717
|
|
|
1718
|
-
@callable('
|
|
1718
|
+
@callable('signed-in')
|
|
1719
1719
|
async pretranslateMemory(data: {
|
|
1720
1720
|
appId: string;
|
|
1721
1721
|
language: string;
|
|
@@ -1726,7 +1726,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1726
1726
|
return pretranslateFromMemory(data, { author: actor });
|
|
1727
1727
|
}
|
|
1728
1728
|
|
|
1729
|
-
@callable('
|
|
1729
|
+
@callable('signed-in')
|
|
1730
1730
|
async applyMemoryMatch(data: {
|
|
1731
1731
|
appId: string;
|
|
1732
1732
|
key: string;
|
|
@@ -1738,7 +1738,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1738
1738
|
return applyTranslationMemoryMatch(data, { author: actor });
|
|
1739
1739
|
}
|
|
1740
1740
|
|
|
1741
|
-
@callable('
|
|
1741
|
+
@callable('signed-in')
|
|
1742
1742
|
async listReleases(data: {
|
|
1743
1743
|
appId: string;
|
|
1744
1744
|
}): Promise<TranslationReleaseRecord[]> {
|
|
@@ -1747,7 +1747,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1747
1747
|
return listTranslationReleases(data);
|
|
1748
1748
|
}
|
|
1749
1749
|
|
|
1750
|
-
@callable('
|
|
1750
|
+
@callable('signed-in')
|
|
1751
1751
|
async createRelease(
|
|
1752
1752
|
data: Omit<TranslationReleaseRecord, 'id' | 'createdAt'> & {
|
|
1753
1753
|
createdAt?: string;
|
|
@@ -1758,7 +1758,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1758
1758
|
return createTranslationRelease(data);
|
|
1759
1759
|
}
|
|
1760
1760
|
|
|
1761
|
-
@callable('
|
|
1761
|
+
@callable('signed-in')
|
|
1762
1762
|
async advanceReleaseHotfix(data: {
|
|
1763
1763
|
id: string;
|
|
1764
1764
|
expectedSequence: number;
|
|
@@ -1772,14 +1772,14 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1772
1772
|
return advanceTranslationReleaseHotfix({ ...data, appId });
|
|
1773
1773
|
}
|
|
1774
1774
|
|
|
1775
|
-
@callable('
|
|
1775
|
+
@callable('signed-in')
|
|
1776
1776
|
async upsertKey(data: TranslationKeyInput): Promise<{ id: string }> {
|
|
1777
1777
|
const actor = this.requireActor();
|
|
1778
1778
|
await this.requireAccess(actor, data, 'manage');
|
|
1779
1779
|
return upsertTranslationKey(data, { createdBy: actor });
|
|
1780
1780
|
}
|
|
1781
1781
|
|
|
1782
|
-
@callable('
|
|
1782
|
+
@callable('signed-in')
|
|
1783
1783
|
async upsertUnit(data: TranslationUnitInput): Promise<{ id: string }> {
|
|
1784
1784
|
const actor = this.requireActor();
|
|
1785
1785
|
await this.requireAccess(actor, data, 'review');
|
|
@@ -1795,14 +1795,14 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1795
1795
|
* anchors the IRIs. Uniqueness = (term, language) — upsert is
|
|
1796
1796
|
* delete-then-create on that identity.
|
|
1797
1797
|
*/
|
|
1798
|
-
@callable('
|
|
1798
|
+
@callable('signed-in')
|
|
1799
1799
|
async listGlossary(data: { appId: string }): Promise<GlossaryTermRecord[]> {
|
|
1800
1800
|
const actor = this.requireActor();
|
|
1801
1801
|
await this.requireAccess(actor, { appId: data?.appId }, 'read');
|
|
1802
1802
|
return listGlossaryTerms(data);
|
|
1803
1803
|
}
|
|
1804
1804
|
|
|
1805
|
-
@callable('
|
|
1805
|
+
@callable('signed-in')
|
|
1806
1806
|
async upsertGlossaryTerm(data: {
|
|
1807
1807
|
appId: string;
|
|
1808
1808
|
term: string;
|
|
@@ -1845,7 +1845,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1845
1845
|
return { id };
|
|
1846
1846
|
}
|
|
1847
1847
|
|
|
1848
|
-
@callable('
|
|
1848
|
+
@callable('signed-in')
|
|
1849
1849
|
async deleteGlossaryTerm(data: { appId: string; id: string }): Promise<{ deleted: boolean }> {
|
|
1850
1850
|
const actor = this.requireActor();
|
|
1851
1851
|
await this.requireAccess(actor, data, 'manage');
|
|
@@ -1860,7 +1860,7 @@ export class TranslationProvider extends ShapeProvider {
|
|
|
1860
1860
|
}
|
|
1861
1861
|
|
|
1862
1862
|
/** History for one (key, language) cell — newest first (Plan 017 AD-P). */
|
|
1863
|
-
@callable('
|
|
1863
|
+
@callable('signed-in')
|
|
1864
1864
|
async listRevisions(data: {
|
|
1865
1865
|
appId: string;
|
|
1866
1866
|
key: string;
|