mirror of
https://github.com/ankitects/anki.git
synced 2025-12-20 10:22:57 -05:00
localize some error messages
This commit is contained in:
parent
67a741958c
commit
d612aa0945
8 changed files with 96 additions and 65 deletions
|
|
@ -18,6 +18,7 @@ enum StringsGroup {
|
||||||
MEDIA_CHECK = 2;
|
MEDIA_CHECK = 2;
|
||||||
CARD_TEMPLATES = 3;
|
CARD_TEMPLATES = 3;
|
||||||
SYNC = 4;
|
SYNC = 4;
|
||||||
|
NETWORK = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1-15 reserved for future use; 2047 for errors
|
// 1-15 reserved for future use; 2047 for errors
|
||||||
|
|
@ -102,6 +103,7 @@ message NetworkError {
|
||||||
PROXY_AUTH = 3;
|
PROXY_AUTH = 3;
|
||||||
}
|
}
|
||||||
NetworkErrorKind kind = 2;
|
NetworkErrorKind kind = 2;
|
||||||
|
string localized = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SyncError {
|
message SyncError {
|
||||||
|
|
@ -117,6 +119,7 @@ message SyncError {
|
||||||
RESYNC_REQUIRED = 7;
|
RESYNC_REQUIRED = 7;
|
||||||
}
|
}
|
||||||
SyncErrorKind kind = 2;
|
SyncErrorKind kind = 2;
|
||||||
|
string localized = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MediaSyncProgress {
|
message MediaSyncProgress {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ class NetworkError(StringError):
|
||||||
def kind(self) -> NetworkErrorKind:
|
def kind(self) -> NetworkErrorKind:
|
||||||
return self.args[1]
|
return self.args[1]
|
||||||
|
|
||||||
|
def localized(self) -> str:
|
||||||
|
return self.args[2]
|
||||||
|
|
||||||
|
|
||||||
class IOError(StringError):
|
class IOError(StringError):
|
||||||
pass
|
pass
|
||||||
|
|
@ -57,6 +60,9 @@ class SyncError(StringError):
|
||||||
def kind(self) -> SyncErrorKind:
|
def kind(self) -> SyncErrorKind:
|
||||||
return self.args[1]
|
return self.args[1]
|
||||||
|
|
||||||
|
def localized(self) -> str:
|
||||||
|
return self.args[2]
|
||||||
|
|
||||||
|
|
||||||
def proto_exception_to_native(err: pb.BackendError) -> Exception:
|
def proto_exception_to_native(err: pb.BackendError) -> Exception:
|
||||||
val = err.WhichOneof("value")
|
val = err.WhichOneof("value")
|
||||||
|
|
@ -64,7 +70,7 @@ def proto_exception_to_native(err: pb.BackendError) -> Exception:
|
||||||
return Interrupted()
|
return Interrupted()
|
||||||
elif val == "network_error":
|
elif val == "network_error":
|
||||||
e = err.network_error
|
e = err.network_error
|
||||||
return NetworkError(e.info, e.kind)
|
return NetworkError(e.info, e.kind, e.localized)
|
||||||
elif val == "io_error":
|
elif val == "io_error":
|
||||||
return IOError(err.io_error.info)
|
return IOError(err.io_error.info)
|
||||||
elif val == "db_error":
|
elif val == "db_error":
|
||||||
|
|
@ -75,7 +81,7 @@ def proto_exception_to_native(err: pb.BackendError) -> Exception:
|
||||||
return StringError(err.invalid_input.info)
|
return StringError(err.invalid_input.info)
|
||||||
elif val == "sync_error":
|
elif val == "sync_error":
|
||||||
e2 = err.sync_error
|
e2 = err.sync_error
|
||||||
return SyncError(e2.info, e2.kind)
|
return SyncError(e2.info, e2.kind, e2.localized)
|
||||||
else:
|
else:
|
||||||
assert_impossible_literal(val)
|
assert_impossible_literal(val)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,14 @@ from typing import List, Union
|
||||||
|
|
||||||
import aqt
|
import aqt
|
||||||
from anki import hooks
|
from anki import hooks
|
||||||
from anki.lang import _
|
|
||||||
from anki.rsbackend import (
|
from anki.rsbackend import (
|
||||||
DBError,
|
|
||||||
Interrupted,
|
Interrupted,
|
||||||
MediaSyncProgress,
|
MediaSyncProgress,
|
||||||
NetworkError,
|
NetworkError,
|
||||||
NetworkErrorKind,
|
|
||||||
Progress,
|
Progress,
|
||||||
ProgressKind,
|
ProgressKind,
|
||||||
StringsGroup,
|
StringsGroup,
|
||||||
SyncError,
|
SyncError,
|
||||||
SyncErrorKind,
|
|
||||||
)
|
)
|
||||||
from anki.types import assert_impossible
|
from anki.types import assert_impossible
|
||||||
from anki.utils import intTime
|
from anki.utils import intTime
|
||||||
|
|
@ -114,40 +110,10 @@ class MediaSyncer:
|
||||||
|
|
||||||
self._log_and_notify(tr(StringsGroup.SYNC, "media-failed"))
|
self._log_and_notify(tr(StringsGroup.SYNC, "media-failed"))
|
||||||
if isinstance(exc, SyncError):
|
if isinstance(exc, SyncError):
|
||||||
kind = exc.kind()
|
showWarning(exc.localized())
|
||||||
if kind == SyncErrorKind.AUTH_FAILED:
|
|
||||||
self.mw.pm.set_sync_key(None)
|
|
||||||
showWarning(
|
|
||||||
_("AnkiWeb ID or password was incorrect; please try again.")
|
|
||||||
)
|
|
||||||
elif kind == SyncErrorKind.SERVER_ERROR:
|
|
||||||
showWarning(
|
|
||||||
_(
|
|
||||||
"AnkiWeb encountered a problem. Please try again in a few minutes."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif kind == SyncErrorKind.MEDIA_CHECK_REQUIRED:
|
|
||||||
showWarning(_("Please use the Tools>Check Media menu option."))
|
|
||||||
elif kind == SyncErrorKind.RESYNC_REQUIRED:
|
|
||||||
showWarning(
|
|
||||||
_(
|
|
||||||
"Please sync again, and post on the support forum if this message keeps appearing."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
showWarning(_("Unexpected error: {}").format(str(exc)))
|
|
||||||
elif isinstance(exc, NetworkError):
|
elif isinstance(exc, NetworkError):
|
||||||
nkind = exc.kind()
|
msg = exc.localized()
|
||||||
if nkind in (NetworkErrorKind.OFFLINE, NetworkErrorKind.TIMEOUT):
|
msg += "\n\n" + tr(StringsGroup.NETWORK, "details", details=str(exc))
|
||||||
showWarning(
|
|
||||||
_("Syncing failed; please check your internet connection.")
|
|
||||||
+ "\n\n"
|
|
||||||
+ _("Detailed error: {}").format(str(exc))
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
showWarning(_("Unexpected error: {}").format(str(exc)))
|
|
||||||
elif isinstance(exc, DBError):
|
|
||||||
showWarning(_("Problem accessing the media database: {}").format(str(exc)))
|
|
||||||
else:
|
else:
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,33 +39,34 @@ enum Progress<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert an Anki error to a protobuf error.
|
/// Convert an Anki error to a protobuf error.
|
||||||
impl std::convert::From<AnkiError> for pb::BackendError {
|
fn anki_error_to_proto_error(err: AnkiError, i18n: &I18n) -> pb::BackendError {
|
||||||
fn from(err: AnkiError) -> Self {
|
use pb::backend_error::Value as V;
|
||||||
use pb::backend_error::Value as V;
|
let localized = err.localized_description(i18n);
|
||||||
let value = match err {
|
let value = match err {
|
||||||
AnkiError::InvalidInput { info } => V::InvalidInput(pb::StringError { info }),
|
AnkiError::InvalidInput { info } => V::InvalidInput(pb::StringError { info }),
|
||||||
AnkiError::TemplateError { info } => V::TemplateParse(pb::TemplateParseError { info }),
|
AnkiError::TemplateError { info } => V::TemplateParse(pb::TemplateParseError { info }),
|
||||||
AnkiError::IOError { info } => V::IoError(pb::StringError { info }),
|
AnkiError::IOError { info } => V::IoError(pb::StringError { info }),
|
||||||
AnkiError::DBError { info } => V::DbError(pb::StringError { info }),
|
AnkiError::DBError { info } => V::DbError(pb::StringError { info }),
|
||||||
AnkiError::NetworkError { info, kind } => V::NetworkError(pb::NetworkError {
|
AnkiError::NetworkError { info, kind } => V::NetworkError(pb::NetworkError {
|
||||||
info,
|
info,
|
||||||
kind: kind.into(),
|
kind: kind.into(),
|
||||||
}),
|
localized,
|
||||||
AnkiError::SyncError { info, kind } => V::SyncError(pb::SyncError {
|
}),
|
||||||
info,
|
AnkiError::SyncError { info, kind } => V::SyncError(pb::SyncError {
|
||||||
kind: kind.into(),
|
info,
|
||||||
}),
|
kind: kind.into(),
|
||||||
AnkiError::Interrupted => V::Interrupted(Empty {}),
|
localized,
|
||||||
};
|
}),
|
||||||
|
AnkiError::Interrupted => V::Interrupted(Empty {}),
|
||||||
|
};
|
||||||
|
|
||||||
pb::BackendError { value: Some(value) }
|
pb::BackendError { value: Some(value) }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert an Anki error to a protobuf output.
|
// Convert an Anki error to a protobuf output.
|
||||||
impl std::convert::From<AnkiError> for pb::backend_output::Value {
|
impl std::convert::From<pb::BackendError> for pb::backend_output::Value {
|
||||||
fn from(err: AnkiError) -> Self {
|
fn from(err: pb::BackendError) -> Self {
|
||||||
pb::backend_output::Value::Error(err.into())
|
pb::backend_output::Value::Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,8 +137,9 @@ impl Backend {
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
// unable to decode
|
// unable to decode
|
||||||
let err = AnkiError::invalid_input("couldn't decode backend request");
|
let err = AnkiError::invalid_input("couldn't decode backend request");
|
||||||
|
let oerr = anki_error_to_proto_error(err, &self.i18n);
|
||||||
let output = pb::BackendOutput {
|
let output = pb::BackendOutput {
|
||||||
value: Some(err.into()),
|
value: Some(oerr.into()),
|
||||||
};
|
};
|
||||||
output.encode(&mut buf).expect("encode failed");
|
output.encode(&mut buf).expect("encode failed");
|
||||||
return buf;
|
return buf;
|
||||||
|
|
@ -153,10 +155,14 @@ impl Backend {
|
||||||
let oval = if let Some(ival) = input.value {
|
let oval = if let Some(ival) = input.value {
|
||||||
match self.run_command_inner(ival) {
|
match self.run_command_inner(ival) {
|
||||||
Ok(output) => output,
|
Ok(output) => output,
|
||||||
Err(err) => err.into(),
|
Err(err) => anki_error_to_proto_error(err, &self.i18n).into(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AnkiError::invalid_input("unrecognized backend input value").into()
|
anki_error_to_proto_error(
|
||||||
|
AnkiError::invalid_input("unrecognized backend input value"),
|
||||||
|
&self.i18n,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
};
|
};
|
||||||
|
|
||||||
pb::BackendOutput { value: Some(oval) }
|
pb::BackendOutput { value: Some(oval) }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
use crate::i18n::{I18n, StringsGroup};
|
||||||
pub use failure::{Error, Fail};
|
pub use failure::{Error, Fail};
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
@ -53,6 +54,36 @@ impl AnkiError {
|
||||||
kind: SyncErrorKind::Other,
|
kind: SyncErrorKind::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn localized_description(&self, i18n: &I18n) -> String {
|
||||||
|
match self {
|
||||||
|
AnkiError::SyncError { info, kind } => {
|
||||||
|
let cat = i18n.get(StringsGroup::Sync);
|
||||||
|
match kind {
|
||||||
|
SyncErrorKind::ServerMessage => info.into(),
|
||||||
|
SyncErrorKind::Other => info.into(),
|
||||||
|
SyncErrorKind::Conflict => cat.tr("conflict"),
|
||||||
|
SyncErrorKind::ServerError => cat.tr("server-error"),
|
||||||
|
SyncErrorKind::ClientTooOld => cat.tr("client-too-old"),
|
||||||
|
SyncErrorKind::AuthFailed => cat.tr("wrong-pass"),
|
||||||
|
SyncErrorKind::MediaCheckRequired => cat.tr("media-check-required"),
|
||||||
|
SyncErrorKind::ResyncRequired => cat.tr("resync-required"),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
AnkiError::NetworkError { kind, .. } => {
|
||||||
|
let cat = i18n.get(StringsGroup::Network);
|
||||||
|
match kind {
|
||||||
|
NetworkErrorKind::Offline => cat.tr("offline"),
|
||||||
|
NetworkErrorKind::Timeout => cat.tr("timeout"),
|
||||||
|
NetworkErrorKind::ProxyAuth => cat.tr("proxy-auth"),
|
||||||
|
NetworkErrorKind::Other => cat.tr("other"),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
_ => "".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ fn ftl_fallback_for_group(group: StringsGroup) -> String {
|
||||||
StringsGroup::MediaCheck => include_str!("media-check.ftl"),
|
StringsGroup::MediaCheck => include_str!("media-check.ftl"),
|
||||||
StringsGroup::CardTemplates => include_str!("card-template-rendering.ftl"),
|
StringsGroup::CardTemplates => include_str!("card-template-rendering.ftl"),
|
||||||
StringsGroup::Sync => include_str!("sync.ftl"),
|
StringsGroup::Sync => include_str!("sync.ftl"),
|
||||||
|
StringsGroup::Network => include_str!("network.ftl"),
|
||||||
}
|
}
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
@ -69,6 +70,7 @@ fn localized_ftl_for_group(group: StringsGroup, lang_ftl_folder: &Path) -> Optio
|
||||||
StringsGroup::MediaCheck => "media-check.ftl",
|
StringsGroup::MediaCheck => "media-check.ftl",
|
||||||
StringsGroup::CardTemplates => "card-template-rendering.ftl",
|
StringsGroup::CardTemplates => "card-template-rendering.ftl",
|
||||||
StringsGroup::Sync => "sync.ftl",
|
StringsGroup::Sync => "sync.ftl",
|
||||||
|
StringsGroup::Network => "network.ftl",
|
||||||
});
|
});
|
||||||
fs::read_to_string(&path)
|
fs::read_to_string(&path)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
|
|
|
||||||
6
rslib/src/i18n/network.ftl
Normal file
6
rslib/src/i18n/network.ftl
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
offline = Please check your internet connection.
|
||||||
|
timeout = Connection timed out. Please try again on a different network.
|
||||||
|
proxy-auth = Your proxy requires authentication.
|
||||||
|
other = A network error occurred.
|
||||||
|
|
||||||
|
details = Error details: {$details}
|
||||||
|
|
@ -14,3 +14,14 @@ media-aborted = Media sync aborted.
|
||||||
media-disabled = Media sync disabled.
|
media-disabled = Media sync disabled.
|
||||||
|
|
||||||
abort-button = Abort
|
abort-button = Abort
|
||||||
|
|
||||||
|
## Error messages
|
||||||
|
|
||||||
|
conflict = Only one copy of Anki can sync to your account at once. Please wait a few minutes, then try again.
|
||||||
|
server-error = AnkiWeb encountered a problem. Please try again in a few minutes.
|
||||||
|
client-too-old =
|
||||||
|
Your Anki version is too old. Please update to the latest version to continue syncing.
|
||||||
|
wrong-pass = AnkiWeb ID or password was incorrect; please try again.
|
||||||
|
media-check-required = Please use the Check Media function, then sync again.
|
||||||
|
resync-required =
|
||||||
|
Please sync again. If this message keeps appearing, please post on the support site.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue