From fcdd04d5ec79c83728c7d433ecd75c44a4991455 Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Mon, 15 Jul 2019 18:28:28 +0200 Subject: [PATCH] Integrate pyformat with localize() (#384) By integrating string formattig with the localize() function call we can make both the translations easier to comprehend, as well as the code more transparant/readable. And by using SafeDict() it is also more reliable if arguments do not add up. When a key is not found the 'template' remains in the string. --- .../resource.language.en_gb/strings.po | 10 ++--- .../resource.language.nl_nl/strings.po | 20 +++++----- resources/lib/favorites.py | 6 +-- resources/lib/kodiwrapper.py | 15 +++++++- resources/lib/search.py | 2 +- resources/lib/streamservice.py | 12 +++--- resources/lib/tvguide.py | 2 +- resources/lib/vrtapihelper.py | 16 ++++---- resources/lib/vrtplayer.py | 2 +- test/test_kodi.py | 38 +++++++++++++++++++ 10 files changed, 86 insertions(+), 37 deletions(-) create mode 100644 test/test_kodi.py diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index 996864e1..ec3a8ebe 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -634,11 +634,11 @@ msgid "Please increase the maximum bandwidth. Maximum bandwidth is set to {max} msgstr "" msgctxt "#30958" -msgid "There is a problem with this VRT NU %s stream. Try again with %s %s %s or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/" +msgid "There is a problem with this VRT NU {protocol} stream. Try again with {component} {state} or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/" msgstr "" msgctxt "#30959" -msgid "and Widevine DRM" +msgid "InputStream Adaptive and Widevine DRM" msgstr "" msgctxt "#30960" @@ -650,7 +650,7 @@ msgid "enabled" msgstr "" msgctxt "#30962" -msgid "There is a problem with this VRT NU %s stream and Kodi %s does not support the alternative stream. Please upgrade to a newer Kodi version or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/" +msgid "There is a problem with this VRT NU {protocol} stream and Kodi {version} does not support the alternative stream. Please upgrade to a newer Kodi version or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/" msgstr "" msgctxt "#30965" @@ -678,7 +678,7 @@ msgid "Failed to get favorites token from VRT NU" msgstr "" msgctxt "#30976" -msgid "Failed to (un)follow program '%s' at VRT NU" +msgid "Failed to (un)follow program '{program}' at VRT NU" msgstr "" msgctxt "#30978" @@ -714,7 +714,7 @@ msgid "Successfully cleared VRT tokens." msgstr "" msgctxt "#30986" -msgid "No on demand stream available for %s." +msgid "No on demand stream available for {title}." msgstr "" msgctxt "#30990" diff --git a/resources/language/resource.language.nl_nl/strings.po b/resources/language/resource.language.nl_nl/strings.po index 5c3c4aa6..69faa8a1 100644 --- a/resources/language/resource.language.nl_nl/strings.po +++ b/resources/language/resource.language.nl_nl/strings.po @@ -526,12 +526,12 @@ msgid "Please increase the maximum bandwidth. Maximum bandwidth is set to {max} msgstr "Verhoog de maximale bandbreedte alstublieft. De maximale bandbreedte is ingesteld op {max} kbps, maar deze stream heeft minimum {min} kbps nodig." msgctxt "#30958" -msgid "There is a problem with this VRT NU %s stream. Try again with %s %s %s or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/" -msgstr "Er is een probleem met deze VRT NU %s-stream. Probeer het opnieuw met %s %s %s of probeer dit programma af te spelen vanaf de VRT NU-website. Meld dit probleem op https://www.vrt.be/vrtnu/help/" +msgid "There is a problem with this VRT NU {protocol} stream. Try again with {component} {state} or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/" +msgstr "Er is een probleem met deze VRT NU {protocol}-stream. Probeer het opnieuw met {component} {state} of probeer dit programma af te spelen vanaf de VRT NU-website. Meld dit probleem op https://www.vrt.be/vrtnu/help/" msgctxt "#30959" -msgid "and Widevine DRM" -msgstr "en Widevine DRM" +msgid "InputStream Adaptive and Widevine DRM" +msgstr "InputStream Adaptive en Widevine DRM" msgctxt "#30960" msgid "disabled" @@ -542,8 +542,8 @@ msgid "enabled" msgstr "ingeschakeld" msgctxt "#30962" -msgid "There is a problem with this VRT NU %s stream and Kodi %s does not support the alternative stream. Please upgrade to a newer Kodi version or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/" -msgstr "Er is een probleem met deze VRT NU %s-stream en Kodi %s ondersteunt de alternatieve stream niet. Schakel over op een nieuwere versie van Kodi of probeer dit programma af te spelen vanaf de VRT NU-website. Meld dit probleem op https://www.vrt.be/vrtnu/help/" +msgid "There is a problem with this VRT NU {protocol} stream and Kodi {version} does not support the alternative stream. Please upgrade to a newer Kodi version or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/" +msgstr "Er is een probleem met deze VRT NU {protocol}-stream en Kodi {version} ondersteunt de alternatieve stream niet. Schakel over op een nieuwere versie van Kodi of probeer dit programma af te spelen vanaf de VRT NU-website. Meld dit probleem op https://www.vrt.be/vrtnu/help/" msgctxt "#30965" msgid "Using a SOCKS proxy requires the PySocks library (script.module.pysocks) installed." @@ -570,8 +570,8 @@ msgid "Failed to get favorites token from VRT NU" msgstr "Ophalen van het favorieten token van VRT NU is mislukt" msgctxt "#30976" -msgid "Failed to (un)follow program '%s' at VRT NU" -msgstr "Het programma '%s' op VRT NU volgen of vergeten is mislukt" +msgid "Failed to (un)follow program '{program}' at VRT NU" +msgstr "Het programma '{program}' op VRT NU volgen of vergeten is mislukt" msgctxt "#30978" msgid "Welcome to VRT NU add-on v2.0.0" @@ -606,8 +606,8 @@ msgid "Successfully cleared VRT tokens." msgstr "VRT tokens werden verwijderd." msgctxt "#30986" -msgid "No on demand stream available for %s." -msgstr "Geen on demand stream beschikbaar voor %s" +msgid "No on demand stream available for {title}." +msgstr "Geen on demand stream beschikbaar voor {title}" msgctxt "#30990" msgid "This program cannot be played. To view this program outside of Belgium, you first have to validate a Belgian mobile phone number on the VRT NU website." diff --git a/resources/lib/favorites.py b/resources/lib/favorites.py index fbf7ed3a..b9a6ab61 100644 --- a/resources/lib/favorites.py +++ b/resources/lib/favorites.py @@ -65,8 +65,8 @@ def set_favorite(self, program, title, value=True): xvrttoken = self._tokenresolver.get_xvrttoken(token_variant='user') if xvrttoken is None: - self._kodi.show_notification(message=self._kodi.localize(30975)) self._kodi.log_error('Failed to get favorites token from VRT NU') + self._kodi.show_notification(message=self._kodi.localize(30975)) return False headers = { @@ -81,8 +81,8 @@ def set_favorite(self, program, title, value=True): req = Request('https://video-user-data.vrt.be/favorites/%s' % self.uuid(program), data=data, headers=headers) result = urlopen(req) if result.getcode() != 200: - self._kodi.show_notification(message=self._kodi.localize(30976) % program) - self._kodi.log_error("Failed to (un)follow program '%s' at VRT NU" % program) + self._kodi.log_error("Failed to (un)follow program '{program}' at VRT NU".format(program=program)) + self._kodi.show_notification(message=self._kodi.localize(30976, program=program)) return False # NOTE: Updates to favorites take a longer time to take effect, so we keep our own cache and use it self._favorites[self.uuid(program)] = dict(value=payload) diff --git a/resources/lib/kodiwrapper.py b/resources/lib/kodiwrapper.py index 37044079..07fac677 100644 --- a/resources/lib/kodiwrapper.py +++ b/resources/lib/kodiwrapper.py @@ -100,6 +100,13 @@ def has_socks(): return has_socks.installed +class SafeDict(dict): + ''' A safe dictionary implementation that does not break down on missing keys ''' + def __missing__(self, key): + ''' Replace missing keys with the original placeholder ''' + return '{' + key + '}' + + class KodiWrapper: ''' A wrapper around all Kodi functionality ''' @@ -291,8 +298,12 @@ def set_locale(self): self.log_notice("Your system does not support locale '%s': %s" % (locale_lang, e), 'Debug') return False - def localize(self, string_id): - ''' Return the translated string from the .po language files ''' + def localize(self, string_id, **kwargs): + ''' Return the translated string from the .po language files, optionally translating variables ''' + if kwargs: + import string + return string.Formatter().vformat(self._addon.getLocalizedString(string_id), (), SafeDict(**kwargs)) + return self._addon.getLocalizedString(string_id) def localize_date(self, date, strftime): diff --git a/resources/lib/search.py b/resources/lib/search.py index e9058ef7..f64592bd 100644 --- a/resources/lib/search.py +++ b/resources/lib/search.py @@ -74,7 +74,7 @@ def search(self, keywords=None, page=None): self.add(keywords) search_items, sort, ascending, content = self._apihelper.get_search_items(keywords, page=page) if not search_items: - self._kodi.show_ok_dialog(heading=self._kodi.localize(30098), message=self._kodi.localize(30099).format(keywords=keywords)) + self._kodi.show_ok_dialog(heading=self._kodi.localize(30098), message=self._kodi.localize(30099, keywords=keywords)) self._kodi.end_of_directory() return diff --git a/resources/lib/streamservice.py b/resources/lib/streamservice.py index dbcf3d89..98ef60fb 100644 --- a/resources/lib/streamservice.py +++ b/resources/lib/streamservice.py @@ -291,15 +291,15 @@ def _handle_bad_stream_error(self, protocol): ''' # HLS AES DRM failed if protocol == 'hls_aes' and not self._kodi.supports_drm(): - message = self._kodi.localize(30962) % (protocol.upper(), self._kodi.kodi_version()) + message = self._kodi.localize(30962, protocol=protocol.upper(), version=self._kodi.kodi_version()) elif protocol == 'hls_aes' and not self._kodi.has_inputstream_adaptive() and self._kodi.get_setting('usedrm', 'true') == 'false': - message = self._kodi.localize(30958) % (protocol.upper(), 'InputStream Adaptive', self._kodi.localize(30959), self._kodi.localize(30961)) + message = self._kodi.localize(30958, protocol=protocol.upper(), component=self._kodi.localize(30959), state=self._kodi.localize(30961)) elif protocol == 'hls_aes' and self._kodi.has_inputstream_adaptive(): - message = self._kodi.localize(30958) % (protocol.upper(), 'Widevine DRM', '', self._kodi.localize(30961)) + message = self._kodi.localize(30958, protocol=protocol.upper(), component='Widevine DRM', state=self._kodi.localize(30961)) elif protocol == 'hls_aes' and self._kodi.get_setting('usedrm', 'true') == 'true': - message = self._kodi.localize(30958) % (protocol.upper(), 'InputStream Adaptive', '', self._kodi.localize(30961)) + message = self._kodi.localize(30958, protocol=protocol.upper(), component='InputStream Adaptive', state=self._kodi.localize(30961)) else: - message = self._kodi.localize(30958) % (protocol.upper(), 'InputStream Adaptive', '', self._kodi.localize(30960)) + message = self._kodi.localize(30958, protocol=protocol.upper(), component='InputStream Adaptive', state=self._kodi.localize(30960)) self._kodi.show_ok_dialog(message=message) self._kodi.end_of_directory() @@ -338,7 +338,7 @@ def _select_hls_substreams(self, master_hls_url, protocol): break if stream_bandwidth > max_bandwidth and not hls_variant_url: - message = self._kodi.localize(30057).format(max=max_bandwidth, min=stream_bandwidth) + message = self._kodi.localize(30057, max=max_bandwidth, min=stream_bandwidth) self._kodi.show_ok_dialog(message=message) self._kodi.open_settings() diff --git a/resources/lib/tvguide.py b/resources/lib/tvguide.py index 6a6514f9..62018374 100644 --- a/resources/lib/tvguide.py +++ b/resources/lib/tvguide.py @@ -110,7 +110,7 @@ def show_channel_menu(self, date): fanart = 'resource://resource.images.studios.coloured/%(studio)s.png' % channel thumb = 'resource://resource.images.studios.white/%(studio)s.png' % channel - plot = '%s\n%s' % (self._kodi.localize(30301).format(**channel), datelong) + plot = '%s\n%s' % (self._kodi.localize(30301, **channel), datelong) channel_items.append(TitleItem( title=channel.get('label'), path=self._kodi.url_for('tvguide', date=date, channel=channel.get('name')), diff --git a/resources/lib/vrtapihelper.py b/resources/lib/vrtapihelper.py index e51812aa..bd6928ac 100644 --- a/resources/lib/vrtapihelper.py +++ b/resources/lib/vrtapihelper.py @@ -400,12 +400,12 @@ def episode_to_listitem(self, episode, program, cache_file, titletype): # Only display when a video disappears if it is within the next 3 months if metadata.offtime is not None and (metadata.offtime - now).days < 93: # Show date when episode is removed - plot_meta += self._kodi.localize(30202).format(date=self._kodi.localize_dateshort(metadata.offtime)) + plot_meta += self._kodi.localize(30202, date=self._kodi.localize_dateshort(metadata.offtime)) # Show the remaining days/hours the episode is still available if (metadata.offtime - now).days > 0: - plot_meta += self._kodi.localize(30203).format(days=(metadata.offtime - now).days) + plot_meta += self._kodi.localize(30203, days=(metadata.offtime - now).days) else: - plot_meta += self._kodi.localize(30204).format(hours=int((metadata.offtime - now).seconds / 3600)) + plot_meta += self._kodi.localize(30204, hours=int((metadata.offtime - now).seconds / 3600)) if plot_meta: metadata.plot = '%s\n%s' % (plot_meta, metadata.plot) @@ -638,7 +638,7 @@ def get_channel_items(self, channels=None, live=True): path = self._kodi.url_for('play_id', video_id=channel.get('live_stream_id')) elif channel.get('live_stream'): path = self._kodi.url_for('play_url', video_url=channel.get('live_stream')) - label = self._kodi.localize(30101).format(**channel) + label = self._kodi.localize(30101, **channel) # A single Live channel means it is the entry for channel's TV Show listing, so make it stand out if channels and len(channels) == 1: label = '[B]%s[/B]' % label @@ -646,9 +646,9 @@ def get_channel_items(self, channels=None, live=True): if channel.get('name') in ['een', 'canvas', 'ketnet']: if self._showfanart: fanart = self.get_live_screenshot(channel.get('name', fanart)) - plot = '%s\n\n%s' % (self._kodi.localize(30102).format(**channel), _tvguide.live_description(channel.get('name'))) + plot = '%s\n\n%s' % (self._kodi.localize(30102, **channel), _tvguide.live_description(channel.get('name'))) else: - plot = self._kodi.localize(30102).format(**channel) + plot = self._kodi.localize(30102, **channel) # NOTE: Playcount is required to not have live streams as "Watched" info_dict = dict(title=label, plot=plot, studio=channel.get('studio'), mediatype='video', playcount=0, duration=0) stream_dict = dict(duration=0) @@ -686,11 +686,11 @@ def get_youtube_items(self, channels=None, live=True): if channel.get('youtube'): path = channel.get('youtube') - label = self._kodi.localize(30103).format(**channel) + label = self._kodi.localize(30103, **channel) # A single Live channel means it is the entry for channel's TV Show listing, so make it stand out if channels and len(channels) == 1: label = '[B]%s[/B]' % label - plot = self._kodi.localize(30104).format(**channel) + plot = self._kodi.localize(30104, **channel) # NOTE: Playcount is required to not have live streams as "Watched" info_dict = dict(title=label, plot=plot, studio=channel.get('studio'), mediatype='video', playcount=0) context_menu = [(self._kodi.localize(30413), 'RunPlugin(%s)' % self._kodi.url_for('delete_cache', cache_file='channel.%s.json' % channel))] diff --git a/resources/lib/vrtplayer.py b/resources/lib/vrtplayer.py index 7fa9511d..e5c94214 100644 --- a/resources/lib/vrtplayer.py +++ b/resources/lib/vrtplayer.py @@ -238,7 +238,7 @@ def play_episode_by_air_date(self, channel, start_date, end_date): ''' Play an episode of a program given the channel and the air date in iso format (2019-07-06T19:35:00) ''' video = self._apihelper.get_episode_by_air_date(channel, start_date, end_date) if video and video.get('video_title'): - self._kodi.show_ok_dialog(message=self._kodi.localize(30986) % video.get('video_title')) + self._kodi.show_ok_dialog(message=self._kodi.localize(30986, title=video.get('video_title'))) self._kodi.end_of_directory() return if not video: diff --git a/test/test_kodi.py b/test/test_kodi.py new file mode 100644 index 00000000..a65f5281 --- /dev/null +++ b/test/test_kodi.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# pylint: disable=missing-docstring + +from __future__ import absolute_import, division, print_function, unicode_literals +import unittest + +from resources.lib import kodiwrapper + +xbmc = __import__('xbmc') +xbmcaddon = __import__('xbmcaddon') +xbmcgui = __import__('xbmcgui') +xbmcplugin = __import__('xbmcplugin') +xbmcvfs = __import__('xbmcvfs') + + +class KodiTests(unittest.TestCase): + + _kodi = kodiwrapper.KodiWrapper(None) + + def test_localize(self): + msg = self._kodi.localize(30958) + self.assertEqual(msg, "There is a problem with this VRT NU {protocol} stream. Try again with {component} {state} or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/") # noqa + print(msg) + + msg = self._kodi.localize(30958, component='Widevine DRM', state='enabled') + self.assertEqual(msg, "There is a problem with this VRT NU {protocol} stream. Try again with Widevine DRM enabled or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/") # noqa + print(msg) + + msg = self._kodi.localize(30958, protocol='MPEG-DASH', component='Widevine DRM', state='enabled') + self.assertEqual(msg, "There is a problem with this VRT NU MPEG-DASH stream. Try again with Widevine DRM enabled or try to play this program from the VRT NU website. Please report this problem at https://www.vrt.be/vrtnu/help/") # noqa + print(msg) + + +if __name__ == '__main__': + unittest.main()