Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added scripts and marked strings for i18n. #396

Merged
merged 1 commit into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ lerna-debug.log
coverage/
dist/
packages/*/package-lock.json

# Transifex
i18n/transifex_input.json
temp
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./i18n
transifex_input = $(i18n)/transifex_input.json

# This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-formatjs

i18n.extract:
# Pulling display strings from .jsx files into .json files...
rm -rf $(transifex_temp)
npm run-script i18n_extract

i18n.concat:
# Gathering JSON messages into one file...
$(transifex_utils) $(transifex_temp) $(transifex_input)

extract_translations: | requirements i18n.extract i18n.concat

.PHONY: requirements
requirements: ## install ci requirements
npm ci
1 change: 1 addition & 0 deletions i18n/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Empty file to preserve directory structure
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"lint:fix": "npm run lint:fix --workspaces",
"bootstrap": "npm install conventional-changelog-conventionalcommits",
"changed": "lerna changed",
"lerna:version": "npx lerna@6 version --conventional-commits --create-release github --no-push"
"lerna:version": "npx lerna@6 version --conventional-commits --create-release github --no-push",
"i18n_extract": "fedx-scripts formatjs extract packages/**/**/*.{js,jsx,ts,tsx}"
},
"devDependencies": {
"@commitlint/config-conventional": "17.6.0",
Expand Down
7 changes: 6 additions & 1 deletion packages/catalog-search/src/ClearCurrentRefinements.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { Button } from '@openedx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { SearchContext } from './SearchContext';
import { clearRefinementsAction } from './data/actions';

Expand Down Expand Up @@ -28,7 +29,11 @@ const ClearCurrentRefinements = ({ className, variant, ...props }) => {
onClick={handleClearAllRefinementsClick}
{...props}
>
{CLEAR_ALL_TEXT}
<FormattedMessage
id="search.facetFilters.clearAll.button"
defaultMessage="clear all"
description="Button text to clear all filters"
/>
</Button>
) }
</span>
Expand Down
52 changes: 47 additions & 5 deletions packages/catalog-search/src/CurrentRefinements.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import classNames from 'classnames';
import { Badge, Button } from '@openedx/paragon';
import { CloseSmall } from '@openedx/paragon/icons';
import { connectCurrentRefinements } from 'react-instantsearch-dom';
import { FormattedMessage, defineMessages, useIntl } from '@edx/frontend-platform/i18n';

import ClearCurrentRefinements from './ClearCurrentRefinements';

Expand All @@ -14,13 +15,33 @@ import {
NUM_CURRENT_REFINEMENTS_TO_DISPLAY,
STYLE_VARIANTS,
LEARNING_TYPE_PATHWAY,
LEARNING_TYPE_COURSE,
LEARNING_TYPE_PROGRAM,
} from './data/constants';
import {
useActiveRefinementsAsFlatArray,
} from './data/hooks';
import { SearchContext } from './SearchContext';
import { removeFromRefinementArray, deleteRefinementAction } from './data/actions';

const messages = defineMessages({
[LEARNING_TYPE_COURSE]: {
id: 'search.facetFilters.filterTitle.course',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not following Camel case format in IDs of strings. So I think, we can maintain the consistency as we are doing in learner portal and admin portal.
search.facetFilters.filterTitle.course -> search.facet.filters.filter.title.course

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is better to use dot . to separate pages/components and use camelCase for page/component name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, camelCase is preferred for message ids.

defaultMessage: 'Course',
description: 'Title for the course filter.',
},
[LEARNING_TYPE_PROGRAM]: {
id: 'search.facetFilters.filterTitle.program',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as mentioned above about id format

defaultMessage: 'Program',
description: 'Title for the program filter.',
},
[LEARNING_TYPE_PATHWAY]: {
id: 'search.facetFilters.filterTitle.pathway',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as mentioned above about id format. And please do it on otherwise her places too.

defaultMessage: 'Pathway',
description: 'Title for the pathway filter.',
},
});

export const CurrentRefinementsBase = ({ items, variant }) => {
if (!items || !items.length) {
return null;
Expand All @@ -29,6 +50,7 @@ export const CurrentRefinementsBase = ({ items, variant }) => {
const [showAllRefinements, setShowAllRefinements] = useState(false);
const { refinements, dispatch } = useContext(SearchContext);
const activeRefinementsAsFlatArray = useActiveRefinementsAsFlatArray(items);
const intl = useIntl();

/**
* Determines the correct number of active refinements to show at any
Expand Down Expand Up @@ -83,10 +105,19 @@ export const CurrentRefinementsBase = ({ items, variant }) => {
variant="light"
onClick={() => handleRefinementBadgeClick(item)}
>
{/* Temporary fix : can be removed when learnerpathway content type is changed to pathways */}
<span className="mr-2">{item.label === LEARNING_TYPE_PATHWAY ? 'Pathway' : item.label}</span>
<span className="mr-2">
{messages[item.label] ? intl.formatMessage(messages[item.label]) : item.label}
</span>

