mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
drop the legacy enum from rslib, and pass separate module/message idx
This commit is contained in:
parent
f485efce16
commit
d6b9cc4d9b
9 changed files with 74 additions and 82 deletions
|
@ -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
|
||||||
|
)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 枚の復習カード"
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -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"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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},
|
||||||
|
|
Loading…
Reference in a new issue