message-map v0.8.0
⧟ MessageMap
Dynamic string generation with a strongly-typed interface.
💁🏼♂️ Introduction
MessageMap is an extra spicy version of String.prototype.replace that applies TypeScript static typing to the concept of C-style string interpolation. Some potential use-cases include:
- Centrally manage error messages for your JavaScript/TypeScript package.
- Build a library of reactive translations for your front-end web application.
- Protect yourself from formatting bugs inside large/unwieldy template strings by maintaining type safety.
- Probably a lot more I haven't considered yet!
NOTE: This package uses modern TypeScript features to achieve type safety,
typescript@>=3.1is recommended!
🔗 Installation
Install via yarn (recommended):
yarn add message-mapInstall via npm:
npm install message-map🛠️ Usage
Basic example
MessageMap has a chaining API where each method returns a new instance of MessageMap, similar to RxJS. This ensures the type signatures remain accurate and improves readability!
import { MessageMap } from 'message-map';
const myStringBuilder = new MessageMap('The date is %month %day, %year. The current epoch is %epoch.')
.required('month')
.required('day')
.required('year')
.optional('epoch', () => String(new Date().getTime())); // We can choose to specify a default value for the optional key.
myStringBuilder.toString({
month: 'January',
day: '1',
year: '2000',
epoch: ... // Optional -- as indicated above. TypeScript will not complain if this prop is missing.
});
// => "The date is January 1, 2000. The current epoch is 946684800."Using validator functions
You can optionally provide Validator functions to MessageMap.required or MessageMap.optional. The callback returns a boolean to indicate whether the interpolation should proceed at all OR a string fallback in case the user-provided substitution is missing or undefined (this enables you to specify defaults for MessageMap.optional replacement keys—as shown in the above example for epoch).
import { MessageMap } from 'message-map';
const myStringBuilder = new MessageMap('My phone number is %phoneNumber')
.required('phoneNumber', str => /^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$/.test(str));
myStringBuilder.toString({
phoneNumber: '555-asdf-1234' // This will raise an error because the phone number won't pass validation!
});Using MessageCollection
The MessageCollection class is intended for large-scale use-cases (such as building a language library) and is configurable via JSON-compatible objects. With TypeScript's resolveJsonModule option enabled, or by providing a static object literal, your MessageCollection can be strongly-typed, too!
import * as languagelibrary from '../path/to/languageLibrary.json'; // Requires `resolveJsonModule` in tsconfig.json
import { MessageCollection } from 'message-map';
const myLanguageLibrary = new MessageCollection(languageLibrary);languageLibrary.json has the following type signature:
interface JsonLanguageLibrary {
[key: string]: string | {
template: string;
optional?: {
[substitution: string]: string | null | {
default?: string;
regex?: string;
};
};
required?: {
[substitution: string]: string | null | {
default?: string;
regex?: string;
};
};
};
}Here's some example JSON:
{
"HELLO_X": {
"template": "Good %partOfDay, %yourName!",
"optional": {
"yourName": {
"default": "Jeeves"
}
},
"required": {
"partOfDay": {
"regex": "//(morning|afternoon|evening)//"
}
}
},
"MORNING": "morning"
}You can then use myLanguageLibrary as a collection of static MessageMap instances:
myLanguageLibrary.get('HELLO_X').toString({
yourName: 'Bojack',
partOfDay: myLanguageLibrary.get('MORNING').toString(),
});
// => "Good morning, Bojack!"For convenience, the MessageCollection class includes an enum of valid key names:
myLanguageLibrary.keys.HELLO_X; // => "HELLO_X"
myLanguageLibrary.keys.MORNING; // => "MORNING"
keyof typeof myLanguageLibrary.keys; // => "HELLO_X" | "MORNING"The underlying MessageMap instances are exposed on MessageCollection.messages, but note they are lazily populated by MessageCollection.get(...). The MessageCollection.messages property is exposed primarily for TypeScript typing needs:
myLanguageLibrary.messageMaps
// => {
// "HELLO_X"?: MessageMap,
// "MORNING"?: MessageMap,
// }Then you can trivially extract the substitution parameters like so:
type MySubstitutionKeys = keyof typeof myLanguageLibrary['keys'];
type MySubstitutionConfig<TKey extends MySubstitutionKeys> = Parameters<typeof myLanguageLibrary['messages'][TKey]['toString']>;Using the UMD build
A UMD build of MessageMap is available via UNPKG. Drop it into your page via a <script> tag:
<script type="text/javascript" src="https://unpkg.com/message-map/dist/index.umd.js"></script>The API for the UMD build has one small difference: MessageMap is the default object with MessageCollection available on MessageMap.Collection. So, you would use...
new MessageMap.Collection(langaugeLibraryJson);Voila!
5 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago