Skip to content

Conversation

@JanCizmar
Copy link
Contributor

@JanCizmar JanCizmar commented Oct 29, 2025

Summary by CodeRabbit

  • New Features
    • Introduced T2 component for Vue, a new translation tool supporting advanced templating. Enables embedding custom content within translated strings through slot injection, supports both plain text and complex translations, and provides optional text wrapping control for flexible component rendering.

- Add tags interpolation similar how to do it in React
- So the user can provide their <a> or <strong> component without the need to provide inner text as separate translation key
@coderabbitai
Copy link

coderabbitai bot commented Oct 29, 2025

Walkthrough

Introduces a new Vue 3 component T2 that enables templated translations with slot injection. The component parses XML-like tags in translated strings and replaces them with corresponding slot content, supporting both wrapped and unwrapped rendering modes.

Changes

Cohort / File(s) Summary
New T2 component
packages/vue/src/T2.ts
Defines a new translation component with props for modelValue, params, defaultValue, noWrap, ns, and language. Implements regex-based parsing of XML-like slot tags within translated strings, injecting slot content where tags match slot names. Handles both VNode and string translation results with optional span wrapping.
Package exports
packages/vue/src/index.ts
Re-exports the new T2 component to make it available as part of the public API.
Example usage
testapps/vue/src/TranslationMethods.vue
Updates imports to include T2 and adds a demonstration of T2 component usage with nested template and slot examples.

Sequence Diagram

sequenceDiagram
    participant Parent as Parent Component
    participant T2 as T2 Component
    participant Hook as useTranslateInternal
    participant Regex as Slot Parser

    Parent->>T2: Render with modelValue + params
    T2->>Hook: useTranslateInternal(props)
    Hook-->>T2: t function
    T2->>T2: Call t(params)
    T2->>T2: Check if result is string or VNode
    
    alt Result is VNode
        T2-->>Parent: Return VNode directly
    else Result is string
        T2->>Regex: Parse for <slot-name>...</slot-name> tags
        Regex-->>T2: Array of text & slot references
        
        alt noWrap is true
            T2-->>Parent: Return parsed array
        else noWrap is false
            T2->>T2: Wrap array in span element
            T2-->>Parent: Return wrapped span
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • T2.ts: The core component implementation contains moderately complex regex-based slot parsing logic and conditional rendering paths that require careful validation for edge cases (empty slots, malformed tags, VNode vs. string handling).
  • Slot injection logic: Verify that regex pattern correctly identifies and extracts slot tags, handles remaining text properly, and gracefully manages missing or undefined slots.
  • Index.ts and TranslationMethods.vue: These are straightforward export and usage additions requiring minimal review effort.

Poem

🐰 A new component hops into view,
With slots and translations—meet T2!
Tags dance in strings, then vanish away,
Replaced by content, hip-hip-hooray! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Support better tags interpolation in Vue.js" directly addresses the main change introduced in this pull request—a new Vue 3 component T2 that parses XML-like tags in translations and replaces them with corresponding slot content. The title is concise, specific, and avoids vague terminology. It accurately reflects the primary feature being added, using clear language that would help a developer scanning the history understand the core contribution.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jancizmar/vue-tags-interpolation

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: 2

🧹 Nitpick comments (3)
packages/vue/src/T2.ts (3)

7-8: Import VNode as a type-only to avoid bundling it

Prevents unnecessary runtime import.

-import { defineComponent, h, VNode } from 'vue';
-import type { PropType } from 'vue';
+import { defineComponent, h } from 'vue';
+import type { PropType, VNode } from 'vue';

52-56: Regex won’t match multiline tag contents and can mis-handle nested tags

Current .*? doesn’t span newlines. Use a dotall-safe class to cover multiline; nested same‑name tags remain unsupported (acceptable for now, but document it).

-      const tagRegex = /<([a-zA-Z-]+)>(.*?)<\/\1>/g;
+      // Allow multiline inner text
+      const tagRegex = /<([a-zA-Z-]+)>([\s\S]*?)<\/\1>/g;

Consider adding tests with newlines and repeated tags of the same name.


15-16: Normalize prop typing for params

Use the object form for consistency with other props and better TS inference.

-    params: Object as PropType<TranslateParams>,
+    params: { type: Object as PropType<TranslateParams> },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7fdef5e and 9cbae4d.

📒 Files selected for processing (3)
  • packages/vue/src/T2.ts (1 hunks)
  • packages/vue/src/index.ts (1 hunks)
  • testapps/vue/src/TranslationMethods.vue (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/vue/src/T2.ts (1)
packages/core/src/types/general.ts (4)
  • TranslationKey (7-7)
  • TranslateParams (17-19)
  • NsType (3-3)
  • TranslateProps (28-35)
🔇 Additional comments (2)
packages/vue/src/index.ts (1)

3-3: Public surface OK

Re-export of T2 looks correct and consistent with the package’s entrypoint.

testapps/vue/src/TranslationMethods.vue (1)

145-145: Import is fine once props align

Importing { T, T2 } is correct; after fixing the prop mismatch above, the sample should render as intended.

Comment on lines +14 to +23
modelValue: { type: String as PropType<TranslationKey>, required: true },
params: Object as PropType<TranslateParams>,
defaultValue: String as PropType<string>,
noWrap: {
type: Boolean,
default: false,
},
ns: { type: String as PropType<NsType> },
language: { type: String as PropType<string> },
},
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Prop/API mismatch: use keyName (and keep modelValue as alias) to match <T> and the example

