(core) updates from grist-core

pull/465/head
Paul Fitzpatrick 1 year ago
commit 572995f19a

@ -218,6 +218,20 @@ export class App extends DisposableWithEvents {
this._mostRecentDocPageModel = pageModel; this._mostRecentDocPageModel = pageModel;
} }
/**
* This method is not called anywhere, it is here just to introduce
* a special translation key. The purpose of this key is to let translators
* control whether a translation is ready to be offered to the user.
*
* If the key has not been translated for a language, and the language
* is not the default language, then the language should not be offered
* or used (unless some flag is set). TODO: implement this once key
* is available in weblate and good translations have been updated.
*/
public checkSpecialTranslationKey() {
return t('Translators: please translate this only when your language is ready to be offered to users');
}
// Get the user profile for testing purposes // Get the user profile for testing purposes
public async testGetProfile(): Promise<any> { public async testGetProfile(): Promise<any> {
const resp = await fetchFromHome('/api/profile/user', {credentials: 'include'}); const resp = await fetchFromHome('/api/profile/user', {credentials: 'include'});

@ -79,7 +79,7 @@ export function makeViewLayoutMenu(viewSection: ViewSectionRec, isReadonly: bool
menuItemLink({ href: gristDoc.getCsvLink(), target: '_blank', download: ''}, menuItemLink({ href: gristDoc.getCsvLink(), target: '_blank', download: ''},
t("Download as CSV"), testId('download-section')), t("Download as CSV"), testId('download-section')),
menuItemLink({ href: gristDoc.getXlsxActiveViewLink(), target: '_blank', download: ''}, menuItemLink({ href: gristDoc.getXlsxActiveViewLink(), target: '_blank', download: ''},
t("Download as XLSX"), testId('download-section')), t("Download as XLSX"), testId('download-section')),
dom.maybe((use) => ['detail', 'single'].includes(use(viewSection.parentKey)), () => dom.maybe((use) => ['detail', 'single'].includes(use(viewSection.parentKey)), () =>
menuItemCmd(allCommands.editLayout, t("Edit Card Layout"), menuItemCmd(allCommands.editLayout, t("Edit Card Layout"),
dom.cls('disabled', isReadonly))), dom.cls('disabled', isReadonly))),

@ -370,7 +370,7 @@ export interface UserAPI {
} }
/** /**
* Parameters for the download CSV and XLSX endpoint (/download/csv & /download/csv). * Parameters for the download CSV and XLSX endpoint (/download/table-schema & /download/csv & /download/csv).
*/ */
export interface DownloadDocParams { export interface DownloadDocParams {
tableId: string; tableId: string;
@ -418,6 +418,7 @@ export interface DocAPI {
getDownloadUrl(template?: boolean): string; getDownloadUrl(template?: boolean): string;
getDownloadXlsxUrl(params?: DownloadDocParams): string; getDownloadXlsxUrl(params?: DownloadDocParams): string;
getDownloadCsvUrl(params: DownloadDocParams): string; getDownloadCsvUrl(params: DownloadDocParams): string;
getDownloadTableSchemaUrl(params: DownloadDocParams): string;
/** /**
* Exports current document to the Google Drive as a spreadsheet file. To invoke this method, first * Exports current document to the Google Drive as a spreadsheet file. To invoke this method, first
* acquire "code" via Google Auth Endpoint (see ShareMenu.ts for an example). * acquire "code" via Google Auth Endpoint (see ShareMenu.ts for an example).
@ -920,6 +921,11 @@ export class DocAPIImpl extends BaseAPI implements DocAPI {
return this._url + '/download/csv?' + encodeQueryParams({...params}); return this._url + '/download/csv?' + encodeQueryParams({...params});
} }
public getDownloadTableSchemaUrl(params: DownloadDocParams) {
// We spread `params` to work around TypeScript being overly cautious.
return this._url + '/download/table-schema?' + encodeQueryParams({...params});
}
public async sendToDrive(code: string, title: string): Promise<{url: string}> { public async sendToDrive(code: string, title: string): Promise<{url: string}> {
const url = new URL(`${this._url}/send-to-drive`); const url = new URL(`${this._url}/send-to-drive`);
url.searchParams.append('title', title); url.searchParams.append('title', title);

@ -0,0 +1,11 @@
import {NumberFormatOptions} from 'app/common/NumberFormat';
export interface WidgetOptions extends NumberFormatOptions {
textColor?: 'string';
fillColor?: 'string';
alignment?: 'left' | 'center' | 'right';
dateFormat?: string;
timeFormat?: string;
widget?: 'HyperLink';
choices?: Array<string>;
}

@ -36,6 +36,7 @@ import {IDocWorkerMap} from "app/server/lib/DocWorkerMap";
import {DownloadOptions, parseExportParameters} from "app/server/lib/Export"; import {DownloadOptions, parseExportParameters} from "app/server/lib/Export";
import {downloadCSV} from "app/server/lib/ExportCSV"; import {downloadCSV} from "app/server/lib/ExportCSV";
import {downloadXLSX} from "app/server/lib/ExportXLSX"; import {downloadXLSX} from "app/server/lib/ExportXLSX";
import {collectTableSchemaInFrictionlessFormat} from "app/server/lib/ExportTableSchema";
import {expressWrap} from 'app/server/lib/expressWrap'; import {expressWrap} from 'app/server/lib/expressWrap';
import {filterDocumentInPlace} from "app/server/lib/filterUtils"; import {filterDocumentInPlace} from "app/server/lib/filterUtils";
import {googleAuthTokenMiddleware} from "app/server/lib/GoogleAuth"; import {googleAuthTokenMiddleware} from "app/server/lib/GoogleAuth";
@ -871,6 +872,26 @@ export class DocWorkerApi {
res.json(result); res.json(result);
})); }));
this._app.get('/api/docs/:docId/download/table-schema', canView, withDoc(async (activeDoc, req, res) => {
const doc = await this._dbManager.getDoc(req);
const options = this._getDownloadOptions(req, doc.name);
const tableSchema = await collectTableSchemaInFrictionlessFormat(activeDoc, req, options);
const apiPath = await this._grist.getResourceUrl(doc, 'api');
const query = new URLSearchParams(req.query as {[key: string]: string});
const tableSchemaPath = `${apiPath}/download/csv?${query.toString()}`;
res.send({
format: "csv",
mediatype: "text/csv",
encoding: "utf-8",
path: tableSchemaPath,
dialect: {
delimiter: ",",
doubleQuote: true,
},
...tableSchema,
});
}));
this._app.get('/api/docs/:docId/download/csv', canView, withDoc(async (activeDoc, req, res) => { this._app.get('/api/docs/:docId/download/csv', canView, withDoc(async (activeDoc, req, res) => {
// Query DB for doc metadata to get the doc title. // Query DB for doc metadata to get the doc title.
const {name: docTitle} = await this._dbManager.getDoc(req); const {name: docTitle} = await this._dbManager.getDoc(req);

@ -204,7 +204,8 @@ export async function exportTable(
label: tc.label, label: tc.label,
type: displayCol.type, type: displayCol.type,
widgetOptions, widgetOptions,
parentPos: tc.parentPos parentPos: tc.parentPos,
description: displayCol.description,
}; };
}).filter(tc => tc !== emptyCol); }).filter(tc => tc !== emptyCol);
@ -279,6 +280,7 @@ export async function exportSection(
label: col.label, label: col.label,
type: col.type, type: col.type,
parentPos: col.parentPos, parentPos: col.parentPos,
description: col.description,
widgetOptions: Object.assign(colWidgetOptions, fieldWidgetOptions), widgetOptions: Object.assign(colWidgetOptions, fieldWidgetOptions),
}; };
}; };

@ -0,0 +1,151 @@
import * as express from 'express';
import {ApiError} from 'app/common/ApiError';
import {WidgetOptions} from 'app/common/WidgetOptions';
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
import {DownloadOptions, exportTable} from 'app/server/lib/Export';
interface ExportColumn {
id: number;
colId: string;
label: string;
type: string;
widgetOptions: WidgetOptions;
description?: string;
parentPos: number;
}
interface FrictionlessFormat {
name: string;
title: string;
schema: {
fields: {
name: string;
type: string;
description?: string;
format?: string;
bareNumber?: boolean;
groupChar?: string;
decimalChar?: string;
gristFormat?: string;
constraint?: {};
trueValue?: string[];
falseValue?: string[];
}[]
}
}
/**
* Return a table schema for frictionless interoperability
*
* See https://specs.frictionlessdata.io/table-schema/#page-frontmatter-title for spec
* @param {Object} activeDoc - the activeDoc that the table being converted belongs to.
* @param {Object} options - options to get the table ID
* @return {Promise<FrictionlessFormat>} Promise for the resulting schema.
*/
export async function collectTableSchemaInFrictionlessFormat(
activeDoc: ActiveDoc,
req: express.Request,
options: DownloadOptions
): Promise<FrictionlessFormat> {
const {tableId} = options;
if (!activeDoc.docData) {
throw new Error('No docData in active document');
}
// Look up the table to make a CSV from.
const settings = activeDoc.docData.docSettings();
const tables = activeDoc.docData.getMetaTable('_grist_Tables');
const tableRef = tables.findRow('tableId', tableId);
if (tableRef === 0) {
throw new ApiError(`Table ${tableId} not found.`, 404);
}
const data = await exportTable(activeDoc, tableRef, req);
const tableSchema = columnsToTableSchema(tableId, data, settings.locale);
return tableSchema;
}
function columnsToTableSchema(
tableId: string,
{tableName, columns}: {tableName: string, columns: ExportColumn[]},
locale: string,
): FrictionlessFormat {
return {
name: tableId.toLowerCase().replace(/_/g, '-'),
title: tableName,
schema: {
fields: columns.map(col => ({
name: col.label,
...(col.description ? {description: col.description} : {}),
...buildTypeField(col, locale),
})),
}
};
}
function buildTypeField(col: ExportColumn, locale: string) {
const type = col.type.split(':', 1)[0];
switch (type) {
case 'Text':
return {
type: 'string',
format: col.widgetOptions.widget === 'HyperLink' ? 'uri' : 'default',
};
case 'Numeric':
return {
type: 'number',
bareNumber: col.widgetOptions?.numMode === 'decimal',
...getNumberSeparators(locale),
};
case 'Integer':
return {
type: 'integer',
bareNumber: col.widgetOptions?.numMode === 'decimal',
groupChar: getNumberSeparators(locale).groupChar,
};
case 'Date':
return {
type: 'date',
format: 'any',
gristFormat: col.widgetOptions?.dateFormat || 'YYYY-MM-DD',
};
case 'DateTime':
return {
type: 'datetime',
format: 'any',
gristFormat: `${col.widgetOptions?.dateFormat} ${col.widgetOptions?.timeFormat}`,
};
case 'Bool':
return {
type: 'boolean',
trueValue: ['TRUE'],
falseValue: ['FALSE'],
};
case 'Choice':
return {
type: 'string',
constraints: {enum: col.widgetOptions?.choices},
};
case 'ChoiceList':
return {
type: 'array',
constraints: {enum: col.widgetOptions?.choices},
};
case 'Reference':
return {type: 'string'};
case 'ReferenceList':
return {type: 'array'};
default:
return {type: 'string'};
}
}
function getNumberSeparators(locale: string) {
const numberWithGroupAndDecimalSeparator = 1000.1;
const parts = Intl.NumberFormat(locale).formatToParts(numberWithGroupAndDecimalSeparator);
return {
groupChar: parts.find(obj => obj.type === 'group')?.value,
decimalChar: parts.find(obj => obj.type === 'decimal')?.value,
};
}

@ -2,7 +2,7 @@
"ACUserManager": { "ACUserManager": {
"Enter email address": "E-Mail Adresse eingeben", "Enter email address": "E-Mail Adresse eingeben",
"Invite new member": "Neues Mitglied einladen", "Invite new member": "Neues Mitglied einladen",
"We'll email an invite to {{email}}": "Wir schicken eine Einladung zu {{email}}" "We'll email an invite to {{email}}": "Eine Einladung wird per E-Mail an {{email}} gesendet"
}, },
"AccessRules": { "AccessRules": {
"Add Column Rule": "Spaltenregel hinzufügen", "Add Column Rule": "Spaltenregel hinzufügen",
@ -95,7 +95,8 @@
"App": { "App": {
"Description": "Beschreibung", "Description": "Beschreibung",
"Key": "Schlüssel", "Key": "Schlüssel",
"Memory Error": "Speicherfehler" "Memory Error": "Speicherfehler",
"Translators: please translate this only when your language is ready to be offered to users": "Übersetzer: Bitte übersetzen Sie dies erst, wenn Ihre Sprache bereit ist, Benutzern angeboten zu werden"
}, },
"AppHeader": { "AppHeader": {
"Home Page": "Startseite", "Home Page": "Startseite",
@ -279,10 +280,10 @@
"Time Zone:": "Zeitzone:", "Time Zone:": "Zeitzone:",
"API": "API", "API": "API",
"Document ID copied to clipboard": "Dokument-ID in die Zwischenablage kopiert", "Document ID copied to clipboard": "Dokument-ID in die Zwischenablage kopiert",
"Ok": "Ok" "Ok": "OK"
}, },
"DocumentUsage": { "DocumentUsage": {
"Attachments Size": "Anhänge Größe", "Attachments Size": "Größe der Anhänge",
"Contact the site owner to upgrade the plan to raise limits.": "Wenden Sie sich an den Eigentümer der Seite, um den Plan zu aktualisieren und die Grenzwerte zu erhöhen.", "Contact the site owner to upgrade the plan to raise limits.": "Wenden Sie sich an den Eigentümer der Seite, um den Plan zu aktualisieren und die Grenzwerte zu erhöhen.",
"Data Size": "Daten Größe", "Data Size": "Daten Größe",
"Document limits {{- link}}.": "Dokumentgrenzwerte {{- link}}.", "Document limits {{- link}}.": "Dokumentgrenzwerte {{- link}}.",
@ -572,7 +573,7 @@
"Save": "Speichern", "Save": "Speichern",
"Select Widget": "Widget auswählen", "Select Widget": "Widget auswählen",
"Series": "Serie", "Series": "Serie",
"Sort & Filter": "Sortieren & Filtern", "Sort & Filter": "Sortieren und Filtern",
"TRANSFORM": "UMWANDELN", "TRANSFORM": "UMWANDELN",
"Theme": "Thema", "Theme": "Thema",
"WIDGET TITLE": "WIDGET-TITEL", "WIDGET TITLE": "WIDGET-TITEL",
@ -687,7 +688,7 @@
"No Default Access": "Kein Standardzugriff", "No Default Access": "Kein Standardzugriff",
"None": "Keine", "None": "Keine",
"Owner": "Eigentümer", "Owner": "Eigentümer",
"View & Edit": "Anzeigen & Bearbeiten", "View & Edit": "Anzeigen und Bearbeiten",
"View Only": "Nur anzeigen", "View Only": "Nur anzeigen",
"Viewer": "Betrachter" "Viewer": "Betrachter"
}, },
@ -825,7 +826,7 @@
}, },
"modals": { "modals": {
"Cancel": "Abbrechen", "Cancel": "Abbrechen",
"Ok": "Ok", "Ok": "OK",
"Save": "Speichern" "Save": "Speichern"
}, },
"pages": { "pages": {
@ -943,7 +944,7 @@
"Row ID": "Zeilen-ID" "Row ID": "Zeilen-ID"
}, },
"HyperLinkEditor": { "HyperLinkEditor": {
"[link label] url": "[Linklabel]-URL" "[link label] url": "[Linkbezeichnung] URL"
}, },
"welcomeTour": { "welcomeTour": {
"Add New": "Neu hinzufügen", "Add New": "Neu hinzufügen",

@ -1,8 +1,8 @@
{ {
"ACUserManager": { "ACUserManager": {
"Enter email address": "Enter email address", "Enter email address": "Enter e-mail address",
"Invite new member": "Invite new member", "Invite new member": "Invite new member",
"We'll email an invite to {{email}}": "We'll email an invite to {{email}}" "We'll email an invite to {{email}}": "An invite will be e-mailed to {{email}}"
}, },
"AccessRules": { "AccessRules": {
"Add Column Rule": "Add Column Rule", "Add Column Rule": "Add Column Rule",
@ -48,7 +48,7 @@
"Allow signing in to this account with Google": "Allow signing in to this account with Google", "Allow signing in to this account with Google": "Allow signing in to this account with Google",
"Change Password": "Change Password", "Change Password": "Change Password",
"Edit": "Edit", "Edit": "Edit",
"Email": "Email", "Email": "E-mail",
"Login Method": "Login Method", "Login Method": "Login Method",
"Name": "Name", "Name": "Name",
"Names only allow letters, numbers and certain special characters": "Names only allow letters, numbers and certain special characters", "Names only allow letters, numbers and certain special characters": "Names only allow letters, numbers and certain special characters",
@ -99,7 +99,8 @@
"App": { "App": {
"Description": "Description", "Description": "Description",
"Key": "Key", "Key": "Key",
"Memory Error": "Memory Error" "Memory Error": "Memory Error",
"Translators: please translate this only when your language is ready to be offered to users": "Translators: please translate this only when your language is ready to be offered to users"
}, },
"AppHeader": { "AppHeader": {
"Home Page": "Home Page", "Home Page": "Home Page",
@ -137,7 +138,7 @@
"Each Y series is followed by two series, for top and bottom error bars.": "Each Y series is followed by two series, for top and bottom error bars.", "Each Y series is followed by two series, for top and bottom error bars.": "Each Y series is followed by two series, for top and bottom error bars.",
"Pick a column": "Pick a column", "Pick a column": "Pick a column",
"Toggle chart aggregation": "Toggle chart aggregation", "Toggle chart aggregation": "Toggle chart aggregation",
"selected new group data columns": "selected new group data columns" "selected new group data columns": "selected new group-data columns"
}, },
"CodeEditorPanel": { "CodeEditorPanel": {
"Access denied": "Access denied", "Access denied": "Access denied",
@ -218,7 +219,7 @@
"Document will be permanently deleted.": "Document will be permanently deleted.", "Document will be permanently deleted.": "Document will be permanently deleted.",
"Documents stay in Trash for 30 days, after which they get deleted permanently.": "Documents stay in Trash for 30 days, after which they get deleted permanently.", "Documents stay in Trash for 30 days, after which they get deleted permanently.": "Documents stay in Trash for 30 days, after which they get deleted permanently.",
"Edited {{at}}": "Edited {{at}}", "Edited {{at}}": "Edited {{at}}",
"Examples & Templates": "Examples & Templates", "Examples & Templates": "Examples and Templates",
"Examples and Templates": "Examples and Templates", "Examples and Templates": "Examples and Templates",
"Featured": "Featured", "Featured": "Featured",
"Manage Users": "Manage Users", "Manage Users": "Manage Users",
@ -271,10 +272,10 @@
"Time Zone:": "Time Zone:", "Time Zone:": "Time Zone:",
"API": "API", "API": "API",
"Document ID copied to clipboard": "Document ID copied to clipboard", "Document ID copied to clipboard": "Document ID copied to clipboard",
"Ok": "Ok" "Ok": "OK"
}, },
"DocumentUsage": { "DocumentUsage": {
"Attachments Size": "Attachments Size", "Attachments Size": "Size of Attachments",
"Contact the site owner to upgrade the plan to raise limits.": "Contact the site owner to upgrade the plan to raise limits.", "Contact the site owner to upgrade the plan to raise limits.": "Contact the site owner to upgrade the plan to raise limits.",
"Data Size": "Data Size", "Data Size": "Data Size",
"For higher limits, ": "For higher limits, ", "For higher limits, ": "For higher limits, ",
@ -300,7 +301,7 @@
"Check out our related tutorial to learn how to create summary tables and charts, and to link charts dynamically.": "Check out our related tutorial to learn how to create summary tables and charts, and to link charts dynamically.", "Check out our related tutorial to learn how to create summary tables and charts, and to link charts dynamically.": "Check out our related tutorial to learn how to create summary tables and charts, and to link charts dynamically.",
"Investment Research": "Investment Research", "Investment Research": "Investment Research",
"Lightweight CRM": "Lightweight CRM", "Lightweight CRM": "Lightweight CRM",
"Tutorial: Analyze & Visualize": "Tutorial: Analyze & Visualize", "Tutorial: Analyze & Visualize": "Tutorial: Analyze and Visualize",
"Tutorial: Create a CRM": "Tutorial: Create a CRM", "Tutorial: Create a CRM": "Tutorial: Create a CRM",
"Tutorial: Manage Business Data": "Tutorial: Manage Business Data", "Tutorial: Manage Business Data": "Tutorial: Manage Business Data",
"Welcome to the Afterschool Program template": "Welcome to the Afterschool Program template", "Welcome to the Afterschool Program template": "Welcome to the Afterschool Program template",
@ -365,7 +366,7 @@
"Hide {{count}} columns_one": "Hide column", "Hide {{count}} columns_one": "Hide column",
"Hide {{count}} columns_other": "Hide {{count}} columns", "Hide {{count}} columns_other": "Hide {{count}} columns",
"Insert column to the {{to}}": "Insert column to the {{to}}", "Insert column to the {{to}}": "Insert column to the {{to}}",
"More sort options ...": "More sort options…", "More sort options ...": "More sorting options…",
"Rename column": "Rename column", "Rename column": "Rename column",
"Reset {{count}} columns_one": "Reset column", "Reset {{count}} columns_one": "Reset column",
"Reset {{count}} columns_other": "Reset {{count}} columns", "Reset {{count}} columns_other": "Reset {{count}} columns",
@ -413,7 +414,7 @@
"Create Workspace": "Create Workspace", "Create Workspace": "Create Workspace",
"Delete": "Delete", "Delete": "Delete",
"Delete {{workspace}} and all included documents?": "Delete {{workspace}} and all included documents?", "Delete {{workspace}} and all included documents?": "Delete {{workspace}} and all included documents?",
"Examples & Templates": "Examples & Templates", "Examples & Templates": "Examples and Templates",
"Import Document": "Import Document", "Import Document": "Import Document",
"Manage Users": "Manage Users", "Manage Users": "Manage Users",
"Rename": "Rename", "Rename": "Rename",
@ -533,7 +534,7 @@
"Select Widget": "Select Widget", "Select Widget": "Select Widget",
"Series_one": "Series", "Series_one": "Series",
"Series_other": "Series", "Series_other": "Series",
"Sort & Filter": "Sort & Filter", "Sort & Filter": "Sort and Filter",
"TRANSFORM": "TRANSFORM", "TRANSFORM": "TRANSFORM",
"Theme": "Theme", "Theme": "Theme",
"WIDGET TITLE": "WIDGET TITLE", "WIDGET TITLE": "WIDGET TITLE",
@ -636,7 +637,7 @@
"No Default Access": "No Default Access", "No Default Access": "No Default Access",
"None": "None", "None": "None",
"Owner": "Owner", "Owner": "Owner",
"View & Edit": "View & Edit", "View & Edit": "View and Edit",
"View Only": "View Only", "View Only": "View Only",
"Viewer": "Viewer" "Viewer": "Viewer"
}, },
@ -660,7 +661,7 @@
"Unmark On-Demand": "Unmark On-Demand" "Unmark On-Demand": "Unmark On-Demand"
}, },
"ViewLayoutMenu": { "ViewLayoutMenu": {
"Advanced Sort & Filter": "Advanced Sort & Filter", "Advanced Sort & Filter": "Advanced Sorting and Filtering",
"Copy anchor link": "Copy anchor link", "Copy anchor link": "Copy anchor link",
"Data selection": "Data selection", "Data selection": "Data selection",
"Delete record": "Delete record", "Delete record": "Delete record",
@ -698,9 +699,9 @@
}, },
"WelcomeQuestions": { "WelcomeQuestions": {
"Education": "Education", "Education": "Education",
"Finance & Accounting": "Finance & Accounting", "Finance & Accounting": "Finance and Accounting",
"HR & Management": "HR & Management", "HR & Management": "HR and Management",
"IT & Technology": "IT & Technology", "IT & Technology": "IT and Technology",
"Marketing": "Marketing", "Marketing": "Marketing",
"Media Production": "Media Production", "Media Production": "Media Production",
"Other": "Other", "Other": "Other",
@ -743,7 +744,7 @@
"Sign in to access this organization's documents.": "Sign in to access this organization's documents.", "Sign in to access this organization's documents.": "Sign in to access this organization's documents.",
"Signed out{{suffix}}": "Signed out{{suffix}}", "Signed out{{suffix}}": "Signed out{{suffix}}",
"Something went wrong": "Something went wrong", "Something went wrong": "Something went wrong",
"The requested page could not be found.{{separator}}Please check the URL and try again.": "The requested page could not be found.{{separator}}Please check the URL and try again.", "The requested page could not be found.{{separator}}Please check the URL and try again.": "Could not find the requested page.{{separator}}Please check the URL and try again.",
"There was an error: {{message}}": "There was an error: {{message}}", "There was an error: {{message}}": "There was an error: {{message}}",
"There was an unknown error.": "There was an unknown error.", "There was an unknown error.": "There was an unknown error.",
"You are now signed out.": "You are now signed out.", "You are now signed out.": "You are now signed out.",
@ -769,7 +770,7 @@
}, },
"modals": { "modals": {
"Cancel": "Cancel", "Cancel": "Cancel",
"Ok": "Ok", "Ok": "OK",
"Save": "Save" "Save": "Save"
}, },
"pages": { "pages": {
@ -878,7 +879,7 @@
"editingFormula is required": "editingFormula is required" "editingFormula is required": "editingFormula is required"
}, },
"HyperLinkEditor": { "HyperLinkEditor": {
"[link label] url": "[link label] url" "[link label] url": "[link label] URL"
}, },
"NumericTextBox": { "NumericTextBox": {
"Currency": "Currency", "Currency": "Currency",

@ -44,7 +44,7 @@
"Allow signing in to this account with Google": "Permitir iniciar sesión en esta cuenta con Google", "Allow signing in to this account with Google": "Permitir iniciar sesión en esta cuenta con Google",
"Change Password": "Cambiar contraseña", "Change Password": "Cambiar contraseña",
"Edit": "Editar", "Edit": "Editar",
"Email": "E-mail", "Email": "Correo electrónico",
"Login Method": "Método de inicio de sesión", "Login Method": "Método de inicio de sesión",
"Name": "Nombre", "Name": "Nombre",
"Names only allow letters, numbers and certain special characters": "Los nombres solo permiten letras, números y ciertos caracteres especiales", "Names only allow letters, numbers and certain special characters": "Los nombres solo permiten letras, números y ciertos caracteres especiales",
@ -84,7 +84,8 @@
"App": { "App": {
"Description": "Descripción", "Description": "Descripción",
"Key": "Clave", "Key": "Clave",
"Memory Error": "Error de memoria" "Memory Error": "Error de memoria",
"Translators: please translate this only when your language is ready to be offered to users": "Traductores: por favor, traduzcan esto sólo cuando su idioma esté listo para ser ofrecido a los usuarios"
}, },
"AppHeader": { "AppHeader": {
"Home Page": "Portada", "Home Page": "Portada",
@ -226,7 +227,7 @@
"Save and Reload": "Guardar y recargar", "Save and Reload": "Guardar y recargar",
"This document's ID (for API use):": "ID de este documento (para uso de API):", "This document's ID (for API use):": "ID de este documento (para uso de API):",
"Time Zone:": "Zona horaria:", "Time Zone:": "Zona horaria:",
"Ok": "Ok", "Ok": "OK",
"Document ID copied to clipboard": "ID de documento copiado al portapapeles", "Document ID copied to clipboard": "ID de documento copiado al portapapeles",
"API": "API" "API": "API"
}, },
@ -612,7 +613,7 @@
"WelcomeQuestions": { "WelcomeQuestions": {
"Education": "Educación", "Education": "Educación",
"Finance & Accounting": "Finanzas y Contabilidad", "Finance & Accounting": "Finanzas y Contabilidad",
"HR & Management": "Recursos humanos y gestión", "HR & Management": "RRHH y Gestión",
"IT & Technology": "TI y Tecnología", "IT & Technology": "TI y Tecnología",
"Marketing": "Publicidad", "Marketing": "Publicidad",
"Media Production": "Producción audiovisual", "Media Production": "Producción audiovisual",
@ -682,7 +683,7 @@
"ACUserManager": { "ACUserManager": {
"Enter email address": "Introduzca la dirección de correo electrónico", "Enter email address": "Introduzca la dirección de correo electrónico",
"Invite new member": "Invitar nuevo miembro", "Invite new member": "Invitar nuevo miembro",
"We'll email an invite to {{email}}": "Enviaremos una invitación a {{email}}" "We'll email an invite to {{email}}": "Se enviará una invitación por correo electrónico a {{email}}"
}, },
"ViewAsDropdown": { "ViewAsDropdown": {
"View As": "Ver como", "View As": "Ver como",
@ -795,7 +796,7 @@
"No Default Access": "Sin acceso predeterminado", "No Default Access": "Sin acceso predeterminado",
"None": "Ninguno", "None": "Ninguno",
"Owner": "Propietario", "Owner": "Propietario",
"View & Edit": "Ver y Editar", "View & Edit": "Ver y editar",
"View Only": "Ver sólo", "View Only": "Ver sólo",
"Viewer": "Espectador" "Viewer": "Espectador"
}, },
@ -830,7 +831,7 @@
}, },
"modals": { "modals": {
"Cancel": "Cancelar", "Cancel": "Cancelar",
"Ok": "Ok", "Ok": "OK",
"Save": "Guardar" "Save": "Guardar"
}, },
"pages": { "pages": {
@ -940,7 +941,7 @@
"convert to card view, select data, and more.": "convertir a la vista de la tarjeta, seleccionar datos y más." "convert to card view, select data, and more.": "convertir a la vista de la tarjeta, seleccionar datos y más."
}, },
"HyperLinkEditor": { "HyperLinkEditor": {
"[link label] url": "[etiqueta de enlace] url" "[link label] url": "[etiqueta de enlace] URL"
}, },
"NumericTextBox": { "NumericTextBox": {
"Currency": "Moneda", "Currency": "Moneda",

@ -352,7 +352,8 @@
"App": { "App": {
"Memory Error": "Errore di memoria", "Memory Error": "Errore di memoria",
"Key": "Chiave", "Key": "Chiave",
"Description": "Descrizione" "Description": "Descrizione",
"Translators: please translate this only when your language is ready to be offered to users": "Traduttori: per favore tradurre questo solo quando la tua lingua è pronta per gli utenti"
}, },
"AppModel": { "AppModel": {
"This team site is suspended. Documents can be read, but not modified.": "Il sito del team è sospeso. Si possono leggere i documenti, ma non modificarli." "This team site is suspended. Documents can be read, but not modified.": "Il sito del team è sospeso. Si possono leggere i documenti, ma non modificarli."
@ -386,7 +387,7 @@
"Allow signing in to this account with Google": "Permetti di accedere a questo account con Google", "Allow signing in to this account with Google": "Permetti di accedere a questo account con Google",
"Change Password": "Cambia password", "Change Password": "Cambia password",
"Edit": "Modifica", "Edit": "Modifica",
"Email": "Email", "Email": "E-mail",
"Login Method": "Metodo di accesso", "Login Method": "Metodo di accesso",
"Name": "Nome", "Name": "Nome",
"Names only allow letters, numbers and certain special characters": "Per il nome puoi usare solo lettere, numeri e alcuni caratteri speciali", "Names only allow letters, numbers and certain special characters": "Per il nome puoi usare solo lettere, numeri e alcuni caratteri speciali",
@ -434,9 +435,9 @@
"When adding table rules, automatically add a rule to grant OWNER full access.": "Quando si mettono regole per la tabella, inserire sempre una che dà pieno accesso a OWNER." "When adding table rules, automatically add a rule to grant OWNER full access.": "Quando si mettono regole per la tabella, inserire sempre una che dà pieno accesso a OWNER."
}, },
"ACUserManager": { "ACUserManager": {
"Enter email address": "Inserisci indirizzo email", "Enter email address": "Inserisci indirizzo e-mail",
"Invite new member": "Invita un nuovo membro", "Invite new member": "Invita un nuovo membro",
"We'll email an invite to {{email}}": "Spediremo un invito a {{email}}" "We'll email an invite to {{email}}": "Un invito sarà inviato a {{email}}"
}, },
"ViewAsDropdown": { "ViewAsDropdown": {
"View As": "Vedi come", "View As": "Vedi come",
@ -598,7 +599,7 @@
"Time Zone:": "Zona oraria:", "Time Zone:": "Zona oraria:",
"API": "API", "API": "API",
"Document ID copied to clipboard": "ID del documento copiato", "Document ID copied to clipboard": "ID del documento copiato",
"Ok": "Ok" "Ok": "OK"
}, },
"DocumentUsage": { "DocumentUsage": {
"Data Size": "Dimensione dei dati", "Data Size": "Dimensione dei dati",
@ -816,7 +817,9 @@
"Open configuration": "Apri configurazione", "Open configuration": "Apri configurazione",
"Print widget": "Widget di stampa", "Print widget": "Widget di stampa",
"Show raw data": "Mostra dati grezzi", "Show raw data": "Mostra dati grezzi",
"Widget options": "Opzioni widget" "Widget options": "Opzioni widget",
"Add to page": "Aggiungi a pagina",
"Collapse widget": "Compatta widget"
}, },
"ViewSectionMenu": { "ViewSectionMenu": {
"(customized)": "(personalizzato)", "(customized)": "(personalizzato)",
@ -957,5 +960,8 @@
"Access Rules": "Regole di accesso", "Access Rules": "Regole di accesso",
"Access rules give you the power to create nuanced rules to determine who can see or edit which parts of your document.": "Le regole di accesso ti danno il potere di creare regole sofisticate per decidere chi può vedere o modificare il tuo documento, e quali parti.", "Access rules give you the power to create nuanced rules to determine who can see or edit which parts of your document.": "Le regole di accesso ti danno il potere di creare regole sofisticate per decidere chi può vedere o modificare il tuo documento, e quali parti.",
"Reference columns are the key to {{relational}} data in Grist.": "Le colonne con riferimenti sono il motore {{relational}} dei dati in Grist." "Reference columns are the key to {{relational}} data in Grist.": "Le colonne con riferimenti sono il motore {{relational}} dei dati in Grist."
},
"DescriptionConfig": {
"DESCRIPTION": "DESCRIZIONE"
} }
} }

File diff suppressed because it is too large Load Diff

@ -823,5 +823,144 @@
}, },
"ChoiceTextBox": { "ChoiceTextBox": {
"CHOICES": "WYBORY" "CHOICES": "WYBORY"
},
"FormulaEditor": {
"Error in the cell": "Błąd w komórce",
"editingFormula is required": "edycja Formuły jest wymagana",
"Column or field is required": "Kolumna lub pole jest wymagane",
"Errors in {{numErrors}} of {{numCells}} cells": "Błędy w {{numErrors}} komórek {{numCells}}",
"Errors in all {{numErrors}} cells": "Błędy we wszystkich komórkach {{numErrors}}"
},
"FieldEditor": {
"Unable to finish saving edited cell": "Nie można zakończyć zapisywania edytowanej komórki",
"It should be impossible to save a plain data value into a formula column": "Zapisanie zwykłej wartości danych w kolumnie formuły powinno być niemożliwe"
},
"HyperLinkEditor": {
"[link label] url": "[etykieta linku] url"
},
"NumericTextBox": {
"Decimals": "Miejsc dziesiętnych",
"Default currency ({{defaultCurrency}})": "Waluta domyślna ({{defaultCurrency}})",
"Number Format": "Format liczb",
"Currency": "Waluta:"
},
"ConditionalStyle": {
"Add another rule": "Dodaj kolejną regułę",
"Add conditional style": "Dodaj styl warunkowy",
"Error in style rule": "Błąd w regule stylu",
"Row Style": "Styl wiersza",
"Rule must return True or False": "Reguła musi zwracać wartość Prawda lub Fałsz"
},
"CurrencyPicker": {
"Invalid currency": "Nieprawidłowa waluta"
},
"DiscussionEditor": {
"Only current page": "Tylko bieżąca strona",
"Remove": "Usuń",
"Resolve": "Rozwiązać",
"Save": "Zapisać",
"Showing last {{nb}} comments": "Wyświetlanie ostatnich {{nb}} komentarzy",
"Started discussion": "Rozpoczęto dyskusję",
"Write a comment": "Napisz komentarz",
"Only my threads": "Tylko moje wątki",
"Open": "Otwórz",
"Reply to a comment": "Odpowiedz na komentarz",
"Show resolved comments": "Pokaż rozwiązane komentarze",
"Cancel": "Anulować",
"Comment": "Komentarz",
"Edit": "Edytuj",
"Marked as resolved": "Oznaczone jako rozwiązane",
"Reply": "Odpowiedź"
},
"EditorTooltip": {
"Convert column to formula": "Konwertuj kolumnę na formułę"
},
"FieldBuilder": {
"Apply Formula to Data": "Zastosuj formułę do danych",
"CELL FORMAT": "FORMAT KOMÓRKI",
"DATA FROM TABLE": "DANE Z TABELI",
"Mixed format": "Format mieszany",
"Mixed types": "Rodzaje mieszane",
"Save field settings for {{colId}} as common": "Zapisz ustawienia pola dla {{colId}} jako typowe",
"Use separate field settings for {{colId}}": "Użyj oddzielnych ustawień pól dla {{colId}}",
"Revert field settings for {{colId}} to common": "Przywróć ustawienia pola dla {{colId}} do wspólnych",
"Changing multiple column types": "Zmienianie wielu typów kolumn"
},
"Reference": {
"CELL FORMAT": "FORMAT KOMÓRKI",
"Row ID": "Identyfikator wiersza",
"SHOW COLUMN": "POKAŻ KOLUMNĘ"
},
"welcomeTour": {
"Add New": "Dodaj nowy",
"Building up": "Budowanie",
"Flying higher": "Latanie wyżej",
"Help Center": "Centrum pomocy",
"Make it relational! Use the {{ref}} type to link tables. ": "Spraw, aby był relacyjny! Użyj typu {{ref}}, aby połączyć tabele. ",
"Reference": "Odnośnik",
"Set formatting options, formulas, or column types, such as dates, choices, or attachments. ": "Ustaw opcje formatowania, formuły lub typy kolumn, takie jak daty, wybory lub załączniki. ",
"Sharing": "Udostępnianie",
"Start with {{equal}} to enter a formula.": "Zacznij od {{equal}}, aby wprowadzić formułę.",
"convert to card view, select data, and more.": "Konwertuj na widok karty, wybierz Dane i nie tylko.",
"creator panel": "Panel tworzenia",
"template library": "Biblioteka szablonów",
"Configuring your document": "Konfigurowanie dokumentu",
"Customizing columns": "Dostosowywanie kolumn",
"Double-click or hit {{enter}} on a cell to edit it. ": "Kliknij dwukrotnie lub naciśnij {{enter}} na komórkę, aby ją edytować. ",
"Enter": "Wejść",
"Editing Data": "Edycja danych",
"Welcome to Grist!": "Witamy w Grist!",
"Use {{helpCenter}} for documentation or questions.": "Użyj {{helpCenter}} do dokumentacji lub pytań.",
"Browse our {{templateLibrary}} to discover what's possible and get inspired.": "Przeglądaj nasze {{templateLibrary}}, aby odkryć, co jest możliwe i zainspirować się.",
"Toggle the {{creatorPanel}} to format columns, ": "Przełącz {{creatorPanel}}, aby sformatować kolumny, ",
"Share": "Udostępnij",
"Use the Share button ({{share}}) to share the document or export data.": "Użyj przycisku Udostępnij ({{share}}), aby udostępnić dokument lub wyeksportować dane.",
"Use {{addNew}} to add widgets, pages, or import more data. ": "Użyj {{addNew}}, aby dodać widżety, strony lub zaimportować więcej danych. "
},
"GristTooltips": {
"Click on “Open row styles” to apply conditional formatting to rows.": "Kliknij na \"Otwórz style wierszy\", aby zastosować formatowanie warunkowe do wierszy.",
"Click the Add New button to create new documents or workspaces, or import data.": "Kliknij przycisk Dodaj nowy, aby utworzyć nowe dokumenty lub obszary robocze albo zaimportować dane.",
"Learn more.": "Dowiedz się więcej.",
"Link your new widget to an existing widget on this page.": "Połącz swój nowy widget z istniejącym widgetem na tej stronie.",
"Nested Filtering": "Filtrowanie zagnieżdżone",
"Pinned filters are displayed as buttons above the widget.": "Przypięte filtry są wyświetlane jako przyciski nad widżetem.",
"Pinning Filters": "Przypinanie filtrów",
"Raw Data page": "Strona z danymi surowymi",
"Reference Columns": "Kolumny referencyjne",
"Select the table containing the data to show.": "Wybierz tabelę zawierającą dane do wyświetlenia.",
"Select the table to link to.": "Wybierz tabelę, do której chcesz utworzyć łącze.",
"Selecting Data": "Wybieranie danych",
"The Raw Data page lists all data tables in your document, including summary tables and tables not included in page layouts.": "Strona Dane surowe zawiera listę wszystkich tabel danych w dokumencie, w tym tabel podsumowania i tabel nieuwzględnionych w układach stron.",
"The total size of all data in this document, excluding attachments.": "Całkowity rozmiar wszystkich danych w tym dokumencie, z wyłączeniem załączników.",
"Try out changes in a copy, then decide whether to replace the original with your edits.": "Wypróbuj zmiany w kopii, a następnie zdecyduj, czy zastąpić oryginał zmianami.",
"Unpin to hide the the button while keeping the filter.": "Odepnij, aby ukryć przycisk, zachowując filtr.",
"You can filter by more than one column.": "Możesz filtrować według więcej niż jednej kolumny.",
"entire": "cały",
"Editing Card Layout": "Edytuj układ karty",
"Formulas that trigger in certain cases, and store the calculated value as data.": "Formuły, które wyzwalają się w określonych przypadkach i przechowują obliczoną wartość jako dane.",
"Useful for storing the timestamp or author of a new record, data cleaning, and more.": "Przydatne do przechowywania znacznika czasu lub autora nowego rekordu, czyszczenia danych i nie tylko.",
"Apply conditional formatting to cells in this column when formula conditions are met.": "Zastosuj formatowanie warunkowe do komórek w tej kolumnie, gdy spełnione są warunki formuły.",
"Apply conditional formatting to rows based on formulas.": "Zastosuj formatowanie warunkowe do wierszy na podstawie formuł.",
"Cells in a reference column always identify an {{entire}} record in that table, but you may select which column from that record to show.": "Komórki w kolumnie odwołania zawsze identyfikują rekord {{entire}} w tej tabeli, ale można wybrać kolumnę z tego rekordu do wyświetlenia.",
"Clicking {{EyeHideIcon}} in each cell hides the field from this view without deleting it.": "Kliknięcie {{EyeHideIcon}} w każdej komórce powoduje ukrycie pola w tym widoku bez jego usuwania.",
"Only those rows will appear which match all of the filters.": "Pojawią się tylko te wiersze, które pasują do wszystkich filtrów.",
"Linking Widgets": "Połączenie widżetów",
"Rearrange the fields in your card by dragging and resizing cells.": "Zmień kolejność pól na karcie, przeciągając komórki i zmieniając ich rozmiar.",
"Reference columns are the key to {{relational}} data in Grist.": "Kolumny referencyjne są kluczem do {{relational}} danych w Grist.",
"They allow for one record to point (or refer) to another.": "Pozwalają one na to, aby jeden rekord wskazywał (lub odnosił się) do innego.",
"This is the secret to Grist's dynamic and productive layouts.": "To jest sekret dynamicznych i produktywnych układów Grist.",
"Updates every 5 minutes.": "Aktualizacje co 5 minut.",
"Use the 𝚺 icon to create summary (or pivot) tables, for totals or subtotals.": "Użyj ikony Σ, aby utworzyć tabele podsumowujące (lub przestawne) dla sum lub sum częściowych.",
"Use the \\u{1D6BA} icon to create summary (or pivot) tables, for totals or subtotals.": "Użyj ikony \\u{1D6BA}, aby utworzyć tabele podsumowujące (przestawne) dla sum lub sum częściowych.",
"relational": "relacyjny",
"Access Rules": "Zasady dostępu",
"Access rules give you the power to create nuanced rules to determine who can see or edit which parts of your document.": "Reguły dostępu umożliwiają tworzenie reguł szczegółowych w celu określenia, kto może wyświetlać lub edytować poszczególne części dokumentu.",
"Add New": "Dodaj nowy"
},
"DescriptionConfig": {
"DESCRIPTION": "OPIS"
},
"LanguageMenu": {
"Language": "Język"
} }
} }

@ -1,8 +1,8 @@
{ {
"ACUserManager": { "ACUserManager": {
"Enter email address": "Digite seu email", "Enter email address": "Digite o endereço de e-mail",
"Invite new member": "Convidar novo membro", "Invite new member": "Convidar novo membro",
"We'll email an invite to {{email}}": "Enviaremos um convite por e-mail para {{email}}" "We'll email an invite to {{email}}": "Um convite será enviado por e-mail para {{email}}"
}, },
"AccessRules": { "AccessRules": {
"Add Column Rule": "Adicionar Regra de Coluna", "Add Column Rule": "Adicionar Regra de Coluna",
@ -95,7 +95,8 @@
"App": { "App": {
"Description": "Descrição", "Description": "Descrição",
"Key": "Chave", "Key": "Chave",
"Memory Error": "Erro de Memória" "Memory Error": "Erro de Memória",
"Translators: please translate this only when your language is ready to be offered to users": "Tradutores: por favor, traduzam isso apenas quando seu idioma estiver pronto para ser oferecido aos usuários"
}, },
"AppHeader": { "AppHeader": {
"Home Page": "Página inicial", "Home Page": "Página inicial",
@ -277,7 +278,7 @@
"Save and Reload": "Salvar e Recarregar", "Save and Reload": "Salvar e Recarregar",
"This document's ID (for API use):": "O ID deste documento (para uso em API):", "This document's ID (for API use):": "O ID deste documento (para uso em API):",
"Time Zone:": "Fuso horário:", "Time Zone:": "Fuso horário:",
"Ok": "Ok", "Ok": "OK",
"Document ID copied to clipboard": "ID do documento copiado para a área de transferência", "Document ID copied to clipboard": "ID do documento copiado para a área de transferência",
"API": "API" "API": "API"
}, },
@ -712,7 +713,7 @@
"Unmark table On-Demand?": "Desmarcar a tabela Sob-Demanda?" "Unmark table On-Demand?": "Desmarcar a tabela Sob-Demanda?"
}, },
"ViewLayoutMenu": { "ViewLayoutMenu": {
"Advanced Sort & Filter": "Classificação e Filtros Avançados", "Advanced Sort & Filter": "Classificação e filtragem avançadas",
"Copy anchor link": "Copiar o link de ancoragem", "Copy anchor link": "Copiar o link de ancoragem",
"Data selection": "Seleção de dados", "Data selection": "Seleção de dados",
"Delete record": "Excluir registro", "Delete record": "Excluir registro",
@ -825,7 +826,7 @@
}, },
"modals": { "modals": {
"Cancel": "Cancelar", "Cancel": "Cancelar",
"Ok": "Ok", "Ok": "OK",
"Save": "Salvar" "Save": "Salvar"
}, },
"pages": { "pages": {
@ -940,7 +941,7 @@
"Error in the cell": "Erro na célula" "Error in the cell": "Erro na célula"
}, },
"HyperLinkEditor": { "HyperLinkEditor": {
"[link label] url": "[rótulo do link] url" "[link label] url": "[rótulo do link] URL"
}, },
"NumericTextBox": { "NumericTextBox": {
"Currency": "Moeda", "Currency": "Moeda",

@ -39,7 +39,7 @@
"ACUserManager": { "ACUserManager": {
"Enter email address": "Введите адрес электронной почты", "Enter email address": "Введите адрес электронной почты",
"Invite new member": "Пригласить нового участника", "Invite new member": "Пригласить нового участника",
"We'll email an invite to {{email}}": "Мы вышлем приглашение на {{email}}" "We'll email an invite to {{email}}": "Приглашение будет отправлено на электронную почту {{email}}"
}, },
"AccountPage": { "AccountPage": {
"API": "API", "API": "API",
@ -50,7 +50,7 @@
"Account settings": "Настройки аккаунта", "Account settings": "Настройки аккаунта",
"Edit": "Редактировать", "Edit": "Редактировать",
"Name": "Имя", "Name": "Имя",
"Email": "Email", "Email": "E-mail",
"Login Method": "Способ входа в систему", "Login Method": "Способ входа в систему",
"Names only allow letters, numbers and certain special characters": "В именах допускаются только буквы, цифры и определенные специальные символы", "Names only allow letters, numbers and certain special characters": "В именах допускаются только буквы, цифры и определенные специальные символы",
"Save": "Сохранить", "Save": "Сохранить",
@ -62,7 +62,8 @@
"App": { "App": {
"Memory Error": "Ошибка памяти", "Memory Error": "Ошибка памяти",
"Description": "Описание", "Description": "Описание",
"Key": "Ключ" "Key": "Ключ",
"Translators: please translate this only when your language is ready to be offered to users": "Переводчики: пожалуйста, переведите это только тогда, когда ваш язык будет готов для использования пользователями"
}, },
"ColorSelect": { "ColorSelect": {
"Apply": "Применить", "Apply": "Применить",
@ -338,7 +339,7 @@
"Edited {{at}}": "Отредактировано {{at}}", "Edited {{at}}": "Отредактировано {{at}}",
"Document will be permanently deleted.": "Документ будет удален навсегда.", "Document will be permanently deleted.": "Документ будет удален навсегда.",
"Documents stay in Trash for 30 days, after which they get deleted permanently.": "Документы остаются в Корзине в течение 30 дней, после чего удаляются навсегда.", "Documents stay in Trash for 30 days, after which they get deleted permanently.": "Документы остаются в Корзине в течение 30 дней, после чего удаляются навсегда.",
"Examples & Templates": "Примеры & Шаблоны", "Examples & Templates": "Примеры и Шаблоны",
"Featured": "Рекомендуемые", "Featured": "Рекомендуемые",
"Manage Users": "Управление пользователями", "Manage Users": "Управление пользователями",
"Move": "Переместить", "Move": "Переместить",
@ -369,7 +370,7 @@
"DocumentSettings": { "DocumentSettings": {
"Document Settings": "Настройки документа", "Document Settings": "Настройки документа",
"Currency:": "Валюта:", "Currency:": "Валюта:",
"Ok": "Ok", "Ok": "OK",
"Locale:": "Регион:", "Locale:": "Регион:",
"Save and Reload": "Сохранить и Перезагрузить", "Save and Reload": "Сохранить и Перезагрузить",
"Save": "Сохранить", "Save": "Сохранить",
@ -394,7 +395,7 @@
}, },
"DocumentUsage": { "DocumentUsage": {
"Data Size": "Размер данных", "Data Size": "Размер данных",
"Attachments Size": "Размер вложений", "Attachments Size": "Размер Вложений",
"For higher limits, ": "Для более высоких пределов, ", "For higher limits, ": "Для более высоких пределов, ",
"Rows": "Строки", "Rows": "Строки",
"Contact the site owner to upgrade the plan to raise limits.": "Свяжитесь с владельцем сайта для обновления тарифа и увеличения лимитов.", "Contact the site owner to upgrade the plan to raise limits.": "Свяжитесь с владельцем сайта для обновления тарифа и увеличения лимитов.",
@ -417,7 +418,7 @@
"Investment Research": "Инвестиционные исследования", "Investment Research": "Инвестиционные исследования",
"Check out our related tutorial for how to link data, and create high-productivity layouts.": "Ознакомьтесь с нашим соответствующим учебником, чтобы узнать, как связывать данные и создавать высокопроизводительные макеты.", "Check out our related tutorial for how to link data, and create high-productivity layouts.": "Ознакомьтесь с нашим соответствующим учебником, чтобы узнать, как связывать данные и создавать высокопроизводительные макеты.",
"Lightweight CRM": "Легкая CRM", "Lightweight CRM": "Легкая CRM",
"Tutorial: Analyze & Visualize": "Учебник: Анализ и визуализация", "Tutorial: Analyze & Visualize": "Учебник: Анализ и Визуализация",
"Check out our related tutorial to learn how to create summary tables and charts, and to link charts dynamically.": "Ознакомьтесь с нашим соответствующим учебником, чтобы узнать, как создавать сводные таблицы и графики, а также динамически связывать графики.", "Check out our related tutorial to learn how to create summary tables and charts, and to link charts dynamically.": "Ознакомьтесь с нашим соответствующим учебником, чтобы узнать, как создавать сводные таблицы и графики, а также динамически связывать графики.",
"Tutorial: Create a CRM": "Учебник: Создание CRM", "Tutorial: Create a CRM": "Учебник: Создание CRM",
"Tutorial: Manage Business Data": "Учебник: Управление бизнес-данными", "Tutorial: Manage Business Data": "Учебник: Управление бизнес-данными",
@ -469,7 +470,7 @@
"Access Details": "Детали доступа", "Access Details": "Детали доступа",
"Create Empty Document": "Создать пустой документ", "Create Empty Document": "Создать пустой документ",
"Delete": "Удалить", "Delete": "Удалить",
"Examples & Templates": "Примеры & Шаблоны", "Examples & Templates": "Примеры и Шаблоны",
"Rename": "Переименовать", "Rename": "Переименовать",
"Delete {{workspace}} and all included documents?": "Удалить {{workspace}} и все прилагаемые документы?", "Delete {{workspace}} and all included documents?": "Удалить {{workspace}} и все прилагаемые документы?",
"Trash": "Корзина", "Trash": "Корзина",
@ -590,7 +591,7 @@
"Save": "Сохранить", "Save": "Сохранить",
"Select Widget": "Выберите виджет", "Select Widget": "Выберите виджет",
"Series_one": "Ряд", "Series_one": "Ряд",
"Sort & Filter": "Сортировка & Фильтрация", "Sort & Filter": "Сортировка и Фильтрация",
"TRANSFORM": "ПРЕОБРАЗОВАНИЕ", "TRANSFORM": "ПРЕОБРАЗОВАНИЕ",
"WIDGET TITLE": "ЗАГОЛОВОК ВИДЖЕТА", "WIDGET TITLE": "ЗАГОЛОВОК ВИДЖЕТА",
"You do not have edit access to this document": "У вас нет прав на редактирование этого документа", "You do not have edit access to this document": "У вас нет прав на редактирование этого документа",
@ -648,7 +649,7 @@
"UserManagerModel": { "UserManagerModel": {
"In Full": "Полный", "In Full": "Полный",
"None": "Без доступа", "None": "Без доступа",
"View & Edit": "Просмотр & Редактирование", "View & Edit": "Просмотр и Редактирование",
"Viewer": "Наблюдатель", "Viewer": "Наблюдатель",
"Owner": "Владелец", "Owner": "Владелец",
"No Default Access": "Нет доступа по умолчанию", "No Default Access": "Нет доступа по умолчанию",
@ -671,7 +672,7 @@
"Compact": "Компактная" "Compact": "Компактная"
}, },
"ViewLayoutMenu": { "ViewLayoutMenu": {
"Advanced Sort & Filter": "Расширенная Сортировка & Фильтр", "Advanced Sort & Filter": "Расширенная Сортировка и Фильтрация",
"Delete record": "Удалить запись", "Delete record": "Удалить запись",
"Delete widget": "Удалить виджет", "Delete widget": "Удалить виджет",
"Download as XLSX": "Скачать как XLSX", "Download as XLSX": "Скачать как XLSX",
@ -682,7 +683,9 @@
"Open configuration": "Открыть конфигурацию", "Open configuration": "Открыть конфигурацию",
"Print widget": "Печать виджета", "Print widget": "Печать виджета",
"Data selection": "Выбор данных", "Data selection": "Выбор данных",
"Widget options": "Параметры виджета" "Widget options": "Параметры виджета",
"Add to page": "Добавить на страницу",
"Collapse widget": "Свернуть виджет"
}, },
"FieldEditor": { "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": "Должно быть невозможно сохранить значение простых данных в столбце формулы",
@ -957,5 +960,8 @@
"Access rules give you the power to create nuanced rules to determine who can see or edit which parts of your document.": "Правила доступа дают вам возможность создавать детальные правила, определяющие, кто может просматривать или редактировать части вашего документа.", "Access rules give you the power to create nuanced rules to determine who can see or edit which parts of your document.": "Правила доступа дают вам возможность создавать детальные правила, определяющие, кто может просматривать или редактировать части вашего документа.",
"Use the 𝚺 icon to create summary (or pivot) tables, for totals or subtotals.": "Используйте 𝚺 значок для создания сводных таблиц для итогов или промежуточных итогов.", "Use the 𝚺 icon to create summary (or pivot) tables, for totals or subtotals.": "Используйте 𝚺 значок для создания сводных таблиц для итогов или промежуточных итогов.",
"relational": "реляционный" "relational": "реляционный"
},
"DescriptionConfig": {
"DESCRIPTION": "ОПИСАНИЕ"
} }
} }

@ -2138,6 +2138,62 @@ function testDocApi() {
assert.deepEqual(resp.data, { error: 'tableId parameter should be a string: undefined' }); assert.deepEqual(resp.data, { error: 'tableId parameter should be a string: undefined' });
}); });
it("GET /docs/{did}/download/table-schema serves table-schema-encoded document", async function() {
const resp = await axios.get(`${serverUrl}/api/docs/${docIds.TestDoc}/download/table-schema?tableId=Foo`, chimpy);
assert.equal(resp.status, 200);
const expected = {
format: "csv",
mediatype: "text/csv",
encoding: "utf-8",
dialect: {
delimiter: ",",
doubleQuote: true,
},
name: 'foo',
title: 'Foo',
schema: {
fields: [{
name: 'A',
type: 'string',
format: 'default',
}, {
name: 'B',
type: 'string',
format: 'default',
}]
}
};
assert.deepInclude(resp.data, expected);
const resp2 = await axios.get(resp.data.path, chimpy);
assert.equal(resp2.status, 200);
assert.equal(resp2.data, 'A,B\nSanta,1\nBob,11\nAlice,2\nFelix,22\n');
});
it("GET /docs/{did}/download/table-schema respects permissions", async function() {
// kiwi has no access to TestDoc
const resp = await axios.get(`${serverUrl}/api/docs/${docIds.TestDoc}/download/table-schema?tableId=Table1`, kiwi);
assert.equal(resp.status, 403);
assert.deepEqual(resp.data, {"error":"No view access"});
});
it("GET /docs/{did}/download/table-schema returns 404 if tableId is invalid", async function() {
const resp = await axios.get(
`${serverUrl}/api/docs/${docIds.TestDoc}/download/table-schema?tableId=MissingTableId`,
chimpy,
);
assert.equal(resp.status, 404);
assert.deepEqual(resp.data, { error: 'Table MissingTableId not found.' });
});
it("GET /docs/{did}/download/table-schema returns 400 if tableId is missing", async function() {
const resp = await axios.get(
`${serverUrl}/api/docs/${docIds.TestDoc}/download/table-schema`, chimpy);
assert.equal(resp.status, 400);
assert.deepEqual(resp.data, { error: 'tableId parameter should be a string: undefined' });
});
it("GET /docs/{did}/download/xlsx serves XLSX-encoded document", async function() { it("GET /docs/{did}/download/xlsx serves XLSX-encoded document", async function() {
const resp = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/download/xlsx?tableId=Table1`, chimpy); const resp = await axios.get(`${serverUrl}/api/docs/${docIds.Timesheets}/download/xlsx?tableId=Table1`, chimpy);
assert.equal(resp.status, 200); assert.equal(resp.status, 200);

Loading…
Cancel
Save