diff --git a/app/client/components/ActionLog.ts b/app/client/components/ActionLog.ts index b6194003..4ea80f3e 100644 --- a/app/client/components/ActionLog.ts +++ b/app/client/components/ActionLog.ts @@ -227,7 +227,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent { } private _buildLogDom() { - this._loadActionSummaries().catch((error) => gristNotify(t("ActionLogFailed"))); + this._loadActionSummaries().catch((error) => gristNotify(t("Action Log failed to load"))); return dom('div.action_log', dom('div.preference_item', koForm.checkbox(this._showAllTables, @@ -395,7 +395,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent { const newName = tableRename[1]; if (!newName) { // TODO - find a better way to send informative notifications. - gristNotify(t('TableRemovedInAction', {tableId:tableId, actionNum: action.actionNum})); + gristNotify(t("Table {{tableId}} was subsequently removed in action #{{actionNum}}", {tableId:tableId, actionNum: action.actionNum})); return; } tableId = newName; @@ -406,7 +406,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent { // Check is this row was removed - if so there's no reason to go on. if (td.removeRows.indexOf(rowId) >= 0) { // TODO - find a better way to send informative notifications. - gristNotify(t("RowRemovedInAction", {actionNum})); + gristNotify(t("This row was subsequently removed in action {{action.actionNum}}", {actionNum})); return; } @@ -416,7 +416,7 @@ export class ActionLog extends dispose.Disposable implements IDomComponent { const newName = columnRename[1]; if (!newName) { // TODO - find a better way to send informative notifications. - gristNotify(t("ColumnRemovedInAction", {colId, actionNum: action.actionNum})); + gristNotify(t("Column {{colId}} was subsequently removed in action #{{action.actionNum}}", {colId, actionNum: action.actionNum})); return; } colId = newName; diff --git a/app/client/components/ChartView.ts b/app/client/components/ChartView.ts index aa42ed84..1d267ce9 100644 --- a/app/client/components/ChartView.ts +++ b/app/client/components/ChartView.ts @@ -655,8 +655,8 @@ export class ChartConfig extends GrainJSDisposable { testId('error-bars'), ), dom.domComputed(this._optionsObj.prop('errorBars'), (value: ChartOptions["errorBars"]) => - value === 'symmetric' ? cssRowHelp(t('EachYFollowedByOne')) : - value === 'separate' ? cssRowHelp(t('EachYFollowedByTwo')) : + value === 'symmetric' ? cssRowHelp(t("Each Y series is followed by a series for the length of error bars.")) : + value === 'separate' ? cssRowHelp(t("Each Y series is followed by two series, for top and bottom error bars.")) : null ), ]), @@ -669,7 +669,7 @@ export class ChartConfig extends GrainJSDisposable { select(this._groupDataColId, this._groupDataOptions), testId('group-by-column'), ), - cssHintRow(t('CreateSeparateSeries')), + cssHintRow(t("Create separate series for each value of the selected column.")), ]), // TODO: user should select x axis before widget reach page @@ -677,7 +677,7 @@ export class ChartConfig extends GrainJSDisposable { cssRow( select( this._xAxis, this._columnsOptions, - { defaultLabel: t('PickColumn') } + { defaultLabel: t("Pick a column") } ), testId('x-axis'), ), @@ -773,7 +773,7 @@ export class ChartConfig extends GrainJSDisposable { private async _setGroupDataColumn(colId: string) { const viewFields = this._section.viewFields.peek().peek(); - await this._gristDoc.docData.bundleActions(t('SelectedNewGroupDataColumns'), async () => { + await this._gristDoc.docData.bundleActions(t("selected new group data columns"), async () => { this._freezeXAxis.set(true); this._freezeYAxis.set(true); try { @@ -872,7 +872,7 @@ export class ChartConfig extends GrainJSDisposable { private async _setAggregation(val: boolean) { try { this._freezeXAxis.set(true); - await this._gristDoc.docData.bundleActions(t("ToggleChartAggregation"), async () => { + await this._gristDoc.docData.bundleActions(t("Toggle chart aggregation"), async () => { if (val) { await this._doAggregation(); } else { diff --git a/app/client/components/CodeEditorPanel.ts b/app/client/components/CodeEditorPanel.ts index a1d3af58..99363842 100644 --- a/app/client/components/CodeEditorPanel.ts +++ b/app/client/components/CodeEditorPanel.ts @@ -28,8 +28,8 @@ export class CodeEditorPanel extends DisposableWithEvents { return dom('div.g-code-panel.clipboard', {tabIndex: "-1"}, dom.maybe(this._denied, () => dom('div.g-code-panel-denied', - dom('h2', dom.text(t('AccessDenied'))), - dom('div', dom.text(t('CodeViewOnlyFullAccess'))), + dom('h2', dom.text(t("Access denied"))), + dom('div', dom.text(t("Code View is available only when you have full document access."))), )), dom.maybe(this._schema, (schema) => { // The reason to scope and rebuild instead of using `kd.text(schema)` is because diff --git a/app/client/components/DataTables.ts b/app/client/components/DataTables.ts index 3ea5f37d..06bcd75a 100644 --- a/app/client/components/DataTables.ts +++ b/app/client/components/DataTables.ts @@ -45,7 +45,7 @@ export class DataTables extends Disposable { cssTableList( /*************** List section **********/ testId('list'), - cssHeader(t('RawDataTables')), + cssHeader(t("Raw Data Tables")), cssList( dom.forEach(this._tables, tableRec => cssItem( @@ -65,11 +65,11 @@ export class DataTables extends Disposable { testId('table-id'), dom.text(tableRec.tableId), ), - { title : t('ClickToCopy') }, + { title : t("Click to copy") }, dom.on('click', async (e, d) => { e.stopImmediatePropagation(); e.preventDefault(); - showTransientTooltip(d, t('TableIDCopied'), { + showTransientTooltip(d, t("Table ID copied to clipboard"), { key: 'copy-table-id' }); await copyToClipboard(tableRec.tableId.peek()); @@ -127,7 +127,7 @@ export class DataTables extends Disposable { return [ menuItem( () => this._duplicateTable(table), - t('DuplicateTable'), + t("Duplicate Table"), testId('menu-duplicate-table'), dom.cls('disabled', use => use(isReadonly) || @@ -144,7 +144,7 @@ export class DataTables extends Disposable { use(docModel.visibleTables.getObservable()).length <= 1 && !use(table.isHidden) )) ), - dom.maybe(isReadonly, () => menuText(t("NoEditAccess"))), + dom.maybe(isReadonly, () => menuText(t("You do not have edit access to this document"))), ]; } @@ -160,7 +160,7 @@ export class DataTables extends Disposable { function doRemove() { return docModel.docData.sendAction(['RemoveTable', r.tableId()]); } - confirmModal(t("DeleteData", {formattedTableName : r.formattedTableName()}), 'Delete', doRemove); + confirmModal(t("Delete {{formattedTableName}} data, and remove it from all pages?", {formattedTableName : r.formattedTableName()}), 'Delete', doRemove); } private _tableRows(table: TableRec) { diff --git a/app/client/components/DocumentUsage.ts b/app/client/components/DocumentUsage.ts index 83bcbc23..914ef0ba 100644 --- a/app/client/components/DocumentUsage.ts +++ b/app/client/components/DocumentUsage.ts @@ -60,7 +60,7 @@ export class DocumentUsage extends Disposable { // Invalid row limits are currently treated as if they are undefined. const maxValue = maxRows && maxRows > 0 ? maxRows : undefined; return { - name: t('Rows'), + name: t("Rows"), currentValue: typeof rowCount !== 'object' ? undefined : rowCount.total, maximumValue: maxValue ?? DEFAULT_MAX_ROWS, unit: 'rows', @@ -75,7 +75,7 @@ export class DocumentUsage extends Disposable { // Invalid data size limits are currently treated as if they are undefined. const maxValue = maxSize && maxSize > 0 ? maxSize : undefined; return { - name: t('DataSize'), + name: t("Data Size"), currentValue: typeof dataSize !== 'number' ? undefined : dataSize, maximumValue: maxValue ?? DEFAULT_MAX_DATA_SIZE, unit: 'MB', @@ -97,7 +97,7 @@ export class DocumentUsage extends Disposable { // Invalid attachments size limits are currently treated as if they are undefined. const maxValue = maxSize && maxSize > 0 ? maxSize : undefined; return { - name: t('AttachmentsSize'), + name: t("Attachments Size"), currentValue: typeof attachmentsSize !== 'number' ? undefined : attachmentsSize, maximumValue: maxValue ?? DEFAULT_MAX_ATTACHMENTS_SIZE, unit: 'GB', @@ -135,7 +135,7 @@ export class DocumentUsage extends Disposable { public buildDom() { return dom('div', - cssHeader(t('Usage'), testId('heading')), + cssHeader(t("Usage"), testId('heading')), dom.domComputed(this._areAllMetricsPending, (isLoading) => { if (isLoading) { return cssSpinner(loadingSpinner(), testId('loading')); } @@ -149,7 +149,7 @@ export class DocumentUsage extends Disposable { return dom.domComputed((use) => { const isAccessDenied = use(this._isAccessDenied); if (isAccessDenied === null) { return null; } - if (isAccessDenied) { return buildMessage(t('UsageStatisticsOnlyFullAccess')); } + if (isAccessDenied) { return buildMessage(t("Usage statistics are only available to users with full access to the document data.")); } const org = use(this._currentOrg); const product = use(this._currentProduct); @@ -237,12 +237,12 @@ export function buildUpgradeMessage( variant: 'short' | 'long', onUpgrade: () => void, ) { - if (!canUpgrade) { return t('LimitContactSiteOwner'); } + if (!canUpgrade) { return t("Contact the site owner to upgrade the plan to raise limits."); } - const upgradeLinkText = t('UpgradeLinkText') + const upgradeLinkText = t("start your 30-day free trial of the Pro plan.") // TODO i18next return [ - variant === 'short' ? null : t('ForHigherLimits'), + variant === 'short' ? null : t("For higher limits, "), buildUpgradeLink( variant === 'short' ? capitalizeFirstWord(upgradeLinkText) : upgradeLinkText, () => onUpgrade(), diff --git a/app/client/components/Drafts.ts b/app/client/components/Drafts.ts index da21b7a6..64316a68 100644 --- a/app/client/components/Drafts.ts +++ b/app/client/components/Drafts.ts @@ -273,7 +273,7 @@ class NotificationAdapter extends Disposable implements Notification { } public showUndoDiscard() { const notifier = this._doc.app.topAppModel.notifier; - const notification = notifier.createUserMessage(t("UndoDiscard"), { + const notification = notifier.createUserMessage(t("Undo discard"), { message: () => discardNotification( dom.on("click", () => { @@ -421,7 +421,7 @@ const styledTooltip = styled('div', ` function cellTooltip(clb: () => any) { return function (ctl: ITooltipControl) { return styledTooltip( - cssLink(t('RestoreLastEdit'), + cssLink(t("Restore last edit"), dom.on('mousedown', (ev) => { ev.preventDefault(); ctl.close(); clb(); }), testId('draft-tooltip'), ), @@ -440,7 +440,7 @@ const styledNotification = styled('div', ` `); function discardNotification(...args: IDomArgs>) { return styledNotification( - t("UndoDiscard"), + t("Undo discard"), testId("draft-notification"), ...args ); diff --git a/app/client/components/GristDoc.ts b/app/client/components/GristDoc.ts index 059a1c25..c5c8f46e 100644 --- a/app/client/components/GristDoc.ts +++ b/app/client/components/GristDoc.ts @@ -314,7 +314,7 @@ export class GristDoc extends DisposableWithEvents { const importSourceElems = ImportSourceElement.fromArray(this.docPluginManager.pluginsList); const importMenuItems = [ { - label: t('ImportFromFile'), + label: t("Import from file"), action: () => Importer.selectAndImport(this, importSourceElems, null, createPreview), }, ...importSourceElems.map(importSourceElem => ({ @@ -599,7 +599,7 @@ export class GristDoc extends DisposableWithEvents { } } const res = await docData.bundleActions( - t("AddedNewLinkedSection", {viewName}), + t("Added new linked section to view {{viewName}}", {viewName}), () => this.addWidgetToPageImpl(val, tableId ?? null) ); @@ -680,7 +680,7 @@ export class GristDoc extends DisposableWithEvents { } return await this._viewLayout!.freezeUntil(docData.bundleActions( - t("SavedLinkedSectionIn", {title:section.title(), name: viewModel.name()}), + t("Saved linked section {{title}} in view {{name}}", {title:section.title(), name: viewModel.name()}), async () => { // if table changes or a table is made a summary table, let's replace the view section by a diff --git a/app/client/components/Importer.ts b/app/client/components/Importer.ts index 248ea324..c9fdf8e9 100644 --- a/app/client/components/Importer.ts +++ b/app/client/components/Importer.ts @@ -631,7 +631,7 @@ export class Importer extends DisposableWithEvents { cssMergeOptions( cssMergeOptionsToggle(labeledSquareCheckbox( updateExistingRecords, - t('UpdateExistingRecords'), + t("Update existing records"), dom.autoDispose(updateRecordsListener), testId('importer-update-existing-records') )), @@ -646,14 +646,14 @@ export class Importer extends DisposableWithEvents { return [ cssMergeOptionsMessage( - t('MergeRowsThatMatch'), + t("Merge rows that match these fields:"), testId('importer-merge-fields-message') ), multiSelect( mergeCols, section.viewFields().peek().map(f => ({label: f.label(), value: f.colId()})) ?? [], { - placeholder: t("SelectFieldsToMatch"), + placeholder: t("Select fields to match on"), error: hasInvalidMergeCols }, dom.autoDispose(mergeColsListener), diff --git a/app/client/components/PluginScreen.ts b/app/client/components/PluginScreen.ts index 923c19b0..3eb9cb88 100644 --- a/app/client/components/PluginScreen.ts +++ b/app/client/components/PluginScreen.ts @@ -55,7 +55,7 @@ export class PluginScreen extends Disposable { public renderError(message: string) { this.render([ this._buildModalTitle(), - cssModalBody(t('ImportFailed'), message, testId('importer-error')), + cssModalBody(t("Import failed: "), message, testId('importer-error')), cssModalButtons( bigBasicButton('Close', dom.on('click', () => this.close()), diff --git a/app/client/components/RecordLayout.js b/app/client/components/RecordLayout.js index 1193ad28..dfa48e3a 100644 --- a/app/client/components/RecordLayout.js +++ b/app/client/components/RecordLayout.js @@ -263,7 +263,7 @@ RecordLayout.prototype.saveLayoutSpec = async function(layoutSpec) { // Use separate copies of addColAction, since sendTableActions modified each in-place. let addActions = gutil.arrayRepeat(addColNum, 0).map(() => addColAction.slice()); - await docData.bundleActions(t('UpdatingRecordLayout'), () => { + await docData.bundleActions(t("Updating record layout."), () => { return Promise.try(() => { return addColNum > 0 ? docModel.dataTables[tableId].sendTableActions(addActions) : []; }) diff --git a/app/client/components/RefSelect.ts b/app/client/components/RefSelect.ts index e3c7d15d..e45377aa 100644 --- a/app/client/components/RefSelect.ts +++ b/app/client/components/RefSelect.ts @@ -97,7 +97,7 @@ export class RefSelect extends Disposable { testId('ref-select-item'), ) ), - cssAddLink(cssAddIcon('Plus'), t('AddColumn'), + cssAddLink(cssAddIcon('Plus'), t("Add Column"), menu(() => [ ...this._validCols.peek() .filter((col) => !this._addedSet.peek().has(col.colId.peek())) @@ -105,7 +105,7 @@ export class RefSelect extends Disposable { menuItem(() => this._addFormulaField({ label: col.label(), value: col.colId() }), col.label.peek()) ), - cssEmptyMenuText(t("NoColumnsAdd")), + cssEmptyMenuText(t("No columns to add")), testId('ref-select-menu'), ]), testId('ref-select-add'), diff --git a/app/client/components/SelectionSummary.ts b/app/client/components/SelectionSummary.ts index 559c56d8..1e3d2bbe 100644 --- a/app/client/components/SelectionSummary.ts +++ b/app/client/components/SelectionSummary.ts @@ -266,7 +266,7 @@ export class SelectionSummary extends Disposable { async function doCopy(value: string, elem: Element) { await copyToClipboard(value); - showTransientTooltip(elem, t('CopiedClipboard'), {key: 'copy-selection-summary'}); + showTransientTooltip(elem, t("Copied to clipboard"), {key: 'copy-selection-summary'}); } const cssSummary = styled('div', ` diff --git a/app/client/components/ValidationPanel.js b/app/client/components/ValidationPanel.js index 530b9a2c..e9f155a7 100644 --- a/app/client/components/ValidationPanel.js +++ b/app/client/components/ValidationPanel.js @@ -33,7 +33,7 @@ dispose.makeDisposable(ValidationPanel); ValidationPanel.prototype.onAddRule = function() { this.validationsTable.sendTableAction(["AddRecord", null, { tableRef: this.docTables.at(0).id(), - name: t("RuleLength", {length: this.validations.peekLength + 1}), + name: t("Rule {{length}}", {length: this.validations.peekLength + 1}), formula: "" }]) .then(function() { @@ -86,7 +86,7 @@ ValidationPanel.prototype.buildDom = function() { 2, '', 1, kf.buttonGroup( kf.button(() => editor.writeObservable(), - 'Apply', { title: t('UpdateFormula')}, + 'Apply', { title: t("Update formula (Shift+Enter)")}, kd.toggleClass('disabled', editorUpToDate) ) ) diff --git a/app/client/components/ViewConfigTab.js b/app/client/components/ViewConfigTab.js index 68b94b2b..6510f11d 100644 --- a/app/client/components/ViewConfigTab.js +++ b/app/client/components/ViewConfigTab.js @@ -124,9 +124,9 @@ ViewConfigTab.prototype._buildAdvancedSettingsDom = function() { const table = sectionData.section.table(); const isCollapsed = ko.observable(true); return [ - kf.collapserLabel(isCollapsed, t('AdvancedSettings'), dom.testId('ViewConfig_advanced')), + kf.collapserLabel(isCollapsed, t("Advanced settings"), dom.testId('ViewConfig_advanced')), kf.helpRow(kd.hide(isCollapsed), - t('BigTablesMayBeMarked'), + t("Big tables may be marked as \"on-demand\" to avoid loading them into the data engine."), kd.style('text-align', 'left'), kd.style('margin-top', '1.5rem') ), @@ -135,7 +135,7 @@ ViewConfigTab.prototype._buildAdvancedSettingsDom = function() { ), kf.row(kd.hide(isCollapsed), kf.buttonGroup(kf.button(() => this._makeOnDemand(table), - kd.text(() => table.onDemand() ? t('UnmarkOnDemandButton') : t('MakeOnDemandButton')), + kd.text(() => table.onDemand() ? t("Unmark On-Demand") : t("Make On-Demand")), dom.testId('ViewConfig_onDemandBtn') )) ), @@ -152,9 +152,9 @@ ViewConfigTab.prototype._buildThemeDom = function() { return cssRow( dom.autoDispose(theme), select(theme, [ - {label: t('Form'), value: 'form' }, - {label: t('Compact'), value: 'compact'}, - {label: t('Blocks'), value: 'blocks' }, + {label: t("Form"), value: 'form' }, + {label: t("Compact"), value: 'compact'}, + {label: t("Blocks"), value: 'blocks' }, ]), testId('detail-theme') ); @@ -173,7 +173,7 @@ ViewConfigTab.prototype._buildLayoutDom = function() { const layoutEditorObs = ko.computed(() => view && view.recordLayout && view.recordLayout.layoutEditor()); return cssRow({style: 'margin-top: 16px;'}, kd.maybe(layoutEditorObs, (editor) => editor.buildFinishButtons()), - primaryButton(t('EditCardLayout'), + primaryButton(t("Edit Card Layout"), dom.autoDispose(layoutEditorObs), dom.on('click', () => commands.allCommands.editLayout.run()), grainjsDom.hide(layoutEditorObs), @@ -222,8 +222,8 @@ ViewConfigTab.prototype._buildCustomTypeItems = function() { // 3) showObs: () => activeSection().customDef.mode() === "plugin", buildDom: () => kd.scope(activeSection, ({customDef}) => dom('div', - kf.row(5, t("PluginColon"), 13, kf.text(customDef.pluginId, {}, {list: "list_plugin"}, dom.testId('ViewConfigTab_customView_pluginId'))), - kf.row(5, t("SectionColon"), 13, kf.text(customDef.sectionId, {}, {list: "list_section"}, dom.testId('ViewConfigTab_customView_sectionId'))), + kf.row(5, t("Plugin: "), 13, kf.text(customDef.pluginId, {}, {list: "list_plugin"}, dom.testId('ViewConfigTab_customView_pluginId'))), + kf.row(5, t("Section: "), 13, kf.text(customDef.sectionId, {}, {list: "list_section"}, dom.testId('ViewConfigTab_customView_sectionId'))), // For both `customPlugin` and `selectedSection` it is possible for the value not to be in the // list of options. Combining and allows both to freely edit the value with // keyboard and to select it from a list. Although the content of the list seems to be diff --git a/app/client/components/duplicatePage.ts b/app/client/components/duplicatePage.ts index 3db955c6..a4e7bca9 100644 --- a/app/client/components/duplicatePage.ts +++ b/app/client/components/duplicatePage.ts @@ -30,7 +30,7 @@ export async function duplicatePage(gristDoc: GristDoc, pageId: number) { cssLabel("Name"), inputEl = cssInput({value: pageName + ' (copy)'}), ), - t("DoesNotCopyData"), + t("Note that this does not copy data, but creates another view of the same data."), ]) )); } @@ -41,7 +41,7 @@ async function makeDuplicate(gristDoc: GristDoc, pageId: number, pageName: strin const viewSections = sourceView.viewSections.peek().peek(); let viewRef = 0; await gristDoc.docData.bundleActions( - t("DuplicatePageName", {pageName}), + t("Duplicate page {{pageName}}", {pageName}), async () => { // create new view and new sections const results = await createNewViewSections(gristDoc.docData, viewSections);