i18n->tr in rslib/ to match Python/TS code
This commit is contained in:
parent
d6b9cc4d9b
commit
3433c02242
@ -426,50 +426,50 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn i18n() {
|
fn i18n() {
|
||||||
// English template
|
// English template
|
||||||
let i18n = I18n::new(&["zz"]);
|
let tr = I18n::new(&["zz"]);
|
||||||
assert_eq!(i18n.translate("valid-key", None), "a valid key");
|
assert_eq!(tr.translate("valid-key", None), "a valid key");
|
||||||
assert_eq!(i18n.translate("invalid-key", None), "invalid-key");
|
assert_eq!(tr.translate("invalid-key", None), "invalid-key");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
i18n.translate("two-args-key", Some(tr_args!["one"=>1.1, "two"=>"2"])),
|
tr.translate("two-args-key", Some(tr_args!["one"=>1.1, "two"=>"2"])),
|
||||||
"two args: 1.1 and 2"
|
"two args: 1.1 and 2"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
i18n.translate("plural", Some(tr_args!["hats"=>1.0])),
|
tr.translate("plural", Some(tr_args!["hats"=>1.0])),
|
||||||
"You have 1 hat."
|
"You have 1 hat."
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
i18n.translate("plural", Some(tr_args!["hats"=>1.1])),
|
tr.translate("plural", Some(tr_args!["hats"=>1.1])),
|
||||||
"You have 1.1 hats."
|
"You have 1.1 hats."
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
i18n.translate("plural", Some(tr_args!["hats"=>3])),
|
tr.translate("plural", Some(tr_args!["hats"=>3])),
|
||||||
"You have 3 hats."
|
"You have 3 hats."
|
||||||
);
|
);
|
||||||
|
|
||||||
// Another language
|
// Another language
|
||||||
let i18n = I18n::new(&["ja_JP"]);
|
let tr = I18n::new(&["ja_JP"]);
|
||||||
assert_eq!(i18n.translate("valid-key", None), "キー");
|
assert_eq!(tr.translate("valid-key", None), "キー");
|
||||||
assert_eq!(i18n.translate("only-in-english", None), "not translated");
|
assert_eq!(tr.translate("only-in-english", None), "not translated");
|
||||||
assert_eq!(i18n.translate("invalid-key", None), "invalid-key");
|
assert_eq!(tr.translate("invalid-key", None), "invalid-key");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
i18n.translate("two-args-key", Some(tr_args!["one"=>1, "two"=>"2"])),
|
tr.translate("two-args-key", Some(tr_args!["one"=>1, "two"=>"2"])),
|
||||||
"1と2"
|
"1と2"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decimal separator
|
// Decimal separator
|
||||||
let i18n = I18n::new(&["pl-PL"]);
|
let tr = I18n::new(&["pl-PL"]);
|
||||||
// Polish will use a comma if the string is translated
|
// Polish will use a comma if the string is translated
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
i18n.translate("one-arg-key", Some(tr_args!["one"=>2.07])),
|
tr.translate("one-arg-key", Some(tr_args!["one"=>2.07])),
|
||||||
"fake Polish 2,07"
|
"fake Polish 2,07"
|
||||||
);
|
);
|
||||||
|
|
||||||
// but if it falls back on English, it will use an English separator
|
// but if it falls back on English, it will use an English separator
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
i18n.translate("two-args-key", Some(tr_args!["one"=>1, "two"=>2.07])),
|
tr.translate("two-args-key", Some(tr_args!["one"=>1, "two"=>2.07])),
|
||||||
"two args: 1 and 2.07"
|
"two args: 1 and 2.07"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ use slog::error;
|
|||||||
impl CollectionService for Backend {
|
impl CollectionService for Backend {
|
||||||
fn latest_progress(&self, _input: pb::Empty) -> Result<pb::Progress> {
|
fn latest_progress(&self, _input: pb::Empty) -> Result<pb::Progress> {
|
||||||
let progress = self.progress_state.lock().unwrap().last_progress;
|
let progress = self.progress_state.lock().unwrap().last_progress;
|
||||||
Ok(progress_to_proto(progress, &self.i18n))
|
Ok(progress_to_proto(progress, &self.tr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_wants_abort(&self, _input: pb::Empty) -> Result<pb::Empty> {
|
fn set_wants_abort(&self, _input: pb::Empty) -> Result<pb::Empty> {
|
||||||
@ -43,7 +43,7 @@ impl CollectionService for Backend {
|
|||||||
input.media_folder_path,
|
input.media_folder_path,
|
||||||
input.media_db_path,
|
input.media_db_path,
|
||||||
self.server,
|
self.server,
|
||||||
self.i18n.clone(),
|
self.tr.clone(),
|
||||||
logger,
|
logger,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -79,26 +79,26 @@ impl CollectionService for Backend {
|
|||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
col.check_database(progress_fn)
|
col.check_database(progress_fn)
|
||||||
.map(|problems| pb::CheckDatabaseOut {
|
.map(|problems| pb::CheckDatabaseOut {
|
||||||
problems: problems.to_i18n_strings(&col.i18n),
|
problems: problems.to_i18n_strings(&col.tr),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_undo_status(&self, _input: pb::Empty) -> Result<pb::UndoStatus> {
|
fn get_undo_status(&self, _input: pb::Empty) -> Result<pb::UndoStatus> {
|
||||||
self.with_col(|col| Ok(col.undo_status().into_protobuf(&col.i18n)))
|
self.with_col(|col| Ok(col.undo_status().into_protobuf(&col.tr)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn undo(&self, _input: pb::Empty) -> Result<pb::UndoStatus> {
|
fn undo(&self, _input: pb::Empty) -> Result<pb::UndoStatus> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
col.undo()?;
|
col.undo()?;
|
||||||
Ok(col.undo_status().into_protobuf(&col.i18n))
|
Ok(col.undo_status().into_protobuf(&col.tr))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn redo(&self, _input: pb::Empty) -> Result<pb::UndoStatus> {
|
fn redo(&self, _input: pb::Empty) -> Result<pb::UndoStatus> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
col.redo()?;
|
col.redo()?;
|
||||||
Ok(col.undo_status().into_protobuf(&col.i18n))
|
Ok(col.undo_status().into_protobuf(&col.tr))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,9 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Convert an Anki error to a protobuf error.
|
/// Convert an Anki error to a protobuf error.
|
||||||
pub(super) fn anki_error_to_proto_error(err: AnkiError, i18n: &I18n) -> pb::BackendError {
|
pub(super) fn anki_error_to_proto_error(err: AnkiError, tr: &I18n) -> pb::BackendError {
|
||||||
use pb::backend_error::Value as V;
|
use pb::backend_error::Value as V;
|
||||||
let localized = err.localized_description(i18n);
|
let localized = err.localized_description(tr);
|
||||||
let value = match err {
|
let value = match err {
|
||||||
AnkiError::InvalidInput { .. } => V::InvalidInput(pb::Empty {}),
|
AnkiError::InvalidInput { .. } => V::InvalidInput(pb::Empty {}),
|
||||||
AnkiError::TemplateError { .. } => V::TemplateParse(pb::Empty {}),
|
AnkiError::TemplateError { .. } => V::TemplateParse(pb::Empty {}),
|
||||||
|
@ -19,7 +19,7 @@ impl I18nService for Backend {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(self
|
Ok(self
|
||||||
.i18n
|
.tr
|
||||||
.translate_via_index(
|
.translate_via_index(
|
||||||
input.module_index as usize,
|
input.module_index as usize,
|
||||||
input.message_index as usize,
|
input.message_index as usize,
|
||||||
@ -31,15 +31,15 @@ impl I18nService for Backend {
|
|||||||
fn format_timespan(&self, input: pb::FormatTimespanIn) -> Result<pb::String> {
|
fn format_timespan(&self, input: pb::FormatTimespanIn) -> Result<pb::String> {
|
||||||
use pb::format_timespan_in::Context;
|
use pb::format_timespan_in::Context;
|
||||||
Ok(match input.context() {
|
Ok(match input.context() {
|
||||||
Context::Precise => time_span(input.seconds, &self.i18n, true),
|
Context::Precise => time_span(input.seconds, &self.tr, true),
|
||||||
Context::Intervals => time_span(input.seconds, &self.i18n, false),
|
Context::Intervals => time_span(input.seconds, &self.tr, false),
|
||||||
Context::AnswerButtons => answer_button_time(input.seconds, &self.i18n),
|
Context::AnswerButtons => answer_button_time(input.seconds, &self.tr),
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn i18n_resources(&self, input: pb::I18nResourcesIn) -> Result<pb::Json> {
|
fn i18n_resources(&self, input: pb::I18nResourcesIn) -> Result<pb::Json> {
|
||||||
serde_json::to_vec(&self.i18n.resources_for_js(&input.modules))
|
serde_json::to_vec(&self.tr.resources_for_js(&input.modules))
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ use self::err::anki_error_to_proto_error;
|
|||||||
|
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
col: Arc<Mutex<Option<Collection>>>,
|
col: Arc<Mutex<Option<Collection>>>,
|
||||||
i18n: I18n,
|
tr: I18n,
|
||||||
server: bool,
|
server: bool,
|
||||||
sync_abort: AbortHandleSlot,
|
sync_abort: AbortHandleSlot,
|
||||||
progress_state: Arc<Mutex<ProgressState>>,
|
progress_state: Arc<Mutex<ProgressState>>,
|
||||||
@ -81,16 +81,16 @@ pub fn init_backend(init_msg: &[u8]) -> std::result::Result<Backend, String> {
|
|||||||
Err(_) => return Err("couldn't decode init request".into()),
|
Err(_) => return Err("couldn't decode init request".into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let i18n = I18n::new(&input.preferred_langs);
|
let tr = I18n::new(&input.preferred_langs);
|
||||||
|
|
||||||
Ok(Backend::new(i18n, input.server))
|
Ok(Backend::new(tr, input.server))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
pub fn new(i18n: I18n, server: bool) -> Backend {
|
pub fn new(tr: I18n, server: bool) -> Backend {
|
||||||
Backend {
|
Backend {
|
||||||
col: Arc::new(Mutex::new(None)),
|
col: Arc::new(Mutex::new(None)),
|
||||||
i18n,
|
tr,
|
||||||
server,
|
server,
|
||||||
sync_abort: Arc::new(Mutex::new(None)),
|
sync_abort: Arc::new(Mutex::new(None)),
|
||||||
progress_state: Arc::new(Mutex::new(ProgressState {
|
progress_state: Arc::new(Mutex::new(ProgressState {
|
||||||
@ -103,7 +103,7 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn i18n(&self) -> &I18n {
|
pub fn i18n(&self) -> &I18n {
|
||||||
&self.i18n
|
&self.tr
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_method(
|
pub fn run_method(
|
||||||
@ -134,7 +134,7 @@ impl Backend {
|
|||||||
pb::ServiceIndex::Cards => CardsService::run_method(self, method, input),
|
pb::ServiceIndex::Cards => CardsService::run_method(self, method, input),
|
||||||
})
|
})
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
let backend_err = anki_error_to_proto_error(err, &self.i18n);
|
let backend_err = anki_error_to_proto_error(err, &self.tr);
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
backend_err.encode(&mut bytes).unwrap();
|
backend_err.encode(&mut bytes).unwrap();
|
||||||
bytes
|
bytes
|
||||||
@ -143,7 +143,7 @@ impl Backend {
|
|||||||
|
|
||||||
pub fn run_db_command_bytes(&self, input: &[u8]) -> std::result::Result<Vec<u8>, Vec<u8>> {
|
pub fn run_db_command_bytes(&self, input: &[u8]) -> std::result::Result<Vec<u8>, Vec<u8>> {
|
||||||
self.db_command(input).map_err(|err| {
|
self.db_command(input).map_err(|err| {
|
||||||
let backend_err = anki_error_to_proto_error(err, &self.i18n);
|
let backend_err = anki_error_to_proto_error(err, &self.tr);
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
backend_err.encode(&mut bytes).unwrap();
|
backend_err.encode(&mut bytes).unwrap();
|
||||||
bytes
|
bytes
|
||||||
|
@ -25,7 +25,7 @@ impl NoteTypesService for Backend {
|
|||||||
|
|
||||||
fn get_stock_notetype_legacy(&self, input: pb::StockNoteType) -> Result<pb::Json> {
|
fn get_stock_notetype_legacy(&self, input: pb::StockNoteType) -> Result<pb::Json> {
|
||||||
// fixme: use individual functions instead of full vec
|
// fixme: use individual functions instead of full vec
|
||||||
let mut all = all_stock_notetypes(&self.i18n);
|
let mut all = all_stock_notetypes(&self.tr);
|
||||||
let idx = (input.kind as usize).min(all.len() - 1);
|
let idx = (input.kind as usize).min(all.len() - 1);
|
||||||
let nt = all.swap_remove(idx);
|
let nt = all.swap_remove(idx);
|
||||||
let schema11: NoteTypeSchema11 = nt.into();
|
let schema11: NoteTypeSchema11 = nt.into();
|
||||||
|
@ -31,10 +31,10 @@ impl From<OpChanges> for pb::OpChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl UndoStatus {
|
impl UndoStatus {
|
||||||
pub(crate) fn into_protobuf(self, i18n: &I18n) -> pb::UndoStatus {
|
pub(crate) fn into_protobuf(self, tr: &I18n) -> pb::UndoStatus {
|
||||||
pb::UndoStatus {
|
pb::UndoStatus {
|
||||||
undo: self.undo.map(|op| op.describe(i18n)).unwrap_or_default(),
|
undo: self.undo.map(|op| op.describe(tr)).unwrap_or_default(),
|
||||||
redo: self.redo.map(|op| op.describe(i18n)).unwrap_or_default(),
|
redo: self.redo.map(|op| op.describe(tr)).unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,12 +52,12 @@ pub(super) enum Progress {
|
|||||||
DatabaseCheck(DatabaseCheckProgress),
|
DatabaseCheck(DatabaseCheckProgress),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn progress_to_proto(progress: Option<Progress>, i18n: &I18n) -> pb::Progress {
|
pub(super) fn progress_to_proto(progress: Option<Progress>, tr: &I18n) -> pb::Progress {
|
||||||
let progress = if let Some(progress) = progress {
|
let progress = if let Some(progress) = progress {
|
||||||
match progress {
|
match progress {
|
||||||
Progress::MediaSync(p) => pb::progress::Value::MediaSync(media_sync_progress(p, i18n)),
|
Progress::MediaSync(p) => pb::progress::Value::MediaSync(media_sync_progress(p, tr)),
|
||||||
Progress::MediaCheck(n) => {
|
Progress::MediaCheck(n) => {
|
||||||
pb::progress::Value::MediaCheck(i18n.media_check_checked(n).into())
|
pb::progress::Value::MediaCheck(tr.media_check_checked(n).into())
|
||||||
}
|
}
|
||||||
Progress::FullSync(p) => pb::progress::Value::FullSync(pb::progress::FullSync {
|
Progress::FullSync(p) => pb::progress::Value::FullSync(pb::progress::FullSync {
|
||||||
transferred: p.transferred_bytes as u32,
|
transferred: p.transferred_bytes as u32,
|
||||||
@ -65,15 +65,15 @@ pub(super) fn progress_to_proto(progress: Option<Progress>, i18n: &I18n) -> pb::
|
|||||||
}),
|
}),
|
||||||
Progress::NormalSync(p) => {
|
Progress::NormalSync(p) => {
|
||||||
let stage = match p.stage {
|
let stage = match p.stage {
|
||||||
SyncStage::Connecting => i18n.sync_syncing(),
|
SyncStage::Connecting => tr.sync_syncing(),
|
||||||
SyncStage::Syncing => i18n.sync_syncing(),
|
SyncStage::Syncing => tr.sync_syncing(),
|
||||||
SyncStage::Finalizing => i18n.sync_checking(),
|
SyncStage::Finalizing => tr.sync_checking(),
|
||||||
}
|
}
|
||||||
.to_string();
|
.to_string();
|
||||||
let added = i18n
|
let added = tr
|
||||||
.sync_added_updated_count(p.local_update, p.remote_update)
|
.sync_added_updated_count(p.local_update, p.remote_update)
|
||||||
.into();
|
.into();
|
||||||
let removed = i18n
|
let removed = tr
|
||||||
.sync_media_removed_count(p.local_remove, p.remote_remove)
|
.sync_media_removed_count(p.local_remove, p.remote_remove)
|
||||||
.into();
|
.into();
|
||||||
pb::progress::Value::NormalSync(pb::progress::NormalSync {
|
pb::progress::Value::NormalSync(pb::progress::NormalSync {
|
||||||
@ -86,15 +86,15 @@ pub(super) fn progress_to_proto(progress: Option<Progress>, i18n: &I18n) -> pb::
|
|||||||
let mut stage_total = 0;
|
let mut stage_total = 0;
|
||||||
let mut stage_current = 0;
|
let mut stage_current = 0;
|
||||||
let stage = match p {
|
let stage = match p {
|
||||||
DatabaseCheckProgress::Integrity => i18n.database_check_checking_integrity(),
|
DatabaseCheckProgress::Integrity => tr.database_check_checking_integrity(),
|
||||||
DatabaseCheckProgress::Optimize => i18n.database_check_rebuilding(),
|
DatabaseCheckProgress::Optimize => tr.database_check_rebuilding(),
|
||||||
DatabaseCheckProgress::Cards => i18n.database_check_checking_cards(),
|
DatabaseCheckProgress::Cards => tr.database_check_checking_cards(),
|
||||||
DatabaseCheckProgress::Notes { current, total } => {
|
DatabaseCheckProgress::Notes { current, total } => {
|
||||||
stage_total = total;
|
stage_total = total;
|
||||||
stage_current = current;
|
stage_current = current;
|
||||||
i18n.database_check_checking_notes()
|
tr.database_check_checking_notes()
|
||||||
}
|
}
|
||||||
DatabaseCheckProgress::History => i18n.database_check_checking_history(),
|
DatabaseCheckProgress::History => tr.database_check_checking_history(),
|
||||||
}
|
}
|
||||||
.to_string();
|
.to_string();
|
||||||
pb::progress::Value::DatabaseCheck(pb::progress::DatabaseCheck {
|
pb::progress::Value::DatabaseCheck(pb::progress::DatabaseCheck {
|
||||||
@ -112,13 +112,13 @@ pub(super) fn progress_to_proto(progress: Option<Progress>, i18n: &I18n) -> pb::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn media_sync_progress(p: MediaSyncProgress, i18n: &I18n) -> pb::progress::MediaSync {
|
fn media_sync_progress(p: MediaSyncProgress, tr: &I18n) -> pb::progress::MediaSync {
|
||||||
pb::progress::MediaSync {
|
pb::progress::MediaSync {
|
||||||
checked: i18n.sync_media_checked_count(p.checked).into(),
|
checked: tr.sync_media_checked_count(p.checked).into(),
|
||||||
added: i18n
|
added: tr
|
||||||
.sync_media_added_count(p.uploaded_files, p.downloaded_files)
|
.sync_media_added_count(p.uploaded_files, p.downloaded_files)
|
||||||
.into(),
|
.into(),
|
||||||
removed: i18n
|
removed: tr
|
||||||
.sync_media_removed_count(p.uploaded_deletions, p.downloaded_deletions)
|
.sync_media_removed_count(p.uploaded_deletions, p.downloaded_deletions)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ impl SchedulingService for Backend {
|
|||||||
|
|
||||||
/// Message rendering only, for old graphs.
|
/// Message rendering only, for old graphs.
|
||||||
fn studied_today_message(&self, input: pb::StudiedTodayMessageIn) -> Result<pb::String> {
|
fn studied_today_message(&self, input: pb::StudiedTodayMessageIn) -> Result<pb::String> {
|
||||||
Ok(studied_today(input.cards, input.seconds as f32, &self.i18n).into())
|
Ok(studied_today(input.cards, input.seconds as f32, &self.tr).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_stats(&self, input: pb::UpdateStatsIn) -> Result<pb::Empty> {
|
fn update_stats(&self, input: pb::UpdateStatsIn) -> Result<pb::Empty> {
|
||||||
|
@ -356,7 +356,7 @@ impl Backend {
|
|||||||
media_folder_path,
|
media_folder_path,
|
||||||
media_db_path,
|
media_db_path,
|
||||||
self.server,
|
self.server,
|
||||||
self.i18n.clone(),
|
self.tr.clone(),
|
||||||
logger,
|
logger,
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ struct RowContext<'a> {
|
|||||||
notetype: Arc<NoteType>,
|
notetype: Arc<NoteType>,
|
||||||
deck: Option<Deck>,
|
deck: Option<Deck>,
|
||||||
original_deck: Option<Option<Deck>>,
|
original_deck: Option<Option<Deck>>,
|
||||||
i18n: &'a I18n,
|
tr: &'a I18n,
|
||||||
timing: SchedTimingToday,
|
timing: SchedTimingToday,
|
||||||
render_context: Option<RenderContext>,
|
render_context: Option<RenderContext>,
|
||||||
}
|
}
|
||||||
@ -152,7 +152,7 @@ impl<'a> RowContext<'a> {
|
|||||||
notetype,
|
notetype,
|
||||||
deck: None,
|
deck: None,
|
||||||
original_deck: None,
|
original_deck: None,
|
||||||
i18n: &col.i18n,
|
tr: &col.tr,
|
||||||
timing,
|
timing,
|
||||||
render_context,
|
render_context,
|
||||||
})
|
})
|
||||||
@ -236,9 +236,9 @@ impl<'a> RowContext<'a> {
|
|||||||
|
|
||||||
fn card_due_str(&mut self) -> String {
|
fn card_due_str(&mut self) -> String {
|
||||||
let due = if self.card.original_deck_id != DeckID(0) {
|
let due = if self.card.original_deck_id != DeckID(0) {
|
||||||
self.i18n.browsing_filtered()
|
self.tr.browsing_filtered()
|
||||||
} else if self.card.queue == CardQueue::New || self.card.ctype == CardType::New {
|
} else if self.card.queue == CardQueue::New || self.card.ctype == CardType::New {
|
||||||
self.i18n.statistics_due_for_new_card(self.card.due)
|
self.tr.statistics_due_for_new_card(self.card.due)
|
||||||
} else {
|
} else {
|
||||||
let date = if self.card.queue == CardQueue::Learn {
|
let date = if self.card.queue == CardQueue::Learn {
|
||||||
TimestampSecs(self.card.due as i64)
|
TimestampSecs(self.card.due as i64)
|
||||||
@ -262,16 +262,16 @@ impl<'a> RowContext<'a> {
|
|||||||
|
|
||||||
fn card_ease_str(&self) -> String {
|
fn card_ease_str(&self) -> String {
|
||||||
match self.card.ctype {
|
match self.card.ctype {
|
||||||
CardType::New => self.i18n.browsing_new().into(),
|
CardType::New => self.tr.browsing_new().into(),
|
||||||
_ => format!("{}%", self.card.ease_factor / 10),
|
_ => format!("{}%", self.card.ease_factor / 10),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn card_interval_str(&self) -> String {
|
fn card_interval_str(&self) -> String {
|
||||||
match self.card.ctype {
|
match self.card.ctype {
|
||||||
CardType::New => self.i18n.browsing_new().into(),
|
CardType::New => self.tr.browsing_new().into(),
|
||||||
CardType::Learn => self.i18n.browsing_learning().into(),
|
CardType::Learn => self.tr.browsing_learning().into(),
|
||||||
_ => time_span((self.card.interval * 86400) as f32, self.i18n, false),
|
_ => time_span((self.card.interval * 86400) as f32, self.tr, false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,18 +19,18 @@ pub fn open_collection<P: Into<PathBuf>>(
|
|||||||
media_folder: P,
|
media_folder: P,
|
||||||
media_db: P,
|
media_db: P,
|
||||||
server: bool,
|
server: bool,
|
||||||
i18n: I18n,
|
tr: I18n,
|
||||||
log: Logger,
|
log: Logger,
|
||||||
) -> Result<Collection> {
|
) -> Result<Collection> {
|
||||||
let col_path = path.into();
|
let col_path = path.into();
|
||||||
let storage = SqliteStorage::open_or_create(&col_path, &i18n, server)?;
|
let storage = SqliteStorage::open_or_create(&col_path, &tr, server)?;
|
||||||
|
|
||||||
let col = Collection {
|
let col = Collection {
|
||||||
storage,
|
storage,
|
||||||
col_path,
|
col_path,
|
||||||
media_folder: media_folder.into(),
|
media_folder: media_folder.into(),
|
||||||
media_db: media_db.into(),
|
media_db: media_db.into(),
|
||||||
i18n,
|
tr,
|
||||||
log,
|
log,
|
||||||
server,
|
server,
|
||||||
state: CollectionState::default(),
|
state: CollectionState::default(),
|
||||||
@ -55,8 +55,8 @@ pub fn open_test_collection() -> Collection {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn open_test_collection_with_server(server: bool) -> Collection {
|
pub fn open_test_collection_with_server(server: bool) -> Collection {
|
||||||
use crate::log;
|
use crate::log;
|
||||||
let i18n = I18n::template_only();
|
let tr = I18n::template_only();
|
||||||
open_collection(":memory:", "", "", server, i18n, log::terminal()).unwrap()
|
open_collection(":memory:", "", "", server, tr, log::terminal()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -76,7 +76,7 @@ pub struct Collection {
|
|||||||
pub(crate) col_path: PathBuf,
|
pub(crate) col_path: PathBuf,
|
||||||
pub(crate) media_folder: PathBuf,
|
pub(crate) media_folder: PathBuf,
|
||||||
pub(crate) media_db: PathBuf,
|
pub(crate) media_db: PathBuf,
|
||||||
pub(crate) i18n: I18n,
|
pub(crate) tr: I18n,
|
||||||
pub(crate) log: Logger,
|
pub(crate) log: Logger,
|
||||||
pub(crate) server: bool,
|
pub(crate) server: bool,
|
||||||
pub(crate) state: CollectionState,
|
pub(crate) state: CollectionState,
|
||||||
|
@ -41,39 +41,39 @@ pub(crate) enum DatabaseCheckProgress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CheckDatabaseOutput {
|
impl CheckDatabaseOutput {
|
||||||
pub fn to_i18n_strings(&self, i18n: &I18n) -> Vec<String> {
|
pub fn to_i18n_strings(&self, tr: &I18n) -> Vec<String> {
|
||||||
let mut probs = Vec::new();
|
let mut probs = Vec::new();
|
||||||
|
|
||||||
if self.notetypes_recovered > 0 {
|
if self.notetypes_recovered > 0 {
|
||||||
probs.push(i18n.database_check_notetypes_recovered());
|
probs.push(tr.database_check_notetypes_recovered());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.card_position_too_high > 0 {
|
if self.card_position_too_high > 0 {
|
||||||
probs.push(i18n.database_check_new_card_high_due(self.card_position_too_high));
|
probs.push(tr.database_check_new_card_high_due(self.card_position_too_high));
|
||||||
}
|
}
|
||||||
if self.card_properties_invalid > 0 {
|
if self.card_properties_invalid > 0 {
|
||||||
probs.push(i18n.database_check_card_properties(self.card_properties_invalid));
|
probs.push(tr.database_check_card_properties(self.card_properties_invalid));
|
||||||
}
|
}
|
||||||
if self.cards_missing_note > 0 {
|
if self.cards_missing_note > 0 {
|
||||||
probs.push(i18n.database_check_card_missing_note(self.cards_missing_note));
|
probs.push(tr.database_check_card_missing_note(self.cards_missing_note));
|
||||||
}
|
}
|
||||||
if self.decks_missing > 0 {
|
if self.decks_missing > 0 {
|
||||||
probs.push(i18n.database_check_missing_decks(self.decks_missing));
|
probs.push(tr.database_check_missing_decks(self.decks_missing));
|
||||||
}
|
}
|
||||||
if self.field_count_mismatch > 0 {
|
if self.field_count_mismatch > 0 {
|
||||||
probs.push(i18n.database_check_field_count(self.field_count_mismatch));
|
probs.push(tr.database_check_field_count(self.field_count_mismatch));
|
||||||
}
|
}
|
||||||
if self.card_ords_duplicated > 0 {
|
if self.card_ords_duplicated > 0 {
|
||||||
probs.push(i18n.database_check_duplicate_card_ords(self.card_ords_duplicated));
|
probs.push(tr.database_check_duplicate_card_ords(self.card_ords_duplicated));
|
||||||
}
|
}
|
||||||
if self.templates_missing > 0 {
|
if self.templates_missing > 0 {
|
||||||
probs.push(i18n.database_check_missing_templates(self.templates_missing));
|
probs.push(tr.database_check_missing_templates(self.templates_missing));
|
||||||
}
|
}
|
||||||
if self.revlog_properties_invalid > 0 {
|
if self.revlog_properties_invalid > 0 {
|
||||||
probs.push(i18n.database_check_revlog_properties(self.revlog_properties_invalid));
|
probs.push(tr.database_check_revlog_properties(self.revlog_properties_invalid));
|
||||||
}
|
}
|
||||||
if self.invalid_utf8 > 0 {
|
if self.invalid_utf8 > 0 {
|
||||||
probs.push(i18n.database_check_notes_with_invalid_utf8(self.invalid_utf8));
|
probs.push(tr.database_check_notes_with_invalid_utf8(self.invalid_utf8));
|
||||||
}
|
}
|
||||||
|
|
||||||
probs.into_iter().map(Into::into).collect()
|
probs.into_iter().map(Into::into).collect()
|
||||||
@ -91,7 +91,7 @@ impl Collection {
|
|||||||
if self.storage.quick_check_corrupt() {
|
if self.storage.quick_check_corrupt() {
|
||||||
debug!(self.log, "quick check failed");
|
debug!(self.log, "quick check failed");
|
||||||
return Err(AnkiError::DBError {
|
return Err(AnkiError::DBError {
|
||||||
info: self.i18n.database_check_corrupt().into(),
|
info: self.tr.database_check_corrupt().into(),
|
||||||
kind: DBErrorKind::Corrupt,
|
kind: DBErrorKind::Corrupt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -266,7 +266,7 @@ impl Collection {
|
|||||||
// if the collection is empty and the user has deleted all note types, ensure at least
|
// if the collection is empty and the user has deleted all note types, ensure at least
|
||||||
// one note type exists
|
// one note type exists
|
||||||
if self.storage.get_all_notetype_names()?.is_empty() {
|
if self.storage.get_all_notetype_names()?.is_empty() {
|
||||||
let mut nt = all_stock_notetypes(&self.i18n).remove(0);
|
let mut nt = all_stock_notetypes(&self.tr).remove(0);
|
||||||
self.add_notetype_inner(&mut nt, usn)?;
|
self.add_notetype_inner(&mut nt, usn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,7 +349,7 @@ impl Collection {
|
|||||||
let extra_cards_required = self
|
let extra_cards_required = self
|
||||||
.storage
|
.storage
|
||||||
.highest_card_ordinal_for_notetype(previous_id)?;
|
.highest_card_ordinal_for_notetype(previous_id)?;
|
||||||
let mut basic = all_stock_notetypes(&self.i18n).remove(0);
|
let mut basic = all_stock_notetypes(&self.tr).remove(0);
|
||||||
let mut field = 3;
|
let mut field = 3;
|
||||||
while basic.fields.len() < field_count {
|
while basic.fields.len() < field_count {
|
||||||
basic.add_field(format!("{}", field));
|
basic.add_field(format!("{}", field));
|
||||||
|
@ -512,7 +512,7 @@ impl Collection {
|
|||||||
if deck.id.0 == 1 {
|
if deck.id.0 == 1 {
|
||||||
// if deleting the default deck, ensure there's a new one, and avoid the grave
|
// if deleting the default deck, ensure there's a new one, and avoid the grave
|
||||||
let mut deck = deck.to_owned();
|
let mut deck = deck.to_owned();
|
||||||
deck.name = self.i18n.deck_config_default_name().into();
|
deck.name = self.tr.deck_config_default_name().into();
|
||||||
deck.set_modified(usn);
|
deck.set_modified(usn);
|
||||||
self.add_or_update_single_deck_with_existing_id(&mut deck, usn)?;
|
self.add_or_update_single_deck_with_existing_id(&mut deck, usn)?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -90,37 +90,37 @@ impl AnkiError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn localized_description(&self, i18n: &I18n) -> String {
|
pub fn localized_description(&self, tr: &I18n) -> String {
|
||||||
match self {
|
match self {
|
||||||
AnkiError::SyncError { info, kind } => match kind {
|
AnkiError::SyncError { info, kind } => match kind {
|
||||||
SyncErrorKind::ServerMessage => info.into(),
|
SyncErrorKind::ServerMessage => info.into(),
|
||||||
SyncErrorKind::Other => info.into(),
|
SyncErrorKind::Other => info.into(),
|
||||||
SyncErrorKind::Conflict => i18n.sync_conflict(),
|
SyncErrorKind::Conflict => tr.sync_conflict(),
|
||||||
SyncErrorKind::ServerError => i18n.sync_server_error(),
|
SyncErrorKind::ServerError => tr.sync_server_error(),
|
||||||
SyncErrorKind::ClientTooOld => i18n.sync_client_too_old(),
|
SyncErrorKind::ClientTooOld => tr.sync_client_too_old(),
|
||||||
SyncErrorKind::AuthFailed => i18n.sync_wrong_pass(),
|
SyncErrorKind::AuthFailed => tr.sync_wrong_pass(),
|
||||||
SyncErrorKind::ResyncRequired => i18n.sync_resync_required(),
|
SyncErrorKind::ResyncRequired => tr.sync_resync_required(),
|
||||||
SyncErrorKind::ClockIncorrect => i18n.sync_clock_off(),
|
SyncErrorKind::ClockIncorrect => tr.sync_clock_off(),
|
||||||
SyncErrorKind::DatabaseCheckRequired => i18n.sync_sanity_check_failed(),
|
SyncErrorKind::DatabaseCheckRequired => tr.sync_sanity_check_failed(),
|
||||||
// server message
|
// server message
|
||||||
SyncErrorKind::SyncNotStarted => "sync not started".into(),
|
SyncErrorKind::SyncNotStarted => "sync not started".into(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
AnkiError::NetworkError { kind, info } => {
|
AnkiError::NetworkError { kind, info } => {
|
||||||
let summary = match kind {
|
let summary = match kind {
|
||||||
NetworkErrorKind::Offline => i18n.network_offline(),
|
NetworkErrorKind::Offline => tr.network_offline(),
|
||||||
NetworkErrorKind::Timeout => i18n.network_timeout(),
|
NetworkErrorKind::Timeout => tr.network_timeout(),
|
||||||
NetworkErrorKind::ProxyAuth => i18n.network_proxy_auth(),
|
NetworkErrorKind::ProxyAuth => tr.network_proxy_auth(),
|
||||||
NetworkErrorKind::Other => i18n.network_other(),
|
NetworkErrorKind::Other => tr.network_other(),
|
||||||
};
|
};
|
||||||
let details = i18n.network_details(info.as_str());
|
let details = tr.network_details(info.as_str());
|
||||||
format!("{}\n\n{}", summary, details)
|
format!("{}\n\n{}", summary, details)
|
||||||
}
|
}
|
||||||
AnkiError::TemplateError { info } => {
|
AnkiError::TemplateError { info } => {
|
||||||
// already localized
|
// already localized
|
||||||
info.into()
|
info.into()
|
||||||
}
|
}
|
||||||
AnkiError::TemplateSaveError { ordinal } => i18n
|
AnkiError::TemplateSaveError { ordinal } => tr
|
||||||
.card_templates_invalid_template_number(ordinal + 1)
|
.card_templates_invalid_template_number(ordinal + 1)
|
||||||
.into(),
|
.into(),
|
||||||
AnkiError::DBError { info, kind } => match kind {
|
AnkiError::DBError { info, kind } => match kind {
|
||||||
@ -130,75 +130,75 @@ impl AnkiError {
|
|||||||
},
|
},
|
||||||
AnkiError::SearchError(kind) => {
|
AnkiError::SearchError(kind) => {
|
||||||
let reason = match kind {
|
let reason = match kind {
|
||||||
SearchErrorKind::MisplacedAnd => i18n.search_misplaced_and(),
|
SearchErrorKind::MisplacedAnd => tr.search_misplaced_and(),
|
||||||
SearchErrorKind::MisplacedOr => i18n.search_misplaced_or(),
|
SearchErrorKind::MisplacedOr => tr.search_misplaced_or(),
|
||||||
SearchErrorKind::EmptyGroup => i18n.search_empty_group(),
|
SearchErrorKind::EmptyGroup => tr.search_empty_group(),
|
||||||
SearchErrorKind::UnopenedGroup => i18n.search_unopened_group(),
|
SearchErrorKind::UnopenedGroup => tr.search_unopened_group(),
|
||||||
SearchErrorKind::UnclosedGroup => i18n.search_unclosed_group(),
|
SearchErrorKind::UnclosedGroup => tr.search_unclosed_group(),
|
||||||
SearchErrorKind::EmptyQuote => i18n.search_empty_quote(),
|
SearchErrorKind::EmptyQuote => tr.search_empty_quote(),
|
||||||
SearchErrorKind::UnclosedQuote => i18n.search_unclosed_quote(),
|
SearchErrorKind::UnclosedQuote => tr.search_unclosed_quote(),
|
||||||
SearchErrorKind::MissingKey => i18n.search_missing_key(),
|
SearchErrorKind::MissingKey => tr.search_missing_key(),
|
||||||
SearchErrorKind::UnknownEscape(ctx) => {
|
SearchErrorKind::UnknownEscape(ctx) => {
|
||||||
i18n.search_unknown_escape(ctx.replace('`', "'"))
|
tr.search_unknown_escape(ctx.replace('`', "'"))
|
||||||
}
|
}
|
||||||
SearchErrorKind::InvalidState(state) => {
|
SearchErrorKind::InvalidState(state) => {
|
||||||
i18n.search_invalid_argument("is:", state.replace('`', "'"))
|
tr.search_invalid_argument("is:", state.replace('`', "'"))
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchErrorKind::InvalidFlag => i18n.search_invalid_flag(),
|
SearchErrorKind::InvalidFlag => tr.search_invalid_flag(),
|
||||||
SearchErrorKind::InvalidPropProperty(prop) => {
|
SearchErrorKind::InvalidPropProperty(prop) => {
|
||||||
i18n.search_invalid_argument("prop:", prop.replace('`', "'"))
|
tr.search_invalid_argument("prop:", prop.replace('`', "'"))
|
||||||
}
|
}
|
||||||
SearchErrorKind::InvalidPropOperator(ctx) => {
|
SearchErrorKind::InvalidPropOperator(ctx) => {
|
||||||
i18n.search_invalid_prop_operator(ctx.as_str())
|
tr.search_invalid_prop_operator(ctx.as_str())
|
||||||
}
|
}
|
||||||
SearchErrorKind::Regex(text) => {
|
SearchErrorKind::Regex(text) => {
|
||||||
format!("<pre>`{}`</pre>", text.replace('`', "'")).into()
|
format!("<pre>`{}`</pre>", text.replace('`', "'")).into()
|
||||||
}
|
}
|
||||||
SearchErrorKind::Other(Some(info)) => info.into(),
|
SearchErrorKind::Other(Some(info)) => info.into(),
|
||||||
SearchErrorKind::Other(None) => i18n.search_invalid_other(),
|
SearchErrorKind::Other(None) => tr.search_invalid_other(),
|
||||||
SearchErrorKind::InvalidNumber { provided, context } => i18n
|
SearchErrorKind::InvalidNumber { provided, context } => tr
|
||||||
.search_invalid_number(
|
.search_invalid_number(
|
||||||
context.replace('`', "'"),
|
context.replace('`', "'"),
|
||||||
provided.replace('`', "'"),
|
provided.replace('`', "'"),
|
||||||
),
|
),
|
||||||
|
|
||||||
SearchErrorKind::InvalidWholeNumber { provided, context } => i18n
|
SearchErrorKind::InvalidWholeNumber { provided, context } => tr
|
||||||
.search_invalid_whole_number(
|
.search_invalid_whole_number(
|
||||||
context.replace('`', "'"),
|
context.replace('`', "'"),
|
||||||
provided.replace('`', "'"),
|
provided.replace('`', "'"),
|
||||||
),
|
),
|
||||||
|
|
||||||
SearchErrorKind::InvalidPositiveWholeNumber { provided, context } => i18n
|
SearchErrorKind::InvalidPositiveWholeNumber { provided, context } => tr
|
||||||
.search_invalid_positive_whole_number(
|
.search_invalid_positive_whole_number(
|
||||||
context.replace('`', "'"),
|
context.replace('`', "'"),
|
||||||
provided.replace('`', "'"),
|
provided.replace('`', "'"),
|
||||||
),
|
),
|
||||||
|
|
||||||
SearchErrorKind::InvalidNegativeWholeNumber { provided, context } => i18n
|
SearchErrorKind::InvalidNegativeWholeNumber { provided, context } => tr
|
||||||
.search_invalid_negative_whole_number(
|
.search_invalid_negative_whole_number(
|
||||||
context.replace('`', "'"),
|
context.replace('`', "'"),
|
||||||
provided.replace('`', "'"),
|
provided.replace('`', "'"),
|
||||||
),
|
),
|
||||||
|
|
||||||
SearchErrorKind::InvalidAnswerButton { provided, context } => i18n
|
SearchErrorKind::InvalidAnswerButton { provided, context } => tr
|
||||||
.search_invalid_answer_button(
|
.search_invalid_answer_button(
|
||||||
context.replace('`', "'"),
|
context.replace('`', "'"),
|
||||||
provided.replace('`', "'"),
|
provided.replace('`', "'"),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
i18n.search_invalid_search(reason).into()
|
tr.search_invalid_search(reason).into()
|
||||||
}
|
}
|
||||||
AnkiError::InvalidInput { info } => {
|
AnkiError::InvalidInput { info } => {
|
||||||
if info.is_empty() {
|
if info.is_empty() {
|
||||||
i18n.errors_invalid_input_empty().into()
|
tr.errors_invalid_input_empty().into()
|
||||||
} else {
|
} else {
|
||||||
i18n.errors_invalid_input_details(info.as_str()).into()
|
tr.errors_invalid_input_details(info.as_str()).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AnkiError::ParseNumError => i18n.errors_parse_number_fail().into(),
|
AnkiError::ParseNumError => tr.errors_parse_number_fail().into(),
|
||||||
AnkiError::DeckIsFiltered => i18n.errors_filtered_parent_deck().into(),
|
AnkiError::DeckIsFiltered => tr.errors_filtered_parent_deck().into(),
|
||||||
AnkiError::FilteredDeckEmpty => i18n.decks_filtered_deck_search_empty().into(),
|
AnkiError::FilteredDeckEmpty => tr.decks_filtered_deck_search_empty().into(),
|
||||||
_ => format!("{:?}", self),
|
_ => format!("{:?}", self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ where
|
|||||||
|
|
||||||
pub fn summarize_output(&self, output: &mut MediaCheckOutput) -> String {
|
pub fn summarize_output(&self, output: &mut MediaCheckOutput) -> String {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
let i = &self.ctx.i18n;
|
let i = &self.ctx.tr;
|
||||||
|
|
||||||
// top summary area
|
// top summary area
|
||||||
if output.trash_count > 0 {
|
if output.trash_count > 0 {
|
||||||
@ -532,9 +532,9 @@ pub(crate) mod test {
|
|||||||
let mgr = MediaManager::new(&media_dir, media_db.clone())?;
|
let mgr = MediaManager::new(&media_dir, media_db.clone())?;
|
||||||
|
|
||||||
let log = log::terminal();
|
let log = log::terminal();
|
||||||
let i18n = I18n::template_only();
|
let tr = I18n::template_only();
|
||||||
|
|
||||||
let col = open_collection(col_path, media_dir, media_db, false, i18n, log)?;
|
let col = open_collection(col_path, media_dir, media_db, false, tr, log)?;
|
||||||
|
|
||||||
Ok((dir, mgr, col))
|
Ok((dir, mgr, col))
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ impl Collection {
|
|||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
"<div><b>{}</b></div><ol>",
|
"<div><b>{}</b></div><ol>",
|
||||||
self.i18n.empty_cards_for_note_type(nt.name.clone())
|
self.tr.empty_cards_for_note_type(nt.name.clone())
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ impl Collection {
|
|||||||
// "Cloze 1, 3"
|
// "Cloze 1, 3"
|
||||||
NoteTypeKind::Cloze => format!(
|
NoteTypeKind::Cloze => format!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
self.i18n.notetypes_cloze_name(),
|
self.tr.notetypes_cloze_name(),
|
||||||
note.empty
|
note.empty
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(ord, _)| (ord + 1).to_string())
|
.map(|(ord, _)| (ord + 1).to_string())
|
||||||
@ -113,7 +113,7 @@ impl Collection {
|
|||||||
"<li class={}>[anki:nid:{}] {}</li>",
|
"<li class={}>[anki:nid:{}] {}</li>",
|
||||||
class,
|
class,
|
||||||
note.nid,
|
note.nid,
|
||||||
self.i18n.empty_cards_count_line(
|
self.tr.empty_cards_count_line(
|
||||||
note.empty.len(),
|
note.empty.len(),
|
||||||
note.current_count,
|
note.current_count,
|
||||||
templates
|
templates
|
||||||
|
@ -491,7 +491,7 @@ impl Collection {
|
|||||||
col.storage.remove_notetype(ntid)?;
|
col.storage.remove_notetype(ntid)?;
|
||||||
let all = col.storage.get_all_notetype_names()?;
|
let all = col.storage.get_all_notetype_names()?;
|
||||||
if all.is_empty() {
|
if all.is_empty() {
|
||||||
let mut nt = all_stock_notetypes(&col.i18n).remove(0);
|
let mut nt = all_stock_notetypes(&col.tr).remove(0);
|
||||||
col.add_notetype_inner(&mut nt, col.usn()?)?;
|
col.add_notetype_inner(&mut nt, col.usn()?)?;
|
||||||
col.set_current_notetype_id(nt.id)
|
col.set_current_notetype_id(nt.id)
|
||||||
} else {
|
} else {
|
||||||
|
@ -56,7 +56,7 @@ impl Collection {
|
|||||||
.ok_or_else(|| AnkiError::invalid_input("no such notetype"))?;
|
.ok_or_else(|| AnkiError::invalid_input("no such notetype"))?;
|
||||||
|
|
||||||
if fill_empty {
|
if fill_empty {
|
||||||
fill_empty_fields(note, &template.config.q_format, &nt, &self.i18n);
|
fill_empty_fields(note, &template.config.q_format, &nt, &self.tr);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.render_card(note, &card, &nt, template, false)
|
self.render_card(note, &card, &nt, template, false)
|
||||||
@ -116,7 +116,7 @@ impl Collection {
|
|||||||
&field_map,
|
&field_map,
|
||||||
card.template_idx,
|
card.template_idx,
|
||||||
nt.is_cloze(),
|
nt.is_cloze(),
|
||||||
&self.i18n,
|
&self.tr,
|
||||||
)?;
|
)?;
|
||||||
Ok(RenderCardOutput { qnodes, anodes })
|
Ok(RenderCardOutput { qnodes, anodes })
|
||||||
}
|
}
|
||||||
@ -165,14 +165,14 @@ fn flag_name(n: u8) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_empty_fields(note: &mut Note, qfmt: &str, nt: &NoteType, i18n: &I18n) {
|
fn fill_empty_fields(note: &mut Note, qfmt: &str, nt: &NoteType, tr: &I18n) {
|
||||||
if let Ok(tmpl) = ParsedTemplate::from_text(qfmt) {
|
if let Ok(tmpl) = ParsedTemplate::from_text(qfmt) {
|
||||||
let cloze_fields = tmpl.cloze_fields();
|
let cloze_fields = tmpl.cloze_fields();
|
||||||
|
|
||||||
for (val, field) in note.fields_mut().iter_mut().zip(nt.fields.iter()) {
|
for (val, field) in note.fields_mut().iter_mut().zip(nt.fields.iter()) {
|
||||||
if field_is_empty(val) {
|
if field_is_empty(val) {
|
||||||
if cloze_fields.contains(&field.name.as_str()) {
|
if cloze_fields.contains(&field.name.as_str()) {
|
||||||
*val = i18n.card_templates_sample_cloze().into();
|
*val = tr.card_templates_sample_cloze().into();
|
||||||
} else {
|
} else {
|
||||||
*val = format!("({})", field.name);
|
*val = format!("({})", field.name);
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@ use crate::{
|
|||||||
use crate::backend_proto::stock_note_type::Kind;
|
use crate::backend_proto::stock_note_type::Kind;
|
||||||
|
|
||||||
impl SqliteStorage {
|
impl SqliteStorage {
|
||||||
pub(crate) fn add_stock_notetypes(&self, i18n: &I18n) -> Result<()> {
|
pub(crate) fn add_stock_notetypes(&self, tr: &I18n) -> Result<()> {
|
||||||
for (idx, mut nt) in all_stock_notetypes(i18n).into_iter().enumerate() {
|
for (idx, mut nt) in all_stock_notetypes(tr).into_iter().enumerate() {
|
||||||
self.add_new_notetype(&mut nt)?;
|
self.add_new_notetype(&mut nt)?;
|
||||||
if idx == Kind::Basic as usize {
|
if idx == Kind::Basic as usize {
|
||||||
self.set_config_entry(&ConfigEntry::boxed(
|
self.set_config_entry(&ConfigEntry::boxed(
|
||||||
@ -31,13 +31,13 @@ impl SqliteStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if changing this, make sure to update StockNoteType enum
|
// if changing this, make sure to update StockNoteType enum
|
||||||
pub fn all_stock_notetypes(i18n: &I18n) -> Vec<NoteType> {
|
pub fn all_stock_notetypes(tr: &I18n) -> Vec<NoteType> {
|
||||||
vec![
|
vec![
|
||||||
basic(i18n),
|
basic(tr),
|
||||||
basic_forward_reverse(i18n),
|
basic_forward_reverse(tr),
|
||||||
basic_optional_reverse(i18n),
|
basic_optional_reverse(tr),
|
||||||
basic_typing(i18n),
|
basic_typing(tr),
|
||||||
cloze(i18n),
|
cloze(tr),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,17 +46,17 @@ fn fieldref<S: AsRef<str>>(name: S) -> String {
|
|||||||
format!("{{{{{}}}}}", name.as_ref())
|
format!("{{{{{}}}}}", name.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn basic(i18n: &I18n) -> NoteType {
|
pub(crate) fn basic(tr: &I18n) -> NoteType {
|
||||||
let mut nt = NoteType {
|
let mut nt = NoteType {
|
||||||
name: i18n.notetypes_basic_name().into(),
|
name: tr.notetypes_basic_name().into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let front = i18n.notetypes_front_field();
|
let front = tr.notetypes_front_field();
|
||||||
let back = i18n.notetypes_back_field();
|
let back = tr.notetypes_back_field();
|
||||||
nt.add_field(front.as_ref());
|
nt.add_field(front.as_ref());
|
||||||
nt.add_field(back.as_ref());
|
nt.add_field(back.as_ref());
|
||||||
nt.add_template(
|
nt.add_template(
|
||||||
i18n.notetypes_card_1_name(),
|
tr.notetypes_card_1_name(),
|
||||||
fieldref(front),
|
fieldref(front),
|
||||||
format!(
|
format!(
|
||||||
"{}\n\n<hr id=answer>\n\n{}",
|
"{}\n\n<hr id=answer>\n\n{}",
|
||||||
@ -68,11 +68,11 @@ pub(crate) fn basic(i18n: &I18n) -> NoteType {
|
|||||||
nt
|
nt
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn basic_typing(i18n: &I18n) -> NoteType {
|
pub(crate) fn basic_typing(tr: &I18n) -> NoteType {
|
||||||
let mut nt = basic(i18n);
|
let mut nt = basic(tr);
|
||||||
nt.name = i18n.notetypes_basic_type_answer_name().into();
|
nt.name = tr.notetypes_basic_type_answer_name().into();
|
||||||
let front = i18n.notetypes_front_field();
|
let front = tr.notetypes_front_field();
|
||||||
let back = i18n.notetypes_back_field();
|
let back = tr.notetypes_back_field();
|
||||||
let tmpl = &mut nt.templates[0].config;
|
let tmpl = &mut nt.templates[0].config;
|
||||||
tmpl.q_format = format!("{}\n\n{{{{type:{}}}}}", fieldref(front.as_ref()), back);
|
tmpl.q_format = format!("{}\n\n{{{{type:{}}}}}", fieldref(front.as_ref()), back);
|
||||||
tmpl.a_format = format!(
|
tmpl.a_format = format!(
|
||||||
@ -84,13 +84,13 @@ pub(crate) fn basic_typing(i18n: &I18n) -> NoteType {
|
|||||||
nt
|
nt
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn basic_forward_reverse(i18n: &I18n) -> NoteType {
|
pub(crate) fn basic_forward_reverse(tr: &I18n) -> NoteType {
|
||||||
let mut nt = basic(i18n);
|
let mut nt = basic(tr);
|
||||||
nt.name = i18n.notetypes_basic_reversed_name().into();
|
nt.name = tr.notetypes_basic_reversed_name().into();
|
||||||
let front = i18n.notetypes_front_field();
|
let front = tr.notetypes_front_field();
|
||||||
let back = i18n.notetypes_back_field();
|
let back = tr.notetypes_back_field();
|
||||||
nt.add_template(
|
nt.add_template(
|
||||||
i18n.notetypes_card_2_name(),
|
tr.notetypes_card_2_name(),
|
||||||
fieldref(back),
|
fieldref(back),
|
||||||
format!(
|
format!(
|
||||||
"{}\n\n<hr id=answer>\n\n{}",
|
"{}\n\n<hr id=answer>\n\n{}",
|
||||||
@ -102,10 +102,10 @@ pub(crate) fn basic_forward_reverse(i18n: &I18n) -> NoteType {
|
|||||||
nt
|
nt
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn basic_optional_reverse(i18n: &I18n) -> NoteType {
|
pub(crate) fn basic_optional_reverse(tr: &I18n) -> NoteType {
|
||||||
let mut nt = basic_forward_reverse(i18n);
|
let mut nt = basic_forward_reverse(tr);
|
||||||
nt.name = i18n.notetypes_basic_optional_reversed_name().into();
|
nt.name = tr.notetypes_basic_optional_reversed_name().into();
|
||||||
let addrev = i18n.notetypes_add_reverse_field();
|
let addrev = tr.notetypes_add_reverse_field();
|
||||||
nt.add_field(addrev.as_ref());
|
nt.add_field(addrev.as_ref());
|
||||||
let tmpl = &mut nt.templates[1].config;
|
let tmpl = &mut nt.templates[1].config;
|
||||||
tmpl.q_format = format!("{{{{#{}}}}}{}{{{{/{}}}}}", addrev, tmpl.q_format, addrev);
|
tmpl.q_format = format!("{{{{#{}}}}}{}{{{{/{}}}}}", addrev, tmpl.q_format, addrev);
|
||||||
@ -113,14 +113,14 @@ pub(crate) fn basic_optional_reverse(i18n: &I18n) -> NoteType {
|
|||||||
nt
|
nt
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cloze(i18n: &I18n) -> NoteType {
|
pub(crate) fn cloze(tr: &I18n) -> NoteType {
|
||||||
let mut nt = NoteType {
|
let mut nt = NoteType {
|
||||||
name: i18n.notetypes_cloze_name().into(),
|
name: tr.notetypes_cloze_name().into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let text = i18n.notetypes_text_field();
|
let text = tr.notetypes_text_field();
|
||||||
nt.add_field(text.as_ref());
|
nt.add_field(text.as_ref());
|
||||||
let back_extra = i18n.notetypes_back_extra_field();
|
let back_extra = tr.notetypes_back_extra_field();
|
||||||
nt.add_field(back_extra.as_ref());
|
nt.add_field(back_extra.as_ref());
|
||||||
let qfmt = format!("{{{{cloze:{}}}}}", text);
|
let qfmt = format!("{{{{cloze:{}}}}}", text);
|
||||||
let afmt = format!("{}<br>\n{{{{{}}}}}", qfmt, back_extra);
|
let afmt = format!("{}<br>\n{{{{{}}}}}", qfmt, back_extra);
|
||||||
|
@ -36,36 +36,36 @@ pub enum Op {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Op {
|
impl Op {
|
||||||
pub fn describe(self, i18n: &I18n) -> String {
|
pub fn describe(self, tr: &I18n) -> String {
|
||||||
match self {
|
match self {
|
||||||
Op::AddDeck => i18n.undo_add_deck(),
|
Op::AddDeck => tr.undo_add_deck(),
|
||||||
Op::AddNote => i18n.undo_add_note(),
|
Op::AddNote => tr.undo_add_note(),
|
||||||
Op::AnswerCard => i18n.undo_answer_card(),
|
Op::AnswerCard => tr.undo_answer_card(),
|
||||||
Op::Bury => i18n.studying_bury(),
|
Op::Bury => tr.studying_bury(),
|
||||||
Op::RemoveDeck => i18n.decks_delete_deck(),
|
Op::RemoveDeck => tr.decks_delete_deck(),
|
||||||
Op::RemoveNote => i18n.studying_delete_note(),
|
Op::RemoveNote => tr.studying_delete_note(),
|
||||||
Op::RenameDeck => i18n.actions_rename_deck(),
|
Op::RenameDeck => tr.actions_rename_deck(),
|
||||||
Op::ScheduleAsNew => i18n.undo_forget_card(),
|
Op::ScheduleAsNew => tr.undo_forget_card(),
|
||||||
Op::SetDueDate => i18n.actions_set_due_date(),
|
Op::SetDueDate => tr.actions_set_due_date(),
|
||||||
Op::Suspend => i18n.studying_suspend(),
|
Op::Suspend => tr.studying_suspend(),
|
||||||
Op::UnburyUnsuspend => i18n.undo_unbury_unsuspend(),
|
Op::UnburyUnsuspend => tr.undo_unbury_unsuspend(),
|
||||||
Op::UpdateCard => i18n.undo_update_card(),
|
Op::UpdateCard => tr.undo_update_card(),
|
||||||
Op::UpdateDeck => i18n.undo_update_deck(),
|
Op::UpdateDeck => tr.undo_update_deck(),
|
||||||
Op::UpdateNote => i18n.undo_update_note(),
|
Op::UpdateNote => tr.undo_update_note(),
|
||||||
Op::UpdatePreferences => i18n.preferences_preferences(),
|
Op::UpdatePreferences => tr.preferences_preferences(),
|
||||||
Op::UpdateTag => i18n.undo_update_tag(),
|
Op::UpdateTag => tr.undo_update_tag(),
|
||||||
Op::SetDeck => i18n.browsing_change_deck(),
|
Op::SetDeck => tr.browsing_change_deck(),
|
||||||
Op::SetFlag => i18n.undo_set_flag(),
|
Op::SetFlag => tr.undo_set_flag(),
|
||||||
Op::FindAndReplace => i18n.browsing_find_and_replace(),
|
Op::FindAndReplace => tr.browsing_find_and_replace(),
|
||||||
Op::ClearUnusedTags => i18n.browsing_clear_unused_tags(),
|
Op::ClearUnusedTags => tr.browsing_clear_unused_tags(),
|
||||||
Op::SortCards => i18n.browsing_reschedule(),
|
Op::SortCards => tr.browsing_reschedule(),
|
||||||
Op::RenameTag => i18n.actions_rename_tag(),
|
Op::RenameTag => tr.actions_rename_tag(),
|
||||||
Op::RemoveTag => i18n.actions_remove_tag(),
|
Op::RemoveTag => tr.actions_remove_tag(),
|
||||||
Op::ReparentTag => i18n.actions_rename_tag(),
|
Op::ReparentTag => tr.actions_rename_tag(),
|
||||||
Op::ReparentDeck => i18n.actions_rename_deck(),
|
Op::ReparentDeck => tr.actions_rename_deck(),
|
||||||
Op::BuildFilteredDeck => i18n.undo_build_filtered_deck(),
|
Op::BuildFilteredDeck => tr.undo_build_filtered_deck(),
|
||||||
Op::RebuildFilteredDeck => i18n.undo_build_filtered_deck(),
|
Op::RebuildFilteredDeck => tr.undo_build_filtered_deck(),
|
||||||
Op::EmptyFilteredDeck => i18n.studying_empty(),
|
Op::EmptyFilteredDeck => tr.studying_empty(),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ impl Collection {
|
|||||||
.maybe_as_days(secs_until_rollover)
|
.maybe_as_days(secs_until_rollover)
|
||||||
.as_seconds(),
|
.as_seconds(),
|
||||||
collapse_time,
|
collapse_time,
|
||||||
&self.i18n,
|
&self.tr,
|
||||||
),
|
),
|
||||||
answer_button_time_collapsible(
|
answer_button_time_collapsible(
|
||||||
choices
|
choices
|
||||||
@ -216,7 +216,7 @@ impl Collection {
|
|||||||
.maybe_as_days(secs_until_rollover)
|
.maybe_as_days(secs_until_rollover)
|
||||||
.as_seconds(),
|
.as_seconds(),
|
||||||
collapse_time,
|
collapse_time,
|
||||||
&self.i18n,
|
&self.tr,
|
||||||
),
|
),
|
||||||
answer_button_time_collapsible(
|
answer_button_time_collapsible(
|
||||||
choices
|
choices
|
||||||
@ -225,7 +225,7 @@ impl Collection {
|
|||||||
.maybe_as_days(secs_until_rollover)
|
.maybe_as_days(secs_until_rollover)
|
||||||
.as_seconds(),
|
.as_seconds(),
|
||||||
collapse_time,
|
collapse_time,
|
||||||
&self.i18n,
|
&self.tr,
|
||||||
),
|
),
|
||||||
answer_button_time_collapsible(
|
answer_button_time_collapsible(
|
||||||
choices
|
choices
|
||||||
@ -234,7 +234,7 @@ impl Collection {
|
|||||||
.maybe_as_days(secs_until_rollover)
|
.maybe_as_days(secs_until_rollover)
|
||||||
.as_seconds(),
|
.as_seconds(),
|
||||||
collapse_time,
|
collapse_time,
|
||||||
&self.i18n,
|
&self.tr,
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -4,26 +4,26 @@
|
|||||||
use crate::i18n::I18n;
|
use crate::i18n::I18n;
|
||||||
|
|
||||||
/// Short string like '4d' to place above answer buttons.
|
/// Short string like '4d' to place above answer buttons.
|
||||||
pub fn answer_button_time(seconds: f32, i18n: &I18n) -> String {
|
pub fn answer_button_time(seconds: f32, tr: &I18n) -> String {
|
||||||
let span = Timespan::from_secs(seconds).natural_span();
|
let span = Timespan::from_secs(seconds).natural_span();
|
||||||
let amount = span.as_rounded_unit_for_answer_buttons();
|
let amount = span.as_rounded_unit_for_answer_buttons();
|
||||||
match span.unit() {
|
match span.unit() {
|
||||||
TimespanUnit::Seconds => i18n.scheduling_answer_button_time_seconds(amount),
|
TimespanUnit::Seconds => tr.scheduling_answer_button_time_seconds(amount),
|
||||||
TimespanUnit::Minutes => i18n.scheduling_answer_button_time_minutes(amount),
|
TimespanUnit::Minutes => tr.scheduling_answer_button_time_minutes(amount),
|
||||||
TimespanUnit::Hours => i18n.scheduling_answer_button_time_hours(amount),
|
TimespanUnit::Hours => tr.scheduling_answer_button_time_hours(amount),
|
||||||
TimespanUnit::Days => i18n.scheduling_answer_button_time_days(amount),
|
TimespanUnit::Days => tr.scheduling_answer_button_time_days(amount),
|
||||||
TimespanUnit::Months => i18n.scheduling_answer_button_time_months(amount),
|
TimespanUnit::Months => tr.scheduling_answer_button_time_months(amount),
|
||||||
TimespanUnit::Years => i18n.scheduling_answer_button_time_years(amount),
|
TimespanUnit::Years => tr.scheduling_answer_button_time_years(amount),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Short string like '4d' to place above answer buttons.
|
/// Short string like '4d' to place above answer buttons.
|
||||||
/// Times within the collapse time are represented like '<10m'
|
/// Times within the collapse time are represented like '<10m'
|
||||||
pub fn answer_button_time_collapsible(seconds: u32, collapse_secs: u32, i18n: &I18n) -> String {
|
pub fn answer_button_time_collapsible(seconds: u32, collapse_secs: u32, tr: &I18n) -> String {
|
||||||
let string = answer_button_time(seconds as f32, i18n);
|
let string = answer_button_time(seconds as f32, tr);
|
||||||
if seconds == 0 {
|
if seconds == 0 {
|
||||||
i18n.scheduling_end().into()
|
tr.scheduling_end().into()
|
||||||
} else if seconds < collapse_secs {
|
} else if seconds < collapse_secs {
|
||||||
format!("<{}", string)
|
format!("<{}", string)
|
||||||
} else {
|
} else {
|
||||||
@ -35,7 +35,7 @@ pub fn answer_button_time_collapsible(seconds: u32, collapse_secs: u32, i18n: &I
|
|||||||
/// If precise is true, show to two decimal places, eg
|
/// If precise is true, show to two decimal places, eg
|
||||||
/// eg 70 seconds -> "1.17 minutes"
|
/// eg 70 seconds -> "1.17 minutes"
|
||||||
/// If false, seconds and days are shown without decimals.
|
/// If false, seconds and days are shown without decimals.
|
||||||
pub fn time_span(seconds: f32, i18n: &I18n, precise: bool) -> String {
|
pub fn time_span(seconds: f32, tr: &I18n, precise: bool) -> String {
|
||||||
let span = Timespan::from_secs(seconds).natural_span();
|
let span = Timespan::from_secs(seconds).natural_span();
|
||||||
let amount = if precise {
|
let amount = if precise {
|
||||||
span.as_unit()
|
span.as_unit()
|
||||||
@ -43,12 +43,12 @@ pub fn time_span(seconds: f32, i18n: &I18n, precise: bool) -> String {
|
|||||||
span.as_rounded_unit()
|
span.as_rounded_unit()
|
||||||
};
|
};
|
||||||
match span.unit() {
|
match span.unit() {
|
||||||
TimespanUnit::Seconds => i18n.scheduling_time_span_seconds(amount),
|
TimespanUnit::Seconds => tr.scheduling_time_span_seconds(amount),
|
||||||
TimespanUnit::Minutes => i18n.scheduling_time_span_minutes(amount),
|
TimespanUnit::Minutes => tr.scheduling_time_span_minutes(amount),
|
||||||
TimespanUnit::Hours => i18n.scheduling_time_span_hours(amount),
|
TimespanUnit::Hours => tr.scheduling_time_span_hours(amount),
|
||||||
TimespanUnit::Days => i18n.scheduling_time_span_days(amount),
|
TimespanUnit::Days => tr.scheduling_time_span_days(amount),
|
||||||
TimespanUnit::Months => i18n.scheduling_time_span_months(amount),
|
TimespanUnit::Months => tr.scheduling_time_span_months(amount),
|
||||||
TimespanUnit::Years => i18n.scheduling_time_span_years(amount),
|
TimespanUnit::Years => tr.scheduling_time_span_years(amount),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@ -171,20 +171,20 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn answer_buttons() {
|
fn answer_buttons() {
|
||||||
let i18n = I18n::template_only();
|
let tr = I18n::template_only();
|
||||||
assert_eq!(answer_button_time(30.0, &i18n), "30s");
|
assert_eq!(answer_button_time(30.0, &tr), "30s");
|
||||||
assert_eq!(answer_button_time(70.0, &i18n), "1m");
|
assert_eq!(answer_button_time(70.0, &tr), "1m");
|
||||||
assert_eq!(answer_button_time(1.1 * MONTH, &i18n), "1.1mo");
|
assert_eq!(answer_button_time(1.1 * MONTH, &tr), "1.1mo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn time_spans() {
|
fn time_spans() {
|
||||||
let i18n = I18n::template_only();
|
let tr = I18n::template_only();
|
||||||
assert_eq!(time_span(1.0, &i18n, false), "1 second");
|
assert_eq!(time_span(1.0, &tr, false), "1 second");
|
||||||
assert_eq!(time_span(30.3, &i18n, false), "30 seconds");
|
assert_eq!(time_span(30.3, &tr, false), "30 seconds");
|
||||||
assert_eq!(time_span(30.3, &i18n, true), "30.3 seconds");
|
assert_eq!(time_span(30.3, &tr, true), "30.3 seconds");
|
||||||
assert_eq!(time_span(90.0, &i18n, false), "1.5 minutes");
|
assert_eq!(time_span(90.0, &tr, false), "1.5 minutes");
|
||||||
assert_eq!(time_span(45.0 * 86_400.0, &i18n, false), "1.5 months");
|
assert_eq!(time_span(45.0 * 86_400.0, &tr, false), "1.5 months");
|
||||||
assert_eq!(time_span(365.0 * 86_400.0 * 1.5, &i18n, false), "1.5 years");
|
assert_eq!(time_span(365.0 * 86_400.0 * 1.5, &tr, false), "1.5 years");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -610,13 +610,13 @@ mod test {
|
|||||||
let col_path = dir.path().join("col.anki2");
|
let col_path = dir.path().join("col.anki2");
|
||||||
fs::write(&col_path, MEDIACHECK_ANKI2).unwrap();
|
fs::write(&col_path, MEDIACHECK_ANKI2).unwrap();
|
||||||
|
|
||||||
let i18n = I18n::template_only();
|
let tr = I18n::template_only();
|
||||||
let mut col = open_collection(
|
let mut col = open_collection(
|
||||||
&col_path,
|
&col_path,
|
||||||
&PathBuf::new(),
|
&PathBuf::new(),
|
||||||
&PathBuf::new(),
|
&PathBuf::new(),
|
||||||
false,
|
false,
|
||||||
i18n,
|
tr,
|
||||||
log::terminal(),
|
log::terminal(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -127,74 +127,71 @@ impl Collection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn card_stats_to_string(&mut self, cs: CardStats) -> Result<String> {
|
fn card_stats_to_string(&mut self, cs: CardStats) -> Result<String> {
|
||||||
let i18n = &self.i18n;
|
let tr = &self.tr;
|
||||||
|
|
||||||
let mut stats = vec![(i18n.card_stats_added().into(), cs.added.date_string())];
|
let mut stats = vec![(tr.card_stats_added().into(), cs.added.date_string())];
|
||||||
if let Some(first) = cs.first_review {
|
if let Some(first) = cs.first_review {
|
||||||
stats.push((i18n.card_stats_first_review().into(), first.date_string()))
|
stats.push((tr.card_stats_first_review().into(), first.date_string()))
|
||||||
}
|
}
|
||||||
if let Some(last) = cs.latest_review {
|
if let Some(last) = cs.latest_review {
|
||||||
stats.push((i18n.card_stats_latest_review().into(), last.date_string()))
|
stats.push((tr.card_stats_latest_review().into(), last.date_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
match cs.due {
|
match cs.due {
|
||||||
Due::Time(secs) => {
|
Due::Time(secs) => {
|
||||||
stats.push((i18n.statistics_due_date().into(), secs.date_string()));
|
stats.push((tr.statistics_due_date().into(), secs.date_string()));
|
||||||
}
|
}
|
||||||
Due::Position(pos) => {
|
Due::Position(pos) => {
|
||||||
stats.push((i18n.card_stats_new_card_position().into(), pos.to_string()));
|
stats.push((tr.card_stats_new_card_position().into(), pos.to_string()));
|
||||||
}
|
}
|
||||||
Due::Unknown => {}
|
Due::Unknown => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
if cs.interval_secs > 0 {
|
if cs.interval_secs > 0 {
|
||||||
stats.push((
|
stats.push((
|
||||||
i18n.card_stats_interval().into(),
|
tr.card_stats_interval().into(),
|
||||||
time_span(cs.interval_secs as f32, i18n, true),
|
time_span(cs.interval_secs as f32, tr, true),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if cs.ease > 0 {
|
if cs.ease > 0 {
|
||||||
stats.push((i18n.card_stats_ease().into(), format!("{}%", cs.ease)));
|
stats.push((tr.card_stats_ease().into(), format!("{}%", cs.ease)));
|
||||||
}
|
}
|
||||||
stats.push((
|
stats.push((tr.card_stats_review_count().into(), cs.reviews.to_string()));
|
||||||
i18n.card_stats_review_count().into(),
|
stats.push((tr.card_stats_lapse_count().into(), cs.lapses.to_string()));
|
||||||
cs.reviews.to_string(),
|
|
||||||
));
|
|
||||||
stats.push((i18n.card_stats_lapse_count().into(), cs.lapses.to_string()));
|
|
||||||
|
|
||||||
if cs.total_secs > 0.0 {
|
if cs.total_secs > 0.0 {
|
||||||
stats.push((
|
stats.push((
|
||||||
i18n.card_stats_average_time().into(),
|
tr.card_stats_average_time().into(),
|
||||||
time_span(cs.average_secs, i18n, true),
|
time_span(cs.average_secs, tr, true),
|
||||||
));
|
));
|
||||||
stats.push((
|
stats.push((
|
||||||
i18n.card_stats_total_time().into(),
|
tr.card_stats_total_time().into(),
|
||||||
time_span(cs.total_secs, i18n, true),
|
time_span(cs.total_secs, tr, true),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.push((i18n.card_stats_card_template().into(), cs.card_type));
|
stats.push((tr.card_stats_card_template().into(), cs.card_type));
|
||||||
stats.push((i18n.card_stats_note_type().into(), cs.note_type));
|
stats.push((tr.card_stats_note_type().into(), cs.note_type));
|
||||||
stats.push((i18n.card_stats_deck_name().into(), cs.deck));
|
stats.push((tr.card_stats_deck_name().into(), cs.deck));
|
||||||
stats.push((i18n.card_stats_card_id().into(), cs.cid.0.to_string()));
|
stats.push((tr.card_stats_card_id().into(), cs.cid.0.to_string()));
|
||||||
stats.push((i18n.card_stats_note_id().into(), cs.nid.0.to_string()));
|
stats.push((tr.card_stats_note_id().into(), cs.nid.0.to_string()));
|
||||||
|
|
||||||
let revlog = cs
|
let revlog = cs
|
||||||
.revlog
|
.revlog
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|e| revlog_to_text(e, i18n))
|
.map(|e| revlog_to_text(e, tr))
|
||||||
.collect();
|
.collect();
|
||||||
let revlog_titles = RevlogText {
|
let revlog_titles = RevlogText {
|
||||||
time: i18n.card_stats_review_log_date().into(),
|
time: tr.card_stats_review_log_date().into(),
|
||||||
kind: i18n.card_stats_review_log_type().into(),
|
kind: tr.card_stats_review_log_type().into(),
|
||||||
kind_class: "".to_string(),
|
kind_class: "".to_string(),
|
||||||
rating: i18n.card_stats_review_log_rating().into(),
|
rating: tr.card_stats_review_log_rating().into(),
|
||||||
interval: i18n.card_stats_interval().into(),
|
interval: tr.card_stats_interval().into(),
|
||||||
ease: i18n.card_stats_ease().into(),
|
ease: tr.card_stats_ease().into(),
|
||||||
rating_class: "".to_string(),
|
rating_class: "".to_string(),
|
||||||
taken_secs: i18n.card_stats_review_log_time_taken().into(),
|
taken_secs: tr.card_stats_review_log_time_taken().into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(CardStatsTemplate {
|
Ok(CardStatsTemplate {
|
||||||
@ -207,15 +204,15 @@ impl Collection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn revlog_to_text(e: RevlogEntry, i18n: &I18n) -> RevlogText {
|
fn revlog_to_text(e: RevlogEntry, tr: &I18n) -> RevlogText {
|
||||||
let dt = Local.timestamp(e.id.as_secs().0, 0);
|
let dt = Local.timestamp(e.id.as_secs().0, 0);
|
||||||
let time = dt.format("<b>%Y-%m-%d</b> @ %H:%M").to_string();
|
let time = dt.format("<b>%Y-%m-%d</b> @ %H:%M").to_string();
|
||||||
let kind = match e.review_kind {
|
let kind = match e.review_kind {
|
||||||
RevlogReviewKind::Learning => i18n.card_stats_review_log_type_learn().into(),
|
RevlogReviewKind::Learning => tr.card_stats_review_log_type_learn().into(),
|
||||||
RevlogReviewKind::Review => i18n.card_stats_review_log_type_review().into(),
|
RevlogReviewKind::Review => tr.card_stats_review_log_type_review().into(),
|
||||||
RevlogReviewKind::Relearning => i18n.card_stats_review_log_type_relearn().into(),
|
RevlogReviewKind::Relearning => tr.card_stats_review_log_type_relearn().into(),
|
||||||
RevlogReviewKind::EarlyReview => i18n.card_stats_review_log_type_filtered().into(),
|
RevlogReviewKind::EarlyReview => tr.card_stats_review_log_type_filtered().into(),
|
||||||
RevlogReviewKind::Manual => i18n.card_stats_review_log_type_manual().into(),
|
RevlogReviewKind::Manual => tr.card_stats_review_log_type_manual().into(),
|
||||||
};
|
};
|
||||||
let kind_class = match e.review_kind {
|
let kind_class = match e.review_kind {
|
||||||
RevlogReviewKind::Learning => String::from("revlog-learn"),
|
RevlogReviewKind::Learning => String::from("revlog-learn"),
|
||||||
@ -229,7 +226,7 @@ fn revlog_to_text(e: RevlogEntry, i18n: &I18n) -> RevlogText {
|
|||||||
String::from("")
|
String::from("")
|
||||||
} else {
|
} else {
|
||||||
let interval_secs = e.interval_secs();
|
let interval_secs = e.interval_secs();
|
||||||
time_span(interval_secs as f32, i18n, true)
|
time_span(interval_secs as f32, tr, true)
|
||||||
};
|
};
|
||||||
let ease = if e.ease_factor > 0 {
|
let ease = if e.ease_factor > 0 {
|
||||||
format!("{}%", e.ease_factor / 10)
|
format!("{}%", e.ease_factor / 10)
|
||||||
@ -241,7 +238,7 @@ fn revlog_to_text(e: RevlogEntry, i18n: &I18n) -> RevlogText {
|
|||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
};
|
};
|
||||||
let taken_secs = i18n
|
let taken_secs = tr
|
||||||
.statistics_seconds_taken((e.taken_millis / 1000) as i32)
|
.statistics_seconds_taken((e.taken_millis / 1000) as i32)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use crate::{i18n::I18n, prelude::*, scheduler::timespan::Timespan};
|
use crate::{i18n::I18n, prelude::*, scheduler::timespan::Timespan};
|
||||||
|
|
||||||
pub fn studied_today(cards: u32, secs: f32, i18n: &I18n) -> String {
|
pub fn studied_today(cards: u32, secs: f32, tr: &I18n) -> String {
|
||||||
let span = Timespan::from_secs(secs).natural_span();
|
let span = Timespan::from_secs(secs).natural_span();
|
||||||
let amount = span.as_unit();
|
let amount = span.as_unit();
|
||||||
let unit = span.unit().as_str();
|
let unit = span.unit().as_str();
|
||||||
@ -12,7 +12,7 @@ pub fn studied_today(cards: u32, secs: f32, i18n: &I18n) -> String {
|
|||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
i18n.statistics_studied_today(unit, secs_per_card, amount, cards)
|
tr.statistics_studied_today(unit, secs_per_card, amount, cards)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ impl Collection {
|
|||||||
pub fn studied_today(&mut self) -> Result<String> {
|
pub fn studied_today(&mut self) -> Result<String> {
|
||||||
let timing = self.timing_today()?;
|
let timing = self.timing_today()?;
|
||||||
let today = self.storage.studied_today(timing.next_day_at)?;
|
let today = self.storage.studied_today(timing.next_day_at)?;
|
||||||
Ok(studied_today(today.cards, today.seconds as f32, &self.i18n))
|
Ok(studied_today(today.cards, today.seconds as f32, &self.tr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,9 +32,9 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn today() {
|
fn today() {
|
||||||
// temporary test of fluent term handling
|
// temporary test of fluent term handling
|
||||||
let i18n = I18n::template_only();
|
let tr = I18n::template_only();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&studied_today(3, 13.0, &i18n).replace("\n", " "),
|
&studied_today(3, 13.0, &tr).replace("\n", " "),
|
||||||
"Studied 3 cards in 13 seconds today (4.33s/card)"
|
"Studied 3 cards in 13 seconds today (4.33s/card)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -507,8 +507,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_card() {
|
fn add_card() {
|
||||||
let i18n = I18n::template_only();
|
let tr = I18n::template_only();
|
||||||
let storage = SqliteStorage::open_or_create(Path::new(":memory:"), &i18n, false).unwrap();
|
let storage = SqliteStorage::open_or_create(Path::new(":memory:"), &tr, false).unwrap();
|
||||||
let mut card = Card::default();
|
let mut card = Card::default();
|
||||||
storage.add_card(&mut card).unwrap();
|
storage.add_card(&mut card).unwrap();
|
||||||
let id1 = card.id;
|
let id1 = card.id;
|
||||||
|
@ -335,11 +335,11 @@ impl SqliteStorage {
|
|||||||
|
|
||||||
// Upgrading/downgrading/legacy
|
// Upgrading/downgrading/legacy
|
||||||
|
|
||||||
pub(super) fn add_default_deck(&self, i18n: &I18n) -> Result<()> {
|
pub(super) fn add_default_deck(&self, tr: &I18n) -> Result<()> {
|
||||||
let mut deck = Deck::new_normal();
|
let mut deck = Deck::new_normal();
|
||||||
deck.id.0 = 1;
|
deck.id.0 = 1;
|
||||||
// fixme: separate key
|
// fixme: separate key
|
||||||
deck.name = i18n.deck_config_default_name().into();
|
deck.name = tr.deck_config_default_name().into();
|
||||||
self.add_or_update_deck_with_existing_id(&deck)
|
self.add_or_update_deck_with_existing_id(&deck)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,10 +113,10 @@ impl SqliteStorage {
|
|||||||
|
|
||||||
// Creating/upgrading/downgrading
|
// Creating/upgrading/downgrading
|
||||||
|
|
||||||
pub(super) fn add_default_deck_config(&self, i18n: &I18n) -> Result<()> {
|
pub(super) fn add_default_deck_config(&self, tr: &I18n) -> Result<()> {
|
||||||
let mut conf = DeckConf::default();
|
let mut conf = DeckConf::default();
|
||||||
conf.id.0 = 1;
|
conf.id.0 = 1;
|
||||||
conf.name = i18n.deck_config_default_name().into();
|
conf.name = tr.deck_config_default_name().into();
|
||||||
self.add_deck_conf(&mut conf)
|
self.add_deck_conf(&mut conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ fn trace(s: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SqliteStorage {
|
impl SqliteStorage {
|
||||||
pub(crate) fn open_or_create(path: &Path, i18n: &I18n, server: bool) -> Result<Self> {
|
pub(crate) fn open_or_create(path: &Path, tr: &I18n, server: bool) -> Result<Self> {
|
||||||
let db = open_or_create_collection_db(path)?;
|
let db = open_or_create_collection_db(path)?;
|
||||||
let (create, ver) = schema_version(&db)?;
|
let (create, ver) = schema_version(&db)?;
|
||||||
|
|
||||||
@ -184,9 +184,9 @@ impl SqliteStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if create {
|
if create {
|
||||||
storage.add_default_deck_config(i18n)?;
|
storage.add_default_deck_config(tr)?;
|
||||||
storage.add_default_deck(i18n)?;
|
storage.add_default_deck(tr)?;
|
||||||
storage.add_stock_notetypes(i18n)?;
|
storage.add_stock_notetypes(tr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if create || upgrade {
|
if create || upgrade {
|
||||||
|
@ -1235,8 +1235,8 @@ mod test {
|
|||||||
|
|
||||||
fn open_col(dir: &Path, server: bool, fname: &str) -> Result<Collection> {
|
fn open_col(dir: &Path, server: bool, fname: &str) -> Result<Collection> {
|
||||||
let path = dir.join(fname);
|
let path = dir.join(fname);
|
||||||
let i18n = I18n::template_only();
|
let tr = I18n::template_only();
|
||||||
open_collection(path, "".into(), "".into(), server, i18n, log::terminal())
|
open_collection(path, "".into(), "".into(), server, tr, log::terminal())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
@ -1390,7 +1390,7 @@ mod test {
|
|||||||
col1.add_or_update_deck(&mut deck)?;
|
col1.add_or_update_deck(&mut deck)?;
|
||||||
|
|
||||||
// and a new notetype
|
// and a new notetype
|
||||||
let mut nt = all_stock_notetypes(&col1.i18n).remove(0);
|
let mut nt = all_stock_notetypes(&col1.tr).remove(0);
|
||||||
nt.name = "new".into();
|
nt.name = "new".into();
|
||||||
col1.add_notetype(&mut nt)?;
|
col1.add_notetype(&mut nt)?;
|
||||||
|
|
||||||
@ -1550,7 +1550,10 @@ mod test {
|
|||||||
// removing things like a notetype forces a full sync
|
// removing things like a notetype forces a full sync
|
||||||
col2.remove_notetype(ntid)?;
|
col2.remove_notetype(ntid)?;
|
||||||
let out = ctx.normal_sync(&mut col2).await;
|
let out = ctx.normal_sync(&mut col2).await;
|
||||||
assert!(matches!(out.required, SyncActionRequired::FullSyncRequired { .. }));
|
assert!(matches!(
|
||||||
|
out.required,
|
||||||
|
SyncActionRequired::FullSyncRequired { .. }
|
||||||
|
));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,14 +246,14 @@ fn parse_inner<'a, I: Iterator<Item = TemplateResult<Token<'a>>>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn template_error_to_anki_error(err: TemplateError, q_side: bool, i18n: &I18n) -> AnkiError {
|
fn template_error_to_anki_error(err: TemplateError, q_side: bool, tr: &I18n) -> AnkiError {
|
||||||
let header = if q_side {
|
let header = if q_side {
|
||||||
i18n.card_template_rendering_front_side_problem()
|
tr.card_template_rendering_front_side_problem()
|
||||||
} else {
|
} else {
|
||||||
i18n.card_template_rendering_back_side_problem()
|
tr.card_template_rendering_back_side_problem()
|
||||||
};
|
};
|
||||||
let details = localized_template_error(i18n, err);
|
let details = localized_template_error(tr, err);
|
||||||
let more_info = i18n.card_template_rendering_more_info();
|
let more_info = tr.card_template_rendering_more_info();
|
||||||
let info = format!(
|
let info = format!(
|
||||||
"{}<br>{}<br><a href='{}'>{}</a>",
|
"{}<br>{}<br><a href='{}'>{}</a>",
|
||||||
header, details, TEMPLATE_ERROR_LINK, more_info
|
header, details, TEMPLATE_ERROR_LINK, more_info
|
||||||
@ -262,31 +262,31 @@ fn template_error_to_anki_error(err: TemplateError, q_side: bool, i18n: &I18n) -
|
|||||||
AnkiError::TemplateError { info }
|
AnkiError::TemplateError { info }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn localized_template_error(i18n: &I18n, err: TemplateError) -> String {
|
fn localized_template_error(tr: &I18n, err: TemplateError) -> String {
|
||||||
match err {
|
match err {
|
||||||
TemplateError::NoClosingBrackets(tag) => i18n
|
TemplateError::NoClosingBrackets(tag) => tr
|
||||||
.card_template_rendering_no_closing_brackets("}}", tag)
|
.card_template_rendering_no_closing_brackets("}}", tag)
|
||||||
.into(),
|
.into(),
|
||||||
TemplateError::ConditionalNotClosed(tag) => i18n
|
TemplateError::ConditionalNotClosed(tag) => tr
|
||||||
.card_template_rendering_conditional_not_closed(format!("{{{{/{}}}}}", tag))
|
.card_template_rendering_conditional_not_closed(format!("{{{{/{}}}}}", tag))
|
||||||
.into(),
|
.into(),
|
||||||
TemplateError::ConditionalNotOpen {
|
TemplateError::ConditionalNotOpen {
|
||||||
closed,
|
closed,
|
||||||
currently_open,
|
currently_open,
|
||||||
} => if let Some(open) = currently_open {
|
} => if let Some(open) = currently_open {
|
||||||
i18n.card_template_rendering_wrong_conditional_closed(
|
tr.card_template_rendering_wrong_conditional_closed(
|
||||||
format!("{{{{/{}}}}}", closed),
|
format!("{{{{/{}}}}}", closed),
|
||||||
format!("{{{{/{}}}}}", open),
|
format!("{{{{/{}}}}}", open),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
i18n.card_template_rendering_conditional_not_open(
|
tr.card_template_rendering_conditional_not_open(
|
||||||
format!("{{{{/{}}}}}", closed),
|
format!("{{{{/{}}}}}", closed),
|
||||||
format!("{{{{#{}}}}}", closed),
|
format!("{{{{#{}}}}}", closed),
|
||||||
format!("{{{{^{}}}}}", closed),
|
format!("{{{{^{}}}}}", closed),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
TemplateError::FieldNotFound { field, filters } => i18n
|
TemplateError::FieldNotFound { field, filters } => tr
|
||||||
.card_template_rendering_no_such_field(format!("{{{{{}{}}}}}", filters, field), field)
|
.card_template_rendering_no_such_field(format!("{{{{{}{}}}}}", filters, field), field)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
@ -531,7 +531,7 @@ pub fn render_card(
|
|||||||
field_map: &HashMap<&str, Cow<str>>,
|
field_map: &HashMap<&str, Cow<str>>,
|
||||||
card_ord: u16,
|
card_ord: u16,
|
||||||
is_cloze: bool,
|
is_cloze: bool,
|
||||||
i18n: &I18n,
|
tr: &I18n,
|
||||||
) -> Result<(Vec<RenderedNode>, Vec<RenderedNode>)> {
|
) -> Result<(Vec<RenderedNode>, Vec<RenderedNode>)> {
|
||||||
// prepare context
|
// prepare context
|
||||||
let mut context = RenderContext {
|
let mut context = RenderContext {
|
||||||
@ -544,22 +544,22 @@ pub fn render_card(
|
|||||||
// question side
|
// question side
|
||||||
let (mut qnodes, qtmpl) = ParsedTemplate::from_text(qfmt)
|
let (mut qnodes, qtmpl) = ParsedTemplate::from_text(qfmt)
|
||||||
.and_then(|tmpl| Ok((tmpl.render(&context)?, tmpl)))
|
.and_then(|tmpl| Ok((tmpl.render(&context)?, tmpl)))
|
||||||
.map_err(|e| template_error_to_anki_error(e, true, i18n))?;
|
.map_err(|e| template_error_to_anki_error(e, true, tr))?;
|
||||||
|
|
||||||
// check if the front side was empty
|
// check if the front side was empty
|
||||||
let empty_message = if is_cloze && cloze_is_empty(field_map, card_ord) {
|
let empty_message = if is_cloze && cloze_is_empty(field_map, card_ord) {
|
||||||
Some(format!(
|
Some(format!(
|
||||||
"<div>{}<br><a href='{}'>{}</a></div>",
|
"<div>{}<br><a href='{}'>{}</a></div>",
|
||||||
i18n.card_template_rendering_missing_cloze(card_ord + 1),
|
tr.card_template_rendering_missing_cloze(card_ord + 1),
|
||||||
TEMPLATE_BLANK_CLOZE_LINK,
|
TEMPLATE_BLANK_CLOZE_LINK,
|
||||||
i18n.card_template_rendering_more_info()
|
tr.card_template_rendering_more_info()
|
||||||
))
|
))
|
||||||
} else if !is_cloze && !qtmpl.renders_with_fields(context.nonempty_fields) {
|
} else if !is_cloze && !qtmpl.renders_with_fields(context.nonempty_fields) {
|
||||||
Some(format!(
|
Some(format!(
|
||||||
"<div>{}<br><a href='{}'>{}</a></div>",
|
"<div>{}<br><a href='{}'>{}</a></div>",
|
||||||
i18n.card_template_rendering_empty_front(),
|
tr.card_template_rendering_empty_front(),
|
||||||
TEMPLATE_BLANK_LINK,
|
TEMPLATE_BLANK_LINK,
|
||||||
i18n.card_template_rendering_more_info()
|
tr.card_template_rendering_more_info()
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -573,7 +573,7 @@ pub fn render_card(
|
|||||||
context.question_side = false;
|
context.question_side = false;
|
||||||
let anodes = ParsedTemplate::from_text(afmt)
|
let anodes = ParsedTemplate::from_text(afmt)
|
||||||
.and_then(|tmpl| tmpl.render(&context))
|
.and_then(|tmpl| tmpl.render(&context))
|
||||||
.map_err(|e| template_error_to_anki_error(e, false, i18n))?;
|
.map_err(|e| template_error_to_anki_error(e, false, tr))?;
|
||||||
|
|
||||||
Ok((qnodes, anodes))
|
Ok((qnodes, anodes))
|
||||||
}
|
}
|
||||||
@ -1113,10 +1113,10 @@ mod test {
|
|||||||
.map(|r| (r.0, r.1.into()))
|
.map(|r| (r.0, r.1.into()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let i18n = I18n::template_only();
|
let tr = I18n::template_only();
|
||||||
use crate::template::RenderedNode as FN;
|
use crate::template::RenderedNode as FN;
|
||||||
|
|
||||||
let qnodes = super::render_card("test{{E}}", "", &map, 1, false, &i18n)
|
let qnodes = super::render_card("test{{E}}", "", &map, 1, false, &tr)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0;
|
.0;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
Loading…
Reference in New Issue
Block a user