PhoenixKit.Modules.Storage.FileDetails (phoenix_kit v2.32.1)

Copy Markdown View Source

A media file's translatable details — title, alt text and description — in one language.

Not a table. The text lives on the file row's data column (V199), and this module is the one read and write path for it:

%{
  "en-US" => %{"title" => "Harbour", "alt" => "Boats in a harbour"},
  "et" => %{"title" => "Sadam"}
}

Every language holds its own text, independently. No language is marked as the primary one in the data, so a site that changes its primary language has nothing to convert: which text stands in for a missing translation is decided when it is read.

This is deliberately not the PhoenixKit.Utils.Multilang structure, which stores every other language as a diff against an embedded primary. That pays off for a record with dozens of fields; for three it buys nothing and ties the stored text to a setting that can change.

Reading

FileDetails.translated_alt(file, "et")      # "" when there is none
FileDetails.translated_title(file, "et")    # nil when there is none
FileDetails.for_locale(file, "et")          # all three

Each field resolves on its own: the language asked for (or a dialect of it) → the site's current primary language → any other language. The alt text never falls back to the file name: a file name read aloud helps nobody, and alt="" correctly marks the image as undescribed.

The site's primary language is read from PhoenixKit.Utils.Multilang; a caller resolving many files passes it once as primary:.

Files from before V199

They hold a title and description in metadata, in no recorded language. While a file's data is empty that text is its primary-language text. The first save moves it into data; from then on data is the truth, and metadata only receives a copy of the primary-language text for the readers that still look there (the PDF processor's fill-if-missing title, the viewer's stored title).

Writing

A save replaces one language's text and nothing else:

Storage.update_file_details(file, %{"title" => "Sadam"}, lang: "et")

Summary

Functions

Changeset for one language's text. attrs holds "title", "alt" and "description"; a key that is absent keeps its current value.

The content language a page shown in locale edits and reads: the enabled language that locale names — so a URL's bare "et" and the hook's "et-EE" store under the one code the site has enabled. A locale that is no enabled language (the Gettext default on a site that never enabled it) is the primary language: text must not pile up under a code no page is ever shown in.

The translatable field names.

The File.details_changeset/2 attrs that store details as file's text in lang.

All three details of file in locale.

The text file holds in lang itself — no fallback to another language, so an editor shows what that language really has. "Itself" includes the same language stored at another precision ("en" for "en-US"), which a save replaces; it does not include a sibling dialect ("en-GB"), which is another language — an editor opened on it would save a copy of it.

The display name of content language lang — or nil on a site with a single language, where there is nothing to tell the editor.

One detail of file in locale: that language's text, else the primary language's, else any language's, else nil. A nil locale reads the primary language.

The alt text in locale, ready for an alt attribute: "" when the file has none. Never the file name.

The description in locale, or nil.

Types

t()

@type t() :: %PhoenixKit.Modules.Storage.FileDetails{
  alt: String.t() | nil,
  description: String.t() | nil,
  title: String.t() | nil
}

Functions

changeset(details, attrs)

@spec changeset(t(), map()) :: Ecto.Changeset.t()

Changeset for one language's text. attrs holds "title", "alt" and "description"; a key that is absent keeps its current value.

content_language(locale, opts \\ [])

@spec content_language(String.t() | nil, keyword()) :: String.t()

The content language a page shown in locale edits and reads: the enabled language that locale names — so a URL's bare "et" and the hook's "et-EE" store under the one code the site has enabled. A locale that is no enabled language (the Gettext default on a site that never enabled it) is the primary language: text must not pile up under a code no page is ever shown in.

Options: :languages — the enabled language codes, :primary.

fields()

@spec fields() :: [String.t()]

The translatable field names.

file_attrs(file, details, lang \\ nil, opts \\ [])

@spec file_attrs(
  PhoenixKit.Modules.Storage.File.t(),
  t(),
  String.t() | nil,
  keyword()
) :: %{
  metadata: map(),
  data: map()
}

The File.details_changeset/2 attrs that store details as file's text in lang.

Only that language's entry in data changes (an entry left with no text is removed). A file from before V199 first gets its metadata text moved into data under the primary language. When lang is the primary language, metadata receives a copy — a cleared field as "", not removed: the PDF processor fills a missing "title" from the document, and a title the user cleared must stay cleared.

for_locale(file, locale \\ nil, opts \\ [])

@spec for_locale(PhoenixKit.Modules.Storage.File.t(), String.t() | nil, keyword()) ::
  %{
    title: String.t() | nil,
    alt: String.t(),
    description: String.t() | nil
  }

All three details of file in locale.

from_file(file, lang \\ nil, opts \\ [])

@spec from_file(PhoenixKit.Modules.Storage.File.t(), String.t() | nil, keyword()) ::
  t()

The text file holds in lang itself — no fallback to another language, so an editor shows what that language really has. "Itself" includes the same language stored at another precision ("en" for "en-US"), which a save replaces; it does not include a sibling dialect ("en-GB"), which is another language — an editor opened on it would save a copy of it.

Options: :primary — the site's primary language (default: the current one).

language_name(lang)

@spec language_name(String.t()) :: String.t() | nil

The display name of content language lang — or nil on a site with a single language, where there is nothing to tell the editor.

translated(file, field, locale \\ nil, opts \\ [])

@spec translated(
  PhoenixKit.Modules.Storage.File.t(),
  String.t(),
  String.t() | nil,
  keyword()
) ::
  String.t() | nil

One detail of file in locale: that language's text, else the primary language's, else any language's, else nil. A nil locale reads the primary language.

Options: :primary — the site's primary language (default: the current one).

translated_alt(file, locale \\ nil, opts \\ [])

@spec translated_alt(PhoenixKit.Modules.Storage.File.t(), String.t() | nil, keyword()) ::
  String.t()

The alt text in locale, ready for an alt attribute: "" when the file has none. Never the file name.

translated_description(file, locale \\ nil, opts \\ [])

@spec translated_description(
  PhoenixKit.Modules.Storage.File.t(),
  String.t() | nil,
  keyword()
) ::
  String.t() | nil

The description in locale, or nil.

translated_title(file, locale \\ nil, opts \\ [])

@spec translated_title(
  PhoenixKit.Modules.Storage.File.t(),
  String.t() | nil,
  keyword()
) ::
  String.t() | nil

The title in locale, or nil.