{"id":13427728,"url":"https://github.com/globalize/globalize","last_synced_at":"2025-05-14T11:08:23.411Z","repository":{"id":997032,"uuid":"807517","full_name":"globalize/globalize","owner":"globalize","description":"Rails I18n de-facto standard library for ActiveRecord model/data translation.","archived":false,"fork":false,"pushed_at":"2025-02-03T23:30:45.000Z","size":1689,"stargazers_count":2167,"open_issues_count":8,"forks_count":501,"subscribers_count":34,"default_branch":"main","last_synced_at":"2025-05-07T10:52:37.388Z","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":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/globalize.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","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},"funding":{"github":["parndt"]}},"created_at":"2010-07-30T14:30:27.000Z","updated_at":"2025-05-06T17:31:47.000Z","dependencies_parsed_at":"2024-01-31T05:08:56.026Z","dependency_job_id":"1fec3ac0-ab1b-4289-a118-bbb57fd85eca","html_url":"https://github.com/globalize/globalize","commit_stats":{"total_commits":937,"total_committers":180,"mean_commits":5.205555555555556,"dds":0.8004268943436499,"last_synced_commit":"16fa278e56e5e0303fd7ff14935dad421b83d1a4"},"previous_names":[],"tags_count":50,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globalize%2Fglobalize","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globalize%2Fglobalize/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globalize%2Fglobalize/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globalize%2Fglobalize/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/globalize","download_url":"https://codeload.github.com/globalize/globalize/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254129481,"owners_count":22019628,"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":[],"created_at":"2024-07-31T01:00:39.426Z","updated_at":"2025-05-14T11:08:23.375Z","avatar_url":"https://github.com/globalize.png","language":"Ruby","readme":"![Globalize](http://globalize.github.io/globalize/images/globalize.png)\n\n[![Build Status](https://github.com/globalize/globalize/workflows/CI/badge.svg)](https://github.com/globalize/globalize/actions) [![Code Climate](https://codeclimate.com/github/globalize/globalize.svg)](https://codeclimate.com/github/globalize/globalize)\n[![Open Source Helpers](https://www.codetriage.com/globalize/globalize/badges/users.svg)](https://www.codetriage.com/globalize/globalize)\n\nYou can chat with us using Gitter:\n\n[![Gitter chat](https://badges.gitter.im/globalize/globalize.svg)](https://gitter.im/globalize/globalize)\n\nGlobalize builds on the [I18n API in Ruby on Rails](http://guides.rubyonrails.org/i18n.html)\nto add model translations to ActiveRecord models.\n\nIn other words, a way to translate actual user-generated content, for example; a single blog post with multiple translations.\n\n## Current state of the gem\n\nGlobalize is not very actively maintained. Pull Requests are welcome, especially for compatibility with new versions of Rails, but none of the maintainers actively use Globalize anymore. If you need a more actively maintained model translation gem, we recommend checking out [Mobility](https://github.com/shioyama/mobility), a natural successor of Globalize created by Chris Salzberg (one of Globalize maintainers) and inspired by the ideas discussed around Globalize. For a more up-to-date discussion of the current situation, see [issue #753](https://github.com/globalize/globalize/issues/753).\n\n\n## Requirements\n\n* ActiveRecord \u003e= 7.0 (see below for installation with older ActiveRecord)\n* I18n\n\n## Installation\n\nTo install the ActiveRecord 7.x compatible version of Globalize with its default setup, just use:\n\n```ruby\ngem install globalize\n```\n\nWhen using Bundler, put this in your Gemfile:\n\n```ruby\ngem \"globalize\", \"~\u003e 7.0\"\n```\n\nPlease help us by letting us know what works, and what doesn't, when using pre-release code. To use a pre-release, put this in your Gemfile:\n\n```ruby\ngem \"globalize\", git: \"https://github.com/globalize/globalize\", branch: \"main\"\n```\n\n## Older ActiveRecord\n* Use Version 6.3 or lower\n\nActiveRecord 4.2 to 6.1:\n\n```ruby\ngem \"globalize\", \"~\u003e 6.3\"\n```\n\n## Model translations\n\nModel translations allow you to translate your models' attribute values. E.g.\n\n```ruby\nclass Post \u003c ActiveRecord::Base\n  translates :title, :text\nend\n```\n\nAllows you to translate the attributes :title and :text per locale:\n\n```ruby\nI18n.locale = :en\npost.title # =\u003e Globalize rocks!\n\nI18n.locale = :he\npost.title # =\u003e גלובאלייז2 שולט!\n```\n\nYou can also set translations with mass-assignment by specifying the locale:\n\n```ruby\npost.attributes = { title: \"גלובאלייז2 שולט!\", locale: :he }\n```\n\nIn order to make this work, you'll need to add the appropriate translation tables.\nGlobalize comes with a handy helper method to help you do this.\nIt's called `create_translation_table!`. Here's an example:\n\nNote that your migrations can use `create_translation_table!` and `drop_translation_table!`\nonly inside the `up` and `down` instance methods, respectively. You cannot use `create_translation_table!`\nand `drop_translation_table!` inside the `change` instance method.\n\n### Creating translation tables\n\nAlso note that before you can create a translation table, you have to define the translated attributes via `translates` in your model as shown above.\n\n```ruby\nclass CreatePosts \u003c ActiveRecord::Migration\n  def change\n    create_table :posts do |t|\n      t.timestamps\n    end\n\n    reversible do |dir|\n      dir.up do\n        Post.create_translation_table! :title =\u003e :string, :text =\u003e :text\n      end\n\n      dir.down do\n        Post.drop_translation_table!\n      end\n    end\n  end\nend\n```\n\nAlso, you can pass options for specific columns. Here’s an example:\n\n```ruby\nclass CreatePosts \u003c ActiveRecord::Migration\n  def change\n    create_table :posts do |t|\n      t.timestamps\n    end\n\n    reversible do |dir|\n      dir.up do\n        Post.create_translation_table! :title =\u003e :string,\n          :text =\u003e {:type =\u003e :text, :null =\u003e false, :default =\u003e \"abc\"}\n      end\n\n      dir.down do\n        Post.drop_translation_table!\n      end\n    end\n  end\nend\n```\n\nNote that the ActiveRecord model `Post` must already exist and have a `translates`\ndirective listing the translated fields.\n\n## Migrating existing data to and from the translated version\n\nAs well as creating a translation table, you can also use `create_translation_table!`\nto migrate across any existing data to the default locale. This can also operate\nin reverse to restore any translations from the default locale back to the model\nwhen you don't want to use a translation table anymore using `drop_translation_table!`\n\nThis feature makes use of `untranslated_attributes` which allows access to the\nmodel's attributes as they were before the translation was applied. Here's an\nexample (which assumes you already have a model called `Post` and its table\nexists):\n\n```ruby\nclass TranslatePosts \u003c ActiveRecord::Migration\n  def change\n    reversible do |dir|\n      dir.up do\n        Post.create_translation_table!({\n          :title =\u003e :string,\n          :text =\u003e :text\n        }, {\n          :migrate_data =\u003e true\n        })\n      end\n\n      dir.down do\n        Post.drop_translation_table! :migrate_data =\u003e true\n      end\n    end\n  end\nend\n```\n\nNOTE: Make sure you drop the translated columns from the parent table after all your data is safely migrated.\n\nTo automatically remove the translated columns from the parent table after the data migration, please use option `remove_source_columns`.\n\n```ruby\nclass TranslatePosts \u003c ActiveRecord::Migration\n  def self.up\n    Post.create_translation_table!({\n      :title =\u003e :string,\n      :text =\u003e :text\n    }, {\n      :migrate_data =\u003e true,\n      :remove_source_columns =\u003e true\n    })\n  end\n\n  def self.down\n    Post.drop_translation_table! :migrate_data =\u003e true\n  end\nend\n```\n\n\nIn order to use a specific locale for migrated data, you can use `I18n.with_locale`:\n\n```ruby\n    I18n.with_locale(:bo) do\n      Post.create_translation_table!({\n        :title =\u003e :string,\n        :text =\u003e :text\n      }, {\n        :migrate_data =\u003e true\n      })\n    end\n```\n\n## Adding additional fields to the translation table\n\nIn order to add a new field to an existing translation table, you can use `add_translation_fields!`:\n\n```ruby\nclass AddAuthorToPost \u003c ActiveRecord::Migration\n  def change\n    reversible do |dir|\n      dir.up do\n        Post.add_translation_fields! author: :text\n      end\n\n      dir.down do\n        remove_column :post_translations, :author\n      end\n    end\n  end\nend\n```\n\nNOTE: Remember to add the new field to the model:\n\n```ruby\ntranslates :title, :author\n```\n## Gotchas\n\nBecause globalize uses the `:locale` key to specify the locale during\nmass-assignment, you should avoid having a `locale` attribute on the parent\nmodel.\n\nIf you like your translated model to update if a translation changes, use the `touch: true` option together with `translates`:\n\n```ruby\n  translates :name, touch: true\n```\n\n## Known Issues\n\nIf you're getting the `ActiveRecord::StatementInvalid: PG::NotNullViolation: ERROR: null value in column \"column_name\" violates not-null constraint` error, the only known way to deal with it as of now is to remove not-null constraint for the globalized columns:\n\n```ruby\nclass RemoveNullConstraintsFromResourceTranslations \u003c ActiveRecord::Migration\n  def change\n    change_column_null :resource_translations, :column_name, true\n  end\nend\n```\n\n## Versioning with Globalize\n\nSee the [globalize-versioning](https://github.com/globalize/globalize-versioning) gem.\n\n## I18n fallbacks for empty translations\n\nIt is possible to enable fallbacks for empty translations. It will depend on the\nconfiguration setting you have set for I18n translations in your Rails config.\n\nYou can enable them by adding the next line to `config/application.rb` (or only\n`config/environments/production.rb` if you only want them in production)\n\n```ruby\n# For version 1.1.0 and above of the `i18n` gem:\nconfig.i18n.fallbacks = [I18n.default_locale]\n# Below version 1.1.0 of the `i18n` gem:\nconfig.i18n.fallbacks = true\n```\n\nBy default, globalize will only use fallbacks when your translation model does\nnot exist or the translation value for the item you've requested is `nil`.\nHowever it is possible to also use fallbacks for `blank` translations by adding\n`:fallbacks_for_empty_translations =\u003e true` to the `translates` method.\n\n```ruby\nclass Post \u003c ActiveRecord::Base\n  translates :title, :name\nend\n\nputs post.translations.inspect\n# =\u003e [#\u003cPost::Translation id: 1, post_id: 1, locale: \"en\", title: \"Globalize rocks!\", name: \"Globalize\"\u003e,\n      #\u003cPost::Translation id: 2, post_id: 1, locale: \"nl\", title: \"\", name: nil\u003e]\n\nI18n.locale = :en\npost.title # =\u003e \"Globalize rocks!\"\npost.name  # =\u003e \"Globalize\"\n\nI18n.locale = :nl\npost.title # =\u003e \"\"\npost.name  # =\u003e \"Globalize\"\n```\n\n```ruby\nclass Post \u003c ActiveRecord::Base\n  translates :title, :name, :fallbacks_for_empty_translations =\u003e true\nend\n\nputs post.translations.inspect\n# =\u003e [#\u003cPost::Translation id: 1, post_id: 1, locale: \"en\", title: \"Globalize rocks!\", name: \"Globalize\"\u003e,\n      #\u003cPost::Translation id: 2, post_id: 1, locale: \"nl\", title: \"\", name: nil\u003e]\n\nI18n.locale = :en\npost.title # =\u003e \"Globalize rocks!\"\npost.name  # =\u003e \"Globalize\"\n\nI18n.locale = :nl\npost.title # =\u003e \"Globalize rocks!\"\npost.name  # =\u003e \"Globalize\"\n```\n\n## Fallback locales to each other\n\nIt is possible to setup locales to fallback to each other.\n\n```ruby\nclass Post \u003c ActiveRecord::Base\n  translates :title, :name\nend\n\nGlobalize.fallbacks = {:en =\u003e [:en, :pl], :pl =\u003e [:pl, :en]}\n\nI18n.locale = :en\nen_post = Post.create(:title =\u003e \"en_title\")\n\nI18n.locale = :pl\npl_post = Post.create(:title =\u003e \"pl_title\")\nen_post.title # =\u003e \"en_title\"\n\nI18n.locale = :en\nen_post.title # =\u003e \"en_title\"\npl_post.title # =\u003e \"pl_title\"\n```\n\n\n## Scoping objects by those with translations\n\nTo only return objects that have a translation for the given locale we can use\nthe `with_translations` scope. This will only return records that have a\ntranslations for the passed in locale.\n\n```ruby\nPost.with_translations(\"en\")\n# =\u003e [\n  #\u003cPost::Translation id: 1, post_id: 1, locale: \"en\", title: \"Globalize rocks!\", name: \"Globalize\"\u003e,\n  #\u003cPost::Translation id: 2, post_id: 1, locale: \"nl\", title: \"\", name: nil\u003e\n]\n\nPost.with_translations(I18n.locale)\n# =\u003e [\n  #\u003cPost::Translation id: 1, post_id: 1, locale: \"en\", title: \"Globalize rocks!\", name: \"Globalize\"\u003e,\n  #\u003cPost::Translation id: 2, post_id: 1, locale: \"nl\", title: \"\", name: nil\u003e\n]\n\nPost.with_translations(\"de\")\n# =\u003e []\n```\n\n## Show different languages\n\nIn views, if there is content from different locales that you wish to display,\nyou should use the `with_locale` option with a block, as below:\n\n```erb\n\u003c% Globalize.with_locale(:en) do %\u003e\n  \u003c%= render \"my_translated_partial\" %\u003e\n\u003c% end %\u003e\n```\n\nYour partial will now be rendered with the `:en` locale set as the current locale.\n\n## Interpolation\n\nGlobalize supports interpolation in a similar manner to I18n.\n\n```ruby\nclass Post \u003c ActiveRecord::Base\n  translates :title\nend\n\nI18n.locale = :en\npost.title = \"Globalize %{superlative}!\"\n\npost.title\n# #=\u003e \"Globalize %{superlative}!\"\n\npost.title(:foo =\u003e \"bar\")\n# SomeError: missing interpolation argument :superlative\n\npost.title(:superlative =\u003e \"rocks\")\n# #=\u003e \"Globalize rocks!\"\n```\n\n## Fragment caching\n\nDon't forget to add globalize locale into the `cache_key` to separate different localizations of the record.\nOne of the possible ways to implement it:\n\n```ruby\n# inside translated model\ndef cache_key\n  [super, Globalize.locale.to_s].join(\"-\")\nend\n```\n\n## Thread-safety\n\nGlobalize uses [request_store](https://github.com/steveklabnik/request_store) gem to clean up thread-global variable after every request.\nRequestStore includes a Railtie that will configure everything properly.\n\nIf you're not using Rails, you may need to consult RequestStore's [README](https://github.com/steveklabnik/request_store#no-rails-no-problem) to configure it.\n\n## Tutorials and articles\n* [Go Global with Rails and I18n](http://www.sitepoint.com/go-global-rails-i18n/) - introductory article about i18n in Rails (Ilya Bodrov)\n\n## Official Globalize extensions\n\n* [globalize-accessors](https://github.com/globalize/globalize-accessors) - generator of accessor methods for models. *(e.g. title_en, title_cz)*\n* [globalize-versioning](https://github.com/globalize/globalize-versioning) - versioning support for using Globalize with [`paper_trail`](https://github.com/airblade/paper_trail).\n\n## Alternative solutions\n\n* [Traco](https://github.com/barsoom/traco) - use multiple columns in the same model (Barsoom)\n* [Mobility](https://github.com/shioyama/mobility) - pluggable translation framework supporting many strategies, including translatable columns, translation tables and hstore/jsonb (Chris Salzberg)\n* [hstore_translate](https://github.com/cfabianski/hstore_translate) - use PostgreSQL's hstore datatype to store translations, instead of separate translation tables (Cédric Fabianski)\n* [json_translate](https://github.com/cfabianski/json_translate) - use PostgreSQL's json/jsonb datatype to store translations, instead of separate translation tables (Cédric Fabianski)\n* [Trasto](https://github.com/yabawock/trasto) - store translations directly in the model in a Postgres Hstore column\n\n## Related solutions\n\n* [friendly_id-globalize](https://github.com/norman/friendly_id-globalize) - lets you use Globalize to translate slugs (Norman Clarke)\n","funding_links":["https://github.com/sponsors/parndt"],"categories":["Active Record","Time \u0026 Space","Internationalization","Ruby","模型"],"sub_categories":["Omniauth","I18n"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobalize%2Fglobalize","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglobalize%2Fglobalize","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobalize%2Fglobalize/lists"}