{"id":31788328,"url":"https://github.com/marcisbee/messageformat-plus","last_synced_at":"2025-10-10T14:17:19.315Z","repository":{"id":315591119,"uuid":"1059490212","full_name":"Marcisbee/messageformat-plus","owner":"Marcisbee","description":"Messageformat with flexible parser and minimal spec deviation","archived":false,"fork":false,"pushed_at":"2025-09-29T07:50:30.000Z","size":44,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-29T08:27:52.020Z","etag":null,"topics":["messageformat"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Marcisbee.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-18T14:10:52.000Z","updated_at":"2025-09-29T07:49:31.000Z","dependencies_parsed_at":"2025-09-19T14:38:04.498Z","dependency_job_id":"2984fce3-4982-488b-815c-1fc9fa1ca732","html_url":"https://github.com/Marcisbee/messageformat-plus","commit_stats":null,"previous_names":["marcisbee/messageformat-plus"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/Marcisbee/messageformat-plus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Marcisbee%2Fmessageformat-plus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Marcisbee%2Fmessageformat-plus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Marcisbee%2Fmessageformat-plus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Marcisbee%2Fmessageformat-plus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Marcisbee","download_url":"https://codeload.github.com/Marcisbee/messageformat-plus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Marcisbee%2Fmessageformat-plus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279004176,"owners_count":26083688,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["messageformat"],"created_at":"2025-10-10T14:17:16.636Z","updated_at":"2025-10-10T14:17:19.301Z","avatar_url":"https://github.com/Marcisbee.png","language":"TypeScript","readme":"# messageformat-plus\n\n[![CI](https://img.shields.io/github/actions/workflow/status/Marcisbee/messageformat-plus/main.yml?branch=main\u0026style=flat-square)](https://github.com/Marcisbee/messageformat-plus/actions)\n[![npm](https://img.shields.io/npm/v/messageformat-plus.svg?style=flat-square)](https://www.npmjs.com/package/messageformat-plus)\n[![jsr](https://jsr.io/badges/@marcisbee/mf?style=flat-square)](https://jsr.io/@marcisbee/mf)\n[![package size](https://deno.bundlejs.com/?q=messageformat-plus\u0026badge=\u0026badge-style=flat-square)](https://bundlephobia.com/result?p=messageformat-plus)\n\nA lightweight, modern take on ICU MessageFormat-inspired message compilation\nwith a focus on:\n\n- Simplicity of authoring\n- Flexible path (dot \u0026 bracket) access\n- Extensible, pluggable formatters\n- Small, dependency‑lean parsing core\n\n## Quick Start\n\n```ts\nimport { MessageFormat } from \"messageformat-plus\"; // adjust path for your environment\n\nconst mf = new MessageFormat(\"en\", {\n  customFormatters: {\n    upcase: (v: unknown) =\u003e String(v).toUpperCase(),\n  },\n});\n\nconst greet = mf.compile(\"Hello {user.name, upcase}! Today is {today, date}.\");\n\nconsole.log(greet({\n  user: { name: \"Ada\" },\n  today: Date.now(),\n}));\n// → e.g. \"Hello ADA! Today is Feb 21, 2016\"\n```\n\n### Built-in Formatters\n\n| Name       | Syntax Examples                                                                                                     |\n| ---------- | ------------------------------------------------------------------------------------------------------------------- |\n| `date`     | `{d, date}`, `{d, date, short}`, `{d, date, full}`                                                                  |\n| `time`     | `{t, time}`, `{t, time, short}`, `{t, time, long}`                                                                  |\n| `duration` | `{s, duration}` (seconds -\u003e `H:MM:SS(.fff)`)                                                                        |\n| `number`   | `{n, number}`, `{n, number, integer}`, `{p, number, percent}`, `{v, number, currency}`, `{v, number, currency:EUR}` |\n| `select`   | `{g, select, male{He} female{She} other{They}}`, `{name, select, other{Hello {name}!}}`                             |\n\n(You can register your own via `customFormatters`.)\n\n## Usage Notes\n\n### Compiling \u0026 Reuse\n\n`compile()` returns a pure function. Reuse it for multiple param sets:\n\n```ts\nconst line = mf.compile(\"Hi {name, select, alice{Alice} other{User}}\");\nline({ name: \"alice\" }); // \"Hi Alice\"\nline({ name: \"bob\" }); // \"Hi User\"\n```\n\n### Variable Paths\n\nSupports mixed dot \u0026 bracket style:\n\n- `{user.name}`\n- `{user.profile[0].email}`\n- `{data.items[order.index].price}` (nested bracketed expression tokens are\n  parsed if they form a valid path sequence)\n\nMissing segments yield `undefined` (no crash).\n\n## How This Deviates From MessageFormat v1\n\nThis project intentionally **does not** aim for byte‑for‑byte compatibility with\nthe original `messageformat` v1 runtime. Key differences:\n\n1. More permissive path resolution Dot \u0026 bracket nesting (`a.b[c[d].e].f`) is\n   parsed into a uniform path array and resolved dynamically. Some syntactic\n   forms that classic MF might reject are accepted here.\n\n2. Formatter argument token flexibility Arguments like `currency:EUR` may arrive\n   from the parser as:\n   - `[\"currency:EUR\"]`\n   - `[\"currency\", \":\", \"EUR\"]`\n   - `[\"currency\", \":\", \"EUR\", \" \"]` The `number` formatter normalizes these\n     automatically.\n\n3. Select value coercion `select` coerces the incoming value to `String(value)`\n   so numeric option keys work: `{count, select, 1{One} other{Many}}`.\n\n4. Partial feature surface (lean core) Not (yet) implementing: `plural`,\n   `selectordinal`, rich nested message interpolation inside select arms, or\n   skeleton-based date/number formatting. Only a focused subset is provided.\n\n5. Enhanced argument parsing model Transformer (formatter) arguments are passed\n   as a structured array. Nested `{}` inside formatter args containing variables\n   are parsed recursively, allowing for dynamic content within select options\n   and other formatter arguments.\n\n6. Error handling Unknown formatter names throw early at runtime. Malformed\n   argument groupings default to a sane fallback rather than fail the whole\n   message.\n\n7. Escaping semantics Backslash escaping is pragmatic: `\\{` and `\\}` produce\n   literal braces. Behavior for some edge escape sequences may differ from\n   canonical ICU rules.\n\nIf you need strict ICU / MessageFormat v1 behavior, use the original library.\nThis project optimizes for ergonomic flexibility and a smaller mental model.\n\n## Custom Formatters\n\n```ts\nconst mf = new MessageFormat(\"en\", {\n  customFormatters: {\n    upper: (v) =\u003e String(v).toUpperCase(),\n  },\n});\n\nmf.compile(\"HELLO {x, upper}\")({ x: \"world\" }); // \"HELLO WORLD\"\n```\n\nA formatter signature:\n\n```ts\n((value: unknown, locale: string | string[], ...args: unknown[]) =\u003e string);\n```\n\n## Testing\n\nRun all tests:\n\n```\ndeno test -A\n```\n\n## Performance\n\nThe parser builds a compact JS expression and uses `new Function` bound with a\nlightweight context (`d` = dispatcher, `p` = path resolver, `l` = locale). This\nkeeps runtime evaluation fast after the initial compile.\n\n## Nested Variables in Formatter Arguments\n\nVariables can be nested within formatter arguments by wrapping them in braces:\n\n```ts\nconst mf = new MessageFormat(\"en\");\n\n// Simple select with nested variable\nconst greet = mf.compile(\"{name, select, other{Hello {name}!}}\");\ngreet({ name: \"Alice\" }); // \"Hello Alice!\"\n\n// More complex example with multiple nested variables\nconst status = mf.compile(`{user.role, select,\n  admin{Welcome {user.name}, you have {permissions.count} permissions}\n  user{Hi {user.name}!}\n  other{Hello guest}\n}`);\n\nstatus({\n  user: { name: \"Bob\", role: \"admin\" },\n  permissions: { count: 15 },\n}); // \"Welcome Bob, you have 15 permissions\"\n```\n\n## Caveats\n\n- Output can vary across environments due to `Intl` differences (especially\n  currency \u0026 time zone strings).\n\n## Contributing\n\nIssues \u0026 PRs welcome. Keep additions minimal \u0026 well‑tested. Preference is for\nincremental, opt‑in complexity rather than broad speculative feature builds.\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcisbee%2Fmessageformat-plus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcisbee%2Fmessageformat-plus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcisbee%2Fmessageformat-plus/lists"}