feat: internationalization #43
@ -4,11 +4,17 @@ use dioxus::dioxus_core::Element;
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_query::prelude::{use_init_query_client};
|
use dioxus_query::prelude::{use_init_query_client};
|
||||||
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
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]
|
#[component]
|
||||||
pub(crate) fn App() -> Element {
|
pub(crate) fn App() -> Element {
|
||||||
use_init_query_client::<QueryValue, QueryErrors, QueryKey>();
|
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! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "min-h-screen text-zinc-200 bg-zinc-800 pt-4 pb-36",
|
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 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::core_macro::rsx;
|
||||||
use dioxus::dioxus_core::Element;
|
use dioxus::dioxus_core::Element;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_query::prelude::QueryResult;
|
use dioxus_query::prelude::QueryResult;
|
||||||
use crate::components::task_list::TaskList;
|
use dioxus_sdk::i18n::use_i18;
|
||||||
use crate::query::QueryValue;
|
use dioxus_sdk::translate;
|
||||||
use crate::query::tasks::use_tasks_with_subtasks_in_category_query;
|
|
||||||
use crate::models::task::{TaskWithSubtasks};
|
|
||||||
|
|
||||||
const CALENDAR_LENGTH_DAYS: usize = 366 * 3;
|
const CALENDAR_LENGTH_DAYS: usize = 366 * 3;
|
||||||
|
|
||||||
@ -20,6 +23,8 @@ pub(crate) fn CategoryCalendarPage() -> Element {
|
|||||||
});
|
});
|
||||||
let tasks_query_result = tasks.result();
|
let tasks_query_result = tasks.result();
|
||||||
|
|
||||||
|
let i18 = use_i18();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
match tasks_query_result.value() {
|
match tasks_query_result.value() {
|
||||||
QueryResult::Ok(QueryValue::TasksWithSubtasks(tasks))
|
QueryResult::Ok(QueryValue::TasksWithSubtasks(tasks))
|
||||||
@ -37,14 +42,17 @@ pub(crate) fn CategoryCalendarPage() -> Element {
|
|||||||
div {
|
div {
|
||||||
class: "pt-1",
|
class: "pt-1",
|
||||||
{
|
{
|
||||||
date_current
|
date_current.format_localized(translate!(
|
||||||
.format_localized(
|
i18,
|
||||||
format!(
|
if date_current.year() == Local::now().year() {
|
||||||
"%A %-d. %B{}",
|
"formats.date_weekday_format"
|
||||||
if date_current.year() != today_date.year()
|
} else {
|
||||||
{" %Y"} else {""}
|
"formats.date_weekday_year_format"
|
||||||
|
}
|
||||||
).as_str(),
|
).as_str(),
|
||||||
Locale::en_US
|
LocaleFromLanguageIdentifier::from(
|
||||||
|
&(i18.selected_language)()
|
||||||
|
).into()
|
||||||
)
|
)
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
use crate::components::task_list::TaskList;
|
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::category::Category;
|
||||||
use crate::models::task::TaskWithSubtasks;
|
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 crate::query::QueryValue;
|
||||||
use chrono::{Local, Locale};
|
use chrono::Local;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_query::prelude::QueryResult;
|
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]
|
#[component]
|
||||||
pub(crate) fn CategoryTodayPage() -> Element {
|
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 = use_tasks_with_subtasks_in_category_query(Category::LongTerm);
|
||||||
let long_term_tasks_query_result = long_term_tasks_query.result();
|
let long_term_tasks_query_result = long_term_tasks_query.result();
|
||||||
|
|
||||||
|
let i18 = use_i18();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "pt-4 flex flex-col gap-8",
|
class: "pt-4 flex flex-col gap-8",
|
||||||
@ -40,7 +46,7 @@ pub(crate) fn CategoryTodayPage() -> Element {
|
|||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
class: "mt-1",
|
class: "mt-1",
|
||||||
"Long-term"
|
{translate!(i18, "long_term")._upper_first()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
@ -103,7 +109,7 @@ pub(crate) fn CategoryTodayPage() -> Element {
|
|||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
class: "mt-1",
|
class: "mt-1",
|
||||||
"Overdue"
|
{translate!(i18, "overdue")._upper_first()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TaskList {
|
TaskList {
|
||||||
@ -122,9 +128,23 @@ pub(crate) fn CategoryTodayPage() -> Element {
|
|||||||
div {
|
div {
|
||||||
class: "mt-1",
|
class: "mt-1",
|
||||||
{
|
{
|
||||||
today_date
|
let format = translate!(i18, "formats.date_weekday_format");
|
||||||
.format_localized("Today, %A %-d. %B", Locale::en_US)
|
let today_date = today_date.format_localized(
|
||||||
.to_string()
|
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
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::components::category_input::CategoryInput;
|
use crate::components::category_input::CategoryInput;
|
||||||
use crate::components::reoccurrence_input::ReoccurrenceIntervalInput;
|
use crate::components::reoccurrence_input::ReoccurrenceIntervalInput;
|
||||||
|
use crate::components::subtasks_form::SubtasksForm;
|
||||||
use crate::models::category::{CalendarTime, Category, Reoccurrence};
|
use crate::models::category::{CalendarTime, Category, Reoccurrence};
|
||||||
use crate::models::task::NewTask;
|
use crate::models::task::NewTask;
|
||||||
use crate::models::task::Task;
|
use crate::models::task::Task;
|
||||||
@ -7,12 +8,14 @@ use crate::query::{QueryErrors, QueryKey, QueryValue};
|
|||||||
use crate::route::Route;
|
use crate::route::Route;
|
||||||
use crate::server::projects::get_projects;
|
use crate::server::projects::get_projects;
|
||||||
use crate::server::tasks::{create_task, delete_task, edit_task};
|
use crate::server::tasks::{create_task, delete_task, edit_task};
|
||||||
use chrono::{Duration};
|
use chrono::Duration;
|
||||||
use dioxus::core_macro::{component, rsx};
|
use dioxus::core_macro::{component, rsx};
|
||||||
use dioxus::dioxus_core::Element;
|
use dioxus::dioxus_core::Element;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_query::prelude::use_query_client;
|
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] = [
|
const REMINDER_OFFSETS: [Option<Duration>; 17] = [
|
||||||
None,
|
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 query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
|
||||||
let task_for_submit = task.clone();
|
let task_for_submit = task.clone();
|
||||||
|
|
||||||
|
let i18 = use_i18();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "p-4 flex flex-col gap-4",
|
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",
|
id: "input_project",
|
||||||
option {
|
option {
|
||||||
value: 0,
|
value: 0,
|
||||||
"None"
|
{translate!(i18, "none")}
|
||||||
},
|
},
|
||||||
for project in projects {
|
for project in projects {
|
||||||
option {
|
option {
|
||||||
@ -330,13 +335,14 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
|||||||
label {
|
label {
|
||||||
r#for: "category_calendar_reminder_offset_index",
|
r#for: "category_calendar_reminder_offset_index",
|
||||||
class: "pr-3 min-w-16 text-right",
|
class: "pr-3 min-w-16 text-right",
|
||||||
{REMINDER_OFFSETS[category_calendar_reminder_offset_index()].map(
|
{REMINDER_OFFSETS[category_calendar_reminder_offset_index()]
|
||||||
|offset| if offset.num_hours() < 1 {
|
.map(
|
||||||
format!("{} min", offset.num_minutes())
|
|offset| if offset.num_hours() < 1 {
|
||||||
} else {
|
format!("{} min", offset.num_minutes())
|
||||||
format!("{} h", offset.num_hours())
|
} 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::category::Category;
|
||||||
use crate::models::task::TaskWithSubtasks;
|
use crate::models::task::TaskWithSubtasks;
|
||||||
|
use chrono::{Datelike, Local};
|
||||||
use dioxus::core_macro::rsx;
|
use dioxus::core_macro::rsx;
|
||||||
use dioxus::dioxus_core::Element;
|
use dioxus::dioxus_core::Element;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_sdk::i18n::use_i18;
|
||||||
|
use dioxus_sdk::translate;
|
||||||
|
use voca_rs::Voca;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
|
pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
|
||||||
|
let i18 = use_i18();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "flex flex-col",
|
class: "flex flex-col",
|
||||||
@ -22,11 +28,47 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
|
|||||||
i {
|
i {
|
||||||
class: "fa-solid fa-bomb"
|
class: "fa-solid fa-bomb"
|
||||||
},
|
},
|
||||||
{deadline.format(if deadline.year() == Local::now().year() {
|
{
|
||||||
" %m. %-d."
|
let today_date = Local::now().date_naive();
|
||||||
} else {
|
format!(
|
||||||
" %m. %-d. %Y"
|
" {}",
|
||||||
}).to_string()}
|
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() {
|
if let Category::Calendar { time, .. } = task.task().category() {
|
||||||
@ -36,7 +78,10 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
|
|||||||
i {
|
i {
|
||||||
class: "fa-solid fa-clock"
|
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()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user
Improve error handling when unwrapping the
language_identifier
.The
language_identifier
is being force unwrapped usingunwrap()
at line 15. This can lead to a panic if the value isNone
.Consider using
match
orif let
to handle theNone
case gracefully: