Skip to content

feat: add 2FA enforcement fail card#1356

Merged
riderx merged 3 commits intomainfrom
WcaleNieWolny/2fa-enforce-fail-card
Jan 4, 2026
Merged

feat: add 2FA enforcement fail card#1356
riderx merged 3 commits intomainfrom
WcaleNieWolny/2fa-enforce-fail-card

Conversation

@WcaleNieWolny
Copy link
Copy Markdown
Contributor

@WcaleNieWolny WcaleNieWolny commented Jan 3, 2026

Summary (AI generated)

Implement 2FA enforcement UI messaging when users don't have 2FA enabled but their organization requires it. Shows a fail card similar to password policy enforcement with "Setup 2FA Now" button that auto-triggers the 2FA setup flow. Prevents unnecessary data loading when access is denied.

Test plan (AI generated)

  • Enable 2FA enforcement on an organization
  • Log in as a user without 2FA enabled
  • Verify the 2FA enforcement card displays on dashboard, apps list, and organization settings
  • Verify "Setup 2FA Now" button redirects to account settings and auto-opens 2FA setup dialog
  • Verify apps and usage data don't load when 2FA access is denied

Checklist (AI generated)

  • My code follows the code style of this project and passes linting
  • My change requires a change to the documentation
  • My change has adequate E2E test coverage
  • I have tested my code manually

Motivation

I want to improve the frontend as the current implementation differs from the implementation described in #1291. I believe that 2FA enforcement is an imporant feautre and fixing the frontend is important for it to work properly.

Business impact

4/9 - Lowly Medium - The dashboard is the place where people interact with Capgo, so if 2FA enforcement doesn't work and doesn't show a clear error users will not understand capgo and they will become frustrated. This could lead to missed saled.

Changes (Human generated)

Before

CleanShot 2026-01-03 at 15 59 52@2x CleanShot 2026-01-03 at 16 00 31@2x CleanShot 2026-01-03 at 16 01 14@2x CleanShot 2026-01-03 at 16 01 51@2x CleanShot 2026-01-03 at 16 02 03@2x

After

CleanShot 2026-01-03 at 16 02 21@2x CleanShot 2026-01-03 at 16 02 28@2x CleanShot 2026-01-03 at 16 02 44@2x

Summary by CodeRabbit

  • New Features

    • 2FA setup prompts in account settings and in-product notices; UI cards that block access when security setup is required.
    • Organization ID copy button with success/error toasts and direct link to enable 2FA/setup flows.
  • Improvements

    • Expanded localization: new user-facing strings added across many languages (placeholders included) to support 2FA, webhooks, password policy, and related UI.
    • More resilient dashboard updates with safer error handling.

✏️ Tip: You can customize this high-level summary in your review settings.

Implement 2FA enforcement UI when users don't have 2FA enabled but their organization requires it. Shows a fail card similar to password policy enforcement with a "Setup 2FA Now" button that auto-triggers the 2FA setup flow. Prevents data loading and displays enforcement message across dashboard, apps list, and organization settings pages.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 3, 2026

📝 Walkthrough

Walkthrough

Adds 2FA/password-compliance gating across UI and store: localized strings added; pages/components (FailedCard, Banner, app, dashboard, settings layout) detect redacted security access and block or reroute flows; account settings auto-opens MFA setup via query; org store avoids false failure states and swallows dashboard update errors.

Changes

Cohort / File(s) Summary
Localization
messages/en.json, messages/*.json (many locales: de.json, es.json, fr.json, hi.json, id.json, it.json, ja.json, ko.json, pl.json, pt-br.json, ru.json, tr.json, vi.json, zh-cn.json)
Added 2fa-setup-required, 2fa-setup-org-access, setup-2fa-now in en.json and corresponding keys plus many webhook/password-policy placeholders across multiple locale files.
Banner UI
src/components/Banner.vue
Added lacksSecurityAccess computed and gated bannerText to suppress billing/banner when security access is indeterminate.
Blocking/Failure Card
src/components/FailedCard.vue
Route-aware toasts, needs2FASetup computed, showCopyOrgId control, copyOrgId() clipboard action, goToAccountSettings() navigation; template updated to show 2FA/password prompts and copy-org-id UI.
Settings Layout
src/layouts/settings.vue
Added needsSecurityCompliance / shouldBlockContent; render FailedCard and hide secondary tabs when content should be blocked.
App & Dashboard Pages
src/pages/app/index.vue, src/pages/dashboard.vue
Added lacksSecurityAccess computed; early-return from data fetches, clear lists, and render FailedCard instead of normal content when access is lacking.
Account Settings
src/pages/settings/account/index.vue
Reads ?setup2fa=true on mount, clears query, uses nextTick and triggers handleMfa() to open MFA setup if needed.
Organization Store
src/stores/organization.ts
Avoids marking org as failed when access is redacted by enforced 2FA/password policy; wrapped updateDashboard calls in try/catch to swallow/log errors and continue.
Manifest
package.json
(Listed in manifest tag; no functional changes described in summary.)

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as Component/Page
    participant Store as Org Store
    participant Router as Router (Account Settings)

    User->>UI: Request page (dashboard/app/settings)
    UI->>Store: Read org security flags (enforcing_2fa, 2fa_has_access, password_policy_config, password_has_access)
    Store-->>UI: Return org state
    UI->>UI: Compute lacksSecurityAccess

    alt Security access denied (redacted)
        UI->>UI: Render FailedCard with 2FA/password prompts
        User->>UI: Click "Setup 2FA Now"
        UI->>Router: Navigate to account settings with ?setup2fa=true
        Router->>Router: onMounted sees setup2fa, clears query
        Router->>UI: Call handleMfa() → open MFA setup dialog
    else Security access granted
        UI->>Store: Fetch apps/dashboard data
        Store-->>UI: Return data
        UI->>User: Render normal content
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • riderx

Poem

🐰 I hop to guard each login door,
Prompt 2FA and nudge once more,
A FailedCard points the way,
Click to set your keys today—
Carrots safe, and hackers snore. 🥕🔐

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a UI failure card for 2FA enforcement, which is the core feature of this pull request.
Description check ✅ Passed The description provides a clear summary, test plan, and motivation for the changes. However, E2E test coverage is marked as unchecked, indicating inadequate test coverage as per the template requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/components/Banner.vue (1)

49-55: Same duplication issue as dashboard.vue.

This lacksSecurityAccess computed is identical to the one in src/pages/dashboard.vue (lines 22-28) and src/pages/app/index.vue (lines 28-34). Please see the refactoring suggestion in the dashboard.vue review comment to centralize this logic in the organization store.

src/pages/app/index.vue (1)

28-34: Same duplication issue as dashboard.vue and Banner.vue.

This is the third instance of identical lacksSecurityAccess logic. Please see the refactoring suggestion in the dashboard.vue review comment to centralize this in the organization store.

🧹 Nitpick comments (2)
src/pages/dashboard.vue (1)

22-28: Refactor duplicated security access logic to organization store.

The lacksSecurityAccess computed is duplicated identically in src/pages/app/index.vue (lines 28-34) and src/components/Banner.vue (lines 49-55). This violates the DRY principle and increases maintenance burden.

🔎 Recommended refactor: Add getter to organization store

In src/stores/organization.ts, add a getter:

+  // Check if user lacks security compliance (2FA or password)
+  get lacksSecurityAccess(): boolean {
+    const org = this.currentOrganization
+    const lacks2FA = org?.enforcing_2fa === true && org?.['2fa_has_access'] === false
+    const lacksPassword = org?.password_policy_config?.enabled && org?.password_has_access === false
+    return lacks2FA || lacksPassword
+  }

Then replace local computed properties in dashboard.vue, app/index.vue, and Banner.vue:

-const lacksSecurityAccess = computed(() => {
-  const org = organizationStore.currentOrganization
-  const lacks2FA = org?.enforcing_2fa === true && org?.['2fa_has_access'] === false
-  const lacksPassword = org?.password_policy_config?.enabled && org?.password_has_access === false
-  return lacks2FA || lacksPassword
-})
+const lacksSecurityAccess = computed(() => organizationStore.lacksSecurityAccess)
src/stores/organization.ts (1)

287-295: Consider extracting duplicate security-check logic into a helper.

This logic duplicates lines 86-94 in the watch handler. Extracting it into a reusable function would improve maintainability and reduce the risk of divergence.

🔎 Proposed refactor
+function shouldMarkOrgAsFailed(org: Organization | undefined): boolean {
+  if (!org) return false
+  const lacks2FAAccess = org.enforcing_2fa === true && org['2fa_has_access'] === false
+  const lacksPasswordAccess = org.password_policy_config?.enabled && org.password_has_access === false
+  if (lacks2FAAccess || lacksPasswordAccess) {
+    return false
+  }
+  return !(!!org.paying || (org.trial_left ?? 0) > 0)
+}

Then use currentOrganizationFailed.value = shouldMarkOrgAsFailed(currentOrganizationRaw) in both locations.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b46b05 and 7fe8a09.

📒 Files selected for processing (8)
  • messages/en.json
  • src/components/Banner.vue
  • src/components/FailedCard.vue
  • src/layouts/settings.vue
  • src/pages/app/index.vue
  • src/pages/dashboard.vue
  • src/pages/settings/account/index.vue
  • src/stores/organization.ts
🧰 Additional context used
📓 Path-based instructions (13)
src/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.vue: Use Vue 3 with Composition API and <script setup> syntax for frontend components
Style components using TailwindCSS with DaisyUI components

src/**/*.vue: Use Vue 3 <script setup> syntax exclusively for all Vue component scripts
Use Tailwind utility classes for layout and spacing in Vue components
Use DaisyUI components (d-btn, d-input, d-card) for interactive elements in Vue components
Use Konsta components ONLY for safe area helpers (top/bottom insets) in Vue components; avoid other uses
Use useRoute() from vue-router to access route parameters and useRouter() for programmatic navigation in Vue components

