mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 08:46:37 -04:00
expose translations to Python
This commit is contained in:
parent
4fe47b7be4
commit
c395003def
4 changed files with 76 additions and 1 deletions
|
@ -39,6 +39,7 @@ message BackendInput {
|
|||
SyncMediaIn sync_media = 27;
|
||||
Empty check_media = 28;
|
||||
TrashMediaFilesIn trash_media_files = 29;
|
||||
TranslateStringIn translate_string = 30;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,6 +59,7 @@ message BackendOutput {
|
|||
Empty sync_media = 27;
|
||||
MediaCheckOut check_media = 28;
|
||||
Empty trash_media_files = 29;
|
||||
string translate_string = 30;
|
||||
|
||||
BackendError error = 2047;
|
||||
}
|
||||
|
@ -281,4 +283,17 @@ message MediaCheckOut {
|
|||
|
||||
message TrashMediaFilesIn {
|
||||
repeated string fnames = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message TranslateStringIn {
|
||||
StringsGroup group = 1;
|
||||
string key = 2;
|
||||
map<string,TranslateArgValue> args = 3;
|
||||
}
|
||||
|
||||
message TranslateArgValue {
|
||||
oneof value {
|
||||
string str = 1;
|
||||
string number = 2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,6 +126,8 @@ MediaSyncProgress = pb.MediaSyncProgress
|
|||
|
||||
MediaCheckOutput = pb.MediaCheckOut
|
||||
|
||||
StringsGroup = pb.StringsGroup
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExtractedLatex:
|
||||
|
@ -318,3 +320,19 @@ class RustBackend:
|
|||
self._run_command(
|
||||
pb.BackendInput(trash_media_files=pb.TrashMediaFilesIn(fnames=fnames))
|
||||
)
|
||||
|
||||
def translate(
|
||||
self, group: pb.StringsGroup, key: str, **kwargs: Union[str, int, float]
|
||||
):
|
||||
args = {}
|
||||
for (k, v) in kwargs.items():
|
||||
if isinstance(v, str):
|
||||
args[k] = pb.TranslateArgValue(str=v)
|
||||
else:
|
||||
args[k] = pb.TranslateArgValue(number=str(v))
|
||||
|
||||
return self._run_command(
|
||||
pb.BackendInput(
|
||||
translate_string=pb.TranslateStringIn(group=group, key=key, args=args)
|
||||
)
|
||||
).translate_string
|
||||
|
|
|
@ -4,6 +4,7 @@ import os
|
|||
import tempfile
|
||||
|
||||
from anki import Collection as aopen
|
||||
from anki.rsbackend import StringsGroup
|
||||
from anki.stdmodels import addBasicModel, models
|
||||
from anki.utils import isWin
|
||||
from tests.shared import assertException, getEmptyCol
|
||||
|
@ -147,3 +148,17 @@ def test_furigana():
|
|||
m["tmpls"][0]["qfmt"] = "{{kana:}}"
|
||||
mm.save(m)
|
||||
c.q(reload=True)
|
||||
|
||||
|
||||
def test_translate():
|
||||
d = getEmptyCol()
|
||||
tr = d.backend.translate
|
||||
|
||||
# strip off unicode separators
|
||||
def no_uni(s: str) -> str:
|
||||
return s.replace("\u2068", "").replace("\u2069", "")
|
||||
|
||||
assert tr(StringsGroup.TEST, "valid-key") == "a valid key"
|
||||
assert "invalid-key" in tr(StringsGroup.TEST, "invalid-key")
|
||||
assert no_uni(tr(StringsGroup.TEST, "plural", hats=1)) == "You have 1 hat."
|
||||
assert no_uni(tr(StringsGroup.TEST, "plural", hats=2)) == "You have 2 hats."
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::template::{
|
|||
RenderedNode,
|
||||
};
|
||||
use crate::text::{extract_av_tags, strip_av_tags, AVTag};
|
||||
use fluent::FluentValue;
|
||||
use prost::Message;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::PathBuf;
|
||||
|
@ -193,6 +194,7 @@ impl Backend {
|
|||
self.remove_media_files(&input.fnames)?;
|
||||
OValue::TrashMediaFiles(Empty {})
|
||||
}
|
||||
Value::TranslateString(input) => OValue::TranslateString(self.translate_string(input)),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -376,6 +378,31 @@ impl Backend {
|
|||
let mut ctx = mgr.dbctx();
|
||||
mgr.remove_files(&mut ctx, fnames)
|
||||
}
|
||||
|
||||
fn translate_string(&self, input: pb::TranslateStringIn) -> String {
|
||||
let group = match pb::StringsGroup::from_i32(input.group) {
|
||||
Some(group) => group,
|
||||
None => return "".to_string(),
|
||||
};
|
||||
let map = input
|
||||
.args
|
||||
.iter()
|
||||
.map(|(k, v)| (k.as_str(), translate_arg_to_fluent_val(&v)))
|
||||
.collect();
|
||||
|
||||
self.i18n.get(group).trn(&input.key, map)
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_arg_to_fluent_val(arg: &pb::TranslateArgValue) -> FluentValue {
|
||||
use pb::translate_arg_value::Value as V;
|
||||
match &arg.value {
|
||||
Some(val) => match val {
|
||||
V::Str(s) => FluentValue::String(s.into()),
|
||||
V::Number(s) => FluentValue::Number(s.into()),
|
||||
},
|
||||
None => FluentValue::String("".into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn ords_hash_to_set(ords: HashSet<u16>) -> Vec<u32> {
|
||||
|
|
Loading…
Reference in a new issue