{"id":21126702,"url":"https://github.com/marviq/madlib-locale","last_synced_at":"2026-05-18T04:05:43.601Z","repository":{"id":18064594,"uuid":"21124151","full_name":"marviq/madlib-locale","owner":"marviq","description":"Handlebars helpers for i18n and l10n","archived":false,"fork":false,"pushed_at":"2020-10-05T06:32:14.000Z","size":465,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":7,"default_branch":"develop","last_synced_at":"2025-10-05T04:48:50.333Z","etag":null,"topics":["accounting","format","handlebars","handlebars-helpers","i18n","internationalization","l10n","localization","moment","polyglot","translate","translation"],"latest_commit_sha":null,"homepage":"","language":"CoffeeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marviq.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-06-23T11:40:40.000Z","updated_at":"2020-10-04T15:01:54.000Z","dependencies_parsed_at":"2022-07-13T05:40:27.768Z","dependency_job_id":null,"html_url":"https://github.com/marviq/madlib-locale","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/marviq/madlib-locale","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marviq%2Fmadlib-locale","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marviq%2Fmadlib-locale/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marviq%2Fmadlib-locale/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marviq%2Fmadlib-locale/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marviq","download_url":"https://codeload.github.com/marviq/madlib-locale/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marviq%2Fmadlib-locale/sbom","scorecard":{"id":622423,"data":{"date":"2025-08-11","repo":{"name":"github.com/marviq/madlib-locale","commit":"87166e1786d4cf747d1664e94402b5c08fe468e2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'develop'","Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"59 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc","Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx","Warn: Project is vulnerable to: GHSA-2j2x-2gpw-g8fm","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-957j-59c2-j692","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-j383-35pm-c5h4","Warn: Project is vulnerable to: GHSA-rm36-94g8-835r","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-jcpv-g9rr-qxrc","Warn: Project is vulnerable to: GHSA-44pw-h2cw-w3vq","Warn: Project is vulnerable to: GHSA-jp4x-w63m-7wgm","Warn: Project is vulnerable to: GHSA-c429-5p7v-vgjp","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-pfv6-prqm-85q8","Warn: Project is vulnerable to: GHSA-6vfc-qv3f-vr6c","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-wrvr-8mpx-r7pp","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-8hfj-j24r-96c4","Warn: Project is vulnerable to: GHSA-wc69-rhjr-hc9g","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-g6ww-v8xp-vmwg","Warn: Project is vulnerable to: GHSA-gqgv-6jq5-jjj9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-7xfp-9c55-5vqj","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg","Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p","Warn: Project is vulnerable to: GHSA-4rq4-32rv-6wp6","Warn: Project is vulnerable to: GHSA-64g7-mvw6-v9qj","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-38fc-wpqx-33j7","Warn: Project is vulnerable to: GHSA-xc7v-wxcw-j472","Warn: Project is vulnerable to: GHSA-cf4h-3jhx-xvhq","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T05:29:22.298Z","repository_id":18064594,"created_at":"2025-08-21T05:29:22.298Z","updated_at":"2025-08-21T05:29:22.298Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279001896,"owners_count":26083226,"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-09T02:00:07.460Z","response_time":59,"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":["accounting","format","handlebars","handlebars-helpers","i18n","internationalization","l10n","localization","moment","polyglot","translate","translation"],"created_at":"2024-11-20T04:44:53.885Z","updated_at":"2025-10-09T18:35:44.037Z","avatar_url":"https://github.com/marviq.png","language":"CoffeeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# madlib-locale\n\n[![npm version](https://badge.fury.io/js/madlib-locale.svg)](http://badge.fury.io/js/madlib-locale)\n[![David dependency drift detection](https://david-dm.org/marviq/madlib-locale.svg)](https://david-dm.org/marviq/madlib-locale)\n\nA [`Handlebars.js`](https://github.com/wycats/handlebars.js#readme) helper collection providing keyed dictionary substitution and simple localization.\n\nIt can format numbers, money, dates, and \"translate\" texts using the following packages:\n\n- [`accounting`](http://openexchangerates.github.io/accounting.js/)\n- [`moment`](http://momentjs.com/)\n- [`node-polyglot`](http://airbnb.io/polyglot.js/)\n\n\n## Installing\n\n```shell\nnpm install handlebars --save\nnpm install madlib-locale --save\n```\n\n\n## Using\n\n`madlib-locale`'s single export is the `localeManager` object, which will need to be `initialize( ... )`-ed before use. `initialize( ... )` returns a\n[(Q) `Promise`](https://github.com/kriskowal/q#readme) that'll be fullfilled when the specified locale's definition file has been loaded; it takes in three\nparameters:\n\n  * The [`Handlebars`-](http://handlebarsjs.com/installation.html#npm) (or [`hbsfy` runtime](https://github.com/epeli/node-hbsfy#helpers) that is to be\n    extended with `madlib-locale`'s helpers;\n  * The locale, expressed  as a valid [BCP 47 language tag](https://tools.ietf.org/html/bcp47#section-2) string; It'll designate a `.json` locale definition\n    file by the same name that is to be loaded;\n  * An optional url base path to retrieve that- and any future locale definition files from; it defaults to `'./i18n'`;\n\n```coffee\nHandlebars      = require( 'handlebars/runtime' )\nlocaleManager   = require( 'madlib-locale' )\n\nlocaleManager\n    .initialize( Handlebars, 'en-GB', '/examples' )\n    .then(\n\n        () -\u003e\n\n            ##  Ready to render templates using the helper functions\n\n            return\n\n        () -\u003e\n\n            console.error( 'something went wrong...' )\n            return\n    )\n    .done()\n```\n\n\n### Change the locale\n\nThe locale can be changed at any time through invoking `localeManager.setLocale( ... )`; it, too, will return a `Promise`.  Once resolved, a re-rendering\nof your templates will ensure they'll be in the new locale.\n\n```coffee\nlocaleManager\n    .setLocale( 'nl-NL' )\n    .then(\n\n        () -\u003e\n\n            ##  Ready to re-render templates using the helper functions\n\n            return\n\n        () -\u003e\n            console.error( 'something went wrong...' )\n            return\n    )\n```\n\n\n### Get the current locale name\n\nTo retrieve the current locale name:\n\n```coffee\n    console.log( \"Current locale: #{ localeManager.getLocaleName() }\" )\n```\n\n\n### Set up a locale definition file\n\nAt its top level, a locale definition file has a `name` string, and `phrases`- and `formatting` objects.\n\n  * `name` is expected to be a valid [BCP 47 language tag](https://tools.ietf.org/html/bcp47#section-2) string.\n    This is also the name of the file (excluding the `.json` filename extension);\n  * \u003ca name=\"definition-phrases\"\u003e`phrases`\u003c/a\u003e is any object acceptable as a phrases dictionary to [`node-polyglot`](http://airbnb.io/polyglot.js/#translation);\n  * `formatting` should contain three further sections:\n      * \u003ca name=\"definition-datetime\"\u003e`datetime`\u003c/a\u003e is a keyword-to-[`Moment` `format( ... )` argument](http://momentjs.com/docs/#/displaying/format/)-mapping.\n        The examples unimaginatively sport descriptive identifying keywords like `date` and `datetime` but you can name them whatever you like;\n      * \u003ca name=\"definition-money\"\u003e`money`\u003c/a\u003e, similary, is a\n        keyword-to-[`Accounting` `formatMoney( ... )` arguments](http://openexchangerates.github.io/accounting.js/#methods)-mapping, expecting only `sign`\n        (currency symbol) and `precision` arguments. The arguments for thousands- and decimal separator markers being taken from the `number` definition below;\n      * \u003ca name=\"definition-number\"\u003e`number`\u003c/a\u003e is an object defining the `decimalMarker`, `thousandMarker` and (default) `precision` arguments to the\n        [`Accounting` `formatNumber( ... )`](http://openexchangerates.github.io/accounting.js/#methods) method;\n\n\nSee also the [examples](https://github.com/marviq/madlib-locale/tree/develop/examples/) on GitHub.\n\n\n### Use from your Handlebars templates\n\n  * Translate: `t` or `T`\n\n      * ... without interpolation\n\n        These helpers take one argument which should be a key into the [`phrases` dictionary](#definition-phrases) in your locale\n        definition file:\n\n        ```hbs\n        \u003cul\u003e\n            \u003cli\u003e{{T 'an.entry.in.your.phrases.dictionary'}}\u003c/li\u003e\n            \u003cli\u003e{{t 'another.entry.in.your.phrases.dictionary'}}\u003c/li\u003e\n        \u003c/ul\u003e\n        ```\n\n        The difference between `T` and `t` is that the former additionally does\n        [first-letter capitalization](https://github.com/epeli/underscore.string#capitalizestring-lowercaserestfalse--string) of the dictionary's value.\n\n        A longer form alternative to `t` which `madlib-locale` has historically provided is `_translate`. It does not have a capitalization variant.\n\n      * ... with interpolation\n\n        These helpers also support [`node-polyglot`'s interpolation](http://airbnb.io/polyglot.js/#interpolation); any additional _positional_ arguments will\n        be interpolated into the resulting dictionary value string as follows:\n\n        ```json\n        {\n            \"phrases\": {\n                \"the.phrases.dictionary.values.can.be.X.with.Y\":    \"translation strings can be %{0} with anything, like: \\\"%{1}\\\"\"\n            ,   \"can.be.interpolated\":                              \"interpolated\"\n            }\n        }\n        ```\n\n        ```hbs\n        {{T 'the.phrases.dictionary.values.can.be.X.with.Y' (t 'can.be.interpolated') some.example.value }}\n        ```\n\n      * ... with named parameters\n\n        Interpolations with _named_ instead of positional parameters are also possible:\n\n        ```json\n        {\n            \"phrases\": {\n                \"the.phrases.dictionary.values.can.be.X.with.Y\":    \"translation strings can be %{foo} with anything, like: \\\"%{bar}\\\"\"\n            ,   \"can.be.interpolated\":                              \"interpolated\"\n            }\n        }\n        ```\n\n        ```hbs\n        {{T 'the.phrases.dictionary.values.can.be.X.with.Y' foo=(t 'can.be.interpolated') bar=some.example.value }}\n        ```\n\n      * ... with pluralization\n\n        Using the special named parameter `smart_count` you can leverage [`node-polyglot`'s pluralization](http://airbnb.io/polyglot.js/#pluralization)\n        mechanism:\n\n        ```json\n        {\n            \"phrases\": {\n                \"some.mice\":    \"a mouse |||| some mice\"\n            }\n        }\n        ```\n\n        ```hbs\n        {{T 'some.cars' smart_count=1 }}\n        {{T 'some.cars' smart_count=42 }}\n        ```\n\n        _Note that even though `node-polyglot` does allow interpolation of the `smart_count` value, it will not receive any localized formatting treatment._\n\n  * Date: `D`\n\n    This helper takes two arguments:\n\n      * A key into the [`formatting.datetime`](#definition-datetime) section of your locale definition file, designating the specific format to use;\n      * Ideally a `Moment` instance, but any value that the [`Moment`](http://momentjs.com/docs/#/parsing/) constructor can grok as its argument should be\n        fine.\n\n    ```hbs\n    \u003cdl\u003e\n        \u003cdt\u003e{{T 'the.date'}}\u003c/dt\u003e\n        \u003cdd\u003e{{D 'date' some.moment.compatible.value.to.be.formatted.as.a.date.string }}\u003c/dd\u003e\n\n        \u003cdt\u003e{{T 'the.datetime'}}\u003c/dt\u003e\n        \u003cdd\u003e{{D 'datetime' some.moment.compatible.value.to.be.formatted.as.a.date-and-time.string }}\u003c/dd\u003e\n    \u003c/ul\u003e\n    ```\n\n    A longer form alternative to `D` which `madlib-locale` has historically provided is `_date`.\n\n  * Number: `N`\n\n    This helper takes one or two arguments:\n\n      * A number value to be formatted;\n      * An, optional, precision argument designating the specific number of decimals to format instead of the current locale definition's default.\n\n    ```hbs\n    \u003col\u003e\n        \u003cli\u003e{{N some.value.to.be.formatted.as.a.number.with.default.precision }}\u003c/li\u003e\n        \u003cli\u003e{{N some.value.to.be.formatted.as.a.number.with.alternative.precision 7 }}\u003c/li\u003e\n    \u003col\u003e\n    ```\n\n    A longer form alternative to `N` which `madlib-locale` has historically provided is `_number`.\n\n  * Money\n\n    This helper takes two arguments:\n\n      * A key into the [`formatting.money`](#definition-money) section of your locale definition file, designating the specific currency to use, or simply\n        `default` if the current locale defintion's default currenct is desired;\n      * A number value to be formatted as an amount, currency symbol included;\n\n    ```hbs\n    {{M 'euro' some.value.to.be.formatted.as.a.amount.of.money }}\n    ```\n\n    A longer form alternative to `M` which `madlib-locale` has historically provided is `_money`.\n\n\n## Contributing\n\nSee [CONTRIBUTING](./CONTRIBUTING.md).\n\n\n## Changelog\n\nSee [CHANGELOG](./CHANGELOG.md) for versions \u003e`0.2.1`;  For older versions, refer to the\n[releases on GitHub](https://github.com/marviq/madlib-locale/releases?after=v0.3.0) for a detailed log of changes.\n\n\n## License\n\n[BSD-3-Clause](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarviq%2Fmadlib-locale","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarviq%2Fmadlib-locale","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarviq%2Fmadlib-locale/lists"}