merge pylib references

This commit is contained in:
Damien Elmes 2020-11-17 19:23:06 +10:00
parent 8fcfdb57ab
commit 9d1b6231d7
22 changed files with 160 additions and 92 deletions

View File

@ -1,5 +1,5 @@
[settings] [settings]
skip=aqt/forms,backend_pb2.py,backend_pb2.pyi,fluent_pb2.py,fluent_pb2.pyi,rsbackend_gen.py,hooks_gen.py skip=aqt/forms,backend_pb2.py,backend_pb2.pyi,fluent_pb2.py,fluent_pb2.pyi,rsbackend_gen.py,hooks_gen.py,genbackend.py
profile=black profile=black
multi_line_output=3 multi_line_output=3
include_trailing_comma=True include_trailing_comma=True

View File

@ -22,7 +22,6 @@ from anki.consts import *
from anki.dbproxy import DBProxy from anki.dbproxy import DBProxy
from anki.decks import DeckManager from anki.decks import DeckManager
from anki.errors import AnkiError from anki.errors import AnkiError
from anki.lang import _
from anki.media import MediaManager, media_paths_from_col_path from anki.media import MediaManager, media_paths_from_col_path
from anki.models import ModelManager from anki.models import ModelManager
from anki.notes import Note from anki.notes import Note
@ -574,7 +573,12 @@ table.review-log {{ {revlog_style} }}
old = self._undo[2] old = self._undo[2]
self.clearUndo() self.clearUndo()
wasLeech = card.note().hasTag("leech") or False wasLeech = card.note().hasTag("leech") or False
self._undo = [1, _("Review"), old + [copy.copy(card)], wasLeech] self._undo = [
1,
self.tr(TR.SCHEDULING_REVIEW),
old + [copy.copy(card)],
wasLeech,
]
def _undoReview(self) -> Any: def _undoReview(self) -> Any:
data = self._undo[2] data = self._undo[2]

View File

@ -1,9 +1,12 @@
# 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
from typing import Any, Dict from __future__ import annotations
from anki.lang import _ from typing import Any, Dict, Optional
import anki
from anki.rsbackend import TR
# whether new cards should be mixed with reviews, or shown first or last # whether new cards should be mixed with reviews, or shown first or last
NEW_CARDS_DISTRIBUTE = 0 NEW_CARDS_DISTRIBUTE = 0
@ -88,30 +91,45 @@ REVLOG_CRAM = 3
########################################################################## ##########################################################################
def newCardOrderLabels() -> Dict[int, Any]: def _tr(col: Optional[anki.collection.Collection]):
if col:
return col.tr
else:
print("routine in consts.py should be passed col")
from anki.lang import tr_legacyglobal
return tr_legacyglobal
def newCardOrderLabels(col: Optional[anki.collection.Collection]) -> Dict[int, Any]:
tr = _tr(col)
return { return {
0: _("Show new cards in random order"), 0: tr(TR.SCHEDULING_SHOW_NEW_CARDS_IN_RANDOM_ORDER),
1: _("Show new cards in order added"), 1: tr(TR.SCHEDULING_SHOW_NEW_CARDS_IN_ORDER_ADDED),
} }
def newCardSchedulingLabels() -> Dict[int, Any]: def newCardSchedulingLabels(
col: Optional[anki.collection.Collection],
) -> Dict[int, Any]:
tr = _tr(col)
return { return {
0: _("Mix new cards and reviews"), 0: tr(TR.SCHEDULING_MIX_NEW_CARDS_AND_REVIEWS),
1: _("Show new cards after reviews"), 1: tr(TR.SCHEDULING_SHOW_NEW_CARDS_AFTER_REVIEWS),
2: _("Show new cards before reviews"), 2: tr(TR.SCHEDULING_SHOW_NEW_CARDS_BEFORE_REVIEWS),
} }
def dynOrderLabels() -> Dict[int, Any]: def dynOrderLabels(col: Optional[anki.collection.Collection]) -> Dict[int, Any]:
tr = _tr(col)
return { return {
0: _("Oldest seen first"), 0: tr(TR.DECKS_OLDEST_SEEN_FIRST),
1: _("Random"), 1: tr(TR.DECKS_RANDOM),
2: _("Increasing intervals"), 2: tr(TR.DECKS_INCREASING_INTERVALS),
3: _("Decreasing intervals"), 3: tr(TR.DECKS_DECREASING_INTERVALS),
4: _("Most lapses"), 4: tr(TR.DECKS_MOST_LAPSES),
5: _("Order added"), 5: tr(TR.DECKS_ORDER_ADDED),
6: _("Order due"), 6: tr(TR.DECKS_ORDER_DUE),
7: _("Latest added first"), 7: tr(TR.DECKS_LATEST_ADDED_FIRST),
8: _("Relative overdueness"), 8: tr(TR.DECKS_RELATIVE_OVERDUENESS),
} }

