{"id":13411798,"url":"https://github.com/fnando/i18n-js","last_synced_at":"2026-10-18T20:20:22.662Z","repository":{"id":652455,"uuid":"294972","full_name":"fnando/i18n-js","owner":"fnando","description":"It's a small library to provide the I18n translations on the Javascript. It comes with Rails support.","archived":false,"fork":false,"pushed_at":"2026-07-14T18:42:21.000Z","size":2964,"stargazers_count":3810,"open_issues_count":6,"forks_count":504,"subscribers_count":48,"default_branch":"main","last_synced_at":"2026-10-12T14:24:25.501Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/fnando.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["fnando"],"custom":["https://paypal.me/nandovieira/🍕"]}},"created_at":"2009-09-02T03:35:45.000Z","updated_at":"2026-10-09T03:16:59.000Z","dependencies_parsed_at":"2024-01-13T17:55:32.835Z","dependency_job_id":"a09ae91d-4718-4ea8-bece-5b602fe1ae9f","html_url":"https://github.com/fnando/i18n-js","commit_stats":{"total_commits":81,"total_committers":8,"mean_commits":10.125,"dds":"0.13580246913580252","last_synced_commit":"1f3305ffa531614de22070ec3a0acbb592902636"},"previous_names":[],"tags_count":79,"template":false,"template_full_name":null,"purl":"pkg:github/fnando/i18n-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnando%2Fi18n-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnando%2Fi18n-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnando%2Fi18n-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnando%2Fi18n-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fnando","download_url":"https://codeload.github.com/fnando/i18n-js/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fnando%2Fi18n-js/sbom","scorecard":{"id":405585,"data":{"date":"2025-08-11","repo":{"name":"github.com/fnando/i18n-js","commit":"237b69b34f73873c2a3c753c45b208e670d15a31"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.6,"checks":[{"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":"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":10,"reason":"no dangerous workflow patterns detected","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":"Code-Review","score":2,"reason":"Found 6/28 approved changesets -- score normalized to 2","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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ruby-tests.yml:1","Info: no jobLevel write permissions found"],"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":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ruby-tests.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/fnando/i18n-js/ruby-tests.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ruby-tests.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/fnando/i18n-js/ruby-tests.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ruby-tests.yml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/fnando/i18n-js/ruby-tests.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ruby-tests.yml:47: update your workflow using https://app.stepsecurity.io/secureworkflow/fnando/i18n-js/ruby-tests.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ruby-tests.yml:56: update your workflow using https://app.stepsecurity.io/secureworkflow/fnando/i18n-js/ruby-tests.yml/main?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"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":"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":"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":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"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.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md: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 'main'"],"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 10 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T21:11:02.263Z","repository_id":652455,"created_at":"2025-08-18T21:11:02.263Z","updated_at":"2025-08-18T21:11:02.263Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":344794859,"owners_count":38327115,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-10-03T21:59:58.778Z","status":"online","status_checked_at":"2026-10-14T02:00:06.392Z","response_time":82,"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":[],"created_at":"2024-07-30T20:01:17.018Z","updated_at":"2026-10-18T20:20:22.656Z","avatar_url":"https://github.com/fnando.png","language":"Ruby","funding_links":["https://github.com/sponsors/fnando","https://paypal.me/nandovieira/🍕"],"categories":["Ruby","Time \u0026 Space","Libraries"],"sub_categories":["I18n","Push Notifications"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg width=\"250\" height=\"58\" src=\"https://github.com/fnando/i18n-js/raw/main/images/i18njs.png\" alt=\"i18n.js\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Export \u003ca href=\"https://rubygems.org/gems/i18n\"\u003ei18n\u003c/a\u003e translations to JSON.\n  \u003cbr\u003e\n  A perfect fit if you want to export translations to JavaScript.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003csmall\u003e\n    Oh, you don't use Ruby? No problem! You can still use i18n-js\n    \u003cbr\u003e\n    and the\n    \u003ca href=\"https://www.npmjs.com/package/i18n-js/v/latest\"\u003ecompanion JavaScript package\u003c/a\u003e.\n  \u003c/small\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/fnando/i18n-js\"\u003e\u003cimg src=\"https://github.com/fnando/i18n-js/workflows/ruby-tests/badge.svg\" alt=\"Tests\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://rubygems.org/gems/i18n-js\"\u003e\u003cimg src=\"https://img.shields.io/gem/v/i18n-js.svg\" alt=\"Gem\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://rubygems.org/gems/i18n-js\"\u003e\u003cimg src=\"https://img.shields.io/gem/dt/i18n-js.svg\" alt=\"Gem\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://tldrlegal.com/license/mit-license\"\u003e\u003cimg src=\"https://img.shields.io/:License-MIT-blue.svg\" alt=\"MIT License\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Installation\n\n```bash\ngem install i18n-js\n```\n\nOr add the following line to your project's Gemfile:\n\n```ruby\ngem \"i18n-js\"\n```\n\nCreate a default configuration file in ./config/i18n.yml\n\n```bash\ni18n init\n```\n\n## Usage\n\nAbout patterns:\n\n- Patterns can use `*` as a wildcard and can appear more than once.\n  - `*` will include everything\n  - `*.messages.*`\n- Patterns starting with `!` are excluded.\n  - `!*.activerecord.*` will exclude all ActiveRecord translations.\n- You can use groups:\n  - `{pt-BR,en}.js.*` will include only `pt-BR` and `en` translations, even if\n    more languages are available.\n\n\u003e [!NOTE]\n\u003e\n\u003e Patterns use [glob](https://rubygems.org/gems/glob), so check it out for the\n\u003e most up-to-date documentation about what's available.\n\nThe config file:\n\n```yml\n---\ntranslations:\n  - file: app/frontend/locales/en.json\n    patterns:\n      - \"*\"\n      - \"!*.activerecord.*\"\n      - \"!*.errors.*\"\n      - \"!*.number.nth.*\"\n\n  - file: app/frontend/locales/:locale.:digest.json\n    patterns:\n      - \"*\"\n```\n\nThe output path can use the following placeholders:\n\n- `:locale` - the language that's being exported.\n- `:digest` - the MD5 hex digest of the exported file.\n\nThe example above could generate a file named\n`app/frontend/locales/en.7bdc958e33231eafb96b81e3d108eff3.json`.\n\nThe config file is processed as erb, so you can have dynamic content on it if\nyou want. The following example shows how to use groups from a variable.\n\n```yml\n---\n\u003c% group = \"{en,pt}\" %\u003e\n\ntranslations:\n  - file: app/frontend/translations.json\n    patterns:\n      - \"\u003c%= group %\u003e.*\"\n      - \"!\u003c%= group %\u003e.activerecord.*\"\n      - \"!\u003c%= group %\u003e.errors.*\"\n      - \"!\u003c%= group %\u003e.number.nth.*\"\n```\n\n### Exporting locale.yml to locale.json\n\nYour i18n yaml file can be exported to JSON using the Ruby API or the command\nline utility. Examples of both approaches are provided below:\n\nThe Ruby API:\n\n```ruby\nrequire \"i18n-js\"\n\n# The following call performs the same task as the CLI `i18n export` command\nI18nJS.call(config_file: \"config/i18n.yml\")\n\n# You can provide the config directly using the following\nconfig = {\n  \"translations\"=\u003e[\n    {\"file\"=\u003e\"app/javascript/locales/:locale.json\", \"patterns\"=\u003e[\"*\"]}\n  ]\n}\n\nI18nJS.call(config: config)\n#=\u003e [\"app/javascript/locales/de.json\", \"app/javascript/locales/en.json\"]\n```\n\nThe CLI API:\n\n```console\n$ i18n --help\nUsage: i18n COMMAND FLAGS\n\nCommands:\n\n- init: Initialize a project\n- export: Export translations as JSON files\n- version: Show package version\n- plugins: List plugins that will be activated\n- lint:translations: Check for missing translations\n- lint:scripts: Lint files using TypeScript\n\nRun `i18n COMMAND --help` for more information on specific commands.\n```\n\nBy default, `i18n` will use `config/i18n.yml` and `config/environment.rb` as the\nconfiguration files. If you don't have these files, then you'll need to specify\nboth `--config` and `--require`.\n\n### Plugins\n\n#### Built-in plugins:\n\n##### `embed_fallback_translations`:\n\nEmbed fallback translations into each locale. This can be useful in cases where\nyou have multiple large translation files and don't want to load the default\nlocale together with the target locale.\n\nWhen `I18n.fallbacks` is configured (e.g. via `I18n::Backend::Fallbacks` or\nRails' `config.i18n.fallbacks`), the full fallback chain is respected. For\nexample, if `es` falls back to `pt` which falls back to `en`, missing `es`\ntranslations are first filled from `pt`, then from `en`. When `I18n.fallbacks`\nis not configured, translations fall back directly to `I18n.default_locale`.\n\nTo use it, add the following to your configuration file:\n\n```yaml\n---\npipeline:\n  - plugin: embed_fallback_translations\n    enabled: true\n```\n\n\u003e [!NOTE]\n\u003e\n\u003e `I18n.fallbacks=(*)` is weird. The most common way of using it is with\n\u003e `I18n.fallbacks[:es] = [:pt, :en]` and\n\u003e `I18n.fallbacks = I18n::Locale::Fallbacks(default_locale, hash_map)`. If you\n\u003e assign just a hash, no error will be raised, but it's not the correct\n\u003e behavior, and you'll get a hash back, rather than a `I18n::Locale::Fallbacks`\n\u003e instance.\n\u003e\n\u003e When assigning an array, remember to pass the default locale as the last\n\u003e argument, as precedence is from left to right.\n\n##### `export_files`:\n\nBy default, i18n-js will export only JSON files out of your translations. This\nplugin allows exporting other file formats. To use it, add the following to your\nconfiguration file:\n\n```yaml\n---\npipeline:\n  - plugin: export_files\n    enabled: true\n    files:\n      - template: path/to/template.erb\n        output: \"%{dir}/%{base_name}.ts\"\n```\n\nYou can export multiple files by defining more entries.\n\nThe output name can use the following placeholders:\n\n- `%{dir}`: the directory where the translation file is.\n- `%{name}`: file name with extension.\n- `%{base_name}`: file name without extension.\n- `%{digest}`: MD5 hexdigest from the generated file.\n\nThe template file must be a valid eRB template. You can execute arbitrary Ruby\ncode, so be careful. An example of how you can generate a file can be seen\nbelow:\n\n```erb\n/* eslint-disable */\n\u003c%= banner %\u003e\n\nimport { i18n } from \"config/i18n\";\n\ni18n.store(\u003c%= JSON.pretty_generate(translations) %\u003e);\n```\n\nThis template is loading the instance from `config/i18n` and storing the\ntranslations that have been loaded. The\n`banner(comment: \"// \", include_time: true)` method is built-in. The generated\nfile will look something like this:\n\n```typescript\n/* eslint-disable */\n// File generated by i18n-js on 2022-12-10 15:37:00 +0000\n\nimport { i18n } from \"config/i18n\";\n\ni18n.store({\n  en: {\n    \"bunny rabbit adventure\": \"bunny rabbit adventure\",\n    \"hello sunshine!\": \"hello sunshine!\",\n    \"time for bed!\": \"time for bed!\",\n  },\n  es: {\n    \"bunny rabbit adventure\": \"conejito conejo aventura\",\n    bye: \"adios\",\n    \"time for bed!\": \"¡hora de acostarse!\",\n  },\n  pt: {\n    \"bunny rabbit adventure\": \"a aventura da coelhinha\",\n    bye: \"tchau\",\n    \"time for bed!\": \"hora de dormir!\",\n  },\n});\n```\n\n#### Plugin API\n\nYou can transform the exported translations by adding plugins. A plugin must\ninherit from `I18nJS::Plugin` and can override 4 instance methods (they're all\noptional and default to a noop implementation). For real examples, see\n[lib/i18n-js/embed_fallback_translations_plugin.rb](https://github.com/fnando/i18n-js/blob/main/lib/i18n-js/embed_fallback_translations_plugin.rb)\nand\n[lib/i18n-js/export_files_plugin.rb](https://github.com/fnando/i18n-js/blob/main/lib/i18n-js/export_files_plugin.rb)\n\n```ruby\n# frozen_string_literal: true\n\nmodule I18nJS\n  class SamplePlugin \u003c I18nJS::Plugin\n    # This method is responsible for transforming the translations. The\n    # translations you'll receive may already be filtered by other plugins\n    # and by the default filtering itself. If you need to access the original\n    # translations, use `I18nJS.translations`.\n    def transform(translations:)\n      # transform `translations` here…\n\n      translations\n    end\n\n    # In case your plugin accepts configuration, this is where you must validate\n    # it, making sure only valid keys and types are provided.\n    # If the configuration contains invalid data, raise an exception using\n    # `raise I18nJS::Schema::InvalidError, error_message`.\n    # Paths passed to schema helpers are relative to the plugin's own config\n    # root (i.e. the pipeline stage object, minus the `plugin:` key).\n    def validate_schema\n      # validate plugin schema here…\n    end\n\n    # Called after schema validation. Use this for any one-time plugin setup.\n    def setup\n    end\n\n    # This method is called whenever `I18nJS.call(**kwargs)` finishes exporting\n    # JSON files based on your configuration.\n    #\n    # You can use it to further process exported files, or generate new files\n    # based on the translations that have been exported.\n    def after_export(files:)\n      # process exported files here…\n    end\n  end\nend\n```\n\nThe class `I18nJS::Plugin` exposes the following helpers:\n\n- `I18nJS::Plugin.key`: the configuration key inferred from your plugin's class\n  name (e.g. `SamplePlugin` → `\"sample\"`). Override this class method to use a\n  custom key.\n- `I18nJS::Plugin#config`: the plugin's own configuration hash (the pipeline\n  stage entry, minus the `plugin:` key).\n- `I18nJS::Plugin#main_config`: the full i18n-js configuration hash.\n- `I18nJS::Plugin#enabled?`: whether the plugin is enabled based on\n  `config[:enabled]`.\n\nPlugins are configured via the `pipeline:` array in the config file. Each entry\nmust have a `plugin:` key matching `Plugin.key` and an `enabled:` boolean. Any\nadditional keys in the stage are passed to the plugin as part of `config`.\n\n```yaml\npipeline:\n  - plugin: sample\n    enabled: true\n    my_option: value\n```\n\nTo distribute a plugin, create a gem whose load path contains a file matching\nthe pattern `i18n-js/*_plugin.rb`. You can verify it will be found by running\n`Gem.find_files(\"i18n-js/*_plugin.rb\")` in an IRB session after installing your\ngem.\n\n### Listing missing translations\n\nTo list missing and extraneous translations, you can use\n`i18n lint:translations`. This command will load your translations similarly to\nhow `i18n export` does, but will output the list of keys that don't have a\nmatching translation against the default locale. Here's an example:\n\n```console\n$ i18n lint:translations\n=\u003e Config file: \"./config/i18n.yml\"\n=\u003e Require file: \"./config/environment.rb\"\n=\u003e Check \"./config/i18n.yml\" for ignored keys.\n=\u003e en: 232 translations\n=\u003e pt-BR: 5 missing, 1 extraneous, 1 ignored\n   - pt-BR.actors.github.metrics (missing)\n   - pt-BR.actors.github.metrics_hint (missing)\n   - pt-BR.actors.github.repo_metrics (missing)\n   - pt-BR.actors.github.repository (missing)\n   - pt-BR.actors.github.user_metrics (missing)\n   - pt-BR.github.repository (extraneous)\n```\n\nThis command will `exit(missing_count)` whenever there are missing translations.\nThis way you can use it as a CI linting tool.\n\nYou can ignore keys by adding a list to the config file:\n\n```yml\n---\ntranslations:\n  - file: app/frontend/locales/en.json\n    patterns:\n      - \"*\"\n      - \"!*.activerecord.*\"\n      - \"!*.errors.*\"\n      - \"!*.number.nth.*\"\n\n  - file: app/frontend/locales/:locale.:digest.json\n    patterns:\n      - \"*\"\n\nlint_translations:\n  ignore:\n    - \"*.mailer.login.subject\"\n    - \"*.mailer.login.body\"\n```\n\n### Linting your JavaScript/TypeScript files\n\nTo lint your script files and check for missing translations (which can signal\nthat you're either using wrong scopes or forgot to add the translation), use\n`i18n lint:scripts`. This command will parse your JavaScript/TypeScript files\nand extract all scopes being used. This command requires a Node.js runtime. You\ncan either specify one via `--node-path`, or let the plugin infer a binary from\nyour `$PATH`.\n\nThe comparison will be made against the export JSON files, which means it'll\nconsider transformations performed by plugins (e.g. the output files may be\naffected by `embed_fallback_translations` plugin).\n\nThe translations that will be extract must be called as one of the following\nways:\n\n- `i18n.t(scope, options)`\n- `i18n.translate(scope, options)`\n- `t(scope, options)`\n\nNotice that only literal strings can be used, as in `i18n.t(\"message\")`. If\nyou're using dynamic scoping through variables (e.g.\n`const scope = \"message\"; i18n.t(scope)`), they will be skipped.\n\n```console\n$ i18n lint:scripts\n=\u003e Config file: \"./config/i18n.yml\"\n=\u003e Require file: \"./config/environment.rb\"\n=\u003e Node: \"/Users/fnando/.asdf/shims/node\"\n=\u003e Available locales: [:en, :es, :pt]\n=\u003e Patterns: [\"!(node_modules)/**/*.js\", \"!(node_modules)/**/*.ts\", \"!(node_modules)/**/*.jsx\", \"!(node_modules)/**/*.tsx\"]\n=\u003e 9 translations, 11 missing, 4 ignored\n   - test/scripts/lint/file.js:1:1: en.js.missing\n   - test/scripts/lint/file.js:1:1: es.js.missing\n   - test/scripts/lint/file.js:1:1: pt.js.missing\n   - test/scripts/lint/file.js:2:8: en.base.js.missing\n   - test/scripts/lint/file.js:2:8: es.base.js.missing\n   - test/scripts/lint/file.js:2:8: pt.base.js.missing\n   - test/scripts/lint/file.js:4:8: en.js.missing\n   - test/scripts/lint/file.js:4:8: es.js.missing\n   - test/scripts/lint/file.js:4:8: pt.js.missing\n   - test/scripts/lint/file.js:6:1: en.another_ignore_scope\n   - test/scripts/lint/file.js:6:1: es.another_ignore_scope\n```\n\nThis command will list all locales and their missing translations. To avoid\nlisting a particular translation, you can set `lint_scripts.ignore` or\n`lint_translations.ignore` in your config file.\n\n```yaml\n---\ntranslations:\n  - file: app/frontend/translations.json\n    patterns:\n      - \"*\"\n\nlint_scripts:\n  ignore:\n    - \"*.ignore_scope\" # will ignore this scope on all languages\n    - pt.another_ignore_scope # will ignore this scope only on `pt`\n```\n\nYou can also set the patterns that will be looked up. By default, it scans all\nJavaScript and TypeScript files that don't live on `node_modules`.\n\n```yaml\n---\ntranslations:\n  - file: app/frontend/translations.json\n    patterns:\n      - \"*\"\n\nlint_scripts:\n  patterns:\n    - \"app/assets/**/*.ts\"\n```\n\n## Automatically export translations\n\n### Using [watchman](https://facebook.github.io/watchman/)\n\nCreate a script at `bin/i18n-watch`.\n\n```bash\n#!/usr/bin/env bash\n\nroot=`pwd`\n\nwatchman watch-del \"$root\"\nwatchman watch-project \"$root\"\nwatchman trigger-del \"$root\" i18n\n\nwatchman -j \u003c\u003c-JSON\n[\n  \"trigger\",\n  \"$root\",\n  {\n    \"name\": \"i18n\",\n    \"expression\": [\n      \"anyof\",\n      [\"match\", \"config/locales/**/*.yml\", \"wholename\"],\n      [\"match\", \"config/i18n.yml\", \"wholename\"]\n    ],\n    \"command\": [\"i18n\", \"export\"]\n  }\n]\nJSON\n\n# If you're running this through Foreman,\n# then uncomment the following lines:\n# while true; do\n#   sleep 1\n# done\n```\n\nMake it executable with `chmod +x bin/i18n-watch`. To watch for changes, run\n`./bin/i18n-watch`. If you're using Foreman, make sure you uncommented the lines\nthat keep the process running (`while..`), and add something like the following\nline to your Procfile:\n\n```\ni18n: ./bin/i18n-watch\n```\n\n### Using [guard](https://rubygems.org/gems/guard)\n\nInstall [guard](https://rubygems.org/gems/guard) and\n[guard-compat](https://rubygems.org/gems/guard-compat). Then create a Guardfile\nwith the following configuration:\n\n```ruby\nguard(:\"i18n-js\",\n      run_on_start: true,\n      config_file: \"./config/i18n.yml\",\n      require_file: \"./config/environment.rb\") do\n  watch(%r{^(app|config)/locales/.+\\.(yml|po)$})\n  watch(%r{^config/i18n.yml$})\n  watch(\"Gemfile\")\nend\n```\n\nIf your files are located in a different path, remember to configure file paths\naccordingly.\n\nNow you can run `guard start -i`.\n\n### Using [listen](https://rubygems.org/gems/listen)\n\nCreate a file under `config/initializers/i18n.rb` with the following content:\n\n```ruby\nRails.application.config.after_initialize do\n  require \"i18n-js/listen\"\n  I18nJS.listen\nend\n```\n\nThe code above will watch for changes based on `config/i18n.yml` and\n`config/locales`. You can customize these options:\n\n- `config_file` - i18n-js configuration file\n- `locales_dir` - one or multiple directories to watch for locales changes\n- `options` - passed directly to\n  [listen](https://github.com/guard/listen/#options)\n- `run_on_start` - export files on start. Defaults to `true`. When disabled,\n  files will be exported only when there are file changes.\n\nExample:\n\n```ruby\nI18nJS.listen(\n  config_file: \"config/i18n.yml\",\n  locales_dir: [\"config/locales\", \"app/views\"],\n  options: {only: %r{.yml$}},\n  run_on_start: false\n)\n```\n\n### Integrating with your frontend\n\nYou're done exporting files, now what? Well, go to\n[i18n](https://github.com/fnando/i18n) to discover how to use the NPM package\nthat loads all the exported translation.\n\n### FAQ\n\n#### I'm running v3. Is there a migration plan?\n\n[There's a document](https://github.com/fnando/i18n-js/tree/main/MIGRATING_FROM_V3_TO_V4.md)\noutlining some of the things you need to do to migrate from v3 to v4. It may not\nbe as complete as we'd like it to be, so let us know if you face any issues\nduring the migration that is not outlined in that document.\n\n#### How can I export translations without having a database around?\n\nSome people may have a build process using something like Docker that don't\nnecessarily have a database available. In this case, you may define your own\nloading file by using something like\n`i18n export --require ./config/i18n_export.rb`, where `i18n_export.rb` may look\nlike this:\n\n```ruby\n# frozen_string_literal: true\n\nrequire \"bundler/setup\"\nrequire \"rails\"\nrequire \"active_support/railtie\"\nrequire \"action_view/railtie\"\n\nI18n.load_path += Dir[\"./config/locales/**/*.yml\"]\n```\n\n\u003e [!NOTE]\n\u003e\n\u003e You may not need to load the ActiveSupport and ActionView lines, or you may\n\u003e need to add additional requires for other libs. With this approach you have\n\u003e full control on what's going to be loaded.\n\n## Maintainer\n\n- [Nando Vieira](https://github.com/fnando)\n\n## Contributors\n\n- https://github.com/fnando/i18n-js/contributors\n\n## Contributing\n\nFor more details about how to contribute, please read\nhttps://github.com/fnando/i18n-js/blob/main/CONTRIBUTING.md.\n\n## License\n\nThe gem is available as open source under the terms of the\n[MIT License](https://opensource.org/licenses/MIT). A copy of the license can be\nfound at https://github.com/fnando/i18n-js/blob/main/LICENSE.md.\n\n## Code of Conduct\n\nEveryone interacting in the i18n-js project's codebases, issue trackers, chat\nrooms and mailing lists is expected to follow the\n[code of conduct](https://github.com/fnando/i18n-js/blob/main/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffnando%2Fi18n-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffnando%2Fi18n-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffnando%2Fi18n-js/lists"}