mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 15:02:21 -04:00
Merge remote-tracking branch 'danielelmes/master' into fix_windows_build
# Conflicts: # Makefile
This commit is contained in:
commit
ac4933faca
17 changed files with 183 additions and 34 deletions
2
.github/scripts/contrib.sh
vendored
2
.github/scripts/contrib.sh
vendored
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -e
|
set -eo pipefail
|
||||||
|
|
||||||
antispam=", at the domain "
|
antispam=", at the domain "
|
||||||
|
|
||||||
|
|
17
Makefile
17
Makefile
|
@ -79,7 +79,7 @@ build-qt:
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean: clean-dist
|
clean: clean-dist
|
||||||
set -e && \
|
set -eo pipefail && \
|
||||||
for dir in $(DEVEL); do \
|
for dir in $(DEVEL); do \
|
||||||
$(SUBMAKE) -C $$dir clean; \
|
$(SUBMAKE) -C $$dir clean; \
|
||||||
done
|
done
|
||||||
|
@ -90,7 +90,7 @@ clean-dist:
|
||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: pyenv buildhash
|
check: pyenv buildhash
|
||||||
set -e && \
|
set -eo pipefail && \
|
||||||
for dir in $(CHECKABLE_RS); do \
|
for dir in $(CHECKABLE_RS); do \
|
||||||
$(SUBMAKE) -C $$dir check; \
|
$(SUBMAKE) -C $$dir check; \
|
||||||
done; \
|
done; \
|
||||||
|
@ -116,3 +116,16 @@ add-buildhash:
|
||||||
ver=$$(cat meta/version); \
|
ver=$$(cat meta/version); \
|
||||||
hash=$$(cat meta/buildhash); \
|
hash=$$(cat meta/buildhash); \
|
||||||
rename "s/-$${ver}-/-$${ver}+$${hash}-/" dist/*-$$ver-*
|
rename "s/-$${ver}-/-$${ver}+$${hash}-/" dist/*-$$ver-*
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: pull-i18n
|
||||||
|
pull-i18n:
|
||||||
|
(cd rslib/ftl && scripts/fetch-latest-translations)
|
||||||
|
(cd qt/ftl && scripts/fetch-latest-translations)
|
||||||
|
(cd qt/i18n && ./pull-git)
|
||||||
|
|
||||||
|
.PHONY: push-i18n
|
||||||
|
push-i18n: pull-i18n
|
||||||
|
(cd rslib/ftl && scripts/upload-latest-templates)
|
||||||
|
(cd qt/ftl && scripts/upload-latest-templates)
|
||||||
|
(cd qt/i18n && ./sync-po-git)
|
||||||
|
|
|
@ -104,7 +104,7 @@ You can do this automatically by adding the following into
|
||||||
.git/hooks/pre-commit or .git/hooks/pre-push and making it executable.
|
.git/hooks/pre-commit or .git/hooks/pre-push and making it executable.
|
||||||
|
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -eo pipefail
|
||||||
make check
|
make check
|
||||||
|
|
||||||
You may need to adjust the PATH variable so that things like a local install
|
You may need to adjust the PATH variable so that things like a local install
|
||||||
|
|
|
@ -516,7 +516,7 @@ def test_nextIvl():
|
||||||
assert ni(c, 3) == 21600000
|
assert ni(c, 3) == 21600000
|
||||||
# (* 100 2.5 1.3 86400)28080000.0
|
# (* 100 2.5 1.3 86400)28080000.0
|
||||||
assert ni(c, 4) == 28080000
|
assert ni(c, 4) == 28080000
|
||||||
assert without_unicode_isolation(d.sched.nextIvlStr(c, 4)) == "10.83mo"
|
assert without_unicode_isolation(d.sched.nextIvlStr(c, 4)) == "10.8mo"
|
||||||
|
|
||||||
|
|
||||||
def test_misc():
|
def test_misc():
|
||||||
|
|
|
@ -613,7 +613,7 @@ def test_nextIvl():
|
||||||
assert ni(c, 3) == 21600000
|
assert ni(c, 3) == 21600000
|
||||||
# (* 100 2.5 1.3 86400)28080000.0
|
# (* 100 2.5 1.3 86400)28080000.0
|
||||||
assert ni(c, 4) == 28080000
|
assert ni(c, 4) == 28080000
|
||||||
assert without_unicode_isolation(d.sched.nextIvlStr(c, 4)) == "10.83mo"
|
assert without_unicode_isolation(d.sched.nextIvlStr(c, 4)) == "10.8mo"
|
||||||
|
|
||||||
|
|
||||||
def test_bury():
|
def test_bury():
|
||||||
|
|
|
@ -6,6 +6,7 @@ from operator import itemgetter
|
||||||
import aqt
|
import aqt
|
||||||
from anki.consts import NEW_CARDS_RANDOM
|
from anki.consts import NEW_CARDS_RANDOM
|
||||||
from anki.lang import _, ngettext
|
from anki.lang import _, ngettext
|
||||||
|
from aqt import gui_hooks
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from aqt.utils import (
|
from aqt.utils import (
|
||||||
askUser,
|
askUser,
|
||||||
|
@ -28,6 +29,7 @@ class DeckConf(QDialog):
|
||||||
self._origNewOrder = None
|
self._origNewOrder = None
|
||||||
self.form = aqt.forms.dconf.Ui_Dialog()
|
self.form = aqt.forms.dconf.Ui_Dialog()
|
||||||
self.form.setupUi(self)
|
self.form.setupUi(self)
|
||||||
|
gui_hooks.deck_conf_did_setup_ui_form(self)
|
||||||
self.mw.checkpoint(_("Options"))
|
self.mw.checkpoint(_("Options"))
|
||||||
self.setupCombos()
|
self.setupCombos()
|
||||||
self.setupConfs()
|
self.setupConfs()
|
||||||
|
@ -40,6 +42,7 @@ class DeckConf(QDialog):
|
||||||
self.setWindowTitle(_("Options for %s") % self.deck["name"])
|
self.setWindowTitle(_("Options for %s") % self.deck["name"])
|
||||||
# qt doesn't size properly with altered fonts otherwise
|
# qt doesn't size properly with altered fonts otherwise
|
||||||
restoreGeom(self, "deckconf", adjustSize=True)
|
restoreGeom(self, "deckconf", adjustSize=True)
|
||||||
|
gui_hooks.deck_conf_will_show(self)
|
||||||
self.show()
|
self.show()
|
||||||
self.exec_()
|
self.exec_()
|
||||||
saveGeom(self, "deckconf")
|
saveGeom(self, "deckconf")
|
||||||
|
@ -218,6 +221,7 @@ class DeckConf(QDialog):
|
||||||
f.replayQuestion.setChecked(c.get("replayq", True))
|
f.replayQuestion.setChecked(c.get("replayq", True))
|
||||||
# description
|
# description
|
||||||
f.desc.setPlainText(self.deck["desc"])
|
f.desc.setPlainText(self.deck["desc"])
|
||||||
|
gui_hooks.deck_conf_did_load_config(self, self.deck, self.conf)
|
||||||
|
|
||||||
def onRestore(self):
|
def onRestore(self):
|
||||||
self.mw.progress.start()
|
self.mw.progress.start()
|
||||||
|
@ -301,6 +305,7 @@ class DeckConf(QDialog):
|
||||||
c["replayq"] = f.replayQuestion.isChecked()
|
c["replayq"] = f.replayQuestion.isChecked()
|
||||||
# description
|
# description
|
||||||
self.deck["desc"] = f.desc.toPlainText()
|
self.deck["desc"] = f.desc.toPlainText()
|
||||||
|
gui_hooks.deck_conf_will_save_config(self, self.deck, self.conf)
|
||||||
self.mw.col.decks.save(self.deck)
|
self.mw.col.decks.save(self.deck)
|
||||||
self.mw.col.decks.save(self.conf)
|
self.mw.col.decks.save(self.conf)
|
||||||
|
|
||||||
|
|
|
@ -518,6 +518,114 @@ class _DeckBrowserWillShowOptionsMenuHook:
|
||||||
deck_browser_will_show_options_menu = _DeckBrowserWillShowOptionsMenuHook()
|
deck_browser_will_show_options_menu = _DeckBrowserWillShowOptionsMenuHook()
|
||||||
|
|
||||||
|
|
||||||
|
class _DeckConfDidLoadConfigHook:
|
||||||
|
"""Called once widget state has been set from deck config"""
|
||||||
|
|
||||||
|
_hooks: List[Callable[["aqt.deckconf.DeckConf", Any, Any], None]] = []
|
||||||
|
|
||||||
|
def append(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None:
|
||||||
|
"""(deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any)"""
|
||||||
|
self._hooks.append(cb)
|
||||||
|
|
||||||
|
def remove(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None:
|
||||||
|
if cb in self._hooks:
|
||||||
|
self._hooks.remove(cb)
|
||||||
|
|
||||||
|
def __call__(
|
||||||
|
self, deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any
|
||||||
|
) -> None:
|
||||||
|
for hook in self._hooks:
|
||||||
|
try:
|
||||||
|
hook(deck_conf, deck, config)
|
||||||
|
except:
|
||||||
|
# if the hook fails, remove it
|
||||||
|
self._hooks.remove(hook)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
deck_conf_did_load_config = _DeckConfDidLoadConfigHook()
|
||||||
|
|
||||||
|
|
||||||
|
class _DeckConfDidSetupUiFormHook:
|
||||||
|
"""Allows modifying or adding widgets in the deck options UI form"""
|
||||||
|
|
||||||
|
_hooks: List[Callable[["aqt.deckconf.DeckConf"], None]] = []
|
||||||
|
|
||||||
|
def append(self, cb: Callable[["aqt.deckconf.DeckConf"], None]) -> None:
|
||||||
|
"""(deck_conf: aqt.deckconf.DeckConf)"""
|
||||||
|
self._hooks.append(cb)
|
||||||
|
|
||||||
|
def remove(self, cb: Callable[["aqt.deckconf.DeckConf"], None]) -> None:
|
||||||
|
if cb in self._hooks:
|
||||||
|
self._hooks.remove(cb)
|
||||||
|
|
||||||
|
def __call__(self, deck_conf: aqt.deckconf.DeckConf) -> None:
|
||||||
|
for hook in self._hooks:
|
||||||
|
try:
|
||||||
|
hook(deck_conf)
|
||||||
|
except:
|
||||||
|
# if the hook fails, remove it
|
||||||
|
self._hooks.remove(hook)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
deck_conf_did_setup_ui_form = _DeckConfDidSetupUiFormHook()
|
||||||
|
|
||||||
|
|
||||||
|
class _DeckConfWillSaveConfigHook:
|
||||||
|
"""Called before widget state is saved to config"""
|
||||||
|
|
||||||
|
_hooks: List[Callable[["aqt.deckconf.DeckConf", Any, Any], None]] = []
|
||||||
|
|
||||||
|
def append(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None:
|
||||||
|
"""(deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any)"""
|
||||||
|
self._hooks.append(cb)
|
||||||
|
|
||||||
|
def remove(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None:
|
||||||
|
if cb in self._hooks:
|
||||||
|
self._hooks.remove(cb)
|
||||||
|
|
||||||
|
def __call__(
|
||||||
|
self, deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any
|
||||||
|
) -> None:
|
||||||
|
for hook in self._hooks:
|
||||||
|
try:
|
||||||
|
hook(deck_conf, deck, config)
|
||||||
|
except:
|
||||||
|
# if the hook fails, remove it
|
||||||
|
self._hooks.remove(hook)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
deck_conf_will_save_config = _DeckConfWillSaveConfigHook()
|
||||||
|
|
||||||
|
|
||||||
|
class _DeckConfWillShowHook:
|
||||||
|
"""Allows modifying the deck options dialog before it is shown"""
|
||||||
|
|
||||||
|
_hooks: List[Callable[["aqt.deckconf.DeckConf"], None]] = []
|
||||||
|
|
||||||
|
def append(self, cb: Callable[["aqt.deckconf.DeckConf"], None]) -> None:
|
||||||
|
"""(deck_conf: aqt.deckconf.DeckConf)"""
|
||||||
|
self._hooks.append(cb)
|
||||||
|
|
||||||
|
def remove(self, cb: Callable[["aqt.deckconf.DeckConf"], None]) -> None:
|
||||||
|
if cb in self._hooks:
|
||||||
|
self._hooks.remove(cb)
|
||||||
|
|
||||||
|
def __call__(self, deck_conf: aqt.deckconf.DeckConf) -> None:
|
||||||
|
for hook in self._hooks:
|
||||||
|
try:
|
||||||
|
hook(deck_conf)
|
||||||
|
except:
|
||||||
|
# if the hook fails, remove it
|
||||||
|
self._hooks.remove(hook)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
deck_conf_will_show = _DeckConfWillShowHook()
|
||||||
|
|
||||||
|
|
||||||
class _EditorDidFireTypingTimerHook:
|
class _EditorDidFireTypingTimerHook:
|
||||||
_hooks: List[Callable[["anki.notes.Note"], None]] = []
|
_hooks: List[Callable[["anki.notes.Note"], None]] = []
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#
|
#
|
||||||
# build mo files
|
# build mo files
|
||||||
#
|
#
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
targetDir="../aqt_data/locale/gettext"
|
targetDir="../aqt_data/locale/gettext"
|
||||||
mkdir -p $targetDir
|
mkdir -p $targetDir
|
||||||
|
@ -9,9 +10,9 @@ mkdir -p $targetDir
|
||||||
echo "Compiling *.po..."
|
echo "Compiling *.po..."
|
||||||
for file in po/desktop/*/anki.po
|
for file in po/desktop/*/anki.po
|
||||||
do
|
do
|
||||||
outdir=$(echo $file | \
|
outdir=$(echo "$file" | \
|
||||||
perl -pe "s%po/desktop/(.*)/anki.po%$targetDir/\1/LC_MESSAGES%")
|
perl -pe "s%po/desktop/(.*)/anki.po%$targetDir/\1/LC_MESSAGES%")
|
||||||
outfile="$outdir/anki.mo"
|
outfile="$outdir/anki.mo"
|
||||||
mkdir -p $outdir
|
mkdir -p $outdir
|
||||||
msgmerge --for-msgfmt $file po/desktop/anki.pot | msgfmt - --output-file=$outfile
|
msgmerge -q "$file" po/desktop/anki.pot | msgfmt - --output-file="$outfile"
|
||||||
done
|
done
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -e
|
set -eo pipefail
|
||||||
|
|
||||||
out=../aqt_data/locale/qt
|
out=../aqt_data/locale/qt
|
||||||
mkdir -p "$out"
|
mkdir -p "$out"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
# pull any pending changes from git repos
|
# pull any pending changes from git repos
|
||||||
./pull-git
|
./pull-git
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#
|
#
|
||||||
# update template .pot file from source code strings
|
# update template .pot file from source code strings
|
||||||
#
|
#
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
topDir=$(dirname $0)/../..
|
topDir=$(dirname $0)/../..
|
||||||
cd $topDir
|
cd $topDir
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# should be on the path.
|
# should be on the path.
|
||||||
#
|
#
|
||||||
|
|
||||||
set -e
|
set -eo pipefail
|
||||||
|
|
||||||
if [ ! -d "designer" ]
|
if [ ! -d "designer" ]
|
||||||
then
|
then
|
||||||
|
|
|
@ -121,6 +121,28 @@ hooks = [
|
||||||
legacy_hook="prepareQA",
|
legacy_hook="prepareQA",
|
||||||
doc="Can modify card text before review/preview.",
|
doc="Can modify card text before review/preview.",
|
||||||
),
|
),
|
||||||
|
# Deck options
|
||||||
|
###################
|
||||||
|
Hook(
|
||||||
|
name="deck_conf_did_setup_ui_form",
|
||||||
|
args=["deck_conf: aqt.deckconf.DeckConf"],
|
||||||
|
doc="Allows modifying or adding widgets in the deck options UI form",
|
||||||
|
),
|
||||||
|
Hook(
|
||||||
|
name="deck_conf_will_show",
|
||||||
|
args=["deck_conf: aqt.deckconf.DeckConf"],
|
||||||
|
doc="Allows modifying the deck options dialog before it is shown",
|
||||||
|
),
|
||||||
|
Hook(
|
||||||
|
name="deck_conf_did_load_config",
|
||||||
|
args=["deck_conf: aqt.deckconf.DeckConf", "deck: Any", "config: Any"],
|
||||||
|
doc="Called once widget state has been set from deck config",
|
||||||
|
),
|
||||||
|
Hook(
|
||||||
|
name="deck_conf_will_save_config",
|
||||||
|
args=["deck_conf: aqt.deckconf.DeckConf", "deck: Any", "config: Any"],
|
||||||
|
doc="Called before widget state is saved to config",
|
||||||
|
),
|
||||||
# Browser
|
# Browser
|
||||||
###################
|
###################
|
||||||
Hook(
|
Hook(
|
||||||
|
|
|
@ -31,8 +31,8 @@ coarsetime = "=0.1.11"
|
||||||
utime = "0.2.1"
|
utime = "0.2.1"
|
||||||
serde-aux = "0.6.1"
|
serde-aux = "0.6.1"
|
||||||
unic-langid = { version = "0.8.0", features = ["macros"] }
|
unic-langid = { version = "0.8.0", features = ["macros"] }
|
||||||
fluent = "0.10.2"
|
fluent = { git = "https://github.com/ankitects/fluent-rs.git", branch="32bit-panic" }
|
||||||
intl-memoizer = "0.3.0"
|
intl-memoizer = { git = "https://github.com/ankitects/fluent-rs.git", branch="32bit-panic" }
|
||||||
num-format = "0.4.0"
|
num-format = "0.4.0"
|
||||||
|
|
||||||
[target.'cfg(target_vendor="apple")'.dependencies]
|
[target.'cfg(target_vendor="apple")'.dependencies]
|
||||||
|
|
|
@ -15,6 +15,8 @@ fn get_identifiers(ftl_text: &str) -> Vec<String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idents.sort();
|
||||||
|
|
||||||
idents
|
idents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -323,7 +323,11 @@ fn set_bundle_formatter_for_langs<T>(bundle: &mut FluentBundle<T>, langs: &[Lang
|
||||||
let num_formatter = NumberFormatter::new(langs);
|
let num_formatter = NumberFormatter::new(langs);
|
||||||
let formatter = move |val: &FluentValue, _intls: &Mutex<IntlLangMemoizer>| -> Option<String> {
|
let formatter = move |val: &FluentValue, _intls: &Mutex<IntlLangMemoizer>| -> Option<String> {
|
||||||
match val {
|
match val {
|
||||||
FluentValue::Number(n) => Some(num_formatter.format(n.value)),
|
FluentValue::Number(n) => {
|
||||||
|
let mut num = n.clone();
|
||||||
|
num.options.maximum_fraction_digits = Some(2);
|
||||||
|
Some(num_formatter.format(num.as_string().to_string()))
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -371,17 +375,12 @@ impl NumberFormatter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(&self, num: f64) -> String {
|
/// Given a pre-formatted number, change the decimal separator as appropriate.
|
||||||
// is it an integer?
|
fn format(&self, num: String) -> String {
|
||||||
if (num - num.round()).abs() < std::f64::EPSILON {
|
if self.decimal_separator != "." {
|
||||||
num.to_string()
|
num.replace(".", self.decimal_separator)
|
||||||
} else {
|
} else {
|
||||||
// currently we hard-code to 2 decimal places
|
num
|
||||||
let mut formatted = format!("{:.2}", num);
|
|
||||||
if self.decimal_separator != "." {
|
|
||||||
formatted = formatted.replace(".", self.decimal_separator)
|
|
||||||
}
|
|
||||||
formatted
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,11 +394,8 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn numbers() {
|
fn numbers() {
|
||||||
let fmter = NumberFormatter::new(&[]);
|
|
||||||
assert_eq!(&fmter.format(1.0), "1");
|
|
||||||
assert_eq!(&fmter.format(1.007), "1.01");
|
|
||||||
let fmter = NumberFormatter::new(&[langid!("pl-PL")]);
|
let fmter = NumberFormatter::new(&[langid!("pl-PL")]);
|
||||||
assert_eq!(&fmter.format(1.007), "1,01");
|
assert_eq!(&fmter.format("1.007".to_string()), "1,007");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -414,7 +410,7 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
i18n.tr_("two-args-key", Some(tr_args!["one"=>1.1, "two"=>"2"])),
|
i18n.tr_("two-args-key", Some(tr_args!["one"=>1.1, "two"=>"2"])),
|
||||||
"two args: 1.10 and 2"
|
"two args: 1.1 and 2"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -423,7 +419,7 @@ mod test {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
i18n.tr_("plural", Some(tr_args!["hats"=>1.1])),
|
i18n.tr_("plural", Some(tr_args!["hats"=>1.1])),
|
||||||
"You have 1.10 hats."
|
"You have 1.1 hats."
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
i18n.tr_("plural", Some(tr_args!["hats"=>3])),
|
i18n.tr_("plural", Some(tr_args!["hats"=>3])),
|
||||||
|
|
|
@ -7,8 +7,9 @@ use crate::i18n::{tr_args, FString, I18n};
|
||||||
pub fn answer_button_time(seconds: f32, i18n: &I18n) -> String {
|
pub fn answer_button_time(seconds: f32, i18n: &I18n) -> String {
|
||||||
let span = Timespan::from_secs(seconds).natural_span();
|
let span = Timespan::from_secs(seconds).natural_span();
|
||||||
let amount = match span.unit() {
|
let amount = match span.unit() {
|
||||||
TimespanUnit::Months | TimespanUnit::Years => span.as_unit(),
|
// months/years shown with 1 decimal place
|
||||||
// we don't show fractional values except for months/years
|
TimespanUnit::Months | TimespanUnit::Years => (span.as_unit() * 10.0).round() / 10.0,
|
||||||
|
// other values shown without decimals
|
||||||
_ => span.as_unit().round(),
|
_ => span.as_unit().round(),
|
||||||
};
|
};
|
||||||
let args = tr_args!["amount" => amount];
|
let args = tr_args!["amount" => amount];
|
||||||
|
@ -173,7 +174,7 @@ mod test {
|
||||||
let i18n = I18n::new(&["zz"], "");
|
let i18n = I18n::new(&["zz"], "");
|
||||||
assert_eq!(answer_button_time(30.0, &i18n), "30s");
|
assert_eq!(answer_button_time(30.0, &i18n), "30s");
|
||||||
assert_eq!(answer_button_time(70.0, &i18n), "1m");
|
assert_eq!(answer_button_time(70.0, &i18n), "1m");
|
||||||
assert_eq!(answer_button_time(1.1 * MONTH, &i18n), "1.10mo");
|
assert_eq!(answer_button_time(1.1 * MONTH, &i18n), "1.1mo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -181,7 +182,7 @@ mod test {
|
||||||
let i18n = I18n::new(&["zz"], "");
|
let i18n = I18n::new(&["zz"], "");
|
||||||
assert_eq!(time_span(1.0, &i18n), "1 second");
|
assert_eq!(time_span(1.0, &i18n), "1 second");
|
||||||
assert_eq!(time_span(30.0, &i18n), "30 seconds");
|
assert_eq!(time_span(30.0, &i18n), "30 seconds");
|
||||||
assert_eq!(time_span(90.0, &i18n), "1.50 minutes");
|
assert_eq!(time_span(90.0, &i18n), "1.5 minutes");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue