Skip to content

Commit

Permalink
Add translation configuration for all strings
Browse files Browse the repository at this point in the history
  • Loading branch information
ang-zeyu committed Dec 16, 2022
1 parent 7bd1740 commit cf69c5c
Show file tree
Hide file tree
Showing 14 changed files with 175 additions and 66 deletions.
31 changes: 31 additions & 0 deletions docs/src/language.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,34 @@ Keeping them enables the following:
- Processing phrase queries such as `"for tomorrow"` accurately; Stop words would be removed automatically from such queries.
- Boolean queries of stop words (e.g. `if AND forecast AND sunny`)
- More accurate ranking for free text queries, which uses stop words in term proximity ranking

## UI Translations

The UI's text can also be overwritten.
Refer to this [link](https://github.com/ang-zeyu/infisearch/tree/main/packages/search-ui/src/translations/en.ts) for the default set of texts.

```ts
infisearch.init({
uiOptions: {
translations: { ... }
}
})
```

| Option | Default | Description |
| ----------- | ----------- | ----------- |
| `resultsLabel` | `'Site results'` | Accessibility label for the [`listbox`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role) containing result previews. This is announced to screenreaders.
| `fsButtonLabel` | `'Search'` | Accessibility label for the original input element that functions as a button when the fullscreen UI is in use.
| `fsButtonPlaceholder` | `undefined`| Placeholder override for the provided `input` that functions as a button when the fullscreen UI is in use.
| `fsPlaceholder` | `'Search this site'` | Placeholder of the input element in the fullscreen UI.
| `fsCloseText` | `'Close'` | Text for the <kbd>Close</kbd> button.
| `filtersButton` | `'Filters'` | Text for the <kbd>Filters</kbd> button if any enum or numeric filters are configured.
| `numResultsFound` | `' results found'` | The text following the number of results found.
| `startSearching` | `'Start Searching Above!'`| Text shown when the input is empty.
| `startingUp` | `'... Starting Up ...'` | Text shown when InfiSearch is still not ready to perform any queries. The setup occurs extremely quickly, you will hopefully not be able to see this text most of the time.
| `navigation` | `'Navigation'` | Navigation controls text.
| `tipHeader` | `'🔎 Advanced search tips'` | Header of the tip popup.
| `tip` | `'Tip'` | First column header of the tip popup.
| `example` | `'Example'` | Second column header of the tip popup.
| `tipRows.xx` (refer [here](https://github.com/ang-zeyu/infisearch/tree/main/packages/search-ui/src/translations/en.ts)) | | Examples for usage of InfiSearch's advanced search syntax.
| `error` | `'Oops! Something went wrong... 🙁'` | Generic error text when something goes wrong
4 changes: 0 additions & 4 deletions docs/src/search_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,7 @@ There are also several options specific to each mode. Note that `dropdown` and `
| Mode | Option | Default | Description |
| ----------- | ----------- | ----------- | ----------- |
| dropdown | `dropdownAlignment` | `'bottom-end'` | `'bottom'` or `'bottom-start'` or `'bottom-end'`.<br><br>This is the side of the input element to align the dropdown results container and dropdown seperator against.<br><br>The alignment will also be automatically flipped horizontally to ensure the most optimal placement.
| auto | `fsInputButtonText` | `undefined`| Placeholder override for the `input` if the fullscreen UI is in use.<br><br>This is added for keyboard [accessibility](./search_configuration_styling.md#styling-the-fullscreen-ui-input-button).
| fullscreen | `fsInputLabel` | `'Search'` | Accessibility label for the original input element, when the fullscreen UI is in use.
| fullscreen | `fsContainer` | `<body>` element | `id` of the element, or an element reference to attach the separate root container to.
| fullscreen | `fsPlaceholder` | `'Search this site'` | Placeholder of the input element in the fullscreen UI.
| fullscreen | `fsCloseText` | `'Close'` | Text for the <kbd>Close</kbd> button.
| fullscreen | `fsScrollLock` | `true` | Whether to automatically scroll lock the body element when the fullscreen UI is opened.
| all except target | `tip` | `true` | Whether to show the tip icon. When hovered over, this shows advanced usage information (e.g. how to perform phrase queries).
| target | `target` | `undefined` | `id` of the element, or an element reference to attach results to.<br><br>Required if using `mode='target'`.
Expand Down
2 changes: 1 addition & 1 deletion packages/mdbook-infisearch/src/infisearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ infisearch.init({
mode,
dropdownAlignment: 'bottom-start',
target: document.getElementById('infisearch-mdbook-target'),
fsInputButtonText: 'Search',
fsButtonPlaceholder: 'Search',
sourceFilesUrl: base_url,
resultsRenderOpts: {
addSearchedTerms: 'search',
Expand Down
3 changes: 2 additions & 1 deletion packages/search-ui/src/InputManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,15 @@ export class IManager {
!that._mrlInputEl.value,
isDone,
isError,
that._mrlOptions.uiOptions.translations,
);

that._mrlState.replaceWith(newIndicatorElement);
that._mrlState = newIndicatorElement;
}

_mrlRefreshHeader(query?: Query) {
const el = headerRender(query, this._mrlGetOrSetFiltersShown);
const el = headerRender(query, this._mrlGetOrSetFiltersShown, this._mrlOptions.uiOptions.translations);
this._mrlHeader.replaceWith(el);
this._mrlHeader = el;
}
Expand Down
41 changes: 35 additions & 6 deletions packages/search-ui/src/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,56 @@ export interface NumericFilterBinding {
ltePlaceholder?: string,
}

export interface Translations {
resultsLabel: string,
fsButtonPlaceholder?: string,
fsButtonLabel: string,
fsPlaceholder: string,
fsCloseText: string,
filtersButton: string,
numResultsFound: string,
startSearching: string,
startingUp: string,
navigation: string,
tipHeader: string,
tip: string,
example: string,
tipRows: {
searchPhrases: string,
requireTerm: string,
excludeTerm: string,
flipResults: string,
groupTerms: string,
searchPrefixes: string,
searchSections: string,
exSearchPhrases: string,
exRequireTerm: string,
exExcludeTerm: string,
exFlipResults: string,
exGroupTerms: string,
exSearchPrefixes: string,
exSearchSections: string[],
}

error: string,
}

export interface UiOptions {
input: HTMLInputElement,
inputDebounce?: number,
preprocessQuery: (input: string) => string,
mode: UiMode,
isMobileDevice: () => boolean,
dropdownAlignment?: 'bottom-start' | 'bottom-end',
label: string,
resultsLabel: string,
fsInputButtonText: string,
fsInputLabel: string,
fsContainer?: HTMLElement,
fsPlaceholder?: string,
fsCloseText?: string,
fsScrollLock: boolean,
target?: HTMLElement,
tip: boolean,
resultsPerPage?: number,
sortFields: { [fieldName: string]: { asc: string, desc: string } },
multiSelectFilters: MultiSelectFilterBinding[],
numericFilters: NumericFilterBinding[],
translations: Translations,
// This is specific to the default resultsRender implementation,
// pulling it up as its a common option
sourceFilesUrl?: string,
Expand Down
14 changes: 9 additions & 5 deletions packages/search-ui/src/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,13 @@ function init(options: Options): {
const {
input, mode,
dropdownAlignment,
label,
fsInputButtonText, fsInputLabel, fsScrollLock,
fsScrollLock,
target,
translations: {
fsButtonPlaceholder,
fsButtonLabel,
resultsLabel,
},
} = uiOptions;

const searcher = new Searcher(options.searcherOptions);
Expand Down Expand Up @@ -193,10 +197,10 @@ function init(options: Options): {
// Otherwise, the input should be focused
initState._mrlShowDropdown();
}
setDropdownInputAria(input, resultContainer, label, originalPlaceholder);
setDropdownInputAria(input, resultContainer, resultsLabel, originalPlaceholder);
} else {
initState._mrlHideDropdown();
unsetDropdownInputAria(input, resultContainer, fsInputLabel, fsInputButtonText);
unsetDropdownInputAria(input, resultContainer, fsButtonLabel, fsButtonPlaceholder);
}
}
toggleUiMode();
Expand Down Expand Up @@ -231,7 +235,7 @@ function init(options: Options): {
addFsTriggerInputListeners();
} else if (input && mode === UiMode.Fullscreen) {
// Fullscreen-only mode
setFsTriggerInput(input, fsInputButtonText, fsInputLabel);
setFsTriggerInput(input, fsButtonPlaceholder, fsButtonLabel);
addFsTriggerInputListeners();
} else if (input && mode === UiMode.Target) {
// Target
Expand Down
10 changes: 5 additions & 5 deletions packages/search-ui/src/search/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Options, UiMode } from '../Options';
import { listItemRender } from '../searchResultTransform/listItemRender';
import { TRANSLATIONS } from '../translations/en';

export function prepareOptions(options: Options) {
options.searcherOptions = options.searcherOptions || ({} as any);
Expand Down Expand Up @@ -53,11 +54,10 @@ export function prepareOptions(options: Options) {
uiOptions.resultsPerPage = uiOptions.resultsPerPage || 10;
uiOptions.maxSubMatches = uiOptions.maxSubMatches || 2;

uiOptions.label = uiOptions.label || 'Search this site';
uiOptions.resultsLabel = uiOptions.resultsLabel || 'Site results';
uiOptions.fsInputLabel = uiOptions.fsInputLabel || 'Search';
uiOptions.fsPlaceholder = uiOptions.fsPlaceholder || 'Search this site';
uiOptions.fsCloseText = uiOptions.fsCloseText || 'Close';
uiOptions.translations = {
...TRANSLATIONS,
...(uiOptions.translations || {}),
};
if (!('fsScrollLock' in uiOptions)) {
uiOptions.fsScrollLock = true;
}
Expand Down
28 changes: 15 additions & 13 deletions packages/search-ui/src/search/rootContainers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ export function dropdownRootRender(
return [root, scrollContainer];
}

export function setFsTriggerInput(input: HTMLElement, fsInputButtonText: string, fsInputLabel: string) {
export function setFsTriggerInput(input: HTMLElement, fsButtonPlaceholder: string, fsButtonLabel: string) {
input.setAttribute('autocomplete', 'off');
input.setAttribute('readonly', '');
input.setAttribute('role', 'button');
input.setAttribute('aria-label', fsInputLabel);
if (fsInputButtonText) input.setAttribute('placeholder', fsInputButtonText);
input.setAttribute('aria-label', fsButtonLabel);
if (fsButtonPlaceholder) input.setAttribute('placeholder', fsButtonPlaceholder);
input.classList.add('infi-button-input');
}

Expand All @@ -120,18 +120,18 @@ function unsetFsTriggerInput(input: HTMLElement, originalPlaceholder: string) {
export function setDropdownInputAria(
input: HTMLElement,
resultContainer: HTMLElement,
label: string,
resultsLabel: string,
originalPlaceholder: string,
) {
unsetFsTriggerInput(input, originalPlaceholder);
setInputAria(input, resultContainer, label);
setInputAria(input, resultContainer, resultsLabel);
}

export function unsetDropdownInputAria(
input: HTMLElement,
resultContainer: HTMLElement,
fsInputLabel: string,
fsInputButtonText: string,
fsButtonLabel: string,
fsButtonPlaceholder: string,
) {
resultContainer.removeAttribute('role');
resultContainer.removeAttribute('aria-label');
Expand All @@ -140,7 +140,7 @@ export function unsetDropdownInputAria(
input.removeAttribute('aria-autocomplete');
input.removeAttribute('aria-controls');
unsetActiveDescendant(input);
setFsTriggerInput(input, fsInputButtonText, fsInputLabel);
setFsTriggerInput(input, fsButtonPlaceholder, fsButtonLabel);
}

// Incremental Id for pages with multiple UIs, for aria attributes.
Expand All @@ -151,10 +151,12 @@ export function fsRootRender(
onClose: (isKeyboardClose: boolean) => void,
): [HTMLElement, HTMLInputElement, () => void, (isKeyboardClose: boolean) => void] {
const {
fsPlaceholder,
fsCloseText,
translations: {
fsPlaceholder,
fsCloseText,
resultsLabel,
},
fsContainer,
label,
} = opts.uiOptions;

const labelId = `infi-fs-label-${fsId}`;
Expand Down Expand Up @@ -212,7 +214,7 @@ export function fsRootRender(
innerRoot.onclick = (ev) => ev.stopPropagation();
innerRoot.onmousedown = (ev) => ev.stopPropagation();

setInputAria(inputEl, resultContainer, label);
setInputAria(inputEl, resultContainer, resultsLabel);

const rootBackdropEl = h('div', { class: 'infi-fs-backdrop' }, innerRoot);

Expand Down Expand Up @@ -265,5 +267,5 @@ export function targetRender(
resultContainer,
);

setInputAria(input, resultContainer, opts.uiOptions.label);
setInputAria(input, resultContainer, opts.uiOptions.translations.resultsLabel);
}
42 changes: 22 additions & 20 deletions packages/search-ui/src/search/tips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ export default function createTipButton(
opts: UiOptions,
cfg: InfiConfig,
): HTMLElement | string {
if (opts.tip === false) {
const { tip, translations } = opts;

if (tip === false) {
return '';
}

Expand All @@ -21,40 +23,40 @@ export default function createTipButton(

const tipListBody = h('tbody', {});

const tipRows = translations.tipRows;

if (cfg.indexingConfig.withPositions) {
tipListBody.append(createRow(
'Search for phrases',
wrapInCode('"for tomorrow"'),
tipRows.searchPhrases,
wrapInCode(tipRows.exSearchPhrases),
));
}

tipListBody.append(
createRow(
'Require a term',
wrapInCode('+sunny weather'),
tipRows.requireTerm,
wrapInCode(tipRows.exRequireTerm),
),
createRow(
'Exclude a term',
wrapInCode('-cloudy sunny'),
tipRows.excludeTerm,
wrapInCode(tipRows.exExcludeTerm),
),
createRow(
'Flip search results',
wrapInCode('~rainy'),
tipRows.flipResults,
wrapInCode(tipRows.exFlipResults),
),
createRow(
'Group terms together',
wrapInCode('~(sunny warm cloudy)'),
tipRows.groupTerms,
wrapInCode(tipRows.exGroupTerms),
),
createRow(
'Search for prefixes',
wrapInCode('run*'),
tipRows.searchPrefixes,
wrapInCode(tipRows.exSearchPrefixes),
),
createRow(
'Search only specific sections',
h('ul', {},
h('li', {}, wrapInCode('title:forecast')),
h('li', {}, wrapInCode('heading:sunny')),
h('li', {}, wrapInCode('body:(rainy gloomy)')),
tipRows.searchSections,
h('ul', {},
...tipRows.exSearchSections.map(t => h('li', {}, wrapInCode(t))),
),
),
);
Expand All @@ -65,14 +67,14 @@ export default function createTipButton(
h(
'thead',
{ class: 'infi-tip-table-header' },
h('tr', {}, h('th', { scope: 'col' }, 'Tip'), h('th', {}, 'Example')),
h('tr', {}, h('th', { scope: 'col' }, translations.tip), h('th', {}, translations.example)),
),
tipListBody,
);
const tipPopup = h(
'div', { class: 'infi-tip-popup-root' },
h('div', { class: 'infi-tip-popup' },
h('div', { class: 'infi-tip-popup-title' }, '🔎 Advanced search tips'),
h('div', { class: 'infi-tip-popup-title' }, translations.tipHeader),
tipList,
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function resultSeparator(
focusOption: (el: HTMLElement) => void,
query: Query,
) {
const { resultsPerPage } = options.uiOptions;
const { resultsPerPage, translations } = options.uiOptions;
const footer = h('div', { class: 'infi-footer', tabindex: '-1' });
if (!query.resultsTotal) {
return footer;
Expand Down Expand Up @@ -43,7 +43,7 @@ export function resultSeparator(
const isDomFocused = document.activeElement === loadMoreButton;

loadMoreButtonWrapped.remove();
footer.append(stateRender(false, true, false, false, false));
footer.append(stateRender(false, true, false, false, false, translations));
// Announce footer information
if (isDomFocused) footer.focus({ preventScroll: true });

Expand Down
Loading

0 comments on commit cf69c5c

Please sign in to comment.