Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/flask_babel/__init__.py: 30%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

285 statements  

1""" 

2 flask_babel 

3 ~~~~~~~~~~~ 

4 

5 Implements i18n/l10n support for Flask applications based on Babel. 

6 

7 :copyright: (c) 2013 by Armin Ronacher, Daniel Neuhäuser. 

8 :license: BSD, see LICENSE for more details. 

9""" 

10import os 

11from dataclasses import dataclass 

12from types import SimpleNamespace 

13from datetime import datetime 

14from contextlib import contextmanager 

15from typing import List, Callable, Optional, Union 

16 

17from babel.support import Translations, NullTranslations 

18from flask import current_app, g 

19from babel import dates, numbers, support, Locale 

20from pytz import timezone, UTC 

21from werkzeug.datastructures import ImmutableDict 

22from werkzeug.utils import cached_property 

23 

24from flask_babel.speaklater import LazyString 

25 

26 

27@dataclass 

28class BabelConfiguration: 

29 """Application-specific configuration for Babel.""" 

30 default_locale: str 

31 default_timezone: str 

32 default_domain: str 

33 default_directories: List[str] 

34 translation_directories: List[str] 

35 

36 instance: 'Babel' 

37 

38 locale_selector: Optional[Callable] = None 

39 timezone_selector: Optional[Callable] = None 

40 

41 

42def get_babel(app=None) -> 'BabelConfiguration': 

43 app = app or current_app 

44 if not hasattr(app, 'extensions'): 

45 app.extensions = {} 

46 return app.extensions['babel'] 

47 

48 

49class Babel: 

50 """Central controller class that can be used to configure how 

51 Flask-Babel behaves. Each application that wants to use Flask-Babel 

52 has to create, or run :meth:`init_app` on, an instance of this class 

53 after the configuration was initialized. 

54 """ 

55 

56 default_date_formats = ImmutableDict({ 

57 'time': 'medium', 

58 'date': 'medium', 

59 'datetime': 'medium', 

60 'time.short': None, 

61 'time.medium': None, 

62 'time.full': None, 

63 'time.long': None, 

64 'date.short': None, 

65 'date.medium': None, 

66 'date.full': None, 

67 'date.long': None, 

68 'datetime.short': None, 

69 'datetime.medium': None, 

70 'datetime.full': None, 

71 'datetime.long': None, 

72 }) 

73 

74 def __init__(self, app=None, date_formats=None, configure_jinja=True, *args, 

75 **kwargs): 

76 """Creates a new Babel instance. 

77 

78 If an application is passed, it will be configured with the provided 

79 arguments. Otherwise, :meth:`init_app` can be used to configure the 

80 application later. 

81 """ 

82 self._configure_jinja = configure_jinja 

83 self.date_formats = date_formats 

84 

85 if app is not None: 

86 self.init_app(app, *args, **kwargs) 

87 

88 def init_app(self, app, default_locale='en', default_domain='messages', 

89 default_translation_directories='translations', 

90 default_timezone='UTC', locale_selector=None, 

91 timezone_selector=None): 

92 """ 

93 Initializes the Babel instance for use with this specific application. 

94 

95 :param app: The application to configure 

96 :param default_locale: The default locale to use for this application 

97 :param default_domain: The default domain to use for this application 

98 :param default_translation_directories: The default translation 

99 directories to use for this 

100 application 

101 :param default_timezone: The default timezone to use for this 

102 application 

103 :param locale_selector: The function to use to select the locale 

104 for a request 

105 :param timezone_selector: The function to use to select the 

106 timezone for a request 

107 """ 

108 if not hasattr(app, 'extensions'): 

109 app.extensions = {} 

110 

111 directories = app.config.get( 

112 'BABEL_TRANSLATION_DIRECTORIES', 

113 default_translation_directories 

114 ).split(';') 

115 

116 app.extensions['babel'] = BabelConfiguration( 

117 default_locale=app.config.get( 

118 'BABEL_DEFAULT_LOCALE', 

119 default_locale 

120 ), 

121 default_timezone=app.config.get( 

122 'BABEL_DEFAULT_TIMEZONE', 

123 default_timezone 

124 ), 

125 default_domain=app.config.get( 

126 'BABEL_DOMAIN', 

127 default_domain 

128 ), 

129 default_directories=directories, 

130 translation_directories=list( 

131 self._resolve_directories(directories, app) 

132 ), 

133 instance=self, 

134 locale_selector=locale_selector, 

135 timezone_selector=timezone_selector 

136 ) 

