diff --git a/crates/bds-cli/src/lib.rs b/crates/bds-cli/src/lib.rs index 1047e66..245a94c 100644 --- a/crates/bds-cli/src/lib.rs +++ b/crates/bds-cli/src/lib.rs @@ -6,6 +6,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{Context, Result, anyhow, bail}; use bds_core::db::{Database, DbConnection}; use bds_core::engine::{self, cli_sync, domain_events}; +use bds_core::i18n::UiLocale; use bds_core::model::{DomainEntity, NotificationAction, Project, ScriptKind}; use bds_core::scripting::{CoreHost, ExecutionControl, ExecutionKind, execute_many_with_host}; use clap::{Args, Parser, Subcommand, ValueEnum}; @@ -359,7 +360,8 @@ fn rebuild(db: &Database, incremental: bool) -> Result { db.conn(), &data_dir, &project.id, - Some(Arc::new(move |value, message| { + Some(Arc::new(move |value, event| { + let message = event.localized(UiLocale::En); let line = format!("[{:>3}%] {message}", (value * 100.0).round() as i32); let mut progress = progress_sink .lock() diff --git a/crates/bds-core/src/engine/auto_translation.rs b/crates/bds-core/src/engine/auto_translation.rs index afc0d33..60a9b9a 100644 --- a/crates/bds-core/src/engine/auto_translation.rs +++ b/crates/bds-core/src/engine/auto_translation.rs @@ -13,6 +13,7 @@ use crate::engine::ai::{ TranslationResult, }; use crate::engine::{EngineError, EngineResult}; +use crate::i18n::{UiLocale, translate, translate_with}; use crate::model::{Media, Post, PostStatus}; use crate::util::frontmatter::read_post_file; @@ -26,6 +27,29 @@ pub struct FillMissingTranslationsReport { pub errors: Vec, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum FillMissingTranslationsProgress { + ScanningPublishedPosts, + TranslatingPost { title: String, language: String }, + Complete, +} + +impl FillMissingTranslationsProgress { + pub fn localized(&self, locale: UiLocale) -> String { + match self { + Self::ScanningPublishedPosts => { + translate(locale, "engine.progress.scanningPublishedPosts") + } + Self::TranslatingPost { title, language } => translate_with( + locale, + "engine.progress.translatingPost", + &[("title", title), ("language", language)], + ), + Self::Complete => translate(locale, "engine.progress.translationBatchComplete"), + } + } +} + pub fn configured_languages(main_language: &str, blog_languages: &[String]) -> Vec { let mut seen = HashSet::new(); std::iter::once(main_language.to_string()) @@ -64,7 +88,7 @@ pub fn fill_missing_translations( main_language: &str, blog_languages: &[String], offline_mode: bool, - mut on_progress: impl FnMut(f32, &str) -> bool, + mut on_progress: impl FnMut(f32, &FillMissingTranslationsProgress) -> bool, ) -> EngineResult { fill_missing_translations_with( conn, @@ -90,7 +114,7 @@ fn fill_missing_translations_with( blog_languages: &[String], post_translator: &mut dyn FnMut(&Post, &str) -> EngineResult, media_translator: &mut dyn FnMut(&Media, &str) -> EngineResult, - on_progress: &mut dyn FnMut(f32, &str) -> bool, + on_progress: &mut dyn FnMut(f32, &FillMissingTranslationsProgress) -> bool, ) -> EngineResult { let configured = configured_languages(main_language, blog_languages); if configured.len() <= 1 { @@ -100,7 +124,10 @@ fn fill_missing_translations_with( }); } let posts = qp::list_posts_by_project(conn, project_id)?; - if !on_progress(0.0, "Scanning published posts") { + if !on_progress( + 0.0, + &FillMissingTranslationsProgress::ScanningPublishedPosts, + ) { return Err(EngineError::Validation("cancelled".to_string())); } let mut work = Vec::new(); @@ -108,7 +135,10 @@ fn fill_missing_translations_with( .into_iter() .filter(|post| post.status == PostStatus::Published && !post.do_not_translate) { - if !on_progress(0.0, "Scanning published posts") { + if !on_progress( + 0.0, + &FillMissingTranslationsProgress::ScanningPublishedPosts, + ) { return Err(EngineError::Validation("cancelled".to_string())); } for language in missing_languages(conn, &post, &configured)? { @@ -126,7 +156,10 @@ fn fill_missing_translations_with( for (index, (post, language)) in work.iter().enumerate() { if !on_progress( 0.15 + (index as f32 / work.len() as f32) * 0.85, - &format!("{} → {language}", post.title), + &FillMissingTranslationsProgress::TranslatingPost { + title: post.title.clone(), + language: language.clone(), + }, ) { return Err(EngineError::Validation("cancelled".to_string())); } @@ -151,7 +184,7 @@ fn fill_missing_translations_with( } } } - if !on_progress(1.0, "Translation batch complete") { + if !on_progress(1.0, &FillMissingTranslationsProgress::Complete) { return Err(EngineError::Validation("cancelled".to_string())); } Ok(report) @@ -399,6 +432,26 @@ mod tests { use crate::engine::post::{create_post, publish_post, upsert_translation}; use tempfile::TempDir; + #[test] + fn progress_events_use_the_selected_ui_locale() { + assert_eq!( + FillMissingTranslationsProgress::ScanningPublishedPosts.localized(UiLocale::De), + "Veröffentlichte Beiträge werden durchsucht…" + ); + assert_eq!( + FillMissingTranslationsProgress::TranslatingPost { + title: "Hallo".into(), + language: "fr".into(), + } + .localized(UiLocale::Fr), + "Traduction de Hallo → fr" + ); + assert_eq!( + FillMissingTranslationsProgress::Complete.localized(UiLocale::Es), + "Lote de traducciones completado" + ); + } + #[test] fn batch_translates_only_missing_languages_and_publishes() { let db = Database::open_in_memory().unwrap(); diff --git a/crates/bds-core/src/engine/rebuild.rs b/crates/bds-core/src/engine/rebuild.rs index 61fbadb..cda69be 100644 --- a/crates/bds-core/src/engine/rebuild.rs +++ b/crates/bds-core/src/engine/rebuild.rs @@ -10,6 +10,7 @@ use crate::engine::media; use crate::engine::post; use crate::engine::script_rebuild; use crate::engine::template_rebuild; +use crate::i18n::{UiLocale, translate, translate_with}; /// Report from a full rebuild operation. #[derive(Debug, Default)] @@ -74,8 +75,69 @@ pub fn rebuild_incremental( Ok(result) } -/// Progress callback: (percent 0.0..1.0, phase description). -pub type ProgressFn = Arc; +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RebuildProgress { + LoadingProjectMetadata, + ScanningPosts, + PostItem { + current: usize, + total: usize, + name: String, + }, + ScanningMedia, + MediaItem { + current: usize, + total: usize, + name: String, + }, + RebuildingTemplates, + RebuildingScripts, + ImportingTags, + RefreshingSemanticIndex, + Complete, +} + +impl RebuildProgress { + pub fn localized(&self, locale: UiLocale) -> String { + match self { + Self::LoadingProjectMetadata => { + translate(locale, "engine.progress.loadingProjectMetadata") + } + Self::ScanningPosts => translate(locale, "engine.progress.scanningPosts"), + Self::PostItem { + current, + total, + name, + } => localize_item(locale, "engine.progress.postItem", *current, *total, name), + Self::ScanningMedia => translate(locale, "engine.progress.scanningMedia"), + Self::MediaItem { + current, + total, + name, + } => localize_item(locale, "engine.progress.mediaItem", *current, *total, name), + Self::RebuildingTemplates => translate(locale, "engine.progress.rebuildingTemplates"), + Self::RebuildingScripts => translate(locale, "engine.progress.rebuildingScripts"), + Self::ImportingTags => translate(locale, "engine.progress.importingTags"), + Self::RefreshingSemanticIndex => { + translate(locale, "engine.progress.refreshingSemanticIndex") + } + Self::Complete => translate(locale, "engine.progress.rebuildComplete"), + } + } +} + +fn localize_item(locale: UiLocale, key: &str, current: usize, total: usize, name: &str) -> String { + let current = current.to_string(); + let total = total.to_string(); + translate_with( + locale, + key, + &[("current", ¤t), ("total", &total), ("name", name)], + ) +} + +/// Progress callback: (percent 0.0..1.0, semantic progress event). +pub type ProgressFn = Arc; /// Orchestrate a full rebuild from filesystem into the database. /// @@ -116,23 +178,23 @@ fn rebuild_from_filesystem_inner( on_progress: Option, ) -> EngineResult { let mut report = FullRebuildReport::default(); - let progress = |pct: f32, msg: &str| { + let progress = |pct: f32, event: RebuildProgress| { if let Some(ref f) = on_progress { - f(pct, msg); + f(pct, &event); } }; // Phase weights: posts 0.0..0.35, media 0.35..0.70, templates 0.70..0.85, scripts 0.85..1.0 // 1. Load portable project metadata and clear all reconstructible rows. - progress(0.0, "Loading project metadata..."); + progress(0.0, RebuildProgress::LoadingProjectMetadata); fts::ensure_fts_tables(conn)?; crate::engine::meta::startup_sync(data_dir)?; crate::engine::meta::sync_metadata_from_filesystem(conn, data_dir, project_id)?; clear_project_rows(conn, project_id)?; // 2. Rebuild posts (0.00 .. 0.35) - progress(0.01, "Scanning posts..."); + progress(0.01, RebuildProgress::ScanningPosts); let post_item_cb: Option = on_progress.as_ref().map(|cb| { let cb = Arc::clone(cb); let f: post::ItemProgressFn = Box::new(move |current, total, name| { @@ -142,8 +204,14 @@ fn rebuild_from_filesystem_inner( 1.0 }; let global_pct = 0.01 + phase_pct * 0.34; - let msg = format!("Posts: {current}/{total} \u{2014} {name}"); - cb(global_pct, &msg); + cb( + global_pct, + &RebuildProgress::PostItem { + current, + total, + name: name.to_string(), + }, + ); }); f }); @@ -160,7 +228,7 @@ fn rebuild_from_filesystem_inner( report.errors.extend(post_report.errors); // 3. Rebuild media (0.35 .. 0.70) - progress(0.35, "Scanning media..."); + progress(0.35, RebuildProgress::ScanningMedia); let media_item_cb: Option = on_progress.as_ref().map(|cb| { let cb = Arc::clone(cb); let f: media::ItemProgressFn = Box::new(move |current, total, name| { @@ -170,8 +238,14 @@ fn rebuild_from_filesystem_inner( 1.0 }; let global_pct = 0.35 + phase_pct * 0.35; - let msg = format!("Media: {current}/{total} \u{2014} {name}"); - cb(global_pct, &msg); + cb( + global_pct, + &RebuildProgress::MediaItem { + current, + total, + name: name.to_string(), + }, + ); }); f }); @@ -188,7 +262,7 @@ fn rebuild_from_filesystem_inner( report.errors.extend(media_report.errors); // 4. Rebuild templates (0.70 .. 0.85) - progress(0.70, "Rebuilding templates..."); + progress(0.70, RebuildProgress::RebuildingTemplates); let tpl_report = template_rebuild::rebuild_templates_from_filesystem(conn, data_dir, project_id)?; report.templates_created = tpl_report.created; @@ -196,7 +270,7 @@ fn rebuild_from_filesystem_inner( report.errors.extend(tpl_report.errors); // 5. Rebuild scripts (0.85 .. 0.95) - progress(0.85, "Rebuilding scripts..."); + progress(0.85, RebuildProgress::RebuildingScripts); let script_report = script_rebuild::rebuild_scripts_from_filesystem(conn, data_dir, project_id)?; report.scripts_created = script_report.created; @@ -204,7 +278,7 @@ fn rebuild_from_filesystem_inner( report.errors.extend(script_report.errors); // 6. Restore relationships and tags (0.95 .. 1.0) - progress(0.95, "Importing tags..."); + progress(0.95, RebuildProgress::ImportingTags); super::tag::import_tags_from_file(conn, data_dir, project_id)?; super::tag::sync_tags_from_posts(conn, project_id)?; post::rebuild_all_links(conn, data_dir, project_id)?; @@ -216,11 +290,11 @@ fn rebuild_from_filesystem_inner( ))); } - progress(0.98, "Refreshing semantic index..."); + progress(0.98, RebuildProgress::RefreshingSemanticIndex); crate::engine::embedding::EmbeddingService::production(conn, data_dir) .index_unindexed(project_id)?; - progress(1.0, "Rebuild complete"); + progress(1.0, RebuildProgress::Complete); Ok(report) } @@ -303,6 +377,27 @@ mod tests { use std::fs; use tempfile::TempDir; + #[test] + fn progress_events_use_the_selected_ui_locale() { + assert_eq!( + RebuildProgress::LoadingProjectMetadata.localized(UiLocale::It), + "Caricamento dei metadati del progetto…" + ); + assert_eq!( + RebuildProgress::PostItem { + current: 2, + total: 4, + name: "Bonjour".into(), + } + .localized(UiLocale::Fr), + "Articles : 2/4 — Bonjour" + ); + assert_eq!( + RebuildProgress::Complete.localized(UiLocale::De), + "Neuaufbau abgeschlossen" + ); + } + fn setup() -> (Database, TempDir) { let db = Database::open_in_memory().unwrap(); db.migrate().unwrap(); diff --git a/crates/bds-core/src/i18n/mod.rs b/crates/bds-core/src/i18n/mod.rs index 7714b4e..0e4275e 100644 --- a/crates/bds-core/src/i18n/mod.rs +++ b/crates/bds-core/src/i18n/mod.rs @@ -234,6 +234,47 @@ static RENDER_MAPS: LazyLock<[HashMap; 5]> = LazyLock::new(|| { mod tests { use super::*; + #[test] + fn engine_progress_keys_exist_in_every_ui_catalog() { + let keys = [ + "engine-progress-scanningPublishedPosts", + "engine-progress-translatingPost", + "engine-progress-translationBatchComplete", + "engine-progress-loadingProjectMetadata", + "engine-progress-scanningPosts", + "engine-progress-postItem", + "engine-progress-scanningMedia", + "engine-progress-mediaItem", + "engine-progress-rebuildingTemplates", + "engine-progress-rebuildingScripts", + "engine-progress-importingTags", + "engine-progress-refreshingSemanticIndex", + "engine-progress-rebuildComplete", + ]; + let catalogs = [ + ("en", UI_EN), + ("de", include_str!("../../../../locales/ui/de.ftl")), + ("fr", include_str!("../../../../locales/ui/fr.ftl")), + ("it", include_str!("../../../../locales/ui/it.ftl")), + ("es", include_str!("../../../../locales/ui/es.ftl")), + ]; + + for (language, source) in catalogs { + let present = resource(source) + .entries() + .filter_map(|entry| match entry { + fluent_syntax::ast::Entry::Message(message) => { + Some(message.id.name.to_string()) + } + _ => None, + }) + .collect::>(); + for key in keys { + assert!(present.contains(key), "{language} is missing {key}"); + } + } + } + // LanguageNormalization invariant: base-extract and fallback #[test] fn normalize_strips_region() { diff --git a/crates/bds-core/src/render/macros.rs b/crates/bds-core/src/render/macros.rs index aaf8748..855e608 100644 --- a/crates/bds-core/src/render/macros.rs +++ b/crates/bds-core/src/render/macros.rs @@ -849,14 +849,27 @@ mod tests { #[test] fn video_macros_with_missing_ids_still_use_the_localized_templates() { - let rendered = - expand_builtin_macros("[[youtube]] [[vimeo]]", &MacroRenderContext::default()); + for (language, youtube_title, vimeo_title) in [ + ("en", "YouTube video", "Vimeo video"), + ("de", "YouTube-Video", "Vimeo-Video"), + ("fr", "Vidéo YouTube", "Vidéo Vimeo"), + ("it", "Video YouTube", "Video Vimeo"), + ("es", "Vídeo de YouTube", "Vídeo de Vimeo"), + ] { + let mut roots = serde_json::Map::new(); + roots.insert("language".into(), serde_json::json!(language)); + let context = MacroRenderContext { + roots, + ..MacroRenderContext::default() + }; + let rendered = expand_builtin_macros("[[youtube]] [[vimeo]]", &context); - assert!(rendered.contains("https://www.youtube.com/embed/?rel=0")); - assert!(rendered.contains("https://player.vimeo.com/video/")); - assert!(rendered.contains("title=\"YouTube video\"")); - assert!(rendered.contains("title=\"Vimeo video\"")); - assert!(!rendered.contains("Missing")); + assert!(rendered.contains("https://www.youtube.com/embed/?rel=0")); + assert!(rendered.contains("https://player.vimeo.com/video/")); + assert!(rendered.contains(&format!("title=\"{youtube_title}\""))); + assert!(rendered.contains(&format!("title=\"{vimeo_title}\""))); + assert!(!rendered.contains("Missing")); + } } #[test] diff --git a/crates/bds-ui/src/app.rs b/crates/bds-ui/src/app.rs index d7c182b..abfb664 100644 --- a/crates/bds-ui/src/app.rs +++ b/crates/bds-ui/src/app.rs @@ -4340,11 +4340,11 @@ impl BdsApp { main_language, &meta.blog_languages, offline_mode, - move |progress, _message| { + move |progress, event| { task_manager_for_progress.report_progress( task_id, Some(progress), - Some(t(locale, "engine.translatingContent")), + Some(event.localized(locale)), ); !task_manager_for_progress.is_cancelled(task_id) }, diff --git a/crates/bds-ui/src/app/engine_handlers.rs b/crates/bds-ui/src/app/engine_handlers.rs index 6ca47a3..f0948a3 100644 --- a/crates/bds-ui/src/app/engine_handlers.rs +++ b/crates/bds-ui/src/app/engine_handlers.rs @@ -3,29 +3,33 @@ use super::*; impl BdsApp { pub(super) fn handle_engine_message(&mut self, message: Message) -> Task { match message { - Message::RebuildDatabase => self.spawn_engine_task( - "engine.rebuildStarted", - |db_path, project_id, data_dir, tm, tid| { - let db = Database::open(&db_path).map_err(|e| e.to_string())?; - let on_progress: engine::rebuild::ProgressFn = Arc::new(move |pct, msg| { - tm.report_progress(tid, Some(pct), Some(msg.to_string())); - }); - let report = engine::rebuild::rebuild_from_filesystem_with_progress( - db.conn(), - &data_dir, - &project_id, - Some(on_progress), - ) - .map_err(|e| e.to_string())?; - let posts = report.posts_created + report.posts_updated; - let media = report.media_created + report.media_updated; - let templates = report.templates_created + report.templates_updated; - let scripts = report.scripts_created + report.scripts_updated; - Ok(format!( - "posts={posts}, media={media}, templates={templates}, scripts={scripts}" - )) - }, - ), + Message::RebuildDatabase => { + let locale = self.ui_locale; + self.spawn_engine_task( + "engine.rebuildStarted", + move |db_path, project_id, data_dir, tm, tid| { + let db = Database::open(&db_path).map_err(|e| e.to_string())?; + let on_progress: engine::rebuild::ProgressFn = + Arc::new(move |pct, event| { + tm.report_progress(tid, Some(pct), Some(event.localized(locale))); + }); + let report = engine::rebuild::rebuild_from_filesystem_with_progress( + db.conn(), + &data_dir, + &project_id, + Some(on_progress), + ) + .map_err(|e| e.to_string())?; + let posts = report.posts_created + report.posts_updated; + let media = report.media_created + report.media_updated; + let templates = report.templates_created + report.templates_updated; + let scripts = report.scripts_created + report.scripts_updated; + Ok(format!( + "posts={posts}, media={media}, templates={templates}, scripts={scripts}" + )) + }, + ) + } Message::ReindexText => { if !self.search_index_rebuild_running { self.active_modal = Some(modal::ModalState::SearchIndexRepair); diff --git a/locales/ui/de.ftl b/locales/ui/de.ftl index e030cbf..012955f 100644 --- a/locales/ui/de.ftl +++ b/locales/ui/de.ftl @@ -639,10 +639,22 @@ metadataDiff-field = { $field }: DB = { $database } · Datei = { $file } metadataDiff-entity = { $entity } · { $path } metadataDiff-orphan = { $reason } · { $path } engine-fillMissingTranslationsStarted = Fehlende Übersetzungen werden ergänzt… -engine-translatingContent = Inhalte werden übersetzt… engine-fillMissingTranslationsComplete = Übersetzung abgeschlossen: { $posts } Beiträge, { $media } Medien, { $failed } fehlgeschlagen engine-autoTranslationStarted = Fehlende Übersetzungen werden erstellt… engine-autoTranslationComplete = { $count } Übersetzungsentwürfe erstellt +engine-progress-scanningPublishedPosts = Veröffentlichte Beiträge werden durchsucht… +engine-progress-translatingPost = { $title } wird übersetzt → { $language } +engine-progress-translationBatchComplete = Übersetzungsstapel abgeschlossen +engine-progress-loadingProjectMetadata = Projektmetadaten werden geladen… +engine-progress-scanningPosts = Beiträge werden durchsucht… +engine-progress-postItem = Beiträge: { $current }/{ $total } — { $name } +engine-progress-scanningMedia = Medien werden durchsucht… +engine-progress-mediaItem = Medien: { $current }/{ $total } — { $name } +engine-progress-rebuildingTemplates = Vorlagen werden neu aufgebaut… +engine-progress-rebuildingScripts = Skripte werden neu aufgebaut… +engine-progress-importingTags = Schlagwörter werden importiert… +engine-progress-refreshingSemanticIndex = Semantischer Index wird aktualisiert… +engine-progress-rebuildComplete = Neuaufbau abgeschlossen translationValidation-run = Prüfen translationValidation-running = Übersetzungen werden geprüft… translationValidation-idle = Prüfung starten, um Übersetzungen in Datenbank und Dateisystem zu kontrollieren. diff --git a/locales/ui/en.ftl b/locales/ui/en.ftl index d3d167b..ac22b68 100644 --- a/locales/ui/en.ftl +++ b/locales/ui/en.ftl @@ -639,10 +639,22 @@ metadataDiff-field = { $field }: DB = { $database } · File = { $file } metadataDiff-entity = { $entity } · { $path } metadataDiff-orphan = { $reason } · { $path } engine-fillMissingTranslationsStarted = Filling missing translations… -engine-translatingContent = Translating content… engine-fillMissingTranslationsComplete = Translation batch complete: { $posts } posts, { $media } media, { $failed } failed engine-autoTranslationStarted = Creating missing translations… engine-autoTranslationComplete = Created { $count } draft translations +engine-progress-scanningPublishedPosts = Scanning published posts… +engine-progress-translatingPost = Translating { $title } → { $language } +engine-progress-translationBatchComplete = Translation batch complete +engine-progress-loadingProjectMetadata = Loading project metadata… +engine-progress-scanningPosts = Scanning posts… +engine-progress-postItem = Posts: { $current }/{ $total } — { $name } +engine-progress-scanningMedia = Scanning media… +engine-progress-mediaItem = Media: { $current }/{ $total } — { $name } +engine-progress-rebuildingTemplates = Rebuilding templates… +engine-progress-rebuildingScripts = Rebuilding scripts… +engine-progress-importingTags = Importing tags… +engine-progress-refreshingSemanticIndex = Refreshing semantic index… +engine-progress-rebuildComplete = Rebuild complete translationValidation-run = Validate translationValidation-running = Validating translations… translationValidation-idle = Run validation to check translations in the database and filesystem. diff --git a/locales/ui/es.ftl b/locales/ui/es.ftl index db568b5..e7f28f6 100644 --- a/locales/ui/es.ftl +++ b/locales/ui/es.ftl @@ -639,10 +639,22 @@ metadataDiff-field = { $field }: BD = { $database } · Archivo = { $file } metadataDiff-entity = { $entity } · { $path } metadataDiff-orphan = { $reason } · { $path } engine-fillMissingTranslationsStarted = Creando las traducciones que faltan… -engine-translatingContent = Traduciendo el contenido… engine-fillMissingTranslationsComplete = Traducción completada: { $posts } entradas, { $media } medios, { $failed } fallos engine-autoTranslationStarted = Creando las traducciones que faltan… engine-autoTranslationComplete = Se crearon { $count } borradores de traducción +engine-progress-scanningPublishedPosts = Analizando las entradas publicadas… +engine-progress-translatingPost = Traduciendo { $title } → { $language } +engine-progress-translationBatchComplete = Lote de traducciones completado +engine-progress-loadingProjectMetadata = Cargando los metadatos del proyecto… +engine-progress-scanningPosts = Analizando las entradas… +engine-progress-postItem = Entradas: { $current }/{ $total } — { $name } +engine-progress-scanningMedia = Analizando los medios… +engine-progress-mediaItem = Medios: { $current }/{ $total } — { $name } +engine-progress-rebuildingTemplates = Reconstruyendo las plantillas… +engine-progress-rebuildingScripts = Reconstruyendo los scripts… +engine-progress-importingTags = Importando las etiquetas… +engine-progress-refreshingSemanticIndex = Actualizando el índice semántico… +engine-progress-rebuildComplete = Reconstrucción completada translationValidation-run = Validar translationValidation-running = Validando traducciones… translationValidation-idle = Ejecuta la validación de las traducciones de la base de datos y el sistema de archivos. diff --git a/locales/ui/fr.ftl b/locales/ui/fr.ftl index e35087c..307da79 100644 --- a/locales/ui/fr.ftl +++ b/locales/ui/fr.ftl @@ -639,10 +639,22 @@ metadataDiff-field = { $field } : base = { $database } · Fichier = { $file } metadataDiff-entity = { $entity } · { $path } metadataDiff-orphan = { $reason } · { $path } engine-fillMissingTranslationsStarted = Création des traductions manquantes… -engine-translatingContent = Traduction du contenu… engine-fillMissingTranslationsComplete = Traduction terminée : { $posts } articles, { $media } médias, { $failed } échecs engine-autoTranslationStarted = Création des traductions manquantes… engine-autoTranslationComplete = { $count } brouillons de traduction créés +engine-progress-scanningPublishedPosts = Analyse des articles publiés… +engine-progress-translatingPost = Traduction de { $title } → { $language } +engine-progress-translationBatchComplete = Lot de traductions terminé +engine-progress-loadingProjectMetadata = Chargement des métadonnées du projet… +engine-progress-scanningPosts = Analyse des articles… +engine-progress-postItem = Articles : { $current }/{ $total } — { $name } +engine-progress-scanningMedia = Analyse des médias… +engine-progress-mediaItem = Médias : { $current }/{ $total } — { $name } +engine-progress-rebuildingTemplates = Reconstruction des modèles… +engine-progress-rebuildingScripts = Reconstruction des scripts… +engine-progress-importingTags = Importation des étiquettes… +engine-progress-refreshingSemanticIndex = Actualisation de l’index sémantique… +engine-progress-rebuildComplete = Reconstruction terminée translationValidation-run = Valider translationValidation-running = Validation des traductions… translationValidation-idle = Lancez la validation des traductions de la base et du système de fichiers. diff --git a/locales/ui/it.ftl b/locales/ui/it.ftl index fc07b59..02a1e6d 100644 --- a/locales/ui/it.ftl +++ b/locales/ui/it.ftl @@ -639,10 +639,22 @@ metadataDiff-field = { $field }: DB = { $database } · File = { $file } metadataDiff-entity = { $entity } · { $path } metadataDiff-orphan = { $reason } · { $path } engine-fillMissingTranslationsStarted = Creazione delle traduzioni mancanti… -engine-translatingContent = Traduzione dei contenuti… engine-fillMissingTranslationsComplete = Traduzione completata: { $posts } articoli, { $media } media, { $failed } errori engine-autoTranslationStarted = Creazione delle traduzioni mancanti… engine-autoTranslationComplete = Create { $count } bozze di traduzione +engine-progress-scanningPublishedPosts = Scansione dei post pubblicati… +engine-progress-translatingPost = Traduzione di { $title } → { $language } +engine-progress-translationBatchComplete = Lotto di traduzioni completato +engine-progress-loadingProjectMetadata = Caricamento dei metadati del progetto… +engine-progress-scanningPosts = Scansione dei post… +engine-progress-postItem = Post: { $current }/{ $total } — { $name } +engine-progress-scanningMedia = Scansione dei media… +engine-progress-mediaItem = Media: { $current }/{ $total } — { $name } +engine-progress-rebuildingTemplates = Ricostruzione dei modelli… +engine-progress-rebuildingScripts = Ricostruzione degli script… +engine-progress-importingTags = Importazione dei tag… +engine-progress-refreshingSemanticIndex = Aggiornamento dell’indice semantico… +engine-progress-rebuildComplete = Ricostruzione completata translationValidation-run = Verifica translationValidation-running = Verifica delle traduzioni… translationValidation-idle = Avvia la verifica delle traduzioni nel database e nel filesystem.