Skip to content

fix translation length + repair localized /data/ - #949

Open
riderx wants to merge 3 commits into
mainfrom
cursor/translation-length-fr-data-b7eb
Open

fix translation length + repair localized /data/#949
riderx wants to merge 3 commits into
mainfrom
cursor/translation-length-fr-data-b7eb

Conversation

@riderx

@riderx riderx commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

  • Instruct the edge translation worker to keep translations roughly the same length as the source (±30%) so UI layouts stay intact.
  • Honor translate="no" and class="notranslate" when collecting HTML segments.
  • Protect the Live Update product name and bump TRANSLATION_CACHE_VERSION so stale locale caches refresh.
  • Fix /data/: bootstrap metrics live in an inline JSON script (not a huge attribute), and live metric regions are marked translate="no".
  • Escape bootstrap JSON for script embeds (jsonForInlineScript) so </script> cannot break out of the tag.

Why /fr/data/ was broken

Localized /data/ pages were still serving the pre-#947 loading shell (spinner + empty tables, no bootstrap metrics). English already SSR-boots metrics. All locales (fr, de, es, …) were stuck on that stale translated HTML.

After deploy of web + translation worker, the cache version bump forces a fresh translation of the fixed English page.

Test plan

  • bun run check in apps/translation-worker (parser tests include translate=no)
  • jsonForInlineScript round-trips and blocks </script> breakouts
  • Deploy translation worker, then hit /fr/data/ and confirm SSR tables + no loading shell
  • Confirm chrome copy is translated, metric rows stay English/stable
  • Spot-check that short nav/labels are not expanded into long sentences
Open in Web Open in Cursor 

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Improved translation handling to preserve excluded content and markup.
    • Added safeguards to keep important terms such as “Live Update” unchanged.
    • Added translation guidance to maintain approximately consistent text length.
    • Improved loading and display of data metrics, charts, and tables.
  • Bug Fixes

    • Prevented dynamic data and marked content from being translated unintentionally.
    • Improved safe handling of metrics embedded in the data page.

cursoragent and others added 2 commits August 11, 2026 13:16
Tell the edge translator to keep copy near source length so UI layouts
do not break, honor translate=no/notranslate regions, and bump the
translation cache. Move /data metrics bootstrap into a JSON script and
skip translating live metric tables so localized pages keep SSR data.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
Match the existing SEO JSON script pattern so Astro leaves the bootstrap
payload untouched.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The translation worker now skips translate="no" and .notranslate content, preserves protected terms, and limits output length. The data page safely bootstraps metrics through JSON and excludes dynamic values from translation.

Changes

Translation and metrics handling

Layer / File(s) Summary
Translation exclusion handling
apps/translation-worker/src/index.ts, apps/translation-worker/scripts/verify-parser.ts
The parser skips translate="no" and .notranslate regions, preserves excluded HTML, and updates the translation cache version and parser coverage.
Translation prompt safeguards
apps/translation-worker/src/index.ts
Batch and single-text prompts preserve Live Update and request output within approximately ±30% of the source length.
Safe metrics bootstrapping
apps/web/src/lib/liveUpdateMetrics.ts, apps/web/src/pages/data.astro
Metrics use escaped JSON in a dedicated script element. Client hydration reads metrics from that element.
Dynamic content translation exclusions
apps/web/src/pages/data.astro
Dynamic timestamps, charts, tables, failure content, and updater data use translate="no".

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: cursoragent

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the two main changes: translation length handling and repair of localized /data/ pages.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@riderx
riderx marked this pull request as ready for review August 11, 2026 13:34

@cursor cursor 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.

Risk: medium. Left a non-blocking comment (not approving): this changes the edge translation worker and forces a sitewide locale cache refresh, which is above the low-risk auto-approve threshold. Cursor Bugbot was not present; reviewers will be assigned for human review.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor cursor 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.

Stale comment

Comment thread apps/web/src/pages/data.astro Outdated
Prevent </script> breakouts when bootstrapping live-update metrics via
set:html by encoding <, >, &, and unicode line separators.

Co-authored-by: Martin DONADIEU <martindonadieu@gmail.com>
@sonarqubecloud

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

2 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/translation-worker/src/index.ts">

<violation number="1" location="apps/translation-worker/src/index.ts:812">
P2: A translate="no" / notranslate element's own translatable attributes (title, alt, aria-label, placeholder) are still collected as translation segments. In collectSegments, appendTag(parts, segments, tag, false, insideBody) runs before shouldSkipElementText is consulted, so the opening tag of the skipped element is split by appendTag and any translatable attribute on it becomes an attribute segment that gets translated and re-injected. The new test only places the title attribute on a nested <td> (inside the raw skip region, so it is correctly preserved), so it does not cover this case. If a live metric region is marked translate="no" on an element that itself carries a title/aria-label, that label would still be translated, contradicting the intent to keep metric regions English/stable.</violation>

<violation number="2" location="apps/translation-worker/src/index.ts:1398">
P1: The ±30% layout constraint is only a prompt instruction; any schema-valid overlong model response passes validation and is cached. Validate each output length and reject/retry or retain source when it exceeds the intended bound.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

'Translate naturally for the user cultural context; adapt idioms, grammar, tone, and phrasing instead of translating word for word.',
'Translate every human-readable label, heading, sentence, and paragraph into the target language, including short navigation labels.',
translationContextSystemHint(),
translationLengthSystemHint(),

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.

P1: The ±30% layout constraint is only a prompt instruction; any schema-valid overlong model response passes validation and is cached. Validate each output length and reject/retry or retain source when it exceeds the intended bound.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/translation-worker/src/index.ts, line 1398:

<comment>The ±30% layout constraint is only a prompt instruction; any schema-valid overlong model response passes validation and is cached. Validate each output length and reject/retry or retain source when it exceeds the intended bound.</comment>

