expose translations to Python

This commit is contained in:
Damien Elmes 2020-02-16 21:07:40 +10:00
parent 4fe47b7be4
commit c395003def
4 changed files with 76 additions and 1 deletions

View file

@ -39,6 +39,7 @@ message BackendInput {
SyncMediaIn sync_media = 27; SyncMediaIn sync_media = 27;
Empty check_media = 28; Empty check_media = 28;
TrashMediaFilesIn trash_media_files = 29; TrashMediaFilesIn trash_media_files = 29;
TranslateStringIn translate_string = 30;
} }
} }
@ -58,6 +59,7 @@ message BackendOutput {
Empty sync_media = 27; Empty sync_media = 27;
MediaCheckOut check_media = 28; MediaCheckOut check_media = 28;
Empty trash_media_files = 29; Empty trash_media_files = 29;
string translate_string = 30;
BackendError error = 2047; BackendError error = 2047;
} }
@ -281,4 +283,17 @@ message MediaCheckOut {
message TrashMediaFilesIn { message TrashMediaFilesIn {
repeated string fnames = 1; 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;
}
}

View file

@ -126,6 +126,8 @@ MediaSyncProgress = pb.MediaSyncProgress
MediaCheckOutput = pb.MediaCheckOut MediaCheckOutput = pb.MediaCheckOut
StringsGroup = pb.StringsGroup
@dataclass @dataclass
class ExtractedLatex: class ExtractedLatex:
@ -318,3 +320,19 @@ class RustBackend:
self._run_command( self._run_command(
pb.BackendInput(trash_media_files=pb.TrashMediaFilesIn(fnames=fnames)) 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

View file

@ -4,6 +4,7 @@ import os
import tempfile import tempfile
from anki import Collection as aopen from anki import Collection as aopen
from anki.rsbackend import StringsGroup
from anki.stdmodels import addBasicModel, models from anki.stdmodels import addBasicModel, models
from anki.utils import isWin from anki.utils import isWin
from tests.shared import assertException, getEmptyCol from tests.shared import assertException, getEmptyCol
@ -147,3 +148,17 @@ def test_furigana():
m["tmpls"][0]["qfmt"] = "{{kana:}}" m["tmpls"][0]["qfmt"] = "{{kana:}}"
mm.save(m) mm.save(m)
c.q(reload=True) 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."

View file

@ -16,6 +16,7 @@ use crate::template::{
RenderedNode, RenderedNode,
}; };
use crate::text::{extract_av_tags, strip_av_tags, AVTag}; use crate::text::{extract_av_tags, strip_av_tags, AVTag};
use fluent::FluentValue;
use prost::Message; use prost::Message;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::path::PathBuf; use std::path::PathBuf;
@ -193,6 +194,7 @@ impl Backend {
self.remove_media_files(&input.fnames)?; self.remove_media_files(&input.fnames)?;
OValue::TrashMediaFiles(Empty {}) OValue::TrashMediaFiles(Empty {})
} }
Value::TranslateString(input) => OValue::TranslateString(self.translate_string(input)),
}) })
} }
@ -376,6 +378,31 @@ impl Backend {
let mut ctx = mgr.dbctx(); let mut ctx = mgr.dbctx();
mgr.remove_files(&mut ctx, fnames) 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> { fn ords_hash_to_set(ords: HashSet<u16>) -> Vec<u32> {