Skip to content

feat(i18n): integrate Crowdin for translation management and add locale files#59

Merged
JoachimLK merged 18 commits into
mainfrom
feat/crowdin-translation
Feb 28, 2026
Merged

feat(i18n): integrate Crowdin for translation management and add locale files#59
JoachimLK merged 18 commits into
mainfrom
feat/crowdin-translation

Conversation

@JoachimLK

@JoachimLK JoachimLK commented Feb 27, 2026

Copy link
Copy Markdown
Contributor
  • Updated package.json to include Crowdin CLI commands for uploading and downloading translations.
  • Added .crowdin.yml for Crowdin project configuration.
  • Created I18N.md documentation for internationalization setup and local Crowdin commands.
  • Configured i18n with Nuxt using i18n.config.ts.
  • Added locale files for English, Spanish, French, and German with basic language keys.

Summary

  • What does this PR change?
  • Why is this needed?

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Chore

Validation

  • I tested locally
  • I added/updated relevant documentation
  • I verified multi-tenant scoping and auth behavior for affected API paths

DCO

  • All commits in this PR are signed off (Signed-off-by) via git commit -s

Summary by CodeRabbit

  • New Features

    • Full internationalization: English, German, Spanish, French, Vietnamese, Norwegian locales added.
    • Locale-aware routing, site config, and SEO head integration so links and metadata respect selected language.
    • Language switcher component added across layouts and pages; UI strings wired to translations.
  • Documentation

    • New i18n guide and README section detailing Crowdin workflow and local testing.
  • Chores

    • Crowdin config and scripts added; i18n module integrated into build and scripts.

…le files

- Updated package.json to include Crowdin CLI commands for uploading and downloading translations.
- Added .crowdin.yml for Crowdin project configuration.
- Created I18N.md documentation for internationalization setup and local Crowdin commands.
- Configured i18n with Nuxt using i18n.config.ts.
- Added locale files for English, Spanish, French, and German with basic language keys.
@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-59 February 27, 2026 18:31 Destroyed
@railway-app

railway-app Bot commented Feb 27, 2026

Copy link
Copy Markdown

🚅 Deployed to the reqcore-pr-59 environment in applirank

Service Status Web Updated (UTC)
applirank ✅ Success (View Logs) Web Feb 27, 2026 at 8:55 pm

@coderabbitai

coderabbitai Bot commented Feb 27, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Enable site-wide internationalization: add Nuxt i18n config, locale JSONs, Crowdin config and scripts; replace literal routes with locale-aware helpers across pages/components/middleware/composables; add LanguageSwitcher UI and wire locale-aware head/meta.

Changes

