{"id":13509669,"url":"https://github.com/crbelaus/trans","last_synced_at":"2025-08-11T12:20:41.189Z","repository":{"id":32850361,"uuid":"59242862","full_name":"crbelaus/trans","owner":"crbelaus","description":"Embedded translations for Ecto","archived":false,"fork":false,"pushed_at":"2024-07-07T11:07:45.000Z","size":267,"stargazers_count":235,"open_issues_count":3,"forks_count":17,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-07-24T15:52:47.572Z","etag":null,"topics":["elixir","i18n","postgresql","translation"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/trans","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/crbelaus.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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}},"created_at":"2016-05-19T21:09:01.000Z","updated_at":"2025-07-19T10:45:22.000Z","dependencies_parsed_at":"2022-07-24T17:32:05.946Z","dependency_job_id":"b6ffe1d3-c5c6-47e0-a1cb-1cd379d48541","html_url":"https://github.com/crbelaus/trans","commit_stats":{"total_commits":180,"total_committers":11,"mean_commits":"16.363636363636363","dds":0.09999999999999998,"last_synced_commit":"02e05f1010059949389589fc460116427ac49a27"},"previous_names":["belaustegui/trans"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/crbelaus/trans","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crbelaus%2Ftrans","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crbelaus%2Ftrans/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crbelaus%2Ftrans/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crbelaus%2Ftrans/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/crbelaus","download_url":"https://codeload.github.com/crbelaus/trans/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crbelaus%2Ftrans/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269886152,"owners_count":24490848,"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-08-11T02:00:10.019Z","response_time":75,"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":["elixir","i18n","postgresql","translation"],"created_at":"2024-08-01T02:01:11.201Z","updated_at":"2025-08-11T12:20:41.161Z","avatar_url":"https://github.com/crbelaus.png","language":"Elixir","funding_links":[],"categories":["Translations and Internationalizations"],"sub_categories":[],"readme":"# Trans\n\n[![Tests](https://github.com/crbelaus/trans/actions/workflows/ci.yml/badge.svg)](https://github.com/crbelaus/trans/actions/workflows/ci.yml/badge.svg)\n[![Hex.pm](https://img.shields.io/hexpm/dt/trans.svg?maxAge=2592000\u0026style=flat-square)](https://hex.pm/packages/trans)\n\n`Trans` provides a way to manage and query translations embedded into schemas\nand removes the necessity of maintaining extra tables only for translation storage.\nIt is inspired by the great [hstore translate](https://rubygems.org/gems/hstore_translate)\ngem for Ruby.\n\n`Trans` is published on [hex.pm](https://hex.pm/packages/trans) and the documentation\nis also [available online](https://hexdocs.pm/trans/). Source code is available in this same\nrepository under the Apache2 License.\n\nOn April 17th, 2017, `Trans` was [featured in HackerNoon](https://hackernoon.com/introducing-trans2-407610887068)\n\n\n## Optional Requirements\n\nHaving Ecto SQL and Postgrex in your application will allow you to use the `Trans.QueryBuilder`\ncomponent to generate database queries based on translated data.  You can still\nuse the `Trans.Translator` component without those dependencies though.\n\n- [Ecto SQL](https://hex.pm/packages/ecto_sql) 3.0 or higher\n- [PostgreSQL](https://hex.pm/packages/postgrex) 9.4 or higher (since `Trans` leverages the JSONB datatype)\n\n\n## Why Trans?\n\nThe traditional approach to content internationalization consists on using an\nadditional table for each translatable schema. This table works only as a storage\nfor the original schema translations. For example, we may have a `posts` and\na `posts_translations` tables.\n\nThis approach has a few disadvantages:\n\n- It complicates the database schema because it creates extra tables that are\n  coupled to the \"main\" ones.\n- It makes migrations and schemas more complicated, since we always have to keep\n  the two tables in sync.\n- It requires constant JOINs in order to filter or fetch records along with their\n  translations.\n\nThe approach used by `Trans` is based on modern RDBMSs support for unstructured\ndatatypes.  Instead of storing the translations in a different table, each\ntranslatable schema has an extra column that contains all of its translations.\nThis approach drastically reduces the number of required JOINs when filtering or\nfetching records.\n\n`Trans` is lightweight and modularized. The `Trans` module provides metadata\nthat is used by the `Trans.Translator` and `Trans.QueryBuilder` modules, which\nimplement the main functionality of this library.\n\n\n## Setup and Quickstart\n\nLet's say that we have an `Article` schema that contains texts in English and we want to translate it to other languages.\n\n```elixir\ndefmodule MyApp.Article do\n  use Ecto.Schema\n\n  schema \"articles\" do\n    field :title, :string\n    field :body, :string\n  end\nend\n```\n\nThe first step would be to add a new JSONB column to the table so we can store the translations in it.\n\n```elixir\ndefmodule MyApp.Repo.Migrations.AddTranslationsToArticles do\n  use Ecto.Migration\n\n  def change do\n    alter table(:articles) do\n      add :translations, :map\n    end\n  end\nend\n```\n\nOnce we have the new database column, we can update the Article schema to include the translations.\n\n```elixir\ndefmodule MyApp.Article do\n  use Ecto.Schema\n  use Trans, translates: [:title, :body], default_locale: :en\n\n  schema \"articles\" do\n    field :title, :string\n    field :body, :string\n\n    # This generates a MyApp.Article.Translations schema with a\n    # MyApp.Article.Translations.Fields for :es and :fr\n    translations [:es, :fr]\n  end\nend\n```\n\nAfter doing this we can leverage the [Trans.Translator](https://hexdocs.pm/trans/Trans.Translator.html) and [Trans.QueryBuilder](https://hexdocs.pm/trans/Trans.QueryBuilder.html) modules to fetch and query translations from the database.\n\nThe translation storage can be done using normal `Ecto.Changeset` functions just like it would be done for any other fields or associations.\n\n```elixir\ndefmodule MyApp.Article do\n  def changeset(article, attrs \\\\ %{}) do\n    article\n    |\u003e cast(attrs, [:title, :body])\n    |\u003e validate_required([:title, :body])\n    |\u003e cast_embed(:translations, with: \u0026cast_translations/2)\n  end\n\n  defp cast_translations(translations, attrs \\\\ %{}) do\n    translations\n    |\u003e cast(attrs, [])\n    |\u003e cast_embed(:es)\n    |\u003e cast_embed(:fr)\n  end\nend\n\n# Then, anywhere in your code:\nchangeset = MyApp.Article.changeset(article, %{\n  translations: %{\n    es: %{title: \"title ES\", body: \"body ES\"},\n    fr: %{title: \"title FR\", body: \"body FR\"}\n  }\n})\n```\n\n## Customizing the translation container\n\nBy default Trans looks for a `translations` field that contains the translations. This is known as the \"translation container\".\n\nYou can override the default translation container passing the `container` option to Trans. In the following example the translations will be stored in the `transcriptions` field.\n\n```elixir\ndefmodule MyApp.Article do\n  use Ecto.Schema\n  use Trans, translates: [:title, :body], default_locale: :en, container: :transcriptions\n\n  schema \"articles\" do\n    field :title, :string\n    field :body, :strings\n    translations [:es, :fr]\n  end\nend\n```\n\n## Customizing the translation schemas\n\nIf you want to use your own translation module you can simply pass the `build_field_schema: false` option when using the `translations` macro.\n\n```elixir\ndefmodule MyApp.Article do\n  use Ecto.Schema\n  use Trans, translates: [:title, :body], default_locale: :en\n\n  defmodule Translations.Fields do\n    use Ecto.Schema\n\n    embedded_schema do\n      field :title, :string\n      field :body, :string\n    end\n  end\n\n  schema \"articles\" do\n    field :title, :string\n    field :body, :string\n\n    translations [:es, :fr], build_field_schema: false\n  end\nend\n```\n\n\n## Is Trans dead?\n\nTrans has a slow release cadence, but that does not mean that it is dead. Trans can be considered as \"done\" in the sense that it does one thing and does it well.\n\nNew releases will happen when there are bugs or new changes. **If the last release is from a long time ago you should take this as a sign of stability and maturity, not as a sign of abandonment.**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrbelaus%2Ftrans","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrbelaus%2Ftrans","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrbelaus%2Ftrans/lists"}