Use DaisyUI (d- prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent

Files:

  • src/pages/dashboard.vue
  • src/layouts/settings.vue
  • src/pages/settings/account/index.vue
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
  • src/components/Banner.vue
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use single quotes and no semicolons per @antfu/eslint-config

Files:

  • src/pages/dashboard.vue
  • src/layouts/settings.vue
  • src/pages/settings/account/index.vue
  • src/stores/organization.ts
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
  • src/components/Banner.vue
src/pages/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

Use file-based routing with unplugin-vue-router for frontend pages

Frontend file-based routing uses src/pages/ directory structure; routes auto-generate with unplugin-vue-router and types are available in src/typed-router.d.ts

Files:

  • src/pages/dashboard.vue
  • src/pages/settings/account/index.vue
  • src/pages/app/index.vue
{capacitor.config.{ts,js},src/**/*.{ts,tsx,vue}}

📄 CodeRabbit inference engine (CLAUDE.md)

Mobile apps should use Capacitor with app ID ee.forgr.capacitor_go for native mobile functionality

Files:

  • src/pages/dashboard.vue
  • src/layouts/settings.vue
  • src/pages/settings/account/index.vue
  • src/stores/organization.ts
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
  • src/components/Banner.vue
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ~/ alias for imports from src/ directory in frontend TypeScript and Vue components

Files:

  • src/pages/dashboard.vue
  • src/layouts/settings.vue
  • src/pages/settings/account/index.vue
  • src/stores/organization.ts
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
  • src/components/Banner.vue
src/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Frontend ESLint must pass before commit; run bun lint:fix to auto-fix issues in frontend files

Files:

  • src/pages/dashboard.vue
  • src/layouts/settings.vue
  • src/pages/settings/account/index.vue
  • src/stores/organization.ts
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
  • src/components/Banner.vue
src/**/*.{vue,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS

Files:

  • src/pages/dashboard.vue
  • src/layouts/settings.vue
  • src/pages/settings/account/index.vue
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
  • src/components/Banner.vue
src/**/*.{vue,ts,tsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

Konsta components are reserved for the safe area helpers; avoid importing konsta anywhere else in the app

Files:

  • src/pages/dashboard.vue
  • src/layouts/settings.vue
  • src/pages/settings/account/index.vue
  • src/stores/organization.ts
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
  • src/components/Banner.vue
src/**/*.{css,scss,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Mirror the Capgo design palette from src/styles/style.css (e.g., --color-primary-500: #515271, --color-azure-500: #119eff) when introducing new UI, using deep slate bases with the Extract azure highlight and soft radii

Files:

  • src/pages/dashboard.vue
  • src/layouts/settings.vue
  • src/pages/settings/account/index.vue
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
  • src/components/Banner.vue
src/layouts/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

Page layout components should be organized in src/layouts/ directory

Files:

  • src/layouts/settings.vue
src/stores/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Pinia stores for state management

Files:

  • src/stores/organization.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript strict mode with path aliases mapping ~/ to src/

Files:

  • src/stores/organization.ts
src/components/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

Reusable Vue components should be organized in src/components/ directory

Files:

  • src/components/FailedCard.vue
  • src/components/Banner.vue
🧠 Learnings (7)
📓 Common learnings
Learnt from: WcaleNieWolny
Repo: Cap-go/capgo PR: 1300
File: supabase/migrations/20251224103713_2fa_enforcement.sql:85-96
Timestamp: 2025-12-25T11:22:19.594Z
Learning: In the 2FA enforcement implementation for supabase/migrations: When an org has enforcing_2fa=true, all users including super_admins must have 2FA enabled before accessing any org functions (including check_org_members_2fa_enabled); this is intentional behavior to ensure consistent security enforcement without exceptions for admins.
📚 Learning: 2025-12-05T17:34:25.556Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T17:34:25.556Z
Learning: Applies to src/stores/**/*.{ts,tsx} : Use Pinia stores for state management

Applied to files:

  • src/pages/dashboard.vue
  • src/pages/app/index.vue
📚 Learning: 2025-12-05T17:34:25.556Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T17:34:25.556Z
Learning: Applies to src/**/*.vue : Use Vue 3 with Composition API and `<script setup>` syntax for frontend components

Applied to files:

  • src/pages/dashboard.vue
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
📚 Learning: 2025-12-23T01:19:04.593Z
Learnt from: riderx
Repo: Cap-go/capgo PR: 1297
File: src/components/dashboard/DeploymentBanner.vue:77-79
Timestamp: 2025-12-23T01:19:04.593Z
Learning: In the Cap-go codebase, ensure that app permission checks never include the role 'owner'. App-level permissions should be based on the user_min_right enum with values: read, upload, write, admin, super_admin (and NOT owner). This pattern applies across Vue components that perform permission checks; if you find a check referencing 'owner' for app-level access, replace it with the appropriate user_min_right value and keep organization-level owner handling in organization.ts.

Applied to files:

  • src/pages/dashboard.vue
  • src/layouts/settings.vue
  • src/pages/settings/account/index.vue
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
  • src/components/Banner.vue
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to src/**/*.vue : Use `useRoute()` from `vue-router` to access route parameters and `useRouter()` for programmatic navigation in Vue components

Applied to files:

  • src/layouts/settings.vue
  • src/pages/settings/account/index.vue
  • src/components/FailedCard.vue
  • src/pages/app/index.vue
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to src/**/*.vue : Use Vue 3 `<script setup>` syntax exclusively for all Vue component scripts

Applied to files:

  • src/pages/app/index.vue
📚 Learning: 2025-12-05T17:34:25.556Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T17:34:25.556Z
Learning: Applies to {capacitor.config.{ts,js},src/**/*.{ts,tsx,vue}} : Mobile apps should use Capacitor with app ID `ee.forgr.capacitor_go` for native mobile functionality

Applied to files:

  • src/components/Banner.vue
🪛 GitHub Actions: Run tests
src/layouts/settings.vue

[error] 10-10: perfectionist/sort-imports: Expected "/components/FailedCard.vue" to come before "/components/Tabs.vue"

🔇 Additional comments (12)
src/pages/settings/account/index.vue (1)

7-7: LGTM! Well-implemented auto-trigger for 2FA setup.

The route-aware MFA auto-setup flow is clean and well-commented. Using nextTick() after clearing the query parameter is a smart approach to prevent the DialogV2's route watcher from closing the dialog prematurely.

Also applies to: 9-9, 28-28, 548-556

src/pages/dashboard.vue (1)

33-37: LGTM! Appropriate security gating for data fetch.

The early return prevents unnecessary data fetching when security access is denied, which is the correct behavior per the PR objectives.

src/layouts/settings.vue (1)

22-33: LGTM! Smart security gating that preserves account access.

The shouldBlockContent logic correctly blocks only organization settings while allowing account settings access, which is essential—users need to access account settings to enable 2FA and resolve the enforcement issue. The comment on Line 30 helpfully explains this design decision.

src/components/Banner.vue (1)

70-72: LGTM! Appropriate guard for unreliable data.

Preventing the billing banner from displaying when security access is lacking is correct—the organization data may be incomplete or unreliable in this state.

src/pages/app/index.vue (1)

45-50: LGTM! Consistent security gating.

The early return matches the pattern in dashboard.vue and correctly prevents data fetching when security access is denied.

messages/en.json (1)

1381-1383: LGTM!

The new i18n keys for 2FA enforcement follow the established naming conventions and message patterns (consistent with the password policy keys like password-policy-required and update-password-now). The messages are clear and user-friendly.

src/components/FailedCard.vue (4)

1-11: LGTM!

Imports are appropriate and follow the coding guidelines. Using useRoute() from vue-router for route awareness and vue-sonner for toast notifications aligns with project patterns.


19-28: LGTM!

The computed properties correctly implement the security access priority chain (password policy → 2FA → subscription). The bracket notation org?.['2fa_has_access'] is the correct approach for properties starting with a digit.


38-55: LGTM!

The goToAccountSettings navigation with query param and copyOrgId clipboard function are well-implemented. Good defensive programming with the early return on missing orgId, and proper error handling with user-friendly toast feedback.


93-125: LGTM!

The 2FA enforcement card follows the established visual pattern from the password policy card. The shield icon is appropriate for 2FA security messaging, and the v-else-if chain correctly prioritizes security policies over subscription status.

src/stores/organization.ts (2)

86-94: LGTM!

The security access gating logic correctly prevents marking the organization as "failed" when the user lacks 2FA or password access. The comment accurately explains that the data is redacted and unreliable in these states. Based on learnings, this aligns with the intentional 2FA enforcement behavior that applies to all users without exceptions.


112-118: LGTM!

Wrapping updateDashboard in try/catch prevents dashboard fetch failures from blocking the UI flow when users lack security access. Logging the error is appropriate for debugging.

WcaleNieWolny and others added 2 commits January 3, 2026 16:05
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added new translations for 2FA setup access and requirement messages in German, Spanish, French, Hindi, Indonesian, Italian, Japanese, Korean, Polish, Portuguese, Russian, Turkish, Vietnamese, and Chinese. This enhances user experience by providing clear instructions for enabling 2FA across different languages.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Jan 3, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (8)
messages/hi.json (1)

134-183: Empty-string translations may cause blank UI instead of fallback text

Several newly added keys in this area (for example add-webhook, allow-dev-builds, allow-device, allow-prod-builds, and nearby notification/security keys) are defined with "" as the value rather than omitting the key or providing a fallback string. If your i18n layer treats an empty string as a valid translation, these labels may render blank instead of falling back to English, and similar patterns appear for other new keys further down the file (webhook, password-policy, org-notifications, etc.).

Please confirm the runtime behavior and either:

  • rely on a documented “empty means fallback” mechanism, or
  • temporarily populate these with English copies / remove the keys until proper translations are available.
messages/ko.json (1)

134-183: Review empty Korean translations for new webhook/security/password keys

As in the Hindi locale, several newly added keys here and further down (e.g. add-webhook, allow-dev-builds, allow-device, allow-prod-builds, plus a number of webhook/password-policy/org-notification keys) are present with an empty string. Unless your i18n implementation explicitly falls back when the value is "", these may render as missing labels/tooltips instead of falling back to English.

Consider confirming the fallback semantics and, if needed, temporarily populating these keys with English text or removing them until translations are ready.

messages/pt-br.json (1)

134-183: Confirm impact of empty PT‑BR strings on i18n fallback

New keys added here and elsewhere in this file (e.g. add-webhook, the various allow-*-builds and assigned, plus many webhook/password/notification keys) use "" instead of localized or fallback text. Depending on your translation runtime, that can yield blank UI text rather than falling back to the English base strings.

It’s worth confirming:

  • whether empty values fall back automatically, and
  • if not, whether you’d prefer to duplicate the English text for now or omit the keys until proper translations are provided.
messages/zh-cn.json (1)

134-183: Empty zh‑CN translations across new keys may lead to missing labels

As with the other locales, several newly added zh‑CN keys (e.g. add-webhook, the new allow-* flags, and many webhook/password/notification/setting keys lower in the file) are defined with empty-string values. If the i18n layer treats these as valid translations rather than “missing”, users may see blank labels/tooltips instead of English fallback.

Please double-check how your message loader handles "", and consider either:

  • letting the key be absent until localized, or
  • populating with the English text so at least something is rendered.
messages/fr.json (1)

134-135: Empty-string translations may suppress English fallback and render blank labels

Many newly added keys (for example allow-dev-builds, allow-device, bundle-deployed, several password-policy and notification keys) are present but set to "". If your i18n stack treats an empty string as a valid translation (no fallback), any UI using these keys will show blank text in French.

Consider either:

  • copying the English text for now, or
  • omitting keys from non-primary locales until you have proper translations so the default locale can safely fall back.

Also applies to: 173-182, 211-212, 280-281, 414-416, 428-429, 648-649, 769-775, 881-882, 907-908, 927-928, 941-943, 953-955, 969-979, 1042-1054, 1062-1065

messages/tr.json (1)

134-135: Review empty-string placeholders to avoid blank Turkish UI text

A large number of new keys (e.g. add-webhook, allow-dev-builds, allow-device, many password-policy and notification keys) are defined but set to "". If your localization layer does not fall back when a key exists but is empty, these will render as missing labels/tooltips in Turkish.

Consider either:

  • temporarily populating them with the English source text, or
  • removing them until proper Turkish translations are ready so fallback behavior remains intact.

Also applies to: 173-182, 211-212, 280-281, 414-416, 428-429, 648-649, 769-775, 881-882, 907-908, 927-928, 941-943, 953-955, 969-979, 1042-1054, 1062-1065

messages/id.json (1)

134-135: Empty-string placeholders across many new keys may cause blank Indonesian UI strings

Similar to the other locales, many newly introduced keys for webhooks, password policy, notifications, and security are present but set to "". If your i18n handling does not fall back when a translation exists but is empty, any UI that uses those keys will show missing text in Indonesian.

It’s worth auditing which of these keys are used in current UI and either:

  • seed them with English source text, or
  • drop them from this locale until proper translations are available.

Also applies to: 173-182, 211-212, 280-281, 414-416, 428-429, 648-649, 769-775, 881-882, 907-908, 927-928, 941-943, 953-955, 969-979, 1042-1054, 1062-1065

messages/de.json (1)

174-177: Tidy up empty/English fallbacks for German ancillary labels before features go live

A number of new keys are present but not localized, for example:

  • Empty strings: allow-dev-builds, allow-device, allow-prod-builds, assigned, bundle-deployed, characters, characters-minimum, completed-at, disabled, several password‑policy toggles (enable-password-policy, enable-policy, enforce-password-policy*, password-action-required, etc.), saving, and others.
  • Mixed language: your-api-key is "Your API Key" while the rest of the section is German.

While most critical paths (2FA and webhook CRUD) are correctly translated, these placeholders will render as blank or English fragments in a German UI once you wire those features.

Suggested approach:

  • For any key already used in UI, provide at least a basic German string or copy the English text as a temporary value, rather than leaving it "".
  • Localize your-api-key (e.g. “Dein API‑Schlüssel”) for consistency, unless you intentionally want the English phrasing.

It’s worth double‑checking how your i18n runtime treats "" values; if they suppress fallback to the base language, you’ll want to avoid "" for any user‑visible key.

Also applies to: 211-212, 280-281, 414-416, 428-429, 685-686, 881-882, 1042-1043, 1053-1054, 1062-1065, 1182-1182, 1408-1408

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc9d672 and 7976fa6.

📒 Files selected for processing (14)
  • messages/de.json
  • messages/es.json
  • messages/fr.json
  • messages/hi.json
  • messages/id.json
  • messages/it.json
  • messages/ja.json
  • messages/ko.json
  • messages/pl.json
  • messages/pt-br.json
  • messages/ru.json
  • messages/tr.json
  • messages/vi.json
  • messages/zh-cn.json
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: WcaleNieWolny
Repo: Cap-go/capgo PR: 1300
File: supabase/migrations/20251224103713_2fa_enforcement.sql:85-96
Timestamp: 2025-12-25T11:22:19.594Z
Learning: In the 2FA enforcement implementation for supabase/migrations: When an org has enforcing_2fa=true, all users including super_admins must have 2FA enabled before accessing any org functions (including check_org_members_2fa_enabled); this is intentional behavior to ensure consistent security enforcement without exceptions for admins.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run tests
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (17)
messages/hi.json (1)

22-24: 2FA enforcement messages look consistent and ready for UI use

The Hindi strings for 2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now accurately convey that the org requires 2FA for all members and should work correctly in the new enforcement/fail card flows. Based on learnings, this matches the intended strict 2FA behavior for all org members.

Also applies to: 1238-1238

messages/ko.json (1)

22-24: Korean 2FA setup/enforcement copy aligns with new fail‑card behavior

2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now are clearly translated and communicate that the organization enforces 2FA for every member, which matches the enforcement rules used by the new UI.

Also applies to: 1238-1238

messages/pt-br.json (1)

22-24: PT‑BR 2FA enforcement messaging is clear and matches product behavior

The new strings for 2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now read naturally in PT‑BR and clearly state that the org requires 2FA from all members, which should work well in the new enforcement card.

Also applies to: 1238-1238

messages/zh-cn.json (1)

22-24: zh‑CN 2FA setup/enforcement keys are present and aligned with enforcement

The Chinese translations for 2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now are in place and correctly convey that the org enforces 2FA for all members, which should support the new fail/enforcement card UX.

Also applies to: 1238-1238

messages/es.json (1)

22-23: 2FA enforcement messaging is accurate and aligned with org‑wide enforcement

2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now clearly state that 2FA is required to access the organization and that the org requires 2FA for all members. This matches the enforcement behavior where no members (including super admins) are exempt once enforcement is enabled. Based on learnings, this is the correct framing for the fail/enforcement card.

Also applies to: 1238-1238

messages/ja.json (1)

22-23: Japanese 2FA gating copy matches org‑wide enforcement semantics

2fa-setup-org-access and 2fa-setup-required clearly say that enabling 2FA is required to access the organization and that 2FA is required for all members; setup-2fa-now is a clear CTA. This is consistent with the enforced behavior that every member, including admins, must have 2FA enabled before accessing org resources. Based on learnings, this is the intended behavior.

Also applies to: 1238-1238

messages/vi.json (1)

22-23: Vietnamese 2FA enforcement text clearly communicates org access requirement

The new strings for 2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now say that two‑factor auth must be enabled to access the organization and that 2FA is required for all members. That matches the actual enforcement behavior (no exceptions, including for admins) and should work well for the 2FA fail card.

Also applies to: 1238-1238

messages/ru.json (1)

22-23: Russian 2FA setup copy correctly reflects enforced access rules

2fa-setup-org-access and 2fa-setup-required explain that 2FA must be enabled to access the organization and that the org requires 2FA for all members; setup-2fa-now is a clear action label. This is consistent with the enforcement rule that no one, including super admins, can access org functions until they enable 2FA.

Also applies to: 1238-1238

messages/fr.json (1)

22-23: 2FA enforcement messaging in French looks correct and aligned with behavior

2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now clearly communicate that 2FA is mandatory for organization access and point users to the correct action; wording and tone are consistent with the rest of the French locale.

Also applies to: 1238-1238

messages/tr.json (1)

22-23: Turkish 2FA enforcement texts are clear and accurate

2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now correctly explain that 2FA is required for organization access and guide the user to configure it, matching the intended enforcement behavior.

Also applies to: 1238-1238

messages/id.json (1)

22-23: Indonesian 2FA enforcement copy is consistent with the intended behavior

2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now clearly state that 2FA is required to access the organization and instruct the user to configure it, matching the 2FA enforcement flow.

Also applies to: 1238-1238

messages/pl.json (2)

22-23: 2FA enforcement messaging is clear and aligned with org‑wide requirement

The new Polish strings for 2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now clearly explain that 2FA is mandatory for all org members and what the user must do, which matches the enforcement behavior.

Also applies to: 1238-1238


322-323: Security, webhook, and password‑policy copy looks consistent where provided

For the keys where you did provide Polish text (e.g. cannot-copy-secure-key, enforce-hashed-api-keys*, expose-metadata*, header/signature descriptions, password‑policy impact and verification messages), wording is technically accurate and consistent with the English semantics and the new enforcement behavior.

Also applies to: 705-707, 736-738, 771-775, 1048-1069

messages/de.json (2)

22-23: German 2FA enforcement messaging correctly describes org‑wide requirement

The new 2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now strings clearly state that 2FA is required for all members to access the org and what action the user must take; this matches the intended enforcement semantics.

Also applies to: 1238-1238


322-323: Webhook, hashed‑API‑key, and password‑policy German copy is technically sound

For the keys where you added German text (e.g. cannot-copy-secure-key, enforce-hashed-api-keys*, expose-metadata*, webhook header/signature descriptions, password‑policy impact and verification messaging), the terminology and phrasing are accurate and consistent with the English behavior and security intent.

Also applies to: 705-707, 736-738, 771-775, 1047-1061, 1242-1246

messages/it.json (2)

22-23: Italian 2FA enforcement copy is clear and matches behavior

The new 2fa-setup-org-access, 2fa-setup-required, and setup-2fa-now strings clearly explain that 2FA is required for org access and prompt the user to configure it; this is consistent with the enforced 2FA behavior.

Also applies to: 1238-1238


322-323: Security, webhook signature, and password‑verification Italian text is coherent

Where Italian translations are provided (e.g. cannot-copy-secure-key, enforce-hashed-api-keys*, expose-metadata*, header/signature descriptions, and the password‑verification required/success messages), the terminology is accurate and consistent with the English intent and should work well with the new enforcement flows.

Also applies to: 705-707, 736-738, 771-775, 1046-1061, 1242-1246

Comment on lines +174 to 182
"allow-dev-builds": "",
"allow-develoment-bui": "Permitir dispositivos de desarrollo",
"allow-device": "",
"allow-device-to-self": "Permitir que los dispositivos se disocien/asocien por sí mismos",
"allow-emulator": "Permitir Emuladores",
"allow-physical-device": "Permitir dispositivos físicos",
"allow-prod-build": "Permitir compilación de producción",
"allow-prod-builds": "",
"already-account": "¿Ya tienes una cuenta?",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, verify the file exists and get basic info
git ls-files | grep -E "messages.*es\.json"

Repository: Cap-go/capgo

Length of output: 72


🏁 Script executed:

# Get the total line count to understand the file size
wc -l messages/es.json

Repository: Cap-go/capgo

Length of output: 77


🏁 Script executed:

# Check a few of the line ranges mentioned to verify empty strings exist
echo "=== Lines 174-182 ==="
sed -n '174,182p' messages/es.json

echo "=== Lines 211-211 ==="
sed -n '211p' messages/es.json

echo "=== Lines 280-281 ==="
sed -n '280,281p' messages/es.json

echo "=== Lines 379-390 ==="
sed -n '379,390p' messages/es.json

Repository: Cap-go/capgo

Length of output: 1582


🏁 Script executed:

# Count total empty string values in the file to understand the scope
grep -c '": ""' messages/es.json

Repository: Cap-go/capgo

Length of output: 58


🏁 Script executed:

# Find the i18n implementation to understand how empty strings are handled
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) | xargs grep -l "i18n\|translation\|fallback" | head -10

Repository: Cap-go/capgo

Length of output: 334


🏁 Script executed:

# Check the main i18n service
cat -n src/services/i18n.ts | head -100

Repository: Cap-go/capgo

Length of output: 951


🏁 Script executed:

# Check the i18n module
cat -n src/modules/i18n.ts | head -100

Repository: Cap-go/capgo

Length of output: 2548


🏁 Script executed:

# Search for how translations are resolved/loaded
rg "fallback|empty|default" src/services/i18n.ts -A 3 -B 3

Repository: Cap-go/capgo

Length of output: 181


🏁 Script executed:

# Search for any fallback configuration in i18n files
rg "fallback" src/modules/i18n.ts src/services/i18n.ts -A 5 -B 5

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check if there's a global i18n config or setup
rg "createI18n\|fallbackLocale" . --type ts --type tsx -A 10 -B 2

Repository: Cap-go/capgo

Length of output: 83


🏁 Script executed:

# Look for where i18n might be configured beyond the module setup
fd "i18n\|locale" --type f -e ts -e tsx -e js | xargs grep -l "fallback" 2>/dev/null

Repository: Cap-go/capgo

Length of output: 40


🏁 Script executed:

# Check the English version to understand the structure
head -n 50 messages/en.json

Repository: Cap-go/capgo

Length of output: 3234


🏁 Script executed:

# Verify if the review's claim about empty strings at all cited lines is accurate
echo "=== Checking all cited line ranges ===" && \
for range in "414-416" "577-578" "698-708" "1011-1024" "1053-1065" "1182-1182"; do
  echo "Lines $range:"; 
  sed -n "${range}p" messages/es.json | grep -c '": ""' || echo "0 empty strings";
done

Repository: Cap-go/capgo

Length of output: 627


🏁 Script executed:

# Use a different approach to check the additional line ranges
echo "=== Lines 414-416 ===" && sed -n '414,416p' messages/es.json
echo -e "\n=== Lines 577-578 ===" && sed -n '577,578p' messages/es.json
echo -e "\n=== Lines 698-708 ===" && sed -n '698,708p' messages/es.json
echo -e "\n=== Lines 1011-1024 ===" && sed -n '1011,1024p' messages/es.json
echo -e "\n=== Lines 1053-1065 ===" && sed -n '1053,1065p' messages/es.json
echo -e "\n=== Line 1182 ===" && sed -n '1182p' messages/es.json

Repository: Cap-go/capgo

Length of output: 3847


🏁 Script executed:

# Check Vue i18n's actual behavior with empty strings by looking at templates/components
rg 'i18n\.\|$t\(' src --type ts --type js -A 2 -B 2 | head -50

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check if there's a fallback locale configured anywhere
rg "fallbackLocale\|fallback.*locale" . --type ts --type js -i

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Search for usage of some of these empty-string keys in components
rg "allow-dev-builds|characters-minimum|enable-password-policy|password-policy-description|saving" src --type ts --type js -B 2 -A 2 | head -60

Repository: Cap-go/capgo

Length of output: 4322


🏁 Script executed:

# Check if these keys are actually used in the UI (templates)
fd -e vue -e html | xargs grep -l "allow-dev-builds\|enable-password-policy\|password-policy" 2>/dev/null | head -10

Repository: Cap-go/capgo

Length of output: 223


🏁 Script executed:

# Verify total count of empty strings again and understand the scale
echo "Total empty string translations:" && grep -c '": ""' messages/es.json
echo "" && echo "Sample of empty string keys:" && grep '": ""' messages/es.json | head -20

Repository: Cap-go/capgo

Length of output: 664


Empty-string translations will render as blank in Spanish UI without fallback configuration

67 keys in this file are set to "", and Vue i18n has no fallback locale configured. Empty strings will render literally as blank text instead of falling back to English. This affects real UI elements: characters-minimum is used in ChangePassword.vue, enable-password-policy and password-policy-description are used in Security.vue, and several others appear in active components.

Consider either:

  • Removing untranslated keys until translations are ready, or
  • Temporarily copying the English strings so the UI remains usable for es, then localizing later.

Also applies to: 211-211, 280-281, 379-390, 414-416, 698-708, 1053-1065, 1182-1182

Comment on lines +1408 to +1412
"your-api-key": "Your API Key",
"your-role-in-org": "Votre rôle dans l'organisation",
"your-settings": "Vos paramètres",
"your-usage": "Votre utilisation :",
"zip-bundle": "Paquet d'application Zip"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Localize "your-api-key" instead of leaving English text in the French bundle

The value "Your API Key" stands out as untranslated in an otherwise French UI and is likely visible in security/API-key flows.

Proposed French translation
-  "your-api-key": "Your API Key",
+  "your-api-key": "Votre clé API",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"your-api-key": "Your API Key",
"your-role-in-org": "Votre rôle dans l'organisation",
"your-settings": "Vos paramètres",
"your-usage": "Votre utilisation :",
"zip-bundle": "Paquet d'application Zip"
"your-api-key": "Votre clé API",
"your-role-in-org": "Votre rôle dans l'organisation",
"your-settings": "Vos paramètres",
"your-usage": "Votre utilisation :",
"zip-bundle": "Paquet d'application Zip"
🤖 Prompt for AI Agents
In messages/fr.json around lines 1408 to 1412 the "your-api-key" entry is left
in English ("Your API Key"); replace the value with the French translation
(e.g., "Votre clé API") while preserving the JSON key, quotes, comma placement
and encoding; update the file so the line reads "your-api-key": "Votre clé API",
matching surrounding formatting.

Comment on lines +1408 to +1412
"your-api-key": "",
"your-role-in-org": "Peran Anda dalam organisasi",
"your-settings": "Pengaturan Anda",
"your-usage": "Penggunaan Anda:",
"zip-bundle": "Paket bundel aplikasi Zip"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fill in "your-api-key" in Indonesian to avoid empty labels

Currently "your-api-key": "" will produce a blank string where this key is used, which is confusing in API/security UIs.

Proposed Indonesian translation
-  "your-api-key": "",
+  "your-api-key": "Kunci API Anda",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"your-api-key": "",
"your-role-in-org": "Peran Anda dalam organisasi",
"your-settings": "Pengaturan Anda",
"your-usage": "Penggunaan Anda:",
"zip-bundle": "Paket bundel aplikasi Zip"
"your-api-key": "Kunci API Anda",
"your-role-in-org": "Peran Anda dalam organisasi",
"your-settings": "Pengaturan Anda",
"your-usage": "Penggunaan Anda:",
"zip-bundle": "Paket bundel aplikasi Zip"
🤖 Prompt for AI Agents
messages/id.json lines 1408-1412: the "your-api-key" value is an empty string
which yields a blank label in UIs; replace the empty value with the Indonesian
translation "Kunci API Anda" so the key reads "your-api-key": "Kunci API Anda",
ensuring the label displays correctly in API/security interfaces.

Comment on lines +134 to 136
"add-webhook": "",
"add-your-first-app-t": "Aggiungi la tua prima app al tuo account e iniziamo a inviare aggiornamenti!",
"add-your-first-bundle": "Aggiungi il tuo primo pacchetto al tuo account e procediamo con gli aggiornamenti!",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find the messages/it.json file and examine it
find . -name "it.json" -path "*/messages/*" 2>/dev/null | head -20

Repository: Cap-go/capgo

Length of output: 74


🏁 Script executed:

# Check the size and structure of the Italian translation file
if [ -f "messages/it.json" ]; then
  echo "=== File info ==="
  wc -l messages/it.json
  echo ""
  echo "=== First 20 lines ==="
  head -20 messages/it.json
fi

Repository: Cap-go/capgo

Length of output: 1937


🏁 Script executed:

# Run the provided script to find empty-string translations
rg -n '":\s*""' messages/*.json 2>/dev/null | head -50

Repository: Cap-go/capgo

Length of output: 2672


🏁 Script executed:

# Check for empty strings specifically in it.json
rg -n '":\s*""' messages/it.json | head -100

Repository: Cap-go/capgo

Length of output: 2503


🏁 Script executed:

# Also check the specific line ranges mentioned in the review
echo "=== Lines 134-136 ===" && sed -n '134,136p' messages/it.json
echo ""
echo "=== Lines 456-461 ===" && sed -n '456,461p' messages/it.json
echo ""
echo "=== Lines 627-637 ===" && sed -n '627,637p' messages/it.json
echo ""
echo "=== Lines 941-943 ===" && sed -n '941,943p' messages/it.json

Repository: Cap-go/capgo

Length of output: 1203


🏁 Script executed:

# Let's check the i18n implementation to understand fallback behavior
find . -name "*.ts" -o -name "*.js" -o -name "*.tsx" -o -name "*.jsx" | xargs grep -l "i18n\|locale\|fallback" 2>/dev/null | head -20

Repository: Cap-go/capgo

Length of output: 728


🏁 Script executed:

# Check the main i18n implementation
cat -n src/services/i18n.ts | head -100

Repository: Cap-go/capgo

Length of output: 951


🏁 Script executed:

# Also check the i18n module
cat -n src/modules/i18n.ts | head -100

Repository: Cap-go/capgo

Length of output: 2548


🏁 Script executed:

# Look for fallback logic and how empty strings are handled
rg -A 5 -B 5 "fallback\|empty\|''\|\"\"" src/services/i18n.ts src/modules/i18n.ts 2>/dev/null | head -150

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check for vue-i18n fallback configuration and how it handles empty strings
rg -n "fallback\|missingWarn\|fallbackWarn\|fallbackLocale" . --type ts --type js --type json 2>/dev/null | head -30

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Look at the actual usage of i18n in components to understand how translations are called
rg -n "\$t\(|t\(|i18n\.t" src/ --type vue --type ts -A 1 -B 1 2>/dev/null | head -50

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check if there's any documentation or README about localization strategy
find . -name "*.md" -o -name "README*" | xargs grep -l "translation\|locali\|i18n" 2>/dev/null

Repository: Cap-go/capgo

Length of output: 88


🏁 Script executed:

# Check the messages README for localization documentation
cat messages/README.md

Repository: Cap-go/capgo

Length of output: 1037


🏁 Script executed:

# Look at vue-i18n configuration more thoroughly
rg -n "createI18n\|setLocaleMessage" . --type ts --type js -A 10 2>/dev/null | head -100

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check if the i18n config has any fallback settings
cat -n src/modules/i18n.ts

Repository: Cap-go/capgo

Length of output: 2548


🏁 Script executed:

# Let's check how vue-i18n handles empty strings by default - search for interpolation or placeholder handling
rg -n "missing\|fall" src/ --type ts --type vue 2>/dev/null | grep -i "i18n\|locale\|translate" | head -20

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Let's verify vue-i18n default behavior by checking package.json
cat package.json | grep -A 2 -B 2 "vue-i18n"

Repository: Cap-go/capgo

Length of output: 396


🏁 Script executed:

# Check if there's any example of how empty strings should be handled
rg -n "isEmpty|empty.*string|\"\"\s*translation" . --type ts --type vue --type json 2>/dev/null | head -20

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Look for any comments or documentation about empty translation values
rg -B 3 -A 3 "add-webhook\|create-webhook\|password-policy" messages/en.json | head -50

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check the English translation file for these keys to understand the expected behavior
grep -n "add-webhook\|create-webhook\|password-policy\|no-webhooks" messages/en.json | head -30

Repository: Cap-go/capgo

Length of output: 1926


🏁 Script executed:

# Let's see how vue-i18n 11.1.12 handles empty strings - check if there's a missing message handler
rg -n "missingWarn\|fallback" . --type ts --type js --type json 2>/dev/null | head -20

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check for any i18n configuration in vite or nuxt config
find . -name "vite.config.*" -o -name "nuxt.config.*" -o -name ".vue-i18n.*" 2>/dev/null | xargs cat 2>/dev/null | head -100

Repository: Cap-go/capgo

Length of output: 3025


🏁 Script executed:

# Check if there's a fallbackLocale configuration anywhere
rg -n "fallbackLocale\|fallback" . --type ts --type js --type json 2>/dev/null | head -20

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Let's check the entire vite.config.ts to see if there's any i18n configuration we missed
tail -200 vite.config.ts | head -100

Repository: Cap-go/capgo

Length of output: 129


🏁 Script executed:

# Search for how vue-i18n is configured in the unplugin setup
rg -B 5 -A 5 "unplugin-vue-i18n" vite.config.ts

Repository: Cap-go/capgo

Length of output: 110


🏁 Script executed:

# Count total empty strings in it.json vs other locales
echo "=== Empty strings count by locale ===" && \
for file in messages/*.json; do 
  locale=$(basename "$file" .json)
  count=$(rg -c '":\s*""' "$file" 2>/dev/null || echo 0)
  echo "$locale: $count"
done | sort -t: -k2 -rn

Repository: Cap-go/capgo

Length of output: 203


🏁 Script executed:

# Find the vite config file with correct path
find . -name "vite.config.*" -type f

Repository: Cap-go/capgo

Length of output: 73


🏁 Script executed:

# Check if there's a nuxt.config or any other config file that might set i18n fallback
find . -name "*.config.*" -type f | grep -v node_modules | grep -v dist | head -20

Repository: Cap-go/capgo

Length of output: 263


🏁 Script executed:

# Look for Vue i18n default fallback behavior configuration in the codebase
rg -n "fallbackLocale\|fallback" src/ --type ts --type js 2>/dev/null

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Let's verify how the i18n is actually used in components - check a sample component
find src/components -name "*.vue" -type f | head -5 | xargs cat 2>/dev/null | head -100

Repository: Cap-go/capgo

Length of output: 3173


🏁 Script executed:

# Let's verify the exact vue-i18n configuration - check if there's a fallbackLocale in vite.config.mts
grep -n "fallbackLocale\|VueI18n" vite.config.mts -A 10 -B 2

Repository: Cap-go/capgo

Length of output: 1094


🏁 Script executed:

# Check vue-i18n documentation by looking at how it handles empty strings in the actual codebase
rg -n "t(" src/ --type vue -B 2 -A 2 2>/dev/null | grep -A 4 "t(" | head -40

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Verify the actual behavior - look for any configuration that might handle missing translations
rg -n "missing\|missingWarn\|warnHtmlMessage" . --type ts --type js --type mts 2>/dev/null

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Let's check the default behavior of vue-i18n by looking at vue-i18n documentation online
# First, let's verify the exact vue-i18n behavior by checking if there's any config that enables fallback
cat vite.config.mts | grep -A 30 "createI18n"

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check if there's any fallback configuration in the actual i18n module setup
cat src/modules/i18n.ts | grep -A 5 -B 5 "createI18n"

Repository: Cap-go/capgo

Length of output: 417


🏁 Script executed:

# Let's count how many of these empty-string keys are actually used in Vue components
rg -n "add-webhook|create-webhook|delete-webhook|password-policy|no-webhooks" src/ --type vue --type ts 2>/dev/null | head -30

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Let's verify the default vue-i18n behavior by checking if there's any documentation in comments
rg -n "fallback\|locale\|missing" src/modules/i18n.ts src/services/i18n.ts -B 3 -A 3 2>/dev/null

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check if empty strings are actually being used in the UI by looking for template usage
rg -n "no-webhooks|password-policy|add-webhook" src/ --type vue 2>/dev/null | head -20

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Verify the complete i18n initialization to see if fallbackLocale is set elsewhere
rg -rn "fallbackLocale" . --type ts --type mts --type js 2>/dev/null

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Let's check vue-i18n default behavior by looking at the actual vue-i18n version in package.json
cat package.json | grep "vue-i18n"

Repository: Cap-go/capgo

Length of output: 128


🏁 Script executed:

# Since fallbackLocale is not explicitly set, let's verify that these keys are actually used in components
# by searching for their usage patterns
rg -n "t\(['\"]add-webhook" src/ --type vue --type ts 2>/dev/null

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check if there's any runtime fallback mechanism elsewhere
rg -n "fallback\|defaultMessage\|placeholder" src/ --type ts --type vue 2>/dev/null | head -20

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Let's verify the actual default behavior of vue-i18n 11.1.12 by checking if fallbackLocale defaults to 'en'
# Check the i18n module once more for any implicit fallback configuration
cat src/modules/i18n.ts

Repository: Cap-go/capgo

Length of output: 2051


🏁 Script executed:

# Check if there are any examples of empty string handling in any test files
find . -name "*.test.*" -o -name "*.spec.*" | xargs grep -l "i18n\|translation" 2>/dev/null | head -5

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Verify that these keys are actually being used in components - spot check for at least one
rg -n "add-webhook\|create-webhook\|delete-webhook\|no-webhooks\|password-policy" src/pages src/components --type vue 2>/dev/null | head -20

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Let's verify vue-i18n's default behavior by checking its documentation or any configuration we might have missed
# Check if there's any global config that sets fallbackLocale
rg -rn "fallback" . --type ts --type mts --type js --type json 2>/dev/null | grep -v node_modules | grep -v ".next" | head -20

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Let's also check if there's any missing message handler configured
rg -rn "missingWarn\|missing.*handler\|onWarn" . --type ts --type mts --type js 2>/dev/null | head -20

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Finally, let's verify that there are actual uses of these empty-string keys in the UI
find src/pages src/components -name "*.vue" -type f 2>/dev/null | xargs grep -l "webhook\|password-policy" | head -5

Repository: Cap-go/capgo

Length of output: 261


🏁 Script executed:

# Let's check the Webhooks.vue component to see which keys it's using
grep -n "t(" src/pages/settings/organization/Webhooks.vue | head -30

Repository: Cap-go/capgo

Length of output: 1719


🏁 Script executed:

# Check the PasswordPolicyWarningBanner.vue component
grep -n "t(" src/components/PasswordPolicyWarningBanner.vue | head -20

Repository: Cap-go/capgo

Length of output: 203


🏁 Script executed:

# Check WebhookForm.vue
grep -n "t(" src/components/WebhookForm.vue | head -20

Repository: Cap-go/capgo

Length of output: 1024


Empty Italian translations on webhook and password-policy features will display blank UI text

In it.json, 75 keys have empty string values "" including critical user-facing labels in webhook management and password policy enforcement. These keys are actively used in components like Webhooks.vue, WebhookForm.vue, and PasswordPolicyWarningBanner.vue.

Examples confirmed in use:

  • Webhooks: add-webhook, create-webhook, delete-webhook, delete-webhook-confirm, edit-webhook, no-webhooks, no-webhooks-description
  • Password policy: password-policy, password-policy-required, password-policy-required-message, password-policy-description, password-update-org-access, password-action-required, password-change-then-verify
  • Controls: allow-dev-builds, allow-device, allow-prod-builds, assigned, characters, disabled, removed, retry, saving

The i18n configuration does not specify a fallbackLocale, so empty strings render as blank text rather than falling back to English. Italian users will encounter missing labels and descriptive text in these features.

Recommended actions:

  • Provide Italian translations for these keys instead of empty strings, or
  • Remove the keys from it.json entirely to enable fallback to English

Also localize your-api-key (e.g., "La tua chiave API") for consistency.

Full list of affected lines

134-136, 174, 176, 181, 211, 280, 307, 379, 382-383, 388-389, 414-415, 428, 456, 460, 627-628, 633-637, 685, 694, 698-699, 707-708, 718, 731, 742, 767, 769-770, 881, 907, 927, 941-942, 993-995, 1042-1043, 1049-1051, 1053, 1056-1057, 1062-1065, 1069, 1138, 1147, 1151, 1155-1157, 1165, 1169, 1182, 1206, 1235-1236, 1240, 1263, 1273, 1319, 1346, 1348, 1364

🤖 Prompt for AI Agents
In messages/it.json around lines 134 to 136 (and other listed lines), many keys
(including add-webhook and several password-policy and control labels) are set
to empty strings causing blank UI text; replace each empty value with the proper
Italian translation (or remove the key entirely to allow fallback to English) —
specifically fill in translations for webhook keys (add-webhook, create-webhook,
delete-webhook, delete-webhook-confirm, edit-webhook, no-webhooks,
no-webhooks-description), password-policy keys (password-policy,
password-policy-required, password-policy-required-message,
password-policy-description, password-update-org-access,
password-action-required, password-change-then-verify), control labels
(allow-dev-builds, allow-device, allow-prod-builds, assigned, characters,
disabled, removed, retry, saving) and also localize your-api-key (e.g., "La tua
chiave API"); ensure no empty string values remain for keys referenced by the
app or remove them so the i18n fallback to English works.

Comment on lines +174 to 182
"allow-dev-builds": "",
"allow-develoment-bui": "開発ビルドを許可する",
"allow-device": "",
"allow-device-to-self": "デバイスに自己分離/関連付けを許可する",
"allow-emulator": "エミュレータを許可する",
"allow-physical-device": "実機を許可",
"allow-prod-build": "本番ビルドを許可",
"allow-prod-builds": "",
"already-account": "すでにアカウントをお持ちですか?",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

git ls-files messages/ | head -20

Repository: Cap-go/capgo

Length of output: 335


🏁 Script executed:

wc -l messages/ja.json

Repository: Cap-go/capgo

Length of output: 77


🏁 Script executed:

sed -n '174,182p' messages/ja.json

Repository: Cap-go/capgo

Length of output: 365


🏁 Script executed:

sed -n '210,212p' messages/ja.json
sed -n '279,282p' messages/ja.json
sed -n '379,391p' messages/ja.json

Repository: Cap-go/capgo

Length of output: 960


🏁 Script executed:

sed -n '414,416p' messages/ja.json
sed -n '577,578p' messages/ja.json
sed -n '698,708p' messages/ja.json

Repository: Cap-go/capgo

Length of output: 648


🏁 Script executed:

sed -n '1011,1024p' messages/ja.json
sed -n '1053,1065p' messages/ja.json
sed -n '1182p' messages/ja.json

Repository: Cap-go/capgo

Length of output: 1681


🏁 Script executed:

# Look at en.json to see what should be translated
sed -n '174,182p' messages/en.json

Repository: Cap-go/capgo

Length of output: 309


🏁 Script executed:

# Find how i18n is implemented
find . -type f \( -name "*.ts" -o -name "*.js" -o -name "*.vue" \) | xargs grep -l "i18n\|i18next\|vue-i18n" | head -10

Repository: Cap-go/capgo

Length of output: 362


🏁 Script executed:

# Check en.json for comparison at the line ranges mentioned
sed -n '414,416p' messages/en.json
sed -n '577,578p' messages/en.json
sed -n '698,708p' messages/en.json

Repository: Cap-go/capgo

Length of output: 710


🏁 Script executed:

# Find i18n configuration
find . -type f \( -name "*.ts" -o -name "*.js" \) | xargs grep -l "createI18n\|i18n:" | head -5

Repository: Cap-go/capgo

Length of output: 77


🏁 Script executed:

# Check package.json for i18n library
grep -i "i18n\|vue-i18n" package.json

Repository: Cap-go/capgo

Length of output: 159


🏁 Script executed:

# Check the main i18n configuration
find . -type f -name "*i18n*" | grep -v node_modules | head -10

Repository: Cap-go/capgo

Length of output: 100


🏁 Script executed:

# Examine the i18n configuration
cat -n ./src/modules/i18n.ts

Repository: Cap-go/capgo

Length of output: 2548


🏁 Script executed:

cat -n ./src/services/i18n.ts

Repository: Cap-go/capgo

Length of output: 951


🏁 Script executed:

# Check remaining line ranges
sed -n '1011,1024p' messages/ja.json
sed -n '1053,1065p' messages/ja.json
sed -n '1182p' messages/ja.json

Repository: Cap-go/capgo

Length of output: 1681


🏁 Script executed:

# Search for any fallbackLocale configuration
grep -r "fallbackLocale" . --include="*.ts" --include="*.js" | grep -v node_modules

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Count empty strings in ja.json
grep -c '": ""' messages/ja.json

Repository: Cap-go/capgo

Length of output: 58


🏁 Script executed:

# Compare with en.json to see how many keys have translations in English
grep -c '": ""' messages/en.json

Repository: Cap-go/capgo

Length of output: 57


🏁 Script executed:

# See what's at lines 1011-1024, 1053-1065, 1182 again
sed -n '1011,1024p' messages/ja.json
sed -n '1053,1065p' messages/ja.json
sed -n '1182p' messages/ja.json

Repository: Cap-go/capgo

Length of output: 1681


Remove or populate 75 untranslated empty-string keys in ja.json to prevent blank UI text

ja.json contains 75 empty-string values (e.g., "allow-dev-builds": ""), while the English source (en.json) has complete translations for all keys. Since no fallbackLocale is configured in the i18n setup, empty strings are treated as valid translations. Japanese users will see blank labels and messages in the UI instead of falling back to English.

Either remove untranslated keys from ja.json until translations are ready, or temporarily copy English values as placeholders for later localization. This affects password-policy screens, org-notifications, webhook delivery/help text, and other critical UI elements.

Affected line ranges: 174-182, 211, 280-281, 379-390, 414-416, 577-578, 698-708, 1011-1024, 1053-1065, 1182.

🤖 Prompt for AI Agents
In messages/ja.json around lines 174-182 (and affected ranges 211, 280-281,
379-390, 414-416, 577-578, 698-708, 1011-1024, 1053-1065, 1182) there are 75
keys with empty-string values causing blank UI text; either remove those
untranslated keys from ja.json so the app will fall back to the source locale,
or populate them with the English source strings from en.json as temporary
placeholders. Update only the empty values (copy the corresponding en.json
value) or delete the key entirely, then run the i18n validation/lint script (or
a quick grep for '": ""') to confirm no remaining empty translations and commit
the cleaned ja.json.

Comment on lines +134 to 136
"add-webhook": "",
"add-your-first-app-t": "Dodaj swoją pierwszą aplikację do swojego konta i zacznijmy wprowadzać aktualizacje!",
"add-your-first-bundle": "Dodaj swoją pierwszą paczkę do swojego konta i zacznijmy wprowadzać aktualizacje!",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, check if the file exists and its total line count
wc -l messages/pl.json

Repository: Cap-go/capgo

Length of output: 77


🏁 Script executed:

#!/bin/bash
# Check the specific lines mentioned in the comment
# Lines 134-136
echo "=== Lines 134-136 ===" 
sed -n '134,136p' messages/pl.json

# Lines 280-281
echo "=== Lines 280-281 ===" 
sed -n '280,281p' messages/pl.json

# Lines 627-637
echo "=== Lines 627-637 ===" 
sed -n '627,637p' messages/pl.json

# Lines 941-943
echo "=== Lines 941-943 ===" 
sed -n '941,943p' messages/pl.json

# Lines 1042-1047
echo "=== Lines 1042-1047 ===" 
sed -n '1042,1047p' messages/pl.json

# Lines 1182-1183
echo "=== Lines 1182-1183 ===" 
sed -n '1182,1183p' messages/pl.json

# Lines 1198-1202
echo "=== Lines 1198-1202 ===" 
sed -n '1198,1202p' messages/pl.json

# Lines 1263-1264
echo "=== Lines 1263-1264 ===" 
sed -n '1263,1264p' messages/pl.json

# Lines 1408-1412
echo "=== Lines 1408-1412 ===" 
sed -n '1408,1412p' messages/pl.json

Repository: Cap-go/capgo

Length of output: 2130


🏁 Script executed:

#!/bin/bash
# Run the suggested helper script to see all empty translations
rg -n '":\s*""' messages/pl.json | head -50

Repository: Cap-go/capgo

Length of output: 1672


🏁 Script executed:

#!/bin/bash
# Check for specific keys mentioned but not verified in line ranges
echo "=== Checking for password-policy related keys ==="
rg -n 'password-policy|password-update-org-access|update-password-now' messages/pl.json

echo "=== Checking for allow-* keys ==="
rg -n 'allow-dev-builds|allow-device|allow-prod-builds' messages/pl.json

echo "=== Checking for generic control keys ==="
rg -n '"assigned"|"characters"|"characters-minimum"|"completed-at"|"removed"|"retry"' messages/pl.json

Repository: Cap-go/capgo

Length of output: 1570


Empty Polish translations for user‑visible keys will likely render blank UI

Multiple keys in messages/pl.json have empty string ("") values that are used in shipped UI. These empty translations will render as blank elements instead of falling back to the default locale if your i18n runtime treats empty strings as defined translations—which is the common behavior.

Affected areas include:

  • Webhook UI: add-webhook, create-first-webhook, create-webhook, delete-webhook, delete-webhook-confirm, delivery-id, delivery-log, delivery-retry-failed, delivery-retry-queued, no-webhooks, no-webhooks-description, subscribed-events
  • Password policy / security: password-action-required, password-change-then-verify, password-policy, password-policy-description, password-policy-required, password-policy-updated, password-update-org-access, enable-password-policy, enforce-password-policy, enforce-password-policy-description, and requirement flags (minimum-length, one-number, one-uppercase, one-special-character)
  • Generic controls: allow-dev-builds, allow-device, allow-prod-builds, assigned, characters, characters-minimum, completed-at, removed, retry, saving

Either provide non-empty fallbacks (Polish or English) for all user-facing keys, or remove these keys from pl.json until translations are ready so the runtime can fall back to the base language.

🤖 Prompt for AI Agents
In messages/pl.json around lines 134 to 136, several user‑visible keys
(including "add-webhook" and many others listed in the review) are set to empty
strings which will render blank in the UI; replace each empty value with a
proper Polish translation or a safe fallback (e.g., the English base string) for
every affected key, or remove those keys from pl.json entirely until
translations are ready so the i18n runtime will fall back to the default locale.
Ensure you update all listed keys (webhook UI keys, password policy/security
keys, and generic control keys) consistently and run a quick i18n spot-check to
confirm no empty strings remain.

Comment on lines +134 to 135
"add-webhook": "",
"add-your-first-app-t": "Добавьте свое первое приложение в свой аккаунт и давайте отправлять обновления!",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cd /tmp && find . -name "ru.json" -type f 2>/dev/null | head -5

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

find . -name "ru.json" -type f 2>/dev/null

Repository: Cap-go/capgo

Length of output: 74


🏁 Script executed:

git ls-files | grep -E "ru\.json|messages"

Repository: Cap-go/capgo

Length of output: 335


🏁 Script executed:

# First get file stats
wc -l messages/ru.json

# Check lines around 134-135
sed -n '130,140p' messages/ru.json

# Check a few more sections mentioned
sed -n '170,185p' messages/ru.json
sed -n '275,285p' messages/ru.json

Repository: Cap-go/capgo

Length of output: 1943


🏁 Script executed:

# Find i18n or translation related files
find . -type f \( -name "*i18n*" -o -name "*translat*" -o -name "*locale*" \) ! -path "*/node_modules/*" 2>/dev/null | head -20

Repository: Cap-go/capgo

Length of output: 153


🏁 Script executed:

# Look for how translations are loaded/used
rg -l "ru\.json|i18n|translation" --type json --type js --type ts | head -20

Repository: Cap-go/capgo

Length of output: 588


🏁 Script executed:

# Search for how empty strings are handled in i18n
rg "empty.*string|fallback.*english|gettext|i18n" --type js --type ts -A 3 | head -50

Repository: Cap-go/capgo

Length of output: 3722


🏁 Script executed:

# List root directory
ls -la

# Check for package.json to understand the i18n library
cat package.json | grep -A 5 -B 5 "i18n\|translat"

Repository: Cap-go/capgo

Length of output: 5518


🏁 Script executed:

# Also search for common i18n libraries usage
rg "i18next|react-intl|format\.js|gettext" package.json

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Get file size and structure
stat messages/ru.json

# Check if there are empty string values
rg '^[[:space:]]*"[^"]+"\s*:\s*""\s*,$' messages/ru.json | head -20

# Also check for the specific keys mentioned
rg '"(add-webhook|delete-webhook|allow-dev-builds|allow-device)"' messages/ru.json

Repository: Cap-go/capgo

Length of output: 748


🏁 Script executed:

# Search for imports and usage of messages files
rg "messages.*ru\|ru\.json" --type js --type ts --type jsx --type tsx -B 2 -A 2 | head -30

Repository: Cap-go/capgo

Length of output: 83


🏁 Script executed:

# Search for how fallback is handled
rg "defaultMessage|fallback.*message|missing.*translation|empty.*value" --type js --type ts -A 3 | head -40

Repository: Cap-go/capgo

Length of output: 677


🏁 Script executed:

# Look for English translation file
find . -name "*en*" -path "*/messages/*" | head -10

# Get file listing of messages directory
ls -la messages/

Repository: Cap-go/capgo

Length of output: 1162


🏁 Script executed:

# Compare structure of English and Russian files
head -20 messages/en.json
wc -l messages/en.json messages/ru.json

Repository: Cap-go/capgo

Length of output: 1737


🏁 Script executed:

# Look for i18n setup/config
find . -type f \( -name "*.js" -o -name "*.ts" -o -name "*.tsx" -o -name "*.jsx" \) ! -path "*/node_modules/*" -exec grep -l "i18n\|messages\|locale\|translation" {} \; | head -15

Repository: Cap-go/capgo

Length of output: 1011


🏁 Script executed:

# Search for how messages are accessed
rg "messages\[|messages\.|formatMessage|translate" --type js --type ts --max-count 10 -B 2 -A 2

Repository: Cap-go/capgo

Length of output: 21420


🏁 Script executed:

# Check for webpack, vite, or other config that might process JSON files
ls -la | grep -E "webpack|vite|build|config"

# Look in src directory structure
find . -maxdepth 3 -type d ! -path "*/node_modules/*" | head -20

Repository: Cap-go/capgo

Length of output: 1142


🏁 Script executed:

# Read the first 200 lines to understand structure
head -200 messages/ru.json | tail -70

Repository: Cap-go/capgo

Length of output: 4222


🏁 Script executed:

# Count empty string values in ru.json
rg ':\s*""\s*[,}]' messages/ru.json | wc -l

# Show actual empty string keys
rg ':\s*""\s*[,}]' messages/ru.json | head -30

Repository: Cap-go/capgo

Length of output: 887


🏁 Script executed:

# Compare specific keys between English and Russian
python3 << 'EOF'
import json

with open('messages/en.json', 'r') as f:
    en = json.load(f)

with open('messages/ru.json', 'r') as f:
    ru = json.load(f)

# Find keys in ru that are empty
empty_keys = [k for k, v in ru.items() if v == ""]
print(f"Total empty string keys in ru.json: {len(empty_keys)}")
print("\nEmpty keys (first 20):")
for key in empty_keys[:20]:
    en_val = en.get(key, "[KEY_NOT_IN_EN]")
    print(f"  {key}: EN='{en_val[:50]}...' RU=''")

# Show the specific keys mentioned in the review
mentioned_keys = ["add-webhook", "delete-webhook", "delete-webhook-confirm", "create-first-webhook", 
                  "create-webhook", "allow-dev-builds", "allow-device", "allow-prod-builds", "disabled"]
print("\n\nKeys mentioned in review:")
for key in mentioned_keys:
    if key in ru:
        print(f"  {key}: empty={ru[key] == ''}, value='{ru[key]}'")
    else:
        print(f"  {key}: NOT FOUND IN RU.JSON")
EOF

Repository: Cap-go/capgo

Length of output: 142


🏁 Script executed:

# Search for the i18n implementation/loader
find . -type f \( -name "*.js" -o -name "*.ts" \) ! -path "*/node_modules/*" | xargs grep -l "messages" | grep -v test | head -10

Repository: Cap-go/capgo

Length of output: 387


🏁 Script executed:

# Look at the structure of src or app directory
find . -maxdepth 2 -type f -name "*.ts" -o -name "*.tsx" -o -name "*.js" | grep -v node_modules | head -15

Repository: Cap-go/capgo

Length of output: 423


🏁 Script executed:

# Check package.json more carefully
cat package.json | head -50

Repository: Cap-go/capgo

Length of output: 3491


🏁 Script executed:

# Search for actual usage of the message keys
rg "add-webhook|delete-webhook" --type js --type ts --type jsx --type tsx | head -20

Repository: Cap-go/capgo

Length of output: 83


🏁 Script executed:

# Show the exact lines mentioned in the review
echo "=== Lines 134-135 ==="
sed -n '134,135p' messages/ru.json

echo -e "\n=== Lines 174-182 ==="
sed -n '174,182p' messages/ru.json

echo -e "\n=== Line 211 ==="
sed -n '211p' messages/ru.json

echo -e "\n=== Lines 280-281 ==="
sed -n '280,281p' messages/ru.json

echo -e "\n=== Lines 379-390 ==="
sed -n '379,390p' messages/ru.json

Repository: Cap-go/capgo

Length of output: 1669


🏁 Script executed:

# Check the i18n module setup
cat src/modules/i18n.ts

Repository: Cap-go/capgo

Length of output: 2051


🏁 Script executed:

# Check the i18n service
cat src/services/i18n.ts

Repository: Cap-go/capgo

Length of output: 741


🏁 Script executed:

# Look for vue-i18n configuration
grep -r "fallback\|missingHandler" --include="*.ts" --include="*.js" | grep -v node_modules

Repository: Cap-go/capgo

Length of output: 1370


🏁 Script executed:

# Check how i18n is initialized in the app
find src -type f \( -name "main.ts" -o -name "app.ts" -o -name "index.ts" \) | head -5 | xargs cat 2>/dev/null | head -100

Repository: Cap-go/capgo

Length of output: 4795


🏁 Script executed:

cat project.inlang/settings.json

Repository: Cap-go/capgo

Length of output: 900


🏁 Script executed:

# Look at how the t() function is used and if there's any fallback logic
grep -n "const.*t.*=" src/modules/i18n.ts | head -20

# Check how locales are loaded
cat src/modules/i18n.ts | head -80

Repository: Cap-go/capgo

Length of output: 2144


🏁 Script executed:

# Search for any custom missing handler or fallback logic
rg "missing.*handler|fallback|empty.*string" src/ --type ts -B 2 -A 2

Repository: Cap-go/capgo

Length of output: 697


🏁 Script executed:

cat project.inlang/settings.json

Repository: Cap-go/capgo

Length of output: 900


🏁 Script executed:

# Check vite config for i18n setup
cat vite.config.mts | grep -A 20 "i18n"

Repository: Cap-go/capgo

Length of output: 1607


🏁 Script executed:

# Search for createI18n call
rg "createI18n" src/ --type ts -B 5 -A 10

Repository: Cap-go/capgo

Length of output: 1052


🏁 Script executed:

wc -l src/modules/i18n.ts src/services/i18n.ts
head -100 src/modules/i18n.ts

Repository: Cap-go/capgo

Length of output: 2113


🏁 Script executed:

tail -50 src/modules/i18n.ts

Repository: Cap-go/capgo

Length of output: 1351


🏁 Script executed:

# Look for tests related to i18n
find . -path "*/node_modules" -prune -o -name "*i18n*.test.ts" -type f -print

# Check messages README
cat messages/README.md

Repository: Cap-go/capgo

Length of output: 1037


🏁 Script executed:

# Search for how the application currently handles missing/empty translations
rg "useI18n\|i18n\.t" src/pages/ --type vue -A 2 | head -30

Repository: Cap-go/capgo

Length of output: 83


🏁 Script executed:

# Sample a component to see how translations are accessed
head -80 src/pages/settings/organization/Webhooks.vue | tail -40

Repository: Cap-go/capgo

Length of output: 1121


🏁 Script executed:

# Look for i18n initialization
grep -r "new VueI18n\|createI18n" src/ --include="*.ts" -B 5 -A 15

Repository: Cap-go/capgo

Length of output: 1292


🏁 Script executed:

# Check the services/i18n.ts
cat src/services/i18n.ts

Repository: Cap-go/capgo

Length of output: 741


🏁 Script executed:

cat src/modules/i18n.ts

Repository: Cap-go/capgo

Length of output: 2051


🏁 Script executed:

find src -name "*.ts" -o -name "*.vue" | xargs grep -l "i18n\|I18n" | head -10

Repository: Cap-go/capgo

Length of output: 362


🏁 Script executed:

# Get context around i18n usage in main entry
grep -r "createI18n\|i18n\.global\|setLocaleMessage" src/ --include="*.ts" --include="*.js" -B 3 -A 8 | head -50

Repository: Cap-go/capgo

Length of output: 2563


🏁 Script executed:

cat vite.config.mts | head -150

Repository: Cap-go/capgo

Length of output: 4299


🏁 Script executed:

# Find the main entry point
find src -maxdepth 2 -name "*.ts" -o -name "*.js" | xargs grep -l "createApp\|new Vue" | head -5

Repository: Cap-go/capgo

Length of output: 89


🏁 Script executed:

# Look for app initialization
ls -la src/ | head -20
cat index.html | head -30

Repository: Cap-go/capgo

Length of output: 2456


🏁 Script executed:

# Find where the app is created
rg "createApp" src/ --type ts -B 2 -A 10 | head -40

Repository: Cap-go/capgo

Length of output: 2301


🏁 Script executed:

# Get a summary of empty strings by category from the review
python3 << 'EOF'
import json

with open('messages/en.json', 'r') as f:
    en = json.load(f)

with open('messages/ru.json', 'r') as f:
    ru = json.load(f)

# Find keys in ru that are empty
empty_keys = [k for k, v in ru.items() if v == ""]
webhook_keys = [k for k in empty_keys if 'webhook' in k]
toggle_keys = [k for k in empty_keys if k in ['allow-dev-builds', 'allow-device', 'allow-prod-builds', 'disabled']]
password_keys = [k for k in empty_keys if 'password' in k or 'characters' in k or 'minimum' in k or 'users-' in k]
delivery_keys = [k for k in empty_keys if 'delivery' in k]

print(f"Total empty string keys: {len(empty_keys)}\n")
print(f"Webhook-related: {len(webhook_keys)} - {webhook_keys}")
print(f"\nToggle/setting helpers: {len(toggle_keys)} - {toggle_keys}")
print(f"\nPassword/requirements: {len(password_keys)} - {password_keys}")
print(f"\nDelivery-related: {len(delivery_keys)} - {delivery_keys}")

# Show what the English versions say
print("\n\nSample translations missing:")
for key in webhook_keys[:3]:
    print(f"  {key}: '{en.get(key, '[NOT FOUND]')}'")
EOF

Repository: Cap-go/capgo

Length of output: 1254


🏁 Script executed:

# Check if there's a fallback language configuration
rg "fallbackLocale|fallback" src/modules/ src/services/ --type ts -B 2 -A 2

Repository: Cap-go/capgo

Length of output: 469


🏁 Script executed:

# Check localesMap to see how locales are loaded
grep -A 20 "localesMap" src/modules/i18n.ts

Repository: Cap-go/capgo

Length of output: 1124


🏁 Script executed:

cat -n src/modules/i18n.ts | head -60

Repository: Cap-go/capgo

Length of output: 2185


🏁 Script executed:

cat -n src/services/i18n.ts

Repository: Cap-go/capgo

Length of output: 951


Multiple new keys use empty strings, which will render as blank text instead of showing English fallback

In ru.json, 76 keys are defined with empty string values, including:

  • Webhook UI: add-webhook, delete-webhook, delete-webhook-confirm, create-first-webhook, create-webhook, and delivery-related labels
  • Toggle/setting helpers: allow-dev-builds, allow-device, allow-prod-builds, disabled
  • Password policy & requirements: characters, characters-minimum, enable-password-policy, enforce-password-policy-description, and 19 others
  • Channel and org settings

Vue I18n is configured without a fallback locale, so empty strings render as blank text rather than falling back to English. This affects usability on Russian locale, especially for security features (2FA enforcement, password policies) and critical workflows (webhooks, delivery logs).

Either remove untranslated keys from ru.json until they're ready for translation, or fill them with English text as placeholders and track them for translation.

🤖 Prompt for AI Agents
messages/ru.json around lines 134-135: multiple keys (including "add-webhook" at
134) are set to empty strings which causes blank UI text because Vue I18n has no
fallback; remove these untranslated keys from ru.json or populate them with
English placeholder values (e.g., duplicate the en.json value) for all empty
entries, and track them for translation so Russian locale renders readable text
until proper translations are provided.

Comment on lines +1408 to +1412
"your-api-key": "",
"your-role-in-org": "Organizasyondaki rolünüz",
"your-settings": "Ayarlarınız",
"your-usage": "Kullanımınız:",
"zip-bundle": "Zip uygulama paketi"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Provide a Turkish value for "your-api-key" instead of leaving it empty

"your-api-key": "" will likely show as an empty label wherever this key is used (e.g., API key dialogs), which is confusing for users.

Proposed Turkish translation
-  "your-api-key": "",
+  "your-api-key": "API anahtarınız",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"your-api-key": "",
"your-role-in-org": "Organizasyondaki rolünüz",
"your-settings": "Ayarlarınız",
"your-usage": "Kullanımınız:",
"zip-bundle": "Zip uygulama paketi"
"your-api-key": "API anahtarınız",
"your-role-in-org": "Organizasyondaki rolünüz",
"your-settings": "Ayarlarınız",
"your-usage": "Kullanımınız:",
"zip-bundle": "Zip uygulama paketi"
🤖 Prompt for AI Agents
In messages/tr.json around lines 1408 to 1412, the "your-api-key" entry is an
empty string; replace it with an appropriate Turkish translation (for example:
"API anahtarınız") so the label displays correctly wherever used; update the
value only, keep the key unchanged and ensure the string follows the project's
quoting/formatting conventions.

Comment on lines +174 to 182
"allow-dev-builds": "",
"allow-develoment-bui": "Cho phép xây dựng phát triển",
"allow-device": "",
"allow-device-to-self": "Cho phép thiết bị tự ngắt kết nối/kết nối",
"allow-emulator": "Cho phép giả lập",
"allow-physical-device": "Cho phép thiết bị vật lý",
"allow-prod-build": "Cho phép bản build sản xuất",
"allow-prod-builds": "",
"already-account": "Bạn đã có tài khoản chưa?",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find the messages directory and confirm the vi.json file exists
find . -name "vi.json" -o -name "messages" -type d | head -20

Repository: Cap-go/capgo

Length of output: 85


🏁 Script executed:

# Get the overall structure of the repository
git ls-files | grep -E "(messages|i18n|locale|translation)" | head -30

Repository: Cap-go/capgo

Length of output: 376


🏁 Script executed:

# Check the messages directory
ls -la messages/ 2>/dev/null || echo "Checking alternative paths..."

Repository: Cap-go/capgo

Length of output: 1143


🏁 Script executed:

# Check the vi.json file at the specific line ranges mentioned
wc -l messages/vi.json

Repository: Cap-go/capgo

Length of output: 77


🏁 Script executed:

# Extract and examine the specific line ranges
echo "=== Lines 174-182 ===" && sed -n '174,182p' messages/vi.json
echo ""
echo "=== Lines 211 ===" && sed -n '211p' messages/vi.json
echo ""
echo "=== Lines 280-281 ===" && sed -n '280,281p' messages/vi.json
echo ""
echo "=== Lines 379-390 ===" && sed -n '379,390p' messages/vi.json
echo ""
echo "=== Lines 414-416 ===" && sed -n '414,416p' messages/vi.json
echo ""
echo "=== Lines 577-578 ===" && sed -n '577,578p' messages/vi.json

Repository: Cap-go/capgo

Length of output: 1673


🏁 Script executed:

# Continue checking remaining line ranges
echo "=== Lines 698-708 ===" && sed -n '698,708p' messages/vi.json
echo ""
echo "=== Lines 1011-1024 ===" && sed -n '1011,1024p' messages/vi.json
echo ""
echo "=== Lines 1053-1065 ===" && sed -n '1053,1065p' messages/vi.json
echo ""
echo "=== Lines 1182 ===" && sed -n '1182p' messages/vi.json

Repository: Cap-go/capgo

Length of output: 3015


🏁 Script executed:

# Check the i18n module and service files
cat messages/README.md

Repository: Cap-go/capgo

Length of output: 1037


🏁 Script executed:

# Examine the i18n module
cat -n src/modules/i18n.ts | head -100

Repository: Cap-go/capgo

Length of output: 2548


🏁 Script executed:

# Check i18n service
cat -n src/services/i18n.ts | head -150

Repository: Cap-go/capgo

Length of output: 951


🏁 Script executed:

# Look for any fallback configuration or missing key handling
rg -A 5 -B 5 "fallback\|missing\|missing-key" src/ --type ts --type js

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check how i18n is used in templates/components to see if there's any fallback logic
rg "i18n\|t\(" src/ --type vue --type ts | head -20

Repository: Cap-go/capgo

Length of output: 83


🏁 Script executed:

# Look for the createI18n config more thoroughly
rg "createI18n\|i18n\." src/modules/i18n.ts -A 15

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Check for any vue-i18n fallback or missing message handlers
rg "missingWarn\|fallbackLocale\|fallback" --type ts --type js

Repository: Cap-go/capgo

Length of output: 38


🏁 Script executed:

# Look at the entire createI18n configuration in i18n.ts
cat -n src/modules/i18n.ts

Repository: Cap-go/capgo

Length of output: 2548


🏁 Script executed:

# Compare empty strings in vi.json vs en.json for the same keys
echo "=== Checking corresponding keys in en.json ===" && \
sed -n '174,182p' messages/en.json && \
echo "" && \
sed -n '414,416p' messages/en.json && \
echo "" && \
sed -n '1053,1065p' messages/en.json

Repository: Cap-go/capgo

Length of output: 1169


🏁 Script executed:

# Search for specific empty string keys in vi.json and compare with en.json
echo "=== Checking 'allow-dev-builds' ===" && \
rg '"allow-dev-builds"' messages/en.json messages/vi.json && \
echo "" && \
echo "=== Checking 'password-policy' ===" && \
rg '"password-policy"' messages/en.json messages/vi.json && \
echo "" && \
echo "=== Checking 'characters' ===" && \
rg '"characters"' messages/en.json messages/vi.json

Repository: Cap-go/capgo

Length of output: 443


🏁 Script executed:

# Count empty string values in vi.json and compare with en.json
echo "Empty strings in vi.json:" && \
rg '": ""' messages/vi.json | wc -l && \
echo "Empty strings in en.json:" && \
rg '": ""' messages/en.json | wc -l

Repository: Cap-go/capgo

Length of output: 112


🏁 Script executed:

# Search vue-i18n documentation behavior regarding empty strings and missing keys
web_search
vue-i18n behavior empty string values fallback to english default locale

Repository: Cap-go/capgo

Length of output: 153


🌐 Web query:

vue-i18n empty string fallback behavior missing translation

💡 Result:

Short answer: By default vue-i18n treats an empty string as a valid (present) translation and will NOT fall back to another locale — so an empty-string message will be returned instead of using the fallback locale. Recent versions add explicit options to change that behaviour.

Details and how to fix it

  • Legacy vue-i18n considered an empty string as translated (no fallback). See the GitHub issue discussing this behavior. [1]
  • Newer vue-i18n releases expose controls: e.g. fallbackRootWithEmptyString (added v8.26+) lets you decide whether an empty string should fall back to root/global messages. The docs also explain fallbackLocale behavior and related warnings. [2][3]
  • Note: the v9 docs call out differences in default empty-string fallback behavior vs earlier versions — check the version of vue-i18n you use and set fallbackRootWithEmptyString (or configure fallbackLocale / missing handlers) accordingly. [2][4]

References

  1. GitHub issue: Change the way missing translation keys are detected. [1]
  2. Vue I18n guide — Fallbacking (official docs). [2]
  3. Vue I18n API reference (fallbackRoot / fallbackRootWithEmptyString explanation). [3]
  4. Legacy repo fallback docs. [4]

🌐 Web query:

vue-i18n default fallback locale configuration

💡 Result:

Short summary — how to configure Vue I18n fallback locale

  • Set fallbackLocale when creating i18n:

    • string: fallbackLocale: 'en'
    • array: fallbackLocale: ['en','fr']
    • decision map: fallbackLocale: { 'de-CH': ['fr','it'], default: ['en','da'] }
      Example: createI18n({ locale: 'ja', fallbackLocale: 'en', messages }) will use 'en' for missing keys. [1]
  • Implicit locale fallback: locales with region/dialect fall back automatically (e.g. de-DE-bavarian → de-DE → de). Append "!" to suppress implicit fallback (e.g. de-DE!). [1]

  • Defaults and warnings:

    • fallbackLocale default is false (no explicit fallback). [2]
    • By default fallback emits console warnings in dev; suppress with fallbackWarn / silentFallbackWarn and missingWarn / silentTranslationWarn depending on Composition vs Legacy API. [1]
  • Nuxt integration: pass fallbackLocale inside vueI18n options (supports same string/array/map shapes). [3]

Sources: Vue I18n fallback guide [1], Vue I18n API (fallbackLocale default) [2], Nuxt i18n docs (vueI18n options) [3].


Empty translations ("") across 76 keys will display as blank in Vietnamese UI

This file contains 76 empty-string values with no corresponding English fallback configured. Vue-i18n treats empty strings as valid translations by default and will not fall back to another locale when empty. This means Vietnamese users will see blank/missing labels for:

  • New feature keys (allow-dev-builds, allow-device, allow-prod-builds)
  • Channel management helpers (channel-allow-device-self, channel-created, channel-disable-auto-update*)
  • Password requirements (characters, characters-minimum)
  • Password policy settings (password-policy, password-policy-description, password-policy-impact-warning, password-policy-required*)
  • Notification descriptions and webhook delivery helpers

Given the security sensitivity of password-policy and 2FA flows, consider either:

  • Removing untranslated keys from vi.json until real translations are ready, or
  • Seeding them with English strings as temporary placeholders and tracking missing translations separately
🤖 Prompt for AI Agents
In messages/vi.json around lines 174 to 182, there are empty-string translations
(and a total of 76 keys) which will render blank in the Vietnamese UI; find all
keys with "" values and either remove those keys from vi.json until a proper
Vietnamese translation exists or populate them with the English source text as
temporary placeholders, and add a short comment or entry in your translation
tracking (e.g., a missing-translations list or ticket) so they can be replaced
with proper Vietnamese translations later.

@riderx riderx merged commit 8445223 into main Jan 4, 2026
11 checks passed
@riderx riderx deleted the WcaleNieWolny/2fa-enforce-fail-card branch January 4, 2026 01:23
Dalanir pushed a commit that referenced this pull request Jan 12, 2026
* feat: add 2FA enforcement fail card

Implement 2FA enforcement UI when users don't have 2FA enabled but their organization requires it. Shows a fail card similar to password policy enforcement with a "Setup 2FA Now" button that auto-triggers the 2FA setup flow. Prevents data loading and displays enforcement message across dashboard, apps list, and organization settings pages.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: sort imports alphabetically in settings.vue

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add 2FA setup messages in multiple languages

Added new translations for 2FA setup access and requirement messages in German, Spanish, French, Hindi, Indonesian, Italian, Japanese, Korean, Polish, Portuguese, Russian, Turkish, Vietnamese, and Chinese. This enhances user experience by providing clear instructions for enabling 2FA across different languages.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
@coderabbitai coderabbitai bot mentioned this pull request Feb 4, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants