{"id":13463393,"url":"https://github.com/r18n/r18n","last_synced_at":"2026-03-11T09:31:54.255Z","repository":{"id":471317,"uuid":"96298","full_name":"r18n/r18n","owner":"r18n","description":"I18n tool to translate your Ruby application.","archived":false,"fork":false,"pushed_at":"2024-12-30T19:35:52.000Z","size":1770,"stargazers_count":514,"open_issues_count":3,"forks_count":67,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-12-20T10:32:14.822Z","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":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/r18n.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":null,"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}},"created_at":"2008-12-24T11:15:15.000Z","updated_at":"2025-11-13T09:12:07.000Z","dependencies_parsed_at":"2022-07-04T18:30:35.181Z","dependency_job_id":"d9fac432-5f50-4972-a88c-477d95022284","html_url":"https://github.com/r18n/r18n","commit_stats":{"total_commits":972,"total_committers":78,"mean_commits":"12.461538461538462","dds":0.2808641975308642,"last_synced_commit":"948414ddda82b0df343ee98ca46d7eeb74da9bb2"},"previous_names":["ai/r18n"],"tags_count":65,"template":false,"template_full_name":null,"purl":"pkg:github/r18n/r18n","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/r18n%2Fr18n","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/r18n%2Fr18n/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/r18n%2Fr18n/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/r18n%2Fr18n/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/r18n","download_url":"https://codeload.github.com/r18n/r18n/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/r18n%2Fr18n/sbom","scorecard":{"id":172601,"data":{"date":"2025-08-04","repo":{"name":"github.com/r18n/r18n","commit":"dc91f270aee672edc38b3cd740fd0953ac3b36fe"},"scorecard":{"version":"v5.2.1-28-gc1d103a9","commit":"c1d103a9bb9f635ec7260bf9aa0699466fa4be0e"},"score":2.6,"checks":[{"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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#binary-artifacts"}},{"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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#token-permissions"}},{"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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#dangerous-workflow"}},{"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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 1/16 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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#code-review"}},{"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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#packaging"}},{"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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/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 '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/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/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 21 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-16T16:57:11.021Z","repository_id":471317,"created_at":"2025-08-16T16:57:11.021Z","updated_at":"2025-08-16T16:57:11.021Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30377275,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-11T06:09:32.197Z","status":"ssl_error","status_checked_at":"2026-03-11T06:09:17.086Z","response_time":84,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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-31T13:00:52.699Z","updated_at":"2026-03-11T09:31:54.233Z","avatar_url":"https://github.com/r18n.png","language":"Ruby","readme":"# R18n\n\nR18n is an i18n tool to translate your Ruby application into several languages.\nIt contains a core gem and out-of-box wrapper plugins for frameworks or\nenvironments (Rails, Sinatra, desktop).\n\n## Quick Demo\n\n[`r18n-core`](https://github.com/r18n/r18n-core) for any purpose, used by wrapper plugins.\n\n`i18n/en.yml`:\n\n```yaml\nuser:\n  edit: Edit user\n  name: User name is %1\n  count: !!pl\n    1: There is 1 user\n    n: There are %1 users\n```\n\n`example.rb`:\n\n```ruby\n# Setup R18n (or just use out-of-box `r18n-rails` or `sinatra-r18n` gem)\nR18n.default_places = './i18n/'\nR18n.set('en')\n\ninclude R18n::Helpers\n\n# Use R18n\nt.user.edit         #=\u003e \"Edit user\"\nt.user.name('John') #=\u003e \"User name is John\"\nt.user.count(5)     #=\u003e \"There are 5 users\"\n\nt.not.exists | 'default' #=\u003e \"default\"\nt.not.exists.translated? #=\u003e false\n\nl Time.now         #=\u003e \"2010-01-03 18:54\"\nl Time.now, :human #=\u003e \"now\"\nl Time.now, :full  #=\u003e \"3rd of January, 2010 18:54\"\n```\n\n### Wrapper plugins\n\n* [`r18n-rails`](https://github.com/r18n/r18n-rails)\n  for Ruby on Rails,\n* [`r18n-rails-api`](https://github.com/r18n/r18n-rails-api)\n  for Ruby on Rails API,\n* [`sinatra-r18n`](https://github.com/r18n/sinatra-r18n)\n  for Sinatra,\n* [`r18n-desktop`](https://github.com/r18n/r18n-desktop)\n  for shell/desktop applications.\n\n## R18n Features\n\n* Nice Ruby-style syntax.\n* Filters.\n* Model Translation (or any Ruby object).\n* Auto-detect user locales.\n* Flexible locales.\n* Total flexibility.\n\n### Ruby-style Syntax\n\nR18n use compact, explicit and ruby-style syntax.\n\nTranslations store in YAML with types:\n\n```yaml\nuser:\n  edit: Edit user\n  name: User name is %1\n  count: !!pl\n    1: There is 1 user\n    n: There are %1 users\n```\n\nTo access translation you can call methods with the same names:\n\n```ruby\nt.user.edit         #=\u003e \"Edit user\"\nt.user.name('John') #=\u003e \"User name is John\"\nt.user.count(5)     #=\u003e \"There are 5 users\"\n\nt.not.exists | 'default' #=\u003e \"default\"\nt.not.exists.translated? #=\u003e false\n```\n\nIf the translation key is the name of an Object method you can access it via\nhash index or use Rails I18n API in `r18n-rails`:\n\n```ruby\nt[:methods] #=\u003e \"Methods\"\n```\n\nIf you use `r18n-rails` plugin, you will have full compatibility with Rails I18n\nsyntax.\n\n### Filters\n\nYou can add filters for some YAML type. For example, we add custom filter to\nreturn different translation depend on user gender\n(it's actual for some languages).\n\n```yaml\nlog:\n  signup: !!gender\n    male: Он зарегистрировался\n    female: Она зарегистрировалась\n```\n\nIn Rails application you can define filter in reloaded `app/i18n/filters.rb`:\n\n```ruby\nR18n::Filters.add('gender') do |translation, config, user|\n  translation[user.gender]\nend\n\nt.log.signup(user)\n```\n\nR18n already has filters for HTML escaping, Textile and Markdown:\n\n```yaml\nhi: !!markdown\n  Hi, **people**!\ngreater: !!escape\n  1 \u003c 2 is true\n```\n\n```ruby\ni18n.hi      #=\u003e \"\u003cp\u003eHi, \u003cstrong\u003epeople!\u003c/strong\u003e\u003c/p\u003e\"\ni18n.greater #=\u003e \"1 \u0026lt; 2 is true\"\n```\n\nYou can also add filters for all returned translations or to issues, when\ntranslation can't be founded.\n\n### Model Translation\n\nYou can translate any class, including ORM models (`ActiveRecord`, `Sequel`,\n`Mongoid`, `MongoMapper`, `DataMapper` or others):\n\n```ruby\nclass Product \u003c ActiveRecord::Base\n  include R18n::Translated\n  # Model has two normal properties: title_en and title_ru\n  translations :title\nend\n\nR18n.set('en') # English\nproduct.title  #=\u003e \"Anthrax\"\n\nR18n.set('ru') # Russian\nproduct.title  #=\u003e \"Сибирская язва\"\n```\n\n### Auto-detect User Locales\n\nR18n has an agnostic core package and plugins with standard support for\nSinatra and desktop applications. So for common cases you can use out-of-box\ngems with user locale auto-detect. For special cases you can use core gem and\nhack everything, that you want (see [\"Total flexibility\"](#total-flexibility)\nsection).\n\nR18n automatically generate fallbacks for current user, based on\nuser locales list from `HTTP_ACCEPT_LANGUAGE`, locale info (in some countries\npeople know several languages), and default locale. For example, if user know\nKazakh and German, R18n will try find translations in: Kazakh → German →\nRussian (second language in Kazakhstan) → English (default locale).\n\n### Flexible Locales\n\nR18n store separated business translations and locale information. So all\nlocales (pluralization rules, time and number localization,\nsome base translations) ship with core gem and can be used out-of-box:\n\nFor example, Russian has built-in different pluralization without any lambdas\nin YAML:\n\n```ruby\nt.user.count(1) #=\u003e \"1 пользователь\"\nt.user.count(2) #=\u003e \"2 пользователя\"\nt.user.count(5) #=\u003e \"5 пользователей\"\n```\n\nLocales are simple Ruby classes, so they are very flexible. For example,\nR18n ship with very perfection `full` time formatter:\n\n```ruby\nR18n.set('en')            # English\nl Time.now, :full         #=\u003e \"1st of December, 2011 12:00\"\nl Time.now + 1.day, :full #=\u003e \"2nd of December, 2011 12:00\"\n\nR18n.set('fr')            # French\nl Time.now, :full         #=\u003e \"1er décembre 2011 12:00\"\nl Time.now + 1.day, :full #=\u003e \"2 décembre 2011 12:00\"\n```\n\nYears in Thailand are counted in the Buddhist Era. It's very easy for R18n:\n\n```ruby\nR18n.set('th')\nR18n.l Time.now, :full #=\u003e \"1 พฤศจิกายน, 2554 12:00\"\n```\n\n### Total Flexibility\n\nR18n is very flexible and agnostic. For example, Rails I18n compatibility level\nis just few filters and custom loader.\n\nTranslation variables and pluralization (\"1 comment\", \"5 comments\") are filters\ntoo, so you can disable, replace or cascade them. For example, you can use the\n\"named variables filter\" from the `r18n-rails-api` gem:\n\n```yaml\ngreeting: \"Hi, %{name}\"\n```\n\n```ruby\nR18n::Filters.on(:named_variables)\nt.greeting(name: 'John') #=\u003e \"Hi, John\"\n```\n\nOr you can add cascade filter on all untranslated messages to collect issues:\n\n```ruby\nR18n::Filters.add(::R18n::Untranslated) do |v, c, transl, untransl, path|\n  File.open('untranslated.list', 'w') { |io| io.puts(path) }\nend\n```\n\nR18n can load translations from anywhere, not just from YAML files. You just\nneed to create loader object with 2 methods: `available` and `load`:\n\n```ruby\nclass DBLoader\n  def available\n    Translation.all.map(\u0026:locale)\n  end\n  def load(locale)\n    Translation.find_by_locale(locale).to_hash\n  end\nend\n\nR18n.default_places = DBLoader.new\nR18n.set('en') # Load English messages from DB\n```\n\nYou can also set several loaders to merge translations from different sources:\n\n```ruby\nR18n.default_places = [DBLoader.new, 'path/to/yaml']\n```\n\nAlso you can use several I18n object in program. For example, to load different\ntranslations for plugin:\n\n```ruby\nplugin_i18n = R18n::I18n.new(locales, plugin_i18n_places)\nplugin_i18n.t.message.hellow\n```\n\n## Services\n\n* [Crowdin](https://crowdin.com/) — supports all of R18n, including\n  pluralization, filters, etc.\n* [Localeapp](https://www.localeapp.com/) – Locale helps content owners and\n  translators work together, so Rails developers are free to write more code.\n* [WebTranslateIt](https://webtranslateit.com/) – web service to translate\n  your app. It allows your customer without programming skills help you\n  with translations.\n","funding_links":[],"categories":["Time \u0026 Space","Ruby"],"sub_categories":["I18n"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fr18n%2Fr18n","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fr18n%2Fr18n","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fr18n%2Fr18n/lists"}