From f0f87cc58e8856978bf3080b7471215788e0fb10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Volf?= Date: Tue, 10 Sep 2024 16:15:47 +0200 Subject: [PATCH] feat: internationalize the app interface --- src/components/app.rs | 6 ++ .../pages/category_calendar_page.rs | 32 ++++++---- src/components/pages/category_today_page.rs | 36 ++++++++--- src/components/task_form.rs | 26 ++++---- src/components/task_list_item.rs | 59 ++++++++++++++++--- 5 files changed, 122 insertions(+), 37 deletions(-) diff --git a/src/components/app.rs b/src/components/app.rs index 708b0f5..7f74f23 100644 --- a/src/components/app.rs +++ b/src/components/app.rs @@ -4,11 +4,17 @@ use dioxus::dioxus_core::Element; use dioxus::prelude::*; use dioxus_query::prelude::{use_init_query_client}; use crate::query::{QueryErrors, QueryKey, QueryValue}; +use dioxus_sdk::i18n::{use_init_i18n}; +use crate::internationalization::get_languages; +use crate::server::internationalization::get_language_identifier; #[component] pub(crate) fn App() -> Element { use_init_query_client::(); + let language_identifier = use_server_future(get_language_identifier)?.unwrap().unwrap(); + use_init_i18n(language_identifier.clone(), language_identifier, get_languages); + rsx! { div { class: "min-h-screen text-zinc-200 bg-zinc-800 pt-4 pb-36", diff --git a/src/components/pages/category_calendar_page.rs b/src/components/pages/category_calendar_page.rs index 2a31b42..1747ea5 100644 --- a/src/components/pages/category_calendar_page.rs +++ b/src/components/pages/category_calendar_page.rs @@ -1,13 +1,16 @@ +use crate::components::task_list::TaskList; +use crate::internationalization::LocaleFromLanguageIdentifier; use crate::models::category::Category; -use chrono::{Datelike, Local, Locale}; +use crate::models::task::TaskWithSubtasks; +use crate::query::tasks::use_tasks_with_subtasks_in_category_query; +use crate::query::QueryValue; +use chrono::{Datelike, Local}; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; use dioxus_query::prelude::QueryResult; -use crate::components::task_list::TaskList; -use crate::query::QueryValue; -use crate::query::tasks::use_tasks_with_subtasks_in_category_query; -use crate::models::task::{TaskWithSubtasks}; +use dioxus_sdk::i18n::use_i18; +use dioxus_sdk::translate; const CALENDAR_LENGTH_DAYS: usize = 366 * 3; @@ -20,6 +23,8 @@ pub(crate) fn CategoryCalendarPage() -> Element { }); let tasks_query_result = tasks.result(); + let i18 = use_i18(); + rsx! { match tasks_query_result.value() { QueryResult::Ok(QueryValue::TasksWithSubtasks(tasks)) @@ -37,14 +42,17 @@ pub(crate) fn CategoryCalendarPage() -> Element { div { class: "pt-1", { - date_current - .format_localized( - format!( - "%A %-d. %B{}", - if date_current.year() != today_date.year() - {" %Y"} else {""} + date_current.format_localized(translate!( + i18, + if date_current.year() == Local::now().year() { + "formats.date_weekday_format" + } else { + "formats.date_weekday_year_format" + } ).as_str(), - Locale::en_US + LocaleFromLanguageIdentifier::from( + &(i18.selected_language)() + ).into() ) .to_string() } diff --git a/src/components/pages/category_today_page.rs b/src/components/pages/category_today_page.rs index 7122eb3..40d77f2 100644 --- a/src/components/pages/category_today_page.rs +++ b/src/components/pages/category_today_page.rs @@ -1,12 +1,16 @@ use crate::components::task_list::TaskList; +use crate::components::task_list_item::TaskListItem; +use crate::internationalization::LocaleFromLanguageIdentifier; use crate::models::category::Category; use crate::models::task::TaskWithSubtasks; -use crate::query::tasks::{use_tasks_with_subtasks_in_category_query}; +use crate::query::tasks::use_tasks_with_subtasks_in_category_query; use crate::query::QueryValue; -use chrono::{Local, Locale}; +use chrono::Local; use dioxus::prelude::*; use dioxus_query::prelude::QueryResult; -use crate::components::task_list_item::TaskListItem; +use dioxus_sdk::i18n::use_i18; +use dioxus_sdk::translate; +use voca_rs::Voca; #[component] pub(crate) fn CategoryTodayPage() -> Element { @@ -22,6 +26,8 @@ pub(crate) fn CategoryTodayPage() -> Element { let long_term_tasks_query = use_tasks_with_subtasks_in_category_query(Category::LongTerm); let long_term_tasks_query_result = long_term_tasks_query.result(); + let i18 = use_i18(); + rsx! { div { class: "pt-4 flex flex-col gap-8", @@ -40,7 +46,7 @@ pub(crate) fn CategoryTodayPage() -> Element { } div { class: "mt-1", - "Long-term" + {translate!(i18, "long_term")._upper_first()} } } div { @@ -103,7 +109,7 @@ pub(crate) fn CategoryTodayPage() -> Element { } div { class: "mt-1", - "Overdue" + {translate!(i18, "overdue")._upper_first()} } } TaskList { @@ -122,9 +128,23 @@ pub(crate) fn CategoryTodayPage() -> Element { div { class: "mt-1", { - today_date - .format_localized("Today, %A %-d. %B", Locale::en_US) - .to_string() + let format = translate!(i18, "formats.date_weekday_format"); + let today_date = today_date.format_localized( + format.as_str(), + LocaleFromLanguageIdentifier::from( + &(i18.selected_language)() + ).into() + ).to_string(); + format!( + "{} – {}", + translate!(i18, "today")._upper_first(), + if translate!(i18, "formats.weekday_lowercase_first") + .parse().unwrap() { + today_date._lower_first() + } else { + today_date + } + ) } } } diff --git a/src/components/task_form.rs b/src/components/task_form.rs index b6e6a42..1dd3696 100644 --- a/src/components/task_form.rs +++ b/src/components/task_form.rs @@ -1,5 +1,6 @@ use crate::components::category_input::CategoryInput; use crate::components::reoccurrence_input::ReoccurrenceIntervalInput; +use crate::components::subtasks_form::SubtasksForm; use crate::models::category::{CalendarTime, Category, Reoccurrence}; use crate::models::task::NewTask; use crate::models::task::Task; @@ -7,12 +8,14 @@ use crate::query::{QueryErrors, QueryKey, QueryValue}; use crate::route::Route; use crate::server::projects::get_projects; use crate::server::tasks::{create_task, delete_task, edit_task}; -use chrono::{Duration}; +use chrono::Duration; use dioxus::core_macro::{component, rsx}; use dioxus::dioxus_core::Element; use dioxus::prelude::*; use dioxus_query::prelude::use_query_client; -use crate::components::subtasks_form::SubtasksForm; +use dioxus_sdk::i18n::use_i18; +use dioxus_sdk::translate; +use voca_rs::Voca; const REMINDER_OFFSETS: [Option; 17] = [ None, @@ -79,6 +82,8 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() let query_client = use_query_client::(); let task_for_submit = task.clone(); + let i18 = use_i18(); + rsx! { div { class: "p-4 flex flex-col gap-4", @@ -172,7 +177,7 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() id: "input_project", option { value: 0, - "None" + {translate!(i18, "none")} }, for project in projects { option { @@ -330,13 +335,14 @@ pub(crate) fn TaskForm(task: Option, on_successful_submit: EventHandler<() label { r#for: "category_calendar_reminder_offset_index", class: "pr-3 min-w-16 text-right", - {REMINDER_OFFSETS[category_calendar_reminder_offset_index()].map( - |offset| if offset.num_hours() < 1 { - format!("{} min", offset.num_minutes()) - } else { - format!("{} h", offset.num_hours()) - } - ).unwrap_or_else(|| "none".to_string())} + {REMINDER_OFFSETS[category_calendar_reminder_offset_index()] + .map( + |offset| if offset.num_hours() < 1 { + format!("{} min", offset.num_minutes()) + } else { + format!("{} h", offset.num_hours()) + } + ).unwrap_or_else(|| translate!(i18, "none"))} } } } diff --git a/src/components/task_list_item.rs b/src/components/task_list_item.rs index c8835b9..ed67c82 100644 --- a/src/components/task_list_item.rs +++ b/src/components/task_list_item.rs @@ -1,12 +1,18 @@ -use chrono::{Datelike, Local}; +use crate::internationalization::LocaleFromLanguageIdentifier; use crate::models::category::Category; use crate::models::task::TaskWithSubtasks; +use chrono::{Datelike, Local}; use dioxus::core_macro::rsx; use dioxus::dioxus_core::Element; use dioxus::prelude::*; +use dioxus_sdk::i18n::use_i18; +use dioxus_sdk::translate; +use voca_rs::Voca; #[component] pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element { + let i18 = use_i18(); + rsx! { div { class: "flex flex-col", @@ -22,11 +28,47 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element { i { class: "fa-solid fa-bomb" }, - {deadline.format(if deadline.year() == Local::now().year() { - " %m. %-d." - } else { - " %m. %-d. %Y" - }).to_string()} + { + let today_date = Local::now().date_naive(); + format!( + " {}", + if deadline == today_date - chrono::Days::new(1) { + translate!(i18, "yesterday") + } else if deadline == today_date { + translate!(i18, "today") + } else if deadline == today_date + chrono::Days::new(1) { + translate!(i18, "tomorrow") + } else if deadline > today_date + && deadline <= today_date + chrono::Days::new(7) { + let deadline = deadline.format_localized( + "%A", + LocaleFromLanguageIdentifier::from( + &(i18.selected_language)() + ).into() + ).to_string(); + if translate!(i18, "formats.weekday_lowercase_first") + .parse().unwrap() { + deadline._lower_first() + } else { + deadline + } + } else { + let format = translate!(i18, + if deadline.year() == today_date.year() { + "formats.date_format" + } else { + "formats.date_year_format" + } + ); + deadline.format_localized( + format.as_str(), + LocaleFromLanguageIdentifier::from( + &(i18.selected_language)() + ).into() + ).to_string() + } + ) + } } } if let Category::Calendar { time, .. } = task.task().category() { @@ -36,7 +78,10 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element { i { class: "fa-solid fa-clock" }, - {calendar_time.time().format(" %k:%M").to_string()} + { + let format = translate!(i18, "formats.time_format"); + format!(" {}",calendar_time.time().format(format.as_str())) + } } } }