From 0b6ae26f3b4c3c8278e569fa6644a544e996c25d Mon Sep 17 00:00:00 2001 From: erdgeist Date: Mon, 13 Jul 2026 16:36:38 +0200 Subject: Add per-node translation management: list, edit, destroy, compare Replaces the old locale-switch-and-edit-the-same-screen workflow, which conflated presentation locale with content locale and let an editor silently drift into editing the wrong language with no persistent signal that anything had changed. Non-default-locale content now has its own explicit routes and screens, never sharing a route param with the ambient chrome locale. - PageTranslationsController: index/show/edit/update/destroy, scoped to Page.non_default_locales; update handles first-time creation too, so there's no separate new/create step. - Reads go through the actual PageTranslation row (Page#translation_summary), never through the Globalize fallback-bearing accessor -- fallback is correct for public rendering but wrong for editing, where a missing translation needs to look empty, not borrowed from another locale. - Translations ride on the page's own draft/head cycle; no independent publish state. - nodes#show gains a Translations section (per-locale Lock+Edit / Create+Lock, Destroy, a link into the read-only Compare view) and a locale indicator on its own default-locale content; nodes#edit, nodes#update, and nodes#autosave are pinned to the default locale via Globalize.with_locale regardless of the ambient route locale. - nodes#show no longer double-loads the node or calls wipe_draft! on every view (see previous commit for why that's now safe). - .preview_link_row is renamed .aligned_action_row now that it has a second real consumer. --- app/views/nodes/show.html.erb | 49 +++++++++++++++++++++++++++--- app/views/page_translations/edit.html.erb | 39 ++++++++++++++++++++++++ app/views/page_translations/index.html.erb | 19 ++++++++++++ app/views/page_translations/show.html.erb | 27 ++++++++++++++++ 4 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 app/views/page_translations/edit.html.erb create mode 100644 app/views/page_translations/index.html.erb create mode 100644 app/views/page_translations/show.html.erb (limited to 'app/views') diff --git a/app/views/nodes/show.html.erb b/app/views/nodes/show.html.erb index 54bb38f..e6a922b 100644 --- a/app/views/nodes/show.html.erb +++ b/app/views/nodes/show.html.erb @@ -1,6 +1,6 @@ <% locked_by_other = @node.locked? && @node.lock_owner != current_user %>
-

<%= title_for_node(@node) %>

+

<%= title_for_node(@node) %> (<%= I18n.default_locale.to_s.upcase %>)

Status
@@ -70,6 +70,46 @@ <% end %>
+
Translations
+
+
+
+ <%= I18n.default_locale.to_s.upcase %> (default) + <%= @page.title %> — updated <%= @default_translation&.updated_at || @page.updated_at %> +
+ <% @translations.each do |t| %> +
+ <%= t[:locale].to_s.upcase %> +
+ <% if t[:exists] %> + <%= link_to t[:title], node_translation_path(@node, t[:locale]) %> — updated <%= t[:updated_at] %> + <% if t[:outdated] %> + may be outdated + <% end %> + <% else %> + Not yet translated. + <% end %> + <% if locked_by_other %> + <%= t[:exists] ? "Lock + Edit" : "Create translation + Lock" %> + <% else %> + <%= link_to edit_node_translation_path(@node, t[:locale]), class: "action_button" do %> + <%= icon(t[:exists] ? "edit" : "language", library: "tabler", "aria-hidden": true) %> + <%= t[:exists] ? "Lock + Edit" : "Create translation + Lock" %> + <% end %> + <% if t[:exists] %> + <%= button_to node_translation_path(@node, t[:locale]), method: :delete, + form: { data: { confirm: "This cannot be undone. Continue?" }, class: 'button_to destructive' } do %> + <%= icon("trash", library: "tabler", "aria-hidden": true) %> + Destroy Translation + <% end %> + <% end %> + <% end %> +
+
+ <% end %> +
+
+
People
@@ -122,7 +162,7 @@
Public Preview <% if @node.draft.preview_token.present? %> - <% end %> -
Abstract
+
Abstract (<%= I18n.default_locale.to_s.upcase %>)
<%= sanitize(@page.abstract) %>
-
Body
+
Body (<%= I18n.default_locale.to_s.upcase %>)
<%= sanitize(@page.body) %>
- <%# Assets not yet addressed - no confirmed model/association seen for this page's attached assets %>
diff --git a/app/views/page_translations/edit.html.erb b/app/views/page_translations/edit.html.erb new file mode 100644 index 0000000..7371a42 --- /dev/null +++ b/app/views/page_translations/edit.html.erb @@ -0,0 +1,39 @@ +
+ Editing the <%= @locale.to_s.upcase %> translation of <%= title_for_node(@node) %>. +
+ +

+ Metadata such as images, tags, template, and author belong to the page + as a whole, not to a single translation — change those from the + <%= link_to 'default-locale editor', edit_node_path(@node) %>. +

+ +
+ <%= button_to 'Unlock + Back', unlock_node_path(@node), method: :put, + form: { class: 'button_to state_changing' }, + disabled: @node.autosave.present? %> + <%= submit_tag "Save #{@locale.to_s.upcase} translation", form: "translation_edit_form" %> + <%= submit_tag "Save + Unlock + Exit", form: "translation_edit_form" %> + <%= link_to "Preview ↗", preview_page_path(@page, :locale => @locale), target: "_blank", rel: "noopener", class: "preview_link" %> +
+ +
+ <%= form_with url: node_translation_path(@node, @locale), method: :patch, local: true, id: "translation_edit_form" do |f| %> +
+
Title
+
+ <%= text_field_tag "page[title]", @translation&.title %> +
+ +
Abstract
+
+ <%= text_area_tag "page[abstract]", @translation&.abstract %> +
+ +
Body
+
+ <%= text_area_tag "page[body]", @translation&.body, :class => 'with_editor' %> +
+
+ <% end %> +
diff --git a/app/views/page_translations/index.html.erb b/app/views/page_translations/index.html.erb new file mode 100644 index 0000000..a8c4f5d --- /dev/null +++ b/app/views/page_translations/index.html.erb @@ -0,0 +1,19 @@ +

Translations — <%= title_for_node(@node) %>

+ +
+
+ <% @translations.each do |t| %> +
+ <%= t[:locale].to_s.upcase %> + <% if t[:exists] %> + <%= t[:title] %> — updated <%= t[:updated_at] %> + <% if t[:outdated] %>may be outdated<% end %> + <%= link_to 'Edit', edit_node_translation_path(@node, t[:locale]) %> + <% else %> + Not yet translated. + <%= link_to 'Add', edit_node_translation_path(@node, t[:locale]) %> + <% end %> +
+ <% end %> +
+
diff --git a/app/views/page_translations/show.html.erb b/app/views/page_translations/show.html.erb new file mode 100644 index 0000000..c5bd151 --- /dev/null +++ b/app/views/page_translations/show.html.erb @@ -0,0 +1,27 @@ +

Compare — <%= title_for_node(@node) %>

+ +
+
+

<%= @locale.to_s.upcase %>

+
Title
+
<%= @translation&.title %>
+ +
Abstract
+
<%= sanitize(@translation&.abstract.to_s) %>
+ +
Body
+
<%= sanitize(@translation&.body.to_s) %>
+
+ +
+

<%= I18n.default_locale.to_s.upcase %> (default)

+
Title
+
<%= @default_translation&.title %>
+ +
Abstract
+
<%= sanitize(@default_translation&.abstract.to_s) %>
+ +
Body
+
<%= sanitize(@default_translation&.body.to_s) %>
+
+
-- cgit v1.3