diff --git a/.github/workflows/integration-testing.yaml b/.github/workflows/integration-testing.yaml
index fb9b9cf2eb..887dd2f73f 100644
--- a/.github/workflows/integration-testing.yaml
+++ b/.github/workflows/integration-testing.yaml
@@ -8,7 +8,7 @@ concurrency: integration_environment
jobs:
variables:
- if: ${{ github.event_name == 'merge_group' }}
+ if: ${{ github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
outputs:
date: ${{ steps.data.outputs.date }}
@@ -29,7 +29,7 @@ jobs:
echo "current_branch=merge-queue"
>> $GITHUB_OUTPUT
build-generic:
- if: ${{ github.event_name == 'merge_group' }}
+ if: ${{ github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch' }}
name: "Integration Image Build"
needs:
- variables
@@ -83,7 +83,7 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-derived:
- if: ${{ github.event_name == 'merge_group' }}
+ if: ${{ github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
name: "Integration Image Build Stage 2"
permissions:
@@ -140,7 +140,7 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
run-tests:
- if: ${{ github.event_name == 'merge_group' }}
+ if: ${{ github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch' }}
name: "Playwright" # This name is referenced when slacking status
needs:
- build-derived
@@ -204,7 +204,7 @@ jobs:
- name: Uninstall
run: helm delete integration-${{ needs.variables.outputs.commit }} -n ${{ secrets.DEV_SANDBOX_NAMESPACE }} --debug --timeout 10m0s
ending-notification:
- if: ${{ github.event_name == 'merge_group' }}
+ if: ${{ github.event_name == 'merge_group' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
needs:
- run-tests
diff --git a/e2e-tests/globals.ts b/e2e-tests/globals.ts
index a4b53698f2..959dd8b4a6 100644
--- a/e2e-tests/globals.ts
+++ b/e2e-tests/globals.ts
@@ -5,6 +5,12 @@ export const LANGUAGES = {
'HE': 'hebrew',
}
+export const SOURCE_LANGUAGES = {
+ 'EN': /^(תרגום|Translation)$/,
+ 'HE': /^(מקור|Source)$/,
+ 'BI': /^(מקור ותרגום|Source with Translation)$/
+}
+
export const cookieObject = {
"name": "interfaceLang",
"value": DEFAULT_LANGUAGE,
diff --git a/e2e-tests/tests/interface-language-is-sticky.spec.ts b/e2e-tests/tests/interface-language-is-sticky.spec.ts
new file mode 100644
index 0000000000..682d48ee93
--- /dev/null
+++ b/e2e-tests/tests/interface-language-is-sticky.spec.ts
@@ -0,0 +1,57 @@
+import {test, expect} from '@playwright/test';
+import {changeLanguageOfText, goToPageWithLang, isIsraelIp} from "../utils";
+import {LANGUAGES, SOURCE_LANGUAGES} from '../globals'
+
+const interfaceTextHE = 'מקורות';
+const interfaceTextEN = 'Texts';
+
+[
+ // Hebrew Interface and English Source
+ {interfaceLanguage: 'Hebrew', interfaceLanguageToggle: LANGUAGES.HE,
+ sourceLanguage: 'English', sourceLanguageToggle: SOURCE_LANGUAGES.EN,
+ expectedSourceText: 'When God began to create', expectedBilingualText: '', expectedInterfaceText: interfaceTextHE },
+
+ // Hebrew Interface and Bilingual Source
+ {interfaceLanguage: 'Hebrew', interfaceLanguageToggle: LANGUAGES.HE,
+ sourceLanguage: 'Bilingual', sourceLanguageToggle: SOURCE_LANGUAGES.BI,
+ expectedSourceText: 'רֵאשִׁ֖ית בָּרָ֣א אֱלֹהִ֑ים אֵ֥ת הַשָּׁמַ֖יִם וְאֵ֥ת הָאָֽרֶץ׃', expectedBilingualText: 'When God began to create', expectedInterfaceText: interfaceTextHE },
+
+ // Hebrew Interface and Hebrew Source
+ {interfaceLanguage: 'Hebrew', interfaceLanguageToggle: LANGUAGES.HE,
+ sourceLanguage: 'Hebrew', sourceLanguageToggle: SOURCE_LANGUAGES.HE,
+ expectedSourceText: 'רֵאשִׁ֖ית בָּרָ֣א אֱלֹהִ֑ים אֵ֥ת הַשָּׁמַ֖יִם וְאֵ֥ת הָאָֽרֶץ׃', expectedBilingualText: '', expectedInterfaceText: interfaceTextHE },
+
+ // English Interface and English Source
+ {interfaceLanguage: 'English', interfaceLanguageToggle: LANGUAGES.EN,
+ sourceLanguage: 'English', sourceLanguageToggle: SOURCE_LANGUAGES.EN,
+ expectedSourceText: 'When God began to create', expectedBilingualText: '', expectedInterfaceText: interfaceTextEN },
+
+ // English Interface and Bilingual Source
+ {interfaceLanguage: 'English', sinterfaceLanguageToggle: LANGUAGES.EN,
+ sourceLanguage: 'Bilingual', sourceLanguageToggle: SOURCE_LANGUAGES.BI,
+ expectedSourceText: 'רֵאשִׁ֖ית בָּרָ֣א אֱלֹהִ֑ים אֵ֥ת הַשָּׁמַ֖יִם וְאֵ֥ת הָאָֽרֶץ׃', expectedBilingualText: 'When God began to create',
+ expectedInterfaceText: interfaceTextEN },
+
+ // English Interface and Hebrew Source
+ {interfaceLanguage: 'English', interfaceLanguageToggle: LANGUAGES.EN,
+ sourceLanguage: 'Hebrew', sourceLanguageToggle: SOURCE_LANGUAGES.HE,
+ expectedSourceText: 'רֵאשִׁ֖ית בָּרָ֣א אֱלֹהִ֑ים אֵ֥ת הַשָּׁמַ֖יִם וְאֵ֥ת הָאָֽרֶץ׃', expectedBilingualText: '', expectedInterfaceText: interfaceTextEN }
+
+].forEach(({interfaceLanguage, interfaceLanguageToggle, sourceLanguage, sourceLanguageToggle, expectedSourceText, expectedBilingualText, expectedInterfaceText}) => {
+ test(`${interfaceLanguage} Interface Language with ${sourceLanguage} Source`, async ({ context }) => {
+
+ // Navigating to Bereshit with selected Interface Language, Hebrew or English
+ const page = await goToPageWithLang(context,'/Genesis.1',`${interfaceLanguageToggle}`)
+
+ // Selecting Source Language
+ await changeLanguageOfText(page, sourceLanguageToggle)
+
+ // Locating the source text segment, then verifying translation
+ await expect(page.locator('div.segmentNumber').first().locator('..').locator('p')).toContainText(`${expectedSourceText}`)
+
+ // Validate Hebrew interface language is still toggled
+ const textLink = page.locator('a.textLink').first()
+ await expect(textLink).toHaveText(`${expectedInterfaceText}`)
+
+ })
+})
\ No newline at end of file
diff --git a/e2e-tests/tests/reader.spec.ts b/e2e-tests/tests/reader.spec.ts
index 3d424d4065..9ed0ae1e6e 100644
--- a/e2e-tests/tests/reader.spec.ts
+++ b/e2e-tests/tests/reader.spec.ts
@@ -15,14 +15,14 @@ test('Navigate to bereshit', async ({ context }) => {
});
test('Verify translations', async ({ context }) => {
- const page = await goToPageWithLang(context, '/Berakhot.28b.4?vhe=Wikisource_Talmud_Bavli&lang=bi&with=all&lang2=he');
+ const page = await goToPageWithLang(context, '/Berakhot.28b.4?vhe=hebrew|Wikisource_Talmud_Bavli&lang=bi&with=all&lang2=he');
await page.getByRole('link', { name: 'Translations (4)' }).click();
await page.locator('#panel-1').getByText('Loading...').waitFor({ state: 'detached' });
page.getByText('A. Cohen, Cambridge University Press, 1921', { exact: true })
});
test('Get word description', async ({ context }) => {
- const page = await goToPageWithLang(context, '/Berakhot.28b.4?vhe=Wikisource_Talmud_Bavli&lang=bi&with=all&lang2=he');
+ const page = await goToPageWithLang(context, '/Berakhot.28b.4?vhe=hebrew|Wikisource_Talmud_Bavli&lang=bi&with=all&lang2=he');
await page.getByRole('link', { name: 'ר\' נחוניא בן הקנה' }).click();
await page.locator('#panel-1').getByText('Loading...').waitFor({ state: 'detached' });
await page.getByText('Looking up words...').waitFor({ state: 'detached' });
@@ -31,11 +31,11 @@ test('Get word description', async ({ context }) => {
test('Open panel window', async ({ context }) => {
- const page = await goToPageWithLang(context, '/Berakhot.28b.4?vhe=Wikisource_Talmud_Bavli&lang=bi&with=all&lang2=he');
+ const page = await goToPageWithLang(context, '/Berakhot.28b.4?vhe=hebrew|Wikisource_Talmud_Bavli&&lang=bi&with=all&lang2=he');
await page.getByText('ולית הלכתא לא כרב הונא ולא כריב"ל כרב הונא הא דאמרן כריב"ל דאריב"ל כיון שהגיע זמ').click();
await page.locator('#panel-1').getByText('Loading...').waitFor({ state: 'detached' });
await page.getByRole('link', { name: 'תלמוד (1)' }).click();
- await page.getByRole('link', { name: 'שבת (1) מלאכות האסורות בשבת ודינים הקשורים לקדושת היום.' }).click();
+ await page.getByRole('link', { name: /^שבת/ }).click();
await page.getByText('טעינה...').waitFor({ state: 'detached' });
await page.getByRole('link', { name: 'Open' }).click();
await page.getByRole('heading', { name: 'Loading...' }).getByText('Loading...').waitFor({ state: 'detached' });
diff --git a/e2e-tests/tests/translation-version-name-appears-in-title.spec.ts b/e2e-tests/tests/translation-version-name-appears-in-title.spec.ts
new file mode 100644
index 0000000000..46bb51ce58
--- /dev/null
+++ b/e2e-tests/tests/translation-version-name-appears-in-title.spec.ts
@@ -0,0 +1,80 @@
+import {test, expect} from '@playwright/test';
+import {goToPageWithLang, changeLanguageOfText} from "../utils";
+import {LANGUAGES, SOURCE_LANGUAGES} from '../globals'
+
+[
+ // Hebrew Interface and Hebrew Source
+ {interfaceLanguage: 'Hebrew', interfaceLanguageToggle: LANGUAGES.HE,
+ sourceLanguage: 'Hebrew', sourceLanguageToggle: SOURCE_LANGUAGES.HE,
+ translations: 'תרגומים', select: 'בחירה', currentlySelected: 'נוכחי'},
+
+ // Hebrew Interface and Bilingual Source
+ {interfaceLanguage: 'Hebrew', interfaceLanguageToggle: LANGUAGES.HE,
+ sourceLanguage: 'Bilingual', sourceLanguageToggle: SOURCE_LANGUAGES.BI,
+ translations: 'תרגומים', select: 'בחירה', currentlySelected: 'נוכחי'},
+
+ // Hebrew Interface and English Source
+ {interfaceLanguage: 'Hebrew', interfaceLanguageToggle: LANGUAGES.HE,
+ sourceLanguage: 'English', sourceLanguageToggle: SOURCE_LANGUAGES.EN,
+ translations: 'תרגומים', select: 'בחירה', currentlySelected: 'נוכחי'},
+
+ // English Interface and English Source
+ {interfaceLanguage: 'English', interfaceLanguageToggle: LANGUAGES.EN,
+ sourceLanguage: 'English', sourceLanguageToggle: SOURCE_LANGUAGES.EN,
+ translations: 'Translations', select: 'Select', currentlySelected: 'Currently Selected'},
+
+ // English Interface and English Source
+ {interfaceLanguage: 'English', interfaceLanguageToggle: LANGUAGES.EN,
+ sourceLanguage: 'Bilingual', sourceLanguageToggle: SOURCE_LANGUAGES.BI,
+ translations: 'Translations', select: 'Select', currentlySelected: 'Currently Selected'},
+
+ // English Interface and Hebrew Source
+ {interfaceLanguage: 'English', interfaceLanguageToggle: LANGUAGES.EN,
+ sourceLanguage: 'Hebrew', sourceLanguageToggle: SOURCE_LANGUAGES.HE,
+ translations: 'Translations', select: 'Select', currentlySelected: 'Currently Selected'}
+
+].forEach(({interfaceLanguage, interfaceLanguageToggle, sourceLanguage, sourceLanguageToggle, translations, currentlySelected, select}) => {
+ test(`${interfaceLanguage} - translation name appears in title for ${sourceLanguage} source text`, async ({ context }) => {
+ // Navigate to Bereshit in specified Interface Language
+ const page = await goToPageWithLang(context,'/Genesis.1', `${interfaceLanguageToggle}`)
+
+ // Change the Source Language of the text
+ await changeLanguageOfText(page, sourceLanguageToggle)
+
+ // Retain the translation name locator
+ const translationNameInTitle = page.locator('span.readerTextVersion')
+
+ // Navigate to the Translations sidebar by clicking on the text title
+ //Clicks on בראשית א׳ / Genesis I
+ await page.locator('h1').click()
+
+ // Click on Translations
+ await page.getByRole('link', {name: `${translations}`}).click()
+
+ // Wait for Translations side-bar to load by waiting for 'Translations' header
+ await page.waitForSelector('h3')
+
+ // Check if the default translation in the title matches the selected translation
+ // NOTE: We are skipping checking for the default translation here, due to the Hebrew text being default Masoretic
+ if(sourceLanguage !== 'Hebrew'){
+ const defaultTranslation = await translationNameInTitle.textContent()
+ await expect(page.locator('div.version-with-preview-title-line', {hasText: defaultTranslation!}).getByRole('link')).toHaveText(`${currentlySelected}`)
+ }
+
+ // TODO: 4th translation, handling Hebrew Interface translations in Hebrew. For example: 'חומש רש״י, רבי שרגא זילברשטיין' should appear in the translation title as written.
+ const translationNames = ['The Schocken Bible, Everett Fox, 1995 ©', '«Да» project']
+
+ // Utilizing the traditional for-loop as there are async issues with foreach
+ for(let i = 0; i < translationNames.length; i++){
+
+ // "Select" another translation.
+ await page.locator('div.version-with-preview-title-line', {hasText: translationNames[i]}).getByText(`${select}`).click()
+
+ // Validate selected translation is reflected in title
+ await expect(translationNameInTitle).toHaveText(translationNames[i])
+
+ // Validate selected translation says 'Currently Selected'
+ await expect(page.locator('div.version-with-preview-title-line', {hasText: translationNames[i]}).getByRole('link')).toHaveText(`${currentlySelected}`)
+ }
+ })
+});
\ No newline at end of file
diff --git a/e2e-tests/utils.ts b/e2e-tests/utils.ts
index dc3922d64e..22d75a0270 100644
--- a/e2e-tests/utils.ts
+++ b/e2e-tests/utils.ts
@@ -1,4 +1,4 @@
-import {DEFAULT_LANGUAGE, LANGUAGES, testUser} from './globals'
+import {DEFAULT_LANGUAGE, LANGUAGES, SOURCE_LANGUAGES, testUser} from './globals'
import {BrowserContext} from 'playwright-core';
import type { Page } from 'playwright-core';
@@ -23,13 +23,16 @@ export const changeLanguage = async (page: Page, language: string) => {
}
export const goToPageWithLang = async (context: BrowserContext, url: string, language=DEFAULT_LANGUAGE) => {
- if (!langCookies.length) {
- const page: Page = await context.newPage();
- await page.goto('');
- await changeLanguage(page, language);
- langCookies = await context.cookies();
- }
+ // If a cookie already has contents, clear it so that the language cookie can be reset
+ if (langCookies.length) {
+ await context.clearCookies()
+ }
+ const page: Page = await context.newPage();
+ await page.goto('');
+ await changeLanguage(page, language);
+ langCookies = await context.cookies();
await context.addCookies(langCookies);
+
// this is a hack to get the cookie to work
const newPage: Page = await context.newPage();
await newPage.goto(url);
@@ -65,4 +68,26 @@ export const goToPageWithUser = async (context: BrowserContext, url: string, use
export const getPathAndParams = (url: string) => {
const urlObj = new URL(url);
return urlObj.pathname + urlObj.search;
+}
+
+export const changeLanguageOfText = async (page: Page, sourceLanguage: RegExp) => {
+ // Clicking on the Source Language toggle
+ await page.getByAltText('Toggle Reader Menu Display Settings').click()
+
+ // Selecting Source Language
+ await page.locator('div').filter({ hasText: sourceLanguage }).click()
+}
+
+export const getCountryByIp = async (page: Page) => {
+ const data = await page.evaluate(() => {
+ return fetch('https://ipapi.co/json/')
+ .then(response => response.json())
+ .then(data => data)
+ })
+ return data.country;
+}
+
+export const isIsraelIp = async (page: Page) => {
+ const country = await getCountryByIp(page);
+ return country === "IL";
}
\ No newline at end of file
diff --git a/reader/views.py b/reader/views.py
index 418a67aee6..3da1370813 100644
--- a/reader/views.py
+++ b/reader/views.py
@@ -34,6 +34,7 @@
from sefaria.model import *
from sefaria.google_storage_manager import GoogleStorageManager
+from sefaria.model.text_reuqest_adapter import TextRequestAdapter
from sefaria.model.user_profile import UserProfile, user_link, public_user_data, UserWrapper
from sefaria.model.collection import CollectionSet
from sefaria.model.webpage import get_webpages_for_ref
@@ -280,6 +281,18 @@ def user_credentials(request):
return {"user_type": "API", "user_id": apikey["uid"]}
+def _reader_redirect_add_languages(request, tref):
+ versions = Ref(tref).version_list()
+ query_params = QueryDict(mutable=True)
+ for vlang, direction in [('ven', 'ltr'), ('vhe', 'rtl')]:
+ version_title = request.GET.get(vlang)
+ if version_title:
+ version_title = version_title.replace('_', ' ')
+ version = next((v for v in versions if v['direction'] == direction and v['versionTitle'] == version_title))
+ query_params[vlang] = f'{version["languageFamilyName"]}|{version["versionTitle"]}'
+ return redirect(f'/{tref}/?{urllib.parse.urlencode(query_params)}')
+
+
@ensure_csrf_cookie
def catchall(request, tref, sheet=None):
"""
@@ -295,6 +308,10 @@ def reader_redirect(uref):
response['Location'] += "?%s" % params if params else ""
return response
+ for version in ['ven', 'vhe']:
+ if request.GET.get(version) and '|' not in request.GET.get(version):
+ return _reader_redirect_add_languages(request, tref)
+
if sheet is None:
try:
oref = Ref.instantiate_ref_with_legacy_parse_fallback(tref)
@@ -320,7 +337,7 @@ def old_versions_redirect(request, tref, lang, version):
def get_connections_mode(filter):
# List of sidebar modes that can function inside a URL parameter to open the sidebar in that state.
- sidebarModes = ("Sheets", "Notes", "About", "AboutSheet", "Navigation", "Translations", "Translation Open","WebPages", "extended notes", "Topics", "Torah Readings", "manuscripts", "Lexicon", "SidebarSearch", "Guide")
+ sidebarModes = ("Sheets", "Notes", "About", "AboutSheet", "Navigation", "Translations", "Translation Open", "Version Open", "WebPages", "extended notes", "Topics", "Torah Readings", "manuscripts", "Lexicon", "SidebarSearch", "Guide")
if filter[0] in sidebarModes:
return filter[0], True
elif filter[0].endswith(" ConnectionsList"):
@@ -330,7 +347,7 @@ def get_connections_mode(filter):
else:
return "TextList", False
-def make_panel_dict(oref, versionEn, versionHe, filter, versionFilter, mode, **kwargs):
+def make_panel_dict(oref, primaryVersion, translationVersion, filter, versionFilter, mode, **kwargs):
"""
Returns a dictionary corresponding to the React panel state,
additionally setting `text` field with textual content.
@@ -338,15 +355,8 @@ def make_panel_dict(oref, versionEn, versionHe, filter, versionFilter, mode, **k
if oref.is_book_level():
index_details = library.get_index(oref.normal()).contents(with_content_counts=True)
index_details["relatedTopics"] = get_topics_for_book(oref.normal(), annotate=True)
- if kwargs.get('extended notes', 0) and (versionEn is not None or versionHe is not None):
- currVersions = {"en": versionEn, "he": versionHe}
- if versionEn is not None and versionHe is not None:
- curr_lang = kwargs.get("panelDisplayLanguage", "en")
- for key in list(currVersions.keys()):
- if key == curr_lang:
- continue
- else:
- currVersions[key] = None
+ if kwargs.get('extended notes', 0):
+ currVersions = {"en": translationVersion, "he": primaryVersion}
panel = {
"menuOpen": "extended notes",
"mode": "Menu",
@@ -369,10 +379,7 @@ def make_panel_dict(oref, versionEn, versionHe, filter, versionFilter, mode, **k
"mode": mode,
"ref": oref.normal(),
"refs": [oref.normal()] if not oref.is_spanning() else [r.normal() for r in oref.split_spanning_ref()],
- "currVersions": {
- "en": versionEn,
- "he": versionHe,
- },
+ "currVersions": {"en": translationVersion, "he": primaryVersion},
"filter": filter,
"versionFilter": versionFilter,
}
@@ -403,15 +410,29 @@ def make_panel_dict(oref, versionEn, versionHe, filter, versionFilter, mode, **k
if settings_override:
panel["settings"] = settings_override
if mode != "Connections" and oref != None:
- try:
- text_family = TextFamily(oref, version=panel["currVersions"]["en"], lang="en", version2=panel["currVersions"]["he"], lang2="he", commentary=False,
- context=True, pad=True, alts=True, wrapLinks=False, translationLanguagePreference=kwargs.get("translationLanguagePreference", None)).contents()
- except NoVersionFoundError:
- text_family = {}
- text_family["updateFromAPI"] = True
- text_family["next"] = oref.next_section_ref().normal() if oref.next_section_ref() else None
- text_family["prev"] = oref.prev_section_ref().normal() if oref.prev_section_ref() else None
- panel["text"] = text_family
+ primary_params = [primaryVersion['languageFamilyName'], primaryVersion['versionTitle']]
+ primary_params[0] = primary_params[0] or 'primary'
+ translation_params = [translationVersion['languageFamilyName'], translationVersion['versionTitle']]
+ translation_params[0] = translation_params[0] or 'translation'
+ text_adapter = TextRequestAdapter(oref.section_ref(), [primary_params, translation_params], return_format='wrap_all_entities')
+ text = text_adapter.get_versions_for_query()
+ # text['ref'] = oref.normal()
+ #now we we should add the he and text attributes
+ if len(text['versions']) == 2:
+ if text['versions'][0]['isPrimary'] and not text['versions'][1]['isSource']:
+ text['he'], text['text'] = text['versions'][0]['text'], text['versions'][1]['text']
+ else:
+ text['he'], text['text'] = text['versions'][1]['text'], text['versions'][0]['text']
+ elif len(text['versions']) == 1:
+ if primary_params == translation_params:
+ text['he'] = text['text'] = text['versions'][0]['text']
+ elif [text['versions'][0]['languageFamilyName'], text['versions'][0]['versionTitle']] == translation_params:
+ text['text'], text['he'] = text['versions'][0]['text'], []
+ else:
+ text['he'], text['text'] = text['versions'][0]['text'], []
+
+ text["updateFromAPI"] = True
+ panel["text"] = text
if oref.index.categories == ["Tanakh", "Torah"]:
panel["indexDetails"] = oref.index.contents() # Included for Torah Parashah titles rendered in text
@@ -490,7 +511,7 @@ def make_sheet_panel_dict(sheet_id, filter, **kwargs):
return panels
-def make_panel_dicts(oref, versionEn, versionHe, filter, versionFilter, multi_panel, **kwargs):
+def make_panel_dicts(oref, primaryVersion, translationVersion, filter, versionFilter, multi_panel, **kwargs):
"""
Returns an array of panel dictionaries.
Depending on whether `multi_panel` is True, connections set in `filter` are displayed in either 1 or 2 panels.
@@ -498,16 +519,22 @@ def make_panel_dicts(oref, versionEn, versionHe, filter, versionFilter, multi_pa
panels = []
# filter may have value [], meaning "all". Therefore we test filter with "is not None".
if filter is not None and multi_panel:
- panels += [make_panel_dict(oref, versionEn, versionHe, filter, versionFilter, "Text", **kwargs)]
- panels += [make_panel_dict(oref, versionEn, versionHe, filter, versionFilter, "Connections", **kwargs)]
+ panels += [make_panel_dict(oref, primaryVersion, translationVersion, filter, versionFilter, "Text", **kwargs)]
+ panels += [make_panel_dict(oref, primaryVersion, translationVersion, filter, versionFilter, "Connections", **kwargs)]
elif filter is not None and not multi_panel:
- panels += [make_panel_dict(oref, versionEn, versionHe, filter, versionFilter, "TextAndConnections", **kwargs)]
+ panels += [make_panel_dict(oref, primaryVersion, translationVersion, filter, versionFilter, "TextAndConnections", **kwargs)]
else:
- panels += [make_panel_dict(oref, versionEn, versionHe, filter, versionFilter, "Text", **kwargs)]
+ panels += [make_panel_dict(oref, primaryVersion, translationVersion, filter, versionFilter, "Text", **kwargs)]
return panels
+def _extract_version_params(request, key):
+ params = request.GET.get(key, '|')
+ params = params.replace("_", " ")
+ languageFamilyName, versionTitle = params.split('|')
+ return {'languageFamilyName': languageFamilyName, 'versionTitle': versionTitle}
+
@sanitize_get_params
def text_panels(request, ref, version=None, lang=None, sheet=None):
"""
@@ -525,12 +552,8 @@ def text_panels(request, ref, version=None, lang=None, sheet=None):
panels = []
multi_panel = not request.user_agent.is_mobile and not "mobile" in request.GET
# Handle first panel which has a different signature in params
- versionEn = request.GET.get("ven", None)
- if versionEn:
- versionEn = versionEn.replace("_", " ")
- versionHe = request.GET.get("vhe", None)
- if versionHe:
- versionHe = versionHe.replace("_", " ")
+ primaryVersion = _extract_version_params(request, 'vhe')
+ translationVersion = _extract_version_params(request, 'ven')
filter = request.GET.get("with").replace("_", " ").split("+") if request.GET.get("with") else None
filter = [] if filter == ["all"] else filter
@@ -540,11 +563,7 @@ def text_panels(request, ref, version=None, lang=None, sheet=None):
if sheet == None:
versionFilter = [request.GET.get("vside").replace("_", " ")] if request.GET.get("vside") else []
- if versionEn and not Version().load({"versionTitle": versionEn, "language": "en"}):
- raise Http404
- if versionHe and not Version().load({"versionTitle": versionHe, "language": "he"}):
- raise Http404
- versionEn, versionHe = override_version_with_preference(oref, request, versionEn, versionHe)
+ # versionEn, versionHe = override_version_with_preference(oref, request, versionEn, versionHe) #TODO
kwargs = {
"panelDisplayLanguage": request.GET.get("lang", request.contentLang),
@@ -564,7 +583,7 @@ def text_panels(request, ref, version=None, lang=None, sheet=None):
kwargs["sidebarSearchQuery"] = request.GET.get("sbsq", None)
kwargs["selectedNamedEntity"] = request.GET.get("namedEntity", None)
kwargs["selectedNamedEntityText"] = request.GET.get("namedEntityText", None)
- panels += make_panel_dicts(oref, versionEn, versionHe, filter, versionFilter, multi_panel, **kwargs)
+ panels += make_panel_dicts(oref, primaryVersion, translationVersion, filter, versionFilter, multi_panel, **kwargs)
elif sheet == True:
panels += make_sheet_panel_dict(ref, filter, **{"panelDisplayLanguage": request.GET.get("lang",request.contentLang), "referer": request.path})
diff --git a/sefaria/datatype/jagged_array.py b/sefaria/datatype/jagged_array.py
index 5d35642afb..3957960061 100644
--- a/sefaria/datatype/jagged_array.py
+++ b/sefaria/datatype/jagged_array.py
@@ -659,12 +659,13 @@ def modify_by_function(self, func, start_sections=None, _cur=None, _curSections=
Func should accept two parameters: 1) text of current segment 2) zero-indexed indices of segment
:param start_sections: array(int), optional param. Sections passed to `func` will be offset by `start_sections`, if passed
"""
- _curSections = _curSections or []
if _cur is None:
_cur = self._store
if isinstance(_cur, str):
+ _curSections = _curSections or [0]
return func(_cur, self.get_offset_sections(_curSections, start_sections))
elif isinstance(_cur, list):
+ _curSections = _curSections or []
return [self.modify_by_function(func, start_sections, temp_curr, _curSections + [i]) for i, temp_curr in enumerate(_cur)]
def flatten_to_array(self, _cur=None):
diff --git a/sefaria/model/text_reuqest_adapter.py b/sefaria/model/text_reuqest_adapter.py
index d98aa31ba1..3030522300 100644
--- a/sefaria/model/text_reuqest_adapter.py
+++ b/sefaria/model/text_reuqest_adapter.py
@@ -98,7 +98,6 @@ def _add_ref_data_to_return_obj(self) -> None:
'heSectionRef': oref.section_ref().he_normal(),
'firstAvailableSectionRef': oref.first_available_section_ref().normal(),
'isSpanning': oref.is_spanning(),
- 'spanningRefs': [r.normal() for r in oref.split_spanning_ref()],
'next': oref.next_section_ref().normal() if oref.next_section_ref() else None,
'prev': oref.prev_section_ref().normal() if oref.prev_section_ref() else None,
'title': oref.context_ref().normal(),
@@ -107,6 +106,8 @@ def _add_ref_data_to_return_obj(self) -> None:
'primary_category': oref.primary_category,
'type': oref.primary_category, #same as primary category
})
+ if self.return_obj['isSpanning']:
+ self.return_obj['spanningRefs'] = [r.normal() for r in oref.split_spanning_ref()]
def _add_index_data_to_return_obj(self) -> None:
index = self.oref.index
diff --git a/sourcesheets/views.py b/sourcesheets/views.py
index 1340dc9111..41e3a2ff73 100644
--- a/sourcesheets/views.py
+++ b/sourcesheets/views.py
@@ -2,7 +2,7 @@
import json
import httplib2
from urllib3.exceptions import NewConnectionError
-from urllib.parse import unquote
+from urllib.parse import unquote, urlencode
from elasticsearch.exceptions import AuthorizationException
from datetime import datetime
from io import StringIO, BytesIO
@@ -193,6 +193,15 @@ def view_sheet(request, sheet_id, editorMode = False):
editor = request.GET.get('editor', '0')
embed = request.GET.get('embed', '0')
+ interface_lang = request.interfaceLang
+ content_lang = request.GET.get("lang", request.contentLang)
+ fixed_content_lang = 'he' if interface_lang == 'hebrew' else 'bi'
+ if content_lang != fixed_content_lang:
+ query_params = request.GET.dict()
+ query_params['lang'] = fixed_content_lang
+ new_url = f"/sheets/{sheet_id}?{urlencode(query_params)}"
+ return redirect(new_url, permanent=True)
+
if editor != '1' and embed !='1' and editorMode is False:
return catchall(request, sheet_id, True)
diff --git a/static/css/s2.css b/static/css/s2.css
index c06d921d2b..62afbc7436 100644
--- a/static/css/s2.css
+++ b/static/css/s2.css
@@ -1485,7 +1485,8 @@ div.interfaceLinks-row a {
.readerPanel .content {
direction: ltr; /* Even in Hebrew Interface, we want scroll bars on the right */
}
-.readerPanel .he {
+.readerPanel .he,
+.readerPanel .content .rtl {
direction: rtl;
text-align: right;
unicode-bidi: initial;
@@ -1519,9 +1520,19 @@ div.interfaceLinks-row a {
.readerPanel.bilingual .readerNavMenu .gridBox {
direction: ltr;
}
-.readerPanel.english .he {
+.readerPanel.english .contentText .he,
+.readerPanel.hebrew .contentText .en,
+.readerPanel.english .contentSpan.primary,
+.readerPanel.english .languageToggle .he,
+.readerPanel.hebrew .contentSpan.translation,
+.readerPanel.hebrew .languageToggle .en {
display: none;
}
+.readerPanel.english .versionsTextList .primary,
+.readerPanel.hebrew .versionsTextList .translation {
+ display: block;
+}
+
.readerPanel.english .he.heOnly{
display: inline;
text-align: right;
@@ -1533,11 +1544,10 @@ div.interfaceLinks-row a {
display: inline;
text-align: right;
}
-.readerPanel.hebrew .en {
- display: none;
-}
.readerPanel.english .heOnly .he,
-.readerPanel.bilingual .heOnly .he {
+.readerPanel.bilingual .heOnly .he,
+.readerPanel.english .enOnly .en,
+.readerPanel.bilingual .enOnly .en {
display: inline;
}
.languageToggle {
@@ -4893,6 +4903,10 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
.tagsList .enOnly {
direction: ltr;
}
+.readerControlsOuter {
+ position: relative;
+ z-index: 103;
+}
.readerControls {
position: relative;
top: 0;
@@ -4900,7 +4914,6 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
width: 100%;
box-sizing: border-box;
text-align: center;
- z-index: 100;
height: 60px;
line-height: 60px;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
@@ -4929,13 +4942,14 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
background-color: #EDEDED;
}
.readerControls .connectionsPanelHeader .connectionsHeaderTitle {
- text-transform: uppercase;
letter-spacing: 1px;
font-size: 16px;
font-weight: lighter;
}
+.readerControls .connectionsPanelHeader .connectionsHeaderTitle:not(.active) {
+ text-transform: uppercase;
+}
.connectionsPanelHeader .connectionsHeaderTitle.active {
- text-transform: none;
cursor: pointer;
}
.connectionsHeaderTitle .fa-chevron-left {
@@ -4965,7 +4979,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
}
.readerControls.transLangPrefSuggBann {
background-color: #EDEDEC;
- z-index: 99;
+ z-index: 2;
}
.readerControls .readerControlsInner.transLangPrefSuggBannInner {
justify-content: center;
@@ -5055,7 +5069,6 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
}
.readerControls.connectionsHeader .readerTextToc {
font-family: "Roboto", "Helvetica Neue", "Helvetica", sans-serif;
- text-transform: uppercase;
color: #666;
width: 100%;
}
@@ -5087,6 +5100,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
display: flex;
flex-direction: row;
text-align: right;
+ align-items: center;
}
/* icons need a little nudge in flipped hebrew mode */
.interface-hebrew .rightButtons {
@@ -5504,7 +5518,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
.bilingual .sheetContent .title .he {
display: none;
}
-.interface-hebrew .readerPanel.english .textRange,
+.interface-hebrew .readerPanel.ltr .textRange,
.interface-hebrew .readerPanel.english .categoryFilterGroup,
.interface-hebrew .readerPanel.bilingual .categoryFilterGroup,
.interface-hebrew .readerPanel.english .essayGroup,
@@ -5513,6 +5527,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
.interface-hebrew .readerPanel.bilingual .textTableOfContents {
direction: ltr;
}
+.interface-english .readerPanel.rtl .textRange,
.interface-english .readerPanel.hebrew .textRange,
.interface-english .readerPanel.hebrew .categoryFilterGroup,
.interface-english .readerPanel.hebrew .essayGroup,
@@ -5621,7 +5636,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
display: inline;
}*/
.bilingual .segment > .he,
-.bilingual .segment > p > .he{
+.bilingual .segment > p > .he {
display: block;
}
.bilingual .segment > .en,
@@ -5649,11 +5664,14 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
.stacked.bilingual .sheetContent .segment > p > .en {
margin-top: 0;
}
-.stacked.bilingual .basetext .segment > .en ,
-.stacked.bilingual .basetext .segment > p > .en {
+.stacked.bilingual .basetext .segment > .translation ,
+.stacked.bilingual .basetext .segment > p > .translation {
margin: 10px 0 20px;
color: #666;
}
+.stacked.bilingual .basetext .segment > p > .he.translation {
+ color: black;
+}
.stacked.bilingual .segment.heOnly > .he,
.stacked.bilingual .segment.enOnly > .en,
.stacked.bilingual .segment.heOnly > p > .he,
@@ -5708,20 +5726,28 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
.readerPanel.english .SheetSource .sheetItem.segment .en {
background-color: white;
}
-.heLeft.bilingual .segment > .en,
-.heRight.bilingual .segment > .he ,
-.heLeft.bilingual .segment > p > .en,
-.heRight.bilingual .segment > p > .he {
+.heLeft.bilingual .segment > .translation,
+.heRight.bilingual .segment > .primary,
+.heLeft.bilingual .segment > p > .translation,
+.heRight.bilingual .segment > p > .primary,
+.heRight.bilingual .sheetItem.segment > .he,
+.heLeft.bilingual .sheetItem.segment > .en{
float: right;
padding-left: 20px;
}
-.heRight.bilingual .segment > .en,
-.heLeft.bilingual .segment > .he,
-.heRight.bilingual .segment > p > .en,
-.heLeft.bilingual .segment > p > .he {
+.heRight.bilingual .segment > .translation,
+.heLeft.bilingual .segment > .primary,
+.heRight.bilingual .segment > p > .translation,
+.heLeft.bilingual .segment > p > .primary,
+.heRight.bilingual .sheetItem.segment > .en,
+.heLeft.bilingual .sheetItem.segment > .he{
float: left;
padding-right: 20px;
}
+.segment > p > .he.translation {
+ --hebrew-font: var(--hebrew-sans-serif-font-family);
+ font-size: 100%;
+}
.basetext .segment:active,
.basetext .segment:focus {
background-color: #f5faff;
@@ -5810,6 +5836,7 @@ body .ui-autocomplete.dictionary-toc-autocomplete .ui-menu-item a.ui-state-focus
.segment.heOnly .en{
display: none;
}
+/*in the text reader we don't have enOnly anymore. it always hvae primary (which is one the meaning of heOnly) maybe this is useful for other cases*/
.segment.enOnly .he{
display: none;
}
@@ -10626,11 +10653,14 @@ section.SheetOutsideBiText {
.readerPanel.hebrew section.SheetSource .sheetItem > .he,
.readerPanel.english section.SheetSource .sheetItem > .en,
+.readerPanel.hebrew section.SheetSource .sheetItem.enOnly > .en > .sourceContentText,
.readerPanel.hebrew section.SheetOutsideBiText .sheetItem > .he,
.readerPanel.english section.SheetOutsideBiText .sheetItem > .en {
display: block;
}
-.readerPanel.hebrew section.SheetSource .sheetItem > .en,
+.readerPanel.hebrew section.SheetSource .sheetItem:not(.enOnly) > .en,
+.readerPanel.hebrew section.SheetSource .sheetItem.enOnly > .en > .ref,
+.readerPanel.hebrew section.SheetSource .sheetItem.enOnly > .he > .sourceContentText,
.readerPanel.english section.SheetSource .sheetItem > .he,
.readerPanel.hebrew section.SheetOutsideBiText .sheetItem > .en,
.readerPanel.english section.SheetOutsideBiText .sheetItem > .he {
@@ -13348,6 +13378,216 @@ span.ref-link-color-3 {color: blue}
cursor: pointer;
}
+.dropdownMenu {
+ position: relative;
+ display: flex;
+ flex-direction: row-reverse;
+ z-index: 3;
+}
+
+.texts-properties-menu {
+ width: 256px;
+ border: 1px solid var(--lighter-grey);
+ border-radius: 5px;
+ box-shadow: 0px 2px 4px var(--lighter-grey);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ top: 100%;
+ background-color: white;
+ --english-font: var(--english-sans-serif-font-family);
+ --hebrew-font: var(--hebrew-sans-serif-font-family);
+}
+
+.dropdownButton {
+ border: none;
+ background-color: inherit;
+}
+
+.rightButtons .dropdownButton {
+ text-align: end;
+}
+
+.toggle-switch-container {
+ align-items: center;
+ display: flex;
+ direction: ltr;
+}
+
+.toggle-switch {
+ position: relative;
+ width: 46px;
+ display: inline-block;
+ text-align: left;
+}
+
+.toggle-switch-checkbox {
+ display: none;
+}
+
+.toggle-switch-label {
+ display: block;
+ overflow: hidden;
+ cursor: pointer;
+ border: 0 solid var(--light-grey);
+ border-radius: 20px;
+}
+
+.toggle-switch-inner {
+ display: block;
+ width: 200%;
+ margin-left: -100%;
+ transition: margin 0.3s ease-in 0s;
+}
+
+.toggle-switch-inner:before,
+.toggle-switch-inner:after {
+ float: left;
+ width: 50%;
+ height: 24px;
+ padding: 0;
+ line-height: 24px;
+ color: white;
+ font-weight: bold;
+ box-sizing: border-box;
+ content: "";
+ color: white;
+}
+
+.toggle-switch-inner:before {
+ padding-left: 10px;
+ background-color: var(--sefaria-blue);
+}
+
+.toggle-switch-inner:after {
+ padding-right: 10px;
+ background-color: var(--light-grey);
+}
+
+.toggle-switch-switch {
+ display: block;
+ width: 20px;
+ height: 20px;
+ background: white;
+ position: absolute;
+ top: 50%;
+ bottom: 0;
+ right: 24px;
+ border: 0 solid var(--light-grey);
+ border-radius: 20px;
+ transition: all 0.3s ease-in 0s;
+ transform: translateY(-50%);
+}
+
+.toggle-switch-checkbox:checked + .toggle-switch-label .toggle-switch-inner {
+ margin-left: 0;
+}
+
+.toggle-switch-checkbox:checked + .toggle-switch-label .toggle-switch-switch {
+ right: 2px;
+}
+
+.toggle-switch-checkbox:disabled + .toggle-switch-label .toggle-switch-inner:after {
+ background-color: var(--lighter-grey);
+}
+
+.toggle-switch-line {
+ display: flex;
+ width: 216px;
+ height: 49px;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.toggle-switch-line:is(.disabled) .int-en,
+.toggle-switch-line:is(.disabled) .int-he {
+ color: var(--light-grey);
+}
+
+.texts-properties-menu .int-en,
+.texts-properties-menu .int-he {
+ align-content: center;
+}
+
+.show-source-translation-buttons {
+ display: flex;
+ flex-direction: column;
+ height: 170px;
+ justify-content: center;
+}
+
+.show-source-translation-buttons .button {
+ margin: unset;
+ display: flex;
+ height: 46px;
+ width: 235px;
+ align-items: center;
+ justify-content: center;
+ margin: 3px 0;
+}
+
+.show-source-translation-buttons .button:not(.checked) {
+ background-color: var(--lighter-grey);
+ color: black;
+}
+
+.layout-button-line {
+ height: 57px;
+ width: 216px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.layout-options {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 16px;
+}
+
+.layout-button {
+ border: none;
+ width: 28px;
+ height: 24px;
+ -webkit-mask: var(--url) no-repeat;
+ -webkit-mask-size: contain;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-position: center;
+ -webkit-mask-size: 100% 100%;
+ background-color: var(--medium-grey);
+ cursor: pointer;
+}
+
+.layout-button.checked {
+ background-color: var(--sefaria-blue);
+}
+
+.text-menu-border {
+ width: 100%;
+ height: 1px;
+ background-color: var(--lighter-grey);
+}
+
+.font-size-line {
+ width: 230px;
+ height: 50px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ direction: ltr;
+}
+
+.font-size-button {
+ display: flex;
+ align-items: center;
+ background-color: white;
+ border: none;
+ cursor: pointer;
+}
+
#currentlyReadingContainer {
margin: 5px 30px;
flex-grow: 1;
diff --git a/static/icons/bi-ltr-heLeft.svg b/static/icons/bi-ltr-heLeft.svg
new file mode 100644
index 0000000000..2d28ee586f
--- /dev/null
+++ b/static/icons/bi-ltr-heLeft.svg
@@ -0,0 +1,50 @@
+
diff --git a/static/icons/bi-ltr-stacked.svg b/static/icons/bi-ltr-stacked.svg
new file mode 100644
index 0000000000..27fb13f535
--- /dev/null
+++ b/static/icons/bi-ltr-stacked.svg
@@ -0,0 +1,26 @@
+
diff --git a/static/icons/bi-rtl-heRight.svg b/static/icons/bi-rtl-heRight.svg
new file mode 100644
index 0000000000..67a7c095d0
--- /dev/null
+++ b/static/icons/bi-rtl-heRight.svg
@@ -0,0 +1,50 @@
+
diff --git a/static/icons/bi-rtl-stacked.svg b/static/icons/bi-rtl-stacked.svg
new file mode 100644
index 0000000000..035734e83f
--- /dev/null
+++ b/static/icons/bi-rtl-stacked.svg
@@ -0,0 +1,26 @@
+
diff --git a/static/icons/enlarge_font.svg b/static/icons/enlarge_font.svg
new file mode 100644
index 0000000000..3c1ff999d6
--- /dev/null
+++ b/static/icons/enlarge_font.svg
@@ -0,0 +1,3 @@
+
diff --git a/static/icons/mixed-beside-ltrrtl.svg b/static/icons/mixed-beside-ltrrtl.svg
new file mode 100644
index 0000000000..f19de475c1
--- /dev/null
+++ b/static/icons/mixed-beside-ltrrtl.svg
@@ -0,0 +1,50 @@
+
diff --git a/static/icons/mixed-beside-rtlltr.svg b/static/icons/mixed-beside-rtlltr.svg
new file mode 100644
index 0000000000..a35ebe96f5
--- /dev/null
+++ b/static/icons/mixed-beside-rtlltr.svg
@@ -0,0 +1,50 @@
+
diff --git a/static/icons/mixed-stacked-ltrrtl.svg b/static/icons/mixed-stacked-ltrrtl.svg
new file mode 100644
index 0000000000..a0d8d6bb75
--- /dev/null
+++ b/static/icons/mixed-stacked-ltrrtl.svg
@@ -0,0 +1,26 @@
+
diff --git a/static/icons/mixed-stacked-rtlltr.svg b/static/icons/mixed-stacked-rtlltr.svg
new file mode 100644
index 0000000000..3e088068f8
--- /dev/null
+++ b/static/icons/mixed-stacked-rtlltr.svg
@@ -0,0 +1,26 @@
+
diff --git a/static/icons/mono-continuous.svg b/static/icons/mono-continuous.svg
new file mode 100644
index 0000000000..f84c2f3fa8
--- /dev/null
+++ b/static/icons/mono-continuous.svg
@@ -0,0 +1,26 @@
+
diff --git a/static/icons/mono-segmented.svg b/static/icons/mono-segmented.svg
new file mode 100644
index 0000000000..9f1b6bffa9
--- /dev/null
+++ b/static/icons/mono-segmented.svg
@@ -0,0 +1,5 @@
+
diff --git a/static/icons/reduce_font.svg b/static/icons/reduce_font.svg
new file mode 100644
index 0000000000..0714abe7fb
--- /dev/null
+++ b/static/icons/reduce_font.svg
@@ -0,0 +1,3 @@
+
diff --git a/static/js/AboutBox.jsx b/static/js/AboutBox.jsx
index 791b77e42c..8bf5e2b9bd 100644
--- a/static/js/AboutBox.jsx
+++ b/static/js/AboutBox.jsx
@@ -3,9 +3,10 @@ import PropTypes from 'prop-types';
import Sefaria from './sefaria/sefaria';
import VersionBlock, {VersionsBlocksList} from './VersionBlock/VersionBlock';
import Component from 'react-class';
-import {InterfaceText} from "./Misc";
+import {InterfaceText, LoadingMessage} from "./Misc";
import {ContentText} from "./ContentText";
import { Modules } from './NavSidebar';
+import {VersionsTextList} from "./VersionsTextList";
class AboutBox extends Component {
@@ -63,8 +64,8 @@ class AboutBox extends Component {
this.setState({versionLangMap: versionsByLang, currentVersionsByActualLangs:currentVersionsByActualLangs});
}
openVersionInSidebar(versionTitle, versionLanguage) {
- this.props.setConnectionsMode("Translation Open", {previousMode: "About"});
- this.props.setFilter(Sefaria.getTranslateVersionsKey(versionTitle, versionLanguage));
+ this.props.setConnectionsMode("Version Open", {previousMode: "About"});
+ this.props.setFilter(Sefaria.getTranslateVersionsKey(versionTitle, versionLanguage), 'About');
}
isSheet(){
return this.props.srefs[0].startsWith("Sheet");
@@ -94,14 +95,36 @@ class AboutBox extends Component {
return
{altText}