feat: internationalization #43

Merged
matous-volf merged 8 commits from feat/internationalization into main 2024-09-10 14:44:47 +00:00
5 changed files with 122 additions and 37 deletions
Showing only changes of commit 94ea49b76f - Show all commits

View File

@ -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();
coderabbitai[bot] commented 2024-09-10 14:34:32 +00:00 (Migrated from github.com)
Review

Improve error handling when unwrapping the language_identifier.

The language_identifier is being force unwrapped using unwrap() at line 15. This can lead to a panic if the value is None.

Consider using match or if let to handle the None case gracefully:

let language_identifier = match use_server_future(get_language_identifier)?.unwrap() {
    Some(identifier) => identifier,
    None => {
        // Handle the case when language identifier is not available
        // For example, you can use a default language identifier
        "en-US".to_string()
    }
};
**Improve error handling when unwrapping the `language_identifier`.** The `language_identifier` is being force unwrapped using `unwrap()` at line 15. This can lead to a panic if the value is `None`. Consider using `match` or `if let` to handle the `None` case gracefully: ```rust let language_identifier = match use_server_future(get_language_identifier)?.unwrap() { Some(identifier) => identifier, None => { // Handle the case when language identifier is not available // For example, you can use a default language identifier "en-US".to_string() } }; ``` <!-- This is an auto-generated comment by CodeRabbit -->
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",

View File

@ -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()
} }

View File

@ -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
}
)
} }
} }
} }

View File

@ -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"))}
} }
} }
} }

View File

@ -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()))
}
} }
} }
} }