From d7afa77c4915eb8a51070360276ddcb36035cc1e Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Mon, 10 Jan 2022 05:05:27 -0600 Subject: [PATCH 1/8] Changed params names to avoid confussions --- src/sessions/twitter/session.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sessions/twitter/session.py b/src/sessions/twitter/session.py index 63a970a4..b964b1de 100644 --- a/src/sessions/twitter/session.py +++ b/src/sessions/twitter/session.py @@ -36,9 +36,9 @@ class Session(base.baseSession): return self.order_direct_messages(data) num = 0 last_id = None - if (name in self.db) == False: + if self.db.get(name) == None: self.db[name] = [] - if ("users" in self.db) == False: + if self.db.get("users") == None: self.db["users"] = {} objects = self.db[name] if ignore_older and len(self.db[name]) > 0: @@ -267,10 +267,10 @@ class Session(base.baseSession): last_id = None else: if self.settings["general"]["reverse_timelines"] == False: - last_id = self.db[name][0].id + since_id = self.db[name][-1].id else: - last_id = self.db[name][-1].id - data = getattr(self.twitter, update_function)(count=self.settings["general"]["max_tweets_per_call"], since_id=last_id, *args, **kwargs) + since_id = self.db[name][0].id + data = getattr(self.twitter, update_function)(count=self.settings["general"]["max_tweets_per_call"], since_id=since_id, *args, **kwargs) results.extend(data) results.reverse() return results From 7a78accd1fe3b86730927348b025d001e5200afa Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Mon, 10 Jan 2022 05:06:31 -0600 Subject: [PATCH 2/8] Fixed a typo --- src/sessions/twitter/session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sessions/twitter/session.py b/src/sessions/twitter/session.py index b964b1de..3c84d0e0 100644 --- a/src/sessions/twitter/session.py +++ b/src/sessions/twitter/session.py @@ -264,7 +264,7 @@ class Session(base.baseSession): returns a list with all items retrieved.""" results = [] if self.db.get(name) == None or self.db.get(name) == []: - last_id = None + since_id = None else: if self.settings["general"]["reverse_timelines"] == False: since_id = self.db[name][-1].id From 301e3d436170570369168d4134a068e12e049065 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Mon, 10 Jan 2022 05:30:14 -0600 Subject: [PATCH 3/8] Display properly HTML Entities in tweets --- doc/changelog.md | 1 + src/controller/messages.py | 1 + src/sessions/twitter/compose.py | 15 +-------------- src/sessions/twitter/templates.py | 2 +- src/sessions/twitter/utils.py | 14 ++++++++++++++ 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/doc/changelog.md b/doc/changelog.md index 6916756a..83d7022f 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -7,6 +7,7 @@ TWBlue Changelog * TWBlue can display image descriptions within Tweet templates. For that, you can use the $image_description variable in your template. * We have restored conversation and threads support powered by Twitter API V2 thanks to a set of improvements we have done in the application, as well as more generous limits to Tweet monthly cap by Twitter. * In the Windows 11 Keymap, the default shortcut to open the keystrokes editor is now CTRL+Alt+Windows+K to avoid conflicts with the new global mute microphone shortcut. +* TWBlue show display properly HTML entities in tweet's text. * TWBlue should no longer load old tweets in buffers. * Fixed issue when uploading attachments (images, videos or gif files) while sending tweets or replies. * Fixed an error that was making TWBlue to ask for a restart after saving account settings, even if such restart was not required. ([#413,](https://github.com/manuelcortez/TWBlue/issues/413)) diff --git a/src/controller/messages.py b/src/controller/messages.py index 421be6d7..9b93ed58 100644 --- a/src/controller/messages.py +++ b/src/controller/messages.py @@ -367,6 +367,7 @@ class viewTweet(basicTweet): pass def clear_text(self, text): + text = utils.StripChars(text) urls = utils.find_urls_in_text(text) for i in urls: if "https://twitter.com/" in i: diff --git a/src/sessions/twitter/compose.py b/src/sessions/twitter/compose.py index 1b761390..539b4df7 100644 --- a/src/sessions/twitter/compose.py +++ b/src/sessions/twitter/compose.py @@ -3,7 +3,6 @@ import platform system = platform.system() from . import utils import re -import html.entities import time import output import languageHandler @@ -11,21 +10,9 @@ import arrow import logging import config from .long_tweets import twishort, tweets +from .utils import StripChars log = logging.getLogger("compose") -def StripChars(s): - """Converts any html entities in s to their unicode-decoded equivalents and returns a string.""" - entity_re = re.compile(r"&(#\d+|\w+);") - def matchFunc(match): - """Nested function to handle a match object. - If we match &blah; and it's not found, &blah; will be returned. - if we match #\d+, unichr(digits) will be returned. - Else, a unicode string will be returned.""" - if match.group(1).startswith('#'): return chr(int(match.group(1)[1:])) - replacement = html.entities.entitydefs.get(match.group(1), "&%s;" % match.group(1)) - return replacement - return str(entity_re.sub(matchFunc, s)) - chars = "abcdefghijklmnopqrstuvwxyz" def compose_tweet(tweet, db, relative_times, show_screen_names=False, session=None): diff --git a/src/sessions/twitter/templates.py b/src/sessions/twitter/templates.py index 156a2d67..bd4067b0 100644 --- a/src/sessions/twitter/templates.py +++ b/src/sessions/twitter/templates.py @@ -32,7 +32,7 @@ def process_text(tweet): elif hasattr(tweet, "text"): text = tweet.text # Cleanup mentions, so we'll remove more than 2 mentions to make the tweet easier to read. - text = utils.clean_mentions(text) + text = utils.clean_mentions(utils.StripChars(text)) # Replace URLS for extended version of those. if hasattr(tweet, "entities"): text = utils.expand_urls(text, tweet.entities) diff --git a/src/sessions/twitter/utils.py b/src/sessions/twitter/utils.py index 47cef7a7..c13d3088 100644 --- a/src/sessions/twitter/utils.py +++ b/src/sessions/twitter/utils.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import re +import html.entities import output import logging import requests @@ -16,6 +17,19 @@ url_re = re.compile(r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4 url_re2 = re.compile("(?:\w+://|www\.)[^ ,.?!#%=+][^ \\n\\t]*") bad_chars = '\'\\\n.,[](){}:;"' +def StripChars(s): + """Converts any html entities in s to their unicode-decoded equivalents and returns a string.""" + entity_re = re.compile(r"&(#\d+|\w+);") + def matchFunc(match): + """Nested function to handle a match object. + If we match &blah; and it's not found, &blah; will be returned. + if we match #\d+, unichr(digits) will be returned. + Else, a unicode string will be returned.""" + if match.group(1).startswith('#'): return chr(int(match.group(1)[1:])) + replacement = html.entities.entitydefs.get(match.group(1), "&%s;" % match.group(1)) + return replacement + return str(entity_re.sub(matchFunc, s)) + def find_urls_in_text(text): return url_re2.findall(text) From c048c3ff32f0f95be72914acfe6bc6954a52bed3 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 25 Jan 2022 01:26:23 -0600 Subject: [PATCH 4/8] Retrieve usernames for timelines from the local database, as opposed to use the last API call as this might not contain items at times --- src/controller/buffers/twitter/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controller/buffers/twitter/base.py b/src/controller/buffers/twitter/base.py index 98005c85..838d0cf1 100644 --- a/src/controller/buffers/twitter/base.py +++ b/src/controller/buffers/twitter/base.py @@ -174,9 +174,9 @@ class BaseBuffer(base.Buffer): self.put_items_on_list(number_of_items) if hasattr(self, "finished_timeline") and self.finished_timeline == False: if "-timeline" in self.name: - self.username = val[0].user.screen_name + self.username = self.session.get_user(self.kwargs.get("user_id")).screen_name elif "-favorite" in self.name: - self.username = self.session.api_call("get_user", **self.kwargs).screen_name + self.username = self.session.get_user(self.kwargs.get("user_id")).screen_name self.finished_timeline = True if number_of_items > 0 and self.name != "sent_tweets" and self.name != "sent_direct_messages" and self.sound != None and self.session.settings["sound"]["session_mute"] == False and self.name not in self.session.settings["other_buffers"]["muted_buffers"] and play_sound == True: self.session.sound.play(self.sound) From e43ddad6786d6e392e82a1d8512573984f8d2960 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 25 Jan 2022 01:27:52 -0600 Subject: [PATCH 5/8] Catch image description sent by Streaming API. Should be enough to fix #449 --- src/sessions/twitter/templates.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sessions/twitter/templates.py b/src/sessions/twitter/templates.py index bd4067b0..46913220 100644 --- a/src/sessions/twitter/templates.py +++ b/src/sessions/twitter/templates.py @@ -45,6 +45,10 @@ def process_image_descriptions(entities): for media in entities["media"]: if media.get("ext_alt_text") != None: image_descriptions.append(media.get("ext_alt_text")) + # Tweets retrieved via the Streaming API have a description field in media photos with image description available. + elif media.get("description") != None: + image_descriptions.append(media.get("description")) + idescriptions = "" for image in image_descriptions: idescriptions += _("Image description: {}.").format(image) From 6e0a94355f7836a4a512204b01b3da27cbe4fca8 Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 25 Jan 2022 10:42:39 -0600 Subject: [PATCH 6/8] Improvements to template substitutions. Closes #452 --- src/sessions/twitter/templates.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sessions/twitter/templates.py b/src/sessions/twitter/templates.py index 46913220..ec54a13d 100644 --- a/src/sessions/twitter/templates.py +++ b/src/sessions/twitter/templates.py @@ -54,6 +54,11 @@ def process_image_descriptions(entities): idescriptions += _("Image description: {}.").format(image) return idescriptions +def remove_unneeded_variables(template, variables): + for variable in variables: + template = re.sub("\$"+variable, "", template) + return template + def render_tweet(tweet, template, session, relative_times=False, offset_seconds=0): """ Renders any given Tweet according to the passed template. Available data for tweets will be stored in the following variables: @@ -65,6 +70,7 @@ def render_tweet(tweet, template, session, relative_times=False, offset_seconds= $text: Tweet text. $image_descriptions: Information regarding image descriptions added by twitter users. """ + global tweet_variables available_data = dict() created_at = process_date(tweet.created_at, relative_times, offset_seconds) available_data.update(date=created_at) @@ -93,7 +99,7 @@ def render_tweet(tweet, template, session, relative_times=False, offset_seconds= if image_descriptions != "": available_data.update(image_descriptions=image_descriptions) result = Template(_(template)).safe_substitute(**available_data) - result = re.sub(r"\$\w+", "", result) + result = remove_unneeded_variables(result, tweet_variables) return result def render_dm(dm, template, session, relative_times=False, offset_seconds=0): @@ -106,6 +112,7 @@ def render_dm(dm, template, session, relative_times=False, offset_seconds=0): $recipient_screen_name: User screen name for user receiving the dm, this is the same name used to reference the user in Twitter. $text: Text of the direct message. """ + global dm_variables available_data = dict() available_data.update(text=utils.expand_urls(dm.message_create["message_data"]["text"], dm.message_create["message_data"]["entities"])) # Let's remove the last 3 digits in the timestamp string. @@ -120,7 +127,7 @@ def render_dm(dm, template, session, relative_times=False, offset_seconds=0): recipient = session.get_user(dm.message_create["target"]["recipient_id"]) available_data.update(sender_display_name=sender.name, sender_screen_name=sender.screen_name, recipient_display_name=recipient.name, recipient_screen_name=recipient.screen_name) result = Template(_(template)).safe_substitute(**available_data) - result = re.sub(r"\$\w+", "", result) + result = remove_unneeded_variables(result, dm_variables) return result # Sesion object is not used in this function but we keep compatibility across all rendering functions. @@ -138,6 +145,7 @@ def render_person(user, template, session=None, relative_times=True, offset_seco $tweets: The number of Tweets (including retweets) issued by the user. This value might be inaccurate. $created_at: The date and time that the user account was created on Twitter. """ + global person_variables available_data = dict(display_name=user.name, screen_name=user.screen_name, followers=user.followers_count, following=user.friends_count, likes=user.favourites_count, listed=user.listed_count, tweets=user.statuses_count) # Nullable values. nullables = ["location", "description"] @@ -147,5 +155,5 @@ def render_person(user, template, session=None, relative_times=True, offset_seco created_at = process_date(user.created_at, relative_times=relative_times, offset_seconds=offset_seconds) available_data.update(created_at=created_at) result = Template(_(template)).safe_substitute(**available_data) - result = re.sub(r"\$\w+", "", result) + result = remove_unneeded_variables(result, person_variables) return result \ No newline at end of file From ac0e7380b058f45b259994f8ffc5de207655cc7d Mon Sep 17 00:00:00 2001 From: Manuel Cortez Date: Tue, 25 Jan 2022 12:01:03 -0600 Subject: [PATCH 7/8] TWBlue should not allow users to retweet or quote any protected account. Closes #454 --- src/controller/buffers/base/base.py | 3 +++ src/controller/buffers/twitter/base.py | 11 +++++++++++ src/controller/mainController.py | 5 +++++ 3 files changed, 19 insertions(+) diff --git a/src/controller/buffers/base/base.py b/src/controller/buffers/base/base.py index 41b31219..178dbd10 100644 --- a/src/controller/buffers/base/base.py +++ b/src/controller/buffers/base/base.py @@ -125,6 +125,9 @@ class Buffer(object): def share_item(self): pass + def can_share(self): + pass + def destroy_status(self): pass diff --git a/src/controller/buffers/twitter/base.py b/src/controller/buffers/twitter/base.py index 838d0cf1..7ebea416 100644 --- a/src/controller/buffers/twitter/base.py +++ b/src/controller/buffers/twitter/base.py @@ -391,6 +391,12 @@ class BaseBuffer(base.Buffer): tweet = self.session.db[self.name][self.buffer.list.get_selected()] return tweet + def can_share(self): + tweet = self.get_right_tweet() + user = self.session.get_user(tweet.user) + is_protected = user.protected + return is_protected==False + @_tweets_exist def reply(self, *args, **kwargs): tweet = self.get_right_tweet() @@ -442,6 +448,8 @@ class BaseBuffer(base.Buffer): @_tweets_exist def share_item(self, *args, **kwargs): + if self.can_share() == False: + return output.speak(_("This action is not supported on protected accounts.")) tweet = self.get_right_tweet() id = tweet.id if self.session.settings["general"]["retweet_mode"] == "ask": @@ -483,6 +491,9 @@ class BaseBuffer(base.Buffer): self.session.sound.play("geo.ogg") if self.session.settings['sound']['indicate_img'] and utils.is_media(tweet): self.session.sound.play("image.ogg") + can_share = self.can_share() + pub.sendMessage("toggleShare", shareable=can_share) + self.buffer.retweet.Enable(can_share) def audio(self, url='', *args, **kwargs): if sound.URLPlayer.player.is_playing(): diff --git a/src/controller/mainController.py b/src/controller/mainController.py index a8f4130d..cc24c47a 100644 --- a/src/controller/mainController.py +++ b/src/controller/mainController.py @@ -134,6 +134,7 @@ class Controller(object): pub.subscribe(self.manage_blocked_user, "blocked-user") pub.subscribe(self.manage_unblocked_user, "unblocked-user") pub.subscribe(self.create_buffer, "createBuffer") + pub.subscribe(self.toggle_share_settings, "toggleShare") if system == "Windows": pub.subscribe(self.invisible_shorcuts_changed, "invisible-shorcuts-changed") widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide) @@ -1657,6 +1658,10 @@ class Controller(object): if sound_to_play != None and buff not in buffer.session.settings["other_buffers"]["muted_buffers"]: self.notify(buffer.session, sound_to_play) + def toggle_share_settings(self, shareable=True): + self.view.retweet.Enable(shareable) + + def check_streams(self): if self.started == False: return From 3d452b3db89d10500c94b3d134eb82cb1889d854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corentin=20Bacqu=C3=A9-Cazenave?= Date: Wed, 26 Jan 2022 18:27:32 +0100 Subject: [PATCH 8/8] Update french translations --- src/locales/fr/LC_MESSAGES/twblue.po | 254 ++++++++++++++------------- 1 file changed, 129 insertions(+), 125 deletions(-) diff --git a/src/locales/fr/LC_MESSAGES/twblue.po b/src/locales/fr/LC_MESSAGES/twblue.po index 02c9859b..82c2c0b9 100644 --- a/src/locales/fr/LC_MESSAGES/twblue.po +++ b/src/locales/fr/LC_MESSAGES/twblue.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: TW Blue 0.94\n" -"POT-Creation-Date: 2021-12-22 18:18+0100\n" -"PO-Revision-Date: 2021-12-27 23:24+0100\n" +"POT-Creation-Date: 2022-01-26 18:25+0100\n" +"PO-Revision-Date: 2022-01-26 18:26+0100\n" "Last-Translator: Corentin Bacqué-Cazenave \n" "Language-Team: Corentin Bacqué-Cazenave \n" "Language: fr\n" @@ -21,56 +21,56 @@ msgid "This action is not supported for this buffer" msgstr "Cette action n'est pas supportée pour ce tampon" #: ../src\controller\buffers\twitter\base.py:70 -#: ../src\controller\mainController.py:337 ../src\controller\settings.py:323 +#: ../src\controller\mainController.py:338 ../src\controller\settings.py:323 msgid "Home" msgstr "Accueil" #: ../src\controller\buffers\twitter\base.py:70 -#: ../src\controller\mainController.py:339 ../src\controller\settings.py:324 +#: ../src\controller\mainController.py:340 ../src\controller\settings.py:324 msgid "Mentions" msgstr "Mentions" #: ../src\controller\buffers\twitter\base.py:70 -#: ../src\controller\mainController.py:341 +#: ../src\controller\mainController.py:342 msgid "Direct messages" msgstr "Messages" #: ../src\controller\buffers\twitter\base.py:70 -#: ../src\controller\mainController.py:343 ../src\controller\settings.py:326 +#: ../src\controller\mainController.py:344 ../src\controller\settings.py:326 msgid "Sent direct messages" msgstr "Messages envoyés" #: ../src\controller\buffers\twitter\base.py:70 -#: ../src\controller\mainController.py:345 ../src\controller\settings.py:327 +#: ../src\controller\mainController.py:346 ../src\controller\settings.py:327 msgid "Sent tweets" msgstr "Tweets envoyés" #: ../src\controller\buffers\twitter\base.py:70 -#: ../src\controller\mainController.py:347 -#: ../src\controller\mainController.py:1390 ../src\controller\settings.py:328 +#: ../src\controller\mainController.py:348 +#: ../src\controller\mainController.py:1391 ../src\controller\settings.py:328 msgid "Likes" msgstr "Favoris" #: ../src\controller\buffers\twitter\base.py:70 -#: ../src\controller\mainController.py:349 -#: ../src\controller\mainController.py:1395 ../src\controller\settings.py:329 +#: ../src\controller\mainController.py:350 +#: ../src\controller\mainController.py:1396 ../src\controller\settings.py:329 msgid "Followers" msgstr "Abonnés" #: ../src\controller\buffers\twitter\base.py:70 -#: ../src\controller\mainController.py:353 -#: ../src\controller\mainController.py:1405 ../src\controller\settings.py:331 +#: ../src\controller\mainController.py:354 +#: ../src\controller\mainController.py:1406 ../src\controller\settings.py:331 msgid "Blocked users" msgstr "Utilisateurs bloqués" #: ../src\controller\buffers\twitter\base.py:70 -#: ../src\controller\mainController.py:355 -#: ../src\controller\mainController.py:1410 ../src\controller\settings.py:332 +#: ../src\controller\mainController.py:356 +#: ../src\controller\mainController.py:1411 ../src\controller\settings.py:332 msgid "Muted users" msgstr "Utilisateurs masqués" #: ../src\controller\buffers\twitter\base.py:70 -#: ../src\controller\mainController.py:1400 ../src\controller\settings.py:330 +#: ../src\controller\mainController.py:1401 ../src\controller\settings.py:330 msgid "Friends" msgstr "Abonnements" @@ -129,46 +129,50 @@ msgstr "%s éléments récupérés" msgid "This buffer is not a timeline; it can't be deleted." msgstr "Ce tampon n'est pas une chronologie ; Impossible de le supprimé." -#: ../src\controller\buffers\twitter\base.py:404 +#: ../src\controller\buffers\twitter\base.py:410 msgid "Reply to {arg0}" msgstr "Répondre à {arg0}" -#: ../src\controller\buffers\twitter\base.py:406 +#: ../src\controller\buffers\twitter\base.py:412 #: ../src\keystrokeEditor\constants.py:11 ../src\wxUI\buffers\base.py:27 msgid "Reply" msgstr "Répondre" -#: ../src\controller\buffers\twitter\base.py:407 +#: ../src\controller\buffers\twitter\base.py:413 msgid "Reply to %s" msgstr "Répondre à %s" -#: ../src\controller\buffers\twitter\base.py:430 +#: ../src\controller\buffers\twitter\base.py:436 #: ../src\controller\buffers\twitter\directMessages.py:124 msgid "New direct message" msgstr "Nouveau message" -#: ../src\controller\buffers\twitter\base.py:430 +#: ../src\controller\buffers\twitter\base.py:436 #: ../src\controller\messages.py:268 msgid "Direct message to %s" msgstr "Message à %s" -#: ../src\controller\buffers\twitter\base.py:461 +#: ../src\controller\buffers\twitter\base.py:452 +msgid "This action is not supported on protected accounts." +msgstr "Cette action n'est pas supportée pour les comptes protégés." + +#: ../src\controller\buffers\twitter\base.py:469 msgid "Add your comment to the tweet" msgstr "Ajoutez votre commentaire pour le tweet" -#: ../src\controller\buffers\twitter\base.py:461 +#: ../src\controller\buffers\twitter\base.py:469 msgid "Quote" msgstr "Citer" -#: ../src\controller\buffers\twitter\base.py:522 +#: ../src\controller\buffers\twitter\base.py:533 msgid "Opening URL..." msgstr "Ouverture de l'URL..." -#: ../src\controller\buffers\twitter\base.py:559 +#: ../src\controller\buffers\twitter\base.py:570 msgid "User details" msgstr "Détails de l'utilisateur" -#: ../src\controller\buffers\twitter\base.py:580 +#: ../src\controller\buffers\twitter\base.py:591 msgid "Opening item in web browser..." msgstr "Ouverture de l'élément dans le navigateur Web..." @@ -207,85 +211,85 @@ msgstr "{0} nouvel abonné" msgid "This action is not supported in the buffer, yet." msgstr "Cette action n'est pas supportée pour le tampon actuel" -#: ../src\controller\mainController.py:277 +#: ../src\controller\mainController.py:278 msgid "Ready" msgstr "Prêt" -#: ../src\controller\mainController.py:351 +#: ../src\controller\mainController.py:352 msgid "Following" msgstr "Abonnements" -#: ../src\controller\mainController.py:356 +#: ../src\controller\mainController.py:357 msgid "Timelines" msgstr "Chronologies" -#: ../src\controller\mainController.py:359 -#: ../src\controller\mainController.py:883 -#: ../src\controller\mainController.py:1582 +#: ../src\controller\mainController.py:360 +#: ../src\controller\mainController.py:884 +#: ../src\controller\mainController.py:1583 msgid "Timeline for {}" msgstr "Chronologie de {}" -#: ../src\controller\mainController.py:360 +#: ../src\controller\mainController.py:361 msgid "Likes timelines" msgstr "Chronologies des favoris" -#: ../src\controller\mainController.py:363 -#: ../src\controller\mainController.py:902 -#: ../src\controller\mainController.py:1584 +#: ../src\controller\mainController.py:364 +#: ../src\controller\mainController.py:903 +#: ../src\controller\mainController.py:1585 msgid "Likes for {}" msgstr "Favoris de {}" -#: ../src\controller\mainController.py:364 +#: ../src\controller\mainController.py:365 msgid "Followers timelines" msgstr "Chronologies des abonnés" -#: ../src\controller\mainController.py:367 -#: ../src\controller\mainController.py:921 -#: ../src\controller\mainController.py:1586 +#: ../src\controller\mainController.py:368 +#: ../src\controller\mainController.py:922 +#: ../src\controller\mainController.py:1587 msgid "Followers for {}" msgstr "Abonnés de {}" -#: ../src\controller\mainController.py:368 +#: ../src\controller\mainController.py:369 msgid "Following timelines" msgstr "Chronologies des abonnements" -#: ../src\controller\mainController.py:371 -#: ../src\controller\mainController.py:940 -#: ../src\controller\mainController.py:1588 +#: ../src\controller\mainController.py:372 +#: ../src\controller\mainController.py:941 +#: ../src\controller\mainController.py:1589 msgid "Friends for {}" msgstr "Abonnements de {}" -#: ../src\controller\mainController.py:372 ../src\wxUI\dialogs\lists.py:13 +#: ../src\controller\mainController.py:373 ../src\wxUI\dialogs\lists.py:13 msgid "Lists" msgstr "Listes" -#: ../src\controller\mainController.py:375 -#: ../src\controller\mainController.py:1422 +#: ../src\controller\mainController.py:376 +#: ../src\controller\mainController.py:1423 msgid "List for {}" msgstr "Liste {}" -#: ../src\controller\mainController.py:376 +#: ../src\controller\mainController.py:377 msgid "Searches" msgstr "Recherches" -#: ../src\controller\mainController.py:379 -#: ../src\controller\mainController.py:426 -#: ../src\controller\mainController.py:431 +#: ../src\controller\mainController.py:380 +#: ../src\controller\mainController.py:427 +#: ../src\controller\mainController.py:432 msgid "Search for {}" msgstr "Recherche de {}" -#: ../src\controller\mainController.py:381 -#: ../src\controller\mainController.py:982 -#: ../src\controller\mainController.py:1590 +#: ../src\controller\mainController.py:382 +#: ../src\controller\mainController.py:983 +#: ../src\controller\mainController.py:1591 msgid "Trending topics for %s" msgstr "Tendances pour %s" -#: ../src\controller\mainController.py:448 -#: ../src\controller\mainController.py:464 -#: ../src\controller\mainController.py:1080 -#: ../src\controller\mainController.py:1099 -#: ../src\controller\mainController.py:1118 -#: ../src\controller\mainController.py:1137 +#: ../src\controller\mainController.py:449 +#: ../src\controller\mainController.py:465 +#: ../src\controller\mainController.py:1081 +#: ../src\controller\mainController.py:1100 +#: ../src\controller\mainController.py:1119 +#: ../src\controller\mainController.py:1138 msgid "" "No session is currently in focus. Focus a session with the next or previous " "session shortcut." @@ -293,152 +297,152 @@ msgstr "" "Aucune session n'a actuellement le focus. Sélectionnez-en une avec le " "raccourci pour la session précédente ou suivante." -#: ../src\controller\mainController.py:452 +#: ../src\controller\mainController.py:453 msgid "Empty buffer." msgstr "Tampon vide." -#: ../src\controller\mainController.py:459 +#: ../src\controller\mainController.py:460 msgid "{0} not found." msgstr "{0} introuvable." -#: ../src\controller\mainController.py:469 +#: ../src\controller\mainController.py:470 msgid "Filters cannot be applied on this buffer" msgstr "Les filtres ne peuvent pas s'appliquer à ce tampon" -#: ../src\controller\mainController.py:522 -#: ../src\controller\mainController.py:539 -#: ../src\controller\mainController.py:568 +#: ../src\controller\mainController.py:523 +#: ../src\controller\mainController.py:540 +#: ../src\controller\mainController.py:569 msgid "Select the user" msgstr "Sélectionnez l'utilisateur" -#: ../src\controller\mainController.py:753 +#: ../src\controller\mainController.py:754 msgid "Add an user alias" msgstr "Ajoute un alias pour l'utilisateur" -#: ../src\controller\mainController.py:761 +#: ../src\controller\mainController.py:762 msgid "Alias has been set correctly for {}." msgstr "L'alias pour {} a correctement été définie" -#: ../src\controller\mainController.py:829 ../src\controller\messages.py:327 +#: ../src\controller\mainController.py:830 ../src\controller\messages.py:327 msgid "MMM D, YYYY. H:m" msgstr "D MMM YYYY à H:m" -#: ../src\controller\mainController.py:957 +#: ../src\controller\mainController.py:958 msgid "Conversation with {0}" msgstr "Conversation avec {0}" -#: ../src\controller\mainController.py:998 -#: ../src\controller\mainController.py:1015 +#: ../src\controller\mainController.py:999 +#: ../src\controller\mainController.py:1016 msgid "There are no coordinates in this tweet" msgstr "Il n'y a aucune coordonnée dans ce tweet" -#: ../src\controller\mainController.py:1000 -#: ../src\controller\mainController.py:1019 +#: ../src\controller\mainController.py:1001 +#: ../src\controller\mainController.py:1020 msgid "Error decoding coordinates. Try again later." msgstr "Erreur pendant le décodage des coordonnées. Réessayez plus tard." -#: ../src\controller\mainController.py:1004 +#: ../src\controller\mainController.py:1005 msgid "Unable to find address in OpenStreetMap." msgstr "Impossible de trouver l'adresse dans OpenStreetMap" -#: ../src\controller\mainController.py:1017 +#: ../src\controller\mainController.py:1018 msgid "There are no results for the coordinates in this tweet" msgstr "Il n'y a aucun résultat pour les coordonnées dans ce tweet" -#: ../src\controller\mainController.py:1128 -#: ../src\controller\mainController.py:1147 +#: ../src\controller\mainController.py:1129 +#: ../src\controller\mainController.py:1148 msgid "%s, %s of %s" msgstr "%s, %s de %s" -#: ../src\controller\mainController.py:1130 -#: ../src\controller\mainController.py:1149 -#: ../src\controller\mainController.py:1174 -#: ../src\controller\mainController.py:1199 +#: ../src\controller\mainController.py:1131 +#: ../src\controller\mainController.py:1150 +#: ../src\controller\mainController.py:1175 +#: ../src\controller\mainController.py:1200 msgid "%s. Empty" msgstr "%s. Vide" -#: ../src\controller\mainController.py:1162 -#: ../src\controller\mainController.py:1166 -#: ../src\controller\mainController.py:1187 +#: ../src\controller\mainController.py:1163 +#: ../src\controller\mainController.py:1167 +#: ../src\controller\mainController.py:1188 msgid "{0}: This account is not logged into Twitter." msgstr "{0}: Ce compte n'est pas connecté à twitter." -#: ../src\controller\mainController.py:1172 -#: ../src\controller\mainController.py:1197 +#: ../src\controller\mainController.py:1173 +#: ../src\controller\mainController.py:1198 msgid "%s. %s, %s of %s" msgstr "%s. %s, %s de %s" -#: ../src\controller\mainController.py:1191 +#: ../src\controller\mainController.py:1192 msgid "{0}: This account is not logged into twitter." msgstr "{0}: Ce compte n'est pas connecté à twitter." -#: ../src\controller\mainController.py:1416 +#: ../src\controller\mainController.py:1417 msgid "This list is already opened" msgstr "Cette liste est déjà ouverte" -#: ../src\controller\mainController.py:1446 -#: ../src\controller\mainController.py:1462 +#: ../src\controller\mainController.py:1447 +#: ../src\controller\mainController.py:1463 msgid "" "An error happened while trying to connect to the server. Please try later." msgstr "" "Une erreur s'est produite en essayant de se connecter au serveur. Veuillez " "réessayer plus tard." -#: ../src\controller\mainController.py:1498 +#: ../src\controller\mainController.py:1499 msgid "The auto-reading of new tweets is enabled for this buffer" msgstr "La lecture automatique des nouveaux tweets est activée pour ce tampon" -#: ../src\controller\mainController.py:1501 +#: ../src\controller\mainController.py:1502 msgid "The auto-reading of new tweets is disabled for this buffer" msgstr "" "La lecture automatique des nouveaux tweets est désactivée pour ce tampon" -#: ../src\controller\mainController.py:1508 +#: ../src\controller\mainController.py:1509 msgid "Session mute on" msgstr "Session muet" -#: ../src\controller\mainController.py:1511 +#: ../src\controller\mainController.py:1512 msgid "Session mute off" msgstr "Session non muet" -#: ../src\controller\mainController.py:1519 +#: ../src\controller\mainController.py:1520 msgid "Buffer mute on" msgstr "Tampon muet" -#: ../src\controller\mainController.py:1522 +#: ../src\controller\mainController.py:1523 msgid "Buffer mute off" msgstr "Tampon non muet" -#: ../src\controller\mainController.py:1542 +#: ../src\controller\mainController.py:1543 msgid "Copied" msgstr "Copié" -#: ../src\controller\mainController.py:1572 +#: ../src\controller\mainController.py:1573 msgid "Unable to update this buffer." msgstr "Impossible de mettre à jour ce tampon." -#: ../src\controller\mainController.py:1575 +#: ../src\controller\mainController.py:1576 msgid "Updating buffer..." msgstr "Actualisation..." -#: ../src\controller\mainController.py:1578 +#: ../src\controller\mainController.py:1579 msgid "{0} items retrieved" msgstr "{0} éléments récupérés" -#: ../src\controller\mainController.py:1597 -#: ../src\controller\mainController.py:1617 +#: ../src\controller\mainController.py:1598 +#: ../src\controller\mainController.py:1618 msgid "Invalid buffer" msgstr "Tampon invalide" -#: ../src\controller\mainController.py:1608 +#: ../src\controller\mainController.py:1609 msgid "Picture {0}" msgstr "Photo {0}" -#: ../src\controller\mainController.py:1609 +#: ../src\controller\mainController.py:1610 msgid "Select the picture" msgstr "Sélectionner la photo" -#: ../src\controller\mainController.py:1628 +#: ../src\controller\mainController.py:1629 msgid "Unable to extract text" msgstr "Impossible d'extraire le texte" @@ -454,7 +458,7 @@ msgstr "%s - %s/%d caractères" msgid "View item" msgstr "Voir l'élément" -#: ../src\controller\messages.py:379 +#: ../src\controller\messages.py:380 msgid "Link copied to clipboard." msgstr "Lien copié dans le Presse-papiers" @@ -1968,27 +1972,27 @@ msgstr "" "elle va être détruite et recréée automatiquement. Si ce problème perciste, " "veuillez envoyer le journal d'erreur aux développeurs de {app}." -#: ../src\sessions\twitter\compose.py:38 ../src\sessions\twitter\compose.py:81 -#: ../src\sessions\twitter\compose.py:146 -#: ../src\sessions\twitter\compose.py:155 +#: ../src\sessions\twitter\compose.py:25 ../src\sessions\twitter\compose.py:68 +#: ../src\sessions\twitter\compose.py:133 +#: ../src\sessions\twitter\compose.py:142 #: ../src\sessions\twitter\templates.py:26 msgid "dddd, MMMM D, YYYY H:m:s" msgstr "dddd D MMMM YYYY à H:m:s" -#: ../src\sessions\twitter\compose.py:89 ../src\sessions\twitter\compose.py:91 +#: ../src\sessions\twitter\compose.py:76 ../src\sessions\twitter\compose.py:78 msgid "Dm to %s " msgstr "Mp à %s" -#: ../src\sessions\twitter\compose.py:130 +#: ../src\sessions\twitter\compose.py:117 msgid "{0}. Quoted tweet from @{1}: {2}" msgstr "{0}. Tweet de @{1} cité : {2}" -#: ../src\sessions\twitter\compose.py:157 -#: ../src\sessions\twitter\compose.py:159 +#: ../src\sessions\twitter\compose.py:144 +#: ../src\sessions\twitter\compose.py:146 msgid "Unavailable" msgstr "Indisponible" -#: ../src\sessions\twitter\compose.py:160 +#: ../src\sessions\twitter\compose.py:147 msgid "" "%s (@%s). %s followers, %s friends, %s tweets. Last tweeted %s. Joined " "Twitter %s" @@ -1996,15 +2000,15 @@ msgstr "" "%s (@%s). %s abonnés, %s abonnements, %s tweets. Dernier tweet envoyé %s. À " "rejoint Twitter %s" -#: ../src\sessions\twitter\compose.py:164 +#: ../src\sessions\twitter\compose.py:151 msgid "No description available" msgstr "Aucune description disponible" -#: ../src\sessions\twitter\compose.py:168 +#: ../src\sessions\twitter\compose.py:155 msgid "private" msgstr "privé" -#: ../src\sessions\twitter\compose.py:169 +#: ../src\sessions\twitter\compose.py:156 msgid "public" msgstr "public" @@ -2018,8 +2022,8 @@ msgstr "%s erreur. Raison: %s" msgid "%s succeeded." msgstr "%s réussi." -#: ../src\sessions\twitter\session.py:450 -#: ../src\sessions\twitter\session.py:528 +#: ../src\sessions\twitter\session.py:451 +#: ../src\sessions\twitter\session.py:529 msgid "Deleted account" msgstr "Compte supprimé" @@ -2043,23 +2047,23 @@ msgstr "" "$display_name (@$screen_name). $followers abonnés, $following abonnements, " "$tweets tweets. À rejoint Twitter $created_at." -#: ../src\sessions\twitter\templates.py:50 +#: ../src\sessions\twitter\templates.py:54 msgid "Image description: {}." msgstr "Description de l'image: {}." -#: ../src\sessions\twitter\utils.py:229 +#: ../src\sessions\twitter\utils.py:243 msgid "Sorry, you are not authorised to see this status." msgstr "Désolé, vous n'êtes pas autorisé à voir ce Tweet." -#: ../src\sessions\twitter\utils.py:231 +#: ../src\sessions\twitter\utils.py:245 msgid "No status found with that ID" msgstr "Aucun Tweet trouvée avec cet ID" -#: ../src\sessions\twitter\utils.py:233 +#: ../src\sessions\twitter\utils.py:247 msgid "Error {0}" msgstr "Erreur {0}" -#: ../src\sessions\twitter\utils.py:260 +#: ../src\sessions\twitter\utils.py:274 msgid "{user_1}, {user_2} and {all_users} more: {text}" msgstr "{user_1}, {user_2} et {all_users} de plus : {text}"