From 2f9010977525978690b2c3fb67fb5919557947b8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 09:52:55 -0500 Subject: [PATCH 01/10] automated update to translation keys (#1325) Co-authored-by: Paul's Grist Bot --- static/locales/en.client.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/locales/en.client.json b/static/locales/en.client.json index c1350e5b95..476eadbcb5 100644 --- a/static/locales/en.client.json +++ b/static/locales/en.client.json @@ -1724,7 +1724,8 @@ "An activation key is used to run Grist Enterprise after a trial period\nof 30 days has expired. Get an activation key by [signing up for Grist\nEnterprise]({{signupLink}}). You do not need an activation key to run\nGrist Core.\n\nLearn more in our [Help Center]({{helpCenter}}).": "An activation key is used to run Grist Enterprise after a trial period\nof 30 days has expired. Get an activation key by [signing up for Grist\nEnterprise]({{signupLink}}). You do not need an activation key to run\nGrist Core.\n\nLearn more in our [Help Center]({{helpCenter}}).", "Disable Grist Enterprise": "Disable Grist Enterprise", "Enable Grist Enterprise": "Enable Grist Enterprise", - "Grist Enterprise is **enabled**.": "Grist Enterprise is **enabled**." + "Grist Enterprise is **enabled**.": "Grist Enterprise is **enabled**.", + "An activation key is used to run Grist Enterprise after a trial period\nof 30 days has expired. Get an activation key by [contacting us]({{contactLink}}) today. You do\nnot need an activation key to run Grist Core.\n\nLearn more in our [Help Center]({{helpCenter}}).": "An activation key is used to run Grist Enterprise after a trial period\nof 30 days has expired. Get an activation key by [contacting us]({{contactLink}}) today. You do\nnot need an activation key to run Grist Core.\n\nLearn more in our [Help Center]({{helpCenter}})." }, "ViewLayout": { "Delete": "Delete", From c311b008652b07337164ef3c25359bff0dd019e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BD=90=E5=AD=90=E7=90=AA?= Date: Wed, 11 Dec 2024 15:44:32 +0000 Subject: [PATCH 02/10] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 78.6% (1150 of 1463 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/zh_Hans/ --- static/locales/zh_Hans.client.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/static/locales/zh_Hans.client.json b/static/locales/zh_Hans.client.json index 415ea42652..4aed0e931e 100644 --- a/static/locales/zh_Hans.client.json +++ b/static/locales/zh_Hans.client.json @@ -624,7 +624,10 @@ "Ok": "好的", "Manage Webhooks": "管理 Webhooks", "Webhooks": "Webhook", - "API Console": "API 控制台" + "API Console": "API 控制台", + "Find slow formulas": "查找慢公式", + "Data Engine": "数据引擎", + "Document ID": "文档 ID" }, "DocumentUsage": { "Attachments Size": "附件大小", From d107464778e70b1bbea6060e7e99b3152cbcffc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BD=90=E5=AD=90=E7=90=AA?= Date: Wed, 11 Dec 2024 04:48:14 +0000 Subject: [PATCH 03/10] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (2 of 2 strings) Translation: Grist/server Translate-URL: https://hosted.weblate.org/projects/grist/server/zh_Hans/ --- static/locales/zh_Hans.server.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/static/locales/zh_Hans.server.json b/static/locales/zh_Hans.server.json index 0967ef424b..0423ce1008 100644 --- a/static/locales/zh_Hans.server.json +++ b/static/locales/zh_Hans.server.json @@ -1 +1,8 @@ -{} +{ + "oidc": { + "emailNotVerifiedError": "请向身份提供商验证您的电子邮件,然后重新登录。" + }, + "sendAppPage": { + "Loading": "加载中" + } +} From b831110a2aeed7cc921c85d13a2b187d0e4ece0f Mon Sep 17 00:00:00 2001 From: VojtechKrystek Date: Thu, 12 Dec 2024 21:13:49 +0000 Subject: [PATCH 04/10] Translated using Weblate (Czech) Currently translated at 7.1% (108 of 1515 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/cs/ --- static/locales/cs.client.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/locales/cs.client.json b/static/locales/cs.client.json index c9261b6f9b..d8584edba4 100644 --- a/static/locales/cs.client.json +++ b/static/locales/cs.client.json @@ -34,7 +34,8 @@ "Seed rules": "Seed pravidla", "Save": "Ulož", "Rules for table ": "Pravidla pro tabulku ", - "Special Rules": "Speciální Pravidla" + "Special Rules": "Speciální Pravidla", + "This default should be changed if editors' access is to be limited. ": "Toto výchozí nastavení by mělo být změněno, pokud má být přístup editorů omezen. " }, "ACUserManager": { "Invite new member": "Pozvat nového uživatele", From dc385a3934756af74df86e3f8fdca940ed487ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BD=90=E5=AD=90=E7=90=AA?= Date: Fri, 13 Dec 2024 02:13:53 +0000 Subject: [PATCH 05/10] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 88.2% (1337 of 1515 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/zh_Hans/ --- static/locales/zh_Hans.client.json | 255 ++++++++++++++++++++++++++--- 1 file changed, 230 insertions(+), 25 deletions(-) diff --git a/static/locales/zh_Hans.client.json b/static/locales/zh_Hans.client.json index 4aed0e931e..ae2ee29edb 100644 --- a/static/locales/zh_Hans.client.json +++ b/static/locales/zh_Hans.client.json @@ -259,7 +259,8 @@ "Workspace not found": "找不到工作区", "You are on the {{siteName}} site. You also have access to the following sites:": "您在 {{siteName}} 网站上。 您还可以访问以下站点:", "You may delete a workspace forever once it has no documents in it.": "一旦其中没有文档,您可以永远删除工作区。", - "Delete {{name}}": "删除 {{name}}" + "Delete {{name}}": "删除 {{name}}", + "personal site": "个人站点" }, "DocPageModel": { "Add Empty Table": "添加空表", @@ -341,7 +342,9 @@ "Welcome to Grist, {{- name}}!": "欢迎来到 Grist,{{- name}}!", "Visit our {{link}} to learn more about Grist.": "访问我们的{{link}} ,了解有关 Grist 的更多信息。", "Sign in": "登录", - "To use Grist, please either sign up or sign in.": "要使用 Grist,请注册或登录。" + "To use Grist, please either sign up or sign in.": "要使用 Grist,请注册或登录。", + "Only show documents": "仅显示文档", + "Learn more in our {{helpCenterLink}}.": "在我们的 {{helpCenterLink}} 中了解更多信息。" }, "HomeLeftPane": { "Delete": "删除", @@ -357,7 +360,9 @@ "Rename": "重命名", "Workspace will be moved to Trash.": "工作区将移至回收站。", "Workspaces": "工作区", - "Tutorial": "教程" + "Tutorial": "教程", + "Grist Resources": "Grist 资源", + "Terms of service": "服务条款" }, "MakeCopyMenu": { "Cancel": "取消", @@ -455,7 +460,11 @@ "Field title": "字段标题", "Submission": "提交", "Enter redirect URL": "输入重定向 URL", - "Select a field in the form widget to configure.": "在表单小部件中选中要配置的字段。" + "Select a field in the form widget to configure.": "在表单小部件中选中要配置的字段。", + "Display button": "显示按钮", + "Default field value": "默认字段值", + "Table column name": "表格的列名", + "Submit another response": "提交其他响应" }, "RowContextMenu": { "Insert row below": "在下方插入行", @@ -465,7 +474,8 @@ "Duplicate rows_other": "重复行", "Insert row": "插入行", "Insert row above": "在上方插入行", - "View as card": "以卡片形式查看" + "View as card": "以卡片形式查看", + "Use as table headers": "作为表头使用" }, "UserManagerModel": { "In Full": "在全", @@ -559,7 +569,9 @@ "Rule must return True or False": "规则必须返回 True 或 False", "Add another rule": "添加另一条规则", "Add conditional style": "添加条件样式", - "Error in style rule": "样式规则错误" + "Error in style rule": "样式规则错误", + "Conditional Style": "条件样式", + "IF...": "如果..." }, "CurrencyPicker": { "Invalid currency": "货币无效" @@ -627,7 +639,38 @@ "API Console": "API 控制台", "Find slow formulas": "查找慢公式", "Data Engine": "数据引擎", - "Document ID": "文档 ID" + "Document ID": "文档 ID", + "Formula timer": "公式计时器", + "Start timing": "开始计时", + "Stop timing...": "停止计时...", + "You can make changes to the document, then stop timing to see the results.": "您可以对文档进行更改,然后停止计时以查看结果。", + "Template": "模版", + "Regular document": "常规文档", + "Document automatically opens in {{fiddleModeDocUrl}}. Anyone may edit, which will create a new unsaved copy.": "文档自动在{{fiddleModeDocUrl}}中打开。任何人都可以编辑,这将创建一个新的未保存的副本。", + "Python": "Python", + "Reload": "重新加载", + "Time Zone": "时区", + "python2 (legacy)": "python2(过时)", + "python3 (recommended)": "python3(推荐)", + "Try API calls from the browser": "尝试从浏览器调用API", + "Cancel": "取消", + "Force reload the document while timing formulas, and show the result.": "在计算公式时强制重新加载文档,并显示结果。", + "Template mode": "模板模式", + "Confirm change": "确认更改", + "Edit": "编辑", + "Change nature of document": "更改文件性质", + "Normal document behavior. All users work on the same copy of the document.": "正常文档行为。所有用户都使用文档的同一副本。", + "This will perform a hard reload of the data engine. This may help if the data engine is stuck in an infinite loop, is indefinitely processing the latest change, or has crashed. No data will be lost, except possibly currently pending actions.": "这将执行数据引擎的强制重新加载。如果数据引擎陷入无限循环、无限期地处理最新更改或已崩溃,这可能会有所帮助。除可能当前待处理的操作外,不会丢失任何数据。", + "Once you start timing, Grist will measure the time it takes to evaluate each formula. This allows diagnosing which formulas are responsible for slow performance when a document is first opened, or when a document responds to changes.": "一旦你开始计时,Grist将测量评估每个公式所需的时间。这有助于诊断在首次打开文档或文档响应更改时,哪些公式导致了性能降低。", + "Change document type": "更改文档类型", + "Regular": "常规", + "Tutorial": "教程", + "Document automatically opens as a user-specific copy.": "文档自动作为用户特定副本打开。", + "Python version used": "Python版本", + "Reload data engine": "重新加载数据引擎", + "Reload data engine?": "重新加载数据引擎?", + "Only available to document editors": "仅适用于文档编辑者", + "Only available to document owners": "仅适用于文档所有者" }, "DocumentUsage": { "Attachments Size": "附件大小", @@ -764,7 +807,18 @@ "Attachment": "附件", "Add column": "添加列", "Numeric": "数值", - "Detect duplicates in...": "在……中检测重复" + "Detect duplicates in...": "在……中检测重复", + "Created at": "创建于", + "Toggle": "切换", + "Reference": "引用", + "DateTime": "日期时间", + "Choice": "选择", + "Choice List": "选择列表", + "Reference List": "引用列表", + "Created by": "创建者", + "Last updated at": "最后更新于", + "Last updated by": "最后更新者", + "Any": "任何" }, "GristDoc": { "Added new linked section to view {{viewName}}": "添加了新的链接部分以查看 {{viewName}}", @@ -859,7 +913,8 @@ "Comma Separated Values (.csv)": "逗号分隔值 (.csv)", "Tab Separated Values (.tsv)": "制表符分隔值 (.tsv)", "Export as...": "导出为……", - "Microsoft Excel (.xlsx)": "Microsoft Excel (.xlsx)" + "Microsoft Excel (.xlsx)": "Microsoft Excel (.xlsx)", + "DOO Separated Values (.dsv)": "分隔符分隔值 (.dsv)" }, "SiteSwitcher": { "Create new team site": "创建新的团队网站", @@ -893,7 +948,8 @@ "API Console": "API 控制台" }, "TopBar": { - "Manage Team": "管理团队" + "Manage Team": "管理团队", + "Manage team": "管理团队" }, "TriggerFormulas": { "Any field": "任何领域", @@ -944,7 +1000,10 @@ "Numeric": "数值", "Text": "文本", "Integer": "整数", - "Search columns": "搜索列" + "Search columns": "搜索列", + "By Name": "按名称", + "By Date Modified": "按修改日期", + "Custom": "自定义" }, "errorPages": { "Add account": "新增帐户", @@ -969,7 +1028,10 @@ "Sign up": "注册", "An unknown error occurred.": "发生未知错误。", "Form not found": "未找到表单", - "Build your own form": "创建自己的表单" + "Build your own form": "创建自己的表单", + "Sign-in failed{{suffix}}": "登录失败{{suffix}}", + "Powered by": "驱动自", + "Failed to log in.{{separator}}Please try again or contact support.": "登录失败。{{separator}}请重试或联系支持人员。" }, "modals": { "Cancel": "取消", @@ -983,7 +1045,8 @@ "Don't show again.": "不再显示。", "Are you sure you want to delete these records?": "确定要删除这些纪录吗?", "Don't show again": "不再显示", - "Don't show tips": "不要显示提示" + "Don't show tips": "不要显示提示", + "Undo to restore": "撤消以恢复" }, "pages": { "Duplicate Page": "复制页面", @@ -1061,7 +1124,9 @@ "Number Format": "数字格式", "Field Format": "字段格式", "Text": "文本", - "Spinner": "下拉列表" + "Spinner": "下拉列表", + "max": "最大", + "min": "最小" }, "Reference": { "CELL FORMAT": "单元格格式", @@ -1126,7 +1191,17 @@ "Use reference columns to relate data in different tables.": "使用参考列来关联不同表格中的数据。", "Forms are here!": "表单来了!", "Learn more": "了解更多", - "Build simple forms right in Grist and share in a click with our new widget. {{learnMoreButton}}": "直接在 Grist 中构建简单的表单,并使用我们全新的小部件一键共享。{{learnMoreButton}}" + "Build simple forms right in Grist and share in a click with our new widget. {{learnMoreButton}}": "直接在 Grist 中构建简单的表单,并使用我们全新的小部件一键共享。{{learnMoreButton}}", + "Example: {{example}}": "示例: {{example}}", + "To allow multiple assignments, change the type of the Reference column to Reference List.": "若要允许多个值,请将“引用”列的类型更改为“引用列表”。", + "These rules are applied after all column rules have been processed, if applicable.": "这些规则在处理完所有列规则后应用(如果适用)。", + "Creates a reverse column in target table that can be edited from either end.": "在目标表中创建一个反向列,该列可以从任意一端进行编辑。", + "This limitation occurs when one end of a two-way reference is configured as a single Reference.": "当双向引用的一端配置为单个引用时,就会出现此限制。", + "Filter displayed dropdown values with a condition.": "过滤器显示带有条件的下拉值。", + "Community widgets are created and maintained by Grist community members.": "社区小部件由Grist社区成员创建和维护。", + "Two-way references are not currently supported for Formula or Trigger Formula columns": "公式或触发器公式列当前不支持双向引用", + "This limitation occurs when one column in a two-way reference has the Reference type.": "当双向引用中的一列具有“引用”类型时,会出现此限制。", + "To allow multiple assignments, change the referenced column's type to Reference List.": "若要允许多个值,请将引用列的类型更改为“引用列表”。" }, "DescriptionConfig": { "DESCRIPTION": "描述" @@ -1196,7 +1271,19 @@ "Clear Queue": "清除队列", "Webhook Settings": "Webhook设置", "URL": "URL", - "Status": "状态" + "Status": "状态", + "Enabled": "启用", + "Name": "名称", + "Event Types": "事件类型", + "Removed webhook.": "已删除的 webhook。", + "Table": "表", + "Webhook Id": "Webhook Id", + "Filter for changes in these columns (semicolon-separated ids)": "筛选这些列中的更改(以分号分隔的id)", + "Cleared webhook queue.": "已清除webhook队列。", + "Columns to check when update (separated by ;)": "更新时要检查的列(以 ; 分隔)", + "Sorry, not all fields can be edited.": "抱歉,并非所有字段都可编辑。", + "Header Authorization": "Header Authorization", + "Memo": "备忘录" }, "WelcomeSitePicker": { "Welcome back": "欢迎回来", @@ -1331,13 +1418,24 @@ "Unpublish your form?": "要取消发布表单吗?", "Unpublish": "取消发布", "Publish": "发布", - "Publish your form?": "要发布表单吗?" + "Publish your form?": "要发布表单吗?", + "Embed this form": "嵌入此表单", + "View": "视图" }, "FormConfig": { "Field Rules": "字段规则", "Field Format": "字段格式", "Field rules": "字段规则", - "Required field": "必填字段" + "Required field": "必填字段", + "Ascending": "升序", + "Options Alignment": "选项对齐", + "Select": "选择", + "Vertical": "垂直", + "Horizontal": "水平的", + "Descending": "降序", + "Options Sort Order": "选项排序顺序", + "Default": "默认的", + "Radio": "单选" }, "DateRangeOptions": { "Last 30 days": "最近 30 天", @@ -1345,7 +1443,9 @@ "Last 7 days": "最近 7 天", "This week": "本周", "This year": "今年", - "Today": "今天" + "Today": "今天", + "Next 7 days": "未来7天", + "Last Week": "上周" }, "AdminPanel": { "Support Grist Labs on GitHub": "在 GitHub 上支持 Grist Labs", @@ -1371,7 +1471,38 @@ "Grist allows for very powerful formulas, using Python. We recommend setting the environment variable GRIST_SANDBOX_FLAVOR to gvisor if your hardware supports it (most will), to run formulas in each document within a sandbox isolated from other documents and isolated from the network.": "Grist可以利用Python支持非常强大的公式。如果您的硬件支持的话(大多数都支持),我们建议将环境变量 GRIST_SANDBOX_FLAVOR 设置为 gvisor,这样就可以在沙盒中运行文档中的公式,沙盒将与其他文档隔离、并与网络隔离。", "unknown": "未知", "Current version of Grist": "Grist的当前版本", - "Support Grist": "支持 Grist" + "Support Grist": "支持 Grist", + "Telemetry": "遥测", + "Sandboxing": "沙盒", + "Error checking for updates": "检查更新时出错", + "Check failed.": "检查失败。", + "Check succeeded.": "检查成功。", + "Current authentication method": "当前认证方式", + "No fault detected.": "未检测到故障。", + "Or, as a fallback, you can set: {{bootKey}} in the environment and visit: {{url}}": "或者,作为后备方案,您可以在环境中设置:{{bootKey}},然后访问:{{url}}", + "Results": "结果", + "You do not have access to the administrator panel.\nPlease log in as an administrator.": "您无权访问管理员面板。\n请以管理员身份登录。", + "Details": "详情", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "Grist 允许配置不同类型的身份验证,包括 SAML 和 OIDC。如果 Grist 可通过网络访问或供多人使用,我们建议启用其中一种。", + "New, Enterprise": "新,企业版", + "{{firstDestinationName}} + {{- remainingDestinationsCount}} more": "{{firstDestinationName}} + {{- remainingDestinationsCount}} 更多", + "Authentication": "认证", + "Notes": "注意", + "Audit Logs": "审计日志", + "Contact us": "联系我们", + "Off": "关闭", + "Grist signs user session cookies with a secret key. Please set this key via the environment variable GRIST_SESSION_SECRET. Grist falls back to a hard-coded default when it is not set. We may remove this notice in the future since session IDs have been updated to be inherently cryptographically secure.": "Grist 使用密钥对用户会话 cookie 进行签名。请通过环境变量 GRIST_SESSION_SECRET 设置此密钥。当它未设置时,Grist 会回退到硬编码的默认值。由于自 v1.1.16 以来生成的会话 ID 本身就具有密码学安全性,因此我们可能会在未来删除此通知。", + "Key to sign sessions with": "密钥用于会话签署", + "Session Secret": "会话机密", + "Sandbox settings for data engine": "数据引擎的沙盒设置", + "Self Checks": "自检", + "Grist signs user session cookies with a secret key. Please set this key via the environment variable GRIST_SESSION_SECRET. Grist falls back to a hard-coded default when it is not set. We may remove this notice in the future as session IDs generated since v1.1.16 are inherently cryptographically secure.": "Grist 使用密钥对用户会话 cookie 进行签名。请通过环境变量 GRIST_SESSION_SECRET 设置此密钥。当未设置时,Grist 会回退到硬编码的默认值。由于自 v1.1.16 以来生成的会话 ID 本身就具有密码学安全性,因此我们可能会在未来删除此通知。", + "Enable Grist Enterprise": "启用Grist企业版", + "Enterprise": "企业", + "checking": "检查中", + "Administrator Panel Unavailable": "管理员面板不可用", + "No information available": "无可用信息", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "Grist 允许配置不同类型的身份验证,包括 SAML 和 OIDC。如果 Grist 可通过网络访问或供多人使用,我们建议启用其中一种。" }, "Menu": { "Copy": "拷贝", @@ -1381,13 +1512,27 @@ "Paragraph": "段落", "Cut": "剪切", "Insert question above": "在上方插入问题", - "Separator": "分割线" + "Separator": "分割线", + "Columns": "列", + "Unmapped fields": "未映射的字段", + "Building blocks": "构建区块" }, "Toggle": { "Field Format": "字段格式" }, "CreateTeamModal": { - "Cancel": "取消" + "Cancel": "取消", + "Team name": "团队名称", + "Team name is required": "团队名称是必填项", + "Choose a name and url for your team site": "为你的团队站点选择名称和url", + "Create site": "创建站点", + "Go to your site": "转到您的站点", + "Work as a Team": "作为一个团队工作", + "Billing is not supported in grist-core": "grist-core不支持计费", + "Team site created": "团队站点已创建", + "Team url": "团队 url", + "Domain name is invalid": "域名无效", + "Domain name is required": "域名为必填项" }, "HiddenQuestionConfig": { "Hidden fields": "隐藏的字段" @@ -1401,16 +1546,76 @@ "FormModel": { "Oops! The form you're looking for doesn't exist.": "抱歉!您要找的表单不存在。", "There was a problem loading the form.": "加载表格时出现问题。", - "You don't have access to this form.": "您无权访问此表单。" + "You don't have access to this form.": "您无权访问此表单。", + "Oops! This form is no longer published.": "啊哦!此表单现已不公开。" }, "FormPage": { "There was an error submitting your form. Please try again.": "提交表单时出现错误。请再试一次。" }, "FormSuccessPage": { "Form Submitted": "表单已提交", - "Thank you! Your response has been recorded.": "谢谢!您的回复已被记录。" + "Thank you! Your response has been recorded.": "谢谢!您的回复已被记录。", + "Submit new response": "提交新的响应" }, "FormContainer": { - "Build your own form": "创建自己的表单" + "Build your own form": "创建自己的表单", + "Powered by": "驱动自" + }, + "Columns": { + "Remove Column": "删除列" + }, + "UnmappedFieldsConfig": { + "Map fields": "映射字段", + "Select All": "选择全部", + "Unmap fields": "取消映射字段", + "Unmapped": "未映射的", + "Clear": "清空", + "Mapped": "已映射的" + }, + "MappedFieldsConfig": { + "Map fields": "映射字段", + "Mapped": "映射", + "Select All": "全选", + "Clear": "清空", + "Unmap fields": "取消映射字段", + "Unmapped": "未映射的" + }, + "WelcomeCoachingCall": { + "On the call, we'll take the time to understand your needs and tailor the call to you. We can show you the Grist basics, or start working with your data right away to build the dashboards you need.": "在通话中,我们会花时间了解您的需求,并为您量身定制通话内容。我们可以向您介绍 Grist 的基础知识,或者立即开始使用您的数据构建所需的仪表板。", + "Schedule your {{freeCoachingCall}} with a member of our team.": "与我们的团队成员预约您的 {{freeCoachingCall}} 。", + "Schedule Call": "预约通话", + "Maybe Later": "或许稍后", + "free coaching call": "免费辅助热线" + }, + "AuditLogStreamingConfig": { + "Enter token": "输入令牌", + "Token": "令牌", + "URL": "URL", + "Enter URL": "输入URL", + "Other": "其他", + "Start streaming": "开始流式传输", + "Save": "保存", + "Learn more": "了解更多" + }, + "AuditLogsPage": { + "Contact us": "联系我们", + "Home": "Home", + "Only site owners may access audit logs.": "只有站点所有者可以访问审计日志。", + "Audit Logs": "审计日志", + "Audit logs for {{siteName}}": "{{siteName}}的审计日志", + "Log streaming": "日志流", + "upgrade your plan": "升级您的计划" + }, + "CustomView": { + "Some required columns aren't mapped": "部分必要的列尚未映射", + "To use this widget, please map all non-optional columns from the creator panel on the right.": "要使用此小部件,请从右侧创建者面板映射所有非可选列。" + }, + "Section": { + "Insert section below": "在下面插入部分", + "Insert section above": "在上面插入部分" + }, + "SupportGristButton": { + "Help Center": "帮助中心", + "Close": "关闭" } } From 4f7310b879546bc0f066dfd8fca86e292d5d7b02 Mon Sep 17 00:00:00 2001 From: VojtechKrystek Date: Thu, 12 Dec 2024 21:26:00 +0000 Subject: [PATCH 06/10] Translated using Weblate (Czech) Currently translated at 47.2% (716 of 1515 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/cs/ --- static/locales/cs.client.json | 732 +++++++++++++++++++++++++++++++++- 1 file changed, 724 insertions(+), 8 deletions(-) diff --git a/static/locales/cs.client.json b/static/locales/cs.client.json index d8584edba4..6bd81b0788 100644 --- a/static/locales/cs.client.json +++ b/static/locales/cs.client.json @@ -11,7 +11,7 @@ "Condition": "Podmínka", "Delete Table Rules": "Vymaž Tabulkové Pravidla", "Default Rules": "Základní Práva", - "Attribute name": "Jméno atributu", + "Attribute name": "Název atributu", "Add User Attributes": "Přidat atribut pro uživatele", "Attribute to Look Up": "Atribut k vyhledání", "Everyone": "Všichni", @@ -35,19 +35,24 @@ "Save": "Ulož", "Rules for table ": "Pravidla pro tabulku ", "Special Rules": "Speciální Pravidla", - "This default should be changed if editors' access is to be limited. ": "Toto výchozí nastavení by mělo být změněno, pokud má být přístup editorů omezen. " + "This default should be changed if editors' access is to be limited. ": "Toto výchozí nastavení by mělo být změněno, pokud má být přístup editorů omezen. ", + "Allow editors to edit structure (e.g. modify and delete tables, columns, layouts), and to write formulas, which give access to all data regardless of read restrictions.": "Povolit editorům upravovat strukturu (např. měnit a mazat tabulky, sloupce, rozvržení) a psát vzorce, které umožňují přístup ke všem datům bez ohledu na omezení čtení.", + "Add Table-wide Rule": "Přidat pravidlo pro celou tabulku" }, "ACUserManager": { "Invite new member": "Pozvat nového uživatele", "We'll email an invite to {{email}}": "Zašleme pozvánku emailem na {{email}}", - "Enter email address": "Napiš e-mailovou adresu" + "Enter email address": "Vepište e-mailovou adresu" }, "ApiKey": { "Remove API Key": "Odeber API klíč", "Click to show": "Klikni pro zobrazení", "Create": "Vytvoř", "Remove": "Odeber", - "By generating an API key, you will be able to make API calls for your own account.": "Tím že vygeneruješ API klíč, budeš schopen používat \"API calls\" pro svůj vlastní účet." + "By generating an API key, you will be able to make API calls for your own account.": "Tím že vygeneruješ API klíč, budeš schopen používat \"API calls\" pro svůj vlastní účet.", + "You're about to delete an API key. This will cause all future requests using this API key to be rejected. Do you still want to delete?": "Chystáte se smazat klíč API. To způsobí, že všechny budoucí požadavky používající tento klíč API budou odmítnuty. Opravdu chcete smazat?", + "This API key can be used to access your account via the API. Don’t share your API key with anyone.": "Tento klíč API lze použít k přístupu k vašemu účtu prostřednictvím API. Klíč API s nikým nesdílejte.", + "This API key can be used to access this account anonymously via the API.": "Tento klíč API lze použít k anonymnímu přístupu k tomuto účtu prostřednictvím API." }, "AccountPage": { "Theme": "Motiv", @@ -64,7 +69,8 @@ "Login Method": "Přihlašovací metody", "API Key": "API klíč", "Name": "Jméno", - "Save": "Ulož" + "Save": "Ulož", + "Allow signing in to this account with Google": "Povolit přihlášení k tomuto účtu pomocí Google" }, "AccountWidget": { "Add Account": "Přidej Účet", @@ -97,7 +103,16 @@ "Make into data column": "Změň na data sloupec", "Enter formula": "Zadej formuli", "Data Columns_one": "Data Sloupec", - "Set trigger formula": "Nastav \"trigger\" formuli" + "Set trigger formula": "Nastav \"trigger\" formuli", + "Column options are limited in summary tables.": "Možnosti sloupců jsou v souhrnných tabulkách omezené.", + "Convert column to data": "Převést sloupec na data", + "TRIGGER FORMULA": "SPOUŠTĚCÍ VZOREC", + "Clear and reset": "Vymazat a resetovat", + "COLUMN LABEL AND ID": "OZNAČENÍ A ID SLOUPCE", + "Clear and make into formula": "Vymazat a převést na vzorec", + "COLUMN BEHAVIOR": "CHOVÁNÍ SLOUPCE", + "Convert to trigger formula": "Převést na spouštěcí vzorec", + "Mixed Behavior": "Smíšené chování" }, "ActionLog": { "Column {{colId}} was subsequently removed in action #{{action.actionNum}}": "Sloupec {{colId}} byl odstraněn po spuštění akce #{{action.actionNum}}", @@ -120,7 +135,10 @@ "Personal Site": "Osobní stránka", "Home Page": "Domácí stránka", "Team Site": "Stránka Týmu", - "Grist Templates": "Šablony Grist" + "Grist Templates": "Šablony Grist", + "Billing Account": "Fakturační účet", + "Manage Team": "Spravovat tým", + "Legacy": "Stará verze" }, "ViewAsDropdown": { "View As": "Zobraz Jako", @@ -133,9 +151,707 @@ "Key": "Klíč" }, "FilterBar": { - "SearchColumns": "Prohledej sloupce" + "SearchColumns": "Prohledej sloupce", + "Search Columns": "Hledat sloupce" }, "AddNewButton": { "Add New": "Přidej Nový" + }, + "CellContextMenu": { + "Clear cell": "Vymazat buňku", + "Clear values": "Vymazat hodnoty", + "Duplicate rows_other": "Duplikovat řádky", + "Filter by this value": "Filtrovat podle této hodnoty", + "Insert column to the right": "Vložit sloupec vpravo", + "Insert row": "Vložit řádek", + "Insert row below": "Vložit řádek pod", + "Reset {{count}} entire columns_one": "Resetovat celý sloupec", + "Reset {{count}} entire columns_other": "Resetovat {{count}} celých sloupců", + "Copy": "Kopírovat", + "Cut": "Vyjmout", + "Copy with headers": "Kopírovat s hlavičkami", + "Reset {{count}} columns_other": "Resetovat {{count}} sloupce", + "Reset {{count}} columns_one": "Resetovat sloupec", + "Paste": "Vložit", + "Delete {{count}} columns_other": "Smazat {{count}} sloupců", + "Delete {{count}} rows_one": "Smazat řádek", + "Insert column to the left": "Vložit sloupec vlevo", + "Delete {{count}} rows_other": "Smazat {{count}} řádků", + "Duplicate rows_one": "Duplikovat řádek", + "Copy anchor link": "Kopírovat odkaz kotvy", + "Delete {{count}} columns_one": "Smazat sloupec", + "Comment": "Komentář", + "Insert row above": "Vložit řádek nad" + }, + "ChartView": { + "Each Y series is followed by a series for the length of error bars.": "Každá Y série je následována sérií pro délku chybových pruhů.", + "Toggle chart aggregation": "Přepnout agregaci grafu", + "Create separate series for each value of the selected column.": "Vytvořit samostatné série pro každou hodnotu vybrané sloupce.", + "selected new group data columns": "vybrané nové sloupce skupinových dat", + "Each Y series is followed by two series, for top and bottom error bars.": "Každá Y série je následována dvěma sériemi, pro horní a dolní chybové pruhy.", + "Pick a column": "Vyberte sloupec" + }, + "CodeEditorPanel": { + "Access denied": "Přístup odepřen", + "Code View is available only when you have full document access.": "Zobrazení kódu je dostupné pouze při plném přístupu k dokumentu." + }, + "ColorSelect": { + "Apply": "Použít", + "Cancel": "Zrušit", + "Default cell style": "Výchozí styl buňky" + }, + "ColumnFilterMenu": { + "All Shown": "Vše zobrazené", + "Filter by Range": "Filtrovat podle rozsahu", + "No matching values": "Žádné odpovídající hodnoty", + "Future Values": "Budoucí hodnoty", + "None": "Žádné", + "Min": "Min", + "All": "Vše", + "All Except": "Vše kromě", + "Other Values": "Ostatní hodnoty", + "Others": "Ostatní", + "Search": "Hledat", + "Search values": "Hledat hodnoty", + "End": "Končí", + "Other Non-Matching": "Ostatní neodpovídající", + "Start": "Začíná", + "Other Matching": "Ostatní odpovídající", + "Max": "Max" + }, + "CustomSectionConfig": { + "Enter Custom URL": "Zadat vlastní URL", + "Learn more about custom widgets": "Zjistit více o vlastních widgetech", + "{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} sloupce, které nejsou typu {{columnType}}, nejsou zobrazeny", + "No {{columnType}} columns in table.": "Žádné sloupce typu {{columnType}} v tabulce.", + "ACCESS LEVEL": "ÚROVEŇ PŘÍSTUPU", + "Accept": "Přijmout", + "Custom URL": "Vlastní URL", + "Developer:": "Vývojář:", + "Last updated:": "Poslední aktualizace:", + "Missing description and author information.": "Chybí popis a informace o autorovi.", + "Reject": "Odmítnout", + "Widget": "Widget", + " (optional)": " (volitelné)", + "Pick a column": "Vyberte sloupec", + "Pick a {{columnType}} column": "Vyberte sloupec typu {{columnType}}", + "Read selected table": "Číst vybranou tabulku", + "Select Custom Widget": "Vybrat vlastní widget", + "Widget does not require any permissions.": "Widget nevyžaduje žádná oprávnění.", + "Widget needs to {{read}} the current table.": "Widget potřebuje {{read}} aktuální tabulku.", + "{{wrongTypeCount}} non-{{columnType}} columns are not shown_one": "{{wrongTypeCount}} sloupec, který není typu {{columnType}}, není zobrazen", + "Widget needs {{fullAccess}} to this document.": "Widget potřebuje {{fullAccess}} k tomuto dokumentu.", + "Clear selection": "Vymazat výběr", + "Full document access": "Plný přístup k dokumentu", + "Add": "Přidat", + "No document access": "Žádný přístup k dokumentu", + "Open configuration": "Otevřít konfiguraci" + }, + "DataTables": { + "Click to copy": "Klikněte pro zkopírování", + "Raw Data Tables": "Tabulky surových dat", + "Duplicate Table": "Duplikovat tabulku", + "Remove Table": "Odstranit tabulku", + "Rename Table": "Přejmenovat tabulku", + "{{action}} Record Card": "{{action}} kartu záznamu", + "Table ID copied to clipboard": "ID tabulky zkopírováno do schránky", + "Edit Record Card": "Upravit kartu záznamu", + "Record Card": "Karta záznamu", + "Record Card Disabled": "Karta záznamu zakázána", + "Delete {{formattedTableName}} data, and remove it from all pages?": "Smazat data {{formattedTableName}} a odstranit je ze všech stránek?", + "You do not have edit access to this document": "Nemáte přístup k úpravám tohoto dokumentu" + }, + "DocHistory": { + "Snapshots": "Snímky", + "Snapshots are unavailable.": "Snímky nejsou k dispozici.", + "Only owners have access to snapshots for documents with access rules.": "Pouze vlastníci mají přístup k snímkům pro dokumenty s pravidly přístupu.", + "Beta": "Beta", + "Activity": "Aktivita", + "Compare to Current": "Porovnat s aktuálním", + "Compare to Previous": "Porovnat s předchozím", + "Open Snapshot": "Otevřít snímek" + }, + "DocMenu": { + "Examples & Templates": "Příklady & šablony", + "Pinned Documents": "Připnuté dokumenty", + "Trash": "Koš", + "Delete": "Smazat", + "Delete Forever": "Smazat navždy", + "Delete {{name}}": "Smazat {{name}}", + "Document will be permanently deleted.": "Dokument bude trvale smazán.", + "Edited {{at}}": "Upraveno {{at}}", + "Move": "Přesunout", + "Move {{name}} to workspace": "Přesunout {{name}} do pracovního prostoru", + "Other Sites": "Ostatní stránky", + "Permanently Delete \"{{name}}\"?": "Trvale smazat \"{{name}}\"?", + "Pin Document": "Připnout dokument", + "This service is not available right now": "Tato služba není momentálně k dispozici", + "Workspace not found": "Pracovní prostor nenalezen", + "Unpin Document": "Odepnout dokument", + "Access Details": "Podrobnosti o přístupu", + "All Documents": "Všechny dokumenty", + "(The organization needs a paid plan)": "(Organizace potřebuje placený plán)", + "Deleted {{at}}": "Smazáno {{at}}", + "By Date Modified": "Podle data úpravy", + "By Name": "Podle názvu", + "Current workspace": "Aktuální pracovní prostor", + "Discover More Templates": "Objevte více šablon", + "Document will be moved to Trash.": "Dokument bude přesunut do koše.", + "Documents stay in Trash for 30 days, after which they get deleted permanently.": "Dokumenty zůstanou v koši 30 dní, po kterých budou trvale smazány.", + "Remove": "Odstranit", + "To restore this document, restore the workspace first.": "Pro obnovení tohoto dokumentu nejprve obnovte pracovní prostor.", + "Trash is empty.": "Koš je prázdný.", + "Any documents created in this site will appear here.": "Jakékoliv dokumenty vytvořené na této stránce se objeví zde.", + "Create my first document": "Vytvořte svůj první dokument", + "You have read-only access to this site. Currently there are no documents.": "Máte pouze přístup pro čtení k této stránce. Momentálně zde nejsou žádné dokumenty.", + "personal site": "osobní stránka", + "Restore": "Obnovit", + "Requires edit permissions": "Vyžaduje oprávnění k úpravám", + "You are on the {{siteName}} site. You also have access to the following sites:": "Jste na stránce {{siteName}}. Máte také přístup k následujícím stránkám:", + "You may delete a workspace forever once it has no documents in it.": "Pracovní prostor můžete trvale smazat, jakmile v něm nebudou žádné dokumenty.", + "Examples and Templates": "Příklady a šablony", + "Manage Users": "Spravovat uživatele", + "You are on your personal site. You also have access to the following sites:": "Jste na své osobní stránce. Máte také přístup k následujícím stránkám:", + "Featured": "Vybrané", + "More Examples and Templates": "Více příkladů a šablon", + "Rename": "Přejmenovat" + }, + "DocumentSettings": { + "Document ID copied to clipboard": "ID dokumentu zkopírováno do schránky", + "API documentation.": "Dokumentace k API.", + "Currency": "Měna", + "Data Engine": "Datový engine", + "Document ID": "ID dokumentu", + "Notify other services on doc changes": "Oznamovat jiným službám změny v dokumentu", + "python2 (legacy)": "python2 (legacy)", + "Force reload the document while timing formulas, and show the result.": "Nutné znovu načíst dokument při časování vzorců a zobrazit výsledek.", + "Formula timer": "Časovač vzorce", + "Reload data engine": "Obnovit datový engine", + "Reload data engine?": "Obnovit datový engine?", + "Start timing": "Začít měřit čas", + "Stop timing...": "Zastavit měření času...", + "Time reload": "Čas pro obnovení", + "Timing is on": "Měření času je zapnuto", + "You can make changes to the document, then stop timing to see the results.": "Můžete provést změny v dokumentu a poté zastavit měření času, abyste viděli výsledky.", + "Document Settings": "Nastavení dokumentu", + "This document's ID (for API use):": "ID tohoto dokumentu (pro použití API):", + "Ok": "OK", + "Webhooks": "Webhooky", + "This will perform a hard reload of the data engine. This may help if the data engine is stuck in an infinite loop, is indefinitely processing the latest change, or has crashed. No data will be lost, except possibly currently pending actions.": "Tímto dojde k tvrdému obnovení datového engine. To může pomoci, pokud je datový engine uvízlý v nekonečné smyčce, neustále zpracovává poslední změnu nebo došlo k jeho pádu. Žádná data nebudou ztracena, kromě případně aktuálně čekajících akcí.", + "Template": "Šablona", + "python3 (recommended)": "python3 (doporučeno)", + "Change document type": "Změna typu dokumentu", + "Regular document": "Běžný dokument", + "Manage webhooks": "Spravovat webhooky", + "Once you start timing, Grist will measure the time it takes to evaluate each formula. This allows diagnosing which formulas are responsible for slow performance when a document is first opened, or when a document responds to changes.": "Jakmile začnete měřit čas, aplikace bude měřit dobu potřebnou k vyhodnocení každého vzorce. To umožňuje diagnostikovat, které vzorce jsou zodpovědné za pomalý výkon při prvním otevření dokumentu nebo když dokument reaguje na změny.", + "Hard reset of data engine": "Tvrdý reset datového engine", + "Change nature of document": "Změna povahy dokumentu", + "Cancel": "Zrušit", + "API URL copied to clipboard": "API URL zkopírováno do schránky", + "API console": "API konzole", + "Coming soon": "Brzy bude k dispozici", + "Copy to clipboard": "Kopírovat do schránky", + "Document ID to use whenever the REST API calls for {{docId}}. See {{apiURL}}": "ID dokumentu, které se použije, kdykoliv REST API požaduje {{docId}}. Viz {{apiURL}}", + "For currency columns": "Pro sloupce s měnou", + "Formula times": "Časy vzorců", + "Find slow formulas": "Najít pomalé vzorce", + "For number and date formats": "Pro formáty čísel a dat", + "Python version used": "Použitá verze Pythonu", + "Python": "Python", + "Reload": "Načíst znovu", + "Time Zone": "Časové pásmo", + "API Console": "API konzole", + "Template mode": "Režim šablony", + "Regular": "Běžný", + "fiddle mode": "Režim pokusů", + "Tutorial": "Návod", + "Confirm change": "Potvrdit změnu", + "Engine (experimental {{span}} change at own risk):": "Engine (experimentální {{span}} změna na vlastní riziko):", + "Time Zone:": "Časové pásmo:", + "API": "API", + "Try API calls from the browser": "Vyzkoušejte volání API z prohlížeče", + "Only available to document editors": "K dispozici pouze editorům dokumentu", + "Only available to document owners": "K dispozici pouze vlastníkům dokumentu", + "Locale:": "Jazyk:", + "Save": "Uložit", + "Currency:": "Měna:", + "Edit": "Upravit", + "ID for API use": "ID pro použití v API", + "Normal document behavior. All users work on the same copy of the document.": "Normální chování dokumentu. Všichni uživatelé pracují se stejnou kopií dokumentu.", + "Document automatically opens as a user-specific copy.": "Dokument se automaticky otevře jako kopie specifická pro uživatele.", + "Manage Webhooks": "Spravovat webhooky", + "Save and Reload": "Uložit a znovu načíst", + "Local currency ({{currency}})": "Místní měna ({{currency}})", + "Default for DateTime columns": "Výchozí pro sloupce DateTime", + "Base doc URL: {{docApiUrl}}": "Základní URL dokumentu: {{docApiUrl}}", + "Locale": "Jazyk", + "Document automatically opens in {{fiddleModeDocUrl}}. Anyone may edit, which will create a new unsaved copy.": "Dokument se automaticky otevře v {{fiddleModeDocUrl}}. Každý může upravit, což vytvoří novou neuloženou kopii." + }, + "DocumentUsage": { + "Contact the site owner to upgrade the plan to raise limits.": "Kontaktujte vlastníka webu, aby upgradoval plán a zvýšil limity.", + "Rows": "Řádky", + "Usage statistics are only available to users with full access to the document data.": "Statistiky použití jsou k dispozici pouze uživatelům s plným přístupem k datům dokumentu.", + "For higher limits, ": "Pro vyšší limity, ", + "Data Size": "Velikost dat", + "Usage": "Použití", + "start your 30-day free trial of the Pro plan.": "Začněte svou 30denní bezplatnou zkušební verzi plánu Pro.", + "Attachments Size": "Velikost příloh" + }, + "GridViewMenus": { + "Add to sort": "Přidat k seřazení", + "Duplicate in {{- label}}": "Duplikovat v {{- label}}", + "Last updated by": "Poslední aktualizace v", + "Any": "Jakékoli", + "Numeric": "Číselné", + "Hidden Columns": "Skryté sloupce", + "Created By": "Vytvořil", + "Reference List": "Seznam referencí", + "Unfreeze {{count}} columns_other": "Odemknout {{count}} sloupců", + "Date": "Datum", + "Add column with type": "Přidat sloupec s typem", + "DateTime": "Datum a čas", + "Convert formula to data": "Převést vzorec na data", + "Delete {{count}} columns_one": "Smazat sloupec", + "Filter Data": "Filtrovat data", + "Freeze {{count}} columns_one": "Zamknout tento sloupec", + "Insert column to the left": "Vložit sloupec vlevo", + "Created at": "Vytvořeno v", + "Created by": "Vytvořil", + "Freeze {{count}} columns_other": "Zamknout {{count}} sloupců", + "Freeze {{count}} more columns_one": "Zamknout jeden další sloupec", + "Freeze {{count}} more columns_other": "Zamknout {{count}} dalších sloupců", + "Hide {{count}} columns_other": "Skrýt {{count}} sloupců", + "Insert column to the {{to}}": "Vložit sloupec do {{to}}", + "More sort options ...": "Více možností seřazení…", + "Rename column": "Přejmenovat sloupec", + "Reset {{count}} columns_one": "Resetovat sloupec", + "Reset {{count}} columns_other": "Resetovat {{count}} sloupce", + "Show column {{- label}}": "Zobrazit sloupec {{- label}}", + "Sorted (#{{count}})_one": "Seřazeno (#{{count}})", + "Sorted (#{{count}})_other": "Seřazeno (#{{count}})", + "Unfreeze all columns": "Odemknout všechny sloupce", + "Adding UUID column": "Přidání sloupce UUID", + "Adding duplicates column": "Přidání sloupce s duplicitami", + "no reference column": "Žádný referenční sloupec", + "Detect Duplicates in...": "Detekce duplicit v...", + "Search columns": "Prohledat sloupce", + "No reference columns.": "Žádné referenční sloupce.", + "Add column": "Přidat sloupec", + "Detect duplicates in...": "Detekce duplicit v...", + "Column Options": "Možnosti sloupce", + "Hide {{count}} columns_one": "Skrýt sloupec", + "Authorship": "Autorství", + "Last Updated By": "Poslední aktualizace v", + "Add formula column": "Přidat sloupec s vzorcem", + "Integer": "Celé číslo", + "Toggle": "Přepínač", + "Attachment": "Příloha", + "Insert column to the right": "Vložit sloupec vpravo", + "Reset {{count}} entire columns_other": "Resetovat {{count}} celých sloupců", + "Sort": "Seřadit", + "Unfreeze {{count}} columns_one": "Odemknout tento sloupec", + "Apply on record changes": "Použít na změny záznamu", + "Apply to new records": "Použít na nové záznamy", + "Created At": "Vytvořeno v", + "Text": "Text", + "Choice": "Výběr", + "Last Updated At": "Poslední aktualizace", + "Add Column": "Přidat sloupec", + "Clear values": "Vymazat hodnoty", + "Show hidden columns": "Zobrazit skryté sloupce", + "Timestamp": "Časové razítko", + "UUID": "UUID", + "Last updated at": "Poslední aktualizace", + "Lookups": "Vyhledávání", + "Shortcuts": "Zkratky", + "Choice List": "Seznam možností", + "Reference": "Reference", + "Delete {{count}} columns_other": "Smazat {{count}} sloupců", + "Reset {{count}} entire columns_one": "Resetovat celý sloupec" + }, + "HomeIntro": { + "Sign up": "Zaregistrovat se", + "This workspace is empty.": "Tento pracovní prostor je prázdný.", + "Visit our {{link}} to learn more.": "Navštivte náš {{link}} a dozvíte se víc.", + "Welcome to Grist, {{name}}!": "Vítejte v Grist, {{name}}!", + "Welcome to {{orgName}}": "Vítejte v {{orgName}}", + "Welcome to Grist, {{- name}}!": "Vítejte v Grist, {{- name}}", + "Any documents created in this site will appear here.": "Jakékoliv dokumenty vytvořené na této stránce se objeví zde.", + "Welcome to Grist!": "Vítejte na Grist!", + "Browse Templates": "Procházet šablony", + "Create Empty Document": "Vytvořit prázdný dokument", + "Help Center": "Centrum nápovědy", + "{{signUp}} to save your work. ": "{{signUp}} pro uložení své práce. ", + "You have read-only access to this site. Currently there are no documents.": "Máte pouze přístup pro čtení k této stránce. Momentálně zde nejsou žádné dokumenty.", + "Get started by exploring templates, or creating your first Grist document.": "Začněte prozkoumáváním šablon nebo vytvořením prvního dokumentu v Grist.", + "Get started by creating your first Grist document.": "Začněte vytvořením prvního dokumentu v aplikaci.", + "Interested in using Grist outside of your team? Visit your free ": "Máte zájem používat Grist mimo svůj tým? Navštivte svůj bezplatný ", + "Import Document": "Importovat dokument", + "Invite Team Members": "Pozvat členy týmu", + "personal site": "osobní stránka", + "Get started by inviting your team and creating your first Grist document.": "Začněte pozváním svého týmu a vytvořením prvního dokumentu v Grist.", + "Sprouts Program": "Program Sprouts", + "Welcome to {{- orgName}}": "Vítejte v {{- orgName}}", + "To use Grist, please either sign up or sign in.": "Chcete-li používat službu Grist, zaregistrujte se nebo přihlaste.", + "Learn more in our {{helpCenterLink}}.": "Více informací najdete na našich stránkách {{helpCenterLink}}.", + "Only show documents": "Zobrazit pouze dokumenty", + "Visit our {{link}} to learn more about Grist.": "Navštivte náš {{link}}, abyste se dozvěděli více o Grist.", + "Sign in": "Přihlásit se" + }, + "HomeLeftPane": { + "Terms of service": "Podmínky služby", + "Import Document": "Importovat dokument", + "Workspace will be moved to Trash.": "Pracovní prostor bude přesunut do koše.", + "Create Workspace": "Vytvořit pracovní prostor", + "Workspaces": "Pracovní prostory", + "Manage Users": "Spravovat uživatele", + "Tutorial": "Tutoriál", + "Examples & Templates": "Šablony", + "Trash": "Koš", + "Delete": "Smazat", + "Access Details": "Podrobnosti o přístupu", + "All Documents": "Všechny dokumenty", + "Rename": "Přejmenovat", + "Create Empty Document": "Vytvořit prázdný dokument", + "Delete {{workspace}} and all included documents?": "Odstranit {{workspace}} a všechny obsažené dokumenty?", + "Grist Resources": "Zdroje Grist" + }, + "PermissionsWidget": { + "Deny All": "Odmítnout vše", + "Read Only": "Pouze pro čtení", + "Allow All": "Povolit vše" + }, + "RecordLayoutEditor": { + "Cancel": "Zrušit", + "Save Layout": "Uložit rozložení", + "Show field {{- label}}": "Zobrazit pole {{- label}}", + "Add Field": "Přidat pole", + "Create New Field": "Vytvořit nové pole" + }, + "RowContextMenu": { + "Duplicate rows_one": "Duplikovat řádek", + "View as card": "Zobrazit jako kartu", + "Duplicate rows_other": "Duplikovat řádky", + "Copy anchor link": "Kopírovat odkaz kotvy", + "Insert row": "Vložit řádek", + "Delete": "Smazat", + "Insert row above": "Vložit řádek nad", + "Use as table headers": "Použít jako záhlaví tabulky", + "Insert row below": "Vložit řádek pod" + }, + "Importer": { + "{{count}} unmatched field_other": "{{count}} neshodující se pole", + "Grist column": "Sloupec Grist", + "{{count}} unmatched field in import_other": "{{count}} neshodující se pole při importu", + "{{count}} unmatched field_one": "{{count}} neshodující se pole", + "Skip Import": "Přeskočit import", + "Merge rows that match these fields:": "Sloučit řádky, které odpovídají těmto polím:", + "Column Mapping": "Mapování sloupce", + "Import from file": "Importovat ze souboru", + "New Table": "Nová tabulka", + "Revert": "Vrátit", + "Destination table": "Cílová tabulka", + "Skip Table on Import": "Přeskočit tabulku při importu", + "Source column": "Zdrojový sloupec", + "Column mapping": "Mapování sloupce", + "Select fields to match on": "Vyberte pole, která chcete porovnat", + "Update existing records": "Aktualizovat stávající záznamy", + "{{count}} unmatched field in import_one": "{{count}} neshodující se pole při importu", + "Skip": "Přeskočit" + }, + "PageWidgetPicker": { + "Select Widget": "Vybrat Widget", + "Group by": "Seskupit podle", + "Building {{- label}} widget": "Vytváření widgetu {{- label}}", + "Add to Page": "Přidat na stránku", + "Select Data": "Vybrat data" + }, + "Pages": { + "Delete": "Smazat", + "The following tables will no longer be visible_one": "Následující tabulka již nebude viditelná.", + "Delete data and this page.": "Smazat data a tuto stránku.", + "The following tables will no longer be visible_other": "Následující tabulky již nebudou viditelné" + }, + "RightPanel": { + "ROW STYLE": "STYL ŘÁDKU", + "GROUPED BY": "SESKUPENO PODLE", + "CUSTOM": "VLASTNÍ", + "Fields_one": "Pole", + "Configuration": "Konfigurace", + "Default field value": "Výchozí hodnota pole", + "Submission": "Odeslání", + "Detach": "Odpojení", + "Edit Data Selection": "Upravit výběr dat", + "Theme": "Motiv", + "Widget": "Widget", + "WIDGET TITLE": "NÁZEV WIDGETU", + "Layout": "Rozložení", + "Required field": "Povinné pole", + "Select a field in the form widget to configure.": "Vyberte pole ve widgetu formuláře, které chcete nakonfigurovat.", + "Field rules": "Pravidla pole", + "Fields_other": "Pole", + "Series_one": "Série", + "DATA TABLE": "TABULKA DAT", + "Columns_one": "Sloupec", + "Change Widget": "Změnit widget", + "DATA TABLE NAME": "NÁZEV DATOVÉ TABULKY", + "COLUMN TYPE": "TYP SLOUPCE", + "Row Style": "Styl řádku", + "Save": "Uložit", + "Select Widget": "Vybrat Widget", + "Series_other": "Série", + "TRANSFORM": "TRANSFORMOVAT", + "You do not have edit access to this document": "Nemáte přístup k úpravám tohoto dokumentu", + "Add referenced columns": "Přidat referenční sloupce", + "Redirect automatically after submission": "Automaticky přesměrovat po odeslání", + "Submit another response": "Odeslat další odpověď", + "Submit button label": "Štítek tlačítka Odeslat", + "Success text": "Text úspěchu", + "Data": "Data", + "Redirection": "Přesměrování", + "Table column name": "Název sloupce tabulky", + "Sort & Filter": "Třídit & filtrovat", + "CHART TYPE": "TYP GRAFU", + "Display button": "Zobrazit tlačítko", + "Enter text": "Vepsat text", + "Reset form": "Obnovit formulář", + "Hidden field": "Skryté pole", + "Enter redirect URL": "Zadejte adresu URL přesměrování", + "No field selected": "Není vybráno žádné pole", + "Field title": "Název pole", + "Columns_other": "Sloupce" + }, + "Tools": { + "Delete document tour?": "Smazat prohlídku dokumentu?", + "TOOLS": "NÁSTROJE", + "Delete": "Smazat", + "Tour of this Document": "Prohlídka tohoto dokumentu", + "Code View": "Zobrazit kód", + "Document History": "Historie dokumentu", + "Return to viewing as yourself": "Vrátit se k zobrazení sám za sebe", + "API Console": "API konzole", + "Validate Data": "Ověření dat", + "Settings": "Nastavení", + "Access Rules": "Pravidla přístupu", + "Raw Data": "Surová data", + "How-to Tutorial": "Návod, jak na to" + }, + "DocPageModel": { + "Add Empty Table": "Přidat prázdnou tabulku", + "Add Page": "Přidat stránku", + "Add Widget to Page": "Přidat widget na stránku", + "Error accessing document": "Chyba při přístupu k dokumentu", + "Reload": "Načíst znovu", + "Sorry, access to this document has been denied. [{{error}}]": "Omlouváme se, přístup k tomuto dokumentu byl odepřen. [{{error}}]", + "You do not have edit access to this document": "Nemáte přístup k úpravám tohoto dokumentu", + "Enter recovery mode": "Vstoupit do režimu obnovy", + "You can try reloading the document, or using recovery mode. Recovery mode opens the document to be fully accessible to owners, and inaccessible to others. It also disables formulas. [{{error}}]": "Můžete zkusit dokument znovu načíst nebo použít režim obnovy. Režim obnovy otevře dokument tak, že bude plně přístupný vlastníkům a nepřístupný ostatním. Také deaktivuje vzorce. [{{error}}]", + "Document owners can attempt to recover the document. [{{error}}]": "Vlastníci dokumentu se mohou pokusit dokument obnovit. [{{error}}]" + }, + "ExampleInfo": { + "Welcome to the Investment Research template": "Vítejte v šabloně Investiční výzkum", + "Welcome to the Lightweight CRM template": "Vítejte v šabloně Lightweight CRM", + "Welcome to the Afterschool Program template": "Vítejte v šabloně Mimoškolního programu", + "Check out our related tutorial for how to link data, and create high-productivity layouts.": "Podívejte se na náš související návod, jak propojit data a vytvořit rozložení pro vysokou produktivitu.", + "Lightweight CRM": "Odlehčený systém CRM", + "Investment Research": "Investiční výzkum", + "Tutorial: Analyze & Visualize": "Tutoriál: Analýza a vizualizace", + "Check out our related tutorial for how to model business data, use formulas, and manage complexity.": "Podívejte se na náš související návod, jak modelovat obchodní data, používat vzorce a řídit složitost.", + "Tutorial: Create a CRM": "Výukový program: Vytvoření CRM", + "Check out our related tutorial to learn how to create summary tables and charts, and to link charts dynamically.": "Podívejte se na náš související návod, jak vytvořit souhrnné tabulky a grafy a jak dynamicky propojit grafy.", + "Tutorial: Manage Business Data": "Tutoriál: Správa obchodních dat", + "Afterschool Program": "Mimoškolní program" + }, + "GristDoc": { + "go to webhook settings": "přejít do nastavení webhooku", + "Added new linked section to view {{viewName}}": "Přidáno nové propojené zobrazení {{viewName}}", + "Saved linked section {{title}} in view {{name}}": "Uložena propojená sekce {{title}} v zobrazení {{name}}", + "Import from file": "Importovat ze souboru" + }, + "MakeCopyMenu": { + "Original Has Modifications": "Původní má úpravy", + "As Template": "Jako šablona", + "Include the structure without any of the data.": "Zahrnout strukturu bez jakýchkoli dat.", + "No destination workspace": "Žádný cílový pracovní prostor", + "Organization": "Organizace", + "Sign up": "Zaregistrovat se", + "Update Original": "Aktualizovat původní", + "Workspace": "Pracovní prostor", + "Download full document and history": "Stáhnout celý dokument a historii", + "Name": "Název", + "Update": "Aktualizovat", + "Original Looks Identical": "Původní vzhled je totožný", + "Cancel": "Zrušit", + "Download document": "Stáhnout dokument", + "Download": "Stáhnout", + "You do not have write access to this site": "Nemáte přístup k zápisu této stránky", + "Be careful, the original has changes not in this document. Those changes will be overwritten.": "Buďte opatrní, původní dokument obsahuje změny, které nejsou v tomto dokumentu. Tyto změny budou přepsány.", + "However, it appears to be already identical.": "Zdá se však, že je již identický.", + "It will be overwritten, losing any content not in this document.": "Bude přepsán, přičemž dojde ke ztrátě jakéhokoli obsahu, který není v tomto dokumentu.", + "Overwrite": "Přepsat", + "The original version of this document will be updated.": "Původní verze tohoto dokumentu bude aktualizována.", + "Remove all data but keep the structure to use as a template": "Odstranit všechna data, ale zachovat strukturu pro použití jako šablonu", + "You do not have write access to the selected workspace": "Nemáte oprávnění pro zápis do vybraného pracovního prostoru", + "Remove document history (can significantly reduce file size)": "Odstranit historii dokumentu (může výrazně zmenšit velikost souboru)", + "Enter document name": "Zadejte název dokumentu", + "Original Looks Unrelated": "Původní se zdá být nesouvisející", + "Replacing the original requires editing rights on the original document.": "Nahrazení původního vyžaduje oprávnění pro úpravy původního dokumentu.", + "To save your changes, please sign up, then reload this page.": "Pro uložení vašich změn se prosím přihlaste a poté znovu načtěte tuto stránku." + }, + "NotifyUI": { + "Notifications": "Oznámení", + "Give feedback": "Poskytněte zpětnou vazbu", + "Report a problem": "Nahlásit problém", + "No notifications": "Žádná oznámení", + "Renew": "Obnovit", + "Upgrade Plan": "Upgraduj svůj Plán", + "Manage billing": "Správa fakturace", + "Ask for help": "Požádat o pomoc", + "Cannot find personal site, sorry!": "Nelze najít osobní stránky, omlouvám se!", + "Go to your free personal site": "Přejděte na své bezplatné osobní stránky" + }, + "RecordLayout": { + "Updating record layout.": "Aktualizace rozložení záznamu." + }, + "Drafts": { + "Restore last edit": "Obnovit poslední úpravu", + "Undo discard": "Zrušit zahození" + }, + "TriggerFormulas": { + "Cancel": "Zrušit", + "Close": "Zavřít", + "Apply on record changes": "Použít na změny záznamu", + "Apply to new records": "Použít na nové záznamy", + "Current field ": "Aktuální pole ", + "Any field": "Jakékoli pole", + "Apply on changes to:": "Použít na změny v:", + "OK": "OK" + }, + "WelcomeSitePicker": { + "You have access to the following Grist sites.": "Máte přístup k následujícím stránkám Grist." + }, + "DuplicateTable": { + "Instead of duplicating tables, it's usually better to segment data using linked views. {{link}}": "Místo duplikování tabulek je obvykle lepší segmentovat data pomocí propojených zobrazení. {{link}}", + "Name for new table": "Název pro novou tabulku", + "Copy all data in addition to the table structure.": "Zkopírovat všechna data kromě struktury tabulky.", + "Only the document default access rules will apply to the copy.": "Na kopii se budou vztahovat pouze výchozí pravidla přístupu dokumentu." + }, + "GridOptions": { + "Horizontal Gridlines": "Vodorovné mřížky", + "Grid Options": "Možnosti mřížky", + "Zebra Stripes": "Zebří pruhy", + "Vertical Gridlines": "Svislé mřížky" + }, + "ShareMenu": { + "Compare to {{termToUse}}": "Porovnat s {{termToUse}}", + "Download": "Stáhnout", + "Save Copy": "Uložit kopii", + "Save Document": "Uložit dokument", + "Access Details": "Podrobnosti o přístupu", + "Edit without affecting the original": "Upravit bez ovlivnění originálu", + "Export CSV": "Export do CSV", + "Export XLSX": "Export do XLSX", + "Manage Users": "Spravovat uživatele", + "Original": "Originál", + "Show in folder": "Zobrazit ve složce", + "Back to Current": "Zpět na aktuální", + "Return to {{termToUse}}": "Návrat na {{termToUse}}", + "Unsaved": "Neuloženo", + "Comma Separated Values (.csv)": "Hodnoty oddělené čárkou (.csv)", + "DOO Separated Values (.dsv)": "Hodnoty oddělené DOO (.dsv)", + "Export as...": "Exportovat jako...", + "Microsoft Excel (.xlsx)": "Microsoft Excel (.xlsx)", + "Tab Separated Values (.tsv)": "Hodnoty oddělené tabulátory (.tsv)", + "Duplicate Document": "Duplicitní dokument", + "Replace {{termToUse}}...": "Nahradit {{termToUse}}…", + "Current Version": "Aktuální verze", + "Send to Google Drive": "Odeslat na Disk Google", + "Work on a Copy": "Pracovat na kopii", + "Share": "Sdílet", + "Download...": "Stáhnout..." + }, + "SortFilterConfig": { + "Save": "Uložit", + "Sort": "ŘADIT", + "Filter": "FILTR", + "Update Sort & Filter settings": "Aktualizovat nastavení řazení a filtru", + "Revert": "Vrátit" + }, + "TypeTransformation": { + "Apply": "Použít", + "Preview": "Náhled", + "Cancel": "Zrušit", + "Update formula (Shift+Enter)": "Aktualizovat vzorec (Shift+Enter)", + "Revise": "Revize" + }, + "WelcomeTour": { + "convert to card view, select data, and more.": "Převést na zobrazení karet, vybrat data a další." + }, + "OnBoardingPopups": { + "Next": "Další", + "Previous": "předchozí", + "Finish": "Dokončit" + }, + "OpenVideoTour": { + "Grist Video Tour": "Videoprohlídka Grist", + "Video Tour": "Videoprohlídka", + "YouTube video player": "Přehrávač videí YouTube" + }, + "RefSelect": { + "No columns to add": "Žádné sloupce k přidání", + "Add Column": "Přidat sloupec" + }, + "SortConfig": { + "Update Data": "Aktualizovat data", + "Search Columns": "Prohledat sloupce", + "Empty values last": "Prázdné poslední hodnoty", + "Add Column": "Přidat sloupec", + "Natural sort": "Přirozené řazení", + "Use choice position": "Použít pozici výběru" + }, + "TopBar": { + "Manage Team": "Spravovat tým", + "Manage team": "Spravovat tým" + }, + "DocTour": { + "Cannot construct a document tour from the data in this document. Ensure there is a table named GristDocTour with columns Title, Body, Placement, and Location.": "Nelze vytvořit prohlídku dokumentu z dat v tomto dokumentu. Ujistěte se, že existuje tabulka s názvem GristDocTour a sloupci Title, Body, Placement a Location.", + "No valid document tour": "Žádná platná prohlídka dokumentu" + }, + "errorPages": { + "Failed to log in.{{separator}}Please try again or contact support.": "Přihlášení se nezdařilo.{{separator}}Zkuste to znovu nebo kontaktujte podporu." + }, + "GristTooltips": { + "You can filter by more than one column.": "Můžete filtrovat podle více než jednoho sloupce.", + "Apply conditional formatting to rows based on formulas.": "Použít podmíněné formátování na řádky na základě vzorců.", + "Select the table containing the data to show.": "Vyberte tabulku obsahující data, která chcete zobrazit." + }, + "OnboardingPage": { + "What brings you to Grist (you can select multiple)?": "Co vás přivedlo k Grist (můžete vybrat více možností)?" + }, + "SiteSwitcher": { + "Create new team site": "Vytvořit nové stránky týmu", + "Switch Sites": "Přepnout stránky" + }, + "ThemeConfig": { + "Appearance ": "Vzhled ", + "Switch appearance automatically to match system": "Přepnout vzhled automaticky podle systému" + }, + "AppModel": { + "This team site is suspended. Documents can be read, but not modified.": "Tato týmová stránka je pozastavena. Dokumenty lze číst, ale nelze je upravovat." + }, + "PluginScreen": { + "Import failed: ": "Import se nezdařil: " + }, + "CreateTeamModal": { + "Choose a name and url for your team site": "Vyberte název a URL pro váš týmový web" + }, + "LeftPanelCommon": { + "Help Center": "Centrum nápovědy" + }, + "UserManagerModel": { + "Editor": "Editor" + }, + "SelectionSummary": { + "Copied to clipboard": "Zkopírováno do schránky" } } From 10c5569e4dc6b5ea0dbbced459de02e7f4011ce4 Mon Sep 17 00:00:00 2001 From: VojtechKrystek Date: Thu, 12 Dec 2024 22:57:19 +0000 Subject: [PATCH 07/10] Translated using Weblate (Czech) Currently translated at 100.0% (2 of 2 strings) Translation: Grist/server Translate-URL: https://hosted.weblate.org/projects/grist/server/cs/ --- static/locales/cs.server.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/static/locales/cs.server.json b/static/locales/cs.server.json index 0967ef424b..25e70562f9 100644 --- a/static/locales/cs.server.json +++ b/static/locales/cs.server.json @@ -1 +1,8 @@ -{} +{ + "sendAppPage": { + "Loading": "Načítání" + }, + "oidc": { + "emailNotVerifiedError": "Ověřte prosím svůj e-mail u poskytovatele identit a znovu se přihlaste." + } +} From dbc3659f482c24d76a01a364b722f473e6fa7be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BD=90=E5=AD=90=E7=90=AA?= Date: Fri, 13 Dec 2024 07:29:12 +0000 Subject: [PATCH 08/10] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 99.7% (1511 of 1515 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/zh_Hans/ --- static/locales/zh_Hans.client.json | 244 +++++++++++++++++++++++++++-- 1 file changed, 230 insertions(+), 14 deletions(-) diff --git a/static/locales/zh_Hans.client.json b/static/locales/zh_Hans.client.json index ae2ee29edb..e1525541bf 100644 --- a/static/locales/zh_Hans.client.json +++ b/static/locales/zh_Hans.client.json @@ -103,7 +103,15 @@ "{{wrongTypeCount}} non-{{columnType}} columns are not shown_one": "{{wrongTypeCount}} 非 {{columnType}} 列未显示", "{{wrongTypeCount}} non-{{columnType}} columns are not shown_other": "{{wrongTypeCount}} 非 {{columnType}} 列未显示", "No {{columnType}} columns in table.": "表格中没有{{columnType}} 列。", - "Clear selection": "清除选择" + "Clear selection": "清除选择", + "Last updated:": "上次更新:", + "Reject": "拒绝", + "ACCESS LEVEL": "访问级别", + "Accept": "接受", + "Custom URL": "自定义 URL", + "Developer:": "开发者:", + "Missing description and author information.": "缺少描述和作者信息。", + "Widget": "小部件" }, "ChartView": { "Create separate series for each value of the selected column.": "为所选列的每个值创建单独的系列。", @@ -150,7 +158,8 @@ "Paste": "粘贴", "Comment": "评论", "Copy": "拷贝", - "Cut": "剪切" + "Cut": "剪切", + "Copy with headers": "带表头复制" }, "ACUserManager": { "Enter email address": "请输入电子邮件地址", @@ -260,7 +269,10 @@ "You are on the {{siteName}} site. You also have access to the following sites:": "您在 {{siteName}} 网站上。 您还可以访问以下站点:", "You may delete a workspace forever once it has no documents in it.": "一旦其中没有文档,您可以永远删除工作区。", "Delete {{name}}": "删除 {{name}}", - "personal site": "个人站点" + "personal site": "个人站点", + "Any documents created in this site will appear here.": "在此站点上创建的任何文档都将显示在此处。", + "Create my first document": "创建我的首个文档", + "You have read-only access to this site. Currently there are no documents.": "您对此站点具有只读访问权限。目前没有任何文件。" }, "DocPageModel": { "Add Empty Table": "添加空表", @@ -654,7 +666,7 @@ "python3 (recommended)": "python3(推荐)", "Try API calls from the browser": "尝试从浏览器调用API", "Cancel": "取消", - "Force reload the document while timing formulas, and show the result.": "在计算公式时强制重新加载文档,并显示结果。", + "Force reload the document while timing formulas, and show the result.": "在计时公式时强制重新加载文档,并显示结果。", "Template mode": "模板模式", "Confirm change": "确认更改", "Edit": "编辑", @@ -670,7 +682,26 @@ "Reload data engine": "重新加载数据引擎", "Reload data engine?": "重新加载数据引擎?", "Only available to document editors": "仅适用于文档编辑者", - "Only available to document owners": "仅适用于文档所有者" + "Only available to document owners": "仅适用于文档所有者", + "Copy to clipboard": "复制到剪贴板", + "Document ID to use whenever the REST API calls for {{docId}}. See {{apiURL}}": "REST API调用 {{docId}} 时使用的文档 ID。参见 {{apiURL}}", + "Time reload": "重新加载时间", + "API URL copied to clipboard": "复制 API URL 到剪贴板", + "API console": "API 控制台", + "For currency columns": "对于货币列", + "Hard reset of data engine": "强制重置数据引擎", + "ID for API use": "API 使用的 ID", + "For number and date formats": "对于数字和日期格式", + "Notify other services on doc changes": "在文档更改时通知其他服务", + "fiddle mode": "演示模式", + "API documentation.": "API 文档。", + "Base doc URL: {{docApiUrl}}": "基于文档 URL:{{docApiUrl}}", + "Coming soon": "即将推出", + "Currency": "货币", + "Default for DateTime columns": "DateTime 列的默认值", + "Locale": "区域设置", + "Manage webhooks": "管理 Webhooks", + "Timing is on": "正在计时" }, "DocumentUsage": { "Attachments Size": "附件大小", @@ -851,7 +882,8 @@ }, "OnBoardingPopups": { "Finish": "结束", - "Next": "下一个" + "Next": "下一个", + "Previous": "上一个" }, "PageWidgetPicker": { "Add to Page": "添加到页面", @@ -1003,7 +1035,8 @@ "Search columns": "搜索列", "By Name": "按名称", "By Date Modified": "按修改日期", - "Custom": "自定义" + "Custom": "自定义", + "Light": "浅色" }, "errorPages": { "Add account": "新增帐户", @@ -1046,7 +1079,8 @@ "Are you sure you want to delete these records?": "确定要删除这些纪录吗?", "Don't show again": "不再显示", "Don't show tips": "不要显示提示", - "Undo to restore": "撤消以恢复" + "Undo to restore": "撤消以恢复", + "TIP": "提示" }, "pages": { "Duplicate Page": "复制页面", @@ -1069,7 +1103,8 @@ "true": "真的", "Field Format": "字段格式", "Multi line": "多行", - "Single line": "单行" + "Single line": "单行", + "Lines": "行" }, "ACLUsers": { "Example Users": "示例用户", @@ -1283,7 +1318,8 @@ "Columns to check when update (separated by ;)": "更新时要检查的列(以 ; 分隔)", "Sorry, not all fields can be edited.": "抱歉,并非所有字段都可编辑。", "Header Authorization": "Header Authorization", - "Memo": "备忘录" + "Memo": "备忘录", + "Ready Column": "就绪列" }, "WelcomeSitePicker": { "Welcome back": "欢迎回来", @@ -1502,7 +1538,8 @@ "checking": "检查中", "Administrator Panel Unavailable": "管理员面板不可用", "No information available": "无可用信息", - "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "Grist 允许配置不同类型的身份验证,包括 SAML 和 OIDC。如果 Grist 可通过网络访问或供多人使用,我们建议启用其中一种。" + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "Grist 允许配置不同类型的身份验证,包括 SAML 和 OIDC。如果 Grist 可通过网络访问或供多人使用,我们建议启用其中一种。", + "Log Streaming": "日志流式传输" }, "Menu": { "Copy": "拷贝", @@ -1518,7 +1555,9 @@ "Building blocks": "构建区块" }, "Toggle": { - "Field Format": "字段格式" + "Field Format": "字段格式", + "Switch": "切换", + "Checkbox": "复选框" }, "CreateTeamModal": { "Cancel": "取消", @@ -1595,7 +1634,18 @@ "Other": "其他", "Start streaming": "开始流式传输", "Save": "保存", - "Learn more": "了解更多" + "Learn more": "了解更多", + "Are you sure you want to delete this streaming destination? This action cannot be undone.": "否确实要删除此流式传输目标?此操作无法撤消。", + "Destination": "目标", + "Add streaming destination": "添加流式传输目标", + "Add destination": "添加目标", + "Cancel": "取消", + "Delete": "删除", + "Delete streaming destination?": "是否删除流式传输目标?", + "Destinations": "目标", + "Edit": "编辑", + "Splunk": "Splunk", + "Edit streaming destination": "编辑流式传输目标" }, "AuditLogsPage": { "Contact us": "联系我们", @@ -1616,6 +1666,172 @@ }, "SupportGristButton": { "Help Center": "帮助中心", - "Close": "关闭" + "Close": "关闭", + "Opted In": "选择加入", + "Admin Panel": "管理面板", + "Opt in to Telemetry": "选择加入 Telemetry", + "Thank you! Your trust and support is greatly appreciated. Opt out any time from the {{link}} in the user menu.": "谢谢!非常感谢您的信任和支持。您可以通过用户菜单中的{{link}}随时退出。", + "Support Grist": "支持 Grist" + }, + "Field": { + "No choices configured": "未配置任何选项", + "No values in show column of referenced table": "引用表的显示列中没有值" + }, + "FormRenderer": { + "Reset": "重置", + "Select...": "选择...", + "Submit": "提交", + "Search": "搜索" + }, + "TimingPage": { + "Column ID": "列 ID", + "Max Time (s)": "最大时间 (s)", + "Loading timing data. Don't close this tab.": "加载计时数据。不要关闭此选项卡。", + "Total Time (s)": "总时间 (s)", + "Table ID": "表 ID", + "Average Time (s)": "平均时间 (s)", + "Formula timer": "公式计时器" + }, + "ChoiceEditor": { + "No choices matching condition": "没有符合条件的选择", + "Error in dropdown condition": "下拉框条件出错", + "No choices to select": "没有可供选择的选项" + }, + "ReferenceUtils": { + "No choices to select": "没有可选的选项", + "Error in dropdown condition": "下拉框条件出错", + "No choices matching condition": "没有符合条件的选项" + }, + "widgetTypesMap": { + "Custom": "自定义", + "Form": "表单", + "Table": "表", + "Card List": "卡片列表", + "Calendar": "日历", + "Card": "卡片", + "Chart": "图表" + }, + "DocTutorial": { + "Finish": "完成", + "Do you want to restart the tutorial? All progress will be lost.": "你想重新开始教程吗?所有进度将会丢失。", + "Click to expand": "点击展开", + "End tutorial": "结束教程", + "Next": "下一个", + "Previous": "上一个", + "Restart": "重启" + }, + "ChoiceListEditor": { + "Error in dropdown condition": "下拉框条件出错", + "No choices matching condition": "没有符合条件的选项", + "No choices to select": "没有可选的选项" + }, + "OnboardingPage": { + "Tell us who you are": "告诉我们你是谁", + "Type here": "在此处输入", + "Welcome": "欢迎", + "Back": "返回", + "Discover Grist in 3 minutes": "3分钟快速入门Grist", + "Go hands-on with the Grist Basics tutorial": "动手实践 Grist 基础教程", + "Go to the tutorial!": "进入教程!", + "Next step": "下一步", + "Skip step": "跳过步骤", + "Skip tutorial": "跳过教程", + "Your role": "您的角色", + "What is your role?": "您的角色是什么?", + "What organization are you with?": "您所在的组织是?", + "What brings you to Grist (you can select multiple)?": "是什么吸引您使用Grist(您可以多选)?", + "Your organization": "您的组织" + }, + "ToggleEnterpriseWidget": { + "Grist Enterprise is **enabled**.": "Grist 企业版**已启用**。", + "An activation key is used to run Grist Enterprise after a trial period\nof 30 days has expired. Get an activation key by [contacting us]({{contactLink}}) today. You do\nnot need an activation key to run Grist Core.\n\nLearn more in our [Help Center]({{helpCenter}}).": "激活密钥用于在30天试用期结束后运行 Grist 企业版。\n立即通过[联系我们]({{contactLink}})获取激活密钥。\n您不需要激活密钥来运行 Grist Core。\n\n在我们的[帮助中心]({{helpCenter}})了解更多。", + "Disable Grist Enterprise": "禁用 Grist 企业版", + "An activation key is used to run Grist Enterprise after a trial period\nof 30 days has expired. Get an activation key by [signing up for Grist\nEnterprise]({{signupLink}}). You do not need an activation key to run\nGrist Core.\n\nLearn more in our [Help Center]({{helpCenter}}).": "激活密钥用于在30天试用期结束后运行 Grist 企业版。\n通过[注册 Grist 企业版]({{signupLink}})获取激活密钥。\n您不需要激活密钥来运行 Grist Core。\n\n在我们的[帮助中心]({{helpCenter}})了解更多信息。", + "Enable Grist Enterprise": "启用 Grist 企业版" + }, + "CustomWidgetGallery": { + "Custom URL": "自定义 URL", + "Developer:": "开发者:", + "(Missing info)": "(缺少信息)", + "Add Widget": "添加小部件", + "Add Your Own Widget": "添加您自己的小部件", + "Add a widget from outside this gallery.": "从这个图库之外添加一个小部件。", + "Cancel": "取消", + "Change Widget": "更改小部件", + "Choose Custom Widget": "选择自定义小部件", + "Community Widget": "社区小部件", + "Grist Widget": "Grist 小部件", + "Last updated:": "上次更新:", + "Learn more about Custom Widgets": "了解有关自定义小部件的更多信息", + "No matching widgets": "没有匹配的小部件", + "Widget URL": "小部件 URL", + "Search": "搜索" + }, + "HomeIntroCards": { + "Finish our basics tutorial": "完成我们的基础教程", + "Help center": "帮助中心", + "Import file": "导入文件", + "Blank document": "空白文档", + "Find solutions and explore more resources {{helpCenterLink}}": "查找解决方案并探索更多资源 {{helpCenterLink}}", + "3 minute video tour": "3分钟视频速览", + "Webinars": "Webinars", + "Templates": "模版", + "Tutorial": "教程", + "Learn more {{webinarsLinks}}": "了解更多 {{webinarsLinks}}", + "Start a new document": "从一个新的文档开始吧" + }, + "ReverseReferenceConfig": { + "Table": "表", + "Target table": "目标表", + "Add two-way reference": "添加双向引用", + "It is the reverse of the reference column {{column}} in table {{table}}.": "它是表{{table}}中引用列{{column}}的反向。", + "Two-way Reference": "双向引用", + "Delete two-way reference?": "是否删除双向引用?", + "Column": "列", + "Delete": "删除", + "Delete column {{column}} in table {{table}}?": "是否删除表{{table}}中的列{{column}}?" + }, + "DropdownConditionConfig": { + "Dropdown Condition": "下拉框条件", + "Invalid columns: {{colIds}}": "无效的列:{{colIds}}", + "Set dropdown condition": "设置下拉框条件" + }, + "DropdownConditionEditor": { + "Enter condition.": "输入条件。" + }, + "OnboardingCards": { + "3 minute video tour": "3分钟视频速览", + "Complete our basics tutorial": "完成我们的基础教程", + "Complete the tutorial": "完成本教程", + "Learn the basic of reference columns, linked widgets, column types, & cards.": "学习参考列、关联小部件、列类型和卡片的基础知识。", + "Learn the basics of reference columns, linked widgets, column types, & cards.": "学习参考列、关联小部件、列类型和卡片的基础知识。" + }, + "ViewLayout": { + "Delete": "删除", + "Delete data and this widget.": "删除数据和此小部件。", + "Table {{tableName}} will no longer be visible": "表{{tableName}}将不再可见", + "raw data page": "原始数据页", + "Keep data and delete widget. Table will remain available in {{rawDataLink}}": "保留数据并删除小部件。表格将在{{rawDataLink}}中仍然可用" + }, + "AdminPanelName": { + "Admin Panel": "管理面板" + }, + "markdown": { + "The toggle is **on**": "此开关已**打开**", + "The toggle is **off**": "此开关已**关闭**" + }, + "markdown.d": { + "The toggle is **off**": "此开关已**关闭**", + "The toggle is **on**": "此开关已**打开**" + }, + "buildReassignModal": { + "Cancel": "取消", + "Each {{targetTable}} record may only be assigned to a single {{sourceTable}} record.": "每个{{targetTable}}记录只能分配给单个{{sourceTable}}记录。", + "Reassign": "重新分配", + "Reassign to new {{sourceTable}} records.": "重新分配给新的{{sourceTable}}记录。", + "Reassign to {{sourceTable}} record {{sourceName}}.": "重新分配给{{sourceTable}}记录{{sourceName}}。", + "Record already assigned_other": "记录已分配", + "{{targetTable}} record {{targetName}} is already assigned to {{sourceTable}} record {{oldSourceName}}.": "{{targetTable}}记录{{targetName}}已分配给{{sourceTable}}记录 {{oldSourceName}}。", + "Record already assigned_one": "记录已分配" } } From 53ce4aa8c765b5e49ff1bb27cb3d8b66b30e9419 Mon Sep 17 00:00:00 2001 From: VojtechKrystek Date: Fri, 13 Dec 2024 07:37:14 +0000 Subject: [PATCH 09/10] Translated using Weblate (Czech) Currently translated at 99.6% (1509 of 1515 strings) Translation: Grist/client Translate-URL: https://hosted.weblate.org/projects/grist/client/cs/ --- static/locales/cs.client.json | 1009 ++++++++++++++++++++++++++++++++- 1 file changed, 999 insertions(+), 10 deletions(-) diff --git a/static/locales/cs.client.json b/static/locales/cs.client.json index 6bd81b0788..440b362fc7 100644 --- a/static/locales/cs.client.json +++ b/static/locales/cs.client.json @@ -148,7 +148,8 @@ "App": { "Description": "Popis", "Memory Error": "Chyba paměti", - "Key": "Klíč" + "Key": "Klíč", + "Translators: please translate this only when your language is ready to be offered to users": "Překladatelé: Překládejte, prosím, až když je váš jazyk připraven k nabídce uživatelům" }, "FilterBar": { "SearchColumns": "Prohledej sloupce", @@ -619,7 +620,10 @@ "Enter redirect URL": "Zadejte adresu URL přesměrování", "No field selected": "Není vybráno žádné pole", "Field title": "Název pole", - "Columns_other": "Sloupce" + "Columns_other": "Sloupce", + "SELECTOR FOR": "SELEKTOR PRO", + "SOURCE DATA": "ZDROJ DAT", + "SELECT BY": "VYBRAT PODLE" }, "Tools": { "Delete document tour?": "Smazat prohlídku dokumentu?", @@ -728,7 +732,9 @@ "OK": "OK" }, "WelcomeSitePicker": { - "You have access to the following Grist sites.": "Máte přístup k následujícím stránkám Grist." + "You have access to the following Grist sites.": "Máte přístup k následujícím stránkám Grist.", + "Welcome back": "Vítejte zpět", + "You can always switch sites using the account menu.": "Můžete vždy přepnout mezi stránkami pomocí nabídky účtu." }, "DuplicateTable": { "Instead of duplicating tables, it's usually better to segment data using linked views. {{link}}": "Místo duplikování tabulek je obvykle lepší segmentovat data pomocí propojených zobrazení. {{link}}", @@ -739,7 +745,7 @@ "GridOptions": { "Horizontal Gridlines": "Vodorovné mřížky", "Grid Options": "Možnosti mřížky", - "Zebra Stripes": "Zebří pruhy", + "Zebra Stripes": "Pruhované řádky", "Vertical Gridlines": "Svislé mřížky" }, "ShareMenu": { @@ -785,7 +791,30 @@ "Revise": "Revize" }, "WelcomeTour": { - "convert to card view, select data, and more.": "Převést na zobrazení karet, vybrat data a další." + "convert to card view, select data, and more.": "Převést na zobrazení karet, vybrat data a další.", + "Double-click or hit {{enter}} on a cell to edit it. ": "Dvojklikněte nebo stiskněte {{enter}} na buňce pro její úpravu. ", + "Help Center": "Centrum podpory", + "Make it relational! Use the {{ref}} type to link tables. ": "Udělejte to relačně! Použijte typ {{ref}} pro propojení tabulek. ", + "Sharing": "Sdílení", + "Welcome to Grist!": "Vítejte na Grist!", + "template library": "knihovna šablon", + "Building up": "Budování", + "Toggle the {{creatorPanel}} to format columns, ": "Přepněte {{creatorPanel}} pro formátování sloupců, ", + "Flying higher": "Letíme výš", + "Reference": "Reference", + "Start with {{equal}} to enter a formula.": "Začněte s {{equal}} pro zadání vzorce.", + "Add New": "Přidat nový", + "Configuring your document": "Konfigurace vašeho dokumentu", + "Use {{addNew}} to add widgets, pages, or import more data. ": "Použijte {{addNew}} pro přidání widgetů, stránek nebo import více dat. ", + "Set formatting options, formulas, or column types, such as dates, choices, or attachments. ": "Nastavte možnosti formátování, vzorce nebo typy sloupců, jako jsou data, volby nebo přílohy. ", + "Customizing columns": "Přizpůsobení sloupců", + "Use {{helpCenter}} for documentation or questions.": "Použijte {{helpCenter}} pro dokumentaci nebo dotazy.", + "Use the Share button ({{share}}) to share the document or export data.": "Použijte tlačítko Sdílet ({{share}}) pro sdílení dokumentu nebo export dat.", + "Browse our {{templateLibrary}} to discover what's possible and get inspired.": "Prozkoumejte naši {{templateLibrary}}, abyste zjistili, co je možné, a získali inspiraci.", + "Enter": "Enter", + "Editing Data": "Úprava dat", + "Share": "Sdílet", + "creator panel": "panel tvůrce" }, "OnBoardingPopups": { "Next": "Další", @@ -818,15 +847,116 @@ "No valid document tour": "Žádná platná prohlídka dokumentu" }, "errorPages": { - "Failed to log in.{{separator}}Please try again or contact support.": "Přihlášení se nezdařilo.{{separator}}Zkuste to znovu nebo kontaktujte podporu." + "Failed to log in.{{separator}}Please try again or contact support.": "Přihlášení se nezdařilo.{{separator}}Zkuste to znovu nebo kontaktujte podporu.", + "An unknown error occurred.": "Došlo k neznámé chybě.", + "There was an error: {{message}}": "Došlo k chybě: {{message}}", + "You are signed in as {{email}}. You can sign in with a different account, or ask an administrator for access.": "Jste přihlášeni jako {{email}}. Můžete se přihlásit pomocí jiného účtu nebo požádat správce o přístup.", + "You do not have access to this organization's documents.": "K dokumentům této organizace nemáte přístup.", + "Form not found": "Formulář nebyl nalezen", + "Powered by": "Powered by", + "Page not found{{suffix}}": "Stránka nebyla nalezena{{suffix}}", + "You are now signed out.": "Nyní jste odhlášeni.", + "Account deleted{{suffix}}": "Účet smazán{{suffix}}", + "The requested page could not be found.{{separator}}Please check the URL and try again.": "Požadovanou stránku se nepodařilo najít.{{separator}}Zkontrolujte prosím adresu URL a zkuste to znovu.", + "Contact support": "Kontaktovat podporu", + "Sign up": "Zaregistrovat se", + "Build your own form": "Vytvořte si vlastní formulář", + "Sign-in failed{{suffix}}": "Přihlášení se nezdařilo{{suffix}}", + "Access denied{{suffix}}": "Přístup odepřen{{suffix}}", + "Error{{suffix}}": "Chyba{{suffix}}", + "Go to main page": "Přejít na hlavní stránku", + "Your account has been deleted.": "Váš účet byl smazán.", + "Sign in": "Přihlásit se", + "Sign in again": "Znovu se přihlaste", + "Sign in to access this organization's documents.": "Pro přístup k dokumentům této organizace se přihlaste.", + "Signed out{{suffix}}": "Odhlášen{{suffix}}", + "Something went wrong": "Něco se pokazilo", + "Add account": "Přidat účet", + "There was an unknown error.": "Došlo k neznámé chybě." }, "GristTooltips": { "You can filter by more than one column.": "Můžete filtrovat podle více než jednoho sloupce.", "Apply conditional formatting to rows based on formulas.": "Použít podmíněné formátování na řádky na základě vzorců.", - "Select the table containing the data to show.": "Vyberte tabulku obsahující data, která chcete zobrazit." + "Select the table containing the data to show.": "Vyberte tabulku obsahující data, která chcete zobrazit.", + "You can choose from widgets available to you in the dropdown, or embed your own by providing its full URL.": "Můžete si vybrat z widgetů, které jsou vám k dispozici v rozevíracím seznamu, nebo vložit svůj vlastní poskytnutím jeho úplné URL.", + "Selecting Data": "Výběr dat", + "A UUID is a randomly-generated string that is useful for unique identifiers and link keys.": "UUID je náhodně generovaný řetězec, který je užitečný pro unikátní identifikátory a klíče odkazů.", + "Build simple forms right in Grist and share in a click with our new widget. {{learnMoreButton}}": "Vytvářejte jednoduché formuláře přímo v Grist a sdílejte je jedním kliknutím s naším novým widgetem. {{learnMoreButton}}", + "To configure your calendar, select columns for start": { + "end dates and event titles. Note each column's type.": "Pro konfiguraci vašeho kalendáře vyberte sloupce pro začáteční a koncové datum a názvy událostí. Poznamenejte si typ každého sloupce." + }, + "Editing Card Layout": "Úprava rozvržení karty", + "Use the 𝚺 icon to create summary (or pivot) tables, for totals or subtotals.": "Použijte ikonu 𝚺 pro vytvoření souhrnných (nebo kontingenčních) tabulek, pro součty nebo mezisoučty.", + "Pinning Filters": "Připnutí filtrů", + "They allow for one record to point (or refer) to another.": "Umožňují jednomu záznamu odkazovat (nebo ukazovat) na jiný.", + "entire": "celý", + "Calendar": "Kalendář", + "Forms are here!": "Formuláře jsou zde!", + "Use the \\u{1D6BA} icon to create summary (or pivot) tables, for totals or subtotals.": "Použijte ikonu \\u{1D6BA} pro vytvoření souhrnných (nebo kontingenčních) tabulek, pro součty nebo mezisoučty.", + "Add New": "Přidat nový", + "Anchor Links": "Odkazy na kotvy", + "This limitation occurs when one column in a two-way reference has the Reference type.": "Toto omezení nastává, když má jeden sloupec v obousměrné referenci typ Reference.", + "Nested Filtering": "Vnořené filtrování", + "Only those rows will appear which match all of the filters.": "Zobrazí se pouze ty řádky, které odpovídají všem filtrům.", + "Can't find the right columns? Click 'Change Widget' to select the table with events data.": "Nemůžete najít správné sloupce? Klikněte na „Změnit widget“ a vyberte tabulku s daty o událostech.", + "Custom Widgets": "Vlastní widgety", + "To allow multiple assignments, change the type of the Reference column to Reference List.": "Pro umožnění více přiřazení změňte typ sloupce Reference na Reference List.", + "This limitation occurs when one end of a two-way reference is configured as a single Reference.": "Toto omezení nastává, když je jeden konec obousměrné reference nakonfigurován jako jednotlivá reference.", + "Apply conditional formatting to cells in this column when formula conditions are met.": "Použijte podmíněné formátování pro buňky v tomto sloupci, když jsou splněny podmínky vzorce.", + "Cells in a reference column always identify an {{entire}} record in that table, but you may select which column from that record to show.": "Buňky ve sloupci reference vždy identifikují {{entire}} záznam v té tabulce, ale můžete vybrat, který sloupec z tohoto záznamu se má zobrazit.", + "Click on “Open row styles” to apply conditional formatting to rows.": "Klikněte na „Otevřít styly řádků“ pro aplikování podmíněného formátování na řádky.", + "Formulas support many Excel functions, full Python syntax, and include a helpful AI Assistant.": "Vzorce podporují mnoho funkcí Excelu, plnou syntaxi Pythonu a zahrnují užitečného asistenta AI.", + "Click the Add New button to create new documents or workspaces, or import data.": "Klikněte na tlačítko Přidat nový pro vytvoření nových dokumentů nebo pracovních prostorů, nebo import dat.", + "Useful for storing the timestamp or author of a new record, data cleaning, and more.": "Užitečné pro ukládání časového razítka nebo autora nového záznamu, čištění dat a další.", + "Formulas that trigger in certain cases, and store the calculated value as data.": "Vzorce, které se spustí v určitých případech, a uloží vypočítanou hodnotu jako data.", + "Link your new widget to an existing widget on this page.": "Propojte svůj nový widget s existujícím widgetem na této stránce.", + "Linking Widgets": "Propojení widgetů", + "Rearrange the fields in your card by dragging and resizing cells.": "Přeuspořádejte pole ve své kartě tažením a změnou velikosti buněk.", + "Community widgets are created and maintained by Grist community members.": "Community widgety jsou vytvářeny a udržovány členy komunity Grist.", + "Creates a reverse column in target table that can be edited from either end.": "Vytvoří zpětný sloupec v cílové tabulce, který lze upravovat z obou stran.", + "Reference Columns": "Referenční sloupce", + "You can choose one of our pre-made widgets or embed your own by providing its full URL.": "Můžete si vybrat jeden z našich předpřipravených widgetů nebo vložit svůj vlastní poskytnutím jeho úplné URL.", + "Reference columns are the key to {{relational}} data in Grist.": "Referenční sloupce jsou klíčem k {{relational}} datům v aplikaci.", + "Select the table to link to.": "Vyberte tabulku, na kterou chcete odkázat.", + "The Raw Data page lists all data tables in your document, including summary tables and tables not included in page layouts.": "Stránka s neopracovanými daty zobrazuje všechny datové tabulky ve vašem dokumentu, včetně souhrnných tabulek a tabulek, které nejsou zahrnuty v rozvrženích stránek.", + "The total size of all data in this document, excluding attachments.": "Celková velikost všech dat v tomto dokumentu, kromě příloh.", + "Raw Data page": "Stránka s neopracovanými daty", + "Updates every 5 minutes.": "Aktualizuje se každých 5 minut.", + "Lookups return data from related tables.": "Vyhledávání vrací data z příbuzných tabulek.", + "Pinned filters are displayed as buttons above the widget.": "Připnuté filtry jsou zobrazeny jako tlačítka nad widgetem.", + "Example: {{example}}": "Příklad: {{example}}", + "Filter displayed dropdown values with a condition.": "Filtrujte zobrazené hodnoty v rozevíracím seznamu podle podmínky.", + "Learn more": "Zjistěte více", + "These rules are applied after all column rules have been processed, if applicable.": "Tato pravidla jsou aplikována po zpracování všech pravidel sloupců, pokud je to relevantní.", + "Clicking {{EyeHideIcon}} in each cell hides the field from this view without deleting it.": "Kliknutím na {{EyeHideIcon}} v každé buňce skryjete pole z tohoto zobrazení, aniž byste jej smazali.", + "Learn more.": "Další informace.", + "This is the secret to Grist's dynamic and productive layouts.": "To je tajemství dynamických a produktivních rozvržení v Grist.", + "Try out changes in a copy, then decide whether to replace the original with your edits.": "Vyzkoušejte změny v kopii a poté se rozhodněte, zda nahradíte originál vašimi úpravami.", + "Unpin to hide the the button while keeping the filter.": "Odepnout, aby se tlačítko skrylo, ale filtr zůstal.", + "relational": "relační", + "Access Rules": "Pravidla přístupu", + "Access rules give you the power to create nuanced rules to determine who can see or edit which parts of your document.": "Pravidla přístupu vám dávají možnost vytvářet jemně laděná pravidla, která určují, kdo může vidět nebo upravovat které části vašeho dokumentu.", + "To make an anchor link that takes the user to a specific cell, click on a row and press {{shortcut}}.": "Pro vytvoření odkazu na kotvu, který přenese uživatele na konkrétní buňku, klikněte na řádek a stiskněte {{shortcut}}.", + "Use reference columns to relate data in different tables.": "Použijte reference sloupce k propojení dat v různých tabulkách.", + "To allow multiple assignments, change the referenced column's type to Reference List.": "Pro umožnění více přiřazení změňte typ referencovaného sloupce na Reference List.", + "Two-way references are not currently supported for Formula or Trigger Formula columns": "Obousměrné odkazy nejsou v současné době podporovány pro sloupce vzorců a spouštěcích vzorců" }, "OnboardingPage": { - "What brings you to Grist (you can select multiple)?": "Co vás přivedlo k Grist (můžete vybrat více možností)?" + "What brings you to Grist (you can select multiple)?": "Co vás přivedlo k Grist (můžete vybrat více možností)?", + "Discover Grist in 3 minutes": "Objevte Grist ve 3 minutách", + "Skip tutorial": "Přeskočit výukový program", + "Welcome": "Vítejte", + "What is your role?": "Jaká je vaše role?", + "Back": "Zpět", + "Next step": "Další krok", + "Go hands-on with the Grist Basics tutorial": "Vyzkoušejte si výukový program Grist Basics v praxi", + "Go to the tutorial!": "Přejděte na výukový program!", + "Skip step": "Přeskočit krok", + "Tell us who you are": "Řekněte nám něco o sobě", + "Type here": "Zadejte zde", + "What organization are you with?": "V jaké organizaci pracujete?", + "Your organization": "Vaše organizace", + "Your role": "Vaše role" }, "SiteSwitcher": { "Create new team site": "Vytvořit nové stránky týmu", @@ -843,15 +973,874 @@ "Import failed: ": "Import se nezdařil: " }, "CreateTeamModal": { - "Choose a name and url for your team site": "Vyberte název a URL pro váš týmový web" + "Choose a name and url for your team site": "Vyberte název a URL pro svůj týmový web", + "Domain name is invalid": "Název domény je neplatný", + "Domain name is required": "Název domény je vyžadován", + "Team name is required": "Název týmu je povinný", + "Work as a Team": "Pracujte jako tým", + "Billing is not supported in grist-core": "Fakturace není v jádře grist podporována", + "Go to your site": "Přejděte na stránku", + "Team name": "Název týmu", + "Create site": "Vytvořit stránku", + "Team site created": "Týmový web vytvořen", + "Team url": "URL adresa týmu", + "Cancel": "Zrušit" }, "LeftPanelCommon": { "Help Center": "Centrum nápovědy" }, "UserManagerModel": { - "Editor": "Editor" + "Editor": "Editor", + "View Only": "Pouze zobrazení", + "View & Edit": "Zobrazit & upravovat", + "Owner": "Majitel", + "In Full": "V plném znění", + "No Default Access": "Bez výchozího přístupu", + "None": "Žádné", + "Viewer": "Návštěvník" }, "SelectionSummary": { "Copied to clipboard": "Zkopírováno do schránky" + }, + "WelcomeQuestions": { + "Welcome to Grist!": "Vítejte na Grist!", + "Other": "Ostatní", + "Sales": "Prodej", + "Finance & Accounting": "Finance a účetnictví", + "Education": "Vzdělávání", + "Media Production": "Mediální produkce", + "Marketing": "Marketing", + "Research": "Výzkum", + "What brings you to Grist? Please help us serve you better.": "Co vás přivedlo ke Grist? Pomozte nám, abychom vám mohli lépe sloužit.", + "Product Development": "Vývoj produktu", + "Type here": "Zadejte zde", + "HR & Management": "Lidské zdroje a řízení", + "IT & Technology": "IT a technologie" + }, + "ConditionalStyle": { + "Row Style": "Styl řádku", + "Add another rule": "Přidat další pravidlo", + "Add conditional style": "Přidat podmíněný styl", + "Error in style rule": "Chyba v pravidle stylu", + "Rule must return True or False": "Pravidlo musí vracet hodnotu Pravda nebo Nepravda", + "Conditional Style": "Podmíněný styl", + "IF...": "KDYŽ..." + }, + "CurrencyPicker": { + "Invalid currency": "Neplatná měna" + }, + "DiscussionEditor": { + "Reply": "Odpovědět", + "Reply to a comment": "Odpovědět na komentář", + "Show resolved comments": "Zobrazit vyřešené komentáře", + "Only current page": "Pouze aktuální stránka", + "Write a comment": "Napsat komentář", + "Comment": "Komentář", + "Edit": "Upravit", + "Marked as resolved": "Označit jako vyřešené", + "Open": "Otevřít", + "Cancel": "Zrušit", + "Only my threads": "Pouze moje vlákna", + "Save": "Uložit", + "Remove": "Odstranit", + "Resolve": "Vyřešit", + "Started discussion": "Diskuse zahájena", + "Showing last {{nb}} comments": "Zobrazuje se posledních {{nb}} komentářů" + }, + "FieldEditor": { + "Unable to finish saving edited cell": "Nelze dokončit uložení upravené buňky", + "It should be impossible to save a plain data value into a formula column": "Mělo by být nemožné uložit běžnou datovou hodnotu do sloupce s vzorcem" + }, + "HyperLinkEditor": { + "[link label] url": "[označení odkazu] URL" + }, + "WebhookPage": { + "Enabled": "Povoleno", + "Status": "Stav", + "Columns to check when update (separated by ;)": "Sloupce ke kontrole při aktualizaci (oddělené ; )", + "URL": "URL", + "Event Types": "Typy událostí", + "Table": "Tabulka", + "Sorry, not all fields can be edited.": "Omlouváme se, ne všechna pole lze upravit.", + "Removed webhook.": "Webhook odstraněn.", + "Ready Column": "Připravený sloupec", + "Name": "Název", + "Clear Queue": "Vymazat frontu", + "Cleared webhook queue.": "Vymazána fronta webhooků.", + "Webhook Settings": "Nastavení webhooku", + "Memo": "Memo", + "Webhook Id": "ID Webhooku", + "Filter for changes in these columns (semicolon-separated ids)": "Filtrujte změny v těchto sloupcích (id oddělená středníkem)", + "Header Authorization": "Hlavička autorizace" + }, + "UserManager": { + "Invite multiple": "Pozvat další", + "{{collaborator}} limit exceeded": "{{collaborator}} překročil limit", + "No default access allows access to be granted to individual documents or workspaces, rather than the full team site.": "Žádný výchozí přístup neumožňuje přístup k jednotlivým dokumentům nebo pracovním prostorům, místo k celému týmovému webu.", + "Your role for this team site": "Vaše úloha na tomto týmovém webu", + "Close": "Zavřít", + "Confirm": "Potvrdit", + "Guest": "Host", + "Invite people to {{resourceType}}": "Pozvat lidi do {{resourceType}}", + "On": "Zapnuto", + "Outside collaborator": "Externí spolupracovník", + "{{limitAt}} of {{limitTop}} {{collaborator}}s": "{{limitAt}} z {{limitTop}} {{collaborator}}ů", + "Team member": "Člen týmu", + "User inherits permissions from {{parent})}. To remove, set 'Inherit access' option to 'None'.": "Uživatel dědí oprávnění od {{rodiče})}. Chcete-li je odebrat, nastavte možnost 'Zdědit přístup' na hodnotu 'Žádný'.", + "No default access allows access to be granted to individual documents or workspaces, rather than the full team site.": "Žádný výchozí přístup neumožňuje přístup k jednotlivým dokumentům nebo pracovním prostorům, místo k celému týmovému webu.", + "Public access": "Veřejný přístup", + "Once you have removed your own access, you will not be able to get it back without assistance from someone else with sufficient access to the {{name}}.": "Po odebrání vlastního přístupu jej nebudete moci získat zpět bez pomoci někoho jiného, kdo má dostatečný přístup k webu {{name}}.", + "Public access: ": "Veřejný přístup: ", + "Save & ": "Uložit & ", + "free collaborator": "volný spolupracovník", + "guest": "host", + "User inherits permissions from {{parent}}. To remove, set 'Inherit access' option to 'None'.": "Uživatel dědí oprávnění z {{parent}}. Chcete-li odebrat, nastavte možnost „Dědit přístup“ na „Žádný“.", + "Public access inherited from {{parent}}. To remove, set 'Inherit access' option to 'None'.": "Veřejný přístup zděděný z {{parent}}. Chcete-li odebrat, nastavte možnost „Dědit přístup“ na „Žádný“.", + "Open Access Rules": "Pravidla otevřeného přístupu", + "You are about to remove your own access to this {{resourceType}}": "Chystáte se odstranit svůj vlastní přístup k tomuto {{resourceType}}", + "Public Access": "Veřejný přístup", + "Create a team to share with more people": "Vytvořte tým pro sdílení s více lidmi", + "Grist support": "Podpora Grist", + "Link copied to clipboard": "Odkaz zkopírován do schránky", + "Manage members of team site": "Správa členů týmu", + "Off": "Vypnuto", + "Copy Link": "Kopírovat odkaz", + "Allow anyone with the link to open.": "Povolit komukoli s odkazem otevřít.", + "Anyone with link ": "Kdokoli s odkazem ", + "Cancel": "Zrušit", + "Remove my access": "Odebrat můj přístup", + "member": "člen", + "team site": "Stránka Týmu", + "Add {{member}} to your team": "Přidejte {{member}} do svého týmu", + "User may not modify their own access.": "Uživatel nesmí upravovat svůj vlastní přístup.", + "User has view access to {{resource}} resulting from manually-set access to resources inside. If removed here, this user will lose access to resources inside.": "Uživatel má přístup k zobrazení {{resource}}, který vyplývá z ručně nastaveného přístupu k vnitřním zdrojům. Pokud bude tento přístup odstraněn, uživatel ztratí přístup k vnitřním zdrojům.", + "Once you have removed your own access, you will not be able to get it back without assistance from someone else with sufficient access to the {{resourceType}}.": "Jakmile odstraníte svůj vlastní přístup, nebudete ho moci získat zpět bez pomoci někoho jiného s dostatečným přístupem k {{resourceType}}.", + "Collaborator": "Spolupracovník", + "Your role for this {{resourceType}}": "Vaše úloha v této oblasti {{resourceType}}" + }, + "SupportGristNudge": { + "Close": "Zavřít", + "Contribute": "Přispět", + "Support Grist": "Podpoř Grist", + "Admin Panel": "Panel správce", + "Support Grist page": "Podpořte stránku Grist", + "Opt in to Telemetry": "Přihlášení k telemetrii", + "Opted In": "Opted In", + "Thank you! Your trust and support is greatly appreciated. Opt out any time from the {{link}} in the user menu.": "Děkujeme! Vaše důvěra a podpora jsou velmi ceněny. Kdykoli se můžete odhlásit pomocí {{link}} v nabídce uživatele.", + "Help Center": "Centrum nápovědy" + }, + "SupportGristPage": { + "Support Grist": "Podpoř Grist", + "Telemetry": "Telemetrie", + "This instance is opted out of telemetry. Only the site administrator has permission to change this.": "Tato instance je odhlášena z telemetrie. Pouze správce webu má oprávnění to změnit.", + "GitHub": "GitHub", + "Manage Sponsorship": "Správa sponzorství", + "Opt out of Telemetry": "Odhlášení od telemetrie", + "You can opt out of telemetry at any time from this page.": "Z telemetrie se můžete odhlásit kdykoli z této stránky.", + "You have opted in to telemetry. Thank you!": "Přihlásili jste se k telemetrii. Děkujeme!", + "Sponsor": "Sponzor", + "Help Center": "Centrum nápovědy", + "Home": "Úvod", + "We only collect usage statistics, as detailed in our {{link}}, never document contents.": "Shromažďujeme pouze statistiky používání, jak je podrobně uvedeno v našem {{link}}, nikdy ne obsah dokumentů.", + "Sponsor Grist Labs on GitHub": "Sponzor Grist Labs na GitHubu", + "This instance is opted in to telemetry. Only the site administrator has permission to change this.": "Tato instance je přihlášena k telemetrii. Pouze správce webu má oprávnění to změnit.", + "Opt in to Telemetry": "Přihlášení k telemetrii", + "GitHub Sponsors page": "Stránka sponzorů GitHub", + "You have opted out of telemetry.": "Odhlásili jste se z telemetrie." + }, + "FloatingPopup": { + "Minimize": "Minimalizovat", + "Maximize": "Maximalizovat" + }, + "MappedFieldsConfig": { + "Select All": "Vybrat vše", + "Unmapped": "Nepřiřazeno", + "Map fields": "Přiřadit pole", + "Mapped": "Přiřazeno", + "Clear": "Vyčistit", + "Unmap fields": "Zrušit přiřazení polí" + }, + "Section": { + "Insert section above": "Vložit sekci nad tuto", + "Insert section below": "Vložit sekci pod tuto" + }, + "ViewLayoutMenu": { + "Copy anchor link": "Kopírovat odkaz kotvy", + "Edit Card Layout": "Úprava rozvržení karty", + "Download as CSV": "Stáhnout jako CSV", + "Delete record": "Odstranit záznam", + "Data selection": "Výběr dat", + "Add to page": "Přidat na stránku", + "Open configuration": "Otevřít konfiguraci", + "Advanced Sort & Filter": "Pokročilé třídění a filtrování", + "Download as XLSX": "Stáhnout jako XLSX", + "Widget options": "Možnosti widgetu", + "Create a form": "Vytvořit formulář", + "Delete widget": "Odstranit widget", + "Show raw data": "Zobrazit surová data", + "Collapse widget": "Sbalit widget", + "Print widget": "Tisknout widget" + }, + "WidgetTitle": { + "Cancel": "Zrušit", + "DATA TABLE NAME": "NÁZEV DATOVÉ TABULKY", + "Save": "Uložit", + "WIDGET DESCRIPTION": "POPIS WIDGETU", + "Override widget title": "Přepsat název widgetu", + "Provide a table name": "Zadejte název tabulky", + "WIDGET TITLE": "NÁZEV WIDGETU" + }, + "menus": { + "Select fields": "Vyberte pole", + "Any": "Jakékoli", + "Choice List": "Seznam možností", + "Reference": "Reference", + "Toggle": "Přepínač", + "Numeric": "Číselné", + "Search columns": "Prohledat sloupce", + "Reference List": "Seznam referencí", + "Attachment": "Příloha", + "Upgrade now": "Upgradujte nyní", + "Date": "Datum", + "DateTime": "Datum a čas", + "* Workspaces are available on team plans. ": "* Pracovní prostory jsou k dispozici v týmových plánech. ", + "Light": "Světlé", + "Integer": "Celé číslo", + "By Name": "Podle názvu", + "Custom": "Vlastní", + "Choice": "Výběr", + "Text": "Text", + "By Date Modified": "Podle data úpravy" + }, + "modals": { + "Delete": "Smazat", + "Are you sure you want to delete this record?": "Opravdu chcete tento záznam odstranit?", + "Don't ask again.": "Neptat se znovu.", + "Don't show again.": "Znovu nezobrazovat.", + "Undo to restore": "Zpět pro obnovení", + "Don't show tips": "Nezobrazovat tipy", + "Cancel": "Zrušit", + "TIP": "TIP", + "Don't show again": "Znovu nezobrazovat", + "Are you sure you want to delete these records?": "Opravdu chcete tyto záznamy odstranit?", + "Got it": "Mám to", + "Ok": "OK", + "Save": "Uložit", + "Dismiss": "Zahodit" + }, + "ChoiceTextBox": { + "CHOICES": "MOŽNOSTI" + }, + "FieldBuilder": { + "Changing multiple column types": "Změna více typů sloupců", + "CELL FORMAT": "FORMÁT BUŇKY", + "Save field settings for {{colId}} as common": "Uložit nastavení pole pro {{colId}} jako běžné", + "DATA FROM TABLE": "DATA Z TABULKY", + "Apply Formula to Data": "Použít vzorec na data", + "Revert field settings for {{colId}} to common": "Vrátit nastavení pole pro {{colId}} na běžné", + "Use separate field settings for {{colId}}": "Použít oddělená nastavení pole pro {{colId}}", + "Mixed format": "Smíšený formát", + "Changing column type": "Změna typu sloupce", + "Mixed types": "Smíšené typy" + }, + "FormView": { + "Unpublish your form?": "Zrušit zveřejnění formuláře?", + "Unpublish": "Zrušit zveřejnění", + "Save your document to publish this form.": "Uložte svůj dokument pro zveřejnění tohoto formuláře.", + "Publish": "Zveřejnit", + "Are you sure you want to reset your form?": "Jste si jisti, že chcete resetovat svůj formulář?", + "Copy code": "Kopírovat kód", + "Copy link": "Kopírovat odkaz", + "Embed this form": "Vložit tento formulář", + "Link copied to clipboard": "Odkaz zkopírován do schránky", + "Preview": "Náhled", + "Share": "Sdílet", + "View": "Zobrazit", + "Publish your form?": "Zveřejnit formulář?", + "Reset form": "Obnovit formulář", + "Share this form": "Sdílet tento formulář", + "Reset": "Resetuj", + "Code copied to clipboard": "Kód zkopírovaný do schránky", + "Anyone with the link below can see the empty form and submit a response.": "Kdokoli s níže uvedeným odkazem může zobrazit prázdný formulář a odeslat odpověď." + }, + "UnmappedFieldsConfig": { + "Mapped": "Přiřazeno", + "Unmapped": "Nepřiřazeno", + "Select All": "Vybrat vše", + "Unmap fields": "Zrušit přiřazení polí", + "Clear": "Vyčistit", + "Map fields": "Přiřadit pole" + }, + "VisibleFieldsConfig": { + "Show {{label}}": "Zobrazit {{label}}", + "Cannot drop items into Hidden Fields": "Není možné přetahovat položky do skrytých polí", + "Hide {{label}}": "Skrýt {{label}}", + "Hidden Fields cannot be reordered": "Skrytá pole nelze přeřazovat", + "Clear": "Vyčistit", + "Hidden {{label}}": "Skryté {{label}}", + "Select All": "Vybrat vše", + "Visible {{label}}": "Viditelné {{label}}" + }, + "ACLUsers": { + "Example Users": "Příklad uživatelů", + "View As": "Zobraz Jako", + "Users from table": "Uživatelé z tabulky" + }, + "CellStyle": { + "Default header style": "Výchozí styl záhlaví", + "Mixed style": "Smíšený styl", + "Open row styles": "Otevřené styly řádku", + "HEADER STYLE": "STYL ZÁHLAVÍ", + "Cell Style": "Styl buňky", + "Header Style": "Styl záhlaví", + "CELL STYLE": "STYL BUŇKY", + "Default cell style": "Výchozí styl buňky" + }, + "ColumnInfo": { + "COLUMN LABEL": "ŠTÍTEK SLOUPCE", + "Cancel": "Zrušit", + "COLUMN DESCRIPTION": "POPIS SLOUPCE", + "COLUMN ID: ": "ID SLOUPCE: ", + "Save": "Uložit" + }, + "FormulaEditor": { + "Expand Editor": "Rozšířit editor", + "use AI Assistant": "použít AI asistenta", + "Enter formula or {{button}}.": "Zadejte vzorec nebo {{button}}.", + "editingFormula is required": "Úprava vzorce je povinná", + "Enter formula.": "Vložit vzorec.", + "Column or field is required": "Sloupec nebo pole je povinné", + "Error in the cell": "Chyba v buňce", + "Errors in all {{numErrors}} cells": "Chyby ve všech {{numErrors}} buňkách", + "Errors in {{numErrors}} of {{numCells}} cells": "Chyby v {{numErrors}} z {{numCells}} buněk" + }, + "ColumnTitle": { + "Add description": "Přidat popis", + "Column ID copied to clipboard": "ID sloupce zkopírováno do schránky", + "Column description": "Popis sloupce", + "Column label": "Štítek sloupce", + "Provide a column label": "Zadejte název sloupce", + "Save": "Uložit", + "Close": "Zavřít", + "COLUMN ID: ": "ID SLOUPCE: ", + "Cancel": "Zrušit" + }, + "FormulaAssistant": { + "Function List": "Seznam funkcí", + "Clear Conversation": "Vymazat konverzaci", + "Data": "Data", + "Cancel": "Zrušit", + "Formula Help. ": "Nápověda k vzorci. ", + "Save": "Uložit", + "You have {{numCredits}} remaining credits.": "Máte {{numCredits}} zbývajících kreditů.", + "Hi, I'm the Grist Formula AI Assistant.": "Ahoj, jsem AI asistent pro Grist vzorce.", + "Code View": "Zobrazit kód", + "I can only help with formulas. I cannot build tables, columns, and views, or write access rules.": "Mohu pomoci pouze s vzorci. Nemohu vytvářet tabulky, sloupce a zobrazení ani psát pravidla přístupu.", + "Learn more": "Zjistěte více", + "For higher limits, contact the site owner.": "Pro vyšší limity se obraťte na majitele webu.", + "For higher limits, {{upgradeNudge}}.": "Pro vyšší limity {{upgradeNudge}}.", + "Grist's AI Formula Assistance. ": "Pomoc s umělou inteligencí Grist's AI Formula. ", + "What do you need help with?": "S čím potřebujete pomoci?", + "See our {{helpFunction}} and {{formulaCheat}}, or visit our {{community}} for more help.": "Podívejte se na naši {{helpFunction}} a {{formulaCheat}}, nebo navštivte naši {{community}} pro více pomoci.", + "Apply": "Použít", + "Sign up for a free Grist account to start using the Formula AI Assistant.": "Zaregistrujte si bezplatný účet Grist a začněte používat asistenta Formula AI.", + "Ask the bot.": "Zeptejte se bota.", + "New Chat": "Nový chat", + "Preview": "Náhled", + "Regenerate": "Přegenerovat", + "Tips": "Tipy", + "There are some things you should know when working with me:": "Existuje několik věcí, které byste měli vědět, když se mnou pracujete:", + "Grist's AI Assistance": "Pomoc s umělou inteligencí společnosti Grist", + "Need help? Our AI assistant can help.": "Potřebujete pomoc? Náš AI asistent vám pomůže.", + "Formula AI Assistant is only available for logged in users.": "AI Asistent pro vzorce je k dispozici pouze pro přihlášené uživatele.", + "You have used all available credits.": "Využili jste všechny dostupné kredity.", + "upgrade your plan": "aktualizujte svůj plán", + "Capabilities": "Schopnosti", + "Community": "Komunita", + "Formula Cheat Sheet": "Přehled vzorců", + "AI Assistant": "AI Asistent", + "Press Enter to apply suggested formula.": "Stisknutím klávesy Enter použijete navržený vzorec.", + "Sign Up for Free": "Zaregistrujte se zdarma", + "upgrade to the Pro Team plan": "přejít na tarif Pro Team" + }, + "SearchModel": { + "Search all tables": "Prohledat všechny tabulky", + "Search all pages": "Prohledat všechny stránky" + }, + "AdminPanel": { + "Checking for updates...": "Kontrola aktualizací...", + "Error": "Chyba", + "Sandbox settings for data engine": "Nastavení Sandboxu pro datový engine", + "Sandboxing": "Sandboxing", + "OK": "OK", + "Updates": "Aktualizace", + "unconfigured": "nekonfigurované", + "Admin Panel": "Panel správce", + "You do not have access to the administrator panel.\nPlease log in as an administrator.": "Nemáte přístup k panelu správce.\nPřihlaste se jako správce.", + "New, Enterprise": "Nové, Enterprise", + "{{firstDestinationName}} + {{- remainingDestinationsCount}} more": "{{firstDestinationName}} + {{- remainingDestinationsCount}} více", + "Session Secret": "Tajný klíč relace (session secret)", + "Key to sign sessions with": "Klíč pro podepisování relací", + "Home": "Úvod", + "Sponsor": "Sponzor", + "Telemetry": "Telemetrie", + "Error checking for updates": "Chyba při kontrole aktualizací", + "Last checked {{time}}": "Poslední kontrola {{time}}", + "Grist is up to date": "Grist je aktuální", + "Learn more.": "Další informace.", + "Newer version available": "K dispozici je novější verze", + "unknown": "neznámý", + "Security Settings": "Nastavení zabezpečení", + "checking": "kontroluji", + "Administrator Panel Unavailable": "Panel správce není k dispozici", + "Log Streaming": "Streamování logů", + "Check failed.": "Kontrola se nezdařila.", + "Results": "Výsledky", + "Contact us": "Kontaktujte nás", + "Grist allows for very powerful formulas, using Python. We recommend setting the environment variable GRIST_SANDBOX_FLAVOR to gvisor if your hardware supports it (most will), to run formulas in each document within a sandbox isolated from other documents and isolated from the network.": "Grist umožňuje velmi mocné vzorce s použitím Pythonu. Doporučujeme nastavit proměnnou prostředí GRIST_SANDBOX_FLAVOR na gvisor, pokud to váš hardware podporuje (většinou ano), aby vzorce v každém dokumentu běžely v pískovišti izolovaném od ostatních dokumentů a izolovaném od sítě.", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "Služba Grist umožňuje konfigurovat různé typy ověřování, včetně SAML a OIDC. Pokud je systém Grist přístupný po síti nebo je zpřístupněn více osobám, doporučujeme jeden z nich povolit.", + "Grist signs user session cookies with a secret key. Please set this key via the environment variable GRIST_SESSION_SECRET. Grist falls back to a hard-coded default when it is not set. We may remove this notice in the future since session IDs have been updated to be inherently cryptographically secure.": "Grist podepisuje cookies uživatelských relací tajným klíčem. Tento klíč nastavte pomocí proměnné prostředí GRIST_SESSION_SECRET. Pokud není nastaven, Grist použije přednastavený tvrdě zakódovaný výchozí klíč. Tuto poznámku můžeme v budoucnu odstranit, protože ID relací generované od verze v1.1.16 jsou inherentně kryptograficky bezpečné.", + "Enable Grist Enterprise": "Povolení Grist Enterprise", + "Audit Logs": "Auditní záznamy", + "Off": "Vypnuto", + "Current": "Aktuální", + "Current version of Grist": "Aktuální verze Grist", + "Help us make Grist better": "Pomozte nám vylepšit Grist", + "Support Grist": "Podpoř Grist", + "Support Grist Labs on GitHub": "Podpořte Grist Labs na GitHubu", + "Version": "Verze", + "Auto-check when this page loads": "Automatická kontrola při načtení této stránky", + "Check now": "Zkontrolovat nyní", + "Grist releases are at ": "Vydání Grist jsou na ", + "No information available": "Nejsou k dispozici žádné informace", + "Authentication": "Autentizace", + "Current authentication method": "Aktuální metoda ověřování", + "Check succeeded.": "Kontrola byla úspěšná.", + "Details": "Podrobnosti", + "No fault detected.": "Žádná chyba nebyla zjištěna.", + "Notes": "Poznámky", + "Self Checks": "Vlastní kontroly", + "Grist allows different types of authentication to be configured, including SAML and OIDC. We recommend enabling one of these if Grist is accessible over the network or being made available to multiple people.": "Služba Grist umožňuje konfigurovat různé typy ověřování, včetně SAML a OIDC. Pokud je systém Grist přístupný po síti nebo je zpřístupněn více osobám, doporučujeme jeden z nich povolit.", + "Grist signs user session cookies with a secret key. Please set this key via the environment variable GRIST_SESSION_SECRET. Grist falls back to a hard-coded default when it is not set. We may remove this notice in the future as session IDs generated since v1.1.16 are inherently cryptographically secure.": "Grist podepisuje soubory cookie relace uživatele tajným klíčem. Tento klíč nastavte pomocí proměnné prostředí GRIST_SESSION_SECRET. Pokud není nastavena, Grist se vrátí k pevně nastavenému výchozímu klíči. Toto upozornění můžeme v budoucnu odstranit, protože identifikátory relací generované od verze 1.1.16 jsou ze své podstaty kryptograficky bezpečné.", + "Or, as a fallback, you can set: {{bootKey}} in the environment and visit: {{url}}": "Nebo můžete jako náhradní řešení nastavit: {{bootKey}} v prostředí a navštívit jej: {{url}}" + }, + "DropdownConditionConfig": { + "Set dropdown condition": "Nastavení rozbalovací podmínky", + "Invalid columns: {{colIds}}": "Neplatné sloupce: {{colIds}}", + "Dropdown Condition": "Podmínka pro rozevírací seznam" + }, + "DropdownConditionEditor": { + "Enter condition.": "Zadejte podmínku." + }, + "duplicatePage": { + "Duplicate page {{pageName}}": "Duplicitní stránka {{pageName}}", + "Note that this does not copy data, but creates another view of the same data.": "Všimněte si, že se nejedná o kopírování dat, ale o vytvoření jiného zobrazení stejných dat." + }, + "breadcrumbs": { + "unsaved": "neuložené", + "fiddle": "Pokusný režim", + "override": "přepsat", + "recovery mode": "režim obnovy", + "snapshot": "snímek", + "You may make edits, but they will create a new copy and will\nnot affect the original document.": "Úpravy můžete provádět, ale vytvoří se nová kopie a\nneovlivní původní dokument." + }, + "search": { + "Find Previous ": "Najít Předchozí ", + "Find Next ": "Najít další ", + "Search": "Hledat", + "Search in document": "Hledat v dokumentu", + "No results": "Žádné výsledky" + }, + "NTextBox": { + "false": "nepravda", + "Field Format": "Formát pole", + "true": "pravda", + "Single line": "Jedno řádkový", + "Lines": "Řádků", + "Multi line": "Více řádkový" + }, + "TypeTransform": { + "Apply": "Použít", + "Update formula (Shift+Enter)": "Aktualizovat vzorec (Shift+Enter)", + "Cancel": "Zrušit", + "Preview": "Náhled", + "Revise": "Revize" + }, + "NumericTextBox": { + "max": "max", + "Field Format": "Formát pole", + "Text": "Text", + "min": "min", + "Spinner": "Spinner", + "Currency": "Měna", + "Default currency ({{defaultCurrency}})": "Výchozí měna ({{defaultCurrency}})", + "Number Format": "Formát čísla", + "Decimals": "Desetinná čísla" + }, + "PagePanels": { + "Open Creator Panel": "Otevřít panel tvůrce", + "Close Creator Panel": "Zavřít panel tvůrce" + }, + "CardContextMenu": { + "Duplicate card": "Duplikovat kartu", + "Insert card above": "Vložte kartu výše", + "Insert card below": "Vložte kartu níže", + "Copy anchor link": "Kopírovat odkaz kotvy", + "Delete card": "Smazat kartu", + "Insert card": "Vložit kartu" + }, + "FormConfig": { + "Default": "Výchozí", + "Options Sort Order": "Možnosti řazení", + "Vertical": "Vertikální", + "Field rules": "Pravidla pole", + "Required field": "Povinné pole", + "Descending": "Sestupně", + "Options Alignment": "Možnosti zarovnání", + "Ascending": "Vzestupně", + "Field Format": "Formát pole", + "Horizontal": "Horizontální", + "Field Rules": "Pravidla pole", + "Select": "Výběr" + }, + "FormErrorPage": { + "Error": "Chyba" + }, + "DateRangeOptions": { + "This week": "Tento týden", + "This year": "Tento rok", + "Last 7 days": "Posledních 7 dní", + "This month": "Tento měsíc", + "Next 7 days": "Příštích 7 dní", + "Last 30 days": "Posledních 30 dní", + "Today": "Dnes", + "Last Week": "Minulý týden" + }, + "FormRenderer": { + "Search": "Hledat", + "Select...": "Vybrat...", + "Submit": "Odeslat", + "Reset": "Resetuj" + }, + "ViewConfigTab": { + "Compact": "Kompaktní", + "Plugin: ": "Plugin: ", + "Edit Card Layout": "Úprava rozvržení karty", + "Big tables may be marked as \"on-demand\" to avoid loading them into the data engine.": "Velké tabulky mohou být označeny jako \"na požádání\", aby se zabránilo jejich načítání do datového engine.", + "Advanced settings": "Pokročilá nastavení", + "Form": "Formulář", + "Unmark On-Demand": "Zrušit označení na vyžádání", + "Make On-Demand": "Vytvářet na vyžádání", + "Blocks": "Bloky", + "Section: ": "Sekce: " + }, + "pages": { + "You do not have edit access to this document": "Nemáte přístup k úpravám tohoto dokumentu", + "Duplicate Page": "Duplicitní stránka", + "Remove": "Odstranit", + "Rename": "Přejmenovat" + }, + "ViewSectionMenu": { + "(modified)": "(upraveno)", + "SORT": "ŘADIT", + "Save": "Uložit", + "FILTER": "FILTR", + "(customized)": "(přizpůsobeno)", + "(empty)": "(prázdný)", + "Revert": "Vrátit", + "Update Sort&Filter settings": "Aktualizovat nastavení řazení a filtru", + "Custom options": "Vlastní možnosti" + }, + "ColumnEditor": { + "COLUMN DESCRIPTION": "POPIS SLOUPCE", + "COLUMN LABEL": "ŠTÍTEK SLOUPCE" + }, + "EditorTooltip": { + "Convert column to formula": "Převést sloupec na vzorec" + }, + "widgetTypesMap": { + "Calendar": "Kalendář", + "Card": "Karta", + "Card List": "Seznam karet", + "Chart": "Graf", + "Custom": "Vlastní", + "Form": "Formulář", + "Table": "Tabulka" + }, + "LanguageMenu": { + "Language": "Jazyk" + }, + "buildViewSectionDom": { + "No row selected in {{title}}": "Žádný řádek není vybrán v {{title}}", + "Not all data is shown": "Nejsou zobrazeny všechny údaje", + "No data": "Žádné údaje" + }, + "Editor": { + "Delete": "Smazat" + }, + "Menu": { + "Unmapped fields": "Nepřiřazená pole", + "Building blocks": "Stavební bloky", + "Columns": "Sloupce", + "Copy": "Kopírovat", + "Cut": "Vyjmout", + "Insert question below": "Vložit otázku pod tuto", + "Paragraph": "Odstavec", + "Separator": "Oddělovač", + "Paste": "Vložit", + "Insert question above": "Vložit otázku nad tuto", + "Header": "Záhlaví" + }, + "FormContainer": { + "Build your own form": "Vytvořte si vlastní formulář", + "Powered by": "Powered by" + }, + "FormModel": { + "Oops! The form you're looking for doesn't exist.": "Oops! Formulář, který hledáte, neexistuje.", + "You don't have access to this form.": "K tomuto formuláři nemáte přístup.", + "There was a problem loading the form.": "Při načítání formuláře došlo k problému.", + "Oops! This form is no longer published.": "Oops! Tento formulář již není publikován." + }, + "FormPage": { + "There was an error submitting your form. Please try again.": "Při odesílání formuláře došlo k chybě. Zkuste to prosím znovu." + }, + "TimingPage": { + "Max Time (s)": "Maximální čas (s)", + "Average Time (s)": "Průměrný čas (s)", + "Table ID": "ID tabulky", + "Number of Calls": "Počet volání", + "Column ID": "ID sloupce", + "Formula timer": "Časovač vzorce", + "Total Time (s)": "Celkový čas (s)", + "Loading timing data. Don't close this tab.": "Načítání časových údajů. Nezavírejte tuto kartu." + }, + "ViewLayout": { + "Delete": "Smazat", + "Table {{tableName}} will no longer be visible": "Tabulka {{tableName}} již nebude viditelná", + "raw data page": "stránka se surovými daty", + "Keep data and delete widget. Table will remain available in {{rawDataLink}}": "Data si ponechte a widget odstraňte. Tabulka zůstane k dispozici v {{rawDataLink}}", + "Delete data and this widget.": "Odstranění dat a tohoto widgetu." + }, + "CustomWidgetGallery": { + "Change Widget": "Změnit widget", + "Last updated:": "Poslední aktualizace:", + "(Missing info)": "(Chybějící informace)", + "Add Widget": "Přidat widget", + "Cancel": "Zrušit", + "Add Your Own Widget": "Přidejte svůj vlastní widget", + "Add a widget from outside this gallery.": "Přidání widgetu mimo tuto galerii.", + "Custom URL": "Vlastní URL", + "Search": "Hledat", + "Widget URL": "Adresa URL widgetu", + "Developer:": "Vývojář:", + "No matching widgets": "Žádné odpovídající widgety", + "Grist Widget": "Widget Grist", + "Choose Custom Widget": "Vybrat vlastní widget", + "Community Widget": "Widget komunity", + "Learn more about Custom Widgets": "Zjistit více o vlastních widgetech" + }, + "ReverseReferenceConfig": { + "Add two-way reference": "Přidání obousměrného odkazu", + "It is the reverse of the reference column {{column}} in table {{table}}.": "Jedná se o opačnou stranu referenčního sloupce {{column}} v tabulce {{table}}.", + "Column": "Sloupec", + "Delete": "Smazat", + "Table": "Tabulka", + "Delete two-way reference?": "Odstranit obousměrný odkaz?", + "Target table": "Cílová tabulka", + "Two-way Reference": "Dvoucestná reference (odkaz)", + "Delete column {{column}} in table {{table}}?": "Smazání sloupce {{column}} v tabulce {{table}}?" + }, + "SupportGristButton": { + "Opt in to Telemetry": "Přihlášení k telemetrii", + "Close": "Zavřít", + "Help Center": "Centrum podpory", + "Admin Panel": "Panel správce", + "Opted In": "Opted In", + "Support Grist": "Podpoř Grist", + "Thank you! Your trust and support is greatly appreciated. Opt out any time from the {{link}} in the user menu.": "Děkujeme! Velmi si vážíme vaší důvěry a podpory. Odhlásit se můžete kdykoli na stránce {{link}} v uživatelské nabídce." + }, + "AuditLogStreamingConfig": { + "Add streaming destination": "Přidat cíl streamování", + "Delete": "Smazat", + "Destination": "Cíl", + "Destinations": "Cíle", + "Edit": "Upravit", + "Edit streaming destination": "Upravit cíl streamování", + "Enter URL": "Zadejte adresu URL", + "Save": "Uložit", + "URL": "URL", + "Delete streaming destination?": "Smazat cíl streamování?", + "Are you sure you want to delete this streaming destination? This action cannot be undone.": "Opravdu chcete tento cíl streamování odstranit? Tuto akci nelze vzít zpět.", + "Start streaming": "Spustit streamování", + "Other": "Ostatní", + "Cancel": "Zrušit", + "Token": "Token", + "Add destination": "Přidat cíl", + "Enter token": "Zadejte token", + "Learn more": "Další informace" + }, + "AuditLogsPage": { + "Contact us": "Kontaktujte nás", + "Audit logs for {{siteName}}": "Protokoly auditu pro {{siteName}}", + "Only site owners may access audit logs.": "K protokolům auditu mají přístup pouze vlastníci stránek.", + "Audit Logs": "Auditní záznamy", + "Log streaming": "Streamování logů", + "Home": "Úvod", + "upgrade your plan": "aktualizujte svůj plán" + }, + "ChoiceListEditor": { + "No choices to select": "Žádné možnosti výběru", + "Error in dropdown condition": "Chyba v rozbalovací podmínce", + "No choices matching condition": "Žádné možnosti odpovídající podmínce" + }, + "Reference": { + "CELL FORMAT": "FORMÁT BUŇKY", + "Row ID": "ID řádku", + "SHOW COLUMN": "ZOBRAZIT SLOUPEC" + }, + "sendToDrive": { + "Sending file to Google Drive": "Odeslání souboru na Disk Google" + }, + "GridView": { + "Click to insert": "Klikněte pro vložení" + }, + "WelcomeCoachingCall": { + "On the call, we'll take the time to understand your needs and tailor the call to you. We can show you the Grist basics, or start working with your data right away to build the dashboards you need.": "Během hovoru si uděláme čas na pochopení vašich potřeb a přizpůsobíme hovor vám. Můžeme vám ukázat základy Grist, nebo rovnou začít pracovat s vašimi daty a vytvořit potřebné dashboardy.", + "free coaching call": "Bezplatná konzultace", + "Maybe Later": "Možná později", + "Schedule your {{freeCoachingCall}} with a member of our team.": "Naplánujte si svou {{freeCoachingCall}} s členem našeho týmu.", + "Schedule Call": "Naplánovat hovor" + }, + "FormSuccessPage": { + "Form Submitted": "Formulář odeslán", + "Thank you! Your response has been recorded.": "Děkujeme! Vaše odpověď byla zaznamenána.", + "Submit new response": "Odeslat novou odpověď" + }, + "Columns": { + "Remove Column": "Odstranit sloupec" + }, + "ChoiceEditor": { + "No choices to select": "Žádné možnosti výběru", + "Error in dropdown condition": "Chyba v rozbalovací podmínce", + "No choices matching condition": "Žádné možnosti odpovídající podmínce" + }, + "Field": { + "No choices configured": "Žádné možnosti nejsou nakonfigurovány", + "No values in show column of referenced table": "Žádné hodnoty ve sloupci zobrazení odkazované tabulky" + }, + "DocTutorial": { + "End tutorial": "Konec výukového programu", + "Finish": "Dokončit", + "Next": "Další", + "Previous": "Předchozí", + "Restart": "Restartovat", + "Click to expand": "Klikněte pro rozšíření", + "Do you want to restart the tutorial? All progress will be lost.": "Chcete znovu spustit výukový program? Veškerý pokrok bude ztracen." + }, + "OnboardingCards": { + "Learn the basics of reference columns, linked widgets, column types, & cards.": "Naučte se základy referenčních sloupců, propojených widgetů, typů sloupců a karet.", + "Complete the tutorial": "Dokončete výukový program", + "3 minute video tour": "3minutová videoprohlídka", + "Complete our basics tutorial": "Dokončete náš základní tutoriál", + "Learn the basic of reference columns, linked widgets, column types, & cards.": "Naučte se základy referenčních sloupců, propojených widgetů, typů sloupců a karet." + }, + "ToggleEnterpriseWidget": { + "Disable Grist Enterprise": "Zakázat Grist Enterprise", + "Enable Grist Enterprise": "Povolení Grist Enterprise", + "Grist Enterprise is **enabled**.": "Služba Grist Enterprise je **povolena**.", + "An activation key is used to run Grist Enterprise after a trial period\nof 30 days has expired. Get an activation key by [signing up for Grist\nEnterprise]({{signupLink}}). You do not need an activation key to run\nGrist Core.\n\nLearn more in our [Help Center]({{helpCenter}}).": "Aktivační klíč slouží ke spuštění aplikace Grist Enterprise po zkušební době.\npo uplynutí 30denní zkušební doby. Aktivační klíč získáte [přihlášením se k odběru služby Grist\nEnterprise]({{signupLink}}). Aktivační klíč nepotřebujete k tomu, abyste mohli spustit službu\nGrist Core.\n\nVíce informací najdete v našem [Centru nápovědy]({{helpCenter}}).", + "An activation key is used to run Grist Enterprise after a trial period\nof 30 days has expired. Get an activation key by [contacting us]({{contactLink}}) today. You do\nnot need an activation key to run Grist Core.\n\nLearn more in our [Help Center]({{helpCenter}}).": "Aktivační klíč slouží ke spuštění aplikace Grist Enterprise po zkušební době.\npo uplynutí 30denní zkušební doby. Aktivační klíč získáte [kontaktujte nás]({{contactLink}}) ještě dnes. Uděláte to\naktivační klíč nepotřebujete ke spuštění aplikace Grist Core.\n\nVíce informací se dozvíte v našem [Centru nápovědy]({{helpCenter}})." + }, + "AdminPanelName": { + "Admin Panel": "Panel správce" + }, + "markdown.d": { + "# New Markdown Function\n *\n * We can _write_ [the usual Markdown](https:": { + "": { + "markdownguide.org) *inside*\n * a Grainjs element.": "# Nová funkce Markdown\n *\n * Můžeme _zapsat_ [obvyklý Markdown](https://markdownguide.org) *vnitř*\n * prvku Grainjs." + } + }, + "The toggle is **on**": "Přepínač je **zapnutý**", + "The toggle is **off**": "Přepínač je **vypnutý**" + }, + "HomeIntroCards": { + "Learn more {{webinarsLinks}}": "Zjistěte více {{webinarsLinks}}", + "Templates": "Šablony", + "Start a new document": "Začněte novým dokumentem", + "Blank document": "Prázdný dokument", + "Find solutions and explore more resources {{helpCenterLink}}": "Najděte řešení a prozkoumejte další zdroje {{helpCenterLink}}", + "3 minute video tour": "3 minutová videoprohlídka", + "Finish our basics tutorial": "Dokončete náš základní výukový návod", + "Help center": "Centrum podpory", + "Tutorial": "Návod", + "Webinars": "Webináře", + "Import file": "Importovat soubor" + }, + "buildReassignModal": { + "Cancel": "Zrušit", + "Reassign": "Nové přiřazení", + "Each {{targetTable}} record may only be assigned to a single {{sourceTable}} record.": "Každý záznam na {{targetTable}} může být přiřazen pouze jednomu záznamu na {{sourceTable}}.", + "Reassign to new {{sourceTable}} records.": "Přeřazení do nových záznamů {{sourceTable}}.", + "Record already assigned_one": "Záznam je již přiřazen", + "Reassign to {{sourceTable}} record {{sourceName}}.": "Přeřazení do tabulky {{sourceTable}} do záznamu {{sourceName}} .", + "Record already assigned_other": "Záznam je již přiřazen", + "{{targetTable}} record {{targetName}} is already assigned to {{sourceTable}} record {{oldSourceName}}.": "{{targetTable}} záznam {{targetName}} je již přiřazen k záznamu {{sourceTable}} {{oldSourceName}} ." + }, + "ValidationPanel": { + "Update formula (Shift+Enter)": "Aktualizovat vzorec (Shift+Enter)", + "Rule {{length}}": "Pravidlo {{length}}" + }, + "FloatingEditor": { + "Collapse Editor": "Sbalit editor" + }, + "HiddenQuestionConfig": { + "Hidden fields": "Skrytá pole" + }, + "CustomView": { + "To use this widget, please map all non-optional columns from the creator panel on the right.": "Pro použití tohoto widgetu prosím přiřaďte všechna nepovinná pole v panelu tvůrce na pravé straně.", + "Some required columns aren't mapped": "Některá požadovaná pole nejsou přiřazena" + }, + "ReferenceUtils": { + "Error in dropdown condition": "Chyba v rozbalovací podmínce", + "No choices matching condition": "Žádné možnosti neodpovídají podmínce", + "No choices to select": "Žádné možnosti výběru" + }, + "ViewAsBanner": { + "UnknownUser": "Neznámý uživatel" + }, + "Clipboard": { + "Unavailable Command": "Nedostupný příkaz", + "Got it": "Mám to" + }, + "FieldContextMenu": { + "Clear field": "Vymazat pole", + "Copy anchor link": "Kopírovat odkaz kotvy", + "Cut": "Vyjmout", + "Hide field": "Skrýt pole", + "Paste": "Vložit", + "Copy": "Kopírovat" + }, + "searchDropdown": { + "Search": "Hledat" + }, + "Toggle": { + "Checkbox": "Zaškrtávací tlačítko", + "Field Format": "Formát pole", + "Switch": "Přepínač" + }, + "DescriptionConfig": { + "DESCRIPTION": "POPIS" + }, + "DescriptionTextArea": { + "DESCRIPTION": "POPIS" + }, + "markdown": { + "The toggle is **on**": "Přepínač je **zapnutý**", + "# New Markdown Function\n *\n * We can _write_ [the usual Markdown](https:": { + "": { + "markdownguide.org) *inside*\n * a Grainjs element.": "# Nová funkce Markdown\n *\n * Můžeme _zapsat_ [obvyklý Markdown](https://markdownguide.org) *vnitř*\n * prvku Grainjs." + } + }, + "The toggle is **off**": "Přepínač je **vypnut**" } } From 793f0ee083952b90a5903083ad47afef70396336 Mon Sep 17 00:00:00 2001 From: Florent Date: Fri, 13 Dec 2024 18:51:12 +0100 Subject: [PATCH 10/10] Extract Groups management from HomeDBManager (#1342) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Context I pave the way to implement Groups for SCIM. ## Proposed solution Refactoring for moving the Groups Management outside of HomeDBManager. ## Related issues #870 ## Has this been tested? - [ ] 👍 yes, I added tests to the test suite - [ ] 💭 no, because this PR is a draft and still needs work - [x] 🙅 no, because this is not relevant here (no methods are used elsewhere than in `lib/homedb`) - [ ] 🙋 no, because I need help --- app/gen-server/lib/homedb/GroupsManager.ts | 275 +++++++++++++++++++++ app/gen-server/lib/homedb/HomeDBManager.ts | 229 +++-------------- app/gen-server/lib/homedb/Interfaces.ts | 8 + 3 files changed, 311 insertions(+), 201 deletions(-) create mode 100644 app/gen-server/lib/homedb/GroupsManager.ts diff --git a/app/gen-server/lib/homedb/GroupsManager.ts b/app/gen-server/lib/homedb/GroupsManager.ts new file mode 100644 index 0000000000..0257f4ff2c --- /dev/null +++ b/app/gen-server/lib/homedb/GroupsManager.ts @@ -0,0 +1,275 @@ +import * as roles from "app/common/roles"; +import { AclRule } from "app/gen-server/entity/AclRule"; +import { Document } from "app/gen-server/entity/Document"; +import { Group } from "app/gen-server/entity/Group"; +import { GroupDescriptor, NonGuestGroup, Resource } from "app/gen-server/lib/homedb/Interfaces"; +import { Organization } from "app/gen-server/entity/Organization"; +import { Permissions } from 'app/gen-server/lib/Permissions'; +import { User } from "app/gen-server/entity/User"; +import { Workspace } from "app/gen-server/entity/Workspace"; + +import { EntityManager } from "typeorm"; + +/** + * Class responsible for Groups and Roles Management. + * + * It's only meant to be used by HomeDBManager. If you want to use one of its (instance or static) methods, + * please make an indirection which passes through HomeDBManager. + */ +export class GroupsManager { + // All groups. + public get defaultGroups(): GroupDescriptor[] { + return this._defaultGroups; + } + + // Groups whose permissions are inherited from parent resource to child resources. + public get defaultBasicGroups(): GroupDescriptor[] { + return this._defaultGroups + .filter(_grpDesc => _grpDesc.nestParent); + } + + // Groups that are common to all resources. + public get defaultCommonGroups(): GroupDescriptor[] { + return this._defaultGroups + .filter(_grpDesc => !_grpDesc.orgOnly); + } + + public get defaultGroupNames(): roles.Role[] { + return this._defaultGroups.map(_grpDesc => _grpDesc.name); + } + + public get defaultBasicGroupNames(): roles.BasicRole[] { + return this.defaultBasicGroups + .map(_grpDesc => _grpDesc.name) as roles.BasicRole[]; + } + + public get defaultNonGuestGroupNames(): roles.NonGuestRole[] { + return this._defaultGroups + .filter(_grpDesc => _grpDesc.name !== roles.GUEST) + .map(_grpDesc => _grpDesc.name) as roles.NonGuestRole[]; + } + + public get defaultCommonGroupNames(): roles.NonMemberRole[] { + return this.defaultCommonGroups + .map(_grpDesc => _grpDesc.name) as roles.NonMemberRole[]; + } + + // Returns a map of userIds to the user's strongest default role on the given resource. + // The resource's aclRules, groups, and memberUsers must be populated. + public static getMemberUserRoles(res: Resource, allowRoles: T[]): {[userId: string]: T} { + // Add the users to a map to ensure uniqueness. (A user may be present in + // more than one group) + const userMap: {[userId: string]: T} = {}; + (res.aclRules as AclRule[]).forEach((aclRule: AclRule) => { + const role = aclRule.group.name as T; + if (allowRoles.includes(role)) { + // Map the users to remove sensitive information from the result and + // to add the group names. + aclRule.group.memberUsers.forEach((u: User) => { + // If the user is already present in another group, use the more + // powerful role name. + userMap[u.id] = userMap[u.id] ? roles.getStrongestRole(userMap[u.id], role) : role; + }); + } + }); + return userMap; + } + + /** + * Five aclRules, each with one group (with the names 'owners', 'editors', 'viewers', + * 'guests', and 'members') are created by default on every new entity (Organization, + * Workspace, Document). These special groups are documented in the _defaultGroups + * constant below. + * + * When a child resource is created under a parent (i.e. when a new Workspace is created + * under an Organization), special groups with a truthy 'nestParent' property are set up + * to include in their memberGroups a single group on initialization - the parent's + * corresponding special group. Special groups with a falsy 'nextParent' property are + * empty on intialization. + * + * NOTE: The groups are ordered from most to least permissive, and should remain that way. + * TODO: app/common/roles already contains an ordering of the default roles. Usage should + * be consolidated. + */ + private readonly _defaultGroups: GroupDescriptor[] = [{ + name: roles.OWNER, + permissions: Permissions.OWNER, + nestParent: true + }, { + name: roles.EDITOR, + permissions: Permissions.EDITOR, + nestParent: true + }, { + name: roles.VIEWER, + permissions: Permissions.VIEW, + nestParent: true + }, { + name: roles.GUEST, + permissions: Permissions.VIEW, + nestParent: false + }, { + name: roles.MEMBER, + permissions: Permissions.VIEW, + nestParent: false, + orgOnly: true + }]; + + /** + * Helper for adjusting acl inheritance rules. Given an array of top-level groups from the + * resource of interest, and an array of inherited groups belonging to the parent resource, + * moves the inherited groups to the group with the destination name or lower, if their + * permission level is lower. If the destination group name is omitted, the groups are + * moved to their original inheritance locations. If the destination group name is null, + * the groups are all removed and there is no access inheritance to this resource. + * Returns the updated array of top-level groups. These returned groups should be saved + * to update the group inheritance in the database. + * + * For all passed-in groups, their .memberGroups will be reset. For + * the basic roles (owner | editor | viewer), these will get updated + * to include inheritedGroups, with roles reduced to dest when dest + * is given. All of the basic roles must be present among + * groups. Any non-basic roles present among inheritedGroups will be + * ignored. + * + * Does not modify inheritedGroups. + */ + public moveInheritedGroups( + groups: NonGuestGroup[], inheritedGroups: Group[], dest?: roles.BasicRole|null + ): void { + // Limit scope to those inheritedGroups that have basic roles (viewers, editors, owners). + inheritedGroups = inheritedGroups.filter(group => roles.isBasicRole(group.name)); + + // NOTE that the special names constant is ordered from least to most permissive. + const reverseDefaultNames = this.defaultBasicGroupNames.reverse(); + + // The destination must be a reserved inheritance group or null. + if (dest && !reverseDefaultNames.includes(dest)) { + throw new Error('moveInheritedGroups called with invalid destination name'); + } + + // Mapping from group names to top-level groups + const topGroups: {[groupName: string]: NonGuestGroup} = {}; + groups.forEach(grp => { + // Note that this has a side effect of initializing the memberGroups arrays. + grp.memberGroups = []; + topGroups[grp.name] = grp; + }); + + // The destFunc maps from an inherited group to its required top-level group name. + const destFunc = (inherited: Group) => + dest === null ? null : reverseDefaultNames.find(sp => sp === inherited.name || sp === dest); + + // Place inherited groups (this has the side-effect of updating member groups) + inheritedGroups.forEach(grp => { + if (!roles.isBasicRole(grp.name)) { + // We filtered out such groups at the start of this method, but just in case... + throw new Error(`${grp.name} is not an inheritable group`); + } + const moveTo = destFunc(grp); + if (moveTo) { + topGroups[moveTo].memberGroups.push(grp); + } + }); + } + + /** + * Update the set of users in a group. TypeORM's .save() method appears to be + * unreliable for a ManyToMany relation with a table with a multi-column primary + * key, so we make the update using explicit deletes and inserts. + */ + public async setGroupUsers(manager: EntityManager, groupId: number, usersBefore: User[], + usersAfter: User[]) { + const userIdsBefore = new Set(usersBefore.map(u => u.id)); + const userIdsAfter = new Set(usersAfter.map(u => u.id)); + const toDelete = [...userIdsBefore].filter(id => !userIdsAfter.has(id)); + const toAdd = [...userIdsAfter].filter(id => !userIdsBefore.has(id)); + if (toDelete.length > 0) { + await manager.createQueryBuilder() + .delete() + .from('group_users') + .whereInIds(toDelete.map(id => ({user_id: id, group_id: groupId}))) + .execute(); + } + if (toAdd.length > 0) { + await manager.createQueryBuilder() + .insert() + // Since we are adding new records in group_users, we may get a duplicate key error if two documents + // are added at the same time (even in transaction, since we are not blocking the whole table). + .orIgnore() + .into('group_users') + .values(toAdd.map(id => ({user_id: id, group_id: groupId}))) + .execute(); + } + } + + /** + * Returns a name to group mapping for the standard groups. Useful when adding a new child + * entity. Finds and includes the correct parent groups as member groups. + */ + public createGroups(inherit?: Organization|Workspace, ownerId?: number): {[name: string]: Group} { + const groupMap: {[name: string]: Group} = {}; + this.defaultGroups.forEach(groupProps => { + if (!groupProps.orgOnly || !inherit) { + // Skip this group if it's an org only group and the resource inherits from a parent. + const group = new Group(); + group.name = groupProps.name; + if (inherit) { + this.setInheritance(group, inherit); + } + groupMap[groupProps.name] = group; + } + }); + // Add the owner explicitly to the owner group. + if (ownerId) { + const ownerGroup = groupMap[roles.OWNER]; + const user = new User(); + user.id = ownerId; + ownerGroup.memberUsers = [user]; + } + return groupMap; + } + + // Sets the given group to inherit the groups in the given parent resource. + public setInheritance(group: Group, parent: Organization|Workspace) { + // Add the parent groups to the group + const groupProps = this.defaultGroups.find(special => special.name === group.name); + if (!groupProps) { + throw new Error(`Non-standard group passed to _addInheritance: ${group.name}`); + } + if (groupProps.nestParent) { + const parentGroups = (parent.aclRules as AclRule[]).map((_aclRule: AclRule) => _aclRule.group); + const inheritGroup = parentGroups.find((_parentGroup: Group) => _parentGroup.name === group.name); + if (!inheritGroup) { + throw new Error(`Special group ${group.name} not found in ${parent.name} for inheritance`); + } + group.memberGroups = [inheritGroup]; + } + } + + // Returns the most permissive default role that does not have more permissions than the passed + // in argument. + public getRoleFromPermissions(permissions: number): roles.Role|null { + permissions &= ~Permissions.PUBLIC; // tslint:disable-line:no-bitwise + const group = this.defaultBasicGroups.find(grp => + (permissions & grp.permissions) === grp.permissions); // tslint:disable-line:no-bitwise + return group ? group.name : null; + } + + // Returns the maxInheritedRole group name set on a resource. + // The resource's aclRules, groups, and memberGroups must be populated. + public getMaxInheritedRole(res: Workspace|Document): roles.BasicRole|null { + const groups = (res.aclRules as AclRule[]).map((_aclRule: AclRule) => _aclRule.group); + let maxInheritedRole: roles.NonGuestRole|null = null; + for (const name of this.defaultBasicGroupNames) { + const group = groups.find(_grp => _grp.name === name); + if (!group) { + throw new Error(`Error in _getMaxInheritedRole: group ${name} not found in ${res.name}`); + } + if (group.memberGroups.length > 0) { + maxInheritedRole = name; + break; + } + } + return roles.getEffectiveRole(maxInheritedRole); + } +} diff --git a/app/gen-server/lib/homedb/HomeDBManager.ts b/app/gen-server/lib/homedb/HomeDBManager.ts index 721b18633f..d4593ebda4 100644 --- a/app/gen-server/lib/homedb/HomeDBManager.ts +++ b/app/gen-server/lib/homedb/HomeDBManager.ts @@ -46,6 +46,7 @@ import { AvailableUsers, DocumentAccessChanges, GetUserOptions, + GroupDescriptor, NonGuestGroup, OrgAccessChanges, PreviousAndCurrent, @@ -87,6 +88,7 @@ import { WhereExpressionBuilder } from "typeorm"; import {v4 as uuidv4} from "uuid"; +import { GroupsManager } from './GroupsManager'; // Support transactions in Sqlite in async code. This is a monkey patch, affecting // the prototypes of various TypeORM classes. @@ -155,13 +157,6 @@ interface QueryOptions { // potentially overriding markPermissions. } -interface GroupDescriptor { - readonly name: roles.Role; - readonly permissions: number; - readonly nestParent: boolean; - readonly orgOnly?: boolean; -} - // Information about a change in billable users. export interface UserChange { userId: number; // who initiated the change @@ -258,6 +253,7 @@ export type BillingOptions = Partial _grpDesc.nestParent); + return this._groupsManager.defaultBasicGroups; } - // Groups that are common to all resources. public get defaultCommonGroups(): GroupDescriptor[] { - return this._defaultGroups - .filter(_grpDesc => !_grpDesc.orgOnly); + return this._groupsManager.defaultCommonGroups; } public get defaultGroupNames(): roles.Role[] { - return this._defaultGroups.map(_grpDesc => _grpDesc.name); + return this._groupsManager.defaultGroupNames; } public get defaultBasicGroupNames(): roles.BasicRole[] { - return this.defaultBasicGroups - .map(_grpDesc => _grpDesc.name) as roles.BasicRole[]; + return this._groupsManager.defaultBasicGroupNames; } public get defaultNonGuestGroupNames(): roles.NonGuestRole[] { - return this._defaultGroups - .filter(_grpDesc => _grpDesc.name !== roles.GUEST) - .map(_grpDesc => _grpDesc.name) as roles.NonGuestRole[]; + return this._groupsManager.defaultNonGuestGroupNames; } public get defaultCommonGroupNames(): roles.NonMemberRole[] { @@ -1221,7 +1170,7 @@ export class HomeDBManager extends EventEmitter { org.owner = user; } // Create the special initial permission groups for the new org. - const groupMap = this._createGroups(); + const groupMap = this._groupsManager.createGroups(); org.aclRules = this.defaultGroups.map(_grpDesc => { // Get the special group with the name needed for this ACL Rule const group = groupMap[_grpDesc.name]; @@ -1629,7 +1578,7 @@ export class HomeDBManager extends EventEmitter { doc.workspace = workspace; doc.createdBy = scope.userId; // Create the special initial permission groups for the new workspace. - const groupMap = this._createGroups(workspace, scope.userId); + const groupMap = this._groupsManager.createGroups(workspace, scope.userId); doc.aclRules = this.defaultCommonGroups.map(_grpDesc => { // Get the special group with the name needed for this ACL Rule const group = groupMap[_grpDesc.name]; @@ -2190,7 +2139,7 @@ export class HomeDBManager extends EventEmitter { return queryResult; } const org: Organization = queryResult.data; - const userRoleMap = getMemberUserRoles(org, this.defaultGroupNames); + const userRoleMap = GroupsManager.getMemberUserRoles(org, this.defaultGroupNames); const users = UsersManager.getResourceUsers(org).filter(u => userRoleMap[u.id]).map(u => { const access = userRoleMap[u.id]; return { @@ -2239,13 +2188,13 @@ export class HomeDBManager extends EventEmitter { return queryFailure; } - const wsMap = getMemberUserRoles(workspace, this.defaultCommonGroupNames); + const wsMap = GroupsManager.getMemberUserRoles(workspace, this.defaultCommonGroupNames); // Also fetch the organization ACLs so we can determine inherited rights. // The orgMap gives the org access inherited by each user. - const orgMap = getMemberUserRoles(org, this.defaultBasicGroupNames); - const orgMapWithMembership = getMemberUserRoles(org, this.defaultGroupNames); + const orgMap = GroupsManager.getMemberUserRoles(org, this.defaultBasicGroupNames); + const orgMapWithMembership = GroupsManager.getMemberUserRoles(org, this.defaultGroupNames); // Iterate through the org since all users will be in the org. const users: UserAccessData[] = UsersManager.getResourceUsers([workspace, org]).map(u => { @@ -2258,7 +2207,7 @@ export class HomeDBManager extends EventEmitter { isMember: orgAccess && orgAccess !== 'guests', }; }); - const maxInheritedRole = this._getMaxInheritedRole(workspace); + const maxInheritedRole = this._groupsManager.getMaxInheritedRole(workspace); const personal = this._filterAccessData(scope, users, maxInheritedRole); return { status: 200, @@ -2297,16 +2246,16 @@ export class HomeDBManager extends EventEmitter { const {trunkId, forkId, forkUserId, snapshotId} = parseUrlId(scope.urlId); const doc = await this._loadDocAccess({...scope, urlId: trunkId}, Permissions.VIEW); - const docMap = getMemberUserRoles(doc, this.defaultCommonGroupNames); + const docMap = GroupsManager.getMemberUserRoles(doc, this.defaultCommonGroupNames); // The wsMap gives the ws access inherited by each user. - const wsMap = getMemberUserRoles(doc.workspace, this.defaultBasicGroupNames); + const wsMap = GroupsManager.getMemberUserRoles(doc.workspace, this.defaultBasicGroupNames); // The orgMap gives the org access inherited by each user. - const orgMap = getMemberUserRoles(doc.workspace.org, this.defaultBasicGroupNames); + const orgMap = GroupsManager.getMemberUserRoles(doc.workspace.org, this.defaultBasicGroupNames); // The orgMapWithMembership gives the full access to the org for each user, including // the "members" level, which grants no default inheritable access but allows the user // to be added freely to workspaces and documents. - const orgMapWithMembership = getMemberUserRoles(doc.workspace.org, this.defaultGroupNames); - const wsMaxInheritedRole = this._getMaxInheritedRole(doc.workspace); + const orgMapWithMembership = GroupsManager.getMemberUserRoles(doc.workspace.org, this.defaultGroupNames); + const wsMaxInheritedRole = this._groupsManager.getMaxInheritedRole(doc.workspace); // Iterate through the org since all users will be in the org. let users: UserAccessData[] = UsersManager.getResourceUsers([doc, doc.workspace, doc.workspace.org]).map(u => { // Merge the strongest roles from the resource and parent resources. Note that the parent @@ -2324,7 +2273,7 @@ export class HomeDBManager extends EventEmitter { isSupport: u.id === this._usersManager.getSupportUserId() ? true : undefined, }; }); - let maxInheritedRole = this._getMaxInheritedRole(doc); + let maxInheritedRole = this._groupsManager.getMaxInheritedRole(doc); if (options?.excludeUsersWithoutAccess) { users = users.filter(user => { @@ -2447,7 +2396,7 @@ export class HomeDBManager extends EventEmitter { // Update the doc groups to inherit the groups in the new workspace/org. // Any previously custom added members remain in the doc groups. doc.aclRules.forEach(aclRule => { - this._setInheritance(aclRule.group, workspace); + this._groupsManager.setInheritance(aclRule.group, workspace); }); // If the org is changing, remove all urlIds for this doc, since there could be // conflicts in the new org. @@ -3360,7 +3309,7 @@ export class HomeDBManager extends EventEmitter { .leftJoinAndSelect('doc_groups.memberUsers', 'doc_users') .andWhere('doc_users.id is not null'); const wsWithDocs = await wsWithDocsQuery.getOne(); - await this._setGroupUsers(manager, wsGuestGroup.id, wsGuestGroup.memberUsers, + await this._groupsManager.setGroupUsers(manager, wsGuestGroup.id, wsGuestGroup.memberUsers, this._usersManager.filterEveryone( UsersManager.getResourceUsers(wsWithDocs?.docs || []) ) @@ -3394,41 +3343,11 @@ export class HomeDBManager extends EventEmitter { throw new Error(`_repairOrgGuests error: found ${orgGroups.length} ${roles.GUEST} ACL group(s)`); } const orgGuestGroup = orgGroups[0]!; - await this._setGroupUsers(manager, orgGuestGroup.id, orgGuestGroup.memberUsers, + await this._groupsManager.setGroupUsers(manager, orgGuestGroup.id, orgGuestGroup.memberUsers, this._usersManager.filterEveryone(UsersManager.getResourceUsers(org.workspaces))); }); } - /** - * Update the set of users in a group. TypeORM's .save() method appears to be - * unreliable for a ManyToMany relation with a table with a multi-column primary - * key, so we make the update using explicit deletes and inserts. - */ - private async _setGroupUsers(manager: EntityManager, groupId: number, usersBefore: User[], - usersAfter: User[]) { - const userIdsBefore = new Set(usersBefore.map(u => u.id)); - const userIdsAfter = new Set(usersAfter.map(u => u.id)); - const toDelete = [...userIdsBefore].filter(id => !userIdsAfter.has(id)); - const toAdd = [...userIdsAfter].filter(id => !userIdsBefore.has(id)); - if (toDelete.length > 0) { - await manager.createQueryBuilder() - .delete() - .from('group_users') - .whereInIds(toDelete.map(id => ({user_id: id, group_id: groupId}))) - .execute(); - } - if (toAdd.length > 0) { - await manager.createQueryBuilder() - .insert() - // Since we are adding new records in group_users, we may get a duplicate key error if two documents - // are added at the same time (even in transaction, since we are not blocking the whole table). - .orIgnore() - .into('group_users') - .values(toAdd.map(id => ({user_id: id, group_id: groupId}))) - .execute(); - } - } - /** * Creates, initializes and saves a workspace in the given org with the given properties. * Product limits on number of workspaces allowed in org are not checked. @@ -3446,7 +3365,7 @@ export class HomeDBManager extends EventEmitter { workspace.org = org; // Create the special initial permission groups for the new workspace. // Optionally add the owner to the workspace. - const groupMap = this._createGroups(org, ownerId); + const groupMap = this._groupsManager.createGroups(org, ownerId); workspace.aclRules = this.defaultCommonGroups.map(_grpDesc => { // Get the special group with the name needed for this ACL Rule const group = groupMap[_grpDesc.name]; @@ -3985,50 +3904,6 @@ export class HomeDBManager extends EventEmitter { }); } - /** - * Returns a name to group mapping for the standard groups. Useful when adding a new child - * entity. Finds and includes the correct parent groups as member groups. - */ - private _createGroups(inherit?: Organization|Workspace, ownerId?: number): {[name: string]: Group} { - const groupMap: {[name: string]: Group} = {}; - this.defaultGroups.forEach(groupProps => { - if (!groupProps.orgOnly || !inherit) { - // Skip this group if it's an org only group and the resource inherits from a parent. - const group = new Group(); - group.name = groupProps.name; - if (inherit) { - this._setInheritance(group, inherit); - } - groupMap[groupProps.name] = group; - } - }); - // Add the owner explicitly to the owner group. - if (ownerId) { - const ownerGroup = groupMap[roles.OWNER]; - const user = new User(); - user.id = ownerId; - ownerGroup.memberUsers = [user]; - } - return groupMap; - } - - // Sets the given group to inherit the groups in the given parent resource. - private _setInheritance(group: Group, parent: Organization|Workspace) { - // Add the parent groups to the group - const groupProps = this.defaultGroups.find(special => special.name === group.name); - if (!groupProps) { - throw new Error(`Non-standard group passed to _addInheritance: ${group.name}`); - } - if (groupProps.nestParent) { - const parentGroups = (parent.aclRules as AclRule[]).map((_aclRule: AclRule) => _aclRule.group); - const inheritGroup = parentGroups.find((_parentGroup: Group) => _parentGroup.name === group.name); - if (!inheritGroup) { - throw new Error(`Special group ${group.name} not found in ${parent.name} for inheritance`); - } - group.memberGroups = [inheritGroup]; - } - } - // Return a QueryResult reflecting the output of a query builder. // If a rawQueryBuilder is supplied, it is used to make the query, // but then the original queryBuilder is used to interpret the results @@ -4183,7 +4058,7 @@ export class HomeDBManager extends EventEmitter { } if (typeof subValue === 'number' || !subValue) { // Find the first special group for which the user has all permissions. - value.access = this._getRoleFromPermissions(subValue || 0); + value.access = this._groupsManager.getRoleFromPermissions(subValue || 0); if (subValue & Permissions.PUBLIC) { // tslint:disable-line:no-bitwise value.public = true; } @@ -4191,7 +4066,7 @@ export class HomeDBManager extends EventEmitter { // Resource may be accessed by multiple users, encoded in JSON. const accessOptions: AccessOption[] = readJson(this._dbType, subValue); value.accessOptions = accessOptions.map(option => ({ - access: this._getRoleFromPermissions(option.perms), ...option + access: this._groupsManager.getRoleFromPermissions(option.perms), ...option })); } delete value.permissions; // permissions is not specified in the api, so we drop it. @@ -4217,33 +4092,6 @@ export class HomeDBManager extends EventEmitter { return entity.accessOptions.length === 0; } - // Returns the most permissive default role that does not have more permissions than the passed - // in argument. - private _getRoleFromPermissions(permissions: number): roles.Role|null { - permissions &= ~Permissions.PUBLIC; // tslint:disable-line:no-bitwise - const group = this.defaultBasicGroups.find(grp => - (permissions & grp.permissions) === grp.permissions); // tslint:disable-line:no-bitwise - return group ? group.name : null; - } - - // Returns the maxInheritedRole group name set on a resource. - // The resource's aclRules, groups, and memberGroups must be populated. - private _getMaxInheritedRole(res: Workspace|Document): roles.BasicRole|null { - const groups = (res.aclRules as AclRule[]).map((_aclRule: AclRule) => _aclRule.group); - let maxInheritedRole: roles.NonGuestRole|null = null; - for (const name of this.defaultBasicGroupNames) { - const group = groups.find(_grp => _grp.name === name); - if (!group) { - throw new Error(`Error in _getMaxInheritedRole: group ${name} not found in ${res.name}`); - } - if (group.memberGroups.length > 0) { - maxInheritedRole = name; - break; - } - } - return roles.getEffectiveRole(maxInheritedRole); - } - /** * Return a query builder to check if we have access to the given resource. * Tests the given permission-level access, defaulting to view permission. @@ -4803,27 +4651,6 @@ async function verifyEntity( }; } -// Returns a map of userIds to the user's strongest default role on the given resource. -// The resource's aclRules, groups, and memberUsers must be populated. -function getMemberUserRoles(res: Resource, allowRoles: T[]): {[userId: string]: T} { - // Add the users to a map to ensure uniqueness. (A user may be present in - // more than one group) - const userMap: {[userId: string]: T} = {}; - (res.aclRules as AclRule[]).forEach((aclRule: AclRule) => { - const role = aclRule.group.name as T; - if (allowRoles.includes(role)) { - // Map the users to remove sensitive information from the result and - // to add the group names. - aclRule.group.memberUsers.forEach((u: User) => { - // If the user is already present in another group, use the more - // powerful role name. - userMap[u.id] = userMap[u.id] ? roles.getStrongestRole(userMap[u.id], role) : role; - }); - } - }); - return userMap; -} - // Extract a human-readable name for the type of entity being selected. function getFrom(queryBuilder: SelectQueryBuilder): string { const alias = queryBuilder.expressionMap.mainAlias; diff --git a/app/gen-server/lib/homedb/Interfaces.ts b/app/gen-server/lib/homedb/Interfaces.ts index 2bb65a57fe..bb125f9b4b 100644 --- a/app/gen-server/lib/homedb/Interfaces.ts +++ b/app/gen-server/lib/homedb/Interfaces.ts @@ -61,6 +61,13 @@ export interface OrgAccessChanges { accessChanges: Omit; } +export interface GroupDescriptor { + readonly name: roles.Role; + readonly permissions: number; + readonly nestParent: boolean; + readonly orgOnly?: boolean; +} + interface AccessChanges { publicAccess: roles.NonGuestRole | null; maxInheritedAccess: roles.BasicRole | null; @@ -70,3 +77,4 @@ interface AccessChanges { } >; } +