{"id":13878243,"url":"https://github.com/westonganger/rails_i18n_manager","last_synced_at":"2025-10-07T15:55:41.007Z","repository":{"id":149902352,"uuid":"622449296","full_name":"westonganger/rails_i18n_manager","owner":"westonganger","description":"Web interface to manage i18n translations helping to facilitate the editors of your translations. Provides a low-tech and complete workflow for importing, translating, and exporting your I18n translation files. Designed to allow you to keep the translation files inside your projects git repository where they should be.","archived":false,"fork":false,"pushed_at":"2025-02-14T10:06:48.000Z","size":1339,"stargazers_count":217,"open_issues_count":1,"forks_count":13,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-08-09T20:56:55.746Z","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/westonganger.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2023-04-02T06:19:46.000Z","updated_at":"2025-07-03T23:10:03.000Z","dependencies_parsed_at":"2023-10-15T17:27:24.529Z","dependency_job_id":"c39eb780-9872-406e-93d1-3825fea0d13b","html_url":"https://github.com/westonganger/rails_i18n_manager","commit_stats":{"total_commits":27,"total_committers":1,"mean_commits":27.0,"dds":0.0,"last_synced_commit":"9ea816925517d4fa00e5e3c1db08e91852c48bdf"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/westonganger/rails_i18n_manager","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/westonganger%2Frails_i18n_manager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/westonganger%2Frails_i18n_manager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/westonganger%2Frails_i18n_manager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/westonganger%2Frails_i18n_manager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/westonganger","download_url":"https://codeload.github.com/westonganger/rails_i18n_manager/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/westonganger%2Frails_i18n_manager/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278802799,"owners_count":26048566,"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-07T02:00:06.786Z","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":[],"created_at":"2024-08-06T08:01:43.765Z","updated_at":"2025-10-07T15:55:40.979Z","avatar_url":"https://github.com/westonganger.png","language":"Ruby","funding_links":[],"categories":["Ruby","Internationalization"],"sub_categories":[],"readme":"# Rails I18n Manager\n\u003e A complete translation editor and workflow\n\n\u003ca href=\"https://badge.fury.io/rb/rails_i18n_manager\" target=\"_blank\"\u003e\u003cimg height=\"21\" style='border:0px;height:21px;' border='0' src=\"https://badge.fury.io/rb/rails_i18n_manager.svg\" alt=\"Gem Version\"\u003e\u003c/a\u003e\n\u003ca href='https://github.com/westonganger/rails_i18n_manager/actions' target='_blank'\u003e\u003cimg src=\"https://github.com/westonganger/rails_i18n_manager/actions/workflows/test.yml/badge.svg\" style=\"max-width:100%;\" height='21' style='border:0px;height:21px;' border='0' alt=\"CI Status\"\u003e\u003c/a\u003e\n\nWeb interface to manage i18n translations helping to facilitate the editors of your translations. Provides a low-tech and complete workflow for importing, translating, and exporting your I18n translation files. Designed to allow you to keep the translation files inside your projects git repository where they should be.\n\nFeatures:\n\n- Import \u0026 export translations using standard i18n YAML/JSON files\n- Allows managing translations for any number of apps\n- Built in support for Google Translation for missing translations\n- Provides an API end point to perform automated downloads of your translations\n\n## Screenshots\n![Screenshot](/screenshot_list.png)\n\u003cbr\u003e\u003cbr\u003e\n![Screenshot](/screenshot_import.png)\n\u003cbr\u003e\u003cbr\u003e\n![Screenshot](/screenshot_edit.png)\n\n## Setup\n\nDeveloped as a Rails engine. So you can add to any existing app or create a brand new app with the functionality.\n\nFirst add the gem to your Gemfile\n\n```ruby\n### Gemfile\ngem \"rails_i18n_manager\"\n```\n\nThen install and run the database migrations\n\n```sh\nbundle install\nbundle exec rake rails_i18n_manager:install:migrations\nbundle exec rake db:migrate\n```\n\n### Routes\n\n#### Option A: Mount to a path\n\n```ruby\n### config/routes.rb\n\n### As sub-path\nmount RailsI18nManager::Engine, at: \"/rails_i18n_manager\", as: \"rails_i18n_manager\"\n\n### OR as root-path\nmount RailsI18nManager::Engine, at: \"/\", as: \"rails_i18n_manager\"\n```\n\n#### Option B: Mount to a subdomain\n\n```ruby\n### config/routes.rb\n\ntranslations_engine_subdomain = \"translations\"\n\nmount RailsI18nManager::Engine,\n  at: \"/\", as: \"translations_engine\",\n  constraints: Proc.new{|request| request.subdomain == translations_engine_subdomain }\n\nnot_engine = Proc.new{|request| request.subdomain != translations_engine_subdomain }\n\nconstraints not_engine do\n  # your app routes here...\nend\n```\n\n### Configuration\n\n```ruby\n### config/initializers/rails_i18n_manager.rb\n\nRailsI18nManager.config do |config|\n  config.google_translate_api_key = ENV.fetch(\"GOOGLE_TRANSLATE_API_KEY\", nil)\n\n  ### You can use our built-in list of all locales Google Translate supports\n  ### OR make your own list. These need to be supported by Google Translate\n  # config.valid_locales = [\"en\", \"es\", \"fr\"]\nend\n```\n\n### Customizing Authentication\n\n```ruby\n### config/routes.rb\n\n### Using Devise\nauthenticated :user do\n  mount RailsI18nManager::Engine, at: \"/rails_i18n_manager\", as: \"rails_i18n_manager\"\nend\n\n### Custom devise-like\nconstraints -\u003e(req){ req.session[:user_id].present? \u0026\u0026 User.find_by(id: req.session[:user_id]) } do\n  mount RailsI18nManager::Engine, at: \"/rails_i18n_manager\", as: \"rails_i18n_manager\"\nend\n\n### HTTP Basic Auth\nwith_http_basic_auth = -\u003e(engine){\n  Rack::Builder.new do\n    use Rack::Auth::Basic do |username, password|\n      ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(username), ::Digest::SHA256.hexdigest(ENV.fetch(\"RAILS_I18N_MANAGER_USERNAME\"))) \u0026\u0026\n        ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(password), ::Digest::SHA256.hexdigest(ENV.fetch(\"RAILS_I18N_MANAGER_PASSWORD\")))\n    end\n    run(engine)\n  end\n}\nmount with_http_basic_auth.call(RailsI18nManager::Engine), at: \"/rails_i18n_manager\", as: \"rails_i18n_manager\"\n```\n\n### API Endpoint\n\nWe provide an endpoint to retrieve your translation files at `/translations`.\n\nYou will likely want to add your own custom authentication strategy and can do so using a routing constraint on the `mount RailsI18nManager` call.\n\nFrom that point you can implement an automated mechanism to update your apps translations using the provided API end point. Some examples\n\nAn example in Ruby:\n\n```ruby\nrequire 'open-uri'\n\nzip_stream = URI.open('https://translations-manager.example.com/translations.zip?export_format=yaml')\nIO.copy_stream(zip_stream, '/tmp/my-app-locales.zip')\n`unzip /tmp/my-app-locales.zip /tmp/my-app-locales/`\n`rsync --delete-after /tmp/my-app-locales/my-app/ /path/to/my-app/config/locales/`\nputs \"Locales are now updated, app restart not-required\"\n```\n\nA command line example using curl:\n\n```\ncurl https://translations-manager.example.com/translations.zip?export_format=json -o /tmp/my-app-locales.zip \\\n  \u0026\u0026 unzip /tmp/my-app-locales.zip /tmp/my-app-locales/ \\\n  \u0026\u0026 rsync --delete-after /tmp/my-app-locales/my-app/ \\\n  \u0026\u0026 echo \"Locales are now updated, app restart not-required\"\n```\n\n## Recommended Workflow for Teams\n\nIt is desirable to reduce how often import/export is performed. It is also desirable that we do not violate the regular PR lifecycle/process. The following workflow should allow for this.\n\nWhen creating a PR you can just create a new YAML file named after your feature name or ticket number and then use the following format:\n\n```yaml\n# config/locales/some_new_feature.yml\n\nen:\n  some_new_key: \"foo\"\nfr:\n  some_new_key: \"bar\"\nes:\n  some_new_key: \"baz\"\n```\n\nWhenever releasing a new version of your application, pre-deploy or some other cadence, then you can have a step where all translation files are uploaded to the `rails_i18n_manager`, have your translator folks double check everything, then export your new files and cleanup all the feature files.\n\n## Recommended I18n Configuration\n\nThe default I18n backend has some glaring issues\n\n- It will silently show \"translation_missing\" text which is very undesirable\n- It will not fallback to your default or any other locale\n\nYou can avoid these issues using either of the techniques below\n\n```ruby\n# config/initializers/i18n.rb\n\nRails.configuration do |config|\n  config.i18n.raise_on_missing_translations = true # WARNING: this will raise exceptions in Production too, preventing your users from using your application even when some silly little translation  is missing\n\n  config.i18n.fallbacks = [I18n.default_locale, :en].uniq # fallback to default locale, or if that is missing then fallback to english translation\nend\n```\n\nYou will likely find that `raise_on_missing_translations` is too aggressive. Causing major outages just because a translation is missing. In that scenario its better to use something like the following:\n\n```ruby\n# config/initializers/i18n.rb\n\nRails.configuration do |config|\n  config.i18n.raise_on_missing_translations = false # Instead we use the custom backend below\n\n  config.i18n.fallbacks = [I18n.default_locale, :en].uniq # fallback to default locale, or if that is missing then fallback to english translation\nend\n\nmodule I18n\n  class CustomI18nBackend\n    include I18n::Backend::Base\n\n    def translate(locale, key, options = EMPTY_HASH)\n      if !key.nil? \u0026\u0026 key.to_s != \"i18n.plural.rule\"\n        translation_value = lookup(locale, key, options[:scope], options)\n\n        if translation_value.blank?\n          if Rails.env.production?\n            # send an email or some other warning mechanism\n          else\n            # Raise exception in non-production environments\n            raise \"Translation not found (locale: #{locale}, key: #{key})\"\n          end\n        end\n      end\n\n      return nil # allow the Backend::Chain to continue to the next backend\n    end\n  end\nend\n\nif I18n.backend.is_a?(I18n::Backend::Chain)\n  I18n.backend.backends.unshift(I18n::CustomI18nBackend)\nelse\n  I18n.backend = I18n::Backend::Chain.new(\n    I18n::CustomI18nBackend,\n    I18n.backend, # retain original backend\n  )\nend\n```\n\n## Development\n\nRun migrations using: `rails db:migrate`\n\nRun server using: `bin/dev` or `cd test/dummy/; rails s`\n\n## Testing\n\n```\nbundle exec rspec\n```\n\nWe can locally test different versions of Rails using `ENV['RAILS_VERSION']`\n\n```\nexport RAILS_VERSION=7.0\nbundle install\nbundle exec rspec\n```\n\n## Other Translation Managers \u0026 Web Interfaces\n\nFor comparison, some other projects for managing Rails translations.\n\n- https://github.com/tolk/tolk - This is the project that inspired rails_i18n_manager. UI and file-based approach. I [attempted to revive tolk](https://github.com/tolk/tolk/pull/161) but gave up as I found the codebase and workflow was really just a legacy ball of spagetti.\n- https://github.com/prograils/lit\n- https://github.com/alphagov/rails_translation_manager\n- https://github.com/glebm/i18n-tasks\n\n\n# Credits\n\nCreated \u0026 Maintained by [Weston Ganger](https://westonganger.com) - [@westonganger](https://github.com/westonganger)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwestonganger%2Frails_i18n_manager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwestonganger%2Frails_i18n_manager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwestonganger%2Frails_i18n_manager/lists"}