• Stars
    star
    148
  • Rank 241,724 (Top 5 %)
  • Language
    C#
  • License
    MIT License
  • Created over 9 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

ICU MessageFormat implementation for .NET.

MessageFormatter for .NET

- better UI strings.

Build

This is an implementation of the ICU Message Format in .NET. For official information about the format, go to: http://userguide.icu-project.org/formatparse/messages

Quickstart

var mf = new MessageFormatter();

var str = @"You have {notifications, plural,
              zero {no notifications}
               one {one notification}
               =42 {a universal amount of notifications}
             other {# notifications}
            }. Have a nice day, {name}!";
var formatted = mf.FormatMessage(str, new Dictionary<string, object>{
  {"notifications", 4},
  {"name", "Jeff"}
});

//Result: You have 4 notifications. Have a nice day, Jeff!

Or, if you don't like dictionaries, and don't mind a bit of reflection..

var formatted = mf.FormatMessage(str, new {
  notifications = 0,
  name = "Jeff"
});

//Result: You have no notifications. Have a nice day, Jeff!

You can use a static method, too:

var formatted = MessageFormatter.Format(str, new {
  notifications = 1,
  name = "Jeff"
});

//Result: You have one notification. Have a nice day, Jeff!

Installation

Either clone this repo and build it, or install it with NuGet:

Install-Package MessageFormat

Features

  • It's fast. Everything is hand-written; no parser-generators, not even regular expressions.
  • It's portable. The library is targeting .NET Standard 2.0.
  • It's compatible with other implementations. I've been peeking a bit at the MessageFormat.js library to make sure the results would be the same.
  • It's (relatively) small. For a .NET library, ~25kb is not a lot.
  • It's very white-space tolerant. You can structure your blocks so they are more readable - look at the example above.
  • Nesting is supported. You can nest your blocks as you please, there's no special structure required to do this, just ensure your braces match.
  • Adding your own formatters. I don't know why you would need to, but if you want, you can add your own formatters, and take advantage of the code in my base classes to help you parse patterns. Look at the source, this is how I implemented the built-in formatters.
  • Exceptions make at least a little sense. When exceptions are thrown due to a bad pattern, the exception should include useful information.
  • There are unit tests. Run them yourself if you want, they're using XUnit.
  • Built-in cache. If you are formatting messages in a tight loop, with different data for each iteration, and if you are reusing the same instance of MessageFormatter, the formatter will cache the tokens of each pattern (nested, too), so it won't have to spend CPU time to parse out literals every time. I benchmarked it, and on my monster machine, it didn't make much of a difference (10000 iterations).
  • Built-in pluralization formatters. Generated from the CLDR pluralization rule data.

Performance

If you look at MessageFormatterCachingTests, you will find a "with cache" and "without cache" test.

My machine runs on a Core i7 3960x, and with about 100,000 iterations with random data (generated beforehand), it takes about 2 seconds (1892ms) with the cache, and about 3 seconds (3236ms) without it. These results are with a debug build, when it is in release mode the time taken is reduced by about 40%! :)

Supported formats

