{"id":28930537,"url":"https://github.com/signavio/i18n","last_synced_at":"2025-07-24T20:34:28.417Z","repository":{"id":39834269,"uuid":"46727585","full_name":"signavio/i18n","owner":"signavio","description":"Minimalist gettext style i18n for JavaScript","archived":false,"fork":false,"pushed_at":"2025-06-12T00:13:28.000Z","size":1712,"stargazers_count":15,"open_issues_count":20,"forks_count":3,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-06-12T01:26:48.440Z","etag":null,"topics":["convenience","i18n","interpolation","javascript","pluralization","po-files","po-loader","react","react-i18n","react-intl","release-relevant","translation"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/signavio.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":"AUTHORS","dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2015-11-23T15:00:24.000Z","updated_at":"2025-05-19T09:33:27.000Z","dependencies_parsed_at":"2023-09-27T21:28:26.542Z","dependency_job_id":"d6a38055-dd9b-45fd-9d4a-e542c8c037a2","html_url":"https://github.com/signavio/i18n","commit_stats":{"total_commits":161,"total_committers":21,"mean_commits":7.666666666666667,"dds":0.7267080745341614,"last_synced_commit":"fc6be2a96c6be1b41c7bb1ab8ed4a6abb9a48374"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"purl":"pkg:github/signavio/i18n","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/signavio%2Fi18n","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/signavio%2Fi18n/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/signavio%2Fi18n/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/signavio%2Fi18n/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/signavio","download_url":"https://codeload.github.com/signavio/i18n/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/signavio%2Fi18n/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261311763,"owners_count":23139485,"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","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":["convenience","i18n","interpolation","javascript","pluralization","po-files","po-loader","react","react-i18n","react-intl","release-relevant","translation"],"created_at":"2025-06-22T15:09:44.663Z","updated_at":"2025-07-24T20:34:28.404Z","avatar_url":"https://github.com/signavio.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @signavio/i18n\n\n[![CircleCI][build-badge]][build]\n[![npm package][npm-badge]][npm]\n\nMinimalist gettext style i18n for JavaScript\n\n## Features\n\n- [Supports React components as interpolations](#interpolations)\n- [Pluralization support](#pluralization) (ngettext style)\n- [markdown support](#markdown)\n- [replacements support](#replacements)\n- Compatible with [webpack po-loader](https://github.com/perchlayer/po-loader)\n- Comes with scripts for extracting translation strings from JavaScript (Babel) sources and updating .pot and .po files\n\n## Installation\n\n```shell\nyarn add @signavio/i18n\n```\n\n## Setup\n\nAdd a section like the following to your `packages.json`:\n\n```json\n{\n  \"scripts\": {\n    \"i18n-init\": \"cd src/locales \u0026\u0026 msginit --no-translator --input messages.pot --locale\",\n    \"i18n\": \"i18n-extract \\\"src/**/*.js\\\" src/locales/messages.pot \u0026\u0026 i18n-merge src/locales/messages.pot src/locales/*.po\"\n  }\n}\n```\n\nCreate the file `.i18nrc` and add a configuration object for gettext message extraction:\n\n```json\n{\n  \"headers\": \"\u003cPOT_HEADERS\u003e\",\n  \"fileName\": \"\u003cPATH_TO_POT\u003e\",\n  \"baseDirectory\": \"\u003cPATH_TO_BASEDIR\u003e\"\n}\n```\n\n**IMPORTANT:** when the second command line argument is passed to the `i18n-extract` command, it will overwrite the `fileName` field of the `.i18nrc` config.\n\nMore available options are documented here: https://github.com/getsentry/babel-gettext-extractor\n\nOptionally, you can also define your babel configuration in the `.i18nrc` file.\nThis allows you to ignore your project's `.babelrc` file when extracting\nmessages, which is helpful if your project is using a legacy version of babel\n(\\\u003c6).\n\n```javascript\n{\n  \"fileName\": \"\u003cPATH_TO_POT\u003e\",\n  \"babel\": {\n    \"babelrc\": false,\n    // other babel settings\n  }\n}\n```\n## Extraction\n`i18n-extract` command will extract up to first two string arguments of the `i18n` call and treat them as translation keys.\nMessage context property will be extracted in the same way as well.\n\n\nHaving the original JS code: \n```javascript\n// translators: singular\ni18n('Translation');\n\n// translators: plural\ni18n('Another translation', 'Another translations');\n\n// translators: context\ni18n('Translation', {context: 'button'});\n\n```\nThe .pot file will have the following content\n\n```\n#. singular\nmsgid \"Translation\"\nmsgstr \"\"\n\n#. plural\nmsgid \"Another translation\"\nmsgid_plural \"Another translations\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n\n#. context\nmsgctxt \"button\"\nmsgid \"Translation\"\nmsgstr \"\"\n```\n\n**IMPORTANT:** only the following parameters will be extracted:\n- String literals (`'single'` and `\"double\"` quotes)\n- Simple template literals (``backticks`` without any nested expressions and tags)\n- Their concatenation with `+` operator\n\nOtherwise the translation function call will be omitted.\n\nThese translations will be extracted:\n```javascript\n\ni18n('Translation');\ni18n(\"Translation\");\ni18n(`Translation`);\n\n\ni18n('Translation ' + \"with\" + ` concatenation`);\ni18n('Translation ' + \"with\" + ` concatenation`, 'Translation ' + \"with\" + ` concatenation plural`);\n\n```\n\nThese contexts will be extracted:\n```javascript\ni18n(\"Translation\", {context: 'button'});\ni18n(\"Translation\", {context: \"button\"});\ni18n(\"Translation\", {context: `button`});\n\n\ni18n(\"Translation\", {context: 'context' + ` with ` + \"concatenation\"});\n```\n\nThese translations will be ignored:\n\n```javascript\n\ni18n('Translation' + 123);\ni18n(\"Translation\" + {});\ni18n(\"Translation\" + foobar);\ni18n(`Translation${\"nested expression with string\"}`);\ni18n(`Translation${foobar}`);\ni18n(someTag`Translation`);\n\ni18n('Translation ' + \"with\" + ` concatenation`);\ni18n('Translation ' + \"with\" + ` concatenation`, 'Translation ' + \"with\" + ` concatenation plural`);\n```\n\nThese contexts will be ignored:\n\n```javascript\n\ni18n(\"Translation\", {context: 'button' + 123});\ni18n(\"Translation\", {context: \"button\" + {}});\ni18n(\"Translation\", {context: \"button\" + foobar});\ni18n(\"Translation\", {context: `button ${'nested expression with string'}`});\ni18n(\"Translation\", {context: `button ${foobar}`});\ni18n(\"Translation\", {context: 'context' + ` with ` + \"concatenation\"});\n```\n\n**IMPORTANT:** when the translation key is valid and the context is not, the translation will be extracted without context.\n\n\n## Usage\n\nAdd the translations to the PO files, and initialize the i18n module in your application using the `init` function:\n\n```javascript\nimport i18n, { init, setLocale } from '@signavio/i18n'\n\nfunction getLangLoader(locale) {\n  // Lazy load the translation bundles\n  return require(`bundle?lazy!json!po!./locales/${locale}.po`)\n}\n\nconst config = {\n  // the default locale to use if the browser preference locale is not available\n  default: 'en_US',\n  // optional mapping of locales\n  map: {\n    en: 'en_US',\n    de: 'de_DE',\n  },\n  // optional regular expression pattern for custom interpolation syntax\n  interpolationPattern: '__(\\\\w+)__', // this is the default value\n}\n\ninit(getLangLoader, config).then(() =\u003e {\n  // promise will be resolved when the translation bundle for the active locale has been loaded\n  alert(i18n('Hello world!'))\n  // \u003e\u003e Hello world!\n\n  // switch to another language\n  setLocale('de').then(() =\u003e {\n    alert(i18n('Hello world!'))\n    // \u003e\u003e Hallo Welt!\n  })\n})\n```\n\n### Interpolations\n\nInterpolations make it easier to include variable content into messages without confusing translators.\nFor instance, if you want to include a computed number in a message, you can do it like this:\n\n```javascript\nconst available = 100\nconst count = available / 10\n\ni18n('Showing __count__ of __available__ entires.', { count, available })\n```\n\nFor your convenience interpolations also support React elements.\nSo you can do things like:\n\n```jsx\ni18n('Contact __supportLink__', {\n  supportLink: \u003ca href=\"mailto:support@signavio.com\"\u003eSupport\u003c/a\u003e,\n})\n```\n\nThe default syntax for interpolations is a group of characters or numbers (`\\w+`) wrapped in double underscores (`__`). Note that the identifiers `context` and `markdown` are reserved and should not be used as a placeholder (see docs below).\n\nIf you require a different syntax this can be customized using the init option `interpolationPattern`. Internally, pattern value will be used to create a regular expression for matching interpolation placeholder like this:\n\n```\nnew RegExp(interpolationPattern, 'g')\n```\n\nIt must contain a capturing group (`(\\w+)`) for capturing the interpolation key.\n\n### Pluralization\n\nOften times you get to the situation that the same message needs to look slightly different depending on whether you talk about one or more things.\nHandling this can add quite a lot of unnecessary code.\nYou can circumvent this with the built in support for pluralizations.\n\n```javascript\ni18n('Showing __count__ item', 'Showing __count__ items', { count })\n```\n\nTo use this feature simply pass two different translations to the `i18n` function.\nThe first string is used for the singular case (`count == ±1` ) and the second one for the plural case.\nNote that you **have** to hand in a variable called `count`.\nThis variable is used to decide which version of the translation to choose.\n\nYou can also use multiple variables, but only the `count` variable is used to select the translation:\n\n```javascript\ni18n(\n    'Showing __count__ item out of __total__',\n    'Showing __count__ items out of __total__',\n    { count, total }\n)\n```\n\n### Message context\n\nSometimes the same translation key can have different meanings based on the context in which is it used.\nMessage context offers a solution to this problem.\nIf you specify the optional `context` parameter you can have different translations for the same translation key.\n\n```javascript\ni18n('Ok', { context: 'button' })\n```\n\n### Markdown\n\nAnother convenience of `@signavio/i18n` is the optional support for markdown in translations.\nBy default this is turned off, but you can activate it by setting the `markdown` option to `true`.\n\n```javascript\ni18n('I want _this_ to be **bold**', {\n  markdown: true,\n})\n```\n### Replacements\n\n\n#### Extraction\n\nSometimes there may be cases when you need to rename several entities across the whole project, but keep the both versions of translations and show them based on the feature flag.\nDoing that manually could be laborious, that's why `i18n-extract` supports a path to the replacements json as an optional third command line argument.\n\n```json\n{\n  \"scripts\": {\n    \"i18n-init\": \"cd src/locales \u0026\u0026 msginit --no-translator --input messages.pot --locale\",\n    \"i18n\": \"i18n-extract \\\"src/**/*.js\\\" src/locales/messages.pot \u003cPATH_TO_REPLACEMENTS_JSON\u003e \u0026\u0026 i18n-merge src/locales/messages.pot src/locales/*.po\"\n  }\n}\n```\n\nReplacements json file should be an object where the keys are translation context names.\nValues of each context are objects for the original translation strings and the values are the new ones.\n\n```json\n{\n  \"some context\": {\n    \"Old translation\": \"New translation\",\n    \"Old translation2\": \"New translation2\"\n  }\n}\n```\n\nIf the translation does not have the context, then it should be stored in the `\"\"` (empty string) field.\n\n```json\n{\n  \"\": {\n    \"Old translation\": \"New translation without context\"\n  },\n  \"some context\": {\n    \"Old translation\": \"New translation with some context\"\n  }\n}\n```\n\nHaving the original JS code:\n\n```javascript\n// translators: comment 1\ni18n('Old translation')\n// translators: comment 2\ni18n('Old translation2')\n```\n\nThe .pot file will have the following content\n\n```\n#. comment 1\nmsgid \"Old translation\"\nmsgstr \"\"\n\n#. comment 1 REPLACEMENT for \"New translation\"\nmsgid \"New translation\"\nmsgstr \"\"\n\n#. comment 2\nmsgid \"Old translation2\"\nmsgstr \"\"\n\n#. comment 2 REPLACEMENT for \"New translation2\"\nmsgid \"New translation2\"\nmsgstr \"\"\n\n```\n\n#### Usage\n\nTo use the replacements instead of the old strings in runtime pass the same replacements object which was used in the extraction step to the `init` function:\n\n```javascript\nimport replacements from 'replacements.json'\ninit(getLangLoader, config, replacements).then(() =\u003e {\n  // promise will be resolved when the translation bundle for the active locale has been loaded\n  alert(i18n('Hello world!'))\n  // \u003e\u003e Hello world!\n\n  // switch to another language\n  setLocale('de').then(() =\u003e {\n    alert(i18n('Hello world!'))\n    // \u003e\u003e Hallo Welt!\n  })\n})\n```\n\n### Publishing Packages\n\nRead the following [guide](PUBLISH.md) to publish packages of the i18n repository\n\n[build-badge]: https://circleci.com/gh/signavio/i18n/tree/master.svg?style=shield\u0026circle-token=:circle-token\n[build]: https://circleci.com/gh/signavio/i18n/tree/master\n[npm-badge]: https://img.shields.io/npm/v/@signavio/i18n.png?style=flat-square\n[npm]: https://www.npmjs.org/package/@signavio/i18n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsignavio%2Fi18n","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsignavio%2Fi18n","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsignavio%2Fi18n/lists"}