<CloseSmall />
<span className="sr-only">Remove the filter {item.label}</span>
<span className="sr-only">
<FormattedMessage
id="search.facetFilters.removeFilter.button"
defaultMessage="Remove the filter {filterTitle}"
description="Button text to remove a filter from the search results"
values={{ filterTitle: item.label }}
/>
</span>
</Badge>
</li>
))}
Expand All @@ -98,7 +129,14 @@ export const CurrentRefinementsBase = ({ items, variant }) => {
onClick={() => setShowAllRefinements(true)}
>
+{activeRefinementsAsFlatArray.length - NUM_CURRENT_REFINEMENTS_TO_DISPLAY}
<span className="sr-only">Show all {activeRefinementsAsFlatArray.length} filters</span>
<span className="sr-only">
<FormattedMessage
id="search.facetFilters.showAll.button"
defaultMessage="Show all {activeRefinementsCount} filters"
description="Button text to show all filters"
values={{ activeRefinementsCount: activeRefinementsAsFlatArray.length }}
/>
</span>
</Badge>
</li>
)}
Expand All @@ -113,7 +151,11 @@ export const CurrentRefinementsBase = ({ items, variant }) => {
variant="link"
size="inline"
>
show less
<FormattedMessage
id="search.facetFilters.showLess.button"
defaultMessage="show less"
description="Button text to show less filters"
/>
</Button>
</li>
)}
Expand Down
13 changes: 11 additions & 2 deletions packages/catalog-search/src/FacetListBase.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import React, { useCallback, useContext } from 'react';
import PropTypes from 'prop-types';

