Skip to content

Commit

Permalink
Revert "Disable localisation when SOURCE_DATE_EPOCH is set (sphin…
Browse files Browse the repository at this point in the history
…x-doc#10949)"

This reverts commit f82c3c9.
  • Loading branch information
jayaddison committed Apr 9, 2023
1 parent c1ab2ed commit a8c1ab4
Show file tree
Hide file tree
Showing 13 changed files with 20 additions and 128 deletions.
2 changes: 0 additions & 2 deletions CHANGES
Expand Up @@ -92,8 +92,6 @@ Bugs fixed
* #11192: Restore correct parallel search index building.
Patch by Jeremy Maitin-Shepard
* Use the new Transifex ``tx`` client
* #9778: Disable localisation when the ``SOURCE_DATE_EPOCH`` environment
variable is set, to assist with 'reproducible builds'. Patch by James Addison

Testing
--------
Expand Down
2 changes: 1 addition & 1 deletion sphinx/builders/html/__init__.py
Expand Up @@ -502,7 +502,7 @@ def prepare_writing(self, docnames: set[str]) -> None:
# typically doesn't include the time of day
lufmt = self.config.html_last_updated_fmt
if lufmt is not None:
self.last_updated = format_date(lufmt or str(_('%b %d, %Y')),
self.last_updated = format_date(lufmt or _('%b %d, %Y'),
language=self.config.language)
else:
self.last_updated = None
Expand Down
2 changes: 1 addition & 1 deletion sphinx/builders/latex/__init__.py
Expand Up @@ -179,7 +179,7 @@ def init_context(self) -> None:
if self.config.today:
self.context['date'] = self.config.today
else:
self.context['date'] = format_date(self.config.today_fmt or str(_('%b %d, %Y')),
self.context['date'] = format_date(self.config.today_fmt or _('%b %d, %Y'),
language=self.config.language)

if self.config.latex_logo:
Expand Down
4 changes: 2 additions & 2 deletions sphinx/domains/std.py
Expand Up @@ -242,9 +242,9 @@ def add_target_and_index(self, firstname: str, sig: str, signode: desc_signature

# create an index entry
if currprogram:
descr = str(_('%s command line option') % currprogram)
descr = _('%s command line option') % currprogram
else:
descr = str(_('command line option'))
descr = _('command line option')
for option in signode.get('allnames', []):
entry = '; '.join([descr, option])
self.indexnode['entries'].append(('pair', entry, signode['ids'][0], '', None))
Expand Down
29 changes: 11 additions & 18 deletions sphinx/locale/__init__.py
Expand Up @@ -4,7 +4,7 @@

import locale
from gettext import NullTranslations, translation
from os import getenv, path
from os import path
from typing import Any, Callable


Expand Down Expand Up @@ -113,21 +113,9 @@ def init(
# the None entry is the system's default locale path
has_translation = True

if getenv('SOURCE_DATE_EPOCH') is not None:
# Disable localization during reproducible source builds
# See https://reproducible-builds.org/docs/source-date-epoch/
#
# Note: Providing an empty/none value to gettext.translation causes
# it to consult various language-related environment variables to find
# locale(s). We don't want that during a reproducible build; we want
# to run through the same code path, but to return NullTranslations.
#
# To achieve that, specify the ISO-639-3 'undetermined' language code,
# which should not match any translation catalogs.
languages: list[str] | None = ['und']
elif language and '_' in language:
if language and '_' in language:
# for language having country code (like "de_AT")
languages = [language, language.split('_')[0]]
languages: Optional[List[str]] = [language, language.split('_')[0]]
elif language:
languages = [language]
else:
Expand Down Expand Up @@ -181,7 +169,7 @@ def is_translator_registered(catalog: str = 'sphinx', namespace: str = 'general'
return (namespace, catalog) in translators


def _lazy_translate(catalog: str, namespace: str, message: str, *args: Any) -> str:
def _lazy_translate(catalog: str, namespace: str, message: str) -> str:
"""Used instead of _ when creating TranslationProxy, because _ is
not bound yet at that time.
"""
Expand Down Expand Up @@ -214,8 +202,13 @@ def setup(app):
.. versionadded:: 1.8
"""
def gettext(message: str, *args: Any) -> str:
return _TranslationProxy(_lazy_translate, catalog, namespace, message, *args) # type: ignore[return-value] # NOQA
def gettext(message: str) -> str:
if not is_translator_registered(catalog, namespace):
# not initialized yet
return _TranslationProxy(_lazy_translate, catalog, namespace, message) # type: ignore[return-value] # noqa: E501
else:
translator = get_translator(catalog, namespace)
return translator.gettext(message)

return gettext

Expand Down
2 changes: 1 addition & 1 deletion sphinx/transforms/__init__.py
Expand Up @@ -106,7 +106,7 @@ def apply(self, **kwargs: Any) -> None:
text = self.config[refname]
if refname == 'today' and not text:
# special handling: can also specify a strftime format
text = format_date(self.config.today_fmt or str(_('%b %d, %Y')),
text = format_date(self.config.today_fmt or _('%b %d, %Y'),
language=self.config.language)
ref.replace_self(nodes.Text(text))

Expand Down
2 changes: 1 addition & 1 deletion sphinx/writers/manpage.py
Expand Up @@ -93,7 +93,7 @@ def __init__(self, document: nodes.document, builder: Builder) -> None:
if self.config.today:
self._docinfo['date'] = self.config.today
else:
self._docinfo['date'] = format_date(self.config.today_fmt or str(_('%b %d, %Y')),
self._docinfo['date'] = format_date(self.config.today_fmt or _('%b %d, %Y'),
language=self.config.language)
self._docinfo['copyright'] = self.config.copyright
self._docinfo['version'] = self.config.version
Expand Down
2 changes: 1 addition & 1 deletion sphinx/writers/texinfo.py
Expand Up @@ -220,7 +220,7 @@ def init_settings(self) -> None:
'project': self.escape(self.config.project),
'copyright': self.escape(self.config.copyright),
'date': self.escape(self.config.today or
format_date(self.config.today_fmt or str(_('%b %d, %Y')),
format_date(self.config.today_fmt or _('%b %d, %Y'),
language=self.config.language)),
})
# title
Expand Down
4 changes: 2 additions & 2 deletions sphinx/writers/text.py
Expand Up @@ -791,8 +791,8 @@ def visit_acks(self, node: Element) -> None:

def visit_image(self, node: Element) -> None:
if 'alt' in node.attributes:
self.add_text(str(_('[image: %s]') % node['alt']))
self.add_text(str(_('[image]')))
self.add_text(_('[image: %s]') % node['alt'])
self.add_text(_('[image]'))
raise nodes.SkipNode

def visit_transition(self, node: Element) -> None:
Expand Down
Binary file not shown.
2 changes: 0 additions & 2 deletions tests/roots/test-locale/locale1/et/LC_MESSAGES/myext.po

This file was deleted.

41 changes: 0 additions & 41 deletions tests/test_locale.py
Expand Up @@ -55,44 +55,3 @@ def test_add_message_catalog(app, rootdir):
assert _('Hello world') == 'HELLO WORLD'
assert _('Hello sphinx') == 'Hello sphinx'
assert _('Hello reST') == 'Hello reST'


def _empty_language_translation(rootdir):
locale_dirs, catalog = [rootdir / 'test-locale' / 'locale1'], 'myext'
locale.translators.clear()
locale.init(locale_dirs, language=None, catalog=catalog)
return locale.get_translation(catalog)


def test_init_environment_language(rootdir, monkeypatch):
with monkeypatch.context() as m:
m.setenv("LANGUAGE", "en_US:en")
_ = _empty_language_translation(rootdir)
assert _('Hello world') == 'HELLO WORLD'

with monkeypatch.context() as m:
m.setenv("LANGUAGE", "et_EE:et")
_ = _empty_language_translation(rootdir)
assert _('Hello world') == 'Tere maailm'


def test_init_reproducible_build_language(rootdir, monkeypatch):
with monkeypatch.context() as m:
m.setenv("SOURCE_DATE_EPOCH", "0")
m.setenv("LANGUAGE", "en_US:en")
_ = _empty_language_translation(rootdir)
sde_en_translation = str(_('Hello world')) # str cast to evaluate lazy method

with monkeypatch.context() as m:
m.setenv("SOURCE_DATE_EPOCH", "0")
m.setenv("LANGUAGE", "et_EE:et")
_ = _empty_language_translation(rootdir)
sde_et_translation = str(_('Hello world')) # str cast to evaluate lazy method

with monkeypatch.context() as m:
m.setenv("LANGUAGE", "et_EE:et")
_ = _empty_language_translation(rootdir)
loc_et_translation = str(_('Hello world')) # str cast to evaluate lazy method

assert sde_en_translation == sde_et_translation
assert sde_et_translation != loc_et_translation
56 changes: 0 additions & 56 deletions tests/test_util_inventory.py
Expand Up @@ -4,7 +4,6 @@
import zlib
from io import BytesIO

from sphinx.testing.util import SphinxTestApp
from sphinx.util.inventory import InventoryFile

inventory_v1 = b'''\
Expand Down Expand Up @@ -84,58 +83,3 @@ def test_read_inventory_v2_not_having_version():
invdata = InventoryFile.load(f, '/util', posixpath.join)
assert invdata['py:module']['module1'] == \
('foo', '', '/util/foo.html#module-module1', 'Long Module desc')


def _write_appconfig(dir, language, prefix=None):
prefix = prefix or language
(dir / prefix).makedirs()
(dir / prefix / 'conf.py').write_text(f'language = "{language}"', encoding='utf8')
(dir / prefix / 'index.rst').write_text('index.rst', encoding='utf8')
assert sorted((dir / prefix).listdir()) == ['conf.py', 'index.rst']
assert (dir / prefix / 'index.rst').exists()
return (dir / prefix)


def _build_inventory(srcdir):
app = SphinxTestApp(srcdir=srcdir)
app.build()
app.cleanup()
return (app.outdir / 'objects.inv')


def test_inventory_localization(tempdir):
# Build an app using Estonian (EE) locale
srcdir_et = _write_appconfig(tempdir, "et")
inventory_et = _build_inventory(srcdir_et)

# Build the same app using English (US) locale
srcdir_en = _write_appconfig(tempdir, "en")
inventory_en = _build_inventory(srcdir_en)

# Ensure that the inventory contents differ
assert inventory_et.read_bytes() != inventory_en.read_bytes()


def test_inventory_reproducible(tempdir, monkeypatch):
with monkeypatch.context() as m:
# Configure reproducible builds
# See: https://reproducible-builds.org/docs/source-date-epoch/
m.setenv("SOURCE_DATE_EPOCH", "0")

# Build an app using Estonian (EE) locale
srcdir_et = _write_appconfig(tempdir, "et")
reproducible_inventory_et = _build_inventory(srcdir_et)

# Build the same app using English (US) locale
srcdir_en = _write_appconfig(tempdir, "en")
reproducible_inventory_en = _build_inventory(srcdir_en)

# Also build the app using Estonian (EE) locale without build reproducibility enabled
srcdir_et = _write_appconfig(tempdir, "et", prefix="localized")
localized_inventory_et = _build_inventory(srcdir_et)

# Ensure that the reproducible inventory contents are identical
assert reproducible_inventory_et.read_bytes() == reproducible_inventory_en.read_bytes()

# Ensure that inventory contents are different between a localized and non-localized build
assert reproducible_inventory_et.read_bytes() != localized_inventory_et.read_bytes()

0 comments on commit a8c1ab4

Please sign in to comment.