drop the legacy enum from rslib, and pass separate module/message idx

This commit is contained in:
Damien Elmes 2021-03-27 11:56:31 +10:00
parent f485efce16
commit d6b9cc4d9b
9 changed files with 74 additions and 82 deletions

View file

@ -3,7 +3,10 @@
from __future__ import annotations
from typing import Any, Dict, List, Optional, Sequence, Union
import os
import sys
import traceback
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
from weakref import ref
import anki.buildinfo
@ -79,16 +82,20 @@ class RustBackend(RustBackendGenerated):
raise backend_exception_to_pylib(err)
def translate(
self, key: Union[LegacyTranslationEnum, int], **kwargs: Union[str, int, float]
self, module_index: int, message_index: int, **kwargs: Union[str, int, float]
) -> str:
int_key = key if isinstance(key, int) else key.value
return self.translate_string(translate_string_in(key=int_key, **kwargs))
return self.translate_string(
translate_string_in(
module_index=module_index, message_index=message_index, **kwargs
)
)
def format_time_span(
self,
seconds: Any,
context: Any = 2,
) -> str:
traceback.print_stack(file=sys.stdout)
print(
"please use col.format_timespan() instead of col.backend.format_time_span()"
)
@ -106,7 +113,7 @@ class RustBackend(RustBackendGenerated):
def translate_string_in(
key: int, **kwargs: Union[str, int, float]
module_index: int, message_index: int, **kwargs: Union[str, int, float]
) -> pb.TranslateStringIn:
args = {}
for (k, v) in kwargs.items():
@ -114,18 +121,29 @@ def translate_string_in(
args[k] = pb.TranslateArgValue(str=v)
else:
args[k] = pb.TranslateArgValue(number=v)
return pb.TranslateStringIn(key=key, args=args)
return pb.TranslateStringIn(
module_index=module_index, message_index=message_index, args=args
)
class Translations(GeneratedTranslations):
def __init__(self, backend: Optional[ref[RustBackend]]):
self.backend = backend
def __call__(self, *args: Any, **kwargs: Any) -> str:
def __call__(self, key: Tuple[int, int], **kwargs: Any) -> str:
"Mimic the old col.tr / TR interface"
return self.backend().translate(*args, **kwargs)
if "pytest" not in sys.modules:
traceback.print_stack(file=sys.stdout)
print("please use tr.message_name() instead of tr(TR.MESSAGE_NAME)")
(module, message) = key
return self.backend().translate(
module_index=module, message_index=message, **kwargs
)
def _translate(
self, module: int, translation: int, args: Dict[str, Union[str, int, float]]
self, module: int, message: int, args: Dict[str, Union[str, int, float]]
) -> str:
return self.backend().translate(module * 1000 + translation, **args)
return self.backend().translate(
module_index=module, message_index=message, **args
)

View file

@ -17,11 +17,11 @@ class Variable(TypedDict):
def legacy_enum() -> str:
out = ["class LegacyTranslationEnum(enum.Enum):"]
out = ["class LegacyTranslationEnum:"]
for module in modules:
for translation in module["translations"]:
key = stringcase.constcase(translation["key"])
value = module["index"] * 1000 + translation["index"]
value = f'({module["index"]}, {translation["index"]})'
out.append(f" {key} = {value}")
return "\n".join(out) + "\n"

View file

@ -9,3 +9,12 @@ def test_no_collection_i18n():
anki.lang.set_lang("ja")
assert no_uni(tr.statistics_reviews(reviews=2)) == "2 枚の復習カード"
def test_legacy_enum():
anki.lang.set_lang("ja")
TR = anki.lang.TR
tr = anki.lang.tr_legacyglobal
no_uni = anki.lang.without_unicode_isolation
assert no_uni(tr(TR.STATISTICS_REVIEWS, reviews=2)) == "2 枚の復習カード"

View file

@ -751,7 +751,8 @@ message TrashMediaFilesIn {
}
message TranslateStringIn {
int32 key = 2;
uint32 module_index = 1;
uint32 message_index = 2;
map<string, TranslateArgValue> args = 3;
}

View file

@ -20,7 +20,6 @@ pub fn write_strings(map: &TranslationsByLang, modules: &[Module]) {
write_translations_by_module(map, &mut buf);
// ordered list of translations by module
write_translation_key_index(modules, &mut buf);
write_legacy_tr_enum(modules, &mut buf);
// methods to generate messages
write_methods(modules, &mut buf);
@ -61,7 +60,7 @@ impl I18n {
/// {doc}
#[inline]
pub fn {func}<'a>(&'a self{in_args}) -> Cow<'a, str> {{
self.tr_("{key}"{out_args})
self.translate("{key}"{out_args})
}}"#,
func = func,
key = key,
@ -109,19 +108,6 @@ fn build_in_args(translation: &Translation) -> String {
format!(", {}", v.join(", "))
}
fn write_legacy_tr_enum(modules: &[Module], buf: &mut String) {
buf.push_str("pub enum LegacyKey {\n");
for module in modules {
for translation in &module.translations {
let key = translation.key.to_pascal_case();
let number = module.index * 1000 + translation.index;
writeln!(buf, r#" {key} = {number},"#, key = key, number = number).unwrap();
}
}
buf.push_str("}\n");
}
fn write_translation_key_index(modules: &[Module], buf: &mut String) {
for module in modules {
writeln!(

View file

@ -12,24 +12,8 @@ use unic_langid::LanguageIdentifier;
use generated::{KEYS_BY_MODULE, STRINGS};
pub use generated::LegacyKey as TR;
pub use fluent::fluent_args as tr_args;
/// Helper for creating args with &strs
#[macro_export]
macro_rules! tr_strs {
( $($key:expr => $value:expr),* ) => {
{
let mut args: fluent::FluentArgs = fluent::FluentArgs::new();
$(
args.add($key, $value.to_string().into());
)*
args
}
};
}
fn remapped_lang_name(lang: &LanguageIdentifier) -> &str {
let region = match &lang.region {
Some(region) => Some(region.as_str()),
@ -162,11 +146,6 @@ pub struct I18n {
inner: Arc<Mutex<I18nInner>>,
}
fn get_key_legacy(val: usize) -> &'static str {
let (module_idx, translation_idx) = (val / 1000, val % 1000);
get_key(module_idx, translation_idx)
}
fn get_key(module_idx: usize, translation_idx: usize) -> &'static str {
KEYS_BY_MODULE
.get(module_idx)
@ -239,24 +218,17 @@ impl I18n {
}
}
/// Get translation with zero arguments.
pub fn tr(&self, key: TR) -> Cow<str> {
let key = get_key_legacy(key as usize);
self.tr_(key, None)
pub fn translate_via_index(
&self,
module_index: usize,
message_index: usize,
args: FluentArgs,
) -> String {
let key = get_key(module_index, message_index);
self.translate(key, Some(args)).into()
}
/// Get translation with one or more arguments.
pub fn trn(&self, key: TR, args: FluentArgs) -> String {
let key = get_key_legacy(key as usize);
self.tr_(key, Some(args)).into()
}
pub fn trn2(&self, key: usize, args: FluentArgs) -> String {
let key = get_key_legacy(key);
self.tr_(key, Some(args)).into()
}
fn tr_<'a>(&'a self, key: &str, args: Option<FluentArgs>) -> Cow<'a, str> {
fn translate<'a>(&'a self, key: &str, args: Option<FluentArgs>) -> Cow<'a, str> {
for bundle in &self.inner.lock().unwrap().bundles {
let msg = match bundle.get_message(key) {
Some(msg) => msg,
@ -455,35 +427,35 @@ mod test {
fn i18n() {
// English template
let i18n = I18n::new(&["zz"]);
assert_eq!(i18n.tr_("valid-key", None), "a valid key");
assert_eq!(i18n.tr_("invalid-key", None), "invalid-key");
assert_eq!(i18n.translate("valid-key", None), "a valid key");
assert_eq!(i18n.translate("invalid-key", None), "invalid-key");
assert_eq!(
i18n.tr_("two-args-key", Some(tr_args!["one"=>1.1, "two"=>"2"])),
i18n.translate("two-args-key", Some(tr_args!["one"=>1.1, "two"=>"2"])),
"two args: 1.1 and 2"
);
assert_eq!(
i18n.tr_("plural", Some(tr_args!["hats"=>1.0])),
i18n.translate("plural", Some(tr_args!["hats"=>1.0])),
"You have 1 hat."
);
assert_eq!(
i18n.tr_("plural", Some(tr_args!["hats"=>1.1])),
i18n.translate("plural", Some(tr_args!["hats"=>1.1])),
"You have 1.1 hats."
);
assert_eq!(
i18n.tr_("plural", Some(tr_args!["hats"=>3])),
i18n.translate("plural", Some(tr_args!["hats"=>3])),
"You have 3 hats."
);
// Another language
let i18n = I18n::new(&["ja_JP"]);
assert_eq!(i18n.tr_("valid-key", None), "キー");
assert_eq!(i18n.tr_("only-in-english", None), "not translated");
assert_eq!(i18n.tr_("invalid-key", None), "invalid-key");
assert_eq!(i18n.translate("valid-key", None), "キー");
assert_eq!(i18n.translate("only-in-english", None), "not translated");
assert_eq!(i18n.translate("invalid-key", None), "invalid-key");
assert_eq!(
i18n.tr_("two-args-key", Some(tr_args!["one"=>1, "two"=>"2"])),
i18n.translate("two-args-key", Some(tr_args!["one"=>1, "two"=>"2"])),
"1と2"
);
@ -491,13 +463,13 @@ mod test {
let i18n = I18n::new(&["pl-PL"]);
// Polish will use a comma if the string is translated
assert_eq!(
i18n.tr_("one-arg-key", Some(tr_args!["one"=>2.07])),
i18n.translate("one-arg-key", Some(tr_args!["one"=>2.07])),
"fake Polish 2,07"
);
// but if it falls back on English, it will use an English separator
assert_eq!(
i18n.tr_("two-args-key", Some(tr_args!["one"=>1, "two"=>2.07])),
i18n.translate("two-args-key", Some(tr_args!["one"=>1, "two"=>2.07])),
"two args: 1 and 2.07"
);
}

View file

@ -12,14 +12,20 @@ pub(super) use pb::i18n_service::Service as I18nService;
impl I18nService for Backend {
fn translate_string(&self, input: pb::TranslateStringIn) -> Result<pb::String> {
let key = input.key;
let map = input
let args = input
.args
.iter()
.map(|(k, v)| (k.as_str(), translate_arg_to_fluent_val(&v)))
.collect();
Ok(self.i18n.trn2(key as usize, map).into())
Ok(self
.i18n
.translate_via_index(
input.module_index as usize,
input.message_index as usize,
args,
)
.into())
}
fn format_timespan(&self, input: pb::FormatTimespanIn) -> Result<pb::String> {

View file

@ -1,4 +1,4 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
pub use anki_i18n::{tr_args, tr_strs, I18n, TR};
pub use anki_i18n::I18n;

View file

@ -8,7 +8,7 @@ pub use crate::{
deckconf::{DeckConf, DeckConfID},
decks::{Deck, DeckID, DeckKind},
err::{AnkiError, Result},
i18n::{tr_args, tr_strs, I18n, TR},
i18n::I18n,
notes::{Note, NoteID},
notetype::{NoteType, NoteTypeID},
ops::{Op, OpChanges, OpOutput},