begin plural string migration

This commit is contained in:
Damien Elmes 2020-11-17 23:24:19 +10:00
parent 2453e5c488
commit e527d31dfc
12 changed files with 166 additions and 36 deletions

View File

@ -24,7 +24,7 @@ import anki
import aqt
import aqt.forms
from anki.httpclient import HttpClient
from anki.lang import _, ngettext, without_unicode_isolation
from anki.lang import ngettext, without_unicode_isolation
from aqt import gui_hooks
from aqt.qt import *
from aqt.utils import (
@ -275,10 +275,7 @@ class AddonManager:
if conflicting:
addons = ", ".join(self.addonName(f) for f in conflicting)
showInfo(
_(
"The following add-ons are incompatible with %(name)s \
and have been disabled: %(found)s"
)
tr(TR.ADDONS_THE_FOLLOWING_ADDONS_ARE_INCOMPATIBLE_WITH)
% dict(name=addon.human_name(), found=addons),
textFormat="plain",
)
@ -468,7 +465,9 @@ and have been disabled: %(found)s"
"manifest": tr(TR.ADDONS_INVALID_ADDON_MANIFEST),
}
msg = messages.get(result.errmsg, _("Unknown error: {}".format(result.errmsg)))
msg = messages.get(
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)
@ -1400,13 +1399,9 @@ def installAddonPackages(
if warn:
names = ",<br>".join(f"<b>{os.path.basename(p)}</b>" for p in paths)
q = _(
"<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"
) % dict(names=names)
q = tr(TR.ADDONS_IMPORTANT_AS_ADDONS_ARE_PROGRAMS_DOWNLOADED) % dict(
names=names
)
if (
not showInfo(
q,

View File

@ -59,3 +59,7 @@ 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?
}

View File

@ -61,3 +61,7 @@ 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-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-second = { $count ->
[one] { $count } second
*[other] { $count } seconds
}

View File

@ -170,15 +170,22 @@ text_remap = {
"studying": ["Space"],
"qt-misc": ["&Edit", "&Guide...", "&Help", "&Undo", "Unexpected response code: %s"],
"adding": ["Added"],
"browsing": ["%d note"],
}
blacklist = {"Anki", "%", "Dialog", "Center", "Left", "Right", "~", "&Cram..."}
def determine_module(text, files):
if not files:
return None
if text in blacklist:
return None
if text.count("%") > 1:
print("skip", text)
return None
if "&" in text:
return "qt-accel"
@ -189,6 +196,7 @@ def determine_module(text, files):
if len(files) == 1:
return list(files)[0]
print(text, files)
assert False
@ -225,8 +233,7 @@ seen_keys = set()
def migrate_entry(entry):
if entry.msgid_plural:
# print("skip plural", entry.msgid)
if not entry.msgid_plural:
return
text = entry.msgid
@ -244,7 +251,7 @@ def migrate_entry(entry):
files2 = set()
for file in files:
if file == "stats":
if file in ("stats", "supermemo_xml"):
continue
file = module_map[file]
files2.add(file)
@ -260,7 +267,7 @@ def migrate_entry(entry):
seen_keys.add(key)
modules.setdefault(module, [])
modules[module].append((key, text))
modules[module].append((key, [entry.msgid, entry.msgid_plural]))
return None
@ -309,8 +316,8 @@ for (module, items) in modules.items():
strings_by_module[module] = items
for item in items:
(key, text) = item
assert text not in keys_by_text
keys_by_text[text] = (module, key)
assert text[0] not in keys_by_text
keys_by_text[text[0]] = (module, key)
with open("strings_by_module.json", "w") as file:
file.write(json.dumps(strings_by_module))

View File

@ -2,6 +2,9 @@
import json
import os
import re
from typing import List
from fluent.syntax import parse, serialize
from fluent.syntax.ast import Message, TextElement, Identifier, Pattern, Junk
@ -13,6 +16,8 @@ qt_modules = {"about", "qt-accel", "addons", "qt-misc"}
modules = json.load(open("strings_by_module.json"))
translations = json.load(open("strings.json"))
plurals = json.load(open("plurals.json"))
# # fixme:
# addons addons-downloaded-fnames Downloaded %(fname)s
@ -33,17 +38,43 @@ translations = json.load(open("strings.json"))
# fixme: isolation chars
def transform_text(text: str) -> str:
# fixme: automatically remap to %s in a compat wrapper? manually fix?
text = (
text.replace("%d", "{ $val }")
.replace("%s", "{ $val }")
.replace("{}", "{ $val }")
)
if "Clock drift" in text:
text = text.replace("\n", "<br>")
else:
text = text.replace("\n", " ")
def plural_text(key, lang, translation):
lang = re.sub("(_|-).*", "", lang)
var = re.findall(r"{ (\$.*?) }", translation[0])
try:
var = var[0]
except:
print(key, lang, translation)
raise
buf = f"{key} = {{ {var} ->\n"
# for each of the plural forms except the last
for idx, msg in enumerate(translation[:-1]):
plural_form = plurals[lang][idx]
buf += f" [{plural_form}] {msg}\n"
# add the catchall
msg = translation[-1]
buf += f" *[other] {msg}\n"
buf += " }\n"
return buf
def transform_text(key: str, texts: List[str], lang: str) -> str:
texts = [
(
text.replace("%d", "{ $count }")
.replace("%s", "{ $count }")
.replace("%(num)d", "{ $count }")
)
for text in texts
]
text = plural_text(key, lang, texts)
print(text)
return text
@ -57,6 +88,14 @@ def check_parses(path: str):
raise Exception(f"file had junk! {path} {ent}")
def munge_key(key):
if key == "browsing-note":
return "browsing-note-count"
if key == "card-templates-card":
return "card-templates-card-count"
return key
for module, items in modules.items():
if module in qt_modules:
folder = qt
@ -68,8 +107,9 @@ for module, items in modules.items():
print(path)
with open(path, "a", encoding="utf8") as file:
for (key, text) in items:
text2 = transform_text(text)
file.write(f"{key} = {text2}\n")
key = munge_key(key)
text2 = transform_text(key, text, "en")
file.write(text2)
check_parses(path)
@ -80,8 +120,12 @@ for module, items in modules.items():
out = []
for (key, text) in items:
if text in map:
out.append((key, transform_text(map[text])))
if text[0] in map:
forms = map[text[0]]
if isinstance(forms, str):
forms = [forms]
key = munge_key(key)
out.append(transform_text(key, forms, lang))
if out:
path = os.path.join(folder, lang, module + ".ftl")
@ -91,7 +135,7 @@ for module, items in modules.items():
print(path)
with open(path, "a", encoding="utf8") as file:
for (key, text) in out:
file.write(f"{key} = {text}\n")
for o in out:
file.write(o)
check_parses(path)

View File

@ -102,3 +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-selected = { $count ->
[one] { $count } selected
*[other] { $count } selected
}

View File

@ -40,3 +40,11 @@ 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?
}

View File

@ -31,3 +31,7 @@ decks-reschedule-cards-based-on-my-answers = Reschedule cards based on my answer
decks-study = Study
decks-study-deck = Study Deck
decks-the-provided-search-did-not-match = The provided search did not match any cards. Would you like to revise it?
decks-it-has-card = { $count ->
[one] It has { $count } card.
*[other] It has { $count } cards.
}

View File

@ -15,3 +15,15 @@ exporting-include-scheduling-information = Include scheduling information
exporting-include-tags = Include tags
exporting-notes-in-plain-text = Notes in Plain Text
exporting-selected-notes = Selected Notes
exporting-card-exported = { $count ->
[one] { $count } card exported.
*[other] { $count } cards exported.
}
exporting-exported-media-file = { $count ->
[one] Exported { $count } media file
*[other] Exported { $count } media files
}
exporting-note-exported = { $count ->
[one] { $count } note exported.
*[other] { $count } notes exported.
}

View File

@ -52,3 +52,23 @@ 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
}

View File

@ -138,3 +138,7 @@ scheduling-steps-must-be-numbers = Steps must be numbers.
scheduling-tag-only = Tag Only
scheduling-the-default-configuration-cant-be-removed = The default configuration can't be removed.
scheduling-your-changes-will-affect-multiple-decks = Your changes will affect multiple decks. If you wish to change only the current deck, please add a new options group first.
scheduling-deck-updated = { $count ->
[one] { $count } deck updated.
*[other] { $count } decks updated.
}

View File

@ -42,3 +42,15 @@ studying-type-answer-unknown-field = Type answer: unknown field { $val }
studying-unbury = Unbury
studying-what-would-you-like-to-unbury = What would you like to unbury?
studying-you-havent-recorded-your-voice-yet = You haven't recorded your voice yet.
studying-card-studied-in = { $count ->
[one] { $count } card studied in
*[other] { $count } cards studied in
}
studying-minute = { $count ->
[one] { $count } minute.
*[other] { $count } minutes.
}
studying-note-and-its-card-deleted = { $count ->
[one] Note and its { $count } card deleted.
*[other] Note and its { $count } cards deleted.
}