137 

138 # a mapping of Babel datetime format strings that can be modified 

139 # to change the defaults. If you invoke :func:`format_datetime` 

140 # and do not provide any format string Flask-Babel will do the 

141 # following things: 

142 # 

143 # 1. look up ``date_formats['datetime']``. By default, ``'medium'`` 

144 # is returned to enforce medium length datetime formats. 

145 # 2. ``date_formats['datetime.medium'] (if ``'medium'`` was 

146 # returned in step one) is looked up. If the return value 

147 # is anything but `None` this is used as new format string. 

148 # otherwise the default for that language is used. 

149 if self.date_formats is None: 

150 self.date_formats = self.default_date_formats.copy() 

151 

152 if self._configure_jinja: 

153 app.jinja_env.filters.update( 

154 datetimeformat=format_datetime, 

155 dateformat=format_date, 

156 timeformat=format_time, 

157 timedeltaformat=format_timedelta, 

158 numberformat=format_number, 

159 decimalformat=format_decimal, 

160 currencyformat=format_currency, 

161 percentformat=format_percent, 

162 scientificformat=format_scientific, 

163 ) 

164 app.jinja_env.add_extension('jinja2.ext.i18n') 

165 app.jinja_env.install_gettext_callables( 

166 gettext=lambda s: get_translations().ugettext(s), 

167 ngettext=lambda s, p, n: get_translations().ungettext(s, p, n), 

168 newstyle=True, 

169 pgettext=lambda c, s: get_translations().upgettext(c, s), 

170 npgettext=lambda c, s, p, n: get_translations().unpgettext( 

171 c, s, p, n 

172 ), 

173 ) 

174 

175 def list_translations(self): 

176 """Returns a list of all the locales translations exist for. The list 

177 returned will be filled with actual locale objects and not just strings. 

178 

179 .. note:: 

180 

181 The default locale will always be returned, even if no translation 

182 files exist for it. 

183 

184 .. versionadded:: 0.6 

185 """ 

186 result = [] 

187 

188 for dirname in get_babel().translation_directories: 

189 if not os.path.isdir(dirname): 

190 continue 

191 

192 for folder in os.listdir(dirname): 

193 locale_dir = os.path.join(dirname, folder, 'LC_MESSAGES') 

194 if not os.path.isdir(locale_dir): 

195 continue 

196 

197 if any(x.endswith('.mo') for x in os.listdir(locale_dir)): 

198 result.append(Locale.parse(folder)) 

199 

200 if self.default_locale not in result: 

201 result.append(self.default_locale) 

202 return result 

203 

204 @property 

205 def default_locale(self) -> Locale: 

206 """The default locale from the configuration as an instance of a 

207 `babel.Locale` object. 

208 """ 

209 return Locale.parse(get_babel().default_locale) 

210 

211 @property 

212 def default_timezone(self) -> timezone: 

213 """The default timezone from the configuration as an instance of a 

214 `pytz.timezone` object. 

215 """ 

216 return timezone(get_babel().default_timezone) 

217 

218 @property 

219 def domain(self) -> str: 

220 """The message domain for the translations as a string. 

221 """ 

222 return get_babel().default_domain 

223 

224 @cached_property 

225 def domain_instance(self): 

226 """The message domain for the translations. 

227 """ 

228 return Domain(domain=self.domain) 

229 

230 @staticmethod 

231 def _resolve_directories(directories: List[str], app=None): 

232 for path in directories: 

233 if os.path.isabs(path): 

234 yield path 

235 elif app is not None: 

236 # We can only resolve relative paths if we have an application 

237 # context. 

238 yield os.path.join(app.root_path, path) 

239 

240 

241def get_translations() -> Union[Translations, NullTranslations]: 

242 """Returns the correct gettext translations that should be used for 

243 this request. This will never fail and return a dummy translation 

244 object if used outside the request or if a translation cannot be found. 

245 """ 

246 return get_domain().get_translations() 

247 

248 

249def get_locale() -> Optional[Locale]: 

250 """Returns the locale that should be used for this request as 

251 `babel.Locale` object. This returns `None` if used outside a request. 

252 """ 

253 ctx = _get_current_context() 

254 if ctx is None: 

255 return None 

256 

257 locale = getattr(ctx, 'babel_locale', None) 

258 if locale is None: 

259 babel = get_babel() 

260 if babel.locale_selector is None: 

261 locale = babel.instance.default_locale 

262 else: 

263 rv = babel.locale_selector() 

264 if rv is None: 