Basically, it should be able to do anything that MessageFormat.js can do.

  • Select Format: {gender, select, male{He likes} female{She likes} other{They like}} cheeseburgers
  • Plural Format: There {msgCount, plural, zero {are no unread messages} one {is 1 unread message} other{are # unread messages}}. (where # is the actual number, with the offset (if any) subtracted).
  • Simple variable replacement: Your name is {name}

Adding your own pluralizer functions

Same thing as with MessageFormat.js, you can add your own pluralizer function. The Pluralizers property is a IDictionary<string, Pluralizer>, so you can remove the built-in ones if you want.

var mf = new MessageFormatter();
mf.Pluralizers.Add("<locale>", n => {
  // ´n´ is the number being pluralized.
  if(n == 0)
    return "zero";
  if(n == 1)
    return "one";
  return "other";
});

There's no restrictions on what strings you may return, nor what strings you may use in your pluralization block.

var mf = new MessageFormatter(true, "en"); // true = use cache
mf.Pluralizers["en"] = n =>
{
    // ´n´ is the number being pluralized.
    if (n == 0)
        return "zero";
    if (n == 1)
        return "one";
    if (n > 1000)
        return "thatsalot";
    return "other";
};

mf.FormatMessage("You have {number, plural, thatsalot {a shitload of notifications} other {# notifications}}", new Dictionary<string, object>{
  {"number", 1001}
});

Escaping literals

Simple - the literals are {, } and # (in a plural block). If literals occur in the text portions, then they need to be quoted by enclosing them in pairs of single quotes ('). A pair of single quotes always represents one single quote ('' -> '), which still applies inside quoted text. (This '{isn''t}' obvious → This {isn't} obvious)

Anything else?

There's not a lot - Alex Sexton of MessageFormat.js did a great job documenting his library, and like I said, I wrote my implementation so it would be (somewhat) compatible with his.

Bugs / issues

If you have issues with the library, and the exception makes no sense, please open an issue and include your message, as well as the data you used.

Author

I'm Jeff Hansen, a software developer who likes to fiddle with string parsing when it is not too difficult. I also do a lot of ASP.NET Web API back-end development, and quite a bit of web front-end stuff.

You can find me on Twitter: @jeffijoe.

More Repositories

1

awilix

Extremely powerful Inversion of Control (IoC) container for Node.JS
TypeScript
2,999
star
2

typesync

Install missing TypeScript typings for dependencies in your package.json.
TypeScript
1,390
star
3

koa-es7-boilerplate

A boilerplate for writing Koa 2 apps in ES7 with Babel.
JavaScript
274
star
4

mobx-task

Makes async function state management in MobX fun.
TypeScript
237
star
5

awilix-koa

Awilix helpers/middleware for Koa 2
TypeScript
129
star
6

yenv

Environment management for Node using YAML.
JavaScript
104
star
7

koa-respond

Koa middleware that adds useful methods to the context.
JavaScript
102
star
8

awilix-express

Awilix helpers/middleware for Express
TypeScript
101
star
9

libx

Collection + Model infrastructure for MobX applications.
TypeScript
99
star
10

httpclientgoodies.net

Useful utilities for the .NET HttpClient.
C#
62
star
11

snicket

Stream Store based on Postgres for Node.JS
TypeScript
39
star
12

screengun

A simple screen recorder for Windows based on ffmpeg
C#
39
star
13

fejl

Error-making utility for Node apps.
TypeScript
35
star
14

logpipe-server

The Logpipe server.
JavaScript
30
star
15

awilix-router-core

Reusable building blocks for writing Awilix router adapters for HTTP frameworks.
TypeScript
25
star
16

validx

Validation library for MobX.
TypeScript
23
star
17

keyblade

Fail fast when accessing undefined properties on objects.
JavaScript
14
star
18

npm-module-boilerplate

A boilerplate for authoring npm modules, with tests and linting.
JavaScript
10
star
19

skadi

A simple object validator/sanitizer based on `is-my-json-valid`.
JavaScript
9
star
20

deltio

A Google Cloud Pub/Sub emulator alternative, written in Rust.
Rust
8
star
21

dration

Duration utilities for JavaScript using primitive types.
TypeScript
6
star
22

icebug

A wrapper around node-inspector and nodemon.
JavaScript
4
star
23

jQuery-Validator.Knockout

KnockoutJS bindings for my jQuery-Validator.
JavaScript
4
star
24

bristol-sentry

Sentry integration for the Bristol logger
JavaScript
3
star
25

smid

Catches errors and returns them. Useful for unit testing.
TypeScript
3
star
26

baconpi-web

BaconPi Web Frontend + Backend
JavaScript
3
star
27

ts-module-boilerplate

TypeScript module boilerplate for Node packages
TypeScript
3
star
28

posish

A tool that makes counting string lines, columns and indexes awesome. Useful for writing parser tests.
JavaScript
2
star
29

total-rename

Utility to replace strings in files and paths.
Go
2
star
30

purge

Windows utility for deleting a directory tree with long paths.
C#
1
star
31

chilli

Rename files to sort-friendly numbers
F#
1
star
32

jQuery-Validator

A formless validation plugin for all kinds of validation - even your own!
JavaScript
1
star