diff --git a/translate/convert/convert.py b/translate/convert/convert.py index dd9633a6b1..6861eb4fb2 100644 --- a/translate/convert/convert.py +++ b/translate/convert/convert.py @@ -119,8 +119,7 @@ def add_duplicates_option(self, default="msgctxt"): default=default, type="choice", choices=["msgctxt", "merge"], - help="what to do with duplicate strings (identical source text): merge, msgctxt (default: '%s')" - % default, + help=f"what to do with duplicate strings (identical source text): merge, msgctxt (default: '{default}')", metavar="DUPLICATESTYLE", ) self.passthrough.append("duplicatestyle") @@ -410,7 +409,7 @@ def opentemplatefile(self, options, fulltemplatepath): # TODO: deal with different names in input/template archives if fulltemplatepath in self.templatearchive: return self.templatearchive.openinputfile(fulltemplatepath) - self.warning("missing template file %s" % fulltemplatepath) + self.warning(f"missing template file {fulltemplatepath}") return super().opentemplatefile(options, fulltemplatepath) def getfulltemplatepath(self, options, templatepath): @@ -452,8 +451,8 @@ def openoutputfile(self, options, fulloutputpath): outputstream = self.outputarchive.openoutputfile(fulloutputpath) if outputstream is None: self.warning( - "Could not find where to put %s in output " - "archive; writing to tmp" % fulloutputpath + f"Could not find where to put {fulloutputpath} in output " + "archive; writing to tmp" ) return BytesIO() return outputstream @@ -464,7 +463,7 @@ def recursiveprocess(self, options): if hasattr(options, "multifilestyle"): self.archiveoptions = {"multifilestyle": options.multifilestyle} for filetype in ("input", "output", "template"): - allowoption = "allowrecursive%s" % filetype + allowoption = f"allowrecursive{filetype}" if options.multifilestyle == "onefile" and getattr( options, allowoption, True ): diff --git a/translate/convert/csv2po.py b/translate/convert/csv2po.py index 5579c2f683..1f0983761e 100644 --- a/translate/convert/csv2po.py +++ b/translate/convert/csv2po.py @@ -202,7 +202,7 @@ def convertstore(self, thecsvfile): ) else: targetheader = self.pofile.makeheader(charset="UTF-8", encoding="8bit") - targetheader.addnote("extracted from %s" % self.csvfile.filename, "developer") + targetheader.addnote(f"extracted from {self.csvfile.filename}", "developer") mightbeheader = True for csvunit in self.csvfile.units: # if self.charset is not None: diff --git a/translate/convert/dtd2po.py b/translate/convert/dtd2po.py index cdaa06190e..158a723459 100644 --- a/translate/convert/dtd2po.py +++ b/translate/convert/dtd2po.py @@ -212,7 +212,7 @@ def convertstore(self, dtd_store): x_accelerator_marker="&", x_merge_on="location", ) - targetheader.addnote("extracted from %s" % dtd_store.filename, "developer") + targetheader.addnote(f"extracted from {dtd_store.filename}", "developer") dtd_store.makeindex() self.mixedentities = self.mixer.match_entities(dtd_store.id_index) diff --git a/translate/convert/factory.py b/translate/convert/factory.py index 60d62af945..57609a65ad 100644 --- a/translate/convert/factory.py +++ b/translate/convert/factory.py @@ -41,7 +41,7 @@ def __init__(self, afile): self.file = afile def __str__(self): - return "Unable to find extension for file: %s" % (self.file) + return f"Unable to find extension for file: {self.file}" class UnsupportedConversionError(Exception): @@ -53,7 +53,7 @@ def __init__(self, in_ext=None, out_ext=None, templ_ext=None): def __str__(self): msg = f"Unsupported conversion from {self.in_ext} to {self.out_ext}" if self.templ_ext: - msg += " with template %s" % (self.templ_ext) + msg += f" with template {self.templ_ext}" return msg diff --git a/translate/convert/ical2po.py b/translate/convert/ical2po.py index 666b1817b1..0915aeef2c 100644 --- a/translate/convert/ical2po.py +++ b/translate/convert/ical2po.py @@ -66,7 +66,7 @@ def convert_unit(self, unit): def convert_store(self): """Convert a single source format file to a target format file.""" - self.extraction_msg = "extracted from %s" % self.source_store.filename + self.extraction_msg = f"extracted from {self.source_store.filename}" for source_unit in self.source_store.units: self.target_store.addunit(self.convert_unit(source_unit)) diff --git a/translate/convert/ini2po.py b/translate/convert/ini2po.py index fb038f667c..65359777ff 100644 --- a/translate/convert/ini2po.py +++ b/translate/convert/ini2po.py @@ -72,7 +72,7 @@ def convert_unit(self, unit): def convert_store(self): """Convert a single source format file to a target format file.""" - self.extraction_msg = "extracted from %s" % self.source_store.filename + self.extraction_msg = f"extracted from {self.source_store.filename}" for source_unit in self.source_store.units: self.target_store.addunit(self.convert_unit(source_unit)) diff --git a/translate/convert/json2po.py b/translate/convert/json2po.py index 7f0ad70982..55f23617ce 100644 --- a/translate/convert/json2po.py +++ b/translate/convert/json2po.py @@ -37,7 +37,7 @@ def convert_store(self, input_store, duplicatestyle="msgctxt"): """Converts a JSON file to a PO file.""" output_store = po.pofile() output_header = output_store.header() - output_header.addnote("extracted from %s" % input_store.filename, "developer") + output_header.addnote(f"extracted from {input_store.filename}", "developer") for input_unit in input_store.units: output_unit = self.convert_unit(input_unit, "developer") if output_unit is not None: diff --git a/translate/convert/mozlang2po.py b/translate/convert/mozlang2po.py index 84c9c333d6..f8badebdc2 100644 --- a/translate/convert/mozlang2po.py +++ b/translate/convert/mozlang2po.py @@ -66,7 +66,7 @@ def convert_unit(self, unit): def convert_store(self): """Convert a single source format file to a target format file.""" - self.extraction_msg = "extracted from %s" % self.source_store.filename + self.extraction_msg = f"extracted from {self.source_store.filename}" for source_unit in self.source_store.units: self.target_store.addunit(self.convert_unit(source_unit)) diff --git a/translate/convert/oo2po.py b/translate/convert/oo2po.py index f12577a9aa..9532e8d02e 100644 --- a/translate/convert/oo2po.py +++ b/translate/convert/oo2po.py @@ -95,21 +95,23 @@ def convertstore(self, theoofile, duplicatestyle="msgctxt"): """Converts an entire oo file to a base class format (.po or XLIFF).""" thetargetfile = po.pofile() # create a header for the file - bug_url = "http://qa.openoffice.org/issues/enter_bug.cgi?%s" % parse.urlencode( - { - "subcomponent": "ui", - "comment": "", - "short_desc": "Localization issue in file: %s" % theoofile.filename, - "component": "l10n", - "form_name": "enter_issue", - } + bug_url = "http://qa.openoffice.org/issues/enter_bug.cgi?{}".format( + parse.urlencode( + { + "subcomponent": "ui", + "comment": "", + "short_desc": f"Localization issue in file: {theoofile.filename}", + "component": "l10n", + "form_name": "enter_issue", + } + ) ) targetheader = thetargetfile.init_headers( x_accelerator_marker="~", x_merge_on="location", report_msgid_bugs_to=bug_url, ) - targetheader.addnote("extracted from %s" % theoofile.filename, "developer") + targetheader.addnote(f"extracted from {theoofile.filename}", "developer") thetargetfile.setsourcelanguage(self.sourcelanguage) thetargetfile.settargetlanguage(self.targetlanguage) # go through the oo and convert each element diff --git a/translate/convert/php2po.py b/translate/convert/php2po.py index 5f0172af42..84e61aedb6 100644 --- a/translate/convert/php2po.py +++ b/translate/convert/php2po.py @@ -66,7 +66,7 @@ def convert_unit(self, unit): def convert_store(self): """Convert a single source format file to a target format file.""" - self.extraction_msg = "extracted from %s" % self.source_store.filename + self.extraction_msg = f"extracted from {self.source_store.filename}" for source_unit in self.source_store.units: self.target_store.addunit(self.convert_unit(source_unit)) diff --git a/translate/convert/po2dtd.py b/translate/convert/po2dtd.py index 731b1f0f9d..d2dbd9d68d 100644 --- a/translate/convert/po2dtd.py +++ b/translate/convert/po2dtd.py @@ -31,7 +31,7 @@ def dtdwarning(message, category, filename, lineno, line=None): - return "Warning: %s\n" % message + return f"Warning: {message}\n" warnings.formatwarning = dtdwarning @@ -56,7 +56,7 @@ def applytranslation(entity, dtdunit, inputunit, mixedentities): if entity in mixedentities: label, unquotedstr = accesskey.extract(unquotedstr) if not unquotedstr: - warnings.warn("Could not find accesskey for %s" % entity) + warnings.warn(f"Could not find accesskey for {entity}") # Use the source language accesskey label, unquotedstr = accesskey.extract(inputunit.source) else: diff --git a/translate/convert/po2prop.py b/translate/convert/po2prop.py index 2328cff092..e6ab4da126 100644 --- a/translate/convert/po2prop.py +++ b/translate/convert/po2prop.py @@ -46,7 +46,7 @@ def applytranslation(key, propunit, inunit, mixedkeys): if key.endswith(akeysuffix) and key in mixedkeys: label, value = accesskey.extract(value) if not value: - warnings.warn("Could not find accesskey for %s" % key) + warnings.warn(f"Could not find accesskey for {key}") # Use the source language accesskey label, value = accesskey.extract(inunit.source) else: @@ -166,7 +166,7 @@ def _explode_gwt_plurals(self): if category != "": new_location = f"{location}[{category}]" else: - new_location = "%s" % (location) + new_location = f"{location}" new_unit.addlocation(new_location) new_unit.target = text self.inputstore.locationindex[new_location] = new_unit @@ -191,7 +191,7 @@ def convertline(self, line): self.inmultilinemsgid = True if delimiter_pos == -1: key = self.personality.key_strip(line) - delimiter = " %s " % self.personality.delimiters[0] + delimiter = f" {self.personality.delimiters[0]} " else: key = self.personality.key_strip(line[:delimiter_pos]) # Calculate space around the equal sign diff --git a/translate/convert/po2rc.py b/translate/convert/po2rc.py index 0ea91ef27f..b1145a055b 100644 --- a/translate/convert/po2rc.py +++ b/translate/convert/po2rc.py @@ -390,7 +390,7 @@ def main(argv=None): "--charset", dest="charset", default=defaultcharset, - help="charset to use to decode the RC files (default: %s)" % defaultcharset, + help=f"charset to use to decode the RC files (default: {defaultcharset})", metavar="CHARSET", ) parser.add_option( @@ -402,7 +402,7 @@ def main(argv=None): "--sublang", dest="sublang", default=defaultsublang, - help="SUBLANG entry (default: %s)" % defaultsublang, + help=f"SUBLANG entry (default: {defaultsublang})", metavar="SUBLANG", ) parser.passthrough.append("charset") diff --git a/translate/convert/po2symb.py b/translate/convert/po2symb.py index 21bce69d69..e2211c21a3 100644 --- a/translate/convert/po2symb.py +++ b/translate/convert/po2symb.py @@ -41,7 +41,7 @@ def escape(text): for key, val in po_escape_map.items(): text = text.replace(key, val) - return '"%s"' % text + return f'"{text}"' def replace_header_items(ps, replacments): @@ -52,7 +52,7 @@ def replace_header_items(ps, replacments): key = match.groupdict()["key"] if key in replacments: ps.current_line = match.expand( - "\\g\\g%s\n" % replacments[key] + f"\\g\\g{replacments[key]}\n" ) ps.read_line() @@ -71,7 +71,7 @@ def parse(ps, header_replacements, body_replacements): body_replacements[key].target or body_replacements[key].source ) ps.current_line = match.expand( - "\\g\\g\\g%s\n" % escape(value) + f"\\g\\g\\g{escape(value)}\n" ) ps.read_line() except StopIteration: diff --git a/translate/convert/pot2po.py b/translate/convert/pot2po.py index 36ef415f23..dbac7a7887 100644 --- a/translate/convert/pot2po.py +++ b/translate/convert/pot2po.py @@ -134,7 +134,7 @@ def convert_stores( def _prepare_merge(input_store, output_store, template_store, **kwargs): """Prepare stores & TM matchers before merging.""" # Dispatch to format specific functions - prepare_merge_hook = "_prepare_merge_%s" % input_store.__class__.__name__ + prepare_merge_hook = f"_prepare_merge_{input_store.__class__.__name__}" if prepare_merge_hook in globals(): globals()[prepare_merge_hook]( input_store, output_store, template_store, **kwargs @@ -159,7 +159,7 @@ def _store_pre_merge(input_store, output_store, template_store, **kwargs): output_store.header = input_store.header # Dispatch to format specific functions - store_pre_merge_hook = "_store_pre_merge_%s" % input_store.__class__.__name__ + store_pre_merge_hook = f"_store_pre_merge_{input_store.__class__.__name__}" if store_pre_merge_hook in globals(): globals()[store_pre_merge_hook]( input_store, output_store, template_store, **kwargs @@ -172,7 +172,7 @@ def _store_post_merge(input_store, output_store, template_store, **kwargs): obsolete messages and similar wrapup tasks. """ # Dispatch to format specific functions - store_post_merge_hook = "_store_post_merge_%s" % input_store.__class__.__name__ + store_post_merge_hook = f"_store_post_merge_{input_store.__class__.__name__}" if store_post_merge_hook in globals(): globals()[store_post_merge_hook]( input_store, output_store, template_store, **kwargs @@ -185,7 +185,7 @@ def _unit_post_merge(input_unit, input_store, output_store, template_store, **kw function. """ # dispatch to format specific functions - unit_post_merge_hook = "_unit_post_merge_%s" % input_unit.__class__.__name__ + unit_post_merge_hook = f"_unit_post_merge_{input_unit.__class__.__name__}" if unit_post_merge_hook in globals(): globals()[unit_post_merge_hook]( input_unit, input_store, output_store, template_store, **kwargs diff --git a/translate/convert/prop2po.py b/translate/convert/prop2po.py index 8f616df36a..a47bbea376 100644 --- a/translate/convert/prop2po.py +++ b/translate/convert/prop2po.py @@ -55,7 +55,7 @@ def convertstore(self, thepropfile): ) else: targetheader = thetargetfile.header() - targetheader.addnote("extracted from %s" % thepropfile.filename, "developer") + targetheader.addnote(f"extracted from {thepropfile.filename}", "developer") thepropfile.makeindex() self.mixedkeys = self.mixer.match_entities(thepropfile.id_index) @@ -228,7 +228,7 @@ def __init__(self, unit): # Some sanity checks if not variant: - raise ValueError("Variant invalid: %s" % (old_variant)) + raise ValueError(f"Variant invalid: {old_variant}") if variant in plurals[key].variants: logger.warning( "Override %s[%s]: %s by %s", @@ -245,7 +245,7 @@ def __init__(self, unit): for key, plural in plurals.items(): # We should have at least "other" (no variant in GWT) if "other" not in plural.variants: - raise ValueError("Should have property %s without any variant" % (key)) + raise ValueError(f"Should have property {key} without any variant") units = [] for name in names: if name in plural.variants: diff --git a/translate/convert/rc2po.py b/translate/convert/rc2po.py index 8f065c0092..467e2192f8 100644 --- a/translate/convert/rc2po.py +++ b/translate/convert/rc2po.py @@ -40,7 +40,7 @@ def convert_store(self, input_store, duplicatestyle="msgctxt"): x_accelerator_marker="&", x_merge_on="location", ) - output_header.addnote("extracted from %s" % input_store.filename, "developer") + output_header.addnote(f"extracted from {input_store.filename}", "developer") for input_unit in input_store.units: output_unit = self.convert_unit(input_unit, "developer") if output_unit is not None: @@ -153,7 +153,7 @@ def main(argv=None): "--lang", dest="lang", default=DEFAULTLANG, - help="LANG entry (default: %s)" % DEFAULTLANG, + help=f"LANG entry (default: {DEFAULTLANG})", metavar="LANG", ) DEFAULTSUBLANG = None @@ -162,7 +162,7 @@ def main(argv=None): "--sublang", dest="sublang", default=DEFAULTSUBLANG, - help="SUBLANG entry (default: %s)" % DEFAULTSUBLANG, + help=f"SUBLANG entry (default: {DEFAULTSUBLANG})", metavar="SUBLANG", ) parser.add_duplicates_option() diff --git a/translate/convert/resx2po.py b/translate/convert/resx2po.py index 8d42b47367..fa0ce81709 100644 --- a/translate/convert/resx2po.py +++ b/translate/convert/resx2po.py @@ -40,7 +40,7 @@ def convert_store(self, input_store, duplicatestyle="msgctxt"): output_header = output_store.init_headers( charset="UTF-8", encoding="8bit", x_accelerator_marker="&" ) - output_header.addnote("extracted from %s" % input_store.filename, "developer") + output_header.addnote(f"extracted from {input_store.filename}", "developer") for input_unit in input_store.units: if input_unit.istranslatable(): output_unit = self.convert_unit(input_unit, "developer") diff --git a/translate/convert/sub2po.py b/translate/convert/sub2po.py index 2157e88ce9..bd81a37402 100644 --- a/translate/convert/sub2po.py +++ b/translate/convert/sub2po.py @@ -34,7 +34,7 @@ def convert_store(input_store, duplicatestyle="msgctxt"): """Converts a subtitle file to a .po file...""" output_store = po.pofile() output_header = output_store.header() - output_header.addnote("extracted from %s" % input_store.filename, "developer") + output_header.addnote(f"extracted from {input_store.filename}", "developer") for input_unit in input_store.units: output_unit = convert_unit(input_unit, "developer") diff --git a/translate/convert/tbx2po.py b/translate/convert/tbx2po.py index ee55dfad48..ecac4a2f62 100644 --- a/translate/convert/tbx2po.py +++ b/translate/convert/tbx2po.py @@ -43,7 +43,9 @@ def convertfile(self, tbxfile): term.source = tbxunit.source term.target = tbxunit.target term.setcontext(tbxunit.getnotes("definition")) - term.addnote("Part of speech: %s" % tbxunit.getnotes("pos"), "developer") + term.addnote( + "Part of speech: {}".format(tbxunit.getnotes("pos")), "developer" + ) self.pofile.addunit(term) self.pofile.removeduplicates() return self.pofile diff --git a/translate/convert/txt2po.py b/translate/convert/txt2po.py index 22c84999ef..85e12cdf79 100644 --- a/translate/convert/txt2po.py +++ b/translate/convert/txt2po.py @@ -68,7 +68,7 @@ def __init__( def convert_store(self): """Convert a single source format file to a target format file.""" - self.extraction_msg = "extracted from %s" % self.source_store.filename + self.extraction_msg = f"extracted from {self.source_store.filename}" for source_unit in self.source_store.units: target_unit = self.target_store.addsourceunit(source_unit.source) diff --git a/translate/convert/yaml2po.py b/translate/convert/yaml2po.py index 0e260b294a..c5a985e07c 100644 --- a/translate/convert/yaml2po.py +++ b/translate/convert/yaml2po.py @@ -66,7 +66,7 @@ def convert_unit(self, unit): def convert_store(self): """Convert a single source format file to a target format file.""" - self.extraction_msg = "extracted from %s" % self.source_store.filename + self.extraction_msg = f"extracted from {self.source_store.filename}" for source_unit in self.source_store.units: self.target_store.addunit(self.convert_unit(source_unit)) diff --git a/translate/filters/checks.py b/translate/filters/checks.py index 68d4109011..c41a09d265 100644 --- a/translate/filters/checks.py +++ b/translate/filters/checks.py @@ -803,8 +803,8 @@ def escapes(self, str1, str2): in the original string you also have them in the translation. """ if not helpers.countsmatch(str1, str2, ("\\", "\\\\")): - escapes1 = ", ".join("'%s'" % word for word in str1.split() if "\\" in word) - escapes2 = ", ".join("'%s'" % word for word in str2.split() if "\\" in word) + escapes1 = ", ".join(f"'{word}'" for word in str1.split() if "\\" in word) + escapes2 = ", ".join(f"'{word}'" for word in str2.split() if "\\" in word) raise SeriousFilterFailure( f"Escapes in original ({escapes1}) don't match " @@ -1051,12 +1051,10 @@ def printf(self, str1, str2): gotmatch = True if str1ord is None: - raise FilterFailure("Added printf variable: %s" % match2.group()) + raise FilterFailure(f"Added printf variable: {match2.group()}") if not gotmatch: - raise FilterFailure( - "Different printf variable: %s" % match2.group() - ) + raise FilterFailure(f"Different printf variable: {match2.group()}") elif str2key: str1key = None @@ -1077,11 +1075,11 @@ def printf(self, str1, str2): if str1fullvar != str2fullvar: raise FilterFailure( - "Different printf variable: %s" % match2.group() + f"Different printf variable: {match2.group()}" ) if str1key is None: - raise FilterFailure("Added printf variable: %s" % match2.group()) + raise FilterFailure(f"Added printf variable: {match2.group()}") else: for var_num1, match1 in enumerate(printf_pat.finditer(str1)): count1 = var_num1 + 1 @@ -1097,7 +1095,7 @@ def printf(self, str1, str2): if (var_num1 == var_num2) and (str1fullvar != str2fullvar): raise FilterFailure( - "Different printf variable: %s" % match2.group() + f"Different printf variable: {match2.group()}" ) if count2 is None: @@ -1105,7 +1103,7 @@ def printf(self, str1, str2): if str1_variables: raise FilterFailure( - "Missing printf variable: %s" % ", ".join(str1_variables) + "Missing printf variable: {}".format(", ".join(str1_variables)) ) if (count1 or count2) and (count1 != count2): @@ -1189,14 +1187,18 @@ def max_anons(anons): if len(extra_in_2) > 0: failure_state = max(failure_state, STATE_SERIOUS) messages.append( - "Unknown named placeholders in translation: %s" % ", ".join(extra_in_2) + "Unknown named placeholders in translation: {}".format( + ", ".join(extra_in_2) + ) ) extra_in_1 = set(data1["namedvars"]).difference(set(data2["namedvars"])) if len(extra_in_1) > 0: failure_state = max(failure_state, STATE_MILD) messages.append( - "Named placeholders absent in translation: %s" % ", ".join(extra_in_1) + "Named placeholders absent in translation: {}".format( + ", ".join(extra_in_1) + ) ) if failure_state == STATE_OK: @@ -1250,12 +1252,12 @@ def accelerators(self, str1, str2): f"accelerator character '{bad2[0]}'" ) else: - messages.append("Missing accelerator '%s'" % accelmarker) + messages.append(f"Missing accelerator '{accelmarker}'") elif count1 == 0: - messages.append("Added accelerator '%s'" % accelmarker) + messages.append(f"Added accelerator '{accelmarker}'") elif count1 == 1 and count2 > count1: messages.append( - "Accelerator '%s' is repeated in translation" % accelmarker + f"Accelerator '{accelmarker}' is repeated in translation" ) else: messages.append( @@ -1335,9 +1337,9 @@ def variables(self, str1, str2): mismatch2.extend(vars2) if mismatch1: - messages.append("Do not translate: %s" % ", ".join(mismatch1)) + messages.append("Do not translate: {}".format(", ".join(mismatch1))) elif mismatch2: - messages.append("Added variables: %s" % ", ".join(mismatch2)) + messages.append("Added variables: {}".format(", ".join(mismatch2))) if messages and mismatch1: raise SeriousFilterFailure(messages) @@ -1541,15 +1543,15 @@ def brackets(self, str1, str2): count2 = str2.count(bracket) if count2 < count1: - missing.append("'%s'" % bracket) + missing.append(f"'{bracket}'") elif count2 > count1: - extra.append("'%s'" % bracket) + extra.append(f"'{bracket}'") if missing: - messages.append("Missing %s" % ", ".join(missing)) + messages.append("Missing {}".format(", ".join(missing))) if extra: - messages.append("Added %s" % ", ".join(extra)) + messages.append("Added {}".format(", ".join(extra))) if messages: raise FilterFailure(messages) @@ -1601,7 +1603,7 @@ def options(self, str1, str2): parts = word1.split("=") if parts[0] not in str2: - raise FilterFailure("Missing or translated option '%s'" % parts[0]) + raise FilterFailure(f"Missing or translated option '{parts[0]}'") if len(parts) > 1 and parts[1] in str2: raise FilterFailure( @@ -1662,7 +1664,7 @@ def simplecaps(self, str1, str2): str2 = self.removevariables(str2) # TODO: review this. The 'I' is specific to English, so it probably # serves no purpose to get sourcelang.sentenceend - str1 = re.sub("[^%s]( I )" % self.config.sourcelang.sentenceend, " i ", str1) + str1 = re.sub(f"[^{self.config.sourcelang.sentenceend}]( I )", " i ", str1) capitals1 = helpers.filtercount(str1, str.isupper) capitals2 = helpers.filtercount(str2, str.isupper) @@ -1726,7 +1728,7 @@ def acronyms(self, str1, str2): if acronyms: raise FilterFailure( - "Consider not translating acronyms: %s" % ", ".join(acronyms) + "Consider not translating acronyms: {}".format(", ".join(acronyms)) ) return True @@ -1755,7 +1757,7 @@ def doublewords(self, str1, str2): for word in words: if word == lastword and word not in self.config.lang.validdoublewords: - raise FilterFailure("The word '%s' is repeated" % word) + raise FilterFailure(f"The word '{word}' is repeated") lastword = word return True @@ -1792,7 +1794,7 @@ def notranslatewords(self, str1, str2): ] if stopwords: - raise FilterFailure("Do not translate: %s" % (", ".join(stopwords))) + raise FilterFailure("Do not translate: {}".format(", ".join(stopwords))) return True @@ -1829,7 +1831,7 @@ def musttranslatewords(self, str1, str2): ] if stopwords: - raise FilterFailure("Please translate: %s" % (", ".join(stopwords))) + raise FilterFailure("Please translate: {}".format(", ".join(stopwords))) return True @@ -1864,7 +1866,9 @@ def validchars(self, str1, str2): ] if invalidchars: - raise FilterFailure("Invalid characters: %s" % (", ".join(invalidchars))) + raise FilterFailure( + "Invalid characters: {}".format(", ".join(invalidchars)) + ) return True @@ -2051,7 +2055,7 @@ def spellcheck(self, str1, str2): errors.difference_update(ignore1, self.config.notranslatewords) if errors: - messages = ["Check the spelling of: %s" % ", ".join(errors)] + messages = ["Check the spelling of: {}".format(", ".join(errors))] raise FilterFailure(messages) return True @@ -2319,12 +2323,10 @@ def validxml(self, str1, str2): if acttag.startswith(""): if match.group("tag") not in lo_emptytags: raise FilterFailure( - "»%s« should not be self-closing/empty" % acttag + f"»{acttag}« should not be self-closing/empty" ) else: opentags.append(acttag) str2 = str2[match.end(0) :] match = re.search(lo_tag_re, str2) if len(opentags) != 0: - raise FilterFailure( - "There is no close tag for »%s«" % opentags.pop() - ) + raise FilterFailure(f"There is no close tag for »{opentags.pop()}«") return True @critical @@ -2464,7 +2464,7 @@ def dialogsizes(self, str1, str2): if pair1[1] not in pair2: # key raise FilterFailure( - "Do not translate the key '%s'" % pair1[1] + f"Do not translate the key '{pair1[1]}'" ) # FIXME we could check more carefully for numbers in pair1[2] @@ -2528,8 +2528,8 @@ def accelerators(self, str1, str2): ) if counter2(str2)[0] > 0: messages.append( - "Accelerator '%s' should not appear in " - "translation" % accelmarker + f"Accelerator '{accelmarker}' should not appear in " + "translation" ) if messages: @@ -2599,7 +2599,9 @@ def gconf(self, str1, str2): if stopwords: raise FilterFailure( - "Do not translate GConf attributes: %s" % (", ".join(stopwords)) + "Do not translate GConf attributes: {}".format( + ", ".join(stopwords) + ) ) return True diff --git a/translate/filters/pofilter.py b/translate/filters/pofilter.py index 781c472446..7169b47ebd 100644 --- a/translate/filters/pofilter.py +++ b/translate/filters/pofilter.py @@ -185,7 +185,7 @@ def build_checkerconfig(self, options): if not os.path.exists(options.notranslatefile): self.error( - "notranslatefile %r does not exist" % options.notranslatefile + f"notranslatefile {options.notranslatefile!r} does not exist" ) with open(options.notranslatefile) as fp: @@ -199,7 +199,7 @@ def build_checkerconfig(self, options): if not os.path.exists(options.musttranslatefile): self.error( - "musttranslatefile %r does not exist" % options.musttranslatefile + f"musttranslatefile {options.musttranslatefile!r} does not exist" ) with open(options.musttranslatefile) as fp: @@ -212,7 +212,7 @@ def build_checkerconfig(self, options): options.validcharsfile = os.path.expanduser(options.validcharsfile) if not os.path.exists(options.validcharsfile): - self.error("validcharsfile %r does not exist" % options.validcharsfile) + self.error(f"validcharsfile {options.validcharsfile!r} does not exist") with open(options.validcharsfile) as fp: validchars = fp.read() diff --git a/translate/lang/af.py b/translate/lang/af.py index a72a2d78de..500d85af79 100644 --- a/translate/lang/af.py +++ b/translate/lang/af.py @@ -39,15 +39,14 @@ class af(common.Common): ) sentenceend = ".!?…" sentencere = re.compile( - r""" + rf""" (?s) # make . also match newlines .*? # anything, but match non-greedy - [%s] # the puntuation for sentence ending + [{sentenceend}] # the puntuation for sentence ending \s+ # the spacing after the puntuation (?='n\s[A-Z]|[^'a-z\d]|'[^n]) # lookahead that next part starts with caps or 'n followed by caps - """ - % sentenceend, + """, re.VERBOSE, ) diff --git a/translate/lang/am.py b/translate/lang/am.py index 3da29496d8..66125b6131 100644 --- a/translate/lang/am.py +++ b/translate/lang/am.py @@ -35,12 +35,11 @@ class am(common.Common): sentenceend = "።!?…" sentencere = re.compile( - r"""(?s) #make . also match newlines + rf"""(?s) #make . also match newlines .*? #anything, but match non-greedy - [%s] #the puntuation for sentence ending + [{sentenceend}] #the puntuation for sentence ending \s* #optional spacing after the puntuation - """ - % sentenceend, + """, re.VERBOSE, ) diff --git a/translate/lang/ar.py b/translate/lang/ar.py index 83f5636b68..7413b36e66 100644 --- a/translate/lang/ar.py +++ b/translate/lang/ar.py @@ -29,7 +29,7 @@ def reverse_quotes(text): def convertquotation(match): - return "”%s“" % match.group(1) + return f"”{match.group(1)}“" return re.sub("“([^”]+)”", convertquotation, text) diff --git a/translate/lang/bn.py b/translate/lang/bn.py index 87ceaf6e74..7970be5190 100644 --- a/translate/lang/bn.py +++ b/translate/lang/bn.py @@ -33,13 +33,12 @@ class bn(common.Common): sentenceend = "।!?…" sentencere = re.compile( - r"""(?s) #make . also match newlines + rf"""(?s) #make . also match newlines .*? #anything, but match non-greedy - [%s] #the puntuation for sentence ending + [{sentenceend}] #the puntuation for sentence ending \s+ #the spacing after the puntuation (?=[^a-z\d])#lookahead that next part starts with caps - """ - % sentenceend, + """, re.VERBOSE, ) diff --git a/translate/lang/code_or.py b/translate/lang/code_or.py index 14e9ad4f1d..ed7069243e 100644 --- a/translate/lang/code_or.py +++ b/translate/lang/code_or.py @@ -33,13 +33,12 @@ class code_or(common.Common): sentenceend = "।!?…" sentencere = re.compile( - r"""(?s) #make . also match newlines + rf"""(?s) #make . also match newlines .*? #anything, but match non-greedy - [%s] #the puntuation for sentence ending + [{sentenceend}] #the puntuation for sentence ending \s+ #the spacing after the puntuation (?=[^a-z\d])#lookahead that next part starts with caps - """ - % sentenceend, + """, re.VERBOSE, ) diff --git a/translate/lang/common.py b/translate/lang/common.py index 2e0df54e4e..79b801b281 100644 --- a/translate/lang/common.py +++ b/translate/lang/common.py @@ -168,14 +168,13 @@ class Common: # example, by checking that the following sentence doesn't start with lower # case or numbers. sentencere = re.compile( - r""" + rf""" (?s) # make . also match newlines .*? # anything, but match non-greedy - [%s] # the puntuation for sentence ending + [{sentenceend}] # the puntuation for sentence ending \s+ # the spacing after the puntuation (?=[^a-zа-џ\d]) # lookahead that next part starts with caps - """ - % sentenceend, + """, re.VERBOSE | re.UNICODE, ) @@ -248,8 +247,8 @@ def __repr__(self): """ detail = "" if self.code: - detail = "(%s)" % self.code - return "" % detail + detail = f"({self.code})" + return f"" @classmethod def numbertranslate(cls, text): diff --git a/translate/lang/el.py b/translate/lang/el.py index 9cb5f63654..4f31fb8c20 100644 --- a/translate/lang/el.py +++ b/translate/lang/el.py @@ -34,14 +34,13 @@ class el(common.Common): sentenceend = ".!;…" sentencere = re.compile( - r""" + rf""" (?s) # make . also match newlines .*? # anything, but match non-greedy - [%s] # the puntuation for sentence ending + [{sentenceend}] # the puntuation for sentence ending \s+ # the spacing after the puntuation (?=[^a-zά-ώ\d]) # lookahead that next part starts with caps - """ - % sentenceend, + """, re.VERBOSE | re.UNICODE, ) diff --git a/translate/lang/factory.py b/translate/lang/factory.py index dfa558afb1..525a42d03d 100644 --- a/translate/lang/factory.py +++ b/translate/lang/factory.py @@ -40,7 +40,7 @@ def getlanguage(code): if code is None: raise ImportError("Can't determine language code") internal_code = prefix + code if code in ("or", "is", "as") else code - module = import_module("translate.lang.%s" % internal_code) + module = import_module(f"translate.lang.{internal_code}") langclass = getattr(module, internal_code) return langclass(code) except ImportError: diff --git a/translate/lang/fr.py b/translate/lang/fr.py index 46be96f79d..8ae65cc9d8 100644 --- a/translate/lang/fr.py +++ b/translate/lang/fr.py @@ -59,7 +59,7 @@ class fr(common.Common): # part punctuation marks and symbols, including : ; « » ! ? % $ # etc. puncdict = {} for c in ":;!?#": - puncdict[c] = "\u00a0%s" % c + puncdict[c] = f"\u00a0{c}" # TODO: consider adding % and $, but think about the consequences of how # they could be part of variables diff --git a/translate/lang/hy.py b/translate/lang/hy.py index 029e6b4fdd..5f707242d0 100644 --- a/translate/lang/hy.py +++ b/translate/lang/hy.py @@ -37,14 +37,13 @@ class hy(common.Common): sentenceend = "։՝՜…" sentencere = re.compile( - r""" + rf""" (?s) # make . also match newlines .*? # anything, but match non-greedy - [%s] # the puntuation for sentence ending + [{sentenceend}] # the puntuation for sentence ending \s+ # the spacing after the puntuation (?=[^a-zա-ֆ\d]) # lookahead that next part starts with caps - """ - % sentenceend, + """, re.VERBOSE | re.UNICODE, ) diff --git a/translate/lang/identify.py b/translate/lang/identify.py index 3fe00f1076..dcca71cd7f 100644 --- a/translate/lang/identify.py +++ b/translate/lang/identify.py @@ -41,13 +41,13 @@ def __init__(self, model_dir=None, conf_file=None): if model_dir is None: model_dir = self.MODEL_DIR if not path.isdir(model_dir): - raise ValueError("Directory does not exist: %s" % (model_dir)) + raise ValueError(f"Directory does not exist: {model_dir}") if conf_file is None: conf_file = self.CONF_FILE conf_file = path.abspath(path.join(model_dir, conf_file)) if not path.isfile(conf_file): - raise ValueError("File does not exist: %s" % (conf_file)) + raise ValueError(f"File does not exist: {conf_file}") self._lang_codes = {} self._load_config(conf_file) diff --git a/translate/lang/ja.py b/translate/lang/ja.py index aa019037d5..abf350449a 100644 --- a/translate/lang/ja.py +++ b/translate/lang/ja.py @@ -37,12 +37,11 @@ class ja(common.Common): # Compared to common.py, we make the space after the sentence ending # optional and don't demand an uppercase letter to follow. sentencere = re.compile( - r"""(?s) #make . also match newlines + rf"""(?s) #make . also match newlines .*? #any text, but match non-greedy - [%s] #the puntuation for sentence ending + [{sentenceend}] #the puntuation for sentence ending \s* #the optional space after the puntuation - """ - % sentenceend, + """, re.VERBOSE, ) diff --git a/translate/lang/km.py b/translate/lang/km.py index 21feb5ed00..e1f213fec7 100644 --- a/translate/lang/km.py +++ b/translate/lang/km.py @@ -38,13 +38,12 @@ class km(common.Common): sentenceend = "!?…។៕៘" sentencere = re.compile( - r"""(?s) #make . also match newlines + rf"""(?s) #make . also match newlines .*? #anything, but match non-greedy - [%s] #the puntuation for sentence ending + [{sentenceend}] #the puntuation for sentence ending \s+ #the spacing after the puntuation (?=[^a-z\d])#lookahead that next part starts with caps - """ - % sentenceend, + """, re.VERBOSE, ) # \u00a0 is non-breaking space diff --git a/translate/lang/ne.py b/translate/lang/ne.py index a8345c4058..a208bf008f 100644 --- a/translate/lang/ne.py +++ b/translate/lang/ne.py @@ -33,14 +33,13 @@ class ne(common.Common): sentenceend = "।!?…" sentencere = re.compile( - r"""(?s) #make . also match newlines + rf"""(?s) #make . also match newlines .*? #anything, but match non-greedy \s? #the single space before the punctuation - [%s] #the puntuation for sentence ending + [{sentenceend}] #the puntuation for sentence ending \s+ #the spacing after the puntuation (?=[^a-z\d])#lookahead that next part starts with caps - """ - % sentenceend, + """, re.VERBOSE, ) diff --git a/translate/lang/ngram.py b/translate/lang/ngram.py index 7393bbb51b..93bce5c244 100644 --- a/translate/lang/ngram.py +++ b/translate/lang/ngram.py @@ -49,7 +49,7 @@ def addText(self, text): ngrams = {} for word in white_space_re.split(text): - word = "_%s_" % word + word = f"_{word}_" size = len(word) for i in range(size - 1): for s in (1, 2, 3, 4): diff --git a/translate/lang/nqo.py b/translate/lang/nqo.py index 42ce9e200c..d163ebced7 100644 --- a/translate/lang/nqo.py +++ b/translate/lang/nqo.py @@ -29,7 +29,7 @@ def reverse_quotes(text): def convertquotation(match): - return "”%s“" % match.group(1) + return f"”{match.group(1)}“" return re.sub("“([^”]+)”", convertquotation, text) diff --git a/translate/lang/pa.py b/translate/lang/pa.py index fc9ae04ede..51a416ecb2 100644 --- a/translate/lang/pa.py +++ b/translate/lang/pa.py @@ -33,14 +33,13 @@ class pa(common.Common): sentenceend = "।!?…" sentencere = re.compile( - r"""(?s) # make . also match newlines + rf"""(?s) # make . also match newlines .*? # anything, but match non-greedy - [%s] # the puntuation for sentence ending + [{sentenceend}] # the puntuation for sentence ending \s+ # the spacing after the puntuation (?=[^a-z\d])# lookahead that next part starts with # caps - """ - % sentenceend, + """, re.VERBOSE, ) diff --git a/translate/lang/scn.py b/translate/lang/scn.py index 4f53d1d089..bf3600e5cc 100644 --- a/translate/lang/scn.py +++ b/translate/lang/scn.py @@ -93,7 +93,7 @@ def italianisms(self, str1, str2): ] if stopwords: - raise FilterFailure("Please translate: %s" % (", ".join(stopwords))) + raise FilterFailure("Please translate: {}".format(", ".join(stopwords))) return True @@ -130,7 +130,9 @@ def vocalism(self, str1, str2): stopwords.append(lower_word) if stopwords: - raise FilterFailure("Please respect vocalism: %s" % (", ".join(stopwords))) + raise FilterFailure( + "Please respect vocalism: {}".format(", ".join(stopwords)) + ) return True @critical @@ -155,7 +157,7 @@ def suffixes(self, str1, str2): if stopwords: raise FilterFailure( - "Please use the correct word endings: %s" % (", ".join(stopwords)) + "Please use the correct word endings: {}".format(", ".join(stopwords)) ) return True diff --git a/translate/lang/vi.py b/translate/lang/vi.py index 99ebccf467..015e4e1ab2 100644 --- a/translate/lang/vi.py +++ b/translate/lang/vi.py @@ -32,7 +32,7 @@ class vi(common.Common): # French, but does not use a space before '?'. puncdict = {} for c in ":;!#": - puncdict[c] = " %s" % c + puncdict[c] = f" {c}" @classmethod def punctranslate(cls, text): diff --git a/translate/lang/zh.py b/translate/lang/zh.py index 0f8663681c..fdcead5788 100644 --- a/translate/lang/zh.py +++ b/translate/lang/zh.py @@ -37,12 +37,11 @@ class zh(common.Common): # Compared to common.py, we make the space after the sentence ending # optional and don't demand an uppercase letter to follow. sentencere = re.compile( - r"""(?s) # make . also match newlines + rf"""(?s) # make . also match newlines .*? # any text, but match non-greedy - [%s] # the puntuation for sentence ending + [{sentenceend}] # the puntuation for sentence ending \s* # the optional space after the puntuation - """ - % sentenceend, + """, re.VERBOSE, ) diff --git a/translate/misc/dictutils.py b/translate/misc/dictutils.py index 5bfbe7b3ae..17703935b9 100644 --- a/translate/misc/dictutils.py +++ b/translate/misc/dictutils.py @@ -30,7 +30,7 @@ def __init__(self, fromdict=None): def __getitem__(self, key): if not isinstance(key, str): raise TypeError( - "cidict can only have str or unicode as key (got %r)" % type(key) + f"cidict can only have str or unicode as key (got {type(key)!r})" ) for akey in self.keys(): if akey.lower() == key.lower(): @@ -40,7 +40,7 @@ def __getitem__(self, key): def __setitem__(self, key, value): if not isinstance(key, str): raise TypeError( - "cidict can only have str or unicode as key (got %r)" % type(key) + f"cidict can only have str or unicode as key (got {type(key)!r})" ) for akey in self.keys(): if akey.lower() == key.lower(): @@ -60,7 +60,7 @@ def update(self, updatedict) -> None: def __delitem__(self, key): if not isinstance(key, str): raise TypeError( - "cidict can only have str or unicode as key (got %r)" % type(key) + f"cidict can only have str or unicode as key (got {type(key)!r})" ) for akey in self.keys(): if akey.lower() == key.lower(): @@ -70,7 +70,7 @@ def __delitem__(self, key): def __contains__(self, key): if not isinstance(key, str): raise TypeError( - "cidict can only have str or unicode as key (got %r)" % type(key) + f"cidict can only have str or unicode as key (got {type(key)!r})" ) for akey in self.keys(): if akey.lower() == key.lower(): diff --git a/translate/misc/file_discovery.py b/translate/misc/file_discovery.py index 8fe4aed8ea..23c944fe9e 100644 --- a/translate/misc/file_discovery.py +++ b/translate/misc/file_discovery.py @@ -77,4 +77,4 @@ def get_abs_data_filename(path_parts, basedirs=None): ) if os.path.exists(datafile): return datafile - raise ValueError('Could not find "%s"' % (os.path.join(*path_parts))) + raise ValueError(f'Could not find "{os.path.join(*path_parts)}"') diff --git a/translate/misc/optrecurse.py b/translate/misc/optrecurse.py index 4128804219..926242920d 100644 --- a/translate/misc/optrecurse.py +++ b/translate/misc/optrecurse.py @@ -83,7 +83,7 @@ def format_option_strings(self, option): """Return a comma-separated list of option strings & metavariables.""" if option.takes_value(): metavar = option.metavar or option.dest.upper() - metavar = "\\fI%s\\fP" % metavar + metavar = f"\\fI{metavar}\\fP" short_opts = [sopt + metavar for sopt in option._short_opts] long_opts = [lopt + "\\fR=\\fP" + metavar for lopt in option._long_opts] else: @@ -92,7 +92,7 @@ def format_option_strings(self, option): opts = short_opts + long_opts if self.short_first else long_opts + short_opts - return "\\fB%s\\fP" % ("\\fR, \\fP".join(opts)) + return "\\fB{}\\fP".format("\\fR, \\fP".join(opts)) class StdoutWrapper: @@ -178,7 +178,7 @@ def formatToolkit(x): usage = "\\fB%prog " usage += " ".join(self.getusageman(option) for option in self.option_list) usage += "\\fP" - result.append("%s\n" % formatprog(usage)) + result.append(f"{formatprog(usage)}\n") description_lines = self.description.split("\n\n")[1:] if description_lines: result.append(".SH DESCRIPTION\n") @@ -192,8 +192,8 @@ def formatToolkit(x): result.append(".PP\n") for option in self.option_list: result.append(".TP\n") - result.append("%s\n" % str(option).replace("-", r"\-")) - result.append("%s\n" % option.help.replace("-", r"\-")) + result.append("{}\n".format(str(option).replace("-", r"\-"))) + result.append("{}\n".format(option.help.replace("-", r"\-"))) return "".join(result) def print_manpage(self, file=None): @@ -238,24 +238,24 @@ def getusagestring(option): """Returns the usage string for the given option.""" optionstring = "|".join(option._short_opts + option._long_opts) if getattr(option, "optionalswitch", False): - optionstring = "[%s]" % optionstring + optionstring = f"[{optionstring}]" if option.metavar: optionstring += " " + option.metavar if getattr(option, "required", False): return optionstring - return "[%s]" % optionstring + return f"[{optionstring}]" @staticmethod def getusageman(option): """Returns the usage string for the given option.""" optionstring = "\\fR|\\fP".join(option._short_opts + option._long_opts) if getattr(option, "optionalswitch", False): - optionstring = "\\fR[\\fP%s\\fR]\\fP" % optionstring + optionstring = f"\\fR[\\fP{optionstring}\\fR]\\fP" if option.metavar: - optionstring += " \\fI%s\\fP" % option.metavar + optionstring += f" \\fI{option.metavar}\\fP" if getattr(option, "required", False): return optionstring - return "\\fR[\\fP%s\\fR]\\fP" % optionstring + return f"\\fR[\\fP{optionstring}\\fR]\\fP" def define_option(self, option): """ @@ -324,7 +324,7 @@ def setformats(self, formats, usetemplates): dest="input", default=None, metavar="INPUT", - help="read from INPUT in %s" % (inputformathelp), + help=f"read from INPUT in {inputformathelp}", ) inputoption.optionalswitch = True inputoption.required = True @@ -347,7 +347,7 @@ def setformats(self, formats, usetemplates): dest="output", default=None, metavar="OUTPUT", - help="write to OUTPUT in %s" % (outputformathelp), + help=f"write to OUTPUT in {outputformathelp}", ) outputoption.optionalswitch = True outputoption.required = True @@ -361,7 +361,7 @@ def setformats(self, formats, usetemplates): dest="template", default=None, metavar="TEMPLATE", - help="read from TEMPLATE in %s" % (templateformathelp), + help=f"read from TEMPLATE in {templateformathelp}", ) self.define_option(templateoption) @@ -374,7 +374,7 @@ def setprogressoptions(self): default="bar", choices=list(ProgressBar.progress_types.keys()), metavar="PROGRESS", - help="show progress as: %s" % (", ".join(ProgressBar.progress_types)), + help="show progress as: {}".format(", ".join(ProgressBar.progress_types)), ) self.define_option(progressoption) @@ -388,7 +388,7 @@ def seterrorleveloptions(self): default="message", choices=self.errorleveltypes, metavar="ERRORLEVEL", - help="show errorlevel as: %s" % (", ".join(self.errorleveltypes)), + help="show errorlevel as: {}".format(", ".join(self.errorleveltypes)), ) self.define_option(errorleveloption) @@ -399,8 +399,8 @@ def getformathelp(formats): if len(formats) == 0: return "" if len(formats) == 1: - return "%s format" % (", ".join(formats)) - return "%s formats" % (", ".join(formats)) + return "{} format".format(", ".join(formats)) + return "{} formats".format(", ".join(formats)) @staticmethod def isrecursive(fileoption, filepurpose="input"): @@ -593,7 +593,7 @@ def recursiveprocess(self, options): self.checkoutputsubdir(options, os.path.dirname(outputpath)) except Exception: self.warning( - "Couldn't handle input file %s" % inputpath, options, sys.exc_info() + f"Couldn't handle input file {inputpath}", options, sys.exc_info() ) continue try: @@ -659,7 +659,7 @@ def opentemplatefile(self, options, fulltemplatepath): if fulltemplatepath is not None: if os.path.isfile(fulltemplatepath): return open(fulltemplatepath, "rb") - self.warning("missing template file %s" % fulltemplatepath) + self.warning(f"missing template file {fulltemplatepath}") return None def processfile( diff --git a/translate/misc/ourdom.py b/translate/misc/ourdom.py index 15ef0fe17d..88f7e1b643 100644 --- a/translate/misc/ourdom.py +++ b/translate/misc/ourdom.py @@ -47,7 +47,7 @@ def writexml_helper(self, writer, indent="", addindent="", newl=""): a_names = sorted(attrs.keys()) for a_name in a_names: - writer.write(' %s="' % a_name) + writer.write(f' {a_name}="') minidom._write_data(writer, attrs[a_name].value) writer.write('"') if self.childNodes: @@ -69,13 +69,13 @@ def writexml_helper(self, writer, indent="", addindent="", newl=""): writer.write(f"{newl}") else: # This is the normal case that we do with pretty layout - writer.write(">%s" % (newl)) + writer.write(f">{newl}") for node in self.childNodes: if node.nodeType != self.TEXT_NODE: node.writexml(writer, (indent + addindent), addindent, newl) writer.write(f"{indent}{newl}") else: - writer.write("/>%s" % (newl)) + writer.write(f"/>{newl}") def getElementsByTagName_helper(parent, name, dummy=None): diff --git a/translate/misc/selector.py b/translate/misc/selector.py index f9cee17920..f2be2bb7b5 100644 --- a/translate/misc/selector.py +++ b/translate/misc/selector.py @@ -357,7 +357,7 @@ def lookup(self, name): else: pattern = self.patterns[self.default_pattern] if name == "": - name = "__pos%s" % self._pos + name = f"__pos{self._pos}" self._pos += 1 return f"(?P<{name}>{pattern})" @@ -368,7 +368,7 @@ def lastly(regex): Adds the ^ and the $ to the beginning and the end, respectively. """ - return "^%s$" % regex + return f"^{regex}$" @staticmethod def openended(regex): @@ -378,7 +378,7 @@ def openended(regex): Adds the ^ to the beginning but no $ to the end. Called as a special alternative to lastly. """ - return "^%s" % regex + return f"^{regex}" def outermost_optionals_split(self, text): """Split out optional portions by outermost matching delims.""" @@ -413,7 +413,7 @@ def parse(self, text): if self.ostart in text: parts = self.outermost_optionals_split(text) parts = map(self.parse, parts) - parts[1::2] = ["(%s)?" % p for p in parts[1::2]] + parts[1::2] = [f"({p})?" for p in parts[1::2]] else: parts = [part.split(self.end) for part in text.split(self.start)] parts = [y for x in parts for y in x] diff --git a/translate/search/lshtein.py b/translate/search/lshtein.py index c8bf8e3150..776ab53716 100644 --- a/translate/search/lshtein.py +++ b/translate/search/lshtein.py @@ -171,4 +171,4 @@ def similarity_real(self, a, b, stoppercentage=40): from sys import argv comparer = LevenshteinComparer() - print("Similarity:\n%s" % comparer.similarity(argv[1], argv[2], 50)) + print(f"Similarity:\n{comparer.similarity(argv[1], argv[2], 50)}") diff --git a/translate/storage/aresource.py b/translate/storage/aresource.py index 58531e28bb..91ff136d3a 100644 --- a/translate/storage/aresource.py +++ b/translate/storage/aresource.py @@ -350,7 +350,7 @@ def escape( if quote_wrapping_whitespaces and ( text[0] in WHITESPACE or text[-1] in WHITESPACE or multispace ): - return '"%s"' % text + return f'"{text}"' # In xml multispace if not quote_wrapping_whitespaces and multispace: return MULTIWHITESPACE.sub(cls.xml_escape_space, text) diff --git a/translate/storage/base.py b/translate/storage/base.py index e73dc97451..ea1bd3baf0 100644 --- a/translate/storage/base.py +++ b/translate/storage/base.py @@ -501,7 +501,7 @@ def get_state_id(self, n=None): if state_range[0] <= n < state_range[1]: return state_id if self.STATE: - raise ValueError("No state containing value %s" % (n)) + raise ValueError(f"No state containing value {n}") return n def get_state_n(self): diff --git a/translate/storage/benchmark.py b/translate/storage/benchmark.py index befe7432e4..c0540171bd 100644 --- a/translate/storage/benchmark.py +++ b/translate/storage/benchmark.py @@ -149,12 +149,11 @@ def parse_placeables(self): if storetype in factory.classes_str: _module, _class = factory.classes_str[storetype] - module = import_module("translate.storage.%s" % _module) + module = import_module(f"translate.storage.{_module}") storeclass = getattr(module, _class) else: print( - "StoreClass: '%s' is not a base class that the class factory can load" - % storetype + f"StoreClass: '{storetype}' is not a base class that the class factory can load" ) sys.exit() diff --git a/translate/storage/bundleprojstore.py b/translate/storage/bundleprojstore.py index bab120d020..0f6be4ab1e 100644 --- a/translate/storage/bundleprojstore.py +++ b/translate/storage/bundleprojstore.py @@ -78,9 +78,9 @@ def append_file(self, afile, fname, ftype="trans", delete_orig=False): from disk if ``delete_orig`` is ``True``. """ if fname and fname in self.zip.namelist(): - raise ValueError("File already in bundle archive: %s" % (fname)) + raise ValueError(f"File already in bundle archive: {fname}") if not fname and isinstance(afile, str) and afile in self.zip.namelist(): - raise ValueError("File already in bundle archive: %s" % (afile)) + raise ValueError(f"File already in bundle archive: {afile}") afile, fname = super().append_file(afile, fname, ftype) self._zip_add(fname, afile) @@ -153,7 +153,7 @@ def get_proj_filename(self, realfname): return fname if realfname in self._tempfiles: return self._tempfiles[realfname] - raise ValueError("Real file not in project store: %s" % (realfname)) + raise ValueError(f"Real file not in project store: {realfname}") def load(self, zipname): """Load the bundle project from the zip file of the given name.""" @@ -257,13 +257,13 @@ def _zip_delete(self, fnames): """Delete the files with the given names from the zip file (``self.zip``).""" # Sanity checking if not isinstance(fnames, (list, tuple)): - raise TypeError("fnames must be list or tuple: %s" % (fnames)) + raise TypeError(f"fnames must be list or tuple: {fnames}") if not self.zip: raise ValueError("No zip file to work on") zippedfiles = self.zip.namelist() for fn in fnames: if fn not in zippedfiles: - raise KeyError("File not in zip archive: %s" % (fn)) + raise KeyError(f"File not in zip archive: {fn}") newzip = self._create_temp_zipfile() newzip.writestr("project.xtp", self._generate_settings()) diff --git a/translate/storage/cpo.py b/translate/storage/cpo.py index f13031257a..c848ca8b83 100644 --- a/translate/storage/cpo.py +++ b/translate/storage/cpo.py @@ -658,7 +658,7 @@ def settypecomment(self, typecomment, present=True): gpo.po_message_set_format(self._gpo_message, gpo_encode(typecomment), present) def hasmarkedcomment(self, commentmarker): - commentmarker = "(%s)" % commentmarker + commentmarker = f"({commentmarker})" for comment in self.getnotes("translator").split("\n"): if comment.startswith(commentmarker): return True diff --git a/translate/storage/dtd.py b/translate/storage/dtd.py index 586605689b..586a1edad9 100644 --- a/translate/storage/dtd.py +++ b/translate/storage/dtd.py @@ -210,7 +210,7 @@ def is_valid_entity_name(name): continue invalid_amps.append(amppos - 1) if len(invalid_amps) > 0: - warnings.warn("invalid ampersands in dtd entity %s" % (name)) + warnings.warn(f"invalid ampersands in dtd entity {name}") for adjustment, amppos in enumerate(invalid_amps): value = value[: amppos - adjustment] + value[amppos - adjustment + 1 :] return value @@ -480,7 +480,7 @@ def parse(self, dtdsrc): ) else: raise ValueError( - "Unexpected quote character... %r" % (self.entityhelp[1]) + f"Unexpected quote character... {self.entityhelp[1]!r}" ) # for any following lines, start at the beginning of the line. remember the quote character self.entityhelp = (0, self.entityhelp[1]) @@ -593,7 +593,7 @@ def serialize(self, out): out.write(unit_str) content += unit_str if not self._valid_store(content): - warnings.warn("DTD file '%s' does not validate" % self.filename) + warnings.warn(f"DTD file '{self.filename}' does not validate") out.truncate(0) def _valid_store(self, content): @@ -612,6 +612,6 @@ def _valid_store(self, content): try: etree.DTD(BytesIO(_input)) except etree.DTDParseError as e: - warnings.warn("DTD parse error: %s" % e.error_log) + warnings.warn(f"DTD parse error: {e.error_log}") return False return True diff --git a/translate/storage/factory.py b/translate/storage/factory.py index c4f5cbd11a..a95504dfed 100644 --- a/translate/storage/factory.py +++ b/translate/storage/factory.py @@ -190,7 +190,7 @@ def getclass( else: storeclass = import_class(*classes_str[ext], "translate.storage") except KeyError: - raise ValueError("Unknown filetype (%s)" % storefilename) + raise ValueError(f"Unknown filetype ({storefilename})") return storeclass diff --git a/translate/storage/fpo.py b/translate/storage/fpo.py index e8ac9e2866..ebd34138d6 100644 --- a/translate/storage/fpo.py +++ b/translate/storage/fpo.py @@ -278,7 +278,7 @@ def hastypecomment(self, typecomment): # check for word boundaries properly by using a regular expression... return ( sum( - len(re.findall("\\b%s\\b" % typecomment, tcline)) + len(re.findall(f"\\b{typecomment}\\b", tcline)) for tcline in self.typecomments ) != 0 @@ -286,18 +286,18 @@ def hastypecomment(self, typecomment): def hasmarkedcomment(self, commentmarker): """Check whether the given comment marker is present as # (commentmarker) ...""" - commentmarker = "(%s)" % commentmarker + commentmarker = f"({commentmarker})" return any(comment.startswith(commentmarker) for comment in self.othercomments) def settypecomment(self, typecomment, present=True): """Alters whether a given typecomment is present.""" if self.hastypecomment(typecomment) != present: if present: - self.typecomments.append("#, %s\n" % typecomment) + self.typecomments.append(f"#, {typecomment}\n") else: # this should handle word boundaries properly ... typecomments = [ - re.sub("\\b%s\\b[ \t,]*" % typecomment, "", tcline) + re.sub(f"\\b{typecomment}\\b[ \t,]*", "", tcline) for tcline in self.typecomments ] self.typecomments = filter( diff --git a/translate/storage/html.py b/translate/storage/html.py index aba618ee93..24b4b25cc8 100644 --- a/translate/storage/html.py +++ b/translate/storage/html.py @@ -415,9 +415,7 @@ def handle_endtag(self, tag): try: popped = self.tag_path.pop() except IndexError: - raise ParseError( - "Mismatched tags: no more tags: line %s" % self.getpos()[0] - ) + raise ParseError(f"Mismatched tags: no more tags: line {self.getpos()[0]}") if popped != tag and popped in self.EMPTY_HTML_ELEMENTS: popped = self.tag_path.pop() if popped != tag: @@ -426,7 +424,7 @@ def handle_endtag(self, tag): f"expected '{popped}' got '{tag}' at line {self.getpos()[0]}" ) - self.append_markup({"type": "endtag", "html_content": "" % tag}) + self.append_markup({"type": "endtag", "html_content": f""}) if tag in self.TRANSLATABLE_ELEMENTS: self.end_translation_unit() @@ -471,27 +469,27 @@ def handle_entityref(self, name): """Handle named entities of the form &aaaa; e.g. ’.""" converted = html5.get(name + ";") if name in ["gt", "lt", "amp"] or not converted: - self.handle_data("&%s;" % name) + self.handle_data(f"&{name};") else: self.handle_data(converted) def handle_comment(self, data): self.auto_close_empty_element() self.append_markup( - {"type": "comment", "html_content": "" % data, "note": data} + {"type": "comment", "html_content": f"", "note": data} ) def handle_decl(self, decl): self.auto_close_empty_element() - self.append_markup({"type": "decl", "html_content": "" % decl}) + self.append_markup({"type": "decl", "html_content": f""}) def handle_pi(self, data): self.auto_close_empty_element() - self.append_markup({"type": "pi", "html_content": "" % data}) + self.append_markup({"type": "pi", "html_content": f""}) def unknown_decl(self, data): self.auto_close_empty_element() - self.append_markup({"type": "cdecl", "html_content": "" % data}) + self.append_markup({"type": "cdecl", "html_content": f""}) class POHTMLParser(htmlfile): diff --git a/translate/storage/ical.py b/translate/storage/ical.py index 02d6912ba4..0eea79c5af 100644 --- a/translate/storage/ical.py +++ b/translate/storage/ical.py @@ -133,5 +133,5 @@ def parse(self, input): "LOCATION", ): newunit = self.addsourceunit(property.value) - newunit.addnote("Start date: %s" % component.dtstart.value) + newunit.addnote(f"Start date: {component.dtstart.value}") newunit.addlocation(f"[{component.uid.value}]{property.name}") diff --git a/translate/storage/lisa.py b/translate/storage/lisa.py index c2893737ca..c6b7796dec 100644 --- a/translate/storage/lisa.py +++ b/translate/storage/lisa.py @@ -309,7 +309,7 @@ def initbody(self): again. """ self.namespace = self.document.getroot().nsmap.get(None, None) - self.body = self.document.find("//%s" % self.namespaced(self.bodyNode)) + self.body = self.document.find(f"//{self.namespaced(self.bodyNode)}") def addsourceunit(self, source): """Adds and returns a new unit with the given string as first entry.""" diff --git a/translate/storage/mo.py b/translate/storage/mo.py index 804f3ca620..10e296934e 100644 --- a/translate/storage/mo.py +++ b/translate/storage/mo.py @@ -266,7 +266,7 @@ def parse(self, input): startvalue, _sizehash, _offsethash, - ) = struct.unpack("%sLHHiiiii" % endian, input[: (7 * 4)]) + ) = struct.unpack(f"{endian}LHHiiiii", input[: (7 * 4)]) if version_maj >= 1: raise base.ParseError( """Unable to process version %d.%d MO files""" @@ -276,10 +276,10 @@ def parse(self, input): nextkey = startkey + (i * 2 * 4) nextvalue = startvalue + (i * 2 * 4) klength, koffset = struct.unpack( - "%sii" % endian, input[nextkey : nextkey + (2 * 4)] + f"{endian}ii", input[nextkey : nextkey + (2 * 4)] ) vlength, voffset = struct.unpack( - "%sii" % endian, input[nextvalue : nextvalue + (2 * 4)] + f"{endian}ii", input[nextvalue : nextvalue + (2 * 4)] ) source = input[koffset : koffset + klength] context = None diff --git a/translate/storage/mozilla_lang.py b/translate/storage/mozilla_lang.py index eaa1415620..e126311e8e 100644 --- a/translate/storage/mozilla_lang.py +++ b/translate/storage/mozilla_lang.py @@ -58,7 +58,7 @@ def __str__(self): if self.getnotes(): notes = (self.eol).join( [ - ("#%s" % note if note.startswith("#") else "# %s" % note) + (f"#{note}" if note.startswith("#") else f"# {note}") for note in self.getnotes("developer").split("\n") ] ) diff --git a/translate/storage/oo.py b/translate/storage/oo.py index e70e928a33..006b33251a 100644 --- a/translate/storage/oo.py +++ b/translate/storage/oo.py @@ -155,7 +155,7 @@ def escape_help_text(text): "embedvar", "alt", ]: - if tag.startswith("<%s" % escape_tag) or tag == "" % escape_tag: + if tag.startswith(f"<{escape_tag}") or tag == f"": escapethistag = True if tag in ["
", ""]: escapethistag = True @@ -426,7 +426,7 @@ def createsubfileindex(self): def getsubfilename(self, line): """Looks up the subfile name for the line.""" if line.count("\t") < 2: - raise ValueError("invalid tab-delimited line: %r" % line) + raise ValueError(f"invalid tab-delimited line: {line!r}") lineparts = line.split("\t", 2) module, filename = lineparts[0], lineparts[1] if self.multifilestyle == "onefile": diff --git a/translate/storage/php.py b/translate/storage/php.py index b1f2c05fe6..f241d8c3c7 100644 --- a/translate/storage/php.py +++ b/translate/storage/php.py @@ -204,7 +204,7 @@ def phpencode(text, quotechar="'"): for a, b in escapes: text = text.replace(a, b) return text - return text.replace("%s" % quotechar, "\\%s" % quotechar) + return text.replace(f"{quotechar}", f"\\{quotechar}") def phpdecode(text, quotechar="'"): diff --git a/translate/storage/placeables/lisa.py b/translate/storage/placeables/lisa.py index ef50c2177c..fe6e695e6d 100644 --- a/translate/storage/placeables/lisa.py +++ b/translate/storage/placeables/lisa.py @@ -209,7 +209,7 @@ def strelem_to_xml(parent_node, elem): def parse_xliff(pstr): parser = etree.XMLParser(resolve_entities=False) - return xml_to_strelem(etree.fromstring("%s" % (pstr), parser)) + return xml_to_strelem(etree.fromstring(f"{pstr}", parser)) xliff.parsers = [parse_xliff] diff --git a/translate/storage/placeables/strelem.py b/translate/storage/placeables/strelem.py index eee51714b7..9616602d87 100644 --- a/translate/storage/placeables/strelem.py +++ b/translate/storage/placeables/strelem.py @@ -82,7 +82,7 @@ def __init__(self, sub=None, id=None, rid=None, xid=None, **kwargs): for key, value in kwargs.items(): if hasattr(self, key): - raise ValueError("attribute already exists: %s" % (key)) + raise ValueError(f"attribute already exists: {key}") setattr(self, key, value) # SPECIAL METHODS # @@ -164,9 +164,9 @@ def __repr__(self): elemstr = ", ".join(repr(elem) for elem in self.sub) return "<%(class)s(%(id)s%(rid)s%(xid)s[%(subs)s])>" % { "class": self.__class__.__name__, - "id": self.id is not None and 'id="%s" ' % (self.id) or "", - "rid": self.rid is not None and 'rid="%s" ' % (self.rid) or "", - "xid": self.xid is not None and 'xid="%s" ' % (self.xid) or "", + "id": self.id is not None and f'id="{self.id}" ' or "", + "rid": self.rid is not None and f'rid="{self.rid}" ' or "", + "xid": self.xid is not None and f'xid="{self.xid}" ' or "", "subs": elemstr, } diff --git a/translate/storage/placeables/xliff.py b/translate/storage/placeables/xliff.py index e93e7cb6c0..c48afdf51e 100644 --- a/translate/storage/placeables/xliff.py +++ b/translate/storage/placeables/xliff.py @@ -109,9 +109,9 @@ def __repr__(self): return "<%(class)s{%(tag)s}(%(id)s%(rid)s%(xid)s[%(subs)s])>" % { "class": self.__class__.__name__, "tag": tag, - "id": self.id is not None and 'id="%s" ' % (self.id) or "", - "rid": self.rid is not None and 'rid="%s" ' % (self.rid) or "", - "xid": self.xid is not None and 'xid="%s" ' % (self.xid) or "", + "id": self.id is not None and f'id="{self.id}" ' or "", + "rid": self.rid is not None and f'rid="{self.rid}" ' or "", + "xid": self.xid is not None and f'xid="{self.xid}" ' or "", "subs": elemstr, } diff --git a/translate/storage/poheader.py b/translate/storage/poheader.py index ac542a20e7..24ee05e4e2 100644 --- a/translate/storage/poheader.py +++ b/translate/storage/poheader.py @@ -107,7 +107,7 @@ class poheader: all classes which represent a po file. """ - x_generator = "Translate Toolkit %s" % __version__.sver + x_generator = f"Translate Toolkit {__version__.sver}" header_order = [ "Project-Id-Version", @@ -192,7 +192,7 @@ def makeheaderdict( defaultargs["Last-Translator"] = last_translator defaultargs["Language-Team"] = language_team defaultargs["MIME-Version"] = mime_version - defaultargs["Content-Type"] = "text/plain; charset=%s" % charset + defaultargs["Content-Type"] = f"text/plain; charset={charset}" defaultargs["Content-Transfer-Encoding"] = encoding if plural_forms: defaultargs["Plural-Forms"] = plural_forms diff --git a/translate/storage/poxliff.py b/translate/storage/poxliff.py index e0b3b500ee..843dba6c00 100644 --- a/translate/storage/poxliff.py +++ b/translate/storage/poxliff.py @@ -355,7 +355,7 @@ def pluralunits(pluralgroups): for entry in singularunits: term = self.UnitClass.createfromxmlElement(entry, namespace=self.namespace) - if nextplural and str(term.getid()) == ("%s[0]" % nextplural.getid()): + if nextplural and str(term.getid()) == (f"{nextplural.getid()}[0]"): self.addunit(nextplural, new=False) nextplural = next(pluralunit_iter, None) else: diff --git a/translate/storage/project.py b/translate/storage/project.py index ac0293d7a7..09b328577d 100644 --- a/translate/storage/project.py +++ b/translate/storage/project.py @@ -113,9 +113,7 @@ def convert_forward(self, input_fname, template=None, output_fname=None, **optio input_type = self.store.get_filename_type(input_fname) if input_type == "tgt": - raise ValueError( - "Cannot convert a target document further: %s" % (input_fname) - ) + raise ValueError(f"Cannot convert a target document further: {input_fname}") templ_fname = None if isinstance(template, str): @@ -185,7 +183,7 @@ def convert_forward(self, input_fname, template=None, output_fname=None, **optio # If the output file already exist, we can't assume that it's safe # to overwrite it. os.unlink(converted_file.name) - raise OSError("Output file already exists: %s" % (output_fname)) + raise OSError(f"Output file already exists: {output_fname}") os.rename(converted_file.name, output_fname) @@ -229,7 +227,7 @@ def get_real_filename(self, projfname): projfile = self.get_file(projfname) rfname = getattr(projfile, "name", getattr(projfile, "filename", None)) if rfname is None: - raise ValueError("Project file has no real file: %s" % (projfname)) + raise ValueError(f"Project file has no real file: {projfname}") return rfname def remove_file(self, projfname, ftype=None): diff --git a/translate/storage/projstore.py b/translate/storage/projstore.py index 312514fc73..a73acebbbf 100644 --- a/translate/storage/projstore.py +++ b/translate/storage/projstore.py @@ -128,7 +128,7 @@ def append_file(self, afile, fname, ftype="trans", delete_orig=False): . Not used in this class. """ if ftype not in self.TYPE_INFO["f_prefix"]: - raise ValueError("Invalid file type: %s" % (ftype)) + raise ValueError(f"Invalid file type: {ftype}") if isinstance(afile, str) and os.path.isfile(afile) and not fname: # Try and use afile as the file name @@ -250,7 +250,7 @@ def get_proj_filename(self, realfname): for fname in self._files: if realfname in (fname, self._files[fname]): return fname - raise ValueError("Real file not in project store: %s" % (realfname)) + raise ValueError(f"Real file not in project store: {realfname}") def load(self, *args, **kwargs): """Load the project in some way. Undefined for this (base) class.""" diff --git a/translate/storage/pypo.py b/translate/storage/pypo.py index 3c4a225466..ab980cb41c 100644 --- a/translate/storage/pypo.py +++ b/translate/storage/pypo.py @@ -187,7 +187,7 @@ def _handle_long_word( def _wrap_chunks(self, chunks: list[str]) -> list[str]: lines = [] if self.width <= 1: - raise ValueError("invalid width %r (must be > 1)" % self.width) + raise ValueError(f"invalid width {self.width!r} (must be > 1)") # Arrange in reverse order so items can be efficiently popped # from a stack of chucks. @@ -237,7 +237,7 @@ def quoteforpo(text, wrapper_obj=None): wrapper_obj = PoWrapper() text = escapeforpo(text) if wrapper_obj.width == -1: - return ['"%s"' % text] + return [f'"{text}"'] lines = text.split("\\n") for i, l in enumerate(lines[:-1]): lines[i] = l + "\\n" @@ -680,7 +680,7 @@ def hasmarkedcomment(self, commentmarker): # (commentmarker) ... """ - commentmarker = "(%s)" % commentmarker + commentmarker = f"({commentmarker})" for comment in self.othercomments: if comment.replace("#", "", 1).strip().startswith(commentmarker): return True @@ -776,7 +776,7 @@ def _getmsgpartstr(self, partname, partlines, partcomments=""): comment = comment[: -len("\\n")] # Before we used to strip. Necessary in some cases? combinedcomment.append(comment) - partcomments = self.quote("_:%s" % "".join(combinedcomment)) + partcomments = self.quote("_:{}".format("".join(combinedcomment))) # Strip heading empty line for multiline string, it was already added above if partcomments[0] == '""': partcomments = partcomments[1:] @@ -898,7 +898,7 @@ def _extract_msgidcomments(self, text=None): def setmsgidcomment(self, msgidcomment): if msgidcomment: - self.msgidcomments = ['"_: %s\\n"' % msgidcomment] + self.msgidcomments = [f'"_: {msgidcomment}\\n"'] else: self.msgidcomments = [] @@ -970,7 +970,9 @@ def removeduplicates(self, duplicatestyle="merge"): markedpos = [] def addcomment(thepo): - thepo.msgidcomments.append('"_: %s\\n"' % " ".join(thepo.getlocations())) + thepo.msgidcomments.append( + '"_: {}\\n"'.format(" ".join(thepo.getlocations())) + ) markedpos.append(thepo) for thepo in self.units: @@ -989,11 +991,11 @@ def addcomment(thepo): origpo = id_dict[id] if origpo not in markedpos and not origpo.msgctxt: origpo.msgctxt.append( - '"%s"' % escapeforpo(" ".join(origpo.getlocations())) + '"{}"'.format(escapeforpo(" ".join(origpo.getlocations()))) ) markedpos.append(thepo) thepo.msgctxt.append( - '"%s"' % escapeforpo(" ".join(thepo.getlocations())) + '"{}"'.format(escapeforpo(" ".join(thepo.getlocations()))) ) if thepo.msgctxt != id_dict[id].msgctxt: uniqueunits.append(thepo) @@ -1009,7 +1011,7 @@ def addcomment(thepo): addcomment(thepo) else: thepo.msgctxt.append( - '"%s"' % escapeforpo(" ".join(thepo.getlocations())) + '"{}"'.format(escapeforpo(" ".join(thepo.getlocations()))) ) id_dict[id] = thepo uniqueunits.append(thepo) diff --git a/translate/storage/resx.py b/translate/storage/resx.py index bae8950ecb..546c10eec5 100644 --- a/translate/storage/resx.py +++ b/translate/storage/resx.py @@ -213,7 +213,7 @@ def addunit(self, unit, new=True): setXMLspace(unit.xmlelement, "preserve") if unit.getid() is None: self._messagenum += 1 - unit.setid("%s" % unit.source.strip(" ")) + unit.setid("{}".format(unit.source.strip(" "))) # adjust the current and previous elements for new ones; # otherwise they will not be indented correctly. if new: diff --git a/translate/storage/tiki.py b/translate/storage/tiki.py index 53236ed68d..bd2b57bd22 100644 --- a/translate/storage/tiki.py +++ b/translate/storage/tiki.py @@ -146,10 +146,7 @@ def serialize(self, out): @staticmethod def _tiki_header(): """Returns a tiki-file header string.""" - return ( - "' % (self.name) + return f'' def enter(self, obj): if not self.enter_action or not callable(self.enter_action): @@ -98,12 +98,12 @@ class TransitionError(WorkflowError): class InvalidStateObjectError(WorkflowError): def __init__(self, obj): - super().__init__("Invalid state object: %s" % (obj)) + super().__init__(f"Invalid state object: {obj}") class StateNotInWorkflowError(Exception): def __init__(self, state): - super().__init__("State not in workflow: %s" % (state)) + super().__init__(f"State not in workflow: {state}") class Workflow: @@ -144,7 +144,7 @@ def add_state(self, state): if not isinstance(state, State): raise InvalidStateObjectError(state) if state in self.states: - raise ValueError("State already in workflow: %s" % (state)) + raise ValueError(f"State already in workflow: {state}") self._states.append(state) if self._initial_state is None: self._initial_state = state diff --git a/translate/tools/pocompile.py b/translate/tools/pocompile.py index bd7665491b..627d3af215 100644 --- a/translate/tools/pocompile.py +++ b/translate/tools/pocompile.py @@ -28,7 +28,7 @@ def _do_msgidcomment(string): - return "_: %s\n" % string + return f"_: {string}\n" class POCompile: diff --git a/translate/tools/poconflicts.py b/translate/tools/poconflicts.py index 10a0cf23a1..43141c9ff3 100644 --- a/translate/tools/poconflicts.py +++ b/translate/tools/poconflicts.py @@ -102,7 +102,7 @@ def recursiveprocess(self, options): success = self.processfile(None, options, fullinputpath) except Exception: self.warning( - "Error processing: input %s" % (fullinputpath), + f"Error processing: input {fullinputpath}", options, sys.exc_info(), ) @@ -189,7 +189,7 @@ def str_len(x): fulloutputpath = os.path.join(options.output, flatsource + os.extsep + "po") conflictfile = po.pofile() for target, unit, filename in translations: - unit.othercomments.append("# (poconflicts) %s\n" % filename) + unit.othercomments.append(f"# (poconflicts) {filename}\n") conflictfile.units.append(unit) with open(fulloutputpath, "wb") as fh: conflictfile.serialize(fh) diff --git a/translate/tools/podebug.py b/translate/tools/podebug.py index a0952ff509..924f4e8535 100644 --- a/translate/tools/podebug.py +++ b/translate/tools/podebug.py @@ -61,8 +61,8 @@ def __init__( self.format = "" else: self.format = format - self.rewritefunc = getattr(self, "rewrite_%s" % rewritestyle, None) - self.ignorefunc = getattr(self, "ignore_%s" % ignoreoption, None) + self.rewritefunc = getattr(self, f"rewrite_{rewritestyle}", None) + self.ignorefunc = getattr(self, f"ignore_{ignoreoption}", None) self.preserveplaceholders = preserveplaceholders @staticmethod @@ -438,7 +438,9 @@ def main(): type="choice", choices=podebug.rewritelist(), metavar="STYLE", - help="the translation rewrite style: %s" % ", ".join(podebug.rewritelist()), + help="the translation rewrite style: {}".format( + ", ".join(podebug.rewritelist()) + ), ) parser.add_option( "", @@ -447,8 +449,9 @@ def main(): type="choice", choices=podebug.ignorelist(), metavar="APPLICATION", - help="apply tagging ignore rules for the given application: %s" - % ", ".join(podebug.ignorelist()), + help="apply tagging ignore rules for the given application: {}".format( + ", ".join(podebug.ignorelist()) + ), ) parser.add_option( "", diff --git a/translate/tools/pogrep.py b/translate/tools/pogrep.py index 7a7795c41d..30731052bf 100644 --- a/translate/tools/pogrep.py +++ b/translate/tools/pogrep.py @@ -254,7 +254,7 @@ def getmatches(self, units): flags |= re.IGNORECASE if not self.useregexp: searchstring = re.escape(searchstring) - self.re_search = re.compile("(%s)" % (searchstring), flags) + self.re_search = re.compile(f"({searchstring})", flags) matches = [] indexes = [] diff --git a/translate/tools/pomerge.py b/translate/tools/pomerge.py index 7604323416..3ad2827861 100644 --- a/translate/tools/pomerge.py +++ b/translate/tools/pomerge.py @@ -69,7 +69,7 @@ def str2bool(option): return True if option in ("no", "false", "0"): return False - raise ValueError("invalid boolean value: %r" % option) + raise ValueError(f"invalid boolean value: {option!r}") def mergestore( @@ -83,15 +83,15 @@ def mergestore( try: mergecomments = str2bool(mergecomments) except ValueError: - raise ValueError("invalid mergecomments value: %r" % mergecomments) + raise ValueError(f"invalid mergecomments value: {mergecomments!r}") try: mergeblanks = str2bool(mergeblanks) except ValueError: - raise ValueError("invalid mergeblanks value: %r" % mergeblanks) + raise ValueError(f"invalid mergeblanks value: {mergeblanks!r}") try: mergefuzzy = str2bool(mergefuzzy) except ValueError: - raise ValueError("invalid mergefuzzy value: %r" % mergefuzzy) + raise ValueError(f"invalid mergefuzzy value: {mergefuzzy!r}") inputstore = factory.getobject(inputfile) if templatefile is None: # just merge nothing diff --git a/translate/tools/porestructure.py b/translate/tools/porestructure.py index fa3d269844..20756f9e75 100644 --- a/translate/tools/porestructure.py +++ b/translate/tools/porestructure.py @@ -88,7 +88,7 @@ def recursiveprocess(self, options): success = self.processfile(options, fullinputpath) except Exception: self.warning( - "Error processing: input %s" % (fullinputpath), + f"Error processing: input {fullinputpath}", options, sys.exc_info(), ) diff --git a/translate/tools/poswap.py b/translate/tools/poswap.py index bd8f63f030..e64d7b9b19 100644 --- a/translate/tools/poswap.py +++ b/translate/tools/poswap.py @@ -75,7 +75,7 @@ def convertpo(inputpofile, outputpotfile, template, reverse=False): elif not reverse: if inputpo.filename: unit.addnote( - "No translation found in %s" % inputpo.filename, origin="programmer" + f"No translation found in {inputpo.filename}", origin="programmer" ) else: unit.addnote( diff --git a/translate/tools/poterminology.py b/translate/tools/poterminology.py index 10d3894a21..c69bb98cf0 100644 --- a/translate/tools/poterminology.py +++ b/translate/tools/poterminology.py @@ -90,7 +90,7 @@ def __init__( if stopfile is None: with contextlib.suppress(Exception): stopfile = file_discovery.get_abs_data_filename( - "stoplist-%s" % self.sourcelanguage + f"stoplist-{self.sourcelanguage}" ) self.stopfile = stopfile self.parse_stopword_file() @@ -400,8 +400,7 @@ def parse_args(self, args=None, values=None): if args and not options.output and not options.update: if os.path.lexists(args[-1]) and not os.path.isdir(args[-1]): self.error( - "To overwrite %s, specify it with -o/--output or -u/--update" - % (args[-1]) + f"To overwrite {args[-1]}, specify it with -o/--output or -u/--update" ) options.output = args[-1] args = args[:-1] @@ -486,7 +485,7 @@ def recursiveprocess(self, options): self.processfile(None, options, fullinputpath) except Exception: self.warning( - "Error processing: input %s" % (fullinputpath), + f"Error processing: input {fullinputpath}", options, sys.exc_info(), ) @@ -547,8 +546,9 @@ def main(): type="string", metavar="STOPFILE", dest="stopfile", - help="read stopword (term exclusion) list from STOPFILE (default %s)" - % file_discovery.get_abs_data_filename("stoplist-en"), + help="read stopword (term exclusion) list from STOPFILE (default {})".format( + file_discovery.get_abs_data_filename("stoplist-en") + ), ) parser.set_defaults(foldtitle=True, ignorecase=False) @@ -645,8 +645,9 @@ def main(): type="choice", choices=TerminologyExtractor.sortorders_default, metavar="ORDER", - help="output sort order(s): %s (may repeat option, default is all in above order)" - % ", ".join(TerminologyExtractor.sortorders_default), + help="output sort order(s): {} (may repeat option, default is all in above order)".format( + ", ".join(TerminologyExtractor.sortorders_default) + ), ) parser.add_option( diff --git a/translate/tools/pretranslate.py b/translate/tools/pretranslate.py index 611c83d92a..1552db5249 100644 --- a/translate/tools/pretranslate.py +++ b/translate/tools/pretranslate.py @@ -182,7 +182,7 @@ def pretranslate_store( if template_store is not None: template_store.makeindex() # template preparation based on type - prepare_template = "prepare_template_%s" % template_store.__class__.__name__ + prepare_template = f"prepare_template_{template_store.__class__.__name__}" if prepare_template in globals(): globals()[prepare_template](template_store) diff --git a/translate/tools/pydiff.py b/translate/tools/pydiff.py index eed65cb3e7..b9ea8ed28f 100644 --- a/translate/tools/pydiff.py +++ b/translate/tools/pydiff.py @@ -235,7 +235,7 @@ def writediff(self, outfile): self.from_lines = [] fromfiledate = 0 else: - outfile.write("%s: No such file or directory\n" % self.fromfile) + outfile.write(f"{self.fromfile}: No such file or directory\n") validfiles = False if os.path.exists(self.tofile): with open(self.tofile) as fh: @@ -248,7 +248,7 @@ def writediff(self, outfile): self.to_lines = [] tofiledate = 0 else: - outfile.write("%s: No such file or directory\n" % self.tofile) + outfile.write(f"{self.tofile}: No such file or directory\n") validfiles = False if not validfiles: return