update remaining python format strings to Fluent

This commit is contained in:
Damien Elmes 2020-11-22 14:57:53 +10:00
parent 751e8183c4
commit 0b848eae56
12 changed files with 187 additions and 98 deletions

View file

@ -43,7 +43,7 @@ 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-found-as-across-bs = Found { $part } across { $whole }.
browsing-home = Home
browsing-ignore-case = Ignore case
browsing-in = <b>In</b>:
@ -54,7 +54,7 @@ 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-nd-names = { $num }: { $name }
browsing-new = (new)
browsing-new-note-type = New note type:
browsing-no-flag = No Flag
@ -102,17 +102,19 @@ 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.
browsing-group = { $count ->
[one] { $count } group
*[other] { $count } groups
}
browsing-note-count = { $count ->
[one] { $count } note
*[other] { $count } notes
}
browsing-note-deleted = { $count ->
[one] { $count } note deleted.
*[other] { $count } notes deleted.
}
browsing-group =
{ $count ->
[one] { $count } group
*[other] { $count } groups
}
browsing-note-count =
{ $count ->
[one] { $count } note
*[other] { $count } notes
}
browsing-note-deleted =
{ $count ->
[one] { $count } note deleted.
*[other] { $count } notes deleted.
}
browsing-window-title = Browse ({ $selected } of { $total } cards selected)

View file

@ -11,7 +11,7 @@ card-templates-front-preview = Front Preview
card-templates-back-preview = Back Preview
card-templates-preview-box = Preview
card-templates-template-box = Template
card-templates-sample-cloze = This is a {"{{c1::"}sample{"}}"} cloze deletion.
card-templates-sample-cloze = This is a { "{{c1::" }sample{ "}}" } cloze deletion.
card-templates-fill-empty = Fill Empty Fields
card-templates-night-mode = Night Mode
# Add "mobile" class to card preview, so the card appears like it would
@ -30,21 +30,23 @@ 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-delete-the-as-card-type-and = Delete the '{ $template }' card type, and its { $cards }?
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-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...
card-templates-card-count = { $count ->
[one] { $count } card
*[other] { $count } cards
}
card-templates-this-will-create-card-proceed = { $count ->
[one] This will create { $count } card. Proceed?
*[other] This will create { $count } cards. Proceed?
}
card-templates-card-count =
{ $count ->
[one] { $count } card
*[other] { $count } cards
}
card-templates-this-will-create-card-proceed =
{ $count ->
[one] This will create { $count } card. Proceed?
*[other] This will create { $count } cards. Proceed?
}

View file

@ -36,7 +36,7 @@ importing-notes-that-could-not-be-imported = Notes that could not be imported as
importing-notes-updated-as-file-had-newer = Notes updated, as file had newer version: { $val }
importing-packaged-anki-deckcollection-apkg-colpkg-zip = Packaged Anki Deck/Collection (*.apkg *.colpkg *.zip)
importing-pauker-18-lesson-paugz = Pauker 1.8 Lesson (*.pau.gz)
importing-rows-had-num1d-fields-expected-num2d = '%(row)s' had %(num1)d fields, expected %(num2)d
importing-rows-had-num1d-fields-expected-num2d = '{ $row }' had { $found } fields, expected { $expected }
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
@ -52,23 +52,28 @@ importing-unable-to-import-from-a-readonly = Unable to import from a read-only f
importing-unknown-file-format = Unknown file format.
importing-update-existing-notes-when-first-field = Update existing notes when first field matches
importing-updated = Updated
importing-note-added = { $count ->
[one] { $count } note added
*[other] { $count } notes added
}
importing-note-imported = { $count ->
[one] { $count } note imported.
*[other] { $count } notes imported.
}
importing-note-unchanged = { $count ->
[one] { $count } note unchanged
*[other] { $count } notes unchanged
}
importing-note-updated = { $count ->
[one] { $count } note updated
*[other] { $count } notes updated
}
importing-processed-media-file = { $count ->
[one] Processed { $count } media file
*[other] Processed { $count } media files
}
importing-note-added =
{ $count ->
[one] { $count } note added
*[other] { $count } notes added
}
importing-note-imported =
{ $count ->
[one] { $count } note imported.
*[other] { $count } notes imported.
}
importing-note-unchanged =
{ $count ->
[one] { $count } note unchanged
*[other] { $count } notes unchanged
}
importing-note-updated =
{ $count ->
[one] { $count } note updated
*[other] { $count } notes updated
}
importing-processed-media-file =
{ $count ->
[one] Processed { $count } media file
*[other] Processed { $count } media files
}

View file

