Merge remote-tracking branch 'origin/develop' into release/2023-02-27

This commit is contained in:
corihudson 2023-02-27 19:11:43 +00:00
commit ee015c12d3
93 changed files with 1877 additions and 659 deletions

1346
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -59,9 +59,11 @@
"scratch-storage": "2.0.2" "scratch-storage": "2.0.2"
}, },
"devDependencies": { "devDependencies": {
"@formatjs/intl-locale": "2.4.34", "@formatjs/intl-datetimeformat": "6.4.3",
"@formatjs/intl-pluralrules": "4.1.0", "@formatjs/intl-locale": "3.0.11",
"@formatjs/intl-relativetimeformat": "8.1.8", "@formatjs/intl-numberformat": "8.3.3",
"@formatjs/intl-pluralrules": "5.1.8",
"@formatjs/intl-relativetimeformat": "11.1.8",
"async": "3.2.2", "async": "3.2.2",
"autoprefixer": "10.4.2", "autoprefixer": "10.4.2",
"babel-cli": "6.26.0", "babel-cli": "6.26.0",
@ -118,7 +120,7 @@
"query-string": "5.1.1", "query-string": "5.1.1",
"react": "16.14.0", "react": "16.14.0",
"react-dom": "16.14.0", "react-dom": "16.14.0",
"react-intl": "2.9.0", "react-intl": "5.25.1",
"react-modal": "3.11.1", "react-modal": "3.11.1",
"react-onclickoutside": "6.7.1", "react-onclickoutside": "6.7.1",
"react-plotly.js": "2.4.0", "react-plotly.js": "2.4.0",
@ -134,8 +136,8 @@
"regenerator-runtime": "0.13.9", "regenerator-runtime": "0.13.9",
"sass": "1.49.7", "sass": "1.49.7",
"sass-loader": "10.2.1", "sass-loader": "10.2.1",
"scratch-gui": "1.3.10", "scratch-gui": "1.3.21",
"scratch-l10n": "3.15.20230212032126", "scratch-l10n": "3.15.20230227032200",
"selenium-webdriver": "4.1.0", "selenium-webdriver": "4.1.0",
"slick-carousel": "1.6.0", "slick-carousel": "1.6.0",
"style-loader": "0.12.3", "style-loader": "0.12.3",

View file

@ -1,5 +1,4 @@
const classNames = require('classnames'); const classNames = require('classnames');
const FormattedRelative = require('react-intl').FormattedRelative;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
@ -13,21 +12,12 @@ const CommentText = props => (
className="mod-comment" className="mod-comment"
text={props.comment} text={props.comment}
/> />
{typeof props.datetimeCreated === 'undefined' ? [] : [
<p
className="comment-text-timestamp"
key="comment-text-timestamp"
>
<FormattedRelative value={new Date(props.datetimeCreated)} />
</p>
]}
</div> </div>
); );
CommentText.propTypes = { CommentText.propTypes = {
className: PropTypes.string, className: PropTypes.string,
comment: PropTypes.string.isRequired, comment: PropTypes.string.isRequired
datetimeCreated: PropTypes.string
}; };
module.exports = CommentText; module.exports = CommentText;

View file

@ -35,9 +35,3 @@
margin: 0; margin: 0;
overflow: hidden; overflow: hidden;
} }
.comment-text-timestamp {
margin: 1rem 0 0;
color: $ui-dark-gray;
font-size: .8rem;
}

View file