265 locale = babel.instance.default_locale 

266 else: 

267 locale = Locale.parse(rv) 

268 ctx.babel_locale = locale 

269 

270 return locale 

271 

272 

273def get_timezone() -> Optional[timezone]: 

274 """Returns the timezone that should be used for this request as 

275 a `pytz.timezone` object. This returns `None` if used outside a request. 

276 """ 

277 ctx = _get_current_context() 

278 tzinfo = getattr(ctx, 'babel_tzinfo', None) 

279 if tzinfo is None: 

280 babel = get_babel() 

281 if babel.timezone_selector is None: 

282 tzinfo = babel.instance.default_timezone 

283 else: 

284 rv = babel.timezone_selector() 

285 if rv is None: 

286 tzinfo = babel.instance.default_timezone 

287 else: 

288 tzinfo = timezone(rv) if isinstance(rv, str) else rv 

289 ctx.babel_tzinfo = tzinfo 

290 return tzinfo 

291 

292 

293def refresh(): 

294 """Refreshes the cached timezones and locale information. This can 

295 be used to switch a translation between a request and if you want 

296 the changes to take place immediately, not just with the next request:: 

297 

298 user.timezone = request.form['timezone'] 

299 user.locale = request.form['locale'] 

300 refresh() 

301 flash(gettext('Language was changed')) 

302 

303 Without that refresh, the :func:`~flask.flash` function would probably 

304 return English text and a now German page. 

305 """ 

306 ctx = _get_current_context() 

307 for key in 'babel_locale', 'babel_tzinfo', 'babel_translations': 

308 if hasattr(ctx, key): 

309 delattr(ctx, key) 

310 

311 if hasattr(ctx, 'forced_babel_locale'): 

312 ctx.babel_locale = ctx.forced_babel_locale 

313 

314 

315@contextmanager 

316def force_locale(locale): 

317 """Temporarily overrides the currently selected locale. 

318 

319 Sometimes it is useful to switch the current locale to different one, do 

320 some tasks and then revert back to the original one. For example, if the 

321 user uses German on the website, but you want to email them in English, 

322 you can use this function as a context manager:: 

323 

324 with force_locale('en_US'): 

325 send_email(gettext('Hello!'), ...) 

326 

327 :param locale: The locale to temporary switch to (ex: 'en_US'). 

328 """ 

329 ctx = _get_current_context() 

330 if ctx is None: 

331 yield 

332 return 

333 

334 orig_attrs = {} 

335 for key in ('babel_translations', 'babel_locale'): 

336 orig_attrs[key] = getattr(ctx, key, None) 

337 

338 try: 

339 ctx.babel_locale = Locale.parse(locale) 

340 ctx.forced_babel_locale = ctx.babel_locale 

341 ctx.babel_translations = None 

342 yield 

343 finally: 

344 if hasattr(ctx, 'forced_babel_locale'): 

345 del ctx.forced_babel_locale 

346 

347 for key, value in orig_attrs.items(): 

348 setattr(ctx, key, value) 

349 

350 

351def _get_format(key, format) -> Optional[str]: 

352 """A small helper for the datetime formatting functions. Looks up 

353 format defaults for different kinds. 

354 """ 

355 babel = get_babel() 

356 if format is None: 

357 format = babel.instance.date_formats[key] 

358 if format in ('short', 'medium', 'full', 'long'): 

359 rv = babel.instance.date_formats['%s.%s' % (key, format)] 

360 if rv is not None: 

361 format = rv 

362 return format 

363 

364 

365def to_user_timezone(datetime): 

366 """Convert a datetime object to the user's timezone. This automatically 

367 happens on all date formatting unless rebasing is disabled. If you need 

368 to convert a :class:`datetime.datetime` object at any time to the user's 

369 timezone (as returned by :func:`get_timezone`) this function can be used. 

370 """ 

371 if datetime.tzinfo is None: 

372 datetime = datetime.replace(tzinfo=UTC) 

373 tzinfo = get_timezone() 

374 return tzinfo.normalize(datetime.astimezone(tzinfo)) 

375 

376 

377def to_utc(datetime): 

378 """Convert a datetime object to UTC and drop tzinfo. This is the 

379 opposite operation to :func:`to_user_timezone`. 

380 """ 

381 if datetime.tzinfo is None: 

382 datetime = get_timezone().localize(datetime) 

383 return datetime.astimezone(UTC).replace(tzinfo=None) 

384 

385 

386def format_datetime(datetime=None, format=None, rebase=True): 

