-
-
Notifications
You must be signed in to change notification settings - Fork 851
Description
Problem Statement
Internationalization is a common need for CLI tools built with Ink, but current architecture makes it challenging for userland i18n packages to work correctly:
Current Pain Point:
// This fails in Ink due to reconciler constraints
import { Trans } from 'react-i18next';
<Trans i18nKey="welcome" components={{ bold: <Text bold /> }} />
// Error: Text string must be rendered inside <Text> componentRoot Cause:
Ink's reconciler requires all text nodes to have isInsideText: true context, but existing i18n libraries like react-i18next generate text nodes that violate this constraint.
1. Implementation Principles
Minimal Primitives Approach
Instead of built-in i18n support, expose minimal primitives that enable robust userland solutions. This approach:
- Keeps Ink core focused and lightweight
- Provides necessary building blocks for community
- Maintains architectural integrity
- Enables multiple i18n frameworks to coexist
Two-Phase Strategy
Why split implementation?
- Risk mitigation: Start with low-risk foundation APIs
- Community feedback: Learn from Phase 1 usage before advanced features
- Incremental value: Immediate benefits while building toward complete solution
- Review efficiency: Smaller PRs are easier to review and merge
Expected Outcome: Enable community packages like ink-i18next, ink-react-intl to work seamlessly:
import { InkTrans } from 'ink-i18next';
<InkTrans i18nKey="welcome" components={{ bold: <Text bold /> }} />
// Works perfectly with Ink's constraintsTechnical Context:
- Reconciler constraint:
reconciler.js:126-130validates text context - Text merging:
squash-text-nodes.jscombines adjacent text nodes - Transform pipeline:
internal_transformhandles styling
Implementation Plan
Step 1: Foundation APIs PR (low risk, immediate value)
Step 2: Advanced utilities PR (based on community feedback)
Step 3: Community adoption and ecosystem growth
References
Would appreciate feedback on this approach before starting implementation!
2. Phase 1: Foundation APIs
1. Text Fragment Composition API
interface TextFragment {
text: string;
transform?: (text: string, index: number) => string;
}
export function composeTextFragments(
fragments: TextFragment[]
): stringPurpose: Safely combine multiple text pieces while respecting Ink's text merging mechanism.
Example:
const result = composeTextFragments([
{ text: "Hello " },
{ text: "World", transform: (text) => chalk.bold(text) },
{ text: "!" }
]);2. Text Context Validation API
interface TextContextInfo {
isInsideText: boolean;
validateTextPlacement: (node: ReactNode) => boolean;
}
export function createTextContext(): TextContextInfoPurpose: Allow userland packages to validate text placement and avoid reconciler errors.
3. Phase 2: Advanced Utilities
1. Styled Text Parser API
interface StyledTextComponent {
tag: string;
component: ComponentType<{ children: ReactNode }>;
}
export function parseStyledText(
template: string,
components: Record<string, StyledTextComponent>,
variables?: Record<string, any>
): ReactElementPurpose: Parse template strings with XML-like tags and convert to Ink-compatible component trees.
Example:
const result = parseStyledText(
"Hello <bold>{name}</bold>, you have <count>{count}</count> messages",
{
bold: { tag: 'bold', component: BoldText },
count: { tag: 'count', component: CountText }
},
{ name: "Alice", count: 5 }
);2. Transform Pipeline API
export function createTransformPipeline(
transforms: Array<(text: string, index: number) => string>
): (text: string, index: number) => string
export function combineTransforms(
baseTransform?: (text: string, index: number) => string,
additionalTransforms: Array<(text: string, index: number) => string>
): (text: string, index: number) => stringPurpose: Extend Ink's internal_transform mechanism for complex text processing pipelines.
Impact & Assessment
Benefits: Unblocks existing i18n pain points, enables community packages, maintains backward compatibility.
Risks: Phase 1 (low) - pure utilities; Phase 2 (medium) - complex parsing logic.
Success Criteria:
- Phase 1: APIs merged, community adoption, zero regressions
- Phase 2: Multiple i18n frameworks supported, documentation available
This proposal aims to solve internationalization challenges while maintaining Ink's architectural principles and community-driven ecosystem approach.