From 7cde6ce24324ca6dcc880ac69f90a55df1163749 Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Sun, 2 Feb 2025 10:18:08 +0100 Subject: [PATCH 1/3] ... --- src/calibre/ebooks/oeb/polish/container.py | 2 +- src/calibre/ebooks/oeb/transforms/split.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/oeb/polish/container.py b/src/calibre/ebooks/oeb/polish/container.py index 49dca6605c..9e41a1f1a5 100644 --- a/src/calibre/ebooks/oeb/polish/container.py +++ b/src/calibre/ebooks/oeb/polish/container.py @@ -344,7 +344,7 @@ class Container(ContainerBase): # {{{ item_id = 'id' while item_id in all_ids: c += 1 - item_id = 'id' + f'{c}' + item_id = f'id{c}' manifest = self.opf_xpath('//opf:manifest')[0] href = self.name_to_href(name, self.opf_name) item = manifest.makeelement(OPF('item'), diff --git a/src/calibre/ebooks/oeb/transforms/split.py b/src/calibre/ebooks/oeb/transforms/split.py index 85381a000e..85b5ce1ec8 100644 --- a/src/calibre/ebooks/oeb/transforms/split.py +++ b/src/calibre/ebooks/oeb/transforms/split.py @@ -366,10 +366,10 @@ class FlowSplitter: elif size <= self.max_flow_size: self.split_trees.append(t) self.log.debug( - f'\t\t\tCommitted sub-tree #{len(self.split_trees)} ({size / 1024.0} KB)') + f'\t\t\tCommitted sub-tree #{len(self.split_trees)} ({size//1024} KB)') else: self.log.debug( - f'\t\t\tSplit tree still too large: {size / 1024.0} KB') + f'\t\t\tSplit tree still too large: {size//1024} KB') self.split_to_size(t) def find_split_point(self, root): From 75aff434179e517ea2c993f64fbc001c1edaad71 Mon Sep 17 00:00:00 2001 From: un-pogaz <46523284+un-pogaz@users.noreply.github.com> Date: Sun, 2 Feb 2025 10:18:08 +0100 Subject: [PATCH 2/3] conversion of remaining % format --- pyproject.toml | 1 - src/calibre/db/cli/cmd_check_library.py | 2 +- src/calibre/db/cli/cmd_list.py | 2 +- src/calibre/db/cli/cmd_list_categories.py | 2 +- src/calibre/db/cli/cmd_set_metadata.py | 4 ++-- src/calibre/devices/cli.py | 2 +- src/calibre/devices/kobo/bookmark.py | 2 +- src/calibre/devices/kobo/books.py | 2 +- src/calibre/devices/smart_device_app/driver.py | 2 +- src/calibre/devices/utils.py | 2 +- src/calibre/ebooks/conversion/plugins/rtf_input.py | 6 ++---- src/calibre/ebooks/docx/writer/container.py | 2 +- src/calibre/ebooks/docx/writer/styles.py | 3 +-- src/calibre/ebooks/metadata/book/base.py | 2 +- src/calibre/ebooks/metadata/mobi.py | 2 +- src/calibre/ebooks/mobi/debug/index.py | 4 ++-- src/calibre/ebooks/mobi/debug/mobi6.py | 12 +++++------- src/calibre/ebooks/mobi/debug/mobi8.py | 4 ++-- src/calibre/ebooks/mobi/reader/mobi6.py | 2 +- src/calibre/ebooks/pdf/render/serialize.py | 2 +- src/calibre/ebooks/readability/readability.py | 3 +-- src/calibre/ebooks/rtf/preprocess.py | 4 ++-- src/calibre/ebooks/textile/functions.py | 2 +- src/calibre/gui2/metadata/single_download.py | 2 +- src/calibre/gui2/tweak_book/check.py | 3 +-- src/calibre/gui2/tweak_book/check_links.py | 2 +- src/calibre/gui2/tweak_book/widgets.py | 2 +- src/calibre/library/catalogs/epub_mobi_builder.py | 13 +++++-------- src/calibre/utils/config.py | 14 +++++--------- src/calibre/utils/fonts/sfnt/subset.py | 7 +++---- src/calibre/utils/zipfile.py | 7 ++++--- src/calibre/web/feeds/__init__.py | 2 +- src/calibre/web/feeds/news.py | 2 +- src/calibre/web/feeds/recipes/collection.py | 2 +- 34 files changed, 55 insertions(+), 70 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 93104cffa0..4fd42fcaf9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,6 @@ unfixable = ['PIE794', 'ISC001'] [tool.ruff.lint.per-file-ignores] "recipes/*" = ['UP'] "setup/changelog.py" = ['ISC001'] -"src/calibre/*" = ['UP031'] "src/calibre/ebooks/unihandecode/*codepoints.py" = ['E501'] "src/calibre/ebooks/metadata/sources/*" = ['UP'] "src/calibre/gui2/store/stores/*" = ['UP'] diff --git a/src/calibre/db/cli/cmd_check_library.py b/src/calibre/db/cli/cmd_check_library.py index a0812a1040..3e4438ec15 100644 --- a/src/calibre/db/cli/cmd_check_library.py +++ b/src/calibre/db/cli/cmd_check_library.py @@ -86,7 +86,7 @@ def _print_check_library_results(checker, check, as_csv=False, out=sys.stdout): else: print(check[1], file=out) for i in list: - print(' %-40.40s - %-40.40s' % (i[0], i[1]), file=out) + print(f' {i[0]:<40.40} -{i[1]:<40.40}', file=out) def main(opts, args, dbctx): diff --git a/src/calibre/db/cli/cmd_list.py b/src/calibre/db/cli/cmd_list.py index a030c20bb6..1b6fdf2adf 100644 --- a/src/calibre/db/cli/cmd_list.py +++ b/src/calibre/db/cli/cmd_list.py @@ -251,7 +251,7 @@ def do_list( ft = text[i][l] if l < len(text[i]) else '' stdout.write(ft.encode('utf-8')) if i < len(text) - 1: - filler = ('%*s' % (widths[i] - str_width(ft) - 1, '')) + filler = ' '*(widths[i] - str_width(ft) - 1) stdout.write((filler + separator).encode('utf-8')) stdout.write(linesep) diff --git a/src/calibre/db/cli/cmd_list_categories.py b/src/calibre/db/cli/cmd_list_categories.py index 0c5e7742a1..58225f30ac 100644 --- a/src/calibre/db/cli/cmd_list_categories.py +++ b/src/calibre/db/cli/cmd_list_categories.py @@ -115,7 +115,7 @@ def do_list(fields, data, opts): for l in range(lines): for i, field in enumerate(text): ft = text[i][l] if l < len(text[i]) else '' - filler = '%*s' % (widths[i] - len(ft) - 1, '') + filler = ' '*(widths[i] - len(ft) - 1) print(ft.encode('utf-8') + filler.encode('utf-8'), end=separator) print() diff --git a/src/calibre/db/cli/cmd_set_metadata.py b/src/calibre/db/cli/cmd_set_metadata.py index aa18a6ce72..6887661e6c 100644 --- a/src/calibre/db/cli/cmd_set_metadata.py +++ b/src/calibre/db/cli/cmd_set_metadata.py @@ -118,9 +118,9 @@ def get_fields(dbctx): def main(opts, args, dbctx): if opts.list_fields: ans = get_fields(dbctx) - prints('%-40s' % _('Title'), _('Field name'), '\n') + prints('{:<40}'.format(_('Title')), _('Field name'), '\n') for key, m in ans: - prints('%-40s' % m['name'], key) + prints('{:<40}'.format(m['name']), key) return 0 def verify_int(x): diff --git a/src/calibre/devices/cli.py b/src/calibre/devices/cli.py index 461185cfc5..3d30a3ba4c 100755 --- a/src/calibre/devices/cli.py +++ b/src/calibre/devices/cli.py @@ -244,7 +244,7 @@ def main(): where = ('Memory', 'Card A', 'Card B') print('Filesystem\tSize \tUsed \tAvail \tUse%') for i in range(3): - print('%-10s\t%s\t%s\t%s\t%s'%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]), + print('{:<10}\t{}\t{}\t{}\t{}'.format(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]), str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+'%')) elif command == 'eject': dev.eject() diff --git a/src/calibre/devices/kobo/bookmark.py b/src/calibre/devices/kobo/bookmark.py index 63269ef685..0d55c3e299 100644 --- a/src/calibre/devices/kobo/bookmark.py +++ b/src/calibre/devices/kobo/bookmark.py @@ -168,7 +168,7 @@ class Bookmark: # {{{ ans = ['Kobo bookmark:'] def fmt(x, y): - ans.append('%-20s: %s'%(str(x), str(y))) + ans.append(f'{x:<20}: {y}') if self.contentId: fmt('ContentID', self.contentId) diff --git a/src/calibre/devices/kobo/books.py b/src/calibre/devices/kobo/books.py index 451b5cea38..598fb48af1 100644 --- a/src/calibre/devices/kobo/books.py +++ b/src/calibre/devices/kobo/books.py @@ -101,7 +101,7 @@ class Book(Book_): ans = ['Kobo metadata:'] def fmt(x, y): - ans.append('%-20s: %s'%(str(x), str(y))) + ans.append(f'{x:<20}: {y}') if self.contentID: fmt('Content ID', self.contentID) diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py index 7e0bdd6e2e..4ded9131fa 100644 --- a/src/calibre/devices/smart_device_app/driver.py +++ b/src/calibre/devices/smart_device_app/driver.py @@ -484,7 +484,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): except: today = time.localtime() date = (today[0], today[1], today[2]) - template = '{title}_%d-%d-%d' % date + template = f'{{title}}_{date[0]}-{date[1]}-{date[2]}' use_subdirs = self.SUPPORTS_SUB_DIRS and settings.use_subdirs from calibre.library.save_to_disk import config, get_components diff --git a/src/calibre/devices/utils.py b/src/calibre/devices/utils.py index ada41e79a8..0c8a10d329 100644 --- a/src/calibre/devices/utils.py +++ b/src/calibre/devices/utils.py @@ -91,7 +91,7 @@ def create_upload_path(mdata, fname, template, sanitize, except: today = time.localtime() date = (today[0], today[1], today[2]) - template = '{title}_%d-%d-%d' % date + template = f'{{title}}_{date[0]}-{date[1]}-{date[2]}' fname = sanitize(fname) ext = path_type.splitext(fname)[1] diff --git a/src/calibre/ebooks/conversion/plugins/rtf_input.py b/src/calibre/ebooks/conversion/plugins/rtf_input.py index 619207852b..b0b813406c 100644 --- a/src/calibre/ebooks/conversion/plugins/rtf_input.py +++ b/src/calibre/ebooks/conversion/plugins/rtf_input.py @@ -192,10 +192,8 @@ class RTFInput(InputFormatPlugin): return name def write_inline_css(self, ic, border_styles): - font_size_classes = ['span.fs%d { font-size: %spt }'%(i, x) for i, x in - enumerate(ic.font_sizes)] - color_classes = ['span.col%d { color: %s }'%(i, x) for i, x in - enumerate(ic.colors) if x != 'false'] + font_size_classes = [f'span.fs{i} {{ font-size: {x}pt }}' for i, x in enumerate(ic.font_sizes)] + color_classes = [f'span.col{i} {{ color: {x} }}' for i, x in enumerate(ic.colors) if x != 'false'] css = textwrap.dedent(''' span.none { text-decoration: none; font-weight: normal; diff --git a/src/calibre/ebooks/docx/writer/container.py b/src/calibre/ebooks/docx/writer/container.py index 19c11404ab..9a0c96c303 100644 --- a/src/calibre/ebooks/docx/writer/container.py +++ b/src/calibre/ebooks/docx/writer/container.py @@ -209,7 +209,7 @@ class DOCX: E = ElementMaker(namespace=self.namespace.namespaces['ep'], nsmap={None:self.namespace.namespaces['ep']}) props = E.Properties( E.Application(__appname__), - E.AppVersion('%02d.%04d' % numeric_version[:2]), + E.AppVersion(f'{numeric_version[0]:02}.{numeric_version[1]:04}'), E.DocSecurity('0'), E.HyperlinksChanged('false'), E.LinksUpToDate('true'), diff --git a/src/calibre/ebooks/docx/writer/styles.py b/src/calibre/ebooks/docx/writer/styles.py index 2d1efef1e8..b49cdec164 100644 --- a/src/calibre/ebooks/docx/writer/styles.py +++ b/src/calibre/ebooks/docx/writer/styles.py @@ -769,8 +769,7 @@ class StylesManager: text_style.seq = i self.descendant_text_styles = sorted(descendant_style_map, key=attrgetter('seq')) - self.log.debug('%d Text Styles %d Combined styles' % tuple(map(len, ( - self.descendant_text_styles, self.combined_styles)))) + self.log.debug(f'{len(self.descendant_text_styles)} Text Styles {len(self.combined_styles)} Combined styles') self.primary_heading_style = None if heading_styles: diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 0a69c4110f..08e79b76cf 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -765,7 +765,7 @@ class Metadata: ans = [] def fmt(x, y): - ans.append('%-20s: %s'%(str(x), str(y))) + ans.append(f'{x:<20}: {y}') fmt('Title', self.title) if self.title_sort: diff --git a/src/calibre/ebooks/metadata/mobi.py b/src/calibre/ebooks/metadata/mobi.py index 9c38d3699e..0f9f5447e3 100644 --- a/src/calibre/ebooks/metadata/mobi.py +++ b/src/calibre/ebooks/metadata/mobi.py @@ -308,7 +308,7 @@ class MetadataUpdater: def dump_pdbrecords(self): # Diagnostic print('MetadataUpdater.dump_pdbrecords()') - print('%10s %10s %10s' % ('offset','flags','val')) + print(f"{'offset':>10} {'flags':>10} {'val':>10}") for i in range(len(self.pdbrecords)): pdbrecord = self.pdbrecords[i] print(f'{pdbrecord[0]:10X} {pdbrecord[1]:10X} {pdbrecord[2]:10X}') diff --git a/src/calibre/ebooks/mobi/debug/index.py b/src/calibre/ebooks/mobi/debug/index.py index 5fecd43dca..c122db4293 100644 --- a/src/calibre/ebooks/mobi/debug/index.py +++ b/src/calibre/ebooks/mobi/debug/index.py @@ -96,13 +96,13 @@ class Index: a = ans.append if self.header is not None: for field in INDEX_HEADER_FIELDS: - a('%-12s: %r'%(FIELD_NAMES.get(field, field), self.header[field])) + a(f'{FIELD_NAMES.get(field, field):<12}: {self.header[field]!r}') ans.extend(['', '']) ans += ['*'*10 + f' Index Record Headers ({len(self.index_headers)} records) ' + '*'*10] for i, header in enumerate(self.index_headers): ans += ['*'*10 + f' Index Record {i} ' + '*'*10] for field in INDEX_HEADER_FIELDS: - a('%-12s: %r'%(FIELD_NAMES.get(field, field), header[field])) + a(f'{FIELD_NAMES.get(field, field):<12}: {header[field]!r}') if self.cncx: a('*'*10 + ' CNCX ' + '*'*10) diff --git a/src/calibre/ebooks/mobi/debug/mobi6.py b/src/calibre/ebooks/mobi/debug/mobi6.py index 99a55f6555..cd5cf871a1 100644 --- a/src/calibre/ebooks/mobi/debug/mobi6.py +++ b/src/calibre/ebooks/mobi/debug/mobi6.py @@ -98,8 +98,8 @@ class SecondaryIndexHeader: # {{{ a = ans.append def u(w): - a('Unknown: %r (%d bytes) (All zeros: %r)'%(w, - len(w), not bool(w.replace(b'\0', b'')))) + a('Unknown: {!r} ({} bytes) (All zeros: {!r})'.format( + w, len(w), not bool(w.replace(b'\0', b'')))) a(f'Header length: {self.header_length}') u(self.unknown1) @@ -199,7 +199,7 @@ class IndexHeader: # {{{ a = ans.append def u(w): - a('Unknown: %r (%d bytes) (All zeros: %r)'%(w, + a('Unknown: {!r} ({} bytes) (All zeros: {!r})'.format(w, len(w), not bool(w.replace(b'\0', b'')))) a(f'Header length: {self.header_length}') @@ -411,7 +411,7 @@ class IndexRecord: # {{{ a = ans.append def u(w): - a('Unknown: %r (%d bytes) (All zeros: %r)'%(w, + a('Unknown: {!r} ({} bytes) (All zeros: {!r})'.format(w, len(w), not bool(w.replace(b'\0', b'')))) for entry in self.indices: offset = entry.offset @@ -582,9 +582,7 @@ class TBSIndexing: # {{{ ans = [] ans.append(f"\nRecord #{r.idx}: Starts at: {dat['geom'][0]} Ends at: {dat['geom'][1]}") s, e, c = dat['starts'], dat['ends'], dat['complete'] - ans.append(('\tContains: %d index entries ' - '(%d ends, %d complete, %d starts)')%tuple(map(len, (s+e+c, e, - c, s)))) + ans.append(f'\tContains: {len(s+e+c)} index entries ({len(e)} ends, {len(c)} complete, {len(s)} starts)') byts = bytearray(r.trailing_data.get('indexing', b'')) ans.append(f'TBS bytes: {format_bytes(byts)}') for typ, entries in (('Ends', e), ('Complete', c), ('Starts', s)): diff --git a/src/calibre/ebooks/mobi/debug/mobi8.py b/src/calibre/ebooks/mobi/debug/mobi8.py index f7b20a0296..0fa06c5bdd 100644 --- a/src/calibre/ebooks/mobi/debug/mobi8.py +++ b/src/calibre/ebooks/mobi/debug/mobi8.py @@ -45,7 +45,7 @@ class FDST: a('Number of section records', self.num_sections) ans.append(f'**** {len(self.sections)} Sections ****') for sec in self.sections: - ans.append('Start: %20d End: %d'%sec) + ans.append(f'Start: {sec[0]:>20} End: {sec[1]}') return '\n'.join(ans) @@ -266,7 +266,7 @@ class MOBIFile: for entries in itervalues(strand): for e in entries: desc.append( - ' %s%d [%-9s] parent: %s (%d) Geometry: (%d, %d)'%( + ' {}{} [{:<9}] parent: {} ({}) Geometry: ({}, {})'.format( e.depth * (' ') + '- ', e.index, e.action, e.parent, e.index-(e.parent or 0), e.start-i*RECORD_SIZE, e.start+e.length-i*RECORD_SIZE)) diff --git a/src/calibre/ebooks/mobi/reader/mobi6.py b/src/calibre/ebooks/mobi/reader/mobi6.py index aac14c1bd1..0628c5c47b 100644 --- a/src/calibre/ebooks/mobi/reader/mobi6.py +++ b/src/calibre/ebooks/mobi/reader/mobi6.py @@ -662,7 +662,7 @@ class MobiReader: elif mi.cover is not None: opf.cover = mi.cover else: - opf.cover = 'images/%05d.jpg' % 1 + opf.cover = f'images/{1:05}.jpg' if not os.path.exists(os.path.join(os.path.dirname(htmlfile), * opf.cover.split('/'))): opf.cover = None diff --git a/src/calibre/ebooks/pdf/render/serialize.py b/src/calibre/ebooks/pdf/render/serialize.py index 581b1f81e1..c6cd31c845 100644 --- a/src/calibre/ebooks/pdf/render/serialize.py +++ b/src/calibre/ebooks/pdf/render/serialize.py @@ -68,7 +68,7 @@ class IndirectObjects: stream.write(b'xref'+EOL) stream.write(f'0 {1 + len(self._offsets)}') stream.write(EOL) - stream.write('%010d 65535 f '%0) + stream.write(f'{0:010} 65535 f ') stream.write(EOL) for offset in self._offsets: diff --git a/src/calibre/ebooks/readability/readability.py b/src/calibre/ebooks/readability/readability.py index 573597aa1c..67d05b7fd0 100644 --- a/src/calibre/ebooks/readability/readability.py +++ b/src/calibre/ebooks/readability/readability.py @@ -366,8 +366,7 @@ class Document: tag = el.tag if weight + content_score < 0: - self.debug('Cleaned %s with score %6.3f and weight %-3s' % - (describe(el), content_score, weight, )) + self.debug(f'Cleaned {describe(el)} with score {content_score:6.3f} and weight {weight:<3}') el.drop_tree() elif el.text_content().count(',') < 10: counts = {} diff --git a/src/calibre/ebooks/rtf/preprocess.py b/src/calibre/ebooks/rtf/preprocess.py index 460c4752b1..430f63fac8 100644 --- a/src/calibre/ebooks/rtf/preprocess.py +++ b/src/calibre/ebooks/rtf/preprocess.py @@ -322,10 +322,10 @@ class RtfTokenizer: l = l + 1 i = i + 1 if l > 10: - raise Exception('Error (at:%d): Too many digits in control word numeric argument.'%[tokenStart]) + raise Exception(f'Error (at:{tokenStart}): Too many digits in control word numeric argument.') if not consumed: - raise Exception('Error (at:%d): Control Word without numeric argument end.'%[tokenStart]) + raise Exception(f'Error (at:{tokenStart}): Control Word without numeric argument end.') separator = '' if isChar(self.rtfData[i], ' '): diff --git a/src/calibre/ebooks/textile/functions.py b/src/calibre/ebooks/textile/functions.py index f4a1f57b9c..3ab0f91ab6 100644 --- a/src/calibre/ebooks/textile/functions.py +++ b/src/calibre/ebooks/textile/functions.py @@ -98,7 +98,7 @@ def getimagesize(url): break p.feed(s) if p.image: - return 'width="%i" height="%i"' % p.image.size + return f'width="{p.image.size[0]}" height="{p.image.size[1]}"' except (OSError, ValueError): return None diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 877fc25ccb..1028ce1e08 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -90,7 +90,7 @@ class RichTextDelegate(QStyledItemDelegate): # {{{ group = (QPalette.ColorGroup.Active if option.state & QStyle.StateFlag.State_Active else QPalette.ColorGroup.Inactive) c = p.color(group, QPalette.ColorRole.HighlightedText) - c = 'rgb(%d, %d, %d)'%c.getRgb()[:3] + c = 'rgb({}, {}, {})'.format(*c.getRgb()[:3]) doc.setDefaultStyleSheet(f' * {{ color: {c} }}') doc.setHtml(index.data() or '') return doc diff --git a/src/calibre/gui2/tweak_book/check.py b/src/calibre/gui2/tweak_book/check.py index c674c323d3..54865766a9 100644 --- a/src/calibre/gui2/tweak_book/check.py +++ b/src/calibre/gui2/tweak_book/check.py @@ -202,8 +202,7 @@ class Check(QSplitter): fix_tt = _('Try to fix all fixable errors automatically. Only works for some types of error.') fix_msg = _('Try to correct all fixable errors automatically') run_tt, run_msg = _('Re-run the check'), _('Re-run check') - header = '

%s [%d / %d]

' % ( - header, self.items.currentRow()+1, self.items.count()) + header = f'

{header} [{self.items.currentRow()+1} / {self.items.count()}]

' msg = '

%s

' footer = '
%s%s

%s
' if err.has_multiple_locations: diff --git a/src/calibre/gui2/tweak_book/check_links.py b/src/calibre/gui2/tweak_book/check_links.py index a7c25e4ed8..11451e0822 100644 --- a/src/calibre/gui2/tweak_book/check_links.py +++ b/src/calibre/gui2/tweak_book/check_links.py @@ -143,7 +143,7 @@ class CheckExternalLinks(Dialog): for i, (locations, err, url) in enumerate(self.errors): if i in self.fixed_errors: continue - text += '
  • %s \xa0[%s]
    %s