diff --git a/experiments/data_model/ts_eemeli/data-model-examples.ts b/experiments/data_model/ts_eemeli/data-model-examples.ts index a04d4cfa7..0a495d327 100644 --- a/experiments/data_model/ts_eemeli/data-model-examples.ts +++ b/experiments/data_model/ts_eemeli/data-model-examples.ts @@ -1,6 +1,6 @@ // MF1: { gender, select, male{he} female{she} other{they} } const genderSelect: Select = { - select: [['gender']], + select: [{ var_path: ['gender'] }], cases: [ { key: ['male'], value: ['he'] }, { key: ['female'], value: ['she'] }, @@ -10,12 +10,12 @@ const genderSelect: Select = { // MF1: { count, plural, one{a message} other{# messages} } const countPlural: Select = { - select: [{ func: 'plural', args: [['count']] }], + select: [{ func: 'plural', args: [{ var_path: ['count'] }] }], cases: [ { key: ['one'], value: ['a message'] }, { key: ['other'], - value: [{ func: 'number', args: [['count']] }, ' messages'] + value: [{ func: 'number', args: [{ var_path: ['count'] }] }, ' messages'] } ] } @@ -23,70 +23,111 @@ const countPlural: Select = { const gameMessages: Resource = { id: 'game-messages', locale: 'en', - messages: { - monsters: { - dinosaur: { indefinite: ['a Dinosaur'], plural: ['Dinosaurs'] }, - elephant: { indefinite: ['an Elephant'], plural: ['Elephants'] }, - ogre: { indefinite: ['an Ogre'], plural: ['Ogres'] }, - other: { indefinite: ['a Monster'], plural: ['Monsters'] } - }, - 'killed-by': [ - 'You have been killed by ', - { msg: ['monsters', ['monster'], 'indefinite'] } - ], - 'kill-count': { - select: [ - { func: 'plural', args: [['monster-count']] }, - { func: 'plural', args: [['dungeon-count']] } - ], - cases: [ + entries: [ + { + id: 'monsters', + entries: [ + { + id: 'dinosaur', + entries: [ + { id: 'indefinite', value: ['a Dinosaur'] }, + { id: 'plural', value: ['Dinosaurs'] } + ] + }, { - key: ['one'], - value: [ - 'You have killed ', - { msg: ['monsters', ['monster'], 'indefinite'] }, - '.' + id: 'elephant', + entries: [ + { id: 'indefinite', value: ['an Elephant'] }, + { id: 'plural', value: ['Elephants'] } ] }, { - key: ['other', 'one'], - value: [ - 'You have killed ', - { func: 'number', args: [['monster-count']] }, - ' ', - { msg: ['monsters', ['monster'], 'plural'] }, - ' in one dungeon.' + id: 'ogre', + entries: [ + { id: 'indefinite', value: ['an Ogre'] }, + { id: 'plural', value: ['Ogres'] } ] }, { - key: ['other', 'other'], - value: [ - 'You have killed ', - { func: 'number', args: [['monster-count']] }, - ' ', - { msg: ['monsters', ['monster'], 'plural'] }, - ' in ', - { func: 'number', args: [['dungeon-count']] }, - ' dungeons.' + id: 'other', + entries: [ + { id: 'indefinite', value: ['a Monster'] }, + { id: 'plural', value: ['Monsters'] } ] } ] + }, + { + id: 'killed-by', + value: [ + 'You have been killed by ', + { msg_path: ['monsters', { var_path: ['monster'] }, 'indefinite'] } + ] + }, + { + id: 'kill-count', + value: { + select: [ + { func: 'plural', args: [{ var_path: ['monster-count'] }] }, + { func: 'plural', args: [{ var_path: ['dungeon-count'] }] } + ], + cases: [ + { + key: ['one'], + value: [ + 'You have killed ', + { + msg_path: ['monsters', { var_path: ['monster'] }, 'indefinite'] + }, + '.' + ] + }, + { + key: ['other', 'one'], + value: [ + 'You have killed ', + { func: 'number', args: [{ var_path: ['monster-count'] }] }, + ' ', + { msg_path: ['monsters', { var_path: ['monster'] }, 'plural'] }, + ' in one dungeon.' + ] + }, + { + key: ['other', 'other'], + value: [ + 'You have killed ', + { func: 'number', args: [{ var_path: ['monster-count'] }] }, + ' ', + { msg_path: ['monsters', { var_path: ['monster'] }, 'plural'] }, + ' in ', + { func: 'number', args: [{ var_path: ['dungeon-count'] }] }, + ' dungeons.' + ] + } + ] + } } - } + ] } const extMessages: Resource = { id: 'remote-ref', locale: 'en', - messages: { - friend: [ - 'Your friend has become ', - { - func: 'sparkle', - args: [ - { id: 'game-messages', msg: ['monsters', ['monster'], 'indefinite'] } - ] - } - ] - } + entries: [ + { + id: 'friend', + value: [ + 'Your friend has become ', + { + func: 'sparkle', + args: [ + { + res_id: 'game-messages', + msg_path: ['monsters', { var_path: ['monster'] }, 'indefinite'] + } + ] + } + ] + } + ] } diff --git a/experiments/data_model/ts_eemeli/data-model.d.ts b/experiments/data_model/ts_eemeli/data-model.d.ts index 1c3bd2da5..39d8b41bc 100644 --- a/experiments/data_model/ts_eemeli/data-model.d.ts +++ b/experiments/data_model/ts_eemeli/data-model.d.ts @@ -6,20 +6,36 @@ interface Resource { id: string locale: string - messages: MessageSet + entries: Entry[] + meta?: Meta +} + +type Entry = Message | MessageSet + +interface MessageSet { + id: string + entries: Entry[] + meta?: Meta } /** - * Really this ought to be a Record, but TS only allows circular references for - * a limited set of types. + * Additional meta information amy be attached to most nodes. In common use, + * this information is not required when formatting a message. */ -type MessageSet = { [key in string]: Message | Select | MessageSet } +interface Meta { + comment?: string + [key: string]: unknown +} /** * The string parts of a message represent fixed values, while placeholder * values are variable. */ -type Message = Value[] +interface Message { + id: string + value: Value[] | Select + meta?: Meta +} /** * Select generalises the plural, selectordinal and select argument types of @@ -32,7 +48,7 @@ type Message = Value[] */ interface Select { select: Value[] - cases: Array<{ key: string[]; value: Message }> + cases: Array<{ key: Literal[]; value: Value[]; meta?: Meta }> } /** @@ -61,7 +77,10 @@ type Literal = string | number * object value, so e.g. `['user', 'name']` would require something like * `{ name: 'Kat' }` as the value of the `'user'` scope variable. */ -type VariableReference = string[] +interface VariableReference { + var_path: Path + meta?: Meta +} /** * To resolve a FunctionReference, an externally defined function is called. @@ -79,13 +98,14 @@ type VariableReference = string[] interface FunctionReference { func: string args: Value[] - options?: Record + options?: Array<{ key: string; value: string | number | boolean }> + meta?: Meta } /** * A MessageReference is a pointer to a Message or a Select. * - * If `id` is undefined, the message is sought in the current Resource. + * If `resource` is undefined, the message is sought in the current Resource. * If it is set, it identifies the resource for the sought message. * * While `msg` has superficially the same type as a Message, all but the last @@ -96,7 +116,15 @@ interface FunctionReference { * `scope` overrides values in the current scope when resolving the message. */ interface MessageReference { - id?: string - msg: Value[] - scope?: Record + res_id?: string + msg_path: Path + scope?: Scope[] + meta?: Meta } + +interface Scope { + name: string + value: Value | boolean | Scope +} + +type Path = Value[]