Cohort / File(s) Summary
I18n config & Crowdin docs
/.crowdin.yml, i18n.config.ts, nuxt.config.ts, I18N.md, README.md, package.json
Add Crowdin config and npm scripts; enable @nuxtjs/i18n; introduce global i18n settings and documentation; update package scripts/deps for Crowdin and tooling.
Locale files
i18n/locales/{en.json,de.json,es.json,fr.json,vi.json,nb.json}
Add multiple locale JSON files with common and home translations (navigation, hero, basic UI strings).
LanguageSwitcher & layouts/head
app/components/LanguageSwitcher.vue, app/layouts/{default.vue,auth.vue,public.vue}, app/app.vue
Add LanguageSwitcher component; render it in layouts (conditional for blog route); register locale-aware SEO head via useLocaleHead({ seo: true }) and propagate head attrs.
Bulk route localization (pages & blog)
app/pages/..., app/pages/blog/..., app/pages/dashboard/... (many files)
Replace hard-coded route strings with locale-aware helpers (:to="$localePath(...)", useLocalePath()); add defineI18nRoute(false)/definePageMeta({ i18n: false }) for blog pages.
Components & sidebar updates
app/components/...
app/components/AppSidebar.vue, CandidateDetailSidebar.vue, OrgSwitcher.vue, PipelineCard.vue
Switch NuxtLink targets and active-route checks to use $localePath/useLocalePath(); integrate LanguageSwitcher in header areas.
Composables & middleware
app/composables/{useCandidate,useCurrentOrg,useJob}.ts, app/middleware/{auth,guest,require-org}.ts
Use useLocalePath() for post-action navigation and middleware redirects to ensure locale-prefixed destinations.
Pages: forms, jobs, candidates, index, roadmap, catalog, etc.
app/pages/index.vue, app/pages/jobs/..., app/pages/roadmap.vue, app/pages/catalog/index.vue, app/pages/blog/..., app/pages/dashboard/...
Localize all internal navigation and CTA links to use $localePath, add i18n translation bindings (e.g., useI18n()/t(...)) in main landing page.
Tooling & manifest changes
package.json, /.crowdin.yml
Add @crowdin/cli devDependency and Crowdin mapping; add Crowdin upload/download/sync npm scripts; add @nuxtjs/i18n dependency and dev tooling (vue-tsc).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as LanguageSwitcher (Client)
    participant Router as Nuxt Router
    participant App as Nuxt App (i18n + Head)
    User->>UI: select locale
    UI->>Router: navigateTo(locale-prefixed route) / $localePath(...)
    Router->>App: resolve locale, load locale JSON
    App->>App: update i18n context and useLocaleHead({seo:true})
    App-->>Router: render localized page (meta/head updated)
    Router-->>User: display localized page
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰
I hopped through routes and swapped their strings,
Planted flags where every small bell rings.
Crowdin sang while locales took flight,
Pages now wear words in morning light.
Carrot breadcrumbs guide each bunny's night. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete. While it mentions some changes (package.json, .crowdin.yml, I18N.md, i18n.config.ts), it lacks detail on the scope of implementation, does not explain why i18n is needed, and all PR template checklist items remain unchecked. Complete the PR template by: (1) providing a detailed 'Summary' explaining what changed and why; (2) marking the appropriate 'Type of change' checkbox (appears to be 'Feature'); (3) checking validation items where applicable (testing, docs); (4) signing off commits with DCO if required.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main changes: integration of Crowdin for translation management and addition of multiple locale files (en, es, fr, de, vi, nb).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/crowdin-translation

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/pages/blog/[...slug].vue (1)

119-127: ⚠️ Potential issue | 🟡 Minor

Hardcoded 'en-US' locale conflicts with i18n goals.

The date formatting uses a hardcoded locale which won't adapt to the user's selected language. Consider using the current i18n locale for consistent formatting.