387 """Return a date formatted according to the given pattern. If no 

388 :class:`~datetime.datetime` object is passed, the current time is 

389 assumed. By default, rebasing happens, which causes the object to 

390 be converted to the user's timezone (as returned by 

391 :func:`to_user_timezone`). This function formats both date and 

392 time. 

393 

394 The format parameter can either be ``'short'``, ``'medium'``, 

395 ``'long'`` or ``'full'`` (in which case the language's default for 

396 that setting is used, or the default from the :attr:`Babel.date_formats` 

397 mapping is used) or a format string as documented by Babel. 

398 

399 This function is also available in the template context as filter 

400 named `datetimeformat`. 

401 """ 

402 format = _get_format('datetime', format) 

403 return _date_format(dates.format_datetime, datetime, format, rebase) 

404 

405 

406def format_date(date=None, format=None, rebase=True): 

407 """Return a date formatted according to the given pattern. If no 

408 :class:`~datetime.datetime` or :class:`~datetime.date` object is passed, 

409 the current time is assumed. By default, rebasing happens, which causes 

410 the object to be converted to the users's timezone (as returned by 

411 :func:`to_user_timezone`). This function only formats the date part 

412 of a :class:`~datetime.datetime` object. 

413 

414 The format parameter can either be ``'short'``, ``'medium'``, 

415 ``'long'`` or ``'full'`` (in which case the language's default for 

416 that setting is used, or the default from the :attr:`Babel.date_formats` 

417 mapping is used) or a format string as documented by Babel. 

418 

419 This function is also available in the template context as filter 

420 named `dateformat`. 

421 """ 

422 if rebase and isinstance(date, datetime): 

423 date = to_user_timezone(date) 

424 format = _get_format('date', format) 

425 return _date_format(dates.format_date, date, format, rebase) 

426 

427 

428def format_time(time=None, format=None, rebase=True): 

429 """Return a time formatted according to the given pattern. If no 

430 :class:`~datetime.datetime` object is passed, the current time is 

431 assumed. By default, rebasing happens, which causes the object to 

432 be converted to the user's timezone (as returned by 

433 :func:`to_user_timezone`). This function formats both date and time. 

434 

435 The format parameter can either be ``'short'``, ``'medium'``, 

436 ``'long'`` or ``'full'`` (in which case the language's default for 

437 that setting is used, or the default from the :attr:`Babel.date_formats` 

438 mapping is used) or a format string as documented by Babel. 

439 

440 This function is also available in the template context as filter 

441 named `timeformat`. 

442 """ 

443 format = _get_format('time', format) 

444 return _date_format(dates.format_time, time, format, rebase) 

445 

446 

447def format_timedelta(datetime_or_timedelta, granularity: str = 'second', 

448 add_direction=False, threshold=0.85): 

449 """Format the elapsed time from the given date to now or the given 

450 timedelta. 

451 

452 This function is also available in the template context as filter 

453 named `timedeltaformat`. 

454 """ 

455 if isinstance(datetime_or_timedelta, datetime): 

456 datetime_or_timedelta = datetime.utcnow() - datetime_or_timedelta 

457 return dates.format_timedelta( 

458 datetime_or_timedelta, 

459 granularity, 

460 threshold=threshold, 

461 add_direction=add_direction, 

462 locale=get_locale() 

463 ) 

464 

465 

466def _date_format(formatter, obj, format, rebase, **extra): 

467 """Internal helper that formats the date.""" 

468 locale = get_locale() 

469 extra = {} 

470 if formatter is not dates.format_date and rebase: 

471 extra['tzinfo'] = get_timezone() 

472 return formatter(obj, format, locale=locale, **extra) 

473 

474 

475def format_number(number) -> str: 

476 """Return the given number formatted for the locale in request 

477 

478 :param number: the number to format 

479 :return: the formatted number 

480 :rtype: unicode 

481 """ 

482 locale = get_locale() 

483 return numbers.format_decimal(number, locale=locale) 

484 

485 

486def format_decimal(number, format=None) -> str: 

487 """Return the given decimal number formatted for the locale in the request. 

488 

489 :param number: the number to format 

490 :param format: the format to use 

491 :return: the formatted number 

492 :rtype: unicode 

493 """ 

494 locale = get_locale() 

495 return numbers.format_decimal(number, format=format, locale=locale) 

496 

497 

498def format_currency(number, currency, format=None, currency_digits=True, 

499 format_type='standard') -> str: 

