feat: complete change to gettext from homebrew i18n solution
This commit is contained in:
@@ -7,6 +7,7 @@ defmodule BDS.Desktop.MenuBar do
|
||||
alias BDS.UI.MenuBar, as: ShellMenuBar
|
||||
alias Desktop.OS
|
||||
alias Desktop.Window
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
def groups(opts \\ []) do
|
||||
opts
|
||||
@@ -140,60 +141,58 @@ defmodule BDS.Desktop.MenuBar do
|
||||
defp native_label(label, nil), do: label
|
||||
defp native_label(label, shortcut), do: label <> "\t" <> shortcut
|
||||
|
||||
defp group_label(:file), do: translate("menuBar.file")
|
||||
defp group_label(:edit), do: translate("menuBar.edit")
|
||||
defp group_label(:view), do: translate("menuBar.view")
|
||||
defp group_label(:blog), do: translate("menuBar.blog")
|
||||
defp group_label(:help), do: translate("menuBar.help")
|
||||
defp group_label(:file), do: dgettext("ui", "File")
|
||||
defp group_label(:edit), do: dgettext("ui", "Edit")
|
||||
defp group_label(:view), do: dgettext("ui", "View")
|
||||
defp group_label(:blog), do: dgettext("ui", "Blog")
|
||||
defp group_label(:help), do: dgettext("ui", "Help")
|
||||
|
||||
defp item_label(:new_post), do: translate("menuBar.newPost")
|
||||
defp item_label(:import_media), do: translate("menuBar.importMedia")
|
||||
defp item_label(:save), do: translate("menuBar.save")
|
||||
defp item_label(:open_in_browser), do: translate("menuBar.openInBrowser")
|
||||
defp item_label(:open_data_folder), do: translate("menuBar.openDataFolder")
|
||||
defp item_label(:close_tab), do: translate("menuBar.closeTab")
|
||||
defp item_label(:quit), do: translate("menuBar.quit")
|
||||
defp item_label(:undo), do: translate("menuBar.undo")
|
||||
defp item_label(:redo), do: translate("menuBar.redo")
|
||||
defp item_label(:cut), do: translate("menuBar.cut")
|
||||
defp item_label(:copy), do: translate("menuBar.copy")
|
||||
defp item_label(:paste), do: translate("menuBar.paste")
|
||||
defp item_label(:delete), do: translate("menuBar.delete")
|
||||
defp item_label(:select_all), do: translate("menuBar.selectAll")
|
||||
defp item_label(:find), do: translate("menuBar.find")
|
||||
defp item_label(:replace), do: translate("menuBar.replace")
|
||||
defp item_label(:edit_preferences), do: translate("menuBar.preferences")
|
||||
defp item_label(:view_posts), do: translate("menuBar.viewPosts")
|
||||
defp item_label(:view_media), do: translate("menuBar.viewMedia")
|
||||
defp item_label(:toggle_sidebar), do: translate("menuBar.toggleSidebar")
|
||||
defp item_label(:toggle_panel), do: translate("menuBar.togglePanel")
|
||||
defp item_label(:toggle_assistant_sidebar), do: translate("menuBar.toggleAssistantSidebar")
|
||||
defp item_label(:toggle_dev_tools), do: translate("menuBar.toggleDevTools")
|
||||
defp item_label(:reload), do: translate("menuBar.reload")
|
||||
defp item_label(:force_reload), do: translate("menuBar.forceReload")
|
||||
defp item_label(:reset_zoom), do: translate("menuBar.resetZoom")
|
||||
defp item_label(:zoom_in), do: translate("menuBar.zoomIn")
|
||||
defp item_label(:zoom_out), do: translate("menuBar.zoomOut")
|
||||
defp item_label(:toggle_full_screen), do: translate("menuBar.toggleFullScreen")
|
||||
defp item_label(:publish_selected), do: translate("menuBar.publishSelected")
|
||||
defp item_label(:preview_post), do: translate("menuBar.previewPost")
|
||||
defp item_label(:edit_menu), do: translate("menuBar.editMenu")
|
||||
defp item_label(:rebuild_database), do: translate("menuBar.rebuildDatabase")
|
||||
defp item_label(:reindex_text), do: translate("menuBar.reindexText")
|
||||
defp item_label(:rebuild_embedding_index), do: translate("menuBar.rebuildEmbeddingIndex")
|
||||
defp item_label(:metadata_diff), do: translate("menuBar.metadataDiff")
|
||||
defp item_label(:regenerate_calendar), do: translate("menuBar.regenerateCalendar")
|
||||
defp item_label(:validate_translations), do: translate("menuBar.validateTranslations")
|
||||
defp item_label(:fill_missing_translations), do: translate("menuBar.fillMissingTranslations")
|
||||
defp item_label(:find_duplicates), do: translate("menuBar.findDuplicates")
|
||||
defp item_label(:generate_sitemap), do: translate("menuBar.generateSite")
|
||||
defp item_label(:validate_site), do: translate("menuBar.validateSite")
|
||||
defp item_label(:upload_site), do: translate("menuBar.uploadSite")
|
||||
defp item_label(:about), do: translate("menuBar.about")
|
||||
defp item_label(:documentation), do: translate("menuBar.documentation")
|
||||
defp item_label(:api_documentation), do: translate("menuBar.apiDocumentation")
|
||||
defp item_label(:view_on_github), do: translate("menuBar.viewOnGithub")
|
||||
defp item_label(:report_issue), do: translate("menuBar.reportIssue")
|
||||
|
||||
defp translate(text), do: ShellData.translate(text, %{}, UILocale.current())
|
||||
defp item_label(:new_post), do: dgettext("ui", "New Post")
|
||||
defp item_label(:import_media), do: dgettext("ui", "Import Media")
|
||||
defp item_label(:save), do: dgettext("ui", "Save")
|
||||
defp item_label(:open_in_browser), do: dgettext("ui", "Open in Browser")
|
||||
defp item_label(:open_data_folder), do: dgettext("ui", "Open Data Folder")
|
||||
defp item_label(:close_tab), do: dgettext("ui", "Close Tab")
|
||||
defp item_label(:quit), do: dgettext("ui", "Quit")
|
||||
defp item_label(:undo), do: dgettext("ui", "Undo")
|
||||
defp item_label(:redo), do: dgettext("ui", "Redo")
|
||||
defp item_label(:cut), do: dgettext("ui", "Cut")
|
||||
defp item_label(:copy), do: dgettext("ui", "Copy")
|
||||
defp item_label(:paste), do: dgettext("ui", "Paste")
|
||||
defp item_label(:delete), do: dgettext("ui", "Delete")
|
||||
defp item_label(:select_all), do: dgettext("ui", "Select All")
|
||||
defp item_label(:find), do: dgettext("ui", "Find")
|
||||
defp item_label(:replace), do: dgettext("ui", "Replace")
|
||||
defp item_label(:edit_preferences), do: dgettext("ui", "Preferences")
|
||||
defp item_label(:view_posts), do: dgettext("ui", "Posts")
|
||||
defp item_label(:view_media), do: dgettext("ui", "Media")
|
||||
defp item_label(:toggle_sidebar), do: dgettext("ui", "Toggle Sidebar")
|
||||
defp item_label(:toggle_panel), do: dgettext("ui", "Toggle Panel")
|
||||
defp item_label(:toggle_assistant_sidebar), do: dgettext("ui", "Toggle Assistant Sidebar")
|
||||
defp item_label(:toggle_dev_tools), do: dgettext("ui", "Toggle Dev Tools")
|
||||
defp item_label(:reload), do: dgettext("ui", "Reload")
|
||||
defp item_label(:force_reload), do: dgettext("ui", "Force Reload")
|
||||
defp item_label(:reset_zoom), do: dgettext("ui", "Reset Zoom")
|
||||
defp item_label(:zoom_in), do: dgettext("ui", "Zoom In")
|
||||
defp item_label(:zoom_out), do: dgettext("ui", "Zoom Out")
|
||||
defp item_label(:toggle_full_screen), do: dgettext("ui", "Toggle Full Screen")
|
||||
defp item_label(:publish_selected), do: dgettext("ui", "Publish Selected")
|
||||
defp item_label(:preview_post), do: dgettext("ui", "Preview Post")
|
||||
defp item_label(:edit_menu), do: dgettext("ui", "Edit Menu")
|
||||
defp item_label(:rebuild_database), do: dgettext("ui", "Rebuild Database")
|
||||
defp item_label(:reindex_text), do: dgettext("ui", "Reindex Text")
|
||||
defp item_label(:rebuild_embedding_index), do: dgettext("ui", "Rebuild Embedding Index")
|
||||
defp item_label(:metadata_diff), do: dgettext("ui", "Metadata Diff")
|
||||
defp item_label(:regenerate_calendar), do: dgettext("ui", "Regenerate Calendar")
|
||||
defp item_label(:validate_translations), do: dgettext("ui", "Validate Translations")
|
||||
defp item_label(:fill_missing_translations), do: dgettext("ui", "Fill Missing Translations")
|
||||
defp item_label(:find_duplicates), do: dgettext("ui", "Find Duplicate Posts")
|
||||
defp item_label(:generate_sitemap), do: dgettext("ui", "Generate Site")
|
||||
defp item_label(:validate_site), do: dgettext("ui", "Validate Site")
|
||||
defp item_label(:upload_site), do: dgettext("ui", "Upload Site")
|
||||
defp item_label(:about), do: dgettext("ui", "About")
|
||||
defp item_label(:documentation), do: dgettext("ui", "Documentation")
|
||||
defp item_label(:api_documentation), do: dgettext("ui", "API Documentation")
|
||||
defp item_label(:view_on_github), do: dgettext("ui", "View on GitHub")
|
||||
defp item_label(:report_issue), do: dgettext("ui", "Report Issue")
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule BDS.Desktop.ShellData do
|
||||
@moduledoc false
|
||||
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
alias BDS.Git
|
||||
alias BDS.I18n
|
||||
alias BDS.Projects
|
||||
@@ -12,12 +14,24 @@ defmodule BDS.Desktop.ShellData do
|
||||
Application.get_env(:bds, :desktop)[:title] || "Blogging Desktop Server"
|
||||
end
|
||||
|
||||
def ui_language do
|
||||
I18n.current_ui_locale()
|
||||
def activity_icon(id) do
|
||||
case to_string(id) do
|
||||
"posts" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"></path><path d="M8 12h8v2H8zm0 4h8v2H8z"></path></svg>)
|
||||
"pages" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M4 4h10v4h6v12H4V4zm10 1.5V9h4.5L14 5.5zM7 12h10v1.5H7V12zm0 3h10v1.5H7V15z"></path></svg>)
|
||||
"media" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"></path></svg>)
|
||||
"scripts" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20 3H4a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h7v2H8v2h8v-2h-3v-2h7a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1zM5 14V5h14v9H5zm2-7.5L9.5 9 7 11.5l1.4 1.4L12.3 9 8.4 5.1 7 6.5zm6.5 5.5h4v-2h-4v2z"></path></svg>)
|
||||
"templates" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M4 4h7v7H4V4zm9 0h7v7h-7V4zM4 13h7v7H4v-7zm9 0h7v7h-7v-7zM5.5 5.5v4h4v-4h-4zm9 0v4h4v-4h-4zm-9 9v4h4v-4h-4zm9 0v4h4v-4h-4z"></path></svg>)
|
||||
"tags" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58s1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41s-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"></path></svg>)
|
||||
"chat" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"></path><circle cx="8" cy="10" r="1.5"></circle><circle cx="12" cy="10" r="1.5"></circle><circle cx="16" cy="10" r="1.5"></circle></svg>)
|
||||
"import" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path></svg>)
|
||||
"git" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M22 11.73L12.27 2a1 1 0 0 0-1.41 0L8.84 4.02l2.56 2.56a1.2 1.2 0 0 1 1.52 1.53l2.47 2.47a1.2 1.2 0 1 1-.72.67l-2.3-2.3v6.06a1.2 1.2 0 1 1-.85 0V8.9a1.2 1.2 0 0 1-.66-1.59L8.35 4.8 2 11.16a1 1 0 0 0 0 1.41L11.73 22a1 1 0 0 0 1.41 0L22 13.14a1 1 0 0 0 0-1.41z"></path></svg>)
|
||||
"settings" -> ~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"></path></svg>)
|
||||
_other -> activity_icon("posts")
|
||||
end
|
||||
end
|
||||
|
||||
def translations(locale \\ nil) do
|
||||
I18n.get_ui_translations(effective_ui_language(locale))
|
||||
def ui_language do
|
||||
I18n.current_ui_locale()
|
||||
end
|
||||
|
||||
def supported_ui_languages do
|
||||
@@ -26,14 +40,6 @@ defmodule BDS.Desktop.ShellData do
|
||||
end)
|
||||
end
|
||||
|
||||
def translate(key, bindings \\ %{}, locale \\ nil) do
|
||||
text = Map.get(translations(locale), to_string(key), to_string(key))
|
||||
|
||||
Enum.reduce(bindings, text, fn {binding, value}, acc ->
|
||||
String.replace(acc, "%{#{binding}}", to_string(value))
|
||||
end)
|
||||
end
|
||||
|
||||
def project_snapshot do
|
||||
Projects.shell_snapshot()
|
||||
rescue
|
||||
@@ -77,20 +83,20 @@ defmodule BDS.Desktop.ShellData do
|
||||
|
||||
def assistant_cards do
|
||||
[
|
||||
%{label: "Offline Gate", text: "Automatic AI actions stay gated by airplane mode."},
|
||||
%{label: dgettext("ui", "Offline Gate"), text: dgettext("ui", "Automatic AI actions stay gated by airplane mode.")},
|
||||
%{
|
||||
label: "Filesystem Sync",
|
||||
text: "Metadata flush, diffing, and rebuild hooks still need editor wiring."
|
||||
label: dgettext("ui", "Filesystem Sync"),
|
||||
text: dgettext("ui", "Metadata flush, diffing, and rebuild hooks still need editor wiring.")
|
||||
},
|
||||
%{label: "Desktop Runtime", text: "The app window is now served from LiveView state."}
|
||||
%{label: dgettext("ui", "Desktop Runtime"), text: dgettext("ui", "The app window is now served from LiveView state.")}
|
||||
]
|
||||
end
|
||||
|
||||
def editor_meta(task_status) do
|
||||
[
|
||||
%{label: "Status", value: task_status.running_task_message || "Idle"},
|
||||
%{label: "Mode", value: "Offline"},
|
||||
%{label: "Main Language", value: ui_language()}
|
||||
%{label: dgettext("ui", "Status"), value: task_status.running_task_message || dgettext("ui", "Idle")},
|
||||
%{label: dgettext("ui", "Mode"), value: dgettext("ui", "Offline")},
|
||||
%{label: dgettext("ui", "Main Language"), value: ui_language()}
|
||||
]
|
||||
end
|
||||
|
||||
@@ -140,70 +146,18 @@ defmodule BDS.Desktop.ShellData do
|
||||
|> Enum.uniq()
|
||||
end
|
||||
|
||||
defp git_remote_state_provider do
|
||||
Application.get_env(:bds, :git_remote_state_provider, &Git.remote_state/2)
|
||||
end
|
||||
|
||||
defp parse_positive_count(value) do
|
||||
case Integer.parse(value) do
|
||||
{count, _rest} when count > 0 -> count
|
||||
_other -> 0
|
||||
end
|
||||
end
|
||||
|
||||
def activity_icon(id) do
|
||||
case to_string(id) do
|
||||
"posts" ->
|
||||
~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"></path><path d="M8 12h8v2H8zm0 4h8v2H8z"></path></svg>)
|
||||
|
||||
"pages" ->
|
||||
~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M4 4h10v4h6v12H4V4zm10 1.5V9h4.5L14 5.5zM7 12h10v1.5H7V12zm0 3h10v1.5H7V15z"></path></svg>)
|
||||
|
||||
"media" ->
|
||||
~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"></path></svg>)
|
||||
|
||||
"scripts" ->
|
||||
~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20 3H4a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h7v2H8v2h8v-2h-3v-2h7a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1zM5 14V5h14v9H5zm2-7.5L9.5 9 7 11.5l1.4 1.4L12.3 9 8.4 5.1 7 6.5zm6.5 5.5h4v-2h-4v2z"></path></svg>)
|
||||
|
||||
"templates" ->
|
||||
~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M4 4h7v7H4V4zm9 0h7v7h-7V4zM4 13h7v7H4v-7zm9 0h7v7h-7v-7zM5.5 5.5v4h4v-4h-4zm9 0v4h4v-4h-4zm-9 9v4h4v-4h-4zm9 0v4h4v-4h-4z"></path></svg>)
|
||||
|
||||
"tags" ->
|
||||
~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58s1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41s-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"></path></svg>)
|
||||
|
||||
"chat" ->
|
||||
~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"></path><circle cx="8" cy="10" r="1.5"></circle><circle cx="12" cy="10" r="1.5"></circle><circle cx="16" cy="10" r="1.5"></circle></svg>)
|
||||
|
||||
"import" ->
|
||||
~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path></svg>)
|
||||
|
||||
"git" ->
|
||||
~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M22 11.73L12.27 2a1 1 0 0 0-1.41 0L8.84 4.02l2.56 2.56a1.2 1.2 0 0 1 1.52 1.53l2.47 2.47a1.2 1.2 0 1 1-.72.67l-2.3-2.3v6.06a1.2 1.2 0 1 1-.85 0V8.9a1.2 1.2 0 0 1-.66-1.59L8.35 4.8 2 11.16a1 1 0 0 0 0 1.41L11.73 22a1 1 0 0 0 1.41 0L22 13.14a1 1 0 0 0 0-1.41z"></path></svg>)
|
||||
|
||||
"settings" ->
|
||||
~s(<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"></path></svg>)
|
||||
|
||||
_other ->
|
||||
activity_icon("posts")
|
||||
end
|
||||
end
|
||||
|
||||
def dashboard_status_label(status) do
|
||||
case to_string(status) do
|
||||
"draft" -> translate("dashboard.status.draft")
|
||||
"published" -> translate("dashboard.status.published")
|
||||
"archived" -> translate("dashboard.status.archived")
|
||||
"draft" -> dgettext("ui", "Draft")
|
||||
"published" -> dgettext("ui", "Published")
|
||||
"archived" -> dgettext("ui", "Archived")
|
||||
other -> other |> String.replace("_", " ") |> String.capitalize()
|
||||
end
|
||||
end
|
||||
|
||||
def dashboard_post_count_label(count) do
|
||||
normalized_count = count || 0
|
||||
|
||||
key =
|
||||
if normalized_count == 1, do: "dashboard.postCount.one", else: "dashboard.postCount.other"
|
||||
|
||||
translate(key, %{count: normalized_count})
|
||||
dngettext("ui", "%{count} post", "%{count} posts", normalized_count, count: normalized_count)
|
||||
end
|
||||
|
||||
def dashboard_tag_cloud_items(items) when is_list(items) do
|
||||
@@ -258,10 +212,10 @@ defmodule BDS.Desktop.ShellData do
|
||||
def route_label(route) do
|
||||
case to_string(route) do
|
||||
"git_log" ->
|
||||
"Git Log"
|
||||
dgettext("ui", "Git Log")
|
||||
|
||||
"post_links" ->
|
||||
"Post Links"
|
||||
dgettext("ui", "Post Links")
|
||||
|
||||
other ->
|
||||
other
|
||||
@@ -288,11 +242,16 @@ defmodule BDS.Desktop.ShellData do
|
||||
end
|
||||
end
|
||||
|
||||
defp effective_ui_language(nil) do
|
||||
BDS.Desktop.UILocale.current() || ui_language()
|
||||
defp git_remote_state_provider do
|
||||
Application.get_env(:bds, :git_remote_state_provider, &Git.remote_state/2)
|
||||
end
|
||||
|
||||
defp effective_ui_language(locale), do: locale
|
||||
defp parse_positive_count(value) do
|
||||
case Integer.parse(value) do
|
||||
{count, _rest} when count > 0 -> count
|
||||
_other -> 0
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_add_panel_tab(tabs, :post, :post_links), do: tabs ++ [:post_links]
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ defmodule BDS.Desktop.ShellLive do
|
||||
alias BDS.UI.{Commands, MenuBar, Session, Workbench}
|
||||
alias Desktop.OS
|
||||
alias BDS.Desktop.Shutdown
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@refresh_interval 1_500
|
||||
@output_entry_limit 20
|
||||
@@ -586,29 +587,26 @@ defmodule BDS.Desktop.ShellLive do
|
||||
|> assign(:current_tab, current_tab(workbench))
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, UILocale.current())
|
||||
|
||||
defp encoded_shortcuts(shortcuts), do: Jason.encode!(shortcuts)
|
||||
|
||||
defp encoded_workbench_session(workbench), do: Jason.encode!(Session.serialize(workbench))
|
||||
|
||||
defp panel_tab_label(:tasks), do: translated("Tasks")
|
||||
defp panel_tab_label(:output), do: translated("Output")
|
||||
defp panel_tab_label(:git_log), do: translated("Git Log")
|
||||
defp panel_tab_label(:tasks), do: dgettext("ui", "Tasks")
|
||||
defp panel_tab_label(:output), do: dgettext("ui", "Output")
|
||||
defp panel_tab_label(:git_log), do: dgettext("ui", "Git Log")
|
||||
defp panel_tab_label(tab), do: ShellData.route_label(tab)
|
||||
|
||||
defp activity_label("AI Assistant"), do: "Chat"
|
||||
defp activity_label("Source Control"), do: "Git"
|
||||
defp activity_label(label), do: translated(label)
|
||||
defp activity_label("AI Assistant"), do: dgettext("ui", "Chat")
|
||||
defp activity_label("Source Control"), do: dgettext("ui", "Git")
|
||||
defp activity_label(label), do: label
|
||||
|
||||
defp active_sidebar_label(activity_buttons, active_view, sidebar_data) do
|
||||
Enum.find_value(activity_buttons, translated(Map.get(sidebar_data, :title, "")), fn button ->
|
||||
Enum.find_value(activity_buttons, Map.get(sidebar_data, :title, ""), fn button ->
|
||||
if button.id == active_view, do: activity_label(button.label), else: nil
|
||||
end)
|
||||
end
|
||||
|
||||
defp sidebar_header_label(label), do: translated(label)
|
||||
defp sidebar_header_label(label), do: label
|
||||
|
||||
defp timeline_height(entry, entries) do
|
||||
max_count =
|
||||
@@ -680,6 +678,8 @@ defmodule BDS.Desktop.ShellLive do
|
||||
if normalized == socket.assigns.page_language do
|
||||
socket
|
||||
else
|
||||
UILocale.put(normalized)
|
||||
|
||||
socket
|
||||
|> assign(:page_language, normalized)
|
||||
|> reload_shell(socket.assigns.workbench)
|
||||
|
||||
@@ -6,9 +6,9 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
import Phoenix.HTML, only: [raw: 1]
|
||||
|
||||
alias BDS.{AI, BoundedAtoms, MapUtils, Persistence}
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Desktop.ShellLive.ChatEditor.{MessageBuild, ModelSelection, ToolTracking}
|
||||
alias BDS.Desktop.ShellLive.TabHelpers
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
embed_templates("chat_editor_html/*")
|
||||
|
||||
@@ -72,7 +72,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
{:noreply, assign(socket, :model_selector_open?, false) |> build_data()}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_parent({:chat_editor_output, translated("Chat"), inspect(reason), "error"})
|
||||
notify_parent({:chat_editor_output, dgettext("ui", "Chat"), inspect(reason), "error"})
|
||||
{:noreply, assign(socket, :model_selector_open?, false) |> build_data()}
|
||||
end
|
||||
end
|
||||
@@ -196,8 +196,8 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
|
||||
socket.assigns.offline_mode ->
|
||||
notify_parent(
|
||||
{:chat_editor_output, translated("Chat"),
|
||||
translated("Automatic AI actions stay gated by airplane mode."), "info"}
|
||||
{:chat_editor_output, dgettext("ui", "Chat"),
|
||||
dgettext("ui", "Automatic AI actions stay gated by airplane mode."), "info"}
|
||||
)
|
||||
|
||||
build_data(socket)
|
||||
@@ -272,7 +272,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
assign(socket, :request, nil) |> build_data()
|
||||
|
||||
{:error, reason} ->
|
||||
notify_parent({:chat_editor_output, translated("Chat"), format_error(reason), "error"})
|
||||
notify_parent({:chat_editor_output, dgettext("ui", "Chat"), format_error(reason), "error"})
|
||||
assign(socket, :request, nil) |> build_data()
|
||||
end
|
||||
end
|
||||
@@ -483,8 +483,8 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
# ── HEEx-callable helpers ─────────────────────────────────────────────────
|
||||
|
||||
@spec message_role_label(atom()) :: String.t()
|
||||
def message_role_label(:user), do: translated("chat.role.you")
|
||||
def message_role_label(_role), do: translated("chat.role.assistant")
|
||||
def message_role_label(:user), do: dgettext("ui", "You")
|
||||
def message_role_label(_role), do: dgettext("ui", "Assistant")
|
||||
|
||||
defdelegate tool_call_name(tool_call), to: ToolTracking
|
||||
defdelegate tool_call_arguments(tool_call), to: ToolTracking
|
||||
@@ -547,10 +547,10 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
<% end %>
|
||||
</summary>
|
||||
<div class="chat-tool-marker-details" data-testid="chat-tool-marker-details">
|
||||
<div class="chat-tool-marker-detail-label"><%= translated("chat.toolArguments") %></div>
|
||||
<div class="chat-tool-marker-detail-label"><%= dgettext("ui", "Arguments") %></div>
|
||||
<pre><%= Jason.encode!(marker.arguments || %{}, pretty: true) %></pre>
|
||||
<%= if marker.result not in [nil, ""] do %>
|
||||
<div class="chat-tool-marker-detail-label"><%= translated("chat.toolResult") %></div>
|
||||
<div class="chat-tool-marker-detail-label"><%= dgettext("ui", "Result") %></div>
|
||||
<pre><%= marker.result %></pre>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -571,7 +571,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
<summary class="chat-inline-surface-header">
|
||||
<span class="chat-inline-surface-icon"><%= surface_icon(@surface.type) %></span>
|
||||
<span class="chat-inline-surface-title"><%= surface_title(@surface) %></span>
|
||||
<button class="chat-inline-surface-dismiss" type="button" phx-click="dismiss_chat_surface" phx-target={@myself} phx-value-surface-id={@surface.id} aria-label={translated("chat.dismissSurface")} data-testid="chat-inline-surface-dismiss">×</button>
|
||||
<button class="chat-inline-surface-dismiss" type="button" phx-click="dismiss_chat_surface" phx-target={@myself} phx-value-surface-id={@surface.id} aria-label={dgettext("ui", "Dismiss surface")} data-testid="chat-inline-surface-dismiss">×</button>
|
||||
</summary>
|
||||
<div class="chat-inline-surface-body">
|
||||
<%= case @surface.type do %>
|
||||
@@ -848,7 +848,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
defp present?(value), do: not is_nil(value)
|
||||
|
||||
defp format_error(%{kind: :endpoint_not_configured}),
|
||||
do: translated("chat.apiKeyRequiredDescription")
|
||||
do: dgettext("ui", "Configure an API key in Settings to enable AI chat.")
|
||||
|
||||
defp format_error(reason), do: inspect(reason)
|
||||
|
||||
@@ -860,7 +860,4 @@ defmodule BDS.Desktop.ShellLive.ChatEditor do
|
||||
:error -> 0
|
||||
end
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -3,8 +3,8 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.MessageBuild do
|
||||
|
||||
alias BDS.AI
|
||||
alias BDS.AI.ChatConversation
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Desktop.ShellLive.ChatEditor.{ModelSelection, ToolSurfaces, ToolTracking}
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec build(term()) :: term()
|
||||
def build(%{current_tab: %{type: :chat, id: conversation_id}} = assigns) do
|
||||
@@ -22,7 +22,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.MessageBuild do
|
||||
|
||||
%{
|
||||
id: conversation.id,
|
||||
title: conversation.title || translated("chat.newChat"),
|
||||
title: conversation.title || dgettext("ui", "New Chat"),
|
||||
model: conversation.model,
|
||||
effective_model: effective_model,
|
||||
available_models: available_models,
|
||||
@@ -268,7 +268,4 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.MessageBuild do
|
||||
|
||||
defp request_started_at(%{started_at: started_at}) when is_integer(started_at), do: started_at
|
||||
defp request_started_at(_request), do: nil
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -2,9 +2,9 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.ModelSelection do
|
||||
@moduledoc false
|
||||
|
||||
alias BDS.AI
|
||||
alias BDS.Desktop.ShellData
|
||||
|
||||
import Phoenix.Component, only: [assign: 3]
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec toggle_model_selector(term(), term()) :: term()
|
||||
def toggle_model_selector(socket, reload) do
|
||||
@@ -34,7 +34,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.ModelSelection do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Chat"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Chat"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -78,7 +78,4 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.ModelSelection do
|
||||
|
||||
defp blank?(value) when is_binary(value), do: String.trim(value) == ""
|
||||
defp blank?(nil), do: true
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule BDS.Desktop.ShellLive.ChatEditor.ToolSurfaces do
|
||||
@moduledoc false
|
||||
|
||||
alias BDS.Desktop.ShellData
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@render_tool_names MapSet.new([
|
||||
"render_card",
|
||||
@@ -85,7 +85,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.ToolSurfaces do
|
||||
|> List.wrap()
|
||||
|> Enum.map(fn entry ->
|
||||
%{
|
||||
label: map_value(entry, "label", translated("chat.role.assistant")),
|
||||
label: map_value(entry, "label", dgettext("ui", "Assistant")),
|
||||
value: numeric_value(map_value(entry, "value", 0)),
|
||||
segments: List.wrap(map_value(entry, "segments", []))
|
||||
}
|
||||
@@ -173,7 +173,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.ToolSurfaces do
|
||||
fields: fields,
|
||||
submit_label:
|
||||
map_value(arguments, "submitLabel") ||
|
||||
map_value(arguments, "submit_label", translated("chat.stop")),
|
||||
map_value(arguments, "submit_label", dgettext("ui", "Stop")),
|
||||
submit_action:
|
||||
map_value(arguments, "submitAction") ||
|
||||
map_value(arguments, "submit_action", "submitForm")
|
||||
@@ -249,7 +249,7 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.ToolSurfaces do
|
||||
defp decode_surface_actions(actions) when is_list(actions) do
|
||||
Enum.map(actions, fn action ->
|
||||
%{
|
||||
label: map_value(action, "label", translated("chat.openSettings")),
|
||||
label: map_value(action, "label", dgettext("ui", "Open Settings")),
|
||||
action: map_value(action, "action", "openSettings"),
|
||||
payload: map_value(action, "payload", %{})
|
||||
}
|
||||
@@ -296,7 +296,4 @@ defmodule BDS.Desktop.ShellLive.ChatEditor.ToolSurfaces do
|
||||
|
||||
defp truthy?(value) when value in [true, "true", 1, "1", "on"], do: true
|
||||
defp truthy?(_value), do: false
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="chat-panel-title">
|
||||
<span class="chat-panel-title-main">
|
||||
<%= if @chat_editor.needs_api_key? do %>
|
||||
<%= translated("chat.setupTitle") %>
|
||||
<%= dgettext("ui", "AI Chat Setup") %>
|
||||
<% else %>
|
||||
<%= @chat_editor.title %>
|
||||
<% end %>
|
||||
@@ -18,7 +18,7 @@
|
||||
phx-target={@myself}
|
||||
data-testid="chat-model-selector-button"
|
||||
>
|
||||
<span><%= @chat_editor.effective_model || translated("chat.modelUnavailable") %></span>
|
||||
<span><%= @chat_editor.effective_model || dgettext("ui", "No model") %></span>
|
||||
<span class="chat-model-selector-caret">▾</span>
|
||||
</button>
|
||||
|
||||
@@ -59,24 +59,24 @@
|
||||
<%= if @chat_editor.needs_api_key? do %>
|
||||
<div class="chat-welcome chat-api-key-state" data-testid="chat-api-key-required">
|
||||
<div class="chat-welcome-icon">🔑</div>
|
||||
<h2><%= translated("chat.apiKeyRequiredTitle") %></h2>
|
||||
<p><%= translated("chat.apiKeyRequiredDescription") %></p>
|
||||
<h2><%= dgettext("ui", "API Key Required") %></h2>
|
||||
<p><%= dgettext("ui", "Configure an API key in Settings to enable AI chat.") %></p>
|
||||
<div class="api-key-form">
|
||||
<button class="api-key-submit" type="button" phx-click="open_chat_settings" phx-target={@myself}><%= translated("chat.openSettings") %></button>
|
||||
<button class="api-key-submit" type="button" phx-click="open_chat_settings" phx-target={@myself}><%= dgettext("ui", "Open Settings") %></button>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= if Enum.empty?(@chat_editor.messages) and not @chat_editor.is_streaming do %>
|
||||
<div class="chat-welcome">
|
||||
<div class="chat-welcome-icon">🤖</div>
|
||||
<h2><%= translated("chat.welcomeTitle") %></h2>
|
||||
<p><%= translated("chat.welcomeDescription") %></p>
|
||||
<h2><%= dgettext("ui", "Welcome to the AI Assistant") %></h2>
|
||||
<p><%= dgettext("ui", "I can help you manage your blog with rich visualizations. Try asking me to:") %></p>
|
||||
<ul>
|
||||
<li><%= translated("chat.welcomeTipSearch") %></li>
|
||||
<li><%= translated("chat.welcomeTipChart") %></li>
|
||||
<li><%= translated("chat.welcomeTipTable") %></li>
|
||||
<li><%= translated("chat.welcomeTipMetadata") %></li>
|
||||
<li><%= translated("chat.welcomeTipTabs") %></li>
|
||||
<li><%= dgettext("ui", "Search for posts about a specific topic") %></li>
|
||||
<li><%= dgettext("ui", "Show a chart of posts published per month") %></li>
|
||||
<li><%= dgettext("ui", "Compare my recent posts in a table") %></li>
|
||||
<li><%= dgettext("ui", "Update metadata for posts or media") %></li>
|
||||
<li><%= dgettext("ui", "Show post statistics by year in tabs with charts") %></li>
|
||||
</ul>
|
||||
</div>
|
||||
<% else %>
|
||||
@@ -149,11 +149,11 @@
|
||||
<%= unless @chat_editor.needs_api_key? do %>
|
||||
<div class="chat-input-container" data-testid="chat-input-container">
|
||||
<%= if @chat_editor.is_streaming do %>
|
||||
<button class="chat-abort-button" data-testid="chat-abort-button" type="button" phx-click="abort_chat_editor_message" phx-target={@myself}>◼ <%= translated("chat.stop") %></button>
|
||||
<button class="chat-abort-button" data-testid="chat-abort-button" type="button" phx-click="abort_chat_editor_message" phx-target={@myself}>◼ <%= dgettext("ui", "Stop") %></button>
|
||||
<% end %>
|
||||
|
||||
<form class="chat-input-wrapper" phx-change="change_chat_editor_input" phx-submit="send_chat_editor_message" phx-target={@myself}>
|
||||
<textarea class="chat-input chat-surface-input" name="message" rows="1" placeholder={translated("chat.inputPlaceholder")} disabled={@chat_editor.is_streaming}><%= @chat_editor.input %></textarea>
|
||||
<textarea class="chat-input chat-surface-input" name="message" rows="1" placeholder={dgettext("ui", "Type a message...")} disabled={@chat_editor.is_streaming}><%= @chat_editor.input %></textarea>
|
||||
<button class="chat-send-button" data-testid="chat-send-button" type="button" phx-click="send_chat_editor_message" phx-target={@myself} disabled={@chat_editor.send_disabled?}>↑</button>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ defmodule BDS.Desktop.ShellLive.ChatSurface do
|
||||
|
||||
import Phoenix.Component, only: [assign: 3]
|
||||
|
||||
alias BDS.Desktop.ShellData
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
def assistant_turn(prompt, socket) do
|
||||
[
|
||||
@@ -12,12 +12,12 @@ defmodule BDS.Desktop.ShellLive.ChatSurface do
|
||||
]
|
||||
end
|
||||
|
||||
def assistant_project_name(nil), do: translated("Projects")
|
||||
def assistant_project_name(nil), do: dgettext("ui", "Projects")
|
||||
def assistant_project_name(project), do: project.name
|
||||
|
||||
def assistant_message_label("assistant"), do: translated("Assistant")
|
||||
def assistant_message_label("user"), do: translated("You")
|
||||
def assistant_message_label(_role), do: translated("Assistant")
|
||||
def assistant_message_label("assistant"), do: dgettext("ui", "Assistant")
|
||||
def assistant_message_label("user"), do: dgettext("ui", "You")
|
||||
def assistant_message_label(_role), do: dgettext("ui", "Assistant")
|
||||
|
||||
def assistant_message_testid(role), do: "assistant-message-#{role}"
|
||||
|
||||
@@ -30,19 +30,9 @@ defmodule BDS.Desktop.ShellLive.ChatSurface do
|
||||
|
||||
defp assistant_reply(socket) do
|
||||
if socket.assigns.offline_mode do
|
||||
ShellData.translate(
|
||||
"Automatic AI actions stay gated by airplane mode.",
|
||||
%{},
|
||||
socket.assigns.page_language
|
||||
)
|
||||
BDS.Gettext.lgettext(socket.assigns.page_language, "ui", "Automatic AI actions stay gated by airplane mode.")
|
||||
else
|
||||
ShellData.translate(
|
||||
"The assistant sidebar chat surface is ready, but model execution is not connected yet.",
|
||||
%{},
|
||||
socket.assigns.page_language
|
||||
)
|
||||
BDS.Gettext.lgettext(socket.assigns.page_language, "ui", "The assistant sidebar chat surface is ready, but model execution is not connected yet.")
|
||||
end
|
||||
end
|
||||
|
||||
defp translated(text), do: ShellData.translate(text, %{}, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -30,6 +30,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
translate_execution_phase: 1
|
||||
]
|
||||
|
||||
use Gettext, backend: BDS.Gettext
|
||||
import TaxonomyEditing,
|
||||
only: [
|
||||
existing_taxonomy_terms: 1,
|
||||
@@ -162,7 +163,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
definition_id = socket.assigns.definition_id
|
||||
|
||||
socket =
|
||||
case FolderPicker.choose_directory(translated("importAnalysis.uploadsFolder")) do
|
||||
case FolderPicker.choose_directory(dgettext("ui", "Uploads Folder")) do
|
||||
{:ok, uploads_folder_path} ->
|
||||
{:ok, _definition} =
|
||||
ImportDefinitions.update_definition(definition_id, %{
|
||||
@@ -175,7 +176,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
build_data(socket)
|
||||
|
||||
{:error, %{message: message}} ->
|
||||
notify_output(translated("activity.import"), message, "error")
|
||||
notify_output(dgettext("ui", "Import"), message, "error")
|
||||
build_data(socket)
|
||||
end
|
||||
|
||||
@@ -187,7 +188,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
project_id = socket.assigns.project_id
|
||||
|
||||
socket =
|
||||
case FilePicker.choose_file(translated("importAnalysis.wxrFile")) do
|
||||
case FilePicker.choose_file(dgettext("ui", "WXR File")) do
|
||||
{:ok, wxr_file_path} ->
|
||||
{:ok, definition} =
|
||||
ImportDefinitions.update_definition(definition_id, %{
|
||||
@@ -214,7 +215,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
socket
|
||||
|> assign(:analysis_state, %{
|
||||
loading: true,
|
||||
step: translated("importAnalysis.analyzingWxr"),
|
||||
step: dgettext("ui", "Analyzing WXR file..."),
|
||||
detail: Path.basename(wxr_file_path),
|
||||
file_path: wxr_file_path,
|
||||
ref: task.ref
|
||||
@@ -226,7 +227,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
build_data(socket)
|
||||
|
||||
{:error, %{message: message}} ->
|
||||
notify_output(translated("activity.import"), message, "error")
|
||||
notify_output(dgettext("ui", "Import"), message, "error")
|
||||
build_data(socket)
|
||||
end
|
||||
|
||||
@@ -430,12 +431,8 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
%{} = report <- ImportDefinitions.decode_analysis_result(definition) do
|
||||
if socket.assigns.offline_mode? do
|
||||
notify_output(
|
||||
translated("activity.import"),
|
||||
ShellData.translate(
|
||||
"Automatic AI actions stay gated by airplane mode.",
|
||||
%{},
|
||||
socket.assigns[:page_language] || ShellData.ui_language()
|
||||
),
|
||||
dgettext("ui", "Import"),
|
||||
BDS.Gettext.lgettext(socket.assigns[:page_language] || ShellData.ui_language(), "ui", "Automatic AI actions stay gated by airplane mode."),
|
||||
"info"
|
||||
)
|
||||
|
||||
@@ -467,15 +464,15 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
mapped_count = TaxonomyEditing.auto_mapped_count(report, updated_report)
|
||||
|
||||
notify_output(
|
||||
translated("activity.import"),
|
||||
translated("importAnalysis.mappedCount", %{count: mapped_count}),
|
||||
dgettext("ui", "Import"),
|
||||
dgettext("ui", "%{count} mapped", count: mapped_count),
|
||||
"info"
|
||||
)
|
||||
|
||||
build_data(socket)
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(translated("activity.import"), inspect(reason), "error")
|
||||
notify_output(dgettext("ui", "Import"), inspect(reason), "error")
|
||||
build_data(socket)
|
||||
end
|
||||
end
|
||||
@@ -552,7 +549,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
|
||||
socket
|
||||
|> assign(:analysis_state, default_analysis_state())
|
||||
|> notify_output(translated("activity.import"), message, "error")
|
||||
|> notify_output(dgettext("ui", "Import"), message, "error")
|
||||
|
||||
match?(%{ref: ^ref}, socket.assigns.execution_state) and reason not in [:normal, :shutdown] ->
|
||||
message = if is_binary(reason), do: reason, else: inspect(reason)
|
||||
@@ -567,7 +564,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
ref: nil
|
||||
})
|
||||
)
|
||||
|> notify_output(translated("activity.import"), message, "error")
|
||||
|> notify_output(dgettext("ui", "Import"), message, "error")
|
||||
|
||||
true ->
|
||||
socket
|
||||
@@ -630,11 +627,11 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
end
|
||||
|
||||
defp maybe_update_tab_meta(socket, name) do
|
||||
title = name || translated("importAnalysis.untitledImport")
|
||||
title = name || dgettext("ui", "Untitled Import")
|
||||
|
||||
notify_parent(
|
||||
{:import_editor_tab_meta, socket.assigns.definition_id, title,
|
||||
translated("importAnalysis.headerDescription")}
|
||||
dgettext("ui", "Select a WordPress export file (WXR) and an uploads folder to analyze what would be imported.")}
|
||||
)
|
||||
|
||||
socket
|
||||
@@ -653,7 +650,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
end
|
||||
end
|
||||
|
||||
defp selected_model_label(nil, []), do: translated("importAnalysis.analyzeWith")
|
||||
defp selected_model_label(nil, []), do: dgettext("ui", "Analyze with...")
|
||||
defp selected_model_label(nil, [model | _rest]), do: model.name || model.id
|
||||
|
||||
defp selected_model_label(model_id, available_models) do
|
||||
@@ -685,14 +682,14 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
socket
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("activity.import"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Import"), inspect(reason), "error")
|
||||
end
|
||||
|
||||
{:error, %{message: message}} ->
|
||||
notify_output(socket, translated("activity.import"), message, "error")
|
||||
notify_output(socket, dgettext("ui", "Import"), message, "error")
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("activity.import"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Import"), inspect(reason), "error")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -713,8 +710,8 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
ref: nil
|
||||
})
|
||||
|> notify_output(
|
||||
translated("activity.import"),
|
||||
translated("importAnalysis.importComplete", %{count: previous_state.count}),
|
||||
dgettext("ui", "Import"),
|
||||
dgettext("ui", "Import completed successfully!", count: previous_state.count),
|
||||
"info"
|
||||
)
|
||||
|
||||
@@ -727,7 +724,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
error: message,
|
||||
ref: nil
|
||||
})
|
||||
|> notify_output(translated("activity.import"), message, "error")
|
||||
|> notify_output(dgettext("ui", "Import"), message, "error")
|
||||
|
||||
{:error, reason} ->
|
||||
message = inspect(reason)
|
||||
@@ -740,7 +737,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
error: message,
|
||||
ref: nil
|
||||
})
|
||||
|> notify_output(translated("activity.import"), message, "error")
|
||||
|> notify_output(dgettext("ui", "Import"), message, "error")
|
||||
end
|
||||
|
||||
# Allow DB connections to settle before rebuilding
|
||||
@@ -812,27 +809,27 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
class="import-definition-name"
|
||||
type="text"
|
||||
name="import_definition[name]"
|
||||
value={@import_editor.definition_name || translated("importAnalysis.untitledImport")}
|
||||
placeholder={translated("importAnalysis.namePlaceholder")}
|
||||
value={@import_editor.definition_name || dgettext("ui", "Untitled Import")}
|
||||
placeholder={dgettext("ui", "Import name...")}
|
||||
/>
|
||||
<p><%= translated("importAnalysis.headerDescription") %></p>
|
||||
<p><%= dgettext("ui", "Select a WordPress export file (WXR) and an uploads folder to analyze what would be imported.") %></p>
|
||||
</form>
|
||||
|
||||
<div class="import-file-selectors">
|
||||
<div class="import-file-row">
|
||||
<label><%= translated("importAnalysis.uploadsFolder") %></label>
|
||||
<label><%= dgettext("ui", "Uploads Folder") %></label>
|
||||
<div class={["import-file-path", if(blank?(@import_editor.uploads_folder_path), do: "placeholder")]}>
|
||||
<%= @import_editor.uploads_folder_path || translated("importAnalysis.noFolderSelected") %>
|
||||
<%= @import_editor.uploads_folder_path || dgettext("ui", "No folder selected") %>
|
||||
</div>
|
||||
<button type="button" phx-click="select_import_uploads_folder" phx-target={@myself}><%= translated("Open") %></button>
|
||||
<button type="button" phx-click="select_import_uploads_folder" phx-target={@myself}><%= dgettext("ui", "Open") %></button>
|
||||
</div>
|
||||
|
||||
<div class="import-file-row">
|
||||
<label><%= translated("importAnalysis.wxrFile") %></label>
|
||||
<label><%= dgettext("ui", "WXR File") %></label>
|
||||
<div class={["import-file-path", if(blank?(@import_editor.wxr_file_path), do: "placeholder")]}>
|
||||
<%= @import_editor.wxr_file_path || translated("importAnalysis.selectFileToAnalyze") %>
|
||||
<%= @import_editor.wxr_file_path || dgettext("ui", "Select a file to analyze") %>
|
||||
</div>
|
||||
<button class="import-analyze-btn" type="button" phx-click="select_import_wxr_file" phx-target={@myself}><%= translated("importAnalysis.selectAndAnalyze") %></button>
|
||||
<button class="import-analyze-btn" type="button" phx-click="select_import_wxr_file" phx-target={@myself}><%= dgettext("ui", "Select & Analyze") %></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -840,7 +837,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<div class="import-loading">
|
||||
<div class="import-spinner"></div>
|
||||
<div class="import-progress">
|
||||
<div class="import-progress-step"><%= @analysis_state.step || translated("importAnalysis.analyzingWxr") %></div>
|
||||
<div class="import-progress-step"><%= @analysis_state.step || dgettext("ui", "Analyzing WXR file...") %></div>
|
||||
<%= if present?(@analysis_state.detail) do %>
|
||||
<div class="import-progress-detail"><%= @analysis_state.detail %></div>
|
||||
<% end %>
|
||||
@@ -851,37 +848,37 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<%= if not is_nil(@report) and not @import_editor.is_loading do %>
|
||||
<div class="import-site-info">
|
||||
<div class="import-site-info-item">
|
||||
<span class="info-label"><%= translated("importAnalysis.site") %></span>
|
||||
<span class="info-value"><%= get_in(@report, [:site_info, :title]) || translated("importAnalysis.untitled") %></span>
|
||||
<span class="info-label"><%= dgettext("ui", "Site") %></span>
|
||||
<span class="info-value"><%= get_in(@report, [:site_info, :title]) || dgettext("ui", "Untitled") %></span>
|
||||
</div>
|
||||
<div class="import-site-info-item">
|
||||
<span class="info-label"><%= translated("importAnalysis.url") %></span>
|
||||
<span class="info-value"><%= get_in(@report, [:site_info, :url]) || translated("importAnalysis.notAvailable") %></span>
|
||||
<span class="info-label"><%= dgettext("ui", "URL") %></span>
|
||||
<span class="info-value"><%= get_in(@report, [:site_info, :url]) || dgettext("ui", "N/A") %></span>
|
||||
</div>
|
||||
<div class="import-site-info-item">
|
||||
<span class="info-label"><%= translated("importAnalysis.language") %></span>
|
||||
<span class="info-value"><%= get_in(@report, [:site_info, :language]) || translated("importAnalysis.notAvailable") %></span>
|
||||
<span class="info-label"><%= dgettext("ui", "Language") %></span>
|
||||
<span class="info-value"><%= get_in(@report, [:site_info, :language]) || dgettext("ui", "N/A") %></span>
|
||||
</div>
|
||||
<div class="import-site-info-item">
|
||||
<span class="info-label"><%= translated("importAnalysis.file") %></span>
|
||||
<span class="info-label"><%= dgettext("ui", "File") %></span>
|
||||
<span class="info-value"><%= @import_editor.wxr_file_path |> to_string() |> Path.basename() %></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="import-stat-cards">
|
||||
<.stat_card label={translated("importAnalysis.posts")} stats={@report.post_stats} />
|
||||
<.stat_card label={dgettext("ui", "posts")} stats={@report.post_stats} />
|
||||
<%= if Map.get(@report, :other_stats) && Map.get(@report.other_stats, :total, 0) > 0 do %>
|
||||
<.other_stat_card label={translated("importAnalysis.other")} stats={@report.other_stats} />
|
||||
<.other_stat_card label={dgettext("ui", "Other")} stats={@report.other_stats} />
|
||||
<% end %>
|
||||
<.stat_card label={translated("importAnalysis.pages")} stats={@report.page_stats} />
|
||||
<.media_stat_card label={translated("importAnalysis.media")} stats={@report.media_stats} />
|
||||
<.taxonomy_stat_card label={translated("importAnalysis.categories")} stats={@report.category_stats} />
|
||||
<.taxonomy_stat_card label={translated("importAnalysis.tags")} stats={@report.tag_stats} />
|
||||
<.stat_card label={dgettext("ui", "pages")} stats={@report.page_stats} />
|
||||
<.media_stat_card label={dgettext("ui", "media")} stats={@report.media_stats} />
|
||||
<.taxonomy_stat_card label={dgettext("ui", "Categories")} stats={@report.category_stats} />
|
||||
<.taxonomy_stat_card label={dgettext("ui", "Tags")} stats={@report.tag_stats} />
|
||||
</div>
|
||||
|
||||
<%= if Enum.any?(Map.get(@report, :date_distribution, [])) do %>
|
||||
<div class="import-date-distribution">
|
||||
<h3><%= translated("importAnalysis.dateDistribution") %></h3>
|
||||
<h3><%= dgettext("ui", "Date Distribution") %></h3>
|
||||
<div class="distribution-bars">
|
||||
<%= for row <- @report.date_distribution do %>
|
||||
<div class="distribution-row">
|
||||
@@ -900,13 +897,13 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<%= if @execution_state.is_executing do %>
|
||||
<div class="import-execution-progress">
|
||||
<div class="import-execution-header">
|
||||
<h3><%= translated("importAnalysis.importing") %></h3>
|
||||
<h3><%= dgettext("ui", "Importing...") %></h3>
|
||||
</div>
|
||||
<div class="import-progress-bar">
|
||||
<div class="import-progress-fill" style={"width: #{execution_progress_width(@execution_state)}%;"}></div>
|
||||
</div>
|
||||
<div class="import-progress-info">
|
||||
<span class="import-phase"><%= @execution_state.phase || translated("importAnalysis.executionStarting") %></span>
|
||||
<span class="import-phase"><%= @execution_state.phase || dgettext("ui", "Starting...") %></span>
|
||||
<%= if present?(@execution_state.detail) do %>
|
||||
<span class="import-detail"><%= @execution_state.detail %></span>
|
||||
<% end %>
|
||||
@@ -921,18 +918,18 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<%= if not @execution_state.is_executing and not @execution_state.completed do %>
|
||||
<div class="import-execute-section">
|
||||
<div class="import-execute-summary">
|
||||
<%= translated("importAnalysis.readyToImport") %>
|
||||
<%= if @counts.tags > 0 do %><span class="import-count-tag"><%= @counts.tags %> <%= translated("importAnalysis.tagsCategories") %></span><% end %>
|
||||
<%= if @counts.posts > 0 do %><span class="import-count-tag"><%= @counts.posts %> <%= translated("importAnalysis.posts") %></span><% end %>
|
||||
<%= if @counts.media > 0 do %><span class="import-count-tag"><%= @counts.media %> <%= translated("importAnalysis.media") %></span><% end %>
|
||||
<%= if @counts.pages > 0 do %><span class="import-count-tag"><%= @counts.pages %> <%= translated("importAnalysis.pages") %></span><% end %>
|
||||
<%= dgettext("ui", "Ready to import:") %>
|
||||
<%= if @counts.tags > 0 do %><span class="import-count-tag"><%= @counts.tags %> <%= dgettext("ui", "tags/categories") %></span><% end %>
|
||||
<%= if @counts.posts > 0 do %><span class="import-count-tag"><%= @counts.posts %> <%= dgettext("ui", "posts") %></span><% end %>
|
||||
<%= if @counts.media > 0 do %><span class="import-count-tag"><%= @counts.media %> <%= dgettext("ui", "media") %></span><% end %>
|
||||
<%= if @counts.pages > 0 do %><span class="import-count-tag"><%= @counts.pages %> <%= dgettext("ui", "pages") %></span><% end %>
|
||||
</div>
|
||||
|
||||
<button class="import-execute-btn" type="button" phx-click="execute_import_editor" phx-target={@myself} disabled={@counts.total == 0}>
|
||||
<%= if @counts.total == 0 do %>
|
||||
<%= translated("importAnalysis.nothingToImport") %>
|
||||
<%= dgettext("ui", "Nothing to Import") %>
|
||||
<% else %>
|
||||
<%= translated("importAnalysis.importItems", %{count: @counts.total}) %>
|
||||
<%= dgettext("ui", "Import %{count} Items", count: @counts.total) %>
|
||||
<% end %>
|
||||
</button>
|
||||
</div>
|
||||
@@ -940,56 +937,56 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
|
||||
<%= if @execution_state.completed do %>
|
||||
<div class="import-execution-complete">
|
||||
<span><%= translated("importAnalysis.importComplete", %{count: @execution_state.count || @counts.total}) %></span>
|
||||
<span><%= dgettext("ui", "Import completed successfully!", count: @execution_state.count || @counts.total) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if present?(@execution_state.error) do %>
|
||||
<div class="import-execution-error">
|
||||
<span><%= translated("importAnalysis.importFailed", %{error: @execution_state.error}) %></span>
|
||||
<span><%= dgettext("ui", "Import failed: %{error}", error: @execution_state.error) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if Enum.any?(@post_conflicts) do %>
|
||||
<.conflict_section title={translated("importAnalysis.postSlugConflicts")} items={@post_conflicts} expanded={@sections.post_conflicts} section="post_conflicts" myself={@myself} />
|
||||
<.conflict_section title={dgettext("ui", "Post Slug Conflicts")} items={@post_conflicts} expanded={@sections.post_conflicts} section="post_conflicts" myself={@myself} />
|
||||
<% end %>
|
||||
|
||||
<%= if Enum.any?(@page_conflicts) do %>
|
||||
<.conflict_section title={translated("importAnalysis.pageSlugConflicts")} items={@page_conflicts} expanded={@sections.page_conflicts} section="page_conflicts" myself={@myself} />
|
||||
<.conflict_section title={dgettext("ui", "Page Slug Conflicts")} items={@page_conflicts} expanded={@sections.page_conflicts} section="page_conflicts" myself={@myself} />
|
||||
<% end %>
|
||||
|
||||
<%= if Enum.any?(@post_items) do %>
|
||||
<.post_detail_section title={translated("importAnalysis.postsWithCount", %{count: length(@post_items)})} items={@post_items} expanded={@sections.posts} section="posts" myself={@myself} />
|
||||
<.post_detail_section title={dgettext("ui", "Posts (%{count})", count: length(@post_items))} items={@post_items} expanded={@sections.posts} section="posts" myself={@myself} />
|
||||
<% end %>
|
||||
|
||||
<%= if Enum.any?(@other_items) do %>
|
||||
<.post_detail_section title={translated("importAnalysis.otherWithCount", %{count: length(@other_items)})} items={@other_items} expanded={@sections.other} section="other" show_type={true} myself={@myself} />
|
||||
<.post_detail_section title={dgettext("ui", "Other (%{count})", count: length(@other_items))} items={@other_items} expanded={@sections.other} section="other" show_type={true} myself={@myself} />
|
||||
<% end %>
|
||||
|
||||
<%= if Enum.any?(@detail_pages) do %>
|
||||
<.post_detail_section title={translated("importAnalysis.pagesWithCount", %{count: length(@detail_pages)})} items={@detail_pages} expanded={@sections.pages} section="pages" myself={@myself} />
|
||||
<.post_detail_section title={dgettext("ui", "Pages (%{count})", count: length(@detail_pages))} items={@detail_pages} expanded={@sections.pages} section="pages" myself={@myself} />
|
||||
<% end %>
|
||||
|
||||
<%= if Enum.any?(@detail_media) do %>
|
||||
<.media_detail_section title={translated("importAnalysis.mediaWithCount", %{count: length(@detail_media)})} items={@detail_media} expanded={@sections.media} section="media" myself={@myself} />
|
||||
<.media_detail_section title={dgettext("ui", "Media (%{count})", count: length(@detail_media))} items={@detail_media} expanded={@sections.media} section="media" myself={@myself} />
|
||||
<% end %>
|
||||
|
||||
<%= if Enum.any?(Map.get(@report.items, :categories, [])) or Enum.any?(Map.get(@report.items, :tags, [])) do %>
|
||||
<section class="import-detail-section">
|
||||
<button class="import-section-toggle" type="button" phx-click="toggle_import_section" phx-target={@myself} phx-value-section="taxonomy">
|
||||
<span><%= translated("importAnalysis.taxonomyTitle") %></span>
|
||||
<span><%= dgettext("ui", "Categories & Tags") %></span>
|
||||
<span class="toggle-icon"><%= if @sections.taxonomy, do: "▾", else: "▸" %></span>
|
||||
</button>
|
||||
|
||||
<%= if @sections.taxonomy do %>
|
||||
<div class="taxonomy-analyze-row">
|
||||
<div class="taxonomy-analyze-dropdown">
|
||||
<button class="taxonomy-analyze-btn" type="button" phx-click="toggle_import_ai_model_selector" phx-target={@myself}><%= translated("importAnalysis.analyzeWith") %></button>
|
||||
<button class="taxonomy-analyze-btn" type="button" phx-click="toggle_import_ai_model_selector" phx-target={@myself}><%= dgettext("ui", "Analyze with...") %></button>
|
||||
<%= if @import_editor.model_selector_open? do %>
|
||||
<div class="taxonomy-model-dropdown">
|
||||
<%= for model <- @import_editor.available_models do %>
|
||||
<button class="taxonomy-model-option" type="button" phx-click="select_import_ai_model" phx-target={@myself} phx-value-model={model.id}>
|
||||
<%= model.provider_name || model.provider || translated("importAnalysis.unknown") %>: <%= model.name || model.id %>
|
||||
<%= model.provider_name || model.provider || dgettext("ui", "Unknown") %>: <%= model.name || model.id %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -1000,12 +997,12 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<%= @import_editor.selected_model_label %>
|
||||
</button>
|
||||
|
||||
<span class="taxonomy-analyze-hint"><%= translated("importAnalysis.aiMappingHint") %></span>
|
||||
<span class="taxonomy-analyze-hint"><%= dgettext("ui", "AI will suggest mappings from new to existing items to avoid duplicates") %></span>
|
||||
</div>
|
||||
|
||||
<div class="import-taxonomy-groups">
|
||||
<.taxonomy_group
|
||||
title={translated("importAnalysis.categories")}
|
||||
title={dgettext("ui", "Categories")}
|
||||
items={Map.get(@report.items, :categories, [])}
|
||||
suggestions={Map.get(@import_editor.taxonomy_terms, :categories, [])}
|
||||
edit={@import_editor.taxonomy_edit}
|
||||
@@ -1013,7 +1010,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
myself={@myself}
|
||||
/>
|
||||
<.taxonomy_group
|
||||
title={translated("importAnalysis.tags")}
|
||||
title={dgettext("ui", "Tags")}
|
||||
items={Map.get(@report.items, :tags, [])}
|
||||
suggestions={Map.get(@import_editor.taxonomy_terms, :tags, [])}
|
||||
edit={@import_editor.taxonomy_edit}
|
||||
@@ -1029,14 +1026,14 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<%= if Enum.any?(Map.get(macros, :discovered, [])) do %>
|
||||
<section class="import-detail-section">
|
||||
<button class="import-section-toggle" type="button" phx-click="toggle_import_section" phx-target={@myself} phx-value-section="macros">
|
||||
<span><%= translated("importAnalysis.macrosWithCount", %{count: macros.total || length(macros.discovered)}) %></span>
|
||||
<span><%= dgettext("ui", "Macros (%{count})", count: macros.total || length(macros.discovered)) %></span>
|
||||
<span class="toggle-icon"><%= if @sections.macros, do: "▾", else: "▸" %></span>
|
||||
</button>
|
||||
|
||||
<%= if @sections.macros do %>
|
||||
<div class="macros-summary">
|
||||
<span class="macros-mapped"><%= translated("importAnalysis.mappedCount", %{count: macros.mapped_count || 0}) %></span>
|
||||
<span class="macros-unmapped"><%= translated("importAnalysis.unmappedCount", %{count: macros.unmapped_count || 0}) %></span>
|
||||
<span class="macros-mapped"><%= dgettext("ui", "%{count} mapped", count: macros.mapped_count || 0) %></span>
|
||||
<span class="macros-unmapped"><%= dgettext("ui", "%{count} unmapped", count: macros.unmapped_count || 0) %></span>
|
||||
</div>
|
||||
<div class="macros-list">
|
||||
<%= for macro <- macros.discovered do %>
|
||||
@@ -1044,9 +1041,9 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<div class="macro-header">
|
||||
<span class="macro-name"><%= macro.name %></span>
|
||||
<span class={"macro-status-badge #{if macro.mapped, do: "mapped", else: "unmapped"}"}>
|
||||
<%= if macro.mapped, do: translated("importAnalysis.macroStatusMapped"), else: translated("importAnalysis.macroStatusUnknown") %>
|
||||
<%= if macro.mapped, do: dgettext("ui", "Mapped"), else: dgettext("ui", "Unknown") %>
|
||||
</span>
|
||||
<span class="macro-count"><%= translated("importAnalysis.macroUses", %{count: macro.total_count}) %></span>
|
||||
<span class="macro-count"><%= dgettext("ui", "%{count} uses", count: macro.total_count) %></span>
|
||||
</div>
|
||||
<%= if Enum.any?(Map.get(macro, :usages, [])) do %>
|
||||
<div class="macro-usages">
|
||||
@@ -1058,17 +1055,17 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<span class="macro-usage-param"><%= k %>=<%= v %></span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= translated("importAnalysis.noParameters") %>
|
||||
<%= dgettext("ui", "(no parameters)") %>
|
||||
<% end %>
|
||||
</span>
|
||||
<span class="macro-usage-count"><%= translated("importAnalysis.macroUses", %{count: usage.count}) %></span>
|
||||
<span class="macro-usage-count"><%= dgettext("ui", "%{count} uses", count: usage.count) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= if Enum.any?(Map.get(macro, :post_slugs, [])) do %>
|
||||
<div class="macro-post-slugs">
|
||||
<%= translated("importAnalysis.usedIn", %{items: Enum.join(Enum.take(macro.post_slugs, 5), ", "), more: if(length(macro.post_slugs) > 5, do: translated("importAnalysis.moreSuffix", %{count: length(macro.post_slugs) - 5}), else: "")}) %>
|
||||
<%= dgettext("ui", "Used in: %{items}%{more}", items: Enum.join(Enum.take(macro.post_slugs, 5), ", "), more: (if length(macro.post_slugs) > 5, do: dgettext("ui", ", +%{count} more", count: length(macro.post_slugs) - 5), else: "")) %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -1084,7 +1081,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path>
|
||||
</svg>
|
||||
<p><%= translated("importAnalysis.emptyState") %></p>
|
||||
<p><%= dgettext("ui", "Select a WordPress export file to begin analysis.") %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -1111,10 +1108,10 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<table class="import-detail-table conflicts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= translated("importAnalysis.slug") %></th>
|
||||
<th><%= translated("importAnalysis.newEntryWxr") %></th>
|
||||
<th><%= translated("importAnalysis.existingEntry") %></th>
|
||||
<th><%= translated("importAnalysis.resolution") %></th>
|
||||
<th><%= dgettext("ui", "Slug") %></th>
|
||||
<th><%= dgettext("ui", "New Entry (WXR)") %></th>
|
||||
<th><%= dgettext("ui", "Existing Entry") %></th>
|
||||
<th><%= dgettext("ui", "Resolution") %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -1122,15 +1119,15 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<tr>
|
||||
<td class="slug-cell"><%= Map.get(item, :slug) %></td>
|
||||
<td><%= Map.get(item, :title) %></td>
|
||||
<td><%= Map.get(item, :existing_title) || translated("importAnalysis.none") %></td>
|
||||
<td><%= Map.get(item, :existing_title) || dgettext("ui", "--") %></td>
|
||||
<td>
|
||||
<form phx-change="change_import_conflict_resolution" phx-target={@myself}>
|
||||
<input type="hidden" name="item_type" value={Map.get(item, :item_type)} />
|
||||
<input type="hidden" name="item_name" value={Map.get(item, :slug)} />
|
||||
<select class="resolution-select" name="resolution">
|
||||
<option value="ignore" selected={conflict_resolution_selected?(item, "ignore")}><%= translated("importAnalysis.ignore") %></option>
|
||||
<option value="overwrite" selected={conflict_resolution_selected?(item, "overwrite")}><%= translated("importAnalysis.overwrite") %></option>
|
||||
<option value="import" selected={Map.get(item, :resolution) == "import"}><%= translated("importAnalysis.importNewSlug") %></option>
|
||||
<option value="ignore" selected={conflict_resolution_selected?(item, "ignore")}><%= dgettext("ui", "Ignore") %></option>
|
||||
<option value="overwrite" selected={conflict_resolution_selected?(item, "overwrite")}><%= dgettext("ui", "Overwrite") %></option>
|
||||
<option value="import" selected={Map.get(item, :resolution) == "import"}><%= dgettext("ui", "Import (new slug)") %></option>
|
||||
</select>
|
||||
</form>
|
||||
</td>
|
||||
@@ -1163,15 +1160,15 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<table class="import-detail-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= translated("importAnalysis.status") %></th>
|
||||
<th><%= dgettext("ui", "Status") %></th>
|
||||
<%= if @show_type do %>
|
||||
<th><%= translated("importAnalysis.type") %></th>
|
||||
<th><%= dgettext("ui", "Type") %></th>
|
||||
<% end %>
|
||||
<th><%= translated("importAnalysis.title") %></th>
|
||||
<th><%= translated("importAnalysis.slug") %></th>
|
||||
<th><%= translated("importAnalysis.categories") %></th>
|
||||
<th><%= translated("importAnalysis.wpStatus") %></th>
|
||||
<th><%= translated("importAnalysis.existingMatch") %></th>
|
||||
<th><%= dgettext("ui", "Title") %></th>
|
||||
<th><%= dgettext("ui", "Slug") %></th>
|
||||
<th><%= dgettext("ui", "Categories") %></th>
|
||||
<th><%= dgettext("ui", "WP Status") %></th>
|
||||
<th><%= dgettext("ui", "Existing Match") %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -1184,8 +1181,8 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<td><%= Map.get(item, :title) %></td>
|
||||
<td class="slug-cell"><%= Map.get(item, :slug) %></td>
|
||||
<td class="categories-cell"><%= joined_or_none(Map.get(item, :categories)) %></td>
|
||||
<td><%= Map.get(item, :wp_status) || translated("importAnalysis.none") %></td>
|
||||
<td class="existing-match"><%= Map.get(item, :existing_title) || translated("importAnalysis.none") %></td>
|
||||
<td><%= Map.get(item, :wp_status) || dgettext("ui", "--") %></td>
|
||||
<td class="existing-match"><%= Map.get(item, :existing_title) || dgettext("ui", "--") %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
@@ -1214,11 +1211,11 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<table class="import-detail-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= translated("importAnalysis.status") %></th>
|
||||
<th><%= translated("importAnalysis.filename") %></th>
|
||||
<th><%= translated("importAnalysis.type") %></th>
|
||||
<th><%= translated("importAnalysis.path") %></th>
|
||||
<th><%= translated("importAnalysis.existingMatch") %></th>
|
||||
<th><%= dgettext("ui", "Status") %></th>
|
||||
<th><%= dgettext("ui", "Filename") %></th>
|
||||
<th><%= dgettext("ui", "Type") %></th>
|
||||
<th><%= dgettext("ui", "Path") %></th>
|
||||
<th><%= dgettext("ui", "Existing Match") %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -1226,9 +1223,9 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<tr>
|
||||
<td><span class={status_badge_class(item.status)}><%= item.status %></span></td>
|
||||
<td><%= Map.get(item, :filename) %></td>
|
||||
<td class="mime-type-cell"><%= Map.get(item, :mime_type) || translated("importAnalysis.none") %></td>
|
||||
<td class="mime-type-cell"><%= Map.get(item, :mime_type) || dgettext("ui", "--") %></td>
|
||||
<td class="slug-cell"><%= Map.get(item, :relative_path) %></td>
|
||||
<td class="existing-match"><%= Map.get(item, :existing_title) || translated("importAnalysis.none") %></td>
|
||||
<td class="existing-match"><%= Map.get(item, :existing_title) || dgettext("ui", "--") %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
@@ -1248,10 +1245,10 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<h3><%= @label %></h3>
|
||||
<div class="import-stat-number"><%= total_stats(@stats) %></div>
|
||||
<div class="import-stat-breakdown">
|
||||
<%= if @stats.new_count > 0 do %><span class="import-stat-tag stat-new"><%= @stats.new_count %> <%= translated("importAnalysis.new") %></span><% end %>
|
||||
<%= if @stats.update_count > 0 do %><span class="import-stat-tag stat-update"><%= @stats.update_count %> <%= translated("importAnalysis.update") %></span><% end %>
|
||||
<%= if @stats.conflict_count > 0 do %><span class="import-stat-tag stat-conflict"><%= @stats.conflict_count %> <%= translated("importAnalysis.conflict") %></span><% end %>
|
||||
<%= if @stats.duplicate_count > 0 do %><span class="import-stat-tag stat-duplicate"><%= @stats.duplicate_count %> <%= translated("importAnalysis.duplicate") %></span><% end %>
|
||||
<%= if @stats.new_count > 0 do %><span class="import-stat-tag stat-new"><%= @stats.new_count %> <%= dgettext("ui", "new") %></span><% end %>
|
||||
<%= if @stats.update_count > 0 do %><span class="import-stat-tag stat-update"><%= @stats.update_count %> <%= dgettext("ui", "update") %></span><% end %>
|
||||
<%= if @stats.conflict_count > 0 do %><span class="import-stat-tag stat-conflict"><%= @stats.conflict_count %> <%= dgettext("ui", "conflict") %></span><% end %>
|
||||
<%= if @stats.duplicate_count > 0 do %><span class="import-stat-tag stat-duplicate"><%= @stats.duplicate_count %> <%= dgettext("ui", "duplicate") %></span><% end %>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
@@ -1285,11 +1282,11 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<h3><%= @label %></h3>
|
||||
<div class="import-stat-number"><%= total_media_stats(@stats) %></div>
|
||||
<div class="import-stat-breakdown">
|
||||
<%= if @stats.new_count > 0 do %><span class="import-stat-tag stat-new"><%= @stats.new_count %> <%= translated("importAnalysis.new") %></span><% end %>
|
||||
<%= if @stats.update_count > 0 do %><span class="import-stat-tag stat-update"><%= @stats.update_count %> <%= translated("importAnalysis.update") %></span><% end %>
|
||||
<%= if @stats.conflict_count > 0 do %><span class="import-stat-tag stat-conflict"><%= @stats.conflict_count %> <%= translated("importAnalysis.conflict") %></span><% end %>
|
||||
<%= if @stats.duplicate_count > 0 do %><span class="import-stat-tag stat-duplicate"><%= @stats.duplicate_count %> <%= translated("importAnalysis.duplicate") %></span><% end %>
|
||||
<%= if @stats.missing_count > 0 do %><span class="import-stat-tag stat-missing"><%= @stats.missing_count %> <%= translated("importAnalysis.missing") %></span><% end %>
|
||||
<%= if @stats.new_count > 0 do %><span class="import-stat-tag stat-new"><%= @stats.new_count %> <%= dgettext("ui", "new") %></span><% end %>
|
||||
<%= if @stats.update_count > 0 do %><span class="import-stat-tag stat-update"><%= @stats.update_count %> <%= dgettext("ui", "update") %></span><% end %>
|
||||
<%= if @stats.conflict_count > 0 do %><span class="import-stat-tag stat-conflict"><%= @stats.conflict_count %> <%= dgettext("ui", "conflict") %></span><% end %>
|
||||
<%= if @stats.duplicate_count > 0 do %><span class="import-stat-tag stat-duplicate"><%= @stats.duplicate_count %> <%= dgettext("ui", "duplicate") %></span><% end %>
|
||||
<%= if @stats.missing_count > 0 do %><span class="import-stat-tag stat-missing"><%= @stats.missing_count %> <%= dgettext("ui", "missing") %></span><% end %>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
@@ -1305,9 +1302,9 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
<h3><%= @label %></h3>
|
||||
<div class="import-stat-number"><%= @stats.existing_count + @stats.mapped_count + @stats.new_count %></div>
|
||||
<div class="import-stat-breakdown">
|
||||
<%= if @stats.existing_count > 0 do %><span class="import-stat-tag stat-update"><%= @stats.existing_count %> <%= translated("importAnalysis.existing") %></span><% end %>
|
||||
<%= if @stats.mapped_count > 0 do %><span class="import-stat-tag stat-mapped"><%= @stats.mapped_count %> <%= translated("importAnalysis.mapped") %></span><% end %>
|
||||
<%= if @stats.new_count > 0 do %><span class="import-stat-tag stat-new"><%= @stats.new_count %> <%= translated("importAnalysis.new") %></span><% end %>
|
||||
<%= if @stats.existing_count > 0 do %><span class="import-stat-tag stat-update"><%= @stats.existing_count %> <%= dgettext("ui", "existing") %></span><% end %>
|
||||
<%= if @stats.mapped_count > 0 do %><span class="import-stat-tag stat-mapped"><%= @stats.mapped_count %> <%= dgettext("ui", "mapped") %></span><% end %>
|
||||
<%= if @stats.new_count > 0 do %><span class="import-stat-tag stat-new"><%= @stats.new_count %> <%= dgettext("ui", "new") %></span><% end %>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
@@ -1342,14 +1339,14 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
type="text"
|
||||
name="mapped_to"
|
||||
value={Map.get(@edit || %{}, :value, Map.get(item, :mapped_to) || "") || ""}
|
||||
placeholder={translated("importAnalysis.mapToPlaceholder")}
|
||||
placeholder={dgettext("ui", "Map to...")}
|
||||
list={"taxonomy-suggestions-#{@type}"}
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button class="taxonomy-edit-btn" type="submit" title={translated("importAnalysis.mapToPlaceholder")}>✓</button>
|
||||
<button class="taxonomy-edit-btn ghost" type="button" phx-click="cancel_import_taxonomy_edit" phx-target={@myself} title={translated("Cancel")}>×</button>
|
||||
<button class="taxonomy-edit-btn" type="submit" title={dgettext("ui", "Map to...")}>✓</button>
|
||||
<button class="taxonomy-edit-btn ghost" type="button" phx-click="cancel_import_taxonomy_edit" phx-target={@myself} title={dgettext("ui", "Cancel")}>×</button>
|
||||
<%= if present?(item.mapped_to) do %>
|
||||
<button class="taxonomy-clear-btn" type="button" phx-click="clear_import_taxonomy_mapping" phx-target={@myself} phx-value-type={@type} phx-value-name={item.name} title={translated("importAnalysis.clearMapping")}>×</button>
|
||||
<button class="taxonomy-clear-btn" type="button" phx-click="clear_import_taxonomy_mapping" phx-target={@myself} phx-value-type={@type} phx-value-name={item.name} title={dgettext("ui", "Clear mapping")}>×</button>
|
||||
<% end %>
|
||||
</form>
|
||||
<% else %>
|
||||
@@ -1380,7 +1377,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
phx-value-name={item.name}
|
||||
phx-value-mapped_to={Map.get(item, :mapped_to) || ""}
|
||||
><%= item.mapped_to %></button>
|
||||
<button class="taxonomy-clear-btn" type="button" phx-click="clear_import_taxonomy_mapping" phx-target={@myself} phx-value-type={@type} phx-value-name={item.name} title={translated("importAnalysis.clearMapping")}>×</button>
|
||||
<button class="taxonomy-clear-btn" type="button" phx-click="clear_import_taxonomy_mapping" phx-target={@myself} phx-value-type={@type} phx-value-name={item.name} title={dgettext("ui", "Clear mapping")}>×</button>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -1416,7 +1413,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
end
|
||||
|
||||
defp joined_or_none(values) when is_list(values) and values != [], do: Enum.join(values, ", ")
|
||||
defp joined_or_none(_values), do: translated("importAnalysis.none")
|
||||
defp joined_or_none(_values), do: dgettext("ui", "--")
|
||||
|
||||
defp status_badge_class(status), do: ["status-badge", status]
|
||||
|
||||
@@ -1445,7 +1442,4 @@ defmodule BDS.Desktop.ShellLive.ImportEditor do
|
||||
|
||||
defp maybe_put(map, _key, nil), do: map
|
||||
defp maybe_put(map, key, value), do: Map.put(map, key, value)
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -2,7 +2,8 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.AnalysisState do
|
||||
@moduledoc false
|
||||
|
||||
alias BDS.{ImportAnalysis, ImportDefinitions, Metadata}
|
||||
alias BDS.Desktop.{FilePicker, FolderPicker, ShellData}
|
||||
alias BDS.Desktop.{FilePicker, FolderPicker}
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec change_definition(term(), term(), term()) :: term()
|
||||
def change_definition(socket, params, reload) do
|
||||
@@ -18,7 +19,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.AnalysisState do
|
||||
@spec select_uploads_folder(term(), term(), term()) :: term()
|
||||
def select_uploads_folder(socket, reload, append_output) do
|
||||
with %{id: definition_id} <- socket.assigns.current_tab do
|
||||
case FolderPicker.choose_directory(translated("importAnalysis.uploadsFolder")) do
|
||||
case FolderPicker.choose_directory(dgettext("ui", "Uploads Folder")) do
|
||||
{:ok, uploads_folder_path} ->
|
||||
{:ok, _definition} =
|
||||
ImportDefinitions.update_definition(definition_id, %{
|
||||
@@ -32,7 +33,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.AnalysisState do
|
||||
|
||||
{:error, %{message: message}} ->
|
||||
socket
|
||||
|> append_output.(translated("activity.import"), message, nil, "error")
|
||||
|> append_output.(dgettext("ui", "Import"), message, nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
else
|
||||
@@ -44,7 +45,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.AnalysisState do
|
||||
def select_and_analyze(socket, reload, append_output) do
|
||||
with %{id: definition_id} <- socket.assigns.current_tab,
|
||||
%{} = definition <- ImportDefinitions.get_definition(definition_id) do
|
||||
case FilePicker.choose_file(translated("importAnalysis.wxrFile")) do
|
||||
case FilePicker.choose_file(dgettext("ui", "WXR File")) do
|
||||
{:ok, wxr_file_path} ->
|
||||
project_id = socket.assigns.projects.active_project_id
|
||||
|
||||
@@ -78,7 +79,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.AnalysisState do
|
||||
:import_editor_analysis_states,
|
||||
Map.put(socket.assigns.import_editor_analysis_states, definition_id, %{
|
||||
loading: true,
|
||||
step: translated("importAnalysis.analyzingWxr"),
|
||||
step: dgettext("ui", "Analyzing WXR file..."),
|
||||
detail: Path.basename(wxr_file_path),
|
||||
file_path: wxr_file_path,
|
||||
ref: task.ref
|
||||
@@ -99,7 +100,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.AnalysisState do
|
||||
|
||||
{:error, %{message: message}} ->
|
||||
socket
|
||||
|> append_output.(translated("activity.import"), message, nil, "error")
|
||||
|> append_output.(dgettext("ui", "Import"), message, nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
else
|
||||
@@ -167,18 +168,18 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.AnalysisState do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("activity.import"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Import"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
|
||||
{:error, %{message: message}} ->
|
||||
socket
|
||||
|> append_output.(translated("activity.import"), message, nil, "error")
|
||||
|> append_output.(dgettext("ui", "Import"), message, nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("activity.import"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Import"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -200,7 +201,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.AnalysisState do
|
||||
:import_editor_analysis_states,
|
||||
Map.delete(socket.assigns.import_editor_analysis_states, definition_id)
|
||||
)
|
||||
|> append_output.(translated("activity.import"), message, nil, "error")
|
||||
|> append_output.(dgettext("ui", "Import"), message, nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -294,12 +295,12 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.AnalysisState do
|
||||
|
||||
def translate_phase(step) when is_binary(step) do
|
||||
case step do
|
||||
"parsing" -> translated("importAnalysis.analysisPhase.parsing")
|
||||
"scanning" -> translated("importAnalysis.analysisPhase.scanning")
|
||||
"taxonomies" -> translated("importAnalysis.analysisPhase.taxonomies")
|
||||
"posts" -> translated("importAnalysis.analysisPhase.posts")
|
||||
"media" -> translated("importAnalysis.analysisPhase.media")
|
||||
"complete" -> translated("importAnalysis.analysisPhase.complete")
|
||||
"parsing" -> dgettext("ui", "Parsing WXR file...")
|
||||
"scanning" -> dgettext("ui", "Scanning entries...")
|
||||
"taxonomies" -> dgettext("ui", "Analyzing taxonomies...")
|
||||
"posts" -> dgettext("ui", "Analyzing posts...")
|
||||
"media" -> dgettext("ui", "Analyzing media...")
|
||||
"complete" -> dgettext("ui", "Analysis complete")
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
@@ -307,8 +308,5 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.AnalysisState do
|
||||
@spec translate_phase(term()) :: term()
|
||||
def translate_phase(other), do: other
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
|
||||
defp present?(value), do: value not in [nil, ""]
|
||||
end
|
||||
|
||||
@@ -2,8 +2,8 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.ProgressTracking do
|
||||
@moduledoc false
|
||||
|
||||
alias BDS.{ImportDefinitions, ImportExecution}
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Desktop.ShellLive.ImportEditor.AnalysisState
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec execute_import(term(), term(), term()) :: term()
|
||||
def execute_import(socket, reload, _append_output) do
|
||||
@@ -129,8 +129,8 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.ProgressTracking do
|
||||
})
|
||||
)
|
||||
|> append_output.(
|
||||
translated("activity.import"),
|
||||
translated("importAnalysis.importComplete", %{count: previous_state.count}),
|
||||
dgettext("ui", "Import"),
|
||||
dgettext("ui", "Import completed successfully!", count: previous_state.count),
|
||||
nil,
|
||||
"info"
|
||||
)
|
||||
@@ -148,7 +148,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.ProgressTracking do
|
||||
ref: nil
|
||||
})
|
||||
)
|
||||
|> append_output.(translated("activity.import"), message, nil, "error")
|
||||
|> append_output.(dgettext("ui", "Import"), message, nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
|
||||
{:error, reason} ->
|
||||
@@ -165,7 +165,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.ProgressTracking do
|
||||
ref: nil
|
||||
})
|
||||
)
|
||||
|> append_output.(translated("activity.import"), message, nil, "error")
|
||||
|> append_output.(dgettext("ui", "Import"), message, nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -208,7 +208,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.ProgressTracking do
|
||||
ref: nil
|
||||
})
|
||||
)
|
||||
|> append_output.(translated("activity.import"), message, nil, "error")
|
||||
|> append_output.(dgettext("ui", "Import"), message, nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -265,16 +265,16 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.ProgressTracking do
|
||||
seconds = div(ms, 1000)
|
||||
|
||||
if seconds < 60 do
|
||||
translated("importAnalysis.eta", %{
|
||||
value: translated("importAnalysis.etaSeconds", %{count: seconds})
|
||||
})
|
||||
dgettext("ui", "ETA: %{value}",
|
||||
value: dgettext("ui", "%{count}s", count: seconds)
|
||||
)
|
||||
else
|
||||
m = div(seconds, 60)
|
||||
s = rem(seconds, 60)
|
||||
|
||||
translated("importAnalysis.eta", %{
|
||||
value: translated("importAnalysis.etaMinutes", %{minutes: m, seconds: s})
|
||||
})
|
||||
dgettext("ui", "ETA: %{value}",
|
||||
value: dgettext("ui", "%{minutes}m %{seconds}s", minutes: m, seconds: s)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -282,18 +282,15 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.ProgressTracking do
|
||||
|
||||
def translate_execution_phase(phase) when is_binary(phase) do
|
||||
case phase do
|
||||
"tags" -> translated("importAnalysis.phase.tags")
|
||||
"posts" -> translated("importAnalysis.phase.posts")
|
||||
"media" -> translated("importAnalysis.phase.media")
|
||||
"pages" -> translated("importAnalysis.phase.pages")
|
||||
"complete" -> translated("importAnalysis.phase.complete")
|
||||
"tags" -> dgettext("ui", "Importing tags & categories...")
|
||||
"posts" -> dgettext("ui", "Importing posts...")
|
||||
"media" -> dgettext("ui", "Importing media...")
|
||||
"pages" -> dgettext("ui", "Importing pages...")
|
||||
"complete" -> dgettext("ui", "Import complete")
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
|
||||
@spec translate_execution_phase(term()) :: term()
|
||||
def translate_execution_phase(other), do: other
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.TaxonomyEditing do
|
||||
@moduledoc false
|
||||
|
||||
alias BDS.{AI, ImportDefinitions, Metadata, Tags}
|
||||
alias BDS.Desktop.ShellData
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec start_taxonomy_edit(term(), term(), term()) :: term()
|
||||
def start_taxonomy_edit(
|
||||
@@ -85,12 +85,8 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.TaxonomyEditing do
|
||||
socket.assigns.offline_mode ->
|
||||
socket
|
||||
|> append_output.(
|
||||
translated("activity.import"),
|
||||
ShellData.translate(
|
||||
"Automatic AI actions stay gated by airplane mode.",
|
||||
%{},
|
||||
socket.assigns.page_language
|
||||
),
|
||||
dgettext("ui", "Import"),
|
||||
BDS.Gettext.lgettext(socket.assigns.page_language, "ui", "Automatic AI actions stay gated by airplane mode."),
|
||||
nil,
|
||||
"info"
|
||||
)
|
||||
@@ -124,8 +120,8 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.TaxonomyEditing do
|
||||
|
||||
socket
|
||||
|> append_output.(
|
||||
translated("activity.import"),
|
||||
translated("importAnalysis.mappedCount", %{count: mapped_count}),
|
||||
dgettext("ui", "Import"),
|
||||
dgettext("ui", "%{count} mapped", count: mapped_count),
|
||||
Map.get(socket.assigns.import_editor_selected_models, definition_id),
|
||||
"info"
|
||||
)
|
||||
@@ -134,7 +130,7 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.TaxonomyEditing do
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(
|
||||
translated("activity.import"),
|
||||
dgettext("ui", "Import"),
|
||||
inspect(reason),
|
||||
Map.get(socket.assigns.import_editor_selected_models, definition_id),
|
||||
"error"
|
||||
@@ -274,19 +270,16 @@ defmodule BDS.Desktop.ShellLive.ImportEditor.TaxonomyEditing do
|
||||
def taxonomy_mapping_tooltip(item) do
|
||||
action =
|
||||
if present?(item.mapped_to),
|
||||
do: translated("importAnalysis.mappingActionEdit"),
|
||||
else: translated("importAnalysis.mappingActionAdd")
|
||||
do: dgettext("ui", "edit"),
|
||||
else: dgettext("ui", "add")
|
||||
|
||||
translated("importAnalysis.mappingTooltip", %{action: action})
|
||||
dgettext("ui", "Click to %{action} mapping", action: action)
|
||||
end
|
||||
|
||||
@spec maybe_put_option(term(), term(), term()) :: term()
|
||||
def maybe_put_option(opts, _key, nil), do: opts
|
||||
def maybe_put_option(opts, key, value), do: Keyword.put(opts, key, value)
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
|
||||
defp present?(value), do: value not in [nil, ""]
|
||||
defp blank_to_nil(""), do: nil
|
||||
defp blank_to_nil(value), do: value
|
||||
|
||||
@@ -77,8 +77,8 @@
|
||||
data-testid="toggle-sidebar"
|
||||
type="button"
|
||||
phx-click="toggle_sidebar"
|
||||
aria-label={translated("Toggle sidebar")}
|
||||
title={translated("Toggle sidebar")}
|
||||
aria-label={dgettext("ui", "Toggle sidebar")}
|
||||
title={dgettext("ui", "Toggle sidebar")}
|
||||
>
|
||||
<span class={["window-titlebar-sidebar-icon", if(@workbench.sidebar_visible, do: "is-active", else: "is-inactive")]}>
|
||||
<span class="window-titlebar-sidebar-pane"></span>
|
||||
@@ -89,8 +89,8 @@
|
||||
data-testid="toggle-panel"
|
||||
type="button"
|
||||
phx-click="toggle_panel"
|
||||
aria-label={translated("Toggle panel")}
|
||||
title={translated("Toggle panel")}
|
||||
aria-label={dgettext("ui", "Toggle panel")}
|
||||
title={dgettext("ui", "Toggle panel")}
|
||||
>
|
||||
<span class={["window-titlebar-panel-icon", if(@workbench.panel.visible, do: "is-active", else: "is-inactive")]}>
|
||||
<span class="window-titlebar-panel-pane"></span>
|
||||
@@ -101,8 +101,8 @@
|
||||
data-testid="toggle-assistant"
|
||||
type="button"
|
||||
phx-click="toggle_assistant_sidebar"
|
||||
aria-label={translated("Toggle assistant")}
|
||||
title={translated("Toggle assistant")}
|
||||
aria-label={dgettext("ui", "Toggle assistant")}
|
||||
title={dgettext("ui", "Toggle assistant")}
|
||||
>
|
||||
<span class={["window-titlebar-assistant-icon", if(@workbench.assistant_sidebar_visible, do: "is-active", else: "is-inactive")]}>
|
||||
<span class="window-titlebar-assistant-pane"></span>
|
||||
@@ -178,8 +178,8 @@
|
||||
data-testid="sidebar-filter-toggle"
|
||||
type="button"
|
||||
phx-click="toggle_sidebar_filters"
|
||||
aria-label={translated(Map.get(@sidebar_data.filters, :toggle_filters_label))}
|
||||
title={translated(Map.get(@sidebar_data.filters, :toggle_filters_label))}
|
||||
aria-label={Map.get(@sidebar_data.filters, :toggle_filters_label)}
|
||||
title={Map.get(@sidebar_data.filters, :toggle_filters_label)}
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M6 12v-1h4v1H6zM4 8v-1h8v1H4zm-2-4v-1h12v1H2z"/>
|
||||
@@ -194,8 +194,8 @@
|
||||
type="button"
|
||||
phx-click="create_sidebar_item"
|
||||
phx-value-kind={create_action.kind}
|
||||
aria-label={translated(create_action.label)}
|
||||
title={translated(create_action.label)}
|
||||
aria-label={create_action.label}
|
||||
title={create_action.label}
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M14 7v1H8v6H7V8H1V7h6V1h1v6h6z"/>
|
||||
@@ -215,7 +215,7 @@
|
||||
<main class="app-content" data-region="content">
|
||||
<div class="tab-bar" data-region="tab-bar">
|
||||
<%= if Enum.empty?(@workbench.tabs) do %>
|
||||
<div class="tab-bar-empty"><%= translated("Dashboard") %></div>
|
||||
<div class="tab-bar-empty"><%= dgettext("ui", "Dashboard") %></div>
|
||||
<% else %>
|
||||
<div class="tab-bar-tabs">
|
||||
<%= for tab <- @workbench.tabs do %>
|
||||
@@ -253,8 +253,8 @@
|
||||
phx-click="close_tab"
|
||||
phx-value-type={tab.type}
|
||||
phx-value-id={tab.id}
|
||||
aria-label={translated("Close tab")}
|
||||
title={translated("Close tab")}
|
||||
aria-label={dgettext("ui", "Close tab")}
|
||||
title={dgettext("ui", "Close tab")}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
@@ -269,41 +269,41 @@
|
||||
<%= if is_nil(@current_tab) do %>
|
||||
<div class="editor-empty">
|
||||
<div class="dashboard-content">
|
||||
<h1 data-testid="editor-title"><%= translated("dashboard.title") %></h1>
|
||||
<p class="text-muted"><%= translated("dashboard.subtitle") %></p>
|
||||
<h1 data-testid="editor-title"><%= dgettext("ui", "Dashboard") %></h1>
|
||||
<p class="text-muted"><%= dgettext("ui", "Overview of your blog database") %></p>
|
||||
|
||||
<div class="dashboard-stats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number"><%= @dashboard.post_stats.total_posts || 0 %></div>
|
||||
<div class="stat-label"><%= translated("dashboard.stats.totalPosts") %></div>
|
||||
<div class="stat-label"><%= dgettext("ui", "Total Posts") %></div>
|
||||
<div class="stat-breakdown">
|
||||
<span class="stat-tag stat-published"><%= translated("dashboard.stats.published", %{count: @dashboard.post_stats.published_count || 0}) %></span>
|
||||
<span class="stat-tag stat-draft"><%= translated("dashboard.stats.drafts", %{count: @dashboard.post_stats.draft_count || 0}) %></span>
|
||||
<span class="stat-tag stat-published"><%= dgettext("ui", "dashboard.stats.published", count: @dashboard.post_stats.published_count || 0) %></span>
|
||||
<span class="stat-tag stat-draft"><%= dgettext("ui", "dashboard.stats.drafts", count: @dashboard.post_stats.draft_count || 0) %></span>
|
||||
<%= if (@dashboard.post_stats.archived_count || 0) > 0 do %>
|
||||
<span class="stat-tag stat-archived"><%= translated("dashboard.stats.archived", %{count: @dashboard.post_stats.archived_count || 0}) %></span>
|
||||
<span class="stat-tag stat-archived"><%= dgettext("ui", "dashboard.stats.archived", count: @dashboard.post_stats.archived_count || 0) %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number"><%= @dashboard.media_stats.media_count || 0 %></div>
|
||||
<div class="stat-label"><%= translated("dashboard.stats.mediaFiles") %></div>
|
||||
<div class="stat-label"><%= dgettext("ui", "Media Files") %></div>
|
||||
<div class="stat-breakdown">
|
||||
<span class="stat-tag"><%= translated("dashboard.stats.images", %{count: @dashboard.media_stats.image_count || 0}) %></span>
|
||||
<span class="stat-tag"><%= dgettext("ui", "dashboard.stats.images", count: @dashboard.media_stats.image_count || 0) %></span>
|
||||
<span class="stat-tag"><%= ShellData.format_bytes(@dashboard.media_stats.total_bytes || 0) %></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number"><%= length(@dashboard_tag_cloud_items) %></div>
|
||||
<div class="stat-label"><%= translated("dashboard.stats.tags") %></div>
|
||||
<div class="stat-label"><%= dgettext("ui", "Tags") %></div>
|
||||
<div class="stat-breakdown">
|
||||
<span class="stat-tag"><%= translated("dashboard.stats.categories", %{count: length(@dashboard_category_counts)}) %></span>
|
||||
<span class="stat-tag"><%= dgettext("ui", "dashboard.stats.categories", count: length(@dashboard_category_counts)) %></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= if Enum.any?(@dashboard_timeline_entries) do %>
|
||||
<div class="dashboard-section">
|
||||
<h4><%= translated("dashboard.section.postsOverTime") %></h4>
|
||||
<h4><%= dgettext("ui", "Posts Over Time") %></h4>
|
||||
<div class="timeline-chart">
|
||||
<%= for entry <- @dashboard_timeline_entries do %>
|
||||
<div class="timeline-bar-container">
|
||||
@@ -322,7 +322,7 @@
|
||||
|
||||
<%= if Enum.any?(@dashboard_tag_cloud_items) do %>
|
||||
<div class="dashboard-section">
|
||||
<h4><%= translated("dashboard.section.tags") %></h4>
|
||||
<h4><%= dgettext("ui", "Tags") %></h4>
|
||||
<div class="tag-cloud">
|
||||
<%= for item <- @dashboard_tag_cloud_items do %>
|
||||
<span class={["dashboard-tag", if(item.color, do: "has-color")]} style={ShellData.render_dashboard_tag_style(item)} title={ShellData.dashboard_post_count_label(item.count)}><%= item.tag %></span>
|
||||
@@ -333,7 +333,7 @@
|
||||
|
||||
<%= if Enum.any?(@dashboard_category_counts) do %>
|
||||
<div class="dashboard-section">
|
||||
<h4><%= translated("dashboard.section.categories") %></h4>
|
||||
<h4><%= dgettext("ui", "Categories") %></h4>
|
||||
<div class="tag-cloud">
|
||||
<%= for category <- @dashboard_category_counts do %>
|
||||
<span class="dashboard-tag dashboard-category" title={ShellData.dashboard_post_count_label(category.count || 0)}>
|
||||
@@ -347,7 +347,7 @@
|
||||
|
||||
<%= if Enum.any?(@dashboard_recent_posts) do %>
|
||||
<div class="dashboard-section">
|
||||
<h4><%= translated("dashboard.section.recentlyUpdated") %></h4>
|
||||
<h4><%= dgettext("ui", "Recently Updated") %></h4>
|
||||
<div class="recent-posts-list">
|
||||
<%= for post <- @dashboard_recent_posts do %>
|
||||
<button
|
||||
@@ -373,8 +373,8 @@
|
||||
<div class="dashboard-inspector-meta" hidden>
|
||||
<%= for item <- @editor_meta do %>
|
||||
<section class="editor-meta-row">
|
||||
<strong data-testid="editor-meta-label"><%= translated(item.label) %></strong>
|
||||
<span><%= translated(item.value) %></span>
|
||||
<strong data-testid="editor-meta-label"><%= item.label %></strong>
|
||||
<span><%= item.value %></span>
|
||||
</section>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -442,8 +442,8 @@
|
||||
<aside class="editor-meta">
|
||||
<%= for item <- @editor_meta do %>
|
||||
<section class="editor-meta-row">
|
||||
<strong data-testid="editor-meta-label"><%= translated(item.label) %></strong>
|
||||
<span><%= translated(item.value) %></span>
|
||||
<strong data-testid="editor-meta-label"><%= item.label %></strong>
|
||||
<span><%= item.value %></span>
|
||||
</section>
|
||||
<% end %>
|
||||
</aside>
|
||||
@@ -471,8 +471,8 @@
|
||||
data-testid="panel-close"
|
||||
type="button"
|
||||
phx-click="toggle_panel"
|
||||
aria-label={translated("Close panel")}
|
||||
title={translated("Close panel")}
|
||||
aria-label={dgettext("ui", "Close panel")}
|
||||
title={dgettext("ui", "Close panel")}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
@@ -493,24 +493,24 @@
|
||||
<div class="assistant-content">
|
||||
<header class="assistant-sidebar-header">
|
||||
<div class="assistant-sidebar-heading">
|
||||
<strong><%= translated("AI Assistant") %></strong>
|
||||
<span class="assistant-sidebar-description"><%= translated("AI conversations") %></span>
|
||||
<strong><%= dgettext("ui", "AI Assistant") %></strong>
|
||||
<span class="assistant-sidebar-description"><%= dgettext("ui", "AI conversations") %></span>
|
||||
</div>
|
||||
<span class={[
|
||||
"assistant-sidebar-status",
|
||||
if(@offline_mode, do: "is-offline", else: "is-online")
|
||||
]}>
|
||||
<%= if @offline_mode, do: translated("Offline"), else: translated("Chat") %>
|
||||
<%= if @offline_mode, do: dgettext("ui", "Offline"), else: dgettext("ui", "Chat") %>
|
||||
</span>
|
||||
</header>
|
||||
|
||||
<section class="assistant-sidebar-context" data-testid="assistant-context">
|
||||
<div class="assistant-sidebar-context-row">
|
||||
<span class="assistant-sidebar-context-label"><%= translated("Project") %></span>
|
||||
<span class="assistant-sidebar-context-label"><%= dgettext("ui", "Project") %></span>
|
||||
<span class="assistant-sidebar-context-value"><%= BDS.Desktop.ShellLive.ChatSurface.assistant_project_name(@current_project) %></span>
|
||||
</div>
|
||||
<div class="assistant-sidebar-context-row">
|
||||
<span class="assistant-sidebar-context-label"><%= translated("Editor") %></span>
|
||||
<span class="assistant-sidebar-context-label"><%= dgettext("ui", "Editor") %></span>
|
||||
<span class="assistant-sidebar-context-value"><%= BDS.Desktop.ShellLive.TabHelpers.tab_title(@current_tab, @tab_meta) %></span>
|
||||
</div>
|
||||
<p class="assistant-sidebar-context-text"><%= BDS.Desktop.ShellLive.TabHelpers.tab_subtitle(@current_tab, @tab_meta) %></p>
|
||||
@@ -527,7 +527,7 @@
|
||||
data-testid="assistant-prompt-input"
|
||||
name="assistant[prompt]"
|
||||
rows="6"
|
||||
placeholder={translated("Ask the assistant about the active project or editor.")}
|
||||
placeholder={dgettext("ui", "Ask the assistant about the active project or editor.")}
|
||||
><%= @assistant_prompt %></textarea>
|
||||
|
||||
<button
|
||||
@@ -536,7 +536,7 @@
|
||||
type="submit"
|
||||
disabled={String.trim(@assistant_prompt || "") == ""}
|
||||
>
|
||||
<%= translated("Start chat") %>
|
||||
<%= dgettext("ui", "Start chat") %>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -544,8 +544,8 @@
|
||||
<div class="assistant-sidebar-welcome">
|
||||
<%= for card <- @assistant_cards do %>
|
||||
<section class="assistant-card">
|
||||
<strong><%= translated(card.label) %></strong>
|
||||
<span><%= translated(card.text) %></span>
|
||||
<strong><%= card.label %></strong>
|
||||
<span><%= card.text %></span>
|
||||
</section>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -576,8 +576,8 @@
|
||||
data-testid="toggle-sidebar"
|
||||
type="button"
|
||||
phx-click="toggle_sidebar"
|
||||
aria-label={translated("Toggle sidebar")}
|
||||
title={translated("Toggle sidebar")}
|
||||
aria-label={dgettext("ui", "Toggle sidebar")}
|
||||
title={dgettext("ui", "Toggle sidebar")}
|
||||
>
|
||||
<span class={["window-titlebar-sidebar-icon", if(@workbench.sidebar_visible, do: "is-active", else: "is-inactive")]}>
|
||||
<span class="window-titlebar-sidebar-pane"></span>
|
||||
@@ -588,8 +588,8 @@
|
||||
data-testid="toggle-panel"
|
||||
type="button"
|
||||
phx-click="toggle_panel"
|
||||
aria-label={translated("Toggle panel")}
|
||||
title={translated("Toggle panel")}
|
||||
aria-label={dgettext("ui", "Toggle panel")}
|
||||
title={dgettext("ui", "Toggle panel")}
|
||||
>
|
||||
<span class={["window-titlebar-panel-icon", if(@workbench.panel.visible, do: "is-active", else: "is-inactive")]}>
|
||||
<span class="window-titlebar-panel-pane"></span>
|
||||
@@ -600,8 +600,8 @@
|
||||
data-testid="toggle-assistant"
|
||||
type="button"
|
||||
phx-click="toggle_assistant_sidebar"
|
||||
aria-label={translated("Toggle assistant")}
|
||||
title={translated("Toggle assistant")}
|
||||
aria-label={dgettext("ui", "Toggle assistant")}
|
||||
title={dgettext("ui", "Toggle assistant")}
|
||||
>
|
||||
<span class={["window-titlebar-assistant-icon", if(@workbench.assistant_sidebar_visible, do: "is-active", else: "is-inactive")]}>
|
||||
<span class="window-titlebar-assistant-pane"></span>
|
||||
@@ -614,7 +614,7 @@
|
||||
class="project-selector-trigger"
|
||||
data-testid="project-selector-trigger"
|
||||
type="button"
|
||||
title={translated("Switch project")}
|
||||
title={dgettext("ui", "Switch project")}
|
||||
phx-click="toggle_project_menu"
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" class="project-icon">
|
||||
@@ -629,7 +629,7 @@
|
||||
<%= if @project_menu_open do %>
|
||||
<div class="project-dropdown" data-testid="project-dropdown" phx-click-away="close_project_menu">
|
||||
<div class="project-dropdown-header">
|
||||
<span><%= translated("Projects") %></span>
|
||||
<span><%= dgettext("ui", "Projects") %></span>
|
||||
</div>
|
||||
<div class="project-list">
|
||||
<%= for project <- @projects.projects do %>
|
||||
@@ -650,10 +650,10 @@
|
||||
</div>
|
||||
<div class="project-dropdown-footer">
|
||||
<button class="existing-project-btn" type="button" phx-click="import_project">
|
||||
<span><%= translated("Open Existing Blog") %></span>
|
||||
<span><%= dgettext("ui", "Open Existing Blog") %></span>
|
||||
</button>
|
||||
<button class="create-project-btn" type="button" phx-click="create_project">
|
||||
<span><%= translated("New Project") %></span>
|
||||
<span><%= dgettext("ui", "New Project") %></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -663,7 +663,7 @@
|
||||
<%= if @status.left.running_task_message do %>
|
||||
<span class="task-spinner"></span>
|
||||
<% end %>
|
||||
<span class="task-message-text"><%= @status.left.running_task_message || translated("Idle") %></span>
|
||||
<span class="task-message-text"><%= @status.left.running_task_message || dgettext("ui", "Idle") %></span>
|
||||
<%= if (@status.left.running_task_overflow || 0) > 0 do %>
|
||||
<span class="status-bar-count">+<%= @status.left.running_task_overflow %></span>
|
||||
<% end %>
|
||||
@@ -673,9 +673,9 @@
|
||||
<span class="status-bar-item"><%= @status.right.post_count %></span>
|
||||
<span class="status-bar-item"><%= @status.right.media_count %></span>
|
||||
<span class="status-bar-item theme-badge"><%= @status.right.theme_badge %></span>
|
||||
<button class={["status-bar-item", "offline-badge", if(@status.right.offline_mode, do: "active")]} data-testid="status-offline-button" type="button" phx-click="toggle_offline_mode" title={translated("Toggle offline mode")}>✈</button>
|
||||
<button class={["status-bar-item", "offline-badge", if(@status.right.offline_mode, do: "active")]} data-testid="status-offline-button" type="button" phx-click="toggle_offline_mode" title={dgettext("ui", "Toggle offline mode")}>✈</button>
|
||||
<form class="status-bar-item language-badge" data-testid="status-language-form" phx-change="change_ui_language">
|
||||
<span><%= translated("UI") %></span>
|
||||
<span><%= dgettext("ui", "UI") %></span>
|
||||
<select class="status-bar-language-select" name="ui_language" data-testid="status-language-select">
|
||||
<%= for language <- @supported_ui_languages do %>
|
||||
<option selected={language.code == @page_language} value={language.code}><%= language.flag %></option>
|
||||
|
||||
@@ -5,12 +5,13 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias BDS.Desktop.{FilePicker, ShellData}
|
||||
alias BDS.Desktop.{FilePicker}
|
||||
alias BDS.{AI, I18n, Media}
|
||||
alias BDS.Media.Media, as: MediaRecord
|
||||
alias BDS.Media.Translation
|
||||
alias BDS.Posts.Post
|
||||
alias BDS.Repo
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
embed_templates("media_editor_html/*")
|
||||
|
||||
@@ -111,7 +112,7 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
def handle_event("replace_media_editor_file", _params, socket) do
|
||||
media = socket.assigns.media
|
||||
|
||||
case FilePicker.choose_file(translated("Replace Media File")) do
|
||||
case FilePicker.choose_file(dgettext("ui", "Replace Media File")) do
|
||||
{:ok, source_path} ->
|
||||
case Media.replace_media_file(media.id, source_path) do
|
||||
{:ok, %MediaRecord{} = updated_media} ->
|
||||
@@ -130,7 +131,7 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
{:noreply, build_data(socket)}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Replace File"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Replace File"), inspect(reason), "error")
|
||||
{:noreply, build_data(socket)}
|
||||
end
|
||||
|
||||
@@ -138,7 +139,7 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, %{message: message}} ->
|
||||
notify_output(socket, translated("Replace File"), message, "error")
|
||||
notify_output(socket, dgettext("ui", "Replace File"), message, "error")
|
||||
{:noreply, build_data(socket)}
|
||||
end
|
||||
end
|
||||
@@ -147,8 +148,8 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
if socket.assigns.offline_mode do
|
||||
notify_output(
|
||||
socket,
|
||||
translated("Detect Language"),
|
||||
translated("Automatic AI actions stay gated by airplane mode."),
|
||||
dgettext("ui", "Detect Language"),
|
||||
dgettext("ui", "Automatic AI actions stay gated by airplane mode."),
|
||||
"info"
|
||||
)
|
||||
|
||||
@@ -186,19 +187,19 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Detect Language"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Detect Language"), inspect(reason), "error")
|
||||
{:noreply, build_data(socket)}
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Detect Language"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Detect Language"), inspect(reason), "error")
|
||||
{:noreply, build_data(socket)}
|
||||
|
||||
_other ->
|
||||
notify_output(
|
||||
socket,
|
||||
translated("Detect Language"),
|
||||
translated("Language detection failed."),
|
||||
dgettext("ui", "Detect Language"),
|
||||
dgettext("ui", "Language detection failed."),
|
||||
"error"
|
||||
)
|
||||
|
||||
@@ -240,7 +241,7 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Link to Post"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Link to Post"), inspect(reason), "error")
|
||||
{:noreply, build_data(socket)}
|
||||
end
|
||||
end
|
||||
@@ -253,7 +254,7 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
{:noreply, build_data(socket)}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Unlink from Post"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Unlink from Post"), inspect(reason), "error")
|
||||
{:noreply, build_data(socket)}
|
||||
end
|
||||
end
|
||||
@@ -312,7 +313,7 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Save Translation"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Save Translation"), inspect(reason), "error")
|
||||
{:noreply, build_data(socket)}
|
||||
end
|
||||
|
||||
@@ -336,8 +337,8 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
if socket.assigns.offline_mode do
|
||||
notify_output(
|
||||
socket,
|
||||
translated("Translate"),
|
||||
translated("Automatic AI actions stay gated by airplane mode."),
|
||||
dgettext("ui", "Translate"),
|
||||
dgettext("ui", "Automatic AI actions stay gated by airplane mode."),
|
||||
"info"
|
||||
)
|
||||
|
||||
@@ -350,12 +351,12 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
{:noreply, build_data(socket)}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Refresh Translation"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Refresh Translation"), inspect(reason), "error")
|
||||
{:noreply, build_data(socket)}
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Refresh Translation"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Refresh Translation"), inspect(reason), "error")
|
||||
{:noreply, build_data(socket)}
|
||||
end
|
||||
end
|
||||
@@ -374,7 +375,7 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Delete Translation"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Delete Translation"), inspect(reason), "error")
|
||||
{:noreply, build_data(socket)}
|
||||
end
|
||||
end
|
||||
@@ -469,11 +470,11 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
|
||||
notify_parent({:media_editor_dirty, media.id, false})
|
||||
notify_parent({:media_editor_tab_meta, media.id, display_title(updated_media), updated_media.original_name || updated_media.mime_type || ""})
|
||||
notify_output(socket, translated("Media"), translated("Media saved"))
|
||||
notify_output(socket, dgettext("ui", "Media"), dgettext("ui", "Media saved"))
|
||||
socket
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Media"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Media"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
end
|
||||
end
|
||||
@@ -516,8 +517,8 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
if socket.assigns.offline_mode do
|
||||
notify_output(
|
||||
socket,
|
||||
translated("Translate"),
|
||||
translated("Automatic AI actions stay gated by airplane mode."),
|
||||
dgettext("ui", "Translate"),
|
||||
dgettext("ui", "Automatic AI actions stay gated by airplane mode."),
|
||||
"info"
|
||||
)
|
||||
|
||||
@@ -537,12 +538,12 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
|> build_data()
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Translate"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Translate"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Translate"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Translate"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
end
|
||||
end
|
||||
@@ -683,14 +684,11 @@ defmodule BDS.Desktop.ShellLive.MediaEditor do
|
||||
end
|
||||
end
|
||||
|
||||
@spec translated(term(), term()) :: term()
|
||||
def translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
|
||||
@spec media_editor_save_state_label(term()) :: term()
|
||||
def media_editor_save_state_label(:dirty), do: translated("Unsaved")
|
||||
def media_editor_save_state_label(:saved), do: translated("Saved")
|
||||
def media_editor_save_state_label(_state), do: translated("Idle")
|
||||
def media_editor_save_state_label(:dirty), do: dgettext("ui", "Unsaved")
|
||||
def media_editor_save_state_label(:saved), do: dgettext("ui", "Saved")
|
||||
def media_editor_save_state_label(_state), do: dgettext("ui", "Idle")
|
||||
|
||||
@spec language_label(term()) :: term()
|
||||
def language_label(code) do
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
]}>
|
||||
<span class="editor-tab-title" data-testid="editor-title"><%= @media_editor.display_title %></span>
|
||||
<%= if @media_editor.dirty? do %>
|
||||
<span class="editor-tab-dirty" title={translated("Unsaved")}>●</span>
|
||||
<span class="editor-tab-dirty" title={dgettext("ui", "Unsaved")}>●</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,7 +26,7 @@
|
||||
phx-target={@myself}
|
||||
>
|
||||
<span class="quick-actions-btn-icon">⚡</span>
|
||||
<span class="quick-actions-btn-label"><%= translated("Quick Actions") %></span>
|
||||
<span class="quick-actions-btn-label"><%= dgettext("ui", "Quick Actions") %></span>
|
||||
</button>
|
||||
|
||||
<%= if @media_editor.quick_actions_open? do %>
|
||||
@@ -40,8 +40,8 @@
|
||||
phx-value-kind="ai_suggestions"
|
||||
>
|
||||
<span class="quick-action-text">
|
||||
<strong><%= translated("AI Suggestions") %></strong>
|
||||
<small><%= translated("Review title, alt text, and caption suggestions") %></small>
|
||||
<strong><%= dgettext("ui", "AI Suggestions") %></strong>
|
||||
<small><%= dgettext("ui", "Review title, alt text, and caption suggestions") %></small>
|
||||
</span>
|
||||
<span class="quick-action-icon">🤖</span>
|
||||
</button>
|
||||
@@ -57,8 +57,8 @@
|
||||
disabled={not @media_editor.can_detect_language?}
|
||||
>
|
||||
<span class="quick-action-text">
|
||||
<strong><%= translated("Detect Language") %></strong>
|
||||
<small><%= translated("Persist the detected language for this media item") %></small>
|
||||
<strong><%= dgettext("ui", "Detect Language") %></strong>
|
||||
<small><%= dgettext("ui", "Persist the detected language for this media item") %></small>
|
||||
</span>
|
||||
<span class="quick-action-icon">🔍</span>
|
||||
</button>
|
||||
@@ -74,8 +74,8 @@
|
||||
disabled={not @media_editor.can_translate?}
|
||||
>
|
||||
<span class="quick-action-text">
|
||||
<strong><%= translated("Translate") %></strong>
|
||||
<small><%= translated("Select a target language for this media item") %></small>
|
||||
<strong><%= dgettext("ui", "Translate") %></strong>
|
||||
<small><%= dgettext("ui", "Select a target language for this media item") %></small>
|
||||
</span>
|
||||
<span class="quick-action-icon">🌍</span>
|
||||
</button>
|
||||
@@ -84,10 +84,10 @@
|
||||
</div>
|
||||
|
||||
<button class="secondary" type="button" phx-click="replace_media_editor_file" phx-target={@myself}>
|
||||
<%= translated("Replace File") %>
|
||||
<%= dgettext("ui", "Replace File") %>
|
||||
</button>
|
||||
<button data-testid="media-save-button" type="button" phx-click="save_media_editor" phx-target={@myself}>
|
||||
<%= translated("Save") %>
|
||||
<%= dgettext("ui", "Save") %>
|
||||
</button>
|
||||
<button
|
||||
class="secondary danger"
|
||||
@@ -96,7 +96,7 @@
|
||||
phx-click="open_overlay"
|
||||
phx-value-kind="confirm_delete"
|
||||
>
|
||||
<%= translated("Delete") %>
|
||||
<%= dgettext("ui", "Delete") %>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -120,58 +120,58 @@
|
||||
<div class="media-details">
|
||||
<form class="media-editor-details-form" data-testid="media-editor-form" phx-change="change_media_editor" phx-target={@myself}>
|
||||
<div class="editor-field">
|
||||
<label><%= translated("File Name") %></label>
|
||||
<label><%= dgettext("ui", "File Name") %></label>
|
||||
<input class="post-editor-input disabled" type="text" value={@media_editor.original_name} disabled />
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("MIME Type") %></label>
|
||||
<label><%= dgettext("ui", "MIME Type") %></label>
|
||||
<input class="post-editor-input disabled" type="text" value={@media_editor.mime_type} disabled />
|
||||
</div>
|
||||
|
||||
<div class="editor-field-row">
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Size") %></label>
|
||||
<label><%= dgettext("ui", "Size") %></label>
|
||||
<input class="post-editor-input disabled" type="text" value={@media_editor.file_size} disabled />
|
||||
</div>
|
||||
|
||||
<%= if @media_editor.dimensions do %>
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Dimensions") %></label>
|
||||
<label><%= dgettext("ui", "Dimensions") %></label>
|
||||
<input class="post-editor-input disabled" type="text" value={@media_editor.dimensions} disabled />
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Title") %></label>
|
||||
<label><%= dgettext("ui", "Title") %></label>
|
||||
<input class="post-editor-input" type="text" name="media_editor[title]" value={@media_editor.form["title"]} />
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Alt Text") %></label>
|
||||
<label><%= dgettext("ui", "Alt Text") %></label>
|
||||
<input class="post-editor-input" type="text" name="media_editor[alt]" value={@media_editor.form["alt"]} />
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Caption") %></label>
|
||||
<label><%= dgettext("ui", "Caption") %></label>
|
||||
<textarea class="post-editor-textarea" name="media_editor[caption]" rows="3"><%= @media_editor.form["caption"] %></textarea>
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Tags") %></label>
|
||||
<label><%= dgettext("ui", "Tags") %></label>
|
||||
<input class="post-editor-input" type="text" name="media_editor[tags]" value={@media_editor.form["tags"]} />
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Author") %></label>
|
||||
<label><%= dgettext("ui", "Author") %></label>
|
||||
<input class="post-editor-input" type="text" name="media_editor[author]" value={@media_editor.form["author"]} />
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Language") %></label>
|
||||
<label><%= dgettext("ui", "Language") %></label>
|
||||
<select class="post-editor-input" name="media_editor[language]">
|
||||
<option value=""><%= translated("None") %></option>
|
||||
<option value=""><%= dgettext("ui", "None") %></option>
|
||||
<%= for language <- @media_editor.languages do %>
|
||||
<option value={language} selected={language == @media_editor.form["language"]}><%= language_label(language) %></option>
|
||||
<% end %>
|
||||
@@ -181,10 +181,10 @@
|
||||
|
||||
<%= if @media_editor.form["language"] not in [nil, ""] do %>
|
||||
<div class="editor-field media-translations-section">
|
||||
<label><%= translated("Translations") %></label>
|
||||
<label><%= dgettext("ui", "Translations") %></label>
|
||||
|
||||
<%= if Enum.empty?(@media_editor.translations) do %>
|
||||
<div class="no-linked-posts"><%= translated("No translations") %></div>
|
||||
<div class="no-linked-posts"><%= dgettext("ui", "No translations") %></div>
|
||||
<% else %>
|
||||
<div class="linked-posts-list">
|
||||
<%= for translation <- @media_editor.translations do %>
|
||||
@@ -199,7 +199,7 @@
|
||||
<%= translation.flag %> <%= language_label(translation.language) %><%= if translation.title, do: " — #{translation.title}" %>
|
||||
</button>
|
||||
<button class="secondary compact" type="button" phx-click="refresh_media_translation" phx-target={@myself} phx-value-language={translation.language}>
|
||||
<%= translated("Refresh") %>
|
||||
<%= dgettext("ui", "Refresh") %>
|
||||
</button>
|
||||
<button class="unlink-btn" type="button" phx-click="delete_media_translation" phx-target={@myself} phx-value-language={translation.language}>×</button>
|
||||
</div>
|
||||
@@ -211,9 +211,9 @@
|
||||
|
||||
<div class="editor-field linked-posts-section">
|
||||
<label>
|
||||
<%= translated("Linked Posts") %>
|
||||
<%= dgettext("ui", "Linked Posts") %>
|
||||
<button class="add-link-btn" type="button" phx-click="toggle_media_post_picker" phx-target={@myself}>
|
||||
<%= translated("Link to Post") %>
|
||||
<%= dgettext("ui", "Link to Post") %>
|
||||
</button>
|
||||
</label>
|
||||
|
||||
@@ -224,14 +224,14 @@
|
||||
type="text"
|
||||
name="media_post_picker[query]"
|
||||
value={@media_editor.post_picker_query}
|
||||
placeholder={translated("Search posts")}
|
||||
placeholder={dgettext("ui", "Search posts")}
|
||||
phx-change="change_media_post_picker"
|
||||
phx-target={@myself}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<%= if Enum.empty?(@media_editor.post_picker_results) do %>
|
||||
<div class="no-posts"><%= translated("No posts to link") %></div>
|
||||
<div class="no-posts"><%= dgettext("ui", "No posts to link") %></div>
|
||||
<% else %>
|
||||
<div class="post-picker-list">
|
||||
<%= for result <- @media_editor.post_picker_results do %>
|
||||
@@ -240,7 +240,7 @@
|
||||
</button>
|
||||
<% end %>
|
||||
<%= if @media_editor.post_picker_overflow_count > 0 do %>
|
||||
<div class="post-picker-more"><%= translated("and %{count} more", %{count: @media_editor.post_picker_overflow_count}) %></div>
|
||||
<div class="post-picker-more"><%= dgettext("ui", "and %{count} more", count: @media_editor.post_picker_overflow_count) %></div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -248,7 +248,7 @@
|
||||
<% end %>
|
||||
|
||||
<%= if Enum.empty?(@media_editor.linked_posts) do %>
|
||||
<div class="no-linked-posts"><%= translated("Not linked to any posts") %></div>
|
||||
<div class="no-linked-posts"><%= dgettext("ui", "Not linked to any posts") %></div>
|
||||
<% else %>
|
||||
<div class="linked-posts-list">
|
||||
<%= for linked_post <- @media_editor.linked_posts do %>
|
||||
@@ -277,27 +277,27 @@
|
||||
<div class="translation-modal-backdrop">
|
||||
<div class="translation-modal">
|
||||
<div class="translation-modal-header">
|
||||
<h2><%= translated("Edit Translation") %></h2>
|
||||
<h2><%= dgettext("ui", "Edit Translation") %></h2>
|
||||
<button class="translation-modal-close" type="button" phx-click="close_media_translation_editor" phx-target={@myself}>×</button>
|
||||
</div>
|
||||
<form class="translation-modal-body" phx-change="change_media_translation" phx-target={@myself}>
|
||||
<input type="hidden" name="media_translation[language]" value={@media_editor.editing_translation["language"]} />
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Title") %></label>
|
||||
<label><%= dgettext("ui", "Title") %></label>
|
||||
<input class="post-editor-input" type="text" name="media_translation[title]" value={@media_editor.editing_translation["title"]} />
|
||||
</div>
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Alt Text") %></label>
|
||||
<label><%= dgettext("ui", "Alt Text") %></label>
|
||||
<input class="post-editor-input" type="text" name="media_translation[alt]" value={@media_editor.editing_translation["alt"]} />
|
||||
</div>
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Caption") %></label>
|
||||
<label><%= dgettext("ui", "Caption") %></label>
|
||||
<textarea class="post-editor-textarea" name="media_translation[caption]" rows="3"><%= @media_editor.editing_translation["caption"] %></textarea>
|
||||
</div>
|
||||
</form>
|
||||
<div class="translation-modal-footer">
|
||||
<button class="secondary" type="button" phx-click="close_media_translation_editor" phx-target={@myself}><%= translated("Cancel") %></button>
|
||||
<button type="button" phx-click="save_media_translation" phx-target={@myself}><%= translated("Save") %></button>
|
||||
<button class="secondary" type="button" phx-click="close_media_translation_editor" phx-target={@myself}><%= dgettext("ui", "Cancel") %></button>
|
||||
<button type="button" phx-click="save_media_translation" phx-target={@myself}><%= dgettext("ui", "Save") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,8 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
|
||||
|
||||
use Phoenix.LiveComponent
|
||||
|
||||
alias BDS.Desktop.ShellData
|
||||
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
alias BDS.Desktop.ShellLive.MenuEditor.{
|
||||
DraftManagement,
|
||||
@@ -215,7 +216,7 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
|
||||
Enum.map(state.items, &TreeOps.persisted_item/1)
|
||||
)
|
||||
|
||||
notify_output(translated("menuEditor.tabTitle"), translated("menuEditor.saved"), "info")
|
||||
notify_output(dgettext("ui", "Blog Menu"), dgettext("ui", "Blog menu saved"), "info")
|
||||
socket |> build_data()
|
||||
end
|
||||
|
||||
@@ -236,8 +237,8 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
|
||||
|
||||
tab_meta =
|
||||
Map.put(socket.assigns.tab_meta, {:menu_editor, tab_id}, %{
|
||||
title: translated("menuEditor.tabTitle"),
|
||||
subtitle: translated("menuEditor.description")
|
||||
title: dgettext("ui", "Blog Menu"),
|
||||
subtitle: dgettext("ui", "Manage the central blog navigation outline and save it to meta/menu.opml.")
|
||||
})
|
||||
|
||||
socket
|
||||
@@ -284,7 +285,7 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
|
||||
phx-target={@myself}
|
||||
style={"--menu-editor-depth: #{@depth};"}
|
||||
>
|
||||
<span class="menu-editor-row-handle" data-menu-drag-handle="true" title={translated("menuEditor.dragHandle")}>⋮⋮</span>
|
||||
<span class="menu-editor-row-handle" data-menu-drag-handle="true" title={dgettext("ui", "Drag menu item")}>⋮⋮</span>
|
||||
<span class="menu-editor-row-kind" title={kind_label(item.kind)} aria-label={kind_label(item.kind)}>
|
||||
<.kind_icon kind={item.kind} />
|
||||
</span>
|
||||
@@ -317,16 +318,16 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
|
||||
<div class="menu-editor-inline-actions">
|
||||
<%= if @menu_editor.draft.type == :page do %>
|
||||
<button class="menu-editor-inline-action" data-testid="menu-editor-create-submenu" type="submit">
|
||||
<%= translated("menuEditor.addSubmenu") %>
|
||||
<%= dgettext("ui", "Add Submenu") %>
|
||||
</button>
|
||||
<% else %>
|
||||
<button class="menu-editor-inline-action" type="submit">
|
||||
<%= translated("menuEditor.addCategoryArchive") %>
|
||||
<%= dgettext("ui", "Add Category Archive") %>
|
||||
</button>
|
||||
<% end %>
|
||||
|
||||
<button class="menu-editor-inline-action" type="button" phx-click="cancel_menu_editor_entry" phx-target={@myself}>
|
||||
<%= translated("Cancel") %>
|
||||
<%= dgettext("ui", "Cancel") %>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -334,7 +335,7 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
|
||||
<%= if @menu_editor.draft.type == :page do %>
|
||||
<div class="menu-editor-picker-list">
|
||||
<%= if @menu_editor.filtered_pages == [] do %>
|
||||
<div class="menu-editor-picker-state"><%= translated("menuEditor.pagePicker.empty") %></div>
|
||||
<div class="menu-editor-picker-state"><%= dgettext("ui", "No matching pages found.") %></div>
|
||||
<% else %>
|
||||
<%= for post <- @menu_editor.filtered_pages do %>
|
||||
<button
|
||||
@@ -353,7 +354,7 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
|
||||
<% else %>
|
||||
<div class="menu-editor-picker-list">
|
||||
<%= if @menu_editor.filtered_categories == [] do %>
|
||||
<div class="menu-editor-picker-state"><%= translated("menuEditor.categoryPicker.empty") %></div>
|
||||
<div class="menu-editor-picker-state"><%= dgettext("ui", "No matching categories found.") %></div>
|
||||
<% else %>
|
||||
<%= for category <- @menu_editor.filtered_categories do %>
|
||||
<button
|
||||
@@ -406,9 +407,6 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
|
||||
"""
|
||||
end
|
||||
|
||||
@spec translated(term(), term()) :: term()
|
||||
def translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
|
||||
@spec row_label(term(), term()) :: term()
|
||||
def row_label(item, category_titles) do
|
||||
@@ -420,24 +418,24 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
|
||||
end
|
||||
|
||||
@spec kind_label(term()) :: term()
|
||||
def kind_label(:home), do: translated("menuEditor.type.home")
|
||||
def kind_label(:page), do: translated("menuEditor.type.page")
|
||||
def kind_label(:category_archive), do: translated("menuEditor.type.categoryArchive")
|
||||
def kind_label(:submenu), do: translated("menuEditor.type.submenu")
|
||||
def kind_label(:home), do: dgettext("ui", "Home")
|
||||
def kind_label(:page), do: dgettext("ui", "Page")
|
||||
def kind_label(:category_archive), do: dgettext("ui", "Category Archive")
|
||||
def kind_label(:submenu), do: dgettext("ui", "Submenu")
|
||||
|
||||
defdelegate draft_item?(menu_editor, item_id), to: TreePredicates
|
||||
|
||||
@spec editing_title(term()) :: term()
|
||||
def editing_title(%{draft: %{type: :category}}), do: translated("menuEditor.addCategoryArchive")
|
||||
def editing_title(_menu_editor), do: translated("menuEditor.pagePicker.title")
|
||||
def editing_title(%{draft: %{type: :category}}), do: dgettext("ui", "Add Category Archive")
|
||||
def editing_title(_menu_editor), do: dgettext("ui", "Select Page")
|
||||
|
||||
@spec editing_hint(term()) :: term()
|
||||
def editing_hint(%{draft: %{type: :category}}), do: translated("menuEditor.categoryPicker.hint")
|
||||
def editing_hint(_menu_editor), do: translated("menuEditor.createHint")
|
||||
def editing_hint(%{draft: %{type: :category}}), do: dgettext("ui", "Select an existing category or press Enter to create a new archive entry")
|
||||
def editing_hint(_menu_editor), do: dgettext("ui", "Select a page below or press Enter to create a submenu")
|
||||
|
||||
@spec editing_placeholder(term()) :: term()
|
||||
def editing_placeholder(%{draft: %{type: :category}}),
|
||||
do: translated("menuEditor.newCategoryPlaceholder")
|
||||
do: dgettext("ui", "Type a category name")
|
||||
|
||||
def editing_placeholder(_menu_editor), do: translated("menuEditor.newEntryPlaceholder")
|
||||
def editing_placeholder(_menu_editor), do: dgettext("ui", "Type a page title or submenu label")
|
||||
end
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
defmodule BDS.Desktop.ShellLive.MenuEditor.DraftManagement do
|
||||
@moduledoc false
|
||||
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Metadata
|
||||
alias BDS.Desktop.ShellLive.MenuEditor.PageCategory
|
||||
alias BDS.Desktop.ShellLive.MenuEditor.TreeOps
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec current_draft(term()) :: term()
|
||||
def current_draft(assigns), do: Map.get(assigns.menu_editor_state || %{}, :draft)
|
||||
@@ -14,7 +14,7 @@ defmodule BDS.Desktop.ShellLive.MenuEditor.DraftManagement do
|
||||
item = %{
|
||||
item_id: Ecto.UUID.generate(),
|
||||
kind: :page,
|
||||
label: translated("menuEditor.newPage"),
|
||||
label: dgettext("ui", "New Page"),
|
||||
slug: nil,
|
||||
children: [],
|
||||
is_home: false
|
||||
@@ -57,7 +57,7 @@ defmodule BDS.Desktop.ShellLive.MenuEditor.DraftManagement do
|
||||
def finalize_submenu_draft(%{draft: %{item_id: item_id, query: query}} = state) do
|
||||
label =
|
||||
if(String.trim(query) == "",
|
||||
do: translated("menuEditor.newSubmenu"),
|
||||
do: dgettext("ui", "New Submenu"),
|
||||
else: String.trim(query)
|
||||
)
|
||||
|
||||
@@ -144,7 +144,4 @@ defmodule BDS.Desktop.ShellLive.MenuEditor.DraftManagement do
|
||||
|
||||
update_state_fun.(socket, &assign_category_to_draft(&1, category))
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -3,9 +3,9 @@ defmodule BDS.Desktop.ShellLive.MenuEditor.State do
|
||||
|
||||
use Phoenix.Component
|
||||
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Menu
|
||||
alias BDS.Desktop.ShellLive.MenuEditor.{PageCategory, TreeOps, TreePredicates}
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec ensure_state(term()) :: term()
|
||||
def ensure_state(assigns) do
|
||||
@@ -30,8 +30,8 @@ defmodule BDS.Desktop.ShellLive.MenuEditor.State do
|
||||
draft_query = Map.get(draft || %{}, :query, "")
|
||||
|
||||
%{
|
||||
title: translated("menuEditor.title"),
|
||||
description: translated("menuEditor.description"),
|
||||
title: dgettext("ui", "Blog Menu Editor"),
|
||||
description: dgettext("ui", "Manage the central blog navigation outline and save it to meta/menu.opml."),
|
||||
items: state.items,
|
||||
selected_id: state.selected_id,
|
||||
draft: draft,
|
||||
@@ -66,8 +66,8 @@ defmodule BDS.Desktop.ShellLive.MenuEditor.State do
|
||||
|
||||
socket
|
||||
|> append_output.(
|
||||
translated("menuEditor.tabTitle"),
|
||||
translated("menuEditor.saved"),
|
||||
dgettext("ui", "Blog Menu"),
|
||||
dgettext("ui", "Blog menu saved"),
|
||||
nil,
|
||||
"info"
|
||||
)
|
||||
@@ -94,7 +94,4 @@ defmodule BDS.Desktop.ShellLive.MenuEditor.State do
|
||||
draft: nil
|
||||
}
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -9,41 +9,41 @@
|
||||
<div class="menu-editor-main">
|
||||
<div class="menu-editor-tree-wrap">
|
||||
<div class="menu-editor-toolbar" data-testid="menu-editor-toolbar" role="toolbar" aria-label={@menu_editor.title}>
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="add-entry" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="add-entry" phx-target={@myself} title={translated("menuEditor.addEntry")}>
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="add-entry" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="add-entry" phx-target={@myself} title={dgettext("ui", "menuEditor.addEntry")}>
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M7 2h2v5h5v2H9v5H7V9H2V7h5V2z" /></svg>
|
||||
</button>
|
||||
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="save" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="save" phx-target={@myself} title={translated("menuEditor.save")}>
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="save" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="save" phx-target={@myself} title={dgettext("ui", "menuEditor.save")}>
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M2 2h9l3 3v9H2V2zm2 1v3h6V3H4zm0 9h8V7H4v5z" /></svg>
|
||||
</button>
|
||||
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="add-category-archive" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="add-category-archive" phx-target={@myself} title={translated("menuEditor.addCategoryArchive")}>
|
||||
<span aria-hidden="true"><%= translated("menuEditor.addCategoryArchiveShort") %></span>
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="add-category-archive" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="add-category-archive" phx-target={@myself} title={dgettext("ui", "menuEditor.addCategoryArchive")}>
|
||||
<span aria-hidden="true"><%= dgettext("ui", "menuEditor.addCategoryArchiveShort") %></span>
|
||||
</button>
|
||||
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="move-up" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="move-up" phx-target={@myself} title={translated("menuEditor.moveUp")} disabled={not @menu_editor.can_move_up?}>
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="move-up" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="move-up" phx-target={@myself} title={dgettext("ui", "menuEditor.moveUp")} disabled={not @menu_editor.can_move_up?}>
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M8 3l4 4H9v6H7V7H4l4-4z" /></svg>
|
||||
</button>
|
||||
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="move-down" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="move-down" phx-target={@myself} title={translated("menuEditor.moveDown")} disabled={not @menu_editor.can_move_down?}>
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="move-down" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="move-down" phx-target={@myself} title={dgettext("ui", "menuEditor.moveDown")} disabled={not @menu_editor.can_move_down?}>
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M7 3h2v6h3l-4 4-4-4h3V3z" /></svg>
|
||||
</button>
|
||||
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="indent" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="indent" phx-target={@myself} title={translated("menuEditor.indent")} disabled={not @menu_editor.can_indent?}>
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="indent" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="indent" phx-target={@myself} title={dgettext("ui", "menuEditor.indent")} disabled={not @menu_editor.can_indent?}>
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M2 4h8v2H2V4zm0 3h4v2H2V7zm0 3h8v2H2v-2zm6-1 3 2-3 2V9z" /></svg>
|
||||
</button>
|
||||
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="unindent" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="unindent" phx-target={@myself} title={translated("menuEditor.unindent")} disabled={not @menu_editor.can_unindent?}>
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="unindent" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="unindent" phx-target={@myself} title={dgettext("ui", "menuEditor.unindent")} disabled={not @menu_editor.can_unindent?}>
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M2 4h8v2H2V4zm0 3h4v2H2V7zm0 3h8v2H2v-2zm3-1-3 2 3 2V9z" /></svg>
|
||||
</button>
|
||||
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="delete" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="delete" phx-target={@myself} title={translated("menuEditor.delete")} disabled={not @menu_editor.can_delete?}>
|
||||
<button class="menu-editor-tool" data-testid="menu-editor-toolbar-button" data-action="delete" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="delete" phx-target={@myself} title={dgettext("ui", "menuEditor.delete")} disabled={not @menu_editor.can_delete?}>
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M6 2h4l1 1h3v2H2V3h3l1-1zm-1 4h2v6H5V6zm4 0h2v6H9V6z" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<%= if @menu_editor.items == [] do %>
|
||||
<div class="menu-editor-empty"><%= translated("menuEditor.empty") %></div>
|
||||
<div class="menu-editor-empty"><%= dgettext("ui", "menuEditor.empty") %></div>
|
||||
<% else %>
|
||||
<div id="menu-editor-tree-shell" class="menu-editor-tree-shell" phx-hook="MenuEditorTree">
|
||||
<ul class="menu-editor-tree-level">
|
||||
|
||||
@@ -6,9 +6,9 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
import Ecto.Query
|
||||
|
||||
alias BDS.{Embeddings, Generation, Git, Posts, Repo}
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.MapUtils
|
||||
alias BDS.Settings.Setting
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
embed_templates("misc_editor_html/*")
|
||||
|
||||
@@ -87,13 +87,13 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|
||||
case Generation.apply_validation(project_id, report) do
|
||||
{:ok, result} ->
|
||||
notify_output(translated("Site Validation"), translated("Validation changes applied"), inspect(result))
|
||||
notify_output(dgettext("ui", "Site Validation"), dgettext("ui", "Validation changes applied"), inspect(result))
|
||||
notify_command("validate_site")
|
||||
{:noreply, socket}
|
||||
end
|
||||
rescue
|
||||
error ->
|
||||
notify_output(translated("Site Validation"), inspect(error), nil, "error")
|
||||
notify_output(dgettext("ui", "Site Validation"), inspect(error), nil, "error")
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@@ -107,19 +107,19 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
{:ok, result} = Posts.fix_invalid_translations(report)
|
||||
|
||||
notify_output(
|
||||
translated("Translation Validation"),
|
||||
translated("translationValidation.toast.fixSuccess", %{
|
||||
dgettext("ui", "Translation Validation"),
|
||||
dgettext("ui", "Deleted %{dbRows} DB rows and %{files} files, flushed %{flushed} translations to disk",
|
||||
dbRows: result.deleted_database_rows,
|
||||
files: result.deleted_files,
|
||||
flushed: result.flushed_translations
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
notify_command("validate_translations")
|
||||
{:noreply, socket}
|
||||
rescue
|
||||
error ->
|
||||
notify_output(translated("Translation Validation"), inspect(error), nil, "error")
|
||||
notify_output(dgettext("ui", "Translation Validation"), inspect(error), nil, "error")
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@@ -162,13 +162,13 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|
||||
next_payload = Map.put(payload, :pairs, next_pairs)
|
||||
notify_tab_meta(tab_type, tab_id, %{payload: next_payload})
|
||||
notify_output(translated("Find Duplicates"), translated("Pair dismissed"))
|
||||
notify_output(dgettext("ui", "Find Duplicates"), dgettext("ui", "Pair dismissed"))
|
||||
|
||||
selected = MapSet.delete(socket.assigns.selected_pairs, pair_id)
|
||||
{:noreply, assign(socket, :selected_pairs, selected) |> build_data()}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(translated("Find Duplicates"), inspect(reason), nil, "error")
|
||||
notify_output(dgettext("ui", "Find Duplicates"), inspect(reason), nil, "error")
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
@@ -193,12 +193,12 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|
||||
next_payload = Map.put(payload, :pairs, next_pairs)
|
||||
notify_tab_meta(tab_type, tab_id, %{payload: next_payload})
|
||||
notify_output(translated("Find Duplicates"), translated("Selected pairs dismissed"))
|
||||
notify_output(dgettext("ui", "Find Duplicates"), dgettext("ui", "Selected pairs dismissed"))
|
||||
|
||||
{:noreply, assign(socket, :selected_pairs, MapSet.new()) |> build_data()}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(translated("Find Duplicates"), inspect(reason), nil, "error")
|
||||
notify_output(dgettext("ui", "Find Duplicates"), inspect(reason), nil, "error")
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
@@ -210,7 +210,7 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, message} ->
|
||||
notify_output(translated("Metadata Diff"), message, nil, "error")
|
||||
notify_output(dgettext("ui", "Metadata Diff"), message, nil, "error")
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
@@ -222,7 +222,7 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
{:noreply, socket}
|
||||
|
||||
{:error, message} ->
|
||||
notify_output(translated("Metadata Diff"), message, nil, "error")
|
||||
notify_output(dgettext("ui", "Metadata Diff"), message, nil, "error")
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
@@ -248,9 +248,6 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|
||||
# ── Public helper functions (used by template) ─────────────────────────────
|
||||
|
||||
@spec translated(String.t(), map()) :: String.t()
|
||||
def translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
|
||||
@spec misc_class(atom()) :: String.t()
|
||||
def misc_class(:site_validation), do: "site-validation-view"
|
||||
@@ -273,16 +270,16 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
def translation_issue_label(issue) do
|
||||
case issue_value(issue, :issue) do
|
||||
"same-language-as-canonical" ->
|
||||
translated("translationValidation.issue.sameLanguage")
|
||||
dgettext("ui", "Translation language matches canonical post language")
|
||||
|
||||
"do-not-translate-has-translations" ->
|
||||
translated("translationValidation.issue.doNotTranslate")
|
||||
dgettext("ui", "Post is marked as do-not-translate but has translations")
|
||||
|
||||
"content-in-database" ->
|
||||
translated("translationValidation.issue.contentInDatabase")
|
||||
dgettext("ui", "Published translation has content stuck in DB instead of filesystem")
|
||||
|
||||
_other ->
|
||||
translated("translationValidation.issue.missingSource")
|
||||
dgettext("ui", "Translation points to a missing source post")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -294,10 +291,10 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
if canonical_language in [nil, ""] do
|
||||
translation_language
|
||||
else
|
||||
translated("translationValidation.languagesWithCanonical", %{
|
||||
dgettext("ui", "%{canonical} = %{translation}",
|
||||
canonical: canonical_language,
|
||||
translation: translation_language
|
||||
})
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -373,7 +370,7 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|
||||
%{
|
||||
kind: :site_validation,
|
||||
title: Map.get(meta, :title, translated("Site Validation")),
|
||||
title: Map.get(meta, :title, dgettext("ui", "Site Validation")),
|
||||
subtitle: Map.get(meta, :subtitle, ""),
|
||||
summary: %{
|
||||
expected: Map.get(summary, :expected_count, 0),
|
||||
@@ -409,7 +406,7 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|
||||
%{
|
||||
kind: :metadata_diff,
|
||||
title: Map.get(meta, :title, translated("Metadata Diff")),
|
||||
title: Map.get(meta, :title, dgettext("ui", "Metadata Diff")),
|
||||
subtitle: Map.get(meta, :subtitle, ""),
|
||||
summary: Map.get(payload, :summary, %{}),
|
||||
tabs:
|
||||
@@ -420,7 +417,7 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
field_summaries: field_summaries(current_tab.items),
|
||||
items: filtered_items,
|
||||
orphan_files: if(is_nil(active_field), do: current_tab.orphan_files, else: []),
|
||||
empty_message: translated("No items")
|
||||
empty_message: dgettext("ui", "No items")
|
||||
}
|
||||
end
|
||||
|
||||
@@ -429,16 +426,16 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|
||||
%{
|
||||
kind: :translation_validation,
|
||||
title: Map.get(meta, :title, translated("Translation Validation")),
|
||||
title: Map.get(meta, :title, dgettext("ui", "Translation Validation")),
|
||||
subtitle: Map.get(meta, :subtitle, ""),
|
||||
summary: %{},
|
||||
summary_text:
|
||||
translated("translationValidation.summary", %{
|
||||
dgettext("ui", "Checked DB rows: %{dbRows} · Checked files: %{files} · Invalid DB rows: %{invalidDb} · Invalid files: %{invalidFiles}",
|
||||
dbRows: report.checked_database_row_count,
|
||||
files: report.checked_filesystem_file_count,
|
||||
invalidDb: length(report.invalid_database_rows),
|
||||
invalidFiles: length(report.invalid_filesystem_files)
|
||||
}),
|
||||
),
|
||||
invalid_database_rows: report.invalid_database_rows,
|
||||
invalid_filesystem_files: report.invalid_filesystem_files,
|
||||
can_fix?: report.invalid_database_rows != [] or report.invalid_filesystem_files != []
|
||||
@@ -448,7 +445,7 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
defp build_duplicates(assigns, meta, payload) do
|
||||
%{
|
||||
kind: :find_duplicates,
|
||||
title: Map.get(meta, :title, translated("Find Duplicates")),
|
||||
title: Map.get(meta, :title, dgettext("ui", "Find Duplicates")),
|
||||
subtitle: Map.get(meta, :subtitle, ""),
|
||||
summary: Map.get(payload, :summary, %{}),
|
||||
pairs: Map.get(payload, :pairs, []),
|
||||
@@ -505,14 +502,14 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|
||||
%{
|
||||
kind: :git_diff,
|
||||
title: Map.get(meta, :title, translated("Git Diff")),
|
||||
title: Map.get(meta, :title, dgettext("ui", "Git Diff")),
|
||||
subtitle: Map.get(meta, :subtitle, ""),
|
||||
summary: %{},
|
||||
files: files,
|
||||
selected_file_path: diff.file_path,
|
||||
active_diff: Map.put(diff, :language, git_diff_language(diff.file_path)),
|
||||
preferences: preferences,
|
||||
empty_message: error_message || translated("No unstaged changes")
|
||||
empty_message: error_message || dgettext("ui", "No unstaged changes")
|
||||
}
|
||||
end
|
||||
|
||||
@@ -534,10 +531,10 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|
||||
cond do
|
||||
not metadata_diff_repairable_tab?(active_tab) ->
|
||||
{:error, translated("No repair action available")}
|
||||
{:error, dgettext("ui", "No repair action available")}
|
||||
|
||||
repair_items == [] ->
|
||||
{:error, translated("No metadata diff items selected")}
|
||||
{:error, dgettext("ui", "No metadata diff items selected")}
|
||||
|
||||
true ->
|
||||
{:ok,
|
||||
@@ -566,7 +563,7 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
|> Enum.map(&%{"file_path" => &1.file_path})
|
||||
|
||||
if selected_orphans == [] do
|
||||
{:error, translated("No orphan files selected")}
|
||||
{:error, dgettext("ui", "No orphan files selected")}
|
||||
else
|
||||
{:ok, %{"tab" => active_tab, "orphans" => selected_orphans}}
|
||||
end
|
||||
@@ -623,7 +620,7 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
defp empty_metadata_diff_tab do
|
||||
%{
|
||||
id: "posts",
|
||||
label: translated("Posts"),
|
||||
label: dgettext("ui", "Posts"),
|
||||
items: [],
|
||||
orphan_files: [],
|
||||
diff_count: 0,
|
||||
@@ -692,17 +689,17 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
MapUtils.attr(item, :meta_label) || entity_id
|
||||
end
|
||||
|
||||
defp metadata_diff_item_type_label("post"), do: translated("Post")
|
||||
defp metadata_diff_item_type_label("post_translation"), do: translated("Translations")
|
||||
defp metadata_diff_item_type_label("media"), do: translated("Media")
|
||||
defp metadata_diff_item_type_label("media_translation"), do: translated("Translations")
|
||||
defp metadata_diff_item_type_label("script"), do: translated("Script")
|
||||
defp metadata_diff_item_type_label("template"), do: translated("Template")
|
||||
defp metadata_diff_item_type_label("project"), do: translated("Project")
|
||||
defp metadata_diff_item_type_label("publishing"), do: translated("Publishing")
|
||||
defp metadata_diff_item_type_label("categories"), do: translated("Categories")
|
||||
defp metadata_diff_item_type_label("category_meta"), do: translated("Categories")
|
||||
defp metadata_diff_item_type_label("embedding"), do: translated("Embeddings")
|
||||
defp metadata_diff_item_type_label("post"), do: dgettext("ui", "Post")
|
||||
defp metadata_diff_item_type_label("post_translation"), do: dgettext("ui", "Translations")
|
||||
defp metadata_diff_item_type_label("media"), do: dgettext("ui", "Media")
|
||||
defp metadata_diff_item_type_label("media_translation"), do: dgettext("ui", "Translations")
|
||||
defp metadata_diff_item_type_label("script"), do: dgettext("ui", "Script")
|
||||
defp metadata_diff_item_type_label("template"), do: dgettext("ui", "Template")
|
||||
defp metadata_diff_item_type_label("project"), do: dgettext("ui", "Project")
|
||||
defp metadata_diff_item_type_label("publishing"), do: dgettext("ui", "Publishing")
|
||||
defp metadata_diff_item_type_label("categories"), do: dgettext("ui", "Categories")
|
||||
defp metadata_diff_item_type_label("category_meta"), do: dgettext("ui", "Categories")
|
||||
defp metadata_diff_item_type_label("embedding"), do: dgettext("ui", "Embeddings")
|
||||
|
||||
defp metadata_diff_item_type_label(entity_type),
|
||||
do: entity_type |> String.replace("_", " ") |> String.capitalize()
|
||||
@@ -720,12 +717,12 @@ defmodule BDS.Desktop.ShellLive.MiscEditor do
|
||||
defp metadata_diff_tab_id("embedding"), do: "embeddings"
|
||||
defp metadata_diff_tab_id(_entity_type), do: "project"
|
||||
|
||||
defp metadata_diff_tab_label("posts"), do: translated("Posts")
|
||||
defp metadata_diff_tab_label("media"), do: translated("Media")
|
||||
defp metadata_diff_tab_label("scripts"), do: translated("Scripts")
|
||||
defp metadata_diff_tab_label("templates"), do: translated("Templates")
|
||||
defp metadata_diff_tab_label("project"), do: translated("Project")
|
||||
defp metadata_diff_tab_label("embeddings"), do: translated("Embeddings")
|
||||
defp metadata_diff_tab_label("posts"), do: dgettext("ui", "Posts")
|
||||
defp metadata_diff_tab_label("media"), do: dgettext("ui", "Media")
|
||||
defp metadata_diff_tab_label("scripts"), do: dgettext("ui", "Scripts")
|
||||
defp metadata_diff_tab_label("templates"), do: dgettext("ui", "Templates")
|
||||
defp metadata_diff_tab_label("project"), do: dgettext("ui", "Project")
|
||||
defp metadata_diff_tab_label("embeddings"), do: dgettext("ui", "Embeddings")
|
||||
|
||||
defp metadata_diff_tab_label(tab_id),
|
||||
do: tab_id |> String.replace("_", " ") |> String.capitalize()
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<p><%= @misc_editor.subtitle %></p>
|
||||
</div>
|
||||
<div class="misc-editor-actions">
|
||||
<button class="secondary" type="button" phx-click="rerun_misc_editor" phx-target={@myself}><%= translated("Refresh") %></button>
|
||||
<button class="secondary" type="button" phx-click="rerun_misc_editor" phx-target={@myself}><%= dgettext("ui", "Refresh") %></button>
|
||||
<%= if @misc_editor.kind == :site_validation do %>
|
||||
<button class="primary" type="button" phx-click="apply_site_validation" phx-target={@myself} disabled={Enum.empty?(@misc_editor.missing_url_paths) and Enum.empty?(@misc_editor.extra_url_paths) and Enum.empty?(@misc_editor.updated_post_url_paths)}><%= translated("Apply") %></button>
|
||||
<button class="primary" type="button" phx-click="apply_site_validation" phx-target={@myself} disabled={Enum.empty?(@misc_editor.missing_url_paths) and Enum.empty?(@misc_editor.extra_url_paths) and Enum.empty?(@misc_editor.updated_post_url_paths)}><%= dgettext("ui", "Apply") %></button>
|
||||
<% end %>
|
||||
<%= if @misc_editor.kind == :find_duplicates do %>
|
||||
<button class="secondary" type="button" phx-click="dismiss_selected_duplicates" phx-target={@myself} disabled={MapSet.size(@misc_editor.selected_pairs) == 0}><%= translated("Dismiss Checked") %></button>
|
||||
<button class="secondary" type="button" phx-click="dismiss_selected_duplicates" phx-target={@myself} disabled={MapSet.size(@misc_editor.selected_pairs) == 0}><%= dgettext("ui", "Dismiss Checked") %></button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -25,9 +25,9 @@
|
||||
<%= case @misc_editor.kind do %>
|
||||
<% :site_validation -> %>
|
||||
<div class="misc-columns">
|
||||
<section class="misc-card"><h3><%= translated("Missing URLs") %></h3><%= if Enum.empty?(@misc_editor.missing_url_paths) do %><p><%= translated("None found") %></p><% end %><ul><%= for path <- @misc_editor.missing_url_paths do %><li><%= path %></li><% end %></ul></section>
|
||||
<section class="misc-card"><h3><%= translated("Extra URLs") %></h3><%= if Enum.empty?(@misc_editor.extra_url_paths) do %><p><%= translated("None found") %></p><% end %><ul><%= for path <- @misc_editor.extra_url_paths do %><li><%= path %></li><% end %></ul></section>
|
||||
<section class="misc-card"><h3><%= translated("Updated URLs") %></h3><%= if Enum.empty?(@misc_editor.updated_post_url_paths) do %><p><%= translated("None found") %></p><% end %><ul><%= for path <- @misc_editor.updated_post_url_paths do %><li><%= path %></li><% end %></ul></section>
|
||||
<section class="misc-card"><h3><%= dgettext("ui", "Missing URLs") %></h3><%= if Enum.empty?(@misc_editor.missing_url_paths) do %><p><%= dgettext("ui", "None found") %></p><% end %><ul><%= for path <- @misc_editor.missing_url_paths do %><li><%= path %></li><% end %></ul></section>
|
||||
<section class="misc-card"><h3><%= dgettext("ui", "Extra URLs") %></h3><%= if Enum.empty?(@misc_editor.extra_url_paths) do %><p><%= dgettext("ui", "None found") %></p><% end %><ul><%= for path <- @misc_editor.extra_url_paths do %><li><%= path %></li><% end %></ul></section>
|
||||
<section class="misc-card"><h3><%= dgettext("ui", "Updated URLs") %></h3><%= if Enum.empty?(@misc_editor.updated_post_url_paths) do %><p><%= dgettext("ui", "None found") %></p><% end %><ul><%= for path <- @misc_editor.updated_post_url_paths do %><li><%= path %></li><% end %></ul></section>
|
||||
</div>
|
||||
|
||||
<% :metadata_diff -> %>
|
||||
@@ -81,7 +81,7 @@
|
||||
phx-value-direction="db_to_file"
|
||||
phx-value-field={field.field_name}
|
||||
>
|
||||
<%= translated("DB to File") %>
|
||||
<%= dgettext("ui", "DB to File") %>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@@ -95,7 +95,7 @@
|
||||
phx-value-direction="file_to_db"
|
||||
phx-value-field={field.field_name}
|
||||
>
|
||||
<%= translated("File to DB") %>
|
||||
<%= dgettext("ui", "File to DB") %>
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -130,7 +130,7 @@
|
||||
<span><%= diff.db_value %></span>
|
||||
</div>
|
||||
<div class="diff-field-value file-value">
|
||||
<span class="diff-source-label"><%= translated("File") %></span>
|
||||
<span class="diff-source-label"><%= dgettext("ui", "File") %></span>
|
||||
<span><%= diff.file_value %></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -145,7 +145,7 @@
|
||||
<%= if @misc_editor.active_field == nil and @misc_editor.orphan_files != [] do %>
|
||||
<section class="orphan-files-section" data-testid="metadata-diff-orphans">
|
||||
<div class="orphan-files-header">
|
||||
<h3><%= translated("Orphan Files") %></h3>
|
||||
<h3><%= dgettext("ui", "Orphan Files") %></h3>
|
||||
<div class="orphan-files-actions">
|
||||
<span class="misc-summary-pill"><%= length(@misc_editor.orphan_files) %></span>
|
||||
<button
|
||||
@@ -155,7 +155,7 @@
|
||||
phx-click="import_metadata_diff_orphans"
|
||||
phx-target={@myself}
|
||||
>
|
||||
<%= translated("Import") %>
|
||||
<%= dgettext("ui", "Import") %>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -165,13 +165,13 @@
|
||||
<header class="diff-item-header">
|
||||
<div>
|
||||
<strong><%= orphan.slug %></strong>
|
||||
<div class="diff-item-meta"><%= translated("Orphan Files") %></div>
|
||||
<div class="diff-item-meta"><%= dgettext("ui", "Orphan Files") %></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="diff-item-fields">
|
||||
<div class="diff-field-row">
|
||||
<div class="diff-field-name"><%= translated("Path") %></div>
|
||||
<div class="diff-field-name"><%= dgettext("ui", "Path") %></div>
|
||||
<div class="diff-field-values">
|
||||
<div class="diff-field-value file-value orphan-path"><span><%= orphan.file_path %></span></div>
|
||||
</div>
|
||||
@@ -192,30 +192,30 @@
|
||||
</section>
|
||||
|
||||
<section class="translation-validation-section">
|
||||
<h3><%= translated("translationValidation.databaseTitle") %></h3>
|
||||
<h3><%= dgettext("ui", "translationValidation.databaseTitle") %></h3>
|
||||
|
||||
<%= if @misc_editor.invalid_database_rows == [] do %>
|
||||
<p class="translation-validation-empty"><%= translated("translationValidation.noneDatabase") %></p>
|
||||
<p class="translation-validation-empty"><%= dgettext("ui", "translationValidation.noneDatabase") %></p>
|
||||
<% else %>
|
||||
<div class="translation-validation-list">
|
||||
<%= for issue <- @misc_editor.invalid_database_rows do %>
|
||||
<article class="translation-validation-card translation-validation-card-db" data-testid="translation-validation-card">
|
||||
<p class="translation-validation-card-title"><%= translation_issue_label(issue) %></p>
|
||||
<dl class="translation-validation-card-meta">
|
||||
<dt><%= translated("translationValidation.field.translationFor") %></dt>
|
||||
<dt><%= dgettext("ui", "translationValidation.field.translationFor") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :translation_for) %></dd>
|
||||
<%= if translation_issue_value(issue, :translation_id) do %>
|
||||
<dt><%= translated("translationValidation.field.translationId") %></dt>
|
||||
<dt><%= dgettext("ui", "translationValidation.field.translationId") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :translation_id) %></dd>
|
||||
<% end %>
|
||||
<%= if translation_issue_value(issue, :title) do %>
|
||||
<dt><%= translated("translationValidation.field.title") %></dt>
|
||||
<dt><%= dgettext("ui", "translationValidation.field.title") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :title) %></dd>
|
||||
<% end %>
|
||||
<dt><%= translated("translationValidation.field.languages") %></dt>
|
||||
<dt><%= dgettext("ui", "translationValidation.field.languages") %></dt>
|
||||
<dd><%= translation_issue_languages(issue) %></dd>
|
||||
<%= if translation_issue_value(issue, :file_path) do %>
|
||||
<dt><%= translated("translationValidation.field.filePath") %></dt>
|
||||
<dt><%= dgettext("ui", "translationValidation.field.filePath") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :file_path) %></dd>
|
||||
<% end %>
|
||||
</dl>
|
||||
@@ -226,26 +226,26 @@
|
||||
</section>
|
||||
|
||||
<section class="translation-validation-section">
|
||||
<h3><%= translated("translationValidation.filesystemTitle") %></h3>
|
||||
<h3><%= dgettext("ui", "translationValidation.filesystemTitle") %></h3>
|
||||
|
||||
<%= if @misc_editor.invalid_filesystem_files == [] do %>
|
||||
<p class="translation-validation-empty"><%= translated("translationValidation.noneFilesystem") %></p>
|
||||
<p class="translation-validation-empty"><%= dgettext("ui", "translationValidation.noneFilesystem") %></p>
|
||||
<% else %>
|
||||
<div class="translation-validation-list">
|
||||
<%= for issue <- @misc_editor.invalid_filesystem_files do %>
|
||||
<article class="translation-validation-card translation-validation-card-file" data-testid="translation-validation-card">
|
||||
<p class="translation-validation-card-title"><%= translation_issue_label(issue) %></p>
|
||||
<dl class="translation-validation-card-meta">
|
||||
<dt><%= translated("translationValidation.field.translationFor") %></dt>
|
||||
<dt><%= dgettext("ui", "translationValidation.field.translationFor") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :translation_for) %></dd>
|
||||
<%= if translation_issue_value(issue, :title) do %>
|
||||
<dt><%= translated("translationValidation.field.title") %></dt>
|
||||
<dt><%= dgettext("ui", "translationValidation.field.title") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :title) %></dd>
|
||||
<% end %>
|
||||
<dt><%= translated("translationValidation.field.languages") %></dt>
|
||||
<dt><%= dgettext("ui", "translationValidation.field.languages") %></dt>
|
||||
<dd><%= translation_issue_languages(issue) %></dd>
|
||||
<%= if translation_issue_value(issue, :file_path) do %>
|
||||
<dt><%= translated("translationValidation.field.filePath") %></dt>
|
||||
<dt><%= dgettext("ui", "translationValidation.field.filePath") %></dt>
|
||||
<dd><%= translation_issue_value(issue, :file_path) %></dd>
|
||||
<% end %>
|
||||
</dl>
|
||||
@@ -256,8 +256,8 @@
|
||||
</section>
|
||||
|
||||
<div class="translation-validation-actions">
|
||||
<button class="secondary" type="button" phx-click="rerun_misc_editor" phx-target={@myself} data-testid="translation-validation-revalidate"><%= translated("translationValidation.revalidate") %></button>
|
||||
<button class="primary" type="button" phx-click="fix_translation_validation" phx-target={@myself} data-testid="translation-validation-fix" disabled={not @misc_editor.can_fix?}><%= translated("translationValidation.fix") %></button>
|
||||
<button class="secondary" type="button" phx-click="rerun_misc_editor" phx-target={@myself} data-testid="translation-validation-revalidate"><%= dgettext("ui", "translationValidation.revalidate") %></button>
|
||||
<button class="primary" type="button" phx-click="fix_translation_validation" phx-target={@myself} data-testid="translation-validation-fix" disabled={not @misc_editor.can_fix?}><%= dgettext("ui", "translationValidation.fix") %></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -269,8 +269,8 @@
|
||||
<button class="linkish" type="button" phx-click="open_duplicate_post" phx-target={@myself} phx-value-id={BDS.MapUtils.attr(pair, :post_id_a)} phx-value-title={BDS.MapUtils.attr(pair, :title_a)}><%= BDS.MapUtils.attr(pair, :title_a) %></button>
|
||||
<span>→</span>
|
||||
<button class="linkish" type="button" phx-click="open_duplicate_post" phx-target={@myself} phx-value-id={BDS.MapUtils.attr(pair, :post_id_b)} phx-value-title={BDS.MapUtils.attr(pair, :title_b)}><%= BDS.MapUtils.attr(pair, :title_b) %></button>
|
||||
<span class="misc-summary-pill"><%= if(BDS.MapUtils.attr(pair, :exact_match), do: translated("Exact Match"), else: "#{Float.round((BDS.MapUtils.attr(pair, :similarity) || 0.0) * 100, 1)}%") %></span>
|
||||
<button class="secondary" type="button" phx-click="dismiss_duplicate_pair" phx-target={@myself} phx-value-post-id-a={BDS.MapUtils.attr(pair, :post_id_a)} phx-value-post-id-b={BDS.MapUtils.attr(pair, :post_id_b)}><%= translated("Dismiss") %></button>
|
||||
<span class="misc-summary-pill"><%= if(BDS.MapUtils.attr(pair, :exact_match), do: dgettext("ui", "Exact Match"), else: "#{Float.round((BDS.MapUtils.attr(pair, :similarity) || 0.0) * 100, 1)}%") %></span>
|
||||
<button class="secondary" type="button" phx-click="dismiss_duplicate_pair" phx-target={@myself} phx-value-post-id-a={BDS.MapUtils.attr(pair, :post_id_a)} phx-value-post-id-b={BDS.MapUtils.attr(pair, :post_id_b)}><%= dgettext("ui", "Dismiss") %></button>
|
||||
</article>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -281,7 +281,7 @@
|
||||
<p class="git-diff-empty"><%= @misc_editor.empty_message %></p>
|
||||
<% else %>
|
||||
<form class="git-diff-toolbar" phx-change="select_git_diff_file" phx-target={@myself}>
|
||||
<label for="git-diff-file-select"><%= translated("gitDiff.changedFiles") %></label>
|
||||
<label for="git-diff-file-select"><%= dgettext("ui", "gitDiff.changedFiles") %></label>
|
||||
<select id="git-diff-file-select" data-testid="git-diff-file-select" name="path">
|
||||
<%= for file_path <- @misc_editor.files do %>
|
||||
<option value={file_path} selected={file_path == @misc_editor.selected_file_path}><%= file_path %></option>
|
||||
|
||||
@@ -2,10 +2,10 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
@moduledoc false
|
||||
|
||||
use Phoenix.Component
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.{I18n, Media, Metadata, Posts, Repo}
|
||||
alias BDS.Media.Media, as: MediaRecord
|
||||
alias BDS.Media.Translation, as: MediaTranslation
|
||||
@@ -38,10 +38,10 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
language_names: language_names(),
|
||||
language_flags: language_flags(),
|
||||
existing_translations: existing_translations(current_tab),
|
||||
ai_title: ShellData.translate("AI Suggestions", %{}, page_language),
|
||||
insert_link_title: ShellData.translate("Insert Link", %{}, page_language),
|
||||
insert_media_title: ShellData.translate("Insert Media", %{}, page_language),
|
||||
language_picker_title: ShellData.translate("Translate", %{}, page_language),
|
||||
ai_title: BDS.Gettext.lgettext(page_language, "ui", "AI Suggestions"),
|
||||
insert_link_title: BDS.Gettext.lgettext(page_language, "ui", "Insert Link"),
|
||||
insert_media_title: BDS.Gettext.lgettext(page_language, "ui", "Insert Media"),
|
||||
language_picker_title: BDS.Gettext.lgettext(page_language, "ui", "Translate"),
|
||||
gallery_title: tab_title,
|
||||
ai_fields: ai_fields(current_tab, tab_title, tab_subtitle, page_language),
|
||||
delete_details: delete_details(current_tab, page_language),
|
||||
@@ -64,8 +64,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
|
||||
def markdown_link(text, url), do: "[#{text}](#{url})"
|
||||
|
||||
def translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
|
||||
|
||||
def project_metadata(nil), do: %{main_language: "en", blog_languages: []}
|
||||
|
||||
@@ -217,7 +216,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
[
|
||||
%{
|
||||
key: "title",
|
||||
label: ShellData.translate("Title", %{}, page_language),
|
||||
label: BDS.Gettext.lgettext(page_language, "ui", "Title"),
|
||||
current_value: post.title || title,
|
||||
suggested_value: "",
|
||||
locked: false,
|
||||
@@ -225,7 +224,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
},
|
||||
%{
|
||||
key: "excerpt",
|
||||
label: ShellData.translate("Excerpt", %{}, page_language),
|
||||
label: BDS.Gettext.lgettext(page_language, "ui", "Excerpt"),
|
||||
current_value: post.excerpt || subtitle,
|
||||
suggested_value: "",
|
||||
locked: false,
|
||||
@@ -233,7 +232,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
},
|
||||
%{
|
||||
key: "slug",
|
||||
label: ShellData.translate("Slug", %{}, page_language),
|
||||
label: BDS.Gettext.lgettext(page_language, "ui", "Slug"),
|
||||
current_value: post.slug || slugify(post.title || title),
|
||||
suggested_value: "",
|
||||
locked: post.status == :published,
|
||||
@@ -254,7 +253,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
[
|
||||
%{
|
||||
key: "title",
|
||||
label: ShellData.translate("Title", %{}, page_language),
|
||||
label: BDS.Gettext.lgettext(page_language, "ui", "Title"),
|
||||
current_value: media.title || title,
|
||||
suggested_value: "",
|
||||
locked: false,
|
||||
@@ -262,7 +261,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
},
|
||||
%{
|
||||
key: "alt",
|
||||
label: ShellData.translate("Alt Text", %{}, page_language),
|
||||
label: BDS.Gettext.lgettext(page_language, "ui", "Alt Text"),
|
||||
current_value: media.alt || "",
|
||||
suggested_value: "",
|
||||
locked: false,
|
||||
@@ -270,7 +269,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
},
|
||||
%{
|
||||
key: "caption",
|
||||
label: ShellData.translate("Caption", %{}, page_language),
|
||||
label: BDS.Gettext.lgettext(page_language, "ui", "Caption"),
|
||||
current_value: media.caption || "",
|
||||
suggested_value: "",
|
||||
locked: false,
|
||||
@@ -306,7 +305,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
|> Enum.map(&(&1 || media_id))
|
||||
|
||||
%{
|
||||
title: ShellData.translate("Delete Media", %{}, page_language),
|
||||
title: BDS.Gettext.lgettext(page_language, "ui", "Delete Media"),
|
||||
entity_name: entity_name,
|
||||
entity_type: "media",
|
||||
reference_list: reference_list
|
||||
@@ -314,7 +313,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
rescue
|
||||
_error ->
|
||||
%{
|
||||
title: ShellData.translate("Delete Media", %{}, page_language),
|
||||
title: BDS.Gettext.lgettext(page_language, "ui", "Delete Media"),
|
||||
entity_name: media_id,
|
||||
entity_type: "media",
|
||||
reference_list: []
|
||||
@@ -327,7 +326,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
|> Kernel.||("tag")
|
||||
|
||||
%{
|
||||
title: ShellData.translate("Delete Tag", %{}, page_language),
|
||||
title: BDS.Gettext.lgettext(page_language, "ui", "Delete Tag"),
|
||||
entity_name: tag_name,
|
||||
entity_type: "tag",
|
||||
reference_list: []
|
||||
@@ -335,7 +334,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
rescue
|
||||
_error ->
|
||||
%{
|
||||
title: ShellData.translate("Delete Tag", %{}, page_language),
|
||||
title: BDS.Gettext.lgettext(page_language, "ui", "Delete Tag"),
|
||||
entity_name: "tag",
|
||||
entity_type: "tag",
|
||||
reference_list: []
|
||||
@@ -344,7 +343,7 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
|
||||
defp delete_details(_tab, page_language) do
|
||||
%{
|
||||
title: ShellData.translate("Delete", %{}, page_language),
|
||||
title: BDS.Gettext.lgettext(page_language, "ui", "Delete"),
|
||||
entity_name: "",
|
||||
entity_type: "item",
|
||||
reference_list: []
|
||||
@@ -366,16 +365,16 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
%{
|
||||
target: target,
|
||||
count: max(length(tags), 1),
|
||||
title: ShellData.translate("Merge Tags", %{}, page_language),
|
||||
message: ShellData.translate("Cannot be undone.", %{}, page_language)
|
||||
title: BDS.Gettext.lgettext(page_language, "ui", "Merge Tags"),
|
||||
message: BDS.Gettext.lgettext(page_language, "ui", "Cannot be undone.")
|
||||
}
|
||||
rescue
|
||||
_error ->
|
||||
%{
|
||||
target: "tag",
|
||||
count: 1,
|
||||
title: ShellData.translate("Merge Tags", %{}, page_language),
|
||||
message: ShellData.translate("Cannot be undone.", %{}, page_language)
|
||||
title: BDS.Gettext.lgettext(page_language, "ui", "Merge Tags"),
|
||||
message: BDS.Gettext.lgettext(page_language, "ui", "Cannot be undone.")
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<%= case @shell_overlay.kind do %>
|
||||
<% :ai_suggestions -> %>
|
||||
<div class="shell-overlay-backdrop ai-suggestions-modal-backdrop" data-testid="shell-overlay-backdrop" phx-window-keydown="overlay_keydown">
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={translated("Cancel")}></button>
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={dgettext("ui", "Cancel")}></button>
|
||||
<div class="ai-suggestions-modal" role="dialog" aria-modal="true">
|
||||
<div class="ai-suggestions-modal-header">
|
||||
<h2><%= @shell_overlay.title %></h2>
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="ai-suggestions-modal-body">
|
||||
<%= if Map.get(@shell_overlay, :error) do %>
|
||||
<div class="ai-suggestions-error">
|
||||
<strong><%= translated("Error") %></strong>
|
||||
<strong><%= dgettext("ui", "Error") %></strong>
|
||||
<span><%= @shell_overlay.error %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -39,27 +39,27 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-suggestions-modal-footer">
|
||||
<button class="button-cancel" type="button" phx-click="close_overlay"><%= translated("Cancel") %></button>
|
||||
<button class="button-apply" type="button" phx-click="overlay_confirm"><%= translated("Apply Selected") %></button>
|
||||
<button class="button-cancel" type="button" phx-click="close_overlay"><%= dgettext("ui", "Cancel") %></button>
|
||||
<button class="button-apply" type="button" phx-click="overlay_confirm"><%= dgettext("ui", "Apply Selected") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% :insert_link -> %>
|
||||
<div class="shell-overlay-backdrop insert-modal-backdrop" data-testid="shell-overlay-backdrop" phx-window-keydown="overlay_keydown">
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={translated("Cancel")}></button>
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={dgettext("ui", "Cancel")}></button>
|
||||
<div class="insert-modal" role="dialog" aria-modal="true">
|
||||
<div class="insert-modal-header">
|
||||
<h2 class="insert-modal-title"><%= @shell_overlay.title %></h2>
|
||||
<div class="insert-modal-tabs">
|
||||
<button class={["insert-modal-tab", if(@shell_overlay.active_tab == :internal, do: "active")]} type="button" phx-click="overlay_set_tab" phx-value-tab="internal"><%= translated("Internal") %></button>
|
||||
<button class={["insert-modal-tab", if(@shell_overlay.active_tab == :external, do: "active")]} type="button" phx-click="overlay_set_tab" phx-value-tab="external"><%= translated("External") %></button>
|
||||
<button class={["insert-modal-tab", if(@shell_overlay.active_tab == :internal, do: "active")]} type="button" phx-click="overlay_set_tab" phx-value-tab="internal"><%= dgettext("ui", "Internal") %></button>
|
||||
<button class={["insert-modal-tab", if(@shell_overlay.active_tab == :external, do: "active")]} type="button" phx-click="overlay_set_tab" phx-value-tab="external"><%= dgettext("ui", "External") %></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= if @shell_overlay.active_tab == :internal do %>
|
||||
<form class="insert-modal-search" phx-change="overlay_set_search">
|
||||
<input class="insert-modal-input" type="text" name="overlay[query]" value={@shell_overlay.search_query} placeholder={translated("sidebar.searchPostsPlaceholder")} />
|
||||
<input class="insert-modal-input" type="text" name="overlay[query]" value={@shell_overlay.search_query} placeholder={dgettext("ui", "Search posts...")} />
|
||||
</form>
|
||||
<div class="insert-modal-results">
|
||||
<%= for result <- if(String.length(@shell_overlay.search_query) >= 2, do: @shell_overlay.results, else: @shell_overlay.related_posts) do %>
|
||||
@@ -69,20 +69,20 @@
|
||||
</button>
|
||||
<% end %>
|
||||
<%= if Enum.empty?(if(String.length(@shell_overlay.search_query) >= 2, do: @shell_overlay.results, else: @shell_overlay.related_posts)) do %>
|
||||
<div class="insert-modal-status"><%= translated("No items") %></div>
|
||||
<div class="insert-modal-status"><%= dgettext("ui", "No items") %></div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<form class="insert-modal-external" phx-change="overlay_update_form">
|
||||
<label class="insert-modal-field">
|
||||
<span class="insert-modal-label"><%= translated("URL") %></span>
|
||||
<span class="insert-modal-label"><%= dgettext("ui", "URL") %></span>
|
||||
<input class="insert-modal-input" type="text" name="overlay[url]" value={@shell_overlay.external_url} />
|
||||
</label>
|
||||
<label class="insert-modal-field">
|
||||
<span class="insert-modal-label"><%= translated("Display Text") %></span>
|
||||
<span class="insert-modal-label"><%= dgettext("ui", "Display Text") %></span>
|
||||
<input class="insert-modal-input" type="text" name="overlay[text]" value={@shell_overlay.external_text} />
|
||||
</label>
|
||||
<button class="insert-modal-submit" type="button" phx-click="overlay_insert_external"><%= translated("Insert") %></button>
|
||||
<button class="insert-modal-submit" type="button" phx-click="overlay_insert_external"><%= dgettext("ui", "Insert") %></button>
|
||||
</form>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -90,13 +90,13 @@
|
||||
|
||||
<% :insert_media -> %>
|
||||
<div class="shell-overlay-backdrop insert-modal-backdrop" data-testid="shell-overlay-backdrop" phx-window-keydown="overlay_keydown">
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={translated("Cancel")}></button>
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={dgettext("ui", "Cancel")}></button>
|
||||
<div class="insert-modal" role="dialog" aria-modal="true">
|
||||
<div class="insert-modal-header">
|
||||
<h2 class="insert-modal-title"><%= @shell_overlay.title %></h2>
|
||||
</div>
|
||||
<form class="insert-modal-search" phx-change="overlay_set_search">
|
||||
<input class="insert-modal-input" type="text" name="overlay[query]" value={@shell_overlay.search_query} placeholder={translated("sidebar.searchMediaPlaceholder")} />
|
||||
<input class="insert-modal-input" type="text" name="overlay[query]" value={@shell_overlay.search_query} placeholder={dgettext("ui", "Search media...")} />
|
||||
</form>
|
||||
<div class="insert-modal-results insert-modal-media-grid">
|
||||
<%= for result <- @shell_overlay.results do %>
|
||||
@@ -115,14 +115,14 @@
|
||||
|
||||
<% :language_picker -> %>
|
||||
<div class="shell-overlay-backdrop language-picker-modal-backdrop" data-testid="shell-overlay-backdrop" phx-window-keydown="overlay_keydown">
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={translated("Cancel")}></button>
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={dgettext("ui", "Cancel")}></button>
|
||||
<div class="language-picker-modal" role="dialog" aria-modal="true">
|
||||
<div class="language-picker-modal-header">
|
||||
<h2><%= @shell_overlay.title %></h2>
|
||||
<button class="language-picker-modal-close" type="button" phx-click="close_overlay">×</button>
|
||||
</div>
|
||||
<div class="language-picker-modal-body">
|
||||
<div class="language-picker-label"><%= translated("Available languages") %></div>
|
||||
<div class="language-picker-label"><%= dgettext("ui", "Available languages") %></div>
|
||||
<div class="language-picker-options">
|
||||
<%= for target <- @shell_overlay.available_targets do %>
|
||||
<button class="language-picker-option" type="button" phx-click="overlay_select_language" phx-value-code={target.code}>
|
||||
@@ -140,7 +140,7 @@
|
||||
|
||||
<% :confirm_delete -> %>
|
||||
<div class="shell-overlay-backdrop confirm-delete-modal-backdrop" data-testid="shell-overlay-backdrop" phx-window-keydown="overlay_keydown">
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={translated("Cancel")}></button>
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={dgettext("ui", "Cancel")}></button>
|
||||
<div class="confirm-delete-modal" role="dialog" aria-modal="true">
|
||||
<div class="confirm-delete-modal-header">
|
||||
<h2><%= @shell_overlay.title %></h2>
|
||||
@@ -151,7 +151,7 @@
|
||||
<%= if @shell_overlay.reference_count > 0 do %>
|
||||
<div class="confirm-delete-warning">
|
||||
<div class="warning-content">
|
||||
<strong><%= translated("This item is referenced by:") %></strong>
|
||||
<strong><%= dgettext("ui", "This item is referenced by:") %></strong>
|
||||
<ul class="reference-list">
|
||||
<%= for title <- @shell_overlay.reference_list do %>
|
||||
<li><span class="reference-title"><%= title %></span></li>
|
||||
@@ -162,15 +162,15 @@
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="confirm-delete-modal-footer">
|
||||
<button class="button-cancel" type="button" phx-click="close_overlay"><%= translated("Cancel") %></button>
|
||||
<button class="button-delete" type="button" phx-click="overlay_confirm"><%= translated("Delete") %></button>
|
||||
<button class="button-cancel" type="button" phx-click="close_overlay"><%= dgettext("ui", "Cancel") %></button>
|
||||
<button class="button-delete" type="button" phx-click="overlay_confirm"><%= dgettext("ui", "Delete") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% :confirm_dialog -> %>
|
||||
<div class="shell-overlay-backdrop confirm-delete-modal-backdrop" data-testid="shell-overlay-backdrop" phx-window-keydown="overlay_keydown">
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={translated("Cancel")}></button>
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={dgettext("ui", "Cancel")}></button>
|
||||
<div class="confirm-delete-modal" role="dialog" aria-modal="true">
|
||||
<div class="confirm-delete-modal-header">
|
||||
<h2><%= @shell_overlay.title %></h2>
|
||||
@@ -180,18 +180,18 @@
|
||||
<div class="confirm-delete-message"><%= @shell_overlay.message %></div>
|
||||
</div>
|
||||
<div class="confirm-delete-modal-footer">
|
||||
<button class="button-cancel" type="button" phx-click="close_overlay"><%= translated("Cancel") %></button>
|
||||
<button class="button-apply" type="button" phx-click="overlay_confirm"><%= translated("Confirm") %></button>
|
||||
<button class="button-cancel" type="button" phx-click="close_overlay"><%= dgettext("ui", "Cancel") %></button>
|
||||
<button class="button-apply" type="button" phx-click="overlay_confirm"><%= dgettext("ui", "Confirm") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% :gallery -> %>
|
||||
<div class="shell-overlay-backdrop gallery-overlay-backdrop" data-testid="shell-overlay-backdrop" phx-window-keydown="overlay_keydown">
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={translated("Cancel")}></button>
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="close_overlay" aria-label={dgettext("ui", "Cancel")}></button>
|
||||
<div class="gallery-overlay" role="dialog" aria-modal="true">
|
||||
<div class="gallery-overlay-header">
|
||||
<h2><%= translated("Gallery") %></h2>
|
||||
<h2><%= dgettext("ui", "Gallery") %></h2>
|
||||
<button class="gallery-overlay-close" type="button" phx-click="close_overlay">×</button>
|
||||
</div>
|
||||
<div class="gallery-overlay-grid">
|
||||
@@ -205,7 +205,7 @@
|
||||
|
||||
<%= if @shell_overlay.lightbox do %>
|
||||
<div class="lightbox-overlay">
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="overlay_close_lightbox" aria-label={translated("Cancel")}></button>
|
||||
<button class="shell-overlay-dismiss" type="button" phx-click="overlay_close_lightbox" aria-label={dgettext("ui", "Cancel")}></button>
|
||||
<div class="lightbox-container">
|
||||
<button class="lightbox-close" type="button" phx-click="overlay_close_lightbox">×</button>
|
||||
<%= if @shell_overlay.lightbox.total_count > 1 do %>
|
||||
|
||||
@@ -7,7 +7,7 @@ defmodule BDS.Desktop.ShellLive.OverlayManager do
|
||||
import Phoenix.LiveView, only: [send_update: 2]
|
||||
|
||||
alias BDS.{AI, Media, Metadata}
|
||||
alias BDS.Desktop.{Overlay, ShellData, UILocale}
|
||||
alias BDS.Desktop.{Overlay}
|
||||
|
||||
alias BDS.Desktop.ShellLive.{
|
||||
MediaEditor,
|
||||
@@ -16,6 +16,7 @@ defmodule BDS.Desktop.ShellLive.OverlayManager do
|
||||
}
|
||||
|
||||
alias BDS.Desktop.ShellLive.OverlayComponents, as: ShellOverlayComponents
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
# ── Event handlers ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -67,8 +68,8 @@ defmodule BDS.Desktop.ShellLive.OverlayManager do
|
||||
if socket.assigns.offline_mode do
|
||||
callbacks.append_output.(
|
||||
socket,
|
||||
translated("AI Suggestions"),
|
||||
translated("Automatic AI actions stay gated by airplane mode."),
|
||||
dgettext("ui", "AI Suggestions"),
|
||||
dgettext("ui", "Automatic AI actions stay gated by airplane mode."),
|
||||
nil,
|
||||
"info"
|
||||
)
|
||||
@@ -265,7 +266,7 @@ defmodule BDS.Desktop.ShellLive.OverlayManager do
|
||||
socket
|
||||
|> assign(:shell_overlay, nil)
|
||||
|> callbacks.append_output.(
|
||||
translated("Delete Media"),
|
||||
dgettext("ui", "Delete Media"),
|
||||
inspect(reason),
|
||||
nil,
|
||||
"error"
|
||||
@@ -392,7 +393,7 @@ defmodule BDS.Desktop.ShellLive.OverlayManager do
|
||||
) :: Phoenix.LiveView.Socket.t()
|
||||
defp close_overlay_with_output(socket, append_output, title, details) do
|
||||
socket
|
||||
|> append_output.(title, translated("Command completed"), details, "info")
|
||||
|> append_output.(title, dgettext("ui", "Command completed"), details, "info")
|
||||
|> assign(:shell_overlay, nil)
|
||||
end
|
||||
|
||||
@@ -444,7 +445,4 @@ defmodule BDS.Desktop.ShellLive.OverlayManager do
|
||||
_error -> "en"
|
||||
end
|
||||
|
||||
@spec translated(String.t(), map()) :: String.t()
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, UILocale.current())
|
||||
end
|
||||
|
||||
@@ -10,6 +10,7 @@ defmodule BDS.Desktop.ShellLive.PanelRenderer do
|
||||
alias BDS.PostLinks
|
||||
alias BDS.Posts
|
||||
alias BDS.Posts.Post
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@doc "Render the active panel tab body."
|
||||
def render_panel_body(assigns) do
|
||||
@@ -38,7 +39,7 @@ defmodule BDS.Desktop.ShellLive.PanelRenderer do
|
||||
phx-click="open_overlay"
|
||||
phx-value-kind={button.kind}
|
||||
>
|
||||
<%= translated(button.label) %>
|
||||
<%= button.label %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -50,8 +51,8 @@ defmodule BDS.Desktop.ShellLive.PanelRenderer do
|
||||
~H"""
|
||||
<%= if Enum.empty?(Map.get(@task_status, :tasks, [])) do %>
|
||||
<div class="panel-entry panel-empty-state">
|
||||
<strong><%= translated("Tasks") %></strong>
|
||||
<span><%= translated("No background tasks running") %></span>
|
||||
<strong><%= dgettext("ui", "Tasks") %></strong>
|
||||
<span><%= dgettext("ui", "No background tasks running") %></span>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="task-list">
|
||||
@@ -79,8 +80,8 @@ defmodule BDS.Desktop.ShellLive.PanelRenderer do
|
||||
~H"""
|
||||
<%= if Enum.empty?(@output_entries) do %>
|
||||
<div class="panel-entry panel-empty-state output-list">
|
||||
<strong><%= translated("Output") %></strong>
|
||||
<span><%= translated("No shell output yet") %></span>
|
||||
<strong><%= dgettext("ui", "Output") %></strong>
|
||||
<span><%= dgettext("ui", "No shell output yet") %></span>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="output-list">
|
||||
@@ -113,13 +114,13 @@ defmodule BDS.Desktop.ShellLive.PanelRenderer do
|
||||
~H"""
|
||||
<%= if Enum.empty?(@backlinks) and Enum.empty?(@outlinks) do %>
|
||||
<div class="panel-entry panel-empty-state">
|
||||
<strong><%= translated("Post Links") %></strong>
|
||||
<span><%= translated("No post links yet") %></span>
|
||||
<strong><%= dgettext("ui", "Post Links") %></strong>
|
||||
<span><%= dgettext("ui", "No post links yet") %></span>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="git-log-list">
|
||||
<%= if Enum.any?(@backlinks) do %>
|
||||
<div class="panel-entry"><strong><%= translated("Backlinks") %></strong></div>
|
||||
<div class="panel-entry"><strong><%= dgettext("ui", "Backlinks") %></strong></div>
|
||||
<%= for entry <- @backlinks do %>
|
||||
<button
|
||||
class="panel-entry task-entry"
|
||||
@@ -137,7 +138,7 @@ defmodule BDS.Desktop.ShellLive.PanelRenderer do
|
||||
<% end %>
|
||||
|
||||
<%= if Enum.any?(@outlinks) do %>
|
||||
<div class="panel-entry"><strong><%= translated("Links To") %></strong></div>
|
||||
<div class="panel-entry"><strong><%= dgettext("ui", "Links To") %></strong></div>
|
||||
<%= for entry <- @outlinks do %>
|
||||
<button
|
||||
class="panel-entry task-entry"
|
||||
@@ -166,15 +167,15 @@ defmodule BDS.Desktop.ShellLive.PanelRenderer do
|
||||
<%= if Enum.empty?(@git_entries) do %>
|
||||
<div class="git-log-list">
|
||||
<div class="panel-entry panel-empty-state">
|
||||
<strong><%= translated("Git Log") %></strong>
|
||||
<span><%= translated("No git history yet") %></span>
|
||||
<strong><%= dgettext("ui", "Git Log") %></strong>
|
||||
<span><%= dgettext("ui", "No git history yet") %></span>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="git-log-list">
|
||||
<%= for entry <- @git_entries do %>
|
||||
<div class="panel-entry task-entry">
|
||||
<strong><%= short_commit_hash(entry.hash) %> <%= entry.subject || translated("No commit subject") %></strong>
|
||||
<strong><%= short_commit_hash(entry.hash) %> <%= entry.subject || dgettext("ui", "No commit subject") %></strong>
|
||||
<span><%= entry.hash %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -189,7 +190,7 @@ defmodule BDS.Desktop.ShellLive.PanelRenderer do
|
||||
~H"""
|
||||
<div class="panel-entry">
|
||||
<strong><%= @panel_label %></strong>
|
||||
<span><%= translated("The shared lower panel is available for tasks, output, git details, and editor-specific diagnostics.") %></span>
|
||||
<span><%= dgettext("ui", "The shared lower panel is available for tasks, output, git details, and editor-specific diagnostics.") %></span>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
@@ -300,7 +301,4 @@ defmodule BDS.Desktop.ShellLive.PanelRenderer do
|
||||
defp progress_percent(_), do: ""
|
||||
|
||||
defp present?(value), do: value not in [nil, ""]
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -6,7 +6,6 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
alias BDS.{AI, Posts, Preview}
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Desktop.ShellLive.PostEditor.{DraftManagement, ListValues, Persistence, PostMetadata}
|
||||
alias BDS.Desktop.UILocale
|
||||
alias BDS.Posts.Post
|
||||
alias BDS.Tags
|
||||
|
||||
@@ -47,6 +46,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
persist: 5
|
||||
]
|
||||
|
||||
use Gettext, backend: BDS.Gettext
|
||||
import PostMetadata,
|
||||
only: [
|
||||
blank?: 1,
|
||||
@@ -461,11 +461,11 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
)
|
||||
|
||||
notify_parent({:post_editor_dirty, post.id, false})
|
||||
notify_output(socket, translated("Post"), translated("Post saved"))
|
||||
notify_output(socket, dgettext("ui", "Post"), dgettext("ui", "Post saved"))
|
||||
socket
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Post"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Post"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
end
|
||||
end
|
||||
@@ -502,11 +502,11 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
)
|
||||
|
||||
notify_parent({:post_editor_dirty, post.id, false})
|
||||
notify_output(socket, translated("Post"), translated("Post published"))
|
||||
notify_output(socket, dgettext("ui", "Post"), dgettext("ui", "Post published"))
|
||||
socket
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Post"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Post"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
end
|
||||
end
|
||||
@@ -543,7 +543,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
socket
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Post"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Post"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
end
|
||||
end
|
||||
@@ -558,7 +558,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
socket
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Post"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Post"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
end
|
||||
end
|
||||
@@ -567,8 +567,8 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
if Map.get(socket.assigns, :offline_mode, true) do
|
||||
notify_output(
|
||||
socket,
|
||||
translated("Detect Language"),
|
||||
translated("Automatic AI actions stay gated by airplane mode."),
|
||||
dgettext("ui", "Detect Language"),
|
||||
dgettext("ui", "Automatic AI actions stay gated by airplane mode."),
|
||||
"info"
|
||||
)
|
||||
|> build_data()
|
||||
@@ -593,14 +593,14 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
|> build_data()
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Detect Language"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Detect Language"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
|
||||
_other ->
|
||||
notify_output(
|
||||
socket,
|
||||
translated("Detect Language"),
|
||||
translated("Language detection failed."),
|
||||
dgettext("ui", "Detect Language"),
|
||||
dgettext("ui", "Language detection failed."),
|
||||
"error"
|
||||
)
|
||||
|> build_data()
|
||||
@@ -613,8 +613,8 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
if Map.get(socket.assigns, :offline_mode, true) do
|
||||
notify_output(
|
||||
socket,
|
||||
translated("Translate"),
|
||||
translated("Automatic AI actions stay gated by airplane mode."),
|
||||
dgettext("ui", "Translate"),
|
||||
dgettext("ui", "Automatic AI actions stay gated by airplane mode."),
|
||||
"info"
|
||||
)
|
||||
|> build_data()
|
||||
@@ -642,12 +642,12 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
socket
|
||||
else
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Translate"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Translate"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Translate"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Translate"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
end
|
||||
end
|
||||
@@ -695,7 +695,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
socket
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("AI Suggestions"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "AI Suggestions"), inspect(reason), "error")
|
||||
|> build_data()
|
||||
end
|
||||
end
|
||||
@@ -813,17 +813,14 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
def post_status_label(status), do: ShellData.dashboard_status_label(status)
|
||||
|
||||
@spec post_editor_save_state_label(term()) :: term()
|
||||
def post_editor_save_state_label(:dirty), do: translated("Unsaved")
|
||||
def post_editor_save_state_label(:saved), do: translated("Saved")
|
||||
def post_editor_save_state_label(:published), do: translated("Published")
|
||||
def post_editor_save_state_label(:discarded), do: translated("Reverted")
|
||||
def post_editor_save_state_label(_state), do: translated("Idle")
|
||||
def post_editor_save_state_label(:dirty), do: dgettext("ui", "Unsaved")
|
||||
def post_editor_save_state_label(:saved), do: dgettext("ui", "Saved")
|
||||
def post_editor_save_state_label(:published), do: dgettext("ui", "Published")
|
||||
def post_editor_save_state_label(:discarded), do: dgettext("ui", "Reverted")
|
||||
def post_editor_save_state_label(_state), do: dgettext("ui", "Idle")
|
||||
|
||||
@spec post_editor_mode_label(term()) :: term()
|
||||
def post_editor_mode_label(:markdown), do: translated("Markdown")
|
||||
def post_editor_mode_label(:preview), do: translated("Preview")
|
||||
def post_editor_mode_label(:markdown), do: dgettext("ui", "Markdown")
|
||||
def post_editor_mode_label(:preview), do: dgettext("ui", "Preview")
|
||||
|
||||
@spec translated(term(), term()) :: term()
|
||||
def translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, UILocale.current())
|
||||
end
|
||||
|
||||
@@ -3,8 +3,8 @@ defmodule BDS.Desktop.ShellLive.PostEditor.Persistence do
|
||||
|
||||
alias BDS.Posts
|
||||
alias BDS.Posts.Post
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Desktop.ShellLive.PostEditor.{DraftManagement, PostMetadata}
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec persist(term(), term(), term(), term(), term()) :: term()
|
||||
def persist(%Post{} = post, draft, active_language, metadata, action) do
|
||||
@@ -54,15 +54,15 @@ defmodule BDS.Desktop.ShellLive.PostEditor.Persistence do
|
||||
@spec discard_label(term()) :: term()
|
||||
def discard_label(%Post{} = post) do
|
||||
if has_published_version?(post),
|
||||
do: translated("Discard Changes"),
|
||||
else: translated("Discard Draft")
|
||||
do: dgettext("ui", "Discard Changes"),
|
||||
else: dgettext("ui", "Discard Draft")
|
||||
end
|
||||
|
||||
@spec discard_title(term()) :: term()
|
||||
def discard_title(%Post{} = post) do
|
||||
if has_published_version?(post),
|
||||
do: translated("Discard changes and restore the published version"),
|
||||
else: translated("Delete this unpublished draft")
|
||||
do: dgettext("ui", "Discard changes and restore the published version"),
|
||||
else: dgettext("ui", "Delete this unpublished draft")
|
||||
end
|
||||
|
||||
defp save_canonical_draft(%Post{id: post_id}, draft) do
|
||||
@@ -112,7 +112,4 @@ defmodule BDS.Desktop.ShellLive.PostEditor.Persistence do
|
||||
|> Enum.map(&String.trim/1)
|
||||
|> Enum.reject(&(&1 == ""))
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -4,9 +4,9 @@ defmodule BDS.Desktop.ShellLive.PostEditor.PostMetadata do
|
||||
import Ecto.Query
|
||||
|
||||
alias BDS.{I18n, Media, Metadata, PostLinks, Posts, Preview, Repo, Templates}
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Media.Media, as: MediaRecord
|
||||
alias BDS.Posts.{Post, PostMedia}
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec project_metadata(term()) :: term()
|
||||
def project_metadata(nil), do: %{main_language: "en", blog_languages: []}
|
||||
@@ -163,7 +163,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor.PostMetadata do
|
||||
|
||||
@spec display_title(term(), term(), term()) :: term()
|
||||
def display_title(title, slug, fallback_id) do
|
||||
blank_to_nil(title) || blank_to_nil(slug) || fallback_id || translated("Untitled")
|
||||
blank_to_nil(title) || blank_to_nil(slug) || fallback_id || dgettext("ui", "Untitled")
|
||||
end
|
||||
|
||||
@spec gallery_count(term()) :: term()
|
||||
@@ -219,7 +219,4 @@ defmodule BDS.Desktop.ShellLive.PostEditor.PostMetadata do
|
||||
trimmed -> trimmed
|
||||
end
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class={["editor-tab", "active", if(@post_editor.dirty?, do: "dirty")]}>
|
||||
<span class="editor-tab-title" data-testid="editor-title"><%= @post_editor.display_title %></span>
|
||||
<%= if @post_editor.dirty? do %>
|
||||
<span class="editor-tab-dirty" title={translated("Unsaved")}>●</span>
|
||||
<span class="editor-tab-dirty" title={dgettext("ui", "Unsaved")}>●</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -25,7 +25,7 @@
|
||||
phx-target={@myself}
|
||||
>
|
||||
<span class="quick-actions-btn-icon">⚡</span>
|
||||
<span class="quick-actions-btn-label"><%= translated("Quick Actions") %></span>
|
||||
<span class="quick-actions-btn-label"><%= dgettext("ui", "Quick Actions") %></span>
|
||||
</button>
|
||||
|
||||
<%= if @post_editor.quick_actions_open? do %>
|
||||
@@ -40,8 +40,8 @@
|
||||
>
|
||||
<span class="quick-action-icon">🤖</span>
|
||||
<span class="quick-action-text">
|
||||
<strong><%= translated("AI Suggestions") %></strong>
|
||||
<small><%= translated("Review title, excerpt, and content suggestions") %></small>
|
||||
<strong><%= dgettext("ui", "AI Suggestions") %></strong>
|
||||
<small><%= dgettext("ui", "Review title, excerpt, and content suggestions") %></small>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -57,8 +57,8 @@
|
||||
>
|
||||
<span class="quick-action-icon">🌍</span>
|
||||
<span class="quick-action-text">
|
||||
<strong><%= translated("Translate") %></strong>
|
||||
<small><%= translated("Select a target language for this post") %></small>
|
||||
<strong><%= dgettext("ui", "Translate") %></strong>
|
||||
<small><%= dgettext("ui", "Select a target language for this post") %></small>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
<%= if @post_editor.can_publish? do %>
|
||||
<button class="success" data-testid="post-publish-button" type="button" phx-click="publish_post_editor" phx-target={@myself}>
|
||||
<%= translated("Publish") %>
|
||||
<%= dgettext("ui", "Publish") %>
|
||||
</button>
|
||||
<% end %>
|
||||
<%= if @post_editor.can_publish? do %>
|
||||
@@ -77,7 +77,7 @@
|
||||
<% end %>
|
||||
<%= if @post_editor.can_delete? do %>
|
||||
<button class="secondary danger" data-testid="post-delete-button" type="button" phx-click="delete_post_editor" phx-target={@myself}>
|
||||
<%= translated("Delete") %>
|
||||
<%= dgettext("ui", "Delete") %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -87,10 +87,10 @@
|
||||
<div class="metadata-toggle-header">
|
||||
<button class={["metadata-toggle", if(@post_editor.metadata_expanded, do: "expanded")]} type="button" phx-click="toggle_post_metadata" phx-target={@myself}>
|
||||
<span class="metadata-toggle-chevron"><%= if @post_editor.metadata_expanded, do: "▼", else: "▶" %></span>
|
||||
<span><%= translated("Metadata") %></span>
|
||||
<span><%= dgettext("ui", "Metadata") %></span>
|
||||
</button>
|
||||
|
||||
<div class="editor-translations-flags" aria-label={translated("Translations")}>
|
||||
<div class="editor-translations-flags" aria-label={dgettext("ui", "Translations")}>
|
||||
<%= for flag <- @post_editor.translation_flags do %>
|
||||
<button
|
||||
class={[
|
||||
@@ -114,19 +114,19 @@
|
||||
<div class={["editor-header-row", if(not @post_editor.metadata_expanded, do: "is-collapsed")]}>
|
||||
<div class="editor-meta">
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Title") %></label>
|
||||
<label><%= dgettext("ui", "Title") %></label>
|
||||
<input class="post-editor-input" type="text" name="post_editor[title]" value={@post_editor.form["title"]} />
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Tags") %></label>
|
||||
<label><%= dgettext("ui", "Tags") %></label>
|
||||
<div class="tag-input-container">
|
||||
<input type="hidden" name="post_editor[tags]" value={@post_editor.form["tags"]} />
|
||||
<div class="tag-input-wrapper">
|
||||
<%= for tag <- @post_editor.tag_chips do %>
|
||||
<span class={["tag-chip", if(tag.color, do: "has-color")]} style={tag_chip_style(tag.color)}>
|
||||
<span><%= tag.name %></span>
|
||||
<button class="tag-chip-remove" type="button" phx-click="remove_post_editor_tag" phx-value-tag={tag.name} phx-target={@myself} aria-label={translated("Remove tag")}>×</button>
|
||||
<button class="tag-chip-remove" type="button" phx-click="remove_post_editor_tag" phx-value-tag={tag.name} phx-target={@myself} aria-label={dgettext("ui", "Remove tag")}>×</button>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
type="text"
|
||||
name="post_editor[tag_query]"
|
||||
value={@post_editor.tag_query}
|
||||
placeholder={translated("Add tag")}
|
||||
placeholder={dgettext("ui", "Add tag")}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
@@ -154,7 +154,7 @@
|
||||
<%= if @post_editor.tag_query_addable? do %>
|
||||
<button class="tag-suggestion create-new" type="button" phx-click="add_post_editor_tag" phx-value-tag={@post_editor.tag_query} phx-target={@myself}>
|
||||
<span class="tag-suggestion-icon">+</span>
|
||||
<span><%= translated("Create tag") %>: <strong><%= @post_editor.tag_query %></strong></span>
|
||||
<span><%= dgettext("ui", "Create tag") %>: <strong><%= @post_editor.tag_query %></strong></span>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -163,12 +163,12 @@
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Author") %></label>
|
||||
<label><%= dgettext("ui", "Author") %></label>
|
||||
<input class="post-editor-input" type="text" name="post_editor[author]" value={@post_editor.form["author"]} />
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Language") %></label>
|
||||
<label><%= dgettext("ui", "Language") %></label>
|
||||
<div class="editor-language-row">
|
||||
<select class="post-editor-input" name="post_editor[language]">
|
||||
<%= for language <- @post_editor.languages do %>
|
||||
@@ -184,7 +184,7 @@
|
||||
phx-target={@myself}
|
||||
disabled={not @post_editor.detect_language_enabled?}
|
||||
>
|
||||
<%= translated("Detect") %>
|
||||
<%= dgettext("ui", "Detect") %>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -193,25 +193,25 @@
|
||||
<label class="editor-checkbox-label">
|
||||
<input type="hidden" name="post_editor[do_not_translate]" value="false" />
|
||||
<input type="checkbox" name="post_editor[do_not_translate]" value="true" checked={@post_editor.form["do_not_translate"]} />
|
||||
<span><%= translated("Do Not Translate") %></span>
|
||||
<span><%= dgettext("ui", "Do Not Translate") %></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="editor-field-row">
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Slug") %></label>
|
||||
<label><%= dgettext("ui", "Slug") %></label>
|
||||
<input class="post-editor-input is-readonly" type="text" readonly value={@post_editor.slug} />
|
||||
</div>
|
||||
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Categories") %></label>
|
||||
<label><%= dgettext("ui", "Categories") %></label>
|
||||
<div class="tag-input-container">
|
||||
<input type="hidden" name="post_editor[categories]" value={@post_editor.form["categories"]} />
|
||||
<div class="tag-input-wrapper">
|
||||
<%= for category <- @post_editor.category_values do %>
|
||||
<span class="tag-chip">
|
||||
<span><%= category %></span>
|
||||
<button class="tag-chip-remove" type="button" phx-click="remove_post_editor_category" phx-value-category={category} phx-target={@myself} aria-label={translated("Remove category")}>×</button>
|
||||
<button class="tag-chip-remove" type="button" phx-click="remove_post_editor_category" phx-value-category={category} phx-target={@myself} aria-label={dgettext("ui", "Remove category")}>×</button>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
type="text"
|
||||
name="post_editor[category_query]"
|
||||
value={@post_editor.category_query}
|
||||
placeholder={translated("Add category")}
|
||||
placeholder={dgettext("ui", "Add category")}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
@@ -236,7 +236,7 @@
|
||||
<%= if @post_editor.category_query_addable? do %>
|
||||
<button class="tag-suggestion create-new" type="button" phx-click="add_post_editor_category" phx-value-category={@post_editor.category_query} phx-target={@myself}>
|
||||
<span class="tag-suggestion-icon">+</span>
|
||||
<span><%= translated("Create category") %>: <strong><%= @post_editor.category_query %></strong></span>
|
||||
<span><%= dgettext("ui", "Create category") %>: <strong><%= @post_editor.category_query %></strong></span>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -247,9 +247,9 @@
|
||||
|
||||
<%= if @post_editor.show_template_selector? do %>
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Template") %></label>
|
||||
<label><%= dgettext("ui", "Template") %></label>
|
||||
<select class="post-editor-input" name="post_editor[template_slug]">
|
||||
<option value=""><%= translated("Default") %></option>
|
||||
<option value=""><%= dgettext("ui", "Default") %></option>
|
||||
<%= for template <- @post_editor.template_options do %>
|
||||
<option value={template.slug} selected={template.slug == @post_editor.form["template_slug"]}><%= template.title %></option>
|
||||
<% end %>
|
||||
@@ -258,10 +258,10 @@
|
||||
<% end %>
|
||||
|
||||
<div class="post-editor-links-panel">
|
||||
<strong><%= translated("Post Links") %></strong>
|
||||
<strong><%= dgettext("ui", "Post Links") %></strong>
|
||||
<div class="post-editor-links-columns">
|
||||
<div>
|
||||
<span class="post-editor-links-label"><%= translated("Backlinks") %></span>
|
||||
<span class="post-editor-links-label"><%= dgettext("ui", "Backlinks") %></span>
|
||||
<%= if Enum.any?(@post_editor.post_links.backlinks) do %>
|
||||
<ul class="editor-list compact">
|
||||
<%= for item <- @post_editor.post_links.backlinks do %>
|
||||
@@ -269,11 +269,11 @@
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<span class="post-editor-empty"><%= translated("No items") %></span>
|
||||
<span class="post-editor-empty"><%= dgettext("ui", "No items") %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
<div>
|
||||
<span class="post-editor-links-label"><%= translated("Links To") %></span>
|
||||
<span class="post-editor-links-label"><%= dgettext("ui", "Links To") %></span>
|
||||
<%= if Enum.any?(@post_editor.post_links.outlinks) do %>
|
||||
<ul class="editor-list compact">
|
||||
<%= for item <- @post_editor.post_links.outlinks do %>
|
||||
@@ -281,7 +281,7 @@
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<span class="post-editor-empty"><%= translated("No items") %></span>
|
||||
<span class="post-editor-empty"><%= dgettext("ui", "No items") %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -290,7 +290,7 @@
|
||||
|
||||
<aside class="editor-media-panel post-editor-side-panel">
|
||||
<div class="post-editor-side-panel-header">
|
||||
<strong><%= translated("Linked Media") %></strong>
|
||||
<strong><%= dgettext("ui", "Linked Media") %></strong>
|
||||
</div>
|
||||
|
||||
<%= if Enum.any?(@post_editor.linked_media) do %>
|
||||
@@ -298,24 +298,24 @@
|
||||
<%= for item <- @post_editor.linked_media do %>
|
||||
<li class="post-editor-media-item">
|
||||
<span class="post-editor-media-title"><%= item.name %></span>
|
||||
<span class="post-editor-media-meta"><%= translated("Order") %>: <%= item.sort_order %></span>
|
||||
<span class="post-editor-media-meta"><%= dgettext("ui", "Order") %>: <%= item.sort_order %></span>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% else %>
|
||||
<div class="post-editor-empty"><%= translated("No linked media") %></div>
|
||||
<div class="post-editor-empty"><%= dgettext("ui", "No linked media") %></div>
|
||||
<% end %>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<button class={["metadata-toggle", if(@post_editor.excerpt_expanded, do: "expanded")]} type="button" phx-click="toggle_post_excerpt" phx-target={@myself}>
|
||||
<span class="metadata-toggle-chevron"><%= if @post_editor.excerpt_expanded, do: "▼", else: "▶" %></span>
|
||||
<span><%= translated("Excerpt") %></span>
|
||||
<span><%= dgettext("ui", "Excerpt") %></span>
|
||||
</button>
|
||||
|
||||
<div class={["editor-excerpt-panel", if(not @post_editor.excerpt_expanded, do: "is-collapsed")]}>
|
||||
<div class="editor-field">
|
||||
<label><%= translated("Excerpt") %></label>
|
||||
<label><%= dgettext("ui", "Excerpt") %></label>
|
||||
<textarea class="post-editor-textarea post-editor-excerpt" name="post_editor[excerpt]" rows="4"><%= @post_editor.form["excerpt"] %></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@@ -323,7 +323,7 @@
|
||||
<div class="editor-body">
|
||||
<div class="editor-toolbar">
|
||||
<div class="editor-toolbar-left">
|
||||
<label><%= translated("Content") %></label>
|
||||
<label><%= dgettext("ui", "Content") %></label>
|
||||
</div>
|
||||
|
||||
<div class="editor-toolbar-center">
|
||||
@@ -351,7 +351,7 @@
|
||||
phx-click="open_overlay"
|
||||
phx-value-kind="insert_link"
|
||||
>
|
||||
<%= translated("Insert Link") %>
|
||||
<%= dgettext("ui", "Insert Link") %>
|
||||
</button>
|
||||
<button
|
||||
class="insert-media-button"
|
||||
@@ -360,7 +360,7 @@
|
||||
phx-click="open_overlay"
|
||||
phx-value-kind="insert_media"
|
||||
>
|
||||
<%= translated("Insert Media") %>
|
||||
<%= dgettext("ui", "Insert Media") %>
|
||||
</button>
|
||||
<% end %>
|
||||
|
||||
@@ -372,7 +372,7 @@
|
||||
phx-click="open_overlay"
|
||||
phx-value-kind="gallery"
|
||||
>
|
||||
<%= translated("Gallery") %> (<%= @post_editor.gallery_count %>)
|
||||
<%= dgettext("ui", "Gallery") %> (<%= @post_editor.gallery_count %>)
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -383,7 +383,7 @@
|
||||
<%= if @post_editor.preview_url do %>
|
||||
<iframe class="editor-preview-frame" src={@post_editor.preview_url}></iframe>
|
||||
<% else %>
|
||||
<div class="post-editor-empty"><%= translated("Preview unavailable") %></div>
|
||||
<div class="post-editor-empty"><%= dgettext("ui", "Preview unavailable") %></div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
@@ -406,10 +406,10 @@
|
||||
</form>
|
||||
|
||||
<div class="editor-footer">
|
||||
<span><strong><%= translated("Created") %>:</strong> <%= @post_editor.footer.created_at %></span>
|
||||
<span><strong><%= translated("Updated") %>:</strong> <%= @post_editor.footer.updated_at %></span>
|
||||
<span><strong><%= dgettext("ui", "Created") %>:</strong> <%= @post_editor.footer.created_at %></span>
|
||||
<span><strong><%= dgettext("ui", "Updated") %>:</strong> <%= @post_editor.footer.updated_at %></span>
|
||||
<%= if @post_editor.footer.published_at do %>
|
||||
<span><strong><%= translated("Published") %>:</strong> <%= @post_editor.footer.published_at %></span>
|
||||
<span><strong><%= dgettext("ui", "Published") %>:</strong> <%= @post_editor.footer.published_at %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,8 +4,8 @@ defmodule BDS.Desktop.ShellLive.ScriptEditor do
|
||||
use Phoenix.LiveComponent
|
||||
|
||||
alias BDS.{Scripts, Scripting}
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Scripts.Script
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
embed_templates("script_editor_html/*")
|
||||
|
||||
@@ -115,18 +115,18 @@ defmodule BDS.Desktop.ShellLive.ScriptEditor do
|
||||
socket
|
||||
|> assign(:draft, nil)
|
||||
|> build_data()
|
||||
|> notify_output(translated("Scripts"), translated("Script saved"))
|
||||
|> notify_output(dgettext("ui", "Scripts"), dgettext("ui", "Script saved"))
|
||||
|> notify_reload()
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> notify_output(translated("Scripts"), inspect(reason), "error")
|
||||
|> notify_output(dgettext("ui", "Scripts"), inspect(reason), "error")
|
||||
|> notify_reload()
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> notify_output(translated("Scripts"), inspect(reason), "error")
|
||||
|> notify_output(dgettext("ui", "Scripts"), inspect(reason), "error")
|
||||
|> notify_reload()
|
||||
end
|
||||
end
|
||||
@@ -151,24 +151,24 @@ defmodule BDS.Desktop.ShellLive.ScriptEditor do
|
||||
socket
|
||||
|> assign(:draft, nil)
|
||||
|> build_data()
|
||||
|> notify_output(translated("Scripts"), translated("Script published"))
|
||||
|> notify_output(dgettext("ui", "Scripts"), dgettext("ui", "Script published"))
|
||||
|> notify_reload()
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> notify_output(translated("Scripts"), inspect(reason), "error")
|
||||
|> notify_output(dgettext("ui", "Scripts"), inspect(reason), "error")
|
||||
|> notify_reload()
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> notify_output(translated("Scripts"), inspect(reason), "error")
|
||||
|> notify_output(dgettext("ui", "Scripts"), inspect(reason), "error")
|
||||
|> notify_reload()
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> notify_output(translated("Scripts"), inspect(reason), "error")
|
||||
|> notify_output(dgettext("ui", "Scripts"), inspect(reason), "error")
|
||||
|> notify_reload()
|
||||
end
|
||||
end
|
||||
@@ -184,10 +184,10 @@ defmodule BDS.Desktop.ShellLive.ScriptEditor do
|
||||
%Script{} = script ->
|
||||
case Scripting.validate(current_draft(socket.assigns, script)["content"] || "") do
|
||||
:ok ->
|
||||
notify_output(socket, translated("Scripts"), translated("Syntax is valid"))
|
||||
notify_output(socket, dgettext("ui", "Scripts"), dgettext("ui", "Syntax is valid"))
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Scripts"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Scripts"), inspect(reason), "error")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -209,10 +209,10 @@ defmodule BDS.Desktop.ShellLive.ScriptEditor do
|
||||
[]
|
||||
) do
|
||||
{:ok, result} ->
|
||||
notify_output(socket, translated("Scripts"), inspect(result))
|
||||
notify_output(socket, dgettext("ui", "Scripts"), inspect(result))
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(socket, translated("Scripts"), inspect(reason), "error")
|
||||
notify_output(socket, dgettext("ui", "Scripts"), inspect(reason), "error")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -227,7 +227,7 @@ defmodule BDS.Desktop.ShellLive.ScriptEditor do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> notify_output(translated("Scripts"), inspect(reason), "error")
|
||||
|> notify_output(dgettext("ui", "Scripts"), inspect(reason), "error")
|
||||
|> notify_reload()
|
||||
end
|
||||
end
|
||||
@@ -287,7 +287,4 @@ defmodule BDS.Desktop.ShellLive.ScriptEditor do
|
||||
send(self(), :reload_shell)
|
||||
socket
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
@@ -7,30 +7,30 @@
|
||||
"status-#{@script_editor.status}"
|
||||
]} data-testid="script-status-badge"><%= BDS.Desktop.ShellData.dashboard_status_label(@script_editor.status) %></span>
|
||||
<%= if @script_editor.can_publish? do %>
|
||||
<button class="success" data-testid="script-publish-button" type="button" phx-click="publish_script_editor" phx-target={@myself}><%= BDS.Desktop.ShellData.translate("Publish", %{}, BDS.Desktop.UILocale.current()) %></button>
|
||||
<button class="success" data-testid="script-publish-button" type="button" phx-click="publish_script_editor" phx-target={@myself}><%= dgettext("ui", "Publish") %></button>
|
||||
<% end %>
|
||||
<button class="secondary scripts-save-button" type="button" phx-click="save_script_editor" phx-target={@myself}><%= BDS.Desktop.ShellData.translate("Save", %{}, BDS.Desktop.UILocale.current()) %></button>
|
||||
<button class="secondary scripts-run-button" type="button" phx-click="run_script_editor" phx-target={@myself}><%= BDS.Desktop.ShellData.translate("Run", %{}, BDS.Desktop.UILocale.current()) %></button>
|
||||
<button class="secondary scripts-check-button" type="button" phx-click="check_script_editor" phx-target={@myself}><%= BDS.Desktop.ShellData.translate("Check Syntax", %{}, BDS.Desktop.UILocale.current()) %></button>
|
||||
<button class="secondary danger" type="button" phx-click="delete_script_editor" phx-target={@myself}><%= BDS.Desktop.ShellData.translate("Delete", %{}, BDS.Desktop.UILocale.current()) %></button>
|
||||
<button class="secondary scripts-save-button" type="button" phx-click="save_script_editor" phx-target={@myself}><%= dgettext("ui", "Save") %></button>
|
||||
<button class="secondary scripts-run-button" type="button" phx-click="run_script_editor" phx-target={@myself}><%= dgettext("ui", "Run") %></button>
|
||||
<button class="secondary scripts-check-button" type="button" phx-click="check_script_editor" phx-target={@myself}><%= dgettext("ui", "Check Syntax") %></button>
|
||||
<button class="secondary danger" type="button" phx-click="delete_script_editor" phx-target={@myself}><%= dgettext("ui", "Delete") %></button>
|
||||
</div>
|
||||
</div>
|
||||
<form class="editor-content scripts-view" phx-change="change_script_editor" phx-target={@myself}>
|
||||
<div class="editor-header-row scripts-meta-row">
|
||||
<div class="editor-meta">
|
||||
<div class="editor-field-row">
|
||||
<div class="editor-field"><label><%= BDS.Desktop.ShellData.translate("Title", %{}, BDS.Desktop.UILocale.current()) %></label><input type="text" name="script_editor[title]" value={@script_editor.title} /></div>
|
||||
<div class="editor-field"><label><%= BDS.Desktop.ShellData.translate("Slug", %{}, BDS.Desktop.UILocale.current()) %></label><input type="text" name="script_editor[slug]" value={@script_editor.slug} /></div>
|
||||
<div class="editor-field"><label><%= dgettext("ui", "Title") %></label><input type="text" name="script_editor[title]" value={@script_editor.title} /></div>
|
||||
<div class="editor-field"><label><%= dgettext("ui", "Slug") %></label><input type="text" name="script_editor[slug]" value={@script_editor.slug} /></div>
|
||||
</div>
|
||||
<div class="editor-field-row">
|
||||
<div class="editor-field"><label><%= BDS.Desktop.ShellData.translate("Kind", %{}, BDS.Desktop.UILocale.current()) %></label><select name="script_editor[kind]"><option value="utility" selected={@script_editor.kind == "utility"}>utility</option><option value="macro" selected={@script_editor.kind == "macro"}>macro</option><option value="transform" selected={@script_editor.kind == "transform"}>transform</option></select></div>
|
||||
<div class="editor-field"><label><%= BDS.Desktop.ShellData.translate("Entrypoint", %{}, BDS.Desktop.UILocale.current()) %></label><select name="script_editor[entrypoint]"><%= for entrypoint <- @script_editor.entrypoints do %><option value={entrypoint} selected={entrypoint == @script_editor.entrypoint}><%= entrypoint %></option><% end %></select></div>
|
||||
<div class="editor-field scripts-enabled-field"><label><input type="checkbox" name="script_editor[enabled]" checked={@script_editor.enabled} /> <%= BDS.Desktop.ShellData.translate("Enabled", %{}, BDS.Desktop.UILocale.current()) %></label></div>
|
||||
<div class="editor-field"><label><%= dgettext("ui", "Kind") %></label><select name="script_editor[kind]"><option value="utility" selected={@script_editor.kind == "utility"}>utility</option><option value="macro" selected={@script_editor.kind == "macro"}>macro</option><option value="transform" selected={@script_editor.kind == "transform"}>transform</option></select></div>
|
||||
<div class="editor-field"><label><%= dgettext("ui", "Entrypoint") %></label><select name="script_editor[entrypoint]"><%= for entrypoint <- @script_editor.entrypoints do %><option value={entrypoint} selected={entrypoint == @script_editor.entrypoint}><%= entrypoint %></option><% end %></select></div>
|
||||
<div class="editor-field scripts-enabled-field"><label><input type="checkbox" name="script_editor[enabled]" checked={@script_editor.enabled} /> <%= dgettext("ui", "Enabled") %></label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-body scripts-editor">
|
||||
<div class="editor-toolbar scripts-toolbar"><div class="editor-toolbar-left"><label><%= BDS.Desktop.ShellData.translate("Content", %{}, BDS.Desktop.UILocale.current()) %></label></div></div>
|
||||
<div class="editor-toolbar scripts-toolbar"><div class="editor-toolbar-left"><label><%= dgettext("ui", "Content") %></label></div></div>
|
||||
<div
|
||||
id={"script-editor-monaco-shell-#{@script_editor.id}"}
|
||||
class="scripts-monaco monaco-editor-shell"
|
||||
@@ -44,6 +44,6 @@
|
||||
<textarea id={"script-editor-content-#{@script_editor.id}"} class="monaco-editor-input code-editor-textarea" name="script_editor[content]" spellcheck="false"><%= @script_editor.content %></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-footer"><span class="text-muted text-small"><%= BDS.Desktop.ShellData.translate("Created", %{}, BDS.Desktop.UILocale.current()) %>: <%= BDS.Persistence.timestamp_to_iso8601(@script_editor.created_at) %></span><span class="text-muted text-small"><%= BDS.Desktop.ShellData.translate("Updated", %{}, BDS.Desktop.UILocale.current()) %>: <%= BDS.Persistence.timestamp_to_iso8601(@script_editor.updated_at) %></span></div>
|
||||
<div class="editor-footer"><span class="text-muted text-small"><%= dgettext("ui", "Created") %>: <%= BDS.Persistence.timestamp_to_iso8601(@script_editor.created_at) %></span><span class="text-muted text-small"><%= dgettext("ui", "Updated") %>: <%= BDS.Persistence.timestamp_to_iso8601(@script_editor.updated_at) %></span></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,10 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor do
|
||||
@moduledoc false
|
||||
|
||||
use Phoenix.LiveComponent
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Repo
|
||||
alias BDS.Templates.Template
|
||||
|
||||
@@ -386,7 +386,4 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor do
|
||||
defp section_matches?(query, keywords),
|
||||
do: Enum.any?(keywords, &String.contains?(&1, String.downcase(query)))
|
||||
|
||||
@spec translated(term(), term()) :: term()
|
||||
def translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -4,8 +4,8 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
|
||||
use Phoenix.Component
|
||||
|
||||
alias BDS.AI
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Desktop.ShellLive.SettingsEditor.EditorSettings
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec ai_form(term()) :: term()
|
||||
def ai_form(assigns) do
|
||||
@@ -88,7 +88,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
|
||||
else
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("AI Settings"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "AI Settings"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -153,7 +153,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
|
||||
else
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("AI Settings"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "AI Settings"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -168,7 +168,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("AI Settings"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "AI Settings"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -315,7 +315,4 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.AISettings do
|
||||
trimmed -> trimmed
|
||||
end
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.EditorSettings do
|
||||
use Phoenix.Component
|
||||
|
||||
alias BDS.Settings
|
||||
alias BDS.Desktop.ShellData
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec editor_form() :: term()
|
||||
def editor_form do
|
||||
@@ -42,7 +42,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.EditorSettings do
|
||||
else
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Editor Settings"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Editor Settings"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -80,7 +80,4 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.EditorSettings do
|
||||
defp truthy?(value), do: value in [true, "true", "on", "1", 1]
|
||||
defp boolean_string(true), do: "true"
|
||||
defp boolean_string(false), do: "false"
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ManagedCategories do
|
||||
use Phoenix.Component
|
||||
|
||||
alias BDS.Metadata
|
||||
alias BDS.Desktop.ShellData
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@protected_categories MapSet.new(["article", "aside", "page", "picture"])
|
||||
@default_category_settings %{
|
||||
@@ -56,8 +56,8 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ManagedCategories do
|
||||
name == "" ->
|
||||
socket
|
||||
|> append_output.(
|
||||
translated("Categories"),
|
||||
translated("Category name is required"),
|
||||
dgettext("ui", "Categories"),
|
||||
dgettext("ui", "Category name is required"),
|
||||
nil,
|
||||
"error"
|
||||
)
|
||||
@@ -72,7 +72,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ManagedCategories do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Categories"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Categories"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -104,7 +104,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ManagedCategories do
|
||||
else
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Categories"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Categories"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -128,7 +128,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ManagedCategories do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Categories"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Categories"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -141,8 +141,8 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ManagedCategories do
|
||||
MapSet.member?(@protected_categories, category) ->
|
||||
socket
|
||||
|> append_output.(
|
||||
translated("Categories"),
|
||||
translated("Protected categories cannot be removed"),
|
||||
dgettext("ui", "Categories"),
|
||||
dgettext("ui", "Protected categories cannot be removed"),
|
||||
nil,
|
||||
"error"
|
||||
)
|
||||
@@ -155,7 +155,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ManagedCategories do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Categories"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Categories"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -197,7 +197,4 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ManagedCategories do
|
||||
trimmed -> trimmed
|
||||
end
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -3,8 +3,8 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.MCPConfig do
|
||||
|
||||
use Phoenix.Component
|
||||
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.MCP.AgentConfig
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@mcp_agents [
|
||||
%{id: :claude_code, label: "Claude Code", supported?: true},
|
||||
@@ -47,15 +47,15 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.MCPConfig do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("MCP"), format_config_error(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "MCP"), format_config_error(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
|
||||
_other ->
|
||||
socket
|
||||
|> append_output.(
|
||||
translated("MCP"),
|
||||
translated("This MCP agent is not supported in the rewrite yet"),
|
||||
dgettext("ui", "MCP"),
|
||||
dgettext("ui", "This MCP agent is not supported in the rewrite yet"),
|
||||
nil,
|
||||
"error"
|
||||
)
|
||||
@@ -70,28 +70,28 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.MCPConfig do
|
||||
end
|
||||
|
||||
defp format_config_error({:read_config, path, reason}) do
|
||||
translated("Could not read MCP config %{path}: %{reason}",
|
||||
dgettext("ui", "Could not read MCP config %{path}: %{reason}",
|
||||
path: path,
|
||||
reason: inspect(reason)
|
||||
)
|
||||
end
|
||||
|
||||
defp format_config_error({:write_config, path, reason}) do
|
||||
translated("Could not write MCP config %{path}: %{reason}",
|
||||
dgettext("ui", "Could not write MCP config %{path}: %{reason}",
|
||||
path: path,
|
||||
reason: inspect(reason)
|
||||
)
|
||||
end
|
||||
|
||||
defp format_config_error({:create_config_dir, path, reason}) do
|
||||
translated("Could not create MCP config folder %{path}: %{reason}",
|
||||
dgettext("ui", "Could not create MCP config folder %{path}: %{reason}",
|
||||
path: path,
|
||||
reason: inspect(reason)
|
||||
)
|
||||
end
|
||||
|
||||
defp format_config_error({:decode_config, path, _reason}) do
|
||||
translated("Could not parse MCP config %{path}", path: path)
|
||||
dgettext("ui", "Could not parse MCP config %{path}", path: path)
|
||||
end
|
||||
|
||||
defp mcp_configured?(%{supported?: false}), do: false
|
||||
@@ -124,7 +124,4 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.MCPConfig do
|
||||
|> Map.get("mcpServers", %{})
|
||||
|> Map.has_key?("bDS")
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ProjectSettings do
|
||||
use Phoenix.Component
|
||||
|
||||
alias BDS.Metadata
|
||||
alias BDS.Desktop.ShellData
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec project_metadata(term()) :: term()
|
||||
def project_metadata(assigns) do
|
||||
@@ -56,7 +56,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ProjectSettings do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Settings"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Settings"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -111,7 +111,4 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.ProjectSettings do
|
||||
trimmed -> trimmed
|
||||
end
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.PublishingSettings do
|
||||
use Phoenix.Component
|
||||
|
||||
alias BDS.Metadata
|
||||
alias BDS.Desktop.ShellData
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec publishing_form(term()) :: term()
|
||||
def publishing_form(metadata) do
|
||||
@@ -37,7 +37,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.PublishingSettings do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Publishing"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Publishing"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -54,7 +54,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.PublishingSettings do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Publishing"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Publishing"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -87,7 +87,4 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.PublishingSettings do
|
||||
trimmed -> trimmed
|
||||
end
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.StyleEditor do
|
||||
use Phoenix.Component
|
||||
|
||||
alias BDS.Metadata
|
||||
alias BDS.Desktop.ShellData
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@themes [
|
||||
"default",
|
||||
@@ -71,7 +71,7 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.StyleEditor do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Style"), inspect(reason), nil, "error")
|
||||
|> append_output.(dgettext("ui", "Style"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -104,7 +104,4 @@ defmodule BDS.Desktop.ShellLive.SettingsEditor.StyleEditor do
|
||||
dark_bg_color: "#0f172a"
|
||||
}
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -8,51 +8,51 @@
|
||||
>
|
||||
<div class="settings-view">
|
||||
<div class="settings-header">
|
||||
<h2 data-testid="editor-title"><%= translated("Settings") %></h2>
|
||||
<h2 data-testid="editor-title"><%= dgettext("ui", "Settings") %></h2>
|
||||
<form class="settings-search" phx-change="change_settings_search" phx-target={@myself}>
|
||||
<input type="text" name="query" value={@settings_editor.search_query} placeholder={translated("Search settings")} />
|
||||
<input type="text" name="query" value={@settings_editor.search_query} placeholder={dgettext("ui", "Search settings")} />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="settings-content">
|
||||
<%= if Enum.empty?(@settings_editor.active_sections) do %>
|
||||
<div class="settings-no-results">
|
||||
<p><%= translated("No settings match the current search") %></p>
|
||||
<p><%= dgettext("ui", "No settings match the current search") %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if @settings_editor.project_visible? do %>
|
||||
<div class="setting-section" id="settings-section-project">
|
||||
<div class="setting-section-header">
|
||||
<h3><%= translated("Project") %></h3>
|
||||
<p class="setting-section-description"><%= translated("Blog identity, URLs, authoring defaults, and bookmarklet setup") %></p>
|
||||
<h3><%= dgettext("ui", "Project") %></h3>
|
||||
<p class="setting-section-description"><%= dgettext("ui", "Blog identity, URLs, authoring defaults, and bookmarklet setup") %></p>
|
||||
</div>
|
||||
<form class="setting-section-content" phx-change="change_settings_project" phx-target={@myself}>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<label class="setting-label"><%= translated("Project Name") %></label>
|
||||
<label class="setting-label"><%= dgettext("ui", "Project Name") %></label>
|
||||
</div>
|
||||
<div class="setting-control"><input type="text" name="settings_project[name]" value={@settings_editor.project["name"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Description") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Description") %></label></div>
|
||||
<div class="setting-control"><textarea name="settings_project[description]" rows="3"><%= @settings_editor.project["description"] %></textarea></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Data Path") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Data Path") %></label></div>
|
||||
<div class="setting-control">
|
||||
<div class="setting-input-group">
|
||||
<input type="text" value={@settings_editor.project_data_path} readonly />
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="open_data_folder"><%= translated("Open") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="open_data_folder"><%= dgettext("ui", "Open") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Public URL") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Public URL") %></label></div>
|
||||
<div class="setting-control"><input type="url" name="settings_project[public_url]" value={@settings_editor.project["public_url"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Main Language") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Main Language") %></label></div>
|
||||
<div class="setting-control">
|
||||
<select name="settings_project[main_language]">
|
||||
<%= for language <- @settings_editor.supported_languages do %>
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Blog Languages") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Blog Languages") %></label></div>
|
||||
<div class="setting-control">
|
||||
<div class="setting-input-group">
|
||||
<%= for language <- @settings_editor.supported_languages do %>
|
||||
@@ -75,15 +75,15 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Default Author") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Default Author") %></label></div>
|
||||
<div class="setting-control"><input type="text" name="settings_project[default_author]" value={@settings_editor.project["default_author"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Max Posts Per Page") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Max Posts Per Page") %></label></div>
|
||||
<div class="setting-control"><input type="number" min="1" max="500" name="settings_project[max_posts_per_page]" value={@settings_editor.project["max_posts_per_page"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Blogmark Category") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Blogmark Category") %></label></div>
|
||||
<div class="setting-control">
|
||||
<select name="settings_project[blogmark_category]">
|
||||
<%= for category <- Enum.map(@settings_editor.categories, & &1.name) do %>
|
||||
@@ -93,67 +93,67 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Blogmark Bookmarklet") %></label></div>
|
||||
<div class="setting-control"><p class="setting-description"><%= translated("Bookmarklet copy support is wired through the desktop runtime and project public URL.") %></p></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Blogmark Bookmarklet") %></label></div>
|
||||
<div class="setting-control"><p class="setting-description"><%= dgettext("ui", "Bookmarklet copy support is wired through the desktop runtime and project public URL.") %></p></div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_project" phx-target={@myself}><%= translated("Save") %></button></div>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_project" phx-target={@myself}><%= dgettext("ui", "Save") %></button></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if @settings_editor.editor_visible? do %>
|
||||
<div class="setting-section" id="settings-section-editor">
|
||||
<div class="setting-section-header">
|
||||
<h3><%= translated("Editor") %></h3>
|
||||
<p class="setting-section-description"><%= translated("Default editing mode and diff presentation") %></p>
|
||||
<h3><%= dgettext("ui", "Editor") %></h3>
|
||||
<p class="setting-section-description"><%= dgettext("ui", "Default editing mode and diff presentation") %></p>
|
||||
</div>
|
||||
<form class="setting-section-content" phx-change="change_settings_editor" phx-target={@myself}>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Default Editor Mode") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Default Editor Mode") %></label></div>
|
||||
<div class="setting-control">
|
||||
<select name="settings_editor[default_mode]">
|
||||
<option value="wysiwyg" selected={@settings_editor.editor["default_mode"] == "wysiwyg"}><%= translated("WYSIWYG") %></option>
|
||||
<option value="markdown" selected={@settings_editor.editor["default_mode"] == "markdown"}><%= translated("Markdown") %></option>
|
||||
<option value="preview" selected={@settings_editor.editor["default_mode"] == "preview"}><%= translated("Preview") %></option>
|
||||
<option value="wysiwyg" selected={@settings_editor.editor["default_mode"] == "wysiwyg"}><%= dgettext("ui", "WYSIWYG") %></option>
|
||||
<option value="markdown" selected={@settings_editor.editor["default_mode"] == "markdown"}><%= dgettext("ui", "Markdown") %></option>
|
||||
<option value="preview" selected={@settings_editor.editor["default_mode"] == "preview"}><%= dgettext("ui", "Preview") %></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Diff View Style") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Diff View Style") %></label></div>
|
||||
<div class="setting-control">
|
||||
<select name="settings_editor[diff_view_style]">
|
||||
<option value="inline" selected={@settings_editor.editor["diff_view_style"] == "inline"}><%= translated("Inline") %></option>
|
||||
<option value="side-by-side" selected={@settings_editor.editor["diff_view_style"] == "side-by-side"}><%= translated("Side by Side") %></option>
|
||||
<option value="inline" selected={@settings_editor.editor["diff_view_style"] == "inline"}><%= dgettext("ui", "Inline") %></option>
|
||||
<option value="side-by-side" selected={@settings_editor.editor["diff_view_style"] == "side-by-side"}><%= dgettext("ui", "Side by Side") %></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Wrap Long Lines") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_editor[wrap_long_lines]" checked={@settings_editor.editor["wrap_long_lines"]} /> <%= translated("Enable line wrapping in diffs") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Wrap Long Lines") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_editor[wrap_long_lines]" checked={@settings_editor.editor["wrap_long_lines"]} /> <%= dgettext("ui", "Enable line wrapping in diffs") %></label></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Hide Unchanged Regions") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_editor[hide_unchanged_regions]" checked={@settings_editor.editor["hide_unchanged_regions"]} /> <%= translated("Collapse unchanged diff hunks") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Hide Unchanged Regions") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_editor[hide_unchanged_regions]" checked={@settings_editor.editor["hide_unchanged_regions"]} /> <%= dgettext("ui", "Collapse unchanged diff hunks") %></label></div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_editor" phx-target={@myself}><%= translated("Save") %></button></div>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_editor" phx-target={@myself}><%= dgettext("ui", "Save") %></button></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if @settings_editor.content_visible? do %>
|
||||
<div class="setting-section" id="settings-section-content">
|
||||
<div class="setting-section-header"><h3><%= translated("Content Categories") %></h3><p class="setting-section-description"><%= translated("Category defaults, rendering flags, and template wiring") %></p></div>
|
||||
<div class="setting-section-header"><h3><%= dgettext("ui", "Content Categories") %></h3><p class="setting-section-description"><%= dgettext("ui", "Category defaults, rendering flags, and template wiring") %></p></div>
|
||||
<div class="setting-section-content">
|
||||
<table class="categories-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= translated("Category") %></th>
|
||||
<th><%= translated("Title") %></th>
|
||||
<th><%= translated("Render in Lists") %></th>
|
||||
<th><%= translated("Show Titles") %></th>
|
||||
<th><%= translated("Post Template") %></th>
|
||||
<th><%= translated("List Template") %></th>
|
||||
<th><%= translated("Actions") %></th>
|
||||
<th><%= dgettext("ui", "Category") %></th>
|
||||
<th><%= dgettext("ui", "Title") %></th>
|
||||
<th><%= dgettext("ui", "Render in Lists") %></th>
|
||||
<th><%= dgettext("ui", "Show Titles") %></th>
|
||||
<th><%= dgettext("ui", "Post Template") %></th>
|
||||
<th><%= dgettext("ui", "List Template") %></th>
|
||||
<th><%= dgettext("ui", "Actions") %></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -167,7 +167,7 @@
|
||||
<td><input type="checkbox" name="category_settings[show_title]" checked={category.show_title} form={"category-form-#{category.name}"} /></td>
|
||||
<td>
|
||||
<select name="category_settings[post_template_slug]" form={"category-form-#{category.name}"}>
|
||||
<option value=""><%= translated("Default") %></option>
|
||||
<option value=""><%= dgettext("ui", "Default") %></option>
|
||||
<%= for template <- @settings_editor.template_options.post do %>
|
||||
<option value={template.slug} selected={template.slug == category.post_template_slug}><%= template.title %></option>
|
||||
<% end %>
|
||||
@@ -175,7 +175,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<select name="category_settings[list_template_slug]" form={"category-form-#{category.name}"}>
|
||||
<option value=""><%= translated("Default") %></option>
|
||||
<option value=""><%= dgettext("ui", "Default") %></option>
|
||||
<%= for template <- @settings_editor.template_options.list do %>
|
||||
<option value={template.slug} selected={template.slug == category.list_template_slug}><%= template.title %></option>
|
||||
<% end %>
|
||||
@@ -186,8 +186,8 @@
|
||||
<form id={"category-form-#{category.name}"} phx-submit="save_settings_category" phx-target={@myself}>
|
||||
<input type="hidden" name="category_settings[category]" value={category.name} />
|
||||
</form>
|
||||
<button class="secondary" type="submit" form={"category-form-#{category.name}"}><%= translated("Save") %></button>
|
||||
<button class="secondary" type="button" phx-click="remove_settings_category" phx-target={@myself} phx-value-category={category.name} disabled={category.protected?}><%= translated("Remove") %></button>
|
||||
<button class="secondary" type="submit" form={"category-form-#{category.name}"}><%= dgettext("ui", "Save") %></button>
|
||||
<button class="secondary" type="button" phx-click="remove_settings_category" phx-target={@myself} phx-value-category={category.name} disabled={category.protected?}><%= dgettext("ui", "Remove") %></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -195,103 +195,103 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Add Category") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Add Category") %></label></div>
|
||||
<div class="setting-control">
|
||||
<div class="setting-input-group">
|
||||
<input type="text" value={@settings_editor.new_category} phx-change="change_settings_new_category" phx-target={@myself} name="name" />
|
||||
<button class="primary" type="button" phx-click="add_settings_category" phx-target={@myself}><%= translated("Add") %></button>
|
||||
<button class="primary" type="button" phx-click="add_settings_category" phx-target={@myself}><%= dgettext("ui", "Add") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-actions"><button class="secondary" type="button" phx-click="reset_settings_categories" phx-target={@myself}><%= translated("Reset to Defaults") %></button></div>
|
||||
<div class="setting-actions"><button class="secondary" type="button" phx-click="reset_settings_categories" phx-target={@myself}><%= dgettext("ui", "Reset to Defaults") %></button></div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if @settings_editor.ai_visible? do %>
|
||||
<div class="setting-section" id="settings-section-ai">
|
||||
<div class="setting-section-header"><h3><%= translated("AI") %></h3><p class="setting-section-description"><%= translated("OpenAI-compatible endpoints, model routing, airplane mode, and system prompt") %></p></div>
|
||||
<div class="setting-section-header"><h3><%= dgettext("ui", "AI") %></h3><p class="setting-section-description"><%= dgettext("ui", "OpenAI-compatible endpoints, model routing, airplane mode, and system prompt") %></p></div>
|
||||
<form class="setting-section-content" phx-change="change_settings_ai" phx-target={@myself}>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Online Endpoint URL") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Online Endpoint URL") %></label></div>
|
||||
<div class="setting-control">
|
||||
<div class="setting-input-group">
|
||||
<input type="url" name="settings_ai[online_url]" value={@settings_editor.ai["online_url"]} />
|
||||
<button class="secondary" type="button" phx-click="refresh_settings_ai_models" phx-target={@myself} phx-value-endpoint="online"><%= translated("Refresh Online Models") %></button>
|
||||
<button class="secondary" type="button" phx-click="refresh_settings_ai_models" phx-target={@myself} phx-value-endpoint="online"><%= dgettext("ui", "Refresh Online Models") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Online API Key") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Online API Key") %></label></div>
|
||||
<div class="setting-control"><input type="password" name="settings_ai[online_api_key]" value={@settings_editor.ai["online_api_key"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Online Chat Model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Online Chat Model") %></label></div>
|
||||
<div class="setting-control"><input type="text" list="settings-ai-online-models" name="settings_ai[online_chat_model]" value={@settings_editor.ai["online_chat_model"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Online Chat Tools") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[online_chat_tools]" checked={@settings_editor.ai["online_chat_tools"]} /> <%= translated("Enable tool calls for the online chat model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Online Chat Tools") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[online_chat_tools]" checked={@settings_editor.ai["online_chat_tools"]} /> <%= dgettext("ui", "Enable tool calls for the online chat model") %></label></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Online Chat Reasoning") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[online_chat_disable_reasoning]" checked={@settings_editor.ai["online_chat_disable_reasoning"]} /> <%= translated("Disable reasoning output for the online chat model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Online Chat Reasoning") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[online_chat_disable_reasoning]" checked={@settings_editor.ai["online_chat_disable_reasoning"]} /> <%= dgettext("ui", "Disable reasoning output for the online chat model") %></label></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Online Title Model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Online Title Model") %></label></div>
|
||||
<div class="setting-control"><input type="text" list="settings-ai-online-models" name="settings_ai[online_title_model]" value={@settings_editor.ai["online_title_model"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Online Image Analysis Model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Online Image Analysis Model") %></label></div>
|
||||
<div class="setting-control"><input type="text" list="settings-ai-online-models" name="settings_ai[online_image_analysis_model]" value={@settings_editor.ai["online_image_analysis_model"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Online Image Support") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[online_chat_images]" checked={@settings_editor.ai["online_chat_images"]} /> <%= translated("Enable image analysis for the online image analysis model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Online Image Support") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[online_chat_images]" checked={@settings_editor.ai["online_chat_images"]} /> <%= dgettext("ui", "Enable image analysis for the online image analysis model") %></label></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Offline Endpoint URL") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Offline Endpoint URL") %></label></div>
|
||||
<div class="setting-control">
|
||||
<div class="setting-input-group">
|
||||
<input type="url" name="settings_ai[offline_url]" value={@settings_editor.ai["offline_url"]} />
|
||||
<button class="secondary" type="button" phx-click="refresh_settings_ai_models" phx-target={@myself} phx-value-endpoint="airplane"><%= translated("Refresh Offline Models") %></button>
|
||||
<button class="secondary" type="button" phx-click="refresh_settings_ai_models" phx-target={@myself} phx-value-endpoint="airplane"><%= dgettext("ui", "Refresh Offline Models") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Offline API Key") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Offline API Key") %></label></div>
|
||||
<div class="setting-control"><input type="password" name="settings_ai[offline_api_key]" value={@settings_editor.ai["offline_api_key"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Airplane Mode") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[offline_mode]" checked={@settings_editor.ai["offline_mode"]} /> <%= translated("Route AI tasks through the airplane endpoint") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Airplane Mode") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[offline_mode]" checked={@settings_editor.ai["offline_mode"]} /> <%= dgettext("ui", "Route AI tasks through the airplane endpoint") %></label></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Offline Chat Model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Offline Chat Model") %></label></div>
|
||||
<div class="setting-control"><input type="text" list="settings-ai-offline-models" name="settings_ai[offline_chat_model]" value={@settings_editor.ai["offline_chat_model"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Offline Chat Tools") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[offline_chat_tools]" checked={@settings_editor.ai["offline_chat_tools"]} /> <%= translated("Enable tool calls for the offline chat model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Offline Chat Tools") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[offline_chat_tools]" checked={@settings_editor.ai["offline_chat_tools"]} /> <%= dgettext("ui", "Enable tool calls for the offline chat model") %></label></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Offline Chat Reasoning") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[offline_chat_disable_reasoning]" checked={@settings_editor.ai["offline_chat_disable_reasoning"]} /> <%= translated("Disable reasoning output for the offline chat model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Offline Chat Reasoning") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[offline_chat_disable_reasoning]" checked={@settings_editor.ai["offline_chat_disable_reasoning"]} /> <%= dgettext("ui", "Disable reasoning output for the offline chat model") %></label></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Offline Title Model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Offline Title Model") %></label></div>
|
||||
<div class="setting-control"><input type="text" list="settings-ai-offline-models" name="settings_ai[offline_title_model]" value={@settings_editor.ai["offline_title_model"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Offline Image Analysis Model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Offline Image Analysis Model") %></label></div>
|
||||
<div class="setting-control"><input type="text" list="settings-ai-offline-models" name="settings_ai[offline_image_analysis_model]" value={@settings_editor.ai["offline_image_analysis_model"]} /></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Offline Image Support") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[offline_chat_images]" checked={@settings_editor.ai["offline_chat_images"]} /> <%= translated("Enable image analysis for the offline image analysis model") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Offline Image Support") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_ai[offline_chat_images]" checked={@settings_editor.ai["offline_chat_images"]} /> <%= dgettext("ui", "Enable image analysis for the offline image analysis model") %></label></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("System Prompt") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "System Prompt") %></label></div>
|
||||
<div class="setting-control"><textarea name="settings_ai[system_prompt]" rows="12"><%= @settings_editor.ai["system_prompt"] %></textarea></div>
|
||||
</div>
|
||||
<datalist id="settings-ai-online-models">
|
||||
@@ -305,53 +305,53 @@
|
||||
<% end %>
|
||||
</datalist>
|
||||
</form>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_ai" phx-target={@myself}><%= translated("Save") %></button><button class="secondary" type="button" phx-click="reset_settings_ai_prompt" phx-target={@myself}><%= translated("Reset to Default") %></button></div>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_ai" phx-target={@myself}><%= dgettext("ui", "Save") %></button><button class="secondary" type="button" phx-click="reset_settings_ai_prompt" phx-target={@myself}><%= dgettext("ui", "Reset to Default") %></button></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if @settings_editor.technology_visible? do %>
|
||||
<div class="setting-section" id="settings-section-technology">
|
||||
<div class="setting-section-header"><h3><%= translated("Technology") %></h3><p class="setting-section-description"><%= translated("Application-level runtime behavior and semantic indexing") %></p></div>
|
||||
<div class="setting-section-header"><h3><%= dgettext("ui", "Technology") %></h3><p class="setting-section-description"><%= dgettext("ui", "Application-level runtime behavior and semantic indexing") %></p></div>
|
||||
<form class="setting-section-content" phx-change="change_settings_project" phx-target={@myself}>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Semantic Similarity") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_project[semantic_similarity_enabled]" checked={@settings_editor.technology["semantic_similarity_enabled"]} /> <%= translated("Enable duplicate search and related-post embeddings") %></label></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Semantic Similarity") %></label></div>
|
||||
<div class="setting-control"><label><input type="checkbox" name="settings_project[semantic_similarity_enabled]" checked={@settings_editor.technology["semantic_similarity_enabled"]} /> <%= dgettext("ui", "Enable duplicate search and related-post embeddings") %></label></div>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info"><label class="setting-label"><%= translated("Scripting Runtime") %></label></div>
|
||||
<div class="setting-control"><p class="setting-description"><%= translated("Scripting capabilities are configured at the application layer in the rewrite and do not expose runtime switching here.") %></p></div>
|
||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Scripting Runtime") %></label></div>
|
||||
<div class="setting-control"><p class="setting-description"><%= dgettext("ui", "Scripting capabilities are configured at the application layer in the rewrite and do not expose runtime switching here.") %></p></div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_project" phx-target={@myself}><%= translated("Save") %></button></div>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_project" phx-target={@myself}><%= dgettext("ui", "Save") %></button></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if @settings_editor.publishing_visible? do %>
|
||||
<div class="setting-section" id="settings-section-publishing">
|
||||
<div class="setting-section-header"><h3><%= translated("Publishing") %></h3><p class="setting-section-description"><%= translated("Deployment credentials for upload tasks") %></p></div>
|
||||
<div class="setting-section-header"><h3><%= dgettext("ui", "Publishing") %></h3><p class="setting-section-description"><%= dgettext("ui", "Deployment credentials for upload tasks") %></p></div>
|
||||
<form class="setting-section-content" phx-change="change_settings_publishing" phx-target={@myself}>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= translated("SSH Mode") %></label></div><div class="setting-control"><select name="settings_publishing[ssh_mode]"><option value="scp" selected={@settings_editor.publishing["ssh_mode"] == "scp"}>scp</option><option value="rsync" selected={@settings_editor.publishing["ssh_mode"] == "rsync"}>rsync</option></select></div></div>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= translated("Host") %></label></div><div class="setting-control"><input type="text" name="settings_publishing[ssh_host]" value={@settings_editor.publishing["ssh_host"]} /></div></div>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= translated("Username") %></label></div><div class="setting-control"><input type="text" name="settings_publishing[ssh_user]" value={@settings_editor.publishing["ssh_user"]} /></div></div>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= translated("Remote Path") %></label></div><div class="setting-control"><input type="text" name="settings_publishing[ssh_remote_path]" value={@settings_editor.publishing["ssh_remote_path"]} /></div></div>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= dgettext("ui", "SSH Mode") %></label></div><div class="setting-control"><select name="settings_publishing[ssh_mode]"><option value="scp" selected={@settings_editor.publishing["ssh_mode"] == "scp"}>scp</option><option value="rsync" selected={@settings_editor.publishing["ssh_mode"] == "rsync"}>rsync</option></select></div></div>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Host") %></label></div><div class="setting-control"><input type="text" name="settings_publishing[ssh_host]" value={@settings_editor.publishing["ssh_host"]} /></div></div>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Username") %></label></div><div class="setting-control"><input type="text" name="settings_publishing[ssh_user]" value={@settings_editor.publishing["ssh_user"]} /></div></div>
|
||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Remote Path") %></label></div><div class="setting-control"><input type="text" name="settings_publishing[ssh_remote_path]" value={@settings_editor.publishing["ssh_remote_path"]} /></div></div>
|
||||
</form>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_publishing" phx-target={@myself}><%= translated("Save") %></button><button class="secondary" type="button" phx-click="clear_settings_publishing" phx-target={@myself}><%= translated("Clear") %></button></div>
|
||||
<div class="setting-actions"><button class="primary" type="button" phx-click="save_settings_publishing" phx-target={@myself}><%= dgettext("ui", "Save") %></button><button class="secondary" type="button" phx-click="clear_settings_publishing" phx-target={@myself}><%= dgettext("ui", "Clear") %></button></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if @settings_editor.mcp_visible? do %>
|
||||
<div class="setting-section" id="settings-section-mcp">
|
||||
<div class="setting-section-header"><h3><%= translated("MCP") %></h3><p class="setting-section-description"><%= translated("Agent configuration files for the built-in bDS MCP server") %></p></div>
|
||||
<div class="setting-section-header"><h3><%= dgettext("ui", "MCP") %></h3><p class="setting-section-description"><%= dgettext("ui", "Agent configuration files for the built-in bDS MCP server") %></p></div>
|
||||
<div class="setting-section-content">
|
||||
<%= for agent <- @settings_editor.mcp do %>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<label class="setting-label"><%= agent.label %></label>
|
||||
<p class="setting-description"><%= agent.config_path || translated("Not supported in the rewrite yet") %></p>
|
||||
<p class="setting-description"><%= agent.config_path || dgettext("ui", "Not supported in the rewrite yet") %></p>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<button class="secondary" type="button" phx-click="toggle_settings_mcp_agent" phx-target={@myself} phx-value-agent={agent.id} disabled={not agent.supported?}>
|
||||
<%= if agent.configured?, do: translated("Remove"), else: translated("Add") %>
|
||||
<%= if agent.configured?, do: dgettext("ui", "Remove"), else: dgettext("ui", "Add") %>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -362,16 +362,16 @@
|
||||
|
||||
<%= if @settings_editor.data_visible? do %>
|
||||
<div class="setting-section" id="settings-section-data">
|
||||
<div class="setting-section-header"><h3><%= translated("Data Maintenance") %></h3><p class="setting-section-description"><%= translated("Rebuild filesystem-backed records and thumbnails") %></p></div>
|
||||
<div class="setting-section-header"><h3><%= dgettext("ui", "Data Maintenance") %></h3><p class="setting-section-description"><%= dgettext("ui", "Rebuild filesystem-backed records and thumbnails") %></p></div>
|
||||
<div class="setting-actions">
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_posts_from_files"><%= translated("Rebuild Posts From Files") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_media_from_files"><%= translated("Rebuild Media From Files") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_scripts_from_files"><%= translated("Rebuild Scripts From Files") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_templates_from_files"><%= translated("Rebuild Templates From Files") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_post_links"><%= translated("Rebuild Links") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="regenerate_missing_thumbnails"><%= translated("Regenerate Missing Thumbnails") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_embedding_index"><%= translated("Rebuild Embedding Index") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="open_data_folder"><%= translated("Open Data Folder") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_posts_from_files"><%= dgettext("ui", "Rebuild Posts From Files") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_media_from_files"><%= dgettext("ui", "Rebuild Media From Files") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_scripts_from_files"><%= dgettext("ui", "Rebuild Scripts From Files") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_templates_from_files"><%= dgettext("ui", "Rebuild Templates From Files") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_post_links"><%= dgettext("ui", "Rebuild Links") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="regenerate_missing_thumbnails"><%= dgettext("ui", "Regenerate Missing Thumbnails") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_embedding_index"><%= dgettext("ui", "Rebuild Embedding Index") %></button>
|
||||
<button class="secondary" type="button" phx-click="settings_shell_command" phx-value-action="open_data_folder"><%= dgettext("ui", "Open Data Folder") %></button>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<div class="style-view" data-testid="style-editor">
|
||||
<div class="style-view-header">
|
||||
<h2 data-testid="editor-title"><%= translated("Style") %></h2>
|
||||
<p><%= translated("Theme preview and renderer theme selection") %></p>
|
||||
<h2 data-testid="editor-title"><%= dgettext("ui", "Style") %></h2>
|
||||
<p><%= dgettext("ui", "Theme preview and renderer theme selection") %></p>
|
||||
</div>
|
||||
|
||||
<div class="style-theme-picker" role="group" aria-label={translated("Theme picker")}>
|
||||
<div class="style-theme-picker" role="group" aria-label={dgettext("ui", "Theme picker")}>
|
||||
<%= for theme <- @style_editor.themes do %>
|
||||
<button type="button" class={["style-theme-option", if(theme.name == @style_editor.selected_theme, do: "selected")]} phx-click="select_style_theme" phx-target={@myself} phx-value-theme={theme.name} aria-pressed={theme.name == @style_editor.selected_theme}>
|
||||
<span class="style-theme-swatch">
|
||||
@@ -21,17 +21,17 @@
|
||||
|
||||
<div class="style-apply-row">
|
||||
<label class="style-preview-mode-control">
|
||||
<span><%= translated("Preview Mode") %></span>
|
||||
<span><%= dgettext("ui", "Preview Mode") %></span>
|
||||
<select phx-change="change_style_preview_mode" phx-target={@myself} name="mode">
|
||||
<option value="auto" selected={@style_editor.preview_mode == "auto"}><%= translated("Auto") %></option>
|
||||
<option value="light" selected={@style_editor.preview_mode == "light"}><%= translated("Light") %></option>
|
||||
<option value="dark" selected={@style_editor.preview_mode == "dark"}><%= translated("Dark") %></option>
|
||||
<option value="auto" selected={@style_editor.preview_mode == "auto"}><%= dgettext("ui", "Auto") %></option>
|
||||
<option value="light" selected={@style_editor.preview_mode == "light"}><%= dgettext("ui", "Light") %></option>
|
||||
<option value="dark" selected={@style_editor.preview_mode == "dark"}><%= dgettext("ui", "Dark") %></option>
|
||||
</select>
|
||||
</label>
|
||||
<button class="primary" type="button" phx-click="apply_style_theme" phx-target={@myself} disabled={@style_editor.selected_theme == @style_editor.applied_theme}><%= translated("Apply Theme") %></button>
|
||||
<button class="primary" type="button" phx-click="apply_style_theme" phx-target={@myself} disabled={@style_editor.selected_theme == @style_editor.applied_theme}><%= dgettext("ui", "Apply Theme") %></button>
|
||||
</div>
|
||||
|
||||
<div class="style-preview-container">
|
||||
<iframe title={translated("Theme Preview")} class="style-preview-frame" src={@style_editor.preview_url}></iframe>
|
||||
<iframe title={dgettext("ui", "Theme Preview")} class="style-preview-frame" src={@style_editor.preview_url}></iframe>
|
||||
</div>
|
||||
</div>
|
||||
@@ -6,6 +6,7 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Desktop.UILocale
|
||||
alias BDS.UI.Registry
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
def sidebar_content(assigns) do
|
||||
UILocale.put(assigns.page_language)
|
||||
@@ -60,9 +61,9 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
type="text"
|
||||
name="sidebar_filters[search]"
|
||||
value={Map.get(@selected_filters, :search) || ""}
|
||||
placeholder={translated(@sidebar_filters_config.search_placeholder)}
|
||||
placeholder={@sidebar_filters_config.search_placeholder}
|
||||
/>
|
||||
<button type="submit" title={translated("sidebar.search")}>
|
||||
<button type="submit" title={dgettext("ui", "Search")}>
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M15.7 14.3l-4.2-4.2c-.2-.2-.5-.3-.8-.3.9-1.1 1.5-2.5 1.5-4C12.2 2.6 9.6 0 6.4 0S.6 2.6.6 5.8s2.6 5.8 5.8 5.8c1.5 0 2.9-.5 4-1.4 0 .3.1.6.3.8l4.2 4.2c.2.2.5.3.7.3s.5-.1.7-.3c.4-.4.4-1 0-1.4zm-9.3-4c-2.5 0-4.5-2-4.5-4.5s2-4.5 4.5-4.5 4.5 2 4.5 4.5-2 4.5-4.5 4.5z"/>
|
||||
</svg>
|
||||
@@ -75,10 +76,10 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
<%= if Map.get(@sidebar_filters_config, :has_active_filters) do %>
|
||||
<div class="filter-status">
|
||||
<span>
|
||||
<%= translated(@sidebar_filters_config.results_label) %>: <%= @sidebar_filters_config.loaded_count %>/<%= @sidebar_filters_config.total_count %>
|
||||
<%= @sidebar_filters_config.results_label %>: <%= @sidebar_filters_config.loaded_count %>/<%= @sidebar_filters_config.total_count %>
|
||||
</span>
|
||||
<button data-testid="sidebar-clear-filters" type="button" phx-click="clear_sidebar_filters">
|
||||
<%= translated(@sidebar_filters_config.clear_filters_label) %>
|
||||
<%= @sidebar_filters_config.clear_filters_label %>
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -96,7 +97,7 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
phx-click="toggle_sidebar_archive"
|
||||
>
|
||||
<span class="collapse-icon"><%= if @archive_collapsed, do: "▶", else: "▼" %></span>
|
||||
<span><%= translated(@sidebar_filters_config.archive_label) %></span>
|
||||
<span><%= @sidebar_filters_config.archive_label %></span>
|
||||
<%= if Map.get(@selected_filters, :year) do %>
|
||||
<button class="clear-filter" type="button" phx-click="clear_sidebar_month" phx-stop-propagation>✕</button>
|
||||
<% end %>
|
||||
@@ -157,9 +158,9 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
phx-click="toggle_sidebar_tags"
|
||||
>
|
||||
<span class="collapse-icon"><%= if @tags_collapsed, do: "▶", else: "▼" %></span>
|
||||
<span><%= translated(@sidebar_filters_config.tags_label) %></span>
|
||||
<span><%= @sidebar_filters_config.tags_label %></span>
|
||||
<%= if Enum.any?(Map.get(@selected_filters, :tags, [])) do %>
|
||||
<button class="clear-filter" type="button" phx-click="clear_sidebar_tags" phx-stop-propagation title={translated(@sidebar_filters_config.clear_tags_label)}>✕</button>
|
||||
<button class="clear-filter" type="button" phx-click="clear_sidebar_tags" phx-stop-propagation title={@sidebar_filters_config.clear_tags_label}>✕</button>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= unless @tags_collapsed do %>
|
||||
@@ -198,9 +199,9 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
phx-click="toggle_sidebar_categories"
|
||||
>
|
||||
<span class="collapse-icon"><%= if @categories_collapsed, do: "▶", else: "▼" %></span>
|
||||
<span><%= translated(@sidebar_filters_config.categories_label) %></span>
|
||||
<span><%= @sidebar_filters_config.categories_label %></span>
|
||||
<%= if Enum.any?(Map.get(@selected_filters, :categories, [])) do %>
|
||||
<button class="clear-filter" type="button" phx-click="clear_sidebar_categories" phx-stop-propagation title={translated(@sidebar_filters_config.clear_categories_label)}>✕</button>
|
||||
<button class="clear-filter" type="button" phx-click="clear_sidebar_categories" phx-stop-propagation title={@sidebar_filters_config.clear_categories_label}>✕</button>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= unless @categories_collapsed do %>
|
||||
@@ -240,7 +241,7 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
~H"""
|
||||
<div class="sidebar-load-more">
|
||||
<button class="load-more-button" data-testid="sidebar-load-more" type="button" phx-click="load_more_sidebar">
|
||||
<%= translated("Load more") %>
|
||||
<%= dgettext("ui", "Load more") %>
|
||||
</button>
|
||||
</div>
|
||||
"""
|
||||
@@ -266,7 +267,7 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
<section class="sidebar-section">
|
||||
<div class="sidebar-section-title">
|
||||
<span class={"section-icon status-#{Map.get(section, :status, "draft")}"}>●</span>
|
||||
<span data-testid="sidebar-section-title"><%= translated(section.title) %></span>
|
||||
<span data-testid="sidebar-section-title"><%= section.title %></span>
|
||||
<span class="sidebar-section-count"><%= Map.get(section, :count, length(Map.get(section, :items, []))) %></span>
|
||||
</div>
|
||||
<div class="sidebar-list">
|
||||
@@ -316,7 +317,7 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
<% end %>
|
||||
<%= if Enum.empty?(Map.get(@sidebar_data, :sections, [])) do %>
|
||||
<div class="sidebar-empty">
|
||||
<p><%= translated(Map.get(@sidebar_data, :empty_message, "No items")) %></p>
|
||||
<p><%= Map.get(@sidebar_data, :empty_message, dgettext("ui", "No items")) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
"""
|
||||
@@ -376,7 +377,7 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="sidebar-empty">
|
||||
<p><%= translated(Map.get(@sidebar_data, :empty_message, "No items")) %></p>
|
||||
<p><%= Map.get(@sidebar_data, :empty_message, dgettext("ui", "No items")) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
"""
|
||||
@@ -398,17 +399,17 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
data-route={item.route}
|
||||
data-item-id={item.id}
|
||||
data-open-title={item.title}
|
||||
data-open-subtitle={translated(item.meta || "")}
|
||||
data-open-subtitle={item.meta || ""}
|
||||
type="button"
|
||||
phx-click="open_sidebar_item"
|
||||
phx-value-route={item.route}
|
||||
phx-value-id={item.id}
|
||||
phx-value-title={item.title}
|
||||
phx-value-subtitle={translated(item.meta || "")}
|
||||
phx-value-subtitle={item.meta || ""}
|
||||
>
|
||||
<span class="chat-item-content">
|
||||
<span class="chat-item-title"><%= item.title %></span>
|
||||
<span class="chat-item-date"><%= translated(item.meta || "") %></span>
|
||||
<span class="chat-item-date"><%= item.meta || "" %></span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
@@ -432,17 +433,17 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
data-route={item.route}
|
||||
data-item-id={item.id}
|
||||
data-open-title={item.title}
|
||||
data-open-subtitle={translated(item.meta || "")}
|
||||
data-open-subtitle={item.meta || ""}
|
||||
type="button"
|
||||
phx-click="open_sidebar_item"
|
||||
phx-value-route={item.route}
|
||||
phx-value-id={item.id}
|
||||
phx-value-title={item.title}
|
||||
phx-value-subtitle={translated(item.meta || "")}
|
||||
phx-value-subtitle={item.meta || ""}
|
||||
>
|
||||
<span class="chat-item-content">
|
||||
<span class="chat-item-title"><%= item.title %></span>
|
||||
<span class="chat-item-date"><%= translated(item.meta || "") %></span>
|
||||
<span class="chat-item-date"><%= item.meta || "" %></span>
|
||||
</span>
|
||||
</button>
|
||||
<% end %>
|
||||
@@ -450,7 +451,7 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="sidebar-empty">
|
||||
<p><%= translated(Map.get(@sidebar_data, :empty_message, "No items")) %></p>
|
||||
<p><%= Map.get(@sidebar_data, :empty_message, dgettext("ui", "No items")) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
"""
|
||||
@@ -465,17 +466,17 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
data-testid="sidebar-open-item"
|
||||
data-route={item.route}
|
||||
data-item-id={item.id}
|
||||
data-open-title={translated(item.title)}
|
||||
data-open-subtitle={translated(Map.get(@sidebar_data, :subtitle, ""))}
|
||||
data-open-title={item.title}
|
||||
data-open-subtitle={Map.get(@sidebar_data, :subtitle, "")}
|
||||
type="button"
|
||||
phx-click="open_sidebar_item"
|
||||
phx-value-route={item.route}
|
||||
phx-value-id={item.id}
|
||||
phx-value-title={translated(item.title)}
|
||||
phx-value-subtitle={translated(Map.get(@sidebar_data, :subtitle, ""))}
|
||||
phx-value-title={item.title}
|
||||
phx-value-subtitle={Map.get(@sidebar_data, :subtitle, "")}
|
||||
>
|
||||
<span class="settings-nav-entry-icon"><%= Map.get(item, :icon, "") %></span>
|
||||
<span><%= translated(item.title) %></span>
|
||||
<span><%= item.title %></span>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -487,7 +488,7 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
<%= for section <- Map.get(@sidebar_data, :sections, []) do %>
|
||||
<section class="sidebar-section">
|
||||
<div class="sidebar-section-header">
|
||||
<span data-testid="sidebar-section-title"><%= translated(section.title) %></span>
|
||||
<span data-testid="sidebar-section-title"><%= section.title %></span>
|
||||
</div>
|
||||
<div class="sidebar-section-items">
|
||||
<%= for item <- Map.get(section, :items, []) do %>
|
||||
@@ -499,8 +500,6 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
"""
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
|
||||
defp sidebar_deletable?(route), do: route in ["post", "media", "scripts", "templates", "chat", "import"]
|
||||
|
||||
@@ -512,13 +511,13 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
defp sidebar_delete_testid("import"), do: "sidebar-delete-import"
|
||||
defp sidebar_delete_testid(route), do: "sidebar-delete-#{route}"
|
||||
|
||||
defp sidebar_delete_title("chat"), do: translated("sidebar.chat.deleteConversation")
|
||||
defp sidebar_delete_title("post"), do: translated("Delete") <> " " <> translated("Post")
|
||||
defp sidebar_delete_title("media"), do: translated("Delete") <> " " <> translated("Media")
|
||||
defp sidebar_delete_title("scripts"), do: translated("Delete") <> " " <> translated("Script")
|
||||
defp sidebar_delete_title("templates"), do: translated("Delete") <> " " <> translated("Template")
|
||||
defp sidebar_delete_title("import"), do: translated("Delete") <> " " <> translated("Import")
|
||||
defp sidebar_delete_title(_route), do: translated("Delete")
|
||||
defp sidebar_delete_title("chat"), do: dgettext("ui", "Delete conversation")
|
||||
defp sidebar_delete_title("post"), do: dgettext("ui", "Delete") <> " " <> dgettext("ui", "Post")
|
||||
defp sidebar_delete_title("media"), do: dgettext("ui", "Delete") <> " " <> dgettext("ui", "Media")
|
||||
defp sidebar_delete_title("scripts"), do: dgettext("ui", "Delete") <> " " <> dgettext("ui", "Script")
|
||||
defp sidebar_delete_title("templates"), do: dgettext("ui", "Delete") <> " " <> dgettext("ui", "Template")
|
||||
defp sidebar_delete_title("import"), do: dgettext("ui", "Delete") <> " " <> dgettext("ui", "Import")
|
||||
defp sidebar_delete_title(_route), do: dgettext("ui", "Delete")
|
||||
|
||||
defp template_sidebar?(sidebar_data), do: Map.get(sidebar_data, :title) == "Templates"
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
@moduledoc false
|
||||
|
||||
alias BDS.Desktop.{FilePicker, ShellData}
|
||||
alias BDS.Desktop.{FilePicker}
|
||||
alias BDS.AI
|
||||
alias BDS.ImportDefinitions
|
||||
alias BDS.Scripts
|
||||
alias BDS.Templates
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@doc """
|
||||
Create a new sidebar item of the given kind for the active project.
|
||||
@@ -35,13 +36,13 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> callbacks.append_output.(translated("sidebar.newPost"), inspect(reason), nil, "error")
|
||||
|> callbacks.append_output.(dgettext("ui", "New Post"), inspect(reason), nil, "error")
|
||||
|> callbacks.reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
|
||||
def create(socket, project_id, "media", callbacks) do
|
||||
case FilePicker.choose_file(translated("sidebar.importMedia")) do
|
||||
case FilePicker.choose_file(dgettext("ui", "Import media")) do
|
||||
{:ok, source_path} ->
|
||||
case BDS.Media.import_media(%{project_id: project_id, source_path: source_path}) do
|
||||
{:ok, _media} ->
|
||||
@@ -50,7 +51,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> callbacks.append_output.(
|
||||
translated("sidebar.importMedia"),
|
||||
dgettext("ui", "Import media"),
|
||||
inspect(reason),
|
||||
nil,
|
||||
"error"
|
||||
@@ -63,7 +64,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
|
||||
{:error, %{message: message}} ->
|
||||
socket
|
||||
|> callbacks.append_output.(translated("sidebar.importMedia"), message, nil, "error")
|
||||
|> callbacks.append_output.(dgettext("ui", "Import media"), message, nil, "error")
|
||||
|> callbacks.reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -71,7 +72,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
def create(socket, project_id, "script", callbacks) do
|
||||
case Scripts.create_script(%{
|
||||
project_id: project_id,
|
||||
title: translated("sidebar.scripts.newScript"),
|
||||
title: dgettext("ui", "New Script"),
|
||||
kind: :utility,
|
||||
content: "print(\"new script\")",
|
||||
entrypoint: "main",
|
||||
@@ -92,7 +93,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> callbacks.append_output.(
|
||||
translated("sidebar.scripts.newScript"),
|
||||
dgettext("ui", "New Script"),
|
||||
inspect(reason),
|
||||
nil,
|
||||
"error"
|
||||
@@ -104,7 +105,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
def create(socket, project_id, "template", callbacks) do
|
||||
case Templates.create_template(%{
|
||||
project_id: project_id,
|
||||
title: translated("sidebar.templates.newTemplate"),
|
||||
title: dgettext("ui", "New Template"),
|
||||
kind: :post,
|
||||
content: "",
|
||||
enabled: true
|
||||
@@ -124,7 +125,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> callbacks.append_output.(
|
||||
translated("sidebar.templates.newTemplate"),
|
||||
dgettext("ui", "New Template"),
|
||||
inspect(reason),
|
||||
nil,
|
||||
"error"
|
||||
@@ -149,7 +150,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> callbacks.append_output.(translated("chat.newChat"), inspect(reason), nil, "error")
|
||||
|> callbacks.append_output.(dgettext("ui", "New Chat"), inspect(reason), nil, "error")
|
||||
|> callbacks.reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -157,7 +158,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
def create(socket, project_id, "import", callbacks) do
|
||||
case ImportDefinitions.create_definition(%{
|
||||
project_id: project_id,
|
||||
name: translated("sidebar.import.newDefinition")
|
||||
name: dgettext("ui", "New Import Definition")
|
||||
}) do
|
||||
{:ok, definition} ->
|
||||
callbacks.open_sidebar.(
|
||||
@@ -174,7 +175,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> callbacks.append_output.(
|
||||
translated("sidebar.import.newDefinition"),
|
||||
dgettext("ui", "New Import Definition"),
|
||||
inspect(reason),
|
||||
nil,
|
||||
"error"
|
||||
@@ -193,6 +194,4 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
|
||||
def action(:chat), do: %{kind: "chat", label: "chat.newChat"}
|
||||
def action(:import), do: %{kind: "import", label: "sidebar.import.newDefinition"}
|
||||
def action(_view), do: nil
|
||||
|
||||
defp translated(text), do: ShellData.translate(text, %{}, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -4,8 +4,7 @@ defmodule BDS.Desktop.ShellLive.SidebarDelete do
|
||||
import Phoenix.Component, only: [assign: 3]
|
||||
|
||||
alias BDS.{AI, ImportDefinitions, Media, Posts, Scripts, Templates}
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Desktop.UILocale
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
@spec request_delete(Phoenix.LiveView.Socket.t(), String.t(), String.t(), String.t() | nil, map()) ::
|
||||
Phoenix.LiveView.Socket.t()
|
||||
@@ -57,7 +56,7 @@ defmodule BDS.Desktop.ShellLive.SidebarDelete do
|
||||
_other ->
|
||||
socket
|
||||
|> assign(:shell_overlay, nil)
|
||||
|> callbacks.append_output.(translated("Delete"), inspect(:unsupported_route), nil, "error")
|
||||
|> callbacks.append_output.(dgettext("ui", "Delete"), inspect(:unsupported_route), nil, "error")
|
||||
|> callbacks.reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
@@ -152,13 +151,13 @@ defmodule BDS.Desktop.ShellLive.SidebarDelete do
|
||||
end
|
||||
|
||||
@spec delete_title(String.t()) :: String.t()
|
||||
defp delete_title("chat"), do: translated("sidebar.chat.deleteConversation")
|
||||
defp delete_title("post"), do: translated("Delete") <> " " <> translated("Post")
|
||||
defp delete_title("media"), do: translated("Delete") <> " " <> translated("Media")
|
||||
defp delete_title("scripts"), do: translated("Delete") <> " " <> translated("Script")
|
||||
defp delete_title("templates"), do: translated("Delete") <> " " <> translated("Template")
|
||||
defp delete_title("import"), do: translated("Delete") <> " " <> translated("Import")
|
||||
defp delete_title(_route), do: translated("Delete")
|
||||
defp delete_title("chat"), do: dgettext("ui", "Delete conversation")
|
||||
defp delete_title("post"), do: dgettext("ui", "Delete") <> " " <> dgettext("ui", "Post")
|
||||
defp delete_title("media"), do: dgettext("ui", "Delete") <> " " <> dgettext("ui", "Media")
|
||||
defp delete_title("scripts"), do: dgettext("ui", "Delete") <> " " <> dgettext("ui", "Script")
|
||||
defp delete_title("templates"), do: dgettext("ui", "Delete") <> " " <> dgettext("ui", "Template")
|
||||
defp delete_title("import"), do: dgettext("ui", "Delete") <> " " <> dgettext("ui", "Import")
|
||||
defp delete_title(_route), do: dgettext("ui", "Delete")
|
||||
|
||||
@spec present_title(String.t() | nil) :: String.t() | nil
|
||||
defp present_title(value) when is_binary(value) do
|
||||
@@ -170,7 +169,4 @@ defmodule BDS.Desktop.ShellLive.SidebarDelete do
|
||||
|
||||
defp present_title(_value), do: nil
|
||||
|
||||
@spec translated(String.t(), map()) :: String.t()
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, UILocale.current())
|
||||
end
|
||||
|
||||
@@ -6,8 +6,9 @@ defmodule BDS.Desktop.ShellLive.TabHelpers do
|
||||
alias BDS.Media.Media, as: MediaRecord
|
||||
alias BDS.Posts.Post
|
||||
alias BDS.UI.Registry
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
def tab_title(nil, _tab_meta), do: translated("Dashboard")
|
||||
def tab_title(nil, _tab_meta), do: dgettext("ui", "Dashboard")
|
||||
|
||||
def tab_title(tab, tab_meta) do
|
||||
case Map.get(tab_meta, {tab.type, tab.id}) do
|
||||
@@ -16,7 +17,7 @@ defmodule BDS.Desktop.ShellLive.TabHelpers do
|
||||
end
|
||||
end
|
||||
|
||||
def tab_subtitle(nil, _tab_meta), do: translated("dashboard.subtitle")
|
||||
def tab_subtitle(nil, _tab_meta), do: dgettext("ui", "Overview of your blog database")
|
||||
|
||||
def tab_subtitle(tab, tab_meta) do
|
||||
case Map.get(tab_meta, {tab.type, tab.id}) do
|
||||
@@ -50,7 +51,7 @@ defmodule BDS.Desktop.ShellLive.TabHelpers do
|
||||
|
||||
defp default_tab_subtitle(_tab), do: "Desktop workbench content routed through the Elixir shell."
|
||||
|
||||
def tab_route_label(nil), do: translated("Dashboard")
|
||||
def tab_route_label(nil), do: dgettext("ui", "Dashboard")
|
||||
def tab_route_label(%{type: type}), do: ShellData.route_label(type)
|
||||
|
||||
def tab_icon_id(nil), do: "posts"
|
||||
@@ -135,7 +136,7 @@ defmodule BDS.Desktop.ShellLive.TabHelpers do
|
||||
defp derived_tab_meta(%{type: :scripts, id: script_id}) do
|
||||
case Scripts.get_script(script_id) do
|
||||
%{title: title, id: id} ->
|
||||
%{title: blank_to_nil(title) || id, subtitle: translated("Automation helpers")}
|
||||
%{title: blank_to_nil(title) || id, subtitle: dgettext("ui", "Automation helpers")}
|
||||
|
||||
_other ->
|
||||
%{}
|
||||
@@ -145,7 +146,7 @@ defmodule BDS.Desktop.ShellLive.TabHelpers do
|
||||
defp derived_tab_meta(%{type: :templates, id: template_id}) do
|
||||
case Templates.get_template(template_id) do
|
||||
%{title: title, id: id} ->
|
||||
%{title: blank_to_nil(title) || id, subtitle: translated("Site rendering")}
|
||||
%{title: blank_to_nil(title) || id, subtitle: dgettext("ui", "Site rendering")}
|
||||
|
||||
_other ->
|
||||
%{}
|
||||
@@ -155,7 +156,7 @@ defmodule BDS.Desktop.ShellLive.TabHelpers do
|
||||
defp derived_tab_meta(%{type: :chat, id: conversation_id}) do
|
||||
case AI.get_chat_conversation(conversation_id) do
|
||||
conversation when is_map(conversation) ->
|
||||
%{title: chat_record_title(conversation), subtitle: translated("AI conversations")}
|
||||
%{title: chat_record_title(conversation), subtitle: dgettext("ui", "AI conversations")}
|
||||
|
||||
_other ->
|
||||
%{}
|
||||
@@ -166,8 +167,8 @@ defmodule BDS.Desktop.ShellLive.TabHelpers do
|
||||
case ImportDefinitions.get_definition(definition_id) do
|
||||
%{name: name} ->
|
||||
%{
|
||||
title: blank_to_nil(name) || translated("importAnalysis.untitledImport"),
|
||||
subtitle: translated("importAnalysis.headerDescription")
|
||||
title: blank_to_nil(name) || dgettext("ui", "Untitled Import"),
|
||||
subtitle: dgettext("ui", "Select a WordPress export file (WXR) and an uploads folder to analyze what would be imported.")
|
||||
}
|
||||
|
||||
_other ->
|
||||
@@ -176,13 +177,13 @@ defmodule BDS.Desktop.ShellLive.TabHelpers do
|
||||
end
|
||||
|
||||
defp derived_tab_meta(%{type: :git_diff, id: "git-working-tree"}) do
|
||||
%{title: translated("Working tree"), subtitle: translated("Working tree and history")}
|
||||
%{title: dgettext("ui", "Working tree"), subtitle: dgettext("ui", "Working tree and history")}
|
||||
end
|
||||
|
||||
defp derived_tab_meta(%{type: :menu_editor}) do
|
||||
%{
|
||||
title: translated("menuEditor.tabTitle"),
|
||||
subtitle: translated("menuEditor.description")
|
||||
title: dgettext("ui", "Blog Menu"),
|
||||
subtitle: dgettext("ui", "Manage the central blog navigation outline and save it to meta/menu.opml.")
|
||||
}
|
||||
end
|
||||
|
||||
@@ -234,6 +235,4 @@ defmodule BDS.Desktop.ShellLive.TabHelpers do
|
||||
trimmed -> trimmed
|
||||
end
|
||||
end
|
||||
|
||||
defp translated(text), do: ShellData.translate(text, %{}, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
|
||||
@@ -6,11 +6,10 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
||||
import Ecto.Query
|
||||
|
||||
alias BDS.{Repo, Tags}
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Desktop.UILocale
|
||||
alias BDS.Posts.Post
|
||||
alias BDS.Tags.Tag
|
||||
alias BDS.Templates.Template
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
embed_templates("tags_editor_html/*")
|
||||
|
||||
@@ -91,7 +90,7 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
||||
|> noreply()
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(translated("Tags"), inspect(reason), "error")
|
||||
notify_output(dgettext("ui", "Tags"), inspect(reason), "error")
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
@@ -133,7 +132,7 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
||||
|> noreply()
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(translated("Tags"), inspect(reason), "error")
|
||||
notify_output(dgettext("ui", "Tags"), inspect(reason), "error")
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
@@ -186,7 +185,7 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
||||
|> noreply()
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(translated("Tags"), inspect(reason), "error")
|
||||
notify_output(dgettext("ui", "Tags"), inspect(reason), "error")
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
@@ -200,7 +199,7 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
||||
{:noreply, load_data(socket)}
|
||||
|
||||
{:error, reason} ->
|
||||
notify_output(translated("Tags"), inspect(reason), "error")
|
||||
notify_output(dgettext("ui", "Tags"), inspect(reason), "error")
|
||||
{:noreply, socket}
|
||||
end
|
||||
end
|
||||
@@ -231,7 +230,7 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
||||
|> load_data()
|
||||
else
|
||||
{:error, reason} ->
|
||||
notify_output(translated("Tags"), inspect(reason), "error")
|
||||
notify_output(dgettext("ui", "Tags"), inspect(reason), "error")
|
||||
socket
|
||||
end
|
||||
end
|
||||
@@ -383,7 +382,4 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
||||
trimmed -> trimmed
|
||||
end
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, UILocale.current())
|
||||
end
|
||||
|
||||
@@ -8,17 +8,17 @@
|
||||
>
|
||||
<div class="tags-view">
|
||||
<div class="tags-view-header">
|
||||
<h2><%= translated("Tags") %></h2>
|
||||
<h2><%= dgettext("ui", "Tags") %></h2>
|
||||
</div>
|
||||
|
||||
<div class="tags-view-content">
|
||||
<div class="tags-section" id="tags-section-cloud">
|
||||
<div class="tags-section-header"><h3><%= translated("Tag Cloud") %></h3></div>
|
||||
<div class="tags-section-header"><h3><%= dgettext("ui", "Tag Cloud") %></h3></div>
|
||||
<div class="tags-section-content">
|
||||
<%= if Enum.empty?(@tags_editor.tags) do %>
|
||||
<div class="tags-empty-state">
|
||||
<p><%= translated("No tags found") %></p>
|
||||
<button class="secondary" type="button" phx-click="sync_tags_editor" phx-target={@myself}><%= translated("Discover") %></button>
|
||||
<p><%= dgettext("ui", "No tags found") %></p>
|
||||
<button class="secondary" type="button" phx-click="sync_tags_editor" phx-target={@myself}><%= dgettext("ui", "Discover") %></button>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="tag-cloud">
|
||||
@@ -33,13 +33,13 @@
|
||||
</div>
|
||||
|
||||
<div class="tags-section" id="tags-section-manage">
|
||||
<div class="tags-section-header"><h3><%= translated("Create / Edit") %></h3></div>
|
||||
<div class="tags-section-header"><h3><%= dgettext("ui", "Create / Edit") %></h3></div>
|
||||
<div class="tags-section-content">
|
||||
<form class="tag-create-form" phx-change="change_new_tag_editor" phx-target={@myself}>
|
||||
<div class="tag-form-row">
|
||||
<input type="text" name="new_tag[name]" value={@tags_editor.new_tag["name"]} placeholder={translated("Tag name")} />
|
||||
<input type="text" name="new_tag[name]" value={@tags_editor.new_tag["name"]} placeholder={dgettext("ui", "Tag name")} />
|
||||
<input type="color" name="new_tag[color]" value={if(@tags_editor.new_tag["color"] in [nil, ""], do: "#3b82f6", else: @tags_editor.new_tag["color"])} />
|
||||
<button class="primary" type="button" phx-click="create_tag_editor" phx-target={@myself}><%= translated("Create") %></button>
|
||||
<button class="primary" type="button" phx-click="create_tag_editor" phx-target={@myself}><%= dgettext("ui", "Create") %></button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -49,13 +49,13 @@
|
||||
<input type="text" name="edit_tag[name]" value={@tags_editor.edit_draft["name"]} />
|
||||
<input type="color" name="edit_tag[color]" value={if(@tags_editor.edit_draft["color"] in [nil, ""], do: "#3b82f6", else: @tags_editor.edit_draft["color"])} />
|
||||
<select name="edit_tag[post_template_slug]">
|
||||
<option value=""><%= translated("No Template") %></option>
|
||||
<option value=""><%= dgettext("ui", "No Template") %></option>
|
||||
<%= for template <- @tags_editor.templates do %>
|
||||
<option value={template.slug} selected={template.slug == @tags_editor.edit_draft["post_template_slug"]}><%= template.title %></option>
|
||||
<% end %>
|
||||
</select>
|
||||
<button class="primary" type="button" phx-click="save_tag_editor" phx-target={@myself}><%= translated("Save") %></button>
|
||||
<button class="danger" type="button" phx-click="delete_tag_editor" phx-target={@myself}><%= translated("Delete") %></button>
|
||||
<button class="primary" type="button" phx-click="save_tag_editor" phx-target={@myself}><%= dgettext("ui", "Save") %></button>
|
||||
<button class="danger" type="button" phx-click="delete_tag_editor" phx-target={@myself}><%= dgettext("ui", "Delete") %></button>
|
||||
</div>
|
||||
</form>
|
||||
<% end %>
|
||||
@@ -63,7 +63,7 @@
|
||||
</div>
|
||||
|
||||
<div class="tags-section" id="tags-section-merge">
|
||||
<div class="tags-section-header"><h3><%= translated("Merge Tags") %></h3></div>
|
||||
<div class="tags-section-header"><h3><%= dgettext("ui", "Merge Tags") %></h3></div>
|
||||
<div class="tags-section-content">
|
||||
<div class="merge-form">
|
||||
<div class="tag-form-row">
|
||||
@@ -72,16 +72,16 @@
|
||||
<option value={tag_name} selected={tag_name == @tags_editor.merge_target}><%= tag_name %></option>
|
||||
<% end %>
|
||||
</select>
|
||||
<button class="primary" type="button" phx-click="merge_tags_editor" disabled={length(@tags_editor.selected) < 2} phx-target={@myself}><%= translated("Merge") %></button>
|
||||
<button class="primary" type="button" phx-click="merge_tags_editor" disabled={length(@tags_editor.selected) < 2} phx-target={@myself}><%= dgettext("ui", "Merge") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tags-section" id="tags-section-sync">
|
||||
<div class="tags-section-header"><h3><%= translated("Sync") %></h3></div>
|
||||
<div class="tags-section-header"><h3><%= dgettext("ui", "Sync") %></h3></div>
|
||||
<div class="tags-section-content">
|
||||
<button class="secondary" type="button" phx-click="sync_tags_editor" phx-target={@myself}><%= translated("Discover") %></button>
|
||||
<button class="secondary" type="button" phx-click="sync_tags_editor" phx-target={@myself}><%= dgettext("ui", "Discover") %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
defmodule BDS.Desktop.ShellLive.TaskLocalization do
|
||||
@moduledoc false
|
||||
|
||||
alias BDS.Desktop.ShellData
|
||||
|
||||
def localize_task_status(task_status, locale) do
|
||||
tasks = Enum.map(Map.get(task_status, :tasks, []), &localize_task(&1, locale))
|
||||
@@ -15,13 +14,13 @@ defmodule BDS.Desktop.ShellLive.TaskLocalization do
|
||||
def translate_editor_meta(items, locale) do
|
||||
Enum.map(items, fn item ->
|
||||
item
|
||||
|> Map.update(:label, nil, &ShellData.translate(&1, %{}, locale))
|
||||
|> Map.update(:label, nil, &BDS.Gettext.lgettext(locale, "ui", &1))
|
||||
|> Map.update(:value, nil, &translate_editor_meta_value(&1, locale))
|
||||
end)
|
||||
end
|
||||
|
||||
def translate_for_socket(socket, text) when is_binary(text),
|
||||
do: ShellData.translate(text, %{}, socket.assigns.page_language)
|
||||
do: BDS.Gettext.lgettext(socket.assigns.page_language, "ui", text)
|
||||
|
||||
def translate_for_socket(_socket, text), do: text
|
||||
|
||||
@@ -42,7 +41,7 @@ defmodule BDS.Desktop.ShellLive.TaskLocalization do
|
||||
progress = Map.get(task, :progress)
|
||||
|
||||
task
|
||||
|> Map.put(:name, ShellData.translate(task.name, %{}, locale))
|
||||
|> Map.put(:name, BDS.Gettext.lgettext(locale, "ui", task.name))
|
||||
|> Map.put(:message, localize_task_message(Map.get(task, :message), locale))
|
||||
|> Map.put(:group_name, localize_task_group(Map.get(task, :group_name), locale))
|
||||
|> Map.put(:status_label, localize_task_status_label(task.status, locale))
|
||||
@@ -54,30 +53,30 @@ defmodule BDS.Desktop.ShellLive.TaskLocalization do
|
||||
|
||||
defp localize_task_message(nil, _locale), do: nil
|
||||
defp localize_task_message("", _locale), do: ""
|
||||
defp localize_task_message(message, locale), do: ShellData.translate(message, %{}, locale)
|
||||
defp localize_task_message(message, locale), do: BDS.Gettext.lgettext(locale, "ui", message)
|
||||
|
||||
defp localize_task_group(nil, _locale), do: nil
|
||||
defp localize_task_group(group, locale), do: ShellData.translate(group, %{}, locale)
|
||||
defp localize_task_group(group, locale), do: BDS.Gettext.lgettext(locale, "ui", group)
|
||||
|
||||
defp localize_task_status_label(status, locale) do
|
||||
status
|
||||
|> to_string()
|
||||
|> String.capitalize()
|
||||
|> ShellData.translate(%{}, locale)
|
||||
|> then(&BDS.Gettext.lgettext(locale, "ui", &1))
|
||||
end
|
||||
|
||||
defp localized_running_task_message([], _locale), do: nil
|
||||
|
||||
defp localized_running_task_message([task | _rest], locale) do
|
||||
cond do
|
||||
task.status == :pending -> ShellData.translate("Queued", %{}, locale) <> ": " <> task.name
|
||||
task.status == :pending -> BDS.Gettext.lgettext(locale, "ui", "Queued") <> ": " <> task.name
|
||||
is_binary(task.message) and task.message != "" -> task.name <> ": " <> task.message
|
||||
true -> task.name
|
||||
end
|
||||
end
|
||||
|
||||
defp translate_editor_meta_value(value, locale) when is_binary(value),
|
||||
do: ShellData.translate(value, %{}, locale)
|
||||
do: BDS.Gettext.lgettext(locale, "ui", value)
|
||||
|
||||
defp translate_editor_meta_value(value, _locale), do: value
|
||||
end
|
||||
|
||||
@@ -4,8 +4,8 @@ defmodule BDS.Desktop.ShellLive.TemplateEditor do
|
||||
use Phoenix.LiveComponent
|
||||
|
||||
alias BDS.{MCP, Templates}
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.Templates.Template
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
embed_templates("template_editor_html/*")
|
||||
|
||||
@@ -107,17 +107,17 @@ defmodule BDS.Desktop.ShellLive.TemplateEditor do
|
||||
socket
|
||||
|> assign(:draft, nil)
|
||||
|> build_data()
|
||||
|> notify_output(translated("Templates"), translated("Template saved"))
|
||||
|> notify_output(dgettext("ui", "Templates"), dgettext("ui", "Template saved"))
|
||||
|> notify_reload()
|
||||
else
|
||||
{:ok, %{valid: false, errors: errors}} ->
|
||||
socket
|
||||
|> notify_output(translated("Templates"), Enum.join(errors, "; "), "error")
|
||||
|> notify_output(dgettext("ui", "Templates"), Enum.join(errors, "; "), "error")
|
||||
|> notify_reload()
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> notify_output(translated("Templates"), inspect(reason), "error")
|
||||
|> notify_output(dgettext("ui", "Templates"), inspect(reason), "error")
|
||||
|> notify_reload()
|
||||
end
|
||||
end
|
||||
@@ -139,17 +139,17 @@ defmodule BDS.Desktop.ShellLive.TemplateEditor do
|
||||
socket
|
||||
|> assign(:draft, nil)
|
||||
|> build_data()
|
||||
|> notify_output(translated("Templates"), translated("Template published"))
|
||||
|> notify_output(dgettext("ui", "Templates"), dgettext("ui", "Template published"))
|
||||
|> notify_reload()
|
||||
else
|
||||
{:ok, %{valid: false, errors: errors}} ->
|
||||
socket
|
||||
|> notify_output(translated("Templates"), Enum.join(errors, "; "), "error")
|
||||
|> notify_output(dgettext("ui", "Templates"), Enum.join(errors, "; "), "error")
|
||||
|> notify_reload()
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> notify_output(translated("Templates"), inspect(reason), "error")
|
||||
|> notify_output(dgettext("ui", "Templates"), inspect(reason), "error")
|
||||
|> notify_reload()
|
||||
end
|
||||
end
|
||||
@@ -165,10 +165,10 @@ defmodule BDS.Desktop.ShellLive.TemplateEditor do
|
||||
%Template{} = template ->
|
||||
case MCP.validate_template(current_draft(socket.assigns, template)["content"] || "") do
|
||||
{:ok, %{valid: true}} ->
|
||||
notify_output(socket, translated("Templates"), translated("Template syntax is valid"))
|
||||
notify_output(socket, dgettext("ui", "Templates"), dgettext("ui", "Template syntax is valid"))
|
||||
|
||||
{:ok, %{valid: false, errors: errors}} ->
|
||||
notify_output(socket, translated("Templates"), Enum.join(errors, "; "), "error")
|
||||
notify_output(socket, dgettext("ui", "Templates"), Enum.join(errors, "; "), "error")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -183,7 +183,7 @@ defmodule BDS.Desktop.ShellLive.TemplateEditor do
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> notify_output(translated("Templates"), inspect(reason), "error")
|
||||
|> notify_output(dgettext("ui", "Templates"), inspect(reason), "error")
|
||||
|> notify_reload()
|
||||
end
|
||||
end
|
||||
@@ -235,7 +235,4 @@ defmodule BDS.Desktop.ShellLive.TemplateEditor do
|
||||
send(self(), :reload_shell)
|
||||
socket
|
||||
end
|
||||
|
||||
defp translated(text, bindings \\ %{}),
|
||||
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())
|
||||
end
|
||||
@@ -7,28 +7,28 @@
|
||||
"status-#{@template_editor.status}"
|
||||
]} data-testid="template-status-badge"><%= BDS.Desktop.ShellData.dashboard_status_label(@template_editor.status) %></span>
|
||||
<%= if @template_editor.can_publish? do %>
|
||||
<button class="success" data-testid="template-publish-button" type="button" phx-click="publish_template_editor" phx-target={@myself}><%= BDS.Desktop.ShellData.translate("Publish", %{}, BDS.Desktop.UILocale.current()) %></button>
|
||||
<button class="success" data-testid="template-publish-button" type="button" phx-click="publish_template_editor" phx-target={@myself}><%= dgettext("ui", "Publish") %></button>
|
||||
<% end %>
|
||||
<button class="secondary templates-save-button" type="button" phx-click="save_template_editor" phx-target={@myself}><%= BDS.Desktop.ShellData.translate("Save", %{}, BDS.Desktop.UILocale.current()) %></button>
|
||||
<button class="secondary templates-validate-button" type="button" phx-click="validate_template_editor" phx-target={@myself}><%= BDS.Desktop.ShellData.translate("Validate", %{}, BDS.Desktop.UILocale.current()) %></button>
|
||||
<button class="secondary danger" type="button" phx-click="delete_template_editor" phx-target={@myself}><%= BDS.Desktop.ShellData.translate("Delete", %{}, BDS.Desktop.UILocale.current()) %></button>
|
||||
<button class="secondary templates-save-button" type="button" phx-click="save_template_editor" phx-target={@myself}><%= dgettext("ui", "Save") %></button>
|
||||
<button class="secondary templates-validate-button" type="button" phx-click="validate_template_editor" phx-target={@myself}><%= dgettext("ui", "Validate") %></button>
|
||||
<button class="secondary danger" type="button" phx-click="delete_template_editor" phx-target={@myself}><%= dgettext("ui", "Delete") %></button>
|
||||
</div>
|
||||
</div>
|
||||
<form class="editor-content templates-view" phx-change="change_template_editor" phx-target={@myself}>
|
||||
<div class="editor-header-row templates-meta-row">
|
||||
<div class="editor-meta">
|
||||
<div class="editor-field-row">
|
||||
<div class="editor-field"><label><%= BDS.Desktop.ShellData.translate("Title", %{}, BDS.Desktop.UILocale.current()) %></label><input type="text" name="template_editor[title]" value={@template_editor.title} /></div>
|
||||
<div class="editor-field"><label><%= BDS.Desktop.ShellData.translate("Slug", %{}, BDS.Desktop.UILocale.current()) %></label><input type="text" name="template_editor[slug]" value={@template_editor.slug} /></div>
|
||||
<div class="editor-field"><label><%= dgettext("ui", "Title") %></label><input type="text" name="template_editor[title]" value={@template_editor.title} /></div>
|
||||
<div class="editor-field"><label><%= dgettext("ui", "Slug") %></label><input type="text" name="template_editor[slug]" value={@template_editor.slug} /></div>
|
||||
</div>
|
||||
<div class="editor-field-row">
|
||||
<div class="editor-field"><label><%= BDS.Desktop.ShellData.translate("Kind", %{}, BDS.Desktop.UILocale.current()) %></label><select name="template_editor[kind]"><option value="post" selected={@template_editor.kind == :post or @template_editor.kind == "post"}>post</option><option value="list" selected={@template_editor.kind == :list or @template_editor.kind == "list"}>list</option><option value="not-found" selected={@template_editor.kind == :"not-found" or @template_editor.kind == "not-found"}>not-found</option><option value="partial" selected={@template_editor.kind == :partial or @template_editor.kind == "partial"}>partial</option></select></div>
|
||||
<div class="editor-field templates-enabled-field"><label><input type="checkbox" name="template_editor[enabled]" checked={@template_editor.enabled} /> <%= BDS.Desktop.ShellData.translate("Enabled", %{}, BDS.Desktop.UILocale.current()) %></label></div>
|
||||
<div class="editor-field"><label><%= dgettext("ui", "Kind") %></label><select name="template_editor[kind]"><option value="post" selected={@template_editor.kind == :post or @template_editor.kind == "post"}>post</option><option value="list" selected={@template_editor.kind == :list or @template_editor.kind == "list"}>list</option><option value="not-found" selected={@template_editor.kind == :"not-found" or @template_editor.kind == "not-found"}>not-found</option><option value="partial" selected={@template_editor.kind == :partial or @template_editor.kind == "partial"}>partial</option></select></div>
|
||||
<div class="editor-field templates-enabled-field"><label><input type="checkbox" name="template_editor[enabled]" checked={@template_editor.enabled} /> <%= dgettext("ui", "Enabled") %></label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-body templates-editor">
|
||||
<div class="editor-toolbar templates-toolbar"><div class="editor-toolbar-left"><label><%= BDS.Desktop.ShellData.translate("Content", %{}, BDS.Desktop.UILocale.current()) %></label></div></div>
|
||||
<div class="editor-toolbar templates-toolbar"><div class="editor-toolbar-left"><label><%= dgettext("ui", "Content") %></label></div></div>
|
||||
<div
|
||||
id={"template-editor-monaco-shell-#{@template_editor.id}"}
|
||||
class="templates-monaco monaco-editor-shell"
|
||||
@@ -42,6 +42,6 @@
|
||||
<textarea id={"template-editor-content-#{@template_editor.id}"} class="monaco-editor-input code-editor-textarea" name="template_editor[content]" spellcheck="false"><%= @template_editor.content %></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-footer"><span class="text-muted text-small"><%= BDS.Desktop.ShellData.translate("Created", %{}, BDS.Desktop.UILocale.current()) %>: <%= BDS.Persistence.timestamp_to_iso8601(@template_editor.created_at) %></span><span class="text-muted text-small"><%= BDS.Desktop.ShellData.translate("Updated", %{}, BDS.Desktop.UILocale.current()) %>: <%= BDS.Persistence.timestamp_to_iso8601(@template_editor.updated_at) %></span></div>
|
||||
<div class="editor-footer"><span class="text-muted text-small"><%= dgettext("ui", "Created") %>: <%= BDS.Persistence.timestamp_to_iso8601(@template_editor.created_at) %></span><span class="text-muted text-small"><%= dgettext("ui", "Updated") %>: <%= BDS.Persistence.timestamp_to_iso8601(@template_editor.updated_at) %></span></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -30,6 +30,7 @@ defmodule BDS.Desktop.UILocale do
|
||||
@spec put(locale()) :: :ok
|
||||
def put(locale) do
|
||||
Process.put(@key, locale)
|
||||
BDS.Gettext.put_locale(locale)
|
||||
:ok
|
||||
end
|
||||
|
||||
|
||||
42
lib/bds/gettext.ex
Normal file
42
lib/bds/gettext.ex
Normal file
@@ -0,0 +1,42 @@
|
||||
defmodule BDS.Gettext do
|
||||
@moduledoc """
|
||||
Gettext backend for bDS2.
|
||||
|
||||
Two domains are used:
|
||||
- "ui" : application chrome, menus, settings, dashboard, toasts
|
||||
- "render" : blog output (Liquid templates, archive labels, pagination, etc.)
|
||||
|
||||
The UI locale is managed per-process via `put_locale/1`.
|
||||
The render locale is passed explicitly via `lgettext/4` so that a single
|
||||
process can resolve UI strings in the OS language and render strings in
|
||||
the project's mainLanguage simultaneously.
|
||||
"""
|
||||
|
||||
use Gettext.Backend, otp_app: :bds
|
||||
|
||||
@doc """
|
||||
Convenience wrapper around `lgettext/4` with empty bindings.
|
||||
|
||||
Returns the resolved string directly (not a tuple).
|
||||
"""
|
||||
def lgettext(locale, domain, msgid) do
|
||||
case lgettext(locale, domain, msgid, %{}) do
|
||||
{:ok, text} -> text
|
||||
{:default, text} -> text
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sets the locale for this backend in the current process.
|
||||
"""
|
||||
def put_locale(locale) do
|
||||
Gettext.put_locale(__MODULE__, locale)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the locale for this backend in the current process.
|
||||
"""
|
||||
def get_locale do
|
||||
Gettext.get_locale(__MODULE__)
|
||||
end
|
||||
end
|
||||
@@ -28,18 +28,6 @@ defmodule BDS.I18n do
|
||||
|
||||
@default_language "en"
|
||||
@default_format_locale "en-US"
|
||||
@locale_files Path.expand("../../priv/i18n/locales/*.json", __DIR__)
|
||||
|> Path.wildcard()
|
||||
|> Enum.sort()
|
||||
|
||||
for file <- @locale_files do
|
||||
@external_resource file
|
||||
end
|
||||
|
||||
@catalogs Enum.into(@locale_files, %{}, fn file ->
|
||||
locale = file |> Path.basename(".json") |> String.downcase()
|
||||
{locale, Jason.decode!(File.read!(file))}
|
||||
end)
|
||||
|
||||
def supported_languages, do: @supported_languages
|
||||
|
||||
@@ -81,33 +69,6 @@ defmodule BDS.I18n do
|
||||
}
|
||||
end
|
||||
|
||||
def get_render_translations(language) do
|
||||
language
|
||||
|> resolve_render_locale()
|
||||
|> catalog_for_locale()
|
||||
end
|
||||
|
||||
def get_ui_translations(locale) do
|
||||
locale
|
||||
|> resolve_ui_locale()
|
||||
|> catalog_for_locale()
|
||||
end
|
||||
|
||||
def translate(language, key) do
|
||||
key = key |> to_string() |> String.trim()
|
||||
|
||||
case resolve_supported_locale(language) do
|
||||
nil ->
|
||||
Map.get(catalog_for_locale(@default_language), key, key)
|
||||
|
||||
locale ->
|
||||
Map.get(catalog_for_locale(locale), key, key)
|
||||
end
|
||||
end
|
||||
|
||||
def translate_render(language, key), do: translate(language, key)
|
||||
def translate_ui(locale, key), do: translate(locale, key)
|
||||
|
||||
def flag(language) do
|
||||
language
|
||||
|> resolve_supported_locale()
|
||||
@@ -151,8 +112,4 @@ defmodule BDS.I18n do
|
||||
|> String.split("-", parts: 2)
|
||||
|> List.first()
|
||||
end
|
||||
|
||||
defp catalog_for_locale(locale) do
|
||||
Map.get(@catalogs, locale, Map.get(@catalogs, @default_language, %{}))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,7 +3,6 @@ defmodule BDS.Rendering.Filters do
|
||||
|
||||
use Liquex.Filter
|
||||
|
||||
alias BDS.I18n
|
||||
alias BDS.Slug
|
||||
|
||||
def i18n(value, language, _context) do
|
||||
@@ -12,7 +11,7 @@ defmodule BDS.Rendering.Filters do
|
||||
if key == "" do
|
||||
""
|
||||
else
|
||||
I18n.translate(language, key)
|
||||
BDS.Gettext.lgettext(language, "render", key)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -59,7 +58,7 @@ defmodule BDS.Rendering.Filters do
|
||||
default_macro_title(
|
||||
Map.get(params, "title"),
|
||||
language,
|
||||
"render.video.youtubeTitle"
|
||||
"YouTube video"
|
||||
)
|
||||
},
|
||||
context
|
||||
@@ -71,7 +70,7 @@ defmodule BDS.Rendering.Filters do
|
||||
%{
|
||||
"id" => Map.get(params, "id", ""),
|
||||
"title" =>
|
||||
default_macro_title(Map.get(params, "title"), language, "render.video.vimeoTitle")
|
||||
default_macro_title(Map.get(params, "title"), language, "Vimeo video")
|
||||
},
|
||||
context
|
||||
)
|
||||
@@ -82,13 +81,17 @@ defmodule BDS.Rendering.Filters do
|
||||
end)
|
||||
end
|
||||
|
||||
defp default_macro_title(nil, language, translation_key),
|
||||
do: I18n.translate(language, translation_key)
|
||||
defp default_macro_title(nil, language, translation),
|
||||
do: translated_macro_title(language, translation)
|
||||
|
||||
defp default_macro_title("", language, translation_key),
|
||||
do: I18n.translate(language, translation_key)
|
||||
defp default_macro_title("", language, translation),
|
||||
do: translated_macro_title(language, translation)
|
||||
|
||||
defp default_macro_title(title, _language, _translation_key), do: title
|
||||
defp default_macro_title(title, _language, _translation), do: title
|
||||
|
||||
defp translated_macro_title(language, translation) do
|
||||
BDS.Gettext.lgettext(language, "render", translation)
|
||||
end
|
||||
|
||||
defp parse_macro_params(nil), do: %{}
|
||||
defp parse_macro_params(""), do: %{}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
defmodule BDS.Rendering.I18n do
|
||||
@moduledoc false
|
||||
|
||||
defdelegate supported_languages(), to: BDS.I18n
|
||||
defdelegate normalize_language(language), to: BDS.I18n
|
||||
defdelegate translate(language, key), to: BDS.I18n
|
||||
defdelegate flag(language), to: BDS.I18n
|
||||
end
|
||||
88
lib/bds/rendering/labels.ex
Normal file
88
lib/bds/rendering/labels.ex
Normal file
@@ -0,0 +1,88 @@
|
||||
defmodule BDS.Rendering.Labels do
|
||||
@moduledoc """
|
||||
Pre-translated render labels passed into Liquid template context.
|
||||
|
||||
All strings use constant msgids so mix gettext.extract can discover them.
|
||||
The render locale is bound explicitly via Gettext.with_locale/3.
|
||||
"""
|
||||
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
def for_language(language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn ->
|
||||
%{
|
||||
taxonomy_label: dgettext("render", "Taxonomy"),
|
||||
backlinks_label: dgettext("render", "Backlinks"),
|
||||
linked_from_label: dgettext("render", "Linked from"),
|
||||
archive_label: dgettext("render", "Archive"),
|
||||
pagination_label: dgettext("render", "Pagination"),
|
||||
newer_label: dgettext("render", "newer"),
|
||||
older_label: dgettext("render", "older"),
|
||||
calendar_open_label: dgettext("render", "Open calendar"),
|
||||
calendar_loading_label: dgettext("render", "Loading calendar…"),
|
||||
calendar_error_label: dgettext("render", "Calendar data could not be loaded."),
|
||||
calendar_title_label: dgettext("render", "Archive calendar"),
|
||||
calendar_close_label: dgettext("render", "Close calendar"),
|
||||
language_switcher_label: dgettext("render", "Language"),
|
||||
site_search_label: dgettext("render", "Site search"),
|
||||
search_placeholder: dgettext("render", "Search..."),
|
||||
not_found_message: dgettext("render", "The requested preview page could not be found."),
|
||||
not_found_back_label: dgettext("render", "Back to preview home"),
|
||||
youtube_video: dgettext("render", "YouTube video"),
|
||||
vimeo_video: dgettext("render", "Vimeo video")
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
def month_name(nil, _language), do: nil
|
||||
|
||||
def month_name(1, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "January") end)
|
||||
end
|
||||
|
||||
def month_name(2, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "February") end)
|
||||
end
|
||||
|
||||
def month_name(3, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "March") end)
|
||||
end
|
||||
|
||||
def month_name(4, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "April") end)
|
||||
end
|
||||
|
||||
def month_name(5, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "May") end)
|
||||
end
|
||||
|
||||
def month_name(6, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "June") end)
|
||||
end
|
||||
|
||||
def month_name(7, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "July") end)
|
||||
end
|
||||
|
||||
def month_name(8, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "August") end)
|
||||
end
|
||||
|
||||
def month_name(9, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "September") end)
|
||||
end
|
||||
|
||||
def month_name(10, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "October") end)
|
||||
end
|
||||
|
||||
def month_name(11, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "November") end)
|
||||
end
|
||||
|
||||
def month_name(12, language) do
|
||||
Gettext.with_locale(BDS.Gettext, language, fn -> dgettext("render", "December") end)
|
||||
end
|
||||
|
||||
def month_name(_month, _language), do: nil
|
||||
end
|
||||
@@ -1,13 +1,14 @@
|
||||
defmodule BDS.Rendering.ListArchive do
|
||||
@moduledoc false
|
||||
|
||||
alias BDS.I18n
|
||||
alias BDS.MapUtils
|
||||
alias BDS.Persistence
|
||||
alias BDS.Rendering.Labels
|
||||
alias BDS.Rendering.LinksAndLanguages
|
||||
alias BDS.Rendering.Metadata, as: RenderMetadata
|
||||
alias BDS.Rendering.PostRendering
|
||||
alias BDS.Rendering.TemplateSelection
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
def list_assigns(project_id, assigns) do
|
||||
metadata = RenderMetadata.project_metadata(project_id)
|
||||
@@ -95,7 +96,9 @@ defmodule BDS.Rendering.ListArchive do
|
||||
canonical_media_path_by_source_path: canonical_media_paths,
|
||||
post_data_json_by_id:
|
||||
Enum.into(posts, %{}, fn post -> {post.id, PostRendering.post_data_json_value(post)} end),
|
||||
day_blocks: day_blocks
|
||||
day_blocks: day_blocks,
|
||||
archive_month_name: Labels.month_name(Map.get(normalized_archive_context, :month), language),
|
||||
labels: Labels.for_language(language)
|
||||
}
|
||||
end
|
||||
|
||||
@@ -145,7 +148,7 @@ defmodule BDS.Rendering.ListArchive do
|
||||
Map.get(
|
||||
assigns,
|
||||
"not_found_message",
|
||||
I18n.translate(language, "render.notFound.message")
|
||||
BDS.Gettext.lgettext(language, "render", "The requested preview page could not be found.")
|
||||
)
|
||||
),
|
||||
not_found_back_label:
|
||||
@@ -155,9 +158,10 @@ defmodule BDS.Rendering.ListArchive do
|
||||
Map.get(
|
||||
assigns,
|
||||
"not_found_back_label",
|
||||
I18n.translate(language, "render.notFound.back")
|
||||
BDS.Gettext.lgettext(language, "render", "Back to preview home")
|
||||
)
|
||||
)
|
||||
),
|
||||
labels: Labels.for_language(language)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ defmodule BDS.Rendering.PostRendering do
|
||||
@moduledoc false
|
||||
|
||||
alias BDS.Rendering.Filters
|
||||
alias BDS.Rendering.Labels
|
||||
alias BDS.Rendering.LinksAndLanguages
|
||||
alias BDS.Rendering.Metadata, as: RenderMetadata
|
||||
alias BDS.Rendering.TemplateSelection
|
||||
@@ -95,7 +96,8 @@ defmodule BDS.Rendering.PostRendering do
|
||||
canonical_post_path_by_slug: canonical_post_paths,
|
||||
canonical_media_path_by_source_path: canonical_media_paths,
|
||||
post_data_json_by_id: post_data_json(post_assigns, post_record),
|
||||
post: build_post_context(post_assigns, post_record, incoming_links, outgoing_links)
|
||||
post: build_post_context(post_assigns, post_record, incoming_links, outgoing_links),
|
||||
labels: Labels.for_language(language)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -1,114 +1,118 @@
|
||||
defmodule BDS.UI.Registry do
|
||||
@moduledoc false
|
||||
|
||||
@sidebar_views [
|
||||
%{
|
||||
id: :posts,
|
||||
label: "Posts",
|
||||
activity_group: :top,
|
||||
editor_route: :post,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :pages,
|
||||
label: "Pages",
|
||||
activity_group: :top,
|
||||
editor_route: :post,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :media,
|
||||
label: "Media",
|
||||
activity_group: :top,
|
||||
editor_route: :media,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :scripts,
|
||||
label: "Scripts",
|
||||
activity_group: :top,
|
||||
editor_route: :scripts,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :templates,
|
||||
label: "Templates",
|
||||
activity_group: :top,
|
||||
editor_route: :templates,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :tags,
|
||||
label: "Tags",
|
||||
activity_group: :top,
|
||||
editor_route: :tags,
|
||||
singleton: true,
|
||||
demo_kind: :singleton
|
||||
},
|
||||
%{
|
||||
id: :chat,
|
||||
label: "AI Assistant",
|
||||
activity_group: :top,
|
||||
editor_route: :chat,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :import,
|
||||
label: "Import",
|
||||
activity_group: :top,
|
||||
editor_route: :import,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :git,
|
||||
label: "Source Control",
|
||||
activity_group: :bottom,
|
||||
editor_route: :git_diff,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :settings,
|
||||
label: "Settings",
|
||||
activity_group: :bottom,
|
||||
editor_route: :settings,
|
||||
singleton: true,
|
||||
demo_kind: :singleton
|
||||
}
|
||||
]
|
||||
|
||||
@editor_routes [
|
||||
%{id: :dashboard, singleton: true, entity_tab: false, title: "Dashboard"},
|
||||
%{id: :post, singleton: false, entity_tab: true, title: "Post"},
|
||||
%{id: :media, singleton: false, entity_tab: true, title: "Media"},
|
||||
%{id: :settings, singleton: true, entity_tab: false, title: "Settings"},
|
||||
%{id: :style, singleton: true, entity_tab: false, title: "Style"},
|
||||
%{id: :tags, singleton: true, entity_tab: false, title: "Tags"},
|
||||
%{id: :chat, singleton: false, entity_tab: true, title: "Chat"},
|
||||
%{id: :import, singleton: false, entity_tab: true, title: "Import"},
|
||||
%{id: :menu_editor, singleton: true, entity_tab: false, title: "Menu"},
|
||||
%{id: :metadata_diff, singleton: true, entity_tab: false, title: "Metadata Diff"},
|
||||
%{id: :git_diff, singleton: false, entity_tab: true, title: "Git Diff"},
|
||||
%{id: :documentation, singleton: true, entity_tab: false, title: "Documentation"},
|
||||
%{id: :api_documentation, singleton: true, entity_tab: false, title: "API"},
|
||||
%{id: :site_validation, singleton: true, entity_tab: false, title: "Site Validation"},
|
||||
%{id: :translation_validation, singleton: true, entity_tab: false, title: "Translations"},
|
||||
%{id: :scripts, singleton: false, entity_tab: true, title: "Script"},
|
||||
%{id: :templates, singleton: false, entity_tab: true, title: "Template"},
|
||||
%{id: :find_duplicates, singleton: true, entity_tab: false, title: "Find Duplicates"}
|
||||
]
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
def default_sidebar_view, do: :posts
|
||||
def sidebar_views, do: @sidebar_views
|
||||
def editor_routes, do: @editor_routes
|
||||
|
||||
def sidebar_view(id) when is_atom(id), do: Enum.find(@sidebar_views, &(&1.id == id))
|
||||
def editor_route(id) when is_atom(id), do: Enum.find(@editor_routes, &(&1.id == id))
|
||||
def sidebar_views do
|
||||
[
|
||||
%{
|
||||
id: :posts,
|
||||
label: dgettext("ui", "Posts"),
|
||||
activity_group: :top,
|
||||
editor_route: :post,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :pages,
|
||||
label: dgettext("ui", "Pages"),
|
||||
activity_group: :top,
|
||||
editor_route: :post,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :media,
|
||||
label: dgettext("ui", "Media"),
|
||||
activity_group: :top,
|
||||
editor_route: :media,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :scripts,
|
||||
label: dgettext("ui", "Scripts"),
|
||||
activity_group: :top,
|
||||
editor_route: :scripts,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :templates,
|
||||
label: dgettext("ui", "Templates"),
|
||||
activity_group: :top,
|
||||
editor_route: :templates,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :tags,
|
||||
label: dgettext("ui", "Tags"),
|
||||
activity_group: :top,
|
||||
editor_route: :tags,
|
||||
singleton: true,
|
||||
demo_kind: :singleton
|
||||
},
|
||||
%{
|
||||
id: :chat,
|
||||
label: dgettext("ui", "AI Assistant"),
|
||||
activity_group: :top,
|
||||
editor_route: :chat,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :import,
|
||||
label: dgettext("ui", "Import"),
|
||||
activity_group: :top,
|
||||
editor_route: :import,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :git,
|
||||
label: dgettext("ui", "Source Control"),
|
||||
activity_group: :bottom,
|
||||
editor_route: :git_diff,
|
||||
entity_tab: true,
|
||||
demo_kind: :entity
|
||||
},
|
||||
%{
|
||||
id: :settings,
|
||||
label: dgettext("ui", "Settings"),
|
||||
activity_group: :bottom,
|
||||
editor_route: :settings,
|
||||
singleton: true,
|
||||
demo_kind: :singleton
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def editor_routes do
|
||||
[
|
||||
%{id: :dashboard, singleton: true, entity_tab: false, title: dgettext("ui", "Dashboard")},
|
||||
%{id: :post, singleton: false, entity_tab: true, title: dgettext("ui", "Post")},
|
||||
%{id: :media, singleton: false, entity_tab: true, title: dgettext("ui", "Media")},
|
||||
%{id: :settings, singleton: true, entity_tab: false, title: dgettext("ui", "Settings")},
|
||||
%{id: :style, singleton: true, entity_tab: false, title: dgettext("ui", "Style")},
|
||||
%{id: :tags, singleton: true, entity_tab: false, title: dgettext("ui", "Tags")},
|
||||
%{id: :chat, singleton: false, entity_tab: true, title: dgettext("ui", "Chat")},
|
||||
%{id: :import, singleton: false, entity_tab: true, title: dgettext("ui", "Import")},
|
||||
%{id: :menu_editor, singleton: true, entity_tab: false, title: dgettext("ui", "Menu")},
|
||||
%{id: :metadata_diff, singleton: true, entity_tab: false, title: dgettext("ui", "Metadata Diff")},
|
||||
%{id: :git_diff, singleton: false, entity_tab: true, title: dgettext("ui", "Git Diff")},
|
||||
%{id: :documentation, singleton: true, entity_tab: false, title: dgettext("ui", "Documentation")},
|
||||
%{id: :api_documentation, singleton: true, entity_tab: false, title: dgettext("ui", "API")},
|
||||
%{id: :site_validation, singleton: true, entity_tab: false, title: dgettext("ui", "Site Validation")},
|
||||
%{id: :translation_validation, singleton: true, entity_tab: false, title: dgettext("ui", "Translations")},
|
||||
%{id: :scripts, singleton: false, entity_tab: true, title: dgettext("ui", "Script")},
|
||||
%{id: :templates, singleton: false, entity_tab: true, title: dgettext("ui", "Template")},
|
||||
%{id: :find_duplicates, singleton: true, entity_tab: false, title: dgettext("ui", "Find Duplicates")}
|
||||
]
|
||||
end
|
||||
|
||||
def sidebar_view(id) when is_atom(id), do: Enum.find(sidebar_views(), &(&1.id == id))
|
||||
def editor_route(id) when is_atom(id), do: Enum.find(editor_routes(), &(&1.id == id))
|
||||
end
|
||||
|
||||
@@ -2,6 +2,7 @@ defmodule BDS.UI.Sidebar do
|
||||
@moduledoc false
|
||||
|
||||
import Ecto.Query
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
alias BDS.AI.ChatConversation
|
||||
alias BDS.ImportDefinitions
|
||||
@@ -29,8 +30,8 @@ defmodule BDS.UI.Sidebar do
|
||||
"chat" => view(project_id, "chat"),
|
||||
"import" =>
|
||||
entity_list_view(
|
||||
"Import",
|
||||
"Import definitions",
|
||||
dgettext("ui", "Import"),
|
||||
dgettext("ui", "Import definitions"),
|
||||
"import",
|
||||
list_import_definitions(project_id)
|
||||
),
|
||||
@@ -57,21 +58,36 @@ defmodule BDS.UI.Sidebar do
|
||||
media_view(project_id, params)
|
||||
|
||||
"scripts" ->
|
||||
entity_list_view("Scripts", "Automation helpers", "scripts", list_scripts(project_id))
|
||||
entity_list_view(
|
||||
dgettext("ui", "Scripts"),
|
||||
dgettext("ui", "Automation helpers"),
|
||||
"scripts",
|
||||
list_scripts(project_id)
|
||||
)
|
||||
|
||||
"templates" ->
|
||||
entity_list_view("Templates", "Site rendering", "templates", list_templates(project_id))
|
||||
entity_list_view(
|
||||
dgettext("ui", "Templates"),
|
||||
dgettext("ui", "Site rendering"),
|
||||
"templates",
|
||||
list_templates(project_id)
|
||||
)
|
||||
|
||||
"tags" ->
|
||||
tags_nav_view(list_tags(project_id))
|
||||
|
||||
"chat" ->
|
||||
entity_list_view("Chat", "AI conversations", "chat", list_conversations())
|
||||
entity_list_view(
|
||||
dgettext("ui", "Chat"),
|
||||
dgettext("ui", "AI conversations"),
|
||||
"chat",
|
||||
list_conversations()
|
||||
)
|
||||
|
||||
"import" ->
|
||||
entity_list_view(
|
||||
"Import",
|
||||
"Import definitions",
|
||||
dgettext("ui", "Import"),
|
||||
dgettext("ui", "Import definitions"),
|
||||
"import",
|
||||
list_import_definitions(project_id)
|
||||
)
|
||||
@@ -92,11 +108,35 @@ defmodule BDS.UI.Sidebar do
|
||||
"posts" => empty_view("posts"),
|
||||
"pages" => empty_view("pages"),
|
||||
"media" => empty_view("media"),
|
||||
"scripts" => entity_list_view("Scripts", "Automation helpers", "scripts", []),
|
||||
"templates" => entity_list_view("Templates", "Site rendering", "templates", []),
|
||||
"scripts" =>
|
||||
entity_list_view(
|
||||
dgettext("ui", "Scripts"),
|
||||
dgettext("ui", "Automation helpers"),
|
||||
"scripts",
|
||||
[]
|
||||
),
|
||||
"templates" =>
|
||||
entity_list_view(
|
||||
dgettext("ui", "Templates"),
|
||||
dgettext("ui", "Site rendering"),
|
||||
"templates",
|
||||
[]
|
||||
),
|
||||
"tags" => tags_nav_view([]),
|
||||
"chat" => entity_list_view("Chat", "AI conversations", "chat", []),
|
||||
"import" => entity_list_view("Import", "Import definitions", "import", []),
|
||||
"chat" =>
|
||||
entity_list_view(
|
||||
dgettext("ui", "Chat"),
|
||||
dgettext("ui", "AI conversations"),
|
||||
"chat",
|
||||
[]
|
||||
),
|
||||
"import" =>
|
||||
entity_list_view(
|
||||
dgettext("ui", "Import"),
|
||||
dgettext("ui", "Import definitions"),
|
||||
"import",
|
||||
[]
|
||||
),
|
||||
"git" => git_view(),
|
||||
"settings" => settings_nav_view()
|
||||
}
|
||||
@@ -105,19 +145,56 @@ defmodule BDS.UI.Sidebar do
|
||||
defp empty_view("posts"), do: posts_view_data([], [], %{}, false, empty_filter_params(), %{})
|
||||
defp empty_view("pages"), do: posts_view_data([], [], %{}, true, empty_filter_params(), %{})
|
||||
defp empty_view("media"), do: media_view_data([], [], empty_filter_params(), %{})
|
||||
defp empty_view("scripts"), do: entity_list_view("Scripts", "Automation helpers", "scripts", [])
|
||||
|
||||
defp empty_view("scripts"),
|
||||
do:
|
||||
entity_list_view(
|
||||
dgettext("ui", "Scripts"),
|
||||
dgettext("ui", "Automation helpers"),
|
||||
"scripts",
|
||||
[]
|
||||
)
|
||||
|
||||
defp empty_view("templates"),
|
||||
do: entity_list_view("Templates", "Site rendering", "templates", [])
|
||||
do:
|
||||
entity_list_view(
|
||||
dgettext("ui", "Templates"),
|
||||
dgettext("ui", "Site rendering"),
|
||||
"templates",
|
||||
[]
|
||||
)
|
||||
|
||||
defp empty_view("tags"), do: tags_nav_view([])
|
||||
defp empty_view("chat"), do: entity_list_view("Chat", "AI conversations", "chat", [])
|
||||
defp empty_view("import"), do: entity_list_view("Import", "Import definitions", "import", [])
|
||||
|
||||
defp empty_view("chat"),
|
||||
do:
|
||||
entity_list_view(
|
||||
dgettext("ui", "Chat"),
|
||||
dgettext("ui", "AI conversations"),
|
||||
"chat",
|
||||
[]
|
||||
)
|
||||
|
||||
defp empty_view("import"),
|
||||
do:
|
||||
entity_list_view(
|
||||
dgettext("ui", "Import"),
|
||||
dgettext("ui", "Import definitions"),
|
||||
"import",
|
||||
[]
|
||||
)
|
||||
|
||||
defp empty_view("git"), do: git_view()
|
||||
defp empty_view("settings"), do: settings_nav_view()
|
||||
|
||||
defp empty_view(_other),
|
||||
do: %{title: "", subtitle: "", layout: "entity_list", items: [], empty_message: "No items"}
|
||||
do: %{
|
||||
title: "",
|
||||
subtitle: "",
|
||||
layout: "entity_list",
|
||||
items: [],
|
||||
empty_message: dgettext("ui", "No items")
|
||||
}
|
||||
|
||||
defp posts_view(project_id, params, pages?) do
|
||||
posts = list_posts(project_id)
|
||||
@@ -144,25 +221,32 @@ defmodule BDS.UI.Sidebar do
|
||||
available_categories = available_categories(base_posts, pages?)
|
||||
|
||||
%{
|
||||
title: if(pages?, do: "Pages", else: "Posts"),
|
||||
title: if(pages?, do: dgettext("ui", "Pages"), else: dgettext("ui", "Posts")),
|
||||
subtitle:
|
||||
if(pages?, do: "Standalone pages", else: "Drafts, published entries, and archive history"),
|
||||
if(pages?,
|
||||
do: dgettext("ui", "Standalone pages"),
|
||||
else: dgettext("ui", "Drafts, published entries, and archive history")
|
||||
),
|
||||
layout: "post_list",
|
||||
empty_message: if(pages?, do: "sidebar.noPagesYet", else: "sidebar.noPostsYet"),
|
||||
empty_message:
|
||||
if(pages?, do: dgettext("ui", "No pages yet"), else: dgettext("ui", "No posts yet")),
|
||||
filters: %{
|
||||
enabled: true,
|
||||
search_placeholder:
|
||||
if(pages?, do: "sidebar.searchPagesPlaceholder", else: "sidebar.searchPostsPlaceholder"),
|
||||
toggle_filters_label: "sidebar.toggleFilters",
|
||||
archive_label: "render.archive",
|
||||
tags_label: "sidebar.tags",
|
||||
categories_label: "sidebar.categories",
|
||||
clear_tags_label: "sidebar.clearTags",
|
||||
clear_categories_label: "sidebar.clearCategories",
|
||||
clear_filters_label: "sidebar.clearFilters",
|
||||
results_label: "sidebar.results",
|
||||
results_for_label: "sidebar.resultsFor",
|
||||
no_results_label: "sidebar.noMatchingPosts",
|
||||
if(pages?,
|
||||
do: dgettext("ui", "Search pages..."),
|
||||
else: dgettext("ui", "Search posts...")
|
||||
),
|
||||
toggle_filters_label: dgettext("ui", "Toggle Filters"),
|
||||
archive_label: dgettext("render", "Archive"),
|
||||
tags_label: dgettext("ui", "Tags"),
|
||||
categories_label: dgettext("ui", "Categories"),
|
||||
clear_tags_label: dgettext("ui", "Clear tags"),
|
||||
clear_categories_label: dgettext("ui", "Clear categories"),
|
||||
clear_filters_label: dgettext("ui", "Clear filters"),
|
||||
results_label: dgettext("ui", "results"),
|
||||
results_for_label: dgettext("ui", "results for"),
|
||||
no_results_label: dgettext("ui", "No matching posts"),
|
||||
year_month_counts: year_month_counts(base_posts, &post_filter_timestamp/1),
|
||||
available_tags: available_tags,
|
||||
available_tag_colors: Map.take(tag_colors, available_tags),
|
||||
@@ -182,16 +266,22 @@ defmodule BDS.UI.Sidebar do
|
||||
}
|
||||
},
|
||||
sections: [
|
||||
build_post_section("Drafts", :draft, grouped_posts.draft, translation_counts, false),
|
||||
build_post_section(
|
||||
"Published",
|
||||
dgettext("ui", "Drafts"),
|
||||
:draft,
|
||||
grouped_posts.draft,
|
||||
translation_counts,
|
||||
false
|
||||
),
|
||||
build_post_section(
|
||||
dgettext("ui", "Published"),
|
||||
:published,
|
||||
grouped_posts.published,
|
||||
translation_counts,
|
||||
true
|
||||
),
|
||||
build_post_section(
|
||||
"Archived",
|
||||
dgettext("ui", "Archived"),
|
||||
:archived,
|
||||
grouped_posts.archived,
|
||||
translation_counts,
|
||||
@@ -215,21 +305,21 @@ defmodule BDS.UI.Sidebar do
|
||||
available_tags = available_tags(base_media, & &1.tags)
|
||||
|
||||
%{
|
||||
title: "Media",
|
||||
subtitle: "Images and files",
|
||||
title: dgettext("ui", "Media"),
|
||||
subtitle: dgettext("ui", "Images and files"),
|
||||
layout: "media_grid",
|
||||
empty_message: "sidebar.noMediaFiles",
|
||||
empty_message: dgettext("ui", "No media files"),
|
||||
filters: %{
|
||||
enabled: true,
|
||||
search_placeholder: "sidebar.searchMediaPlaceholder",
|
||||
toggle_filters_label: "sidebar.toggleFilters",
|
||||
archive_label: "render.archive",
|
||||
tags_label: "sidebar.tags",
|
||||
clear_tags_label: "sidebar.clearTags",
|
||||
clear_filters_label: "sidebar.clearFilters",
|
||||
results_label: "sidebar.results",
|
||||
results_for_label: "sidebar.resultsFor",
|
||||
no_results_label: "sidebar.noMediaFiles",
|
||||
search_placeholder: dgettext("ui", "Search media..."),
|
||||
toggle_filters_label: dgettext("ui", "Toggle Filters"),
|
||||
archive_label: dgettext("render", "Archive"),
|
||||
tags_label: dgettext("ui", "Tags"),
|
||||
clear_tags_label: dgettext("ui", "Clear tags"),
|
||||
clear_filters_label: dgettext("ui", "Clear filters"),
|
||||
results_label: dgettext("ui", "results"),
|
||||
results_for_label: dgettext("ui", "results for"),
|
||||
no_results_label: dgettext("ui", "No media files"),
|
||||
year_month_counts: year_month_counts(base_media, &Map.get(&1, :updated_at)),
|
||||
available_tags: available_tags,
|
||||
available_tag_colors: Map.take(tag_colors, available_tags),
|
||||
@@ -266,13 +356,13 @@ defmodule BDS.UI.Sidebar do
|
||||
|
||||
defp tags_nav_view(tags) do
|
||||
%{
|
||||
title: "Tags",
|
||||
subtitle: "Tag management",
|
||||
title: dgettext("ui", "Tags"),
|
||||
subtitle: dgettext("ui", "Tag management"),
|
||||
layout: "nav_list",
|
||||
items: [
|
||||
%{id: "tags-cloud", title: "Tag Cloud", icon: "☁️", route: "tags"},
|
||||
%{id: "tags-manage", title: "Create / Edit", icon: "✏️", route: "tags"},
|
||||
%{id: "tags-merge", title: "Merge Tags", icon: "🔀", route: "tags"}
|
||||
%{id: "tags-cloud", title: dgettext("ui", "Tag Cloud"), icon: "☁️", route: "tags"},
|
||||
%{id: "tags-manage", title: dgettext("ui", "Create / Edit"), icon: "✏️", route: "tags"},
|
||||
%{id: "tags-merge", title: dgettext("ui", "Merge Tags"), icon: "🔀", route: "tags"}
|
||||
],
|
||||
summary_badge: length(tags)
|
||||
}
|
||||
@@ -280,34 +370,44 @@ defmodule BDS.UI.Sidebar do
|
||||
|
||||
defp settings_nav_view do
|
||||
%{
|
||||
title: "Settings",
|
||||
subtitle: "Project and publishing",
|
||||
title: dgettext("ui", "Settings"),
|
||||
subtitle: dgettext("ui", "Project and publishing"),
|
||||
layout: "nav_list",
|
||||
items: [
|
||||
%{id: "settings-project", title: "Project", icon: "📁", route: "settings"},
|
||||
%{id: "settings-editor", title: "Editor", icon: "📝", route: "settings"},
|
||||
%{id: "settings-content", title: "Content", icon: "📋", route: "settings"},
|
||||
%{id: "settings-ai", title: "AI", icon: "🤖", route: "settings"},
|
||||
%{id: "settings-technology", title: "Technology", icon: "⚙️", route: "settings"},
|
||||
%{id: "settings-publishing", title: "Publishing", icon: "🚀", route: "settings"},
|
||||
%{id: "settings-data", title: "Data", icon: "🗄️", route: "settings"},
|
||||
%{id: "settings-mcp", title: "MCP", icon: "🔌", route: "settings"},
|
||||
%{id: "settings-style", title: "Style", icon: "🎨", route: "style"}
|
||||
%{id: "settings-project", title: dgettext("ui", "Project"), icon: "📁", route: "settings"},
|
||||
%{id: "settings-editor", title: dgettext("ui", "Editor"), icon: "📝", route: "settings"},
|
||||
%{id: "settings-content", title: dgettext("ui", "Content"), icon: "📋", route: "settings"},
|
||||
%{id: "settings-ai", title: dgettext("ui", "AI"), icon: "🤖", route: "settings"},
|
||||
%{
|
||||
id: "settings-technology",
|
||||
title: dgettext("ui", "Technology"),
|
||||
icon: "⚙️",
|
||||
route: "settings"
|
||||
},
|
||||
%{
|
||||
id: "settings-publishing",
|
||||
title: dgettext("ui", "Publishing"),
|
||||
icon: "🚀",
|
||||
route: "settings"
|
||||
},
|
||||
%{id: "settings-data", title: dgettext("ui", "Data"), icon: "🗄️", route: "settings"},
|
||||
%{id: "settings-mcp", title: dgettext("ui", "MCP"), icon: "🔌", route: "settings"},
|
||||
%{id: "settings-style", title: dgettext("ui", "Style"), icon: "🎨", route: "style"}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
defp git_view do
|
||||
%{
|
||||
title: "Git",
|
||||
subtitle: "Working tree and history",
|
||||
title: dgettext("ui", "Git"),
|
||||
subtitle: dgettext("ui", "Working tree and history"),
|
||||
layout: "entity_list",
|
||||
empty_message: "No items",
|
||||
empty_message: dgettext("ui", "No items"),
|
||||
items: [
|
||||
%{
|
||||
id: "git-working-tree",
|
||||
title: "Working tree",
|
||||
meta: "Working tree and history",
|
||||
title: dgettext("ui", "Working tree"),
|
||||
meta: dgettext("ui", "Working tree and history"),
|
||||
route: "git_diff",
|
||||
updated_at: nil
|
||||
}
|
||||
@@ -320,7 +420,7 @@ defmodule BDS.UI.Sidebar do
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
layout: "entity_list",
|
||||
empty_message: "No items",
|
||||
empty_message: dgettext("ui", "No items"),
|
||||
items:
|
||||
Enum.map(items, fn item ->
|
||||
%{
|
||||
@@ -350,7 +450,10 @@ defmodule BDS.UI.Sidebar do
|
||||
status: Atom.to_string(post.status),
|
||||
language_count: 1 + Map.get(translation_counts, post.id, 0),
|
||||
meta_timestamp:
|
||||
if(published_meta?, do: post.published_at || post.updated_at, else: post.updated_at),
|
||||
if(published_meta?,
|
||||
do: post.published_at || post.updated_at,
|
||||
else: post.updated_at
|
||||
),
|
||||
route: "post",
|
||||
search_blob: post_search_blob(post)
|
||||
}
|
||||
@@ -651,7 +754,7 @@ defmodule BDS.UI.Sidebar do
|
||||
cond do
|
||||
present?(post.title) -> post.title
|
||||
present?(post.slug) -> post.slug
|
||||
true -> "Untitled"
|
||||
true -> dgettext("ui", "Untitled")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
defmodule BDS.UI.Workbench do
|
||||
@moduledoc false
|
||||
|
||||
use Gettext, backend: BDS.Gettext
|
||||
|
||||
alias BDS.UI.Registry
|
||||
|
||||
@singleton_tabs MapSet.new([
|
||||
@@ -175,6 +177,9 @@ defmodule BDS.UI.Workbench do
|
||||
end
|
||||
|
||||
def status_bar(state, opts) do
|
||||
post_count = Keyword.get(opts, :post_count, 0)
|
||||
media_count = Keyword.get(opts, :media_count, 0)
|
||||
|
||||
%{
|
||||
left: %{
|
||||
running_task_message: Keyword.get(opts, :running_task_message),
|
||||
@@ -182,10 +187,10 @@ defmodule BDS.UI.Workbench do
|
||||
},
|
||||
right: %{
|
||||
post_status: post_status(state, Keyword.get(opts, :active_post_status)),
|
||||
post_count: "#{Keyword.get(opts, :post_count, 0)} posts",
|
||||
post_count_value: Keyword.get(opts, :post_count, 0),
|
||||
media_count: "#{Keyword.get(opts, :media_count, 0)} media",
|
||||
media_count_value: Keyword.get(opts, :media_count, 0),
|
||||
post_count: "#{post_count} #{dngettext("ui", "post", "posts", post_count)}",
|
||||
post_count_value: post_count,
|
||||
media_count: "#{media_count} #{dngettext("ui", "media", "media", media_count)}",
|
||||
media_count_value: media_count,
|
||||
token_usage: token_usage(state, Keyword.get(opts, :token_usage)),
|
||||
theme_badge: Keyword.get(opts, :theme_badge, "default"),
|
||||
offline_mode: Keyword.get(opts, :offline_mode, false),
|
||||
|
||||
Reference in New Issue
Block a user