mirror of
https://github.com/ankitects/anki.git
synced 2025-11-11 07:07:13 -05:00
refactor language handling
Because Qt translations need to be initialized before any widgets are created, and because the Qt language needs to match the gettext language in order for translated shortcuts to work, per-profile language settings aren't possible. So instead of storing the language in the profile, we use pm.meta['defaultLang'] for all profiles and initialize language handling in __init__.py The language selection in the preferences has been removed, because in a school setting a student fiddling with the language could potentially cause other students to be unable to navigate the UI. Instead, Anki will accept -l/--lang passed on the command line to override the original language chosen at first program startup.
This commit is contained in:
parent
dbc004e0ef
commit
d919615475
5 changed files with 46 additions and 115 deletions
|
|
@ -1,8 +1,10 @@
|
||||||
# Copyright: Damien Elmes <anki@ichi2.net>
|
# Copyright: Damien Elmes <anki@ichi2.net>
|
||||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
import os, sys
|
import os, sys, __builtin__
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
|
import locale, gettext
|
||||||
|
import anki.lang
|
||||||
|
|
||||||
appVersion="2.0-beta3"
|
appVersion="2.0-beta3"
|
||||||
appWebsite="http://ankisrs.net/"
|
appWebsite="http://ankisrs.net/"
|
||||||
|
|
@ -49,6 +51,44 @@ class DialogManager(object):
|
||||||
|
|
||||||
dialogs = DialogManager()
|
dialogs = DialogManager()
|
||||||
|
|
||||||
|
# Language handling
|
||||||
|
##########################################################################
|
||||||
|
# Qt requires its translator to be installed before any GUI widgets are
|
||||||
|
# loaded, and we need the Qt language to match the gettext language or
|
||||||
|
# translated shortcuts will not work.
|
||||||
|
|
||||||
|
_gtrans = None
|
||||||
|
_qtrans = None
|
||||||
|
|
||||||
|
def langDir():
|
||||||
|
dir = os.path.join(moduleDir, "aqt", "locale")
|
||||||
|
if not os.path.exists(dir):
|
||||||
|
dir = os.path.join(os.path.dirname(sys.argv[0]), "locale")
|
||||||
|
return dir
|
||||||
|
|
||||||
|
def setupLang(pm, app, force=None):
|
||||||
|
global _gtrans, _qtrans
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
lang = force or pm.meta["defaultLang"]
|
||||||
|
dir = langDir()
|
||||||
|
# gettext
|
||||||
|
_gtrans = gettext.translation(
|
||||||
|
'ankiqt', dir, languages=[lang], fallback=True)
|
||||||
|
__builtin__.__dict__['_'] = _gtrans.ugettext
|
||||||
|
__builtin__.__dict__['ngettext'] = _gtrans.ungettext
|
||||||
|
anki.lang.setLang(lang, local=False)
|
||||||
|
if lang in ("he","ar","fa"):
|
||||||
|
app.setLayoutDirection(Qt.RightToLeft)
|
||||||
|
else:
|
||||||
|
app.setLayoutDirection(Qt.LeftToRight)
|
||||||
|
# qt
|
||||||
|
_qtrans = QTranslator()
|
||||||
|
if _qtrans.load("qt_" + lang, dir):
|
||||||
|
app.installTranslator(_qtrans)
|
||||||
|
|
||||||
# App initialisation
|
# App initialisation
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
@ -80,20 +120,15 @@ def run():
|
||||||
parser.usage = "%prog [OPTIONS]"
|
parser.usage = "%prog [OPTIONS]"
|
||||||
parser.add_option("-b", "--base", help="Path to base folder")
|
parser.add_option("-b", "--base", help="Path to base folder")
|
||||||
parser.add_option("-p", "--profile", help="Profile name to load")
|
parser.add_option("-p", "--profile", help="Profile name to load")
|
||||||
|
parser.add_option("-l", "--lang", help="Interface language (en, de, etc)")
|
||||||
(opts, args) = parser.parse_args(sys.argv[1:])
|
(opts, args) = parser.parse_args(sys.argv[1:])
|
||||||
|
|
||||||
# profile manager
|
# profile manager
|
||||||
from aqt.profiles import ProfileManager
|
from aqt.profiles import ProfileManager
|
||||||
pm = ProfileManager(opts.base, opts.profile)
|
pm = ProfileManager(opts.base, opts.profile)
|
||||||
|
|
||||||
# qt translations
|
# i18n
|
||||||
qtTranslator = QTranslator()
|
setupLang(pm, app, opts.lang)
|
||||||
languageDir = os.path.join(moduleDir, "aqt", "locale")
|
|
||||||
if not os.path.exists(languageDir):
|
|
||||||
languageDir = os.path.join(
|
|
||||||
os.path.dirname(sys.argv[0]), "locale")
|
|
||||||
if qtTranslator.load("qt_" + pm.meta['defaultLang'], languageDir):
|
|
||||||
app.installTranslator(qtTranslator)
|
|
||||||
|
|
||||||
import aqt.main
|
import aqt.main
|
||||||
mw = aqt.main.AnkiQt(app, pm)
|
mw = aqt.main.AnkiQt(app, pm)
|
||||||
|
|
|
||||||
49
aqt/main.py
49
aqt/main.py
|
|
@ -21,20 +21,12 @@ from aqt.utils import saveGeom, restoreGeom, showInfo, showWarning, \
|
||||||
askUserDialog, applyStyles, getText, showText, showCritical, getFile, \
|
askUserDialog, applyStyles, getText, showText, showCritical, getFile, \
|
||||||
tooltip, openHelp, openLink
|
tooltip, openHelp, openLink
|
||||||
|
|
||||||
## fixme: open plugin folder broken on win32?
|
|
||||||
|
|
||||||
## models remembering the previous group
|
|
||||||
|
|
||||||
class AnkiQt(QMainWindow):
|
class AnkiQt(QMainWindow):
|
||||||
def __init__(self, app, profileManager):
|
def __init__(self, app, profileManager):
|
||||||
QMainWindow.__init__(self)
|
QMainWindow.__init__(self)
|
||||||
aqt.mw = self
|
aqt.mw = self
|
||||||
self.app = app
|
self.app = app
|
||||||
self.pm = profileManager
|
self.pm = profileManager
|
||||||
# use the global language for early init; once a profile or the
|
|
||||||
# profile manager is loaded we can switch to a user's preferred
|
|
||||||
# language
|
|
||||||
self.setupLang(force=self.pm.meta['defaultLang'])
|
|
||||||
# running 2.0 for the first time?
|
# running 2.0 for the first time?
|
||||||
if self.pm.meta['firstRun']:
|
if self.pm.meta['firstRun']:
|
||||||
# load the new deck user profile
|
# load the new deck user profile
|
||||||
|
|
@ -100,7 +92,6 @@ class AnkiQt(QMainWindow):
|
||||||
self.loadProfile()
|
self.loadProfile()
|
||||||
|
|
||||||
def showProfileManager(self):
|
def showProfileManager(self):
|
||||||
self.setupLang(force=self.pm.meta['defaultLang'])
|
|
||||||
d = self.profileDiag = QDialog()
|
d = self.profileDiag = QDialog()
|
||||||
f = self.profileForm = aqt.forms.profiles.Ui_Dialog()
|
f = self.profileForm = aqt.forms.profiles.Ui_Dialog()
|
||||||
f.setupUi(d)
|
f.setupUi(d)
|
||||||
|
|
@ -191,7 +182,6 @@ Are you sure?"""):
|
||||||
self.refreshProfilesList()
|
self.refreshProfilesList()
|
||||||
|
|
||||||
def loadProfile(self):
|
def loadProfile(self):
|
||||||
self.setupLang()
|
|
||||||
# show main window
|
# show main window
|
||||||
if self.pm.profile['mainWindowState']:
|
if self.pm.profile['mainWindowState']:
|
||||||
restoreGeom(self, "mainWindow")
|
restoreGeom(self, "mainWindow")
|
||||||
|
|
@ -755,45 +745,6 @@ upload, overwriting any changes either here or on AnkiWeb. Proceed?""")):
|
||||||
else:
|
else:
|
||||||
self.moveToState("overview")
|
self.moveToState("overview")
|
||||||
|
|
||||||
# Language handling
|
|
||||||
##########################################################################
|
|
||||||
|
|
||||||
def setupLang(self, force=None):
|
|
||||||
"Set the user interface language for the current profile."
|
|
||||||
import locale, gettext
|
|
||||||
import anki.lang
|
|
||||||
try:
|
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
lang = force if force else self.pm.profile["lang"]
|
|
||||||
languageDir=os.path.join(aqt.moduleDir, "aqt", "locale")
|
|
||||||
if not os.path.exists(languageDir):
|
|
||||||
languageDir = os.path.join(
|
|
||||||
os.path.dirname(sys.argv[0]), "locale")
|
|
||||||
self.languageTrans = gettext.translation('ankiqt', languageDir,
|
|
||||||
languages=[lang],
|
|
||||||
fallback=True)
|
|
||||||
self.installTranslation()
|
|
||||||
if getattr(self, 'form', None):
|
|
||||||
self.form.retranslateUi(self)
|
|
||||||
anki.lang.setLang(lang, local=False)
|
|
||||||
if lang in ("he","ar","fa"):
|
|
||||||
self.app.setLayoutDirection(Qt.RightToLeft)
|
|
||||||
else:
|
|
||||||
self.app.setLayoutDirection(Qt.LeftToRight)
|
|
||||||
|
|
||||||
def getTranslation(self, text):
|
|
||||||
return self.languageTrans.ugettext(text)
|
|
||||||
|
|
||||||
def getTranslation2(self, text1, text2, n):
|
|
||||||
return self.languageTrans.ungettext(text1, text2, n)
|
|
||||||
|
|
||||||
def installTranslation(self):
|
|
||||||
import __builtin__
|
|
||||||
__builtin__.__dict__['_'] = self.getTranslation
|
|
||||||
__builtin__.__dict__['ngettext'] = self.getTranslation2
|
|
||||||
|
|
||||||
# Menu, title bar & status
|
# Menu, title bar & status
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
import datetime, time, os
|
import datetime, time, os
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from anki.lang import langs
|
|
||||||
from aqt.utils import openFolder, showWarning, getText, openHelp
|
from aqt.utils import openFolder, showWarning, getText, openHelp
|
||||||
import aqt
|
import aqt
|
||||||
|
|
||||||
|
|
@ -19,7 +18,6 @@ class Preferences(QDialog):
|
||||||
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
|
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
|
||||||
lambda: openHelp("profileprefs"))
|
lambda: openHelp("profileprefs"))
|
||||||
self.setupCollection()
|
self.setupCollection()
|
||||||
self.setupLang()
|
|
||||||
self.setupNetwork()
|
self.setupNetwork()
|
||||||
self.setupBackup()
|
self.setupBackup()
|
||||||
self.setupOptions()
|
self.setupOptions()
|
||||||
|
|
@ -69,35 +67,6 @@ class Preferences(QDialog):
|
||||||
d.crt = int(time.mktime(date.timetuple()))
|
d.crt = int(time.mktime(date.timetuple()))
|
||||||
d.setMod()
|
d.setMod()
|
||||||
|
|
||||||
# Language handling
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def setupLang(self):
|
|
||||||
# interface lang
|
|
||||||
for (lang, code) in langs:
|
|
||||||
self.form.interfaceLang.addItem(lang)
|
|
||||||
self.form.interfaceLang.setCurrentIndex(
|
|
||||||
self.codeToIndex(self.prof['lang']))
|
|
||||||
self.connect(self.form.interfaceLang,
|
|
||||||
SIGNAL("currentIndexChanged(QString)"),
|
|
||||||
self.interfaceLangChanged)
|
|
||||||
|
|
||||||
def codeToIndex(self, code):
|
|
||||||
n = 0
|
|
||||||
for (lang, type) in langs:
|
|
||||||
if code == type:
|
|
||||||
return n
|
|
||||||
n += 1
|
|
||||||
# default to english
|
|
||||||
return self.codeToIndex("en")
|
|
||||||
|
|
||||||
def interfaceLangChanged(self):
|
|
||||||
self.prof['lang'] = (
|
|
||||||
langs[self.form.interfaceLang.currentIndex()])[1]
|
|
||||||
self.mw.setupLang()
|
|
||||||
self.form.retranslateUi(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Network
|
# Network
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ profileConf = dict(
|
||||||
mainWindowState=None,
|
mainWindowState=None,
|
||||||
numBackups=30,
|
numBackups=30,
|
||||||
lastOptimize=intTime(),
|
lastOptimize=intTime(),
|
||||||
lang="en",
|
|
||||||
|
|
||||||
# editing
|
# editing
|
||||||
fullSearch=False,
|
fullSearch=False,
|
||||||
|
|
@ -142,7 +141,6 @@ computer.""")
|
||||||
|
|
||||||
def create(self, name):
|
def create(self, name):
|
||||||
prof = profileConf.copy()
|
prof = profileConf.copy()
|
||||||
prof['lang'] = self.meta['defaultLang']
|
|
||||||
self.db.execute("insert into profiles values (?, ?)",
|
self.db.execute("insert into profiles values (?, ?)",
|
||||||
name, cPickle.dumps(prof))
|
name, cPickle.dumps(prof))
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>439</width>
|
<width>441</width>
|
||||||
<height>420</height>
|
<height>426</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
|
@ -30,27 +30,6 @@
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>12</number>
|
<number>12</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Language:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="interfaceLang">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>300</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="showEstimates">
|
<widget class="QCheckBox" name="showEstimates">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -483,7 +462,6 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>interfaceLang</tabstop>
|
|
||||||
<tabstop>showEstimates</tabstop>
|
<tabstop>showEstimates</tabstop>
|
||||||
<tabstop>showProgress</tabstop>
|
<tabstop>showProgress</tabstop>
|
||||||
<tabstop>stripHTML</tabstop>
|
<tabstop>stripHTML</tabstop>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue