diff --git a/README.md b/README.md index 584ca938..2904b13d 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ see [our roadmap](https://github.com/gristlabs/grist-core/projects/1), drop a question in [our forum](https://community.getgrist.com), or browse [our extensive documentation](https://support.getgrist.com). + ## Using Grist If you just want a quick demo of Grist: @@ -223,6 +224,14 @@ Grist benefits its users: include Grist in your pipeline. And if a feature is missing, you can just take the source code and build on top of it. +## Sponsors + +

+ + + +

+ ## Reviews * [Grist on ProductHunt](https://www.producthunt.com/posts/grist-2) @@ -261,6 +270,7 @@ GRIST_LIST_PUBLIC_SITES | if set to true, sites shared with the public will be l GRIST_MANAGED_WORKERS | if set, Grist can assume that if a url targeted at a doc worker returns a 404, that worker is gone GRIST_MAX_UPLOAD_ATTACHMENT_MB | max allowed size for attachments (0 or empty for unlimited). GRIST_MAX_UPLOAD_IMPORT_MB | max allowed size for imports (except .grist files) (0 or empty for unlimited). +GRIST_OFFER_ALL_LANGUAGES | if set, all translated langauages are offered to the user (by default, only languages with a special 'good enough' key set are offered to user). GRIST_ORG_IN_PATH | if true, encode org in path rather than domain GRIST_PAGE_TITLE_SUFFIX | a string to append to the end of the `` in HTML documents. Defaults to `" - Grist"`. Set to `_blank` for no suffix at all. GRIST_PROXY_AUTH_HEADER | header which will be set by a (reverse) proxy webserver with an authorized users' email. This can be used as an alternative to a SAML service. See also GRIST_FORWARD_AUTH_HEADER. diff --git a/app/client/ui/CodeHighlight.ts b/app/client/ui/CodeHighlight.ts index 825ebb72..63b24ddd 100644 --- a/app/client/ui/CodeHighlight.ts +++ b/app/client/ui/CodeHighlight.ts @@ -17,7 +17,7 @@ export function buildHighlightedCode( ): HTMLElement { const highlighter = ace.acequire('ace/ext/static_highlight'); const PythonMode = ace.acequire('ace/mode/python').Mode; - const theme = ace.acequire('ace/theme/chrome'); + const aceTheme = ace.acequire('ace/theme/chrome'); const mode = new PythonMode(); return cssHighlightedCode( @@ -32,7 +32,7 @@ export function buildHighlightedCode( codeText = lines.slice(0, options.maxLines).join("\n") + " \u2026"; // Ellipsis } } - elem.innerHTML = highlighter.render(codeText, mode, theme, 1, true).html; + elem.innerHTML = highlighter.render(codeText, mode, aceTheme, 1, true).html; } else { elem.textContent = options.placeholder || ''; } diff --git a/app/server/localization.ts b/app/server/localization.ts index c97252ab..38b7c37c 100644 --- a/app/server/localization.ts +++ b/app/server/localization.ts @@ -1,3 +1,5 @@ +import {appSettings} from 'app/server/lib/AppSettings'; +import log from 'app/server/lib/log'; import {lstatSync, readdirSync, readFileSync} from 'fs'; import {createInstance, i18n} from 'i18next'; import {LanguageDetector} from 'i18next-http-middleware'; @@ -26,7 +28,7 @@ export function setupLocale(appRoot: string): i18n { throw new Error("Unrecognized resource file " + fileName); } supportedNamespaces.add(namespace); - preload.push([lang, namespace, fullPath]); + preload.push([namespace, lang, fullPath]); supportedLngs.add(lang); } @@ -51,15 +53,32 @@ export function setupLocale(appRoot: string): i18n { }).catch((err: any) => { // This should not happen, the promise should be resolved synchronously, without // any errors reported. - console.error("i18next failed unexpectedly", err); + log.error("i18next failed unexpectedly", err); }); if (errorDuringLoad) { - console.error('i18next failed to load', errorDuringLoad); + log.error('i18next failed to load', errorDuringLoad); throw errorDuringLoad; } // Load all files synchronously. - for(const [lng, ns, fullPath] of preload) { - instance.addResourceBundle(lng, ns, JSON.parse(readFileSync(fullPath, 'utf8'))); + // First sort by ns, which will put "client" first. That lets us check for a + // client key which, if absent, means the language should be ignored. + preload.sort((a, b) => a[0].localeCompare(b[0])); + const offerAll = appSettings.section('locale').flag('offerAllLanguages').readBool({ + envVar: 'GRIST_OFFER_ALL_LANGUAGES', + }); + const shouldIgnoreLng = new Set<string>(); + for(const [ns, lng, fullPath] of preload) { + const data = JSON.parse(readFileSync(fullPath, 'utf8')); + // If the "Translators: please ..." key in "App" has not been translated, + // ignore this language for this and later namespaces. + if (!offerAll && ns === 'client' && + !Object.keys(data.App || {}).some(key => key.includes('Translators: please'))) { + shouldIgnoreLng.add(lng); + log.debug(`skipping incomplete language ${lng} (set GRIST_OFFER_ALL_LANGUAGES if you want it)`); + } + if (!shouldIgnoreLng.has(lng)) { + instance.addResourceBundle(lng, ns, data); + } } return instance; } diff --git a/static/locales/de.client.json b/static/locales/de.client.json index 6d11f0b3..d9c04bbd 100644 --- a/static/locales/de.client.json +++ b/static/locales/de.client.json @@ -2,7 +2,7 @@ "ACUserManager": { "Enter email address": "E-Mail Adresse eingeben", "Invite new member": "Neues Mitglied einladen", - "We'll email an invite to {{email}}": "Eine Einladung wird per E-Mail an {{email}} gesendet" + "We'll email an invite to {{email}}": "Wir schicken eine Einladung zu {{email}}" }, "AccessRules": { "Add Column Rule": "Spaltenregel hinzufügen", @@ -382,7 +382,7 @@ "Hide column": "Spalte ausblenden", "Hide {{count}} columns": "{{count}} Spalten ausblenden", "Insert column to the {{to}}": "Spalte in {{to}} einfügen", - "More sort options ...": "Weitere Sortieroptionen…", + "More sort options ...": "Mehr Sortieroptionen…", "Rename column": "Spalte umbenennen", "Reset column": "Spalte zurücksetzen", "Reset entire column": "Gesamte Spalte zurücksetzen", @@ -575,7 +575,7 @@ "Save": "Speichern", "Select Widget": "Widget auswählen", "Series": "Serie", - "Sort & Filter": "Sortieren und Filtern", + "Sort & Filter": "Sortieren & Filtern", "TRANSFORM": "UMWANDELN", "Theme": "Thema", "WIDGET TITLE": "WIDGET-TITEL", @@ -690,7 +690,7 @@ "No Default Access": "Kein Standardzugriff", "None": "Keine", "Owner": "Eigentümer", - "View & Edit": "Anzeigen und Bearbeiten", + "View & Edit": "Anzeigen & Bearbeiten", "View Only": "Nur anzeigen", "Viewer": "Betrachter" }, @@ -715,7 +715,7 @@ "Unmark table On-Demand?": "Markierung der Tabelle auf-Befehl aufheben?" }, "ViewLayoutMenu": { - "Advanced Sort & Filter": "Erweitertes Sortieren und Filtern", + "Advanced Sort & Filter": "Erweitertes Sortieren & Filtern", "Copy anchor link": "Ankerlink kopieren", "Data selection": "Datenauswahl", "Delete record": "Datensatz löschen", @@ -1029,5 +1029,9 @@ }, "DescriptionConfig": { "DESCRIPTION": "BESCHREIBUNG" + }, + "PagePanels": { + "Close Creator Panel": "Ersteller-Panel schließen", + "Open Creator Panel": "Ersteller-Panel öffnen" } } diff --git a/static/locales/en.client.json b/static/locales/en.client.json index 755e0d9c..5f5a7db6 100644 --- a/static/locales/en.client.json +++ b/static/locales/en.client.json @@ -965,5 +965,9 @@ }, "DescriptionConfig": { "DESCRIPTION": "DESCRIPTION" + }, + "PagePanels": { + "Close Creator Panel": "Close Creator Panel", + "Open Creator Panel": "Open Creator Panel" } } diff --git a/static/locales/es.client.json b/static/locales/es.client.json index edc0ca4c..057505dd 100644 --- a/static/locales/es.client.json +++ b/static/locales/es.client.json @@ -685,7 +685,7 @@ "ACUserManager": { "Enter email address": "Introduzca la dirección de correo electrónico", "Invite new member": "Invitar nuevo miembro", - "We'll email an invite to {{email}}": "Se enviará una invitación por correo electrónico a {{email}}" + "We'll email an invite to {{email}}": "Enviaremos una invitación por correo electrónico a {{email}}" }, "ViewAsDropdown": { "View As": "Ver como", @@ -1019,5 +1019,9 @@ }, "DescriptionConfig": { "DESCRIPTION": "DESCRIPCIÓN" + }, + "PagePanels": { + "Close Creator Panel": "Cerrar el panel de creación", + "Open Creator Panel": "Abrir el panel de creación" } } diff --git a/static/locales/fa.client.json b/static/locales/fa.client.json new file mode 100644 index 00000000..1d8828aa --- /dev/null +++ b/static/locales/fa.client.json @@ -0,0 +1,42 @@ +{ + "ACUserManager": { + "Invite new member": "دعوت از عضو جدید", + "Enter email address": "آدرس رایانامه(ایمیل) را وارد کنید", + "We'll email an invite to {{email}}": "ما دعوت‌نامه‌ای به آدرس {{email}} ارسال خواهیم کرد" + }, + "AccessRules": { + "Add Column Rule": "افزودن قاعده ستون", + "Add Default Rule": "افزودن قاعده پیش‌فرض", + "Add Table Rules": "افزودن قواعد جدول", + "Add User Attributes": "افزودن خصیصه‌های کاربر", + "Allow everyone to view Access Rules.": "به همه اجازه مشاهده قواعد دسترسی را بدهید.", + "Attribute name": "نام خصیصه", + "Attribute to Look Up": "خصیصه برای جستجو", + "Checking...": "در حال بررسی…", + "Condition": "شرط", + "Default Rules": "قواعد پیش‌فرض", + "Delete Table Rules": "حذف قواعد جدول", + "Enter Condition": "وارد کردن شرط", + "Everyone": "همه", + "Everyone Else": "دیگران", + "Invalid": "نامعتبر", + "Permission to access the document in full when needed": "اجازه دسترسی کامل به سند وقتی نیاز شد", + "Permission to view Access Rules": "اجازه مشاهده قواعد دسترسی", + "Permissions": "اجازه‌ها", + "Remove column {{- colId }} from {{- tableId }} rules": "حذف ستون {{- colId }} از قواعد {{- tableId }}", + "Remove {{- tableId }} rules": "حذف قواعد {{- tableId }}", + "Remove {{- name }} user attribute": "حذف خصیصه کاربر {{- name }}", + "Reset": "بازنشانی", + "Rules for table ": "قواعد برای جدول ", + "Save": "ذخیره", + "Saved": "ذخیره شد", + "Special Rules": "قواعد ویژه", + "Type a message...": "پیامی بنویسید…", + "User Attributes": "خصیصه‌های کاربر", + "View As": "مشاهده به عنوان", + "Seed rules": "قواعد دانه(Seed)", + "Allow everyone to copy the entire document, or view it in full in fiddle mode.\nUseful for examples and templates, but not for sensitive data.": "به همه اجازه دهید کل سند را کپی کنند، یا آن را به صورت کامل در حالت ور رفتن مشاهده کنند.\nمفید برای نمونه‌ها و قالب‌ها، اما نه برای داده‌های حساس.", + "Lookup Column": "جستجوی ستون", + "Lookup Table": "جستجوی جدول" + } +} diff --git a/static/locales/pl.client.json b/static/locales/pl.client.json index 5ef5c86d..ff24d76f 100644 --- a/static/locales/pl.client.json +++ b/static/locales/pl.client.json @@ -517,7 +517,7 @@ "ROW STYLE": "STYL WIERSZA", "Row Style": "Styl wiersza", "SELECT BY": "WYBIERZ WEDŁUG", - "TRANSFORM": "PRZEKSZTAŁCAĆ", + "TRANSFORM": "ZMIANA DANYCH", "Theme": "Motyw", "WIDGET TITLE": "TYTUŁ WIDŻETU", "Series_one": "Seria", @@ -601,7 +601,7 @@ "Apply": "Zastosuj", "Cancel": "Anuluj", "Preview": "Podgląd", - "Revise": "Zrewidować", + "Revise": "Popraw", "Update formula (Shift+Enter)": "Aktualizacja formuły (Shift+Enter)" }, "UserManagerModel": { @@ -687,7 +687,7 @@ "Override widget title": "Zastąp tytuł widżetu", "WIDGET TITLE": "TYTUŁ WIDŻETU", "Provide a table name": "Podaj nazwę tabeli", - "Cancel": "Anulować", + "Cancel": "Anuluj", "Save": "Zapisz" }, "breadcrumbs": { @@ -726,16 +726,16 @@ "Upgrade now": "Uaktualnij teraz", "Any": "Dowolny", "Integer": "Liczba całkowita", - "Toggle": "Przełączać", + "Toggle": "Przełącznik", "Date": "Data", "Choice": "Wybór", "Choice List": "Lista wyboru", "Reference": "Odnośnik", - "Reference List": "Lista referencyjna", + "Reference List": "Lista odnośników", "Select fields": "Wybierz pola", - "Numeric": "Numeryczny", + "Numeric": "Liczba", "Text": "Tekst", - "DateTime": "DataGodzina", + "DateTime": "Data i godzina", "Attachment": "Załącznik" }, "modals": { @@ -765,7 +765,7 @@ "Example Users": "Przykładowi Użytkownicy" }, "TypeTransform": { - "Cancel": "Anulować", + "Cancel": "Anuluj", "Preview": "Podgląd", "Revise": "Zrewidować", "Apply": "Zastosuj", @@ -867,7 +867,7 @@ "Open": "Otwórz", "Reply to a comment": "Odpowiedz na komentarz", "Show resolved comments": "Pokaż rozwiązane komentarze", - "Cancel": "Anulować", + "Cancel": "Anuluj", "Comment": "Komentarz", "Edit": "Edytuj", "Marked as resolved": "Oznaczone jako rozwiązane", diff --git a/static/locales/pt_BR.client.json b/static/locales/pt_BR.client.json index 226d5632..f0b872ed 100644 --- a/static/locales/pt_BR.client.json +++ b/static/locales/pt_BR.client.json @@ -2,7 +2,7 @@ "ACUserManager": { "Enter email address": "Digite o endereço de e-mail", "Invite new member": "Convidar novo membro", - "We'll email an invite to {{email}}": "Um convite será enviado por e-mail para {{email}}" + "We'll email an invite to {{email}}": "Enviaremos um convite por e-mail para {{email}}" }, "AccessRules": { "Add Column Rule": "Adicionar Regra de Coluna", @@ -690,7 +690,7 @@ "No Default Access": "Sem Acesso Padrão", "None": "Nenhum", "Owner": "Proprietário", - "View & Edit": "Ver e Editar", + "View & Edit": "Ver & Editar", "View Only": "Somente Ver", "Viewer": "Observador" }, @@ -715,7 +715,7 @@ "Unmark table On-Demand?": "Desmarcar a tabela Sob-Demanda?" }, "ViewLayoutMenu": { - "Advanced Sort & Filter": "Classificação e filtragem avançadas", + "Advanced Sort & Filter": "Ordenar & filtrar avançados", "Copy anchor link": "Copiar o link de ancoragem", "Data selection": "Seleção de dados", "Delete record": "Excluir registro", @@ -1029,5 +1029,9 @@ }, "DescriptionConfig": { "DESCRIPTION": "DESCRIÇÃO" + }, + "PagePanels": { + "Open Creator Panel": "Abrir o Painel do Criador", + "Close Creator Panel": "Fechar Painel do Criador" } } diff --git a/static/locales/ru.client.json b/static/locales/ru.client.json index 812cd93d..ba71e94a 100644 --- a/static/locales/ru.client.json +++ b/static/locales/ru.client.json @@ -39,7 +39,7 @@ "ACUserManager": { "Enter email address": "Введите адрес электронной почты", "Invite new member": "Пригласить нового участника", - "We'll email an invite to {{email}}": "Приглашение будет отправлено на электронную почту {{email}}" + "We'll email an invite to {{email}}": "Мы вышлем приглашение на {{email}}" }, "AccountPage": { "API": "API", @@ -470,7 +470,7 @@ "Access Details": "Детали доступа", "Create Empty Document": "Создать пустой документ", "Delete": "Удалить", - "Examples & Templates": "Примеры и Шаблоны", + "Examples & Templates": "Примеры & Шаблоны", "Rename": "Переименовать", "Delete {{workspace}} and all included documents?": "Удалить {{workspace}} и все прилагаемые документы?", "Trash": "Корзина", @@ -505,7 +505,9 @@ "Show column {{- label}}": "Показать столбец {{- label}}", "Sort": "Сортировать", "Unfreeze {{count}} columns_other": "Открепить {{count}} столбцов", - "Unfreeze {{count}} columns_one": "Открепить этот столбец" + "Unfreeze {{count}} columns_one": "Открепить этот столбец", + "Insert column to the left": "Вставить столбец слева", + "Insert column to the right": "Вставить столбец справа" }, "FilterBar": { "SearchColumns": "Столбцы поиска", @@ -591,7 +593,7 @@ "Save": "Сохранить", "Select Widget": "Выберите виджет", "Series_one": "Ряд", - "Sort & Filter": "Сортировка и Фильтрация", + "Sort & Filter": "Сортировка & Фильтрация", "TRANSFORM": "ПРЕОБРАЗОВАНИЕ", "WIDGET TITLE": "ЗАГОЛОВОК ВИДЖЕТА", "You do not have edit access to this document": "У вас нет прав на редактирование этого документа", @@ -649,7 +651,7 @@ "UserManagerModel": { "In Full": "Полный", "None": "Без доступа", - "View & Edit": "Просмотр и Редактирование", + "View & Edit": "Просмотр & Редактирование", "Viewer": "Наблюдатель", "Owner": "Владелец", "No Default Access": "Нет доступа по умолчанию", @@ -672,7 +674,7 @@ "Compact": "Компактная" }, "ViewLayoutMenu": { - "Advanced Sort & Filter": "Расширенная Сортировка и Фильтрация", + "Advanced Sort & Filter": "Расширенная Сортировка & Фильтрация", "Delete record": "Удалить запись", "Delete widget": "Удалить виджет", "Download as XLSX": "Скачать как XLSX", @@ -963,5 +965,9 @@ }, "DescriptionConfig": { "DESCRIPTION": "ОПИСАНИЕ" + }, + "PagePanels": { + "Close Creator Panel": "Закрыть Панель Создателя", + "Open Creator Panel": "Открыть Панель Создателя" } } diff --git a/static/locales/zh_Hans.client.json b/static/locales/zh_Hans.client.json index 603cf76d..9f421b29 100644 --- a/static/locales/zh_Hans.client.json +++ b/static/locales/zh_Hans.client.json @@ -448,7 +448,9 @@ "Download as XLSX": "下载为 XLSX", "Edit Card Layout": "编辑卡片布局", "Show raw data": "显示原始数据", - "Widget options": "小部件选项" + "Widget options": "小部件选项", + "Add to page": "添加到页面", + "Collapse widget": "折叠小部件" }, "ViewSectionMenu": { "Save": "保存", @@ -465,15 +467,19 @@ "Cannot drop items into Hidden Fields": "无法将项目放入隐藏字段", "Clear": "清除", "Hidden Fields cannot be reordered": "隐藏字段无法重新排序", - "Select All": "全选" + "Select All": "全选", + "Hide {{label}}": "隐藏 {{label}}", + "Hidden {{label}}": "隐藏 {{label}}", + "Show {{label}}": "显示 {{label}}", + "Visible {{label}}": "可见 {{label}}" }, "WelcomeQuestions": { - "IT & Technology": "资讯科技", + "IT & Technology": "IT和技术", "Sales": "销售量", "What brings you to Grist? Please help us serve you better.": "是什么让你来到格里斯特? 请帮助我们更好地为您服务。", "Education": "教育", "Finance & Accounting": "财务与会计", - "HR & Management": "人力资源与管理", + "HR & Management": "人力资源和管理", "Marketing": "营销", "Media Production": "媒体制作", "Other": "其他", @@ -555,7 +561,7 @@ "Ok": "好的" }, "DocumentUsage": { - "Attachments Size": "附件尺寸", + "Attachments Size": "附件大小", "Contact the site owner to upgrade the plan to raise limits.": "联系站点所有者以升级计划以提高限制。", "Data Size": "数据大小", "For higher limits, ": "对于更高的限制, ", @@ -658,7 +664,9 @@ "Unfreeze {{count}} columns_one": "解冻此列", "Unfreeze all columns": "解冻所有列", "Unfreeze {{count}} columns_other": "解冻 {{count}} 列", - "Show column {{- label}}": "显示列 {{- label}}" + "Show column {{- label}}": "显示列 {{- label}}", + "Insert column to the left": "在左侧插入列", + "Insert column to the right": "在右侧插入列" }, "GristDoc": { "Added new linked section to view {{viewName}}": "添加了新的链接部分以查看 {{viewName}}", @@ -801,7 +809,19 @@ "menus": { "* Workspaces are available on team plans. ": "* 工作区在团队计划中可用。 ", "Select fields": "选择字段", - "Upgrade now": "现在升级" + "Upgrade now": "现在升级", + "Reference List": "参考列表", + "Toggle": "切换", + "Date": "日期", + "DateTime": "日期时间", + "Choice": "选择", + "Choice List": "选择列表", + "Reference": "参考", + "Attachment": "附件", + "Any": "任何", + "Numeric": "数值", + "Text": "文本", + "Integer": "整数" }, "errorPages": { "Add account": "新增帐户", @@ -887,7 +907,7 @@ "Unable to finish saving edited cell": "无法完成保存编辑的单元格" }, "HyperLinkEditor": { - "[link label] url": "[链接标签] url" + "[link label] url": "[链接标签] URL" }, "NumericTextBox": { "Currency": "货币", @@ -942,5 +962,12 @@ "Clicking {{EyeHideIcon}} in each cell hides the field from this view without deleting it.": "单击每个单元格中的 {{EyeHideIcon}} 可隐藏此视图中的字段而不将其删除。", "Editing Card Layout": "编辑卡片布局", "Formulas that trigger in certain cases, and store the calculated value as data.": "在某些情况下触发的公式,并将计算值存储为数据。" + }, + "DescriptionConfig": { + "DESCRIPTION": "描述" + }, + "PagePanels": { + "Close Creator Panel": "关闭创作者面板", + "Open Creator Panel": "打开创作者面板" } } diff --git a/yarn.lock b/yarn.lock index 9db3e13c..71a16bcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5698,13 +5698,6 @@ marked@4.2.12: resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.12.tgz#d69a64e21d71b06250da995dcd065c11083bebb5" integrity sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw== -matcher@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz" - integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== - dependencies: - escape-string-regexp "^4.0.0" - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz"