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_TRADEMARK_URL=''
LOGO_WHITE_URL='' LOGO_WHITE_URL=''
FAVICON_URL='' FAVICON_URL=''
LEGACY_THEME_NAME=''
MARKETING_SITE_BASE_URL='' MARKETING_SITE_BASE_URL=''
ORDER_HISTORY_URL='' ORDER_HISTORY_URL=''
REFRESH_ACCESS_TOKEN_ENDPOINT='' 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_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
LEGACY_THEME_NAME=''
MARKETING_SITE_BASE_URL='http://localhost:18000' MARKETING_SITE_BASE_URL='http://localhost:18000'
ORDER_HISTORY_URL='http://localhost:1996/orders' ORDER_HISTORY_URL='http://localhost:1996/orders'
PORT=2000 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_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
LEGACY_THEME_NAME=''
MARKETING_SITE_BASE_URL='http://localhost:18000' MARKETING_SITE_BASE_URL='http://localhost:18000'
ORDER_HISTORY_URL='http://localhost:1996/orders' ORDER_HISTORY_URL='http://localhost:1996/orders'
PORT=2000 PORT=2000

7783
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -13,7 +13,7 @@ export default function LmsHtmlFragment({
<html> <html>
<head> <head>
<base href="${getConfig().LMS_BASE_URL}" target="_parent"> <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"> <link rel="stylesheet" type="text/css" href="${getConfig().BASE_URL}/src/course-home/outline-tab/LmsHtmlFragment.css">
</head> </head>
<body class="${className}">${html}</body> <body class="${className}">${html}</body>

View File

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

View File

@@ -1,12 +1,22 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; 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 { OverlayTrigger, Popover } from '@edx/paragon';
import messages from '../messages'; import messages from '../messages';
function PassingGradeTooltip({ intl, passingGrade, tooltipClassName }) { function PassingGradeTooltip({ intl, passingGrade, tooltipClassName }) {
const isLocaleRtl = isRtl(getLocale());
let passingGradeDirection = passingGrade < 50 ? '' : '-';
if (isLocaleRtl) {
passingGradeDirection = passingGrade < 50 ? '-' : '';
}
return ( return (
<> <>
<OverlayTrigger <OverlayTrigger
@@ -21,17 +31,17 @@ function PassingGradeTooltip({ intl, passingGrade, tooltipClassName }) {
)} )}
> >
<g> <g>
<circle cx={`${passingGrade}%`} cy="50%" r="8.5" fill="transparent" /> <circle cx={`${isLocaleRtl ? 100 - passingGrade : passingGrade}%`} cy="50%" r="8.5" fill="transparent" />
<circle className="grade-bar--passing" cx={`${passingGrade}%`} cy="50%" r="4.5" /> <circle className="grade-bar--passing" cx={`${isLocaleRtl ? 100 - passingGrade : passingGrade}%`} cy="50%" r="4.5" />
</g> </g>
</OverlayTrigger> </OverlayTrigger>
<text <text
className="x-small" className="x-small"
textAnchor={passingGrade < 50 ? 'start' : 'end'} textAnchor={passingGrade < 50 ? 'start' : 'end'}
x={`${passingGrade}%`} x={`${isLocaleRtl ? 100 - passingGrade : passingGrade}%`}
y="90px" y="90px"
style={{ transform: `translateX(${passingGrade < 50 ? '' : '-'}3.4em)` }} style={{ transform: `translateX(${passingGradeDirection}3.4em)` }}
> >
{intl.formatMessage(messages.passingGradeLabel)} {intl.formatMessage(messages.passingGradeLabel)}
</text> </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'; 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; const courseId = defaultCourseId;
function assertLoadedHeader(container) { 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. // Ensure the course number and org appear - this proves we loaded course metadata properly.
expect(courseHeader).toHaveTextContent(courseMetadata.number); expect(courseHeader).toHaveTextContent(courseMetadata.number);
expect(courseHeader).toHaveTextContent(courseMetadata.org); 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 { faHome } from '@fortawesome/free-solid-svg-icons';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { SelectMenu } from '@edx/paragon'; import { SelectMenu } from '@edx/paragon';
import { Link } from 'react-router-dom';
import { useModel, useModels } from '../../generic/model-store'; import { useModel, useModels } from '../../generic/model-store';
/** [MM-P2P] Experiment */ /** [MM-P2P] Experiment */
import { MMP2PFlyoverTrigger } from '../../experiments/mm-p2p'; import { MMP2PFlyoverTrigger } from '../../experiments/mm-p2p';
@@ -30,9 +31,12 @@ function CourseBreadcrumb({
> >
{ getConfig().ENABLE_JUMPNAV !== 'true' || content.length < 2 || !isStaff { 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} {defaultContent.label}
</a> </Link>
) )
: ( : (
<SelectMenu isLink defaultMessage={defaultContent.label}> <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"> <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"> <ol className="list-unstyled d-flex flex-nowrap align-items-center m-0">
<li className="list-unstyled d-flex m-0"> <li className="list-unstyled d-flex m-0">
<a <Link
href={`/course/${courseId}/home`}
className="flex-shrink-0 text-primary" className="flex-shrink-0 text-primary"
to={`/course/${courseId}/home`}
> >
<FontAwesomeIcon icon={faHome} className="mr-2" /> <FontAwesomeIcon icon={faHome} className="mr-2" />
<FormattedMessage <FormattedMessage
@@ -132,7 +136,7 @@ export default function CourseBreadcrumbs({
description="The course home link in breadcrumbs nav" description="The course home link in breadcrumbs nav"
defaultMessage="Course" defaultMessage="Course"
/> />
</a> </Link>
</li> </li>
{links.map(content => ( {links.map(content => (
<CourseBreadcrumb <CourseBreadcrumb

View File

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

View File

@@ -237,7 +237,13 @@ export function fetchSequence(sequenceId) {
dispatch(fetchSequenceSuccess({ sequenceId })); dispatch(fetchSequenceSuccess({ sequenceId }));
} }
} catch (error) { } 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 })); dispatch(fetchSequenceFailure({ sequenceId }));
} }
}; };

View File

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

View File

@@ -108,6 +108,7 @@ initialize({
TERMS_OF_SERVICE_URL: process.env.TERMS_OF_SERVICE_URL || null, TERMS_OF_SERVICE_URL: process.env.TERMS_OF_SERVICE_URL || null,
TWITTER_HASHTAG: process.env.TWITTER_HASHTAG || null, TWITTER_HASHTAG: process.env.TWITTER_HASHTAG || null,
TWITTER_URL: process.env.TWITTER_URL || null, TWITTER_URL: process.env.TWITTER_URL || null,
LEGACY_THEME_NAME: process.env.LEGACY_THEME_NAME || null,
}, 'LearnerAppConfig'); }, '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 { .course-tabs-navigation {
border-bottom: solid 1px #eaeaea; border-bottom: solid 1px #eaeaea;

View File

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

View File

@@ -3,7 +3,7 @@ import { Factory } from 'rosie';
import { initializeTestStore, render, screen } from '../setupTest'; import { initializeTestStore, render, screen } from '../setupTest';
import LoadedTabPage from './LoadedTabPage'; 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('../instructor-toolbar/InstructorToolbar', () => () => <div data-testid="InstructorToolbar" />);
jest.mock('../shared/streak-celebration/StreakCelebrationModal', () => () => <div data-testid="StreakModal" />); 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 Footer from '@edx/frontend-component-footer';
import { Toast } from '@edx/paragon'; import { Toast } from '@edx/paragon';
import { Header } from '../course-header'; import { LearningHeader as Header } from '@edx/frontend-component-header';
import { getAccessDeniedRedirectUrl } from '../shared/access';
import PageLoading from '../generic/PageLoading'; import PageLoading from '../generic/PageLoading';
import { getAccessDeniedRedirectUrl } from '../shared/access';
import { useModel } from '../generic/model-store'; import { useModel } from '../generic/model-store';
import genericMessages from '../generic/messages'; import genericMessages from '../generic/messages';