Skip to content

RR-T42 Fixing translations#110

Merged
ucswift merged 1 commit intomasterfrom
develop
Mar 20, 2026
Merged

RR-T42 Fixing translations#110
ucswift merged 1 commit intomasterfrom
develop

Conversation

@ucswift
Copy link
Member

@ucswift ucswift commented Mar 20, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Language selection now available on the login screen
    • Added support for German, French, Italian, Polish, Swedish, and Ukrainian languages
  • Documentation

    • Added project operating guidelines documentation
  • Chores

    • Updated repository configuration

@coderabbitai
Copy link

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

This PR introduces comprehensive multi-language support by adding 7 new language translations (Swedish, German, French, Italian, Polish, Ukrainian, Spanish), refactoring i18n initialization with language validation, and implementing language selection UI in login and settings. Additionally, .gitignore patterns and a CLAUDE.md policy document are added.

Changes

Cohort / File(s) Summary
Configuration & Documentation
.gitignore, CLAUDE.md
Added .dual-graph/ to ignore patterns and introduced a new operational policy document defining MCP context retrieval procedures, token-usage tooling, context-store JSON schema, and Resgrid stack code-quality/style/testing rules.
i18n Infrastructure
src/lib/i18n/index.tsx, src/lib/i18n/resources.ts
Introduced getInitialLanguage() function to validate saved and device locale languages against supported resource keys; expanded resources object with 7 new language entries (es, sv, de, fr, it, pl, uk) and updated existing structure format; Language type now reflects extended locale set.
Language Selection UI
src/app/login/login-form.tsx, src/components/settings/language-item.tsx
Added language dropdown selector to login form with LANGUAGES constant and state integration; extended LanguageItem options with Swedish, German, French, Italian, Polish, Ukrainian, and Arabic language choices.
Translation Resources
src/translations/en.json, src/translations/ar.json, src/translations/es.json, src/translations/de.json, src/translations/fr.json, src/translations/it.json, src/translations/pl.json, src/translations/sv.json, src/translations/uk.json
Added login.select_language key and extended settings language labels across all locales; introduced complete German, French, Italian, Polish, Swedish, and Ukrainian translation files (~978 lines each) covering audio streams, Bluetooth, calendar, calls, contacts, login, messaging, settings, shifts, navigation, and other app domains with interpolation placeholders.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 In Swedish, German, French we speak,
Polish, Ukrainian—no more pique!
The rabbit hops through tongues so grand,
Now every user finds their land! 🌍✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title 'RR-T42 Fixing translations' is vague and does not clearly convey what specific translation work was done. While it references a ticket and mentions translations, it lacks detail about the actual changes (adding new languages, updating existing translations, etc.). Provide a more descriptive title such as 'Add support for multiple languages (Spanish, Swedish, German, etc.)' or 'Extend i18n with additional language resources and UI selectors' to better communicate the scope of translation changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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

@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: 5

🧹 Nitpick comments (1)
src/app/login/login-form.tsx (1)

24-34: Wrap language labels in t() to follow translation governance.

The LANGUAGES array at lines 24-34 uses hardcoded labels that bypass the translation system. Line 206 renders label={lang.label} directly without t(). The file already imports useTranslation() and translation keys exist in en.json (e.g., "english", "spanish", "swedish").

♻️ Suggested refactor

Replace hardcoded labels with translation keys:

-const LANGUAGES: { label: string; value: Language }[] = [
-  { label: 'English', value: 'en' },
-  { label: 'Español', value: 'es' },
-  { label: 'Svenska', value: 'sv' },
-  { label: 'Deutsch', value: 'de' },
-  { label: 'Français', value: 'fr' },
-  { label: 'Italiano', value: 'it' },
-  { label: 'Polski', value: 'pl' },
-  { label: 'Українська', value: 'uk' },
-  { label: 'العربية', value: 'ar' },
-];
+const LANGUAGES: { labelKey: string; value: Language }[] = [
+  { labelKey: 'english', value: 'en' },
+  { labelKey: 'spanish', value: 'es' },
+  { labelKey: 'swedish', value: 'sv' },
+  { labelKey: 'german', value: 'de' },
+  { labelKey: 'french', value: 'fr' },
+  { labelKey: 'italian', value: 'it' },
+  { labelKey: 'polish', value: 'pl' },
+  { labelKey: 'ukrainian', value: 'uk' },
+  { labelKey: 'arabic', value: 'ar' },
+];

