From d6b9cc4d9b3a3b4eb60d133637a916afb5054ee8 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 27 Mar 2021 11:56:31 +1000 Subject: [PATCH] drop the legacy enum from rslib, and pass separate module/message idx --- pylib/anki/_backend/__init__.py | 38 ++++++++++++----- pylib/anki/_backend/genfluent.py | 4 +- qt/tests/test_i18n.py | 9 ++++ rslib/backend.proto | 3 +- rslib/i18n/build/write_strings.rs | 16 +------ rslib/i18n/src/lib.rs | 70 ++++++++++--------------------- rslib/src/backend/i18n.rs | 12 ++++-- rslib/src/i18n.rs | 2 +- rslib/src/prelude.rs | 2 +- 9 files changed, 74 insertions(+), 82 deletions(-) diff --git a/pylib/anki/_backend/__init__.py b/pylib/anki/_backend/__init__.py index 830440bc1..f4665b8a4 100644 --- a/pylib/anki/_backend/__init__.py +++ b/pylib/anki/_backend/__init__.py @@ -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 + ) diff --git a/pylib/anki/_backend/genfluent.py b/pylib/anki/_backend/genfluent.py index 8187646d3..9a47382e0 100644 --- a/pylib/anki/_backend/genfluent.py +++ b/pylib/anki/_backend/genfluent.py @@ -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" diff --git a/qt/tests/test_i18n.py b/qt/tests/test_i18n.py index 93c51c967..008a1b55f 100644 --- a/qt/tests/test_i18n.py +++ b/qt/tests/test_i18n.py @@ -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 枚の復習カード" diff --git a/rslib/backend.proto b/rslib/backend.proto index ae2f9e30f..8a626f92a 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -751,7 +751,8 @@ message TrashMediaFilesIn { } message TranslateStringIn { - int32 key = 2; + uint32 module_index = 1; + uint32 message_index = 2; map args = 3; } diff --git a/rslib/i18n/build/write_strings.rs b/rslib/i18n/build/write_strings.rs index c89cf54d4..219080a03 100644 --- a/rslib/i18n/build/write_strings.rs +++ b/rslib/i18n/build/write_strings.rs @@ -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!( diff --git a/rslib/i18n/src/lib.rs b/rslib/i18n/src/lib.rs index 4fc132f0c..1b93eee8f 100644 --- a/rslib/i18n/src/lib.rs +++ b/rslib/i18n/src/lib.rs @@ -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>, } -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 { - 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) -> Cow<'a, str> { + fn translate<'a>(&'a self, key: &str, args: Option) -> 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" ); } diff --git a/rslib/src/backend/i18n.rs b/rslib/src/backend/i18n.rs index c2a2e6eab..b4e62ee29 100644 --- a/rslib/src/backend/i18n.rs +++ b/rslib/src/backend/i18n.rs @@ -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 { - 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 { diff --git a/rslib/src/i18n.rs b/rslib/src/i18n.rs index b951ab4e9..452e91d12 100644 --- a/rslib/src/i18n.rs +++ b/rslib/src/i18n.rs @@ -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; diff --git a/rslib/src/prelude.rs b/rslib/src/prelude.rs index 949188e98..0def3f554 100644 --- a/rslib/src/prelude.rs +++ b/rslib/src/prelude.rs @@ -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},