From 0a5d7806700bb3154529b99abc75d37ccc46faff Mon Sep 17 00:00:00 2001 From: erdgeist Date: Sat, 25 Jul 2026 02:23:13 +0200 Subject: Extract admin strings to i18n: the nodes cluster --- app/views/nodes/_node_list.html.erb | 16 +-- app/views/nodes/chapters.html.erb | 4 +- app/views/nodes/drafts.html.erb | 2 +- app/views/nodes/edit.html.erb | 70 ++++++------ app/views/nodes/index.html.erb | 16 +-- app/views/nodes/mine.html.erb | 2 +- app/views/nodes/new.html.erb | 24 ++--- app/views/nodes/show.html.erb | 148 +++++++++++++------------- app/views/nodes/sitemap.html.erb | 2 +- app/views/nodes/tags.html.erb | 2 +- app/views/nodes/trashed.html.erb | 24 ++--- config/locales/de.yml | 171 ++++++++++++++++++++++++++++++ config/locales/en.yml | 171 ++++++++++++++++++++++++++++++ test/controllers/nodes_controller_test.rb | 7 +- 14 files changed, 499 insertions(+), 160 deletions(-) diff --git a/app/views/nodes/_node_list.html.erb b/app/views/nodes/_node_list.html.erb index f7210f12..1e5d0ed9 100644 --- a/app/views/nodes/_node_list.html.erb +++ b/app/views/nodes/_node_list.html.erb @@ -3,23 +3,23 @@ <%= hidden_field_tag "kinds[]", kind %> <% end %> <%= hidden_field_tag :tags, params[:tags] if params[:tags].present? %> - <%= text_field_tag :q, params[:q], placeholder: "Search title, abstract, body…" %> + <%= text_field_tag :q, params[:q], placeholder: t(".search_placeholder") %> <%= button_tag type: "submit", class: "action_button" do %> - <%= icon("search", library: "tabler", "aria-hidden": true) %> Search + <%= icon("search", library: "tabler", "aria-hidden": true) %> <%= t("admin.common.search") %> <% end %> <% if params[:q].present? || params[:kinds].present? || params[:tags].present? %> - <%= link_to "Reset", url_for(controller: params[:controller], action: params[:action]) %> + <%= link_to t("admin.common.reset"), url_for(controller: params[:controller], action: params[:action]) %> <% end %> <% end %> <%= will_paginate @nodes %> - - - - - + + + + + <% @nodes.each do |node| %> "> diff --git a/app/views/nodes/chapters.html.erb b/app/views/nodes/chapters.html.erb index 543bc53c..def56659 100644 --- a/app/views/nodes/chapters.html.erb +++ b/app/views/nodes/chapters.html.erb @@ -1,10 +1,10 @@ -

Chapters

+

<%= t(".title") %>

<%= form_tag chapters_nodes_path, method: :get, class: "node_search_form" do %> <%= button_tag type: "submit", class: "action_button" do %> - <%= icon("filter", library: "tabler", "aria-hidden": true) %> Filter + <%= icon("filter", library: "tabler", "aria-hidden": true) %> <%= t(".filter") %> <% end %> <% end %> diff --git a/app/views/nodes/drafts.html.erb b/app/views/nodes/drafts.html.erb index 6d0830c9..84ba0e07 100644 --- a/app/views/nodes/drafts.html.erb +++ b/app/views/nodes/drafts.html.erb @@ -1,3 +1,3 @@ -

Nodes with drafts or autosaves

+

<%= t(".title") %>

<%= render 'node_list' %> diff --git a/app/views/nodes/edit.html.erb b/app/views/nodes/edit.html.erb index 4d58e62c..1d07a21e 100644 --- a/app/views/nodes/edit.html.erb +++ b/app/views/nodes/edit.html.erb @@ -1,18 +1,18 @@

<%= title_for_node(@node) %>

