feat: internationalization #43
@@ -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::<QueryValue, QueryErrors, QueryKey>();
 | 
			
		||||
    
 | 
			
		||||
    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",
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
                                        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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",
 | 
			
		||||
                                    {
 | 
			
		||||
                                        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
 | 
			
		||||
                                        .format_localized("Today, %A %-d. %B", Locale::en_US)
 | 
			
		||||
                                        .to_string()
 | 
			
		||||
                                            }
 | 
			
		||||
                                        )
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 
 | 
			
		||||
@@ -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<Duration>; 17] = [
 | 
			
		||||
    None,
 | 
			
		||||
@@ -79,6 +82,8 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
 | 
			
		||||
    let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
 | 
			
		||||
    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<Task>, 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<Task>, 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(
 | 
			
		||||
                                    {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())}
 | 
			
		||||
                                        ).unwrap_or_else(|| translate!(i18, "none"))}
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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."
 | 
			
		||||
                        {
 | 
			
		||||
                            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 {
 | 
			
		||||
                            " %m. %-d. %Y"
 | 
			
		||||
                        }).to_string()}
 | 
			
		||||
                                        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()))
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user
	
Improve error handling when unwrapping the
language_identifier.The
language_identifieris being force unwrapped usingunwrap()at line 15. This can lead to a panic if the value isNone.Consider using
matchorif letto handle theNonecase gracefully: