Merge pull request #835 from abdnh/init-lang

Set up default language before loading profile
This commit is contained in:
Damien Elmes 2020-11-22 16:25:44 +10:00 committed by GitHub
commit 0c08ff5317
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 82 additions and 50 deletions

View File

@ -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.

View File

@ -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

View File

@ -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")

View File

@ -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,12 +201,16 @@ 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
lang = force or pm.meta["defaultLang"] if firstTime:
lang = pm.meta["defaultLang"]
else:
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()
anki.lang.set_lang(lang, ldir) if not firstTime:
# set active language
anki.lang.set_lang(lang, ldir)
# switch direction for RTL languages # switch direction for RTL languages
if anki.lang.is_rtl(lang): if anki.lang.is_rtl(lang):
@ -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)

View File

@ -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: