Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #9 from openedx/feature/NOVA-136
Browse files Browse the repository at this point in the history
feat: add i18n to app
  • Loading branch information
Rodra authored Dec 19, 2023
2 parents 691b9c5 + 9ed994c commit 44f9a76
Show file tree
Hide file tree
Showing 24 changed files with 373 additions and 126 deletions.
8 changes: 8 additions & 0 deletions .tx/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[main]
host = https://www.transifex.com

[o:open-edx:p:edx-platform:r:frontend-component-ai-translations]
file_filter = src/i18n/messages/<lang>.json
source_file = src/i18n/transifex_input.json
source_lang = en
type = KEYVALUEJSON
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export TRANSIFEX_RESOURCE = frontend-component-header-edx
transifex_resource = frontend-component-header-edx
export TRANSIFEX_RESOURCE = frontend-component-ai-translations
transifex_resource = frontend-component-ai-translations
transifex_langs = "ar,fr,es_419,zh_CN,pt,it,de,uk,ru,hi,fr_CA"

transifex_utils = ./node_modules/.bin/transifex-utils.js
Expand Down
312 changes: 216 additions & 96 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"scripts": {
"build": "make build",
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"i18n_extract": "fedx-scripts formatjs extract",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
Expand Down Expand Up @@ -39,7 +39,9 @@
"@testing-library/react": "10.4.9",
"@testing-library/user-event": "^14.5.1",
"@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
"babel-plugin-react-intl": "^8.2.25",
"enzyme": "3.11.0",
"glob": "7.2.0",
"husky": "8.0.3",
"jest": "29.7.0"
},
Expand Down
13 changes: 9 additions & 4 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { useState } from 'react';
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import {
ActionRow, Collapsible, Icon, IconButton, Image,
} from '@edx/paragon';
import { ChevronLeft, ChevronRight, Close } from '@edx/paragon/icons';
import PropTypes from 'prop-types';

import messages from './messages';
import XpertLogo from './XpertLogo';

const App = ({ setIsAiTranslations, closeTranscriptSettings }) => {
Expand All @@ -14,6 +16,7 @@ const App = ({ setIsAiTranslations, closeTranscriptSettings }) => {
translationsError: false,
view: '',
});
const intl = useIntl();

const handleAppState = (updatedData) => {
setAppState((previousState) => ({ ...previousState, ...updatedData }));
Expand Down Expand Up @@ -45,7 +48,9 @@ const App = ({ setIsAiTranslations, closeTranscriptSettings }) => {
>
<div className="d-flex flex-column justify-content-center">
<XpertLogo />
Get free translations
<h4 className="h4 free-transltions-text mt-2.5">
<FormattedMessage {...messages.getFreeTranslations} />
</h4>
<Image
src={googleTranslateImage}
className="flex-grow-0"
Expand All @@ -70,7 +75,7 @@ const App = ({ setIsAiTranslations, closeTranscriptSettings }) => {
size="sm"
iconAs={Icon}
src={ChevronLeft}
alt="back button to main transcript settings view"
alt={intl.formatMessage(messages.backButtonAlt)}
onClick={handleBackButton}
/>
<ActionRow.Spacer />
Expand All @@ -82,12 +87,12 @@ const App = ({ setIsAiTranslations, closeTranscriptSettings }) => {
setIsAiTranslations(false);
}}
src={Close}
alt="close settings"
alt={intl.formatMessage(messages.closeButtonAlt)}
data-testid="action-row-close-btn"
/>
</ActionRow>
<div className="d-flex flex-column" key="ai-translations-views">
Translations is not available
<FormattedMessage {...messages.translationsNotAvailable} />
</div>
</>
)}
Expand Down
56 changes: 33 additions & 23 deletions src/App.test.jsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,42 @@
import * as React from 'react';
import { act, render, screen } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
// eslint-disable-next-line import/no-extraneous-dependencies
import userEvent from '@testing-library/user-event';

import App from './App';
import messages from './messages';

const courseId = 'Fake-ID';

