From cfd9424e9fa2a5ba9ce5c3a2089e357af264b2e0 Mon Sep 17 00:00:00 2001 From: Leangseu Kim Date: Mon, 4 Mar 2024 10:42:43 -0500 Subject: [PATCH] chore: add src and dest translation --- src/course-home/data/api.js | 1 + .../Unit/__snapshots__/index.test.jsx.snap | 2 +- src/courseware/course/sequence/Unit/index.jsx | 14 +++-- .../course/sequence/Unit/index.test.jsx | 11 +++- .../useSelectLanguage.js | 5 +- .../useSelectLanguage.test.js | 13 +++-- src/courseware/course/sequence/Unit/urls.js | 8 ++- .../course/sequence/Unit/urls.test.js | 55 ++++++++++++------- src/courseware/data/api.js | 2 - 9 files changed, 72 insertions(+), 39 deletions(-) diff --git a/src/course-home/data/api.js b/src/course-home/data/api.js index 963bdefc76..685ba173d9 100644 --- a/src/course-home/data/api.js +++ b/src/course-home/data/api.js @@ -109,6 +109,7 @@ function normalizeCourseHomeCourseMetadata(metadata, rootSlug) { url: tab.url, })), isMasquerading: data.originalUserIsStaff && !data.isStaff, + language: data.language?.slice(0, 2), }; } diff --git a/src/courseware/course/sequence/Unit/__snapshots__/index.test.jsx.snap b/src/courseware/course/sequence/Unit/__snapshots__/index.test.jsx.snap index 5b93437a8d..169174db44 100644 --- a/src/courseware/course/sequence/Unit/__snapshots__/index.test.jsx.snap +++ b/src/courseware/course/sequence/Unit/__snapshots__/index.test.jsx.snap @@ -16,7 +16,7 @@ exports[`Unit component output BookmarkButton props not bookmarked, bookmark upd /> `; -exports[`Unit component output TranslationSelection renders if wholeCourseTranslationEnabled 1`] = ` +exports[`Unit component output TranslationSelection renders if wholeCourseTranslationEnabled and language is defined 1`] = `
diff --git a/src/courseware/course/sequence/Unit/index.jsx b/src/courseware/course/sequence/Unit/index.jsx index dcaff7a559..656f6cdda0 100644 --- a/src/courseware/course/sequence/Unit/index.jsx +++ b/src/courseware/course/sequence/Unit/index.jsx @@ -24,24 +24,28 @@ const Unit = ({ const examAccess = useExamAccess({ id }); const shouldDisplayHonorCode = useShouldDisplayHonorCode({ courseId, id }); const unit = useModel(modelKeys.units, id); - const { wholeCourseTranslationEnabled } = useModel('coursewareMeta', courseId); + const { language, wholeCourseTranslationEnabled } = useModel('courseHomeMeta', courseId); const isProcessing = unit.bookmarkedUpdateState === 'loading'; const view = authenticatedUser ? views.student : views.public; - const { selectedLanguage, setSelectedLanguage } = useSelectLanguage(courseId); + const { selectedLanguage, setSelectedLanguage } = useSelectLanguage({ + courseId, + language, + }); const iframeUrl = useMemo(() => getIFrameUrl({ id, view, format, examAccess, - translateLanguage: selectedLanguage, - }), [id, view, format, examAccess, selectedLanguage]); + srcLanguage: language, + destLanguage: selectedLanguage, + }), [id, view, format, examAccess, selectedLanguage, language]); return (

{unit.title}

- {wholeCourseTranslationEnabled && ( + {wholeCourseTranslationEnabled && language && ( ({ wholeCourseTranslationEnabled: fal const mockUnitsFn = jest.fn(() => unit); when(useModel) - .calledWith('coursewareMeta', props.courseId) + .calledWith('courseHomeMeta', props.courseId) .mockImplementation(mockCoursewareMetaFn) .calledWith(modelKeys.units, props.id) .mockImplementation(mockUnitsFn); @@ -201,8 +201,8 @@ describe('Unit component', () => { }); }); describe('TranslationSelection', () => { - test('renders if wholeCourseTranslationEnabled', () => { - mockCoursewareMetaFn.mockReturnValueOnce({ wholeCourseTranslationEnabled: true }); + test('renders if wholeCourseTranslationEnabled and language is defined', () => { + mockCoursewareMetaFn.mockReturnValueOnce({ wholeCourseTranslationEnabled: true, language: 'en' }); el = shallow(); expect(el.snapshot).toMatchSnapshot(); expect(el.instance.findByType('TranslationSelection')).toHaveLength(1); @@ -212,6 +212,11 @@ describe('Unit component', () => { el = shallow(); expect(el.instance.findByType('TranslationSelection')).toHaveLength(0); }); + test('does not render if language is undefined', () => { + mockCoursewareMetaFn.mockReturnValueOnce({ wholeCourseTranslationEnabled: true }); + el = shallow(); + expect(el.instance.findByType('TranslationSelection')).toHaveLength(0); + }); }); }); }); diff --git a/src/courseware/course/sequence/Unit/translation-selection/useSelectLanguage.js b/src/courseware/course/sequence/Unit/translation-selection/useSelectLanguage.js index 5977ee0244..c6aed3994a 100644 --- a/src/courseware/course/sequence/Unit/translation-selection/useSelectLanguage.js +++ b/src/courseware/course/sequence/Unit/translation-selection/useSelectLanguage.js @@ -6,6 +6,7 @@ import { } from '../../../../../data/localStorage'; export const selectedLanguageKey = 'selectedLanguages'; +// This will be use to check supported languages export const languages = Object.entries({ en: 'English', es: 'Spanish', @@ -18,11 +19,11 @@ export const stateKeys = StrictDict({ selectedLanguage: 'selectedLanguage', }); -const useSelectLanguage = (courseId) => { +const useSelectLanguage = ({ courseId, language }) => { const selectedLanguageItem = getLocalStorage(selectedLanguageKey) || {}; const [selectedLanguage, updateSelectedLanguage] = useKeyedState( stateKeys.selectedLanguage, - selectedLanguageItem[courseId] || 'en', + selectedLanguageItem[courseId] || language, ); const setSelectedLanguage = useCallback((newSelectedLanguage) => { diff --git a/src/courseware/course/sequence/Unit/translation-selection/useSelectLanguage.test.js b/src/courseware/course/sequence/Unit/translation-selection/useSelectLanguage.test.js index a461ae709f..62e8e3ea8f 100644 --- a/src/courseware/course/sequence/Unit/translation-selection/useSelectLanguage.test.js +++ b/src/courseware/course/sequence/Unit/translation-selection/useSelectLanguage.test.js @@ -29,7 +29,10 @@ jest.mock('../../../../../data/localStorage', () => ({ })); describe('useSelectLanguage', () => { - const courseId = 'some-course-id'; + const props = { + courseId: 'test-course-id', + language: 'en', + }; beforeEach(() => { jest.clearAllMocks(); @@ -41,8 +44,8 @@ describe('useSelectLanguage', () => { languages.forEach(([key, value]) => { it(`initializes selectedLanguage to the selected language (${value})`, () => { - getLocalStorage.mockReturnValueOnce({ [courseId]: key }); - const { selectedLanguage } = useSelectLanguage(courseId); + getLocalStorage.mockReturnValueOnce({ [props.courseId]: key }); + const { selectedLanguage } = useSelectLanguage(props); state.expectInitializedWith(stateKeys.selectedLanguage, key); expect(selectedLanguage).toBe(key); @@ -50,12 +53,12 @@ describe('useSelectLanguage', () => { }); test('setSelectedLanguage behavior', () => { - const { setSelectedLanguage } = useSelectLanguage(courseId); + const { setSelectedLanguage } = useSelectLanguage(props); setSelectedLanguage('es'); state.expectSetStateCalledWith(stateKeys.selectedLanguage, 'es'); expect(setLocalStorage).toHaveBeenCalledWith(selectedLanguageKey, { - [courseId]: 'es', + [props.courseId]: 'es', }); }); }); diff --git a/src/courseware/course/sequence/Unit/urls.js b/src/courseware/course/sequence/Unit/urls.js index abed160641..0d76edcde4 100644 --- a/src/courseware/course/sequence/Unit/urls.js +++ b/src/courseware/course/sequence/Unit/urls.js @@ -12,7 +12,8 @@ export const getIFrameUrl = ({ view, format, examAccess, - translateLanguage, + srcLanguage, + destLanguage, }) => { const xblockUrl = `${getConfig().LMS_BASE_URL}/xblock/${id}`; const params = stringify({ @@ -20,7 +21,10 @@ export const getIFrameUrl = ({ view, ...(format && { format }), ...(!examAccess.blockAccess && { exam_access: examAccess.accessToken }), - ...(translateLanguage && { translate_lang: translateLanguage }), + ...(srcLanguage && destLanguage && srcLanguage !== destLanguage && { + src_lang: srcLanguage, + dest_lang: destLanguage, + }), }); return `${xblockUrl}?${params}`; }; diff --git a/src/courseware/course/sequence/Unit/urls.test.js b/src/courseware/course/sequence/Unit/urls.test.js index 409b5d65a1..2b0e46d8f7 100644 --- a/src/courseware/course/sequence/Unit/urls.test.js +++ b/src/courseware/course/sequence/Unit/urls.test.js @@ -17,28 +17,45 @@ const props = { view: 'test-view', format: 'test-format', examAccess: { blockAccess: false, accessToken: 'test-access-token' }, - translateLanguage: 'test-translate-language', }; -describe('urls module', () => { - describe('getIFrameUrl', () => { - test('format provided, exam access and token available', () => { - const params = stringify({ - ...iframeParams, - view: props.view, - format: props.format, - exam_access: props.examAccess.accessToken, - translate_lang: props.translateLanguage, - }); - expect(getIFrameUrl(props)).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`); +describe('urls module getIFrameUrl', () => { + test('format provided, exam access and token available', () => { + const params = stringify({ + ...iframeParams, + view: props.view, + format: props.format, + exam_access: props.examAccess.accessToken, }); - test('no format provided, exam access blocked', () => { - const params = stringify({ ...iframeParams, view: props.view }); - expect(getIFrameUrl({ - id: props.id, - view: props.view, - examAccess: { blockAccess: true }, - })).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`); + expect(getIFrameUrl(props)).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`); + }); + test('no format provided, exam access blocked', () => { + const params = stringify({ ...iframeParams, view: props.view }); + expect(getIFrameUrl({ + id: props.id, + view: props.view, + examAccess: { blockAccess: true }, + })).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`); + }); + test('src and dest languages provided', () => { + const params = stringify({ + ...iframeParams, + view: props.view, + src_lang: 'test-src-lang', + dest_lang: 'test-dest-lang', }); + expect(getIFrameUrl({ + ...props, + srcLanguage: 'test-src-lang', + destLanguage: 'test-dest-lang', + })).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`); + }); + test('src and dest languages provided are the same', () => { + const params = stringify({ ...iframeParams, view: props.view }); + expect(getIFrameUrl({ + ...props, + srcLanguage: 'test-lang', + destLanguage: 'test-lang', + })).toEqual(`${config.LMS_BASE_URL}/xblock/${props.id}?${params}`); }); }); diff --git a/src/courseware/data/api.js b/src/courseware/data/api.js index 7643796695..f3a82a5e2b 100644 --- a/src/courseware/data/api.js +++ b/src/courseware/data/api.js @@ -110,8 +110,6 @@ function normalizeMetadata(metadata) { showCalculator: data.show_calculator, notes: camelCaseObject(data.notes), marketingUrl: data.marketing_url, - language: data.language, - wholeCourseTranslationEnabled: data.whole_course_translation_enabled, celebrations: camelCaseObject(data.celebrations), userHasPassingGrade: data.user_has_passing_grade, courseExitPageIsActive: data.course_exit_page_is_active,