Merge pull request #835 from abdnh/init-lang
Set up default language before loading profile
This commit is contained in:
commit
0c08ff5317
@ -8,3 +8,8 @@ profiles-profile-corrupt = Profile Corrupt
|
|||||||
profiles-profiles = Profiles
|
profiles-profiles = Profiles
|
||||||
profiles-quit = Quit
|
profiles-quit = Quit
|
||||||
profiles-user-1 = User 1
|
profiles-user-1 = User 1
|
||||||
|
profiles-confirm-lang-choice = Are you sure you wish to display Anki's interface in { $lang }?
|
||||||
|
profiles-could-not-create-data-folder = Anki could not create its data folder. Please see the File Locations section of the manual, and ensure that location is not read-only.
|
||||||
|
profiles-prefs-corrupt-title = Preferences Corrupt
|
||||||
|
profiles-prefs-file-is-corrupt = Anki's prefs21.db file was corrupt and has been recreated. If you were using multiple profiles, please add them back using the same names to recover your cards.
|
||||||
|
profiles-profile-does-not-exist = Requested profile does not exist.
|
||||||
|
@ -61,6 +61,12 @@ qt-misc-would-you-like-to-download-it = Would you like to download it now?
|
|||||||
qt-misc-your-collection-file-appears-to-be = Your collection file appears to be corrupt. This can happen when the file is copied or moved while Anki is open, or when the collection is stored on a network or cloud drive. If problems persist after restarting your computer, please open an automatic backup from the profile screen.
|
qt-misc-your-collection-file-appears-to-be = Your collection file appears to be corrupt. This can happen when the file is copied or moved while Anki is open, or when the collection is stored on a network or cloud drive. If problems persist after restarting your computer, please open an automatic backup from the profile screen.
|
||||||
qt-misc-your-computers-storage-may-be-full = Your computer's storage may be full. Please delete some unneeded files, then try again.
|
qt-misc-your-computers-storage-may-be-full = Your computer's storage may be full. Please delete some unneeded files, then try again.
|
||||||
qt-misc-your-firewall-or-antivirus-program-is = Your firewall or antivirus program is preventing Anki from creating a connection to itself. Please add an exception for Anki.
|
qt-misc-your-firewall-or-antivirus-program-is = Your firewall or antivirus program is preventing Anki from creating a connection to itself. Please add an exception for Anki.
|
||||||
|
qt-misc-error = Error
|
||||||
|
qt-misc-no-temp-folder = No usable temporary folder found. Make sure C:\\temp exists or TEMP in your environment points to a valid, writable folder.
|
||||||
|
qt-misc-incompatible-video-driver = Your video driver is incompatible. Please start Anki again, and Anki will switch to a slower, more compatible mode.
|
||||||
|
qt-misc-error-loading-graphics-driver = Error loading '{ $mode }' graphics driver. Please start Anki again to try next driver. { $context }
|
||||||
|
qt-misc-anki-is-running = Anki Already Running
|
||||||
|
qt-misc-if-instance-is-not-responding = If the existing instance of Anki is not responding, please close it using your task manager, or restart your computer.
|
||||||
qt-misc-second =
|
qt-misc-second =
|
||||||
{ $count ->
|
{ $count ->
|
||||||
[one] { $count } second
|
[one] { $count } second
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import locale
|
||||||
import re
|
import re
|
||||||
from typing import Optional
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
import anki
|
import anki
|
||||||
|
|
||||||
@ -171,6 +172,36 @@ def set_lang(lang: str, locale_dir: str) -> None:
|
|||||||
locale_folder = locale_dir
|
locale_folder = locale_dir
|
||||||
|
|
||||||
|
|
||||||
|
def get_def_lang(lang: Optional[str] = None) -> Tuple[int, str]:
|
||||||
|
"""Return lang converted to name used on disk and its index, defaulting to system language
|
||||||
|
or English if not available."""
|
||||||
|
try:
|
||||||
|
(sys_lang, enc) = locale.getdefaultlocale()
|
||||||
|
except:
|
||||||
|
# fails on osx
|
||||||
|
sys_lang = "en_US"
|
||||||
|
user_lang = lang
|
||||||
|
if user_lang in compatMap:
|
||||||
|
user_lang = compatMap[user_lang]
|
||||||
|
idx = None
|
||||||
|
lang = None
|
||||||
|
en = None
|
||||||
|
for l in (user_lang, sys_lang):
|
||||||
|
for c, (name, code) in enumerate(langs):
|
||||||
|
if code == "en_US":
|
||||||
|
en = c
|
||||||
|
if code == l:
|
||||||
|
idx = c
|
||||||
|
lang = l
|
||||||
|
if idx is not None:
|
||||||
|
break
|
||||||
|
# if the specified language and the system language aren't available, revert to english
|
||||||
|
if idx is None:
|
||||||
|
idx = en
|
||||||
|
lang = "en_US"
|
||||||
|
return (idx, lang)
|
||||||
|
|
||||||
|
|
||||||
def is_rtl(lang: str) -> bool:
|
def is_rtl(lang: str) -> bool:
|
||||||
return lang in ("he", "ar", "fa")
|
return lang in ("he", "ar", "fa")
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ from anki.consts import HELP_SITE
|
|||||||
from anki.rsbackend import RustBackend
|
from anki.rsbackend import RustBackend
|
||||||
from anki.utils import checksum, isLin, isMac
|
from anki.utils import checksum, isLin, isMac
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from aqt.utils import locale_dir
|
from aqt.utils import TR, locale_dir, tr
|
||||||
|
|
||||||
# we want to be able to print unicode debug info to console without
|
# we want to be able to print unicode debug info to console without
|
||||||
# fear of a traceback on systems with the console set to ASCII
|
# fear of a traceback on systems with the console set to ASCII
|
||||||
@ -175,7 +175,10 @@ _qtrans: Optional[QTranslator] = None
|
|||||||
|
|
||||||
|
|
||||||
def setupLangAndBackend(
|
def setupLangAndBackend(
|
||||||
pm: ProfileManager, app: QApplication, force: Optional[str] = None
|
pm: ProfileManager,
|
||||||
|
app: QApplication,
|
||||||
|
force: Optional[str] = None,
|
||||||
|
firstTime: bool = False,
|
||||||
) -> RustBackend:
|
) -> RustBackend:
|
||||||
global _qtrans
|
global _qtrans
|
||||||
try:
|
try:
|
||||||
@ -198,11 +201,15 @@ def setupLangAndBackend(
|
|||||||
builtins.__dict__["ngettext"] = fn_ngettext
|
builtins.__dict__["ngettext"] = fn_ngettext
|
||||||
|
|
||||||
# get lang and normalize into ja/zh-CN form
|
# get lang and normalize into ja/zh-CN form
|
||||||
|
if firstTime:
|
||||||
|
lang = pm.meta["defaultLang"]
|
||||||
|
else:
|
||||||
lang = force or pm.meta["defaultLang"]
|
lang = force or pm.meta["defaultLang"]
|
||||||
lang = anki.lang.lang_to_disk_lang(lang)
|
lang = anki.lang.lang_to_disk_lang(lang)
|
||||||
|
|
||||||
# set active language
|
|
||||||
ldir = locale_dir()
|
ldir = locale_dir()
|
||||||
|
if not firstTime:
|
||||||
|
# set active language
|
||||||
anki.lang.set_lang(lang, ldir)
|
anki.lang.set_lang(lang, ldir)
|
||||||
|
|
||||||
# switch direction for RTL languages
|
# switch direction for RTL languages
|
||||||
@ -269,8 +276,8 @@ class AnkiApp(QApplication):
|
|||||||
# existing instance running but hung
|
# existing instance running but hung
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
None,
|
None,
|
||||||
"Anki Already Running",
|
tr(TR.QT_MISC_ANKI_IS_RUNNING),
|
||||||
"If the existing instance of Anki is not responding, please close it using your task manager, or restart your computer.",
|
tr(TR.QT_MISC_IF_INSTANCE_IS_NOT_RESPONDING),
|
||||||
)
|
)
|
||||||
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@ -356,8 +363,10 @@ def setupGL(pm):
|
|||||||
if "Failed to create OpenGL context" in msg:
|
if "Failed to create OpenGL context" in msg:
|
||||||
QMessageBox.critical(
|
QMessageBox.critical(
|
||||||
None,
|
None,
|
||||||
"Error",
|
tr(TR.QT_MISC_ERROR),
|
||||||
f"Error loading '{mode}' graphics driver. Please start Anki again to try next driver. {context}",
|
tr(
|
||||||
|
TR.QT_MISC_ERROR_LOADING_GRAPHICS_DRIVER, mode=mode, context=context
|
||||||
|
),
|
||||||
)
|
)
|
||||||
pm.nextGlMode()
|
pm.nextGlMode()
|
||||||
return
|
return
|
||||||
@ -427,6 +436,10 @@ def _run(argv=None, exec=True):
|
|||||||
profiler = cProfile.Profile()
|
profiler = cProfile.Profile()
|
||||||
profiler.enable()
|
profiler.enable()
|
||||||
|
|
||||||
|
# default to specified/system language before getting user's preference so that we can localize some more strings
|
||||||
|
lang = anki.lang.get_def_lang(opts.lang)
|
||||||
|
anki.lang.set_lang(lang[1], locale_dir())
|
||||||
|
|
||||||
# profile manager
|
# profile manager
|
||||||
pm = None
|
pm = None
|
||||||
try:
|
try:
|
||||||
@ -475,10 +488,8 @@ def _run(argv=None, exec=True):
|
|||||||
if not pm:
|
if not pm:
|
||||||
QMessageBox.critical(
|
QMessageBox.critical(
|
||||||
None,
|
None,
|
||||||
"Error",
|
tr(TR.QT_MISC_ERROR),
|
||||||
"""\
|
tr(TR.PROFILES_COULD_NOT_CREATE_DATA_FOLDER),
|
||||||
Anki could not create its data folder. Please see the File Locations \
|
|
||||||
section of the manual, and ensure that location is not read-only.""",
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -517,29 +528,26 @@ section of the manual, and ensure that location is not read-only.""",
|
|||||||
except:
|
except:
|
||||||
QMessageBox.critical(
|
QMessageBox.critical(
|
||||||
None,
|
None,
|
||||||
"Error",
|
tr(TR.QT_MISC_ERROR),
|
||||||
"""\
|
tr(TR.QT_MISC_NO_TEMP_FOLDER),
|
||||||
No usable temporary folder found. Make sure C:\\temp exists or TEMP in your \
|
|
||||||
environment points to a valid, writable folder.""",
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if pmLoadResult.firstTime:
|
if pmLoadResult.firstTime:
|
||||||
pm.setDefaultLang()
|
pm.setDefaultLang(lang[0])
|
||||||
|
|
||||||
if pmLoadResult.loadError:
|
if pmLoadResult.loadError:
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
None,
|
None,
|
||||||
"Preferences Corrupt",
|
tr(TR.PROFILES_PREFS_CORRUPT_TITLE),
|
||||||
"""Anki's prefs21.db file was corrupt and has been recreated. If you were using multiple \
|
tr(TR.PROFILES_PREFS_FILE_IS_CORRUPT),
|
||||||
profiles, please add them back using the same names to recover your cards.""",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if opts.profile:
|
if opts.profile:
|
||||||
pm.openProfile(opts.profile)
|
pm.openProfile(opts.profile)
|
||||||
|
|
||||||
# i18n & backend
|
# i18n & backend
|
||||||
backend = setupLangAndBackend(pm, app, opts.lang)
|
backend = setupLangAndBackend(pm, app, opts.lang, pmLoadResult.firstTime)
|
||||||
|
|
||||||
if isLin and pm.glMode() == "auto":
|
if isLin and pm.glMode() == "auto":
|
||||||
from aqt.utils import gfxDriverIsBroken
|
from aqt.utils import gfxDriverIsBroken
|
||||||
@ -548,8 +556,8 @@ environment points to a valid, writable folder.""",
|
|||||||
pm.nextGlMode()
|
pm.nextGlMode()
|
||||||
QMessageBox.critical(
|
QMessageBox.critical(
|
||||||
None,
|
None,
|
||||||
"Error",
|
tr(TR.QT_MISC_ERROR),
|
||||||
"Your video driver is incompatible. Please start Anki again, and Anki will switch to a slower, more compatible mode.",
|
tr(TR.QT_MISC_INCOMPATIBLE_VIDEO_DRIVER),
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
@ -97,7 +97,9 @@ class ProfileManager:
|
|||||||
def openProfile(self, profile) -> None:
|
def openProfile(self, profile) -> None:
|
||||||
if profile:
|
if profile:
|
||||||
if profile not in self.profiles():
|
if profile not in self.profiles():
|
||||||
QMessageBox.critical(None, "Error", "Requested profile does not exist.")
|
QMessageBox.critical(
|
||||||
|
None, tr(TR.QT_MISC_ERROR), tr(TR.PROFILES_PROFILE_DOES_NOT_EXIST)
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
self.load(profile)
|
self.load(profile)
|
||||||
@ -482,7 +484,7 @@ create table if not exists profiles
|
|||||||
######################################################################
|
######################################################################
|
||||||
# On first run, allow the user to choose the default language
|
# On first run, allow the user to choose the default language
|
||||||
|
|
||||||
def setDefaultLang(self) -> None:
|
def setDefaultLang(self, idx: int) -> None:
|
||||||
# create dialog
|
# create dialog
|
||||||
class NoCloseDiag(QDialog):
|
class NoCloseDiag(QDialog):
|
||||||
def reject(self):
|
def reject(self):
|
||||||
@ -490,28 +492,9 @@ create table if not exists profiles
|
|||||||
|
|
||||||
d = self.langDiag = NoCloseDiag()
|
d = self.langDiag = NoCloseDiag()
|
||||||
f = self.langForm = aqt.forms.setlang.Ui_Dialog()
|
f = self.langForm = aqt.forms.setlang.Ui_Dialog()
|
||||||
|
f.setupUi(d)
|
||||||
qconnect(d.accepted, self._onLangSelected)
|
qconnect(d.accepted, self._onLangSelected)
|
||||||
qconnect(d.rejected, lambda: True)
|
qconnect(d.rejected, lambda: True)
|
||||||
# default to the system language
|
|
||||||
try:
|
|
||||||
(lang, enc) = locale.getdefaultlocale()
|
|
||||||
except:
|
|
||||||
# fails on osx
|
|
||||||
lang = "en_US"
|
|
||||||
# find index
|
|
||||||
idx = None
|
|
||||||
en = None
|
|
||||||
for c, (name, code) in enumerate(anki.lang.langs):
|
|
||||||
if code == "en_US":
|
|
||||||
en = c
|
|
||||||
if code == lang:
|
|
||||||
idx = c
|
|
||||||
# if the system language isn't available, revert to english
|
|
||||||
if idx is None:
|
|
||||||
idx = en
|
|
||||||
lang = "en_US"
|
|
||||||
anki.lang.set_lang(lang, locale_dir())
|
|
||||||
f.setupUi(d)
|
|
||||||
# update list
|
# update list
|
||||||
f.lang.addItems([x[0] for x in anki.lang.langs])
|
f.lang.addItems([x[0] for x in anki.lang.langs])
|
||||||
f.lang.setCurrentRow(idx)
|
f.lang.setCurrentRow(idx)
|
||||||
@ -522,12 +505,11 @@ create table if not exists profiles
|
|||||||
obj = anki.lang.langs[f.lang.currentRow()]
|
obj = anki.lang.langs[f.lang.currentRow()]
|
||||||
code = obj[1]
|
code = obj[1]
|
||||||
name = obj[0]
|
name = obj[0]
|
||||||
en = "Are you sure you wish to display Anki's interface in %s?"
|
|
||||||
r = QMessageBox.question(
|
r = QMessageBox.question(
|
||||||
None, "Anki", en % name, QMessageBox.Yes | QMessageBox.No, QMessageBox.No # type: ignore
|
None, "Anki", tr(TR.PROFILES_CONFIRM_LANG_CHOICE, lang=name), QMessageBox.Yes | QMessageBox.No, QMessageBox.No # type: ignore
|
||||||
)
|
)
|
||||||
if r != QMessageBox.Yes:
|
if r != QMessageBox.Yes:
|
||||||
return self.setDefaultLang()
|
return self.setDefaultLang(f.lang.currentRow())
|
||||||
self.setLang(code)
|
self.setLang(code)
|
||||||
|
|
||||||
def setLang(self, code) -> None:
|
def setLang(self, code) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user