@ -7,4 +7,4 @@ about-copy-debug-info = Copy Debug Info
about-if-you-have-contributed-and-are = If you have contributed and are not on this list, please get in touch.
about-version = Version { $val }
about-visit-website = <a href='{ $val }'>Visit website</a>
about-written-by-damien-elmes-with-patches = Written by Damien Elmes, with patches, translation, testing and design from:<p>%(cont)s
about-written-by-damien-elmes-with-patches = Written by Damien Elmes, with patches, translation, testing and design from:<p>{ $cont }

View file

@ -19,13 +19,13 @@ addons-code = Code:
addons-config = Config
addons-configuration = Configuration
addons-corrupt-addon-file = Corrupt add-on file.
addons-disabled = (disabled)
addons-disabled = (disabled)
addons-disabled2 = (disabled)
addons-download-complete-please-restart-anki-to = Download complete. Please restart Anki to apply changes.
addons-downloaded-fnames = Downloaded %(fname)s
addons-downloading-adbd-kb02fkb = Downloading %(a)d/%(b)d (%(kb)0.2fKB)...
addons-error-downloading-ids-errors = Error downloading <i>%(id)s</i>: %(error)s
addons-error-installing-bases-errors = Error installing <i>%(base)s</i>: %(error)s
addons-downloaded-fnames = Downloaded { $fname }
addons-downloading-adbd-kb02fkb = Downloading { $part }/{ $total } ({ $kilobytes }KB)...
addons-error-downloading-ids-errors = Error downloading <i>{ $id }</i>: { $error }
addons-error-installing-bases-errors = Error installing <i>{ $base }</i>: { $error }
addons-get-addons = Get Add-ons...
addons-important-as-addons-are-programs-downloaded = <b>Important</b>: As add-ons are programs downloaded from the internet, they are potentially malicious.<b>You should only install add-ons you trust.</b><br><br>Are you sure you want to proceed with the installation of the following Anki add-on(s)?<br><br>%(names)s
addons-install-addon = Install Add-on
@ -33,12 +33,12 @@ addons-install-addons = Install Add-on(s)
addons-install-anki-addon = Install Anki add-on
addons-install-from-file = Install from file...
addons-installation-complete = Installation complete
addons-installed-names = Installed %(name)s
addons-installed-names = Installed { $name }
addons-installed-successfully = Installed successfully.
addons-invalid-addon-manifest = Invalid add-on manifest.
addons-invalid-code = Invalid code.
addons-invalid-code-or-addon-not-available = Invalid code, or add-on not available for your version of Anki.
addons-invalid-configuration = Invalid configuration:
addons-invalid-configuration = Invalid configuration:
addons-invalid-configuration-top-level-object-must = Invalid configuration: top level object must be a map
addons-no-updates-available = No updates available.
addons-one-or-more-errors-occurred = One or more errors occurred:
@ -49,7 +49,7 @@ addons-please-restart-anki-to-complete-the = <b>Please restart Anki to complete
addons-please-select-a-single-addon-first = Please select a single add-on first.
addons-requires = (requires { $val })
addons-restored-defaults = Restored defaults
addons-the-following-addons-are-incompatible-with = The following add-ons are incompatible with %(name)s and have been disabled: %(found)s
addons-the-following-addons-are-incompatible-with = The following add-ons are incompatible with { $name } and have been disabled: { $found }
addons-the-following-addons-have-updates-available = The following add-ons have updates available. Install them now?
addons-the-following-conflicting-addons-were-disabled = The following conflicting add-ons were disabled:
addons-this-addon-is-not-compatible-with = This add-on is not compatible with your version of Anki.
@ -59,7 +59,8 @@ addons-unable-to-update-or-delete-addon = Unable to update or delete add-on. Ple
addons-unknown-error = Unknown error: { $val }
addons-view-addon-page = View Add-on Page
addons-view-files = View Files
addons-delete-the-numd-selected-addon = { $count ->
[one] Delete the { $count } selected add-on?
*[other] Delete the { $count } selected add-ons?
}
addons-delete-the-numd-selected-addon =
{ $count ->
[one] Delete the { $count } selected add-on?
*[other] Delete the { $count } selected add-ons?
}

View file

@ -42,13 +42,11 @@ class TextImporter(NoteImporter):
if row:
log.append(
self.col.tr(
TR.IMPORTING_ROWS_HAD_NUM1D_FIELDS_EXPECTED_NUM2D
TR.IMPORTING_ROWS_HAD_NUM1D_FIELDS_EXPECTED_NUM2D,
row=" ".join(row),
found=len(row),
expected=self.numFields,
)
% {
"row": " ".join(row),
"num1": len(row),
"num2": self.numFields,
}
)
ignored += 1
continue

