From 4ff7a9eab33d9c43ad944aa9c0e8df1393ff9527 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Mon, 15 Feb 2021 11:09:20 +0200 Subject: [PATCH 1/2] Add meta; adjust structure accordingly --- .../ts_eemeli/data-model-examples.ts | 85 +++++++++++++------ .../data_model/ts_eemeli/data-model.d.ts | 34 ++++++-- 2 files changed, 84 insertions(+), 35 deletions(-) diff --git a/experiments/data_model/ts_eemeli/data-model-examples.ts b/experiments/data_model/ts_eemeli/data-model-examples.ts index a04d4cfa7..b1f93984c 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: [{ 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: [{ path: ['count'] }] }], cases: [ { key: ['one'], value: ['a message'] }, { key: ['other'], - value: [{ func: 'number', args: [['count']] }, ' messages'] + value: [{ func: 'number', args: [{ path: ['count'] }] }, ' messages'] } ] } @@ -25,26 +25,50 @@ const gameMessages: Resource = { 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'] } + messages: { + dinosaur: { + messages: { + indefinite: { value: ['a Dinosaur'] }, + plural: { value: ['Dinosaurs'] } + } + }, + elephant: { + messages: { + indefinite: { value: ['an Elephant'] }, + plural: { value: ['Elephants'] } + } + }, + ogre: { + messages: { + indefinite: { value: ['an Ogre'] }, + plural: { value: ['Ogres'] } + } + }, + other: { + messages: { + indefinite: { value: ['a Monster'] }, + plural: { value: ['Monsters'] } + } + } + } + }, + 'killed-by': { + value: [ + 'You have been killed by ', + { msg: ['monsters', { path: ['monster'] }, 'indefinite'] } + ] }, - 'killed-by': [ - 'You have been killed by ', - { msg: ['monsters', ['monster'], 'indefinite'] } - ], 'kill-count': { select: [ - { func: 'plural', args: [['monster-count']] }, - { func: 'plural', args: [['dungeon-count']] } + { func: 'plural', args: [{ path: ['monster-count'] }] }, + { func: 'plural', args: [{ path: ['dungeon-count'] }] } ], cases: [ { key: ['one'], value: [ 'You have killed ', - { msg: ['monsters', ['monster'], 'indefinite'] }, + { msg: ['monsters', { path: ['monster'] }, 'indefinite'] }, '.' ] }, @@ -52,9 +76,9 @@ const gameMessages: Resource = { key: ['other', 'one'], value: [ 'You have killed ', - { func: 'number', args: [['monster-count']] }, + { func: 'number', args: [{ path: ['monster-count'] }] }, ' ', - { msg: ['monsters', ['monster'], 'plural'] }, + { msg: ['monsters', { path: ['monster'] }, 'plural'] }, ' in one dungeon.' ] }, @@ -62,11 +86,11 @@ const gameMessages: Resource = { key: ['other', 'other'], value: [ 'You have killed ', - { func: 'number', args: [['monster-count']] }, + { func: 'number', args: [{ path: ['monster-count'] }] }, ' ', - { msg: ['monsters', ['monster'], 'plural'] }, + { msg: ['monsters', { path: ['monster'] }, 'plural'] }, ' in ', - { func: 'number', args: [['dungeon-count']] }, + { func: 'number', args: [{ path: ['dungeon-count'] }] }, ' dungeons.' ] } @@ -79,14 +103,19 @@ const extMessages: Resource = { id: 'remote-ref', locale: 'en', messages: { - friend: [ - 'Your friend has become ', - { - func: 'sparkle', - args: [ - { id: 'game-messages', msg: ['monsters', ['monster'], 'indefinite'] } - ] - } - ] + friend: { + value: [ + 'Your friend has become ', + { + func: 'sparkle', + args: [ + { + id: 'game-messages', + msg: ['monsters', { 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..e4d1c16d5 100644 --- a/experiments/data_model/ts_eemeli/data-model.d.ts +++ b/experiments/data_model/ts_eemeli/data-model.d.ts @@ -6,20 +6,34 @@ interface Resource { id: string locale: string - messages: MessageSet + messages: Record + meta?: Meta +} + +interface MessageSet { + messages: Record + 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 +} + +type Formattable = Message | Select /** * The string parts of a message represent fixed values, while placeholder * values are variable. */ -type Message = Value[] +interface Message { + value: Value[] + meta?: Meta +} /** * Select generalises the plural, selectordinal and select argument types of @@ -32,7 +46,8 @@ type Message = Value[] */ interface Select { select: Value[] - cases: Array<{ key: string[]; value: Message }> + cases: Array<{ key: string[]; value: Value[]; meta?: Meta }> + meta?: Meta } /** @@ -61,7 +76,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 { + path: Value[] + meta?: Meta +} /** * To resolve a FunctionReference, an externally defined function is called. @@ -80,6 +98,7 @@ interface FunctionReference { func: string args: Value[] options?: Record + meta?: Meta } /** @@ -99,4 +118,5 @@ interface MessageReference { id?: string msg: Value[] scope?: Record + meta?: Meta } From 0e8396fb285085b6ed77ab0e41f08d412389d871 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Mon, 15 Feb 2021 20:20:23 +0200 Subject: [PATCH 2/2] Merge with Zibi --- .../ts_eemeli/data-model-examples.ts | 152 ++++++++++-------- .../data_model/ts_eemeli/data-model.d.ts | 34 ++-- 2 files changed, 103 insertions(+), 83 deletions(-) diff --git a/experiments/data_model/ts_eemeli/data-model-examples.ts b/experiments/data_model/ts_eemeli/data-model-examples.ts index b1f93984c..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: [{ path: ['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: [{ path: ['count'] }] }], + select: [{ func: 'plural', args: [{ var_path: ['count'] }] }], cases: [ { key: ['one'], value: ['a message'] }, { key: ['other'], - value: [{ func: 'number', args: [{ path: ['count'] }] }, ' messages'] + value: [{ func: 'number', args: [{ var_path: ['count'] }] }, ' messages'] } ] } @@ -23,99 +23,111 @@ const countPlural: Select = { const gameMessages: Resource = { id: 'game-messages', locale: 'en', - messages: { - monsters: { - messages: { - dinosaur: { - messages: { - indefinite: { value: ['a Dinosaur'] }, - plural: { value: ['Dinosaurs'] } - } - }, - elephant: { - messages: { - indefinite: { value: ['an Elephant'] }, - plural: { value: ['Elephants'] } - } - }, - ogre: { - messages: { - indefinite: { value: ['an Ogre'] }, - plural: { value: ['Ogres'] } - } + entries: [ + { + id: 'monsters', + entries: [ + { + id: 'dinosaur', + entries: [ + { id: 'indefinite', value: ['a Dinosaur'] }, + { id: 'plural', value: ['Dinosaurs'] } + ] }, - other: { - messages: { - indefinite: { value: ['a Monster'] }, - plural: { value: ['Monsters'] } - } - } - } - }, - 'killed-by': { - value: [ - 'You have been killed by ', - { msg: ['monsters', { path: ['monster'] }, 'indefinite'] } - ] - }, - 'kill-count': { - select: [ - { func: 'plural', args: [{ path: ['monster-count'] }] }, - { func: 'plural', args: [{ path: ['dungeon-count'] }] } - ], - cases: [ { - key: ['one'], - value: [ - 'You have killed ', - { msg: ['monsters', { path: ['monster'] }, 'indefinite'] }, - '.' + id: 'elephant', + entries: [ + { id: 'indefinite', value: ['an Elephant'] }, + { id: 'plural', value: ['Elephants'] } ] }, { - key: ['other', 'one'], - value: [ - 'You have killed ', - { func: 'number', args: [{ path: ['monster-count'] }] }, - ' ', - { msg: ['monsters', { path: ['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: [{ path: ['monster-count'] }] }, - ' ', - { msg: ['monsters', { path: ['monster'] }, 'plural'] }, - ' in ', - { func: 'number', args: [{ path: ['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: { + entries: [ + { + id: 'friend', value: [ 'Your friend has become ', { func: 'sparkle', args: [ { - id: 'game-messages', - msg: ['monsters', { path: ['monster'] }, 'indefinite'] + 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 e4d1c16d5..39d8b41bc 100644 --- a/experiments/data_model/ts_eemeli/data-model.d.ts +++ b/experiments/data_model/ts_eemeli/data-model.d.ts @@ -6,12 +6,15 @@ interface Resource { id: string locale: string - messages: Record + entries: Entry[] meta?: Meta } +type Entry = Message | MessageSet + interface MessageSet { - messages: Record + id: string + entries: Entry[] meta?: Meta } @@ -24,14 +27,13 @@ interface Meta { [key: string]: unknown } -type Formattable = Message | Select - /** * The string parts of a message represent fixed values, while placeholder * values are variable. */ interface Message { - value: Value[] + id: string + value: Value[] | Select meta?: Meta } @@ -46,8 +48,7 @@ interface Message { */ interface Select { select: Value[] - cases: Array<{ key: string[]; value: Value[]; meta?: Meta }> - meta?: Meta + cases: Array<{ key: Literal[]; value: Value[]; meta?: Meta }> } /** @@ -77,7 +78,7 @@ type Literal = string | number * `{ name: 'Kat' }` as the value of the `'user'` scope variable. */ interface VariableReference { - path: Value[] + var_path: Path meta?: Meta } @@ -97,14 +98,14 @@ interface VariableReference { 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 @@ -115,8 +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[]