feat: Persian language support added (#553)
* fix: corrected typos justify-contnt-center -> justify-content-center Visiblity -> Visibility Wraper -> Wrapper closeAssetinfo -> closeAssetInfo RestictDatesInput -> RestrictDatesInput isOnSmallcreen -> isOnSmallScreen Repsonse -> Response configuation -> configuration seconary -> secondary comparesion -> comparison * feat: Persian language (fa_IR) added * refactor: better variable name for languages * chore: sort languages alphabetically
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
||||
transifex_resource = frontend-app-course-authoring
|
||||
export TRANSIFEX_RESOURCE = ${transifex_resource}
|
||||
transifex_langs = "ar,fr,es_419,zh_CN,pt,it,de,uk,ru,hi,fr_CA,it_IT,pt_PT,de_DE"
|
||||
transifex_langs = "ar,de,de_DE,es_419,fa_IR,fr,fr_CA,hi,it,it_IT,pt,pt_PT,ru,uk,zh_CN"
|
||||
|
||||
intl_imports = ./node_modules/.bin/intl-imports.js
|
||||
transifex_utils = ./node_modules/.bin/transifex-utils.js
|
||||
|
||||
@@ -85,7 +85,7 @@ const AdvancedSettings = ({ intl, courseId }) => {
|
||||
}
|
||||
if (loadingSettingsStatus === RequestStatus.DENIED) {
|
||||
return (
|
||||
<div className="row justify-contnt-center m-6">
|
||||
<div className="row justify-content-center m-6">
|
||||
<Placeholder />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -41,7 +41,7 @@ const CustomPageCard = ({
|
||||
}));
|
||||
};
|
||||
|
||||
const toggleVisibilty = () => {
|
||||
const toggleVisibility = () => {
|
||||
dispatch(updateCustomPageVisibility({
|
||||
blockId: page.id,
|
||||
metadata: { course_staff_only: !page.courseStaffOnly },
|
||||
@@ -87,7 +87,7 @@ const CustomPageCard = ({
|
||||
src={page.courseStaffOnly ? VisibilityOff : Visibility}
|
||||
iconAs={Icon}
|
||||
alt={intl.formatMessage(messages.visibilityTooltipContent)}
|
||||
onClick={toggleVisibilty}
|
||||
onClick={toggleVisibility}
|
||||
data-testid="visibility-toggle-icon"
|
||||
/>
|
||||
<IconButtonWithTooltip
|
||||
|
||||
@@ -19,7 +19,7 @@ import { executeThunk } from '../utils';
|
||||
import { RequestStatus } from '../data/constants';
|
||||
import CustomPageCard from './CustomPageCard';
|
||||
import {
|
||||
generateUpdateVisiblityApiResponse,
|
||||
generateUpdateVisibilityApiResponse,
|
||||
courseId,
|
||||
initialState,
|
||||
generateXblockData,
|
||||
@@ -64,7 +64,7 @@ const mockStore = async ({
|
||||
const xblockEditUrl = `${getApiBaseUrl()}/xblock/${blockId}`;
|
||||
|
||||
axiosMock.onDelete(xblockEditUrl).reply(204);
|
||||
axiosMock.onPut(xblockEditUrl).reply(200, generateUpdateVisiblityApiResponse(blockId, visibility));
|
||||
axiosMock.onPut(xblockEditUrl).reply(200, generateUpdateVisibilityApiResponse(blockId, visibility));
|
||||
axiosMock.onGet(xblockEditUrl).reply(200, generateXblockData(blockId));
|
||||
|
||||
await executeThunk(deleteSingleCustomPage({
|
||||
|
||||
@@ -105,7 +105,7 @@ const CustomPages = ({
|
||||
}
|
||||
if (loadingStatus === RequestStatus.DENIED) {
|
||||
return (
|
||||
<div data-testid="under-construction-placeholder" className="row justify-contnt-center m-6">
|
||||
<div data-testid="under-construction-placeholder" className="row justify-content-center m-6">
|
||||
<Placeholder />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -52,7 +52,7 @@ export const generateXblockData = (
|
||||
data: '<p>test</p>',
|
||||
});
|
||||
|
||||
export const generateUpdateVisiblityApiResponse = (
|
||||
export const generateUpdateVisibilityApiResponse = (
|
||||
blockId,
|
||||
visibility,
|
||||
) => ({
|
||||
|
||||
@@ -33,8 +33,8 @@ export function fetchAssets(courseId) {
|
||||
try {
|
||||
const { totalCount } = await getAssets(courseId);
|
||||
const { assets } = await getAssets(courseId, totalCount);
|
||||
const parsedAssests = updateFileValues(assets);
|
||||
dispatch(addModels({ modelType: 'assets', models: parsedAssests }));
|
||||
const parsedAssets = updateFileValues(assets);
|
||||
dispatch(addModels({ modelType: 'assets', models: parsedAssets }));
|
||||
dispatch(setAssetIds({
|
||||
assetIds: assets.map(asset => asset.id),
|
||||
}));
|
||||
@@ -79,10 +79,10 @@ export function addAssetFile(courseId, file) {
|
||||
|
||||
try {
|
||||
const { asset } = await addAsset(courseId, file);
|
||||
const [parsedAssest] = updateFileValues([asset]);
|
||||
const [parsedAssets] = updateFileValues([asset]);
|
||||
dispatch(addModel({
|
||||
modelType: 'assets',
|
||||
model: { ...parsedAssest },
|
||||
model: { ...parsedAssets },
|
||||
}));
|
||||
dispatch(addAssetSuccess({
|
||||
assetId: asset.id,
|
||||
|
||||
@@ -144,7 +144,7 @@ const messages = defineMessages({
|
||||
'header.label.secondary.nav': {
|
||||
id: 'header.label.secondary.nav',
|
||||
defaultMessage: 'Secondary',
|
||||
description: 'The aria label for the seconary nav',
|
||||
description: 'The aria label for the secondary nav',
|
||||
},
|
||||
'header.label.courseOutline': {
|
||||
id: 'header.label.courseOutline',
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
import { messages as footerMessages } from '@edx/frontend-component-footer';
|
||||
import { messages as paragonMessages } from '@edx/paragon';
|
||||
import arMessages from './messages/ar.json';
|
||||
import frMessages from './messages/fr.json';
|
||||
import es419Messages from './messages/es_419.json';
|
||||
import zhcnMessages from './messages/zh_CN.json';
|
||||
import ptMessages from './messages/pt.json';
|
||||
import itMessages from './messages/it.json';
|
||||
import ukMessages from './messages/uk.json';
|
||||
import deMessages from './messages/de.json';
|
||||
import ruMessages from './messages/ru.json';
|
||||
import hiMessages from './messages/hi.json';
|
||||
import deDEMessages from './messages/de_DE.json';
|
||||
import es419Messages from './messages/es_419.json';
|
||||
import faIRMessages from './messages/fa_IR.json';
|
||||
import frMessages from './messages/fr.json';
|
||||
import frCAMessages from './messages/fr_CA.json';
|
||||
import dedeMessages from './messages/de_DE.json';
|
||||
import ititMessages from './messages/it_IT.json';
|
||||
import ptptMessages from './messages/pt_PT.json';
|
||||
import hiMessages from './messages/hi.json';
|
||||
import itMessages from './messages/it.json';
|
||||
import itITMessages from './messages/it_IT.json';
|
||||
import ptMessages from './messages/pt.json';
|
||||
import ptPTMessages from './messages/pt_PT.json';
|
||||
import ruMessages from './messages/ru.json';
|
||||
import ukMessages from './messages/uk.json';
|
||||
import zhCNMessages from './messages/zh_CN.json';
|
||||
// no need to import en messages-- they are in the defaultMessage field
|
||||
|
||||
const appMessages = {
|
||||
ar: arMessages,
|
||||
'es-419': es419Messages,
|
||||
fr: frMessages,
|
||||
'zh-cn': zhcnMessages,
|
||||
pt: ptMessages,
|
||||
it: itMessages,
|
||||
de: deMessages,
|
||||
hi: hiMessages,
|
||||
'de-de': deDEMessages,
|
||||
'es-419': es419Messages,
|
||||
'fa-ir': faIRMessages,
|
||||
fr: frMessages,
|
||||
'fr-ca': frCAMessages,
|
||||
hi: hiMessages,
|
||||
it: itMessages,
|
||||
'it-it': itITMessages,
|
||||
ru: ruMessages,
|
||||
pt: ptMessages,
|
||||
'pt-pt': ptPTMessages,
|
||||
uk: ukMessages,
|
||||
'de-de': dedeMessages,
|
||||
'it-it': ititMessages,
|
||||
'pt-pt': ptptMessages,
|
||||
'zh-cn': zhCNMessages,
|
||||
};
|
||||
|
||||
export default [
|
||||
|
||||
1
src/i18n/messages/fa_IR.json
Normal file
1
src/i18n/messages/fa_IR.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import FieldFeedback from '../../../../../../generic/FieldFeedback';
|
||||
|
||||
const RestictDatesInput = ({
|
||||
const RestrictDatesInput = ({
|
||||
value,
|
||||
type,
|
||||
label,
|
||||
@@ -64,7 +64,7 @@ const RestictDatesInput = ({
|
||||
);
|
||||
};
|
||||
|
||||
RestictDatesInput.propTypes = {
|
||||
RestrictDatesInput.propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
fieldName: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
@@ -77,7 +77,7 @@ RestictDatesInput.propTypes = {
|
||||
dataTestId: PropTypes.string,
|
||||
};
|
||||
|
||||
RestictDatesInput.defaultProps = {
|
||||
RestrictDatesInput.defaultProps = {
|
||||
fieldClasses: '',
|
||||
helpText: '',
|
||||
feedbackClasses: '',
|
||||
@@ -85,4 +85,4 @@ RestictDatesInput.defaultProps = {
|
||||
dataTestId: null,
|
||||
};
|
||||
|
||||
export default React.memo(RestictDatesInput);
|
||||
export default React.memo(RestrictDatesInput);
|
||||
|
||||
@@ -33,7 +33,7 @@ const AppList = ({ intl }) => {
|
||||
const features = useModels('features', featureIds);
|
||||
const isGlobalStaff = getAuthenticatedUser().administrator;
|
||||
const ltiProvider = !['openedx', 'legacy'].includes(activeAppId);
|
||||
const isOnSmallcreen = useIsOnSmallScreen();
|
||||
const isOnSmallScreen = useIsOnSmallScreen();
|
||||
|
||||
const showOneEdxProvider = useMemo(() => apps.filter(app => (
|
||||
activeAppId === 'openedx' ? app.id !== 'legacy' : app.id !== 'openedx'
|
||||
@@ -122,8 +122,8 @@ const AppList = ({ intl }) => {
|
||||
|
||||
return (
|
||||
<div className="my-sm-4" data-testid="appList">
|
||||
<div className={!isOnSmallcreen ? 'd-flex flex-row justify-content-between align-items-center' : 'mb-4'}>
|
||||
<h3 className={isOnSmallcreen ? 'mb-3' : 'm-0'}>
|
||||
<div className={!isOnSmallScreen ? 'd-flex flex-row justify-content-between align-items-center' : 'mb-4'}>
|
||||
<h3 className={isOnSmallScreen ? 'mb-3' : 'm-0'}>
|
||||
{intl.formatMessage(messages.heading)}
|
||||
</h3>
|
||||
<Form.Switch
|
||||
@@ -144,7 +144,7 @@ const AppList = ({ intl }) => {
|
||||
lg: 4,
|
||||
xl: 4,
|
||||
}}
|
||||
className={!isOnSmallcreen && 'mt-5'}
|
||||
className={!isOnSmallScreen && 'mt-5'}
|
||||
>
|
||||
{(isGlobalStaff || ltiProvider) ? showAppCard(apps) : showAppCard(showOneEdxProvider)}
|
||||
</CardGrid>
|
||||
|
||||
@@ -47,7 +47,7 @@ function renderComponent() {
|
||||
container = wrapper.container;
|
||||
}
|
||||
|
||||
function generateCourseLevelAPIRepsonse({
|
||||
function generateCourseLevelAPIResponse({
|
||||
success, enabled,
|
||||
}) {
|
||||
return {
|
||||
@@ -96,7 +96,7 @@ describe('XpertUnitSummarySettings', () => {
|
||||
describe('with successful network connections', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: true,
|
||||
}));
|
||||
@@ -111,7 +111,7 @@ describe('XpertUnitSummarySettings', () => {
|
||||
|
||||
test('Shows switch on if disabled from backend', async () => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: false,
|
||||
}));
|
||||
@@ -129,7 +129,7 @@ describe('XpertUnitSummarySettings', () => {
|
||||
|
||||
test('Shows disable radio selected if enabled from backend', async () => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: false,
|
||||
}));
|
||||
@@ -143,7 +143,7 @@ describe('XpertUnitSummarySettings', () => {
|
||||
describe('first time course configuration', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(400, generateCourseLevelAPIRepsonse({
|
||||
.reply(400, generateCourseLevelAPIResponse({
|
||||
success: false,
|
||||
enabled: undefined,
|
||||
}));
|
||||
@@ -151,7 +151,7 @@ describe('XpertUnitSummarySettings', () => {
|
||||
renderComponent();
|
||||
});
|
||||
|
||||
test('Does not show as enabled if configuation does not exist', async () => {
|
||||
test('Does not show as enabled if configuration does not exist', async () => {
|
||||
await waitFor(() => expect(container.querySelector('#enable-xpert-unit-summary-toggle')).toBeTruthy());
|
||||
expect(container.querySelector('#enable-xpert-unit-summary-toggle').checked).not.toBeTruthy();
|
||||
expect(queryByTestId(container, 'enable-badge')).not.toBeTruthy();
|
||||
@@ -161,13 +161,13 @@ describe('XpertUnitSummarySettings', () => {
|
||||
describe('saving configuration changes', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: false,
|
||||
}));
|
||||
|
||||
axiosMock.onPost(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: true,
|
||||
}));
|
||||
@@ -190,7 +190,7 @@ describe('XpertUnitSummarySettings', () => {
|
||||
describe('testing configurable gating', () => {
|
||||
beforeEach(async () => {
|
||||
axiosMock.onGet(API.getXpertConfigurationStatusUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: true,
|
||||
}));
|
||||
@@ -207,13 +207,13 @@ describe('XpertUnitSummarySettings', () => {
|
||||
describe('removing course configuration', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: true,
|
||||
}));
|
||||
|
||||
axiosMock.onDelete(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: undefined,
|
||||
}));
|
||||
@@ -235,13 +235,13 @@ describe('XpertUnitSummarySettings', () => {
|
||||
describe('resetting course units', () => {
|
||||
test('reset all units to be enabled', async () => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: true,
|
||||
}));
|
||||
|
||||
axiosMock.onPost(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: true,
|
||||
}));
|
||||
@@ -257,13 +257,13 @@ describe('XpertUnitSummarySettings', () => {
|
||||
|
||||
test('reset all units to be disabled', async () => {
|
||||
axiosMock.onGet(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: false,
|
||||
}));
|
||||
|
||||
axiosMock.onPost(API.getXpertSettingsUrl(courseId))
|
||||
.reply(200, generateCourseLevelAPIRepsonse({
|
||||
.reply(200, generateCourseLevelAPIResponse({
|
||||
success: true,
|
||||
enabled: false,
|
||||
}));
|
||||
|
||||
@@ -181,8 +181,8 @@ export function setupYupExtensions() {
|
||||
Yup.addMethod(Yup.string, 'compare', function compare(message, type) {
|
||||
return this.test('isGreater', message, function isGreater() {
|
||||
// This function compare 2 dates or 2 times. It return no error if dateInstance/timeInstance is empty
|
||||
// of if startTime or endTime is not present for time comparesion
|
||||
// or startDate or endDate is not present for date comparesion
|
||||
// of if startTime or endTime is not present for time comparison
|
||||
// or startDate or endDate is not present for date comparison
|
||||
|
||||
if (!this.parent
|
||||
|| (!(this.parent.startTime && this.parent.endTime) && type === 'time')
|
||||
|
||||
Reference in New Issue
Block a user