Formats message strings with number, date, plural, and select placeholders to create localized messages

Overview

messagepipemessagepipe

snyk npm package size

Formats message strings with number, date, plural, and select placeholders to create localized messages.

  • Small. Between 700 bytes and 1.3 kilobytes (minified and gzipped). Zero dependencies.
  • Fast. Does absolute minimum amount of computations necessary. View benchmarks.
  • Tree Shakable. Includes separate global transformers config that can be omitted.
  • Pipe syntax. Transformer functions can customized and chained.
  • View framework support. Use React/Preact etc. components as transformers.
  • It has good TypeScript support.
import { MessagePipe } from 'messagepipe'

const msg = MessagePipe().compile('Hello {planet}!')

msg({ planet: 'Mars' }) // => "Hello Mars!"

live demo

import { MessagePipe } from 'messagepipe'

const { compile } = MessagePipe({
  reverse: (val) => val.split('').reverse().join(''),
  capitalize: (val) => val[0].toUpperCase() + val.slice(1).toLowerCase(),
})

const msg = compile('Hello {planet | reverse | capitalize}!')

msg({ planet: 'Mars' }) // => "Hello Sram!"

live demo

Install

npm install messagepipe

Guide

Core concepts

          ┌-transformer
          |     ┌-argument name
          |     |     ┌-argument value
          ├--┐  ├---┐ ├-┐

  {name | json, space:101}

  ├----------------------┘
  |├--┘   ├-------------┘
  ||      |     ├-------┘
  ||      |     └-argument
  ||      └-pipe
  |└-selector
  └-message

In one message there can only be one selector, but there can be unlimited number of pipes with unlimited number of arguments in them. It is possible to build dynamic selector (meaning message can be inside it), but it is not possible to build dynamic pipes except for argument values.

So both of these are valid:

  1. "Hello {agents.{index}.fistName}";
  2. "{a} + {b} = {a | sum, sequence:{b}}". (Note: sum is a custom transformer in this case).

Message

Contains everything between { and } that in large includes 1 selector and n pipes.

Selector

String value that points to value from given props object e.g.:

  • "{name}" + { name: 'john' } => "john";
  • "{agents[0].name}" + { agents: [{ name: 'john' }] } => "john"

Pipe

A combination of 1 transformer and n arguments e.g.:

  • "{name | capitalize}";
  • "{name | reverse | capitalize}";
  • "{a | sum, sequence:1, double}" (Note: argument "double" will pass true value to "sum" transformer).

Transformer

Function that can transform value that is being selected from given props.

Lets define "capitalize" transformer that would uppercase the first letter of any string:

function capitalize(value: string) {
  return value[0].toUpperCase() + value.slice(1).toLowerCase();
}

To use this transformer define it when initiating MessagePipe and then it will be available to pipes with name "capitalize":

const msgPipe = MessagePipe({
  capitalize,
})

This would be valid use case for it: "Greetings {name | capitalize}!".

Argument

To allow more functionality, we can use arguments, that are passed to transformer function.

function increment(value: number, { by = 1 }: Record<string, any> = {}) {
  return value + by;
}

We can now use it like this:

  • "{count | increment}" + { count: 1 } => 2;
  • "{count | increment | by:1}" + { count: 1 } => 2;
  • "{count | increment | by:5}" + { count: 1 } => 6.

We can stack any number of arguments separated by , (comma).

Global transformers

There are number of already provided transformers, but they MUST be added to MessagePipe function when initiating. This is by design to help with tree shaking (although they don't contribute that much to package size, if there are additions in future, that won't hurt anyone).

defaultTransformers

function defaultTransformers(): MessagePipeTransformers

select

Selects what text to show based on incoming value.

const msg = compile('{gender | select, male:"He", female:"She", other:"They"} liked this.')

msg({ gender: 'male' }) // "He liked this"
msg({ gender: 'female' }) // "She liked this"
msg({ }) // "They liked this"

json

Runs value through JSON.stringify.

intlTransformers

function intlTransformers(locale?: string): MessagePipeTransformers

number

Formats numbers using Intl.NumberFormat. All options are available as arguments in pipes.

const msg = compile('{price | number}')

msg({ price: 123456.789 }) // "123,456.789"
const msg = compile('Price: {price | number, style:"currency", currency:"EUR"}')

msg({ price: 123 }) // "Price: 123,00 €"

plural

Selects correct text to show based on Intl.PluralRules. All options are available as arguments in pipes.

const msg = compile('I have {fruits | plural, one:"1 fruit", other:"# fruits"}')

msg({ fruits: 0 }) // "I have 0 fruits"
msg({ fruits: 1 }) // "I have 1 fruit"
msg({ fruits: 2 }) // "I have 2 fruits"

date

Formats date using Intl.DateTimeFormat. All options are available as arguments in pipes.

const msg = compile('Todays date {now | date}')