View File

@ -11,8 +11,13 @@ import anki # pylint: disable=unused-import
import anki.backend_pb2 as pb import anki.backend_pb2 as pb
from anki.consts import * from anki.consts import *
from anki.errors import DeckRenameError from anki.errors import DeckRenameError
from anki.lang import _ from anki.rsbackend import (
from anki.rsbackend import DeckTreeNode, NotFoundError, from_json_bytes, to_json_bytes TR,
DeckTreeNode,
NotFoundError,
from_json_bytes,
to_json_bytes,
)
from anki.utils import ids2str, intTime from anki.utils import ids2str, intTime
# legacy code may pass this in as the type argument to .id() # legacy code may pass this in as the type argument to .id()
@ -379,7 +384,7 @@ class DeckManager:
deck = self.get(did, default=default) deck = self.get(did, default=default)
if deck: if deck:
return deck["name"] return deck["name"]
return _("[no deck]") return self.col.tr(TR.DECKS_NO_DECK)
def nameOrNone(self, did: int) -> Optional[str]: def nameOrNone(self, did: int) -> Optional[str]:
deck = self.get(did, default=False) deck = self.get(did, default=False)

View File

@ -13,7 +13,7 @@ from zipfile import ZipFile
from anki import hooks from anki import hooks
from anki.collection import Collection from anki.collection import Collection
from anki.lang import _ from anki.rsbackend import TR
from anki.utils import ids2str, namedtmp, splitFields, stripHTML from anki.utils import ids2str, namedtmp, splitFields, stripHTML
@ -92,12 +92,12 @@ class Exporter:
class TextCardExporter(Exporter): class TextCardExporter(Exporter):
key = lambda self: _("Cards in Plain Text")
ext = ".txt" ext = ".txt"
includeHTML = True includeHTML = True
def __init__(self, col) -> None: def __init__(self, col) -> None:
Exporter.__init__(self, col) Exporter.__init__(self, col)
self.key = col.tr(TR.EXPORTING_CARDS_IN_PLAIN_TEXT)
def doExport(self, file) -> None: def doExport(self, file) -> None:
ids = sorted(self.cardIds()) ids = sorted(self.cardIds())
@ -122,7 +122,6 @@ class TextCardExporter(Exporter):
class TextNoteExporter(Exporter): class TextNoteExporter(Exporter):
key = lambda self: _("Notes in Plain Text")
ext = ".txt" ext = ".txt"
includeTags = True includeTags = True
includeHTML = True includeHTML = True
@ -130,6 +129,7 @@ class TextNoteExporter(Exporter):
def __init__(self, col: Collection) -> None: def __init__(self, col: Collection) -> None:
Exporter.__init__(self, col) Exporter.__init__(self, col)
self.includeID = False self.includeID = False
self.key = col.tr(TR.EXPORTING_NOTES_IN_PLAIN_TEXT)
def doExport(self, file: BufferedWriter) -> None: def doExport(self, file: BufferedWriter) -> None:
cardIds = self.cardIds() cardIds = self.cardIds()
@ -164,13 +164,13 @@ where cards.id in %s)"""
class AnkiExporter(Exporter): class AnkiExporter(Exporter):
key = lambda self: _("Anki 2.0 Deck")
ext = ".anki2" ext = ".anki2"
includeSched: Union[bool, None] = False includeSched: Union[bool, None] = False
includeMedia = True includeMedia = True
def __init__(self, col: Collection) -> None: def __init__(self, col: Collection) -> None:
Exporter.__init__(self, col) Exporter.__init__(self, col)
self.key = col.tr(TR.EXPORTING_ANKI_20_DECK)
def deckIds(self) -> List[int]: def deckIds(self) -> List[int]:
if self.cids: if self.cids:
@ -313,11 +313,11 @@ class AnkiExporter(Exporter):
class AnkiPackageExporter(AnkiExporter): class AnkiPackageExporter(AnkiExporter):
key = lambda self: _("Anki Deck Package")
ext = ".apkg" ext = ".apkg"
def __init__(self, col: Collection) -> None: def __init__(self, col: Collection) -> None:
AnkiExporter.__init__(self, col) AnkiExporter.__init__(self, col)
self.key = col.tr(TR.EXPORTING_ANKI_DECK_PACKAGE)
def exportInto(self, path: str) -> None: def exportInto(self, path: str) -> None:
# open a zip file # open a zip file
@ -395,13 +395,13 @@ class AnkiPackageExporter(AnkiExporter):
class AnkiCollectionPackageExporter(AnkiPackageExporter): class AnkiCollectionPackageExporter(AnkiPackageExporter):
key = lambda self: _("Anki Collection Package")
ext = ".colpkg" ext = ".colpkg"
verbatim = True verbatim = True
includeSched = None includeSched = None
def __init__(self, col): def __init__(self, col):
AnkiPackageExporter.__init__(self, col) AnkiPackageExporter.__init__(self, col)
self.key = col.tr(TR.EXPORTING_ANKI_COLLECTION_PACKAGE)
def doExport(self, z, path): def doExport(self, z, path):
"Export collection. Caller must re-open afterwards." "Export collection. Caller must re-open afterwards."

View File

@ -7,12 +7,16 @@ from anki.importing.csvfile import TextImporter
from anki.importing.mnemo import MnemosyneImporter from anki.importing.mnemo import MnemosyneImporter
from anki.importing.pauker import PaukerImporter from anki.importing.pauker import PaukerImporter
from anki.importing.supermemo_xml import SupermemoXmlImporter # type: ignore from anki.importing.supermemo_xml import SupermemoXmlImporter # type: ignore
from anki.lang import _ from anki.lang import _, tr_legacyglobal
from anki.rsbackend import TR
Importers = ( Importers = (
(_("Text separated by tabs or semicolons (*)"), TextImporter), (tr_legacyglobal(TR.IMPORTING_TEXT_SEPARATED_BY_TABS_OR_SEMICOLONS), TextImporter),
(_("Packaged Anki Deck/Collection (*.apkg *.colpkg *.zip)"), AnkiPackageImporter), (
(_("Mnemosyne 2.0 Deck (*.db)"), MnemosyneImporter), tr_legacyglobal(TR.IMPORTING_PACKAGED_ANKI_DECKCOLLECTION_APKG_COLPKG_ZIP),
(_("Supermemo XML export (*.xml)"), SupermemoXmlImporter), AnkiPackageImporter,
(_("Pauker 1.8 Lesson (*.pau.gz)"), PaukerImporter), ),
(tr_legacyglobal(TR.IMPORTING_MNEMOSYNE_20_DECK_DB), MnemosyneImporter),
(tr_legacyglobal(TR.IMPORTING_SUPERMEMO_XML_EXPORT_XML), SupermemoXmlImporter),
(tr_legacyglobal(TR.IMPORTING_PAUKER_18_LESSON_PAUGZ), PaukerImporter),
) )

View File

@ -9,7 +9,7 @@ from anki.collection import Collection
from anki.consts import * from anki.consts import *
from anki.decks import DeckManager from anki.decks import DeckManager
from anki.importing.base import Importer from anki.importing.base import Importer
from anki.lang import _ from anki.rsbackend import TR
from anki.utils import intTime, joinFields, splitFields from anki.utils import intTime, joinFields, splitFields
GUID = 1 GUID = 1
@ -131,22 +131,25 @@ class Anki2Importer(Importer):
else: else:
dupesIdentical.append(note) dupesIdentical.append(note)
self.log.append(_("Notes found in file: %d") % total) self.log.append(self.dst.tr(TR.IMPORTING_NOTES_FOUND_IN_FILE, val="%s") % total)
if dupesIgnored: if dupesIgnored:
self.log.append( self.log.append(
_("Notes that could not be imported as note type has changed: %d") self.dst.tr(TR.IMPORTING_NOTES_THAT_COULD_NOT_BE_IMPORTED, val="%s")
% len(dupesIgnored) % len(dupesIgnored)
) )
if update: if update:
self.log.append( self.log.append(
_("Notes updated, as file had newer version: %d") % len(update) self.dst.tr(TR.IMPORTING_NOTES_UPDATED_AS_FILE_HAD_NEWER, val="%s")
% len(update)
) )
if add: if add:
self.log.append(_("Notes added from file: %d") % len(add)) self.log.append(
self.dst.tr(TR.IMPORTING_NOTES_ADDED_FROM_FILE, val="%s") % len(add)
)
if dupesIdentical: if dupesIdentical:
self.log.append( self.log.append(
_("Notes skipped, as they're already in your collection: %d") self.dst.tr(TR.IMPORTING_NOTES_SKIPPED_AS_THEYRE_ALREADY_IN, val="%s")
% len(dupesIdentical) % len(dupesIdentical)
) )
@ -154,16 +157,16 @@ class Anki2Importer(Importer):
if dupesIgnored: if dupesIgnored:
for row in dupesIgnored: for row in dupesIgnored:
self._logNoteRow(_("Skipped"), row) self._logNoteRow(self.dst.tr(TR.IMPORTING_SKIPPED), row)
if update: if update:
for row in update: for row in update:
self._logNoteRow(_("Updated"), row) self._logNoteRow(self.dst.tr(TR.IMPORTING_UPDATED), row)
if add: if add:
for row in add: for row in add:
self._logNoteRow(_("Added"), row) self._logNoteRow(self.dst.tr(TR.ADDING_ADDED), row)
if dupesIdentical: if dupesIdentical:
for row in dupesIdentical: for row in dupesIdentical:
self._logNoteRow(_("Identical"), row) self._logNoteRow(self.dst.tr(TR.IMPORTING_IDENTICAL), row)
# export info for calling code # export info for calling code
self.dupes = len(dupesIdentical) self.dupes = len(dupesIdentical)

View File

@ -7,7 +7,7 @@ from typing import Any, List, Optional, TextIO, Union
from anki.collection import Collection from anki.collection import Collection
from anki.importing.noteimp import ForeignNote, NoteImporter from anki.importing.noteimp import ForeignNote, NoteImporter
from anki.lang import _ from anki.rsbackend import TR
class TextImporter(NoteImporter): class TextImporter(NoteImporter):
@ -41,7 +41,9 @@ class TextImporter(NoteImporter):
if len(row) != self.numFields: if len(row) != self.numFields:
if row: if row:
log.append( log.append(
_("'%(row)s' had %(num1)d fields, " "expected %(num2)d") self.col.tr(
TR.IMPORTING_ROWS_HAD_NUM1D_FIELDS_EXPECTED_NUM2D
)
% { % {
"row": " ".join(row), "row": " ".join(row),
"num1": len(row), "num1": len(row),
@ -53,7 +55,7 @@ class TextImporter(NoteImporter):
note = self.noteFromFields(row) note = self.noteFromFields(row)
notes.append(note) notes.append(note)
except (csv.Error) as e: except (csv.Error) as e:
log.append(_("Aborted: %s") % str(e)) log.append(self.col.tr(TR.IMPORTING_ABORTED, val="%s") % str(e))
self.log = log self.log = log
self.ignored = ignored self.ignored = ignored
self.close() self.close()

View File

@ -7,7 +7,8 @@ from typing import cast
from anki.db import DB from anki.db import DB
from anki.importing.noteimp import ForeignCard, ForeignNote, NoteImporter from anki.importing.noteimp import ForeignCard, ForeignNote, NoteImporter
from anki.lang import _, ngettext from anki.lang import ngettext
from anki.rsbackend import TR
from anki.stdmodels import addBasicModel, addClozeModel from anki.stdmodels import addBasicModel, addClozeModel
@ -21,7 +22,9 @@ class MnemosyneImporter(NoteImporter):
db = DB(self.file) db = DB(self.file)
ver = db.scalar("select value from global_variables where key='version'") ver = db.scalar("select value from global_variables where key='version'")
if not ver.startswith("Mnemosyne SQL 1") and ver not in ("2", "3"): if not ver.startswith("Mnemosyne SQL 1") and ver not in ("2", "3"):
self.log.append(_("File version unknown, trying import anyway.")) self.log.append(
self.col.tr(TR.IMPORTING_FILE_VERSION_UNKNOWN_TRYING_IMPORT_ANYWAY)
)
# gather facts into temp objects # gather facts into temp objects
curid = None curid = None
notes = {} notes = {}

View File

@ -7,7 +7,8 @@ from typing import Dict, List, Optional, Tuple, Union
from anki.collection import Collection from anki.collection import Collection
from anki.consts import NEW_CARDS_RANDOM, STARTING_FACTOR from anki.consts import NEW_CARDS_RANDOM, STARTING_FACTOR
from anki.importing.base import Importer from anki.importing.base import Importer
from anki.lang import _, ngettext from anki.lang import ngettext
from anki.rsbackend import TR
from anki.utils import ( from anki.utils import (
fieldChecksum, fieldChecksum,
guid64, guid64,
@ -135,8 +136,10 @@ class NoteImporter(Importer):
# loop through the notes # loop through the notes
updates = [] updates = []
updateLog = [] updateLog = []
updateLogTxt = _("First field matched: %s") updateLogTxt = self.col.tr(TR.IMPORTING_FIRST_FIELD_MATCHED, val="%s")
dupeLogTxt = _("Added duplicate with first field: %s") dupeLogTxt = self.col.tr(
TR.IMPORTING_ADDED_DUPLICATE_WITH_FIRST_FIELD, val="%s"
)
new = [] new = []
self._ids: List[int] = [] self._ids: List[int] = []
self._cards: List[Tuple] = [] self._cards: List[Tuple] = []
@ -153,12 +156,17 @@ class NoteImporter(Importer):
csum = fieldChecksum(fld0) csum = fieldChecksum(fld0)
# first field must exist # first field must exist
if not fld0: if not fld0:
self.log.append(_("Empty first field: %s") % " ".join(n.fields)) self.log.append(
self.col.tr(TR.IMPORTING_EMPTY_FIRST_FIELD, val="%s")
% " ".join(n.fields)
)
continue continue
# earlier in import? # earlier in import?
if fld0 in firsts and self.importMode != ADD_MODE: if fld0 in firsts and self.importMode != ADD_MODE:
# duplicates in source file; log and ignore # duplicates in source file; log and ignore
self.log.append(_("Appeared twice in file: %s") % fld0) self.log.append(
self.col.tr(TR.IMPORTING_APPEARED_TWICE_IN_FILE, val="%s") % fld0
)
continue continue
firsts[fld0] = True firsts[fld0] = True
# already exists? # already exists?

View File

@ -148,7 +148,7 @@ current_catalog: Optional[
] = None ] = None
# the current Fluent translation instance # the current Fluent translation instance
current_i18n: Optional[anki.rsbackend.RustBackend] current_i18n: Optional[anki.rsbackend.RustBackend] = None
# path to locale folder # path to locale folder
locale_folder = "" locale_folder = ""
@ -161,6 +161,14 @@ def _(str: str) -> str:
return str return str
def tr_legacyglobal(*args, **kwargs) -> str:
"Should use col.tr() instead."
if current_i18n:
return current_i18n.translate(*args, **kwargs)
else:
return "tr_legacyglobal() called without active backend"
def ngettext(single: str, plural: str, n: int) -> str: def ngettext(single: str, plural: str, n: int) -> str:
if current_catalog: if current_catalog:
return current_catalog.ngettext(single, plural, n) return current_catalog.ngettext(single, plural, n)

View File

@ -11,9 +11,8 @@ from typing import Any, List, Optional, Tuple
import anki import anki
from anki import hooks from anki import hooks
from anki.lang import _
from anki.models import NoteType from anki.models import NoteType
from anki.rsbackend import pb from anki.rsbackend import TR, pb
from anki.template import TemplateRenderContext, TemplateRenderOutput from anki.template import TemplateRenderContext, TemplateRenderOutput
from anki.utils import call, isMac, namedtmp, tmpdir from anki.utils import call, isMac, namedtmp, tmpdir
@ -129,15 +128,7 @@ def _save_latex_image(
# don't mind if the sequence is only part of a command # don't mind if the sequence is only part of a command
bad_re = "\\" + bad + "[^a-zA-Z]" bad_re = "\\" + bad + "[^a-zA-Z]"
if re.search(bad_re, tmplatex): if re.search(bad_re, tmplatex):
return ( return col.tr(TR.MEDIA_FOR_SECURITY_REASONS_IS_NOT, val=bad)
_(
"""\
For security reasons, '%s' is not allowed on cards. You can still use \
it by placing the command in a different package, and importing that \
package in the LaTeX header instead."""
)
% bad
)
# commands to use # commands to use
if svg: if svg:
@ -160,7 +151,7 @@ package in the LaTeX header instead."""
os.chdir(tmpdir()) os.chdir(tmpdir())
for latexCmd in latexCmds: for latexCmd in latexCmds:
if call(latexCmd, stdout=log, stderr=log): if call(latexCmd, stdout=log, stderr=log):
return _errMsg(latexCmd[0], texpath) return _errMsg(col, latexCmd[0], texpath)
# add to media # add to media
with open(png_or_svg, "rb") as file: with open(png_or_svg, "rb") as file:
data = file.read() data = file.read()
@ -172,9 +163,9 @@ package in the LaTeX header instead."""
log.close() log.close()
def _errMsg(type: str, texpath: str) -> Any: def _errMsg(col: anki.collection.Collection, type: str, texpath: str) -> Any:
msg = (_("Error executing %s.") % type) + "<br>" msg = (col.tr(TR.MEDIA_ERROR_EXECUTING, val="%s") % type) + "<br>"
msg += (_("Generated file: %s") % texpath) + "<br>" msg += (col.tr(TR.MEDIA_GENERATED_FILE, val="%s") % texpath) + "<br>"
try: try:
with open(namedtmp("latex_log.txt", rm=False)) as f: with open(namedtmp("latex_log.txt", rm=False)) as f:
log = f.read() log = f.read()
@ -182,7 +173,7 @@ def _errMsg(type: str, texpath: str) -> Any:
raise Exception() raise Exception()
msg += "<small><pre>" + html.escape(log) + "</pre></small>" msg += "<small><pre>" + html.escape(log) + "</pre></small>"
except: except:
msg += _("Have you installed latex and dvipng/dvisvgm?") msg += col.tr(TR.MEDIA_HAVE_YOU_INSTALLED_LATEX_AND_DVIPNGDVISVGM)
return msg return msg

View File

@ -11,8 +11,14 @@ from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
import anki # pylint: disable=unused-import import anki # pylint: disable=unused-import
import anki.backend_pb2 as pb import anki.backend_pb2 as pb
from anki.consts import * from anki.consts import *
from anki.lang import _ from anki.lang import without_unicode_isolation
from anki.rsbackend import NotFoundError, StockNoteType, from_json_bytes, to_json_bytes from anki.rsbackend import (
TR,
NotFoundError,
StockNoteType,
from_json_bytes,
to_json_bytes,
)
from anki.utils import checksum, ids2str, intTime, joinFields, splitFields from anki.utils import checksum, ids2str, intTime, joinFields, splitFields
# types # types
@ -260,7 +266,9 @@ class ModelManager:
def copy(self, m: NoteType) -> NoteType: def copy(self, m: NoteType) -> NoteType:
"Copy, save and return." "Copy, save and return."
m2 = copy.deepcopy(m) m2 = copy.deepcopy(m)
m2["name"] = _("%s copy") % m2["name"] m2["name"] = without_unicode_isolation(
self.col.tr(TR.NOTETYPES_COPY, val=m2["name"])
)
m2["id"] = 0 m2["id"] = 0
self.add(m2) self.add(m2)
return m2 return m2

View File

@ -25,9 +25,9 @@ from anki import hooks
from anki.cards import Card from anki.cards import Card
from anki.consts import * from anki.consts import *
from anki.decks import Deck, DeckConfig, DeckManager, QueueConfig from anki.decks import Deck, DeckConfig, DeckManager, QueueConfig
from anki.lang import _
from anki.notes import Note from anki.notes import Note
from anki.rsbackend import ( from anki.rsbackend import (
TR,
CountsForDeckToday, CountsForDeckToday,
DeckTreeNode, DeckTreeNode,
FormatTimeSpanContext, FormatTimeSpanContext,
@ -1258,7 +1258,7 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe
"Return the next interval for CARD as a string." "Return the next interval for CARD as a string."
ivl_secs = self.nextIvl(card, ease) ivl_secs = self.nextIvl(card, ease)
if not ivl_secs: if not ivl_secs:
return _("(end)") return self.col.tr(TR.SCHEDULING_END)
s = self.col.format_timespan(ivl_secs, FormatTimeSpanContext.ANSWER_BUTTONS) s = self.col.format_timespan(ivl_secs, FormatTimeSpanContext.ANSWER_BUTTONS)
if ivl_secs < self.col.conf["collapseTime"]: if ivl_secs < self.col.conf["collapseTime"]:
s = "<" + s s = "<" + s

View File

@ -3,6 +3,7 @@
import os import os
import shutil import shutil
from anki.lang import without_unicode_isolation
from tests.shared import getEmptyCol from tests.shared import getEmptyCol
@ -20,7 +21,8 @@ def test_latex():
assert len(os.listdir(col.media.dir())) == 0 assert len(os.listdir(col.media.dir())) == 0
# check the error message # check the error message
msg = note.cards()[0].q() msg = note.cards()[0].q()
assert "executing nolatex" in msg print(msg)
assert "executing nolatex" in without_unicode_isolation(msg)
assert "installed" in msg assert "installed" in msg
# check if we have latex installed, and abort test if we don't # check if we have latex installed, and abort test if we don't
if not shutil.which("latex") or not shutil.which("dvipng"): if not shutil.which("latex") or not shutil.which("dvipng"):
@ -91,5 +93,5 @@ def _test_includes_bad_command(bad):
note = col.newNote() note = col.newNote()
note["Front"] = "[latex]%s[/latex]" % bad note["Front"] = "[latex]%s[/latex]" % bad
col.addNote(note) col.addNote(note)
q = note.cards()[0].q() q = without_unicode_isolation(note.cards()[0].q())
return ("'%s' is not allowed on cards" % bad in q, "Card content: %s" % q) return ("'%s' is not allowed on cards" % bad in q, "Card content: %s" % q)

View File

@ -5,10 +5,10 @@ import os
import re import re
import sys import sys
import stringcase
import pylib.anki.backend_pb2 as pb import pylib.anki.backend_pb2 as pb
import stringcase
TYPE_DOUBLE = 1 TYPE_DOUBLE = 1
TYPE_FLOAT = 2 TYPE_FLOAT = 2
TYPE_INT64 = 3 TYPE_INT64 = 3

View File

@ -57,7 +57,7 @@ class DeckConf(QDialog):
import anki.consts as cs import anki.consts as cs
f = self.form f = self.form
f.newOrder.addItems(list(cs.newCardOrderLabels().values())) f.newOrder.addItems(list(cs.newCardOrderLabels(self.mw.col).values()))
qconnect(f.newOrder.currentIndexChanged, self.onNewOrderChanged) qconnect(f.newOrder.currentIndexChanged, self.onNewOrderChanged)
# Conf list # Conf list

View File

@ -44,8 +44,8 @@ class DeckConf(QDialog):
def initialSetup(self): def initialSetup(self):
import anki.consts as cs import anki.consts as cs
self.form.order.addItems(list(cs.dynOrderLabels().values())) self.form.order.addItems(list(cs.dynOrderLabels(self.mw.col).values()))
self.form.order_2.addItems(list(cs.dynOrderLabels().values())) self.form.order_2.addItems(list(cs.dynOrderLabels(self.mw.col).values()))
qconnect(self.form.resched.stateChanged, self._onReschedToggled) qconnect(self.form.resched.stateChanged, self._onReschedToggled)

View File

@ -86,7 +86,7 @@ class Preferences(QDialog):
else: else:
f.hwAccel.setChecked(self.mw.pm.glMode() != "software") f.hwAccel.setChecked(self.mw.pm.glMode() != "software")
f.newSpread.addItems(list(c.newCardSchedulingLabels().values())) f.newSpread.addItems(list(c.newCardSchedulingLabels(self.mw.col).values()))
f.useCurrent.setCurrentIndex(int(not qc.get("addToCur", True))) f.useCurrent.setCurrentIndex(int(not qc.get("addToCur", True)))

View File

@ -237,6 +237,11 @@ def migrate_entry(entry):
if len(files) == 1 and "stats" in files: if len(files) == 1 and "stats" in files:
return None return None
for e in entry.occurrences:
if "importing/__init__.py" in e[0]:
files = ["importing"]
break
files2 = set() files2 = set()
for file in files: for file in files:
if file == "stats": if file == "stats":

View File

@ -3,8 +3,8 @@
import glob, re, json, stringcase import glob, re, json, stringcase
files = ( files = (
# glob.glob("../../pylib/**/*.py", recursive=True) + glob.glob("../../pylib/**/*.py", recursive=True)
glob.glob("../../qt/**/*.py", recursive=True) # glob.glob("../../qt/**/*.py", recursive=True)
) )
string_re = re.compile(r'_\(\s*(".*?")\s*\)') string_re = re.compile(r'_\(\s*(".*?")\s*\)')
@ -15,6 +15,7 @@ blacklist = {
"Label1", "Label1",
"After pressing OK, you can choose which tags to include.", "After pressing OK, you can choose which tags to include.",
"Filter/Cram", "Filter/Cram",
"Show %s",
# previewer.py needs updating to fix these # previewer.py needs updating to fix these
"Shortcut key: R", "Shortcut key: R",
"Shortcut key: B", "Shortcut key: B",
@ -34,9 +35,9 @@ def repl(m):
if "%d" in text or "%s" in text: if "%d" in text or "%s" in text:
# replace { $val } with %s for compat with old code # replace { $val } with %s for compat with old code
return f'tr(TR.{screaming}, val="%s")' return f'tr_legacyglobal(TR.{screaming}, val="%s")'
return f"tr(TR.{screaming})" return f"tr_legacyglobal(TR.{screaming})"
for file in files: for file in files:
@ -46,6 +47,7 @@ for file in files:
buf2 = string_re.sub(repl, buf) buf2 = string_re.sub(repl, buf)
if buf != buf2: if buf != buf2:
lines = buf2.split("\n") lines = buf2.split("\n")
lines.insert(3, "from aqt.utils import tr, TR") lines.insert(3, "from anki.rsbackend import TR")
lines.insert(3, "from anki.lang import tr_legacyglobal")
buf2 = "\n".join(lines) buf2 = "\n".join(lines)
open(file, "w").write(buf2) open(file, "w").write(buf2)

View File

@ -27,18 +27,23 @@ importing-map-to = Map to { $val }
importing-map-to-tags = Map to Tags importing-map-to-tags = Map to Tags
importing-mapped-to = mapped to <b>{ $val }</b> importing-mapped-to = mapped to <b>{ $val }</b>
importing-mapped-to-tags = mapped to <b>Tags</b> importing-mapped-to-tags = mapped to <b>Tags</b>
importing-mnemosyne-20-deck-db = Mnemosyne 2.0 Deck (*.db)
importing-multicharacter-separators-are-not-supported-please = Multi-character separators are not supported. Please enter one character only. importing-multicharacter-separators-are-not-supported-please = Multi-character separators are not supported. Please enter one character only.
importing-notes-added-from-file = Notes added from file: { $val } importing-notes-added-from-file = Notes added from file: { $val }
importing-notes-found-in-file = Notes found in file: { $val } importing-notes-found-in-file = Notes found in file: { $val }
importing-notes-skipped-as-theyre-already-in = Notes skipped, as they're already in your collection: { $val } importing-notes-skipped-as-theyre-already-in = Notes skipped, as they're already in your collection: { $val }
importing-notes-that-could-not-be-imported = Notes that could not be imported as note type has changed: { $val } importing-notes-that-could-not-be-imported = Notes that could not be imported as note type has changed: { $val }
importing-notes-updated-as-file-had-newer = Notes updated, as file had newer version: { $val } importing-notes-updated-as-file-had-newer = Notes updated, as file had newer version: { $val }
importing-packaged-anki-deckcollection-apkg-colpkg-zip = Packaged Anki Deck/Collection (*.apkg *.colpkg *.zip)
importing-pauker-18-lesson-paugz = Pauker 1.8 Lesson (*.pau.gz)
importing-rows-had-num1d-fields-expected-num2d = '%(row)s' had %(num1)d fields, expected %(num2)d importing-rows-had-num1d-fields-expected-num2d = '%(row)s' had %(num1)d fields, expected %(num2)d
importing-selected-file-was-not-in-utf8 = Selected file was not in UTF-8 format. Please see the importing section of the manual. importing-selected-file-was-not-in-utf8 = Selected file was not in UTF-8 format. Please see the importing section of the manual.
importing-semicolon = Semicolon importing-semicolon = Semicolon
importing-skipped = Skipped importing-skipped = Skipped
importing-supermemo-xml-export-xml = Supermemo XML export (*.xml)
importing-tab = Tab importing-tab = Tab
importing-tag-modified-notes = Tag modified notes: importing-tag-modified-notes = Tag modified notes:
importing-text-separated-by-tabs-or-semicolons = Text separated by tabs or semicolons (*)
importing-the-first-field-of-the-note = The first field of the note type must be mapped. importing-the-first-field-of-the-note = The first field of the note type must be mapped.
importing-the-provided-file-is-not-a = The provided file is not a valid .apkg file. importing-the-provided-file-is-not-a = The provided file is not a valid .apkg file.
importing-this-file-does-not-appear-to = This file does not appear to be a valid .apkg file. If you're getting this error from a file downloaded from AnkiWeb, chances are that your download failed. Please try again, and if the problem persists, please try again with a different browser. importing-this-file-does-not-appear-to = This file does not appear to be a valid .apkg file. If you're getting this error from a file downloaded from AnkiWeb, chances are that your download failed. Please try again, and if the problem persists, please try again with a different browser.