diff --git a/app/views/budgets/show.html.erb b/app/views/budgets/show.html.erb
index 33eea2d50..768cf7727 100644
--- a/app/views/budgets/show.html.erb
+++ b/app/views/budgets/show.html.erb
@@ -9,8 +9,10 @@
<%# Top Section: Donut and Summary side by side %>
<%# Budget Donut %>
-
- <% if @budget.available_to_allocate.negative? %>
+
<%= render "settings/hostings/openai_settings" %>
@@ -19,8 +22,10 @@
<%= settings_section title: t(".sync_settings") do %>
<%= render "settings/hostings/sync_settings" %>
<% end %>
-<%= settings_section title: t(".invites") do %>
- <%= render "settings/hostings/invite_code_settings" %>
+<% if Current.user.super_admin? %>
+ <%= settings_section title: t(".invites") do %>
+ <%= render "settings/hostings/invite_code_settings" %>
+ <% end %>
<% end %>
<%= settings_section title: t(".danger_zone") do %>
<%= render "settings/hostings/danger_zone_settings" %>
diff --git a/app/views/settings/payments/show.html.erb b/app/views/settings/payments/show.html.erb
index b395545a3..ac0184cea 100644
--- a/app/views/settings/payments/show.html.erb
+++ b/app/views/settings/payments/show.html.erb
@@ -13,7 +13,7 @@
<% if @family.has_active_subscription? %>
- Currently on the <%= @family.subscription.name %>.
+ Currently on the <%= @family.subscription.name %>.
<% if @family.next_payment_date %>
<% if @family.subscription_pending_cancellation? %>
@@ -25,7 +25,7 @@
<% elsif @family.trialing? %>
- Currently using the open demo of <%= product_name %>
+ Currently using the open demo of <%= product_name %>
(Data will be deleted in <%= @family.days_left_in_trial %> days)
diff --git a/app/views/settings/preferences/show.html.erb b/app/views/settings/preferences/show.html.erb
index a9f44e029..22faaf010 100644
--- a/app/views/settings/preferences/show.html.erb
+++ b/app/views/settings/preferences/show.html.erb
@@ -57,30 +57,21 @@
<% end %>
-<%= settings_section title: t(".theme_title"), subtitle: t(".theme_subtitle") do %>
-
data-money-field-precision-value="<%= options[:precision] %>"<% end %>
+ <% if options[:step].present? %>data-money-field-step-value="<%= options[:step] %>"<% end %>>
<% if options[:label_tooltip] %>
diff --git a/app/views/transactions/_summary.html.erb b/app/views/transactions/_summary.html.erb
index 818283f2c..b77853cc7 100644
--- a/app/views/transactions/_summary.html.erb
+++ b/app/views/transactions/_summary.html.erb
@@ -1,19 +1,39 @@
<%# locals: (totals:) %>
+<%# Show Inflow/Outflow labels only when the result set contains exclusively transfers
+ (income and expense are both $0). For mixed filters (e.g. Expense+Transfer),
+ we keep Income/Expenses labels — transfer amounts aren't included in the summary
+ bar in that case, though the transaction list still shows both types. %>
+<% show_transfers = totals.income_money.zero? && totals.expense_money.zero? &&
+ (totals.transfer_inflow_money.amount > 0 || totals.transfer_outflow_money.amount > 0) %>
+ <% end %>
+ <% end %>
+
+ <%# For split child, show parent info and actions %>
+ <% if @entry.split_child? %>
+ <% dialog.with_section(title: t("splits.child.title"), open: true) do %>
+
<% end %>
diff --git a/charts/sure/.gitignore b/charts/sure/.gitignore
new file mode 100644
index 000000000..58f68018c
--- /dev/null
+++ b/charts/sure/.gitignore
@@ -0,0 +1,2 @@
+# Vendored subchart tarballs (regenerated by `helm dependency build`)
+charts/
diff --git a/charts/sure/CHANGELOG.md b/charts/sure/CHANGELOG.md
index f0d636ba5..628fcdce7 100644
--- a/charts/sure/CHANGELOG.md
+++ b/charts/sure/CHANGELOG.md
@@ -5,22 +5,49 @@ All notable changes to the Sure Helm chart will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-### [0.0.0], [0.6.5]
+## [0.6.9-alpha] - 2026-03-24
+
+### Changed
+- Bumped `pipelock.image.tag` from `1.5.0` to `2.0.0`
+- CI: Pipelock GitHub Action updated from `@v1` to `@v2`
+- Compose example image changed from pinned `1.5.0` to `latest` with pin comment
+- Bumped `pipelock.image.tag` from `0.3.1` to `0.3.2`
+- Consolidated `compose.example.pipelock.yml` into `compose.example.ai.yml` — Pipelock now runs alongside Ollama in one compose file with health checks, config volume mount, and MCP env vars (`MCP_API_TOKEN`, `MCP_USER_EMAIL`)
+- CI: Pipelock scan `fail-on-findings` changed from `false` to `true`; added `exclude-paths` for locale help text false positives
### Added
+- **Pipelock v2.0 features**:
+ - `pipelock.trustedDomains`: first-class support for allowing internal services whose public DNS resolves to private IPs (prevents SSRF false positives)
+ - `pipelock.mcpToolPolicy.redirectProfiles`: route matched MCP tool calls to audited handler programs instead of blocking
+ - Updated `pipelock.example.yaml` with v2.0 feature documentation (trusted domains, redirect profiles, attack simulation, security scoring)
+ - Updated `extraConfig` comment to mention new v2.0 sections (sandbox, reverse_proxy)
+- Pipelock v2.0 highlights available via `extraConfig`: process sandbox (Linux/macOS), generic HTTP reverse proxy, adaptive enforcement exempt domains, kill switch API port isolation
+- **Bumped** `pipelock.image.tag` from `0.3.2` to `1.5.0`
+- **Pipelock security proxy** (`pipelock.enabled=true`): Separate Deployment + Service that provides two scanning layers
+ - **Forward proxy** (port 8888): Scans outbound HTTPS from Faraday-based clients (e.g. ruby-openai). Auto-injects `HTTPS_PROXY`/`HTTP_PROXY`/`NO_PROXY` env vars into app pods
+ - **MCP reverse proxy** (port 8889): Scans inbound MCP traffic for DLP, prompt injection, and tool poisoning. Auto-computes upstream URL via `sure.pipelockUpstream` helper
+ - **WebSocket proxy** configuration support (disabled by default)
+ - ConfigMap with scanning config (DLP, prompt injection detection, MCP input/tool scanning, response scanning)
+ - ConfigMap checksum annotation for automatic pod restart on config changes
+ - Helm helpers: `sure.pipelockImage`, `sure.pipelockUpstream`
+ - Health and readiness probes on the Pipelock deployment
+ - `imagePullSecrets` with fallback to app-level secrets
+ - Boolean safety: uses `hasKey` to prevent Helm's `default` from swallowing explicit `false`
+ - Configurable ports via `forwardProxy.port` and `mcpProxy.port` (single source of truth across Service, Deployment, and env vars)
+- `pipelock.example.yaml` reference config for Docker Compose deployments
+- **Pipelock operational hardening**:
+ - `pipelock.serviceMonitor`: Prometheus Operator ServiceMonitor for /metrics on the proxy port
+ - `pipelock.ingress`: Ingress template for MCP reverse proxy (external AI assistant access in k8s)
+ - `pipelock.pdb`: PodDisruptionBudget with minAvailable/maxUnavailable mutual exclusion guard
+ - `pipelock.topologySpreadConstraints`: Pod spread across nodes
+ - `pipelock.logging`: Structured logging config (format, output, include_allowed, include_blocked)
+ - `pipelock.extraConfig`: Escape hatch for additional pipelock.yaml config sections
+ - `pipelock.requireForExternalAssistant`: Helm guard that fails when externalAssistant is enabled without pipelock
+ - Component label (`app.kubernetes.io/component: pipelock`) on Service metadata for selector targeting
+ - NOTES.txt: Pipelock health check commands, MCP access info, security notes, metrics status
-- First (nightly/test) releases via
-
-### [0.6.6] - 2025-12-31
-
-### Added
-
-- First version/release that aligns versions with monorepo
-- CNPG: render `Cluster.spec.backup` from `cnpg.cluster.backup`.
- - If `backup.method` is omitted and `backup.volumeSnapshot` is present, the chart will infer `method: volumeSnapshot`.
- - For snapshot backups, `backup.volumeSnapshot.className` is required (template fails early if missing).
- - Example-only keys like `backup.ttl` and `backup.volumeSnapshot.enabled` are stripped to avoid CRD warnings.
-- CNPG: render `Cluster.spec.plugins` from `cnpg.cluster.plugins` (enables barman-cloud plugin / WAL archiver configuration).
+### Fixed
+- Renamed `_asserts.tpl` to `asserts.tpl` — Helm's `_` prefix convention prevented guards from executing
## [0.6.7-alpha] - 2026-01-10
@@ -33,6 +60,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Production-ready HA timeouts: 200ms connect, 1s read/write, 3 reconnection attempts
- Backward compatible with existing `REDIS_URL` deployments
+### [0.6.6] - 2025-12-31
+
+### Added
+
+- First version/release that aligns versions with monorepo
+- CNPG: render `Cluster.spec.backup` from `cnpg.cluster.backup`.
+ - If `backup.method` is omitted and `backup.volumeSnapshot` is present, the chart will infer `method: volumeSnapshot`.
+ - For snapshot backups, `backup.volumeSnapshot.className` is required (template fails early if missing).
+ - Example-only keys like `backup.ttl` and `backup.volumeSnapshot.enabled` are stripped to avoid CRD warnings.
+- CNPG: render `Cluster.spec.plugins` from `cnpg.cluster.plugins` (enables barman-cloud plugin / WAL archiver configuration).
+
+### [0.0.0], [0.6.5]
+
+### Added
+
+- First (nightly/test) releases via
+
## Notes
- Chart version and application version are kept in sync
- Requires Kubernetes >= 1.25.0
diff --git a/charts/sure/Chart.lock b/charts/sure/Chart.lock
new file mode 100644
index 000000000..5cdb2cfcc
--- /dev/null
+++ b/charts/sure/Chart.lock
@@ -0,0 +1,9 @@
+dependencies:
+- name: cloudnative-pg
+ repository: https://cloudnative-pg.github.io/charts
+ version: 0.27.1
+- name: redis-operator
+ repository: https://ot-container-kit.github.io/helm-charts
+ version: 0.23.0
+digest: sha256:5ffa5c535cb5feea62a29665045a79da8a5d058c3ba11c4db37a4afa97563e3e
+generated: "2026-03-02T21:16:32.757224371-05:00"
diff --git a/charts/sure/Chart.yaml b/charts/sure/Chart.yaml
index 6c2cd02eb..cee4ff9b6 100644
--- a/charts/sure/Chart.yaml
+++ b/charts/sure/Chart.yaml
@@ -2,8 +2,8 @@ apiVersion: v2
name: sure
description: Official Helm chart for deploying the Sure Rails app (web + Sidekiq) on Kubernetes with optional HA PostgreSQL (CloudNativePG) and Redis.
type: application
-version: 0.6.8-alpha.14
-appVersion: "0.6.8-alpha.14"
+version: 0.7.0-alpha.3
+appVersion: "0.7.0-alpha.3"
kubeVersion: ">=1.25.0-0"
diff --git a/charts/sure/README.md b/charts/sure/README.md
index 6a004d153..28b8e164f 100644
--- a/charts/sure/README.md
+++ b/charts/sure/README.md
@@ -12,6 +12,7 @@ Official Helm chart for deploying the Sure Rails application on Kubernetes. It s
- Optional subcharts
- CloudNativePG (operator) + Cluster CR for PostgreSQL with HA support
- OT-CONTAINER-KIT redis-operator for Redis HA (replication by default, optional Sentinel)
+- Optional Pipelock AI agent security proxy (forward proxy + MCP reverse proxy with DLP, prompt injection, and tool poisoning detection)
- Security best practices: runAsNonRoot, readOnlyRootFilesystem, optional existingSecret, no hardcoded secrets
- Scalability
- Replicas (web/worker), resources, topology spread constraints
@@ -637,6 +638,156 @@ hpa:
targetCPUUtilizationPercentage: 70
```
+## Pipelock (AI agent security proxy)
+
+[Pipelock](https://github.com/luckyPipewrench/pipelock) is an optional security proxy that scans AI agent traffic for secret exfiltration, prompt injection, tool poisoning, and SSRF. It runs as a separate Deployment with two listeners:
+
+- **Forward proxy** (port 8888): Scans outbound HTTPS from Faraday-based AI clients. Auto-injected via `HTTPS_PROXY` env vars when enabled.
+- **MCP reverse proxy** (port 8889): Scans inbound MCP traffic from external AI assistants.
+
+v2.0 adds enhanced tool poisoning detection (full JSON schema scanning), per-read kill switch preemption on long-lived connections, trusted domain allowlisting, and MCP tool redirect profiles. Process sandboxing and attack simulation are also available via `extraConfig` and CLI.
+
+### Enabling Pipelock
+
+```yaml
+pipelock:
+ enabled: true
+ image:
+ tag: "2.0.0"
+ mode: balanced # strict, balanced, or audit
+```
+
+### Trusted domains
+
+In Kubernetes, services often have public DNS records that resolve to private IPs. Without `trustedDomains`, the SSRF scanner blocks this legitimate traffic. Add trusted domains to allow them through:
+
+```yaml
+pipelock:
+ trustedDomains:
+ - "api.internal.example.com"
+ - "*.corp.example.com"
+```
+
+### MCP tool redirect profiles
+
+Redirect profiles route matched MCP tool calls to an audited handler program instead of blocking. The handler returns a synthetic MCP response, keeping the agent's flow intact while enforcing policy:
+
+```yaml
+pipelock:
+ mcpToolPolicy:
+ enabled: true
+ action: redirect # or use per-rule action overrides
+ redirectProfiles:
+ safe-fetch:
+ exec: ["/pipelock", "internal-redirect", "fetch-proxy"]
+ reason: "Route fetch calls through audited proxy"
+```
+
+### Validating your config
+
+Pipelock v2.0 includes two CLI tools for config validation:
+
+```bash
+# Run 24 synthetic attack scenarios against your config
+pipelock simulate --config pipelock.yaml
+
+# Score your config's security posture (0-100)
+pipelock audit score --config pipelock.yaml
+```
+
+### Exposing MCP to external AI assistants
+
+When running in Kubernetes, external AI agents need network access to the MCP reverse proxy port. Enable the Pipelock Ingress:
+
+```yaml
+pipelock:
+ enabled: true
+ ingress:
+ enabled: true
+ className: nginx
+ annotations:
+ cert-manager.io/cluster-issuer: letsencrypt
+ hosts:
+ - host: pipelock.example.com
+ paths:
+ - path: /
+ pathType: Prefix
+ tls:
+ - hosts: [pipelock.example.com]
+ secretName: pipelock-tls
+```
+
+Security: The Ingress routes to port `mcp` (8889). Ensure `MCP_API_TOKEN` is set so the MCP endpoint requires authentication. The Ingress itself does not add auth.
+
+### Metrics (Prometheus)
+
+Pipelock exposes `/metrics` on the forward proxy port. Enable scraping with a ServiceMonitor:
+
+```yaml
+pipelock:
+ serviceMonitor:
+ enabled: true
+ interval: 30s
+ portName: proxy # matches Service port name for 8888
+ additionalLabels:
+ release: prometheus # match your Prometheus Operator selector
+```
+
+### PodDisruptionBudget
+
+Protect Pipelock from node drains:
+
+```yaml
+pipelock:
+ pdb:
+ enabled: true
+ maxUnavailable: 1 # safe for single-replica; use minAvailable when replicas > 1
+```
+
+Note: Setting `minAvailable` with `replicas=1` blocks eviction entirely. Use `maxUnavailable` for single-replica deployments.
+
+### Structured logging
+
+```yaml
+pipelock:
+ logging:
+ format: json # json or text
+ output: stdout
+ includeAllowed: false
+ includeBlocked: true
+```
+
+### Extra config (escape hatch)
+
+For Pipelock config sections not covered by structured values (session profiling, data budgets, kill switch, sandbox, reverse proxy, adaptive enforcement, etc.), use `extraConfig`:
+
+```yaml
+pipelock:
+ extraConfig:
+ session_profiling:
+ enabled: true
+ max_sessions: 1000
+ adaptive_enforcement:
+ enabled: true
+ exempt_domains:
+ - "*.example.com"
+ kill_switch:
+ api_listen: ":9090" # dedicated port for kill switch API
+```
+
+These are appended verbatim to `pipelock.yaml`. Do not duplicate keys already rendered by the chart.
+
+### Requiring Pipelock for external assistants
+
+To enforce that Pipelock is enabled whenever the external AI assistant feature is active:
+
+```yaml
+pipelock:
+ requireForExternalAssistant: true
+```
+
+This causes `helm template` / `helm install` to fail if `rails.externalAssistant.enabled=true` and `pipelock.enabled=false`. Note: this only guards the `externalAssistant` path. Direct MCP access via `MCP_API_TOKEN` is configured through env vars and not detectable from Helm values.
+
## Security Notes
- Never commit secrets in `values.yaml`. Use `rails.existingSecret` or a tool like Sealed Secrets.
@@ -660,6 +811,7 @@ See `values.yaml` for the complete configuration surface, including:
- `migrations.*`: strategy job or initContainer
- `simplefin.encryption.*`: enable + backfill options
- `cronjobs.*`: custom CronJobs
+- `pipelock.*`: AI agent security proxy (forward proxy, MCP reverse proxy, DLP, injection scanning, trusted domains, tool redirect profiles, logging, serviceMonitor, ingress, PDB, extraConfig)
- `service.*`, `ingress.*`, `serviceMonitor.*`, `hpa.*`
## Helm tests
diff --git a/charts/sure/templates/NOTES.txt b/charts/sure/templates/NOTES.txt
index f4c340b9d..e9e46850f 100644
--- a/charts/sure/templates/NOTES.txt
+++ b/charts/sure/templates/NOTES.txt
@@ -41,7 +41,40 @@ Troubleshooting
- For CloudNativePG, verify the RW service exists and the primary is Ready.
- For redis-operator, verify the RedisSentinel CR reports Ready and that the master service resolves.
+{{- if .Values.pipelock.enabled }}
+
+Pipelock (AI agent security proxy)
+-----------------------------------
+5) Verify pipelock is running:
+ kubectl rollout status deploy/{{ include "sure.fullname" . }}-pipelock -n {{ .Release.Namespace }}
+ kubectl logs deploy/{{ include "sure.fullname" . }}-pipelock -n {{ .Release.Namespace }} --tail=20
+
+6) MCP access for external AI assistants:
+{{- if .Values.pipelock.ingress.enabled }}
+{{- range .Values.pipelock.ingress.hosts }}
+ - Ingress: http{{ if $.Values.pipelock.ingress.tls }}s{{ end }}://{{ .host }}
+{{- end }}
+{{- else }}
+ - No Ingress configured. Port-forward for local access:
+ kubectl port-forward -n {{ .Release.Namespace }} svc/{{ include "sure.fullname" . }}-pipelock 8889:{{ .Values.pipelock.mcpProxy.port | default 8889 }}
+{{- end }}
+
+ Security: Enable TLS on the pipelock Ingress and ensure MCP_API_TOKEN is set.
+ The MCP endpoint requires authentication but the Ingress does not add it.
+
+7) Metrics:
+{{- if .Values.pipelock.serviceMonitor.enabled }}
+ - ServiceMonitor enabled — Prometheus will scrape /metrics on port {{ .Values.pipelock.serviceMonitor.portName }}.
+{{- else }}
+ - ServiceMonitor not enabled. Metrics are available at http://:{{ .Values.pipelock.forwardProxy.port | default 8888 }}/metrics
+ Enable with: pipelock.serviceMonitor.enabled=true
+{{- end }}
+{{- end }}
+
Security reminder
-----------------
- For production, prefer immutable image tags (for example, image.tag=v1.2.3) instead of 'latest'.
-- Provide secrets via an existing Kubernetes Secret or a secret manager (External Secrets, Sealed Secrets).
\ No newline at end of file
+- Provide secrets via an existing Kubernetes Secret or a secret manager (External Secrets, Sealed Secrets).
+{{- if .Values.pipelock.enabled }}
+- When exposing MCP to external AI assistants, always enable pipelock to scan inbound traffic.
+{{- end }}
\ No newline at end of file
diff --git a/charts/sure/templates/_asserts.tpl b/charts/sure/templates/_asserts.tpl
deleted file mode 100644
index de1cf0bbb..000000000
--- a/charts/sure/templates/_asserts.tpl
+++ /dev/null
@@ -1,7 +0,0 @@
-{{/*
-Mutual exclusivity and configuration guards
-*/}}
-
-{{- if and .Values.redisOperator.managed.enabled .Values.redisSimple.enabled -}}
-{{- fail "Invalid configuration: Both redisOperator.managed.enabled and redisSimple.enabled are true. Enable only one in-cluster Redis provider." -}}
-{{- end -}}
diff --git a/charts/sure/templates/_env.tpl b/charts/sure/templates/_env.tpl
index ccf0c1b69..e50ab7ce8 100644
--- a/charts/sure/templates/_env.tpl
+++ b/charts/sure/templates/_env.tpl
@@ -11,6 +11,7 @@ The helper always injects:
- optional Active Record Encryption keys (controlled by rails.encryptionEnv.enabled)
- optional DATABASE_URL + DB_PASSWORD (includeDatabase=true and helper can compute a DB URL)
- optional REDIS_URL + REDIS_PASSWORD (includeRedis=true and helper can compute a Redis URL)
+- optional HTTPS_PROXY / HTTP_PROXY / NO_PROXY (pipelock.enabled=true)
- rails.settings / rails.extraEnv / rails.extraEnvVars
- optional additional per-workload env / envFrom blocks via extraEnv / extraEnvFrom.
*/}}
@@ -77,10 +78,44 @@ The helper always injects:
{{- end }}
{{- end }}
{{- end }}
+{{- if and $ctx.Values.pipelock.enabled (ne (toString (dig "forwardProxy" "enabled" true $ctx.Values.pipelock)) "false") }}
+{{- $proxyPort := 8888 -}}
+{{- if $ctx.Values.pipelock.forwardProxy -}}
+{{- $proxyPort = int ($ctx.Values.pipelock.forwardProxy.port | default 8888) -}}
+{{- end }}
+- name: HTTPS_PROXY
+ value: {{ printf "http://%s-pipelock.%s.svc.cluster.local:%d" (include "sure.fullname" $ctx) $ctx.Release.Namespace $proxyPort | quote }}
+- name: HTTP_PROXY
+ value: {{ printf "http://%s-pipelock.%s.svc.cluster.local:%d" (include "sure.fullname" $ctx) $ctx.Release.Namespace $proxyPort | quote }}
+- name: NO_PROXY
+ value: "localhost,127.0.0.1,.svc.cluster.local,.cluster.local"
+{{- end }}
{{- range $k, $v := $ctx.Values.rails.settings }}
- name: {{ $k }}
value: {{ $v | quote }}
{{- end }}
+{{- if $ctx.Values.rails.externalAssistant.enabled }}
+- name: EXTERNAL_ASSISTANT_URL
+ value: {{ $ctx.Values.rails.externalAssistant.url | quote }}
+{{- if $ctx.Values.rails.externalAssistant.tokenSecretRef }}
+- name: EXTERNAL_ASSISTANT_TOKEN
+ valueFrom:
+ secretKeyRef:
+ name: {{ $ctx.Values.rails.externalAssistant.tokenSecretRef.name }}
+ key: {{ $ctx.Values.rails.externalAssistant.tokenSecretRef.key }}
+{{- else }}
+- name: EXTERNAL_ASSISTANT_TOKEN
+ value: {{ $ctx.Values.rails.externalAssistant.token | quote }}
+{{- end }}
+- name: EXTERNAL_ASSISTANT_AGENT_ID
+ value: {{ $ctx.Values.rails.externalAssistant.agentId | quote }}
+- name: EXTERNAL_ASSISTANT_SESSION_KEY
+ value: {{ $ctx.Values.rails.externalAssistant.sessionKey | quote }}
+{{- if $ctx.Values.rails.externalAssistant.allowedEmails }}
+- name: EXTERNAL_ASSISTANT_ALLOWED_EMAILS
+ value: {{ $ctx.Values.rails.externalAssistant.allowedEmails | quote }}
+{{- end }}
+{{- end }}
{{- range $k, $v := $ctx.Values.rails.extraEnv }}
- name: {{ $k }}
value: {{ $v | quote }}
diff --git a/charts/sure/templates/_helpers.tpl b/charts/sure/templates/_helpers.tpl
index 436127959..d36105db9 100644
--- a/charts/sure/templates/_helpers.tpl
+++ b/charts/sure/templates/_helpers.tpl
@@ -157,3 +157,27 @@ true
{{- default "redis-password" .Values.redis.passwordKey -}}
{{- end -}}
{{- end -}}
+
+{{/* Pipelock image string */}}
+{{- define "sure.pipelockImage" -}}
+{{- $repo := "ghcr.io/luckypipewrench/pipelock" -}}
+{{- $tag := "latest" -}}
+{{- if .Values.pipelock.image -}}
+{{- $repo = .Values.pipelock.image.repository | default $repo -}}
+{{- $tag = .Values.pipelock.image.tag | default $tag -}}
+{{- end -}}
+{{- printf "%s:%s" $repo $tag -}}
+{{- end -}}
+
+{{/* Pipelock MCP upstream URL (auto-compute or explicit override) */}}
+{{- define "sure.pipelockUpstream" -}}
+{{- $upstream := "" -}}
+{{- if .Values.pipelock.mcpProxy -}}
+{{- $upstream = .Values.pipelock.mcpProxy.upstream | default "" -}}
+{{- end -}}
+{{- if $upstream -}}
+{{- $upstream -}}
+{{- else -}}
+{{- printf "http://%s:%d/mcp" (include "sure.fullname" .) (int (.Values.service.port | default 80)) -}}
+{{- end -}}
+{{- end -}}
diff --git a/charts/sure/templates/asserts.tpl b/charts/sure/templates/asserts.tpl
new file mode 100644
index 000000000..1d481c0e9
--- /dev/null
+++ b/charts/sure/templates/asserts.tpl
@@ -0,0 +1,23 @@
+{{/*
+Mutual exclusivity and configuration guards
+*/}}
+
+{{- if and .Values.redisOperator.managed.enabled .Values.redisSimple.enabled -}}
+{{- fail "Invalid configuration: Both redisOperator.managed.enabled and redisSimple.enabled are true. Enable only one in-cluster Redis provider." -}}
+{{- end -}}
+
+{{- $extEnabled := false -}}
+{{- if .Values.rails -}}{{- if .Values.rails.externalAssistant -}}{{- if .Values.rails.externalAssistant.enabled -}}
+{{- $extEnabled = true -}}
+{{- end -}}{{- end -}}{{- end -}}
+{{- $plEnabled := false -}}
+{{- if .Values.pipelock -}}{{- if .Values.pipelock.enabled -}}
+{{- $plEnabled = true -}}
+{{- end -}}{{- end -}}
+{{- $requirePL := false -}}
+{{- if .Values.pipelock -}}{{- if .Values.pipelock.requireForExternalAssistant -}}
+{{- $requirePL = true -}}
+{{- end -}}{{- end -}}
+{{- if and $extEnabled (not $plEnabled) $requirePL -}}
+{{- fail "pipelock.requireForExternalAssistant is true but pipelock.enabled is false. Enable pipelock (pipelock.enabled=true) when using rails.externalAssistant, or set pipelock.requireForExternalAssistant=false." -}}
+{{- end -}}
diff --git a/charts/sure/templates/pipelock-configmap.yaml b/charts/sure/templates/pipelock-configmap.yaml
new file mode 100644
index 000000000..b9c8fa5b7
--- /dev/null
+++ b/charts/sure/templates/pipelock-configmap.yaml
@@ -0,0 +1,151 @@
+{{- if .Values.pipelock.enabled }}
+{{- $fwdEnabled := true -}}
+{{- $fwdMaxTunnel := 300 -}}
+{{- $fwdIdleTimeout := 60 -}}
+{{- if .Values.pipelock.forwardProxy -}}
+{{- if hasKey .Values.pipelock.forwardProxy "enabled" -}}
+{{- $fwdEnabled = .Values.pipelock.forwardProxy.enabled -}}
+{{- end -}}
+{{- $fwdMaxTunnel = int (.Values.pipelock.forwardProxy.maxTunnelSeconds | default 300) -}}
+{{- $fwdIdleTimeout = int (.Values.pipelock.forwardProxy.idleTimeoutSeconds | default 60) -}}
+{{- end -}}
+{{- $wsEnabled := false -}}
+{{- $wsMaxMsg := 1048576 -}}
+{{- $wsMaxConns := 128 -}}
+{{- $wsScanText := true -}}
+{{- $wsAllowBinary := false -}}
+{{- $wsForwardCookies := false -}}
+{{- $wsMaxConnSec := 3600 -}}
+{{- $wsIdleTimeout := 300 -}}
+{{- $wsOriginPolicy := "rewrite" -}}
+{{- if .Values.pipelock.websocketProxy -}}
+{{- if hasKey .Values.pipelock.websocketProxy "enabled" -}}
+{{- $wsEnabled = .Values.pipelock.websocketProxy.enabled -}}
+{{- end -}}
+{{- $wsMaxMsg = int (.Values.pipelock.websocketProxy.maxMessageBytes | default 1048576) -}}
+{{- $wsMaxConns = int (.Values.pipelock.websocketProxy.maxConcurrentConnections | default 128) -}}
+{{- if hasKey .Values.pipelock.websocketProxy "scanTextFrames" -}}
+{{- $wsScanText = .Values.pipelock.websocketProxy.scanTextFrames -}}
+{{- end -}}
+{{- if hasKey .Values.pipelock.websocketProxy "allowBinaryFrames" -}}
+{{- $wsAllowBinary = .Values.pipelock.websocketProxy.allowBinaryFrames -}}
+{{- end -}}
+{{- if hasKey .Values.pipelock.websocketProxy "forwardCookies" -}}
+{{- $wsForwardCookies = .Values.pipelock.websocketProxy.forwardCookies -}}
+{{- end -}}
+{{- $wsMaxConnSec = int (.Values.pipelock.websocketProxy.maxConnectionSeconds | default 3600) -}}
+{{- $wsIdleTimeout = int (.Values.pipelock.websocketProxy.idleTimeoutSeconds | default 300) -}}
+{{- $wsOriginPolicy = .Values.pipelock.websocketProxy.originPolicy | default "rewrite" -}}
+{{- end -}}
+{{- $mcpPolicyEnabled := true -}}
+{{- $mcpPolicyAction := "warn" -}}
+{{- if .Values.pipelock.mcpToolPolicy -}}
+{{- if hasKey .Values.pipelock.mcpToolPolicy "enabled" -}}
+{{- $mcpPolicyEnabled = .Values.pipelock.mcpToolPolicy.enabled -}}
+{{- end -}}
+{{- $mcpPolicyAction = .Values.pipelock.mcpToolPolicy.action | default "warn" -}}
+{{- end -}}
+{{- $mcpBindingEnabled := true -}}
+{{- $mcpBindingAction := "warn" -}}
+{{- if .Values.pipelock.mcpSessionBinding -}}
+{{- if hasKey .Values.pipelock.mcpSessionBinding "enabled" -}}
+{{- $mcpBindingEnabled = .Values.pipelock.mcpSessionBinding.enabled -}}
+{{- end -}}
+{{- $mcpBindingAction = .Values.pipelock.mcpSessionBinding.unknownToolAction | default "warn" -}}
+{{- end -}}
+{{- $chainEnabled := true -}}
+{{- $chainAction := "warn" -}}
+{{- $chainWindow := 20 -}}
+{{- $chainGap := 3 -}}
+{{- if .Values.pipelock.toolChainDetection -}}
+{{- if hasKey .Values.pipelock.toolChainDetection "enabled" -}}
+{{- $chainEnabled = .Values.pipelock.toolChainDetection.enabled -}}
+{{- end -}}
+{{- $chainAction = .Values.pipelock.toolChainDetection.action | default "warn" -}}
+{{- $chainWindow = int (.Values.pipelock.toolChainDetection.windowSize | default 20) -}}
+{{- $chainGap = int (.Values.pipelock.toolChainDetection.maxGap | default 3) -}}
+{{- end -}}
+{{- $logFormat := "json" -}}
+{{- $logOutput := "stdout" -}}
+{{- $logIncludeAllowed := false -}}
+{{- $logIncludeBlocked := true -}}
+{{- if .Values.pipelock.logging -}}
+{{- $logFormat = .Values.pipelock.logging.format | default "json" -}}
+{{- $logOutput = .Values.pipelock.logging.output | default "stdout" -}}
+{{- if hasKey .Values.pipelock.logging "includeAllowed" -}}
+{{- $logIncludeAllowed = .Values.pipelock.logging.includeAllowed -}}
+{{- end -}}
+{{- if hasKey .Values.pipelock.logging "includeBlocked" -}}
+{{- $logIncludeBlocked = .Values.pipelock.logging.includeBlocked -}}
+{{- end -}}
+{{- end }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ include "sure.fullname" . }}-pipelock
+ labels:
+ {{- include "sure.labels" . | nindent 4 }}
+data:
+ pipelock.yaml: |
+ version: 1
+ mode: {{ .Values.pipelock.mode | default "balanced" }}
+{{- if .Values.pipelock.trustedDomains }}
+ trusted_domains:
+{{- range .Values.pipelock.trustedDomains }}
+ - {{ . | quote }}
+{{- end }}
+{{- end }}
+ forward_proxy:
+ enabled: {{ $fwdEnabled }}
+ max_tunnel_seconds: {{ $fwdMaxTunnel }}
+ idle_timeout_seconds: {{ $fwdIdleTimeout }}
+ websocket_proxy:
+ enabled: {{ $wsEnabled }}
+ max_message_bytes: {{ $wsMaxMsg }}
+ max_concurrent_connections: {{ $wsMaxConns }}
+ scan_text_frames: {{ $wsScanText }}
+ allow_binary_frames: {{ $wsAllowBinary }}
+ forward_cookies: {{ $wsForwardCookies }}
+ strip_compression: true
+ max_connection_seconds: {{ $wsMaxConnSec }}
+ idle_timeout_seconds: {{ $wsIdleTimeout }}
+ origin_policy: {{ $wsOriginPolicy }}
+ dlp:
+ scan_env: true
+ include_defaults: true
+ response_scanning:
+ enabled: true
+ action: warn
+ include_defaults: true
+ mcp_input_scanning:
+ enabled: true
+ action: block
+ on_parse_error: block
+ mcp_tool_scanning:
+ enabled: true
+ action: warn
+ detect_drift: true
+ mcp_tool_policy:
+ enabled: {{ $mcpPolicyEnabled }}
+ action: {{ $mcpPolicyAction }}
+{{- if and .Values.pipelock.mcpToolPolicy .Values.pipelock.mcpToolPolicy.redirectProfiles }}
+ redirect_profiles:
+ {{- toYaml .Values.pipelock.mcpToolPolicy.redirectProfiles | nindent 8 }}
+{{- end }}
+ mcp_session_binding:
+ enabled: {{ $mcpBindingEnabled }}
+ unknown_tool_action: {{ $mcpBindingAction }}
+ tool_chain_detection:
+ enabled: {{ $chainEnabled }}
+ action: {{ $chainAction }}
+ window_size: {{ $chainWindow }}
+ max_gap: {{ $chainGap }}
+ logging:
+ format: {{ $logFormat }}
+ output: {{ $logOutput }}
+ include_allowed: {{ $logIncludeAllowed }}
+ include_blocked: {{ $logIncludeBlocked }}
+{{- if .Values.pipelock.extraConfig }}
+ {{- toYaml .Values.pipelock.extraConfig | nindent 4 }}
+{{- end }}
+{{- end }}
diff --git a/charts/sure/templates/pipelock-deployment.yaml b/charts/sure/templates/pipelock-deployment.yaml
new file mode 100644
index 000000000..bf57ec8ab
--- /dev/null
+++ b/charts/sure/templates/pipelock-deployment.yaml
@@ -0,0 +1,101 @@
+{{- if .Values.pipelock.enabled }}
+{{- $fwdPort := 8888 -}}
+{{- $mcpPort := 8889 -}}
+{{- $pullPolicy := "IfNotPresent" -}}
+{{- if .Values.pipelock.forwardProxy -}}
+{{- $fwdPort = int (.Values.pipelock.forwardProxy.port | default 8888) -}}
+{{- end -}}
+{{- if .Values.pipelock.mcpProxy -}}
+{{- $mcpPort = int (.Values.pipelock.mcpProxy.port | default 8889) -}}
+{{- end -}}
+{{- if .Values.pipelock.image -}}
+{{- $pullPolicy = .Values.pipelock.image.pullPolicy | default "IfNotPresent" -}}
+{{- end }}
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "sure.fullname" . }}-pipelock
+ labels:
+ {{- include "sure.labels" . | nindent 4 }}
+spec:
+ replicas: {{ .Values.pipelock.replicas | default 1 }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/component: pipelock
+ {{- include "sure.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/component: pipelock
+ {{- include "sure.selectorLabels" . | nindent 8 }}
+ annotations:
+ checksum/config: {{ include (print $.Template.BasePath "/pipelock-configmap.yaml") . | sha256sum }}
+ {{- with .Values.pipelock.podAnnotations }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ spec:
+ securityContext:
+ {{- toYaml .Values.podSecurityContext | nindent 8 }}
+ {{- $plSecrets := coalesce .Values.pipelock.image.imagePullSecrets .Values.image.imagePullSecrets }}
+ {{- if $plSecrets }}
+ imagePullSecrets:
+ {{- toYaml $plSecrets | nindent 8 }}
+ {{- end }}
+ volumes:
+ - name: config
+ configMap:
+ name: {{ include "sure.fullname" . }}-pipelock
+ containers:
+ - name: pipelock
+ image: {{ include "sure.pipelockImage" . }}
+ imagePullPolicy: {{ $pullPolicy }}
+ args:
+ - "run"
+ - "--config"
+ - "/etc/pipelock/pipelock.yaml"
+ - "--listen"
+ - "0.0.0.0:{{ $fwdPort }}"
+ - "--mcp-listen"
+ - "0.0.0.0:{{ $mcpPort }}"
+ - "--mcp-upstream"
+ - {{ include "sure.pipelockUpstream" . | quote }}
+ volumeMounts:
+ - name: config
+ mountPath: /etc/pipelock
+ readOnly: true
+ ports:
+ - name: proxy
+ containerPort: {{ $fwdPort }}
+ protocol: TCP
+ - name: mcp
+ containerPort: {{ $mcpPort }}
+ protocol: TCP
+ livenessProbe:
+ httpGet:
+ path: /health
+ port: proxy
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ timeoutSeconds: 3
+ failureThreshold: 3
+ readinessProbe:
+ httpGet:
+ path: /health
+ port: proxy
+ initialDelaySeconds: 3
+ periodSeconds: 5
+ timeoutSeconds: 2
+ failureThreshold: 3
+ securityContext:
+ {{- toYaml .Values.securityContext | nindent 12 }}
+ resources:
+ {{- toYaml (.Values.pipelock.resources | default dict) | nindent 12 }}
+ nodeSelector:
+ {{- toYaml (.Values.pipelock.nodeSelector | default dict) | nindent 8 }}
+ affinity:
+ {{- toYaml (.Values.pipelock.affinity | default dict) | nindent 8 }}
+ tolerations:
+ {{- toYaml (.Values.pipelock.tolerations | default list) | nindent 8 }}
+ topologySpreadConstraints:
+ {{- toYaml (.Values.pipelock.topologySpreadConstraints | default (list)) | nindent 8 }}
+{{- end }}
diff --git a/charts/sure/templates/pipelock-ingress.yaml b/charts/sure/templates/pipelock-ingress.yaml
new file mode 100644
index 000000000..49c3e7ef8
--- /dev/null
+++ b/charts/sure/templates/pipelock-ingress.yaml
@@ -0,0 +1,42 @@
+{{- if and .Values.pipelock.enabled .Values.pipelock.ingress.enabled }}
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: {{ include "sure.fullname" . }}-pipelock
+ labels:
+ {{- include "sure.labels" . | nindent 4 }}
+ {{- with .Values.pipelock.ingress.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- if .Values.pipelock.ingress.className }}
+ ingressClassName: {{ .Values.pipelock.ingress.className }}
+ {{- end }}
+ {{- if .Values.pipelock.ingress.hosts }}
+ rules:
+ {{- range .Values.pipelock.ingress.hosts }}
+ {{- if not .paths }}
+ {{- fail "each entry in pipelock.ingress.hosts must include at least one paths item" }}
+ {{- end }}
+ - host: {{ .host | quote }}
+ http:
+ paths:
+ {{- range .paths }}
+ - path: {{ .path }}
+ pathType: {{ .pathType }}
+ backend:
+ service:
+ name: {{ include "sure.fullname" $ }}-pipelock
+ port:
+ name: mcp
+ {{- end }}
+ {{- end }}
+ {{- else }}
+ {{- fail "pipelock.ingress.enabled=true requires at least one entry in pipelock.ingress.hosts" }}
+ {{- end }}
+ {{- if .Values.pipelock.ingress.tls }}
+ tls:
+ {{- toYaml .Values.pipelock.ingress.tls | nindent 4 }}
+ {{- end }}
+{{- end }}
diff --git a/charts/sure/templates/pipelock-pdb.yaml b/charts/sure/templates/pipelock-pdb.yaml
new file mode 100644
index 000000000..59f7da34a
--- /dev/null
+++ b/charts/sure/templates/pipelock-pdb.yaml
@@ -0,0 +1,21 @@
+{{- if and .Values.pipelock.enabled .Values.pipelock.pdb.enabled }}
+{{- if and .Values.pipelock.pdb.minAvailable .Values.pipelock.pdb.maxUnavailable }}
+{{- fail "pipelock.pdb: set either minAvailable or maxUnavailable, not both." -}}
+{{- end }}
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+ name: {{ include "sure.fullname" . }}-pipelock
+ labels:
+ {{- include "sure.labels" . | nindent 4 }}
+spec:
+ {{- if .Values.pipelock.pdb.minAvailable }}
+ minAvailable: {{ .Values.pipelock.pdb.minAvailable }}
+ {{- else if .Values.pipelock.pdb.maxUnavailable }}
+ maxUnavailable: {{ .Values.pipelock.pdb.maxUnavailable }}
+ {{- end }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/component: pipelock
+ {{- include "sure.selectorLabels" . | nindent 6 }}
+{{- end }}
diff --git a/charts/sure/templates/pipelock-service.yaml b/charts/sure/templates/pipelock-service.yaml
new file mode 100644
index 000000000..c20cac0a4
--- /dev/null
+++ b/charts/sure/templates/pipelock-service.yaml
@@ -0,0 +1,31 @@
+{{- if .Values.pipelock.enabled }}
+{{- $fwdPort := 8888 -}}
+{{- $mcpPort := 8889 -}}
+{{- if .Values.pipelock.forwardProxy -}}
+{{- $fwdPort = int (.Values.pipelock.forwardProxy.port | default 8888) -}}
+{{- end -}}
+{{- if .Values.pipelock.mcpProxy -}}
+{{- $mcpPort = int (.Values.pipelock.mcpProxy.port | default 8889) -}}
+{{- end }}
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "sure.fullname" . }}-pipelock
+ labels:
+ {{- include "sure.labels" . | nindent 4 }}
+ app.kubernetes.io/component: pipelock
+spec:
+ type: {{ (.Values.pipelock.service).type | default "ClusterIP" }}
+ selector:
+ app.kubernetes.io/component: pipelock
+ {{- include "sure.selectorLabels" . | nindent 4 }}
+ ports:
+ - name: proxy
+ port: {{ $fwdPort }}
+ targetPort: proxy
+ protocol: TCP
+ - name: mcp
+ port: {{ $mcpPort }}
+ targetPort: mcp
+ protocol: TCP
+{{- end }}
diff --git a/charts/sure/templates/pipelock-servicemonitor.yaml b/charts/sure/templates/pipelock-servicemonitor.yaml
new file mode 100644
index 000000000..dfe2d2c54
--- /dev/null
+++ b/charts/sure/templates/pipelock-servicemonitor.yaml
@@ -0,0 +1,21 @@
+{{- if and .Values.pipelock.enabled .Values.pipelock.serviceMonitor.enabled }}
+apiVersion: monitoring.coreos.com/v1
+kind: ServiceMonitor
+metadata:
+ name: {{ include "sure.fullname" . }}-pipelock
+ labels:
+ {{- include "sure.labels" . | nindent 4 }}
+ {{- with .Values.pipelock.serviceMonitor.additionalLabels }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ selector:
+ matchLabels:
+ app.kubernetes.io/component: pipelock
+ {{- include "sure.selectorLabels" . | nindent 6 }}
+ endpoints:
+ - interval: {{ .Values.pipelock.serviceMonitor.interval }}
+ scrapeTimeout: {{ .Values.pipelock.serviceMonitor.scrapeTimeout }}
+ path: {{ .Values.pipelock.serviceMonitor.path }}
+ port: {{ .Values.pipelock.serviceMonitor.portName }}
+{{- end }}
diff --git a/charts/sure/values.yaml b/charts/sure/values.yaml
index 3ecd95f94..d2b5da6d0 100644
--- a/charts/sure/values.yaml
+++ b/charts/sure/values.yaml
@@ -54,6 +54,20 @@ rails:
ONBOARDING_STATE: "open"
AI_DEBUG_MODE: "false"
+ # External AI Assistant (optional)
+ # Delegates chat to a remote AI agent that calls back via MCP.
+ externalAssistant:
+ enabled: false
+ url: "" # e.g., https://your-agent-host/v1/chat
+ token: "" # Bearer token for the external AI gateway
+ agentId: "main" # Agent routing identifier
+ sessionKey: "agent:main:main" # Session key for persistent agent sessions
+ allowedEmails: "" # Comma-separated emails allowed to use external assistant (empty = all)
+ # For production, use a Secret reference instead of plaintext:
+ # tokenSecretRef:
+ # name: external-assistant-secret
+ # key: token
+
# Database: CloudNativePG (operator chart dependency) and a Cluster CR (optional)
cloudnative-pg:
config:
@@ -300,7 +314,7 @@ web:
# Probes
livenessProbe:
httpGet:
- path: /
+ path: /up
port: http
initialDelaySeconds: 20
periodSeconds: 10
@@ -308,7 +322,7 @@ web:
failureThreshold: 6
readinessProbe:
httpGet:
- path: /
+ path: /up
port: http
initialDelaySeconds: 10
periodSeconds: 5
@@ -316,7 +330,7 @@ web:
failureThreshold: 6
startupProbe:
httpGet:
- path: /
+ path: /up
port: http
failureThreshold: 30
periodSeconds: 5
@@ -465,3 +479,126 @@ hpa:
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
+
+# Pipelock: AI agent security proxy (optional)
+# Provides forward proxy (outbound HTTPS scanning) and MCP reverse proxy
+# (inbound MCP traffic scanning for prompt injection, DLP, tool poisoning).
+# More info: https://github.com/luckyPipewrench/pipelock
+pipelock:
+ enabled: false
+ image:
+ repository: ghcr.io/luckypipewrench/pipelock
+ tag: "2.0.0"
+ pullPolicy: IfNotPresent
+ imagePullSecrets: []
+ replicas: 1
+ # Pipelock run mode: strict, balanced, audit
+ mode: balanced
+ # Trusted domains: allow internal services whose public DNS resolves to private IPs.
+ # Without this, SSRF scanning blocks legitimate internal-to-internal traffic.
+ # Example: ["api.internal.example.com", "*.corp.example.com"]
+ trustedDomains: []
+ forwardProxy:
+ enabled: true
+ port: 8888
+ maxTunnelSeconds: 300
+ idleTimeoutSeconds: 60
+ mcpProxy:
+ port: 8889
+ # Auto-computed when empty: http://:/mcp
+ upstream: ""
+ # WebSocket proxy: bidirectional frame scanning for ws/wss connections.
+ # Runs on the same listener as the forward proxy at /ws?url=.
+ websocketProxy:
+ enabled: false
+ maxMessageBytes: 1048576 # 1MB per message
+ maxConcurrentConnections: 128
+ scanTextFrames: true # DLP + injection scanning on text frames
+ allowBinaryFrames: false # block binary frames by default
+ forwardCookies: false
+ maxConnectionSeconds: 3600 # 1 hour max connection lifetime
+ idleTimeoutSeconds: 300 # 5 min idle timeout
+ originPolicy: rewrite # rewrite, forward, or strip
+ # MCP tool policy: pre-execution rules for tool calls (shell obfuscation, etc.)
+ mcpToolPolicy:
+ enabled: true
+ action: warn
+ # Redirect profiles: route matched tool calls to audited handler programs instead
+ # of blocking. The handler returns a synthetic MCP response. Fail-closed on error.
+ # Example:
+ # redirectProfiles:
+ # safe-fetch:
+ # exec: ["/pipelock", "internal-redirect", "fetch-proxy"]
+ # reason: "Route fetch calls through audited proxy"
+ redirectProfiles: {}
+ # MCP session binding: pins tool inventory on first tools/list, detects injection
+ mcpSessionBinding:
+ enabled: true
+ unknownToolAction: warn
+ # Tool call chain detection: detects multi-step attack patterns (recon, exfil, etc.)
+ toolChainDetection:
+ enabled: true
+ action: warn
+ windowSize: 20
+ maxGap: 3
+ service:
+ type: ClusterIP
+ resources:
+ requests:
+ cpu: 50m
+ memory: 64Mi
+ limits:
+ memory: 128Mi
+ podAnnotations: {}
+ nodeSelector: {}
+ tolerations: []
+ affinity: {}
+ topologySpreadConstraints: []
+
+ # Prometheus Operator ServiceMonitor for /metrics on the proxy port
+ serviceMonitor:
+ enabled: false
+ interval: 30s
+ scrapeTimeout: 10s
+ path: /metrics
+ portName: proxy # matches Service port name "proxy" (8888)
+ additionalLabels: {}
+
+ # Ingress for MCP reverse proxy (port 8889) — external AI assistants need this in k8s
+ ingress:
+ enabled: false
+ className: ""
+ annotations: {}
+ hosts:
+ - host: pipelock.local
+ paths:
+ - path: /
+ pathType: Prefix
+ tls: []
+
+ # PodDisruptionBudget — protects pipelock during node drains.
+ # WARNING: minAvailable with replicas=1 blocks eviction entirely.
+ # Use maxUnavailable: 1 for single-replica deployments, or increase replicas.
+ pdb:
+ enabled: false
+ minAvailable: "" # set to 1 when replicas > 1
+ maxUnavailable: 1 # safe default: allows 1 pod to be evicted
+
+ # Structured logging for k8s log aggregation
+ logging:
+ format: json
+ output: stdout
+ includeAllowed: false
+ includeBlocked: true
+
+ # Escape hatch: ADDITIONAL config sections appended to pipelock.yaml.
+ # Use for sections not covered by structured values above (session_profiling,
+ # data_budget, adaptive_enforcement, kill_switch, sandbox, reverse_proxy, etc.)
+ # Do NOT duplicate keys already rendered above - behavior is parser-dependent.
+ extraConfig: {}
+
+ # Hard-fail helm template when externalAssistant is enabled without pipelock.
+ # NOTE: This only guards the rails.externalAssistant path. Direct MCP access
+ # (/mcp endpoint with MCP_API_TOKEN) is not detectable from Helm values.
+ # For full coverage, also ensure pipelock is enabled whenever MCP_API_TOKEN is set.
+ requireForExternalAssistant: false
diff --git a/compose.example.ai.yml b/compose.example.ai.yml
index e711fc8f4..0ffc93a8b 100644
--- a/compose.example.ai.yml
+++ b/compose.example.ai.yml
@@ -1,21 +1,42 @@
# ===========================================================================
-# Example Docker Compose file with additional Ollama service for LLM tools
+# Example Docker Compose file with Ollama (local LLM), OpenClaw (external AI
+# assistant), and Pipelock (agent security proxy)
# ===========================================================================
#
# Purpose:
# --------
#
-# This file is an example Docker Compose configuration for self hosting
-# Sure with Ollama on your local machine or on a cloud VPS.
+# This file extends the standard Sure setup with optional AI capabilities:
#
-# The configuration below is a "standard" setup that works out of the box,
-# but if you're running this outside of a local network, it is recommended
-# to set the environment variables for extra security.
+# Pipelock — agent security proxy
+# (always runs)
+# - Forward proxy (port 8888): scans outbound HTTPS from Faraday-based
+# clients (e.g. ruby-openai). NOT covered: SimpleFin, Coinbase, or
+# anything using Net::HTTP/HTTParty directly. HTTPS_PROXY is
+# cooperative; Docker Compose has no egress network policy.
+# - MCP reverse proxy (port 8889): scans inbound AI traffic (DLP,
+# prompt injection, tool poisoning, tool call policy). External AI
+# clients should connect to Pipelock on port 8889 rather than
+# directly to Sure's /mcp endpoint. Note: /mcp is still reachable
+# on web port 3000 (auth token required); Pipelock adds scanning
+# but Docker Compose cannot enforce network-level routing.
+#
+# Ollama + Open WebUI — local LLM inference
+# (optional, --profile local-ai)
+# - Only starts when you run: docker compose --profile local-ai up
+#
+# Ollama + Open WebUI + OpenClaw — external AI assistant
+# (optional, --profile external-assistant)
+# - Local LLM inference available via Ollama + Open WebUI
+# - Starts an OpenClaw locally for Sure's external assistant mode.
+# - Set EXTERNAL_ASSISTANT_URL to http://openclaw:18789/v1/chat/completions
+# for internal routing.
#
# Setup:
# ------
#
-# To run this, you should read the setup guide:
+# 1. Copy pipelock.example.yaml alongside this file (or customize it).
+# 2. Read the full setup guide:
#
# https://github.com/we-promise/sure/blob/main/docs/hosting/docker.md
#
@@ -24,7 +45,7 @@
#
# If you run into problems, you should open a Discussion here:
#
-# https://github.com/we-promise/sure/discussions/categories/general
+# https://github.com/we-promise/sure/discussions/categories/ai-usage
#
x-db-env: &db_env
@@ -41,19 +62,76 @@ x-rails-env: &rails_env
DB_HOST: db
DB_PORT: 5432
REDIS_URL: redis://redis:6379/1
+ # MCP server endpoint — enables /mcp for external AI assistants (e.g. Claude, GPT).
+ # Set both values to activate. MCP_USER_EMAIL must match an existing user's email.
+ # External AI clients should connect via Pipelock (port 8889) for scanning.
+ MCP_API_TOKEN: ${MCP_API_TOKEN:-}
+ MCP_USER_EMAIL: ${MCP_USER_EMAIL:-}
+ # Route outbound HTTPS through Pipelock for clients that respect HTTPS_PROXY.
+ # Covered: OpenAI API (ruby-openai/Faraday). NOT covered: SimpleFin, Coinbase (Net::HTTP).
+ HTTPS_PROXY: "http://pipelock:8888"
+ HTTP_PROXY: "http://pipelock:8888"
+ # Skip proxy for internal Docker network services (including ollama for local LLM calls)
+ NO_PROXY: "db,redis,pipelock,ollama,openclaw,localhost,127.0.0.1"
AI_DEBUG_MODE: "true" # Useful for debugging, set to "false" in production
# Ollama using OpenAI API compatible endpoints
OPENAI_ACCESS_TOKEN: token-can-be-any-value-for-ollama
OPENAI_MODEL: llama3.1:8b # Note: Use tool-enabled model
OPENAI_URI_BASE: http://ollama:11434/v1
+ # Vector store — pgvector keeps all data local (requires pgvector/pgvector Docker image for db)
+ VECTOR_STORE_PROVIDER: pgvector
+ EMBEDDING_MODEL: nomic-embed-text
+ EMBEDDING_DIMENSIONS: "1024"
# NOTE: enabling OpenAI will incur costs when you use AI-related features in the app (chat, rules). Make sure you have set appropriate spend limits on your account before adding this.
# OPENAI_ACCESS_TOKEN: ${OPENAI_ACCESS_TOKEN}
+ # External AI Assistant — delegates chat to a remote AI agent (e.g., OpenClaw).
+ # The agent calls back to Sure's /mcp endpoint for financial data.
+ # Set EXTERNAL_ASSISTANT_URL + TOKEN to activate, then either set ASSISTANT_TYPE=external
+ # here (forces all families) or choose "External" in Settings > Self-Hosting > AI Assistant.
+ ASSISTANT_TYPE: ${ASSISTANT_TYPE:-}
+ EXTERNAL_ASSISTANT_URL: ${EXTERNAL_ASSISTANT_URL:-http://openclaw:18789/v1/chat/completions}
+ EXTERNAL_ASSISTANT_TOKEN: ${EXTERNAL_ASSISTANT_TOKEN:-}
+ EXTERNAL_ASSISTANT_AGENT_ID: ${EXTERNAL_ASSISTANT_AGENT_ID:-main}
+ EXTERNAL_ASSISTANT_SESSION_KEY: ${EXTERNAL_ASSISTANT_SESSION_KEY:-agent:main:main}
+ EXTERNAL_ASSISTANT_ALLOWED_EMAILS: ${EXTERNAL_ASSISTANT_ALLOWED_EMAILS:-}
services:
+ pipelock:
+ image: ghcr.io/luckypipewrench/pipelock:latest # pin to a specific version (e.g., :2.0.0) for production
+ container_name: pipelock
+ hostname: pipelock
+ restart: unless-stopped
+ volumes:
+ - ./pipelock.example.yaml:/etc/pipelock/pipelock.yaml:ro
+ command:
+ - "run"
+ - "--config"
+ - "/etc/pipelock/pipelock.yaml"
+ - "--listen"
+ - "0.0.0.0:8888"
+ - "--mcp-listen"
+ - "0.0.0.0:8889"
+ - "--mcp-upstream"
+ - "http://web:3000/mcp"
+ ports:
+ # MCP reverse proxy — external AI assistants connect here
+ - "${MCP_PROXY_PORT:-8889}:8889"
+ # Uncomment to expose forward proxy endpoints (/health, /metrics, /stats):
+ # - "8888:8888"
+ healthcheck:
+ test: ["CMD", "/pipelock", "healthcheck", "--addr", "127.0.0.1:8888"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
+ start_period: 30s
+ networks:
+ - sure_net
+
# Note: You still have to download models manually using the ollama CLI or via Open WebUI
ollama:
profiles:
- - ai
+ - local-ai
+ - external-assistant
volumes:
- ollama:/root/.ollama
container_name: ollama
@@ -64,7 +142,7 @@ services:
- "11434:11434"
environment:
- OLLAMA_KEEP_ALIVE=1h
- - OLLAMA_MODELS=deepseek-r1:8b,llama3.1:8b # Pre-load model on startup, you can change this to your preferred model
+ - OLLAMA_MODELS=deepseek-r1:8b,llama3.1:8b,nomic-embed-text # Pre-load model on startup, you can change this to your preferred model
networks:
- sure_net
# Recommended: Enable GPU support
@@ -78,7 +156,8 @@ services:
ollama-webui:
profiles:
- - ai
+ - local-ai
+ - external-assistant
image: ghcr.io/open-webui/open-webui
container_name: ollama-webui
volumes:
@@ -101,11 +180,66 @@ services:
networks:
- sure_net
+ # OpenClaw gateway for Sure's "external assistant" mode.
+ # Based on OpenClaw Docker install docs:
+ # https://docs.openclaw.ai/install/docker
+ openclaw:
+ profiles:
+ - external-assistant
+ image: ${OPENCLAW_IMAGE:-ghcr.io/openclaw/openclaw:latest}
+ hostname: openclaw
+ restart: unless-stopped
+ init: true
+ environment:
+ HOME: /home/node
+ TERM: xterm-256color
+ TZ: ${OPENCLAW_TZ:-UTC}
+ OPENCLAW_GATEWAY_TOKEN: ${EXTERNAL_ASSISTANT_TOKEN:-changeme}
+ OPENCLAW_ALLOW_INSECURE_PRIVATE_WS: ${OPENCLAW_ALLOW_INSECURE_PRIVATE_WS:-}
+ command:
+ [
+ "node",
+ "dist/index.js",
+ "gateway",
+ # OpenClaw may exit with "Missing config" on first boot in example setups.
+ # `--allow-unconfigured` keeps the gateway running until you complete `openclaw setup`.
+ "pass",
+ "--allow-unconfigured",
+ "--bind",
+ "${OPENCLAW_GATEWAY_BIND:-lan}",
+ "--port",
+ "18789",
+ ]
+ healthcheck:
+ test:
+ [
+ "CMD",
+ "node",
+ "-e",
+ "fetch('http://127.0.0.1:18789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))",
+ ]
+ interval: 30s
+ timeout: 5s
+ retries: 5
+ start_period: 20s
+ ports:
+ # Expose for local onboarding/debugging. Sure uses the internal service name.
+ - "${OPENCLAW_GATEWAY_PORT:-18789}:18789"
+ volumes:
+ - openclaw-config:/home/node/.openclaw
+ - openclaw-workspace:/home/node/.openclaw/workspace
+ networks:
+ - sure_net
+
web:
image: ghcr.io/we-promise/sure:stable
volumes:
- app-storage:/rails/storage
ports:
+ # Web UI for browser access. Note: /mcp is also reachable on this port,
+ # bypassing Pipelock's MCP scanning (auth token is still required).
+ # For hardened deployments, use `expose: [3000]` instead and front
+ # the web UI with a separate reverse proxy.
- ${PORT:-3000}:3000
restart: unless-stopped
environment:
@@ -115,6 +249,8 @@ services:
condition: service_healthy
redis:
condition: service_healthy
+ pipelock: # Remove this block and unset HTTPS_PROXY/HTTP_PROXY to run without Pipelock
+ condition: service_healthy
dns:
- 8.8.8.8
- 1.1.1.1
@@ -132,6 +268,8 @@ services:
condition: service_healthy
redis:
condition: service_healthy
+ pipelock: # Remove this block and unset HTTPS_PROXY/HTTP_PROXY to run without Pipelock
+ condition: service_healthy
dns:
- 8.8.8.8
- 1.1.1.1
@@ -141,7 +279,7 @@ services:
- sure_net
db:
- image: postgres:16
+ image: pgvector/pgvector:pg16
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data
@@ -195,7 +333,10 @@ volumes:
redis-data:
ollama:
ollama-webui:
+ openclaw-config:
+ openclaw-workspace:
networks:
sure_net:
driver: bridge
+ name: sure_net
diff --git a/compose.example.pipelock.yml b/compose.example.pipelock.yml
deleted file mode 100644
index b70bbb916..000000000
--- a/compose.example.pipelock.yml
+++ /dev/null
@@ -1,275 +0,0 @@
-# ===========================================================================
-# Example Docker Compose file with Pipelock agent security proxy
-# ===========================================================================
-#
-# Purpose:
-# --------
-#
-# This file adds Pipelock (https://github.com/luckyPipewrench/pipelock)
-# as a security proxy for Sure, providing two layers of protection:
-#
-# 1. Forward proxy (port 8888) — routes outbound HTTPS through Pipelock
-# for clients that respect the HTTPS_PROXY environment variable.
-#
-# 2. MCP reverse proxy (port 8889) — scans inbound MCP traffic from
-# external AI assistants bidirectionally (DLP, prompt injection,
-# tool poisoning, tool call policy).
-#
-# Forward proxy coverage:
-# -----------------------
-#
-# Covered (Faraday-based clients respect HTTPS_PROXY automatically):
-# - OpenAI API calls (ruby-openai gem)
-# - Market data providers using Faraday
-#
-# NOT covered (these clients ignore HTTPS_PROXY):
-# - SimpleFin (HTTParty / Net::HTTP)
-# - Coinbase (HTTParty / Net::HTTP)
-# - Any code using Net::HTTP or HTTParty directly
-#
-# For covered traffic, Pipelock provides:
-# - Domain allowlisting (only known-good external APIs can be reached)
-# - SSRF protection (blocks connections to private/internal IPs)
-# - DLP scanning on connection targets (detects exfiltration patterns)
-# - Rate limiting per domain
-# - Structured JSON audit logging of all outbound connections
-#
-# MCP reverse proxy coverage:
-# ---------------------------
-#
-# External AI assistants connect to Pipelock on port 8889 instead of
-# directly to Sure's /mcp endpoint. Pipelock scans all traffic:
-#
-# Request scanning (client → Sure):
-# - DLP detection (blocks credential/secret leakage in tool arguments)
-# - Prompt injection detection in tool call parameters
-# - Tool call policy enforcement (blocks dangerous operations)
-#
-# Response scanning (Sure → client):
-# - Prompt injection detection in tool response content
-# - Tool poisoning / drift detection (tool definitions changing)
-#
-# The MCP endpoint on Sure (port 3000/mcp) should NOT be exposed directly
-# to the internet. Route all external MCP traffic through Pipelock.
-#
-# Limitations:
-# ------------
-#
-# HTTPS_PROXY is cooperative. Docker Compose has no egress network policy,
-# so any code path that doesn't check the env var can connect directly.
-# For hard enforcement, deploy with network-level controls that deny all
-# egress except through the proxy. Example for Kubernetes:
-#
-# # NetworkPolicy: deny all egress, allow only proxy + DNS
-# egress:
-# - to:
-# - podSelector:
-# matchLabels:
-# app: pipelock
-# ports:
-# - port: 8888
-# - ports:
-# - port: 53
-# protocol: UDP
-#
-# Monitoring:
-# -----------
-#
-# Pipelock logs every connection and MCP request as structured JSON to stdout.
-# View logs with: docker compose logs pipelock
-#
-# Forward proxy endpoints (port 8888):
-# http://localhost:8888/health - liveness check
-# http://localhost:8888/metrics - Prometheus metrics
-# http://localhost:8888/stats - JSON summary
-#
-# More info: https://github.com/luckyPipewrench/pipelock
-#
-# Setup:
-# ------
-#
-# 1. Copy this file to compose.yml (or use -f flag)
-# 2. Set your environment variables (OPENAI_ACCESS_TOKEN, MCP_API_TOKEN, etc.)
-# 3. docker compose up
-#
-# Pipelock runs both proxies in a single container:
-# - Port 8888: forward proxy for outbound HTTPS (internal only)
-# - Port 8889: MCP reverse proxy for external AI assistants
-#
-# External AI clients connect to http://:8889 as their MCP endpoint.
-# Pipelock scans the traffic and forwards clean requests to Sure's /mcp.
-#
-# Customization:
-# --------------
-#
-# Requires Pipelock with MCP HTTP listener support (--mcp-listen flag).
-# See: https://github.com/luckyPipewrench/pipelock/releases
-#
-# Edit the pipelock command to change the mode:
-# --mode strict Block unknown domains (recommended for production)
-# --mode balanced Warn on unknown domains, block known-bad (default)
-# --mode audit Log everything, block nothing (for evaluation)
-#
-# For a custom config, mount a file and use --config instead of --mode:
-# volumes:
-# - ./config/pipelock.yml:/etc/pipelock/config.yml:ro
-# command: ["run", "--config", "/etc/pipelock/config.yml",
-# "--mcp-listen", "0.0.0.0:8889", "--mcp-upstream", "http://web:3000/mcp"]
-#
-
-x-db-env: &db_env
- POSTGRES_USER: ${POSTGRES_USER:-sure_user}
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-sure_password}
- POSTGRES_DB: ${POSTGRES_DB:-sure_production}
-
-x-rails-env: &rails_env
- <<: *db_env
- SECRET_KEY_BASE: ${SECRET_KEY_BASE:-a7523c3d0ae56415046ad8abae168d71074a79534a7062258f8d1d51ac2f76d3c3bc86d86b6b0b307df30d9a6a90a2066a3fa9e67c5e6f374dbd7dd4e0778e13}
- SELF_HOSTED: "true"
- RAILS_FORCE_SSL: "false"
- RAILS_ASSUME_SSL: "false"
- DB_HOST: db
- DB_PORT: 5432
- REDIS_URL: redis://redis:6379/1
- # NOTE: enabling OpenAI will incur costs when you use AI-related features in the app (chat, rules). Make sure you have set appropriate spend limits on your account before adding this.
- OPENAI_ACCESS_TOKEN: ${OPENAI_ACCESS_TOKEN}
- # MCP server endpoint — enables /mcp for external AI assistants (e.g. Claude, GPT).
- # Set both values to activate. MCP_USER_EMAIL must match an existing user's email.
- # External AI clients connect via Pipelock (port 8889), not directly to /mcp.
- MCP_API_TOKEN: ${MCP_API_TOKEN:-}
- MCP_USER_EMAIL: ${MCP_USER_EMAIL:-}
- # Route outbound HTTPS through Pipelock for clients that respect HTTPS_PROXY.
- # See "Forward proxy coverage" section above for which clients are covered.
- HTTPS_PROXY: "http://pipelock:8888"
- HTTP_PROXY: "http://pipelock:8888"
- # Skip proxy for internal Docker network services
- NO_PROXY: "db,redis,pipelock,localhost,127.0.0.1"
-
-services:
- pipelock:
- image: ghcr.io/luckypipewrench/pipelock:latest
- container_name: pipelock
- hostname: pipelock
- restart: unless-stopped
- command:
- - "run"
- - "--listen"
- - "0.0.0.0:8888"
- - "--mode"
- - "balanced"
- - "--mcp-listen"
- - "0.0.0.0:8889"
- - "--mcp-upstream"
- - "http://web:3000/mcp"
- ports:
- # MCP reverse proxy — external AI assistants connect here
- - "${MCP_PROXY_PORT:-8889}:8889"
- # Uncomment to expose forward proxy endpoints (/health, /metrics, /stats):
- # - "8888:8888"
- healthcheck:
- test: ["CMD", "/pipelock", "healthcheck", "--addr", "127.0.0.1:8888"]
- interval: 10s
- timeout: 5s
- retries: 3
- start_period: 30s
- networks:
- - sure_net
-
- web:
- image: ghcr.io/we-promise/sure:stable
- volumes:
- - app-storage:/rails/storage
- ports:
- # Web UI for browser access. Note: /mcp is also reachable on this port,
- # bypassing Pipelock's MCP scanning (auth token is still required).
- # For hardened deployments, use `expose: [3000]` instead and front
- # the web UI with a separate reverse proxy.
- - ${PORT:-3000}:3000
- restart: unless-stopped
- environment:
- <<: *rails_env
- depends_on:
- db:
- condition: service_healthy
- redis:
- condition: service_healthy
- pipelock:
- condition: service_healthy
- networks:
- - sure_net
-
- worker:
- image: ghcr.io/we-promise/sure:stable
- command: bundle exec sidekiq
- volumes:
- - app-storage:/rails/storage
- restart: unless-stopped
- depends_on:
- db:
- condition: service_healthy
- redis:
- condition: service_healthy
- pipelock:
- condition: service_healthy
- environment:
- <<: *rails_env
- networks:
- - sure_net
-
- db:
- image: postgres:16
- restart: unless-stopped
- volumes:
- - postgres-data:/var/lib/postgresql/data
- environment:
- <<: *db_env
- healthcheck:
- test: [ "CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB" ]
- interval: 5s
- timeout: 5s
- retries: 5
- networks:
- - sure_net
-
- backup:
- profiles:
- - backup
- image: prodrigestivill/postgres-backup-local
- restart: unless-stopped
- volumes:
- - /opt/sure-data/backups:/backups # Change this path to your desired backup location on the host machine
- environment:
- - POSTGRES_HOST=db
- - POSTGRES_DB=${POSTGRES_DB:-sure_production}
- - POSTGRES_USER=${POSTGRES_USER:-sure_user}
- - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-sure_password}
- - SCHEDULE=@daily # Runs once a day at midnight
- - BACKUP_KEEP_DAYS=7 # Keeps the last 7 days of backups
- - BACKUP_KEEP_WEEKS=4 # Keeps 4 weekly backups
- - BACKUP_KEEP_MONTHS=6 # Keeps 6 monthly backups
- depends_on:
- - db
- networks:
- - sure_net
-
- redis:
- image: redis:latest
- restart: unless-stopped
- volumes:
- - redis-data:/data
- healthcheck:
- test: [ "CMD", "redis-cli", "ping" ]
- interval: 5s
- timeout: 5s
- retries: 5
- networks:
- - sure_net
-
-volumes:
- app-storage:
- postgres-data:
- redis-data:
-
-networks:
- sure_net:
- driver: bridge
diff --git a/config/application.rb b/config/application.rb
index 3a63072b2..1269f1aad 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -39,6 +39,9 @@ module Sure
theme: [ "light", "dark" ] # available in view as params[:theme]
}
+ # Enable Skylight instrumentation for ActiveJob (background workers)
+ config.skylight.probes << "active_job" if defined?(Skylight)
+
# Enable Rack::Attack middleware for API rate limiting
config.middleware.use Rack::Attack
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 347a18617..fc7120a0a 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -78,11 +78,13 @@ Rails.application.configure do
config.action_mailer.default_url_options = { host: ENV["APP_DOMAIN"] }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
- address: ENV["SMTP_ADDRESS"],
- port: ENV["SMTP_PORT"],
- user_name: ENV["SMTP_USERNAME"],
- password: ENV["SMTP_PASSWORD"],
- tls: ENV["SMTP_TLS_ENABLED"] == "true"
+ address: ENV["SMTP_ADDRESS"],
+ port: ENV["SMTP_PORT"],
+ user_name: ENV["SMTP_USERNAME"],
+ password: ENV["SMTP_PASSWORD"],
+ tls: ENV["SMTP_TLS_ENABLED"] == "true",
+ openssl_verify_mode: ENV["SMTP_TLS_SKIP_VERIFY"] == "true" ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER,
+ ca_file: ENV["SSL_CA_FILE"]
}
# Ignore bad email addresses and do not raise email delivery errors.
diff --git a/config/initializers/active_storage_authorization.rb b/config/initializers/active_storage_authorization.rb
new file mode 100644
index 000000000..7766dc3c3
--- /dev/null
+++ b/config/initializers/active_storage_authorization.rb
@@ -0,0 +1,45 @@
+# Override Active Storage blob serving to enforce authorization
+Rails.application.config.to_prepare do
+ module ActiveStorageAttachmentAuthorization
+ extend ActiveSupport::Concern
+
+ included do
+ include Authentication
+ before_action :authorize_transaction_attachment, if: :transaction_attachment?
+ end
+
+ private
+
+ def authorize_transaction_attachment
+ attachment = ActiveStorage::Attachment.find_by(blob: authorized_blob)
+ return unless attachment&.record_type == "Transaction"
+
+ transaction = attachment.record
+
+ # Check if current user has access to this transaction's family
+ unless Current.family == transaction.entry.account.family
+ raise ActiveRecord::RecordNotFound
+ end
+ end
+
+ def transaction_attachment?
+ return false unless authorized_blob
+
+ attachment = ActiveStorage::Attachment.find_by(blob: authorized_blob)
+ attachment&.record_type == "Transaction"
+ end
+
+ def authorized_blob
+ @blob || @representation&.blob
+ end
+ end
+
+ [
+ ActiveStorage::Blobs::RedirectController,
+ ActiveStorage::Blobs::ProxyController,
+ ActiveStorage::Representations::RedirectController,
+ ActiveStorage::Representations::ProxyController
+ ].each do |controller|
+ controller.include ActiveStorageAttachmentAuthorization
+ end
+end
diff --git a/config/initializers/rswag.rb b/config/initializers/rswag.rb
new file mode 100644
index 000000000..5bef48784
--- /dev/null
+++ b/config/initializers/rswag.rb
@@ -0,0 +1,11 @@
+if defined?(Rswag::Ui) && Rails.env.development?
+ Rswag::Ui.configure do |c|
+ c.openapi_endpoint "/api-docs/openapi.yaml", "Sure API V1"
+ end
+end
+
+if defined?(Rswag::Api) && Rails.env.development?
+ Rswag::Api.configure do |c|
+ c.openapi_root = Rails.root.join("docs", "api").to_s
+ end
+end
diff --git a/config/initializers/version.rb b/config/initializers/version.rb
index 60ce0779c..fa7f79b90 100644
--- a/config/initializers/version.rb
+++ b/config/initializers/version.rb
@@ -16,7 +16,7 @@ module Sure
private
def semver
- "0.6.8-alpha.14"
+ "0.7.0-alpha.3"
end
end
end
diff --git a/config/locales/breadcrumbs/en.yml b/config/locales/breadcrumbs/en.yml
index 9d81ba325..1c840acdc 100644
--- a/config/locales/breadcrumbs/en.yml
+++ b/config/locales/breadcrumbs/en.yml
@@ -1,6 +1,8 @@
---
en:
breadcrumbs:
+ categorize: Categorize
exports: Exports
home: Home
imports: Imports
+ transactions: Transactions
diff --git a/config/locales/breadcrumbs/pl.yml b/config/locales/breadcrumbs/pl.yml
new file mode 100644
index 000000000..5d4755451
--- /dev/null
+++ b/config/locales/breadcrumbs/pl.yml
@@ -0,0 +1,6 @@
+---
+pl:
+ breadcrumbs:
+ exports: Eksporty
+ home: Strona główna
+ imports: Importy
diff --git a/config/locales/defaults/de.yml b/config/locales/defaults/de.yml
index 1d0f1af64..27bdd0dd2 100644
--- a/config/locales/defaults/de.yml
+++ b/config/locales/defaults/de.yml
@@ -3,6 +3,8 @@ de:
defaults:
brand_name: "%{brand_name}"
product_name: "%{product_name}"
+ global:
+ expand: "Aufklappen"
activerecord:
errors:
messages:
diff --git a/config/locales/defaults/en.yml b/config/locales/defaults/en.yml
index f2caaa233..bf860dde4 100644
--- a/config/locales/defaults/en.yml
+++ b/config/locales/defaults/en.yml
@@ -153,6 +153,8 @@ en:
helpers:
select:
prompt: Please select
+ search_placeholder: "Search"
+ default_label: "Select..."
submit:
create: Create %{model}
submit: Save %{model}
diff --git a/config/locales/defaults/es.yml b/config/locales/defaults/es.yml
index 96757c381..82b73c583 100644
--- a/config/locales/defaults/es.yml
+++ b/config/locales/defaults/es.yml
@@ -1,5 +1,10 @@
---
es:
+ defaults:
+ brand_name: "%{brand_name}"
+ product_name: "%{product_name}"
+ global:
+ expand: "Expandir"
activerecord:
errors:
messages:
diff --git a/config/locales/doorkeeper.pl.yml b/config/locales/doorkeeper.pl.yml
new file mode 100644
index 000000000..a561c3b7d
--- /dev/null
+++ b/config/locales/doorkeeper.pl.yml
@@ -0,0 +1,153 @@
+pl:
+ activerecord:
+ attributes:
+ doorkeeper/application:
+ name: "Nazwa"
+ redirect_uri: "URI przekierowania"
+ errors:
+ models:
+ doorkeeper/application:
+ attributes:
+ redirect_uri:
+ fragment_present: "nie może zawierać fragmentu."
+ invalid_uri: "musi być poprawnym URI."
+ unspecified_scheme: "musi określać schemat."
+ relative_uri: "musi być bezwzględnym URI."
+ secured_uri: "musi być URI HTTPS/SSL."
+ forbidden_uri: "jest zabronione przez serwer."
+ scopes:
+ not_match_configured: "nie odpowiada zakresom skonfigurowanym na serwerze."
+
+ doorkeeper:
+ applications:
+ confirmations:
+ destroy: "Czy na pewno?"
+ buttons:
+ edit: "Edytuj"
+ destroy: "Usuń"
+ submit: "Zapisz"
+ cancel: "Anuluj"
+ authorize: "Autoryzuj"
+ form:
+ error: "Ups! Sprawdź formularz pod kątem możliwych błędów"
+ help:
+ confidential: "Aplikacja będzie używana tam, gdzie sekret klienta może pozostać poufny. Natywne aplikacje mobilne i aplikacje Single Page App są uznawane za niepoufne."
+ redirect_uri: "Użyj jednej linii dla każdego URI"
+ blank_redirect_uri: "Pozostaw puste, jeśli skonfigurowano dostawcę do użycia Client Credentials, Resource Owner Password Credentials lub innego typu grant, który nie wymaga URI przekierowania."
+ scopes: "Oddzielaj zakresy spacjami. Pozostaw puste, aby użyć domyślnych zakresów."
+ edit:
+ title: "Edytuj aplikację"
+ index:
+ title: "Twoje aplikacje"
+ new: "Nowa aplikacja"
+ name: "Nazwa"
+ callback_url: "URL callback"
+ confidential: "Poufna?"
+ actions: "Akcje"
+ confidentiality:
+ "yes": "Tak"
+ "no": "Nie"
+ new:
+ title: "Nowa aplikacja"
+ show:
+ title: "Aplikacja: %{name}"
+ application_id: "UID"
+ secret: "Sekret"
+ secret_hashed: "Zahaszowany sekret"
+ scopes: "Zakresy"
+ confidential: "Poufna"
+ callback_urls: "URL-e callback"
+ actions: "Akcje"
+ not_defined: "Nie zdefiniowano"
+
+ authorizations:
+ buttons:
+ authorize: "Autoryzuj"
+ deny: "Odmów"
+ error:
+ title: "Wystąpił błąd"
+ new:
+ title: "Wymagana autoryzacja"
+ prompt: "Zezwolić aplikacji %{client_name} na użycie Twojego konta?"
+ able_to: "Ta aplikacja będzie mogła"
+ show:
+ title: "Kod autoryzacyjny"
+ form_post:
+ title: "Wyślij ten formularz"
+
+ authorized_applications:
+ confirmations:
+ revoke: "Czy na pewno?"
+ buttons:
+ revoke: "Cofnij"
+ index:
+ title: "Twoje autoryzowane aplikacje"
+ application: "Aplikacja"
+ created_at: "Utworzono"
+ date_format: "%Y-%m-%d %H:%M:%S"
+
+ pre_authorization:
+ status: "Wstępna autoryzacja"
+
+ errors:
+ messages:
+ invalid_request:
+ unknown: "Żądanie nie zawiera wymaganego parametru, zawiera nieobsługiwaną wartość parametru lub jest nieprawidłowo sformułowane."
+ missing_param: "Brakuje wymaganego parametru: %{value}."
+ request_not_authorized: "Żądanie wymaga autoryzacji. Wymagany parametr autoryzacji jest brakujący lub nieprawidłowy."
+ invalid_code_challenge: "Code challenge jest wymagany."
+ invalid_redirect_uri: "Żądany URI przekierowania jest nieprawidłowy lub nie zgadza się z URI przekierowania klienta."
+ unauthorized_client: "Klient nie ma uprawnień do wykonania tego żądania tą metodą."
+ access_denied: "Właściciel zasobu lub serwer autoryzacji odrzucił żądanie."
+ invalid_scope: "Żądany zakres jest nieprawidłowy, nieznany lub błędnie sformułowany."
+ invalid_code_challenge_method:
+ zero: "Serwer autoryzacji nie obsługuje PKCE, ponieważ brak akceptowanych wartości code_challenge_method."
+ one: "code_challenge_method musi mieć wartość %{challenge_methods}."
+ few: "code_challenge_method musi mieć jedną z wartości: %{challenge_methods}."
+ many: "code_challenge_method musi mieć jedną z wartości: %{challenge_methods}."
+ other: "code_challenge_method musi mieć jedną z wartości: %{challenge_methods}."
+ server_error: "Serwer autoryzacji napotkał nieoczekiwany warunek, który uniemożliwił realizację żądania."
+ temporarily_unavailable: "Serwer autoryzacji jest chwilowo niedostępny z powodu przeciążenia lub prac serwisowych."
+
+ credential_flow_not_configured: "Przepływ Resource Owner Password Credentials zakończył się niepowodzeniem, ponieważ Doorkeeper.configure.resource_owner_from_credentials nie jest skonfigurowany."
+ resource_owner_authenticator_not_configured: "Wyszukiwanie właściciela zasobu zakończyło się niepowodzeniem, ponieważ Doorkeeper.configure.resource_owner_authenticator nie jest skonfigurowany."
+ admin_authenticator_not_configured: "Dostęp do panelu administratora jest zabroniony, ponieważ Doorkeeper.configure.admin_authenticator nie jest skonfigurowany."
+
+ unsupported_response_type: "Serwer autoryzacji nie obsługuje tego typu odpowiedzi."
+ unsupported_response_mode: "Serwer autoryzacji nie obsługuje tego trybu odpowiedzi."
+
+ invalid_client: "Uwierzytelnienie klienta nie powiodło się z powodu nieznanego klienta, braku uwierzytelnienia klienta lub nieobsługiwanej metody uwierzytelnienia."
+ invalid_grant: "Podany grant autoryzacyjny jest nieprawidłowy, wygasł, został cofnięty, nie odpowiada URI przekierowania użytemu w żądaniu autoryzacji lub został wydany innemu klientowi."
+ unsupported_grant_type: "Typ grantu autoryzacyjnego nie jest obsługiwany przez serwer autoryzacji."
+
+ invalid_token:
+ revoked: "Token dostępu został cofnięty"
+ expired: "Token dostępu wygasł"
+ unknown: "Token dostępu jest nieprawidłowy"
+ revoke:
+ unauthorized: "Nie masz uprawnień do cofnięcia tego tokenu"
+
+ forbidden_token:
+ missing_scope: "Dostęp do tego zasobu wymaga zakresu \"%{oauth_scopes}\"."
+
+ flash:
+ applications:
+ create:
+ notice: "Aplikacja została utworzona."
+ destroy:
+ notice: "Aplikacja została usunięta."
+ update:
+ notice: "Aplikacja została zaktualizowana."
+ authorized_applications:
+ destroy:
+ notice: "Autoryzacja aplikacji została cofnięta."
+
+ layouts:
+ admin:
+ title: "Doorkeeper"
+ nav:
+ oauth2_provider: "Dostawca OAuth2"
+ applications: "Aplikacje"
+ home: "Strona główna"
+ application:
+ title: "Wymagana autoryzacja OAuth"
\ No newline at end of file
diff --git a/config/locales/mailers/invitation_mailer/pl.yml b/config/locales/mailers/invitation_mailer/pl.yml
new file mode 100644
index 000000000..ff833bd77
--- /dev/null
+++ b/config/locales/mailers/invitation_mailer/pl.yml
@@ -0,0 +1,5 @@
+---
+pl:
+ invitation_mailer:
+ invite_email:
+ subject: "%{inviter} zaprosił(a) Cię do dołączenia do swojego gospodarstwa domowego w %{product_name}!"
diff --git a/config/locales/mailers/pdf_import_mailer/de.yml b/config/locales/mailers/pdf_import_mailer/de.yml
new file mode 100644
index 000000000..072a80c2e
--- /dev/null
+++ b/config/locales/mailers/pdf_import_mailer/de.yml
@@ -0,0 +1,5 @@
+---
+de:
+ pdf_import_mailer:
+ next_steps:
+ subject: "Ihr PDF-Dokument wurde analysiert - %{product_name}"
diff --git a/config/locales/mailers/pdf_import_mailer/es.yml b/config/locales/mailers/pdf_import_mailer/es.yml
new file mode 100644
index 000000000..d5d423152
--- /dev/null
+++ b/config/locales/mailers/pdf_import_mailer/es.yml
@@ -0,0 +1,5 @@
+---
+es:
+ pdf_import_mailer:
+ next_steps:
+ subject: "Tu documento PDF ha sido analizado - %{product_name}"
\ No newline at end of file
diff --git a/config/locales/mailers/pdf_import_mailer/pl.yml b/config/locales/mailers/pdf_import_mailer/pl.yml
new file mode 100644
index 000000000..713401ee8
--- /dev/null
+++ b/config/locales/mailers/pdf_import_mailer/pl.yml
@@ -0,0 +1,5 @@
+---
+pl:
+ pdf_import_mailer:
+ next_steps:
+ subject: "Twój dokument PDF został przeanalizowany - %{product_name}"
diff --git a/config/locales/models/account/pl.yml b/config/locales/models/account/pl.yml
new file mode 100644
index 000000000..885e5830a
--- /dev/null
+++ b/config/locales/models/account/pl.yml
@@ -0,0 +1,23 @@
+---
+pl:
+ activerecord:
+ attributes:
+ account:
+ balance: Saldo
+ currency: Waluta
+ family: "%{moniker}"
+ family_id: "%{moniker}"
+ name: Nazwa
+ subtype: Podtyp
+ models:
+ account: Konto
+ account/bond: Obligacje
+ account/credit_card: Karta kredytowa
+ account/crypto: Kryptowaluty
+ account/depository: Konto bankowe
+ account/investment: Inwestycje
+ account/loan: Pożyczka
+ account/other_asset: Inne aktywa
+ account/other_liability: Inne zobowiązania
+ account/property: Nieruchomość
+ account/vehicle: Pojazd
diff --git a/config/locales/models/address/pl.yml b/config/locales/models/address/pl.yml
new file mode 100644
index 000000000..babc3e590
--- /dev/null
+++ b/config/locales/models/address/pl.yml
@@ -0,0 +1,11 @@
+---
+pl:
+ address:
+ attributes:
+ country: Kraj
+ line1: Ulica i numer
+ line2: Numer mieszkania (opcjonalnie)
+ locality: Miejscowość
+ postal_code: Kod pocztowy
+ region: Województwo/Region
+ format: "%{line1} %{line2}, %{locality}, %{region} %{postal_code} %{country}"
diff --git a/config/locales/models/category/de.yml b/config/locales/models/category/de.yml
new file mode 100644
index 000000000..58fa84e3e
--- /dev/null
+++ b/config/locales/models/category/de.yml
@@ -0,0 +1,7 @@
+---
+de:
+ models:
+ category:
+ uncategorized: Nicht kategorisiert
+ other_investments: Sonstige Anlagen
+ investment_contributions: Anlagebeiträge
diff --git a/config/locales/models/category/es.yml b/config/locales/models/category/es.yml
new file mode 100644
index 000000000..90a24cd55
--- /dev/null
+++ b/config/locales/models/category/es.yml
@@ -0,0 +1,7 @@
+---
+es:
+ models:
+ category:
+ uncategorized: Sin clasificar
+ other_investments: Otras inversiones
+ investment_contributions: Aportaciones a inversiones
\ No newline at end of file
diff --git a/config/locales/models/category/pl.yml b/config/locales/models/category/pl.yml
new file mode 100644
index 000000000..da69aeae9
--- /dev/null
+++ b/config/locales/models/category/pl.yml
@@ -0,0 +1,7 @@
+---
+pl:
+ models:
+ category:
+ uncategorized: Bez kategorii
+ other_investments: Inne inwestycje
+ investment_contributions: Wpłaty inwestycyjne
diff --git a/config/locales/models/coinbase_account/de.yml b/config/locales/models/coinbase_account/de.yml
new file mode 100644
index 000000000..9afe7a778
--- /dev/null
+++ b/config/locales/models/coinbase_account/de.yml
@@ -0,0 +1,5 @@
+---
+de:
+ coinbase:
+ processor:
+ paid_via: "Bezahlt über %{method}"
diff --git a/config/locales/models/coinbase_account/es.yml b/config/locales/models/coinbase_account/es.yml
new file mode 100644
index 000000000..88904258c
--- /dev/null
+++ b/config/locales/models/coinbase_account/es.yml
@@ -0,0 +1,5 @@
+---
+es:
+ coinbase:
+ processor:
+ paid_via: "Pagado mediante %{method}"
\ No newline at end of file
diff --git a/config/locales/models/coinbase_account/pl.yml b/config/locales/models/coinbase_account/pl.yml
new file mode 100644
index 000000000..52c5f099f
--- /dev/null
+++ b/config/locales/models/coinbase_account/pl.yml
@@ -0,0 +1,5 @@
+---
+pl:
+ coinbase:
+ processor:
+ paid_via: Zapłacono przez %{method}
diff --git a/config/locales/models/coinstats_item/de.yml b/config/locales/models/coinstats_item/de.yml
new file mode 100644
index 000000000..d56bc04aa
--- /dev/null
+++ b/config/locales/models/coinstats_item/de.yml
@@ -0,0 +1,12 @@
+---
+de:
+ models:
+ coinstats_item:
+ syncer:
+ importing_wallets: Wallets werden von CoinStats importiert...
+ checking_configuration: Wallet-Konfiguration wird geprüft...
+ wallets_need_setup:
+ one: "1 Wallet muss eingerichtet werden..."
+ other: "%{count} Wallets müssen eingerichtet werden..."
+ processing_holdings: Bestände werden verarbeitet...
+ calculating_balances: Salden werden berechnet...
diff --git a/config/locales/models/coinstats_item/en.yml b/config/locales/models/coinstats_item/en.yml
index e02b561cf..b5feb8217 100644
--- a/config/locales/models/coinstats_item/en.yml
+++ b/config/locales/models/coinstats_item/en.yml
@@ -3,8 +3,8 @@ en:
models:
coinstats_item:
syncer:
- importing_wallets: Importing wallets from CoinStats...
- checking_configuration: Checking wallet configuration...
- wallets_need_setup: "%{count} wallets need setup..."
+ importing_wallets: Importing crypto accounts from CoinStats...
+ checking_configuration: Checking CoinStats account configuration...
+ wallets_need_setup: "%{count} crypto accounts need setup..."
processing_holdings: Processing holdings...
calculating_balances: Calculating balances...
diff --git a/config/locales/models/coinstats_item/es.yml b/config/locales/models/coinstats_item/es.yml
new file mode 100644
index 000000000..c2bc6d3fc
--- /dev/null
+++ b/config/locales/models/coinstats_item/es.yml
@@ -0,0 +1,10 @@
+---
+es:
+ models:
+ coinstats_item:
+ syncer:
+ importing_wallets: Importando carteras desde CoinStats...
+ checking_configuration: Comprobando la configuración de la cartera...
+ wallets_need_setup: "%{count} carteras necesitan configuración..."
+ processing_holdings: Procesando activos...
+ calculating_balances: Calculando saldos...
\ No newline at end of file
diff --git a/config/locales/models/coinstats_item/pl.yml b/config/locales/models/coinstats_item/pl.yml
new file mode 100644
index 000000000..08b2f9105
--- /dev/null
+++ b/config/locales/models/coinstats_item/pl.yml
@@ -0,0 +1,14 @@
+---
+pl:
+ models:
+ coinstats_item:
+ syncer:
+ importing_wallets: Importowanie portfeli z CoinStats...
+ checking_configuration: Sprawdzanie konfiguracji portfela...
+ wallets_need_setup:
+ one: "%{count} portfel wymaga konfiguracji..."
+ few: "%{count} portfele wymagają konfiguracji..."
+ many: "%{count} portfeli wymaga konfiguracji..."
+ other: "%{count} portfela wymaga konfiguracji..."
+ processing_holdings: Przetwarzanie posiadanych aktywów...
+ calculating_balances: Obliczanie sald...
diff --git a/config/locales/models/entry/pl.yml b/config/locales/models/entry/pl.yml
new file mode 100644
index 000000000..a6b6c19b9
--- /dev/null
+++ b/config/locales/models/entry/pl.yml
@@ -0,0 +1,9 @@
+---
+pl:
+ activerecord:
+ errors:
+ models:
+ entry:
+ attributes:
+ base:
+ invalid_sell_quantity: nie można sprzedać %{sell_qty} udziałów %{ticker}, ponieważ posiadasz tylko %{current_qty} udziałów
diff --git a/config/locales/models/import/pl.yml b/config/locales/models/import/pl.yml
new file mode 100644
index 000000000..2c10762b6
--- /dev/null
+++ b/config/locales/models/import/pl.yml
@@ -0,0 +1,13 @@
+---
+pl:
+ activerecord:
+ attributes:
+ import:
+ currency: Waluta
+ number_format: Format liczby
+ errors:
+ models:
+ import:
+ attributes:
+ raw_file_str:
+ invalid_csv_format: nie jest prawidłowym formatem CSV
diff --git a/config/locales/models/provider_warnings/pl.yml b/config/locales/models/provider_warnings/pl.yml
new file mode 100644
index 000000000..89fa5c9aa
--- /dev/null
+++ b/config/locales/models/provider_warnings/pl.yml
@@ -0,0 +1,4 @@
+---
+pl:
+ provider_warnings:
+ limited_investment_data: Dane inwestycyjne od tego dostawcy są ograniczone. Etykiety transakcji (Kupno, Sprzedaż, Dywidenda) są niedostępne, co może wpłynąć na dokładność budżetu. Rozważ tworzenie reguł wykluczenia lub kategoryzacji transakcji inwestycyjnych.
diff --git a/config/locales/models/time_series/value/pl.yml b/config/locales/models/time_series/value/pl.yml
new file mode 100644
index 000000000..c63b9ca38
--- /dev/null
+++ b/config/locales/models/time_series/value/pl.yml
@@ -0,0 +1,9 @@
+---
+pl:
+ activemodel:
+ errors:
+ models:
+ time_series/value:
+ attributes:
+ value:
+ must_be_a_money_or_numeric: musi być typu Money lub Numeric
diff --git a/config/locales/models/transaction/en.yml b/config/locales/models/transaction/en.yml
new file mode 100644
index 000000000..7359a69b4
--- /dev/null
+++ b/config/locales/models/transaction/en.yml
@@ -0,0 +1,11 @@
+---
+en:
+ activerecord:
+ errors:
+ models:
+ transaction:
+ attributes:
+ attachments:
+ too_many: "cannot exceed %{max} files per transaction"
+ too_large: "file %{index} is too large (maximum %{max_mb}MB)"
+ invalid_format: "file %{index} has unsupported format (%{file_format})"
diff --git a/config/locales/models/transaction/pl.yml b/config/locales/models/transaction/pl.yml
new file mode 100644
index 000000000..ec6c6304a
--- /dev/null
+++ b/config/locales/models/transaction/pl.yml
@@ -0,0 +1,11 @@
+---
+pl:
+ activerecord:
+ errors:
+ models:
+ transaction:
+ attributes:
+ attachments:
+ too_many: nie może przekraczać %{max} plików na transakcję
+ too_large: plik %{index} jest zbyt duży (maksymalnie %{max_mb}MB)
+ invalid_format: plik %{index} ma nieobsługiwany format (%{file_format})
diff --git a/config/locales/models/transaction/pt-BR.yml b/config/locales/models/transaction/pt-BR.yml
new file mode 100644
index 000000000..725f792b9
--- /dev/null
+++ b/config/locales/models/transaction/pt-BR.yml
@@ -0,0 +1,11 @@
+---
+pt-BR:
+ activerecord:
+ errors:
+ models:
+ transaction:
+ attributes:
+ attachments:
+ too_many: "não é possível exceder %{max} arquivos por transação"
+ too_large: "O arquivo %{index} é muito grande (máximo de %{max_mb}MB)"
+ invalid_format: "O arquivo %{index} possui um formato não suportado (%{file_format})"
diff --git a/config/locales/models/transfer/pl.yml b/config/locales/models/transfer/pl.yml
new file mode 100644
index 000000000..b61fe86a5
--- /dev/null
+++ b/config/locales/models/transfer/pl.yml
@@ -0,0 +1,18 @@
+---
+pl:
+ activerecord:
+ errors:
+ models:
+ transfer:
+ attributes:
+ base:
+ inflow_cannot_be_in_multiple_transfers: Transakcja wpływu nie może być częścią wielu przelewów
+ must_be_from_different_accounts: Przelew musi dotyczyć różnych kont
+ must_be_from_same_family: Przelew musi być w ramach tego samego gospodarstwa domowego
+ must_be_within_date_range: Daty transakcji przelewu muszą być w ciągu 4 dni od siebie
+ must_have_opposite_amounts: Transakcje przelewu muszą mieć przeciwne kwoty
+ must_have_single_currency: Przelew musi być w jednej walucie
+ outflow_cannot_be_in_multiple_transfers: Transakcja wypływu nie może być częścią wielu przelewów
+ transfer:
+ name: Przelew do %{to_account}
+ payment_name: Płatność do %{to_account}
diff --git a/config/locales/models/trend/pl.yml b/config/locales/models/trend/pl.yml
new file mode 100644
index 000000000..221d4c262
--- /dev/null
+++ b/config/locales/models/trend/pl.yml
@@ -0,0 +1,13 @@
+---
+pl:
+ activemodel:
+ errors:
+ models:
+ trend:
+ attributes:
+ current:
+ must_be_of_the_same_type_as_previous: musi być tego samego typu co poprzednia wartość
+ must_be_of_type_money_numeric_or_nil: musi być typu Money, Numeric lub nil
+ previous:
+ must_be_of_the_same_type_as_current: musi być tego samego typu co aktualna wartość
+ must_be_of_type_money_numeric_or_nil: musi być typu Money, Numeric lub nil
diff --git a/config/locales/models/user/pl.yml b/config/locales/models/user/pl.yml
new file mode 100644
index 000000000..2a696f2f6
--- /dev/null
+++ b/config/locales/models/user/pl.yml
@@ -0,0 +1,20 @@
+---
+pl:
+ activerecord:
+ attributes:
+ user:
+ email: Email
+ family: "%{moniker}"
+ family_id: "%{moniker}"
+ first_name: Imię
+ last_name: Nazwisko
+ password: Hasło
+ password_confirmation: Potwierdzenie hasła
+ errors:
+ models:
+ user:
+ attributes:
+ base:
+ cannot_deactivate_admin_with_other_users: Administrator nie może usunąć konta, gdy obecni są inni użytkownicy. Najpierw usuń wszystkich członków.
+ profile_image:
+ invalid_file_size: rozmiar pliku nie może przekraczać %{max_megabytes}MB
diff --git a/config/locales/views/account_sharings/en.yml b/config/locales/views/account_sharings/en.yml
new file mode 100644
index 000000000..2a219a054
--- /dev/null
+++ b/config/locales/views/account_sharings/en.yml
@@ -0,0 +1,29 @@
+---
+en:
+ account_sharings:
+ show:
+ title: Account Sharing
+ subtitle: Control who can see and interact with this account
+ member: Member
+ permission: Permission
+ shared: Shared
+ no_members: No other members in your %{moniker} to share with
+ permissions:
+ full_control: Full control
+ full_control_description: Can view, edit, and manage transactions
+ read_write: Can annotate
+ read_write_description: Can categorize, tag, and add notes
+ read_only: View only
+ read_only_description: Can only view account data
+ save: Save sharing settings
+ owner_label: "Owner: %{name}"
+ shared_with_count:
+ one: Shared with 1 member
+ other: "Shared with %{count} members"
+ include_in_finances: Include in my budgets & reports
+ exclude_from_finances: Exclude from my budgets & reports
+ finance_toggle_description: Count this account in your net worth, budgets, and reports
+ update:
+ success: Sharing settings updated
+ not_owner: Only the account owner can manage sharing
+ finance_toggle_success: Finance inclusion preference updated
diff --git a/config/locales/views/account_sharings/pl.yml b/config/locales/views/account_sharings/pl.yml
new file mode 100644
index 000000000..010b2b54b
--- /dev/null
+++ b/config/locales/views/account_sharings/pl.yml
@@ -0,0 +1,31 @@
+---
+pl:
+ account_sharings:
+ show:
+ title: Udostępnianie konta
+ subtitle: Zarządzaj tym, kto może przeglądać i używać tego konta
+ member: Członek
+ permission: Uprawnienie
+ shared: Udostępnione
+ no_members: Brak innych członków w Twoim %{moniker}, którym można udostępnić
+ permissions:
+ full_control: Pełna kontrola
+ full_control_description: Może przeglądać, edytować i zarządzać transakcjami
+ read_write: Odczyt i zapis
+ read_write_description: Może kategoryzować, tagować i dodawać notatki
+ read_only: Tylko podgląd
+ read_only_description: Może tylko przeglądać dane konta
+ save: Zapisz ustawienia udostępniania
+ owner_label: 'Właściciel: %{name}'
+ shared_with_count:
+ one: Udostępniono 1 członkowi
+ few: Udostępniono %{count} członkom
+ many: Udostępniono %{count} członkom
+ other: Udostępniono %{count} członkom
+ include_in_finances: Uwzględniaj w moich budżetach i raportach
+ exclude_from_finances: Wykluczaj z moich budżetów i raportów
+ finance_toggle_description: Uwzględniaj to konto w majątku netto, budżetach i raportach
+ update:
+ success: Zaktualizowano ustawienia udostępniania
+ not_owner: Tylko właściciel konta może zarządzać udostępnianiem
+ finance_toggle_success: Zaktualizowano preferencję uwzględniania w finansach
diff --git a/config/locales/views/account_sharings/pt-BR.yml b/config/locales/views/account_sharings/pt-BR.yml
new file mode 100644
index 000000000..2fa59ae1d
--- /dev/null
+++ b/config/locales/views/account_sharings/pt-BR.yml
@@ -0,0 +1,29 @@
+---
+pt-BR:
+ account_sharings:
+ show:
+ title: Compartilhamento de conta
+ subtitle: Controle quem pode ver e interagir com esta conta.
+ member: Membro
+ permission: Permissão
+ shared: Compartilhada
+ no_members: Não há outros membros em seu %{moniker} com quem compartilhar.
+ permissions:
+ full_control: Controle total
+ full_control_description: É possível visualizar, editar e gerenciar transações.
+ read_write: Pode fazer anotações
+ read_write_description: É possível categorizar, etiquetar e adicionar notas.
+ read_only: Somente visualização
+ read_only_description: Só é possível visualizar os dados da conta.
+ save: Salvar configurações de compartilhamento
+ owner_label: "Proprietário: %{name}"
+ shared_with_count:
+ one: Compartilhado com 1 membro
+ other: "Compartilhado com %{count} membros"
+ include_in_finances: Incluir nos meus orçamentos e relatórios
+ exclude_from_finances: Excluir dos meus orçamentos e relatórios
+ finance_toggle_description: Inclua essa conta em seu patrimônio líquido, orçamentos e relatórios.
+ update:
+ success: Configurações de compartilhamento atualizadas
+ not_owner: Somente o proprietário da conta pode gerenciar o compartilhamento.
+ finance_toggle_success: Preferência de inclusão financeira atualizada
diff --git a/config/locales/views/accounts/de.yml b/config/locales/views/accounts/de.yml
index 0e9196476..f163a31b1 100644
--- a/config/locales/views/accounts/de.yml
+++ b/config/locales/views/accounts/de.yml
@@ -1,34 +1,58 @@
+---
de:
accounts:
account:
+ edit: Bearbeiten
link_lunchflow: Mit Lunch Flow verknüpfen
+ link_provider: Mit Provider verknüpfen
+ unlink_provider: Von Provider trennen
troubleshoot: Fehlerbehebung
+ enable: Konto aktivieren
+ disable: Konto deaktivieren
+ set_default: Als Standard festlegen
+ remove_default: Standard aufheben
+ default_label: Standard
+ delete: Konto löschen
chart:
data_not_available: Für den ausgewählten Zeitraum sind keine Daten verfügbar
create:
success: "%{type}-Konto erstellt"
+ set_default:
+ depository_only: "Nur Bargeld- und Kreditkartenkonten können als Standard festgelegt werden."
destroy:
success: "%{type}-Konto zur Löschung vorgemerkt"
+ cannot_delete_linked: "Ein verknüpftes Konto kann nicht gelöscht werden. Bitte trennen Sie es zuerst."
empty:
empty_message: Füge ein Konto über eine Verbindung, einen Import oder manuell hinzu
new_account: Neues Konto
no_accounts: Noch keine Konten vorhanden
form:
- balance: Aktueller Kontostand
+ balance: "Kontostand zum Datum:"
+ opening_balance_date_label: Eröffnungsdatum des Kontostands
name_label: Kontoname
name_placeholder: Beispielkontoname
+ additional_details: Weitere Angaben
+ institution_name_label: Name der Institution
+ institution_name_placeholder: "z. B. Chase Bank"
+ institution_domain_label: Domain der Institution
+ institution_domain_placeholder: "z. B. chase.com"
+ notes_label: Notizen
+ notes_placeholder: Zusätzliche Informationen wie Kontonummern, Sort codes, IBAN, Routing-Nummern usw.
index:
accounts: Konten
manual_accounts:
other_accounts: Andere Konten
new_account: Neues Konto
sync: Alle synchronisieren
+ sync_all:
+ syncing: "Konten werden synchronisiert..."
new:
import_accounts: Konten importieren
method_selector:
connected_entry: Konto verknüpfen
connected_entry_eu: EU-Konto verknüpfen
link_with_provider: "Mit %{provider} verknüpfen"
+ lunchflow_entry: Lunch-Flow-Konto verknüpfen
manual_entry: Kontostand manuell eingeben
title: Wie möchtest du es hinzufügen
title: Was möchtest du hinzufügen
@@ -36,13 +60,22 @@ de:
activity:
amount: Betrag
balance: Kontostand
+ confirmed: Bestätigt
date: Datum
entries: Buchungen
entry: Buchung
+ filter: Filtern
new: Neu
+ new_activity: Neue Aktivität
new_balance: Neuer Kontostand
+ new_trade: Neuer Trade
new_transaction: Neue Transaktion
+ new_transfer: Neue Überweisung
no_entries: Keine Buchungen gefunden
+ pending: Ausstehend
+ search:
+ placeholder: Buchungen nach Name suchen
+ status: Status
title: Aktivität
chart:
balance: Kontostand
@@ -53,6 +86,8 @@ de:
confirm_title: Konto löschen
edit: Bearbeiten
import: Transaktionen importieren
+ import_trades: Trades importieren
+ import_transactions: Transaktionen importieren
manage: Konten verwalten
update:
success: "%{type}-Konto aktualisiert"
@@ -78,6 +113,42 @@ de:
credit_card: Kreditkarte
loan: Darlehen
other_liability: Sonstige Verbindlichkeit
+ subtype_regions:
+ us: Vereinigte Staaten
+ uk: Vereinigtes Königreich
+ ca: Kanada
+ au: Australien
+ eu: Europa
+ generic: Generisch
+ tax_treatments:
+ taxable: Versteuerbar
+ tax_deferred: Steuerlich aufgeschoben
+ tax_exempt: Steuerfrei
+ tax_advantaged: Steuerbegünstigt
+ tax_treatment_descriptions:
+ taxable: Gewinne werden bei Realisierung besteuert
+ tax_deferred: Beiträge abzugsfähig, Besteuerung bei Auszahlung
+ tax_exempt: Beiträge nach Steuer, Gewinne nicht besteuert
+ tax_advantaged: Besondere Steuervorteile unter Bedingungen
+ confirm_unlink:
+ title: Konto vom Provider trennen?
+ description_html: "Sie sind dabei, %{account_name} von %{provider_name} zu trennen. Das Konto wird zu einem manuellen Konto."
+ warning_title: Was das bedeutet
+ warning_no_sync: Das Konto wird nicht mehr automatisch mit dem Provider synchronisiert
+ warning_manual_updates: Sie müssen Buchungen und Salden manuell pflegen
+ warning_transactions_kept: Bestehende Buchungen und Salden bleiben erhalten
+ warning_can_delete: Nach dem Trennen können Sie das Konto bei Bedarf löschen
+ confirm_button: Bestätigen und trennen
+ unlink:
+ success: "Konto erfolgreich getrennt. Es ist jetzt ein manuelles Konto."
+ not_linked: "Konto ist mit keinem Provider verknüpft"
+ error: "Konto konnte nicht getrennt werden: %{error}"
+ generic_error: "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut."
+ select_provider:
+ title: Provider zum Verknüpfen auswählen
+ description: "Wählen Sie den Provider, mit dem %{account_name} verknüpft werden soll"
+ already_linked: "Konto ist bereits mit einem Provider verknüpft"
+ no_providers: "Derzeit sind keine Provider konfiguriert"
email_confirmations:
new:
diff --git a/config/locales/views/accounts/en.yml b/config/locales/views/accounts/en.yml
index ef748b6b7..23af6cfab 100644
--- a/config/locales/views/accounts/en.yml
+++ b/config/locales/views/accounts/en.yml
@@ -1,24 +1,37 @@
---
en:
accounts:
+ not_authorized: "You don't have permission to manage this account"
account:
+ edit: Edit
link_lunchflow: Link with Lunch Flow
link_provider: Link with provider
unlink_provider: Unlink from provider
troubleshoot: Troubleshoot
+ enable: Enable account
+ disable: Disable account
+ set_default: Set as default
+ remove_default: Unset default
+ default_label: Default
+ delete: Delete account
+ sharing: Sharing
chart:
data_not_available: Data not available for the selected period
create:
success: "%{type} account created"
+ set_default:
+ depository_only: "Only cash and credit card accounts can be set as default."
destroy:
success: "%{type} account scheduled for deletion"
cannot_delete_linked: "Cannot delete a linked account. Please unlink it first."
+ failed: "Resource deletion failed. Try again later."
empty:
empty_message: Add an account either via connection, importing or entering manually.
new_account: New account
no_accounts: No accounts yet
form:
- balance: Current balance
+ balance: "Balance on date:"
+ opening_balance_date_label: Opening balance date
name_label: Account name
name_placeholder: Example account name
additional_details: Additional details
diff --git a/config/locales/views/accounts/es.yml b/config/locales/views/accounts/es.yml
index c99a987a1..613bf93ce 100644
--- a/config/locales/views/accounts/es.yml
+++ b/config/locales/views/accounts/es.yml
@@ -2,14 +2,26 @@
es:
accounts:
account:
+ edit: Editar
link_lunchflow: Vincular con Lunch Flow
+ link_provider: Vincular con proveedor
+ unlink_provider: Desvincular de proveedor
troubleshoot: Solucionar problemas
+ enable: Activar cuenta
+ disable: Desactivar cuenta
+ set_default: Establecer como predeterminada
+ remove_default: Quitar predeterminada
+ default_label: Predeterminada
+ delete: Eliminar cuenta
chart:
data_not_available: Datos no disponibles para el período seleccionado
create:
success: "Cuenta %{type} creada"
+ set_default:
+ depository_only: "Solo las cuentas de efectivo y tarjeta de crédito pueden establecerse como predeterminadas."
destroy:
success: "Cuenta %{type} programada para eliminación"
+ cannot_delete_linked: "No se puede eliminar una cuenta vinculada. Por favor, desvincúlela primero."
empty:
empty_message: Añade una cuenta mediante conexión, importación o introducción manual.
new_account: Nueva cuenta
@@ -18,12 +30,21 @@ es:
balance: Saldo actual
name_label: Nombre de la cuenta
name_placeholder: Ejemplo de nombre de cuenta
+ additional_details: Detalles adicionales
+ institution_name_label: Nombre de la institución
+ institution_name_placeholder: ej. Chase Bank
+ institution_domain_label: Dominio de la institución
+ institution_domain_placeholder: ej. chase.com
+ notes_label: Notas
+ notes_placeholder: Guarda información adicional como números de cuenta, códigos de sucursal, IBAN, números de ruta, etc.
index:
accounts: Cuentas
manual_accounts:
other_accounts: Otras cuentas
new_account: Nueva cuenta
sync: Sincronizar todo
+ sync_all:
+ syncing: "Sincronizando cuentas..."
new:
import_accounts: Importar cuentas
method_selector:
@@ -38,15 +59,22 @@ es:
activity:
amount: Cantidad
balance: Saldo
+ confirmed: Confirmado
date: Fecha
entries: entradas
entry: entrada
+ filter: Filtrar
new: Nuevo
+ new_activity: Nueva actividad
new_balance: Nuevo saldo
+ new_trade: Nueva operación
new_transaction: Nueva transacción
+ new_transfer: Nueva transferencia
no_entries: No se encontraron entradas
+ pending: Pendiente
search:
placeholder: Buscar entradas por nombre
+ status: Estado
title: Actividad
chart:
balance: Saldo
@@ -57,6 +85,8 @@ es:
confirm_title: ¿Eliminar cuenta?
edit: Editar
import: Importar transacciones
+ import_trades: Importar operaciones
+ import_transactions: Importar transacciones
manage: Gestionar cuentas
update:
success: "Cuenta %{type} actualizada"
@@ -82,8 +112,44 @@ es:
credit_card: Tarjeta de crédito
loan: Préstamo
other_liability: Otra deuda
+ tax_treatments:
+ taxable: Sujeto a impuestos
+ tax_deferred: Impuestos diferidos
+ tax_exempt: Exento de impuestos
+ tax_advantaged: Ventaja fiscal
+ tax_treatment_descriptions:
+ taxable: Ganancias gravadas al realizarse
+ tax_deferred: Aportaciones deducibles, impuestos al retirar
+ tax_exempt: Aportaciones después de impuestos, ganancias exentas
+ tax_advantaged: Beneficios fiscales especiales con condiciones
+ subtype_regions:
+ us: Estados Unidos
+ uk: Reino Unido
+ ca: Canadá
+ au: Australia
+ eu: Europa
+ generic: General
+ confirm_unlink:
+ title: ¿Desvincular cuenta del proveedor?
+ description_html: "Estás a punto de desvincular %{account_name} de %{provider_name}. Esto la convertirá en una cuenta manual."
+ warning_title: Qué significa esto
+ warning_no_sync: La cuenta dejará de sincronizarse automáticamente con tu proveedor
+ warning_manual_updates: Deberás añadir transacciones y actualizar saldos manualmente
+ warning_transactions_kept: Se conservarán todas las transacciones y saldos existentes
+ warning_can_delete: Tras desvincularla, podrás eliminar la cuenta si es necesario
+ confirm_button: Confirmar y desvincular
+ unlink:
+ success: "Cuenta desvinculada correctamente. Ahora es una cuenta manual."
+ not_linked: "La cuenta no está vinculada a un proveedor"
+ error: "Error al desvincular la cuenta: %{error}"
+ generic_error: "Ha ocurrido un error inesperado. Por favor, inténtalo de nuevo."
+ select_provider:
+ title: Selecciona un proveedor para vincular
+ description: "Elige qué proveedor quieres usar para vincular %{account_name}"
+ already_linked: "La cuenta ya está vinculada a un proveedor"
+ no_providers: "No hay proveedores configurados actualmente"
email_confirmations:
new:
invalid_token: Enlace de confirmación inválido o caducado.
- success_login: Tu correo electrónico ha sido confirmado. Por favor, inicia sesión con tu nueva dirección de correo electrónico.
+ success_login: Tu correo electrónico ha sido confirmado. Por favor, inicia sesión con tu nueva dirección de correo electrónico.
\ No newline at end of file
diff --git a/config/locales/views/accounts/fr.yml b/config/locales/views/accounts/fr.yml
index 3c9dae3bb..7d2eafcb8 100644
--- a/config/locales/views/accounts/fr.yml
+++ b/config/locales/views/accounts/fr.yml
@@ -2,14 +2,23 @@
fr:
accounts:
account:
+ edit: Modifier
link_lunchflow: Lier avec Lunch Flow
link_provider: Lier avec un fournisseur
unlink_provider: Délier du fournisseur
troubleshoot: Dépannage
+ enable: Activer le compte
+ disable: Désactiver le compte
+ set_default: Définir par défaut
+ remove_default: Retirer par défaut
+ default_label: Par défaut
+ delete: Supprimer le compte
chart:
data_not_available: Données non disponibles pour la période sélectionnée
create:
success: "Compte %{type} créé"
+ set_default:
+ depository_only: "Seuls les comptes de liquidités et de carte de crédit peuvent être définis par défaut."
destroy:
success: "Le compte %{type} a été préparé à la suppression"
cannot_delete_linked: "Impossible de supprimer un compte lié. Veuillez d'abord le délier."
diff --git a/config/locales/views/accounts/pl.yml b/config/locales/views/accounts/pl.yml
new file mode 100644
index 000000000..88418d805
--- /dev/null
+++ b/config/locales/views/accounts/pl.yml
@@ -0,0 +1,158 @@
+---
+pl:
+ accounts:
+ not_authorized: Nie masz uprawnień do zarządzania tym kontem
+ account:
+ edit: Edytuj
+ link_lunchflow: Połącz z Lunch Flow
+ link_provider: Połącz z dostawcą
+ unlink_provider: Odłącz od dostawcy
+ troubleshoot: Rozwiąż problem
+ enable: Włącz konto
+ disable: Wyłącz konto
+ set_default: Ustaw jako domyślne
+ remove_default: Usuń status domyślnego
+ default_label: Domyślne
+ delete: Usuń konto
+ sharing: Udostępnianie
+ chart:
+ data_not_available: Dane niedostępne dla wybranego okresu
+ create:
+ success: Utworzono konto %{type}
+ set_default:
+ depository_only: Tylko konta gotówkowe i karty kredytowe mogą być ustawione jako domyślne.
+ destroy:
+ success: Zaplanowano usunięcie konta %{type}
+ cannot_delete_linked: Nie można usunąć połączonego konta. Najpierw je odłącz.
+ empty:
+ empty_message: Dodaj konto przez połączenie, import lub ręczne wprowadzenie.
+ new_account: Nowe konto
+ no_accounts: Brak kont
+ form:
+ balance: 'Saldo na dzień:'
+ opening_balance_date_label: Data salda początkowego
+ name_label: Nazwa konta
+ name_placeholder: Przykładowa nazwa konta
+ additional_details: Dodatkowe informacje
+ institution_name_label: Nazwa instytucji
+ institution_name_placeholder: np. PKO Bank Polski
+ institution_domain_label: Domena instytucji
+ institution_domain_placeholder: np. pkobp.pl
+ notes_label: Notatki
+ notes_placeholder: Zapisz dodatkowe informacje, takie jak numery kont, kody rozliczeniowe, IBAN, numery routingowe itp.
+ index:
+ accounts: Konta
+ manual_accounts:
+ other_accounts: Inne konta
+ new_account: Nowe konto
+ sync: Synchronizuj wszystko
+ sync_all:
+ syncing: Trwa synchronizacja kont...
+ new:
+ import_accounts: Importuj konta
+ method_selector:
+ connected_entry: Połącz konto
+ connected_entry_eu: Połącz konto z UE
+ link_with_provider: Połącz z %{provider}
+ lunchflow_entry: Połącz konto Lunch Flow
+ manual_entry: Wprowadź saldo konta
+ title: Jak chcesz je dodać?
+ title: Co chcesz dodać?
+ show:
+ activity:
+ amount: Kwota
+ balance: Saldo
+ confirmed: Potwierdzone
+ date: Data
+ entries: wpisy
+ entry: wpis
+ filter: Filtr
+ new: Nowy
+ new_activity: Nowa aktywność
+ new_balance: Nowe saldo
+ new_trade: Nowa transakcja giełdowa
+ new_transaction: Nowa transakcja
+ new_transfer: Nowy przelew
+ no_entries: Nie znaleziono wpisów
+ pending: Oczekujące
+ search:
+ placeholder: Szukaj wpisów po nazwie
+ status: Status
+ title: Aktywność
+ chart:
+ balance: Saldo
+ owed: Kwota zadłużenia
+ menu:
+ confirm_accept: Usuń "%{name}"
+ confirm_body_html: "
Usuwając to konto, usuniesz jego historię wartości, co wpłynie na różne aspekty Twojego ogólnego bilansu. Ta akcja będzie miała bezpośredni wpływ na obliczenia majątku netto i wykresy konta.
Po usunięciu nie będzie możliwości przywrócenia informacji o koncie, ponieważ trzeba będzie dodać je ponownie jako nowe konto.
"
+ confirm_title: Usunąć konto?
+ edit: Edytuj
+ import: Importuj transakcje
+ import_trades: Importuj transakcje giełdowe
+ import_transactions: Importuj transakcje
+ manage: Zarządzaj kontami
+ update:
+ success: Zaktualizowano konto %{type}
+ sidebar:
+ missing_data: Brak danych historycznych
+ missing_data_description: "%{product} używa zewnętrznych dostawców do pobierania historycznych kursów walut, cen papierów wartościowych i innych danych. Te dane są wymagane do dokładnego obliczania historycznych sald kont."
+ configure_providers: Skonfiguruj tutaj swoich dostawców.
+ tabs:
+ all: Wszystkie
+ assets: Aktywa
+ debts: Zobowiązania
+ new_asset: Nowe aktywo
+ new_debt: Nowe zobowiązanie
+ new_account: Nowe konto
+ new_account_group: Nowe %{account_group}
+ types:
+ depository: Gotówka
+ investment: Inwestycje
+ crypto: Kryptowaluty
+ property: Nieruchomość
+ vehicle: Pojazd
+ bond: Obligacje
+ other_asset: Inne aktywo
+ credit_card: Karta kredytowa
+ loan: Pożyczka
+ other_liability: Inne zobowiązanie
+ tax_treatments:
+ taxable: Opodatkowane
+ tax_deferred: Odroczony podatek
+ tax_exempt: Zwolnione z podatku
+ tax_advantaged: Preferencyjne podatkowo
+ tax_treatment_descriptions:
+ taxable: Zyski opodatkowane przy realizacji
+ tax_deferred: Wpłaty odliczalne, opodatkowanie przy wypłacie
+ tax_exempt: Wpłaty po opodatkowaniu, zyski nieopodatkowane
+ tax_advantaged: Specjalne korzyści podatkowe pod pewnymi warunkami
+ subtype_regions:
+ us: Stany Zjednoczone
+ uk: Wielka Brytania
+ ca: Kanada
+ au: Australia
+ eu: Europa
+ generic: Ogólne
+ confirm_unlink:
+ title: Odłączyć konto od dostawcy?
+ description_html: Zaraz odłączysz %{account_name} od %{provider_name}. Konto zostanie przekształcone w konto ręczne.
+ warning_title: Co to oznacza
+ warning_no_sync: Konto nie będzie już synchronizować się automatycznie z dostawcą
+ warning_manual_updates: Trzeba będzie ręcznie dodawać transakcje i aktualizować salda
+ warning_transactions_kept: Wszystkie istniejące transakcje i salda zostaną zachowane
+ warning_can_delete: Po odłączeniu będzie można usunąć konto, jeśli zajdzie taka potrzeba
+ confirm_button: Potwierdź i odłącz
+ unlink:
+ success: Konto zostało pomyślnie odłączone. Teraz jest kontem ręcznym.
+ not_linked: Konto nie jest połączone z dostawcą
+ error: 'Nie udało się odłączyć konta: %{error}'
+ generic_error: Wystąpił nieoczekiwany błąd. Spróbuj ponownie.
+ select_provider:
+ title: Wybierz dostawcę do połączenia
+ description: Wybierz dostawcę, którego chcesz użyć do połączenia %{account_name}
+ already_linked: Konto jest już połączone z dostawcą
+ no_providers: Obecnie nie skonfigurowano żadnych dostawców
+ email_confirmations:
+ new:
+ invalid_token: Nieprawidłowy lub wygasły link potwierdzający.
+ success_login: Twój adres e-mail został potwierdzony. Zaloguj się przy użyciu nowego adresu e-mail.
diff --git a/config/locales/views/admin/invitations/en.yml b/config/locales/views/admin/invitations/en.yml
new file mode 100644
index 000000000..389566e19
--- /dev/null
+++ b/config/locales/views/admin/invitations/en.yml
@@ -0,0 +1,8 @@
+---
+en:
+ admin:
+ invitations:
+ destroy:
+ success: "Invitation deleted."
+ destroy_all:
+ success: "All invitations for this family have been deleted."
diff --git a/config/locales/views/admin/invitations/pl.yml b/config/locales/views/admin/invitations/pl.yml
new file mode 100644
index 000000000..162ac466e
--- /dev/null
+++ b/config/locales/views/admin/invitations/pl.yml
@@ -0,0 +1,8 @@
+---
+pl:
+ admin:
+ invitations:
+ destroy:
+ success: Zaproszenie zostało usunięte.
+ destroy_all:
+ success: Wszystkie zaproszenia dla tej rodziny zostały usunięte.
diff --git a/config/locales/views/admin/invitations/pt-BR.yml b/config/locales/views/admin/invitations/pt-BR.yml
new file mode 100644
index 000000000..1ea583b2f
--- /dev/null
+++ b/config/locales/views/admin/invitations/pt-BR.yml
@@ -0,0 +1,8 @@
+---
+pt-BR:
+ admin:
+ invitations:
+ destroy:
+ success: "Convite excluído."
+ destroy_all:
+ success: "Todos os convites para esta família foram cancelados."
diff --git a/config/locales/views/admin/sso_providers/de.yml b/config/locales/views/admin/sso_providers/de.yml
new file mode 100644
index 000000000..4f46a9b61
--- /dev/null
+++ b/config/locales/views/admin/sso_providers/de.yml
@@ -0,0 +1,115 @@
+---
+de:
+ admin:
+ unauthorized: "Sie sind nicht berechtigt, auf diesen Bereich zuzugreifen."
+ sso_providers:
+ index:
+ title: "SSO-Provider"
+ description: "Single-Sign-On-Authentifizierungsprovider für Ihre Instanz verwalten"
+ add_provider: "Provider hinzufügen"
+ no_providers_title: "Keine SSO-Provider"
+ no_providers_message: "Fügen Sie Ihren ersten SSO-Provider hinzu."
+ note: "Änderungen an SSO-Providern erfordern einen Neustart des Servers. Alternativ aktivieren Sie das Feature AUTH_PROVIDERS_SOURCE=db, um Provider dynamisch aus der Datenbank zu laden."
+ table:
+ name: "Name"
+ strategy: "Strategie"
+ status: "Status"
+ issuer: "Issuer"
+ actions: "Aktionen"
+ enabled: "Aktiviert"
+ disabled: "Deaktiviert"
+ legacy_providers_title: "Umgebungskonfigurierte Provider"
+ legacy_providers_notice: "Diese Provider werden über Umgebungsvariablen oder YAML konfiguriert und können nicht über diese Oberfläche verwaltet werden. Zur Verwaltung hier migrieren Sie sie zu datenbankgestützten Providern (AUTH_PROVIDERS_SOURCE=db) und legen Sie sie in der Oberfläche neu an."
+ env_configured: "Env/YAML"
+ new:
+ title: "SSO-Provider hinzufügen"
+ description: "Neuen Single-Sign-On-Authentifizierungsprovider konfigurieren"
+ edit:
+ title: "SSO-Provider bearbeiten"
+ description: "Konfiguration für %{label} aktualisieren"
+ create:
+ success: "SSO-Provider wurde erfolgreich erstellt."
+ update:
+ success: "SSO-Provider wurde erfolgreich aktualisiert."
+ destroy:
+ success: "SSO-Provider wurde erfolgreich gelöscht."
+ confirm: "Möchten Sie diesen Provider wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden."
+ toggle:
+ success_enabled: "SSO-Provider wurde erfolgreich aktiviert."
+ success_disabled: "SSO-Provider wurde erfolgreich deaktiviert."
+ confirm_enable: "Möchten Sie diesen Provider aktivieren?"
+ confirm_disable: "Möchten Sie diesen Provider deaktivieren?"
+ form:
+ basic_information: "Grundinformationen"
+ oauth_configuration: "OAuth/OIDC-Konfiguration"
+ strategy_label: "Strategie"
+ strategy_help: "Die zu verwendende Authentifizierungsstrategie"
+ name_label: "Name"
+ name_placeholder: "z. B. openid_connect, keycloak, authentik"
+ name_help: "Eindeutige Kennung (nur Kleinbuchstaben, Zahlen, Unterstriche)"
+ label_label: "Bezeichnung"
+ label_placeholder: "z. B. Anmelden mit Keycloak"
+ label_help: "Button-Text für Benutzer"
+ icon_label: "Icon"
+ icon_placeholder: "z. B. key, google, github"
+ icon_help: "Lucide-Icon-Name (optional)"
+ enabled_label: "Diesen Provider aktivieren"
+ enabled_help: "Benutzer können sich bei Aktivierung mit diesem Provider anmelden"
+ issuer_label: "Issuer"
+ issuer_placeholder: "https://accounts.google.com"
+ issuer_help: "OIDC-Issuer-URL (validiert .well-known/openid-configuration)"
+ client_id_label: "Client ID"
+ client_id_placeholder: "your-client-id"
+ client_id_help: "OAuth-Client-ID Ihres Identitätsanbieters"
+ client_secret_label: "Client Secret"
+ client_secret_placeholder_new: "your-client-secret"
+ client_secret_placeholder_existing: "••••••••••••••••"
+ client_secret_help: "OAuth-Client-Secret (verschlüsselt in der Datenbank)"
+ client_secret_help_existing: " – leer lassen, um bestehendes beizubehalten"
+ redirect_uri_label: "Redirect URI"
+ redirect_uri_placeholder: "https://yourdomain.com/auth/openid_connect/callback"
+ redirect_uri_help: "Callback-URL, die beim Identitätsanbieter eingetragen werden muss"
+ copy_button: "Kopieren"
+ cancel: "Abbrechen"
+ submit: "Provider speichern"
+ errors_title: "%{count} Fehler verhinderte das Speichern dieses Providers:"
+ provisioning_title: "Benutzer-Bereitstellung"
+ default_role_label: "Standardrolle für neue Benutzer"
+ default_role_help: "Rolle für per JIT-SSO bereitgestellte Benutzer. Standard: Mitglied."
+ role_guest: "Gast"
+ role_member: "Mitglied"
+ role_admin: "Admin"
+ role_super_admin: "Super-Admin"
+ role_mapping_title: "Gruppen-Rollen-Zuordnung (optional)"
+ role_mapping_help: "IdP-Gruppen/Claims auf Anwendungsrollen abbilden. Höchste passende Rolle wird zugewiesen. Leer = Standardrolle oben."
+ super_admin_groups: "Super-Admin-Gruppen"
+ admin_groups: "Admin-Gruppen"
+ guest_groups: "Gast-Gruppen"
+ member_groups: "Mitglieder-Gruppen"
+ groups_help: "Kommagetrennte IdP-Gruppennamen. * = alle Gruppen."
+ advanced_title: "Erweiterte OIDC-Einstellungen"
+ scopes_label: "Benutzerdefinierte Scopes"
+ scopes_help: "Leerzeichengetrennte OIDC-Scopes. Leer = Standard (openid email profile). 'groups' für Gruppen-Claims."
+ prompt_label: "Authentifizierungs-Prompt"
+ prompt_default: "Standard (IdP entscheidet)"
+ prompt_login: "Login erzwingen (erneut anmelden)"
+ prompt_consent: "Zustimmung erzwingen (erneut autorisieren)"
+ prompt_select_account: "Kontoauswahl"
+ prompt_none: "Kein Prompt (stille Anmeldung)"
+ prompt_help: "Steuert, wie der IdP den Benutzer während der Anmeldung auffordert."
+ test_connection: "Verbindung testen"
+ saml_configuration: "SAML-Konfiguration"
+ idp_metadata_url: "IdP-Metadaten-URL"
+ idp_metadata_url_help: "URL zu den SAML-Metadaten Ihres IdP. Andere SAML-Einstellungen werden dann automatisch gesetzt."
+ manual_saml_config: "Manuelle Konfiguration (ohne Metadaten-URL)"
+ manual_saml_help: "Nur verwenden, wenn Ihr IdP keine Metadaten-URL bereitstellt."
+ idp_sso_url: "IdP-SSO-URL"
+ idp_slo_url: "IdP-SLO-URL (optional)"
+ idp_certificate: "IdP-Zertifikat"
+ idp_certificate_help: "X.509-Zertifikat im PEM-Format. Erforderlich ohne Metadaten-URL."
+ idp_cert_fingerprint: "Zertifikats-Fingerabdruck (Alternative)"
+ name_id_format: "NameID-Format"
+ name_id_email: "E-Mail-Adresse (Standard)"
+ name_id_persistent: "Persistent"
+ name_id_transient: "Transient"
+ name_id_unspecified: "Nicht angegeben"
diff --git a/config/locales/views/admin/sso_providers/es.yml b/config/locales/views/admin/sso_providers/es.yml
new file mode 100644
index 000000000..ab9567955
--- /dev/null
+++ b/config/locales/views/admin/sso_providers/es.yml
@@ -0,0 +1,115 @@
+---
+es:
+ admin:
+ unauthorized: "No tienes autorización para acceder a esta área."
+ sso_providers:
+ index:
+ title: "Proveedores de SSO"
+ description: "Gestiona los proveedores de autenticación de inicio de sesión único para tu instancia"
+ add_provider: "Añadir proveedor"
+ no_providers_title: "No hay proveedores de SSO"
+ no_providers_message: "Empieza añadiendo tu primer proveedor de SSO."
+ note: "Los cambios en los proveedores de SSO requieren un reinicio del servidor para surtir efecto. Alternativamente, activa la función AUTH_PROVIDERS_SOURCE=db para cargar los proveedores desde la base de datos de forma dinámica."
+ table:
+ name: "Nombre"
+ strategy: "Estrategia"
+ status: "Estado"
+ issuer: "Emisor (Issuer)"
+ actions: "Acciones"
+ enabled: "Activado"
+ disabled: "Desactivado"
+ legacy_providers_title: "Proveedores configurados por entorno"
+ legacy_providers_notice: "Estos proveedores se configuran mediante variables de entorno o YAML y no pueden gestionarse a través de esta interfaz. Para gestionarlos aquí, mígralos a proveedores respaldados por la base de datos activando AUTH_PROVIDERS_SOURCE=db y recreándolos en la interfaz de usuario."
+ env_configured: "Env/YAML"
+ new:
+ title: "Añadir proveedor de SSO"
+ description: "Configura un nuevo proveedor de autenticación de inicio de sesión único"
+ edit:
+ title: "Editar proveedor de SSO"
+ description: "Actualizar configuración para %{label}"
+ create:
+ success: "El proveedor de SSO se ha creado correctamente."
+ update:
+ success: "El proveedor de SSO se ha actualizado correctamente."
+ destroy:
+ success: "El proveedor de SSO se ha eliminado correctamente."
+ confirm: "¿Estás seguro de que quieres eliminar este proveedor? Esta acción no se puede deshacer."
+ toggle:
+ success_enabled: "El proveedor de SSO se ha activado correctamente."
+ success_disabled: "El proveedor de SSO se ha desactivado correctamente."
+ confirm_enable: "¿Estás seguro de que quieres activar este proveedor?"
+ confirm_disable: "¿Estás seguro de que quieres desactivar este proveedor?"
+ form:
+ basic_information: "Información básica"
+ oauth_configuration: "Configuración OAuth/OIDC"
+ strategy_label: "Estrategia"
+ strategy_help: "La estrategia de autenticación a utilizar"
+ name_label: "Nombre"
+ name_placeholder: "ej. openid_connect, keycloak, authentik"
+ name_help: "Identificador único (solo minúsculas, números y guiones bajos)"
+ label_label: "Etiqueta"
+ label_placeholder: "ej. Iniciar sesión con Keycloak"
+ label_help: "Texto del botón mostrado a los usuarios"
+ icon_label: "Icono"
+ icon_placeholder: "ej. key, google, github"
+ icon_help: "Nombre del icono de Lucide (opcional)"
+ enabled_label: "Activar este proveedor"
+ enabled_help: "Los usuarios pueden iniciar sesión con este proveedor cuando está activado"
+ issuer_label: "Emisor (Issuer)"
+ issuer_placeholder: "https://accounts.google.com"
+ issuer_help: "URL del emisor OIDC (validará el punto de conexión .well-known/openid-configuration)"
+ client_id_label: "ID de cliente (Client ID)"
+ client_id_placeholder: "tu-id-de-cliente"
+ client_id_help: "ID de cliente OAuth de tu proveedor de identidad"
+ client_secret_label: "Secreto de cliente (Client Secret)"
+ client_secret_placeholder_new: "tu-secreto-de-cliente"
+ client_secret_placeholder_existing: "••••••••••••••••"
+ client_secret_help: "Secreto de cliente OAuth (encriptado en la base de datos)"
+ client_secret_help_existing: " - dejar en blanco para mantener el actual"
+ redirect_uri_label: "URI de redirección"
+ redirect_uri_placeholder: "https://tudominio.com/auth/openid_connect/callback"
+ redirect_uri_help: "URL de retorno para configurar en tu proveedor de identidad"
+ copy_button: "Copiar"
+ cancel: "Cancelar"
+ submit: "Guardar proveedor"
+ errors_title: "%{count} error impidió que se guardara este proveedor:"
+ provisioning_title: "Aprovisionamiento de usuarios"
+ default_role_label: "Rol predeterminado para nuevos usuarios"
+ default_role_help: "Rol asignado a los usuarios creados mediante el aprovisionamiento de cuentas SSO Just-In-Time (JIT). Por defecto es Miembro."
+ role_guest: "Invitado"
+ role_member: "Miembro"
+ role_admin: "Administrador"
+ role_super_admin: "Superadministrador"
+ role_mapping_title: "Mapeo de Grupos a Roles (Opcional)"
+ role_mapping_help: "Mapea grupos/notificaciones del IdP a roles de la aplicación. A los usuarios se les asigna el rol más alto coincidente. Deja en blanco para usar el rol predeterminado de arriba."
+ super_admin_groups: "Grupos de Superadministrador"
+ admin_groups: "Grupos de Administrador"
+ guest_groups: "Grupos de Invitado"
+ member_groups: "Grupos de Miembro"
+ groups_help: "Lista de nombres de grupos del IdP separados por comas. Usa * para coincidir con todos los grupos."
+ advanced_title: "Ajustes avanzados de OIDC"
+ scopes_label: "Ámbitos (Scopes) personalizados"
+ scopes_help: "Lista de ámbitos OIDC separados por espacios. Deja en blanco para los predeterminados (openid email profile). Añade 'groups' para recuperar las notificaciones de grupo."
+ prompt_label: "Solicitud de autenticación (Prompt)"
+ prompt_default: "Predeterminado (el IdP decide)"
+ prompt_login: "Forzar inicio de sesión (reautenticar)"
+ prompt_consent: "Forzar consentimiento (reautorizar)"
+ prompt_select_account: "Selección de cuenta (elegir cuenta)"
+ prompt_none: "Sin solicitud (autenticación silenciosa)"
+ prompt_help: "Controla cómo el IdP solicita información al usuario durante la autenticación."
+ test_connection: "Probar conexión"
+ saml_configuration: "Configuración SAML"
+ idp_metadata_url: "URL de metadatos del IdP"
+ idp_metadata_url_help: "URL a los metadatos SAML de tu IdP. Si se proporciona, otros ajustes de SAML se configurarán automáticamente."
+ manual_saml_config: "Configuración manual (si no se usa URL de metadatos)"
+ manual_saml_help: "Usa estos ajustes solo si tu IdP no proporciona una URL de metadatos."
+ idp_sso_url: "URL de SSO del IdP"
+ idp_slo_url: "URL de SLO del IdP (opcional)"
+ idp_certificate: "Certificado del IdP"
+ idp_certificate_help: "Certificado X.509 en formato PEM. Obligatorio si no se usa URL de metadatos."
+ idp_cert_fingerprint: "Huella digital del certificado (alternativa)"
+ name_id_format: "Formato de NameID"
+ name_id_email: "Dirección de correo (predeterminado)"
+ name_id_persistent: "Persistente"
+ name_id_transient: "Transitorio"
+ name_id_unspecified: "Sin especificar"
\ No newline at end of file
diff --git a/config/locales/views/admin/sso_providers/pl.yml b/config/locales/views/admin/sso_providers/pl.yml
new file mode 100644
index 000000000..65bb18051
--- /dev/null
+++ b/config/locales/views/admin/sso_providers/pl.yml
@@ -0,0 +1,115 @@
+---
+pl:
+ admin:
+ unauthorized: Nie masz uprawnień do dostępu do tego obszaru.
+ sso_providers:
+ index:
+ title: Dostawcy SSO
+ description: Zarządzaj dostawcami uwierzytelniania jednokrotnego logowania dla swojej instancji
+ add_provider: Dodaj dostawcę
+ no_providers_title: Brak dostawców SSO
+ no_providers_message: Zacznij od dodania pierwszego dostawcy SSO.
+ note: Zmiany dostawców SSO wymagają ponownego uruchomienia serwera, aby weszły w życie. Alternatywnie włącz flagę AUTH_PROVIDERS_SOURCE=db, aby dynamicznie ładować dostawców z bazy danych.
+ table:
+ name: Nazwa
+ strategy: Strategia
+ status: Status
+ issuer: Wystawca
+ actions: Akcje
+ enabled: Włączony
+ disabled: Wyłączony
+ legacy_providers_title: Dostawcy skonfigurowani przez zmienne środowiskowe
+ legacy_providers_notice: Ci dostawcy są skonfigurowani przez zmienne środowiskowe lub YAML i nie można nimi zarządzać przez ten interfejs. Aby zarządzać nimi tutaj, przenieś ich do dostawców opartych na bazie danych, włączając AUTH_PROVIDERS_SOURCE=db i odtwarzając ich w interfejsie.
+ env_configured: Środowisko/YAML
+ new:
+ title: Dodaj dostawcę SSO
+ description: Skonfiguruj nowego dostawcę uwierzytelniania jednokrotnego logowania
+ edit:
+ title: Edytuj dostawcę SSO
+ description: Zaktualizuj konfigurację dla %{label}
+ create:
+ success: Dostawca SSO został pomyślnie utworzony.
+ update:
+ success: Dostawca SSO został pomyślnie zaktualizowany.
+ destroy:
+ success: Dostawca SSO został pomyślnie usunięty.
+ confirm: Czy na pewno chcesz usunąć tego dostawcę? Tej akcji nie można cofnąć.
+ toggle:
+ success_enabled: Dostawca SSO został pomyślnie włączony.
+ success_disabled: Dostawca SSO został pomyślnie wyłączony.
+ confirm_enable: Czy na pewno chcesz włączyć tego dostawcę?
+ confirm_disable: Czy na pewno chcesz wyłączyć tego dostawcę?
+ form:
+ basic_information: Podstawowe informacje
+ oauth_configuration: Konfiguracja OAuth/OIDC
+ strategy_label: Strategia
+ strategy_help: Strategia uwierzytelniania do użycia
+ name_label: Nazwa
+ name_placeholder: np. openid_connect, keycloak, authentik
+ name_help: Unikalny identyfikator (tylko małe litery, cyfry i podkreślenia)
+ label_label: Etykieta
+ label_placeholder: np. Zaloguj przez Keycloak
+ label_help: Tekst przycisku widoczny dla użytkowników
+ icon_label: Ikona
+ icon_placeholder: np. key, google, github
+ icon_help: Nazwa ikony Lucide (opcjonalne)
+ enabled_label: Włącz tego dostawcę
+ enabled_help: Użytkownicy mogą logować się przez tego dostawcę, gdy jest włączony
+ issuer_label: Wystawca
+ issuer_placeholder: https://accounts.google.com
+ issuer_help: URL wystawcy OIDC (waliduje endpoint .well-known/openid-configuration)
+ client_id_label: ID klienta
+ client_id_placeholder: twoj-client-id
+ client_id_help: ID klienta OAuth od Twojego dostawcy tożsamości
+ client_secret_label: Sekret klienta
+ client_secret_placeholder_new: twoj-client-secret
+ client_secret_placeholder_existing: "zapisany-sekret"
+ client_secret_help: Sekret klienta OAuth (zaszyfrowany w bazie danych)
+ client_secret_help_existing: " - pozostaw puste, aby zachować istniejący"
+ redirect_uri_label: URI przekierowania
+ redirect_uri_placeholder: https://yourdomain.com/auth/openid_connect/callback
+ redirect_uri_help: URL zwrotny do skonfigurowania u Twojego dostawcy tożsamości
+ copy_button: Kopiuj
+ cancel: Anuluj
+ submit: Zapisz dostawcę
+ errors_title: "%{count} błąd uniemożliwia zapisanie tego dostawcy:"
+ provisioning_title: Udostępnianie użytkowników
+ default_role_label: Domyślna rola dla nowych użytkowników
+ default_role_help: "Rola przypisywana użytkownikom tworzonymi przez SSO just-in-time (JIT). Domyślnie: Członek."
+ role_guest: Gość
+ role_member: Członek
+ role_admin: Administrator
+ role_super_admin: Superadministrator
+ role_mapping_title: Mapowanie grup na role (opcjonalne)
+ role_mapping_help: Mapuj grupy/claims IdP na role aplikacji. Użytkownicy otrzymują najwyższą pasującą rolę. Pozostaw puste, aby używać domyślnej roli powyżej.
+ super_admin_groups: Grupy Super Admina
+ admin_groups: Grupy Admina
+ guest_groups: Grupy Gościa
+ member_groups: Grupy Członka
+ groups_help: Lista nazw grup IdP oddzielona przecinkami. Użyj *, aby pasować do wszystkich grup.
+ advanced_title: Zaawansowane ustawienia OIDC
+ scopes_label: Własne zakresy
+ scopes_help: Lista zakresów OIDC oddzielona spacjami. Pozostaw puste dla wartości domyślnych (openid email profile). Dodaj 'groups', aby pobrać claims grup.
+ prompt_label: Monit uwierzytelniania
+ prompt_default: Domyślny (decyduje IdP)
+ prompt_login: Wymuś logowanie (ponowne uwierzytelnienie)
+ prompt_consent: Wymuś zgodę (ponowna autoryzacja)
+ prompt_select_account: Wybór konta (wybierz konto)
+ prompt_none: Bez monitu (uwierzytelnienie ciche)
+ prompt_help: Określa, w jaki sposób IdP wyświetla monity użytkownikowi podczas uwierzytelniania.
+ test_connection: Testuj połączenie
+ saml_configuration: Konfiguracja SAML
+ idp_metadata_url: URL metadanych IdP
+ idp_metadata_url_help: URL do metadanych SAML Twojego IdP. Jeśli podany, inne ustawienia SAML zostaną skonfigurowane automatycznie.
+ manual_saml_config: Konfiguracja ręczna (jeśli nie używasz adresu URL metadanych)
+ manual_saml_help: Używaj tych ustawień tylko wtedy, gdy Twój IdP nie udostępnia adresu URL metadanych.
+ idp_sso_url: URL SSO IdP
+ idp_slo_url: IdP SLO URL (opcjonalne)
+ idp_certificate: Certyfikat IdP
+ idp_certificate_help: Certyfikat X.509 w formacie PEM. Wymagany, jeśli nie używasz adresu URL metadanych.
+ idp_cert_fingerprint: Odcisk cyfrowy certyfikatu (alternatywa)
+ name_id_format: Format identyfikatora nazwy
+ name_id_email: Adres e-mail (domyślny)
+ name_id_persistent: Trwały
+ name_id_transient: Przejściowy
+ name_id_unspecified: Nieokreślony
diff --git a/config/locales/views/admin/users/de.yml b/config/locales/views/admin/users/de.yml
new file mode 100644
index 000000000..2eda1c6b9
--- /dev/null
+++ b/config/locales/views/admin/users/de.yml
@@ -0,0 +1,45 @@
+---
+de:
+ admin:
+ users:
+ index:
+ title: "Benutzerverwaltung"
+ description: "Benutzerrollen für Ihre Instanz verwalten. Super-Admins haben Zugriff auf SSO-Provider und Benutzerverwaltung."
+ section_title: "Benutzer"
+ you: "(Sie)"
+ trial_ends_at: "Testversion endet"
+ not_available: "k. A."
+ no_users: "Keine Benutzer gefunden."
+ filters:
+ role: "Rolle"
+ role_all: "Alle Rollen"
+ trial_status: "Teststatus"
+ trial_all: "Alle"
+ trial_expiring_soon: "Läuft in 7 Tagen ab"
+ trial_trialing: "In Testphase"
+ submit: "Filtern"
+ summary:
+ trials_expiring_7_days: "Testversionen laufen in den nächsten 7 Tagen ab"
+ table:
+ user: "Benutzer"
+ trial_ends_at: "Testversion endet"
+ family_accounts: "Familienkonten"
+ family_transactions: "Familienbuchungen"
+ last_login: "Letzte Anmeldung"
+ session_count: "Anzahl Sitzungen"
+ never: "Nie"
+ role: "Rolle"
+ role_descriptions_title: "Rollenbeschreibungen"
+ roles:
+ guest: "Gast"
+ member: "Mitglied"
+ admin: "Admin"
+ super_admin: "Super-Admin"
+ role_descriptions:
+ guest: "Assistenten-orientierte Nutzung mit eingeschränkten Rechten für Einführungsabläufe."
+ member: "Standard-Zugriff. Kann eigene Konten, Buchungen und Einstellungen verwalten."
+ admin: "Familien-Administrator. Zugriff auf erweiterte Einstellungen wie API-Schlüssel, Importe und KI-Prompts."
+ super_admin: "Instanz-Administrator. Kann SSO-Provider, Benutzerrollen verwalten und Benutzer für Support vertreten."
+ update:
+ success: "Benutzerrolle wurde erfolgreich aktualisiert."
+ failure: "Benutzerrolle konnte nicht aktualisiert werden."
diff --git a/config/locales/views/admin/users/en.yml b/config/locales/views/admin/users/en.yml
index 7eb7102a7..c14a243d5 100644
--- a/config/locales/views/admin/users/en.yml
+++ b/config/locales/views/admin/users/en.yml
@@ -5,11 +5,14 @@ en:
index:
title: "User Management"
description: "Manage user roles for your instance. Super admins can access SSO provider settings and user management."
- section_title: "Users"
+ section_title: "Families / Groups"
you: "(You)"
trial_ends_at: "Trial ends"
not_available: "n/a"
no_users: "No users found."
+ unnamed_family: "Unnamed Family/Group"
+ no_subscription: "No subscription"
+ family_summary: "%{members} members · %{accounts} accounts · %{transactions} transactions"
filters:
role: "Role"
role_all: "All roles"
@@ -40,6 +43,11 @@ en:
member: "Basic user access. Can manage their own accounts, transactions, and settings."
admin: "Family administrator. Can access advanced settings like API keys, imports, and AI prompts."
super_admin: "Instance administrator. Can manage SSO providers, user roles, and impersonate users for support."
+ invitations:
+ pending_label: "Invited (pending)"
+ expires: "Expires %{date}"
+ delete: "Delete"
+ delete_all: "Delete All"
update:
success: "User role updated successfully."
failure: "Failed to update user role."
diff --git a/config/locales/views/admin/users/es.yml b/config/locales/views/admin/users/es.yml
new file mode 100644
index 000000000..d2cad061f
--- /dev/null
+++ b/config/locales/views/admin/users/es.yml
@@ -0,0 +1,45 @@
+---
+es:
+ admin:
+ users:
+ index:
+ title: "Gestión de usuarios"
+ description: "Gestiona los roles de usuario para tu instancia. Los superadministradores pueden acceder a la configuración del proveedor de SSO y a la gestión de usuarios."
+ section_title: "Usuarios"
+ you: "(Tú)"
+ trial_ends_at: "La prueba finaliza"
+ not_available: "n/a"
+ no_users: "No se han encontrado usuarios."
+ filters:
+ role: "Rol"
+ role_all: "Todos los roles"
+ trial_status: "Estado de la prueba"
+ trial_all: "Todos"
+ trial_expiring_soon: "Caduca en 7 días"
+ trial_trialing: "En periodo de prueba"
+ submit: "Filtrar"
+ summary:
+ trials_expiring_7_days: "Pruebas que caducan en los próximos 7 días"
+ table:
+ user: "Usuario"
+ trial_ends_at: "La prueba finaliza"
+ family_accounts: "Cuentas familiares"
+ family_transactions: "Transacciones familiares"
+ last_login: "Último inicio de sesión"
+ session_count: "Número de sesiones"
+ never: "Nunca"
+ role: "Rol"
+ role_descriptions_title: "Descripción de los roles"
+ roles:
+ guest: "Invitado"
+ member: "Miembro"
+ admin: "Administrador"
+ super_admin: "Superadministrador"
+ role_descriptions:
+ guest: "Experiencia centrada en el asistente con permisos restringidos intencionadamente para flujos de introducción."
+ member: "Acceso de usuario básico. Pueden gestionar sus propias cuentas, transacciones y ajustes."
+ admin: "Administrador de la familia. Puede acceder a ajustes avanzados como claves API, importaciones e instrucciones de IA."
+ super_admin: "Administrador de la instancia. Puede gestionar proveedores de SSO, roles de usuario y suplantar a usuarios para soporte."
+ update:
+ success: "Rol de usuario actualizado correctamente."
+ failure: "Error al actualizar el rol de usuario."
\ No newline at end of file
diff --git a/config/locales/views/admin/users/pl.yml b/config/locales/views/admin/users/pl.yml
new file mode 100644
index 000000000..f618ebcc3
--- /dev/null
+++ b/config/locales/views/admin/users/pl.yml
@@ -0,0 +1,53 @@
+---
+pl:
+ admin:
+ users:
+ index:
+ title: Zarządzanie użytkownikami
+ description: Zarządzaj rolami użytkowników w swojej instancji. Superadministratorzy mają dostęp do ustawień dostawców SSO i zarządzania użytkownikami.
+ section_title: Rodziny / Grupy
+ you: "(Ty)"
+ trial_ends_at: Koniec okresu próbnego
+ not_available: brak danych
+ no_users: Nie znaleziono użytkowników.
+ unnamed_family: Nienazwana rodzina/grupa
+ no_subscription: Brak subskrypcji
+ family_summary: "%{members} członków · %{accounts} kont · %{transactions} transakcji"
+ filters:
+ role: Rola
+ role_all: Wszystkie role
+ trial_status: Status okresu próbnego
+ trial_all: Wszystkie
+ trial_expiring_soon: Wygasa za 7 dni
+ trial_trialing: W okresie próbnym
+ submit: Filtruj
+ summary:
+ trials_expiring_7_days: Okresy próbne wygasające w ciągu 7 dni
+ table:
+ user: Użytkownik
+ trial_ends_at: Koniec okresu próbnego
+ family_accounts: Konta rodziny
+ family_transactions: Transakcje rodziny
+ last_login: Ostatnie logowanie
+ session_count: Liczba sesji
+ never: Nigdy
+ role: Rola
+ role_descriptions_title: Opisy ról
+ roles:
+ guest: Gość
+ member: Członek
+ admin: Administrator
+ super_admin: Superadministrator
+ role_descriptions:
+ guest: Tryb skupiony na Asystencie z celowo ograniczonymi uprawnieniami dla przepływów wprowadzających.
+ member: Podstawowy dostęp użytkownika. Może zarządzać własnymi kontami, transakcjami i ustawieniami.
+ admin: Administrator rodziny. Ma dostęp do ustawień zaawansowanych, takich jak klucze API, importy i prompty AI.
+ super_admin: Administrator instancji. Może zarządzać dostawcami SSO, rolami użytkowników i wcielać się w użytkowników na potrzeby wsparcia.
+ invitations:
+ pending_label: Zaproszony (oczekuje)
+ expires: Wygasa %{date}
+ delete: Usuń
+ delete_all: Usuń wszystkie
+ update:
+ success: Rola użytkownika została pomyślnie zaktualizowana.
+ failure: Nie udało się zaktualizować roli użytkownika.
diff --git a/config/locales/views/admin/users/pt-BR.yml b/config/locales/views/admin/users/pt-BR.yml
new file mode 100644
index 000000000..591a60b6f
--- /dev/null
+++ b/config/locales/views/admin/users/pt-BR.yml
@@ -0,0 +1,53 @@
+---
+pt-BR:
+ admin:
+ users:
+ index:
+ title: "Gestão de Usuários"
+ description: "Gerencie as funções de usuário da sua instância. Os superadministradores podem acessar as configurações do provedor SSO e o gerenciamento de usuários."
+ section_title: "Famílias / Grupos"
+ you: "(Você)"
+ trial_ends_at: "Fim do teste"
+ not_available: "n/a"
+ no_users: "Nenhum usuário encontrado."
+ unnamed_family: "Família/Grupo sem nome"
+ no_subscription: "Sem assinatura"
+ family_summary: "%{members} membros · %{accounts} contas · %{transactions} transações"
+ filters:
+ role: "Cargo"
+ role_all: "Todas as funções"
+ trial_status: "Status do teste"
+ trial_all: "Todos"
+ trial_expiring_soon: "Expira em 7 dias"
+ trial_trialing: "Em teste"
+ submit: "Filtro"
+ summary:
+ trials_expiring_7_days: "Testes expiram nos próximos 7 dias"
+ table:
+ user: "Usuário"
+ trial_ends_at: "Fim do teste"
+ family_accounts: "Contas familiares"
+ family_transactions: "Transações familiares"
+ last_login: "Último login"
+ session_count: "Contagem de sessões"
+ never: "Nunca"
+ role: "Cargo"
+ role_descriptions_title: "Descrições de Cargos"
+ roles:
+ guest: "Convidado"
+ member: "Membro"
+ admin: "Admin"
+ super_admin: "Super Admin"
+ role_descriptions:
+ guest: "Experiência com foco no assistente, com permissões intencionalmente restritas para fluxos de trabalho introdutórios."
+ member: "Acesso básico de usuário. Pode gerenciar suas próprias contas, transações e configurações."
+ admin: "Administrador familiar. Pode acessar configurações avançadas como chaves de API, importações e avisos de IA."
+ super_admin: "Administrador de instância. Pode gerenciar provedores de SSO, funções de usuário e representar usuários para suporte."
+ invitations:
+ pending_label: "Convidado (pendente)"
+ expires: "Expira em %{date}"
+ delete: "Excluir"
+ delete_all: "Excluir todos"
+ update:
+ success: "Função de usuário atualizada com sucesso."
+ failure: "Falha ao atualizar a função do usuário."
diff --git a/config/locales/views/application/pl.yml b/config/locales/views/application/pl.yml
new file mode 100644
index 000000000..80b45673a
--- /dev/null
+++ b/config/locales/views/application/pl.yml
@@ -0,0 +1,10 @@
+---
+pl:
+ number:
+ currency:
+ format:
+ delimiter: " "
+ format: "%n %u"
+ precision: 2
+ separator: ","
+ unit: "zł"
diff --git a/config/locales/views/binance_items/en.yml b/config/locales/views/binance_items/en.yml
new file mode 100644
index 000000000..ae3bd298a
--- /dev/null
+++ b/config/locales/views/binance_items/en.yml
@@ -0,0 +1,75 @@
+---
+en:
+ binance_items:
+ create:
+ default_name: Binance
+ success: Successfully connected to Binance! Your account is being synced.
+ update:
+ success: Successfully updated Binance configuration.
+ destroy:
+ success: Scheduled Binance connection for deletion.
+ setup_accounts:
+ title: Import Binance Account
+ subtitle: Select which portfolios to track
+ instructions: Select the Binance portfolios you want to import. Only portfolios with balances are shown.
+ no_accounts: All accounts have been imported.
+ accounts_count:
+ one: "%{count} account available"
+ other: "%{count} accounts available"
+ select_all: Select all
+ import_selected: Import Selected
+ cancel: Cancel
+ creating: Importing...
+ complete_account_setup:
+ success:
+ one: "Imported %{count} account"
+ other: "Imported %{count} accounts"
+ none_selected: No accounts selected
+ no_accounts: No accounts to import
+ binance_item:
+ provider_name: Binance
+ syncing: Syncing...
+ reconnect: Credentials need updating
+ deletion_in_progress: Deleting...
+ sync_status:
+ no_accounts: No accounts found
+ all_synced:
+ one: "%{count} account synced"
+ other: "%{count} accounts synced"
+ partial_sync: "%{linked_count} synced, %{unlinked_count} need setup"
+ status: "Last synced %{timestamp} ago"
+ status_with_summary: "Last synced %{timestamp} ago - %{summary}"
+ status_never: Never synced
+ update_credentials: Update credentials
+ delete: Delete
+ no_accounts_title: No accounts found
+ no_accounts_message: Your Binance portfolio will appear here after syncing.
+ setup_needed: Account ready to import
+ setup_description: Select which Binance portfolios you want to track.
+ setup_action: Import Account
+ import_accounts_menu: Import Account
+ stale_rate_warning: "Balance is approximate — the exact exchange rate for %{date} was unavailable. Will update on next sync."
+ select_existing_account:
+ title: Link Binance Account
+ no_accounts_found: No Binance accounts found.
+ wait_for_sync: Wait for Binance to finish syncing
+ check_provider_health: Check that your Binance API credentials are valid
+ currently_linked_to: "Currently linked to: %{account_name}"
+ link: Link
+ cancel: Cancel
+ link_existing_account:
+ success: Successfully linked to Binance account
+ errors:
+ only_manual: Only manual accounts can be linked to Binance
+ invalid_binance_account: Invalid Binance account
+ binance_item:
+ syncer:
+ checking_credentials: Checking credentials...
+ credentials_invalid: Invalid API credentials. Please check your API key and secret.
+ importing_accounts: Importing accounts from Binance...
+ checking_configuration: Checking account configuration...
+ accounts_need_setup:
+ one: "%{count} account needs setup"
+ other: "%{count} accounts need setup"
+ processing_accounts: Processing account data...
+ calculating_balances: Calculating balances...
diff --git a/config/locales/views/bonds/pl.yml b/config/locales/views/bonds/pl.yml
new file mode 100644
index 000000000..c4af32682
--- /dev/null
+++ b/config/locales/views/bonds/pl.yml
@@ -0,0 +1,153 @@
+---
+pl:
+ bonds:
+ edit:
+ edit: Edytuj %{account}
+ form:
+ initial_balance: Początkowe saldo obligacji
+ tax_wrapper: Otoczka podatkowa
+ auto_buy_new_issues: Automatycznie kupuj nowe emisje obligacji
+ auto_buy_new_issues_hint: Po rozliczeniu zapadalności automatycznie kup kolejną emisję z dostępnych środków dla kont IKE/IKZE.
+ subtypes:
+ eod:
+ short: EOD
+ long: 10-letnia obligacja oszczędnościowa Skarbu Państwa
+ rod:
+ short: ROD
+ long: 12-letnia rodzinna obligacja oszczędnościowa
+ other_bond:
+ short: Inna
+ long: Inna obligacja
+ new:
+ title: Wprowadź dane obligacji
+ tabs:
+ positions:
+ positions: Pozycje obligacji
+ new_activity: Nowa aktywność
+ name: Nazwa
+ weight: Udział
+ rate: Oprocentowanie
+ holdings: Stan
+ maturity: Zapadalność
+ total_return: Łączny zwrot
+ no_purchases: Nie dodano jeszcze żadnych zakupów.
+ closed:
+ closed: Zamknięte partie obligacji
+ name: Nazwa
+ weight: Udział
+ rate: Oprocentowanie
+ holdings: Stan
+ maturity: Zapadalność
+ total_return: Łączny zwrot
+ no_closed_lots: Brak zamkniętych partii obligacji.
+ closed_lots: Zamknięte partie obligacji
+ closed_lot_meta: "Zakupiono %{purchased}, zamknięto %{closed}"
+ settled_net: Rozliczono netto
+ cash_holding:
+ cash_position: Gotówka z obligacji
+ purchase_holding:
+ unknown: Nieznane
+ update_needed: Wymagana aktualizacja
+ purchased: "Zakupiono %{date}"
+ term_months:
+ one: "%{count} miesiąc"
+ few: "%{count} miesiące"
+ many: "%{count} miesięcy"
+ other: "%{count} miesiąca"
+ principal_term: "Kapitał, %{term}"
+ bond_meta: "%{rate_type} / %{coupon}"
+ inflation_meta_gus: "%{inflation} inflacji + %{margin} marży (GUS SDP %{indicator})"
+ inflation_meta_manual: "%{inflation} inflacji + %{margin} marży (ręcznie)"
+ first_period_fixed_rate: Stałe oprocentowanie pierwszego okresu
+ maturity: "Zapada %{date}"
+ maturity_label: Data zapadalności
+ since_purchase: Od zakupu
+ projected_to_maturity: Prognoza do zapadalności
+ pending_review: Oczekiwanie na zaktualizowane stawki emisji
+ edit: Edytuj
+ remove: Usuń
+ confirm_remove: Usunąć ten zakup?
+
+ bond_lots:
+ not_bond_account: "To konto nie jest kontem obligacji."
+ new:
+ title: Dodaj zakup dla %{account}
+ edit:
+ title: Edytuj zakup dla %{account}
+ show:
+ settings: Ustawienia
+ history: Historia
+ no_history: Brak historii kapitalizacji.
+ overview_principal: Kapitał
+ overview_settlement: Kwota rozliczenia
+ overview_maturity: Data zapadalności
+ overview_closed_on: Zamknięto
+ history_period: "Okres %{period}: %{start} - %{end}"
+ history_balance: "Saldo %{opening} -> %{closing}"
+ history_interest: "Odsetki %{interest} przy %{rate}"
+ history_capitalized: Skapitalizowano
+ history_partial: Okres częściowy
+ history_inflation_gus: "Użyta inflacja: %{inflation} + %{margin} marży (GUS SDP %{indicator}, odn. %{reference})"
+ history_inflation_manual: "Użyta inflacja: %{inflation} + %{margin} marży (założenie ręczne)"
+ history_inflation_first_period: "Stała stopa pierwszego okresu"
+ unknown: Nieznane
+ purchased: "Zakupiono %{date}"
+ delete_title: Usuń zakup
+ delete_subtitle: Usuń ten zakup i powiązany wpis aktywności.
+ delete: Usuń
+ delete_confirm: Usunąć ten zakup?
+ create:
+ success: Dodano zakup obligacji
+ update:
+ success: Zaktualizowano zakup obligacji
+ destroy:
+ success: Usunięto zakup obligacji
+ form:
+ purchased_on: Data zakupu
+ issue_date: Data emisji
+ amount: Kwota kapitału
+ units: Jednostki
+ nominal_per_unit: Nominał na jednostkę
+ term_months: Okres (miesiące)
+ subtype: Typ obligacji
+ rate_type: Typ oprocentowania
+ coupon_frequency: Częstotliwość kuponu
+ interest_rate: Oprocentowanie
+ interest_rate_placeholder: "4.25"
+ first_period_rate: Oprocentowanie pierwszego okresu (%)
+ inflation_margin: Marża inflacyjna (%)
+ auto_fetch_inflation: Pobieraj inflację automatycznie z GUS
+ auto_fetch_disabled_hint: Automatyczny import CPI jest globalnie wyłączony w ustawieniach self-hosting. Wprowadź inflację ręcznie.
+ inflation_rate_assumption: Założenie CPI (%)
+ cpi_lag_months: Opóźnienie CPI (miesiące)
+ early_redemption_fee: Opłata za wcześniejszy wykup
+ auto_close_on_maturity: Automatycznie zamknij w dniu zapadalności
+ auto_close_on_maturity_hint: Automatycznie rozlicz tę partię w dniu zapadalności i przeksięguj środki na gotówkę konta.
+ tax_strategy: Obsługa podatku przy zapadalności
+ tax_rate: Stawka podatku (%)
+ tax_strategies:
+ standard: Podatek standardowy
+ reduced: Podatek obniżony
+ exempt: Zwolnione z podatku (IKE/IKZE)
+ rate_types:
+ fixed: Stałe
+ variable: Zmienne
+ coupon_frequencies:
+ monthly: Miesięcznie
+ quarterly: Kwartalnie
+ semi_annual: Półrocznie
+ annual: Rocznie
+ at_maturity: W terminie zapadalności
+ submit: Dodaj zakup
+ update: Zaktualizuj zakup
+ activity:
+ purchase_name: "Zakup obligacji: %{subtype}"
+ maturity_settlement_name: "Rozliczenie zapadalności obligacji: %{subtype}"
+ maturity_settlement_notes_with_tax: "Kwota zakupu: %{purchase_amount}\nŁączne odsetki: %{interest_amount}\nPotrącony podatek: %{tax_withheld_amount}"
+ maturity_settlement_notes_without_tax: "Kwota zakupu: %{purchase_amount}\nŁączne odsetki: %{interest_amount}\nPotrącony podatek: brak"
+
+ closed_purchase_holding:
+ closed_meta: "Zakupiono %{purchased}, zamknięto %{closed}"
+ closed_rate_meta: "%{periods} okresów kapitalizacji"
+ settled_net: "Rozliczono netto %{net}"
+ history_meta: "Odsetki %{interest}, podatek %{tax}"
diff --git a/config/locales/views/budgets/de.yml b/config/locales/views/budgets/de.yml
new file mode 100644
index 000000000..00ac0252b
--- /dev/null
+++ b/config/locales/views/budgets/de.yml
@@ -0,0 +1,10 @@
+---
+de:
+ budgets:
+ name:
+ custom_range: "%{start} - %{end_date}"
+ month_year: "%{month}"
+ show:
+ tabs:
+ actual: Ist
+ budgeted: Budgetiert
diff --git a/config/locales/views/budgets/en.yml b/config/locales/views/budgets/en.yml
index 6f98a5686..c727dc37c 100644
--- a/config/locales/views/budgets/en.yml
+++ b/config/locales/views/budgets/en.yml
@@ -8,3 +8,12 @@ en:
tabs:
actual: Actual
budgeted: Budgeted
+ copy_previous_prompt:
+ title: "Set up your budget"
+ description: "You can copy your budget from %{source_name} or start fresh."
+ copy_button: "Copy from %{source_name}"
+ fresh_button: "Start fresh"
+ copy_previous:
+ success: "Budget copied from %{source_name}"
+ no_source: "No previous budget found to copy from"
+ already_initialized: "This budget has already been set up"
diff --git a/config/locales/views/budgets/es.yml b/config/locales/views/budgets/es.yml
new file mode 100644
index 000000000..87bc1649b
--- /dev/null
+++ b/config/locales/views/budgets/es.yml
@@ -0,0 +1,10 @@
+---
+es:
+ budgets:
+ name:
+ custom_range: "%{start} - %{end_date}"
+ month_year: "%{month}"
+ show:
+ tabs:
+ actual: Real
+ budgeted: Presupuestado
\ No newline at end of file
diff --git a/config/locales/views/budgets/pl.yml b/config/locales/views/budgets/pl.yml
new file mode 100644
index 000000000..ab3dc9828
--- /dev/null
+++ b/config/locales/views/budgets/pl.yml
@@ -0,0 +1,19 @@
+---
+pl:
+ budgets:
+ name:
+ custom_range: "%{start} do %{end_date}"
+ month_year: "%{month}"
+ show:
+ tabs:
+ actual: Rzeczywiste
+ budgeted: Zaplanowane
+ copy_previous_prompt:
+ title: "Skonfiguruj swój budżet"
+ description: "Możesz skopiować budżet z %{source_name} lub zacząć od zera."
+ copy_button: "Kopiuj z %{source_name}"
+ fresh_button: "Zacznij od zera"
+ copy_previous:
+ success: "Skopiowano budżet z %{source_name}"
+ no_source: "Nie znaleziono poprzedniego budżetu do skopiowania"
+ already_initialized: "Ten budżet został już skonfigurowany"
diff --git a/config/locales/views/budgets/pt-BR.yml b/config/locales/views/budgets/pt-BR.yml
new file mode 100644
index 000000000..d0c4474a1
--- /dev/null
+++ b/config/locales/views/budgets/pt-BR.yml
@@ -0,0 +1,19 @@
+---
+pt-BR:
+ budgets:
+ name:
+ custom_range: "%{start} - %{end_date}"
+ month_year: "%{month}"
+ show:
+ tabs:
+ actual: Atual
+ budgeted: Orçamento
+ copy_previous_prompt:
+ title: "Defina seu orçamento"
+ description: "Você pode copiar seu orçamento de %{source_name} ou começar do zero."
+ copy_button: "Copiar de %{source_name}"
+ fresh_button: "Comece do zero"
+ copy_previous:
+ success: "Orçamento copiado de %{source_name}"
+ no_source: "Não foi encontrado nenhum orçamento anterior para servir de base."
+ already_initialized: "Este orçamento já foi definido."
diff --git a/config/locales/views/categories/pl.yml b/config/locales/views/categories/pl.yml
new file mode 100644
index 000000000..85720c432
--- /dev/null
+++ b/config/locales/views/categories/pl.yml
@@ -0,0 +1,34 @@
+---
+pl:
+ categories:
+ bootstrap:
+ success: Domyślne kategorie zostały pomyślnie utworzone
+ category:
+ delete: Usuń kategorię
+ edit: Edytuj kategorię
+ create:
+ success: Kategoria została pomyślnie utworzona
+ destroy:
+ success: Kategoria została pomyślnie usunięta
+ edit:
+ edit: Edytuj kategorię
+ form:
+ placeholder: Nazwa kategorii
+ index:
+ bootstrap: Użyj domyślnych (zalecane)
+ categories: Kategorie
+ categories_expenses: Kategorie wydatków
+ categories_incomes: Kategorie przychodów
+ empty: Nie znaleziono kategorii
+ new: Nowa kategoria
+ menu:
+ loading: Ładowanie...
+ new:
+ new_category: Nowa kategoria
+ update:
+ success: Kategoria została pomyślnie zaktualizowana
+ category:
+ dropdowns:
+ show:
+ bootstrap: Wygeneruj domyślne kategorie
+ empty: Nie znaleziono kategorii
diff --git a/config/locales/views/category/deletions/pl.yml b/config/locales/views/category/deletions/pl.yml
new file mode 100644
index 000000000..c4561efc3
--- /dev/null
+++ b/config/locales/views/category/deletions/pl.yml
@@ -0,0 +1,13 @@
+---
+pl:
+ category:
+ deletions:
+ create:
+ success: Kategoria transakcji została usunięta
+ new:
+ category: Kategoria
+ delete_and_leave_uncategorized: Usuń "%{category_name}" i pozostaw bez kategorii
+ delete_and_recategorize: Usuń "%{category_name}" i przypisz nową kategorię
+ delete_category: Usunąć kategorię?
+ explanation: Po usunięciu tej kategorii każda transakcja przypisana do "%{category_name}" będzie bez kategorii. Zamiast pozostawiać je bez kategorii, możesz poniżej przypisać nową kategorię.
+ replacement_category_prompt: Wybierz kategorię
diff --git a/config/locales/views/category/dropdowns/pl.yml b/config/locales/views/category/dropdowns/pl.yml
new file mode 100644
index 000000000..74380bb48
--- /dev/null
+++ b/config/locales/views/category/dropdowns/pl.yml
@@ -0,0 +1,11 @@
+---
+pl:
+ category:
+ dropdowns:
+ row:
+ delete: Usuń kategorię
+ edit: Edytuj kategorię
+ show:
+ clear: Wyczyść kategorię
+ no_categories: Nie znaleziono kategorii
+ search_placeholder: Szukaj
diff --git a/config/locales/views/chats/de.yml b/config/locales/views/chats/de.yml
new file mode 100644
index 000000000..2d38fb9aa
--- /dev/null
+++ b/config/locales/views/chats/de.yml
@@ -0,0 +1,5 @@
+---
+de:
+ chats:
+ demo_banner_title: "Demo-Modus aktiv"
+ demo_banner_message: "Sie nutzen ein Open-Weight Qwen3-LLM mit Credits von Cloudflare Workers AI. Die Ergebnisse können variieren, da die Codebasis hauptsächlich mit `gpt-4.1` getestet wurde – Ihre Tokens werden jedoch nicht anderswo zum Training verwendet! 🤖"
diff --git a/config/locales/views/chats/es.yml b/config/locales/views/chats/es.yml
new file mode 100644
index 000000000..d1d8ed83f
--- /dev/null
+++ b/config/locales/views/chats/es.yml
@@ -0,0 +1,5 @@
+---
+es:
+ chats:
+ demo_banner_title: "Modo de demostración activo"
+ demo_banner_message: "Estás utilizando un LLM Qwen3 de pesos abiertos con créditos proporcionados por Cloudflare Workers AI. Los resultados pueden variar, ya que la base de código se probó principalmente con `gpt-4.1`, ¡pero tus tokens no se enviarán a ningún otro lugar para ser entrenados! 🤖"
\ No newline at end of file
diff --git a/config/locales/views/chats/pl.yml b/config/locales/views/chats/pl.yml
new file mode 100644
index 000000000..588542fce
--- /dev/null
+++ b/config/locales/views/chats/pl.yml
@@ -0,0 +1,5 @@
+---
+pl:
+ chats:
+ demo_banner_title: "Aktywny tryb demo"
+ demo_banner_message: "Używasz modelu open-weights Qwen3 LLM z kredytami udostępnionymi przez Cloudflare Workers AI. Wyniki mogą się różnić, ponieważ kod aplikacji był głównie testowany na `gpt-4.1`, ale Twoje tokeny nie są nigdzie dalej wykorzystywane do trenowania! 🤖"
diff --git a/config/locales/views/coinbase_items/de.yml b/config/locales/views/coinbase_items/de.yml
new file mode 100644
index 000000000..a7300e8d8
--- /dev/null
+++ b/config/locales/views/coinbase_items/de.yml
@@ -0,0 +1,78 @@
+---
+de:
+ coinbase_items:
+ create:
+ default_name: Coinbase
+ success: Mit Coinbase verbunden! Ihre Konten werden synchronisiert.
+ update:
+ success: Coinbase-Konfiguration wurde erfolgreich aktualisiert.
+ destroy:
+ success: Coinbase-Verbindung wurde zur Löschung vorgemerkt.
+ setup_accounts:
+ title: Coinbase-Wallets importieren
+ subtitle: Wählen Sie die zu verfolgenden Wallets
+ instructions: Wählen Sie die Wallets zum Import. Nicht ausgewählte Wallets bleiben verfügbar für einen späteren Import.
+ no_accounts: Alle Wallets wurden bereits importiert.
+ accounts_count:
+ one: "%{count} Wallet verfügbar"
+ other: "%{count} Wallets verfügbar"
+ select_all: Alle auswählen
+ import_selected: Ausgewählte importieren
+ cancel: Abbrechen
+ creating: Importiere...
+ complete_account_setup:
+ success:
+ one: "%{count} Wallet importiert"
+ other: "%{count} Wallets importiert"
+ none_selected: Keine Wallets ausgewählt
+ no_accounts: Keine Wallets zum Import
+ coinbase_item:
+ provider_name: Coinbase
+ syncing: Synchronisiere...
+ reconnect: Zugangsdaten müssen aktualisiert werden
+ deletion_in_progress: Wird gelöscht...
+ sync_status:
+ no_accounts: Keine Konten gefunden
+ all_synced:
+ one: "%{count} Konto synchronisiert"
+ other: "%{count} Konten synchronisiert"
+ partial_sync: "%{linked_count} synchronisiert, %{unlinked_count} müssen eingerichtet werden"
+ status: "Zuletzt synchronisiert vor %{timestamp}"
+ status_with_summary: "Zuletzt synchronisiert vor %{timestamp} – %{summary}"
+ status_never: Noch nie synchronisiert
+ update_credentials: Zugangsdaten aktualisieren
+ delete: Löschen
+ no_accounts_title: Keine Konten gefunden
+ no_accounts_message: Ihre Coinbase-Wallets erscheinen hier nach dem Sync.
+ setup_needed: Wallets bereit zum Import
+ setup_description: Wählen Sie die Coinbase-Wallets, die Sie verfolgen möchten.
+ setup_action: Wallets importieren
+ import_wallets_menu: Wallets importieren
+ more_wallets_available:
+ one: "%{count} weiteres Wallet zum Import verfügbar"
+ other: "%{count} weitere Wallets zum Import verfügbar"
+ select_existing_account:
+ title: Coinbase-Konto verknüpfen
+ no_accounts_found: Keine Coinbase-Konten gefunden.
+ wait_for_sync: Warten Sie, bis Coinbase die Synchronisation abgeschlossen hat
+ check_provider_health: Prüfen Sie, ob Ihre Coinbase-API-Zugangsdaten gültig sind
+ balance: Saldo
+ currently_linked_to: "Aktuell verknüpft mit: %{account_name}"
+ link: Verknüpfen
+ cancel: Abbrechen
+ link_existing_account:
+ success: Erfolgreich mit Coinbase-Konto verknüpft
+ errors:
+ only_manual: Nur manuelle Konten können mit Coinbase verknüpft werden
+ invalid_coinbase_account: Ungültiges Coinbase-Konto
+ coinbase_item:
+ syncer:
+ checking_credentials: Zugangsdaten werden geprüft...
+ credentials_invalid: Ungültige API-Zugangsdaten. Bitte API-Key und Secret prüfen.
+ importing_accounts: Konten werden von Coinbase importiert...
+ checking_configuration: Kontokonfiguration wird geprüft...
+ accounts_need_setup:
+ one: "%{count} Konto muss eingerichtet werden"
+ other: "%{count} Konten müssen eingerichtet werden"
+ processing_accounts: Kontodaten werden verarbeitet...
+ calculating_balances: Salden werden berechnet...
diff --git a/config/locales/views/coinbase_items/es.yml b/config/locales/views/coinbase_items/es.yml
new file mode 100644
index 000000000..4efeeb0eb
--- /dev/null
+++ b/config/locales/views/coinbase_items/es.yml
@@ -0,0 +1,78 @@
+---
+es:
+ coinbase_items:
+ create:
+ default_name: Coinbase
+ success: ¡Conexión con Coinbase establecida con éxito! Tus cuentas se están sincronizando.
+ update:
+ success: Configuración de Coinbase actualizada correctamente.
+ destroy:
+ success: Conexión de Coinbase programada para su eliminación.
+ setup_accounts:
+ title: Importar carteras de Coinbase
+ subtitle: Selecciona qué carteras quieres seguir
+ instructions: Selecciona las carteras que quieres importar. Las carteras no seleccionadas seguirán estando disponibles por si quieres añadirlas más tarde.
+ no_accounts: Se han importado todas las carteras.
+ accounts_count:
+ one: "%{count} cartera disponible"
+ other: "%{count} carteras disponibles"
+ select_all: Seleccionar todas
+ import_selected: Importar seleccionadas
+ cancel: Cancelar
+ creating: Importando...
+ complete_account_setup:
+ success:
+ one: "Se ha importado %{count} cartera"
+ other: "Se han importado %{count} carteras"
+ none_selected: No se ha seleccionado ninguna cartera
+ no_accounts: No hay carteras para importar
+ coinbase_item:
+ provider_name: Coinbase
+ syncing: Sincronizando...
+ reconnect: Es necesario actualizar las credenciales
+ deletion_in_progress: Eliminando...
+ sync_status:
+ no_accounts: No se han encontrado cuentas
+ all_synced:
+ one: "%{count} cuenta sincronizada"
+ other: "%{count} cuentas sincronizadas"
+ partial_sync: "%{linked_count} sincronizadas, %{unlinked_count} necesitan configuración"
+ status: "Sincronizado hace %{timestamp}"
+ status_with_summary: "Sincronizado hace %{timestamp} - %{summary}"
+ status_never: Nunca sincronizado
+ update_credentials: Actualizar credenciales
+ delete: Eliminar
+ no_accounts_title: No se han encontrado cuentas
+ no_accounts_message: Tus carteras de Coinbase aparecerán aquí después de la sincronización.
+ setup_needed: Carteras listas para importar
+ setup_description: Selecciona qué carteras de Coinbase quieres seguir.
+ setup_action: Importar carteras
+ import_wallets_menu: Importar carteras
+ more_wallets_available:
+ one: "%{count} cartera más disponible para importar"
+ other: "%{count} carteras más disponibles para importar"
+ select_existing_account:
+ title: Vincular cuenta de Coinbase
+ no_accounts_found: No se han encontrado cuentas de Coinbase.
+ wait_for_sync: Espera a que Coinbase termine de sincronizar
+ check_provider_health: Comprueba que tus credenciales de la API de Coinbase sean válidas
+ balance: Saldo
+ currently_linked_to: "Vinculada actualmente a: %{account_name}"
+ link: Vincular
+ cancel: Cancelar
+ link_existing_account:
+ success: Vinculado correctamente a la cuenta de Coinbase
+ errors:
+ only_manual: Solo las cuentas manuales pueden vincularse a Coinbase
+ invalid_coinbase_account: Cuenta de Coinbase no válida
+ coinbase_item:
+ syncer:
+ checking_credentials: Comprobando credenciales...
+ credentials_invalid: Credenciales de API no válidas. Por favor, comprueba tu clave API y el secreto.
+ importing_accounts: Importando cuentas desde Coinbase...
+ checking_configuration: Comprobando la configuración de la cuenta...
+ accounts_need_setup:
+ one: "%{count} cuenta necesita configuración"
+ other: "%{count} cuentas necesitan configuración"
+ processing_accounts: Procesando datos de la cuenta...
+ calculating_balances: Calculando saldos.
\ No newline at end of file
diff --git a/config/locales/views/coinbase_items/pl.yml b/config/locales/views/coinbase_items/pl.yml
new file mode 100644
index 000000000..644cff0ca
--- /dev/null
+++ b/config/locales/views/coinbase_items/pl.yml
@@ -0,0 +1,88 @@
+---
+pl:
+ coinbase_items:
+ create:
+ default_name: Coinbase
+ success: Pomyślnie połączono z Coinbase! Twoje konta są synchronizowane.
+ update:
+ success: Pomyślnie zaktualizowano konfigurację Coinbase.
+ destroy:
+ success: Zaplanowano usunięcie połączenia Coinbase.
+ setup_accounts:
+ title: Importuj portfele Coinbase
+ subtitle: Wybierz portfele do śledzenia
+ instructions: Wybierz portfele, które chcesz zaimportować. Niewybrane portfele pozostaną dostępne, jeśli zechcesz dodać je później.
+ no_accounts: Wszystkie portfele zostały zaimportowane.
+ accounts_count:
+ one: "Dostępny %{count} portfel"
+ few: "Dostępne %{count} portfele"
+ many: "Dostępnych %{count} portfeli"
+ other: "Dostępne %{count} portfeli"
+ select_all: Wybierz wszystkie
+ import_selected: Importuj wybrane
+ cancel: Anuluj
+ creating: Importowanie...
+ complete_account_setup:
+ success:
+ one: Zaimportowano %{count} portfel
+ few: Zaimportowano %{count} portfele
+ many: Zaimportowano %{count} portfeli
+ other: Zaimportowano %{count} portfeli
+ none_selected: Nie wybrano portfeli
+ no_accounts: Brak portfeli do importu
+ coinbase_item:
+ provider_name: Coinbase
+ syncing: Synchronizacja...
+ reconnect: Dane uwierzytelniające wymagają aktualizacji
+ deletion_in_progress: Usuwanie...
+ sync_status:
+ no_accounts: Nie znaleziono kont
+ all_synced:
+ one: "%{count} konto zsynchronizowane"
+ few: "%{count} konta zsynchronizowane"
+ many: "%{count} kont zsynchronizowanych"
+ other: "%{count} kont zsynchronizowanych"
+ partial_sync: "%{linked_count} zsynchronizowanych, %{unlinked_count} wymaga konfiguracji"
+ status: Ostatnia synchronizacja %{timestamp} temu
+ status_with_summary: Ostatnia synchronizacja %{timestamp} temu - %{summary}
+ status_never: Nigdy nie synchronizowano
+ update_credentials: Zaktualizuj dane uwierzytelniające
+ delete: Usuń
+ no_accounts_title: Nie znaleziono kont
+ no_accounts_message: Twoje portfele Coinbase pojawią się tutaj po synchronizacji.
+ setup_needed: Portfele gotowe do importu
+ setup_description: Wybierz portfele Coinbase, które chcesz śledzić.
+ setup_action: Importuj portfele
+ import_wallets_menu: Importuj portfele
+ more_wallets_available:
+ one: "Jeszcze %{count} portfel do zaimportowania"
+ few: "Jeszcze %{count} portfele do zaimportowania"
+ many: "Jeszcze %{count} portfeli do zaimportowania"
+ other: "Jeszcze %{count} portfeli do zaimportowania"
+ select_existing_account:
+ title: Połącz konto Coinbase
+ no_accounts_found: Nie znaleziono kont Coinbase.
+ wait_for_sync: Poczekaj, aż Coinbase zakończy synchronizację
+ check_provider_health: Sprawdź, czy dane API Coinbase są prawidłowe
+ balance: Saldo
+ currently_linked_to: 'Aktualnie połączone z: %{account_name}'
+ link: Połącz
+ cancel: Anuluj
+ link_existing_account:
+ success: Pomyślnie połączono z kontem Coinbase
+ errors:
+ only_manual: Z Coinbase można łączyć tylko konta manualne
+ invalid_coinbase_account: Nieprawidłowe konto Coinbase
+ coinbase_item:
+ syncer:
+ checking_credentials: Sprawdzanie danych uwierzytelniających...
+ credentials_invalid: Nieprawidłowe dane API. Sprawdź klucz i sekret API.
+ importing_accounts: Importowanie kont z Coinbase...
+ checking_configuration: Sprawdzanie konfiguracji kont...
+ accounts_need_setup:
+ one: "%{count} konto wymaga konfiguracji"
+ few: "%{count} konta wymagają konfiguracji"
+ many: "%{count} kont wymaga konfiguracji"
+ other: "%{count} kont wymaga konfiguracji"
+ processing_accounts: Przetwarzanie danych kont...
+ calculating_balances: Obliczanie sald...
diff --git a/config/locales/views/coinstats_items/de.yml b/config/locales/views/coinstats_items/de.yml
new file mode 100644
index 000000000..56ce87bd8
--- /dev/null
+++ b/config/locales/views/coinstats_items/de.yml
@@ -0,0 +1,63 @@
+---
+de:
+ coinstats_items:
+ create:
+ success: CoinStats-Provider-Verbindung wurde erfolgreich eingerichtet.
+ default_name: CoinStats-Verbindung
+ errors:
+ validation_failed: "Validierung fehlgeschlagen: %{message}."
+ update:
+ success: CoinStats-Provider-Verbindung wurde erfolgreich aktualisiert.
+ errors:
+ validation_failed: "Validierung fehlgeschlagen: %{message}."
+ destroy:
+ success: CoinStats-Provider-Verbindung wurde zur Löschung vorgemerkt.
+ link_wallet:
+ success: "%{count} Krypto-Wallet(s) erfolgreich verknüpft."
+ missing_params: "Fehlende erforderliche Parameter: Adresse und Blockchain."
+ failed: Verknüpfung des Krypto-Wallets fehlgeschlagen.
+ error: "Krypto-Wallet-Verknüpfung fehlgeschlagen: %{message}."
+ new:
+ title: Krypto-Wallet mit CoinStats verknüpfen
+ blockchain_fetch_error: Blockchains konnten nicht geladen werden. Bitte später erneut versuchen.
+ address_label: Adresse
+ address_placeholder: Erforderlich
+ blockchain_label: Blockchain
+ blockchain_placeholder: Erforderlich
+ blockchain_select_blank: Blockchain auswählen
+ link: Krypto-Wallet verknüpfen
+ not_configured_title: CoinStats-Provider-Verbindung nicht konfiguriert
+ not_configured_message: Zum Verknüpfen eines Krypto-Wallets müssen Sie zuerst die CoinStats-Provider-Verbindung konfigurieren.
+ not_configured_step1_html: Gehen Sie zu Einstellungen → Provider
+ not_configured_step2_html: Suchen Sie den CoinStats-Provider
+ not_configured_step3_html: Folgen Sie den Einrichtungsanweisungen zur Konfiguration
+ go_to_settings: Zu den Provider-Einstellungen
+ setup_instructions: "Einrichtungsanleitung:"
+ step1_html: Besuchen Sie das CoinStats Public API Dashboard, um einen API-Key zu erhalten.
+ step2: Tragen Sie Ihren API-Key unten ein und klicken Sie auf Konfigurieren.
+ step3_html: Nach erfolgreicher Verbindung gehen Sie zum Konten-Tab, um Krypto-Wallets einzurichten.
+ api_key_label: API-Key
+ api_key_placeholder: Erforderlich
+ configure: Konfigurieren
+ update_configuration: Neu konfigurieren
+ default_name: CoinStats-Verbindung
+ status_configured_html: Bereit zur Nutzung
+ status_not_configured: Nicht konfiguriert
+ coinstats_item:
+ deletion_in_progress: Krypto-Wallet-Daten werden gelöscht…
+ provider_name: CoinStats
+ syncing: Synchronisiere…
+ sync_status:
+ no_accounts: Keine Krypto-Wallets gefunden
+ all_synced:
+ one: "%{count} Krypto-Wallet synchronisiert"
+ other: "%{count} Krypto-Wallets synchronisiert"
+ partial_sync: "%{linked_count} Krypto-Wallets synchronisiert, %{unlinked_count} müssen eingerichtet werden"
+ reconnect: Erneut verbinden
+ status: Zuletzt synchronisiert vor %{timestamp}
+ status_never: Noch nie synchronisiert
+ status_with_summary: "Zuletzt synchronisiert vor %{timestamp} • %{summary}"
+ update_api_key: API-Key aktualisieren
+ delete: Löschen
+ no_wallets_title: Keine Krypto-Wallets verbunden
+ no_wallets_message: Derzeit sind keine Krypto-Wallets mit CoinStats verbunden.
diff --git a/config/locales/views/coinstats_items/en.yml b/config/locales/views/coinstats_items/en.yml
index 00add222d..ecd41109b 100644
--- a/config/locales/views/coinstats_items/en.yml
+++ b/config/locales/views/coinstats_items/en.yml
@@ -17,17 +17,31 @@ en:
missing_params: "Missing required parameters: address and blockchain."
failed: Crypto wallet linking failed.
error: "Crypto wallet linking failed: %{message}."
+ link_exchange:
+ success: "%{name} exchange linked."
+ missing_params: Exchange and credentials are required.
+ invalid_exchange: Selected exchange is no longer supported.
+ failed: Failed to link exchange.
+ error: "Failed to link exchange: %{message}."
new:
- title: Link a Crypto Wallet with CoinStats
+ title: Link Crypto with CoinStats
blockchain_fetch_error: Failed load Blockchains. Please try again later.
+ link_wallet_title: Link Wallet Address
+ link_wallet_description: Track a self-custody wallet or a single on-chain address through CoinStats.
address_label: Address
address_placeholder: Required
blockchain_label: Blockchain
blockchain_placeholder: Required
blockchain_select_blank: Select a Blockchain
- link: Link Crypto Wallet
+ link_wallet_submit: Link Crypto Wallet
+ link_exchange_title: Link Exchange API
+ link_exchange_description: Use a read-only exchange API key so CoinStats can sync balances and transactions from Bitvavo, Binance, and other supported exchanges.
+ link_exchange_note: If your exchange requires API-key activation or email confirmation, complete that step before linking here.
+ exchange_select_blank: Select an exchange
+ exchange_label: Exchange
+ link_exchange_submit: Link Exchange
not_configured_title: CoinStats provider connection not configured
- not_configured_message: To link a crypto wallet, you must first configure the CoinStats provider connection.
+ not_configured_message: To link a crypto wallet or exchange, you must first configure the CoinStats provider connection.
not_configured_step1_html: Go to Settings → Providers
not_configured_step2_html: Locate the CoinStats provider
not_configured_step3_html: Follow the provided setup Instructions to complete provider configuration
@@ -35,7 +49,7 @@ en:
setup_instructions: "Setup Instructions:"
step1_html: Visit the CoinStats Public API Dashboard to obtain an API key.
step2: Enter your API key below and click Configure.
- step3_html: After a successful connection, visit the Accounts tab to set up crypto wallets.
+ step3_html: After a successful connection, visit the Accounts tab to set up your crypto accounts.
api_key_label: API Key
api_key_placeholder: Required
configure: Configure
diff --git a/config/locales/views/coinstats_items/es.yml b/config/locales/views/coinstats_items/es.yml
new file mode 100644
index 000000000..36744d53e
--- /dev/null
+++ b/config/locales/views/coinstats_items/es.yml
@@ -0,0 +1,63 @@
+---
+es:
+ coinstats_items:
+ create:
+ success: Conexión del proveedor CoinStats configurada correctamente.
+ default_name: Conexión de CoinStats
+ errors:
+ validation_failed: "Error de validación: %{message}."
+ update:
+ success: Conexión del proveedor CoinStats actualizada correctamente.
+ errors:
+ validation_failed: "Error de validación: %{message}."
+ destroy:
+ success: Conexión del proveedor CoinStats programada para su eliminación.
+ link_wallet:
+ success: "%{count} cartera(s) de criptomonedas vinculada(s) correctamente."
+ missing_params: "Faltan parámetros requeridos: dirección y blockchain."
+ failed: Error al vincular la cartera de criptomonedas.
+ error: "Error al vincular la cartera de criptomonedas: %{message}."
+ new:
+ title: Vincular una cartera de criptomonedas con CoinStats
+ blockchain_fetch_error: Error al cargar las Blockchains. Por favor, inténtalo de nuevo más tarde.
+ address_label: Dirección
+ address_placeholder: Obligatorio
+ blockchain_label: Blockchain
+ blockchain_placeholder: Obligatorio
+ blockchain_select_blank: Selecciona una Blockchain
+ link: Vincular cartera de criptomonedas
+ not_configured_title: Conexión del proveedor CoinStats no configurada
+ not_configured_message: Para vincular una cartera de criptomonedas, primero debes configurar la conexión del proveedor CoinStats.
+ not_configured_step1_html: Ve a Ajustes → Proveedores
+ not_configured_step2_html: Localiza el proveedor CoinStats
+ not_configured_step3_html: Sigue las instrucciones de configuración proporcionadas para completar la configuración del proveedor
+ go_to_settings: Ir a Ajustes de proveedores
+ setup_instructions: "Instrucciones de configuración:"
+ step1_html: Visita el Panel de la API pública de CoinStats para obtener una clave API.
+ step2: Introduce tu clave API a continuación y haz clic en Configurar.
+ step3_html: Tras una conexión exitosa, visita la pestaña de Cuentas para configurar tus carteras de criptomonedas.
+ api_key_label: Clave API
+ api_key_placeholder: Obligatorio
+ configure: Configurar
+ update_configuration: Reconfigurar
+ default_name: Conexión de CoinStats
+ status_configured_html: Listo para usar
+ status_not_configured: No configurado
+ coinstats_item:
+ deletion_in_progress: Los datos de la cartera de criptomonedas se están eliminando…
+ provider_name: CoinStats
+ syncing: Sincronizando…
+ sync_status:
+ no_accounts: No se han encontrado carteras de criptomonedas
+ all_synced:
+ one: "%{count} cartera de criptomonedas sincronizada"
+ other: "%{count} carteras de criptomonedas sincronizadas"
+ partial_sync: "%{linked_count} carteras de criptomonedas sincronizadas, %{unlinked_count} necesitan configuración"
+ reconnect: Reconectar
+ status: Sincronizado hace %{timestamp}
+ status_never: Nunca sincronizado
+ status_with_summary: "Sincronizado hace %{timestamp} • %{summary}"
+ update_api_key: Actualizar clave API
+ delete: Eliminar
+ no_wallets_title: No hay carteras de criptomonedas conectadas
+ no_wallets_message: Actualmente no hay carteras de criptomonedas conectadas a CoinStats.
\ No newline at end of file
diff --git a/config/locales/views/coinstats_items/pl.yml b/config/locales/views/coinstats_items/pl.yml
new file mode 100644
index 000000000..968c9aa0b
--- /dev/null
+++ b/config/locales/views/coinstats_items/pl.yml
@@ -0,0 +1,69 @@
+---
+pl:
+ coinstats_items:
+ create:
+ success: Połączenie z dostawcą CoinStats zostało pomyślnie skonfigurowane.
+ default_name: Połączenie CoinStats
+ errors:
+ validation_failed: 'Walidacja nie powiodła się: %{message}.'
+ update:
+ success: Połączenie z dostawcą CoinStats zostało pomyślnie zaktualizowane.
+ errors:
+ validation_failed: 'Walidacja nie powiodła się: %{message}.'
+ destroy:
+ success: Połączenie z dostawcą CoinStats zostało zaplanowane do usunięcia.
+ link_wallet:
+ success:
+ one: "Pomyślnie połączono %{count} portfel kryptowalutowy."
+ few: "Pomyślnie połączono %{count} portfele kryptowalutowe."
+ many: "Pomyślnie połączono %{count} portfeli kryptowalutowych."
+ other: "Pomyślnie połączono %{count} portfela kryptowalutowego."
+ missing_params: 'Brak wymaganych parametrów: address i blockchain.'
+ failed: Łączenie portfela kryptowalutowego nie powiodło się.
+ error: 'Łączenie portfela kryptowalutowego nie powiodło się: %{message}.'
+ new:
+ title: Połącz portfel kryptowalutowy z CoinStats
+ blockchain_fetch_error: Nie udało się wczytać blockchainów. Spróbuj ponownie później.
+ address_label: Adres
+ address_placeholder: Wymagane
+ blockchain_label: Blockchain
+ blockchain_placeholder: Wymagane
+ blockchain_select_blank: Wybierz blockchain
+ link: Połącz portfel kryptowalutowy
+ not_configured_title: Połączenie z dostawcą CoinStats nie jest skonfigurowane
+ not_configured_message: Aby połączyć portfel kryptowalutowy, najpierw skonfiguruj połączenie z dostawcą CoinStats.
+ not_configured_step1_html: Przejdź do Ustawienia → Dostawcy
+ not_configured_step2_html: Znajdź dostawcę CoinStats
+ not_configured_step3_html: Postępuj zgodnie z podanymi instrukcjami konfiguracji, aby ukończyć konfigurację dostawcy
+ go_to_settings: Przejdź do ustawień dostawców
+ setup_instructions: 'Instrukcje konfiguracji:'
+ step1_html: Odwiedź panel publicznego API CoinStats, aby uzyskać klucz API.
+ step2: Wprowadź poniżej swój klucz API i kliknij Skonfiguruj.
+ step3_html: Po udanym połączeniu przejdź do zakładki Konta, aby skonfigurować portfele kryptowalutowe.
+ api_key_label: Klucz API
+ api_key_placeholder: Wymagane
+ configure: Skonfiguruj
+ update_configuration: Skonfiguruj ponownie
+ default_name: Połączenie CoinStats
+ status_configured_html: Gotowe do użycia
+ status_not_configured: Nieskonfigurowane
+ coinstats_item:
+ deletion_in_progress: Trwa usuwanie danych portfela kryptowalutowego…
+ provider_name: CoinStats
+ syncing: Synchronizacja…
+ sync_status:
+ no_accounts: Nie znaleziono portfeli kryptowalutowych
+ all_synced:
+ one: "Zsynchronizowano %{count} portfel kryptowalutowy"
+ few: "Zsynchronizowano %{count} portfele kryptowalutowe"
+ many: "Zsynchronizowano %{count} portfeli kryptowalutowych"
+ other: "Zsynchronizowano %{count} portfeli kryptowalutowych"
+ partial_sync: "Zsynchronizowano %{linked_count} portfeli kryptowalutowych, %{unlinked_count} wymaga konfiguracji"
+ reconnect: Połącz ponownie
+ status: Ostatnia synchronizacja %{timestamp} temu
+ status_never: Nigdy nie synchronizowano
+ status_with_summary: Ostatnia synchronizacja %{timestamp} temu • %{summary}
+ update_api_key: Zaktualizuj klucz API
+ delete: Usuń
+ no_wallets_title: Brak połączonych portfeli kryptowalutowych
+ no_wallets_message: Obecnie żadne portfele kryptowalutowe nie są połączone z CoinStats.
diff --git a/config/locales/views/components/de.yml b/config/locales/views/components/de.yml
new file mode 100644
index 000000000..ec3e70fed
--- /dev/null
+++ b/config/locales/views/components/de.yml
@@ -0,0 +1,67 @@
+---
+de:
+ provider_sync_summary:
+ title: Sync-Zusammenfassung
+ last_sync: "Letzter Sync: vor %{time_ago}"
+ accounts:
+ title: Konten
+ total: "Gesamt: %{count}"
+ linked: "Verknüpft: %{count}"
+ unlinked: "Nicht verknüpft: %{count}"
+ institutions: "Institute: %{count}"
+ transactions:
+ title: Buchungen
+ seen: "Erfasst: %{count}"
+ imported: "Importiert: %{count}"
+ updated: "Aktualisiert: %{count}"
+ skipped: "Übersprungen: %{count}"
+ fetching: "Wird vom Broker abgerufen..."
+ protected:
+ one: "%{count} Buchung geschützt (nicht überschrieben)"
+ other: "%{count} Buchungen geschützt (nicht überschrieben)"
+ view_protected: Geschützte Buchungen anzeigen
+ skip_reasons:
+ excluded: Ausgeschlossen
+ user_modified: Vom Benutzer geändert
+ import_locked: CSV-Import
+ protected: Geschützt
+ holdings:
+ title: Bestände
+ found: "Gefunden: %{count}"
+ processed: "Verarbeitet: %{count}"
+ trades:
+ title: Trades
+ imported: "Importiert: %{count}"
+ skipped: "Übersprungen: %{count}"
+ fetching: "Aktivitäten werden vom Broker abgerufen..."
+ health:
+ title: Status
+ view_error_details: Fehlerdetails anzeigen
+ rate_limited: "Rate-Limit vor %{time_ago}"
+ recently: kürzlich
+ errors: "Fehler: %{count}"
+ pending_reconciled:
+ one: "%{count} ausstehende Doppelbuchung abgeglichen"
+ other: "%{count} ausstehende Doppelbuchungen abgeglichen"
+ view_reconciled: Abgeglichene Buchungen anzeigen
+ duplicate_suggestions:
+ one: "%{count} mögliche Doppelbuchung zur Prüfung"
+ other: "%{count} mögliche Doppelbuchungen zur Prüfung"
+ view_duplicate_suggestions: Vorgeschlagene Duplikate anzeigen
+ stale_pending:
+ one: "%{count} veraltete ausstehende Buchung (von Budgets ausgeschlossen)"
+ other: "%{count} veraltete ausstehende Buchungen (von Budgets ausgeschlossen)"
+ view_stale_pending: Betroffene Konten anzeigen
+ stale_pending_count:
+ one: "%{count} Buchung"
+ other: "%{count} Buchungen"
+ stale_unmatched:
+ one: "%{count} ausstehende Buchung benötigt manuelle Prüfung"
+ other: "%{count} ausstehende Buchungen benötigen manuelle Prüfung"
+ view_stale_unmatched: Zu prüfende Buchungen anzeigen
+ stale_unmatched_count:
+ one: "%{count} Buchung"
+ other: "%{count} Buchungen"
+ data_warnings: "Datenwarnungen: %{count}"
+ notices: "Hinweise: %{count}"
+ view_data_quality: Datenqualitätsdetails anzeigen
diff --git a/config/locales/views/components/es.yml b/config/locales/views/components/es.yml
new file mode 100644
index 000000000..4587a0088
--- /dev/null
+++ b/config/locales/views/components/es.yml
@@ -0,0 +1,66 @@
+es:
+ provider_sync_summary:
+ title: Resumen de sincronización
+ last_sync: "Última sincronización: hace %{time_ago}"
+ accounts:
+ title: Cuentas
+ total: "Total: %{count}"
+ linked: "Vinculadas: %{count}"
+ unlinked: "Desvinculadas: %{count}"
+ institutions: "Instituciones: %{count}"
+ transactions:
+ title: Transacciones
+ seen: "Detectadas: %{count}"
+ imported: "Importadas: %{count}"
+ updated: "Actualizadas: %{count}"
+ skipped: "Omitidas: %{count}"
+ fetching: "Obteniendo del bróker..."
+ protected:
+ one: "%{count} entrada protegida (no sobrescrita)"
+ other: "%{count} entradas protegidas (no sobrescritas)"
+ view_protected: Ver entradas protegidas
+ skip_reasons:
+ excluded: Excluida
+ user_modified: Modificada por el usuario
+ import_locked: Importación CSV
+ protected: Protegida
+ holdings:
+ title: Posiciones
+ found: "Encontradas: %{count}"
+ processed: "Procesadas: %{count}"
+ trades:
+ title: Operaciones
+ imported: "Importadas: %{count}"
+ skipped: "Omitidas: %{count}"
+ fetching: "Obteniendo actividades del bróker..."
+ health:
+ title: Estado de salud
+ view_error_details: Ver detalles del error
+ rate_limited: "Límite de frecuencia alcanzado hace %{time_ago}"
+ recently: recientemente
+ errors: "Errores: %{count}"
+ pending_reconciled:
+ one: "%{count} transacción pendiente duplicada conciliada"
+ other: "%{count} transacciones pendientes duplicadas conciliadas"
+ view_reconciled: Ver transacciones conciliadas
+ duplicate_suggestions:
+ one: "%{count} posible duplicado necesita revisión"
+ other: "%{count} posibles duplicados necesitan revisión"
+ view_duplicate_suggestions: Ver duplicados sugeridos
+ stale_pending:
+ one: "%{count} transacción pendiente antigua (excluida de presupuestos)"
+ other: "%{count} transacciones pendientes antiguas (excluidas de presupuestos)"
+ view_stale_pending: Ver cuentas afectadas
+ stale_pending_count:
+ one: "%{count} transacción"
+ other: "%{count} transacciones"
+ stale_unmatched:
+ one: "%{count} transacción pendiente necesita revisión manual"
+ other: "%{count} transacciones pendientes necesitan revisión manual"
+ view_stale_unmatched: Ver transacciones que necesitan revisión
+ stale_unmatched_count:
+ one: "%{count} transacción"
+ other: "%{count} transacciones"
+ data_warnings: "Avisos de datos: %{count}"
+ notices: "Avisos: %{count}"
+ view_data_quality: Ver detalles de calidad de datos
\ No newline at end of file
diff --git a/config/locales/views/components/pl.yml b/config/locales/views/components/pl.yml
new file mode 100644
index 000000000..3c55c50f6
--- /dev/null
+++ b/config/locales/views/components/pl.yml
@@ -0,0 +1,81 @@
+---
+pl:
+ provider_sync_summary:
+ title: Podsumowanie synchronizacji
+ last_sync: 'Ostatnia synchronizacja: %{time_ago} temu'
+ accounts:
+ title: Konta
+ total: 'Łącznie: %{count}'
+ linked: 'Połączone: %{count}'
+ unlinked: 'Niepołączone: %{count}'
+ institutions: 'Instytucje: %{count}'
+ transactions:
+ title: Transakcje
+ seen: 'Wykryte: %{count}'
+ imported: 'Zaimportowane: %{count}'
+ updated: 'Zaktualizowane: %{count}'
+ skipped: 'Pominięte: %{count}'
+ fetching: Pobieranie z biura maklerskiego...
+ protected:
+ one: "%{count} wpis chroniony (nie nadpisano)"
+ few: "%{count} wpisy chronione (nie nadpisano)"
+ many: "%{count} wpisów chronionych (nie nadpisano)"
+ other: "%{count} wpisów chronionych (nie nadpisano)"
+ view_protected: Zobacz chronione wpisy
+ skip_reasons:
+ excluded: Wykluczone
+ user_modified: Zmodyfikowane przez użytkownika
+ import_locked: Import z CSV
+ protected: Chronione
+ holdings:
+ title: Pozycje
+ found: 'Znalezione: %{count}'
+ processed: 'Przetworzone: %{count}'
+ trades:
+ title: Transakcje giełdowe
+ imported: 'Zaimportowane: %{count}'
+ skipped: 'Pominięte: %{count}'
+ fetching: Pobieranie aktywności z biura maklerskiego...
+ health:
+ title: Kondycja
+ view_error_details: Zobacz szczegóły błędów
+ rate_limited: Ograniczenie limitu %{time_ago}
+ recently: niedawno
+ errors: 'Błędy: %{count}'
+ pending_reconciled:
+ one: "Uzgodniono %{count} zduplikowaną transakcję oczekującą"
+ few: "Uzgodniono %{count} zduplikowane transakcje oczekujące"
+ many: "Uzgodniono %{count} zduplikowanych transakcji oczekujących"
+ other: "Uzgodniono %{count} zduplikowanych transakcji oczekujących"
+ view_reconciled: Zobacz uzgodnione transakcje
+ duplicate_suggestions:
+ one: "%{count} możliwy duplikat wymaga przeglądu"
+ few: "%{count} możliwe duplikaty wymagają przeglądu"
+ many: "%{count} możliwych duplikatów wymaga przeglądu"
+ other: "%{count} możliwe duplikaty wymagają przeglądu"
+ view_duplicate_suggestions: Zobacz sugerowane duplikaty
+ stale_pending:
+ one: "%{count} przestarzała transakcja oczekująca (wykluczona z budżetów)"
+ few: "%{count} przestarzałe transakcje oczekujące (wykluczone z budżetów)"
+ many: "%{count} przestarzałych transakcji oczekujących (wykluczonych z budżetów)"
+ other: "%{count} przestarzałych transakcji oczekujących (wykluczonych z budżetów)"
+ view_stale_pending: Zobacz dotknięte konta
+ stale_pending_count:
+ one: "%{count} transakcja"
+ few: "%{count} transakcje"
+ many: "%{count} transakcji"
+ other: "%{count} transakcji"
+ stale_unmatched:
+ one: "%{count} transakcja oczekująca wymaga ręcznego przeglądu"
+ few: "%{count} transakcje oczekujące wymagają ręcznego przeglądu"
+ many: "%{count} transakcji oczekujących wymaga ręcznego przeglądu"
+ other: "%{count} transakcji oczekujących wymaga ręcznego przeglądu"
+ view_stale_unmatched: Zobacz transakcje wymagające przeglądu
+ stale_unmatched_count:
+ one: "%{count} transakcja"
+ few: "%{count} transakcje"
+ many: "%{count} transakcji"
+ other: "%{count} transakcji"
+ data_warnings: 'Ostrzeżenia danych: %{count}'
+ notices: 'Powiadomienia: %{count}'
+ view_data_quality: Zobacz szczegóły jakości danych
diff --git a/config/locales/views/components/pt-BR.yml b/config/locales/views/components/pt-BR.yml
new file mode 100644
index 000000000..c496a0ac9
--- /dev/null
+++ b/config/locales/views/components/pt-BR.yml
@@ -0,0 +1,67 @@
+---
+pt-BR:
+ provider_sync_summary:
+ title: Resumo da sincronização
+ last_sync: "Última sincronização: há %{time_ago}"
+ accounts:
+ title: Contas
+ total: "Total: %{count}"
+ linked: "Vinculado: %{count}"
+ unlinked: "Não vinculado: %{count}"
+ institutions: "Instituições: %{count}"
+ transactions:
+ title: Transações
+ seen: "Vistos: %{count}"
+ imported: "Importado: %{count}"
+ updated: "Atualizado: %{count}"
+ skipped: "Ignorada: %{count}"
+ fetching: "Buscando na corretora..."
+ protected:
+ one: "%{count} entrada protegida (não sobrescrita)"
+ other: "%{count} entradas protegidas (não sobrescritas)"
+ view_protected: Ver entradas protegidas
+ skip_reasons:
+ excluded: Excluída
+ user_modified: Usuário modificou
+ import_locked: Importação de CSV
+ protected: Protegida
+ holdings:
+ title: Holdings
+ found: "Encontrado: %{count}"
+ processed: "Processados: %{count}"
+ trades:
+ title: Trocas
+ imported: "Importado: %{count}"
+ skipped: "Ignorado: %{count}"
+ fetching: "Buscar atividades da corretora..."
+ health:
+ title: Saúde
+ view_error_details: Ver detalhes do erro
+ rate_limited: "Taxa limitada %{time_ago}"
+ recently: recentemente
+ errors: "Erros: %{count}"
+ pending_reconciled:
+ one: "%{count} transações pendentes duplicadas reconciliadas"
+ other: "%{count} transações pendentes duplicadas reconciliadas"
+ view_reconciled: Visualizar transações conciliadas
+ duplicate_suggestions:
+ one: "%{count} possível duplicado precisa de revisão"
+ other: "%{count} possíveis duplicados precisam ser revisados"
+ view_duplicate_suggestions: Ver duplicados sugeridos
+ stale_pending:
+ one: "%{count} transações pendentes obsoletas (excluídas dos orçamentos)"
+ other: "%{count} transações pendentes obsoletas (excluídas dos orçamentos)"
+ view_stale_pending: Veja as contas afetadas
+ stale_pending_count:
+ one: "%{count} transação"
+ other: "%{count} transações"
+ stale_unmatched:
+ one: "%{count} transação pendente precisa de revisão manual"
+ other: "%{count} transações pendentes precisam de revisão manual"
+ view_stale_unmatched: Veja as transações que precisam de revisão.
+ stale_unmatched_count:
+ one: "%{count} transação"
+ other: "%{count} transações"
+ data_warnings: "Avisos de dados: %{count}"
+ notices: "Avisos: %{count}"
+ view_data_quality: Veja os detalhes da qualidade dos dados.
diff --git a/config/locales/views/credit_cards/pl.yml b/config/locales/views/credit_cards/pl.yml
new file mode 100644
index 000000000..2b5a0fa81
--- /dev/null
+++ b/config/locales/views/credit_cards/pl.yml
@@ -0,0 +1,25 @@
+---
+pl:
+ credit_cards:
+ edit:
+ edit: Edytuj %{account}
+ form:
+ annual_fee: Opłata roczna
+ annual_fee_placeholder: '99'
+ apr: RRSO
+ apr_placeholder: '15.99'
+ available_credit: Dostępny limit kredytowy
+ available_credit_placeholder: '10000'
+ expiration_date: Data ważności
+ minimum_payment: Minimalna spłata
+ minimum_payment_placeholder: '100'
+ new:
+ title: Wprowadź dane karty kredytowej
+ overview:
+ amount_owed: Kwota zadłużenia
+ annual_fee: Opłata roczna
+ apr: RRSO
+ available_credit: Dostępny limit kredytowy
+ expiration_date: Data ważności
+ minimum_payment: Minimalna spłata
+ unknown: Nieznane
diff --git a/config/locales/views/cryptos/de.yml b/config/locales/views/cryptos/de.yml
index 06039fa62..250562848 100644
--- a/config/locales/views/cryptos/de.yml
+++ b/config/locales/views/cryptos/de.yml
@@ -3,5 +3,18 @@ de:
cryptos:
edit:
edit: "%{account} bearbeiten"
+ form:
+ subtype_label: Kontotyp
+ subtype_prompt: Typ auswählen...
+ subtype_none: Nicht angegeben
+ tax_treatment_label: Steuerbehandlung
+ tax_treatment_hint: Die meisten Kryptowährungen werden in versteuerten Konten gehalten. Wählen Sie eine andere Option bei steuerbegünstigten Konten (z. B. selbst verwaltetes IRA).
new:
title: Kontostand eingeben
+ subtypes:
+ wallet:
+ short: Wallet
+ long: Krypto-Wallet
+ exchange:
+ short: Börse
+ long: Krypto-Börse
diff --git a/config/locales/views/cryptos/en.yml b/config/locales/views/cryptos/en.yml
index 26649e9ad..d2a4e3ddd 100644
--- a/config/locales/views/cryptos/en.yml
+++ b/config/locales/views/cryptos/en.yml
@@ -5,10 +5,10 @@ en:
edit: Edit %{account}
form:
subtype_label: Account type
- subtype_prompt: Select type...
- subtype_none: Not specified
+ subtype_prompt: Select account type
+ subtype_none: None
tax_treatment_label: Tax Treatment
- tax_treatment_hint: Most cryptocurrency is held in taxable accounts. Select a different option if held in a tax-advantaged account like a self-directed IRA.
+ tax_treatment_hint: Most cryptocurrency is held in taxable accounts. Select a different option if held in a tax-advantaged account.
new:
title: Enter account balance
subtypes:
diff --git a/config/locales/views/cryptos/es.yml b/config/locales/views/cryptos/es.yml
index 15a8125f5..14ea42ded 100644
--- a/config/locales/views/cryptos/es.yml
+++ b/config/locales/views/cryptos/es.yml
@@ -3,5 +3,18 @@ es:
cryptos:
edit:
edit: Editar %{account}
+ form:
+ subtype_label: Tipo de cuenta
+ subtype_prompt: Seleccionar tipo...
+ subtype_none: No especificado
+ tax_treatment_label: Tratamiento fiscal
+ tax_treatment_hint: La mayoría de las criptomonedas se mantienen en cuentas sujetas a impuestos. Selecciona una opción diferente si se mantienen en una cuenta con ventajas fiscales.
new:
title: Introducir saldo de la cuenta
+ subtypes:
+ wallet:
+ short: Cartera
+ long: Cartera de criptomonedas
+ exchange:
+ short: Exchange
+ long: Exchange de criptomonedas
\ No newline at end of file
diff --git a/config/locales/views/cryptos/pl.yml b/config/locales/views/cryptos/pl.yml
new file mode 100644
index 000000000..6dc0cfe87
--- /dev/null
+++ b/config/locales/views/cryptos/pl.yml
@@ -0,0 +1,20 @@
+---
+pl:
+ cryptos:
+ edit:
+ edit: Edytuj %{account}
+ form:
+ subtype_label: Typ konta
+ subtype_prompt: Wybierz typ...
+ subtype_none: Nie określono
+ tax_treatment_label: Sposób opodatkowania
+ tax_treatment_hint: Większość kryptowalut jest utrzymywana na kontach opodatkowanych. Wybierz inną opcję, jeśli środki są trzymane na koncie uprzywilejowanym podatkowo, np. samodzielnie prowadzonym IRA.
+ new:
+ title: Wprowadź saldo konta
+ subtypes:
+ wallet:
+ short: Portfel
+ long: Portfel kryptowalutowy
+ exchange:
+ short: Giełda
+ long: Giełda kryptowalut
diff --git a/config/locales/views/depositories/pl.yml b/config/locales/views/depositories/pl.yml
new file mode 100644
index 000000000..5f526fb2b
--- /dev/null
+++ b/config/locales/views/depositories/pl.yml
@@ -0,0 +1,10 @@
+---
+pl:
+ depositories:
+ edit:
+ edit: Edytuj %{account}
+ form:
+ none: Brak
+ subtype_prompt: Wybierz typ konta
+ new:
+ title: Wprowadź saldo konta
diff --git a/config/locales/views/email_confirmation_mailer/pl.yml b/config/locales/views/email_confirmation_mailer/pl.yml
new file mode 100644
index 000000000..6f086bd37
--- /dev/null
+++ b/config/locales/views/email_confirmation_mailer/pl.yml
@@ -0,0 +1,9 @@
+---
+pl:
+ email_confirmation_mailer:
+ confirmation_email:
+ body: Niedawno poproszono o zmianę Twojego adresu e-mail. Kliknij przycisk poniżej, aby potwierdzić tę zmianę.
+ cta: Potwierdź zmianę e-maila
+ expiry_notice: Ten link wygaśnie za %{hours} godzin.
+ greeting: Cześć!
+ subject: "%{product_name}: potwierdź zmianę adresu e-mail"
diff --git a/config/locales/views/enable_banking_items/de.yml b/config/locales/views/enable_banking_items/de.yml
new file mode 100644
index 000000000..c7ead9255
--- /dev/null
+++ b/config/locales/views/enable_banking_items/de.yml
@@ -0,0 +1,49 @@
+---
+de:
+ enable_banking_items:
+ authorize:
+ authorization_failed: Autorisierung konnte nicht gestartet werden
+ bank_required: Bitte wählen Sie eine Bank.
+ invalid_redirect: Die erhaltene Autorisierungs-URL ist ungültig. Bitte versuchen Sie es erneut.
+ redirect_uri_not_allowed: Weiterleitung nicht erlaubt. Konfigurieren Sie %{callback_url} in den Enable-Banking-App-Einstellungen.
+ unexpected_error: Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.
+ callback:
+ authorization_error: Autorisierung fehlgeschlagen
+ invalid_callback: Ungültige Callback-Parameter.
+ item_not_found: Verbindung nicht gefunden.
+ session_failed: Autorisierung konnte nicht abgeschlossen werden
+ success: Erfolgreich mit Ihrer Bank verbunden. Ihre Konten werden synchronisiert.
+ unexpected_error: Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.
+ complete_account_setup:
+ all_skipped: Alle Konten wurden übersprungen. Sie können sie später auf der Konten-Seite einrichten.
+ no_accounts: Keine Konten zum Einrichten verfügbar.
+ success: "%{count} Konten wurden erfolgreich angelegt!"
+ create:
+ success: Enable-Banking-Konfiguration erfolgreich.
+ destroy:
+ success: Die Enable-Banking-Verbindung wurde zur Löschung vorgemerkt.
+ link_accounts:
+ already_linked: Die ausgewählten Konten sind bereits verknüpft.
+ link_failed: Konten konnten nicht verknüpft werden
+ no_accounts_selected: Keine Konten ausgewählt.
+ no_session: Keine aktive Enable-Banking-Verbindung. Bitte verbinden Sie zuerst eine Bank.
+ success: "%{count} Konten wurden erfolgreich verknüpft."
+ link_existing_account:
+ success: Konto erfolgreich mit Enable-Banking verknüpft
+ errors:
+ only_manual: Nur manuelle Konten können verknüpft werden
+ invalid_enable_banking_account: Ungültiges Enable-Banking-Konto ausgewählt
+ new:
+ link_enable_banking_title: Enable-Banking verknüpfen
+ reauthorize:
+ invalid_redirect: Die erhaltene Autorisierungs-URL ist ungültig. Bitte versuchen Sie es erneut.
+ reauthorization_failed: Erneute Autorisierung fehlgeschlagen
+ select_bank:
+ cancel: Abbrechen
+ check_country: Bitte prüfen Sie Ihre Ländereinstellungen.
+ credentials_required: Bitte konfigurieren Sie zuerst Ihre Enable-Banking-Zugangsdaten.
+ description: Wählen Sie die Bank, die Sie mit Ihren Konten verbinden möchten.
+ no_banks: Für diese Region/Land sind keine Banken verfügbar.
+ title: Wählen Sie Ihre Bank
+ update:
+ success: Enable-Banking-Konfiguration aktualisiert.
diff --git a/config/locales/views/enable_banking_items/es.yml b/config/locales/views/enable_banking_items/es.yml
new file mode 100644
index 000000000..62d890f0e
--- /dev/null
+++ b/config/locales/views/enable_banking_items/es.yml
@@ -0,0 +1,49 @@
+---
+es:
+ enable_banking_items:
+ authorize:
+ authorization_failed: Error al iniciar la autorización
+ bank_required: Por favor, selecciona un banco.
+ invalid_redirect: La URL de autorización recibida no es válida. Por favor, inténtalo de nuevo.
+ redirect_uri_not_allowed: Redirección no permitida. Por favor, configura `%{callback_url}` en los ajustes de tu aplicación de Enable Banking.
+ unexpected_error: Ha ocurrido un error inesperado. Por favor, inténtalo de nuevo.
+ callback:
+ authorization_error: Error de autorización
+ invalid_callback: Parámetros de retorno (callback) no válidos.
+ item_not_found: Conexión no encontrada.
+ session_failed: No se ha podido completar la autorización
+ success: Conectado correctamente con tu banco. Tus cuentas se están sincronizando.
+ unexpected_error: Ha ocurrido un error inesperado. Por favor, inténtalo de nuevo.
+ complete_account_setup:
+ all_skipped: Se han omitido todas las cuentas. Puedes configurarlas más tarde en la página de cuentas.
+ no_accounts: No hay cuentas disponibles para configurar.
+ success: ¡Se han creado %{count} cuentas correctamente!
+ create:
+ success: Configuración de Enable Banking realizada con éxito.
+ destroy:
+ success: La conexión de Enable Banking se ha puesto en cola para su eliminación.
+ link_accounts:
+ already_linked: Las cuentas seleccionadas ya están vinculadas.
+ link_failed: Error al vincular las cuentas
+ no_accounts_selected: No se ha seleccionado ninguna cuenta.
+ no_session: No hay ninguna conexión activa de Enable Banking. Por favor, conéctate primero a un banco.
+ success: Se han vinculado %{count} cuentas correctamente.
+ link_existing_account:
+ success: Cuenta vinculada correctamente a Enable Banking
+ errors:
+ only_manual: Solo se pueden vincular cuentas manuales
+ invalid_enable_banking_account: Se ha seleccionado una cuenta de Enable Banking no válida
+ new:
+ link_enable_banking_title: Vincular Enable Banking
+ reauthorize:
+ invalid_redirect: La URL de autorización recibida no es válida. Por favor, inténtalo de nuevo.
+ reauthorization_failed: Error en la reautorización
+ select_bank:
+ cancel: Cancelar
+ check_country: Por favor, comprueba los ajustes de tu código de país.
+ credentials_required: Por favor, configura primero tus credenciales de Enable Banking.
+ description: Selecciona el banco que quieres conectar a tus cuentas.
+ no_banks: No hay bancos disponibles para este país/región.
+ title: Selecciona tu banco
+ update:
+ success: Configuración de Enable Banking actualizada.
\ No newline at end of file
diff --git a/config/locales/views/enable_banking_items/pl.yml b/config/locales/views/enable_banking_items/pl.yml
new file mode 100644
index 000000000..596febecb
--- /dev/null
+++ b/config/locales/views/enable_banking_items/pl.yml
@@ -0,0 +1,57 @@
+---
+pl:
+ enable_banking_items:
+ authorize:
+ authorization_failed: Nie udało się rozpocząć autoryzacji
+ bank_required: Wybierz bank.
+ invalid_redirect: Otrzymany adres URL autoryzacji jest nieprawidłowy. Spróbuj ponownie.
+ redirect_uri_not_allowed: Przekierowanie niedozwolone. Skonfiguruj `%{callback_url}` w ustawieniach aplikacji Enable Banking.
+ unexpected_error: Wystąpił nieoczekiwany błąd. Spróbuj ponownie.
+ callback:
+ authorization_error: Autoryzacja nie powiodła się
+ invalid_callback: Nieprawidłowe parametry callback.
+ item_not_found: Nie znaleziono połączenia.
+ session_failed: Nie udało się zakończyć autoryzacji
+ success: Pomyślnie połączono z bankiem. Twoje konta są synchronizowane.
+ unexpected_error: Wystąpił nieoczekiwany błąd. Spróbuj ponownie.
+ complete_account_setup:
+ all_skipped: Wszystkie konta zostały pominięte. Możesz skonfigurować je później na stronie kont.
+ no_accounts: Brak kont dostępnych do konfiguracji.
+ success:
+ one: Pomyślnie utworzono %{count} konto!
+ few: Pomyślnie utworzono %{count} konta!
+ many: Pomyślnie utworzono %{count} kont!
+ other: Pomyślnie utworzono %{count} konta!
+ create:
+ success: Konfiguracja Enable Banking zakończona pomyślnie.
+ destroy:
+ success: Połączenie Enable Banking zostało dodane do kolejki usuwania.
+ link_accounts:
+ already_linked: Wybrane konta są już połączone.
+ link_failed: Nie udało się połączyć kont
+ no_accounts_selected: Nie wybrano żadnych kont.
+ no_session: Brak aktywnego połączenia Enable Banking. Najpierw połącz się z bankiem.
+ success:
+ one: Pomyślnie połączono %{count} konto.
+ few: Pomyślnie połączono %{count} konta.
+ many: Pomyślnie połączono %{count} kont.
+ other: Pomyślnie połączono %{count} konta.
+ link_existing_account:
+ success: Konto zostało pomyślnie połączone z Enable Banking
+ errors:
+ only_manual: Można połączyć tylko konta manualne
+ invalid_enable_banking_account: Wybrano nieprawidłowe konto Enable Banking
+ new:
+ link_enable_banking_title: Połącz Enable Banking
+ reauthorize:
+ invalid_redirect: Otrzymany adres URL autoryzacji jest nieprawidłowy. Spróbuj ponownie.
+ reauthorization_failed: Ponowna autoryzacja nie powiodła się
+ select_bank:
+ cancel: Anuluj
+ check_country: Sprawdź ustawienia kodu kraju.
+ credentials_required: Najpierw skonfiguruj dane uwierzytelniające Enable Banking.
+ description: Wybierz bank, który chcesz połączyć ze swoimi kontami.
+ no_banks: Brak dostępnych banków dla tego kraju/regionu.
+ title: Wybierz swój bank
+ update:
+ success: Konfiguracja Enable Banking została zaktualizowana.
diff --git a/config/locales/views/entries/de.yml b/config/locales/views/entries/de.yml
index 4b08c0138..ca6893024 100644
--- a/config/locales/views/entries/de.yml
+++ b/config/locales/views/entries/de.yml
@@ -12,3 +12,12 @@ de:
loading: Buchungen werden geladen...
update:
success: Buchung aktualisiert
+ unlock:
+ success: Buchung freigegeben. Sie kann beim nächsten Sync aktualisiert werden.
+ protection:
+ tooltip: Vor Sync geschützt
+ title: Vor Sync geschützt
+ description: Ihre Änderungen an dieser Buchung werden nicht durch den Provider-Sync überschrieben.
+ locked_fields_label: "Gesperrte Felder:"
+ unlock_button: Sync-Updates zulassen
+ unlock_confirm: Soll der Sync diese Buchung aktualisieren dürfen? Ihre Änderungen können beim nächsten Sync überschrieben werden.
diff --git a/config/locales/views/entries/es.yml b/config/locales/views/entries/es.yml
index 54b20e21d..e8dee8098 100644
--- a/config/locales/views/entries/es.yml
+++ b/config/locales/views/entries/es.yml
@@ -12,3 +12,12 @@ es:
loading: Cargando entradas...
update:
success: Entrada actualizada
+ unlock:
+ success: Entrada desbloqueada. Podría actualizarse en la próxima sincronización.
+ protection:
+ tooltip: Protegido contra sincronización
+ title: Protegido contra sincronización
+ description: Los cambios que realices en esta entrada no serán sobrescritos por la sincronización del proveedor.
+ locked_fields_label: "Campos bloqueados:"
+ unlock_button: Permitir que la sincronización actualice
+ unlock_confirm: ¿Permitir que la sincronización actualice esta entrada? Tus cambios podrían sobrescribirse en la próxima sincronización.
diff --git a/config/locales/views/entries/pl.yml b/config/locales/views/entries/pl.yml
new file mode 100644
index 000000000..fc2f0a03b
--- /dev/null
+++ b/config/locales/views/entries/pl.yml
@@ -0,0 +1,23 @@
+---
+pl:
+ entries:
+ create:
+ success: Wpis został utworzony
+ destroy:
+ success: Wpis został usunięty
+ empty:
+ description: Spróbuj dodać wpis, zmienić filtry lub doprecyzować wyszukiwanie
+ title: Nie znaleziono wpisów
+ loading:
+ loading: Ładowanie wpisów...
+ update:
+ success: Wpis został zaktualizowany
+ unlock:
+ success: Wpis został odblokowany. Może zostać zaktualizowany podczas następnej synchronizacji.
+ protection:
+ tooltip: Chronione przed synchronizacją
+ title: Chronione przed synchronizacją
+ description: Twoje zmiany w tym wpisie nie zostaną nadpisane przez synchronizację z dostawcą.
+ locked_fields_label: "Zablokowane pola:"
+ unlock_button: Zezwól synchronizacji na aktualizację
+ unlock_confirm: Zezwolić synchronizacji na aktualizację tego wpisu? Twoje zmiany mogą zostać nadpisane przy następnej synchronizacji.
diff --git a/config/locales/views/entries/pt-BR.yml b/config/locales/views/entries/pt-BR.yml
index c753c1f47..a0884417f 100644
--- a/config/locales/views/entries/pt-BR.yml
+++ b/config/locales/views/entries/pt-BR.yml
@@ -12,3 +12,12 @@ pt-BR:
loading: Carregando entradas...
update:
success: Entrada atualizada
+ unlock:
+ success: Entrada desbloqueada. Ela poderá ser atualizada na próxima sincronização.
+ protection:
+ tooltip: Protegido contra sincronização
+ title: Protegido contra sincronização
+ description: As suas edições nesta entrada não serão sobrescritas pela sincronização do provedor.
+ locked_fields_label: "Campos bloqueados:"
+ unlock_button: Permitir sincronização para atualizar
+ unlock_confirm: Permitir que a sincronização atualize esta entrada? Suas alterações poderão ser sobrescritas na próxima sincronização.
diff --git a/config/locales/views/family_exports/pl.yml b/config/locales/views/family_exports/pl.yml
new file mode 100644
index 000000000..cdde24b00
--- /dev/null
+++ b/config/locales/views/family_exports/pl.yml
@@ -0,0 +1,31 @@
+---
+pl:
+ family_exports:
+ access_denied: Brak dostępu
+ create:
+ success: Eksport został rozpoczęty. Wkrótce będzie można go pobrać.
+ delete_confirmation: Czy na pewno chcesz usunąć ten eksport? Tej akcji nie można cofnąć.
+ delete_failed_confirmation: Czy na pewno chcesz usunąć ten nieudany eksport?
+ destroy:
+ success: Eksport został pomyślnie usunięty
+ export_not_ready: Eksport nie jest jeszcze gotowy do pobrania
+ exporting: Trwa eksportowanie...
+ index:
+ title: Eksporty
+ new: Nowy eksport
+ table:
+ title: Eksporty
+ header:
+ date: Data
+ filename: Nazwa pliku
+ status: Status
+ actions: Akcje
+ row:
+ status:
+ in_progress: W trakcie
+ complete: Zakończony
+ failed: Nieudany
+ actions:
+ delete: Usuń
+ download: Pobierz
+ empty: Brak eksportów.
diff --git a/config/locales/views/holdings/de.yml b/config/locales/views/holdings/de.yml
index 0d5bb4219..3954a23ad 100644
--- a/config/locales/views/holdings/de.yml
+++ b/config/locales/views/holdings/de.yml
@@ -5,9 +5,37 @@ de:
brokerage_cash: Depotguthaben
destroy:
success: Position gelöscht
+ update:
+ success: Einstandspreis gespeichert.
+ error: Ungültiger Einstandspreis.
+ unlock_cost_basis:
+ success: Einstandspreis freigegeben. Er kann beim nächsten Sync aktualisiert werden.
+ remap_security:
+ success: Wertpapier erfolgreich aktualisiert.
+ security_not_found: Das ausgewählte Wertpapier konnte nicht gefunden werden.
+ reset_security:
+ success: Wertpapier auf Provider-Wert zurückgesetzt.
+ errors:
+ security_collision: "Umbildung nicht möglich: Sie haben bereits eine Position für %{ticker} am %{date}."
+ cost_basis_sources:
+ manual: Vom Benutzer gesetzt
+ calculated: Aus Trades
+ provider: Vom Provider
+ cost_basis_cell:
+ unknown: "--"
+ set_cost_basis_header: "Einstandspreis für %{ticker} setzen (%{qty} Anteile)"
+ total_cost_basis_label: Gesamter Einstandspreis
+ or_per_share_label: "Oder pro Anteil eingeben:"
+ per_share: pro Anteil
+ cancel: Abbrechen
+ save: Speichern
+ overwrite_confirm_title: Einstandspreis überschreiben?
+ overwrite_confirm_body: "Dies ersetzt den aktuellen Einstandspreis von %{current}."
holding:
per_share: pro Anteil
shares: "%{qty} Anteile"
+ unknown: "--"
+ no_cost_basis: Kein Einstandspreis
index:
average_cost: Durchschnittlicher Einstandspreis
holdings: Positionen
@@ -23,13 +51,35 @@ de:
avg_cost_label: Durchschnittlicher Einstandspreis
current_market_price_label: Aktueller Marktpreis
delete: Löschen
- delete_subtitle: Dadurch wird die Position und alle zugehörigen Trades auf diesem Konto gelöscht Diese Aktion kann nicht rückgängig gemacht werden
+ delete_subtitle: Dadurch wird die Position und alle zugehörigen Trades auf diesem Konto gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.
delete_title: Position löschen
+ edit_security: Wertpapier bearbeiten
history: Verlauf
+ no_trade_history: Für diese Position ist keine Trade-Historie verfügbar.
overview: Übersicht
portfolio_weight_label: Portfolio-Gewichtung
settings: Einstellungen
+ security_label: Wertpapier
+ originally: "war %{ticker}"
+ search_security: Wertpapier suchen
+ search_security_placeholder: Nach Ticker oder Name suchen
+ cancel: Abbrechen
+ remap_security: Speichern
+ no_security_provider: Wertpapier-Provider nicht konfiguriert. Suche nach Wertpapieren nicht möglich.
+ security_remapped_label: Wertpapier umgebildet
+ provider_sent: "Provider: %{ticker}"
+ reset_to_provider: Auf Provider zurücksetzen
+ reset_confirm_title: Wertpapier auf Provider zurücksetzen?
+ reset_confirm_body: "Das Wertpapier wird von %{current} zurück auf %{original} geändert; alle zugehörigen Trades werden verschoben."
ticker_label: Ticker
trade_history_entry: "%{qty} Anteile von %{security} zu %{price}"
total_return_label: Gesamtrendite
+ shares_label: Anteile
+ book_value_label: Buchwert
+ market_value_label: Marktwert
unknown: Unbekannt
+ cost_basis_locked_label: Einstandspreis ist gesperrt
+ cost_basis_locked_description: Ihr manuell gesetzter Einstandspreis wird durch Syncs nicht geändert.
+ unlock_cost_basis: Freigeben
+ unlock_confirm_title: Einstandspreis freigeben?
+ unlock_confirm_body: Der Einstandspreis kann dann durch Provider-Syncs oder Trade-Berechnungen aktualisiert werden.
diff --git a/config/locales/views/holdings/en.yml b/config/locales/views/holdings/en.yml
index a14efe9b6..4fe40c9e6 100644
--- a/config/locales/views/holdings/en.yml
+++ b/config/locales/views/holdings/en.yml
@@ -15,6 +15,10 @@ en:
security_not_found: Could not find the selected security.
reset_security:
success: Security reset to provider value.
+ sync_prices:
+ success: Market data synced successfully.
+ unavailable: Market data sync is not available for offline securities.
+ provider_error: Could not fetch latest prices. Please try again in a few minutes.
errors:
security_collision: "Cannot remap: you already have a holding for %{ticker} on %{date}."
cost_basis_sources:
@@ -82,3 +86,11 @@ en:
unlock_cost_basis: Unlock
unlock_confirm_title: Unlock cost basis?
unlock_confirm_body: This will allow the cost basis to be updated by provider syncs or trade calculations.
+ shares_label: Shares
+ book_value_label: Book Value
+ market_value_label: Market Value
+ market_data_label: Market data
+ market_data_sync_button: Refresh
+ last_price_update: Last price update
+ syncing: Syncing...
+ never: Never
diff --git a/config/locales/views/holdings/es.yml b/config/locales/views/holdings/es.yml
index 0db0f3010..9ebbce072 100644
--- a/config/locales/views/holdings/es.yml
+++ b/config/locales/views/holdings/es.yml
@@ -5,9 +5,37 @@ es:
brokerage_cash: Efectivo en la cuenta de corretaje
destroy:
success: Posición eliminada
+ update:
+ success: Base de costes guardada.
+ error: Valor de base de costes no válido.
+ unlock_cost_basis:
+ success: Base de costes desbloqueada. Podría actualizarse en la próxima sincronización.
+ remap_security:
+ success: Valor actualizado correctamente.
+ security_not_found: No se ha podido encontrar el valor seleccionado.
+ reset_security:
+ success: Valor restablecido al valor del proveedor.
+ errors:
+ security_collision: "No se puede reasignar: ya tienes una posición para %{ticker} en la fecha %{date}."
+ cost_basis_sources:
+ manual: Configurado por el usuario
+ calculated: Desde operaciones
+ provider: Desde el proveedor
+ cost_basis_cell:
+ unknown: "--"
+ set_cost_basis_header: "Establecer base de costes para %{ticker} (%{qty} acciones)"
+ total_cost_basis_label: Base de costes total
+ or_per_share_label: "O introduce por acción:"
+ per_share: por acción
+ cancel: Cancelar
+ save: Guardar
+ overwrite_confirm_title: ¿Sobrescribir base de costes?
+ overwrite_confirm_body: "Esto reemplazará la base de costes actual de %{current}."
holding:
per_share: por acción
shares: "%{qty} acciones"
+ unknown: "--"
+ no_cost_basis: Sin base de costes
index:
average_cost: Costo promedio
holdings: Posiciones
@@ -17,21 +45,50 @@ es:
return: Rendimiento total
weight: Peso
missing_price_tooltip:
- description: Esta inversión tiene valores faltantes y no pudimos calcular
- su rendimiento o valor.
+ description: Esta inversión tiene valores faltantes y no pudimos calcular su rendimiento o valor.
missing_data: Datos faltantes
+ sync_prices:
+ success: Datos de mercado sincronizados correctamente.
+ unavailable: La sincronización de datos de mercado no está disponible para valores fuera de línea.
+ provider_error: No se pudieron obtener los precios más recientes. Inténtalo de nuevo en unos minutos.
show:
avg_cost_label: Costo promedio
current_market_price_label: Precio de mercado actual
delete: Eliminar
- delete_subtitle: Esto eliminará la posición y todas tus operaciones asociadas
- en esta cuenta. Esta acción no se puede deshacer.
+ delete_subtitle: Esto eliminará la posición y todas tus operaciones asociadas en esta cuenta. Esta acción no se puede deshacer.
delete_title: Eliminar posición
+ edit_security: Editar valor
history: Historial
+ no_trade_history: No hay historial de operaciones disponible para esta posición.
overview: Resumen
portfolio_weight_label: Peso en el portafolio
settings: Configuración
+ security_label: Valor
+ originally: "era %{ticker}"
+ search_security: Buscar valor
+ search_security_placeholder: Buscar por ticker o nombre
+ cancel: Cancelar
+ remap_security: Guardar
+ no_security_provider: Proveedor de valores no configurado. No se pueden buscar valores.
+ security_remapped_label: Valor reasignado
+ provider_sent: "El proveedor envió: %{ticker}"
+ reset_to_provider: Restablecer al proveedor
+ reset_confirm_title: ¿Restablecer valor al del proveedor?
+ reset_confirm_body: "Esto cambiará el valor de %{current} de nuevo a %{original} y moverá todas las operaciones asociadas."
ticker_label: Ticker
trade_history_entry: "%{qty} acciones de %{security} a %{price}"
total_return_label: Rendimiento total
+ shares_label: Acciones
+ book_value_label: Valor en libros
+ market_value_label: Valor de mercado
+ market_data_label: Datos de mercado
+ market_data_sync_button: Actualizar
+ last_price_update: Última actualización de precio
+ syncing: Sincronizando...
+ never: Nunca
unknown: Desconocido
+ cost_basis_locked_label: La base de costes está bloqueada
+ cost_basis_locked_description: La base de costes establecida manualmente no cambiará con las sincronizaciones.
+ unlock_cost_basis: Desbloquear
+ unlock_confirm_title: ¿Desbloquear base de costes?
+ unlock_confirm_body: Esto permitirá que la base de costes se actualice mediante las sincronizaciones del proveedor o los cálculos de operaciones.
\ No newline at end of file
diff --git a/config/locales/views/holdings/fr.yml b/config/locales/views/holdings/fr.yml
index 09c96ef83..2959132a1 100644
--- a/config/locales/views/holdings/fr.yml
+++ b/config/locales/views/holdings/fr.yml
@@ -33,4 +33,7 @@ fr:
ticker_label: Ticker
trade_history_entry: "%{qty} actions de %{security} à %{price}"
total_return_label: Rendement total
+ shares_label: Actions
+ book_value_label: Valeur comptable
+ market_value_label: Valeur marchande
unknown: Inconnu
diff --git a/config/locales/views/holdings/pl.yml b/config/locales/views/holdings/pl.yml
new file mode 100644
index 000000000..c9e364c13
--- /dev/null
+++ b/config/locales/views/holdings/pl.yml
@@ -0,0 +1,94 @@
+---
+pl:
+ holdings:
+ cash:
+ brokerage_cash: Gotówka maklerska
+ destroy:
+ success: Pozycja została usunięta
+ update:
+ success: Koszt bazowy został zapisany.
+ error: Nieprawidłowa wartość kosztu bazowego.
+ unlock_cost_basis:
+ success: Odblokowano koszt bazowy. Może zostać zaktualizowany przy następnej synchronizacji.
+ remap_security:
+ success: Papier wartościowy został pomyślnie zaktualizowany.
+ security_not_found: Nie udało się znaleźć wybranego papieru wartościowego.
+ reset_security:
+ success: Zresetowano papier wartościowy do wartości od dostawcy.
+ sync_prices:
+ success: Dane rynkowe zostały pomyślnie zsynchronizowane.
+ unavailable: Synchronizacja danych rynkowych nie jest dostępna dla papierów offline.
+ provider_error: Nie udało się pobrać najnowszych cen. Spróbuj ponownie za kilka minut.
+ errors:
+ security_collision: 'Nie można zmienić mapowania: masz już pozycję dla %{ticker} z dnia %{date}.'
+ cost_basis_sources:
+ manual: Ustawione przez użytkownika
+ calculated: Z transakcji giełdowych
+ provider: Od dostawcy
+ cost_basis_cell:
+ unknown: "--"
+ set_cost_basis_header: Ustaw koszt bazowy dla %{ticker} (%{qty} akcji)
+ total_cost_basis_label: Łączny koszt bazowy
+ or_per_share_label: 'Lub wpisz na akcję:'
+ per_share: na akcję
+ cancel: Anuluj
+ save: Zapisz
+ overwrite_confirm_title: Nadpisać koszt bazowy?
+ overwrite_confirm_body: Spowoduje to zastąpienie aktualnego kosztu bazowego wynoszącego %{current}.
+ holding:
+ per_share: na akcję
+ shares: "%{qty} akcji"
+ unknown: "--"
+ no_cost_basis: Brak kosztu bazowego
+ index:
+ average_cost: Średni koszt
+ holdings: Pozycje
+ name: Nazwa
+ new_holding: Nowa aktywność
+ no_holdings: Brak pozycji do wyświetlenia.
+ return: Łączny zwrot
+ weight: Udział
+ missing_price_tooltip:
+ description: Ta inwestycja ma brakujące wartości i nie mogliśmy obliczyć jej zwrotów ani wartości.
+ missing_data: Brakujące dane
+ show:
+ avg_cost_label: Średni koszt
+ current_market_price_label: Aktualna cena rynkowa
+ delete: Usuń
+ delete_subtitle: Spowoduje to usunięcie pozycji i wszystkich powiązanych transakcji na tym koncie. Tej akcji nie można cofnąć.
+ delete_title: Usuń pozycję
+ edit_security: Edytuj papier wartościowy
+ history: Historia
+ no_trade_history: Brak historii transakcji dla tej pozycji.
+ overview: Przegląd
+ portfolio_weight_label: Udział w portfelu
+ settings: Ustawienia
+ security_label: Papier wartościowy
+ originally: wcześniej %{ticker}
+ search_security: Wyszukaj papier wartościowy
+ search_security_placeholder: Szukaj po tickerze lub nazwie
+ cancel: Anuluj
+ remap_security: Zapisz
+ no_security_provider: Dostawca papierów wartościowych nie jest skonfigurowany. Nie można wyszukiwać papierów wartościowych.
+ security_remapped_label: Papier wartościowy zmieniony
+ provider_sent: 'Dostawca przesłał: %{ticker}'
+ reset_to_provider: Przywróć wartość od dostawcy
+ reset_confirm_title: Przywrócić papier wartościowy do wartości od dostawcy?
+ reset_confirm_body: Spowoduje to zmianę papieru wartościowego z %{current} z powrotem na %{original} i przeniesienie wszystkich powiązanych transakcji.
+ ticker_label: Symbol ticker
+ trade_history_entry: "%{qty} akcji %{security} po %{price}"
+ total_return_label: Łączny zwrot
+ unknown: Nieznane
+ cost_basis_locked_label: Podstawa kosztowa jest zablokowana
+ cost_basis_locked_description: Ręcznie ustawiona podstawa kosztowa nie będzie zmieniana przez synchronizacje.
+ unlock_cost_basis: Odblokuj
+ unlock_confirm_title: Odblokować podstawę kosztową?
+ unlock_confirm_body: To pozwoli aktualizować podstawę kosztową przez synchronizacje dostawcy lub obliczenia transakcji.
+ shares_label: Akcje
+ book_value_label: Wartość księgowa
+ market_value_label: Wartość rynkowa
+ market_data_label: Dane rynkowe
+ market_data_sync_button: Odśwież
+ last_price_update: Ostatnia aktualizacja ceny
+ syncing: Synchronizacja...
+ never: Nigdy
diff --git a/config/locales/views/impersonation_sessions/pl.yml b/config/locales/views/impersonation_sessions/pl.yml
new file mode 100644
index 000000000..3bc49a92b
--- /dev/null
+++ b/config/locales/views/impersonation_sessions/pl.yml
@@ -0,0 +1,15 @@
+---
+pl:
+ impersonation_sessions:
+ approve:
+ success: Wniosek został zatwierdzony
+ complete:
+ success: Sesja została zakończona
+ create:
+ success: Wniosek został wysłany do użytkownika. Oczekiwanie na zatwierdzenie.
+ join:
+ success: Dołączono do sesji
+ leave:
+ success: Opuszczono sesję
+ reject:
+ success: Wniosek został odrzucony
diff --git a/config/locales/views/imports/ca.yml b/config/locales/views/imports/ca.yml
index b6f188dd7..8de803977 100644
--- a/config/locales/views/imports/ca.yml
+++ b/config/locales/views/imports/ca.yml
@@ -110,3 +110,8 @@ ca:
description: Aquí tens un resum dels nous elements que s'afegiran al teu compte
un cop publiquis aquesta importació.
title: Confirma les teves dades d'importació
+ summary_item_label: Element
+ summary_count_label: Quantitat
+ empty_summary: No s'han trobat registres importables en aquest fitxer. Pot estar buit, o les línies no coincideixen amb el format d'exportació esperat (cada línia ha de ser un objecte JSON amb les claus «type» i «data», amb tipus admesos per aquesta importació).
+ publish_import: Publicar la importació
+ back_to_imports: Tornar a les importacions
diff --git a/config/locales/views/imports/de.yml b/config/locales/views/imports/de.yml
index ce94b1a0b..faab67cc0 100644
--- a/config/locales/views/imports/de.yml
+++ b/config/locales/views/imports/de.yml
@@ -1,6 +1,30 @@
---
de:
import:
+ qif_category_selections:
+ show:
+ title: "Konfigurieren und auswählen"
+ description: "Überprüfe das erkannte Datumsformat und wähle dann die Kategorien und Tags aus deiner QIF-Datei aus, die in %{product_name} importiert werden sollen."
+ categories_heading: Kategorien
+ categories_found:
+ one: "1 Kategorie gefunden"
+ other: "%{count} Kategorien gefunden"
+ category_name_col: Kategoriename
+ transactions_col: Buchungen
+ tags_heading: Tags
+ tags_found:
+ one: "1 Tag gefunden"
+ other: "%{count} Tags gefunden"
+ tag_name_col: Tag-Name
+ txn_count:
+ one: "1 Buchung"
+ other: "%{count} Buchungen"
+ empty_state_primary: In dieser QIF-Datei wurden keine Kategorien oder Tags gefunden.
+ empty_state_secondary: Alle Transaktionen werden ohne Kategorien und Tags importiert.
+ submit: Weiter zur Überprüfung
+ split_warning_title: Aufgeteilte Buchungen erkannt
+ split_warning_description: "Diese QIF-Datei enthält aufgeteilte Buchungen. Aufgeteilte Buchungen werden noch nicht unterstützt – jede aufgeteilte Buchung wird als einzelne Buchung mit ihrem Gesamtbetrag und ohne Kategorie importiert. Die einzelnen Aufteilungsdetails werden nicht übernommen."
+ split_badge: aufgeteilt
cleans:
show:
description: Bearbeite deine Daten in der Tabelle unten. Rote Zellen sind ungültig.
@@ -8,8 +32,18 @@ de:
errors_notice_mobile: Deine Daten enthalten Fehler. Tippe auf den Fehler-Tooltip, um Details zu sehen.
title: Daten bereinigen
configurations:
+ update:
+ success: Import wurde erfolgreich konfiguriert.
+ category_import:
+ button_label: Weiter
+ description: Lade eine einfache CSV-Datei hoch (z. B. wie bei einem Export). Die Spalten werden automatisch zugeordnet.
+ instructions: Wähle Weiter, um die CSV zu parsen und zum Bereinigungsschritt zu gelangen.
mint_import:
date_format_label: Datumsformat
+ rule_import:
+ description: Konfigurieren Sie den Regel-Import. Regeln werden basierend auf den CSV-Daten erstellt oder aktualisiert.
+ process_button: Regeln verarbeiten
+ process_help: Klicken Sie unten, um Ihre CSV zu verarbeiten und Regelzeilen zu erzeugen.
show:
description: Wähle die Spalten aus, die den jeweiligen Feldern in deiner CSV entsprechen.
title: Import konfigurieren
@@ -17,6 +51,7 @@ de:
date_format_label: Datumsformat
transaction_import:
date_format_label: Datumsformat
+ rows_to_skip_label: Erste n Zeilen überspringen
confirms:
mappings:
create_account: Konto erstellen
@@ -36,6 +71,15 @@ de:
tag_mapping_title: Tags zuweisen
uploads:
show:
+ qif_title: QIF-Datei hochladen
+ qif_description: Wähle das Konto, zu dem diese QIF-Datei gehört, und lade deinen .qif-Export aus Quicken hoch.
+ qif_account_label: Konto
+ qif_account_placeholder: Konto auswählen…
+ qif_file_prompt: um deine QIF-Datei hier hinzuzufügen
+ qif_file_hint: Nur .qif-Dateien
+ qif_submit: QIF hochladen
+ browse: Durchsuchen
+ csv_file_prompt: um deine CSV-Datei hier hinzuzufügen
description: Füge unten deine CSV-Datei ein oder lade sie hoch. Bitte lies die Anweisungen in der Tabelle unten, bevor du beginnst.
instructions_1: Unten siehst du ein Beispiel einer CSV-Datei mit verfügbaren Spalten für den Import.
instructions_2: Deine CSV muss eine Kopfzeile enthalten.
@@ -44,6 +88,19 @@ de:
instructions_5: Keine Kommas, Währungssymbole oder Klammern in Zahlen verwenden.
title: Daten importieren
imports:
+ date_format:
+ heading: Datumsformat
+ description: "Das Datumsformat wurde automatisch aus deiner Datei erkannt. Ändere es, wenn die Datumsangaben falsch aussehen."
+ preview: "Erstes erkanntes Datum"
+ error_title: "Datumsformat konnte nicht erkannt werden"
+ error_description: "Keines der unterstützten Datumsformate konnte die Datumsangaben in dieser Datei lesen. Bitte überprüfe, ob die Datei gültige Datumseinträge enthält."
+ steps:
+ upload: Hochladen
+ configure: Konfigurieren
+ clean: Bereinigen
+ map: Zuordnen
+ confirm: Bestätigen
+ select: Auswählen
index:
title: Importe
new: Neuer Import
@@ -71,12 +128,67 @@ de:
new:
description: Du kannst verschiedene Datentypen manuell über CSV importieren oder eine unserer Importvorlagen wie Mint verwenden.
import_accounts: Konten importieren
+ import_categories: Kategorien importieren
+ import_file: Dokument importieren
+ import_file_description: KI-gestützte Analyse für PDFs und durchsuchbarer Upload für weitere Formate
import_mint: Von Mint importieren
import_portfolio: Investitionen importieren
+ import_rules: Regeln importieren
import_transactions: Transaktionen importieren
+ import_qif: Von Quicken importieren (QIF)
+ requires_account: Importiere zuerst Konten, um diese Option zu nutzen.
resume: "%{type} fortsetzen"
sources: Quellen
title: Neuer CSV-Import
+ create:
+ file_too_large: Datei ist zu groß. Maximale Größe %{max_size} MB.
+ invalid_file_type: Ungültiger Dateityp. Bitte laden Sie eine CSV-Datei hoch.
+ csv_uploaded: CSV wurde erfolgreich hochgeladen.
+ pdf_too_large: PDF ist zu groß. Maximale Größe %{max_size} MB.
+ pdf_processing: Ihre PDF wird verarbeitet. Sie erhalten eine E-Mail, wenn die Analyse abgeschlossen ist.
+ invalid_pdf: Die hochgeladene Datei ist keine gültige PDF.
+ document_too_large: Dokument ist zu groß. Maximale Größe %{max_size} MB.
+ invalid_document_file_type: Ungültiger Dokumenttyp für den aktiven Vektorspeicher.
+ document_uploaded: Dokument wurde erfolgreich hochgeladen.
+ document_upload_failed: Das Dokument konnte nicht in den Vektorspeicher hochgeladen werden. Bitte versuchen Sie es erneut.
+ document_provider_not_configured: Kein Vektorspeicher für Dokument-Uploads konfiguriert.
+ show:
+ finalize_upload: Bitte schließen Sie den Datei-Upload ab.
+ finalize_mappings: Bitte schließen Sie die Zuordnungen ab, bevor Sie fortfahren.
+ errors:
+ custom_column_requires_inflow: "Bei benutzerdefinierten Spalten muss eine Einnahmen-Spalte ausgewählt werden."
+ document_types:
+ bank_statement: Kontoauszug
+ credit_card_statement: Kreditkartenabrechnung
+ investment_statement: Wertpapierabrechnung
+ financial_document: Finanzdokument
+ contract: Vertrag
+ other: Sonstiges Dokument
+ unknown: Unbekanntes Dokument
+ pdf_import:
+ processing_title: Ihre PDF wird verarbeitet
+ processing_description: Wir analysieren Ihr Dokument mit KI. Das kann einen Moment dauern. Sie erhalten eine E-Mail, wenn die Analyse abgeschlossen ist.
+ check_status: Status prüfen
+ back_to_dashboard: Zurück zur Übersicht
+ failed_title: Verarbeitung fehlgeschlagen
+ failed_description: Ihre PDF konnte nicht verarbeitet werden. Bitte versuchen Sie es erneut oder kontaktieren Sie den Support.
+ try_again: Erneut versuchen
+ delete_import: Import löschen
+ complete_title: Dokument analysiert
+ complete_description: Wir haben Ihre PDF analysiert – hier ist das Ergebnis.
+ document_type_label: Dokumenttyp
+ summary_label: Zusammenfassung
+ email_sent_notice: Sie haben eine E-Mail mit den nächsten Schritten erhalten.
+ back_to_imports: Zurück zu Importen
+ unknown_state_title: Unbekannter Status
+ unknown_state_description: Dieser Import befindet sich in einem unerwarteten Zustand. Bitte kehren Sie zu den Importen zurück.
+ processing_failed_with_message: "%{message}"
+ processing_failed_generic: "Verarbeitung fehlgeschlagen: %{error}"
ready:
description: Hier ist eine Zusammenfassung der neuen Elemente, die deinem Konto hinzugefügt werden, sobald du diesen Import veröffentlichst.
title: Importdaten bestätigen
+ summary_item_label: Eintrag
+ summary_count_label: Anzahl
+ empty_summary: In dieser Datei wurden keine importierbaren Datensätze gefunden. Sie ist möglicherweise leer, oder die Zeilen entsprechen nicht dem erwarteten Exportformat (jede Zeile sollte ein JSON-Objekt mit den Schlüsseln „type“ und „data“ und unterstützten Typen sein).
+ publish_import: Import veröffentlichen
+ back_to_imports: Zurück zu Importen
diff --git a/config/locales/views/imports/en.yml b/config/locales/views/imports/en.yml
index 6be06ff26..9f41ab712 100644
--- a/config/locales/views/imports/en.yml
+++ b/config/locales/views/imports/en.yml
@@ -1,6 +1,30 @@
---
en:
import:
+ qif_category_selections:
+ show:
+ title: "Configure & select"
+ description: "Review the detected date format, then choose which categories and tags from your QIF file to bring into %{product_name}."
+ categories_heading: Categories
+ categories_found:
+ one: "1 category found"
+ other: "%{count} categories found"
+ category_name_col: Category name
+ transactions_col: Transactions
+ tags_heading: Tags
+ tags_found:
+ one: "1 tag found"
+ other: "%{count} tags found"
+ tag_name_col: Tag name
+ txn_count:
+ one: "1 txn"
+ other: "%{count} txns"
+ split_warning_title: Split transactions detected
+ split_warning_description: "This QIF file contains split transactions. Splits are not yet supported, so each split transaction will be imported as a single transaction with its full amount and no category. The individual split breakdowns will not be preserved."
+ split_badge: split
+ empty_state_primary: No categories or tags were found in this QIF file.
+ empty_state_secondary: All transactions will be imported without categories or tags.
+ submit: Continue to review
cleans:
show:
description: Edit your data in the table below. Red cells are invalid.
@@ -33,6 +57,13 @@ en:
date_format_label: Date format
rows_to_skip_label: Skip first n rows
confirms:
+ sure_import:
+ title: Confirm your import
+ description: Review the data that will be imported from your export file.
+ summary: Import summary
+ empty_summary: We could not find any importable records in this file. It may be empty, or the lines may not match the expected export format (each line should be a JSON object with "type" and "data" keys, using types this import supports).
+ publish_button: Start import
+ cancel: Cancel
mappings:
create_account: Create account
csv_mapping_label: "%{mapping} in CSV"
@@ -59,6 +90,15 @@ en:
tag_mapping_title: Assign your tags
uploads:
show:
+ qif_title: Upload QIF file
+ qif_description: Select the account this QIF file belongs to, then upload your .qif export from Quicken.
+ qif_account_label: Account
+ qif_account_placeholder: Select an account…
+ qif_file_prompt: to add your QIF file here
+ qif_file_hint: .qif files only
+ qif_submit: Upload QIF
+ browse: Browse
+ csv_file_prompt: to add your CSV file here
description: Paste or upload your CSV file below. Please review the instructions
in the table below before beginning.
instructions_1: Below is an example CSV with columns available for import.
@@ -68,7 +108,41 @@ en:
instructions_4: Columns marked with an asterisk (*) are required data.
instructions_5: No commas, no currency symbols, and no parentheses in numbers.
title: Import your data
+ sure_import:
+ title: Import from export
+ description: Upload the all.ndjson file from your data export to restore your accounts, transactions, categories, and more.
+ drop_title: Drop NDJSON to upload
+ drop_subtitle: Your file will be uploaded automatically
+ browse: Browse
+ browse_hint: to add your all.ndjson file here
+ upload_button: Upload NDJSON
+ hint_html: Upload the all.ndjson file from your data export ZIP
+ ndjson_invalid: Must be valid NDJSON with at least one record
imports:
+ date_format:
+ heading: Date format
+ description: "The date format was auto-detected from your file. Change it if dates look incorrect."
+ preview: "First parsed date"
+ error_title: "Unable to detect date format"
+ error_description: "None of the supported date formats could parse the dates in this file. Please check that the file contains valid date entries."
+ type_labels:
+ transaction_import: "Transaction import"
+ trade_import: "Trade import"
+ account_import: "Account import"
+ mint_import: "Mint import"
+ qif_import: "QIF import"
+ category_import: "Category import"
+ rule_import: "Rule import"
+ pdf_import: "PDF import"
+ document_import: "Document import"
+ sure_import: "Sure import"
+ steps:
+ upload: Upload
+ configure: Configure
+ clean: Clean
+ map: Map
+ confirm: Confirm
+ select: Select
index:
title: Imports
new: New Import
@@ -80,6 +154,17 @@ en:
status: Status
actions: Actions
row:
+ type_labels:
+ transaction_import: "Transaction"
+ trade_import: "Trade"
+ account_import: "Account"
+ mint_import: "Mint"
+ qif_import: "QIF"
+ category_import: "Category"
+ rule_import: "Rule"
+ pdf_import: "PDF"
+ document_import: "Document"
+ sure_import: "Sure"
status:
in_progress: In progress
uploading: Processing rows
@@ -94,16 +179,22 @@ en:
view: View
empty: No imports yet.
new:
- description: You can manually import various types of data via CSV or use one
- of our import templates like Mint.
+ description: Import from a financial tool or upload raw data files.
+ tab_financial_tools: Financial Tools & Files
+ tab_raw_data: Raw Data
+ coming_soon: Coming soon
+ import_ynab: Import from YNAB
import_accounts: Import accounts
import_categories: Import categories
import_mint: Import from Mint
import_portfolio: Import investments
import_rules: Import rules
import_transactions: Import transactions
+ import_qif: Import from Quicken (QIF)
+ import_sure: Import from Sure
+ import_sure_description: Full-export .ndjson file
import_file: Import document
- import_file_description: AI-powered analysis for PDFs and searchable upload for other supported files
+ import_file_description: AI-powered analysis for PDFs and searchable file upload
requires_account: Import accounts first to unlock this option.
resume: Resume %{type}
sources: Sources
@@ -112,6 +203,7 @@ en:
file_too_large: File is too large. Maximum size is %{max_size}MB.
invalid_file_type: Invalid file type. Please upload a CSV file.
csv_uploaded: CSV uploaded successfully.
+ ndjson_uploaded: NDJSON file uploaded successfully.
pdf_too_large: PDF file is too large. Maximum size is %{max_size}MB.
pdf_processing: Your PDF is being processed. You will receive an email when analysis is complete.
invalid_pdf: The uploaded file is not a valid PDF.
@@ -119,6 +211,8 @@ en:
invalid_document_file_type: Invalid document file type for the active vector store.
document_uploaded: Document uploaded successfully.
document_upload_failed: We couldn't upload the document to the vector store. Please try again.
+ invalid_ndjson_file_type: Invalid file type or format. Please upload a valid .ndjson or .json export file.
+ ndjson_uploaded: NDJSON file uploaded successfully.
document_provider_not_configured: No vector store is configured for document uploads.
show:
finalize_upload: Please finalize your file upload.
@@ -127,6 +221,11 @@ en:
description: Here's a summary of the new items that will be added to your account
once you publish this import.
title: Confirm your import data
+ summary_item_label: Item
+ summary_count_label: Count
+ empty_summary: We could not find any importable records in this file. It may be empty, or the lines may not match the expected export format (each line should be a JSON object with "type" and "data" keys, using types this import supports).
+ publish_import: Publish import
+ back_to_imports: Back to imports
errors:
custom_column_requires_inflow: "Custom column imports require an inflow column to be selected"
document_types:
diff --git a/config/locales/views/imports/es.yml b/config/locales/views/imports/es.yml
index 8674ebcae..b8f0c9ca5 100644
--- a/config/locales/views/imports/es.yml
+++ b/config/locales/views/imports/es.yml
@@ -1,6 +1,30 @@
---
es:
import:
+ qif_category_selections:
+ show:
+ title: "Configurar y seleccionar"
+ description: "Revisa el formato de fecha detectado y luego elige qué categorías y etiquetas de tu archivo QIF importar en %{product_name}."
+ categories_heading: Categorías
+ categories_found:
+ one: "1 categoría encontrada"
+ other: "%{count} categorías encontradas"
+ category_name_col: Nombre de categoría
+ transactions_col: Transacciones
+ tags_heading: Etiquetas
+ tags_found:
+ one: "1 etiqueta encontrada"
+ other: "%{count} etiquetas encontradas"
+ tag_name_col: Nombre de etiqueta
+ txn_count:
+ one: "1 transacción"
+ other: "%{count} transacciones"
+ empty_state_primary: No se encontraron categorías ni etiquetas en este archivo QIF.
+ empty_state_secondary: Todas las transacciones se importarán sin categorías ni etiquetas.
+ submit: Continuar a la revisión
+ split_warning_title: Transacciones divididas detectadas
+ split_warning_description: "Este archivo QIF contiene transacciones divididas. Las divisiones aún no son compatibles, por lo que cada transacción dividida se importará como una única transacción con su importe total y sin categoría. Los desgloses individuales de las divisiones no se conservarán."
+ split_badge: dividida
cleans:
show:
description: Edita tus datos en la tabla de abajo. Las celdas rojas son inválidas.
@@ -8,8 +32,18 @@ es:
errors_notice_mobile: Tienes errores en tus datos. Toca el tooltip del error para ver los detalles.
title: Limpia tus datos
configurations:
+ update:
+ success: Importación configurada correctamente.
+ category_import:
+ button_label: Continuar
+ description: Sube un archivo CSV sencillo (como el que generamos al exportar tus datos). Mapearemos las columnas automáticamente.
+ instructions: Selecciona continuar para analizar tu CSV y pasar al paso de limpieza.
mint_import:
date_format_label: Formato de fecha
+ rule_import:
+ description: Configura tu importación de reglas. Las reglas se crearán o actualizarán basándose en los datos del CSV.
+ process_button: Procesar reglas
+ process_help: Haz clic en el botón de abajo para procesar tu CSV y generar las filas de reglas.
show:
description: Selecciona las columnas que corresponden a cada campo en tu CSV.
title: Configura tu importación
@@ -17,29 +51,35 @@ es:
date_format_label: Formato de fecha
transaction_import:
date_format_label: Formato de fecha
+ rows_to_skip_label: Omitir las primeras n filas
confirms:
mappings:
create_account: Crear cuenta
csv_mapping_label: "%{mapping} en CSV"
sure_mapping_label: "%{mapping} en %{product_name}"
- no_accounts: Aún no tienes cuentas. Por favor, crea una cuenta que podamos usar para las filas
- (sin asignar) en tu CSV o vuelve al paso de Limpieza y proporciona un nombre de cuenta que podamos usar.
+ no_accounts: Aún no tienes cuentas. Por favor, crea una cuenta que podamos usar para las filas (sin asignar) en tu CSV o vuelve al paso de Limpieza y proporciona un nombre de cuenta que podamos usar.
rows_label: Filas
unassigned_account: ¿Necesitas crear una nueva cuenta para las filas sin asignar?
show:
- account_mapping_description: Asigna todas las cuentas de tu archivo importado a las cuentas existentes de Sure.
- También puedes añadir nuevas cuentas o dejarlas sin categorizar.
+ account_mapping_description: Asigna todas las cuentas de tu archivo importado a las cuentas existentes de Sure. También puedes añadir nuevas cuentas o dejarlas sin categorizar.
account_mapping_title: Asigna tus cuentas
account_type_mapping_description: Asigna todos los tipos de cuenta de tu archivo importado a los de Sure.
account_type_mapping_title: Asigna tus tipos de cuenta
- category_mapping_description: Asigna todas las categorías de tu archivo importado a las categorías existentes de Sure.
- También puedes añadir nuevas categorías o dejarlas sin categorizar.
+ category_mapping_description: Asigna todas las categorías de tu archivo importado a las categorías existentes de Sure. También puedes añadir nuevas categorías o dejarlas sin categorizar.
category_mapping_title: Asigna tus categorías
- tag_mapping_description: Asigna todas las etiquetas de tu archivo importado a las etiquetas existentes de Sure.
- También puedes añadir nuevas etiquetas o dejarlas sin categorizar.
+ tag_mapping_description: Asigna todas las etiquetas de tu archivo importado a las etiquetas existentes de Sure. También puedes añadir nuevas etiquetas o dejarlas sin categorizar.
tag_mapping_title: Asigna tus etiquetas
uploads:
show:
+ qif_title: Subir archivo QIF
+ qif_description: Selecciona la cuenta a la que pertenece este archivo QIF y sube tu exportación .qif desde Quicken.
+ qif_account_label: Cuenta
+ qif_account_placeholder: Seleccionar una cuenta…
+ qif_file_prompt: para añadir tu archivo QIF aquí
+ qif_file_hint: Solo archivos .qif
+ qif_submit: Subir QIF
+ browse: Examinar
+ csv_file_prompt: para añadir tu archivo CSV aquí
description: Pega o sube tu archivo CSV abajo. Por favor, revisa las instrucciones en la tabla de abajo antes de comenzar.
instructions_1: Abajo hay un ejemplo de CSV con columnas disponibles para importar.
instructions_2: Tu CSV debe tener una fila de encabezado.
@@ -48,14 +88,27 @@ es:
instructions_5: Sin comas, sin símbolos de moneda y sin paréntesis en los números.
title: Importa tus datos
imports:
+ date_format:
+ heading: Formato de fecha
+ description: "El formato de fecha se detectó automáticamente desde tu archivo. Cámbialo si las fechas parecen incorrectas."
+ preview: "Primera fecha analizada"
+ error_title: "No se puede detectar el formato de fecha"
+ error_description: "Ninguno de los formatos de fecha compatibles pudo analizar las fechas de este archivo. Por favor, comprueba que el archivo contiene entradas de fecha válidas."
+ steps:
+ upload: Subir
+ configure: Configurar
+ clean: Limpiar
+ map: Mapear
+ confirm: Confirmar
+ select: Seleccionar
index:
title: Importaciones
new: Nueva importación
table:
- title: Imports
+ title: Importaciones
header:
date: Fecha
- operation: Operation
+ operation: Operación
status: Estado
actions: Acciones
row:
@@ -68,19 +121,74 @@ es:
failed: Fallido
actions:
revert: Revertir
- confirm_revert: Esto eliminará las transacciones que se importaron, pero aún podrá revisar y volver a importar sus datos en cualquier momento.
+ confirm_revert: Esto eliminará las transacciones que se importaron, pero aún podrás revisar y volver a importar tus datos en cualquier momento.
delete: Eliminar
view: Ver
empty: Aún no hay importaciones.
new:
description: Puedes importar manualmente varios tipos de datos mediante CSV o usar una de nuestras plantillas de importación como Mint.
import_accounts: Importar cuentas
+ import_categories: Importar categorías
import_mint: Importar desde Mint
import_portfolio: Importar inversiones
+ import_rules: Importar reglas
import_transactions: Importar transacciones
+ import_qif: Importar desde Quicken (QIF)
+ import_file: Importar documento
+ import_file_description: Análisis potenciado por IA para PDFs y subida con búsqueda para otros archivos compatibles
+ requires_account: Importa cuentas primero para desbloquear esta opción.
resume: Reanudar %{type}
sources: Fuentes
- title: Nueva importación CSV
+ title: Nueva importación
+ create:
+ file_too_large: El archivo es demasiado grande. El tamaño máximo es %{max_size}MB.
+ invalid_file_type: Tipo de archivo no válido. Por favor, sube un archivo CSV.
+ csv_uploaded: CSV subido correctamente.
+ pdf_too_large: El archivo PDF es demasiado grande. El tamaño máximo es %{max_size}MB.
+ pdf_processing: Tu PDF se está procesando. Recibirás un correo electrónico cuando el análisis haya finalizado.
+ invalid_pdf: El archivo subido no es un PDF válido.
+ document_too_large: El documento es demasiado grande. El tamaño máximo es %{max_size}MB.
+ invalid_document_file_type: Tipo de archivo de documento no válido para el almacén de vectores activo.
+ document_uploaded: Documento subido correctamente.
+ document_upload_failed: No hemos podido subir el documento al almacén de vectores. Por favor, inténtalo de nuevo.
+ document_provider_not_configured: No hay ningún almacén de vectores configurado para la subida de documentos.
+ show:
+ finalize_upload: Por favor, finaliza la subida de tu archivo.
+ finalize_mappings: Por favor, finaliza tus mapeos antes de continuar.
ready:
description: Aquí tienes un resumen de los nuevos elementos que se añadirán a tu cuenta una vez publiques esta importación.
title: Confirma tus datos de importación
+ summary_item_label: Elemento
+ summary_count_label: Cantidad
+ empty_summary: No se han encontrado registros importables en este archivo. Puede estar vacío, o las líneas no coinciden con el formato de exportación esperado (cada línea debe ser un objeto JSON con las claves «type» y «data», usando tipos que admite esta importación).
+ publish_import: Publicar importación
+ back_to_imports: Volver a importaciones
+ errors:
+ custom_column_requires_inflow: "Las importaciones de columnas personalizadas requieren que se seleccione una columna de entrada de fondos (inflow)"
+ document_types:
+ bank_statement: Extracto bancario
+ credit_card_statement: Extracto de tarjeta de crédito
+ investment_statement: Extracto de inversiones
+ financial_document: Documento financiero
+ contract: Contrato
+ other: Otro documento
+ unknown: Documento desconocido
+ pdf_import:
+ processing_title: Procesando tu PDF
+ processing_description: Estamos analizando tu documento mediante IA. Esto puede tardar un momento. Recibirás un correo electrónico cuando el análisis finalice.
+ check_status: Comprobar estado
+ back_to_dashboard: Volver al panel
+ failed_title: Error en el procesamiento
+ failed_description: No hemos podido procesar tu documento PDF. Por favor, inténtalo de nuevo o contacta con soporte.
+ try_again: Reintentar
+ delete_import: Eliminar importación
+ complete_title: Documento analizado
+ complete_description: Hemos analizado tu PDF y esto es lo que hemos encontrado.
+ document_type_label: Tipo de documento
+ summary_label: Resumen
+ email_sent_notice: Se te ha enviado un correo electrónico con los siguientes pasos.
+ back_to_imports: Volver a importaciones
+ unknown_state_title: Estado desconocido
+ unknown_state_description: Esta importación se encuentra en un estado inesperado. Por favor, vuelve a importaciones.
+ processing_failed_with_message: "%{message}"
+ processing_failed_generic: "Error en el procesamiento: %{error}"
\ No newline at end of file
diff --git a/config/locales/views/imports/fr.yml b/config/locales/views/imports/fr.yml
index 386ea94d6..4a34d031f 100644
--- a/config/locales/views/imports/fr.yml
+++ b/config/locales/views/imports/fr.yml
@@ -1,6 +1,30 @@
---
fr:
import:
+ qif_category_selections:
+ show:
+ title: "Configurer et sélectionner"
+ description: "Vérifiez le format de date détecté, puis choisissez les catégories et étiquettes de votre fichier QIF à importer dans %{product_name}."
+ categories_heading: Catégories
+ categories_found:
+ one: "1 catégorie trouvée"
+ other: "%{count} catégories trouvées"
+ category_name_col: Nom de la catégorie
+ transactions_col: Transactions
+ tags_heading: Étiquettes
+ tags_found:
+ one: "1 étiquette trouvée"
+ other: "%{count} étiquettes trouvées"
+ tag_name_col: Nom de l'étiquette
+ txn_count:
+ one: "1 opération"
+ other: "%{count} opérations"
+ empty_state_primary: Aucune catégorie ou étiquette trouvée dans ce fichier QIF.
+ empty_state_secondary: Toutes les transactions seront importées sans catégories ni étiquettes.
+ submit: Continuer vers la revue
+ split_warning_title: Transactions scindées détectées
+ split_warning_description: "Ce fichier QIF contient des transactions scindées. Les transactions scindées ne sont pas encore prises en charge : chaque transaction scindée sera importée comme une transaction unique avec son montant total et sans catégorie. Les ventilations individuelles ne seront pas conservées."
+ split_badge: scindée
cleans:
show:
description: Modifiez vos données dans le tableau ci-dessous. Les cellules rouges sont invalides.
@@ -29,6 +53,13 @@ fr:
date_format_label: Format de date
rows_to_skip_label: Ignorer les n premières lignes
confirms:
+ sure_import:
+ title: Confirmer votre importation
+ description: Vérifiez les données qui seront importées depuis votre fichier d'export.
+ summary: Résumé de l'importation
+ empty_summary: Aucun enregistrement importable n'a été trouvé dans ce fichier. Il est peut-être vide, ou les lignes ne correspondent pas au format d'export attendu (chaque ligne doit être un objet JSON avec les clés « type » et « data », pour des types pris en charge par cet import).
+ publish_button: Démarrer l'importation
+ cancel: Annuler
mappings:
create_account: Créer un compte
csv_mapping_label: "%{mapping} dans le CSV"
@@ -47,6 +78,15 @@ fr:
tag_mapping_title: Attribuez vos étiquettes
uploads:
show:
+ qif_title: Téléverser le fichier QIF
+ qif_description: Sélectionnez le compte auquel appartient ce fichier QIF, puis téléversez votre export .qif depuis Quicken.
+ qif_account_label: Compte
+ qif_account_placeholder: Sélectionner un compte…
+ qif_file_prompt: pour ajouter votre fichier QIF ici
+ qif_file_hint: Fichiers .qif uniquement
+ qif_submit: Téléverser le QIF
+ browse: Parcourir
+ csv_file_prompt: pour ajouter votre fichier CSV ici
description: Collez ou téléversez votre fichier CSV ci-dessous. Veuillez examiner les instructions dans le tableau ci-dessous avant de commencer.
instructions_1: Voici un exemple de CSV avec des colonnes disponibles pour l'importation.
instructions_2: Votre CSV doit avoir une ligne d'en-tête
@@ -54,8 +94,32 @@ fr:
instructions_4: Les colonnes marquées avec une étoile (*) sont des données requises.
instructions_5: Pas de virgules, pas de symboles monétaires et pas de parenthèses dans les nombres.
title: Importez vos données
+ sure_import:
+ title: Importer depuis l'export
+ description: Téléversez le fichier all.ndjson de votre export de données pour restaurer vos comptes, transactions, catégories et plus encore.
+ drop_title: Déposez le NDJSON pour téléverser
+ drop_subtitle: Votre fichier sera téléversé automatiquement
+ browse: Parcourir
+ browse_hint: pour ajouter votre fichier all.ndjson ici
+ upload_button: Téléverser le NDJSON
+ hint_html: Téléversez le fichier all.ndjson de l'archive ZIP d'export de vos données
+ ndjson_invalid: Le fichier doit être un NDJSON valide avec au moins un enregistrement
imports:
+ date_format:
+ heading: Format de date
+ description: "Le format de date a été détecté automatiquement depuis votre fichier. Modifiez-le si les dates semblent incorrectes."
+ preview: "Première date analysée"
+ error_title: "Impossible de détecter le format de date"
+ error_description: "Aucun des formats de date pris en charge n'a pu analyser les dates dans ce fichier. Veuillez vérifier que le fichier contient des entrées de date valides."
+ steps:
+ upload: Téléverser
+ configure: Configurer
+ clean: Nettoyer
+ map: Mapper
+ confirm: Confirmer
+ select: Sélectionner
index:
+ title: Importations
imports: Imports
new: Nouvelle importation
table:
@@ -87,9 +151,62 @@ fr:
import_portfolio: Importer les investissements
import_rules: Importer les règles
import_transactions: Importer les transactions
+ import_qif: Importer depuis Quicken (QIF)
+ import_file: Importer un document
+ import_file_description: Analyse par IA pour les PDFs et téléversement avec recherche pour les autres fichiers pris en charge
+ requires_account: Importez d'abord des comptes pour débloquer cette option.
resume: Reprendre %{type}
sources: Sources
title: Nouvelle importation CSV
+ create:
+ file_too_large: Le fichier est trop volumineux. La taille maximale est de %{max_size} Mo.
+ invalid_file_type: Type de fichier invalide. Veuillez téléverser un fichier CSV.
+ csv_uploaded: CSV téléversé avec succès.
+ pdf_too_large: Le fichier PDF est trop volumineux. La taille maximale est de %{max_size} Mo.
+ pdf_processing: Votre PDF est en cours de traitement. Vous recevrez un e-mail lorsque l'analyse sera terminée.
+ invalid_pdf: Le fichier téléversé n'est pas un PDF valide.
+ document_too_large: Le document est trop volumineux. La taille maximale est de %{max_size} Mo.
+ invalid_document_file_type: Type de fichier de document invalide pour le magasin de vecteurs actif.
+ document_uploaded: Document téléversé avec succès.
+ document_upload_failed: Nous n'avons pas pu téléverser le document dans le magasin de vecteurs. Veuillez réessayer.
+ document_provider_not_configured: Aucun magasin de vecteurs n'est configuré pour les téléversements de documents.
+ show:
+ finalize_upload: Veuillez finaliser le téléversement de votre fichier.
+ finalize_mappings: Veuillez finaliser vos correspondances avant de continuer.
+ errors:
+ custom_column_requires_inflow: "Les importations de colonnes personnalisées nécessitent la sélection d'une colonne d'entrée"
+ document_types:
+ bank_statement: Relevé bancaire
+ credit_card_statement: Relevé de carte de crédit
+ investment_statement: Relevé d'investissement
+ financial_document: Document financier
+ contract: Contrat
+ other: Autre document
+ unknown: Document inconnu
+ pdf_import:
+ processing_title: Traitement de votre PDF
+ processing_description: Nous analysons votre document à l'aide de l'IA. Cela peut prendre un moment. Vous recevrez un e-mail lorsque l'analyse sera terminée.
+ check_status: Vérifier le statut
+ back_to_dashboard: Retour au tableau de bord
+ failed_title: Traitement échoué
+ failed_description: Nous n'avons pas pu traiter votre document PDF. Veuillez réessayer ou contacter le support.
+ try_again: Réessayer
+ delete_import: Supprimer l'importation
+ complete_title: Document analysé
+ complete_description: Nous avons analysé votre PDF et voici ce que nous avons trouvé.
+ document_type_label: Type de document
+ summary_label: Résumé
+ email_sent_notice: Un e-mail vous a été envoyé avec les prochaines étapes.
+ back_to_imports: Retour aux importations
+ unknown_state_title: État inconnu
+ unknown_state_description: Cette importation est dans un état inattendu. Veuillez retourner aux importations.
+ processing_failed_with_message: "%{message}"
+ processing_failed_generic: "Traitement échoué : %{error}"
ready:
description: Voici un résumé des nouveaux éléments qui seront ajoutés à votre compte une fois que vous aurez publié cette importation.
title: Confirmez vos données d'importation
+ summary_item_label: Élément
+ summary_count_label: Nombre
+ empty_summary: Aucun enregistrement importable n'a été trouvé dans ce fichier. Il est peut-être vide, ou les lignes ne correspondent pas au format d'export attendu (chaque ligne doit être un objet JSON avec les clés « type » et « data », pour des types pris en charge par cet import).
+ publish_import: Publier l'importation
+ back_to_imports: Retour aux importations
diff --git a/config/locales/views/imports/nb.yml b/config/locales/views/imports/nb.yml
index b0544f12e..cebd1ddb0 100644
--- a/config/locales/views/imports/nb.yml
+++ b/config/locales/views/imports/nb.yml
@@ -94,4 +94,9 @@ nb:
ready:
description: Her er en oppsummering av de nye elementene som vil bli lagt til kontoen din
når du publiserer denne importen.
- title: Bekreft importdataene dine
\ No newline at end of file
+ title: Bekreft importdataene dine
+ summary_item_label: Element
+ summary_count_label: Antall
+ empty_summary: Vi fant ingen poster som kan importeres i denne filen. Den kan være tom, eller linjene samsvarer ikke med det forventede eksportformatet (hver linje skal være et JSON-objekt med nøklene «type» og «data», med typer denne importen støtter).
+ publish_import: Publiser import
+ back_to_imports: Tilbake til importer
\ No newline at end of file
diff --git a/config/locales/views/imports/nl.yml b/config/locales/views/imports/nl.yml
index 898d82c96..31a2d3678 100644
--- a/config/locales/views/imports/nl.yml
+++ b/config/locales/views/imports/nl.yml
@@ -93,3 +93,8 @@ nl:
ready:
description: Hier is een samenvatting van de nieuwe items die aan uw account worden toegevoegd zodra u deze import publiceert.
title: Uw importgegevens bevestigen
+ summary_item_label: Item
+ summary_count_label: Aantal
+ empty_summary: Er zijn geen importeerbare records in dit bestand gevonden. Het bestand is mogelijk leeg, of de regels voldoen niet aan het verwachte exportformaat (elke regel moet een JSON-object zijn met de sleutels „type“ en „data“, met typen die deze import ondersteunt).
+ publish_import: Import publiceren
+ back_to_imports: Terug naar importen
diff --git a/config/locales/views/imports/pl.yml b/config/locales/views/imports/pl.yml
new file mode 100644
index 000000000..af8db190c
--- /dev/null
+++ b/config/locales/views/imports/pl.yml
@@ -0,0 +1,241 @@
+---
+pl:
+ import:
+ qif_category_selections:
+ show:
+ title: Wybierz kategorie i tagi
+ description: Wybierz, które kategorie i tagi z pliku QIF chcesz zaimportować do Sure. Odznaczone elementy zostaną usunięte z tych transakcji.
+ categories_heading: Kategorie
+ categories_found:
+ one: Znaleziono 1 kategorię
+ few: Znaleziono %{count} kategorie
+ many: Znaleziono %{count} kategorii
+ other: Znaleziono %{count} kategorii
+ category_name_col: Nazwa kategorii
+ transactions_col: Transakcje
+ tags_heading: Tagi
+ tags_found:
+ one: Znaleziono 1 tag
+ few: Znaleziono %{count} tagi
+ many: Znaleziono %{count} tagów
+ other: Znaleziono %{count} tagów
+ tag_name_col: Nazwa tagu
+ txn_count:
+ one: 1 transakcja
+ few: "%{count} transakcje"
+ many: "%{count} transakcji"
+ other: "%{count} transakcji"
+ split_warning_title: Wykryto transakcje dzielone
+ split_warning_description: Ten plik QIF zawiera transakcje dzielone. Podziały nie są jeszcze obsługiwane, więc każda taka transakcja zostanie zaimportowana jako pojedyncza transakcja z pełną kwotą i bez kategorii. Szczegółowy podział nie zostanie zachowany.
+ split_badge: podział
+ empty_state_primary: W tym pliku QIF nie znaleziono kategorii ani tagów.
+ empty_state_secondary: Wszystkie transakcje zostaną zaimportowane bez kategorii i tagów.
+ submit: Przejdź do podsumowania
+ cleans:
+ show:
+ description: Edytuj swoje dane w tabeli poniżej. Czerwone komórki są nieprawidłowe.
+ errors_notice: Masz błędy w danych. Najedź na błąd, aby zobaczyć szczegóły.
+ errors_notice_mobile: Masz błędy w danych. Dotknij dymku błędu, aby zobaczyć szczegóły.
+ title: Wyczyść dane
+ configurations:
+ update:
+ success: Import został pomyślnie skonfigurowany.
+ category_import:
+ button_label: Dalej
+ description: Prześlij prosty plik CSV, taki jak ten generowany podczas eksportu danych. Kolumny zostaną automatycznie zmapowane.
+ instructions: Wybierz Dalej, aby przeanalizować plik CSV i przejść do etapu czyszczenia.
+ mint_import:
+ date_format_label: Format daty
+ rule_import:
+ description: Skonfiguruj import reguł. Reguły zostaną utworzone lub zaktualizowane na podstawie danych z CSV.
+ process_button: Przetwórz reguły
+ process_help: Kliknij poniższy przycisk, aby przetworzyć CSV i wygenerować wiersze reguł.
+ show:
+ description: Wybierz kolumny odpowiadające poszczególnym polom w pliku CSV.
+ title: Skonfiguruj import
+ trade_import:
+ date_format_label: Format daty
+ transaction_import:
+ date_format_label: Format daty
+ rows_to_skip_label: Pomiń pierwsze n wierszy
+ confirms:
+ sure_import:
+ title: Potwierdź import
+ description: Przejrzyj dane, które zostaną zaimportowane z pliku eksportu.
+ summary: Podsumowanie importu
+ empty_summary: Nie udało się znaleźć w tym pliku żadnych rekordów nadających się do importu. Plik może być pusty albo jego wiersze nie pasują do oczekiwanego formatu eksportu. Każdy wiersz powinien być obiektem JSON z kluczami "type" i "data" oraz typami obsługiwanymi przez ten import.
+ publish_button: Rozpocznij import
+ cancel: Anuluj
+ mappings:
+ create_account: Utwórz konto
+ csv_mapping_label: "%{mapping} w CSV"
+ sure_mapping_label: "%{mapping} w %{product_name}"
+ no_accounts: Nie masz jeszcze żadnych kont. Utwórz konto, którego można użyć dla nieprzypisanych wierszy w CSV, albo wróć do etapu czyszczenia i podaj nazwę konta, której można użyć.
+ rows_label: Wiersze
+ unassigned_account: Chcesz utworzyć nowe konto dla nieprzypisanych wierszy?
+ show:
+ account_mapping_description: Przypisz wszystkie konta z importowanego pliku do istniejących kont %{product}. Możesz też dodać nowe konta lub zostawić je bez przypisania.
+ account_mapping_title: Przypisz konta
+ account_type_mapping_description: Przypisz wszystkie typy kont z importowanego pliku do typów używanych w %{product}.
+ account_type_mapping_title: Przypisz typy kont
+ category_mapping_description: Przypisz wszystkie kategorie z importowanego pliku do istniejących kategorii %{product}. Możesz też dodać nowe kategorie lub zostawić je bez przypisania.
+ category_mapping_title: Przypisz kategorie
+ tag_mapping_description: Przypisz wszystkie tagi z importowanego pliku do istniejących tagów %{product}. Możesz też dodać nowe tagi lub zostawić je bez przypisania.
+ tag_mapping_title: Przypisz tagi
+ uploads:
+ show:
+ qif_title: Prześlij plik QIF
+ qif_description: Wybierz konto, do którego należy ten plik QIF, a następnie prześlij eksport .qif z Quicken.
+ qif_account_label: Konto
+ qif_account_placeholder: Wybierz konto…
+ qif_file_prompt: aby dodać tutaj plik QIF
+ qif_file_hint: tylko pliki .qif
+ qif_submit: Prześlij QIF
+ browse: Przeglądaj
+ csv_file_prompt: aby dodać tutaj plik CSV
+ description: Wklej lub prześlij poniżej swój plik CSV. Przed rozpoczęciem zapoznaj się z instrukcjami w tabeli poniżej.
+ instructions_1: Poniżej znajduje się przykładowy plik CSV z kolumnami dostępnymi do importu.
+ instructions_2: Twój plik CSV musi zawierać wiersz nagłówka.
+ instructions_3: Możesz nazwać kolumny dowolnie. Zmapujesz je na późniejszym etapie.
+ instructions_4: Kolumny oznaczone gwiazdką (*) są wymagane.
+ instructions_5: W liczbach nie używaj przecinków, symboli walut ani nawiasów.
+ title: Zaimportuj dane
+ sure_import:
+ title: Importuj z eksportu
+ description: Prześlij plik all.ndjson z eksportu danych, aby przywrócić konta, transakcje, kategorie i inne dane.
+ drop_title: Upuść plik NDJSON, aby go przesłać
+ drop_subtitle: Plik zostanie przesłany automatycznie
+ browse: Przeglądaj
+ browse_hint: aby dodać tutaj plik all.ndjson
+ upload_button: Prześlij NDJSON
+ hint_html: Prześlij plik all.ndjson z archiwum ZIP eksportu danych
+ ndjson_invalid: Plik musi być poprawnym NDJSON i zawierać co najmniej jeden rekord
+ imports:
+ type_labels:
+ transaction_import: Import transakcji
+ trade_import: Import transakcji giełdowych
+ account_import: Import kont
+ mint_import: Import z Mint
+ qif_import: Import QIF
+ category_import: Import kategorii
+ rule_import: Import reguł
+ pdf_import: Import PDF
+ document_import: Import dokumentu
+ sure_import: Import z Sure
+ steps:
+ upload: Przesyłanie
+ configure: Konfiguracja
+ clean: Czyszczenie
+ map: Mapowanie
+ confirm: Potwierdzenie
+ select: Wybór
+ index:
+ title: Importy
+ new: Nowy import
+ table:
+ title: Importy
+ header:
+ date: Data
+ operation: Operacja
+ status: Status
+ actions: Akcje
+ row:
+ type_labels:
+ transaction_import: Transakcja
+ trade_import: Transakcja giełdowa
+ account_import: Konto
+ mint_import: "Mint"
+ qif_import: "QIF"
+ category_import: Kategoria
+ rule_import: Reguła
+ pdf_import: "PDF"
+ document_import: Dokument
+ sure_import: "Sure"
+ status:
+ in_progress: W toku
+ uploading: Przetwarzanie wierszy
+ reverting: Cofanie
+ revert_failed: Nie udało się cofnąć
+ complete: Zakończono
+ failed: Niepowodzenie
+ actions:
+ revert: Cofnij
+ confirm_revert: To usunie zaimportowane transakcje, ale nadal będzie można w każdej chwili przejrzeć i ponownie zaimportować dane.
+ delete: Usuń
+ view: Zobacz
+ empty: Brak importów.
+ new:
+ description: Importuj dane z narzędzia finansowego lub prześlij surowe pliki danych.
+ tab_financial_tools: Narzędzia i pliki finansowe
+ tab_raw_data: Surowe dane
+ coming_soon: Wkrótce
+ import_ynab: Importuj z YNAB
+ import_accounts: Importuj konta
+ import_categories: Importuj kategorie
+ import_mint: Importuj z Mint
+ import_portfolio: Importuj inwestycje
+ import_rules: Importuj reguły
+ import_transactions: Importuj transakcje
+ import_qif: Importuj z Quicken (QIF)
+ import_sure: Importuj z Sure
+ import_sure_description: Pełny plik eksportu .ndjson
+ import_file: Importuj dokument
+ import_file_description: Analiza PDF wspierana przez AI oraz przesyłanie przeszukiwalnych plików
+ requires_account: Najpierw zaimportuj konta, aby odblokować tę opcję.
+ resume: Wznów %{type}
+ sources: Źródła
+ title: Nowy import
+ create:
+ file_too_large: Plik jest zbyt duży. Maksymalny rozmiar to %{max_size} MB.
+ invalid_file_type: Nieprawidłowy typ pliku. Prześlij plik CSV.
+ csv_uploaded: Plik CSV został pomyślnie przesłany.
+ ndjson_uploaded: Plik NDJSON został pomyślnie przesłany.
+ pdf_too_large: Plik PDF jest zbyt duży. Maksymalny rozmiar to %{max_size} MB.
+ pdf_processing: Twój plik PDF jest przetwarzany. Po zakończeniu analizy otrzymasz wiadomość e-mail.
+ invalid_pdf: Przesłany plik nie jest prawidłowym plikiem PDF.
+ document_too_large: Plik dokumentu jest zbyt duży. Maksymalny rozmiar to %{max_size} MB.
+ invalid_document_file_type: Nieprawidłowy typ pliku dokumentu dla aktywnego magazynu wektorowego.
+ document_uploaded: Dokument został pomyślnie przesłany.
+ document_upload_failed: Nie udało się przesłać dokumentu do magazynu wektorowego. Spróbuj ponownie.
+ invalid_ndjson_file_type: Nieprawidłowy typ lub format pliku. Prześlij poprawny plik eksportu .ndjson lub .json.
+ document_provider_not_configured: Nie skonfigurowano magazynu wektorowego dla przesyłania dokumentów.
+ show:
+ finalize_upload: Dokończ przesyłanie pliku.
+ finalize_mappings: Dokończ mapowanie przed przejściem dalej.
+ ready:
+ description: Oto podsumowanie nowych elementów, które zostaną dodane do konta po opublikowaniu tego importu.
+ title: Potwierdź dane importu
+ summary_item_label: Element
+ summary_count_label: Liczba
+ empty_summary: Nie udało się znaleźć w tym pliku żadnych rekordów nadających się do importu. Plik może być pusty albo jego wiersze nie pasują do oczekiwanego formatu eksportu. Każdy wiersz powinien być obiektem JSON z kluczami "type" i "data" oraz typami obsługiwanymi przez ten import.
+ publish_import: Opublikuj import
+ back_to_imports: Wróć do importów
+ errors:
+ custom_column_requires_inflow: Import niestandardowych kolumn wymaga wybrania kolumny wpływu
+ document_types:
+ bank_statement: Wyciąg bankowy
+ credit_card_statement: Wyciąg z karty kredytowej
+ investment_statement: Wyciąg inwestycyjny
+ financial_document: Dokument finansowy
+ contract: Umowa
+ other: Inny dokument
+ unknown: Nieznany dokument
+ pdf_import:
+ processing_title: Przetwarzanie PDF
+ processing_description: Analizujemy Twój dokument przy użyciu AI. Może to chwilę potrwać. Po zakończeniu analizy otrzymasz wiadomość e-mail.
+ check_status: Sprawdź status
+ back_to_dashboard: Wróć do pulpitu
+ failed_title: Przetwarzanie nie powiodło się
+ failed_description: Nie udało się przetworzyć dokumentu PDF. Spróbuj ponownie lub skontaktuj się ze wsparciem.
+ try_again: Spróbuj ponownie
+ delete_import: Usuń import
+ complete_title: Dokument przeanalizowany
+ complete_description: Przeanalizowaliśmy Twój plik PDF. Oto, co znaleźliśmy.
+ document_type_label: Typ dokumentu
+ summary_label: Podsumowanie
+ email_sent_notice: Wysłano do Ciebie wiadomość e-mail z dalszymi krokami.
+ back_to_imports: Wróć do importów
+ unknown_state_title: Nieznany stan
+ unknown_state_description: Ten import jest w nieoczekiwanym stanie. Wróć do listy importów.
+ processing_failed_with_message: "%{message}"
+ processing_failed_generic: "Przetwarzanie nie powiodło się: %{error}"
diff --git a/config/locales/views/imports/pt-BR.yml b/config/locales/views/imports/pt-BR.yml
index 6183ac6bd..84edb7b83 100644
--- a/config/locales/views/imports/pt-BR.yml
+++ b/config/locales/views/imports/pt-BR.yml
@@ -101,3 +101,8 @@ pt-BR:
description: Aqui está um resumo dos novos itens que serão adicionados à sua conta
assim que você publicar esta importação.
title: Confirmar seus dados de importação
+ summary_item_label: Item
+ summary_count_label: Quantidade
+ empty_summary: Não foi possível encontrar registros importáveis neste arquivo. Ele pode estar vazio, ou as linhas não correspondem ao formato de exportação esperado (cada linha deve ser um objeto JSON com as chaves «type» e «data», usando tipos suportados por esta importação).
+ publish_import: Publicar importação
+ back_to_imports: Voltar às importações
diff --git a/config/locales/views/imports/ro.yml b/config/locales/views/imports/ro.yml
index 76a74371f..bec1cf695 100644
--- a/config/locales/views/imports/ro.yml
+++ b/config/locales/views/imports/ro.yml
@@ -80,3 +80,8 @@ ro:
ready:
description: Iată un rezumat al elementelor noi care vor fi adăugate contului tău odată ce vei publica acest import.
title: Confirmă datele importate
+ summary_item_label: Element
+ summary_count_label: Număr
+ empty_summary: Nu s-au găsit înregistrări importabile în acest fișier. Fișierul poate fi gol sau liniile nu respectă formatul de export așteptat (fiecare linie trebuie să fie un obiect JSON cu cheile „type” și „data”, folosind tipuri acceptate de acest import).
+ publish_import: Publică importul
+ back_to_imports: Înapoi la importuri
diff --git a/config/locales/views/imports/tr.yml b/config/locales/views/imports/tr.yml
index 1edec3ae2..8296d655c 100644
--- a/config/locales/views/imports/tr.yml
+++ b/config/locales/views/imports/tr.yml
@@ -79,4 +79,9 @@ tr:
title: Yeni CSV İçe Aktarma
ready:
description: Bu içe aktarmayı yayınladığınızda hesabınıza eklenecek yeni öğelerin özeti aşağıdadır.
- title: İçe aktarma verilerinizi onaylayın
\ No newline at end of file
+ title: İçe aktarma verilerinizi onaylayın
+ summary_item_label: Öğe
+ summary_count_label: Adet
+ empty_summary: Bu dosyada içe aktarılabilir kayıt bulunamadı. Dosya boş olabilir veya satırlar beklenen dışa aktarma biçimiyle eşleşmiyor (her satır, bu içe aktarmanın desteklediği türlerle «type» ve «data» anahtarlarına sahip bir JSON nesnesi olmalıdır).
+ publish_import: İçe aktarmayı yayınla
+ back_to_imports: İçe aktarmalara dön
\ No newline at end of file
diff --git a/config/locales/views/imports/zh-CN.yml b/config/locales/views/imports/zh-CN.yml
index 9ce64c768..8ea66765b 100644
--- a/config/locales/views/imports/zh-CN.yml
+++ b/config/locales/views/imports/zh-CN.yml
@@ -90,3 +90,8 @@ zh-CN:
ready:
description: 以下是发布导入后将添加到您账户的新项目摘要。
title: 确认导入数据
+ summary_item_label: 项目
+ summary_count_label: 数量
+ empty_summary: 在此文件中未找到可导入的记录。文件可能为空,或各行不符合预期的导出格式(每行应为包含「type」和「data」键的 JSON 对象,且类型须为本导入支持的类型)。
+ publish_import: 发布导入
+ back_to_imports: 返回导入列表
diff --git a/config/locales/views/imports/zh-TW.yml b/config/locales/views/imports/zh-TW.yml
index 25c7bd541..a7a47849b 100644
--- a/config/locales/views/imports/zh-TW.yml
+++ b/config/locales/views/imports/zh-TW.yml
@@ -90,3 +90,8 @@ zh-TW:
ready:
description: 以下是發佈此匯入後,將新增至您帳戶的項目摘要。
title: 確認您的匯入資料
+ summary_item_label: 項目
+ summary_count_label: 數量
+ empty_summary: 在此檔案中找不到可匯入的記錄。檔案可能是空的,或各行不符合預期的匯出格式(每行應為包含「type」與「data」鍵的 JSON 物件,且類型須為此匯入支援的類型)。
+ publish_import: 發佈匯入
+ back_to_imports: 返回匯入列表
diff --git a/config/locales/views/indexa_capital_items/de.yml b/config/locales/views/indexa_capital_items/de.yml
new file mode 100644
index 000000000..09d0e2a22
--- /dev/null
+++ b/config/locales/views/indexa_capital_items/de.yml
@@ -0,0 +1,247 @@
+---
+de:
+ indexa_capital_items:
+ sync_status:
+ no_accounts: "Keine Konten gefunden"
+ synced:
+ one: "%{count} Konto synchronisiert"
+ other: "%{count} Konten synchronisiert"
+ synced_with_setup: "%{linked} synchronisiert, %{unlinked} benötigen Einrichtung"
+ institution_summary:
+ none: "Keine Institute verbunden"
+ count:
+ one: "%{count} Institut"
+ other: "%{count} Institute"
+ errors:
+ provider_not_configured: "IndexaCapital-Anbieter ist nicht konfiguriert"
+
+
+ sync:
+ status:
+ importing: "Konten werden von IndexaCapital importiert..."
+ processing: "Depots und Aktivitäten werden verarbeitet..."
+ calculating: "Salden werden berechnet..."
+ importing_data: "Kontodaten werden importiert..."
+ checking_setup: "Kontokonfiguration wird geprüft..."
+ needs_setup: "%{count} Konten benötigen Einrichtung..."
+ success: "Synchronisation gestartet"
+
+
+ panel:
+ setup_instructions: "Einrichtungsanleitung:"
+ step_1: "Besuchen Sie Ihr IndexaCapital Dashboard, um einen schreibgeschützten API-Token zu erstellen"
+ step_2: "Fügen Sie Ihren API-Token unten ein und klicken Sie auf Speichern"
+ step_3: "Nach erfolgreicher Verbindung gehen Sie zur Registerkarte Konten, um neue Konten einzurichten"
+ field_descriptions: "Feldbeschreibungen:"
+ optional: "(Optional)"
+ required: "(Pflichtfeld)"
+ optional_with_default: "(optional, Standard: %{default_value})"
+ alternative_auth: "Oder nutzen Sie Benutzername/Passwort-Anmeldung..."
+ save_button: "Konfiguration speichern"
+ update_button: "Konfiguration aktualisieren"
+ status_configured_html: "Konfiguriert und einsatzbereit. Besuchen Sie die Registerkarte Konten, um Konten zu verwalten und einzurichten."
+ status_not_configured: "Nicht konfiguriert"
+ fields:
+ api_token:
+ label: "API-Token"
+ description: "Ihr schreibgeschützter API-Token aus dem IndexaCapital Dashboard"
+ placeholder_new: "API-Token hier einfügen"
+ placeholder_update: "Neuen API-Token zum Aktualisieren eingeben"
+ username:
+ label: "Benutzername"
+ description: "Ihr IndexaCapital Benutzername/E-Mail"
+ placeholder_new: "Benutzername hier einfügen"
+ placeholder_update: "Neuen Benutzernamen zum Aktualisieren eingeben"
+ document:
+ label: "Dokument-ID"
+ description: "Ihre IndexaCapital Dokument-/ID-Nummer"
+ placeholder_new: "Dokument-ID hier einfügen"
+ placeholder_update: "Neue Dokument-ID zum Aktualisieren eingeben"
+ password:
+ label: "Passwort"
+ description: "Ihr IndexaCapital Passwort"
+ placeholder_new: "Passwort hier einfügen"
+ placeholder_update: "Neues Passwort zum Aktualisieren eingeben"
+
+
+ create:
+ success: "IndexaCapital-Verbindung erfolgreich erstellt"
+ update:
+ success: "IndexaCapital-Verbindung aktualisiert"
+ destroy:
+ success: "IndexaCapital-Verbindung entfernt"
+ index:
+ title: "IndexaCapital-Verbindungen"
+
+
+ loading:
+ loading_message: "IndexaCapital-Konten werden geladen..."
+ loading_title: "Laden"
+
+ link_accounts:
+ all_already_linked:
+ one: "Das ausgewählte Konto (%{names}) ist bereits verknüpft"
+ other: "Alle %{count} ausgewählten Konten sind bereits verknüpft: %{names}"
+ api_error: "API-Fehler: %{message}"
+ invalid_account_names:
+ one: "Konto mit leerem Namen kann nicht verknüpft werden"
+ other: "%{count} Konten mit leeren Namen können nicht verknüpft werden"
+ link_failed: "Konten konnten nicht verknüpft werden"
+ no_accounts_selected: "Bitte wählen Sie mindestens ein Konto aus"
+ no_api_key: "IndexaCapital-Zugangsdaten nicht gefunden. Bitte in den Anbieter-Einstellungen konfigurieren."
+ partial_invalid: "Erfolgreich %{created_count} Konto/Konten verknüpft, %{already_linked_count} waren bereits verknüpft, %{invalid_count} Konto/Konten hatten ungültige Namen"
+ partial_success: "Erfolgreich %{created_count} Konto/Konten verknüpft. %{already_linked_count} Konto/Konten waren bereits verknüpft: %{already_linked_names}"
+ success:
+ one: "Erfolgreich %{count} Konto verknüpft"
+ other: "Erfolgreich %{count} Konten verknüpft"
+
+
+ indexa_capital_item:
+ accounts_need_setup: "Konten benötigen Einrichtung"
+ delete: "Verbindung löschen"
+ deletion_in_progress: "Löschung läuft..."
+ error: "Fehler"
+ more_accounts_available:
+ one: "%{count} weiteres Konto verfügbar"
+ other: "%{count} weitere Konten verfügbar"
+ no_accounts_description: "Diese Verbindung hat noch keine verknüpften Konten."
+ no_accounts_title: "Keine Konten"
+ provider_name: "IndexaCapital"
+ requires_update: "Verbindung muss aktualisiert werden"
+ setup_action: "Neue Konten einrichten"
+ setup_description: "%{linked} von %{total} Konten verknüpft. Wählen Sie Kontotypen für Ihre neu importierten IndexaCapital-Konten."
+ setup_needed: "Neue Konten bereit zur Einrichtung"
+ status: "Vor %{timestamp} synchronisiert — %{summary}"
+ status_never: "Noch nie synchronisiert"
+ syncing: "Synchronisiere..."
+ total: "Gesamt"
+ unlinked: "Nicht verknüpft"
+ update_credentials: "Zugangsdaten aktualisieren"
+
+
+ select_accounts:
+ accounts_selected: "Konten ausgewählt"
+ api_error: "API-Fehler: %{message}"
+ cancel: "Abbrechen"
+ configure_name_in_provider: "Import nicht möglich – bitte Kontoname in IndexaCapital konfigurieren"
+ description: "Wählen Sie die Konten aus, die Sie mit Ihrem %{product_name}-Konto verknüpfen möchten."
+ link_accounts: "Ausgewählte Konten verknüpfen"
+ no_accounts_found: "Keine Konten gefunden. Bitte überprüfen Sie Ihre IndexaCapital-Zugangsdaten."
+ no_api_key: "IndexaCapital-Zugangsdaten sind nicht konfiguriert. Bitte in den Einstellungen konfigurieren."
+ no_credentials_configured: "Bitte konfigurieren Sie zuerst Ihre IndexaCapital-Zugangsdaten in den Anbieter-Einstellungen."
+ no_name_placeholder: "(Kein Name)"
+ title: "IndexaCapital-Konten auswählen"
+
+ select_existing_account:
+ account_already_linked: "Dieses Konto ist bereits mit einem Anbieter verknüpft"
+ all_accounts_already_linked: "Alle IndexaCapital-Konten sind bereits verknüpft"
+ api_error: "API-Fehler: %{message}"
+ balance_label: "Saldo:"
+ cancel: "Abbrechen"
+ cancel_button: "Abbrechen"
+ configure_name_in_provider: "Import nicht möglich – bitte Kontoname in IndexaCapital konfigurieren"
+ connect_hint: "Verbinden Sie ein IndexaCapital-Konto für automatische Synchronisation."
+ description: "Wählen Sie ein IndexaCapital-Konto zur Verknüpfung mit diesem Konto. Transaktionen werden automatisch synchronisiert und dedupliziert."
+ header: "Mit IndexaCapital verknüpfen"
+ link_account: "Konto verknüpfen"
+ link_button: "Dieses Konto verknüpfen"
+ linking_to: "Verknüpfe mit:"
+ no_account_specified: "Kein Konto angegeben"
+ no_accounts: "Keine unverknüpften IndexaCapital-Konten gefunden."
+ no_accounts_found: "Keine IndexaCapital-Konten gefunden. Bitte überprüfen Sie Ihre Zugangsdaten."
+ no_api_key: "IndexaCapital-Zugangsdaten sind nicht konfiguriert. Bitte in den Einstellungen konfigurieren."
+ no_credentials_configured: "Bitte konfigurieren Sie zuerst Ihre IndexaCapital-Zugangsdaten in den Anbieter-Einstellungen."
+ no_name_placeholder: "(Kein Name)"
+ settings_link: "Zu den Anbieter-Einstellungen"
+ subtitle: "IndexaCapital-Konto auswählen"
+ title: "%{account_name} mit IndexaCapital verknüpfen"
+
+ link_existing_account:
+ account_already_linked: "Dieses Konto ist bereits mit einem Anbieter verknüpft"
+ api_error: "API-Fehler: %{message}"
+ invalid_account_name: "Konto mit leerem Namen kann nicht verknüpft werden"
+ provider_account_already_linked: "Dieses IndexaCapital-Konto ist bereits mit einem anderen Konto verknüpft"
+ provider_account_not_found: "IndexaCapital-Konto nicht gefunden"
+ missing_parameters: "Erforderliche Parameter fehlen"
+ no_api_key: "IndexaCapital-Zugangsdaten nicht gefunden. Bitte in den Anbieter-Einstellungen konfigurieren."
+ success: "%{account_name} erfolgreich mit IndexaCapital verknüpft"
+
+ setup_accounts:
+ account_type_label: "Kontotyp:"
+ accounts_count:
+ one: "%{count} Konto verfügbar"
+ other: "%{count} Konten verfügbar"
+ all_accounts_linked: "Alle Ihre IndexaCapital-Konten sind bereits eingerichtet."
+ api_error: "API-Fehler: %{message}"
+ creating: "Konten werden erstellt..."
+ fetch_failed: "Konten konnten nicht geladen werden"
+ import_selected: "Ausgewählte Konten importieren"
+ instructions: "Wählen Sie die Konten aus, die Sie von IndexaCapital importieren möchten. Sie können mehrere Konten auswählen."
+ no_accounts: "Keine unverknüpften Konten von dieser IndexaCapital-Verbindung gefunden."
+ no_accounts_to_setup: "Keine Konten zum Einrichten"
+ no_api_key: "IndexaCapital-Zugangsdaten sind nicht konfiguriert. Bitte überprüfen Sie Ihre Verbindungseinstellungen."
+ select_all: "Alle auswählen"
+ account_types:
+ skip: "Dieses Konto überspringen"
+ depository: "Giro- oder Sparkonto"
+ credit_card: "Kreditkarte"
+ investment: "Depot/Anlagekonto"
+ crypto: "Kryptowährungs-Konto"
+ loan: "Darlehen oder Hypothek"
+ other_asset: "Sonstiges Vermögen"
+ subtype_labels:
+ depository: "Konto-Untertyp:"
+ credit_card: ""
+ investment: "Anlagetyp:"
+ crypto: ""
+ loan: "Darlehenstyp:"
+ other_asset: ""
+ subtype_messages:
+ credit_card: "Kreditkarten werden automatisch als Kreditkartenkonten eingerichtet."
+ other_asset: "Für sonstiges Vermögen sind keine weiteren Optionen nötig."
+ crypto: "Kryptowährungs-Konten werden zur Verwaltung von Beständen und Transaktionen eingerichtet."
+ subtypes:
+ depository:
+ checking: "Girokonto"
+ savings: "Sparkonto"
+ hsa: "Gesundheits-Sparkonto"
+ cd: "Festgeld"
+ money_market: "Geldmarkt"
+ investment:
+ brokerage: "Brokerage"
+ pension: "Rente"
+ retirement: "Altersvorsorge"
+ "401k": "401(k)"
+ roth_401k: "Roth 401(k)"
+ "403b": "403(b)"
+ tsp: "Thrift Savings Plan"
+ "529_plan": "529 Plan"
+ hsa: "Gesundheits-Sparkonto"
+ mutual_fund: "Investmentfonds"
+ ira: "Traditioneller IRA"
+ roth_ira: "Roth IRA"
+ angel: "Angel"
+ loan:
+ mortgage: "Hypothek"
+ student: "Studienkredit"
+ auto: "Autokredit"
+ other: "Sonstiges Darlehen"
+ balance: "Saldo"
+ cancel: "Abbrechen"
+ choose_account_type: "Wählen Sie den passenden Kontotyp für jedes IndexaCapital-Konto:"
+ create_accounts: "Konten erstellen"
+ creating_accounts: "Konten werden erstellt..."
+ historical_data_range: "Zeitraum für Verlauf:"
+ subtitle: "Wählen Sie die passenden Kontotypen für Ihre importierten Konten"
+ sync_start_date_help: "Wählen Sie, wie weit die Transaktionshistorie synchronisiert werden soll."
+ sync_start_date_label: "Transaktionen synchronisieren ab:"
+ title: "Ihre IndexaCapital-Konten einrichten"
+
+ complete_account_setup:
+ all_skipped: "Alle Konten wurden übersprungen. Es wurden keine Konten erstellt."
+ creation_failed: "Konten konnten nicht erstellt werden: %{error}"
+ no_accounts: "Keine Konten zum Einrichten."
+ success: "Erfolgreich %{count} Konto/Konten erstellt."
+
+ preload_accounts:
+ no_credentials_configured: "Bitte konfigurieren Sie zuerst Ihre IndexaCapital-Zugangsdaten in den Anbieter-Einstellungen."
diff --git a/config/locales/views/indexa_capital_items/es.yml b/config/locales/views/indexa_capital_items/es.yml
new file mode 100644
index 000000000..f63db32d8
--- /dev/null
+++ b/config/locales/views/indexa_capital_items/es.yml
@@ -0,0 +1,241 @@
+---
+es:
+ indexa_capital_items:
+ sync_status:
+ no_accounts: "No se han encontrado cuentas"
+ synced:
+ one: "%{count} cuenta sincronizada"
+ other: "%{count} cuentas sincronizadas"
+ synced_with_setup: "%{linked} sincronizadas, %{unlinked} necesitan configuración"
+ institution_summary:
+ none: "No hay instituciones conectadas"
+ count:
+ one: "%{count} institución"
+ other: "%{count} instituciones"
+ errors:
+ provider_not_configured: "El proveedor Indexa Capital no está configurado"
+
+ sync:
+ status:
+ importing: "Importando cuentas de Indexa Capital..."
+ processing: "Procesando posiciones y actividades..."
+ calculating: "Calculando saldos..."
+ importing_data: "Importando datos de la cuenta..."
+ checking_setup: "Comprobando la configuración de la cuenta..."
+ needs_setup: "%{count} cuentas necesitan configuración..."
+ success: "Sincronización iniciada"
+
+ panel:
+ setup_instructions: "Instrucciones de configuración:"
+ step_1: "Visita tu panel de Indexa Capital para generar un token de API de solo lectura"
+ step_2: "Pega tu token de API a continuación y haz clic en Guardar"
+ step_3: "Tras una conexión exitosa, ve a la pestaña Cuentas para configurar las nuevas cuentas"
+ field_descriptions: "Descripciones de los campos:"
+ optional: "(Opcional)"
+ required: "(obligatorio)"
+ optional_with_default: "(opcional, por defecto %{default_value})"
+ alternative_auth: "O usa la autenticación por usuario/contraseña en su lugar..."
+ save_button: "Guardar configuración"
+ update_button: "Actualizar configuración"
+ status_configured_html: "Configurado y listo para usar. Visita la pestaña de Cuentas para gestionar y configurar tus cuentas."
+ status_not_configured: "No configurado"
+ fields:
+ api_token:
+ label: "Token de API"
+ description: "Tu token de API de solo lectura del panel de Indexa Capital"
+ placeholder_new: "Pega tu token de API aquí"
+ placeholder_update: "Introduce el nuevo token de API para actualizar"
+ username:
+ label: "Usuario"
+ description: "Tu usuario/email de Indexa Capital"
+ placeholder_new: "Pega el usuario aquí"
+ placeholder_update: "Introduce el nuevo usuario para actualizar"
+ document:
+ label: "Documento de identidad"
+ description: "Tu documento/ID de Indexa Capital"
+ placeholder_new: "Pega el ID del documento aquí"
+ placeholder_update: "Introduce el nuevo ID de documento para actualizar"
+ password:
+ label: "Contraseña"
+ description: "Tu contraseña de Indexa Capital"
+ placeholder_new: "Pega la contraseña aquí"
+ placeholder_update: "Introduce la nueva contraseña para actualizar"
+
+ create:
+ success: "Conexión con Indexa Capital creada correctamente"
+ update:
+ success: "Conexión con Indexa Capital actualizada"
+ destroy:
+ success: "Conexión con Indexa Capital eliminada"
+ index:
+ title: "Conexiones de Indexa Capital"
+
+ loading:
+ loading_message: "Cargando cuentas de Indexa Capital..."
+ loading_title: "Cargando"
+
+ link_accounts:
+ all_already_linked:
+ one: "La cuenta seleccionada (%{names}) ya está vinculada"
+ other: "Las %{count} cuentas seleccionadas ya están vinculadas: %{names}"
+ api_error: "Error de la API: %{message}"
+ invalid_account_names:
+ one: "No se puede vincular una cuenta sin nombre"
+ other: "No se pueden vincular %{count} cuentas sin nombre"
+ link_failed: "Error al vincular las cuentas"
+ no_accounts_selected: "Por favor, selecciona al menos una cuenta"
+ no_api_key: "No se han encontrado las credenciales de Indexa Capital. Por favor, configúralas en los ajustes del proveedor."
+ partial_invalid: "Se han vinculado correctamente %{created_count} cuenta(s), %{already_linked_count} ya estaban vinculadas, %{invalid_count} cuenta(s) tenían nombres no válidos"
+ partial_success: "Se han vinculado correctamente %{created_count} cuenta(s). %{already_linked_count} cuenta(s) ya estaban vinculadas: %{already_linked_names}"
+ success:
+ one: "Cuenta vinculada correctamente"
+ other: "%{count} cuentas vinculadas correctamente"
+
+ indexa_capital_item:
+ accounts_need_setup: "Las cuentas necesitan configuración"
+ delete: "Eliminar conexión"
+ deletion_in_progress: "eliminación en curso..."
+ error: "Error"
+ more_accounts_available:
+ one: "Hay %{count} cuenta más disponible"
+ other: "Hay %{count} cuentas más disponibles"
+ no_accounts_description: "Esta conexión aún no tiene cuentas vinculadas."
+ no_accounts_title: "Sin cuentas"
+ provider_name: "Indexa Capital"
+ requires_update: "La conexión necesita una actualización"
+ setup_action: "Configurar nuevas cuentas"
+ setup_description: "%{linked} de %{total} cuentas vinculadas. Elige los tipos de cuenta para tus cuentas de Indexa Capital recién importadas."
+ setup_needed: "Nuevas cuentas listas para configurar"
+ status: "Sincronizado hace %{timestamp} — %{summary}"
+ status_never: "Nunca sincronizado"
+ syncing: "Sincronizando..."
+ total: "Total"
+ unlinked: "Desvinculadas"
+ update_credentials: "Actualizar credenciales"
+
+ select_accounts:
+ accounts_selected: "cuentas seleccionadas"
+ api_error: "Error de la API: %{message}"
+ cancel: "Cancelar"
+ configure_name_in_provider: "No se puede importar; por favor, configura el nombre de la cuenta en Indexa Capital"
+ description: "Selecciona las cuentas que quieres vincular a tu cuenta de %{product_name}."
+ link_accounts: "Vincular cuentas seleccionadas"
+ no_accounts_found: "No se han encontrado cuentas. Por favor, comprueba tus credenciales de Indexa Capital."
+ no_api_key: "Las credenciales de Indexa Capital no están configuradas. Por favor, configúralas en Ajustes."
+ no_credentials_configured: "Por favor, configura primero tus credenciales de Indexa Capital en los ajustes del proveedor."
+ no_name_placeholder: "(Sin nombre)"
+ title: "Seleccionar cuentas de Indexa Capital"
+
+ select_existing_account:
+ account_already_linked: "Esta cuenta ya está vinculada a un proveedor"
+ all_accounts_already_linked: "Todas las cuentas de Indexa Capital ya están vinculadas"
+ api_error: "Error de la API: %{message}"
+ balance_label: "Saldo:"
+ cancel: "Cancelar"
+ cancel_button: "Cancelar"
+ configure_name_in_provider: "No se puede importar; por favor, configura el nombre de la cuenta en Indexa Capital"
+ connect_hint: "Conecta una cuenta de Indexa Capital para habilitar la sincronización automática."
+ description: "Selecciona una cuenta de Indexa Capital para vincularla con esta cuenta. Las transacciones se sincronizarán y se eliminarán los duplicados automáticamente."
+ header: "Vincular con Indexa Capital"
+ link_account: "Vincular cuenta"
+ link_button: "Vincular esta cuenta"
+ linking_to: "Vinculando a:"
+ no_account_specified: "No se ha especificado ninguna cuenta"
+ no_accounts: "No se han encontrado cuentas de Indexa Capital sin vincular."
+ no_accounts_found: "No se han encontrado cuentas de Indexa Capital. Por favor, comprueba tus credenciales."
+ no_api_key: "Las credenciales de Indexa Capital no están configuradas. Por favor, configúralas en Ajustes."
+ no_credentials_configured: "Por favor, configura primero tus credenciales de Indexa Capital en los ajustes del proveedor."
+ no_name_placeholder: "(Sin nombre)"
+ settings_link: "Ir a ajustes del proveedor"
+ subtitle: "Elige una cuenta de Indexa Capital"
+ title: "Vincular %{account_name} con Indexa Capital"
+
+ link_existing_account:
+ account_already_linked: "Esta cuenta ya está vinculada a un proveedor"
+ api_error: "Error de la API: %{message}"
+ invalid_account_name: "No se puede vincular una cuenta sin nombre"
+ provider_account_already_linked: "Esta cuenta de Indexa Capital ya está vinculada a otra cuenta"
+ provider_account_not_found: "Cuenta de Indexa Capital no encontrada"
+ missing_parameters: "Faltan parámetros obligatorios"
+ no_api_key: "No se han encontrado las credenciales de Indexa Capital. Por favor, configúralas en los ajustes del proveedor."
+ success: "Se ha vinculado correctamente %{account_name} con Indexa Capital"
+
+ setup_accounts:
+ account_type_label: "Tipo de cuenta:"
+ accounts_count:
+ one: "%{count} cuenta disponible"
+ other: "%{count} cuentas disponibles"
+ all_accounts_linked: "Todas tus cuentas de Indexa Capital ya han sido configuradas."
+ api_error: "Error de la API: %{message}"
+ creating: "Creando cuentas..."
+ fetch_failed: "Error al obtener las cuentas"
+ import_selected: "Importar cuentas seleccionadas"
+ instructions: "Selecciona las cuentas que quieres importar de Indexa Capital. Puedes elegir varias cuentas."
+ no_accounts: "No se han encontrado cuentas sin vincular en esta conexión de Indexa Capital."
+ no_accounts_to_setup: "No hay cuentas para configurar"
+ no_api_key: "Las credenciales de Indexa Capital no están configuradas. Por favor, comprueba los ajustes de conexión."
+ select_all: "Seleccionar todas"
+ account_types:
+ skip: "Omitir esta cuenta"
+ depository: "Cuenta corriente o de ahorro"
+ credit_card: "Tarjeta de crédito"
+ investment: "Cuenta de inversión"
+ crypto: "Cuenta de criptomonedas"
+ loan: "Préstamo o hipoteca"
+ other_asset: "Otro activo"
+ subtype_labels:
+ depository: "Subtipo de cuenta:"
+ credit_card: ""
+ investment: "Tipo de inversión:"
+ crypto: ""
+ loan: "Tipo de préstamo:"
+ other_asset: ""
+ subtype_messages:
+ credit_card: "Las tarjetas de crédito se configurarán automáticamente como cuentas de tarjeta de crédito."
+ other_asset: "No se necesitan opciones adicionales para otros activos."
+ crypto: "Las cuentas de criptomonedas se configurarán para seguir posiciones y transacciones."
+ subtypes:
+ depository:
+ checking: "Corriente"
+ savings: "Ahorros"
+ hsa: "Cuenta de ahorros para la salud (HSA)"
+ cd: "Certificado de depósito"
+ money_market: "Mercado monetario"
+ investment:
+ brokerage: "Bróker"
+ pension: "Plan de pensiones"
+ retirement: "Jubilación"
+ "401k": "401(k)"
+ roth_401k: "Roth 401(k)"
+ "403b": "403(b)"
+ tsp: "Plan de ahorro TSP"
+ "529_plan": "Plan 529"
+ hsa: "Cuenta de ahorros para la salud (HSA)"
+ mutual_fund: "Fondo de inversión"
+ ira: "IRA tradicional"
+ roth_ira: "Roth IRA"
+ angel: "Capital riesgo / Angel"
+ loan:
+ mortgage: "Hipoteca"
+ student: "Préstamo estudiantil"
+ auto: "Préstamo de coche"
+ other: "Otro préstamo"
+ balance: "Saldo"
+ cancel: "Cancelar"
+ choose_account_type: "Elige el tipo de cuenta correcto para cada cuenta de Indexa Capital:"
+ create_accounts: "Crear cuentas"
+ creating_accounts: "Creando cuentas..."
+ historical_data_range: "Rango de datos históricos:"
+ subtitle: "Elige los tipos de cuenta correctos para tus cuentas importadas"
+ sync_start_date_help: "Selecciona desde qué fecha quieres sincronizar el historial de transacciones."
+ sync_start_date_label: "Empezar a sincronizar transacciones desde:"
+ title: "Configura tus cuentas de Indexa Capital"
+
+ complete_account_setup:
+ all_skipped: "Se han omitido todas las cuentas. No se ha creado ninguna."
+ creation_failed: "Error al crear las cuentas: %{error}"
+ no_accounts: "No hay cuentas para configurar."
+ success: "Se han creado correctamente %{count} cuenta(s)."
+
+ preload_accounts:
+ no_credentials_configured: "Por favor, configura primero tus credenciales de Indexa Capital en los ajustes del proveedor."
\ No newline at end of file
diff --git a/config/locales/views/indexa_capital_items/pl.yml b/config/locales/views/indexa_capital_items/pl.yml
new file mode 100644
index 000000000..abab63caa
--- /dev/null
+++ b/config/locales/views/indexa_capital_items/pl.yml
@@ -0,0 +1,247 @@
+---
+pl:
+ indexa_capital_items:
+ sync_status:
+ no_accounts: Nie znaleziono kont
+ synced:
+ one: Zsynchronizowano %{count} konto
+ few: Zsynchronizowano %{count} konta
+ many: Zsynchronizowano %{count} kont
+ other: Zsynchronizowano %{count} konta
+ synced_with_setup: Zsynchronizowano %{linked}, %{unlinked} wymaga konfiguracji
+ institution_summary:
+ none: Nie połączono żadnych instytucji
+ count:
+ one: "%{count} instytucja"
+ few: "%{count} instytucje"
+ many: "%{count} instytucji"
+ other: "%{count} instytucji"
+ errors:
+ provider_not_configured: Dostawca IndexaCapital nie jest skonfigurowany
+ sync:
+ status:
+ importing: Importowanie kont z IndexaCapital...
+ processing: Przetwarzanie pozycji i aktywności...
+ calculating: Obliczanie sald...
+ importing_data: Importowanie danych konta...
+ checking_setup: Sprawdzanie konfiguracji kont...
+ needs_setup: "%{count} kont wymaga konfiguracji..."
+ success: Synchronizacja rozpoczęta
+ panel:
+ setup_instructions: 'Instrukcje konfiguracji:'
+ step_1: Wejdź do panelu Indexa Capital, aby wygenerować token API tylko do odczytu
+ step_2: Wklej poniżej token API i kliknij Zapisz
+ step_3: Po pomyślnym połączeniu przejdź do zakładki Konta, aby skonfigurować nowe konta
+ field_descriptions: 'Opisy pól:'
+ optional: "(opcjonalne)"
+ required: "(wymagane)"
+ optional_with_default: "(opcjonalne, domyślnie %{default_value})"
+ alternative_auth: Lub użyj uwierzytelniania loginem i hasłem...
+ save_button: Zapisz konfigurację
+ update_button: Zaktualizuj konfigurację
+ status_configured_html: Skonfigurowano i gotowe do użycia. Przejdź do zakładki Konta, aby zarządzać kontami i je konfigurować.
+ status_not_configured: Nie skonfigurowano
+ fields:
+ api_token:
+ label: Token API
+ description: Twój token API tylko do odczytu z panelu Indexa Capital
+ placeholder_new: Wklej tutaj swój token API
+ placeholder_update: Wprowadź nowy token API, aby zaktualizować
+ username:
+ label: Nazwa użytkownika
+ description: Twoja nazwa użytkownika lub e-mail w Indexa Capital
+ placeholder_new: Wklej tutaj nazwę użytkownika
+ placeholder_update: Wprowadź nową nazwę użytkownika, aby zaktualizować
+ document:
+ label: ID dokumentu
+ description: Twój dokument lub identyfikator w Indexa Capital
+ placeholder_new: Wklej tutaj identyfikator dokumentu
+ placeholder_update: Wprowadź nowy identyfikator dokumentu, aby zaktualizować
+ password:
+ label: Hasło
+ description: Twoje hasło do Indexa Capital
+ placeholder_new: Wklej tutaj hasło
+ placeholder_update: Wprowadź nowe hasło, aby zaktualizować
+ create:
+ success: Połączenie IndexaCapital zostało pomyślnie utworzone
+ update:
+ success: Połączenie IndexaCapital zostało zaktualizowane
+ destroy:
+ success: Połączenie IndexaCapital zostało usunięte
+ index:
+ title: Połączenia IndexaCapital
+ loading:
+ loading_message: Ładowanie kont IndexaCapital...
+ loading_title: Ładowanie
+ link_accounts:
+ all_already_linked:
+ one: Wybrane konto (%{names}) jest już połączone
+ few: 'Wszystkie %{count} wybrane konta są już połączone: %{names}'
+ many: 'Wszystkie %{count} wybranych kont są już połączone: %{names}'
+ other: 'Wszystkie %{count} wybrane konta są już połączone: %{names}'
+ api_error: 'Błąd API: %{message}'
+ invalid_account_names:
+ one: Nie można połączyć konta bez nazwy
+ few: Nie można połączyć %{count} kont bez nazw
+ many: Nie można połączyć %{count} kont bez nazw
+ other: Nie można połączyć %{count} kont bez nazw
+ link_failed: Nie udało się połączyć kont
+ no_accounts_selected: Wybierz co najmniej jedno konto
+ no_api_key: Nie znaleziono danych logowania IndexaCapital. Skonfiguruj je w ustawieniach dostawców.
+ partial_invalid: Pomyślnie połączono %{created_count} kont, %{already_linked_count} było już połączonych, a %{invalid_count} miało nieprawidłowe nazwy
+ partial_success: 'Pomyślnie połączono %{created_count} kont. %{already_linked_count} było już połączonych: %{already_linked_names}'
+ success:
+ one: Pomyślnie połączono %{count} konto
+ few: Pomyślnie połączono %{count} konta
+ many: Pomyślnie połączono %{count} kont
+ other: Pomyślnie połączono %{count} konta
+ indexa_capital_item:
+ accounts_need_setup: Konta wymagają konfiguracji
+ delete: Usuń połączenie
+ deletion_in_progress: trwa usuwanie...
+ error: Błąd
+ more_accounts_available:
+ one: Dostępne jest jeszcze %{count} konto
+ few: Dostępne są jeszcze %{count} konta
+ many: Dostępnych jest jeszcze %{count} kont
+ other: Dostępnych jest jeszcze %{count} konta
+ no_accounts_description: To połączenie nie ma jeszcze żadnych połączonych kont.
+ no_accounts_title: Brak kont
+ provider_name: IndexaCapital
+ requires_update: Połączenie wymaga aktualizacji
+ setup_action: Skonfiguruj nowe konta
+ setup_description: Połączono %{linked} z %{total} kont. Wybierz typy dla nowo zaimportowanych kont IndexaCapital.
+ setup_needed: Nowe konta gotowe do konfiguracji
+ status: Zsynchronizowano %{timestamp} temu — %{summary}
+ status_never: Nigdy nie synchronizowano
+ syncing: Trwa synchronizacja...
+ total: Łącznie
+ unlinked: Niepołączone
+ update_credentials: Zaktualizuj dane uwierzytelniające
+ select_accounts:
+ accounts_selected: wybrano konta
+ api_error: 'Błąd API: %{message}'
+ cancel: Anuluj
+ configure_name_in_provider: Nie można importować. Skonfiguruj nazwę konta w IndexaCapital.
+ description: Wybierz konta, które chcesz połączyć ze swoim kontem %{product_name}.
+ link_accounts: Połącz wybrane konta
+ no_accounts_found: Nie znaleziono kont. Sprawdź dane logowania IndexaCapital.
+ no_api_key: Dane logowania IndexaCapital nie są skonfigurowane. Skonfiguruj je w Ustawieniach.
+ no_credentials_configured: Najpierw skonfiguruj dane logowania IndexaCapital w ustawieniach dostawców.
+ no_name_placeholder: "(Brak nazwy)"
+ title: Wybierz konta IndexaCapital
+ select_existing_account:
+ account_already_linked: To konto jest już połączone z dostawcą
+ all_accounts_already_linked: Wszystkie konta IndexaCapital są już połączone
+ api_error: 'Błąd API: %{message}'
+ balance_label: 'Saldo:'
+ cancel: Anuluj
+ cancel_button: Anuluj
+ configure_name_in_provider: Nie można importować. Skonfiguruj nazwę konta w IndexaCapital.
+ connect_hint: Połącz konto IndexaCapital, aby włączyć automatyczną synchronizację.
+ description: Wybierz konto IndexaCapital, które chcesz połączyć z tym kontem. Transakcje będą synchronizowane i automatycznie deduplikowane.
+ header: Połącz z IndexaCapital
+ link_account: Połącz konto
+ link_button: Połącz to konto
+ linking_to: 'Łączenie z:'
+ no_account_specified: Nie wskazano konta
+ no_accounts: Nie znaleziono niepołączonych kont IndexaCapital.
+ no_accounts_found: Nie znaleziono kont IndexaCapital. Sprawdź dane logowania.
+ no_api_key: Dane logowania IndexaCapital nie są skonfigurowane. Skonfiguruj je w Ustawieniach.
+ no_credentials_configured: Najpierw skonfiguruj dane logowania IndexaCapital w ustawieniach dostawców.
+ no_name_placeholder: "(Brak nazwy)"
+ settings_link: Przejdź do ustawień dostawców
+ subtitle: Wybierz konto IndexaCapital
+ title: Połącz %{account_name} z IndexaCapital
+ link_existing_account:
+ account_already_linked: To konto jest już połączone z dostawcą
+ api_error: 'Błąd API: %{message}'
+ invalid_account_name: Nie można połączyć konta bez nazwy
+ provider_account_already_linked: To konto IndexaCapital jest już połączone z innym kontem
+ provider_account_not_found: Nie znaleziono konta IndexaCapital
+ missing_parameters: Brakuje wymaganych parametrów
+ no_api_key: Nie znaleziono danych logowania IndexaCapital. Skonfiguruj je w ustawieniach dostawców.
+ success: Pomyślnie połączono %{account_name} z IndexaCapital
+ setup_accounts:
+ account_type_label: 'Typ konta:'
+ accounts_count:
+ one: Dostępne %{count} konto
+ few: Dostępne %{count} konta
+ many: Dostępnych %{count} kont
+ other: Dostępnych %{count} konta
+ all_accounts_linked: Wszystkie Twoje konta IndexaCapital zostały już skonfigurowane.
+ api_error: 'Błąd API: %{message}'
+ creating: Tworzenie kont...
+ fetch_failed: Nie udało się pobrać kont
+ import_selected: Importuj wybrane konta
+ instructions: Wybierz konta, które chcesz zaimportować z IndexaCapital. Możesz wybrać wiele kont.
+ no_accounts: Nie znaleziono niepołączonych kont dla tego połączenia IndexaCapital.
+ no_accounts_to_setup: Brak kont do skonfigurowania
+ no_api_key: Dane logowania IndexaCapital nie są skonfigurowane. Sprawdź ustawienia połączenia.
+ select_all: Zaznacz wszystkie
+ account_types:
+ skip: Pomiń to konto
+ depository: Konto bieżące lub oszczędnościowe
+ credit_card: Karta kredytowa
+ investment: Konto inwestycyjne
+ crypto: Konto kryptowalutowe
+ loan: Pożyczka lub hipoteka
+ other_asset: Inne aktywo
+ subtype_labels:
+ depository: 'Podtyp konta:'
+ credit_card: ''
+ investment: 'Typ inwestycji:'
+ crypto: ''
+ loan: 'Typ pożyczki:'
+ other_asset: ''
+ subtype_messages:
+ credit_card: Karty kredytowe zostaną automatycznie skonfigurowane jako konta kart kredytowych.
+ other_asset: Dla innych aktywów nie są potrzebne dodatkowe opcje.
+ crypto: Konta kryptowalutowe zostaną skonfigurowane do śledzenia pozycji i transakcji.
+ subtypes:
+ depository:
+ checking: Bieżące
+ savings: Oszczędnościowe
+ hsa: Konto oszczędnościowe na cele zdrowotne
+ cd: Lokata terminowa
+ money_market: Rynek pieniężny
+ investment:
+ brokerage: Maklerskie
+ pension: Emerytalne
+ retirement: Emerytalne
+ 401k: 401(k)
+ roth_401k: Roth 401(k)
+ 403b: 403(b)
+ tsp: Plan oszczędnościowy TSP
+ 529_plan: Plan 529
+ hsa: Konto oszczędnościowe na cele zdrowotne
+ mutual_fund: Fundusz inwestycyjny
+ ira: Tradycyjne IRA
+ roth_ira: Roth IRA
+ angel: Inwestycja anielska
+ loan:
+ mortgage: Hipoteka
+ student: Pożyczka studencka
+ auto: Kredyt samochodowy
+ other: Inna pożyczka
+ balance: Saldo
+ cancel: Anuluj
+ choose_account_type: 'Wybierz poprawny typ konta dla każdego konta IndexaCapital:'
+ create_accounts: Utwórz konta
+ creating_accounts: Tworzenie kont...
+ historical_data_range: 'Zakres danych historycznych:'
+ subtitle: Wybierz poprawne typy dla importowanych kont
+ sync_start_date_help: Wybierz, jak daleko wstecz chcesz synchronizować historię transakcji.
+ sync_start_date_label: 'Rozpocznij synchronizację transakcji od:'
+ title: Skonfiguruj konta IndexaCapital
+ complete_account_setup:
+ all_skipped: Wszystkie konta zostały pominięte. Nie utworzono żadnych kont.
+ creation_failed: 'Nie udało się utworzyć kont: %{error}'
+ no_accounts: Brak kont do skonfigurowania.
+ success:
+ one: Pomyślnie utworzono %{count} konto.
+ few: Pomyślnie utworzono %{count} konta.
+ many: Pomyślnie utworzono %{count} kont.
+ other: Pomyślnie utworzono %{count} konta.
+ preload_accounts:
+ no_credentials_configured: Najpierw skonfiguruj dane logowania IndexaCapital w ustawieniach dostawców.
diff --git a/config/locales/views/investments/de.yml b/config/locales/views/investments/de.yml
index b7ba4e403..ba6066fdb 100644
--- a/config/locales/views/investments/de.yml
+++ b/config/locales/views/investments/de.yml
@@ -10,6 +10,109 @@ de:
title: Kontostand eingeben
show:
chart_title: Gesamtwert
+ subtypes:
+ brokerage:
+ short: Brokerage
+ long: Brokerage
+ "401k":
+ short: "401(k)"
+ long: "401(k)"
+ roth_401k:
+ short: Roth 401(k)
+ long: Roth 401(k)
+ "403b":
+ short: "403(b)"
+ long: "403(b)"
+ "457b":
+ short: "457(b)"
+ long: "457(b)"
+ tsp:
+ short: TSP
+ long: Thrift Savings Plan
+ ira:
+ short: IRA
+ long: Traditionelles IRA
+ roth_ira:
+ short: Roth IRA
+ long: Roth IRA
+ sep_ira:
+ short: SEP IRA
+ long: SEP IRA
+ simple_ira:
+ short: SIMPLE IRA
+ long: SIMPLE IRA
+ "529_plan":
+ short: "529 Plan"
+ long: "529 Bildungssparplan"
+ hsa:
+ short: HSA
+ long: Health Savings Account
+ ugma:
+ short: UGMA
+ long: UGMA-Treuhandkonto
+ utma:
+ short: UTMA
+ long: UTMA-Treuhandkonto
+ isa:
+ short: ISA
+ long: Individual Savings Account
+ lisa:
+ short: LISA
+ long: Lifetime ISA
+ sipp:
+ short: SIPP
+ long: Self-Invested Personal Pension
+ workplace_pension_uk:
+ short: Pension
+ long: Betriebliche Altersvorsorge
+ rrsp:
+ short: RRSP
+ long: Registered Retirement Savings Plan
+ tfsa:
+ short: TFSA
+ long: Tax-Free Savings Account
+ resp:
+ short: RESP
+ long: Registered Education Savings Plan
+ lira:
+ short: LIRA
+ long: Locked-In Retirement Account
+ rrif:
+ short: RRIF
+ long: Registered Retirement Income Fund
+ super:
+ short: Super
+ long: Superannuation
+ smsf:
+ short: SMSF
+ long: Self-Managed Super Fund
+ pea:
+ short: PEA
+ long: Plan d'Épargne en Actions
+ pillar_3a:
+ short: Säule 3a
+ long: Private Vorsorge (Säule 3a)
+ riester:
+ short: Riester
+ long: Riester-Rente
+ pension:
+ short: Pension
+ long: Pension
+ retirement:
+ short: Ruhestand
+ long: Ruhestandskonto
+ mutual_fund:
+ short: Fonds
+ long: Investmentfonds
+ angel:
+ short: Angel
+ long: Angel-Investment
+ trust:
+ short: Trust
+ long: Trust
+ other:
+ short: Sonstige
+ long: Sonstige Anlage
value_tooltip:
cash: Bargeld
holdings: Positionen
diff --git a/config/locales/views/investments/es.yml b/config/locales/views/investments/es.yml
index 14db2128f..c1a3ac0e3 100644
--- a/config/locales/views/investments/es.yml
+++ b/config/locales/views/investments/es.yml
@@ -10,8 +10,117 @@ es:
title: Introduce el saldo de la cuenta
show:
chart_title: Valor total
+ subtypes:
+ # Estados Unidos
+ brokerage:
+ short: Corretaje
+ long: Cuenta de corretaje (Brokerage)
+ 401k:
+ short: 401(k)
+ long: Plan de jubilación 401(k)
+ roth_401k:
+ short: Roth 401(k)
+ long: Plan de jubilación Roth 401(k)
+ 403b:
+ short: 403(b)
+ long: Plan de jubilación 403(b)
+ 457b:
+ short: 457(b)
+ long: Plan de jubilación 457(b)
+ tsp:
+ short: TSP
+ long: Thrift Savings Plan (Plan de ahorro para empleados federales)
+ ira:
+ short: IRA
+ long: Cuenta de jubilación individual (Traditional IRA)
+ roth_ira:
+ short: Roth IRA
+ long: Cuenta de jubilación individual Roth (Roth IRA)
+ sep_ira:
+ short: SEP IRA
+ long: SEP IRA (Plan de pensión simplificado para empleados)
+ simple_ira:
+ short: SIMPLE IRA
+ long: SIMPLE IRA (Plan de incentivos para empleados)
+ 529_plan:
+ short: Plan 529
+ long: Plan 529 de ahorro para educación
+ hsa:
+ short: HSA
+ long: Cuenta de ahorros para la salud (Health Savings Account)
+ ugma:
+ short: UGMA
+ long: Cuenta de custodia UGMA
+ utma:
+ short: UTMA
+ long: Cuenta de custodia UTMA
+ # Reino Unido
+ isa:
+ short: ISA
+ long: Cuenta de ahorro individual (ISA)
+ lisa:
+ short: LISA
+ long: ISA vitalicia (Lifetime ISA)
+ sipp:
+ short: SIPP
+ long: Pensión personal con gestión propia (SIPP)
+ workplace_pension_uk:
+ short: Pensión
+ long: Pensión del lugar de trabajo
+ # Canadá
+ rrsp:
+ short: RRSP
+ long: Plan registrado de ahorro para la jubilación (RRSP)
+ tfsa:
+ short: TFSA
+ long: Cuenta de ahorros libre de impuestos (TFSA)
+ resp:
+ short: RESP
+ long: Plan registrado de ahorros para educación (RESP)
+ lira:
+ short: LIRA
+ long: Cuenta de jubilación inmovilizada (LIRA)
+ rrif:
+ short: RRIF
+ long: Fondo registrado de ingresos para la jubilación (RRIF)
+ # Australia
+ super:
+ short: Super
+ long: Superannuation (Fondo de pensiones australiano)
+ smsf:
+ short: SMSF
+ long: Fondo de pensiones gestionado por uno mismo (SMSF)
+ # Europa
+ pea:
+ short: PEA
+ long: Plan de ahorro en acciones (PEA - Francia)
+ pillar_3a:
+ short: Pilar 3a
+ long: Pensión privada (Pilar 3a - Suiza)
+ riester:
+ short: Riester
+ long: Plan de pensiones Riester (Riester-Rente - Alemania)
+ # Genéricos
+ pension:
+ short: Pensión
+ long: Plan de pensiones
+ retirement:
+ short: Jubilación
+ long: Cuenta de jubilación
+ mutual_fund:
+ short: Fondo de inversión
+ long: Fondo de inversión (Mutual Fund)
+ angel:
+ short: Angel
+ long: Inversión Angel (Capital riesgo)
+ trust:
+ short: Fideicomiso
+ long: Fideicomiso (Trust)
+ other:
+ short: Otra
+ long: Otra inversión
value_tooltip:
cash: Efectivo
holdings: Inversiones
total: Saldo de la cartera
- total_value_tooltip: El saldo total de la cartera es la suma del efectivo de corretaje (disponible para operar) y el valor de mercado actual de tus inversiones.
+ total_value_tooltip: El saldo total de la cartera es la suma del efectivo de corretaje (disponible para operar) y el valor de mercado actual de tus inversiones.
\ No newline at end of file
diff --git a/config/locales/views/investments/pl.yml b/config/locales/views/investments/pl.yml
new file mode 100644
index 000000000..94ab55f34
--- /dev/null
+++ b/config/locales/views/investments/pl.yml
@@ -0,0 +1,120 @@
+---
+pl:
+ investments:
+ edit:
+ edit: Edytuj %{account}
+ form:
+ none: Brak
+ subtype_prompt: Wybierz typ inwestycji
+ new:
+ title: Wprowadź saldo konta
+ show:
+ chart_title: Łączna wartość
+ subtypes:
+ brokerage:
+ short: Maklerskie
+ long: Konto maklerskie
+ 401k:
+ short: 401(k)
+ long: 401(k)
+ roth_401k:
+ short: Roth 401(k)
+ long: Roth 401(k)
+ 403b:
+ short: 403(b)
+ long: 403(b)
+ 457b:
+ short: 457(b)
+ long: 457(b)
+ tsp:
+ short: TSP
+ long: Plan oszczędnościowy Thrift
+ ira:
+ short: IRA
+ long: Tradycyjne IRA
+ roth_ira:
+ short: Roth IRA
+ long: Roth IRA
+ sep_ira:
+ short: SEP IRA
+ long: SEP IRA
+ simple_ira:
+ short: SIMPLE IRA
+ long: SIMPLE IRA
+ 529_plan:
+ short: 529 Plan
+ long: Edukacyjny plan oszczędnościowy 529
+ hsa:
+ short: HSA
+ long: Konto oszczędnościowe na cele zdrowotne
+ ugma:
+ short: UGMA
+ long: Powiernicze konto UGMA
+ utma:
+ short: UTMA
+ long: Powiernicze konto UTMA
+ isa:
+ short: ISA
+ long: Indywidualne konto oszczędnościowe
+ lisa:
+ short: LISA
+ long: Dożywotnie konto ISA
+ sipp:
+ short: SIPP
+ long: Indywidualna emerytura samodzielnie inwestowana
+ workplace_pension_uk:
+ short: Emerytalne
+ long: Emerytura pracownicza
+ rrsp:
+ short: RRSP
+ long: Zarejestrowany emerytalny plan oszczędnościowy
+ tfsa:
+ short: TFSA
+ long: Konto oszczędnościowe zwolnione z podatku
+ resp:
+ short: RESP
+ long: Zarejestrowany edukacyjny plan oszczędnościowy
+ lira:
+ short: LIRA
+ long: Zablokowane konto emerytalne
+ rrif:
+ short: RRIF
+ long: Zarejestrowany fundusz dochodu emerytalnego
+ super:
+ short: Super
+ long: Fundusz emerytalny
+ smsf:
+ short: SMSF
+ long: Samodzielnie zarządzany fundusz emerytalny
+ pea:
+ short: PEA
+ long: Plan oszczędnościowy w akcjach (PEA)
+ pillar_3a:
+ short: Pillar 3a
+ long: Prywatna emerytura (Filar 3a)
+ riester:
+ short: Riester
+ long: Riester-Rente
+ pension:
+ short: Emerytalne
+ long: Emerytalne
+ retirement:
+ short: Emerytalne
+ long: Konto emerytalne
+ mutual_fund:
+ short: Fundusz
+ long: Fundusz inwestycyjny
+ angel:
+ short: Anioł
+ long: Inwestycja anielska
+ trust:
+ short: Trust
+ long: Trust
+ other:
+ short: Inne
+ long: Inna inwestycja
+ value_tooltip:
+ cash: Gotówka
+ holdings: Pozycje
+ total: Saldo portfela
+ total_value_tooltip: Łączne saldo portfela to suma gotówki maklerskiej (dostępnej do handlu) oraz aktualnej wartości rynkowej Twoich pozycji.
diff --git a/config/locales/views/invitation_mailer/pl.yml b/config/locales/views/invitation_mailer/pl.yml
new file mode 100644
index 000000000..dadffa0a8
--- /dev/null
+++ b/config/locales/views/invitation_mailer/pl.yml
@@ -0,0 +1,8 @@
+---
+pl:
+ invitation_mailer:
+ invite_email:
+ accept_button: Zaakceptuj zaproszenie
+ body: "%{inviter} zaprosił(a) Cię do dołączenia do %{family} %{moniker} w %{product_name}!"
+ expiry_notice: To zaproszenie wygaśnie za %{days} dni
+ greeting: Witamy w %{product_name}!
diff --git a/config/locales/views/invitations/de.yml b/config/locales/views/invitations/de.yml
index 759899268..8aec53344 100644
--- a/config/locales/views/invitations/de.yml
+++ b/config/locales/views/invitations/de.yml
@@ -1,7 +1,14 @@
---
de:
invitations:
+ accept_choice:
+ create_account: Neues Konto erstellen
+ joined_household: Sie sind dem Haushalt beigetreten.
+ message: "%{inviter} hat Sie eingeladen, als %{role} beizutreten."
+ sign_in_existing: Ich habe bereits ein Konto
+ title: "%{family} beitreten"
create:
+ existing_user_added: Der Benutzer wurde Ihrem Haushalt hinzugefügt.
failure: Einladung konnte nicht gesendet werden.
success: Einladung erfolgreich gesendet.
destroy:
@@ -12,6 +19,7 @@ de:
email_label: E-Mail-Adresse
email_placeholder: E-Mail-Adresse eingeben
role_admin: Administrator
+ role_guest: Gast
role_label: Rolle
role_member: Mitglied
submit: Einladung senden
diff --git a/config/locales/views/invitations/es.yml b/config/locales/views/invitations/es.yml
index b17da8342..d47edf372 100644
--- a/config/locales/views/invitations/es.yml
+++ b/config/locales/views/invitations/es.yml
@@ -1,7 +1,14 @@
---
es:
invitations:
+ accept_choice:
+ create_account: Crear cuenta nueva
+ joined_household: Te has unido a la unidad familiar.
+ message: "%{inviter} te ha invitado a unirte como %{role}."
+ sign_in_existing: Ya tengo una cuenta
+ title: Unirse a %{family}
create:
+ existing_user_added: El usuario ha sido añadido a tu unidad familiar.
failure: No se pudo enviar la invitación
success: Invitación enviada con éxito
destroy:
@@ -12,8 +19,9 @@ es:
email_label: Dirección de correo electrónico
email_placeholder: Introduce la dirección de correo electrónico
role_admin: Administrador
+ role_guest: Invitado
role_label: Rol
role_member: Miembro
submit: Enviar invitación
- subtitle: Envía una invitación para unirte a tu cuenta familiar en Maybe
+ subtitle: Envía una invitación para unirte a tu cuenta familiar en %{product_name}
title: Invitar a alguien
diff --git a/config/locales/views/invitations/pl.yml b/config/locales/views/invitations/pl.yml
new file mode 100644
index 000000000..61e18dc0b
--- /dev/null
+++ b/config/locales/views/invitations/pl.yml
@@ -0,0 +1,27 @@
+---
+pl:
+ invitations:
+ accept_choice:
+ create_account: Utwórz nowe konto
+ joined_household: Dołączono do gospodarstwa domowego.
+ message: "%{inviter} zaprosił(a) Cię jako %{role}."
+ sign_in_existing: Mam już konto
+ title: Dołącz do %{family}
+ create:
+ existing_user_added: Użytkownik został dodany do Twojego gospodarstwa domowego.
+ failure: Nie udało się wysłać zaproszenia
+ success: Zaproszenie zostało wysłane
+ destroy:
+ failure: Wystąpił problem podczas usuwania zaproszenia.
+ not_authorized: Nie masz uprawnień do zarządzania zaproszeniami.
+ success: Zaproszenie zostało pomyślnie usunięte.
+ new:
+ email_label: Adres e-mail
+ email_placeholder: Wpisz adres e-mail
+ role_admin: Administrator
+ role_guest: Gość
+ role_label: Rola
+ role_member: Członek
+ submit: Wyślij zaproszenie
+ subtitle: Wyślij zaproszenie do dołączenia do Twojego konta %{moniker} w %{product_name}
+ title: Zaproś kogoś
diff --git a/config/locales/views/invite_codes/pl.yml b/config/locales/views/invite_codes/pl.yml
new file mode 100644
index 000000000..ef9108f60
--- /dev/null
+++ b/config/locales/views/invite_codes/pl.yml
@@ -0,0 +1,6 @@
+---
+pl:
+ invite_codes:
+ index:
+ invite_code_description: Wygeneruj nowy kod, aby zobaczyć go tutaj. Wykorzystane kody nie są już wyświetlane.
+ no_invite_codes: Brak kodów do wyświetlenia
diff --git a/config/locales/views/layout/ca.yml b/config/locales/views/layout/ca.yml
index 5f6a3b9f8..8868c451c 100644
--- a/config/locales/views/layout/ca.yml
+++ b/config/locales/views/layout/ca.yml
@@ -2,6 +2,7 @@
ca:
layouts:
application:
+ privacy_mode: Alternar mode de privadesa
nav:
assistant: Assistent
budgets: Pressupostos
diff --git a/config/locales/views/layout/de.yml b/config/locales/views/layout/de.yml
index 55626797c..4738880b3 100644
--- a/config/locales/views/layout/de.yml
+++ b/config/locales/views/layout/de.yml
@@ -2,6 +2,7 @@
de:
layouts:
application:
+ privacy_mode: Datenschutzmodus umschalten
nav:
assistant: Assistent
budgets: Budgets
diff --git a/config/locales/views/layout/en.yml b/config/locales/views/layout/en.yml
index 702f68746..dfb4ffaed 100644
--- a/config/locales/views/layout/en.yml
+++ b/config/locales/views/layout/en.yml
@@ -2,6 +2,7 @@
en:
layouts:
application:
+ privacy_mode: Toggle privacy mode
nav:
assistant: Assistant
budgets: Budgets
diff --git a/config/locales/views/layout/es.yml b/config/locales/views/layout/es.yml
index bc14dd208..b1627fd14 100644
--- a/config/locales/views/layout/es.yml
+++ b/config/locales/views/layout/es.yml
@@ -2,6 +2,7 @@
es:
layouts:
application:
+ privacy_mode: Alternar modo de privacidad
nav:
assistant: Asistente
budgets: Presupuestos
diff --git a/config/locales/views/layout/fr.yml b/config/locales/views/layout/fr.yml
index fb99e913e..54f9cca4b 100644
--- a/config/locales/views/layout/fr.yml
+++ b/config/locales/views/layout/fr.yml
@@ -2,6 +2,7 @@
fr:
layouts:
application:
+ privacy_mode: Activer/désactiver le mode confidentialité
nav:
assistant: Assistant
budgets: Budgets
diff --git a/config/locales/views/layout/nb.yml b/config/locales/views/layout/nb.yml
index 42aeca716..ebabc16cf 100644
--- a/config/locales/views/layout/nb.yml
+++ b/config/locales/views/layout/nb.yml
@@ -2,6 +2,7 @@
nb:
layouts:
application:
+ privacy_mode: Veksle personvernmodus
nav:
assistant: Assistent
budgets: Budsjett
diff --git a/config/locales/views/layout/nl.yml b/config/locales/views/layout/nl.yml
index 43bee1c11..9ee7a2b1b 100644
--- a/config/locales/views/layout/nl.yml
+++ b/config/locales/views/layout/nl.yml
@@ -2,6 +2,7 @@
nl:
layouts:
application:
+ privacy_mode: Privacymodus in-/uitschakelen
nav:
assistant: Assistent
budgets: Budgetten
diff --git a/config/locales/views/layout/pl.yml b/config/locales/views/layout/pl.yml
new file mode 100644
index 000000000..fe70cadf6
--- /dev/null
+++ b/config/locales/views/layout/pl.yml
@@ -0,0 +1,24 @@
+---
+pl:
+ layouts:
+ application:
+ privacy_mode: Przełącz tryb prywatności
+ nav:
+ assistant: Asystent
+ budgets: Budżety
+ home: Strona główna
+ reports: Raporty
+ transactions: Transakcje
+ auth:
+ existing_account: Masz już konto?
+ no_account: Nowy użytkownik w %{product_name}?
+ sign_in: Zaloguj się
+ sign_up: Załóż konto
+ shared:
+ footer:
+ privacy_policy: Polityka prywatności
+ terms_of_service: Warunki korzystania
+ trial:
+ open_demo: Otwórz demo
+ data_deleted_in_days: Dane zostaną usunięte za %{days} dni
+ contribute: Wesprzyj
diff --git a/config/locales/views/layout/pt-BR.yml b/config/locales/views/layout/pt-BR.yml
index 580269edf..2f76fecca 100644
--- a/config/locales/views/layout/pt-BR.yml
+++ b/config/locales/views/layout/pt-BR.yml
@@ -2,6 +2,7 @@
pt-BR:
layouts:
application:
+ privacy_mode: Alternar modo de privacidade
nav:
assistant: Assistente
budgets: Orçamentos
diff --git a/config/locales/views/layout/ro.yml b/config/locales/views/layout/ro.yml
index fc2f35f0a..15ed02dac 100644
--- a/config/locales/views/layout/ro.yml
+++ b/config/locales/views/layout/ro.yml
@@ -2,6 +2,7 @@
ro:
layouts:
application:
+ privacy_mode: Comutare mod confidențialitate
nav:
assistant: Asistent
budgets: Bugete
diff --git a/config/locales/views/layout/tr.yml b/config/locales/views/layout/tr.yml
index cff503f7c..d7a482e42 100644
--- a/config/locales/views/layout/tr.yml
+++ b/config/locales/views/layout/tr.yml
@@ -2,6 +2,7 @@
tr:
layouts:
application:
+ privacy_mode: Gizlilik modunu değiştir
nav:
assistant: Asistan
budgets: Bütçeler
diff --git a/config/locales/views/layout/zh-CN.yml b/config/locales/views/layout/zh-CN.yml
index 2d22156d1..8fc58045e 100644
--- a/config/locales/views/layout/zh-CN.yml
+++ b/config/locales/views/layout/zh-CN.yml
@@ -3,6 +3,7 @@
zh-CN:
layouts:
application:
+ privacy_mode: 切换隐私模式
nav:
assistant: 智能助手
budgets: 预算管理
diff --git a/config/locales/views/layout/zh-TW.yml b/config/locales/views/layout/zh-TW.yml
index 9c1b4da94..091625bea 100644
--- a/config/locales/views/layout/zh-TW.yml
+++ b/config/locales/views/layout/zh-TW.yml
@@ -2,6 +2,7 @@
zh-TW:
layouts:
application:
+ privacy_mode: 切換隱私模式
nav:
assistant: 助手
budgets: 預算
diff --git a/config/locales/views/loans/en.yml b/config/locales/views/loans/en.yml
index 33eb76f33..157c01cf2 100644
--- a/config/locales/views/loans/en.yml
+++ b/config/locales/views/loans/en.yml
@@ -10,6 +10,8 @@ en:
rate_type: Rate type
term_months: Term (months)
term_months_placeholder: '360'
+ subtype_prompt: Select loan type
+ subtype_none: None
new:
title: Enter loan details
overview:
diff --git a/config/locales/views/loans/pl.yml b/config/locales/views/loans/pl.yml
new file mode 100644
index 000000000..2ea423f2d
--- /dev/null
+++ b/config/locales/views/loans/pl.yml
@@ -0,0 +1,23 @@
+---
+pl:
+ loans:
+ edit:
+ edit: Edytuj %{account}
+ form:
+ interest_rate: Oprocentowanie
+ interest_rate_placeholder: '5.25'
+ initial_balance: Początkowe saldo pożyczki
+ rate_type: Typ oprocentowania
+ term_months: Okres (miesiące)
+ term_months_placeholder: '360'
+ new:
+ title: Wprowadź dane pożyczki
+ overview:
+ interest_rate: Oprocentowanie
+ monthly_payment: Rata miesięczna
+ not_applicable: Nie dotyczy
+ original_principal: Kapitał początkowy
+ remaining_principal: Pozostały kapitał
+ term: Okres
+ type: Typ
+ unknown: Nieznane
diff --git a/config/locales/views/lunchflow_items/de.yml b/config/locales/views/lunchflow_items/de.yml
index e25330471..5aa783ebc 100644
--- a/config/locales/views/lunchflow_items/de.yml
+++ b/config/locales/views/lunchflow_items/de.yml
@@ -1,62 +1,145 @@
+---
de:
lunchflow_items:
create:
- success: Lunch-Flow-Verbindung erfolgreich erstellt
+ success: Lunch‑Flow-Verbindung erfolgreich erstellt
destroy:
- success: Lunch-Flow-Verbindung entfernt
+ success: Lunch‑Flow-Verbindung entfernt
index:
- title: Lunch-Flow-Verbindungen
+ title: Lunch‑Flow-Verbindungen
loading:
- loading_message: Lunch-Flow-Konten werden geladen...
+ loading_message: Lunch‑Flow-Konten werden geladen...
loading_title: Wird geladen
link_accounts:
all_already_linked:
one: "Das ausgewählte Konto (%{names}) ist bereits verknüpft"
other: "Alle %{count} ausgewählten Konten sind bereits verknüpft: %{names}"
api_error: "API-Fehler: %{message}"
+ invalid_account_names:
+ one: "Konto mit leerem Namen kann nicht verknüpft werden"
+ other: "%{count} Konten mit leerem Namen können nicht verknüpft werden"
link_failed: Konten konnten nicht verknüpft werden
no_accounts_selected: Bitte wähle mindestens ein Konto aus
+ partial_invalid: "%{created_count} Konto/Konten verknüpft, %{already_linked_count} waren bereits verknüpft, %{invalid_count} hatten ungültige Namen"
partial_success: "%{created_count} Konto/Konten erfolgreich verknüpft. %{already_linked_count} Konto/Konten waren bereits verknüpft: %{already_linked_names}"
success:
one: "%{count} Konto erfolgreich verknüpft"
other: "%{count} Konten erfolgreich verknüpft"
lunchflow_item:
+ accounts_need_setup: Konten müssen eingerichtet werden
delete: Verbindung löschen
deletion_in_progress: Löschung wird durchgeführt...
error: Fehler
no_accounts_description: Diese Verbindung enthält derzeit keine verknüpften Konten.
no_accounts_title: Keine Konten
+ setup_action: Neue Konten einrichten
+ setup_description: "%{linked} von %{total} Konten verknüpft. Wähle die richtigen Kontotypen für deine neu importierten Lunch‑Flow-Konten."
+ setup_needed: Neue Konten bereit zur Einrichtung
status: "Vor %{timestamp} synchronisiert"
status_never: Noch nie synchronisiert
+ status_with_summary: "Zuletzt vor %{timestamp} synchronisiert • %{summary}"
syncing: Wird synchronisiert...
+ total: Gesamt
+ unlinked: Nicht verknüpft
select_accounts:
accounts_selected: Konten ausgewählt
api_error: "API-Fehler: %{message}"
cancel: Abbrechen
+ configure_name_in_lunchflow: Import nicht möglich – bitte Kontoname in Lunch‑Flow konfigurieren
description: Wähle die Konten aus, die du mit deinem %{product_name}-Konto verknüpfen möchtest.
link_accounts: Ausgewählte Konten verknüpfen
no_accounts_found: Keine Konten gefunden. Bitte überprüfe deine API-Key-Konfiguration.
- no_api_key: Lunch-Flow-API-Schlüssel ist nicht konfiguriert. Bitte konfiguriere ihn in den Einstellungen.
- title: Lunch-Flow-Konten auswählen
+ no_api_key: Lunch‑Flow-API-Schlüssel ist nicht konfiguriert. Bitte konfiguriere ihn in den Einstellungen.
+ no_name_placeholder: "(Kein Name)"
+ title: Lunch‑Flow-Konten auswählen
select_existing_account:
account_already_linked: Dieses Konto ist bereits mit einem Anbieter verknüpft
- all_accounts_already_linked: Alle Lunch-Flow-Konten sind bereits verknüpft
+ all_accounts_already_linked: Alle Lunch‑Flow-Konten sind bereits verknüpft
api_error: "API-Fehler: %{message}"
cancel: Abbrechen
- description: Wähle ein Lunch-Flow-Konto aus, um es mit diesem Konto zu verknüpfen. Transaktionen werden automatisch synchronisiert und doppelte Einträge entfernt.
+ configure_name_in_lunchflow: Import nicht möglich – bitte Kontoname in Lunch‑Flow konfigurieren
+ description: Wähle ein Lunch‑Flow-Konto aus, um es mit diesem Konto zu verknüpfen. Transaktionen werden automatisch synchronisiert und doppelte Einträge entfernt.
link_account: Konto verknüpfen
no_account_specified: Kein Konto angegeben
- no_accounts_found: Keine Lunch-Flow-Konten gefunden. Bitte überprüfe deine API-Key-Konfiguration.
- no_api_key: Lunch-Flow-API-Schlüssel ist nicht konfiguriert. Bitte konfiguriere ihn in den Einstellungen.
- title: "%{account_name} mit Lunch Flow verknüpfen"
+ no_accounts_found: Keine Lunch‑Flow-Konten gefunden. Bitte überprüfe deine API-Key-Konfiguration.
+ no_api_key: Lunch‑Flow-API-Schlüssel ist nicht konfiguriert. Bitte konfiguriere ihn in den Einstellungen.
+ no_name_placeholder: "(Kein Name)"
+ title: "%{account_name} mit Lunch‑Flow verknüpfen"
link_existing_account:
account_already_linked: Dieses Konto ist bereits mit einem Anbieter verknüpft
api_error: "API-Fehler: %{message}"
- lunchflow_account_already_linked: Dieses Lunch-Flow-Konto ist bereits mit einem anderen Konto verknüpft
- lunchflow_account_not_found: Lunch-Flow-Konto nicht gefunden
+ invalid_account_name: Konto mit leerem Namen kann nicht verknüpft werden
+ lunchflow_account_already_linked: Dieses Lunch‑Flow-Konto ist bereits mit einem anderen Konto verknüpft
+ lunchflow_account_not_found: Lunch‑Flow-Konto nicht gefunden
missing_parameters: Erforderliche Parameter fehlen
- success: "%{account_name} erfolgreich mit Lunch Flow verknüpft"
+ success: "%{account_name} erfolgreich mit Lunch‑Flow verknüpft"
+ setup_accounts:
+ account_type_label: "Kontotyp:"
+ all_accounts_linked: "Alle deine Lunch‑Flow-Konten sind bereits eingerichtet."
+ api_error: "API-Fehler: %{message}"
+ fetch_failed: "Konten konnten nicht geladen werden"
+ no_accounts_to_setup: "Keine Konten zum Einrichten"
+ no_api_key: "Der Lunch‑Flow-API-Schlüssel ist nicht konfiguriert. Bitte prüfe deine Verbindungseinstellungen."
+ account_types:
+ skip: Dieses Konto überspringen
+ depository: Giro- oder Sparkonto
+ credit_card: Kreditkarte
+ investment: Anlagekonto
+ loan: Darlehen oder Hypothek
+ other_asset: Sonstiges Vermögen
+ subtype_labels:
+ depository: "Konto-Untertyp:"
+ credit_card: ""
+ investment: "Anlagetyp:"
+ loan: "Darlehenstyp:"
+ other_asset: ""
+ subtype_messages:
+ credit_card: "Kreditkarten werden automatisch als Kreditkartenkonten eingerichtet."
+ other_asset: "Für sonstiges Vermögen sind keine weiteren Optionen nötig."
+ subtypes:
+ depository:
+ checking: Girokonto
+ savings: Sparkonto
+ hsa: Health Savings Account
+ cd: Festgeld
+ money_market: Geldmarkt
+ investment:
+ brokerage: Brokerage
+ pension: Pension
+ retirement: Ruhestand
+ "401k": "401(k)"
+ roth_401k: "Roth 401(k)"
+ "403b": "403(b)"
+ tsp: Thrift Savings Plan
+ "529_plan": "529 Plan"
+ hsa: Health Savings Account
+ mutual_fund: Fonds
+ ira: Traditionelles IRA
+ roth_ira: Roth IRA
+ angel: Business Angel
+ loan:
+ mortgage: Hypothek
+ student: Studienkredit
+ auto: Autokredit
+ other: Sonstiges Darlehen
+ balance: Saldo
+ cancel: Abbrechen
+ choose_account_type: "Wähle den richtigen Kontotyp für jedes Lunch‑Flow-Konto:"
+ create_accounts: Konten anlegen
+ creating_accounts: Konten werden angelegt...
+ historical_data_range: "Historischer Datenbereich:"
+ subtitle: Wähle die richtigen Kontotypen für deine importierten Konten
+ sync_start_date_help: Wähle, wie weit die Buchungshistorie zurück synchronisiert werden soll. Maximal sind 3 Jahre verfügbar.
+ sync_start_date_label: "Buchungen synchronisieren ab:"
+ title: Lunch‑Flow-Konten einrichten
+ complete_account_setup:
+ all_skipped: "Alle Konten wurden übersprungen. Es wurden keine Konten angelegt."
+ creation_failed: "Konten konnten nicht angelegt werden: %{error}"
+ no_accounts: "Keine Konten zum Einrichten."
+ success:
+ one: "%{count} Konto erfolgreich angelegt."
+ other: "%{count} Konten erfolgreich angelegt."
sync:
success: Synchronisierung gestartet
update:
- success: Lunch-Flow-Verbindung aktualisiert
+ success: Lunch‑Flow-Verbindung aktualisiert
diff --git a/config/locales/views/lunchflow_items/es.yml b/config/locales/views/lunchflow_items/es.yml
index a64a308dc..c2889172e 100644
--- a/config/locales/views/lunchflow_items/es.yml
+++ b/config/locales/views/lunchflow_items/es.yml
@@ -15,49 +15,129 @@ es:
one: "La cuenta seleccionada (%{names}) ya está vinculada"
other: "Todas las %{count} cuentas seleccionadas ya están vinculadas: %{names}"
api_error: "Error de API: %{message}"
+ invalid_account_names:
+ one: "No se puede vincular una cuenta con el nombre en blanco"
+ other: "No se pueden vincular %{count} cuentas con nombres en blanco"
link_failed: Error al vincular cuentas
no_accounts_selected: Por favor, selecciona al menos una cuenta
+ partial_invalid: "Se han vinculado %{created_count} cuenta(s) con éxito, %{already_linked_count} ya estaban vinculadas, %{invalid_count} cuenta(s) tenían nombres no válidos"
partial_success: "%{created_count} cuenta(s) vinculada(s) con éxito. %{already_linked_count} cuenta(s) ya estaban vinculadas: %{already_linked_names}"
success:
one: "%{count} cuenta vinculada con éxito"
other: "%{count} cuentas vinculadas con éxito"
lunchflow_item:
+ accounts_need_setup: Las cuentas necesitan configuración
delete: Eliminar conexión
deletion_in_progress: eliminación en progreso...
error: Error
no_accounts_description: Esta conexión aún no tiene cuentas vinculadas.
no_accounts_title: Sin cuentas
+ setup_action: Configurar nuevas cuentas
+ setup_description: "%{linked} de %{total} cuentas vinculadas. Elige los tipos de cuenta para tus cuentas de Lunch Flow recién importadas."
+ setup_needed: Nuevas cuentas listas para configurar
status: "Sincronizado hace %{timestamp}"
status_never: Nunca sincronizado
+ status_with_summary: "Sincronizado hace %{timestamp} • %{summary}"
syncing: Sincronizando...
+ total: Total
+ unlinked: Desvinculadas
select_accounts:
accounts_selected: cuentas seleccionadas
api_error: "Error de API: %{message}"
cancel: Cancelar
- description: Selecciona las cuentas que deseas vincular a tu cuenta de Sure.
+ configure_name_in_lunchflow: "No se puede importar: por favor, configura el nombre de la cuenta en Lunch Flow"
+ description: Selecciona las cuentas que deseas vincular a tu cuenta de %{product_name}.
link_accounts: Vincular cuentas seleccionadas
no_accounts_found: No se encontraron cuentas. Por favor, verifica la configuración de tu clave API.
no_api_key: La clave API de Lunch Flow no está configurada. Por favor, configúrala en Configuración.
+ no_name_placeholder: "(Sin nombre)"
title: Seleccionar cuentas de Lunch Flow
select_existing_account:
account_already_linked: Esta cuenta ya está vinculada a un proveedor
all_accounts_already_linked: Todas las cuentas de Lunch Flow ya están vinculadas
api_error: "Error de API: %{message}"
cancel: Cancelar
+ configure_name_in_lunchflow: "No se puede importar: por favor, configura el nombre de la cuenta en Lunch Flow"
description: Selecciona una cuenta de Lunch Flow para vincular con esta cuenta. Las transacciones se sincronizarán y desduplicarán automáticamente.
link_account: Vincular cuenta
no_account_specified: No se especificó ninguna cuenta
no_accounts_found: No se encontraron cuentas de Lunch Flow. Por favor, verifica la configuración de tu clave API.
no_api_key: La clave API de Lunch Flow no está configurada. Por favor, configúrala en Configuración.
+ no_name_placeholder: "(Sin nombre)"
title: "Vincular %{account_name} con Lunch Flow"
link_existing_account:
account_already_linked: Esta cuenta ya está vinculada a un proveedor
api_error: "Error de API: %{message}"
+ invalid_account_name: No se puede vincular una cuenta con el nombre en blanco
lunchflow_account_already_linked: Esta cuenta de Lunch Flow ya está vinculada a otra cuenta
lunchflow_account_not_found: Cuenta de Lunch Flow no encontrada
missing_parameters: Faltan parámetros requeridos
success: "%{account_name} vinculada con Lunch Flow con éxito"
+ setup_accounts:
+ account_type_label: "Tipo de cuenta:"
+ all_accounts_linked: "Todas tus cuentas de Lunch Flow ya han sido configuradas."
+ api_error: "Error de API: %{message}"
+ fetch_failed: "Error al obtener las cuentas"
+ no_accounts_to_setup: "No hay cuentas para configurar"
+ no_api_key: "La clave API de Lunch Flow no está configurada. Por favor, comprueba los ajustes de conexión."
+ account_types:
+ skip: Omitir esta cuenta
+ depository: Cuenta corriente o de ahorro
+ credit_card: Tarjeta de crédito
+ investment: Cuenta de inversión
+ loan: Préstamo o hipoteca
+ other_asset: Otro activo
+ subtype_labels:
+ depository: "Subtipo de cuenta:"
+ credit_card: ""
+ investment: "Tipo de inversión:"
+ loan: "Tipo de préstamo:"
+ other_asset: ""
+ subtype_messages:
+ credit_card: "Las tarjetas de crédito se configurarán automáticamente como cuentas de tarjeta de crédito."
+ other_asset: "No se necesitan opciones adicionales para otros activos."
+ subtypes:
+ depository:
+ checking: Corriente
+ savings: Ahorros
+ hsa: Cuenta de ahorros para la salud (HSA)
+ cd: Certificado de depósito
+ money_market: Mercado monetario
+ investment:
+ brokerage: Bróker
+ pension: Plan de pensiones
+ retirement: Jubilación
+ "401k": "401(k)"
+ roth_401k: "Roth 401(k)"
+ "403b": "403(b)"
+ tsp: Plan de ahorro TSP
+ "529_plan": Plan 529
+ hsa: Cuenta de ahorros para la salud (HSA)
+ mutual_fund: Fondo de inversión
+ ira: IRA tradicional
+ roth_ira: Roth IRA
+ angel: Inversión Angel
+ loan:
+ mortgage: Hipoteca
+ student: Préstamo estudiantil
+ auto: Préstamo de coche
+ other: Otro préstamo
+ balance: Saldo
+ cancel: Cancelar
+ choose_account_type: "Elige el tipo de cuenta correcto para cada cuenta de Lunch Flow:"
+ create_accounts: Crear cuentas
+ creating_accounts: Creando cuentas...
+ historical_data_range: "Rango de datos históricos:"
+ subtitle: Elige los tipos de cuenta correctos para tus cuentas importadas
+ sync_start_date_help: Selecciona cuánto tiempo atrás deseas sincronizar el historial de transacciones. Máximo 3 años de historial disponibles.
+ sync_start_date_label: "Empezar a sincronizar transacciones desde:"
+ title: Configura tus cuentas de Lunch Flow
+ complete_account_setup:
+ all_skipped: "Se omitieron todas las cuentas. No se creó ninguna cuenta."
+ creation_failed: "Error al crear las cuentas: %{error}"
+ no_accounts: "No hay cuentas para configurar."
+ success: "Se han creado %{count} cuenta(s) con éxito."
sync:
success: Sincronización iniciada
update:
- success: Conexión con Lunch Flow actualizada
+ success: Conexión con Lunch Flow actualizada
\ No newline at end of file
diff --git a/config/locales/views/lunchflow_items/pl.yml b/config/locales/views/lunchflow_items/pl.yml
new file mode 100644
index 000000000..8d243a3f9
--- /dev/null
+++ b/config/locales/views/lunchflow_items/pl.yml
@@ -0,0 +1,153 @@
+---
+pl:
+ lunchflow_items:
+ create:
+ success: Połączenie Lunch Flow zostało pomyślnie utworzone
+ destroy:
+ success: Połączenie Lunch Flow zostało usunięte
+ index:
+ title: Połączenia Lunch Flow
+ loading:
+ loading_message: Ładowanie kont Lunch Flow...
+ loading_title: Ładowanie
+ link_accounts:
+ all_already_linked:
+ one: Wybrane konto (%{names}) jest już połączone
+ few: 'Wszystkie %{count} wybrane konta są już połączone: %{names}'
+ many: 'Wszystkie %{count} wybranych kont jest już połączonych: %{names}'
+ other: 'Wszystkie %{count} wybrane konta są już połączone: %{names}'
+ api_error: 'Błąd API: %{message}'
+ invalid_account_names:
+ one: Nie można połączyć konta bez nazwy
+ few: Nie można połączyć %{count} kont bez nazwy
+ many: Nie można połączyć %{count} kont bez nazwy
+ other: Nie można połączyć %{count} kont bez nazwy
+ link_failed: Nie udało się połączyć kont
+ no_accounts_selected: Wybierz co najmniej jedno konto
+ partial_invalid: Pomyślnie połączono %{created_count} konto(a), %{already_linked_count} było już połączonych, %{invalid_count} konto(a) miało nieprawidłowe nazwy
+ partial_success: 'Pomyślnie połączono %{created_count} konto(a). %{already_linked_count} konto(a) było już połączonych: %{already_linked_names}'
+ success:
+ one: Pomyślnie połączono %{count} konto
+ few: Pomyślnie połączono %{count} konta
+ many: Pomyślnie połączono %{count} kont
+ other: Pomyślnie połączono %{count} kont
+ lunchflow_item:
+ accounts_need_setup: Konta wymagają konfiguracji
+ delete: Usuń połączenie
+ deletion_in_progress: usuwanie w toku...
+ error: Błąd
+ no_accounts_description: To połączenie nie ma jeszcze żadnych połączonych kont.
+ no_accounts_title: Brak kont
+ setup_action: Skonfiguruj nowe konta
+ setup_description: "%{linked} z %{total} kont połączonych. Wybierz typy kont dla nowo zaimportowanych kont Lunch Flow."
+ setup_needed: Nowe konta gotowe do konfiguracji
+ status: Zsynchronizowano %{timestamp} temu
+ status_never: Nigdy nie synchronizowano
+ status_with_summary: Ostatnia synchronizacja %{timestamp} temu • %{summary}
+ syncing: Synchronizacja...
+ total: Łącznie
+ unlinked: Niepodłączone
+ select_accounts:
+ accounts_selected: wybranych kont
+ api_error: 'Błąd API: %{message}'
+ cancel: Anuluj
+ configure_name_in_lunchflow: Nie można zaimportować — skonfiguruj nazwę konta w Lunchflow
+ description: Wybierz konta, które chcesz połączyć z kontem %{product_name}.
+ link_accounts: Połącz wybrane konta
+ no_accounts_found: Nie znaleziono kont. Sprawdź konfigurację klucza API.
+ no_api_key: Klucz API Lunch Flow nie jest skonfigurowany. Skonfiguruj go w Ustawieniach.
+ no_name_placeholder: "(Brak nazwy)"
+ title: Wybierz konta Lunch Flow
+ select_existing_account:
+ account_already_linked: To konto jest już połączone z dostawcą
+ all_accounts_already_linked: Wszystkie konta Lunch Flow są już połączone
+ api_error: 'Błąd API: %{message}'
+ cancel: Anuluj
+ configure_name_in_lunchflow: Nie można zaimportować — skonfiguruj nazwę konta w Lunchflow
+ description: Wybierz konto Lunch Flow do połączenia z tym kontem. Transakcje będą synchronizowane i deduplikowane automatycznie.
+ link_account: Połącz konto
+ no_account_specified: Nie podano konta
+ no_accounts_found: Nie znaleziono kont Lunch Flow. Sprawdź konfigurację klucza API.
+ no_api_key: Klucz API Lunch Flow nie jest skonfigurowany. Skonfiguruj go w Ustawieniach.
+ no_name_placeholder: "(Brak nazwy)"
+ title: Połącz %{account_name} z Lunch Flow
+ link_existing_account:
+ account_already_linked: To konto jest już połączone z dostawcą
+ api_error: 'Błąd API: %{message}'
+ invalid_account_name: Nie można połączyć konta bez nazwy
+ lunchflow_account_already_linked: To konto Lunch Flow jest już połączone z innym kontem
+ lunchflow_account_not_found: Nie znaleziono konta Lunch Flow
+ missing_parameters: Brak wymaganych parametrów
+ success: Pomyślnie połączono %{account_name} z Lunch Flow
+ setup_accounts:
+ account_type_label: 'Typ konta:'
+ all_accounts_linked: Wszystkie Twoje konta Lunch Flow są już skonfigurowane.
+ api_error: 'Błąd API: %{message}'
+ fetch_failed: Nie udało się pobrać kont
+ no_accounts_to_setup: Brak kont do konfiguracji
+ no_api_key: Klucz API Lunch Flow nie jest skonfigurowany. Sprawdź ustawienia połączenia.
+ account_types:
+ skip: Pomiń to konto
+ depository: Konto bieżące lub oszczędnościowe
+ credit_card: Karta kredytowa
+ investment: Konto inwestycyjne
+ loan: Pożyczka lub kredyt hipoteczny
+ other_asset: Inne aktywa
+ subtype_labels:
+ depository: 'Podtyp konta:'
+ credit_card: 'Podtyp karty kredytowej:'
+ investment: 'Typ inwestycji:'
+ loan: 'Typ pożyczki:'
+ other_asset: 'Podtyp aktywa:'
+ subtype_messages:
+ credit_card: Karty kredytowe zostaną automatycznie skonfigurowane jako konta kart kredytowych.
+ other_asset: Dla innych aktywów nie są potrzebne dodatkowe opcje.
+ subtypes:
+ depository:
+ checking: Konto bieżące
+ savings: Oszczędnościowe
+ hsa: Konto oszczędnościowe na cele zdrowotne
+ cd: Lokata terminowa
+ money_market: Rynek pieniężny
+ investment:
+ brokerage: Maklerskie
+ pension: Emerytura
+ retirement: Emerytalne
+ 401k: 401(k)
+ roth_401k: Roth 401(k)
+ 403b: 403(b)
+ tsp: Plan oszczędnościowy Thrift
+ 529_plan: 529 Plan
+ hsa: Konto oszczędnościowe na cele zdrowotne
+ mutual_fund: Fundusz inwestycyjny
+ ira: Tradycyjne IRA
+ roth_ira: Roth IRA
+ angel: Anielska
+ loan:
+ mortgage: Kredyt hipoteczny
+ student: Pożyczka studencka
+ auto: Pożyczka na samochód
+ other: Inna pożyczka
+ balance: Saldo
+ cancel: Anuluj
+ choose_account_type: 'Wybierz poprawny typ dla każdego konta Lunch Flow:'
+ create_accounts: Utwórz konta
+ creating_accounts: Tworzenie kont...
+ historical_data_range: 'Zakres danych historycznych:'
+ subtitle: Wybierz poprawne typy dla importowanych kont
+ sync_start_date_help: Wybierz, jak daleko wstecz chcesz synchronizować historię transakcji. Dostępne są maksymalnie 3 lata historii.
+ sync_start_date_label: 'Synchronizuj transakcje od:'
+ title: Skonfiguruj swoje konta Lunch Flow
+ complete_account_setup:
+ all_skipped: Wszystkie konta zostały pominięte. Nie utworzono żadnych kont.
+ creation_failed: 'Nie udało się utworzyć kont: %{error}'
+ no_accounts: Brak kont do skonfigurowania.
+ success:
+ one: Pomyślnie utworzono %{count} konto.
+ few: Pomyślnie utworzono %{count} konta.
+ many: Pomyślnie utworzono %{count} kont.
+ other: Pomyślnie utworzono %{count} konta.
+ sync:
+ success: Rozpoczęto synchronizację
+ update:
+ success: Połączenie Lunch Flow zostało zaktualizowane
diff --git a/config/locales/views/merchants/de.yml b/config/locales/views/merchants/de.yml
index 2efbc7dc5..f0da0c60e 100644
--- a/config/locales/views/merchants/de.yml
+++ b/config/locales/views/merchants/de.yml
@@ -6,19 +6,26 @@ de:
success: Neuer Händler erfolgreich erstellt
destroy:
success: Händler erfolgreich gelöscht
+ unlinked_success: Händler von deinen Transaktionen entfernt
edit:
title: Händler bearbeiten
form:
name_placeholder: Händlername
+ website_placeholder: Website (z. B. starbucks.com)
+ website_hint: Gib die Website des Händlers ein, um dessen Logo automatisch anzuzeigen
index:
empty: Noch keine Händler vorhanden
new: Neuer Händler
+ merge: Händler zusammenführen
title: Händler
family_title: Händler der Familie
family_empty: Noch keine Händler der Familie vorhanden
provider_title: Anbieter-Händler
provider_empty: Noch keine Anbieter-Händler mit dieser Familie verbunden
provider_read_only: Anbieter-Händler werden von deinen verbundenen Institutionen synchronisiert. Sie können hier nicht bearbeitet werden.
+ provider_info: Diese Händler wurden automatisch von deinen Bankverbindungen oder der KI erkannt. Du kannst sie bearbeiten, um deine eigene Kopie zu erstellen, oder sie entfernen, um sie von deinen Transaktionen zu trennen.
+ unlinked_title: Kürzlich getrennt
+ unlinked_info: Diese Händler wurden kürzlich von deinen Transaktionen entfernt. Sie verschwinden nach 30 Tagen aus dieser Liste, sofern sie nicht erneut einer Transaktion zugewiesen werden.
table:
merchant: Händler
actions: Aktionen
@@ -30,7 +37,26 @@ de:
confirm_title: Händler löschen
delete: Händler löschen
edit: Händler bearbeiten
+ merge:
+ title: Händler zusammenführen
+ description: Wähle einen Zielhändler und die Händler, die darin zusammengeführt werden sollen. Alle Transaktionen der zusammengeführten Händler werden dem Ziel zugewiesen.
+ target_label: Zusammenführen in (Ziel)
+ select_target: Zielhändler auswählen …
+ sources_label: Händler zum Zusammenführen
+ sources_hint: Die ausgewählten Händler werden in den Zielhändler zusammengeführt. Familienhändler werden gelöscht, Anbieter-Händler werden getrennt.
+ submit: Ausgewählte zusammenführen
new:
title: Neuer Händler
+ perform_merge:
+ success: "%{count} Händler erfolgreich zusammengeführt"
+ no_merchants_selected: Keine Händler zum Zusammenführen ausgewählt
+ target_not_found: Zielhändler nicht gefunden
+ invalid_merchants: Ungültige Händler ausgewählt
+ provider_merchant:
+ edit: Bearbeiten
+ remove: Entfernen
+ remove_confirm_title: Händler entfernen?
+ remove_confirm_body: Bist du sicher, dass du %{name} entfernen möchtest? Dadurch werden alle zugehörigen Transaktionen von diesem Händler getrennt, der Händler selbst wird nicht gelöscht.
update:
success: Händler erfolgreich aktualisiert
+ converted_success: Händler umgewandelt und erfolgreich aktualisiert
diff --git a/config/locales/views/merchants/en.yml b/config/locales/views/merchants/en.yml
index efd51ba76..3b4907cf4 100644
--- a/config/locales/views/merchants/en.yml
+++ b/config/locales/views/merchants/en.yml
@@ -24,6 +24,10 @@ en:
provider_empty: "No provider merchants linked to this %{moniker} yet"
provider_read_only: Provider merchants are synced from your connected institutions. They cannot be edited here.
provider_info: These merchants were automatically detected by your bank connections or AI. You can edit them to create your own copy, or remove them to unlink from your transactions.
+ enhance_info:
+ one: "%{count} provider merchant is missing website information. Enhance with AI to detect websites, display logos, and merge duplicate merchants."
+ other: "%{count} provider merchants are missing website information. Enhance with AI to detect websites, display logos, and merge duplicate merchants."
+ enhance_button: Enhance with AI
unlinked_title: Recently unlinked
unlinked_info: These merchants were recently removed from your transactions. They will disappear from this list after 30 days unless re-assigned to a transaction.
table:
@@ -57,6 +61,9 @@ en:
remove: Remove
remove_confirm_title: Remove merchant?
remove_confirm_body: Are you sure you want to remove %{name}? This will unlink all associated transactions from this merchant but will not delete the merchant itself.
+ enhance:
+ success: Provider merchant enhancement started. Merchants will be enhanced and duplicates merged shortly.
+ already_running: Enhancement is already in progress. Please wait for it to finish.
update:
success: Merchant updated successfully
converted_success: Merchant converted and updated successfully
diff --git a/config/locales/views/merchants/es.yml b/config/locales/views/merchants/es.yml
index f769dfea1..fb04644f5 100644
--- a/config/locales/views/merchants/es.yml
+++ b/config/locales/views/merchants/es.yml
@@ -6,31 +6,57 @@ es:
success: Nuevo comercio creado con éxito
destroy:
success: Comercio eliminado con éxito
+ unlinked_success: Comercio eliminado de tus transacciones
edit:
title: Editar comercio
form:
name_placeholder: Nombre del comercio
+ website_placeholder: Sitio web (ej. starbucks.com)
+ website_hint: Introduce el sitio web del comercio para mostrar automáticamente su logotipo
index:
empty: Aún no hay comercios
new: Nuevo comercio
+ merge: Fusionar comercios
title: Comercios
- family_title: Comercios familiares
- family_empty: Aún no hay comercios familiares
+ family_title: "Comercios de %{moniker}"
+ family_empty: "Aún no hay comercios de %{moniker}"
provider_title: Comercios del proveedor
- provider_empty: Ningún comercio del proveedor vinculado a esta familia todavía
+ provider_empty: "Aún no hay comercios del proveedor vinculados a %{moniker}"
provider_read_only: Los comercios del proveedor se sincronizan desde tus instituciones conectadas. No se pueden editar aquí.
+ provider_info: Estos comercios han sido detectados automáticamente por tus conexiones bancarias o por IA. Puedes editarlos para crear tu propia copia o eliminarlos para desvincularlos de tus transacciones.
+ unlinked_title: Desvinculados recientemente
+ unlinked_info: Estos comercios se han eliminado recientemente de tus transacciones. Desaparecerán de esta lista tras 30 días, a menos que se vuelvan a asignar a una transacción.
table:
merchant: Comercio
actions: Acciones
source: Origen
merchant:
confirm_accept: Eliminar comercio
- confirm_body: ¿Estás seguro de que deseas eliminar este comercio? Eliminar este comercio
+ confirm_body: ¿Estás seguro de que deseas eliminar este comercio? Eliminar este comercio
desvinculará todas las transacciones asociadas y puede afectar a tus informes.
confirm_title: ¿Eliminar comercio?
delete: Eliminar comercio
edit: Editar comercio
+ merge:
+ title: Fusionar comercios
+ description: Selecciona un comercio de destino y los comercios que deseas fusionar en él. Todas las transacciones de los comercios fusionados se reasignarán al de destino.
+ target_label: Fusionar en (destino)
+ select_target: Seleccionar comercio de destino...
+ sources_label: Comercios a fusionar
+ sources_hint: Los comercios seleccionados se fusionarán en el de destino. Los comercios familiares se eliminarán y los de proveedores se desvincularán.
+ submit: Fusionar seleccionados
new:
title: Nuevo comercio
+ perform_merge:
+ success: Se han fusionado %{count} comercios correctamente
+ no_merchants_selected: No se han seleccionado comercios para fusionar
+ target_not_found: No se ha encontrado el comercio de destino
+ invalid_merchants: Se han seleccionado comercios no válidos
+ provider_merchant:
+ edit: Editar
+ remove: Eliminar
+ remove_confirm_title: ¿Eliminar comercio?
+ remove_confirm_body: ¿Estás seguro de que quieres eliminar %{name}? Esto desvinculará todas las transacciones asociadas a este comercio, pero no eliminará el comercio en sí.
update:
success: Comercio actualizado con éxito
+ converted_success: Comercio convertido y actualizado con éxito
diff --git a/config/locales/views/merchants/pl.yml b/config/locales/views/merchants/pl.yml
new file mode 100644
index 000000000..c95e40896
--- /dev/null
+++ b/config/locales/views/merchants/pl.yml
@@ -0,0 +1,70 @@
+---
+pl:
+ family_merchants:
+ create:
+ error: 'Błąd podczas tworzenia kontrahenta: %{error}'
+ success: Nowy kontrahent został pomyślnie utworzony
+ destroy:
+ success: Kontrahent został pomyślnie usunięty
+ unlinked_success: Kontrahent został odłączony od Twoich transakcji
+ edit:
+ title: Edytuj kontrahenta
+ form:
+ name_placeholder: Nazwa kontrahenta
+ website_placeholder: Strona internetowa (np. starbucks.com)
+ website_hint: Wprowadź stronę internetową kontrahenta, aby automatycznie wyświetlać jego logo
+ index:
+ empty: Brak kontrahentów
+ new: Nowy kontrahent
+ merge: Scal kontrahentów
+ title: Kontrahenci
+ family_title: "Kontrahenci %{moniker}"
+ family_empty: Brak kontrahentów %{moniker}
+ provider_title: Kontrahenci od dostawcy
+ provider_empty: Brak kontrahentów dostawcy połączonych z tym %{moniker}
+ provider_read_only: Kontrahenci dostawcy są synchronizowani z połączonych instytucji. Nie można ich tutaj edytować.
+ provider_info: Ci kontrahenci zostali automatycznie wykryci przez połączenia bankowe lub AI. Możesz ich edytować, aby utworzyć własną kopię, albo usunąć, aby odłączyć ich od transakcji.
+ enhance_info:
+ one: "%{count} kontrahentowi dostawcy brakuje informacji o stronie internetowej. Ulepsz dane z pomocą AI, aby wykryć strony, wyświetlić loga i scalić duplikaty kontrahentów."
+ few: "%{count} kontrahentom dostawcy brakuje informacji o stronie internetowej. Ulepsz dane z pomocą AI, aby wykryć strony, wyświetlić loga i scalić duplikaty kontrahentów."
+ many: "%{count} kontrahentom dostawcy brakuje informacji o stronie internetowej. Ulepsz dane z pomocą AI, aby wykryć strony, wyświetlić loga i scalić duplikaty kontrahentów."
+ other: "%{count} kontrahentom dostawcy brakuje informacji o stronie internetowej. Ulepsz dane z pomocą AI, aby wykryć strony, wyświetlić loga i scalić duplikaty kontrahentów."
+ enhance_button: Ulepsz z pomocą AI
+ unlinked_title: Ostatnio odłączone
+ unlinked_info: Ci kontrahenci zostali niedawno odłączeni od Twoich transakcji. Znikną z tej listy po 30 dniach, chyba że zostaną ponownie przypisani do transakcji.
+ table:
+ merchant: Kontrahent
+ actions: Akcje
+ source: Źródło
+ merchant:
+ confirm_accept: Usuń kontrahenta
+ confirm_body: Czy na pewno chcesz usunąć tego kontrahenta? Usunięcie odłączy wszystkie powiązane transakcje i może wpłynąć na raporty.
+ confirm_title: Usunąć kontrahenta?
+ delete: Usuń kontrahenta
+ edit: Edytuj kontrahenta
+ merge:
+ title: Scal kontrahentów
+ description: Wybierz kontrahenta docelowego i kontrahentów do scalenia z nim. Wszystkie transakcje scalonych kontrahentów zostaną przypisane do celu.
+ target_label: Scal do (docelowy)
+ select_target: Wybierz kontrahenta docelowego...
+ sources_label: Kontrahenci do scalenia
+ sources_hint: Wybrani kontrahenci zostaną scaleni z celem. Kontrahenci rodziny zostaną usunięci, a kontrahenci dostawcy odłączeni.
+ submit: Scal wybrane
+ new:
+ title: Nowy kontrahent
+ perform_merge:
+ success: Pomyślnie scalono %{count} kontrahentów
+ no_merchants_selected: Nie wybrano kontrahentów do scalenia
+ target_not_found: Nie znaleziono kontrahenta docelowego
+ invalid_merchants: Wybrano nieprawidłowych kontrahentów
+ provider_merchant:
+ edit: Edytuj
+ remove: Usuń
+ remove_confirm_title: Usunąć kontrahenta?
+ remove_confirm_body: Czy na pewno chcesz usunąć %{name}? To odłączy wszystkie powiązane transakcje od tego kontrahenta, ale nie usunie samego kontrahenta.
+ enhance:
+ success: Rozpoczęto ulepszanie kontrahentów dostawcy. Kontrahenci zostaną ulepszeni, a duplikaty wkrótce scalone.
+ already_running: Ulepszanie już trwa. Poczekaj na zakończenie.
+ update:
+ success: Kontrahent został pomyślnie zaktualizowany
+ converted_success: Kontrahent został pomyślnie przekonwertowany i zaktualizowany
diff --git a/config/locales/views/mercury_items/de.yml b/config/locales/views/mercury_items/de.yml
new file mode 100644
index 000000000..85c1a15d9
--- /dev/null
+++ b/config/locales/views/mercury_items/de.yml
@@ -0,0 +1,147 @@
+---
+de:
+ mercury_items:
+ create:
+ success: "Mercury-Verbindung erfolgreich erstellt"
+ destroy:
+ success: "Mercury-Verbindung entfernt"
+ index:
+ title: "Mercury-Verbindungen"
+ loading:
+ loading_message: "Mercury-Konten werden geladen..."
+ loading_title: "Laden"
+ link_accounts:
+ all_already_linked:
+ one: "Das ausgewählte Konto (%{names}) ist bereits verknüpft"
+ other: "Alle %{count} ausgewählten Konten sind bereits verknüpft: %{names}"
+ api_error: "API-Fehler: %{message}"
+ invalid_account_names:
+ one: "Konto mit leerem Namen kann nicht verknüpft werden"
+ other: "%{count} Konten mit leeren Namen können nicht verknüpft werden"
+ link_failed: "Konten konnten nicht verknüpft werden"
+ no_accounts_selected: "Bitte wählen Sie mindestens ein Konto aus"
+ no_api_token: "Mercury API-Token nicht gefunden. Bitte in den Anbieter-Einstellungen konfigurieren."
+ partial_invalid: "Erfolgreich %{created_count} Konto/Konten verknüpft, %{already_linked_count} waren bereits verknüpft, %{invalid_count} Konto/Konten hatten ungültige Namen"
+ partial_success: "Erfolgreich %{created_count} Konto/Konten verknüpft. %{already_linked_count} Konto/Konten waren bereits verknüpft: %{already_linked_names}"
+ success:
+ one: "Erfolgreich %{count} Konto verknüpft"
+ other: "Erfolgreich %{count} Konten verknüpft"
+ mercury_item:
+ accounts_need_setup: "Konten benötigen Einrichtung"
+ delete: "Verbindung löschen"
+ deletion_in_progress: "Löschung läuft..."
+ error: "Fehler"
+ no_accounts_description: "Diese Verbindung hat noch keine verknüpften Konten."
+ no_accounts_title: "Keine Konten"
+ setup_action: "Neue Konten einrichten"
+ setup_description: "%{linked} von %{total} Konten verknüpft. Wählen Sie Kontotypen für Ihre neu importierten Mercury-Konten."
+ setup_needed: "Neue Konten bereit zur Einrichtung"
+ status: "Vor %{timestamp} synchronisiert"
+ status_never: "Noch nie synchronisiert"
+ status_with_summary: "Zuletzt vor %{timestamp} synchronisiert – %{summary}"
+ syncing: "Synchronisiere..."
+ total: "Gesamt"
+ unlinked: "Nicht verknüpft"
+ select_accounts:
+ accounts_selected: "Konten ausgewählt"
+ api_error: "API-Fehler: %{message}"
+ cancel: "Abbrechen"
+ configure_name_in_mercury: "Import nicht möglich – bitte Kontoname in Mercury konfigurieren"
+ description: "Wählen Sie die Konten aus, die Sie mit Ihrem %{product_name}-Konto verknüpfen möchten."
+ link_accounts: "Ausgewählte Konten verknüpfen"
+ no_accounts_found: "Keine Konten gefunden. Bitte überprüfen Sie Ihre API-Token-Konfiguration."
+ no_api_token: "Mercury API-Token ist nicht konfiguriert. Bitte in den Einstellungen konfigurieren."
+ no_credentials_configured: "Bitte konfigurieren Sie zuerst Ihren Mercury API-Token in den Anbieter-Einstellungen."
+ no_name_placeholder: "(Kein Name)"
+ title: "Mercury-Konten auswählen"
+ select_existing_account:
+ account_already_linked: "Dieses Konto ist bereits mit einem Anbieter verknüpft"
+ all_accounts_already_linked: "Alle Mercury-Konten sind bereits verknüpft"
+ api_error: "API-Fehler: %{message}"
+ cancel: "Abbrechen"
+ configure_name_in_mercury: "Import nicht möglich – bitte Kontoname in Mercury konfigurieren"
+ description: "Wählen Sie ein Mercury-Konto zur Verknüpfung mit diesem Konto. Transaktionen werden automatisch synchronisiert und dedupliziert."
+ link_account: "Konto verknüpfen"
+ no_account_specified: "Kein Konto angegeben"
+ no_accounts_found: "Keine Mercury-Konten gefunden. Bitte überprüfen Sie Ihre API-Token-Konfiguration."
+ no_api_token: "Mercury API-Token ist nicht konfiguriert. Bitte in den Einstellungen konfigurieren."
+ no_credentials_configured: "Bitte konfigurieren Sie zuerst Ihren Mercury API-Token in den Anbieter-Einstellungen."
+ no_name_placeholder: "(Kein Name)"
+ title: "%{account_name} mit Mercury verknüpfen"
+ link_existing_account:
+ account_already_linked: "Dieses Konto ist bereits mit einem Anbieter verknüpft"
+ api_error: "API-Fehler: %{message}"
+ invalid_account_name: "Konto mit leerem Namen kann nicht verknüpft werden"
+ mercury_account_already_linked: "Dieses Mercury-Konto ist bereits mit einem anderen Konto verknüpft"
+ mercury_account_not_found: "Mercury-Konto nicht gefunden"
+ missing_parameters: "Erforderliche Parameter fehlen"
+ no_api_token: "Mercury API-Token nicht gefunden. Bitte in den Anbieter-Einstellungen konfigurieren."
+ success: "%{account_name} erfolgreich mit Mercury verknüpft"
+ setup_accounts:
+ account_type_label: "Kontotyp:"
+ all_accounts_linked: "Alle Ihre Mercury-Konten sind bereits eingerichtet."
+ api_error: "API-Fehler: %{message}"
+ fetch_failed: "Konten konnten nicht geladen werden"
+ no_accounts_to_setup: "Keine Konten zum Einrichten"
+ no_api_token: "Mercury API-Token ist nicht konfiguriert. Bitte überprüfen Sie Ihre Verbindungseinstellungen."
+ account_types:
+ skip: "Dieses Konto überspringen"
+ depository: "Giro- oder Sparkonto"
+ credit_card: "Kreditkarte"
+ investment: "Depot/Anlagekonto"
+ loan: "Darlehen oder Hypothek"
+ other_asset: "Sonstiges Vermögen"
+ subtype_labels:
+ depository: "Konto-Untertyp:"
+ credit_card: ""
+ investment: "Anlagetyp:"
+ loan: "Darlehenstyp:"
+ other_asset: ""
+ subtype_messages:
+ credit_card: "Kreditkarten werden automatisch als Kreditkartenkonten eingerichtet."
+ other_asset: "Für sonstiges Vermögen sind keine weiteren Optionen nötig."
+ subtypes:
+ depository:
+ checking: "Girokonto"
+ savings: "Sparkonto"
+ hsa: "Gesundheits-Sparkonto"
+ cd: "Festgeld"
+ money_market: "Geldmarkt"
+ investment:
+ brokerage: "Brokerage"
+ pension: "Rente"
+ retirement: "Altersvorsorge"
+ "401k": "401(k)"
+ roth_401k: "Roth 401(k)"
+ "403b": "403(b)"
+ tsp: "Thrift Savings Plan"
+ "529_plan": "529 Plan"
+ hsa: "Gesundheits-Sparkonto"
+ mutual_fund: "Investmentfonds"
+ ira: "Traditioneller IRA"
+ roth_ira: "Roth IRA"
+ angel: "Angel"
+ loan:
+ mortgage: "Hypothek"
+ student: "Studienkredit"
+ auto: "Autokredit"
+ other: "Sonstiges Darlehen"
+ balance: "Saldo"
+ cancel: "Abbrechen"
+ choose_account_type: "Wählen Sie den passenden Kontotyp für jedes Mercury-Konto:"
+ create_accounts: "Konten erstellen"
+ creating_accounts: "Konten werden erstellt..."
+ historical_data_range: "Zeitraum für Verlauf:"
+ subtitle: "Wählen Sie die passenden Kontotypen für Ihre importierten Konten"
+ sync_start_date_help: "Wählen Sie, wie weit die Transaktionshistorie synchronisiert werden soll. Maximal 3 Jahre Verlauf verfügbar."
+ sync_start_date_label: "Transaktionen synchronisieren ab:"
+ title: "Ihre Mercury-Konten einrichten"
+ complete_account_setup:
+ all_skipped: "Alle Konten wurden übersprungen. Es wurden keine Konten erstellt."
+ creation_failed: "Konten konnten nicht erstellt werden: %{error}"
+ no_accounts: "Keine Konten zum Einrichten."
+ success: "Erfolgreich %{count} Konto/Konten erstellt."
+ sync:
+ success: "Synchronisation gestartet"
+ update:
+ success: "Mercury-Verbindung aktualisiert"
diff --git a/config/locales/views/mercury_items/es.yml b/config/locales/views/mercury_items/es.yml
new file mode 100644
index 000000000..085607cd7
--- /dev/null
+++ b/config/locales/views/mercury_items/es.yml
@@ -0,0 +1,147 @@
+---
+es:
+ mercury_items:
+ create:
+ success: Conexión con Mercury creada con éxito
+ destroy:
+ success: Conexión con Mercury eliminada
+ index:
+ title: Conexiones de Mercury
+ loading:
+ loading_message: Cargando cuentas de Mercury...
+ loading_title: Cargando
+ link_accounts:
+ all_already_linked:
+ one: "La cuenta seleccionada (%{names}) ya está vinculada"
+ other: "Todas las %{count} cuentas seleccionadas ya están vinculadas: %{names}"
+ api_error: "Error de API: %{message}"
+ invalid_account_names:
+ one: "No se puede vincular una cuenta con el nombre en blanco"
+ other: "No se pueden vincular %{count} cuentas con nombres en blanco"
+ link_failed: Error al vincular cuentas
+ no_accounts_selected: Por favor, selecciona al menos una cuenta
+ no_api_token: No se encontró el token de API de Mercury. Por favor, configúralo en los Ajustes del Proveedor.
+ partial_invalid: "Se han vinculado %{created_count} cuenta(s) con éxito, %{already_linked_count} ya estaban vinculadas, %{invalid_count} cuenta(s) tenían nombres no válidos"
+ partial_success: "%{created_count} cuenta(s) vinculada(s) con éxito. %{already_linked_count} cuenta(s) ya estaban vinculadas: %{already_linked_names}"
+ success:
+ one: "%{count} cuenta vinculada con éxito"
+ other: "%{count} cuentas vinculadas con éxito"
+ mercury_item:
+ accounts_need_setup: Las cuentas necesitan configuración
+ delete: Eliminar conexión
+ deletion_in_progress: eliminación en curso...
+ error: Error
+ no_accounts_description: Esta conexión aún no tiene cuentas vinculadas.
+ no_accounts_title: Sin cuentas
+ setup_action: Configurar nuevas cuentas
+ setup_description: "%{linked} de %{total} cuentas vinculadas. Elige los tipos de cuenta para tus cuentas de Mercury recién importadas."
+ setup_needed: Nuevas cuentas listas para configurar
+ status: "Sincronizado hace %{timestamp}"
+ status_never: Nunca sincronizado
+ status_with_summary: "Sincronizado hace %{timestamp} - %{summary}"
+ syncing: Sincronizando...
+ total: Total
+ unlinked: Desvinculadas
+ select_accounts:
+ accounts_selected: cuentas seleccionadas
+ api_error: "Error de API: %{message}"
+ cancel: Cancelar
+ configure_name_in_mercury: "No se puede importar: por favor, configura el nombre de la cuenta en Mercury"
+ description: Selecciona las cuentas que deseas vincular a tu cuenta de %{product_name}.
+ link_accounts: Vincular cuentas seleccionadas
+ no_accounts_found: No se encontraron cuentas. Por favor, verifica la configuración de tu token de API.
+ no_api_token: El token de API de Mercury no está configurado. Por favor, configúralo en Ajustes.
+ no_credentials_configured: Por favor, configura primero tu token de API de Mercury en los Ajustes del Proveedor.
+ no_name_placeholder: "(Sin nombre)"
+ title: Seleccionar cuentas de Mercury
+ select_existing_account:
+ account_already_linked: Esta cuenta ya está vinculada a un proveedor
+ all_accounts_already_linked: Todas las cuentas de Mercury ya están vinculadas
+ api_error: "Error de API: %{message}"
+ cancel: Cancelar
+ configure_name_in_mercury: "No se puede importar: por favor, configura el nombre de la cuenta en Mercury"
+ description: Selecciona una cuenta de Mercury para vincular con esta cuenta. Las transacciones se sincronizarán y desduplicarán automáticamente.
+ link_account: Vincular cuenta
+ no_account_specified: No se especificó ninguna cuenta
+ no_accounts_found: No se encontraron cuentas de Mercury. Por favor, verifica la configuración de tu token de API.
+ no_api_token: El token de API de Mercury no está configurado. Por favor, configúralo en Ajustes.
+ no_credentials_configured: Por favor, configura primero tu token de API de Mercury en los Ajustes del Proveedor.
+ no_name_placeholder: "(Sin nombre)"
+ title: "Vincular %{account_name} con Mercury"
+ link_existing_account:
+ account_already_linked: Esta cuenta ya está vinculada a un proveedor
+ api_error: "Error de API: %{message}"
+ invalid_account_name: No se puede vincular una cuenta con el nombre en blanco
+ mercury_account_already_linked: Esta cuenta de Mercury ya está vinculada a otra cuenta
+ mercury_account_not_found: Cuenta de Mercury no encontrada
+ missing_parameters: Faltan parámetros requeridos
+ no_api_token: No se encontró el token de API de Mercury. Por favor, configúralo en los Ajustes del Proveedor.
+ success: "%{account_name} vinculada con Mercury con éxito"
+ setup_accounts:
+ account_type_label: "Tipo de cuenta:"
+ all_accounts_linked: "Todas tus cuentas de Mercury ya han sido configuradas."
+ api_error: "Error de API: %{message}"
+ fetch_failed: "Error al obtener las cuentas"
+ no_accounts_to_setup: "No hay cuentas para configurar"
+ no_api_token: "El token de API de Mercury no está configurado. Por favor, comprueba los ajustes de conexión."
+ account_types:
+ skip: Omitir esta cuenta
+ depository: Cuenta corriente o de ahorro
+ credit_card: Tarjeta de crédito
+ investment: Cuenta de inversión
+ loan: Préstamo o hipoteca
+ other_asset: Otro activo
+ subtype_labels:
+ depository: "Subtipo de cuenta:"
+ credit_card: ""
+ investment: "Tipo de inversión:"
+ loan: "Tipo de préstamo:"
+ other_asset: ""
+ subtype_messages:
+ credit_card: "Las tarjetas de crédito se configurarán automáticamente como cuentas de tarjeta de crédito."
+ other_asset: "No se necesitan opciones adicionales para otros activos."
+ subtypes:
+ depository:
+ checking: Corriente
+ savings: Ahorros
+ hsa: Cuenta de ahorros para la salud (HSA)
+ cd: Certificado de depósito
+ money_market: Mercado monetario
+ investment:
+ brokerage: Bróker
+ pension: Plan de pensiones
+ retirement: Jubilación
+ "401k": "401(k)"
+ roth_401k: "Roth 401(k)"
+ "403b": "403(b)"
+ tsp: Plan de ahorro TSP
+ "529_plan": Plan 529
+ hsa: Cuenta de ahorros para la salud (HSA)
+ mutual_fund: Fondo de inversión
+ ira: IRA tradicional
+ roth_ira: Roth IRA
+ angel: Inversión Angel
+ loan:
+ mortgage: Hipoteca
+ student: Préstamo estudiantil
+ auto: Préstamo de coche
+ other: Otro préstamo
+ balance: Saldo
+ cancel: Cancelar
+ choose_account_type: "Elige el tipo de cuenta correcto para cada cuenta de Mercury:"
+ create_accounts: Crear cuentas
+ creating_accounts: Creando cuentas...
+ historical_data_range: "Rango de datos históricos:"
+ subtitle: Elige los tipos de cuenta correctos para tus cuentas importadas
+ sync_start_date_help: Selecciona cuánto tiempo atrás deseas sincronizar el historial de transacciones. Máximo 3 años de historial disponibles.
+ sync_start_date_label: "Empezar a sincronizar transacciones desde:"
+ title: Configura tus cuentas de Mercury
+ complete_account_setup:
+ all_skipped: "Se omitieron todas las cuentas. No se creó ninguna cuenta."
+ creation_failed: "Error al crear las cuentas: %{error}"
+ no_accounts: "No hay cuentas para configurar."
+ success: "Se han creado %{count} cuenta(s) con éxito."
+ sync:
+ success: Sincronización iniciada
+ update:
+ success: Conexión con Mercury actualizada
\ No newline at end of file
diff --git a/config/locales/views/mercury_items/pl.yml b/config/locales/views/mercury_items/pl.yml
new file mode 100644
index 000000000..4fd424428
--- /dev/null
+++ b/config/locales/views/mercury_items/pl.yml
@@ -0,0 +1,157 @@
+---
+pl:
+ mercury_items:
+ create:
+ success: Połączenie Mercury zostało pomyślnie utworzone
+ destroy:
+ success: Połączenie Mercury zostało usunięte
+ index:
+ title: Połączenia Mercury
+ loading:
+ loading_message: Ładowanie kont Mercury...
+ loading_title: Ładowanie
+ link_accounts:
+ all_already_linked:
+ one: Wybrane konto (%{names}) jest już połączone
+ few: 'Wszystkie %{count} wybrane konta są już połączone: %{names}'
+ many: 'Wszystkie %{count} wybranych kont jest już połączonych: %{names}'
+ other: 'Wszystkie %{count} wybrane konta są już połączone: %{names}'
+ api_error: 'Błąd API: %{message}'
+ invalid_account_names:
+ one: Nie można połączyć konta bez nazwy
+ few: Nie można połączyć %{count} kont bez nazwy
+ many: Nie można połączyć %{count} kont bez nazwy
+ other: Nie można połączyć %{count} kont bez nazwy
+ link_failed: Nie udało się połączyć kont
+ no_accounts_selected: Wybierz co najmniej jedno konto
+ no_api_token: Nie znaleziono tokenu API Mercury. Skonfiguruj go w Ustawieniach dostawcy.
+ partial_invalid: Pomyślnie połączono %{created_count} konto(a), %{already_linked_count} było już połączonych, %{invalid_count} konto(a) miało nieprawidłowe nazwy
+ partial_success: 'Pomyślnie połączono %{created_count} konto(a). %{already_linked_count} konto(a) było już połączonych: %{already_linked_names}'
+ success:
+ one: Pomyślnie połączono %{count} konto
+ few: Pomyślnie połączono %{count} konta
+ many: Pomyślnie połączono %{count} kont
+ other: Pomyślnie połączono %{count} kont
+ mercury_item:
+ accounts_need_setup: Konta wymagają konfiguracji
+ delete: Usuń połączenie
+ deletion_in_progress: usuwanie w toku...
+ error: Błąd
+ no_accounts_description: To połączenie nie ma jeszcze żadnych połączonych kont.
+ no_accounts_title: Brak kont
+ setup_action: Skonfiguruj nowe konta
+ setup_description: "%{linked} z %{total} kont połączonych. Wybierz typy kont dla nowo zaimportowanych kont Mercury."
+ setup_needed: Nowe konta gotowe do konfiguracji
+ status: Zsynchronizowano %{timestamp} temu
+ status_never: Nigdy nie synchronizowano
+ status_with_summary: Ostatnia synchronizacja %{timestamp} temu - %{summary}
+ syncing: Synchronizacja...
+ total: Łącznie
+ unlinked: Niepodłączone
+ select_accounts:
+ accounts_selected: wybranych kont
+ api_error: 'Błąd API: %{message}'
+ cancel: Anuluj
+ configure_name_in_mercury: Nie można zaimportować — skonfiguruj nazwę konta w Mercury
+ description: Wybierz konta, które chcesz połączyć z kontem %{product_name}.
+ link_accounts: Połącz wybrane konta
+ no_accounts_found: Nie znaleziono kont. Sprawdź konfigurację tokenu API.
+ no_api_token: Token API Mercury nie jest skonfigurowany. Skonfiguruj go w Ustawieniach.
+ no_credentials_configured: Skonfiguruj token API Mercury najpierw w Ustawieniach dostawcy.
+ no_name_placeholder: "(Brak nazwy)"
+ title: Wybierz konta Mercury
+ select_existing_account:
+ account_already_linked: To konto jest już połączone z dostawcą
+ all_accounts_already_linked: Wszystkie konta Mercury są już połączone
+ api_error: 'Błąd API: %{message}'
+ cancel: Anuluj
+ configure_name_in_mercury: Nie można zaimportować — skonfiguruj nazwę konta w Mercury
+ description: Wybierz konto Mercury do połączenia z tym kontem. Transakcje będą synchronizowane i deduplikowane automatycznie.
+ link_account: Połącz konto
+ no_account_specified: Nie podano konta
+ no_accounts_found: Nie znaleziono kont Mercury. Sprawdź konfigurację tokenu API.
+ no_api_token: Token API Mercury nie jest skonfigurowany. Skonfiguruj go w Ustawieniach.
+ no_credentials_configured: Skonfiguruj token API Mercury najpierw w Ustawieniach dostawcy.
+ no_name_placeholder: "(Brak nazwy)"
+ title: Połącz %{account_name} z Mercury
+ link_existing_account:
+ account_already_linked: To konto jest już połączone z dostawcą
+ api_error: 'Błąd API: %{message}'
+ invalid_account_name: Nie można połączyć konta bez nazwy
+ mercury_account_already_linked: To konto Mercury jest już połączone z innym kontem
+ mercury_account_not_found: Nie znaleziono konta Mercury
+ missing_parameters: Brak wymaganych parametrów
+ no_api_token: Nie znaleziono tokenu API Mercury. Skonfiguruj go w Ustawieniach dostawcy.
+ success: Pomyślnie połączono %{account_name} z Mercury
+ setup_accounts:
+ account_type_label: 'Typ konta:'
+ all_accounts_linked: Wszystkie Twoje konta Mercury są już skonfigurowane.
+ api_error: 'Błąd API: %{message}'
+ fetch_failed: Nie udało się pobrać kont
+ no_accounts_to_setup: Brak kont do konfiguracji
+ no_api_token: Token API Mercury nie jest skonfigurowany. Sprawdź ustawienia połączenia.
+ account_types:
+ skip: Pomiń to konto
+ depository: Konto bieżące lub oszczędnościowe
+ credit_card: Karta kredytowa
+ investment: Konto inwestycyjne
+ loan: Pożyczka lub kredyt hipoteczny
+ other_asset: Inne aktywa
+ subtype_labels:
+ depository: 'Podtyp konta:'
+ credit_card: 'Podtyp karty kredytowej:'
+ investment: 'Typ inwestycji:'
+ loan: 'Typ pożyczki:'
+ other_asset: 'Podtyp aktywa:'
+ subtype_messages:
+ credit_card: Karty kredytowe zostaną automatycznie skonfigurowane jako konta kart kredytowych.
+ other_asset: Dla innych aktywów nie są potrzebne dodatkowe opcje.
+ subtypes:
+ depository:
+ checking: Konto bieżące
+ savings: Oszczędnościowe
+ hsa: Konto oszczędnościowe na cele zdrowotne
+ cd: Lokata terminowa
+ money_market: Rynek pieniężny
+ investment:
+ brokerage: Maklerskie
+ pension: Emerytura
+ retirement: Emerytalne
+ 401k: 401(k)
+ roth_401k: Roth 401(k)
+ 403b: 403(b)
+ tsp: Plan oszczędnościowy Thrift
+ 529_plan: 529 Plan
+ hsa: Konto oszczędnościowe na cele zdrowotne
+ mutual_fund: Fundusz inwestycyjny
+ ira: Tradycyjne IRA
+ roth_ira: Roth IRA
+ angel: Anielska
+ loan:
+ mortgage: Kredyt hipoteczny
+ student: Pożyczka studencka
+ auto: Pożyczka na samochód
+ other: Inna pożyczka
+ balance: Saldo
+ cancel: Anuluj
+ choose_account_type: 'Wybierz poprawny typ dla każdego konta Mercury:'
+ create_accounts: Utwórz konta
+ creating_accounts: Tworzenie kont...
+ historical_data_range: 'Zakres danych historycznych:'
+ subtitle: Wybierz poprawne typy dla importowanych kont
+ sync_start_date_help: Wybierz, jak daleko wstecz chcesz synchronizować historię transakcji. Dostępne są maksymalnie 3 lata historii.
+ sync_start_date_label: 'Synchronizuj transakcje od:'
+ title: Skonfiguruj swoje konta Mercury
+ complete_account_setup:
+ all_skipped: Wszystkie konta zostały pominięte. Nie utworzono żadnych kont.
+ creation_failed: 'Nie udało się utworzyć kont: %{error}'
+ no_accounts: Brak kont do skonfigurowania.
+ success:
+ one: Pomyślnie utworzono %{count} konto.
+ few: Pomyślnie utworzono %{count} konta.
+ many: Pomyślnie utworzono %{count} kont.
+ other: Pomyślnie utworzono %{count} konta.
+ sync:
+ success: Rozpoczęto synchronizację
+ update:
+ success: Połączenie Mercury zostało zaktualizowane
diff --git a/config/locales/views/mfa/pl.yml b/config/locales/views/mfa/pl.yml
new file mode 100644
index 000000000..4e2b6f868
--- /dev/null
+++ b/config/locales/views/mfa/pl.yml
@@ -0,0 +1,34 @@
+---
+pl:
+ mfa:
+ backup_codes:
+ backup_codes_description: Każdy kod można użyć tylko raz. Przechowuj je bezpiecznie.
+ backup_codes_title: Twoje kody zapasowe
+ continue: Przejdź do ustawień bezpieczeństwa
+ description: Przechowuj te kody zapasowe w bezpiecznym miejscu — będą potrzebne, jeśli utracisz dostęp do aplikacji uwierzytelniającej
+ page_title: Kody zapasowe
+ title: Zapisz swoje kody zapasowe
+ create:
+ invalid_code: Nieprawidłowy kod weryfikacyjny. Spróbuj ponownie.
+ disable:
+ success: Uwierzytelnianie dwuskładnikowe zostało wyłączone
+ new:
+ code_label: Kod weryfikacyjny
+ code_placeholder: Wpisz 6-cyfrowy kod
+ description: Zwiększ bezpieczeństwo konta, konfigurując uwierzytelnianie dwuskładnikowe
+ page_title: Konfiguracja uwierzytelniania dwuskładnikowego
+ scan_description: Użyj aplikacji uwierzytelniającej, np. Google Authenticator lub 1Password, aby zeskanować ten kod QR
+ scan_title: 1. Zeskanuj kod QR
+ secret_description: Jeśli nie możesz zeskanować kodu QR, wpisz ręcznie ten klucz tajny w aplikacji uwierzytelniającej
+ secret_title: Kod do ręcznego wpisania
+ title: Skonfiguruj uwierzytelnianie dwuskładnikowe
+ verify_button: Zweryfikuj i włącz 2FA
+ verify_description: Wpisz 6-cyfrowy kod z aplikacji uwierzytelniającej
+ verify_title: 2. Wprowadź kod weryfikacyjny
+ verify:
+ description: Wpisz kod z aplikacji uwierzytelniającej, aby kontynuować
+ page_title: Zweryfikuj uwierzytelnianie dwuskładnikowe
+ title: Uwierzytelnianie dwuskładnikowe
+ verify_button: Zweryfikuj
+ verify_code:
+ invalid_code: Nieprawidłowy kod uwierzytelniający. Spróbuj ponownie.
diff --git a/config/locales/views/oidc_accounts/en.yml b/config/locales/views/oidc_accounts/en.yml
index 4ed91677c..e04729307 100644
--- a/config/locales/views/oidc_accounts/en.yml
+++ b/config/locales/views/oidc_accounts/en.yml
@@ -18,6 +18,7 @@ en:
info_email: "Email:"
info_name: "Name:"
submit_create: Create Account
+ submit_accept_invitation: Accept Invitation
account_creation_disabled: New account creation via single sign-on is disabled. Please contact an administrator to create your account.
cancel: Cancel
new_user:
diff --git a/config/locales/views/oidc_accounts/pl.yml b/config/locales/views/oidc_accounts/pl.yml
new file mode 100644
index 000000000..8b24b8217
--- /dev/null
+++ b/config/locales/views/oidc_accounts/pl.yml
@@ -0,0 +1,34 @@
+---
+pl:
+ oidc_accounts:
+ link:
+ title_link: Połącz konto OIDC
+ title_create: Utwórz konto
+ verify_heading: Zweryfikuj swoją tożsamość
+ verify_description_html: "Aby połączyć konto %{provider}%{email_suffix}, zweryfikuj tożsamość, wpisując hasło."
+ email_suffix_html: " (adres: %{email})"
+ email_label: E-mail
+ email_placeholder: Wpisz e-mail
+ password_label: Hasło
+ password_placeholder: Wpisz hasło
+ verify_hint: To pomaga upewnić się, że tylko Ty możesz łączyć zewnętrzne konta ze swoim profilem.
+ submit_link: Połącz konto
+ create_heading: Utwórz nowe konto
+ create_description_html: "Nie znaleziono konta dla adresu %{email}. Kliknij poniżej, aby utworzyć nowe konto używając tożsamości %{provider}."
+ info_email: "E-mail:"
+ info_name: "Imię i nazwisko:"
+ submit_create: Utwórz konto
+ submit_accept_invitation: Zaakceptuj zaproszenie
+ account_creation_disabled: Tworzenie nowych kont przez logowanie jednokrotne jest wyłączone. Skontaktuj się z administratorem, aby utworzyć konto.
+ cancel: Anuluj
+ new_user:
+ title: Dokończ tworzenie konta
+ heading: Utwórz swoje konto
+ description: Potwierdź swoje dane, aby dokończyć tworzenie konta przy użyciu tożsamości %{provider}.
+ email_label: E-mail (od dostawcy SSO)
+ first_name_label: Imię
+ first_name_placeholder: Wpisz imię
+ last_name_label: Nazwisko
+ last_name_placeholder: Wpisz nazwisko
+ submit: Utwórz konto
+ cancel: Anuluj
diff --git a/config/locales/views/onboardings/de.yml b/config/locales/views/onboardings/de.yml
index 845e9a3fb..741805a63 100644
--- a/config/locales/views/onboardings/de.yml
+++ b/config/locales/views/onboardings/de.yml
@@ -16,8 +16,13 @@ de:
first_name_placeholder: Vorname
last_name: Nachname
last_name_placeholder: Nachname
+ group_name: Gruppenname
+ group_name_placeholder: Gruppenname
household_name: Haushaltsname
household_name_placeholder: Haushaltsname
+ moniker_prompt: "%{product_name} wird genutzt mit …"
+ moniker_family: Familienmitglieder (nur Sie selbst oder mit Partner, Kindern usw.)
+ moniker_group: Personengruppe (Firma, Verein, Verband o. Ä.)
country: Land
submit: Weiter
preferences:
diff --git a/config/locales/views/onboardings/es.yml b/config/locales/views/onboardings/es.yml
index 1fc48268a..d32b480cf 100644
--- a/config/locales/views/onboardings/es.yml
+++ b/config/locales/views/onboardings/es.yml
@@ -16,8 +16,13 @@ es:
first_name_placeholder: Nombre
last_name: Apellido
last_name_placeholder: Apellido
+ group_name: Nombre del grupo
+ group_name_placeholder: Nombre del grupo
household_name: Nombre del hogar
household_name_placeholder: Nombre del hogar
+ moniker_prompt: "Usaré %{product_name} con..."
+ moniker_family: Miembros de la familia (solo tú o con pareja, adolescentes, etc.)
+ moniker_group: Un grupo de personas (empresa, club, asociación o cualquier otro tipo)
country: País
submit: Continuar
preferences:
diff --git a/config/locales/views/onboardings/pl.yml b/config/locales/views/onboardings/pl.yml
new file mode 100644
index 000000000..f62cf790c
--- /dev/null
+++ b/config/locales/views/onboardings/pl.yml
@@ -0,0 +1,66 @@
+---
+pl:
+ onboardings:
+ header:
+ sign_out: Wyloguj się
+ setup: Konfiguracja
+ preferences: Preferencje
+ goals: Cele
+ start: Początek
+ logout:
+ sign_out: Wyloguj się
+ show:
+ title: Skonfigurujmy Twoje konto
+ subtitle: Na początek skonfigurujmy Twój profil.
+ first_name: Imię
+ first_name_placeholder: Imię
+ last_name: Nazwisko
+ last_name_placeholder: Nazwisko
+ group_name: Nazwa grupy
+ group_name_placeholder: Nazwa grupy
+ household_name: Nazwa gospodarstwa domowego
+ household_name_placeholder: Nazwa gospodarstwa domowego
+ moniker_prompt: Będę używać %{product_name} z ...
+ moniker_family: Członkami rodziny (tylko sobą lub z partnerem, nastolatkami itd.)
+ moniker_group: Grupą osób (firma, klub, stowarzyszenie lub inny typ)
+ country: Kraj
+ submit: Dalej
+ preferences:
+ title: Skonfiguruj swoje preferencje
+ subtitle: Skonfigurujmy Twoje preferencje.
+ example: Przykładowe konto
+ preview: Podgląd sposobu wyświetlania danych na podstawie preferencji.
+ color_theme: Motyw kolorystyczny
+ theme_system: Systemowy
+ theme_light: Jasny
+ theme_dark: Ciemny
+ locale: Język
+ currency: Waluta
+ date_format: Format daty
+ submit: Zakończ
+ goals:
+ title: Co Cię tutaj sprowadza?
+ subtitle: Wybierz jeden lub więcej celów, które chcesz osiągnąć, używając %{product_name} do zarządzania finansami osobistymi.
+ unified_accounts: Widzieć wszystkie moje konta w jednym miejscu
+ cashflow: Lepiej rozumieć przepływy pieniężne i wydatki
+ budgeting: Zarządzać planami finansowymi i budżetem
+ partner: Zarządzać finansami z partnerem
+ investments: Śledzić inwestycje
+ ai_insights: Korzystać z AI do lepszego zrozumienia moich finansów
+ optimization: Analizować i optymalizować konta
+ reduce_stress: Zmniejszyć stres lub niepokój związany z finansami
+ submit: Dalej
+ trial:
+ title: Wypróbuj Sure przez 45 dni
+ data_deletion: Dane zostaną wtedy usunięte
+ description_html: Od dziś możesz dokładnie przetestować produkt. Jeśli Ci się spodoba, uruchom go samodzielnie lub wesprzyj projekt, aby dalej korzystać tutaj.
+ try_button: Wypróbuj Sure przez 45 dni
+ continue_trial: Kontynuuj okres próbny
+ upgrade: Ulepsz
+ how_it_works: Jak to tutaj działa
+ today: Dziś
+ today_description: Otrzymasz bezpłatny dostęp do Sure na 45 dni na naszym AWS.
+ in_40_days: Za 40 dni (%{date})
+ in_40_days_description: Wyślemy Ci przypomnienie o eksporcie danych.
+ in_45_days: Za 45 dni (%{date})
+ in_45_days_description: Usuwamy dane — wesprzyj projekt, aby dalej korzystać z Sure tutaj!
diff --git a/config/locales/views/other_assets/de.yml b/config/locales/views/other_assets/de.yml
index 969e44f52..a6bc88674 100644
--- a/config/locales/views/other_assets/de.yml
+++ b/config/locales/views/other_assets/de.yml
@@ -3,5 +3,7 @@ de:
other_assets:
edit:
edit: "%{account} bearbeiten"
+ balance_tracking_info: "Sonstige Vermögenswerte werden über manuelle Bewertungen („Neuer Saldo“) erfasst, nicht über Buchungen. Cashflow ändert den Kontostand nicht."
new:
title: Vermögenswertdetails eingeben
+ balance_tracking_info: "Sonstige Vermögenswerte werden über manuelle Bewertungen („Neuer Saldo“) erfasst, nicht über Buchungen. Cashflow ändert den Kontostand nicht."
diff --git a/config/locales/views/other_assets/es.yml b/config/locales/views/other_assets/es.yml
index d04b956ae..3a48fddcd 100644
--- a/config/locales/views/other_assets/es.yml
+++ b/config/locales/views/other_assets/es.yml
@@ -3,5 +3,7 @@ es:
other_assets:
edit:
edit: Editar %{account}
+ balance_tracking_info: "El seguimiento de otros activos se realiza mediante valoraciones manuales usando 'Nuevo saldo', no mediante transacciones. El flujo de caja no afectará al saldo de la cuenta."
new:
title: Introduce los detalles del activo
+ balance_tracking_info: "El seguimiento de otros activos se realiza mediante valoraciones manuales usando 'Nuevo saldo', no mediante transacciones. El flujo de caja no afectará al saldo de la cuenta."
diff --git a/config/locales/views/other_assets/pl.yml b/config/locales/views/other_assets/pl.yml
new file mode 100644
index 000000000..3e27ea98e
--- /dev/null
+++ b/config/locales/views/other_assets/pl.yml
@@ -0,0 +1,9 @@
+---
+pl:
+ other_assets:
+ edit:
+ edit: Edytuj %{account}
+ balance_tracking_info: Inne aktywa są śledzone poprzez ręczne wyceny za pomocą opcji „Nowe saldo”, a nie transakcje. Przepływy pieniężne nie wpływają na saldo konta.
+ new:
+ title: Wprowadź dane aktywa
+ balance_tracking_info: Inne aktywa są śledzone poprzez ręczne wyceny za pomocą opcji „Nowe saldo”, a nie transakcje. Przepływy pieniężne nie wpływają na saldo konta.
diff --git a/config/locales/views/other_liabilities/pl.yml b/config/locales/views/other_liabilities/pl.yml
new file mode 100644
index 000000000..b6ef16a55
--- /dev/null
+++ b/config/locales/views/other_liabilities/pl.yml
@@ -0,0 +1,7 @@
+---
+pl:
+ other_liabilities:
+ edit:
+ edit: Edytuj %{account}
+ new:
+ title: Wprowadź dane zobowiązania
diff --git a/config/locales/views/pages/de.yml b/config/locales/views/pages/de.yml
index 4df171506..a381eb2d6 100644
--- a/config/locales/views/pages/de.yml
+++ b/config/locales/views/pages/de.yml
@@ -3,10 +3,20 @@ de:
pages:
changelog:
title: Was ist neu
+ privacy:
+ title: Datenschutzrichtlinie
+ heading: Datenschutzrichtlinie
+ placeholder: Der Inhalt der Datenschutzrichtlinie wird hier angezeigt.
+ terms:
+ title: Nutzungsbedingungen
+ heading: Nutzungsbedingungen
+ placeholder: Der Inhalt der Nutzungsbedingungen wird hier angezeigt.
dashboard:
welcome: "Willkommen zurück, %{name}"
subtitle: "Hier siehst du, was in deinen Finanzen passiert."
new: "Neu"
+ drag_to_reorder: "Bereich per Drag & Drop neu anordnen"
+ toggle_section: "Sichtbarkeit des Bereichs umschalten"
net_worth_chart:
data_not_available: Für den ausgewählten Zeitraum sind keine Daten verfügbar.
title: Nettovermögen
@@ -15,6 +25,7 @@ de:
no_account_subtitle: Da noch keine Konten hinzugefügt wurden, gibt es keine Daten anzuzeigen. Füge dein erstes Konto hinzu, um Dashboard-Daten zu sehen.
no_account_title: Noch keine Konten vorhanden
balance_sheet:
+ title: "Bilanz"
no_items: "Noch keine %{name}"
add_accounts: "Füge deine %{name}-Konten hinzu, um eine vollständige Übersicht zu erhalten."
cashflow_sankey:
@@ -29,3 +40,19 @@ de:
outflows_donut:
title: "Ausgaben"
total_outflows: "Gesamtausgaben"
+ categories: "Kategorien"
+ value: "Wert"
+ weight: "Gewicht"
+ investment_summary:
+ title: "Investitionen"
+ total_return: "Gesamtrendite"
+ holding: "Position"
+ weight: "Gewicht"
+ value: "Wert"
+ return: "Rendite"
+ period_activity: "%{period} Aktivität"
+ contributions: "Einlagen"
+ withdrawals: "Entnahmen"
+ trades: "Trades"
+ no_investments: "Keine Anlagekonten"
+ add_investment: "Füge ein Anlagekonto hinzu, um dein Portfolio zu verfolgen"
diff --git a/config/locales/views/pages/es.yml b/config/locales/views/pages/es.yml
index 1c3c86115..db7095fb2 100644
--- a/config/locales/views/pages/es.yml
+++ b/config/locales/views/pages/es.yml
@@ -3,10 +3,20 @@ es:
pages:
changelog:
title: Novedades
+ privacy:
+ title: Política de privacidad
+ heading: Política de privacidad
+ placeholder: El contenido de la política de privacidad se mostrará aquí.
+ terms:
+ title: Condiciones del servicio
+ heading: Condiciones del servicio
+ placeholder: El contenido de las condiciones del servicio se mostrará aquí.
dashboard:
welcome: "Bienvenido de nuevo, %{name}"
subtitle: "Esto es lo que está pasando con tus finanzas"
new: "Nuevo"
+ drag_to_reorder: "Arrastra para reordenar la sección"
+ toggle_section: "Alternar visibilidad de la sección"
net_worth_chart:
data_not_available: Datos no disponibles para el período seleccionado
title: Patrimonio neto
@@ -15,6 +25,7 @@ es:
no_account_subtitle: Como no se han añadido cuentas, no hay datos para mostrar. Añade tus primeras cuentas para empezar a ver los datos del panel.
no_account_title: Aún no hay cuentas
balance_sheet:
+ title: "Balance de situación"
no_items: "Aún no hay %{name}"
add_accounts: "Añade tus cuentas de %{name} para ver un desglose completo"
cashflow_sankey:
@@ -29,3 +40,19 @@ es:
outflows_donut:
title: "Salidas"
total_outflows: "Salidas totales"
+ categories: "Categorías"
+ value: "Valor"
+ weight: "Peso"
+ investment_summary:
+ title: "Inversiones"
+ total_return: "Rentabilidad total"
+ holding: "Activo"
+ weight: "Peso"
+ value: "Valor"
+ return: "Rentabilidad"
+ period_activity: "Actividad de %{period}"
+ contributions: "Aportaciones"
+ withdrawals: "Retiradas"
+ trades: "Operaciones"
+ no_investments: "No hay cuentas de inversión"
+ add_investment: "Añade una cuenta de inversión para realizar el seguimiento de tu cartera"
\ No newline at end of file
diff --git a/config/locales/views/pages/pl.yml b/config/locales/views/pages/pl.yml
new file mode 100644
index 000000000..bdf2c9713
--- /dev/null
+++ b/config/locales/views/pages/pl.yml
@@ -0,0 +1,79 @@
+---
+pl:
+ pages:
+ changelog:
+ title: Co nowego
+ privacy:
+ title: Polityka prywatności
+ heading: Polityka prywatności
+ placeholder: Treść polityki prywatności będzie wyświetlana tutaj.
+ terms:
+ title: Warunki korzystania z usługi
+ heading: Warunki korzystania z usługi
+ placeholder: Treść warunków korzystania z usługi będzie wyświetlana tutaj.
+ dashboard:
+ welcome: Witaj ponownie, %{name}
+ subtitle: Zobacz, co dzieje się z Twoimi finansami
+ new: Nowe
+ bond_rate_review_notice:
+ one: "1 partia obligacji wymaga aktualizacji stopy emisji (%{accounts})."
+ few: "%{count} partie obligacji wymagają aktualizacji stopy emisji (%{accounts})."
+ many: "%{count} partii obligacji wymaga aktualizacji stopy emisji (%{accounts})."
+ other: "%{count} partii obligacji wymaga aktualizacji stopy emisji (%{accounts})."
+ drag_to_reorder: Przeciągnij, aby zmienić kolejność sekcji
+ toggle_section: Przełącz widoczność sekcji
+ net_worth_chart:
+ data_not_available: Dane niedostępne dla wybranego okresu
+ title: Majątek netto
+ no_account_empty_state:
+ new_account: Nowe konto
+ no_account_subtitle: Ponieważ nie dodano jeszcze żadnych kont, nie ma danych do wyświetlenia. Dodaj pierwsze konto, aby zacząć przeglądać dane pulpitu.
+ no_account_title: Brak kont
+ balance_sheet:
+ title: Bilans
+ no_items: Brak %{name}
+ add_accounts: Dodaj konta %{name}, aby zobaczyć pełny podział
+ cashflow_sankey:
+ title: Przepływy pieniężne
+ no_data_title: Brak danych o przepływach pieniężnych dla tego okresu
+ no_data_description: Dodaj transakcje, aby wyświetlić dane o przepływach pieniężnych, albo poszerz zakres czasu
+ add_transaction: Dodaj transakcję
+ no_accounts:
+ title: Brak kont
+ description: Dodaj konta, aby wyświetlić dane o majątku netto
+ add_account: Dodaj konto
+ outflows_donut:
+ title: Wydatki
+ total_outflows: Łączne wydatki
+ categories: Kategorie
+ value: Wartość
+ weight: Udział
+ investment_summary:
+ title: Inwestycje
+ total_return: Łączny zwrot
+ holding: Pozycja
+ weight: Udział
+ value: Wartość
+ return: Zwrot
+ period_activity: Aktywność za %{period}
+ contributions: Wpłaty
+ withdrawals: Wypłaty
+ trades: Transakcje giełdowe
+ no_investments: Brak kont inwestycyjnych
+ add_investment: Dodaj konto inwestycyjne, aby śledzić swój portfel
+ bond_summary:
+ title: Obligacje
+ total_return: Łączny zwrot
+ bond: Obligacja
+ rate: Oprocentowanie
+ principal: Kapitał
+ maturity: Zapadalność
+ maturity_label: Data zapadalności
+ principal_term: Kapitał, %{term}
+ term_months:
+ one: 1 miesiąc
+ few: "%{count} miesiące"
+ many: "%{count} miesięcy"
+ other: "%{count} miesiąca"
+ no_bonds: Brak kont obligacji
+ account_wrapper: "%{account} • %{wrapper}"
diff --git a/config/locales/views/password_mailer/pl.yml b/config/locales/views/password_mailer/pl.yml
new file mode 100644
index 000000000..f9144cf1f
--- /dev/null
+++ b/config/locales/views/password_mailer/pl.yml
@@ -0,0 +1,8 @@
+---
+pl:
+ password_mailer:
+ password_reset:
+ cta: Zresetuj hasło
+ ignore_if_not_requested: Jeśli to nie Ty wysłałeś(aś) to żądanie, zignoruj tę wiadomość.
+ request_made: Otrzymaliśmy prośbę o reset hasła do %{product_name}. Kliknij link, aby je zresetować.
+ subject: "%{product_name}: zresetuj hasło"
diff --git a/config/locales/views/password_resets/de.yml b/config/locales/views/password_resets/de.yml
index 01d03288d..4f7536949 100644
--- a/config/locales/views/password_resets/de.yml
+++ b/config/locales/views/password_resets/de.yml
@@ -1,6 +1,8 @@
---
de:
password_resets:
+ disabled: Passwort-Zurücksetzen über Sure ist deaktiviert. Bitte setzen Sie Ihr Passwort über Ihren Identitätsanbieter zurück.
+ sso_only_user: Ihr Konto nutzt SSO zur Anmeldung. Bitte wenden Sie sich an Ihren Administrator, um Ihre Zugangsdaten zu verwalten.
edit:
title: Passwort zurücksetzen
new:
diff --git a/config/locales/views/password_resets/es.yml b/config/locales/views/password_resets/es.yml
index 36fe99a79..4fa5c10ea 100644
--- a/config/locales/views/password_resets/es.yml
+++ b/config/locales/views/password_resets/es.yml
@@ -1,14 +1,15 @@
---
es:
password_resets:
+ disabled: El restablecimiento de contraseña a través de Sure está desactivado. Por favor, restablece tu contraseña a través de tu proveedor de identidad.
+ sso_only_user: Tu cuenta utiliza SSO para la autenticación. Por favor, contacta con tu administrador para gestionar tus credenciales.
edit:
title: Restablecer contraseña
new:
- requested: Por favor, revisa tu correo electrónico para obtener
- un enlace para restablecer tu contraseña.
+ requested: Por favor, revisa tu correo electrónico para obtener un enlace para restablecer tu contraseña.
submit: Restablecer contraseña
title: Restablecer contraseña
back: Volver
update:
- invalid_token: Token inválido.
- success: Tu contraseña ha sido restablecida.
+ invalid_token: Token no válido.
+ success: Tu contraseña ha sido restablecida.
\ No newline at end of file
diff --git a/config/locales/views/password_resets/pl.yml b/config/locales/views/password_resets/pl.yml
new file mode 100644
index 000000000..6ee9f6368
--- /dev/null
+++ b/config/locales/views/password_resets/pl.yml
@@ -0,0 +1,15 @@
+---
+pl:
+ password_resets:
+ disabled: Resetowanie hasła przez Sure jest wyłączone. Zresetuj hasło przez swojego dostawcę tożsamości.
+ sso_only_user: Twoje konto używa SSO do uwierzytelniania. Skontaktuj się z administratorem, aby zarządzać danymi logowania.
+ edit:
+ title: Zresetuj hasło
+ new:
+ requested: Sprawdź swoją skrzynkę e-mail, aby zresetować hasło.
+ submit: Zresetuj hasło
+ title: Zresetuj hasło
+ back: Wróć
+ update:
+ invalid_token: Nieprawidłowy token.
+ success: Twoje hasło zostało zresetowane.
diff --git a/config/locales/views/passwords/pl.yml b/config/locales/views/passwords/pl.yml
new file mode 100644
index 000000000..19c1cef79
--- /dev/null
+++ b/config/locales/views/passwords/pl.yml
@@ -0,0 +1,10 @@
+---
+pl:
+ passwords:
+ edit:
+ password: Nowe hasło
+ password_challenge: Obecne hasło
+ submit: Zresetuj hasło
+ title: Zaktualizuj hasło
+ update:
+ success: Twoje hasło zostało zresetowane.
diff --git a/config/locales/views/pdf_import_mailer/de.yml b/config/locales/views/pdf_import_mailer/de.yml
new file mode 100644
index 000000000..8df811a38
--- /dev/null
+++ b/config/locales/views/pdf_import_mailer/de.yml
@@ -0,0 +1,17 @@
+---
+de:
+ pdf_import_mailer:
+ next_steps:
+ greeting: "Hallo %{name},"
+ intro: "Wir haben die PDF-Datei analysiert, die Sie an %{product} hochgeladen haben."
+ document_type_label: Dokumenttyp
+ summary_label: KI-Zusammenfassung
+ transactions_note: Dieses Dokument scheint Buchungen zu enthalten. Sie können diese jetzt extrahieren und prüfen.
+ document_stored_note: Dieses Dokument wurde zu Ihrer Referenz gespeichert. Es kann für Kontext in zukünftigen KI-Gesprächen verwendet werden.
+ next_steps_label: Was als Nächstes?
+ next_steps_intro: "Sie haben mehrere Möglichkeiten:"
+ option_extract_transactions: Buchungen aus diesem Kontoauszug extrahieren
+ option_keep_reference: Dokument für Referenz in zukünftigen KI-Gesprächen behalten
+ option_delete: Import löschen, wenn Sie ihn nicht mehr benötigen
+ view_import_button: Importdetails anzeigen
+ footer_note: Dies ist eine automatische Nachricht. Bitte antworten Sie nicht direkt auf diese E-Mail.
diff --git a/config/locales/views/pdf_import_mailer/es.yml b/config/locales/views/pdf_import_mailer/es.yml
new file mode 100644
index 000000000..d7c2e24af
--- /dev/null
+++ b/config/locales/views/pdf_import_mailer/es.yml
@@ -0,0 +1,17 @@
+---
+es:
+ pdf_import_mailer:
+ next_steps:
+ greeting: "Hola %{name},"
+ intro: "Hemos terminado de analizar el documento PDF que subiste a %{product}."
+ document_type_label: Tipo de documento
+ summary_label: Resumen de la IA
+ transactions_note: Este documento parece contener transacciones. Ya puedes extraerlas y revisarlas.
+ document_stored_note: Este documento ha sido guardado para tu referencia. Se puede utilizar para proporcionar contexto en futuras conversaciones con la IA.
+ next_steps_label: ¿Qué sigue ahora?
+ next_steps_intro: "Tienes varias opciones:"
+ option_extract_transactions: Extraer las transacciones de este extracto
+ option_keep_reference: Guardar este documento como referencia para futuras conversaciones con la IA
+ option_delete: Eliminar esta importación si ya no la necesitas
+ view_import_button: Ver detalles de la importación
+ footer_note: Este es un mensaje automático. Por favor, no respondas directamente a este correo electrónico.
\ No newline at end of file
diff --git a/config/locales/views/pdf_import_mailer/pl.yml b/config/locales/views/pdf_import_mailer/pl.yml
new file mode 100644
index 000000000..7881fcf32
--- /dev/null
+++ b/config/locales/views/pdf_import_mailer/pl.yml
@@ -0,0 +1,17 @@
+---
+pl:
+ pdf_import_mailer:
+ next_steps:
+ greeting: "Cześć %{name},"
+ intro: "Zakończyliśmy analizę dokumentu PDF przesłanego do %{product}."
+ document_type_label: Typ dokumentu
+ summary_label: Podsumowanie AI
+ transactions_note: Ten dokument wygląda na zawierający transakcje. Możesz je teraz wyodrębnić i przejrzeć.
+ document_stored_note: Ten dokument został zapisany do Twojego wglądu. Może służyć jako kontekst w przyszłych rozmowach z AI.
+ next_steps_label: Co dalej?
+ next_steps_intro: "Masz kilka opcji:"
+ option_extract_transactions: Wyodrębnij transakcje z tego wyciągu
+ option_keep_reference: Zachowaj dokument jako materiał referencyjny do przyszłych rozmów z AI
+ option_delete: Usuń ten import, jeśli nie jest już potrzebny
+ view_import_button: Zobacz szczegóły importu
+ footer_note: To wiadomość automatyczna. Prosimy nie odpowiadać bezpośrednio na ten e-mail.
diff --git a/config/locales/views/pending_duplicate_merges/en.yml b/config/locales/views/pending_duplicate_merges/en.yml
new file mode 100644
index 000000000..c62c6e2b0
--- /dev/null
+++ b/config/locales/views/pending_duplicate_merges/en.yml
@@ -0,0 +1,14 @@
+---
+en:
+ pending_duplicate_merges:
+ new:
+ title: Merge with Posted Transaction
+ warning_title: Manual Duplicate Merging
+ warning_description: Use this to manually merge a pending transaction with its posted version. This will delete the pending transaction and keep only the posted one.
+ pending_transaction: Pending Transaction
+ select_posted: Select Posted Transaction to Merge With
+ showing_range: "Showing %{start} - %{end}"
+ previous: "← Previous 10"
+ next: "Next 10 →"
+ no_candidates: No posted transactions found in this account.
+ submit_button: Merge Transactions
diff --git a/config/locales/views/pending_duplicate_merges/pl.yml b/config/locales/views/pending_duplicate_merges/pl.yml
new file mode 100644
index 000000000..17051288b
--- /dev/null
+++ b/config/locales/views/pending_duplicate_merges/pl.yml
@@ -0,0 +1,14 @@
+---
+pl:
+ pending_duplicate_merges:
+ new:
+ title: Połącz z zaksięgowaną transakcją
+ warning_title: Ręczne łączenie duplikatów
+ warning_description: Użyj tej opcji, aby ręcznie połączyć transakcję oczekującą z jej zaksięgowaną wersją. Transakcja oczekująca zostanie usunięta, a pozostanie tylko zaksięgowana.
+ pending_transaction: Transakcja oczekująca
+ select_posted: Wybierz zaksięgowaną transakcję do połączenia
+ showing_range: "Wyświetlane %{start} - %{end}"
+ previous: "← Poprzednie 10"
+ next: "Następne 10 →"
+ no_candidates: Nie znaleziono zaksięgowanych transakcji na tym koncie.
+ submit_button: Połącz transakcje
diff --git a/config/locales/views/plaid_items/de.yml b/config/locales/views/plaid_items/de.yml
index 01371319d..033449fd2 100644
--- a/config/locales/views/plaid_items/de.yml
+++ b/config/locales/views/plaid_items/de.yml
@@ -21,3 +21,8 @@ de:
status_never: Synchronisierung erforderlich
syncing: Wird synchronisiert...
update: Verbindung aktualisieren
+ select_existing_account:
+ title: "%{account_name} mit Plaid verknüpfen"
+ description: Wählen Sie ein Plaid-Konto zur Verknüpfung mit Ihrem bestehenden Konto
+ cancel: Abbrechen
+ link_account: Konto verknüpfen
diff --git a/config/locales/views/plaid_items/es.yml b/config/locales/views/plaid_items/es.yml
index 7931f1591..32c3802fe 100644
--- a/config/locales/views/plaid_items/es.yml
+++ b/config/locales/views/plaid_items/es.yml
@@ -8,12 +8,10 @@ es:
plaid_item:
add_new: Añadir nueva conexión
confirm_accept: Eliminar institución
- confirm_body: Esto eliminará permanentemente todas las cuentas de este grupo y
- todos los datos asociados.
+ confirm_body: Esto eliminará permanentemente todas las cuentas de este grupo y todos los datos asociados.
confirm_title: ¿Eliminar institución?
connection_lost: Conexión perdida
- connection_lost_description: Esta conexión ya no es válida. Necesitarás
- eliminar esta conexión y añadirla de nuevo para continuar sincronizando los datos.
+ connection_lost_description: Esta conexión ya no es válida. Necesitarás eliminar esta conexión y añadirla de nuevo para continuar sincronizando los datos.
delete: Eliminar
error: Ocurrió un error mientras se sincronizaban los datos
no_accounts_description: No pudimos cargar ninguna cuenta de esta institución financiera.
@@ -23,3 +21,8 @@ es:
status_never: Requiere sincronización de datos
syncing: Sincronizando...
update: Actualizar conexión
+ select_existing_account:
+ title: "Vincular %{account_name} a Plaid"
+ description: Selecciona una cuenta de Plaid para vincularla a tu cuenta existente
+ cancel: Cancelar
+ link_account: Vincular cuenta
\ No newline at end of file
diff --git a/config/locales/views/plaid_items/pl.yml b/config/locales/views/plaid_items/pl.yml
new file mode 100644
index 000000000..d72409f10
--- /dev/null
+++ b/config/locales/views/plaid_items/pl.yml
@@ -0,0 +1,28 @@
+---
+pl:
+ plaid_items:
+ create:
+ success: Konto zostało pomyślnie połączone. Poczekaj na synchronizację kont.
+ destroy:
+ success: Konta zostały zaplanowane do usunięcia.
+ plaid_item:
+ add_new: Dodaj nowe połączenie
+ confirm_accept: Usuń instytucję
+ confirm_body: To trwale usunie wszystkie konta w tej grupie oraz wszystkie powiązane dane.
+ confirm_title: Usunąć instytucję?
+ connection_lost: Utracono połączenie
+ connection_lost_description: To połączenie nie jest już prawidłowe. Aby kontynuować synchronizację danych, usuń je i dodaj ponownie.
+ delete: Usuń
+ error: Wystąpił błąd podczas synchronizacji danych
+ no_accounts_description: Nie udało się wczytać żadnych kont z tej instytucji finansowej.
+ no_accounts_title: Nie znaleziono kont
+ requires_update: Połącz ponownie
+ status: Ostatnia synchronizacja %{timestamp} temu
+ status_never: Wymaga synchronizacji danych
+ syncing: Synchronizacja...
+ update: Aktualizuj
+ select_existing_account:
+ title: Połącz %{account_name} z Plaid
+ description: Wybierz konto Plaid, aby połączyć je z istniejącym kontem
+ cancel: Anuluj
+ link_account: Połącz konto
diff --git a/config/locales/views/properties/pl.yml b/config/locales/views/properties/pl.yml
new file mode 100644
index 000000000..efa8e4528
--- /dev/null
+++ b/config/locales/views/properties/pl.yml
@@ -0,0 +1,32 @@
+---
+pl:
+ properties:
+ edit:
+ edit: Edytuj %{account}
+ form:
+ address_line1: Adres
+ address_line1_placeholder: 123 Main St
+ area: Powierzchnia użytkowa
+ area_placeholder: '2000'
+ area_unit: Jednostka miary
+ country: Kraj
+ country_placeholder: US
+ locality: Miasto
+ locality_placeholder: San Francisco
+ none: Brak
+ postal_code: Kod pocztowy
+ postal_code_placeholder: '94105'
+ region: Województwo/Region
+ region_placeholder: CA
+ subtype_prompt: Wybierz typ nieruchomości
+ year_built: Rok budowy
+ year_built_placeholder: '2000'
+ new:
+ title: Wprowadź dane nieruchomości
+ overview:
+ living_area: Powierzchnia użytkowa
+ market_value: Wartość rynkowa
+ purchase_price: Cena zakupu
+ trend: Trend
+ unknown: Nieznane
+ year_built: Rok budowy
diff --git a/config/locales/views/recurring_transactions/de.yml b/config/locales/views/recurring_transactions/de.yml
index 305e2461f..ddb4bc938 100644
--- a/config/locales/views/recurring_transactions/de.yml
+++ b/config/locales/views/recurring_transactions/de.yml
@@ -4,10 +4,18 @@ de:
upcoming: Anstehende wiederkehrende Transaktionen
projected: Prognostiziert
recurring: Wiederkehrend
+ expected_today: "Erwartet heute"
+ expected_in:
+ one: "Erwartet in %{count} Tag"
+ other: "Erwartet in %{count} Tagen"
expected_on: Erwartet am %{date}
day_of_month: Tag %{day} des Monats
identify_patterns: Muster erkennen
cleanup_stale: Alte Einträge bereinigen
+ settings:
+ enable_label: Wiederkehrende Transaktionen aktivieren
+ enable_description: Erkenne automatisch wiederkehrende Transaktionsmuster und zeige anstehende prognostizierte Transaktionen an.
+ settings_updated: Einstellungen für wiederkehrende Transaktionen aktualisiert
info:
title: Automatische Mustererkennung
manual_description: Du kannst Muster manuell erkennen oder alte wiederkehrende Transaktionen mit den obigen Schaltflächen bereinigen.
@@ -21,6 +29,11 @@ de:
marked_active: Wiederkehrende Transaktion als aktiv markiert
deleted: Wiederkehrende Transaktion gelöscht
confirm_delete: Bist du sicher, dass du diese wiederkehrende Transaktion löschen möchtest?
+ marked_as_recurring: Transaktion als wiederkehrend markiert
+ already_exists: Für dieses Muster existiert bereits eine manuelle wiederkehrende Transaktion
+ creation_failed: Wiederkehrende Transaktion konnte nicht erstellt werden. Bitte überprüfe die Transaktionsdetails und versuche es erneut.
+ unexpected_error: Beim Erstellen der wiederkehrenden Transaktion ist ein unerwarteter Fehler aufgetreten
+ amount_range: "Bereich: %{min} bis %{max}"
empty:
title: Keine wiederkehrenden Transaktionen gefunden
description: Klicke auf „Muster erkennen“, um automatisch wiederkehrende Transaktionen aus deinem Verlauf zu erkennen.
@@ -35,3 +48,5 @@ de:
status:
active: Aktiv
inactive: Inaktiv
+ badges:
+ manual: Manuell
diff --git a/config/locales/views/recurring_transactions/es.yml b/config/locales/views/recurring_transactions/es.yml
index bd42f4268..690b835d8 100644
--- a/config/locales/views/recurring_transactions/es.yml
+++ b/config/locales/views/recurring_transactions/es.yml
@@ -5,10 +5,18 @@ es:
upcoming: Próximas Transacciones Recurrentes
projected: Proyectado
recurring: Recurrente
+ expected_today: "Esperado hoy"
+ expected_in:
+ one: "Esperado en %{count} día"
+ other: "Esperado en %{count} días"
expected_on: Esperado el %{date}
day_of_month: Día %{day} del mes
identify_patterns: Identificar Patrones
cleanup_stale: Limpiar Obsoletos
+ settings:
+ enable_label: Activar transacciones recurrentes
+ enable_description: Detecta automáticamente patrones de transacciones recurrentes y muestra las próximas transacciones proyectadas.
+ settings_updated: Configuración de transacciones recurrentes actualizada
info:
title: Detección Automática de Patrones
manual_description: Puedes identificar patrones manualmente o limpiar transacciones recurrentes obsoletas usando los botones de arriba.
@@ -22,11 +30,16 @@ es:
marked_active: Transacción recurrente marcada como activa
deleted: Transacción recurrente eliminada
confirm_delete: ¿Estás seguro de que deseas eliminar esta transacción recurrente?
+ marked_as_recurring: Transacción marcada como recurrente
+ already_exists: Ya existe una transacción recurrente manual para este patrón
+ creation_failed: Error al crear la transacción recurrente. Por favor, comprueba los detalles e inténtalo de nuevo.
+ unexpected_error: Ha ocurrido un error inesperado al crear la transacción recurrente
+ amount_range: "Rango: %{min} a %{max}"
empty:
title: No se encontraron transacciones recurrentes
description: Haz clic en "Identificar Patrones" para detectar automáticamente transacciones recurrentes de tu historial de transacciones.
table:
- merchant: Comerciante
+ merchant: Nombre
amount: Importe
expected_day: Día Esperado
next_date: Próxima Fecha
@@ -36,3 +49,5 @@ es:
status:
active: Activa
inactive: Inactiva
+ badges:
+ manual: Manual
\ No newline at end of file
diff --git a/config/locales/views/recurring_transactions/pl.yml b/config/locales/views/recurring_transactions/pl.yml
new file mode 100644
index 000000000..3825ffc41
--- /dev/null
+++ b/config/locales/views/recurring_transactions/pl.yml
@@ -0,0 +1,54 @@
+---
+pl:
+ recurring_transactions:
+ title: Transakcje cykliczne
+ upcoming: Nadchodzące transakcje cykliczne
+ projected: Prognozowane
+ recurring: Cykliczne
+ expected_today: Oczekiwane dziś
+ expected_in:
+ one: Oczekiwane za %{count} dzień
+ few: Oczekiwane za %{count} dni
+ many: Oczekiwane za %{count} dni
+ other: Oczekiwane za %{count} dni
+ day_of_month: Dzień %{day} miesiąca
+ identify_patterns: Wykryj wzorce
+ cleanup_stale: Wyczyść nieaktualne
+ settings:
+ enable_label: Włącz transakcje cykliczne
+ enable_description: Automatycznie wykrywaj wzorce transakcji cyklicznych i pokazuj nadchodzące prognozowane transakcje.
+ settings_updated: Ustawienia transakcji cyklicznych zostały zaktualizowane
+ info:
+ title: Automatyczne wykrywanie wzorców
+ manual_description: Możesz ręcznie wykryć wzorce lub wyczyścić nieaktualne transakcje cykliczne przy użyciu przycisków powyżej.
+ automatic_description: 'Automatyczne wykrywanie uruchamia się także po:'
+ triggers:
+ - zakończeniu importu CSV (transakcje, transakcje giełdowe, konta itd.)
+ - zakończeniu synchronizacji dowolnego dostawcy (Plaid, SimpleFIN itd.)
+ identified: Wykryto %{count} wzorców transakcji cyklicznych
+ cleaned_up: Wyczyszczono %{count} nieaktualnych transakcji cyklicznych
+ marked_inactive: Transakcję cykliczną oznaczono jako nieaktywną
+ marked_active: Transakcję cykliczną oznaczono jako aktywną
+ deleted: Transakcja cykliczna została usunięta
+ confirm_delete: Czy na pewno chcesz usunąć tę transakcję cykliczną?
+ marked_as_recurring: Transakcja została oznaczona jako cykliczna
+ already_exists: Dla tego wzorca istnieje już ręcznie utworzona transakcja cykliczna
+ creation_failed: Nie udało się utworzyć transakcji cyklicznej. Sprawdź szczegóły transakcji i spróbuj ponownie.
+ unexpected_error: Wystąpił nieoczekiwany błąd podczas tworzenia transakcji cyklicznej
+ amount_range: 'Zakres: %{min} do %{max}'
+ empty:
+ title: Nie znaleziono transakcji cyklicznych
+ description: Kliknij „Wykryj wzorce”, aby automatycznie wykryć transakcje cykliczne na podstawie historii transakcji.
+ table:
+ merchant: Nazwa
+ amount: Kwota
+ expected_day: Oczekiwany dzień
+ next_date: Następna data
+ last_occurrence: Ostatnie wystąpienie
+ status: Status
+ actions: Akcje
+ status:
+ active: Aktywna
+ inactive: Nieaktywna
+ badges:
+ manual: Ręczna
diff --git a/config/locales/views/registrations/de.yml b/config/locales/views/registrations/de.yml
index f0747f360..f60f7d6ba 100644
--- a/config/locales/views/registrations/de.yml
+++ b/config/locales/views/registrations/de.yml
@@ -17,6 +17,7 @@ de:
invitation_message: "%{inviter} hat dich eingeladen als %{role} beizutreten"
join_family_title: "%{family} beitreten"
role_admin: Administrator
+ role_guest: Gast
role_member: Mitglied
submit: Konto erstellen
title: Erstelle dein Konto
diff --git a/config/locales/views/registrations/es.yml b/config/locales/views/registrations/es.yml
index fc4b1169d..fadc9e6d5 100644
--- a/config/locales/views/registrations/es.yml
+++ b/config/locales/views/registrations/es.yml
@@ -11,21 +11,21 @@ es:
closed: Las inscripciones están actualmente cerradas.
create:
failure: Hubo un problema al registrarse.
- invalid_invite_code: Código de invitación inválido, por favor inténtalo de nuevo.
+ invalid_invite_code: Código de invitación no válido, por favor inténtalo de nuevo.
success: Te has registrado con éxito.
new:
invitation_message: "%{inviter} te ha invitado a unirte como %{role}"
- join_family_title: Únete a %{family}
+ join_family_title: Únete a %{family} %{moniker}
role_admin: administrador
+ role_guest: invitado
role_member: miembro
submit: Crear cuenta
title: Crea tu cuenta
- welcome_body: Para comenzar, debes registrarte para obtener una nueva cuenta. Luego podrás
- configurar ajustes adicionales dentro de la aplicación.
- welcome_title: ¡Bienvenido a Self Hosted %{product_name}!
+ welcome_body: Para comenzar, debes registrarte para obtener una nueva cuenta. Luego podrás configurar ajustes adicionales dentro de la aplicación.
+ welcome_title: ¡Bienvenido a %{product_name} (Self Hosted)!
password_placeholder: Introduce tu contraseña
password_requirements:
length: Mínimo 8 caracteres
case: Mayúsculas y minúsculas
number: Un número (0-9)
- special: "Un carácter especial (!, @, #, $, %, etc)"
+ special: "Un carácter especial (!, @, #, $, %, etc)"
\ No newline at end of file
diff --git a/config/locales/views/registrations/pl.yml b/config/locales/views/registrations/pl.yml
new file mode 100644
index 000000000..c8cf9bac6
--- /dev/null
+++ b/config/locales/views/registrations/pl.yml
@@ -0,0 +1,31 @@
+---
+pl:
+ helpers:
+ label:
+ user:
+ invite_code: Kod zaproszenia
+ submit:
+ user:
+ create: Kontynuuj
+ registrations:
+ closed: Rejestracja jest obecnie zamknięta.
+ create:
+ failure: Wystąpił problem podczas rejestracji.
+ invalid_invite_code: Nieprawidłowy kod zaproszenia, spróbuj ponownie.
+ success: Rejestracja zakończyła się pomyślnie.
+ new:
+ invitation_message: "%{inviter} zaprosił(a) Cię do dołączenia jako %{role}"
+ join_family_title: Dołącz do %{family} %{moniker}
+ role_admin: administrator
+ role_guest: gość
+ role_member: członek
+ submit: Utwórz konto
+ title: Utwórz swoje konto
+ welcome_body: Aby rozpocząć, musisz zarejestrować nowe konto. Następnie będzie można skonfigurować dodatkowe ustawienia w aplikacji.
+ welcome_title: Witamy w Self Hosted %{product_name}!
+ password_placeholder: Wpisz hasło
+ password_requirements:
+ length: Minimum 8 znaków
+ case: Wielkie i małe litery
+ number: Liczba (0-9)
+ special: "Znak specjalny (!, @, #, $, %, itp.)"
diff --git a/config/locales/views/reports/de.yml b/config/locales/views/reports/de.yml
index 66f68d5a2..9baf148c1 100644
--- a/config/locales/views/reports/de.yml
+++ b/config/locales/views/reports/de.yml
@@ -5,6 +5,9 @@ de:
title: Berichte
subtitle: Umfassende Einblicke in deine finanzielle Situation
export: CSV exportieren
+ print_report: Bericht drucken
+ drag_to_reorder: "Bereich per Drag & Drop neu anordnen"
+ toggle_section: "Sichtbarkeit des Bereichs umschalten"
periods:
monthly: Monatlich
quarterly: Vierteljährlich
@@ -45,6 +48,7 @@ de:
budgeted: Budgetiert
remaining: Verbleibend
over_by: Überschritten um
+ shared: geteilt
suggested_daily: "%{amount} pro Tag empfohlen für %{days} verbleibende Tage"
no_budgets: Keine Budgetkategorien für diesen Monat eingerichtet
status:
@@ -80,6 +84,49 @@ de:
description: Erfasse deine Finanzen, indem du Transaktionen hinzufügst oder deine Konten verbindest, um umfassende Berichte zu sehen
add_transaction: Transaktion hinzufügen
add_account: Konto hinzufügen
+ net_worth:
+ title: Nettovermögen
+ current_net_worth: Aktuelles Nettovermögen
+ period_change: Änderung im Zeitraum
+ assets_vs_liabilities: Vermögen vs. Verbindlichkeiten
+ total_assets: Vermögen
+ total_liabilities: Verbindlichkeiten
+ no_assets: Keine Vermögenswerte
+ no_liabilities: Keine Verbindlichkeiten
+ investment_performance:
+ title: Anlageperformance
+ portfolio_value: Portfoliowert
+ total_return: Gesamtrendite
+ contributions: Einlagen im Zeitraum
+ withdrawals: Entnahmen im Zeitraum
+ top_holdings: Top-Positionen
+ holding: Position
+ weight: Gewicht
+ value: Wert
+ return: Rendite
+ accounts: Anlagekonten
+ gains_by_tax_treatment: Gewinne nach Steuerbehandlung
+ unrealized_gains: Nicht realisierte Gewinne
+ realized_gains: Realisierte Gewinne
+ total_gains: Gesamtgewinne
+ taxable_realized_note: Diese Gewinne können steuerpflichtig sein
+ no_data: "-"
+ view_details: Details anzeigen
+ holdings_count:
+ one: "%{count} Position"
+ other: "%{count} Positionen"
+ sells_count:
+ one: "%{count} Verkauf"
+ other: "%{count} Verkäufe"
+ holdings: Positionen
+ sell_trades: Verkaufstrades
+ and_more: "+%{count} weitere"
+ investment_flows:
+ title: Anlageflüsse
+ description: Verfolge Geldflüsse in und aus deinen Anlagekonten
+ contributions: Einlagen
+ withdrawals: Entnahmen
+ net_flow: Nettozufluss
transactions_breakdown:
title: Transaktionsübersicht
no_transactions: Keine Transaktionen für den ausgewählten Zeitraum und Filter gefunden
@@ -114,10 +161,14 @@ de:
expense: Ausgaben
income: Einnahmen
uncategorized: Ohne Kategorie
- transactions: Transaktionen
+ entries:
+ one: "%{count} Eintrag"
+ other: "%{count} Einträge"
percentage: "% des Gesamtbetrags"
pagination:
- showing: Zeige %{count} Transaktionen
+ showing:
+ one: Zeige %{count} Eintrag
+ other: Zeige %{count} Einträge
previous: Zurück
next: Weiter
google_sheets_instructions:
@@ -136,3 +187,52 @@ de:
open_sheets: Google Sheets öffnen
go_to_api_keys: Zu den API-Schlüsseln
close: Verstanden
+ print:
+ document_title: Finanzbericht
+ title: Finanzbericht
+ generated_on: "Erstellt am %{date}"
+ summary:
+ title: Zusammenfassung
+ income: Einnahmen
+ expenses: Ausgaben
+ net_savings: Nettoersparnis
+ budget: Budget
+ vs_prior: "%{percent}% vs. Vorperiode"
+ of_income: "%{percent}% der Einnahmen"
+ used: genutzt
+ net_worth:
+ title: Nettovermögen
+ current_balance: Aktueller Kontostand
+ this_period: dieser Zeitraum
+ assets: Vermögen
+ liabilities: Verbindlichkeiten
+ no_liabilities: Keine Verbindlichkeiten
+ trends:
+ title: Monatliche Trends
+ month: Monat
+ income: Einnahmen
+ expenses: Ausgaben
+ net: Netto
+ savings_rate: Sparquote
+ average: Durchschnitt
+ current_month_note: "* Aktueller Monat (Teildaten)"
+ investments:
+ title: Anlagen
+ portfolio_value: Portfoliowert
+ total_return: Gesamtrendite
+ contributions: Einlagen
+ withdrawals: Entnahmen
+ this_period: dieser Zeitraum
+ top_holdings: Top-Positionen
+ holding: Position
+ weight: Gewicht
+ value: Wert
+ return: Rendite
+ spending:
+ title: Ausgaben nach Kategorie
+ income: Einnahmen
+ expenses: Ausgaben
+ category: Kategorie
+ amount: Betrag
+ percent: "%"
+ more_categories: "+ %{count} weitere Kategorien"
diff --git a/config/locales/views/reports/es.yml b/config/locales/views/reports/es.yml
index 8feab2eb5..00934b2fb 100644
--- a/config/locales/views/reports/es.yml
+++ b/config/locales/views/reports/es.yml
@@ -34,6 +34,7 @@ es:
budgeted: Presupuestado
remaining: Restante
over_by: Exceso de
+ shared: compartido
suggested_daily: "%{amount} sugerido por día durante los %{days} días restantes"
no_budgets: No hay categorías de presupuesto configuradas para este mes
status:
@@ -134,6 +135,22 @@ es:
value: Valor
return: Rentabilidad
accounts: Cuentas de inversión
+ gains_by_tax_treatment: Ganancias por tratamiento fiscal
+ unrealized_gains: Ganancias no realizadas
+ realized_gains: Ganancias realizadas
+ total_gains: Ganancias totales
+ taxable_realized_note: Estas ganancias pueden estar sujetas a impuestos
+ no_data: "-"
+ view_details: Ver detalles
+ holdings_count:
+ one: "%{count} activo"
+ other: "%{count} activos"
+ sells_count:
+ one: "%{count} venta"
+ other: "%{count} ventas"
+ holdings: Activos
+ sell_trades: Operaciones de venta
+ and_more: "+%{count} más"
investment_flows:
title: Flujos de inversión
description: Controla el dinero que entra y sale de tus cuentas de inversión
@@ -147,7 +164,7 @@ es:
steps: "Para importar en Google Sheets:\n1. Crea una nueva hoja de cálculo\n2. En la celda A1, introduce la fórmula que se muestra abajo\n3. Pulsa Enter"
security_warning: "Esta URL incluye tu clave API. ¡Mantenla segura!"
need_key: Para importar los datos en Google Sheets necesitas una clave API.
- step1: "Ve a ajustes → Clave API"
+ step1: "Ve a Ajustes → Claves API"
step2: "Crea una nueva clave API con permiso de lectura (\"read\")"
step3: Copia la clave API
step4: "Añádela a esta URL como: ?api_key=TU_CLAVE"
@@ -209,4 +226,4 @@ es:
category: Categoría
amount: Importe
percent: "%"
- more_categories: "+ %{count} más categorías"
+ more_categories: "+ %{count} más categorías"
\ No newline at end of file
diff --git a/config/locales/views/reports/pl.yml b/config/locales/views/reports/pl.yml
new file mode 100644
index 000000000..3158c8c17
--- /dev/null
+++ b/config/locales/views/reports/pl.yml
@@ -0,0 +1,236 @@
+---
+pl:
+ reports:
+ index:
+ title: Raporty
+ subtitle: Kompleksowy wgląd w kondycję Twoich finansów
+ export: Eksportuj CSV
+ print_report: Drukuj raport
+ drag_to_reorder: Przeciągnij, aby zmienić kolejność sekcji
+ toggle_section: Przełącz widoczność sekcji
+ periods:
+ monthly: Miesięcznie
+ quarterly: Kwartalnie
+ ytd: Od początku roku
+ last_6_months: Ostatnie 6 miesięcy
+ custom: Własny zakres
+ date_range:
+ from: Od
+ to: Do
+ showing_period: Wyświetlanie danych od %{start} do %{end}
+ invalid_date_range: Data końcowa nie może być wcześniejsza niż data początkowa. Daty zostały zamienione.
+ summary:
+ total_income: Łączne przychody
+ total_expenses: Łączne wydatki
+ net_savings: Saldo oszczędności
+ budget_performance: Realizacja budżetu
+ vs_previous: względem poprzedniego okresu
+ income_minus_expenses: Przychody minus wydatki
+ of_budget_used: wykorzystanego budżetu
+ no_budget_data: Brak danych budżetowych dla tego okresu
+ budget_performance:
+ title: Realizacja budżetu
+ spent: Wydano
+ budgeted: Zaplanowano
+ remaining: Pozostało
+ over_by: Przekroczono o
+ shared: współdzielony
+ suggested_daily: "Sugerowane %{amount} dziennie przez pozostałe %{days} dni"
+ no_budgets: Nie skonfigurowano kategorii budżetowych na ten miesiąc
+ status:
+ good: Zgodnie z planem
+ warning: Blisko limitu
+ over: Przekroczono budżet
+ trends:
+ title: Trendy i wnioski
+ monthly_breakdown: Podział miesięczny
+ month: Miesiąc
+ income: Przychody
+ expenses: Wydatki
+ net: Saldo
+ savings_rate: Stopa oszczędzania
+ current: bieżący
+ avg_monthly_income: Śr. miesięczny przychód
+ avg_monthly_expenses: Śr. miesięczne wydatki
+ avg_monthly_savings: Śr. miesięczne oszczędności
+ no_data: Brak danych trendu
+ spending_patterns: Wzorce wydatków
+ weekday_spending: Wydatki w dni robocze
+ weekend_spending: Wydatki w weekendy
+ total: Łącznie
+ avg_per_transaction: Śr. na transakcję
+ transactions: Transakcje
+ insight_title: Wniosek
+ insight_higher_weekend: W weekendy wydajesz o %{percent}% więcej na transakcję niż w dni robocze
+ insight_higher_weekday: W dni robocze wydajesz o %{percent}% więcej na transakcję niż w weekendy
+ insight_similar: Twoje wydatki na transakcję są podobne w dni robocze i weekendy
+ no_spending_data: Brak danych o wydatkach dla tego okresu
+ empty_state:
+ title: Brak danych
+ description: Zacznij śledzić swoje finanse, dodając transakcje lub łącząc konta, aby zobaczyć pełne raporty
+ add_transaction: Dodaj transakcję
+ add_account: Dodaj konto
+ transactions_breakdown:
+ title: Podział aktywności
+ no_transactions: Brak aktywności dla wybranego okresu i filtrów
+ filters:
+ title: Filtry
+ category: Kategoria
+ account: Konto
+ tag: Tag
+ amount_min: Min. kwota
+ amount_max: Maks. kwota
+ date_range: Zakres dat
+ all_categories: Wszystkie kategorie
+ all_accounts: Wszystkie konta
+ all_tags: Wszystkie tagi
+ apply: Zastosuj filtry
+ clear: Wyczyść filtry
+ sort:
+ label: Sortuj według
+ date_desc: Data (od najnowszych)
+ amount_desc: Kwota (malejąco)
+ amount_asc: Kwota (rosnąco)
+ export:
+ label: Eksport
+ csv: CSV
+ excel: Excel
+ pdf: PDF
+ google_sheets: Otwórz w Google Sheets
+ table:
+ category: Kategoria
+ amount: Kwota
+ type: Typ
+ expense: Wydatki
+ income: Przychody
+ uncategorized: Bez kategorii
+ entries:
+ one: "%{count} wpis"
+ few: "%{count} wpisy"
+ many: "%{count} wpisów"
+ other: "%{count} wpisu"
+ percentage: "% całości"
+ pagination:
+ showing:
+ one: Wyświetlono %{count} wpis
+ few: Wyświetlono %{count} wpisy
+ many: Wyświetlono %{count} wpisów
+ other: Wyświetlono %{count} wpisu
+ previous: Poprzednia
+ next: Następna
+ net_worth:
+ title: Majątek netto
+ current_net_worth: Bieżący majątek netto
+ period_change: Zmiana w okresie
+ assets_vs_liabilities: Aktywa vs zobowiązania
+ total_assets: Aktywa
+ total_liabilities: Zobowiązania
+ no_assets: Brak aktywów
+ no_liabilities: Brak zobowiązań
+ investment_performance:
+ title: Wyniki inwestycji
+ portfolio_value: Wartość portfela
+ total_return: Łączny zwrot
+ contributions: Wpłaty w okresie
+ withdrawals: Wypłaty w okresie
+ top_holdings: Największe pozycje
+ holding: Pozycja
+ weight: Udział
+ value: Wartość
+ return: Zwrot
+ accounts: Konta inwestycyjne
+ gains_by_tax_treatment: Zyski według rodzaju opodatkowania
+ unrealized_gains: Niezrealizowane zyski
+ realized_gains: Zrealizowane zyski
+ total_gains: Łączne zyski
+ taxable_realized_note: Te zyski mogą podlegać opodatkowaniu
+ no_data: "-"
+ view_details: Zobacz szczegóły
+ holdings_count:
+ one: "%{count} pozycja"
+ few: "%{count} pozycje"
+ many: "%{count} pozycji"
+ other: "%{count} pozycji"
+ sells_count:
+ one: "%{count} sprzedaż"
+ few: "%{count} sprzedaże"
+ many: "%{count} sprzedaży"
+ other: "%{count} sprzedaży"
+ holdings: Pozycje
+ sell_trades: Transakcje sprzedaży
+ and_more: "+%{count} więcej"
+ investment_flows:
+ title: Przepływy inwestycyjne
+ description: Śledź środki wpływające i wypływające z kont inwestycyjnych
+ contributions: Wpłaty
+ withdrawals: Wypłaty
+ net_flow: Przepływ netto
+ google_sheets_instructions:
+ title_with_key: Skopiuj URL do Google Sheets
+ title_no_key: Wymagany klucz API
+ ready: Twój URL CSV (z kluczem API) jest gotowy.
+ steps: |-
+ Aby zaimportować do Google Sheets:
+ 1. Utwórz nowy arkusz Google
+ 2. W komórce A1 wpisz formułę pokazaną poniżej
+ 3. Naciśnij Enter
+ security_warning: Ten URL zawiera Twój klucz API. Przechowuj go bezpiecznie.
+ need_key: Aby importować dane do Google Sheets, potrzebujesz klucza API.
+ step1: Przejdź do Ustawienia → Klucze API
+ step2: Utwórz nowy klucz API z uprawnieniem "read"
+ step3: Skopiuj klucz API
+ step4: 'Dodaj go do tego URL-a tak: ?api_key=YOUR_KEY'
+ example: Przykład
+ then_use: Następnie użyj pełnego URL-a z =IMPORTDATA() w Google Sheets.
+ open_sheets: Otwórz Google Sheets
+ go_to_api_keys: Przejdź do kluczy API
+ close: Rozumiem
+ print:
+ document_title: Raport finansowy
+ title: Raport finansowy
+ generated_on: Wygenerowano %{date}
+ summary:
+ title: Podsumowanie
+ income: Przychody
+ expenses: Wydatki
+ net_savings: Saldo oszczędności
+ budget: Budżet
+ vs_prior: "%{percent}% względem poprzedniego"
+ of_income: "%{percent}% przychodu"
+ used: wykorzystano
+ net_worth:
+ title: Majątek netto
+ current_balance: Bieżące saldo
+ this_period: w tym okresie
+ assets: Aktywa
+ liabilities: Zobowiązania
+ no_liabilities: Brak zobowiązań
+ trends:
+ title: Trendy miesięczne
+ month: Miesiąc
+ income: Przychody
+ expenses: Wydatki
+ net: Saldo
+ savings_rate: Stopa oszczędzania
+ average: Średnia
+ current_month_note: "* Bieżący miesiąc (dane częściowe)"
+ investments:
+ title: Inwestycje
+ portfolio_value: Wartość portfela
+ total_return: Łączny zwrot
+ contributions: Wpłaty
+ withdrawals: Wypłaty
+ this_period: w tym okresie
+ top_holdings: Największe pozycje
+ holding: Pozycja
+ weight: Udział
+ value: Wartość
+ return: Zwrot
+ spending:
+ title: Wydatki według kategorii
+ income: Przychody
+ expenses: Wydatki
+ category: Kategoria
+ amount: Kwota
+ percent: "%"
+ more_categories: "+ %{count} więcej kategorii"
diff --git a/config/locales/views/reports/pt-BR.yml b/config/locales/views/reports/pt-BR.yml
index 915199949..d48dfe0f5 100644
--- a/config/locales/views/reports/pt-BR.yml
+++ b/config/locales/views/reports/pt-BR.yml
@@ -5,6 +5,7 @@ pt-BR:
title: Relatórios
subtitle: Insights completos sobre sua saúde financeira
export: Exportar CSV
+ print_report: Imprimir Relatório
drag_to_reorder: "Arraste para reordenar a seção"
toggle_section: "Alternar visibilidade da seção"
periods:
@@ -33,6 +34,7 @@ pt-BR:
budgeted: Orçado
remaining: Restante
over_by: Excedido em
+ shared: compartilhado
suggested_daily: "%{amount} sugerido por dia para os %{days} dias restantes"
no_budgets: Nenhuma categoria de orçamento configurada para este mês
status:
@@ -102,12 +104,59 @@ pt-BR:
expense: Despesas
income: Receitas
uncategorized: Sem Categoria
- transactions: transações
+ entries:
+ one: "%{count} entrada"
+ other: "%{count} entradas"
percentage: "% do Total"
pagination:
- showing: Exibindo %{count} transações
+ showing:
+ one: Exibindo %{count} entrada
+ other: Exibindo %{count} entradas
previous: Anterior
next: Próximo
+ net_worth:
+ title: Patrimônio líquido
+ current_net_worth: Patrimônio líquido atual
+ period_change: Alteração de período
+ assets_vs_liabilities: Ativos versus Passivos
+ total_assets: Ativos
+ total_liabilities: Passivos
+ no_assets: Sem ativos
+ no_liabilities: Sem passivos
+ investment_performance:
+ title: Desempenho de Investimentos
+ portfolio_value: Valor do Portfólio
+ total_return: Retorno Total
+ contributions: Contribuições do Período
+ withdrawals: Saques do Período
+ top_holdings: Principais Aplicações
+ holding: Holding
+ weight: Peso
+ value: Valor
+ return: Retorno
+ accounts: Contas de Investimento
+ gains_by_tax_treatment: Ganhos por tratamento tributário
+ unrealized_gains: Ganhos Não Realizados
+ realized_gains: Ganhos Realizados
+ total_gains: Ganhos Totais
+ taxable_realized_note: Esses ganhos podem estar sujeitos a impostos
+ no_data: "-"
+ view_details: Ver detalhes
+ holdings_count:
+ one: "%{count} holding"
+ other: "%{count} holdings"
+ sells_count:
+ one: "%{count} venda"
+ other: "%{count} vendas"
+ holdings: Holdings
+ sell_trades: Venda de Negócios
+ and_more: "+%{count} mais"
+ investment_flows:
+ title: Fluxos de investimento
+ description: Acompanhe o fluxo de dinheiro que entra e sai de suas contas de investimento.
+ contributions: Contribuições
+ withdrawals: Saques
+ net_flow: Fluxo Líquido
google_sheets_instructions:
title_with_key: "✅ Copiar URL para Google Sheets"
title_no_key: "⚠️ Chave de API Necessária"
@@ -124,3 +173,57 @@ pt-BR:
open_sheets: Abrir Google Sheets
go_to_api_keys: Ir para Chaves de API
close: Entendi
+ print:
+ document_title: Relatório Financeiro
+ title: Relatório Financeiro
+ generated_on: "Gerado em %{date}"
+ # Summary section
+ summary:
+ title: Resumo
+ income: Receita
+ expenses: Despesas
+ net_savings: Poupança Líquida
+ budget: Orçamento
+ vs_prior: "%{percent}% vs anterior"
+ of_income: "%{percent}% da Receita"
+ used: usado
+ # Net Worth section
+ net_worth:
+ title: Patrimônio Líquido
+ current_balance: Saldo Atual
+ this_period: neste período
+ assets: Ativos
+ liabilities: Passivos
+ no_liabilities: Sem passivos
+ # Monthly Trends section
+ trends:
+ title: Tendências Mensais
+ month: Mês
+ income: Receita
+ expenses: Despesas
+ net: Líquido
+ savings_rate: Taxa de Poupança
+ average: Média
+ current_month_note: "* Mês atual (dados parciais)"
+ # Investments section
+ investments:
+ title: Investimentos
+ portfolio_value: Valor do Portfólio
+ total_return: Retorno Total
+ contributions: Contribuições
+ withdrawals: Saques
+ this_period: este período
+ top_holdings: Principais Aplicações
+ holding: Holding
+ weight: Peso
+ value: Valor
+ return: Retorno
+ # Spending by Category section
+ spending:
+ title: Gastos por Categoria
+ income: Receita
+ expenses: Despesas
+ category: Categoria
+ amount: Quantia
+ percent: "%"
+ more_categories: "+ %{count} mais categorias"
diff --git a/config/locales/views/rules/de.yml b/config/locales/views/rules/de.yml
index cc57171b0..c6da99379 100644
--- a/config/locales/views/rules/de.yml
+++ b/config/locales/views/rules/de.yml
@@ -3,6 +3,21 @@ de:
rules:
no_action: Keine Aktion
no_condition: Keine Bedingung
+ actions:
+ value_placeholder: Wert eingeben
+ apply_all:
+ button: Alle anwenden
+ confirm_title: Alle Regeln anwenden
+ confirm_message: Du bist dabei, %{count} Regeln anzuwenden, die %{transactions} eindeutige Transaktionen betreffen. Bitte bestätige, wenn du fortfahren möchtest.
+ confirm_button: Bestätigen und alle anwenden
+ success: Alle Regeln wurden zur Ausführung in die Warteschlange gestellt
+ ai_cost_title: KI-Kostenschätzung
+ ai_cost_message: Dies verwendet KI, um bis zu %{transactions} Transaktionen zu kategorisieren.
+ estimated_cost: "Geschätzte Kosten: ca. %{cost} $"
+ cost_unavailable_model: Kostenschätzung für Modell „%{model}“ nicht verfügbar.
+ cost_unavailable_no_provider: Kostenschätzung nicht verfügbar (kein LLM-Anbieter konfiguriert).
+ cost_warning: Es können Kosten entstehen. Bitte informiere dich beim Modellanbieter über die aktuellen Preise.
+ view_usage: Nutzungshistorie anzeigen
recent_runs:
title: Letzte Ausführungen
description: Zeige die Ausführungsgeschichte deiner Regeln einschließlich Erfolgs-/Fehlerstatus und Transaktionsanzahlen.
@@ -23,3 +38,15 @@ de:
pending: Ausstehend
success: Erfolgreich
failed: Fehlgeschlagen
+ clear_ai_cache:
+ button: KI-Cache zurücksetzen
+ confirm_title: KI-Cache zurücksetzen?
+ confirm_body: Bist du sicher, dass du den KI-Cache zurücksetzen möchtest? Dadurch können KI-Regeln alle Transaktionen erneut verarbeiten. Dies kann zusätzliche API-Kosten verursachen.
+ confirm_button: Cache zurücksetzen
+ success: Der KI-Cache wird geleert. Das kann einen Moment dauern.
+ condition_filters:
+ transaction_type:
+ income: Einnahme
+ expense: Ausgabe
+ transfer: Überweisung
+ equal_to: Gleich
diff --git a/config/locales/views/rules/es.yml b/config/locales/views/rules/es.yml
index 3c494e8fb..2390c7676 100644
--- a/config/locales/views/rules/es.yml
+++ b/config/locales/views/rules/es.yml
@@ -3,6 +3,21 @@ es:
rules:
no_action: Sin acción
no_condition: Sin condición
+ actions:
+ value_placeholder: Introduce un valor
+ apply_all:
+ button: Aplicar todas
+ confirm_title: Aplicar todas las reglas
+ confirm_message: Estás a punto de aplicar %{count} reglas que afectan a %{transactions} transacciones únicas. Por favor, confirma si deseas continuar.
+ confirm_button: Confirmar y aplicar todas
+ success: Todas las reglas han sido puestas en cola para su ejecución
+ ai_cost_title: Estimación de costes de IA
+ ai_cost_message: Esto utilizará IA para categorizar hasta %{transactions} transacciones.
+ estimated_cost: "Coste estimado: ~$%{cost}"
+ cost_unavailable_model: Estimación de costes no disponible para el modelo "%{model}".
+ cost_unavailable_no_provider: Estimación de costes no disponible (no hay proveedor de LLM configurado).
+ cost_warning: Puedes incurrir en costes, consulta con el proveedor del modelo los precios más actualizados.
+ view_usage: Ver historial de uso
recent_runs:
title: Ejecuciones Recientes
description: Ver el historial de ejecución de tus reglas incluyendo el estado de éxito/fallo y los conteos de transacciones.
@@ -23,3 +38,15 @@ es:
pending: Pendiente
success: Éxito
failed: Fallido
+ clear_ai_cache:
+ button: Restablecer caché de IA
+ confirm_title: ¿Restablecer caché de IA?
+ confirm_body: ¿Estás seguro de que deseas restablecer la caché de IA? Esto permitirá que las reglas de IA vuelvan a procesar todas las transacciones. Esto puede incurrir en costes adicionales de API.
+ confirm_button: Restablecer caché
+ success: Se está limpiando la caché de IA. Esto puede tardar unos momentos.
+ condition_filters:
+ transaction_type:
+ income: Ingreso
+ expense: Gasto
+ transfer: Transferencia
+ equal_to: Igual a
diff --git a/config/locales/views/rules/pl.yml b/config/locales/views/rules/pl.yml
new file mode 100644
index 000000000..a891c5627
--- /dev/null
+++ b/config/locales/views/rules/pl.yml
@@ -0,0 +1,52 @@
+---
+pl:
+ rules:
+ no_action: Brak akcji
+ no_condition: Brak warunku
+ actions:
+ value_placeholder: Wprowadź wartość
+ apply_all:
+ button: Zastosuj wszystkie
+ confirm_title: Zastosować wszystkie reguły
+ confirm_message: Za chwilę zastosujesz %{count} reguł wpływających na %{transactions} unikalnych transakcji. Potwierdź, jeśli chcesz kontynuować.
+ confirm_button: Potwierdź i zastosuj wszystkie
+ success: Wszystkie reguły zostały dodane do kolejki wykonania
+ ai_cost_title: Szacowanie kosztu AI
+ ai_cost_message: To użyje AI do kategoryzacji maksymalnie %{transactions} transakcji.
+ estimated_cost: 'Szacowany koszt: ~$%{cost}'
+ cost_unavailable_model: Szacowanie kosztu niedostępne dla modelu "%{model}".
+ cost_unavailable_no_provider: Szacowanie kosztu niedostępne (brak skonfigurowanego dostawcy LLM).
+ cost_warning: Mogą zostać naliczone koszty, sprawdź u dostawcy modelu aktualny cennik.
+ view_usage: Zobacz historię użycia
+ recent_runs:
+ title: Ostatnie uruchomienia
+ description: Zobacz historię wykonania reguł, w tym status powodzenia/niepowodzenia oraz liczbę transakcji.
+ unnamed_rule: Reguła bez nazwy
+ columns:
+ date_time: Data/godzina
+ execution_type: Typ
+ status: Status
+ rule_name: Nazwa reguły
+ transactions_counts:
+ queued: W kolejce
+ processed: Przetworzone
+ modified: Zmodyfikowane
+ execution_types:
+ manual: Ręczne
+ scheduled: Zaplanowane
+ statuses:
+ pending: Oczekujące
+ success: Pomyślnie
+ failed: Błąd
+ clear_ai_cache:
+ button: Zresetuj cache AI
+ confirm_title: Zresetować cache AI?
+ confirm_body: Czy na pewno chcesz zresetować cache AI? Umożliwi to regułom AI ponowne przetworzenie wszystkich transakcji. Może to wygenerować dodatkowe koszty API.
+ confirm_button: Zresetuj cache
+ success: Trwa czyszczenie cache AI. To może potrwać chwilę.
+ condition_filters:
+ transaction_type:
+ income: Przychód
+ expense: Wydatek
+ transfer: Przelew
+ equal_to: Równe
diff --git a/config/locales/views/securities/es.yml b/config/locales/views/securities/es.yml
new file mode 100644
index 000000000..2ccd9cdd4
--- /dev/null
+++ b/config/locales/views/securities/es.yml
@@ -0,0 +1,6 @@
+---
+es:
+ securities:
+ combobox:
+ display: "%{symbol} - %{name} (%{exchange})"
+ exchange_label: "%{symbol} (%{exchange})"
\ No newline at end of file
diff --git a/config/locales/views/securities/pl.yml b/config/locales/views/securities/pl.yml
new file mode 100644
index 000000000..5a3d8655d
--- /dev/null
+++ b/config/locales/views/securities/pl.yml
@@ -0,0 +1,6 @@
+---
+pl:
+ securities:
+ combobox:
+ display: "%{symbol} - %{name} (%{exchange})"
+ exchange_label: "%{symbol} (%{exchange})"
diff --git a/config/locales/views/sessions/de.yml b/config/locales/views/sessions/de.yml
index 1d307a7de..f472825b0 100644
--- a/config/locales/views/sessions/de.yml
+++ b/config/locales/views/sessions/de.yml
@@ -3,12 +3,19 @@ de:
sessions:
create:
invalid_credentials: Ungültige E-Mail-Adresse oder falsches Passwort.
+ local_login_disabled: Anmeldung mit Passwort ist deaktiviert. Bitte nutze Single Sign-On.
destroy:
logout_successful: Du hast dich erfolgreich abgemeldet.
+ post_logout:
+ logout_successful: Du hast dich erfolgreich abgemeldet.
openid_connect:
+ account_linked: "Konto erfolgreich mit %{provider} verknüpft"
failed: Anmeldung über OpenID Connect fehlgeschlagen.
failure:
failed: Anmeldung fehlgeschlagen.
+ sso_provider_unavailable: "Der SSO-Anbieter ist derzeit nicht verfügbar. Bitte versuche es später erneut oder wende dich an einen Administrator."
+ sso_invalid_response: "Vom SSO-Anbieter wurde eine ungültige Antwort erhalten. Bitte versuche es erneut."
+ sso_failed: "Single Sign-On-Anmeldung fehlgeschlagen. Bitte versuche es erneut."
new:
email: E-Mail-Adresse
email_placeholder: du@beispiel.de
@@ -20,3 +27,7 @@ de:
openid_connect: Mit OpenID Connect anmelden
oidc: Mit OpenID Connect anmelden
google_auth_connect: Mit Google anmelden
+ local_login_admin_only: Lokale Anmeldung ist auf Administratoren beschränkt.
+ no_auth_methods_enabled: Derzeit sind keine Anmeldemethoden aktiviert. Bitte wende dich an einen Administrator.
+ demo_banner_title: "Demo-Modus aktiv"
+ demo_banner_message: "Dies ist eine Demo-Umgebung. Anmeldedaten wurden zur Vereinfachung vorausgefüllt. Bitte gib keine echten oder sensiblen Daten ein."
diff --git a/config/locales/views/sessions/es.yml b/config/locales/views/sessions/es.yml
index d7dbbee6b..1e6cc34d2 100644
--- a/config/locales/views/sessions/es.yml
+++ b/config/locales/views/sessions/es.yml
@@ -2,24 +2,32 @@
es:
sessions:
create:
- invalid_credentials: Correo electrónico o contraseña inválidos.
- local_login_disabled: El inicio de sesión con contraseña local está deshabilitado. Utiliza el inicio de sesión único (SSO).
+ invalid_credentials: Correo electrónico o contraseña no válidos.
+ local_login_disabled: El inicio de sesión con contraseña local está desactivado. Por favor, utiliza el inicio de sesión único (SSO).
destroy:
- logout_successful: Has cerrado sesión con éxito.
+ logout_successful: Has cerrado sesión correctamente.
+ post_logout:
+ logout_successful: Has cerrado sesión correctamente.
openid_connect:
+ account_linked: "Cuenta vinculada correctamente a %{provider}"
failed: No se pudo autenticar a través de OpenID Connect.
failure:
failed: No se pudo autenticar.
+ sso_provider_unavailable: "El proveedor de SSO no está disponible en este momento. Por favor, inténtalo de nuevo más tarde o contacta con un administrador."
+ sso_invalid_response: "Se ha recibido una respuesta no válida del proveedor de SSO. Por favor, inténtalo de nuevo."
+ sso_failed: "Error en la autenticación de inicio de sesión único (SSO). Por favor, inténtalo de nuevo."
new:
email: Dirección de correo electrónico
email_placeholder: tu@ejemplo.com
- forgot_password: ¿Olvidaste tu contraseña?
+ forgot_password: ¿Has olvidado tu contraseña?
password: Contraseña
submit: Iniciar sesión
- title: Inicia sesión en tu cuenta
+ title: Sure
password_placeholder: Introduce tu contraseña
- openid_connect: Inicia sesión con OpenID Connect
- oidc: Inicia sesión con OpenID Connect
- google_auth_connect: Inicia sesión con Google
+ openid_connect: Iniciar sesión con OpenID Connect
+ oidc: Iniciar sesión con OpenID Connect
+ google_auth_connect: Iniciar sesión con Google
local_login_admin_only: El inicio de sesión local está restringido a administradores.
- no_auth_methods_enabled: No hay métodos de autenticación habilitados actualmente. Ponte en contacto con un administrador.
+ no_auth_methods_enabled: No hay métodos de autenticación habilitados actualmente. Por favor, contacta con un administrador.
+ demo_banner_title: "Modo de demostración activo"
+ demo_banner_message: "Este es un entorno de demostración. Las credenciales de acceso se han rellenado automáticamente para tu comodidad. Por favor, no introduzcas información real o sensible."
\ No newline at end of file
diff --git a/config/locales/views/sessions/pl.yml b/config/locales/views/sessions/pl.yml
new file mode 100644
index 000000000..7fac5305f
--- /dev/null
+++ b/config/locales/views/sessions/pl.yml
@@ -0,0 +1,33 @@
+---
+pl:
+ sessions:
+ create:
+ invalid_credentials: Nieprawidłowy adres e-mail lub hasło.
+ local_login_disabled: Logowanie lokalnym hasłem jest wyłączone. Użyj logowania jednokrotnego (SSO).
+ destroy:
+ logout_successful: Zostałeś(aś) pomyślnie wylogowany(a).
+ post_logout:
+ logout_successful: Zostałeś(aś) pomyślnie wylogowany(a).
+ openid_connect:
+ account_linked: "Konto zostało pomyślnie połączone z %{provider}"
+ failed: Nie udało się uwierzytelnić przez OpenID Connect.
+ failure:
+ failed: Nie udało się uwierzytelnić.
+ sso_provider_unavailable: "Dostawca SSO jest obecnie niedostępny. Spróbuj ponownie później lub skontaktuj się z administratorem."
+ sso_invalid_response: "Otrzymano nieprawidłową odpowiedź od dostawcy SSO. Spróbuj ponownie."
+ sso_failed: "Uwierzytelnianie SSO nie powiodło się. Spróbuj ponownie."
+ new:
+ email: Adres e-mail
+ email_placeholder: ty@example.com
+ forgot_password: Nie pamiętasz hasła?
+ password: Hasło
+ submit: Zaloguj się
+ title: Sure
+ password_placeholder: Wpisz hasło
+ openid_connect: Zaloguj się przez OpenID Connect
+ oidc: Zaloguj się przez OpenID Connect
+ google_auth_connect: Zaloguj się przez Google
+ local_login_admin_only: Logowanie lokalne jest ograniczone do administratorów.
+ no_auth_methods_enabled: Obecnie nie włączono żadnej metody uwierzytelniania. Skontaktuj się z administratorem.
+ demo_banner_title: "Aktywny tryb demo"
+ demo_banner_message: "To jest środowisko demonstracyjne. Dane logowania zostały wstępnie uzupełnione dla wygody. Nie wprowadzaj prawdziwych ani wrażliwych informacji."
diff --git a/config/locales/views/settings/api_keys/pl.yml b/config/locales/views/settings/api_keys/pl.yml
new file mode 100644
index 000000000..bef95529c
--- /dev/null
+++ b/config/locales/views/settings/api_keys/pl.yml
@@ -0,0 +1,76 @@
+---
+pl:
+ settings:
+ api_keys_controller:
+ success: Twój klucz API został pomyślnie utworzony
+ revoked_successfully: Klucz API został pomyślnie unieważniony
+ revoke_failed: Nie udało się unieważnić klucza API
+ scope_descriptions:
+ read_accounts: Podgląd kont
+ read_transactions: Podgląd transakcji
+ read_balances: Podgląd sald
+ write_transactions: Tworzenie transakcji
+ api_keys:
+ show:
+ title: Zarządzanie kluczami API
+ no_api_key:
+ title: Klucz API
+ heading: Uzyskaj programowy dostęp do danych konta
+ description: Uzyskaj programowy dostęp do danych Sure za pomocą bezpiecznego klucza API.
+ what_you_can_do: 'Co możesz zrobić przez API:'
+ feature_1: Uzyskiwać programowy dostęp do danych konta
+ feature_2: Budować własne integracje i aplikacje
+ feature_3: Automatyzować pobieranie i analizę danych
+ security_note_title: Bezpieczeństwo przede wszystkim
+ security_note: Twój klucz API będzie miał ograniczone uprawnienia zależne od wybranych zakresów. Jednocześnie możesz mieć tylko jeden aktywny klucz API.
+ create_api_key: Utwórz klucz API
+ current_api_key:
+ title: Twój klucz API
+ description: Twój aktywny klucz API jest gotowy do użycia. Przechowuj go bezpiecznie i nigdy nie udostępniaj publicznie.
+ active: Aktywny
+ key_name: Nazwa
+ created_at: Utworzono
+ last_used: Ostatnie użycie
+ expires: Wygasa
+ ago: temu
+ never_used: Nigdy nie użyto
+ never_expires: Nigdy nie wygasa
+ permissions: Uprawnienia
+ usage_instructions_title: Jak używać klucza API
+ usage_instructions: 'Dołącz klucz API w nagłówku X-Api-Key podczas wysyłania żądań do API Maybe:'
+ regenerate_key: Utwórz nowy klucz
+ revoke_key: Unieważnij klucz
+ revoke_confirmation: Czy na pewno chcesz unieważnić ten klucz API? Tej akcji nie można cofnąć, a wszystkie aplikacje używające tego klucza zostaną natychmiast wyłączone.
+ new:
+ title: Utwórz klucz API
+ create_new_key: Utwórz nowy klucz API
+ description: Skonfiguruj nowy klucz API, nadając mu opisową nazwę i odpowiednie uprawnienia.
+ name_label: Nazwa klucza API
+ name_placeholder: np. Aplikacja produkcyjna, Dashboard analityczny
+ name_help: Wybierz opisową nazwę, aby łatwo rozpoznać przeznaczenie tego klucza.
+ permissions_label: Uprawnienia
+ permissions_help: Wybierz uprawnienia potrzebne dla klucza API. Zawsze możesz utworzyć nowy klucz z innymi uprawnieniami.
+ scope_details:
+ read_accounts: Podgląd informacji o kontach, sald i danych na poziomie konta
+ read_transactions: Podgląd danych transakcyjnych, kategorii i szczegółów transakcji
+ read_balances: Podgląd historycznych danych salda i trendów wartości kont
+ write_transactions: Tworzenie i aktualizacja rekordów transakcji (wkrótce)
+ security_warning_title: Ważna informacja o bezpieczeństwie
+ security_warning: Klucz API zostanie wyświetlony tylko raz po utworzeniu. Przechowuj go bezpiecznie i nigdy nie udostępniaj publicznie. Jeśli go zgubisz, trzeba będzie utworzyć nowy.
+ create_key: Utwórz klucz API
+ cancel: Anuluj
+ created:
+ title: Klucz API utworzony
+ success_title: Klucz API został pomyślnie utworzony
+ success_description: Twój nowy klucz API jest gotowy do użycia. Skopiuj go teraz, ponieważ później nie będzie już widoczny.
+ your_api_key: Twój klucz API
+ key_name: Nazwa
+ permissions: Uprawnienia
+ critical_warning_title: "⚠️ Ważne: zapisz klucz API teraz"
+ critical_warning_1: To jedyny moment, kiedy zobaczysz klucz API w postaci jawnej.
+ critical_warning_2: Skopiuj go i przechowuj bezpiecznie w menedżerze haseł lub aplikacji.
+ critical_warning_3: Jeśli zgubisz ten klucz, trzeba będzie utworzyć nowy.
+ usage_instructions_title: Szybki start
+ usage_instructions: 'Użyj klucza API, dodając go w nagłówku X-Api-Key:'
+ copy_key: Skopiuj klucz API
+ continue: Przejdź do ustawień klucza API
diff --git a/config/locales/views/settings/de.yml b/config/locales/views/settings/de.yml
index 708145ff9..e8a64d97e 100644
--- a/config/locales/views/settings/de.yml
+++ b/config/locales/views/settings/de.yml
@@ -3,6 +3,7 @@ de:
settings:
payments:
renewal: "Ihr Beitrag wird fortgesetzt am %{date}."
+ cancellation: "Ihr Beitrag endet am %{date}."
settings:
ai_prompts:
show:
@@ -42,6 +43,9 @@ de:
theme_system: System
theme_title: Design
timezone: Zeitzone
+ month_start_day: Budgetmonat beginnt am
+ month_start_day_hint: Lege fest, wann dein Budgetmonat beginnt (z. B. Gehaltstag)
+ month_start_day_warning: Deine Budgets und MTD-Berechnungen verwenden diesen benutzerdefinierten Starttag anstelle des 1. jedes Monats.
profiles:
destroy:
cannot_remove_self: Du kannst dich nicht selbst aus dem Konto entfernen.
@@ -73,6 +77,9 @@ de:
reset_account_with_sample_data_warning: Löscht alle vorhandenen Daten und lädt anschließend neue Beispieldaten, um eine vorbefüllte Umgebung zu erkunden.
email: E-Mail
first_name: Vorname
+ group_form_input_placeholder: Gruppennamen eingeben
+ group_form_label: Gruppenname
+ group_title: Gruppenmitglieder
household_form_input_placeholder: Haushaltsnamen eingeben
household_form_label: Haushaltsname
household_subtitle: Lade Familienmitglieder, Partner oder andere Personen ein. Eingeladene können sich in deinen Haushalt einloggen und auf gemeinsame Konten zugreifen.
@@ -90,6 +97,23 @@ de:
securities:
show:
page_title: Sicherheit
+ mfa_title: Zwei-Faktor-Authentifizierung
+ mfa_description: Erhöhe die Sicherheit deines Kontos, indem du bei der Anmeldung einen Code von deiner Authenticator-App verlangst
+ enable_mfa: 2FA aktivieren
+ disable_mfa: 2FA deaktivieren
+ disable_mfa_confirm: Bist du sicher, dass du die Zwei-Faktor-Authentifizierung deaktivieren möchtest?
+ sso_title: Verbundene Konten
+ sso_subtitle: Verwalte deine Single Sign-On-Kontoverbindungen
+ sso_disconnect: Trennen
+ sso_last_used: Zuletzt verwendet
+ sso_never: Nie
+ sso_no_email: Keine E-Mail
+ sso_no_identities: Keine SSO-Konten verbunden
+ sso_connect_hint: Melde dich ab und mit einem SSO-Anbieter an, um ein Konto zu verbinden.
+ sso_confirm_title: Konto trennen?
+ sso_confirm_body: Bist du sicher, dass du dein %{provider}-Konto trennen möchtest? Du kannst es später erneut verbinden, indem du dich erneut mit diesem Anbieter anmeldest.
+ sso_confirm_button: Trennen
+ sso_warning_message: Dies ist deine einzige Anmeldemethode. Du solltest vor dem Trennen ein Passwort in den Sicherheitseinstellungen festlegen, sonst könntest du dich aus deinem Konto ausschließen.
settings_nav:
accounts_label: Konten
advanced_section_title: Erweitert
@@ -124,3 +148,27 @@ de:
choose: Foto hochladen
choose_label: (optional)
change: Foto ändern
+ providers:
+ show:
+ coinbase_title: Coinbase
+ encryption_error:
+ title: Verschlüsselungskonfiguration erforderlich
+ message: Die Active-Record-Verschlüsselungsschlüssel sind nicht konfiguriert. Bitte stelle die Verschlüsselungszugangsdaten (active_record_encryption.primary_key, active_record_encryption.deterministic_key und active_record_encryption.key_derivation_salt) in deinen Rails-Zugangsdaten oder Umgebungsvariablen ein, bevor du Sync-Anbieter verwendest.
+ coinbase_panel:
+ setup_instructions: "So verbindest du Coinbase:"
+ step1_html: Gehe zu Coinbase API-Einstellungen
+ step2: Erstelle einen neuen API-Schlüssel mit Leseberechtigung (Konten anzeigen, Transaktionen anzeigen)
+ step3: Kopiere deinen API-Schlüssel und dein API-Geheimnis und füge sie unten ein
+ api_key_label: API-Schlüssel
+ api_key_placeholder: Gib deinen Coinbase-API-Schlüssel ein
+ api_secret_label: API-Geheimnis
+ api_secret_placeholder: Gib dein Coinbase-API-Geheimnis ein
+ connect_button: Coinbase verbinden
+ syncing: Wird synchronisiert…
+ sync: Synchronisieren
+ disconnect_confirm: Bist du sicher, dass du diese Coinbase-Verbindung trennen möchtest? Deine synchronisierten Konten werden zu manuellen Konten.
+ status_connected: Coinbase ist verbunden und synchronisiert deine Krypto-Bestände.
+ status_not_connected: Nicht verbunden. Gib deine API-Zugangsdaten oben ein, um zu starten.
+ enable_banking_panel:
+ callback_url_instruction: "Für die Callback-URL, verwende %{callback_url}."
+ connection_error: Verbindungsfehler
diff --git a/config/locales/views/settings/en.yml b/config/locales/views/settings/en.yml
index e0030605a..02eda5228 100644
--- a/config/locales/views/settings/en.yml
+++ b/config/locales/views/settings/en.yml
@@ -26,6 +26,22 @@ en:
page_title: Payments
subscription_subtitle: Update your credit card details
subscription_title: Manage contributions
+ appearances:
+ show:
+ page_title: Appearance
+ theme_title: Theme
+ theme_subtitle: Choose a preferred theme for the app
+ theme_dark: Dark
+ theme_light: Light
+ theme_system: System
+ transactions_title: Transactions
+ transactions_subtitle: Customize how transactions are displayed
+ dashboard_title: Dashboard
+ dashboard_subtitle: Customize how the dashboard is displayed
+ dashboard_two_column_title: Two-column layout
+ dashboard_two_column_description: Display dashboard widgets in two columns on large screens. When off, widgets stack in a single column.
+ split_grouped_title: Group split transactions
+ split_grouped_description: Show split transactions grouped under their parent in the transaction list. When off, split children appear as individual rows.
preferences:
show:
country: Country
@@ -38,15 +54,15 @@ en:
language: Language
language_auto: Browser language
page_title: Preferences
- theme_dark: Dark
- theme_light: Light
- theme_subtitle: Choose a preferred theme for the app
- theme_system: System
- theme_title: Theme
timezone: Timezone
month_start_day: Budget month starts on
month_start_day_hint: Set when your budget month starts (e.g., payday)
month_start_day_warning: Your budgets and MTD calculations will use this custom start day instead of the 1st of each month.
+ sharing_title: "%{moniker} Sharing"
+ sharing_subtitle: "Control how accounts are shared in your %{moniker}"
+ sharing_default_label: Default sharing for new accounts
+ sharing_shared: Share with all members
+ sharing_private: Keep private by default
profiles:
destroy:
cannot_remove_self: You cannot remove yourself from the account.
@@ -142,6 +158,7 @@ en:
transactions_section_title: Transactions
whats_new_label: What's new
api_keys_label: API Key
+ appearance_label: Appearance
bank_sync_label: Bank Sync
settings_nav_link_large:
next: Next
@@ -172,5 +189,25 @@ en:
disconnect_confirm: Are you sure you want to disconnect this Coinbase connection? Your synced accounts will become manual accounts.
status_connected: Coinbase is connected and syncing your crypto holdings.
status_not_connected: Not connected. Enter your API credentials above to get started.
+ binance_panel:
+ setup_instructions: "To connect Binance, create a read-only API key:"
+ step1_html: 'Go to Binance API Management'
+ step2: "Create a new API key with Enable Reading permission only"
+ step3: "Paste your API Key and Secret below"
+ no_withdraw_warning: "Warning: do NOT enable withdrawal permissions"
+ ip_hint_title: "IP Whitelisting Required"
+ ip_hint_body: "Add the app server's egress IP to the Binance API Key whitelist:"
+ ip_hint_contact_admin: "Contact your administrator to obtain the app server's egress IP address."
+ api_key_label: API Key
+ api_key_placeholder: Paste your Binance API Key
+ api_secret_label: API Secret
+ api_secret_placeholder: Paste your Binance API Secret
+ connect_button: Connect Binance
+ syncing: Syncing...
+ sync: Sync
+ disconnect_confirm: "Are you sure you want to disconnect Binance?"
+ status_connected: Binance connected
+ status_not_connected: Binance not connected
enable_banking_panel:
+ callback_url_instruction: "For the callback URL, use %{callback_url}."
connection_error: Connection Error
diff --git a/config/locales/views/settings/es.yml b/config/locales/views/settings/es.yml
index 1867ac49e..b9346a547 100644
--- a/config/locales/views/settings/es.yml
+++ b/config/locales/views/settings/es.yml
@@ -4,6 +4,7 @@ es:
settings:
payments:
renewal: "Tu contribución continúa el %{date}."
+ cancellation: "Tu contribución finaliza el %{date}."
settings:
ai_prompts:
show:
@@ -22,9 +23,9 @@ es:
subtitle: La IA identifica y enriquece los datos de transacciones con información de comerciantes
payments:
show:
- page_title: Pago
- subscription_subtitle: Actualiza tu suscripción y detalles de pago
- subscription_title: Gestionar suscripción
+ page_title: Pagos
+ subscription_subtitle: Actualiza los detalles de tu tarjeta de crédito
+ subscription_title: Gestionar contribuciones
preferences:
show:
country: País
@@ -43,6 +44,9 @@ es:
theme_system: Sistema
theme_title: Tema
timezone: Zona horaria
+ month_start_day: El mes de presupuesto comienza el
+ month_start_day_hint: Establece cuándo empieza tu mes financiero (ej. el día de cobro)
+ month_start_day_warning: Tus presupuestos y cálculos del mes en curso utilizarán este día personalizado en lugar del día 1 de cada mes.
profiles:
destroy:
cannot_remove_self: No puedes eliminarte a ti mismo de la cuenta.
@@ -74,9 +78,12 @@ es:
reset_account_with_sample_data_warning: Elimina todos tus datos existentes y luego carga nuevos datos de ejemplo para que puedas explorar con un entorno prellenado.
email: Correo electrónico
first_name: Nombre
+ group_form_input_placeholder: Introduce el nombre del grupo
+ group_form_label: Nombre del grupo
+ group_title: Miembros del Grupo
household_form_input_placeholder: Introduce el nombre del grupo familiar
household_form_label: Nombre del grupo familiar
- household_subtitle: Invita a miembros de la familia, socios y otras personas. Los invitados pueden entrar en la cuenta y acceder a las cuentas compartidas.
+ household_subtitle: Los invitados pueden entrar en tu cuenta de %{moniker} y acceder a los recursos compartidos.
household_title: Grupo Familiar
invitation_link: Enlace de invitación
invite_member: Añadir miembro
@@ -91,6 +98,23 @@ es:
securities:
show:
page_title: Seguridad
+ mfa_title: Autenticación de Dos Factores (2FA)
+ mfa_description: Añade una capa extra de seguridad a tu cuenta requiriendo un código de tu aplicación de autenticación al iniciar sesión.
+ enable_mfa: Activar 2FA
+ disable_mfa: Desactivar 2FA
+ disable_mfa_confirm: ¿Estás seguro de que deseas desactivar la autenticación de dos factores?
+ sso_title: Cuentas Conectadas
+ sso_subtitle: Gestiona tus conexiones de inicio de sesión único (SSO)
+ sso_disconnect: Desconectar
+ sso_last_used: Último uso
+ sso_never: Nunca
+ sso_no_email: Sin correo
+ sso_no_identities: No hay cuentas de SSO conectadas
+ sso_connect_hint: Cierra sesión e iníciala con un proveedor de SSO para conectar una cuenta.
+ sso_confirm_title: ¿Desconectar cuenta?
+ sso_confirm_body: ¿Estás seguro de que deseas desconectar tu cuenta de %{provider}? Podrás volver a conectarla más tarde iniciando sesión de nuevo con ese proveedor.
+ sso_confirm_button: Desconectar
+ sso_warning_message: Este es tu único método de acceso. Deberías establecer una contraseña en tus ajustes de seguridad antes de desconectarlo, de lo contrario podrías perder el acceso a tu cuenta.
settings_nav:
accounts_label: Cuentas
advanced_section_title: Avanzado
@@ -125,3 +149,27 @@ es:
choose: Subir foto
choose_label: (opcional)
change: Cambiar foto
+ providers:
+ show:
+ coinbase_title: Coinbase
+ encryption_error:
+ title: Configuración de Cifrado Requerida
+ message: Las claves de cifrado de Active Record no están configuradas. Por favor, asegúrate de que las credenciales de cifrado (active_record_encryption.primary_key, active_record_encryption.deterministic_key y active_record_encryption.key_derivation_salt) estén correctamente configuradas en tus credenciales de Rails o variables de entorno antes de usar proveedores de sincronización.
+ coinbase_panel:
+ setup_instructions: "Para conectar Coinbase:"
+ step1_html: Ve a los Ajustes de API de Coinbase
+ step2: Crea una nueva clave API con permisos de solo lectura (ver cuentas, ver transacciones)
+ step3: Copia tu clave API y tu secreto de API y pégalos a continuación
+ api_key_label: Clave API
+ api_key_placeholder: Introduce tu clave API de Coinbase
+ api_secret_label: Secreto de API
+ api_secret_placeholder: Introduce tu secreto de API de Coinbase
+ connect_button: Conectar Coinbase
+ syncing: Sincronizando...
+ sync: Sincronizar
+ disconnect_confirm: ¿Estás seguro de que deseas desconectar esta conexión de Coinbase? Tus cuentas sincronizadas pasarán a ser cuentas manuales.
+ status_connected: Coinbase está conectado y sincronizando tus activos de criptomonedas.
+ status_not_connected: No conectado. Introduce tus credenciales de API arriba para comenzar.
+ enable_banking_panel:
+ callback_url_instruction: "Para la URL de retorno (callback), utiliza %{callback_url}."
+ connection_error: Error de conexión
\ No newline at end of file
diff --git a/config/locales/views/settings/hostings/de.yml b/config/locales/views/settings/hostings/de.yml
index cef56500f..322560e6c 100644
--- a/config/locales/views/settings/hostings/de.yml
+++ b/config/locales/views/settings/hostings/de.yml
@@ -16,6 +16,7 @@ de:
show:
general: Allgemeine Einstellungen
financial_data_providers: Finanzdatenanbieter
+ sync_settings: Synchronisierungseinstellungen
invites: Einladungscodes
title: Self-Hosting
danger_zone: Gefahrenbereich
@@ -24,11 +25,22 @@ de:
confirm_clear_cache:
title: Daten-Cache leeren?
body: Bist du sicher, dass du den Daten-Cache leeren möchtest? Dadurch werden alle Wechselkurse, Wertpapierpreise, Kontostände und andere Daten entfernt. Diese Aktion kann nicht rückgängig gemacht werden.
+ provider_selection:
+ title: Anbieterauswahl
+ description: Wähle, welcher Dienst für Wechselkurse und Wertpapierpreise verwendet werden soll. Yahoo Finance ist kostenlos und benötigt keinen API-Schlüssel. Twelve Data erfordert einen kostenlosen API-Schlüssel, bietet aber möglicherweise eine bessere Datenabdeckung.
+ exchange_rate_provider_label: Wechselkursanbieter
+ securities_provider_label: Wertpapiere (Aktienkurse) Anbieter
+ env_configured_message: Die Anbieterauswahl ist deaktiviert, weil Umgebungsvariablen (EXCHANGE_RATE_PROVIDER oder SECURITIES_PROVIDER) gesetzt sind. Um die Auswahl hier zu aktivieren, entferne diese Umgebungsvariablen aus deiner Konfiguration.
+ providers:
+ twelve_data: Twelve Data
+ yahoo_finance: Yahoo Finance
brand_fetch_settings:
description: Gib die von Brand Fetch bereitgestellte Client-ID ein.
label: Client-ID
placeholder: Gib hier deine Client-ID ein
title: Brand Fetch Einstellungen
+ high_res_label: Hochauflösende Logos aktivieren
+ high_res_description: Wenn aktiviert, werden Logos in 120x120 statt 40x40 abgerufen. Das liefert schärfere Bilder auf hochauflösenden Displays.
openai_settings:
description: Gib dein Zugriffstoken ein und konfiguriere optional einen benutzerdefinierten, OpenAI-kompatiblen Anbieter.
env_configured_message: Erfolgreich über Umgebungsvariablen konfiguriert.
@@ -38,6 +50,12 @@ de:
uri_base_placeholder: "https://api.openai.com/v1 (Standard)"
model_label: Modell (optional)
model_placeholder: "gpt-4.1 (Standard)"
+ json_mode_label: JSON-Modus
+ json_mode_auto: Auto (empfohlen)
+ json_mode_strict: Streng (am besten für Denk-Modelle)
+ json_mode_none: Keiner (am besten für Standard-Modelle)
+ json_mode_json_object: JSON-Objekt
+ json_mode_help: "Der strenge Modus funktioniert am besten mit Denk-Modellen (qwen-thinking, deepseek-reasoner). Der Modus Keiner funktioniert am besten mit Standard-Modellen (llama, mistral, gpt-oss)."
title: OpenAI
yahoo_finance_settings:
title: Yahoo Finance
@@ -53,11 +71,25 @@ de:
label: API-Schlüssel
placeholder: Gib hier deinen API-Schlüssel ein
plan: "%{plan}-Tarif"
+ plan_upgrade_warning_title: Einige Ticker erfordern einen kostenpflichtigen Tarif
+ plan_upgrade_warning_description: Die folgenden Ticker in deinem Portfolio können mit deinem aktuellen Twelve-Data-Tarif keine Kurse synchronisieren.
+ requires_plan: erfordert %{plan}-Tarif
+ view_pricing: Twelve-Data-Preise anzeigen
title: Twelve Data
update:
failure: Ungültiger Einstellungswert
success: Einstellungen aktualisiert
invalid_onboarding_state: Ungültiger Onboarding-Status
+ invalid_sync_time: Ungültiges Synchronisierungszeitformat. Bitte verwende das Format HH:MM (z. B. 02:30).
+ scheduler_sync_failed: Einstellungen gespeichert, aber die Synchronisierungsplanung konnte nicht aktualisiert werden. Bitte versuche es erneut oder prüfe die Server-Logs.
clear_cache:
cache_cleared: Daten-Cache wurde geleert. Dies kann einige Augenblicke dauern.
not_authorized: Du bist nicht berechtigt, diese Aktion auszuführen.
+ sync_settings:
+ auto_sync_label: Automatische Synchronisierung aktivieren
+ auto_sync_description: Wenn aktiviert, werden alle Konten täglich zur angegebenen Zeit automatisch synchronisiert.
+ auto_sync_time_label: Synchronisierungszeit (HH:MM)
+ auto_sync_time_description: Lege die Tageszeit fest, zu der die automatische Synchronisierung erfolgen soll.
+ include_pending_label: Ausstehende Transaktionen einbeziehen
+ include_pending_description: Wenn aktiviert, werden ausstehende (noch nicht gebuchte) Transaktionen importiert und bei Buchung automatisch abgeglichen. Deaktivieren, wenn deine Bank unzuverlässige Ausstehend-Daten liefert.
+ env_configured_message: Diese Einstellung ist deaktiviert, weil eine Anbieter-Umgebungsvariable (SIMPLEFIN_INCLUDE_PENDING oder PLAID_INCLUDE_PENDING) gesetzt ist. Entferne sie, um diese Einstellung zu aktivieren.
diff --git a/config/locales/views/settings/hostings/en.yml b/config/locales/views/settings/hostings/en.yml
index 8f3fcec32..814d0b13c 100644
--- a/config/locales/views/settings/hostings/en.yml
+++ b/config/locales/views/settings/hostings/en.yml
@@ -7,6 +7,9 @@ en:
email_confirmation_description: When enabled, users must confirm their email
address when changing it.
email_confirmation_title: Require email confirmation
+ default_family_title: Default family for new users
+ default_family_description: "Put new users on this family/group only if they have no invitation."
+ default_family_none: None (create new family)
generate_tokens: Generate new code
generated_tokens: Generated codes
title: Onboarding
@@ -16,6 +19,7 @@ en:
invite_only: Invite-only
show:
general: General Settings
+ ai_assistant: AI Assistant
financial_data_providers: Financial Data Providers
sync_settings: Sync Settings
invites: Invite Codes
@@ -35,6 +39,32 @@ en:
providers:
twelve_data: Twelve Data
yahoo_finance: Yahoo Finance
+ assistant_settings:
+ title: AI Assistant
+ description: Choose how the chat assistant responds. Builtin uses your configured LLM provider directly. External delegates to a remote AI agent that can call back to Sure's financial tools via MCP.
+ type_label: Assistant type
+ type_builtin: Builtin (direct LLM)
+ type_external: External (remote agent)
+ external_status: External assistant endpoint
+ external_configured: Configured
+ external_not_configured: Not configured. Enter the URL and token below, or set EXTERNAL_ASSISTANT_URL and EXTERNAL_ASSISTANT_TOKEN environment variables.
+ env_notice: "Assistant type is locked to '%{type}' via ASSISTANT_TYPE environment variable."
+ env_configured_external: Successfully configured through environment variables.
+ url_label: Endpoint URL
+ url_placeholder: "https://your-agent-host/v1/chat"
+ url_help: The full URL to your agent's API endpoint. Your agent provider will give you this.
+ token_label: API Token
+ token_placeholder: Enter the token from your agent provider
+ token_help: The authentication token provided by your external agent. This is sent as a Bearer token with each request.
+ agent_id_label: Agent ID (Optional)
+ agent_id_placeholder: "main (default)"
+ agent_id_help: Routes to a specific agent when the provider hosts multiple. Leave blank for the default.
+ disconnect_title: External connection
+ disconnect_description: Remove the external assistant connection and switch back to the builtin assistant.
+ disconnect_button: Disconnect
+ confirm_disconnect:
+ title: Disconnect external assistant?
+ body: This will remove the saved URL, token, and agent ID, and switch to the builtin assistant. You can reconnect later by entering new credentials.
brand_fetch_settings:
description: Enter the Client ID provided by Brand Fetch
label: Client ID
@@ -83,6 +113,8 @@ en:
invalid_onboarding_state: Invalid onboarding state
invalid_sync_time: Invalid sync time format. Please use HH:MM format (e.g., 02:30).
scheduler_sync_failed: Settings saved, but failed to update the sync schedule. Please try again or check the server logs.
+ disconnect_external_assistant:
+ external_assistant_disconnected: External assistant disconnected
clear_cache:
cache_cleared: Data cache has been cleared. This may take a few moments to complete.
not_authorized: You are not authorized to perform this action
diff --git a/config/locales/views/settings/hostings/es.yml b/config/locales/views/settings/hostings/es.yml
index df51060cd..aff9d71f8 100644
--- a/config/locales/views/settings/hostings/es.yml
+++ b/config/locales/views/settings/hostings/es.yml
@@ -4,7 +4,7 @@ es:
hostings:
invite_code_settings:
description: Controla cómo se registran nuevas personas en tu instancia de %{product}.
- email_confirmation_description: Cuando está habilitado, los usuarios deben confirmar su dirección de correo electrónico al cambiarla.
+ email_confirmation_description: Cuando está habilitado, los usuarios deben confirmar su dirección de correo electrónico al cambiarla o registrarse.
email_confirmation_title: Requerir confirmación de correo electrónico
generate_tokens: Generar nuevo código
generated_tokens: Códigos generados
@@ -15,22 +15,59 @@ es:
invite_only: Solo con invitación
show:
general: Configuración General
+ ai_assistant: Asistente de IA
financial_data_providers: Proveedores de Datos Financieros
+ sync_settings: Ajustes de Sincronización
invites: Códigos de Invitación
title: Autoalojamiento
danger_zone: Zona de Peligro
clear_cache: Limpiar caché de datos
- clear_cache_warning: Limpiar la caché de datos eliminará todos los tipos de cambio, precios de valores,
- saldos de cuentas y otros datos. Esto no eliminará cuentas, transacciones, categorías u otros datos propiedad del usuario.
+ clear_cache_warning: Limpiar la caché de datos eliminará todos los tipos de cambio, precios de valores, saldos de cuentas y otros datos temporales. Esto no eliminará cuentas, transacciones, categorías ni otros datos del usuario.
confirm_clear_cache:
title: ¿Limpiar caché de datos?
- body: ¿Estás seguro de que deseas limpiar la caché de datos? Esto eliminará todos los tipos de cambio,
- precios de valores, saldos de cuentas y otros datos. Esta acción no se puede deshacer.
+ body: ¿Estás seguro de que deseas limpiar la caché de datos? Se eliminarán tipos de cambio, precios y saldos históricos. Esta acción no se puede deshacer.
+ provider_selection:
+ title: Selección de Proveedores
+ description: Elige qué servicio usar para obtener tipos de cambio y precios de acciones. Yahoo Finance es gratuito y no requiere clave API. Twelve Data requiere una clave API (gratuita disponible) pero ofrece mayor cobertura de datos.
+ exchange_rate_provider_label: Proveedor de tipos de cambio
+ securities_provider_label: Proveedor de valores (Precios de acciones)
+ env_configured_message: La selección de proveedor está desactivada porque las variables de entorno (EXCHANGE_RATE_PROVIDER o SECURITIES_PROVIDER) están definidas. Para habilitar la selección aquí, elimina dichas variables de tu configuración.
+ providers:
+ twelve_data: Twelve Data
+ yahoo_finance: Yahoo Finance
+ assistant_settings:
+ title: Asistente de IA
+ description: Elige cómo responde el asistente del chat. "Integrado" utiliza directamente tu proveedor de LLM configurado. "Externo" delega en un agente remoto que puede interactuar con las herramientas financieras de Sure mediante MCP.
+ type_label: Tipo de asistente
+ type_builtin: Integrado (LLM directo)
+ type_external: Externo (agente remoto)
+ external_status: Punto de conexión del asistente externo
+ external_configured: Configurado
+ external_not_configured: No configurado. Introduce la URL y el token a continuación, o define las variables de entorno EXTERNAL_ASSISTANT_URL y EXTERNAL_ASSISTANT_TOKEN.
+ env_notice: "El tipo de asistente está fijado en '%{type}' mediante la variable de entorno ASSISTANT_TYPE."
+ env_configured_external: Configurado correctamente mediante variables de entorno.
+ url_label: URL del punto de conexión (Endpoint)
+ url_placeholder: "https://tu-agente-host/v1/chat"
+ url_help: La URL completa del punto de conexión de la API de tu agente. Tu proveedor de agentes te facilitará esta dirección.
+ token_label: Token de API
+ token_placeholder: Introduce el token de tu proveedor de agentes
+ token_help: El token de autenticación proporcionado por tu agente externo. Se envía como un token Bearer en cada solicitud.
+ agent_id_label: ID del Agente (Opcional)
+ agent_id_placeholder: "main (por defecto)"
+ agent_id_help: Dirige las peticiones a un agente específico si el proveedor aloja varios. Déjalo en blanco para el predeterminado.
+ disconnect_title: Conexión externa
+ disconnect_description: Elimina la conexión del asistente externo y vuelve al asistente integrado.
+ disconnect_button: Desconectar
+ confirm_disconnect:
+ title: ¿Desconectar asistente externo?
+ body: Esto eliminará la URL guardada, el token y el ID del agente, y cambiará al asistente integrado. Podrás volver a conectarlo más tarde introduciendo nuevas credenciales.
brand_fetch_settings:
description: Introduce el ID de Cliente proporcionado por Brand Fetch
label: ID de Cliente
placeholder: Introduce tu ID de Cliente aquí
title: Configuración de Brand Fetch
+ high_res_label: Activar logotipos de alta resolución
+ high_res_description: Cuando está habilitado, los logotipos se obtendrán a una resolución de 120x120 en lugar de 40x40. Esto ofrece imágenes más nítidas en pantallas de alta densidad de píxeles (DPI).
openai_settings:
description: Introduce el token de acceso y, opcionalmente, configura un proveedor compatible con OpenAI personalizado
env_configured_message: Configurado con éxito a través de variables de entorno.
@@ -40,6 +77,12 @@ es:
uri_base_placeholder: "https://api.openai.com/v1 (por defecto)"
model_label: Modelo (Opcional)
model_placeholder: "gpt-4.1 (por defecto)"
+ json_mode_label: Modo JSON
+ json_mode_auto: Automático (recomendado)
+ json_mode_strict: Estricto (mejor para modelos "thinking")
+ json_mode_none: Ninguno (mejor para modelos estándar)
+ json_mode_json_object: Objeto JSON
+ json_mode_help: "El modo Estricto funciona mejor con modelos de razonamiento (qwen-thinking, deepseek-reasoner). El modo Ninguno funciona mejor con modelos estándar (llama, mistral, gpt-oss)."
title: OpenAI
yahoo_finance_settings:
title: Yahoo Finance
@@ -55,11 +98,27 @@ es:
label: Clave API
placeholder: Introduce tu clave API aquí
plan: Plan %{plan}
+ plan_upgrade_warning_title: Algunos activos requieren un plan de pago
+ plan_upgrade_warning_description: Los siguientes activos de tu cartera no pueden sincronizar precios con tu plan actual de Twelve Data.
+ requires_plan: requiere el plan %{plan}
+ view_pricing: Ver precios de Twelve Data
title: Twelve Data
update:
- failure: Valor de configuración inválido
+ failure: Valor de configuración no válido
success: Configuración actualizada
- invalid_onboarding_state: Estado de incorporación inválido
+ invalid_onboarding_state: Estado de incorporación no válido
+ invalid_sync_time: Formato de hora de sincronización no válido. Por favor, usa el formato HH:MM (ej. 02:30).
+ scheduler_sync_failed: Ajustes guardados, pero no se pudo actualizar la programación de sincronización. Inténtalo de nuevo o revisa los registros del servidor.
+ disconnect_external_assistant:
+ external_assistant_disconnected: Asistente externo desconectado
clear_cache:
cache_cleared: La caché de datos ha sido limpiada. Esto puede tardar unos momentos en completarse.
not_authorized: No estás autorizado para realizar esta acción
+ sync_settings:
+ auto_sync_label: Activar sincronización automática
+ auto_sync_description: Cuando está habilitado, todas las cuentas se sincronizarán automáticamente cada día a la hora especificada.
+ auto_sync_time_label: Hora de sincronización (HH:MM)
+ auto_sync_time_description: Especifica la hora del día en la que debe ocurrir la sincronización automática.
+ include_pending_label: Incluir transacciones pendientes
+ include_pending_description: Cuando está habilitado, las transacciones pendientes (no liquidadas) se importarán y se conciliarán automáticamente cuando se confirmen. Desactívalo si tu banco proporciona datos pendientes poco fiables.
+ env_configured_message: Este ajuste está desactivado porque hay una variable de entorno del proveedor (SIMPLEFIN_INCLUDE_PENDING o PLAID_INCLUDE_PENDING) definida. Elimínala para habilitar este ajuste aquí.
\ No newline at end of file
diff --git a/config/locales/views/settings/hostings/pl.yml b/config/locales/views/settings/hostings/pl.yml
new file mode 100644
index 000000000..95b449e35
--- /dev/null
+++ b/config/locales/views/settings/hostings/pl.yml
@@ -0,0 +1,155 @@
+---
+pl:
+ settings:
+ hostings:
+ invite_code_settings:
+ description: Kontroluj, jak nowe osoby rejestrują się w Twojej instancji %{product}.
+ email_confirmation_description: Gdy opcja jest włączona, użytkownicy muszą potwierdzić adres e-mail przy jego zmianie.
+ email_confirmation_title: Wymagaj potwierdzenia e-mail
+ default_family_title: Domyślna rodzina dla nowych użytkowników
+ default_family_description: Dodaj nowych użytkowników do tej rodziny/grupy tylko wtedy, gdy nie mają zaproszenia.
+ default_family_none: Brak (utwórz nową rodzinę)
+ generate_tokens: Wygeneruj nowy kod
+ generated_tokens: Wygenerowane kody
+ title: Onboarding
+ states:
+ open: Otwarty
+ closed: Zamknięty
+ invite_only: Tylko na zaproszenie
+ show:
+ general: Ustawienia ogólne
+ ai_assistant: Asystent AI
+ financial_data_providers: Dostawcy danych finansowych
+ sync_settings: Ustawienia synchronizacji
+ invites: Kody zaproszeń
+ title: Self-Hosting
+ danger_zone: Strefa ryzyka
+ clear_cache: Wyczyść pamięć podręczną danych
+ clear_cache_warning: Wyczyszczenie pamięci podręcznej danych usunie wszystkie kursy walut, ceny papierów wartościowych, salda kont i inne dane. Nie usunie to kont, transakcji, kategorii ani innych danych należących do użytkownika.
+ confirm_clear_cache:
+ title: Wyczyścić pamięć podręczną danych?
+ body: Czy na pewno chcesz wyczyścić pamięć podręczną danych? Spowoduje to usunięcie wszystkich kursów walut, cen papierów wartościowych, sald kont i innych danych. Tej akcji nie można cofnąć.
+ provider_selection:
+ title: Wybór dostawcy
+ description: Wybierz usługę do pobierania kursów walut i cen papierów wartościowych. Yahoo Finance jest darmowe i nie wymaga klucza API. Twelve Data wymaga darmowego klucza API, ale może oferować szersze pokrycie danych.
+ exchange_rate_provider_label: Dostawca kursów walut
+ securities_provider_label: Dostawca papierów wartościowych (cen akcji)
+ env_configured_message: Wybór dostawcy jest wyłączony, ponieważ ustawiono zmienne środowiskowe (EXCHANGE_RATE_PROVIDER lub SECURITIES_PROVIDER). Aby włączyć wybór tutaj, usuń te zmienne z konfiguracji.
+ providers:
+ twelve_data: Twelve Data
+ yahoo_finance: Yahoo Finance
+ assistant_settings:
+ title: Asystent AI
+ description: Wybierz, jak odpowiada asystent czatu. Wbudowany asystent korzysta bezpośrednio ze skonfigurowanego dostawcy LLM. Zewnętrzny deleguje zapytania do zdalnego agenta AI, który może korzystać z narzędzi finansowych Sure przez MCP.
+ type_label: Typ asystenta
+ type_builtin: Wbudowany (bezpośredni LLM)
+ type_external: Zewnętrzny (zdalny agent)
+ external_status: Endpoint zewnętrznego asystenta
+ external_configured: Skonfigurowany
+ external_not_configured: Nieskonfigurowany. Wprowadź poniżej URL i token albo ustaw zmienne środowiskowe EXTERNAL_ASSISTANT_URL i EXTERNAL_ASSISTANT_TOKEN.
+ env_notice: Typ asystenta jest zablokowany na '%{type}' przez zmienną środowiskową ASSISTANT_TYPE.
+ env_configured_external: Pomyślnie skonfigurowano przez zmienne środowiskowe.
+ url_label: URL endpointu
+ url_placeholder: https://your-agent-host/v1/chat
+ url_help: Pełny URL endpointu API Twojego agenta. Otrzymasz go od dostawcy agenta.
+ token_label: Token API
+ token_placeholder: Wprowadź token od dostawcy agenta
+ token_help: Token uwierzytelniający dostarczony przez zewnętrznego agenta. Jest wysyłany jako token Bearer z każdym żądaniem.
+ agent_id_label: ID agenta (opcjonalne)
+ agent_id_placeholder: main (domyślnie)
+ agent_id_help: Kieruje do konkretnego agenta, gdy dostawca hostuje ich wiele. Pozostaw puste, aby użyć domyślnego.
+ disconnect_title: Połączenie zewnętrzne
+ disconnect_description: Usuń połączenie z zewnętrznym asystentem i przełącz z powrotem na asystenta wbudowanego.
+ disconnect_button: Rozłącz
+ confirm_disconnect:
+ title: Rozłączyć zewnętrznego asystenta?
+ body: To usunie zapisany URL, token i ID agenta oraz przełączy na asystenta wbudowanego. Możesz połączyć ponownie później, podając nowe dane.
+ brand_fetch_settings:
+ description: Wprowadź Client ID otrzymany od Brand Fetch
+ label: Client ID
+ placeholder: Wprowadź tutaj swój Client ID
+ title: Ustawienia Brand Fetch
+ high_res_label: Włącz logotypy o wysokiej rozdzielczości
+ high_res_description: Gdy włączone, logotypy będą pobierane w rozdzielczości 120x120 zamiast 40x40. Zapewnia to ostrzejsze obrazy na ekranach o wysokim DPI.
+ openai_settings:
+ description: Wprowadź token dostępu i opcjonalnie skonfiguruj niestandardowego dostawcę zgodnego z OpenAI
+ env_configured_message: Pomyślnie skonfigurowano przez zmienne środowiskowe.
+ access_token_label: Token dostępu
+ access_token_placeholder: Wprowadź tutaj token dostępu
+ uri_base_label: Bazowy URL API (opcjonalne)
+ uri_base_placeholder: https://api.openai.com/v1 (domyślnie)
+ model_label: Model (opcjonalne)
+ model_placeholder: gpt-4.1 (domyślnie)
+ json_mode_label: Tryb JSON
+ json_mode_auto: Auto (zalecane)
+ json_mode_strict: Ścisły (najlepszy dla modeli rozumujących)
+ json_mode_none: Brak (najlepszy dla modeli standardowych)
+ json_mode_json_object: Obiekt JSON
+ json_mode_help: Tryb ścisły najlepiej działa z modelami rozumującymi (qwen-thinking, deepseek-reasoner). Tryb brak najlepiej działa z modelami standardowymi (llama, mistral, gpt-oss).
+ title: OpenAI
+ yahoo_finance_settings:
+ title: Yahoo Finance
+ description: Yahoo Finance zapewnia bezpłatny dostęp do cen akcji, kursów walut i danych finansowych bez potrzeby klucza API.
+ status_active: Yahoo Finance jest aktywne i działa
+ status_inactive: Połączenie z Yahoo Finance nie powiodło się
+ connection_failed: Nie można połączyć z Yahoo Finance
+ troubleshooting: Sprawdź połączenie internetowe i ustawienia zapory. Yahoo Finance może być tymczasowo niedostępne.
+ twelve_data_settings:
+ api_calls_used: "%{used} / %{limit} wykorzystanych dziennych wywołań API (%{percentage})"
+ description: Wprowadź klucz API otrzymany od Twelve Data
+ env_configured_message: Pomyślnie skonfigurowano przez zmienną środowiskową TWELVE_DATA_API_KEY.
+ label: Klucz API
+ placeholder: Wprowadź tutaj klucz API
+ plan: "plan %{plan}"
+ plan_upgrade_warning_title: Niektóre tickery wymagają płatnego planu
+ plan_upgrade_warning_description: Poniższych tickerów w Twoim portfelu nie można synchronizować przy Twoim aktualnym planie Twelve Data.
+ requires_plan: wymaga planu %{plan}
+ view_pricing: Zobacz cennik Twelve Data
+ title: Twelve Data
+ gus_sdp_settings:
+ title: GUS SDP (Inflacja)
+ description: Opcjonalny klucz API do danych inflacyjnych GUS SDP. Pozostaw puste, aby korzystać z bezpłatnego poziomu anonimowego.
+ env_configured_message: Pomyślnie skonfigurowano przez zmienną środowiskową GUS_SDP_API_KEY.
+ configured_in_settings_message: Klucz API skonfigurowany w ustawieniach.
+ configured_via_env: Klucz API jest skonfigurowany przez zmienną środowiskową GUS_SDP_API_KEY.
+ free_default_message: Brak skonfigurowanego klucza API. Domyślnie używany jest bezpłatny dostęp anonimowy.
+ clear_api_key: Wyczyść zapisany klucz API
+ clear_api_key_confirm: Usunąć zapisany klucz API GUS?
+ label: Klucz API
+ placeholder: Wprowadź klucz API GUS SDP
+ import_enabled_label: Włącz automatyczny import CPI GUS
+ import_enabled_help: Domyślnie wyłączone. Włącz tylko, jeśli chcesz automatycznie importować stawki CPI do obliczeń EOD/ROD.
+ import_enabled_env_locked: To ustawienie jest zablokowane przez zmienną środowiskową GUS_INFLATION_IMPORT_ENABLED.
+ start_year: Rok początkowy
+ end_year: Rok końcowy
+ import_now: Importuj historię CPI teraz
+ last_import: Ostatni import
+ last_range: Ostatni zakres
+ last_count: Zaimportowane rekordy
+ last_error: Ostatni błąd
+ stored_records: Zapisane rekordy
+ stored_range: Zapisany zakres
+ never: Nigdy
+ import_gus_inflation_rates:
+ import_enqueued: Import CPI został dodany do kolejki.
+ import_disabled: Import CPI jest wyłączony. Najpierw włącz go w ustawieniach.
+ invalid_import_range: Nieprawidłowy zakres lat dla importu CPI.
+ update:
+ failure: Nieprawidłowa wartość ustawienia
+ success: Ustawienia zostały zaktualizowane
+ invalid_onboarding_state: Nieprawidłowy stan onboardingu
+ invalid_sync_time: Nieprawidłowy format czasu synchronizacji. Użyj formatu HH:MM (np. 02:30).
+ scheduler_sync_failed: Ustawienia zapisano, ale nie udało się zaktualizować harmonogramu synchronizacji. Spróbuj ponownie lub sprawdź logi serwera.
+ disconnect_external_assistant:
+ external_assistant_disconnected: Zewnętrzny asystent został rozłączony
+ clear_cache:
+ cache_cleared: Pamięć podręczna danych została wyczyszczona. Zakończenie operacji może zająć chwilę.
+ not_authorized: Nie masz uprawnień do wykonania tej akcji
+ sync_settings:
+ auto_sync_label: Włącz automatyczną synchronizację
+ auto_sync_description: Gdy włączone, wszystkie konta będą automatycznie synchronizowane codziennie o wskazanej godzinie.
+ auto_sync_time_label: Czas synchronizacji (HH:MM)
+ auto_sync_time_description: Określ godzinę dnia, o której ma odbywać się automatyczna synchronizacja.
+ include_pending_label: Uwzględniaj transakcje oczekujące
+ include_pending_description: Gdy włączone, transakcje oczekujące (niezaksięgowane) będą importowane i automatycznie uzgadniane po zaksięgowaniu. Wyłącz, jeśli Twój bank dostarcza niewiarygodne dane oczekujące.
+ env_configured_message: To ustawienie jest wyłączone, ponieważ ustawiono zmienną środowiskową dostawcy (SIMPLEFIN_INCLUDE_PENDING lub PLAID_INCLUDE_PENDING). Usuń ją, aby włączyć to ustawienie.
diff --git a/config/locales/views/settings/pl.yml b/config/locales/views/settings/pl.yml
new file mode 100644
index 000000000..9962be6a7
--- /dev/null
+++ b/config/locales/views/settings/pl.yml
@@ -0,0 +1,192 @@
+---
+pl:
+ views:
+ settings:
+ payments:
+ renewal: Twoje wsparcie będzie kontynuowane %{date}.
+ cancellation: Twoje wsparcie kończy się %{date}.
+ settings:
+ ai_prompts:
+ show:
+ page_title: Prompty AI
+ openai_label: OpenAI
+ disable_ai: Wyłącz asystenta AI
+ prompt_instructions: Instrukcje promptów
+ main_system_prompt:
+ title: Główny prompt systemowy
+ subtitle: Podstawowe instrukcje definiujące, jak asystent AI zachowuje się we wszystkich rozmowach
+ transaction_categorizer:
+ title: Kategoryzator transakcji
+ subtitle: AI automatycznie kategoryzuje Twoje transakcje na podstawie zdefiniowanych kategorii
+ merchant_detector:
+ title: Wykrywanie sprzedawcy
+ subtitle: AI identyfikuje i wzbogaca dane transakcji o informacje o sprzedawcy
+ payments:
+ show:
+ page_title: Płatności
+ subscription_subtitle: Zaktualizuj dane swojej karty kredytowej
+ subscription_title: Zarządzaj wpłatami
+ appearances:
+ show:
+ page_title: Wygląd
+ theme_title: Motyw
+ theme_subtitle: Wybierz preferowany motyw aplikacji
+ theme_dark: Ciemny
+ theme_light: Jasny
+ theme_system: Systemowy
+ transactions_title: Transakcje
+ transactions_subtitle: Dostosuj sposób wyświetlania transakcji
+ dashboard_title: Pulpit
+ dashboard_subtitle: Dostosuj sposób wyświetlania pulpitu
+ dashboard_two_column_title: Układ dwukolumnowy
+ dashboard_two_column_description: Wyświetlaj widżety pulpitu w dwóch kolumnach na dużych ekranach. Gdy opcja jest wyłączona, widżety układają się w jednej kolumnie.
+ split_grouped_title: Grupuj podzielone transakcje
+ split_grouped_description: Pokazuj podzielone transakcje zgrupowane pod transakcją nadrzędną na liście transakcji. Gdy opcja jest wyłączona, pozycje podrzędne pojawiają się jako osobne wiersze.
+ preferences:
+ show:
+ country: Kraj
+ currency: Waluta
+ date_format: Format daty
+ general_subtitle: Skonfiguruj swoje preferencje
+ general_title: Ogólne
+ default_period: Domyślny okres
+ default_account_order: Domyślna kolejność kont
+ language: Język
+ language_auto: Język przeglądarki
+ page_title: Preferencje
+ timezone: Strefa czasowa
+ month_start_day: Miesiąc budżetowy zaczyna się
+ month_start_day_hint: Ustaw, kiedy zaczyna się Twój miesiąc budżetowy (np. dzień wypłaty)
+ month_start_day_warning: Twoje budżety i obliczenia MTD będą używać tego niestandardowego dnia startowego zamiast 1. dnia każdego miesiąca.
+ sharing_title: "Udostępnianie %{moniker}"
+ sharing_subtitle: Kontroluj, jak konta są udostępniane w Twoim %{moniker}
+ sharing_default_label: Domyślne udostępnianie dla nowych kont
+ sharing_shared: Udostępnij wszystkim członkom
+ sharing_private: Domyślnie ustaw jako prywatne
+ profiles:
+ destroy:
+ cannot_remove_self: Nie możesz usunąć siebie z konta.
+ member_removal_failed: Wystąpił problem podczas usuwania członka.
+ member_removed: Członek został pomyślnie usunięty.
+ not_authorized: Nie masz uprawnień do usuwania członków.
+ show:
+ confirm_delete:
+ body: Czy na pewno chcesz trwale usunąć swoje konto? Tej akcji nie można cofnąć.
+ title: Usunąć konto?
+ confirm_reset:
+ body: Czy na pewno chcesz zresetować swoje konto? Spowoduje to usunięcie wszystkich kont, kategorii, sprzedawców, tagów i innych danych. Tej akcji nie można cofnąć.
+ title: Zresetować konto?
+ confirm_reset_with_sample_data:
+ body: Czy na pewno chcesz zresetować swoje konto i załadować przykładowe dane? Spowoduje to usunięcie istniejących danych i zastąpienie ich danymi demonstracyjnymi, aby bezpiecznie poznawać Sure.
+ title: Zresetować konto i załadować przykładowe dane?
+ confirm_remove_invitation:
+ body: Czy na pewno chcesz usunąć zaproszenie dla %{email}?
+ title: Usuń zaproszenie
+ confirm_remove_member:
+ body: Czy na pewno chcesz usunąć %{name} ze swojego konta?
+ title: Usuń członka
+ danger_zone_title: Strefa ryzyka
+ delete_account: Usuń konto
+ delete_account_warning: Usunięcie konta trwale usunie wszystkie Twoje dane i nie można tego cofnąć.
+ reset_account: Zresetuj konto
+ reset_account_warning: Reset konta usunie wszystkie konta, kategorie, sprzedawców, tagi i inne dane, ale pozostawi Twoje konto użytkownika.
+ reset_account_with_sample_data: Zresetuj i załaduj dane
+ reset_account_with_sample_data_warning: Usuń wszystkie istniejące dane i załaduj nowe dane przykładowe, aby poznawać aplikację na gotowym środowisku.
+ email: E-mail
+ first_name: Imię
+ group_form_input_placeholder: Wprowadź nazwę grupy
+ group_form_label: Nazwa grupy
+ group_title: Członkowie grupy
+ household_form_input_placeholder: Wprowadź nazwę gospodarstwa domowego
+ household_form_label: Nazwa gospodarstwa domowego
+ household_subtitle: Zaproszone osoby mogą zalogować się do Twojego konta %{moniker} i uzyskać dostęp do współdzielonych zasobów.
+ household_title: Gospodarstwo domowe
+ invitation_link: Odnośnik do zaproszenia
+ invite_member: Dodaj członka
+ last_name: Nazwisko
+ page_title: Informacje profilowe
+ pending: Oczekujące
+ profile_subtitle: Dostosuj sposób, w jaki wyświetlasz się w %{product_name}
+ profile_title: Dane osobiste
+ remove_invitation: Usuń zaproszenie
+ remove_member: Usuń członka
+ save: Zapisz
+ securities:
+ show:
+ page_title: Bezpieczeństwo
+ mfa_title: Uwierzytelnianie dwuskładnikowe
+ mfa_description: Dodaj dodatkową warstwę bezpieczeństwa do konta, wymagając kodu z aplikacji uwierzytelniającej podczas logowania
+ enable_mfa: Włącz 2FA
+ disable_mfa: Wyłącz 2FA
+ disable_mfa_confirm: Czy na pewno chcesz wyłączyć uwierzytelnianie dwuskładnikowe?
+ sso_title: Połączone konta
+ sso_subtitle: Zarządzaj połączeniami kont jednokrotnego logowania
+ sso_disconnect: Odłącz
+ sso_last_used: Ostatnio używane
+ sso_never: Nigdy
+ sso_no_email: Brak adresu e-mail
+ sso_no_identities: Brak połączonych kont SSO
+ sso_connect_hint: Wyloguj się i zaloguj przez dostawcę SSO, aby połączyć konto.
+ sso_confirm_title: Odłączyć konto?
+ sso_confirm_body: Czy na pewno chcesz odłączyć konto %{provider}? Możesz je połączyć ponownie później, logując się ponownie przez tego dostawcę.
+ sso_confirm_button: Odłącz
+ sso_warning_message: To jest Twoja jedyna metoda logowania. Przed odłączeniem ustaw hasło w ustawieniach bezpieczeństwa, w przeciwnym razie możesz utracić dostęp do konta.
+ settings_nav:
+ accounts_label: Konta
+ advanced_section_title: Zaawansowane
+ ai_prompts_label: Prompty AI
+ api_key_label: Klucz API
+ payment_label: Płatności
+ categories_label: Kategorie
+ feedback_label: Opinie
+ general_section_title: Ogólne
+ imports_label: Importy
+ exports_label: Eksporty
+ logout: Wyloguj
+ merchants_label: Sprzedawcy
+ guides_label: Przewodniki
+ other_section_title: Więcej
+ preferences_label: Preferencje
+ profile_label: Informacje profilowe
+ recurring_transactions_label: Cykliczne
+ rules_label: Reguły
+ security_label: Bezpieczeństwo
+ self_hosting_label: Hosting własny
+ tags_label: Tagi
+ transactions_section_title: Transakcje
+ whats_new_label: Co nowego
+ api_keys_label: Klucz API
+ appearance_label: Wygląd
+ bank_sync_label: Synchronizacja banku
+ settings_nav_link_large:
+ next: Dalej
+ previous: Wstecz
+ user_avatar_field:
+ accepted_formats: JPG lub PNG. Maks. 5 MB.
+ choose: Prześlij zdjęcie
+ choose_label: "(opcjonalne)"
+ change: Zmień zdjęcie
+ providers:
+ show:
+ coinbase_title: Coinbase
+ encryption_error:
+ title: Wymagana konfiguracja szyfrowania
+ message: Klucze szyfrowania Active Record nie są skonfigurowane. Upewnij się, że dane uwierzytelniające szyfrowania (active_record_encryption.primary_key, active_record_encryption.deterministic_key oraz active_record_encryption.key_derivation_salt) są poprawnie ustawione w poświadczeniach Rails lub zmiennych środowiskowych przed użyciem dostawców synchronizacji.
+ coinbase_panel:
+ setup_instructions: 'Aby połączyć Coinbase:'
+ step1_html: Przejdź do Ustawień API Coinbase
+ step2: Utwórz nowy klucz API z uprawnieniami tylko do odczytu (podgląd kont, podgląd transakcji)
+ step3: Skopiuj klucz API i sekret API, a następnie wklej je poniżej
+ api_key_label: Klucz API
+ api_key_placeholder: Wprowadź klucz API Coinbase
+ api_secret_label: Sekret API
+ api_secret_placeholder: Wprowadź sekret API Coinbase
+ connect_button: Połącz Coinbase
+ syncing: Synchronizacja...
+ sync: Synchronizuj
+ disconnect_confirm: Czy na pewno chcesz odłączyć to połączenie Coinbase? Twoje zsynchronizowane konta staną się kontami ręcznymi.
+ status_connected: Coinbase jest połączony i synchronizuje Twoje zasoby kryptowalutowe.
+ status_not_connected: Brak połączenia. Wprowadź powyżej dane API, aby rozpocząć.
+ enable_banking_panel:
+ callback_url_instruction: Dla URL callback użyj %{callback_url}.
+ connection_error: Błąd połączenia
diff --git a/config/locales/views/settings/pt-BR.yml b/config/locales/views/settings/pt-BR.yml
index cc393360a..26fa9fa13 100644
--- a/config/locales/views/settings/pt-BR.yml
+++ b/config/locales/views/settings/pt-BR.yml
@@ -4,6 +4,7 @@ pt-BR:
settings:
payments:
renewal: "Sua contribuição continua em %{date}."
+ cancellation: "Sua contribuição termina em %{date}."
settings:
ai_prompts:
show:
@@ -25,6 +26,22 @@ pt-BR:
page_title: Pagamento
subscription_subtitle: Atualize sua assinatura e detalhes de pagamento
subscription_title: Gerenciar assinatura
+ appearances:
+ show:
+ page_title: Aparência
+ theme_title: Tema
+ theme_subtitle: Escolha um tema preferido para o aplicativo
+ theme_dark: Escuro
+ theme_light: Claro
+ theme_system: Sistema
+ transactions_title: Transações
+ transactions_subtitle: Personalize como as transações são exibidas
+ dashboard_title: Painel
+ dashboard_subtitle: Personalize como o painel é exibido
+ dashboard_two_column_title: Layout de duas colunas
+ dashboard_two_column_description: Exibe os widgets do painel em duas colunas em telas grandes. Quando desativado, os widgets ficam empilhados em uma única coluna.
+ split_grouped_title: Agrupar transações divididas
+ split_grouped_description: Mostra as transações divididas agrupadas sob o registro principal na lista de transações. Quando desativado, as subdivisões aparecem como linhas individuais.
preferences:
show:
country: País
@@ -33,15 +50,19 @@ pt-BR:
general_subtitle: Configure suas preferências
general_title: Geral
default_period: Período Padrão
+ default_account_order: Ordem de conta padrão
language: Idioma
language_auto: Idioma do navegador
page_title: Preferências
- theme_dark: Escuro
- theme_light: Claro
- theme_subtitle: Escolha um tema preferido para o aplicativo
- theme_system: Sistema
- theme_title: Tema
timezone: Fuso horário
+ month_start_day: O mês do orçamento começa em
+ month_start_day_hint: Defina quando começa o seu mês de orçamento (por exemplo, no dia do pagamento).
+ month_start_day_warning: Seus orçamentos e cálculos MTD usarão essa data de início personalizada em vez do dia 1º de cada mês.
+ sharing_title: "Compartilhamento %{moniker}"
+ sharing_subtitle: "Controle como as contas são compartilhadas no seu %{moniker}"
+ sharing_default_label: Compartilhamento padrão para novas contas
+ sharing_shared: Compartilhar com todos os membros
+ sharing_private: Manter privado por padrão
profiles:
destroy:
cannot_remove_self: Você não pode se remover da conta.
@@ -56,6 +77,9 @@ pt-BR:
confirm_reset:
body: Tem certeza de que deseja resetar sua conta? Isso excluirá todas as suas contas, categorias, comerciantes, tags e outros dados. Esta ação não pode ser desfeita.
title: Resetar conta?
+ confirm_reset_with_sample_data:
+ body: Tem certeza de que deseja redefinir sua conta e carregar dados de exemplo? Isso excluirá seus dados existentes e os substituirá por dados de demonstração para que você possa explorar o Sure com segurança.
+ title: Redefinir conta e carregar dados de exemplo?
confirm_remove_invitation:
body: Tem certeza de que deseja remover o convite para %{email}?
title: Remover Convite
@@ -68,12 +92,16 @@ pt-BR:
os seus dados e não pode ser desfeito.
reset_account: Resetar conta
reset_account_warning: Resetar sua conta excluirá todas as suas contas, categorias, comerciantes, tags e outros dados, mas manterá sua conta de usuário intacta.
+ reset_account_with_sample_data: Reiniciar e pré-carregar
+ reset_account_with_sample_data_warning: Exclua todos os seus dados existentes e, em seguida, carregue novos dados de amostra para que você possa explorar um ambiente pré-configurado.
email: E-mail
first_name: Primeiro Nome
+ group_form_input_placeholder: Digite o nome do grupo
+ group_form_label: Nome do grupo
+ group_title: Membros do grupo
household_form_input_placeholder: Digite o nome da família
household_form_label: Nome da família
- household_subtitle: Convide membros da família, parceiros e outros indivíduos. Os convidados
- podem fazer login na sua família e acessar suas contas compartilhadas.
+ household_subtitle: Os convidados podem entrar na sua conta %{moniker} e acessar os recursos compartilhados.
household_title: Família
invitation_link: Link de convite
invite_member: Adicionar membro
@@ -88,8 +116,27 @@ pt-BR:
securities:
show:
page_title: Segurança
+ mfa_title: Autenticação de Dois Fatores
+ mfa_description: Adicione uma camada extra de segurança à sua conta, exigindo um código do seu aplicativo autenticador ao fazer login.
+ enable_mfa: Ativar 2FA
+ disable_mfa: Desativar 2FA
+ disable_mfa_confirm: Tem certeza de que deseja desativar a autenticação de dois fatores?
+ sso_title: Contas Conectadas
+ sso_subtitle: Gerencie suas conexões de contas de login único (SSO)
+ sso_disconnect: Desconectar
+ sso_last_used: Último uso
+ sso_never: Nunca
+ sso_no_email: Sem e-mail
+ sso_no_identities: Nenhuma conta SSO conectada
+ sso_connect_hint: Saia da sua conta e entre novamente com um provedor SSO para conectar uma conta.
+ sso_confirm_title: Desconectar Conta?
+ sso_confirm_body: Tem certeza de que deseja desconectar sua conta %{provider}? Você poderá reconectá-la posteriormente fazendo login novamente com esse provedor.
+ sso_confirm_button: Desconectar
+ sso_warning_message: Este é o seu único método de login. Você deve definir uma senha nas suas configurações de segurança antes de desconectar, caso contrário, você poderá ficar bloqueado na sua conta.
settings_nav:
accounts_label: Contas
+ advanced_section_title: Avançado
+ ai_prompts_label: Sugestões de IA
api_key_label: Chave da API
payment_label: Pagamento
categories_label: Categorias
@@ -99,15 +146,19 @@ pt-BR:
exports_label: Exportações
logout: Sair
merchants_label: Comerciantes
+ guides_label: Guias
other_section_title: Mais
preferences_label: Preferências
profile_label: Conta
+ recurring_transactions_label: Recorrente
rules_label: Regras
security_label: Segurança
self_hosting_label: Auto hospedagem
tags_label: Tags
transactions_section_title: Transações
whats_new_label: Novidades
+ api_keys_label: Chave da API
+ bank_sync_label: Sincronização bancária
settings_nav_link_large:
next: Próximo
previous: Voltar
@@ -116,3 +167,27 @@ pt-BR:
choose: Enviar foto
choose_label: (opcional)
change: Alterar foto
+ providers:
+ show:
+ coinbase_title: Coinbase
+ encryption_error:
+ title: Configuração de criptografia necessária
+ message: As chaves de criptografia do Active Record não estão configuradas. Certifique-se de que as credenciais de criptografia (active_record_encryption.primary_key, active_record_encryption.deterministic_key e active_record_encryption.key_derivation_salt) estejam configuradas corretamente em suas credenciais do Rails ou variáveis de ambiente antes de usar provedores de sincronização.
+ coinbase_panel:
+ setup_instructions: "Para conectar à Coinbase:"
+ step1_html: Acesse Configurações da API da Coinbase
+ step2: Crie uma nova chave de API com permissões somente leitura (visualizar contas, visualizar transações)
+ step3: Copie sua chave de API e o segredo da API e cole-os abaixo
+ api_key_label: Chave de API
+ api_key_placeholder: Insira sua chave de API da Coinbase
+ api_secret_label: Segredo da API
+ api_secret_placeholder: Insira seu segredo da API da Coinbase
+ connect_button: Conectar à Coinbase
+ syncing: Sincronizando...
+ sync: Sincronizar
+ disconnect_confirm: Tem certeza de que deseja desconectar esta conexão com a Coinbase? Suas contas sincronizadas se tornarão contas manuais.
+ status_connected: A Coinbase está conectada e sincronizando seus ativos em criptomoedas.
+ status_not_connected: Não conectado. Insira suas credenciais de API acima para começar.
+ enable_banking_panel:
+ callback_url_instruction: "Para a URL de retorno de chamada, use %{callback_url}."
+ connection_error: Erro de conexão
diff --git a/config/locales/views/settings/securities/pl.yml b/config/locales/views/settings/securities/pl.yml
new file mode 100644
index 000000000..57ff230a2
--- /dev/null
+++ b/config/locales/views/settings/securities/pl.yml
@@ -0,0 +1,10 @@
+---
+pl:
+ settings:
+ securities:
+ show:
+ disable_mfa: Wyłącz 2FA
+ disable_mfa_confirm: Czy na pewno chcesz wyłączyć uwierzytelnianie dwuskładnikowe? To obniży poziom bezpieczeństwa konta.
+ enable_mfa: Włącz 2FA
+ mfa_description: Dodaj dodatkową warstwę bezpieczeństwa, wymagając kodu z aplikacji uwierzytelniającej podczas logowania
+ mfa_title: Uwierzytelnianie dwuskładnikowe
diff --git a/config/locales/views/settings/sso_identities/de.yml b/config/locales/views/settings/sso_identities/de.yml
new file mode 100644
index 000000000..2df58d70a
--- /dev/null
+++ b/config/locales/views/settings/sso_identities/de.yml
@@ -0,0 +1,7 @@
+---
+de:
+ settings:
+ sso_identities:
+ destroy:
+ cannot_unlink_last: Die letzte Identität kann nicht getrennt werden
+ success: Erfolg
diff --git a/config/locales/views/settings/sso_identities/es.yml b/config/locales/views/settings/sso_identities/es.yml
new file mode 100644
index 000000000..a1edda19f
--- /dev/null
+++ b/config/locales/views/settings/sso_identities/es.yml
@@ -0,0 +1,7 @@
+---
+es:
+ settings:
+ sso_identities:
+ destroy:
+ cannot_unlink_last: No se puede desvincular la última identidad
+ success: Éxito
\ No newline at end of file
diff --git a/config/locales/views/settings/sso_identities/pl.yml b/config/locales/views/settings/sso_identities/pl.yml
new file mode 100644
index 000000000..ddc23496a
--- /dev/null
+++ b/config/locales/views/settings/sso_identities/pl.yml
@@ -0,0 +1,7 @@
+---
+pl:
+ settings:
+ sso_identities:
+ destroy:
+ cannot_unlink_last: Nie można odłączyć ostatniej tożsamości
+ success: Tożsamość została odłączona
diff --git a/config/locales/views/shared/en.yml b/config/locales/views/shared/en.yml
index 37e22cd34..f94797c77 100644
--- a/config/locales/views/shared/en.yml
+++ b/config/locales/views/shared/en.yml
@@ -10,5 +10,6 @@ en:
label: Amount
syncing_notice:
syncing: Syncing accounts data...
+ require_admin: "Only admins can perform this action"
trend_change:
no_change: "no change"
diff --git a/config/locales/views/shared/pl.yml b/config/locales/views/shared/pl.yml
new file mode 100644
index 000000000..03b5a73bf
--- /dev/null
+++ b/config/locales/views/shared/pl.yml
@@ -0,0 +1,15 @@
+---
+pl:
+ shared:
+ confirm_modal:
+ accept: Potwierdź
+ body_html: "
Nie będzie można cofnąć tej decyzji
"
+ cancel: Anuluj
+ title: Czy na pewno?
+ money_field:
+ label: Kwota
+ syncing_notice:
+ syncing: Synchronizowanie danych kont...
+ require_admin: "Tę akcję mogą wykonać tylko administratorzy"
+ trend_change:
+ no_change: "bez zmian"
diff --git a/config/locales/views/simplefin_items/de.yml b/config/locales/views/simplefin_items/de.yml
index 9e6201489..105640fb8 100644
--- a/config/locales/views/simplefin_items/de.yml
+++ b/config/locales/views/simplefin_items/de.yml
@@ -30,8 +30,27 @@ de:
label: "SimpleFin-Setup-Token:"
placeholder: "Füge hier dein SimpleFin-Setup-Token ein..."
help_text: "Das Token sollte eine lange Zeichenfolge aus Buchstaben und Zahlen sein."
+ setup_accounts:
+ stale_accounts:
+ title: "Konten nicht mehr in SimpleFIN"
+ description: "Diese Konten existieren in deiner Datenbank, werden aber nicht mehr von SimpleFIN bereitgestellt. Das kann passieren, wenn sich Kontokonfigurationen beim Anbieter ändern."
+ action_prompt: "Was möchtest du tun?"
+ action_delete: "Konto und alle Transaktionen löschen"
+ action_move: "Transaktionen verschieben nach:"
+ action_skip: "Vorerst überspringen"
+ transaction_count:
+ one: "%{count} Transaktion"
+ other: "%{count} Transaktionen"
complete_account_setup:
- success: SimpleFin-Konten wurden erfolgreich eingerichtet! Deine Transaktionen und Positionen werden im Hintergrund importiert.
+ all_skipped: "Alle Konten wurden übersprungen. Es wurden keine Konten erstellt."
+ no_accounts: "Keine Konten zum Einrichten."
+ success:
+ one: "Ein SimpleFIN-Konto erfolgreich erstellt! Deine Transaktionen und Positionen werden im Hintergrund importiert."
+ other: "%{count} SimpleFIN-Konten erfolgreich erstellt! Deine Transaktionen und Positionen werden im Hintergrund importiert."
+ stale_accounts_processed: "Veraltete Konten: %{deleted} gelöscht, %{moved} verschoben."
+ stale_accounts_errors:
+ one: "%{count} Aktion für veraltetes Konto fehlgeschlagen. Details in den Logs prüfen."
+ other: "%{count} Aktionen für veraltete Konten fehlgeschlagen. Details in den Logs prüfen."
simplefin_item:
add_new: Neue Verbindung hinzufügen
confirm_accept: Verbindung löschen
@@ -46,8 +65,43 @@ de:
setup_needed: Neue Konten bereit zur Einrichtung
setup_description: Wähle die Kontotypen für deine neu importierten SimpleFin-Konten aus.
setup_action: Neue Konten einrichten
+ setup_accounts_menu: Konten einrichten
+ more_accounts_available:
+ one: "%{count} weiteres Konto kann eingerichtet werden"
+ other: "%{count} weitere Konten können eingerichtet werden"
+ accounts_skipped_tooltip: "Einige Konten wurden aufgrund von Fehlern bei der Synchronisierung übersprungen"
+ accounts_skipped_label: "Übersprungen: %{count}"
+ rate_limited_ago: "Ratenbegrenzung (vor %{time})"
+ rate_limited_recently: "Kürzlich ratenbegrenzt"
status: Zuletzt vor %{timestamp} synchronisiert
status_never: Noch nie synchronisiert
status_with_summary: "Zuletzt vor %{timestamp} synchronisiert • %{summary}"
syncing: Wird synchronisiert...
update: Verbindung aktualisieren
+ stale_pending_note: "(von Budgets ausgeschlossen)"
+ stale_pending_accounts: "in: %{accounts}"
+ reconciled_details_note: "(Details siehe Synchronisierungszusammenfassung)"
+ duplicate_accounts_skipped: "Einige Konten wurden als Duplikate übersprungen — nutze „Bestehendes Konto verknüpfen“, um sie zusammenzuführen."
+ select_existing_account:
+ title: "%{account_name} mit SimpleFIN verknüpfen"
+ description: Wähle ein SimpleFIN-Konto aus, das mit deinem bestehenden Konto verknüpft werden soll
+ cancel: Abbrechen
+ link_account: Konto verknüpfen
+ no_accounts_found: "Keine SimpleFIN-Konten für diesen %{moniker} gefunden."
+ wait_for_sync: Wenn du gerade verbunden oder synchronisiert hast, versuche es nach Abschluss der Synchronisierung erneut.
+ unlink_to_move: Um eine Verknüpfung zu verschieben, trenne sie zuerst im Aktionsmenü des Kontos.
+ all_accounts_already_linked: Alle SimpleFIN-Konten scheinen bereits verknüpft zu sein.
+ currently_linked_to: "Aktuell verknüpft mit: %{account_name}"
+ link_existing_account:
+ success: Konto erfolgreich mit SimpleFIN verknüpft
+ errors:
+ only_manual: Nur manuelle Konten können verknüpft werden
+ invalid_simplefin_account: Ungültiges SimpleFIN-Konto ausgewählt
+ reconciled_status:
+ message:
+ one: "%{count} doppelte ausstehende Transaktion abgeglichen"
+ other: "%{count} doppelte ausstehende Transaktionen abgeglichen"
+ stale_pending_status:
+ message:
+ one: "%{count} ausstehende Transaktion älter als %{days} Tage"
+ other: "%{count} ausstehende Transaktionen älter als %{days} Tage"
diff --git a/config/locales/views/simplefin_items/es.yml b/config/locales/views/simplefin_items/es.yml
index e36d3db59..f1755c82b 100644
--- a/config/locales/views/simplefin_items/es.yml
+++ b/config/locales/views/simplefin_items/es.yml
@@ -2,52 +2,106 @@
es:
simplefin_items:
new:
- title: Conectar SimpleFin
+ title: Conectar SimpleFIN
setup_token: Token de configuración
- setup_token_placeholder: pega tu token de configuración de SimpleFin
+ setup_token_placeholder: pega tu token de configuración de SimpleFIN
connect: Conectar
cancel: Cancelar
create:
- success: ¡Conexión SimpleFin añadida con éxito! Tus cuentas aparecerán en breve mientras se sincronizan en segundo plano.
+ success: ¡Conexión SimpleFIN añadida con éxito! Tus cuentas aparecerán en breve mientras se sincronizan en segundo plano.
errors:
- blank_token: Por favor, introduce un token de configuración de SimpleFin.
- invalid_token: Token de configuración inválido. Por favor, verifica que has copiado el token completo desde SimpleFin Bridge.
- token_compromised: El token de configuración puede estar comprometido, expirado o ya utilizado. Por favor, crea uno nuevo.
+ blank_token: Por favor, introduce un token de configuración de SimpleFIN.
+ invalid_token: Token de configuración no válido. Por favor, verifica que has copiado el token completo desde SimpleFIN Bridge.
+ token_compromised: El token de configuración puede estar comprometido, caducado o ya utilizado. Por favor, crea uno nuevo.
create_failed: "No se pudo conectar: %{message}"
- unexpected: Ocurrió un error inesperado. Por favor, inténtalo de nuevo o contacta con soporte.
+ unexpected: Ha ocurrido un error inesperado. Por favor, inténtalo de nuevo.
destroy:
- success: La conexión SimpleFin será eliminada.
+ success: La conexión SimpleFIN será eliminada.
update:
- success: ¡Conexión SimpleFin actualizada con éxito! Tus cuentas están siendo reconectadas.
+ success: ¡Conexión SimpleFIN actualizada con éxito! Tus cuentas se están reconectando.
errors:
- blank_token: Por favor, introduce un token de configuración de SimpleFin.
- invalid_token: Token de configuración inválido. Por favor, verifica que has copiado el token completo desde SimpleFin Bridge.
- token_compromised: El token de configuración puede estar comprometido, expirado o ya utilizado. Por favor, crea uno nuevo.
+ blank_token: Por favor, introduce un token de configuración de SimpleFIN.
+ invalid_token: Token de configuración no válido. Por favor, verifica que has copiado el token completo desde SimpleFIN Bridge.
+ token_compromised: El token de configuración puede estar comprometido, caducado o ya utilizado. Por favor, crea uno nuevo.
update_failed: "No se pudo actualizar la conexión: %{message}"
- unexpected: Ocurrió un error inesperado. Por favor, inténtalo de nuevo o contacta con soporte.
+ unexpected: Ha ocurrido un error inesperado. Por favor, inténtalo de nuevo.
edit:
setup_token:
- label: "Token de configuración de SimpleFin:"
- placeholder: "Pega aquí tu token de configuración de SimpleFin..."
- help_text: "El token debería ser una cadena larga que comienza con letras y números."
+ label: "Token de configuración de SimpleFIN:"
+ placeholder: "Pega aquí tu token de configuración de SimpleFIN..."
+ help_text: "El token debería ser una cadena larga que comienza con letras y números"
+ setup_accounts:
+ stale_accounts:
+ title: "Cuentas que ya no están en SimpleFIN"
+ description: "Estas cuentas existen en tu base de datos pero SimpleFIN ya no las proporciona. Esto puede ocurrir cuando cambian las configuraciones de origen."
+ action_prompt: "¿Qué te gustaría hacer?"
+ action_delete: "Eliminar cuenta y todas las transacciones"
+ action_move: "Mover transacciones a:"
+ action_skip: "Omitir por ahora"
+ transaction_count:
+ one: "%{count} transacción"
+ other: "%{count} transacciones"
complete_account_setup:
- success: ¡Las cuentas de SimpleFin se han configurado con éxito! Tus transacciones y activos se están importando en segundo plano.
+ all_skipped: "Se han omitido todas las cuentas. No se ha creado ninguna."
+ no_accounts: "No hay cuentas para configurar."
+ success:
+ one: "¡Se ha creado correctamente %{count} cuenta de SimpleFIN! Tus transacciones y posiciones se están importando en segundo plano."
+ other: "¡Se han creado correctamente %{count} cuentas de SimpleFIN! Tus transacciones y posiciones se están importando en segundo plano."
+ stale_accounts_processed: "Cuentas obsoletas: %{deleted} eliminadas, %{moved} movidas."
+ stale_accounts_errors:
+ one: "Error en la acción de %{count} cuenta obsoleta. Revisa los registros para más detalles."
+ other: "Error en las acciones de %{count} cuentas obsoletas. Revisa los registros para más detalles."
simplefin_item:
add_new: Añadir nueva conexión
confirm_accept: Eliminar conexión
confirm_body: Esto eliminará permanentemente todas las cuentas de este grupo y todos los datos asociados.
- confirm_title: ¿Eliminar conexión SimpleFin?
+ confirm_title: ¿Eliminar conexión SimpleFIN?
delete: Eliminar
- deletion_in_progress: "(eliminación en progreso...)"
- error: Ocurrió un error al sincronizar los datos
+ deletion_in_progress: "(eliminación en curso...)"
+ error: Ha ocurrido un error al sincronizar los datos
no_accounts_description: Esta conexión aún no tiene cuentas sincronizadas.
no_accounts_title: No se encontraron cuentas
- requires_update: Requiere reautenticación
+ requires_update: Reconectar
setup_needed: Nuevas cuentas listas para configurar
- setup_description: Elige los tipos de cuenta para tus nuevas cuentas importadas de SimpleFin.
+ setup_description: Elige los tipos de cuenta para tus nuevas cuentas importadas de SimpleFIN.
setup_action: Configurar nuevas cuentas
+ setup_accounts_menu: Configurar cuentas
+ more_accounts_available:
+ one: "Hay %{count} cuenta más disponible para configurar"
+ other: "Hay %{count} cuentas más disponibles para configurar"
+ accounts_skipped_tooltip: "Se omitieron algunas cuentas debido a errores durante la sincronización"
+ accounts_skipped_label: "Omitidas: %{count}"
+ rate_limited_ago: "Límite de frecuencia alcanzado (hace %{time})"
+ rate_limited_recently: "Límite de frecuencia alcanzado recientemente"
status: Última sincronización hace %{timestamp}
status_never: Nunca sincronizado
status_with_summary: "Última sincronización hace %{timestamp} • %{summary}"
syncing: Sincronizando...
- update: Actualizar conexión
\ No newline at end of file
+ update: Actualizar
+ stale_pending_note: "(excluido de presupuestos)"
+ stale_pending_accounts: "en: %{accounts}"
+ reconciled_details_note: "(ver resumen de sincronización para detalles)"
+ duplicate_accounts_skipped: "Se omitieron algunas cuentas por estar duplicadas — usa 'Vincular cuentas existentes' para fusionarlas."
+ select_existing_account:
+ title: "Vincular %{account_name} a SimpleFIN"
+ description: Selecciona una cuenta de SimpleFIN para vincularla a tu cuenta existente
+ cancel: Cancelar
+ link_account: Vincular cuenta
+ no_accounts_found: "No se han encontrado cuentas de SimpleFIN para este %{moniker}."
+ wait_for_sync: Si acabas de conectar o sincronizar, inténtalo de nuevo cuando finalice la sincronización.
+ unlink_to_move: Para mover un vínculo, primero desvincúlalo desde el menú de acciones de la cuenta.
+ all_accounts_already_linked: Todas las cuentas de SimpleFIN parecen estar ya vinculadas.
+ currently_linked_to: "Vinculada actualmente a: %{account_name}"
+ link_existing_account:
+ success: Cuenta vinculada correctamente a SimpleFIN
+ errors:
+ only_manual: Solo se pueden vincular cuentas manuales
+ invalid_simplefin_account: Se ha seleccionado una cuenta de SimpleFIN no válida
+ reconciled_status:
+ message:
+ one: "%{count} transacción pendiente duplicada conciliada"
+ other: "%{count} transacciones pendientes duplicadas conciliadas"
+ stale_pending_status:
+ message:
+ one: "%{count} transacción pendiente con más de %{days} días"
+ other: "%{count} transacciones pendientes con más de %{days} días"
\ No newline at end of file
diff --git a/config/locales/views/simplefin_items/pl.yml b/config/locales/views/simplefin_items/pl.yml
new file mode 100644
index 000000000..6a54341d3
--- /dev/null
+++ b/config/locales/views/simplefin_items/pl.yml
@@ -0,0 +1,119 @@
+---
+pl:
+ simplefin_items:
+ new:
+ title: Połącz SimpleFIN
+ setup_token: Token konfiguracji
+ setup_token_placeholder: wklej token konfiguracji SimpleFIN
+ connect: Połącz
+ cancel: Anuluj
+ create:
+ success: Połączenie SimpleFIN zostało pomyślnie dodane! Twoje konta pojawią się wkrótce po synchronizacji w tle.
+ errors:
+ blank_token: Wprowadź token konfiguracji SimpleFIN.
+ invalid_token: Nieprawidłowy token konfiguracji. Sprawdź, czy skopiowano pełny token z SimpleFIN Bridge.
+ token_compromised: Token konfiguracji może być naruszony, wygasły albo już użyty. Utwórz nowy.
+ create_failed: 'Nie udało się połączyć: %{message}'
+ unexpected: Wystąpił nieoczekiwany błąd. Spróbuj ponownie.
+ destroy:
+ success: Połączenie SimpleFIN zostanie usunięte
+ update:
+ success: Połączenie SimpleFIN zostało pomyślnie zaktualizowane! Trwa ponowne łączenie kont.
+ errors:
+ blank_token: Wprowadź token konfiguracji SimpleFIN.
+ invalid_token: Nieprawidłowy token konfiguracji. Sprawdź, czy skopiowano pełny token z SimpleFIN Bridge.
+ token_compromised: Token konfiguracji może być naruszony, wygasły albo już użyty. Utwórz nowy.
+ update_failed: 'Nie udało się zaktualizować połączenia: %{message}'
+ unexpected: Wystąpił nieoczekiwany błąd. Spróbuj ponownie.
+ edit:
+ setup_token:
+ label: 'Token konfiguracji SimpleFIN:'
+ placeholder: Wklej tutaj token konfiguracji SimpleFIN...
+ help_text: Token powinien być długim ciągiem znaków zaczynającym się od liter i cyfr
+ setup_accounts:
+ stale_accounts:
+ title: Konta, których nie ma już w SimpleFIN
+ description: Te konta istnieją w bazie danych, ale nie są już dostarczane przez SimpleFIN. Może się to zdarzyć przy zmianie konfiguracji kont po stronie dostawcy.
+ action_prompt: Co chcesz zrobić?
+ action_delete: Usuń konto i wszystkie transakcje
+ action_move: 'Przenieś transakcje do:'
+ action_skip: Pomiń na razie
+ transaction_count:
+ one: "%{count} transakcja"
+ few: "%{count} transakcje"
+ many: "%{count} transakcji"
+ other: "%{count} transakcji"
+ complete_account_setup:
+ all_skipped: Wszystkie konta zostały pominięte. Nie utworzono żadnych kont.
+ no_accounts: Brak kont do skonfigurowania.
+ success:
+ one: Pomyślnie utworzono %{count} konto SimpleFIN! Twoje transakcje i pozycje są importowane w tle.
+ few: Pomyślnie utworzono %{count} konta SimpleFIN! Twoje transakcje i pozycje są importowane w tle.
+ many: Pomyślnie utworzono %{count} kont SimpleFIN! Twoje transakcje i pozycje są importowane w tle.
+ other: Pomyślnie utworzono %{count} kont SimpleFIN! Twoje transakcje i pozycje są importowane w tle.
+ stale_accounts_processed: 'Przestarzałe konta: %{deleted} usuniętych, %{moved} przeniesionych.'
+ stale_accounts_errors:
+ one: "%{count} akcja dla przestarzałego konta nie powiodła się. Sprawdź logi."
+ few: "%{count} akcje dla przestarzałych kont nie powiodły się. Sprawdź logi."
+ many: "%{count} akcji dla przestarzałych kont nie powiodło się. Sprawdź logi."
+ other: "%{count} akcji dla przestarzałych kont nie powiodło się. Sprawdź logi."
+ simplefin_item:
+ add_new: Dodaj nowe połączenie
+ confirm_accept: Usuń połączenie
+ confirm_body: Spowoduje to trwałe usunięcie wszystkich kont w tej grupie i wszystkich powiązanych danych.
+ confirm_title: Usunąć połączenie SimpleFIN?
+ delete: Usuń
+ deletion_in_progress: "(usuwanie w toku...)"
+ error: Wystąpił błąd podczas synchronizacji danych
+ no_accounts_description: To połączenie nie ma jeszcze żadnych zsynchronizowanych kont.
+ no_accounts_title: Nie znaleziono kont
+ requires_update: Połącz ponownie
+ setup_needed: Nowe konta gotowe do konfiguracji
+ setup_description: Wybierz typy kont dla nowo zaimportowanych kont SimpleFIN.
+ setup_action: Skonfiguruj nowe konta
+ setup_accounts_menu: Skonfiguruj konta
+ more_accounts_available:
+ one: "%{count} dodatkowe konto do skonfigurowania"
+ few: "%{count} dodatkowe konta do skonfigurowania"
+ many: "%{count} dodatkowych kont do skonfigurowania"
+ other: "%{count} dodatkowych kont do skonfigurowania"
+ accounts_skipped_tooltip: Niektóre konta zostały pominięte z powodu błędów podczas synchronizacji
+ accounts_skipped_label: 'Pominięte: %{count}'
+ rate_limited_ago: Ograniczenie limitu (%{time} temu)
+ rate_limited_recently: Ostatnio ograniczono limitem
+ status: Ostatnia synchronizacja %{timestamp} temu
+ status_never: Nigdy nie synchronizowano
+ status_with_summary: Ostatnia synchronizacja %{timestamp} temu • %{summary}
+ syncing: Synchronizacja...
+ update: Aktualizuj
+ stale_pending_note: "(wykluczone z budżetów)"
+ stale_pending_accounts: 'w: %{accounts}'
+ reconciled_details_note: "(szczegóły w podsumowaniu synchronizacji)"
+ duplicate_accounts_skipped: Niektóre konta zostały pominięte jako duplikaty — użyj opcji „Połącz istniejące konta”, aby je scalić.
+ select_existing_account:
+ title: Połącz %{account_name} z SimpleFIN
+ description: Wybierz konto SimpleFIN do połączenia z istniejącym kontem
+ cancel: Anuluj
+ link_account: Połącz konto
+ no_accounts_found: Nie znaleziono kont SimpleFIN dla tej/tego %{moniker}.
+ wait_for_sync: Jeśli właśnie połączyłeś lub zsynchronizowałeś, spróbuj ponownie po zakończeniu synchronizacji.
+ unlink_to_move: Aby przenieść połączenie, najpierw odłącz je z menu akcji konta.
+ all_accounts_already_linked: Wszystkie konta SimpleFIN są już połączone.
+ currently_linked_to: 'Aktualnie połączone z: %{account_name}'
+ link_existing_account:
+ success: Konto zostało pomyślnie połączone z SimpleFIN
+ errors:
+ only_manual: Można połączyć tylko konta manualne
+ invalid_simplefin_account: Wybrano nieprawidłowe konto SimpleFIN
+ reconciled_status:
+ message:
+ one: "%{count} oczekująca zduplikowana transakcja została uzgodniona"
+ few: "%{count} oczekujące zduplikowane transakcje zostały uzgodnione"
+ many: "%{count} oczekujących zduplikowanych transakcji zostało uzgodnionych"
+ other: "%{count} oczekujących zduplikowanych transakcji zostało uzgodnionych"
+ stale_pending_status:
+ message:
+ one: "%{count} oczekująca transakcja starsza niż %{days} dni"
+ few: "%{count} oczekujące transakcje starsze niż %{days} dni"
+ many: "%{count} oczekujących transakcji starszych niż %{days} dni"
+ other: "%{count} oczekujących transakcji starszych niż %{days} dni"
diff --git a/config/locales/views/snaptrade_items/de.yml b/config/locales/views/snaptrade_items/de.yml
new file mode 100644
index 000000000..c0f0cd22c
--- /dev/null
+++ b/config/locales/views/snaptrade_items/de.yml
@@ -0,0 +1,190 @@
+---
+de:
+ snaptrade_items:
+ default_name: "SnapTrade-Verbindung"
+ create:
+ success: "SnapTrade wurde erfolgreich eingerichtet."
+ update:
+ success: "SnapTrade-Konfiguration wurde erfolgreich aktualisiert."
+ destroy:
+ success: "SnapTrade-Verbindung wurde zur Löschung vorgemerkt."
+ connect:
+ decryption_failed: "SnapTrade-Zugangsdaten konnten nicht gelesen werden. Bitte löschen Sie die Verbindung und legen Sie sie neu an."
+ connection_failed: "Verbindung zu SnapTrade fehlgeschlagen: %{message}"
+ callback:
+ success: "Broker verbunden! Bitte wählen Sie die zu verknüpfenden Konten."
+ no_item: "SnapTrade-Konfiguration nicht gefunden."
+ complete_account_setup:
+ success:
+ one: "%{count} Konto erfolgreich verknüpft."
+ other: "%{count} Konten erfolgreich verknüpft."
+ partial_success:
+ one: "%{count} Konto verknüpft. %{failed_count} Verknüpfung(en) fehlgeschlagen."
+ other: "%{count} Konten verknüpft. %{failed_count} Verknüpfung(en) fehlgeschlagen."
+ link_failed: "Konten konnten nicht verknüpft werden: %{errors}"
+ no_accounts: "Es wurden keine Konten zur Verknüpfung ausgewählt."
+ preload_accounts:
+ not_configured: "SnapTrade ist nicht konfiguriert."
+ select_accounts:
+ not_configured: "SnapTrade ist nicht konfiguriert."
+ select_existing_account:
+ not_found: "Konto oder SnapTrade-Konfiguration nicht gefunden."
+ title: "Mit SnapTrade-Konto verknüpfen"
+ header: "Bestehendes Konto verknüpfen"
+ subtitle: "Wählen Sie ein SnapTrade-Konto zur Verknüpfung"
+ no_accounts: "Keine unverknüpften SnapTrade-Konten verfügbar."
+ connect_hint: "Möglicherweise müssen Sie zuerst einen Broker verbinden."
+ settings_link: "Zu den Provider-Einstellungen"
+ linking_to: "Verknüpfe mit Konto:"
+ balance_label: "Saldo:"
+ link_button: "Verknüpfen"
+ cancel_button: "Abbrechen"
+ link_existing_account:
+ success: "Erfolgreich mit SnapTrade-Konto verknüpft."
+ failed: "Verknüpfung fehlgeschlagen: %{message}"
+ not_found: "Konto nicht gefunden."
+ connections:
+ unknown_brokerage: "Unbekannter Broker"
+ delete_connection:
+ success: "Verbindung erfolgreich gelöscht. Ein Platz ist frei."
+ failed: "Löschen der Verbindung fehlgeschlagen: %{message}"
+ missing_authorization_id: "Autorisierungs-ID fehlt"
+ api_deletion_failed: "Verbindung konnte bei SnapTrade nicht gelöscht werden – Zugangsdaten fehlen. Die Verbindung kann in Ihrem SnapTrade-Konto noch existieren."
+ delete_orphaned_user:
+ success: "Verwaiste Registrierung wurde erfolgreich gelöscht."
+ failed: "Löschen der verwaisten Registrierung fehlgeschlagen."
+ setup_accounts:
+ title: "SnapTrade-Konten einrichten"
+ header: "SnapTrade-Konten einrichten"
+ subtitle: "Wählen Sie die zu verknüpfenden Broker-Konten"
+ syncing: "Ihre Konten werden abgerufen..."
+ loading: "Konten werden von SnapTrade geladen..."
+ loading_hint: "Klicken Sie auf Aktualisieren, um nach Konten zu suchen."
+ refresh: "Aktualisieren"
+ info_title: "SnapTrade-Anlagedaten"
+ info_holdings: "Bestände mit aktuellen Preisen und Mengen"
+ info_cost_basis: "Einstandskosten pro Position (falls verfügbar)"
+ info_activities: "Handelshistorie mit Aktivitätslabels (Kaufen, Verkaufen, Dividende usw.)"
+ info_history: "Bis zu 3 Jahre Buchungshistorie"
+ free_tier_note: "SnapTrade Free Tier erlaubt 5 Broker-Verbindungen. Nutzung im SnapTrade-Dashboard prüfen."
+ no_accounts_title: "Keine Konten gefunden"
+ no_accounts_message: "Es wurden keine Broker-Konten gefunden. Das kann passieren, wenn Sie die Verbindung abgebrochen haben oder Ihr Broker nicht unterstützt wird."
+ try_again: "Broker verbinden"
+ back_to_settings: "Zurück zu Einstellungen"
+ available_accounts: "Verfügbare Konten"
+ balance_label: "Saldo:"
+ account_number: "Konto:"
+ create_button: "Ausgewählte Konten anlegen"
+ cancel_button: "Abbrechen"
+ creating: "Konten werden angelegt..."
+ done_button: "Fertig"
+ or_link_existing: "Oder mit einem bestehenden Konto verknüpfen statt neu anlegen:"
+ select_account: "Konto auswählen..."
+ link_button: "Verknüpfen"
+ linked_accounts: "Bereits verknüpft"
+ linked_to: "Verknüpft mit:"
+ snaptrade_item:
+ accounts_need_setup:
+ one: "%{count} Konto muss eingerichtet werden"
+ other: "%{count} Konten müssen eingerichtet werden"
+ deletion_in_progress: "Löschung läuft..."
+ syncing: "Synchronisiere..."
+ requires_update: "Verbindung muss aktualisiert werden"
+ error: "Sync-Fehler"
+ status: "Zuletzt synchronisiert vor %{timestamp} – %{summary}"
+ status_never: "Noch nie synchronisiert"
+ reconnect: "Erneut verbinden"
+ connect_brokerage: "Broker verbinden"
+ add_another_brokerage: "Weiteren Broker verbinden"
+ delete: "Löschen"
+ setup_needed: "Konten müssen eingerichtet werden"
+ setup_description: "Einige Konten von SnapTrade müssen Sure-Konten zugeordnet werden."
+ setup_action: "Konten einrichten"
+ setup_accounts_menu: "Konten einrichten"
+ manage_connections: "Verbindungen verwalten"
+ more_accounts_available:
+ one: "%{count} weiteres Konto kann eingerichtet werden"
+ other: "%{count} weitere Konten können eingerichtet werden"
+ no_accounts_title: "Keine Konten gefunden"
+ no_accounts_description: "Verbinden Sie einen Broker, um Ihre Anlagekonten zu importieren."
+
+ providers:
+ snaptrade:
+ name: "SnapTrade"
+ connection_description: "Verbinden Sie Ihren Broker über SnapTrade (25+ Broker unterstützt)"
+ description: "SnapTrade verbindet mit 25+ großen Brokern (Fidelity, Vanguard, Schwab, Robinhood usw.) und liefert vollständige Handelshistorie mit Aktivitätslabels und Einstandskosten."
+ setup_title: "Einrichtungsanleitung:"
+ step_1_html: "Konto erstellen unter dashboard.snaptrade.com"
+ step_2: "Client ID und Consumer Key aus dem Dashboard kopieren"
+ step_3: "Zugangsdaten unten eintragen und auf Speichern klicken"
+ step_4: "Auf die Konten-Seite gehen und „Weiteren Broker verbinden“ nutzen, um Ihre Anlagekonten zu verknüpfen"
+ free_tier_warning: "Free Tier enthält 5 Broker-Verbindungen. Weitere erfordern einen kostenpflichtigen SnapTrade-Plan."
+ client_id_label: "Client ID"
+ client_id_placeholder: "SnapTrade Client ID eingeben"
+ client_id_update_placeholder: "Neue Client ID zum Aktualisieren eingeben"
+ consumer_key_label: "Consumer Key"
+ consumer_key_placeholder: "SnapTrade Consumer Key eingeben"
+ consumer_key_update_placeholder: "Neuen Consumer Key zum Aktualisieren eingeben"
+ save_button: "Konfiguration speichern"
+ update_button: "Konfiguration aktualisieren"
+ status_connected:
+ one: "%{count} Konto von SnapTrade"
+ other: "%{count} Konten von SnapTrade"
+ needs_setup:
+ one: "%{count} muss eingerichtet werden"
+ other: "%{count} müssen eingerichtet werden"
+ status_ready: "Bereit zum Verbinden von Brokern"
+ status_needs_registration: "Zugangsdaten gespeichert. Gehen Sie zur Konten-Seite, um Broker zu verbinden."
+ status_not_configured: "Nicht konfiguriert"
+ setup_accounts_button: "Konten einrichten"
+ connect_button: "Broker verbinden"
+ connected_brokerages: "Verbunden:"
+ manage_connections: "Verbindungen verwalten"
+ connection_limit_info: "SnapTrade Free Tier erlaubt 5 Broker-Verbindungen. Löschen Sie ungenutzte Verbindungen, um Plätze freizugeben."
+ loading_connections: "Verbindungen werden geladen..."
+ connections_error: "Verbindungen konnten nicht geladen werden: %{message}"
+ accounts_count:
+ one: "%{count} Konto"
+ other: "%{count} Konten"
+ orphaned_connection: "Verwaiste Verbindung (lokal nicht synchronisiert)"
+ needs_linking: "muss verknüpft werden"
+ no_connections: "Keine Broker-Verbindungen gefunden."
+ delete_connection: "Löschen"
+ delete_connection_title: "Broker-Verbindung löschen?"
+ delete_connection_body: "Die Verbindung zu %{brokerage} wird dauerhaft von SnapTrade entfernt. Alle Konten dieses Brokers werden getrennt. Zum erneuten Sync müssen Sie sich wieder verbinden."
+ delete_connection_confirm: "Verbindung löschen"
+ orphaned_users_title:
+ one: "%{count} verwaiste Registrierung"
+ other: "%{count} verwaiste Registrierungen"
+ orphaned_users_description: "Das sind frühere SnapTrade-Registrierungen, die Ihre Verbindungsplätze belegen. Löschen Sie sie, um Plätze freizugeben."
+ orphaned_user: "Verwaiste Registrierung"
+ delete_orphaned_user: "Löschen"
+ delete_orphaned_user_title: "Verwaiste Registrierung löschen?"
+ delete_orphaned_user_body: "Diese verwaiste SnapTrade-Registrierung und alle zugehörigen Broker-Verbindungen werden dauerhaft gelöscht, Verbindungsplätze werden frei."
+ delete_orphaned_user_confirm: "Registrierung löschen"
+
+ snaptrade_item:
+ sync_status:
+ no_accounts: "Keine Konten gefunden"
+ synced:
+ one: "%{count} Konto synchronisiert"
+ other: "%{count} Konten synchronisiert"
+ synced_with_setup: "%{linked} synchronisiert, %{unlinked} müssen eingerichtet werden"
+ institution_summary:
+ none: "Keine Institute verbunden"
+ count:
+ one: "%{count} Institut"
+ other: "%{count} Institute"
+ brokerage_summary:
+ none: "Keine Broker verbunden"
+ count:
+ one: "%{count} Broker"
+ other: "%{count} Broker"
+ syncer:
+ discovering: "Konten werden ermittelt..."
+ importing: "Konten werden von SnapTrade importiert..."
+ processing: "Bestände und Aktivitäten werden verarbeitet..."
+ calculating: "Salden werden berechnet..."
+ checking_config: "Kontokonfiguration wird geprüft..."
+ needs_setup: "%{count} Konten müssen eingerichtet werden..."
+ activities_fetching_async: "Aktivitäten werden im Hintergrund geladen. Bei neuen Broker-Verbindungen kann das bis zu einer Minute dauern."
diff --git a/config/locales/views/snaptrade_items/en.yml b/config/locales/views/snaptrade_items/en.yml
index 63b70a28c..e9cdfd250 100644
--- a/config/locales/views/snaptrade_items/en.yml
+++ b/config/locales/views/snaptrade_items/en.yml
@@ -17,7 +17,9 @@ en:
success:
one: "Successfully linked %{count} account."
other: "Successfully linked %{count} accounts."
- partial_success: "Linked %{linked} account(s). %{failed} failed to link."
+ partial_success:
+ one: "Linked %{count} account. %{failed_count} failed to link."
+ other: "Linked %{count} accounts. %{failed_count} failed to link."
link_failed: "Failed to link accounts: %{errors}"
no_accounts: "No accounts were selected for linking."
preload_accounts:
diff --git a/config/locales/views/snaptrade_items/es.yml b/config/locales/views/snaptrade_items/es.yml
new file mode 100644
index 000000000..47a6ca609
--- /dev/null
+++ b/config/locales/views/snaptrade_items/es.yml
@@ -0,0 +1,190 @@
+---
+es:
+ snaptrade_items:
+ default_name: "Conexión de SnapTrade"
+ create:
+ success: "SnapTrade configurado correctamente."
+ update:
+ success: "Configuración de SnapTrade actualizada correctamente."
+ destroy:
+ success: "Conexión de SnapTrade programada para su eliminación."
+ connect:
+ decryption_failed: "No se han podido leer las credenciales de SnapTrade. Por favor, elimina y vuelve a crear esta conexión."
+ connection_failed: "Error al conectar con SnapTrade: %{message}"
+ callback:
+ success: "¡Bróker conectado! Por favor, selecciona qué cuentas quieres vincular."
+ no_item: "No se ha encontrado la configuración de SnapTrade."
+ complete_account_setup:
+ success:
+ one: "Se ha vinculado %{count} cuenta correctamente."
+ other: "Se han vinculado %{count} cuentas correctamente."
+ partial_success:
+ one: "Se ha vinculado %{count} cuenta. %{failed_count} ha fallado."
+ other: "Se han vinculado %{count} cuentas. %{failed_count} han fallado."
+ link_failed: "Error al vincular las cuentas: %{errors}"
+ no_accounts: "No se ha seleccionado ninguna cuenta para vincular."
+ preload_accounts:
+ not_configured: "SnapTrade no está configurado."
+ select_accounts:
+ not_configured: "SnapTrade no está configurado."
+ select_existing_account:
+ not_found: "No se ha encontrado la cuenta o la configuración de SnapTrade."
+ title: "Vincular a cuenta de SnapTrade"
+ header: "Vincular cuenta existente"
+ subtitle: "Selecciona una cuenta de SnapTrade para realizar la vinculación"
+ no_accounts: "No hay cuentas de SnapTrade disponibles sin vincular."
+ connect_hint: "Es posible que primero debas conectar un bróker."
+ settings_link: "Ir a Ajustes del proveedor"
+ linking_to: "Vinculando a la cuenta:"
+ balance_label: "Saldo:"
+ link_button: "Vincular"
+ cancel_button: "Cancelar"
+ link_existing_account:
+ success: "Vinculado correctamente a la cuenta de SnapTrade."
+ failed: "Error al vincular la cuenta: %{message}"
+ not_found: "Cuenta no encontrada."
+ connections:
+ unknown_brokerage: "Bróker desconocido"
+ delete_connection:
+ success: "Conexión eliminada correctamente. Se ha liberado un espacio."
+ failed: "Error al eliminar la conexión: %{message}"
+ missing_authorization_id: "Falta el ID de autorización"
+ api_deletion_failed: "No se pudo eliminar la conexión de SnapTrade por falta de credenciales. Es posible que la conexión aún exista en tu cuenta de SnapTrade."
+ delete_orphaned_user:
+ success: "Registro huérfano eliminado correctamente."
+ failed: "Error al eliminar el registro huérfano."
+ setup_accounts:
+ title: "Configurar cuentas de SnapTrade"
+ header: "Configura tus cuentas de SnapTrade"
+ subtitle: "Selecciona qué cuentas de bróker quieres vincular"
+ syncing: "Obteniendo tus cuentas..."
+ loading: "Obteniendo cuentas de SnapTrade..."
+ loading_hint: "Haz clic en Actualizar para buscar cuentas."
+ refresh: "Actualizar"
+ info_title: "Datos de inversión de SnapTrade"
+ info_holdings: "Posiciones con precios y cantidades actuales"
+ info_cost_basis: "Base de costes por posición (cuando esté disponible)"
+ info_activities: "Historial de operaciones con etiquetas de actividad (Compra, Venta, Dividendo, etc.)"
+ info_history: "Hasta 3 años de historial de transacciones"
+ free_tier_note: "El nivel gratuito de SnapTrade permite 5 conexiones de bróker. Consulta tu panel de SnapTrade para ver el uso actual."
+ no_accounts_title: "No se han encontrado cuentas"
+ no_accounts_message: "No se han encontrado cuentas de bróker. Esto puede ocurrir si cancelaste la conexión o si tu bróker no es compatible."
+ try_again: "Conectar bróker"
+ back_to_settings: "Volver a Ajustes"
+ available_accounts: "Cuentas disponibles"
+ balance_label: "Saldo:"
+ account_number: "Cuenta:"
+ create_button: "Crear cuentas seleccionadas"
+ cancel_button: "Cancelar"
+ creating: "Creando cuentas..."
+ done_button: "Listo"
+ or_link_existing: "O vincula a una cuenta existente en lugar de crear una nueva:"
+ select_account: "Selecciona una cuenta..."
+ link_button: "Vincular"
+ linked_accounts: "Ya vinculadas"
+ linked_to: "Vinculada a:"
+ snaptrade_item:
+ accounts_need_setup:
+ one: "%{count} cuenta necesita configuración"
+ other: "%{count} cuentas necesitan configuración"
+ deletion_in_progress: "Eliminación en curso..."
+ syncing: "Sincronizando..."
+ requires_update: "La conexión necesita una actualización"
+ error: "Error de sincronización"
+ status: "Sincronizado hace %{timestamp} - %{summary}"
+ status_never: "Nunca sincronizado"
+ reconnect: "Reconectar"
+ connect_brokerage: "Conectar bróker"
+ add_another_brokerage: "Conectar otro bróker"
+ delete: "Eliminar"
+ setup_needed: "Las cuentas necesitan configuración"
+ setup_description: "Algunas cuentas de SnapTrade deben vincularse a cuentas de Sure."
+ setup_action: "Configurar cuentas"
+ setup_accounts_menu: "Configurar cuentas"
+ manage_connections: "Gestionar conexiones"
+ more_accounts_available:
+ one: "Hay %{count} cuenta más disponible para configurar"
+ other: "Hay %{count} cuentas más disponibles para configurar"
+ no_accounts_title: "No se han detectado cuentas"
+ no_accounts_description: "Conecta un bróker para importar tus cuentas de inversión."
+
+ providers:
+ snaptrade:
+ name: "SnapTrade"
+ connection_description: "Conecta con tu bróker a través de SnapTrade (más de 25 brókers compatibles)"
+ description: "SnapTrade conecta con más de 25 brókers principales (Fidelity, Vanguard, Schwab, Robinhood, etc.) y proporciona un historial completo de operaciones con etiquetas de actividad y base de costes."
+ setup_title: "Instrucciones de configuración:"
+ step_1_html: "Crea una cuenta en dashboard.snaptrade.com"
+ step_2: "Copia tu Client ID y tu Consumer Key desde el panel"
+ step_3: "Introduce tus credenciales a continuación y haz clic en Guardar"
+ step_4: "Ve a la página de Cuentas y usa 'Conectar otro bróker' para vincular tus cuentas de inversión"
+ free_tier_warning: "El nivel gratuito incluye 5 conexiones de bróker. Las conexiones adicionales requieren un plan de pago de SnapTrade."
+ client_id_label: "Client ID"
+ client_id_placeholder: "Introduce tu Client ID de SnapTrade"
+ client_id_update_placeholder: "Introduce el nuevo Client ID para actualizar"
+ consumer_key_label: "Consumer Key"
+ consumer_key_placeholder: "Introduce tu Consumer Key de SnapTrade"
+ consumer_key_update_placeholder: "Introduce la nueva Consumer Key para actualizar"
+ save_button: "Guardar configuración"
+ update_button: "Actualizar configuración"
+ status_connected:
+ one: "%{count} cuenta de SnapTrade"
+ other: "%{count} cuentas de SnapTrade"
+ needs_setup:
+ one: "%{count} necesita configuración"
+ other: "%{count} necesitan configuración"
+ status_ready: "Listo para conectar brókers"
+ status_needs_registration: "Credenciales guardadas. Ve a la página de Cuentas para conectar brókers."
+ status_not_configured: "No configurado"
+ setup_accounts_button: "Configurar cuentas"
+ connect_button: "Conectar bróker"
+ connected_brokerages: "Conectados:"
+ manage_connections: "Gestionar conexiones"
+ connection_limit_info: "El nivel gratuito de SnapTrade permite 5 conexiones de bróker. Elimina conexiones sin usar para liberar espacios."
+ loading_connections: "Cargando conexiones..."
+ connections_error: "Error al cargar las conexiones: %{message}"
+ accounts_count:
+ one: "%{count} cuenta"
+ other: "%{count} cuentas"
+ orphaned_connection: "Conexión huérfana (no sincronizada localmente)"
+ needs_linking: "necesita vincularse"
+ no_connections: "No se han encontrado conexiones de bróker."
+ delete_connection: "Eliminar"
+ delete_connection_title: "¿Eliminar conexión del bróker?"
+ delete_connection_body: "Esto eliminará permanentemente la conexión de %{brokerage} de SnapTrade. Todas las cuentas de este bróker se desvincularán. Deberás volver a conectar para sincronizar estas cuentas de nuevo."
+ delete_connection_confirm: "Eliminar conexión"
+ orphaned_users_title:
+ one: "%{count} registro huérfano"
+ other: "%{count} registros huérfanos"
+ orphaned_users_description: "Estos son registros de usuario de SnapTrade anteriores que están ocupando tus espacios de conexión. Elimínalos para liberar espacio."
+ orphaned_user: "Registro huérfano"
+ delete_orphaned_user: "Eliminar"
+ delete_orphaned_user_title: "¿Eliminar registro huérfano?"
+ delete_orphaned_user_body: "Esto eliminará permanentemente este usuario de SnapTrade huérfano y todas sus conexiones de bróker, liberando espacios de conexión."
+ delete_orphaned_user_confirm: "Eliminar registro"
+
+ snaptrade_item:
+ sync_status:
+ no_accounts: "No se han encontrado cuentas"
+ synced:
+ one: "%{count} cuenta sincronizada"
+ other: "%{count} cuentas sincronizadas"
+ synced_with_setup: "%{linked} sincronizadas, %{unlinked} necesitan configuración"
+ institution_summary:
+ none: "No hay instituciones conectadas"
+ count:
+ one: "%{count} institución"
+ other: "%{count} instituciones"
+ brokerage_summary:
+ none: "No hay brókers conectados"
+ count:
+ one: "%{count} bróker"
+ other: "%{count} brókers"
+ syncer:
+ discovering: "Detectando cuentas..."
+ importing: "Importando cuentas de SnapTrade..."
+ processing: "Procesando posiciones y actividades..."
+ calculating: "Calculando saldos..."
+ checking_config: "Comprobando la configuración de la cuenta..."
+ needs_setup: "%{count} cuentas necesitan configuración..."
+ activities_fetching_async: "Las actividades se están obteniendo en segundo plano. Esto puede tardar hasta un minuto para conexiones de bróker recientes."
\ No newline at end of file
diff --git a/config/locales/views/snaptrade_items/pl.yml b/config/locales/views/snaptrade_items/pl.yml
new file mode 100644
index 000000000..ba3eb7058
--- /dev/null
+++ b/config/locales/views/snaptrade_items/pl.yml
@@ -0,0 +1,210 @@
+---
+pl:
+ snaptrade_items:
+ default_name: Połączenie SnapTrade
+ create:
+ success: Pomyślnie skonfigurowano SnapTrade.
+ update:
+ success: Pomyślnie zaktualizowano konfigurację SnapTrade.
+ destroy:
+ success: Zaplanowano usunięcie połączenia SnapTrade.
+ connect:
+ decryption_failed: Nie można odczytać danych uwierzytelniających SnapTrade. Usuń i utwórz ponownie to połączenie.
+ connection_failed: 'Nie udało się połączyć z SnapTrade: %{message}'
+ callback:
+ success: Biuro maklerskie zostało połączone! Wybierz konta do połączenia.
+ no_item: Nie znaleziono konfiguracji SnapTrade.
+ complete_account_setup:
+ success:
+ one: Pomyślnie połączono %{count} konto.
+ few: Pomyślnie połączono %{count} konta.
+ many: Pomyślnie połączono %{count} kont.
+ other: Pomyślnie połączono %{count} konta.
+ partial_success:
+ one: "Połączono %{count} konto. Nie udało się połączyć %{failed_count}."
+ few: "Połączono %{count} konta. Nie udało się połączyć %{failed_count}."
+ many: "Połączono %{count} kont. Nie udało się połączyć %{failed_count}."
+ other: "Połączono %{count} konta. Nie udało się połączyć %{failed_count}."
+ link_failed: 'Nie udało się połączyć kont: %{errors}'
+ no_accounts: Nie wybrano żadnych kont do połączenia.
+ preload_accounts:
+ not_configured: SnapTrade nie jest skonfigurowane.
+ select_accounts:
+ not_configured: SnapTrade nie jest skonfigurowane.
+ select_existing_account:
+ not_found: Konto lub konfiguracja SnapTrade nie zostało znalezione.
+ title: Połącz z kontem SnapTrade
+ header: Połącz istniejące konto
+ subtitle: Wybierz konto SnapTrade do połączenia
+ no_accounts: Brak niepołączonych kont SnapTrade.
+ connect_hint: Może najpierw musisz połączyć biuro maklerskie.
+ settings_link: Przejdź do ustawień dostawcy
+ linking_to: 'Łączenie z kontem:'
+ balance_label: 'Saldo:'
+ link_button: Połącz
+ cancel_button: Anuluj
+ link_existing_account:
+ success: Pomyślnie połączono z kontem SnapTrade.
+ failed: 'Nie udało się połączyć konta: %{message}'
+ not_found: Konto nie zostało znalezione.
+ connections:
+ unknown_brokerage: Nieznane biuro maklerskie
+ delete_connection:
+ success: Połączenie zostało pomyślnie usunięte. Zwolniono jedno miejsce.
+ failed: 'Nie udało się usunąć połączenia: %{message}'
+ missing_authorization_id: Brak identyfikatora autoryzacji
+ api_deletion_failed: Nie można usunąć połączenia z SnapTrade — brak danych uwierzytelniających. Połączenie może nadal istnieć na koncie SnapTrade.
+ delete_orphaned_user:
+ success: Osierocona rejestracja została usunięta pomyślnie.
+ failed: Nie udało się usunąć osieroconej rejestracji.
+ setup_accounts:
+ title: Skonfiguruj konta SnapTrade
+ header: Skonfiguruj swoje konta SnapTrade
+ subtitle: Wybierz konta maklerskie do połączenia
+ syncing: Pobieranie kont...
+ loading: Pobieranie kont z SnapTrade...
+ loading_hint: Kliknij Odśwież, aby sprawdzić konta.
+ refresh: Odśwież
+ info_title: Dane inwestycyjne SnapTrade
+ info_holdings: Aktywa z aktualnymi cenami i ilościami
+ info_cost_basis: Podstawa kosztowa na pozycję (gdy dostępna)
+ info_activities: Historia transakcji z etykietami aktywności (Kupno, Sprzedaż, Dywidenda itp.)
+ info_history: Do 3 lat historii transakcji
+ free_tier_note: Plan bezpłatny SnapTrade umożliwia 5 połączeń z biurami maklerskimi. Sprawdź swój pulpit SnapTrade, aby zobaczyć bieżące użycie.
+ no_accounts_title: Nie znaleziono kont
+ no_accounts_message: Nie znaleziono kont maklerskich. Może to nastąpić, jeśli anulowałeś połączenie lub Twoje biuro maklerskie nie jest obsługiwane.
+ try_again: Połącz biuro maklerskie
+ back_to_settings: Powrót do ustawień
+ available_accounts: Dostępne konta
+ balance_label: 'Saldo:'
+ account_number: 'Konto:'
+ create_button: Utwórz wybrane konta
+ cancel_button: Anuluj
+ creating: Tworzenie kont...
+ done_button: Gotowe
+ or_link_existing: 'Lub połącz z istniejącym kontem zamiast tworzyć nowe:'
+ select_account: Wybierz konto...
+ link_button: Połącz
+ linked_accounts: Już połączone
+ linked_to: 'Połączono z:'
+ snaptrade_item:
+ accounts_need_setup:
+ one: "%{count} konto wymaga konfiguracji"
+ few: "%{count} konta wymagają konfiguracji"
+ many: "%{count} kont wymaga konfiguracji"
+ other: "%{count} kont wymaga konfiguracji"
+ deletion_in_progress: Usuwanie w toku...
+ syncing: Synchronizacja...
+ requires_update: Połączenie wymaga aktualizacji
+ error: Błąd synchronizacji
+ status: Ostatnia synchronizacja %{timestamp} temu — %{summary}
+ status_never: Nigdy nie synchronizowano
+ reconnect: Połącz ponownie
+ connect_brokerage: Połącz biuro maklerskie
+ add_another_brokerage: Połącz kolejne biuro maklerskie
+ delete: Usuń
+ setup_needed: Konta wymagają konfiguracji
+ setup_description: Niektóre konta z SnapTrade muszą zostać połączone z kontami Sure.
+ setup_action: Konfiguruj konta
+ setup_accounts_menu: Skonfiguruj konta
+ manage_connections: Zarządzaj połączeniami
+ more_accounts_available:
+ one: "%{count} dodatkowe konto dostępne do konfiguracji"
+ few: "%{count} dodatkowe konta dostępne do konfiguracji"
+ many: "%{count} dodatkowych kont dostępnych do konfiguracji"
+ other: "%{count} dodatkowych kont dostępnych do konfiguracji"
+ no_accounts_title: Nie wykryto kont
+ no_accounts_description: Połącz biuro maklerskie, aby zaimportować swoje konta inwestycyjne.
+ providers:
+ snaptrade:
+ name: SnapTrade
+ connection_description: Połącz się ze swoim biurem maklerskim przez SnapTrade (obsługuje 25+ brokerów)
+ description: SnapTrade łączy się z ponad 25 głównymi biurami maklerskimi (Fidelity, Vanguard, Schwab, Robinhood itp.) i udostępnia pełną historię transakcji z etykietami aktywności oraz podstawą kosztową.
+ setup_title: 'Instrukcje konfiguracji:'
+ step_1_html: Utwórz konto na dashboard.snaptrade.com
+ step_2: Skopiuj swój Client ID i Consumer Key z pulpitu nawigacyjnego
+ step_3: Wprowadź swoje dane uwierzytelniające poniżej i kliknij Zapisz
+ step_4: Przejdź do strony Konta i użyj opcji „Połącz kolejne biuro maklerskie", aby połączyć swoje konta inwestycyjne
+ free_tier_warning: Plan bezpłatny obejmuje 5 połączeń z biurami maklerskimi. Dodatkowe połączenia wymagają płatnego planu SnapTrade.
+ client_id_label: Client ID
+ client_id_placeholder: Wprowadź Client ID SnapTrade
+ client_id_update_placeholder: Wprowadź nowy Client ID, aby zaktualizować
+ consumer_key_label: Consumer Key
+ consumer_key_placeholder: Wprowadź Consumer Key SnapTrade
+ consumer_key_update_placeholder: Wprowadź nowy Consumer Key, aby zaktualizować
+ save_button: Zapisz konfigurację
+ update_button: Zaktualizuj konfigurację
+ status_connected:
+ one: "%{count} konto z SnapTrade"
+ few: "%{count} konta z SnapTrade"
+ many: "%{count} kont z SnapTrade"
+ other: "%{count} konta z SnapTrade"
+ needs_setup:
+ one: "%{count} wymaga konfiguracji"
+ few: "%{count} wymagają konfiguracji"
+ many: "%{count} wymaga konfiguracji"
+ other: "%{count} wymaga konfiguracji"
+ status_ready: Gotowe do połączenia z biurami maklerskimi
+ status_needs_registration: Dane uwierzytelniające zapisane. Przejdź do strony Konta, aby połączyć biura maklerskie.
+ status_not_configured: Nieskonfigurowane
+ setup_accounts_button: Konfiguruj konta
+ connect_button: Połącz biuro maklerskie
+ connected_brokerages: 'Połączone:'
+ manage_connections: Zarządzaj połączeniami
+ connection_limit_info: Plan bezpłatny SnapTrade umożliwia 5 połączeń z biurami maklerskimi. Usuń nieużywane połączenia, aby zwolnić miejsca.
+ loading_connections: Ładowanie połączeń...
+ connections_error: 'Nie udało się załadować połączeń: %{message}'
+ accounts_count:
+ one: "%{count} konto"
+ few: "%{count} konta"
+ many: "%{count} kont"
+ other: "%{count} kont"
+ orphaned_connection: Osierocone połączenie (niezsynchronizowane lokalnie)
+ needs_linking: wymaga połączenia
+ no_connections: Nie znaleziono połączeń z biurami maklerskimi.
+ delete_connection: Usuń
+ delete_connection_title: Usunąć połączenie z biurem maklerskim?
+ delete_connection_body: Spowoduje to trwałe usunięcie połączenia %{brokerage} z SnapTrade. Wszystkie konta tego biura maklerskiego zostaną odłączone. Będziesz musiał ponownie się połączyć, aby synchronizować te konta.
+ delete_connection_confirm: Usuń połączenie
+ orphaned_users_title:
+ one: "%{count} osierocona rejestracja"
+ few: "%{count} osierocone rejestracje"
+ many: "%{count} osieroconych rejestracji"
+ other: "%{count} osieroconych rejestracji"
+ orphaned_users_description: Są to poprzednie rejestracje użytkowników SnapTrade, które zajmują Twoje miejsca połączeń. Usuń je, aby zwolnić miejsca.
+ orphaned_user: Osierocona rejestracja
+ delete_orphaned_user: Usuń
+ delete_orphaned_user_title: Usunąć osieroconą rejestrację?
+ delete_orphaned_user_body: Spowoduje to trwałe usunięcie tej osieroconej rejestracji użytkownika SnapTrade i wszystkich jego połączeń z biurami maklerskimi, zwalniając miejsca połączeń.
+ delete_orphaned_user_confirm: Usuń rejestrację
+ snaptrade_item:
+ sync_status:
+ no_accounts: Nie znaleziono kont
+ synced:
+ one: "%{count} konto zsynchronizowane"
+ few: "%{count} konta zsynchronizowane"
+ many: "%{count} kont zsynchronizowanych"
+ other: "%{count} kont zsynchronizowanych"
+ synced_with_setup: "%{linked} zsynchronizowane, %{unlinked} wymagają konfiguracji"
+ institution_summary:
+ none: Brak połączonych instytucji
+ count:
+ one: "%{count} instytucja"
+ few: "%{count} instytucje"
+ many: "%{count} instytucji"
+ other: "%{count} instytucji"
+ brokerage_summary:
+ none: Brak połączonych biur maklerskich
+ count:
+ one: "%{count} biuro maklerskie"
+ few: "%{count} biura maklerskie"
+ many: "%{count} biur maklerskich"
+ other: "%{count} biur maklerskich"
+ syncer:
+ discovering: Odnajdywanie kont...
+ importing: Importowanie kont z SnapTrade...
+ processing: Przetwarzanie aktywów i aktywności...
+ calculating: Obliczanie sald...
+ checking_config: Sprawdzanie konfiguracji konta...
+ needs_setup: "%{count} kont wymaga konfiguracji..."
+ activities_fetching_async: Aktywności są pobierane w tle. Może to potrwać do minuty dla nowych połączeń z biurami maklerskimi.
diff --git a/config/locales/views/splits/en.yml b/config/locales/views/splits/en.yml
new file mode 100644
index 000000000..db9a5e037
--- /dev/null
+++ b/config/locales/views/splits/en.yml
@@ -0,0 +1,47 @@
+---
+en:
+ splits:
+ new:
+ title: Split Transaction
+ description: Split this transaction into multiple entries with different categories and amounts.
+ submit: Split Transaction
+ cancel: Cancel
+ add_row: Add split
+ remove_row: Remove
+ remaining: Remaining
+ amounts_must_match: Split amounts must equal the original transaction amount.
+ name_label: Name
+ name_placeholder: Split name
+ amount_label: Amount
+ category_label: Category
+ uncategorized: "(uncategorized)"
+ original_name: "Name:"
+ original_date: "Date:"
+ original_amount: "Amount"
+ split_number: "Split #%{number}"
+ create:
+ success: Transaction split successfully
+ not_splittable: This transaction cannot be split.
+ destroy:
+ success: Transaction unsplit successfully
+ show:
+ title: Split Entries
+ description: This transaction has been split into the following entries.
+ button_title: Split Transaction
+ button_description: Split this transaction into multiple entries with different categories and amounts.
+ button: Split
+ unsplit_title: Unsplit Transaction
+ unsplit_button: Unsplit
+ unsplit_confirm: This will remove all split entries and restore the original transaction.
+ edit:
+ title: Edit Split
+ description: Modify the split entries for this transaction.
+ submit: Update Split
+ not_split: This transaction is not split.
+ update:
+ success: Split updated successfully
+ child:
+ title: Part of Split
+ description: This entry is part of a split transaction.
+ edit_split: Edit Split
+ unsplit: Unsplit
diff --git a/config/locales/views/splits/pl.yml b/config/locales/views/splits/pl.yml
new file mode 100644
index 000000000..01e4cb19c
--- /dev/null
+++ b/config/locales/views/splits/pl.yml
@@ -0,0 +1,47 @@
+---
+pl:
+ splits:
+ new:
+ title: Podziel transakcję
+ description: Podziel tę transakcję na wiele wpisów z różnymi kategoriami i kwotami.
+ submit: Podziel transakcję
+ cancel: Anuluj
+ add_row: Dodaj podział
+ remove_row: Usuń
+ remaining: Pozostało
+ amounts_must_match: Kwoty podziału muszą być równe kwocie oryginalnej transakcji.
+ name_label: Nazwa
+ name_placeholder: Nazwa podziału
+ amount_label: Kwota
+ category_label: Kategoria
+ uncategorized: "(bez kategorii)"
+ original_name: 'Nazwa:'
+ original_date: 'Data:'
+ original_amount: Kwota
+ split_number: 'Podział nr %{number}'
+ create:
+ success: Transakcja została pomyślnie podzielona
+ not_splittable: Tej transakcji nie można podzielić.
+ destroy:
+ success: Podział transakcji został pomyślnie cofnięty
+ show:
+ title: Podzielone wpisy
+ description: Ta transakcja została podzielona na poniższe wpisy.
+ button_title: Podziel transakcję
+ button_description: Podziel tę transakcję na wiele wpisów z różnymi kategoriami i kwotami.
+ button: Podziel
+ unsplit_title: Cofnij podział transakcji
+ unsplit_button: Cofnij podział
+ unsplit_confirm: To usunie wszystkie wpisy podziału i przywróci oryginalną transakcję.
+ edit:
+ title: Edytuj podział
+ description: Zmodyfikuj wpisy podziału dla tej transakcji.
+ submit: Zaktualizuj podział
+ not_split: Ta transakcja nie jest podzielona.
+ update:
+ success: Podział został pomyślnie zaktualizowany
+ child:
+ title: Część podziału
+ description: Ten wpis jest częścią podzielonej transakcji.
+ edit_split: Edytuj podział
+ unsplit: Cofnij podział
diff --git a/config/locales/views/subscriptions/pl.yml b/config/locales/views/subscriptions/pl.yml
new file mode 100644
index 000000000..d40212625
--- /dev/null
+++ b/config/locales/views/subscriptions/pl.yml
@@ -0,0 +1,18 @@
+---
+pl:
+ subscriptions:
+ self_hosted_alert: "%{product_name} nie jest dostępny w trybie self-hosted."
+ upgrade:
+ contribute_and_support_sure: "Wspieraj i rozwijaj Sure"
+ cta: "Wesprzyj dalszy rozwój tej bazy kodu!"
+ header:
+ support: "Wesprzyj"
+ sure: "Sure"
+ today: "już dziś"
+ redirect_to_stripe: "W kolejnym kroku nastąpi przekierowanie do Stripe, który obsługuje płatności kartą."
+ trialing:
+ one: "Twoje dane zostaną usunięte za %{count} dzień"
+ few: "Twoje dane zostaną usunięte za %{count} dni"
+ many: "Twoje dane zostaną usunięte za %{count} dni"
+ other: "Twoje dane zostaną usunięte za %{count} dnia"
+ trial_over: "Okres próbny dobiegł końca"
diff --git a/config/locales/views/tag/deletions/pl.yml b/config/locales/views/tag/deletions/pl.yml
new file mode 100644
index 000000000..0b29e4ca1
--- /dev/null
+++ b/config/locales/views/tag/deletions/pl.yml
@@ -0,0 +1,13 @@
+---
+pl:
+ tag:
+ deletions:
+ create:
+ deleted: Tag został usunięty
+ new:
+ delete_and_leave_uncategorized: Usuń "%{tag_name}"
+ delete_and_recategorize: Usuń "%{tag_name}" i przypisz nowy tag
+ delete_tag: Usunąć tag?
+ explanation: "%{tag_name} zostanie usunięty z transakcji i innych obiektów oznaczonych tagami. Zamiast pozostawiać je bez tagu, możesz poniżej przypisać nowy tag."
+ replacement_tag_prompt: Wybierz tag
+ tag: Tag
diff --git a/config/locales/views/tags/pl.yml b/config/locales/views/tags/pl.yml
new file mode 100644
index 000000000..af9624452
--- /dev/null
+++ b/config/locales/views/tags/pl.yml
@@ -0,0 +1,23 @@
+---
+pl:
+ tags:
+ create:
+ created: Tag został utworzony
+ error: 'Błąd podczas tworzenia tagu: %{error}'
+ destroy:
+ deleted: Tag został usunięty
+ edit:
+ edit: Edytuj tag
+ form:
+ placeholder: Nazwa tagu
+ index:
+ empty: Brak tagów
+ new: Nowy tag
+ tags: Tagi
+ new:
+ new: Nowy tag
+ tag:
+ delete: Usuń
+ edit: Edytuj
+ update:
+ updated: Tag został zaktualizowany
diff --git a/config/locales/views/trades/de.yml b/config/locales/views/trades/de.yml
index 51417c1f9..912d5bd9a 100644
--- a/config/locales/views/trades/de.yml
+++ b/config/locales/views/trades/de.yml
@@ -6,24 +6,37 @@ de:
account_prompt: Konto suchen
amount: Betrag
holding: Tickersymbol
+ holding_optional: Tickersymbol (optional)
price: Preis pro Anteil
qty: Menge
submit: Transaktion hinzufügen
ticker_placeholder: AAPL
type: Typ
+ type_buy: Kaufen
+ type_sell: Verkaufen
+ type_deposit: Einzahlung
+ type_withdrawal: Auszahlung
+ type_dividend: Dividende
+ type_interest: Zinsen
+ dividend_requires_security: Für Dividenden ist ein Wertpapier erforderlich
header:
buy: Kaufen
+ sell: Verkaufen
+ dividend: Dividende
+ interest: Zinsen
current_market_price_label: Aktueller Marktpreis
overview: Übersicht
purchase_price_label: Kaufpreis
purchase_qty_label: Kaufmenge
- sell: Verkaufen
symbol_label: Symbol
total_return_label: Nicht realisierter Gewinn/Verlust
new:
title: Neue Transaktion
show:
additional: Zusätzlich
+ amount_label: Betrag
+ buy: Kaufen
+ category_label: Kategorie
cost_per_share_label: Kosten pro Anteil
date_label: Datum
delete: Löschen
@@ -32,7 +45,10 @@ de:
details: Details
exclude_subtitle: Dieser Trade wird nicht in Berichten und Berechnungen berücksichtigt
exclude_title: Von Analysen ausschließen
+ no_category: Keine Kategorie
note_label: Notiz
note_placeholder: Füge hier zusätzliche Notizen hinzu …
quantity_label: Menge
+ sell: Verkaufen
settings: Einstellungen
+ type_label: Typ
diff --git a/config/locales/views/trades/en.yml b/config/locales/views/trades/en.yml
index 606975c19..b2257c26f 100644
--- a/config/locales/views/trades/en.yml
+++ b/config/locales/views/trades/en.yml
@@ -5,30 +5,43 @@ en:
account: Transfer account (optional)
account_prompt: Search account
amount: Amount
+ fee: Transaction fee
holding: Ticker symbol
+ holding_optional: Ticker symbol (optional)
price: Price per share
qty: Quantity
submit: Add transaction
ticker_placeholder: AAPL
type: Type
+ type_buy: Buy
+ type_sell: Sell
+ type_deposit: Deposit
+ type_withdrawal: Withdrawal
+ type_dividend: Dividend
+ type_interest: Interest
+ dividend_requires_security: Security is required for dividends
header:
buy: Buy
+ sell: Sell
+ dividend: Dividend
+ interest: Interest
current_market_price_label: Current Market Price
overview: Overview
purchase_price_label: Purchase Price
purchase_qty_label: Purchase Quantity
- sell: Sell
symbol_label: Symbol
total_return_label: Unrealized gain/loss
new:
title: New transaction
show:
additional: Additional
+ amount_label: Amount
buy: Buy
category_label: Category
cost_per_share_label: Cost per Share
date_label: Date
delete: Delete
+ fee_label: Transaction fee
delete_subtitle: This action cannot be undone
delete_title: Delete Trade
details: Details
diff --git a/config/locales/views/trades/es.yml b/config/locales/views/trades/es.yml
index 44198fb57..58c86d756 100644
--- a/config/locales/views/trades/es.yml
+++ b/config/locales/views/trades/es.yml
@@ -6,24 +6,37 @@ es:
account_prompt: Buscar cuenta
amount: Importe
holding: Símbolo del ticker
+ holding_optional: Símbolo del ticker (opcional)
price: Precio por acción
qty: Cantidad
submit: Añadir transacción
ticker_placeholder: AAPL
type: Tipo
+ type_buy: Comprar
+ type_sell: Vender
+ type_deposit: Depósito
+ type_withdrawal: Retiro
+ type_dividend: Dividendo
+ type_interest: Interés
+ dividend_requires_security: Se requiere un valor para los dividendos
header:
buy: Comprar
+ sell: Vender
+ dividend: Dividendo
+ interest: Interés
current_market_price_label: Precio de mercado actual
overview: Resumen
purchase_price_label: Precio de compra
purchase_qty_label: Cantidad comprada
- sell: Vender
symbol_label: Símbolo
total_return_label: Ganancia/pérdida no realizada
new:
title: Nueva transacción
show:
additional: Adicional
+ amount_label: Importe
+ buy: Compra
+ category_label: Categoría
cost_per_share_label: Costo por acción
date_label: Fecha
delete: Eliminar
@@ -32,7 +45,10 @@ es:
details: Detalles
exclude_subtitle: Esta operación no se incluirá en informes y cálculos
exclude_title: Excluir de los análisis
+ no_category: Sin categoría
note_label: Nota
note_placeholder: Añade aquí cualquier nota adicional...
quantity_label: Cantidad
+ sell: Venta
settings: Configuración
+ type_label: Tipo
\ No newline at end of file
diff --git a/config/locales/views/trades/fr.yml b/config/locales/views/trades/fr.yml
index 7332d4eb6..e94a04678 100644
--- a/config/locales/views/trades/fr.yml
+++ b/config/locales/views/trades/fr.yml
@@ -6,24 +6,35 @@ fr:
account_prompt: Rechercher un compte
amount: Montant
holding: Symbole boursier
+ holding_optional: Symbole boursier (facultatif)
price: Prix par action
qty: Quantité
submit: Ajouter la transaction
ticker_placeholder: AAPL
type: Type
+ type_buy: Acheter
+ type_sell: Vendre
+ type_deposit: Dépôt
+ type_withdrawal: Retrait
+ type_dividend: Dividende
+ type_interest: Intérêts
+ dividend_requires_security: Un titre est requis pour les dividendes
header:
buy: Acheter
+ sell: Vendre
+ dividend: Dividende
+ interest: Intérêts
current_market_price_label: Prix du marché actuel
overview: Aperçu
purchase_price_label: Prix d'achat
purchase_qty_label: Quantité achetée
- sell: Vendre
symbol_label: Symbole
total_return_label: Gain/perte non réalisé(e)
new:
title: Nouvelle transaction
show:
additional: Détails supplémentaires
+ amount_label: Montant
cost_per_share_label: Coût par action
date_label: Date
delete: Supprimer
diff --git a/config/locales/views/trades/pl.yml b/config/locales/views/trades/pl.yml
new file mode 100644
index 000000000..44e16cd9d
--- /dev/null
+++ b/config/locales/views/trades/pl.yml
@@ -0,0 +1,56 @@
+---
+pl:
+ trades:
+ form:
+ account: Konto transferowe (opcjonalnie)
+ account_prompt: Wyszukaj konto
+ amount: Kwota
+ fee: Opłata transakcyjna
+ holding: Symbol ticker
+ holding_optional: Symbol ticker (opcjonalnie)
+ price: Cena za akcję
+ qty: Ilość
+ submit: Dodaj transakcję
+ ticker_placeholder: AAPL
+ type: Typ
+ type_buy: Kupno
+ type_sell: Sprzedaż
+ type_deposit: Wpłata
+ type_withdrawal: Wypłata
+ type_dividend: Dywidenda
+ type_interest: Odsetki
+ dividend_requires_security: Dla dywidend wymagany jest papier wartościowy
+ header:
+ buy: Kupno
+ sell: Sprzedaż
+ dividend: Dywidenda
+ interest: Odsetki
+ current_market_price_label: Aktualna cena rynkowa
+ overview: Przegląd
+ purchase_price_label: Cena zakupu
+ purchase_qty_label: Ilość zakupu
+ symbol_label: Symbol
+ total_return_label: Niezrealizowany zysk/strata
+ new:
+ title: Nowa transakcja
+ show:
+ additional: Dodatkowe
+ amount_label: Kwota
+ buy: Kupno
+ category_label: Kategoria
+ cost_per_share_label: Koszt za akcję
+ date_label: Data
+ delete: Usuń
+ fee_label: Opłata transakcyjna
+ delete_subtitle: Tej akcji nie można cofnąć
+ delete_title: Usuń transakcję giełdową
+ details: Szczegóły
+ exclude_subtitle: Ta transakcja giełdowa nie będzie uwzględniana w raportach i obliczeniach
+ exclude_title: Wyklucz z analityki
+ no_category: Brak kategorii
+ note_label: Notatka
+ note_placeholder: Dodaj tutaj dodatkowe notatki...
+ quantity_label: Ilość
+ sell: Sprzedaż
+ settings: Ustawienia
+ type_label: Typ
diff --git a/config/locales/views/transactions/ca.yml b/config/locales/views/transactions/ca.yml
index 7a1a2ad04..dc3b94981 100644
--- a/config/locales/views/transactions/ca.yml
+++ b/config/locales/views/transactions/ca.yml
@@ -122,6 +122,7 @@ ca:
transaction:
pending: Pendent
pending_tooltip: Transacció pendent — pot canviar quan es publiqui
+ linked_with_provider: Vinculat amb %{provider}
possible_duplicate: Duplicat?
potential_duplicate_tooltip: Això pot ser un duplicat d'una altra transacció
review_recommended: Revisa
diff --git a/config/locales/views/transactions/de.yml b/config/locales/views/transactions/de.yml
index 2674005b4..2b19d6942 100644
--- a/config/locales/views/transactions/de.yml
+++ b/config/locales/views/transactions/de.yml
@@ -1,6 +1,7 @@
---
de:
transactions:
+ unknown_name: Unbekannte Transaktion
form:
account: Konto
account_prompt: Konto auswählen
@@ -29,6 +30,15 @@ de:
delete_subtitle: Diese Aktion löscht die Transaktion dauerhaft, beeinflusst deine bisherigen Kontostände und kann nicht rückgängig gemacht werden.
delete_title: Transaktion löschen
details: Details
+ exclude: Ausschließen
+ exclude_description: Ausgeschlossene Transaktionen werden aus Budgetberechnungen und Berichten entfernt.
+ activity_type: Aktivitätsart
+ activity_type_description: Art der Anlageaktivität (Kauf, Verkauf, Dividende usw.). Wird automatisch erkannt oder manuell gesetzt.
+ one_time_title: Einmalige %{type}
+ one_time_description: Einmalige Transaktionen werden aus bestimmten Budgetberechnungen und Berichten ausgeschlossen, damit du das Wichtige besser erkennst.
+ convert_to_trade_title: In Wertpapier-Trade umwandeln
+ convert_to_trade_description: Diese Transaktion in einen Kauf- oder Verkaufstrade mit Wertpapierdetails für die Portfolioverfolgung umwandeln.
+ convert_to_trade_button: In Trade umwandeln
merchant_label: Händler
name_label: Name
nature: Typ
@@ -38,7 +48,45 @@ de:
overview: Übersicht
settings: Einstellungen
tags_label: Tags
+ tab_transactions: Transaktionen
+ tab_upcoming: Anstehend
uncategorized: (ohne Kategorie)
+ activity_labels:
+ buy: Kaufen
+ sell: Verkaufen
+ sweep_in: Sweep In
+ sweep_out: Sweep Out
+ dividend: Dividende
+ reinvestment: Reinvestition
+ interest: Zinsen
+ fee: Gebühr
+ transfer: Überweisung
+ contribution: Einlage
+ withdrawal: Entnahme
+ exchange: Umtausch
+ other: Sonstige
+ mark_recurring: Als wiederkehrend markieren
+ mark_recurring_subtitle: Als wiederkehrende Transaktion verfolgen. Die Betragsabweichung wird automatisch aus den letzten 6 Monaten ähnlicher Transaktionen berechnet.
+ mark_recurring_title: Wiederkehrende Transaktion
+ potential_duplicate_title: Mögliches Duplikat erkannt
+ potential_duplicate_description: Diese ausstehende Transaktion könnte mit der unten stehenden gebuchten Transaktion übereinstimmen. Wenn ja, führe sie zusammen, um Doppelzählung zu vermeiden.
+ merge_duplicate: Ja, zusammenführen
+ keep_both: Nein, beide behalten
+ transaction:
+ pending: Ausstehend
+ pending_tooltip: Ausstehende Transaktion — kann sich bei Buchung ändern
+ linked_with_provider: Mit %{provider} verknüpft
+ activity_type_tooltip: Art der Anlageaktivität
+ possible_duplicate: Duplikat?
+ potential_duplicate_tooltip: Dies könnte ein Duplikat einer anderen Transaktion sein
+ review_recommended: Prüfen
+ review_recommended_tooltip: Große Betragsabweichung — Prüfung empfohlen, ob es sich um ein Duplikat handelt
+ merge_duplicate:
+ success: Transaktionen erfolgreich zusammengeführt
+ failure: Transaktionen konnten nicht zusammengeführt werden
+ dismiss_duplicate:
+ success: Als getrennte Transaktionen beibehalten
+ failure: Duplikatshinweis konnte nicht verworfen werden
header:
edit_categories: Kategorien bearbeiten
edit_imports: Importe bearbeiten
@@ -48,6 +96,65 @@ de:
index:
transaction: Transaktion
transactions: Transaktionen
+ import: Import
+ list:
+ drag_drop_title: CSV zum Importieren ablegen
+ drag_drop_subtitle: Transaktionen direkt hochladen
+ transaction: Transaktion
+ transactions: Transaktionen
+ toggle_recurring_section: Anstehende wiederkehrende Transaktionen ein-/ausblenden
+ search:
+ filters:
+ account: Konto
+ date: Datum
+ type: Typ
+ status: Status
+ amount: Betrag
+ category: Kategorie
+ tag: Tag
+ merchant: Händler
+ convert_to_trade:
+ title: In Wertpapier-Trade umwandeln
+ description: Diese Transaktion in einen Trade mit Wertpapierdetails umwandeln
+ date_label: "Datum:"
+ account_label: "Konto:"
+ amount_label: "Betrag:"
+ security_label: Wertpapier
+ security_prompt: Wertpapier auswählen…
+ security_custom: "+ Eigenes Tickersymbol eingeben"
+ security_not_listed_hint: Dein Wertpapier nicht dabei? Wähle unten in der Liste „Eigenes Tickersymbol eingeben“.
+ ticker_placeholder: AAPL
+ ticker_hint: Gib das Aktien-/ETF-Tickersymbol ein (z. B. AAPL, MSFT)
+ ticker_search_placeholder: Nach Ticker suchen…
+ ticker_search_hint: Nach Tickersymbol oder Firmenname suchen oder eigenes Tickersymbol eingeben
+ price_mismatch_title: Preis weicht möglicherweise ab
+ price_mismatch_message: "Dein Preis (%{entered_price}/Aktie) weicht deutlich vom aktuellen Marktpreis von %{ticker} (%{market_price}) ab. Wenn das falsch erscheint, hast du vielleicht das falsche Wertpapier gewählt — versuche „Eigenes Tickersymbol eingeben“, um das richtige anzugeben."
+ quantity_label: Menge (Aktien)
+ quantity_placeholder: z. B. 20
+ quantity_hint: Anzahl der gehandelten Aktien
+ price_label: Preis pro Aktie
+ price_placeholder: z. B. 52,15
+ price_hint: Preis pro Aktie (%{currency})
+ qty_or_price_hint: Gib mindestens Menge ODER Preis ein. Der andere Wert wird aus dem Transaktionsbetrag (%{amount}) berechnet.
+ trade_type_label: Trade-Typ
+ trade_type_hint: Kauf oder Verkauf von Wertpapieranteilen
+ exchange_label: Börse (optional)
+ exchange_placeholder: XNAS
+ exchange_hint: Leer lassen für automatische Erkennung
+ cancel: Abbrechen
+ submit: In Trade umwandeln
+ success: Transaktion in Trade umgewandelt
+ conversion_note: "Umgewandelt aus Transaktion: %{original_name} (%{original_date})"
+ errors:
+ not_investment_account: Nur Transaktionen in Anlagekonten können in Trades umgewandelt werden
+ already_converted: Diese Transaktion wurde bereits umgewandelt oder ausgeschlossen
+ enter_ticker: Bitte gib ein Tickersymbol ein
+ security_not_found: Das gewählte Wertpapier existiert nicht mehr. Bitte wähle ein anderes.
+ select_security: Bitte wähle ein Wertpapier aus oder gib eines ein
+ enter_qty_or_price: Bitte gib entweder Menge oder Preis pro Aktie ein. Der andere Wert wird aus dem Transaktionsbetrag berechnet.
+ invalid_qty_or_price: Ungültige Menge oder Preis. Bitte gib gültige positive Werte ein.
+ conversion_failed: "Transaktion konnte nicht umgewandelt werden: %{error}"
+ unexpected_error: "Unerwarteter Fehler bei der Umwandlung: %{error}"
searches:
filters:
amount_filter:
@@ -61,10 +168,15 @@ de:
on_or_after: am oder nach %{date}
on_or_before: am oder vor %{date}
transfer: Überweisung
+ confirmed: Bestätigt
+ pending: Ausstehend
type_filter:
expense: Ausgabe
income: Einnahme
transfer: Überweisung
+ status_filter:
+ confirmed: Bestätigt
+ pending: Ausstehend
menu:
account_filter: Konto
amount_filter: Betrag
@@ -74,6 +186,7 @@ de:
clear_filters: Filter löschen
date_filter: Datum
merchant_filter: Händler
+ status_filter: Status
tag_filter: Tag
type_filter: Typ
search:
diff --git a/config/locales/views/transactions/en.yml b/config/locales/views/transactions/en.yml
index 02287d644..c5b5c3f0c 100644
--- a/config/locales/views/transactions/en.yml
+++ b/config/locales/views/transactions/en.yml
@@ -2,6 +2,9 @@
en:
transactions:
unknown_name: Unknown transaction
+ selection_bar:
+ duplicate: Duplicate
+ edit: Edit
form:
account: Account
account_prompt: Select an Account
@@ -13,6 +16,7 @@ en:
description_placeholder: Describe transaction
expense: Expense
income: Income
+ merchant_label: Merchant
none: (none)
note_label: Notes
note_placeholder: Enter a note
@@ -31,6 +35,7 @@ en:
balances, and cannot be undone.
delete_title: Delete transaction
details: Details
+ attachments: Attachments
exclude: Exclude
exclude_description: Excluded transactions will be removed from budgeting calculations and reports.
activity_type: Activity Type
@@ -40,6 +45,9 @@ en:
convert_to_trade_title: Convert to Security Trade
convert_to_trade_description: Convert this transaction into a Buy or Sell trade with security details for portfolio tracking.
convert_to_trade_button: Convert to Trade
+ pending_duplicate_merger_title: Duplicate of Posted Transaction?
+ pending_duplicate_merger_description: Manually merge this pending transaction with its posted version.
+ pending_duplicate_merger_button: Open merger
merchant_label: Merchant
name_label: Name
nature: Type
@@ -73,21 +81,39 @@ en:
potential_duplicate_description: This pending transaction may be the same as the posted transaction below. If so, merge them to avoid double-counting.
merge_duplicate: Yes, merge them
keep_both: No, keep both
+ split_parent_row:
+ split_label: "Split"
transaction:
pending: Pending
pending_tooltip: Pending transaction — may change when posted
- linked_with_plaid: Linked with Plaid
+ linked_with_provider: Linked with %{provider}
activity_type_tooltip: Investment activity type
possible_duplicate: Duplicate?
potential_duplicate_tooltip: This may be a duplicate of another transaction
review_recommended: Review
review_recommended_tooltip: Large amount difference — review recommended to check if this is a duplicate
+ split: Split
+ split_tooltip: This transaction has been split into multiple entries
+ split_child_tooltip: Part of a split transaction
merge_duplicate:
success: Transactions merged successfully
failure: Could not merge transactions
dismiss_duplicate:
success: Kept as separate transactions
failure: Could not dismiss duplicate suggestion
+ pending_duplicate_merge:
+ possible_duplicate: Duplicate?
+ possible_duplicate_short: Dup?
+ review_recommended: Review
+ review_recommended_short: Rev
+ confirm_title: "Merge with posted transaction (%{posted_amount})"
+ reject_title: Keep as separate transactions
+ summary:
+ total_transactions: Total transactions
+ income: Income
+ expenses: Expenses
+ inflow: Inflow
+ outflow: Outflow
header:
edit_categories: Edit categories
edit_imports: Edit imports
@@ -98,6 +124,42 @@ en:
transaction: transaction
transactions: transactions
import: Import
+ categorize_button:
+ one: "Categorize (1)"
+ other: "Categorize (%{count})"
+ categorizes:
+ show:
+ exit: "Exit"
+ skip: "Skip"
+ remaining:
+ one: "1 uncategorized transaction remaining"
+ other: "%{count} uncategorized transactions remaining"
+ transaction_count:
+ one: "1 transaction"
+ other: "%{count} transactions"
+ transactions_hint: "Uncheck to exclude a transaction, or assign it a different category directly in its row."
+ assign_category: "Assign a category"
+ assign_category_prompt: "→ assign"
+ filter_placeholder: "Search categories..."
+ col_transaction: "Transaction"
+ col_date: "Date"
+ col_amount: "Amount"
+ col_category: "Category"
+ type_income: "Income"
+ type_expense: "Expense"
+ create_rule_label: "Create Categorization Rule"
+ rule_description_prefix: "Future %{type} transactions with name containing"
+ rule_description_suffix: "should also get this category."
+ no_categories: "No matching categories"
+ all_done: "All transactions are categorized"
+ create:
+ categorized:
+ one: "1 transaction categorized"
+ other: "%{count} transactions categorized"
+ rule_creation_failed: "Transactions categorized, but the rule could not be created (it may already exist)."
+ entry_row:
+ include_checkbox: "Include %{name}"
+ assign_category_select: "Assign category for %{name}"
list:
drag_drop_title: Drop CSV to import
drag_drop_subtitle: Upload transactions directly
@@ -196,3 +258,21 @@ en:
less_than: less than
form:
toggle_selection_checkboxes: Toggle all checkboxes
+ attachments:
+ cannot_exceed: "Cannot exceed %{count} attachments per transaction"
+ uploaded_one: "Attachment uploaded successfully"
+ uploaded_many: "%{count} attachments uploaded successfully"
+ failed_upload: "Failed to upload attachment: %{error}"
+ no_files_selected: "No files selected for upload"
+ attachment_deleted: "Attachment deleted successfully"
+ failed_delete: "Failed to delete attachment: %{error}"
+ upload_failed: "Failed to upload attachment. Please try again or contact support."
+ delete_failed: "Failed to delete attachment. Please try again or contact support."
+ upload: "Upload"
+ no_attachments: "No attachments yet"
+ select_up_to: "Select up to %{count} files (images or PDFs, max %{size}MB each) • %{used} of %{count} used"
+ files:
+ one: "File (1)"
+ other: "Files (%{count})"
+ browse_to_add: "Browse to add files"
+ max_reached: "Maximum file limit reached (%{count}/%{max}). Delete an existing file to upload another."
diff --git a/config/locales/views/transactions/es.yml b/config/locales/views/transactions/es.yml
index 03c01f78c..b5d294422 100644
--- a/config/locales/views/transactions/es.yml
+++ b/config/locales/views/transactions/es.yml
@@ -1,6 +1,7 @@
---
es:
transactions:
+ unknown_name: Transacción desconocida
form:
account: Cuenta
account_prompt: Selecciona una cuenta
@@ -29,6 +30,15 @@ es:
delete_subtitle: Esto eliminará permanentemente la transacción, afectará tus saldos históricos y no se podrá deshacer.
delete_title: Eliminar transacción
details: Detalles
+ exclude: Excluir
+ exclude_description: Las transacciones excluidas se eliminarán de los cálculos y presupuestos e informes.
+ activity_type: Tipo de actividad
+ activity_type_description: Tipo de actividad de inversión (Compra, Venta, Dividendo, etc.). Detectado automáticamente o configurado manualmente.
+ one_time_title: Transacción puntual %{type}
+ one_time_description: Las transacciones puntuales se excluirán de ciertos cálculos de presupuesto e informes para ayudarte a ver lo que es realmente importante.
+ convert_to_trade_title: Convertir en operación de valores
+ convert_to_trade_description: Convierte esta transacción en una operación de compra o venta con detalles del valor para el seguimiento de la cartera.
+ convert_to_trade_button: Convertir en operación
merchant_label: Comerciante
name_label: Nombre
nature: Tipo
@@ -38,7 +48,46 @@ es:
overview: Resumen
settings: Configuración
tags_label: Etiquetas
+ tab_transactions: Transacciones
+ tab_upcoming: Próximas
uncategorized: "(sin categorizar)"
+ activity_labels:
+ buy: Compra
+ sell: Venta
+ sweep_in: Transferencia de barrido (entrada)
+ sweep_out: Transferencia de barrido (salida)
+ dividend: Dividendo
+ reinvestment: Reinversión
+ interest: Interés
+ fee: Comisión
+ transfer: Transferencia
+ contribution: Aportación
+ withdrawal: Retirada
+ exchange: Intercambio
+ other: Otros
+ mark_recurring: Marcar como recurrente
+ mark_recurring_subtitle: Realiza el seguimiento como una transacción recurrente. La variación del importe se calcula automáticamente basándose en transacciones similares de los últimos 6 meses.
+ mark_recurring_title: Transacción recurrente
+ potential_duplicate_title: Posible duplicado detectado
+ potential_duplicate_description: Esta transacción pendiente podría ser la misma que la transacción confirmada a continuación. Si es así, combínalas para evitar una doble contabilización.
+ duplicate_resolution:
+ merge_duplicate: Sí, combinarlas
+ keep_both: No, mantener ambas
+ transaction:
+ pending: Pendiente
+ pending_tooltip: Transacción pendiente — puede cambiar al confirmarse
+ linked_with_provider: Vinculado con %{provider}
+ activity_type_tooltip: Tipo de actividad de inversión
+ possible_duplicate: ¿Duplicada?
+ potential_duplicate_tooltip: Esto puede ser un duplicado de otra transacción
+ review_recommended: Revisar
+ review_recommended_tooltip: Gran diferencia de importe — se recomienda revisar para comprobar si es un duplicado
+ merge_duplicate:
+ success: Transacciones combinadas correctamente
+ failure: No se pudieron combinar las transacciones
+ dismiss_duplicate:
+ success: Mantenidas como transacciones separadas
+ failure: No se pudo descartar la sugerencia de duplicado
header:
edit_categories: Editar categorías
edit_imports: Editar importaciones
@@ -48,6 +97,65 @@ es:
index:
transaction: transacción
transactions: transacciones
+ import: Importar
+ list:
+ drag_drop_title: Suelta el CSV para importar
+ drag_drop_subtitle: Sube transacciones directamente
+ transaction: transacción
+ transactions: transacciones
+ toggle_recurring_section: Alternar próximas transacciones recurrentes
+ search:
+ filters:
+ account: Cuenta
+ date: Fecha
+ type: Tipo
+ status: Estado
+ amount: Importe
+ category: Categoría
+ tag: Etiqueta
+ merchant: Comerciante
+ convert_to_trade:
+ title: Convertir en operación de valores
+ description: Convierte esta transacción en una operación con detalles del valor
+ date_label: "Fecha:"
+ account_label: "Cuenta:"
+ amount_label: "Importe:"
+ security_label: Valor
+ security_prompt: Selecciona un valor...
+ security_custom: "+ Introducir ticker personalizado"
+ security_not_listed_hint: ¿No ves tu valor? Selecciona "Introducir ticker personalizado" al final de la lista.
+ ticker_placeholder: AAPL
+ ticker_hint: Introduce el símbolo del ticker de la acción/ETF (ej. AAPL, MSFT)
+ ticker_search_placeholder: Buscar un ticker...
+ ticker_search_hint: Busca por símbolo de ticker o nombre de empresa, o escribe un ticker personalizado
+ price_mismatch_title: Es posible que el precio no coincida
+ price_mismatch_message: "Tu precio (%{entered_price}/acción) difiere significativamente del precio de mercado actual de %{ticker} (%{market_price}). Si esto parece incorrecto, es posible que hayas seleccionado el valor equivocado — intenta usar \"Introducir ticker personalizado\" para especificar el correcto."
+ quantity_label: Cantidad (Acciones)
+ quantity_placeholder: ej. 20
+ quantity_hint: Número de acciones negociadas
+ price_label: Precio por acción
+ price_placeholder: ej. 52.15
+ price_hint: Precio por acción (%{currency})
+ qty_or_price_hint: Introduce al menos la cantidad O el precio. El otro se calculará a partir del importe de la transacción (%{amount}).
+ trade_type_label: Tipo de operación
+ trade_type_hint: Comprar o vender acciones de un valor
+ exchange_label: Bolsa (Opcional)
+ exchange_placeholder: XNAS
+ exchange_hint: Deja en blanco para detectar automáticamente
+ cancel: Cancelar
+ submit: Convertir en operación
+ success: Transacción convertida en operación correctamente
+ conversion_note: "Convertido desde la transacción: %{original_name} (%{original_date})"
+ errors:
+ not_investment_account: Solo las transacciones en cuentas de inversión pueden convertirse en operaciones
+ already_converted: Esta transacción ya ha sido convertida o excluida
+ enter_ticker: Por favor, introduce un símbolo de ticker
+ security_not_found: El valor seleccionado ya no existe. Por favor, selecciona otro.
+ select_security: Por favor, selecciona o introduce un valor
+ enter_qty_or_price: Por favor, introduce la cantidad o el precio por acción. El otro se calculará a partir del importe de la transacción.
+ invalid_qty_or_price: Cantidad o precio no válidos. Por favor, introduce valores positivos válidos.
+ conversion_failed: "Error al convertir la transacción: %{error}"
+ unexpected_error: "Error inesperado durante la conversión: %{error}"
searches:
filters:
amount_filter:
@@ -61,10 +169,15 @@ es:
on_or_after: en o después de %{date}
on_or_before: en o antes de %{date}
transfer: Transferencia
+ confirmed: Confirmada
+ pending: Pendiente
type_filter:
expense: Gasto
income: Ingreso
transfer: Transferencia
+ status_filter:
+ confirmed: Confirmada
+ pending: Pendiente
menu:
account_filter: Cuenta
amount_filter: Importe
@@ -74,6 +187,7 @@ es:
clear_filters: Limpiar filtros
date_filter: Fecha
merchant_filter: Comerciante
+ status_filter: Estado
tag_filter: Etiqueta
type_filter: Tipo
search:
@@ -81,4 +195,4 @@ es:
greater_than: mayor que
less_than: menor que
form:
- toggle_selection_checkboxes: Alternar todas las casillas
+ toggle_selection_checkboxes: Alternar todas las casillas
\ No newline at end of file
diff --git a/config/locales/views/transactions/fr.yml b/config/locales/views/transactions/fr.yml
index e4f6960e9..d6aeb52a3 100644
--- a/config/locales/views/transactions/fr.yml
+++ b/config/locales/views/transactions/fr.yml
@@ -45,6 +45,8 @@ fr:
edit_merchants: Modifier les marchands
edit_tags: Modifier les étiquettes
import: Importer
+ transaction:
+ linked_with_provider: Lié avec %{provider}
index:
transaction: transaction
transactions: transactions
diff --git a/config/locales/views/transactions/nb.yml b/config/locales/views/transactions/nb.yml
index 5661ad062..479659c30 100644
--- a/config/locales/views/transactions/nb.yml
+++ b/config/locales/views/transactions/nb.yml
@@ -46,6 +46,8 @@ nb:
edit_merchants: Rediger selgere
edit_tags: Rediger tagger
import: Importer
+ transaction:
+ linked_with_provider: Koblet med %{provider}
index:
transaction: transaksjon
transactions: transaksjoner
diff --git a/config/locales/views/transactions/nl.yml b/config/locales/views/transactions/nl.yml
index 4e3bb1dfc..25415e4fc 100644
--- a/config/locales/views/transactions/nl.yml
+++ b/config/locales/views/transactions/nl.yml
@@ -74,6 +74,7 @@ nl:
transaction:
pending: Wachtend
pending_tooltip: Wachtende transactie — kan wijzigen bij posting
+ linked_with_provider: Gekoppeld met %{provider}
activity_type_tooltip: Beleggingsactiviteitstype
possible_duplicate: Duplicaat?
potential_duplicate_tooltip: Dit kan een duplicaat zijn van een andere transactie
diff --git a/config/locales/views/transactions/pl.yml b/config/locales/views/transactions/pl.yml
new file mode 100644
index 000000000..9ae58f25e
--- /dev/null
+++ b/config/locales/views/transactions/pl.yml
@@ -0,0 +1,242 @@
+---
+pl:
+ transactions:
+ unknown_name: Nieznana transakcja
+ selection_bar:
+ duplicate: Duplikat
+ edit: Edytuj
+ form:
+ account: Konto
+ account_prompt: Wybierz konto
+ amount: Kwota
+ category: Kategoria
+ category_prompt: Wybierz kategorię
+ date: Data
+ description: Opis
+ description_placeholder: Opisz transakcję
+ expense: Wydatek
+ income: Przychód
+ merchant_label: Kontrahent
+ none: "(brak)"
+ note_label: Notatki
+ note_placeholder: Wprowadź notatkę
+ submit: Dodaj transakcję
+ tags_label: Tagi
+ transfer: Przelew
+ new:
+ new_transaction: Nowa transakcja
+ show:
+ account_label: Konto
+ amount: Kwota
+ category_label: Kategoria
+ date_label: Data
+ delete: Usuń
+ delete_subtitle: To trwale usuwa transakcję, wpływa na historyczne salda i nie może zostać cofnięte.
+ delete_title: Usuń transakcję
+ details: Szczegóły
+ attachments: Załączniki
+ exclude: Wyklucz
+ exclude_description: Wykluczone transakcje zostaną usunięte z obliczeń budżetowych i raportów.
+ activity_type: Typ aktywności
+ activity_type_description: Typ aktywności inwestycyjnej (kupno, sprzedaż, dywidenda itp.). Wykrywany automatycznie lub ustawiany ręcznie.
+ one_time_title: Jednorazowe %{type}
+ one_time_description: Jednorazowe transakcje będą wykluczone z części obliczeń budżetowych i raportów, aby łatwiej było zobaczyć to, co naprawdę istotne.
+ convert_to_trade_title: Zamień na transakcję papieru wartościowego
+ convert_to_trade_description: Zamień tę transakcję na zlecenie kupna lub sprzedaży z danymi papieru wartościowego do śledzenia portfela.
+ convert_to_trade_button: Zamień na transakcję giełdową
+ pending_duplicate_merger_title: Duplikat zaksięgowanej transakcji?
+ pending_duplicate_merger_description: Ręcznie połącz tę oczekującą transakcję z jej zaksięgowaną wersją.
+ pending_duplicate_merger_button: Otwórz łączenie
+ merchant_label: Kontrahent
+ name_label: Nazwa
+ nature: Typ
+ none: "(brak)"
+ note_label: Notatki
+ note_placeholder: Wprowadź notatkę
+ overview: Przegląd
+ settings: Ustawienia
+ tags_label: Tagi
+ tab_transactions: Transakcje
+ tab_upcoming: Nadchodzące
+ uncategorized: "(bez kategorii)"
+ activity_labels:
+ buy: Kupno
+ sell: Sprzedaż
+ sweep_in: Transfer przychodzący
+ sweep_out: Transfer wychodzący
+ dividend: Dywidenda
+ reinvestment: Reinwestycja
+ interest: Odsetki
+ fee: Opłata
+ transfer: Przelew
+ contribution: Wpłata
+ withdrawal: Wypłata
+ exchange: Wymiana
+ other: Inne
+ mark_recurring: Oznacz jako cykliczną
+ mark_recurring_subtitle: Śledź to jako transakcję cykliczną. Różnica kwoty jest automatycznie obliczana na podstawie ostatnich 6 miesięcy podobnych transakcji.
+ mark_recurring_title: Transakcja cykliczna
+ potential_duplicate_title: Wykryto możliwy duplikat
+ potential_duplicate_description: Ta oczekująca transakcja może być tą samą transakcją co zaksięgowana transakcja poniżej. Jeśli tak, połącz je, aby uniknąć podwójnego liczenia.
+ merge_duplicate:
+ success: Pomyślnie połączono transakcje
+ failure: Nie udało się połączyć transakcji
+ keep_both: Nie, zachowaj obie
+ split_parent_row:
+ split_label: Podział
+ transaction:
+ pending: Oczekująca
+ pending_tooltip: Transakcja oczekująca — może się zmienić po zaksięgowaniu
+ linked_with_provider: Połączono z %{provider}
+ activity_type_tooltip: Typ aktywności inwestycyjnej
+ possible_duplicate: Duplikat?
+ potential_duplicate_tooltip: To może być duplikat innej transakcji
+ review_recommended: Sprawdź
+ review_recommended_tooltip: Duża różnica kwoty — zalecana weryfikacja, aby sprawdzić, czy to duplikat
+ split: Podział
+ split_tooltip: Ta transakcja została podzielona na wiele wpisów
+ split_child_tooltip: Część transakcji dzielonej
+ dismiss_duplicate:
+ success: Pozostawiono jako oddzielne transakcje
+ failure: Nie udało się odrzucić sugestii duplikatu
+ pending_duplicate_merge:
+ possible_duplicate: Duplikat?
+ possible_duplicate_short: Dupl.?
+ review_recommended: Sprawdź
+ review_recommended_short: Weryf.
+ confirm_title: Połącz z zaksięgowaną transakcją (%{posted_amount})
+ reject_title: Zachowaj jako oddzielne transakcje
+ summary:
+ total_transactions: Łącznie transakcji
+ income: Przychody
+ expenses: Wydatki
+ inflow: Wpływy
+ outflow: Wypływy
+ header:
+ edit_categories: Edytuj kategorie
+ edit_imports: Edytuj importy
+ edit_merchants: Edytuj kontrahentów
+ edit_tags: Edytuj tagi
+ import: Importuj
+ index:
+ transaction: transakcja
+ transactions: transakcje
+ import: Importuj
+ list:
+ drag_drop_title: Upuść plik CSV, aby zaimportować
+ drag_drop_subtitle: Prześlij transakcje bezpośrednio
+ transaction: transakcja
+ transactions: transakcje
+ toggle_recurring_section: Przełącz nadchodzące transakcje cykliczne
+ search:
+ filters:
+ account: Konto
+ date: Data
+ type: Typ
+ status: Status
+ amount: Kwota
+ category: Kategoria
+ tag: Tag
+ merchant: Kontrahent
+ convert_to_trade:
+ title: Zamień na transakcję papieru wartościowego
+ description: Zamień tę transakcję na transakcję giełdową z danymi papieru wartościowego
+ date_label: 'Data:'
+ account_label: 'Konto:'
+ amount_label: 'Kwota:'
+ security_label: Papier wartościowy
+ security_prompt: Wybierz papier wartościowy...
+ security_custom: "+ Wprowadź własny ticker"
+ security_not_listed_hint: Nie widzisz swojego papieru? Wybierz na dole listy opcję „Wprowadź własny ticker”.
+ ticker_placeholder: AAPL
+ ticker_hint: Wprowadź symbol giełdowy akcji lub ETF-u, np. AAPL lub MSFT
+ ticker_search_placeholder: Szukaj tickera...
+ ticker_search_hint: Szukaj po symbolu, nazwie spółki lub wpisz własny ticker
+ price_mismatch_title: Cena może się nie zgadzać
+ price_mismatch_message: Twoja cena (%{entered_price}/akcję) znacznie różni się od bieżącej ceny rynkowej %{ticker} (%{market_price}). Jeśli to wygląda nieprawidłowo, mogłeś wybrać zły papier wartościowy — spróbuj użyć opcji „Wprowadź własny ticker”, aby wskazać poprawny.
+ quantity_label: Ilość (akcji)
+ quantity_placeholder: np. 20
+ quantity_hint: Liczba akcji objętych transakcją
+ price_label: Cena za akcję
+ price_placeholder: np. 52.15
+ price_hint: Cena za akcję (%{currency})
+ qty_or_price_hint: Wprowadź przynajmniej ilość albo cenę. Druga wartość zostanie obliczona z kwoty transakcji (%{amount}).
+ trade_type_label: Typ transakcji giełdowej
+ trade_type_hint: Kupno lub sprzedaż akcji papieru wartościowego
+ exchange_label: Giełda (opcjonalne)
+ exchange_placeholder: XNAS
+ exchange_hint: Pozostaw puste, aby wykryć automatycznie
+ cancel: Anuluj
+ submit: Zamień na transakcję giełdową
+ success: Transakcja została zamieniona na transakcję giełdową
+ conversion_note: 'Przekształcono z transakcji: %{original_name} (%{original_date})'
+ errors:
+ not_investment_account: Tylko transakcje na kontach inwestycyjnych można zamieniać na transakcje giełdowe
+ already_converted: Ta transakcja została już przekształcona albo wykluczona
+ enter_ticker: Wprowadź symbol tickera
+ security_not_found: Wybrany papier wartościowy już nie istnieje. Wybierz inny.
+ select_security: Wybierz albo wpisz papier wartościowy
+ enter_qty_or_price: Wprowadź ilość albo cenę za akcję. Druga wartość zostanie obliczona z kwoty transakcji.
+ invalid_qty_or_price: Nieprawidłowa ilość lub cena. Wprowadź poprawne dodatnie wartości.
+ conversion_failed: 'Nie udało się przekształcić transakcji: %{error}'
+ unexpected_error: 'Wystąpił nieoczekiwany błąd podczas przekształcania: %{error}'
+ searches:
+ filters:
+ amount_filter:
+ equal_to: Równe
+ greater_than: Większe niż
+ less_than: Mniejsze niż
+ placeholder: '0'
+ badge:
+ expense: Wydatek
+ income: Przychód
+ on_or_after: od %{date}
+ on_or_before: do %{date}
+ transfer: Przelew
+ confirmed: Potwierdzone
+ pending: Oczekujące
+ type_filter:
+ expense: Wydatek
+ income: Przychód
+ transfer: Przelew
+ status_filter:
+ confirmed: Potwierdzone
+ pending: Oczekujące
+ menu:
+ account_filter: Konto
+ amount_filter: Kwota
+ apply: Zastosuj
+ cancel: Anuluj
+ category_filter: Kategoria
+ clear_filters: Wyczyść filtry
+ date_filter: Data
+ merchant_filter: Kontrahent
+ status_filter: Status
+ tag_filter: Tag
+ type_filter: Typ
+ search:
+ equal_to: równe
+ greater_than: większe niż
+ less_than: mniejsze niż
+ form:
+ toggle_selection_checkboxes: Przełącz wszystkie pola wyboru
+ attachments:
+ cannot_exceed: Nie można przekroczyć %{count} załączników na transakcję
+ uploaded_one: Załącznik został pomyślnie przesłany
+ uploaded_many: "Pomyślnie przesłano %{count} załączników"
+ failed_upload: 'Nie udało się przesłać załącznika: %{error}'
+ no_files_selected: Nie wybrano plików do przesłania
+ attachment_deleted: Załącznik został pomyślnie usunięty
+ failed_delete: 'Nie udało się usunąć załącznika: %{error}'
+ upload_failed: Nie udało się przesłać załącznika. Spróbuj ponownie lub skontaktuj się ze wsparciem.
+ delete_failed: Nie udało się usunąć załącznika. Spróbuj ponownie lub skontaktuj się ze wsparciem.
+ upload: Prześlij
+ no_attachments: Brak załączników
+ select_up_to: Wybierz maksymalnie %{count} plików (obrazy lub PDF, maks. %{size} MB każdy) • wykorzystano %{used} z %{count}
+ files:
+ one: Plik (1)
+ few: Pliki (%{count})
+ many: Plików (%{count})
+ other: Plików (%{count})
+ browse_to_add: Przeglądaj, aby dodać pliki
+ max_reached: Osiągnięto maksymalny limit plików (%{count}/%{max}). Usuń istniejący plik, aby przesłać kolejny.
diff --git a/config/locales/views/transactions/pt-BR.yml b/config/locales/views/transactions/pt-BR.yml
index 15476c952..5d898a27a 100644
--- a/config/locales/views/transactions/pt-BR.yml
+++ b/config/locales/views/transactions/pt-BR.yml
@@ -49,6 +49,8 @@ pt-BR:
edit_merchants: Editar comerciantes
edit_tags: Editar tags
import: Importar
+ transaction:
+ linked_with_provider: Vinculado com %{provider}
index:
transaction: transação
transactions: transações
diff --git a/config/locales/views/transactions/ro.yml b/config/locales/views/transactions/ro.yml
index 8312d3406..dd0d71087 100644
--- a/config/locales/views/transactions/ro.yml
+++ b/config/locales/views/transactions/ro.yml
@@ -45,6 +45,8 @@ ro:
edit_merchants: Editează comercianți
edit_tags: Editează etichete
import: Importă
+ transaction:
+ linked_with_provider: Conectat cu %{provider}
index:
transaction: tranzacție
transactions: tranzacții
diff --git a/config/locales/views/transactions/tr.yml b/config/locales/views/transactions/tr.yml
index b7f545a8e..bb27ce92c 100644
--- a/config/locales/views/transactions/tr.yml
+++ b/config/locales/views/transactions/tr.yml
@@ -45,6 +45,8 @@ tr:
edit_merchants: Satıcıları düzenle
edit_tags: Etiketleri düzenle
import: İçe aktar
+ transaction:
+ linked_with_provider: "%{provider} ile bağlantılı"
index:
transaction: işlem
transactions: işlemler
diff --git a/config/locales/views/transactions/zh-CN.yml b/config/locales/views/transactions/zh-CN.yml
index b8533bba2..11fa2e3e5 100644
--- a/config/locales/views/transactions/zh-CN.yml
+++ b/config/locales/views/transactions/zh-CN.yml
@@ -25,6 +25,8 @@ zh-CN:
edit_merchants: 编辑商户
edit_tags: 编辑标签
import: 导入
+ transaction:
+ linked_with_provider: 已与 %{provider} 关联
index:
import: 导入
transaction: 交易
diff --git a/config/locales/views/transactions/zh-TW.yml b/config/locales/views/transactions/zh-TW.yml
index d5bb0fb46..53c080a8e 100644
--- a/config/locales/views/transactions/zh-TW.yml
+++ b/config/locales/views/transactions/zh-TW.yml
@@ -48,6 +48,8 @@ zh-TW:
edit_merchants: 編輯商家
edit_tags: 編輯標籤
import: 匯入
+ transaction:
+ linked_with_provider: 已與 %{provider} 連結
index:
transaction: 筆交易
transactions: 筆交易
diff --git a/config/locales/views/transfers/pl.yml b/config/locales/views/transfers/pl.yml
new file mode 100644
index 000000000..8f01f23ad
--- /dev/null
+++ b/config/locales/views/transfers/pl.yml
@@ -0,0 +1,30 @@
+---
+pl:
+ transfers:
+ create:
+ success: Przelew został utworzony
+ destroy:
+ success: Przelew został usunięty
+ form:
+ amount: Kwota
+ date: Data
+ expense: Wydatek
+ from: Z
+ income: Przychód
+ select_account: Wybierz konto
+ submit: Utwórz przelew
+ to: Do
+ transfer: Przelew
+ new:
+ title: Nowy przelew
+ show:
+ delete: Usuń przelew
+ delete_subtitle: Ta operacja usuwa przelew. Nie usunie transakcji źródłowych.
+ delete_title: Usunąć przelew?
+ details: Szczegóły
+ note_label: Notatki
+ note_placeholder: Dodaj notatkę do tego przelewu
+ overview: Przegląd
+ settings: Ustawienia
+ update:
+ success: Przelew został zaktualizowany
diff --git a/config/locales/views/users/es.yml b/config/locales/views/users/es.yml
index 54ef054d3..da6fb7cd9 100644
--- a/config/locales/views/users/es.yml
+++ b/config/locales/views/users/es.yml
@@ -8,6 +8,9 @@ es:
email_change_initiated: Por favor, revisa tu nueva dirección de correo electrónico
para obtener instrucciones de confirmación.
success: Tu perfil ha sido actualizado.
+ resend_confirmation_email:
+ success: Se ha puesto en cola el envío de un nuevo correo de confirmación.
+ no_pending_change: ¡No hay ningún cambio de correo electrónico pendiente en este momento!
reset:
success: Tu cuenta ha sido restablecida. Los datos se eliminarán en segundo plano en algún momento.
unauthorized: No estás autorizado para realizar esta acción.
diff --git a/config/locales/views/users/pl.yml b/config/locales/views/users/pl.yml
new file mode 100644
index 000000000..d4d9690f7
--- /dev/null
+++ b/config/locales/views/users/pl.yml
@@ -0,0 +1,17 @@
+---
+pl:
+ users:
+ destroy:
+ success: Twoje konto zostało usunięte.
+ update:
+ email_change_failed: Nie udało się zmienić adresu e-mail.
+ email_change_initiated: Sprawdź nowy adres e-mail, aby dokończyć potwierdzenie zmiany.
+ success: Twój profil został zaktualizowany.
+ resend_confirmation_email:
+ success: Nowa wiadomość potwierdzająca została dodana do kolejki wysyłki.
+ no_pending_change: Brak oczekującej zmiany adresu e-mail!
+ reset:
+ success: Twoje konto zostało zresetowane. Dane zostaną usunięte w tle za jakiś czas.
+ unauthorized: Nie masz uprawnień do wykonania tej akcji
+ reset_with_sample_data:
+ success: Twoje konto zostało zresetowane, a dane przykładowe są przygotowywane. Dane demo pojawią się wkrótce.
diff --git a/config/locales/views/valuations/pl.yml b/config/locales/views/valuations/pl.yml
new file mode 100644
index 000000000..b99fa06ff
--- /dev/null
+++ b/config/locales/views/valuations/pl.yml
@@ -0,0 +1,30 @@
+---
+pl:
+ valuations:
+ form:
+ amount: Kwota
+ submit: Dodaj aktualizację salda
+ header:
+ balance: Saldo
+ index:
+ change: zmiana
+ date: data
+ new_entry: Nowy wpis
+ no_valuations: Brak wycen dla tego konta
+ valuations: Wartość
+ value: wartość
+ new:
+ title: Nowe saldo
+ show:
+ amount: Kwota
+ date_label: Data
+ delete: Usuń
+ delete_subtitle: Tej akcji nie można cofnąć
+ delete_title: Usuń wpis
+ details: Szczegóły
+ name_label: Nazwa
+ name_placeholder: Wprowadź nazwę tego wpisu
+ note_label: Notatki
+ note_placeholder: Dodaj dodatkowe szczegóły dotyczące tego wpisu
+ overview: Przegląd
+ settings: Ustawienia
diff --git a/config/locales/views/vehicles/pl.yml b/config/locales/views/vehicles/pl.yml
new file mode 100644
index 000000000..1f53f8d63
--- /dev/null
+++ b/config/locales/views/vehicles/pl.yml
@@ -0,0 +1,25 @@
+---
+pl:
+ vehicles:
+ edit:
+ edit: Edytuj %{account}
+ form:
+ make: Marka
+ make_placeholder: Toyota
+ mileage: Przebieg
+ mileage_placeholder: '15000'
+ mileage_unit: Jednostka
+ model: Model
+ model_placeholder: Camry
+ year: Rok
+ year_placeholder: '2023'
+ new:
+ title: Wprowadź dane pojazdu
+ overview:
+ current_price: Aktualna cena
+ make_model: Marka i model
+ mileage: Przebieg
+ purchase_price: Cena zakupu
+ trend: Trend
+ unknown: Nieznane
+ year: Rok
diff --git a/config/routes.rb b/config/routes.rb
index fd0e77665..35ca76559 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -49,6 +49,21 @@ Rails.application.routes.draw do
end
end
+ resources :binance_items, only: [ :index, :new, :create, :show, :edit, :update, :destroy ] do
+ collection do
+ get :select_accounts
+ post :link_accounts
+ get :select_existing_account
+ post :link_existing_account
+ end
+
+ member do
+ post :sync
+ get :setup_accounts
+ post :complete_account_setup
+ end
+ end
+
resources :snaptrade_items, only: [ :index, :new, :create, :show, :edit, :update, :destroy ] do
collection do
get :preload_accounts
@@ -74,6 +89,7 @@ Rails.application.routes.draw do
resources :coinstats_items, only: [ :index, :new, :create, :update, :destroy ] do
collection do
post :link_wallet
+ post :link_exchange
end
member do
post :sync
@@ -107,6 +123,11 @@ Rails.application.routes.draw do
mount Lookbook::Engine, at: "/design-system"
+ if Rails.env.development?
+ mount Rswag::Api::Engine => "/api-docs"
+ mount Rswag::Ui::Engine => "/api-docs"
+ end
+
# Uses basic auth - see config/initializers/sidekiq.rb
mount Sidekiq::Web => "/sidekiq"
@@ -125,6 +146,8 @@ Rails.application.routes.draw do
end
end
+ get "exports/archive/:token", to: "archived_exports#show", as: :archived_export
+
get "changelog", to: "pages#changelog"
get "feedback", to: "pages#feedback"
patch "dashboard/preferences", to: "pages#update_preferences"
@@ -165,8 +188,10 @@ Rails.application.routes.draw do
namespace :settings do
resource :profile, only: [ :show, :destroy ]
resource :preferences, only: :show
+ resource :appearance, only: %i[show update]
resource :hosting, only: %i[show update] do
delete :clear_cache, on: :collection
+ delete :disconnect_external_assistant, on: :collection
end
resource :payment, only: :show
resource :security, only: :show
@@ -210,6 +235,7 @@ Rails.application.routes.draw do
end
resources :budgets, only: %i[index show edit update], param: :month_year do
+ post :copy_previous, on: :member
get :picker, on: :collection
resources :budget_categories, only: %i[index show update]
@@ -226,6 +252,7 @@ Rails.application.routes.draw do
collection do
get :merge
post :perform_merge
+ post :enhance
end
end
@@ -242,6 +269,7 @@ Rails.application.routes.draw do
resource :configuration, only: %i[show update], module: :import
resource :clean, only: :show, module: :import
resource :confirm, only: :show, module: :import
+ resource :qif_category_selection, only: %i[show update], module: :import
resources :rows, only: %i[show update], module: :import
resources :mappings, only: :update, module: :import
@@ -252,6 +280,7 @@ Rails.application.routes.draw do
post :unlock_cost_basis
patch :remap_security
post :reset_security
+ post :sync_prices
end
end
resources :trades, only: %i[show new create update destroy] do
@@ -267,11 +296,18 @@ Rails.application.routes.draw do
namespace :transactions do
resource :bulk_deletion, only: :create
resource :bulk_update, only: %i[new create]
+ resource :categorize, only: %i[show create] do
+ patch :assign_entry, on: :collection
+ get :preview_rule, on: :collection
+ end
end
resources :transactions, only: %i[index new create show update destroy] do
+ resource :split, only: %i[new create edit update destroy]
resource :transfer_match, only: %i[new create]
+ resource :pending_duplicate_merges, only: %i[new create]
resource :category, only: :update, controller: :transaction_categories
+ resources :attachments, only: %i[show create destroy], controller: :transaction_attachments
collection do
delete :clear_filter
@@ -329,6 +365,8 @@ Rails.application.routes.draw do
post :sync
get :sparkline
patch :toggle_active
+ patch :set_default
+ patch :remove_default
get :select_provider
get :confirm_unlink
delete :unlink
@@ -337,6 +375,8 @@ Rails.application.routes.draw do
collection do
post :sync_all
end
+
+ resource :sharing, only: [ :show, :update ], controller: "account_sharings"
end
# Convenience routes for polymorphic paths
@@ -379,6 +419,8 @@ Rails.application.routes.draw do
post "auth/login", to: "auth#login"
post "auth/refresh", to: "auth#refresh"
post "auth/sso_exchange", to: "auth#sso_exchange"
+ post "auth/sso_link", to: "auth#sso_link"
+ post "auth/sso_create_account", to: "auth#sso_create_account"
patch "auth/enable_ai", to: "auth#enable_ai"
# Production API endpoints
@@ -393,6 +435,7 @@ Rails.application.routes.draw do
resources :valuations, only: [ :create, :update, :show ]
resources :imports, only: [ :index, :show, :create ]
resource :usage, only: [ :show ], controller: :usage
+ resource :balance_sheet, only: [ :show ], controller: :balance_sheet
post :sync, to: "sync#create"
resources :chats, only: [ :index, :show, :create, :update, :destroy ] do
@@ -507,6 +550,12 @@ Rails.application.routes.draw do
end
end
resources :users, only: [ :index, :update ]
+ resources :invitations, only: [ :destroy ]
+ resources :families, only: [] do
+ member do
+ delete :invitations, to: "invitations#destroy_all"
+ end
+ end
end
# Defines the root path route ("/")
diff --git a/config/schedule.yml b/config/schedule.yml
index c0d324408..c3903a229 100644
--- a/config/schedule.yml
+++ b/config/schedule.yml
@@ -29,4 +29,16 @@ clean_data:
cron: "0 3 * * *" # daily at 3:00 AM
class: "DataCleanerJob"
queue: "scheduled"
- description: "Cleans up old data (e.g., expired merchant associations)"
+ description: "Cleans up old data (e.g., expired merchant associations, expired archived exports)"
+
+clean_inactive_families:
+ cron: "0 4 * * *" # daily at 4:00 AM
+ class: "InactiveFamilyCleanerJob"
+ queue: "scheduled"
+ description: "Archives and destroys families that expired their trial without subscribing (managed mode only)"
+
+refresh_demo_family:
+ cron: "0 5 * * *" # daily at 5:00 AM UTC
+ class: "DemoFamilyRefreshJob"
+ queue: "scheduled"
+ description: "Refreshes demo family data and emails super admins with daily usage summary"
diff --git a/db/migrate/20260219190000_scope_mercury_account_uniqueness_to_item.rb b/db/migrate/20260219190000_scope_mercury_account_uniqueness_to_item.rb
new file mode 100644
index 000000000..e60147588
--- /dev/null
+++ b/db/migrate/20260219190000_scope_mercury_account_uniqueness_to_item.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class ScopeMercuryAccountUniquenessToItem < ActiveRecord::Migration[7.2]
+ def up
+ # Allow the same Mercury account_id to be linked by different families (different mercury_items).
+ # Uniqueness is scoped per mercury_item, mirroring simplefin_accounts.
+ remove_index :mercury_accounts, name: "index_mercury_accounts_on_account_id", if_exists: true
+ unless index_exists?(:mercury_accounts, [ :mercury_item_id, :account_id ], unique: true, name: "index_mercury_accounts_on_item_and_account_id")
+ add_index :mercury_accounts,
+ [ :mercury_item_id, :account_id ],
+ unique: true,
+ name: "index_mercury_accounts_on_item_and_account_id"
+ end
+ end
+
+ def down
+ if MercuryAccount.group(:account_id).having("COUNT(*) > 1").exists?
+ raise ActiveRecord::IrreversibleMigration,
+ "Cannot restore global unique index on mercury_accounts.account_id: " \
+ "duplicate account_id values exist across mercury_items. " \
+ "Remove duplicates first before rolling back."
+ end
+
+ remove_index :mercury_accounts, name: "index_mercury_accounts_on_item_and_account_id", if_exists: true
+ unless index_exists?(:mercury_accounts, :account_id, name: "index_mercury_accounts_on_account_id")
+ add_index :mercury_accounts, :account_id, name: "index_mercury_accounts_on_account_id", unique: true
+ end
+ end
+end
diff --git a/db/migrate/20260219200001_scope_plaid_item_uniqueness.rb b/db/migrate/20260219200001_scope_plaid_item_uniqueness.rb
new file mode 100644
index 000000000..53c9e2a11
--- /dev/null
+++ b/db/migrate/20260219200001_scope_plaid_item_uniqueness.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+# Scope plaid_accounts uniqueness to plaid_item so the same external account
+# can be linked in multiple families. See: https://github.com/we-promise/sure/issues/740
+# Class name avoids "Account" to prevent secret-scanner false positive (AWS Access ID pattern)
+class ScopePlaidItemUniqueness < ActiveRecord::Migration[7.2]
+ def up
+ remove_index :plaid_accounts, name: "index_plaid_accounts_on_plaid_id", if_exists: true
+ return if index_exists?(:plaid_accounts, [ :plaid_item_id, :plaid_id ], unique: true, name: "index_plaid_accounts_on_item_and_plaid_id")
+
+ add_index :plaid_accounts,
+ [ :plaid_item_id, :plaid_id ],
+ unique: true,
+ name: "index_plaid_accounts_on_item_and_plaid_id"
+ end
+
+ def down
+ if execute("SELECT 1 FROM plaid_accounts WHERE plaid_id IS NOT NULL GROUP BY plaid_id HAVING COUNT(DISTINCT plaid_item_id) > 1 LIMIT 1").any?
+ raise ActiveRecord::IrreversibleMigration,
+ "Cannot rollback: cross-item duplicates exist in plaid_accounts. Remove duplicates first."
+ end
+
+ remove_index :plaid_accounts, name: "index_plaid_accounts_on_item_and_plaid_id", if_exists: true
+ return if index_exists?(:plaid_accounts, :plaid_id, name: "index_plaid_accounts_on_plaid_id")
+
+ add_index :plaid_accounts, :plaid_id, name: "index_plaid_accounts_on_plaid_id", unique: true
+ end
+end
+
+# Backwards-compatible alias for environments that may still reference the
+# original migration constant derived from the old filename.
+ScopePlaidAccountUniquenessToItem = ScopePlaidItemUniqueness unless defined?(ScopePlaidAccountUniquenessToItem)
diff --git a/db/migrate/20260219200002_scope_indexa_capital_account_uniqueness_to_item.rb b/db/migrate/20260219200002_scope_indexa_capital_account_uniqueness_to_item.rb
new file mode 100644
index 000000000..864aa96ef
--- /dev/null
+++ b/db/migrate/20260219200002_scope_indexa_capital_account_uniqueness_to_item.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+# Scope indexa_capital_accounts uniqueness to indexa_capital_item so the same
+# external account can be linked in multiple families. See: https://github.com/we-promise/sure/issues/740
+class ScopeIndexaCapitalAccountUniquenessToItem < ActiveRecord::Migration[7.2]
+ def up
+ remove_index :indexa_capital_accounts, name: "index_indexa_capital_accounts_on_indexa_capital_account_id", if_exists: true
+ return if index_exists?(:indexa_capital_accounts, [ :indexa_capital_item_id, :indexa_capital_account_id ], unique: true, name: "index_indexa_capital_accounts_on_item_and_account_id")
+
+ add_index :indexa_capital_accounts,
+ [ :indexa_capital_item_id, :indexa_capital_account_id ],
+ unique: true,
+ name: "index_indexa_capital_accounts_on_item_and_account_id",
+ where: "indexa_capital_account_id IS NOT NULL"
+ end
+
+ def down
+ if execute("SELECT 1 FROM indexa_capital_accounts WHERE indexa_capital_account_id IS NOT NULL GROUP BY indexa_capital_account_id HAVING COUNT(DISTINCT indexa_capital_item_id) > 1 LIMIT 1").any?
+ raise ActiveRecord::IrreversibleMigration,
+ "Cannot rollback: cross-item duplicates exist in indexa_capital_accounts. Remove duplicates first."
+ end
+
+ remove_index :indexa_capital_accounts, name: "index_indexa_capital_accounts_on_item_and_account_id", if_exists: true
+ return if index_exists?(:indexa_capital_accounts, :indexa_capital_account_id, name: "index_indexa_capital_accounts_on_indexa_capital_account_id")
+
+ add_index :indexa_capital_accounts, :indexa_capital_account_id,
+ name: "index_indexa_capital_accounts_on_indexa_capital_account_id",
+ unique: true,
+ where: "indexa_capital_account_id IS NOT NULL"
+ end
+end
diff --git a/db/migrate/20260219200003_scope_snaptrade_account_uniqueness_to_item.rb b/db/migrate/20260219200003_scope_snaptrade_account_uniqueness_to_item.rb
new file mode 100644
index 000000000..2413e2203
--- /dev/null
+++ b/db/migrate/20260219200003_scope_snaptrade_account_uniqueness_to_item.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+# Scope snaptrade_accounts uniqueness to snaptrade_item so the same external
+# account can be linked in multiple families. See: https://github.com/we-promise/sure/issues/740
+class ScopeSnaptradeAccountUniquenessToItem < ActiveRecord::Migration[7.2]
+ def up
+ remove_index :snaptrade_accounts, name: "index_snaptrade_accounts_on_snaptrade_account_id", if_exists: true
+
+ unless index_exists?(:snaptrade_accounts, [ :snaptrade_item_id, :snaptrade_account_id ], unique: true, name: "index_snaptrade_accounts_on_item_and_snaptrade_account_id")
+ add_index :snaptrade_accounts,
+ [ :snaptrade_item_id, :snaptrade_account_id ],
+ unique: true,
+ name: "index_snaptrade_accounts_on_item_and_snaptrade_account_id",
+ where: "snaptrade_account_id IS NOT NULL"
+ end
+ end
+
+ def down
+ if execute("SELECT 1 FROM snaptrade_accounts WHERE snaptrade_account_id IS NOT NULL GROUP BY snaptrade_account_id HAVING COUNT(DISTINCT snaptrade_item_id) > 1 LIMIT 1").any?
+ raise ActiveRecord::IrreversibleMigration,
+ "Cannot rollback: cross-item duplicates exist in snaptrade_accounts. Remove duplicates first."
+ end
+
+ remove_index :snaptrade_accounts, name: "index_snaptrade_accounts_on_item_and_snaptrade_account_id", if_exists: true
+ unless index_exists?(:snaptrade_accounts, :snaptrade_account_id, name: "index_snaptrade_accounts_on_snaptrade_account_id")
+ add_index :snaptrade_accounts, :snaptrade_account_id,
+ name: "index_snaptrade_accounts_on_snaptrade_account_id",
+ unique: true,
+ where: "snaptrade_account_id IS NOT NULL"
+ end
+ end
+end
diff --git a/db/migrate/20260219200004_scope_coinbase_account_uniqueness_to_item.rb b/db/migrate/20260219200004_scope_coinbase_account_uniqueness_to_item.rb
new file mode 100644
index 000000000..3f77d92c2
--- /dev/null
+++ b/db/migrate/20260219200004_scope_coinbase_account_uniqueness_to_item.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# NEW constraint: add per-item unique index on coinbase_accounts. Unlike Plaid/Snaptrade,
+# there was no prior unique index—this can fail if existing data has duplicate
+# (coinbase_item_id, account_id) pairs. See: https://github.com/we-promise/sure/issues/740
+class ScopeCoinbaseAccountUniquenessToItem < ActiveRecord::Migration[7.2]
+ def up
+ return if index_exists?(:coinbase_accounts, [ :coinbase_item_id, :account_id ], unique: true, name: "index_coinbase_accounts_on_item_and_account_id")
+
+ if execute("SELECT 1 FROM coinbase_accounts WHERE account_id IS NOT NULL GROUP BY coinbase_item_id, account_id HAVING COUNT(*) > 1 LIMIT 1").any?
+ raise ActiveRecord::Migration::IrreversibleMigration,
+ "Duplicate (coinbase_item_id, account_id) pairs exist in coinbase_accounts. Resolve duplicates before running this migration."
+ end
+
+ add_index :coinbase_accounts,
+ [ :coinbase_item_id, :account_id ],
+ unique: true,
+ name: "index_coinbase_accounts_on_item_and_account_id",
+ where: "account_id IS NOT NULL"
+ end
+
+ def down
+ remove_index :coinbase_accounts, name: "index_coinbase_accounts_on_item_and_account_id", if_exists: true
+ end
+end
diff --git a/db/migrate/20260219200006_scope_lunchflow_account_uniqueness_to_item.rb b/db/migrate/20260219200006_scope_lunchflow_account_uniqueness_to_item.rb
new file mode 100644
index 000000000..236a29500
--- /dev/null
+++ b/db/migrate/20260219200006_scope_lunchflow_account_uniqueness_to_item.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# NEW constraint: add per-item unique index on lunchflow_accounts. Unlike Plaid/Snaptrade,
+# there was no prior unique index—this can fail if existing data has duplicate
+# (lunchflow_item_id, account_id) pairs. See: https://github.com/we-promise/sure/issues/740
+class ScopeLunchflowAccountUniquenessToItem < ActiveRecord::Migration[7.2]
+ def up
+ return if index_exists?(:lunchflow_accounts, [ :lunchflow_item_id, :account_id ], unique: true, name: "index_lunchflow_accounts_on_item_and_account_id")
+
+ if execute("SELECT 1 FROM lunchflow_accounts WHERE account_id IS NOT NULL GROUP BY lunchflow_item_id, account_id HAVING COUNT(*) > 1 LIMIT 1").any?
+ raise ActiveRecord::Migration::IrreversibleMigration,
+ "Duplicate (lunchflow_item_id, account_id) pairs exist in lunchflow_accounts. Resolve duplicates before running this migration."
+ end
+
+ add_index :lunchflow_accounts,
+ [ :lunchflow_item_id, :account_id ],
+ unique: true,
+ name: "index_lunchflow_accounts_on_item_and_account_id",
+ where: "account_id IS NOT NULL"
+ end
+
+ def down
+ remove_index :lunchflow_accounts, name: "index_lunchflow_accounts_on_item_and_account_id", if_exists: true
+ end
+end
diff --git a/db/migrate/20260303120000_backfill_investment_contribution_categories.rb b/db/migrate/20260303120000_backfill_investment_contribution_categories.rb
new file mode 100644
index 000000000..8a010d8b4
--- /dev/null
+++ b/db/migrate/20260303120000_backfill_investment_contribution_categories.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+class BackfillInvestmentContributionCategories < ActiveRecord::Migration[7.2]
+ def up
+ # PR #924 fixed auto-categorization of investment contributions going forward,
+ # but transfers created before that PR have kind = 'investment_contribution'
+ # with category_id NULL. This backfill assigns the correct category to those
+ # transactions using the family's existing "Investment Contributions" category.
+ #
+ # Safety:
+ # - Only updates transactions where category_id IS NULL (never overwrites user choices)
+ # - Only updates transactions that already have kind = 'investment_contribution'
+ # - Skips families that don't have an Investment Contributions category yet
+ # (it will be lazily created on their next new transfer)
+ # - If a family has duplicate locale-variant categories, picks the oldest one
+ # (matches Family#investment_contributions_category dedup behavior)
+
+ # Static snapshot of Category.all_investment_contributions_names at migration time.
+ # Inlined to avoid coupling to app code that may change after this migration ships.
+ locale_names = [
+ "Investment Contributions",
+ "Contributions aux investissements",
+ "Contribucions d'inversió",
+ "Investeringsbijdragen"
+ ]
+
+ quoted_names = locale_names.map { |n| connection.quote(n) }.join(", ")
+
+ say_with_time "Backfilling category for investment_contribution transactions" do
+ execute <<-SQL.squish
+ UPDATE transactions
+ SET category_id = matched_category.id
+ FROM entries, accounts,
+ LATERAL (
+ SELECT c.id
+ FROM categories c
+ WHERE c.family_id = accounts.family_id
+ AND c.name IN (#{quoted_names})
+ ORDER BY c.created_at ASC
+ LIMIT 1
+ ) AS matched_category
+ WHERE transactions.kind = 'investment_contribution'
+ AND transactions.category_id IS NULL
+ AND entries.entryable_id = transactions.id
+ AND entries.entryable_type = 'Transaction'
+ AND accounts.id = entries.account_id
+ SQL
+ end
+ end
+
+ def down
+ # No-op: we cannot distinguish backfilled records from ones that were
+ # categorized at creation time, so reverting would incorrectly clear
+ # legitimately assigned categories.
+ end
+end
diff --git a/db/migrate/20260305120000_add_default_account_to_users.rb b/db/migrate/20260305120000_add_default_account_to_users.rb
new file mode 100644
index 000000000..9bb6d0619
--- /dev/null
+++ b/db/migrate/20260305120000_add_default_account_to_users.rb
@@ -0,0 +1,5 @@
+class AddDefaultAccountToUsers < ActiveRecord::Migration[7.2]
+ def change
+ add_reference :users, :default_account, type: :uuid, foreign_key: { to_table: :accounts, on_delete: :nullify }, null: true
+ end
+end
diff --git a/db/migrate/20260308113006_remove_classification_from_categories.rb b/db/migrate/20260308113006_remove_classification_from_categories.rb
new file mode 100644
index 000000000..b2862fc29
--- /dev/null
+++ b/db/migrate/20260308113006_remove_classification_from_categories.rb
@@ -0,0 +1,9 @@
+class RemoveClassificationFromCategories < ActiveRecord::Migration[7.2]
+ def up
+ rename_column :categories, :classification, :classification_unused
+ end
+
+ def down
+ rename_column :categories, :classification_unused, :classification
+ end
+end
diff --git a/db/migrate/20260314120000_remove_unique_email_family_index_from_invitations.rb b/db/migrate/20260314120000_remove_unique_email_family_index_from_invitations.rb
new file mode 100644
index 000000000..d714a4143
--- /dev/null
+++ b/db/migrate/20260314120000_remove_unique_email_family_index_from_invitations.rb
@@ -0,0 +1,9 @@
+class RemoveUniqueEmailFamilyIndexFromInvitations < ActiveRecord::Migration[7.2]
+ def change
+ remove_index :invitations, [ :email, :family_id ], name: "index_invitations_on_email_and_family_id"
+ add_index :invitations, [ :email, :family_id ],
+ name: "index_invitations_on_email_and_family_id_pending",
+ unique: true,
+ where: "accepted_at IS NULL"
+ end
+end
diff --git a/db/migrate/20260314131357_create_archived_exports.rb b/db/migrate/20260314131357_create_archived_exports.rb
new file mode 100644
index 000000000..1fecb099e
--- /dev/null
+++ b/db/migrate/20260314131357_create_archived_exports.rb
@@ -0,0 +1,15 @@
+class CreateArchivedExports < ActiveRecord::Migration[7.2]
+ def change
+ create_table :archived_exports, id: :uuid do |t|
+ t.string :email, null: false
+ t.string :family_name
+ t.string :download_token_digest, null: false
+ t.datetime :expires_at, null: false
+
+ t.timestamps
+ end
+
+ add_index :archived_exports, :download_token_digest, unique: true
+ add_index :archived_exports, :expires_at
+ end
+end
diff --git a/db/migrate/20260316120000_create_vector_store_chunks.rb b/db/migrate/20260316120000_create_vector_store_chunks.rb
new file mode 100644
index 000000000..216768486
--- /dev/null
+++ b/db/migrate/20260316120000_create_vector_store_chunks.rb
@@ -0,0 +1,47 @@
+class CreateVectorStoreChunks < ActiveRecord::Migration[7.2]
+ def up
+ return unless pgvector_available?
+
+ enable_extension "vector" unless extension_enabled?("vector")
+
+ create_table :vector_store_chunks, id: :uuid do |t|
+ t.string :store_id, null: false
+ t.string :file_id, null: false
+ t.string :filename
+ t.integer :chunk_index, null: false, default: 0
+ t.text :content, null: false
+ t.column :embedding, "vector(#{ENV.fetch('EMBEDDING_DIMENSIONS', '1024')})", null: false
+ t.jsonb :metadata, null: false, default: {}
+ t.timestamps null: false
+ end
+
+ add_index :vector_store_chunks, :store_id
+ add_index :vector_store_chunks, :file_id
+ add_index :vector_store_chunks, [ :store_id, :file_id, :chunk_index ], unique: true,
+ name: "index_vector_store_chunks_on_store_file_chunk"
+ end
+
+ def down
+ drop_table :vector_store_chunks, if_exists: true
+ disable_extension "vector" if extension_enabled?("vector")
+ end
+
+ private
+
+ # Only run this migration when pgvector is explicitly configured as the
+ # vector store provider AND the extension is actually available on the
+ # PostgreSQL server. Previously we only checked server availability,
+ # which caused failures in production Docker environments where the
+ # extension may be present but the DB user lacks superuser privileges
+ # to enable it.
+ def pgvector_available?
+ return false unless ENV["VECTOR_STORE_PROVIDER"].to_s.downcase == "pgvector"
+
+ result = ActiveRecord::Base.connection.execute(
+ "SELECT 1 FROM pg_available_extensions WHERE name = 'vector' LIMIT 1"
+ )
+ result.any?
+ rescue
+ false
+ end
+end
diff --git a/db/migrate/20260320080659_add_parent_entry_id_to_entries.rb b/db/migrate/20260320080659_add_parent_entry_id_to_entries.rb
new file mode 100644
index 000000000..164b52567
--- /dev/null
+++ b/db/migrate/20260320080659_add_parent_entry_id_to_entries.rb
@@ -0,0 +1,6 @@
+class AddParentEntryIdToEntries < ActiveRecord::Migration[7.2]
+ def change
+ add_reference :entries, :parent_entry, type: :uuid, null: true,
+ foreign_key: { to_table: :entries, on_delete: :cascade }
+ end
+end
diff --git a/db/migrate/20260322120702_add_fee_to_trades.rb b/db/migrate/20260322120702_add_fee_to_trades.rb
new file mode 100644
index 000000000..09b9c91a2
--- /dev/null
+++ b/db/migrate/20260322120702_add_fee_to_trades.rb
@@ -0,0 +1,5 @@
+class AddFeeToTrades < ActiveRecord::Migration[7.2]
+ def change
+ add_column :trades, :fee, :decimal, precision: 19, scale: 4, default: 0, null: false
+ end
+end
diff --git a/db/migrate/20260324100000_add_account_sharing_support.rb b/db/migrate/20260324100000_add_account_sharing_support.rb
new file mode 100644
index 000000000..253606850
--- /dev/null
+++ b/db/migrate/20260324100000_add_account_sharing_support.rb
@@ -0,0 +1,21 @@
+class AddAccountSharingSupport < ActiveRecord::Migration[7.2]
+ def change
+ # Family-level default: whether new accounts are shared with all members by default
+ add_column :families, :default_account_sharing, :string, default: "shared", null: false
+
+ # Account ownership: who created/owns the account
+ add_reference :accounts, :owner, type: :uuid, foreign_key: { to_table: :users }, null: true, index: true
+
+ # Sharing join table: per-user access to accounts they don't own
+ create_table :account_shares, id: :uuid, default: -> { "gen_random_uuid()" } do |t|
+ t.references :account, type: :uuid, null: false, foreign_key: true
+ t.references :user, type: :uuid, null: false, foreign_key: true
+ t.string :permission, null: false, default: "read_only"
+ t.boolean :include_in_finances, null: false, default: true
+ t.timestamps
+ end
+
+ add_index :account_shares, [ :account_id, :user_id ], unique: true
+ add_index :account_shares, [ :user_id, :include_in_finances ]
+ end
+end
diff --git a/db/migrate/20260324100001_backfill_account_owners_and_shares.rb b/db/migrate/20260324100001_backfill_account_owners_and_shares.rb
new file mode 100644
index 000000000..8ed6b0416
--- /dev/null
+++ b/db/migrate/20260324100001_backfill_account_owners_and_shares.rb
@@ -0,0 +1,34 @@
+class BackfillAccountOwnersAndShares < ActiveRecord::Migration[7.2]
+ def up
+ # Existing families keep current behavior: all accounts shared
+ Family.update_all(default_account_sharing: "shared")
+
+ # For each family, assign all accounts to the admin (or first user)
+ Family.find_each do |family|
+ admin = family.users.find_by(role: %w[admin super_admin]) || family.users.order(:created_at).first
+ next unless admin
+
+ family.accounts.where(owner_id: nil).update_all(owner_id: admin.id)
+
+ # Create shares for non-owner members (preserves current full-access behavior)
+ member_ids = family.users.where.not(id: admin.id).pluck(:id)
+ account_ids = family.accounts.pluck(:id)
+
+ if member_ids.any? && account_ids.any?
+ records = member_ids.product(account_ids).map do |user_id, account_id|
+ { user_id: user_id, account_id: account_id, permission: "full_control",
+ include_in_finances: true, created_at: Time.current, updated_at: Time.current }
+ end
+
+ AccountShare.upsert_all(records, unique_by: %i[account_id user_id])
+ end
+ end
+
+ # Owner is enforced at the model level via before_validation callback
+ # Keeping nullable at DB level for backward compatibility with tests/seeds
+ end
+
+ def down
+ raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/db/migrate/20260324100002_add_check_constraints_to_sharing_columns.rb b/db/migrate/20260324100002_add_check_constraints_to_sharing_columns.rb
new file mode 100644
index 000000000..0a21a4113
--- /dev/null
+++ b/db/migrate/20260324100002_add_check_constraints_to_sharing_columns.rb
@@ -0,0 +1,6 @@
+class AddCheckConstraintsToSharingColumns < ActiveRecord::Migration[7.2]
+ def change
+ add_check_constraint :families, "default_account_sharing IN ('shared', 'private')", name: "chk_families_default_account_sharing"
+ add_check_constraint :account_shares, "permission IN ('full_control', 'read_write', 'read_only')", name: "chk_account_shares_permission"
+ end
+end
diff --git a/db/migrate/20260324100003_change_accounts_owner_fk_to_nullify.rb b/db/migrate/20260324100003_change_accounts_owner_fk_to_nullify.rb
new file mode 100644
index 000000000..bd2ce4298
--- /dev/null
+++ b/db/migrate/20260324100003_change_accounts_owner_fk_to_nullify.rb
@@ -0,0 +1,11 @@
+class ChangeAccountsOwnerFkToNullify < ActiveRecord::Migration[7.2]
+ def up
+ remove_foreign_key :accounts, :users, column: :owner_id
+ add_foreign_key :accounts, :users, column: :owner_id, on_delete: :nullify
+ end
+
+ def down
+ remove_foreign_key :accounts, :users, column: :owner_id
+ add_foreign_key :accounts, :users, column: :owner_id
+ end
+end
diff --git a/db/migrate/20260326112218_add_account_id_to_recurring_transactions.rb b/db/migrate/20260326112218_add_account_id_to_recurring_transactions.rb
new file mode 100644
index 000000000..d8289f59d
--- /dev/null
+++ b/db/migrate/20260326112218_add_account_id_to_recurring_transactions.rb
@@ -0,0 +1,64 @@
+class AddAccountIdToRecurringTransactions < ActiveRecord::Migration[7.2]
+ def up
+ add_reference :recurring_transactions, :account, type: :uuid, null: true, foreign_key: true
+
+ # Backfill account_id from the most recent matching entry
+ execute <<~SQL
+ UPDATE recurring_transactions rt
+ SET account_id = subquery.account_id
+ FROM (
+ SELECT DISTINCT ON (rt2.id) rt2.id AS recurring_transaction_id, e.account_id
+ FROM recurring_transactions rt2
+ JOIN entries e ON e.entryable_type = 'Transaction'
+ AND e.currency = rt2.currency
+ AND e.amount = rt2.amount
+ AND EXTRACT(DAY FROM e.date) BETWEEN GREATEST(rt2.expected_day_of_month - 2, 1) AND LEAST(rt2.expected_day_of_month + 2, 31)
+ JOIN accounts a ON a.id = e.account_id AND a.family_id = rt2.family_id
+ LEFT JOIN transactions t ON t.id = e.entryable_id
+ WHERE rt2.account_id IS NULL
+ AND (
+ (rt2.merchant_id IS NOT NULL AND t.merchant_id = rt2.merchant_id)
+ OR (rt2.merchant_id IS NULL AND e.name = rt2.name)
+ )
+ ORDER BY rt2.id, e.date DESC
+ ) subquery
+ WHERE rt.id = subquery.recurring_transaction_id
+ SQL
+
+ # Remove old unique indexes
+ remove_index :recurring_transactions, name: "idx_recurring_txns_merchant", if_exists: true
+ remove_index :recurring_transactions, name: "idx_recurring_txns_name", if_exists: true
+
+ # Add new unique indexes that include account_id
+ add_index :recurring_transactions,
+ [ :family_id, :account_id, :merchant_id, :amount, :currency ],
+ unique: true,
+ where: "merchant_id IS NOT NULL",
+ name: "idx_recurring_txns_acct_merchant"
+
+ add_index :recurring_transactions,
+ [ :family_id, :account_id, :name, :amount, :currency ],
+ unique: true,
+ where: "name IS NOT NULL AND merchant_id IS NULL",
+ name: "idx_recurring_txns_acct_name"
+ end
+
+ def down
+ remove_index :recurring_transactions, name: "idx_recurring_txns_acct_merchant", if_exists: true
+ remove_index :recurring_transactions, name: "idx_recurring_txns_acct_name", if_exists: true
+
+ add_index :recurring_transactions,
+ [ :family_id, :merchant_id, :amount, :currency ],
+ unique: true,
+ where: "merchant_id IS NOT NULL",
+ name: "idx_recurring_txns_merchant"
+
+ add_index :recurring_transactions,
+ [ :family_id, :name, :amount, :currency ],
+ unique: true,
+ where: "name IS NOT NULL AND merchant_id IS NULL",
+ name: "idx_recurring_txns_name"
+
+ remove_reference :recurring_transactions, :account
+ end
+end
diff --git a/db/migrate/20260327103000_add_exchange_portfolio_fields_to_coinstats_items.rb b/db/migrate/20260327103000_add_exchange_portfolio_fields_to_coinstats_items.rb
new file mode 100644
index 000000000..5b493ae31
--- /dev/null
+++ b/db/migrate/20260327103000_add_exchange_portfolio_fields_to_coinstats_items.rb
@@ -0,0 +1,11 @@
+class AddExchangePortfolioFieldsToCoinstatsItems < ActiveRecord::Migration[7.2]
+ def change
+ add_column :coinstats_items, :exchange_portfolio_id, :string
+ add_column :coinstats_items, :exchange_connection_id, :string
+
+ add_index :coinstats_items, [ :family_id, :exchange_portfolio_id ],
+ unique: true,
+ where: "exchange_portfolio_id IS NOT NULL"
+ add_index :coinstats_items, :exchange_connection_id
+ end
+end
diff --git a/db/migrate/20260327130000_increase_crypto_quantity_precision.rb b/db/migrate/20260327130000_increase_crypto_quantity_precision.rb
new file mode 100644
index 000000000..1288f9442
--- /dev/null
+++ b/db/migrate/20260327130000_increase_crypto_quantity_precision.rb
@@ -0,0 +1,11 @@
+class IncreaseCryptoQuantityPrecision < ActiveRecord::Migration[7.2]
+ def up
+ change_column :holdings, :qty, :decimal, precision: 24, scale: 8, null: false
+ change_column :trades, :qty, :decimal, precision: 24, scale: 8
+ end
+
+ def down
+ change_column :holdings, :qty, :decimal, precision: 19, scale: 4, null: false
+ change_column :trades, :qty, :decimal, precision: 19, scale: 4
+ end
+end
diff --git a/db/migrate/20260328120000_add_kind_to_securities.rb b/db/migrate/20260328120000_add_kind_to_securities.rb
new file mode 100644
index 000000000..3a31ff62a
--- /dev/null
+++ b/db/migrate/20260328120000_add_kind_to_securities.rb
@@ -0,0 +1,7 @@
+class AddKindToSecurities < ActiveRecord::Migration[7.2]
+ def change
+ add_column :securities, :kind, :string, null: false, default: "standard"
+ add_index :securities, :kind
+ add_check_constraint :securities, "kind IN ('standard', 'cash')", name: "chk_securities_kind"
+ end
+end
diff --git a/db/migrate/20260329111830_create_binance_items_and_accounts.rb b/db/migrate/20260329111830_create_binance_items_and_accounts.rb
new file mode 100644
index 000000000..949ca46cb
--- /dev/null
+++ b/db/migrate/20260329111830_create_binance_items_and_accounts.rb
@@ -0,0 +1,48 @@
+class CreateBinanceItemsAndAccounts < ActiveRecord::Migration[7.2]
+ def change
+ create_table :binance_items, id: :uuid do |t|
+ t.references :family, null: false, foreign_key: true, type: :uuid
+ t.string :name
+
+ t.string :institution_name
+ t.string :institution_domain
+ t.string :institution_url
+ t.string :institution_color
+
+ t.string :status, default: "good"
+ t.boolean :scheduled_for_deletion, default: false
+ t.boolean :pending_account_setup, default: false
+
+ t.datetime :sync_start_date
+ t.jsonb :raw_payload
+
+ t.text :api_key
+ t.text :api_secret
+
+ t.timestamps
+ end
+
+ add_index :binance_items, :status
+
+ create_table :binance_accounts, id: :uuid do |t|
+ t.references :binance_item, null: false, foreign_key: true, type: :uuid
+
+ t.string :name
+ t.string :account_type
+ t.string :currency
+ t.decimal :current_balance, precision: 19, scale: 4
+
+ t.jsonb :institution_metadata
+ t.jsonb :raw_payload
+ t.jsonb :raw_transactions_payload
+ t.jsonb :extra, default: {}, null: false
+
+ t.timestamps
+ end
+
+ add_index :binance_accounts, :account_type
+ add_index :binance_accounts, [ :binance_item_id, :account_type ],
+ unique: true,
+ name: "index_binance_accounts_on_item_and_type"
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f63254146..dfbb6be1a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
+ActiveRecord::Schema[7.2].define(version: 2026_03_30_050801) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"
@@ -29,6 +29,20 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.index ["provider_type", "provider_id"], name: "index_account_providers_on_provider_type_and_provider_id", unique: true
end
+ create_table "account_shares", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ t.uuid "account_id", null: false
+ t.uuid "user_id", null: false
+ t.string "permission", default: "read_only", null: false
+ t.boolean "include_in_finances", default: true, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["account_id", "user_id"], name: "index_account_shares_on_account_id_and_user_id", unique: true
+ t.index ["account_id"], name: "index_account_shares_on_account_id"
+ t.index ["user_id", "include_in_finances"], name: "index_account_shares_on_user_id_and_include_in_finances"
+ t.index ["user_id"], name: "index_account_shares_on_user_id"
+ t.check_constraint "permission::text = ANY (ARRAY['full_control'::character varying::text, 'read_write'::character varying::text, 'read_only'::character varying::text])", name: "chk_account_shares_permission"
+ end
+
create_table "accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "subtype"
t.uuid "family_id", null: false
@@ -49,8 +63,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.string "institution_name"
t.string "institution_domain"
t.text "notes"
- t.jsonb "holdings_snapshot_data"
- t.datetime "holdings_snapshot_at"
+ t.uuid "owner_id"
t.index ["accountable_id", "accountable_type"], name: "index_accounts_on_accountable_id_and_accountable_type"
t.index ["accountable_type"], name: "index_accounts_on_accountable_type"
t.index ["currency"], name: "index_accounts_on_currency"
@@ -60,6 +73,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.index ["family_id", "status"], name: "index_accounts_on_family_id_and_status"
t.index ["family_id"], name: "index_accounts_on_family_id"
t.index ["import_id"], name: "index_accounts_on_import_id"
+ t.index ["owner_id"], name: "index_accounts_on_owner_id"
t.index ["plaid_account_id"], name: "index_accounts_on_plaid_account_id"
t.index ["simplefin_account_id"], name: "index_accounts_on_simplefin_account_id"
t.index ["status"], name: "index_accounts_on_status"
@@ -125,6 +139,17 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.index ["user_id"], name: "index_api_keys_on_user_id"
end
+ create_table "archived_exports", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ t.string "email", null: false
+ t.string "family_name"
+ t.string "download_token_digest", null: false
+ t.datetime "expires_at", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["download_token_digest"], name: "index_archived_exports_on_download_token_digest", unique: true
+ t.index ["expires_at"], name: "index_archived_exports_on_expires_at"
+ end
+
create_table "balances", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "account_id", null: false
t.date "date", null: false
@@ -152,6 +177,43 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.index ["account_id"], name: "index_balances_on_account_id"
end
+ create_table "binance_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ t.uuid "binance_item_id", null: false
+ t.string "name"
+ t.string "account_type"
+ t.string "currency"
+ t.decimal "current_balance", precision: 19, scale: 4
+ t.jsonb "institution_metadata"
+ t.jsonb "raw_payload"
+ t.jsonb "raw_transactions_payload"
+ t.jsonb "extra", default: {}, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["account_type"], name: "index_binance_accounts_on_account_type"
+ t.index ["binance_item_id", "account_type"], name: "index_binance_accounts_on_item_and_type", unique: true
+ t.index ["binance_item_id"], name: "index_binance_accounts_on_binance_item_id"
+ end
+
+ create_table "binance_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ t.uuid "family_id", null: false
+ t.string "name"
+ t.string "institution_name"
+ t.string "institution_domain"
+ t.string "institution_url"
+ t.string "institution_color"
+ t.string "status", default: "good"
+ t.boolean "scheduled_for_deletion", default: false
+ t.boolean "pending_account_setup", default: false
+ t.datetime "sync_start_date"
+ t.jsonb "raw_payload"
+ t.text "api_key"
+ t.text "api_secret"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["family_id"], name: "index_binance_items_on_family_id"
+ t.index ["status"], name: "index_binance_items_on_status"
+ end
+
create_table "budget_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "budget_id", null: false
t.uuid "category_id", null: false
@@ -184,8 +246,8 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.uuid "parent_id"
- t.string "classification", default: "expense", null: false
t.string "lucide_icon", default: "shapes", null: false
+ t.string "classification_unused", default: "expense", null: false
t.index ["family_id"], name: "index_categories_on_family_id"
end
@@ -215,6 +277,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_coinbase_accounts_on_account_id"
+ t.index ["coinbase_item_id", "account_id"], name: "index_coinbase_accounts_on_item_and_account_id", unique: true, where: "(account_id IS NOT NULL)"
t.index ["coinbase_item_id"], name: "index_coinbase_accounts_on_coinbase_item_id"
end
@@ -276,6 +339,10 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.string "api_key", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.string "exchange_portfolio_id"
+ t.string "exchange_connection_id"
+ t.index ["exchange_connection_id"], name: "index_coinstats_items_on_exchange_connection_id"
+ t.index ["family_id", "exchange_portfolio_id"], name: "index_coinstats_items_on_family_id_and_exchange_portfolio_id", unique: true, where: "(exchange_portfolio_id IS NOT NULL)"
t.index ["family_id"], name: "index_coinstats_items_on_family_id"
t.index ["status"], name: "index_coinstats_items_on_status"
end
@@ -387,6 +454,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.string "source"
t.boolean "user_modified", default: false, null: false
t.boolean "import_locked", default: false, null: false
+ t.uuid "parent_entry_id"
t.index "lower((name)::text)", name: "index_entries_on_lower_name"
t.index ["account_id", "date"], name: "index_entries_on_account_id_and_date"
t.index ["account_id", "source", "external_id"], name: "index_entries_on_account_source_and_external_id", unique: true, where: "((external_id IS NOT NULL) AND (source IS NOT NULL))"
@@ -395,6 +463,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.index ["entryable_type"], name: "index_entries_on_entryable_type"
t.index ["import_id"], name: "index_entries_on_import_id"
t.index ["import_locked"], name: "index_entries_on_import_locked_true", where: "(import_locked = true)"
+ t.index ["parent_entry_id"], name: "index_entries_on_parent_entry_id"
t.index ["user_modified"], name: "index_entries_on_user_modified_true", where: "(user_modified = true)"
end
@@ -504,6 +573,8 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.string "vector_store_id"
t.string "moniker", default: "Family", null: false
t.string "assistant_type", default: "builtin", null: false
+ t.string "default_account_sharing", default: "shared", null: false
+ t.check_constraint "default_account_sharing::text = ANY (ARRAY['shared'::character varying::text, 'private'::character varying::text])", name: "chk_families_default_account_sharing"
t.check_constraint "month_start_day >= 1 AND month_start_day <= 28", name: "month_start_day_range"
end
@@ -545,7 +616,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.uuid "account_id", null: false
t.uuid "security_id", null: false
t.date "date", null: false
- t.decimal "qty", precision: 19, scale: 4, null: false
+ t.decimal "qty", precision: 24, scale: 8, null: false
t.decimal "price", precision: 19, scale: 4, null: false
t.decimal "amount", precision: 19, scale: 4, null: false
t.string "currency", null: false
@@ -693,8 +764,8 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.date "sync_start_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
- t.index ["indexa_capital_account_id"], name: "index_indexa_capital_accounts_on_indexa_capital_account_id", unique: true
t.index ["indexa_capital_authorization_id"], name: "idx_on_indexa_capital_authorization_id_58db208d52"
+ t.index ["indexa_capital_item_id", "indexa_capital_account_id"], name: "index_indexa_capital_accounts_on_item_and_account_id", unique: true, where: "(indexa_capital_account_id IS NOT NULL)"
t.index ["indexa_capital_item_id"], name: "index_indexa_capital_accounts_on_indexa_capital_item_id"
end
@@ -740,7 +811,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "token_digest"
- t.index ["email", "family_id"], name: "index_invitations_on_email_and_family_id", unique: true
+ t.index ["email", "family_id"], name: "index_invitations_on_email_and_family_id_pending", unique: true, where: "(accepted_at IS NULL)"
t.index ["email"], name: "index_invitations_on_email"
t.index ["family_id"], name: "index_invitations_on_family_id"
t.index ["inviter_id"], name: "index_invitations_on_inviter_id"
@@ -802,6 +873,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.boolean "holdings_supported", default: true, null: false
t.jsonb "raw_holdings_payload"
t.index ["account_id"], name: "index_lunchflow_accounts_on_account_id"
+ t.index ["lunchflow_item_id", "account_id"], name: "index_lunchflow_accounts_on_item_and_account_id", unique: true, where: "(account_id IS NOT NULL)"
t.index ["lunchflow_item_id"], name: "index_lunchflow_accounts_on_lunchflow_item_id"
end
@@ -859,7 +931,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.jsonb "raw_transactions_payload"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
- t.index ["account_id"], name: "index_mercury_accounts_on_account_id", unique: true
+ t.index ["mercury_item_id", "account_id"], name: "index_mercury_accounts_on_item_and_account_id", unique: true
t.index ["mercury_item_id"], name: "index_mercury_accounts_on_mercury_item_id"
end
@@ -1016,7 +1088,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.jsonb "raw_transactions_payload", default: {}
t.jsonb "raw_holdings_payload", default: {}
t.jsonb "raw_liabilities_payload", default: {}
- t.index ["plaid_id"], name: "index_plaid_accounts_on_plaid_id", unique: true
+ t.index ["plaid_item_id", "plaid_id"], name: "index_plaid_accounts_on_item_and_plaid_id", unique: true
t.index ["plaid_item_id"], name: "index_plaid_accounts_on_plaid_item_id"
end
@@ -1069,8 +1141,10 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.decimal "expected_amount_min", precision: 19, scale: 4
t.decimal "expected_amount_max", precision: 19, scale: 4
t.decimal "expected_amount_avg", precision: 19, scale: 4
- t.index ["family_id", "merchant_id", "amount", "currency"], name: "idx_recurring_txns_merchant", unique: true, where: "(merchant_id IS NOT NULL)"
- t.index ["family_id", "name", "amount", "currency"], name: "idx_recurring_txns_name", unique: true, where: "((name IS NOT NULL) AND (merchant_id IS NULL))"
+ t.uuid "account_id"
+ t.index ["account_id"], name: "index_recurring_transactions_on_account_id"
+ t.index ["family_id", "account_id", "merchant_id", "amount", "currency"], name: "idx_recurring_txns_acct_merchant", unique: true, where: "(merchant_id IS NOT NULL)"
+ t.index ["family_id", "account_id", "name", "amount", "currency"], name: "idx_recurring_txns_acct_name", unique: true, where: "((name IS NOT NULL) AND (merchant_id IS NULL))"
t.index ["family_id", "status"], name: "index_recurring_transactions_on_family_id_and_status"
t.index ["family_id"], name: "index_recurring_transactions_on_family_id"
t.index ["merchant_id"], name: "index_recurring_transactions_on_merchant_id"
@@ -1172,9 +1246,12 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.integer "failed_fetch_count", default: 0, null: false
t.datetime "last_health_check_at"
t.string "website_url"
+ t.string "kind", default: "standard", null: false
t.index "upper((ticker)::text), COALESCE(upper((exchange_operating_mic)::text), ''::text)", name: "index_securities_on_ticker_and_exchange_operating_mic_unique", unique: true
t.index ["country_code"], name: "index_securities_on_country_code"
t.index ["exchange_operating_mic"], name: "index_securities_on_exchange_operating_mic"
+ t.index ["kind"], name: "index_securities_on_kind"
+ t.check_constraint "kind::text = ANY (ARRAY['standard'::character varying, 'cash'::character varying]::text[])", name: "chk_securities_kind"
end
create_table "security_prices", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@@ -1262,7 +1339,6 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
create_table "snaptrade_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "snaptrade_item_id", null: false
t.string "name"
- t.string "account_id"
t.string "snaptrade_account_id"
t.string "snaptrade_authorization_id"
t.string "account_number"
@@ -1284,8 +1360,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.datetime "updated_at", null: false
t.boolean "activities_fetch_pending", default: false
t.date "sync_start_date"
- t.index ["account_id"], name: "index_snaptrade_accounts_on_account_id", unique: true
- t.index ["snaptrade_account_id"], name: "index_snaptrade_accounts_on_snaptrade_account_id", unique: true
+ t.index ["snaptrade_item_id", "snaptrade_account_id"], name: "index_snaptrade_accounts_on_item_and_snaptrade_account_id", unique: true, where: "(snaptrade_account_id IS NOT NULL)"
t.index ["snaptrade_item_id"], name: "index_snaptrade_accounts_on_snaptrade_item_id"
end
@@ -1416,21 +1491,15 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
create_table "trades", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "security_id", null: false
- t.decimal "qty", precision: 19, scale: 4
+ t.decimal "qty", precision: 24, scale: 8
t.decimal "price", precision: 19, scale: 10
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "currency"
t.jsonb "locked_attributes", default: {}
- t.decimal "realized_gain", precision: 19, scale: 4
- t.decimal "cost_basis_amount", precision: 19, scale: 4
- t.string "cost_basis_currency"
- t.integer "holding_period_days"
- t.string "realized_gain_confidence"
- t.string "realized_gain_currency"
t.string "investment_activity_label"
+ t.decimal "fee", precision: 19, scale: 4, default: "0.0", null: false
t.index ["investment_activity_label"], name: "index_trades_on_investment_activity_label"
- t.index ["realized_gain"], name: "index_trades_on_realized_gain_not_null", where: "(realized_gain IS NOT NULL)"
t.index ["security_id"], name: "index_trades_on_security_id"
end
@@ -1495,6 +1564,8 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
t.jsonb "preferences", default: {}, null: false
t.string "locale"
t.string "ui_layout"
+ t.uuid "default_account_id"
+ t.index ["default_account_id"], name: "index_users_on_default_account_id"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["family_id"], name: "index_users_on_family_id"
t.index ["last_viewed_chat_id"], name: "index_users_on_last_viewed_chat_id"
@@ -1523,14 +1594,19 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
end
add_foreign_key "account_providers", "accounts", on_delete: :cascade
+ add_foreign_key "account_shares", "accounts"
+ add_foreign_key "account_shares", "users"
add_foreign_key "accounts", "families"
add_foreign_key "accounts", "imports"
add_foreign_key "accounts", "plaid_accounts"
add_foreign_key "accounts", "simplefin_accounts"
+ add_foreign_key "accounts", "users", column: "owner_id", on_delete: :nullify
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "api_keys", "users"
add_foreign_key "balances", "accounts", on_delete: :cascade
+ add_foreign_key "binance_accounts", "binance_items"
+ add_foreign_key "binance_items", "families"
add_foreign_key "budget_categories", "budgets"
add_foreign_key "budget_categories", "categories"
add_foreign_key "budgets", "families"
@@ -1543,6 +1619,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
add_foreign_key "enable_banking_accounts", "enable_banking_items"
add_foreign_key "enable_banking_items", "families"
add_foreign_key "entries", "accounts", on_delete: :cascade
+ add_foreign_key "entries", "entries", column: "parent_entry_id", on_delete: :cascade
add_foreign_key "entries", "imports"
add_foreign_key "eval_results", "eval_runs"
add_foreign_key "eval_results", "eval_samples"
@@ -1579,6 +1656,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
add_foreign_key "pension_entries", "retirement_configs"
add_foreign_key "plaid_accounts", "plaid_items"
add_foreign_key "plaid_items", "families"
+ add_foreign_key "recurring_transactions", "accounts"
add_foreign_key "recurring_transactions", "families"
add_foreign_key "recurring_transactions", "merchants"
add_foreign_key "rejected_transfers", "transactions", column: "inflow_transaction_id"
@@ -1607,6 +1685,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_22_100001) do
add_foreign_key "transactions", "merchants"
add_foreign_key "transfers", "transactions", column: "inflow_transaction_id", on_delete: :cascade
add_foreign_key "transfers", "transactions", column: "outflow_transaction_id", on_delete: :cascade
+ add_foreign_key "users", "accounts", column: "default_account_id", on_delete: :nullify
add_foreign_key "users", "chats", column: "last_viewed_chat_id"
add_foreign_key "users", "families"
end
diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml
index 4a1515f90..e71c1568c 100644
--- a/docs/api/openapi.yaml
+++ b/docs/api/openapi.yaml
@@ -545,6 +545,13 @@ components:
properties:
message:
type: string
+ SuccessMessage:
+ type: object
+ required:
+ - message
+ properties:
+ message:
+ type: string
ImportConfiguration:
type: object
properties:
@@ -2718,3 +2725,66 @@ paths:
type: string
description: Additional notes
required: true
+ "/api/v1/users/reset":
+ delete:
+ summary: Reset account
+ tags:
+ - Users
+ description: Resets all financial data (accounts, categories, merchants, tags,
+ etc.) for the current user's family while keeping the user account intact.
+ The reset runs asynchronously in the background. Requires admin role.
+ security:
+ - apiKeyAuth: []
+ responses:
+ '200':
+ description: account reset initiated
+ content:
+ application/json:
+ schema:
+ "$ref": "#/components/schemas/SuccessMessage"
+ '401':
+ description: unauthorized
+ content:
+ application/json:
+ schema:
+ "$ref": "#/components/schemas/ErrorResponse"
+ '403':
+ description: "forbidden \u2014 requires read_write scope and admin role"
+ content:
+ application/json:
+ schema:
+ "$ref": "#/components/schemas/ErrorResponse"
+ "/api/v1/users/me":
+ delete:
+ summary: Delete account
+ tags:
+ - Users
+ description: Permanently deactivates the current user account and all associated
+ data. This action cannot be undone.
+ security:
+ - apiKeyAuth: []
+ responses:
+ '200':
+ description: account deleted
+ content:
+ application/json:
+ schema:
+ "$ref": "#/components/schemas/SuccessMessage"
+ '401':
+ description: unauthorized
+ content:
+ application/json:
+ schema:
+ "$ref": "#/components/schemas/ErrorResponse"
+ '403':
+ description: insufficient scope
+ content:
+ application/json:
+ schema:
+ "$ref": "#/components/schemas/ErrorResponse"
+ '422':
+ description: deactivation failed
+ content:
+ application/json:
+ schema:
+ "$ref": "#/components/schemas/ErrorResponse"
diff --git a/docs/api/users.md b/docs/api/users.md
new file mode 100644
index 000000000..d7f4f1c12
--- /dev/null
+++ b/docs/api/users.md
@@ -0,0 +1,117 @@
+# Users API Documentation
+
+The Users API allows external applications to manage user account data within Sure. The OpenAPI description is generated directly from executable request specs, ensuring it always reflects the behaviour of the running Rails application.
+
+## Generated OpenAPI specification
+
+- The source of truth for the documentation lives in [`spec/requests/api/v1/users_spec.rb`](../../spec/requests/api/v1/users_spec.rb). These specs authenticate against the Rails stack, exercise every user endpoint, and capture real response shapes.
+- Regenerate the OpenAPI document with:
+
+ ```sh
+ RAILS_ENV=test bundle exec rake rswag:specs:swaggerize
+ ```
+
+ The task compiles the request specs and writes the result to [`docs/api/openapi.yaml`](openapi.yaml).
+
+- Run just the documentation specs with:
+
+ ```sh
+ bundle exec rspec spec/requests/api/v1/users_spec.rb
+ ```
+
+## Authentication requirements
+
+All user endpoints require an OAuth2 access token or API key that grants the `read_write` scope.
+
+## Available endpoints
+
+| Endpoint | Scope | Description |
+| --- | --- | --- |
+| `DELETE /api/v1/users/reset` | `read_write` | Reset account data while preserving the user account. |
+| `DELETE /api/v1/users/me` | `read_write` | Permanently delete the user account. |
+
+Refer to the generated [`openapi.yaml`](openapi.yaml) for request/response schemas, reusable components (errors), and security definitions.
+
+## Reset account
+
+`DELETE /api/v1/users/reset`
+
+Resets all financial data (accounts, categories, merchants, tags, transactions, etc.) for the current user's family while keeping the user account intact. The reset runs asynchronously in the background.
+
+### Request
+
+No request body required.
+
+### Response
+
+```json
+{
+ "message": "Account reset has been initiated"
+}
+```
+
+### Use cases
+
+- Clear all financial data to start fresh
+- Remove test data after initial setup
+- Reset to a clean state for new imports
+
+## Delete account
+
+`DELETE /api/v1/users/me`
+
+Permanently deactivates the current user account and all associated data. This action cannot be undone.
+
+### Request
+
+No request body required.
+
+### Response
+
+```json
+{
+ "message": "Account has been deleted"
+}
+```
+
+### Error responses
+
+In addition to standard error codes (`unauthorized`, `insufficient_scope`), the delete endpoint may return:
+
+**422 Unprocessable Entity**
+
+```json
+{
+ "error": "Failed to delete account",
+ "details": ["Cannot deactivate admin with other users"]
+}
+```
+
+This occurs when the user cannot be deactivated (for example, an admin user with other active users in the family).
+
+## Security considerations
+
+- Both endpoints require the `read_write` scope. Read-only API keys cannot access these endpoints.
+- Deactivated users cannot access these endpoints.
+- The reset operation preserves the user account, allowing you to continue using Sure with a clean slate.
+- The delete operation is permanent and removes the user account entirely.
+
+## Error responses
+
+Errors conform to the shared `ErrorResponse` schema in the OpenAPI document:
+
+```json
+{
+ "error": "error_code",
+ "message": "Human readable error message",
+ "details": ["Optional array of extra context"]
+}
+```
+
+Common error codes include:
+
+| Code | Description |
+| --- | --- |
+| `unauthorized` | Missing or invalid API key |
+| `insufficient_scope` | API key lacks required `read_write` scope |
+| `Failed to delete account` | Account deletion failed (see details field) |
\ No newline at end of file
diff --git a/docs/hosting/ai.md b/docs/hosting/ai.md
index 0e6d56d1f..f13f79046 100644
--- a/docs/hosting/ai.md
+++ b/docs/hosting/ai.md
@@ -11,6 +11,30 @@ Sure includes an AI assistant that can help users understand their financial dat
> 👉 Help us by taking a structured approach to your issue reporting. 🙏
+## Architecture: Two AI Pipelines
+
+Sure has **two separate AI systems** that operate independently. Understanding this is important because they have different configuration requirements.
+
+### 1. Chat Assistant (conversational)
+
+The interactive chat where users ask questions about their finances. Routes through one of two backends:
+
+- **Builtin** (default): Uses the OpenAI-compatible provider configured via `OPENAI_ACCESS_TOKEN` / `OPENAI_URI_BASE` / `OPENAI_MODEL`. Calls Sure's function tools directly (get_accounts, get_transactions, etc.).
+- **External**: Delegates the entire conversation to a remote AI agent. The agent calls back to Sure via MCP to access financial data. Set `ASSISTANT_TYPE=external` as a global override, or configure each family's assistant type in Settings.
+
+### 2. Auto-Categorization and Merchant Detection (background)
+
+Background jobs that classify transactions and detect merchants. These **always** use the OpenAI-compatible provider (`OPENAI_ACCESS_TOKEN`), regardless of what the chat assistant uses. They rely on structured function calling with JSON schemas, not conversational chat.
+
+### What this means in practice
+
+| Setting | Chat assistant | Auto-categorization |
+|---------|---------------|---------------------|
+| `ASSISTANT_TYPE=builtin` (default) | Uses OpenAI provider | Uses OpenAI provider |
+| `ASSISTANT_TYPE=external` | Uses external agent | Still uses OpenAI provider |
+
+If you use an external agent for chat, you still need `OPENAI_ACCESS_TOKEN` set for auto-categorization and merchant detection to work. The two systems are fully independent.
+
## Quickstart: OpenAI Token
The easiest way to get started with AI features in Sure is to use OpenAI:
@@ -288,7 +312,436 @@ For self-hosted deployments, you can configure AI settings through the web inter
- **OpenAI URI Base** - Custom endpoint (leave blank for OpenAI)
- **OpenAI Model** - Model name (required for custom endpoints)
-**Note:** Settings in the UI override environment variables. If you change settings in the UI, those values take precedence.
+**Note:** Environment variables take precedence over UI settings. When an env var is set, the corresponding UI field is disabled.
+
+## External AI Assistant
+
+Instead of using the built-in LLM (which calls OpenAI or a local model directly), you can delegate chat to an **external AI agent**. The agent receives the conversation, can call back to Sure's financial data via MCP, and streams a response.
+
+This is useful when:
+- You have a custom AI agent with domain knowledge, memory, or personality
+- You want to use a non-OpenAI-compatible model (the agent translates)
+- You want to keep LLM credentials and logic outside Sure entirely
+
+> [!IMPORTANT]
+> **Set `ASSISTANT_TYPE=external` to route all users to the external agent.** Without it, routing falls back to each family's `assistant_type` DB column (configurable per-family in the Settings UI), then defaults to `"builtin"`. If you want a global override that applies to every family regardless of their UI setting, set the env var. If you only want specific families to use the external agent, skip the env var and configure it per-family in Settings.
+
+> [!NOTE]
+> The external assistant handles **chat only**. Auto-categorization and merchant detection still use the OpenAI-compatible provider (`OPENAI_ACCESS_TOKEN`). See [Architecture: Two AI Pipelines](#architecture-two-ai-pipelines) for details.
+
+### How It Works
+
+1. User sends a message in the Sure chat UI
+2. Sure sends the conversation to your agent's API endpoint (OpenAI chat completions format)
+3. Your agent processes it using whatever LLM, tools, or context it needs
+4. Your agent can call Sure's `/mcp` endpoint for financial data (accounts, transactions, balance sheet, holdings)
+5. Your agent streams the response back to Sure via Server-Sent Events (SSE)
+
+The agent's API must be **OpenAI chat completions compatible**: accept `POST` with a `messages` array, return SSE with `delta.content` chunks.
+
+### Configuration
+
+Configure via the UI or environment variables:
+
+**Settings UI:**
+1. Go to **Settings** -> **Self-Hosting**
+2. Set **Assistant type** to "External (remote agent)"
+3. Enter the **Endpoint URL** and **API Token** from your agent provider
+4. Optionally set an **Agent ID** if the provider hosts multiple agents
+
+**Environment variables:**
+```bash
+ASSISTANT_TYPE=external # Global override (or set per-family in UI)
+EXTERNAL_ASSISTANT_URL=https://your-agent/v1/chat/completions
+EXTERNAL_ASSISTANT_TOKEN=your-api-token
+EXTERNAL_ASSISTANT_AGENT_ID=main # Optional, defaults to "main"
+EXTERNAL_ASSISTANT_SESSION_KEY=agent:main:main # Optional, for session persistence
+EXTERNAL_ASSISTANT_ALLOWED_EMAILS=user@example.com # Optional, comma-separated allowlist
+```
+
+When environment variables are set, the corresponding UI fields are disabled (env takes precedence).
+
+### MCP Callback Endpoint
+
+Sure exposes a [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) endpoint at `/mcp` so your external agent can call back and query financial data. This is how the agent accesses accounts, transactions, balance sheets, and other user data.
+
+**Protocol:** JSON-RPC 2.0 over HTTP POST
+
+**Authentication:** Bearer token via `Authorization` header
+
+**Environment variables:**
+```bash
+MCP_API_TOKEN=your-secret-token # Bearer token the agent sends to authenticate
+MCP_USER_EMAIL=user@example.com # Email of the Sure user the agent acts as
+```
+
+The agent must send requests to `https://your-sure-instance/mcp` with:
+```
+Authorization: Bearer
+Content-Type: application/json
+```
+
+**Supported methods:**
+
+| Method | Description |
+|--------|-------------|
+| `initialize` | Handshake, returns server info and capabilities |
+| `tools/list` | Lists available tools with names, descriptions, and input schemas |
+| `tools/call` | Calls a specific tool by name with arguments |
+
+**Available tools** (exposed via `tools/list`):
+
+| Tool | Description |
+|------|-------------|
+| `get_accounts` | Retrieve account information |
+| `get_transactions` | Query transaction history |
+| `get_holdings` | Investment holdings data |
+| `get_balance_sheet` | Current financial position |
+| `get_income_statement` | Income and expenses |
+| `import_bank_statement` | Import bank statement data |
+| `search_family_files` | Search uploaded documents |
+
+**Example: list tools**
+```bash
+curl -X POST https://your-sure-instance/mcp \
+ -H "Authorization: Bearer $MCP_API_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
+```
+
+**Example: call a tool**
+```bash
+curl -X POST https://your-sure-instance/mcp \
+ -H "Authorization: Bearer $MCP_API_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_accounts","arguments":{}}}'
+```
+
+### OpenClaw Gateway Example
+
+[OpenClaw](https://github.com/luckyPipewrench/openclaw) is an AI agent gateway that exposes agents as OpenAI-compatible endpoints. If your agent runs behind OpenClaw, configure it like this:
+
+```bash
+ASSISTANT_TYPE=external
+EXTERNAL_ASSISTANT_URL=http://your-openclaw-host:18789/v1/chat/completions
+EXTERNAL_ASSISTANT_TOKEN=your-gateway-token
+EXTERNAL_ASSISTANT_AGENT_ID=your-agent-name
+```
+
+**OpenClaw setup requirements:**
+- The gateway must have `chatCompletions.enabled: true` in its config
+- The agent's MCP config must point to Sure's `/mcp` endpoint with the correct `MCP_API_TOKEN`
+- The URL format is always `/v1/chat/completions` (OpenAI-compatible)
+
+**Kubernetes in-cluster example** (agent in a different namespace):
+```bash
+# URL uses Kubernetes DNS: ..svc.cluster.local:
+EXTERNAL_ASSISTANT_URL=http://my-agent.my-namespace.svc.cluster.local:18789/v1/chat/completions
+```
+
+### Security with Pipelock
+
+When [Pipelock](https://github.com/luckyPipewrench/pipelock) is enabled (`pipelock.enabled=true` in Helm, or the `pipelock` service in Docker Compose), all traffic between Sure and the external agent is scanned:
+
+- **Outbound** (Sure -> agent): routed through Pipelock's forward proxy via `HTTPS_PROXY`
+- **Inbound** (agent -> Sure /mcp): routed through Pipelock's MCP reverse proxy (port 8889)
+
+Pipelock scans for prompt injection, DLP violations, and tool poisoning. The external agent does not need Pipelock installed. Sure's Pipelock handles both directions.
+
+**`NO_PROXY` behavior (Helm/Kubernetes only):** The Helm chart's env template sets `NO_PROXY` to include `.svc.cluster.local` and other internal domains. This means in-cluster agent URLs (like `http://agent.namespace.svc.cluster.local:18789`) bypass the forward proxy and go directly. If your agent is in-cluster, its traffic won't be forward-proxy scanned (but MCP callbacks from the agent are still scanned by the reverse proxy). Docker Compose deployments use a different `NO_PROXY` set; check your compose file for the exact values.
+
+**`mcpToolPolicy` note:** The Helm chart's `pipelock.mcpToolPolicy.enabled` defaults to `true`. If you haven't defined any policy rules, disable it:
+
+```yaml
+# Helm values
+pipelock:
+ mcpToolPolicy:
+ enabled: false
+```
+
+See the [Pipelock documentation](https://github.com/luckyPipewrench/pipelock) for tool policy configuration details.
+
+### Network Policies (Kubernetes)
+
+If you use Kubernetes NetworkPolicies (and you should), both Sure and the agent's namespace need rules to allow traffic in both directions.
+
+> [!WARNING]
+> **Port number gotcha:** Kubernetes network policies evaluate **after** kube-proxy DNAT. This means egress rules must use the pod's `targetPort`, not the service port. If your agent's Service maps port 18789 to targetPort 18790, the network policy must allow port **18790**.
+
+**Sure namespace egress** (Sure calling the agent):
+```yaml
+# Allow Sure -> agent namespace
+- to:
+ - namespaceSelector:
+ matchLabels:
+ kubernetes.io/metadata.name: agent-namespace
+ ports:
+ - protocol: TCP
+ port: 18790 # targetPort, not service port!
+```
+
+**Sure namespace ingress** (agent calling Sure's pipelock MCP reverse proxy):
+```yaml
+# Allow agent -> Sure pipelock MCP reverse proxy
+- from:
+ - namespaceSelector:
+ matchLabels:
+ kubernetes.io/metadata.name: agent-namespace
+ ports:
+ - protocol: TCP
+ port: 8889
+```
+
+**Agent namespace** needs the reverse: egress to Sure on port 8889, ingress from Sure on its listening port.
+
+### Access Control
+
+Use `EXTERNAL_ASSISTANT_ALLOWED_EMAILS` to restrict which users can use the external assistant. When set, only users whose email matches the comma-separated list will see the AI chat. When blank, all users can access it.
+
+### Docker Compose Example
+
+```yaml
+x-rails-env: &rails_env
+ ASSISTANT_TYPE: external
+ EXTERNAL_ASSISTANT_URL: https://your-agent/v1/chat/completions
+ EXTERNAL_ASSISTANT_TOKEN: your-api-token
+ MCP_API_TOKEN: your-mcp-token # For agent callback
+ MCP_USER_EMAIL: user@example.com # User the agent acts as
+```
+
+Or configure the assistant via the Settings UI after startup (MCP env vars are still required for callback).
+
+## Assistant Architecture
+
+Sure's AI assistant system uses a modular architecture that allows different assistant implementations to be plugged in based on configuration. This section explains the architecture for contributors who want to understand or extend the system.
+
+### Overview
+
+The assistant system evolved from a monolithic class to a module-based architecture with a registry pattern. This allows Sure to support multiple assistant types (builtin, external) and makes it easy to add new implementations.
+
+**Key benefits:**
+- **Extensible:** Add new assistant types without modifying existing code
+- **Configurable:** Choose assistant type per family or globally
+- **Isolated:** Each implementation has its own logic and dependencies
+- **Testable:** Implementations are independent and can be tested separately
+
+### Component Hierarchy
+
+#### `Assistant` Module
+
+The main entry point for all assistant operations. Located in `app/models/assistant.rb`.
+
+**Key methods:**
+
+| Method | Description |
+|--------|-------------|
+| `.for_chat(chat)` | Returns the appropriate assistant instance for a chat |
+| `.config_for(chat)` | Returns configuration for builtin assistants |
+| `.available_types` | Lists all registered assistant types |
+| `.function_classes` | Returns all available function/tool classes |
+
+**Example usage:**
+
+```ruby
+# Get an assistant for a chat
+assistant = Assistant.for_chat(chat)
+
+# Respond to a message
+assistant.respond_to(message)
+```
+
+#### `Assistant::Base`
+
+Abstract base class that all assistant implementations inherit from. Located in `app/models/assistant/base.rb`.
+
+**Contract:**
+- Must implement `respond_to(message)` instance method
+- Includes `Assistant::Broadcastable` for real-time updates
+- Receives the `chat` object in the initializer
+
+**Example implementation:**
+
+```ruby
+class Assistant::MyCustom < Assistant::Base
+ def respond_to(message)
+ # Your custom logic here
+ assistant_message = AssistantMessage.new(chat: chat, content: "Response")
+ assistant_message.save!
+ end
+end
+```
+
+#### `Assistant::Builtin`
+
+The default implementation that uses the configured OpenAI-compatible LLM provider. Located in `app/models/assistant/builtin.rb`.
+
+**Features:**
+- Uses `Assistant::Provided` for LLM provider selection
+- Uses `Assistant::Configurable` for system prompts and function configuration
+- Supports function calling via `Assistant::FunctionToolCaller`
+- Streams responses in real-time
+
+**Key methods:**
+
+| Method | Description |
+|--------|-------------|
+| `.for_chat(chat)` | Creates a new builtin assistant with config |
+| `#respond_to(message)` | Processes a message using the LLM |
+
+#### `Assistant::External`
+
+Implementation for delegating chat to a remote AI agent. Located in `app/models/assistant/external.rb`.
+
+**Features:**
+- Sends conversation to external agent via OpenAI-compatible API
+- Agent calls back to Sure's `/mcp` endpoint for financial data
+- Supports access control via email allowlist
+- Streams responses from the agent
+
+**Configuration:**
+
+```ruby
+config = Assistant::External.config
+# => #
+```
+
+### Registry Pattern
+
+The `Assistant` module uses a registry to map type names to implementation classes:
+
+```ruby
+REGISTRY = {
+ "builtin" => Assistant::Builtin,
+ "external" => Assistant::External
+}.freeze
+```
+
+**Type selection logic:**
+
+1. Check `ENV["ASSISTANT_TYPE"]` (global override)
+2. Check `chat.user.family.assistant_type` (per-family setting)
+3. Default to `"builtin"`
+
+**Example:**
+
+```ruby
+# Global override
+ENV["ASSISTANT_TYPE"] = "external"
+Assistant.for_chat(chat) # => Assistant::External instance
+
+# Per-family setting
+family.update(assistant_type: "external")
+Assistant.for_chat(chat) # => Assistant::External instance
+
+# Default
+Assistant.for_chat(chat) # => Assistant::Builtin instance
+```
+
+### Function Registry
+
+The `Assistant.function_classes` method centralizes all available financial tools:
+
+```ruby
+def self.function_classes
+ [
+ Function::GetTransactions,
+ Function::GetAccounts,
+ Function::GetHoldings,
+ Function::GetBalanceSheet,
+ Function::GetIncomeStatement,
+ Function::ImportBankStatement,
+ Function::SearchFamilyFiles
+ ]
+end
+```
+
+These functions are:
+- Used by builtin assistants for LLM function calling
+- Exposed via the MCP endpoint for external agents
+- Defined in `app/models/assistant/function/`
+
+### Adding a New Assistant Type
+
+To add a custom assistant implementation:
+
+#### 1. Create the implementation class
+
+```ruby
+# app/models/assistant/my_custom.rb
+class Assistant::MyCustom < Assistant::Base
+ class << self
+ def for_chat(chat)
+ new(chat)
+ end
+ end
+
+ def respond_to(message)
+ # Your implementation here
+ # Must create and save an AssistantMessage
+ assistant_message = AssistantMessage.new(
+ chat: chat,
+ content: "My custom response"
+ )
+ assistant_message.save!
+ end
+end
+```
+
+#### 2. Register the implementation
+
+```ruby
+# app/models/assistant.rb
+REGISTRY = {
+ "builtin" => Assistant::Builtin,
+ "external" => Assistant::External,
+ "my_custom" => Assistant::MyCustom
+}.freeze
+```
+
+#### 3. Add validation
+
+```ruby
+# app/models/family.rb
+ASSISTANT_TYPES = %w[builtin external my_custom].freeze
+```
+
+#### 4. Use the new type
+
+```bash
+# Global override
+ASSISTANT_TYPE=my_custom
+
+# Or set per-family in the database
+family.update(assistant_type: "my_custom")
+```
+
+### Integration Points
+
+#### Pipelock Integration
+
+For external assistants, Pipelock can scan traffic:
+- **Outbound:** Sure -> agent (via `HTTPS_PROXY`)
+- **Inbound:** Agent -> Sure /mcp (via MCP reverse proxy on port 8889)
+
+See the [External AI Assistant](#external-ai-assistant) and [Pipelock](pipelock.md) documentation for configuration.
+
+#### OpenClaw/WebSocket Support
+
+The `Assistant::External` implementation currently uses HTTP streaming. Future implementations could use WebSocket connections via OpenClaw or other gateways.
+
+**Example future implementation:**
+
+```ruby
+class Assistant::WebSocket < Assistant::Base
+ def respond_to(message)
+ # Connect via WebSocket
+ # Stream bidirectional communication
+ # Handle tool calls via MCP
+ end
+end
+```
+
+Register it in the `REGISTRY` and add to `Family::ASSISTANT_TYPES` to activate.
## AI Cache Management
@@ -589,6 +1042,42 @@ ollama pull model-name # Install a model
3. Restart Sure after changing environment variables
4. Check logs for specific error messages
+### "Failed to generate response" with External Assistant
+
+**Symptom:** Chat shows "Failed to generate response" when expecting the external assistant
+
+**Check in order:**
+
+1. **Is external routing active?** Sure uses external mode when `ASSISTANT_TYPE=external` is set as an env var, OR when the family's `assistant_type` is set to "external" in Settings. Check what the pod sees:
+ ```bash
+ kubectl exec deploy/sure-web -c rails -- env | grep ASSISTANT_TYPE
+ kubectl exec deploy/sure-worker -c sidekiq -- env | grep ASSISTANT_TYPE
+ ```
+ If the env var is unset, check the family setting in the database or Settings UI.
+
+2. **Can Sure reach the agent?** Test from inside the worker pod (use `sh -c` so the env var expands inside the pod, not locally):
+ ```bash
+ kubectl exec deploy/sure-worker -c sidekiq -- \
+ sh -c 'curl -s -o /dev/null -w "%{http_code}" \
+ -H "Authorization: Bearer $EXTERNAL_ASSISTANT_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d "{\"model\":\"test\",\"messages\":[{\"role\":\"user\",\"content\":\"ping\"}]}" \
+ $EXTERNAL_ASSISTANT_URL'
+ ```
+ - **Exit code 7 (connection refused):** Network policy is blocking. Check egress rules, and remember to use the `targetPort`, not the service port.
+ - **HTTP 401/403:** Token mismatch between Sure's `EXTERNAL_ASSISTANT_TOKEN` and the agent's expected token.
+ - **HTTP 404:** Wrong URL path. Must be `/v1/chat/completions`.
+
+3. **Check worker logs** for the actual error:
+ ```bash
+ kubectl logs deploy/sure-worker -c sidekiq --tail=50 | grep -i "external\|assistant\|error"
+ ```
+
+4. **If using Pipelock:** Check pipelock sidecar logs. A crashed pipelock can block outbound requests:
+ ```bash
+ kubectl logs deploy/sure-worker -c pipelock --tail=20
+ ```
+
### High Costs
**Symptom:** Unexpected bills from cloud provider
@@ -608,7 +1097,7 @@ ollama pull model-name # Install a model
### Custom System Prompts
-Sure's AI assistant uses a system prompt that defines its behavior. The prompt is defined in `app/models/assistant/configurable.rb`.
+The builtin AI assistant uses a system prompt that defines its behavior. The prompt is defined in `app/models/assistant/configurable.rb`. This does not apply to external assistants, which manage their own prompts.
To customize:
1. Fork the repository
@@ -628,8 +1117,11 @@ The assistant uses OpenAI's function calling (tool use) to access user data:
**Available functions:**
- `get_transactions` - Retrieve transaction history
- `get_accounts` - Get account information
+- `get_holdings` - Investment holdings data
- `get_balance_sheet` - Current financial position
- `get_income_statement` - Income and expenses
+- `import_bank_statement` - Import bank statement data
+- `search_family_files` - Search uploaded documents
These are defined in `app/models/assistant/function/`.
@@ -648,7 +1140,7 @@ Sure's AI assistant can search documents that have been uploaded to a family's v
| Backend | Status | Best For | Requirements |
|---------|--------|----------|--------------|
| **OpenAI** (default) | ready | Cloud deployments, zero setup | `OPENAI_ACCESS_TOKEN` |
-| **Pgvector** | scaffolded | Self-hosted, full data privacy | PostgreSQL with `pgvector` extension |
+| **Pgvector** | ready | Self-hosted, full data privacy | PostgreSQL with `pgvector` extension + embedding model |
| **Qdrant** | scaffolded | Self-hosted, dedicated vector DB | Running Qdrant instance |
#### Configuration
@@ -658,22 +1150,35 @@ Sure's AI assistant can search documents that have been uploaded to a family's v
No extra configuration is needed. If you already have `OPENAI_ACCESS_TOKEN` set for the AI assistant, document search works automatically. OpenAI manages chunking, embedding, and retrieval.
```bash
-# Already set for AI chat — document search uses the same token
+# Already set for AI chat - document search uses the same token
OPENAI_ACCESS_TOKEN=sk-proj-...
```
##### Pgvector (Self-Hosted)
-> [!CAUTION]
-> Only `OpenAI` has been implemented!
+Use PostgreSQL's pgvector extension for fully local document search. All data stays on your infrastructure.
-Use PostgreSQL's pgvector extension for fully local document search:
+**Requirements:**
+- Use the `pgvector/pgvector:pg16` Docker image instead of `postgres:16` (drop-in replacement)
+- An embedding model served via an OpenAI-compatible `/v1/embeddings` endpoint (e.g. Ollama with `nomic-embed-text`)
+- Run the migration with `VECTOR_STORE_PROVIDER=pgvector` to create the `vector_store_chunks` table
```bash
+# Required
VECTOR_STORE_PROVIDER=pgvector
+
+# Embedding model configuration
+EMBEDDING_MODEL=nomic-embed-text # Default: nomic-embed-text
+EMBEDDING_DIMENSIONS=1024 # Default: 1024 (must match your model)
+EMBEDDING_URI_BASE=http://ollama:11434/v1 # Falls back to OPENAI_URI_BASE if not set
+EMBEDDING_ACCESS_TOKEN= # Falls back to OPENAI_ACCESS_TOKEN if not set
```
-> **Note:** The pgvector adapter is currently a skeleton. A future release will add full support including embedding model configuration.
+If you are using Ollama (as in `compose.example.ai.yml`), pull the embedding model:
+
+```bash
+docker compose exec ollama ollama pull nomic-embed-text
+```
##### Qdrant (Self-Hosted)
@@ -777,4 +1282,4 @@ For issues with AI features:
---
-**Last Updated:** October 2025
+**Last Updated:** March 2026
diff --git a/docs/hosting/docker.md b/docs/hosting/docker.md
index 8fd6a25cf..09114ed09 100644
--- a/docs/hosting/docker.md
+++ b/docs/hosting/docker.md
@@ -152,6 +152,62 @@ Your app is now set up. You can visit it at `http://localhost:3000` in your brow
If you find bugs or have a feature request, be sure to read through our [contributing guide here](https://github.com/we-promise/sure/wiki/How-to-Contribute-Effectively-to-Sure).
+## AI features, external assistant, and Pipelock
+
+Sure ships with a separate compose file for AI-related features: `compose.example.ai.yml`. It adds:
+
+- **Pipelock** (always on): AI agent security proxy that scans outbound LLM calls and inbound MCP traffic
+- **Ollama + Open WebUI** (optional `--profile ai`): local LLM inference
+
+### Using the AI compose file
+
+```bash
+# Download both compose files
+curl -o compose.yml https://raw.githubusercontent.com/we-promise/sure/main/compose.example.yml
+curl -o compose.ai.yml https://raw.githubusercontent.com/we-promise/sure/main/compose.example.ai.yml
+curl -o pipelock.example.yaml https://raw.githubusercontent.com/we-promise/sure/main/pipelock.example.yaml
+
+# Run with Pipelock (no local LLM)
+docker compose -f compose.ai.yml up -d
+
+# Run with Pipelock + Ollama
+docker compose -f compose.ai.yml --profile ai up -d
+```
+
+### Setting up the external AI assistant
+
+The external assistant delegates chat to a remote AI agent instead of calling LLMs directly. The agent calls back to Sure's `/mcp` endpoint for financial data (accounts, transactions, balance sheet).
+
+1. Set the MCP endpoint credentials in your `.env`:
+ ```bash
+ MCP_API_TOKEN=generate-a-random-token-here
+ MCP_USER_EMAIL=your@email.com # must match an existing Sure user
+ ```
+
+2. Set the external assistant connection:
+ ```bash
+ EXTERNAL_ASSISTANT_URL=https://your-agent/v1/chat/completions
+ EXTERNAL_ASSISTANT_TOKEN=your-agent-api-token
+ ```
+
+3. Choose how to activate:
+ - **Per-family (UI):** Go to Settings > Self-Hosting > AI Assistant, select "External"
+ - **Global (env):** Set `ASSISTANT_TYPE=external` to force all families to use external
+
+See [docs/hosting/ai.md](ai.md) for full configuration details including agent ID, session keys, and email allowlisting.
+
+### Pipelock security proxy
+
+Pipelock sits between Sure and external services, scanning AI traffic for:
+
+- **Secret exfiltration** (DLP): catches API keys, tokens, or personal data leaking in prompts
+- **Prompt injection**: detects attempts to override system instructions
+- **Tool poisoning**: validates MCP tool calls against known-safe patterns
+
+When using `compose.example.ai.yml`, Pipelock is always running. External AI agents should connect to port 8889 (MCP reverse proxy) instead of directly to Sure's `/mcp` on port 3000.
+
+For full Pipelock configuration, see [docs/hosting/pipelock.md](pipelock.md).
+
## How to update your app
The mechanism that updates your self-hosted Sure app is the GHCR (Github Container Registry) Docker image that you see in the `compose.yml` file:
diff --git a/docs/hosting/mcp.md b/docs/hosting/mcp.md
new file mode 100644
index 000000000..671feb700
--- /dev/null
+++ b/docs/hosting/mcp.md
@@ -0,0 +1,338 @@
+# MCP Server for External AI Assistants
+
+Sure includes a Model Context Protocol (MCP) server endpoint that allows external AI assistants like Claude Desktop, GPT agents, or custom AI clients to query your financial data.
+
+## What is MCP?
+
+[Model Context Protocol](https://modelcontextprotocol.io/) is a JSON-RPC 2.0 protocol that enables AI assistants to access structured data and tools from external applications. Instead of copying and pasting financial data into a chat window, your AI assistant can directly query Sure's data through a secure API.
+
+This is useful when:
+- You want to use an external AI assistant (Claude, GPT, custom agents) to analyze your Sure financial data
+- You prefer to keep your LLM provider separate from Sure
+- You're building custom AI agents that need access to financial tools
+
+## Prerequisites
+
+To enable the MCP endpoint, you need to set two environment variables:
+
+| Variable | Description | Example |
+|----------|-------------|---------|
+| `MCP_API_TOKEN` | Bearer token for authentication | `your-secret-token-here` |
+| `MCP_USER_EMAIL` | Email of the Sure user whose data the assistant can access | `user@example.com` |
+
+Both variables are **required**. The endpoint will not activate if either is missing.
+
+### Generating a secure token
+
+Generate a random token for `MCP_API_TOKEN`:
+
+```bash
+# macOS/Linux
+openssl rand -base64 32
+
+# Or use any secure password generator
+```
+
+### Choosing the user
+
+The `MCP_USER_EMAIL` must match an existing Sure user's email address. The AI assistant will have access to all financial data for that user's family.
+
+> [!CAUTION]
+> The AI assistant will have **read access to all financial data** for the specified user. Only set this for users you trust with your AI provider.
+
+## Configuration
+
+### Docker Compose
+
+Add the environment variables to your `compose.yml`:
+
+```yaml
+x-rails-env: &rails_env
+ MCP_API_TOKEN: your-secret-token-here
+ MCP_USER_EMAIL: user@example.com
+```
+
+Both `web` and `worker` services inherit this configuration.
+
+### Kubernetes (Helm)
+
+Add the variables to your `values.yaml` or set them via Secrets:
+
+```yaml
+env:
+ MCP_API_TOKEN: your-secret-token-here
+ MCP_USER_EMAIL: user@example.com
+```
+
+Or create a Secret and reference it:
+
+```yaml
+envFrom:
+ - secretRef:
+ name: sure-mcp-credentials
+```
+
+## Protocol Details
+
+The MCP endpoint is available at:
+
+```
+POST /mcp
+```
+
+### Authentication
+
+All requests must include the `MCP_API_TOKEN` as a Bearer token:
+
+```
+Authorization: Bearer
+```
+
+### Supported Methods
+
+Sure implements the following JSON-RPC 2.0 methods:
+
+| Method | Description |
+|--------|-------------|
+| `initialize` | Protocol handshake, returns server info and capabilities |
+| `tools/list` | Lists available financial tools with schemas |
+| `tools/call` | Executes a tool with provided arguments |
+
+### Available Tools
+
+The MCP endpoint exposes these financial tools:
+
+| Tool | Description |
+|------|-------------|
+| `get_transactions` | Retrieve transaction history with filtering |
+| `get_accounts` | Get account information and balances |
+| `get_holdings` | Query investment holdings |
+| `get_balance_sheet` | Current financial position (assets, liabilities, net worth) |
+| `get_income_statement` | Income and expenses over a period |
+| `import_bank_statement` | Import bank statement data |
+| `search_family_files` | Search uploaded documents in the vault |
+
+These are the same tools used by Sure's builtin AI assistant.
+
+## Example Requests
+
+### Initialize
+
+Handshake to verify protocol version and capabilities:
+
+```bash
+curl -X POST https://your-sure-instance/mcp \
+ -H "Authorization: Bearer your-secret-token" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "initialize"
+ }'
+```
+
+Response:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "protocolVersion": "2025-03-26",
+ "capabilities": {
+ "tools": {}
+ },
+ "serverInfo": {
+ "name": "sure",
+ "version": "1.0"
+ }
+ }
+}
+```
+
+### List Tools
+
+Get available tools with their schemas:
+
+```bash
+curl -X POST https://your-sure-instance/mcp \
+ -H "Authorization: Bearer your-secret-token" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "id": 2,
+ "method": "tools/list"
+ }'
+```
+
+Response includes tool names, descriptions, and JSON schemas for parameters.
+
+### Call a Tool
+
+Execute a tool to get transactions:
+
+```bash
+curl -X POST https://your-sure-instance/mcp \
+ -H "Authorization: Bearer your-secret-token" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "id": 3,
+ "method": "tools/call",
+ "params": {
+ "name": "get_transactions",
+ "arguments": {
+ "start_date": "2024-01-01",
+ "end_date": "2024-01-31"
+ }
+ }
+ }'
+```
+
+Response:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 3,
+ "result": {
+ "content": [
+ {
+ "type": "text",
+ "text": "[{\"id\":\"...\",\"amount\":-45.99,\"date\":\"2024-01-15\",\"name\":\"Coffee Shop\"}]"
+ }
+ ]
+ }
+}
+```
+
+## Security Considerations
+
+### Transient Session Isolation
+
+The MCP controller creates a **transient session** for each request. This prevents session state leaks that could expose other users' data if the Sure instance is using impersonation features.
+
+Each MCP request:
+1. Authenticates the token
+2. Loads the user specified in `MCP_USER_EMAIL`
+3. Creates a temporary session scoped to that user
+4. Executes the tool call
+5. Discards the session
+
+This ensures the AI assistant can only access data for the intended user.
+
+### Pipelock Security Scanning
+
+For production deployments, we recommend using [Pipelock](https://github.com/luckyPipewrench/pipelock) to scan MCP traffic for security threats.
+
+Pipelock provides:
+- **DLP scanning**: Detects secrets being exfiltrated through tool calls
+- **Prompt injection detection**: Identifies attempts to manipulate the AI
+- **Tool poisoning detection**: Prevents malicious tool call sequences
+- **Policy enforcement**: Block or warn on suspicious patterns
+
+See the [Pipelock documentation](pipelock.md) and the example configuration in `compose.example.pipelock.yml` for setup instructions.
+
+### Network Security
+
+The `/mcp` endpoint is exposed on the same port as the web UI (default 3000). For hardened deployments:
+
+**Docker Compose:**
+- The MCP endpoint is protected by the `MCP_API_TOKEN` but is reachable on port 3000
+- For additional security, use Pipelock's MCP reverse proxy (port 8889) which adds scanning
+- See `compose.example.ai.yml` for a Pipelock configuration
+
+**Kubernetes:**
+- Use NetworkPolicies to restrict access to the MCP endpoint
+- Route external agents through Pipelock's MCP reverse proxy
+- See the [Helm chart documentation](../../charts/sure/README.md) for Pipelock ingress setup
+
+## Production Deployment
+
+For a production-ready setup with security scanning:
+
+1. **Download the example configuration:**
+
+ ```bash
+ curl -o compose.ai.yml https://raw.githubusercontent.com/we-promise/sure/main/compose.example.ai.yml
+ curl -o pipelock.example.yaml https://raw.githubusercontent.com/we-promise/sure/main/pipelock.example.yaml
+ ```
+
+2. **Set your MCP credentials in `.env`:**
+
+ ```bash
+ MCP_API_TOKEN=your-secret-token
+ MCP_USER_EMAIL=user@example.com
+ ```
+
+3. **Start the stack:**
+
+ ```bash
+ docker compose -f compose.ai.yml up -d
+ ```
+
+4. **Connect your AI assistant to the Pipelock MCP proxy:**
+
+ ```
+ http://your-server:8889
+ ```
+
+The Pipelock proxy (port 8889) scans all MCP traffic before forwarding to Sure's `/mcp` endpoint.
+
+## Connecting AI Assistants
+
+### Claude Desktop
+
+Configure Claude Desktop to use Sure's MCP server:
+
+1. Open Claude Desktop settings
+2. Add a new MCP server
+3. Set the endpoint to `http://your-server:8889` (if using Pipelock) or `http://your-server:3000/mcp`
+4. Add the authorization header: `Authorization: Bearer your-secret-token`
+
+### Custom Agents
+
+Any AI agent that supports JSON-RPC 2.0 can connect to the MCP endpoint. The agent should:
+
+1. Send a POST request to `/mcp`
+2. Include the `Authorization: Bearer ` header
+3. Use the JSON-RPC 2.0 format for requests
+4. Handle the protocol methods: `initialize`, `tools/list`, `tools/call`
+
+## Troubleshooting
+
+### "MCP endpoint not configured" error
+
+**Symptom:** Requests return HTTP 503 with "MCP endpoint not configured"
+
+**Fix:** Ensure both `MCP_API_TOKEN` and `MCP_USER_EMAIL` are set as environment variables and restart Sure.
+
+### "unauthorized" error
+
+**Symptom:** Requests return HTTP 401 with "unauthorized"
+
+**Fix:** Verify the `Authorization` header contains the correct token: `Bearer `
+
+### "MCP user not configured" error
+
+**Symptom:** Requests return HTTP 503 with "MCP user not configured"
+
+**Fix:** The `MCP_USER_EMAIL` does not match an existing user. Check that:
+- The email is correct
+- The user exists in the database
+- There are no typos or extra spaces
+
+### Pipelock connection refused
+
+**Symptom:** AI assistant cannot connect to Pipelock's MCP proxy (port 8889)
+
+**Fix:**
+1. Verify Pipelock is running: `docker compose ps pipelock`
+2. Check Pipelock health: `docker compose exec pipelock /pipelock healthcheck --addr 127.0.0.1:8888`
+3. Verify the port is exposed in your `compose.yml`
+
+## See Also
+
+- [External AI Assistant Configuration](ai.md#external-ai-assistant) - Configure Sure's chat to use an external agent
+- [Pipelock Security Proxy](pipelock.md) - Set up security scanning for MCP traffic
+- [Model Context Protocol Specification](https://modelcontextprotocol.io/) - Official MCP documentation
diff --git a/docs/hosting/oidc.md b/docs/hosting/oidc.md
index 9100bb5f9..91318c907 100644
--- a/docs/hosting/oidc.md
+++ b/docs/hosting/oidc.md
@@ -346,6 +346,7 @@ When enabled:
When disabled (default):
- Providers are loaded from `config/auth.yml`
- Changes require a server restart
+- In production, YAML is the default unless `AUTH_PROVIDERS_SOURCE=db` is explicitly set
### 6.2 Admin UI for SSO providers
diff --git a/docs/hosting/pipelock.md b/docs/hosting/pipelock.md
new file mode 100644
index 000000000..b1f039eeb
--- /dev/null
+++ b/docs/hosting/pipelock.md
@@ -0,0 +1,222 @@
+# Pipelock: AI Agent Security Proxy
+
+[Pipelock](https://github.com/luckyPipewrench/pipelock) is an optional security proxy that scans AI agent traffic flowing through Sure. It protects against secret exfiltration, prompt injection, and tool poisoning.
+
+## What Pipelock does
+
+Pipelock runs as a separate proxy service alongside Sure with two listeners:
+
+| Listener | Port | Direction | What it scans |
+|----------|------|-----------|---------------|
+| Forward proxy | 8888 | Outbound (Sure to LLM) | DLP (secrets in prompts), response injection |
+| MCP reverse proxy | 8889 | Inbound (agent to Sure /mcp) | Prompt injection, tool poisoning, DLP |
+
+### Forward proxy (outbound)
+
+When `HTTPS_PROXY=http://pipelock:8888` is set, outbound HTTPS requests from Faraday-based clients (like `ruby-openai`) are routed through Pipelock. It scans request bodies for leaked secrets and response bodies for prompt injection.
+
+**Covered:** OpenAI API calls via ruby-openai (uses Faraday).
+**Not covered:** SimpleFIN, Coinbase, Plaid, or anything using Net::HTTP/HTTParty directly. These bypass `HTTPS_PROXY`.
+
+### MCP reverse proxy (inbound)
+
+External AI assistants that call Sure's `/mcp` endpoint should connect through Pipelock on port 8889 instead of directly to port 3000. Pipelock scans:
+
+- Tool call arguments (DLP, shell obfuscation detection)
+- Tool responses (injection payloads)
+- Session binding (detects tool inventory manipulation)
+- Tool call chains (multi-step attack patterns like recon then exfil)
+
+## Docker Compose setup
+
+The `compose.example.ai.yml` file includes Pipelock. To use it:
+
+1. Download the compose file and Pipelock config:
+ ```bash
+ curl -o compose.ai.yml https://raw.githubusercontent.com/we-promise/sure/main/compose.example.ai.yml
+ curl -o pipelock.example.yaml https://raw.githubusercontent.com/we-promise/sure/main/pipelock.example.yaml
+ ```
+
+2. Start the stack:
+ ```bash
+ docker compose -f compose.ai.yml up -d
+ ```
+
+3. Verify Pipelock is healthy:
+ ```bash
+ docker compose -f compose.ai.yml ps pipelock
+ # Should show "healthy"
+ ```
+
+### Connecting external AI agents
+
+External agents should use the MCP reverse proxy port:
+
+```text
+http://your-server:8889
+```
+
+The agent must include the `MCP_API_TOKEN` as a Bearer token in requests. Set this in your `.env`:
+
+```bash
+MCP_API_TOKEN=generate-a-random-token
+MCP_USER_EMAIL=your@email.com
+```
+
+### Running without Pipelock
+
+To use `compose.example.ai.yml` without Pipelock, remove the `pipelock` service and its `depends_on` entries from `web` and `worker`, then unset the proxy env vars (`HTTPS_PROXY`, `HTTP_PROXY`).
+
+Or use the standard `compose.example.yml` which does not include Pipelock.
+
+## Helm (Kubernetes) setup
+
+Enable Pipelock in your Helm values:
+
+```yaml
+pipelock:
+ enabled: true
+ image:
+ tag: "2.0.0"
+ mode: balanced
+```
+
+This creates a separate Deployment, Service, and ConfigMap. The chart auto-injects `HTTPS_PROXY`/`HTTP_PROXY`/`NO_PROXY` into web and worker pods.
+
+v2.0 adds trusted domain allowlisting, MCP tool redirect profiles, enhanced tool poisoning detection (full JSON schema scanning), and per-read kill switch preemption on long-lived connections. Process sandboxing and attack simulation are also available via `extraConfig` and CLI.
+
+### Exposing MCP to external agents (Kubernetes)
+
+In Kubernetes, external agents cannot reach the MCP port by default. Enable the Pipelock Ingress:
+
+```yaml
+pipelock:
+ enabled: true
+ ingress:
+ enabled: true
+ className: nginx
+ hosts:
+ - host: pipelock.example.com
+ paths:
+ - path: /
+ pathType: Prefix
+ tls:
+ - hosts: [pipelock.example.com]
+ secretName: pipelock-tls
+```
+
+Or port-forward for testing:
+
+```bash
+kubectl port-forward svc/sure-pipelock 8889:8889 -n sure
+```
+
+### Monitoring
+
+Enable the ServiceMonitor for Prometheus scraping:
+
+```yaml
+pipelock:
+ serviceMonitor:
+ enabled: true
+ interval: 30s
+ additionalLabels:
+ release: prometheus
+```
+
+Metrics are available at `/metrics` on the forward proxy port (8888).
+
+### Eviction protection
+
+For production, enable the PodDisruptionBudget:
+
+```yaml
+pipelock:
+ pdb:
+ enabled: true
+ maxUnavailable: 1
+```
+
+See the [Helm chart README](../../charts/sure/README.md#pipelock-ai-agent-security-proxy) for all configuration options.
+
+## Pipelock configuration file
+
+The `pipelock.example.yaml` file (Docker Compose) or ConfigMap (Helm) controls scanning behavior. Key sections:
+
+| Section | What it controls |
+|---------|-----------------|
+| `mode` | `strict` (block threats), `balanced` (warn + block critical), `audit` (log only) |
+| `trusted_domains` | Allow internal services whose public DNS resolves to private IPs |
+| `forward_proxy` | Outbound HTTPS scanning (tunnel timeouts, idle timeouts) |
+| `dlp` | Data loss prevention (scan env vars, built-in patterns) |
+| `response_scanning` | Scan LLM responses for prompt injection |
+| `mcp_input_scanning` | Scan inbound MCP requests |
+| `mcp_tool_scanning` | Validate tool calls, detect drift |
+| `mcp_tool_policy` | Pre-execution rules, shell obfuscation, redirect profiles |
+| `mcp_session_binding` | Pin tool inventory, detect manipulation |
+| `tool_chain_detection` | Multi-step attack patterns |
+| `websocket_proxy` | WebSocket frame scanning (disabled by default) |
+| `logging` | Output format (json/text), verbosity |
+
+For the Helm chart, most sections are configurable via `values.yaml`. For additional sections not covered by structured values (session profiling, data budgets, kill switch, sandbox, reverse proxy, adaptive enforcement), use the `extraConfig` escape hatch:
+
+```yaml
+pipelock:
+ extraConfig:
+ session_profiling:
+ enabled: true
+ max_sessions: 1000
+```
+
+## Modes
+
+| Mode | Behavior | Use case |
+|------|----------|----------|
+| `strict` | Block all detected threats | Production with sensitive data |
+| `balanced` | Warn on low-severity, block on high-severity | Default; good for most deployments |
+| `audit` | Log everything, block nothing | Initial rollout, testing |
+
+Start with `audit` mode to see what Pipelock detects without blocking anything. Review the logs, then switch to `balanced` or `strict`.
+
+## Limitations
+
+- Forward proxy only covers Faraday-based HTTP clients. Net::HTTP, HTTParty, and other libraries ignore `HTTPS_PROXY`.
+- Docker Compose has no egress network policies. The `/mcp` endpoint on port 3000 is still reachable directly (auth token required). For enforcement, use Kubernetes NetworkPolicies.
+- Pipelock scans text content. Binary payloads (images, file uploads) are passed through by default.
+
+## Troubleshooting
+
+### Pipelock container not starting
+
+Check the config file is mounted correctly:
+```bash
+docker compose -f compose.ai.yml logs pipelock
+```
+
+Common issues:
+- Missing `pipelock.example.yaml` file
+- YAML syntax errors in config
+- Port conflicts (8888 or 8889 already in use)
+
+### LLM calls failing with proxy errors
+
+If AI chat stops working after enabling Pipelock:
+```bash
+# Check Pipelock logs for blocked requests
+docker compose -f compose.ai.yml logs pipelock --tail=50
+```
+
+If requests are being incorrectly blocked, switch to `audit` mode in the config file and restart:
+```yaml
+mode: audit
+```
+
+### MCP requests not reaching Sure
+
+Verify the MCP upstream is configured correctly:
+```bash
+# Test from inside the Pipelock container
+docker compose -f compose.ai.yml exec pipelock /pipelock healthcheck --addr 127.0.0.1:8888
+```
+
+Check that `MCP_API_TOKEN` and `MCP_USER_EMAIL` are set in your `.env` file and that the email matches an existing Sure user.
diff --git a/lib/feature_flags.rb b/lib/feature_flags.rb
index e20472e81..bd9c648eb 100644
--- a/lib/feature_flags.rb
+++ b/lib/feature_flags.rb
@@ -3,9 +3,14 @@
module FeatureFlags
class << self
def db_sso_providers?
- auth_source = ENV.fetch("AUTH_PROVIDERS_SOURCE") do
- Rails.configuration.app_mode.self_hosted? ? "db" : "yaml"
- end
+ auth_source = ENV["AUTH_PROVIDERS_SOURCE"]
+ return auth_source.to_s.downcase == "db" if auth_source.present?
+
+ # In production, prefer YAML by default so boot-time tasks (e.g. db:prepare)
+ # do not attempt to query SSO provider tables before migrations run.
+ return false if Rails.env.production?
+
+ auth_source = Rails.configuration.app_mode.self_hosted? ? "db" : "yaml"
auth_source.to_s.downcase == "db"
end
diff --git a/mobile/README.md b/mobile/README.md
index 95f2897fb..b7a843467 100644
--- a/mobile/README.md
+++ b/mobile/README.md
@@ -172,6 +172,8 @@ flutter build apk --release
flutter build appbundle --release
```
+Android release metadata comes from `pubspec.yaml` (`version: +`). Keep the numeric build code increasing for every release so Android can install upgrades over older APKs.
+
### iOS
```bash
diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart
index 2f3aca585..5b1a582f5 100644
--- a/mobile/lib/main.dart
+++ b/mobile/lib/main.dart
@@ -6,9 +6,11 @@ import 'providers/auth_provider.dart';
import 'providers/accounts_provider.dart';
import 'providers/transactions_provider.dart';
import 'providers/chat_provider.dart';
+import 'providers/theme_provider.dart';
import 'screens/backend_config_screen.dart';
import 'screens/login_screen.dart';
import 'screens/main_navigation_screen.dart';
+import 'screens/sso_onboarding_screen.dart';
import 'services/api_config.dart';
import 'services/connectivity_service.dart';
import 'services/log_service.dart';
@@ -34,6 +36,7 @@ class SureApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => ConnectivityService()),
ChangeNotifierProvider(create: (_) => AuthProvider()),
ChangeNotifierProvider(create: (_) => ChatProvider()),
+ ChangeNotifierProvider(create: (_) => ThemeProvider()),
ChangeNotifierProxyProvider(
create: (_) => AccountsProvider(),
update: (_, connectivityService, accountsProvider) {
@@ -61,7 +64,8 @@ class SureApp extends StatelessWidget {
},
),
],
- child: MaterialApp(
+ child: Consumer(
+ builder: (context, themeProvider, _) => MaterialApp(
title: 'Sure Finances',
debugShowCheckedModeBanner: false,
theme: ThemeData(
@@ -138,14 +142,14 @@ class SureApp extends StatelessWidget {
),
),
),
- themeMode: ThemeMode.system,
+ themeMode: themeProvider.themeMode,
routes: {
'/config': (context) => const BackendConfigScreen(),
'/login': (context) => const LoginScreen(),
'/home': (context) => const MainNavigationScreen(),
},
home: const AppWrapper(),
- ),
+ )),
);
}
}
@@ -255,6 +259,10 @@ class _AppWrapperState extends State {
return const MainNavigationScreen();
}
+ if (authProvider.ssoOnboardingPending) {
+ return const SsoOnboardingScreen();
+ }
+
return LoginScreen(
onGoToSettings: _goToBackendConfig,
);
diff --git a/mobile/lib/providers/accounts_provider.dart b/mobile/lib/providers/accounts_provider.dart
index efa6e65bd..35c85a928 100644
--- a/mobile/lib/providers/accounts_provider.dart
+++ b/mobile/lib/providers/accounts_provider.dart
@@ -3,12 +3,14 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import '../models/account.dart';
import '../services/accounts_service.dart';
+import '../services/balance_sheet_service.dart';
import '../services/offline_storage_service.dart';
import '../services/connectivity_service.dart';
import '../services/log_service.dart';
class AccountsProvider with ChangeNotifier {
final AccountsService _accountsService = AccountsService();
+ final BalanceSheetService _balanceSheetService = BalanceSheetService();
final OfflineStorageService _offlineStorage = OfflineStorageService();
final LogService _log = LogService.instance;
@@ -19,11 +21,23 @@ class AccountsProvider with ChangeNotifier {
Map? _pagination;
ConnectivityService? _connectivityService;
+ // Summary / net worth data
+ String? _netWorthFormatted;
+ String? _assetsFormatted;
+ String? _liabilitiesFormatted;
+ String? _familyCurrency;
+ bool _isBalanceSheetStale = false;
+
List get accounts => _accounts;
bool get isLoading => _isLoading;
bool get isInitializing => _isInitializing;
String? get errorMessage => _errorMessage;
Map? get pagination => _pagination;
+ String? get netWorthFormatted => _netWorthFormatted;
+ String? get assetsFormatted => _assetsFormatted;
+ String? get liabilitiesFormatted => _liabilitiesFormatted;
+ String? get familyCurrency => _familyCurrency;
+ bool get isBalanceSheetStale => _isBalanceSheetStale;
List get assetAccounts {
final assets = _accounts.where((a) => a.isAsset).toList();
@@ -126,6 +140,11 @@ class AccountsProvider with ChangeNotifier {
_errorMessage = 'You are offline. Please connect to the internet to load accounts.';
}
+ // Fetch balance sheet independently — works even with cached accounts
+ if (isOnline) {
+ await _fetchBalanceSheet(accessToken);
+ }
+
_isLoading = false;
_isInitializing = false;
notifyListeners();
@@ -164,11 +183,46 @@ class AccountsProvider with ChangeNotifier {
}
}
+ /// Fetches balance sheet data and updates formatted net worth, assets,
+ /// and liabilities values for display. On failure, marks the existing
+ /// values as stale rather than clearing them.
+ Future _fetchBalanceSheet(String accessToken) async {
+ try {
+ final result = await _balanceSheetService.getBalanceSheet(accessToken: accessToken);
+ if (result['success'] == true) {
+ _familyCurrency = result['currency'] as String?;
+ final netWorth = result['net_worth'] as Map?;
+ final assets = result['assets'] as Map?;
+ final liabilities = result['liabilities'] as Map?;
+ _netWorthFormatted = netWorth?['formatted'] as String?;
+ _assetsFormatted = assets?['formatted'] as String?;
+ _liabilitiesFormatted = liabilities?['formatted'] as String?;
+ _isBalanceSheetStale = false;
+ } else {
+ // Keep existing values but mark as stale
+ if (_netWorthFormatted != null) {
+ _isBalanceSheetStale = true;
+ }
+ }
+ } catch (e) {
+ _log.error('AccountsProvider', 'Error fetching balance sheet: $e');
+ // Keep existing values but mark as stale
+ if (_netWorthFormatted != null) {
+ _isBalanceSheetStale = true;
+ }
+ }
+ }
+
void clearAccounts() {
_accounts = [];
_pagination = null;
_errorMessage = null;
_isInitializing = true;
+ _netWorthFormatted = null;
+ _assetsFormatted = null;
+ _liabilitiesFormatted = null;
+ _familyCurrency = null;
+ _isBalanceSheetStale = false;
notifyListeners();
}
diff --git a/mobile/lib/providers/auth_provider.dart b/mobile/lib/providers/auth_provider.dart
index 3b6a6510e..884ef3d53 100644
--- a/mobile/lib/providers/auth_provider.dart
+++ b/mobile/lib/providers/auth_provider.dart
@@ -22,6 +22,15 @@ class AuthProvider with ChangeNotifier {
bool _mfaRequired = false;
bool _showMfaInput = false; // Track if we should show MFA input field
+ // SSO onboarding state
+ bool _ssoOnboardingPending = false;
+ String? _ssoLinkingCode;
+ String? _ssoEmail;
+ String? _ssoFirstName;
+ String? _ssoLastName;
+ bool _ssoAllowAccountCreation = false;
+ bool _ssoHasPendingInvitation = false;
+
User? get user => _user;
bool get isIntroLayout => _user?.isIntroLayout ?? false;
bool get aiEnabled => _user?.aiEnabled ?? false;
@@ -36,6 +45,15 @@ class AuthProvider with ChangeNotifier {
bool get mfaRequired => _mfaRequired;
bool get showMfaInput => _showMfaInput; // Expose MFA input state
+ // SSO onboarding getters
+ bool get ssoOnboardingPending => _ssoOnboardingPending;
+ String? get ssoLinkingCode => _ssoLinkingCode;
+ String? get ssoEmail => _ssoEmail;
+ String? get ssoFirstName => _ssoFirstName;
+ String? get ssoLastName => _ssoLastName;
+ bool get ssoAllowAccountCreation => _ssoAllowAccountCreation;
+ bool get ssoHasPendingInvitation => _ssoHasPendingInvitation;
+
AuthProvider() {
_loadStoredAuth();
}
@@ -266,9 +284,22 @@ class AuthProvider with ChangeNotifier {
if (result['success'] == true) {
_tokens = result['tokens'] as AuthTokens?;
_user = result['user'] as User?;
+ _ssoOnboardingPending = false;
_isLoading = false;
notifyListeners();
return true;
+ } else if (result['account_not_linked'] == true) {
+ // SSO onboarding needed - store linking data
+ _ssoOnboardingPending = true;
+ _ssoLinkingCode = result['linking_code'] as String?;
+ _ssoEmail = result['email'] as String?;
+ _ssoFirstName = result['first_name'] as String?;
+ _ssoLastName = result['last_name'] as String?;
+ _ssoAllowAccountCreation = result['allow_account_creation'] == true;
+ _ssoHasPendingInvitation = result['has_pending_invitation'] == true;
+ _isLoading = false;
+ notifyListeners();
+ return false;
} else {
_errorMessage = result['error'] as String?;
_isLoading = false;
@@ -284,6 +315,107 @@ class AuthProvider with ChangeNotifier {
}
}
+ Future ssoLinkAccount({
+ required String email,
+ required String password,
+ }) async {
+ if (_ssoLinkingCode == null) {
+ _errorMessage = 'No pending SSO session. Please try signing in again.';
+ notifyListeners();
+ return false;
+ }
+
+ _errorMessage = null;
+ _isLoading = true;
+ notifyListeners();
+
+ try {
+ final result = await _authService.ssoLink(
+ linkingCode: _ssoLinkingCode!,
+ email: email,
+ password: password,
+ );
+
+ if (result['success'] == true) {
+ _tokens = result['tokens'] as AuthTokens?;
+ _user = result['user'] as User?;
+ _clearSsoOnboardingState();
+ _isLoading = false;
+ notifyListeners();
+ return true;
+ } else {
+ _errorMessage = result['error'] as String?;
+ _isLoading = false;
+ notifyListeners();
+ return false;
+ }
+ } catch (e, stackTrace) {
+ LogService.instance.error('AuthProvider', 'SSO link error: $e\n$stackTrace');
+ _errorMessage = 'Failed to link account. Please try again.';
+ _isLoading = false;
+ notifyListeners();
+ return false;
+ }
+ }
+
+ Future ssoCreateAccount({
+ String? firstName,
+ String? lastName,
+ }) async {
+ if (_ssoLinkingCode == null) {
+ _errorMessage = 'No pending SSO session. Please try signing in again.';
+ notifyListeners();
+ return false;
+ }
+
+ _errorMessage = null;
+ _isLoading = true;
+ notifyListeners();
+
+ try {
+ final result = await _authService.ssoCreateAccount(
+ linkingCode: _ssoLinkingCode!,
+ firstName: firstName,
+ lastName: lastName,
+ );
+
+ if (result['success'] == true) {
+ _tokens = result['tokens'] as AuthTokens?;
+ _user = result['user'] as User?;
+ _clearSsoOnboardingState();
+ _isLoading = false;
+ notifyListeners();
+ return true;
+ } else {
+ _errorMessage = result['error'] as String?;
+ _isLoading = false;
+ notifyListeners();
+ return false;
+ }
+ } catch (e, stackTrace) {
+ LogService.instance.error('AuthProvider', 'SSO create account error: $e\n$stackTrace');
+ _errorMessage = 'Failed to create account. Please try again.';
+ _isLoading = false;
+ notifyListeners();
+ return false;
+ }
+ }
+
+ void cancelSsoOnboarding() {
+ _clearSsoOnboardingState();
+ notifyListeners();
+ }
+
+ void _clearSsoOnboardingState() {
+ _ssoOnboardingPending = false;
+ _ssoLinkingCode = null;
+ _ssoEmail = null;
+ _ssoFirstName = null;
+ _ssoLastName = null;
+ _ssoAllowAccountCreation = false;
+ _ssoHasPendingInvitation = false;
+ }
+
Future logout() async {
await _authService.logout();
_tokens = null;
diff --git a/mobile/lib/providers/chat_provider.dart b/mobile/lib/providers/chat_provider.dart
index 5016c934f..158e2a147 100644
--- a/mobile/lib/providers/chat_provider.dart
+++ b/mobile/lib/providers/chat_provider.dart
@@ -11,6 +11,7 @@ class ChatProvider with ChangeNotifier {
Chat? _currentChat;
bool _isLoading = false;
bool _isSendingMessage = false;
+ bool _isWaitingForResponse = false;
String? _errorMessage;
Timer? _pollingTimer;
@@ -22,6 +23,7 @@ class ChatProvider with ChangeNotifier {
Chat? get currentChat => _currentChat;
bool get isLoading => _isLoading;
bool get isSendingMessage => _isSendingMessage;
+ bool get isWaitingForResponse => _isWaitingForResponse;
String? get errorMessage => _errorMessage;
/// Fetch list of chats
@@ -103,18 +105,31 @@ class ChatProvider with ChangeNotifier {
if (result['success'] == true) {
final chat = result['chat'] as Chat;
- _currentChat = chat;
- _chats.insert(0, chat);
_errorMessage = null;
- // Start polling for AI response if initial message was sent
if (initialMessage != null) {
+ // Inject the user message locally so the UI renders it immediately
+ // without waiting for the first poll.
+ final now = DateTime.now();
+ final userMessage = Message(
+ id: 'pending_${now.millisecondsSinceEpoch}',
+ type: 'text',
+ role: 'user',
+ content: initialMessage,
+ createdAt: now,
+ updatedAt: now,
+ );
+ _currentChat = chat.copyWith(messages: [userMessage]);
+ _chats.insert(0, _currentChat!);
_startPolling(accessToken, chat.id);
+ } else {
+ _currentChat = chat;
+ _chats.insert(0, chat);
}
_isLoading = false;
notifyListeners();
- return chat;
+ return _currentChat!;
} else {
_errorMessage = result['error'] ?? 'Failed to create chat';
_isLoading = false;
@@ -244,8 +259,10 @@ class ChatProvider with ChangeNotifier {
/// Start polling for new messages (AI responses)
void _startPolling(String accessToken, String chatId) {
- _stopPolling();
+ _pollingTimer?.cancel();
_lastAssistantContentLength = null;
+ _isWaitingForResponse = true;
+ notifyListeners();
_pollingTimer = Timer.periodic(const Duration(seconds: 2), (timer) async {
await _pollForUpdates(accessToken, chatId);
@@ -256,6 +273,7 @@ class ChatProvider with ChangeNotifier {
void _stopPolling() {
_pollingTimer?.cancel();
_pollingTimer = null;
+ _isWaitingForResponse = false;
}
/// Poll for updates
@@ -302,6 +320,13 @@ class ChatProvider with ChangeNotifier {
if (shouldUpdate) {
_currentChat = updatedChat;
+ // Hide thinking indicator as soon as the first assistant content arrives.
+ if (_isWaitingForResponse) {
+ final lastMsg = updatedChat.messages.lastOrNull;
+ if (lastMsg != null && lastMsg.isAssistant && lastMsg.content.isNotEmpty) {
+ _isWaitingForResponse = false;
+ }
+ }
notifyListeners();
}
@@ -311,9 +336,10 @@ class ChatProvider with ChangeNotifier {
if (newLen > (_lastAssistantContentLength ?? 0)) {
_lastAssistantContentLength = newLen;
} else {
- // Content stable: no growth since last poll
+ // Content stable: no growth since last poll — done.
_stopPolling();
_lastAssistantContentLength = null;
+ notifyListeners();
}
}
}
diff --git a/mobile/lib/providers/theme_provider.dart b/mobile/lib/providers/theme_provider.dart
new file mode 100644
index 000000000..51920a45a
--- /dev/null
+++ b/mobile/lib/providers/theme_provider.dart
@@ -0,0 +1,50 @@
+import 'package:flutter/material.dart';
+import '../services/preferences_service.dart';
+
+class ThemeProvider extends ChangeNotifier {
+ ThemeMode _themeMode = ThemeMode.system;
+
+ ThemeMode get themeMode => _themeMode;
+
+ ThemeProvider() {
+ _loadThemeMode();
+ }
+
+ Future _loadThemeMode() async {
+ try {
+ final mode = await PreferencesService.instance.getThemeMode();
+ _themeMode = _fromString(mode);
+ } catch (_) {
+ _themeMode = ThemeMode.system;
+ }
+ notifyListeners();
+ }
+
+ Future setThemeMode(ThemeMode mode) async {
+ _themeMode = mode;
+ notifyListeners();
+ await PreferencesService.instance.setThemeMode(_toString(mode));
+ }
+
+ static ThemeMode _fromString(String mode) {
+ switch (mode) {
+ case 'light':
+ return ThemeMode.light;
+ case 'dark':
+ return ThemeMode.dark;
+ default:
+ return ThemeMode.system;
+ }
+ }
+
+ static String _toString(ThemeMode mode) {
+ switch (mode) {
+ case ThemeMode.light:
+ return 'light';
+ case ThemeMode.dark:
+ return 'dark';
+ case ThemeMode.system:
+ return 'system';
+ }
+ }
+}
diff --git a/mobile/lib/screens/chat_conversation_screen.dart b/mobile/lib/screens/chat_conversation_screen.dart
index 66b6d20c4..e5e85d264 100644
--- a/mobile/lib/screens/chat_conversation_screen.dart
+++ b/mobile/lib/screens/chat_conversation_screen.dart
@@ -5,13 +5,15 @@ import '../models/chat.dart';
import '../providers/auth_provider.dart';
import '../providers/chat_provider.dart';
import '../models/message.dart';
+import '../widgets/typing_indicator.dart';
class _SendMessageIntent extends Intent {
const _SendMessageIntent();
}
class ChatConversationScreen extends StatefulWidget {
- final String chatId;
+ /// Null means this is a brand-new chat — it will be created on first send.
+ final String? chatId;
const ChatConversationScreen({
super.key,
@@ -26,23 +28,78 @@ class _ChatConversationScreenState extends State {
final TextEditingController _messageController = TextEditingController();
final ScrollController _scrollController = ScrollController();
+ /// Tracks the real chat ID once the chat has been created.
+ String? _chatId;
+
+ ChatProvider? _chatProvider;
+ bool _listenerAdded = false;
+
@override
void initState() {
super.initState();
- _loadChat();
+ _chatId = widget.chatId;
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (!mounted) return;
+ _chatProvider = Provider.of(context, listen: false);
+ _chatProvider!.addListener(_onChatChanged);
+ _listenerAdded = true;
+ if (_chatId == null) {
+ _chatProvider!.clearCurrentChat();
+ }
+ });
+ if (_chatId != null) {
+ _loadChat();
+ }
}
@override
void dispose() {
+ if (_listenerAdded && _chatProvider != null) {
+ _chatProvider!.removeListener(_onChatChanged);
+ _chatProvider = null;
+ _listenerAdded = false;
+ }
_messageController.dispose();
_scrollController.dispose();
super.dispose();
}
- Future _loadChat() async {
+ void _onChatChanged() {
+ if (!mounted) return;
+ final chatProvider = Provider.of(context, listen: false);
+ if (chatProvider.isWaitingForResponse || chatProvider.isSendingMessage) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (mounted) _scrollToBottom();
+ });
+ }
+ }
+
+ void _scrollToBottom() {
+ if (_scrollController.hasClients) {
+ _scrollController.animateTo(
+ _scrollController.position.maxScrollExtent,
+ duration: const Duration(milliseconds: 300),
+ curve: Curves.easeOut,
+ );
+ }
+ }
+
+ Future _loadChat({bool forceRefresh = false}) async {
+ if (_chatId == null) return;
+
final authProvider = Provider.of(context, listen: false);
final chatProvider = Provider.of(context, listen: false);
+ // Skip fetch if the provider already has this chat loaded (e.g. just created).
+ if (!forceRefresh && chatProvider.currentChat?.id == _chatId) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (mounted && _scrollController.hasClients) {
+ _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
+ }
+ });
+ return;
+ }
+
final accessToken = await authProvider.getValidAccessToken();
if (accessToken == null) {
await authProvider.logout();
@@ -51,12 +108,11 @@ class _ChatConversationScreenState extends State {
await chatProvider.fetchChat(
accessToken: accessToken,
- chatId: widget.chatId,
+ chatId: _chatId!,
);
- // Scroll to bottom after loading
WidgetsBinding.instance.addPostFrameCallback((_) {
- if (_scrollController.hasClients) {
+ if (mounted && _scrollController.hasClients) {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
});
@@ -75,25 +131,47 @@ class _ChatConversationScreenState extends State {
return;
}
- final shouldUpdateTitle = chatProvider.currentChat?.hasDefaultTitle == true;
-
_messageController.clear();
- final delivered = await chatProvider.sendMessage(
- accessToken: accessToken,
- chatId: widget.chatId,
- content: content,
- );
-
- if (delivered && shouldUpdateTitle) {
- await chatProvider.updateChatTitle(
+ if (_chatId == null) {
+ // First message in a new chat — create the chat with it.
+ final chat = await chatProvider.createChat(
accessToken: accessToken,
- chatId: widget.chatId,
title: Chat.generateTitle(content),
+ initialMessage: content,
);
+ if (!mounted) return;
+ if (chat == null) {
+ // Restore the message so the user doesn't lose it.
+ _messageController.text = content;
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text(chatProvider.errorMessage ?? 'Failed to start conversation. Please try again.'),
+ backgroundColor: Colors.red,
+ ),
+ );
+ return;
+ }
+ setState(() => _chatId = chat.id);
+ } else {
+ final shouldUpdateTitle =
+ chatProvider.currentChat?.hasDefaultTitle == true;
+
+ final delivered = await chatProvider.sendMessage(
+ accessToken: accessToken,
+ chatId: _chatId!,
+ content: content,
+ );
+
+ if (delivered && shouldUpdateTitle) {
+ await chatProvider.updateChatTitle(
+ accessToken: accessToken,
+ chatId: _chatId!,
+ title: Chat.generateTitle(content),
+ );
+ }
}
- // Scroll to bottom after sending
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
@@ -137,13 +215,17 @@ class _ChatConversationScreenState extends State {
},
);
- if (newTitle != null && newTitle.isNotEmpty && newTitle != currentTitle && mounted) {
+ if (newTitle != null &&
+ newTitle.isNotEmpty &&
+ newTitle != currentTitle &&
+ mounted) {
+ if (_chatId == null) return;
final authProvider = Provider.of(context, listen: false);
final accessToken = await authProvider.getValidAccessToken();
if (accessToken != null) {
await chatProvider.updateChatTitle(
accessToken: accessToken,
- chatId: widget.chatId,
+ chatId: _chatId!,
title: newTitle,
);
}
@@ -164,57 +246,56 @@ class _ChatConversationScreenState extends State {
appBar: AppBar(
title: Consumer(
builder: (context, chatProvider, _) {
+ final title = chatProvider.currentChat?.title ?? 'New Conversation';
return GestureDetector(
- onTap: _editTitle,
+ onTap: _chatId != null ? _editTitle : null,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Text(
- chatProvider.currentChat?.title ?? 'Chat',
+ title,
overflow: TextOverflow.ellipsis,
),
),
- const SizedBox(width: 4),
- const Icon(Icons.edit, size: 18),
+ if (_chatId != null) ...[
+ const SizedBox(width: 4),
+ const Icon(Icons.edit, size: 18),
+ ],
],
),
);
},
),
actions: [
- IconButton(
- icon: const Icon(Icons.refresh),
- onPressed: _loadChat,
- tooltip: 'Refresh',
- ),
+ if (widget.chatId != null)
+ IconButton(
+ icon: const Icon(Icons.refresh),
+ onPressed: () => _loadChat(forceRefresh: true),
+ tooltip: 'Refresh',
+ ),
],
),
body: Consumer(
builder: (context, chatProvider, _) {
if (chatProvider.isLoading && chatProvider.currentChat == null) {
- return const Center(
- child: CircularProgressIndicator(),
- );
+ return const Center(child: CircularProgressIndicator());
}
- if (chatProvider.errorMessage != null && chatProvider.currentChat == null) {
+ if (chatProvider.errorMessage != null &&
+ chatProvider.currentChat == null &&
+ _chatId != null) {
return Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- Icon(
- Icons.error_outline,
- size: 64,
- color: colorScheme.error,
- ),
+ Icon(Icons.error_outline,
+ size: 64, color: colorScheme.error),
const SizedBox(height: 16),
- Text(
- 'Failed to load chat',
- style: Theme.of(context).textTheme.titleLarge,
- ),
+ Text('Failed to load chat',
+ style: Theme.of(context).textTheme.titleLarge),
const SizedBox(height: 8),
Text(
chatProvider.errorMessage!,
@@ -237,68 +318,23 @@ class _ChatConversationScreenState extends State {
return Column(
children: [
- // Messages list
Expanded(
- child: messages.isEmpty
- ? Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Icon(
- Icons.chat_bubble_outline,
- size: 64,
- color: colorScheme.onSurfaceVariant,
- ),
- const SizedBox(height: 16),
- Text(
- 'Start a conversation',
- style: Theme.of(context).textTheme.titleLarge,
- ),
- const SizedBox(height: 8),
- Text(
- 'Send a message to begin chatting with the AI assistant.',
- style: TextStyle(color: colorScheme.onSurfaceVariant),
- textAlign: TextAlign.center,
- ),
- ],
- ),
- )
- : ListView.builder(
- controller: _scrollController,
- padding: const EdgeInsets.all(16),
- itemCount: messages.length,
- itemBuilder: (context, index) {
- final message = messages[index];
- return _MessageBubble(
- message: message,
- formatTime: _formatTime,
- );
- },
- ),
- ),
-
- // Loading indicator when sending
- if (chatProvider.isSendingMessage)
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
- child: Row(
- children: [
- const SizedBox(
- width: 16,
- height: 16,
- child: CircularProgressIndicator(strokeWidth: 2),
- ),
- const SizedBox(width: 12),
- Text(
- 'AI is thinking...',
- style: TextStyle(
- color: colorScheme.onSurfaceVariant,
- fontStyle: FontStyle.italic,
- ),
- ),
- ],
- ),
+ child: ListView.builder(
+ controller: _scrollController,
+ padding: const EdgeInsets.all(16),
+ itemCount: messages.length +
+ (chatProvider.isWaitingForResponse ? 1 : 0),
+ itemBuilder: (context, index) {
+ if (index == messages.length) {
+ return const _TypingIndicatorBubble();
+ }
+ return _MessageBubble(
+ message: messages[index],
+ formatTime: _formatTime,
+ );
+ },
),
+ ),
// Message input
Container(
@@ -315,7 +351,8 @@ class _ChatConversationScreenState extends State {
),
child: Shortcuts(
shortcuts: const {
- SingleActivator(LogicalKeyboardKey.enter): _SendMessageIntent(),
+ SingleActivator(LogicalKeyboardKey.enter):
+ _SendMessageIntent(),
},
child: Actions(
actions: >{
@@ -343,12 +380,15 @@ class _ChatConversationScreenState extends State {
),
maxLines: null,
textCapitalization: TextCapitalization.sentences,
+ autofocus: _chatId == null,
),
),
const SizedBox(width: 8),
IconButton(
icon: const Icon(Icons.send),
- onPressed: chatProvider.isSendingMessage ? null : _sendMessage,
+ onPressed: chatProvider.isSendingMessage
+ ? null
+ : _sendMessage,
color: colorScheme.primary,
iconSize: 28,
),
@@ -382,7 +422,8 @@ class _MessageBubble extends StatelessWidget {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Row(
- mainAxisAlignment: isUser ? MainAxisAlignment.end : MainAxisAlignment.start,
+ mainAxisAlignment:
+ isUser ? MainAxisAlignment.end : MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!isUser)
@@ -398,12 +439,17 @@ class _MessageBubble extends StatelessWidget {
const SizedBox(width: 8),
Flexible(
child: Column(
- crossAxisAlignment: isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
+ crossAxisAlignment:
+ isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ SelectionArea(
+ child: Container(
+ padding:
+ const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
- color: isUser ? colorScheme.primary : colorScheme.surfaceContainerHighest,
+ color: isUser
+ ? colorScheme.primary
+ : colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(16),
),
child: Column(
@@ -412,10 +458,13 @@ class _MessageBubble extends StatelessWidget {
Text(
message.content,
style: TextStyle(
- color: isUser ? colorScheme.onPrimary : colorScheme.onSurfaceVariant,
+ color: isUser
+ ? colorScheme.onPrimary
+ : colorScheme.onSurfaceVariant,
),
),
- if (message.toolCalls != null && message.toolCalls!.isNotEmpty)
+ if (message.toolCalls != null &&
+ message.toolCalls!.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 8),
child: Wrap(
@@ -436,6 +485,7 @@ class _MessageBubble extends StatelessWidget {
],
),
),
+ ),
const SizedBox(height: 4),
Text(
formatTime(message.createdAt),
@@ -463,3 +513,40 @@ class _MessageBubble extends StatelessWidget {
);
}
}
+
+class _TypingIndicatorBubble extends StatelessWidget {
+ const _TypingIndicatorBubble();
+
+ @override
+ Widget build(BuildContext context) {
+ final colorScheme = Theme.of(context).colorScheme;
+
+ return Padding(
+ padding: const EdgeInsets.only(bottom: 16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ CircleAvatar(
+ radius: 16,
+ backgroundColor: colorScheme.primaryContainer,
+ child: Icon(
+ Icons.smart_toy,
+ size: 18,
+ color: colorScheme.onPrimaryContainer,
+ ),
+ ),
+ const SizedBox(width: 8),
+ Container(
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ decoration: BoxDecoration(
+ color: colorScheme.surfaceContainerHighest,
+ borderRadius: BorderRadius.circular(16),
+ ),
+ child: const TypingIndicator(),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/mobile/lib/screens/chat_list_screen.dart b/mobile/lib/screens/chat_list_screen.dart
index d49bbd3fd..3c1606ecd 100644
--- a/mobile/lib/screens/chat_list_screen.dart
+++ b/mobile/lib/screens/chat_list_screen.dart
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
-import '../models/chat.dart';
import '../providers/auth_provider.dart';
import '../providers/chat_provider.dart';
import 'chat_conversation_screen.dart';
@@ -36,56 +35,17 @@ class _ChatListScreenState extends State {
await _loadChats();
}
- Future _createNewChat() async {
- final authProvider = Provider.of(context, listen: false);
- final chatProvider = Provider.of(context, listen: false);
+ Future _openNewChat() async {
+ if (!mounted) return;
- final accessToken = await authProvider.getValidAccessToken();
- if (accessToken == null) {
- await authProvider.logout();
- return;
- }
-
- // Show loading dialog
- if (mounted) {
- showDialog(
- context: context,
- barrierDismissible: false,
- builder: (context) => const Center(
- child: CircularProgressIndicator(),
- ),
- );
- }
-
- final chat = await chatProvider.createChat(
- accessToken: accessToken,
- title: Chat.defaultTitle,
+ await Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => const ChatConversationScreen(chatId: null),
+ ),
);
- // Close loading dialog
- if (mounted) {
- Navigator.pop(context);
- }
-
- if (chat != null && mounted) {
- // Navigate to chat conversation
- await Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => ChatConversationScreen(chatId: chat.id),
- ),
- );
-
- // Refresh list after returning
- _loadChats();
- } else if (mounted) {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(
- content: Text(chatProvider.errorMessage ?? 'Failed to create chat'),
- backgroundColor: Colors.red,
- ),
- );
- }
+ if (mounted) _loadChats();
}
String _formatDateTime(DateTime dateTime) {
@@ -297,7 +257,7 @@ class _ChatListScreenState extends State {
},
),
floatingActionButton: FloatingActionButton(
- onPressed: _createNewChat,
+ onPressed: _openNewChat,
tooltip: 'New Chat',
child: const Icon(Icons.add),
),
diff --git a/mobile/lib/screens/dashboard_screen.dart b/mobile/lib/screens/dashboard_screen.dart
index 9b872b7ab..b42c75ec8 100644
--- a/mobile/lib/screens/dashboard_screen.dart
+++ b/mobile/lib/screens/dashboard_screen.dart
@@ -1,3 +1,4 @@
+import 'dart:async';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/account.dart';
@@ -12,7 +13,6 @@ import '../widgets/net_worth_card.dart';
import '../widgets/currency_filter.dart';
import 'transaction_form_screen.dart';
import 'transactions_list_screen.dart';
-import 'log_viewer_screen.dart';
class DashboardScreen extends StatefulWidget {
const DashboardScreen({super.key});
@@ -25,6 +25,7 @@ class DashboardScreenState extends State {
final LogService _log = LogService.instance;
bool _showSyncSuccess = false;
int _previousPendingCount = 0;
+ Timer? _syncSuccessTimer;
TransactionsProvider? _transactionsProvider;
// Filter state
@@ -52,6 +53,7 @@ class DashboardScreenState extends State {
@override
void dispose() {
+ _syncSuccessTimer?.cancel();
_transactionsProvider?.removeListener(_onTransactionsChanged);
super.dispose();
}
@@ -61,28 +63,33 @@ class DashboardScreenState extends State {
if (transactionsProvider == null || !mounted) {
return;
}
-
+
final currentPendingCount = transactionsProvider.pendingCount;
- // If pending count decreased, it means transactions were synced
+ // Show sync success when pending count decreased (local transactions uploaded)
if (_previousPendingCount > 0 && currentPendingCount < _previousPendingCount) {
- setState(() {
- _showSyncSuccess = true;
- });
-
- // Hide the success indicator after 3 seconds
- Future.delayed(const Duration(seconds: 3), () {
- if (mounted) {
- setState(() {
- _showSyncSuccess = false;
- });
- }
- });
+ _showSyncSuccessIndicator();
}
_previousPendingCount = currentPendingCount;
}
+ void _showSyncSuccessIndicator() {
+ _syncSuccessTimer?.cancel();
+
+ setState(() {
+ _showSyncSuccess = true;
+ });
+
+ _syncSuccessTimer = Timer(const Duration(seconds: 3), () {
+ if (mounted) {
+ setState(() {
+ _showSyncSuccess = false;
+ });
+ }
+ });
+ }
+
Future _loadAccounts() async {
final authProvider = Provider.of(context, listen: false);
final accountsProvider = Provider.of(context, listen: false);
@@ -161,19 +168,23 @@ class DashboardScreenState extends State {
if (mounted) {
ScaffoldMessenger.of(context).clearSnackBars();
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(
- content: Row(
- children: [
- Icon(Icons.check_circle, color: Colors.white),
- SizedBox(width: 12),
- Text('Sync completed successfully'),
- ],
+ if (transactionsProvider.error != null) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Row(
+ children: [
+ const Icon(Icons.error, color: Colors.white),
+ const SizedBox(width: 12),
+ const Expanded(child: Text('Sync failed. Please try again.')),
+ ],
+ ),
+ backgroundColor: Colors.red,
+ duration: const Duration(seconds: 3),
),
- backgroundColor: Colors.green,
- duration: Duration(seconds: 2),
- ),
- );
+ );
+ } else {
+ _showSyncSuccessIndicator();
+ }
}
} catch (e) {
_log.error('DashboardScreen', 'Error in _performManualSync: $e');
@@ -339,83 +350,34 @@ class DashboardScreenState extends State {
}
}
- Future _handleLogout() async {
- final confirmed = await showDialog(
- context: context,
- builder: (context) => AlertDialog(
- title: const Text('Sign Out'),
- content: const Text('Are you sure you want to sign out?'),
- actions: [
- TextButton(
- onPressed: () => Navigator.pop(context, false),
- child: const Text('Cancel'),
- ),
- TextButton(
- onPressed: () => Navigator.pop(context, true),
- child: const Text('Sign Out'),
- ),
- ],
- ),
- );
-
- if (confirmed == true && mounted) {
- final authProvider = Provider.of(context, listen: false);
- final accountsProvider = Provider.of(context, listen: false);
-
- accountsProvider.clearAccounts();
- await authProvider.logout();
- }
- }
-
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Scaffold(
- appBar: AppBar(
- actions: [
- if (_showSyncSuccess)
- Padding(
- padding: const EdgeInsets.only(right: 8),
- child: AnimatedOpacity(
- opacity: _showSyncSuccess ? 1.0 : 0.0,
- duration: const Duration(milliseconds: 300),
- child: const Icon(
- Icons.cloud_done,
- color: Colors.green,
- size: 28,
- ),
- ),
- ),
- Semantics(
- label: 'Open debug logs',
- button: true,
- child: IconButton(
- icon: const Icon(Icons.bug_report),
- onPressed: () {
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => const LogViewerScreen()),
- );
- },
- tooltip: 'Debug Logs',
- ),
- ),
- IconButton(
- icon: const Icon(Icons.refresh),
- onPressed: _handleRefresh,
- tooltip: 'Refresh',
- ),
- IconButton(
- icon: const Icon(Icons.logout),
- onPressed: _handleLogout,
- tooltip: 'Sign Out',
- ),
- ],
- ),
body: Column(
children: [
const ConnectivityBanner(),
+ if (_showSyncSuccess)
+ AnimatedOpacity(
+ opacity: _showSyncSuccess ? 1.0 : 0.0,
+ duration: const Duration(milliseconds: 300),
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
+ color: Colors.green.withValues(alpha: 0.1),
+ child: const Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Icon(Icons.cloud_done, color: Colors.green, size: 18),
+ SizedBox(width: 8),
+ Text(
+ 'Synced',
+ style: TextStyle(color: Colors.green, fontSize: 13),
+ ),
+ ],
+ ),
+ ),
+ ),
Expanded(
child: Consumer2(
builder: (context, authProvider, accountsProvider, _) {
@@ -516,6 +478,8 @@ class DashboardScreenState extends State {
});
},
formatAmount: _formatAmount,
+ netWorthFormatted: accountsProvider.netWorthFormatted,
+ isStale: accountsProvider.isBalanceSheetStale,
),
),
diff --git a/mobile/lib/screens/settings_screen.dart b/mobile/lib/screens/settings_screen.dart
index 04b8e2649..867e22c7b 100644
--- a/mobile/lib/screens/settings_screen.dart
+++ b/mobile/lib/screens/settings_screen.dart
@@ -3,10 +3,12 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import '../providers/auth_provider.dart';
+import '../providers/theme_provider.dart';
import '../services/offline_storage_service.dart';
import '../services/log_service.dart';
import '../services/preferences_service.dart';
import '../services/user_service.dart';
+import 'log_viewer_screen.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@@ -354,6 +356,22 @@ class _SettingsScreenState extends State {
onTap: () => _launchContactUrl(context),
),
+ Semantics(
+ label: 'Open debug logs',
+ button: true,
+ child: ListTile(
+ leading: const Icon(Icons.bug_report),
+ title: const Text('Debug Logs'),
+ subtitle: const Text('View app diagnostic logs'),
+ onTap: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(builder: (context) => const LogViewerScreen()),
+ );
+ },
+ ),
+ ),
+
const Divider(),
// Display Settings Section
@@ -382,6 +400,37 @@ class _SettingsScreenState extends State {
},
),
+ Consumer(
+ builder: (context, themeProvider, _) {
+ return ListTile(
+ leading: const Icon(Icons.brightness_6_outlined),
+ title: const Text('Theme'),
+ trailing: SegmentedButton(
+ segments: const [
+ ButtonSegment(
+ value: ThemeMode.light,
+ icon: Icon(Icons.light_mode, size: 18),
+ tooltip: 'Light',
+ ),
+ ButtonSegment(
+ value: ThemeMode.system,
+ icon: Icon(Icons.brightness_auto, size: 18),
+ tooltip: 'System',
+ ),
+ ButtonSegment(
+ value: ThemeMode.dark,
+ icon: Icon(Icons.dark_mode, size: 18),
+ tooltip: 'Dark',
+ ),
+ ],
+ selected: {themeProvider.themeMode},
+ onSelectionChanged: (modes) => themeProvider.setThemeMode(modes.first),
+ showSelectedIcon: false,
+ ),
+ );
+ },
+ ),
+
const Divider(),
// Data Management Section
diff --git a/mobile/lib/screens/sso_onboarding_screen.dart b/mobile/lib/screens/sso_onboarding_screen.dart
new file mode 100644
index 000000000..3fee35077
--- /dev/null
+++ b/mobile/lib/screens/sso_onboarding_screen.dart
@@ -0,0 +1,373 @@
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import '../providers/auth_provider.dart';
+
+class SsoOnboardingScreen extends StatefulWidget {
+ const SsoOnboardingScreen({super.key});
+
+ @override
+ State createState() => _SsoOnboardingScreenState();
+}
+
+class _SsoOnboardingScreenState extends State {
+ bool _showLinkForm = true;
+ final _linkFormKey = GlobalKey();
+ final _createFormKey = GlobalKey();
+ final _emailController = TextEditingController();
+ final _passwordController = TextEditingController();
+ final _firstNameController = TextEditingController();
+ final _lastNameController = TextEditingController();
+ bool _obscurePassword = true;
+
+ @override
+ void initState() {
+ super.initState();
+ final authProvider = Provider.of(context, listen: false);
+ _emailController.text = authProvider.ssoEmail ?? '';
+ _firstNameController.text = authProvider.ssoFirstName ?? '';
+ _lastNameController.text = authProvider.ssoLastName ?? '';
+ }
+
+ @override
+ void dispose() {
+ _emailController.dispose();
+ _passwordController.dispose();
+ _firstNameController.dispose();
+ _lastNameController.dispose();
+ super.dispose();
+ }
+
+ Future _handleLinkAccount() async {
+ if (!_linkFormKey.currentState!.validate()) return;
+
+ final authProvider = Provider.of(context, listen: false);
+ await authProvider.ssoLinkAccount(
+ email: _emailController.text.trim(),
+ password: _passwordController.text,
+ );
+ }
+
+ Future _handleCreateAccount() async {
+ if (!_createFormKey.currentState!.validate()) return;
+
+ final authProvider = Provider.of(context, listen: false);
+ await authProvider.ssoCreateAccount(
+ firstName: _firstNameController.text.trim(),
+ lastName: _lastNameController.text.trim(),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final colorScheme = Theme.of(context).colorScheme;
+
+ return Scaffold(
+ appBar: AppBar(
+ leading: IconButton(
+ icon: const Icon(Icons.arrow_back),
+ onPressed: () {
+ Provider.of(context, listen: false)
+ .cancelSsoOnboarding();
+ },
+ ),
+ title: const Text('Link Your Account'),
+ ),
+ body: SafeArea(
+ child: SingleChildScrollView(
+ padding: const EdgeInsets.all(24),
+ child: Consumer(
+ builder: (context, authProvider, _) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ // Header
+ SvgPicture.asset(
+ 'assets/images/google_g_logo.svg',
+ width: 48,
+ height: 48,
+ ),
+ const SizedBox(height: 16),
+ Text(
+ authProvider.ssoEmail != null
+ ? 'Signed in as ${authProvider.ssoEmail}'
+ : 'Google account verified',
+ textAlign: TextAlign.center,
+ style: Theme.of(context).textTheme.bodyLarge?.copyWith(
+ color: colorScheme.onSurfaceVariant,
+ ),
+ ),
+ const SizedBox(height: 24),
+
+ // Error message
+ if (authProvider.errorMessage != null)
+ Container(
+ padding: const EdgeInsets.all(12),
+ margin: const EdgeInsets.only(bottom: 16),
+ decoration: BoxDecoration(
+ color: colorScheme.errorContainer,
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Row(
+ children: [
+ Icon(Icons.error_outline, color: colorScheme.error),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Text(
+ authProvider.errorMessage!,
+ style: TextStyle(
+ color: colorScheme.onErrorContainer),
+ ),
+ ),
+ IconButton(
+ icon: const Icon(Icons.close),
+ onPressed: () => authProvider.clearError(),
+ iconSize: 20,
+ ),
+ ],
+ ),
+ ),
+
+ // Tab selector
+ if (authProvider.ssoAllowAccountCreation) ...[
+ Container(
+ decoration: BoxDecoration(
+ color: colorScheme.surfaceContainerHighest
+ .withValues(alpha: 0.3),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Row(
+ children: [
+ Expanded(
+ child: _TabButton(
+ label: 'Link Existing',
+ isSelected: _showLinkForm,
+ onTap: () =>
+ setState(() => _showLinkForm = true),
+ ),
+ ),
+ Expanded(
+ child: _TabButton(
+ label: authProvider.ssoHasPendingInvitation
+ ? 'Accept Invitation'
+ : 'Create New',
+ isSelected: !_showLinkForm,
+ onTap: () =>
+ setState(() => _showLinkForm = false),
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 24),
+ ],
+
+ // Link existing account form
+ if (_showLinkForm) _buildLinkForm(authProvider, colorScheme),
+
+ // Create new account form
+ if (!_showLinkForm)
+ _buildCreateForm(authProvider, colorScheme),
+ ],
+ );
+ },
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _buildLinkForm(AuthProvider authProvider, ColorScheme colorScheme) {
+ return Form(
+ key: _linkFormKey,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Container(
+ padding: const EdgeInsets.all(12),
+ decoration: BoxDecoration(
+ color: colorScheme.primaryContainer.withValues(alpha: 0.3),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Row(
+ children: [
+ Icon(Icons.link, color: colorScheme.primary),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Text(
+ 'Enter your existing account credentials to link with Google Sign-In.',
+ style: TextStyle(color: colorScheme.onSurface),
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 16),
+ TextFormField(
+ controller: _emailController,
+ keyboardType: TextInputType.emailAddress,
+ autocorrect: false,
+ textInputAction: TextInputAction.next,
+ decoration: const InputDecoration(
+ labelText: 'Email',
+ prefixIcon: Icon(Icons.email_outlined),
+ ),
+ validator: (value) {
+ if (value == null || value.isEmpty) return 'Please enter your email';
+ if (!value.contains('@')) return 'Please enter a valid email';
+ return null;
+ },
+ ),
+ const SizedBox(height: 16),
+ TextFormField(
+ controller: _passwordController,
+ obscureText: _obscurePassword,
+ textInputAction: TextInputAction.done,
+ decoration: InputDecoration(
+ labelText: 'Password',
+ prefixIcon: const Icon(Icons.lock_outlined),
+ suffixIcon: IconButton(
+ icon: Icon(
+ _obscurePassword
+ ? Icons.visibility_outlined
+ : Icons.visibility_off_outlined,
+ ),
+ onPressed: () {
+ setState(() => _obscurePassword = !_obscurePassword);
+ },
+ ),
+ ),
+ validator: (value) {
+ if (value == null || value.isEmpty) return 'Please enter your password';
+ return null;
+ },
+ onFieldSubmitted: (_) => _handleLinkAccount(),
+ ),
+ const SizedBox(height: 24),
+ ElevatedButton(
+ onPressed: authProvider.isLoading ? null : _handleLinkAccount,
+ child: authProvider.isLoading
+ ? const SizedBox(
+ height: 20,
+ width: 20,
+ child: CircularProgressIndicator(strokeWidth: 2),
+ )
+ : const Text('Link Account'),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _buildCreateForm(AuthProvider authProvider, ColorScheme colorScheme) {
+ final hasPendingInvitation = authProvider.ssoHasPendingInvitation;
+ return Form(
+ key: _createFormKey,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Container(
+ padding: const EdgeInsets.all(12),
+ decoration: BoxDecoration(
+ color: colorScheme.primaryContainer.withValues(alpha: 0.3),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Row(
+ children: [
+ Icon(
+ hasPendingInvitation ? Icons.mail_outline : Icons.person_add,
+ color: colorScheme.primary,
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Text(
+ hasPendingInvitation
+ ? 'You have a pending invitation. Accept it to join an existing household.'
+ : 'Create a new account using your Google identity.',
+ style: TextStyle(color: colorScheme.onSurface),
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 16),
+ TextFormField(
+ controller: _firstNameController,
+ textInputAction: TextInputAction.next,
+ decoration: const InputDecoration(
+ labelText: 'First Name',
+ prefixIcon: Icon(Icons.person_outlined),
+ ),
+ validator: (value) {
+ if (value == null || value.isEmpty) return 'Please enter your first name';
+ return null;
+ },
+ ),
+ const SizedBox(height: 16),
+ TextFormField(
+ controller: _lastNameController,
+ textInputAction: TextInputAction.done,
+ decoration: const InputDecoration(
+ labelText: 'Last Name',
+ prefixIcon: Icon(Icons.person_outlined),
+ ),
+ validator: (value) {
+ if (value == null || value.isEmpty) return 'Please enter your last name';
+ return null;
+ },
+ onFieldSubmitted: (_) => _handleCreateAccount(),
+ ),
+ const SizedBox(height: 24),
+ ElevatedButton(
+ onPressed: authProvider.isLoading ? null : _handleCreateAccount,
+ child: authProvider.isLoading
+ ? const SizedBox(
+ height: 20,
+ width: 20,
+ child: CircularProgressIndicator(strokeWidth: 2),
+ )
+ : Text(hasPendingInvitation
+ ? 'Accept Invitation'
+ : 'Create Account'),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class _TabButton extends StatelessWidget {
+ final String label;
+ final bool isSelected;
+ final VoidCallback onTap;
+
+ const _TabButton({
+ required this.label,
+ required this.isSelected,
+ required this.onTap,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final colorScheme = Theme.of(context).colorScheme;
+
+ return GestureDetector(
+ onTap: onTap,
+ child: Container(
+ padding: const EdgeInsets.symmetric(vertical: 12),
+ decoration: BoxDecoration(
+ color: isSelected ? colorScheme.primary : Colors.transparent,
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Text(
+ label,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ color: isSelected ? colorScheme.onPrimary : colorScheme.onSurface,
+ fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/mobile/lib/services/auth_service.dart b/mobile/lib/services/auth_service.dart
index 98a98f31e..e31c34554 100644
--- a/mobile/lib/services/auth_service.dart
+++ b/mobile/lib/services/auth_service.dart
@@ -364,6 +364,20 @@ class AuthService {
Future