Here’s the fix:
defmodule Octafest.Api.Post do
use Ash.Resource,
data_layer: AshSqlite.DataLayer
sqlite do
table "posts"
repo Octafest.Repo
end
attributes do
uuid_primary_key :id
attribute :title, Octafest.Shared.Translated
attribute :content, Octafest.Shared.Translated
end
actions do
defaults ~w(create read update destroy)a
read :by_id do
argument :id, :uuid, allow_nil?: false
get? true
filter expr(id == ^arg(:id))
end
read :translated do
argument :locale, :atom, allow_nil?: false
prepare build(
load: [
{:t_title, locale: arg(:locale)},
{:t_content, locale: arg(:locale)}
]
)
end
end
calculations do
calculate :t_title, :string, {TranslateAttribute, attribute: :title} do
argument :locale, :atom do
default :en
end
end
calculate :t_content, :string, {TranslateAttribute, attribute: :content} do
argument :locale, :atom do
default :en
end
end
end
code_interface do
define_for Octafest.Api
define :create, action: :create
define :read_all, action: :read
define :update, action: :update
define :destroy, action: :destroy
define :get_by_id, args: [:id], action: :by_id
define :translated, args: [:locale], action: :translated
end
end
defmodule TranslateAttribute do
use Ash.Calculation
@impl true
def select(_, opts, _), do: [opts[:attribute]]
@impl true
@spec calculate(any(), any(), %{:locale => atom(), optional(any()) => any()}) :: list()
def calculate(records, opts, %{locale: locale}) do
records
|> Enum.map(fn record ->
record |> Map.get(opts[:attribute]) |> Map.get(locale)
end)
end
end
And this is how you call it:
defmodule OctafestWeb.AdminLive do
use OctafestWeb, :admin_live_view
alias Octafest.Api.Post
def mount(_params, _session, socket) do
posts = Post.translated!(:en)
locales = ~w(en lt)a
create_form = get_post_create_form()
socket =
assign(socket,
posts: posts,
locales: locales,
locale: :en
)
{:ok, socket}
end
end
But i’ve decided against using this, as this is quite a bit of code and while i could most likely add an extension it feels somewhat magical.