500 """Return the given number formatted for the locale in the request. 

501 

502 :param number: the number to format 

503 :param currency: the currency code 

504 :param format: the format to use 

505 :param currency_digits: use the currency’s number of decimal digits 

506 [default: True] 

507 :param format_type: the currency format type to use 

508 [default: standard] 

509 :return: the formatted number 

510 :rtype: unicode 

511 """ 

512 locale = get_locale() 

513 return numbers.format_currency( 

514 number, 

515 currency, 

516 format=format, 

517 locale=locale, 

518 currency_digits=currency_digits, 

519 format_type=format_type 

520 ) 

521 

522 

523def format_percent(number, format=None) -> str: 

524 """Return formatted percent value for the locale in the request. 

525 

526 :param number: the number to format 

527 :param format: the format to use 

528 :return: the formatted percent number 

529 :rtype: unicode 

530 """ 

531 locale = get_locale() 

532 return numbers.format_percent(number, format=format, locale=locale) 

533 

534 

535def format_scientific(number, format=None) -> str: 

536 """Return value formatted in scientific notation for the locale in request 

537 

538 :param number: the number to format 

539 :param format: the format to use 

540 :return: the formatted percent number 

541 :rtype: unicode 

542 """ 

543 locale = get_locale() 

544 return numbers.format_scientific(number, format=format, locale=locale) 

545 

546 

547class Domain(object): 

548 """Localization domain. By default, it will look for translations in the 

549 Flask application directory and "messages" domain - all message catalogs 

550 should be called ``messages.mo``. 

551  

552 Additional domains are supported passing a list of domain names to the 

553 ``domain`` argument, but note that in this case they must match a list 

554 passed to ``translation_directories``, eg:: 

555 

556 Domain( 

557 translation_directories=[ 

558 "/path/to/translations/with/messages/domain", 

559 "/another/path/to/translations/with/another/domain", 

560 ], 

561 domains=[ 

562 "messages", 

563 "myapp", 

564 ] 

565 ) 

566 """ 

567 

568 def __init__(self, translation_directories=None, domain='messages'): 

569 if isinstance(translation_directories, str): 

570 translation_directories = [translation_directories] 

571 self._translation_directories = translation_directories 

572 

573 self.domain = domain.split(';') 

574 

575 self.cache = {} 

576 

577 def __repr__(self): 

578 return '<Domain({!r}, {!r})>'.format( 

579 self._translation_directories, 

580 self.domain 

581 ) 

582 

583 @property 

584 def translation_directories(self): 

585 if self._translation_directories is not None: 

586 return self._translation_directories 

587 return get_babel().translation_directories 

588 

589 def as_default(self): 

590 """Set this domain as default for the current request""" 

591 ctx = _get_current_context() 

592 

593 if ctx is None: 

594 raise RuntimeError("No request context") 

595 

596 ctx.babel_domain = self 

597 

598 def get_translations_cache(self, ctx): 

599 """Returns dictionary-like object for translation caching""" 

600 return self.cache 

601 

602 def get_translations(self): 

603 ctx = _get_current_context() 

604 

605 if ctx is None: 

606 return support.NullTranslations() 

607 

608 cache = self.get_translations_cache(ctx) 

609 locale = get_locale() 

610 try: 

611 return cache[str(locale), self.domain[0]] 

612 except KeyError: 

613 translations = support.Translations() 

614 

615 for index, dirname in enumerate(self.translation_directories): 

616 

617 domain = ( 

618 self.domain[0] 

619 if len(self.domain) == 1 

620 else self.domain[index] 

621 ) 

622 

623 catalog = support.Translations.load( 

624 dirname, 

625 [locale], 

626 domain 

627 ) 

628 translations.merge(catalog) 

629 # FIXME: Workaround for merge() being really, really stupid. It 

630 # does not copy _info, plural(), or any other instance variables 

631 # populated by GNUTranslations. We probably want to stop using 

632 # `support.Translations.merge` entirely. 

633 if hasattr(catalog, 'plural'): 

634 translations.plural = catalog.plural 

635 

636 cache[str(locale), self.domain[0]] = translations 

637 return translations 

638 

639 def gettext(self, string, **variables): 

640 """Translates a string with the current locale and passes in the 

641 given keyword arguments as mapping to a string formatting string. 

642 

643 :: 

644 

645 gettext(u'Hello World!') 

646 gettext(u'Hello %(name)s!', name='World') 

647 """ 

648 t = self.get_translations() 

649 s = t.ugettext(string) 

650 return s if not variables else s % variables 

651 