Then at line 206, update the render:

-<SelectItem key={lang.value} label={lang.label} value={lang.value} />
+<SelectItem key={lang.value} label={t(lang.labelKey)} value={lang.value} />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/login/login-form.tsx` around lines 24 - 34, The LANGUAGES array
currently uses hardcoded labels; change it to store translation keys instead
(e.g., value pairs where label is the key like 'english', 'spanish', etc.) and
then call t() when rendering (the code that uses label={lang.label} should
become label={t(lang.label)}). Locate the LANGUAGES constant and replace its
label strings with the corresponding translation keys, ensure useTranslation()
is used in this file to obtain t, and update the render site that passes
label={lang.label} to call t(lang.label) instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLAUDE.md`:
- Line 59: Replace the phrase "Whenever you make a decision" with the tighter
wording "Whenever you decide" in the sentence that currently reads "Whenever you
make a decision, identify a task, note a next step, fact, or blocker during a
conversation, append it to `.dual-graph/context-store.json`" so the policy reads
"Whenever you decide, identify a task, note a next step, fact, or blocker during
a conversation, append it to `.dual-graph/context-store.json`."
- Line 114: Replace the unhyphenated phrase "Expo managed project" with the
hyphenated compound adjective "Expo-managed project" in the sentence "This is an
Expo managed project using prebuild. Do NOT make native code changes outside
Expo prebuild capabilities." so it reads "This is an Expo-managed project using
prebuild..." to correct the compound adjective usage.
- Around line 59-73: Update the CLAUDE.md context-store logging rules to
explicitly forbid logging secrets/PII and require validation/sanitization before
appending to .dual-graph/context-store.json: add a new guardrail paragraph
stating no credentials, tokens, emails, phone numbers, SSNs, or user-identifying
data may be logged; require that the “To append” workflow validate the entry
(content length <15 words, allowed tags/files), run a sanitizer/PII detector on
content and files, and reject or redact entries containing PII/secrets; only
write the file and call graph_register_edit on .dual-graph/context-store.json
after the entry passes these checks; reference graph_register_edit and
.dual-graph/context-store.json in the doc so implementers know where enforcement
must occur.

