mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 07:22:23 -04:00
allow js to request specific i18n modules
Brings the payload on the congrats page with a non-English language down from about 150k to 15k
This commit is contained in:
parent
8920a6f9ea
commit
b57e9be46f
9 changed files with 58 additions and 22 deletions
|
@ -1041,8 +1041,8 @@ table.review-log {{ {revlog_style} }}
|
||||||
def set_wants_abort(self) -> None:
|
def set_wants_abort(self) -> None:
|
||||||
self._backend.set_wants_abort()
|
self._backend.set_wants_abort()
|
||||||
|
|
||||||
def i18n_resources(self) -> bytes:
|
def i18n_resources(self, modules: Sequence[str]) -> bytes:
|
||||||
return self._backend.i18n_resources()
|
return self._backend.i18n_resources(modules=modules)
|
||||||
|
|
||||||
def abort_media_sync(self) -> None:
|
def abort_media_sync(self) -> None:
|
||||||
self._backend.abort_media_sync()
|
self._backend.abort_media_sync()
|
||||||
|
|
|
@ -269,12 +269,17 @@ def congrats_info() -> bytes:
|
||||||
return aqt.mw.col.congrats_info()
|
return aqt.mw.col.congrats_info()
|
||||||
|
|
||||||
|
|
||||||
|
def i18n_resources() -> bytes:
|
||||||
|
args = from_json_bytes(request.data)
|
||||||
|
return aqt.mw.col.i18n_resources(modules=args["modules"])
|
||||||
|
|
||||||
|
|
||||||
post_handlers = {
|
post_handlers = {
|
||||||
"graphData": graph_data,
|
"graphData": graph_data,
|
||||||
"graphPreferences": graph_preferences,
|
"graphPreferences": graph_preferences,
|
||||||
"setGraphPreferences": set_graph_preferences,
|
"setGraphPreferences": set_graph_preferences,
|
||||||
# pylint: disable=unnecessary-lambda
|
# pylint: disable=unnecessary-lambda
|
||||||
"i18nResources": lambda: aqt.mw.col.i18n_resources(),
|
"i18nResources": i18n_resources,
|
||||||
"congratsInfo": congrats_info,
|
"congratsInfo": congrats_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -263,7 +263,7 @@ service MediaService {
|
||||||
service I18nService {
|
service I18nService {
|
||||||
rpc TranslateString(TranslateStringIn) returns (String);
|
rpc TranslateString(TranslateStringIn) returns (String);
|
||||||
rpc FormatTimespan(FormatTimespanIn) returns (String);
|
rpc FormatTimespan(FormatTimespanIn) returns (String);
|
||||||
rpc I18nResources(Empty) returns (Json);
|
rpc I18nResources(I18nResourcesIn) returns (Json);
|
||||||
}
|
}
|
||||||
|
|
||||||
service CollectionService {
|
service CollectionService {
|
||||||
|
@ -773,6 +773,10 @@ message FormatTimespanIn {
|
||||||
Context context = 2;
|
Context context = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message I18nResourcesIn {
|
||||||
|
repeated string modules = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message StudiedTodayMessageIn {
|
message StudiedTodayMessageIn {
|
||||||
uint32 cards = 1;
|
uint32 cards = 1;
|
||||||
double seconds = 2;
|
double seconds = 2;
|
||||||
|
|
|
@ -183,7 +183,6 @@ impl I18n {
|
||||||
pub fn new<S: AsRef<str>>(locale_codes: &[S]) -> Self {
|
pub fn new<S: AsRef<str>>(locale_codes: &[S]) -> Self {
|
||||||
let mut input_langs = vec![];
|
let mut input_langs = vec![];
|
||||||
let mut bundles = Vec::with_capacity(locale_codes.len() + 1);
|
let mut bundles = Vec::with_capacity(locale_codes.len() + 1);
|
||||||
let mut resource_text = vec![];
|
|
||||||
|
|
||||||
for code in locale_codes {
|
for code in locale_codes {
|
||||||
let code = code.as_ref();
|
let code = code.as_ref();
|
||||||
|
@ -210,7 +209,6 @@ impl I18n {
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
if let Some(bundle) = get_bundle_with_extra(&text, Some(lang.clone())) {
|
if let Some(bundle) = get_bundle_with_extra(&text, Some(lang.clone())) {
|
||||||
resource_text.push(text);
|
|
||||||
bundles.push(bundle);
|
bundles.push(bundle);
|
||||||
output_langs.push(lang);
|
output_langs.push(lang);
|
||||||
} else {
|
} else {
|
||||||
|
@ -223,7 +221,6 @@ impl I18n {
|
||||||
let template_lang = "en-US".parse().unwrap();
|
let template_lang = "en-US".parse().unwrap();
|
||||||
let template_text = ftl_localized_text(&template_lang).unwrap();
|
let template_text = ftl_localized_text(&template_lang).unwrap();
|
||||||
let template_bundle = get_bundle_with_extra(&template_text, None).unwrap();
|
let template_bundle = get_bundle_with_extra(&template_text, None).unwrap();
|
||||||
resource_text.push(template_text);
|
|
||||||
bundles.push(template_bundle);
|
bundles.push(template_bundle);
|
||||||
output_langs.push(template_lang);
|
output_langs.push(template_lang);
|
||||||
|
|
||||||
|
@ -238,7 +235,6 @@ impl I18n {
|
||||||
inner: Arc::new(Mutex::new(I18nInner {
|
inner: Arc::new(Mutex::new(I18nInner {
|
||||||
bundles,
|
bundles,
|
||||||
langs: output_langs,
|
langs: output_langs,
|
||||||
resource_text,
|
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,15 +284,35 @@ impl I18n {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return text from configured locales for use with the JS Fluent implementation.
|
/// Return text from configured locales for use with the JS Fluent implementation.
|
||||||
pub fn resources_for_js(&self) -> ResourcesForJavascript {
|
pub fn resources_for_js(&self, desired_modules: &[String]) -> ResourcesForJavascript {
|
||||||
let inner = self.inner.lock().unwrap();
|
let inner = self.inner.lock().unwrap();
|
||||||
|
let resources = get_modules(&inner.langs, desired_modules);
|
||||||
ResourcesForJavascript {
|
ResourcesForJavascript {
|
||||||
langs: inner.langs.iter().map(ToString::to_string).collect(),
|
langs: inner.langs.iter().map(ToString::to_string).collect(),
|
||||||
resources: inner.resource_text.clone(),
|
resources,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_modules(langs: &[LanguageIdentifier], desired_modules: &[String]) -> Vec<String> {
|
||||||
|
langs
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|lang| {
|
||||||
|
let mut buf = String::new();
|
||||||
|
let lang_name = remapped_lang_name(&lang);
|
||||||
|
if let Some(strings) = STRINGS.get(lang_name) {
|
||||||
|
for module_name in desired_modules {
|
||||||
|
if let Some(text) = strings.get(module_name.as_str()) {
|
||||||
|
buf.push_str(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// This temporarily behaves like the older code; in the future we could either
|
/// This temporarily behaves like the older code; in the future we could either
|
||||||
/// access each &str separately, or load them on demand.
|
/// access each &str separately, or load them on demand.
|
||||||
fn ftl_localized_text(lang: &LanguageIdentifier) -> Option<String> {
|
fn ftl_localized_text(lang: &LanguageIdentifier) -> Option<String> {
|
||||||
|
@ -317,9 +333,6 @@ struct I18nInner {
|
||||||
// last element
|
// last element
|
||||||
bundles: Vec<FluentBundle<FluentResource>>,
|
bundles: Vec<FluentBundle<FluentResource>>,
|
||||||
langs: Vec<LanguageIdentifier>,
|
langs: Vec<LanguageIdentifier>,
|
||||||
// fixme: this is a relic from the old implementation, and we could gather
|
|
||||||
// it only when needed in the future
|
|
||||||
resource_text: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple number formatting implementation
|
// Simple number formatting implementation
|
||||||
|
|
|
@ -32,8 +32,8 @@ impl I18nService for Backend {
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn i18n_resources(&self, _input: pb::Empty) -> Result<pb::Json> {
|
fn i18n_resources(&self, input: pb::I18nResourcesIn) -> Result<pb::Json> {
|
||||||
serde_json::to_vec(&self.i18n.resources_for_js())
|
serde_json::to_vec(&self.i18n.resources_for_js(&input.modules))
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
// 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
|
||||||
|
|
||||||
import { getCongratsInfo } from "./lib";
|
import { getCongratsInfo } from "./lib";
|
||||||
import { setupI18n } from "anki/i18n";
|
import { setupI18n, ModuleName } from "anki/i18n";
|
||||||
import { checkNightMode } from "anki/nightmode";
|
import { checkNightMode } from "anki/nightmode";
|
||||||
|
|
||||||
import CongratsPage from "./CongratsPage.svelte";
|
import CongratsPage from "./CongratsPage.svelte";
|
||||||
|
|
||||||
export async function congrats(target: HTMLDivElement): Promise<void> {
|
export async function congrats(target: HTMLDivElement): Promise<void> {
|
||||||
checkNightMode();
|
checkNightMode();
|
||||||
await setupI18n();
|
await setupI18n({ modules: [ModuleName.SCHEDULING] });
|
||||||
const info = await getCongratsInfo();
|
const info = await getCongratsInfo();
|
||||||
new CongratsPage({
|
new CongratsPage({
|
||||||
target,
|
target,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import type { SvelteComponent } from "svelte/internal";
|
import type { SvelteComponent } from "svelte/internal";
|
||||||
|
|
||||||
import { setupI18n } from "anki/i18n";
|
import { setupI18n, ModuleName } from "anki/i18n";
|
||||||
import { checkNightMode } from "anki/nightmode";
|
import { checkNightMode } from "anki/nightmode";
|
||||||
|
|
||||||
import GraphsPage from "./GraphsPage.svelte";
|
import GraphsPage from "./GraphsPage.svelte";
|
||||||
|
@ -33,7 +33,7 @@ export function graphs(
|
||||||
): void {
|
): void {
|
||||||
const nightMode = checkNightMode();
|
const nightMode = checkNightMode();
|
||||||
|
|
||||||
setupI18n().then(() => {
|
setupI18n({ modules: [ModuleName.STATISTICS, ModuleName.SCHEDULING] }).then(() => {
|
||||||
new GraphsPage({
|
new GraphsPage({
|
||||||
target,
|
target,
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -91,11 +91,20 @@ def typescript_arg_name(arg: Variable) -> str:
|
||||||
else:
|
else:
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
def module_names() -> str:
|
||||||
|
buf = "export enum ModuleName {\n"
|
||||||
|
for module in modules:
|
||||||
|
name = module["name"]
|
||||||
|
upper = name.upper()
|
||||||
|
buf += f' {upper} = "{name}",\n'
|
||||||
|
buf += "}\n"
|
||||||
|
return buf
|
||||||
|
|
||||||
|
|
||||||
out = ""
|
out = ""
|
||||||
|
|
||||||
out += methods()
|
out += methods()
|
||||||
|
out += module_names()
|
||||||
|
|
||||||
open(outfile, "wb").write(
|
open(outfile, "wb").write(
|
||||||
(
|
(
|
||||||
|
|
|
@ -70,8 +70,13 @@ export class I18n {
|
||||||
// global singleton
|
// global singleton
|
||||||
export const i18n = new I18n();
|
export const i18n = new I18n();
|
||||||
|
|
||||||
export async function setupI18n(): Promise<void> {
|
import type { ModuleName } from "./i18n";
|
||||||
const resp = await fetch("/_anki/i18nResources", { method: "POST" });
|
|
||||||
|
export async function setupI18n(args: { modules: ModuleName[] }): Promise<void> {
|
||||||
|
const resp = await fetch("/_anki/i18nResources", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(args),
|
||||||
|
});
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
throw Error(`unexpected reply: ${resp.statusText}`);
|
throw Error(`unexpected reply: ${resp.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue