Compare commits

...

23 Commits

Author SHA1 Message Date
ihor-romaniuk
eac884bd59 fix: add support for legacy theme static for the LmsHtmlFragment 2022-01-18 18:19:33 +00:00
Carla Duarte
2be382d01f fix: RTL bug on progress tab (#804) 2022-01-18 18:19:06 +00:00
ihor-romaniuk
c828a43d0e feat: add rtl support for chart on progress tab 2022-01-18 18:19:06 +00:00
Michael Terry
1eca5522cd fix: don't log errors when we ask for sequence metadata for units (#790) 2022-01-14 09:34:09 +00:00
Peter Pinch
a21abde463 squash! update package-lock.json 2021-12-20 17:55:30 +01:00
Peter Pinch
37d9646629 chore: update frontend-component-header 2.4.3 2021-12-20 17:55:30 +01:00
Peter Pinch
ad72980ad7 chore: update frontend-build to 9.0.5 2021-12-20 17:55:30 +01:00
Peter Pinch
71bcb6ba62 squash! remove duplicate code from cherry pick 2021-12-20 17:55:30 +01:00
Asad Iqbal
da867d0ef6 feat: Removed course header stuff (#715) 2021-12-20 17:55:30 +01:00
Asad Iqbal
131096b4a5 chore: update paragon 2021-12-20 17:55:30 +01:00
Régis Behmo
76e83cc737 fix: Use Link from router to fix path based routing issue (#780)
Co-authored-by: Arslan <arslan.ashraf@arbisoft.com>
2021-12-20 15:40:25 +00:00
renovate[bot]
83fa3f78bc fix(deps): update dependency @edx/paragon to v16.14.9 (#683)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-15 09:08:25 -04:00
renovate[bot]
1e4f3ec151 chore(deps): update dependency @testing-library/user-event to v13.4.1 (#684)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-15 09:08:08 -04:00
renovate[bot]
1ac806b7dd fix(deps): update dependency js-cookie to v3 (#596)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Michael Terry <mterry@edx.org>
2021-10-14 15:57:03 -04:00
renovate[bot]
1d08618be9 fix(deps): update dependency core-js to v3.18.3 (#627)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-14 15:40:12 -04:00
renovate[bot]
b90a54759c chore(deps): update dependency jest to v27.2.5 (#619)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-14 15:29:41 -04:00
renovate[bot]
a1ef37ca0b fix(deps): update dependency react-router-dom to v5.3.0 (#629)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-14 15:27:39 -04:00
renovate[bot]
d178913e4b chore(deps): update dependency glob to v7.2.0 (#649)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-14 15:26:30 -04:00
renovate[bot]
9f2ce9d152 chore(deps): update dependency @testing-library/user-event to v13.3.0 (#679)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-14 15:25:22 -04:00
renovate[bot]
d6722ca271 fix(deps): update dependency @edx/paragon to v16.14.7 (#644)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-14 15:21:27 -04:00
renovate[bot]
aa2004434e fix(deps): update dependency @edx/frontend-enterprise-utils to v1.1.0 (#682)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-14 15:18:56 -04:00
Renovate Bot
921f3eef06 fix(deps): update dependency @pact-foundation/pact to v9.16.4 2021-10-11 00:04:09 +00:00
edX Transifex Bot
8337fc79be chore(i18n): update translations 2021-10-11 02:10:22 +05:00
29 changed files with 4290 additions and 4713 deletions

1
.env
View File

@@ -20,6 +20,7 @@ LOGO_URL=''
LOGO_TRADEMARK_URL=''
LOGO_WHITE_URL=''
FAVICON_URL=''
LEGACY_THEME_NAME=''
MARKETING_SITE_BASE_URL=''
ORDER_HISTORY_URL=''
REFRESH_ACCESS_TOKEN_ENDPOINT=''

View File

@@ -19,6 +19,7 @@ LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
LEGACY_THEME_NAME=''
MARKETING_SITE_BASE_URL='http://localhost:18000'
ORDER_HISTORY_URL='http://localhost:1996/orders'
PORT=2000

View File

@@ -19,6 +19,7 @@ LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
LEGACY_THEME_NAME=''
MARKETING_SITE_BASE_URL='http://localhost:18000'
ORDER_HISTORY_URL='http://localhost:1996/orders'
PORT=2000

7783
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -33,20 +33,21 @@
"dependencies": {
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
"@edx/frontend-component-footer": "10.1.6",
"@edx/frontend-enterprise-utils": "1.0.0",
"@edx/frontend-lib-special-exams": "1.13.3",
"@edx/frontend-platform": "1.12.7",
"@edx/paragon": "16.13.3",
"@edx/frontend-enterprise-utils": "1.1.1",
"@edx/frontend-lib-special-exams": "1.14.1",
"@edx/frontend-platform": "1.14.3",
"@edx/paragon": "16.19.0",
"@edx/frontend-component-header": "^2.4.3",
"@fortawesome/fontawesome-svg-core": "1.2.36",
"@fortawesome/free-brands-svg-icons": "5.15.4",
"@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/react-fontawesome": "0.1.15",
"@pact-foundation/pact": "9.16.3",
"@pact-foundation/pact": "9.16.4",
"@reduxjs/toolkit": "1.6.2",
"classnames": "2.3.1",
"core-js": "3.16.4",
"js-cookie": "2.2.1",
"core-js": "3.18.3",
"js-cookie": "3.0.1",
"lodash.camelcase": "4.3.0",
"prop-types": "15.7.2",
"react": "17.0.2",
@@ -55,7 +56,7 @@
"react-helmet": "6.1.0",
"react-redux": "7.2.5",
"react-router": "5.2.1",
"react-router-dom": "5.2.1",
"react-router-dom": "5.3.0",
"react-share": "4.4.0",
"redux": "4.1.1",
"regenerator-runtime": "0.13.9",
@@ -64,17 +65,17 @@
"util": "0.12.4"
},
"devDependencies": {
"@edx/frontend-build": "8.0.4",
"@edx/frontend-build": "9.0.5",
"@testing-library/dom": "7.16.3",
"@testing-library/jest-dom": "5.14.1",
"@testing-library/react": "10.3.0",
"@testing-library/user-event": "13.2.1",
"@testing-library/user-event": "13.4.1",
"axios-mock-adapter": "1.20.0",
"codecov": "3.8.3",
"es-check": "6.0.0",
"glob": "7.1.7",
"glob": "7.2.0",
"husky": "7.0.2",
"jest": "27.0.6",
"jest": "27.2.5",
"jest-chain": "1.1.5",
"reactifex": "1.1.1",
"rosie": "2.1.0"

View File

@@ -1,34 +0,0 @@
import React from 'react';
import { getConfig } from '@edx/frontend-platform';
import { getLoginRedirectUrl } from '@edx/frontend-platform/auth';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button } from '@edx/paragon';
import genericMessages from '../generic/messages';
function AnonymousUserMenu({ intl }) {
return (
<div>
<Button
className="mr-3"
variant="outline-primary"
href={`${getConfig().LMS_BASE_URL}/register?next=${encodeURIComponent(global.location.href)}`}
>
{intl.formatMessage(genericMessages.registerSentenceCase)}
</Button>
<Button
variant="primary"
href={`${getLoginRedirectUrl(global.location.href)}`}
>
{intl.formatMessage(genericMessages.signInSentenceCase)}
</Button>
</div>
);
}
AnonymousUserMenu.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(AnonymousUserMenu);

View File

@@ -1,76 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Dropdown } from '@edx/paragon';
import messages from './messages';
function AuthenticatedUserDropdown({ enterpriseLearnerPortalLink, intl, username }) {
let dashboardMenuItem = (
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/dashboard`}>
{intl.formatMessage(messages.dashboard)}
</Dropdown.Item>
);
if (enterpriseLearnerPortalLink && Object.keys(enterpriseLearnerPortalLink).length > 0) {
dashboardMenuItem = (
<Dropdown.Item href={enterpriseLearnerPortalLink.href}>
{enterpriseLearnerPortalLink.content}
</Dropdown.Item>
);
}
return (
<>
<a className="text-gray-700 mr-3" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
<Dropdown className="user-dropdown">
<Dropdown.Toggle variant="outline-primary">
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
<span data-hj-suppress className="d-none d-md-inline">
{username}
</span>
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right">
{dashboardMenuItem}
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/u/${username}`}>
{intl.formatMessage(messages.profile)}
</Dropdown.Item>
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/account/settings`}>
{intl.formatMessage(messages.account)}
</Dropdown.Item>
{!enterpriseLearnerPortalLink && (
// Users should only see Order History if they do not have an available
// learner portal, because an available learner portal currently means
// that they access content via Subscriptions, in which context an "order"
// is not relevant.
<Dropdown.Item href={getConfig().ORDER_HISTORY_URL}>
{intl.formatMessage(messages.orderHistory)}
</Dropdown.Item>
)}
<Dropdown.Item href={getConfig().LOGOUT_URL}>
{intl.formatMessage(messages.signOut)}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</>
);
}
AuthenticatedUserDropdown.propTypes = {
intl: intlShape.isRequired,
username: PropTypes.string.isRequired,
enterpriseLearnerPortalLink: PropTypes.shape({
type: PropTypes.string,
href: PropTypes.string,
content: PropTypes.string,
}),
};
AuthenticatedUserDropdown.defaultProps = {
enterpriseLearnerPortalLink: undefined,
};
export default injectIntl(AuthenticatedUserDropdown);

View File

@@ -1,99 +0,0 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { useEnterpriseConfig } from '@edx/frontend-enterprise-utils';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
import AnonymousUserMenu from './AnonymousUserMenu';
import AuthenticatedUserDropdown from './AuthenticatedUserDropdown';
import messages from './messages';
function LinkedLogo({
href,
src,
alt,
...attributes
}) {
return (
<a href={href} {...attributes}>
<img className="d-block" src={src} alt={alt} />
</a>
);
}
LinkedLogo.propTypes = {
href: PropTypes.string.isRequired,
src: PropTypes.string.isRequired,
alt: PropTypes.string.isRequired,
};
function Header({
courseOrg, courseNumber, courseTitle, intl, showUserDropdown,
}) {
const { authenticatedUser } = useContext(AppContext);
const { enterpriseLearnerPortalLink, enterpriseCustomerBrandingConfig } = useEnterpriseConfig(
authenticatedUser,
getConfig().ENTERPRISE_LEARNER_PORTAL_HOSTNAME,
getConfig().LMS_BASE_URL,
);
let headerLogo = (
<LinkedLogo
className="logo"
href={`${getConfig().LMS_BASE_URL}/dashboard`}
src={getConfig().LOGO_URL}
alt={getConfig().SITE_NAME}
/>
);
if (enterpriseCustomerBrandingConfig && Object.keys(enterpriseCustomerBrandingConfig).length > 0) {
headerLogo = (
<LinkedLogo
className="logo"
href={enterpriseCustomerBrandingConfig.logoDestination}
src={enterpriseCustomerBrandingConfig.logo}
alt={enterpriseCustomerBrandingConfig.logoAltText}
/>
);
}
return (
<header className="course-header">
<a className="sr-only sr-only-focusable" href="#main-content">{intl.formatMessage(messages.skipNavLink)}</a>
<div className="container-xl py-2 d-flex align-items-center">
{headerLogo}
<div className="flex-grow-1 course-title-lockup" style={{ lineHeight: 1 }}>
<span className="d-block small m-0">{courseOrg} {courseNumber}</span>
<span className="d-block m-0 font-weight-bold course-title">{courseTitle}</span>
</div>
{showUserDropdown && authenticatedUser && (
<AuthenticatedUserDropdown
enterpriseLearnerPortalLink={enterpriseLearnerPortalLink}
username={authenticatedUser.username}
/>
)}
{showUserDropdown && !authenticatedUser && (
<AnonymousUserMenu />
)}
</div>
</header>
);
}
Header.propTypes = {
courseOrg: PropTypes.string,
courseNumber: PropTypes.string,
courseTitle: PropTypes.string,
intl: intlShape.isRequired,
showUserDropdown: PropTypes.bool,
};
Header.defaultProps = {
courseOrg: null,
courseNumber: null,
courseTitle: null,
showUserDropdown: true,
};
export default injectIntl(Header);

View File

@@ -1,29 +0,0 @@
import React from 'react';
import {
authenticatedUser, initializeMockApp, render, screen,
} from '../setupTest';
import { Header } from './index';
describe('Header', () => {
beforeAll(async () => {
// We need to mock AuthService to implicitly use `getAuthenticatedUser` within `AppContext.Provider`.
await initializeMockApp();
});
it('displays user button', () => {
render(<Header />);
expect(screen.getByRole('button')).toHaveTextContent(authenticatedUser.username);
});
it('displays course data', () => {
const courseData = {
courseOrg: 'course-org',
courseNumber: 'course-number',
courseTitle: 'course-title',
};
render(<Header {...courseData} />);
expect(screen.getByText(`${courseData.courseOrg} ${courseData.courseNumber}`)).toBeInTheDocument();
expect(screen.getByText(courseData.courseTitle)).toBeInTheDocument();
});
});

View File

@@ -1,46 +0,0 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
courseMaterial: {
id: 'learn.navigation.course.tabs.label',
defaultMessage: 'Course Material',
description: 'The accessible label for course tabs navigation',
},
dashboard: {
id: 'header.menu.dashboard.label',
defaultMessage: 'Dashboard',
description: 'The text for the user menu Dashboard navigation link.',
},
help: {
id: 'header.help.label',
defaultMessage: 'Help',
description: 'The text for the link to the Help Center',
},
profile: {
id: 'header.menu.profile.label',
defaultMessage: 'Profile',
description: 'The text for the user menu Profile navigation link.',
},
account: {
id: 'header.menu.account.label',
defaultMessage: 'Account',
description: 'The text for the user menu Account navigation link.',
},
orderHistory: {
id: 'header.menu.orderHistory.label',
defaultMessage: 'Order History',
description: 'The text for the user menu Order History navigation link.',
},
skipNavLink: {
id: 'header.navigation.skipNavLink',
defaultMessage: 'Skip to main content.',
description: 'A link used by screen readers to allow users to skip to the main content of the page.',
},
signOut: {
id: 'header.menu.signOut.label',
defaultMessage: 'Sign Out',
description: 'The label for the user menu Sign Out action.',
},
});
export default messages;

View File

@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Header } from '../../course-header';
import { LearningHeader as Header } from '@edx/frontend-component-header';
import PageLoading from '../../generic/PageLoading';
import { unsubscribeFromCourseGoal } from '../data/api';

View File

@@ -13,7 +13,7 @@ export default function LmsHtmlFragment({
<html>
<head>
<base href="${getConfig().LMS_BASE_URL}" target="_parent">
<link rel="stylesheet" href="/static/css/bootstrap/lms-main.css">
<link rel="stylesheet" href="/static/${getConfig().LEGACY_THEME_NAME ? `${getConfig().LEGACY_THEME_NAME}/` : ''}css/bootstrap/lms-main.css">
<link rel="stylesheet" type="text/css" href="${getConfig().BASE_URL}/src/course-home/outline-tab/LmsHtmlFragment.css">
</head>
<body class="${className}">${html}</body>

View File

@@ -2,7 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
getLocale, injectIntl, intlShape, isRtl,
} from '@edx/frontend-platform/i18n';
import { OverlayTrigger, Popover } from '@edx/paragon';
import { useModel } from '../../../../generic/model-store';
@@ -23,6 +25,14 @@ function CurrentGradeTooltip({ intl, tooltipClassName }) {
const currentGrade = Number((visiblePercent * 100).toFixed(0));
let currentGradeDirection = currentGrade < 50 ? '' : '-';
const isLocaleRtl = isRtl(getLocale());
if (isLocaleRtl) {
currentGradeDirection = currentGrade < 50 ? '-' : '';
}
return (
<>
<OverlayTrigger
@@ -37,16 +47,16 @@ function CurrentGradeTooltip({ intl, tooltipClassName }) {
)}
>
<g>
<circle cx={`${Math.min(...[currentGrade, 100])}%`} cy="50%" r="8.5" fill="transparent" />
<rect className="grade-bar__divider" x={`${Math.min(...[currentGrade, 100])}%`} style={{ transform: 'translateY(2.61em)' }} />
<circle cx={`${Math.min(...[isLocaleRtl ? 100 - currentGrade : currentGrade, 100])}%`} cy="50%" r="8.5" fill="transparent" />
<rect className="grade-bar__divider" x={`${Math.min(...[isLocaleRtl ? 100 - currentGrade : currentGrade, 100])}%`} style={{ transform: 'translateY(2.61em)' }} />
</g>
</OverlayTrigger>
<text
className="x-small"
textAnchor={currentGrade < 50 ? 'start' : 'end'}
x={`${Math.min(...[currentGrade, 100])}%`}
x={`${Math.min(...[isLocaleRtl ? 100 - currentGrade : currentGrade, 100])}%`}
y="20px"
style={{ transform: `translateX(${currentGrade < 50 ? '' : '-'}3.4em)` }}
style={{ transform: `translateX(${currentGradeDirection}3.4em)` }}
>
{intl.formatMessage(messages.currentGradeLabel)}
</text>

View File

@@ -1,12 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
getLocale, injectIntl, intlShape, isRtl,
} from '@edx/frontend-platform/i18n';
import { OverlayTrigger, Popover } from '@edx/paragon';
import messages from '../messages';
function PassingGradeTooltip({ intl, passingGrade, tooltipClassName }) {
const isLocaleRtl = isRtl(getLocale());
let passingGradeDirection = passingGrade < 50 ? '' : '-';
if (isLocaleRtl) {
passingGradeDirection = passingGrade < 50 ? '-' : '';
}
return (
<>
<OverlayTrigger
@@ -21,17 +31,17 @@ function PassingGradeTooltip({ intl, passingGrade, tooltipClassName }) {
)}
>
<g>
<circle cx={`${passingGrade}%`} cy="50%" r="8.5" fill="transparent" />
<circle className="grade-bar--passing" cx={`${passingGrade}%`} cy="50%" r="4.5" />
<circle cx={`${isLocaleRtl ? 100 - passingGrade : passingGrade}%`} cy="50%" r="8.5" fill="transparent" />
<circle className="grade-bar--passing" cx={`${isLocaleRtl ? 100 - passingGrade : passingGrade}%`} cy="50%" r="4.5" />
</g>
</OverlayTrigger>
<text
className="x-small"
textAnchor={passingGrade < 50 ? 'start' : 'end'}
x={`${passingGrade}%`}
x={`${isLocaleRtl ? 100 - passingGrade : passingGrade}%`}
y="90px"
style={{ transform: `translateX(${passingGrade < 50 ? '' : '-'}3.4em)` }}
style={{ transform: `translateX(${passingGradeDirection}3.4em)` }}
>
{intl.formatMessage(messages.passingGradeLabel)}
</text>

View File

@@ -1,2 +1,2 @@
export { default as Header } from './Header';
/* eslint-disable import/prefer-default-export */
export { default as CourseTabsNavigation } from './CourseTabsNavigation';

View File

@@ -0,0 +1,11 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
courseMaterial: {
id: 'learn.navigation.course.tabs.label',
defaultMessage: 'Course Material',
description: 'The accessible label for course tabs navigation',
},
});
export default messages;

View File

@@ -159,7 +159,7 @@ describe('CoursewareContainer', () => {
const courseId = defaultCourseId;
function assertLoadedHeader(container) {
const courseHeader = container.querySelector('.course-header');
const courseHeader = container.querySelector('.learning-header');
// Ensure the course number and org appear - this proves we loaded course metadata properly.
expect(courseHeader).toHaveTextContent(courseMetadata.number);
expect(courseHeader).toHaveTextContent(courseMetadata.org);

View File

@@ -6,6 +6,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faHome } from '@fortawesome/free-solid-svg-icons';
import { useSelector } from 'react-redux';
import { SelectMenu } from '@edx/paragon';
import { Link } from 'react-router-dom';
import { useModel, useModels } from '../../generic/model-store';
/** [MM-P2P] Experiment */
import { MMP2PFlyoverTrigger } from '../../experiments/mm-p2p';
@@ -30,9 +31,12 @@ function CourseBreadcrumb({
>
{ getConfig().ENABLE_JUMPNAV !== 'true' || content.length < 2 || !isStaff
? (
<a className="text-primary-500" href={`/course/${courseId}/${defaultContent.id}`}>
<Link
className="text-primary-500"
to={`/course/${courseId}/${defaultContent.id}`}
>
{defaultContent.label}
</a>
</Link>
)
: (
<SelectMenu isLink defaultMessage={defaultContent.label}>
@@ -122,9 +126,9 @@ export default function CourseBreadcrumbs({
<nav aria-label="breadcrumb" className="my-4 d-inline-block col-sm-10">
<ol className="list-unstyled d-flex flex-nowrap align-items-center m-0">
<li className="list-unstyled d-flex m-0">
<a
href={`/course/${courseId}/home`}
<Link
className="flex-shrink-0 text-primary"
to={`/course/${courseId}/home`}
>
<FontAwesomeIcon icon={faHome} className="mr-2" />
<FormattedMessage
@@ -132,7 +136,7 @@ export default function CourseBreadcrumbs({
description="The course home link in breadcrumbs nav"
defaultMessage="Course"
/>
</a>
</Link>
</li>
{links.map(content => (
<CourseBreadcrumb

View File

@@ -2,6 +2,7 @@ import React from 'react';
import { screen, render } from '@testing-library/react';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { getConfig } from '@edx/frontend-platform';
import { BrowserRouter } from 'react-router-dom';
import { useModel, useModels } from '../../generic/model-store';
import CourseBreadcrumbs from './CourseBreadcrumbs';
@@ -105,12 +106,14 @@ describe('CourseBreadcrumbs', () => {
],
]);
render(
<CourseBreadcrumbs
courseId="course-v1:edX+DemoX+Demo_Course"
sectionId="block-v1:edX+DemoX+Demo_Course+type@chapter+block@interactive_demonstrations"
sequenceId="block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions"
isStaff
/>,
<BrowserRouter>
<CourseBreadcrumbs
courseId="course-v1:edX+DemoX+Demo_Course"
sectionId="block-v1:edX+DemoX+Demo_Course+type@chapter+block@interactive_demonstrations"
sequenceId="block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions"
isStaff
/>
</BrowserRouter>,
);
it('renders course breadcrumbs as expected', async () => {
expect(screen.queryAllByRole('link')).toHaveLength(1);

View File

@@ -237,7 +237,13 @@ export function fetchSequence(sequenceId) {
dispatch(fetchSequenceSuccess({ sequenceId }));
}
} catch (error) {
logError(error);
// Some errors are expected - for example, CoursewareContainer may request sequence metadata for a unit and rely
// on the request failing to notice that it actually does have a unit (mostly so it doesn't have to know anything
// about the opaque key structure). In such cases, the backend gives us a 422.
const isExpected = error.response && error.response.status === 422;
if (!isExpected) {
logError(error);
}
dispatch(fetchSequenceFailure({ sequenceId }));
}
};

View File

@@ -11,16 +11,6 @@ const messages = defineMessages({
defaultMessage: 'register',
description: 'Text in a link, prompting the user to create an account. Used in "learning.logistration.alert"',
},
registerSentenceCase: {
id: 'general.register.sentenceCase',
defaultMessage: 'Register',
description: 'Text in a button, prompting the user to register.',
},
signInLowercase: {
id: 'learning.logistration.login', // ID left for historical purposes
defaultMessage: 'sign in',
description: 'Text in a link, prompting the user to log in. Used in "learning.logistration.alert"',
},
signInSentenceCase: {
id: 'general.signIn.sentenceCase',
defaultMessage: 'Sign in',

View File

@@ -1,399 +1,399 @@
{
"learning.accessExpiration.deadline": "Upgrade by {date} to get unlimited access to the course as long as it exists on the site.",
"learning.accessExpiration.header": "Audit Access Expires {date}",
"learning.accessExpiration.body": "You lose all access to this course, including your progress, on {date}.",
"instructorToolbar.pageBanner.courseHasExpired": "This learner no longer has access to this course. Their access expired on {date}.",
"learning.accessExpiration.upgradeNow": "Upgrade now",
"learning.outline.alert.start.short": "Course starts {timeRemaining} at {courseStartTime}.",
"learning.outline.alert.end.long": "This course is ending {timeRemaining} on {courseEndDate}.",
"learning.outline.alert.end.calendar": "Dont forget to add a calendar reminder!",
"instructorToolbar.pageBanner.courseHasNotStarted": "This learner does not yet have access to this course. The course starts on {date}.",
"learning.enrollment.alert": "You must be enrolled in the course to see course content.",
"learning.staff.enrollment.alert": "You are viewing this course as staff, and are not enrolled.",
"learning.enrollment.enrollNow.Inline": "Enroll now",
"learning.enrollment.enrollNow.Sentence": "Enroll now.",
"learning.enrollment.success": "You've successfully enrolled in this course!",
"account-activation.alert.title": "Activate your account so you can log back in",
"account-activation.alert.button": "Continue to {siteName}",
"account-activation.alert.message": "We sent an email to {boldEmail} with a link to activate your account. Cant find it? Check your spam folder or\n {sendEmailTag}.",
"account-activation.resend.link": "resend the email",
"learning.logistration.alert": "To see course content, {signIn} or {register}.",
"learn.navigation.course.tabs.label": "Course Material",
"header.menu.dashboard.label": "Dashboard",
"header.help.label": "Help",
"header.menu.profile.label": "Profile",
"header.menu.account.label": "Account",
"header.menu.orderHistory.label": "Order History",
"header.navigation.skipNavLink": "Skip to main content.",
"header.menu.signOut.label": "Sign Out",
"learning.dates.badge.completed": "Completed",
"learning.dates.badge.dueNext": "Due next",
"learning.dates.badge.pastDue": "Past due",
"learning.dates.title": "Important dates",
"learning.dates.badge.today": "Today",
"learning.dates.badge.unreleased": "Not yet released",
"learning.dates.badge.verifiedOnly": "Verified only",
"learning.goals.unsubscribe.contact": "contact support",
"learning.goals.unsubscribe.description": "You will no longer receive email reminders about your goal for {courseTitle}.",
"learning.goals.unsubscribe.errorHeader": "Something went wrong",
"learning.goals.unsubscribe.goToDashboard": "Go to dashboard",
"learning.goals.unsubscribe.header": "Youve unsubscribed from goal reminders",
"learning.goals.unsubscribe.loading": "Unsubscribing…",
"learning.goals.unsubscribe.errorDescription": "We were unable to unsubscribe you from goal reminder emails. Please try again later or {contactSupport} for help.",
"learning.outline.alert.cert.when": "This course ends on {courseEndDateFormatted}. Final grades and certificates are\n scheduled to be available after {certificateAvailableDate}.",
"cert.alert.earned.unavailable.header": "Your grade and certificate will be ready soon!",
"cert.alert.earned.ready.header": "Congratulations! Your certificate is ready.",
"cert.alert.notPassing.header": "You are not eligible for a certificate",
"cert.alert.notPassing.button": "View grades",
"learning.outline.alert.end.short": "This course is ending {timeRemaining} at {courseEndTime}.",
"alert.enroll": " to access the full course.",
"learning.privateCourse.signInOrRegister": "{signIn} or {register} and then enroll in this course.",
"learning.outline.alert.scheduled-content.heading": "More content is coming soon!",
"learning.outline.alert.scheduled-content.body": "This course will have more content released at a future date. Look out for email updates or check back on this course for updates.",
"learning.outline.alert.scheduled-content.button": "View Course Schedule",
"learning.outline.dates.all": "View all course dates",
"learning.outline.collapseAll": "Collapse all",
"learning.outline.completedAssignment": "Completed",
"learning.outline.completedSection": "Completed section",
"learning.outline.dates": "Important dates",
"learning.outline.editGoal": "Edit goal",
"learning.outline.expandAll": "Expand all",
"learning.outline.goal": "Goal",
"learning.outline.goalUnsure": "Not sure yet",
"learning.outline.handouts": "Course Handouts",
"learning.outline.incompleteAssignment": "Incomplete",
"learning.outline.incompleteSection": "Incomplete section",
"learning.outline.learnMore": "Learn More",
"learning.outline.altText.openSection": "Open",
"learning.outline.resume": "Resume course",
"learning.outline.setGoal": "To start, set a course goal by selecting the option below that best describes your learning plan.",
"learning.outline.start": "Start Course",
"learning.outline.tools": "Course Tools",
"learning.outline.upgradeButton": "Upgrade ({symbol}{price})",
"learning.outline.upgradeTitle": "Pursue a verified certificate",
"learning.outline.certificateAlt": "Example Certificate",
"learning.outline.welcomeMessage": "Welcome Message",
"learning.outline.welcomeMessageShowMoreButton": "Show More",
"learning.outline.welcomeMessageShowLessButton": "Show Less",
"learning.outline.goalWelcome": "Welcome to",
"learning.proctoringPanel.header": "This course contains proctored exams",
"learning.proctoringPanel.status.notStarted": "Not Started",
"learning.proctoringPanel.status.started": "Started",
"learning.proctoringPanel.status.submitted": "Submitted",
"learning.proctoringPanel.status.verified": "Verified",
"learning.proctoringPanel.status.rejected": "Rejected",
"learning.proctoringPanel.status.error": "Error",
"learning.proctoringPanel.status.otherCourseApproved": "Approved in Another Course",
"learning.proctoringPanel.status.expiringSoon": "Expiring Soon",
"learning.proctoringPanel.status": "Current Onboarding Status:",
"learning.proctoringPanel.message.notStarted": "You have not started your onboarding exam.",
"learning.proctoringPanel.message.started": "You have started your onboarding exam.",
"learning.proctoringPanel.message.submitted": "You have submitted your onboarding exam.",
"learning.proctoringPanel.message.verified": "Your onboarding exam has been approved in this course.",
"learning.proctoringPanel.message.rejected": "Your onboarding exam has been rejected. Please retry onboarding.",
"learning.proctoringPanel.message.error": "An error has occurred during your onboarding exam. Please retry onboarding.",
"learning.proctoringPanel.message.otherCourseApproved": "Your onboarding exam has been approved in another course.",
"learning.proctoringPanel.detail.otherCourseApproved": "If your device has changed, we recommend that you complete this course's onboarding exam in order to ensure that your setup still meets the requirements for proctoring.",
"learning.proctoringPanel.message.expiringSoon": "Your onboarding profile has been approved in another course. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.",
"learning.proctoringPanel.generalInfo": "You must complete the onboarding process prior to taking any proctored exam. ",
"learning.proctoringPanel.generalInfoSubmitted": "Your submitted profile is in review.",
"learning.proctoringPanel.generalTime": "Onboarding profile review can take 2+ business days.",
"learning.proctoringPanel.onboardingButton": "Complete Onboarding",
"learning.proctoringPanel.onboardingPracticeButton": "View Onboarding Exam",
"learning.proctoringPanel.onboardingButtonNotOpen": "Onboarding Opens: {releaseDate}",
"learning.proctoringPanel.reviewRequirementsButton": "Review instructions and system requirements",
"learning.proctoringPanel.onboardingButtonPastDue": "Onboarding Past Due",
"learning.outline.sequence-due": "{description} due {assignmentDue}",
"progress.certificateStatus.unverifiedBody": "In order to generate a certificate, you must complete ID verification. {idVerificationSupportLink}.",
"progress.certificateStatus.downloadableBody": "Showcase your accomplishment on LinkedIn or your resumé today. You can download your certificate now and access it any time from your Dashboard and Profile.",
"courseCelebration.certificateBody.notAvailable.endDate": "This course ended on {endDate} and final grades and certificates are scheduled to be\n available after {certAvailableDate}.",
"progress.certificateStatus.notPassingHeader": "Certificate status",
"progress.certificateStatus.notPassingBody": "In order to qualify for a certificate, you must have a passing grade.",
"progress.certificateStatus.inProgressHeader": "More content is coming soon!",
"progress.certificateStatus.inProgressBody": "It looks like there is more content in this course that will be released in the future. Look out for email updates or check back on your course for when this content will be available.",
"progress.certificateStatus.requestableHeader": "Certificate status",
"progress.certificateStatus.requestableBody": "Congratulations, you qualified for a certificate! In order to access your certificate, request it below.",
"progress.certificateStatus.requestableButton": "Request certificate",
"progress.certificateStatus.unverifiedHeader": "Certificate status",
"progress.certificateStatus.unverifiedButton": "Verify ID",
"progress.certificateStatus.courseCelebration.verificationPending": "Your ID verification is pending and your certificate will be available once approved.",
"progress.certificateStatus.downloadableHeader": "Your certificate is available!",
"progress.certificateStatus.downloadableButton": "Download my certificate",
"progress.certificateStatus.viewableButton": "View my certificate",
"progress.certificateStatus.notAvailableHeader": "Certificate status",
"progress.certificateStatus.upgradeHeader": "Earn a certificate",
"progress.certificateStatus.upgradeBody": "You are in an audit track and do not qualify for a certificate. In order to work towards a certificate, upgrade your course today.",
"progress.certificateStatus.upgradeButton": "Upgrade now",
"progress.certificateStatus.unverifiedHomeHeader": "Verify your identity to earn a certificate!",
"progress.certificateStatus.unverifiedHomeButton": "Verify my ID",
"progress.certificateStatus.unverifiedHomeBody": "In order to generate a certificate for this course, you must complete the ID verification process.",
"progress.completion.donut.label": "completed",
"progress.completion.body": "This represents how much of the course content you have completed. Note that some content may not yet be released.",
"progress.completion.tooltip.locked": "Content that you have completed.",
"progress.completion.header": "Course completion",
"progress.completion.tooltip": "Content that you have access to and have not completed.",
"progress.completion.tooltip.complete": "Content that is locked and available only to those who upgrade.",
"progress.completion.donut.percentComplete": "You have completed {percent}% of content in this course.",
"progress.completion.donut.percentIncomplete": "You have not completed {percent}% of content in this course that you have access to.",
"progress.completion.donut.percentLocked": "{percent}% of content in this course is locked and available only for those who upgrade.",
"progress.ungradedAlert": "For progress on ungraded aspects of the course, view your {outlineLink}.",
"progress.footnotes.droppableAssignments": "The lowest {numDroppable, plural, one{# {assignmentType} score is} other{# {assignmentType} scores are}} dropped.",
"progress.assignmentType": "Assignment type",
"progress.footnotes.backToContent": "Back to content",
"progress.courseGrade.body": "This represents your weighted grade against the grade needed to pass this course.",
"progress.courseGrade.gradeBar.altText": "Your current grade is {currentGrade}%. A weighted grade of {passingGrade}% is required to pass in this course.",
"progress.courseGrade.footer.generic.passing": "Youre currently passing this course",
"progress.courseGrade.footer.nonPassing": "A weighted grade of {passingGrade}% is required to pass in this course",
"progress.courseGrade.footer.passing": "Youre currently passing this course with a grade of {letterGrade} ({minGrade}-{maxGrade}%)",
"progress.courseGrade.preview.headerLocked": "locked feature",
"progress.courseGrade.preview.headerLimited": "limited feature",
"progress.courseGrade.preview.header.ariaHidden": "Preview of a ",
"progress.courseGrade.preview.body.unlockCertificate": "Unlock to view grades and work towards a certificate.",
"progress.courseGrade.partialpreview.body.unlockCertificate": "Unlock to work towards a certificate.",
"progress.courseGrade.preview.body.upgradeDeadlinePassed": "The deadline to upgrade in this course has passed.",
"progress.courseGrade.preview.button.upgrade": "Upgrade now",
"progress.courseGrade.gradeRange.tooltip": "Grade ranges for this course:",
"progress.courseOutline": "Course Outline",
"progress.courseGrade.label.currentGrade": "Your current grade",
"progress.detailedGrades": "Detailed grades",
"progress.detailedGrades.emptyTable": "You currently have no graded problem scores.",
"progress.footnotes.title": "Grade summary footnotes",
"progress.gradeSummary.grade": "Grade",
"progress.courseGrade.grades": "Grades",
"progress.courseGrade.gradeRange.Tooltip": "Grade range tooltip",
"progress.gradeSummary": "Grade summary",
"progress.gradeSummary.limitedAccessExplanation": "You have limited access to graded assignments as part of the audit track in this course.",
"progress.gradeSummary.tooltip.alt": "Grade summary tooltip",
"progress.gradeSummary.tooltip.body": "Your course assignment's weight is determined by your instructor. By multiplying your grade by the weight for that assignment type, your weighted grade is calculated. Your weighted grade is what's used to determine if you pass the course.",
"progress.courseGrade.label.passingGrade": "Passing grade",
"progress.detailedGrades.problemScore.label": "Problem Scores:",
"progress.detailedGrades.problemScore.toggleButton": "Toggle individual problem scores for {subsectionTitle}",
"progress.score": "Score",
"progress.weight": "Weight",
"progress.weightedGrade": "Weighted grade",
"progress.weightedGradeSummary": "Your current weighted grade summary",
"progress.noAcessToAssignmentType": "You do not have access to assignments of type {assignmentType}",
"progress.noAcessToSubsection": "You do not have access to subsection {displayName}",
"progress.header": "Your progress",
"progress.header.targetUser": "Course progress for {username}",
"progress.link.studio": "View grading in Studio",
"progress.relatedLinks.datesCard.description": "A schedule view of your course due dates and upcoming assignments.",
"learning.accessExpiration.deadline": "Mettez à niveau avant le {date} pour obtenir un accès illimité au cours tant qu'il existe sur le site.",
"learning.accessExpiration.header": "Laccès à laudit expire le {date}",
"learning.accessExpiration.body": "Vous perdez tout accès à ce cours, y compris votre progression, le {date}.",
"instructorToolbar.pageBanner.courseHasExpired": "Cet apprenant n'a plus accès à ce cours. Leur accès a expiré le {date}.",
"learning.accessExpiration.upgradeNow": "Mettre à jour dès maintenant",
"learning.outline.alert.start.short": "Le cours commence dans {timeRemaining} à {courseStartTime}.",
"learning.outline.alert.end.long": "Ce cours se termine dans {timeRemaining}, le {courseEndDate}.",
"learning.outline.alert.end.calendar": "N'oubliez pas d'ajouter un rappel dans le calendrier!",
"instructorToolbar.pageBanner.courseHasNotStarted": "Cet apprenant n'a pas encore accès à ce cours. Le cours commencera le {date}.",
"learning.enrollment.alert": "Vous devez être inscrit à ce cours pour voir le contenu du cours.",
"learning.staff.enrollment.alert": "Vous regardez ce cours en tant que membre de l'équipe, vous n'êtes pas inscrits.",
"learning.enrollment.enrollNow.Inline": "Inscrivez-vous maintenant.",
"learning.enrollment.enrollNow.Sentence": "Inscrivez-vous maintenant.",
"learning.enrollment.success": "Vous vous êtes inscrits avec succès à ce cours!",
"account-activation.alert.title": "Activez votre compte afin de pouvoir vous reconnecter",
"account-activation.alert.button": "Continuer vers {siteName}",
"account-activation.alert.message": "Nous avons envoyé un courriel à {boldEmail} contenant un lien pour activer votre compte. Il est introuvable ? Vérifiez votre dossier de pourriel ou\n {sendEmailTag}.",
"account-activation.resend.link": "renvoyer le courriel",
"learning.logistration.alert": "Pour afficher le contenu du cours, {signIn} ou {register}.",
"learn.navigation.course.tabs.label": "Matériel de cours",
"header.menu.dashboard.label": "Tableau de bord",
"header.help.label": "Aide",
"header.menu.profile.label": "Profil",
"header.menu.account.label": "Compte",
"header.menu.orderHistory.label": "Historique des commandes",
"header.navigation.skipNavLink": "Passer au contenu principal",
"header.menu.signOut.label": "Se déconnecter",
"learning.dates.badge.completed": "Terminé",
"learning.dates.badge.dueNext": "À venir",
"learning.dates.badge.pastDue": "En retard",
"learning.dates.title": "Dates importantes",
"learning.dates.badge.today": "Aujourd'hui",
"learning.dates.badge.unreleased": "Pas encore publié",
"learning.dates.badge.verifiedOnly": "Vérifié seulement",
"learning.goals.unsubscribe.contact": "contacter le support",
"learning.goals.unsubscribe.description": "Vous ne recevrez plus de rappels par courriel au sujet de votre objectif pour {courseTitle}.",
"learning.goals.unsubscribe.errorHeader": "Quelque chose s'est mal passé",
"learning.goals.unsubscribe.goToDashboard": "Aller au tableau de bord",
"learning.goals.unsubscribe.header": "Vous vous êtes désabonné des rappels d'objectifs",
"learning.goals.unsubscribe.loading": "Désinscription...",
"learning.goals.unsubscribe.errorDescription": "Nous n'avons pas pu vous désinscrire des courriels de rappel d'objectif. Veuillez réessayer plus tard ou {contactSupport} pour obtenir de l'aide.",
"learning.outline.alert.cert.when": "Ce cours se termine le {courseEndDateFormatted}. Les notes finales et les attestations\n devraient être disponibles après {certificateAvailableDate}.",
"cert.alert.earned.unavailable.header": "Votre note et votre attestation seront bientôt prêtes !",
"cert.alert.earned.ready.header": "Félicitations ! Votre attestation est prête.",
"cert.alert.notPassing.header": "Vous n'êtes pas éligible pour une attestation",
"cert.alert.notPassing.button": "Voir les notes",
"learning.outline.alert.end.short": "Le cours termine dans {timeRemaining} à {courseEndTime}.",
"alert.enroll": " pour accéder au cours complet.",
"learning.privateCourse.signInOrRegister": "{signIn} ou {register}, puis inscrivez-vous à ce cours.",
"learning.outline.alert.scheduled-content.heading": "Plus de contenu sera bientôt disponible!",
"learning.outline.alert.scheduled-content.body": "Ce cours aura plus de contenu publié à une date future. Surveillez les mises à jour par courriel ou revenez voir ce cours pour les mises à jour.",
"learning.outline.alert.scheduled-content.button": "Voir l'horaire du cours",
"learning.outline.dates.all": "Voir toutes les dates de cours",
"learning.outline.collapseAll": "Tout replier",
"learning.outline.completedAssignment": "Terminé",
"learning.outline.completedSection": "Section complétée",
"learning.outline.dates": "Dates importantes",
"learning.outline.editGoal": "Modifier l'objectif",
"learning.outline.expandAll": "Tout développer",
"learning.outline.goal": "Objectif",
"learning.outline.goalUnsure": "Décider plus tard",
"learning.outline.handouts": "Documents de cours",
"learning.outline.incompleteAssignment": "Inachevé",
"learning.outline.incompleteSection": "Section incomplète",
"learning.outline.learnMore": "En savoir plus",
"learning.outline.altText.openSection": "Ouvrir",
"learning.outline.resume": "Poursuivre le cours",
"learning.outline.setGoal": "Pour commencer, définissez un objectif de cours en sélectionnant l'option ci-dessous qui décrit le mieux votre plan d'apprentissage.",
"learning.outline.start": "Commencer le cours",
"learning.outline.tools": "Outils de cours",
"learning.outline.upgradeButton": "Mise-à-jour ({symbol}{price})",
"learning.outline.upgradeTitle": "Obtenir un certificat vérifié",
"learning.outline.certificateAlt": "Exemple d'attestation",
"learning.outline.welcomeMessage": "Message de bienvenue",
"learning.outline.welcomeMessageShowMoreButton": "Montrer plus",
"learning.outline.welcomeMessageShowLessButton": "Montrer moins",
"learning.outline.goalWelcome": "Bienvenue sur",
"learning.proctoringPanel.header": "Ce cours contient des examens surveillés",
"learning.proctoringPanel.status.notStarted": "Non démarré",
"learning.proctoringPanel.status.started": "Débuté",
"learning.proctoringPanel.status.submitted": "Soumis",
"learning.proctoringPanel.status.verified": "Vérifié",
"learning.proctoringPanel.status.rejected": "Reje",
"learning.proctoringPanel.status.error": "Erreur",
"learning.proctoringPanel.status.otherCourseApproved": "Approuvé dans un autre cours",
"learning.proctoringPanel.status.expiringSoon": "Expire bientôt",
"learning.proctoringPanel.status": "Statut actuel d'intégration :",
"learning.proctoringPanel.message.notStarted": "Vous n'avez pas commencé votre examen d'intégration.",
"learning.proctoringPanel.message.started": "Vous avez commencé votre examen d'intégration.",
"learning.proctoringPanel.message.submitted": "Vous avez soumis votre examen d'intégration.",
"learning.proctoringPanel.message.verified": "Votre examen d'intégration a été approuvé pour ce cours.",
"learning.proctoringPanel.message.rejected": "Votre examen d'intégration a été rejeté. Veuillez réessayer l'intégration.",
"learning.proctoringPanel.message.error": "Une erreur s'est produite lors de votre examen d'intégration. Veuillez réessayer l'intégration.",
"learning.proctoringPanel.message.otherCourseApproved": "Votre examen d'intégration a été approuvé dans un autre cours.",
"learning.proctoringPanel.detail.otherCourseApproved": "Si votre appareil a changé, nous vous recommandons de passer l'examen d'intégration de ce cours afin de vous assurer que votre configuration répond toujours aux exigences de surveillance.",
"learning.proctoringPanel.message.expiringSoon": "Votre profil d'intégration a été approuvé dans un autre cours. Cependant, votre statut d'intégration expire bientôt. Veuillez compléter à nouveau l'intégration afin que vous soyez en mesure de continuer à passer des examens surveillés.",
"learning.proctoringPanel.generalInfo": "Vous devez terminer le processus d'intégration avant de passer un examen surveillé.",
"learning.proctoringPanel.generalInfoSubmitted": "Votre profil soumis est en cours de révision.",
"learning.proctoringPanel.generalTime": "L'examen du profil d'intégration peut prendre plus de 2 jours ouvrables.",
"learning.proctoringPanel.onboardingButton": "Complétez l'intégration",
"learning.proctoringPanel.onboardingPracticeButton": "Voir l'examen d'intégration",
"learning.proctoringPanel.onboardingButtonNotOpen": "Ouverture de l'intégration : {releaseDate}",
"learning.proctoringPanel.reviewRequirementsButton": "Examiner les instructions et la configuration système requise",
"learning.proctoringPanel.onboardingButtonPastDue": "Intégration en retard",
"learning.outline.sequence-due": "{description} échéance {assignmentDue}",
"progress.certificateStatus.unverifiedBody": "Afin de générer une attestation, vous devez effectuer une vérification d'identité. {idVerificationSupportLink}.",
"progress.certificateStatus.downloadableBody": "Présentez vos réalisations sur LinkedIn ou votre curriculum vitae aujourd'hui. Vous pouvez télécharger votre certificat maintenant et y accéder à tout moment depuis votre tableau de bord et votre profil.",
"courseCelebration.certificateBody.notAvailable.endDate": "Ce cours se termine le {endDate} et les notes finales et les attestations sont programmées pour être\n disponibles après le {certAvailableDate}.",
"progress.certificateStatus.notPassingHeader": "État de l'attestation",
"progress.certificateStatus.notPassingBody": "Pour être admissible à une attestation, vous devez avoir la note de passage.",
"progress.certificateStatus.inProgressHeader": "Plus de contenu sera bientôt disponible!",
"progress.certificateStatus.inProgressBody": "Il semble qu'il y ait plus de contenu dans ce cours qui sera publié dans le futur. Attendez les mises à jour par courriel ou revenez sur votre cours pour savoir quand ce contenu sera disponible.",
"progress.certificateStatus.requestableHeader": "État de l'attestation",
"progress.certificateStatus.requestableBody": "Félicitations, vous vous qualifiez pour une attestation! Pour accéder à votre attestation, demandez-la ci-dessous.",
"progress.certificateStatus.requestableButton": "Demander une attestation",
"progress.certificateStatus.unverifiedHeader": "État de l'attestation",
"progress.certificateStatus.unverifiedButton": "Vérifiez votre identité",
"progress.certificateStatus.courseCelebration.verificationPending": "La vérification de votre identité est en attente et votre attestation sera disponible une fois approuvé.",
"progress.certificateStatus.downloadableHeader": "Votre attestation est disponible!",
"progress.certificateStatus.downloadableButton": "Téléchargez mon attestation",
"progress.certificateStatus.viewableButton": "Voir mon attestation",
"progress.certificateStatus.notAvailableHeader": "État de l'attestation",
"progress.certificateStatus.upgradeHeader": "Obtenir un certificat",
"progress.certificateStatus.upgradeBody": "Vous êtes dans une piste d'audit et n'êtes pas admissible à une attestation. Afin d'obtenir vers une attestation, mettez à niveau votre cours dès aujourd'hui.",
"progress.certificateStatus.upgradeButton": "Mettre à jour dès maintenant",
"progress.certificateStatus.unverifiedHomeHeader": "Vérifiez votre identité pour obtenir une attestation !",
"progress.certificateStatus.unverifiedHomeButton": "Vérifiez mon identité",
"progress.certificateStatus.unverifiedHomeBody": "Afin de générer une attestation pour ce cours, vous devez compléter le processus de vérification d'identité.",
"progress.completion.donut.label": "achevée",
"progress.completion.body": "Cela représente la part du contenu du cours que vous avez terminé. Notez que certains contenus peuvent ne pas encore être publiés.",
"progress.completion.tooltip.locked": "Contenu que vous avez terminé.",
"progress.completion.header": "Achèvement du cours",
"progress.completion.tooltip": "Contenu auquel vous avez accès et que vous n'avez pas terminé.",
"progress.completion.tooltip.complete": "Contenu verrouillé et disponible uniquement pour ceux qui effectuent une mise à niveau.",
"progress.completion.donut.percentComplete": "Vous avez terminé {percent}% du contenu de ce cours.",
"progress.completion.donut.percentIncomplete": "Vous n'avez pas terminé {percent}% du contenu de ce cours auquel vous avez accès.",
"progress.completion.donut.percentLocked": "{percent}% du contenu de ce cours est verrouillé et disponible uniquement pour ceux qui effectuent une mise à niveau.",
"progress.ungradedAlert": "Pour connaître la progression des aspects non notés du cours, consultez votre {outlineLink}.",
"progress.footnotes.droppableAssignments": "Le plus bas {numDroppable, plural, one{# {assignmentType} score} autre{# {assignmentType} scores}} supprimé.",
"progress.assignmentType": "Type d'évaluation",
"progress.footnotes.backToContent": "Retour au contenu",
"progress.courseGrade.body": "Cela représente votre note pondérée par rapport à la note nécessaire pour réussir ce cours.",
"progress.courseGrade.gradeBar.altText": "Votre note actuelle est {currentGrade}%. Une note pondérée de {passingGrade}% est nécessaire afin de réussir ce cours.",
"progress.courseGrade.footer.generic.passing": "Vous réussissez actuellement ce cours",
"progress.courseGrade.footer.nonPassing": "Une note pondérée de {passingGrade}% est nécessaire pour réussir ce cours.",
"progress.courseGrade.footer.passing": "Vous réussissez actuellement ce cours avec une note de {letterGrade} ({minGrade}-{maxGrade}%)",
"progress.courseGrade.preview.headerLocked": "fonction verrouillée",
"progress.courseGrade.preview.headerLimited": "fonctionnalité limitée",
"progress.courseGrade.preview.header.ariaHidden": "Aperçu d'un ",
"progress.courseGrade.preview.body.unlockCertificate": "Déverrouillez pour afficher les notes et obtenir une attestation.",
"progress.courseGrade.partialpreview.body.unlockCertificate": "Déverrouillez pour travailler en vue d'une attestation.",
"progress.courseGrade.preview.body.upgradeDeadlinePassed": "La date limite de mise à niveau dans ce cours est écoulée.",
"progress.courseGrade.preview.button.upgrade": "Mettre à jour dès maintenant",
"progress.courseGrade.gradeRange.tooltip": "Plage de notes pour ce cours :",
"progress.courseOutline": "Plan du Cours",
"progress.courseGrade.label.currentGrade": "Votre note actuelle",
"progress.detailedGrades": "Notes détaillées",
"progress.detailedGrades.emptyTable": "Vous n'avez actuellement aucun score de problème noté.",
"progress.footnotes.title": "Notes de bas de page du résumé des notes",
"progress.gradeSummary.grade": "Note",
"progress.courseGrade.grades": "Notes",
"progress.courseGrade.gradeRange.Tooltip": "Info-bulle de plage de notes",
"progress.gradeSummary": "Relevé de notes",
"progress.gradeSummary.limitedAccessExplanation": "Vous avez un accès limité aux devoirs notés dans le cadre du parcours audit de ce cours.",
"progress.gradeSummary.tooltip.alt": "Info-bulle des résumés de notes",
"progress.gradeSummary.tooltip.body": "La pondération de ce travail est déterminé par votre instructeur. En multipliant votre note par la pondération du travail, une note pondérée est calculée. Cette note pondérée servira à déterminer si vous réussissez le cours.",
"progress.courseGrade.label.passingGrade": "Note de passage",
"progress.detailedGrades.problemScore.label": "Score aux exercices : ",
"progress.detailedGrades.problemScore.toggleButton": "Basculer les scores des problèmes individuels pour {subsectionTitle}",
"progress.score": "Note",
"progress.weight": "Pondération",
"progress.weightedGrade": "Note pondérée",
"progress.weightedGradeSummary": "Votre résumé de note pondéré actuel",
"progress.noAcessToAssignmentType": "Vous n'avez pas accès aux devoirs de type {assignmentType}",
"progress.noAcessToSubsection": "Vous n'avez pas accès à la sous-section {displayName}",
"progress.header": "Votre progression",
"progress.header.targetUser": "Progression pour {username}",
"progress.link.studio": "Voir la notation dans Studio",
"progress.relatedLinks.datesCard.description": "Une vue du calendrier des dates d'échéance de vos cours et des devoirs à venir.",
"progress.relatedLinks.datesCard.link": "Dates",
"progress.relatedLinks.outlineCard.description": "A birds-eye view of your course content.",
"progress.relatedLinks.outlineCard.link": "Course Outline",
"progress.relatedLinks": "Related links",
"datesBanner.suggestedSchedule": "Weve built a suggested schedule to help you stay on track. But dont worry—its flexible so you can learn at your own pace.",
"progress.relatedLinks.outlineCard.description": "Une vue d'ensemble du contenu de votre cours.",
"progress.relatedLinks.outlineCard.link": "Plan du Cours",
"progress.relatedLinks": "Liens connexes",
"datesBanner.suggestedSchedule": "Nous avons établi un calendrier suggéré pour vous aider à rester sur la bonne voie. Mais ne vous inquiétez pas—il est flexible et vous permet d'apprendre à votre rythme.",
"datesBanner.upgradeToCompleteGradedBanner.header": "Upgrade to unlock",
"datesBanner.upgradeToCompleteGradedBanner.body": "You are auditing this course, which means that you are unable to participate in graded assignments. To complete graded assignments as part of this course, you can upgrade today.",
"datesBanner.upgradeToCompleteGradedBanner.button": "Upgrade now",
"datesBanner.upgradeToCompleteGradedBanner.body": "Vous auditez ce cours, ce qui signifie que vous ne pouvez pas participer aux devoirs notés. Pour terminer les devoirs notés dans le cadre de ce cours, vous pouvez mettre à niveau dès aujourd'hui.",
"datesBanner.upgradeToCompleteGradedBanner.button": "Mettre à jour dès maintenant",
"datesBanner.upgradeToResetBanner.body": "To keep yourself on track, you can update this schedule and shift the past due assignments into the future. Dont worry—you wont lose any of the progress youve made when you shift your due dates.",
"datesBanner.upgradeToResetBanner.button": "Upgrade to shift due dates",
"datesBanner.resetDatesBanner.header": "It looks like you missed some important deadlines based on our suggested schedule.",
"datesBanner.upgradeToResetBanner.button": "Réviser votre type d'inscription pour déplacer les dates limites",
"datesBanner.resetDatesBanner.header": "Il semble que vous ayez manqué des dates limites importantes en fonction de notre calendrier suggéré.",
"datesBanner.resetDatesBanner.body": "To keep yourself on track, you can update this schedule and shift the past due assignments into the future. Dont worry—you wont lose any of the progress youve made when you shift your due dates.",
"datesBanner.resetDatesBanner.button": "Shift due dates",
"unit.bookmark.button.add.bookmark": "Bookmark this page",
"unit.bookmark.button.remove.bookmark": "Bookmarked",
"learning.celebration.completed": "You just completed the first section of your course.",
"learning.celebration.congrats": "Congratulations!",
"learning.celebration.earned": "You earned it!",
"learning.celebration.emailSubject": "I'm on my way to completing {title} online with {platform}!",
"learning.celebration.forward": "Keep going",
"learning.celebration.share": "Take a moment to celebrate and share your progress.",
"learning.celebration.social": "Im on my way to completing {title} online with {platform}. What are you spending your time learning?",
"calculator.instructions.button.label": "Calculator Instructions",
"calculator.instructions": "For detailed information, see the {expressions_link}.",
"calculator.instructions.support.title": "Help Center",
"calculator.instructions.useful.tips": "Useful tips:",
"calculator.hint1": "Use parentheses () to make expressions clear. You can use parentheses inside other parentheses.",
"calculator.hint2": "Do not use spaces in expressions.",
"calculator.hint3": "For constants, indicate multiplication explicitly (example: 5*c).",
"calculator.hint4": "For affixes, type the number and affix without a space (example: 5c).",
"calculator.hint5": "For functions, type the name of the function, then the expression in parentheses.",
"calculator.instruction.table.to.use.heading": "To Use",
"datesBanner.resetDatesBanner.button": "Déplacer les dates limites",
"unit.bookmark.button.add.bookmark": "Ajouter cette page aux favoris",
"unit.bookmark.button.remove.bookmark": "Ajouté aux favoris",
"learning.celebration.completed": "Vous avez complété la première section de votre cours.",
"learning.celebration.congrats": "Félicitations !",
"learning.celebration.earned": "Vous l'avez mérité!",
"learning.celebration.emailSubject": "Je complèterai bientôt {title} en ligne avec {platform}!",
"learning.celebration.forward": "Continuer",
"learning.celebration.share": "Prenez un moment pour célébrer et partager votre progrès.",
"learning.celebration.social": "Je suis sur le point de terminer {title} en ligne avec {platform}. Que passez-vous votre temps à apprendre?",
"calculator.instructions.button.label": "Instructions de la calculatrice",
"calculator.instructions": "Pour plus d'informations, consultez le {expressions_link}.",
"calculator.instructions.support.title": "Centre d'aide",
"calculator.instructions.useful.tips": "Conseils utiles:",
"calculator.hint1": "Utilisez les parenthèses () pour clarifier vos expressions. Vous pouvez utiliser des parenthèses au sein d'autres parenthèses.",
"calculator.hint2": "Ne pas utiliser d'espaces dans ces expressions.",
"calculator.hint3": "Pour les constantes, indiquer explicitement la multiplication (par exemple: 5*c).",
"calculator.hint4": "Pour les suffixes, indiquer le nombre et le suffixe sans espace (exemple : 5c).",
"calculator.hint5": "Pour les fonctions, taper le nom de la fonction, puis l'expression entre parenthèses.",
"calculator.instruction.table.to.use.heading": "À utiliser",
"calculator.instruction.table.type.heading": "Type",
"calculator.instruction.table.examples.heading": "Examples",
"calculator.instruction.table.to.use.numbers": "Numbers",
"calculator.instruction.table.to.use.numbers.type1": "Integers",
"calculator.instruction.table.examples.heading": "Exemples",
"calculator.instruction.table.to.use.numbers": "Nombres",
"calculator.instruction.table.to.use.numbers.type1": "Nombres entiers",
"calculator.instruction.table.to.use.numbers.type2": "Fractions",
"calculator.instruction.table.to.use.numbers.type3": "Decimals",
"calculator.instruction.table.to.use.operators": "Operators",
"calculator.instruction.table.to.use.operators.type1": "(add, subtract, multiply, divide)",
"calculator.instruction.table.to.use.operators.type2": "(raise to a power)",
"calculator.instruction.table.to.use.operators.type3": "(parallel resistors)",
"calculator.instruction.table.to.use.constants": "Constants",
"calculator.instruction.table.to.use.numbers.type3": "Nombres décimaux",
"calculator.instruction.table.to.use.operators": "Opérateurs",
"calculator.instruction.table.to.use.operators.type1": "(additionner, soustraire, multiplier, diviser)",
"calculator.instruction.table.to.use.operators.type2": "(élever à une puissance)",
"calculator.instruction.table.to.use.operators.type3": "(résistances parallèles)",
"calculator.instruction.table.to.use.constants": "Constantes",
"calculator.instruction.table.to.use.affixes": "Affixes",
"calculator.instruction.table.to.use.affixes.type": "Percent sign (%) and metric affixes ({affixes})",
"calculator.instruction.table.to.use.basic.functions": "Basic functions",
"calculator.instruction.table.to.use.trig.functions": "Trigonometric functions",
"calculator.instruction.table.to.use.scientific.notation": "Scientific notation",
"calculator.instruction.table.to.use.scientific.notation.type1": "{exponentSyntax} and the exponent",
"calculator.instruction.table.to.use.scientific.notation.type2": "{notationSyntax} notation",
"calculator.instruction.table.to.use.scientific.notation.type3": "{notationSyntax} and the exponent",
"calculator.button.label": "Calculator",
"calculator.input.field.label": "Calculator Input",
"calculator.submit.button.label": "Calculate",
"calculator.result.field.label": "Calculator Result",
"calculator.result.field.placeholder": "Result",
"notes.button.show": "Show Notes",
"notes.button.hide": "Hide Notes",
"courseExit.catalogSearchSuggestion": "Looking to learn more? {searchOurCatalogLink} to find more courses and programs to explore.",
"courseCelebration.certificateBody.available": "\n Showcase your accomplishment on LinkedIn or your resumé today.\n You can download your certificate now and access it any time from your\n {dashboardLink} and {profileLink}.",
"courseCelebration.certificateBody.unverified": "In order to generate a certificate, you must complete ID verification.\n {idVerificationSupportLink} now.",
"courseCelebration.certificateBody.upgradable": "Its not too late to upgrade. For {price} you will unlock access to all graded\n assignments in this course. Upon completion, you will receive a verified certificate which is a\n valuable credential to improve your job prospects and advance your career, or highlight your\n certificate in school applications.",
"courseCelebration.upgradeDiscountCodePrompt": "Use code {code} at checkout for {percent}% off!",
"courseCelebration.recommendations.heading": "Keep building your skills with these courses!",
"calculator.instruction.table.to.use.affixes.type": "Pourcentage (%) et suffixes métriques ({affixes})",
"calculator.instruction.table.to.use.basic.functions": "Fonctions de base",
"calculator.instruction.table.to.use.trig.functions": "Fonctions trigonométriques",
"calculator.instruction.table.to.use.scientific.notation": "Notation scientifique",
"calculator.instruction.table.to.use.scientific.notation.type1": "{exponentSyntax} et l'exposant",
"calculator.instruction.table.to.use.scientific.notation.type2": "notation {notationSyntax}",
"calculator.instruction.table.to.use.scientific.notation.type3": "{notationSyntax} et l'exposant",
"calculator.button.label": "Calculatrice",
"calculator.input.field.label": "Sasie Calculatrice",
"calculator.submit.button.label": "Calculer",
"calculator.result.field.label": "Résultat de la calculatrice",
"calculator.result.field.placeholder": "Résultat",
"notes.button.show": "Afficher les notes",
"notes.button.hide": "Masquer les notes",
"courseExit.catalogSearchSuggestion": "Vous souhaitez en apprendre plus? {searchOurCatalogLink} pour trouver plus de cours et de programmes à explorer.",
"courseCelebration.certificateBody.available": "\n Affichez vos accomplissements sur LinkedIn ou votre CV dès aujourd'hui.\n Vous pouvez télécharger votre attestation maintenant et y accéder à tout moment depuis vos\n {dashboardLink} et {profileLink}.",
"courseCelebration.certificateBody.unverified": "Afin de générer une attestation, vous devez effectuer une vérification d'identité.\n {idVerificationSupportLink} maintenant.",
"courseCelebration.certificateBody.upgradable": "Il nest pas trop tard pour effectuer une mise à niveau. Pour {price}, vous débloquerez l'accès à tous les\n devoirs dans ce cours. À la fin, vous recevrez une attestation qui est une source\n d'informations précieuses pour améliorer vos perspectives d'emploi et faire progresser votre carrière, ou mettre en valeur votre\n attestation dans des demandes d'admission.",
"courseCelebration.upgradeDiscountCodePrompt": "Utilisez le code {code} lors du paiement pour {percent}% de réduction!",
"courseCelebration.recommendations.heading": "Continuez à développer vos compétences avec ces cours!",
"courseCelebration.recommendations.formatting.list_join": "{style, select, punctuation {, } conjunction { {sp}and } other { }}",
"courseCelebration.recommendations.browse_catalog": "Explore more courses",
"courseCelebration.recommendations.loading_recommendations": "Loading recommendations",
"courseCelebration.recommendations.card.schools.label": "Schools and Partners",
"courseCelebration.recommendations.label": "Course",
"courseCelebration.dashboardInfo": "You can access this course and its materials on your {dashboardLink}.",
"courseExit.programs.applyForCredit": "Apply for credit",
"courseCelebration.certificateHeader.downloadable": "Your certificate is available!",
"courseCelebration.certificateHeader.notAvailable": "Your grade and certificate will be ready soon!",
"courseCelebration.certificateBody.notAvailable.accessCertificate": "If you have earned a passing grade, your certificate will be automatically issued.",
"courseCelebration.certificateHeader.unverified": "You must complete verification to receive your certificate.",
"courseCelebration.certificateHeader.requestable": "Congratulations, you qualified for a certificate!",
"courseCelebration.certificateHeader.upgradable": "Upgrade to pursue a verified certificate",
"courseCelebration.certificateImage": "Sample certificate",
"courseCelebration.completedCourseHeader": "You have completed your course.",
"courseCelebration.congratulationsHeader": "Congratulations!",
"courseCelebration.congratulationsImage": "Four people raising their hands in celebration",
"courseExit.courseInProgressDescription": "It looks like there is more content in this course that will be released in the future. Look out for email updates or check back on your course for when this content will be available.",
"courseExit.courseInProgressHeader": "More content is coming soon!",
"courseExit.dashboardLink": "Dashboard",
"courseCelebration.downloadButton": "Download my certificate",
"courseExit.endOfCourseDescription": "Unfortunately, you are not currently eligible for a certificate. You need to receive a passing grade to be eligible for a certificate.",
"courseExit.endOfCourseHeader": "Youve reached the end of the course!",
"courseExit.endOfCourseTitle": "End of Course",
"courseExit.idVerificationSupportLink": "Learn more about ID verification",
"courseCelebration.linkedinAddToProfileButton": "Add to LinkedIn profile",
"courseExit.programs.microBachelors.learnMore": "Learn more about how your MicroBachelors credential can be applied for credit.",
"courseExit.programs.microMasters.learnMore": "Learn more about the process of applying MicroMasters certificates to Masters degrees.",
"courseExit.programs.microMasters.mastersMessage": "If youre interested in using your MicroMasters certificate towards a Masters program, you can get started today!",
"learn.sequence.navigation.complete.button": "Complete the course",
"courseExit.nextButton.endOfCourse": "Next (end of course)",
"courseExit.profileLink": "Profile",
"courseExit.programs.lastCourse": "You have completed the last course in {title}!",
"courseCelebration.requestCertificateBodyText": "In order to access your certificate, request it below.",
"courseCelebration.requestCertificateButton": "Request certificate",
"courseExit.searchOurCatalogLink": "Search our catalog",
"courseCelebration.shareMessage": "Share your success on social media or email.",
"courseExit.social.shareCompletionMessage": "I just completed {title} with {platform}!",
"courseExit.upgradeButton": "Upgrade now",
"courseExit.upgradeLink": "upgrade now",
"courseCelebration.verificationPending": "Your ID verification is pending and your certificate will be available once approved.",
"courseExit.verifiedCertificateSupportLink": "Learn more about verified certificates",
"courseCelebration.verifyIdentityButton": "Verify ID now",
"courseCelebration.viewCertificateButton": "View my certificate",
"courseExit.viewCourseScheduleButton": "View course schedule",
"courseExit.viewCoursesButton": "View my courses",
"courseExit.viewGradesButton": "View grades",
"courseExit.programCompletion.dashboardMessage": "To view your certificate status, check the Programs section of your {programLink}.",
"courseExit.upgradeFootnote": "Access to this course and its materials are available on your dashboard until {expirationDate}. To extend access, {upgradeLink}.",
"learn.course.license.allRightsReserved.text": "All Rights Reserved",
"learn.course.license.creativeCommons.terms.preamble": "Creative Commons licensed content, with terms as follows:",
"learn.course.license.creativeCommons.terms.by": "Attribution",
"learn.course.license.creativeCommons.terms.nc": "Noncommercial",
"learn.course.license.creativeCommons.terms.nd": "No Derivatives",
"learn.course.license.creativeCommons.terms.sa": "Share Alike",
"learn.course.license.creativeCommons.terms.zero": "No terms",
"learn.course.license.creativeCommons.text": "Some Rights Reserved",
"learn.breadcrumb.navigation.course.home": "Course",
"notification.tray.container": "Notification tray",
"notification.open.button": "Show notification tray",
"notification.close.button": "Close notification tray",
"responsive.close.notification": "Back to course",
"courseCelebration.recommendations.browse_catalog": "Explorez plus de cours",
"courseCelebration.recommendations.loading_recommendations": "Chargement des recommandations",
"courseCelebration.recommendations.card.schools.label": "Écoles et Partenaires",
"courseCelebration.recommendations.label": "Cours",
"courseCelebration.dashboardInfo": "Vous pouvez accéder à ce cours et à ses supports sur votre {dashboardLink}.",
"courseExit.programs.applyForCredit": "Demander un crédit",
"courseCelebration.certificateHeader.downloadable": "Votre attestation est disponible!",
"courseCelebration.certificateHeader.notAvailable": "Votre note et votre attestation seront bientôt prêtes !",
"courseCelebration.certificateBody.notAvailable.accessCertificate": "Si vous avez obtenu une note de passage, votre attestation sera automatiquement générée.",
"courseCelebration.certificateHeader.unverified": "Vous devez avoir complété votre vérification pour recevoir votre attestation.",
"courseCelebration.certificateHeader.requestable": "Félicitations, vous avez terminé le processus pour passer un certificat !",
"courseCelebration.certificateHeader.upgradable": "Mettre à niveau pour obtenir une attestation",
"courseCelebration.certificateImage": "Exemple d'attestation",
"courseCelebration.completedCourseHeader": "Vous avez complété ce cours.",
"courseCelebration.congratulationsHeader": "Félicitations !",
"courseCelebration.congratulationsImage": "Quatre personnes levant leurs mains pour célébrer",
"courseExit.courseInProgressDescription": "Il semble qu'il y ait plus de contenu dans ce cours qui sera publié dans le futur. Attendez les mises à jour par courriel ou revenez sur votre cours pour savoir quand ce contenu sera disponible.",
"courseExit.courseInProgressHeader": "Plus de contenu sera bientôt disponible!",
"courseExit.dashboardLink": "Tableau de bord",
"courseCelebration.downloadButton": "Téléchargez mon attestation",
"courseExit.endOfCourseDescription": "Malheureusement, vous n'êtes actuellement pas éligible pour une attestatation. Vous devez recevoir une note de passage pour être admissible à une attestation.",
"courseExit.endOfCourseHeader": "Vous avez atteint la fin du cours!",
"courseExit.endOfCourseTitle": "Fin du cours",
"courseExit.idVerificationSupportLink": "En savoir plus sur la vérification d'identité",
"courseCelebration.linkedinAddToProfileButton": "Ajouter au profil LinkedIn",
"courseExit.programs.microBachelors.learnMore": "Apprenez-en davantage sur la façon dont votre accréditation MicroBachelors peut être utilisée pour demander un crédit.",
"courseExit.programs.microMasters.learnMore": "En savoir plus sur le processus dapplication des attestations MicroMasters aux diplômes de maîtrise.",
"courseExit.programs.microMasters.mastersMessage": "Si vous souhaitez utiliser votre attestation MicroMasters en vue d'un programme de maîtrise, vous pouvez commencer dès aujourd'hui!",
"learn.sequence.navigation.complete.button": "Compléter le cours",
"courseExit.nextButton.endOfCourse": "Suivant (fin du cours)",
"courseExit.profileLink": "Profil",
"courseExit.programs.lastCourse": "Vous avez terminé le dernier cours de {title}!",
"courseCelebration.requestCertificateBodyText": "Pour accéder à votre certificat, demandez-le ci-dessous.",
"courseCelebration.requestCertificateButton": "Demander une attestation",
"courseExit.searchOurCatalogLink": "Rechercher dans notre catalogue",
"courseCelebration.shareMessage": "Partagez votre succès sur les réseaux sociaux ou par courriel.",
"courseExit.social.shareCompletionMessage": "Je viens de terminer {title} avec {platform}!",
"courseExit.upgradeButton": "Mettre à jour dès maintenant",
"courseExit.upgradeLink": "mettre à jour maintenant",
"courseCelebration.verificationPending": "La vérification de votre identité est en attente et votre attestation sera disponible une fois approuvé.",
"courseExit.verifiedCertificateSupportLink": "En savoir plus sur les attestations",
"courseCelebration.verifyIdentityButton": "Vérifiez votre identité maintenant",
"courseCelebration.viewCertificateButton": "Voir mon attestation",
"courseExit.viewCourseScheduleButton": "Voir le calendrier de cours",
"courseExit.viewCoursesButton": "Voir mes cours",
"courseExit.viewGradesButton": "Voir les notes",
"courseExit.programCompletion.dashboardMessage": "Pour afficher l'état de votre attestation, consultez la section Programmes de votre {programLink}.",
"courseExit.upgradeFootnote": "L'accès à ce cours et à ses supports est disponible sur votre tableau de bord jusqu'au {expirationDate}. Pour étendre l'accès, {upgradeLink}.",
"learn.course.license.allRightsReserved.text": "Tous droits servés",
"learn.course.license.creativeCommons.terms.preamble": "Contenu sous license Creative Commons, avec les conditions suivantes:",
"learn.course.license.creativeCommons.terms.by": "Crédit",
"learn.course.license.creativeCommons.terms.nc": "Non commercial",
"learn.course.license.creativeCommons.terms.nd": "Pas de travaux dérivés",
"learn.course.license.creativeCommons.terms.sa": "Partage à l'identique",
"learn.course.license.creativeCommons.terms.zero": "Aucun termes",
"learn.course.license.creativeCommons.text": "Quelques droits servés",
"learn.breadcrumb.navigation.course.home": "Cours",
"notification.tray.container": "Barre de notification",
"notification.open.button": "Afficher la barre de notification",
"notification.close.button": "Fermer la barre de notification",
"responsive.close.notification": "Retour au cours",
"notification.tray.title": "Notifications",
"notification.tray.no.message": "You have no new notifications at this time.",
"learn.contentLock.content.locked": "Content Locked",
"learn.contentLock.complete.prerequisite": "You must complete the prerequisite: '{prereqSectionName}' to access this content.",
"learn.contentLock.goToSection": "Go To Prerequisite Section",
"learn.hiddenAfterDue.gradeAvailable": "If you have completed this assignment, your grade is available on the {progressPage}.",
"learn.hiddenAfterDue.header": "The due date for this assignment has passed.",
"learn.hiddenAfterDue.description": "Because the due date has passed, this assignment is no longer available.",
"learn.hiddenAfterDue.progressPage": "progress page",
"learn.honorCode.content": "Honesty and academic integrity are important to {siteName} and the institutions providing courses and programs on the {siteName} site. By clicking “I agree” below, I confirm that I have read, understand, and will abide by the {link} for the {siteName} Site.",
"learn.honorCode.name": "Honor Code",
"learn.honorCode.cancel": "Cancel",
"learn.honorCode.agree": "I agree",
"gatedContent.paragraph.bulletOne": "Earn a {verifiedCertLink} of completion to showcase on your resumé",
"gatedContent.paragraph.bulletTwo": "Unlock access to all course activities, including {gradedAssignments}",
"gatedContent.paragraph.bulletThree": "{fullAccess} to course content and materials, even after the course ends",
"gatedContent.paragraph.bulletFour": "Support our {nonProfitMission} at edX",
"learn.lockPaywall.title": "Graded assignments are locked",
"learn.lockPaywall.content": "Upgrade to gain access to locked features like this one and get the most out of your course.",
"learn.lockPaywall.example.alt": "Example Certificate",
"learn.lockPaywall.list.intro": "When you upgrade, you:",
"learn.lockPaywall.list.bullet1.linktext": "verified certificate",
"learn.lockPaywall.list.bullet2.boldtext": "graded assignments",
"learn.lockPaywall.list.bullet3.boldtext": "Full access",
"learn.lockPaywall.list.bullet4.boldtext": "non-profit mission",
"learn.loading.content.lock": "Loading locked content messaging...",
"learn.loading.honor.codk": "Loading honor code messaging...",
"learn.loading.learning.sequence": "Loading learning sequence...",
"learn.course.load.failure": "There was an error loading this course.",
"learn.sequence.no.content": "There is no content here.",
"learn.sequence.navigation.next.button": "Next",
"learn.sequence.navigation.next.up.button": "Next Up: {title}",
"learn.sequence.navigation.previous.button": "Previous",
"learn.course.sequence.navigation.mobile.menu": "{current} of {total}",
"learn.redirect.interstitial.message": "Redirecting...",
"learn.loading.error": "Error: {error}",
"learning.celebration.emailBody": "What are you spending your time learning?",
"learning.social.shareEmail": "Share your progress via email.",
"learning.social.shareService": "Share your progress on {service}.",
"general.altText.close": "Close",
"learning.logistration.register": "register",
"general.register.sentenceCase": "Register",
"learning.logistration.login": "sign in",
"general.signIn.sentenceCase": "Sign in",
"learn.course.tabs.navigation.overflow.menu": "More...",
"learning.offer.screenReaderPrices": "Original price: {originalPrice}, discount price: {discountedPrice}",
"learning.upgradeButton.screenReaderInlinePrices": "Original price: {originalPrice}",
"learning.upgradeButton.buttonText": "Upgrade for {pricing}",
"learning.upgradeNowButton.buttonText": "Upgrade now for {pricing}",
"learning.generic.upgradeNotification.verifiedCertLink": "verified certificate",
"learning.generic.upgradeNotification.verifiedCertMessage": "Earn a {verifiedCertLink} of completion to showcase on your resumé",
"learning.generic.upgradeNotification.noFBE.nonProfitMission": "Support our {nonProfitMission} at edX",
"learning.generic.upgradeNotification.gradedAssignments": "graded assignments",
"learning.generic.upgradeNotification.verifiedCertLink.fullAccess": "Full access",
"learning.generic.upgradeNotification.FBE.nonProfitMission": "non-profit mission",
"learning.generic.upgradeNotification.unlockGraded": "Unlock your access to all course activities, including {gradedAssignments}",
"learning.generic.upgradeNotification.fullAccess": "{fullAccess} to course content and materials, even after the course ends",
"learning.generic.upgradeNotification.nonProfitMission": "Support our {nonProfitMission} at edX",
"learning.generic.upgradeNotification.expirationAccessLoss.progress": "including any progress",
"learning.generic.upgradeNotification.expirationVerifiedCert.benefits": "benefits of upgrading",
"learning.generic.upgradeNotification.expirationAccessLoss": "You will lose all access to this course, {includingAnyProgress}, on {date}.",
"learning.generic.upgradeNotification.expirationVerifiedCert": "Upgrading your course enables you to pursue a verified certificate and unlocks numerous features. Learn more about the {benefitsOfUpgrading}.",
"learning.generic.upgradeNotification.expirationDays": "{dayCount, number} {dayCount, plural, \n one {day}\n other {days}} left",
"learning.generic.upgradeNotification.expirationHours": "{hourCount, number} {hourCount, plural,\n one {hour}\n other {hours}} left",
"learning.generic.upgradeNotification.expirationMinutes": "Less than 1 hour left",
"learning.generic.upgradeNotification.expiration": "Course access will expire {date}",
"learning.generic.upgradeNotification.firstTimeLearnerDiscount": "{percentage}% First-Time Learner Discount",
"learning.generic.upgradeNotification.accessExpiration": "Upgrade your course today",
"learning.generic.upgradeNotification.accessExpirationUrgent": "Course Access Expiration",
"learning.generic.upgradeNotification.pursueAverifiedCertificate": "Pursue a verified certificate",
"learning.generic.upgradeNotification.code": "Use code {code} at checkout",
"masquerade-widget.userName.error.generic": "An error has occurred; please try again.",
"masquerade-widget.userName.input.placeholder": "Username or email",
"masquerade-widget.userName.input.label": "Masquerade as this user",
"notification.tray.no.message": "Vous n'avez aucune nouvelle notification pour le moment.",
"learn.contentLock.content.locked": "Contenu vérouillé",
"learn.contentLock.complete.prerequisite": "Vous devez compléter le prérequis: '{prereqSectionName}' pour accéder à ce contenu.",
"learn.contentLock.goToSection": "Aller à la section des prérequis",
"learn.hiddenAfterDue.gradeAvailable": "Si vous avez complété ce travail, votre note est disponible sur {progressPage}.",
"learn.hiddenAfterDue.header": "La date d'échéance de ce devoir est passée.",
"learn.hiddenAfterDue.description": "Ce devoir n'est plus disponible, car la date limite est passée.",
"learn.hiddenAfterDue.progressPage": "page de progression",
"learn.honorCode.content": "L'honnêteté et l'intégrité académique sont importantes pour {siteName} et les institutions offrant des cours et des programmes sur le site {siteName}. En cliquant sur « J'accepte » ci-dessous, je confirme que j'ai lu, compris et respecterai le {link} pour le site {siteName}.",
"learn.honorCode.name": "Code d'honneur",
"learn.honorCode.cancel": "Annuler",
"learn.honorCode.agree": "Je suis d'accord",
"gatedContent.paragraph.bulletOne": "Obtenez un {verifiedCertLink} d'achèvement pour le mettre en valeur sur votre CV",
"gatedContent.paragraph.bulletTwo": "Déverrouillez l'accès à toutes les activités du cours, y compris {gradedAssignments}",
"gatedContent.paragraph.bulletThree": "{fullAccess} au contenu et aux supports du cours, même après la fin du cours",
"gatedContent.paragraph.bulletFour": "Soutenez notre {nonProfitMission} sur edX",
"learn.lockPaywall.title": "Les devoirs notés sont verrouillés",
"learn.lockPaywall.content": "Mettez à niveau pour accéder à des fonctionnalités verrouillées comme celle-ci et tirer le meilleur parti de votre cours.",
"learn.lockPaywall.example.alt": "Exemple d'attestation",
"learn.lockPaywall.list.intro": "Lorsque vous effectuez une mise à niveau, vous :",
"learn.lockPaywall.list.bullet1.linktext": "attestation vérifiée",
"learn.lockPaywall.list.bullet2.boldtext": "évaluations notées",
"learn.lockPaywall.list.bullet3.boldtext": "Accès complet",
"learn.lockPaywall.list.bullet4.boldtext": "mission à but non lucratif",
"learn.loading.content.lock": "Chargement du contenu bloqué de messagerie...",
"learn.loading.honor.codk": "Chargement de la messagerie du code d'honneur...",
"learn.loading.learning.sequence": "Chargement de la séquence d'apprentissage...",
"learn.course.load.failure": "Il y a eu une erreur lors du chargement de ce cours.",
"learn.sequence.no.content": "Il n'y a pas de contenu ici.",
"learn.sequence.navigation.next.button": "Suivant",
"learn.sequence.navigation.next.up.button": "Prochaine étape : {title}",
"learn.sequence.navigation.previous.button": "Précédent",
"learn.course.sequence.navigation.mobile.menu": "{current} de {total}",
"learn.redirect.interstitial.message": "Redirection...",
"learn.loading.error": "Erreur : {error}",
"learning.celebration.emailBody": "Qu'apprenez-vous durant votre temps libre?",
"learning.social.shareEmail": "Partagez vos progrès par courriel.",
"learning.social.shareService": "Partagez vos progrès sur {service}.",
"general.altText.close": "Fermer",
"learning.logistration.register": "inscription",
"general.register.sentenceCase": "S'inscrire",
"learning.logistration.login": "connexion",
"general.signIn.sentenceCase": "Connectez-vous",
"learn.course.tabs.navigation.overflow.menu": "Plus...",
"learning.offer.screenReaderPrices": "Prix d'origine: {originalPrice}, prix réduit : {discountedPrice}",
"learning.upgradeButton.screenReaderInlinePrices": "Prix d'origine : {originalPrice}",
"learning.upgradeButton.buttonText": "Mise à niveau pour {pricing}",
"learning.upgradeNowButton.buttonText": "Mettez à niveau maintenant pour {pricing}",
"learning.generic.upgradeNotification.verifiedCertLink": "attestation vérifiée",
"learning.generic.upgradeNotification.verifiedCertMessage": "Obtenez un {verifiedCertLink} d'achèvement pour le mettre en valeur sur votre CV",
"learning.generic.upgradeNotification.noFBE.nonProfitMission": "Soutenez notre {nonProfitMission} sur edX",
"learning.generic.upgradeNotification.gradedAssignments": "évaluations notées",
"learning.generic.upgradeNotification.verifiedCertLink.fullAccess": "Accès complet",
"learning.generic.upgradeNotification.FBE.nonProfitMission": "mission à but non lucratif",
"learning.generic.upgradeNotification.unlockGraded": "Déverrouillez l'accès à toutes les activités du cours, y compris {gradedAssignments}",
"learning.generic.upgradeNotification.fullAccess": "{fullAccess} au contenu et aux supports du cours, même après la fin du cours",
"learning.generic.upgradeNotification.nonProfitMission": "Soutenez notre {nonProfitMission} sur edX",
"learning.generic.upgradeNotification.expirationAccessLoss.progress": "incluant tous les progrès",
"learning.generic.upgradeNotification.expirationVerifiedCert.benefits": "bénéfices d'une mise à niveau",
"learning.generic.upgradeNotification.expirationAccessLoss": "Vous perdez tout accès à ce cours, {includingAnyProgress}, le {date}.",
"learning.generic.upgradeNotification.expirationVerifiedCert": "Mettre à niveau votre cours vous permettra de tenter d'obtenir une attestation vérifié et d'avoir accès à beaucoup d'autres fonctionnalités. Apprenez en plus à propos des {benefitsOfUpgrading}.",
"learning.generic.upgradeNotification.expirationDays": "{dayCount, number} {dayCount, plural, \n one {day}\n other {days}} restant",
"learning.generic.upgradeNotification.expirationHours": "{hourCount, number} {hourCount, plural,\n one {hour}\n other {hours}} restant",
"learning.generic.upgradeNotification.expirationMinutes": "Il reste moins de 1 heure",
"learning.generic.upgradeNotification.expiration": "L'accès au cours expirera le {date}",
"learning.generic.upgradeNotification.firstTimeLearnerDiscount": "{percentage}% de rabais pour les nouveaux apprenants",
"learning.generic.upgradeNotification.accessExpiration": "Mettre à niveau votre cours aujourd'hui",
"learning.generic.upgradeNotification.accessExpirationUrgent": "L'accès au cours expire",
"learning.generic.upgradeNotification.pursueAverifiedCertificate": "Obtenir un certificat vérifié",
"learning.generic.upgradeNotification.code": "Utilisez le code {code} lors du paiement",
"masquerade-widget.userName.error.generic": "Une erreur est survenue; veuillez réessayer.",
"masquerade-widget.userName.input.placeholder": "Nom d'utilisateur ou courriel",
"masquerade-widget.userName.input.label": "Se faire passer pour cet utilisateur",
"learning.effortEstimation.combinedEstimate": "{minutes} + {activities}",
"learning.effortEstimation.activities": "{activityCount, plural, one {# activity} other {# activities}}",
"learning.effortEstimation.activities": "{activityCount, plural, one {# activité} other {# activités}}",
"learning.effortEstimation.minutesAbbreviated": "{minuteCount, plural, one {# min} other {# min}}",
"learning.effortEstimation.minutesFull": "{minuteCount, plural, one {# minute} other {# minutes}}",
"learning.streakCelebration.congratulations": "Congratulations!",
"learning.streakCelebration.body": "Keep it up, youre on a roll!",
"learning.streakCelebration.button": "Keep it up",
"learning.streakCelebration.buttonSrOnly": "Close modal and continue",
"learning.streakCelebration.buttonAA759": "Continue with course",
"learning.streakCelebration.header": "day streak",
"learning.streakCelebration.factoidABoldedSection": "are 20x more likely to pass their course",
"learning.streakCelebration.factoidBBoldedSection": "complete 5x as much course content on average",
"learning.streakCelebration.streakDiscountMessage": "Youve unlocked a 15% off discount when you upgrade this course for a limited time only.",
"learning.streakcelebration.factoida": "Users who learn {streak_length} days in a row {bolded_section} than those who dont.",
"learning.streakcelebration.factoidb": "Users who learn {streak_length} days in a row {bolded_section} vs. those who dont.",
"learning.streakCelebration.streakCelebrationCouponEndDateMessage": "Ends {date}.",
"learning.loading.failure": "There was an error loading this course.",
"learning.loading": "Loading course page…"
"learning.streakCelebration.congratulations": "Félicitations !",
"learning.streakCelebration.body": "Continuez comme ça, vous êtes sur une lancée!",
"learning.streakCelebration.button": "Continuez ainsi",
"learning.streakCelebration.buttonSrOnly": "Fermer le modal et continuer",
"learning.streakCelebration.buttonAA759": "Continuer le cours",
"learning.streakCelebration.header": "série de jours",
"learning.streakCelebration.factoidABoldedSection": "ont 20 fois plus de chances de réussir leur cours",
"learning.streakCelebration.factoidBBoldedSection": "termine 5 fois plus de contenu de cours en moyenne",
"learning.streakCelebration.streakDiscountMessage": "Vous avez droit à 15% de rabais si vous mettez à jour ce cours pour un temps limité seulement.",
"learning.streakcelebration.factoida": "Les utilisateurs qui apprennent {streak_length} jours d'affilée {bolded_section} que ceux qui n'apprennent pas.",
"learning.streakcelebration.factoidb": "Les utilisateurs qui apprennent {streak_length} jours d'affilée {bolded_section} par rapport à ceux qui ne le font pas.",
"learning.streakCelebration.streakCelebrationCouponEndDateMessage": "Termine {date}.",
"learning.loading.failure": "Il y a eu une erreur lors du chargement de ce cours.",
"learning.loading": "Chargement de vos cours..."
}

View File

@@ -108,6 +108,7 @@ initialize({
TERMS_OF_SERVICE_URL: process.env.TERMS_OF_SERVICE_URL || null,
TWITTER_HASHTAG: process.env.TWITTER_HASHTAG || null,
TWITTER_URL: process.env.TWITTER_URL || null,
LEGACY_THEME_NAME: process.env.LEGACY_THEME_NAME || null,
}, 'LearnerAppConfig');
},
},

View File

@@ -36,29 +36,6 @@
}
}
.course-header {
min-width: 0;
.course-title-lockup {
min-width: 0;
span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-bottom: 0.1rem;
}
}
.user-dropdown {
.btn {
height: 3rem;
@media (max-width: -1 + map-get($grid-breakpoints, "sm")) {
padding: 0 0.5rem;
}
}
}
}
.course-tabs-navigation {
border-bottom: solid 1px #eaeaea;

View File

@@ -5,7 +5,7 @@ import { Helmet } from 'react-helmet';
import { getConfig } from '@edx/frontend-platform';
import { useToggle } from '@edx/paragon';
import { CourseTabsNavigation } from '../course-header';
import { CourseTabsNavigation } from '../course-tabs';
import { useModel } from '../generic/model-store';
import { AlertList } from '../generic/user-messages';
import StreakModal from '../shared/streak-celebration';

View File

@@ -3,7 +3,7 @@ import { Factory } from 'rosie';
import { initializeTestStore, render, screen } from '../setupTest';
import LoadedTabPage from './LoadedTabPage';
jest.mock('../course-header/CourseTabsNavigation', () => () => <div data-testid="CourseTabsNavigation" />);
jest.mock('../course-tabs/CourseTabsNavigation', () => () => <div data-testid="CourseTabsNavigation" />);
jest.mock('../instructor-toolbar/InstructorToolbar', () => () => <div data-testid="InstructorToolbar" />);
jest.mock('../shared/streak-celebration/StreakCelebrationModal', () => () => <div data-testid="StreakModal" />);

View File

@@ -6,9 +6,9 @@ import { Redirect } from 'react-router';
import Footer from '@edx/frontend-component-footer';
import { Toast } from '@edx/paragon';
import { Header } from '../course-header';
import { getAccessDeniedRedirectUrl } from '../shared/access';
import { LearningHeader as Header } from '@edx/frontend-component-header';
import PageLoading from '../generic/PageLoading';
import { getAccessDeniedRedirectUrl } from '../shared/access';
import { useModel } from '../generic/model-store';
import genericMessages from '../generic/messages';