import { NO_OPTIONS_FOUND, STYLE_VARIANTS } from './data/constants';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { STYLE_VARIANTS } from './data/constants';
import FacetDropdown from './FacetDropdown';
import TypeaheadFacetDropdown from './TypeaheadFacetDropdown';
import FacetItem from './FacetItem';
Expand Down Expand Up @@ -62,7 +63,15 @@ const FacetListBase = ({
const renderItems = useCallback(
() => {
if (!items?.length) {
return <span className="p-2 d-block">{NO_OPTIONS_FOUND}</span>;
return (
<span className="p-2 d-block">
<FormattedMessage
id="search.facetFilters.noOptionsFound"
defaultMessage="No options found."
description="Message displayed when no options are found for a facet filter."
/>
</span>
);
}

return items.map((item) => {
Expand Down
32 changes: 27 additions & 5 deletions packages/catalog-search/src/LearningTypeRadioFacet.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Dropdown, Input } from '@openedx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { SearchContext } from './SearchContext';
import {
setRefinementAction,
Expand All @@ -11,6 +12,7 @@ import { LEARNING_TYPE_COURSE, LEARNING_TYPE_PROGRAM, LEARNING_TYPE_PATHWAY } fr

const LearningTypeRadioFacet = ({ enablePathways }) => {
const { refinements, dispatch } = useContext(SearchContext);

// only bold the dropdown title if the learning type is Course or Program
const typeCourseSelected = refinements.content_type && refinements.content_type.includes(LEARNING_TYPE_COURSE);
const typeProgramSelected = refinements.content_type && refinements.content_type.includes(LEARNING_TYPE_PROGRAM);
Expand All @@ -33,7 +35,11 @@ const LearningTypeRadioFacet = ({ enablePathways }) => {
variant="inverse-primary"
className={classNames({ 'font-weight-bold': boldTitle })}
>
Learning Type
<FormattedMessage
id="search.facetFilters.learningType.title"
defaultMessage="Learning Type"
description="Title for the learning type facet filter"
/>
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item as="label" className="mb-0 py-3 d-flex align-items-center">
Expand All @@ -45,7 +51,11 @@ const LearningTypeRadioFacet = ({ enablePathways }) => {
data-testid="learning-type-any"
/>
<span className={classNames('facet-item-label', {})}>
Any
<FormattedMessage
id="search.facetFilters.learningType.any"
defaultMessage="Any"
description="Title for the learning type facet filter to return all types of learning content"
/>
</span>
</Dropdown.Item>
<Dropdown.Item as="label" className="mb-0 py-3 d-flex align-items-center">
Expand All @@ -57,7 +67,11 @@ const LearningTypeRadioFacet = ({ enablePathways }) => {
data-testid="learning-type-courses"
/>
<span className={classNames('facet-item-label', { 'is-refined': typeCourseSelected })}>
Courses
<FormattedMessage
id="search.facetFilters.learningType.courses"
defaultMessage="Courses"
description="Title for the learning type facet filter to return courses only"
/>
</span>
</Dropdown.Item>
<Dropdown.Item as="label" className="mb-0 py-3 d-flex align-items-center">
Expand All @@ -69,7 +83,11 @@ const LearningTypeRadioFacet = ({ enablePathways }) => {
data-testid="learning-type-programs"
/>
<span className={classNames('facet-item-label', { 'is-refined': typeProgramSelected })}>
Programs
<FormattedMessage
id="search.facetFilters.learningType.programs"
defaultMessage="Programs"
description="Title for the learning type facet filter to return programs only"
/>
</span>
</Dropdown.Item>
{
Expand All @@ -85,7 +103,11 @@ const LearningTypeRadioFacet = ({ enablePathways }) => {
data-testid="learning-type-pathways"
/>
<span className={classNames('facet-item-label', { 'is-refined': typePathwaySelected })}>
Pathways
<FormattedMessage
id="search.facetFilters.learningType.pathways"
defaultMessage="Pathways"
description="Title for the learning type facet filter to return pathways only"
/>
</span>
</Dropdown.Item>
)
Expand Down
41 changes: 35 additions & 6 deletions packages/catalog-search/src/MobileFilterMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { connectCurrentRefinements } from 'react-instantsearch-dom';
import { Button } from '@openedx/paragon';
import { ArrowDropDown, Close } from '@openedx/paragon/icons';

import { FormattedMessage } from '@edx/frontend-platform/i18n';
import ClearCurrentRefinements from './ClearCurrentRefinements';

import { useActiveRefinementsAsFlatArray } from './data/hooks';
Expand All @@ -22,10 +23,19 @@ export const MobileFilterMenuBase = ({ children, className, items }) => {
onClick={() => setIsOpen(true)}
>
<div className="mr-2">
Filters
<FormattedMessage
id="catalog.search.filters"
defaultMessage="Filters"
description="Label for the filters button."
/>
{activeRefinementsAsFlatArray && activeRefinementsAsFlatArray.length > 0 && (
<span className="ml-1">
({activeRefinementsAsFlatArray.length} selected)
<FormattedMessage
id="catalog.search.filters.selected"
defaultMessage="({count} selected)"
description="Label for the number of selected filters."
values={{ count: activeRefinementsAsFlatArray.length }}
/>
</span>
)}
</div>
Expand All @@ -45,10 +55,19 @@ export const MobileFilterMenuBase = ({ children, className, items }) => {
<div className="modal-content">
<div className="modal-header d-flex align-items-center">
<h5 className="modal-title text-center w-100">
All Filters
<FormattedMessage
id="catalog.search.filters.all"
defaultMessage="All Filters"
description="Label for the all filters button."
/>
{activeRefinementsAsFlatArray && activeRefinementsAsFlatArray.length > 0 && (
<span className="ml-1">
({activeRefinementsAsFlatArray.length} selected)
<FormattedMessage
id="catalog.search.filters.all.selected"
defaultMessage="({count} selected)"
description="Label for the number of selected filters."
values={{ count: activeRefinementsAsFlatArray.length }}
/>
</span>
)}
</h5>
Expand All @@ -60,7 +79,13 @@ export const MobileFilterMenuBase = ({ children, className, items }) => {
<Close
id="icon-close-mobile-filter-menu"
/>
<span className="sr-only">close filter menu</span>
<span className="sr-only">
<FormattedMessage
id="catalog.search.filters.close"
defaultMessage="close filter menu"
description="Label for the close filter menu button."
/>
</span>
</Button>
</div>
<div className="modal-body p-0">
Expand All @@ -75,7 +100,11 @@ export const MobileFilterMenuBase = ({ children, className, items }) => {
className="btn-brand-primary btn-block py-2 m-0"
onClick={() => setIsOpen(false)}
>
Done
<FormattedMessage
id="catalog.search.filters.done"
defaultMessage="Done"
description="Label for the done button."
/>
</Button>
</div>
</div>
Expand Down
14 changes: 12 additions & 2 deletions packages/catalog-search/src/SearchBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import classNames from 'classnames';
import { SearchField } from '@openedx/paragon';
import debounce from 'lodash.debounce';
import { connectSearchBox } from 'react-instantsearch-dom';
import { useIntl, defineMessages } from '@edx/frontend-platform/i18n';

import { sendTrackEvent } from '@edx/frontend-platform/analytics';

Expand All @@ -26,7 +27,15 @@ import {
} from './data/constants';
import SearchSuggestions from './SearchSuggestions';

export const searchText = 'Search courses';
const messages = defineMessages({
searchCoursesText: {
id: 'header.search.input.box.placeholder',
description: 'Placeholder text for the search input box',
defaultMessage: 'Search courses',
},
});

export const searchText = messages.searchCoursesText.defaultMessage;
// this prefix will be combined with one of the SearchBox props to create a full tracking event name
// only if event name prop is provided by user. In the absence of the tracking name prop,
// no tracking event will be sent.
Expand All @@ -53,6 +62,7 @@ export const SearchBoxBase = ({
const [showSuggestions, setShowSuggestions] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
const [preQueryHits, setPreQueryHits] = useState([]);
const intl = useIntl();

/**
* Handles when a search is submitted by adding the user's search
Expand Down Expand Up @@ -155,7 +165,7 @@ export const SearchBoxBase = ({
{!hideTitle && (
/* eslint-disable-next-line jsx-a11y/label-has-associated-control */
<label id="search-input-box" className="fe__searchfield-input-box text-brand-primary">
{headerTitle || searchText}
{headerTitle || intl.formatMessage(messages.searchCoursesText)}
</label>
)}
<SearchField.Advanced
Expand Down
Loading
Loading