🌐 Proposed fix to use current locale
+const { locale } = useI18n()
+
 <span v-if="post!.date" class="inline-flex items-center gap-1.5">
   <Calendar class="size-3.5" />
   {{
-    new Date(post!.date).toLocaleDateString('en-US', {
+    new Date(post!.date).toLocaleDateString(locale, {
       year: 'numeric',
       month: 'long',
       day: 'numeric',
     })
   }}
 </span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/pages/blog/`[...slug].vue around lines 119 - 127, Replace the hardcoded
'en-US' in the template's date formatting (the expression new
Date(post!.date).toLocaleDateString('en-US', {...}) inside the
blog/[...slug].vue template) with the app's current i18n locale value (e.g.,
obtain locale from useI18n() or the component's i18n instance and use
locale.value or $i18n.locale). Move the date formatting into a small
computed/property (e.g., formattedDate or formatPostDate) that calls
toLocaleDateString(currentLocale, options) and provide a sensible fallback
locale if locale is undefined, then use that computed value in the template.
🧹 Nitpick comments (3)
package.json (1)

47-48: Consider exposing vue-tsc through a dedicated npm script.

Since vue-tsc is now present, adding a typecheck script would make CI/local checks easier to standardize.

♻️ Suggested script addition
   "scripts": {
+    "typecheck": "vue-tsc --noEmit",
     "build": "nuxt build",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 47 - 48, Add a new npm script named "typecheck" in
package.json that runs vue-tsc (e.g., invoking the installed "vue-tsc" binary
with noEmit) so CI and local developers can run a standardized type check;
update the "scripts" object to include the "typecheck" key and ensure it uses
"vue-tsc" (and any flags like --noEmit) to perform type-only checking.
i18n.config.ts (1)

5-6: Enable missing/fallback warnings in development to catch translation issues during QA.

Hardcoding both warnings to false prevents detection of missing keys. Use import.meta.dev to conditionally enable warnings in development while keeping logs clean in production.

♻️ Suggested tweak
 export default defineI18nConfig(() => ({
   legacy: false,
   locale: 'en',
   fallbackLocale: 'en',
-  missingWarn: false,
-  fallbackWarn: false,
+  missingWarn: import.meta.dev,
+  fallbackWarn: import.meta.dev,
 }))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@i18n.config.ts` around lines 5 - 6, The config currently hardcodes
missingWarn: false and fallbackWarn: false which hides missing-translation
issues; change these to be conditional on the dev environment by using
import.meta.dev (e.g., set missingWarn: import.meta.dev and fallbackWarn:
import.meta.dev) so warnings are enabled during development/QA and remain
disabled in production; update the i18n config where missingWarn and
fallbackWarn are defined to use import.meta.dev instead of literal false.
app/pages/dashboard/candidates/index.vue (1)

128-128: Consider using i18n locale for date formatting.

Similar to the blog page, toLocaleDateString() without an explicit locale will use the browser's default rather than the user's selected i18n locale.

🌐 Optional: Use i18n locale for consistency
+const { locale } = useI18n()
+
-<span>{{ new Date(c.createdAt).toLocaleDateString() }}</span>
+<span>{{ new Date(c.createdAt).toLocaleDateString(locale) }}</span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/pages/dashboard/candidates/index.vue` at line 128, Replace the plain new
Date(c.createdAt).toLocaleDateString() with the app i18n-aware formatter: import
and call the vue-i18n helpers (e.g., useI18n) in the component setup to get the
locale/date formatter (for example const { d } = useI18n()), then render the
date as d(new Date(c.createdAt)) or explicitly pass the i18n locale into
Intl.DateTimeFormat using useI18n().locale so the displayed date follows the
user-selected i18n locale instead of the browser default.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@app/pages/blog/`[...slug].vue:
- Around line 119-127: Replace the hardcoded 'en-US' in the template's date
formatting (the expression new Date(post!.date).toLocaleDateString('en-US',
{...}) inside the blog/[...slug].vue template) with the app's current i18n
locale value (e.g., obtain locale from useI18n() or the component's i18n
instance and use locale.value or $i18n.locale). Move the date formatting into a
small computed/property (e.g., formattedDate or formatPostDate) that calls
toLocaleDateString(currentLocale, options) and provide a sensible fallback
locale if locale is undefined, then use that computed value in the template.

---

Nitpick comments:
In `@app/pages/dashboard/candidates/index.vue`:
- Line 128: Replace the plain new Date(c.createdAt).toLocaleDateString() with
the app i18n-aware formatter: import and call the vue-i18n helpers (e.g.,
useI18n) in the component setup to get the locale/date formatter (for example
const { d } = useI18n()), then render the date as d(new Date(c.createdAt)) or
explicitly pass the i18n locale into Intl.DateTimeFormat using useI18n().locale
so the displayed date follows the user-selected i18n locale instead of the
browser default.

In `@i18n.config.ts`:
- Around line 5-6: The config currently hardcodes missingWarn: false and
fallbackWarn: false which hides missing-translation issues; change these to be
conditional on the dev environment by using import.meta.dev (e.g., set
missingWarn: import.meta.dev and fallbackWarn: import.meta.dev) so warnings are
enabled during development/QA and remain disabled in production; update the i18n
config where missingWarn and fallbackWarn are defined to use import.meta.dev
instead of literal false.

In `@package.json`:
- Around line 47-48: Add a new npm script named "typecheck" in package.json that
runs vue-tsc (e.g., invoking the installed "vue-tsc" binary with noEmit) so CI
and local developers can run a standardized type check; update the "scripts"
object to include the "typecheck" key and ensure it uses "vue-tsc" (and any
flags like --noEmit) to perform type-only checking.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22994b2 and 4c409f7.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (46)
  • .crowdin.yml
  • I18N.md
  • README.md
  • app/app.vue
  • app/components/AppSidebar.vue
  • app/components/CandidateDetailSidebar.vue
  • app/components/OrgSwitcher.vue
  • app/components/PipelineCard.vue
  • app/composables/useCandidate.ts
  • app/composables/useCurrentOrg.ts
  • app/composables/useJob.ts
  • app/layouts/public.vue
  • app/middleware/auth.ts
  • app/middleware/guest.ts
  • app/middleware/require-org.ts
  • app/pages/auth/sign-in.vue
  • app/pages/auth/sign-up.vue
  • app/pages/blog/[...slug].vue
  • app/pages/blog/index.vue
  • app/pages/catalog/index.vue
  • app/pages/dashboard/applications/[id].vue
  • app/pages/dashboard/applications/index.vue
  • app/pages/dashboard/candidates/[id].vue
  • app/pages/dashboard/candidates/index.vue
  • app/pages/dashboard/candidates/new.vue
  • app/pages/dashboard/index.vue
  • app/pages/dashboard/jobs/[id]/application-form.vue
  • app/pages/dashboard/jobs/[id]/candidates.vue
  • app/pages/dashboard/jobs/[id]/index.vue
  • app/pages/dashboard/jobs/[id]/pipeline.vue
  • app/pages/dashboard/jobs/[id]/swipe.vue
  • app/pages/dashboard/jobs/index.vue
  • app/pages/dashboard/jobs/new.vue
  • app/pages/index.vue
  • app/pages/jobs/[slug]/apply.vue
  • app/pages/jobs/[slug]/confirmation.vue
  • app/pages/jobs/[slug]/index.vue
  • app/pages/jobs/index.vue
  • app/pages/roadmap.vue
  • i18n.config.ts
  • i18n/locales/de.json
  • i18n/locales/en.json
  • i18n/locales/es.json
  • i18n/locales/fr.json
  • nuxt.config.ts
  • package.json

@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-59 February 27, 2026 18:49 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/LanguageSwitcher.vue`:
- Line 36: The hard-coded aria-label on the select element in
LanguageSwitcher.vue should be localized; replace aria-label="Select language"
with a bound value using the project's i18n helper (e.g.
:aria-label="$t('language.select')" or :aria-label="t('language.select')"
depending on whether you use $t in templates or useI18n's t in setup), and add
the corresponding key (e.g. language.select or selectLanguage) to your locale
message files so every locale provides the translated string.

In `@nuxt.config.ts`:
- Around line 135-143: The robots disallow array in nuxt.config.ts only lists
trailing-slash paths (the disallow array) which misses exact paths like
/dashboard and localized variants like /en/dashboard; update the disallow array
to include non-trailing-slash variants and broader patterns (e.g., add
'/dashboard', '/auth', '/onboarding' and their localized forms without trailing
slashes such as '/*/dashboard', '/*/auth', '/*/onboarding' or use wildcard
variants like '/dashboard*') so both bare and localized paths are covered by the
robots rules.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c409f7 and b16d4d3.

📒 Files selected for processing (8)
  • I18N.md
  • app/components/AppSidebar.vue
  • app/components/LanguageSwitcher.vue
  • app/layouts/auth.vue
  • app/layouts/default.vue
  • app/layouts/public.vue
  • i18n/locales/vi.json
  • nuxt.config.ts

Comment thread app/components/LanguageSwitcher.vue Outdated
Comment thread nuxt.config.ts
Comment on lines +135 to +143
disallow: [
'/dashboard/',
'/auth/',
'/api/',
'/onboarding/',
'/*/dashboard/',
'/*/auth/',
'/*/onboarding/',
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Include non-trailing-slash variants in robots.disallow.

Current entries (e.g., /dashboard/) may not cover exact paths like /dashboard and localized /en/dashboard.

🔧 Proposed fix
   robots: {
     disallow: [
+      '/dashboard',
       '/dashboard/',
+      '/auth',
       '/auth/',
+      '/api',
       '/api/',
+      '/onboarding',
       '/onboarding/',
+      '/*/dashboard',
       '/*/dashboard/',
+      '/*/auth',
       '/*/auth/',
+      '/*/onboarding',
       '/*/onboarding/',
     ],
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nuxt.config.ts` around lines 135 - 143, The robots disallow array in
nuxt.config.ts only lists trailing-slash paths (the disallow array) which misses
exact paths like /dashboard and localized variants like /en/dashboard; update
the disallow array to include non-trailing-slash variants and broader patterns
(e.g., add '/dashboard', '/auth', '/onboarding' and their localized forms
without trailing slashes such as '/*/dashboard', '/*/auth', '/*/onboarding' or
use wildcard variants like '/dashboard*') so both bare and localized paths are
covered by the robots rules.

@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-59 February 27, 2026 18:56 Destroyed
@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-59 February 27, 2026 19:22 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/LanguageSwitcher.vue`:
- Around line 122-130: The type mismatch occurs because selectedLocale (from
localeOptions in handleLocaleChange) is inferred as string but switchLocalePath
expects the locale union type; change the code in handleLocaleChange to cast the
found value to the expected type (e.g., const selectedLocale =
localeOptions.value.find(... )?.code as SwitchLocale) or adjust the typing of
localeOptions so .code is already SwitchLocale, then pass that typed value into
switchLocalePath(selectedLocale) to satisfy the pipeline type check.
- Around line 38-44: normalizePath currently causes TS errors because split() is
being called on a nullable type; fix by normalizing the input before splitting
and using nullish coalescing on intermediate values. In the normalizePath
function, replace uses of path.split and subsequent destructuring with a safe
expression like (path ?? '').split('?')[0] and then (withoutQuery ??
'').split('#')[0], and ensure trimmed is computed from a non-null string (e.g.,
(withoutHash ?? '').replace(...)). Update the variables withoutQuery,
withoutHash and trimmed accordingly so TypeScript can verify they are strings.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b16d4d3 and 5f7e28f.

📒 Files selected for processing (4)
  • I18N.md
  • app/components/LanguageSwitcher.vue
  • i18n/locales/nb.json
  • nuxt.config.ts
✅ Files skipped from review due to trivial changes (1)
  • I18N.md

Comment thread app/components/LanguageSwitcher.vue
Comment thread app/components/LanguageSwitcher.vue
@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-59 February 27, 2026 19:28 Destroyed
@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-59 February 27, 2026 19:55 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/pages/index.vue (1)

197-197: Use a route object to pass query parameters to localePath instead of embedding them in the path string.

According to @nuxtjs/i18n v10.x documentation, query parameters should be passed as a structured route object via the query property rather than appended as a string. This approach is more robust and aligns with the library's recommended patterns.

♻️ Suggested change
-            :to="$localePath('/auth/sign-in?live=1')"
+            :to="$localePath({ path: '/auth/sign-in', query: { live: '1' } })"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/pages/index.vue` at line 197, Replace the string path passed to
$localePath in the :to binding with a route object: call $localePath with an
object that specifies the path '/auth/sign-in' (or the route name) and a query
property containing live set to '1', so the :to uses a structured route object
(via $localePath) instead of embedding '?live=1' in the path string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/layouts/default.vue`:
- Line 4: The computed isBlogRoute currently tests route.path with
/^\/blog(?:\/|$)/ which misses locale-prefixed routes; update isBlogRoute to
either use the locale-independent route.name (e.g., check if route.name ===
'blog' or startsWith 'blog') or adjust the regex to allow an optional locale
segment before /blog (e.g., ^\/[a-zA-Z-]{2,}?(?:\/)?\/blog(?:\/|$)), and ensure
the LanguageSwitcher visibility logic relies on the updated isBlogRoute computed
value; locate the computed isBlogRoute declaration and replace the test
accordingly.

---

Nitpick comments:
In `@app/pages/index.vue`:
- Line 197: Replace the string path passed to $localePath in the :to binding
with a route object: call $localePath with an object that specifies the path
'/auth/sign-in' (or the route name) and a query property containing live set to
'1', so the :to uses a structured route object (via $localePath) instead of
embedding '?live=1' in the path string.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f7e28f and cd2acb2.

📒 Files selected for processing (14)
  • I18N.md
  • app/components/LanguageSwitcher.vue
  • app/layouts/default.vue
  • app/pages/blog/[...slug].vue
  • app/pages/blog/index.vue
  • app/pages/catalog/index.vue
  • app/pages/index.vue
  • app/pages/roadmap.vue
  • i18n/locales/de.json
  • i18n/locales/en.json
  • i18n/locales/es.json
  • i18n/locales/fr.json
  • i18n/locales/nb.json
  • i18n/locales/vi.json
🚧 Files skipped from review as they are similar to previous changes (8)
  • i18n/locales/de.json
  • I18N.md
  • app/pages/catalog/index.vue
  • i18n/locales/vi.json
  • i18n/locales/en.json
  • app/components/LanguageSwitcher.vue
  • i18n/locales/nb.json
  • app/pages/roadmap.vue

Comment thread app/layouts/default.vue
<script setup lang="ts">
const route = useRoute()

const isBlogRoute = computed(() => /^\/blog(?:\/|$)/.test(route.path))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Regex does not account for locale-prefixed routes.

With Nuxt i18n, blog routes will be locale-prefixed (e.g., /en/blog, /es/blog, /fr/blog). The current regex only matches paths starting literally with /blog, so the LanguageSwitcher will incorrectly appear on localized blog pages.

Consider checking route.name (locale-independent) or adjusting the regex to handle the optional locale prefix:

🐛 Proposed fix using route name or adjusted regex

Option 1 (preferred): Use route name which is locale-independent

-const isBlogRoute = computed(() => /^\/blog(?:\/|$)/.test(route.path))
+const isBlogRoute = computed(() => route.name?.toString().startsWith('blog'))

Option 2: Adjust regex to handle optional locale prefix

-const isBlogRoute = computed(() => /^\/blog(?:\/|$)/.test(route.path))
+const isBlogRoute = computed(() => /^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?blog(?:\/|$)/.test(route.path))
📝 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
const isBlogRoute = computed(() => /^\/blog(?:\/|$)/.test(route.path))
const isBlogRoute = computed(() => route.name?.toString().startsWith('blog'))
Suggested change
const isBlogRoute = computed(() => /^\/blog(?:\/|$)/.test(route.path))
const isBlogRoute = computed(() => /^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?blog(?:\/|$)/.test(route.path))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/layouts/default.vue` at line 4, The computed isBlogRoute currently tests
route.path with /^\/blog(?:\/|$)/ which misses locale-prefixed routes; update
isBlogRoute to either use the locale-independent route.name (e.g., check if
route.name === 'blog' or startsWith 'blog') or adjust the regex to allow an
optional locale segment before /blog (e.g.,
^\/[a-zA-Z-]{2,}?(?:\/)?\/blog(?:\/|$)), and ensure the LanguageSwitcher
visibility logic relies on the updated isBlogRoute computed value; locate the
computed isBlogRoute declaration and replace the test accordingly.

@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-59 February 27, 2026 20:01 Destroyed
@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-59 February 27, 2026 20:31 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/pages/index.vue`:
- Around line 119-123: Replace the hardcoded blog links so they use the
locale-aware routing helper instead of "/blog": update the NuxtLink instances
that render {{ t('home.nav.blog') }} to call the $localePath('/blog') (or
localePath('/blog')) helper for the to prop, ensuring both occurrences (the link
near the top using NuxtLink and the other occurrence later in the file) use the
same locale-aware expression so navigation preserves the active locale.

In `@i18n/locales/de.json`:
- Around line 10-27: The German locale entries under the "home" object are still
in English; update the values for home.nav keys
("features","openPositions","roadmap","blog","github","dashboard","logIn","signUp"),
home.badge, and home.hero keys
("titleLine1","titleHighlight","subtitleLine1","subtitleLine2","ctaDemo","ctaStart","hint")
with proper German translations so the UI is fully localized; keep the keys
unchanged and only replace the string values with correct German phrases and
punctuation to match the existing localization style.

In `@i18n/locales/vi.json`:
- Around line 10-27: The vi.json locale is incomplete: keys under the "home"
object such as "features", "openPositions", "roadmap", "blog", "github",
"dashboard", "logIn", "signUp" and the "hero" subkeys ("titleLine1",
"titleHighlight", "subtitleLine1", "subtitleLine2", "ctaDemo", "ctaStart",
"hint") are still in English; update those values with correct Vietnamese
translations (preserving the JSON keys exactly) so the landing page is fully
localized for the Vietnamese locale.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd2acb2 and c85d1e0.

📒 Files selected for processing (6)
  • app/pages/index.vue
  • i18n/locales/de.json
  • i18n/locales/en.json
  • i18n/locales/fr.json
  • i18n/locales/nb.json
  • i18n/locales/vi.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • i18n/locales/fr.json

Comment thread app/pages/index.vue
Comment on lines 119 to 123
to="/blog"
class="rounded-md px-3 py-1.5 text-[13px] font-medium text-surface-400 transition hover:text-white"
>
Blog
{{ t('home.nav.blog') }}
</NuxtLink>

Copy link
Copy Markdown

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, let's verify the file exists and check its size
ls -lh app/pages/index.vue 2>/dev/null || echo "File not found"
wc -l app/pages/index.vue 2>/dev/null || echo "Unable to count lines"

Repository: reqcore-inc/reqcore

Length of output: 155


🏁 Script executed:

#!/bin/bash
# Check lines 119-123 (first blog link location)
echo "=== Lines 119-123 ==="
sed -n '119,123p' app/pages/index.vue

# Check lines 1338-1340 (second blog link location)
echo -e "\n=== Lines 1338-1340 ==="
sed -n '1338,1340p' app/pages/index.vue

# Also check surrounding context for better understanding
echo -e "\n=== Context around 119-123 (115-125) ==="
sed -n '115,125p' app/pages/index.vue

echo -e "\n=== Context around 1338-1340 (1335-1345) ==="
sed -n '1335,1345p' app/pages/index.vue

Repository: reqcore-inc/reqcore

Length of output: 1302


🏁 Script executed:

#!/bin/bash
# Run the verification script from the review to find hardcoded NuxtLink paths
rg -nP --type=vue '<NuxtLink[^>]*\bto="/[^"]*"' app -C2

Repository: reqcore-inc/reqcore

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Check if $localePath is used in the file and how
echo "=== Usage of \$localePath in file ==="
rg '\$localePath' app/pages/index.vue -n -B1 -A1 | head -50

Repository: reqcore-inc/reqcore

Length of output: 3372


Use locale-aware routing for blog links.

Both blog links remain hardcoded to "/blog" while all other internal routes in the file use $localePath() for locale-aware navigation. This inconsistency may cause the active locale context to be lost when navigating to blog.

🔧 Proposed fix
-          <NuxtLink
-            to="/blog"
+          <NuxtLink
+            :to="$localePath('/blog')"
             class="rounded-md px-3 py-1.5 text-[13px] font-medium text-surface-400 transition hover:text-white"
           >
             {{ t('home.nav.blog') }}
           </NuxtLink>
-          <NuxtLink to="/blog" class="transition hover:text-white">
+          <NuxtLink :to="$localePath('/blog')" class="transition hover:text-white">
             Blog
           </NuxtLink>

Affects lines 119-123 and 1338-1340.

📝 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
to="/blog"
class="rounded-md px-3 py-1.5 text-[13px] font-medium text-surface-400 transition hover:text-white"
>
Blog
{{ t('home.nav.blog') }}
</NuxtLink>
<NuxtLink
:to="$localePath('/blog')"
class="rounded-md px-3 py-1.5 text-[13px] font-medium text-surface-400 transition hover:text-white"
>
{{ t('home.nav.blog') }}
</NuxtLink>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/pages/index.vue` around lines 119 - 123, Replace the hardcoded blog links
so they use the locale-aware routing helper instead of "/blog": update the
NuxtLink instances that render {{ t('home.nav.blog') }} to call the
$localePath('/blog') (or localePath('/blog')) helper for the to prop, ensuring
both occurrences (the link near the top using NuxtLink and the other occurrence
later in the file) use the same locale-aware expression so navigation preserves
the active locale.

Comment thread i18n/locales/de.json
Comment on lines +10 to +27
"features": "Features",
"openPositions": "Open Positions",
"roadmap": "Roadmap",
"blog": "Blog",
"github": "GitHub",
"dashboard": "Dashboard",
"logIn": "Log in",
"signUp": "Sign up"
},
"badge": "Open Source and Self-Hosted",
"hero": {
"titleLine1": "The hiring system",
"titleHighlight": "you actually own",
"subtitleLine1": "Purpose-built for recruiting teams who refuse per-seat pricing.",
"subtitleLine2": "Designed for the self-hosted era.",
"ctaDemo": "Try live demo",
"ctaStart": "Get started free",
"hint": "No sign-up needed - instant access with sample data"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

German locale is only partially translated.

home.nav.*, home.badge, and most home.hero.* strings are still English. This will produce mixed-language UI for German users.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@i18n/locales/de.json` around lines 10 - 27, The German locale entries under
the "home" object are still in English; update the values for home.nav keys
("features","openPositions","roadmap","blog","github","dashboard","logIn","signUp"),
home.badge, and home.hero keys
("titleLine1","titleHighlight","subtitleLine1","subtitleLine2","ctaDemo","ctaStart","hint")
with proper German translations so the UI is fully localized; keep the keys
unchanged and only replace the string values with correct German phrases and
punctuation to match the existing localization style.

Comment thread i18n/locales/vi.json
Comment on lines +10 to +27
"features": "Features",
"openPositions": "Open Positions",
"roadmap": "Roadmap",
"blog": "Blog",
"github": "GitHub",
"dashboard": "Dashboard",
"logIn": "Log in",
"signUp": "Sign up"
},
"badge": "Open Source and Self-Hosted",
"hero": {
"titleLine1": "The hiring system",
"titleHighlight": "you actually own",
"subtitleLine1": "Purpose-built for recruiting teams who refuse per-seat pricing.",
"subtitleLine2": "Designed for the self-hosted era.",
"ctaDemo": "Try live demo",
"ctaStart": "Get started free",
"hint": "No sign-up needed - instant access with sample data"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Vietnamese locale is only partially translated.

Most home.* values are still English, so selecting Vietnamese will not fully localize the landing page.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@i18n/locales/vi.json` around lines 10 - 27, The vi.json locale is incomplete:
keys under the "home" object such as "features", "openPositions", "roadmap",
"blog", "github", "dashboard", "logIn", "signUp" and the "hero" subkeys
("titleLine1", "titleHighlight", "subtitleLine1", "subtitleLine2", "ctaDemo",
"ctaStart", "hint") are still in English; update those values with correct
Vietnamese translations (preserving the JSON keys exactly) so the landing page
is fully localized for the Vietnamese locale.

@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-59 February 27, 2026 20:50 Destroyed
@JoachimLK JoachimLK merged commit 759b1e6 into main Feb 28, 2026
4 checks passed
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.

1 participant