View file

@ -202,9 +202,9 @@ def show(mw):
)
)
abouttext += "<p>" + tr(TR.ABOUT_WRITTEN_BY_DAMIEN_ELMES_WITH_PATCHES) % {
"cont": ", ".join(allusers)
}
abouttext += "<p>" + tr(
TR.ABOUT_WRITTEN_BY_DAMIEN_ELMES_WITH_PATCHES, cont=", ".join(allusers)
)
abouttext += "<p>" + tr(TR.ABOUT_IF_YOU_HAVE_CONTRIBUTED_AND_ARE)
abouttext += "<p>" + tr(TR.ABOUT_A_BIG_THANKS_TO_ALL_THE)
abt.label.setMinimumWidth(800)

View file

@ -275,8 +275,11 @@ class AddonManager:
if conflicting:
addons = ", ".join(self.addonName(f) for f in conflicting)
showInfo(
tr(TR.ADDONS_THE_FOLLOWING_ADDONS_ARE_INCOMPATIBLE_WITH)
% dict(name=addon.human_name(), found=addons),
tr(
TR.ADDONS_THE_FOLLOWING_ADDONS_ARE_INCOMPATIBLE_WITH,
name=addon.human_name(),
found=addons,
),
textFormat="plain",
)
@ -306,7 +309,7 @@ class AddonManager:
meta = self.addon_meta(dir)
name = meta.human_name()
if not meta.enabled:
name += tr(TR.ADDONS_DISABLED)
name += " " + tr(TR.ADDONS_DISABLED)
return name
# Conflict resolution
@ -469,26 +472,24 @@ class AddonManager:
result.errmsg, tr(TR.ADDONS_UNKNOWN_ERROR, val=result.errmsg)
)
if mode == "download": # preserve old format strings for i18n
template = tr(TR.ADDONS_ERROR_DOWNLOADING_IDS_ERRORS)
if mode == "download":
template = tr(TR.ADDONS_ERROR_DOWNLOADING_IDS_ERRORS, id=base, error=msg)
else:
template = tr(TR.ADDONS_ERROR_INSTALLING_BASES_ERRORS)
template = tr(TR.ADDONS_ERROR_INSTALLING_BASES_ERRORS, base=base, error=msg)
name = base
return [template % dict(base=name, id=name, error=msg)]
return [template]
def _installationSuccessReport(
self, result: InstallOk, base: str, mode: str = "download"
) -> List[str]:
if mode == "download": # preserve old format strings for i18n
template = tr(TR.ADDONS_DOWNLOADED_FNAMES)
else:
template = tr(TR.ADDONS_INSTALLED_NAMES)
name = result.name or base
strings = [template % dict(name=name, fname=name)]
if mode == "download":
template = tr(TR.ADDONS_DOWNLOADED_FNAMES, fname=name)
else:
template = tr(TR.ADDONS_INSTALLED_NAMES, name=name)
strings = [template]
if result.conflicts:
strings.append(
@ -1074,13 +1075,12 @@ class DownloaderInstaller(QObject):
def _progress_callback(self, up: int, down: int) -> None:
self.dl_bytes += down
self.mgr.mw.progress.update(
# T: "%(a)d" is the index of the element currently
# downloaded. "%(b)d" is the number of element to download,
# and "%(kb)0.2f" is the number of downloaded
# kilobytes. This lead for example to "Downloading 3/5
# (27KB)"
label=tr(TR.ADDONS_DOWNLOADING_ADBD_KB02FKB)
% dict(a=len(self.log) + 1, b=len(self.ids), kb=self.dl_bytes / 1024)
label=tr(
TR.ADDONS_DOWNLOADING_ADBD_KB02FKB,
part=len(self.log) + 1,
total=len(self.ids),
kilobytes=self.dl_bytes / 1024,
)
)
def _download_all(self) -> None:
@ -1361,7 +1361,7 @@ class ConfigEditor(QDialog):
showInfo(msg)
return
except Exception as e:
showInfo(tr(TR.ADDONS_INVALID_CONFIGURATION) + repr(e))
showInfo(tr(TR.ADDONS_INVALID_CONFIGURATION) + " " + repr(e))
return
if not isinstance(new_conf, dict):

View file

@ -1352,8 +1352,10 @@ QTableView {{ gridline-color: {grid} }}
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 = tr(TR.BROWSING_ND_NAMES) % dict(
n=c + 1, name=self._escapeMenuItem(tmpl["name"])
name = tr(
TR.BROWSING_ND_NAMES,
num=c + 1,
name=self._escapeMenuItem(tmpl["name"]),
)
subm.addItem(
name, self._filterFunc("note", nt["name"], "card", str(c + 1))
@ -1995,7 +1997,7 @@ where id in %s"""
notes = sum(len(r[1]) for r in res)
part1 = tr(TR.BROWSING_GROUP, count=groups)
part2 = tr(TR.BROWSING_NOTE_COUNT, count=notes)
t += tr(TR.BROWSING_FOUND_AS_ACROSS_BS) % dict(a=part1, b=part2)
t += tr(TR.BROWSING_FOUND_AS_ACROSS_BS, part=part1, whole=part2)
t += "<p><ol>"
for val, nids in res:
t += (

View file

@ -564,8 +564,10 @@ class CardLayout(QDialog):
template = self.current_template()
cards = tr(TR.CARD_TEMPLATES_CARD_COUNT, count=card_cnt)
msg = tr(TR.CARD_TEMPLATES_DELETE_THE_AS_CARD_TYPE_AND) % dict(
a=template["name"], b=cards
msg = tr(
TR.CARD_TEMPLATES_DELETE_THE_AS_CARD_TYPE_AND,
template=template["name"],
cards=cards,
)
if not askUser(msg):
return

View file

@ -13,3 +13,9 @@ py_binary(
srcs = ["extract-strings.py"],
deps = [requirement("fluent-syntax")],
)
py_binary(
name = "transform-string",
srcs = ["transform-string.py"],
deps = [requirement("fluent-syntax")],
)

View file

@ -0,0 +1,71 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
Tool to apply transform to an ftl string and its translations.
"""
import os
import json
import glob
from fluent.syntax import parse, serialize
from fluent.syntax.ast import Junk
template_root = os.environ["BUILD_WORKSPACE_DIRECTORY"]
template_files = glob.glob(
os.path.join(template_root, "ftl", "*", "*.ftl"), recursive=True
)
translation_root = os.path.join(template_root, "..", "anki-i18n")
translation_files = glob.glob(
os.path.join(translation_root, "*", "*", "*", "*.ftl"), recursive=True
)
target_repls = [
["addons-downloaded-fnames", "%(fname)s", "{ $fname }"],
["addons-downloading-adbd-kb02fkb", "%(a)d", "{ $part }"],
["addons-downloading-adbd-kb02fkb", "%(b)d", "{ $total }"],
["addons-downloading-adbd-kb02fkb", "%(kb)0.2f", "{ $kilobytes }"],
["addons-error-downloading-ids-errors", "%(id)s", "{ $id }"],
["addons-error-downloading-ids-errors", "%(error)s", "{ $error }"],
["addons-error-installing-bases-errors", "%(base)s", "{ $base }"],
["addons-error-installing-bases-errors", "%(error)s", "{ $error }"],
["addons-important-as-addons-are-programs-downloaded", "%(name)s", "{ $name }"],
["addons-installed-names", "%(name)s", "{ $name }"],
["addons-the-following-addons-are-incompatible-with", "%(name)s", "{ $name }"],
["addons-the-following-addons-are-incompatible-with", "%(found)s", "{ $found }"],
["about-written-by-damien-elmes-with-patches", "%(cont)s", "{ $cont }"],
["importing-rows-had-num1d-fields-expected-num2d", "%(row)s", "{ $row }"],
["importing-rows-had-num1d-fields-expected-num2d", "%(num1)d", "{ $found }"],
["importing-rows-had-num1d-fields-expected-num2d", "%(num2)d", "{ $expected }"],
["card-templates-delete-the-as-card-type-and", "%(a)s", "{ $template }"],
["card-templates-delete-the-as-card-type-and", "%(b)s", "{ $cards }"],
["browsing-found-as-across-bs", "%(a)s", "{ $part }"],
["browsing-found-as-across-bs", "%(b)s", "{ $whole }"],
["browsing-nd-names", "%(n)d", "{ $num }"],
["browsing-nd-names", "%(name)s", "{ $name }"],
]
def transform_string_in_file(path):
obj = parse(open(path).read(), with_spans=False)
changed = False
for ent in obj.body:
if isinstance(ent, Junk):
raise Exception(f"file had junk! {path} {ent}")
if getattr(ent, "id", None):
key = ent.id.name
for (target_key, src, dst) in target_repls:
if key == target_key:
for elem in ent.value.elements:
newval = elem.value.replace(src, dst)
if newval != elem.value:
elem.value = newval
changed = True
if changed:
open(path, "w", encoding="utf8").write(serialize(obj))
print("updated", path)
for path in template_files + translation_files:
transform_string_in_file(path)