<file context>
@@ -1379,8 +1395,9 @@ async function translateBatchWithJsonMode(env: Env, targetLanguage: string, batc
               'Translate naturally for the user cultural context; adapt idioms, grammar, tone, and phrasing instead of translating word for word.',
               'Translate every human-readable label, heading, sentence, and paragraph into the target language, including short navigation labels.',
               translationContextSystemHint(),
+              translationLengthSystemHint(),
               'Preserve brand names, product names, developer terms, URLs, code identifiers, file paths, package names, language codes, numbers, punctuation, and whitespace meaning.',
-              'Do not translate or transliterate literal tokens such as Capgo, Capacitor, code, API, SDK, CLI, npm, bun, GitHub, Cloudflare, package names, command names, and framework names.',
</file context>


const translate = readAttributeValue(tag, 'translate')
if (translate?.trim().toLowerCase() === 'no') return true
if (hasNoTranslateClass(readAttributeValue(tag, 'class'))) return true

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.

P2: A translate="no" / notranslate element's own translatable attributes (title, alt, aria-label, placeholder) are still collected as translation segments. In collectSegments, appendTag(parts, segments, tag, false, insideBody) runs before shouldSkipElementText is consulted, so the opening tag of the skipped element is split by appendTag and any translatable attribute on it becomes an attribute segment that gets translated and re-injected. The new test only places the title attribute on a nested (inside the raw skip region, so it is correctly preserved), so it does not cover this case. If a live metric region is marked translate="no" on an element that itself carries a title/aria-label, that label would still be translated, contradicting the intent to keep metric regions English/stable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/translation-worker/src/index.ts, line 812:

<comment>A translate="no" / notranslate element's own translatable attributes (title, alt, aria-label, placeholder) are still collected as translation segments. In collectSegments, appendTag(parts, segments, tag, false, insideBody) runs before shouldSkipElementText is consulted, so the opening tag of the skipped element is split by appendTag and any translatable attribute on it becomes an attribute segment that gets translated and re-injected. The new test only places the title attribute on a nested <td> (inside the raw skip region, so it is correctly preserved), so it does not cover this case. If a live metric region is marked translate="no" on an element that itself carries a title/aria-label, that label would still be translated, contradicting the intent to keep metric regions English/stable.</comment>

<file context>
@@ -796,9 +796,21 @@ function languageSelectorTargetLocale(tag: string): string | null {
 
+  const translate = readAttributeValue(tag, 'translate')
+  if (translate?.trim().toLowerCase() === 'no') return true
+  if (hasNoTranslateClass(readAttributeValue(tag, 'class'))) return true
+
   const id = readAttributeValue(tag, 'id')
</file context>

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/translation-worker/scripts/verify-parser.ts`:
- Around line 99-106: Extend the raw-preservation assertion for
noTranslateParsed.parts to verify the complete div.notranslate fragment: opening
and closing tags, their attributes, and the unchanged “Keep metrics English”
text. Keep the existing tbody translate="no" checks intact and ensure the
assertions inspect string parts.

In `@apps/translation-worker/src/index.ts`:
- Around line 1365-1367: Update the shared post-response flow used by
translateSingleText(), assertTranslatedBatch(), and refreshCacheIncrementally()
to validate each restored translation against its source length before rendering
or caching. Enforce the intended roughly ±30% bound, and on violation retry
through the existing translation mechanism or return a safe fallback; apply the
same behavior to both batch and single-text results while preserving
unchanged-string validation.

In `@apps/web/src/lib/liveUpdateMetrics.ts`:
- Around line 153-162: Update jsonForInlineScript to use replaceAll() with
String.raw escape literals instead of the current global-regex replacements,
while preserving the existing escaping order and behavior for JSON characters,
U+2028, and U+2029.

In `@apps/web/src/pages/data.astro`:
- Line 85: Add aria-live="polite" to concise status nodes covering every
client-updated metric rendered by renderMetrics() and renderDaily(), including
the success KPI, daily chart, country/platform/updater tables, and failure lead.
Prefer dedicated status elements rather than making whole tables or charts live,
while preserving the existing timestamp, empty-state, and failure-list regions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fbe14a72-ed3f-4549-9495-5e401dd43172

📥 Commits

Reviewing files that changed from the base of the PR and between 5ed2138 and 5b2a428.

📒 Files selected for processing (4)
  • apps/translation-worker/scripts/verify-parser.ts
  • apps/translation-worker/src/index.ts
  • apps/web/src/lib/liveUpdateMetrics.ts
  • apps/web/src/pages/data.astro
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment on lines +99 to +106
assert(
noTranslateBody.every((text) => !text.includes('United States') && !text.includes('Download failure') && !text.includes('Keep metrics English')),
'Parser collected text from translate=no / notranslate regions',
)
assert(
noTranslateParsed.parts.some((part) => typeof part === 'string' && part.includes('United States') && part.includes('Download failure (46%)')),
'Parser did not preserve translate=no markup as raw HTML',
)

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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Assert raw preservation for .notranslate.

The test checks that Keep metrics English is absent from body segments. The raw-preservation assertion covers only the <tbody translate="no"> fragment. A regression could drop or rewrite the <div class="notranslate"> wrapper and still pass. Assert that the .notranslate opening tag, closing tag, attributes, and unchanged text remain in noTranslateParsed.parts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/translation-worker/scripts/verify-parser.ts` around lines 99 - 106,
Extend the raw-preservation assertion for noTranslateParsed.parts to verify the
complete div.notranslate fragment: opening and closing tags, their attributes,
and the unchanged “Keep metrics English” text. Keep the existing tbody
translate="no" checks intact and ensure the assertions inspect string parts.

Comment on lines +1365 to +1367
function translationLengthSystemHint(): string {
return 'Keep each translation about the same length as the source (similar character count, roughly within ±30%). Do not expand short UI labels, buttons, nav items, table headers, or headings into longer sentences. Over-long translations break Capgo page layouts and UI spacing.'
}

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.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -u

rg -n -C 12 'function assertTranslatedBatch|assertTranslatedBatch\(' \
  apps/translation-worker/src/index.ts || true

rg -n -C 8 'translationLengthSystemHint|translateSingleText|translateBatchWithJsonMode|length' \
  apps/translation-worker --glob '*.ts' || true

Repository: Cap-go/website

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -u

sed -n '1220,1555p' apps/translation-worker/src/index.ts

Repository: Cap-go/website

Length of output: 14007


🏁 Script executed:

#!/bin/bash
set -u

rg -n -C 10 'translateBatch\(|translateSingleText\(|translateBatchIndividually\(|putTranslation|translations|cache' \
  apps/translation-worker/src/index.ts | head -n 260

python3 - <<'PY'
from pathlib import Path
import re

source = Path("apps/translation-worker/src/index.ts").read_text()

batch = re.search(
    r"function assertTranslatedBatch\(.*?\n\}\n\nfunction bodyTranslationStats",
    source,
    re.S,
)
single = re.search(
    r"async function translateSingleText\(.*?\n\}\n\nasync function translateBatchIndividually",
    source,
    re.S,
)

assert batch and single
batch_body = batch.group(0)
single_body = single.group(0)

# The batch assertion compares normalized equality only. It has no ratio or
# length-bound calculation.
assert "source === target" in batch_body
assert "length" not in batch_body.split("function bodyTranslationStats", 1)[0]
assert "±30%" not in batch_body

# The single-text success path returns restored model output directly.
assert "return protectedText.restore(translated)" in single_body
assert "assertTranslatedBatch" not in single_body

source_text = "Deploy updates safely to every device"
overlong_translation = source_text + " with detailed guidance for all supported environments and release channels"
assert len(overlong_translation) > len(source_text) * 1.3
print({
    "source_length": len(source_text),
    "overlong_translation_length": len(overlong_translation),
    "exceeds_30_percent": len(overlong_translation) > len(source_text) * 1.3,
    "batch_length_check_present": False,
    "single_length_check_present": False,
    "single_returns_restored_output": True,
})
PY

Repository: Cap-go/website

Length of output: 10293


🏁 Script executed:

#!/bin/bash
set -u

rg -n 'async function|function .*Translation|translatedBatches|StoredTranslatedResponse|renderTranslatedHtml|assertTranslatedBody|translateBatch\(' \
  apps/translation-worker/src/index.ts | tail -n 140

python3 - <<'PY'
from pathlib import Path
import re

source = Path("apps/translation-worker/src/index.ts").read_text()

batch = re.search(
    r"function assertTranslatedBatch\(.*?\n\}\n\nfunction bodyTranslationStats",
    source,
    re.S,
)
single = re.search(
    r"async function translateSingleText\(.*?\n\}\n\nasync function translateBatchIndividually",
    source,
    re.S,
)
assert batch and single

batch_body = batch.group(0)
single_body = single.group(0)

# The batch assertion has no source/translated length comparison.
comparison_lines = [
    line for line in batch_body.splitlines()
    if "source" in line and "translated" in line
]
assert any("source === target" in line for line in batch_body.splitlines())
assert not any(
    ("source.length" in line or "translated.length" in line or "target.length" in line)
    for line in comparison_lines
)

# The single-text success path returns restored model output directly.
assert "return protectedText.restore(translated)" in single_body
assert "assertTranslatedBatch" not in single_body
assert "length" not in single_body.split("return protectedText.restore(translated)", 1)[0].rsplit("\n", 8)[-8:]

source_text = "Deploy updates safely to every device"
overlong_translation = source_text + " with detailed guidance for all supported environments and release channels"
ratio = len(overlong_translation) / len(source_text)
assert ratio > 1.30

print({
    "source_length": len(source_text),
    "overlong_translation_length": len(overlong_translation),
    "length_ratio": round(ratio, 2),
    "batch_length_comparison_present": False,
    "single_length_check_present": False,
    "single_returns_restored_output": True,
})
PY

Repository: Cap-go/website

Length of output: 8357


🏁 Script executed:

#!/bin/bash
set -u

sed -n '2140,2245p' apps/translation-worker/src/index.ts
sed -n '1680,1742p' apps/translation-worker/src/index.ts

Repository: Cap-go/website

Length of output: 7367


Enforce the translation length bound before caching.

translationLengthSystemHint() provides prompt guidance only. translateSingleText() returns restored model output without a length check, and assertTranslatedBatch() checks only unchanged strings. refreshCacheIncrementally() then renders and stores these results. Add a shared post-response check against each restored translation and its source, with retry or a safe fallback, for both batch and single-text translations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/translation-worker/src/index.ts` around lines 1365 - 1367, Update the
shared post-response flow used by translateSingleText(),
assertTranslatedBatch(), and refreshCacheIncrementally() to validate each
restored translation against its source length before rendering or caching.
Enforce the intended roughly ±30% bound, and on violation retry through the
existing translation mechanism or return a safe fallback; apply the same
behavior to both batch and single-text results while preserving unchanged-string
validation.

Comment on lines +153 to +162

/** JSON safe to embed inside <script> via set:html (blocks </script> breakouts). */
export function jsonForInlineScript(value: unknown): string {
return JSON.stringify(value)
.replace(/</g, '\\u003c')
.replace(/>/g, '\\u003e')
.replace(/&/g, '\\u0026')
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029')
}

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

fd -aH -t f 'package\.json|tsconfig.*\.json|\.browserslistrc|astro\.config.*' . \
  -x rg -n 'browserslist|engines|target|compatibility|ES20' {}

Repository: Cap-go/website

Length of output: 367


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- target/config files ---'
fd -aH -t f 'package.json|tsconfig.*\.json|\.browserslistrc|astro.config.*|sonar-project.*' . \
  -x sh -c 'echo "### $1"; sed -n "1,180p" "$1"' sh {}

printf '%s\n' '--- function and usages ---'
rg -n -A20 -B8 'jsonForInlineScript|replaceAll|String\.raw' apps package.json tsconfig*.json 2>/dev/null || true

printf '%s\n' '--- relevant project files ---'
git ls-files '*liveUpdateMetrics.ts' '*package.json' '*tsconfig*.json' '*astro.config*' '*sonar*'

Repository: Cap-go/website

Length of output: 50371


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import json

p = Path("apps/web/src/lib/liveUpdateMetrics.ts")
text = p.read_text()
start = text.index("export function jsonForInlineScript")
end = text.find("\n}", start) + 2
print(text[start:end])

for path in sorted(Path(".").rglob("package.json")):
    try:
        data = json.loads(path.read_text())
    except Exception:
        continue
    for key in ("engines", "browserslist"):
        if key in data:
            print(f"{path}: {key}={data[key]!r}")
PY
PY

Repository: Cap-go/website

Length of output: 448


🏁 Script executed (no clone):

node - <<'JS'
const current = value => JSON.stringify(value)
  .replace(/</g, '\\u003c')
  .replace(/>/g, '\\u003e')
  .replace(/&/g, '\\u0026')
  .replace(/\u2028/g, '\\u2028')
  .replace(/\u2029/g, '\\u2029');

const proposed = value => JSON.stringify(value)
  .replaceAll('<', String.raw`\u003c`)
  .replaceAll('>', String.raw`\u003e`)
  .replaceAll('&', String.raw`\u0026`)
  .replaceAll('\u2028', String.raw`\u2028`)
  .replaceAll('\u2029', String.raw`\u2029`);

const value = {
  text: '<>&\u2028\u2029</script>',
  repeated: '<<>>&&\u2028\u2029',
  nested: ['<', '>', '&'],
};
console.log({ current: current(value), proposed: proposed(value), equal: current(value) === proposed(value) });
JS

Length of output: 530


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- TypeScript target declarations ---'
rg -n --glob '!**/*.webp' --glob '!**/*.png' --glob '!**/*.jpg' \
  '"target"|"lib"|extends.*astro/tsconfigs|browserslist|engines' \
  package.json apps/*/package.json apps/*/tsconfig*.json tsconfig*.json 2>/dev/null || true

printf '%s\n' '--- package and lock metadata ---'
rg -n --glob '!**/*.webp' --glob '!**/*.png' --glob '!**/*.jpg' \
  'astro/tsconfigs/strict|typescript@|astro@|target:|target =' \
  bun.lock bun.lockb package.json apps 2>/dev/null | head -80 || true

printf '%s\n' '--- exact function context ---'
nl -ba apps/web/src/lib/liveUpdateMetrics.ts | sed -n '145,168p'

Repository: Cap-go/website

Length of output: 6536


🌐 Web query:

Astro 7 astro/tsconfigs/strict default target TypeScript compilerOptions target

💡 Result:

Astro provides three extensible TypeScript configuration presets—base, strict, and strictest—which are located in the astro/tsconfigs/ directory [1][2][3]. These presets are designed to be extended in your project's tsconfig.json file [1][3]. The astro/tsconfigs/strict preset does not mandate a specific target; instead, it inherits from the base configuration and enables a suite of strict type-checking options [4]. In modern Astro projects, the target is typically determined by your own tsconfig.json settings or left to the default behavior of the TypeScript compiler environment [5]. When using these presets, you are encouraged to explicitly set your compilerOptions in your project's tsconfig.json to meet your specific environment needs [6][7]. A common modern configuration for the target option in Astro projects is ESNext, which allows you to leverage the latest ECMAScript features supported by your target deployment environment [5]. To use the strict preset, configure your tsconfig.json as follows: { "extends": "astro/tsconfigs/strict", "compilerOptions": { "target": "ESNext" } } The base template, which underlies the others, is maintained by the Astro team to enable support for modern JavaScript features while remaining compatible with Astro's architecture [1][3]. For the most accurate and up-to-date look at the specific compiler options included in the strict preset, you can inspect the source code directly in the official Astro repository under packages/astro/tsconfigs/ [1][3].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- shared build target ---'
sed -n '96,120p' apps/shared/astro-config.mjs

printf '%s\n' '--- function and call sites ---'
rg -n -A18 -B6 'jsonForInlineScript' apps/web/src apps/shared

Repository: Cap-go/website

Length of output: 6140


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

source = Path("apps/web/src/lib/liveUpdateMetrics.ts").read_text()
shared = Path("apps/shared/astro-config.mjs").read_text()

assert "target: 'es2022'" in shared
assert re.search(r"\.replace\(/</g, '\\\\\\\\u003c'\)", source)
assert re.search(r"\.replace\(/>/g, '\\\\\\\\u003e'\)", source)
assert re.search(r"\.replace\(/&/g, '\\\\\\\\u0026'\)", source)
assert ".replace(/\\u2028/g" in source
assert ".replace(/\\u2029/g" in source

print("build target: es2022")
print("current serializer contains all five global escapes")
print("replaceAll is available in ES2021 and later")
print("String.raw is available in ES2015 and later")
PY

Repository: Cap-go/website

Length of output: 240


Replace the global regex calls with replaceAll() and String.raw.

The shared Vite build target is ES2022, and this form preserves the current escaping behavior.

🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 161-161: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=Cap-go_website&issues=AZ_xDRsGPbLGDTR3pln_&open=AZ_xDRsGPbLGDTR3pln_&pullRequest=949


[warning] 159-159: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=Cap-go_website&issues=AZ_xDRsGPbLGDTR3pln7&open=AZ_xDRsGPbLGDTR3pln7&pullRequest=949


[warning] 160-160: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=Cap-go_website&issues=AZ_xDRsGPbLGDTR3pln9&open=AZ_xDRsGPbLGDTR3pln9&pullRequest=949


[warning] 157-157: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=Cap-go_website&issues=AZ_xDRsGPbLGDTR3pln3&open=AZ_xDRsGPbLGDTR3pln3&pullRequest=949


[warning] 157-157: String.raw should be used to avoid escaping \.

See more on https://sonarcloud.io/project/issues?id=Cap-go_website&issues=AZ_xDRsGPbLGDTR3pln4&open=AZ_xDRsGPbLGDTR3pln4&pullRequest=949


[warning] 161-161: String.raw should be used to avoid escaping \.

See more on https://sonarcloud.io/project/issues?id=Cap-go_website&issues=AZ_xDRsGPbLGDTR3ploA&open=AZ_xDRsGPbLGDTR3ploA&pullRequest=949


[warning] 158-158: String.raw should be used to avoid escaping \.

See more on https://sonarcloud.io/project/issues?id=Cap-go_website&issues=AZ_xDRsGPbLGDTR3pln6&open=AZ_xDRsGPbLGDTR3pln6&pullRequest=949


[warning] 159-159: String.raw should be used to avoid escaping \.

See more on https://sonarcloud.io/project/issues?id=Cap-go_website&issues=AZ_xDRsGPbLGDTR3pln8&open=AZ_xDRsGPbLGDTR3pln8&pullRequest=949


[warning] 160-160: String.raw should be used to avoid escaping \.

See more on https://sonarcloud.io/project/issues?id=Cap-go_website&issues=AZ_xDRsGPbLGDTR3pln-&open=AZ_xDRsGPbLGDTR3pln-&pullRequest=949


[warning] 158-158: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=Cap-go_website&issues=AZ_xDRsGPbLGDTR3pln5&open=AZ_xDRsGPbLGDTR3pln5&pullRequest=949

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/lib/liveUpdateMetrics.ts` around lines 153 - 162, Update
jsonForInlineScript to use replaceAll() with String.raw escape literals instead
of the current global-regex replacements, while preserving the existing escaping
order and behavior for JSON characters, U+2028, and U+2029.

Source: Linters/SAST tools


<div class="data-kpis" aria-label="Reliability summary">
<div><p>Success rate</p><strong data-kpi="success-rate">{Math.ceil(metrics.success_rate)}%</strong></div>
<div><p>Success rate</p><strong data-kpi="success-rate" translate="no">{Math.ceil(metrics.success_rate)}%</strong></div>

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add live-region coverage for all client-updated metric regions.

renderMetrics() and renderDaily() update the success KPI, daily chart, country table, platform table, updater table, and failure lead. These changed elements do not expose aria-live. The existing live regions for the timestamp, empty states, and failure list do not cover these siblings. Screen-reader users can miss refreshed values after fetchMetrics() completes.

Add aria-live="polite" to concise status nodes for these updates. Prefer a dedicated status node when making an entire table or chart live would be verbose.

As per coding guidelines, **/*.{astro,jsx,tsx,js,ts} requires dynamic content to use aria-live regions.

Also applies to: 93-93, 121-121, 164-164, 193-193, 246-246

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/pages/data.astro` at line 85, Add aria-live="polite" to concise
status nodes covering every client-updated metric rendered by renderMetrics()
and renderDaily(), including the success KPI, daily chart,
country/platform/updater tables, and failure lead. Prefer dedicated status
elements rather than making whole tables or charts live, while preserving the
existing timestamp, empty-state, and failure-list regions.

Source: Coding guidelines

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