msg({ now: new Date('1977-05-25') }) // "Todays date 25/05/1977"

time

Formats time using Intl.DateTimeFormat. All options are available as arguments in pipes.

const msg = compile('Currently it is {now | time}')

msg({ now: new Date('1983-05-25 16:42') }) // "Currently it is 16:42:00"

API

MessagePipe

This is the main function that takes in all the transformers that will be available to all the messages.

function MessagePipe(transformers?: MessagePipeTransformers): {
  compileRaw(message: string): (props?: Record<string, any>) => string[]
  compile(message: string): (props?: Record<string, any>) => string
}

Example usage:

const messagePipe = MessagePipe({
  hello: (value) => `Hello ${value}!`,
})

Now all the messages that get compiled from messagePipe can use this transformer like so "{name | hello}".

compile

This is where given message gets parsed and prepared for usage. It is very efficient compiler that does only 1 pass and prepares very tiny and performant function from it.

Given this message "Hello {name | capitalize}!", compiler will output this function (a) => "Hello " + capitalize(a.name) + "!" and that is the only thing that runs when executing it. No hidden performance penalties.

compileRaw

This is practically the same as compile but instead of it returning one string, it returns array of all of the things as a separate chunks so that this compiler can be used as part of React component for example.

So from the example that was before, the output of that message would be (a) => ["Hello ", capitalize(a.name), "!"].

Benchmarks

It is necessary for me that this library is as small and as fast as possible. Since this library compares directly with MessageFormat, I treated both as equal in benchmarks.

Message MessageFormat MessagePipe Improvement
"Wow" 926,368 ops/s 1,847,253 ops/s 2x
"Hello {planet}" 560,131 ops/s 1,024,051 ops/s 1.8x
select transformer 209,513 ops/s 337,226 ops/s 1.6x

Framework integration

Works with React and Preact out of the box. Just swap out compile with compileRaw and good to go. This works because it returns raw array of values that was the output of selectors and transformers.

import { MessagePipe } from 'messagepipe'

function Mention(username) {
  const {href} = useUser(username)

  return <a href={href}>{username}</a>
}

// We use React/Preact component as a transformer
const { compileRaw } = MessagePipe({ Mention })
const msg = compileRaw('Hello {name | Mention}!')

function App() {
  return <div>{msg({name: 'john'})}</div>
} // => "<div>Hello <a href="...">john</a>!</div>"

Live demo on Stackblitz.

Since we used compileRaw, library would output something like this: ['Hello ', [ReactElement], '!'].

This will work with any kind of framework or custom library.

Motivation

I was used to messageformat being the go to for this sort of stuff, but it has big flaws in the spec and library maintainers obviously wouldn't want to deviate from it. So the goal for messagepipe was to create NEW spec that solves all of the issues with it + must be faster & smaller.

One immediate flaw that MessagePipe solves is ability to select nested values and build dynamic messages.

License

MIT © Marcis Bergmanis

You might also like...

This is just a script I put together to check and notify me via email (MailGun) when there's an earlier date before my initial appointment date. It doesn't handle rescheduling.

This is just a script I put together to check and notify me via email (MailGun) when there's an earlier date before my initial appointment date. It doesn't handle rescheduling.

US-visa-appointment-notifier This is just a script I put together to check and notify me via email (MailGun) when there's an earlier date before my in

Jan 4, 2023

Picky is a jQuery plugin that provides simple client-side date validation when entering dates using select tags.

jquery.picky.js Picky is a jQuery plugin that provides simple client-side date validation when entering dates using select tags. Features Instead of g

Apr 25, 2021

A javascript based whatsapp bot for downloading and sending media from youtube and facebook in different formats alongwith couple of other features.

Whatsmazan Available Features Downlaod youtube mp4 Video and send Downlaod youtube mp3 audio and send Search something from youtube Downlaod facebook

Oct 30, 2022

Random Fractals stash of Observable Data Tools 🛠️ and Notebooks 📚 in ES Modules .js, .nb.json, .ojs, .omd, .html and .qmd document formats for Data Previews

Random Fractals stash of Observable Data Tools 🛠️ and Notebooks 📚 in ES Modules .js, .nb.json, .ojs, .omd, .html and .qmd document formats for Data Previews

Random Fractals stash of Observable Data Tools 🛠️ and Notebooks 📚 in ES Modules .js, .nb.json, .ojs, .omd, .html and .qmd document formats for Data Previews in a browser and in VSCode IDE with Observable JS extension, Quarto extension, and new Quarto publishing tools.

Nov 25, 2022

front.phone is a Javascript library that identifies, validates and formats phone numbers.

front.phone front.phone is a Javascript library that identifies, validates and formats phone numbers. Demo The main goal of this project is to create

Oct 27, 2022

Ordered lists, flat or nested, multiple formats ordered lists.

