Merge pull request #400 from CamilleLegeron/missing-translations

Missing translations
pull/410/head
Paul Fitzpatrick 1 year ago committed by GitHub
commit 7925641641
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +1,4 @@
import { makeT } from 'app/client/lib/localization';
import * as commands from 'app/client/components/commands';
import { urlState } from 'app/client/models/gristUrlState';
import { IOnBoardingMsg, startOnBoarding } from "app/client/ui/OnBoardingPopups";
@ -6,25 +7,26 @@ import { icon } from "app/client/ui2018/icons";
import { cssLink } from "app/client/ui2018/links";
import { dom, styled } from "grainjs";
const t = makeT('WelcomeTour');
export const welcomeTour: IOnBoardingMsg[] = [
{
title: 'Editing Data',
title: t('Editing Data'),
body: () => [
dom('p',
'Double-click or hit ', Key(KeyContent('Enter')), ' on a cell to edit it. ',
'Start with ', Key(KeyStrong('=')), ' to enter a formula.'
)
t('Double-click or hit {{enter}} on a cell to edit it. ', {enter: Key(KeyContent(t('Enter')))}),
t('Start with {{equal}} to enter a formula.', { equal: Key(KeyStrong('=')) }))
],
selector: '.field_clip',
placement: 'bottom',
},
{
selector: '.tour-creator-panel',
title: 'Configuring your document',
title: t('Configuring your document'),
body: () => [
dom('p',
'Toggle the ', dom('em', 'creator panel'), ' to format columns, ',
'convert to card view, select data, and more.'
t('Toggle the {{creatorPanel}} to format columns, ', {creatorPanel: dom('em', t('creator panel'))}),
t('convert to card view, select data, and more.')
)
],
placement: 'left',
@ -32,50 +34,53 @@ export const welcomeTour: IOnBoardingMsg[] = [
},
{
selector: '.tour-type-selector',
title: 'Customizing columns',
title: t('Customizing columns'),
body: () => [
dom('p',
'Set formatting options, formulas, or column types, such as dates, choices, or attachments. '),
t('Set formatting options, formulas, or column types, such as dates, choices, or attachments. ')),
dom('p',
'Make it relational! Use the ', Key('Reference'), ' type to link tables. '
t('Make it relational! Use the {{ref}} type to link tables. ', {ref: Key(t('Reference'))}),
)
],
placement: 'right',
},
{
selector: '.tour-add-new',
title: 'Building up',
title: t('Building up'),
body: () => [
dom('p', 'Use ', Key('Add New'), ' to add widgets, pages, or import more data. ')
dom('p', t('Use {{addNew}} to add widgets, pages, or import more data. ', {addNew: Key(t('Add New'))}))
],
placement: 'right',
},
{
selector: '.tour-share-icon',
title: 'Sharing',
title: t('Sharing'),
body: () => [
dom('p', 'Use the Share button (', TopBarButtonIcon('Share'), ') to share the document or export data.')
dom('p', t('Use the Share button ({{share}}) to share the document or export data.',
{share: TopBarButtonIcon(t('Share'))}))
],
placement: 'bottom',
cropPadding: true,
},
{
selector: '.tour-help-center',
title: 'Flying higher',
title: t('Flying higher'),
body: () => [
dom('p', 'Use ', Key(GreyIcon('Help'), 'Help Center'), ' for documentation or questions.'),
dom('p', t('Use {{helpCenter}} for documentation or questions.',
{helpCenter: Key(GreyIcon('Help'), t('Help Center'))})),
],
placement: 'right',
},
{
selector: '.tour-welcome',
title: 'Welcome to Grist!',
title: t('Welcome to Grist!'),
body: () => [
dom('p', 'Browse our ',
cssLink({target: '_blank', href: urlState().makeUrl({homePage: "templates"})},
'template library', cssInlineIcon('FieldLink')),
"to discover what's possible and get inspired."
),
dom('p', t("Browse our {{templateLibrary}} to discover what's possible and get inspired.",
{
templateLibrary: cssLink({ target: '_blank', href: urlState().makeUrl({ homePage: "templates" }) },
t('template library'), cssInlineIcon('FieldLink'))
}
)),
],
showHasModal: true,
}

@ -1,3 +1,4 @@
import { makeT } from 'app/client/lib/localization';
import {allCommands} from 'app/client/components/commands';
import {GristDoc} from 'app/client/components/GristDoc';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
@ -7,6 +8,8 @@ import {theme, vars} from 'app/client/ui2018/cssVars';
import {ConditionalStyle} from 'app/client/widgets/ConditionalStyle';
import {Computed, Disposable, dom, DomContents, fromKo, styled} from 'grainjs';
const t = makeT('CellStyle');
export class CellStyle extends Disposable {
constructor(
@ -20,8 +23,8 @@ export class CellStyle extends Disposable {
public buildDom(): DomContents {
return [
cssLine(
cssLabel('CELL STYLE'),
cssButton('Open row styles', dom.on('click', allCommands.viewTabOpen.run)),
cssLabel(t('CELL STYLE')),
cssButton(t('Open row styles'), dom.on('click', allCommands.viewTabOpen.run)),
),
cssRow(
dom.domComputedOwned(fromKo(this._field.config.style), (holder, options) => {
@ -58,12 +61,12 @@ export class CellStyle extends Disposable {
}, {
onSave: () => options.save(),
onRevert: () => options.revert(),
placeholder: use => use(hasMixedStyle) ? 'Mixed style' : 'Default cell style'
placeholder: use => use(hasMixedStyle) ? t('Mixed style') : t('Default cell style')
}
);
}),
),
dom.create(ConditionalStyle, "Cell Style", this._field, this._gristDoc, fromKo(this._field.config.multiselect))
dom.create(ConditionalStyle, t("Cell Style"), this._field, this._gristDoc, fromKo(this._field.config.multiselect))
];
}
}

@ -1,3 +1,4 @@
import {makeT} from 'app/client/lib/localization';
import {DataRowModel} from 'app/client/models/DataRowModel';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
import {KoSaveableObservable} from 'app/client/models/modelUtil';
@ -13,6 +14,8 @@ export type IChoiceOptions = Style
export type ChoiceOptions = Record<string, IChoiceOptions | undefined>;
export type ChoiceOptionsByName = Map<string, IChoiceOptions | undefined>;
const t = makeT('ChoiceTextBox');
export function getRenderFillColor(choiceOptions?: IChoiceOptions) {
return choiceOptions?.fillColor ?? DEFAULT_FILL_COLOR;
}
@ -78,7 +81,7 @@ export class ChoiceTextBox extends NTextBox {
);
return [
super.buildConfigDom(),
cssLabel('CHOICES'),
cssLabel(t('CHOICES')),
cssRow(
dom.autoDispose(disabled),
dom.autoDispose(mixed),

@ -1,3 +1,4 @@
import {makeT} from 'app/client/lib/localization';
import {GristDoc} from 'app/client/components/GristDoc';
import {ColumnRec} from 'app/client/models/DocModel';
import {KoSaveableObservable} from 'app/client/models/modelUtil';
@ -18,6 +19,7 @@ import {Computed, Disposable, dom, DomContents, makeTestId, Observable, styled}
import debounce = require('lodash/debounce');
const testId = makeTestId('test-widget-style-');
const t = makeT('ConditionalStyle');
export class ConditionalStyle extends Disposable {
// Holds data from currently selected record (holds data only when this field has conditional styles).
@ -71,12 +73,12 @@ export class ConditionalStyle extends Disposable {
{ style: 'margin-top: 16px' },
withInfoTooltip(
textButton(
'Add conditional style',
t('Add conditional style'),
testId('add-conditional-style'),
dom.on('click', () => this._ruleOwner.addEmptyRule()),
dom.prop('disabled', this._disabled),
),
(this._label === 'Row Style'
(this._label === t('Row Style')
? GristTooltips.addRowConditionalStyle()
: GristTooltips.addColumnConditionalStyle()
),
@ -113,8 +115,8 @@ export class ConditionalStyle extends Disposable {
const errorMessage = Computed.create(owner, use => {
const value = use(currentValue);
return (!use(hasError) ? '' :
isRaisedException(value) ? 'Error in style rule' :
'Rule must return True or False');
isRaisedException(value) ? t('Error in style rule') :
t('Rule must return True or False'));
});
return dom('div',
testId(`conditional-rule-${ruleIndex}`),
@ -153,7 +155,7 @@ export class ConditionalStyle extends Disposable {
)
),
cssRow(
textButton('Add another rule',
textButton(t('Add another rule'),
dom.on('click', () => this._ruleOwner.addEmptyRule()),
testId('add-another-rule'),
dom.prop('disabled', use => this._disabled && use(this._disabled))

@ -1,9 +1,12 @@
import { makeT } from 'app/client/lib/localization';
import {ACSelectItem, buildACSelect} from "app/client/lib/ACSelect";
import {Computed, IDisposableOwner, Observable} from "grainjs";
import {ACIndexImpl} from "app/client/lib/ACIndex";
import {testId} from 'app/client/ui2018/cssVars';
import {currencies} from 'app/common/Locales';
const t = makeT('CurrencyPicker');
interface CurrencyPickerOptions {
// The label to use in the select menu for the default option.
defaultCurrencyLabel: string;
@ -40,7 +43,7 @@ export function buildCurrencyPicker(
save(_, item: ACSelectItem | undefined) {
// Save only if we have found a match
if (!item) {
throw new Error("Invalid currency");
throw new Error(t("Invalid currency"));
}
// For default value, return undefined to use default currency for document.
onSave(item.value === defaultCurrencyLabel ? undefined : item.value);

@ -1,4 +1,5 @@
import {GristDoc} from 'app/client/components/GristDoc';
import {makeT} from 'app/client/lib/localization';
import {FocusLayer} from 'app/client/lib/FocusLayer';
import {createObsArray} from 'app/client/lib/koArrayWrap';
import {localStorageBoolObs} from 'app/client/lib/localStorageObs';
@ -35,6 +36,7 @@ import maxSize from 'popper-max-size-modifier';
import flatMap = require('lodash/flatMap');
const testId = makeTestId('test-discussion-');
const t = makeT('DiscussionEditor');
const COMMENTS_LIMIT = 200;
interface DiscussionPopupProps {
@ -68,7 +70,7 @@ export class CellWithComments extends Disposable implements ICellView {
public async reply(comment: CellRec, text: string): Promise<void> {
const author = commentAuthor(this.gristDoc);
await this.gristDoc.docData.bundleActions("Reply to a comment", () => Promise.all([
await this.gristDoc.docData.bundleActions(t("Reply to a comment"), () => Promise.all([
this.gristDoc.docModel.cells.sendTableAction([
"AddRecord",
null,
@ -150,7 +152,7 @@ export class EmptyCell extends CellWithComments implements ICellView {
})
}
];
await props.gristDoc.docData.sendActions([addComment], 'Started discussion');
await props.gristDoc.docData.sendActions([addComment], t('Started discussion'));
}
}
@ -221,9 +223,9 @@ class EmptyCellView extends Disposable {
text: this._newText,
onSave: () => this.props.onSave(this._newText.get()),
onCancel: () => this.props.closeClicked?.(),
editorArgs: [{placeholder: 'Write a comment'}],
mainButton: 'Comment',
buttons: ['Cancel'],
editorArgs: [{placeholder: t('Write a comment')}],
mainButton: t('Comment'),
buttons: [t('Cancel')],
args: [testId('editor-start')]
}));
}
@ -274,7 +276,7 @@ class CellWithCommentsView extends Disposable implements IDomComponent {
public buildDom() {
return cssTopic(
dom.maybe(this._truncated, () => cssTruncate(`Showing last ${COMMENTS_LIMIT} comments`)),
dom.maybe(this._truncated, () => cssTruncate(t("Showing last {{nb}} comments", {nb: COMMENTS_LIMIT}))),
cssTopic.cls('-panel', this.props.panel),
domOnCustom(CommentView.EDIT, (s: CommentView) => this._onEditComment(s)),
domOnCustom(CommentView.CANCEL, (s: CommentView) => this._onCancelEdit()),
@ -358,7 +360,7 @@ class CellWithCommentsView extends Disposable implements IDomComponent {
onSave: () => this._save(),
onCancel: () => this.props.closeClicked?.(),
mainButton: 'Send',
editorArgs: [{placeholder: 'Comment'}],
editorArgs: [{placeholder: t('Comment')}],
args: [testId('editor-add')]
}));
}
@ -464,8 +466,8 @@ class CommentView extends Disposable {
const text = Observable.create(owner, comment.text.peek() ?? '');
return dom.create(CommentEntry, {
text,
mainButton: 'Save',
buttons: ['Cancel'],
mainButton: t('Save'),
buttons: [t('Cancel')],
onSave: async () => {
const value = text.get();
text.set("");
@ -503,7 +505,7 @@ class CommentView extends Disposable {
dom.maybe(use => !use(this.isEditing) && !this.props.isReply && !use(comment.resolved),
() => dom.domComputed(use => {
if (!use(this.replying)) {
return cssReplyButton(icon('Message'), 'Reply',
return cssReplyButton(icon('Message'), t('Reply'),
testId('comment-reply-button'),
dom.on('click', withStop(() => this.replying.set(true))),
dom.style('margin-left', use2 => use2(this._hasReplies) ? '16px' : '0px'),
@ -513,8 +515,8 @@ class CommentView extends Disposable {
return dom.create(CommentEntry, {
text,
args: [dom.style('margin-top', '8px'), testId('editor-reply')],
mainButton: 'Reply',
buttons: ['Cancel'],
mainButton: t('Reply'),
buttons: [t('Cancel')],
onSave: async () => {
const value = text.get();
this.replying.set(false);
@ -522,7 +524,7 @@ class CommentView extends Disposable {
},
onCancel: () => this.replying.set(false),
onClick: (button) => {
if (button === 'Cancel') {
if (button === t('Cancel')) {
this.replying.set(false);
}
},
@ -538,7 +540,7 @@ class CommentView extends Disposable {
testId('comment-resolved'),
icon('FieldChoice'),
cssResolvedText(dom.text(
`Marked as resolved`
t(`Marked as resolved`)
)));
}),
]),
@ -554,23 +556,23 @@ class CommentView extends Disposable {
!canResolve ? null :
menuItem(
() => this.props.topic.resolve(this.props.comment),
'Resolve'
t('Resolve')
),
!comment.resolved() ? null :
menuItem(
() => this.props.topic.open(comment),
'Open'
t('Open')
),
menuItem(
() => this.props.topic.remove(comment),
'Remove',
t('Remove'),
dom.cls('disabled', use => {
return currentUser !== use(comment.userRef);
})
),
menuItem(
() => this._edit(),
'Edit',
t('Edit'),
dom.cls('disabled', use => {
return currentUser !== use(comment.userRef);
})
@ -605,7 +607,7 @@ class CommentEntry extends Disposable {
public buildDom() {
const text = this.props.text;
const clickBuilder = (button: string) => dom.on('click', () => {
if (button === "Cancel") {
if (button === t("Cancel")) {
this.props.onCancel?.();
} else {
this.props.onClick?.(button);
@ -699,9 +701,9 @@ export class DiscussionPanel extends Disposable implements IDomComponent {
const tables = Computed.create(owner, use => {
// Filter out those tables that are not available by ACL.
if (use(this._currentPageKo)) {
return [...new Set(use(viewSections).map(vs => use(vs.table)).filter(t => use(t.tableId)))];
return [...new Set(use(viewSections).map(vs => use(vs.table)).filter(tb => use(tb.tableId)))];
} else {
return use(this._grist.docModel.visibleTables.getObservable()).filter(t => use(t.tableId));
return use(this._grist.docModel.visibleTables.getObservable()).filter(tb => use(tb.tableId));
}
});
@ -774,8 +776,8 @@ export class DiscussionPanel extends Disposable implements IDomComponent {
;
});
const allDiscussions = Computed.create(owner, use => {
const list = flatMap(flatMap(use(tables).map(t => {
const columns = use(use(t.columns).getObservable());
const list = flatMap(flatMap(use(tables).map(tb => {
const columns = use(use(tb.columns).getObservable());
const dList = columns.map(col => use(use(col.cells).getObservable())
.filter(c => use(c.root) && use(c.type) === CellInfoType.COMMENT));
return dList;
@ -818,9 +820,9 @@ export class DiscussionPanel extends Disposable implements IDomComponent {
testId('panel-menu'),
menu(() => {
return [cssDropdownMenu(
labeledSquareCheckbox(this._onlyMine, "Only my threads", testId('my-threads')),
labeledSquareCheckbox(this._currentPage, "Only current page", testId('only-page')),
labeledSquareCheckbox(this._resolved, "Show resolved comments", testId('show-resolved')),
labeledSquareCheckbox(this._onlyMine, t("Only my threads"), testId('my-threads')),
labeledSquareCheckbox(this._currentPage, t("Only current page"), testId('only-page')),
labeledSquareCheckbox(this._resolved, t("Show resolved comments"), testId('show-resolved')),
)];
}, {placement: 'bottom-start'}),
dom.on('click', stopPropagation)

@ -1,13 +1,16 @@
import {makeT} from 'app/client/lib/localization';
import {ITooltipControl, showTooltip, tooltipCloseButton} from 'app/client/ui/tooltips';
import {colors, testId} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons';
import {cssLink} from 'app/client/ui2018/links';
import {dom, styled} from 'grainjs';
const t = makeT('EditorTooltip');
export function showTooltipToCreateFormula(editorDom: HTMLElement, convert: () => void) {
function buildTooltip(ctl: ITooltipControl) {
return cssConvertTooltip(icon('Convert'),
cssLink('Convert column to formula',
cssLink(t('Convert column to formula'),
dom.on('mousedown', (ev) => { ev.preventDefault(); convert(); }),
testId('editor-tooltip-convert'),
),

@ -9,6 +9,7 @@ import { KoArray } from 'app/client/lib/koArray';
import * as kd from 'app/client/lib/koDom';
import * as kf from 'app/client/lib/koForm';
import * as koUtil from 'app/client/lib/koUtil';
import { makeT } from 'app/client/lib/localization';
import { reportError } from 'app/client/models/AppModel';
import { DataRowModel } from 'app/client/models/DataRowModel';
import { ColumnRec, DocModel, ViewFieldRec } from 'app/client/models/DocModel';
@ -38,7 +39,7 @@ import * as ko from 'knockout';
import * as _ from 'underscore';
const testId = makeTestId('test-fbuilder-');
const t = makeT('FieldBuilder');
// Creates a FieldBuilder object for each field in viewFields
@ -228,7 +229,7 @@ export class FieldBuilder extends Disposable {
defaultWidget.onWrite((value) => this.field.config.widget(value));
const disabled = Computed.create(null, use => !use(this.field.config.sameWidgets));
return [
cssLabel('CELL FORMAT'),
cssLabel(t('CELL FORMAT')),
cssRow(
grainjsDom.autoDispose(defaultWidget),
widgetOptions.length <= 2 ?
@ -242,7 +243,7 @@ export class FieldBuilder extends Disposable {
widgetOptions,
{
disabled,
defaultLabel: 'Mixed format'
defaultLabel: t('Mixed format')
}
),
testId('widget-select')
@ -286,7 +287,7 @@ export class FieldBuilder extends Disposable {
// If we are waiting for a server response
use(this.isCallPending),
menuCssClass: cssTypeSelectMenu.className,
defaultLabel: 'Mixed types',
defaultLabel: t('Mixed types'),
renderOptionArgs: (op) => {
if (['Ref', 'RefList'].includes(selectType.get())) {
// Don't show tip if a reference column type is already selected.
@ -340,7 +341,7 @@ export class FieldBuilder extends Disposable {
// If we selected multiple empty/formula columns, make the change for all of them.
if (this.field.viewSection.peek().selectedFields.peek().length > 1 &&
['formula', 'empty'].indexOf(this.field.viewSection.peek().columnsBehavior.peek())) {
return this.gristDoc.docData.bundleActions("Changing multiple column types", () =>
return this.gristDoc.docData.bundleActions(t("Changing multiple column types"), () =>
Promise.all(this.field.viewSection.peek().selectedFields.peek().map(f =>
f.column.peek().type.setAndSave(calculatedType)
))).catch(reportError);
@ -369,7 +370,7 @@ export class FieldBuilder extends Disposable {
return use(this.origColumn.disableModifyBase) || use(this.field.config.multiselect);
});
return [
cssLabel('DATA FROM TABLE',
cssLabel(t('DATA FROM TABLE'),
!this._showRefConfigPopup.peek() ? null : this.gristDoc.behavioralPromptsManager.attachTip(
'referenceColumnsConfig',
{
@ -417,7 +418,7 @@ export class FieldBuilder extends Disposable {
}
}),
kf.row(
15, kf.label('Apply Formula to Data'),
15, kf.label(t('Apply Formula to Data')),
3, kf.buttonGroup(
kf.checkButton(transformButton,
dom('span.glyphicon.glyphicon-flash'),
@ -498,7 +499,7 @@ export class FieldBuilder extends Disposable {
public fieldSettingsUseSeparate() {
return this.gristDoc.docData.bundleActions(
`Use separate field settings for ${this.origColumn.colId()}`, () => {
t("Use separate field settings for {{colId}}", { colId: this.origColumn.colId() }), () => {
return Promise.all([
setSaveValue(this.field.widgetOptions, this.field.column().widgetOptions()),
setSaveValue(this.field.visibleCol, this.field.column().visibleCol()),
@ -510,7 +511,7 @@ export class FieldBuilder extends Disposable {
public fieldSettingsSaveAsCommon() {
return this.gristDoc.docData.bundleActions(
`Save field settings for ${this.origColumn.colId()} as common`, () => {
t("Save field settings for {{colId}} as common", { colId: this.origColumn.colId() }), () => {
return Promise.all([
setSaveValue(this.field.column().widgetOptions, this.field.widgetOptions()),
setSaveValue(this.field.column().visibleCol, this.field.visibleCol()),
@ -525,7 +526,7 @@ export class FieldBuilder extends Disposable {
public fieldSettingsRevertToCommon() {
return this.gristDoc.docData.bundleActions(
`Revert field settings for ${this.origColumn.colId()} to common`, () => {
t("Revert field settings for {{colId}} to common", { colId: this.origColumn.colId() }), () => {
return Promise.all([
setSaveValue(this.field.widgetOptions, ''),
setSaveValue(this.field.visibleCol, 0),

@ -2,6 +2,7 @@ import * as commands from 'app/client/components/commands';
import {Cursor} from 'app/client/components/Cursor';
import {GristDoc} from 'app/client/components/GristDoc';
import {UnsavedChange} from 'app/client/components/UnsavedChanges';
import {makeT} from 'app/client/lib/localization';
import {DataRowModel} from 'app/client/models/DataRowModel';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
import {reportError} from 'app/client/models/errors';
@ -17,6 +18,8 @@ import {CellPosition} from "app/client/components/CellPosition";
type IEditorConstructor = typeof NewBaseEditor;
const t = makeT('FieldEditor');
/**
* Check if the typed-in value should change the cell without opening the cell editor, and if so,
* saves and returns true. E.g. on typing space, CheckBoxEditor toggles the cell without opening.
@ -320,7 +323,7 @@ export class FieldEditor extends Disposable {
await editor.prepForSave();
if (this.isDisposed()) {
// We shouldn't normally get disposed here, but if we do, avoid confusing JS errors.
console.warn("Unable to finish saving edited cell"); // tslint:disable-line:no-console
console.warn(t("Unable to finish saving edited cell")); // tslint:disable-line:no-console
return false;
}
@ -349,7 +352,7 @@ export class FieldEditor extends Disposable {
const value = editor.getCellValue();
if (col.isRealFormula()) {
// tslint:disable-next-line:no-console
console.warn("It should be impossible to save a plain data value into a formula column");
console.warn(t("It should be impossible to save a plain data value into a formula column"));
} else {
// This could still be an isFormula column if it's empty (isEmpty is true), but we don't
// need to toggle isFormula in that case, since the data engine takes care of that.

@ -1,5 +1,6 @@
import * as AceEditor from 'app/client/components/AceEditor';
import {createGroup} from 'app/client/components/commands';
import {makeT} from 'app/client/lib/localization';
import {DataRowModel} from 'app/client/models/DataRowModel';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
import {colors, testId, theme} from 'app/client/ui2018/cssVars';
@ -20,6 +21,7 @@ import debounce = require('lodash/debounce');
// How wide to expand the FormulaEditor when an error is shown in it.
const minFormulaErrorWidth = 400;
const t = makeT('FormulaEditor');
export interface IFormulaEditorOptions extends Options {
cssClass?: string;
@ -293,7 +295,7 @@ export function openFormulaEditor(options: {
const column = options.column ?? options.field?.column();
if (!column) {
throw new Error('Column or field is required');
throw new Error(t('Column or field is required'));
}
// AsyncOnce ensures it's called once even if triggered multiple times.
@ -338,7 +340,7 @@ export function openFormulaEditor(options: {
const editingFormula = options.editingFormula ?? options?.field?.editingFormula;
if (!editingFormula) {
throw new Error('editingFormula is required');
throw new Error(t('editingFormula is required'));
}
// When formula is empty enter formula-editing mode (highlight formula icons; click on a column inserts its ID).
@ -393,9 +395,9 @@ export function createFormulaErrorObs(owner: MultiHolder, gristDoc: GristDoc, or
const numErrors = tableData.countErrors(colId) || 0;
errorMessage.set(
(numErrors === 0) ? '' :
(numCells === 1) ? `Error in the cell` :
(numErrors === numCells) ? `Errors in all ${numErrors} cells` :
`Errors in ${numErrors} of ${numCells} cells`
(numCells === 1) ? t(`Error in the cell`) :
(numErrors === numCells) ? t(`Errors in all {{numErrors}} cells`, {numErrors}) :
t(`Errors in {{numErrors}} of {{numCells}} cells`, {numErrors, numCells})
);
} else {
errorMessage.set('');

@ -1,6 +1,9 @@
import {makeT} from 'app/client/lib/localization';
import {FieldOptions} from 'app/client/widgets/NewBaseEditor';
import {NTextEditor} from 'app/client/widgets/NTextEditor';
const t = makeT('HyperLinkEditor');
/**
* HyperLinkEditor - Is the same NTextEditor but with some placeholder text to help explain
* to the user how links should be formatted.
@ -8,6 +11,6 @@ import {NTextEditor} from 'app/client/widgets/NTextEditor';
export class HyperLinkEditor extends NTextEditor {
constructor(options: FieldOptions) {
super(options);
this.textInput.setAttribute('placeholder', '[link label] url');
this.textInput.setAttribute('placeholder', t('[link label] url'));
}
}

@ -1,6 +1,7 @@
/**
* See app/common/NumberFormat for description of options we support.
*/
import {makeT} from 'app/client/lib/localization';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
import {reportError} from 'app/client/models/errors';
import {cssLabel, cssRow} from 'app/client/ui/RightPanelStyles';
@ -16,6 +17,7 @@ import {BindableValue, Computed, dom, DomContents, DomElementArg,
import * as LocaleCurrency from 'locale-currency';
const t = makeT('NumericTextBox');
const modeOptions: Array<ISelectorOption<NumMode>> = [
{value: 'currency', label: '$'},
{value: 'decimal', label: ','},
@ -85,23 +87,23 @@ export class NumericTextBox extends NTextBox {
return [
super.buildConfigDom(),
cssLabel('Number Format'),
cssLabel(t('Number Format')),
cssRow(
dom.autoDispose(holder),
makeButtonSelect(numMode, modeOptions, setMode, disabledStyle, cssModeSelect.cls(''), testId('numeric-mode')),
makeButtonSelect(numSign, signOptions, setSign, disabledStyle, cssSignSelect.cls(''), testId('numeric-sign')),
),
dom.maybe((use) => use(numMode) === 'currency', () => [
cssLabel('Currency'),
cssLabel(t('Currency')),
cssRow(
dom.domComputed(docCurrency, (defaultCurrency) =>
buildCurrencyPicker(holder, currency, setCurrency,
{defaultCurrencyLabel: `Default currency (${defaultCurrency})`, disabled})
{defaultCurrencyLabel: t(`Default currency ({{defaultCurrency}})`, {defaultCurrency}), disabled})
),
testId("numeric-currency")
)
]),
cssLabel('Decimals'),
cssLabel(t('Decimals')),
cssRow(
decimals('min', minDecimals, defaultMin, setMinDecimals, disabled, testId('numeric-min-decimals')),
decimals('max', maxDecimals, defaultMax, setMaxDecimals, disabled, testId('numeric-max-decimals')),

@ -1,3 +1,4 @@
import {makeT} from 'app/client/lib/localization';
import {DataRowModel} from 'app/client/models/DataRowModel';
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
import {cssLabel, cssRow} from 'app/client/ui/RightPanelStyles';
@ -8,6 +9,9 @@ import {NTextBox} from 'app/client/widgets/NTextBox';
import {isFullReferencingType, isVersions} from 'app/common/gristTypes';
import {Computed, dom, styled} from 'grainjs';
const t = makeT('Reference');
/**
* Reference - The widget for displaying references to another table's records.
*/
@ -33,14 +37,14 @@ export class Reference extends NTextBox {
icon: 'FieldColumn',
disabled: isFullReferencingType(use(col.type)) || use(col.isTransforming)
}))
.concat([{label: 'Row ID', value: 0, icon: 'FieldColumn'}]);
.concat([{label: t('Row ID'), value: 0, icon: 'FieldColumn'}]);
});
}
public buildConfigDom() {
return [
this.buildTransformConfigDom(),
cssLabel('CELL FORMAT'),
cssLabel(t('CELL FORMAT')),
super.buildConfigDom()
];
}
@ -48,7 +52,7 @@ export class Reference extends NTextBox {
public buildTransformConfigDom() {
const disabled = Computed.create(null, use => use(this.field.config.multiselect));
return [
cssLabel('SHOW COLUMN'),
cssLabel(t('SHOW COLUMN')),
cssRow(
dom.autoDispose(disabled),
select(this._visibleColRef, this._validCols, {

@ -36,6 +36,7 @@ export function mergeOptions(options: any, type: string) {
// The names of widgets are used, instead of the actual classes needed, in order to limit
// the spread of dependencies. See ./UserTypeImpl for actual classes.
export const typeDefs: any = {
// TODO : translate labels (can not use classic makeT function)
Any: {
label: 'Any',
icon: 'FieldAny',

@ -666,7 +666,7 @@
"Current field ": "Aktuelles Feld ",
"OK": "OK"
},
"TypeTransformation": {
"TypeTransform": {
"Apply": "Anwenden",
"Cancel": "Abbrechen",
"Preview": "Vorschau",

@ -38,7 +38,8 @@
"User Attributes": "User Attributes",
"View As": "View As",
"Seed rules": "Seed rules",
"When adding table rules, automatically add a rule to grant OWNER full access.": "When adding table rules, automatically add a rule to grant OWNER full access."
"When adding table rules, automatically add a rule to grant OWNER full access.": "When adding table rules, automatically add a rule to grant OWNER full access.",
"Permission to edit document structure": "Permission to edit document structure"
},
"AccountPage": {
"API": "API",
@ -603,7 +604,8 @@
"Return to viewing as yourself": "Return to viewing as yourself",
"TOOLS": "TOOLS",
"Tour of this Document": "Tour of this Document",
"Validate Data": "Validate Data"
"Validate Data": "Validate Data",
"Settings": "Settings"
},
"TopBar": {
"Manage Team": "Manage Team"
@ -779,5 +781,119 @@
"Preview": "Preview",
"Revise": "Revise",
"Update formula (Shift+Enter)": "Update formula (Shift+Enter)"
},
"CellStyle": {
"CELL STYLE": "CELL STYLE",
"Cell Style": "Cell Style",
"Default cell style": "Default cell style",
"Mixed style": "Mixed style",
"Open row styles": "Open row styles"
},
"ChoiceTextBox": {
"CHOICES": "CHOICES"
},
"ColumnEditor": {
"COLUMN DESCRIPTION": "COLUMN DESCRIPTION",
"COLUMN LABEL": "COLUMN LABEL"
},
"ColumnInfo": {
"COLUMN DESCRIPTION": "COLUMN DESCRIPTION",
"COLUMN ID: ": "COLUMN ID: ",
"COLUMN LABEL": "COLUMN LABEL",
"Cancel": "Cancel",
"Save": "Save"
},
"ConditionalStyle": {
"Add another rule": "Add another rule",
"Add conditional style": "Add conditional style",
"Error in style rule": "Error in style rule",
"Row Style": "Row Style",
"Rule must return True or False": "Rule must return True or False"
},
"CurrencyPicker": {
"Invalid currency": "Invalid currency"
},
"DiscussionEditor": {
"Cancel": "Cancel",
"Comment": "Comment",
"Edit": "Edit",
"Marked as resolved": "Marked as resolved",
"Only current page": "Only current page",
"Only my threads": "Only my threads",
"Open": "Open",
"Remove": "Remove",
"Reply": "Reply",
"Reply to a comment": "Reply to a comment",
"Resolve": "Resolve",
"Save": "Save",
"Show resolved comments": "Show resolved comments",
"Showing last {{nb}} comments": "Showing last {{nb}} comments",
"Started discussion": "Started discussion",
"Write a comment": "Write a comment"
},
"EditorTooltip": {
"Convert column to formula": "Convert column to formula"
},
"FieldBuilder": {
"Apply Formula to Data": "Apply Formula to Data",
"CELL FORMAT": "CELL FORMAT",
"Changing multiple column types": "Changing multiple column types",
"DATA FROM TABLE": "DATA FROM TABLE",
"Mixed format": "Mixed format",
"Mixed types": "Mixed types",
"Revert field settings for {{colId}} to common": "Revert field settings for {{colId}} to common",
"Save field settings for {{colId}} as common": "Save field settings for {{colId}} as common",
"Use separate field settings for {{colId}}": "Use separate field settings for {{colId}}"
},
"FieldEditor": {
"It should be impossible to save a plain data value into a formula column": "It should be impossible to save a plain data value into a formula column",
"Unable to finish saving edited cell": "Unable to finish saving edited cell"
},
"FormulaEditor": {
"Column or field is required": "Column or field is required",
"Error in the cell": "Error in the cell",
"Errors in all {{numErrors}} cells": "Errors in all {{numErrors}} cells",
"Errors in {{numErrors}} of {{numCells}} cells": "Errors in {{numErrors}} of {{numCells}} cells",
"editingFormula is required": "editingFormula is required"
},
"HyperLinkEditor": {
"[link label] url": "[link label] url"
},
"NumericTextBox": {
"Currency": "Currency",
"Decimals": "Decimals",
"Default currency ({{defaultCurrency}})": "Default currency ({{defaultCurrency}})",
"Number Format": "Number Format"
},
"Reference": {
"CELL FORMAT": "CELL FORMAT",
"Row ID": "Row ID",
"SHOW COLUMN": "SHOW COLUMN"
},
"welcomeTour": {
"Add New": "Add New",
"Browse our {{templateLibrary}} to discover what's possible and get inspired.": "Browse our {{templateLibrary}} to discover what's possible and get inspired.",
"Building up": "Building up",
"Configuring your document": "Configuring your document",
"Customizing columns": "Customizing columns",
"Double-click or hit {{enter}} on a cell to edit it. ": "Double-click or hit {{enter}} on a cell to edit it. ",
"Editing Data": "Editing Data",
"Enter": "Enter",
"Flying higher": "Flying higher",
"Help Center": "Help Center",
"Make it relational! Use the {{ref}} type to link tables. ": "Make it relational! Use the {{ref}} type to link tables. ",
"Reference": "Reference",
"Set formatting options, formulas, or column types, such as dates, choices, or attachments. ": "Set formatting options, formulas, or column types, such as dates, choices, or attachments. ",
"Share": "Share",
"Sharing": "Sharing",
"Start with {{equal}} to enter a formula.": "Start with {{equal}} to enter a formula.",
"Toggle the {{creatorPanel}} to format columns, ": "Toggle the {{creatorPanel}} to format columns, ",
"Use the Share button ({{share}}) to share the document or export data.": "Use the Share button ({{share}}) to share the document or export data.",
"Use {{addNew}} to add widgets, pages, or import more data. ": "Use {{addNew}} to add widgets, pages, or import more data. ",
"Use {{helpCenter}} for documentation or questions.": "Use {{helpCenter}} for documentation or questions.",
"Welcome to Grist!": "Welcome to Grist!",
"convert to card view, select data, and more.": "convert to card view, select data, and more.",
"creator panel": "creator panel",
"template library": "template library"
}
}

@ -768,7 +768,7 @@
"SelectionSummary": {
"Copied to clipboard": "Copiado al portapapeles"
},
"TypeTransformation": {
"TypeTransform": {
"Apply": "Aplicar",
"Cancel": "Cancelar",
"Preview": "Vista previa",

@ -610,7 +610,7 @@
"Cancel": "Annuler",
"Close": "Fermer"
},
"TypeTransformation": {
"TypeTransform": {
"Apply": "Appliquer",
"Cancel": "Annuler",
"Preview": "Aperçu",

@ -666,7 +666,7 @@
"Current field ": "Campo atual ",
"OK": "OK"
},
"TypeTransformation": {
"TypeTransform": {
"Apply": "Aplicar",
"Cancel": "Cancelar",
"Preview": "Pré-visualização",

Loading…
Cancel
Save