We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Session expiring do not display a localized logout message.
# Bug

## Description

Like #2391, if a source has their session expire, they will not be shown a localized message when they log out.

## Steps to Reproduce

Set session expire to 30 seconds. Log in. Set locale to not-english. Wait 30 seconds. Refresh. See no-localized flashed message.

## Expected Behavior

The logout message is localized.

## Actual Behavior

It is not.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `securedrop/source_app/__init__.py`
Content:
```
1 from datetime import datetime, timedelta
2 from flask import (Flask, render_template, flash, Markup, request, g, session,
3                    url_for, redirect)
4 from flask_babel import gettext
5 from flask_assets import Environment
6 from flask_wtf.csrf import CSRFProtect
7 from jinja2 import evalcontextfilter
8 from os import path
9 from sqlalchemy.orm.exc import NoResultFound
10 
11 import crypto_util
12 import i18n
13 import store
14 import template_filters
15 import version
16 
17 from db import Source, db_session
18 from request_that_secures_file_uploads import RequestThatSecuresFileUploads
19 from source_app import main, info, api
20 from source_app.decorators import ignore_static
21 from source_app.utils import logged_in
22 
23 
24 def create_app(config):
25     app = Flask(__name__,
26                 template_folder=config.SOURCE_TEMPLATES_DIR,
27                 static_folder=path.join(config.SECUREDROP_ROOT, 'static'))
28     app.request_class = RequestThatSecuresFileUploads
29     app.config.from_object(config.SourceInterfaceFlaskConfig)
30 
31     # The default CSRF token expiration is 1 hour. Since large uploads can
32     # take longer than an hour over Tor, we increase the valid window to 24h.
33     app.config['WTF_CSRF_TIME_LIMIT'] = 60 * 60 * 24
34     CSRFProtect(app)
35 
36     assets = Environment(app)
37     app.config['assets'] = assets
38 
39     i18n.setup_app(app)
40 
41     app.jinja_env.trim_blocks = True
42     app.jinja_env.lstrip_blocks = True
43     app.jinja_env.globals['version'] = version.__version__
44     if getattr(config, 'CUSTOM_HEADER_IMAGE', None):
45         app.jinja_env.globals['header_image'] = config.CUSTOM_HEADER_IMAGE
46         app.jinja_env.globals['use_custom_header_image'] = True
47     else:
48         app.jinja_env.globals['header_image'] = 'logo.png'
49         app.jinja_env.globals['use_custom_header_image'] = False
50 
51     app.jinja_env.filters['rel_datetime_format'] = \
52         template_filters.rel_datetime_format
53     app.jinja_env.filters['nl2br'] = evalcontextfilter(template_filters.nl2br)
54     app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat
55 
56     for module in [main, info, api]:
57         app.register_blueprint(module.make_blueprint(config))
58 
59     @app.before_request
60     @ignore_static
61     def check_tor2web():
62         # ignore_static here so we only flash a single message warning
63         # about Tor2Web, corresponding to the initial page load.
64         if 'X-tor2web' in request.headers:
65             flash(Markup(gettext(
66                 '<strong>WARNING:</strong> You appear to be using Tor2Web. '
67                 'This <strong>does not</strong> provide anonymity. '
68                 '<a href="{url}">Why is this dangerous?</a>')
69                 .format(url=url_for('info.tor2web_warning'))),
70                   "banner-warning")
71 
72     @app.before_request
73     @ignore_static
74     def setup_g():
75         """Store commonly used values in Flask's special g object"""
76         g.locale = i18n.get_locale()
77         g.text_direction = i18n.get_text_direction(g.locale)
78         g.html_lang = i18n.locale_to_rfc_5646(g.locale)
79         g.locales = i18n.get_locale2name()
80 
81         if 'expires' in session and datetime.utcnow() >= session['expires']:
82             session.clear()
83             msg = render_template('session_timeout.html')
84             flash(Markup(msg), "important")
85 
86         session['expires'] = datetime.utcnow() + \
87             timedelta(minutes=getattr(config,
88                                       'SESSION_EXPIRATION_MINUTES',
89                                       30))
90 
91         # ignore_static here because `crypto_util.hash_codename` is scrypt
92         # (very time consuming), and we don't need to waste time running if
93         # we're just serving a static resource that won't need to access
94         # these common values.
95         if logged_in():
96             g.codename = session['codename']
97             g.filesystem_id = crypto_util.hash_codename(g.codename)
98             try:
99                 g.source = Source.query \
100                             .filter(Source.filesystem_id == g.filesystem_id) \
101                             .one()
102             except NoResultFound as e:
103                 app.logger.error(
104                     "Found no Sources when one was expected: %s" %
105                     (e,))
106                 del session['logged_in']
107                 del session['codename']
108                 return redirect(url_for('main.index'))
109             g.loc = store.path(g.filesystem_id)
110 
111     @app.teardown_appcontext
112     def shutdown_session(exception=None):
113         """Automatically remove database sessions at the end of the request, or
114         when the application shuts down"""
115         db_session.remove()
116 
117     @app.errorhandler(404)
118     def page_not_found(error):
119         return render_template('notfound.html'), 404
120 
121     @app.errorhandler(500)
122     def internal_error(error):
123         return render_template('error.html'), 500
124 
125     return app
126
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/securedrop/source_app/__init__.py b/securedrop/source_app/__init__.py
--- a/securedrop/source_app/__init__.py
+++ b/securedrop/source_app/__init__.py
@@ -79,8 +79,11 @@
         g.locales = i18n.get_locale2name()
 
         if 'expires' in session and datetime.utcnow() >= session['expires']:
-            session.clear()
             msg = render_template('session_timeout.html')
+
+            # clear the session after we render the message so it's localized
+            session.clear()
+
             flash(Markup(msg), "important")
 
         session['expires'] = datetime.utcnow() + \

{"golden_diff": "diff --git a/securedrop/source_app/__init__.py b/securedrop/source_app/__init__.py\n--- a/securedrop/source_app/__init__.py\n+++ b/securedrop/source_app/__init__.py\n@@ -79,8 +79,11 @@\n         g.locales = i18n.get_locale2name()\n \n         if 'expires' in session and datetime.utcnow() >= session['expires']:\n-            session.clear()\n             msg = render_template('session_timeout.html')\n+\n+            # clear the session after we render the message so it's localized\n+            session.clear()\n+\n             flash(Markup(msg), \"important\")\n \n         session['expires'] = datetime.utcnow() + \\\n", "issue": "Session expiring do not display a localized logout message.\n# Bug\r\n\r\n## Description\r\n\r\nLike #2391, if a source has their session expire, they will not be shown a localized message when they log out.\r\n\r\n## Steps to Reproduce\r\n\r\nSet session expire to 30 seconds. Log in. Set locale to not-english. Wait 30 seconds. Refresh. See no-localized flashed message.\r\n\r\n## Expected Behavior\r\n\r\nThe logout message is localized.\r\n\r\n## Actual Behavior\r\n\r\nIt is not.\n", "before_files": [{"content": "from datetime import datetime, timedelta\nfrom flask import (Flask, render_template, flash, Markup, request, g, session,\n                   url_for, redirect)\nfrom flask_babel import gettext\nfrom flask_assets import Environment\nfrom flask_wtf.csrf import CSRFProtect\nfrom jinja2 import evalcontextfilter\nfrom os import path\nfrom sqlalchemy.orm.exc import NoResultFound\n\nimport crypto_util\nimport i18n\nimport store\nimport template_filters\nimport version\n\nfrom db import Source, db_session\nfrom request_that_secures_file_uploads import RequestThatSecuresFileUploads\nfrom source_app import main, info, api\nfrom source_app.decorators import ignore_static\nfrom source_app.utils import logged_in\n\n\ndef create_app(config):\n    app = Flask(__name__,\n                template_folder=config.SOURCE_TEMPLATES_DIR,\n                static_folder=path.join(config.SECUREDROP_ROOT, 'static'))\n    app.request_class = RequestThatSecuresFileUploads\n    app.config.from_object(config.SourceInterfaceFlaskConfig)\n\n    # The default CSRF token expiration is 1 hour. Since large uploads can\n    # take longer than an hour over Tor, we increase the valid window to 24h.\n    app.config['WTF_CSRF_TIME_LIMIT'] = 60 * 60 * 24\n    CSRFProtect(app)\n\n    assets = Environment(app)\n    app.config['assets'] = assets\n\n    i18n.setup_app(app)\n\n    app.jinja_env.trim_blocks = True\n    app.jinja_env.lstrip_blocks = True\n    app.jinja_env.globals['version'] = version.__version__\n    if getattr(config, 'CUSTOM_HEADER_IMAGE', None):\n        app.jinja_env.globals['header_image'] = config.CUSTOM_HEADER_IMAGE\n        app.jinja_env.globals['use_custom_header_image'] = True\n    else:\n        app.jinja_env.globals['header_image'] = 'logo.png'\n        app.jinja_env.globals['use_custom_header_image'] = False\n\n    app.jinja_env.filters['rel_datetime_format'] = \\\n        template_filters.rel_datetime_format\n    app.jinja_env.filters['nl2br'] = evalcontextfilter(template_filters.nl2br)\n    app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat\n\n    for module in [main, info, api]:\n        app.register_blueprint(module.make_blueprint(config))\n\n    @app.before_request\n    @ignore_static\n    def check_tor2web():\n        # ignore_static here so we only flash a single message warning\n        # about Tor2Web, corresponding to the initial page load.\n        if 'X-tor2web' in request.headers:\n            flash(Markup(gettext(\n                '<strong>WARNING:</strong> You appear to be using Tor2Web. '\n                'This <strong>does not</strong> provide anonymity. '\n                '<a href=\"{url}\">Why is this dangerous?</a>')\n                .format(url=url_for('info.tor2web_warning'))),\n                  \"banner-warning\")\n\n    @app.before_request\n    @ignore_static\n    def setup_g():\n        \"\"\"Store commonly used values in Flask's special g object\"\"\"\n        g.locale = i18n.get_locale()\n        g.text_direction = i18n.get_text_direction(g.locale)\n        g.html_lang = i18n.locale_to_rfc_5646(g.locale)\n        g.locales = i18n.get_locale2name()\n\n        if 'expires' in session and datetime.utcnow() >= session['expires']:\n            session.clear()\n            msg = render_template('session_timeout.html')\n            flash(Markup(msg), \"important\")\n\n        session['expires'] = datetime.utcnow() + \\\n            timedelta(minutes=getattr(config,\n                                      'SESSION_EXPIRATION_MINUTES',\n                                      30))\n\n        # ignore_static here because `crypto_util.hash_codename` is scrypt\n        # (very time consuming), and we don't need to waste time running if\n        # we're just serving a static resource that won't need to access\n        # these common values.\n        if logged_in():\n            g.codename = session['codename']\n            g.filesystem_id = crypto_util.hash_codename(g.codename)\n            try:\n                g.source = Source.query \\\n                            .filter(Source.filesystem_id == g.filesystem_id) \\\n                            .one()\n            except NoResultFound as e:\n                app.logger.error(\n                    \"Found no Sources when one was expected: %s\" %\n                    (e,))\n                del session['logged_in']\n                del session['codename']\n                return redirect(url_for('main.index'))\n            g.loc = store.path(g.filesystem_id)\n\n    @app.teardown_appcontext\n    def shutdown_session(exception=None):\n        \"\"\"Automatically remove database sessions at the end of the request, or\n        when the application shuts down\"\"\"\n        db_session.remove()\n\n    @app.errorhandler(404)\n    def page_not_found(error):\n        return render_template('notfound.html'), 404\n\n    @app.errorhandler(500)\n    def internal_error(error):\n        return render_template('error.html'), 500\n\n    return app\n", "path": "securedrop/source_app/__init__.py"}], "after_files": [{"content": "from datetime import datetime, timedelta\nfrom flask import (Flask, render_template, flash, Markup, request, g, session,\n                   url_for, redirect)\nfrom flask_babel import gettext\nfrom flask_assets import Environment\nfrom flask_wtf.csrf import CSRFProtect\nfrom jinja2 import evalcontextfilter\nfrom os import path\nfrom sqlalchemy.orm.exc import NoResultFound\n\nimport crypto_util\nimport i18n\nimport store\nimport template_filters\nimport version\n\nfrom db import Source, db_session\nfrom request_that_secures_file_uploads import RequestThatSecuresFileUploads\nfrom source_app import main, info, api\nfrom source_app.decorators import ignore_static\nfrom source_app.utils import logged_in\n\n\ndef create_app(config):\n    app = Flask(__name__,\n                template_folder=config.SOURCE_TEMPLATES_DIR,\n                static_folder=path.join(config.SECUREDROP_ROOT, 'static'))\n    app.request_class = RequestThatSecuresFileUploads\n    app.config.from_object(config.SourceInterfaceFlaskConfig)\n\n    # The default CSRF token expiration is 1 hour. Since large uploads can\n    # take longer than an hour over Tor, we increase the valid window to 24h.\n    app.config['WTF_CSRF_TIME_LIMIT'] = 60 * 60 * 24\n    CSRFProtect(app)\n\n    assets = Environment(app)\n    app.config['assets'] = assets\n\n    i18n.setup_app(app)\n\n    app.jinja_env.trim_blocks = True\n    app.jinja_env.lstrip_blocks = True\n    app.jinja_env.globals['version'] = version.__version__\n    if getattr(config, 'CUSTOM_HEADER_IMAGE', None):\n        app.jinja_env.globals['header_image'] = config.CUSTOM_HEADER_IMAGE\n        app.jinja_env.globals['use_custom_header_image'] = True\n    else:\n        app.jinja_env.globals['header_image'] = 'logo.png'\n        app.jinja_env.globals['use_custom_header_image'] = False\n\n    app.jinja_env.filters['rel_datetime_format'] = \\\n        template_filters.rel_datetime_format\n    app.jinja_env.filters['nl2br'] = evalcontextfilter(template_filters.nl2br)\n    app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat\n\n    for module in [main, info, api]:\n        app.register_blueprint(module.make_blueprint(config))\n\n    @app.before_request\n    @ignore_static\n    def check_tor2web():\n        # ignore_static here so we only flash a single message warning\n        # about Tor2Web, corresponding to the initial page load.\n        if 'X-tor2web' in request.headers:\n            flash(Markup(gettext(\n                '<strong>WARNING:</strong> You appear to be using Tor2Web. '\n                'This <strong>does not</strong> provide anonymity. '\n                '<a href=\"{url}\">Why is this dangerous?</a>')\n                .format(url=url_for('info.tor2web_warning'))),\n                  \"banner-warning\")\n\n    @app.before_request\n    @ignore_static\n    def setup_g():\n        \"\"\"Store commonly used values in Flask's special g object\"\"\"\n        g.locale = i18n.get_locale()\n        g.text_direction = i18n.get_text_direction(g.locale)\n        g.html_lang = i18n.locale_to_rfc_5646(g.locale)\n        g.locales = i18n.get_locale2name()\n\n        if 'expires' in session and datetime.utcnow() >= session['expires']:\n            msg = render_template('session_timeout.html')\n\n            # clear the session after we render the message so it's localized\n            session.clear()\n\n            flash(Markup(msg), \"important\")\n\n        session['expires'] = datetime.utcnow() + \\\n            timedelta(minutes=getattr(config,\n                                      'SESSION_EXPIRATION_MINUTES',\n                                      30))\n\n        # ignore_static here because `crypto_util.hash_codename` is scrypt\n        # (very time consuming), and we don't need to waste time running if\n        # we're just serving a static resource that won't need to access\n        # these common values.\n        if logged_in():\n            g.codename = session['codename']\n            g.filesystem_id = crypto_util.hash_codename(g.codename)\n            try:\n                g.source = Source.query \\\n                            .filter(Source.filesystem_id == g.filesystem_id) \\\n                            .one()\n            except NoResultFound as e:\n                app.logger.error(\n                    \"Found no Sources when one was expected: %s\" %\n                    (e,))\n                del session['logged_in']\n                del session['codename']\n                return redirect(url_for('main.index'))\n            g.loc = store.path(g.filesystem_id)\n\n    @app.teardown_appcontext\n    def shutdown_session(exception=None):\n        \"\"\"Automatically remove database sessions at the end of the request, or\n        when the application shuts down\"\"\"\n        db_session.remove()\n\n    @app.errorhandler(404)\n    def page_not_found(error):\n        return render_template('notfound.html'), 404\n\n    @app.errorhandler(500)\n    def internal_error(error):\n        return render_template('error.html'), 500\n\n    return app\n", "path": "securedrop/source_app/__init__.py"}]}