652 def ngettext(self, singular, plural, num, **variables): 

653 """Translates a string with the current locale and passes in the 

654 given keyword arguments as mapping to a string formatting string. 

655 The `num` parameter is used to dispatch between singular and various 

656 plural forms of the message. It is available in the format string 

657 as ``%(num)d`` or ``%(num)s``. The source language should be 

658 English or a similar language which only has one plural form. 

659 

660 :: 

661 

662 ngettext(u'%(num)d Apple', u'%(num)d Apples', num=len(apples)) 

663 """ 

664 variables.setdefault('num', num) 

665 t = self.get_translations() 

666 s = t.ungettext(singular, plural, num) 

667 return s if not variables else s % variables 

668 

669 def pgettext(self, context, string, **variables): 

670 """Like :func:`gettext` but with a context. 

671 

672 .. versionadded:: 0.7 

673 """ 

674 t = self.get_translations() 

675 s = t.upgettext(context, string) 

676 return s if not variables else s % variables 

677 

678 def npgettext(self, context, singular, plural, num, **variables): 

679 """Like :func:`ngettext` but with a context. 

680 

681 .. versionadded:: 0.7 

682 """ 

683 variables.setdefault('num', num) 

684 t = self.get_translations() 

685 s = t.unpgettext(context, singular, plural, num) 

686 return s if not variables else s % variables 

687 

688 def lazy_gettext(self, string, **variables): 

689 """Like :func:`gettext` but the string returned is lazy which means 

690 it will be translated when it is used as an actual string. 

691 

692 Example:: 

693 

694 hello = lazy_gettext(u'Hello World') 

695 

696 @app.route('/') 

697 def index(): 

698 return unicode(hello) 

699 """ 

700 return LazyString(self.gettext, string, **variables) 

701 

702 def lazy_ngettext(self, singular, plural, num, **variables): 

703 """Like :func:`ngettext` but the string returned is lazy which means 

704 it will be translated when it is used as an actual string. 

705 

706 Example:: 

707 

708 apples = lazy_ngettext( 

709 u'%(num)d Apple', 

710 u'%(num)d Apples', 

711 num=len(apples) 

712 ) 

713 

714 @app.route('/') 

715 def index(): 

716 return unicode(apples) 

717 """ 

718 return LazyString(self.ngettext, singular, plural, num, **variables) 

719 

720 def lazy_pgettext(self, context, string, **variables): 

721 """Like :func:`pgettext` but the string returned is lazy which means 

722 it will be translated when it is used as an actual string. 

723 

724 .. versionadded:: 0.7 

725 """ 

726 return LazyString(self.pgettext, context, string, **variables) 

727 

728 

729def _get_current_context() -> Optional[SimpleNamespace]: 

730 if not g: 

731 return None 

732 

733 if not hasattr(g, "_flask_babel"): 

734 g._flask_babel = SimpleNamespace() 

735 

736 return g._flask_babel # noqa 

737 

738 

739def get_domain() -> Domain: 

740 ctx = _get_current_context() 

741 if ctx is None: 

742 # this will use NullTranslations 

743 return Domain() 

744 

745 try: 

746 return ctx.babel_domain 

747 except AttributeError: 

748 pass 

749 

750 ctx.babel_domain = get_babel().instance.domain_instance 

751 return ctx.babel_domain 

752 

753 

754# Create shortcuts for the default Flask domain 

755def gettext(*args, **kwargs) -> str: 

756 return get_domain().gettext(*args, **kwargs) 

757 

758 

759_ = gettext 

760 

761 

762def ngettext(*args, **kwargs) -> str: 

763 return get_domain().ngettext(*args, **kwargs) 

764 

765 

766def pgettext(*args, **kwargs) -> str: 

767 return get_domain().pgettext(*args, **kwargs) 

768 

769 

770def npgettext(*args, **kwargs) -> str: 

771 return get_domain().npgettext(*args, **kwargs) 

772 

773 

774def lazy_gettext(*args, **kwargs) -> LazyString: 

775 return LazyString(gettext, *args, **kwargs) 

776 

777 

778def lazy_pgettext(*args, **kwargs) -> LazyString: 

779 return LazyString(pgettext, *args, **kwargs) 

780 

781 

782def lazy_ngettext(*args, **kwargs) -> LazyString: 

783 return LazyString(ngettext, *args, **kwargs) 

784 

785 

786def lazy_npgettext(*args, **kwargs) -> LazyString: 

787 return LazyString(npgettext, *args, **kwargs)