- <%= button_to 'Unlock + Back', unlock_node_path(@node), method: :put, + <%= button_to t(".unlock_back"), unlock_node_path(@node), method: :put, form: { class: 'button_to state_changing' }, disabled: @node.autosave.present? %> <% if @node.autosave || (@node.draft && @node.head) %> <%= button_to revert_node_path(@node), method: :put, - form: { data: { confirm: "This cannot be undone. Continue?" }, class: 'button_to destructive' } do %> + form: { data: { confirm: t(".confirm_discard") }, class: 'button_to destructive' } do %> <%= icon("trash", library: "tabler", "aria-hidden": true) %> - <%= @node.draft && !@node.autosave ? "Destroy Draft" : "Discard Autosave" %> + <%= @node.draft && !@node.autosave ? t(".destroy_draft") : t(".discard_autosave") %> <% end %> <% if pair = @node.available_layer_pairs.find { |p| p.include?(:autosave) } %> - <%= button_to 'What changed?', + <%= button_to t(".what_changed"), diff_node_revisions_path(@node), method: :get, params: { start_revision: pair.first, end_revision: pair.last }, @@ -20,9 +20,9 @@ <% end %> <% end %> - <%= submit_tag "Save Draft", form: dom_id(@node, :edit) %> - <%= submit_tag "Save + Unlock + Exit", form: dom_id(@node, :edit) %> - <%= link_to "Preview ↗", preview_page_path(@page), target: "_blank", rel: "noopener", class: "preview_link" %> + <%= submit_tag t(".save_draft"), form: dom_id(@node, :edit) %> + <%= submit_tag t(".save_unlock_exit"), form: dom_id(@node, :edit) %> + <%= link_to t(".preview"), preview_page_path(@page), target: "_blank", rel: "noopener", class: "preview_link" %>
@@ -37,13 +37,13 @@
-
Title
+
<%= t("admin.columns.title") %>
<%= d.text_field :title %>
-
Abstract
+
<%= t(".abstract") %>
<%= d.text_area :abstract %>
-
Attachments
+
<%= t(".attachments") %>
@@ -81,10 +81,10 @@
  • <%= icon("grip-vertical", library: "tabler", "aria-hidden": true) %> - -
  • @@ -92,9 +92,9 @@
    - Metadata (slug, parent, tags, template, author, images) + <%= t(".metadata_summary") %>
    -
    Slug
    +
    <%= t(".slug") %>
    <%= f.text_field( :staged_slug, :value => @node.staged_slug || @node.slug @@ -102,10 +102,10 @@ %>
    -
    parent
    +
    <%= t(".parent") %>
    <%= text_field_tag :move_to_search_term, @node.parent.title rescue "" %> -

    Start typing to find a new parent for this node.

    +

    <%= t(".parent_hint") %>

    <%= f.hidden_field( :staged_parent_id, @@ -114,22 +114,22 @@ %>
    -
    Tags
    +
    <%= t(".tags") %>
    <%= text_field_tag :tag_list, @page.tag_list.join(', ') %> - Comma separated, lowercase. + <%= t(".tags_hint") %>
    -
    Publish at
    +
    <%= t(".publish_at") %>
    <%= d.datetime_select :published_at, :value => @page.published_at %>
    -
    Template
    +
    <%= t(".template") %>
    <%= d.select :template_name, custom_page_templates, {:prompt => 'Select Template'} %> - Set automatically based on how this node was created - change it if needed. + <%= t(".template_hint") %>
    -
    Author
    +
    <%= t(".author") %>
    <%= d.select :user_id, user_list, :selected => @page.user_id || @node.draft&.user_id || @node.head&.user_id %> @@ -139,13 +139,13 @@
    -
    Body
    +
    <%= t(".body") %>
    - -
    @@ -154,7 +154,7 @@
    <%= d.text_area :body, :class => 'with_editor' %>
    @@ -162,8 +162,8 @@ <% end %>
    - <%= f.submit 'Save Draft' %> - <%= f.submit 'Save + Unlock + Exit' %> + <%= f.submit t(".save_draft") %> + <%= f.submit t(".save_unlock_exit") %>
    <% end %> diff --git a/app/views/nodes/index.html.erb b/app/views/nodes/index.html.erb index 3bb251c5..7ab5679e 100644 --- a/app/views/nodes/index.html.erb +++ b/app/views/nodes/index.html.erb @@ -1,20 +1,20 @@ -

    Nodes

    +

    <%= t(".title") %>

    <%= link_to new_node_path, class: 'action_button' do %> - <%= icon("plus", library: "tabler", "aria-hidden": true) %> Create node + <%= icon("plus", library: "tabler", "aria-hidden": true) %> <%= t(".create_node") %> <% end %> - Creates a node with no parent or kind set — for a specific page, use the wizard's create flow or "add child" from an existing node instead. + <%= t(".create_hint") %>
    <%= will_paginate @nodes %>
    IDTitleActionsLocked byRev.<%= t("admin.columns.id") %><%= t("admin.columns.title") %><%= t("admin.columns.actions") %><%= t("admin.columns.locked_by") %><%= t("admin.columns.rev") %>
    - - - - - + + + + + <% @nodes.each do |node| %> "> diff --git a/app/views/nodes/mine.html.erb b/app/views/nodes/mine.html.erb index fc5680a9..84ba0e07 100644 --- a/app/views/nodes/mine.html.erb +++ b/app/views/nodes/mine.html.erb @@ -1,3 +1,3 @@ -

    My work

    +

    <%= t(".title") %>

    <%= render 'node_list' %> diff --git a/app/views/nodes/new.html.erb b/app/views/nodes/new.html.erb index f9efbf50..b56bbeed 100644 --- a/app/views/nodes/new.html.erb +++ b/app/views/nodes/new.html.erb @@ -1,4 +1,4 @@ -

    Create new node

    +

    <%= t(".title") %>

    <% if @node.errors.any? %>
    @@ -9,7 +9,7 @@ <%= form_tag nodes_path do %>
    -
    Type
    +
    <%= t(".type") %>
    <% CccConventions::NODE_KINDS.each do |kind, config| %>

    @@ -23,14 +23,14 @@ <% end %>

    -
    Title
    +
    <%= t("admin.columns.title") %>
    <%= text_field_tag :title, nil, required: true %> - A URL slug will be generated from this automatically. You can review or adjust it afterward under the "metadata" section's Slug field. + <%= t(".slug_hint") %>
    "> -
    Parent
    +
    <%= t(".parent") %>
    <%= text_field_tag :parent_search_term, @parent&.title %> <%= hidden_field_tag :parent_id, @parent&.id, data: { unique_name: @parent&.computed_unique_name } %> @@ -38,18 +38,18 @@
    -
    Resulting path
    +
    <%= t(".resulting_path") %>
    - This preview updates as you type. The final path can still be changed afterward by editing the title (for the last segment) or moving the node to a different parent (for everything before it). + <%= t(".path_hint") %>
    <% if @attach_asset %> -
    attachment
    +
    <%= t(".attachment") %>
    • @@ -60,14 +60,14 @@ :target => "_blank", :rel => "noopener" %>
    - This asset will automatically be attached to the new page. - + <%= t(".attach_hint") %> + <%= hidden_field_tag :asset_id, @attach_asset.id %>
    <% end %>
    -
    <%= submit_tag "Create" %>
    +
    <%= submit_tag t("admin.common.create") %>
    <% end %> diff --git a/app/views/nodes/show.html.erb b/app/views/nodes/show.html.erb index e37f5034..a337b555 100644 --- a/app/views/nodes/show.html.erb +++ b/app/views/nodes/show.html.erb @@ -2,24 +2,24 @@

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

    -
    Status
    +
    <%= t(".status") %>
    - Head - <%= @node.head ? "#{@node.head.title} (rev #{@node.head.revision}, #{@node.head.updated_at})" : "none — never published" %> + <%= t(".head") %> + <%= @node.head ? t(".head_line", :title => @node.head.title, :rev => @node.head.revision, :time => @node.head.updated_at) : t(".never_published") %>
    - Draft - <%= @node.draft ? "#{@node.draft.title} (rev #{@node.draft.revision}, saved #{@node.draft.updated_at})" : "none" %> + <%= t(".draft") %> + <%= @node.draft ? t(".draft_line", :title => @node.draft.title, :rev => @node.draft.revision, :time => @node.draft.updated_at) : t(".none") %>
    - Autosave - <%= @node.autosave ? "#{@node.autosave.title} (unsaved, #{@node.autosave.updated_at})" : "none" %> + <%= t(".autosave") %> + <%= @node.autosave ? t(".autosave_line", :title => @node.autosave.title, :time => @node.autosave.updated_at) : t(".none") %>
    - <% edit_label = @node.autosave ? "Continue Editing" : (@node.draft ? "Edit Draft" : "Edit") %> + <% edit_label = @node.autosave ? t(".continue_editing") : (@node.draft ? t(".edit_draft") : t(".lock_edit")) %>
    <% if locked_by_other %> @@ -27,17 +27,17 @@ <% else %> <%= link_to edit_node_path(@node), class: "action_button" do %> <%= icon("edit", library: "tabler", "aria-hidden": true) %> - <%= @node.autosave ? "Continue Editing" : (@node.draft ? "Edit Draft" : "Lock + Edit") %> + <%= edit_label %> <% end %> <% if !@node.draft && !@node.autosave %> - Nothing pending — this will start a fresh draft. + <%= t(".fresh_draft_hint") %> <% end %> <% end %>
    <% @node.available_layer_pairs.each do |pair| %>
    - <%= button_to "Diff #{pair.first.to_s.capitalize} vs. #{pair.last.to_s.capitalize}", + <%= button_to t(".diff_layers", :from => t(".layer_#{pair.first}"), :to => t(".layer_#{pair.last}")), diff_node_revisions_path(@node), method: :get, params: { start_revision: pair.first, end_revision: pair.last }, @@ -48,26 +48,26 @@ <% unless locked_by_other %> <% if @node.draft && !@node.autosave && !@node.in_trash? && !@node.trash_node? %>
    - <%= button_to 'Publish', publish_node_path(@node), method: :put, - form: { data: { confirm: "Publish this draft?" }, class: 'button_to state_changing' } %> + <%= button_to t(".publish"), publish_node_path(@node), method: :put, + form: { data: { confirm: t(".confirm_publish") }, class: 'button_to state_changing' } %>
    <% end %> <% if @node.autosave || (@node.draft && @node.head) %>
    <%= button_to revert_node_path(@node), method: :put, params: { return_to: request.path }, - form: { data: { confirm: "This cannot be undone. Continue?" }, class: 'button_to destructive' } do %> + form: { data: { confirm: t("admin.common.confirm_irreversible") }, class: 'button_to destructive' } do %> <%= icon("trash", library: "tabler", "aria-hidden": true) %> - <%= @node.draft && !@node.autosave ? "Destroy Draft" : "Discard Autosave" %> + <%= @node.draft && !@node.autosave ? t(".destroy_draft") : t(".discard_autosave") %> <% end %>
    <% end %> <% unless @node.trash_node? || @node.in_trash? || @node.root? %>
    <%= button_to trash_node_path(@node), method: :put, - form: { data: { confirm: "Move this page and everything beneath it to the Trash? All published content in it will go offline." }, class: 'button_to destructive' } do %> + form: { data: { confirm: t(".confirm_trash") }, class: 'button_to destructive' } do %> <%= icon("trash", library: "tabler", "aria-hidden": true) %> - Move to Trash + <%= t(".move_to_trash") %> <% end %>
    <% end %> @@ -75,79 +75,77 @@
    <% if locked_by_other %> - Locked — see People below to unlock before editing, publishing, or discarding. + <%= t(".locked_hint") %> <% end %>
    <% if @node.in_trash? %> -
    Trash
    +
    <%= t(".trash") %>
    <% if (entry = @node.last_trash_entry) && entry.metadata.dig("path", "from") %>
    - Was at + <%= t(".was_at") %> <%= entry.metadata.dig("path", "from") %>
    <% end %>
    - Restore to + <%= t(".restore_to") %> <% suggestion = @node.suggested_restore_parent %> <%= form_tag restore_from_trash_node_path(@node), :method => :put, :class => "aligned_action_row" do %>
    <%= text_field_tag :restore_search_term, suggestion && (suggestion.head&.title || suggestion.draft&.title || suggestion.slug), - :placeholder => "Search for a new parent…" %> + :placeholder => t(".restore_placeholder") %>
    <%= hidden_field_tag :parent_id, suggestion&.id %> - <%= submit_tag "Restore here", :class => "action_button action_button_no_margin_top" %> + <%= submit_tag t(".restore_here"), :class => "action_button action_button_no_margin_top" %> <% end %> - Restored pages come back unpublished. Republish each page deliberately. + <%= t(".restore_hint") %>
    <% doomed_below = @node.descendants.count %> <%= button_to node_path(@node), method: :delete, - form: { data: { confirm: doomed_below > 0 ? - "Delete this page and the #{doomed_below} pages beneath it permanently? This cannot be undone." : - "Delete this page permanently? This cannot be undone." }, + form: { data: { confirm: t(".confirm_delete", :count => doomed_below) }, class: 'button_to destructive' } do %> <%= icon("trash-x", library: "tabler", "aria-hidden": true) %> - Delete permanently<%= " (including #{doomed_below} beneath)" if doomed_below > 0 %> + <%= t(".delete_permanently") %><%= " " + t(".including_beneath", :count => doomed_below) if doomed_below > 0 %> <% end %>
    <% end %> -
    Translations
    +
    <%= t(".translations") %>
    - <% @translations.each do |t| %> + <% @translations.each do |translation| %>
    - <%= t[:locale].to_s.upcase %> + <%= translation[: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 + <% if translation[:exists] %> + <%= link_to translation[:title], node_translation_path(@node, translation[:locale]) %> <%= t(".updated", :time => translation[:updated_at]) %> + <% if translation[:outdated] %> + <%= t(".may_be_outdated") %> <% end %> <% else %> - Not yet translated. + <%= t(".not_yet_translated") %> <% end %> <% if locked_by_other %> - <%= t[:exists] ? "Lock + Edit" : "Create translation + Lock" %> + <%= translation[:exists] ? t(".lock_edit") : t(".create_translation") %> <% 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" %> + <%= link_to edit_node_translation_path(@node, translation[:locale]), class: "action_button" do %> + <%= icon(translation[:exists] ? "edit" : "language", library: "tabler", "aria-hidden": true) %> + <%= translation[:exists] ? t(".lock_edit") : t(".create_translation") %> <% 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 %> + <% if translation[:exists] %> + <%= button_to node_translation_path(@node, translation[:locale]), method: :delete, + form: { data: { confirm: t("admin.common.confirm_irreversible") }, class: 'button_to destructive' } do %> <%= icon("trash", library: "tabler", "aria-hidden": true) %> - Destroy Translation + <%= t(".destroy_translation") %> <% end %> <% end %> <% end %> @@ -157,20 +155,20 @@
    -
    People
    +
    <%= t(".people") %>
    - Author + <%= t(".author") %> <%= @page.user.try(:login) %>
    - Editor + <%= t(".editor") %> <%= @page.editor.try(:login) %>
    <% if @node.locked? %>
    - Locked by + <%= t("admin.columns.locked_by") %> <%= @node.lock_owner.login %> <%= unlock_link %>
    @@ -178,54 +176,54 @@
    -
    Dates
    +
    <%= t(".dates") %>
    - Last updated + <%= t(".last_updated") %> <%= @page.updated_at %>
    <% if @page.published_at.present? %>
    - <%= @page.public? ? 'Published at' : 'Will publish at' %> + <%= @page.public? ? t(".published_at") : t(".will_publish_at") %> <%= @page.published_at %>
    <% end %>
    -
    Links
    +
    <%= t(".links") %>
    - Public + <%= t(".public") %> <%= link_to @page.public_link, content_url(@node.unique_path) %>
    <% if @node.draft %>
    - Admin Preview + <%= t(".admin_preview") %> <%= link_to preview_page_path(@node.draft), preview_page_path(@node.draft) %>
    - Public Preview + <%= t(".public_preview") %> <% if @node.draft.preview_token.present? %>
    <%= link_to shared_preview_path(token: @node.draft.preview_token), shared_preview_url(token: @node.draft.preview_token), target: "_blank", rel: "noopener" %> - <%= button_to 'Revoke', revoke_shared_preview_node_path(@node), method: :put, form: { data: { confirm: "Revoke this preview link? Anyone currently using it will immediately lose access." }, class: 'button_to state_changing' } %> + <%= button_to t(".revoke"), revoke_shared_preview_node_path(@node), method: :put, form: { data: { confirm: t(".confirm_revoke") }, class: 'button_to state_changing' } %>
    <% else %> - <%= button_to 'Create public preview link', generate_shared_preview_node_path(@node), method: :put, form: { class: 'button_to state_changing' } %> + <%= button_to t(".create_preview_link"), generate_shared_preview_node_path(@node), method: :put, form: { class: 'button_to state_changing' } %> <% end %>
    <% end %>
    -
    History
    +
    <%= t(".history") %>
    - <%= pluralize(@node.pages.count, 'published revision', 'published revisions') %> + <%= t(".published_revisions", :count => @node.pages.count) %>
      <% @node.pages.order(:revision).each do |page| %> @@ -236,11 +234,11 @@ <% end %>
    - +
    -
    Tags
    +
    <%= t(".tags") %>
    <% if @page.tag_list.any? %>
      @@ -249,11 +247,11 @@ <% end %>
    <% else %> - No tags. + <%= t(".no_tags") %> <% end %>
    -
    Attachments
    +
    <%= t(".attachments") %>
    <% if @page.assets.any? %> <% headline_asset_id = @page.headline_asset&.id %> @@ -262,7 +260,7 @@
  • "> <%= link_to image_tag(asset.upload.url(:thumb)), asset_path(asset) %> <% if asset.id == headline_asset_id %> - + <%= icon("star", library: "tabler", "aria-hidden": true) %> <% end %> @@ -272,38 +270,38 @@ <% end %>

    <%= link_to new_asset_path(:node_id => @node.id), class: 'action_button' do %> - <%= icon("paperclip", library: "tabler", "aria-hidden": true) %> Upload new attachment + <%= icon("paperclip", library: "tabler", "aria-hidden": true) %> <%= t(".upload_attachment") %> <% end %> - To add or remove existing attachments, edit this node instead. + <%= t(".attachments_hint") %>

  • -
    Events
    +
    <%= t(".events") %>
      <% @node.events.order(:start_time).each do |event| %>
    • <%= event_schedule_text(event) %> - [<%= link_to 'show', event_path(event, return_to: request.path) %> - | <%= link_to 'edit', edit_event_path(event, return_to: request.path) %>] + [<%= link_to t(".show_link"), event_path(event, return_to: request.path) %> + | <%= link_to t(".edit_link"), edit_event_path(event, return_to: request.path) %>]
    • <% end %>
    <% mapping = default_event_tag_mapping(@page) %>

    <%= link_to new_event_path(node_id: @node.id, tag_list: mapping&.last, auto_tag_source: mapping&.first, return_to: request.path), class: 'action_button' do %> - <%= icon("calendar-plus", library: "tabler", "aria-hidden": true) %> Add event + <%= icon("calendar-plus", library: "tabler", "aria-hidden": true) %> <%= t(".add_event") %> <% end %>

    <% matches = matching_node_kinds(@node) %> <% if @node.children.any? || matches.any? %> -
    Children
    +
    <%= t(".children") %>
    <% if @node.children.any? %>
    - <%= pluralize(@node.children.count, 'child', 'children') %> + <%= t(".children_count", :count => @node.children.count) %>
      <% @node.children.order(:slug).each do |child| %>
    • <%= link_to (child.head&.title || child.draft&.title || child.slug), node_path(child) %>
    • @@ -317,7 +315,7 @@ <% link_params = { kind: kind } %> <% link_params[:parent_id] = @node.id if kind == "generic" %> <%= link_to new_node_path(link_params), class: 'action_button' do %> - <%= icon("text-plus", library: "tabler", "aria-hidden": true) %> Add child type <%= resolve_kind_text(config[:label]) %> + <%= icon("text-plus", library: "tabler", "aria-hidden": true) %> <%= t(".add_child", :kind => resolve_kind_text(config[:label])) %> <% end %> <% end %>

      @@ -325,10 +323,10 @@
    <% end %> -
    Abstract (<%= I18n.default_locale.to_s.upcase %>)
    +
    <%= t(".abstract_locale", :lang => I18n.default_locale.to_s.upcase) %>
    <%= sanitize(@page.abstract) %>
    -
    Body (<%= I18n.default_locale.to_s.upcase %>)
    +
    <%= t(".body_locale", :lang => I18n.default_locale.to_s.upcase) %>
    <%= sanitize(@page.body) %>
    diff --git a/app/views/nodes/sitemap.html.erb b/app/views/nodes/sitemap.html.erb index a799c179..f749a803 100644 --- a/app/views/nodes/sitemap.html.erb +++ b/app/views/nodes/sitemap.html.erb @@ -1,4 +1,4 @@ -

    Sitemap

    +

    <%= t(".title") %>

    <% diff --git a/app/views/nodes/tags.html.erb b/app/views/nodes/tags.html.erb index 0ebc6ce6..7cf7386c 100644 --- a/app/views/nodes/tags.html.erb +++ b/app/views/nodes/tags.html.erb @@ -1,3 +1,3 @@ -

    Nodes tagged: <%= params[:tags] %>

    +

    <%= t(".title", :tag => params[:tags]) %>

    <%= render 'node_list' %> diff --git a/app/views/nodes/trashed.html.erb b/app/views/nodes/trashed.html.erb index 9a4808e6..a3c2cf41 100644 --- a/app/views/nodes/trashed.html.erb +++ b/app/views/nodes/trashed.html.erb @@ -1,42 +1,40 @@ -

    Trash

    +

    <%= t(".title") %>

    <% if @nodes.empty? %> -

    The Trash is empty.

    +

    <%= t(".empty") %>

    <% else %> <%= will_paginate @nodes %>
    IDTitleActionsLocked byRev.<%= t("admin.columns.id") %><%= t("admin.columns.title") %><%= t("admin.columns.actions") %><%= t("admin.columns.locked_by") %><%= t("admin.columns.rev") %>
    - - - - + + + + <% @nodes.each do |node| %> <% entry = node.last_trash_entry %> <% doomed_below = node.descendants.count %> <% end %>
    TitleWas atTrashedActions<%= t("admin.columns.title") %><%= t(".was_at") %><%= t(".trashed") %><%= t("admin.columns.actions") %>
    - <%= link_to (node.draft&.title || node.slug), node_path(node) %><%= " (+ #{doomed_below} beneath)" if doomed_below > 0 %> + <%= link_to (node.draft&.title || node.slug), node_path(node) %><%= " " + t(".beneath", :count => doomed_below) if doomed_below > 0 %> <%= entry&.metadata&.dig("path", "from") %> <% if entry %> - <%= entry.occurred_at.strftime("%Y-%m-%d %H:%M") %> by <%= entry.actor_name %> + <%= t(".trashed_by", :time => entry.occurred_at.strftime("%Y-%m-%d %H:%M"), :actor => entry.actor_name) %> <% end %> <%= button_to node_path(node), method: :delete, - form: { data: { confirm: doomed_below > 0 ? - "Delete \"#{node.draft&.title || node.slug}\" and the #{doomed_below} pages beneath it permanently? This cannot be undone." : - "Delete \"#{node.draft&.title || node.slug}\" permanently? This cannot be undone." }, + form: { data: { confirm: t(".confirm_delete", :title => (node.draft&.title || node.slug), :count => doomed_below) }, class: 'button_to destructive' } do %> <%= icon("trash-x", library: "tabler", "aria-hidden": true) %> - Delete permanently + <%= t(".delete_permanently") %> <% end %>
    <%= will_paginate @nodes %> -

    To restore a page, open it and pick a new parent in its Trash section.

    +

    <%= t(".restore_hint") %>

    <% end %> diff --git a/config/locales/de.yml b/config/locales/de.yml index 7d49a872..3be58e54 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -187,3 +187,174 @@ de: disable_second_factor: "Zweiten Faktor entfernen" reset_second_factor: "Zweiten Faktor zurücksetzen" reset_confirm: "Zweiten Faktor von %{login} zurücksetzen? Die Anmeldung ist danach nur mit Passwort möglich." + + admin: + common: + create: "Anlegen" + edit: "Bearbeiten" + destroy: "Löschen" + search: "Suchen" + reset: "Zurücksetzen" + confirm_irreversible: "Das kann nicht rückgängig gemacht werden. Fortfahren?" + toggle_preview: "Live-Vorschau umschalten" + force_render: "Vorschau neu rendern" + columns: + id: "ID" + title: "Titel" + actions: "Aktionen" + locked_by: "Gesperrt von" + rev: "Rev." + path: "Pfad" + nodes: + index: + title: "Nodes" + create_node: "Node anlegen" + create_hint: "Legt einen Node ohne Eltern-Node und Art an. Für eine konkrete Seite besser den Anlege-Dialog oder \"Kind hinzufügen\" an einem bestehenden Node verwenden." + new: + title: "Neuen Node anlegen" + type: "Art" + slug_hint: "Der URL-Slug wird daraus automatisch erzeugt. Er lässt sich danach im Abschnitt \"Metadata\" unter Slug prüfen und anpassen." + parent: "Eltern-Node" + resulting_path: "Kompletter Pfad" + copy: "kopieren" + path_hint: "Diese Vorschau aktualisiert sich beim Tippen. Der endgültige Pfad lässt sich später ändern: über den Titel (letztes Segment) oder durch Verschieben unter einen anderen Eltern-Nodes (alles davor)." + attachment: "Anhang" + attach_hint: "Dieses Asset wird der neuen Seite automatisch angehängt." + as_headline: "als Aufmacher der Seite" + edit: + save_draft: "Entwurf speichern" + save_unlock_exit: "Speichern + Entsperren + Verlassen" + preview: "Vorschau ↗" + abstract: "Abstract" + attachments: "Anhänge" + headline_hint: "Das markierte Bild oder PDF wird der Aufmacher dieser Seite auf der öffentlichen Website. Ist nichts markiert, bekommen Besucher stattdessen einen Link auf alle angehängten Bilder." + metadata_summary: "Metadaten (Slug, Eltern-Node, Tags, Template, Autor, Bilder)" + slug: "Slug" + parent: "Eltern-Node" + parent_hint: "Tippen, um einen neuen Eltern-Node für diesen Node zu finden." + tags: "Tags" + tags_hint: "Kommagetrennt, Kleinschreibung." + publish_at: "Veröffentlichen am" + template: "Template" + template_hint: "Wird beim Anlegen automatisch gesetzt — bei Bedarf ändern." + author: "Autor" + body: "Text" + live_preview: "Live-Vorschau" + force_refresh: "Neu laden" + unlock_back: "Entsperren + Zurück" + what_changed: "Was hat sich geändert?" + confirm_discard: "Das kann nicht rückgängig gemacht werden. Fortfahren?" + destroy_draft: "Entwurf löschen" + discard_autosave: "Autosave verwerfen" + attach_search_placeholder: "Bilder zum Anhängen suchen…" + set_headline_label: "Als Aufmacherbild setzen" + set_headline_title: "Dieses Foto als Aufmacherbild der Seite verwenden" + remove_image_label: "Bild entfernen" + show: + status: "Status" + head: "Head" + draft: "Entwurf" + autosave: "Autosave" + head_line: "%{title} (Rev %{rev}, %{time})" + draft_line: "%{title} (Rev %{rev}, gespeichert %{time})" + autosave_line: "%{title} (ungespeichert, %{time})" + never_published: "keine — nie veröffentlicht" + none: "keine" + continue_editing: "Weiter bearbeiten" + edit_draft: "Entwurf bearbeiten" + lock_edit: "Sperren + Bearbeiten" + fresh_draft_hint: "Nichts offen — hier beginnt ein neuer Entwurf." + diff_layers: "Diff %{from} vs. %{to}" + layer_head: "Head" + layer_draft: "Entwurf" + layer_autosave: "Autosave" + publish: "Veröffentlichen" + confirm_publish: "Diesen Entwurf veröffentlichen?" + destroy_draft: "Entwurf löschen" + discard_autosave: "Autosave verwerfen" + confirm_trash: "Diese Seite und alles darunter in den Papierkorb verschieben? Alle veröffentlichten Inhalte darin gehen offline." + move_to_trash: "In den Papierkorb" + locked_hint: "Gesperrt — zum Bearbeiten, Veröffentlichen oder Verwerfen erst unter Personen entsperren." + trash: "Papierkorb" + was_at: "War unter" + restore_to: "Wiederherstellen nach" + restore_placeholder: "Neuen Eltern-Node suchen…" + restore_here: "Hierhin wiederherstellen" + restore_hint: "Wiederhergestellte Seiten kommen unveröffentlicht zurück. Jede (Unter-)Seite muss einzeln neu veröffentlicht werden." + confirm_delete: + one: "Diese Seite endgültig löschen? Das kann nicht rückgängig gemacht werden." + other: "Diese Seite und die %{count} Seiten darunter endgültig löschen? Das kann nicht rückgängig gemacht werden." + delete_permanently: "Endgültig löschen" + including_beneath: + one: "(inkl. 1 darunter)" + other: "(inkl. %{count} darunter)" + translations: "Übersetzungen" + updated: "— aktualisiert %{time}" + may_be_outdated: "womöglich veraltet" + not_yet_translated: "Noch nicht übersetzt." + create_translation: "Übersetzung anlegen + Sperren" + destroy_translation: "Übersetzung löschen" + people: "Personen" + author: "Autor" + editor: "Editor" + dates: "Daten" + last_updated: "Zuletzt geändert" + published_at: "Veröffentlicht am" + will_publish_at: "Wird veröffentlicht am" + links: "Links" + public: "Öffentlich" + admin_preview: "Admin-Vorschau" + public_preview: "Öffentliche Vorschau" + revoke: "Zurückziehen" + confirm_revoke: "Diesen Vorschau-Link zurückziehen? Wer ihn gerade nutzt, verliert sofort den Zugriff." + create_preview_link: "Öffentlichen Vorschau-Link erzeugen" + history: "Verlauf" + published_revisions: + one: "1 veröffentlichte Revision" + other: "%{count} veröffentlichte Revisionen" + revision_history: "Revisionsübersicht (Diff / Wiederherstellen)" + tags: "Tags" + no_tags: "Keine Tags." + attachments: "Anhänge" + headline_title: "Aufmacherbild dieser Seite" + upload_attachment: "Neuen Anhang hochladen" + attachments_hint: "Zum Hinzufügen oder Entfernen bestehender Anhänge diesen Node bearbeiten." + events: "Termine" + show_link: "anzeigen" + edit_link: "bearbeiten" + add_event: "Termin hinzufügen" + children: "Kinder" + children_count: + one: "1 Kind" + other: "%{count} Kinder" + add_child: "Kind vom Typ %{kind} anlegen" + abstract_locale: "Abstract (%{lang})" + body_locale: "Text (%{lang})" + drafts: + title: "Nodes mit Entwürfen oder Autosaves" + mine: + title: "Meine Arbeit" + tags: + title: "Nodes mit Tag: %{tag}" + sitemap: + title: "Sitemap" + chapters: + title: "Chapters" + filter: "Filter" + node_list: + search_placeholder: "Titel, Abstract, Text durchsuchen…" + trashed: + title: "Papierkorb" + empty: "Der Papierkorb ist leer." + was_at: "War unter" + trashed: "Gelöscht" + beneath: + one: "(+ 1 darunter)" + other: "(+ %{count} darunter)" + trashed_by: "%{time} von %{actor}" + confirm_delete: + zero: "\"%{title}\" endgültig löschen? Das kann nicht rückgängig gemacht werden." + one: "\"%{title}\" und die 1 Seite darunter endgültig löschen? Das kann nicht rückgängig gemacht werden." + other: "\"%{title}\" und die %{count} Seiten darunter endgültig löschen? Das kann nicht rückgängig gemacht werden." + delete_permanently: "Endgültig löschen" + restore_hint: "Zum Wiederherstellen eine Seite öffnen und in ihrem Papierkorb-Abschnitt einen neuen Eltern-Node wählen." diff --git a/config/locales/en.yml b/config/locales/en.yml index ea94952a..a15c7dd9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -139,3 +139,174 @@ en: disable_second_factor: "Disable second factor" reset_second_factor: "Reset second factor" reset_confirm: "Reset %{login}'s second factor? They will log in with password only afterwards." + + admin: + common: + create: "Create" + edit: "Edit" + destroy: "Destroy" + search: "Search" + reset: "Reset" + confirm_irreversible: "This cannot be undone. Continue?" + toggle_preview: "Toggle live preview" + force_render: "Force preview render" + columns: + id: "ID" + title: "Title" + actions: "Actions" + locked_by: "Locked by" + rev: "Rev." + path: "Path" + nodes: + index: + title: "Nodes" + create_node: "Create node" + create_hint: "Creates a node with no parent or kind set — for a specific page, use the wizard's create flow or \"add child\" from an existing node instead." + new: + title: "Create new node" + type: "Type" + slug_hint: "A URL slug will be generated from this automatically. You can review or adjust it afterward under the \"metadata\" section's Slug field." + parent: "Parent" + resulting_path: "Resulting path" + copy: "copy" + path_hint: "This preview updates as you type. The final path can still be changed afterward by editing the title (for the last segment) or moving the node to a different parent (for everything before it)." + attachment: "attachment" + attach_hint: "This asset will automatically be attached to the new page." + as_headline: "as the page's headline" + edit: + save_draft: "Save Draft" + save_unlock_exit: "Save + Unlock + Exit" + preview: "Preview ↗" + abstract: "Abstract" + attachments: "Attachments" + headline_hint: "The starred image or PDF becomes this page's headline on the public site. If none is starred, visitors get a link to browse all attached images instead." + metadata_summary: "Metadata (slug, parent, tags, template, author, images)" + slug: "Slug" + parent: "parent" + parent_hint: "Start typing to find a new parent for this node." + tags: "Tags" + tags_hint: "Comma separated, lowercase." + publish_at: "Publish at" + template: "Template" + template_hint: "Set automatically based on how this node was created - change it if needed." + author: "Author" + body: "Body" + live_preview: "Live preview" + force_refresh: "Force refresh" + unlock_back: "Unlock + Back" + what_changed: "What changed?" + confirm_discard: "This cannot be undone. Continue?" + destroy_draft: "Destroy Draft" + discard_autosave: "Discard Autosave" + attach_search_placeholder: "Search images to attach…" + set_headline_label: "Set as headline image" + set_headline_title: "Use this photo as the page's headline image" + remove_image_label: "Remove image" + show: + status: "Status" + head: "Head" + draft: "Draft" + autosave: "Autosave" + head_line: "%{title} (rev %{rev}, %{time})" + draft_line: "%{title} (rev %{rev}, saved %{time})" + autosave_line: "%{title} (unsaved, %{time})" + never_published: "none — never published" + none: "none" + continue_editing: "Continue Editing" + edit_draft: "Edit Draft" + lock_edit: "Lock + Edit" + fresh_draft_hint: "Nothing pending — this will start a fresh draft." + diff_layers: "Diff %{from} vs. %{to}" + layer_head: "Head" + layer_draft: "Draft" + layer_autosave: "Autosave" + publish: "Publish" + confirm_publish: "Publish this draft?" + destroy_draft: "Destroy Draft" + discard_autosave: "Discard Autosave" + confirm_trash: "Move this page and everything beneath it to the Trash? All published content in it will go offline." + move_to_trash: "Move to Trash" + locked_hint: "Locked — see People below to unlock before editing, publishing, or discarding." + trash: "Trash" + was_at: "Was at" + restore_to: "Restore to" + restore_placeholder: "Search for a new parent…" + restore_here: "Restore here" + restore_hint: "Restored pages come back unpublished. Republish each page deliberately." + confirm_delete: + one: "Delete this page permanently? This cannot be undone." + other: "Delete this page and the %{count} pages beneath it permanently? This cannot be undone." + delete_permanently: "Delete permanently" + including_beneath: + one: "(including 1 beneath)" + other: "(including %{count} beneath)" + translations: "Translations" + updated: "— updated %{time}" + may_be_outdated: "may be outdated" + not_yet_translated: "Not yet translated." + create_translation: "Create translation + Lock" + destroy_translation: "Destroy Translation" + people: "People" + author: "Author" + editor: "Editor" + dates: "Dates" + last_updated: "Last updated" + published_at: "Published at" + will_publish_at: "Will publish at" + links: "Links" + public: "Public" + admin_preview: "Admin Preview" + public_preview: "Public Preview" + revoke: "Revoke" + confirm_revoke: "Revoke this preview link? Anyone currently using it will immediately lose access." + create_preview_link: "Create public preview link" + history: "History" + published_revisions: + one: "1 published revision" + other: "%{count} published revisions" + revision_history: "Revision history (diff / restore)" + tags: "Tags" + no_tags: "No tags." + attachments: "Attachments" + headline_title: "This page's headline image" + upload_attachment: "Upload new attachment" + attachments_hint: "To add or remove existing attachments, edit this node instead." + events: "Events" + show_link: "show" + edit_link: "edit" + add_event: "Add event" + children: "Children" + children_count: + one: "1 child" + other: "%{count} children" + add_child: "Add child type %{kind}" + abstract_locale: "Abstract (%{lang})" + body_locale: "Body (%{lang})" + drafts: + title: "Nodes with drafts or autosaves" + mine: + title: "My work" + tags: + title: "Nodes tagged: %{tag}" + sitemap: + title: "Sitemap" + chapters: + title: "Chapters" + filter: "Filter" + node_list: + search_placeholder: "Search title, abstract, body…" + trashed: + title: "Trash" + empty: "The Trash is empty." + was_at: "Was at" + trashed: "Trashed" + beneath: + one: "(+ 1 beneath)" + other: "(+ %{count} beneath)" + trashed_by: "%{time} by %{actor}" + confirm_delete: + zero: "Delete \"%{title}\" permanently? This cannot be undone." + one: "Delete \"%{title}\" and the 1 page beneath it permanently? This cannot be undone." + other: "Delete \"%{title}\" and the %{count} pages beneath it permanently? This cannot be undone." + delete_permanently: "Delete permanently" + restore_hint: "To restore a page, open it and pick a new parent in its Trash section." diff --git a/test/controllers/nodes_controller_test.rb b/test/controllers/nodes_controller_test.rb index de536278..8b59fc37 100644 --- a/test/controllers/nodes_controller_test.rb +++ b/test/controllers/nodes_controller_test.rb @@ -438,7 +438,8 @@ class NodesControllerTest < ActionController::TestCase get :show, params: { id: node.id } assert_response :success - assert_select "a", text: "Add event" + assert_select "a", text: I18n.t("nodes.show.add_event") + assert_select "a[href*='tag_list=open-day']" assert_select "a[href*='auto_tag_source=erfa-detail']" end @@ -449,7 +450,7 @@ class NodesControllerTest < ActionController::TestCase get :show, params: { id: node.id } assert_response :success - assert_select "a", text: "Add event" + assert_select "a", text: I18n.t("nodes.show.add_event") assert_select "a[href*='tag_list=']", count: 0 end @@ -600,7 +601,7 @@ class NodesControllerTest < ActionController::TestCase assert_includes assigns(:nodes), presse_node assert_not_includes assigns(:nodes), erfa_node - assert_select "h1", "Nodes tagged: pressemitteilung" + assert_select "h1", :text => I18n.t("nodes.tags.title", :tag => "pressemitteilung"), :count => 1 assert_select "h1", :text => "Chapters", :count => 0 end -- cgit v1.3