Make a good part of the app localizable and add French translations (#325)

Co-authored-by: Yohan Boniface <yohanboniface@free.fr>
This commit is contained in:
Arnaud Peich
2022-10-28 18:11:08 +02:00
committed by GitHub
parent ec20e7fb68
commit 79deeca640
78 changed files with 2364 additions and 665 deletions

View File

@@ -8,6 +8,9 @@ import {cssSelectBtn} from 'app/client/ui2018/select';
import {isValidHex} from 'app/common/gutil';
import {BindableValue, Computed, Disposable, dom, Observable, onKeyDown, styled} from 'grainjs';
import {defaultMenuOptions, IOpenController, setPopupToCreateDom} from 'popweasel';
import {makeT} from 'app/client/lib/localization';
const t = makeT('ui2018.ColorSelect');
export interface StyleOptions {
textColor: ColorOption,
@@ -61,7 +64,7 @@ export function colorSelect(
onSave,
onOpen,
onRevert,
placeholder = 'Default cell style',
placeholder = t('DefaultCellStyle'),
} = options;
const selectBtn = cssSelectBtn(
cssContent(
@@ -185,12 +188,12 @@ function buildColorPicker(ctl: IOpenController,
}),
cssButtonRow(
primaryButton('Apply',
primaryButton(t('Apply'),
dom.on('click', () => ctl.close()),
dom.boolAttr("disabled", notChanged),
testId('colors-save')
),
basicButton('Cancel',
basicButton(t('Cancel'),
dom.on('click', () => revert()),
testId('colors-cancel')
)

View File

@@ -5,6 +5,7 @@
*
* Workspace is a clickable link and document and page names are editable labels.
*/
import {makeT} from 'app/client/lib/localization';
import { urlState } from 'app/client/models/gristUrlState';
import { cssHideForNarrowScreen, mediaNotSmall, testId, theme } from 'app/client/ui2018/cssVars';
import { editableLabel } from 'app/client/ui2018/editableLabel';
@@ -15,6 +16,8 @@ import { userOverrideParams } from 'app/common/gristUrls';
import { BindableValue, dom, Observable, styled } from 'grainjs';
import { tooltip } from 'popweasel';
const t = makeT('ui2018.breadcrumbs');
export const cssBreadcrumbs = styled('div', `
color: ${theme.lightText};
white-space: nowrap;
@@ -82,11 +85,6 @@ interface PartialWorkspace {
name: string;
}
const fiddleExplanation = (
'You may make edits, but they will create a new copy and will\n' +
'not affect the original document.'
);
export function docBreadcrumbs(
workspace: Observable<PartialWorkspace|null>,
docName: Observable<string>,
@@ -143,20 +141,20 @@ export function docBreadcrumbs(
dom.maybe(options.isPublic, () => cssPublicIcon('PublicFilled', testId('bc-is-public'))),
dom.domComputed((use) => {
if (options.isSnapshot && use(options.isSnapshot)) {
return cssTag('snapshot', testId('snapshot-tag'));
return cssTag(t('Snapshot'), testId('snapshot-tag'));
}
if (use(options.isFork)) {
return cssTag('unsaved', testId('unsaved-tag'));
return cssTag(t('Unsaved'), testId('unsaved-tag'));
}
if (use(options.isRecoveryMode)) {
return cssAlertTag('recovery mode',
return cssAlertTag(t('RecoveryMode'),
dom('a', dom.on('click', () => options.cancelRecoveryMode()),
icon('CrossSmall')),
testId('recovery-mode-tag'));
}
const userOverride = use(options.userOverride);
if (userOverride) {
return cssAlertTag(userOverride.user?.email || 'override',
return cssAlertTag(userOverride.user?.email || t('Override'),
dom('a',
urlState().setHref(userOverrideParams(null)),
icon('CrossSmall')
@@ -165,7 +163,7 @@ export function docBreadcrumbs(
);
}
if (use(options.isFiddle)) {
return cssTag('fiddle', tooltip({title: fiddleExplanation}), testId('fiddle-tag'));
return cssTag(t('Fiddle'), tooltip({title: t('FiddleExplanation')}), testId('fiddle-tag'));
}
}),
separator(' / ',

View File

@@ -1,4 +1,5 @@
import { Command } from 'app/client/components/commands';
import { makeT } from 'app/client/lib/localization';
import { NeedUpgradeError, reportError } from 'app/client/models/errors';
import { textButton } from 'app/client/ui2018/buttons';
import { cssCheckboxSquare, cssLabel, cssLabelText } from 'app/client/ui2018/checkbox';
@@ -10,6 +11,8 @@ import { BindableValue, Computed, dom, DomElementArg, DomElementMethod, IDomArgs
MaybeObsArray, MutableObsArray, Observable, styled } from 'grainjs';
import * as weasel from 'popweasel';
const t = makeT('ui2018.menus');
export interface IOptionFull<T> {
value: T;
label: string;
@@ -175,7 +178,7 @@ export function multiSelect<T>(selectedOptions: MutableObsArray<T>,
const selectedOptionsText = Computed.create(null, selectedOptionsSet, (use, selectedOpts) => {
if (selectedOpts.size === 0) {
return options.placeholder ?? 'Select fields';
return options.placeholder ?? t('SelectFields');
}
const optionArray = Array.isArray(availableOptions) ? availableOptions : use(availableOptions);
@@ -309,8 +312,8 @@ export function upgradableMenuItem(needUpgrade: boolean, action: () => void, ...
export function upgradeText(needUpgrade: boolean, onClick: () => void) {
if (!needUpgrade) { return null; }
return menuText(dom('span', '* Workspaces are available on team plans. ',
cssUpgradeTextButton('Upgrade now', dom.on('click', () => onClick()))));
return menuText(dom('span', t('WorkspacesAvailableOnTeamPlans'),
cssUpgradeTextButton(t('UpgradeNow'), dom.on('click', () => onClick()))));
}
/**

View File

@@ -1,4 +1,5 @@
import {FocusLayer} from 'app/client/lib/FocusLayer';
import {makeT} from 'app/client/lib/localization';
import {reportError} from 'app/client/models/errors';
import {cssInput} from 'app/client/ui/cssInput';
import {prepareForTransition, TransitionWatcher} from 'app/client/ui/transitions';
@@ -11,6 +12,8 @@ import {Computed, Disposable, dom, DomContents, DomElementArg, input, keyframes,
MultiHolder, Observable, styled} from 'grainjs';
import {cssMenuElem} from 'app/client/ui2018/menus';
const t = makeT('ui2018.modals');
// IModalControl is passed into the function creating the body of the modal.
export interface IModalControl {
// Observable for whether there is work in progress that's delaying the closing of the modal. It
@@ -303,13 +306,13 @@ export function saveModal(createFunc: (ctl: IModalControl, owner: MultiHolder) =
cssModalTitle(options.title, testId('modal-title')),
cssModalBody(options.body),
cssModalButtons(
bigPrimaryButton(options.saveLabel || 'Save',
bigPrimaryButton(options.saveLabel || t('Save'),
dom.boolAttr('disabled', isSaveDisabled),
dom.on('click', save),
testId('modal-confirm'),
),
options.extraButtons,
options.hideCancel ? null : bigBasicButton('Cancel',
options.hideCancel ? null : bigBasicButton(t('Cancel'),
dom.on('click', () => ctl.close()),
testId('modal-cancel'),
),
@@ -423,7 +426,7 @@ export function invokePrompt(
const prom = new Promise<string|undefined>((resolve) => {
onResolve = resolve;
});
promptModal(title, onResolve!, btnText ?? 'Ok', initial, placeholder, () => {
promptModal(title, onResolve!, btnText ?? t('Ok'), initial, placeholder, () => {
if (onResolve) {
onResolve(undefined);
}

View File

@@ -1,4 +1,5 @@
import { isDesktop } from 'app/client/lib/browserInfo';
import { makeT } from 'app/client/lib/localization';
import { cssEditorInput } from "app/client/ui/HomeLeftPane";
import { itemHeader, itemHeaderWrapper, treeViewContainer } from "app/client/ui/TreeViewComponentCss";
import { theme } from "app/client/ui2018/cssVars";
@@ -6,6 +7,8 @@ import { icon } from "app/client/ui2018/icons";
import { menu, menuItem, menuText } from "app/client/ui2018/menus";
import { dom, domComputed, DomElementArg, makeTestId, observable, Observable, styled } from "grainjs";
const t = makeT('ui2018.pages');
const testId = makeTestId('test-docpage-');
// the actions a page can do
@@ -31,13 +34,13 @@ export function buildPageDom(name: Observable<string>, actions: PageActions, ...
const isRenaming = observable(false);
const pageMenu = () => [
menuItem(() => isRenaming.set(true), "Rename", testId('rename'),
menuItem(() => isRenaming.set(true), t("Rename"), testId('rename'),
dom.cls('disabled', actions.isReadonly)),
menuItem(actions.onRemove, 'Remove', testId('remove'),
menuItem(actions.onRemove, t('Remove'), testId('remove'),
dom.cls('disabled', (use) => use(actions.isReadonly) || actions.isRemoveDisabled())),
menuItem(actions.onDuplicate, 'Duplicate Page', testId('duplicate'),
menuItem(actions.onDuplicate, t('DuplicatePage'), testId('duplicate'),
dom.cls('disabled', actions.isReadonly)),
dom.maybe(actions.isReadonly, () => menuText('You do not have edit access to this document')),
dom.maybe(actions.isReadonly, () => menuText(t('NoEditAccess'))),
];
let pageElem: HTMLElement;

View File

@@ -3,6 +3,7 @@
* Takes a `SearchModel` that controls the search behavior.
*/
import { allCommands, createGroup } from 'app/client/components/commands';
import { makeT } from 'app/client/lib/localization';
import { reportError } from 'app/client/models/AppModel';
import { SearchModel } from 'app/client/models/SearchModel';
import { hoverTooltip } from 'app/client/ui/tooltips';
@@ -16,6 +17,8 @@ import debounce = require('lodash/debounce');
export * from 'app/client/models/SearchModel';
const t = makeT('ui2018.search');
const EXPAND_TIME = .5;
const searchWrapper = styled('div', `
@@ -143,7 +146,7 @@ export function searchBar(model: SearchModel, testId: TestId = noTestId) {
model.isOpen.set(_value === undefined ? !model.isOpen.get() : _value);
}, 100);
const inputElem: HTMLInputElement = searchInput(model.value, {onInput: true},
{type: 'text', placeholder: 'Search in document'},
{type: 'text', placeholder: t('SearchInDocument')},
dom.on('blur', () => (
keepExpanded ?
setTimeout(() => inputElem.focus(), 0) :
@@ -182,7 +185,7 @@ export function searchBar(model: SearchModel, testId: TestId = noTestId) {
const noMatch = use(model.noMatch);
const isEmpty = use(model.isEmpty);
if (isEmpty) { return null; }
if (noMatch) { return cssLabel("No results"); }
if (noMatch) { return cssLabel(t("NoResults")); }
return [
cssArrowBtn(
icon('Dropdown'),
@@ -192,7 +195,7 @@ export function searchBar(model: SearchModel, testId: TestId = noTestId) {
dom.on('click', () => model.findNext()),
hoverTooltip(
[
'Find Next ',
t('FindNext'),
cssShortcut(`(${['Enter', allCommands.findNext.humanKeys].join(', ')})`),
],
{key: 'searchArrowBtnTooltip'}
@@ -206,7 +209,7 @@ export function searchBar(model: SearchModel, testId: TestId = noTestId) {
dom.on('click', () => model.findPrev()),
hoverTooltip(
[
'Find Previous ',
t('FindPrevious'),
cssShortcut(allCommands.findPrev.getKeysDesc()),
],
{key: 'searchArrowBtnTooltip'}