modelValue as the translation key is unintuitive and breaks the provided example. Support keyName (preferred) and keep modelValue as a backward‑compatible alias; guard when both are missing.

Apply:

@@
 export const T2 = defineComponent({
   name: 'T2',
   props: {
-    modelValue: { type: String as PropType<TranslationKey>, required: true },
-    params: Object as PropType<TranslateParams>,
+    // Prefer keyName for consistency with <T>, accept modelValue as an alias
+    keyName: { type: String as PropType<TranslationKey> },
+    modelValue: { type: String as PropType<TranslationKey> },
+    params: { type: Object as PropType<TranslateParams> },
     defaultValue: String as PropType<string>,
@@
   render() {
-    // Get the translation text first
-    const params: TranslateProps = {
-      key: this.$props.modelValue,
+    // Pick key from keyName first, then modelValue (alias)
+    const resolvedKey = (this.$props as any).keyName ?? this.$props.modelValue;
+    if (!resolvedKey) {
+      if (process.env.NODE_ENV !== 'production') {
+        console.warn('[T2] keyName or modelValue is required.');
+      }
+      return null;
+    }
+    const params: TranslateProps = {
+      key: resolvedKey,
       params: this.$props.params,
       defaultValue: this.$props.defaultValue,
       noWrap: this.$props.noWrap,
       ns: this.$props.ns,
       language: this.$props.language,
     };

Also applies to: 30-37

🤖 Prompt for AI Agents
In packages/vue/src/T2.ts around lines 14-23 (and also update similarly at lines
30-37), the component props use modelValue as the primary translation key which
conflicts with the <T> API and the example; add a new prop keyName: { type:
String as PropType<TranslationKey> } as the preferred prop and keep modelValue
as a backward-compatible alias (same type) while ensuring runtime logic reads
keyName first and falls back to modelValue, and throw or warn if both are
missing; update types and prop validation accordingly in both prop blocks
mentioned.

Comment on lines +118 to +121
<T2
key-name="hello"
default-value="Pro vytváření <strong>Hostů</strong> je třeba mít <link-to-help>ověřenou doménu</link-to-help>"
>
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

Fix prop name: T2 currently requires modelValue, not keyName.

Example uses key-name, so required prop is missing and key becomes undefined. Either:

  • Change usage to model-value="hello", or
  • Update T2 to accept keyName (preferred for consistency with <T>).

See proposed T2 diff below to support both.

Run to locate other usages:


🏁 Script executed:

#!/bin/bash
rg -nP -C2 --type=vue '<T2\b[^>]*\b(key-name|model-value)\b' testapps/vue || true

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Find T2 component definition
fd -e vue . testapps/vue --exec grep -l 'export.*T2\|name.*T2\|component.*T2' {} \; 2>/dev/null | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for T2 component definition in components directory
fd -e vue . testapps/vue -path '*component*' -o -path '*T2*' 2>/dev/null | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find all T2 usages without type filter
rg -n '<T2' testapps/vue --max-count 50

Length of output: 57


🏁 Script executed:

#!/bin/bash
# Look at the specific file mentioned in review
cat testapps/vue/src/TranslationMethods.vue | sed -n '115,125p'

Length of output: 426


🏁 Script executed:

#!/bin/bash
# Get imports and script section from TranslationMethods.vue
head -30 testapps/vue/src/TranslationMethods.vue

Length of output: 682


🏁 Script executed:

#!/bin/bash
# Search for T2 export or definition across entire codebase
rg -n 'export.*component.*T2|export default.*T2|<script.*setup|name:\s*["\x27]T2' --max-count 50

Length of output: 1005


🏁 Script executed:

#!/bin/bash
# Look for T2 in components or other common locations
fd T2 . --type f --type d 2>/dev/null | head -20

Length of output: 24


🏁 Script executed:

#!/bin/bash
# Search for T component definitions (might clarify pattern)
rg -n '<T\b|<T1\b|component.*\bT[0-9]' testapps/vue/src --max-count 20

Length of output: 965


🏁 Script executed:

#!/bin/bash
# View T2 component definition
cat packages/vue/src/T2.ts

Length of output: 3208


🏁 Script executed:

#!/bin/bash
# View T component definition for comparison
cat packages/vue/src/T.ts

Length of output: 1245


🏁 Script executed:

#!/bin/bash
# Check for vue-i18n-ally package main export
cat packages/vue/src/index.ts | head -50

Length of output: 347


Fix T2 prop: use model-value instead of key-name.

T2 requires the modelValue prop (rendered as model-value in templates), but the usage provides key-name. This leaves modelValue undefined, breaking the translation. Either:

  • Change to model-value="hello", or
  • Update T2 to accept keyName for consistency with the <T> component.
🤖 Prompt for AI Agents
In testapps/vue/src/TranslationMethods.vue around lines 118 to 121, the T2 usage
passes key-name but T2 expects modelValue (model-value in templates), so the
translation is broken; fix by replacing key-name="hello" with
model-value="hello" in the T2 tag, or alternatively update the T2 component to
accept a keyName prop (map it to modelValue internally) to keep parity with the
<T> component — choose the model-value change for the quickest fix or implement
the prop alias in T2 if you need backwards compatibility.

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