@datamoc/mw_games 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/i18n/EditSession.d.ts +101 -0
- package/dist/i18n/EditSession.d.ts.map +1 -0
- package/dist/i18n/EditSession.js +150 -0
- package/dist/i18n/EditSession.js.map +1 -0
- package/dist/i18n/Fluent.js +9 -1
- package/dist/i18n/Fluent.js.map +1 -1
- package/dist/i18n/FormatSpec.d.ts +75 -0
- package/dist/i18n/FormatSpec.d.ts.map +1 -0
- package/dist/i18n/FormatSpec.js +303 -0
- package/dist/i18n/FormatSpec.js.map +1 -0
- package/dist/i18n/SemanticMessage.d.ts +15 -2
- package/dist/i18n/SemanticMessage.d.ts.map +1 -1
- package/dist/i18n/SemanticMessage.js +16 -2
- package/dist/i18n/SemanticMessage.js.map +1 -1
- package/dist/i18n/SoundMarkers.d.ts +38 -0
- package/dist/i18n/SoundMarkers.d.ts.map +1 -0
- package/dist/i18n/SoundMarkers.js +48 -0
- package/dist/i18n/SoundMarkers.js.map +1 -0
- package/dist/i18n/Validate.d.ts +26 -0
- package/dist/i18n/Validate.d.ts.map +1 -1
- package/dist/i18n/Validate.js +33 -0
- package/dist/i18n/Validate.js.map +1 -1
- package/dist/i18n/index.d.ts +34 -2
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +80 -10
- package/dist/i18n/index.js.map +1 -1
- package/dist/mw_games.global.js +42 -35
- package/dist/two-d/render/AnimatedSprite.d.ts +4 -0
- package/dist/two-d/render/AnimatedSprite.d.ts.map +1 -1
- package/dist/two-d/render/AnimatedSprite.js +4 -0
- package/dist/two-d/render/AnimatedSprite.js.map +1 -1
- package/dist/two-d/render/LayeredSprite.d.ts +8 -0
- package/dist/two-d/render/LayeredSprite.d.ts.map +1 -1
- package/dist/two-d/render/LayeredSprite.js +8 -0
- package/dist/two-d/render/LayeredSprite.js.map +1 -1
- package/dist/two-d/render/TileMap.d.ts +7 -0
- package/dist/two-d/render/TileMap.d.ts.map +1 -1
- package/dist/two-d/render/TileMap.js +7 -0
- package/dist/two-d/render/TileMap.js.map +1 -1
- package/dist/two-d/render/TintedSprite.d.ts +12 -0
- package/dist/two-d/render/TintedSprite.d.ts.map +1 -1
- package/dist/two-d/render/TintedSprite.js +12 -0
- package/dist/two-d/render/TintedSprite.js.map +1 -1
- package/dist/two-d/ui/Label.d.ts +14 -0
- package/dist/two-d/ui/Label.d.ts.map +1 -1
- package/dist/two-d/ui/Label.js +40 -0
- package/dist/two-d/ui/Label.js.map +1 -1
- package/dist/two-d/ui/MessageBox.d.ts +10 -2
- package/dist/two-d/ui/MessageBox.d.ts.map +1 -1
- package/dist/two-d/ui/MessageBox.js +30 -9
- package/dist/two-d/ui/MessageBox.js.map +1 -1
- package/dist/two-d/ui/NinePatch.d.ts +2 -0
- package/dist/two-d/ui/NinePatch.d.ts.map +1 -1
- package/dist/two-d/ui/NinePatch.js +2 -0
- package/dist/two-d/ui/NinePatch.js.map +1 -1
- package/dist/two-d/ui/RichLabel.d.ts +56 -0
- package/dist/two-d/ui/RichLabel.d.ts.map +1 -0
- package/dist/two-d/ui/RichLabel.js +135 -0
- package/dist/two-d/ui/RichLabel.js.map +1 -0
- package/dist/two-d/ui/index.d.ts +6 -0
- package/dist/two-d/ui/index.d.ts.map +1 -1
- package/dist/two-d/ui/index.js +3 -0
- package/dist/two-d/ui/index.js.map +1 -1
- package/dist/two-d/ui/markdown.d.ts +55 -0
- package/dist/two-d/ui/markdown.d.ts.map +1 -0
- package/dist/two-d/ui/markdown.js +145 -0
- package/dist/two-d/ui/markdown.js.map +1 -0
- package/dist/two-d/ui/reveal.d.ts +41 -0
- package/dist/two-d/ui/reveal.d.ts.map +1 -0
- package/dist/two-d/ui/reveal.js +22 -0
- package/dist/two-d/ui/reveal.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ of their own; improvements to `mwg`'s own files are shared back.
|
|
|
11
11
|
**[Live examples and API docs](https://datamoc.github.io/mwg/)**: every example below,
|
|
12
12
|
playable in the browser with no download, plus the generated API reference.
|
|
13
13
|
|
|
14
|
-
> **Status: early release (v0.4
|
|
14
|
+
> **Status: early release (v0.5.4).** Every module in the shared floor below, plus optional
|
|
15
15
|
> 3D, mobile (Capacitor) and desktop (WebView2) packaging, is built and tested - see
|
|
16
16
|
> [ROADMAP.md](ROADMAP.md) for the full, numbered history.
|
|
17
17
|
>
|
|
@@ -119,7 +119,9 @@ them rather than pretending otherwise:
|
|
|
119
119
|
| cross-origin images | an `<img>` from a sibling file taints a WebGL texture in Chrome | assets travel as `data:` URIs inside those scripts |
|
|
120
120
|
|
|
121
121
|
The practical effect: resources are **compiled at build time**, so at runtime every lookup
|
|
122
|
-
is synchronous. Game code never awaits an asset.
|
|
122
|
+
is synchronous. Game code never awaits an asset. The developer-side scripts behind this
|
|
123
|
+
and the other workflows (translation editing, benchmarks, packaging) are listed in
|
|
124
|
+
[tools.md](tools.md).
|
|
123
125
|
|
|
124
126
|
## Capability spec
|
|
125
127
|
|
|
@@ -144,15 +146,16 @@ only some do.
|
|
|
144
146
|
| | camera with follow, pan, shake and zoom | `mwg/two-d/render` |
|
|
145
147
|
| | particle effects | `mwg/two-d/render` |
|
|
146
148
|
| | screen transitions: fade, flash, tint | `mwg/two-d/render` |
|
|
147
|
-
| **ui** | windows, lists, tooltips | `mwg/two-d/ui` |
|
|
149
|
+
| **ui** | windows, lists, tooltips, markdown labels | `mwg/two-d/ui` |
|
|
148
150
|
| | text and fonts, including non-latin fallback | PixiJS |
|
|
149
151
|
| | pointer, keyboard and gamepad input with rebinding | `mwg/core` |
|
|
150
152
|
| **text** | message tables per language, compiled at build time | `mwg/i18n` |
|
|
151
|
-
| | plurals, gendered forms and
|
|
153
|
+
| | plurals, gendered forms, interpolation, and Python f-string-style format specs | `mwg/i18n` |
|
|
152
154
|
| | Fluent-style FTL catalogs with locale-aware fallback and variants | `mwg/i18n` |
|
|
153
155
|
| | left-to-right, right-to-left and vertical writing | `mwg/i18n` |
|
|
154
156
|
| | interface mirrored for right-to-left languages | `mwg/two-d/ui` |
|
|
155
157
|
| | a missing translation falls back rather than showing a key | `mwg/i18n` |
|
|
158
|
+
| | per-message sound cues as an `audio` channel, plus inline `{sound:path}` markers for timed dialogue cues | `mwg/i18n` + `mwg/two-d/ui` |
|
|
156
159
|
| **audio** | sound effects with pooling, music with crossfade | `mwg/audio` |
|
|
157
160
|
|
|
158
161
|
### World
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { Catalog } from './index.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The testable core behind `tools/i18n-edit.mjs`: a base (reference-language) catalog and
|
|
4
|
+
* the target catalog being translated, plus the pure operations a split-screen translation
|
|
5
|
+
* editor needs. Nothing here touches the filesystem or the terminal - the tool owns file
|
|
6
|
+
* IO and rendering, this module owns the catalog arithmetic, so both are verifiable with
|
|
7
|
+
* plain unit tests.
|
|
8
|
+
*
|
|
9
|
+
* Audio cues are ordinary catalog entries under this scheme (`<key>.audio` holding a
|
|
10
|
+
* sound path, read by the semantic formatter's `audio` channel), not a parallel map - so
|
|
11
|
+
* associating a sound with a string is just setting one more key, and the translatable
|
|
12
|
+
* key list simply skips `*.audio` entries rather than asking a translator to translate a
|
|
13
|
+
* file path.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import {
|
|
18
|
+
* AUDIO_SUFFIX, copyFromBase, createEditSession, cueKeyFor, deleteTargetKey,
|
|
19
|
+
* isAudioKey, sessionCompleteness, sessionKeys, sessionRow, sessionRows,
|
|
20
|
+
* setTargetSound, setTargetText, swapSession,
|
|
21
|
+
* } from '@datamoc/mw_games/i18n';
|
|
22
|
+
*
|
|
23
|
+
* let session = createEditSession(
|
|
24
|
+
* { locale: 'en', direction: 'ltr', messages: { greeting: 'Hello!', 'hit.audio': 'sounds/hit.wav' } },
|
|
25
|
+
* { locale: 'fr', direction: 'ltr', messages: {} },
|
|
26
|
+
* );
|
|
27
|
+
*
|
|
28
|
+
* sessionKeys(session); // ['greeting'] - 'hit.audio' is a cue, not translated
|
|
29
|
+
* isAudioKey(`hit${AUDIO_SUFFIX}`); // true
|
|
30
|
+
* sessionRow(session, 'greeting').status; // 'missing'
|
|
31
|
+
* sessionRows(session, { missingOnly: true }).length; // 1
|
|
32
|
+
* sessionCompleteness(session); // 0
|
|
33
|
+
*
|
|
34
|
+
* session = setTargetText(session, 'greeting', 'Bonjour !');
|
|
35
|
+
* session = copyFromBase(session, 'greeting');
|
|
36
|
+
* session = setTargetSound(session, 'hit', 'sons/coup.wav');
|
|
37
|
+
* cueKeyFor(session, 'greeting'); // 'greeting.audio'
|
|
38
|
+
* session = deleteTargetKey(session, 'greeting');
|
|
39
|
+
* session = swapSession(session);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
/** one translatable key as the editor shows it: reference text left, target text right */
|
|
43
|
+
export interface EditRow {
|
|
44
|
+
key: string;
|
|
45
|
+
baseText: string;
|
|
46
|
+
/** '' when the target has no entry for this key yet */
|
|
47
|
+
targetText: string;
|
|
48
|
+
status: 'ok' | 'missing' | 'extra';
|
|
49
|
+
/** effective cue: the target's own `<key>.audio`, else the base's, else the family's (see below) */
|
|
50
|
+
sound?: string;
|
|
51
|
+
/** the full audio key `sound` above came from, when any */
|
|
52
|
+
soundKey?: string;
|
|
53
|
+
/** true when `sound` above was inherited from the base catalog */
|
|
54
|
+
soundInherited: boolean;
|
|
55
|
+
}
|
|
56
|
+
export interface EditSession {
|
|
57
|
+
base: Catalog;
|
|
58
|
+
target: Catalog;
|
|
59
|
+
}
|
|
60
|
+
export interface RowFilter {
|
|
61
|
+
/** only keys missing from the target */
|
|
62
|
+
missingOnly?: boolean;
|
|
63
|
+
/** case-insensitive substring matched against key and both texts */
|
|
64
|
+
query?: string;
|
|
65
|
+
}
|
|
66
|
+
export declare function createEditSession(base: Catalog, target: Catalog): EditSession;
|
|
67
|
+
/** the suffix marking a sound-cue entry rather than translatable text */
|
|
68
|
+
export declare const AUDIO_SUFFIX = ".audio";
|
|
69
|
+
export declare function isAudioKey(key: string): boolean;
|
|
70
|
+
/** every translatable key across both catalogs, sorted; `*.audio` cues are not translated */
|
|
71
|
+
export declare function sessionKeys(session: EditSession): string[];
|
|
72
|
+
export declare function sessionRow(session: EditSession, key: string): EditRow;
|
|
73
|
+
/**
|
|
74
|
+
* Which audio key `setTargetSound` writes for `key`: the key's own `<key>.audio` when one
|
|
75
|
+
* already exists on either side, else the family's (`<type>.audio` for a channel key),
|
|
76
|
+
* else the key's own. One cue per family rather than one per channel.
|
|
77
|
+
*/
|
|
78
|
+
export declare function cueKeyFor(session: EditSession, key: string): string;
|
|
79
|
+
export declare function sessionRows(session: EditSession, filter?: RowFilter): EditRow[];
|
|
80
|
+
/**
|
|
81
|
+
* Sets the target's text for `key`, preserving a plural structure when the new text parses
|
|
82
|
+
* as a plain JSON object over an existing plural entry - otherwise the entry becomes the
|
|
83
|
+
* string as typed. A select message (from an FTL source) edited here downgrades to a
|
|
84
|
+
* plain string; the tool warns about that on save rather than this function refusing it.
|
|
85
|
+
*/
|
|
86
|
+
export declare function setTargetText(session: EditSession, key: string, text: string): EditSession;
|
|
87
|
+
/** fills a missing target entry with the base language's own value, plural structure included */
|
|
88
|
+
export declare function copyFromBase(session: EditSession, key: string): EditSession;
|
|
89
|
+
export declare function deleteTargetKey(session: EditSession, key: string): EditSession;
|
|
90
|
+
/**
|
|
91
|
+
* Associates a sound cue with `key` by setting the target's audio entry (`cueKeyFor`
|
|
92
|
+
* picks the family's `<type>.audio` for a channel key with no cue of its own). An empty
|
|
93
|
+
* path clears the cue (removing the entry rather than storing an empty string, which
|
|
94
|
+
* `validateMessageAudio` would flag).
|
|
95
|
+
*/
|
|
96
|
+
export declare function setTargetSound(session: EditSession, key: string, path: string): EditSession;
|
|
97
|
+
/** fraction of the base catalog's translatable keys the target also has; cue keys excluded */
|
|
98
|
+
export declare function sessionCompleteness(session: EditSession): number;
|
|
99
|
+
/** swaps which catalog is the reference and which is edited, for main-language-to-main-language work */
|
|
100
|
+
export declare function swapSession(session: EditSession): EditSession;
|
|
101
|
+
//# sourceMappingURL=EditSession.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditSession.d.ts","sourceRoot":"","sources":["../../src/i18n/EditSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,YAAY,CAAC;AAGxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,0FAA0F;AAC1F,MAAM,WAAW,OAAO;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC;IACnC,oGAAoG;IACpG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,cAAc,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACzB,wCAAwC;IACxC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,WAAW,CAE7E;AAED,yEAAyE;AACzE,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,6FAA6F;AAC7F,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,EAAE,CAG1D;AAkBD,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAkCrE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAInE;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,GAAE,SAAc,GAAG,OAAO,EAAE,CAYnF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAc1F;AAED,iGAAiG;AACjG,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAI3E;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAK9E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAM3F;AAED,8FAA8F;AAC9F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAMhE;AAED,wGAAwG;AACxG,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,CAE7D"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { catalogCompleteness, messageText } from './Content.js';
|
|
2
|
+
export function createEditSession(base, target) {
|
|
3
|
+
return { base, target };
|
|
4
|
+
}
|
|
5
|
+
/** the suffix marking a sound-cue entry rather than translatable text */
|
|
6
|
+
export const AUDIO_SUFFIX = '.audio';
|
|
7
|
+
export function isAudioKey(key) {
|
|
8
|
+
return key.endsWith(AUDIO_SUFFIX);
|
|
9
|
+
}
|
|
10
|
+
/** every translatable key across both catalogs, sorted; `*.audio` cues are not translated */
|
|
11
|
+
export function sessionKeys(session) {
|
|
12
|
+
const keys = new Set([...Object.keys(session.base.messages), ...Object.keys(session.target.messages)]);
|
|
13
|
+
return [...keys].filter((key) => !isAudioKey(key)).sort();
|
|
14
|
+
}
|
|
15
|
+
function audioText(value) {
|
|
16
|
+
if (value === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
const text = messageText(value);
|
|
19
|
+
return text === '' ? undefined : text;
|
|
20
|
+
}
|
|
21
|
+
/** text channels sharing one semantic type share its cue: `combat.damage.log` shows
|
|
22
|
+
* `combat.damage.audio` when it has no `<key>.audio` of its own */
|
|
23
|
+
const TEXT_CHANNELS = ['log', 'compact', 'accessibility', 'debug'];
|
|
24
|
+
function familyAudioKey(key) {
|
|
25
|
+
const dot = key.lastIndexOf('.');
|
|
26
|
+
if (dot <= 0 || !TEXT_CHANNELS.includes(key.slice(dot + 1)))
|
|
27
|
+
return undefined;
|
|
28
|
+
return `${key.slice(0, dot)}${AUDIO_SUFFIX}`;
|
|
29
|
+
}
|
|
30
|
+
export function sessionRow(session, key) {
|
|
31
|
+
const baseValue = session.base.messages[key];
|
|
32
|
+
const targetValue = session.target.messages[key];
|
|
33
|
+
const status = baseValue !== undefined && targetValue !== undefined ? 'ok' : baseValue === undefined ? 'extra' : 'missing';
|
|
34
|
+
const cueKeys = [`${key}${AUDIO_SUFFIX}`];
|
|
35
|
+
const family = familyAudioKey(key);
|
|
36
|
+
if (family)
|
|
37
|
+
cueKeys.push(family);
|
|
38
|
+
let sound;
|
|
39
|
+
let soundKey;
|
|
40
|
+
let soundInherited = false;
|
|
41
|
+
for (const candidate of cueKeys) {
|
|
42
|
+
const own = audioText(session.target.messages[candidate]);
|
|
43
|
+
if (own !== undefined) {
|
|
44
|
+
sound = own;
|
|
45
|
+
soundKey = candidate;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
const inherited = audioText(session.base.messages[candidate]);
|
|
49
|
+
if (inherited !== undefined) {
|
|
50
|
+
sound = inherited;
|
|
51
|
+
soundKey = candidate;
|
|
52
|
+
soundInherited = true;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
key,
|
|
58
|
+
baseText: baseValue === undefined ? '' : messageText(baseValue),
|
|
59
|
+
targetText: targetValue === undefined ? '' : messageText(targetValue),
|
|
60
|
+
status,
|
|
61
|
+
sound,
|
|
62
|
+
soundKey,
|
|
63
|
+
soundInherited,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Which audio key `setTargetSound` writes for `key`: the key's own `<key>.audio` when one
|
|
68
|
+
* already exists on either side, else the family's (`<type>.audio` for a channel key),
|
|
69
|
+
* else the key's own. One cue per family rather than one per channel.
|
|
70
|
+
*/
|
|
71
|
+
export function cueKeyFor(session, key) {
|
|
72
|
+
const own = `${key}${AUDIO_SUFFIX}`;
|
|
73
|
+
if (own in session.target.messages || own in session.base.messages)
|
|
74
|
+
return own;
|
|
75
|
+
return familyAudioKey(key) ?? own;
|
|
76
|
+
}
|
|
77
|
+
export function sessionRows(session, filter = {}) {
|
|
78
|
+
const query = filter.query?.toLowerCase() ?? '';
|
|
79
|
+
return sessionKeys(session)
|
|
80
|
+
.map((key) => sessionRow(session, key))
|
|
81
|
+
.filter((row) => !filter.missingOnly || row.status === 'missing')
|
|
82
|
+
.filter((row) => query === '' ||
|
|
83
|
+
row.key.toLowerCase().includes(query) ||
|
|
84
|
+
row.baseText.toLowerCase().includes(query) ||
|
|
85
|
+
row.targetText.toLowerCase().includes(query));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Sets the target's text for `key`, preserving a plural structure when the new text parses
|
|
89
|
+
* as a plain JSON object over an existing plural entry - otherwise the entry becomes the
|
|
90
|
+
* string as typed. A select message (from an FTL source) edited here downgrades to a
|
|
91
|
+
* plain string; the tool warns about that on save rather than this function refusing it.
|
|
92
|
+
*/
|
|
93
|
+
export function setTargetText(session, key, text) {
|
|
94
|
+
const current = session.target.messages[key] ?? session.base.messages[key];
|
|
95
|
+
let value = text;
|
|
96
|
+
if (current !== undefined && typeof current === 'object' && !('format' in current)) {
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(text);
|
|
99
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
100
|
+
value = parsed;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// not JSON - keep the string as typed
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { ...session, target: { ...session.target, messages: { ...session.target.messages, [key]: value } } };
|
|
108
|
+
}
|
|
109
|
+
/** fills a missing target entry with the base language's own value, plural structure included */
|
|
110
|
+
export function copyFromBase(session, key) {
|
|
111
|
+
const baseValue = session.base.messages[key];
|
|
112
|
+
if (baseValue === undefined)
|
|
113
|
+
throw new Error(`copyFromBase: "${key}" is not a key in the base catalog`);
|
|
114
|
+
return { ...session, target: { ...session.target, messages: { ...session.target.messages, [key]: baseValue } } };
|
|
115
|
+
}
|
|
116
|
+
export function deleteTargetKey(session, key) {
|
|
117
|
+
if (!(key in session.target.messages))
|
|
118
|
+
throw new Error(`deleteTargetKey: "${key}" is not a key in the target catalog`);
|
|
119
|
+
const messages = { ...session.target.messages };
|
|
120
|
+
delete messages[key];
|
|
121
|
+
return { ...session, target: { ...session.target, messages } };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Associates a sound cue with `key` by setting the target's audio entry (`cueKeyFor`
|
|
125
|
+
* picks the family's `<type>.audio` for a channel key with no cue of its own). An empty
|
|
126
|
+
* path clears the cue (removing the entry rather than storing an empty string, which
|
|
127
|
+
* `validateMessageAudio` would flag).
|
|
128
|
+
*/
|
|
129
|
+
export function setTargetSound(session, key, path) {
|
|
130
|
+
const messages = { ...session.target.messages };
|
|
131
|
+
const audioKey = cueKeyFor(session, key);
|
|
132
|
+
if (path === '')
|
|
133
|
+
delete messages[audioKey];
|
|
134
|
+
else
|
|
135
|
+
messages[audioKey] = path;
|
|
136
|
+
return { ...session, target: { ...session.target, messages } };
|
|
137
|
+
}
|
|
138
|
+
/** fraction of the base catalog's translatable keys the target also has; cue keys excluded */
|
|
139
|
+
export function sessionCompleteness(session) {
|
|
140
|
+
const stripAudio = (catalog) => ({
|
|
141
|
+
...catalog,
|
|
142
|
+
messages: Object.fromEntries(Object.entries(catalog.messages).filter(([key]) => !isAudioKey(key))),
|
|
143
|
+
});
|
|
144
|
+
return catalogCompleteness(stripAudio(session.base), stripAudio(session.target));
|
|
145
|
+
}
|
|
146
|
+
/** swaps which catalog is the reference and which is edited, for main-language-to-main-language work */
|
|
147
|
+
export function swapSession(session) {
|
|
148
|
+
return { base: session.target, target: session.base };
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=EditSession.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditSession.js","sourceRoot":"","sources":["../../src/i18n/EditSession.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAsEhE,MAAM,UAAU,iBAAiB,CAAC,IAAa,EAAE,MAAe;IAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACzB,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAErC,MAAM,UAAU,UAAU,CAAC,GAAW;IACrC,OAAO,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AACnC,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,WAAW,CAAC,OAAoB;IAC/C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACvG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC3D,CAAC;AAED,SAAS,SAAS,CAAC,KAA+B;IACjD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAChC,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AACvC,CAAC;AAED;mEACmE;AACnE,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,CAAC,CAAC;AAEnE,SAAS,cAAc,CAAC,GAAW;IAClC,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9E,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,YAAY,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAoB,EAAE,GAAW;IAC3D,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,SAAS,KAAK,SAAS,IAAI,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3H,MAAM,OAAO,GAAG,CAAC,GAAG,GAAG,GAAG,YAAY,EAAE,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,KAAyB,CAAC;IAC9B,IAAI,QAA4B,CAAC;IACjC,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,KAAK,MAAM,SAAS,IAAI,OAAO,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;QAC1D,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACvB,KAAK,GAAG,GAAG,CAAC;YACZ,QAAQ,GAAG,SAAS,CAAC;YACrB,MAAM;QACP,CAAC;QACD,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9D,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC7B,KAAK,GAAG,SAAS,CAAC;YAClB,QAAQ,GAAG,SAAS,CAAC;YACrB,cAAc,GAAG,IAAI,CAAC;YACtB,MAAM;QACP,CAAC;IACF,CAAC;IACD,OAAO;QACN,GAAG;QACH,QAAQ,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC;QAC/D,UAAU,EAAE,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC;QACrE,MAAM;QACN,KAAK;QACL,QAAQ;QACR,cAAc;KACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,OAAoB,EAAE,GAAW;IAC1D,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,YAAY,EAAE,CAAC;IACpC,IAAI,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO,GAAG,CAAC;IAC/E,OAAO,cAAc,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAoB,EAAE,MAAM,GAAc,EAAE;IACvE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAChD,OAAO,WAAW,CAAC,OAAO,CAAC;SACzB,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;SACtC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC;SAChE,MAAM,CACN,CAAC,GAAG,EAAE,EAAE,CACP,KAAK,KAAK,EAAE;QACZ,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;QACrC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;QAC1C,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAC7C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,OAAoB,EAAE,GAAW,EAAE,IAAY;IAC5E,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC3E,IAAI,KAAK,GAAiB,IAAI,CAAC;IAC/B,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC,EAAE,CAAC;QACpF,IAAI,CAAC;YACJ,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC7E,KAAK,GAAG,MAAgC,CAAC;YAC1C,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,sCAAsC;QACvC,CAAC;IACF,CAAC;IACD,OAAO,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AAC9G,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,YAAY,CAAC,OAAoB,EAAE,GAAW;IAC7D,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,SAAS,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,oCAAoC,CAAC,CAAC;IACxG,OAAO,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAClH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAoB,EAAE,GAAW;IAChE,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,sCAAsC,CAAC,CAAC;IACvH,MAAM,QAAQ,GAAG,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IAChD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;IACrB,OAAO,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC;AAChE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAoB,EAAE,GAAW,EAAE,IAAY;IAC7E,MAAM,QAAQ,GAAG,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IAChD,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACzC,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC;;QACtC,QAAQ,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC;AAChE,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,mBAAmB,CAAC,OAAoB;IACvD,MAAM,UAAU,GAAG,CAAC,OAAgB,EAAW,EAAE,CAAC,CAAC;QAClD,GAAG,OAAO;QACV,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;KAClG,CAAC,CAAC;IACH,OAAO,mBAAmB,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAClF,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,WAAW,CAAC,OAAoB;IAC/C,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;AACvD,CAAC"}
|
package/dist/i18n/Fluent.js
CHANGED
|
@@ -65,7 +65,15 @@ export function parseFTL(locale, source, options = {}) {
|
|
|
65
65
|
return { locale, direction: options.direction ?? (/^(ar|he|fa|ur)(?:-|$)/i.test(locale) ? 'rtl' : 'ltr'), messages };
|
|
66
66
|
}
|
|
67
67
|
function normalize(text) {
|
|
68
|
-
|
|
68
|
+
//`{$name}` renders as `{name}`; a conversion or format spec rides along untouched
|
|
69
|
+
//(`{$dmg:03d}` becomes `{dmg:03d}`), for `t()`'s own f-string handling downstream
|
|
70
|
+
return text
|
|
71
|
+
.replace(/\{\s*\$([\w-]+)\s*(?:!([sra]))?\s*(?::\s*([^{}]*?))?\s*\}/g, (_, name, conv, spec) => {
|
|
72
|
+
const conversion = conv ? `!${conv}` : '';
|
|
73
|
+
const suffix = spec === undefined ? '' : `:${spec.trim()}`;
|
|
74
|
+
return `{${name}${conversion}${suffix}}`;
|
|
75
|
+
})
|
|
76
|
+
.trim();
|
|
69
77
|
}
|
|
70
78
|
function selectMessage(locale, selectorName, variants) {
|
|
71
79
|
const defaults = variants.filter((variant) => variant.default);
|
package/dist/i18n/Fluent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Fluent.js","sourceRoot":"","sources":["../../src/i18n/Fluent.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"Fluent.js","sourceRoot":"","sources":["../../src/i18n/Fluent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python f-string-style value formatting for catalog messages: `{dmg:03d}`, `{hp:.1%}`,
|
|
3
|
+
* `{name:>12}`. Plain `{token}` interpolation stays exactly as it was; a format spec after
|
|
4
|
+
* a colon picks a deterministic, locale-independent rendering (padded damage numbers, hex
|
|
5
|
+
* ids, fixed-precision percentages), the counterpart to `Format.ts`, whose `Intl` wrappers
|
|
6
|
+
* render the locale-aware way instead ("1,234" vs "1 234").
|
|
7
|
+
*
|
|
8
|
+
* Supported subset of
|
|
9
|
+
* https://docs.python.org/3/library/string.html#format-specification-mini-language -
|
|
10
|
+
* `[[fill]align][sign][z][#][0][width][,|_][.precision][type]` with types
|
|
11
|
+
* `b c d e E f F g G n o s x X %`, verified case by case against CPython itself
|
|
12
|
+
* (two-digit exponents, stripped `g` precision, `_` grouping binary/octal/hex in fours).
|
|
13
|
+
* Deliberately outside the subset: nested replacement fields in the spec (`{:{width}}`),
|
|
14
|
+
* which are left for a game to resolve before calling `t()`. Anything outside the subset
|
|
15
|
+
* returns `undefined` rather than guessing, and `t()` leaves that placeholder untouched
|
|
16
|
+
* the way it already does for a missing token.
|
|
17
|
+
*
|
|
18
|
+
* One divergence no implementation choice can close: JavaScript numbers carry no
|
|
19
|
+
* int/float distinction, so integer-valued floats always take the integer path (`3.0`
|
|
20
|
+
* renders as `3`, and takes `d` where CPython would raise for a float). Games that need
|
|
21
|
+
* `3.0` spell it with an explicit float type (`{v:.1f}`).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { formatSpec, tokenizeMessage, diffPlaceholders } from '@datamoc/mw_games/i18n';
|
|
26
|
+
*
|
|
27
|
+
* formatSpec(7, '03d'); // '007'
|
|
28
|
+
* formatSpec(0.125, '.1%'); // '12.5%'
|
|
29
|
+
* formatSpec('hi', '^7'); // ' hi '
|
|
30
|
+
*
|
|
31
|
+
* tokenizeMessage('Hit for {dmg:03d}!');
|
|
32
|
+
* // ['Hit for ', { raw: '{dmg:03d}', token: 'dmg', debug: false, conv: undefined, spec: '03d' }, '!']
|
|
33
|
+
*
|
|
34
|
+
* diffPlaceholders('Hit for {dmg:03d}!', 'Touché pour {dmg} !');
|
|
35
|
+
* // { missing: [], extra: [], changed: [{ token: 'dmg', base: ':03d', target: '' }] }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export interface Placeholder {
|
|
39
|
+
/** the placeholder exactly as written, for untouched passthrough */
|
|
40
|
+
raw: string;
|
|
41
|
+
token: string;
|
|
42
|
+
debug: boolean;
|
|
43
|
+
conv: string | undefined;
|
|
44
|
+
spec: string | undefined;
|
|
45
|
+
}
|
|
46
|
+
export type MessagePart = string | Placeholder;
|
|
47
|
+
/**
|
|
48
|
+
* Splits a message into literal text and `{token}`, `{token:spec}`, `{token!conv}` and
|
|
49
|
+
* `{token=}` placeholders - the single definition of that grammar, shared by `t()`'s own
|
|
50
|
+
* interpolation and by `diffPlaceholders` below, so the editor's consistency check can
|
|
51
|
+
* never disagree with the runtime about what counts as a placeholder. `{sound:path}` is an
|
|
52
|
+
* inline sound marker, not a translatable placeholder, and is left for `parseSoundMarkers`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function tokenizeMessage(text: string): MessagePart[];
|
|
55
|
+
export interface PlaceholderDiff {
|
|
56
|
+
/** tokens the base message needs that the target never supplies */
|
|
57
|
+
missing: string[];
|
|
58
|
+
/** tokens the target supplies that the base never needs */
|
|
59
|
+
extra: string[];
|
|
60
|
+
/** same token, different conversion/spec/debug shape on each side */
|
|
61
|
+
changed: Array<{
|
|
62
|
+
token: string;
|
|
63
|
+
base: string;
|
|
64
|
+
target: string;
|
|
65
|
+
}>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Compares two message texts placeholder by placeholder - the check a translation editor
|
|
69
|
+
* runs between a reference string and its translation, since a dropped or reworded token
|
|
70
|
+
* (`{dmg:03d}` becoming `{damage}`, or vanishing entirely) reaches the player as a raw
|
|
71
|
+
* placeholder exactly the way a missing key would.
|
|
72
|
+
*/
|
|
73
|
+
export declare function diffPlaceholders(baseText: string, targetText: string): PlaceholderDiff;
|
|
74
|
+
export declare function formatSpec(value: string | number, spec: string, language?: string): string | undefined;
|
|
75
|
+
//# sourceMappingURL=FormatSpec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormatSpec.d.ts","sourceRoot":"","sources":["../../src/i18n/FormatSpec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,MAAM,WAAW,WAAW;IAC3B,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,CAAC;AAI/C;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAgB3D;AAMD,MAAM,WAAW,eAAe;IAC/B,mEAAmE;IACnE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,qEAAqE;IACrE,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,CAiBtF;AAqFD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAO,GAAG,MAAM,GAAG,SAAS,CAkGpG"}
|