def create_multi_sign_request(data: dict) -> dict: """ View to create requests for collectively signing a document :param data: The document to sign, the owner of the document, and the emails of the users invited to sign the doc. :return: A message about the result of the procedure """ if 'mail' not in session or not current_app.is_whitelisted(session['eppn']): return {'error': True, 'message': gettext('Unauthorized')} if session['mail'] != data['owner']: current_app.logger.error(f"User {session['mail']} is trying to create an invitation as {data['owner']}") return {'error': True, 'message': gettext("You cannot invite as %(owner)s") % {'owner': data['owner']}} for invite in data['invites']: invite['email'] = invite['email'].lower() try: current_app.logger.info(f"Creating multi signature request for user {session['eppn']}") owner = {'name': session['displayName'], 'email': data['owner']} invites = current_app.doc_store.add_document(data['document'], owner, data['invites'], data['sendsigned']) except Exception as e: current_app.logger.error(f'Problem processing multi sign request: {e}') return {'error': True, 'message': gettext('Problem creating invitation to sign, please try again')} recipients = [f"{invite['name']} <{invite['email']}>" for invite in invites] if len(recipients) > 0: try: doc_name = data['document']['name'] invited_link = url_for('edusign.get_index', _external=True) mail_context = { 'document_name': doc_name, 'inviter_email': f"{owner['email']}", 'inviter_name': f"{owner['name']}", 'invited_link': invited_link, 'text': data['text'], } mail_context_html = mail_context.copy() mail_context_html['inviter_name_and_email'] = f"{owner['name']} <{owner['email']}>" with force_locale('en'): subject_en = gettext('You have been invited to sign "%(document_name)s"') % {'document_name': doc_name} body_txt_en = render_template('invitation_email.txt.jinja2', **mail_context) body_html_en = render_template('invitation_email.html.jinja2', **mail_context_html) with force_locale('sv'): subject_sv = gettext('You have been invited to sign "%(document_name)s"') % {'document_name': doc_name} body_txt_sv = render_template('invitation_email.txt.jinja2', **mail_context) body_html_sv = render_template('invitation_email.html.jinja2', **mail_context_html) sendmail(recipients, subject_en, subject_sv, body_txt_en, body_html_en, body_txt_sv, body_html_sv) except Exception as e: current_app.doc_store.remove_document(uuid.UUID(data['document']['key']), force=True) current_app.logger.error(f'Problem sending invitation email: {e}') return {'error': True, 'message': gettext('There was a problem and the invitation email(s) were not sent')} message = gettext("Success sending invitations to sign") return {'message': message}
def send_multisign_reminder(data: dict) -> dict: """ Send emails to remind people to sign some document :param data: The key of the document pending signatures :return: A message about the result of the procedure """ try: pending = current_app.doc_store.get_pending_invites(uuid.UUID(data['key'])) docname = current_app.doc_store.get_document_name(uuid.UUID(data['key'])) except Exception as e: current_app.logger.error(f'Problem finding users pending to multi sign: {e}') return {'error': True, 'message': gettext('Problem finding the users pending to multi sign')} if not pending: current_app.logger.error(f"Could not find users pending signing the multi sign request {data['key']}") return {'error': True, 'message': gettext('Problem finding the users pending to sign')} if not docname: current_app.logger.error(f"Could not find document {data['key']} pending signing the multi sign request") return {'error': True, 'message': gettext('Could not find the document')} recipients = [ f"{invite['name']} <{invite['email']}>" for invite in pending if not invite['signed'] and not invite['declined'] ] if len(recipients) > 0: try: invited_link = url_for('edusign.get_index', _external=True) mail_context = { 'document_name': docname, 'inviter__email': f"{session['mail']}", 'inviter_name': f"{session['displayName']}", 'invited_link': invited_link, 'text': 'text' in data and data['text'] or "", } mail_context_html = mail_context.copy() mail_context_html['inviter_name_and_email'] = f"{session['displayName']} <{session['mail']}>" with force_locale('en'): subject_en = gettext('A reminder to sign "%(document_name)s"') % {'document_name': docname} body_txt_en = render_template('reminder_email.txt.jinja2', **mail_context) body_html_en = render_template('reminder_email.html.jinja2', **mail_context_html) with force_locale('sv'): subject_sv = gettext('A reminder to sign "%(document_name)s"') % {'document_name': docname} body_txt_sv = render_template('reminder_email.txt.jinja2', **mail_context) body_html_sv = render_template('reminder_email.html.jinja2', **mail_context_html) sendmail(recipients, subject_en, subject_sv, body_txt_en, body_html_en, body_txt_sv, body_html_sv) except Exception as e: current_app.logger.error(f'Problem sending reminder email: {e}') return {'error': True, 'message': gettext('Problem sending the email, please try again')} message = gettext("Success sending reminder email to pending users") return {'message': message}
def decline_invitation(data): key = uuid.UUID(data['key']) email = session['mail'] try: current_app.doc_store.decline_document(key, email) except Exception as e: current_app.logger.error(f'Problem declining signature of document: {e}') return {'error': True, 'message': gettext('Problem declining signature, please try again')} try: owner_data = current_app.doc_store.get_owner_data(key) if not owner_data: current_app.logger.error( f"Problem sending email about {session['mail']} declining document {key} with no owner data" ) else: pending = current_app.doc_store.get_pending_invites(key) if len(pending) > 0: template = 'declined_by_email' else: template = 'final_declined_by_email' recipients = [f"{owner_data['name']} <{owner_data['email']}>"] mail_context = { 'document_name': owner_data['docname'], 'invited_name': session['displayName'], 'invited_email': session['mail'], } with force_locale('en'): subject_en = gettext('%(name)s declined to sign "%(docname)s"') % { 'name': owner_data['name'], 'docname': owner_data['docname'], } body_txt_en = render_template(f'{template}.txt.jinja2', **mail_context) body_html_en = render_template(f'{template}.html.jinja2', **mail_context) with force_locale('sv'): subject_sv = gettext('%(name)s declined to sign "%(docname)s"') % { 'name': owner_data['name'], 'docname': owner_data['docname'], } body_txt_sv = render_template(f'{template}.txt.jinja2', **mail_context) body_html_sv = render_template(f'{template}.html.jinja2', **mail_context) sendmail(recipients, subject_en, subject_sv, body_txt_en, body_html_en, body_txt_sv, body_html_sv) except Exception as e: current_app.logger.error(f'Problem sending email of declination: {e}') message = gettext("Success declining signature") return {'message': message}
def about_ar(): with force_locale('ar'): return render_template('about.html', lang="ar", heading=_("About Me"), image=url_for('static', filename='img/home-bg.jpg'))
def xfail_untranslated_messages(config: SDConfig, locale: str, msgids: Iterable[str]) -> Generator: """ Trigger pytest.xfail for untranslated strings. Given a list of gettext message IDs (strings marked for translation with gettext or ngettext in source code) used in this context manager's block, check that each has been translated in `locale`. Call pytest.xfail if any has not. Without this, to detect when gettext fell back to English, we'd have to hard-code the expected translations, which has obvious maintenance problems. You also can't just check that the result of a gettext call isn't the source string, because some translations are the same. """ with force_locale(locale): if locale != "en_US": catalog = message_catalog(config, locale) for msgid in msgids: m = catalog.get(msgid) if not m: pytest.xfail( "locale {} message catalog lacks msgid: {}".format( locale, msgid)) if not m.string: pytest.xfail( "locale {} has no translation for msgid: {}".format( locale, msgid)) yield
def req(): code = request.form.get('user') user = User.get_or_none(User.code == code) if not user: flash(_('There is no user with this private code.')) return redirect(url_for('c.front')) old_req = MailRequest.get_or_none(MailRequest.requested_by == g.user, MailRequest.requested_from == user, MailRequest.is_active == True) if old_req: flash(_('Sorry, you have already made a request.')) return redirect(url_for('c.profile', pcode=code)) MailRequest.create(requested_by=g.user, requested_from=user, comment='Hey, please send me a postcard!') with force_locale(user.site_lang): send_email( user, _p('mail', '%(user)s wants your postcard', user=g.user.name), '{}\n\n{}'.format( _p('mail', '%(user)s has asked you to send them a postcard. ' 'Please click on the button in their profile and send one!', user=g.user.name), url_for('c.profile', pcode=g.user.code, _external=True))) flash(_('Your postcard request has been sent.'), 'info') return redirect(url_for('c.profile', pcode=code))
def projects_ar(): with force_locale('ar'): return render_template('projects.html', heading=_("My Works and Projects"), lang="ar", image=url_for('static', filename='img/home-bg.jpg'))
def grant(code): req = ProfileRequest.get_or_none(ProfileRequest.id == code, ProfileRequest.requested_from == g.user) if not req: flash(_('There is no request with this code')) return redirect(url_for('c.front')) req.granted = True req.save() user = req.requested_by with force_locale(user.site_lang): send_email( user, _p('mail', '%(user)s wants your postcard', user=g.user.name), '{}\n\n{}'.format( _p('mail', '%(user)s has accepted your request to send them a postcard. ' 'Please click on the button in their profile and send one!', user=g.user.name), url_for('c.profile', pcode=g.user.code, _external=True))) flash(_('Thank you for granting permission to send you a postcard!'), 'info') if user.privacy < AddressPrivacy.CLOSED: return redirect(url_for('c.profile', pcode=user.code)) return redirect(url_for('c.front'))
def comment(code): mailcode = MailCode.get_or_none(MailCode.code == code, MailCode.received_on.is_null(False), MailCode.sent_to == g.user) if not mailcode: flash(_('No such mailcode.')) return redirect(url_for('c.front')) comment = request.form.get('comment', '').strip() if comment: if mailcode.comment: flash(_('Cannot change already stored comment, sorry.')) else: mailcode.comment = comment mailcode.save() with force_locale(mailcode.sent_by.site_lang or 'en'): send_email( mailcode.sent_by, _p('mail', 'Comment on your postcard %(code)s', code=mailcode.lcode), '{}:\n\n{}\n\n{}'.format( _p('mail', '%(user)s has just left a reply to your postcard', user=g.user.name), comment, url_for('c.card', code=mailcode.code, _external=True))) flash(_('Comment sent, thank you for connecting!'), 'info') return redirect(url_for('c.card', code=mailcode.code))
def render_zip(container, path, file, lang, categories=False): with app.app.app_context(): with force_locale(lang): directory = tempfile.mkdtemp() count = len(container.graphs) if isinstance( container, Collation) else container.graphs.count() for nr, graph in enumerate(container.graphs): png_name = graph.name_without_accents + ".png" if categories: category_dir = os.path.join(directory, graph.category_without_accents) if not os.path.exists(category_dir): os.makedirs(category_dir) png_path = os.path.join(category_dir, png_name) else: png_path = os.path.join(directory, png_name) render_chart_png(graph, png_path) file.progress = round((nr + 1) / count * 100) db_session.add(file) db_session.commit() util.zipdir(directory, path) file.ready = True db_session.add(file) db_session.commit()
def send_reminders(guid, event_guid): try: event = Event.get_by_guid_or_404(event_guid) _set_task_progress(0) draft_settlements = event.calculate_balance() timenow=datetime.utcnow().replace(microsecond=0).isoformat() total_payments = len(draft_settlements) i = 0 for settlement in draft_settlements: if settlement.sender.email: with force_locale(settlement.sender.locale): pdf = get_balance_pdf(event, settlement.sender.locale, timenow) send_email(_('Please settle your depts!'), sender=current_app.config['ADMIN_NOREPLY_SENDER'], recipients=[settlement.sender.email], text_body=render_template('email/reminder_email.txt', settlement=settlement, timenow=timenow), html_body=render_template('email/reminder_email.html', settlement=settlement, timenow=timenow), attachments=[('balance.pdf', 'application/pdf', pdf)], sync=True) i += 1 _set_task_progress(100*i//total_payments) _set_task_progress(100) except: _set_task_progress(100) app.logger.error('Unhandled exception', exc_info=sys.exc_info())
def format_poll(poll): """Returns the JSON representation of the given poll. """ with force_locale(poll.locale): if poll.is_finished(): return _format_finished_poll(poll) return _format_running_poll(poll)
def request_balance(guid, event_guid, eventuser_guid): try: event = Event.get_by_guid_or_404(event_guid) eventuser = EventUser.get_by_guid_or_404(eventuser_guid) _set_task_progress(0) timenow=datetime.utcnow().replace(microsecond=0).isoformat() pdf = get_balance_pdf(event, eventuser.locale, timenow, recalculate=True) with force_locale(eventuser.locale): send_email(_('Your balance of event %(eventname)s', eventname=event.name), sender=current_app.config['ADMIN_NOREPLY_SENDER'], recipients=[eventuser.email], text_body=render_template('email/balance_email.txt', username=eventuser.username, eventname=event.name, timenow=timenow), html_body=render_template('email/balance_email.html', username=eventuser.username, eventname=event.name, timenow=timenow), attachments=[('balance.pdf', 'application/pdf', pdf)], sync=True) _set_task_progress(100) except: _set_task_progress(100) app.logger.error('Unhandled exception', exc_info=sys.exc_info())
def ask(): code = request.form.get('user') user = User.get_or_none(User.code == code) if not user: flash(_('There is no user with this private code.')) return redirect(url_for('c.front')) old_req = ProfileRequest.get_or_none(ProfileRequest.requested_by == g.user, ProfileRequest.requested_from == user) if old_req: flash(_('Sorry, you have already made a request.')) return redirect(url_for('c.profile', pcode=code)) req = ProfileRequest.create(requested_by=g.user, requested_from=user) with force_locale(user.site_lang): send_email( user, _p('mail', '%(user)s wants to send you a postcard', user=g.user.name), '{}:\n\n{}'.format( _p('mail', '%(user)s has asked you to open your address to them, ' 'so that they could send you a postcard. Please click on the link ' 'to grant them permission, or ignore this message to deny', user=g.user.name), url_for('c.grant', code=req.id, _external=True))) flash(_('Permission request sent.', 'info')) return redirect(url_for('c.profile', pcode=code))
def send_email(**kwargs): smtp_configs = [ 'SMTP_SERVER', 'SMTP_USER', 'SMTP_PASSWORD', 'SMTP_PORT' ] try: query = None with current_app.app_context(): query = Configuration.query.filter( Configuration.name.in_(smtp_configs)) configs = dict( (c.name, c.value) for c in query) smtp_user = configs.get('SMTP_USER') smtp_passwd = configs.get('SMTP_PASSWORD') smtp_server = configs.get('SMTP_SERVER') smtp_port = configs.get('SMTP_PORT') if logger.isEnabledFor(logging.DEBUG): logger.debug('SMTP configuration: %s:%s (%s %s%s)', smtp_server, smtp_port, smtp_user, smtp_passwd[0], (len(smtp_passwd) - 1) * '*') base_dir = os.path.dirname(os.path.abspath(__file__)) templateLoader = jinja2.FileSystemLoader( searchpath='{}/templates/mail'.format(base_dir)) templateEnv = jinja2.Environment( extensions=['jinja2.ext.i18n'], loader=templateLoader) templ = templateEnv.get_template('{}.html'.format(kwargs.get('template'))) with force_locale(kwargs.get('locale', 'pt')): kwargs['gettext'] = babel_gettext body = templ.render(kwargs) server = smtplib.SMTP_SSL(smtp_server, smtp_port) server.ehlo() server.login(smtp_user, smtp_passwd) from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.header import Header from email.utils import formataddr to = kwargs.get('to').split() sender = 'LEMONADE <*****@*****.**>' msg = MIMEMultipart('alternative') msg['From'] = sender msg['Subject'] = kwargs.get('subject') msg['To'] = ','.join(to) msg.attach(MIMEText(body, 'html')) print(body) to_list = to server.sendmail(sender, to_list, msg.as_string()) print('*' * 50) except Exception: logger.exception('Error in metric job') db.session.rollback() raise
def test_ensure_request_context(self): en = Locale(b'en') fr = Locale(b'fr') # by default, a request context is present is test case setup. first let's # show no new request context is set up and that set_locale works as # advertised app_ctx = _app_ctx_stack.top current_ctx = _request_ctx_stack.top assert current_ctx is not None assert get_locale() == en with i18n.ensure_request_context(): assert current_ctx == _request_ctx_stack.top with force_locale(fr): assert get_locale() == fr self._ctx.pop() self._ctx = None assert _app_ctx_stack.top is None assert _request_ctx_stack.top is None assert get_locale() is None app_ctx.push() # # ensure set_locale() doesn't set up a request context # with force_locale(fr): # assert _request_ctx_stack.top is None # assert get_locale() is None with i18n.ensure_request_context(): assert _request_ctx_stack.top is not None assert current_ctx != _request_ctx_stack.top assert get_locale() == en with force_locale(fr): assert get_locale() == fr assert _request_ctx_stack.top is None assert get_locale() is None # test chaining with i18n.ensure_request_context(), force_locale(fr): assert get_locale() == fr
def test_finnish_translations(app): """ Test that translations exists for finnish. """ # For flask_babel to work, we need to run in app context with app.app_context(): # Setup locale to finnish with force_locale('fi_FI'): assert _( "Hello World") == "Hei Maailma", "Message is not translated"
def blog_ar(): posts = get_blog_posts('ar') with force_locale('ar'): return render_template('blog.html', heading=_("Blog"), image=url_for('static', filename='img/home-bg.jpg'), lang='ar', posts=posts)
def _get_error_messages(self): # Force translation of global error messages (stored as LazyString's) into the language of the schema. with force_locale(self.language_code): messages = {k: str(v) for k, v in error_messages.items()} if 'messages' in self.json: messages.update(self.json['messages']) return messages
def format_actions(poll): """Returns the JSON data of all available actions of the given poll. Additional to the options of the poll, a 'End Poll' action is appended. The returned list looks similar to this: ``` [{ "name": "<First Option> (0)", "integration": { "context": { "poll_id": "<unique_id>", "vote": 0 }, "url": "http://<hostname:port>/vote" } }, ... additional entries for all poll options ... { "name": "End Poll", "integration": { "url": "http://<hostname:port>/end", "context": { "poll_id": "<unique_id>" } } }] ``` """ with force_locale(poll.locale): options = poll.vote_options name = "{name}" if not poll.secret: # display current number of votes name += " ({votes})" actions = [{ 'name': name.format(name=vote, votes=poll.count_votes(vote_id)), 'integration': { 'url': url_for('vote', _external=True), 'context': { 'vote': vote_id, 'poll_id': poll.id } } } for vote_id, vote in enumerate(options)] # add action to end the poll actions.append({ 'name': tr("End Poll"), 'integration': { 'url': url_for('end_poll', _external=True), 'context': { 'poll_id': poll.id } } }) return actions
def _translate(self, text, **variables): if self.current_conversation and self.current_conversation.language: language = self.current_conversation.language with force_locale(language[:2]): text = gettext(text, **variables) return text text = gettext(text, **variables) return text
def contact_ar(): with force_locale('ar'): email_body = _( 'Hi yshalsager, I came across your website and would like to get in touch.' ) return render_template('contact.html', lang="ar", email_body=email_body, heading=_("Contact Me"), image=url_for('static', filename='img/contact.webp'))
def _send_alerts(): logger = get_celery_logger() num_sent = 0 for client in Client.query.filter_eligible_for_sending().all(): with force_locale(client.locale): try: if client.maybe_notify(): num_sent += 1 except Exception as e: logger.exception("Failed to send alert to %s: %s", client, e) logger.info("Sent %s alerts", num_sent)
def decorated_function(*args, **kwargs): if len(args) > 1: receiver = args[1] else: receiver = kwargs["receiver"] locale = "" if receiver is not None and receiver.locale is not None: locale = receiver.locale else: locale = "en_US" with force_locale(locale): return f(*args, **kwargs)
def user_gettext(user_id, string, **variables): user_pref = User.query.filter_by(user_id=user_id).first() if user_pref: locale = user_pref.locale else: locale = user_utils.locale(user_id) user_pref = User(user_id, locale) db.session.add(user_pref) db.session.commit() db.session.flush() with force_locale(locale): return gettext(string, **variables)
def send_password_reset_email(user): with force_locale(user.locale): token = user.get_reset_password_token() url = url_for('auth.reset_password', token=token, _external=True) send_email(_('Reset Your Password'), sender=current_app.config['ADMIN_NOREPLY_SENDER'], recipients=[user.email], text_body=render_template('email/reset_password.txt', username=user.username, url=url), html_body=render_template('email/reset_password.html', username=user.username, url=url))
def test_force_locale(self): app = flask.Flask(__name__) b = babel.Babel(app) @b.localeselector def select_locale(): return 'de_DE' with app.test_request_context(): assert str(babel.get_locale()) == 'de_DE' with babel.force_locale('en_US'): assert str(babel.get_locale()) == 'en_US' assert str(babel.get_locale()) == 'de_DE'
def delete_dish(dish_name: str) -> None: """Удаление информации о блюде из БД по его названию""" try: current_app.logger.debug( f'Enter into {delete_dish.__name__} with params: dish_name={dish_name}' ) with force_locale('ru'): db.session.query(Dish).filter_by(name=gettext(dish_name)).delete() db.session.flush() db.session.commit() except IntegrityError as e: db.session.rollback() current_app.logger.error(f'Откат операции удаления блюда: {e}')
def by_name(self, country, language='en'): """ Fetch a country's ISO3166-1 two letter country code from its name. An optional language parameter is also available. Warning: This depends on the quality of the available translations. If no match is found, returns an empty string. """ with force_locale(language): for code, name in self: if name == country: return code return ''
def generate_and_send_summaries(cutoff_days, org_id): ostats = overdue_stats_by_org() cutoffs = [int(i) for i in cutoff_days.split(',')] error_emails = set() ot = OrgTree() top_org = Organization.query.get(org_id) if not top_org: raise ValueError("No org with ID {} found.".format(org_id)) name_key = SiteSummaryEmail_ATMA.name_key(org=top_org.name) for user in User.query.filter_by(deleted_id=None).all(): if not (user.has_role(ROLE.STAFF.value) and user.email_ready()[0] and (top_org in ot.find_top_level_org(user.organizations))): continue args = load_template_args(user=user) with force_locale(user.locale_code): args['eproms_site_summary_table'] = generate_overdue_table_html( cutoff_days=cutoffs, overdue_stats=ostats, user=user, top_org=top_org, ) summary_email = MailResource(app_text(name_key), locale_code=user.locale_code, variables=args) em = EmailMessage(recipients=user.email, sender=current_app.config['MAIL_DEFAULT_SENDER'], subject=summary_email.subject, body=summary_email.body) try: em.send_message() except SMTPRecipientsRefused as exc: msg = ("Error sending site summary email to {}: " "{}".format(user.email, exc)) sys = User.query.filter_by(email='__system__').first() auditable_event(message=msg, user_id=(sys.id if sys else user.id), subject_id=user.id, context="user") current_app.logger.error(msg) for email in exc[0]: error_emails.add(email) return error_emails or None
def render_template_i18n(template_name_or_list, **context): """Try to build an ordered list of template to satisfy the current locale.""" template_list = [] # Use locale if present in **context if "locale" in context: locale = Locale.parse(context["locale"]) else: # Use get_locale() or default_locale locale = flask_babel.get_locale() if isinstance(template_name_or_list, str): template_list = get_template_i18n(template_name_or_list, locale) else: # Search for locale for each member of the list, do not bypass for template in template_name_or_list: template_list.extend(get_template_i18n(template, locale)) with ensure_request_context(), force_locale(locale): return render_template(template_list, **context)
def patch_forgot_password(): """Need to customize flash message shown in forgot_password No hooks available to customize the message, so this function is intended to be a drop in replacement with only the text of the message altered, as per TN-1030 """ """Prompt for email and send reset password email.""" user_manager = current_app.user_manager # Initialize form form = user_manager.forgot_password_form(request.form) # Process valid POST if request.method == 'POST' and form.validate(): email = form.email.data user, user_email = user_manager.find_user_by_email(email) if user: with force_locale(user.locale_code): user_manager.send_reset_password_email(email) # Prepare one-time system message flash(_("If the email address '%(email)s' is in the system, a " "reset password email will now have been sent to it. " "Please open that email and follow the instructions to " "reset your password.", email=email), 'success') # Redirect to the login page return redirect( _endpoint_url(user_manager.after_forgot_password_endpoint)) # Process GET or invalid POST return user_manager.render_function( user_manager.forgot_password_template, form=form)
def user_gettext(user_id, string, **variables): # TODO: user's locale should be saved in DB locale = fb_utils.get_language(user_id) with force_locale(locale): return gettext(string, **variables)
def test_get_template_i18n_fr(app, test_request_context): template_path = "/myfile.txt" with force_locale("fr"): result = i18n.get_template_i18n(template_path, get_locale()) assert "/myfile.fr.txt" in result assert "/myfile.txt" in result
def function_with_forced_locale(): if not locale_code: return fn() with force_locale(locale_code): return fn()