@ -1,15 +1,14 @@
const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react'); const React = require('react');
const FlexRow = require('../../../flex-row/flex-row.jsx'); const FlexRow = require('../../../flex-row/flex-row.jsx');
const FooterBox = require('../../container/footer.jsx'); const FooterBox = require('../../container/footer.jsx');
const LanguageChooser = require('../../../languagechooser/languagechooser.jsx'); const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
const {getLocale} = require('../../../../lib/locales.js');
require('../footer.scss'); require('../footer.scss');
const ConferenceFooter = props => ( const ConferenceFooter = () => (
<FooterBox> <FooterBox>
<FlexRow className="scratch-links"> <FlexRow className="scratch-links">
<div className="family"> <div className="family">
@ -86,12 +85,8 @@ const ConferenceFooter = props => (
</div> </div>
</div> </div>
</FlexRow> </FlexRow>
<LanguageChooser locale={props.intl.locale} /> <LanguageChooser locale={getLocale()} />
</FooterBox> </FooterBox>
); );
ConferenceFooter.propTypes = { module.exports = ConferenceFooter;
intl: intlShape
};
module.exports = injectIntl(ConferenceFooter);

View file

@ -1,15 +1,14 @@
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const React = require('react'); const React = require('react');
const FlexRow = require('../../../flex-row/flex-row.jsx'); const FlexRow = require('../../../flex-row/flex-row.jsx');
const FooterBox = require('../../container/footer.jsx'); const FooterBox = require('../../container/footer.jsx');
const LanguageChooser = require('../../../languagechooser/languagechooser.jsx'); const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
const {getLocale} = require('../../../../lib/locales.js');
require('../footer.scss'); require('../footer.scss');
const ConferenceFooter = props => ( const ConferenceFooter = () => (
<FooterBox> <FooterBox>
<div className="collaborators"> <div className="collaborators">
<h4>Sponsors</h4> <h4>Sponsors</h4>
@ -213,12 +212,8 @@ const ConferenceFooter = props => (
</div> </div>
</div> </div>
</FlexRow> </FlexRow>
<LanguageChooser locale={props.intl.locale} /> <LanguageChooser locale={getLocale()} />
</FooterBox> </FooterBox>
); );
ConferenceFooter.propTypes = { module.exports = ConferenceFooter;
intl: intlShape
};
module.exports = injectIntl(ConferenceFooter);

View file

@ -1,15 +1,14 @@
const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react'); const React = require('react');
const FlexRow = require('../../../flex-row/flex-row.jsx'); const FlexRow = require('../../../flex-row/flex-row.jsx');
const FooterBox = require('../../container/footer.jsx'); const FooterBox = require('../../container/footer.jsx');
const LanguageChooser = require('../../../languagechooser/languagechooser.jsx'); const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
const {getLocale} = require('../../../../lib/locales');
require('../footer.scss'); require('../footer.scss');
const ConferenceFooter = props => ( const ConferenceFooter = () => (
<FooterBox> <FooterBox>
<FlexRow className="scratch-links"> <FlexRow className="scratch-links">
<div className="family"> <div className="family">
@ -107,12 +106,8 @@ const ConferenceFooter = props => (
</div> </div>
</div> </div>
</FlexRow> </FlexRow>
<LanguageChooser locale={props.intl.locale} /> <LanguageChooser locale={getLocale()} />
</FooterBox> </FooterBox>
); );
ConferenceFooter.propTypes = { module.exports = ConferenceFooter;
intl: intlShape
};
module.exports = injectIntl(ConferenceFooter);

View file

@ -1,13 +1,11 @@
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const React = require('react'); const React = require('react');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const FlexRow = require('../../../flex-row/flex-row.jsx'); const FlexRow = require('../../../flex-row/flex-row.jsx');
const FooterBox = require('../../container/footer.jsx'); const FooterBox = require('../../container/footer.jsx');
const LanguageChooser = require('../../../languagechooser/languagechooser.jsx'); const LanguageChooser = require('../../../languagechooser/languagechooser.jsx');
const {getLocale} = require('../../../../lib/locales.js');
require('../footer.scss'); require('../footer.scss');
@ -146,7 +144,7 @@ const ConferenceFooter = props => (
</div> </div>
</div> </div>
</FlexRow> </FlexRow>
<LanguageChooser locale={props.intl.locale} /> <LanguageChooser locale={getLocale()} />
<div className="organized-by-message"> <div className="organized-by-message">
<FormattedMessage id={props.organizedByMsgId} /> <FormattedMessage id={props.organizedByMsgId} />
</div> </div>
@ -154,8 +152,7 @@ const ConferenceFooter = props => (
); );
ConferenceFooter.propTypes = { ConferenceFooter.propTypes = {
intl: intlShape,
organizedByMsgId: PropTypes.string organizedByMsgId: PropTypes.string
}; };
module.exports = injectIntl(ConferenceFooter); module.exports = ConferenceFooter;

View file

@ -1,6 +1,5 @@
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const MediaQuery = require('react-responsive').default; const MediaQuery = require('react-responsive').default;
const connect = require('react-redux').connect; const connect = require('react-redux').connect;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
@ -10,6 +9,8 @@ const FooterBox = require('../container/footer.jsx');
const LanguageChooser = require('../../languagechooser/languagechooser.jsx'); const LanguageChooser = require('../../languagechooser/languagechooser.jsx');
const frameless = require('../../../lib/frameless'); const frameless = require('../../../lib/frameless');
const intlShape = require('../../../lib/intl-shape');
const {getLocale} = require('../../../lib/locales.js');
const getScratchWikiLink = require('../../../lib/scratch-wiki'); const getScratchWikiLink = require('../../../lib/scratch-wiki');
require('./footer.scss'); require('./footer.scss');
@ -213,12 +214,12 @@ const Footer = props => (
</dl> </dl>
</div> </div>
</MediaQuery> </MediaQuery>
<LanguageChooser locale={props.intl.locale} /> <LanguageChooser locale={getLocale()} />
</FooterBox> </FooterBox>
); );
Footer.propTypes = { Footer.propTypes = {
intl: intlShape.isRequired, intl: intlShape.isRequired, // eslint-disable-line react/no-unused-prop-types
scratchWikiLink: PropTypes.string scratchWikiLink: PropTypes.string
}; };

View file

@ -8,10 +8,10 @@ const ReactPhoneInput = require('react-telephone-input/lib/withStyles').default;
const Row = require('formsy-react-components').Row; const Row = require('formsy-react-components').Row;
const Help = require('formsy-react-components/release/components/help').default; const Help = require('formsy-react-components/release/components/help').default;
const ErrorMessages = require('formsy-react-components/release/components/error-messages').default; const ErrorMessages = require('formsy-react-components/release/components/error-messages').default;
const intl = require('react-intl');
const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC; const defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
const inputHOC = require('./input-hoc.jsx'); const inputHOC = require('./input-hoc.jsx');
const intl = require('../../lib/intl.jsx');
const validationHOCFactory = require('./validations.jsx').validationHOCFactory; const validationHOCFactory = require('./validations.jsx').validationHOCFactory;
require('./row.scss'); require('./row.scss');

View file

@ -1,5 +1,5 @@
const defaults = require('lodash.defaultsdeep'); const defaults = require('lodash.defaultsdeep');
const intl = require('../../lib/intl.jsx'); const intl = require('react-intl');
const omit = require('lodash.omit'); const omit = require('lodash.omit');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');

View file

@ -1,13 +1,14 @@
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const bindAll = require('lodash.bindall'); const bindAll = require('lodash.bindall');
const connect = require('react-redux').connect; const connect = require('react-redux').connect;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const intlShape = require('../../lib/intl-shape');
const Button = require('../forms/button.jsx'); const Button = require('../forms/button.jsx');
const Spinner = require('../spinner/spinner.jsx'); const Spinner = require('../spinner/spinner.jsx');
require('./helpwidget.scss'); require('./helpwidget.scss');
// map Scratch locale to supported Freshdesk locale // map Scratch locale to supported Freshdesk locale

View file

@ -3,9 +3,10 @@ const classNames = require('classnames');
const React = require('react'); const React = require('react');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
import {Formik} from 'formik'; import {Formik} from 'formik';
const {injectIntl, intlShape} = require('react-intl'); const {injectIntl} = require('react-intl');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const intlShape = require('../../lib/intl-shape');
const FormikSelect = require('../../components/formik-forms/formik-select.jsx'); const FormikSelect = require('../../components/formik-forms/formik-select.jsx');
const JoinFlowStep = require('./join-flow-step.jsx'); const JoinFlowStep = require('./join-flow-step.jsx');
const InfoButton = require('../info-button/info-button.jsx'); const InfoButton = require('../info-button/info-button.jsx');

View file

@ -3,9 +3,10 @@ const classNames = require('classnames');
const React = require('react'); const React = require('react');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
import {Formik} from 'formik'; import {Formik} from 'formik';
const {injectIntl, intlShape} = require('react-intl'); const {injectIntl} = require('react-intl');
const countryData = require('../../lib/country-data'); const countryData = require('../../lib/country-data');
const intlShape = require('../../lib/intl-shape');
const FormikSelect = require('../../components/formik-forms/formik-select.jsx'); const FormikSelect = require('../../components/formik-forms/formik-select.jsx');
const JoinFlowStep = require('./join-flow-step.jsx'); const JoinFlowStep = require('./join-flow-step.jsx');
const FormikCheckbox = require('../../components/formik-forms/formik-checkbox.jsx'); const FormikCheckbox = require('../../components/formik-forms/formik-checkbox.jsx');

View file

@ -3,14 +3,16 @@ const classNames = require('classnames');
const React = require('react'); const React = require('react');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
import {Formik} from 'formik'; import {Formik} from 'formik';
const {injectIntl, intlShape} = require('react-intl'); const {injectIntl} = require('react-intl');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const intlShape = require('../../lib/intl-shape');
const validate = require('../../lib/validate'); const validate = require('../../lib/validate');
const JoinFlowStep = require('./join-flow-step.jsx'); const JoinFlowStep = require('./join-flow-step.jsx');
const FormikInput = require('../../components/formik-forms/formik-input.jsx'); const FormikInput = require('../../components/formik-forms/formik-input.jsx');
const InfoButton = require('../info-button/info-button.jsx'); const InfoButton = require('../info-button/info-button.jsx');
const Captcha = require('../../components/captcha/captcha.jsx'); const Captcha = require('../../components/captcha/captcha.jsx');
require('./join-flow-steps.scss'); require('./join-flow-steps.scss');
class EmailStep extends React.Component { class EmailStep extends React.Component {

View file

@ -3,9 +3,10 @@ const classNames = require('classnames');
const React = require('react'); const React = require('react');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
import {Formik} from 'formik'; import {Formik} from 'formik';
const {injectIntl, intlShape} = require('react-intl'); const {injectIntl} = require('react-intl');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const intlShape = require('../../lib/intl-shape');
const FormikRadioButton = require('../../components/formik-forms/formik-radio-button.jsx'); const FormikRadioButton = require('../../components/formik-forms/formik-radio-button.jsx');
const JoinFlowStep = require('./join-flow-step.jsx'); const JoinFlowStep = require('./join-flow-step.jsx');
const InfoButton = require('../info-button/info-button.jsx'); const InfoButton = require('../info-button/info-button.jsx');

View file

@ -3,10 +3,10 @@ const connect = require('react-redux').connect;
const defaults = require('lodash.defaultsdeep'); const defaults = require('lodash.defaultsdeep');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const injectIntl = require('react-intl').injectIntl;
const api = require('../../lib/api'); const api = require('../../lib/api');
const injectIntl = require('../../lib/intl.jsx').injectIntl; const intlShape = require('../../lib/intl-shape');
const intlShape = require('../../lib/intl.jsx').intlShape;
const sessionActions = require('../../redux/session.js'); const sessionActions = require('../../redux/session.js');
const validate = require('../../lib/validate'); const validate = require('../../lib/validate');

View file

@ -3,7 +3,7 @@ const React = require('react');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intl = require('../../lib/intl.jsx'); const intlShape = require('../../lib/intl-shape');
const Spinner = require('../../components/spinner/spinner.jsx'); const Spinner = require('../../components/spinner/spinner.jsx');
const ModalTitle = require('../modal/base/modal-title.jsx'); const ModalTitle = require('../modal/base/modal-title.jsx');
@ -29,7 +29,7 @@ const NextStepButton = props => (
NextStepButton.propTypes = { NextStepButton.propTypes = {
content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
intl: intl.intlShape, intl: intlShape,
waiting: PropTypes.bool waiting: PropTypes.bool
}; };

View file

@ -2,8 +2,9 @@ const bindAll = require('lodash.bindall');
const React = require('react'); const React = require('react');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const {injectIntl, intlShape} = require('react-intl'); const {injectIntl} = require('react-intl');
const intlShape = require('../../lib/intl-shape');
const JoinFlowStep = require('./join-flow-step.jsx'); const JoinFlowStep = require('./join-flow-step.jsx');
require('./join-flow-steps.scss'); require('./join-flow-steps.scss');

View file

@ -3,8 +3,9 @@ const classNames = require('classnames');
const React = require('react'); const React = require('react');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
import {Formik} from 'formik'; import {Formik} from 'formik';
const {injectIntl, intlShape} = require('react-intl'); const {injectIntl} = require('react-intl');
const intlShape = require('../../lib/intl-shape');
const validate = require('../../lib/validate'); const validate = require('../../lib/validate');
const FormikInput = require('../../components/formik-forms/formik-input.jsx'); const FormikInput = require('../../components/formik-forms/formik-input.jsx');
const FormikCheckbox = require('../../components/formik-forms/formik-checkbox.jsx'); const FormikCheckbox = require('../../components/formik-forms/formik-checkbox.jsx');

View file

@ -3,8 +3,9 @@ const React = require('react');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
import {Formik} from 'formik'; import {Formik} from 'formik';
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const {injectIntl, intlShape} = require('react-intl'); const {injectIntl} = require('react-intl');
const intlShape = require('../../lib/intl-shape');
const JoinFlowStep = require('./join-flow-step.jsx'); const JoinFlowStep = require('./join-flow-step.jsx');
require('./join-flow-steps.scss'); require('./join-flow-steps.scss');

View file

@ -1,10 +1,10 @@
const bindAll = require('lodash.bindall'); const bindAll = require('lodash.bindall');
const classNames = require('classnames'); const classNames = require('classnames');
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const intlShape = require('../../lib/intl-shape');
const jar = require('../../lib/jar.js'); const jar = require('../../lib/jar.js');
const languages = require('scratch-l10n').default; const languages = require('scratch-l10n').default;
const Form = require('../forms/form.jsx'); const Form = require('../forms/form.jsx');

View file

@ -3,8 +3,8 @@ const connect = require('react-redux').connect;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const intlShape = require('../../lib/intl-shape');
const navigationActions = require('../../redux/navigation.js'); const navigationActions = require('../../redux/navigation.js');
const Modal = require('../modal/base/modal.jsx'); const Modal = require('../modal/base/modal.jsx');

View file

@ -2,9 +2,9 @@ const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const Modal = require('../base/modal.jsx'); const Modal = require('../base/modal.jsx');
const intlShape = require('../../../lib/intl-shape');
const Form = require('../../forms/form.jsx'); const Form = require('../../forms/form.jsx');
const Button = require('../../forms/button.jsx'); const Button = require('../../forms/button.jsx');
const Spinner = require('../../spinner/spinner.jsx'); const Spinner = require('../../spinner/spinner.jsx');

View file

@ -2,9 +2,9 @@ const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const Modal = require('../base/modal.jsx'); const Modal = require('../base/modal.jsx');
const intlShape = require('../../../lib/intl-shape');
const Button = require('../../forms/button.jsx'); const Button = require('../../forms/button.jsx');
const FlexRow = require('../../flex-row/flex-row.jsx'); const FlexRow = require('../../flex-row/flex-row.jsx');

View file

@ -2,9 +2,9 @@ const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const Modal = require('../base/modal.jsx'); const Modal = require('../base/modal.jsx');
const intlShape = require('../../../lib/intl-shape');
const Button = require('../../forms/button.jsx'); const Button = require('../../forms/button.jsx');
const FlexRow = require('../../flex-row/flex-row.jsx'); const FlexRow = require('../../flex-row/flex-row.jsx');

View file

@ -3,7 +3,6 @@ const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const Modal = require('../base/modal.jsx'); const Modal = require('../base/modal.jsx');
const ModalInnerContent = require('../base/modal-inner-content.jsx'); const ModalInnerContent = require('../base/modal-inner-content.jsx');
const Button = require('../../forms/button.jsx'); const Button = require('../../forms/button.jsx');
@ -12,9 +11,11 @@ const FlexRow = require('../../flex-row/flex-row.jsx');
const MuteStep = require('./mute-step.jsx'); const MuteStep = require('./mute-step.jsx');
const FeedbackForm = require('./feedback-form.jsx'); const FeedbackForm = require('./feedback-form.jsx');
const classNames = require('classnames'); const classNames = require('classnames');
require('./modal.scss');
const api = require('../../../lib/api'); const api = require('../../../lib/api');
const intlShape = require('../../../lib/intl-shape');
require('./modal.scss');
const steps = { const steps = {
COMMENT_ISSUE: 0, COMMENT_ISSUE: 0,

View file

@ -4,9 +4,9 @@ const React = require('react');
const connect = require('react-redux').connect; const connect = require('react-redux').connect;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const Modal = require('../base/modal.jsx'); const Modal = require('../base/modal.jsx');
const intlShape = require('../../../lib/intl-shape');
const ModalTitle = require('../base/modal-title.jsx'); const ModalTitle = require('../base/modal-title.jsx');
const ModalInnerContent = require('../base/modal-inner-content.jsx'); const ModalInnerContent = require('../base/modal-inner-content.jsx');
const Select = require('../../forms/select.jsx'); const Select = require('../../forms/select.jsx');

View file

@ -1,9 +1,9 @@
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const classNames = require('classnames'); const classNames = require('classnames');
const intlShape = require('../../../lib/intl-shape');
const Modal = require('../base/modal.jsx'); const Modal = require('../base/modal.jsx');
const ModalTitle = require('../base/modal-title.jsx'); const ModalTitle = require('../base/modal-title.jsx');
const ModalInnerContent = require('../base/modal-inner-content.jsx'); const ModalInnerContent = require('../base/modal-inner-content.jsx');

View file

@ -3,10 +3,10 @@ const classNames = require('classnames');
const connect = require('react-redux').connect; const connect = require('react-redux').connect;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const intlShape = require('../../../lib/intl-shape');
const messageCountActions = require('../../../redux/message-count.js'); const messageCountActions = require('../../../redux/message-count.js');
const navigationActions = require('../../../redux/navigation.js'); const navigationActions = require('../../../redux/navigation.js');
const sessionActions = require('../../../redux/session.js'); const sessionActions = require('../../../redux/session.js');

View file

@ -1,11 +1,11 @@
/* eslint-disable react/no-multi-comp */ /* eslint-disable react/no-multi-comp */
const bindAll = require('lodash.bindall'); const bindAll = require('lodash.bindall');
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const intl = require('react-intl');
const intl = require('../../lib/intl.jsx'); const intlShape = require('../../lib/intl-shape');
const Card = require('../../components/card/card.jsx'); const Card = require('../../components/card/card.jsx');
const Checkbox = require('../../components/forms/checkbox.jsx'); const Checkbox = require('../../components/forms/checkbox.jsx');

View file

@ -1,14 +1,13 @@
/* eslint-disable react/no-multi-comp */ /* eslint-disable react/no-multi-comp */
const bindAll = require('lodash.bindall'); const bindAll = require('lodash.bindall');
const injectIntl = require('react-intl').injectIntl; const {injectIntl, FormattedMessage} = require('react-intl');
const intlShape = require('react-intl').intlShape;
const omit = require('lodash.omit'); const omit = require('lodash.omit');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const api = require('../../lib/api'); const api = require('../../lib/api');
const countryData = require('../../lib/country-data'); const countryData = require('../../lib/country-data');
const intl = require('../../lib/intl.jsx'); const intlShape = require('../../lib/intl-shape');
const Avatar = require('../../components/avatar/avatar.jsx'); const Avatar = require('../../components/avatar/avatar.jsx');
const Button = require('../../components/forms/button.jsx'); const Button = require('../../components/forms/button.jsx');
@ -174,7 +173,7 @@ class UsernameStep extends React.Component {
{this.props.title ? ( {this.props.title ? (
this.props.title this.props.title
) : ( ) : (
<intl.FormattedMessage id="registration.usernameStepTitle" /> <FormattedMessage id="registration.usernameStepTitle" />
)} )}
</h2> </h2>
<p className="description"> <p className="description">
@ -182,9 +181,9 @@ class UsernameStep extends React.Component {
this.props.description this.props.description
) : ( ) : (
<span> <span>
<intl.FormattedMessage id="registration.usernameStepDescription" />&nbsp; <FormattedMessage id="registration.usernameStepDescription" />&nbsp;
<b> <b>
<intl.FormattedMessage id="registration.usernameStepRealName" /> <FormattedMessage id="registration.usernameStepRealName" />
</b> </b>
</span> </span>
)} )}
@ -281,7 +280,7 @@ class UsernameStep extends React.Component {
/> />
<GeneralError name="all" /> <GeneralError name="all" />
<NextStepButton <NextStepButton
text={<intl.FormattedMessage id="registration.nextStep" />} text={<FormattedMessage id="registration.nextStep" />}
waiting={this.props.waiting || this.state.waiting} waiting={this.props.waiting || this.state.waiting}
/> />
</Form> </Form>
@ -298,7 +297,7 @@ class UsernameStep extends React.Component {
UsernameStep.propTypes = { UsernameStep.propTypes = {
activeStep: PropTypes.number, activeStep: PropTypes.number,
description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
intl: intl.intlShape, intl: intlShape,
onNextStep: PropTypes.func, onNextStep: PropTypes.func,
showPassword: PropTypes.bool, showPassword: PropTypes.bool,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
@ -339,7 +338,7 @@ class ChoosePasswordStep extends React.Component {
{this.props.intl.formatMessage({id: 'registration.choosePasswordStepTitle'})} {this.props.intl.formatMessage({id: 'registration.choosePasswordStepTitle'})}
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="registration.choosePasswordStepDescription" /> <FormattedMessage id="registration.choosePasswordStepDescription" />
<Tooltip <Tooltip
tipContent={ tipContent={
this.props.intl.formatMessage({id: 'registration.choosePasswordStepTooltip'}) this.props.intl.formatMessage({id: 'registration.choosePasswordStepTooltip'})
@ -384,7 +383,7 @@ class ChoosePasswordStep extends React.Component {
onChange={this.handleChangeShowPassword} onChange={this.handleChangeShowPassword}
/> />
<NextStepButton <NextStepButton
text={<intl.FormattedMessage id="registration.nextStep" />} text={<FormattedMessage id="registration.nextStep" />}
waiting={this.props.waiting || this.state.waiting} waiting={this.props.waiting || this.state.waiting}
/> />
</Form> </Form>
@ -501,12 +500,12 @@ class DemographicsStep extends React.Component {
return ( return (
<Slide className="registration-step demographics-step"> <Slide className="registration-step demographics-step">
<h2> <h2>
<intl.FormattedMessage id="registration.personalStepTitle" /> <FormattedMessage id="registration.personalStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
{this.props.description ? {this.props.description ?
this.props.description : this.props.description :
<intl.FormattedMessage id="registration.personalStepDescription" /> <FormattedMessage id="registration.personalStepDescription" />
} }
<Tooltip <Tooltip
tipContent={ tipContent={
@ -589,7 +588,7 @@ class DemographicsStep extends React.Component {
valueLabel="I'm a robot!" valueLabel="I'm a robot!"
/> />
<NextStepButton <NextStepButton
text={<intl.FormattedMessage id="registration.nextStep" />} text={<FormattedMessage id="registration.nextStep" />}
waiting={this.props.waiting} waiting={this.props.waiting}
/> />
</Form> </Form>
@ -629,10 +628,10 @@ const IntlDemographicsStep = injectIntl(DemographicsStep);
const NameStep = props => ( const NameStep = props => (
<Slide className="registration-step name-step"> <Slide className="registration-step name-step">
<h2> <h2>
<intl.FormattedHTMLMessage id="teacherRegistration.nameStepTitle" /> <FormattedMessage id="teacherRegistration.nameStepTitleNew" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.nameStepDescription" /> <FormattedMessage id="teacherRegistration.nameStepDescription" />
<Tooltip <Tooltip
tipContent={ tipContent={
props.intl.formatMessage({id: 'registration.nameStepTooltip'}) props.intl.formatMessage({id: 'registration.nameStepTooltip'})
@ -675,7 +674,7 @@ const NameStep = props => (
}} }}
/> />
<NextStepButton <NextStepButton
text={<intl.FormattedMessage id="registration.nextStep" />} text={<FormattedMessage id="registration.nextStep" />}
waiting={props.waiting} waiting={props.waiting}
/> />
</Form> </Form>
@ -748,10 +747,10 @@ class OrganizationStep extends React.Component {
return ( return (
<Slide className="registration-step organization-step"> <Slide className="registration-step organization-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.organization" /> <FormattedMessage id="teacherRegistration.organization" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.privacyDescription" /> <FormattedMessage id="teacherRegistration.privacyDescription" />
<Tooltip <Tooltip
tipContent={ tipContent={
this.props.intl.formatMessage({id: 'registration.nameStepTooltip'}) this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
@ -792,9 +791,9 @@ class OrganizationStep extends React.Component {
}} }}
/> />
<div className="organization-type"> <div className="organization-type">
<b className="row-label"><intl.FormattedMessage id="teacherRegistration.orgType" /></b> <b className="row-label"><FormattedMessage id="teacherRegistration.orgType" /></b>
<p className="help-text"> <p className="help-text">
<intl.FormattedMessage id="teacherRegistration.checkAll" /> <FormattedMessage id="teacherRegistration.checkAll" />
</p> </p>
<CheckboxGroup <CheckboxGroup
required required
@ -833,9 +832,9 @@ class OrganizationStep extends React.Component {
/> />
</div> </div>
<div className="url-input"> <div className="url-input">
<b className="row-label"><intl.FormattedMessage id="general.website" /></b> <b className="row-label"><FormattedMessage id="general.website" /></b>
<p className="help-text"> <p className="help-text">
<intl.FormattedMessage id="teacherRegistration.notRequired" /> <FormattedMessage id="teacherRegistration.notRequired" />
</p> </p>
<Input <Input
name="organization.url" name="organization.url"
@ -853,7 +852,7 @@ class OrganizationStep extends React.Component {
/> />
</div> </div>
<NextStepButton <NextStepButton
text={<intl.FormattedMessage id="registration.nextStep" />} text={<FormattedMessage id="registration.nextStep" />}
waiting={this.props.waiting} waiting={this.props.waiting}
/> />
</Form> </Form>
@ -906,10 +905,10 @@ class AddressStep extends React.Component {
return ( return (
<Slide className="registration-step address-step"> <Slide className="registration-step address-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.addressStepTitle" /> <FormattedMessage id="teacherRegistration.addressStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.privacyDescription" /> <FormattedMessage id="teacherRegistration.privacyDescription" />
<Tooltip <Tooltip
tipContent={ tipContent={
this.props.intl.formatMessage({id: 'registration.nameStepTooltip'}) this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
@ -988,10 +987,10 @@ class AddressStep extends React.Component {
/> : [] /> : []
} }
<b className="row-label"> <b className="row-label">
<intl.FormattedMessage id="teacherRegistration.zipCode" /> <FormattedMessage id="teacherRegistration.zipCode" />
</b> </b>
{this.state.countryChoice === 'us' ? [] : <p className="help-text"> {this.state.countryChoice === 'us' ? [] : <p className="help-text">
<intl.FormattedMessage id="teacherRegistration.notRequired" /> <FormattedMessage id="teacherRegistration.notRequired" />
</p>} </p>}
<Input <Input
name="address.zip" name="address.zip"
@ -1008,7 +1007,7 @@ class AddressStep extends React.Component {
/> />
<GeneralError name="all" /> <GeneralError name="all" />
<NextStepButton <NextStepButton
text={<intl.FormattedMessage id="registration.nextStep" />} text={<FormattedMessage id="registration.nextStep" />}
waiting={this.props.waiting || this.state.waiting} waiting={this.props.waiting || this.state.waiting}
/> />
</Form> </Form>
@ -1062,10 +1061,10 @@ class UseScratchStep extends React.Component {
return ( return (
<Slide className="registration-step usescratch-step"> <Slide className="registration-step usescratch-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.useScratchStepTitle" /> <FormattedMessage id="teacherRegistration.useScratchStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.useScratchStepDescription" /> <FormattedMessage id="teacherRegistration.useScratchStepDescription" />
<Tooltip <Tooltip
tipContent={ tipContent={
this.props.intl.formatMessage({id: 'registration.nameStepTooltip'}) this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
@ -1098,7 +1097,7 @@ class UseScratchStep extends React.Component {
maxCharacters={this.props.maxCharacters} maxCharacters={this.props.maxCharacters}
/> />
<NextStepButton <NextStepButton
text={<intl.FormattedMessage id="registration.nextStep" />} text={<FormattedMessage id="registration.nextStep" />}
waiting={this.props.waiting} waiting={this.props.waiting}
/> />
</Form> </Form>
@ -1192,10 +1191,10 @@ class EmailStep extends React.Component {
return ( return (
<Slide className="registration-step email-step"> <Slide className="registration-step email-step">
<h2> <h2>
<intl.FormattedMessage id="teacherRegistration.emailStepTitle" /> <FormattedMessage id="teacherRegistration.emailStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="teacherRegistration.emailStepDescription" /> <FormattedMessage id="teacherRegistration.emailStepDescription" />
<Tooltip <Tooltip
tipContent={ tipContent={
this.props.intl.formatMessage({id: 'registration.nameStepTooltip'}) this.props.intl.formatMessage({id: 'registration.nameStepTooltip'})
@ -1237,7 +1236,7 @@ class EmailStep extends React.Component {
/> />
<GeneralError name="all" /> <GeneralError name="all" />
<NextStepButton <NextStepButton
text={<intl.FormattedMessage id="registration.nextStep" />} text={<FormattedMessage id="registration.nextStep" />}
waiting={this.props.waiting} waiting={this.props.waiting}
/> />
</Form> </Form>
@ -1272,6 +1271,7 @@ EmailStep.defaultProps = {
const IntlEmailStep = injectIntl(EmailStep); const IntlEmailStep = injectIntl(EmailStep);
const EducatorResourcesLink = chunks => <a href="/educators#resources">{chunks}</a>;
/* /*
* TEACHER APPROVAL STEP * TEACHER APPROVAL STEP
@ -1279,17 +1279,17 @@ const IntlEmailStep = injectIntl(EmailStep);
const TeacherApprovalStep = props => ( const TeacherApprovalStep = props => (
<Slide className="registration-step last-step"> <Slide className="registration-step last-step">
<h2> <h2>
<intl.FormattedMessage id="registration.lastStepTitle" /> <FormattedMessage id="registration.lastStepTitle" />
</h2> </h2>
<p className="description"> <p className="description">
<intl.FormattedMessage id="registration.lastStepDescription" /> <FormattedMessage id="registration.lastStepDescription" />
</p> </p>
{props.confirmed || !props.email ? {props.confirmed || !props.email ?
[] : ( [] : (
<Card className="confirm"> <Card className="confirm">
<h4><intl.FormattedMessage id="registration.confirmYourEmail" /></h4> <h4><FormattedMessage id="registration.confirmYourEmail" /></h4>
<p> <p>
<intl.FormattedMessage id="registration.confirmYourEmailDescription" /><br /> <FormattedMessage id="registration.confirmYourEmailDescription" /><br />
<strong>{props.email}</strong> <strong>{props.email}</strong>
</p> </p>
</Card> </Card>
@ -1297,16 +1297,19 @@ const TeacherApprovalStep = props => (
} }
{props.invited ? {props.invited ?
<Card className="wait"> <Card className="wait">
<h4><intl.FormattedMessage id="registration.waitForApproval" /></h4> <h4><FormattedMessage id="registration.waitForApproval" /></h4>
<p> <p>
<intl.FormattedMessage id="registration.waitForApprovalDescription" /> <FormattedMessage id="registration.waitForApprovalDescription" />
</p> </p>
</Card> : [] </Card> : []
} }
<Card className="resources"> <Card className="resources">
<h4><intl.FormattedMessage id="registration.checkOutResources" /></h4> <h4><FormattedMessage id="registration.checkOutResources" /></h4>
<p> <p>
<intl.FormattedHTMLMessage id="registration.checkOutResourcesDescription" /> <FormattedMessage
id="registration.checkOutResourcesDescriptionHTML"
values={{a: EducatorResourcesLink}}
/>
</p> </p>
</Card> </Card>
</Slide> </Slide>

View file

@ -0,0 +1,39 @@
const React = require('react');
const {useEffect, useState} = React;
const PropTypes = require('prop-types');
const {FormattedRelativeTime} = require('react-intl');
const {selectUnit} = require('../../lib/select-unit');
const RelativeTime = ({value}) => {
const [selectedUnit, setSelectedUnit] = useState(selectUnit(value));
useEffect(() => {
// It is unlikely that users will leave this running for days. Don't
// auto-update beyond hours.
if (!['second', 'minute', 'hour'].includes(selectedUnit.unit)) return;
const timerId = setInterval(() => {
const nextSelectedUnit = selectUnit(value);
if (selectedUnit.value !== nextSelectedUnit.value ||
selectUnit.unit !== nextSelectedUnit.unit) {
setSelectedUnit(nextSelectedUnit);
}
}, 10000);
return () => clearTimeout(timerId);
}, [value, selectedUnit]);
return (
<FormattedRelativeTime
value={selectedUnit.value}
unit={selectedUnit.unit}
numeric="auto"
/>
);
};
RelativeTime.propTypes = {
value: PropTypes.instanceOf(Date)
};
module.exports = RelativeTime;

View file

@ -1,9 +1,9 @@
const classNames = require('classnames'); const classNames = require('classnames');
const FormattedRelative = require('react-intl').FormattedRelative;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const FlexRow = require('../flex-row/flex-row.jsx'); const FlexRow = require('../flex-row/flex-row.jsx');
const RelativeTime = require('../relative-time/relative-time.jsx');
require('./social-message.scss'); require('./social-message.scss');
@ -24,7 +24,7 @@ const SocialMessage = props => (
</div> </div>
</div> </div>
<span className="social-message-date"> <span className="social-message-date">
<FormattedRelative value={new Date(props.datetime)} /> <RelativeTime value={new Date(props.datetime)} />
</span> </span>
</FlexRow> </FlexRow>
</props.as> </props.as>

View file

@ -1,13 +1,12 @@
import 'regenerator-runtime/runtime'; // Needed for async/await import 'regenerator-runtime/runtime'; // Needed for async/await
const jar = require('./lib/jar'); const jar = require('./lib/jar');
import intlPolyfill from './lib/intl-polyfill';
/** /**
* ----------------------------------------------------------------------------- * -----------------------------------------------------------------------------
* L10N * L10N
* ----------------------------------------------------------------------------- * -----------------------------------------------------------------------------
*/ */
(async () => { (() => {
/* /*
* Bind locale code from cookie if available. Uses navigator language API as a fallback. * Bind locale code from cookie if available. Uses navigator language API as a fallback.
* *
@ -37,7 +36,6 @@ import intlPolyfill from './lib/intl-polyfill';
window._locale = updateLocale(); window._locale = updateLocale();
document.documentElement.lang = window._locale; document.documentElement.lang = window._locale;
await intlPolyfill(window._locale);
})(); })();
/** /**

View file

@ -166,6 +166,7 @@
"registration.cantCreateAccount": "Scratch could not create your account.", "registration.cantCreateAccount": "Scratch could not create your account.",
"registration.checkOutResources": "Get Started with Resources", "registration.checkOutResources": "Get Started with Resources",
"registration.checkOutResourcesDescription": "Explore materials for educators and facilitators written by the Scratch Team, including <a href='/educators#resources'>tips, tutorials, and guides</a>.", "registration.checkOutResourcesDescription": "Explore materials for educators and facilitators written by the Scratch Team, including <a href='/educators#resources'>tips, tutorials, and guides</a>.",
"registration.checkOutResourcesDescriptionHTML": "Explore materials for educators and facilitators written by the Scratch Team, including <a>tips, tutorials, and guides</a>.",
"registration.choosePasswordStepDescription": "Type in a new password for your account. You will use this password the next time you log into Scratch.", "registration.choosePasswordStepDescription": "Type in a new password for your account. You will use this password the next time you log into Scratch.",
"registration.choosePasswordStepTitle": "Create a password", "registration.choosePasswordStepTitle": "Create a password",
"registration.choosePasswordStepTooltip": "Don't use your name or anything that's easy for someone else to guess.", "registration.choosePasswordStepTooltip": "Don't use your name or anything that's easy for someone else to guess.",

View file

@ -1,9 +1,9 @@
// this file should only be `required` in the format-time // This file polyfills the required Intl objects and locale data.
// when Intl.RelativeTimeFormat is not available (Safari < 14), but // The react-intl library uses PluralRules, RelativeTimeFormat, NumberFormat,
// we're not currently able to do the code splitting in www, and it // and DateTimeFormat. Even if browsers support these objects, it is possible
// is always included. To reduce the amount of data that's loaded limit // that the browser does not support the specific locale.
// the number of languages loaded to just the top few that are still using // There are a small number of Scratch locales that do not have polyfill locale
// safari <14. These seven account for most uses. // data available. See /src/lib/locales.js for how they are handled.
// relativetimeformat depends on locale which also needs to be polyfilled in // relativetimeformat depends on locale which also needs to be polyfilled in
// safari <14 // safari <14
// The plural rules is required for safari 12. // The plural rules is required for safari 12.
@ -11,6 +11,8 @@ import 'regenerator-runtime/runtime'; // Needed for async/await
import {shouldPolyfill as shouldPolyfillLocale} from '@formatjs/intl-locale/should-polyfill'; import {shouldPolyfill as shouldPolyfillLocale} from '@formatjs/intl-locale/should-polyfill';
import {shouldPolyfill as shouldPolyfillRelativeTimeFormat} from '@formatjs/intl-relativetimeformat/should-polyfill'; import {shouldPolyfill as shouldPolyfillRelativeTimeFormat} from '@formatjs/intl-relativetimeformat/should-polyfill';
import {shouldPolyfill as shouldPolyfillPluralRules} from '@formatjs/intl-pluralrules/should-polyfill'; import {shouldPolyfill as shouldPolyfillPluralRules} from '@formatjs/intl-pluralrules/should-polyfill';
import {shouldPolyfill as shouldPolyfillNumberFormat} from '@formatjs/intl-numberformat/should-polyfill';
import {shouldPolyfill as shouldPolyfillDateTimeFormat} from '@formatjs/intl-datetimeformat/should-polyfill';
/** /**
* polyfill all the parts needed from intl * polyfill all the parts needed from intl
* @param {string} locale currently selected locale * @param {string} locale currently selected locale
@ -19,293 +21,458 @@ import {shouldPolyfill as shouldPolyfillPluralRules} from '@formatjs/intl-plural
const intlPolyfill = async function (locale) { const intlPolyfill = async function (locale) {
if (!(shouldPolyfillLocale() || if (!(shouldPolyfillLocale() ||
shouldPolyfillPluralRules(locale) || shouldPolyfillPluralRules(locale) ||
shouldPolyfillRelativeTimeFormat(locale))) { shouldPolyfillRelativeTimeFormat(locale) ||
shouldPolyfillNumberFormat(locale) ||
shouldPolyfillDateTimeFormat(locale))) {
return; return;
} }
if (shouldPolyfillRelativeTimeFormat(locale)) { if (shouldPolyfillLocale()) {
await import('@formatjs/intl-relativetimeformat/polyfill'); await import('@formatjs/intl-locale/polyfill-force');
} }
if (shouldPolyfillPluralRules(locale)) { if (shouldPolyfillPluralRules(locale)) {
await import('@formatjs/intl-pluralrules/polyfill'); await import('@formatjs/intl-pluralrules/polyfill-force');
} }
if (shouldPolyfillLocale(locale)) { if (shouldPolyfillRelativeTimeFormat(locale)) {
await import('@formatjs/intl-locale/polyfill'); await import('@formatjs/intl-relativetimeformat/polyfill-force');
}
if (shouldPolyfillNumberFormat(locale)) {
await import('@formatjs/intl-numberformat/polyfill-force');
}
if (shouldPolyfillDateTimeFormat(locale)) {
await import('@formatjs/intl-datetimeformat/polyfill-force');
} }
switch (locale.toLowerCase().split('-')[0]) { switch (locale.toLowerCase().split('-')[0]) {
case 'af': case 'af':
await import('@formatjs/intl-relativetimeformat/locale-data/af');
await import('@formatjs/intl-pluralrules/locale-data/af'); await import('@formatjs/intl-pluralrules/locale-data/af');
await import('@formatjs/intl-relativetimeformat/locale-data/af');
await import('@formatjs/intl-numberformat/locale-data/af');
await import('@formatjs/intl-datetimeformat/locale-data/af');
break; break;
case 'ar': case 'ar':
await import('@formatjs/intl-relativetimeformat/locale-data/ar');
await import('@formatjs/intl-pluralrules/locale-data/ar'); await import('@formatjs/intl-pluralrules/locale-data/ar');
await import('@formatjs/intl-relativetimeformat/locale-data/ar');
await import('@formatjs/intl-numberformat/locale-data/ar');
await import('@formatjs/intl-datetimeformat/locale-data/ar');
break; break;
case 'am': case 'am':
await import('@formatjs/intl-relativetimeformat/locale-data/am');
await import('@formatjs/intl-pluralrules/locale-data/am'); await import('@formatjs/intl-pluralrules/locale-data/am');
await import('@formatjs/intl-relativetimeformat/locale-data/am');
await import('@formatjs/intl-numberformat/locale-data/am');
await import('@formatjs/intl-datetimeformat/locale-data/am');
break; break;
case 'an': case 'ast':
await import('@formatjs/intl-relativetimeformat/locale-data/en'); await import('@formatjs/intl-pluralrules/locale-data/ast');
await import('@formatjs/intl-pluralrules/locale-data/an'); await import('@formatjs/intl-relativetimeformat/locale-data/ast');
await import('@formatjs/intl-numberformat/locale-data/ast');
await import('@formatjs/intl-datetimeformat/locale-data/ast');
break; break;
case 'az': case 'az':
await import('@formatjs/intl-relativetimeformat/locale-data/az');
await import('@formatjs/intl-pluralrules/locale-data/az'); await import('@formatjs/intl-pluralrules/locale-data/az');
await import('@formatjs/intl-relativetimeformat/locale-data/az');
await import('@formatjs/intl-numberformat/locale-data/az');
await import('@formatjs/intl-datetimeformat/locale-data/az');
break; break;
case 'id': case 'id':
await import('@formatjs/intl-relativetimeformat/locale-data/id');
await import('@formatjs/intl-pluralrules/locale-data/id'); await import('@formatjs/intl-pluralrules/locale-data/id');
await import('@formatjs/intl-relativetimeformat/locale-data/id');
await import('@formatjs/intl-numberformat/locale-data/id');
await import('@formatjs/intl-datetimeformat/locale-data/id');
break; break;
case 'bn': case 'bn':
await import('@formatjs/intl-relativetimeformat/locale-data/bn');
await import('@formatjs/intl-pluralrules/locale-data/bn'); await import('@formatjs/intl-pluralrules/locale-data/bn');
await import('@formatjs/intl-relativetimeformat/locale-data/bn');
await import('@formatjs/intl-numberformat/locale-data/bn');
await import('@formatjs/intl-datetimeformat/locale-data/bn');
break; break;
case 'be': case 'be':
await import('@formatjs/intl-relativetimeformat/locale-data/be');
await import('@formatjs/intl-pluralrules/locale-data/be'); await import('@formatjs/intl-pluralrules/locale-data/be');
await import('@formatjs/intl-relativetimeformat/locale-data/be');
await import('@formatjs/intl-numberformat/locale-data/be');
await import('@formatjs/intl-datetimeformat/locale-data/be');
break; break;
case 'bg': case 'bg':
await import('@formatjs/intl-relativetimeformat/locale-data/bg');
await import('@formatjs/intl-pluralrules/locale-data/bg'); await import('@formatjs/intl-pluralrules/locale-data/bg');
await import('@formatjs/intl-relativetimeformat/locale-data/bg');
await import('@formatjs/intl-numberformat/locale-data/bg');
await import('@formatjs/intl-datetimeformat/locale-data/bg');
break; break;
case 'ca': case 'ca':
await import('@formatjs/intl-relativetimeformat/locale-data/ca');
await import('@formatjs/intl-pluralrules/locale-data/ca'); await import('@formatjs/intl-pluralrules/locale-data/ca');
await import('@formatjs/intl-relativetimeformat/locale-data/ca');
await import('@formatjs/intl-numberformat/locale-data/ca');
await import('@formatjs/intl-datetimeformat/locale-data/ca');
break; break;
case 'cs': case 'cs':
await import('@formatjs/intl-relativetimeformat/locale-data/cs');
await import('@formatjs/intl-pluralrules/locale-data/cs'); await import('@formatjs/intl-pluralrules/locale-data/cs');
await import('@formatjs/intl-relativetimeformat/locale-data/cs');
await import('@formatjs/intl-numberformat/locale-data/cs');
await import('@formatjs/intl-datetimeformat/locale-data/cs');
break; break;
case 'cy': case 'cy':
await import('@formatjs/intl-relativetimeformat/locale-data/cy');
await import('@formatjs/intl-pluralrules/locale-data/cy'); await import('@formatjs/intl-pluralrules/locale-data/cy');
await import('@formatjs/intl-relativetimeformat/locale-data/cy');
await import('@formatjs/intl-numberformat/locale-data/cy');
await import('@formatjs/intl-datetimeformat/locale-data/cy');
break; break;
case 'da': case 'da':
await import('@formatjs/intl-relativetimeformat/locale-data/da');
await import('@formatjs/intl-pluralrules/locale-data/da'); await import('@formatjs/intl-pluralrules/locale-data/da');
await import('@formatjs/intl-relativetimeformat/locale-data/da');
await import('@formatjs/intl-numberformat/locale-data/da');
await import('@formatjs/intl-datetimeformat/locale-data/da');
break; break;
case 'de': case 'de':
await import('@formatjs/intl-relativetimeformat/locale-data/de');
await import('@formatjs/intl-pluralrules/locale-data/de'); await import('@formatjs/intl-pluralrules/locale-data/de');
await import('@formatjs/intl-relativetimeformat/locale-data/de');
await import('@formatjs/intl-numberformat/locale-data/de');
await import('@formatjs/intl-datetimeformat/locale-data/de');
break; break;
case 'et': case 'et':
await import('@formatjs/intl-relativetimeformat/locale-data/et');
await import('@formatjs/intl-pluralrules/locale-data/et'); await import('@formatjs/intl-pluralrules/locale-data/et');
await import('@formatjs/intl-relativetimeformat/locale-data/et');
await import('@formatjs/intl-numberformat/locale-data/et');
await import('@formatjs/intl-datetimeformat/locale-data/et');
break; break;
case 'el': case 'el':
await import('@formatjs/intl-relativetimeformat/locale-data/el');
await import('@formatjs/intl-pluralrules/locale-data/el'); await import('@formatjs/intl-pluralrules/locale-data/el');
await import('@formatjs/intl-relativetimeformat/locale-data/el');
await import('@formatjs/intl-numberformat/locale-data/el');
await import('@formatjs/intl-datetimeformat/locale-data/el');
break; break;
case 'en': case 'en':
await import('@formatjs/intl-relativetimeformat/locale-data/en');
await import('@formatjs/intl-pluralrules/locale-data/en'); await import('@formatjs/intl-pluralrules/locale-data/en');
await import('@formatjs/intl-relativetimeformat/locale-data/en');
await import('@formatjs/intl-numberformat/locale-data/en');
await import('@formatjs/intl-datetimeformat/locale-data/en');
break; break;
case 'es': case 'es':
case 'rap':
case 'qu':
await import('@formatjs/intl-relativetimeformat/locale-data/es');
await import('@formatjs/intl-pluralrules/locale-data/es'); await import('@formatjs/intl-pluralrules/locale-data/es');
await import('@formatjs/intl-relativetimeformat/locale-data/es');
await import('@formatjs/intl-numberformat/locale-data/es');
await import('@formatjs/intl-datetimeformat/locale-data/es');
break;
case 'eo':
await import('@formatjs/intl-pluralrules/locale-data/eo');
await import('@formatjs/intl-relativetimeformat/locale-data/eo');
await import('@formatjs/intl-numberformat/locale-data/eo');
await import('@formatjs/intl-datetimeformat/locale-data/eo');
break; break;
case 'eu': case 'eu':
await import('@formatjs/intl-relativetimeformat/locale-data/eu');
await import('@formatjs/intl-pluralrules/locale-data/eu'); await import('@formatjs/intl-pluralrules/locale-data/eu');
await import('@formatjs/intl-relativetimeformat/locale-data/eu');
await import('@formatjs/intl-numberformat/locale-data/eu');
await import('@formatjs/intl-datetimeformat/locale-data/eu');
break; break;
case 'fa': case 'fa':
await import('@formatjs/intl-relativetimeformat/locale-data/fa');
await import('@formatjs/intl-pluralrules/locale-data/fa'); await import('@formatjs/intl-pluralrules/locale-data/fa');
await import('@formatjs/intl-relativetimeformat/locale-data/fa');
await import('@formatjs/intl-numberformat/locale-data/fa');
await import('@formatjs/intl-datetimeformat/locale-data/fa');
break;
case 'fil':
await import('@formatjs/intl-pluralrules/locale-data/fil');
await import('@formatjs/intl-relativetimeformat/locale-data/fil');
await import('@formatjs/intl-numberformat/locale-data/fil');
await import('@formatjs/intl-datetimeformat/locale-data/fil');
break; break;
case 'fr': case 'fr':
case 'ht':
await import('@formatjs/intl-relativetimeformat/locale-data/fr');
await import('@formatjs/intl-pluralrules/locale-data/fr'); await import('@formatjs/intl-pluralrules/locale-data/fr');
await import('@formatjs/intl-relativetimeformat/locale-data/fr');
await import('@formatjs/intl-numberformat/locale-data/fr');
await import('@formatjs/intl-datetimeformat/locale-data/fr');
break; break;
case 'fy': case 'fy':
await import('@formatjs/intl-relativetimeformat/locale-data/fy');
await import('@formatjs/intl-pluralrules/locale-data/fy'); await import('@formatjs/intl-pluralrules/locale-data/fy');
await import('@formatjs/intl-relativetimeformat/locale-data/fy');
await import('@formatjs/intl-numberformat/locale-data/fy');
await import('@formatjs/intl-datetimeformat/locale-data/fy');
break; break;
case 'ga': case 'ga':
await import('@formatjs/intl-relativetimeformat/locale-data/ga');
await import('@formatjs/intl-pluralrules/locale-data/ga'); await import('@formatjs/intl-pluralrules/locale-data/ga');
await import('@formatjs/intl-relativetimeformat/locale-data/ga');
await import('@formatjs/intl-numberformat/locale-data/ga');
await import('@formatjs/intl-datetimeformat/locale-data/ga');
break; break;
case 'gd': case 'gd':
await import('@formatjs/intl-relativetimeformat/locale-data/gd');
await import('@formatjs/intl-pluralrules/locale-data/gd'); await import('@formatjs/intl-pluralrules/locale-data/gd');
await import('@formatjs/intl-relativetimeformat/locale-data/gd');
await import('@formatjs/intl-numberformat/locale-data/gd');
await import('@formatjs/intl-datetimeformat/locale-data/gd');
break; break;
case 'gl': case 'gl':
await import('@formatjs/intl-relativetimeformat/locale-data/gl');
await import('@formatjs/intl-pluralrules/locale-data/gl'); await import('@formatjs/intl-pluralrules/locale-data/gl');
await import('@formatjs/intl-relativetimeformat/locale-data/gl');
await import('@formatjs/intl-numberformat/locale-data/gl');
await import('@formatjs/intl-datetimeformat/locale-data/gl');
break; break;
case 'ko': case 'ko':
await import('@formatjs/intl-relativetimeformat/locale-data/ko');
await import('@formatjs/intl-pluralrules/locale-data/ko'); await import('@formatjs/intl-pluralrules/locale-data/ko');
await import('@formatjs/intl-relativetimeformat/locale-data/ko');
await import('@formatjs/intl-numberformat/locale-data/ko');
await import('@formatjs/intl-datetimeformat/locale-data/ko');
break;
case 'ha':
await import('@formatjs/intl-pluralrules/locale-data/ha');
await import('@formatjs/intl-relativetimeformat/locale-data/ha');
await import('@formatjs/intl-numberformat/locale-data/ha');
await import('@formatjs/intl-datetimeformat/locale-data/ha');
break; break;
case 'hy': case 'hy':
await import('@formatjs/intl-relativetimeformat/locale-data/hy');
await import('@formatjs/intl-pluralrules/locale-data/hy'); await import('@formatjs/intl-pluralrules/locale-data/hy');
await import('@formatjs/intl-relativetimeformat/locale-data/hy');
await import('@formatjs/intl-numberformat/locale-data/hy');
await import('@formatjs/intl-datetimeformat/locale-data/hy');
break; break;
case 'he': case 'he':
await import('@formatjs/intl-relativetimeformat/locale-data/he');
await import('@formatjs/intl-pluralrules/locale-data/he'); await import('@formatjs/intl-pluralrules/locale-data/he');
await import('@formatjs/intl-relativetimeformat/locale-data/he');
await import('@formatjs/intl-numberformat/locale-data/he');
await import('@formatjs/intl-datetimeformat/locale-data/he');
break; break;
case 'hr': case 'hr':
await import('@formatjs/intl-relativetimeformat/locale-data/hr');
await import('@formatjs/intl-pluralrules/locale-data/hr'); await import('@formatjs/intl-pluralrules/locale-data/hr');
await import('@formatjs/intl-relativetimeformat/locale-data/hr');
await import('@formatjs/intl-numberformat/locale-data/hr');
await import('@formatjs/intl-datetimeformat/locale-data/hr');
break; break;
case 'xh': case 'xh':
await import('@formatjs/intl-relativetimeformat/locale-data/xh');
await import('@formatjs/intl-pluralrules/locale-data/xh'); await import('@formatjs/intl-pluralrules/locale-data/xh');
await import('@formatjs/intl-relativetimeformat/locale-data/xh');
await import('@formatjs/intl-numberformat/locale-data/xh');
await import('@formatjs/intl-datetimeformat/locale-data/xh');
break; break;
case 'zu': case 'zu':
await import('@formatjs/intl-relativetimeformat/locale-data/zu');
await import('@formatjs/intl-pluralrules/locale-data/zu'); await import('@formatjs/intl-pluralrules/locale-data/zu');
await import('@formatjs/intl-relativetimeformat/locale-data/zu');
await import('@formatjs/intl-numberformat/locale-data/zu');
await import('@formatjs/intl-datetimeformat/locale-data/zu');
break; break;
case 'is': case 'is':
await import('@formatjs/intl-relativetimeformat/locale-data/is');
await import('@formatjs/intl-pluralrules/locale-data/is'); await import('@formatjs/intl-pluralrules/locale-data/is');
await import('@formatjs/intl-relativetimeformat/locale-data/is');
await import('@formatjs/intl-numberformat/locale-data/is');
await import('@formatjs/intl-datetimeformat/locale-data/is');
break; break;
case 'it': case 'it':
await import('@formatjs/intl-relativetimeformat/locale-data/it');
await import('@formatjs/intl-pluralrules/locale-data/it'); await import('@formatjs/intl-pluralrules/locale-data/it');
await import('@formatjs/intl-relativetimeformat/locale-data/it');
await import('@formatjs/intl-numberformat/locale-data/it');
await import('@formatjs/intl-datetimeformat/locale-data/it');
break; break;
case 'ka': case 'ka':
await import('@formatjs/intl-relativetimeformat/locale-data/ka');
await import('@formatjs/intl-pluralrules/locale-data/ka'); await import('@formatjs/intl-pluralrules/locale-data/ka');
await import('@formatjs/intl-relativetimeformat/locale-data/ka');
await import('@formatjs/intl-numberformat/locale-data/ka');
await import('@formatjs/intl-datetimeformat/locale-data/ka');
break; break;
case 'kk': case 'kk':
await import('@formatjs/intl-relativetimeformat/locale-data/kk');
await import('@formatjs/intl-pluralrules/locale-data/kk'); await import('@formatjs/intl-pluralrules/locale-data/kk');
await import('@formatjs/intl-relativetimeformat/locale-data/kk');
await import('@formatjs/intl-numberformat/locale-data/kk');
await import('@formatjs/intl-datetimeformat/locale-data/kk');
break;
case 'qu':
await import('@formatjs/intl-pluralrules/locale-data/en');
await import('@formatjs/intl-relativetimeformat/locale-data/qu');
await import('@formatjs/intl-numberformat/locale-data/qu');
await import('@formatjs/intl-datetimeformat/locale-data/qu');
break; break;
case 'sw': case 'sw':
await import('@formatjs/intl-relativetimeformat/locale-data/sw');
await import('@formatjs/intl-pluralrules/locale-data/sw'); await import('@formatjs/intl-pluralrules/locale-data/sw');
await import('@formatjs/intl-relativetimeformat/locale-data/sw');
await import('@formatjs/intl-numberformat/locale-data/sw');
await import('@formatjs/intl-datetimeformat/locale-data/sw');
break; break;
case 'ku': case 'ku':
await import('@formatjs/intl-relativetimeformat/locale-data/ku');
await import('@formatjs/intl-pluralrules/locale-data/ku'); await import('@formatjs/intl-pluralrules/locale-data/ku');
await import('@formatjs/intl-relativetimeformat/locale-data/ku');
await import('@formatjs/intl-numberformat/locale-data/ku');
await import('@formatjs/intl-datetimeformat/locale-data/ku');
break; break;
case 'ckb': case 'ckb':
await import('@formatjs/intl-relativetimeformat/locale-data/ckb');
await import('@formatjs/intl-pluralrules/locale-data/ckb'); await import('@formatjs/intl-pluralrules/locale-data/ckb');
await import('@formatjs/intl-relativetimeformat/locale-data/ckb');
await import('@formatjs/intl-numberformat/locale-data/ckb');
await import('@formatjs/intl-datetimeformat/locale-data/ckb');
break; break;
case 'lv': case 'lv':
await import('@formatjs/intl-relativetimeformat/locale-data/lv');
await import('@formatjs/intl-pluralrules/locale-data/lv'); await import('@formatjs/intl-pluralrules/locale-data/lv');
await import('@formatjs/intl-relativetimeformat/locale-data/lv');
await import('@formatjs/intl-numberformat/locale-data/lv');
await import('@formatjs/intl-datetimeformat/locale-data/lv');
break; break;
case 'lt': case 'lt':
await import('@formatjs/intl-relativetimeformat/locale-data/lt');
await import('@formatjs/intl-pluralrules/locale-data/lt'); await import('@formatjs/intl-pluralrules/locale-data/lt');
await import('@formatjs/intl-relativetimeformat/locale-data/lt');
await import('@formatjs/intl-numberformat/locale-data/lt');
await import('@formatjs/intl-datetimeformat/locale-data/lt');
break; break;
case 'hu': case 'hu':
await import('@formatjs/intl-relativetimeformat/locale-data/hu');
await import('@formatjs/intl-pluralrules/locale-data/hu'); await import('@formatjs/intl-pluralrules/locale-data/hu');
await import('@formatjs/intl-relativetimeformat/locale-data/hu');
await import('@formatjs/intl-numberformat/locale-data/hu');
await import('@formatjs/intl-datetimeformat/locale-data/hu');
break; break;
case 'mi': case 'mi':
await import('@formatjs/intl-relativetimeformat/locale-data/mi');
await import('@formatjs/intl-pluralrules/locale-data/en'); await import('@formatjs/intl-pluralrules/locale-data/en');
await import('@formatjs/intl-relativetimeformat/locale-data/mi');
await import('@formatjs/intl-numberformat/locale-data/mi');
await import('@formatjs/intl-datetimeformat/locale-data/mi');
break; break;
case 'mn': case 'mn':
await import('@formatjs/intl-relativetimeformat/locale-data/mn');
await import('@formatjs/intl-pluralrules/locale-data/mn'); await import('@formatjs/intl-pluralrules/locale-data/mn');
await import('@formatjs/intl-relativetimeformat/locale-data/mn');
await import('@formatjs/intl-numberformat/locale-data/mn');
await import('@formatjs/intl-datetimeformat/locale-data/mn');
break; break;
case 'nl': case 'nl':
await import('@formatjs/intl-relativetimeformat/locale-data/nl');
await import('@formatjs/intl-pluralrules/locale-data/nl'); await import('@formatjs/intl-pluralrules/locale-data/nl');
await import('@formatjs/intl-relativetimeformat/locale-data/nl');
await import('@formatjs/intl-numberformat/locale-data/nl');
await import('@formatjs/intl-datetimeformat/locale-data/nl');
break; break;
case 'ja': case 'ja':
await import('@formatjs/intl-relativetimeformat/locale-data/ja');
await import('@formatjs/intl-pluralrules/locale-data/ja'); await import('@formatjs/intl-pluralrules/locale-data/ja');
await import('@formatjs/intl-relativetimeformat/locale-data/ja');
await import('@formatjs/intl-numberformat/locale-data/ja');
await import('@formatjs/intl-datetimeformat/locale-data/ja');
break; break;
case 'nb': case 'nb':
await import('@formatjs/intl-relativetimeformat/locale-data/nb');
await import('@formatjs/intl-pluralrules/locale-data/nb'); await import('@formatjs/intl-pluralrules/locale-data/nb');
await import('@formatjs/intl-relativetimeformat/locale-data/nb');
await import('@formatjs/intl-numberformat/locale-data/nb');
await import('@formatjs/intl-datetimeformat/locale-data/nb');
break; break;
case 'nn': case 'nn':
await import('@formatjs/intl-relativetimeformat/locale-data/nn');
await import('@formatjs/intl-pluralrules/locale-data/nn'); await import('@formatjs/intl-pluralrules/locale-data/nn');
await import('@formatjs/intl-relativetimeformat/locale-data/nn');
await import('@formatjs/intl-numberformat/locale-data/nn');
await import('@formatjs/intl-datetimeformat/locale-data/nn');
break; break;
case 'or': case 'or':
await import('@formatjs/intl-relativetimeformat/locale-data/or');
await import('@formatjs/intl-pluralrules/locale-data/or'); await import('@formatjs/intl-pluralrules/locale-data/or');
await import('@formatjs/intl-relativetimeformat/locale-data/or');
await import('@formatjs/intl-numberformat/locale-data/or');
await import('@formatjs/intl-datetimeformat/locale-data/or');
break; break;
case 'uz': case 'uz':
await import('@formatjs/intl-relativetimeformat/locale-data/uz');
await import('@formatjs/intl-pluralrules/locale-data/uz'); await import('@formatjs/intl-pluralrules/locale-data/uz');
await import('@formatjs/intl-relativetimeformat/locale-data/uz');
await import('@formatjs/intl-numberformat/locale-data/uz');
await import('@formatjs/intl-datetimeformat/locale-data/uz');
break; break;
case 'th': case 'th':
await import('@formatjs/intl-relativetimeformat/locale-data/th');
await import('@formatjs/intl-pluralrules/locale-data/th'); await import('@formatjs/intl-pluralrules/locale-data/th');
await import('@formatjs/intl-relativetimeformat/locale-data/th');
await import('@formatjs/intl-numberformat/locale-data/th');
await import('@formatjs/intl-datetimeformat/locale-data/th');
break; break;
case 'km': case 'km':
await import('@formatjs/intl-relativetimeformat/locale-data/km');
await import('@formatjs/intl-pluralrules/locale-data/km'); await import('@formatjs/intl-pluralrules/locale-data/km');
await import('@formatjs/intl-relativetimeformat/locale-data/km');
await import('@formatjs/intl-numberformat/locale-data/km');
await import('@formatjs/intl-datetimeformat/locale-data/km');
break; break;
case 'pl': case 'pl':
await import('@formatjs/intl-relativetimeformat/locale-data/pl');
await import('@formatjs/intl-pluralrules/locale-data/pl'); await import('@formatjs/intl-pluralrules/locale-data/pl');
await import('@formatjs/intl-relativetimeformat/locale-data/pl');
await import('@formatjs/intl-numberformat/locale-data/pl');
await import('@formatjs/intl-datetimeformat/locale-data/pl');
break; break;
case 'pt': case 'pt':
await import('@formatjs/intl-relativetimeformat/locale-data/pt');
await import('@formatjs/intl-pluralrules/locale-data/pt'); await import('@formatjs/intl-pluralrules/locale-data/pt');
await import('@formatjs/intl-relativetimeformat/locale-data/pt');
await import('@formatjs/intl-numberformat/locale-data/pt');
await import('@formatjs/intl-datetimeformat/locale-data/pt');
break; break;
case 'ro': case 'ro':
await import('@formatjs/intl-relativetimeformat/locale-data/ro');
await import('@formatjs/intl-pluralrules/locale-data/ro'); await import('@formatjs/intl-pluralrules/locale-data/ro');
await import('@formatjs/intl-relativetimeformat/locale-data/ro');
await import('@formatjs/intl-numberformat/locale-data/ro');
await import('@formatjs/intl-datetimeformat/locale-data/ro');
break; break;
case 'ru': case 'ru':
await import('@formatjs/intl-relativetimeformat/locale-data/ru');
await import('@formatjs/intl-pluralrules/locale-data/ru'); await import('@formatjs/intl-pluralrules/locale-data/ru');
await import('@formatjs/intl-relativetimeformat/locale-data/ru');
await import('@formatjs/intl-numberformat/locale-data/ru');
await import('@formatjs/intl-datetimeformat/locale-data/ru');
break; break;
case 'nso': case 'nso':
await import('@formatjs/intl-relativetimeformat/locale-data/en');
await import('@formatjs/intl-pluralrules/locale-data/nso'); await import('@formatjs/intl-pluralrules/locale-data/nso');
await import('@formatjs/intl-relativetimeformat/locale-data/en');
await import('@formatjs/intl-numberformat/locale-data/en');
await import('@formatjs/intl-datetimeformat/locale-data/en');
break; break;
case 'tn': case 'tn':
await import('@formatjs/intl-relativetimeformat/locale-data/en');
await import('@formatjs/intl-pluralrules/locale-data/tn'); await import('@formatjs/intl-pluralrules/locale-data/tn');
await import('@formatjs/intl-relativetimeformat/locale-data/en');
await import('@formatjs/intl-numberformat/locale-data/en');
await import('@formatjs/intl-datetimeformat/locale-data/en');
break; break;
case 'sk': case 'sk':
await import('@formatjs/intl-relativetimeformat/locale-data/sk');
await import('@formatjs/intl-pluralrules/locale-data/sk'); await import('@formatjs/intl-pluralrules/locale-data/sk');
await import('@formatjs/intl-relativetimeformat/locale-data/sk');
await import('@formatjs/intl-numberformat/locale-data/sk');
await import('@formatjs/intl-datetimeformat/locale-data/sk');
break; break;
case 'sl': case 'sl':
await import('@formatjs/intl-relativetimeformat/locale-data/sl');
await import('@formatjs/intl-pluralrules/locale-data/sl'); await import('@formatjs/intl-pluralrules/locale-data/sl');
await import('@formatjs/intl-relativetimeformat/locale-data/sl');
await import('@formatjs/intl-numberformat/locale-data/sl');
await import('@formatjs/intl-datetimeformat/locale-data/sl');
break; break;
case 'sr': case 'sr':
await import('@formatjs/intl-relativetimeformat/locale-data/sr');
await import('@formatjs/intl-pluralrules/locale-data/sr'); await import('@formatjs/intl-pluralrules/locale-data/sr');
await import('@formatjs/intl-relativetimeformat/locale-data/sr');
await import('@formatjs/intl-numberformat/locale-data/sr');
await import('@formatjs/intl-datetimeformat/locale-data/sr');
break; break;
case 'fi': case 'fi':
await import('@formatjs/intl-relativetimeformat/locale-data/fi');
await import('@formatjs/intl-pluralrules/locale-data/fi'); await import('@formatjs/intl-pluralrules/locale-data/fi');
await import('@formatjs/intl-relativetimeformat/locale-data/fi');
await import('@formatjs/intl-numberformat/locale-data/fi');
await import('@formatjs/intl-datetimeformat/locale-data/fi');
break; break;
case 'sv': case 'sv':
await import('@formatjs/intl-relativetimeformat/locale-data/sv');
await import('@formatjs/intl-pluralrules/locale-data/sv'); await import('@formatjs/intl-pluralrules/locale-data/sv');
await import('@formatjs/intl-relativetimeformat/locale-data/sv');
await import('@formatjs/intl-numberformat/locale-data/sv');
await import('@formatjs/intl-datetimeformat/locale-data/sv');
break; break;
case 'vi': case 'vi':
await import('@formatjs/intl-relativetimeformat/locale-data/vi');
await import('@formatjs/intl-pluralrules/locale-data/vi'); await import('@formatjs/intl-pluralrules/locale-data/vi');
await import('@formatjs/intl-relativetimeformat/locale-data/vi');
await import('@formatjs/intl-numberformat/locale-data/vi');
await import('@formatjs/intl-datetimeformat/locale-data/vi');
break; break;
case 'tr': case 'tr':
await import('@formatjs/intl-relativetimeformat/locale-data/tr');
await import('@formatjs/intl-pluralrules/locale-data/tr'); await import('@formatjs/intl-pluralrules/locale-data/tr');
await import('@formatjs/intl-relativetimeformat/locale-data/tr');
await import('@formatjs/intl-numberformat/locale-data/tr');
await import('@formatjs/intl-datetimeformat/locale-data/tr');
break; break;
case 'uk': case 'uk':
await import('@formatjs/intl-relativetimeformat/locale-data/uk');
await import('@formatjs/intl-pluralrules/locale-data/uk'); await import('@formatjs/intl-pluralrules/locale-data/uk');
await import('@formatjs/intl-relativetimeformat/locale-data/uk');
await import('@formatjs/intl-numberformat/locale-data/uk');
await import('@formatjs/intl-datetimeformat/locale-data/uk');
break; break;
case 'zh': case 'zh':
await import('@formatjs/intl-relativetimeformat/locale-data/zh');
await import('@formatjs/intl-pluralrules/locale-data/zh'); await import('@formatjs/intl-pluralrules/locale-data/zh');
await import('@formatjs/intl-relativetimeformat/locale-data/zh');
await import('@formatjs/intl-numberformat/locale-data/zh');
await import('@formatjs/intl-datetimeformat/locale-data/zh');
break; break;
default: default:
await import('@formatjs/intl-relativetimeformat/locale-data/en');
await import('@formatjs/intl-pluralrules/locale-data/en'); await import('@formatjs/intl-pluralrules/locale-data/en');
await import('@formatjs/intl-relativetimeformat/locale-data/en');
await import('@formatjs/intl-numberformat/locale-data/en');
await import('@formatjs/intl-datetimeformat/locale-data/en');
break; break;
} }
}; };

10
src/lib/intl-shape.js Normal file
View file

@ -0,0 +1,10 @@
const PropTypes = require('prop-types');
// intlShape was removed in react-intl@3 and replaced with a TypeScript interface.
// These are some of the commonly used properties from the intl object.
const intlShape = PropTypes.shape({
locale: PropTypes.string.isRequired,
formatMessage: PropTypes.func.isRequired
});
module.exports = intlShape;

View file

@ -1,7 +0,0 @@
const ReactIntl = require('react-intl');
// Add locale data to react intl for all supported languages
const localeData = require('scratch-l10n').localeData;
ReactIntl.addLocaleData(localeData);
module.exports = ReactIntl;

44
src/lib/locales.js Normal file
View file

@ -0,0 +1,44 @@
/**
* Scratch has some locales that are not recognized by Intl. Use an appropriate alternative for these locales.
* @param {string} locale Scratch's locale
* @returns {string} the locale to use in IntlProvider
*/
const scratchLocaleToIntlLocale = locale => {
switch (locale) {
case 'ab':
return 'ru';
case 'an':
case 'rap':
return 'es';
case 'ht':
case 'oc':
return 'fr';
default:
return locale;
}
};
/**
* Gets the locale for the current window.
* @returns {string} locale
*/
const getLocale = () => {
// Get locale from global namespace (see "init.js")
let locale = window._locale || 'en';
if (typeof window._messages !== 'undefined') {
if (typeof window._messages[locale] === 'undefined') {
// Fall back on the split
locale = locale.split('-')[0];
}
if (typeof window._messages[locale] === 'undefined') {
// Language appears to not be supported fall back to 'en'
locale = 'en';
}
}
return locale;
};
module.exports = {
getLocale,
scratchLocaleToIntlLocale
};

View file

@ -2,11 +2,13 @@
const React = require('react'); // eslint-disable-line const React = require('react'); // eslint-disable-line
const ReactDOM = require('react-dom'); const ReactDOM = require('react-dom');
const StoreProvider = require('react-redux').Provider; const StoreProvider = require('react-redux').Provider;
const IntlProvider = require('react-intl').IntlProvider;
const IntlProvider = require('./intl.jsx').IntlProvider; const {getLocale, scratchLocaleToIntlLocale} = require('./locales.js');
const permissionsActions = require('../redux/permissions.js'); const permissionsActions = require('../redux/permissions.js');
const sessionActions = require('../redux/session.js'); const sessionActions = require('../redux/session.js');
const configureStore = require('./configure-store.js'); const configureStore = require('./configure-store.js');
import intlPolyfill from '../lib/intl-polyfill';
require('../main.scss'); require('../main.scss');
@ -20,38 +22,35 @@ require('../main.scss');
*/ */
const render = (jsx, element, reducers, initialState, enhancer) => { const render = (jsx, element, reducers, initialState, enhancer) => {
// Get locale and messages from global namespace (see "init.js") // Get locale and messages from global namespace (see "init.js")
let locale = window._locale || 'en'; const locale = getLocale();
let messages = {}; let messages = {};
if (typeof window._messages !== 'undefined') { if (typeof window._messages !== 'undefined') {
if (typeof window._messages[locale] === 'undefined') {
// Fall back on the split
locale = locale.split('-')[0];
}
if (typeof window._messages[locale] === 'undefined') {
// Language appears to not be supported fall back to 'en'
locale = 'en';
}
messages = window._messages[locale]; messages = window._messages[locale];
} }
const store = configureStore(reducers, initialState, enhancer); const intlLocale = scratchLocaleToIntlLocale(locale);
// react-intl needs Intl before rendering
intlPolyfill(intlLocale).then(() => {
const store = configureStore(reducers, initialState, enhancer);
// Render view component // Render view component
ReactDOM.render( ReactDOM.render(
<StoreProvider store={store}> <StoreProvider store={store}>
<IntlProvider <IntlProvider
locale={locale} locale={intlLocale}
messages={messages} messages={messages}
> textComponent="span"
{jsx} >
</IntlProvider> {jsx}
</StoreProvider>, </IntlProvider>
element </StoreProvider>,
); element
);
// Get initial session & permissions // Get initial session & permissions
store.dispatch(permissionsActions.getPermissions()); store.dispatch(permissionsActions.getPermissions());
store.dispatch(sessionActions.refreshSession()); store.dispatch(sessionActions.refreshSession());
});
}; };
module.exports = render; module.exports = render;

82
src/lib/select-unit.js Normal file
View file

@ -0,0 +1,82 @@
const MILLISECONDS_PER_SECOND = 1000;
const SECONDS_PER_MINUTE = 60;
const MINUTES_PER_HOUR = 60;
const HOURS_PER_DAY = 24;
/**
* Determines the best unit (seconds, minutes, hours, days, months, or years)
* and value to represent the time difference between two dates.
* @param {Date} time - date to calculate relative units
* @param {Date} [relativeTo] - optional relative date, defaults to now
* @returns {object} calculated best unit and value
*/
const selectUnit = (time, relativeTo) => {
// Default to now
if (!relativeTo) relativeTo = new Date();
const seconds = (time - relativeTo) / MILLISECONDS_PER_SECOND;
if (Math.abs(seconds) < SECONDS_PER_MINUTE) {
return {
value: Math.trunc(seconds),
unit: 'second'
};
}
const minutes = seconds / SECONDS_PER_MINUTE;
if (Math.abs(minutes) < MINUTES_PER_HOUR) {
return {
value: Math.trunc(minutes),
unit: 'minute'
};
}
const hours = minutes / MINUTES_PER_HOUR;
if (Math.abs(hours) < HOURS_PER_DAY) {
return {
value: Math.trunc(hours),
unit: 'hour'
};
}
const days = hours / HOURS_PER_DAY;
let years = time.getFullYear() - relativeTo.getFullYear();
let months = time.getMonth() - relativeTo.getMonth() + (12 * years);
// Handle calendar months different but less than a complete month elapsed
if (months > 0 && time.getDate() < relativeTo.getDate()) months--;
if (months < 0 && time.getDate() > relativeTo.getDate()) months++;
if (Math.abs(months) < 1) {
return {
value: Math.trunc(days),
unit: 'day'
};
}
// Handle calendar years different but less than a complete year elapsed
if (years > 0 && (time.getMonth() < relativeTo.getMonth() ||
(time.getMonth() === relativeTo.getMonth() && time.getDate() < relativeTo.getDate()))) {
years--;
}
if (years < 0 && (time.getMonth() > relativeTo.getMonth() ||
(time.getMonth() === relativeTo.getMonth() && time.getDate() > relativeTo.getDate()))) {
years++;
}
if (Math.abs(years) < 1) {
return {
value: months,
unit: 'month'
};
}
return {
value: years,
unit: 'year'
};
};
module.exports = {
selectUnit
};

View file

@ -4,8 +4,8 @@ const React = require('react');
const MediaQuery = require('react-responsive').default; const MediaQuery = require('react-responsive').default;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const intlShape = require('../../../lib/intl-shape');
const render = require('../../../lib/render.jsx'); const render = require('../../../lib/render.jsx');
const frameless = require('../../../lib/frameless'); const frameless = require('../../../lib/frameless');

View file

@ -4,8 +4,8 @@ const React = require('react');
const MediaQuery = require('react-responsive').default; const MediaQuery = require('react-responsive').default;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const intlShape = require('../../../lib/intl-shape');
const render = require('../../../lib/render.jsx'); const render = require('../../../lib/render.jsx');
const frameless = require('../../../lib/frameless'); const frameless = require('../../../lib/frameless');
@ -2129,6 +2129,7 @@ class AnnualReport extends React.Component {
comment={this.props.intl.formatMessage( comment={this.props.intl.formatMessage(
{id: 'annualReport.2020.communityQuote2Text'} {id: 'annualReport.2020.communityQuote2Text'}
)} )}
datetimeCreated="2020-01-01"
/> />
</div> </div>
</div> </div>

View file

@ -4,8 +4,8 @@ const React = require('react');
const MediaQuery = require('react-responsive').default; const MediaQuery = require('react-responsive').default;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const intlShape = require('../../../lib/intl-shape');
const render = require('../../../lib/render.jsx'); const render = require('../../../lib/render.jsx');
const frameless = require('../../../lib/frameless'); const frameless = require('../../../lib/frameless');

View file

@ -1,8 +1,9 @@
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react'); const React = require('react');
const intlShape = require('../../lib/intl-shape');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');

View file

@ -1,7 +1,6 @@
const React = require('react'); const React = require('react');
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const TitleBanner = require('../../components/title-banner/title-banner.jsx'); const TitleBanner = require('../../components/title-banner/title-banner.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx');
@ -10,6 +9,10 @@ const Page = require('../../components/page/www/page.jsx');
require('./camp.scss'); require('./camp.scss');
const MainStudio = chunks => <a href="https://scratch.mit.edu/studios/4160302/">{chunks}</a>;
const FinalStudio = chunks => <a href="https://scratch.mit.edu/studios/4160301/">{chunks}</a>;
const CounselorsStudio = chunks => <a href="https://scratch.mit.edu/studios/4160300/">{chunks}</a>;
const Camp = injectIntl(() => ( const Camp = injectIntl(() => (
<div> <div>
<TitleBanner className="masthead mod-blue-bg"> <TitleBanner className="masthead mod-blue-bg">
@ -40,7 +43,10 @@ const Camp = injectIntl(() => (
<center> <center>
<h2><FormattedMessage id="camp.welcome" /></h2> <h2><FormattedMessage id="camp.welcome" /></h2>
<p id="intro"> <p id="intro">
<FormattedHTMLMessage id="camp.welcomeIntro" /> <FormattedMessage
id="camp.welcomeIntroHTML"
values={{br: <br />}}
/>
</p> </p>
</center> </center>
<center> <center>
@ -63,7 +69,10 @@ const Camp = injectIntl(() => (
<div className="sidebar column"> <div className="sidebar column">
<h3><FormattedMessage id="camp.particpateTitle" /></h3> <h3><FormattedMessage id="camp.particpateTitle" /></h3>
<p> <p>
<FormattedHTMLMessage id="camp.part1Particpate" /> <FormattedMessage
id="camp.part1ParticpateHTML"
values={{a: MainStudio}}
/>
</p> </p>
</div> </div>
</FlexRow> </FlexRow>
@ -86,7 +95,10 @@ const Camp = injectIntl(() => (
<div className="sidebar column"> <div className="sidebar column">
<h3><FormattedMessage id="camp.particpateTitle" /></h3> <h3><FormattedMessage id="camp.particpateTitle" /></h3>
<p> <p>
<FormattedHTMLMessage id="camp.part2Particpate" /> <FormattedMessage
id="camp.part2ParticpateHTML"
values={{a: MainStudio}}
/>
</p> </p>
</div> </div>
</FlexRow> </FlexRow>
@ -109,7 +121,10 @@ const Camp = injectIntl(() => (
<div className="sidebar column"> <div className="sidebar column">
<h3><FormattedMessage id="camp.particpateTitle" /></h3> <h3><FormattedMessage id="camp.particpateTitle" /></h3>
<p> <p>
<FormattedHTMLMessage id="camp.part3Particpate" /> <FormattedMessage
id="camp.part3ParticpateHTML"
values={{a: FinalStudio}}
/>
</p> </p>
</div> </div>
</FlexRow> </FlexRow>
@ -132,7 +147,10 @@ const Camp = injectIntl(() => (
src="/images/camp/dolphin.svg" src="/images/camp/dolphin.svg"
/> />
<p> <p>
<FormattedHTMLMessage id="camp.infoCounselors" /> <FormattedMessage
id="camp.infoCounselorsHTML"
values={{a: CounselorsStudio}}
/>
</p> </p>
</div> </div>
<div> <div>

View file

@ -3,19 +3,24 @@
"camp.dates": "July 24th - August 13th", "camp.dates": "July 24th - August 13th",
"camp.welcome": "Welcome to Scratch Camp 2017!", "camp.welcome": "Welcome to Scratch Camp 2017!",
"camp.welcomeIntro": "Come take a dive into the ocean with us and design your very own creation. Your creation can be anything you might find in the ocean - real or made up! <br /> In this years camp, dive down deep with us in these three parts:", "camp.welcomeIntro": "Come take a dive into the ocean with us and design your very own creation. Your creation can be anything you might find in the ocean - real or made up! <br /> In this years camp, dive down deep with us in these three parts:",
"camp.welcomeIntroHTML": "Come take a dive into the ocean with us and design your very own creation. Your creation can be anything you might find in the ocean - real or made up!{br}In this years camp, dive down deep with us in these three parts:",
"camp.part1Dates":"Part 1 (July 24th - July 30th)", "camp.part1Dates":"Part 1 (July 24th - July 30th)",
"camp.detailsTitle": "Details:", "camp.detailsTitle": "Details:",
"camp.part1Details": "Create a project introducing us to a character, real or imagined, that lives in the ocean. You could create a monster from the depths, a cute little starfish, a taco eating shark, or anything else you can imagine.", "camp.part1Details": "Create a project introducing us to a character, real or imagined, that lives in the ocean. You could create a monster from the depths, a cute little starfish, a taco eating shark, or anything else you can imagine.",
"camp.particpateTitle": "How to Participate:", "camp.particpateTitle": "How to Participate:",
"camp.part1Particpate": "Part 1 of camp will take place in the <a href=\"https://scratch.mit.edu/studios/4160302/\">Main Camp Cabin studio</a>. Here you can ask questions, view other Scratchers' creations, and submit your own. Go to the studio to learn more!", "camp.part1Particpate": "Part 1 of camp will take place in the <a href=\"https://scratch.mit.edu/studios/4160302/\">Main Camp Cabin studio</a>. Here you can ask questions, view other Scratchers' creations, and submit your own. Go to the studio to learn more!",
"camp.part1ParticpateHTML": "Part 1 of camp will take place in the <a>Main Camp Cabin studio</a>. Here you can ask questions, view other Scratchers' creations, and submit your own. Go to the studio to learn more!",
"camp.part2Dates": "Part 2 (July 31st - August 6th)", "camp.part2Dates": "Part 2 (July 31st - August 6th)",
"camp.part2Details": "Now make your character interactive! Does your character have questions to ask us? What happens when you click on it? Does it have any special powers? And more!", "camp.part2Details": "Now make your character interactive! Does your character have questions to ask us? What happens when you click on it? Does it have any special powers? And more!",
"camp.part2Particpate":"Part 2 of camp will also take place in the <a href=\"https://scratch.mit.edu/studios/4160302/\">Main Camp Cabin studio</a>. Here you can ask questions, view other Scratchers' creations, and submit your own. Go to the studio to learn more!", "camp.part2Particpate":"Part 2 of camp will also take place in the <a href=\"https://scratch.mit.edu/studios/4160302/\">Main Camp Cabin studio</a>. Here you can ask questions, view other Scratchers' creations, and submit your own. Go to the studio to learn more!",
"camp.part2ParticpateHTML":"Part 2 of camp will also take place in the <a>Main Camp Cabin studio</a>. Here you can ask questions, view other Scratchers' creations, and submit your own. Go to the studio to learn more!",
"camp.part3Dates": "Part 3 (August 7th - August 13th)", "camp.part3Dates": "Part 3 (August 7th - August 13th)",
"camp.part3Details": "Create a project using your own creation along with other Scratchers creations. It could be a game, story, animation, or anything you come up with!", "camp.part3Details": "Create a project using your own creation along with other Scratchers creations. It could be a game, story, animation, or anything you come up with!",
"camp.part3Particpate":"The <a href=\"https://scratch.mit.edu/studios/4160301/\">Final Projects Camp Cabin studio</a> will hold part 3 of this year's Scratch Camp. Here you can submit your final project, give feedback to others, and celebrate Scratch Camp! Swim on over to the studio when part 3 comes out!", "camp.part3Particpate":"The <a href=\"https://scratch.mit.edu/studios/4160301/\">Final Projects Camp Cabin studio</a> will hold part 3 of this year's Scratch Camp. Here you can submit your final project, give feedback to others, and celebrate Scratch Camp! Swim on over to the studio when part 3 comes out!",
"camp.part3ParticpateHTML":"The <a>Final Projects Camp Cabin studio</a> will hold part 3 of this year's Scratch Camp. Here you can submit your final project, give feedback to others, and celebrate Scratch Camp! Swim on over to the studio when part 3 comes out!",
"camp.helpfulInfo": "Helpful Information", "camp.helpfulInfo": "Helpful Information",
"camp.infoCounselors": "The <a href=\"https://scratch.mit.edu/studios/4160300/\">Camp Counselors studio</a> offers a variety of examples for your ocean creation. You can also directly communicate with the Counselors there.", "camp.infoCounselors": "The <a href=\"https://scratch.mit.edu/studios/4160300/\">Camp Counselors studio</a> offers a variety of examples for your ocean creation. You can also directly communicate with the Counselors there.",
"camp.infoCounselorsHTML": "The <a>Camp Counselors studio</a> offers a variety of examples for your ocean creation. You can also directly communicate with the Counselors there.",
"camp.infoPart3":"Remember, in part 3, you must use some other creations made for this Scratch Camp. Use their part 2 project to learn about the character's personality!", "camp.infoPart3":"Remember, in part 3, you must use some other creations made for this Scratch Camp. Use their part 2 project to learn about the character's personality!",
"camp.infoTime":"Don't worry if you aren't around the whole time, you can always participate in whatever part you are available for! Just have fun and dive deep!" "camp.infoTime":"Don't worry if you aren't around the whole time, you can always participate in whatever part you are available for! Just have fun and dive deep!"
} }

View file

@ -1,9 +1,9 @@
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const React = require('react'); const React = require('react');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const intlShape = require('../../lib/intl-shape');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const HelpForm = require('../../components/helpform/helpform.jsx'); const HelpForm = require('../../components/helpform/helpform.jsx');

View file

@ -1,12 +1,12 @@
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const React = require('react'); const React = require('react');
const FlexRow = require('../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx');
const bindAll = require('lodash.bindall'); const bindAll = require('lodash.bindall');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const intlShape = require('../../lib/intl-shape');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const detectOS = require('../../lib/detect-os.js').default; const detectOS = require('../../lib/detect-os.js').default;
const OS_ENUM = require('../../lib/os-enum.js'); const OS_ENUM = require('../../lib/os-enum.js');

View file

@ -1,7 +1,8 @@
import React, {useState} from 'react'; import React, {useState} from 'react';
import {FormattedMessage, injectIntl, intlShape} from 'react-intl'; import {FormattedMessage, injectIntl} from 'react-intl';
import Page from '../../../components/page/www/page.jsx'; import Page from '../../../components/page/www/page.jsx';
const intlShape = require('../../../lib/intl-shape');
import render from '../../../lib/render.jsx'; import render from '../../../lib/render.jsx';
import FlexRow from '../../../components/flex-row/flex-row.jsx'; import FlexRow from '../../../components/flex-row/flex-row.jsx';

View file

@ -1,10 +1,9 @@
const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const React = require('react'); const React = require('react');
const api = require('../../../lib/api'); const api = require('../../../lib/api');
const intlShape = require('../../../lib/intl-shape');
const FlexRow = require('../../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../../components/flex-row/flex-row.jsx');
const SubNavigation = require('../../../components/subnavigation/subnavigation.jsx'); const SubNavigation = require('../../../components/subnavigation/subnavigation.jsx');
const TitleBanner = require('../../../components/title-banner/title-banner.jsx'); const TitleBanner = require('../../../components/title-banner/title-banner.jsx');
@ -119,7 +118,10 @@ class Download extends React.Component {
<h2 className="installation-column-number-text">{'1'}</h2> <h2 className="installation-column-number-text">{'1'}</h2>
</div> </div>
<h3><FormattedMessage id="download.airTitle" /></h3> <h3><FormattedMessage id="download.airTitle" /></h3>
<p><FormattedHTMLMessage id="download.airBody" /></p> <p><FormattedMessage
id="download.airBodyHTML"
values={{a: chunks => <a href="https://airsdk.harman.com/runtime">{chunks}</a>}}
/></p>
</div> </div>
<div className="installation-column"> <div className="installation-column">
<div className="installation-column-number"> <div className="installation-column-number">
@ -208,8 +210,14 @@ class Download extends React.Component {
<section id="other"> <section id="other">
<span className="nav-spacer" /> <span className="nav-spacer" />
<h2><FormattedMessage id="download.otherVersionsTitle" /></h2> <h2><FormattedMessage id="download.otherVersionsTitle" /></h2>
<p><FormattedHTMLMessage id="download.otherVersionsOlder" /></p> <p><FormattedMessage
<p><FormattedHTMLMessage id="download.otherVersionsAdmin" /></p> id="download.otherVersionsOlderHTML"
values={{a: chunks => <a href="http://scratch.mit.edu/scratch_1.4/">{chunks}</a>}}
/></p>
<p><FormattedMessage
id="download.otherVersionsAdminHTML"
values={{a: chunks => <a href="http://llk.github.io/scratch-msi/">{chunks}</a>}}
/></p>
</section> </section>
<section id="issues"> <section id="issues">
@ -217,7 +225,10 @@ class Download extends React.Component {
<h2><FormattedMessage id="download.knownIssuesTitle" /></h2> <h2><FormattedMessage id="download.knownIssuesTitle" /></h2>
<p><FormattedMessage id="download.knownIssuesOne" /></p> <p><FormattedMessage id="download.knownIssuesOne" /></p>
<p><FormattedMessage id="download.knownIssuesTwo" /></p> <p><FormattedMessage id="download.knownIssuesTwo" /></p>
<p><FormattedHTMLMessage id="download.knownIssuesThree" /></p> <p><FormattedMessage
id="download.knownIssuesThree"
values={{b: chunks => <b>{chunks}</b>}}
/></p>
<p><FormattedMessage id="download.knownIssuesFour" /></p> <p><FormattedMessage id="download.knownIssuesFour" /></p>
<a <a
className="button mod-link" className="button mod-link"

View file

@ -4,6 +4,7 @@
"download.installation": "Installation", "download.installation": "Installation",
"download.airTitle": "Adobe AIR", "download.airTitle": "Adobe AIR",
"download.airBody": "If you don't already have it, download and install the latest <a href=\"https://airsdk.harman.com/runtime\">Adobe AIR</a>", "download.airBody": "If you don't already have it, download and install the latest <a href=\"https://airsdk.harman.com/runtime\">Adobe AIR</a>",
"download.airBodyHTML": "If you don't already have it, download and install the latest <a>Adobe AIR</a>",
"download.macOSX": "Mac OS X", "download.macOSX": "Mac OS X",
"download.macOlder": "Mac OS 10.5 & Older", "download.macOlder": "Mac OS 10.5 & Older",
"download.windows": "Windows", "download.windows": "Windows",
@ -20,7 +21,9 @@
"download.currentVersion": "The current version is {version}.", "download.currentVersion": "The current version is {version}.",
"download.otherVersionsTitle": "Other Versions of Scratch", "download.otherVersionsTitle": "Other Versions of Scratch",
"download.otherVersionsOlder": "If you have an older computer, or cannot install the Scratch 2.0 offline editor, you can try installing <a href=\"http://scratch.mit.edu/scratch_1.4/\">Scratch 1.4</a>.", "download.otherVersionsOlder": "If you have an older computer, or cannot install the Scratch 2.0 offline editor, you can try installing <a href=\"http://scratch.mit.edu/scratch_1.4/\">Scratch 1.4</a>.",
"download.otherVersionsOlderHTML": "If you have an older computer, or cannot install the Scratch 2.0 offline editor, you can try installing <a>Scratch 1.4</a>.",
"download.otherVersionsAdmin": "If you are a network administrator: a Scratch 2.0 MSI has been created and maintained by a member of the community and hosted for public download <a href=\"http://llk.github.io/scratch-msi/\">here</a>.", "download.otherVersionsAdmin": "If you are a network administrator: a Scratch 2.0 MSI has been created and maintained by a member of the community and hosted for public download <a href=\"http://llk.github.io/scratch-msi/\">here</a>.",
"download.otherVersionsAdminHTML": "If you are a network administrator: a Scratch 2.0 MSI has been created and maintained by a member of the community and hosted for public download <a>here</a>.",
"download.knownIssuesTitle": "Known issues", "download.knownIssuesTitle": "Known issues",
"download.knownIssuesOne": "If your offline editor is crashing directly after Scratch is opened, install the Scratch 2 offline editor again (see step 2 above). This issue is due to a bug introduced in Adobe AIR version 14 (released April 2014).", "download.knownIssuesOne": "If your offline editor is crashing directly after Scratch is opened, install the Scratch 2 offline editor again (see step 2 above). This issue is due to a bug introduced in Adobe AIR version 14 (released April 2014).",
"download.knownIssuesTwo": "Graphic effects blocks (in \"Looks\") may slow down projects due to a known Flash bug.", "download.knownIssuesTwo": "Graphic effects blocks (in \"Looks\") may slow down projects due to a known Flash bug.",

View file

@ -1,10 +1,10 @@
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react'); const React = require('react');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const intlShape = require('../../lib/intl-shape');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx');

View file

@ -1,12 +1,13 @@
const bindAll = require('lodash.bindall'); const bindAll = require('lodash.bindall');
const classNames = require('classnames'); const classNames = require('classnames');
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react'); const React = require('react');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const api = require('../../lib/api'); const api = require('../../lib/api');
const intlShape = require('../../lib/intl-shape');
const {getLocale} = require('../../lib/locales.js');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const Tabs = require('../../components/tabs/tabs.jsx'); const Tabs = require('../../components/tabs/tabs.jsx');
@ -79,7 +80,7 @@ class Explore extends React.Component {
handleGetExploreMore () { handleGetExploreMore () {
const qText = `&q=${this.state.acceptableTabs[this.state.category]}` || '*'; const qText = `&q=${this.state.acceptableTabs[this.state.category]}` || '*';
const mode = `&mode=${(this.state.mode ? this.state.mode : 'trending')}`; const mode = `&mode=${(this.state.mode ? this.state.mode : 'trending')}`;
const locale = this.props.intl.locale; const locale = getLocale();
const queryString = const queryString =
`limit=${this.state.loadNumber}&offset=${this.state.offset}&language=${locale}${mode}${qText}`; `limit=${this.state.loadNumber}&offset=${this.state.offset}&language=${locale}${mode}${qText}`;

View file

@ -1,9 +1,9 @@
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const React = require('react'); const React = require('react');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const intlShape = require('../../lib/intl-shape');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const InformationPage = require('../../components/informationpage/informationpage.jsx'); const InformationPage = require('../../components/informationpage/informationpage.jsx');

View file

@ -1,9 +1,9 @@
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react'); const React = require('react');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const intlShape = require('../../lib/intl-shape');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx');

View file

@ -1,8 +1,6 @@
const bindAll = require('lodash.bindall'); const bindAll = require('lodash.bindall');
const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const React = require('react'); const React = require('react');
const Button = require('../../components/forms/button.jsx'); const Button = require('../../components/forms/button.jsx');
@ -13,6 +11,7 @@ const TTTModal = require('../../components/modal/ttt/modal.jsx');
const TTTTile = require('../../components/ttt-tile/ttt-tile.jsx'); const TTTTile = require('../../components/ttt-tile/ttt-tile.jsx');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const intlShape = require('../../lib/intl-shape');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const Tiles = require('./ttt.json'); const Tiles = require('./ttt.json');
@ -99,7 +98,7 @@ class Ideas extends React.Component {
<FormattedMessage id="ideas.gettingStartedTitle" /> <FormattedMessage id="ideas.gettingStartedTitle" />
</h2> </h2>
<p> <p>
<FormattedHTMLMessage id="ideas.gettingStartedText" /> <FormattedMessage id="ideas.gettingStartedText" />
</p> </p>
<a href="/projects/editor/?tutorial=getStarted"> <a href="/projects/editor/?tutorial=getStarted">
<Button className="ideas-button"> <Button className="ideas-button">
@ -119,7 +118,7 @@ class Ideas extends React.Component {
<FormattedMessage id="ideas.activityGuidesTitle" /> <FormattedMessage id="ideas.activityGuidesTitle" />
</h2> </h2>
<p> <p>
<FormattedHTMLMessage id="ideas.activityGuidesText" /> <FormattedMessage id="ideas.activityGuidesText" />
</p> </p>
</div> </div>
<MasonryGrid > <MasonryGrid >
@ -152,7 +151,7 @@ class Ideas extends React.Component {
<FormattedMessage id="ideas.cardsTitle" /> <FormattedMessage id="ideas.cardsTitle" />
</h2> </h2>
<p> <p>
<FormattedHTMLMessage id="ideas.cardsText" /> <FormattedMessage id="ideas.cardsText" />
</p> </p>
<a <a
href={this.props.intl.formatMessage({ href={this.props.intl.formatMessage({
@ -192,7 +191,7 @@ class Ideas extends React.Component {
<FormattedMessage id="ideas.starterProjectsTitle" /> <FormattedMessage id="ideas.starterProjectsTitle" />
</h2> </h2>
<p> <p>
<FormattedHTMLMessage id="ideas.starterProjectsText" /> <FormattedMessage id="ideas.starterProjectsText" />
</p> </p>
<p> <p>
<a href="/starter_projects"> <a href="/starter_projects">
@ -220,7 +219,10 @@ class Ideas extends React.Component {
<FormattedMessage id="ideas.desktopEditorHeader" /> <FormattedMessage id="ideas.desktopEditorHeader" />
</h3> </h3>
<p> <p>
<FormattedHTMLMessage id="ideas.desktopEditorBody" /> <FormattedMessage
id="ideas.desktopEditorBodyHTML"
values={{a: chunks => <a href="/download">{chunks}</a>}}
/>
</p> </p>
</div> </div>
<div className="tips-info-body mod-narrow"> <div className="tips-info-body mod-narrow">
@ -232,7 +234,13 @@ class Ideas extends React.Component {
<FormattedMessage id="ideas.questionsHeader" /> <FormattedMessage id="ideas.questionsHeader" />
</h3> </h3>
<p> <p>
<FormattedHTMLMessage id="ideas.questionsBody" /> <FormattedMessage
id="ideas.questionsBodyHTML"
values={{
faq: chunks => <a href="/info/faq">{chunks}</a>,
forum: chunks => <a href="/discuss/7/">{chunks}</a>
}}
/>
</p> </p>
</div> </div>
</FlexRow> </FlexRow>

View file

@ -29,9 +29,10 @@
"ideas.educatorGuide": "Educator Guide", "ideas.educatorGuide": "Educator Guide",
"ideas.desktopEditorHeader": "Scratch App Download", "ideas.desktopEditorHeader": "Scratch App Download",
"ideas.desktopEditorBody": "To create projects without an Internet connection, you can <a href=\"/download\">download the Scratch app</a>.", "ideas.desktopEditorBody": "To create projects without an Internet connection, you can <a href=\"/download\">download the Scratch app</a>.",
"ideas.desktopEditorBodyHTML": "To create projects without an Internet connection, you can <a>download the Scratch app</a>.",
"ideas.questionsHeader": "Questions", "ideas.questionsHeader": "Questions",
"ideas.questionsBody": "Have more questions? See the <a href=\"/info/faq\">Frequently Asked Questions</a> or visit the <a href=\"/discuss/7/\">Help with Scripts Forum</a>.", "ideas.questionsBody": "Have more questions? See the <a href=\"/info/faq\">Frequently Asked Questions</a> or visit the <a href=\"/discuss/7/\">Help with Scripts Forum</a>.",
"ideas.questionsBodyHTML": "Have more questions? See the <faq>Frequently Asked Questions</faq> or visit the <forum>Help with Scripts Forum</forum>.",
"ideas.cardsPurchase": "Purchase Printed Set", "ideas.cardsPurchase": "Purchase Printed Set",
"ideas.MakeItFlyTitle": "Make It Fly", "ideas.MakeItFlyTitle": "Make It Fly",
"ideas.MakeItFlyDescription": "Choose any character and make it fly!", "ideas.MakeItFlyDescription": "Choose any character and make it fly!",

View file

@ -3,10 +3,10 @@ const classNames = require('classnames');
const connect = require('react-redux').connect; const connect = require('react-redux').connect;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const intlShape = require('../../../lib/intl-shape');
const Comment = require('../../../components/comment/comment.jsx'); const Comment = require('../../../components/comment/comment.jsx');
const FlexRow = require('../../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../../components/flex-row/flex-row.jsx');
const SocialMessage = require('../../../components/social-message/social-message.jsx'); const SocialMessage = require('../../../components/social-message/social-message.jsx');

View file

@ -1,10 +1,10 @@
const classNames = require('classnames'); const classNames = require('classnames');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const intlShape = require('../../../lib/intl-shape');
const SocialMessage = require('../../../components/social-message/social-message.jsx'); const SocialMessage = require('../../../components/social-message/social-message.jsx');
const CuratorInviteMessage = props => ( const CuratorInviteMessage = props => (

View file

@ -1,10 +1,10 @@
const FormattedDate = require('react-intl').FormattedDate; const FormattedDate = require('react-intl').FormattedDate;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const intlShape = require('../../../lib/intl-shape');
const Button = require('../../../components/forms/button.jsx'); const Button = require('../../../components/forms/button.jsx');
const FlexRow = require('../../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../../components/flex-row/flex-row.jsx');

View file

@ -1,10 +1,10 @@
const classNames = require('classnames'); const classNames = require('classnames');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const intlShape = require('../../../lib/intl-shape');
const SocialMessage = require('../../../components/social-message/social-message.jsx'); const SocialMessage = require('../../../components/social-message/social-message.jsx');
const UserJoinMessage = props => ( const UserJoinMessage = props => (

View file

@ -2,10 +2,10 @@ const bindAll = require('lodash.bindall');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const FormattedNumber = require('react-intl').FormattedNumber; const FormattedNumber = require('react-intl').FormattedNumber;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const intlShape = require('../../lib/intl-shape');
const Button = require('../../components/forms/button.jsx'); const Button = require('../../components/forms/button.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx');
const Form = require('../../components/forms/form.jsx'); const Form = require('../../components/forms/form.jsx');

View file

@ -1,10 +1,9 @@
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react'); const React = require('react');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const intlShape = require('../../lib/intl-shape');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx');

View file

@ -6,11 +6,11 @@ const classNames = require('classnames');
const FlexRow = require('../../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../../components/flex-row/flex-row.jsx');
const Avatar = require('../../../components/avatar/avatar.jsx'); const Avatar = require('../../../components/avatar/avatar.jsx');
const EmojiText = require('../../../components/emoji-text/emoji-text.jsx'); const EmojiText = require('../../../components/emoji-text/emoji-text.jsx');
const FormattedRelative = require('react-intl').FormattedRelative;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const ComposeComment = require('./compose-comment.jsx'); const ComposeComment = require('./compose-comment.jsx');
const DeleteCommentModal = require('../../../components/modal/comments/delete-comment.jsx'); const DeleteCommentModal = require('../../../components/modal/comments/delete-comment.jsx');
const ReportCommentModal = require('../../../components/modal/comments/report-comment.jsx'); const ReportCommentModal = require('../../../components/modal/comments/report-comment.jsx');
const RelativeTime = require('../../../components/relative-time/relative-time.jsx');
const decorateText = require('../../../lib/decorate-text.jsx'); const decorateText = require('../../../lib/decorate-text.jsx');
require('./comment.scss'); require('./comment.scss');
@ -224,7 +224,7 @@ class Comment extends React.Component {
</span> </span>
<FlexRow className="comment-bottom-row"> <FlexRow className="comment-bottom-row">
<span className="comment-time"> <span className="comment-time">
<FormattedRelative value={new Date(datetimeCreated)} /> <RelativeTime value={new Date(datetimeCreated)} />
</span> </span>
{(canReply && visible) ? ( {(canReply && visible) ? (
<span <span

View file

@ -3,9 +3,9 @@ const React = require('react');
const bindAll = require('lodash.bindall'); const bindAll = require('lodash.bindall');
const connect = require('react-redux').connect; const connect = require('react-redux').connect;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const api = require('../../lib/api'); const api = require('../../lib/api');
const intlShape = require('../../lib/intl-shape');
const previewActions = require('../../redux/preview'); const previewActions = require('../../redux/preview');
class FormsyProjectUpdater extends React.Component { class FormsyProjectUpdater extends React.Component {

View file

@ -1,8 +1,8 @@
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const intlShape = require('react-intl').intlShape;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const intlShape = require('../../lib/intl-shape');
const MediaQuery = require('react-responsive').default; const MediaQuery = require('react-responsive').default;
const React = require('react'); const React = require('react');
const Formsy = require('formsy-react').default; const Formsy = require('formsy-react').default;

View file

@ -2,8 +2,9 @@ const React = require('react');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const {FormattedMessage, injectIntl, intlShape} = require('react-intl'); const {FormattedMessage, injectIntl} = require('react-intl');
const intlShape = require('../../lib/intl-shape');
const InformationPage = require('../../components/informationpage/informationpage.jsx'); const InformationPage = require('../../components/informationpage/informationpage.jsx');
const helpEmailLink = ( const helpEmailLink = (

View file

@ -1,8 +1,8 @@
const React = require('react'); const React = require('react');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const intlShape = require('../../lib/intl-shape');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const InformationPage = require('../../components/informationpage/informationpage.jsx'); const InformationPage = require('../../components/informationpage/informationpage.jsx');

View file

@ -2,11 +2,12 @@ const bindAll = require('lodash.bindall');
const connect = require('react-redux').connect; const connect = require('react-redux').connect;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const api = require('../../lib/api'); const api = require('../../lib/api');
const intlShape = require('../../lib/intl-shape');
const {getLocale} = require('../../lib/locales.js');
const Button = require('../../components/forms/button.jsx'); const Button = require('../../components/forms/button.jsx');
const Form = require('../../components/forms/form.jsx'); const Form = require('../../components/forms/form.jsx');
const Grid = require('../../components/grid/grid.jsx'); const Grid = require('../../components/grid/grid.jsx');
@ -126,7 +127,7 @@ class Search extends React.Component {
} }
handleGetSearchMore () { handleGetSearchMore () {
const termText = this.encodeSearchTerm(); const termText = this.encodeSearchTerm();
const locale = this.props.intl.locale; const locale = getLocale();
const loadNumber = this.state.loadNumber; const loadNumber = this.state.loadNumber;
const offset = this.state.offset; const offset = this.state.offset;
const mode = this.state.mode; const mode = this.state.mode;

View file

@ -1,12 +1,12 @@
const bindAll = require('lodash.bindall'); const bindAll = require('lodash.bindall');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const MediaQuery = require('react-responsive').default; const MediaQuery = require('react-responsive').default;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const frameless = require('../../lib/frameless'); const frameless = require('../../lib/frameless');
const intlShape = require('../../lib/intl-shape');
const sessionActions = require('../../redux/session.js'); const sessionActions = require('../../redux/session.js');
const shuffle = require('../../lib/shuffle.js').shuffle; const shuffle = require('../../lib/shuffle.js').shuffle;
@ -328,7 +328,6 @@ class SplashPresentation extends React.Component { // eslint-disable-line react/
render () { render () {
const featured = this.renderHomepageRows(); const featured = this.renderHomepageRows();
const formatHTMLMessage = this.props.intl.formatHTMLMessage;
const formatMessage = this.props.intl.formatMessage; const formatMessage = this.props.intl.formatMessage;
const messages = { const messages = {
@ -343,8 +342,8 @@ class SplashPresentation extends React.Component { // eslint-disable-line react/
'intro.forParents': formatMessage({id: 'intro.forParents'}), 'intro.forParents': formatMessage({id: 'intro.forParents'}),
'intro.join': formatMessage({id: 'intro.join'}), 'intro.join': formatMessage({id: 'intro.join'}),
'intro.startCreating': formatMessage({id: 'intro.startCreating'}), 'intro.startCreating': formatMessage({id: 'intro.startCreating'}),
'intro.tagLine1': formatHTMLMessage({id: 'intro.tagLine1'}), 'intro.tagLine1': formatMessage({id: 'intro.tagLine1'}),
'intro.tagLine2': formatHTMLMessage({id: 'intro.tagLine2'}), 'intro.tagLine2': formatMessage({id: 'intro.tagLine2'}),
'intro.watchVideo': formatMessage({id: 'intro.watchVideo'}), 'intro.watchVideo': formatMessage({id: 'intro.watchVideo'}),
'teacherbanner.greeting': formatMessage({id: 'teacherbanner.greeting'}), 'teacherbanner.greeting': formatMessage({id: 'teacherbanner.greeting'}),
'teacherbanner.subgreeting': formatMessage({id: 'teacherbanner.subgreeting'}), 'teacherbanner.subgreeting': formatMessage({id: 'teacherbanner.subgreeting'}),

View file

@ -1,9 +1,9 @@
const React = require('react'); const React = require('react');
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const intlShape = require('../../lib/intl-shape');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const Carousel = require('../../components/carousel/carousel.jsx'); const Carousel = require('../../components/carousel/carousel.jsx');
const Box = require('../../components/box/box.jsx'); const Box = require('../../components/box/box.jsx');

View file

@ -3,10 +3,10 @@ const connect = require('react-redux').connect;
const defaults = require('lodash.defaultsdeep'); const defaults = require('lodash.defaultsdeep');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const injectIntl = require('react-intl').injectIntl;
const api = require('../../lib/api'); const api = require('../../lib/api');
const injectIntl = require('../../lib/intl.jsx').injectIntl; const intlShape = require('../../lib/intl-shape');
const intlShape = require('../../lib/intl.jsx').intlShape;
const sessionStatus = require('../../redux/session').Status; const sessionStatus = require('../../redux/session').Status;
const navigationActions = require('../../redux/navigation.js'); const navigationActions = require('../../redux/navigation.js');

View file

@ -2,10 +2,10 @@ const bindAll = require('lodash.bindall');
const defaults = require('lodash.defaultsdeep'); const defaults = require('lodash.defaultsdeep');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const injectIntl = require('react-intl').injectIntl;
const api = require('../../lib/api'); const api = require('../../lib/api');
const injectIntl = require('../../lib/intl.jsx').injectIntl; const intlShape = require('../../lib/intl-shape');
const intlShape = require('../../lib/intl.jsx').intlShape;
const route = require('../../lib/route'); const route = require('../../lib/route');
const Deck = require('../../components/deck/deck.jsx'); const Deck = require('../../components/deck/deck.jsx');

View file

@ -2,8 +2,9 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import {FormattedMessage, injectIntl, intlShape} from 'react-intl'; import {FormattedMessage, injectIntl} from 'react-intl';
import intlShape from '../../../lib/intl-shape';
import {selectStudioTitle, selectStudioDescription, selectStudioImage} from '../../../redux/studio'; import {selectStudioTitle, selectStudioDescription, selectStudioImage} from '../../../redux/studio';
import Modal from '../../../components/modal/base/modal.jsx'; import Modal from '../../../components/modal/base/modal.jsx';

View file

@ -2,8 +2,9 @@ import React, {useState} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import {FormattedMessage} from 'react-intl'; import {FormattedMessage} from 'react-intl';
const {injectIntl, intlShape} = require('react-intl'); const {injectIntl} = require('react-intl');
import intlShape from '../../../lib/intl-shape.js';
import ModalInnerContent from '../../../components/modal/base/modal-inner-content.jsx'; import ModalInnerContent from '../../../components/modal/base/modal-inner-content.jsx';
import TransferHostTile from './transfer-host-tile.jsx'; import TransferHostTile from './transfer-host-tile.jsx';

View file

@ -3,10 +3,11 @@ import React, {useState} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import classNames from 'classnames'; import classNames from 'classnames';
import {FormattedMessage, intlShape, injectIntl} from 'react-intl'; import {FormattedMessage, injectIntl} from 'react-intl';
import {useAlertContext} from '../../components/alert/alert-context'; import {useAlertContext} from '../../components/alert/alert-context';
import {Errors, inviteCurator} from './lib/studio-member-actions'; import {Errors, inviteCurator} from './lib/studio-member-actions';
import intlShape from '../../lib/intl-shape';
import ValidationMessage from '../../components/forms/validation-message.jsx'; import ValidationMessage from '../../components/forms/validation-message.jsx';
const errorToMessageId = error => { const errorToMessageId = error => {

View file

@ -1,5 +1,6 @@
{ {
"teacherRegistration.nameStepTitle": "First &amp; Last Name", "teacherRegistration.nameStepTitle": "First &amp; Last Name",
"teacherRegistration.nameStepTitleNew": "First & Last Name",
"teacherRegistration.nameStepDescription": "Your name will not be displayed publicly, and will be kept confidential and secure.", "teacherRegistration.nameStepDescription": "Your name will not be displayed publicly, and will be kept confidential and secure.",
"teacherRegistration.firstName": "First Name", "teacherRegistration.firstName": "First Name",
"teacherRegistration.lastName": "Last Name", "teacherRegistration.lastName": "Last Name",

View file

@ -3,10 +3,10 @@ const connect = require('react-redux').connect;
const defaults = require('lodash.defaultsdeep'); const defaults = require('lodash.defaultsdeep');
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const injectIntl = require('react-intl').injectIntl;
const api = require('../../lib/api'); const api = require('../../lib/api');
const injectIntl = require('../../lib/intl.jsx').injectIntl; const intlShape = require('../../lib/intl-shape');
const intlShape = require('../../lib/intl.jsx').intlShape;
const sessionActions = require('../../redux/session.js'); const sessionActions = require('../../redux/session.js');
const Deck = require('../../components/deck/deck.jsx'); const Deck = require('../../components/deck/deck.jsx');

View file

@ -1,14 +1,16 @@
const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const React = require('react'); const React = require('react');
const Page = require('../../../components/page/www/page.jsx'); const Page = require('../../../components/page/www/page.jsx');
const intlShape = require('../../../lib/intl-shape');
const render = require('../../../lib/render.jsx'); const render = require('../../../lib/render.jsx');
const InformationPage = require('../../../components/informationpage/informationpage.jsx'); const InformationPage = require('../../../components/informationpage/informationpage.jsx');
const RequestFormLink = chunks => <a href="/educators/register">{chunks}</a>;
const ScratchEdLink = chunks => <a href="http://scratched.gse.harvard.edu/">{chunks}</a>;
const TeacherFaq = props => ( const TeacherFaq = props => (
<InformationPage title={props.intl.formatMessage({id: 'teacherfaq.title'})}> <InformationPage title={props.intl.formatMessage({id: 'teacherfaq.title'})}>
<div className="inner info-inner"> <div className="inner info-inner">
@ -47,7 +49,10 @@ const TeacherFaq = props => (
width="565" width="565"
/> />
<dt><FormattedMessage id="teacherfaq.teacherSignUpTitle" /></dt> <dt><FormattedMessage id="teacherfaq.teacherSignUpTitle" /></dt>
<dd><FormattedHTMLMessage id="teacherfaq.teacherSignUpBody" /></dd> <dd><FormattedMessage
id="teacherfaq.teacherSignUpBodyHTML"
values={{a: RequestFormLink}}
/></dd>
<dt><FormattedMessage id="teacherfaq.classMultipleTeachersTitle" /></dt> <dt><FormattedMessage id="teacherfaq.classMultipleTeachersTitle" /></dt>
<dd><FormattedMessage id="teacherfaq.classMultipleTeachersBody" /></dd> <dd><FormattedMessage id="teacherfaq.classMultipleTeachersBody" /></dd>
<dt><FormattedMessage id="teacherfaq.teacherPersonalTitle" /></dt> <dt><FormattedMessage id="teacherfaq.teacherPersonalTitle" /></dt>
@ -76,9 +81,12 @@ const TeacherFaq = props => (
/> />
</dd> </dd>
<dt><FormattedMessage id="teacherfaq.teacherEdTitle" /></dt> <dt><FormattedMessage id="teacherfaq.teacherEdTitle" /></dt>
<dd><FormattedHTMLMessage id="teacherfaq.teacherEdBody" /></dd> <dd><FormattedMessage
id="teacherfaq.teacherEdBodyHTML"
values={{a: ScratchEdLink}}
/></dd>
<dt><FormattedMessage id="teacherfaq.teacherFeaturesTitle" /></dt> <dt><FormattedMessage id="teacherfaq.teacherFeaturesTitle" /></dt>
<dd><FormattedHTMLMessage id="teacherfaq.teacherFeaturesBody" /></dd> <dd><FormattedMessage id="teacherfaq.teacherFeaturesBody" /></dd>
<ul> <ul>
<li><FormattedMessage id="teacherfaq.teacherFeaturesConvert" /></li> <li><FormattedMessage id="teacherfaq.teacherFeaturesConvert" /></li>
<li><FormattedMessage id="teacherfaq.teacherMoveStudents" /></li> <li><FormattedMessage id="teacherfaq.teacherMoveStudents" /></li>
@ -86,7 +94,7 @@ const TeacherFaq = props => (
<li><FormattedMessage id="teacherfaq.teacherLMSs" /></li> <li><FormattedMessage id="teacherfaq.teacherLMSs" /></li>
<li><FormattedMessage id="teacherfaq.teacherLimitStudent" /></li> <li><FormattedMessage id="teacherfaq.teacherLimitStudent" /></li>
</ul> </ul>
<dd><FormattedHTMLMessage id="teacherfaq.teacherWillNotImplement" /></dd> <dd><FormattedMessage id="teacherfaq.teacherWillNotImplement" /></dd>
</dl> </dl>
</section> </section>
<section id="student-accounts"> <section id="student-accounts">
@ -187,7 +195,7 @@ const TeacherFaq = props => (
<dd><FormattedMessage id="teacherfaq.commBlockGamesBody1" /></dd> <dd><FormattedMessage id="teacherfaq.commBlockGamesBody1" /></dd>
<dd><FormattedMessage id="teacherfaq.commBlockGamesBody2" /></dd> <dd><FormattedMessage id="teacherfaq.commBlockGamesBody2" /></dd>
<dt><FormattedMessage id="teacherfaq.commInappropriateTitle" /></dt> <dt><FormattedMessage id="teacherfaq.commInappropriateTitle" /></dt>
<dd><FormattedHTMLMessage id="teacherfaq.commInappropriateBody" /></dd> <dd><FormattedMessage id="teacherfaq.commInappropriateBody" /></dd>
</dl> </dl>
</section> </section>
</div> </div>

View file

@ -8,6 +8,7 @@
"teacherfaq.teacherWhatBody": "A Scratch Teacher Account provides educators with additional features to manage student participation on Scratch, including the ability to create student accounts, organize student projects into studios, and monitor student comments. Learn more about Teacher Accounts in the video below:", "teacherfaq.teacherWhatBody": "A Scratch Teacher Account provides educators with additional features to manage student participation on Scratch, including the ability to create student accounts, organize student projects into studios, and monitor student comments. Learn more about Teacher Accounts in the video below:",
"teacherfaq.teacherSignUpTitle": "How do I request a teacher account?", "teacherfaq.teacherSignUpTitle": "How do I request a teacher account?",
"teacherfaq.teacherSignUpBody": "To request a Teacher Account, please go to the teacher account <a href=\"/educators/register\">request form</a>.", "teacherfaq.teacherSignUpBody": "To request a Teacher Account, please go to the teacher account <a href=\"/educators/register\">request form</a>.",
"teacherfaq.teacherSignUpBodyHTML": "To request a Teacher Account, please go to the teacher account <a>request form</a>.",
"teacherfaq.classMultipleTeachersTitle": "Can a class have multiple teachers?", "teacherfaq.classMultipleTeachersTitle": "Can a class have multiple teachers?",
"teacherfaq.classMultipleTeachersBody": "A class can only have one teacher account associated with it.", "teacherfaq.classMultipleTeachersBody": "A class can only have one teacher account associated with it.",
"teacherfaq.teacherPersonalTitle": "Why do you need to know my personal information during registration?", "teacherfaq.teacherPersonalTitle": "Why do you need to know my personal information during registration?",
@ -16,6 +17,7 @@
"teacherfaq.teacherGoogleBody": "No, Scratch does not connect with any classroom management services.", "teacherfaq.teacherGoogleBody": "No, Scratch does not connect with any classroom management services.",
"teacherfaq.teacherEdTitle": "Are Scratch Teacher accounts linked to ScratchEd accounts?", "teacherfaq.teacherEdTitle": "Are Scratch Teacher accounts linked to ScratchEd accounts?",
"teacherfaq.teacherEdBody": "No, Scratch Teacher accounts are not linked to <a href=\"http://scratched.gse.harvard.edu/\">ScratchEd</a> accounts.", "teacherfaq.teacherEdBody": "No, Scratch Teacher accounts are not linked to <a href=\"http://scratched.gse.harvard.edu/\">ScratchEd</a> accounts.",
"teacherfaq.teacherEdBodyHTML": "No, Scratch Teacher accounts are not linked to <a>ScratchEd</a> accounts.",
"teacherfaq.teacherFeaturesTitle": "Does this feature exist, and if not, can you please add it?", "teacherfaq.teacherFeaturesTitle": "Does this feature exist, and if not, can you please add it?",
"teacherfaq.teacherFeaturesBody": "Many features are commonly requested, including:", "teacherfaq.teacherFeaturesBody": "Many features are commonly requested, including:",
"teacherfaq.teacherFeaturesConvert": "Converting existing Scratch Accounts into Student Accounts", "teacherfaq.teacherFeaturesConvert": "Converting existing Scratch Accounts into Student Accounts",

View file

@ -1,6 +1,5 @@
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const React = require('react'); const React = require('react');
@ -10,6 +9,7 @@ const TitleBanner = require('../../../components/title-banner/title-banner.jsx')
const Button = require('../../../components/forms/button.jsx'); const Button = require('../../../components/forms/button.jsx');
const Page = require('../../../components/page/www/page.jsx'); const Page = require('../../../components/page/www/page.jsx');
const intlShape = require('../../../lib/intl-shape');
const render = require('../../../lib/render.jsx'); const render = require('../../../lib/render.jsx');
require('./landing.scss'); require('./landing.scss');
@ -275,7 +275,7 @@ const Landing = props => (
id="teacherlanding.codeClub" id="teacherlanding.codeClub"
values={{ values={{
codeClubLink: ( codeClubLink: (
<a href="https://projects.raspberrypi.org/en/codeclub/scratch-module-1"> <a href="https://projects.raspberrypi.org/en/pathways/scratch-intro">
<FormattedMessage id="teacherlanding.codeClubLink" /> <FormattedMessage id="teacherlanding.codeClubLink" />
</a> </a>
) )

View file

@ -8,6 +8,7 @@
"wedoLegacy.downloadWin": "Download for Windows 10+", "wedoLegacy.downloadWin": "Download for Windows 10+",
"wedoLegacy.setupTitle": "2. Setup & Help", "wedoLegacy.setupTitle": "2. Setup & Help",
"wedoLegacy.setupText": "Connect your WeDo 2.0 by following the steps in the <a href=\"/projects/editor/?tip_bar=ext2\">Tips Window</a>", "wedoLegacy.setupText": "Connect your WeDo 2.0 by following the steps in the <a href=\"/projects/editor/?tip_bar=ext2\">Tips Window</a>",
"wedoLegacy.setupTextHTML": "Connect your WeDo 2.0 by following the steps in the <a>Tips Window</a>",
"wedoLegacy.createTitle": "3. Create", "wedoLegacy.createTitle": "3. Create",
"wedoLegacy.createText": "Use the WeDo extension blocks to turn on lights, control motors, and make your project interactive", "wedoLegacy.createText": "Use the WeDo extension blocks to turn on lights, control motors, and make your project interactive",
"wedoLegacy.wedo2SetupInstructions": "WeDo 2.0 Setup Instructions", "wedoLegacy.wedo2SetupInstructions": "WeDo 2.0 Setup Instructions",

View file

@ -1,4 +1,3 @@
const FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react'); const React = require('react');
@ -7,6 +6,8 @@ const render = require('../../lib/render.jsx');
require('./wedo2.scss'); require('./wedo2.scss');
const TipsWindowLink = chunks => <a href="/projects/editor/?tip_bar=ext2">{chunks}</a>;
const Wedo2 = () => ( const Wedo2 = () => (
<div className="wedo"> <div className="wedo">
<div className="top-banner"> <div className="top-banner">
@ -40,7 +41,7 @@ const Wedo2 = () => (
<FormattedMessage id="wedoLegacy.installTitle" /> <FormattedMessage id="wedoLegacy.installTitle" />
</h4> </h4>
<p> <p>
<FormattedHTMLMessage id="wedoLegacy.installText" /> <FormattedMessage id="wedoLegacy.installText" />
<br /> <br />
<a href="https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1084869222&mt=12"> <a href="https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1084869222&mt=12">
<FormattedMessage id="wedoLegacy.downloadMac" /> <FormattedMessage id="wedoLegacy.downloadMac" />
@ -57,7 +58,10 @@ const Wedo2 = () => (
<FormattedMessage id="wedoLegacy.setupTitle" /> <FormattedMessage id="wedoLegacy.setupTitle" />
</h4> </h4>
<p> <p>
<FormattedHTMLMessage id="wedoLegacy.setupText" /> <FormattedMessage
id="wedoLegacy.setupTextHTML"
values={{a: TipsWindowLink}}
/>
</p> </p>
</div> </div>
<div className="column"> <div className="column">

View file

@ -1,9 +1,9 @@
const injectIntl = require('react-intl').injectIntl; const injectIntl = require('react-intl').injectIntl;
const intlShape = require('react-intl').intlShape;
const FormattedMessage = require('react-intl').FormattedMessage; const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react'); const React = require('react');
const Page = require('../../components/page/www/page.jsx'); const Page = require('../../components/page/www/page.jsx');
const intlShape = require('../../lib/intl-shape');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const FlexRow = require('../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx');

View file

@ -1,35 +1,50 @@
/* /*
* Helpers for using enzyme and react-test-renderer with react-intl * Helpers for using enzyme and react-test-renderer with react-intl
* Directly from https://github.com/yahoo/react-intl/wiki/Testing-with-React-Intl
*/ */
import React from 'react'; import React from 'react';
import renderer from 'react-test-renderer'; import renderer from 'react-test-renderer';
import {IntlProvider, intlShape} from 'react-intl'; import {createIntl, IntlProvider} from 'react-intl';
import {mount, shallow} from 'enzyme'; import {mount, shallow} from 'enzyme';
import intlShape from '../../src/lib/intl-shape';
const intlProvider = new IntlProvider({locale: 'en'}, {}); const shallowWithIntl = (node, {context} = {}) => {
const {intl} = intlProvider.getChildContext(); return shallow(
node,
{
context: Object.assign({}, context),
wrappingComponent: IntlProvider,
wrappingComponentProps: {
locale: 'en',
messages: {}
}
}
).dive();
};
const nodeWithIntlProp = node => React.cloneElement(node, {intl}); const mountWithIntl = (node, {context, childContextTypes} = {}) => {
const intl = createIntl({locale: 'en', messages: {}});
const shallowWithIntl = (node, {context} = {}) => shallow( return mount(
nodeWithIntlProp(node), node,
{ {
context: Object.assign({}, context, {intl}) context: Object.assign({}, context, {intl}),
} childContextTypes: Object.assign({}, {intl: intlShape}, childContextTypes),
); wrappingComponent: IntlProvider,
wrappingComponentProps: {
const mountWithIntl = (node, {context, childContextTypes} = {}) => mount( locale: 'en',
nodeWithIntlProp(node), messages: {}
{ }
context: Object.assign({}, context, {intl}), }
childContextTypes: Object.assign({}, {intl: intlShape}, childContextTypes) );
} };
);
// react-test-renderer component for use with snapshot testing // react-test-renderer component for use with snapshot testing
const componentWithIntl = (children, props = {locale: 'en'}) => renderer.create( const componentWithIntl = (children, props = {locale: 'en'}) => renderer.create(
<IntlProvider {...props}>{children}</IntlProvider> <IntlProvider
textComponent="span"
{...props}
>
{children}
</IntlProvider>
); );
export { export {

View file

@ -1,10 +1,10 @@
const React = require('react'); const React = require('react');
const {shallowWithIntl} = require('../../helpers/intl-helpers.jsx'); const {shallow} = require('enzyme');
const CommentingStatus = require('../../../src/components/commenting-status/commenting-status.jsx'); const CommentingStatus = require('../../../src/components/commenting-status/commenting-status.jsx');
describe('CommentingStatus', () => { describe('CommentingStatus', () => {
test('Basic render', () => { test('Basic render', () => {
const component = shallowWithIntl( const component = shallow(
<CommentingStatus /> <CommentingStatus />
); );
expect(component.find('div.commenting-status').exists()).toBe(true); expect(component.find('div.commenting-status').exists()).toBe(true);
@ -12,7 +12,7 @@ describe('CommentingStatus', () => {
}); });
test('ClassNames added', () => { test('ClassNames added', () => {
const component = shallowWithIntl( const component = shallow(
<CommentingStatus <CommentingStatus
className="class1" className="class1"
innerClassName="class2" innerClassName="class2"
@ -23,7 +23,7 @@ describe('CommentingStatus', () => {
}); });
test('Children added', () => { test('Children added', () => {
const component = shallowWithIntl( const component = shallow(
<CommentingStatus> <CommentingStatus>
<img className="myChildDiv" /> <img className="myChildDiv" />
</CommentingStatus> </CommentingStatus>

View file

@ -48,7 +48,7 @@ describe('Compose Comment test', () => {
/> />
, {context: {store}} , {context: {store}}
); );
return wrapper.dive(); // unwrap redux connect(injectIntl(ComposeComment)) return wrapper;
}; };
test('status is EDITING when props do not contain a muteStatus ', () => { test('status is EDITING when props do not contain a muteStatus ', () => {

View file

@ -13,7 +13,7 @@ describe('EmailConfirmationBanner', () => {
expect(component.text()).not.toContain('MockEmailConfirmationModal'); expect(component.text()).not.toContain('MockEmailConfirmationModal');
const confirmWrapper = component.find({id: 'emailConfirmationBanner.confirm'}); const confirmWrapper = component.find({id: 'emailConfirmationBanner.confirm'});
const confirmLink = mountWithIntl(confirmWrapper.instance().props.values.confirmLink); const confirmLink = mountWithIntl(confirmWrapper.props().values.confirmLink);
confirmLink.simulate('click'); confirmLink.simulate('click');
component.update(); component.update();

View file

@ -39,7 +39,7 @@ describe('Modal', () => {
); );
const tipsLinkWrapper = component.find({id: 'emailConfirmationModal.havingTrouble'}); const tipsLinkWrapper = component.find({id: 'emailConfirmationModal.havingTrouble'});
const tipsLink = mountWithIntl(tipsLinkWrapper.instance().props.values.tipsLink); const tipsLink = mountWithIntl(tipsLinkWrapper.props().values.tipsLink);
tipsLink.simulate('click'); tipsLink.simulate('click');
expect(component.text()).toContain('emailConfirmationModal.confirmingTips'); expect(component.text()).toContain('emailConfirmationModal.confirmingTips');
}); });

View file

@ -9,6 +9,8 @@ jest.mock('../../../src/views/studio/studio-comment.js', () => (
)); ));
describe('Studio comments', () => { describe('Studio comments', () => {
const testComments = [{id: 123, author: {}, datetime_created: new Date().toISOString()}];
test('if there are no comments, they get loaded', () => { test('if there are no comments, they get loaded', () => {
const loadComments = jest.fn(); const loadComments = jest.fn();
const component = mountWithIntl( const component = mountWithIntl(
@ -25,7 +27,7 @@ describe('Studio comments', () => {
// When updated to have comments, load is not called again // When updated to have comments, load is not called again
loadComments.mockClear(); loadComments.mockClear();
component.setProps({comments: [{id: 123, author: {}}]}); component.setProps({comments: testComments});
component.update(); component.update();
expect(loadComments).not.toHaveBeenCalled(); expect(loadComments).not.toHaveBeenCalled();
@ -42,7 +44,7 @@ describe('Studio comments', () => {
<StudioComments <StudioComments
hasFetchedSession hasFetchedSession
isAdmin={false} isAdmin={false}
comments={[{id: 123, author: {}}]} comments={testComments}
handleResetComments={resetComments} handleResetComments={resetComments}
/> />
); );
@ -69,7 +71,7 @@ describe('Studio comments', () => {
<StudioComments <StudioComments
isAdmin isAdmin
hasFetchedSession hasFetchedSession
comments={[{id: 123, author: {}}]} comments={testComments}
handleResetComments={resetComments} handleResetComments={resetComments}
/> />
); );
@ -81,7 +83,7 @@ describe('Studio comments', () => {
<StudioComments <StudioComments
hasFetchedSession hasFetchedSession
isAdmin={false} isAdmin={false}
comments={[{id: 123, author: {}}]} comments={testComments}
shouldShowCommentsList={false} shouldShowCommentsList={false}
/> />
); );
@ -94,7 +96,7 @@ describe('Studio comments', () => {
<StudioComments <StudioComments
hasFetchedSession hasFetchedSession
isAdmin={false} isAdmin={false}
comments={[{id: 123, author: {}}]} comments={testComments}
shouldShowCommentsList shouldShowCommentsList
/> />
@ -110,7 +112,7 @@ describe('Studio comments', () => {
<StudioComments <StudioComments
hasFetchedSession hasFetchedSession
isAdmin={false} isAdmin={false}
comments={[{id: 123, author: {}}]} comments={testComments}
shouldShowCommentsList shouldShowCommentsList
singleCommentId singleCommentId
/> />
@ -129,7 +131,7 @@ describe('Studio comments', () => {
<StudioComments <StudioComments
hasFetchedSession hasFetchedSession
isAdmin={false} isAdmin={false}
comments={[{id: 123, author: {}}]} comments={testComments}
shouldShowCommentsList={false} shouldShowCommentsList={false}
singleCommentId singleCommentId
/> />
@ -146,7 +148,7 @@ describe('Studio comments', () => {
<StudioComments <StudioComments
hasFetchedSession={false} hasFetchedSession={false}
isAdmin={false} isAdmin={false}
comments={[{id: 123, author: {}}]} comments={testComments}
shouldShowCommentsGloballyOffError shouldShowCommentsGloballyOffError
/> />
); );
@ -159,7 +161,7 @@ describe('Studio comments', () => {
<StudioComments <StudioComments
hasFetchedSession={false} hasFetchedSession={false}
isAdmin={false} isAdmin={false}
comments={[{id: 123, author: {}}]} comments={testComments}
shouldShowCommentsGloballyOffError={false} shouldShowCommentsGloballyOffError={false}
/> />
); );

View file

@ -0,0 +1,63 @@
const {selectUnit} = require('../../../src/lib/select-unit');
describe('unit test lib/select-unit.js', () => {
test('selects seconds', () => {
const result = selectUnit(
new Date('2023-02-03T12:00:00.000Z'),
new Date('2023-02-03T12:00:05.000Z')
);
expect(result.unit).toEqual('second');
expect(result.value).toEqual(-5);
});
test('selects minutes', () => {
const result = selectUnit(
new Date('2023-02-03T12:01:10.000Z'),
new Date('2023-02-03T12:00:00.000Z')
);
expect(result.unit).toEqual('minute');
expect(result.value).toEqual(1);
});
test('selects hours', () => {
const result = selectUnit(
new Date('2023-02-03T12:10:00.000Z'),
new Date('2023-02-03T14:00:00.000Z')
);
expect(result.unit).toEqual('hour');
expect(result.value).toEqual(-1);
});
test('selects days', () => {
const result = selectUnit(
new Date('2023-02-03T12:00:00.000Z'),
new Date('2023-03-02T12:00:00.000Z')
);
expect(result.unit).toEqual('day');
expect(result.value).toEqual(-27);
});
test('selects months', () => {
const result = selectUnit(
new Date('2023-02-03T12:00:00.000Z'),
new Date('2024-02-02T12:00:00.000Z')
);
expect(result.unit).toEqual('month');
expect(result.value).toEqual(-11);
});
test('selects years', () => {
const result = selectUnit(
new Date('2023-02-03T12:00:00.000Z'),
new Date('2025-02-04T12:00:00.000Z')
);
expect(result.unit).toEqual('year');
expect(result.value).toEqual(-2);
});
});