mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 15:02:21 -04:00
drop the separate i18n backend
This commit is contained in:
parent
874bc085fe
commit
ba17567617
6 changed files with 31 additions and 106 deletions
|
@ -145,7 +145,7 @@ current_catalog: Optional[
|
||||||
] = None
|
] = None
|
||||||
|
|
||||||
# the current Fluent translation instance
|
# the current Fluent translation instance
|
||||||
current_i18n: Optional[anki.rsbackend.I18nBackend]
|
current_i18n: Optional[anki.rsbackend.RustBackend]
|
||||||
|
|
||||||
# path to locale folder
|
# path to locale folder
|
||||||
locale_folder = ""
|
locale_folder = ""
|
||||||
|
@ -175,9 +175,9 @@ def set_lang(lang: str, locale_dir: str) -> None:
|
||||||
current_catalog = gettext.translation(
|
current_catalog = gettext.translation(
|
||||||
"anki", gettext_dir, languages=[lang], fallback=True
|
"anki", gettext_dir, languages=[lang], fallback=True
|
||||||
)
|
)
|
||||||
current_i18n = anki.rsbackend.I18nBackend(
|
|
||||||
preferred_langs=[lang], ftl_folder=ftl_dir
|
current_i18n = anki.rsbackend.RustBackend(ftl_folder=ftl_dir, langs=[lang])
|
||||||
)
|
|
||||||
locale_folder = locale_dir
|
locale_folder = locale_dir
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -200,12 +200,20 @@ def _on_progress(progress_bytes: bytes) -> bool:
|
||||||
|
|
||||||
|
|
||||||
class RustBackend:
|
class RustBackend:
|
||||||
def __init__(self, server: bool = False) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
ftl_folder: Optional[str] = None,
|
||||||
|
langs: Optional[List[str]] = None,
|
||||||
|
server: bool = False,
|
||||||
|
) -> None:
|
||||||
|
# pick up global defaults if not provided
|
||||||
|
if ftl_folder is None:
|
||||||
ftl_folder = os.path.join(anki.lang.locale_folder, "fluent")
|
ftl_folder = os.path.join(anki.lang.locale_folder, "fluent")
|
||||||
|
if langs is None:
|
||||||
|
langs = [anki.lang.currentLang]
|
||||||
|
|
||||||
init_msg = pb.BackendInit(
|
init_msg = pb.BackendInit(
|
||||||
locale_folder_path=ftl_folder,
|
locale_folder_path=ftl_folder, preferred_langs=langs, server=server,
|
||||||
preferred_langs=[anki.lang.currentLang],
|
|
||||||
server=server,
|
|
||||||
)
|
)
|
||||||
self._backend = ankirspy.open_backend(init_msg.SerializeToString())
|
self._backend = ankirspy.open_backend(init_msg.SerializeToString())
|
||||||
self._backend.set_progress_callback(_on_progress)
|
self._backend.set_progress_callback(_on_progress)
|
||||||
|
@ -428,19 +436,6 @@ def translate_string_in(
|
||||||
return pb.TranslateStringIn(key=key, args=args)
|
return pb.TranslateStringIn(key=key, args=args)
|
||||||
|
|
||||||
|
|
||||||
class I18nBackend:
|
|
||||||
def __init__(self, preferred_langs: List[str], ftl_folder: str) -> None:
|
|
||||||
init_msg = pb.I18nBackendInit(
|
|
||||||
locale_folder_path=ftl_folder, preferred_langs=preferred_langs
|
|
||||||
)
|
|
||||||
self._backend = ankirspy.open_i18n(init_msg.SerializeToString())
|
|
||||||
|
|
||||||
def translate(self, key: TR, **kwargs: Union[str, int, float]) -> str:
|
|
||||||
return self._backend.translate(
|
|
||||||
translate_string_in(key, **kwargs).SerializeToString()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# temporarily force logging of media handling
|
# temporarily force logging of media handling
|
||||||
if "RUST_LOG" not in os.environ:
|
if "RUST_LOG" not in os.environ:
|
||||||
os.environ["RUST_LOG"] = "warn,anki::media=debug"
|
os.environ["RUST_LOG"] = "warn,anki::media=debug"
|
||||||
|
|
|
@ -17,6 +17,7 @@ import anki.lang
|
||||||
import aqt.buildinfo
|
import aqt.buildinfo
|
||||||
from anki import version as _version
|
from anki import version as _version
|
||||||
from anki.consts import HELP_SITE
|
from anki.consts import HELP_SITE
|
||||||
|
from anki.rsbackend import RustBackend
|
||||||
from anki.utils import checksum, isLin, isMac
|
from anki.utils import checksum, isLin, isMac
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from aqt.utils import locale_dir
|
from aqt.utils import locale_dir
|
||||||
|
@ -162,15 +163,15 @@ dialogs = DialogManager()
|
||||||
# Qt requires its translator to be installed before any GUI widgets are
|
# Qt requires its translator to be installed before any GUI widgets are
|
||||||
# loaded, and we need the Qt language to match the gettext language or
|
# loaded, and we need the Qt language to match the gettext language or
|
||||||
# translated shortcuts will not work.
|
# translated shortcuts will not work.
|
||||||
#
|
|
||||||
# The Qt translator needs to be retained to work.
|
|
||||||
|
|
||||||
|
# A reference to the Qt translator needs to be held to prevent it from
|
||||||
|
# being immediately deallocated.
|
||||||
_qtrans: Optional[QTranslator] = None
|
_qtrans: Optional[QTranslator] = None
|
||||||
|
|
||||||
|
|
||||||
def setupLang(
|
def setupLangAndBackend(
|
||||||
pm: ProfileManager, app: QApplication, force: Optional[str] = None
|
pm: ProfileManager, app: QApplication, force: Optional[str] = None
|
||||||
) -> None:
|
) -> RustBackend:
|
||||||
global _qtrans
|
global _qtrans
|
||||||
try:
|
try:
|
||||||
locale.setlocale(locale.LC_ALL, "")
|
locale.setlocale(locale.LC_ALL, "")
|
||||||
|
@ -218,6 +219,8 @@ def setupLang(
|
||||||
if _qtrans.load("qtbase_" + qt_lang, qt_dir):
|
if _qtrans.load("qtbase_" + qt_lang, qt_dir):
|
||||||
app.installTranslator(_qtrans)
|
app.installTranslator(_qtrans)
|
||||||
|
|
||||||
|
return anki.lang.current_i18n
|
||||||
|
|
||||||
|
|
||||||
# App initialisation
|
# App initialisation
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -465,8 +468,8 @@ environment points to a valid, writable folder.""",
|
||||||
if opts.profile:
|
if opts.profile:
|
||||||
pm.openProfile(opts.profile)
|
pm.openProfile(opts.profile)
|
||||||
|
|
||||||
# i18n
|
# i18n & backend
|
||||||
setupLang(pm, app, opts.lang)
|
backend = setupLangAndBackend(pm, app, opts.lang)
|
||||||
|
|
||||||
if isLin and pm.glMode() == "auto":
|
if isLin and pm.glMode() == "auto":
|
||||||
from aqt.utils import gfxDriverIsBroken
|
from aqt.utils import gfxDriverIsBroken
|
||||||
|
@ -483,7 +486,7 @@ environment points to a valid, writable folder.""",
|
||||||
# load the main window
|
# load the main window
|
||||||
import aqt.main
|
import aqt.main
|
||||||
|
|
||||||
mw = aqt.main.AnkiQt(app, pm, opts, args)
|
mw = aqt.main.AnkiQt(app, pm, backend, opts, args)
|
||||||
if exec:
|
if exec:
|
||||||
app.exec()
|
app.exec()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -29,6 +29,7 @@ from anki import hooks
|
||||||
from anki.collection import _Collection
|
from anki.collection import _Collection
|
||||||
from anki.hooks import runHook
|
from anki.hooks import runHook
|
||||||
from anki.lang import _, ngettext
|
from anki.lang import _, ngettext
|
||||||
|
from anki.rsbackend import RustBackend
|
||||||
from anki.sound import AVTag, SoundOrVideoTag
|
from anki.sound import AVTag, SoundOrVideoTag
|
||||||
from anki.storage import Collection
|
from anki.storage import Collection
|
||||||
from anki.utils import devMode, ids2str, intTime, isMac, isWin, splitFields
|
from anki.utils import devMode, ids2str, intTime, isMac, isWin, splitFields
|
||||||
|
@ -78,10 +79,12 @@ class AnkiQt(QMainWindow):
|
||||||
self,
|
self,
|
||||||
app: QApplication,
|
app: QApplication,
|
||||||
profileManager: ProfileManagerType,
|
profileManager: ProfileManagerType,
|
||||||
|
backend: RustBackend,
|
||||||
opts: Namespace,
|
opts: Namespace,
|
||||||
args: List[Any],
|
args: List[Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
QMainWindow.__init__(self)
|
QMainWindow.__init__(self)
|
||||||
|
self.backend = backend
|
||||||
self.state = "startup"
|
self.state = "startup"
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.col: Optional[_Collection] = None
|
self.col: Optional[_Collection] = None
|
||||||
|
|
|
@ -621,52 +621,3 @@ fn media_sync_progress(p: &MediaSyncProgress, i18n: &I18n) -> pb::MediaSyncProgr
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Standalone I18n backend
|
|
||||||
/// This is a hack to allow translating strings in the GUI
|
|
||||||
/// when a collection is not open, and in the future it should
|
|
||||||
/// either be shared with or merged into the backend object.
|
|
||||||
///////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
pub struct I18nBackend {
|
|
||||||
i18n: I18n,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_i18n_backend(init_msg: &[u8]) -> Result<I18nBackend> {
|
|
||||||
let input: pb::I18nBackendInit = match pb::I18nBackendInit::decode(init_msg) {
|
|
||||||
Ok(req) => req,
|
|
||||||
Err(_) => return Err(AnkiError::invalid_input("couldn't decode init msg")),
|
|
||||||
};
|
|
||||||
|
|
||||||
let log = log::terminal();
|
|
||||||
|
|
||||||
let i18n = I18n::new(&input.preferred_langs, input.locale_folder_path, log);
|
|
||||||
|
|
||||||
Ok(I18nBackend { i18n })
|
|
||||||
}
|
|
||||||
|
|
||||||
impl I18nBackend {
|
|
||||||
pub fn translate(&self, req: &[u8]) -> String {
|
|
||||||
let req = match pb::TranslateStringIn::decode(req) {
|
|
||||||
Ok(req) => req,
|
|
||||||
Err(_e) => return "decoding error".into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.translate_string(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_string(&self, input: pb::TranslateStringIn) -> String {
|
|
||||||
let key = match pb::FluentString::from_i32(input.key) {
|
|
||||||
Some(key) => key,
|
|
||||||
None => return "invalid key".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let map = input
|
|
||||||
.args
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| (k.as_str(), translate_arg_to_fluent_val(&v)))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
self.i18n.trn(key, map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,9 +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 anki::backend::{
|
use anki::backend::{init_backend, Backend as RustBackend};
|
||||||
init_backend, init_i18n_backend, Backend as RustBackend, I18nBackend as RustI18nBackend,
|
|
||||||
};
|
|
||||||
use pyo3::exceptions::Exception;
|
use pyo3::exceptions::Exception;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::PyBytes;
|
use pyo3::types::PyBytes;
|
||||||
|
@ -87,30 +85,6 @@ impl Backend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// I18n backend
|
|
||||||
//////////////////////////////////
|
|
||||||
|
|
||||||
#[pyclass]
|
|
||||||
struct I18nBackend {
|
|
||||||
backend: RustI18nBackend,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyfunction]
|
|
||||||
fn open_i18n(init_msg: &PyBytes) -> PyResult<I18nBackend> {
|
|
||||||
match init_i18n_backend(init_msg.as_bytes()) {
|
|
||||||
Ok(backend) => Ok(I18nBackend { backend }),
|
|
||||||
Err(e) => Err(exceptions::Exception::py_err(format!("{:?}", e))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl I18nBackend {
|
|
||||||
fn translate(&self, input: &PyBytes) -> String {
|
|
||||||
let in_bytes = input.as_bytes();
|
|
||||||
self.backend.translate(in_bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Module definition
|
// Module definition
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
|
|
||||||
|
@ -119,7 +93,6 @@ fn ankirspy(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
m.add_class::<Backend>()?;
|
m.add_class::<Backend>()?;
|
||||||
m.add_wrapped(wrap_pyfunction!(buildhash)).unwrap();
|
m.add_wrapped(wrap_pyfunction!(buildhash)).unwrap();
|
||||||
m.add_wrapped(wrap_pyfunction!(open_backend)).unwrap();
|
m.add_wrapped(wrap_pyfunction!(open_backend)).unwrap();
|
||||||
m.add_wrapped(wrap_pyfunction!(open_i18n)).unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue