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 __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 from weakref import ref
import anki.buildinfo import anki.buildinfo
@ -79,16 +82,20 @@ class RustBackend(RustBackendGenerated):
raise backend_exception_to_pylib(err) raise backend_exception_to_pylib(err)
def translate( 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: ) -> str:
int_key = key if isinstance(key, int) else key.value return self.translate_string(
return self.translate_string(translate_string_in(key=int_key, **kwargs)) translate_string_in(
module_index=module_index, message_index=message_index, **kwargs
)
)
def format_time_span( def format_time_span(
self, self,
seconds: Any, seconds: Any,
context: Any = 2, context: Any = 2,
) -> str: ) -> str:
traceback.print_stack(file=sys.stdout)
print( print(
"please use col.format_timespan() instead of col.backend.format_time_span()" "please use col.format_timespan() instead of col.backend.format_time_span()"
) )
@ -106,7 +113,7 @@ class RustBackend(RustBackendGenerated):
def translate_string_in( def translate_string_in(
key: int, **kwargs: Union[str, int, float] module_index: int, message_index: int, **kwargs: Union[str, int, float]
) -> pb.TranslateStringIn: ) -> pb.TranslateStringIn:
args = {} args = {}
for (k, v) in kwargs.items(): for (k, v) in kwargs.items():
@ -114,18 +121,29 @@ def translate_string_in(
args[k] = pb.TranslateArgValue(str=v) args[k] = pb.TranslateArgValue(str=v)
else: else:
args[k] = pb.TranslateArgValue(number=v) 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): class Translations(GeneratedTranslations):
def __init__(self, backend: Optional[ref[RustBackend]]): def __init__(self, backend: Optional[ref[RustBackend]]):
self.backend = backend 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" "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( 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: ) -> 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: def legacy_enum() -> str:
out = ["class LegacyTranslationEnum(enum.Enum):"] out = ["class LegacyTranslationEnum:"]
for module in modules: for module in modules:
for translation in module["translations"]: for translation in module["translations"]:
key = stringcase.constcase(translation["key"]) key = stringcase.constcase(translation["key"])
value = module["index"] * 1000 + translation["index"] value = f'({module["index"]}, {translation["index"]})'
out.append(f" {key} = {value}") out.append(f" {key} = {value}")
return "\n".join(out) + "\n" return "\n".join(out) + "\n"

View file

@ -9,3 +9,12 @@ def test_no_collection_i18n():
anki.lang.set_lang("ja") anki.lang.set_lang("ja")
assert no_uni(tr.statistics_reviews(reviews=2)) == "2 枚の復習カード" 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 { message TranslateStringIn {
int32 key = 2; uint32 module_index = 1;
uint32 message_index = 2;
map<string, TranslateArgValue> args = 3; 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); write_translations_by_module(map, &mut buf);
// ordered list of translations by module // ordered list of translations by module
write_translation_key_index(modules, &mut buf); write_translation_key_index(modules, &mut buf);
write_legacy_tr_enum(modules, &mut buf);
// methods to generate messages // methods to generate messages
write_methods(modules, &mut buf); write_methods(modules, &mut buf);
@ -61,7 +60,7 @@ impl I18n {
/// {doc} /// {doc}
#[inline] #[inline]
pub fn {func}<'a>(&'a self{in_args}) -> Cow<'a, str> {{ pub fn {func}<'a>(&'a self{in_args}) -> Cow<'a, str> {{
self.tr_("{key}"{out_args}) self.translate("{key}"{out_args})
}}"#, }}"#,
func = func, func = func,
key = key, key = key,
@ -109,19 +108,6 @@ fn build_in_args(translation: &Translation) -> String {
format!(", {}", v.join(", ")) 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) { fn write_translation_key_index(modules: &[Module], buf: &mut String) {
for module in modules { for module in modules {
writeln!( writeln!(

View file

@ -12,24 +12,8 @@ use unic_langid::LanguageIdentifier;
use generated::{KEYS_BY_MODULE, STRINGS}; use generated::{KEYS_BY_MODULE, STRINGS};
pub use generated::LegacyKey as TR;
pub use fluent::fluent_args as tr_args; 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 { fn remapped_lang_name(lang: &LanguageIdentifier) -> &str {
let region = match &lang.region { let region = match &lang.region {
Some(region) => Some(region.as_str()), Some(region) => Some(region.as_str()),
@ -162,11 +146,6 @@ pub struct I18n {
inner: Arc<Mutex<I18nInner>>, 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 { fn get_key(module_idx: usize, translation_idx: usize) -> &'static str {
KEYS_BY_MODULE KEYS_BY_MODULE
.get(module_idx) .get(module_idx)
@ -239,24 +218,17 @@ impl I18n {
} }
} }
/// Get translation with zero arguments. pub fn translate_via_index(
pub fn tr(&self, key: TR) -> Cow<str> { &self,
let key = get_key_legacy(key as usize); module_index: usize,
self.tr_(key, None) 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. fn translate<'a>(&'a self, key: &str, args: Option<FluentArgs>) -> Cow<'a, str> {
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> {
for bundle in &self.inner.lock().unwrap().bundles { for bundle in &self.inner.lock().unwrap().bundles {
let msg = match bundle.get_message(key) { let msg = match bundle.get_message(key) {
Some(msg) => msg, Some(msg) => msg,
@ -455,35 +427,35 @@ mod test {
fn i18n() { fn i18n() {
// English template // English template
let i18n = I18n::new(&["zz"]); let i18n = I18n::new(&["zz"]);
assert_eq!(i18n.tr_("valid-key", None), "a valid key"); assert_eq!(i18n.translate("valid-key", None), "a valid key");
assert_eq!(i18n.tr_("invalid-key", None), "invalid-key"); assert_eq!(i18n.translate("invalid-key", None), "invalid-key");
assert_eq!( 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" "two args: 1.1 and 2"
); );
assert_eq!( assert_eq!(
i18n.tr_("plural", Some(tr_args!["hats"=>1.0])), i18n.translate("plural", Some(tr_args!["hats"=>1.0])),
"You have 1 hat." "You have 1 hat."
); );
assert_eq!( 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." "You have 1.1 hats."
); );
assert_eq!( assert_eq!(
i18n.tr_("plural", Some(tr_args!["hats"=>3])), i18n.translate("plural", Some(tr_args!["hats"=>3])),
"You have 3 hats." "You have 3 hats."
); );
// Another language // Another language
let i18n = I18n::new(&["ja_JP"]); let i18n = I18n::new(&["ja_JP"]);
assert_eq!(i18n.tr_("valid-key", None), "キー"); assert_eq!(i18n.translate("valid-key", None), "キー");
assert_eq!(i18n.tr_("only-in-english", None), "not translated"); assert_eq!(i18n.translate("only-in-english", None), "not translated");
assert_eq!(i18n.tr_("invalid-key", None), "invalid-key"); assert_eq!(i18n.translate("invalid-key", None), "invalid-key");
assert_eq!( 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" "1と2"
); );
@ -491,13 +463,13 @@ mod test {
let i18n = I18n::new(&["pl-PL"]); let i18n = I18n::new(&["pl-PL"]);
// Polish will use a comma if the string is translated // Polish will use a comma if the string is translated
assert_eq!( 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" "fake Polish 2,07"
); );
// but if it falls back on English, it will use an English separator // but if it falls back on English, it will use an English separator
assert_eq!( 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" "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 { impl I18nService for Backend {
fn translate_string(&self, input: pb::TranslateStringIn) -> Result<pb::String> { fn translate_string(&self, input: pb::TranslateStringIn) -> Result<pb::String> {
let key = input.key; let args = input
let map = input
.args .args
.iter() .iter()
.map(|(k, v)| (k.as_str(), translate_arg_to_fluent_val(&v))) .map(|(k, v)| (k.as_str(), translate_arg_to_fluent_val(&v)))
.collect(); .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> { fn format_timespan(&self, input: pb::FormatTimespanIn) -> Result<pb::String> {

View file

@ -1,4 +1,4 @@
// 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
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}, deckconf::{DeckConf, DeckConfID},
decks::{Deck, DeckID, DeckKind}, decks::{Deck, DeckID, DeckKind},
err::{AnkiError, Result}, err::{AnkiError, Result},
i18n::{tr_args, tr_strs, I18n, TR}, i18n::I18n,
notes::{Note, NoteID}, notes::{Note, NoteID},
notetype::{NoteType, NoteTypeID}, notetype::{NoteType, NoteTypeID},
ops::{Op, OpChanges, OpOutput}, ops::{Op, OpChanges, OpOutput},