diff --git a/pylib/anki/importing/__init__.py b/pylib/anki/importing/__init__.py index 69628cd98..03e645c30 100644 --- a/pylib/anki/importing/__init__.py +++ b/pylib/anki/importing/__init__.py @@ -7,7 +7,7 @@ from anki.importing.csvfile import TextImporter from anki.importing.mnemo import MnemosyneImporter from anki.importing.pauker import PaukerImporter from anki.importing.supermemo_xml import SupermemoXmlImporter # type: ignore -from anki.lang import _, tr_legacyglobal +from anki.lang import tr_legacyglobal from anki.rsbackend import TR Importers = ( diff --git a/pylib/anki/template_legacy.py b/pylib/anki/template_legacy.py index 49410e1db..b2bbea5c4 100644 --- a/pylib/anki/template_legacy.py +++ b/pylib/anki/template_legacy.py @@ -12,8 +12,7 @@ the legacy addHook() API. # import re # from typing import Any, Callable # -# from anki.lang import _ -# from anki.template import ( +# # from anki.template import ( # CLOZE_REGEX_MATCH_GROUP_CONTENT, # CLOZE_REGEX_MATCH_GROUP_HINT, # CLOZE_REGEX_MATCH_GROUP_TAG, diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index e2dd14803..ec5d5d523 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -186,20 +186,14 @@ def setupLangAndBackend( # add _ and ngettext globals used by legacy code def fn__(arg): - print("accessing _ without importing from anki.lang will break in the future") print("".join(traceback.format_stack()[-2])) - from anki.lang import _ - - return _(arg) + print("_ global will break in the future; please see anki/lang.py") + return arg def fn_ngettext(a, b, c): - print( - "accessing ngettext without importing from anki.lang will break in the future" - ) print("".join(traceback.format_stack()[-2])) - from anki.lang import ngettext - - return ngettext(a, b, c) + print("ngettext global will break in the future; please see anki/lang.py") + return b builtins.__dict__["_"] = fn__ builtins.__dict__["ngettext"] = fn_ngettext diff --git a/qt/aqt/about.py b/qt/aqt/about.py index f2470ccc4..73205ad85 100644 --- a/qt/aqt/about.py +++ b/qt/aqt/about.py @@ -5,7 +5,6 @@ import platform import time import aqt.forms -from anki.lang import _ from anki.utils import versionWithBuild from aqt.addons import AddonManager, AddonMeta from aqt.qt import * diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index bd2492757..d6f093b4f 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -8,7 +8,6 @@ import aqt.editor import aqt.forms import aqt.modelchooser from anki.consts import MODEL_CLOZE -from anki.lang import _ from anki.notes import Note from anki.utils import htmlToTextLine, isMac from aqt import AnkiQt, gui_hooks @@ -148,7 +147,7 @@ class AddCards(QDialog): txt = htmlToTextLine(", ".join(fields)) if len(txt) > 30: txt = txt[:30] + "..." - line = _('Edit "%s"') % txt + line = tr(TR.ADDING_EDIT, val=txt) line = gui_hooks.addcards_will_add_history_entry(line, note) a = m.addAction(line) qconnect(a.triggered, lambda b, nid=nid: self.editHistory(nid)) diff --git a/qt/aqt/addons.py b/qt/aqt/addons.py index 8053278be..ef8d18912 100644 --- a/qt/aqt/addons.py +++ b/qt/aqt/addons.py @@ -24,7 +24,7 @@ import anki import aqt import aqt.forms from anki.httpclient import HttpClient -from anki.lang import ngettext, without_unicode_isolation +from anki.lang import without_unicode_isolation from aqt import gui_hooks from aqt.qt import * from aqt.utils import ( diff --git a/qt/aqt/customstudy.py b/qt/aqt/customstudy.py index 11424ab24..7771357f8 100644 --- a/qt/aqt/customstudy.py +++ b/qt/aqt/customstudy.py @@ -3,7 +3,6 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import aqt from anki.consts import * -from anki.lang import _ from aqt.qt import * from aqt.utils import TR, showInfo, showWarning, tr diff --git a/qt/aqt/deckchooser.py b/qt/aqt/deckchooser.py index 90fe10cbb..e5e2139e7 100644 --- a/qt/aqt/deckchooser.py +++ b/qt/aqt/deckchooser.py @@ -3,7 +3,6 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html from typing import Any -from anki.lang import _ from aqt import AnkiQt, gui_hooks from aqt.qt import * from aqt.utils import TR, shortcut, tr diff --git a/qt/aqt/dyndeckconf.py b/qt/aqt/dyndeckconf.py index 3389ebb84..c7a1df695 100644 --- a/qt/aqt/dyndeckconf.py +++ b/qt/aqt/dyndeckconf.py @@ -4,7 +4,6 @@ from typing import List, Optional import aqt -from anki.lang import _ from aqt.qt import * from aqt.utils import TR, askUser, openHelp, restoreGeom, saveGeom, showWarning, tr diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py index cc9ecc6a3..6225a842b 100644 --- a/qt/aqt/editcurrent.py +++ b/qt/aqt/editcurrent.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import aqt.editor -from anki.lang import _ from aqt import gui_hooks from aqt.main import ResetReason from aqt.qt import * diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index e55f5f2a1..38e9aec96 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -22,7 +22,6 @@ import aqt.sound from anki.cards import Card from anki.hooks import runFilter from anki.httpclient import HttpClient -from anki.lang import _ from anki.notes import Note from anki.utils import checksum, isLin, isWin, namedtmp, stripHTMLMedia from aqt import AnkiQt, gui_hooks diff --git a/qt/aqt/errors.py b/qt/aqt/errors.py index c78f527cb..04c08e7d0 100644 --- a/qt/aqt/errors.py +++ b/qt/aqt/errors.py @@ -8,7 +8,6 @@ import traceback from markdown import markdown -from anki.lang import _ from aqt import mw from aqt.qt import * from aqt.utils import TR, showText, showWarning, supportText, tr diff --git a/qt/aqt/forms/build_ui.py b/qt/aqt/forms/build_ui.py index 9eca6b164..3b61ddb09 100644 --- a/qt/aqt/forms/build_ui.py +++ b/qt/aqt/forms/build_ui.py @@ -38,8 +38,7 @@ with open(py_file, "w") as file: # (cat < $py # # -*- coding: utf-8 -*- # # pylint: disable=unsubscriptable-object,unused-import -# from anki.lang import _ -# EOF +# # EOF # rm $py.tmp # fi # done diff --git a/qt/aqt/modelchooser.py b/qt/aqt/modelchooser.py index 6f0dfedc6..ea63e6d60 100644 --- a/qt/aqt/modelchooser.py +++ b/qt/aqt/modelchooser.py @@ -3,7 +3,6 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html from typing import Optional -from anki.lang import _ from aqt import AnkiQt, gui_hooks from aqt.qt import * from aqt.utils import TR, shortcut, tr diff --git a/qt/aqt/overview.py b/qt/aqt/overview.py index 6c635087c..2d0ed63bb 100644 --- a/qt/aqt/overview.py +++ b/qt/aqt/overview.py @@ -7,7 +7,6 @@ from dataclasses import dataclass from typing import Optional import aqt -from anki.lang import _ from aqt import gui_hooks from aqt.sound import av_player from aqt.toolbar import BottomBar diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index a1e2f2090..33ebd4fbd 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -3,7 +3,6 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import anki.lang import aqt -from anki.lang import _ from aqt import AnkiQt from aqt.qt import * from aqt.utils import TR, askUser, openHelp, showInfo, showWarning, tr diff --git a/qt/aqt/previewer.py b/qt/aqt/previewer.py index 136a33c3f..560050d6d 100644 --- a/qt/aqt/previewer.py +++ b/qt/aqt/previewer.py @@ -7,7 +7,6 @@ import time from typing import Any, Callable, Optional, Union from anki.cards import Card -from anki.lang import _ from aqt import AnkiQt, gui_hooks from aqt.qt import ( QAbstractItemView, diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py index 8b8e2f31d..1a4eb41e0 100644 --- a/qt/aqt/profiles.py +++ b/qt/aqt/profiles.py @@ -16,7 +16,7 @@ import aqt.forms import aqt.sound from anki import Collection from anki.db import DB -from anki.lang import _, without_unicode_isolation +from anki.lang import without_unicode_isolation from anki.rsbackend import SyncAuth from anki.utils import intTime, isMac, isWin from aqt import appHelpSite diff --git a/qt/aqt/progress.py b/qt/aqt/progress.py index c54742cae..d86af56d7 100644 --- a/qt/aqt/progress.py +++ b/qt/aqt/progress.py @@ -7,7 +7,6 @@ import time from typing import Optional import aqt.forms -from anki.lang import _ from aqt.qt import * from aqt.utils import TR, tr diff --git a/qt/aqt/sidebar.py b/qt/aqt/sidebar.py index e80ff4383..57e395e87 100644 --- a/qt/aqt/sidebar.py +++ b/qt/aqt/sidebar.py @@ -7,7 +7,6 @@ from enum import Enum import aqt from anki.errors import DeckRenameError -from anki.lang import _ from aqt.qt import * from aqt.utils import TR, getOnlyText, showWarning, tr diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index 212f86e95..d2a172ddb 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -16,7 +16,6 @@ from typing import Any, Callable, Dict, List, Optional, Tuple import aqt from anki import hooks from anki.cards import Card -from anki.lang import _ from anki.sound import AV_REF_RE, AVTag, SoundOrVideoTag from anki.utils import isLin, isMac, isWin from aqt import gui_hooks diff --git a/qt/aqt/stats.py b/qt/aqt/stats.py index 693377a1a..f6376488a 100644 --- a/qt/aqt/stats.py +++ b/qt/aqt/stats.py @@ -1,23 +1,23 @@ # Copyright: Ankitects Pty Ltd and contributors # -*- coding: utf-8 -*- # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - from __future__ import annotations import time import aqt -from anki.lang import _ from aqt import gui_hooks from aqt.qt import * from aqt.theme import theme_manager from aqt.utils import ( + TR, addCloseShortcut, getSaveFile, maybeHideClose, restoreGeom, saveGeom, tooltip, + tr, ) @@ -39,7 +39,9 @@ class NewDeckStats(QDialog): f.groupBox.setVisible(False) f.groupBox_2.setVisible(False) restoreGeom(self, self.name) - b = f.buttonBox.addButton(_("Save PDF"), QDialogButtonBox.ActionRole) + b = f.buttonBox.addButton( + tr(TR.STATISTICS_SAVE_PDF), QDialogButtonBox.ActionRole + ) qconnect(b.clicked, self.saveImage) b.setAutoDefault(False) maybeHideClose(self.form.buttonBox) @@ -61,10 +63,10 @@ class NewDeckStats(QDialog): def _imagePath(self): name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time())) - name = "anki-" + _("stats") + name + name = "anki-" + tr(TR.STATISTICS_STATS) + name file = getSaveFile( self, - title=_("Save PDF"), + title=tr(TR.STATISTICS_SAVE_PDF), dir_description="stats", key="stats", ext=".pdf", @@ -77,7 +79,7 @@ class NewDeckStats(QDialog): if not path: return self.form.web.page().printToPdf(path) - tooltip(_("Saved.")) + tooltip(tr(TR.STATISTICS_SAVED)) def changePeriod(self, n): pass @@ -111,7 +113,9 @@ class DeckStats(QDialog): self.setStyleSheet("QGroupBox { border: 0; }") f.setupUi(self) restoreGeom(self, self.name) - b = f.buttonBox.addButton(_("Save PDF"), QDialogButtonBox.ActionRole) + b = f.buttonBox.addButton( + tr(TR.STATISTICS_SAVE_PDF), QDialogButtonBox.ActionRole + ) qconnect(b.clicked, self.saveImage) b.setAutoDefault(False) qconnect(f.groups.clicked, lambda: self.changeScope("deck")) @@ -139,10 +143,10 @@ class DeckStats(QDialog): def _imagePath(self): name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time())) - name = "anki-" + _("stats") + name + name = "anki-" + tr(TR.STATISTICS_STATS) + name file = getSaveFile( self, - title=_("Save PDF"), + title=tr(TR.STATISTICS_SAVE_PDF), dir_description="stats", key="stats", ext=".pdf", @@ -155,7 +159,7 @@ class DeckStats(QDialog): if not path: return self.form.web.page().printToPdf(path) - tooltip(_("Saved.")) + tooltip(tr(TR.STATISTICS_SAVED)) def changePeriod(self, n): self.period = n diff --git a/qt/aqt/studydeck.py b/qt/aqt/studydeck.py index 0b4d0b400..c0f544408 100644 --- a/qt/aqt/studydeck.py +++ b/qt/aqt/studydeck.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import aqt -from anki.lang import _ from aqt import gui_hooks from aqt.qt import * from aqt.utils import ( diff --git a/qt/aqt/toolbar.py b/qt/aqt/toolbar.py index af4746452..a6585d3e4 100644 --- a/qt/aqt/toolbar.py +++ b/qt/aqt/toolbar.py @@ -6,7 +6,6 @@ from __future__ import annotations from typing import Any, Dict, Optional import aqt -from anki.lang import _ from anki.rsbackend import SyncStatus from aqt import gui_hooks from aqt.qt import * diff --git a/qt/aqt/update.py b/qt/aqt/update.py index 3018fd4b6..d1178350a 100644 --- a/qt/aqt/update.py +++ b/qt/aqt/update.py @@ -6,7 +6,6 @@ import time import requests import aqt -from anki.lang import _ from anki.utils import platDesc, versionWithBuild from aqt.qt import * from aqt.utils import TR, openLink, showText, tr diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index 63b1de775..aa8f52a3a 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -11,7 +11,6 @@ from typing import TYPE_CHECKING, Any, List, Optional, Union import anki import aqt -from anki.lang import _ from anki.rsbackend import TR # pylint: disable=unused-import from anki.utils import invalidFilename, isMac, isWin, noBundledLibs, versionWithBuild from aqt.qt import * diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py index de2363a6d..c4d15f319 100644 --- a/qt/aqt/webview.py +++ b/qt/aqt/webview.py @@ -8,7 +8,7 @@ import sys from typing import Any, Callable, List, Optional, Sequence, Tuple import anki -from anki.lang import _, is_rtl +from anki.lang import is_rtl from anki.utils import isLin, isMac, isWin from aqt import gui_hooks from aqt.qt import * @@ -375,7 +375,7 @@ class AnkiWebView(QWebEngineView): if isWin: # T: include a font for your language on Windows, eg: "Segoe UI", "MS Mincho" - family = _('"Segoe UI"') + family = tr(TR.QT_MISC_SEGOE_UI) button_style = "button { font-family:%s; }" % family button_style += "\n:focus { outline: 1px solid %s; }" % color_hl font = "font-size:12px;font-family:%s;" % family diff --git a/qt/po/scripts/extract-po-strings.py b/qt/po/scripts/extract-po-strings.py index 56df14f70..934ea4581 100644 --- a/qt/po/scripts/extract-po-strings.py +++ b/qt/po/scripts/extract-po-strings.py @@ -99,7 +99,7 @@ module_map = { "update": "qt-misc", "utils": "qt-misc", "webview": "qt-misc", - "stats": "stats", + "stats": "statistics", } text_remap = { @@ -170,22 +170,15 @@ text_remap = { "studying": ["Space"], "qt-misc": ["&Edit", "&Guide...", "&Help", "&Undo", "Unexpected response code: %s"], "adding": ["Added"], - "browsing": ["%d note"], } blacklist = {"Anki", "%", "Dialog", "Center", "Left", "Right", "~", "&Cram..."} def determine_module(text, files): - if not files: - return None if text in blacklist: return None - if text.count("%") > 1: - print("skip", text) - return None - if "&" in text: return "qt-accel" @@ -196,7 +189,6 @@ def determine_module(text, files): if len(files) == 1: return list(files)[0] - print(text, files) assert False @@ -233,26 +225,22 @@ seen_keys = set() def migrate_entry(entry): - if not entry.msgid_plural: + if entry.msgid_plural: + # print("skip plural", entry.msgid) return + entry.occurrences = [e for e in entry.occurrences if "aqt/stats.py" in e[0]] + if not entry.occurrences: + return None + + print(entry.occurrences) text = entry.msgid files = set( [os.path.splitext(os.path.basename(e[0]))[0] for e in entry.occurrences] ) - # drop translations only used by old graphs - if len(files) == 1 and "stats" in files: - return None - - for e in entry.occurrences: - if "importing/__init__.py" in e[0]: - files = ["importing"] - break files2 = set() for file in files: - if file in ("stats", "supermemo_xml"): - continue file = module_map[file] files2.add(file) module = determine_module(text, files2) @@ -267,7 +255,7 @@ def migrate_entry(entry): seen_keys.add(key) modules.setdefault(module, []) - modules[module].append((key, [entry.msgid, entry.msgid_plural])) + modules[module].append((key, text)) return None @@ -316,8 +304,8 @@ for (module, items) in modules.items(): strings_by_module[module] = items for item in items: (key, text) = item - assert text[0] not in keys_by_text - keys_by_text[text[0]] = (module, key) + assert text not in keys_by_text + keys_by_text[text] = (module, key) with open("strings_by_module.json", "w") as file: file.write(json.dumps(strings_by_module)) diff --git a/qt/po/scripts/rewrite-refs.py b/qt/po/scripts/rewrite-refs.py index e8a6c06f7..5746be453 100644 --- a/qt/po/scripts/rewrite-refs.py +++ b/qt/po/scripts/rewrite-refs.py @@ -41,8 +41,6 @@ def repl(m): for file in files: - if file.endswith("stats.py"): - continue buf = open(file).read() buf2 = string_re.sub(repl, buf) if buf != buf2: diff --git a/qt/po/scripts/write-ftls.py b/qt/po/scripts/write-ftls.py index 231ced54b..2df0acc1e 100644 --- a/qt/po/scripts/write-ftls.py +++ b/qt/po/scripts/write-ftls.py @@ -2,9 +2,6 @@ import json import os -import re - -from typing import List from fluent.syntax import parse, serialize from fluent.syntax.ast import Message, TextElement, Identifier, Pattern, Junk @@ -16,8 +13,6 @@ qt_modules = {"about", "qt-accel", "addons", "qt-misc"} modules = json.load(open("strings_by_module.json")) translations = json.load(open("strings.json")) -plurals = json.load(open("plurals.json")) - # # fixme: # addons addons-downloaded-fnames Downloaded %(fname)s @@ -38,43 +33,17 @@ plurals = json.load(open("plurals.json")) # fixme: isolation chars -def plural_text(key, lang, translation): - lang = re.sub("(_|-).*", "", lang) - - var = re.findall(r"{ (\$.*?) }", translation[0]) - try: - var = var[0] - except: - print(key, lang, translation) - raise - - buf = f"{key} = {{ {var} ->\n" - - # for each of the plural forms except the last - for idx, msg in enumerate(translation[:-1]): - plural_form = plurals[lang][idx] - buf += f" [{plural_form}] {msg}\n" - - # add the catchall - msg = translation[-1] - buf += f" *[other] {msg}\n" - buf += " }\n" - return buf - - -def transform_text(key: str, texts: List[str], lang: str) -> str: - texts = [ - ( - text.replace("%d", "{ $count }") - .replace("%s", "{ $count }") - .replace("%(num)d", "{ $count }") - ) - for text in texts - ] - - text = plural_text(key, lang, texts) - print(text) - +def transform_text(text: str) -> str: + # fixme: automatically remap to %s in a compat wrapper? manually fix? + text = ( + text.replace("%d", "{ $val }") + .replace("%s", "{ $val }") + .replace("{}", "{ $val }") + ) + if "Clock drift" in text: + text = text.replace("\n", "
") + else: + text = text.replace("\n", " ") return text @@ -88,28 +57,22 @@ def check_parses(path: str): raise Exception(f"file had junk! {path} {ent}") -def munge_key(key): - if key == "browsing-note": - return "browsing-note-count" - if key == "card-templates-card": - return "card-templates-card-count" - return key - - for module, items in modules.items(): if module in qt_modules: folder = qt else: folder = core + if not module.startswith("statistics"): + continue + # templates path = os.path.join(folder, "templates", module + ".ftl") print(path) with open(path, "a", encoding="utf8") as file: for (key, text) in items: - key = munge_key(key) - text2 = transform_text(key, text, "en") - file.write(text2) + text2 = transform_text(text) + file.write(f"{key} = {text2}\n") check_parses(path) @@ -120,12 +83,8 @@ for module, items in modules.items(): out = [] for (key, text) in items: - if text[0] in map: - forms = map[text[0]] - if isinstance(forms, str): - forms = [forms] - key = munge_key(key) - out.append(transform_text(key, forms, lang)) + if text in map: + out.append((key, transform_text(map[text]))) if out: path = os.path.join(folder, lang, module + ".ftl") @@ -135,7 +94,7 @@ for module, items in modules.items(): print(path) with open(path, "a", encoding="utf8") as file: - for o in out: - file.write(o) + for (key, text) in out: + file.write(f"{key} = {text}\n") check_parses(path) diff --git a/qt/tools/build_ui.sh b/qt/tools/build_ui.sh index 7b5db20c5..b72ca0c4f 100755 --- a/qt/tools/build_ui.sh +++ b/qt/tools/build_ui.sh @@ -33,7 +33,6 @@ do (cat < $py # -*- coding: utf-8 -*- # pylint: disable=unsubscriptable-object,unused-import -from anki.lang import _ EOF rm $py.tmp fi diff --git a/rslib/ftl/statistics.ftl b/rslib/ftl/statistics.ftl index ba50942c5..449f50b74 100644 --- a/rslib/ftl/statistics.ftl +++ b/rslib/ftl/statistics.ftl @@ -199,3 +199,6 @@ statistics-cards-per-day = *[other] { $count } cards/day } statistics-average-ease = Average ease +statistics-save-pdf = Save PDF +statistics-saved = Saved. +statistics-stats = stats