describe('App', () => {
it('renders collapsible button', () => {
const onSetIsAiTranslations = jest.fn();
render(
<App
setIsAiTranslations={onSetIsAiTranslations}
closeTranscriptSettings={() => {}}
courseId={courseId}
/>,
<IntlProvider locale="en">
<App
setIsAiTranslations={onSetIsAiTranslations}
closeTranscriptSettings={() => {}}
courseId={courseId}
/>
</IntlProvider>,
);

expect(
screen.getByText(/Get free translations/),
screen.getByText(messages.getFreeTranslations.defaultMessage),
).toBeInTheDocument();
});

it('renders App component', async () => {
const onSetIsAiTranslations = jest.fn();

render(
<App
setIsAiTranslations={onSetIsAiTranslations}
closeTranscriptSettings={() => {}}
/>,
<IntlProvider locale="en">
<App
setIsAiTranslations={onSetIsAiTranslations}
closeTranscriptSettings={() => {}}
/>
</IntlProvider>,
);

userEvent.click(screen.getByTestId('app-entry-btn'));
Expand All @@ -40,41 +46,45 @@ describe('App', () => {
it('goes back to previous view', async () => {
const onSetIsAiTranslations = jest.fn();
render(
<App
setIsAiTranslations={onSetIsAiTranslations}
closeTranscriptSettings={() => {}}
/>,
<IntlProvider locale="en">
<App
setIsAiTranslations={onSetIsAiTranslations}
closeTranscriptSettings={() => {}}
/>
</IntlProvider>,
);

userEvent.click(
screen.getByText(/Get free translations/),
screen.getByText(messages.getFreeTranslations.defaultMessage),
);
expect(await screen.findByText(/Translations is not available/)).toBeInTheDocument();
expect(await screen.findByText(messages.translationsNotAvailable.defaultMessage)).toBeInTheDocument();
expect(await screen.findByTestId('action-row-back-btn')).toBeInTheDocument();

await act(async () => {
userEvent.click(screen.queryByTestId('action-row-back-btn'));
});

expect(onSetIsAiTranslations).toHaveBeenCalled();
expect(await screen.findByText(/Get free translations/)).toBeInTheDocument();
expect(await screen.findByText(messages.getFreeTranslations.defaultMessage)).toBeInTheDocument();
expect(screen.queryByText(/Get free translations is not available/)).not.toBeInTheDocument();
});

it('calls closeTranscriptSettings when close button is clicked', async () => {
const onSetIsAiTranslations = jest.fn();
const onCloseTranscriptSettings = jest.fn();
render(
<App
setIsAiTranslations={onSetIsAiTranslations}
closeTranscriptSettings={onCloseTranscriptSettings}
/>,
<IntlProvider locale="en">
<App
setIsAiTranslations={onSetIsAiTranslations}
closeTranscriptSettings={onCloseTranscriptSettings}
/>
</IntlProvider>,
);

userEvent.click(
screen.getByText(/Get free translations/),
screen.getByText(messages.getFreeTranslations.defaultMessage),
);
expect(await screen.findByText(/Translations is not available/)).toBeInTheDocument();
expect(await screen.findByText(messages.translationsNotAvailable.defaultMessage)).toBeInTheDocument();
expect(await screen.findByTestId('action-row-close-btn')).toBeInTheDocument();

await userEvent.click(screen.queryByTestId('action-row-close-btn'));
Expand Down
38 changes: 38 additions & 0 deletions src/i18n/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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 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';
// 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,
'fr-ca': frCAMessages,
ru: ruMessages,
uk: ukMessages,
'de-de': dedeMessages,
'it-it': ititMessages,
'pt-pt': ptptMessages,
};

export default [
paragonMessages,
appMessages,
];
23 changes: 23 additions & 0 deletions src/i18n/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import messages from './index';

describe('messages', () => {
it('contains app messages', () => {
expect(messages.length).toEqual(2);
const appMessages = messages[1];
const languages = Object.getOwnPropertyNames(appMessages);
expect(languages).toContainEqual('ar');
expect(languages).toContainEqual('es-419');
expect(languages).toContainEqual('fr');
expect(languages).toContainEqual('zh-cn');
expect(languages).toContainEqual('pt');
expect(languages).toContainEqual('it');
expect(languages).toContainEqual('de');
expect(languages).toContainEqual('hi');
expect(languages).toContainEqual('fr-ca');
expect(languages).toContainEqual('ru');
expect(languages).toContainEqual('uk');
expect(languages).toContainEqual('de-de');
expect(languages).toContainEqual('it-it');
expect(languages).toContainEqual('pt-pt');
});
});
1 change: 1 addition & 0 deletions src/i18n/messages/ar.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/de_DE.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/es_419.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/fa_IR.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/fr_CA.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/hi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/it.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/it_IT.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/pt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/pt_PT.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/ru.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/uk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/i18n/messages/zh_CN.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
26 changes: 26 additions & 0 deletions src/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { defineMessages } from '@edx/frontend-platform/i18n';

const messages = defineMessages({
getFreeTranslations: {
id: 'ai-translations.request.heading.top',
defaultMessage: 'Get free translations',
description: 'Translations heading available',
},
translationsNotAvailable: {
id: 'ai-translations.request.heading.bottom',
defaultMessage: 'Translations service is not available',
description: 'Translations heading not available',
},
backButtonAlt: {
id: 'ai-translations.icon.button.back.alt',
defaultMessage: 'Back button to main transcript settings view',
description: 'alt text for back icon button',
},
closeButtonAlt: {
id: 'ai-translations.icon.button.close.alt',
defaultMessage: 'Close settings',
description: 'alt text for close settings icon button',
},
});

export default messages;

0 comments on commit 44f9a76

Please sign in to comment.