mirror of
https://github.com/ankitects/anki.git
synced 2025-12-13 23:00:58 -05:00
merge bulk of qt/ - designer files still to do
This commit is contained in:
parent
6a5c397866
commit
6418993840
49 changed files with 1094 additions and 545 deletions
|
|
@ -9,7 +9,7 @@ from anki.lang import _
|
|||
from anki.utils import versionWithBuild
|
||||
from aqt.addons import AddonManager, AddonMeta
|
||||
from aqt.qt import *
|
||||
from aqt.utils import supportText, tooltip
|
||||
from aqt.utils import TR, supportText, tooltip, tr
|
||||
|
||||
|
||||
class ClosableQDialog(QDialog):
|
||||
|
|
@ -83,9 +83,9 @@ def show(mw):
|
|||
"""
|
||||
info = " " + " ".join(info.splitlines(True))
|
||||
QApplication.clipboard().setText(info)
|
||||
tooltip(_("Copied to clipboard"), parent=dialog)
|
||||
tooltip(tr(TR.ABOUT_COPIED_TO_CLIPBOARD), parent=dialog)
|
||||
|
||||
btn = QPushButton(_("Copy Debug Info"))
|
||||
btn = QPushButton(tr(TR.ABOUT_COPY_DEBUG_INFO))
|
||||
qconnect(btn.clicked, onCopy)
|
||||
abt.buttonBox.addButton(btn, QDialogButtonBox.ActionRole)
|
||||
abt.buttonBox.button(QDialogButtonBox.Ok).setFocus()
|
||||
|
|
@ -101,13 +101,13 @@ system. It's free and open source."
|
|||
"Anki is licensed under the AGPL3 license. Please see "
|
||||
"the license file in the source distribution for more information."
|
||||
)
|
||||
abouttext += "<p>" + _("Version %s") % versionWithBuild() + "<br>"
|
||||
abouttext += "<p>" + tr(TR.ABOUT_VERSION, val="%s") % versionWithBuild() + "<br>"
|
||||
abouttext += ("Python %s Qt %s PyQt %s<br>") % (
|
||||
platform.python_version(),
|
||||
QT_VERSION_STR,
|
||||
PYQT_VERSION_STR,
|
||||
)
|
||||
abouttext += (_("<a href='%s'>Visit website</a>") % aqt.appWebsite) + "</span>"
|
||||
abouttext += (tr(TR.ABOUT_VISIT_WEBSITE, val="%s") % aqt.appWebsite) + "</span>"
|
||||
|
||||
# automatically sorted; add new lines at the end
|
||||
allusers = sorted(
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from aqt.main import ResetReason
|
|||
from aqt.qt import *
|
||||
from aqt.sound import av_player
|
||||
from aqt.utils import (
|
||||
TR,
|
||||
addCloseShortcut,
|
||||
askUser,
|
||||
downArrow,
|
||||
|
|
@ -25,6 +26,7 @@ from aqt.utils import (
|
|||
shortcut,
|
||||
showWarning,
|
||||
tooltip,
|
||||
tr,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -35,7 +37,7 @@ class AddCards(QDialog):
|
|||
self.mw = mw
|
||||
self.form = aqt.forms.addcards.Ui_Dialog()
|
||||
self.form.setupUi(self)
|
||||
self.setWindowTitle(_("Add"))
|
||||
self.setWindowTitle(tr(TR.ACTIONS_ADD))
|
||||
self.setMinimumHeight(300)
|
||||
self.setMinimumWidth(400)
|
||||
self.setupChoosers()
|
||||
|
|
@ -67,26 +69,26 @@ class AddCards(QDialog):
|
|||
bb = self.form.buttonBox
|
||||
ar = QDialogButtonBox.ActionRole
|
||||
# add
|
||||
self.addButton = bb.addButton(_("Add"), ar)
|
||||
self.addButton = bb.addButton(tr(TR.ACTIONS_ADD), ar)
|
||||
qconnect(self.addButton.clicked, self.addCards)
|
||||
self.addButton.setShortcut(QKeySequence("Ctrl+Return"))
|
||||
self.addButton.setToolTip(shortcut(_("Add (shortcut: ctrl+enter)")))
|
||||
self.addButton.setToolTip(shortcut(tr(TR.ADDING_ADD_SHORTCUT_CTRLANDENTER)))
|
||||
# close
|
||||
self.closeButton = QPushButton(_("Close"))
|
||||
self.closeButton = QPushButton(tr(TR.ACTIONS_CLOSE))
|
||||
self.closeButton.setAutoDefault(False)
|
||||
bb.addButton(self.closeButton, QDialogButtonBox.RejectRole)
|
||||
# help
|
||||
self.helpButton = QPushButton(_("Help"), clicked=self.helpRequested) # type: ignore
|
||||
self.helpButton = QPushButton(tr(TR.ACTIONS_HELP), clicked=self.helpRequested) # type: ignore
|
||||
self.helpButton.setAutoDefault(False)
|
||||
bb.addButton(self.helpButton, QDialogButtonBox.HelpRole)
|
||||
# history
|
||||
b = bb.addButton(_("History") + " " + downArrow(), ar)
|
||||
b = bb.addButton(tr(TR.ADDING_HISTORY) + " " + downArrow(), ar)
|
||||
if isMac:
|
||||
sc = "Ctrl+Shift+H"
|
||||
else:
|
||||
sc = "Ctrl+H"
|
||||
b.setShortcut(QKeySequence(sc))
|
||||
b.setToolTip(_("Shortcut: %s") % shortcut(sc))
|
||||
b.setToolTip(tr(TR.ADDING_SHORTCUT, val="%s") % shortcut(sc))
|
||||
qconnect(b.clicked, self.onHistory)
|
||||
b.setEnabled(False)
|
||||
self.historyButton = b
|
||||
|
|
@ -151,7 +153,7 @@ class AddCards(QDialog):
|
|||
a = m.addAction(line)
|
||||
qconnect(a.triggered, lambda b, nid=nid: self.editHistory(nid))
|
||||
else:
|
||||
a = m.addAction(_("(Note deleted)"))
|
||||
a = m.addAction(tr(TR.ADDING_NOTE_DELETED))
|
||||
a.setEnabled(False)
|
||||
gui_hooks.add_cards_will_show_history_menu(self, m)
|
||||
m.exec_(self.historyButton.mapToGlobal(QPoint(0, 0)))
|
||||
|
|
@ -166,7 +168,7 @@ class AddCards(QDialog):
|
|||
ret = note.dupeOrEmpty()
|
||||
problem = None
|
||||
if ret == 1:
|
||||
problem = _("The first field is empty.")
|
||||
problem = tr(TR.ADDING_THE_FIRST_FIELD_IS_EMPTY)
|
||||
problem = gui_hooks.add_cards_will_add_note(problem, note)
|
||||
if problem is not None:
|
||||
showWarning(problem, help="AddItems#AddError")
|
||||
|
|
@ -199,7 +201,7 @@ class AddCards(QDialog):
|
|||
# workaround for PyQt focus bug
|
||||
self.editor.hideCompleters()
|
||||
|
||||
tooltip(_("Added"), period=500)
|
||||
tooltip(tr(TR.ADDING_ADDED), period=500)
|
||||
av_player.stop_and_clear_queue()
|
||||
self.onReset(keep=True)
|
||||
self.mw.col.autosave()
|
||||
|
|
@ -229,7 +231,7 @@ class AddCards(QDialog):
|
|||
def ifCanClose(self, onOk: Callable) -> None:
|
||||
def afterSave():
|
||||
ok = self.editor.fieldsAreBlank(self.previousNote) or askUser(
|
||||
_("Close and lose current input?"), defaultno=True
|
||||
tr(TR.ADDING_CLOSE_AND_LOSE_CURRENT_INPUT), defaultno=True
|
||||
)
|
||||
if ok:
|
||||
onOk()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# 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 io
|
||||
|
|
@ -310,7 +309,7 @@ and have been disabled: %(found)s"
|
|||
meta = self.addon_meta(dir)
|
||||
name = meta.human_name()
|
||||
if not meta.enabled:
|
||||
name += _(" (disabled)")
|
||||
name += tr(TR.ADDONS_DISABLED)
|
||||
return name
|
||||
|
||||
# Conflict resolution
|
||||
|
|
@ -423,10 +422,7 @@ and have been disabled: %(found)s"
|
|||
return True
|
||||
except OSError as e:
|
||||
showWarning(
|
||||
_(
|
||||
"Unable to update or delete add-on. Please start Anki while holding down the shift key to temporarily disable add-ons, then try again.\n\nDebug info: %s"
|
||||
)
|
||||
% e,
|
||||
tr(TR.ADDONS_UNABLE_TO_UPDATE_OR_DELETE_ADDON, val="%s") % e,
|
||||
textFormat="plain",
|
||||
)
|
||||
return False
|
||||
|
|
@ -468,16 +464,16 @@ and have been disabled: %(found)s"
|
|||
) -> List[str]:
|
||||
|
||||
messages = {
|
||||
"zip": _("Corrupt add-on file."),
|
||||
"manifest": _("Invalid add-on manifest."),
|
||||
"zip": tr(TR.ADDONS_CORRUPT_ADDON_FILE),
|
||||
"manifest": tr(TR.ADDONS_INVALID_ADDON_MANIFEST),
|
||||
}
|
||||
|
||||
msg = messages.get(result.errmsg, _("Unknown error: {}".format(result.errmsg)))
|
||||
|
||||
if mode == "download": # preserve old format strings for i18n
|
||||
template = _("Error downloading <i>%(id)s</i>: %(error)s")
|
||||
template = tr(TR.ADDONS_ERROR_DOWNLOADING_IDS_ERRORS)
|
||||
else:
|
||||
template = _("Error installing <i>%(base)s</i>: %(error)s")
|
||||
template = tr(TR.ADDONS_ERROR_INSTALLING_BASES_ERRORS)
|
||||
|
||||
name = base
|
||||
|
||||
|
|
@ -488,24 +484,22 @@ and have been disabled: %(found)s"
|
|||
) -> List[str]:
|
||||
|
||||
if mode == "download": # preserve old format strings for i18n
|
||||
template = _("Downloaded %(fname)s")
|
||||
template = tr(TR.ADDONS_DOWNLOADED_FNAMES)
|
||||
else:
|
||||
template = _("Installed %(name)s")
|
||||
template = tr(TR.ADDONS_INSTALLED_NAMES)
|
||||
|
||||
name = result.name or base
|
||||
strings = [template % dict(name=name, fname=name)]
|
||||
|
||||
if result.conflicts:
|
||||
strings.append(
|
||||
_("The following conflicting add-ons were disabled:")
|
||||
tr(TR.ADDONS_THE_FOLLOWING_CONFLICTING_ADDONS_WERE_DISABLED)
|
||||
+ " "
|
||||
+ ", ".join(self.addonName(f) for f in result.conflicts)
|
||||
)
|
||||
|
||||
if not result.compatible:
|
||||
strings.append(
|
||||
_("This add-on is not compatible with your version of Anki.")
|
||||
)
|
||||
strings.append(tr(TR.ADDONS_THIS_ADDON_IS_NOT_COMPATIBLE_WITH))
|
||||
|
||||
return strings
|
||||
|
||||
|
|
@ -743,9 +737,13 @@ class AddonsDialog(QDialog):
|
|||
name = addon.human_name()
|
||||
|
||||
if not addon.enabled:
|
||||
return name + " " + _("(disabled)")
|
||||
return name + " " + tr(TR.ADDONS_DISABLED2)
|
||||
elif not addon.compatible():
|
||||
return name + " " + _("(requires %s)") % self.compatible_string(addon)
|
||||
return (
|
||||
name
|
||||
+ " "
|
||||
+ tr(TR.ADDONS_REQUIRES, val="%s") % self.compatible_string(addon)
|
||||
)
|
||||
|
||||
return name
|
||||
|
||||
|
|
@ -804,7 +802,7 @@ class AddonsDialog(QDialog):
|
|||
def onlyOneSelected(self) -> Optional[str]:
|
||||
dirs = self.selectedAddons()
|
||||
if len(dirs) != 1:
|
||||
showInfo(_("Please select a single add-on first."))
|
||||
showInfo(tr(TR.ADDONS_PLEASE_SELECT_A_SINGLE_ADDON_FIRST))
|
||||
return None
|
||||
return dirs[0]
|
||||
|
||||
|
|
@ -820,7 +818,7 @@ class AddonsDialog(QDialog):
|
|||
if re.match(r"^\d+$", addon):
|
||||
openLink(aqt.appShared + "info/{}".format(addon))
|
||||
else:
|
||||
showWarning(_("Add-on was not downloaded from AnkiWeb."))
|
||||
showWarning(tr(TR.ADDONS_ADDON_WAS_NOT_DOWNLOADED_FROM_ANKIWEB))
|
||||
|
||||
def onViewFiles(self) -> None:
|
||||
# if nothing selected, open top level folder
|
||||
|
|
@ -865,13 +863,13 @@ class AddonsDialog(QDialog):
|
|||
if log:
|
||||
show_log_to_user(self, log)
|
||||
else:
|
||||
tooltip(_("No updates available."))
|
||||
tooltip(tr(TR.ADDONS_NO_UPDATES_AVAILABLE))
|
||||
|
||||
def onInstallFiles(self, paths: Optional[List[str]] = None) -> Optional[bool]:
|
||||
if not paths:
|
||||
key = _("Packaged Anki Add-on") + " (*{})".format(self.mgr.ext)
|
||||
key = tr(TR.ADDONS_PACKAGED_ANKI_ADDON) + " (*{})".format(self.mgr.ext)
|
||||
paths = getFile(
|
||||
self, _("Install Add-on(s)"), None, key, key="addons", multi=True
|
||||
self, tr(TR.ADDONS_INSTALL_ADDONS), None, key, key="addons", multi=True
|
||||
)
|
||||
if not paths:
|
||||
return False
|
||||
|
|
@ -882,7 +880,7 @@ class AddonsDialog(QDialog):
|
|||
return None
|
||||
|
||||
def check_for_updates(self) -> None:
|
||||
tooltip(_("Checking..."))
|
||||
tooltip(tr(TR.ADDONS_CHECKING))
|
||||
check_and_prompt_for_updates(self, self.mgr, self.after_downloading)
|
||||
|
||||
def onConfig(self) -> None:
|
||||
|
|
@ -899,7 +897,7 @@ class AddonsDialog(QDialog):
|
|||
|
||||
conf = self.mgr.getConfig(addon)
|
||||
if conf is None:
|
||||
showInfo(_("Add-on has no configuration."))
|
||||
showInfo(tr(TR.ADDONS_ADDON_HAS_NO_CONFIGURATION))
|
||||
return
|
||||
|
||||
ConfigEditor(self, addon, conf)
|
||||
|
|
@ -919,7 +917,7 @@ class GetAddons(QDialog):
|
|||
self.form = aqt.forms.getaddons.Ui_Dialog()
|
||||
self.form.setupUi(self)
|
||||
b = self.form.buttonBox.addButton(
|
||||
_("Browse Add-ons"), QDialogButtonBox.ActionRole
|
||||
tr(TR.ADDONS_BROWSE_ADDONS), QDialogButtonBox.ActionRole
|
||||
)
|
||||
qconnect(b.clicked, self.onBrowse)
|
||||
restoreGeom(self, "getaddons", adjustSize=True)
|
||||
|
|
@ -934,7 +932,7 @@ class GetAddons(QDialog):
|
|||
try:
|
||||
ids = [int(n) for n in self.form.code.text().split()]
|
||||
except ValueError:
|
||||
showWarning(_("Invalid code."))
|
||||
showWarning(tr(TR.ADDONS_INVALID_CODE))
|
||||
return
|
||||
|
||||
self.ids = ids
|
||||
|
|
@ -1007,21 +1005,22 @@ def describe_log_entry(id_and_entry: DownloadLogEntry) -> str:
|
|||
if isinstance(entry, DownloadError):
|
||||
if entry.status_code is not None:
|
||||
if entry.status_code in (403, 404):
|
||||
buf += _(
|
||||
"Invalid code, or add-on not available for your version of Anki."
|
||||
)
|
||||
else:
|
||||
buf += _("Unexpected response code: %s") % entry.status_code
|
||||
buf += tr(TR.ADDONS_INVALID_CODE_OR_ADDON_NOT_AVAILABLE)
|
||||
else:
|
||||
buf += (
|
||||
_("Please check your internet connection.")
|
||||
tr(TR.QT_MISC_UNEXPECTED_RESPONSE_CODE, val="%s")
|
||||
% entry.status_code
|
||||
)
|
||||
else:
|
||||
buf += (
|
||||
tr(TR.ADDONS_PLEASE_CHECK_YOUR_INTERNET_CONNECTION)
|
||||
+ "\n\n"
|
||||
+ str(entry.exception)
|
||||
)
|
||||
elif isinstance(entry, InstallError):
|
||||
buf += entry.errmsg
|
||||
else:
|
||||
buf += _("Installed successfully.")
|
||||
buf += tr(TR.ADDONS_INSTALLED_SUCCESSFULLY)
|
||||
|
||||
return buf
|
||||
|
||||
|
|
@ -1091,7 +1090,7 @@ class DownloaderInstaller(QObject):
|
|||
# and "%(kb)0.2f" is the number of downloaded
|
||||
# kilobytes. This lead for example to "Downloading 3/5
|
||||
# (27KB)"
|
||||
label=_("Downloading %(a)d/%(b)d (%(kb)0.2fKB)...")
|
||||
label=tr(TR.ADDONS_DOWNLOADING_ADBD_KB02FKB)
|
||||
% dict(a=len(self.log) + 1, b=len(self.ids), kb=self.dl_bytes / 1024)
|
||||
)
|
||||
|
||||
|
|
@ -1110,9 +1109,9 @@ def show_log_to_user(parent: QWidget, log: List[DownloadLogEntry]) -> None:
|
|||
have_problem = download_encountered_problem(log)
|
||||
|
||||
if have_problem:
|
||||
text = _("One or more errors occurred:")
|
||||
text = tr(TR.ADDONS_ONE_OR_MORE_ERRORS_OCCURRED)
|
||||
else:
|
||||
text = _("Download complete. Please restart Anki to apply changes.")
|
||||
text = tr(TR.ADDONS_DOWNLOAD_COMPLETE_PLEASE_RESTART_ANKI_TO)
|
||||
text += "<br><br>" + download_log_to_html(log)
|
||||
|
||||
if have_problem:
|
||||
|
|
@ -1265,7 +1264,7 @@ def prompt_to_update(
|
|||
) -> None:
|
||||
names = map(lambda x: mgr.addonName(str(x)), ids)
|
||||
if not askUser(
|
||||
_("The following add-ons have updates available. Install them now?")
|
||||
tr(TR.ADDONS_THE_FOLLOWING_ADDONS_HAVE_UPDATES_AVAILABLE)
|
||||
+ "\n\n"
|
||||
+ "\n".join(names)
|
||||
):
|
||||
|
|
@ -1307,7 +1306,7 @@ class ConfigEditor(QDialog):
|
|||
def onRestoreDefaults(self) -> None:
|
||||
default_conf = self.mgr.addonConfigDefaults(self.addon)
|
||||
self.updateText(default_conf)
|
||||
tooltip(_("Restored defaults"), parent=self)
|
||||
tooltip(tr(TR.ADDONS_RESTORED_DEFAULTS), parent=self)
|
||||
|
||||
def setupFonts(self) -> None:
|
||||
font_mono = QFontDatabase.systemFont(QFontDatabase.FixedFont)
|
||||
|
|
@ -1373,11 +1372,11 @@ class ConfigEditor(QDialog):
|
|||
showInfo(msg)
|
||||
return
|
||||
except Exception as e:
|
||||
showInfo(_("Invalid configuration: ") + repr(e))
|
||||
showInfo(tr(TR.ADDONS_INVALID_CONFIGURATION) + repr(e))
|
||||
return
|
||||
|
||||
if not isinstance(new_conf, dict):
|
||||
showInfo(_("Invalid configuration: top level object must be a map"))
|
||||
showInfo(tr(TR.ADDONS_INVALID_CONFIGURATION_TOP_LEVEL_OBJECT_MUST))
|
||||
return
|
||||
|
||||
if new_conf != self.conf:
|
||||
|
|
@ -1417,7 +1416,7 @@ def installAddonPackages(
|
|||
not showInfo(
|
||||
q,
|
||||
parent=parent,
|
||||
title=_("Install Anki add-on"),
|
||||
title=tr(TR.ADDONS_INSTALL_ANKI_ADDON),
|
||||
type="warning",
|
||||
customBtns=[QMessageBox.No, QMessageBox.Yes],
|
||||
)
|
||||
|
|
@ -1430,9 +1429,7 @@ def installAddonPackages(
|
|||
if log:
|
||||
log_html = "<br>".join(log)
|
||||
if advise_restart:
|
||||
log_html += "<br><br>" + _(
|
||||
"<b>Please restart Anki to complete the installation.</b>"
|
||||
)
|
||||
log_html += "<br><br>" + tr(TR.ADDONS_PLEASE_RESTART_ANKI_TO_COMPLETE_THE)
|
||||
if len(log) == 1 and not strictly_modal:
|
||||
tooltip(log_html, parent=parent)
|
||||
else:
|
||||
|
|
@ -1440,15 +1437,15 @@ def installAddonPackages(
|
|||
log_html,
|
||||
parent=parent,
|
||||
textFormat="rich",
|
||||
title=_("Installation complete"),
|
||||
title=tr(TR.ADDONS_INSTALLATION_COMPLETE),
|
||||
)
|
||||
if errs:
|
||||
msg = _("Please report this to the respective add-on author(s).")
|
||||
msg = tr(TR.ADDONS_PLEASE_REPORT_THIS_TO_THE_RESPECTIVE)
|
||||
showWarning(
|
||||
"<br><br>".join(errs + [msg]),
|
||||
parent=parent,
|
||||
textFormat="rich",
|
||||
title=_("Add-on installation error"),
|
||||
title=tr(TR.ADDONS_ADDON_INSTALLATION_ERROR),
|
||||
)
|
||||
|
||||
return not errs
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import html
|
||||
|
|
@ -34,6 +33,7 @@ from aqt.qt import *
|
|||
from aqt.sidebar import NewSidebarTreeView, SidebarItemType, SidebarTreeViewBase
|
||||
from aqt.theme import theme_manager
|
||||
from aqt.utils import (
|
||||
TR,
|
||||
MenuList,
|
||||
SubMenu,
|
||||
askUser,
|
||||
|
|
@ -169,7 +169,7 @@ class DataModel(QAbstractTableModel):
|
|||
break
|
||||
# give the user a hint an invalid column was added by an add-on
|
||||
if not txt:
|
||||
txt = _("Add-on")
|
||||
txt = tr(TR.BROWSING_ADDON)
|
||||
return txt
|
||||
else:
|
||||
return
|
||||
|
|
@ -331,13 +331,13 @@ class DataModel(QAbstractTableModel):
|
|||
return c.model()["name"]
|
||||
elif type == "cardIvl":
|
||||
if c.type == CARD_TYPE_NEW:
|
||||
return _("(new)")
|
||||
return tr(TR.BROWSING_NEW)
|
||||
elif c.type == CARD_TYPE_LRN:
|
||||
return _("(learning)")
|
||||
return tr(TR.BROWSING_LEARNING)
|
||||
return self.col.format_timespan(c.ivl * 86400)
|
||||
elif type == "cardEase":
|
||||
if c.type == CARD_TYPE_NEW:
|
||||
return _("(new)")
|
||||
return tr(TR.BROWSING_NEW)
|
||||
return "%d%%" % (c.factor / 10)
|
||||
elif type == "deck":
|
||||
if c.odid:
|
||||
|
|
@ -366,7 +366,7 @@ class DataModel(QAbstractTableModel):
|
|||
|
||||
def nextDue(self, c, index):
|
||||
if c.odid:
|
||||
return _("(filtered)")
|
||||
return tr(TR.BROWSING_FILTERED)
|
||||
elif c.queue == QUEUE_TYPE_LRN:
|
||||
date = c.due
|
||||
elif c.queue == QUEUE_TYPE_NEW or c.type == CARD_TYPE_NEW:
|
||||
|
|
@ -621,7 +621,7 @@ class Browser(QMainWindow):
|
|||
f = self.form
|
||||
qconnect(f.previewButton.clicked, self.onTogglePreview)
|
||||
f.previewButton.setToolTip(
|
||||
_("Preview Selected Card (%s)") % shortcut("Ctrl+Shift+P")
|
||||
tr(TR.BROWSING_PREVIEW_SELECTED_CARD, val="%s") % shortcut("Ctrl+Shift+P")
|
||||
)
|
||||
f.previewButton.setShortcut("Ctrl+Shift+P")
|
||||
|
||||
|
|
@ -737,21 +737,21 @@ class Browser(QMainWindow):
|
|||
|
||||
def setupColumns(self):
|
||||
self.columns = [
|
||||
("question", _("Question")),
|
||||
("answer", _("Answer")),
|
||||
("template", _("Card")),
|
||||
("deck", _("Deck")),
|
||||
("noteFld", _("Sort Field")),
|
||||
("noteCrt", _("Created")),
|
||||
("question", tr(TR.BROWSING_QUESTION)),
|
||||
("answer", tr(TR.BROWSING_ANSWER)),
|
||||
("template", tr(TR.BROWSING_CARD)),
|
||||
("deck", tr(TR.DECKS_DECK)),
|
||||
("noteFld", tr(TR.BROWSING_SORT_FIELD)),
|
||||
("noteCrt", tr(TR.BROWSING_CREATED)),
|
||||
("noteMod", tr(TR.SEARCH_NOTE_MODIFIED)),
|
||||
("cardMod", tr(TR.SEARCH_CARD_MODIFIED)),
|
||||
("cardDue", tr(TR.STATISTICS_DUE_DATE)),
|
||||
("cardIvl", _("Interval")),
|
||||
("cardEase", _("Ease")),
|
||||
("cardReps", _("Reviews")),
|
||||
("cardLapses", _("Lapses")),
|
||||
("noteTags", _("Tags")),
|
||||
("note", _("Note")),
|
||||
("cardIvl", tr(TR.BROWSING_INTERVAL)),
|
||||
("cardEase", tr(TR.BROWSING_EASE)),
|
||||
("cardReps", tr(TR.SCHEDULING_REVIEWS)),
|
||||
("cardLapses", tr(TR.SCHEDULING_LAPSES)),
|
||||
("noteTags", tr(TR.EDITING_TAGS)),
|
||||
("note", tr(TR.BROWSING_NOTE)),
|
||||
]
|
||||
self.columns.sort(key=itemgetter(1))
|
||||
|
||||
|
|
@ -762,7 +762,7 @@ class Browser(QMainWindow):
|
|||
qconnect(self.form.searchButton.clicked, self.onSearchActivated)
|
||||
qconnect(self.form.searchEdit.lineEdit().returnPressed, self.onSearchActivated)
|
||||
self.form.searchEdit.setCompleter(None)
|
||||
self._searchPrompt = _("<type here to search; hit enter to show current deck>")
|
||||
self._searchPrompt = tr(TR.BROWSING_TYPE_HERE_TO_SEARCH)
|
||||
self.form.searchEdit.addItems(
|
||||
[self._searchPrompt] + self.mw.pm.profile["searchHistory"]
|
||||
)
|
||||
|
|
@ -938,9 +938,7 @@ QTableView {{ gridline-color: {grid} }}
|
|||
type = self.model.activeCols[idx]
|
||||
noSort = ("question", "answer")
|
||||
if type in noSort:
|
||||
showInfo(
|
||||
_("Sorting on this column is not supported. Please " "choose another.")
|
||||
)
|
||||
showInfo(tr(TR.BROWSING_SORTING_ON_THIS_COLUMN_IS_NOT))
|
||||
type = self.col.conf["sortType"]
|
||||
if self.col.conf["sortType"] != type:
|
||||
self.col.conf["sortType"] = type
|
||||
|
|
@ -994,7 +992,7 @@ QTableView {{ gridline-color: {grid} }}
|
|||
if type in self.model.activeCols:
|
||||
if len(self.model.activeCols) < 2:
|
||||
self.model.endReset()
|
||||
return showInfo(_("You must have at least one column."))
|
||||
return showInfo(tr(TR.BROWSING_YOU_MUST_HAVE_AT_LEAST_ONE))
|
||||
self.model.activeCols.remove(type)
|
||||
adding = False
|
||||
else:
|
||||
|
|
@ -1028,7 +1026,7 @@ QTableView {{ gridline-color: {grid} }}
|
|||
pass
|
||||
|
||||
def setupSidebar(self) -> None:
|
||||
dw = self.sidebarDockWidget = QDockWidget(_("Sidebar"), self)
|
||||
dw = self.sidebarDockWidget = QDockWidget(tr(TR.BROWSING_SIDEBAR), self)
|
||||
dw.setFeatures(QDockWidget.DockWidgetClosable)
|
||||
dw.setObjectName("Sidebar")
|
||||
dw.setAllowedAreas(Qt.LeftDockWidgetArea)
|
||||
|
|
@ -1105,14 +1103,14 @@ QTableView {{ gridline-color: {grid} }}
|
|||
|
||||
def _stdTree(self, root) -> None:
|
||||
item = SidebarItem(
|
||||
_("Whole Collection"),
|
||||
tr(TR.BROWSING_WHOLE_COLLECTION),
|
||||
":/icons/collection.svg",
|
||||
self._filterFunc(""),
|
||||
item_type=SidebarItemType.COLLECTION,
|
||||
)
|
||||
root.addChild(item)
|
||||
item = SidebarItem(
|
||||
_("Current Deck"),
|
||||
tr(TR.BROWSING_CURRENT_DECK),
|
||||
":/icons/deck.svg",
|
||||
self._filterFunc("deck:current"),
|
||||
item_type=SidebarItemType.CURRENT_DECK,
|
||||
|
|
@ -1252,41 +1250,44 @@ QTableView {{ gridline-color: {grid} }}
|
|||
|
||||
def _commonFilters(self):
|
||||
return self._simpleFilters(
|
||||
((_("Whole Collection"), ""), (_("Current Deck"), "deck:current"))
|
||||
(
|
||||
(tr(TR.BROWSING_WHOLE_COLLECTION), ""),
|
||||
(tr(TR.BROWSING_CURRENT_DECK), "deck:current"),
|
||||
)
|
||||
)
|
||||
|
||||
def _todayFilters(self):
|
||||
subm = SubMenu(_("Today"))
|
||||
subm = SubMenu(tr(TR.BROWSING_TODAY))
|
||||
subm.addChild(
|
||||
self._simpleFilters(
|
||||
(
|
||||
(_("Added Today"), "added:1"),
|
||||
(_("Studied Today"), "rated:1"),
|
||||
(_("Again Today"), "rated:1:1"),
|
||||
(tr(TR.BROWSING_ADDED_TODAY), "added:1"),
|
||||
(tr(TR.BROWSING_STUDIED_TODAY), "rated:1"),
|
||||
(tr(TR.BROWSING_AGAIN_TODAY), "rated:1:1"),
|
||||
)
|
||||
)
|
||||
)
|
||||
return subm
|
||||
|
||||
def _cardStateFilters(self):
|
||||
subm = SubMenu(_("Card State"))
|
||||
subm = SubMenu(tr(TR.BROWSING_CARD_STATE))
|
||||
subm.addChild(
|
||||
self._simpleFilters(
|
||||
(
|
||||
(_("New"), "is:new"),
|
||||
(_("Learning"), "is:learn"),
|
||||
(_("Review"), "is:review"),
|
||||
(tr(TR.ACTIONS_NEW), "is:new"),
|
||||
(tr(TR.SCHEDULING_LEARNING), "is:learn"),
|
||||
(tr(TR.SCHEDULING_REVIEW), "is:review"),
|
||||
(tr(TR.FILTERING_IS_DUE), "is:due"),
|
||||
None,
|
||||
(_("Suspended"), "is:suspended"),
|
||||
(_("Buried"), "is:buried"),
|
||||
(tr(TR.BROWSING_SUSPENDED), "is:suspended"),
|
||||
(tr(TR.BROWSING_BURIED), "is:buried"),
|
||||
None,
|
||||
(_("Red Flag"), "flag:1"),
|
||||
(_("Orange Flag"), "flag:2"),
|
||||
(_("Green Flag"), "flag:3"),
|
||||
(_("Blue Flag"), "flag:4"),
|
||||
(_("No Flag"), "flag:0"),
|
||||
(_("Any Flag"), "-flag:0"),
|
||||
(tr(TR.ACTIONS_RED_FLAG), "flag:1"),
|
||||
(tr(TR.ACTIONS_ORANGE_FLAG), "flag:2"),
|
||||
(tr(TR.ACTIONS_GREEN_FLAG), "flag:3"),
|
||||
(tr(TR.ACTIONS_BLUE_FLAG), "flag:4"),
|
||||
(tr(TR.BROWSING_NO_FLAG), "flag:0"),
|
||||
(tr(TR.BROWSING_ANY_FLAG), "-flag:0"),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -1296,9 +1297,9 @@ QTableView {{ gridline-color: {grid} }}
|
|||
return label.replace("&", "&&")
|
||||
|
||||
def _tagFilters(self):
|
||||
m = SubMenu(_("Tags"))
|
||||
m = SubMenu(tr(TR.EDITING_TAGS))
|
||||
|
||||
m.addItem(_("Clear Unused"), self.clearUnusedTags)
|
||||
m.addItem(tr(TR.BROWSING_CLEAR_UNUSED), self.clearUnusedTags)
|
||||
m.addSeparator()
|
||||
|
||||
tagList = MenuList()
|
||||
|
|
@ -1316,7 +1317,9 @@ QTableView {{ gridline-color: {grid} }}
|
|||
fullname = parent_prefix + node.name
|
||||
if node.children:
|
||||
subm = parent.addMenu(escaped_name)
|
||||
subm.addItem(_("Filter"), self._filterFunc("deck", fullname))
|
||||
subm.addItem(
|
||||
tr(TR.ACTIONS_FILTER), self._filterFunc("deck", fullname)
|
||||
)
|
||||
subm.addSeparator()
|
||||
addDecks(subm, node.children, fullname + "::")
|
||||
else:
|
||||
|
|
@ -1326,15 +1329,15 @@ QTableView {{ gridline-color: {grid} }}
|
|||
ml = MenuList()
|
||||
addDecks(ml, alldecks.children, "")
|
||||
|
||||
root = SubMenu(_("Decks"))
|
||||
root = SubMenu(tr(TR.ACTIONS_DECKS))
|
||||
root.addChild(ml.chunked())
|
||||
|
||||
return root
|
||||
|
||||
def _noteTypeFilters(self):
|
||||
m = SubMenu(_("Note Types"))
|
||||
m = SubMenu(tr(TR.NOTETYPES_NOTE_TYPES))
|
||||
|
||||
m.addItem(_("Manage..."), self.mw.onNoteTypes)
|
||||
m.addItem(tr(TR.ACTIONS_MANAGE), self.mw.onNoteTypes)
|
||||
m.addSeparator()
|
||||
|
||||
noteTypes = MenuList()
|
||||
|
|
@ -1346,14 +1349,16 @@ QTableView {{ gridline-color: {grid} }}
|
|||
else:
|
||||
subm = noteTypes.addMenu(escaped_nt_name)
|
||||
|
||||
subm.addItem(_("All Card Types"), self._filterFunc("note", nt["name"]))
|
||||
subm.addItem(
|
||||
tr(TR.BROWSING_ALL_CARD_TYPES), self._filterFunc("note", nt["name"])
|
||||
)
|
||||
subm.addSeparator()
|
||||
|
||||
# add templates
|
||||
for c, tmpl in enumerate(nt["tmpls"]):
|
||||
# T: name is a card type name. n it's order in the list of card type.
|
||||
# T: this is shown in browser's filter, when seeing the list of card type of a note type.
|
||||
name = _("%(n)d: %(name)s") % dict(
|
||||
name = tr(TR.BROWSING_ND_NAMES) % dict(
|
||||
n=c + 1, name=self._escapeMenuItem(tmpl["name"])
|
||||
)
|
||||
subm.addItem(
|
||||
|
|
@ -1375,9 +1380,9 @@ QTableView {{ gridline-color: {grid} }}
|
|||
ml.addSeparator()
|
||||
|
||||
if self._currentFilterIsSaved():
|
||||
ml.addItem(_("Remove Current Filter..."), self._onRemoveFilter)
|
||||
ml.addItem(tr(TR.BROWSING_REMOVE_CURRENT_FILTER), self._onRemoveFilter)
|
||||
else:
|
||||
ml.addItem(_("Save Current Filter..."), self._onSaveFilter)
|
||||
ml.addItem(tr(TR.BROWSING_SAVE_CURRENT_FILTER), self._onSaveFilter)
|
||||
|
||||
saved = self.col.get_config("savedFilters")
|
||||
if not saved:
|
||||
|
|
@ -1390,7 +1395,7 @@ QTableView {{ gridline-color: {grid} }}
|
|||
return ml
|
||||
|
||||
def _onSaveFilter(self) -> None:
|
||||
name = getOnlyText(_("Please give your filter a name:"))
|
||||
name = getOnlyText(tr(TR.BROWSING_PLEASE_GIVE_YOUR_FILTER_A_NAME))
|
||||
if not name:
|
||||
return
|
||||
filt = self.form.searchEdit.lineEdit().text()
|
||||
|
|
@ -1401,7 +1406,9 @@ QTableView {{ gridline-color: {grid} }}
|
|||
|
||||
def _onRemoveFilter(self):
|
||||
name = self._currentFilterIsSaved()
|
||||
if not askUser(_("Remove %s from your saved searches?") % name):
|
||||
if not askUser(
|
||||
tr(TR.BROWSING_REMOVE_FROM_YOUR_SAVED_SEARCHES, val="%s") % name
|
||||
):
|
||||
return
|
||||
del self.col.conf["savedFilters"][name]
|
||||
self.col.setMod()
|
||||
|
|
@ -1490,7 +1497,7 @@ where id in %s"""
|
|||
% ids2str(sf)
|
||||
)
|
||||
if mods > 1:
|
||||
showInfo(_("Please select cards from only one note type."))
|
||||
showInfo(tr(TR.BROWSING_PLEASE_SELECT_CARDS_FROM_ONLY_ONE))
|
||||
return
|
||||
return sf
|
||||
|
||||
|
|
@ -1544,7 +1551,7 @@ where id in %s"""
|
|||
nids = self.selectedNotes()
|
||||
if not nids:
|
||||
return
|
||||
self.mw.checkpoint(_("Delete Notes"))
|
||||
self.mw.checkpoint(tr(TR.BROWSING_DELETE_NOTES))
|
||||
self.model.beginReset()
|
||||
# figure out where to place the cursor after the deletion
|
||||
curRow = self.form.tableView.selectionModel().currentIndex().row()
|
||||
|
|
@ -1590,8 +1597,8 @@ where id in %s"""
|
|||
ret = StudyDeck(
|
||||
self.mw,
|
||||
current=current,
|
||||
accept=_("Move Cards"),
|
||||
title=_("Change Deck"),
|
||||
accept=tr(TR.BROWSING_MOVE_CARDS),
|
||||
title=tr(TR.BROWSING_CHANGE_DECK),
|
||||
help="browse",
|
||||
parent=self,
|
||||
)
|
||||
|
|
@ -1600,10 +1607,10 @@ where id in %s"""
|
|||
did = self.col.decks.id(ret.name)
|
||||
deck = self.col.decks.get(did)
|
||||
if deck["dyn"]:
|
||||
showWarning(_("Cards can't be manually moved into a filtered deck."))
|
||||
showWarning(tr(TR.BROWSING_CARDS_CANT_BE_MANUALLY_MOVED_INTO))
|
||||
return
|
||||
self.model.beginReset()
|
||||
self.mw.checkpoint(_("Change Deck"))
|
||||
self.mw.checkpoint(tr(TR.BROWSING_CHANGE_DECK))
|
||||
self.col.set_deck(cids, did)
|
||||
self.model.endReset()
|
||||
self.mw.requireReset(reason=ResetReason.BrowserSetDeck, context=self)
|
||||
|
|
@ -1616,7 +1623,7 @@ where id in %s"""
|
|||
|
||||
def _addTags(self, tags, label, prompt, func):
|
||||
if prompt is None:
|
||||
prompt = _("Enter tags to add:")
|
||||
prompt = tr(TR.BROWSING_ENTER_TAGS_TO_ADD)
|
||||
if tags is None:
|
||||
(tags, r) = getTag(self, self.col, prompt)
|
||||
else:
|
||||
|
|
@ -1626,7 +1633,7 @@ where id in %s"""
|
|||
if func is None:
|
||||
func = self.col.tags.bulkAdd
|
||||
if label is None:
|
||||
label = _("Add Tags")
|
||||
label = tr(TR.BROWSING_ADD_TAGS)
|
||||
if label:
|
||||
self.mw.checkpoint(label)
|
||||
self.model.beginReset()
|
||||
|
|
@ -1636,9 +1643,12 @@ where id in %s"""
|
|||
|
||||
def deleteTags(self, tags=None, label=None):
|
||||
if label is None:
|
||||
label = _("Delete Tags")
|
||||
label = tr(TR.BROWSING_DELETE_TAGS)
|
||||
self.addTags(
|
||||
tags, label, _("Enter tags to delete:"), func=self.col.tags.bulkRem
|
||||
tags,
|
||||
label,
|
||||
tr(TR.BROWSING_ENTER_TAGS_TO_DELETE),
|
||||
func=self.col.tags.bulkRem,
|
||||
)
|
||||
|
||||
def clearUnusedTags(self):
|
||||
|
|
@ -1731,7 +1741,7 @@ where id in %s"""
|
|||
+ ids2str(cids)
|
||||
)
|
||||
if not cids2:
|
||||
return showInfo(_("Only new cards can be repositioned."))
|
||||
return showInfo(tr(TR.BROWSING_ONLY_NEW_CARDS_CAN_BE_REPOSITIONED))
|
||||
d = QDialog(self)
|
||||
d.setWindowModality(Qt.WindowModal)
|
||||
frm = aqt.forms.reposition.Ui_Dialog()
|
||||
|
|
@ -1741,14 +1751,14 @@ where id in %s"""
|
|||
)
|
||||
pmin = pmin or 0
|
||||
pmax = pmax or 0
|
||||
txt = _("Queue top: %d") % pmin
|
||||
txt += "\n" + _("Queue bottom: %d") % pmax
|
||||
txt = tr(TR.BROWSING_QUEUE_TOP, val="%s") % pmin
|
||||
txt += "\n" + tr(TR.BROWSING_QUEUE_BOTTOM, val="%s") % pmax
|
||||
frm.label.setText(txt)
|
||||
frm.start.selectAll()
|
||||
if not d.exec_():
|
||||
return
|
||||
self.model.beginReset()
|
||||
self.mw.checkpoint(_("Reposition"))
|
||||
self.mw.checkpoint(tr(TR.ACTIONS_REPOSITION))
|
||||
self.col.sched.sortCards(
|
||||
cids,
|
||||
start=frm.start.value(),
|
||||
|
|
@ -1774,7 +1784,7 @@ where id in %s"""
|
|||
if not d.exec_():
|
||||
return
|
||||
self.model.beginReset()
|
||||
self.mw.checkpoint(_("Reschedule"))
|
||||
self.mw.checkpoint(tr(TR.BROWSING_RESCHEDULE))
|
||||
if frm.asNew.isChecked():
|
||||
self.col.sched.forgetCards(self.selectedCards())
|
||||
else:
|
||||
|
|
@ -1881,7 +1891,7 @@ where id in %s"""
|
|||
restore_is_checked(frm.ignoreCase, combo + "ignoreCase")
|
||||
|
||||
frm.find.setFocus()
|
||||
allfields = [_("All Fields")] + fields
|
||||
allfields = [tr(TR.BROWSING_ALL_FIELDS)] + fields
|
||||
frm.field.addItems(allfields)
|
||||
restore_combo_index_for_session(frm.field, allfields, combo + "Field")
|
||||
qconnect(frm.buttonBox.helpRequested, self.onFindReplaceHelp)
|
||||
|
|
@ -1906,7 +1916,7 @@ where id in %s"""
|
|||
save_is_checked(frm.re, combo + "Regex")
|
||||
save_is_checked(frm.ignoreCase, combo + "ignoreCase")
|
||||
|
||||
self.mw.checkpoint(_("Find and Replace"))
|
||||
self.mw.checkpoint(tr(TR.BROWSING_FIND_AND_REPLACE))
|
||||
# starts progress dialog as well
|
||||
self.model.beginReset()
|
||||
|
||||
|
|
@ -1976,7 +1986,9 @@ where id in %s"""
|
|||
field = fields[frm.fields.currentIndex()]
|
||||
self.duplicatesReport(frm.webView, field, search_text, frm, web_context)
|
||||
|
||||
search = frm.buttonBox.addButton(_("Search"), QDialogButtonBox.ActionRole)
|
||||
search = frm.buttonBox.addButton(
|
||||
tr(TR.ACTIONS_SEARCH), QDialogButtonBox.ActionRole
|
||||
)
|
||||
qconnect(search.clicked, onClick)
|
||||
d.show()
|
||||
|
||||
|
|
@ -1985,7 +1997,7 @@ where id in %s"""
|
|||
res = self.mw.col.findDupes(fname, search)
|
||||
if not self._dupesButton:
|
||||
self._dupesButton = b = frm.buttonBox.addButton(
|
||||
_("Tag Duplicates"), QDialogButtonBox.ActionRole
|
||||
tr(TR.BROWSING_TAG_DUPLICATES), QDialogButtonBox.ActionRole
|
||||
)
|
||||
qconnect(b.clicked, lambda: self._onTagDupes(res))
|
||||
t = ""
|
||||
|
|
@ -1993,7 +2005,7 @@ where id in %s"""
|
|||
notes = sum(len(r[1]) for r in res)
|
||||
part1 = ngettext("%d group", "%d groups", groups) % groups
|
||||
part2 = ngettext("%d note", "%d notes", notes) % notes
|
||||
t += _("Found %(a)s across %(b)s.") % dict(a=part1, b=part2)
|
||||
t += tr(TR.BROWSING_FOUND_AS_ACROSS_BS) % dict(a=part1, b=part2)
|
||||
t += "<p><ol>"
|
||||
for val, nids in res:
|
||||
t += (
|
||||
|
|
@ -2012,15 +2024,15 @@ where id in %s"""
|
|||
if not res:
|
||||
return
|
||||
self.model.beginReset()
|
||||
self.mw.checkpoint(_("Tag Duplicates"))
|
||||
self.mw.checkpoint(tr(TR.BROWSING_TAG_DUPLICATES))
|
||||
nids = set()
|
||||
for s, nidlist in res:
|
||||
nids.update(nidlist)
|
||||
self.col.tags.bulkAdd(list(nids), _("duplicate"))
|
||||
self.col.tags.bulkAdd(list(nids), tr(TR.BROWSING_DUPLICATE))
|
||||
self.mw.progress.finish()
|
||||
self.model.endReset()
|
||||
self.mw.requireReset(reason=ResetReason.BrowserTagDupes, context=self)
|
||||
tooltip(_("Notes tagged."))
|
||||
tooltip(tr(TR.BROWSING_NOTES_TAGGED))
|
||||
|
||||
def dupeLinkClicked(self, link):
|
||||
self.search_for(link)
|
||||
|
|
@ -2177,10 +2189,10 @@ class ChangeModel(QDialog):
|
|||
map = QWidget()
|
||||
l = QGridLayout()
|
||||
combos = []
|
||||
targets = [x["name"] for x in dst] + [_("Nothing")]
|
||||
targets = [x["name"] for x in dst] + [tr(TR.BROWSING_NOTHING)]
|
||||
indices = {}
|
||||
for i, x in enumerate(src):
|
||||
l.addWidget(QLabel(_("Change %s to:") % x["name"]), i, 0)
|
||||
l.addWidget(QLabel(tr(TR.BROWSING_CHANGE_TO, val="%s") % x["name"]), i, 0)
|
||||
cb = QComboBox()
|
||||
cb.addItems(targets)
|
||||
idx = min(i, len(targets) - 1)
|
||||
|
|
@ -2267,7 +2279,7 @@ Are you sure you want to continue?"""
|
|||
)
|
||||
):
|
||||
return
|
||||
self.browser.mw.checkpoint(_("Change Note Type"))
|
||||
self.browser.mw.checkpoint(tr(TR.BROWSING_CHANGE_NOTE_TYPE))
|
||||
b = self.browser
|
||||
b.mw.col.modSchema(check=True)
|
||||
b.mw.progress.start()
|
||||
|
|
|
|||
|
|
@ -60,13 +60,15 @@ class CardLayout(QDialog):
|
|||
self.mobile_emulation_enabled = False
|
||||
self.have_autoplayed = False
|
||||
self.mm._remove_from_cache(self.model["id"])
|
||||
self.mw.checkpoint(_("Card Types"))
|
||||
self.mw.checkpoint(tr(TR.CARD_TEMPLATES_CARD_TYPES))
|
||||
self.change_tracker = ChangeTracker(self.mw)
|
||||
self.setupTopArea()
|
||||
self.setupMainArea()
|
||||
self.setupButtons()
|
||||
self.setupShortcuts()
|
||||
self.setWindowTitle(_("Card Types for %s") % self.model["name"])
|
||||
self.setWindowTitle(
|
||||
tr(TR.CARD_TEMPLATES_CARD_TYPES_FOR, val="%s") % self.model["name"]
|
||||
)
|
||||
v1 = QVBoxLayout()
|
||||
v1.addWidget(self.topArea)
|
||||
v1.addWidget(self.mainArea)
|
||||
|
|
@ -108,7 +110,9 @@ class CardLayout(QDialog):
|
|||
self.topArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
self.topAreaForm = aqt.forms.clayout_top.Ui_Form()
|
||||
self.topAreaForm.setupUi(self.topArea)
|
||||
self.topAreaForm.templateOptions.setText(_("Options") + " " + downArrow())
|
||||
self.topAreaForm.templateOptions.setText(
|
||||
tr(TR.ACTIONS_OPTIONS) + " " + downArrow()
|
||||
)
|
||||
qconnect(self.topAreaForm.templateOptions.clicked, self.onMore)
|
||||
qconnect(
|
||||
self.topAreaForm.templatesBox.currentIndexChanged,
|
||||
|
|
@ -240,7 +244,7 @@ class CardLayout(QDialog):
|
|||
qconnect(widg.returnPressed, self.on_search_next)
|
||||
|
||||
def setup_cloze_number_box(self):
|
||||
names = (_("Cloze %d") % n for n in self.cloze_numbers)
|
||||
names = (tr(TR.CARD_TEMPLATES_CLOZE, val="%s") % n for n in self.cloze_numbers)
|
||||
self.pform.cloze_number_combo.addItems(names)
|
||||
try:
|
||||
idx = self.cloze_numbers.index(self.ord + 1)
|
||||
|
|
@ -381,28 +385,28 @@ class CardLayout(QDialog):
|
|||
|
||||
def setupButtons(self):
|
||||
l = self.buttons = QHBoxLayout()
|
||||
help = QPushButton(_("Help"))
|
||||
help = QPushButton(tr(TR.ACTIONS_HELP))
|
||||
help.setAutoDefault(False)
|
||||
l.addWidget(help)
|
||||
qconnect(help.clicked, self.onHelp)
|
||||
l.addStretch()
|
||||
self.add_field_button = QPushButton(_("Add Field"))
|
||||
self.add_field_button = QPushButton(tr(TR.FIELDS_ADD_FIELD))
|
||||
self.add_field_button.setAutoDefault(False)
|
||||
l.addWidget(self.add_field_button)
|
||||
qconnect(self.add_field_button.clicked, self.onAddField)
|
||||
if not self._isCloze():
|
||||
flip = QPushButton(_("Flip"))
|
||||
flip = QPushButton(tr(TR.CARD_TEMPLATES_FLIP))
|
||||
flip.setAutoDefault(False)
|
||||
l.addWidget(flip)
|
||||
qconnect(flip.clicked, self.onFlip)
|
||||
l.addStretch()
|
||||
save = QPushButton(_("Save"))
|
||||
save = QPushButton(tr(TR.ACTIONS_SAVE))
|
||||
save.setAutoDefault(False)
|
||||
save.setShortcut(QKeySequence("Ctrl+Return"))
|
||||
l.addWidget(save)
|
||||
qconnect(save.clicked, self.accept)
|
||||
|
||||
close = QPushButton(_("Cancel"))
|
||||
close = QPushButton(tr(TR.ACTIONS_CANCEL))
|
||||
close.setAutoDefault(False)
|
||||
l.addWidget(close)
|
||||
qconnect(close.clicked, self.reject)
|
||||
|
|
@ -548,7 +552,7 @@ class CardLayout(QDialog):
|
|||
|
||||
def onRemove(self):
|
||||
if len(self.templates) < 2:
|
||||
return showInfo(_("At least one card type is required."))
|
||||
return showInfo(tr(TR.CARD_TEMPLATES_AT_LEAST_ONE_CARD_TYPE_IS))
|
||||
|
||||
def get_count():
|
||||
return self.mm.template_use_count(self.model["id"], self.ord)
|
||||
|
|
@ -558,7 +562,7 @@ class CardLayout(QDialog):
|
|||
|
||||
template = self.current_template()
|
||||
cards = ngettext("%d card", "%d cards", card_cnt) % card_cnt
|
||||
msg = _("Delete the '%(a)s' card type, and its %(b)s?") % dict(
|
||||
msg = tr(TR.CARD_TEMPLATES_DELETE_THE_AS_CARD_TYPE_AND) % dict(
|
||||
a=template["name"], b=cards
|
||||
)
|
||||
if not askUser(msg):
|
||||
|
|
@ -583,7 +587,9 @@ class CardLayout(QDialog):
|
|||
|
||||
def onRename(self):
|
||||
template = self.current_template()
|
||||
name = getOnlyText(_("New name:"), default=template["name"]).replace('"', "")
|
||||
name = getOnlyText(tr(TR.ACTIONS_NEW_NAME), default=template["name"]).replace(
|
||||
'"', ""
|
||||
)
|
||||
if not name.strip():
|
||||
return
|
||||
|
||||
|
|
@ -597,7 +603,8 @@ class CardLayout(QDialog):
|
|||
template = self.current_template()
|
||||
current_pos = self.templates.index(template) + 1
|
||||
pos = getOnlyText(
|
||||
_("Enter new card position (1...%s):") % n, default=str(current_pos)
|
||||
tr(TR.CARD_TEMPLATES_ENTER_NEW_CARD_POSITION_1, val="%s") % n,
|
||||
default=str(current_pos),
|
||||
)
|
||||
if not pos:
|
||||
return
|
||||
|
|
@ -619,7 +626,7 @@ class CardLayout(QDialog):
|
|||
def _newCardName(self):
|
||||
n = len(self.templates) + 1
|
||||
while 1:
|
||||
name = _("Card %d") % n
|
||||
name = tr(TR.CARD_TEMPLATES_CARD, val="%s") % n
|
||||
if name not in [t["name"] for t in self.templates]:
|
||||
break
|
||||
n += 1
|
||||
|
|
@ -673,29 +680,29 @@ adjust the template manually to switch the question and answer."""
|
|||
m = QMenu(self)
|
||||
|
||||
if not self._isCloze():
|
||||
a = m.addAction(_("Add Card Type..."))
|
||||
a = m.addAction(tr(TR.CARD_TEMPLATES_ADD_CARD_TYPE))
|
||||
qconnect(a.triggered, self.onAddCard)
|
||||
|
||||
a = m.addAction(_("Remove Card Type..."))
|
||||
a = m.addAction(tr(TR.CARD_TEMPLATES_REMOVE_CARD_TYPE))
|
||||
qconnect(a.triggered, self.onRemove)
|
||||
|
||||
a = m.addAction(_("Rename Card Type..."))
|
||||
a = m.addAction(tr(TR.CARD_TEMPLATES_RENAME_CARD_TYPE))
|
||||
qconnect(a.triggered, self.onRename)
|
||||
|
||||
a = m.addAction(_("Reposition Card Type..."))
|
||||
a = m.addAction(tr(TR.CARD_TEMPLATES_REPOSITION_CARD_TYPE))
|
||||
qconnect(a.triggered, self.onReorder)
|
||||
|
||||
m.addSeparator()
|
||||
|
||||
t = self.current_template()
|
||||
if t["did"]:
|
||||
s = _(" (on)")
|
||||
s = tr(TR.CARD_TEMPLATES_ON)
|
||||
else:
|
||||
s = _(" (off)")
|
||||
a = m.addAction(_("Deck Override...") + s)
|
||||
s = tr(TR.CARD_TEMPLATES_OFF)
|
||||
a = m.addAction(tr(TR.CARD_TEMPLATES_DECK_OVERRIDE) + s)
|
||||
qconnect(a.triggered, self.onTargetDeck)
|
||||
|
||||
a = m.addAction(_("Browser Appearance..."))
|
||||
a = m.addAction(tr(TR.CARD_TEMPLATES_BROWSER_APPEARANCE))
|
||||
qconnect(a.triggered, self.onBrowserDisplay)
|
||||
|
||||
m.exec_(self.topAreaForm.templateOptions.mapToGlobal(QPoint(0, 0)))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# -*- coding: utf-8 -*-
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
import aqt
|
||||
from anki.consts import *
|
||||
from anki.lang import _
|
||||
|
|
@ -50,11 +49,11 @@ class CustomStudy(QDialog):
|
|||
smin = 1
|
||||
smax = DYN_MAX_SIZE
|
||||
sval = 1
|
||||
post = _("cards")
|
||||
post = tr(TR.CUSTOM_STUDY_CARDS)
|
||||
tit = ""
|
||||
spShow = True
|
||||
typeShow = False
|
||||
ok = _("OK")
|
||||
ok = tr(TR.CUSTOM_STUDY_OK)
|
||||
|
||||
def plus(num):
|
||||
if num == 1000:
|
||||
|
|
@ -68,8 +67,10 @@ class CustomStudy(QDialog):
|
|||
new, self.conf["new"]["perDay"] - self.deck["newToday"][1]
|
||||
)
|
||||
newExceeding = min(new, new - newUnderLearning)
|
||||
tit = _("New cards in deck over today limit: %s") % plus(newExceeding)
|
||||
pre = _("Increase today's new card limit by")
|
||||
tit = tr(TR.CUSTOM_STUDY_NEW_CARDS_IN_DECK_OVER_TODAY, val="%s") % plus(
|
||||
newExceeding
|
||||
)
|
||||
pre = tr(TR.CUSTOM_STUDY_INCREASE_TODAYS_NEW_CARD_LIMIT_BY)
|
||||
sval = min(new, self.deck.get("extendNew", 10))
|
||||
smin = -DYN_MAX_SIZE
|
||||
smax = newExceeding
|
||||
|
|
@ -80,27 +81,29 @@ class CustomStudy(QDialog):
|
|||
rev, self.conf["rev"]["perDay"] - self.deck["revToday"][1]
|
||||
)
|
||||
revExceeding = min(rev, rev - revUnderLearning)
|
||||
tit = _("Reviews due in deck over today limit: %s") % plus(revExceeding)
|
||||
pre = _("Increase today's review limit by")
|
||||
tit = tr(TR.CUSTOM_STUDY_REVIEWS_DUE_IN_DECK_OVER_TODAY, val="%s") % plus(
|
||||
revExceeding
|
||||
)
|
||||
pre = tr(TR.CUSTOM_STUDY_INCREASE_TODAYS_REVIEW_LIMIT_BY)
|
||||
sval = min(rev, self.deck.get("extendRev", 10))
|
||||
smin = -DYN_MAX_SIZE
|
||||
smax = revExceeding
|
||||
elif idx == RADIO_FORGOT:
|
||||
pre = _("Review cards forgotten in last")
|
||||
post = _("days")
|
||||
pre = tr(TR.CUSTOM_STUDY_REVIEW_CARDS_FORGOTTEN_IN_LAST)
|
||||
post = tr(TR.SCHEDULING_DAYS)
|
||||
smax = 30
|
||||
elif idx == RADIO_AHEAD:
|
||||
pre = _("Review ahead by")
|
||||
post = _("days")
|
||||
pre = tr(TR.CUSTOM_STUDY_REVIEW_AHEAD_BY)
|
||||
post = tr(TR.SCHEDULING_DAYS)
|
||||
elif idx == RADIO_PREVIEW:
|
||||
pre = _("Preview new cards added in the last")
|
||||
post = _("days")
|
||||
pre = tr(TR.CUSTOM_STUDY_PREVIEW_NEW_CARDS_ADDED_IN_THE)
|
||||
post = tr(TR.SCHEDULING_DAYS)
|
||||
sval = 1
|
||||
elif idx == RADIO_CRAM:
|
||||
pre = _("Select")
|
||||
post = _("cards from the deck")
|
||||
pre = tr(TR.CUSTOM_STUDY_SELECT)
|
||||
post = tr(TR.CUSTOM_STUDY_CARDS_FROM_THE_DECK)
|
||||
# tit = _("After pressing OK, you can choose which tags to include.")
|
||||
ok = _("Choose Tags")
|
||||
ok = tr(TR.CUSTOM_STUDY_CHOOSE_TAGS)
|
||||
sval = 100
|
||||
typeShow = True
|
||||
sp.setVisible(spShow)
|
||||
|
|
@ -138,7 +141,7 @@ class CustomStudy(QDialog):
|
|||
elif i == RADIO_CRAM:
|
||||
tags = self._getTags()
|
||||
# the rest create a filtered deck
|
||||
cur = self.mw.col.decks.byName(_("Custom Study Session"))
|
||||
cur = self.mw.col.decks.byName(tr(TR.CUSTOM_STUDY_CUSTOM_STUDY_SESSION))
|
||||
if cur:
|
||||
if not cur["dyn"]:
|
||||
showInfo(tr(TR.CUSTOM_STUDY_MUST_RENAME_DECK))
|
||||
|
|
@ -150,7 +153,9 @@ class CustomStudy(QDialog):
|
|||
dyn = cur
|
||||
self.mw.col.decks.select(cur["id"])
|
||||
else:
|
||||
did = self.mw.col.decks.new_filtered(_("Custom Study Session"))
|
||||
did = self.mw.col.decks.new_filtered(
|
||||
tr(TR.CUSTOM_STUDY_CUSTOM_STUDY_SESSION)
|
||||
)
|
||||
dyn = self.mw.col.decks.get(did)
|
||||
# and then set various options
|
||||
if i == RADIO_FORGOT:
|
||||
|
|
@ -187,7 +192,7 @@ class CustomStudy(QDialog):
|
|||
# generate cards
|
||||
self.created_custom_study = True
|
||||
if not self.mw.col.sched.rebuild_filtered_deck(dyn["id"]):
|
||||
return showWarning(_("No cards matched the criteria you provided."))
|
||||
return showWarning(tr(TR.CUSTOM_STUDY_NO_CARDS_MATCHED_THE_CRITERIA_YOU))
|
||||
self.mw.moveToState("overview")
|
||||
QDialog.accept(self)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# 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
|
||||
|
||||
from copy import deepcopy
|
||||
|
|
@ -16,7 +15,7 @@ from aqt import AnkiQt, gui_hooks
|
|||
from aqt.qt import *
|
||||
from aqt.sound import av_player
|
||||
from aqt.toolbar import BottomBar
|
||||
from aqt.utils import askUser, getOnlyText, openLink, shortcut, showWarning, tr
|
||||
from aqt.utils import TR, askUser, getOnlyText, openLink, shortcut, showWarning, tr
|
||||
|
||||
|
||||
class DeckBrowserBottomBar:
|
||||
|
|
@ -79,7 +78,7 @@ class DeckBrowser:
|
|||
elif cmd == "import":
|
||||
self.mw.onImport()
|
||||
elif cmd == "create":
|
||||
deck = getOnlyText(_("Name for deck:"))
|
||||
deck = getOnlyText(tr(TR.DECKS_NAME_FOR_DECK))
|
||||
if deck:
|
||||
self.mw.col.decks.id(deck)
|
||||
gui_hooks.sidebar_should_refresh_decks()
|
||||
|
|
@ -144,9 +143,9 @@ class DeckBrowser:
|
|||
buf = """
|
||||
<tr><th colspan=5 align=start>%s</th><th class=count>%s</th>
|
||||
<th class=count>%s</th><th class=optscol></th></tr>""" % (
|
||||
_("Deck"),
|
||||
tr(TR.DECKS_DECK),
|
||||
tr(TR.STATISTICS_DUE_COUNT),
|
||||
_("New"),
|
||||
tr(TR.ACTIONS_NEW),
|
||||
)
|
||||
buf += self._topLevelDragRow()
|
||||
|
||||
|
|
@ -225,13 +224,13 @@ class DeckBrowser:
|
|||
|
||||
def _showOptions(self, did: str) -> None:
|
||||
m = QMenu(self.mw)
|
||||
a = m.addAction(_("Rename"))
|
||||
a = m.addAction(tr(TR.ACTIONS_RENAME))
|
||||
qconnect(a.triggered, lambda b, did=did: self._rename(int(did)))
|
||||
a = m.addAction(_("Options"))
|
||||
a = m.addAction(tr(TR.ACTIONS_OPTIONS))
|
||||
qconnect(a.triggered, lambda b, did=did: self._options(did))
|
||||
a = m.addAction(_("Export"))
|
||||
a = m.addAction(tr(TR.ACTIONS_EXPORT))
|
||||
qconnect(a.triggered, lambda b, did=did: self._export(did))
|
||||
a = m.addAction(_("Delete"))
|
||||
a = m.addAction(tr(TR.ACTIONS_DELETE))
|
||||
qconnect(a.triggered, lambda b, did=did: self._delete(int(did)))
|
||||
gui_hooks.deck_browser_will_show_options_menu(m, int(did))
|
||||
m.exec_(QCursor.pos())
|
||||
|
|
@ -240,10 +239,10 @@ class DeckBrowser:
|
|||
self.mw.onExport(did=did)
|
||||
|
||||
def _rename(self, did: int) -> None:
|
||||
self.mw.checkpoint(_("Rename Deck"))
|
||||
self.mw.checkpoint(tr(TR.ACTIONS_RENAME_DECK))
|
||||
deck = self.mw.col.decks.get(did)
|
||||
oldName = deck["name"]
|
||||
newName = getOnlyText(_("New deck name:"), default=oldName)
|
||||
newName = getOnlyText(tr(TR.DECKS_NEW_DECK_NAME), default=oldName)
|
||||
newName = newName.replace('"', "")
|
||||
if not newName or newName == oldName:
|
||||
return
|
||||
|
|
@ -277,7 +276,7 @@ class DeckBrowser:
|
|||
self.show()
|
||||
|
||||
def _delete(self, did):
|
||||
self.mw.checkpoint(_("Delete Deck"))
|
||||
self.mw.checkpoint(tr(TR.DECKS_DELETE_DECK))
|
||||
deck = self.mw.col.decks.get(did)
|
||||
if not deck["dyn"]:
|
||||
dids = [did] + [r[1] for r in self.mw.col.decks.children(did)]
|
||||
|
|
@ -293,7 +292,7 @@ class DeckBrowser:
|
|||
deck["dyn"]
|
||||
or not extra
|
||||
or askUser(
|
||||
(_("Are you sure you wish to delete %s?") % deck["name"]) + extra
|
||||
(tr(TR.DECKS_ARE_YOU_SURE_YOU_WISH_TO, val="%s") % deck["name"]) + extra
|
||||
)
|
||||
):
|
||||
self.mw.progress.start()
|
||||
|
|
@ -305,9 +304,9 @@ class DeckBrowser:
|
|||
######################################################################
|
||||
|
||||
drawLinks = [
|
||||
["", "shared", _("Get Shared")],
|
||||
["", "create", _("Create Deck")],
|
||||
["Ctrl+Shift+I", "import", _("Import File")],
|
||||
["", "shared", tr(TR.DECKS_GET_SHARED)],
|
||||
["", "create", tr(TR.DECKS_CREATE_DECK)],
|
||||
["Ctrl+Shift+I", "import", tr(TR.DECKS_IMPORT_FILE)],
|
||||
]
|
||||
|
||||
def _drawButtons(self):
|
||||
|
|
@ -315,7 +314,7 @@ class DeckBrowser:
|
|||
drawLinks = deepcopy(self.drawLinks)
|
||||
for b in drawLinks:
|
||||
if b[0]:
|
||||
b[0] = _("Shortcut key: %s") % shortcut(b[0])
|
||||
b[0] = tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % shortcut(b[0])
|
||||
buf += """
|
||||
<button title='%s' onclick='pycmd(\"%s\");'>%s</button>""" % tuple(
|
||||
b
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# 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 shortcut
|
||||
from aqt.utils import TR, shortcut, tr
|
||||
|
||||
|
||||
class DeckChooser(QHBoxLayout):
|
||||
|
|
@ -26,12 +25,12 @@ class DeckChooser(QHBoxLayout):
|
|||
|
||||
def setupDecks(self) -> None:
|
||||
if self.label:
|
||||
self.deckLabel = QLabel(_("Deck"))
|
||||
self.deckLabel = QLabel(tr(TR.DECKS_DECK))
|
||||
self.addWidget(self.deckLabel)
|
||||
# decks box
|
||||
self.deck = QPushButton(clicked=self.onDeckChange) # type: ignore
|
||||
self.deck.setAutoDefault(False)
|
||||
self.deck.setToolTip(shortcut(_("Target Deck (Ctrl+D)")))
|
||||
self.deck.setToolTip(shortcut(tr(TR.QT_MISC_TARGET_DECK_CTRLANDD)))
|
||||
QShortcut(QKeySequence("Ctrl+D"), self.widget, activated=self.onDeckChange) # type: ignore
|
||||
self.addWidget(self.deck)
|
||||
# starting label
|
||||
|
|
@ -48,11 +47,13 @@ class DeckChooser(QHBoxLayout):
|
|||
did = c.odid
|
||||
else:
|
||||
did = 1
|
||||
self.setDeckName(self.mw.col.decks.nameOrNone(did) or _("Default"))
|
||||
self.setDeckName(
|
||||
self.mw.col.decks.nameOrNone(did) or tr(TR.QT_MISC_DEFAULT)
|
||||
)
|
||||
else:
|
||||
self.setDeckName(
|
||||
self.mw.col.decks.nameOrNone(self.mw.col.models.current()["did"])
|
||||
or _("Default")
|
||||
or tr(TR.QT_MISC_DEFAULT)
|
||||
)
|
||||
# layout
|
||||
sizePolicy = QSizePolicy(QSizePolicy.Policy(7), QSizePolicy.Policy(0))
|
||||
|
|
@ -74,7 +75,7 @@ class DeckChooser(QHBoxLayout):
|
|||
if not self.mw.col.conf.get("addToCur", True):
|
||||
self.setDeckName(
|
||||
self.mw.col.decks.nameOrNone(self.mw.col.models.current()["did"])
|
||||
or _("Default")
|
||||
or tr(TR.QT_MISC_DEFAULT)
|
||||
)
|
||||
|
||||
def onDeckChange(self) -> None:
|
||||
|
|
@ -84,8 +85,8 @@ class DeckChooser(QHBoxLayout):
|
|||
ret = StudyDeck(
|
||||
self.mw,
|
||||
current=current,
|
||||
accept=_("Choose"),
|
||||
title=_("Choose Deck"),
|
||||
accept=tr(TR.ACTIONS_CHOOSE),
|
||||
title=tr(TR.QT_MISC_CHOOSE_DECK),
|
||||
help="addingnotes",
|
||||
cancel=False,
|
||||
parent=self.widget,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# -*- coding: utf-8 -*-
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
from operator import itemgetter
|
||||
from typing import Any, Dict
|
||||
|
||||
|
|
@ -13,6 +12,7 @@ from anki.lang import _, ngettext
|
|||
from aqt import gui_hooks
|
||||
from aqt.qt import *
|
||||
from aqt.utils import (
|
||||
TR,
|
||||
askUser,
|
||||
getOnlyText,
|
||||
openHelp,
|
||||
|
|
@ -21,6 +21,7 @@ from aqt.utils import (
|
|||
showInfo,
|
||||
showWarning,
|
||||
tooltip,
|
||||
tr,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ class DeckConf(QDialog):
|
|||
self.form = aqt.forms.dconf.Ui_Dialog()
|
||||
self.form.setupUi(self)
|
||||
gui_hooks.deck_conf_did_setup_ui_form(self)
|
||||
self.mw.checkpoint(_("Options"))
|
||||
self.mw.checkpoint(tr(TR.ACTIONS_OPTIONS))
|
||||
self.setupCombos()
|
||||
self.setupConfs()
|
||||
self.setWindowModality(Qt.WindowModal)
|
||||
|
|
@ -44,7 +45,7 @@ class DeckConf(QDialog):
|
|||
self.form.buttonBox.button(QDialogButtonBox.RestoreDefaults).clicked,
|
||||
self.onRestore,
|
||||
)
|
||||
self.setWindowTitle(_("Options for %s") % self.deck["name"])
|
||||
self.setWindowTitle(tr(TR.ACTIONS_OPTIONS_FOR, val="%s") % self.deck["name"])
|
||||
# qt doesn't size properly with altered fonts otherwise
|
||||
restoreGeom(self, "deckconf", adjustSize=True)
|
||||
gui_hooks.deck_conf_will_show(self)
|
||||
|
|
@ -86,13 +87,13 @@ class DeckConf(QDialog):
|
|||
|
||||
def confOpts(self):
|
||||
m = QMenu(self.mw)
|
||||
a = m.addAction(_("Add"))
|
||||
a = m.addAction(tr(TR.ACTIONS_ADD))
|
||||
qconnect(a.triggered, self.addGroup)
|
||||
a = m.addAction(_("Delete"))
|
||||
a = m.addAction(tr(TR.ACTIONS_DELETE))
|
||||
qconnect(a.triggered, self.remGroup)
|
||||
a = m.addAction(_("Rename"))
|
||||
a = m.addAction(tr(TR.ACTIONS_RENAME))
|
||||
qconnect(a.triggered, self.renameGroup)
|
||||
a = m.addAction(_("Set for all subdecks"))
|
||||
a = m.addAction(tr(TR.SCHEDULING_SET_FOR_ALL_SUBDECKS))
|
||||
qconnect(a.triggered, self.setChildren)
|
||||
if not self.childDids:
|
||||
a.setEnabled(False)
|
||||
|
|
@ -118,7 +119,7 @@ class DeckConf(QDialog):
|
|||
self.form.count.setText(txt)
|
||||
|
||||
def addGroup(self) -> None:
|
||||
name = getOnlyText(_("New options group name:"))
|
||||
name = getOnlyText(tr(TR.SCHEDULING_NEW_OPTIONS_GROUP_NAME))
|
||||
if not name:
|
||||
return
|
||||
|
||||
|
|
@ -134,7 +135,7 @@ class DeckConf(QDialog):
|
|||
|
||||
def remGroup(self) -> None:
|
||||
if int(self.conf["id"]) == 1:
|
||||
showInfo(_("The default configuration can't be removed."), self)
|
||||
showInfo(tr(TR.SCHEDULING_THE_DEFAULT_CONFIGURATION_CANT_BE_REMOVED), self)
|
||||
else:
|
||||
gui_hooks.deck_conf_will_remove_config(self, self.deck, self.conf)
|
||||
self.mw.col.modSchema(check=True)
|
||||
|
|
@ -145,7 +146,7 @@ class DeckConf(QDialog):
|
|||
|
||||
def renameGroup(self) -> None:
|
||||
old = self.conf["name"]
|
||||
name = getOnlyText(_("New name:"), default=old)
|
||||
name = getOnlyText(tr(TR.ACTIONS_NEW_NAME), default=old)
|
||||
if not name or name == old:
|
||||
return
|
||||
|
||||
|
|
@ -156,7 +157,7 @@ class DeckConf(QDialog):
|
|||
|
||||
def setChildren(self):
|
||||
if not askUser(
|
||||
_("Set all decks below %s to this option group?") % self.deck["name"]
|
||||
tr(TR.SCHEDULING_SET_ALL_DECKS_BELOW_TO, val="%s") % self.deck["name"]
|
||||
):
|
||||
return
|
||||
for did in self.childDids:
|
||||
|
|
@ -194,7 +195,7 @@ class DeckConf(QDialog):
|
|||
lim = x
|
||||
else:
|
||||
lim = min(x, lim)
|
||||
return _("(parent limit: %d)") % lim
|
||||
return tr(TR.SCHEDULING_PARENT_LIMIT, val="%s") % lim
|
||||
|
||||
def loadConf(self):
|
||||
self.conf = self.mw.col.decks.confForDid(self.deck["id"])
|
||||
|
|
@ -273,10 +274,10 @@ class DeckConf(QDialog):
|
|||
ret.append(i)
|
||||
except:
|
||||
# invalid, don't update
|
||||
showWarning(_("Steps must be numbers."))
|
||||
showWarning(tr(TR.SCHEDULING_STEPS_MUST_BE_NUMBERS))
|
||||
return
|
||||
if len(ret) < minSize:
|
||||
showWarning(_("At least one step is required."))
|
||||
showWarning(tr(TR.SCHEDULING_AT_LEAST_ONE_STEP_IS_REQUIRED))
|
||||
return
|
||||
conf[key] = ret
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typing import List, Optional
|
|||
import aqt
|
||||
from anki.lang import _
|
||||
from aqt.qt import *
|
||||
from aqt.utils import askUser, openHelp, restoreGeom, saveGeom, showWarning
|
||||
from aqt.utils import TR, askUser, openHelp, restoreGeom, saveGeom, showWarning, tr
|
||||
|
||||
|
||||
class DeckConf(QDialog):
|
||||
|
|
@ -18,14 +18,14 @@ class DeckConf(QDialog):
|
|||
self.form = aqt.forms.dyndconf.Ui_Dialog()
|
||||
self.form.setupUi(self)
|
||||
if first:
|
||||
label = _("Build")
|
||||
label = tr(TR.DECKS_BUILD)
|
||||
else:
|
||||
label = _("Rebuild")
|
||||
label = tr(TR.ACTIONS_REBUILD)
|
||||
self.ok = self.form.buttonBox.addButton(label, QDialogButtonBox.AcceptRole)
|
||||
self.mw.checkpoint(_("Options"))
|
||||
self.mw.checkpoint(tr(TR.ACTIONS_OPTIONS))
|
||||
self.setWindowModality(Qt.WindowModal)
|
||||
qconnect(self.form.buttonBox.helpRequested, lambda: openHelp("filtered-decks"))
|
||||
self.setWindowTitle(_("Options for %s") % self.deck["name"])
|
||||
self.setWindowTitle(tr(TR.ACTIONS_OPTIONS_FOR, val="%s") % self.deck["name"])
|
||||
restoreGeom(self, "dyndeckconf")
|
||||
self.initialSetup()
|
||||
self.loadConf()
|
||||
|
|
@ -154,9 +154,9 @@ it?"""
|
|||
ret.append(i)
|
||||
except:
|
||||
# invalid, don't update
|
||||
showWarning(_("Steps must be numbers."))
|
||||
showWarning(tr(TR.SCHEDULING_STEPS_MUST_BE_NUMBERS))
|
||||
return None
|
||||
if len(ret) < minSize:
|
||||
showWarning(_("At least one step is required."))
|
||||
showWarning(tr(TR.SCHEDULING_AT_LEAST_ONE_STEP_IS_REQUIRED))
|
||||
return None
|
||||
return ret
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# -*- 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 *
|
||||
from aqt.utils import restoreGeom, saveGeom, tooltip
|
||||
from aqt.utils import TR, restoreGeom, saveGeom, tooltip, tr
|
||||
|
||||
|
||||
class EditCurrent(QDialog):
|
||||
|
|
@ -17,7 +16,7 @@ class EditCurrent(QDialog):
|
|||
self.mw = mw
|
||||
self.form = aqt.forms.editcurrent.Ui_Dialog()
|
||||
self.form.setupUi(self)
|
||||
self.setWindowTitle(_("Edit Current"))
|
||||
self.setWindowTitle(tr(TR.EDITING_EDIT_CURRENT))
|
||||
self.setMinimumHeight(400)
|
||||
self.setMinimumWidth(250)
|
||||
self.form.buttonBox.button(QDialogButtonBox.Close).setShortcut(
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ from aqt.qt import *
|
|||
from aqt.sound import av_player, getAudio
|
||||
from aqt.theme import theme_manager
|
||||
from aqt.utils import (
|
||||
TR,
|
||||
getFile,
|
||||
openHelp,
|
||||
qtMenuShortcutWorkaround,
|
||||
|
|
@ -40,6 +41,7 @@ from aqt.utils import (
|
|||
showInfo,
|
||||
showWarning,
|
||||
tooltip,
|
||||
tr,
|
||||
)
|
||||
from aqt.webview import AnkiWebView
|
||||
|
||||
|
|
@ -122,16 +124,16 @@ class Editor:
|
|||
self._addButton(
|
||||
None,
|
||||
"fields",
|
||||
_("Customize Fields"),
|
||||
_("Fields") + "...",
|
||||
tr(TR.EDITING_CUSTOMIZE_FIELDS),
|
||||
tr(TR.EDITING_FIELDS) + "...",
|
||||
disables=False,
|
||||
rightside=False,
|
||||
),
|
||||
self._addButton(
|
||||
None,
|
||||
"cards",
|
||||
_("Customize Card Templates (Ctrl+L)"),
|
||||
_("Cards") + "...",
|
||||
tr(TR.EDITING_CUSTOMIZE_CARD_TEMPLATES_CTRLANDL),
|
||||
tr(TR.EDITING_CARDS) + "...",
|
||||
disables=False,
|
||||
rightside=False,
|
||||
),
|
||||
|
|
@ -140,22 +142,37 @@ class Editor:
|
|||
gui_hooks.editor_did_init_left_buttons(lefttopbtns, self)
|
||||
|
||||
righttopbtns: List[str] = [
|
||||
self._addButton("text_bold", "bold", _("Bold text (Ctrl+B)"), id="bold"),
|
||||
self._addButton(
|
||||
"text_italic", "italic", _("Italic text (Ctrl+I)"), id="italic"
|
||||
"text_bold", "bold", tr(TR.EDITING_BOLD_TEXT_CTRLANDB), id="bold"
|
||||
),
|
||||
self._addButton(
|
||||
"text_under", "underline", _("Underline text (Ctrl+U)"), id="underline"
|
||||
"text_italic",
|
||||
"italic",
|
||||
tr(TR.EDITING_ITALIC_TEXT_CTRLANDI),
|
||||
id="italic",
|
||||
),
|
||||
self._addButton(
|
||||
"text_super", "super", _("Superscript (Ctrl++)"), id="superscript"
|
||||
"text_under",
|
||||
"underline",
|
||||
tr(TR.EDITING_UNDERLINE_TEXT_CTRLANDU),
|
||||
id="underline",
|
||||
),
|
||||
self._addButton(
|
||||
"text_super",
|
||||
"super",
|
||||
tr(TR.EDITING_SUPERSCRIPT_CTRLANDAND),
|
||||
id="superscript",
|
||||
),
|
||||
self._addButton(
|
||||
"text_sub", "sub", tr(TR.EDITING_SUBSCRIPT_CTRLAND), id="subscript"
|
||||
),
|
||||
self._addButton(
|
||||
"text_clear", "clear", tr(TR.EDITING_REMOVE_FORMATTING_CTRLANDR)
|
||||
),
|
||||
self._addButton("text_sub", "sub", _("Subscript (Ctrl+=)"), id="subscript"),
|
||||
self._addButton("text_clear", "clear", _("Remove formatting (Ctrl+R)")),
|
||||
self._addButton(
|
||||
None,
|
||||
"colour",
|
||||
_("Set foreground colour (F7)"),
|
||||
tr(TR.EDITING_SET_FOREGROUND_COLOUR_F7),
|
||||
"""
|
||||
<div id="forecolor"
|
||||
style="display: inline-block; background: #000; border-radius: 5px;"
|
||||
|
|
@ -165,17 +182,19 @@ class Editor:
|
|||
self._addButton(
|
||||
None,
|
||||
"changeCol",
|
||||
_("Change colour (F8)"),
|
||||
tr(TR.EDITING_CHANGE_COLOUR_F8),
|
||||
"""
|
||||
<div style="display: inline-block; border-radius: 5px;"
|
||||
class="topbut rainbow"
|
||||
>""",
|
||||
),
|
||||
self._addButton("text_cloze", "cloze", _("Cloze deletion (Ctrl+Shift+C)")),
|
||||
self._addButton(
|
||||
"paperclip", "attach", _("Attach pictures/audio/video (F3)")
|
||||
"text_cloze", "cloze", tr(TR.EDITING_CLOZE_DELETION_CTRLANDSHIFTANDC)
|
||||
),
|
||||
self._addButton("media-record", "record", _("Record audio (F5)")),
|
||||
self._addButton(
|
||||
"paperclip", "attach", tr(TR.EDITING_ATTACH_PICTURESAUDIOVIDEO_F3)
|
||||
),
|
||||
self._addButton("media-record", "record", tr(TR.EDITING_RECORD_AUDIO_F5)),
|
||||
self._addButton("more", "more"),
|
||||
]
|
||||
|
||||
|
|
@ -197,7 +216,7 @@ class Editor:
|
|||
bgcol = self.mw.app.palette().window().color().name() # type: ignore
|
||||
# then load page
|
||||
self.web.stdHtml(
|
||||
_html % (bgcol, bgcol, topbuts, _("Show Duplicates")),
|
||||
_html % (bgcol, bgcol, topbuts, tr(TR.EDITING_SHOW_DUPLICATES)),
|
||||
css=["css/editor.css"],
|
||||
js=["js/vendor/jquery.js", "js/editor.js"],
|
||||
context=self,
|
||||
|
|
@ -571,11 +590,13 @@ class Editor:
|
|||
tb.setSpacing(12)
|
||||
tb.setContentsMargins(2, 6, 2, 6)
|
||||
# tags
|
||||
l = QLabel(_("Tags"))
|
||||
l = QLabel(tr(TR.EDITING_TAGS))
|
||||
tb.addWidget(l, 1, 0)
|
||||
self.tags = aqt.tagedit.TagEdit(self.widget)
|
||||
qconnect(self.tags.lostFocus, self.saveTags)
|
||||
self.tags.setToolTip(shortcut(_("Jump to tags with Ctrl+Shift+T")))
|
||||
self.tags.setToolTip(
|
||||
shortcut(tr(TR.EDITING_JUMP_TO_TAGS_WITH_CTRLANDSHIFTANDT))
|
||||
)
|
||||
border = theme_manager.str_color("border")
|
||||
self.tags.setStyleSheet(f"border: 1px solid {border}")
|
||||
tb.addWidget(self.tags, 1, 1)
|
||||
|
|
@ -708,12 +729,12 @@ to a cloze type first, via 'Notes>Change Note Type'"""
|
|||
extension_filter = " ".join(
|
||||
"*." + extension for extension in sorted(itertools.chain(pics, audio))
|
||||
)
|
||||
key = _("Media") + " (" + extension_filter + ")"
|
||||
key = tr(TR.EDITING_MEDIA) + " (" + extension_filter + ")"
|
||||
|
||||
def accept(file):
|
||||
self.addMedia(file, canDelete=True)
|
||||
|
||||
file = getFile(self.widget, _("Add Media"), accept, key, key="media")
|
||||
file = getFile(self.widget, tr(TR.EDITING_ADD_MEDIA), accept, key, key="media")
|
||||
self.parentWindow.activateWindow()
|
||||
|
||||
def addMedia(self, path, canDelete=False):
|
||||
|
|
@ -746,7 +767,7 @@ to a cloze type first, via 'Notes>Change Note Type'"""
|
|||
file = getAudio(self.widget)
|
||||
except Exception as e:
|
||||
showWarning(
|
||||
_("Couldn't record audio. Have you installed 'lame'?")
|
||||
tr(TR.EDITING_COULDNT_RECORD_AUDIO_HAVE_YOU_INSTALLED)
|
||||
+ "\n\n"
|
||||
+ repr(str(e))
|
||||
)
|
||||
|
|
@ -845,13 +866,14 @@ to a cloze type first, via 'Notes>Change Note Type'"""
|
|||
with client.get(url) as response:
|
||||
if response.status_code != 200:
|
||||
error_msg = (
|
||||
_("Unexpected response code: %s") % response.status_code
|
||||
tr(TR.QT_MISC_UNEXPECTED_RESPONSE_CODE, val="%s")
|
||||
% response.status_code
|
||||
)
|
||||
return None
|
||||
filecontents = response.content
|
||||
content_type = response.headers.get("content-type")
|
||||
except (urllib.error.URLError, requests.exceptions.RequestException) as e:
|
||||
error_msg = _("An error occurred while opening %s") % e
|
||||
error_msg = tr(TR.EDITING_AN_ERROR_OCCURRED_WHILE_OPENING, val="%s") % e
|
||||
return None
|
||||
finally:
|
||||
self.mw.progress.finish()
|
||||
|
|
@ -948,13 +970,17 @@ to a cloze type first, via 'Notes>Change Note Type'"""
|
|||
m = QMenu(self.mw)
|
||||
|
||||
for text, handler, shortcut in (
|
||||
(_("MathJax inline"), self.insertMathjaxInline, "Ctrl+M, M"),
|
||||
(_("MathJax block"), self.insertMathjaxBlock, "Ctrl+M, E"),
|
||||
(_("MathJax chemistry"), self.insertMathjaxChemistry, "Ctrl+M, C"),
|
||||
(_("LaTeX"), self.insertLatex, "Ctrl+T, T"),
|
||||
(_("LaTeX equation"), self.insertLatexEqn, "Ctrl+T, E"),
|
||||
(_("LaTeX math env."), self.insertLatexMathEnv, "Ctrl+T, M"),
|
||||
(_("Edit HTML"), self.onHtmlEdit, "Ctrl+Shift+X"),
|
||||
(tr(TR.EDITING_MATHJAX_INLINE), self.insertMathjaxInline, "Ctrl+M, M"),
|
||||
(tr(TR.EDITING_MATHJAX_BLOCK), self.insertMathjaxBlock, "Ctrl+M, E"),
|
||||
(
|
||||
tr(TR.EDITING_MATHJAX_CHEMISTRY),
|
||||
self.insertMathjaxChemistry,
|
||||
"Ctrl+M, C",
|
||||
),
|
||||
(tr(TR.EDITING_LATEX), self.insertLatex, "Ctrl+T, T"),
|
||||
(tr(TR.EDITING_LATEX_EQUATION), self.insertLatexEqn, "Ctrl+T, E"),
|
||||
(tr(TR.EDITING_LATEX_MATH_ENV), self.insertLatexMathEnv, "Ctrl+T, M"),
|
||||
(tr(TR.EDITING_EDIT_HTML), self.onHtmlEdit, "Ctrl+Shift+X"),
|
||||
):
|
||||
a = m.addAction(text)
|
||||
qconnect(a.triggered, handler)
|
||||
|
|
@ -1204,11 +1230,11 @@ class EditorWebView(AnkiWebView):
|
|||
|
||||
def contextMenuEvent(self, evt: QContextMenuEvent) -> None:
|
||||
m = QMenu(self)
|
||||
a = m.addAction(_("Cut"))
|
||||
a = m.addAction(tr(TR.EDITING_CUT))
|
||||
qconnect(a.triggered, self.onCut)
|
||||
a = m.addAction(_("Copy"))
|
||||
a = m.addAction(tr(TR.ACTIONS_COPY))
|
||||
qconnect(a.triggered, self.onCopy)
|
||||
a = m.addAction(_("Paste"))
|
||||
a = m.addAction(tr(TR.EDITING_PASTE))
|
||||
qconnect(a.triggered, self.onPaste)
|
||||
gui_hooks.editor_will_show_context_menu(self, m)
|
||||
m.popup(QCursor.pos())
|
||||
|
|
|
|||
|
|
@ -77,11 +77,7 @@ your system's temporary folder may be incorrect."""
|
|||
if "abortSchemaMod" in error:
|
||||
return
|
||||
if "10013" in error:
|
||||
return showWarning(
|
||||
_(
|
||||
"Your firewall or antivirus program is preventing Anki from creating a connection to itself. Please add an exception for Anki."
|
||||
)
|
||||
)
|
||||
return showWarning(tr(TR.QT_MISC_YOUR_FIREWALL_OR_ANTIVIRUS_PROGRAM_IS))
|
||||
if "no default input" in error.lower():
|
||||
return showWarning(
|
||||
_(
|
||||
|
|
@ -94,11 +90,7 @@ your system's temporary folder may be incorrect."""
|
|||
if "Beautiful Soup is not an HTTP client" in error:
|
||||
return
|
||||
if "database or disk is full" in error or "Errno 28" in error:
|
||||
return showWarning(
|
||||
_(
|
||||
"Your computer's storage may be full. Please delete some unneeded files, then try again."
|
||||
)
|
||||
)
|
||||
return showWarning(tr(TR.QT_MISC_YOUR_COMPUTERS_STORAGE_MAY_BE_FULL))
|
||||
if "disk I/O error" in error:
|
||||
showWarning(markdown(tr(TR.ERRORS_ACCESSING_DB)))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from anki import hooks
|
|||
from anki.exporting import Exporter, exporters
|
||||
from anki.lang import _, ngettext
|
||||
from aqt.qt import *
|
||||
from aqt.utils import checkInvalidFilename, getSaveFile, showWarning, tooltip
|
||||
from aqt.utils import TR, checkInvalidFilename, getSaveFile, showWarning, tooltip, tr
|
||||
|
||||
|
||||
class ExportDialog(QDialog):
|
||||
|
|
@ -49,13 +49,13 @@ class ExportDialog(QDialog):
|
|||
self.exporterChanged(idx)
|
||||
# deck list
|
||||
if self.cids is None:
|
||||
self.decks = [_("All Decks")]
|
||||
self.decks = [tr(TR.EXPORTING_ALL_DECKS)]
|
||||
self.decks.extend(d.name for d in self.col.decks.all_names_and_ids())
|
||||
else:
|
||||
self.decks = [_("Selected Notes")]
|
||||
self.decks = [tr(TR.EXPORTING_SELECTED_NOTES)]
|
||||
self.frm.deck.addItems(self.decks)
|
||||
# save button
|
||||
b = QPushButton(_("Export..."))
|
||||
b = QPushButton(tr(TR.EXPORTING_EXPORT))
|
||||
self.frm.buttonBox.addButton(b, QDialogButtonBox.AcceptRole)
|
||||
# set default option if accessed through deck button
|
||||
if did:
|
||||
|
|
@ -107,7 +107,7 @@ class ExportDialog(QDialog):
|
|||
self.exporter.did = self.col.decks.id(name)
|
||||
if self.isVerbatim:
|
||||
name = time.strftime("-%Y-%m-%d@%H-%M-%S", time.localtime(time.time()))
|
||||
deck_name = _("collection") + name
|
||||
deck_name = tr(TR.EXPORTING_COLLECTION) + name
|
||||
else:
|
||||
# Get deck name and remove invalid filename characters
|
||||
deck_name = self.decks[self.frm.deck.currentIndex()]
|
||||
|
|
@ -121,7 +121,7 @@ class ExportDialog(QDialog):
|
|||
while 1:
|
||||
file = getSaveFile(
|
||||
self,
|
||||
_("Export"),
|
||||
tr(TR.ACTIONS_EXPORT),
|
||||
"export",
|
||||
key_str,
|
||||
self.exporter.ext,
|
||||
|
|
@ -142,7 +142,7 @@ class ExportDialog(QDialog):
|
|||
f = open(file, "wb")
|
||||
f.close()
|
||||
except (OSError, IOError) as e:
|
||||
showWarning(_("Couldn't save file: %s") % str(e))
|
||||
showWarning(tr(TR.EXPORTING_COULDNT_SAVE_FILE, val="%s") % str(e))
|
||||
else:
|
||||
os.unlink(file)
|
||||
|
||||
|
|
@ -174,7 +174,7 @@ class ExportDialog(QDialog):
|
|||
|
||||
def on_export_finished(self):
|
||||
if self.isVerbatim:
|
||||
msg = _("Collection exported.")
|
||||
msg = tr(TR.EXPORTING_COLLECTION_EXPORTED)
|
||||
self.mw.reopen()
|
||||
else:
|
||||
if self.isTextNote:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from anki.rsbackend import TemplateError
|
|||
from aqt import AnkiQt, gui_hooks
|
||||
from aqt.qt import *
|
||||
from aqt.schema_change_tracker import ChangeTracker
|
||||
from aqt.utils import askUser, getOnlyText, openHelp, showWarning, tooltip
|
||||
from aqt.utils import TR, askUser, getOnlyText, openHelp, showWarning, tooltip, tr
|
||||
|
||||
|
||||
class FieldDialog(QDialog):
|
||||
|
|
@ -20,11 +20,11 @@ class FieldDialog(QDialog):
|
|||
self.mm = self.mw.col.models
|
||||
self.model = nt
|
||||
self.mm._remove_from_cache(self.model["id"])
|
||||
self.mw.checkpoint(_("Fields"))
|
||||
self.mw.checkpoint(tr(TR.EDITING_FIELDS))
|
||||
self.change_tracker = ChangeTracker(self.mw)
|
||||
self.form = aqt.forms.fields.Ui_Dialog()
|
||||
self.form.setupUi(self)
|
||||
self.setWindowTitle(_("Fields for %s") % self.model["name"])
|
||||
self.setWindowTitle(tr(TR.FIELDS_FIELDS_FOR, val="%s") % self.model["name"])
|
||||
self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False)
|
||||
self.form.buttonBox.button(QDialogButtonBox.Cancel).setAutoDefault(False)
|
||||
self.form.buttonBox.button(QDialogButtonBox.Save).setAutoDefault(False)
|
||||
|
|
@ -87,14 +87,14 @@ class FieldDialog(QDialog):
|
|||
if ignoreOrd is not None and f["ord"] == ignoreOrd:
|
||||
continue
|
||||
if f["name"] == txt:
|
||||
showWarning(_("That field name is already used."))
|
||||
showWarning(tr(TR.FIELDS_THAT_FIELD_NAME_IS_ALREADY_USED))
|
||||
return
|
||||
return txt
|
||||
|
||||
def onRename(self):
|
||||
idx = self.currentIdx
|
||||
f = self.model["flds"][idx]
|
||||
name = self._uniqueName(_("New name:"), self.currentIdx, f["name"])
|
||||
name = self._uniqueName(tr(TR.ACTIONS_NEW_NAME), self.currentIdx, f["name"])
|
||||
if not name:
|
||||
return
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ class FieldDialog(QDialog):
|
|||
self.form.fieldList.setCurrentRow(idx)
|
||||
|
||||
def onAdd(self):
|
||||
name = self._uniqueName(_("Field name:"))
|
||||
name = self._uniqueName(tr(TR.FIELDS_FIELD_NAME))
|
||||
if not name:
|
||||
return
|
||||
if not self.change_tracker.mark_schema():
|
||||
|
|
@ -120,10 +120,10 @@ class FieldDialog(QDialog):
|
|||
|
||||
def onDelete(self):
|
||||
if len(self.model["flds"]) < 2:
|
||||
return showWarning(_("Notes require at least one field."))
|
||||
return showWarning(tr(TR.FIELDS_NOTES_REQUIRE_AT_LEAST_ONE_FIELD))
|
||||
count = self.mm.useCount(self.model)
|
||||
c = ngettext("%d note", "%d notes", count) % count
|
||||
if not askUser(_("Delete field from %s?") % c):
|
||||
if not askUser(tr(TR.FIELDS_DELETE_FIELD_FROM, val="%s") % c):
|
||||
return
|
||||
if not self.change_tracker.mark_schema():
|
||||
return
|
||||
|
|
@ -137,7 +137,9 @@ class FieldDialog(QDialog):
|
|||
def onPosition(self, delta=-1):
|
||||
idx = self.currentIdx
|
||||
l = len(self.model["flds"])
|
||||
txt = getOnlyText(_("New position (1...%d):") % l, default=str(idx + 1))
|
||||
txt = getOnlyText(
|
||||
tr(TR.FIELDS_NEW_POSITION_1, val="%s") % l, default=str(idx + 1)
|
||||
)
|
||||
if not txt:
|
||||
return
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# coding=utf-8
|
||||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
|
@ -43,14 +42,14 @@ class ChangeMap(QDialog):
|
|||
n = 0
|
||||
setCurrent = False
|
||||
for field in self.model["flds"]:
|
||||
item = QListWidgetItem(_("Map to %s") % field["name"])
|
||||
item = QListWidgetItem(tr(TR.IMPORTING_MAP_TO, val="%s") % field["name"])
|
||||
self.frm.fields.addItem(item)
|
||||
if current == field["name"]:
|
||||
setCurrent = True
|
||||
self.frm.fields.setCurrentRow(n)
|
||||
n += 1
|
||||
self.frm.fields.addItem(QListWidgetItem(_("Map to Tags")))
|
||||
self.frm.fields.addItem(QListWidgetItem(_("Ignore field")))
|
||||
self.frm.fields.addItem(QListWidgetItem(tr(TR.IMPORTING_MAP_TO_TAGS)))
|
||||
self.frm.fields.addItem(QListWidgetItem(tr(TR.IMPORTING_IGNORE_FIELD)))
|
||||
if not setCurrent:
|
||||
if current == "_tags":
|
||||
self.frm.fields.setCurrentRow(n)
|
||||
|
|
@ -100,7 +99,7 @@ class ImportDialog(QDialog):
|
|||
self.frm.tagModified.setText(self.mw.pm.profile.get("tagModified", ""))
|
||||
self.frm.tagModified.setCol(self.mw.col)
|
||||
# import button
|
||||
b = QPushButton(_("Import"))
|
||||
b = QPushButton(tr(TR.ACTIONS_IMPORT))
|
||||
self.frm.buttonBox.addButton(b, QDialogButtonBox.AcceptRole)
|
||||
self.exec_()
|
||||
|
||||
|
|
@ -156,24 +155,24 @@ you can enter it here. Use \\t to represent tab."""
|
|||
else:
|
||||
d = self.importer.dialect.delimiter
|
||||
if d == "\t":
|
||||
d = _("Tab")
|
||||
d = tr(TR.IMPORTING_TAB)
|
||||
elif d == ",":
|
||||
d = _("Comma")
|
||||
d = tr(TR.IMPORTING_COMMA)
|
||||
elif d == " ":
|
||||
d = _("Space")
|
||||
d = tr(TR.STUDYING_SPACE)
|
||||
elif d == ";":
|
||||
d = _("Semicolon")
|
||||
d = tr(TR.IMPORTING_SEMICOLON)
|
||||
elif d == ":":
|
||||
d = _("Colon")
|
||||
d = tr(TR.IMPORTING_COLON)
|
||||
else:
|
||||
d = repr(d)
|
||||
txt = _("Fields separated by: %s") % d
|
||||
txt = tr(TR.IMPORTING_FIELDS_SEPARATED_BY, val="%s") % d
|
||||
self.frm.autoDetect.setText(txt)
|
||||
|
||||
def accept(self):
|
||||
self.importer.mapping = self.mapping
|
||||
if not self.importer.mappingOk():
|
||||
showWarning(_("The first field of the note type must be mapped."))
|
||||
showWarning(tr(TR.IMPORTING_THE_FIRST_FIELD_OF_THE_NOTE))
|
||||
return
|
||||
self.importer.importMode = self.frm.importMode.currentIndex()
|
||||
self.mw.pm.profile["importMode"] = self.importer.importMode
|
||||
|
|
@ -186,7 +185,7 @@ you can enter it here. Use \\t to represent tab."""
|
|||
self.mw.col.models.save(self.importer.model, updateReqs=False)
|
||||
self.mw.col.decks.select(did)
|
||||
self.mw.progress.start()
|
||||
self.mw.checkpoint(_("Import"))
|
||||
self.mw.checkpoint(tr(TR.ACTIONS_IMPORT))
|
||||
|
||||
def on_done(future: Future):
|
||||
self.mw.progress.finish()
|
||||
|
|
@ -208,7 +207,7 @@ you can enter it here. Use \\t to represent tab."""
|
|||
showText(msg)
|
||||
return
|
||||
else:
|
||||
txt = _("Importing complete.") + "\n"
|
||||
txt = tr(TR.IMPORTING_IMPORTING_COMPLETE) + "\n"
|
||||
if self.importer.log:
|
||||
txt += "\n".join(self.importer.log)
|
||||
self.close()
|
||||
|
|
@ -248,16 +247,16 @@ you can enter it here. Use \\t to represent tab."""
|
|||
self.grid.setSpacing(6)
|
||||
fields = self.importer.fields()
|
||||
for num in range(len(self.mapping)):
|
||||
text = _("Field <b>%d</b> of file is:") % (num + 1)
|
||||
text = tr(TR.IMPORTING_FIELD_OF_FILE_IS, val="%s") % (num + 1)
|
||||
self.grid.addWidget(QLabel(text), num, 0)
|
||||
if self.mapping[num] == "_tags":
|
||||
text = _("mapped to <b>Tags</b>")
|
||||
text = tr(TR.IMPORTING_MAPPED_TO_TAGS)
|
||||
elif self.mapping[num]:
|
||||
text = _("mapped to <b>%s</b>") % self.mapping[num]
|
||||
text = tr(TR.IMPORTING_MAPPED_TO, val="%s") % self.mapping[num]
|
||||
else:
|
||||
text = _("<ignored>")
|
||||
text = tr(TR.IMPORTING_IGNORED)
|
||||
self.grid.addWidget(QLabel(text), num, 1)
|
||||
button = QPushButton(_("Change"))
|
||||
button = QPushButton(tr(TR.IMPORTING_CHANGE))
|
||||
self.grid.addWidget(button, num, 2)
|
||||
qconnect(button.clicked, lambda _, s=self, n=num: s.changeMappingNum(n))
|
||||
|
||||
|
|
@ -308,7 +307,7 @@ def showUnicodeWarning():
|
|||
|
||||
def onImport(mw):
|
||||
filt = ";;".join([x[0] for x in importing.Importers])
|
||||
file = getFile(mw, _("Import"), None, key="import", filter=filt)
|
||||
file = getFile(mw, tr(TR.ACTIONS_IMPORT), None, key="import", filter=filt)
|
||||
if not file:
|
||||
return
|
||||
file = str(file)
|
||||
|
|
@ -316,18 +315,10 @@ def onImport(mw):
|
|||
head, ext = os.path.splitext(file)
|
||||
ext = ext.lower()
|
||||
if ext == ".anki":
|
||||
showInfo(
|
||||
_(
|
||||
".anki files are from a very old version of Anki. You can import them with Anki 2.0, available on the Anki website."
|
||||
)
|
||||
)
|
||||
showInfo(tr(TR.IMPORTING_ANKI_FILES_ARE_FROM_A_VERY))
|
||||
return
|
||||
elif ext == ".anki2":
|
||||
showInfo(
|
||||
_(
|
||||
".anki2 files are not directly importable - please import the .apkg or .zip file you have received instead."
|
||||
)
|
||||
)
|
||||
showInfo(tr(TR.IMPORTING_ANKI2_FILES_ARE_NOT_DIRECTLY_IMPORTABLE))
|
||||
return
|
||||
|
||||
importFile(mw, file)
|
||||
|
|
@ -364,7 +355,7 @@ def importFile(mw, file):
|
|||
mw.progress.finish()
|
||||
msg = repr(str(e))
|
||||
if msg == "'unknownFormat'":
|
||||
showWarning(_("Unknown file format."))
|
||||
showWarning(tr(TR.IMPORTING_UNKNOWN_FILE_FORMAT))
|
||||
else:
|
||||
msg = tr(TR.IMPORTING_FAILED_DEBUG_INFO) + "\n"
|
||||
msg += str(traceback.format_exc())
|
||||
|
|
@ -513,7 +504,7 @@ def _replaceWithApkg(mw, filename, backup):
|
|||
future.result()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
showWarning(_("The provided file is not a valid .apkg file."))
|
||||
showWarning(tr(TR.IMPORTING_THE_PROVIDED_FILE_IS_NOT_A))
|
||||
return
|
||||
|
||||
if not mw.loadCollection():
|
||||
|
|
@ -521,6 +512,6 @@ def _replaceWithApkg(mw, filename, backup):
|
|||
if backup:
|
||||
mw.col.modSchema(check=False)
|
||||
|
||||
tooltip(_("Importing complete."))
|
||||
tooltip(tr(TR.IMPORTING_IMPORTING_COMPLETE))
|
||||
|
||||
mw.taskman.run_in_background(do_import, on_done)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# 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 enum
|
||||
|
|
@ -120,7 +119,9 @@ class AnkiQt(QMainWindow):
|
|||
self.setupAddons(args)
|
||||
self.finish_ui_setup()
|
||||
except:
|
||||
showInfo(_("Error during startup:\n%s") % traceback.format_exc())
|
||||
showInfo(
|
||||
tr(TR.QT_MISC_ERROR_DURING_STARTUP, val="%s") % traceback.format_exc()
|
||||
)
|
||||
sys.exit(1)
|
||||
# must call this after ui set up
|
||||
if self.safeMode:
|
||||
|
|
@ -291,10 +292,10 @@ class AnkiQt(QMainWindow):
|
|||
return not checkInvalidFilename(name) and name != "addons21"
|
||||
|
||||
def onAddProfile(self):
|
||||
name = getOnlyText(_("Name:")).strip()
|
||||
name = getOnlyText(tr(TR.ACTIONS_NAME)).strip()
|
||||
if name:
|
||||
if name in self.pm.profiles():
|
||||
return showWarning(_("Name exists."))
|
||||
return showWarning(tr(TR.QT_MISC_NAME_EXISTS))
|
||||
if not self.profileNameOk(name):
|
||||
return
|
||||
self.pm.create(name)
|
||||
|
|
@ -302,13 +303,13 @@ class AnkiQt(QMainWindow):
|
|||
self.refreshProfilesList()
|
||||
|
||||
def onRenameProfile(self):
|
||||
name = getOnlyText(_("New name:"), default=self.pm.name).strip()
|
||||
name = getOnlyText(tr(TR.ACTIONS_NEW_NAME), default=self.pm.name).strip()
|
||||
if not name:
|
||||
return
|
||||
if name == self.pm.name:
|
||||
return
|
||||
if name in self.pm.profiles():
|
||||
return showWarning(_("Name exists."))
|
||||
return showWarning(tr(TR.QT_MISC_NAME_EXISTS))
|
||||
if not self.profileNameOk(name):
|
||||
return
|
||||
self.pm.rename(name)
|
||||
|
|
@ -317,7 +318,7 @@ class AnkiQt(QMainWindow):
|
|||
def onRemProfile(self):
|
||||
profs = self.pm.profiles()
|
||||
if len(profs) < 2:
|
||||
return showWarning(_("There must be at least one profile."))
|
||||
return showWarning(tr(TR.QT_MISC_THERE_MUST_BE_AT_LEAST_ONE))
|
||||
# sure?
|
||||
if not askUser(
|
||||
_(
|
||||
|
|
@ -348,7 +349,7 @@ Replace your collection with an earlier backup?"""
|
|||
|
||||
getFile(
|
||||
self.profileDiag,
|
||||
_("Revert to backup"),
|
||||
tr(TR.QT_MISC_REVERT_TO_BACKUP),
|
||||
cb=doOpen,
|
||||
filter="*.colpkg",
|
||||
dir=self.pm.backupFolder(),
|
||||
|
|
@ -359,11 +360,7 @@ Replace your collection with an earlier backup?"""
|
|||
# move the existing collection to the trash, as it may not open
|
||||
self.pm.trashCollection()
|
||||
except:
|
||||
showWarning(
|
||||
_(
|
||||
"Unable to move existing file to trash - please try restarting your computer."
|
||||
)
|
||||
)
|
||||
showWarning(tr(TR.QT_MISC_UNABLE_TO_MOVE_EXISTING_FILE_TO))
|
||||
return
|
||||
|
||||
self.pendingImport = path
|
||||
|
|
@ -553,9 +550,9 @@ close the profile or restart Anki."""
|
|||
if not self.col:
|
||||
return
|
||||
if self.restoringBackup:
|
||||
label = _("Closing...")
|
||||
label = tr(TR.QT_MISC_CLOSING)
|
||||
else:
|
||||
label = _("Backing Up...")
|
||||
label = tr(TR.QT_MISC_BACKING_UP)
|
||||
self.progress.start(label=label)
|
||||
corrupt = False
|
||||
try:
|
||||
|
|
@ -641,7 +638,7 @@ from the profile screen."
|
|||
# have two weeks passed?
|
||||
if (intTime() - self.pm.profile["lastOptimize"]) < 86400 * 14:
|
||||
return
|
||||
self.progress.start(label=_("Optimizing..."))
|
||||
self.progress.start(label=tr(TR.QT_MISC_OPTIMIZING))
|
||||
self.col.optimize()
|
||||
self.pm.profile["lastOptimize"] = intTime()
|
||||
self.pm.save()
|
||||
|
|
@ -672,7 +669,7 @@ from the profile screen."
|
|||
def _selectedDeck(self) -> Optional[Deck]:
|
||||
did = self.col.decks.selected()
|
||||
if not self.col.decks.nameOrNone(did):
|
||||
showInfo(_("Please select a deck."))
|
||||
showInfo(tr(TR.QT_MISC_PLEASE_SELECT_A_DECK))
|
||||
return None
|
||||
return self.col.decks.get(did)
|
||||
|
||||
|
|
@ -732,8 +729,8 @@ from the profile screen."
|
|||
return
|
||||
web_context = ResetRequired(self)
|
||||
self.web.set_bridge_command(lambda url: self.delayedMaybeReset(), web_context)
|
||||
i = _("Waiting for editing to finish.")
|
||||
b = self.button("refresh", _("Resume Now"), id="resume")
|
||||
i = tr(TR.QT_MISC_WAITING_FOR_EDITING_TO_FINISH)
|
||||
b = self.button("refresh", tr(TR.QT_MISC_RESUME_NOW), id="resume")
|
||||
self.web.stdHtml(
|
||||
"""
|
||||
<center><div style="height: 100%%">
|
||||
|
|
@ -762,7 +759,7 @@ from the profile screen."
|
|||
) -> str:
|
||||
class_ = "but " + class_
|
||||
if key:
|
||||
key = _("Shortcut key: %s") % key
|
||||
key = tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % key
|
||||
else:
|
||||
key = ""
|
||||
return """
|
||||
|
|
@ -1033,17 +1030,19 @@ title="%s" %s>%s</button>""" % (
|
|||
gui_hooks.review_did_undo(cid)
|
||||
else:
|
||||
self.reset()
|
||||
tooltip(_("Reverted to state prior to '%s'.") % n.lower())
|
||||
tooltip(tr(TR.QT_MISC_REVERTED_TO_STATE_PRIOR_TO, val="%s") % n.lower())
|
||||
gui_hooks.state_did_revert(n)
|
||||
self.maybeEnableUndo()
|
||||
|
||||
def maybeEnableUndo(self) -> None:
|
||||
if self.col and self.col.undoName():
|
||||
self.form.actionUndo.setText(_("Undo %s") % self.col.undoName())
|
||||
self.form.actionUndo.setText(
|
||||
tr(TR.QT_MISC_UNDO2, val="%s") % self.col.undoName()
|
||||
)
|
||||
self.form.actionUndo.setEnabled(True)
|
||||
gui_hooks.undo_state_did_change(True)
|
||||
else:
|
||||
self.form.actionUndo.setText(_("Undo"))
|
||||
self.form.actionUndo.setText(tr(TR.QT_MISC_UNDO))
|
||||
self.form.actionUndo.setEnabled(False)
|
||||
gui_hooks.undo_state_did_change(False)
|
||||
|
||||
|
|
@ -1119,7 +1118,7 @@ title="%s" %s>%s</button>""" % (
|
|||
import aqt.importing
|
||||
|
||||
if not os.path.exists(path):
|
||||
showInfo(_("Please use File>Import to import this file."))
|
||||
showInfo(tr(TR.QT_MISC_PLEASE_USE_FILEIMPORT_TO_IMPORT_THIS))
|
||||
return None
|
||||
|
||||
aqt.importing.importFile(self, path)
|
||||
|
|
@ -1161,9 +1160,9 @@ title="%s" %s>%s</button>""" % (
|
|||
if not search:
|
||||
if not deck["dyn"]:
|
||||
search = 'deck:"%s" ' % deck["name"]
|
||||
while self.col.decks.id_for_name(_("Filtered Deck %d") % n):
|
||||
while self.col.decks.id_for_name(tr(TR.QT_MISC_FILTERED_DECK, val="%s") % n):
|
||||
n += 1
|
||||
name = _("Filtered Deck %d") % n
|
||||
name = tr(TR.QT_MISC_FILTERED_DECK, val="%s") % n
|
||||
did = self.col.decks.new_filtered(name)
|
||||
diag = aqt.dyndeckconf.DeckConf(self, first=True, search=search)
|
||||
if not diag.ok:
|
||||
|
|
@ -1537,7 +1536,7 @@ will be lost. Continue?"""
|
|||
)
|
||||
frm.log.appendPlainText(to_append)
|
||||
except UnicodeDecodeError:
|
||||
to_append = _("<non-unicode text>")
|
||||
to_append = tr(TR.QT_MISC_NON_UNICODE_TEXT)
|
||||
to_append = gui_hooks.debug_console_did_evaluate_python(
|
||||
to_append, text, frm
|
||||
)
|
||||
|
|
@ -1600,9 +1599,9 @@ will be lost. Continue?"""
|
|||
return None
|
||||
self.pendingImport = buf
|
||||
if is_addon:
|
||||
msg = _("Add-on will be installed when a profile is opened.")
|
||||
msg = tr(TR.QT_MISC_ADDON_WILL_BE_INSTALLED_WHEN_A)
|
||||
else:
|
||||
msg = _("Deck will be imported when a profile is opened.")
|
||||
msg = tr(TR.QT_MISC_DECK_WILL_BE_IMPORTED_WHEN_A)
|
||||
return tooltip(msg)
|
||||
if not self.interactiveState() or self.progress.busy():
|
||||
# we can't raise the main window while in profile dialog, syncing, etc
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typing import Optional
|
|||
from anki.lang import _
|
||||
from aqt import AnkiQt, gui_hooks
|
||||
from aqt.qt import *
|
||||
from aqt.utils import shortcut
|
||||
from aqt.utils import TR, shortcut, tr
|
||||
|
||||
|
||||
class ModelChooser(QHBoxLayout):
|
||||
|
|
@ -37,11 +37,11 @@ class ModelChooser(QHBoxLayout):
|
|||
|
||||
def setupModels(self) -> None:
|
||||
if self.label:
|
||||
self.modelLabel = QLabel(_("Type"))
|
||||
self.modelLabel = QLabel(tr(TR.NOTETYPES_TYPE))
|
||||
self.addWidget(self.modelLabel)
|
||||
# models box
|
||||
self.models = QPushButton()
|
||||
self.models.setToolTip(shortcut(_("Change Note Type (Ctrl+N)")))
|
||||
self.models.setToolTip(shortcut(tr(TR.QT_MISC_CHANGE_NOTE_TYPE_CTRLANDN)))
|
||||
QShortcut(QKeySequence("Ctrl+N"), self.widget, activated=self.on_activated) # type: ignore
|
||||
self.models.setAutoDefault(False)
|
||||
self.addWidget(self.models)
|
||||
|
|
@ -73,7 +73,7 @@ class ModelChooser(QHBoxLayout):
|
|||
|
||||
current = self.deck.models.current()["name"]
|
||||
# edit button
|
||||
edit = QPushButton(_("Manage"), clicked=self.onEdit) # type: ignore
|
||||
edit = QPushButton(tr(TR.QT_MISC_MANAGE), clicked=self.onEdit) # type: ignore
|
||||
|
||||
def nameFunc():
|
||||
return sorted(self.deck.models.allNames())
|
||||
|
|
@ -81,8 +81,8 @@ class ModelChooser(QHBoxLayout):
|
|||
ret = StudyDeck(
|
||||
self.mw,
|
||||
names=nameFunc,
|
||||
accept=_("Choose"),
|
||||
title=_("Choose Note Type"),
|
||||
accept=tr(TR.ACTIONS_CHOOSE),
|
||||
title=tr(TR.QT_MISC_CHOOSE_NOTE_TYPE),
|
||||
help="_notes",
|
||||
current=current,
|
||||
parent=self.widget,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from anki.rsbackend import pb
|
|||
from aqt import AnkiQt, gui_hooks
|
||||
from aqt.qt import *
|
||||
from aqt.utils import (
|
||||
TR,
|
||||
askUser,
|
||||
getText,
|
||||
maybeHideClose,
|
||||
|
|
@ -21,6 +22,7 @@ from aqt.utils import (
|
|||
restoreGeom,
|
||||
saveGeom,
|
||||
showInfo,
|
||||
tr,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -33,7 +35,7 @@ class Models(QDialog):
|
|||
self.col = mw.col.weakref()
|
||||
assert self.col
|
||||
self.mm = self.col.models
|
||||
self.mw.checkpoint(_("Note Types"))
|
||||
self.mw.checkpoint(tr(TR.NOTETYPES_NOTE_TYPES))
|
||||
self.form = aqt.forms.models.Ui_Dialog()
|
||||
self.form.setupUi(self)
|
||||
qconnect(
|
||||
|
|
@ -54,20 +56,20 @@ class Models(QDialog):
|
|||
box = f.buttonBox
|
||||
|
||||
default_buttons = [
|
||||
(_("Add"), self.onAdd),
|
||||
(_("Rename"), self.onRename),
|
||||
(_("Delete"), self.onDelete),
|
||||
(tr(TR.ACTIONS_ADD), self.onAdd),
|
||||
(tr(TR.ACTIONS_RENAME), self.onRename),
|
||||
(tr(TR.ACTIONS_DELETE), self.onDelete),
|
||||
]
|
||||
|
||||
if self.fromMain:
|
||||
default_buttons.extend(
|
||||
[
|
||||
(_("Fields..."), self.onFields),
|
||||
(_("Cards..."), self.onCards),
|
||||
(tr(TR.NOTETYPES_FIELDS), self.onFields),
|
||||
(tr(TR.NOTETYPES_CARDS), self.onCards),
|
||||
]
|
||||
)
|
||||
|
||||
default_buttons.append((_("Options..."), self.onAdvanced))
|
||||
default_buttons.append((tr(TR.NOTETYPES_OPTIONS), self.onAdvanced))
|
||||
|
||||
for label, func in gui_hooks.models_did_init_buttons(default_buttons, self):
|
||||
button = box.addButton(label, QDialogButtonBox.ActionRole)
|
||||
|
|
@ -84,7 +86,7 @@ class Models(QDialog):
|
|||
|
||||
def onRename(self) -> None:
|
||||
nt = self.current_notetype()
|
||||
txt = getText(_("New name:"), default=nt["name"])
|
||||
txt = getText(tr(TR.ACTIONS_NEW_NAME), default=nt["name"])
|
||||
name = txt[0].replace('"', "")
|
||||
if txt[1] and name:
|
||||
nt["name"] = name
|
||||
|
|
@ -120,20 +122,20 @@ class Models(QDialog):
|
|||
def onAdd(self) -> None:
|
||||
m = AddModel(self.mw, self).get()
|
||||
if m:
|
||||
txt = getText(_("Name:"), default=m["name"])[0].replace('"', "")
|
||||
txt = getText(tr(TR.ACTIONS_NAME), default=m["name"])[0].replace('"', "")
|
||||
if txt:
|
||||
m["name"] = txt
|
||||
self.saveAndRefresh(m)
|
||||
|
||||
def onDelete(self) -> None:
|
||||
if len(self.models) < 2:
|
||||
showInfo(_("Please add another note type first."), parent=self)
|
||||
showInfo(tr(TR.NOTETYPES_PLEASE_ADD_ANOTHER_NOTE_TYPE_FIRST), parent=self)
|
||||
return
|
||||
idx = self.form.modelsList.currentRow()
|
||||
if self.models[idx].use_count:
|
||||
msg = _("Delete this note type and all its cards?")
|
||||
msg = tr(TR.NOTETYPES_DELETE_THIS_NOTE_TYPE_AND_ALL)
|
||||
else:
|
||||
msg = _("Delete this unused note type?")
|
||||
msg = tr(TR.NOTETYPES_DELETE_THIS_UNUSED_NOTE_TYPE)
|
||||
if not askUser(msg, parent=self):
|
||||
return
|
||||
|
||||
|
|
@ -158,7 +160,7 @@ class Models(QDialog):
|
|||
frm.latexsvg.setChecked(nt.get("latexsvg", False))
|
||||
frm.latexHeader.setText(nt["latexPre"])
|
||||
frm.latexFooter.setText(nt["latexPost"])
|
||||
d.setWindowTitle(_("Options for %s") % nt["name"])
|
||||
d.setWindowTitle(tr(TR.ACTIONS_OPTIONS_FOR, val="%s") % nt["name"])
|
||||
qconnect(frm.buttonBox.helpRequested, lambda: openHelp("math?id=latex"))
|
||||
restoreGeom(d, "modelopts")
|
||||
gui_hooks.models_advanced_will_show(d)
|
||||
|
|
@ -209,12 +211,12 @@ class AddModel(QDialog):
|
|||
# standard models
|
||||
self.models = []
|
||||
for (name, func) in stdmodels.get_stock_notetypes(self.col):
|
||||
item = QListWidgetItem(_("Add: %s") % name)
|
||||
item = QListWidgetItem(tr(TR.NOTETYPES_ADD, val="%s") % name)
|
||||
self.dialog.models.addItem(item)
|
||||
self.models.append((True, func))
|
||||
# add copies
|
||||
for m in sorted(self.col.models.all(), key=itemgetter("name")):
|
||||
item = QListWidgetItem(_("Clone: %s") % m["name"])
|
||||
item = QListWidgetItem(tr(TR.NOTETYPES_CLONE, val="%s") % m["name"])
|
||||
self.dialog.models.addItem(item)
|
||||
self.models.append((False, m)) # type: ignore
|
||||
self.dialog.models.setCurrentRow(0)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
|
@ -12,7 +11,7 @@ from anki.lang import _
|
|||
from aqt import gui_hooks
|
||||
from aqt.sound import av_player
|
||||
from aqt.toolbar import BottomBar
|
||||
from aqt.utils import askUserDialog, openLink, shortcut, tooltip
|
||||
from aqt.utils import TR, askUserDialog, openLink, shortcut, tooltip, tr
|
||||
|
||||
|
||||
class OverviewBottomBar:
|
||||
|
|
@ -67,7 +66,7 @@ class Overview:
|
|||
self.mw.col.startTimebox()
|
||||
self.mw.moveToState("review")
|
||||
if self.mw.state == "overview":
|
||||
tooltip(_("No cards are due yet."))
|
||||
tooltip(tr(TR.STUDYING_NO_CARDS_ARE_DUE_YET))
|
||||
elif url == "anki":
|
||||
print("anki menu")
|
||||
elif url == "opts":
|
||||
|
|
@ -128,13 +127,13 @@ class Overview:
|
|||
info = self.mw.col.sched.congratulations_info()
|
||||
if info.have_sched_buried and info.have_user_buried:
|
||||
opts = [
|
||||
_("Manually Buried Cards"),
|
||||
_("Buried Siblings"),
|
||||
_("All Buried Cards"),
|
||||
_("Cancel"),
|
||||
tr(TR.STUDYING_MANUALLY_BURIED_CARDS),
|
||||
tr(TR.STUDYING_BURIED_SIBLINGS),
|
||||
tr(TR.STUDYING_ALL_BURIED_CARDS),
|
||||
tr(TR.ACTIONS_CANCEL),
|
||||
]
|
||||
|
||||
diag = askUserDialog(_("What would you like to unbury?"), opts)
|
||||
diag = askUserDialog(tr(TR.STUDYING_WHAT_WOULD_YOU_LIKE_TO_UNBURY), opts)
|
||||
diag.setDefault(0)
|
||||
ret = diag.run()
|
||||
if ret == opts[0]:
|
||||
|
|
@ -226,13 +225,13 @@ to their original deck."""
|
|||
</table>
|
||||
</td><td align=center>
|
||||
%s</td></tr></table>""" % (
|
||||
_("New"),
|
||||
tr(TR.ACTIONS_NEW),
|
||||
counts[0],
|
||||
_("Learning"),
|
||||
tr(TR.SCHEDULING_LEARNING),
|
||||
counts[1],
|
||||
_("To Review"),
|
||||
tr(TR.STUDYING_TO_REVIEW),
|
||||
counts[2],
|
||||
but("study", _("Study Now"), id="study", extra=" autofocus"),
|
||||
but("study", tr(TR.STUDYING_STUDY_NOW), id="study", extra=" autofocus"),
|
||||
)
|
||||
|
||||
_body = """
|
||||
|
|
@ -249,20 +248,20 @@ to their original deck."""
|
|||
|
||||
def _renderBottom(self):
|
||||
links = [
|
||||
["O", "opts", _("Options")],
|
||||
["O", "opts", tr(TR.ACTIONS_OPTIONS)],
|
||||
]
|
||||
if self.mw.col.decks.current()["dyn"]:
|
||||
links.append(["R", "refresh", _("Rebuild")])
|
||||
links.append(["E", "empty", _("Empty")])
|
||||
links.append(["R", "refresh", tr(TR.ACTIONS_REBUILD)])
|
||||
links.append(["E", "empty", tr(TR.STUDYING_EMPTY)])
|
||||
else:
|
||||
links.append(["C", "studymore", _("Custom Study")])
|
||||
links.append(["C", "studymore", tr(TR.ACTIONS_CUSTOM_STUDY)])
|
||||
# links.append(["F", "cram", _("Filter/Cram")])
|
||||
if self.mw.col.sched.haveBuried():
|
||||
links.append(["U", "unbury", _("Unbury")])
|
||||
links.append(["U", "unbury", tr(TR.STUDYING_UNBURY)])
|
||||
buf = ""
|
||||
for b in links:
|
||||
if b[0]:
|
||||
b[0] = _("Shortcut key: %s") % shortcut(b[0])
|
||||
b[0] = tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % shortcut(b[0])
|
||||
buf += """
|
||||
<button title="%s" onclick='pycmd("%s")'>%s</button>""" % tuple(
|
||||
b
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
import anki.lang
|
||||
import aqt
|
||||
from anki.lang import _
|
||||
|
|
@ -69,7 +68,9 @@ class Preferences(QDialog):
|
|||
def onLangIdxChanged(self, idx):
|
||||
code = anki.lang.langs[idx][1]
|
||||
self.mw.pm.setLang(code)
|
||||
showInfo(_("Please restart Anki to complete language change."), parent=self)
|
||||
showInfo(
|
||||
tr(TR.PREFERENCES_PLEASE_RESTART_ANKI_TO_COMPLETE_LANGUAGE), parent=self
|
||||
)
|
||||
|
||||
# Collection options
|
||||
######################################################################
|
||||
|
|
@ -117,7 +118,7 @@ class Preferences(QDialog):
|
|||
self.mw.pm.setGlMode("auto")
|
||||
else:
|
||||
self.mw.pm.setGlMode("software")
|
||||
showInfo(_("Changes will take effect when you restart Anki."))
|
||||
showInfo(tr(TR.PREFERENCES_CHANGES_WILL_TAKE_EFFECT_WHEN_YOU))
|
||||
|
||||
qc = d.conf
|
||||
qc["addToCur"] = not f.useCurrent.currentIndex()
|
||||
|
|
@ -149,11 +150,7 @@ class Preferences(QDialog):
|
|||
if haveNew == wantNew:
|
||||
return
|
||||
|
||||
if not askUser(
|
||||
_(
|
||||
"This will reset any cards in learning, clear filtered decks, and change the scheduler version. Proceed?"
|
||||
)
|
||||
):
|
||||
if not askUser(tr(TR.PREFERENCES_THIS_WILL_RESET_ANY_CARDS_IN)):
|
||||
return
|
||||
|
||||
if wantNew:
|
||||
|
|
@ -247,4 +244,4 @@ Not currently enabled; click the sync button in the main window to enable."""
|
|||
self.mw.pm.set_interrupt_audio(self.form.interrupt_audio.isChecked())
|
||||
|
||||
if restart_required:
|
||||
showInfo(_("Changes will take effect when you restart Anki."))
|
||||
showInfo(tr(TR.PREFERENCES_CHANGES_WILL_TAKE_EFFECT_WHEN_YOU))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
# mypy: check-untyped-defs
|
||||
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
|
|
@ -26,7 +25,7 @@ from aqt.qt import (
|
|||
from aqt.reviewer import replay_audio
|
||||
from aqt.sound import av_player, play_clicked_audio
|
||||
from aqt.theme import theme_manager
|
||||
from aqt.utils import restoreGeom, saveGeom
|
||||
from aqt.utils import TR, restoreGeom, saveGeom, tr
|
||||
from aqt.webview import AnkiWebView
|
||||
|
||||
|
||||
|
|
@ -62,7 +61,7 @@ class Previewer(QDialog):
|
|||
self.show()
|
||||
|
||||
def _create_gui(self):
|
||||
self.setWindowTitle(_("Preview"))
|
||||
self.setWindowTitle(tr(TR.ACTIONS_PREVIEW))
|
||||
|
||||
qconnect(self.finished, self._on_finished)
|
||||
self.silentlyClose = True
|
||||
|
|
@ -73,14 +72,14 @@ class Previewer(QDialog):
|
|||
self.bbox = QDialogButtonBox()
|
||||
|
||||
self._replay = self.bbox.addButton(
|
||||
_("Replay Audio"), QDialogButtonBox.ActionRole
|
||||
tr(TR.ACTIONS_REPLAY_AUDIO), QDialogButtonBox.ActionRole
|
||||
)
|
||||
self._replay.setAutoDefault(False)
|
||||
self._replay.setShortcut(QKeySequence("R"))
|
||||
self._replay.setToolTip(_("Shortcut key: %s" % "R"))
|
||||
qconnect(self._replay.clicked, self._on_replay_audio)
|
||||
|
||||
both_sides_button = QCheckBox(_("Back Side Only"))
|
||||
both_sides_button = QCheckBox(tr(TR.QT_MISC_BACK_SIDE_ONLY))
|
||||
both_sides_button.setShortcut(QKeySequence("B"))
|
||||
both_sides_button.setToolTip(_("Shortcut key: %s" % "B"))
|
||||
self.bbox.addButton(both_sides_button, QDialogButtonBox.ActionRole)
|
||||
|
|
@ -159,7 +158,7 @@ class Previewer(QDialog):
|
|||
c = self.card()
|
||||
func = "_showQuestion"
|
||||
if not c:
|
||||
txt = _("(please select 1 card)")
|
||||
txt = tr(TR.QT_MISC_PLEASE_SELECT_1_CARD)
|
||||
bodyclass = ""
|
||||
self._last_state = None
|
||||
else:
|
||||
|
|
@ -240,12 +239,12 @@ class MultiCardPreviewer(Previewer):
|
|||
self._prev = self.bbox.addButton("<", QDialogButtonBox.ActionRole)
|
||||
self._prev.setAutoDefault(False)
|
||||
self._prev.setShortcut(QKeySequence("Left"))
|
||||
self._prev.setToolTip(_("Shortcut key: Left arrow"))
|
||||
self._prev.setToolTip(tr(TR.QT_MISC_SHORTCUT_KEY_LEFT_ARROW))
|
||||
|
||||
self._next = self.bbox.addButton(">", QDialogButtonBox.ActionRole)
|
||||
self._next.setAutoDefault(True)
|
||||
self._next.setShortcut(QKeySequence("Right"))
|
||||
self._next.setToolTip(_("Shortcut key: Right arrow or Enter"))
|
||||
self._next.setToolTip(tr(TR.QT_MISC_SHORTCUT_KEY_RIGHT_ARROW_OR_ENTER))
|
||||
|
||||
qconnect(self._prev.clicked, self._on_prev)
|
||||
qconnect(self._next.clicked, self._on_next)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
# Profile handling
|
||||
##########################################################################
|
||||
# - Saves in pickles rather than json to easily store Qt window state.
|
||||
# - Saves in sqlite rather than a flat file so the config can't be corrupted
|
||||
|
||||
import io
|
||||
import locale
|
||||
import pickle
|
||||
|
|
@ -28,6 +23,12 @@ from aqt import appHelpSite
|
|||
from aqt.qt import *
|
||||
from aqt.utils import TR, locale_dir, showWarning, tr
|
||||
|
||||
# Profile handling
|
||||
##########################################################################
|
||||
# - Saves in pickles rather than json to easily store Qt window state.
|
||||
# - Saves in sqlite rather than a flat file so the config can't be corrupted
|
||||
|
||||
|
||||
metaConf = dict(
|
||||
ver=0,
|
||||
updates=True,
|
||||
|
|
@ -251,7 +252,7 @@ class ProfileManager:
|
|||
except:
|
||||
QMessageBox.warning(
|
||||
None,
|
||||
_("Profile Corrupt"),
|
||||
tr(TR.PROFILES_PROFILE_CORRUPT),
|
||||
_(
|
||||
"""\
|
||||
Anki could not read your profile data. Window sizes and your sync login \
|
||||
|
|
@ -304,12 +305,13 @@ details have been forgotten."""
|
|||
oldFolder = midFolder
|
||||
else:
|
||||
showWarning(
|
||||
_("Please remove the folder %s and try again.") % midFolder
|
||||
tr(TR.PROFILES_PLEASE_REMOVE_THE_FOLDER_AND, val="%s")
|
||||
% midFolder
|
||||
)
|
||||
self.name = oldName
|
||||
return
|
||||
else:
|
||||
showWarning(_("Folder already exists."))
|
||||
showWarning(tr(TR.PROFILES_FOLDER_ALREADY_EXISTS))
|
||||
self.name = oldName
|
||||
return
|
||||
|
||||
|
|
@ -476,7 +478,7 @@ create table if not exists profiles
|
|||
|
||||
def _ensureProfile(self) -> None:
|
||||
"Create a new profile if none exists."
|
||||
self.create(_("User 1"))
|
||||
self.create(tr(TR.PROFILES_USER_1))
|
||||
p = os.path.join(self.base, "README.txt")
|
||||
with open(p, "w", encoding="utf8") as file:
|
||||
file.write(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# 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
|
||||
|
|
@ -10,6 +9,7 @@ from typing import Optional
|
|||
import aqt.forms
|
||||
from anki.lang import _
|
||||
from aqt.qt import *
|
||||
from aqt.utils import TR, tr
|
||||
|
||||
# Progress info
|
||||
##########################################################################
|
||||
|
|
@ -80,7 +80,7 @@ class ProgressManager:
|
|||
if not parent and self.mw.isVisible():
|
||||
parent = self.mw
|
||||
|
||||
label = label or _("Processing...")
|
||||
label = label or tr(TR.QT_MISC_PROCESSING)
|
||||
self._win = ProgressDialog(parent)
|
||||
self._win.form.progressBar.setMinimum(min)
|
||||
self._win.form.progressBar.setMaximum(max)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import difflib
|
||||
|
|
@ -22,7 +21,14 @@ from aqt.qt import *
|
|||
from aqt.sound import av_player, getAudio, play_clicked_audio
|
||||
from aqt.theme import theme_manager
|
||||
from aqt.toolbar import BottomBar
|
||||
from aqt.utils import askUserDialog, downArrow, qtMenuShortcutWorkaround, tooltip
|
||||
from aqt.utils import (
|
||||
TR,
|
||||
askUserDialog,
|
||||
downArrow,
|
||||
qtMenuShortcutWorkaround,
|
||||
tooltip,
|
||||
tr,
|
||||
)
|
||||
from aqt.webview import AnkiWebView
|
||||
|
||||
|
||||
|
|
@ -92,8 +98,10 @@ class Reviewer:
|
|||
)
|
||||
mins = int(round(elapsed[0] / 60))
|
||||
part2 = ngettext("%s minute.", "%s minutes.", mins) % mins
|
||||
fin = _("Finish")
|
||||
diag = askUserDialog("%s %s" % (part1, part2), [_("Continue"), fin])
|
||||
fin = tr(TR.STUDYING_FINISH)
|
||||
diag = askUserDialog(
|
||||
"%s %s" % (part1, part2), [tr(TR.STUDYING_CONTINUE), fin]
|
||||
)
|
||||
diag.setIcon(QMessageBox.Information)
|
||||
if diag.run() == fin:
|
||||
return self.mw.moveToState("deckBrowser")
|
||||
|
|
@ -388,7 +396,7 @@ class Reviewer:
|
|||
Please run Tools>Empty Cards"""
|
||||
)
|
||||
else:
|
||||
warn = _("Type answer: unknown field %s") % fld
|
||||
warn = tr(TR.STUDYING_TYPE_ANSWER_UNKNOWN_FIELD, val="%s") % fld
|
||||
return re.sub(self.typeAnsPat, warn, buf)
|
||||
else:
|
||||
# empty field, remove type answer pattern
|
||||
|
|
@ -569,9 +577,9 @@ time = %(time)d;
|
|||
</script>
|
||||
""" % dict(
|
||||
rem=self._remaining(),
|
||||
edit=_("Edit"),
|
||||
editkey=_("Shortcut key: %s") % "E",
|
||||
more=_("More"),
|
||||
edit=tr(TR.STUDYING_EDIT),
|
||||
editkey=tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % "E",
|
||||
more=tr(TR.STUDYING_MORE),
|
||||
downArrow=downArrow(),
|
||||
time=self.card.timeTaken() // 1000,
|
||||
)
|
||||
|
|
@ -581,8 +589,8 @@ time = %(time)d;
|
|||
<span class=stattxt>%s</span><br>
|
||||
<button title="%s" id="ansbut" class="focus" onclick='pycmd("ans");'>%s</button>""" % (
|
||||
self._remaining(),
|
||||
_("Shortcut key: %s") % _("Space"),
|
||||
_("Show Answer"),
|
||||
tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % tr(TR.STUDYING_SPACE),
|
||||
tr(TR.STUDYING_SHOW_ANSWER),
|
||||
)
|
||||
# wrap it in a table so it has the same top margin as the ease buttons
|
||||
middle = (
|
||||
|
|
@ -626,17 +634,21 @@ time = %(time)d;
|
|||
button_count = self.mw.col.sched.answerButtons(self.card)
|
||||
if button_count == 2:
|
||||
buttons_tuple: Tuple[Tuple[int, str], ...] = (
|
||||
(1, _("Again")),
|
||||
(2, _("Good")),
|
||||
(1, tr(TR.STUDYING_AGAIN)),
|
||||
(2, tr(TR.STUDYING_GOOD)),
|
||||
)
|
||||
elif button_count == 3:
|
||||
buttons_tuple = ((1, _("Again")), (2, _("Good")), (3, _("Easy")))
|
||||
buttons_tuple = (
|
||||
(1, tr(TR.STUDYING_AGAIN)),
|
||||
(2, tr(TR.STUDYING_GOOD)),
|
||||
(3, tr(TR.STUDYING_EASY)),
|
||||
)
|
||||
else:
|
||||
buttons_tuple = (
|
||||
(1, _("Again")),
|
||||
(2, _("Hard")),
|
||||
(3, _("Good")),
|
||||
(4, _("Easy")),
|
||||
(1, tr(TR.STUDYING_AGAIN)),
|
||||
(2, tr(TR.STUDYING_HARD)),
|
||||
(3, tr(TR.STUDYING_GOOD)),
|
||||
(4, tr(TR.STUDYING_EASY)),
|
||||
)
|
||||
buttons_tuple = gui_hooks.reviewer_will_init_answer_buttons(
|
||||
buttons_tuple, self, self.card
|
||||
|
|
@ -657,7 +669,7 @@ time = %(time)d;
|
|||
%s</button></td>""" % (
|
||||
due,
|
||||
extra,
|
||||
_("Shortcut key: %s") % i,
|
||||
tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % i,
|
||||
i,
|
||||
i,
|
||||
label,
|
||||
|
|
@ -682,9 +694,9 @@ time = %(time)d;
|
|||
|
||||
def onLeech(self, card: Card) -> None:
|
||||
# for now
|
||||
s = _("Card was a leech.")
|
||||
s = tr(TR.STUDYING_CARD_WAS_A_LEECH)
|
||||
if card.queue < 0:
|
||||
s += " " + _("It has been suspended.")
|
||||
s += " " + tr(TR.STUDYING_IT_HAS_BEEN_SUSPENDED)
|
||||
tooltip(s)
|
||||
|
||||
# Context menu
|
||||
|
|
@ -695,48 +707,48 @@ time = %(time)d;
|
|||
currentFlag = self.card and self.card.userFlag()
|
||||
opts = [
|
||||
[
|
||||
_("Flag Card"),
|
||||
tr(TR.STUDYING_FLAG_CARD),
|
||||
[
|
||||
[
|
||||
_("Red Flag"),
|
||||
tr(TR.ACTIONS_RED_FLAG),
|
||||
"Ctrl+1",
|
||||
lambda: self.setFlag(1),
|
||||
dict(checked=currentFlag == 1),
|
||||
],
|
||||
[
|
||||
_("Orange Flag"),
|
||||
tr(TR.ACTIONS_ORANGE_FLAG),
|
||||
"Ctrl+2",
|
||||
lambda: self.setFlag(2),
|
||||
dict(checked=currentFlag == 2),
|
||||
],
|
||||
[
|
||||
_("Green Flag"),
|
||||
tr(TR.ACTIONS_GREEN_FLAG),
|
||||
"Ctrl+3",
|
||||
lambda: self.setFlag(3),
|
||||
dict(checked=currentFlag == 3),
|
||||
],
|
||||
[
|
||||
_("Blue Flag"),
|
||||
tr(TR.ACTIONS_BLUE_FLAG),
|
||||
"Ctrl+4",
|
||||
lambda: self.setFlag(4),
|
||||
dict(checked=currentFlag == 4),
|
||||
],
|
||||
],
|
||||
],
|
||||
[_("Mark Note"), "*", self.onMark],
|
||||
[_("Bury Card"), "-", self.onBuryCard],
|
||||
[_("Bury Note"), "=", self.onBuryNote],
|
||||
[_("Suspend Card"), "@", self.onSuspendCard],
|
||||
[_("Suspend Note"), "!", self.onSuspend],
|
||||
[_("Delete Note"), "Ctrl+Delete", self.onDelete],
|
||||
[_("Options"), "O", self.onOptions],
|
||||
[tr(TR.STUDYING_MARK_NOTE), "*", self.onMark],
|
||||
[tr(TR.STUDYING_BURY_CARD), "-", self.onBuryCard],
|
||||
[tr(TR.STUDYING_BURY_NOTE), "=", self.onBuryNote],
|
||||
[tr(TR.ACTIONS_SUSPEND_CARD), "@", self.onSuspendCard],
|
||||
[tr(TR.STUDYING_SUSPEND_NOTE), "!", self.onSuspend],
|
||||
[tr(TR.STUDYING_DELETE_NOTE), "Ctrl+Delete", self.onDelete],
|
||||
[tr(TR.ACTIONS_OPTIONS), "O", self.onOptions],
|
||||
None,
|
||||
[_("Replay Audio"), "R", self.replayAudio],
|
||||
[_("Pause Audio"), "5", self.on_pause_audio],
|
||||
[_("Audio -5s"), "6", self.on_seek_backward],
|
||||
[_("Audio +5s"), "7", self.on_seek_forward],
|
||||
[_("Record Own Voice"), "Shift+V", self.onRecordVoice],
|
||||
[_("Replay Own Voice"), "V", self.onReplayRecorded],
|
||||
[tr(TR.ACTIONS_REPLAY_AUDIO), "R", self.replayAudio],
|
||||
[tr(TR.STUDYING_PAUSE_AUDIO), "5", self.on_pause_audio],
|
||||
[tr(TR.STUDYING_AUDIO_5S), "6", self.on_seek_backward],
|
||||
[tr(TR.STUDYING_AUDIO_AND5S), "7", self.on_seek_forward],
|
||||
[tr(TR.STUDYING_RECORD_OWN_VOICE), "Shift+V", self.onRecordVoice],
|
||||
[tr(TR.STUDYING_REPLAY_OWN_VOICE), "V", self.onReplayRecorded],
|
||||
]
|
||||
return opts
|
||||
|
||||
|
|
@ -793,15 +805,15 @@ time = %(time)d;
|
|||
self._drawMark()
|
||||
|
||||
def onSuspend(self) -> None:
|
||||
self.mw.checkpoint(_("Suspend"))
|
||||
self.mw.checkpoint(tr(TR.STUDYING_SUSPEND))
|
||||
self.mw.col.sched.suspend_cards([c.id for c in self.card.note().cards()])
|
||||
tooltip(_("Note suspended."))
|
||||
tooltip(tr(TR.STUDYING_NOTE_SUSPENDED))
|
||||
self.mw.reset()
|
||||
|
||||
def onSuspendCard(self) -> None:
|
||||
self.mw.checkpoint(_("Suspend"))
|
||||
self.mw.checkpoint(tr(TR.STUDYING_SUSPEND))
|
||||
self.mw.col.sched.suspend_cards([self.card.id])
|
||||
tooltip(_("Card suspended."))
|
||||
tooltip(tr(TR.STUDYING_CARD_SUSPENDED))
|
||||
self.mw.reset()
|
||||
|
||||
def onDelete(self) -> None:
|
||||
|
|
@ -809,7 +821,7 @@ time = %(time)d;
|
|||
# window
|
||||
if self.mw.state != "review" or not self.card:
|
||||
return
|
||||
self.mw.checkpoint(_("Delete"))
|
||||
self.mw.checkpoint(tr(TR.ACTIONS_DELETE))
|
||||
cnt = len(self.card.note().cards())
|
||||
self.mw.col.remove_notes([self.card.note().id])
|
||||
self.mw.reset()
|
||||
|
|
@ -821,16 +833,16 @@ time = %(time)d;
|
|||
)
|
||||
|
||||
def onBuryCard(self) -> None:
|
||||
self.mw.checkpoint(_("Bury"))
|
||||
self.mw.checkpoint(tr(TR.STUDYING_BURY))
|
||||
self.mw.col.sched.bury_cards([self.card.id])
|
||||
self.mw.reset()
|
||||
tooltip(_("Card buried."))
|
||||
tooltip(tr(TR.STUDYING_CARD_BURIED))
|
||||
|
||||
def onBuryNote(self) -> None:
|
||||
self.mw.checkpoint(_("Bury"))
|
||||
self.mw.checkpoint(tr(TR.STUDYING_BURY))
|
||||
self.mw.col.sched.bury_note(self.card.note())
|
||||
self.mw.reset()
|
||||
tooltip(_("Note buried."))
|
||||
tooltip(tr(TR.STUDYING_NOTE_BURIED))
|
||||
|
||||
def onRecordVoice(self) -> None:
|
||||
self._recordedAudio = getAudio(self.mw, encode=False)
|
||||
|
|
@ -838,6 +850,6 @@ time = %(time)d;
|
|||
|
||||
def onReplayRecorded(self) -> None:
|
||||
if not self._recordedAudio:
|
||||
tooltip(_("You haven't recorded your voice yet."))
|
||||
tooltip(tr(TR.STUDYING_YOU_HAVENT_RECORDED_YOUR_VOICE_YET))
|
||||
return
|
||||
av_player.play_file(self._recordedAudio)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
|
@ -10,7 +9,7 @@ import aqt
|
|||
from anki.errors import DeckRenameError
|
||||
from anki.lang import _
|
||||
from aqt.qt import *
|
||||
from aqt.utils import getOnlyText, showWarning
|
||||
from aqt.utils import TR, getOnlyText, showWarning, tr
|
||||
|
||||
|
||||
class SidebarItemType(Enum):
|
||||
|
|
@ -71,7 +70,7 @@ class NewSidebarTreeView(SidebarTreeViewBase):
|
|||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.customContextMenuRequested.connect(self.onContextMenu) # type: ignore
|
||||
self.context_menus = {
|
||||
SidebarItemType.DECK: ((_("Rename"), self.rename_deck),),
|
||||
SidebarItemType.DECK: ((tr(TR.ACTIONS_RENAME), self.rename_deck),),
|
||||
}
|
||||
|
||||
def onContextMenu(self, point: QPoint) -> None:
|
||||
|
|
@ -92,10 +91,10 @@ class NewSidebarTreeView(SidebarTreeViewBase):
|
|||
m.exec_(QCursor.pos())
|
||||
|
||||
def rename_deck(self, item: "aqt.browser.SidebarItem") -> None:
|
||||
self.mw.checkpoint(_("Rename Deck"))
|
||||
self.mw.checkpoint(tr(TR.ACTIONS_RENAME_DECK))
|
||||
deck = self.mw.col.decks.get(item.id)
|
||||
old_name = deck["name"]
|
||||
new_name = getOnlyText(_("New deck name:"), default=old_name)
|
||||
new_name = getOnlyText(tr(TR.DECKS_NEW_DECK_NAME), default=old_name)
|
||||
new_name = new_name.replace('"', "")
|
||||
if not new_name or new_name == old_name:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ from aqt import gui_hooks
|
|||
from aqt.mpv import MPV, MPVBase, MPVCommandError
|
||||
from aqt.qt import *
|
||||
from aqt.taskman import TaskManager
|
||||
from aqt.utils import restoreGeom, saveGeom, showWarning, startup_info
|
||||
from aqt.utils import TR, restoreGeom, saveGeom, showWarning, startup_info, tr
|
||||
|
||||
try:
|
||||
import pyaudio
|
||||
|
|
@ -318,11 +318,7 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method
|
|||
try:
|
||||
ret.result()
|
||||
except FileNotFoundError:
|
||||
showWarning(
|
||||
_(
|
||||
"Sound and video on cards will not function until mpv or mplayer is installed."
|
||||
)
|
||||
)
|
||||
showWarning(tr(TR.MEDIA_SOUND_AND_VIDEO_ON_CARDS_WILL))
|
||||
# must call cb() here, as we don't currently have another way
|
||||
# to flag to av_player that we've stopped
|
||||
cb()
|
||||
|
|
@ -499,7 +495,7 @@ class _Recorder:
|
|||
finally:
|
||||
self.cleanup()
|
||||
if ret:
|
||||
raise Exception(_("Error running %s") % " ".join(cmd))
|
||||
raise Exception(tr(TR.MEDIA_ERROR_RUNNING, val="%s") % " ".join(cmd))
|
||||
|
||||
def cleanup(self) -> None:
|
||||
if os.path.exists(processingSrc):
|
||||
|
|
@ -596,10 +592,10 @@ def getAudio(parent: QWidget, encode: bool = True) -> Optional[str]:
|
|||
restoreGeom(mb, "audioRecorder")
|
||||
mb.setWindowTitle("Anki")
|
||||
mb.setIconPixmap(QPixmap(":/icons/media-record.png"))
|
||||
but = QPushButton(_("Save"))
|
||||
but = QPushButton(tr(TR.ACTIONS_SAVE))
|
||||
mb.addButton(but, QMessageBox.AcceptRole)
|
||||
but.setDefault(True)
|
||||
but = QPushButton(_("Cancel"))
|
||||
but = QPushButton(tr(TR.ACTIONS_CANCEL))
|
||||
mb.addButton(but, QMessageBox.RejectRole)
|
||||
mb.setEscapeButton(but)
|
||||
t = time.time()
|
||||
|
|
@ -607,7 +603,7 @@ def getAudio(parent: QWidget, encode: bool = True) -> Optional[str]:
|
|||
time.sleep(r.startupDelay)
|
||||
QApplication.instance().processEvents() # type: ignore
|
||||
while not mb.clickedButton():
|
||||
txt = _("Recording...<br>Time: %0.1f")
|
||||
txt = tr(TR.MEDIA_RECORDINGTIME)
|
||||
mb.setText(txt % (time.time() - t))
|
||||
mb.show()
|
||||
QApplication.instance().processEvents() # type: ignore
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# -*- 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 getOnlyText, openHelp, restoreGeom, saveGeom, shortcut, showInfo
|
||||
from aqt.utils import (
|
||||
TR,
|
||||
getOnlyText,
|
||||
openHelp,
|
||||
restoreGeom,
|
||||
saveGeom,
|
||||
shortcut,
|
||||
showInfo,
|
||||
tr,
|
||||
)
|
||||
|
||||
|
||||
class StudyDeck(QDialog):
|
||||
|
|
@ -43,9 +51,9 @@ class StudyDeck(QDialog):
|
|||
for b in buttons:
|
||||
self.form.buttonBox.addButton(b, QDialogButtonBox.ActionRole)
|
||||
else:
|
||||
b = QPushButton(_("Add"))
|
||||
b = QPushButton(tr(TR.ACTIONS_ADD))
|
||||
b.setShortcut(QKeySequence("Ctrl+N"))
|
||||
b.setToolTip(shortcut(_("Add New Deck (Ctrl+N)")))
|
||||
b.setToolTip(shortcut(tr(TR.DECKS_ADD_NEW_DECK_CTRLANDN)))
|
||||
self.form.buttonBox.addButton(b, QDialogButtonBox.ActionRole)
|
||||
qconnect(b.clicked, self.onAddDeck)
|
||||
if title:
|
||||
|
|
@ -64,7 +72,7 @@ class StudyDeck(QDialog):
|
|||
self.origNames = names()
|
||||
self.name = None
|
||||
self.ok = self.form.buttonBox.addButton(
|
||||
accept or _("Study"), QDialogButtonBox.AcceptRole
|
||||
accept or tr(TR.DECKS_STUDY), QDialogButtonBox.AcceptRole
|
||||
)
|
||||
self.setWindowModality(Qt.WindowModal)
|
||||
qconnect(self.form.buttonBox.helpRequested, lambda: openHelp(help))
|
||||
|
|
@ -132,7 +140,7 @@ class StudyDeck(QDialog):
|
|||
gui_hooks.state_did_reset.remove(self.onReset)
|
||||
row = self.form.list.currentRow()
|
||||
if row < 0:
|
||||
showInfo(_("Please select something."))
|
||||
showInfo(tr(TR.DECKS_PLEASE_SELECT_SOMETHING))
|
||||
return
|
||||
self.name = self.names[self.form.list.currentRow()]
|
||||
QDialog.accept(self)
|
||||
|
|
@ -148,7 +156,7 @@ class StudyDeck(QDialog):
|
|||
default = self.form.filter.text()
|
||||
else:
|
||||
default = self.names[self.form.list.currentRow()]
|
||||
n = getOnlyText(_("New deck name:"), default=default)
|
||||
n = getOnlyText(tr(TR.DECKS_NEW_DECK_NAME), default=default)
|
||||
n = n.strip()
|
||||
if n:
|
||||
did = self.mw.col.decks.id(n)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# 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
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
|
@ -12,6 +11,7 @@ from anki.rsbackend import SyncStatus
|
|||
from aqt import gui_hooks
|
||||
from aqt.qt import *
|
||||
from aqt.sync import get_sync_status
|
||||
from aqt.utils import TR, tr
|
||||
from aqt.webview import AnkiWebView
|
||||
|
||||
|
||||
|
|
@ -102,30 +102,30 @@ class Toolbar:
|
|||
links = [
|
||||
self.create_link(
|
||||
"decks",
|
||||
_("Decks"),
|
||||
tr(TR.ACTIONS_DECKS),
|
||||
self._deckLinkHandler,
|
||||
tip=_("Shortcut key: %s") % "D",
|
||||
tip=tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % "D",
|
||||
id="decks",
|
||||
),
|
||||
self.create_link(
|
||||
"add",
|
||||
_("Add"),
|
||||
tr(TR.ACTIONS_ADD),
|
||||
self._addLinkHandler,
|
||||
tip=_("Shortcut key: %s") % "A",
|
||||
tip=tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % "A",
|
||||
id="add",
|
||||
),
|
||||
self.create_link(
|
||||
"browse",
|
||||
_("Browse"),
|
||||
tr(TR.QT_MISC_BROWSE),
|
||||
self._browseLinkHandler,
|
||||
tip=_("Shortcut key: %s") % "B",
|
||||
tip=tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % "B",
|
||||
id="browse",
|
||||
),
|
||||
self.create_link(
|
||||
"stats",
|
||||
_("Stats"),
|
||||
tr(TR.QT_MISC_STATS),
|
||||
self._statsLinkHandler,
|
||||
tip=_("Shortcut key: %s") % "T",
|
||||
tip=tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % "T",
|
||||
id="stats",
|
||||
),
|
||||
]
|
||||
|
|
@ -140,8 +140,8 @@ class Toolbar:
|
|||
######################################################################
|
||||
|
||||
def _create_sync_link(self) -> str:
|
||||
name = _("Sync")
|
||||
title = _("Shortcut key: %s") % "Y"
|
||||
name = tr(TR.QT_MISC_SYNC)
|
||||
title = tr(TR.ACTIONS_SHORTCUT_KEY, val="%s") % "Y"
|
||||
label = "sync"
|
||||
self.link_handlers[label] = self._syncLinkHandler
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import aqt
|
|||
from anki.lang import _
|
||||
from anki.utils import platDesc, versionWithBuild
|
||||
from aqt.qt import *
|
||||
from aqt.utils import openLink, showText
|
||||
from aqt.utils import TR, openLink, showText, tr
|
||||
|
||||
|
||||
class LatestVersionFinder(QThread):
|
||||
|
|
@ -56,12 +56,12 @@ class LatestVersionFinder(QThread):
|
|||
|
||||
|
||||
def askAndUpdate(mw, ver):
|
||||
baseStr = _("""<h1>Anki Updated</h1>Anki %s has been released.<br><br>""") % ver
|
||||
baseStr = tr(TR.QT_MISC_ANKI_UPDATEDANKI_HAS_BEEN_RELEASED, val="%s") % ver
|
||||
msg = QMessageBox(mw)
|
||||
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) # type: ignore
|
||||
msg.setIcon(QMessageBox.Information)
|
||||
msg.setText(baseStr + _("Would you like to download it now?"))
|
||||
button = QPushButton(_("Ignore this update"))
|
||||
msg.setText(baseStr + tr(TR.QT_MISC_WOULD_YOU_LIKE_TO_DOWNLOAD_IT))
|
||||
button = QPushButton(tr(TR.QT_MISC_IGNORE_THIS_UPDATE))
|
||||
msg.addButton(button, QMessageBox.RejectRole)
|
||||
msg.setDefaultButton(QMessageBox.Yes)
|
||||
ret = msg.exec_()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# 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 os
|
||||
|
|
@ -54,7 +53,7 @@ def openHelp(section):
|
|||
|
||||
|
||||
def openLink(link):
|
||||
tooltip(_("Loading..."), period=1000)
|
||||
tooltip(tr(TR.QT_MISC_LOADING), period=1000)
|
||||
with noBundledLibs():
|
||||
QDesktopServices.openUrl(QUrl(link))
|
||||
|
||||
|
|
@ -145,7 +144,7 @@ def showText(
|
|||
def onCopy():
|
||||
QApplication.clipboard().setText(text.toPlainText())
|
||||
|
||||
btn = QPushButton(_("Copy to Clipboard"))
|
||||
btn = QPushButton(tr(TR.QT_MISC_COPY_TO_CLIPBOARD))
|
||||
qconnect(btn.clicked, onCopy)
|
||||
box.addButton(btn, QDialogButtonBox.ActionRole)
|
||||
|
||||
|
|
@ -209,8 +208,8 @@ class ButtonedDialog(QMessageBox):
|
|||
for b in buttons:
|
||||
self._buttons.append(self.addButton(b, QMessageBox.AcceptRole))
|
||||
if help:
|
||||
self.addButton(_("Help"), QMessageBox.HelpRole)
|
||||
buttons.append(_("Help"))
|
||||
self.addButton(tr(TR.ACTIONS_HELP), QMessageBox.HelpRole)
|
||||
buttons.append(tr(TR.ACTIONS_HELP))
|
||||
# self.setLayout(v)
|
||||
|
||||
def run(self):
|
||||
|
|
@ -408,9 +407,7 @@ def getSaveFile(parent, title, dir_description, key, ext, fname=None):
|
|||
aqt.mw.pm.profile[config_key] = dir
|
||||
# check if it exists
|
||||
if os.path.exists(file):
|
||||
if not askUser(
|
||||
_("This file exists. Are you sure you want to overwrite it?"), parent
|
||||
):
|
||||
if not askUser(tr(TR.QT_MISC_THIS_FILE_EXISTS_ARE_YOU_SURE), parent):
|
||||
return None
|
||||
return file
|
||||
|
||||
|
|
@ -656,7 +653,7 @@ def closeTooltip():
|
|||
def checkInvalidFilename(str, dirsep=True):
|
||||
bad = invalidFilename(str, dirsep)
|
||||
if bad:
|
||||
showWarning(_("The following character can not be used: %s") % bad)
|
||||
showWarning(tr(TR.QT_MISC_THE_FOLLOWING_CHARACTER_CAN_NOT_BE, val="%s") % bad)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# -*- coding: utf-8 -*-
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
import dataclasses
|
||||
import json
|
||||
import re
|
||||
|
|
@ -14,7 +13,7 @@ from anki.utils import isLin, isMac, isWin
|
|||
from aqt import gui_hooks
|
||||
from aqt.qt import *
|
||||
from aqt.theme import theme_manager
|
||||
from aqt.utils import openLink, showInfo
|
||||
from aqt.utils import TR, openLink, showInfo, tr
|
||||
|
||||
serverbaseurl = re.compile(r"^.+:\/\/[^\/]+")
|
||||
|
||||
|
|
@ -286,7 +285,7 @@ class AnkiWebView(QWebEngineView):
|
|||
|
||||
def contextMenuEvent(self, evt: QContextMenuEvent) -> None:
|
||||
m = QMenu(self)
|
||||
a = m.addAction(_("Copy"))
|
||||
a = m.addAction(tr(TR.ACTIONS_COPY))
|
||||
qconnect(a.triggered, self.onCopy)
|
||||
gui_hooks.webview_will_show_context_menu(self, m)
|
||||
m.popup(QCursor.pos())
|
||||
|
|
|
|||
49
repos.bzl
49
repos.bzl
|
|
@ -129,44 +129,49 @@ def register_repos():
|
|||
qtpo_i18n_commit = "872d7f0f6bde52577e8fc795dd85699b0eeb97d5"
|
||||
qtpo_i18n_shallow_since = "1605564627 +0000"
|
||||
|
||||
new_git_repository(
|
||||
name = "rslib_ftl",
|
||||
build_file_content = """
|
||||
i18n_build_content = """
|
||||
filegroup(
|
||||
name = "files",
|
||||
srcs = glob(["**/*.ftl"]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
exports_files(["l10n.toml"])
|
||||
""",
|
||||
commit = core_i18n_commit,
|
||||
shallow_since = core_i18n_shallow_since,
|
||||
remote = "https://github.com/ankitects/anki-core-i18n",
|
||||
"""
|
||||
|
||||
native.new_local_repository(
|
||||
name = "rslib_ftl",
|
||||
path = "../anki-i18n/core",
|
||||
build_file_content = i18n_build_content,
|
||||
)
|
||||
|
||||
# new_git_repository(
|
||||
# name = "rslib_ftl",
|
||||
# build_file_content = i18n_build_content,
|
||||
# commit = core_i18n_commit,
|
||||
# shallow_since = core_i18n_shallow_since,
|
||||
# remote = "https://github.com/ankitects/anki-core-i18n",
|
||||
# )
|
||||
|
||||
if not native.existing_rule("extra_ftl"):
|
||||
new_git_repository(
|
||||
native.new_local_repository(
|
||||
name = "extra_ftl",
|
||||
build_file_content = """
|
||||
filegroup(
|
||||
name = "files",
|
||||
srcs = glob(["**/*.ftl"]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
exports_files(["l10n.toml"])
|
||||
""",
|
||||
commit = qtftl_i18n_commit,
|
||||
shallow_since = qtftl_i18n_shallow_since,
|
||||
remote = "https://github.com/ankitects/anki-desktop-ftl",
|
||||
path = "../anki-i18n/qtftl",
|
||||
build_file_content = i18n_build_content,
|
||||
)
|
||||
|
||||
# new_git_repository(
|
||||
# name = "extra_ftl",
|
||||
# build_file_content = i18n_build_content,
|
||||
# commit = qtftl_i18n_commit,
|
||||
# shallow_since = qtftl_i18n_shallow_since,
|
||||
# remote = "https://github.com/ankitects/anki-desktop-ftl",
|
||||
# )
|
||||
|
||||
new_git_repository(
|
||||
name = "aqt_po",
|
||||
build_file_content = """
|
||||
exports_files(glob(["**/*.pot", "**/*.po"]),
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
commit = qtpo_i18n_commit,
|
||||
|
|
|
|||
32
rslib/ftl/actions.ftl
Normal file
32
rslib/ftl/actions.ftl
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
actions-add = Add
|
||||
actions-blue-flag = Blue Flag
|
||||
actions-cancel = Cancel
|
||||
actions-choose = Choose
|
||||
actions-close = Close
|
||||
actions-copy = Copy
|
||||
actions-custom-study = Custom Study
|
||||
actions-decks = Decks
|
||||
actions-delete = Delete
|
||||
actions-export = Export
|
||||
actions-filter = Filter
|
||||
actions-green-flag = Green Flag
|
||||
actions-help = Help
|
||||
actions-import = Import
|
||||
actions-manage = Manage...
|
||||
actions-name = Name:
|
||||
actions-new = New
|
||||
actions-new-name = New name:
|
||||
actions-options = Options
|
||||
actions-options-for = Options for { $val }
|
||||
actions-orange-flag = Orange Flag
|
||||
actions-preview = Preview
|
||||
actions-rebuild = Rebuild
|
||||
actions-red-flag = Red Flag
|
||||
actions-rename = Rename
|
||||
actions-rename-deck = Rename Deck
|
||||
actions-replay-audio = Replay Audio
|
||||
actions-reposition = Reposition
|
||||
actions-save = Save
|
||||
actions-search = Search
|
||||
actions-shortcut-key = Shortcut key: { $val }
|
||||
actions-suspend-card = Suspend Card
|
||||
9
rslib/ftl/adding.ftl
Normal file
9
rslib/ftl/adding.ftl
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
adding-add-shortcut-ctrlandenter = Add (shortcut: ctrl+enter)
|
||||
adding-added = Added
|
||||
adding-close-and-lose-current-input = Close and lose current input?
|
||||
adding-edit = Edit "{ $val }"
|
||||
adding-history = History
|
||||
adding-note-deleted = (Note deleted)
|
||||
adding-shortcut = Shortcut: { $val }
|
||||
adding-the-first-field-is-empty = The first field is empty.
|
||||
adding-you-have-a-cloze-deletion-note = You have a cloze deletion note type but have not made any cloze deletions. Proceed?
|
||||
104
rslib/ftl/browsing.ftl
Normal file
104
rslib/ftl/browsing.ftl
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
browsing-add-notes = Add Notes...
|
||||
browsing-add-tags = Add Tags
|
||||
browsing-add-tags2 = Add Tags...
|
||||
browsing-added-today = Added Today
|
||||
browsing-addon = Add-on
|
||||
browsing-again-today = Again Today
|
||||
browsing-all-card-types = All Card Types
|
||||
browsing-all-fields = All Fields
|
||||
browsing-answer = Answer
|
||||
browsing-any-cards-mapped-to-nothing-will = Any cards mapped to nothing will be deleted. If a note has no remaining cards, it will be lost. Are you sure you want to continue?
|
||||
browsing-any-flag = Any Flag
|
||||
browsing-browser-appearance = Browser Appearance
|
||||
browsing-browser-options = Browser Options
|
||||
browsing-buried = Buried
|
||||
browsing-card = Card
|
||||
browsing-card-list = Card List
|
||||
browsing-card-state = Card State
|
||||
browsing-cards-cant-be-manually-moved-into = Cards can't be manually moved into a filtered deck.
|
||||
browsing-change-deck = Change Deck
|
||||
browsing-change-deck2 = Change Deck...
|
||||
browsing-change-note-type = Change Note Type
|
||||
browsing-change-note-type2 = Change Note Type...
|
||||
browsing-change-to = Change { $val } to:
|
||||
browsing-clear-unused = Clear Unused
|
||||
browsing-clear-unused-tags = Clear Unused Tags
|
||||
browsing-created = Created
|
||||
browsing-ctrlandshiftande = Ctrl+Shift+E
|
||||
browsing-current-deck = Current Deck
|
||||
browsing-current-note-type = Current note type:
|
||||
browsing-delete-notes = Delete Notes
|
||||
browsing-delete-tags = Delete Tags
|
||||
browsing-duplicate = duplicate
|
||||
browsing-ease = Ease
|
||||
browsing-end = End
|
||||
browsing-enter-tags-to-add = Enter tags to add:
|
||||
browsing-enter-tags-to-delete = Enter tags to delete:
|
||||
browsing-filter = Filter...
|
||||
browsing-filtered = (filtered)
|
||||
browsing-find = <b>Find</b>:
|
||||
browsing-find-and-replace = Find and Replace
|
||||
browsing-find-duplicates = Find Duplicates
|
||||
browsing-first-card = First Card
|
||||
browsing-flag = Flag
|
||||
browsing-font = <b>Font</b>:
|
||||
browsing-font-size = <b>Font Size</b>:
|
||||
browsing-found-as-across-bs = Found %(a)s across %(b)s.
|
||||
browsing-home = Home
|
||||
browsing-ignore-case = Ignore case
|
||||
browsing-in = <b>In</b>:
|
||||
browsing-interval = Interval
|
||||
browsing-last-card = Last Card
|
||||
browsing-learning = (learning)
|
||||
browsing-line-size = <b>Line Size</b>:
|
||||
browsing-manage-note-types = Manage Note Types...
|
||||
browsing-move-cards = Move Cards
|
||||
browsing-move-cards-to-deck = Move cards to deck:
|
||||
browsing-nd-names = %(n)d: %(name)s
|
||||
browsing-new = (new)
|
||||
browsing-new-note-type = New note type:
|
||||
browsing-no-flag = No Flag
|
||||
browsing-note = Note
|
||||
browsing-notes-tagged = Notes tagged.
|
||||
browsing-nothing = Nothing
|
||||
browsing-only-new-cards-can-be-repositioned = Only new cards can be repositioned.
|
||||
browsing-optional-filter = Optional filter:
|
||||
browsing-override-back-template = Override back template:
|
||||
browsing-override-font = Override font:
|
||||
browsing-override-front-template = Override front template:
|
||||
browsing-place-at-end-of-new-card = Place at end of new card queue
|
||||
browsing-place-in-review-queue-with-interval = Place in review queue with interval between:
|
||||
browsing-please-give-your-filter-a-name = Please give your filter a name:
|
||||
browsing-please-select-cards-from-only-one = Please select cards from only one note type.
|
||||
browsing-preview-selected-card = Preview Selected Card ({ $val })
|
||||
browsing-question = Question
|
||||
browsing-queue-bottom = Queue bottom: { $val }
|
||||
browsing-queue-top = Queue top: { $val }
|
||||
browsing-randomize-order = Randomize order
|
||||
browsing-remove-current-filter = Remove Current Filter...
|
||||
browsing-remove-from-your-saved-searches = Remove { $val } from your saved searches?
|
||||
browsing-remove-tags = Remove Tags...
|
||||
browsing-replace-with = <b>Replace With</b>:
|
||||
browsing-reposition = Reposition...
|
||||
browsing-reposition-new-cards = Reposition New Cards
|
||||
browsing-reschedule = Reschedule
|
||||
browsing-save-current-filter = Save Current Filter...
|
||||
browsing-search-in = Search in:
|
||||
browsing-search-within-formatting-slow = Search within formatting (slow)
|
||||
browsing-shift-position-of-existing-cards = Shift position of existing cards
|
||||
browsing-sidebar = Sidebar
|
||||
browsing-sort-field = Sort Field
|
||||
browsing-sorting-on-this-column-is-not = Sorting on this column is not supported. Please choose another.
|
||||
browsing-start-position = Start position:
|
||||
browsing-step = Step:
|
||||
browsing-studied-today = Studied Today
|
||||
browsing-suspended = Suspended
|
||||
browsing-tag-duplicates = Tag Duplicates
|
||||
browsing-target-field = Target field:
|
||||
browsing-today = Today
|
||||
browsing-toggle-mark = Toggle Mark
|
||||
browsing-toggle-suspend = Toggle Suspend
|
||||
browsing-treat-input-as-regular-expression = Treat input as regular expression
|
||||
browsing-type-here-to-search = <type here to search; hit enter to show current deck>
|
||||
browsing-whole-collection = Whole Collection
|
||||
browsing-you-must-have-at-least-one = You must have at least one column.
|
||||
|
|
@ -21,3 +21,22 @@ card-templates-preview-settings = Options
|
|||
card-templates-invalid-template-number = Card template { $number } has a problem.
|
||||
card-templates-changes-saved = Changes saved.
|
||||
card-templates-discard-changes = Discard changes?
|
||||
card-templates-add-card-type = Add Card Type...
|
||||
card-templates-anki-couldnt-find-the-line-between = Anki couldn't find the line between the question and answer. Please adjust the template manually to switch the question and answer.
|
||||
card-templates-at-least-one-card-type-is = At least one card type is required.
|
||||
card-templates-browser-appearance = Browser Appearance...
|
||||
card-templates-card = Card { $val }
|
||||
card-templates-card-types = Card Types
|
||||
card-templates-card-types-for = Card Types for { $val }
|
||||
card-templates-cloze = Cloze { $val }
|
||||
card-templates-deck-override = Deck Override...
|
||||
card-templates-delete-the-as-card-type-and = Delete the '%(a)s' card type, and its %(b)s?
|
||||
card-templates-enter-deck-to-place-new = Enter deck to place new { $val } cards in, or leave blank:
|
||||
card-templates-enter-new-card-position-1 = Enter new card position (1...{ $val }):
|
||||
card-templates-flip = Flip
|
||||
card-templates-form = Form
|
||||
card-templates-off = (off)
|
||||
card-templates-on = (on)
|
||||
card-templates-remove-card-type = Remove Card Type...
|
||||
card-templates-rename-card-type = Rename Card Type...
|
||||
card-templates-reposition-card-type = Reposition Card Type...
|
||||
|
|
|
|||
|
|
@ -1 +1,28 @@
|
|||
custom-study-must-rename-deck = Please rename the existing Custom Study deck first.
|
||||
custom-study-all-cards-in-random-order-dont = All cards in random order (don't reschedule)
|
||||
custom-study-all-review-cards-in-random-order = All review cards in random order
|
||||
custom-study-cards = cards
|
||||
custom-study-cards-from-the-deck = cards from the deck
|
||||
custom-study-choose-tags = Choose Tags
|
||||
custom-study-custom-study-session = Custom Study Session
|
||||
custom-study-due-cards-only = Due cards only
|
||||
custom-study-increase-todays-new-card-limit = Increase today's new card limit
|
||||
custom-study-increase-todays-new-card-limit-by = Increase today's new card limit by
|
||||
custom-study-increase-todays-review-card-limit = Increase today's review card limit
|
||||
custom-study-increase-todays-review-limit-by = Increase today's review limit by
|
||||
custom-study-new-cards-in-deck-over-today = New cards in deck over today limit: { $val }
|
||||
custom-study-new-cards-only = New cards only
|
||||
custom-study-no-cards-matched-the-criteria-you = No cards matched the criteria you provided.
|
||||
custom-study-ok = OK
|
||||
custom-study-preview-new-cards = Preview new cards
|
||||
custom-study-preview-new-cards-added-in-the = Preview new cards added in the last
|
||||
custom-study-require-one-or-more-of-these = Require one or more of these tags:
|
||||
custom-study-review-ahead = Review ahead
|
||||
custom-study-review-ahead-by = Review ahead by
|
||||
custom-study-review-cards-forgotten-in-last = Review cards forgotten in last
|
||||
custom-study-review-forgotten-cards = Review forgotten cards
|
||||
custom-study-reviews-due-in-deck-over-today = Reviews due in deck over today limit: { $val }
|
||||
custom-study-select = Select
|
||||
custom-study-select-tags-to-exclude = Select tags to exclude:
|
||||
custom-study-selective-study = Selective Study
|
||||
custom-study-study-by-card-state-or-tag = Study by card state or tag
|
||||
|
|
|
|||
33
rslib/ftl/decks.ftl
Normal file
33
rslib/ftl/decks.ftl
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
decks-add-new-deck-ctrlandn = Add New Deck (Ctrl+N)
|
||||
decks-are-you-sure-you-wish-to = Are you sure you wish to delete { $val }?
|
||||
decks-build = Build
|
||||
decks-cards-selected-by = cards selected by
|
||||
decks-create-deck = Create Deck
|
||||
decks-custom-steps-in-minutes = Custom steps (in minutes)
|
||||
decks-deck = Deck
|
||||
decks-decreasing-intervals = Decreasing intervals
|
||||
decks-delete-deck = Delete Deck
|
||||
decks-enable-second-filter = Enable second filter
|
||||
decks-filter = Filter:
|
||||
decks-filter-2 = Filter 2
|
||||
decks-get-shared = Get Shared
|
||||
decks-import-file = Import File
|
||||
decks-increasing-intervals = Increasing intervals
|
||||
decks-latest-added-first = Latest added first
|
||||
decks-limit-to = Limit to
|
||||
decks-minutes = minutes
|
||||
decks-most-lapses = Most lapses
|
||||
decks-name-for-deck = Name for deck:
|
||||
decks-new-deck-name = New deck name:
|
||||
decks-no-deck = [no deck]
|
||||
decks-oldest-seen-first = Oldest seen first
|
||||
decks-order-added = Order added
|
||||
decks-order-due = Order due
|
||||
decks-please-select-something = Please select something.
|
||||
decks-random = Random
|
||||
decks-relative-overdueness = Relative overdueness
|
||||
decks-repeat-failed-cards-after = Repeat failed cards after
|
||||
decks-reschedule-cards-based-on-my-answers = Reschedule cards based on my answers in this deck
|
||||
decks-study = Study
|
||||
decks-study-deck = Study Deck
|
||||
decks-the-provided-search-did-not-match = The provided search did not match any cards. Would you like to revise it?
|
||||
35
rslib/ftl/editing.ftl
Normal file
35
rslib/ftl/editing.ftl
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
editing-add-media = Add Media
|
||||
editing-an-error-occurred-while-opening = An error occurred while opening { $val }
|
||||
editing-attach-picturesaudiovideo-f3 = Attach pictures/audio/video (F3)
|
||||
editing-bold-text-ctrlandb = Bold text (Ctrl+B)
|
||||
editing-cards = Cards
|
||||
editing-change-colour-f8 = Change colour (F8)
|
||||
editing-cloze-deletion-ctrlandshiftandc = Cloze deletion (Ctrl+Shift+C)
|
||||
editing-couldnt-record-audio-have-you-installed = Couldn't record audio. Have you installed 'lame'?
|
||||
editing-customize-card-templates-ctrlandl = Customize Card Templates (Ctrl+L)
|
||||
editing-customize-fields = Customize Fields
|
||||
editing-cut = Cut
|
||||
editing-edit-current = Edit Current
|
||||
editing-edit-html = Edit HTML
|
||||
editing-fields = Fields
|
||||
editing-html-editor = HTML Editor
|
||||
editing-italic-text-ctrlandi = Italic text (Ctrl+I)
|
||||
editing-jump-to-tags-with-ctrlandshiftandt = Jump to tags with Ctrl+Shift+T
|
||||
editing-latex = LaTeX
|
||||
editing-latex-equation = LaTeX equation
|
||||
editing-latex-math-env = LaTeX math env.
|
||||
editing-mathjax-block = MathJax block
|
||||
editing-mathjax-chemistry = MathJax chemistry
|
||||
editing-mathjax-inline = MathJax inline
|
||||
editing-media = Media
|
||||
editing-paste = Paste
|
||||
editing-record-audio-f5 = Record audio (F5)
|
||||
editing-remove-formatting-ctrlandr = Remove formatting (Ctrl+R)
|
||||
editing-set-foreground-colour-f7 = Set foreground colour (F7)
|
||||
editing-show-duplicates = Show Duplicates
|
||||
editing-subscript-ctrland = Subscript (Ctrl+=)
|
||||
editing-superscript-ctrlandand = Superscript (Ctrl++)
|
||||
editing-tags = Tags
|
||||
editing-to-make-a-cloze-deletion-on = To make a cloze deletion on an existing note, you need to change it to a cloze type first, via 'Notes>Change Note Type'
|
||||
editing-underline-text-ctrlandu = Underline text (Ctrl+U)
|
||||
editing-warning-cloze-deletions-will-not-work = Warning, cloze deletions will not work until you switch the type at the top to Cloze.
|
||||
17
rslib/ftl/exporting.ftl
Normal file
17
rslib/ftl/exporting.ftl
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
exporting-all-decks = All Decks
|
||||
exporting-anki-20-deck = Anki 2.0 Deck
|
||||
exporting-anki-collection-package = Anki Collection Package
|
||||
exporting-anki-deck-package = Anki Deck Package
|
||||
exporting-cards-in-plain-text = Cards in Plain Text
|
||||
exporting-collection = collection
|
||||
exporting-collection-exported = Collection exported.
|
||||
exporting-couldnt-save-file = Couldn't save file: { $val }
|
||||
exporting-export = Export...
|
||||
exporting-export-format = <b>Export format</b>:
|
||||
exporting-include = <b>Include</b>:
|
||||
exporting-include-html-and-media-references = Include HTML and media references
|
||||
exporting-include-media = Include media
|
||||
exporting-include-scheduling-information = Include scheduling information
|
||||
exporting-include-tags = Include tags
|
||||
exporting-notes-in-plain-text = Notes in Plain Text
|
||||
exporting-selected-notes = Selected Notes
|
||||
14
rslib/ftl/fields.ftl
Normal file
14
rslib/ftl/fields.ftl
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
fields-add-field = Add Field
|
||||
fields-delete-field-from = Delete field from { $val }?
|
||||
fields-editing-font = Editing Font
|
||||
fields-field = Field:
|
||||
fields-field-name = Field name:
|
||||
fields-fields-for = Fields for { $val }
|
||||
fields-font = Font:
|
||||
fields-new-position-1 = New position (1...{ $val }):
|
||||
fields-notes-require-at-least-one-field = Notes require at least one field.
|
||||
fields-remember-last-input-when-adding = Remember last input when adding
|
||||
fields-reverse-text-direction-rtl = Reverse text direction (RTL)
|
||||
fields-size = Size:
|
||||
fields-sort-by-this-field-in-the = Sort by this field in the browser
|
||||
fields-that-field-name-is-already-used = That field name is already used.
|
||||
|
|
@ -1 +1,49 @@
|
|||
importing-failed-debug-info = Import failed. Debugging info:
|
||||
importing-aborted = Aborted: { $val }
|
||||
importing-added-duplicate-with-first-field = Added duplicate with first field: { $val }
|
||||
importing-allow-html-in-fields = Allow HTML in fields
|
||||
importing-anki-files-are-from-a-very = .anki files are from a very old version of Anki. You can import them with Anki 2.0, available on the Anki website.
|
||||
importing-anki2-files-are-not-directly-importable = .anki2 files are not directly importable - please import the .apkg or .zip file you have received instead.
|
||||
importing-appeared-twice-in-file = Appeared twice in file: { $val }
|
||||
importing-by-default-anki-will-detect-the = By default, Anki will detect the character between fields, such as a tab, comma, and so on. If Anki is detecting the character incorrectly, you can enter it here. Use \t to represent tab.
|
||||
importing-change = Change
|
||||
importing-colon = Colon
|
||||
importing-comma = Comma
|
||||
importing-empty-first-field = Empty first field: { $val }
|
||||
importing-field-mapping = Field mapping
|
||||
importing-field-of-file-is = Field <b>{ $val }</b> of file is:
|
||||
importing-fields-separated-by = Fields separated by: { $val }
|
||||
importing-file-version-unknown-trying-import-anyway = File version unknown, trying import anyway.
|
||||
importing-first-field-matched = First field matched: { $val }
|
||||
importing-identical = Identical
|
||||
importing-ignore-field = Ignore field
|
||||
importing-ignore-lines-where-first-field-matches = Ignore lines where first field matches existing note
|
||||
importing-ignored = <ignored>
|
||||
importing-import-even-if-existing-note-has = Import even if existing note has same first field
|
||||
importing-import-options = Import options
|
||||
importing-importing-complete = Importing complete.
|
||||
importing-invalid-file-please-restore-from-backup = Invalid file. Please restore from backup.
|
||||
importing-map-to = Map to { $val }
|
||||
importing-map-to-tags = Map to Tags
|
||||
importing-mapped-to = mapped to <b>{ $val }</b>
|
||||
importing-mapped-to-tags = mapped to <b>Tags</b>
|
||||
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-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-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-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-semicolon = Semicolon
|
||||
importing-skipped = Skipped
|
||||
importing-tab = Tab
|
||||
importing-tag-modified-notes = Tag modified notes:
|
||||
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-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-will-delete-your-existing-collection = This will delete your existing collection and replace it with the data in the file you're importing. Are you sure?
|
||||
importing-unable-to-import-from-a-readonly = Unable to import from a read-only file.
|
||||
importing-unknown-file-format = Unknown file format.
|
||||
importing-update-existing-notes-when-first-field = Update existing notes when first field matches
|
||||
importing-updated = Updated
|
||||
|
|
|
|||
7
rslib/ftl/media.ftl
Normal file
7
rslib/ftl/media.ftl
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
media-error-executing = Error executing { $val }.
|
||||
media-error-running = Error running { $val }
|
||||
media-for-security-reasons-is-not = For security reasons, '{ $val }' 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.
|
||||
media-generated-file = Generated file: { $val }
|
||||
media-have-you-installed-latex-and-dvipngdvisvgm = Have you installed latex and dvipng/dvisvgm?
|
||||
media-recordingtime = Recording...<br>Time: %0.1f
|
||||
media-sound-and-video-on-cards-will = Sound and video on cards will not function until mpv or mplayer is installed.
|
||||
|
|
@ -19,3 +19,18 @@ notetypes-cloze-name = Cloze
|
|||
notetypes-card-1-name = Card 1
|
||||
notetypes-card-2-name = Card 2
|
||||
|
||||
notetypes-add = Add: { $val }
|
||||
notetypes-add-note-type = Add Note Type
|
||||
notetypes-cards = Cards...
|
||||
notetypes-clone = Clone: { $val }
|
||||
notetypes-copy = { $val } copy
|
||||
notetypes-create-scalable-images-with-dvisvgm = Create scalable images with dvisvgm
|
||||
notetypes-delete-this-note-type-and-all = Delete this note type and all its cards?
|
||||
notetypes-delete-this-unused-note-type = Delete this unused note type?
|
||||
notetypes-fields = Fields...
|
||||
notetypes-footer = Footer
|
||||
notetypes-header = Header
|
||||
notetypes-note-types = Note Types
|
||||
notetypes-options = Options...
|
||||
notetypes-please-add-another-note-type-first = Please add another note type first.
|
||||
notetypes-type = Type
|
||||
|
|
|
|||
39
rslib/ftl/preferences.ftl
Normal file
39
rslib/ftl/preferences.ftl
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
preferences-anki-21-scheduler-beta = Anki 2.1 scheduler (beta)
|
||||
preferences-automatically-sync-on-profile-openclose = Automatically sync on profile open/close
|
||||
preferences-backups = Backups
|
||||
preferences-backups2 = backups
|
||||
preferences-backupsanki-will-create-a-backup-of = <html><head/><body><p><span style=" font-weight:600;">Backups</span><br/>Anki will create a backup of your collection each time it is closed.</p></body></html>
|
||||
preferences-basic = Basic
|
||||
preferences-change-deck-depending-on-note-type = Change deck depending on note type
|
||||
preferences-changes-will-take-effect-when-you = Changes will take effect when you restart Anki.
|
||||
preferences-hardware-acceleration-faster-may-cause-display = Hardware acceleration (faster, may cause display issues)
|
||||
preferences-hours-past-midnight = hours past midnight
|
||||
preferences-interface-language = Interface language:
|
||||
preferences-interrupt-current-audio-when-answering = Interrupt current audio when answering
|
||||
preferences-keep = Keep
|
||||
preferences-learn-ahead-limit = Learn ahead limit
|
||||
preferences-mins = mins
|
||||
preferences-network = Network
|
||||
preferences-next-day-starts-at = Next day starts at
|
||||
preferences-night-mode = Night mode
|
||||
preferences-note-media-is-not-backed-up = Note: Media is not backed up. Please create a periodic backup of your Anki folder to be safe.
|
||||
preferences-on-next-sync-force-changes-in = On next sync, force changes in one direction
|
||||
preferences-paste-clipboard-images-as-png = Paste clipboard images as PNG
|
||||
preferences-paste-without-shift-key-strips-formatting = Paste without shift key strips formatting
|
||||
preferences-periodically-sync-media = Periodically sync media
|
||||
preferences-please-restart-anki-to-complete-language = Please restart Anki to complete language change.
|
||||
preferences-preferences = Preferences
|
||||
preferences-scheduling = Scheduling
|
||||
preferences-show-learning-cards-with-larger-steps = Show learning cards with larger steps before reviews
|
||||
preferences-show-next-review-time-above-answer = Show next review time above answer buttons
|
||||
preferences-show-play-buttons-on-cards-with = Show play buttons on cards with audio
|
||||
preferences-show-remaining-card-count-during-review = Show remaining card count during review
|
||||
preferences-some-settings-will-take-effect-after = Some settings will take effect after you restart Anki.
|
||||
preferences-synchronisation = <b>Synchronisation</b>
|
||||
preferences-synchronizationnot-currently-enabled-click-the-sync = <b>Synchronization</b><br> Not currently enabled; click the sync button in the main window to enable.
|
||||
preferences-synchronize-audio-and-images-too = Synchronize audio and images too
|
||||
preferences-this-will-reset-any-cards-in = This will reset any cards in learning, clear filtered decks, and change the scheduler version. Proceed?
|
||||
preferences-timebox-time-limit = Timebox time limit
|
||||
preferences-user-interface-size = User interface size
|
||||
preferences-when-adding-default-to-current-deck = When adding, default to current deck
|
||||
preferences-you-can-restore-backups-via-fileswitch = You can restore backups via File>Switch Profile.
|
||||
10
rslib/ftl/profiles.ftl
Normal file
10
rslib/ftl/profiles.ftl
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
profiles-anki-could-not-read-your-profile = Anki could not read your profile data. Window sizes and your sync login details have been forgotten.
|
||||
profiles-anki-could-not-rename-your-profile = Anki could not rename your profile because it could not rename the profile folder on disk. Please ensure you have permission to write to Documents/Anki and no other programs are accessing your profile folders, then try again.
|
||||
profiles-folder-already-exists = Folder already exists.
|
||||
profiles-open = Open
|
||||
profiles-open-backup = Open Backup...
|
||||
profiles-please-remove-the-folder-and = Please remove the folder { $val } and try again.
|
||||
profiles-profile-corrupt = Profile Corrupt
|
||||
profiles-profiles = Profiles
|
||||
profiles-quit = Quit
|
||||
profiles-user-1 = User 1
|
||||
|
|
@ -90,3 +90,51 @@ scheduling-how-to-custom-study = If you wish to study outside of the regular sch
|
|||
# used in scheduling-how-to-custom-study
|
||||
# "... you can use the custom study feature."
|
||||
scheduling-custom-study = custom study
|
||||
scheduling-always-include-question-side-when-replaying = Always include question side when replaying audio
|
||||
scheduling-at-least-one-step-is-required = At least one step is required.
|
||||
scheduling-automatically-play-audio = Automatically play audio
|
||||
scheduling-bury-related-new-cards-until-the = Bury related new cards until the next day
|
||||
scheduling-bury-related-reviews-until-the-next = Bury related reviews until the next day
|
||||
scheduling-days = days
|
||||
scheduling-description = Description
|
||||
scheduling-description-to-show-on-overview-screen = Description to show on overview screen, for current deck:
|
||||
scheduling-easy-bonus = Easy bonus
|
||||
scheduling-easy-interval = Easy interval
|
||||
scheduling-end = (end)
|
||||
scheduling-general = General
|
||||
scheduling-graduating-interval = Graduating interval
|
||||
scheduling-hard-interval = Hard interval
|
||||
scheduling-ignore-answer-times-longer-than = Ignore answer times longer than
|
||||
scheduling-interval-modifier = Interval modifier
|
||||
scheduling-lapses = Lapses
|
||||
scheduling-lapses2 = lapses
|
||||
scheduling-learning = Learning
|
||||
scheduling-leech-action = Leech action
|
||||
scheduling-leech-threshold = Leech threshold
|
||||
scheduling-maximum-interval = Maximum interval
|
||||
scheduling-maximum-reviewsday = Maximum reviews/day
|
||||
scheduling-minimum-interval = Minimum interval
|
||||
scheduling-mix-new-cards-and-reviews = Mix new cards and reviews
|
||||
scheduling-new-cards = New Cards
|
||||
scheduling-new-cardsday = New cards/day
|
||||
scheduling-new-interval = New interval
|
||||
scheduling-new-options-group-name = New options group name:
|
||||
scheduling-options-group = Options group:
|
||||
scheduling-order = Order
|
||||
scheduling-parent-limit = (parent limit: { $val })
|
||||
scheduling-review = Review
|
||||
scheduling-reviews = Reviews
|
||||
scheduling-seconds = seconds
|
||||
scheduling-set-all-decks-below-to = Set all decks below { $val } to this option group?
|
||||
scheduling-set-for-all-subdecks = Set for all subdecks
|
||||
scheduling-show-answer-timer = Show answer timer
|
||||
scheduling-show-new-cards-after-reviews = Show new cards after reviews
|
||||
scheduling-show-new-cards-before-reviews = Show new cards before reviews
|
||||
scheduling-show-new-cards-in-order-added = Show new cards in order added
|
||||
scheduling-show-new-cards-in-random-order = Show new cards in random order
|
||||
scheduling-starting-ease = Starting ease
|
||||
scheduling-steps-in-minutes = Steps (in minutes)
|
||||
scheduling-steps-must-be-numbers = Steps must be numbers.
|
||||
scheduling-tag-only = Tag Only
|
||||
scheduling-the-default-configuration-cant-be-removed = The default configuration can't be removed.
|
||||
scheduling-your-changes-will-affect-multiple-decks = Your changes will affect multiple decks. If you wish to change only the current deck, please add a new options group first.
|
||||
|
|
|
|||
44
rslib/ftl/studying.ftl
Normal file
44
rslib/ftl/studying.ftl
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
studying-again = Again
|
||||
studying-all-buried-cards = All Buried Cards
|
||||
studying-audio-5s = Audio -5s
|
||||
studying-audio-and5s = Audio +5s
|
||||
studying-buried-siblings = Buried Siblings
|
||||
studying-bury = Bury
|
||||
studying-bury-card = Bury Card
|
||||
studying-bury-note = Bury Note
|
||||
studying-card-buried = Card buried.
|
||||
studying-card-suspended = Card suspended.
|
||||
studying-card-was-a-leech = Card was a leech.
|
||||
studying-cards-will-be-automatically-returned-to = Cards will be automatically returned to their original decks after you review them.
|
||||
studying-continue = Continue
|
||||
studying-delete-note = Delete Note
|
||||
studying-deleting-this-deck-from-the-deck = Deleting this deck from the deck list will return all remaining cards to their original deck.
|
||||
studying-easy = Easy
|
||||
studying-edit = Edit
|
||||
studying-empty = Empty
|
||||
studying-finish = Finish
|
||||
studying-flag-card = Flag Card
|
||||
studying-good = Good
|
||||
studying-hard = Hard
|
||||
studying-it-has-been-suspended = It has been suspended.
|
||||
studying-manually-buried-cards = Manually Buried Cards
|
||||
studying-mark-note = Mark Note
|
||||
studying-more = More
|
||||
studying-no-cards-are-due-yet = No cards are due yet.
|
||||
studying-note-buried = Note buried.
|
||||
studying-note-suspended = Note suspended.
|
||||
studying-pause-audio = Pause Audio
|
||||
studying-please-run-toolsempty-cards = Please run Tools>Empty Cards
|
||||
studying-record-own-voice = Record Own Voice
|
||||
studying-replay-own-voice = Replay Own Voice
|
||||
studying-show-answer = Show Answer
|
||||
studying-space = Space
|
||||
studying-study-now = Study Now
|
||||
studying-suspend = Suspend
|
||||
studying-suspend-note = Suspend Note
|
||||
studying-this-is-a-special-deck-for = This is a special deck for studying outside of the normal schedule.
|
||||
studying-to-review = To Review
|
||||
studying-type-answer-unknown-field = Type answer: unknown field { $val }
|
||||
studying-unbury = Unbury
|
||||
studying-what-would-you-like-to-unbury = What would you like to unbury?
|
||||
studying-you-havent-recorded-your-voice-yet = You haven't recorded your voice yet.
|
||||
Loading…
Reference in a new issue