In `@src/translations/pl.json`:
- Around line 48-49: The string value for key "tap_scan_to_find_devices" uses a
mismatched closing ASCII quote which breaks JSON; update the closing quote
character after the word Skanuj from U+0022 (") to the matching Unicode right
double quotation mark U+201D (”) so the opening „ (U+201E) and closing ”
(U+201D) match and the JSON string is valid.

In `@src/translations/uk.json`:
- Around line 61-62: The Ukrainian translation keys for "attendeesCount"
currently only provide singular and a single _plural form which is insufficient;
update the translation entries for "attendeesCount" (and any other Ukrainian
keys using only _plural) to CLDR-aware forms by adding "attendeesCount_one",
"attendeesCount_few", "attendeesCount_many", and "attendeesCount_other" with
correct Ukrainian declensions, or alternatively disable compatibilityJSON:'v3'
so i18next will use legacy 2-form mapping—ensure you update all similar keys
(e.g., those around lines with attendeesCount) to use the _one/_few/_many/_other
suffixes so counts render correctly.

---

Nitpick comments:
In `@src/app/login/login-form.tsx`:
- Around line 24-34: The LANGUAGES array currently uses hardcoded labels; change
it to store translation keys instead (e.g., value pairs where label is the key
like 'english', 'spanish', etc.) and then call t() when rendering (the code that
uses label={lang.label} should become label={t(lang.label)}). Locate the
LANGUAGES constant and replace its label strings with the corresponding
translation keys, ensure useTranslation() is used in this file to obtain t, and
update the render site that passes label={lang.label} to call t(lang.label)
instead.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1036bb58-f859-47c5-927e-32b9a82c039c

📥 Commits

Reviewing files that changed from the base of the PR and between d72d9ed and f489807.

📒 Files selected for processing (15)
  • .gitignore
  • CLAUDE.md
  • src/app/login/login-form.tsx
  • src/components/settings/language-item.tsx
  • src/lib/i18n/index.tsx
  • src/lib/i18n/resources.ts
  • src/translations/ar.json
  • src/translations/de.json
  • src/translations/en.json
  • src/translations/es.json
  • src/translations/fr.json
  • src/translations/it.json
  • src/translations/pl.json
  • src/translations/sv.json
  • src/translations/uk.json


## Context Store

Whenever you make a decision, identify a task, note a next step, fact, or blocker during a conversation, append it to `.dual-graph/context-store.json`.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Tighten wording for readability.

“Whenever you make a decision” is slightly wordy; “Whenever you decide” reads cleaner in policy text.

🧰 Tools
🪛 LanguageTool

[style] ~59-~59: ‘make a decision’ might be wordy. Consider a shorter alternative.
Context: ...r hook. ## Context Store Whenever you make a decision, identify a task, note a next step, fac...

(EN_WORDINESS_PREMIUM_MAKE_A_DECISION)

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

In `@CLAUDE.md` at line 59, Replace the phrase "Whenever you make a decision" with
the tighter wording "Whenever you decide" in the sentence that currently reads
"Whenever you make a decision, identify a task, note a next step, fact, or
blocker during a conversation, append it to `.dual-graph/context-store.json`" so
the policy reads "Whenever you decide, identify a task, note a next step, fact,
or blocker during a conversation, append it to
`.dual-graph/context-store.json`."

Comment on lines +59 to +73
Whenever you make a decision, identify a task, note a next step, fact, or blocker during a conversation, append it to `.dual-graph/context-store.json`.

**Entry format:**
```json
{"type": "decision|task|next|fact|blocker", "content": "one sentence max 15 words", "tags": ["topic"], "files": ["relevant/file.ts"], "date": "YYYY-MM-DD"}
```

**To append:** Read the file → add the new entry to the array → Write it back → call `graph_register_edit` on `.dual-graph/context-store.json`.

**Rules:**
- Only log things worth remembering across sessions (not every minor detail)
- `content` must be under 15 words
- `files` lists the files this decision/task relates to (can be empty)
- Log immediately when the item arises — not at session end

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add explicit “no secrets/PII” guardrails to context-store logging.

This section mandates frequent logging but never forbids credentials, tokens, or user-identifying data. Add a hard rule to prevent compliance/privacy leaks.

🔐 Suggested policy patch
 ## Context Store

 Whenever you make a decision, identify a task, note a next step, fact, or blocker during a conversation, append it to `.dual-graph/context-store.json`.
@@
 **Rules:**
 - Only log things worth remembering across sessions (not every minor detail)
+- Never log secrets or sensitive data (API keys, tokens, passwords, PII, incident payloads)
 - `content` must be under 15 words
 - `files` lists the files this decision/task relates to (can be empty)
 - Log immediately when the item arises — not at session end
🧰 Tools
🪛 LanguageTool

[style] ~59-~59: ‘make a decision’ might be wordy. Consider a shorter alternative.
Context: ...r hook. ## Context Store Whenever you make a decision, identify a task, note a next step, fac...

(EN_WORDINESS_PREMIUM_MAKE_A_DECISION)

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

In `@CLAUDE.md` around lines 59 - 73, Update the CLAUDE.md context-store logging
rules to explicitly forbid logging secrets/PII and require
validation/sanitization before appending to .dual-graph/context-store.json: add
a new guardrail paragraph stating no credentials, tokens, emails, phone numbers,
SSNs, or user-identifying data may be logged; require that the “To append”
workflow validate the entry (content length <15 words, allowed tags/files), run
a sanitizer/PII detector on content and files, and reject or redact entries
containing PII/secrets; only write the file and call graph_register_edit on
.dual-graph/context-store.json after the entry passes these checks; reference
graph_register_edit and .dual-graph/context-store.json in the doc so
implementers know where enforcement must occur.

- Organize by feature: group related components, hooks, and styles together.
- **Conditional rendering: use `? :` — never `&&`.**
- All user-visible text must be wrapped in `t()` from `react-i18next`. Translation files are in `src/translations`.
- This is an Expo managed project using prebuild. Do NOT make native code changes outside Expo prebuild capabilities.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use a hyphenated compound adjective.

“Expo managed project” should be “Expo-managed project”.

🧰 Tools
🪛 LanguageTool

[grammar] ~114-~114: Use a hyphen to join words.
Context: ...in src/translations. - This is an Expo managed project using prebuild. Do NOT m...

(QB_NEW_EN_HYPHEN)

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

In `@CLAUDE.md` at line 114, Replace the unhyphenated phrase "Expo managed
project" with the hyphenated compound adjective "Expo-managed project" in the
sentence "This is an Expo managed project using prebuild. Do NOT make native
code changes outside Expo prebuild capabilities." so it reads "This is an
Expo-managed project using prebuild..." to correct the compound adjective usage.

Comment on lines +48 to +49
"tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth",
"unknown_device": "Nieznane urządzenie"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify JSON parses successfully after the fix.
python - <<'PY'
import json
path = "src/translations/pl.json"
with open(path, "r", encoding="utf-8") as f:
    json.load(f)
print("OK: valid JSON")
PY

Repository: Resgrid/Responder

Length of output: 827


🏁 Script executed:

sed -n '45,52p' src/translations/pl.json | cat -A

Repository: Resgrid/Responder

Length of output: 508


🏁 Script executed:

hexdump -C src/translations/pl.json | sed -n '100,150p'

Repository: Resgrid/Responder

Length of output: 106


🏁 Script executed:

python3 <<'EOF'
with open('src/translations/pl.json', 'rb') as f:
    content = f.read()
    # Find line 48 (approximately)
    lines = content.split(b'\n')
    for i, line in enumerate(lines[45:52], start=46):
        print(f"Line {i}: {line}")
        print(f"  Repr: {repr(line)}")
        print()
EOF

Repository: Resgrid/Responder

Length of output: 942


🏁 Script executed:

python3 <<'EOF'
# Decode the problematic line to see the actual characters
line_48_bytes = b'    "tap_scan_to_find_devices": "Naci\xc5\x9bnij \xe2\x80\x9eSkanuj", aby znale\xc5\xba\xc4\x87 urz\xc4\x85dzenia audio Bluetooth",'
line_48_str = line_48_bytes.decode('utf-8')
print("Line 48 as text:")
print(line_48_str)
print()

# Check what the fancy quotes are
print("Character analysis:")
for i, char in enumerate(line_48_str):
    if ord(char) > 127:
        print(f"Position {i}: {repr(char)} (U+{ord(char):04X})")
EOF

Repository: Resgrid/Responder

Length of output: 321


🏁 Script executed:

python3 <<'EOF'
# Get the exact sequence around "Skanuj"
line_48_bytes = b'    "tap_scan_to_find_devices": "Naci\xc5\x9bnij \xe2\x80\x9eSkanuj", aby znale\xc5\xba\xc4\x87 urz\xc4\x85dzenia audio Bluetooth",'
line_48_str = line_48_bytes.decode('utf-8')

# Find "Skanuj" and show what comes after it
idx = line_48_str.find('Skanuj')
print(f"Characters around 'Skanuj':")
segment = line_48_str[idx-2:idx+10]
print(f"Text: {segment}")
print(f"Repr: {repr(segment)}")
print()

# Check each character
for i, char in enumerate(segment):
    print(f"Pos {i}: {repr(char)} - U+{ord(char):04X} - {char if ord(char) < 128 else 'non-ASCII'}")
EOF

Repository: Resgrid/Responder

Length of output: 427


🏁 Script executed:

python3 <<'EOF'
# Now I see the issue - there's a U+0022 (regular double quote) inside the string
# which terminates the JSON string prematurely
# The fix should use matching curly quotes

# Current (broken):
broken = '    "tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth",'

# Option 1: Use matching fancy quotes
# U+201E = „ (left double quotation mark)
# U+201D = " (right double quotation mark)
fixed = '    "tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth",'

print("Broken version:")
print(repr(broken))
print()
print("Fixed version (with matching fancy quotes):")
print(repr(fixed))
print()

# Verify the fix
import json
test_json = '{' + '\n' + fixed + '\n' + '}'
try:
    json.loads(test_json)
    print("✓ Fixed version parses as valid JSON")
except json.JSONDecodeError as e:
    print(f"✗ Still invalid: {e}")
EOF

Repository: Resgrid/Responder

Length of output: 407


🏁 Script executed:

python3 <<'EOF'
# The issue is: opening fancy quote „ (U+201E) but closing with ASCII " (U+0022)
# Need matching closing fancy quote U+201D (")

broken = '    "tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth",'
# The " after Skanuj is U+0022 (ASCII double quote)
# It should be U+201D (right double quotation mark)

fixed = '    "tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth",'

print("Broken (with ASCII quote):")
print(repr(broken))
idx = broken.find('Skanuj')
segment = broken[idx:idx+8]
print(f"  Around Skanuj: {repr(segment)}")
print()

print("Fixed (with matching fancy quotes):")
print(repr(fixed))
idx = fixed.find('Skanuj')
segment = fixed[idx:idx+8]
print(f"  Around Skanuj: {repr(segment)}")
print()

# Verify the fix
import json
test_json = '{' + '\n' + fixed + '\n' + '}'
try:
    json.loads(test_json)
    print("✓ Fixed version parses as valid JSON")
except json.JSONDecodeError as e:
    print(f"✗ Still invalid: {e}")
EOF

Repository: Resgrid/Responder

Length of output: 466


🏁 Script executed:

python3 <<'EOF'
# I need to use the actual Unicode character U+201D for the closing quote
# Let me build it correctly

broken = '    "tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth",'
# Replace the ASCII " after Skanuj with the fancy closing quote U+201D
fixed = '    "tap_scan_to_find_devices": "Naciśnij „Skanuj\u201d, aby znaleźć urządzenia audio Bluetooth",'

print("Broken version:")
for i, c in enumerate(broken):
    if i >= 39 and i <= 52:
        print(f"  [{i}] {repr(c)} U+{ord(c):04X}")

print()
print("Fixed version:")
for i, c in enumerate(fixed):
    if i >= 39 and i <= 52:
        print(f"  [{i}] {repr(c)} U+{ord(c):04X}")

print()
# Verify the fix
import json
test_json = '{' + '\n' + fixed + '\n' + '}'
try:
    result = json.loads(test_json)
    print("✓ Fixed version parses as valid JSON")
except json.JSONDecodeError as e:
    print(f"✗ Still invalid: {e}")
EOF

Repository: Resgrid/Responder

Length of output: 691


🏁 Script executed:

python3 <<'EOF'
# The test JSON was incomplete. Let me check the full file structure
import json

# Read the actual file
with open('src/translations/pl.json', 'r', encoding='utf-8') as f:
    content = f.read()

# Try to parse it
try:
    data = json.loads(content)
    print("✓ File currently parses as valid JSON")
except json.JSONDecodeError as e:
    print(f"✗ File is invalid JSON:")
    print(f"  Error: {e}")
    print(f"  Line {e.lineno}, Column {e.colno}")
    
    # Show the problematic line
    lines = content.split('\n')
    if e.lineno - 1 < len(lines):
        print(f"  Line content: {repr(lines[e.lineno - 1])}")
EOF

Repository: Resgrid/Responder

Length of output: 306


🏁 Script executed:

python3 <<'EOF'
# Now let me verify that replacing U+0022 with U+201D fixes it
import json

with open('src/translations/pl.json', 'r', encoding='utf-8') as f:
    content = f.read()

# Replace the ASCII quote with the fancy closing quote on line 48
# Find the problematic sequence: „Skanuj" and replace with „Skanuj"
fixed_content = content.replace(
    'Naciśnij „Skanuj", aby',
    'Naciśnij „Skanuj\u201d, aby'
)

# Try to parse the fixed version
try:
    data = json.loads(fixed_content)
    print("✓ File parses as valid JSON after fix")
    print(f"  Total keys: {len(data)}")
except json.JSONDecodeError as e:
    print(f"✗ Still invalid: {e}")

# Show the before and after
print()
print("Before (broken):")
lines = content.split('\n')
print(f"  {repr(lines[47])}")
print()
print("After (fixed):")
lines_fixed = fixed_content.split('\n')
print(f"  {repr(lines_fixed[47])}")
EOF

Repository: Resgrid/Responder

Length of output: 343


Replace the closing ASCII quote with a matching Unicode right double quotation mark on line 48.

The JSON is invalid because line 48 uses an opening fancy quote „ (U+201E) but closes with a regular ASCII quote " (U+0022), which prematurely terminates the string value.

Fix
-    "tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth",
+    "tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth",

Change the closing quote after "Skanuj" from " (U+0022) to " (U+201D).

📝 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
"tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth",
"unknown_device": "Nieznane urządzenie"
"tap_scan_to_find_devices": "Naciśnij „Skanuj", aby znaleźć urządzenia audio Bluetooth",
"unknown_device": "Nieznane urządzenie"
🧰 Tools
🪛 Biome (2.4.7)

[error] 48-48: Property key must be double quoted

(parse)


[error] 48-48: expected : but instead found znaleźć

(parse)


[error] 48-48: expected , but instead found urządzenia

(parse)


[error] 48-48: expected : but instead found audio

(parse)


[error] 48-48: expected , but instead found Bluetooth

(parse)


[error] 48-48: Missing closing quote

(parse)


[error] 49-49: expected , but instead found "unknown_device"

(parse)

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

In `@src/translations/pl.json` around lines 48 - 49, The string value for key
"tap_scan_to_find_devices" uses a mismatched closing ASCII quote which breaks
JSON; update the closing quote character after the word Skanuj from U+0022 (")
to the matching Unicode right double quotation mark U+201D (”) so the opening „
(U+201E) and closing ” (U+201D) match and the JSON string is valid.

Comment on lines +61 to +62
"attendeesCount": "{{count}} учасник",
"attendeesCount_plural": "{{count}} учасників",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== i18n package versions =="
rg -n "\"i18next\"|\"react-i18next\"|\"i18next-icu\"" package.json

echo
echo "== i18n initialization (plural/compat settings) =="
fd "index.tsx" src/lib/i18n --exec sed -n '1,220p' {}

echo
echo "== Ukrainian plural-style keys present =="
rg -n "\"(attendeesCount|eventsCount|recipients_count|selected_count|bulkDeleteSuccess|confirmDelete)\"" src/translations/uk.json

echo
echo "== Check if modern plural suffixes exist (_one/_few/_many/_other) =="
rg -n "\"[a-zA-Z0-9_]+_(one|few|many|other)\"" src/translations/uk.json || true

Repository: Resgrid/Responder

Length of output: 1731


Ukrainian pluralization is incomplete and will produce grammatically incorrect forms.

Lines 61–62 and 83–84 provide only singular + _plural forms for Ukrainian. However, Ukrainian requires 4 plural forms (one, few, many, other) per CLDR rules. With compatibilityJSON: 'v3' enabled, i18next will map values to only 2 forms, causing incorrect declension for counts 2–4 and 5+. Update to use modern CLDR suffixes (_one, _few, _many, _other) or switch to non-v3 compat mode.

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

In `@src/translations/uk.json` around lines 61 - 62, The Ukrainian translation
keys for "attendeesCount" currently only provide singular and a single _plural
form which is insufficient; update the translation entries for "attendeesCount"
(and any other Ukrainian keys using only _plural) to CLDR-aware forms by adding
"attendeesCount_one", "attendeesCount_few", "attendeesCount_many", and
"attendeesCount_other" with correct Ukrainian declensions, or alternatively
disable compatibilityJSON:'v3' so i18next will use legacy 2-form mapping—ensure
you update all similar keys (e.g., those around lines with attendeesCount) to
use the _one/_few/_many/_other suffixes so counts render correctly.

@ucswift
Copy link
Member Author

ucswift commented Mar 20, 2026

Approve

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

This PR is approved.

@ucswift ucswift merged commit 113c4d2 into master Mar 20, 2026
12 of 14 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Mar 20, 2026
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