Ordered lists, flat or nested, multiple formats ordered lists.

logseq-plugin-ol 有序列表,单级或多级、多种样式的有序列表。 Ordered lists, flat or nested, multiple formats ordered lists. 使用展示 (Usage) 在想要展示为有序列表的块上添加一个以 #.ol 开头的标签就可以了。有

Jan 1, 2023

devDependency to test feed formats for ssb-db2

ssb-feed-format A tool that you install as a devDependency to check whether your feed format for SSB is correct and ready to be installed in ssb-db2.

Jun 18, 2022

Chromium extension for displaying all the available formats of an AppleMusic album.

Chromium extension for displaying all the available formats of an AppleMusic album.

AppleMusic-Formats-Extension Chromium extension for displaying all the available formats of an AppleMusic album. Before Vs After How to Install? From

Dec 16, 2022

Markdown Transformer. Transform markdown files to different formats

Mdtx Inspired by generative programming and weed :). So I was learning Elm language at home usually in the evening and now I am missing all this gener

Jan 2, 2023
Releases(v0.2.3)
  • v0.2.3(Aug 29, 2022)

    • Adds deno support;
    • Adds cache; This improves performance significantly. There's currently only one (to me) known package that does something like this. So I did a bench against that:

    With Cache enabled

    messagepipe + cache ........... 108,929,836 ops/sec ±0.12% (100 runs sampled)
    format-message + cache ........ 23,294,473 ops/sec ±0.08% (100 runs sampled)
    

    Without Cache

    messagepipe ................... 920,184 ops/sec ±0.44% (98 runs sampled)
    format-message ................ 356,317 ops/sec ±49.86% (38 runs sampled)
    

    Full Changelog: https://github.com/Marcisbee/messagepipe/compare/v0.2.2...v0.2.3

    Source code(tar.gz)
    Source code(zip)
  • v0.2.2(Aug 27, 2022)

  • v0.2.1(Aug 27, 2022)

  • v0.2.0(Aug 27, 2022)

  • v0.1.1(Aug 23, 2022)

  • v0.1.0(Aug 23, 2022)

Owner
Marcis Bergmanis
Front end enthusiast, creator of Exome
Marcis Bergmanis
Downloads & formats all of your Roblox private messages.

Roblox Message Downloader This tool was created due to the recent news sent out by Roblox. On April 22nd, 2022, all private messages sent by Roblox be

Raincoat Games 2 Apr 7, 2022
Multiplies a number by zero. Useful for when you need to multiply a number by zero

multiply-by-zero Multiplies a number by zero. Useful for when you need to multiply a number by zero Please consider checking out the links of this pro

Dheirya Tyagi 2 Jul 3, 2022
jquery-input-mask-phone-number.js - A simple, easy jquery format phone number mask library

jquery-input-mask-phone-number A jQuery Plugin to make masks on input field to US phone format. Quick start 1. Add latest jQuery and jquery-input-mask

Raja Rama Mohan Thavalam 12 Aug 25, 2022
Modern approach to Low Quality Image Placeholders (LQIP) using webp and sharp.

lqip-modern Modern approach to Low Quality Image Placeholders (LQIP) using webp and sharp. (demo) This approach is extremely fast and produces much sm

Travis Fischer 187 Dec 30, 2022
Holder renders image placeholders in browser using SVG

Holder Holder renders image placeholders in browser using SVG. Used by thousands of open source projects (including Bootstrap v3) and many other sites

Ivan Malopinsky 5.8k Jan 2, 2023
Unread-Messages.js is a lightweight library that lets a user add floating number notifications to any object.

Unread-Messages.js About Unread-Messages.js is a lightweight library that lets a user add mobile-like notification counter badge to any object with ju

Mulaza Jacinto 2 Dec 18, 2021
An esbuild plugin to inject your application's version number or today's date into your files

esbuild-plugin-version-injector An esbuild plugin to inject your application's version number or today's date into your files This plugin was inspired

Favware 6 Dec 6, 2022
VanillaSelectBox - A dropdown menu with lots of features that takes a select tag and transforms it into a single or multi-select menu with 1 or 2 levels

vanillaSelectBox v1.0.0 vanillaSelectBox : v1.00 : Adding a package.json file New : Possibility to change the dropdown tree and change the remote sear

philippe MEYER 103 Dec 16, 2022
this is a single-page web application. we built a book website where the user can add , remove and display books. we used modules to implement these functionalities. also, we used the Date class to display the date and time.

Awsome Books In this Project, we have built A Books websites. Built With ?? HTML CSS javascript Git & Github Live Demo Here you can find the live Demo

Nedjwa Bouraiou 10 Aug 3, 2022
A chat logs online saver for discord bots to save messages history & cleared messages online

Chat Logs NPM package that saves messages online to view it later Useful for bots where users can save messages history & cleared messages online Supp

TARIQ 8 Dec 28, 2022